完善系统仿真优化计划交互

This commit is contained in:
huojiarong committed 2026-09-15 08:32:31 +00:00
1 parent 42ffdfff7d
commit 50c9f59d78
4 files changed
+812 -24

No files matched your search

+489 -6
View File
@@ -14,6 +14,7 @@ import hashlib
import importlib.util
import json
import math
import stat
import sys
import tempfile
import unittest
@@ -107,9 +108,15 @@ def _json_bytes(value: object) -> bytes:
def _valid_spec(
*,
seed: int = 12345,
population_size: int = 4,
mutation_factor: float = 0.8,
crossover_probability: float = 0.7,
max_simulation_runs: int = 6,
lower: float = 0.0,
upper: float = 4.0,
relative_tolerance: float = 1e-12,
absolute_tolerance: float = 1e-12,
constraint_scale: float = 1.0,
) -> dict[str, object]:
return {
"optimizationSchemaVersion": 1,
@@ -138,23 +145,23 @@ def _valid_spec(
"lower": None,
"upper": 4.0,
"tolerance": 0.0,
"scale": 1.0,
"scale": constraint_scale,
}
],
"algorithm": {
"name": "differentialEvolution",
"seed": seed,
"populationSize": 4,
"mutationFactor": 0.8,
"crossoverProbability": 0.7,
"populationSize": population_size,
"mutationFactor": mutation_factor,
"crossoverProbability": crossover_probability,
},
"budget": {
"maxSimulationRuns": max_simulation_runs,
"maxWallSeconds": 60.0,
},
"validation": {
"relativeTolerance": 1e-12,
"absoluteTolerance": 1e-12,
"relativeTolerance": relative_tolerance,
"absoluteTolerance": absolute_tolerance,
},
}
@@ -207,9 +214,15 @@ def _make_plan(
*,
output_name: str = "optimization-output",
seed: int = 12345,
population_size: int = 4,
mutation_factor: float = 0.8,
crossover_probability: float = 0.7,
max_simulation_runs: int = 6,
lower: float = 0.0,
upper: float = 4.0,
relative_tolerance: float = 1e-12,
absolute_tolerance: float = 1e-12,
constraint_scale: float = 1.0,
inspection: dict[str, object] | None = None,
) -> optimization.RuntimePlan:
directory.mkdir(parents=True, exist_ok=True)
@@ -217,9 +230,15 @@ def _make_plan(
source_path.write_bytes(_json_bytes(PROJECT))
spec_payload = _valid_spec(
seed=seed,
population_size=population_size,
mutation_factor=mutation_factor,
crossover_probability=crossover_probability,
max_simulation_runs=max_simulation_runs,
lower=lower,
upper=upper,
relative_tolerance=relative_tolerance,
absolute_tolerance=absolute_tolerance,
constraint_scale=constraint_scale,
)
spec_path = directory / "optimization-spec.json"
spec_path.write_bytes(_json_bytes(spec_payload))
@@ -268,6 +287,18 @@ def _xml_parameter_values(xml: bytes) -> dict[tuple[str, str], str]:
return values
def _recursive_mapping_keys(value: object) -> list[str]:
keys: list[str] = []
if isinstance(value, dict):
for key, child in value.items():
keys.append(str(key))
keys.extend(_recursive_mapping_keys(child))
elif isinstance(value, list):
for child in value:
keys.extend(_recursive_mapping_keys(child))
return keys
class OptimizationSpecTests(unittest.TestCase):
def test_spec_rejects_unknown_fields_at_every_nested_contract(self) -> None:
parsed = optimization.parse_optimization_spec(_valid_spec())
@@ -807,6 +838,458 @@ class CandidateIsolationTests(unittest.TestCase):
self.assertEqual(plan.baseline_xml, BASELINE_XML)
class PlanPresentationTests(unittest.TestCase):
_FORBIDDEN_KEY_FRAGMENTS = (
"sha256",
"hash",
"token",
"algorithm",
"seed",
"population",
"searchrunlimit",
"fullgenerations",
"requiredassertions",
"parametercontinuity",
)
@staticmethod
def _recommended_plan(
directory: Path,
*,
max_simulation_runs: int = 25,
output_name: str = "optimization-output",
inspection: dict[str, object] | None = None,
) -> optimization.RuntimePlan:
return _make_plan(
directory,
output_name=output_name,
seed=optimization.RECOMMENDED_ALGORITHM_SEED,
population_size=optimization.RECOMMENDED_POPULATION_SIZE,
mutation_factor=optimization.RECOMMENDED_MUTATION_FACTOR,
crossover_probability=(
optimization.RECOMMENDED_CROSSOVER_PROBABILITY
),
max_simulation_runs=max_simulation_runs,
relative_tolerance=(
optimization.RECOMMENDED_VALIDATION_RELATIVE_TOLERANCE
),
absolute_tolerance=(
optimization.RECOMMENDED_VALIDATION_ABSOLUTE_TOLERANCE
),
constraint_scale=4.0,
inspection=inspection,
)
@staticmethod
def _command_args(
plan: optimization.RuntimePlan,
*,
present: bool = False,
plan_file: Path | None = None,
) -> argparse.Namespace:
return argparse.Namespace(
input=str(plan.source.path),
spec=str(plan.spec_source.path),
output_dir=str(plan.output_directory),
base_url=plan.base_url,
timeout=plan.timeout,
present=present,
plan_file=str(plan_file) if plan_file is not None else None,
)
def assert_safe_presentation(self, payload: object) -> None:
normalized_keys = {
"".join(character for character in key.casefold() if character.isalnum())
for key in _recursive_mapping_keys(payload)
}
leaked = {
key
for key in normalized_keys
if any(
fragment in key for fragment in self._FORBIDDEN_KEY_FRAGMENTS
)
}
self.assertEqual(leaked, set())
def test_presentation_is_a_closed_human_facing_view(self) -> None:
with tempfile.TemporaryDirectory() as directory_text:
inspection = _inspection()
result_variables = inspection["system"]["resultVariables"]
assert isinstance(result_variables, list)
for variable in result_variables:
assert isinstance(variable, dict)
variable["backendOnly"] = "BACKEND-METADATA-SENTINEL"
plan = self._recommended_plan(
Path(directory_text),
max_simulation_runs=17,
inspection=inspection,
)
presentation = plan.presentation_dict()
self.assertEqual(
set(presentation),
{
"objective",
"designVariables",
"constraints",
"runLimits",
"assumptions",
"outputDirectory",
"sourceWillBeOverwritten",
},
)
objective = presentation["objective"]
self.assertIsInstance(objective, dict)
self.assertEqual(objective["resultKey"], "sensor.output")
objective_text = json.dumps(objective, ensure_ascii=False)
self.assertIn("final", objective_text)
self.assertIn("minimize", objective_text)
self.assertIn("m", objective_text)
design_variables = presentation["designVariables"]
self.assertIsInstance(design_variables, list)
self.assertEqual(len(design_variables), 1)
design_variable = design_variables[0]
self.assertEqual(
set(design_variable),
{
"id",
"componentId",
"parameter",
"label",
"quantity",
"current",
"lower",
"upper",
"unit",
},
)
self.assertEqual(design_variable["id"], "gain")
self.assertEqual(design_variable["componentId"], "component-a")
self.assertEqual(design_variable["parameter"], "gain")
self.assertEqual(design_variable["current"], 2.0)
self.assertEqual(design_variable["lower"], 0.0)
self.assertEqual(design_variable["upper"], 4.0)
self.assertEqual(design_variable["unit"], "m")
constraints = presentation["constraints"]
self.assertIsInstance(constraints, list)
self.assertEqual(len(constraints), 1)
constraint_text = json.dumps(constraints[0], ensure_ascii=False)
self.assertIn("sensor.limit", constraint_text)
self.assertIn("maximum", constraint_text)
self.assertIn("4.0", constraint_text)
self.assertEqual(
presentation["runLimits"],
{
"maxSimulationBudgetSlots": 17,
"searchLaunchTimeLimitSeconds": 60.0,
"inFlightSimulationMayFinishAfterLimit": True,
"freshVerificationSlotsReserved": 1,
"verificationRunsOnlyIfFeasibleCandidateFound": True,
},
)
self.assertEqual(
presentation["assumptions"],
{
"continuousLinearSiDesignVariableIds": ["gain"],
"portsTopologyModesUnchanged": True,
},
)
self.assertEqual(
presentation["outputDirectory"], str(plan.output_directory)
)
self.assertFalse(presentation["sourceWillBeOverwritten"])
self.assert_safe_presentation(presentation)
serialized = json.dumps(presentation, ensure_ascii=False)
for forbidden_value in (
plan.source.sha256,
plan.spec_source.sha256,
plan.confirmation_token,
hashlib.sha256(plan.baseline_xml).hexdigest(),
"differentialEvolution",
"BACKEND-METADATA-SENTINEL",
plan.base_url,
):
self.assertNotIn(forbidden_value, serialized)
def test_presentation_only_adds_attention_when_budget_is_insufficient(self) -> None:
with tempfile.TemporaryDirectory() as directory_text:
root = Path(directory_text)
sufficient = self._recommended_plan(
root / "sufficient", max_simulation_runs=17
)
insufficient = self._recommended_plan(
root / "insufficient", max_simulation_runs=9
)
sufficient_presentation = sufficient.presentation_dict()
insufficient_presentation = insufficient.presentation_dict()
self.assertEqual(sufficient.public_dict()["warnings"], [])
self.assertNotIn("warnings", sufficient_presentation)
self.assertNotIn("attention", sufficient_presentation)
self.assertEqual(
[item["code"] for item in insufficient.public_dict()["warnings"]],
["OPTIMIZATION_BUDGET_INITIAL_POPULATION_ONLY"],
)
self.assertNotIn("warnings", insufficient_presentation)
self.assertIn("attention", insufficient_presentation)
self.assertTrue(insufficient_presentation["attention"])
attention_text = json.dumps(
insufficient_presentation["attention"], ensure_ascii=False
)
self.assertNotIn(
"OPTIMIZATION_BUDGET_INITIAL_POPULATION_ONLY", attention_text
)
self.assertNotIn("generation", attention_text.casefold())
self.assert_safe_presentation(insufficient_presentation)
def test_presentation_discloses_only_non_default_confirmed_settings(
self,
) -> None:
with tempfile.TemporaryDirectory() as directory_text:
plan = _make_plan(Path(directory_text))
presentation = plan.presentation_dict()
self.assertEqual(
presentation["nonDefaultSettings"],
{
"search": {
"randomSeed": 12345,
"populationSize": 4,
},
"verification": {
"relativeTolerance": 1e-12,
"absoluteTolerance": 1e-12,
},
"constraintRankingScales": [
{"id": "limit", "scale": 1.0}
],
},
)
serialized = json.dumps(presentation, ensure_ascii=False)
for forbidden_value in (
plan.source.sha256,
plan.spec_source.sha256,
plan.confirmation_token,
plan.base_url,
"differentialEvolution",
):
self.assertNotIn(forbidden_value, serialized)
def test_command_plan_preserves_legacy_audit_stdout_without_new_flags(
self,
) -> None:
with tempfile.TemporaryDirectory() as directory_text:
plan = _make_plan(Path(directory_text))
args = self._command_args(plan)
with mock.patch.object(
optimization, "build_runtime_plan", return_value=plan
), mock.patch.object(
optimization.simulation, "emit_json"
) as emit_json:
result = optimization.command_plan(args)
self.assertEqual(result, 0)
emit_json.assert_called_once_with(plan.public_dict())
self.assertFalse(plan.output_directory.exists())
def test_command_plan_present_saves_audit_plan_and_emits_safe_wrapper(
self,
) -> None:
with tempfile.TemporaryDirectory() as directory_text:
root = Path(directory_text)
plan = self._recommended_plan(root)
plan_file = root / "saved-optimization-plan.json"
args = self._command_args(plan, present=True, plan_file=plan_file)
with mock.patch.object(
optimization, "build_runtime_plan", return_value=plan
), mock.patch.object(
optimization.simulation, "emit_json"
) as emit_json, mock.patch.object(
optimization,
"_write_new_private_file",
wraps=optimization._write_new_private_file,
) as write_new_file:
result = optimization.command_plan(args)
self.assertEqual(result, 0)
write_new_file.assert_called_once()
written_path, written_bytes = write_new_file.call_args.args
self.assertEqual(written_path, plan_file)
self.assertEqual(json.loads(written_bytes), plan.public_dict())
self.assertEqual(
json.loads(plan_file.read_text(encoding="utf-8")),
plan.public_dict(),
)
self.assertEqual(stat.S_IMODE(plan_file.stat().st_mode), 0o600)
self.assertEqual(
list(root.glob(f".{plan_file.name}.*.tmp")),
[],
)
expected_stdout = {
"ok": True,
"command": "optimization-plan",
"view": "presentation",
"confirmationRequired": True,
"presentation": plan.presentation_dict(),
}
emit_json.assert_called_once_with(expected_stdout)
self.assert_safe_presentation(expected_stdout["presentation"])
def test_command_plan_requires_present_and_plan_file_together(self) -> None:
with tempfile.TemporaryDirectory() as directory_text:
root = Path(directory_text)
plan = _make_plan(root)
cases = (
self._command_args(plan, present=True),
self._command_args(
plan,
plan_file=root / "unpaired-plan.json",
),
)
for args in cases:
with self.subTest(
present=args.present, plan_file=args.plan_file
), mock.patch.object(
optimization, "build_runtime_plan", return_value=plan
) as build_runtime_plan, mock.patch.object(
optimization.simulation, "emit_json"
) as emit_json:
with self.assertRaises(optimization.simulation.InputError):
optimization.command_plan(args)
build_runtime_plan.assert_not_called()
emit_json.assert_not_called()
self.assertFalse((root / "unpaired-plan.json").exists())
def test_command_plan_rejects_plan_file_inside_or_around_output_directory(
self,
) -> None:
with tempfile.TemporaryDirectory() as directory_text:
root = Path(directory_text)
plan = _make_plan(root, output_name="reserved/output")
cases = {
"same": plan.output_directory,
"descendant": plan.output_directory / "plan.json",
"ancestor": plan.output_directory.parent,
}
for label, plan_file in cases.items():
args = self._command_args(
plan, present=True, plan_file=plan_file
)
with self.subTest(label=label), mock.patch.object(
optimization, "build_runtime_plan", return_value=plan
), mock.patch.object(
optimization.simulation, "emit_json"
) as emit_json:
with self.assertRaises(optimization.simulation.InputError):
optimization.command_plan(args)
emit_json.assert_not_called()
def test_command_plan_never_overwrites_an_existing_plan_file(self) -> None:
with tempfile.TemporaryDirectory() as directory_text:
root = Path(directory_text)
plan = _make_plan(root)
plan_file = root / "existing-plan.json"
original = b"keep this exact file\n"
plan_file.write_bytes(original)
args = self._command_args(plan, present=True, plan_file=plan_file)
with mock.patch.object(
optimization, "build_runtime_plan", return_value=plan
), mock.patch.object(
optimization.simulation, "emit_json"
) as emit_json:
with self.assertRaises(optimization.simulation.InputError):
optimization.command_plan(args)
self.assertEqual(plan_file.read_bytes(), original)
emit_json.assert_not_called()
def test_command_plan_rejects_a_broken_plan_file_symlink(self) -> None:
with tempfile.TemporaryDirectory() as directory_text:
root = Path(directory_text)
plan = _make_plan(root)
target = root / "missing-target.json"
plan_file = root / "plan-link.json"
plan_file.symlink_to(target)
args = self._command_args(plan, present=True, plan_file=plan_file)
with mock.patch.object(
optimization, "build_runtime_plan", return_value=plan
) as build_runtime_plan, mock.patch.object(
optimization.simulation, "emit_json"
) as emit_json:
with self.assertRaises(optimization.simulation.InputError):
optimization.command_plan(args)
build_runtime_plan.assert_not_called()
emit_json.assert_not_called()
self.assertTrue(plan_file.is_symlink())
self.assertFalse(target.exists())
def test_command_plan_reports_a_parent_symlink_loop_as_input_error(
self,
) -> None:
with tempfile.TemporaryDirectory() as directory_text:
root = Path(directory_text)
plan = _make_plan(root)
loop = root / "loop"
loop.symlink_to(loop.name)
plan_file = loop / "plan.json"
args = self._command_args(plan, present=True, plan_file=plan_file)
with mock.patch.object(
optimization, "build_runtime_plan", return_value=plan
) as build_runtime_plan, mock.patch.object(
optimization.simulation, "emit_json"
) as emit_json:
with self.assertRaises(
optimization.simulation.InputError
) as raised:
optimization.command_plan(args)
self.assertEqual(
raised.exception.code,
"OPTIMIZATION_PLAN_FILE_PATH_INVALID",
)
build_runtime_plan.assert_not_called()
emit_json.assert_not_called()
def test_command_plan_does_not_emit_when_plan_file_write_fails(self) -> None:
with tempfile.TemporaryDirectory() as directory_text:
root = Path(directory_text)
plan = _make_plan(root)
plan_file = root / "unwritten-plan.json"
args = self._command_args(plan, present=True, plan_file=plan_file)
write_error = optimization.simulation.ArtifactError(
"OUTPUT_WRITE_FAILED",
"simulated exclusive write failure",
{"path": str(plan_file)},
)
with mock.patch.object(
optimization, "build_runtime_plan", return_value=plan
), mock.patch.object(
optimization,
"_write_new_private_file",
side_effect=write_error,
), mock.patch.object(
optimization.simulation, "emit_json"
) as emit_json:
with self.assertRaises(optimization.simulation.ArtifactError):
optimization.command_plan(args)
emit_json.assert_not_called()
self.assertFalse(plan_file.exists())
class RankingAndConfirmationTests(unittest.TestCase):
def test_plan_warns_when_budget_cannot_cover_a_complete_generation(self) -> None:
with tempfile.TemporaryDirectory() as directory_text: