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. |
|
||||
| `sbend_radii` | `(10.0,)` | Available radii 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. |
|
||||
| `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 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
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ def main() -> None:
|
|||
greedy_h_weight=1.5,
|
||||
),
|
||||
objective=ObjectiveWeights(
|
||||
bend_penalty=250.0,
|
||||
sbend_penalty=500.0,
|
||||
bend_penalty=50.0,
|
||||
sbend_penalty=150.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
|
||||
|
||||
|
||||
|
|
@ -6,31 +9,31 @@ def main() -> None:
|
|||
print("Running Example 03: Locked Paths...")
|
||||
|
||||
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...")
|
||||
results_a = route(
|
||||
RoutingProblem(
|
||||
stack = build_routing_stack(
|
||||
problem=RoutingProblem(
|
||||
bounds=bounds,
|
||||
nets=(NetSpec("netA", Port(10, 0, 0), Port(90, 0, 0), width=2.0),),
|
||||
),
|
||||
options=options,
|
||||
).results_by_net
|
||||
options=RoutingOptions(search=SearchOptions(bend_radii=(10.0,))),
|
||||
)
|
||||
engine = stack.world
|
||||
evaluator = stack.evaluator
|
||||
results_a = stack.finder.route_all()
|
||||
|
||||
print("Routing detour net around locked path...")
|
||||
results_b = route(
|
||||
RoutingProblem(
|
||||
bounds=bounds,
|
||||
nets=(NetSpec("netB", Port(50, -20, 90), Port(50, 20, 90), width=2.0),),
|
||||
static_obstacles=results_a["netA"].locked_geometry,
|
||||
for polygon in results_a["netA"].locked_geometry:
|
||||
engine.add_static_obstacle(polygon)
|
||||
results_b = PathFinder(
|
||||
AStarContext(
|
||||
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,
|
||||
).results_by_net
|
||||
).route_all()
|
||||
|
||||
results = {**results_a, **results_b}
|
||||
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 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.utils.visualization import plot_routing_results
|
||||
|
||||
|
|
@ -8,10 +9,12 @@ from inire.utils.visualization import plot_routing_results
|
|||
def _route_scenario(
|
||||
bounds: tuple[float, float, float, float],
|
||||
obstacles: list[Polygon],
|
||||
bend_collision_type: str,
|
||||
netlist: dict[str, tuple[Port, Port]],
|
||||
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,
|
||||
) -> dict[str, RoutingResult]:
|
||||
problem = RoutingProblem(
|
||||
|
|
@ -23,6 +26,8 @@ def _route_scenario(
|
|||
search=SearchOptions(
|
||||
bend_radii=(10.0,),
|
||||
bend_collision_type=bend_collision_type,
|
||||
bend_proxy_geometry=bend_proxy_geometry,
|
||||
bend_physical_geometry=bend_physical_geometry,
|
||||
bend_clip_margin=bend_clip_margin,
|
||||
),
|
||||
objective=ObjectiveWeights(
|
||||
|
|
@ -40,29 +45,30 @@ def main() -> None:
|
|||
bounds = (-20, -20, 170, 170)
|
||||
obs_arc = Polygon([(40, 110), (60, 110), (60, 130), (40, 130)])
|
||||
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_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)...")
|
||||
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)...")
|
||||
res_bbox = _route_scenario(bounds, obstacles, "bbox", netlist_bbox, {"bbox_model": 2.0})
|
||||
print("Routing Scenario 3 (Clipped BBox)...")
|
||||
res_clipped = _route_scenario(
|
||||
res_bbox = _route_scenario(bounds, obstacles, netlist_bbox, {"bbox_model": 2.0}, bend_collision_type="bbox")
|
||||
print("Routing Scenario 3 (Custom Manhattan Geometry With Matching Proxy)...")
|
||||
res_custom = _route_scenario(
|
||||
bounds,
|
||||
obstacles,
|
||||
"clipped_bbox",
|
||||
netlist_clipped,
|
||||
{"clipped_model": 2.0},
|
||||
bend_clip_margin=1.0,
|
||||
netlist_custom,
|
||||
{"custom_geometry": 2.0},
|
||||
bend_physical_geometry=custom_bend,
|
||||
bend_proxy_geometry=custom_bend,
|
||||
)
|
||||
|
||||
all_results = {**res_arc, **res_bbox, **res_clipped}
|
||||
all_netlists = {**netlist_arc, **netlist_bbox, **netlist_clipped}
|
||||
all_results = {**res_arc, **res_bbox, **res_custom}
|
||||
all_netlists = {**netlist_arc, **netlist_bbox, **netlist_custom}
|
||||
|
||||
fig, _ax = plot_routing_results(all_results, obstacles, bounds, netlist=all_netlists)
|
||||
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.geometry.collision import RoutingWorld
|
||||
from inire import CongestionOptions, NetSpec, RoutingOptions, RoutingProblem, SearchOptions, route
|
||||
from inire.geometry.components import BendCollisionModel, BendPhysicalGeometry
|
||||
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
|
||||
|
||||
|
||||
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:
|
||||
print("Running Example 08: Custom Bend Geometry...")
|
||||
|
||||
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)
|
||||
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...")
|
||||
results_std = PathFinder(
|
||||
AStarContext(
|
||||
evaluator,
|
||||
RoutingProblem(
|
||||
bounds=bounds,
|
||||
nets=(NetSpec("custom_bend", start, target, width=2.0),),
|
||||
),
|
||||
RoutingOptions(
|
||||
search=SearchOptions(bend_radii=(10.0,), sbend_radii=()),
|
||||
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()
|
||||
print("Routing standard arc in its own session...")
|
||||
results_std = _run_session(bounds, "standard_arc", start, target)
|
||||
print("Routing custom geometry with a separate custom proxy in its own session...")
|
||||
results_custom = _run_session(
|
||||
bounds,
|
||||
"custom_geometry_and_proxy",
|
||||
start,
|
||||
target,
|
||||
bend_physical_geometry=custom_physical,
|
||||
bend_proxy_geometry=custom_proxy,
|
||||
)
|
||||
|
||||
all_results = {**results_std, **results_custom}
|
||||
fig, _ax = plot_routing_results(
|
||||
|
|
@ -67,8 +60,8 @@ def main() -> None:
|
|||
[],
|
||||
bounds,
|
||||
netlist={
|
||||
"custom_bend": (start, target),
|
||||
"custom_model": (start, target),
|
||||
"standard_arc": (start, target),
|
||||
"custom_geometry_and_proxy": (start, target),
|
||||
},
|
||||
)
|
||||
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:
|
||||
* **Arc**: High-fidelity geometry (Highest accuracy).
|
||||
* **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"]
|
||||
BendCollisionModelName = Literal["arc", "bbox", "clipped_bbox"]
|
||||
BendCollisionModel = BendCollisionModelName | Polygon
|
||||
BendPhysicalGeometry = Literal["arc"] | Polygon
|
||||
|
||||
|
||||
def _normalize_length(value: float) -> float:
|
||||
|
|
@ -281,6 +282,7 @@ class Bend90:
|
|||
direction: Literal["CW", "CCW"],
|
||||
sagitta: float = 0.01,
|
||||
collision_type: BendCollisionModel = "arc",
|
||||
physical_geometry_type: BendPhysicalGeometry = "arc",
|
||||
clip_margin: float | None = None,
|
||||
dilation: float = 0.0,
|
||||
) -> ComponentResult:
|
||||
|
|
@ -310,16 +312,33 @@ class Bend90:
|
|||
mirror_y=(sign < 0),
|
||||
)
|
||||
|
||||
physical_geometry = arc_polys
|
||||
if dilation > 0:
|
||||
dilated_physical_geometry = _get_arc_polygons(
|
||||
(float(center_xy[0]), float(center_xy[1])),
|
||||
if isinstance(physical_geometry_type, Polygon):
|
||||
physical_geometry = _apply_collision_model(
|
||||
arc_polys[0],
|
||||
physical_geometry_type,
|
||||
radius,
|
||||
width,
|
||||
(float(center_xy[0]), float(center_xy[1])),
|
||||
ts,
|
||||
sagitta,
|
||||
dilation=dilation,
|
||||
rotation_deg=float(start_port.r),
|
||||
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_physical_geometry if collision_type == "arc" else [poly.buffer(dilation) for poly in collision_polys]
|
||||
)
|
||||
|
|
@ -349,6 +368,7 @@ class SBend:
|
|||
width: float,
|
||||
sagitta: float = 0.01,
|
||||
collision_type: BendCollisionModel = "arc",
|
||||
physical_geometry_type: BendPhysicalGeometry = "arc",
|
||||
clip_margin: float | None = None,
|
||||
dilation: float = 0.0,
|
||||
) -> ComponentResult:
|
||||
|
|
@ -402,12 +422,41 @@ class SBend:
|
|||
)[0],
|
||||
]
|
||||
|
||||
physical_geometry = actual_geometry
|
||||
if dilation > 0:
|
||||
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],
|
||||
if isinstance(physical_geometry_type, Polygon):
|
||||
physical_geometry = [
|
||||
_apply_collision_model(
|
||||
arc1,
|
||||
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_physical_geometry if collision_type == "arc" else [poly.buffer(dilation) for poly in geometry]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import warnings
|
||||
from dataclasses import dataclass, field
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from shapely.geometry import Polygon
|
||||
|
||||
from inire.geometry.components import BendCollisionModel
|
||||
from inire.geometry.primitives import Port
|
||||
|
||||
|
||||
|
|
@ -43,6 +43,8 @@ class SearchOptions:
|
|||
bend_radii: tuple[float, ...] = (50.0, 100.0)
|
||||
sbend_radii: tuple[float, ...] = (10.0,)
|
||||
bend_collision_type: BendCollisionModel = "arc"
|
||||
bend_proxy_geometry: BendCollisionModel | None = None
|
||||
bend_physical_geometry: BendPhysicalGeometry | None = None
|
||||
bend_clip_margin: float | None = None
|
||||
visibility_guidance: VisibilityGuidance = "tangent_corner"
|
||||
|
||||
|
|
@ -51,6 +53,36 @@ class SearchOptions:
|
|||
object.__setattr__(self, "sbend_radii", tuple(self.sbend_radii))
|
||||
if self.sbend_offsets is not None:
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ def process_move(
|
|||
cp = parent.port
|
||||
coll_type = config.bend_collision_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
|
||||
|
||||
abs_key = (
|
||||
|
|
@ -41,6 +43,7 @@ def process_move(
|
|||
params,
|
||||
net_width,
|
||||
coll_key,
|
||||
physical_key,
|
||||
self_dilation,
|
||||
)
|
||||
if abs_key in context.move_cache_abs:
|
||||
|
|
@ -54,6 +57,7 @@ def process_move(
|
|||
params,
|
||||
net_width,
|
||||
coll_key,
|
||||
physical_key,
|
||||
self_dilation,
|
||||
)
|
||||
if rel_key in context.move_cache_rel:
|
||||
|
|
@ -69,6 +73,7 @@ def process_move(
|
|||
net_width,
|
||||
params[1],
|
||||
collision_type=coll_type,
|
||||
physical_geometry_type=config.bend_physical_geometry,
|
||||
clip_margin=config.bend_clip_margin,
|
||||
dilation=self_dilation,
|
||||
)
|
||||
|
|
@ -79,6 +84,7 @@ def process_move(
|
|||
params[1],
|
||||
net_width,
|
||||
collision_type=coll_type,
|
||||
physical_geometry_type=config.bend_physical_geometry,
|
||||
clip_margin=config.bend_clip_margin,
|
||||
dilation=self_dilation,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
|||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from inire.geometry.components import BendCollisionModel
|
||||
from inire.model import RoutingOptions, RoutingProblem
|
||||
from inire.geometry.components import BendCollisionModel, BendPhysicalGeometry
|
||||
from inire.model import RoutingOptions, RoutingProblem, resolve_bend_geometry
|
||||
from inire.results import RouteMetrics
|
||||
from inire.router.visibility import VisibilityManager
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ if TYPE_CHECKING:
|
|||
@dataclass(frozen=True, slots=True)
|
||||
class SearchRunConfig:
|
||||
bend_collision_type: BendCollisionModel
|
||||
bend_physical_geometry: BendPhysicalGeometry
|
||||
bend_clip_margin: float | None
|
||||
node_limit: int
|
||||
return_partial: bool = False
|
||||
|
|
@ -38,8 +39,13 @@ class SearchRunConfig:
|
|||
self_collision_check: bool = False,
|
||||
) -> SearchRunConfig:
|
||||
search = options.search
|
||||
bend_collision_type, bend_physical_geometry = resolve_bend_geometry(
|
||||
search,
|
||||
bend_collision_override=bend_collision_type,
|
||||
)
|
||||
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,
|
||||
node_limit=search.node_limit if node_limit is None else node_limit,
|
||||
return_partial=return_partial,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import time
|
|||
from dataclasses import dataclass
|
||||
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.router._astar_types import AStarContext, AStarMetrics, SearchRunConfig
|
||||
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:
|
||||
path: Sequence[ComponentResult] | None = state.initial_paths[net_id]
|
||||
else:
|
||||
coll_model = search.bend_collision_type
|
||||
coll_model, _ = resolve_bend_geometry(search)
|
||||
skip_congestion = False
|
||||
if congestion.use_tiered_strategy and iteration == 0:
|
||||
skip_congestion = True
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -23,7 +23,7 @@ def materialize_path_seed(
|
|||
path: list[ComponentResult] = []
|
||||
current = start
|
||||
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
|
||||
|
||||
for segment in seed.segments:
|
||||
|
|
@ -36,6 +36,7 @@ def materialize_path_seed(
|
|||
net_width,
|
||||
segment.direction,
|
||||
collision_type=bend_collision_type,
|
||||
physical_geometry_type=bend_physical_geometry,
|
||||
clip_margin=bend_clip_margin,
|
||||
dilation=dilation,
|
||||
)
|
||||
|
|
@ -46,6 +47,7 @@ def materialize_path_seed(
|
|||
segment.radius,
|
||||
net_width,
|
||||
collision_type=bend_collision_type,
|
||||
physical_geometry_type=bend_physical_geometry,
|
||||
clip_margin=bend_clip_margin,
|
||||
dilation=dilation,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@ def run_example_06() -> ScenarioOutcome:
|
|||
box(40, 60, 60, 80),
|
||||
box(40, 10, 60, 30),
|
||||
]
|
||||
custom_physical = Polygon([(0, -11), (11, -11), (11, 0), (9, 0), (9, -9), (0, -9)])
|
||||
scenarios = [
|
||||
(
|
||||
_build_evaluator(bounds, obstacles=obstacles),
|
||||
|
|
@ -268,12 +269,12 @@ def run_example_06() -> ScenarioOutcome:
|
|||
),
|
||||
(
|
||||
_build_evaluator(bounds, obstacles=obstacles),
|
||||
{"clipped_model": (Port(10, 20, 0), Port(90, 40, 90))},
|
||||
{"clipped_model": 2.0},
|
||||
{"custom_geometry": (Port(10, 20, 0), Port(90, 40, 90))},
|
||||
{"custom_geometry": 2.0},
|
||||
{
|
||||
"bend_radii": [10.0],
|
||||
"bend_collision_type": "clipped_bbox",
|
||||
"bend_clip_margin": 1.0,
|
||||
"bend_physical_geometry": custom_physical,
|
||||
"bend_proxy_geometry": custom_physical,
|
||||
"use_tiered_strategy": False,
|
||||
},
|
||||
),
|
||||
|
|
@ -353,27 +354,29 @@ def run_example_07() -> ScenarioOutcome:
|
|||
|
||||
def run_example_08() -> ScenarioOutcome:
|
||||
bounds = (0, 0, 150, 150)
|
||||
netlist = {"custom_bend": (Port(20, 20, 0), Port(100, 100, 90))}
|
||||
widths = {"custom_bend": 2.0}
|
||||
custom_model = Polygon([(-10, -10), (10, -10), (10, 10), (-10, 10)])
|
||||
evaluator = _build_evaluator(bounds)
|
||||
netlist = {"standard_arc": (Port(20, 20, 0), Port(100, 100, 90))}
|
||||
widths = {"standard_arc": 2.0}
|
||||
custom_physical = Polygon([(0, -11), (11, -11), (11, 0), (9, 0), (9, -9), (0, -9)])
|
||||
custom_proxy = box(0, -11, 11, 0)
|
||||
|
||||
t0 = perf_counter()
|
||||
results_std = _build_pathfinder(
|
||||
evaluator,
|
||||
_build_evaluator(bounds),
|
||||
bounds=bounds,
|
||||
nets=_net_specs(netlist, widths),
|
||||
bend_radii=[10.0],
|
||||
sbend_radii=[],
|
||||
max_iterations=1,
|
||||
use_tiered_strategy=False,
|
||||
metrics=AStarMetrics(),
|
||||
).route_all()
|
||||
results_custom = _build_pathfinder(
|
||||
evaluator,
|
||||
_build_evaluator(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_collision_type=custom_model,
|
||||
bend_physical_geometry=custom_physical,
|
||||
bend_proxy_geometry=custom_proxy,
|
||||
sbend_radii=[],
|
||||
max_iterations=1,
|
||||
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
|
||||
|
||||
|
||||
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)])
|
||||
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_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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ BASELINE_SECONDS = {
|
|||
"example_05_orientation_stress": 0.5630,
|
||||
"example_06_bend_collision_models": 5.2382,
|
||||
"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,
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ EXPECTED_OUTCOMES = {
|
|||
"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_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},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ EXPECTED_OUTCOMES = {
|
|||
"example_05_orientation_stress": (3, 3, 3),
|
||||
"example_06_bend_collision_models": (3, 3, 3),
|
||||
"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),
|
||||
}
|
||||
|
||||
|
|
@ -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())
|
||||
|
||||
|
||||
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)
|
||||
netlist = {"custom_bend": (Port(20, 20, 0), Port(100, 100, 90))}
|
||||
widths = {"custom_bend": 2.0}
|
||||
evaluator = _build_evaluator(bounds)
|
||||
netlist = {"standard_arc": (Port(20, 20, 0), Port(100, 100, 90))}
|
||||
widths = {"standard_arc": 2.0}
|
||||
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(
|
||||
evaluator,
|
||||
_build_evaluator(bounds),
|
||||
bounds=bounds,
|
||||
nets=_net_specs(netlist, widths),
|
||||
bend_radii=[10.0],
|
||||
sbend_radii=[],
|
||||
max_iterations=1,
|
||||
use_tiered_strategy=False,
|
||||
metrics=AStarMetrics(),
|
||||
).route_all()
|
||||
custom = _build_pathfinder(
|
||||
evaluator,
|
||||
_build_evaluator(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_collision_type=Polygon([(-10, -10), (10, -10), (10, 10), (-10, 10)]),
|
||||
bend_physical_geometry=custom_physical,
|
||||
bend_proxy_geometry=custom_proxy,
|
||||
sbend_radii=[],
|
||||
max_iterations=1,
|
||||
use_tiered_strategy=False,
|
||||
metrics=AStarMetrics(),
|
||||
).route_all()
|
||||
|
||||
assert standard["custom_bend"].is_valid
|
||||
assert standard["custom_bend"].reached_target
|
||||
assert not custom["custom_model"].is_valid
|
||||
assert custom["custom_model"].reached_target
|
||||
assert custom["custom_model"].collisions == 2
|
||||
assert standard["standard_arc"].is_valid
|
||||
assert standard["standard_arc"].reached_target
|
||||
assert custom["custom_geometry_and_proxy"].is_valid
|
||||
assert custom["custom_geometry_and_proxy"].reached_target
|
||||
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