--seed: shuffle the order connections are routed in
The router is deterministic, but its result depends heavily on which connection routes first. A seed shuffles that order reproducibly, so a change can be judged over several orders instead of the one it happened to get.
This commit is contained in:
@@ -94,6 +94,7 @@ def route_board_exact(
|
||||
edge_clearance: int | None = None,
|
||||
resolution: int = 1,
|
||||
heuristic_weight: float = 1.0,
|
||||
seed: int | None = None,
|
||||
) -> ExactRouteResult:
|
||||
"""Route ``board`` orthogonally and verify the result with exact geometry.
|
||||
|
||||
@@ -122,6 +123,7 @@ def route_board_exact(
|
||||
edge_clearance=edge_clearance,
|
||||
resolution=resolution,
|
||||
heuristic_weight=heuristic_weight,
|
||||
seed=seed,
|
||||
)
|
||||
edge = 0 if edge_clearance is None else edge_clearance
|
||||
outline = board.outline
|
||||
|
||||
@@ -28,6 +28,7 @@ from __future__ import annotations
|
||||
|
||||
import heapq
|
||||
import math
|
||||
import random
|
||||
|
||||
from freeroute.board.board import BasicBoard
|
||||
from freeroute.board.items import ConductionArea, ObstacleArea, Pin
|
||||
@@ -154,8 +155,12 @@ class GridRouter:
|
||||
edge_clearance: int | None = None,
|
||||
resolution: int = 1,
|
||||
heuristic_weight: float = 1.0,
|
||||
seed: int | None = None,
|
||||
) -> None:
|
||||
self.board = board
|
||||
# None keeps the deterministic order; a seed shuffles which connection
|
||||
# routes first, to measure how much a result owes to the order
|
||||
self.seed = seed
|
||||
# how far (in cells) an escape stub may run to leave a walled-in pin
|
||||
self._escape_cells = 12
|
||||
self._crowded_cache: dict = {}
|
||||
@@ -819,9 +824,13 @@ class GridRouter:
|
||||
best_count = -1
|
||||
best_snapshot: list[tuple[int, list, list]] | None = None
|
||||
best_unrouted = len(conns)
|
||||
# conns[i].index == i is what rip-up relies on, so shuffle a view
|
||||
order = list(conns)
|
||||
if self.seed is not None:
|
||||
random.Random(self.seed).shuffle(order)
|
||||
for _ in range(passes):
|
||||
changed = False
|
||||
for conn in conns: # deterministic: connection creation order
|
||||
for conn in order:
|
||||
if conn.routed:
|
||||
continue
|
||||
if self._try_route(conn, conns):
|
||||
@@ -1050,6 +1059,7 @@ def route_board(
|
||||
edge_clearance: int | None = None,
|
||||
resolution: int = 1,
|
||||
heuristic_weight: float = 1.0,
|
||||
seed: int | None = None,
|
||||
) -> RouteResult:
|
||||
"""Convenience wrapper: build a :class:`GridRouter` and route the board."""
|
||||
router = GridRouter(
|
||||
@@ -1065,5 +1075,6 @@ def route_board(
|
||||
edge_clearance=edge_clearance,
|
||||
resolution=resolution,
|
||||
heuristic_weight=heuristic_weight,
|
||||
seed=seed,
|
||||
)
|
||||
return router.route()
|
||||
|
||||
@@ -80,6 +80,7 @@ def route_dsn_board(
|
||||
layers: list[int] | None = None,
|
||||
rip_up: bool = True,
|
||||
max_passes: int = 10,
|
||||
seed: int | None = None,
|
||||
) -> tuple[RouteResult, int, list[str]]:
|
||||
"""Route a parsed :class:`DsnBoard`; return the board-unit result, the scale,
|
||||
and the layer names. ``layers`` restricts routing to those signal-layer
|
||||
@@ -100,6 +101,7 @@ def route_dsn_board(
|
||||
layers=layers,
|
||||
rip_up=rip_up,
|
||||
max_passes=max_passes,
|
||||
seed=seed,
|
||||
)
|
||||
return result, scale, [layer.name for layer in dsn.layers]
|
||||
|
||||
@@ -140,11 +142,12 @@ def build_routing_result(
|
||||
rip_up: bool = True,
|
||||
max_passes: int = 10,
|
||||
edge_clearance_mm: float | None = None,
|
||||
seed: int | None = None,
|
||||
) -> RoutingResult:
|
||||
"""Route ``dsn`` (grid track) and convert to a DSN-unit RoutingResult."""
|
||||
route, scale, layer_names = route_dsn_board(
|
||||
dsn, layers=layers, rip_up=rip_up, max_passes=max_passes,
|
||||
edge_clearance_mm=edge_clearance_mm, resolution=resolution,
|
||||
edge_clearance_mm=edge_clearance_mm, resolution=resolution, seed=seed,
|
||||
)
|
||||
return _to_routing_result(route, dsn, scale, layer_names)
|
||||
|
||||
@@ -160,6 +163,7 @@ def route_dsn_board_exact(
|
||||
rip_up: bool = True,
|
||||
shove: bool = False,
|
||||
diagonal: bool = False,
|
||||
seed: int | None = None,
|
||||
) -> tuple[ExactRouteResult, int, list[str]]:
|
||||
"""Route a parsed :class:`DsnBoard` with the exact-geometry (orthogonal,
|
||||
DRC-verified) track; return the exact result, the scale, and layer names.
|
||||
@@ -184,6 +188,7 @@ def route_dsn_board_exact(
|
||||
rip_up=rip_up,
|
||||
shove=shove,
|
||||
diagonal=diagonal,
|
||||
seed=seed,
|
||||
)
|
||||
return exact, scale, [layer.name for layer in dsn.layers]
|
||||
|
||||
@@ -199,6 +204,7 @@ def build_exact_routing_result(
|
||||
rip_up: bool = True,
|
||||
max_passes: int = 10,
|
||||
edge_clearance_mm: float | None = None,
|
||||
seed: int | None = None,
|
||||
) -> RoutingResult:
|
||||
"""Route ``dsn`` (exact track) and convert to a DSN-unit RoutingResult."""
|
||||
exact, scale, layer_names = route_dsn_board_exact(
|
||||
@@ -211,6 +217,7 @@ def build_exact_routing_result(
|
||||
edge_clearance_mm=edge_clearance_mm,
|
||||
resolution=resolution,
|
||||
heuristic_weight=heuristic_weight,
|
||||
seed=seed,
|
||||
)
|
||||
return _to_routing_result(exact.result, dsn, scale, layer_names)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user