example fixes and improvements
This commit is contained in:
parent
e11132b51d
commit
e2c91076f7
18 changed files with 336 additions and 144 deletions
5
DOCS.md
5
DOCS.md
|
|
@ -80,7 +80,9 @@ Use `RoutingProblem.initial_paths` to provide semantic per-net seeds. Seeds are
|
||||||
| `bend_radii` | `(50.0, 100.0)` | Available radii for 90-degree bends. |
|
| `bend_radii` | `(50.0, 100.0)` | Available radii for 90-degree bends. |
|
||||||
| `sbend_radii` | `(10.0,)` | Available radii for S-bends. |
|
| `sbend_radii` | `(10.0,)` | Available radii for S-bends. |
|
||||||
| `sbend_offsets` | `None` | Optional explicit lateral offsets for S-bends. |
|
| `sbend_offsets` | `None` | Optional explicit lateral offsets for S-bends. |
|
||||||
| `bend_collision_type` | `"arc"` | Bend collision model: `"arc"`, `"bbox"`, `"clipped_bbox"`, or a custom polygon. |
|
| `bend_collision_type` | `"arc"` | Bend collision/proxy model: `"arc"`, `"bbox"`, `"clipped_bbox"`, or, for backward compatibility, a custom polygon. A legacy custom polygon here is treated as both the physical bend and its proxy unless overridden by the split fields below. |
|
||||||
|
| `bend_proxy_geometry` | `None` | Optional explicit bend proxy geometry. Use this when you want a custom search/collision envelope that differs from the routed bend shape. Supplying only a custom polygon proxy warns and keeps the physical bend as the standard arc. |
|
||||||
|
| `bend_physical_geometry` | `None` | Optional explicit bend physical geometry. Use `"arc"` or a custom polygon. If you set a custom physical polygon and do not set a proxy, the proxy defaults to the same polygon. |
|
||||||
| `bend_clip_margin` | `None` | Optional legacy shrink margin for `"clipped_bbox"`. Leave `None` for the default 8-point proxy. |
|
| `bend_clip_margin` | `None` | Optional legacy shrink margin for `"clipped_bbox"`. Leave `None` for the default 8-point proxy. |
|
||||||
| `visibility_guidance` | `"tangent_corner"` | Visibility-derived straight candidate strategy. |
|
| `visibility_guidance` | `"tangent_corner"` | Visibility-derived straight candidate strategy. |
|
||||||
|
|
||||||
|
|
@ -161,6 +163,7 @@ Lower-level search and collision modules are semi-private implementation details
|
||||||
|
|
||||||
- Increase `objective.bend_penalty` to discourage ladders of small bends.
|
- Increase `objective.bend_penalty` to discourage ladders of small bends.
|
||||||
- Increase available `search.bend_radii` when larger turns are physically acceptable.
|
- Increase available `search.bend_radii` when larger turns are physically acceptable.
|
||||||
|
- Use `search.bend_physical_geometry` and `search.bend_proxy_geometry` together when you need a real custom bend shape plus a different conservative proxy.
|
||||||
|
|
||||||
### Visibility guidance
|
### Visibility guidance
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ def main() -> None:
|
||||||
greedy_h_weight=1.5,
|
greedy_h_weight=1.5,
|
||||||
),
|
),
|
||||||
objective=ObjectiveWeights(
|
objective=ObjectiveWeights(
|
||||||
bend_penalty=250.0,
|
bend_penalty=50.0,
|
||||||
sbend_penalty=500.0,
|
sbend_penalty=150.0,
|
||||||
),
|
),
|
||||||
congestion=CongestionOptions(base_penalty=1000.0),
|
congestion=CongestionOptions(base_penalty=1000.0),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
from inire import NetSpec, ObjectiveWeights, Port, RoutingOptions, RoutingProblem, SearchOptions, route
|
from inire import NetSpec, Port, RoutingOptions, RoutingProblem, SearchOptions
|
||||||
|
from inire.router._astar_types import AStarContext
|
||||||
|
from inire.router._router import PathFinder
|
||||||
|
from inire.router._stack import build_routing_stack
|
||||||
from inire.utils.visualization import plot_routing_results
|
from inire.utils.visualization import plot_routing_results
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -6,31 +9,31 @@ def main() -> None:
|
||||||
print("Running Example 03: Locked Paths...")
|
print("Running Example 03: Locked Paths...")
|
||||||
|
|
||||||
bounds = (0, -50, 100, 50)
|
bounds = (0, -50, 100, 50)
|
||||||
options = RoutingOptions(
|
|
||||||
search=SearchOptions(bend_radii=(10.0,)),
|
|
||||||
objective=ObjectiveWeights(
|
|
||||||
bend_penalty=250.0,
|
|
||||||
sbend_penalty=500.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
print("Routing initial net...")
|
print("Routing initial net...")
|
||||||
results_a = route(
|
stack = build_routing_stack(
|
||||||
RoutingProblem(
|
problem=RoutingProblem(
|
||||||
bounds=bounds,
|
bounds=bounds,
|
||||||
nets=(NetSpec("netA", Port(10, 0, 0), Port(90, 0, 0), width=2.0),),
|
nets=(NetSpec("netA", Port(10, 0, 0), Port(90, 0, 0), width=2.0),),
|
||||||
),
|
),
|
||||||
options=options,
|
options=RoutingOptions(search=SearchOptions(bend_radii=(10.0,))),
|
||||||
).results_by_net
|
)
|
||||||
|
engine = stack.world
|
||||||
|
evaluator = stack.evaluator
|
||||||
|
results_a = stack.finder.route_all()
|
||||||
|
|
||||||
print("Routing detour net around locked path...")
|
print("Routing detour net around locked path...")
|
||||||
results_b = route(
|
for polygon in results_a["netA"].locked_geometry:
|
||||||
RoutingProblem(
|
engine.add_static_obstacle(polygon)
|
||||||
bounds=bounds,
|
results_b = PathFinder(
|
||||||
nets=(NetSpec("netB", Port(50, -20, 90), Port(50, 20, 90), width=2.0),),
|
AStarContext(
|
||||||
static_obstacles=results_a["netA"].locked_geometry,
|
evaluator,
|
||||||
|
RoutingProblem(
|
||||||
|
bounds=bounds,
|
||||||
|
nets=(NetSpec("netB", Port(50, -20, 90), Port(50, 20, 90), width=2.0),),
|
||||||
|
),
|
||||||
|
RoutingOptions(search=SearchOptions(bend_radii=(10.0,))),
|
||||||
),
|
),
|
||||||
options=options,
|
).route_all()
|
||||||
).results_by_net
|
|
||||||
|
|
||||||
results = {**results_a, **results_b}
|
results = {**results_a, **results_b}
|
||||||
fig, ax = plot_routing_results(results, [], bounds)
|
fig, ax = plot_routing_results(results, [], bounds)
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 78 KiB |
|
|
@ -1,6 +1,7 @@
|
||||||
from shapely.geometry import Polygon
|
from shapely.geometry import Polygon
|
||||||
|
|
||||||
from inire import CongestionOptions, NetSpec, ObjectiveWeights, RoutingOptions, RoutingProblem, RoutingResult, SearchOptions, route
|
from inire import CongestionOptions, NetSpec, ObjectiveWeights, RoutingOptions, RoutingProblem, RoutingResult, SearchOptions, route
|
||||||
|
from inire.geometry.components import BendCollisionModel, BendPhysicalGeometry
|
||||||
from inire.geometry.primitives import Port
|
from inire.geometry.primitives import Port
|
||||||
from inire.utils.visualization import plot_routing_results
|
from inire.utils.visualization import plot_routing_results
|
||||||
|
|
||||||
|
|
@ -8,10 +9,12 @@ from inire.utils.visualization import plot_routing_results
|
||||||
def _route_scenario(
|
def _route_scenario(
|
||||||
bounds: tuple[float, float, float, float],
|
bounds: tuple[float, float, float, float],
|
||||||
obstacles: list[Polygon],
|
obstacles: list[Polygon],
|
||||||
bend_collision_type: str,
|
|
||||||
netlist: dict[str, tuple[Port, Port]],
|
netlist: dict[str, tuple[Port, Port]],
|
||||||
widths: dict[str, float],
|
widths: dict[str, float],
|
||||||
*,
|
*,
|
||||||
|
bend_collision_type: BendCollisionModel = "arc",
|
||||||
|
bend_proxy_geometry: BendCollisionModel | None = None,
|
||||||
|
bend_physical_geometry: BendPhysicalGeometry | None = None,
|
||||||
bend_clip_margin: float | None = None,
|
bend_clip_margin: float | None = None,
|
||||||
) -> dict[str, RoutingResult]:
|
) -> dict[str, RoutingResult]:
|
||||||
problem = RoutingProblem(
|
problem = RoutingProblem(
|
||||||
|
|
@ -23,6 +26,8 @@ def _route_scenario(
|
||||||
search=SearchOptions(
|
search=SearchOptions(
|
||||||
bend_radii=(10.0,),
|
bend_radii=(10.0,),
|
||||||
bend_collision_type=bend_collision_type,
|
bend_collision_type=bend_collision_type,
|
||||||
|
bend_proxy_geometry=bend_proxy_geometry,
|
||||||
|
bend_physical_geometry=bend_physical_geometry,
|
||||||
bend_clip_margin=bend_clip_margin,
|
bend_clip_margin=bend_clip_margin,
|
||||||
),
|
),
|
||||||
objective=ObjectiveWeights(
|
objective=ObjectiveWeights(
|
||||||
|
|
@ -40,29 +45,30 @@ def main() -> None:
|
||||||
bounds = (-20, -20, 170, 170)
|
bounds = (-20, -20, 170, 170)
|
||||||
obs_arc = Polygon([(40, 110), (60, 110), (60, 130), (40, 130)])
|
obs_arc = Polygon([(40, 110), (60, 110), (60, 130), (40, 130)])
|
||||||
obs_bbox = Polygon([(40, 60), (60, 60), (60, 80), (40, 80)])
|
obs_bbox = Polygon([(40, 60), (60, 60), (60, 80), (40, 80)])
|
||||||
obs_clipped = Polygon([(40, 10), (60, 10), (60, 30), (40, 30)])
|
obs_custom = Polygon([(40, 10), (60, 10), (60, 30), (40, 30)])
|
||||||
|
custom_bend = Polygon([(0, -11), (11, -11), (11, 0), (9, 0), (9, -9), (0, -9)])
|
||||||
|
|
||||||
obstacles = [obs_arc, obs_bbox, obs_clipped]
|
obstacles = [obs_arc, obs_bbox, obs_custom]
|
||||||
netlist_arc = {"arc_model": (Port(10, 120, 0), Port(90, 140, 90))}
|
netlist_arc = {"arc_model": (Port(10, 120, 0), Port(90, 140, 90))}
|
||||||
netlist_bbox = {"bbox_model": (Port(10, 70, 0), Port(90, 90, 90))}
|
netlist_bbox = {"bbox_model": (Port(10, 70, 0), Port(90, 90, 90))}
|
||||||
netlist_clipped = {"clipped_model": (Port(10, 20, 0), Port(90, 40, 90))}
|
netlist_custom = {"custom_geometry": (Port(10, 20, 0), Port(90, 40, 90))}
|
||||||
|
|
||||||
print("Routing Scenario 1 (Arc)...")
|
print("Routing Scenario 1 (Arc)...")
|
||||||
res_arc = _route_scenario(bounds, obstacles, "arc", netlist_arc, {"arc_model": 2.0})
|
res_arc = _route_scenario(bounds, obstacles, netlist_arc, {"arc_model": 2.0}, bend_collision_type="arc")
|
||||||
print("Routing Scenario 2 (BBox)...")
|
print("Routing Scenario 2 (BBox)...")
|
||||||
res_bbox = _route_scenario(bounds, obstacles, "bbox", netlist_bbox, {"bbox_model": 2.0})
|
res_bbox = _route_scenario(bounds, obstacles, netlist_bbox, {"bbox_model": 2.0}, bend_collision_type="bbox")
|
||||||
print("Routing Scenario 3 (Clipped BBox)...")
|
print("Routing Scenario 3 (Custom Manhattan Geometry With Matching Proxy)...")
|
||||||
res_clipped = _route_scenario(
|
res_custom = _route_scenario(
|
||||||
bounds,
|
bounds,
|
||||||
obstacles,
|
obstacles,
|
||||||
"clipped_bbox",
|
netlist_custom,
|
||||||
netlist_clipped,
|
{"custom_geometry": 2.0},
|
||||||
{"clipped_model": 2.0},
|
bend_physical_geometry=custom_bend,
|
||||||
bend_clip_margin=1.0,
|
bend_proxy_geometry=custom_bend,
|
||||||
)
|
)
|
||||||
|
|
||||||
all_results = {**res_arc, **res_bbox, **res_clipped}
|
all_results = {**res_arc, **res_bbox, **res_custom}
|
||||||
all_netlists = {**netlist_arc, **netlist_bbox, **netlist_clipped}
|
all_netlists = {**netlist_arc, **netlist_bbox, **netlist_custom}
|
||||||
|
|
||||||
fig, _ax = plot_routing_results(all_results, obstacles, bounds, netlist=all_netlists)
|
fig, _ax = plot_routing_results(all_results, obstacles, bounds, netlist=all_netlists)
|
||||||
fig.savefig("examples/06_bend_collision_models.png")
|
fig.savefig("examples/06_bend_collision_models.png")
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 60 KiB |
|
|
@ -1,65 +1,58 @@
|
||||||
from shapely.geometry import Polygon
|
from shapely.geometry import Polygon, box
|
||||||
|
|
||||||
from inire import CongestionOptions, NetSpec, RoutingOptions, RoutingProblem, SearchOptions
|
from inire import CongestionOptions, NetSpec, RoutingOptions, RoutingProblem, SearchOptions, route
|
||||||
from inire.geometry.collision import RoutingWorld
|
from inire.geometry.components import BendCollisionModel, BendPhysicalGeometry
|
||||||
from inire.geometry.primitives import Port
|
from inire.geometry.primitives import Port
|
||||||
from inire.router._astar_types import AStarContext, AStarMetrics
|
|
||||||
from inire.router._router import PathFinder
|
|
||||||
from inire.router.cost import CostEvaluator
|
|
||||||
from inire.router.danger_map import DangerMap
|
|
||||||
from inire.utils.visualization import plot_routing_results
|
from inire.utils.visualization import plot_routing_results
|
||||||
|
|
||||||
|
|
||||||
|
def _run_session(
|
||||||
|
bounds: tuple[float, float, float, float],
|
||||||
|
net_id: str,
|
||||||
|
start: Port,
|
||||||
|
target: Port,
|
||||||
|
*,
|
||||||
|
bend_collision_type: BendCollisionModel = "arc",
|
||||||
|
bend_proxy_geometry: BendCollisionModel | None = None,
|
||||||
|
bend_physical_geometry: BendPhysicalGeometry | None = None,
|
||||||
|
) -> dict[str, object]:
|
||||||
|
problem = RoutingProblem(
|
||||||
|
bounds=bounds,
|
||||||
|
nets=(NetSpec(net_id, start, target, width=2.0),),
|
||||||
|
)
|
||||||
|
options = RoutingOptions(
|
||||||
|
search=SearchOptions(
|
||||||
|
bend_radii=(10.0,),
|
||||||
|
bend_collision_type=bend_collision_type,
|
||||||
|
bend_proxy_geometry=bend_proxy_geometry,
|
||||||
|
bend_physical_geometry=bend_physical_geometry,
|
||||||
|
sbend_radii=(),
|
||||||
|
),
|
||||||
|
congestion=CongestionOptions(max_iterations=1, use_tiered_strategy=False),
|
||||||
|
)
|
||||||
|
return route(problem, options=options).results_by_net
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
print("Running Example 08: Custom Bend Geometry...")
|
print("Running Example 08: Custom Bend Geometry...")
|
||||||
|
|
||||||
bounds = (0, 0, 150, 150)
|
bounds = (0, 0, 150, 150)
|
||||||
engine = RoutingWorld(clearance=2.0)
|
|
||||||
danger_map = DangerMap(bounds=bounds)
|
|
||||||
danger_map.precompute([])
|
|
||||||
evaluator = CostEvaluator(engine, danger_map, bend_penalty=50.0, sbend_penalty=150.0)
|
|
||||||
metrics = AStarMetrics()
|
|
||||||
start = Port(20, 20, 0)
|
start = Port(20, 20, 0)
|
||||||
target = Port(100, 100, 90)
|
target = Port(100, 100, 90)
|
||||||
|
custom_physical = Polygon([(0, -11), (11, -11), (11, 0), (9, 0), (9, -9), (0, -9)])
|
||||||
|
custom_proxy = box(0, -11, 11, 0)
|
||||||
|
|
||||||
print("Routing with standard arc...")
|
print("Routing standard arc in its own session...")
|
||||||
results_std = PathFinder(
|
results_std = _run_session(bounds, "standard_arc", start, target)
|
||||||
AStarContext(
|
print("Routing custom geometry with a separate custom proxy in its own session...")
|
||||||
evaluator,
|
results_custom = _run_session(
|
||||||
RoutingProblem(
|
bounds,
|
||||||
bounds=bounds,
|
"custom_geometry_and_proxy",
|
||||||
nets=(NetSpec("custom_bend", start, target, width=2.0),),
|
start,
|
||||||
),
|
target,
|
||||||
RoutingOptions(
|
bend_physical_geometry=custom_physical,
|
||||||
search=SearchOptions(bend_radii=(10.0,), sbend_radii=()),
|
bend_proxy_geometry=custom_proxy,
|
||||||
congestion=CongestionOptions(max_iterations=1),
|
)
|
||||||
),
|
|
||||||
),
|
|
||||||
metrics=metrics,
|
|
||||||
).route_all()
|
|
||||||
|
|
||||||
custom_poly = Polygon([(-10, -10), (10, -10), (10, 10), (-10, 10)])
|
|
||||||
|
|
||||||
print("Routing with custom collision model...")
|
|
||||||
results_custom = PathFinder(
|
|
||||||
AStarContext(
|
|
||||||
evaluator,
|
|
||||||
RoutingProblem(
|
|
||||||
bounds=bounds,
|
|
||||||
nets=(NetSpec("custom_model", start, target, width=2.0),),
|
|
||||||
),
|
|
||||||
RoutingOptions(
|
|
||||||
search=SearchOptions(
|
|
||||||
bend_radii=(10.0,),
|
|
||||||
bend_collision_type=custom_poly,
|
|
||||||
sbend_radii=(),
|
|
||||||
),
|
|
||||||
congestion=CongestionOptions(max_iterations=1, use_tiered_strategy=False),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
metrics=AStarMetrics(),
|
|
||||||
use_tiered_strategy=False,
|
|
||||||
).route_all()
|
|
||||||
|
|
||||||
all_results = {**results_std, **results_custom}
|
all_results = {**results_std, **results_custom}
|
||||||
fig, _ax = plot_routing_results(
|
fig, _ax = plot_routing_results(
|
||||||
|
|
@ -67,8 +60,8 @@ def main() -> None:
|
||||||
[],
|
[],
|
||||||
bounds,
|
bounds,
|
||||||
netlist={
|
netlist={
|
||||||
"custom_bend": (start, target),
|
"standard_arc": (start, target),
|
||||||
"custom_model": (start, target),
|
"custom_geometry_and_proxy": (start, target),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
fig.savefig("examples/08_custom_bend_geometry.png")
|
fig.savefig("examples/08_custom_bend_geometry.png")
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,14 @@ Demonstrates the Negotiated Congestion algorithm handling multiple intersecting
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 2. Custom Bend Geometry Models
|
## 2. Bend Geometry Models
|
||||||
`inire` supports multiple collision models for bends, allowing a trade-off between search speed and geometric accuracy:
|
`inire` supports multiple collision models for bends, allowing a trade-off between search speed and geometric accuracy:
|
||||||
* **Arc**: High-fidelity geometry (Highest accuracy).
|
* **Arc**: High-fidelity geometry (Highest accuracy).
|
||||||
* **BBox**: Simple axis-aligned bounding box (Fastest search).
|
* **BBox**: Simple axis-aligned bounding box (Fastest search).
|
||||||
* **Clipped BBox**: A balanced model that clips the corners of the AABB to better fit the arc (Optimal performance).
|
* **Custom Manhattan Geometry**: A custom 90-degree bend polygon with the same width as the normal waveguide.
|
||||||
|
|
||||||
Example 08 also demonstrates a custom polygonal bend geometry. It uses a centered `20x20` box as a custom bend collision model.
|
Example 06 uses the Manhattan polygon as both the true routed bend geometry and the collision proxy.
|
||||||
|
Example 08 compares the standard arc against a run that uses a custom physical bend plus a separate custom proxy polygon, with each net routed in its own session.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ from .primitives import Port, rotation_matrix2
|
||||||
MoveKind = Literal["straight", "bend90", "sbend"]
|
MoveKind = Literal["straight", "bend90", "sbend"]
|
||||||
BendCollisionModelName = Literal["arc", "bbox", "clipped_bbox"]
|
BendCollisionModelName = Literal["arc", "bbox", "clipped_bbox"]
|
||||||
BendCollisionModel = BendCollisionModelName | Polygon
|
BendCollisionModel = BendCollisionModelName | Polygon
|
||||||
|
BendPhysicalGeometry = Literal["arc"] | Polygon
|
||||||
|
|
||||||
|
|
||||||
def _normalize_length(value: float) -> float:
|
def _normalize_length(value: float) -> float:
|
||||||
|
|
@ -281,6 +282,7 @@ class Bend90:
|
||||||
direction: Literal["CW", "CCW"],
|
direction: Literal["CW", "CCW"],
|
||||||
sagitta: float = 0.01,
|
sagitta: float = 0.01,
|
||||||
collision_type: BendCollisionModel = "arc",
|
collision_type: BendCollisionModel = "arc",
|
||||||
|
physical_geometry_type: BendPhysicalGeometry = "arc",
|
||||||
clip_margin: float | None = None,
|
clip_margin: float | None = None,
|
||||||
dilation: float = 0.0,
|
dilation: float = 0.0,
|
||||||
) -> ComponentResult:
|
) -> ComponentResult:
|
||||||
|
|
@ -310,16 +312,33 @@ class Bend90:
|
||||||
mirror_y=(sign < 0),
|
mirror_y=(sign < 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
physical_geometry = arc_polys
|
if isinstance(physical_geometry_type, Polygon):
|
||||||
if dilation > 0:
|
physical_geometry = _apply_collision_model(
|
||||||
dilated_physical_geometry = _get_arc_polygons(
|
arc_polys[0],
|
||||||
(float(center_xy[0]), float(center_xy[1])),
|
physical_geometry_type,
|
||||||
radius,
|
radius,
|
||||||
width,
|
width,
|
||||||
|
(float(center_xy[0]), float(center_xy[1])),
|
||||||
ts,
|
ts,
|
||||||
sagitta,
|
rotation_deg=float(start_port.r),
|
||||||
dilation=dilation,
|
mirror_y=(sign < 0),
|
||||||
)
|
)
|
||||||
|
uses_physical_custom_geometry = True
|
||||||
|
else:
|
||||||
|
physical_geometry = arc_polys
|
||||||
|
uses_physical_custom_geometry = False
|
||||||
|
if dilation > 0:
|
||||||
|
if uses_physical_custom_geometry:
|
||||||
|
dilated_physical_geometry = [poly.buffer(dilation) for poly in physical_geometry]
|
||||||
|
else:
|
||||||
|
dilated_physical_geometry = _get_arc_polygons(
|
||||||
|
(float(center_xy[0]), float(center_xy[1])),
|
||||||
|
radius,
|
||||||
|
width,
|
||||||
|
ts,
|
||||||
|
sagitta,
|
||||||
|
dilation=dilation,
|
||||||
|
)
|
||||||
dilated_collision_geometry = (
|
dilated_collision_geometry = (
|
||||||
dilated_physical_geometry if collision_type == "arc" else [poly.buffer(dilation) for poly in collision_polys]
|
dilated_physical_geometry if collision_type == "arc" else [poly.buffer(dilation) for poly in collision_polys]
|
||||||
)
|
)
|
||||||
|
|
@ -349,6 +368,7 @@ class SBend:
|
||||||
width: float,
|
width: float,
|
||||||
sagitta: float = 0.01,
|
sagitta: float = 0.01,
|
||||||
collision_type: BendCollisionModel = "arc",
|
collision_type: BendCollisionModel = "arc",
|
||||||
|
physical_geometry_type: BendPhysicalGeometry = "arc",
|
||||||
clip_margin: float | None = None,
|
clip_margin: float | None = None,
|
||||||
dilation: float = 0.0,
|
dilation: float = 0.0,
|
||||||
) -> ComponentResult:
|
) -> ComponentResult:
|
||||||
|
|
@ -402,12 +422,41 @@ class SBend:
|
||||||
)[0],
|
)[0],
|
||||||
]
|
]
|
||||||
|
|
||||||
physical_geometry = actual_geometry
|
if isinstance(physical_geometry_type, Polygon):
|
||||||
if dilation > 0:
|
physical_geometry = [
|
||||||
dilated_physical_geometry = [
|
_apply_collision_model(
|
||||||
_get_arc_polygons((float(c1_xy[0]), float(c1_xy[1])), radius, width, ts1, sagitta, dilation=dilation)[0],
|
arc1,
|
||||||
_get_arc_polygons((float(c2_xy[0]), float(c2_xy[1])), radius, width, ts2, sagitta, dilation=dilation)[0],
|
physical_geometry_type,
|
||||||
|
radius,
|
||||||
|
width,
|
||||||
|
(float(c1_xy[0]), float(c1_xy[1])),
|
||||||
|
ts1,
|
||||||
|
rotation_deg=float(start_port.r),
|
||||||
|
mirror_y=(sign < 0),
|
||||||
|
)[0],
|
||||||
|
_apply_collision_model(
|
||||||
|
arc2,
|
||||||
|
physical_geometry_type,
|
||||||
|
radius,
|
||||||
|
width,
|
||||||
|
(float(c2_xy[0]), float(c2_xy[1])),
|
||||||
|
ts2,
|
||||||
|
rotation_deg=float(start_port.r),
|
||||||
|
mirror_y=(sign > 0),
|
||||||
|
)[0],
|
||||||
]
|
]
|
||||||
|
uses_physical_custom_geometry = True
|
||||||
|
else:
|
||||||
|
physical_geometry = actual_geometry
|
||||||
|
uses_physical_custom_geometry = False
|
||||||
|
if dilation > 0:
|
||||||
|
if uses_physical_custom_geometry:
|
||||||
|
dilated_physical_geometry = [poly.buffer(dilation) for poly in physical_geometry]
|
||||||
|
else:
|
||||||
|
dilated_physical_geometry = [
|
||||||
|
_get_arc_polygons((float(c1_xy[0]), float(c1_xy[1])), radius, width, ts1, sagitta, dilation=dilation)[0],
|
||||||
|
_get_arc_polygons((float(c2_xy[0]), float(c2_xy[1])), radius, width, ts2, sagitta, dilation=dilation)[0],
|
||||||
|
]
|
||||||
dilated_collision_geometry = (
|
dilated_collision_geometry = (
|
||||||
dilated_physical_geometry if collision_type == "arc" else [poly.buffer(dilation) for poly in geometry]
|
dilated_physical_geometry if collision_type == "arc" else [poly.buffer(dilation) for poly in geometry]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import warnings
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import TYPE_CHECKING, Literal
|
from typing import TYPE_CHECKING, Literal
|
||||||
|
|
||||||
from inire.geometry.components import BendCollisionModel
|
from shapely.geometry import Polygon
|
||||||
|
|
||||||
|
from inire.geometry.components import BendCollisionModel, BendPhysicalGeometry
|
||||||
from inire.seeds import PathSeed
|
from inire.seeds import PathSeed
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from shapely.geometry import Polygon
|
|
||||||
|
|
||||||
from inire.geometry.components import BendCollisionModel
|
|
||||||
from inire.geometry.primitives import Port
|
from inire.geometry.primitives import Port
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -43,6 +43,8 @@ class SearchOptions:
|
||||||
bend_radii: tuple[float, ...] = (50.0, 100.0)
|
bend_radii: tuple[float, ...] = (50.0, 100.0)
|
||||||
sbend_radii: tuple[float, ...] = (10.0,)
|
sbend_radii: tuple[float, ...] = (10.0,)
|
||||||
bend_collision_type: BendCollisionModel = "arc"
|
bend_collision_type: BendCollisionModel = "arc"
|
||||||
|
bend_proxy_geometry: BendCollisionModel | None = None
|
||||||
|
bend_physical_geometry: BendPhysicalGeometry | None = None
|
||||||
bend_clip_margin: float | None = None
|
bend_clip_margin: float | None = None
|
||||||
visibility_guidance: VisibilityGuidance = "tangent_corner"
|
visibility_guidance: VisibilityGuidance = "tangent_corner"
|
||||||
|
|
||||||
|
|
@ -51,6 +53,36 @@ class SearchOptions:
|
||||||
object.__setattr__(self, "sbend_radii", tuple(self.sbend_radii))
|
object.__setattr__(self, "sbend_radii", tuple(self.sbend_radii))
|
||||||
if self.sbend_offsets is not None:
|
if self.sbend_offsets is not None:
|
||||||
object.__setattr__(self, "sbend_offsets", tuple(self.sbend_offsets))
|
object.__setattr__(self, "sbend_offsets", tuple(self.sbend_offsets))
|
||||||
|
if self.bend_physical_geometry is None and isinstance(self.bend_proxy_geometry, Polygon):
|
||||||
|
warnings.warn(
|
||||||
|
"Custom bend proxy provided without bend_physical_geometry; routed bends will keep standard arc geometry.",
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_bend_geometry(
|
||||||
|
search: SearchOptions,
|
||||||
|
*,
|
||||||
|
bend_collision_override: BendCollisionModel | None = None,
|
||||||
|
) -> tuple[BendCollisionModel, BendPhysicalGeometry]:
|
||||||
|
bend_physical_geometry = search.bend_physical_geometry
|
||||||
|
if bend_physical_geometry is None and isinstance(search.bend_collision_type, Polygon) and search.bend_proxy_geometry is None:
|
||||||
|
bend_physical_geometry = search.bend_collision_type
|
||||||
|
if bend_physical_geometry is None:
|
||||||
|
bend_physical_geometry = "arc"
|
||||||
|
|
||||||
|
if bend_collision_override is not None:
|
||||||
|
bend_proxy_geometry = bend_collision_override
|
||||||
|
elif search.bend_proxy_geometry is not None:
|
||||||
|
bend_proxy_geometry = search.bend_proxy_geometry
|
||||||
|
elif isinstance(search.bend_collision_type, Polygon):
|
||||||
|
bend_proxy_geometry = search.bend_collision_type
|
||||||
|
elif bend_physical_geometry != "arc" and search.bend_collision_type == "arc":
|
||||||
|
bend_proxy_geometry = bend_physical_geometry
|
||||||
|
else:
|
||||||
|
bend_proxy_geometry = search.bend_collision_type
|
||||||
|
|
||||||
|
return bend_proxy_geometry, bend_physical_geometry
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ def process_move(
|
||||||
cp = parent.port
|
cp = parent.port
|
||||||
coll_type = config.bend_collision_type
|
coll_type = config.bend_collision_type
|
||||||
coll_key = id(coll_type) if isinstance(coll_type, Polygon) else coll_type
|
coll_key = id(coll_type) if isinstance(coll_type, Polygon) else coll_type
|
||||||
|
physical_type = config.bend_physical_geometry
|
||||||
|
physical_key = id(physical_type) if isinstance(physical_type, Polygon) else physical_type
|
||||||
self_dilation = context.cost_evaluator.collision_engine.clearance / 2.0
|
self_dilation = context.cost_evaluator.collision_engine.clearance / 2.0
|
||||||
|
|
||||||
abs_key = (
|
abs_key = (
|
||||||
|
|
@ -41,6 +43,7 @@ def process_move(
|
||||||
params,
|
params,
|
||||||
net_width,
|
net_width,
|
||||||
coll_key,
|
coll_key,
|
||||||
|
physical_key,
|
||||||
self_dilation,
|
self_dilation,
|
||||||
)
|
)
|
||||||
if abs_key in context.move_cache_abs:
|
if abs_key in context.move_cache_abs:
|
||||||
|
|
@ -54,6 +57,7 @@ def process_move(
|
||||||
params,
|
params,
|
||||||
net_width,
|
net_width,
|
||||||
coll_key,
|
coll_key,
|
||||||
|
physical_key,
|
||||||
self_dilation,
|
self_dilation,
|
||||||
)
|
)
|
||||||
if rel_key in context.move_cache_rel:
|
if rel_key in context.move_cache_rel:
|
||||||
|
|
@ -69,6 +73,7 @@ def process_move(
|
||||||
net_width,
|
net_width,
|
||||||
params[1],
|
params[1],
|
||||||
collision_type=coll_type,
|
collision_type=coll_type,
|
||||||
|
physical_geometry_type=config.bend_physical_geometry,
|
||||||
clip_margin=config.bend_clip_margin,
|
clip_margin=config.bend_clip_margin,
|
||||||
dilation=self_dilation,
|
dilation=self_dilation,
|
||||||
)
|
)
|
||||||
|
|
@ -79,6 +84,7 @@ def process_move(
|
||||||
params[1],
|
params[1],
|
||||||
net_width,
|
net_width,
|
||||||
collision_type=coll_type,
|
collision_type=coll_type,
|
||||||
|
physical_geometry_type=config.bend_physical_geometry,
|
||||||
clip_margin=config.bend_clip_margin,
|
clip_margin=config.bend_clip_margin,
|
||||||
dilation=self_dilation,
|
dilation=self_dilation,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from inire.geometry.components import BendCollisionModel
|
from inire.geometry.components import BendCollisionModel, BendPhysicalGeometry
|
||||||
from inire.model import RoutingOptions, RoutingProblem
|
from inire.model import RoutingOptions, RoutingProblem, resolve_bend_geometry
|
||||||
from inire.results import RouteMetrics
|
from inire.results import RouteMetrics
|
||||||
from inire.router.visibility import VisibilityManager
|
from inire.router.visibility import VisibilityManager
|
||||||
|
|
||||||
|
|
@ -16,6 +16,7 @@ if TYPE_CHECKING:
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
class SearchRunConfig:
|
class SearchRunConfig:
|
||||||
bend_collision_type: BendCollisionModel
|
bend_collision_type: BendCollisionModel
|
||||||
|
bend_physical_geometry: BendPhysicalGeometry
|
||||||
bend_clip_margin: float | None
|
bend_clip_margin: float | None
|
||||||
node_limit: int
|
node_limit: int
|
||||||
return_partial: bool = False
|
return_partial: bool = False
|
||||||
|
|
@ -38,8 +39,13 @@ class SearchRunConfig:
|
||||||
self_collision_check: bool = False,
|
self_collision_check: bool = False,
|
||||||
) -> SearchRunConfig:
|
) -> SearchRunConfig:
|
||||||
search = options.search
|
search = options.search
|
||||||
|
bend_collision_type, bend_physical_geometry = resolve_bend_geometry(
|
||||||
|
search,
|
||||||
|
bend_collision_override=bend_collision_type,
|
||||||
|
)
|
||||||
return cls(
|
return cls(
|
||||||
bend_collision_type=search.bend_collision_type if bend_collision_type is None else bend_collision_type,
|
bend_collision_type=bend_collision_type,
|
||||||
|
bend_physical_geometry=bend_physical_geometry,
|
||||||
bend_clip_margin=search.bend_clip_margin,
|
bend_clip_margin=search.bend_clip_margin,
|
||||||
node_limit=search.node_limit if node_limit is None else node_limit,
|
node_limit=search.node_limit if node_limit is None else node_limit,
|
||||||
return_partial=return_partial,
|
return_partial=return_partial,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from inire.model import NetOrder, NetSpec
|
from inire.model import NetOrder, NetSpec, resolve_bend_geometry
|
||||||
from inire.results import RoutingOutcome, RoutingReport, RoutingResult
|
from inire.results import RoutingOutcome, RoutingReport, RoutingResult
|
||||||
from inire.router._astar_types import AStarContext, AStarMetrics, SearchRunConfig
|
from inire.router._astar_types import AStarContext, AStarMetrics, SearchRunConfig
|
||||||
from inire.router._search import route_astar
|
from inire.router._search import route_astar
|
||||||
|
|
@ -173,7 +173,7 @@ class PathFinder:
|
||||||
if iteration == 0 and state.initial_paths and net_id in state.initial_paths:
|
if iteration == 0 and state.initial_paths and net_id in state.initial_paths:
|
||||||
path: Sequence[ComponentResult] | None = state.initial_paths[net_id]
|
path: Sequence[ComponentResult] | None = state.initial_paths[net_id]
|
||||||
else:
|
else:
|
||||||
coll_model = search.bend_collision_type
|
coll_model, _ = resolve_bend_geometry(search)
|
||||||
skip_congestion = False
|
skip_congestion = False
|
||||||
if congestion.use_tiered_strategy and iteration == 0:
|
if congestion.use_tiered_strategy and iteration == 0:
|
||||||
skip_congestion = True
|
skip_congestion = True
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from inire.model import SearchOptions
|
from inire.model import SearchOptions, resolve_bend_geometry
|
||||||
from inire.seeds import Bend90Seed, PathSeed, SBendSeed, StraightSeed
|
from inire.seeds import Bend90Seed, PathSeed, SBendSeed, StraightSeed
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
@ -23,7 +23,7 @@ def materialize_path_seed(
|
||||||
path: list[ComponentResult] = []
|
path: list[ComponentResult] = []
|
||||||
current = start
|
current = start
|
||||||
dilation = clearance / 2.0
|
dilation = clearance / 2.0
|
||||||
bend_collision_type = search.bend_collision_type
|
bend_collision_type, bend_physical_geometry = resolve_bend_geometry(search)
|
||||||
bend_clip_margin = search.bend_clip_margin
|
bend_clip_margin = search.bend_clip_margin
|
||||||
|
|
||||||
for segment in seed.segments:
|
for segment in seed.segments:
|
||||||
|
|
@ -36,6 +36,7 @@ def materialize_path_seed(
|
||||||
net_width,
|
net_width,
|
||||||
segment.direction,
|
segment.direction,
|
||||||
collision_type=bend_collision_type,
|
collision_type=bend_collision_type,
|
||||||
|
physical_geometry_type=bend_physical_geometry,
|
||||||
clip_margin=bend_clip_margin,
|
clip_margin=bend_clip_margin,
|
||||||
dilation=dilation,
|
dilation=dilation,
|
||||||
)
|
)
|
||||||
|
|
@ -46,6 +47,7 @@ def materialize_path_seed(
|
||||||
segment.radius,
|
segment.radius,
|
||||||
net_width,
|
net_width,
|
||||||
collision_type=bend_collision_type,
|
collision_type=bend_collision_type,
|
||||||
|
physical_geometry_type=bend_physical_geometry,
|
||||||
clip_margin=bend_clip_margin,
|
clip_margin=bend_clip_margin,
|
||||||
dilation=dilation,
|
dilation=dilation,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,7 @@ def run_example_06() -> ScenarioOutcome:
|
||||||
box(40, 60, 60, 80),
|
box(40, 60, 60, 80),
|
||||||
box(40, 10, 60, 30),
|
box(40, 10, 60, 30),
|
||||||
]
|
]
|
||||||
|
custom_physical = Polygon([(0, -11), (11, -11), (11, 0), (9, 0), (9, -9), (0, -9)])
|
||||||
scenarios = [
|
scenarios = [
|
||||||
(
|
(
|
||||||
_build_evaluator(bounds, obstacles=obstacles),
|
_build_evaluator(bounds, obstacles=obstacles),
|
||||||
|
|
@ -268,12 +269,12 @@ def run_example_06() -> ScenarioOutcome:
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
_build_evaluator(bounds, obstacles=obstacles),
|
_build_evaluator(bounds, obstacles=obstacles),
|
||||||
{"clipped_model": (Port(10, 20, 0), Port(90, 40, 90))},
|
{"custom_geometry": (Port(10, 20, 0), Port(90, 40, 90))},
|
||||||
{"clipped_model": 2.0},
|
{"custom_geometry": 2.0},
|
||||||
{
|
{
|
||||||
"bend_radii": [10.0],
|
"bend_radii": [10.0],
|
||||||
"bend_collision_type": "clipped_bbox",
|
"bend_physical_geometry": custom_physical,
|
||||||
"bend_clip_margin": 1.0,
|
"bend_proxy_geometry": custom_physical,
|
||||||
"use_tiered_strategy": False,
|
"use_tiered_strategy": False,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -353,27 +354,29 @@ def run_example_07() -> ScenarioOutcome:
|
||||||
|
|
||||||
def run_example_08() -> ScenarioOutcome:
|
def run_example_08() -> ScenarioOutcome:
|
||||||
bounds = (0, 0, 150, 150)
|
bounds = (0, 0, 150, 150)
|
||||||
netlist = {"custom_bend": (Port(20, 20, 0), Port(100, 100, 90))}
|
netlist = {"standard_arc": (Port(20, 20, 0), Port(100, 100, 90))}
|
||||||
widths = {"custom_bend": 2.0}
|
widths = {"standard_arc": 2.0}
|
||||||
custom_model = Polygon([(-10, -10), (10, -10), (10, 10), (-10, 10)])
|
custom_physical = Polygon([(0, -11), (11, -11), (11, 0), (9, 0), (9, -9), (0, -9)])
|
||||||
evaluator = _build_evaluator(bounds)
|
custom_proxy = box(0, -11, 11, 0)
|
||||||
|
|
||||||
t0 = perf_counter()
|
t0 = perf_counter()
|
||||||
results_std = _build_pathfinder(
|
results_std = _build_pathfinder(
|
||||||
evaluator,
|
_build_evaluator(bounds),
|
||||||
bounds=bounds,
|
bounds=bounds,
|
||||||
nets=_net_specs(netlist, widths),
|
nets=_net_specs(netlist, widths),
|
||||||
bend_radii=[10.0],
|
bend_radii=[10.0],
|
||||||
sbend_radii=[],
|
sbend_radii=[],
|
||||||
max_iterations=1,
|
max_iterations=1,
|
||||||
|
use_tiered_strategy=False,
|
||||||
metrics=AStarMetrics(),
|
metrics=AStarMetrics(),
|
||||||
).route_all()
|
).route_all()
|
||||||
results_custom = _build_pathfinder(
|
results_custom = _build_pathfinder(
|
||||||
evaluator,
|
_build_evaluator(bounds),
|
||||||
bounds=bounds,
|
bounds=bounds,
|
||||||
nets=_net_specs({"custom_model": netlist["custom_bend"]}, {"custom_model": 2.0}),
|
nets=_net_specs({"custom_geometry_and_proxy": netlist["standard_arc"]}, {"custom_geometry_and_proxy": 2.0}),
|
||||||
bend_radii=[10.0],
|
bend_radii=[10.0],
|
||||||
bend_collision_type=custom_model,
|
bend_physical_geometry=custom_physical,
|
||||||
|
bend_proxy_geometry=custom_proxy,
|
||||||
sbend_radii=[],
|
sbend_radii=[],
|
||||||
max_iterations=1,
|
max_iterations=1,
|
||||||
use_tiered_strategy=False,
|
use_tiered_strategy=False,
|
||||||
|
|
|
||||||
|
|
@ -137,13 +137,29 @@ def test_custom_bend_collision_polygon_uses_local_transform() -> None:
|
||||||
assert result.collision_geometry[0].symmetric_difference(expected).area < 1e-6
|
assert result.collision_geometry[0].symmetric_difference(expected).area < 1e-6
|
||||||
|
|
||||||
|
|
||||||
def test_custom_bend_collision_polygon_only_overrides_search_geometry() -> None:
|
def test_custom_bend_collision_polygon_is_true_geometry() -> None:
|
||||||
custom_poly = Polygon([(0, -11), (11, -11), (11, 0), (9, 0), (9, -9), (0, -9)])
|
custom_poly = Polygon([(0, -11), (11, -11), (11, 0), (9, 0), (9, -9), (0, -9)])
|
||||||
result = Bend90.generate(Port(0, 0, 0), 10.0, 2.0, direction="CCW", collision_type=custom_poly, dilation=1.0)
|
result = Bend90.generate(
|
||||||
|
Port(0, 0, 0),
|
||||||
|
10.0,
|
||||||
|
2.0,
|
||||||
|
direction="CCW",
|
||||||
|
collision_type=custom_poly,
|
||||||
|
physical_geometry_type=custom_poly,
|
||||||
|
dilation=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
assert result.collision_geometry[0].symmetric_difference(result.physical_geometry[0]).area > 1e-6
|
assert result.collision_geometry[0].symmetric_difference(result.physical_geometry[0]).area < 1e-6
|
||||||
assert result.dilated_collision_geometry is not None
|
assert result.dilated_collision_geometry is not None
|
||||||
assert result.dilated_physical_geometry is not None
|
assert result.dilated_physical_geometry is not None
|
||||||
|
assert result.dilated_collision_geometry[0].symmetric_difference(result.dilated_physical_geometry[0]).area < 1e-6
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_bend_collision_polygon_can_differ_from_physical_geometry() -> None:
|
||||||
|
custom_proxy = Polygon([(0, -11), (11, -11), (11, 0), (0, 0)])
|
||||||
|
result = Bend90.generate(Port(0, 0, 0), 10.0, 2.0, direction="CCW", collision_type=custom_proxy, dilation=1.0)
|
||||||
|
|
||||||
|
assert result.collision_geometry[0].symmetric_difference(result.physical_geometry[0]).area > 1e-6
|
||||||
assert result.dilated_collision_geometry[0].symmetric_difference(result.dilated_physical_geometry[0]).area > 1e-6
|
assert result.dilated_collision_geometry[0].symmetric_difference(result.dilated_physical_geometry[0]).area > 1e-6
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ BASELINE_SECONDS = {
|
||||||
"example_05_orientation_stress": 0.5630,
|
"example_05_orientation_stress": 0.5630,
|
||||||
"example_06_bend_collision_models": 5.2382,
|
"example_06_bend_collision_models": 5.2382,
|
||||||
"example_07_large_scale_routing": 1.2081,
|
"example_07_large_scale_routing": 1.2081,
|
||||||
"example_08_custom_bend_geometry": 4.2111,
|
"example_08_custom_bend_geometry": 0.9848,
|
||||||
"example_09_unroutable_best_effort": 0.0056,
|
"example_09_unroutable_best_effort": 0.0056,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ EXPECTED_OUTCOMES = {
|
||||||
"example_05_orientation_stress": {"total_results": 3, "valid_results": 3, "reached_targets": 3},
|
"example_05_orientation_stress": {"total_results": 3, "valid_results": 3, "reached_targets": 3},
|
||||||
"example_06_bend_collision_models": {"total_results": 3, "valid_results": 3, "reached_targets": 3},
|
"example_06_bend_collision_models": {"total_results": 3, "valid_results": 3, "reached_targets": 3},
|
||||||
"example_07_large_scale_routing": {"total_results": 10, "valid_results": 10, "reached_targets": 10},
|
"example_07_large_scale_routing": {"total_results": 10, "valid_results": 10, "reached_targets": 10},
|
||||||
"example_08_custom_bend_geometry": {"total_results": 2, "valid_results": 1, "reached_targets": 2},
|
"example_08_custom_bend_geometry": {"total_results": 2, "valid_results": 2, "reached_targets": 2},
|
||||||
"example_09_unroutable_best_effort": {"total_results": 1, "valid_results": 0, "reached_targets": 0},
|
"example_09_unroutable_best_effort": {"total_results": 1, "valid_results": 0, "reached_targets": 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ EXPECTED_OUTCOMES = {
|
||||||
"example_05_orientation_stress": (3, 3, 3),
|
"example_05_orientation_stress": (3, 3, 3),
|
||||||
"example_06_bend_collision_models": (3, 3, 3),
|
"example_06_bend_collision_models": (3, 3, 3),
|
||||||
"example_07_large_scale_routing": (10, 10, 10),
|
"example_07_large_scale_routing": (10, 10, 10),
|
||||||
"example_08_custom_bend_geometry": (2, 1, 2),
|
"example_08_custom_bend_geometry": (2, 2, 2),
|
||||||
"example_09_unroutable_best_effort": (1, 0, 0),
|
"example_09_unroutable_best_effort": (1, 0, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,35 +150,107 @@ def test_example_07_reduced_bottleneck_uses_adaptive_greedy_callback() -> None:
|
||||||
assert all(result.reached_target for result in results.values())
|
assert all(result.reached_target for result in results.values())
|
||||||
|
|
||||||
|
|
||||||
def test_example_08_custom_box_restores_legacy_collision_outcome() -> None:
|
def test_example_06_custom_geometry_can_be_true_physical_geometry() -> None:
|
||||||
|
bounds = (-20, -20, 170, 170)
|
||||||
|
obstacles = (
|
||||||
|
Polygon([(40, 110), (60, 110), (60, 130), (40, 130)]),
|
||||||
|
Polygon([(40, 60), (60, 60), (60, 80), (40, 80)]),
|
||||||
|
Polygon([(40, 10), (60, 10), (60, 30), (40, 30)]),
|
||||||
|
)
|
||||||
|
custom_poly = Polygon([(0, -11), (11, -11), (11, 0), (9, 0), (9, -9), (0, -9)])
|
||||||
|
result = route(
|
||||||
|
RoutingProblem(
|
||||||
|
bounds=bounds,
|
||||||
|
nets=(NetSpec("custom_geometry", Port(10, 20, 0), Port(90, 40, 90), width=2.0),),
|
||||||
|
static_obstacles=obstacles,
|
||||||
|
),
|
||||||
|
options=RoutingOptions(
|
||||||
|
search=SearchOptions(
|
||||||
|
bend_radii=(10.0,),
|
||||||
|
bend_physical_geometry=custom_poly,
|
||||||
|
bend_proxy_geometry=custom_poly,
|
||||||
|
),
|
||||||
|
objective=ObjectiveWeights(bend_penalty=50.0, sbend_penalty=150.0),
|
||||||
|
congestion=CongestionOptions(use_tiered_strategy=False),
|
||||||
|
),
|
||||||
|
).results_by_net["custom_geometry"]
|
||||||
|
|
||||||
|
assert result.is_valid
|
||||||
|
bends = [component for component in result.path if component.move_type == "bend90"]
|
||||||
|
assert bends
|
||||||
|
assert all(
|
||||||
|
component.collision_geometry[0].symmetric_difference(component.physical_geometry[0]).area < 1e-6
|
||||||
|
for component in bends
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_proxy_without_physical_geometry_warns_and_keeps_arc_geometry() -> None:
|
||||||
|
custom_proxy = Polygon([(0, -11), (11, -11), (11, 0), (0, 0)])
|
||||||
|
|
||||||
|
with pytest.warns(UserWarning, match="Custom bend proxy provided without bend_physical_geometry"):
|
||||||
|
search = SearchOptions(
|
||||||
|
bend_radii=(10.0,),
|
||||||
|
sbend_radii=(),
|
||||||
|
bend_proxy_geometry=custom_proxy,
|
||||||
|
)
|
||||||
|
|
||||||
|
problem = RoutingProblem(
|
||||||
|
bounds=(0, 0, 150, 150),
|
||||||
|
nets=(NetSpec("proxy_only", Port(20, 20, 0), Port(100, 100, 90), width=2.0),),
|
||||||
|
)
|
||||||
|
result = route(
|
||||||
|
problem,
|
||||||
|
options=RoutingOptions(
|
||||||
|
search=search,
|
||||||
|
congestion=CongestionOptions(max_iterations=1, use_tiered_strategy=False),
|
||||||
|
),
|
||||||
|
).results_by_net["proxy_only"]
|
||||||
|
|
||||||
|
bends = [component for component in result.path if component.move_type == "bend90"]
|
||||||
|
assert bends
|
||||||
|
assert all(
|
||||||
|
component.collision_geometry[0].symmetric_difference(component.physical_geometry[0]).area > 1e-6
|
||||||
|
for component in bends
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_example_08_custom_geometry_runs_in_separate_sessions() -> None:
|
||||||
bounds = (0, 0, 150, 150)
|
bounds = (0, 0, 150, 150)
|
||||||
netlist = {"custom_bend": (Port(20, 20, 0), Port(100, 100, 90))}
|
netlist = {"standard_arc": (Port(20, 20, 0), Port(100, 100, 90))}
|
||||||
widths = {"custom_bend": 2.0}
|
widths = {"standard_arc": 2.0}
|
||||||
evaluator = _build_evaluator(bounds)
|
custom_physical = Polygon([(0, -11), (11, -11), (11, 0), (9, 0), (9, -9), (0, -9)])
|
||||||
|
custom_proxy = box(0, -11, 11, 0)
|
||||||
|
|
||||||
standard = _build_pathfinder(
|
standard = _build_pathfinder(
|
||||||
evaluator,
|
_build_evaluator(bounds),
|
||||||
bounds=bounds,
|
bounds=bounds,
|
||||||
nets=_net_specs(netlist, widths),
|
nets=_net_specs(netlist, widths),
|
||||||
bend_radii=[10.0],
|
bend_radii=[10.0],
|
||||||
sbend_radii=[],
|
sbend_radii=[],
|
||||||
max_iterations=1,
|
max_iterations=1,
|
||||||
|
use_tiered_strategy=False,
|
||||||
metrics=AStarMetrics(),
|
metrics=AStarMetrics(),
|
||||||
).route_all()
|
).route_all()
|
||||||
custom = _build_pathfinder(
|
custom = _build_pathfinder(
|
||||||
evaluator,
|
_build_evaluator(bounds),
|
||||||
bounds=bounds,
|
bounds=bounds,
|
||||||
nets=_net_specs({"custom_model": netlist["custom_bend"]}, {"custom_model": 2.0}),
|
nets=_net_specs({"custom_geometry_and_proxy": netlist["standard_arc"]}, {"custom_geometry_and_proxy": 2.0}),
|
||||||
bend_radii=[10.0],
|
bend_radii=[10.0],
|
||||||
bend_collision_type=Polygon([(-10, -10), (10, -10), (10, 10), (-10, 10)]),
|
bend_physical_geometry=custom_physical,
|
||||||
|
bend_proxy_geometry=custom_proxy,
|
||||||
sbend_radii=[],
|
sbend_radii=[],
|
||||||
max_iterations=1,
|
max_iterations=1,
|
||||||
use_tiered_strategy=False,
|
use_tiered_strategy=False,
|
||||||
metrics=AStarMetrics(),
|
metrics=AStarMetrics(),
|
||||||
).route_all()
|
).route_all()
|
||||||
|
|
||||||
assert standard["custom_bend"].is_valid
|
assert standard["standard_arc"].is_valid
|
||||||
assert standard["custom_bend"].reached_target
|
assert standard["standard_arc"].reached_target
|
||||||
assert not custom["custom_model"].is_valid
|
assert custom["custom_geometry_and_proxy"].is_valid
|
||||||
assert custom["custom_model"].reached_target
|
assert custom["custom_geometry_and_proxy"].reached_target
|
||||||
assert custom["custom_model"].collisions == 2
|
custom_bends = [component for component in custom["custom_geometry_and_proxy"].path if component.move_type == "bend90"]
|
||||||
|
assert custom_bends
|
||||||
|
assert all(
|
||||||
|
component.collision_geometry[0].symmetric_difference(component.physical_geometry[0]).area > 1e-6
|
||||||
|
for component in custom_bends
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue