tiered bbox

This commit is contained in:
jan 2026-03-10 00:21:19 -07:00
commit 9fac436c50
5 changed files with 45 additions and 15 deletions

View file

@ -64,7 +64,7 @@ class ComponentResult:
self.length = length
# Vectorized bounds calculation
self.bounds = shapely.bounds(geometry)
self.dilated_bounds = shapely.bounds(dilated_geometry) if dilated_geometry else None
self.dilated_bounds = shapely.bounds(dilated_geometry) if dilated_geometry is not None else None
def translate(self, dx: float, dy: float) -> ComponentResult:
"""
@ -72,15 +72,16 @@ class ComponentResult:
"""
# Vectorized translation if possible, else list comp
# Shapely 2.x affinity functions still work on single geometries efficiently
geoms = self.geometry
if self.dilated_geometry:
geoms = geoms + self.dilated_geometry
geoms = list(self.geometry)
num_geom = len(self.geometry)
if self.dilated_geometry is not None:
geoms.extend(self.dilated_geometry)
from shapely.affinity import translate
translated = [translate(p, dx, dy) for p in geoms]
new_geom = translated[:len(self.geometry)]
new_dil = translated[len(self.geometry):] if self.dilated_geometry else None
new_geom = translated[:num_geom]
new_dil = translated[num_geom:] if self.dilated_geometry is not None else None
new_port = Port(self.end_port.x + dx, self.end_port.y + dy, self.end_port.orientation)
return ComponentResult(new_geom, new_port, self.length, new_dil)

View file

@ -194,6 +194,7 @@ class AStarRouter:
target: Port,
net_width: float,
net_id: str = 'default',
bend_collision_type: Literal['arc', 'bbox', 'clipped_bbox'] | None = None,
) -> list[ComponentResult] | None:
"""
Route a single net using A*.
@ -203,10 +204,14 @@ class AStarRouter:
target: Target port.
net_width: Waveguide width (um).
net_id: Optional net identifier.
bend_collision_type: Override collision model for this route.
Returns:
List of moves forming the path, or None if failed.
"""
if bend_collision_type is not None:
self.config.bend_collision_type = bend_collision_type
self._collision_cache.clear()
open_set: list[AStarNode] = []
# Key: (x, y, orientation) rounded to 1nm
@ -311,7 +316,9 @@ class AStarRouter:
res = self._move_cache[abs_key]
else:
# Level 2: Relative cache (orientation only)
rel_key = (base_ori, 'S', length, net_width, self._self_dilation)
# Dilation is now 0.0 for caching to save translation time.
# It will be calculated lazily in _add_node if needed.
rel_key = (base_ori, 'S', length, net_width, 0.0)
if rel_key in self._move_cache:
res_rel = self._move_cache[rel_key]
# Check closed set before translating
@ -322,7 +329,7 @@ class AStarRouter:
continue
res = res_rel.translate(cp.x, cp.y)
else:
res_rel = Straight.generate(Port(0, 0, base_ori), length, net_width, dilation=self._self_dilation)
res_rel = Straight.generate(Port(0, 0, base_ori), length, net_width, dilation=0.0)
self._move_cache[rel_key] = res_rel
res = res_rel.translate(cp.x, cp.y)
self._move_cache[abs_key] = res
@ -335,7 +342,7 @@ class AStarRouter:
if abs_key in self._move_cache:
res = self._move_cache[abs_key]
else:
rel_key = (base_ori, 'B', radius, direction, net_width, self.config.bend_collision_type, self._self_dilation)
rel_key = (base_ori, 'B', radius, direction, net_width, self.config.bend_collision_type, 0.0)
if rel_key in self._move_cache:
res_rel = self._move_cache[rel_key]
# Check closed set before translating
@ -353,7 +360,7 @@ class AStarRouter:
direction,
collision_type=self.config.bend_collision_type,
clip_margin=self.config.bend_clip_margin,
dilation=self._self_dilation
dilation=0.0
)
self._move_cache[rel_key] = res_rel
res = res_rel.translate(cp.x, cp.y)
@ -367,7 +374,7 @@ class AStarRouter:
if abs_key in self._move_cache:
res = self._move_cache[abs_key]
else:
rel_key = (base_ori, 'SB', offset, radius, net_width, self.config.bend_collision_type, self._self_dilation)
rel_key = (base_ori, 'SB', offset, radius, net_width, self.config.bend_collision_type, 0.0)
if rel_key in self._move_cache:
res_rel = self._move_cache[rel_key]
# Check closed set before translating
@ -386,7 +393,7 @@ class AStarRouter:
width=net_width,
collision_type=self.config.bend_collision_type,
clip_margin=self.config.bend_clip_margin,
dilation=self._self_dilation
dilation=0.0
)
self._move_cache[rel_key] = res_rel
res = res_rel.translate(cp.x, cp.y)
@ -424,9 +431,20 @@ class AStarRouter:
if self._collision_cache[cache_key]:
return
else:
# Lazy Dilation: compute dilated polygons only if we need a collision check
if result.dilated_geometry is None:
# We need to update the ComponentResult with dilated geometry
# For simplicity, we'll just buffer the polygons here.
# In a more optimized version, ComponentResult might have a .dilate() method.
dilated = [p.buffer(self._self_dilation) for p in result.geometry]
result.dilated_geometry = dilated
# Re-calculate dilated bounds
import shapely
result.dilated_bounds = shapely.bounds(dilated)
hard_coll = False
for i, poly in enumerate(result.geometry):
dil_poly = result.dilated_geometry[i] if result.dilated_geometry else None
dil_poly = result.dilated_geometry[i]
if self.cost_evaluator.collision_engine.check_collision(
poly, net_id, buffer_mode='static', start_port=parent.port, end_port=result.end_port,
dilated_geometry=dil_poly
@ -437,6 +455,13 @@ class AStarRouter:
if hard_coll:
return
# Lazy Dilation for self-intersection and cost evaluation
if result.dilated_geometry is None:
dilated = [p.buffer(self._self_dilation) for p in result.geometry]
result.dilated_geometry = dilated
import shapely
result.dilated_bounds = shapely.bounds(dilated)
# 3. Check for Self-Intersection (Limited to last 100 segments for performance)
if result.dilated_geometry:
# Union of current move's bounds for fast path-wide pruning

View file

@ -111,9 +111,13 @@ class PathFinder:
self.cost_evaluator.collision_engine.remove_path(net_id)
# 2. Reroute with current congestion info
# Tiered Strategy: use clipped_bbox for Iteration 0 for speed.
# Switch to arc for higher iterations if collisions persist.
coll_model = "clipped_bbox" if iteration == 0 else "arc"
net_start = time.monotonic()
path = self.router.route(start, target, width, net_id=net_id)
logger.debug(f' Net {net_id} routed in {time.monotonic() - net_start:.4f}s')
path = self.router.route(start, target, width, net_id=net_id, bend_collision_type=coll_model)
logger.debug(f' Net {net_id} routed in {time.monotonic() - net_start:.4f}s using {coll_model}')
if path:
# 3. Add to index