[planner] try bends=2 before bends=(2, 4)
This commit is contained in:
parent
b6c222cdc2
commit
54c4cd9a4a
2 changed files with 108 additions and 11 deletions
|
|
@ -50,6 +50,7 @@ from .interface import (
|
||||||
PreparedRouteResult,
|
PreparedRouteResult,
|
||||||
RoutePlanningError,
|
RoutePlanningError,
|
||||||
RoutePortContext,
|
RoutePortContext,
|
||||||
|
route_error_is_fatal,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -908,6 +909,12 @@ class RoutingPlanner:
|
||||||
|
|
||||||
TRACE_INTO_MAX_BENDS: int = 4
|
TRACE_INTO_MAX_BENDS: int = 4
|
||||||
|
|
||||||
|
def trace_into_bend_budgets(self, family: PrimitiveKind) -> tuple[int, ...]:
|
||||||
|
"""Return staged trace_into bend budgets for the route rotation parity."""
|
||||||
|
if family == 'bend':
|
||||||
|
return tuple(budget for budget in (1, 3) if budget <= self.TRACE_INTO_MAX_BENDS)
|
||||||
|
return tuple(budget for budget in (2, 4) if budget <= self.TRACE_INTO_MAX_BENDS)
|
||||||
|
|
||||||
def plan_leg(
|
def plan_leg(
|
||||||
self,
|
self,
|
||||||
family: PrimitiveKind,
|
family: PrimitiveKind,
|
||||||
|
|
@ -1197,17 +1204,30 @@ class RoutingPlanner:
|
||||||
desired.rotation = port_dst.rotation - pi
|
desired.rotation = port_dst.rotation - pi
|
||||||
desired.ptype = out_ptype
|
desired.ptype = out_ptype
|
||||||
family, length, jog, ccw = self.trace_into_spec(context_src.port, desired)
|
family, length, jog, ccw = self.trace_into_spec(context_src.port, desired)
|
||||||
leg = self.plan_leg(
|
leg = None
|
||||||
family,
|
last_error: Exception | None = None
|
||||||
context_src,
|
for max_bends in self.trace_into_bend_budgets(family):
|
||||||
length=length,
|
try:
|
||||||
jog=jog,
|
leg = self.plan_leg(
|
||||||
ccw=ccw,
|
family,
|
||||||
plug_into=portspec_dst if plug_destination else None,
|
context_src,
|
||||||
constrain_jog=family == 'bend',
|
length=length,
|
||||||
max_bends=self.TRACE_INTO_MAX_BENDS,
|
jog=jog,
|
||||||
**(dict(kwargs) | {'out_ptype': out_ptype}),
|
ccw=ccw,
|
||||||
)
|
plug_into=portspec_dst if plug_destination else None,
|
||||||
|
constrain_jog=family == 'bend',
|
||||||
|
max_bends=max_bends,
|
||||||
|
**(dict(kwargs) | {'out_ptype': out_ptype}),
|
||||||
|
)
|
||||||
|
break
|
||||||
|
except (BuildError, NotImplementedError) as err:
|
||||||
|
if route_error_is_fatal(err):
|
||||||
|
raise
|
||||||
|
last_error = err
|
||||||
|
if leg is None:
|
||||||
|
if last_error is not None:
|
||||||
|
raise last_error
|
||||||
|
raise BuildError('No legal primitive offer for trace_into route')
|
||||||
renames = ((thru, context_src.portspec),) if thru is not None else ()
|
renames = ((thru, context_src.portspec),) if thru is not None else ()
|
||||||
return self.prepared_result_from_legs(
|
return self.prepared_result_from_legs(
|
||||||
(leg,),
|
(leg,),
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ from numpy import pi
|
||||||
from numpy.testing import assert_equal
|
from numpy.testing import assert_equal
|
||||||
|
|
||||||
from masque import Library, PathTool, Port, Pather
|
from masque import Library, PathTool, Port, Pather
|
||||||
|
from masque.builder.planner import PreparedRouteResult, RoutePlanningError, RoutePortContext, RoutingPlanner
|
||||||
|
from masque.builder.planner.planner import Candidate, RouteLeg
|
||||||
from masque.builder.tools import BendOffer, PrimitiveOffer, StraightOffer, Tool
|
from masque.builder.tools import BendOffer, PrimitiveOffer, StraightOffer, Tool
|
||||||
from masque.error import BuildError, PortError
|
from masque.error import BuildError, PortError
|
||||||
|
|
||||||
|
|
@ -260,3 +262,78 @@ def test_pather_trace_into_rejects_reserved_route_kwargs(
|
||||||
assert p.pattern.ports['B'].rotation is not None
|
assert p.pattern.ports['B'].rotation is not None
|
||||||
assert numpy.isclose(p.pattern.ports['B'].rotation, dst.rotation)
|
assert numpy.isclose(p.pattern.ports['B'].rotation, dst.rotation)
|
||||||
assert len(p._paths['A']) == 0
|
assert len(p._paths['A']) == 0
|
||||||
|
|
||||||
|
|
||||||
|
class TraceIntoBudgetPlanner(RoutingPlanner):
|
||||||
|
def __init__(self, successes: set[int], fatal_at: set[int] | None = None) -> None:
|
||||||
|
self.successes = successes
|
||||||
|
self.fatal_at = set() if fatal_at is None else fatal_at
|
||||||
|
self.attempts: list[int | None] = []
|
||||||
|
|
||||||
|
def plan_leg(
|
||||||
|
self,
|
||||||
|
family: Any,
|
||||||
|
context: RoutePortContext,
|
||||||
|
*,
|
||||||
|
length: float | None = None,
|
||||||
|
jog: float | None = None,
|
||||||
|
ccw: Any = None,
|
||||||
|
plug_into: str | None = None,
|
||||||
|
constrain_jog: bool = False,
|
||||||
|
max_bends: int | None = None,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> RouteLeg:
|
||||||
|
_ = family, length, jog, ccw, constrain_jog, kwargs
|
||||||
|
self.attempts.append(max_bends)
|
||||||
|
if max_bends in self.fatal_at:
|
||||||
|
raise RoutePlanningError('fatal', fatal=True)
|
||||||
|
if max_bends not in self.successes:
|
||||||
|
raise BuildError('try next budget')
|
||||||
|
return RouteLeg(
|
||||||
|
portspec=context.portspec,
|
||||||
|
start_port=context.port.copy(),
|
||||||
|
tool=context.tool,
|
||||||
|
candidate=Candidate((), context.port.copy(), 0.0, 0, 0.0),
|
||||||
|
plug_into=plug_into,
|
||||||
|
)
|
||||||
|
|
||||||
|
def prepared_result_from_legs(
|
||||||
|
self,
|
||||||
|
legs: Any,
|
||||||
|
*,
|
||||||
|
renames: tuple[tuple[str, str], ...] = (),
|
||||||
|
) -> PreparedRouteResult:
|
||||||
|
_ = legs, renames
|
||||||
|
return PreparedRouteResult(())
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
('dst', 'successes', 'attempts'),
|
||||||
|
[
|
||||||
|
(Port((-10, 0), rotation=pi, ptype='wire'), {2}, [2]),
|
||||||
|
(Port((-10, 0), rotation=pi, ptype='wire'), {4}, [2, 4]),
|
||||||
|
(Port((-10, -10), rotation=3 * pi / 2, ptype='wire'), {1}, [1]),
|
||||||
|
(Port((-10, -10), rotation=3 * pi / 2, ptype='wire'), {3}, [1, 3]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_trace_into_uses_staged_bend_budgets(
|
||||||
|
dst: Port,
|
||||||
|
successes: set[int],
|
||||||
|
attempts: list[int],
|
||||||
|
) -> None:
|
||||||
|
planner = TraceIntoBudgetPlanner(successes)
|
||||||
|
context = RoutePortContext('src', Port((0, 0), rotation=0, ptype='wire'), PathTool(layer='M1', width=1, ptype='wire'))
|
||||||
|
|
||||||
|
planner.plan_trace_into(context, 'dst', dst, out_ptype=None, plug_destination=True, thru=None)
|
||||||
|
|
||||||
|
assert planner.attempts == attempts
|
||||||
|
|
||||||
|
|
||||||
|
def test_trace_into_staged_bend_budget_stops_on_fatal_error() -> None:
|
||||||
|
planner = TraceIntoBudgetPlanner({4}, fatal_at={2})
|
||||||
|
context = RoutePortContext('src', Port((0, 0), rotation=0, ptype='wire'), PathTool(layer='M1', width=1, ptype='wire'))
|
||||||
|
|
||||||
|
with pytest.raises(RoutePlanningError, match='fatal'):
|
||||||
|
planner.plan_trace_into(context, 'dst', Port((-10, 0), rotation=pi, ptype='wire'), out_ptype=None, plug_destination=True, thru=None)
|
||||||
|
|
||||||
|
assert planner.attempts == [2]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue