routing.core.bundle_optimization#
Bundle optimization: phenomena measured on a bundle, aggregated into a GA problem, recorded pass by pass.
Three layers. A Phenomenon measures one physical effect on the bundle as a
vector (one entry per line, collision pair, turn or rule violation). Cost and
Constraint are the two aggregation policies over a phenomenon — soft F term
versus hard G block — picked per OptimizationTerm, derived from the bundle’s own
rules (OptimizationTerm.from_bundle()) or handed to the orchestrator as a runtime
override. The GASolver (pymoo GA with swappable operator configs) minimizes the
assembled BundleProblem; the routing.core.harness.HarnessOptimizer
orchestrates the passes, recorded in the HarnessOptimizationHistory dashboard.
- class routing.core.bundle_optimization.AnnealedSurvivalConfig(*, name: str = '', temp0: float = 0.05)[source]#
Bases:
ModelSurvival lever: annealed tolerance to infeasibility, cooling over the generation budget.
- build(problem: BundleProblem, config: GAConfig) Survival[source]#
Build the configured
AnnealedViolationSurvivalcooling over the termination’s generation budget.
- temp0: float#
- class routing.core.bundle_optimization.AnnealedViolationSurvival(temp0: float, n_gen_total: int)[source]#
Bases:
SurvivalFeasibility-first survival whose infeasible ranking is softened by a cooling temperature.
Feasible individuals (CV<=0) always survive over infeasible ones and rank among themselves by objective. Infeasible ones rank by their violation plus a non-negative noise scaled by a temperature decaying linearly to zero over
n_gen_totalgenerations: early on a worse-violation individual can survive (the annealing escape), and as the temperature reaches zero the ranking becomes exact violation descent (feasibility by the end). The objective never enters the infeasible comparison (unlike stochastic ranking), so it cannot pull the search toward more violation. Generation counted internally (robust to pymoo’scopy_algorithmdeepcopy).
- class routing.core.bundle_optimization.BackBend(bundle: Bundle, interactions: BundleInteractions)[source]#
Bases:
PhenomenonPer turn
dot(outgoing_a, outgoing_b)at the shared point; positive = acute fold-back of one route.- name: str = 'BackBend'#
- property size: int#
One entry per successive line pair.
- class routing.core.bundle_optimization.BlockCrossoverConfig(*, name: str = '', granularity: str = 'chain', prob: float = 0.9)[source]#
Bases:
ModelDomain crossover lever: groups inherited whole from one parent (see
GroupBlockCrossover).- build(problem: BundleProblem, config: GAConfig) Crossover[source]#
Build the configured
GroupBlockCrossoverat point or chain granularity.
- granularity: str#
- prob: float#
- class routing.core.bundle_optimization.BundleEncoding(slices: list[tuple[BundlePoint, slice, type[BundleVariable]]], lower: ndarray, upper: ndarray, n_var: int)[source]#
Bases:
objectMaps a Bundle’s updatable BundleVariables to and from a flat numpy vector.
Bounds are read straight off each BundlePoint (
lower/upper, accumulated byBundleLine.clip_bounds_in_cadmap()over incident lines): a 3-entry box for a PointVariable, a scalar range for a PortVariable.- classmethod from_bundle(bundle: Bundle) BundleEncoding[source]#
Build the vector layout from the bundle’s updatable points and their accumulated bounds.
Raises when an updatable point carries no bounds: the bundle was not prepared — call
Bundle.attach_local_cadmaps()first.
- class routing.core.bundle_optimization.BundleProblem(bundle: ~routing.core.bundling.Bundle, encoding: ~routing.core.bundle_optimization.BundleEncoding, costs: list[~routing.core.bundle_optimization.Cost], constraints: list[~routing.core.bundle_optimization.Constraint] = <factory>)[source]#
Bases:
objectWires bundle + encoding + costs + constraints. Solver-facing.
Requires a prepared bundle (local CadMaps attached, bounds clipped):
BundleEncoding.from_bundle()enforces that contract at encoding time.- constraints: list[Constraint]#
- diagnose(x: ndarray | None = None) dict[source]#
Per-cost contribution and per-constraint violation summary at state
x(current state if None).
- encoding: BundleEncoding#
- evaluate(x: ndarray) tuple[float, ndarray][source]#
Set the Bundle to state
x, return(F, G). Mutates the Bundle in place.
- classmethod from_terms(bundle: Bundle, interactions: BundleInteractions, terms: list[OptimizationTerm], stage: str) BundleProblem[source]#
Bind the terms active in
stageon the prepared bundle and assemble the problem.Each term declares the stages it joins (
OptimizationTerm.active_in). Binding is pure measurement wiring: the bundle must arrive prepared and posed —HarnessOptimizer._run_pass()owns that sequence.
- property lower: ndarray#
Per-variable lower bounds.
- property n_constr: int#
Total number of entries in the concatenated G vector.
- property n_var: int#
Number of optimization variables in the flat vector.
- property upper: ndarray#
Per-variable upper bounds.
- violated_entries() list[ViolatedEntry][source]#
Every G entry above its tolerance, mapped to the carrier entity its phenomenon measures.
The per-entity identity
diagnose()aggregates away: oneViolatedEntryper violated entry (residual > 0), carrying the owning constraint’s name, the carrier (aBundleLine,CollisionPair,Turn, or None for entity-less rule entries) and the residual.
- class routing.core.bundle_optimization.BundleSnapshot(*, name: str = '', label: str, bundle: ~routing.core.bundling.Bundle, values: list[list[float] | float], locks: list[~volmdlr.primitives3d.Cylinder] = <factory>, solutions: list[~routing.core.core.Solution] = <factory>, passes_before: int = 0, signature: list[int] | None = None)[source]#
Bases:
ModelOne labeled bundle state outside the pass cycle — a (re)generation — with the locks that led to it.
solutionsis the complete routed set of that generation (references, not copies): the replayable side of the mutable cadmap —CadMap.restore_solutions()rebuilds the solution/pack state from it,CadMap.withdraw_zones()strips the later locks.passes_beforeis the chronology key: how many optimization passes the history held when the snapshot was taken, so rendering interleaves snapshots and passes in event order.- label: str#
- locks: list[Cylinder]#
- passes_before: int#
- signature: list[int] | None#
- values: list[list[float] | float]#
- class routing.core.bundle_optimization.CadInterference(bundle: Bundle, interactions: BundleInteractions, *, alpha: float = 0.0)[source]#
Bases:
PhenomenonPer-line deepest pipe-surface penetration into CAD along the scored span (world units,
>= 0).alphais the safety margin in voxel units, padding the packed radius. Measure only: the satisfiability precondition — posing every port-leader base at the first clear abscissa of its axis (Bundle.clear_port_cad_margin(), meaningful atalpha = 0too) — belongs to the pass preparation, before the interactions snapshot and the encoding read the posed state.- property carriers: list[BundleLine]#
The scored line measured by each entry.
- name: str = 'CadInterference'#
- property size: int#
One entry per scored line.
- class routing.core.bundle_optimization.CauchyMutation(scale: float = 0.1, gene_prob: float | None = None)[source]#
Bases:
MutationHeavy-tailed (Cauchy) mutation: large jumps to cross constraint-violation ridges in a single move.
Each gene is perturbed with probability
probby a Cauchy step scaled byscale * (xu - xl); the heavy tail produces occasional large jumps a Gaussian/polynomial mutation never would, then the result is clipped to the bounds. Pairs with Deb selection: the jump explores, Deb keeps what lands at lower violation.
- class routing.core.bundle_optimization.CauchyMutationConfig(*, name: str = '', scale: float = 0.1, gene_prob: float | None = None)[source]#
Bases:
ModelHeavy-tailed mutation lever: Cauchy jumps across violation ridges (see
CauchyMutation).- build(problem: BundleProblem, config: GAConfig) Mutation[source]#
Build the configured
CauchyMutation.
- gene_prob: float | None#
- scale: float#
- class routing.core.bundle_optimization.ChainTranslationConfig(*, name: str = '', scale: float = 0.15, chain_prob: float | None = None)[source]#
Bases:
ModelDomain mutation lever: whole-chain Cauchy translations (see
ChainTranslationMutation).- build(problem: BundleProblem, config: GAConfig) Mutation[source]#
Build the configured
ChainTranslationMutationover the problem’s chain groups.
- chain_prob: float | None#
- scale: float#
- class routing.core.bundle_optimization.ChainTranslationMutation(groups: VariableGroups, scale: float = 0.15, chain_prob: float | None = None)[source]#
Bases:
MutationTranslate whole chains: one shared Cauchy step per mutated chain, ports stepped along their own axis.
Independent per-gene mutation kinks a chain — every point moves its own way, raising length and back-bend before any constraint gain. Moving the chain as one rigid piece is the move the harness physically allows; the Cauchy tail still produces the occasional large escape jump.
- class routing.core.bundle_optimization.Constraint(phenomenon: Phenomenon, tolerance: float = 0.0)[source]#
Bases:
objectHard form of a phenomenon: G block
measure - tolerance, feasible when<= 0(pymoo convention).- property name: str#
The measured phenomenon’s name.
- property size: int#
Number of entries contributed to the global G vector.
- class routing.core.bundle_optimization.Cost(phenomenon: Phenomenon, weight: float = 1.0, power: int = 1)[source]#
Bases:
objectSoft form of a phenomenon:
weight * sum(max(0, measure)^power)added to F.- property name: str#
The measured phenomenon’s name.
- class routing.core.bundle_optimization.CriteriaTermination(criteria: TerminationCriteria)[source]#
Bases:
Terminationpymoo Termination racing every active criterion of a
TerminationCriteria— first to trip stops the run.Progress is the largest consumed budget fraction (generations, wall-clock); it jumps to 1.0 when the current regime’s stall window trips or when
stop_when_feasiblefires on a feasible best. Switching regime clears the other regime’s window, so each regime is judged on a fresh stall clock. Windows count evaluations (not generations), staying meaningful at any population size — 20 generations mean 1000 evaluations at pop 50 but only 40 at pop 2.
- class routing.core.bundle_optimization.GAConfig(*, name: str = '', pop_size: int = 2, seed: int = 1, eliminate_duplicates: bool = True, x0: list[float] | None = None, jitter_sigma: float | None = None, jitter_distribution: str = 'normal', termination: TerminationCriteria = TerminationCriteria(name='', n_gen=12500, max_seconds=None, feasible_tol=1e-06, feasible_stall=StallCriterion(name='', ftol=0.001, evals=1000), violation_stall=None, stop_when_feasible=False), verbose: bool = False, mutation: PolynomialMutationConfig | CauchyMutationConfig | ChainTranslationConfig | None = PolynomialMutationConfig(name='', genes_per_child=3.0), crossover: BlockCrossoverConfig | None = None, survival: AnnealedSurvivalConfig | None = None, term_record_period: int | None = None)[source]#
Bases:
ModelEvery GA option in one place; defaults = the 2026-07 campaign verdict (pop 2, ~3 mutated genes per child).
At
pop_size=2always setjitter_sigma(production:max_shift, normal) — two full-domain random individuals are a dead start.terminationdeclares the stopping policy (TerminationCriteria): every active criterion races and the first to trip stops the run.term_record_periodrecords the per-term F/G breakdown at the Deb-best every that many generations (one extra bundle evaluation each — significant at tiny populations).- crossover: BlockCrossoverConfig | None#
- eliminate_duplicates: bool#
- initial_sampling(x0: ndarray) FloatRandomSampling[source]#
JitteredSeedSamplingaroundx0whenjitter_sigmais set, full-domain random otherwise.
- jitter_distribution: str#
- jitter_sigma: float | None#
- mutation: PolynomialMutationConfig | CauchyMutationConfig | ChainTranslationConfig | None#
- pop_size: int#
- seed: int#
- survival: AnnealedSurvivalConfig | None#
- term_record_period: int | None#
- termination: TerminationCriteria#
- verbose: bool#
- x0: list[float] | None#
- class routing.core.bundle_optimization.GASolver(config: GAConfig | None = None)[source]#
Bases:
objectConstrained single-objective GA (pymoo, Deb dominance) driven by a
GAConfig.The production solver. Builds the pymoo algorithm from the config’s operator choices, minimizes with per-generation tracing and Ctrl+C recovery (returning the Deb-best seen), and packages a
SolverResult. Handles constrained and unconstrained problems alike — an empty G reduces the Deb ranking to plain F descent.- solve(problem: BundleProblem) SolverResult[source]#
Run the GA on
problem; KeyboardInterrupt returns the best-so-far. Leaves the bundle atx_best.
- class routing.core.bundle_optimization.GroupBlockCrossover(var_to_group: ndarray, prob: float = 0.9)[source]#
Bases:
CrossoverUniform crossover at group granularity: each group inherited whole from one parent.
Per-gene mixing (SBX/UX) can hand a point’s x to one parent and its y to the other, or tear a chain in two — geometrically incoherent children the Deb ranking then discards. Inheriting each group whole recombines the bundle at the level where the physics is separable.
- class routing.core.bundle_optimization.HarnessOptimizationHistory(passes: list[OptimizationPass] | None = None, snapshots: list[BundleSnapshot] | None = None, name: str = '')[source]#
Bases:
DessiaObjectDashboard of a harness optimization run: pass records plus (re)generation snapshots.
One
OptimizationPassper pass, plus theBundleSnapshotstates recorded outside the pass cycle (each (re)generation of the robust pipeline, with its locks).volume_models()renders snapshots and every pass’s three states in event order;plot_data()returns the convergence dashboard (objective and violation per pass, per-term curves when the GAConfig’sterm_record_periodrecorded them). The records serialize with the platform object: the passes share one bundle by reference (pointer machinery) and each line’s local cadmap stays runtime-only — the curves and states are carried, not the gigabytes.- best() OptimizationPass[source]#
Return the pass with the lowest feasible objective (violation-penalized tie-break).
- final() OptimizationPass[source]#
Return the last pass — the refined result (the shaping pass after a full
HarnessOptimizer.optimize()).
- plot_data(reference_path: str = '#', **kwargs) Graph2D#
Specific display tag for PlotData. Add a picture argument.
- record_pass(bundle: Bundle, raw_values: list[list[float] | float], relaxed_values: list[list[float] | float], optimized_values: list[list[float] | float], result: SolverResult, stage: str) OptimizationPass[source]#
Append one pass’s three captured states (raw → relaxed → optimized) and its solver outcome.
- record_state(label: str, bundle: Bundle, locks: list[Cylinder] | None = None, solutions: list[Solution] | None = None) BundleSnapshot[source]#
Capture the bundle’s current state under
label— a (re)generation event outside the pass cycle.solutionsis that generation’s complete routed set, kept so the mutable cadmap can be put back to this snapshot (CadMap.restore_solutions()) without rerouting from scratch.
- class routing.core.bundle_optimization.JitteredSeedSampling(x_seed: ndarray, sigma: float, seed: int = 0, distribution: str = 'uniform')[source]#
Bases:
FloatRandomSamplingpymoo initial sampling:
x_seedperturbed within±sigma(clipped to bounds).The unperturbed seed is preserved as individual 0.
- class routing.core.bundle_optimization.LineInterpenetration(bundle: Bundle, interactions: BundleInteractions)[source]#
Bases:
PhenomenonPer collision pair
(radius_a + radius_b) - segment_distance; positive = the two lines interpenetrate.The pair set is the interactions snapshot (see
Bundle.collision_candidate_pairs()for the candidate semantics: leader pairs never candidates, kinship exemptions). The radii sums are precomputed once — only the distances move with the geometry.- property carriers: list[CollisionPair]#
The collision pair measured by each entry.
- name: str = 'LineInterpenetration'#
- property size: int#
One entry per collision pair.
- class routing.core.bundle_optimization.OptimizationPass(*, name: str = '', bundle: Bundle, raw_values: list[list[float] | float], relaxed_values: list[list[float] | float], optimized_values: list[list[float] | float], result: SolverResult | None = None, stage: str = '', signature: list[int] | None = None)[source]#
Bases:
ModelOne optimization pass: three line-states captured over a shared topology, plus its solver outcome.
raw_valuesis the pass input (pre-relaxation),relaxed_valuesis post-SeedRelaxer,optimized_valuesis post-GA — each a snapshot of the points’ variable values (Bundle.capture_values(), plain floats/lists; topology and cadmap shared by reference, no deep copy).stagenames the pass’s stage — the terms whoseactive_indeclares it entered this problem.volmdlr_primitives()materializes each state in turn to build one mesh per state, with a progressive alpha (optimized opaque) under a caller-driven color and name suffix.- materialize() Bundle[source]#
Write the pass’s optimized state onto its bundle and return it (mutates in place).
- optimized_values: list[list[float] | float]#
- raw_values: list[list[float] | float]#
- relaxed_values: list[list[float] | float]#
- result: SolverResult | None#
- signature: list[int] | None#
- stage: str#
- class routing.core.bundle_optimization.OptimizationTerm(phenomenon: Callable[[Bundle, BundleInteractions], Phenomenon | list[Phenomenon]], hard: bool = True, soft: bool = False, weight: float = 1.0, power: int = 2, tolerance: float = 0.0, active_in: tuple[str, ...] = ('unlock', 'consolidate', 'shape'))[source]#
Bases:
objectHow one phenomenon enters the optimization: hard G block, soft F cost, or both.
phenomenonis the binding factory — aPhenomenonsubclass or any callable(bundle, interactions) -> Phenomenon | list[Phenomenon].weight/powershape the soft form,toleranceoffsets the hard form.active_indeclares the stages whose problems the term joins — every stage by default; the design-rule terms declare("shape",), being antagonistic to consolidation.- active_in: tuple[str, ...] = ('unlock', 'consolidate', 'shape')#
- bind(bundle: Bundle, interactions: BundleInteractions) tuple[list[Cost], list[Constraint]][source]#
Instantiate the phenomenon(s) on this pass’s bundle and wrap them in the requested forms.
A hard+soft term shares one phenomenon instance between its Cost and its Constraint.
- classmethod from_bundle(bundle: Bundle, alpha: float = 0.0, out_of_grid_penalty: float = 1000.0) list[OptimizationTerm][source]#
Terms of
bundle’s optimization problem: the intrinsic physics plus one term per bundle-active rule.The intrinsic trio is the problem definition of any harness — weighted length as the objective (out-of-grid samples charged
out_of_grid_penalty), CAD interference under thealphavoxel margin and line interpenetration as hard constraints. Every bundle-active rule (non-emptyDesignRule.evaluate_on_bundle()) joins with the form itsRuleEnforcementdeclares — hard constraint on the shaping stage when the rule carries none. A custom cost is aPhenomenonsubclass plus one term, handed to the optimizer as a runtime override.
- hard: bool = True#
- phenomenon: Callable[[Bundle, BundleInteractions], Phenomenon | list[Phenomenon]]#
- power: int = 2#
- soft: bool = False#
- tolerance: float = 0.0#
- weight: float = 1.0#
- class routing.core.bundle_optimization.Phenomenon[source]#
Bases:
ABCOne physical effect measured on the bundle: a vector with one entry per carrier entity.
Bound to a prepared bundle for one optimization pass;
measure()reads the bundle’s current geometry. The entry count is fixed by the seed topology (the stable-G-shape contract). Subclasses keep the uniform(bundle, interactions, **params)construction signature, so the class itself is anOptimizationTermfactory — specialize parameters withfunctools.partial.- property carriers: list[BundleLine | CollisionPair | Turn | None]#
Carrier entity per measured entry, index-aligned with
measure(); None when an entry has none.
- abstractmethod measure() ndarray[source]#
Per-entity measures at the bundle’s current geometry; positive = violating.
- name: str#
- abstract property size: int#
Number of measured entries (stable for a fixed bundle topology).
- class routing.core.bundle_optimization.PolynomialMutationConfig(*, name: str = '', genes_per_child: float | None = 3.0)[source]#
Bases:
Modelpymoo polynomial mutation sized in expected mutated genes per child (the campaign’s
pm3nlever).- build(problem: BundleProblem, config: GAConfig) Mutation[source]#
PM with
prob_var = genes_per_child / n_var(pymoo default when unset).
- genes_per_child: float | None#
- class routing.core.bundle_optimization.RelaxationConfig(*, name: str = '', enabled: bool = True, max_search: int = 2, max_passes: int = 8)[source]#
Bases:
ModelSeed-repair policy: whether the orchestrator runs the
SeedRelaxerbefore each pass, and its budget.- enabled: bool#
- max_passes: int#
- max_search: int#
- class routing.core.bundle_optimization.RelaxationSite(point: BundlePoint, lines: list[BundleLine], pairs: list[CollisionPair])[source]#
Bases:
objectA movable BundlePoint with its local interference scope.
The lines it carries and the collision pairs touching them. The seed-repair descent relaxes each site in turn.
- interference(alpha: float | None) float[source]#
CAD penetration of the incident lines (skipped when
alphais None) + positive interpenetration of the touching pairs.
- relax(voxel_size: float, max_search: int, alpha: float | None) None[source]#
Move the point to the neighbor minimizing its local interference; ties break on smallest shift.
Scoring every incident line and pair (clean ones included) keeps the move safe at a shared junction: a position is taken only if the local total drops, so a clean line is never sacrificed to rescue an interfering one. Candidate positions are voxel-aligned, so the chosen neighbor may still graze sub-voxel; the optimizer then resolves the remainder.
- classmethod sites_for(points: list[BundlePoint], lines_by_point: dict[BundlePoint, list[BundleLine]], pairs: list[CollisionPair]) list[RelaxationSite][source]#
One site per updatable point: its incident lines and the collision pairs touching them.
Each pair is attached to the four endpoints of its two lines (which never coincide, adjacency being excluded from the candidate set).
pointsorder is preserved for a deterministic descent.
- class routing.core.bundle_optimization.RuleViolations(bundle: Bundle, interactions: BundleInteractions, *, rule: DesignRule)[source]#
Bases:
PhenomenonOne design rule’s violation vector at the bundle’s current along-path measures.
Thin bridge: the distance logic lives in
rule.evaluate_on_bundle(already the pymoog <= 0convention); aggregation into F or G belongs to the optimizer’s forms, like any other phenomenon. The entry count is fixed at the seed topology (the hook’s stable-shape contract).- property size: int#
Entries contributed by the rule (fixed by bundle topology).
- class routing.core.bundle_optimization.SeedRelaxer(max_search: int = 2, max_passes: int = 8)[source]#
Bases:
objectCoordinate-descent seed repair driving a bundle to a feasible optimizer seed.
A policy object (descent budget only), owned by the orchestrator;
relax()receives the problem it repairs. Each shared, movable BundlePoint is nudged (within its optimizer bounds, by up tomax_searchvoxels) to the position minimizing the summedBundleLine.cad_penetration()of its incident lines plus the positiveBundleLine.interpenetration()of the touching collision pairs — co-scored, so repairing one interference cannot silently create the other.The scope and margins are read off the problem’s own
CadInterference/LineInterpenetrationphenomena (hard or soft): a repaired seed is feasible for exactly the interference terms the solver will read, by construction. With neither phenomenon in the problem the relaxer is a no-op. Back-bend and design rules are deliberately not relaxed — a one-step descent on those is riskier than letting the GA move. Run afterBundle.attach_local_cadmaps(); mutates the bundle in place.- relax(problem: BundleProblem, show_diff: bool = False) None[source]#
Run the descent until the problem’s interference seed is clean or a sweep stops improving.
Every accepted move strictly lowers the global interference, so the sweep terminates; a move-free pass stops it early. At
TRACEverbosity, traces a per-line penetration/shift table;show_diffoverlays the interfering seed (red) over the repaired one (green).
- class routing.core.bundle_optimization.SolverResult(*, name: str = '', x_best: list[float], f_best: float, g_best: list[float], max_violation: float, n_eval: int, elapsed_seconds: float, converged: bool, interrupted: bool, pymoo_best_objective: float | None = None, objective_history: list[list[float]] = <factory>, violation_history: list[list[float]] = <factory>, time_history: list[list[float]] = <factory>, term_history: list[list[float | dict[str, float]]] = <factory>)[source]#
Bases:
ModelOutcome and per-generation traces of one GA run on a BundleProblem.
x_best/g_bestare encoding-space vectors stored as plain float lists (JSON-portable); consumers re-wrap withnp.asarraybefore unpacking.f_bestre-evaluates the selected best, whereaspymoo_best_objectiveis what pymoo itself reported — the two diverge wheninterruptedcut the run short.- converged: bool#
- elapsed_seconds: float#
- f_best: float#
- g_best: list[float]#
- interrupted: bool#
- max_violation: float#
- n_eval: int#
- objective_history: list[list[float]]#
- pymoo_best_objective: float | None#
- term_history: list[list[float | dict[str, float]]]#
- time_history: list[list[float]]#
- violation_history: list[list[float]]#
- x_best: list[float]#
- class routing.core.bundle_optimization.StallCriterion(*, name: str = '', ftol: float, evals: int = 1000)[source]#
Bases:
ModelStagnation window: trip when the tracked best improves by less than
ftoloverevalsconsecutive evaluations.- evals: int#
- ftol: float#
- class routing.core.bundle_optimization.TerminationCriteria(*, name: str = '', n_gen: int = 12500, max_seconds: float | None = None, feasible_tol: float = 1e-06, feasible_stall: StallCriterion | None = StallCriterion(name='', ftol=0.001, evals=1000), violation_stall: StallCriterion | None = None, stop_when_feasible: bool = False)[source]#
Bases:
ModelDeclarative stopping policy of one GA run — budgets, per-regime stalls, and the unlock cut-off.
n_genalways caps andmax_secondsraces against it when set.feasible_stallwatches the best objective while feasible;violation_stallwatches the best constraint violation while infeasible (None keeps the escape on its full budget — the historical policy).stop_when_feasibleends the run the moment the best individual is feasible: the unlock stage’s criterion, where reaching feasibility is the whole objective and any further polishing belongs to the next stage.- feasible_stall: StallCriterion | None#
- feasible_tol: float#
- max_seconds: float | None#
- n_gen: int#
- stop_when_feasible: bool#
- violation_stall: StallCriterion | None#
- class routing.core.bundle_optimization.VariableGroups(point_slices: list[list[slice]], port_slices: list[list[slice]], var_to_group: ndarray)[source]#
Bases:
objectEncoding variables partitioned into physically-cohesive groups (single points, or whole chains).
A chain is a maximal run of updatable points threaded through degree-≤2 points — the span of harness between two junctions, which physically slides as one piece. Junctions (degree ≥ 3) stay singletons. Consumed by the domain operators (block crossover, chain translation).
- classmethod from_encoding(encoding: BundleEncoding, bundle: Bundle, chains: bool) VariableGroups[source]#
Group encoding variables by point, or by chain (union-find over degree-≤2 lines) when
chains.
- point_slices: list[list[slice]]#
- port_slices: list[list[slice]]#
- var_to_group: ndarray#
- class routing.core.bundle_optimization.ViolatedEntry(constraint: str, carrier: BundleLine | CollisionPair | Turn | None, residual: float)[source]#
Bases:
objectOne violated G entry: its constraint, the carrier entity it measures, and the residual (measure - tolerance).
- carrier: BundleLine | CollisionPair | Turn | None#
- constraint: str#
- residual: float#
- class routing.core.bundle_optimization.WeightedLength(bundle: Bundle, interactions: BundleInteractions, *, out_of_grid_penalty: float = 1000.0)[source]#
Bases:
PhenomenonPer-line weighted length (mean traversed weight times length).
A NaN
BundleLine.weighted_length()(scored span traversing only blocked voxels) reads asout_of_grid_penalty: a NaN F silently corrupts the solver’s ranking.- property carriers: list[BundleLine]#
The scored line measured by each entry.
- name: str = 'WeightedLength'#
- property size: int#
One entry per scored line.