Download src/python/tom_world03/expression.py from c0mbatduckzz/TOMK: direct link, hf CLI and curl.
- Browser
- Download file 5.13 kB
-
https://huggingface.co/c0mbatduckzz/TOMK/resolve/main/src/python/tom_world03/expression.py
- Command line
-
hf download hf://c0mbatduckzz/TOMK/src/python/tom_world03/expression.py
-
curl -L -o expression.py https://huggingface.co/c0mbatduckzz/TOMK/resolve/main/src/python/tom_world03/expression.py
5.13 kB
| """Typed continuous expression evaluation over affine TOM trajectories.""" | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Any, Mapping, Protocol | |
| from .interval import ClosedInterval | |
| from .rational import Q | |
| class TrajectoryLike(Protocol): | |
| def state_at(self, time: Q) -> Mapping[str, Q]: ... | |
| def field_interval(self, name: str, time: ClosedInterval) -> ClosedInterval: ... | |
| def field_rate(self, name: str) -> Q: ... | |
| def field_affine(self, name: str) -> tuple[Q, Q]: ... | |
| class DualInterval: | |
| value: ClosedInterval | |
| derivative: ClosedInterval | |
| def _op(expr: Mapping[str, Any]) -> str: | |
| value = expr.get("op") | |
| if not isinstance(value, str): | |
| raise ValueError("expression requires string op") | |
| return value | |
| def validate_expression(expr: Any, *, depth: int = 0, max_depth: int = 64) -> None: | |
| if depth > max_depth: | |
| raise ValueError("expression depth exceeds profile limit") | |
| if not isinstance(expr, Mapping): | |
| raise TypeError("relation expression nodes must be objects") | |
| op = _op(expr) | |
| if op == "const": | |
| Q.from_value(expr.get("value")) | |
| return | |
| if op == "field": | |
| if not isinstance(expr.get("name"), str) or not expr["name"]: | |
| raise ValueError("field expression requires nonempty name") | |
| return | |
| if op == "time": | |
| return | |
| if op == "neg": | |
| validate_expression(expr.get("value"), depth=depth + 1, max_depth=max_depth) | |
| return | |
| if op in {"add", "sub", "mul"}: | |
| args = expr.get("args") | |
| if not isinstance(args, list) or len(args) != 2: | |
| raise ValueError(f"{op} expression requires exactly two args") | |
| validate_expression(args[0], depth=depth + 1, max_depth=max_depth) | |
| validate_expression(args[1], depth=depth + 1, max_depth=max_depth) | |
| return | |
| raise ValueError(f"unsupported continuous expression op {op}") | |
| def evaluate_point(expr: Mapping[str, Any], trajectory: TrajectoryLike, time: Q) -> Q: | |
| op = _op(expr) | |
| if op == "const": | |
| return Q.from_value(expr["value"]) | |
| if op == "time": | |
| return time | |
| if op == "field": | |
| state = trajectory.state_at(time) | |
| name = str(expr["name"]) | |
| if name not in state: | |
| raise ValueError(f"trajectory has no field {name}") | |
| return Q.from_value(state[name]) | |
| if op == "neg": | |
| return -evaluate_point(expr["value"], trajectory, time) | |
| a = evaluate_point(expr["args"][0], trajectory, time) | |
| b = evaluate_point(expr["args"][1], trajectory, time) | |
| if op == "add": | |
| return a + b | |
| if op == "sub": | |
| return a - b | |
| if op == "mul": | |
| return a * b | |
| raise ValueError(f"unsupported op {op}") | |
| def evaluate_dual_interval( | |
| expr: Mapping[str, Any], | |
| trajectory: TrajectoryLike, | |
| time: ClosedInterval, | |
| ) -> DualInterval: | |
| op = _op(expr) | |
| if op == "const": | |
| return DualInterval(ClosedInterval.point(expr["value"]), ClosedInterval.point(0)) | |
| if op == "time": | |
| return DualInterval(time, ClosedInterval.point(1)) | |
| if op == "field": | |
| name = str(expr["name"]) | |
| return DualInterval( | |
| trajectory.field_interval(name, time), | |
| ClosedInterval.point(trajectory.field_rate(name)), | |
| ) | |
| if op == "neg": | |
| a = evaluate_dual_interval(expr["value"], trajectory, time) | |
| return DualInterval(-a.value, -a.derivative) | |
| a = evaluate_dual_interval(expr["args"][0], trajectory, time) | |
| b = evaluate_dual_interval(expr["args"][1], trajectory, time) | |
| if op == "add": | |
| return DualInterval(a.value + b.value, a.derivative + b.derivative) | |
| if op == "sub": | |
| return DualInterval(a.value - b.value, a.derivative - b.derivative) | |
| if op == "mul": | |
| return DualInterval( | |
| a.value * b.value, | |
| a.derivative * b.value + a.value * b.derivative, | |
| ) | |
| raise ValueError(f"unsupported op {op}") | |
| def affine_coefficients( | |
| expr: Mapping[str, Any], trajectory: TrajectoryLike | |
| ) -> tuple[Q, Q] | None: | |
| """Return (slope, intercept) when expr is affine in global time.""" | |
| op = _op(expr) | |
| if op == "const": | |
| return Q(0), Q.from_value(expr["value"]) | |
| if op == "time": | |
| return Q(1), Q(0) | |
| if op == "field": | |
| return trajectory.field_affine(str(expr["name"])) | |
| if op == "neg": | |
| inner = affine_coefficients(expr["value"], trajectory) | |
| return None if inner is None else (-inner[0], -inner[1]) | |
| left = affine_coefficients(expr["args"][0], trajectory) | |
| right = affine_coefficients(expr["args"][1], trajectory) | |
| if left is None or right is None: | |
| return None | |
| if op == "add": | |
| return left[0] + right[0], left[1] + right[1] | |
| if op == "sub": | |
| return left[0] - right[0], left[1] - right[1] | |
| if op == "mul": | |
| if left[0] == Q(0): | |
| return right[0] * left[1], right[1] * left[1] | |
| if right[0] == Q(0): | |
| return left[0] * right[1], left[1] * right[1] | |
| return None | |
| raise ValueError(f"unsupported op {op}") | |