--rollback: undo a rip-up that leaves fewer connections routed
The connections a rip-up route crosses are rerouted at once in free space. If that leaves fewer connections routed than before, the new route comes out and every displaced connection goes back on its old path, with its rip count as it was. Even trades are kept.
This commit is contained in:
@@ -113,6 +113,7 @@ class _Connection:
|
||||
"b_group",
|
||||
"a_end",
|
||||
"b_end",
|
||||
"path",
|
||||
)
|
||||
|
||||
def __init__(self, index: int, net_no: int, a: Pin, b: Pin) -> None:
|
||||
@@ -134,6 +135,8 @@ class _Connection:
|
||||
self.b_group: tuple[list[Pin], list] = ([b], [])
|
||||
self.a_end: Pin | None = a
|
||||
self.b_end: Pin | None = b
|
||||
# the node path last committed, so a rip-up can be undone
|
||||
self.path: list[Node] | None = None
|
||||
|
||||
|
||||
class GridRouter:
|
||||
@@ -159,6 +162,7 @@ class GridRouter:
|
||||
seed: int | None = None,
|
||||
route_first: frozenset | None = None,
|
||||
access_cost: float = 0.0,
|
||||
rollback: bool = False,
|
||||
) -> None:
|
||||
self.board = board
|
||||
# Pin access: while a pin still has connections to route, the cells
|
||||
@@ -168,6 +172,10 @@ class GridRouter:
|
||||
self.access_cost = max(float(access_cost), 0.0)
|
||||
self._doorways: dict[Node, list[tuple[int, int]]] = {}
|
||||
self._pending: dict[int, int] = {}
|
||||
# Rip-up as a transaction: the connections a rip-up route crosses are
|
||||
# rerouted at once, and if fewer connections are routed afterwards
|
||||
# than before, everything goes back as it was.
|
||||
self.rollback = bool(rollback)
|
||||
# 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
|
||||
@@ -788,6 +796,7 @@ class GridRouter:
|
||||
# --- commit / rip -------------------------------------------------------
|
||||
|
||||
def _commit(self, conn: _Connection, path: list[Node]) -> None:
|
||||
conn.path = list(path)
|
||||
wire_segs, via_cells = _split_path(path)
|
||||
via_cells = list(via_cells)
|
||||
if isinstance(conn.a_end, ConductionArea):
|
||||
@@ -981,11 +990,41 @@ class GridRouter:
|
||||
path = self._search(conn, allow_ripup=True, conns=conns)
|
||||
if path is None:
|
||||
return False
|
||||
for idx in sorted(self._crossed_owners(path, conn.net_no, conns)):
|
||||
self._rip(conns[idx])
|
||||
victims = [conns[i] for i in sorted(self._crossed_owners(path, conn.net_no, conns))]
|
||||
if self.rollback:
|
||||
return self._ripup_transaction(conn, path, victims, conns)
|
||||
for victim in victims:
|
||||
self._rip(victim)
|
||||
self._commit(conn, path)
|
||||
return True
|
||||
|
||||
def _ripup_transaction(self, conn, path, victims, conns) -> bool:
|
||||
"""Route ``conn`` through ``victims``, reroute them at once in free
|
||||
space, and undo it all if fewer connections end up routed."""
|
||||
saved = [(v, v.path, v.a_end, v.b_end, v.rip_count) for v in victims]
|
||||
own_rips = conn.rip_count
|
||||
for victim in victims:
|
||||
self._rip(victim)
|
||||
self._commit(conn, path)
|
||||
rerouted = 0
|
||||
for victim in victims:
|
||||
again = self._search(victim, allow_ripup=False, conns=conns)
|
||||
if again is not None:
|
||||
self._commit(victim, again)
|
||||
rerouted += 1
|
||||
if 1 + rerouted >= len(victims):
|
||||
return True # a gain, or an even trade that may pay off later
|
||||
for victim in victims:
|
||||
if victim.routed:
|
||||
self._rip(victim)
|
||||
self._rip(conn)
|
||||
conn.rip_count = own_rips
|
||||
for victim, old_path, a_end, b_end, rips in saved:
|
||||
victim.a_end, victim.b_end = a_end, b_end
|
||||
self._commit(victim, old_path)
|
||||
victim.rip_count = rips
|
||||
return False
|
||||
|
||||
def _build_connections(self) -> list[_Connection]:
|
||||
pins_by_net = self._pins_by_net()
|
||||
zones_by_net: dict[int, list] = {}
|
||||
@@ -1189,6 +1228,7 @@ def route_board(
|
||||
seed: int | None = None,
|
||||
route_first: frozenset | None = None,
|
||||
access_cost: float = 0.0,
|
||||
rollback: bool = False,
|
||||
) -> RouteResult:
|
||||
"""Convenience wrapper: build a :class:`GridRouter` and route the board."""
|
||||
router = GridRouter(
|
||||
@@ -1207,5 +1247,6 @@ def route_board(
|
||||
seed=seed,
|
||||
route_first=route_first,
|
||||
access_cost=access_cost,
|
||||
rollback=rollback,
|
||||
)
|
||||
return router.route()
|
||||
|
||||
Reference in New Issue
Block a user