diff --git a/examples/07_large_scale_routing.py b/examples/07_large_scale_routing.py index b97cd37..e58f781 100644 --- a/examples/07_large_scale_routing.py +++ b/examples/07_large_scale_routing.py @@ -15,7 +15,7 @@ def main() -> None: # 1. Setup Environment bounds = (0, 0, 1000, 1000) engine = CollisionEngine(clearance=6.0) - + # Bottleneck at x=500, 200um gap obstacles = [ box(450, 0, 550, 400), @@ -27,7 +27,7 @@ def main() -> None: danger_map = DangerMap(bounds=bounds) danger_map.precompute(obstacles) - evaluator = CostEvaluator(engine, danger_map, greedy_h_weight=2.0, unit_length_cost=0.1, bend_penalty=100.0, sbend_penalty=200.0, congestion_penalty=20.0) + evaluator = CostEvaluator(engine, danger_map, greedy_h_weight=2.0, unit_length_cost=0.1, bend_penalty=100.0, sbend_penalty=400.0, congestion_penalty=20.0) router = AStarRouter(evaluator, node_limit=2000000, snap_size=5.0, bend_radii=[50.0], sbend_radii=[50.0], use_analytical_sbends=False) pf = PathFinder(router, evaluator, max_iterations=50, base_congestion_penalty=20.0, congestion_multiplier=1.2) @@ -37,7 +37,7 @@ def main() -> None: num_nets = 10 start_x = 50 start_y_base = 500 - (num_nets * 10.0) / 2.0 - + end_x = 950 end_y_base = 100 end_y_pitch = 800.0 / (num_nets - 1) @@ -51,18 +51,18 @@ def main() -> None: # 3. Route print(f"Routing {len(netlist)} nets through 200um bottleneck...") - + iteration_stats = [] def iteration_callback(idx, current_results): successes = sum(1 for r in current_results.values() if r.is_valid) total_collisions = sum(r.collisions for r in current_results.values()) total_nodes = pf.router.metrics['nodes_expanded'] - + # Identify Hotspots hotspots = {} overlap_matrix = {} # (net_a, net_b) -> count - + for nid, res in current_results.items(): if res.path: for comp in res.path: @@ -77,7 +77,7 @@ def main() -> None: cx, cy = poly.centroid.x, poly.centroid.y grid_key = (int(cx/20)*20, int(cy/20)*20) hotspots[grid_key] = hotspots.get(grid_key, 0) + 1 - + # Record pair pair = tuple(sorted((nid, other_nid))) overlap_matrix[pair] = overlap_matrix.get(pair, 0) + 1 @@ -89,7 +89,7 @@ def main() -> None: if hotspots: top_hotspots = sorted(hotspots.items(), key=lambda x: x[1], reverse=True)[:3] print(f" Top Hotspots: {top_hotspots}") - + # Adaptive Greediness: Decay from 2.0 to 1.1 over 25 iterations new_greedy = max(1.1, 2.0 - ((idx + 1) / 25.0)) evaluator.greedy_h_weight = new_greedy @@ -101,13 +101,13 @@ def main() -> None: 'Congestion': total_collisions, 'Nodes': total_nodes }) - + # Save plots only for certain iterations to save time if idx % 20 == 0 or idx == pf.max_iterations - 1: # Save a plot of this iteration's result fig, ax = plot_routing_results(current_results, obstacles, bounds, netlist=netlist) plot_danger_map(danger_map, ax=ax) - + # Overlay failures: show where they stopped for nid, res in current_results.items(): if not res.is_valid and res.path: @@ -116,7 +116,7 @@ def main() -> None: dist = abs(last_p.x - target_p.x) + abs(last_p.y - target_p.y) ax.scatter(last_p.x, last_p.y, color='red', marker='x', s=100) ax.text(last_p.x, last_p.y, f" {nid} (rem: {dist:.0f}um)", color='red', fontsize=8) - + fig.savefig(f"examples/07_iteration_{idx:02d}.png") import matplotlib.pyplot as plt plt.close(fig) @@ -126,7 +126,7 @@ def main() -> None: fig_d, ax_d = plot_expansion_density(pf.accumulated_expanded_nodes, bounds) fig_d.savefig(f"examples/07_iteration_{idx:02d}_density.png") plt.close(fig_d) - + pf.router.reset_metrics() import cProfile, pstats @@ -136,7 +136,7 @@ def main() -> None: results = pf.route_all(netlist, net_widths, store_expanded=True, iteration_callback=iteration_callback, shuffle_nets=True, seed=42) t1 = time.perf_counter() profiler.disable() - + # ... (rest of the code) stats = pstats.Stats(profiler).sort_stats('tottime') stats.print_stats(20) @@ -148,10 +148,10 @@ def main() -> None: print("-" * 40) for s in iteration_stats: print(f"{s['Iteration']:<5} | {s['Success']:<8} | {s['Congestion']:<8} | {s['Nodes']:<10}") - + success_count = sum(1 for res in results.values() if res.is_valid) print(f"\nFinal: Routed {success_count}/{len(netlist)} nets successfully.") - + for nid, res in results.items(): target_p = netlist[nid][1] if not res.is_valid: @@ -166,10 +166,10 @@ def main() -> None: # 5. Visualize fig, ax = plot_routing_results(results, obstacles, bounds, netlist=netlist) - + # Overlay Danger Map plot_danger_map(danger_map, ax=ax) - + # Overlay Expanded Nodes from last routed net (as an example) if pf.router.last_expanded_nodes: print(f"Plotting {len(pf.router.last_expanded_nodes)} expanded nodes for the last net...") diff --git a/inire/geometry/collision.py b/inire/geometry/collision.py index 8df698a..f2fc28d 100644 --- a/inire/geometry/collision.py +++ b/inire/geometry/collision.py @@ -326,7 +326,7 @@ class CollisionEngine: t_min = max(tx_min, ty_min) t_max = min(tx_max, ty_max) - if t_max < 0 or t_min > t_max or t_min > 1.0: + if t_max <= 1e-9 or t_min > t_max or t_min >= 1.0 - 1e-9: continue # If rectangle, slab is exact diff --git a/inire/router/astar.py b/inire/router/astar.py index e7cccbd..b74b8d6 100644 --- a/inire/router/astar.py +++ b/inire/router/astar.py @@ -256,22 +256,38 @@ class AStarRouter: target_dist = abs(target.x - cp.x) if target_dist <= max_reach and target_dist > self.config.min_straight_length: straight_lengths.add(snap_search_grid(target_dist, snap)) - # Space for turning: target_dist - R and target_dist - 2R - for radius in self.config.bend_radii: - if target_dist > radius + self.config.min_straight_length: - straight_lengths.add(snap_search_grid(target_dist - radius, snap)) - if target_dist > 2 * radius + self.config.min_straight_length: - straight_lengths.add(snap_search_grid(target_dist - 2 * radius, snap)) + + # Space for turning: target_dist - R and target_dist - 2R + for radius in self.config.bend_radii: + l1 = target_dist - radius + if l1 > self.config.min_straight_length: + s_l1 = snap_search_grid(l1, snap) + if s_l1 <= max_reach and s_l1 > 0.1: + straight_lengths.add(s_l1) + + l2 = target_dist - 2 * radius + if l2 > self.config.min_straight_length: + s_l2 = snap_search_grid(l2, snap) + if s_l2 <= max_reach and s_l2 > 0.1: + straight_lengths.add(s_l2) else: # Vertical target_dist = abs(target.y - cp.y) if target_dist <= max_reach and target_dist > self.config.min_straight_length: straight_lengths.add(snap_search_grid(target_dist, snap)) - # Space for turning: target_dist - R and target_dist - 2R - for radius in self.config.bend_radii: - if target_dist > radius + self.config.min_straight_length: - straight_lengths.add(snap_search_grid(target_dist - radius, snap)) - if target_dist > 2 * radius + self.config.min_straight_length: - straight_lengths.add(snap_search_grid(target_dist - 2 * radius, snap)) + + # Space for turning: target_dist - R and target_dist - 2R + for radius in self.config.bend_radii: + l1 = target_dist - radius + if l1 > self.config.min_straight_length: + s_l1 = snap_search_grid(l1, snap) + if s_l1 <= max_reach and s_l1 > 0.1: + straight_lengths.add(s_l1) + + l2 = target_dist - 2 * radius + if l2 > self.config.min_straight_length: + s_l2 = snap_search_grid(l2, snap) + if s_l2 <= max_reach and s_l2 > 0.1: + straight_lengths.add(s_l2) # NO standard samples here! Only milestones.