#!/usr/bin/env python3 """Deterministic single-objective optimizer for the system-simulation skill. The optimizer treats the existing FastAPI backend as the authority for model compilation and simulation. It never rewrites the source project in place and only changes explicitly selected continuous SI parameters in derived candidates after the user confirms the complete plan. """ from __future__ import annotations import argparse import bisect import copy import csv import hashlib import io import json import math import os import platform import random import re import sys import tempfile import time import uuid import xml.etree.ElementTree as ET from dataclasses import dataclass, field from pathlib import Path from typing import Iterable, Mapping, Sequence SCRIPT_DIRECTORY = Path(__file__).resolve().parent if str(SCRIPT_DIRECTORY) not in sys.path: sys.path.insert(0, str(SCRIPT_DIRECTORY)) import simulation_skill as simulation # noqa: E402 OPTIMIZATION_SCHEMA_VERSION = 1 OPTIMIZATION_RESULT_SCHEMA_VERSION = 2 STATISTIC_IMPLEMENTATION_VERSION = 1 CONTINUITY_POLICY = "user-assertion-explicit-false-veto-v1" SEARCH_POLICY = "de-rand-1-bin-deferred-reflection-1d-endpoints-stagnation-v3" NO_NEW_SUBMISSION_GENERATION_LIMIT = 3 TERMINAL_TREND_DIAGNOSTIC_VERSION = 1 TERMINAL_TREND_FRACTION = 0.05 TERMINAL_TREND_MAX_FRACTION = 0.20 TERMINAL_TREND_MIN_SAMPLES = 6 TERMINAL_TREND_RELATIVE_CHANGE_THRESHOLD = 0.01 TERMINAL_TREND_DIRECTIONAL_CONSISTENCY_THRESHOLD = 0.80 TERMINAL_TREND_RANGE_THRESHOLD = 0.02 MAX_DESIGN_VARIABLES = 16 MAX_RESPONSE_CONSTRAINTS = 16 MAX_SIMULATION_RUNS = 200 MAX_WALL_SECONDS = 7 * 24 * 60 * 60 OPTIMIZATION_ID_PATTERN = re.compile(r"^[A-Za-z0-9._-]{1,96}$") IDENTIFIER_PATTERN = re.compile(r"^[A-Za-z][A-Za-z0-9._-]{0,63}$") SUPPORTED_STATISTICS = { "final", "minimum", "maximum", "timeMean", "rms", "integral", "absoluteIntegral", "peakAbsolute", } def _metric_unit(series_unit: str, statistic_kind: str) -> str: if statistic_kind in {"integral", "absoluteIntegral"}: return f"({series_unit})*s" if series_unit else "s" return series_unit class OptimizationError(simulation.SkillCliError): def __init__( self, code: str, message: str, details: object | None = None, ) -> None: super().__init__(code, message, exit_code=4, details=details) class SearchStop(Exception): def __init__(self, reason: str) -> None: super().__init__(reason) self.reason = reason @dataclass(frozen=True) class TimeWindow: start: float end: float def as_dict(self) -> dict[str, float]: return {"start": self.start, "end": self.end} @dataclass(frozen=True) class StatisticSpec: kind: str window: TimeWindow | None def as_dict(self) -> dict[str, object]: return { "kind": self.kind, "window": self.window.as_dict() if self.window is not None else None, } @dataclass(frozen=True) class GoalSpec: kind: str value: float | None def as_dict(self) -> dict[str, object]: payload: dict[str, object] = {"kind": self.kind} if self.value is not None: payload["value"] = self.value return payload @dataclass(frozen=True) class ObjectiveSpec: result_key: str expected_unit: str statistic: StatisticSpec goal: GoalSpec def as_dict(self) -> dict[str, object]: return { "resultKey": self.result_key, "expectedUnit": self.expected_unit, "metricUnit": _metric_unit(self.expected_unit, self.statistic.kind), "statistic": self.statistic.as_dict(), "goal": self.goal.as_dict(), } @dataclass(frozen=True) class DesignVariableSpec: id: str component_id: str parameter: str unit: str lower: float upper: float def as_dict(self) -> dict[str, object]: return { "id": self.id, "componentId": self.component_id, "parameter": self.parameter, "unit": self.unit, "lower": self.lower, "upper": self.upper, } @dataclass(frozen=True) class ResponseConstraintSpec: id: str result_key: str expected_unit: str statistic: StatisticSpec lower: float | None upper: float | None tolerance: float scale: float def as_dict(self) -> dict[str, object]: return { "id": self.id, "resultKey": self.result_key, "expectedUnit": self.expected_unit, "metricUnit": _metric_unit(self.expected_unit, self.statistic.kind), "statistic": self.statistic.as_dict(), "lower": self.lower, "upper": self.upper, "tolerance": self.tolerance, "scale": self.scale, } @dataclass(frozen=True) class AlgorithmSpec: name: str seed: int population_size: int mutation_factor: float crossover_probability: float def as_dict(self) -> dict[str, object]: return { "name": self.name, "seed": self.seed, "populationSize": self.population_size, "mutationFactor": self.mutation_factor, "crossoverProbability": self.crossover_probability, "workers": 1, "strategy": "DE/rand/1/bin", "searchPolicy": SEARCH_POLICY, "updating": "deferred", "boundaryHandling": "reflection", "oneDimensionalEndpointSeedingPolicy": ( "exactBoundsWhenOneDesignVariable" ), "noNewSubmissionGenerationLimit": ( NO_NEW_SUBMISSION_GENERATION_LIMIT ), } @dataclass(frozen=True) class BudgetSpec: max_simulation_runs: int max_wall_seconds: float def as_dict(self) -> dict[str, object]: return { "maxSimulationRuns": self.max_simulation_runs, "maxWallSeconds": self.max_wall_seconds, "reservedFreshVerificationRuns": 1, } @dataclass(frozen=True) class ValidationSpec: relative_tolerance: float absolute_tolerance: float def as_dict(self) -> dict[str, object]: return { "freshRuns": 1, "relativeTolerance": self.relative_tolerance, "absoluteTolerance": self.absolute_tolerance, } @dataclass(frozen=True) class OptimizationSpec: objective: ObjectiveSpec design_variables: tuple[DesignVariableSpec, ...] constraints: tuple[ResponseConstraintSpec, ...] algorithm: AlgorithmSpec budget: BudgetSpec validation: ValidationSpec def as_dict(self) -> dict[str, object]: return { "optimizationSchemaVersion": OPTIMIZATION_SCHEMA_VERSION, "objective": self.objective.as_dict(), "designVariables": [item.as_dict() for item in self.design_variables], "constraints": [item.as_dict() for item in self.constraints], "algorithm": self.algorithm.as_dict(), "budget": self.budget.as_dict(), "validation": self.validation.as_dict(), } @dataclass(frozen=True) class ResolvedDesignVariable: spec: DesignVariableSpec label: str quantity: str initial: float was_explicit: bool original_value: object catalog_optimization_eligible: bool | None def as_dict(self) -> dict[str, object]: return { **self.spec.as_dict(), "label": self.label, "quantity": self.quantity, "initial": self.initial, "wasExplicit": self.was_explicit, "originalValue": self.original_value, "continuity": { "machineVerified": False, "userAssertionRequired": True, "catalogOptimizationEligible": ( self.catalog_optimization_eligible ), "editorAbsent": True, "optionsAbsent": True, }, } @dataclass(frozen=True) class RuntimePlan: source: simulation.SourceFile spec_source: simulation.SourceFile spec: OptimizationSpec inspection: dict[str, object] baseline_xml: bytes variables: dict[str, dict[str, object]] resolved_design_variables: tuple[ResolvedDesignVariable, ...] output_directory: Path base_url: str timeout: float confirmation_token: str def public_dict(self) -> dict[str, object]: search_run_limit = self.spec.budget.max_simulation_runs - 1 population_size = self.spec.algorithm.population_size full_generations_with_unique_candidates = max( 0, (search_run_limit - population_size) // population_size ) warnings: list[dict[str, object]] = [] design_variable_ids = [ item.spec.id for item in self.resolved_design_variables ] if full_generations_with_unique_candidates == 0: warnings.append( { "code": "OPTIMIZATION_BUDGET_INITIAL_POPULATION_ONLY", "message": ( "The simulation budget covers the initial population and " "fresh verification, but no complete DE generation if every " "candidate is unique." ), } ) return { "ok": True, "command": "optimization-plan", "confirmationRequired": True, "source": { "path": str(self.source.path), "sha256": self.source.sha256, "format": self.source.format, }, "spec": { "path": str(self.spec_source.path), "sha256": self.spec_source.sha256, "resolved": self.spec.as_dict(), }, "baselineSystemXmlSha256": _sha256(self.baseline_xml), "objectiveMetadata": self.variables[self.spec.objective.result_key], "objectiveMetricUnit": _metric_unit( self.spec.objective.expected_unit, self.spec.objective.statistic.kind, ), "constraintMetadata": [ self.variables[item.result_key] for item in self.spec.constraints ], "designVariables": [ item.as_dict() for item in self.resolved_design_variables ], "parameterContinuity": { "policy": CONTINUITY_POLICY, "machineVerified": False, "backendDeclarationRequired": False, "userAssertionRequiredFor": design_variable_ids, }, "requiredAssertions": [ { "code": "OPTIMIZATION_CONTINUITY_USER_ASSERTION", "designVariableIds": design_variable_ids, "message": ( "Confirm that every listed design variable is continuous, " "uses a linear SI scale, and cannot change ports, topology, " "modes, or other discrete model structure. The backend does " "not verify this property." ), } ], "execution": { "algorithm": self.spec.algorithm.as_dict(), "searchRunLimit": search_run_limit, "totalRunLimit": self.spec.budget.max_simulation_runs, "fullGenerationsWithUniqueCandidates": ( full_generations_with_unique_candidates ), "sequential": True, "backendBaseUrl": self.base_url, "readTimeoutSeconds": self.timeout, "outputDirectory": str(self.output_directory), "sourceWillBeOverwritten": False, }, "warnings": warnings, "planHash": self.confirmation_token, "confirmationToken": self.confirmation_token, } @dataclass class Trial: evaluation_id: int stage: str simulation_id: str parameters: dict[str, float] status: str duration_seconds: float objective_value: float | None = None objective_loss: float | None = None constraints: list[dict[str, object]] = field(default_factory=list) feasible: bool = False total_constraint_violation: float | None = None failure_code: str | None = None failure_message: str | None = None cache_reuse_count: int = 0 def as_dict(self) -> dict[str, object]: return { "evaluationId": self.evaluation_id, "stage": self.stage, "simulationId": self.simulation_id, "parameters": self.parameters, "status": self.status, "durationSeconds": self.duration_seconds, "objectiveValue": self.objective_value, "objectiveLoss": self.objective_loss, "constraints": self.constraints, "feasible": self.feasible, "totalConstraintViolation": self.total_constraint_violation, "failureCode": self.failure_code, "failureMessage": self.failure_message, "cacheReuseCount": self.cache_reuse_count, } def _sha256(data: bytes) -> str: return hashlib.sha256(data).hexdigest() def _mapping(value: object, path: str) -> dict[str, object]: if not isinstance(value, dict): raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path} must be an object.", ) return value def _list(value: object, path: str) -> list[object]: if not isinstance(value, list): raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path} must be an array.", ) return value def _keys( value: Mapping[str, object], *, path: str, required: set[str], optional: set[str] = frozenset(), ) -> None: missing = sorted(required - set(value)) unknown = sorted(set(value) - required - optional) if missing or unknown: raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path} contains missing or unsupported fields.", {"path": path, "missing": missing, "unknown": unknown}, ) def _text(value: object, path: str, *, identifier: bool = False) -> str: if not isinstance(value, str) or not value: raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path} must be a non-empty string.", ) if identifier and IDENTIFIER_PATTERN.fullmatch(value) is None: raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path} must match {IDENTIFIER_PATTERN.pattern}.", ) return value def _number(value: object, path: str) -> float: if not isinstance(value, (int, float)) or isinstance(value, bool): raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path} must be a finite number.", ) parsed = float(value) if not math.isfinite(parsed): raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path} must be a finite number.", ) return parsed def _integer(value: object, path: str) -> int: if not isinstance(value, int) or isinstance(value, bool): raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path} must be an integer.", ) return value def _optional_number(value: object, path: str) -> float | None: return None if value is None else _number(value, path) def _parse_statistic(value: object, path: str) -> StatisticSpec: payload = _mapping(value, path) _keys(payload, path=path, required={"kind", "window"}) kind = _text(payload["kind"], f"{path}.kind") if kind not in SUPPORTED_STATISTICS: raise simulation.InputError( "OPTIMIZATION_STATISTIC_UNSUPPORTED", f"{path}.kind is not supported.", {"supported": sorted(SUPPORTED_STATISTICS)}, ) raw_window = payload["window"] if raw_window is None: window = None else: window_payload = _mapping(raw_window, f"{path}.window") _keys( window_payload, path=f"{path}.window", required={"start", "end"}, ) start = _number(window_payload["start"], f"{path}.window.start") end = _number(window_payload["end"], f"{path}.window.end") if not start < end or not math.isfinite(end - start): raise simulation.InputError( "OPTIMIZATION_WINDOW_INVALID", f"{path}.window requires start < end with a finite numeric span.", ) window = TimeWindow(start, end) return StatisticSpec(kind, window) def _parse_goal(value: object, path: str) -> GoalSpec: payload = _mapping(value, path) _keys(payload, path=path, required={"kind"}, optional={"value"}) kind = _text(payload["kind"], f"{path}.kind") if kind not in {"minimize", "maximize", "target"}: raise simulation.InputError( "OPTIMIZATION_GOAL_UNSUPPORTED", f"{path}.kind must be minimize, maximize, or target.", ) if kind == "target": if "value" not in payload: raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path}.value is required for a target goal.", ) target = _number(payload["value"], f"{path}.value") else: if "value" in payload: raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path}.value is only valid for a target goal.", ) target = None return GoalSpec(kind, target) def parse_optimization_spec(value: object) -> OptimizationSpec: payload = _mapping(value, "optimization spec") _keys( payload, path="optimization spec", required={ "optimizationSchemaVersion", "objective", "designVariables", "constraints", "algorithm", "budget", "validation", }, ) schema_version = _integer( payload["optimizationSchemaVersion"], "optimizationSchemaVersion", ) if schema_version != OPTIMIZATION_SCHEMA_VERSION: raise simulation.InputError( "OPTIMIZATION_SCHEMA_VERSION_UNSUPPORTED", f"optimizationSchemaVersion must be {OPTIMIZATION_SCHEMA_VERSION}.", ) objective_payload = _mapping(payload["objective"], "objective") _keys( objective_payload, path="objective", required={"resultKey", "expectedUnit", "statistic", "goal"}, ) objective = ObjectiveSpec( result_key=_text(objective_payload["resultKey"], "objective.resultKey"), expected_unit=( objective_payload["expectedUnit"] if isinstance(objective_payload["expectedUnit"], str) else _text(objective_payload["expectedUnit"], "objective.expectedUnit") ), statistic=_parse_statistic(objective_payload["statistic"], "objective.statistic"), goal=_parse_goal(objective_payload["goal"], "objective.goal"), ) raw_design_variables = _list(payload["designVariables"], "designVariables") if not 1 <= len(raw_design_variables) <= MAX_DESIGN_VARIABLES: raise simulation.InputError( "OPTIMIZATION_DESIGN_VARIABLE_COUNT_INVALID", f"designVariables must contain 1 to {MAX_DESIGN_VARIABLES} entries.", ) design_variables: list[DesignVariableSpec] = [] for index, raw_item in enumerate(raw_design_variables): path = f"designVariables[{index}]" item = _mapping(raw_item, path) _keys( item, path=path, required={"id", "componentId", "parameter", "unit", "lower", "upper"}, ) unit = item["unit"] if not isinstance(unit, str): raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path}.unit must be a string.", ) lower = _number(item["lower"], f"{path}.lower") upper = _number(item["upper"], f"{path}.upper") if not lower < upper or not math.isfinite(upper - lower): raise simulation.InputError( "OPTIMIZATION_BOUNDS_INVALID", f"{path} requires lower < upper with a finite numeric span.", ) design_variables.append( DesignVariableSpec( id=_text(item["id"], f"{path}.id", identifier=True), component_id=_text(item["componentId"], f"{path}.componentId"), parameter=_text(item["parameter"], f"{path}.parameter"), unit=unit, lower=lower, upper=upper, ) ) ids = [item.id for item in design_variables] targets = [(item.component_id, item.parameter) for item in design_variables] if len(ids) != len(set(ids)) or len(targets) != len(set(targets)): raise simulation.InputError( "OPTIMIZATION_DESIGN_VARIABLE_DUPLICATE", "Design-variable IDs and component/parameter targets must be unique.", ) raw_constraints = _list(payload["constraints"], "constraints") if len(raw_constraints) > MAX_RESPONSE_CONSTRAINTS: raise simulation.InputError( "OPTIMIZATION_CONSTRAINT_COUNT_INVALID", f"constraints may contain at most {MAX_RESPONSE_CONSTRAINTS} entries.", ) constraints: list[ResponseConstraintSpec] = [] for index, raw_item in enumerate(raw_constraints): path = f"constraints[{index}]" item = _mapping(raw_item, path) _keys( item, path=path, required={ "id", "resultKey", "expectedUnit", "statistic", "lower", "upper", "tolerance", "scale", }, ) unit = item["expectedUnit"] if not isinstance(unit, str): raise simulation.InputError( "OPTIMIZATION_SPEC_INVALID", f"{path}.expectedUnit must be a string.", ) lower = _optional_number(item["lower"], f"{path}.lower") upper = _optional_number(item["upper"], f"{path}.upper") tolerance = _number(item["tolerance"], f"{path}.tolerance") scale = _number(item["scale"], f"{path}.scale") if lower is None and upper is None: raise simulation.InputError( "OPTIMIZATION_CONSTRAINT_INVALID", f"{path} requires at least one finite bound.", ) if lower is not None and upper is not None and lower > upper: raise simulation.InputError( "OPTIMIZATION_CONSTRAINT_INVALID", f"{path} requires lower <= upper.", ) if tolerance < 0 or scale <= 0: raise simulation.InputError( "OPTIMIZATION_CONSTRAINT_INVALID", f"{path}.tolerance must be non-negative and scale must be positive.", ) constraints.append( ResponseConstraintSpec( id=_text(item["id"], f"{path}.id", identifier=True), result_key=_text(item["resultKey"], f"{path}.resultKey"), expected_unit=unit, statistic=_parse_statistic(item["statistic"], f"{path}.statistic"), lower=lower, upper=upper, tolerance=tolerance, scale=scale, ) ) constraint_ids = [item.id for item in constraints] if len(constraint_ids) != len(set(constraint_ids)): raise simulation.InputError( "OPTIMIZATION_CONSTRAINT_DUPLICATE", "Constraint IDs must be unique.", ) algorithm_payload = _mapping(payload["algorithm"], "algorithm") _keys( algorithm_payload, path="algorithm", required={ "name", "seed", "populationSize", "mutationFactor", "crossoverProbability", }, ) algorithm_name = _text(algorithm_payload["name"], "algorithm.name") if algorithm_name != "differentialEvolution": raise simulation.InputError( "OPTIMIZATION_ALGORITHM_UNSUPPORTED", "Only differentialEvolution is supported in optimization schema 1.", ) seed = _integer(algorithm_payload["seed"], "algorithm.seed") population_size = _integer( algorithm_payload["populationSize"], "algorithm.populationSize" ) mutation_factor = _number( algorithm_payload["mutationFactor"], "algorithm.mutationFactor" ) crossover_probability = _number( algorithm_payload["crossoverProbability"], "algorithm.crossoverProbability", ) if not 0 <= seed <= 2**32 - 1: raise simulation.InputError( "OPTIMIZATION_ALGORITHM_INVALID", "algorithm.seed is out of range." ) if not 4 <= population_size <= 50: raise simulation.InputError( "OPTIMIZATION_ALGORITHM_INVALID", "algorithm.populationSize must be between 4 and 50.", ) if not 0 < mutation_factor <= 2 or not 0 <= crossover_probability <= 1: raise simulation.InputError( "OPTIMIZATION_ALGORITHM_INVALID", "Mutation must be in (0, 2] and crossover probability in [0, 1].", ) algorithm = AlgorithmSpec( algorithm_name, seed, population_size, mutation_factor, crossover_probability, ) budget_payload = _mapping(payload["budget"], "budget") _keys( budget_payload, path="budget", required={"maxSimulationRuns", "maxWallSeconds"}, ) max_runs = _integer( budget_payload["maxSimulationRuns"], "budget.maxSimulationRuns" ) max_wall_seconds = _number( budget_payload["maxWallSeconds"], "budget.maxWallSeconds" ) if not population_size + 1 <= max_runs <= MAX_SIMULATION_RUNS: raise simulation.InputError( "OPTIMIZATION_BUDGET_INVALID", "maxSimulationRuns must fit the initial population plus one fresh " f"verification run and may not exceed {MAX_SIMULATION_RUNS}.", ) if not 10 <= max_wall_seconds <= MAX_WALL_SECONDS: raise simulation.InputError( "OPTIMIZATION_BUDGET_INVALID", f"maxWallSeconds must be between 10 and {MAX_WALL_SECONDS}.", ) budget = BudgetSpec(max_runs, max_wall_seconds) validation_payload = _mapping(payload["validation"], "validation") _keys( validation_payload, path="validation", required={"relativeTolerance", "absoluteTolerance"}, ) relative_tolerance = _number( validation_payload["relativeTolerance"], "validation.relativeTolerance", ) absolute_tolerance = _number( validation_payload["absoluteTolerance"], "validation.absoluteTolerance", ) if relative_tolerance < 0 or absolute_tolerance < 0: raise simulation.InputError( "OPTIMIZATION_VALIDATION_INVALID", "Validation tolerances must be non-negative.", ) validation = ValidationSpec(relative_tolerance, absolute_tolerance) return OptimizationSpec( objective=objective, design_variables=tuple(design_variables), constraints=tuple(constraints), algorithm=algorithm, budget=budget, validation=validation, ) def _validate_output_target(path_text: str) -> Path: path = Path(path_text).expanduser().resolve(strict=False) if path.exists(): if not path.is_dir(): raise simulation.InputError( "OUTPUT_DIR_NOT_DIRECTORY", f"Output path is not a directory: {path}", ) try: if any(path.iterdir()): raise simulation.InputError( "OUTPUT_DIR_NOT_EMPTY", "Optimization output directory must be new or empty.", {"path": str(path)}, ) except OSError as exc: raise simulation.ArtifactError( "OUTPUT_DIR_UNREADABLE", str(exc), {"path": str(path)} ) from exc return path def _parameter_contracts( inspection: Mapping[str, object], ) -> dict[tuple[str, str], tuple[dict[str, object], dict[str, object] | None]]: system = inspection.get("system") if not isinstance(system, Mapping): return {} details = system.get("componentDetails") if not isinstance(details, list): return {} contracts: dict[ tuple[str, str], tuple[dict[str, object], dict[str, object] | None] ] = {} for detail in details: if not isinstance(detail, dict): continue component_id = detail.get("id") compiled = detail.get("compiled") if not isinstance(component_id, str) or not isinstance(compiled, dict): continue source_component = detail.get("source") source_mapping = source_component if isinstance(source_component, dict) else None parameters = compiled.get("parameters") if not isinstance(parameters, list): continue for parameter in parameters: if isinstance(parameter, dict) and isinstance(parameter.get("name"), str): contracts[(component_id, str(parameter["name"]))] = ( parameter, source_mapping, ) return contracts def _source_parameter( source_component: Mapping[str, object] | None, parameter_name: str, ) -> tuple[bool, object]: if not isinstance(source_component, Mapping): return False, None data = source_component.get("data") if not isinstance(data, Mapping): return False, None parameters = data.get("parameters") if not isinstance(parameters, Mapping) or parameter_name not in parameters: return False, None return True, parameters[parameter_name] def _resolve_design_variables( spec: OptimizationSpec, inspection: Mapping[str, object], ) -> tuple[ResolvedDesignVariable, ...]: contracts = _parameter_contracts(inspection) resolved: list[ResolvedDesignVariable] = [] for variable in spec.design_variables: target = (variable.component_id, variable.parameter) contract_pair = contracts.get(target) if contract_pair is None: raise simulation.InputError( "OPTIMIZATION_PARAMETER_UNKNOWN", "A design variable does not identify a compiled component parameter.", {"componentId": variable.component_id, "parameter": variable.parameter}, ) contract, source_component = contract_pair catalog_optimization_eligible: bool | None = None if "optimizationEligible" in contract: optimization_eligible = contract["optimizationEligible"] if type(optimization_eligible) is not bool: raise simulation.BackendError( "BACKEND_PARAMETER_CONTRACT_INVALID", "The optional optimizationEligible parameter field must be a boolean.", { "componentId": variable.component_id, "parameter": variable.parameter, "optimizationEligible": optimization_eligible, }, ) if not optimization_eligible: raise simulation.InputError( "OPTIMIZATION_PARAMETER_INELIGIBLE", "The backend contract explicitly marks this parameter as " "ineligible for continuous optimization.", { "componentId": variable.component_id, "parameter": variable.parameter, "optimizationEligible": False, }, ) catalog_optimization_eligible = True if "editor" in contract: editor = contract["editor"] if not isinstance(editor, str) or not editor: raise simulation.BackendError( "BACKEND_PARAMETER_CONTRACT_INVALID", "The optional editor parameter field must be a non-empty string.", { "componentId": variable.component_id, "parameter": variable.parameter, "editor": editor, }, ) raise simulation.InputError( "OPTIMIZATION_PARAMETER_DISCRETE_METADATA", "A parameter with an editor cannot be used for continuous optimization.", { "componentId": variable.component_id, "parameter": variable.parameter, "optimizationEligible": contract.get("optimizationEligible"), "editor": editor, }, ) if "options" in contract: options = contract["options"] if not isinstance(options, list) or not options: raise simulation.BackendError( "BACKEND_PARAMETER_CONTRACT_INVALID", "The optional options parameter field must be a non-empty array.", { "componentId": variable.component_id, "parameter": variable.parameter, "options": options, }, ) raise simulation.InputError( "OPTIMIZATION_PARAMETER_DISCRETE_METADATA", "A parameter with discrete options cannot be used for continuous optimization.", { "componentId": variable.component_id, "parameter": variable.parameter, "optimizationEligible": contract.get("optimizationEligible"), "options": options, }, ) contract_unit = contract.get("unit") if not isinstance(contract_unit, str) or contract_unit != variable.unit: raise simulation.InputError( "OPTIMIZATION_PARAMETER_UNIT_MISMATCH", "The design-variable unit must exactly match the backend SI contract.", { "id": variable.id, "expected": contract_unit, "received": variable.unit, }, ) current = contract.get("value") initial = _number( current, f"compiled parameter {variable.component_id}.{variable.parameter}.value", ) if not variable.lower <= initial <= variable.upper: raise simulation.InputError( "OPTIMIZATION_INITIAL_OUTSIDE_BOUNDS", "The current SI parameter value must lie inside the declared search bounds.", {"id": variable.id, "initial": initial}, ) minimum = contract.get("minimum") maximum = contract.get("maximum") if isinstance(minimum, (int, float)) and not isinstance(minimum, bool): minimum_value = float(minimum) minimum_exclusive = contract.get("minimumExclusive") is True if variable.lower < minimum_value or ( minimum_exclusive and variable.lower <= minimum_value ): raise simulation.InputError( "OPTIMIZATION_BOUNDS_OUTSIDE_CONTRACT", "A lower search bound violates the registered parameter contract.", { "id": variable.id, "minimum": minimum_value, "minimumExclusive": minimum_exclusive, }, ) if isinstance(maximum, (int, float)) and not isinstance(maximum, bool): maximum_value = float(maximum) if variable.upper > maximum_value: raise simulation.InputError( "OPTIMIZATION_BOUNDS_OUTSIDE_CONTRACT", "An upper search bound violates the registered parameter contract.", {"id": variable.id, "maximum": maximum_value}, ) was_explicit, original_value = _source_parameter( source_component, variable.parameter ) resolved.append( ResolvedDesignVariable( spec=variable, label=str(contract.get("label") or variable.parameter), quantity=str(contract.get("quantity") or "dimensionless"), initial=initial, was_explicit=was_explicit, original_value=original_value, catalog_optimization_eligible=catalog_optimization_eligible, ) ) return tuple(resolved) def _validate_result_contracts( spec: OptimizationSpec, variables: Mapping[str, Mapping[str, object]], ) -> None: requested: list[tuple[str, str, str]] = [ ( "objective", spec.objective.result_key, spec.objective.expected_unit, ) ] requested.extend( (f"constraint {constraint.id}", constraint.result_key, constraint.expected_unit) for constraint in spec.constraints ) for label, result_key, expected_unit in requested: metadata = variables.get(result_key) if metadata is None: raise simulation.InputError( "OPTIMIZATION_RESULT_VARIABLE_UNKNOWN", f"The {label} result key is not declared by the compiled model.", {"resultKey": result_key}, ) actual_unit = metadata.get("unit") if not isinstance(actual_unit, str): actual_unit = "" if actual_unit != expected_unit: raise simulation.InputError( "OPTIMIZATION_RESULT_UNIT_MISMATCH", f"The {label} expected unit does not match result metadata.", { "resultKey": result_key, "expected": expected_unit, "received": actual_unit, }, ) def _confirmation_token( *, source_sha256: str, spec_sha256: str, baseline_xml_sha256: str, output_directory: Path, base_url: str, timeout: float, ) -> str: payload = { "contract": "system-simulation-optimization-confirmation-v3", "continuityPolicy": CONTINUITY_POLICY, "searchPolicy": SEARCH_POLICY, "sourceSha256": source_sha256, "specSha256": spec_sha256, "baselineSystemXmlSha256": baseline_xml_sha256, "outputDirectory": str(output_directory), "baseUrl": base_url, "readTimeoutSeconds": timeout, } canonical = json.dumps( payload, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False, ).encode("utf-8") return _sha256(canonical) def build_runtime_plan( input_path: str, spec_path: str, output_directory: str, *, base_url: str, timeout: float, ) -> RuntimePlan: source = simulation.load_source(input_path, "json") if source.format != "json": raise simulation.InputError( "OPTIMIZATION_SOURCE_FORMAT_UNSUPPORTED", "Optimization schema 1 requires an editable ReactFlow project JSON v1 source.", ) spec_source = simulation.load_source(spec_path, "json") spec = parse_optimization_spec(spec_source.parsed) target_output = _validate_output_target(output_directory) component_ids = list( dict.fromkeys(item.component_id for item in spec.design_variables) ) inspection = simulation.inspect_source( source, base_url=base_url, timeout=timeout, component_ids=component_ids, variable_limit=1_000_000, ) variables = simulation._available_variables(inspection) _validate_result_contracts(spec, variables) resolved = _resolve_design_variables(spec, inspection) baseline_xml = simulation._json_to_xml( source, base_url=base_url, timeout=timeout, ) token = _confirmation_token( source_sha256=source.sha256, spec_sha256=spec_source.sha256, baseline_xml_sha256=_sha256(baseline_xml), output_directory=target_output, base_url=base_url, timeout=timeout, ) return RuntimePlan( source=source, spec_source=spec_source, spec=spec, inspection=inspection, baseline_xml=baseline_xml, variables=variables, resolved_design_variables=resolved, output_directory=target_output, base_url=base_url, timeout=timeout, confirmation_token=token, ) def command_plan(args: argparse.Namespace) -> int: plan = build_runtime_plan( args.input, args.spec, args.output_dir, base_url=args.base_url, timeout=args.timeout, ) simulation.emit_json(plan.public_dict()) return 0 def _loss(value: float, goal: GoalSpec) -> float: if goal.kind == "minimize": return value if goal.kind == "maximize": return -value assert goal.value is not None return abs(value - goal.value) def _interpolate( times: Sequence[float], values: Sequence[float], point: float, ) -> float: index = bisect.bisect_left(times, point) if index < len(times) and times[index] == point: return values[index] if index == 0 or index == len(times): raise OptimizationError( "OPTIMIZATION_WINDOW_NOT_COVERED", "A completed simulation does not cover a requested statistic window.", {"point": point, "start": times[0], "end": times[-1]}, ) left_time = times[index - 1] right_time = times[index] time_span = right_time - left_time if not math.isfinite(time_span): raise OptimizationError( "OPTIMIZATION_METRIC_OVERFLOW", "Interpolation time span overflowed the finite numeric contract.", ) fraction = (point - left_time) / time_span interpolated = ( (1.0 - fraction) * values[index - 1] + fraction * values[index] ) if not math.isfinite(interpolated): raise OptimizationError( "OPTIMIZATION_METRIC_OVERFLOW", "Interpolated result value overflowed the finite numeric contract.", ) return interpolated def _windowed_series( times: Sequence[float], values: Sequence[float], window: TimeWindow | None, ) -> tuple[list[float], list[float]]: if len(times) != len(values) or not times: raise OptimizationError( "OPTIMIZATION_RESULT_SERIES_INVALID", "Objective and constraint series must be non-empty and match time length.", ) converted_times: list[float] = [] converted_values: list[float] = [] previous: float | None = None for raw_time, raw_value in zip(times, values): if ( not isinstance(raw_time, (int, float)) or isinstance(raw_time, bool) or not math.isfinite(float(raw_time)) or not isinstance(raw_value, (int, float)) or isinstance(raw_value, bool) or not math.isfinite(float(raw_value)) ): raise OptimizationError( "OPTIMIZATION_RESULT_SERIES_INVALID", "Objective and constraint series must contain finite numbers.", ) current_time = float(raw_time) if previous is not None and current_time <= previous: raise OptimizationError( "OPTIMIZATION_RESULT_TIME_INVALID", "Simulation time must be strictly increasing for optimization metrics.", ) converted_times.append(current_time) converted_values.append(float(raw_value)) previous = current_time if window is None: return converted_times, converted_values if window.start < converted_times[0] or window.end > converted_times[-1]: raise OptimizationError( "OPTIMIZATION_WINDOW_NOT_COVERED", "A completed simulation does not cover a requested statistic window.", { "window": window.as_dict(), "resultStart": converted_times[0], "resultEnd": converted_times[-1], }, ) start_index = bisect.bisect_left(converted_times, window.start) end_index = bisect.bisect_right(converted_times, window.end) selected_times = converted_times[start_index:end_index] selected_values = converted_values[start_index:end_index] if not selected_times or selected_times[0] != window.start: selected_times.insert(0, window.start) selected_values.insert( 0, _interpolate(converted_times, converted_values, window.start), ) if selected_times[-1] != window.end: selected_times.append(window.end) selected_values.append( _interpolate(converted_times, converted_values, window.end) ) return selected_times, selected_values def statistic_value( times: Sequence[float], values: Sequence[float], statistic: StatisticSpec, ) -> float: selected_times, selected_values = _windowed_series( times, values, statistic.window ) if statistic.kind == "final": return selected_values[-1] if statistic.kind == "minimum": return min(selected_values) if statistic.kind == "maximum": return max(selected_values) if statistic.kind == "peakAbsolute": return max(abs(value) for value in selected_values) if len(selected_times) < 2 or selected_times[-1] <= selected_times[0]: raise OptimizationError( "OPTIMIZATION_STATISTIC_WINDOW_TOO_SHORT", f"{statistic.kind} requires a positive-duration time window.", ) duration = selected_times[-1] - selected_times[0] if not math.isfinite(duration): raise OptimizationError( "OPTIMIZATION_METRIC_OVERFLOW", "Statistic time span overflowed the finite numeric contract.", ) def finite_sum(terms: Iterable[float]) -> float: try: value = math.fsum(terms) except (OverflowError, ValueError) as exc: raise OptimizationError( "OPTIMIZATION_METRIC_OVERFLOW", "Statistic integration overflowed the finite numeric contract.", ) from exc if not math.isfinite(value): raise OptimizationError( "OPTIMIZATION_METRIC_OVERFLOW", "Statistic integration overflowed the finite numeric contract.", ) return value if statistic.kind == "integral": return finite_sum( 0.5 * (left_value + right_value) * (right_time - left_time) for left_time, right_time, left_value, right_value in zip( selected_times, selected_times[1:], selected_values, selected_values[1:], ) ) if statistic.kind == "absoluteIntegral": return finite_sum( 0.5 * (abs(left_value) + abs(right_value)) * (right_time - left_time) for left_time, right_time, left_value, right_value in zip( selected_times, selected_times[1:], selected_values, selected_values[1:], ) ) if statistic.kind == "timeMean": integral = statistic_value( selected_times, selected_values, StatisticSpec("integral", None), ) mean = integral / duration if not math.isfinite(mean): raise OptimizationError( "OPTIMIZATION_METRIC_OVERFLOW", "Time-mean calculation overflowed the finite numeric contract.", ) return mean if statistic.kind == "rms": square_integral = finite_sum( 0.5 * (left_value**2 + right_value**2) * (right_time - left_time) for left_time, right_time, left_value, right_value in zip( selected_times, selected_times[1:], selected_values, selected_values[1:], ) ) rms = math.sqrt(max(square_integral / duration, 0.0)) if not math.isfinite(rms): raise OptimizationError( "OPTIMIZATION_METRIC_OVERFLOW", "RMS calculation overflowed the finite numeric contract.", ) return rms raise AssertionError(f"Unhandled statistic kind: {statistic.kind}") def _planned_objective_end(plan: RuntimePlan) -> float | None: window = plan.spec.objective.statistic.window if window is not None: return window.end system = plan.inspection.get("system") simulation_settings = ( system.get("simulation") if isinstance(system, Mapping) else None ) if isinstance(simulation_settings, Mapping): for key in ("t_stop", "tStop", "endTime", "stopTime"): value = simulation_settings.get(key) if ( isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(float(value)) ): return float(value) try: root = ET.fromstring(plan.baseline_xml) except ET.ParseError: return None simulation_element = root.find("./Simulation") if simulation_element is None: return None for attribute in ("tStop", "endTime", "stopTime"): raw_value = simulation_element.get(attribute) if raw_value is None: continue try: value = float(raw_value) except ValueError: continue if math.isfinite(value): return value return None def _objective_endpoint_trend( result: Mapping[str, object], objective: ObjectiveSpec, *, expected_end: float | None = None, ) -> dict[str, object]: """Describe recent movement for a final-value objective without claiming steady state.""" base: dict[str, object] = { "diagnosticVersion": TERMINAL_TREND_DIAGNOSTIC_VERSION, "applicable": objective.statistic.kind == "final", "steadyStateProven": False, "heuristic": { "preferredTailFraction": TERMINAL_TREND_FRACTION, "maximumTailFraction": TERMINAL_TREND_MAX_FRACTION, "minimumSamples": TERMINAL_TREND_MIN_SAMPLES, "relativeChangeThreshold": ( TERMINAL_TREND_RELATIVE_CHANGE_THRESHOLD ), "directionalConsistencyThreshold": ( TERMINAL_TREND_DIRECTIONAL_CONSISTENCY_THRESHOLD ), "relativeRangeThreshold": TERMINAL_TREND_RANGE_THRESHOLD, }, } if objective.statistic.kind != "final": return {**base, "status": "notApplicable"} if result.get("status") != "completed" or result.get("success") is not True: return { **base, "status": "unavailable", "reason": "freshVerificationNotCompleted", } raw_series = result.get("series") if not isinstance(raw_series, Mapping): return { **base, "status": "unavailable", "reason": "freshVerificationSeriesMissing", } raw_times = raw_series.get("time") raw_values = raw_series.get(objective.result_key) if not isinstance(raw_times, list) or not isinstance(raw_values, list): return { **base, "status": "unavailable", "reason": "freshVerificationSeriesMissing", } try: times, values = _windowed_series( raw_times, raw_values, objective.statistic.window, ) except OptimizationError as exc: return { **base, "status": "unavailable", "reason": "freshVerificationSeriesInvalid", "diagnosticErrorCode": exc.code, } required_end = ( objective.statistic.window.end if objective.statistic.window is not None else expected_end ) if required_end is None: return { **base, "status": "unavailable", "reason": "plannedEndpointUnavailable", } end_tolerance = max(1e-12, abs(required_end) * 1e-12) if not math.isclose( times[-1], required_end, rel_tol=1e-12, abs_tol=end_tolerance ): return { **base, "status": "unavailable", "reason": "plannedEndpointNotCovered", "expectedEnd": required_end, "actualSeriesEnd": times[-1], } if len(times) < TERMINAL_TREND_MIN_SAMPLES: return { **base, "status": "insufficientData", "availableSamples": len(times), } duration = times[-1] - times[0] if duration <= 0.0 or not math.isfinite(duration): return { **base, "status": "insufficientData", "availableSamples": len(times), } preferred_start = times[-1] - TERMINAL_TREND_FRACTION * duration start_index = bisect.bisect_left(times, preferred_start) start_index = min(start_index, len(times) - TERMINAL_TREND_MIN_SAMPLES) tail_duration = times[-1] - times[start_index] if tail_duration > TERMINAL_TREND_MAX_FRACTION * duration: return { **base, "status": "insufficientData", "availableSamples": len(times), } tail_values = values[start_index:] increments = [ right - left for left, right in zip(tail_values, tail_values[1:]) ] positive = sum(item > 0.0 for item in increments) negative = sum(item < 0.0 for item in increments) nonzero_increments = positive + negative directional_consistency = ( max(positive, negative) / nonzero_increments if nonzero_increments else 0.0 ) delta = tail_values[-1] - tail_values[0] maximum_absolute_full = max(abs(item) for item in values) scale = max( max(abs(item) for item in tail_values), 1e-6 * maximum_absolute_full, sys.float_info.min, ) relative_change = delta / scale relative_range = (max(tail_values) - min(tail_values)) / scale average_slope = delta / tail_duration if any( not math.isfinite(item) for item in (delta, scale, relative_change, relative_range, average_slope) ): return { **base, "status": "unavailable", "reason": "terminalTrendNumericOverflow", } directional_change_detected = ( abs(relative_change) >= TERMINAL_TREND_RELATIVE_CHANGE_THRESHOLD and directional_consistency >= TERMINAL_TREND_DIRECTIONAL_CONSISTENCY_THRESHOLD ) tail_variability_detected = relative_range >= TERMINAL_TREND_RANGE_THRESHOLD material_change = directional_change_detected or tail_variability_detected direction = "flat" if material_change and not directional_change_detected: direction = "fluctuating" elif delta > 0.0: direction = "increasing" elif delta < 0.0: direction = "decreasing" detection_reasons: list[str] = [] if directional_change_detected: detection_reasons.append("directionalChange") if tail_variability_detected: detection_reasons.append("tailVariability") return { **base, "status": ( "materialChangeDetected" if material_change else "noMaterialChangeDetected" ), "tailStart": times[start_index], "tailEnd": times[-1], "samplesUsed": len(tail_values), "startValue": tail_values[0], "endValue": tail_values[-1], "change": delta, "averageSlopePerSecond": average_slope, "relativeChange": relative_change, "absoluteRelativeChange": abs(relative_change), "relativeRange": relative_range, "direction": direction, "directionalConsistency": directional_consistency, "nonzeroIncrementCount": nonzero_increments, "detectionReasons": detection_reasons, "directionalChangeDetected": directional_change_detected, "tailVariabilityDetected": tail_variability_detected, "materialChangeDetected": material_change, } def _completed_metrics( result: Mapping[str, object], spec: OptimizationSpec, ) -> tuple[float, float, list[dict[str, object]], bool, float]: status = str(result.get("status") or "") if status != "completed" or result.get("success") is not True: raise OptimizationError( "OPTIMIZATION_SIMULATION_INCOMPLETE", "Only a complete successful simulation can be scored.", {"status": status, "success": result.get("success")}, ) raw_variables = result.get("variables") if not isinstance(raw_variables, list): raise OptimizationError( "OPTIMIZATION_RESULT_METADATA_INVALID", "A completed simulation does not contain result-variable metadata.", ) variables: dict[str, dict[str, object]] = {} for item in raw_variables: if not isinstance(item, dict) or not isinstance(item.get("key"), str): raise OptimizationError( "OPTIMIZATION_RESULT_METADATA_INVALID", "Completed result-variable metadata is outside the backend contract.", ) key = str(item["key"]) if key in variables: raise OptimizationError( "OPTIMIZATION_RESULT_METADATA_INVALID", "Completed result-variable metadata contains a duplicate key.", {"resultKey": key}, ) variables[key] = item requested_metadata: list[tuple[str, str, str]] = [ ("objective", spec.objective.result_key, spec.objective.expected_unit) ] requested_metadata.extend( ( f"constraint {constraint.id}", constraint.result_key, constraint.expected_unit, ) for constraint in spec.constraints ) for label, result_key, expected_unit in requested_metadata: metadata = variables.get(result_key) if metadata is None: raise OptimizationError( "OPTIMIZATION_RESULT_METADATA_MISMATCH", f"The completed result does not declare the {label} result key.", {"resultKey": result_key}, ) actual_unit = metadata.get("unit") if not isinstance(actual_unit, str) or actual_unit != expected_unit: raise OptimizationError( "OPTIMIZATION_RESULT_METADATA_MISMATCH", f"The completed result changed the {label} unit after planning.", { "resultKey": result_key, "expected": expected_unit, "received": actual_unit, }, ) series = result.get("series") if not isinstance(series, Mapping): raise OptimizationError( "OPTIMIZATION_RESULT_SERIES_INVALID", "Simulation result does not contain a series object.", ) times = series.get("time") objective_values = series.get(spec.objective.result_key) if not isinstance(times, list) or not isinstance(objective_values, list): raise OptimizationError( "OPTIMIZATION_RESULT_VARIABLE_MISSING", "The completed result is missing time or the objective series.", {"resultKey": spec.objective.result_key}, ) objective_value = statistic_value( times, objective_values, spec.objective.statistic ) objective_loss = _loss(objective_value, spec.objective.goal) if not math.isfinite(objective_loss): raise OptimizationError( "OPTIMIZATION_METRIC_OVERFLOW", "The objective loss overflowed the finite numeric contract.", ) constraint_results: list[dict[str, object]] = [] total_violation = 0.0 feasible = True for constraint in spec.constraints: raw_values = series.get(constraint.result_key) if not isinstance(raw_values, list): raise OptimizationError( "OPTIMIZATION_RESULT_VARIABLE_MISSING", "The completed result is missing a constraint series.", {"constraint": constraint.id, "resultKey": constraint.result_key}, ) value = statistic_value(times, raw_values, constraint.statistic) lower_violation = ( max(0.0, constraint.lower - constraint.tolerance - value) if constraint.lower is not None else 0.0 ) upper_violation = ( max(0.0, value - constraint.upper - constraint.tolerance) if constraint.upper is not None else 0.0 ) raw_violation = max(lower_violation, upper_violation) normalized_violation = raw_violation / constraint.scale lower_margin = ( value - constraint.lower if constraint.lower is not None else None ) upper_margin = ( constraint.upper - value if constraint.upper is not None else None ) finite_margins = [ margin for margin in (lower_margin, upper_margin) if margin is not None ] margin = min(finite_margins) if finite_margins else None derived_values = [ lower_violation, upper_violation, raw_violation, normalized_violation, *finite_margins, total_violation + normalized_violation, ] if any(not math.isfinite(item) for item in derived_values): raise OptimizationError( "OPTIMIZATION_METRIC_OVERFLOW", "A response-constraint calculation overflowed the finite numeric contract.", {"constraint": constraint.id}, ) item_feasible = raw_violation == 0.0 feasible = feasible and item_feasible total_violation += normalized_violation constraint_results.append( { "id": constraint.id, "resultKey": constraint.result_key, "value": value, "unit": _metric_unit( constraint.expected_unit, constraint.statistic.kind ), "seriesUnit": constraint.expected_unit, "lower": constraint.lower, "upper": constraint.upper, "tolerance": constraint.tolerance, "margin": margin, "violation": raw_violation, "normalizedViolation": normalized_violation, "feasible": item_feasible, } ) return ( objective_value, objective_loss, constraint_results, feasible, total_violation, ) def _format_float(value: float) -> str: normalized = 0.0 if value == 0.0 else value return format(normalized, ".17g") def _format_report_float(value: float) -> str: normalized = 0.0 if value == 0.0 else value return format(normalized, ".12g") def _reflect_unit_interval(value: float) -> float: if not math.isfinite(value): raise AssertionError("Optimizer generated a non-finite coordinate.") wrapped = value % 2.0 reflected = wrapped if wrapped <= 1.0 else 2.0 - wrapped return 0.0 if reflected == 0.0 else reflected def _markdown_text(value: object) -> str: return ( str(value) .replace("&", "&") .replace("<", "<") .replace(">", ">") .replace("|", "|") .replace("`", "`") .replace("[", "[") .replace("]", "]") .replace("(", "(") .replace(")", ")") .replace("!", "!") .replace("\r", " ") .replace("\n", " ") ) def _candidate_parameters( plan: RuntimePlan, normalized_vector: Sequence[float], ) -> dict[str, float]: if len(normalized_vector) != len(plan.resolved_design_variables): raise AssertionError("Candidate vector dimension does not match the plan.") values: dict[str, float] = {} for coordinate, resolved in zip( normalized_vector, plan.resolved_design_variables ): if not math.isfinite(float(coordinate)): raise AssertionError("Optimizer generated a non-finite coordinate.") clipped = min(1.0, max(0.0, float(coordinate))) spec = resolved.spec value = spec.lower + clipped * (spec.upper - spec.lower) if not math.isfinite(value): raise OptimizationError( "OPTIMIZATION_CANDIDATE_OVERFLOW", "A normalized candidate did not map to a finite SI parameter value.", {"id": spec.id}, ) values[spec.id] = 0.0 if value == 0.0 else value return values def _normalized_initial(plan: RuntimePlan) -> list[float]: normalized: list[float] = [] for resolved in plan.resolved_design_variables: coordinate = (resolved.initial - resolved.spec.lower) / ( resolved.spec.upper - resolved.spec.lower ) if not math.isfinite(coordinate): raise OptimizationError( "OPTIMIZATION_CANDIDATE_OVERFLOW", "The baseline parameter could not be normalized to a finite value.", {"id": resolved.spec.id}, ) normalized.append(coordinate) return normalized def _candidate_xml(plan: RuntimePlan, parameters: Mapping[str, float]) -> bytes: try: root = ET.fromstring(plan.baseline_xml) except ET.ParseError as exc: # pragma: no cover - backend contract guard raise simulation.BackendError( "BACKEND_BASELINE_XML_INVALID", "Backend-generated baseline System XML is not parseable.", ) from exc components_parent = root.find("./Components") if components_parent is None: raise simulation.BackendError( "BACKEND_BASELINE_XML_INVALID", "Backend-generated System XML has no Components element.", ) components = { component.get("id"): component for component in components_parent.findall("./Component") if component.get("id") is not None } for resolved in plan.resolved_design_variables: component = components.get(resolved.spec.component_id) if component is None: raise simulation.BackendError( "BACKEND_BASELINE_XML_INVALID", "An optimized component is missing from baseline System XML.", {"componentId": resolved.spec.component_id}, ) parameter = next( ( item for item in component.findall("./Parameter") if item.get("name") == resolved.spec.parameter ), None, ) if parameter is None: raise simulation.BackendError( "BACKEND_BASELINE_XML_INVALID", "An optimized parameter is missing from baseline System XML.", { "componentId": resolved.spec.component_id, "parameter": resolved.spec.parameter, }, ) parameter.set("value", _format_float(parameters[resolved.spec.id])) return ET.tostring(root, encoding="utf-8", xml_declaration=True) def _optimized_project( plan: RuntimePlan, parameters: Mapping[str, float], ) -> dict[str, object]: project = copy.deepcopy(plan.source.parsed) if not isinstance(project, dict): # pragma: no cover - load_source guarantees this raise AssertionError("ReactFlow project root is not an object.") nodes = project.get("nodes") if not isinstance(nodes, list): raise simulation.InputError( "PROJECT_NODES_INVALID", "The source project no longer contains a nodes array.", ) nodes_by_id = { node.get("id"): node for node in nodes if isinstance(node, dict) and isinstance(node.get("id"), str) } for resolved in plan.resolved_design_variables: node = nodes_by_id.get(resolved.spec.component_id) if not isinstance(node, dict): raise simulation.InputError( "OPTIMIZATION_PARAMETER_UNKNOWN", "An optimized component is missing from the project copy.", ) data = node.get("data") if not isinstance(data, dict): raise simulation.InputError( "OPTIMIZATION_PARAMETER_UNKNOWN", "An optimized component has no data object.", ) raw_parameters = data.get("parameters") if raw_parameters is None: raw_parameters = {} data["parameters"] = raw_parameters if not isinstance(raw_parameters, dict): raise simulation.InputError( "OPTIMIZATION_PARAMETER_UNKNOWN", "An optimized component has no parameter object.", ) raw_parameters[resolved.spec.parameter] = parameters[resolved.spec.id] return project def _candidate_cache_key(parameters: Mapping[str, float], order: Sequence[str]) -> bytes: return json.dumps( [_format_float(parameters[item]) for item in order], separators=(",", ":"), ).encode("ascii") def _trial_rank(trial: Trial) -> tuple[float, ...]: if trial.status != "completed" or trial.objective_loss is None: return (2.0, math.inf, math.inf) if trial.feasible: return (0.0, trial.objective_loss, float(trial.evaluation_id)) violation = ( trial.total_constraint_violation if trial.total_constraint_violation is not None else math.inf ) return (1.0, violation, trial.objective_loss, float(trial.evaluation_id)) def _is_close(first: float, second: float, validation: ValidationSpec) -> bool: return math.isclose( first, second, rel_tol=validation.relative_tolerance, abs_tol=validation.absolute_tolerance, ) def _atomic_write(path: Path, data: bytes) -> None: temporary = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp") try: with temporary.open("xb") as stream: stream.write(data) stream.flush() os.fsync(stream.fileno()) os.replace(temporary, path) except OSError as exc: try: temporary.unlink(missing_ok=True) except OSError: pass raise simulation.ArtifactError( "OPTIMIZATION_ARTIFACT_WRITE_FAILED", str(exc), {"path": str(path)}, ) from exc def _atomic_json(path: Path, payload: object) -> None: data = ( json.dumps(payload, ensure_ascii=False, allow_nan=False, indent=2) + "\n" ).encode("utf-8") _atomic_write(path, data) def _artifact(path: Path) -> dict[str, object]: data = path.read_bytes() return { "path": str(path), "sha256": _sha256(data), "sizeBytes": len(data), } class OptimizationRunner: def __init__(self, plan: RuntimePlan, optimization_id: str) -> None: self.plan = plan self.optimization_id = optimization_id self.output_directory = plan.output_directory self.progress_directory = self.output_directory / "simulation-progress" self.events_path = self.output_directory / "optimization-events.jsonl" self.trials: list[Trial] = [] self.cache: dict[bytes, Trial] = {} self.optimizer_calls = 0 self.search_proposals = 0 self.cache_hits = 0 self.backend_submissions = 0 self.search_backend_submissions = 0 self.verification_backend_submissions = 0 self.completed_generations = 0 self.generations_with_new_backend_submissions = 0 self.generations_without_new_backend_submissions = 0 self.ending_no_new_submission_generation_streak = 0 self.max_no_new_submission_generation_streak = 0 self.final_population_unique_candidates: int | None = None self.optimizer_call_limit: int | None = None self.current_simulation_id: str | None = None self.started_monotonic = time.monotonic() self.started_unix = time.time() self.search_run_limit = plan.spec.budget.max_simulation_runs - 1 self._events: io.TextIOWrapper | None = None @property def parameter_order(self) -> list[str]: return [item.spec.id for item in self.plan.resolved_design_variables] def _counts_payload(self) -> dict[str, object]: search_trials = [trial for trial in self.trials if trial.stage == "search"] verification_trials = [ trial for trial in self.trials if trial.stage == "verification" ] return { "optimizerCalls": self.optimizer_calls, "candidateRequestsAllStages": self.optimizer_calls, "searchProposals": self.search_proposals, "backendSubmissions": self.backend_submissions, "backendSubmissionSlotsConsumed": self.backend_submissions, "searchBackendSubmissions": self.search_backend_submissions, "searchSubmissionSlotsConsumed": ( self.search_backend_submissions ), "verificationBackendSubmissions": ( self.verification_backend_submissions ), "verificationSubmissionSlotsConsumed": ( self.verification_backend_submissions ), "cacheHits": self.cache_hits, "generations": self.completed_generations, "generationsWithNewBackendSubmissions": ( self.generations_with_new_backend_submissions ), "generationsWithoutNewBackendSubmissions": ( self.generations_without_new_backend_submissions ), "endingNoNewSubmissionGenerationStreak": ( self.ending_no_new_submission_generation_streak ), "maxNoNewSubmissionGenerationStreak": ( self.max_no_new_submission_generation_streak ), "finalPopulationUniqueCandidates": ( self.final_population_unique_candidates ), "remainingSearchRunBudget": max( 0, self.search_run_limit - self.search_backend_submissions ), "completedTrials": sum( trial.status == "completed" for trial in self.trials ), "failedTrials": sum( trial.status != "completed" for trial in self.trials ), "searchCompletedTrials": sum( trial.status == "completed" for trial in search_trials ), "searchFailedTrials": sum( trial.status != "completed" for trial in search_trials ), "verificationCompletedTrials": sum( trial.status == "completed" for trial in verification_trials ), "verificationFailedTrials": sum( trial.status != "completed" for trial in verification_trials ), "recordedTrials": len(self.trials), "unrecordedSubmissionSlotsConsumed": max( 0, self.backend_submissions - len(self.trials) ), "searchUnrecordedSubmissionSlotsConsumed": max( 0, self.search_backend_submissions - len(search_trials) ), "verificationUnrecordedSubmissionSlotsConsumed": max( 0, self.verification_backend_submissions - len(verification_trials), ), } def _search_summary(self, termination_reason: str) -> dict[str, object]: categories = { "simulationBudgetExhausted": "budget", "searchWallTimeReached": "wallTime", "populationCollapsedAfterDuplicateStagnation": "stagnation", "duplicateProposalStagnation": "stagnation", "optimizerCallLimitReached": "safeguard", "userCancelled": "cancelled", "error": "error", } return { "terminationReason": termination_reason, "terminationCategory": categories.get(termination_reason, "other"), "searchConvergenceEstablished": False, "populationCollapsedToSingleCandidate": ( termination_reason == "populationCollapsedAfterDuplicateStagnation" ), "candidateRequests": self.search_proposals, "backendSubmissions": self.search_backend_submissions, "submissionSlotsConsumed": self.search_backend_submissions, "submissionCounting": "reservedBeforeProgressLogAndBackendStream", "cacheHits": self.cache_hits, "cacheHitRate": ( self.cache_hits / self.search_proposals if self.search_proposals else 0.0 ), "budget": { "limit": self.search_run_limit, "used": self.search_backend_submissions, "unused": max( 0, self.search_run_limit - self.search_backend_submissions ), "exhausted": ( self.search_backend_submissions >= self.search_run_limit ), }, "generations": { "completed": self.completed_generations, "withNewBackendSubmissions": ( self.generations_with_new_backend_submissions ), "withoutNewBackendSubmissions": ( self.generations_without_new_backend_submissions ), "endingNoNewSubmissionStreak": ( self.ending_no_new_submission_generation_streak ), "maximumNoNewSubmissionStreak": ( self.max_no_new_submission_generation_streak ), }, "finalPopulationUniqueCandidates": ( self.final_population_unique_candidates ), "noNewSubmissionGenerationLimit": ( NO_NEW_SUBMISSION_GENERATION_LIMIT ), "optimizerCallLimit": self.optimizer_call_limit, } def _timing_payload(self) -> dict[str, object]: durations = [trial.duration_seconds for trial in self.trials] return { "wallClockSeconds": max(0.0, time.monotonic() - self.started_monotonic), "simulationRuns": len(durations), "simulationDurationTotalSeconds": ( math.fsum(durations) if durations else 0.0 ), "simulationDurationMinimumSeconds": ( min(durations) if durations else None ), "simulationDurationMaximumSeconds": ( max(durations) if durations else None ), "simulationDurationMeanSeconds": ( math.fsum(durations) / len(durations) if durations else None ), } def _emit(self, payload: Mapping[str, object]) -> None: event = dict(payload) if self._events is not None: serialized = json.dumps( event, ensure_ascii=False, allow_nan=False, separators=(",", ":") ) self._events.write(serialized + "\n") self._events.flush() simulation.emit_json(event) def _write_evaluations(self) -> None: columns = [ "evaluationId", "stage", "status", "feasible", "objectiveValue", "objectiveLoss", "totalConstraintViolation", "simulationId", "durationSeconds", "failureCode", "cacheReuseCount", ] columns.extend(f"parameter.{item}" for item in self.parameter_order) for constraint in self.plan.spec.constraints: columns.extend( ( f"constraint.{constraint.id}.value", f"constraint.{constraint.id}.margin", f"constraint.{constraint.id}.violation", ) ) stream = io.StringIO(newline="") writer = csv.DictWriter(stream, fieldnames=columns, lineterminator="\n") writer.writeheader() for trial in self.trials: row: dict[str, object] = { "evaluationId": trial.evaluation_id, "stage": trial.stage, "status": trial.status, "feasible": str(trial.feasible).lower(), "objectiveValue": ( _format_float(trial.objective_value) if trial.objective_value is not None else "" ), "objectiveLoss": ( _format_float(trial.objective_loss) if trial.objective_loss is not None else "" ), "totalConstraintViolation": ( _format_float(trial.total_constraint_violation) if trial.total_constraint_violation is not None else "" ), "simulationId": trial.simulation_id, "durationSeconds": _format_float(trial.duration_seconds), "failureCode": trial.failure_code or "", "cacheReuseCount": trial.cache_reuse_count, } for key, value in trial.parameters.items(): row[f"parameter.{key}"] = _format_float(value) constraints = { str(item.get("id")): item for item in trial.constraints } for constraint in self.plan.spec.constraints: item = constraints.get(constraint.id, {}) for name in ("value", "margin", "violation"): raw_value = item.get(name) row[f"constraint.{constraint.id}.{name}"] = ( _format_float(float(raw_value)) if isinstance(raw_value, (int, float)) and not isinstance(raw_value, bool) else "" ) writer.writerow(row) _atomic_write( self.output_directory / "evaluations.csv", stream.getvalue().encode("utf-8"), ) def _best_trial(self, *, feasible_only: bool) -> Trial | None: candidates = [ trial for trial in self.trials if trial.stage == "search" and trial.status == "completed" and trial.objective_loss is not None and (trial.feasible or not feasible_only) ] return min(candidates, key=_trial_rank) if candidates else None def _checkpoint(self, termination_reason: str | None = None) -> None: best = self._best_trial(feasible_only=True) payload: dict[str, object] = { "optimizationResultSchemaVersion": OPTIMIZATION_RESULT_SCHEMA_VERSION, "optimizationId": self.optimization_id, "sourceSha256": self.plan.source.sha256, "specSha256": self.plan.spec_source.sha256, "counts": self._counts_payload(), "bestFeasibleSearch": best.as_dict() if best is not None else None, "trials": [trial.as_dict() for trial in self.trials], } if termination_reason is not None: payload["terminationReason"] = termination_reason _atomic_json(self.output_directory / "checkpoint.json", payload) self._write_evaluations() def _inner_event_sink(self, evaluation_id: int, simulation_id: str): def sink(payload: object) -> None: if not isinstance(payload, Mapping): return if payload.get("event") != "progress": return self._emit( { "event": "optimization-simulation-progress", "optimizationId": self.optimization_id, "evaluationId": evaluation_id, "simulationId": simulation_id, "searchRunLimit": self.search_run_limit, "simulation": dict(payload), } ) return sink def _evaluate( self, normalized_vector: Sequence[float], *, stage: str, bypass_cache: bool = False, retain_result: bool = False, ) -> tuple[Trial, dict[str, object] | None]: elapsed = time.monotonic() - self.started_monotonic if stage == "search": if self.search_backend_submissions >= self.search_run_limit: raise SearchStop("simulationBudgetExhausted") if elapsed >= self.plan.spec.budget.max_wall_seconds: raise SearchStop("searchWallTimeReached") elif self.backend_submissions >= self.plan.spec.budget.max_simulation_runs: raise SearchStop("simulationBudgetExhausted") self.optimizer_calls += 1 if stage == "search": self.search_proposals += 1 parameters = _candidate_parameters(self.plan, normalized_vector) cache_key = _candidate_cache_key(parameters, self.parameter_order) if not bypass_cache and cache_key in self.cache: cached = self.cache[cache_key] cached.cache_reuse_count += 1 self.cache_hits += 1 self._checkpoint() self._emit( { "event": "optimization-cache-hit", "optimizationId": self.optimization_id, "evaluationId": cached.evaluation_id, "parameters": parameters, } ) return cached, None evaluation_id = self.backend_submissions + 1 simulation_id = f"{self.optimization_id}.{evaluation_id:04d}" xml = _candidate_xml(self.plan, parameters) progress_path = self.progress_directory / f"evaluation-{evaluation_id:04d}.jsonl" started = time.monotonic() self.backend_submissions += 1 if stage == "search": self.search_backend_submissions += 1 else: self.verification_backend_submissions += 1 self._emit( { "event": "optimization-evaluation-started", "optimizationId": self.optimization_id, "evaluationId": evaluation_id, "stage": stage, "simulationId": simulation_id, "parameters": parameters, } ) result: dict[str, object] | None = None backend_error: dict[str, object] | None = None self.current_simulation_id = simulation_id try: result, backend_error = simulation._read_simulation_stream( self.plan.base_url, xml, simulation_id, self.plan.timeout, progress_path, event_sink=self._inner_event_sink(evaluation_id, simulation_id), full_result=None, ) except BaseException: raise else: self.current_simulation_id = None duration = time.monotonic() - started contract_error: OptimizationError | None = None if ( result is not None and str(result.get("status") or "") == "completed" and result.get("success") is True ): try: ( objective_value, objective_loss, constraints, feasible, total_violation, ) = _completed_metrics(result, self.plan.spec) except OptimizationError as exc: contract_error = exc trial = Trial( evaluation_id=evaluation_id, stage=stage, simulation_id=simulation_id, parameters=parameters, status="failed", duration_seconds=duration, failure_code=exc.code, failure_message=exc.message, ) else: trial = Trial( evaluation_id=evaluation_id, stage=stage, simulation_id=simulation_id, parameters=parameters, status="completed", duration_seconds=duration, objective_value=objective_value, objective_loss=objective_loss, constraints=constraints, feasible=feasible, total_constraint_violation=total_violation, ) else: result_status = ( str(result.get("status") or "failed") if isinstance(result, Mapping) else "failed" ) if result_status == "completed": result_status = "failed" failure_message = ( str(result.get("message") or "Simulation did not complete successfully.") if isinstance(result, Mapping) else "Simulation ended without a usable result." ) trial = Trial( evaluation_id=evaluation_id, stage=stage, simulation_id=simulation_id, parameters=parameters, status=result_status, duration_seconds=duration, failure_code="OPTIMIZATION_SIMULATION_INCOMPLETE", failure_message=failure_message, ) if backend_error is not None: trial.failure_message = json.dumps( backend_error, ensure_ascii=False, allow_nan=False ) self.trials.append(trial) if not bypass_cache: self.cache[cache_key] = trial self._checkpoint() self._emit( { "event": "optimization-evaluation-completed", "optimizationId": self.optimization_id, "evaluation": trial.as_dict(), "bestFeasibleSearch": ( self._best_trial(feasible_only=True).as_dict() if self._best_trial(feasible_only=True) is not None else None ), } ) if contract_error is not None: raise contract_error return trial, result if retain_result else None def _initial_population(self, random_source: random.Random) -> list[list[float]]: population_size = self.plan.spec.algorithm.population_size dimension = len(self.plan.resolved_design_variables) population = [[0.0] * dimension for _ in range(population_size)] for coordinate in range(dimension): strata = list(range(population_size)) random_source.shuffle(strata) for row, stratum in enumerate(strata): population[row][coordinate] = ( stratum + random_source.random() ) / population_size population[0] = _normalized_initial(self.plan) if dimension == 1: available_rows = iter(range(population_size - 1, 0, -1)) for endpoint in (0.0, 1.0): if any(row[0] == endpoint for row in population): continue population[next(available_rows)] = [endpoint] return population def _population_unique_candidate_count( self, population: Sequence[Sequence[float]] ) -> int: return len( { _candidate_cache_key( _candidate_parameters(self.plan, vector), self.parameter_order, ) for vector in population } ) def _search(self) -> tuple[str, int]: algorithm = self.plan.spec.algorithm random_source = random.Random(algorithm.seed) population = self._initial_population(random_source) trials: list[Trial] = [] termination_reason = "simulationBudgetExhausted" maximum_optimizer_calls = max(100, self.search_run_limit * 20) self.optimizer_call_limit = maximum_optimizer_calls try: for index, vector in enumerate(population): trial, _ = self._evaluate(vector, stage="search") if index == 0 and trial.status != "completed": raise OptimizationError( "OPTIMIZATION_BASELINE_FAILED", "The baseline model must complete successfully before search starts.", trial.as_dict(), ) trials.append(trial) self.final_population_unique_candidates = ( self._population_unique_candidate_count(population) ) while ( self.search_backend_submissions < self.search_run_limit and self.search_proposals < maximum_optimizer_calls ): submissions_before = self.search_backend_submissions cache_hits_before = self.cache_hits source_population = [list(vector) for vector in population] source_trials = list(trials) next_population = [list(vector) for vector in population] next_trials = list(trials) for target_index in range(len(population)): if self.search_proposals >= maximum_optimizer_calls: raise SearchStop("optimizerCallLimitReached") available = [ index for index in range(len(source_population)) if index != target_index ] first_index, second_index, third_index = random_source.sample( available, 3 ) mutant = [ _reflect_unit_interval( source_population[first_index][coordinate] + algorithm.mutation_factor * ( source_population[second_index][coordinate] - source_population[third_index][coordinate] ) ) for coordinate in range(len(source_population[target_index])) ] mandatory_coordinate = random_source.randrange(len(mutant)) candidate = [ ( mutant[coordinate] if coordinate == mandatory_coordinate or random_source.random() <= algorithm.crossover_probability else source_population[target_index][coordinate] ) for coordinate in range(len(mutant)) ] candidate_trial, _ = self._evaluate( candidate, stage="search" ) if _trial_rank(candidate_trial) < _trial_rank( source_trials[target_index] ): next_population[target_index] = candidate next_trials[target_index] = candidate_trial population = next_population trials = next_trials self.completed_generations += 1 new_submissions = ( self.search_backend_submissions - submissions_before ) new_cache_hits = self.cache_hits - cache_hits_before if new_submissions: self.generations_with_new_backend_submissions += 1 self.ending_no_new_submission_generation_streak = 0 else: self.generations_without_new_backend_submissions += 1 self.ending_no_new_submission_generation_streak += 1 self.max_no_new_submission_generation_streak = max( self.max_no_new_submission_generation_streak, self.ending_no_new_submission_generation_streak, ) self.final_population_unique_candidates = ( self._population_unique_candidate_count(population) ) self._emit( { "event": "optimization-generation-completed", "optimizationId": self.optimization_id, "generation": self.completed_generations, "backendSubmissions": self.backend_submissions, "searchRunLimit": self.search_run_limit, "backendSubmissionsThisGeneration": new_submissions, "cacheHitsThisGeneration": new_cache_hits, "consecutiveGenerationsWithoutNewBackendSubmissions": ( self.ending_no_new_submission_generation_streak ), "populationUniqueCandidates": ( self.final_population_unique_candidates ), } ) self._checkpoint() if ( self.ending_no_new_submission_generation_streak >= NO_NEW_SUBMISSION_GENERATION_LIMIT ): termination_reason = ( "populationCollapsedAfterDuplicateStagnation" if self.final_population_unique_candidates == 1 else "duplicateProposalStagnation" ) break if ( termination_reason == "simulationBudgetExhausted" and self.search_proposals >= maximum_optimizer_calls and self.search_backend_submissions < self.search_run_limit ): termination_reason = "optimizerCallLimitReached" except SearchStop as stop: termination_reason = stop.reason self.final_population_unique_candidates = ( self._population_unique_candidate_count(population) ) return termination_reason, self.completed_generations def cancel_active(self) -> None: if self.current_simulation_id is None: return simulation_id = self.current_simulation_id try: simulation.http_json( self.plan.base_url, "/api/system-xml/simulations/" + simulation.urllib.parse.quote(simulation_id, safe="") + "/cancel", method="POST", payload={"reason": "user"}, timeout=self.plan.timeout, ) except simulation.SkillCliError: pass finally: self.current_simulation_id = None def _source_is_unchanged(self) -> bool: try: return _sha256(self.plan.source.path.read_bytes()) == self.plan.source.sha256 except OSError: return False def _verification_comparison( self, search: Trial, verification: Trial, ) -> dict[str, object]: validation = self.plan.spec.validation comparisons: list[dict[str, object]] = [] objective_matches = ( search.objective_value is not None and verification.objective_value is not None and _is_close( search.objective_value, verification.objective_value, validation, ) ) comparisons.append( { "id": "objective", "search": search.objective_value, "verification": verification.objective_value, "matches": objective_matches, } ) search_constraints = { str(item.get("id")): item for item in search.constraints } verification_constraints = { str(item.get("id")): item for item in verification.constraints } for constraint in self.plan.spec.constraints: first = search_constraints.get(constraint.id, {}).get("value") second = verification_constraints.get(constraint.id, {}).get("value") matches = ( isinstance(first, (int, float)) and not isinstance(first, bool) and isinstance(second, (int, float)) and not isinstance(second, bool) and _is_close(float(first), float(second), validation) ) comparisons.append( { "id": constraint.id, "search": first, "verification": second, "matches": matches, } ) source_unchanged = self._source_is_unchanged() return { "passed": ( verification.status == "completed" and verification.feasible and source_unchanged and all(item["matches"] is True for item in comparisons) ), "sourceUnchanged": source_unchanged, "relativeTolerance": validation.relative_tolerance, "absoluteTolerance": validation.absolute_tolerance, "metrics": comparisons, } def _improvement( self, baseline: Trial | None, best: Trial | None ) -> dict[str, object] | None: if ( baseline is None or best is None or baseline.objective_value is None or best.objective_value is None ): return None goal = self.plan.spec.objective.goal baseline_loss = _loss(baseline.objective_value, goal) best_loss = _loss(best.objective_value, goal) absolute = baseline_loss - best_loss if not math.isfinite(absolute): absolute = None relative = ( absolute / abs(baseline_loss) if absolute is not None and baseline_loss != 0.0 else None ) if relative is not None and not math.isfinite(relative): relative = None return { "baselineValue": baseline.objective_value, "verifiedValue": best.objective_value, "baselineLoss": baseline_loss, "verifiedLoss": best_loss, "lossReduction": absolute, "relativeLossReduction": relative, } def _write_best_artifacts( self, best: Trial, result: Mapping[str, object], ) -> dict[str, object]: created_paths: list[Path] = [] try: with tempfile.TemporaryDirectory( prefix=".best-artifacts-", dir=self.output_directory, ) as staging_text: staging = Path(staging_text) best_parameters_path = staging / "best-parameters.json" simulation._write_new_file( best_parameters_path, ( json.dumps( { "sourceSha256": self.plan.source.sha256, "specSha256": self.plan.spec_source.sha256, "parameters": [ { **resolved.spec.as_dict(), "value": best.parameters[resolved.spec.id], } for resolved in self.plan.resolved_design_variables ], }, ensure_ascii=False, allow_nan=False, indent=2, ) + "\n" ).encode("utf-8"), ) best_xml_path = staging / "best-system.xml" simulation._write_new_file( best_xml_path, _candidate_xml(self.plan, best.parameters), ) best_project_path = staging / "best-project.json" best_project = _optimized_project(self.plan, best.parameters) simulation._write_new_file( best_project_path, ( json.dumps( best_project, ensure_ascii=False, allow_nan=False, indent=2, ) + "\n" ).encode("utf-8"), ) result_path = staging / "result.json" simulation._write_result_json(result_path, result) variables, series = simulation._validate_result_shape(result) csv_path = staging / "results.csv" csv_bytes = simulation._download_csv( self.plan.base_url, self.plan.timeout, simulation._project_name(self.plan.inspection), variables, series, ) simulation._write_new_file(csv_path, csv_bytes) metadata = simulation._variable_metadata_by_key(variables) chart_keys = list( dict.fromkeys( [self.plan.spec.objective.result_key] + [item.result_key for item in self.plan.spec.constraints] ) ) missing = [ key for key in chart_keys if key not in metadata or key not in series ] if missing: raise OptimizationError( "OPTIMIZATION_RESULT_VARIABLE_MISSING", "Verified result is missing a requested report curve.", {"missing": missing}, ) staged_chart_paths = [ Path(path) for path in simulation._write_charts( staging, "separate", chart_keys, metadata, series, ) ] staged_artifacts = { "bestParameters": best_parameters_path, "bestSystemXml": best_xml_path, "bestProject": best_project_path, "result": result_path, "csv": csv_path, } final_artifacts: dict[str, object] = {} for name, staged_path in staged_artifacts.items(): final_path = self.output_directory / staged_path.name simulation._write_new_file(final_path, staged_path.read_bytes()) created_paths.append(final_path) final_artifacts[name] = _artifact(final_path) final_chart_paths: list[Path] = [] for staged_path in staged_chart_paths: final_path = self.output_directory / staged_path.name simulation._write_new_file(final_path, staged_path.read_bytes()) created_paths.append(final_path) final_chart_paths.append(final_path) final_artifacts["charts"] = [ _artifact(path) for path in final_chart_paths ] return final_artifacts except BaseException as exc: cleanup_failures: list[dict[str, str]] = [] for path in reversed(created_paths): try: path.unlink(missing_ok=True) except OSError as cleanup_exc: cleanup_failures.append( {"path": str(path), "error": str(cleanup_exc)} ) if cleanup_failures: raise simulation.ArtifactError( "OPTIMIZATION_ARTIFACT_ROLLBACK_FAILED", "Failed to remove partially committed best artifacts.", {"failures": cleanup_failures}, ) from exc raise def _discard_generated_artifacts( self, artifacts: Mapping[str, object] ) -> None: output_root = self.output_directory.resolve() paths: list[Path] = [] for value in artifacts.values(): items = value if isinstance(value, list) else [value] for item in items: if not isinstance(item, Mapping): continue raw_path = item.get("path") if not isinstance(raw_path, str): continue path = Path(raw_path).resolve() if not path.is_relative_to(output_root): raise simulation.ArtifactError( "OPTIMIZATION_ARTIFACT_PATH_INVALID", "A generated artifact resolved outside the optimization output directory.", {"path": str(path)}, ) paths.append(path) for path in paths: try: path.unlink(missing_ok=True) except OSError as exc: raise simulation.ArtifactError( "OPTIMIZATION_ARTIFACT_REMOVE_FAILED", str(exc), {"path": str(path)}, ) from exc def _report_markdown(self, result: Mapping[str, object]) -> str: objective = self.plan.spec.objective objective_metric_unit = _metric_unit( objective.expected_unit, objective.statistic.kind ) baseline = result.get("baseline") best_search = result.get("bestSearch") best_diagnostic_search = result.get("bestDiagnosticSearch") search_comparison = ( best_search if isinstance(best_search, Mapping) else best_diagnostic_search ) best = result.get("verifiedBest") counts = result.get("counts") count_values = counts if isinstance(counts, Mapping) else {} search = result.get("search") search_values = search if isinstance(search, Mapping) else {} search_generations = search_values.get("generations") generation_values = ( search_generations if isinstance(search_generations, Mapping) else {} ) timing = result.get("timing") timing_values = timing if isinstance(timing, Mapping) else {} goal_text = objective.goal.kind if objective.goal.value is not None: goal_text += f" {_format_report_float(objective.goal.value)}" window_text = ( "完整返回时段" if objective.statistic.window is None else ( f"[{_format_report_float(objective.statistic.window.start)}, " f"{_format_report_float(objective.statistic.window.end)}] s" ) ) def trial_number(trial: object, field_name: str) -> str: if not isinstance(trial, Mapping): return "—" value = trial.get(field_name) if not isinstance(value, (int, float)) or isinstance(value, bool): return "—" return _format_report_float(float(value)) def constraint_map(trial: object) -> dict[str, Mapping[str, object]]: if not isinstance(trial, Mapping): return {} raw_constraints = trial.get("constraints") if not isinstance(raw_constraints, list): return {} return { str(item.get("id")): item for item in raw_constraints if isinstance(item, Mapping) } status = str(result.get("solutionStatus") or "") search_points = count_values.get("searchCompletedTrials") search_scope = ( f"本次完整完成的 {search_points} 个搜索仿真" if isinstance(search_points, int) and not isinstance(search_points, bool) else "本次完整完成的搜索仿真" ) conclusion = { "verified": ( f"这是{search_scope}中表现最好的可行候选," "且已通过一次独立复验;这不证明搜索收敛、系统达到稳态或全局最优。" ), "noFeasibleCandidate": ( "预算内没有找到完整可行候选;所列诊断点不能称为优化方案。" ), "verificationFailed": ( "搜索阶段找到了可行候选,但新鲜复验未通过;不交付已验证方案。" ), "aborted": "优化在完成可靠复验前中止;不交付已验证方案。", }.get(status, "本次运行没有形成可交付的已验证方案。") full_generations_with_unique_candidates = max( 0, ( self.search_run_limit - self.plan.spec.algorithm.population_size ) // self.plan.spec.algorithm.population_size, ) endpoint_seeding_text = ( "- 单变量边界覆盖:初始种群明确包含上下界" if len(self.plan.resolved_design_variables) == 1 else ( "- 多变量边界覆盖:不枚举全部边界组合;初始种群使用" "逐坐标拉丁超立方分层" ) ) termination_reason = str(result.get("terminationReason") or "") termination_descriptions = { "simulationBudgetExhausted": "搜索仿真预算已用完", "searchWallTimeReached": "搜索达到墙钟时间上限", "populationCollapsedAfterDuplicateStagnation": ( "连续多代只生成已缓存候选,且种群已塌缩到同一个精确候选;" "这是重复停滞状态,不是收敛判定" ), "duplicateProposalStagnation": ( "连续多代只生成已缓存候选,但种群尚未合一;这是重复候选停滞," "不是收敛判定" ), "optimizerCallLimitReached": ( "候选请求触发内部防死循环上限;这是安全停止,不是收敛判定" ), "userCancelled": "用户取消了搜索", "error": "搜索因错误中止", } termination_description = termination_descriptions.get( termination_reason, "搜索已停止" ) project_name = simulation._project_name(self.plan.inspection) status_label = { "verified": "候选已复验", "noFeasibleCandidate": "未找到可行候选", "verificationFailed": "候选复验失败", "aborted": "运行已中止", }.get(status, "没有已验证方案") parameter_section_title = ( "最佳参数" if status == "verified" else "参数范围" ) search_comparison_label = ( "搜索阶段最佳可行点" if isinstance(best_search, Mapping) else "约束违反最小的完整诊断点(不可行)" ) search_constraint_column = ( "搜索最佳" if isinstance(best_search, Mapping) else "搜索诊断(不可行)" ) lines = [ f"# {_markdown_text(project_name)} 优化结果 — {status_label}", "", f"- 结论:{conclusion}", f"- 目标:`{goal_text}` `{_markdown_text(objective.result_key)}` / `{objective.statistic.kind}`({_markdown_text(objective_metric_unit or 'dimensionless')})", f"- 目标时间窗:{window_text}", f"- 候选复验状态:`{result.get('solutionStatus')}`", "", f"## {parameter_section_title}", "", ( "- 连续性声明:后端不验证参数是否连续;以下变量由用户在确认" "完整计划时声明为线性 SI 连续参数,且不会改变端口、拓扑或模式。" ), "", "| ID | 组件参数 | 起点 | 下界 | 上界 | 验证值 | 单位 |", "| --- | --- | ---: | ---: | ---: | ---: | --- |", ] best_parameters = ( best.get("parameters") if isinstance(best, Mapping) else {} ) if not isinstance(best_parameters, Mapping): best_parameters = {} boundary_notes: list[str] = [] for variable in self.plan.resolved_design_variables: verified = best_parameters.get(variable.spec.id) lines.append( "| {id} | `{component}.{parameter}` | {initial} | {lower} | {upper} | {best} | {unit} |".format( id=variable.spec.id, component=_markdown_text(variable.spec.component_id), parameter=_markdown_text(variable.spec.parameter), initial=_format_report_float(variable.initial), lower=_format_report_float(variable.spec.lower), upper=_format_report_float(variable.spec.upper), best=( _format_report_float(float(verified)) if isinstance(verified, (int, float)) and not isinstance(verified, bool) else "—" ), unit=_markdown_text(variable.spec.unit or "dimensionless"), ) ) if isinstance(verified, (int, float)) and not isinstance(verified, bool): boundary_name: str | None = None boundary_tolerance = max( math.ulp(variable.spec.lower), math.ulp(variable.spec.upper), sys.float_info.epsilon * (variable.spec.upper - variable.spec.lower) * 4.0, ) if abs(float(verified) - variable.spec.lower) <= boundary_tolerance: boundary_name = "下界" elif abs(float(verified) - variable.spec.upper) <= boundary_tolerance: boundary_name = "上界" if boundary_name is not None: boundary_notes.append( "`{}` 位于当前确认范围的{}".format( _markdown_text(variable.spec.id), boundary_name ) ) if boundary_notes: lines.extend( [ "", "- 边界说明:{}。这只说明当前范围内得到的是边界候选;" "在确认更宽范围符合物理、安全和组件合同前,不据此建议放宽边界。".format( ";".join(boundary_notes) ), ] ) lines.extend( [ "", "## 目标对比", "", "| 项目 | 数值 |", "| --- | ---: |", f"| 基线 | {trial_number(baseline, 'objectiveValue')} |", f"| {search_comparison_label} | {trial_number(search_comparison, 'objectiveValue')} |", f"| 新鲜复验 | {trial_number(best, 'objectiveValue')} |", "", ] ) if isinstance(best_diagnostic_search, Mapping): lines.extend( [ "- 该诊断点的归一化约束违反总量:{};它不是可行方案。".format( trial_number( best_diagnostic_search, "totalConstraintViolation", ) ), "", ] ) endpoint_trend = result.get("objectiveEndpointTrend") endpoint_lines: list[str] = [] if isinstance(endpoint_trend, Mapping) and endpoint_trend.get("applicable"): endpoint_lines.extend(["## 终点趋势检查", ""]) trend_status = endpoint_trend.get("status") if trend_status == "materialChangeDetected": relative_change = endpoint_trend.get("absoluteRelativeChange") relative_percent = ( _format_report_float(float(relative_change) * 100.0) if isinstance(relative_change, (int, float)) and not isinstance(relative_change, bool) else "—" ) relative_range = endpoint_trend.get("relativeRange") relative_range_percent = ( _format_report_float(float(relative_range) * 100.0) if isinstance(relative_range, (int, float)) and not isinstance(relative_range, bool) else "—" ) if endpoint_trend.get("direction") == "fluctuating": endpoint_lines.append( "- 复验曲线在末段 [{start}, {end}] s 仍有明显波动:" "首尾净变化约 {change}%,峰峰范围约 {range_}%;因此 `final` " "只代表终点快照,不能据此声称达到稳态。".format( start=trial_number(endpoint_trend, "tailStart"), end=trial_number(endpoint_trend, "tailEnd"), change=relative_percent, range_=relative_range_percent, ) ) else: endpoint_lines.append( "- 复验曲线在末段 [{start}, {end}] s 仍{direction}," "首尾净变化约 {percent}%;因此 `final` 只代表终点快照," "不能据此声称达到稳态。".format( start=trial_number(endpoint_trend, "tailStart"), end=trial_number(endpoint_trend, "tailEnd"), direction=( "上升" if endpoint_trend.get("direction") == "increasing" else "下降" ), percent=relative_percent, ) ) endpoint_lines.append( "- 末段平均变化率:{} {}/s;这是启发式风险提示,不改变复验状态。".format( trial_number(endpoint_trend, "averageSlopePerSecond"), _markdown_text(objective_metric_unit or "dimensionless"), ) ) elif trend_status == "noMaterialChangeDetected": endpoint_lines.append( "- 启发式检查未发现明显的末端变化;这仍不是稳态证明。" ) elif trend_status == "insufficientData": endpoint_lines.append( "- 完整复验曲线的末段样本不足,无法可靠检查终点附近的变化;" "不能据此判断稳态。" ) else: endpoint_lines.append( "- 趋势诊断不可用({});没有完整覆盖计划终点的新鲜序列时," "不推断终点趋势。".format( _markdown_text(endpoint_trend.get("reason") or "原因未提供") ) ) endpoint_lines.append("") lines.extend(["## 约束与验证", ""]) baseline_constraints = constraint_map(baseline) search_constraints = constraint_map(search_comparison) verified_constraints = constraint_map(best) if self.plan.spec.constraints: lines.extend( [ f"| 约束 | 下界 | 上界 | 容差 | 基线 | {search_constraint_column} | 新鲜复验 | 复验余量 | 统计量单位 |", "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |", ] ) for constraint in self.plan.spec.constraints: baseline_item = baseline_constraints.get(constraint.id, {}) search_item = search_constraints.get(constraint.id, {}) verified_item = verified_constraints.get(constraint.id, {}) lines.append( "| {id} | {lower} | {upper} | {tolerance} | {baseline} | {search} | {verified} | {margin} | {unit} |".format( id=_markdown_text(constraint.id), lower=( _format_report_float(constraint.lower) if constraint.lower is not None else "—" ), upper=( _format_report_float(constraint.upper) if constraint.upper is not None else "—" ), tolerance=_format_report_float(constraint.tolerance), baseline=trial_number(baseline_item, "value"), search=trial_number(search_item, "value"), verified=trial_number(verified_item, "value"), margin=trial_number(verified_item, "margin"), unit=_markdown_text( _metric_unit( constraint.expected_unit, constraint.statistic.kind, ) or "dimensionless" ), ) ) lines.append("") else: lines.extend(["- 未声明响应约束。", ""]) verification = result.get("verification") if isinstance(verification, Mapping): lines.append( "- 新鲜复验通过:{}".format( "是" if verification.get("passed") is True else "否" ) ) lines.append( "- 复验结束时源工程未变化:{}".format( "是" if verification.get("sourceUnchanged") is True else "否" ) ) lines.append( f"- 相对/绝对复验容差:`{verification.get('relativeTolerance')}` / `{verification.get('absoluteTolerance')}`" ) raw_metrics = verification.get("metrics") if isinstance(raw_metrics, list): for metric in raw_metrics: if isinstance(metric, Mapping): lines.append( "- `{id}`:搜索 `{search}`,复验 `{verification}`,匹配 {matches}".format( id=_markdown_text(metric.get("id")), search=trial_number(metric, "search"), verification=trial_number(metric, "verification"), matches=( "是" if metric.get("matches") is True else "否" ), ) ) else: lines.append("- 未生成可验证的可行候选。") lines.append("") lines.extend(endpoint_lines) lines.extend( [ "## 搜索过程", "", f"- 搜索停止:`{termination_reason}` — {termination_description}", f"- 搜索候选评估:{count_values.get('searchProposals', '—')}", f"- 搜索请求(计入仿真预算):{count_values.get('searchSubmissionSlotsConsumed', '—')} / {self.search_run_limit}", f"- 搜索完成 / 失败或不完整:{count_values.get('searchCompletedTrials', '—')} / {count_values.get('searchFailedTrials', '—')}", f"- 独立复验请求(计入仿真预算):{count_values.get('verificationSubmissionSlotsConsumed', '—')};完成 / 失败或不完整:{count_values.get('verificationCompletedTrials', '—')} / {count_values.get('verificationFailedTrials', '—')}", f"- 计入仿真预算的请求总数:{count_values.get('backendSubmissionSlotsConsumed', '—')}", f"- 未形成试验记录的预算请求:{count_values.get('unrecordedSubmissionSlotsConsumed', '—')}", f"- 重复候选(缓存命中):{count_values.get('cacheHits', '—')}", f"- 完整代:{generation_values.get('completed', '—')};产生新仿真的代:{generation_values.get('withNewBackendSubmissions', '—')};纯重复代:{generation_values.get('withoutNewBackendSubmissions', '—')}", f"- 未使用的搜索仿真额度:{count_values.get('remainingSearchRunBudget', '—')}", f"- 仿真耗时:总计 {trial_number(timing_values, 'simulationDurationTotalSeconds')} s;单次最短 / 最长 / 平均 {trial_number(timing_values, 'simulationDurationMinimumSeconds')} / {trial_number(timing_values, 'simulationDurationMaximumSeconds')} / {trial_number(timing_values, 'simulationDurationMeanSeconds')} s", "", "## 算法与预算", "", ( "- 算法:标准库实现的 `DE/rand/1/bin`,每代冻结供体种群、" "越界坐标反射回可行域,顺序执行,不做局部抛光" ), endpoint_seeding_text, f"- 随机种子:`{self.plan.spec.algorithm.seed}`", f"- 种群大小:{self.plan.spec.algorithm.population_size}", f"- 变异因子 / 交叉概率:{_format_report_float(self.plan.spec.algorithm.mutation_factor)} / {_format_report_float(self.plan.spec.algorithm.crossover_probability)}", f"- 后端仿真上限:{self.plan.spec.budget.max_simulation_runs}(搜索最多 {self.search_run_limit},预留 1 次新鲜复验)", f"- 候选都唯一时预算可覆盖的完整 DE 代数:{full_generations_with_unique_candidates}", f"- 搜索软墙钟上限:{_format_report_float(self.plan.spec.budget.max_wall_seconds)} s", "", "## 审计信息", "", f"- 源工程:`{_markdown_text(self.plan.source.path)}`", f"- 源 SHA-256:`{self.plan.source.sha256}`", f"- 计划哈希:`{self.plan.confirmation_token}`", f"- 搜索策略:`{SEARCH_POLICY}`", "", "## 产物", "", "- `optimization-result.json`:权威结构化汇总", "- `evaluations.csv`:逐次已记录后端提交尝试摘要", "- `optimization-events.jsonl`:优化与内层仿真进度", "- `checkpoint.json`:中断时的已完成试验快照", ] ) if result.get("solutionStatus") == "verified": lines.extend( [ "- `best-project.json` / `best-system.xml`:验证后的派生模型;源文件未覆盖", "- `result.json` / `results.csv` / `curve-*.svg`:最终新鲜复验结果", "", "派生工程会把被优化参数的原表达式替换为普通 SI 数值;其他工程字段保持不变。", ] ) return "\n".join(lines) + "\n" def _result_payload( self, *, termination_reason: str, solution_status: str, baseline: Trial | None, best_search: Trial | None, best_diagnostic_search: Trial | None, verification_trial: Trial | None, verification: Mapping[str, object] | None, artifacts: Mapping[str, object], objective_endpoint_trend: Mapping[str, object] | None = None, source_unchanged: bool | None = None, error: Mapping[str, object] | None = None, ) -> dict[str, object]: source_status = ( self._source_is_unchanged() if source_unchanged is None else source_unchanged ) claim = { "verified": ( "best verified feasible point among completed search simulations " "within the declared budget; " "not a proof of global optimality, search convergence, or steady state" ), "noFeasibleCandidate": ( "no complete feasible candidate found within the declared budget" ), "verificationFailed": ( "a feasible search candidate was found but fresh verification failed" ), "aborted": "optimization aborted before a verified solution was established", }.get(solution_status, "no verified solution was established") verification_payload = ( dict(verification) if verification is not None else None ) if verification_payload is not None: verification_payload["backendSubmissions"] = ( self.verification_backend_submissions ) verification_payload["submissionSlotsConsumed"] = ( self.verification_backend_submissions ) verification_payload["meaning"] = ( "fresh candidate reproducibility and feasibility; not search convergence" ) result: dict[str, object] = { "optimizationResultSchemaVersion": OPTIMIZATION_RESULT_SCHEMA_VERSION, "optimizationId": self.optimization_id, "solutionStatus": solution_status, "terminationReason": termination_reason, "search": self._search_summary(termination_reason), "claim": claim, "source": { "path": str(self.plan.source.path), "sha256": self.plan.source.sha256, "unchanged": source_status, }, "spec": { "path": str(self.plan.spec_source.path), "sha256": self.plan.spec_source.sha256, "resolved": self.plan.spec.as_dict(), }, "planHash": self.plan.confirmation_token, "continuityPolicy": { "id": CONTINUITY_POLICY, "machineVerified": False, "confirmedUserAssertion": True, "designVariableIds": [ item.spec.id for item in self.plan.resolved_design_variables ], }, "objectiveMetric": { "resultKey": self.plan.spec.objective.result_key, "statistic": self.plan.spec.objective.statistic.as_dict(), "seriesUnit": self.plan.spec.objective.expected_unit, "unit": _metric_unit( self.plan.spec.objective.expected_unit, self.plan.spec.objective.statistic.kind, ), }, "environment": { "python": platform.python_version(), "platform": platform.platform(), "statisticImplementationVersion": STATISTIC_IMPLEMENTATION_VERSION, "backendBaseUrl": self.plan.base_url, "backendBuildFingerprint": None, }, "counts": self._counts_payload(), "timing": self._timing_payload(), "baseline": baseline.as_dict() if baseline is not None else None, "bestSearch": best_search.as_dict() if best_search is not None else None, "bestDiagnosticSearch": ( best_diagnostic_search.as_dict() if best_diagnostic_search is not None else None ), "verifiedBest": ( verification_trial.as_dict() if solution_status == "verified" and verification_trial is not None else None ), "verification": verification_payload, "objectiveEndpointTrend": ( dict(objective_endpoint_trend) if objective_endpoint_trend is not None else None ), "improvement": self._improvement( baseline, verification_trial if solution_status == "verified" else None, ), "trials": [trial.as_dict() for trial in self.trials], "artifacts": dict(artifacts), } if error is not None: result["error"] = dict(error) return result def _finalize_result(self, payload: dict[str, object]) -> dict[str, object]: report_path = self.output_directory / "report.md" _atomic_write(report_path, self._report_markdown(payload).encode("utf-8")) artifacts = payload.get("artifacts") if isinstance(artifacts, dict): supporting_artifacts = { "plan": self.output_directory / "optimization-plan.json", "report": report_path, "evaluations": self.output_directory / "evaluations.csv", "events": self.events_path, "checkpoint": self.output_directory / "checkpoint.json", } for name, path in supporting_artifacts.items(): if path.is_file(): artifacts[name] = _artifact(path) result_path = self.output_directory / "optimization-result.json" _atomic_json(result_path, payload) return payload def run(self) -> dict[str, object]: simulation._prepare_output_directory(str(self.output_directory)) try: self.progress_directory.mkdir() self._events = self.events_path.open( "x", encoding="utf-8", newline="\n" ) except OSError as exc: raise simulation.ArtifactError( "OPTIMIZATION_OUTPUT_INITIALIZATION_FAILED", str(exc), {"path": str(self.output_directory)}, ) from exc try: _atomic_json( self.output_directory / "optimization-plan.json", self.plan.public_dict(), ) self._emit( { "event": "optimization-started", "optimizationId": self.optimization_id, "source": str(self.plan.source.path), "sourceSha256": self.plan.source.sha256, "maxSimulationRuns": self.plan.spec.budget.max_simulation_runs, "searchRunLimit": self.search_run_limit, "outputDirectory": str(self.output_directory), } ) termination_reason, _ = self._search() baseline = next( ( trial for trial in self.trials if trial.stage == "search" and trial.evaluation_id == 1 ), None, ) best_search = self._best_trial(feasible_only=True) diagnostic_best = self._best_trial(feasible_only=False) if best_search is None: self._checkpoint(termination_reason) payload = self._result_payload( termination_reason=termination_reason, solution_status="noFeasibleCandidate", baseline=baseline, best_search=None, best_diagnostic_search=diagnostic_best, verification_trial=None, verification=None, artifacts={}, objective_endpoint_trend=_objective_endpoint_trend( {}, self.plan.spec.objective, expected_end=_planned_objective_end(self.plan), ), ) self._emit( { "event": "optimization-finished", "optimizationId": self.optimization_id, "solutionStatus": payload["solutionStatus"], "terminationReason": payload["terminationReason"], } ) return self._finalize_result(payload) if not self._source_is_unchanged(): raise OptimizationError( "OPTIMIZATION_SOURCE_CHANGED", "The source project changed after planning; fresh verification was not started.", ) best_vector = [ (best_search.parameters[resolved.spec.id] - resolved.spec.lower) / (resolved.spec.upper - resolved.spec.lower) for resolved in self.plan.resolved_design_variables ] verification_trial, verification_result = self._evaluate( best_vector, stage="verification", bypass_cache=True, retain_result=True, ) comparison = self._verification_comparison( best_search, verification_trial ) objective_endpoint_trend = _objective_endpoint_trend( verification_result or {}, self.plan.spec.objective, expected_end=_planned_objective_end(self.plan), ) verified = comparison["passed"] is True and verification_result is not None solution_status = "verified" if verified else "verificationFailed" artifacts: dict[str, object] = {} if verified: assert verification_result is not None artifacts.update( self._write_best_artifacts( verification_trial, verification_result ) ) final_source_unchanged = self._source_is_unchanged() if not final_source_unchanged: self._discard_generated_artifacts(artifacts) artifacts.clear() comparison["sourceUnchanged"] = False comparison["passed"] = False verified = False solution_status = "verificationFailed" else: final_source_unchanged = self._source_is_unchanged() self._checkpoint(termination_reason) payload = self._result_payload( termination_reason=termination_reason, solution_status=solution_status, baseline=baseline, best_search=best_search, best_diagnostic_search=None, verification_trial=verification_trial, verification=comparison, artifacts=artifacts, objective_endpoint_trend=objective_endpoint_trend, source_unchanged=final_source_unchanged, ) self._emit( { "event": "optimization-finished", "optimizationId": self.optimization_id, "solutionStatus": payload["solutionStatus"], "terminationReason": payload["terminationReason"], } ) return self._finalize_result(payload) finally: if self._events is not None: self._events.close() self._events = None def finalize_error( self, code: str, message: str, *, termination_reason: str, ) -> None: if not self.output_directory.exists(): return try: self._checkpoint(termination_reason) baseline = self.trials[0] if self.trials else None best_feasible = self._best_trial(feasible_only=True) best_diagnostic = ( None if best_feasible is not None else self._best_trial(feasible_only=False) ) payload = self._result_payload( termination_reason=termination_reason, solution_status="aborted", baseline=baseline, best_search=best_feasible, best_diagnostic_search=best_diagnostic, verification_trial=None, verification=None, artifacts={}, objective_endpoint_trend=_objective_endpoint_trend( {}, self.plan.spec.objective, expected_end=_planned_objective_end(self.plan), ), error={"code": code, "message": message}, ) self._finalize_result(payload) except (OSError, ValueError, simulation.SkillCliError): pass def command_optimize(args: argparse.Namespace) -> int: if not args.confirmed: raise simulation.InputError( "OPTIMIZATION_CONFIRMATION_REQUIRED", "Run optimization-plan first and explicitly confirm that exact plan.", ) plan = build_runtime_plan( args.input, args.spec, args.output_dir, base_url=args.base_url, timeout=args.timeout, ) mismatches: dict[str, object] = {} if args.expected_source_sha256 != plan.source.sha256: mismatches["sourceSha256"] = { "expected": args.expected_source_sha256, "actual": plan.source.sha256, } if args.expected_spec_sha256 != plan.spec_source.sha256: mismatches["specSha256"] = { "expected": args.expected_spec_sha256, "actual": plan.spec_source.sha256, } if args.confirmation_token != plan.confirmation_token: mismatches["confirmationToken"] = "does not match the current plan" if mismatches: raise simulation.InputError( "OPTIMIZATION_CONFIRMATION_STALE", "Source, spec, backend conversion, or output path changed after planning.", mismatches, ) optimization_id = args.optimization_id or f"opt-{uuid.uuid4().hex[:24]}" if OPTIMIZATION_ID_PATTERN.fullmatch(optimization_id) is None: raise simulation.InputError( "OPTIMIZATION_ID_INVALID", "Optimization ID must match [A-Za-z0-9._-]{1,96}.", ) runner = OptimizationRunner(plan, optimization_id) try: result = runner.run() except KeyboardInterrupt: runner.cancel_active() runner.finalize_error( "OPTIMIZATION_CANCELLED_BY_USER", "Optimization was interrupted by the user.", termination_reason="userCancelled", ) raise except simulation.SkillCliError as exc: runner.cancel_active() runner.finalize_error( exc.code, exc.message, termination_reason="error", ) raise simulation.emit_json( { "event": "optimization-completed", "optimizationId": optimization_id, "solutionStatus": result.get("solutionStatus"), "terminationReason": result.get("terminationReason"), "result": str(plan.output_directory / "optimization-result.json"), "report": str(plan.output_directory / "report.md"), "outputDirectory": str(plan.output_directory), } ) return 0 if result.get("solutionStatus") == "verified" else 4 def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description=( "Plan and run bounded, single-objective optimization for a " "SystemSimulationApp ReactFlow project." ) ) parser.add_argument( "--base-url", default=simulation.DEFAULT_BASE_URL, help="Loopback FastAPI base URL (default: %(default)s).", ) parser.add_argument( "--timeout", default=simulation.DEFAULT_TIMEOUT_SECONDS, type=simulation.positive_timeout, help="HTTP/heartbeat read timeout in seconds (default: %(default)s).", ) subparsers = parser.add_subparsers(dest="command", required=True) plan_parser = subparsers.add_parser( "plan", help="Validate and preview an immutable optimization plan without running it.", ) plan_parser.add_argument("input") plan_parser.add_argument("--spec", required=True) plan_parser.add_argument("--output-dir", required=True) plan_parser.set_defaults(handler=command_plan) optimize_parser = subparsers.add_parser( "optimize", help="Execute an explicitly confirmed optimization plan.", ) optimize_parser.add_argument("input") optimize_parser.add_argument("--spec", required=True) optimize_parser.add_argument("--output-dir", required=True) optimize_parser.add_argument("--expected-source-sha256", required=True) optimize_parser.add_argument("--expected-spec-sha256", required=True) optimize_parser.add_argument("--confirmation-token", required=True) optimize_parser.add_argument("--confirmed", action="store_true") optimize_parser.add_argument("--optimization-id") optimize_parser.set_defaults(handler=command_optimize) return parser def main(argv: Sequence[str] | None = None) -> int: simulation.configure_standard_streams() parser = build_parser() args = parser.parse_args(argv) try: args.base_url = simulation.validate_base_url(args.base_url) return int(args.handler(args)) except simulation.SkillCliError as exc: simulation.emit_json( simulation.stable_error_payload(exc), stream=sys.stderr, ) return exc.exit_code except KeyboardInterrupt: simulation.emit_json( simulation.stable_error_payload( OptimizationError( "OPTIMIZATION_CANCELLED_BY_USER", "Optimization was interrupted and no additional candidate will be started.", ) ), stream=sys.stderr, ) return 4 if __name__ == "__main__": raise SystemExit(main())