split actuators from energy storage

This commit is contained in:
2026-03-04 14:18:52 -06:00
parent e99a14d087
commit 216879bdd5
13 changed files with 601 additions and 681 deletions

View File

@@ -115,7 +115,7 @@ def domain_list(ctx):
@click.argument("domain_name")
@click.option("--passes", "-p", default="1,2,3", help="Comma-separated pass numbers to run")
@click.option("--threshold", "-t", default=0.1, type=float, help="Score threshold for pass 3")
@click.option("--dimensions", "-d", default="platform,power_source",
@click.option("--dimensions", "-d", default="platform,actuator,energy_storage",
help="Comma-separated dimension names")
@click.pass_context
def run(ctx, domain_name, passes, threshold, dimensions):

View File

@@ -191,7 +191,11 @@ class ConstraintResolver:
def _check_unmet_requirements(
self, all_deps: list[tuple[str, Dependency]], result: ConstraintResult
) -> None:
"""Rule 5: Required condition not provided by any entity → conditional."""
"""Rule 5: Required condition not provided by any entity → conditional.
Energy-category requirements (e.g. energy_form) are hard blocks —
you cannot power an actuator with an incompatible energy source.
"""
provides = {(d.key, d.value) for _, d in all_deps if d.constraint_type == "provides"}
# Ambient conditions that don't need to be explicitly provided
ambient = {
@@ -207,7 +211,11 @@ class ConstraintResolver:
continue # Infrastructure is external, not checked here
key_val = (dep.key, dep.value)
if key_val not in provides and key_val not in ambient:
result.warnings.append(
msg = (
f"{name} requires {dep.key}={dep.value} "
f"but no entity in this combination provides it"
)
if dep.category == "energy":
result.violations.append(msg)
else:
result.warnings.append(msg)

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@ def admin_index():
def reseed():
repo = get_repo()
counts = load_transport_seed(repo)
total = counts["platforms"] + counts["power_sources"]
total = counts["platforms"] + counts["actuators"] + counts["energy_storages"]
flash(
f"Reseed complete — added {total} entities, {counts['domains']} domains.",
"success",
@@ -43,7 +43,7 @@ def wipe_and_reseed():
repo = get_repo()
repo.clear_all()
counts = load_transport_seed(repo)
total = counts["platforms"] + counts["power_sources"]
total = counts["platforms"] + counts["actuators"] + counts["energy_storages"]
flash(
f"Wiped all data and reseeded — {total} entities, {counts['domains']} domains.",
"success",

View File

@@ -118,7 +118,7 @@
<h3>Entities</h3>
<p>
The building blocks. Each entity belongs to a <em>dimension</em>
(e.g. platform, power_source) and carries typed dependencies
(e.g. platform, actuator, energy_storage) and carries typed dependencies
that define its physical properties and constraints.
</p>
<div class="concept-examples">

View File

@@ -78,9 +78,10 @@ def spaceship():
def solar_sail():
return Entity(
name="Solar Sail",
dimension="power_source",
dimension="actuator",
description="Propulsion via radiation pressure",
dependencies=[
Dependency("energy", "energy_form", "radiation_pressure", None, "requires"),
Dependency("environment", "atmosphere", "vacuum_or_thin", None, "requires"),
Dependency("force", "power_density_w_kg", "0.01", "W/kg", "provides"),
Dependency("environment", "medium", "space", None, "requires"),
@@ -88,13 +89,27 @@ def solar_sail():
)
@pytest.fixture
def solar_radiation():
return Entity(
name="Solar Radiation",
dimension="energy_storage",
description="Photon flux from a nearby star",
dependencies=[
Dependency("energy", "energy_form", "radiation_pressure", None, "provides"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
],
)
@pytest.fixture
def human_pedalling():
return Entity(
name="Human Pedalling",
dimension="power_source",
dimension="actuator",
description="Human-powered via pedalling",
dependencies=[
Dependency("energy", "energy_form", "biological", None, "requires"),
Dependency("force", "power_density_w_kg", "1.5", "W/kg", "provides"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
],
@@ -102,14 +117,43 @@ def human_pedalling():
@pytest.fixture
def nuclear_reactor():
def food_calories():
return Entity(
name="Modular Nuclear Reactor",
dimension="power_source",
description="Small modular nuclear fission reactor",
name="Food/Calories",
dimension="energy_storage",
description="Metabolic energy from food",
dependencies=[
Dependency("energy", "energy_form", "biological", None, "provides"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"),
],
)
@pytest.fixture
def nuclear_thermal_drive():
return Entity(
name="Nuclear Thermal Drive",
dimension="actuator",
description="Nuclear fission reactor for thrust",
dependencies=[
Dependency("energy", "energy_form", "nuclear_thermal", None, "requires"),
Dependency("force", "power_density_w_kg", "50", "W/kg", "provides"),
Dependency("physical", "mass_kg", "2000", "kg", "range_min"),
Dependency("physical", "mass_kg", "1500", "kg", "range_min"),
],
)
@pytest.fixture
def nuclear_fuel():
return Entity(
name="Nuclear Fuel",
dimension="energy_storage",
description="Enriched uranium fuel rods",
dependencies=[
Dependency("energy", "energy_form", "nuclear_thermal", None, "provides"),
Dependency("physical", "mass_kg", "500", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "500000", "Wh/kg", "provides"),
],
)
@@ -118,11 +162,12 @@ def nuclear_reactor():
def hydrogen_engine():
return Entity(
name="Hydrogen Combustion Engine",
dimension="power_source",
description="Hydrogen fuel cell",
dimension="actuator",
description="Hydrogen combustion engine",
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "requires"),
Dependency("force", "power_density_w_kg", "1500", "W/kg", "provides"),
Dependency("physical", "mass_kg", "30", "kg", "range_min"),
Dependency("physical", "mass_kg", "25", "kg", "range_min"),
],
)
@@ -131,12 +176,41 @@ def hydrogen_engine():
def ice_engine():
return Entity(
name="Internal Combustion Engine",
dimension="power_source",
description="Gas-powered engine",
dimension="actuator",
description="Spark-ignition engine",
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "requires"),
Dependency("force", "power_density_w_kg", "1000", "W/kg", "provides"),
Dependency("environment", "atmosphere", "standard", None, "requires"),
Dependency("physical", "mass_kg", "50", "kg", "range_min"),
Dependency("physical", "mass_kg", "40", "kg", "range_min"),
],
)
@pytest.fixture
def gasoline():
return Entity(
name="Gasoline",
dimension="energy_storage",
description="Liquid hydrocarbon fuel",
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "provides"),
Dependency("physical", "mass_kg", "10", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "1500", "Wh/kg", "provides"),
],
)
@pytest.fixture
def hydrogen():
return Entity(
name="Hydrogen",
dimension="energy_storage",
description="Compressed hydrogen gas",
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "provides"),
Dependency("physical", "mass_kg", "5", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "600", "Wh/kg", "provides"),
],
)

View File

@@ -32,7 +32,7 @@ def test_reseed_after_clear_restores_data(seeded_repo):
counts = load_transport_seed(repo)
assert len(repo.list_entities()) == original_entities
assert len(repo.list_domains()) == original_domains
assert counts["platforms"] + counts["power_sources"] == original_entities
assert counts["platforms"] + counts["actuators"] + counts["energy_storages"] == original_entities
def test_additive_reseed_no_duplicates(seeded_repo):
@@ -44,5 +44,5 @@ def test_additive_reseed_no_duplicates(seeded_repo):
assert len(repo.list_entities()) == before
assert counts["platforms"] == 0
assert counts["power_sources"] == 0
assert counts["actuators"] + counts["energy_storages"] == 0
assert counts["domains"] == 0

View File

@@ -7,20 +7,21 @@ from physcom.models.entity import Entity
def test_generates_cartesian_product(seeded_repo):
from physcom.seed.transport_example import PLATFORMS, POWER_SOURCES
from physcom.seed.transport_example import PLATFORMS, ACTUATORS, ENERGY_STORAGES
combos = generate_combinations(seeded_repo, ["platform", "power_source"])
expected = len(PLATFORMS) * len(POWER_SOURCES)
combos = generate_combinations(seeded_repo, ["platform", "actuator", "energy_storage"])
expected = len(PLATFORMS) * len(ACTUATORS) * len(ENERGY_STORAGES)
assert len(combos) == expected
def test_each_combo_has_one_per_dimension(seeded_repo):
combos = generate_combinations(seeded_repo, ["platform", "power_source"])
combos = generate_combinations(seeded_repo, ["platform", "actuator", "energy_storage"])
for combo in combos:
dims = [e.dimension for e in combo.entities]
assert "platform" in dims
assert "power_source" in dims
assert len(combo.entities) == 2
assert "actuator" in dims
assert "energy_storage" in dims
assert len(combo.entities) == 3
def test_missing_dimension_raises(seeded_repo):

View File

@@ -5,37 +5,37 @@ from physcom.models.combination import Combination
from physcom.models.entity import Entity, Dependency
def test_compatible_ground_combo(bicycle, human_pedalling):
"""Bicycle + Human Pedalling should be valid."""
def test_compatible_ground_combo(bicycle, human_pedalling, food_calories):
"""Bicycle + Human Pedalling + Food/Calories should be valid."""
resolver = ConstraintResolver()
combo = Combination(entities=[bicycle, human_pedalling])
combo = Combination(entities=[bicycle, human_pedalling, food_calories])
result = resolver.resolve(combo)
assert result.status != "p1_fail", f"Unexpected block: {result.violations}"
def test_solar_sail_blocks_with_walking(walking, solar_sail):
def test_solar_sail_blocks_with_walking(walking, solar_sail, solar_radiation):
"""Walking (ground) + Solar Sail (space) should be blocked by medium mutex."""
resolver = ConstraintResolver()
combo = Combination(entities=[walking, solar_sail])
combo = Combination(entities=[walking, solar_sail, solar_radiation])
result = resolver.resolve(combo)
assert result.status == "p1_fail"
assert any("mutually exclusive" in v for v in result.violations)
def test_spaceship_compatible_with_solar_sail(spaceship, solar_sail):
def test_spaceship_compatible_with_solar_sail(spaceship, solar_sail, solar_radiation):
"""Spaceship + Solar Sail both need space/vacuum — should not conflict."""
resolver = ConstraintResolver()
combo = Combination(entities=[spaceship, solar_sail])
combo = Combination(entities=[spaceship, solar_sail, solar_radiation])
result = resolver.resolve(combo)
# Should not be blocked by atmosphere or medium
medium_blocks = [v for v in result.violations if "mutually exclusive" in v]
assert len(medium_blocks) == 0
def test_nuclear_reactor_blocks_with_bicycle(bicycle, nuclear_reactor):
"""Nuclear reactor min_mass=2000kg vs bicycle max_mass=30kg → range incompatibility."""
def test_nuclear_drive_blocks_with_bicycle(bicycle, nuclear_thermal_drive, nuclear_fuel):
"""Nuclear drive min_mass=1500kg + fuel min_mass=500kg vs bicycle max_mass=30kg → range incompatibility."""
resolver = ConstraintResolver()
combo = Combination(entities=[bicycle, nuclear_reactor])
combo = Combination(entities=[bicycle, nuclear_thermal_drive, nuclear_fuel])
result = resolver.resolve(combo)
assert result.status == "p1_fail"
assert any("mass" in v.lower() for v in result.violations)
@@ -50,7 +50,7 @@ def test_power_density_mismatch_blocks():
],
)
power = Entity(
name="TinyPower", dimension="power_source",
name="TinyPower", dimension="actuator",
dependencies=[
Dependency("force", "power_density_w_kg", "0.01", "W/kg", "provides"),
],
@@ -71,7 +71,7 @@ def test_power_density_under_powered_warning():
],
)
power = Entity(
name="WeakPower", dimension="power_source",
name="WeakPower", dimension="actuator",
dependencies=[
Dependency("force", "power_density_w_kg", "50", "W/kg", "provides"),
],
@@ -91,7 +91,7 @@ def test_requires_vs_excludes():
dependencies=[Dependency("environment", "oxygen", "true", None, "requires")],
)
b = Entity(
name="B", dimension="power_source",
name="B", dimension="actuator",
dependencies=[Dependency("environment", "oxygen", "true", None, "excludes")],
)
resolver = ConstraintResolver()
@@ -101,21 +101,21 @@ def test_requires_vs_excludes():
assert any("excludes" in v for v in result.violations)
def test_ice_engine_blocks_with_spaceship(spaceship, ice_engine):
def test_ice_engine_blocks_with_spaceship(spaceship, ice_engine, gasoline):
"""ICE requires standard atmosphere, spaceship requires vacuum_or_thin → mutex."""
resolver = ConstraintResolver()
combo = Combination(entities=[spaceship, ice_engine])
combo = Combination(entities=[spaceship, ice_engine, gasoline])
result = resolver.resolve(combo)
assert result.status == "p1_fail"
assert any("atmosphere" in v for v in result.violations)
def test_hydrogen_bicycle_valid(bicycle, hydrogen_engine):
def test_hydrogen_bicycle_valid(bicycle, hydrogen_engine, hydrogen):
"""Hydrogen bike — the README's example of a plausible novel concept."""
resolver = ConstraintResolver()
combo = Combination(entities=[bicycle, hydrogen_engine])
combo = Combination(entities=[bicycle, hydrogen_engine, hydrogen])
result = resolver.resolve(combo)
# Should pass constraints (mass range is compatible: h2 min 30kg, bike max 30kg)
# Should pass constraints (mass range is compatible: h2 engine min 25kg, bike max 30kg)
# This is actually a borderline case — let's just verify no hard physics blocks
range_blocks = [v for v in result.violations if "mutually exclusive" in v or "atmosphere" in v]
assert len(range_blocks) == 0
@@ -129,14 +129,14 @@ def test_energy_density_deficit_blocks():
Dependency("physical", "energy_density_wh_kg", "2000", "Wh/kg", "range_min"),
],
)
power = Entity(
name="Battery", dimension="power_source",
storage = Entity(
name="Battery", dimension="energy_storage",
dependencies=[
Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"),
],
)
resolver = ConstraintResolver()
combo = Combination(entities=[platform, power])
combo = Combination(entities=[platform, storage])
result = resolver.resolve(combo)
assert result.status == "p1_fail"
assert any("energy density deficit" in v for v in result.violations)
@@ -150,14 +150,14 @@ def test_energy_density_under_density_warning():
Dependency("physical", "energy_density_wh_kg", "400", "Wh/kg", "range_min"),
],
)
power = Entity(
name="Battery", dimension="power_source",
storage = Entity(
name="Battery", dimension="energy_storage",
dependencies=[
Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"),
],
)
resolver = ConstraintResolver()
combo = Combination(entities=[platform, power])
combo = Combination(entities=[platform, storage])
result = resolver.resolve(combo)
assert result.status != "p1_fail"
assert any("under-density" in w for w in result.warnings)
@@ -172,14 +172,14 @@ def test_energy_density_no_constraint_if_no_provider():
],
)
# Solar Sail-style: no energy_density_wh_kg declared
power = Entity(
name="Solar Sail", dimension="power_source",
actuator = Entity(
name="Solar Sail", dimension="actuator",
dependencies=[
Dependency("force", "power_density_w_kg", "0.01", "W/kg", "provides"),
],
)
resolver = ConstraintResolver()
combo = Combination(entities=[platform, power])
combo = Combination(entities=[platform, actuator])
result = resolver.resolve(combo)
density_violations = [v for v in result.violations if "energy density" in v]
assert len(density_violations) == 0

View File

@@ -13,10 +13,10 @@ def test_pass1_filters_impossible_combos(seeded_repo):
scorer = Scorer(domain)
pipeline = Pipeline(seeded_repo, resolver, scorer)
result = pipeline.run(domain, ["platform", "power_source"], passes=[1])
result = pipeline.run(domain, ["platform", "actuator", "energy_storage"], passes=[1])
from physcom.seed.transport_example import PLATFORMS, POWER_SOURCES
expected = len(PLATFORMS) * len(POWER_SOURCES)
from physcom.seed.transport_example import PLATFORMS, ACTUATORS, ENERGY_STORAGES
expected = len(PLATFORMS) * len(ACTUATORS) * len(ENERGY_STORAGES)
assert result.total_generated == expected
assert result.pass1_failed > 0
assert result.pass1_valid + result.pass1_conditional + result.pass1_failed == expected
@@ -30,7 +30,7 @@ def test_pass123_produces_scored_results(seeded_repo):
pipeline = Pipeline(seeded_repo, resolver, scorer)
result = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3, 5],
)
@@ -50,7 +50,7 @@ def test_pass4_with_mock_llm(seeded_repo):
pipeline = Pipeline(seeded_repo, resolver, scorer, llm=mock_llm)
result = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3, 4, 5],
)
@@ -65,7 +65,7 @@ def test_blocked_combos_not_scored(seeded_repo):
pipeline = Pipeline(seeded_repo, resolver, scorer)
result = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.0, passes=[1, 2, 3, 5],
)

View File

@@ -9,7 +9,7 @@ def test_pipeline_run_lifecycle(seeded_repo):
"""Pipeline run should transition: pending -> running -> completed."""
repo = seeded_repo
domain = repo.get_domain("urban_commuting")
config = {"passes": [1, 2, 3], "threshold": 0.1, "dimensions": ["platform", "power_source"]}
config = {"passes": [1, 2, 3], "threshold": 0.1, "dimensions": ["platform", "actuator", "energy_storage"]}
run_id = repo.create_pipeline_run(domain.id, config)
run = repo.get_pipeline_run(run_id)
@@ -19,10 +19,10 @@ def test_pipeline_run_lifecycle(seeded_repo):
scorer = Scorer(domain)
pipeline = Pipeline(repo, resolver, scorer)
pipeline.run(domain, ["platform", "power_source"], passes=[1, 2, 3], run_id=run_id)
pipeline.run(domain, ["platform", "actuator", "energy_storage"], passes=[1, 2, 3], run_id=run_id)
from physcom.seed.transport_example import PLATFORMS, POWER_SOURCES
expected = len(PLATFORMS) * len(POWER_SOURCES)
from physcom.seed.transport_example import PLATFORMS, ACTUATORS, ENERGY_STORAGES
expected = len(PLATFORMS) * len(ACTUATORS) * len(ENERGY_STORAGES)
run = repo.get_pipeline_run(run_id)
assert run["status"] == "completed"
@@ -35,7 +35,7 @@ def test_pipeline_run_failed(seeded_repo):
"""Pipeline run should be marked failed on error."""
repo = seeded_repo
domain = repo.get_domain("urban_commuting")
config = {"passes": [1], "threshold": 0.1, "dimensions": ["platform", "power_source"]}
config = {"passes": [1], "threshold": 0.1, "dimensions": ["platform", "actuator", "energy_storage"]}
run_id = repo.create_pipeline_run(domain.id, config)
# Manually mark as failed (simulating what the web route does on exception)
@@ -58,7 +58,7 @@ def test_resume_skips_completed_combos(seeded_repo):
# First run: passes 1-3
run_id_1 = repo.create_pipeline_run(domain.id, {"passes": [1, 2, 3]})
result1 = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3], run_id=run_id_1,
)
assert result1.pass2_estimated > 0
@@ -67,7 +67,7 @@ def test_resume_skips_completed_combos(seeded_repo):
# Second run: same passes — should skip all combos (already pass_reached >= 3)
run_id_2 = repo.create_pipeline_run(domain.id, {"passes": [1, 2, 3]})
result2 = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3], run_id=run_id_2,
)
# pass2_estimated still counted (reloaded from DB) but no new estimation work
@@ -93,7 +93,7 @@ def test_cancellation_stops_processing(seeded_repo):
repo.update_pipeline_run(run_id, status="cancelled")
result = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3], run_id=run_id,
)
@@ -115,7 +115,7 @@ def test_status_guard_no_downgrade_reviewed(seeded_repo):
# Run pipeline to get scored combos
result = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3],
)
@@ -148,7 +148,7 @@ def test_human_notes_preserved_on_rerun(seeded_repo):
# First run
pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3],
)
@@ -177,7 +177,7 @@ def test_human_notes_preserved_on_rerun(seeded_repo):
# Re-run pipeline
pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3],
)
@@ -212,7 +212,7 @@ def test_get_combo_pass_reached(seeded_repo):
pipeline = Pipeline(repo, resolver, scorer)
pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3],
)
@@ -238,7 +238,7 @@ def test_blocked_combos_have_results(seeded_repo):
pipeline = Pipeline(repo, resolver, scorer)
result = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3],
)
@@ -269,7 +269,7 @@ def test_all_passes_run_and_tracked(seeded_repo):
run_id = repo.create_pipeline_run(domain.id, {"passes": [1, 2, 3]})
result = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3], run_id=run_id,
)
@@ -289,7 +289,9 @@ def test_save_combination_loads_existing_status(seeded_repo):
repo = seeded_repo
from physcom.models.combination import Combination
entities = repo.list_entities(dimension="platform")[:1] + repo.list_entities(dimension="power_source")[:1]
entities = (repo.list_entities(dimension="platform")[:1]
+ repo.list_entities(dimension="actuator")[:1]
+ repo.list_entities(dimension="energy_storage")[:1])
combo = Combination(entities=entities)
saved = repo.save_combination(combo)
assert saved.status == "pending"
@@ -316,7 +318,7 @@ def test_p3_fail_below_threshold(seeded_repo):
# Use a very high threshold so most combos fail pass 3
result = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.99, passes=[1, 2, 3],
)
@@ -350,7 +352,7 @@ def test_p4_fail_implausible(seeded_repo):
pipeline = Pipeline(repo, resolver, scorer, llm=mock_llm)
result = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.0, passes=[1, 2, 3, 4],
)
@@ -381,7 +383,7 @@ def test_p4_pass_plausible(seeded_repo):
pipeline = Pipeline(repo, resolver, scorer, llm=mock_llm)
result = pipeline.run(
domain, ["platform", "power_source"],
domain, ["platform", "actuator", "energy_storage"],
score_threshold=0.01, passes=[1, 2, 3, 4],
)

View File

@@ -35,7 +35,7 @@ def test_add_and_get_entity(repo):
def test_list_entities_by_dimension(repo):
repo.add_entity(Entity(name="A", dimension="platform"))
repo.add_entity(Entity(name="B", dimension="platform"))
repo.add_entity(Entity(name="C", dimension="power_source"))
repo.add_entity(Entity(name="C", dimension="actuator"))
platforms = repo.list_entities(dimension="platform")
assert len(platforms) == 2
@@ -65,7 +65,7 @@ def test_add_and_get_domain(repo):
def test_combination_save_and_dedup(repo):
e1 = repo.add_entity(Entity(name="A", dimension="platform"))
e2 = repo.add_entity(Entity(name="B", dimension="power_source"))
e2 = repo.add_entity(Entity(name="B", dimension="actuator"))
from physcom.models.combination import Combination
combo = Combination(entities=[e1, e2])
@@ -79,12 +79,14 @@ def test_combination_save_and_dedup(repo):
def test_seed_loads(seeded_repo):
from physcom.seed.transport_example import PLATFORMS, POWER_SOURCES, ALL_DOMAINS
from physcom.seed.transport_example import PLATFORMS, ACTUATORS, ENERGY_STORAGES, ALL_DOMAINS
platforms = seeded_repo.list_entities(dimension="platform")
power_sources = seeded_repo.list_entities(dimension="power_source")
actuators = seeded_repo.list_entities(dimension="actuator")
energy_storages = seeded_repo.list_entities(dimension="energy_storage")
assert len(platforms) == len(PLATFORMS)
assert len(power_sources) == len(POWER_SOURCES)
assert len(actuators) == len(ACTUATORS)
assert len(energy_storages) == len(ENERGY_STORAGES)
domains = seeded_repo.list_domains()
assert len(domains) == len(ALL_DOMAINS)

View File

@@ -71,7 +71,7 @@ class TestScorer:
scorer = Scorer(urban_domain)
combo = Combination(entities=[
Entity(name="Car", dimension="platform"),
Entity(name="ICE", dimension="power_source"),
Entity(name="ICE", dimension="actuator"),
])
combo.id = 1
raw = {"speed": 60.0, "cost_efficiency": 0.5, "safety": 0.7,