let domains author their own pass-2 estimator as formulas, not Python

Pass 2 previously only knew one estimator: a hardcoded physics model that
matches dimensions literally named platform/actuator/energy_storage. Any
domain outside that shape (e.g. archery) got all-zero estimates and failed
every combo. Domains can now declare free variables and per-metric formulas
as data instead; a safe AST-based evaluator (engine/formula.py, no eval())
resolves declared entity properties via dep(key, constraint_type) and
generalizes the existing hand-nested mass-budget search into an N-variable
recursive optimizer. Fully additive -- the legacy platform/actuator/
energy_storage path is untouched and still runs unchanged for every domain
that declares no formulas.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 21:28:15 -05:00
parent 3795a7e826
commit 3429bce8d0
15 changed files with 1064 additions and 9 deletions

View File

@@ -1,7 +1,7 @@
"""Tests for the database repository."""
from physcom.models.entity import Entity, Dependency
from physcom.models.domain import Domain, MetricBound
from physcom.models.domain import Domain, FreeVariable, MetricBound, MetricFormula
def test_ensure_dimension(repo):
@@ -63,6 +63,62 @@ def test_add_and_get_domain(repo):
assert loaded.metric_bounds[0].metric_name == "speed"
def test_add_domain_with_free_variables_and_formulas(repo):
domain = Domain(
name="archery_test",
metric_bounds=[MetricBound("drawback_force", weight=1.0, norm_min=0, norm_max=500)],
free_variables=[
FreeVariable(
name="draw_weight",
floor_formula='dep("draw_weight", "range_min")',
ceiling_formula='dep("draw_weight", "range_max")',
sort_order=0,
),
],
metric_formulas=[
MetricFormula(metric_name="drawback_force", formula="draw_weight * 1.5"),
],
)
saved = repo.add_domain(domain)
assert saved.id is not None
loaded = repo.get_domain("archery_test")
assert loaded is not None
assert len(loaded.free_variables) == 1
assert loaded.free_variables[0].name == "draw_weight"
assert loaded.free_variables[0].id is not None
assert len(loaded.metric_formulas) == 1
assert loaded.metric_formulas[0].formula == "draw_weight * 1.5"
def test_free_variable_and_formula_crud(repo):
domain = repo.add_domain(Domain(name="crud_test"))
fv = repo.add_free_variable(
domain.id,
FreeVariable(name="x", floor_formula="0", ceiling_formula="100", sort_order=0),
)
mf = repo.add_metric_formula(
domain.id, MetricFormula(metric_name="m", formula="x * 2")
)
repo.update_free_variable(
fv.id, FreeVariable(name="x", floor_formula="1", ceiling_formula="200", sort_order=0)
)
repo.update_metric_formula(mf.id, MetricFormula(metric_name="m", formula="x * 3"))
loaded = repo.get_domain_by_id(domain.id)
assert loaded.free_variables[0].floor_formula == "1"
assert loaded.free_variables[0].ceiling_formula == "200"
assert loaded.metric_formulas[0].formula == "x * 3"
repo.delete_free_variable(fv.id)
repo.delete_metric_formula(mf.id)
loaded = repo.get_domain_by_id(domain.id)
assert loaded.free_variables == []
assert loaded.metric_formulas == []
def test_combination_save_and_dedup(repo):
e1 = repo.add_entity(Entity(name="A", dimension="platform"))
e2 = repo.add_entity(Entity(name="B", dimension="actuator"))