284 lines
8.4 KiB
Python
284 lines
8.4 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import TYPE_CHECKING, Literal
|
|
|
|
from inire.seeds import PathSeed
|
|
|
|
if TYPE_CHECKING:
|
|
from shapely.geometry import Polygon
|
|
|
|
from inire.geometry.components import ComponentResult
|
|
|
|
|
|
RoutingOutcome = Literal["completed", "colliding", "partial", "unroutable"]
|
|
ConflictTraceStage = Literal["iteration", "restored_best", "final"]
|
|
FrontierTraceReason = Literal["closed_set", "hard_collision", "self_collision", "cost"]
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class RoutingReport:
|
|
static_collision_count: int = 0
|
|
dynamic_collision_count: int = 0
|
|
self_collision_count: int = 0
|
|
total_length: float = 0.0
|
|
|
|
@property
|
|
def collision_count(self) -> int:
|
|
return self.static_collision_count + self.dynamic_collision_count + self.self_collision_count
|
|
|
|
@property
|
|
def is_valid(self) -> bool:
|
|
return self.collision_count == 0
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class ComponentConflictTrace:
|
|
other_net_id: str
|
|
self_component_index: int
|
|
other_component_index: int
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class NetConflictTrace:
|
|
net_id: str
|
|
outcome: RoutingOutcome
|
|
reached_target: bool
|
|
report: RoutingReport
|
|
conflicting_net_ids: tuple[str, ...] = ()
|
|
component_conflicts: tuple[ComponentConflictTrace, ...] = ()
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class ConflictTraceEntry:
|
|
stage: ConflictTraceStage
|
|
iteration: int | None
|
|
completed_net_ids: tuple[str, ...]
|
|
conflict_edges: tuple[tuple[str, str], ...]
|
|
nets: tuple[NetConflictTrace, ...]
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class FrontierPruneSample:
|
|
reason: FrontierTraceReason
|
|
move_type: str
|
|
hotspot_index: int
|
|
parent_state: tuple[int, int, int]
|
|
end_state: tuple[int, int, int]
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class NetFrontierTrace:
|
|
net_id: str
|
|
hotspot_bounds: tuple[tuple[float, float, float, float], ...]
|
|
pruned_closed_set: int
|
|
pruned_hard_collision: int
|
|
pruned_self_collision: int
|
|
pruned_cost: int
|
|
samples: tuple[FrontierPruneSample, ...] = ()
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class PrePairNetTrace:
|
|
net_id: str
|
|
nodes_expanded: int
|
|
congestion_check_calls: int
|
|
pruned_closed_set: int
|
|
pruned_cost: int
|
|
pruned_hard_collision: int
|
|
guidance_seed_present: bool
|
|
frontier: NetFrontierTrace
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class PrePairFrontierTraceEntry:
|
|
iteration: int
|
|
routed_net_ids: tuple[str, ...]
|
|
conflict_edges: tuple[tuple[str, str], ...]
|
|
nets: tuple[PrePairNetTrace, ...]
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class IterationNetAttemptTrace:
|
|
net_id: str
|
|
reached_target: bool
|
|
nodes_expanded: int
|
|
congestion_check_calls: int
|
|
pruned_closed_set: int
|
|
pruned_cost: int
|
|
pruned_hard_collision: int
|
|
guidance_seed_present: bool
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class IterationTraceEntry:
|
|
iteration: int
|
|
congestion_penalty: float
|
|
routed_net_ids: tuple[str, ...]
|
|
completed_nets: int
|
|
conflict_edges: int
|
|
total_dynamic_collisions: int
|
|
nodes_expanded: int
|
|
congestion_check_calls: int
|
|
congestion_candidate_ids: int
|
|
congestion_exact_pair_checks: int
|
|
net_attempts: tuple[IterationNetAttemptTrace, ...] = ()
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class RouteMetrics:
|
|
nodes_expanded: int
|
|
moves_generated: int
|
|
moves_added: int
|
|
pruned_closed_set: int
|
|
pruned_hard_collision: int
|
|
pruned_cost: int
|
|
route_iterations: int
|
|
nets_routed: int
|
|
nets_reached_target: int
|
|
warm_start_paths_built: int
|
|
warm_start_paths_used: int
|
|
refine_path_calls: int
|
|
timeout_events: int
|
|
iteration_reverify_calls: int
|
|
iteration_reverified_nets: int
|
|
iteration_conflicting_nets: int
|
|
iteration_conflict_edges: int
|
|
nets_carried_forward: int
|
|
score_component_calls: int
|
|
score_component_total_ns: int
|
|
path_cost_calls: int
|
|
danger_map_lookup_calls: int
|
|
danger_map_cache_hits: int
|
|
danger_map_cache_misses: int
|
|
danger_map_query_calls: int
|
|
danger_map_total_ns: int
|
|
move_cache_abs_hits: int
|
|
move_cache_abs_misses: int
|
|
move_cache_rel_hits: int
|
|
move_cache_rel_misses: int
|
|
guidance_match_moves: int
|
|
guidance_match_moves_straight: int
|
|
guidance_match_moves_bend90: int
|
|
guidance_match_moves_sbend: int
|
|
guidance_bonus_applied: float
|
|
guidance_bonus_applied_straight: float
|
|
guidance_bonus_applied_bend90: float
|
|
guidance_bonus_applied_sbend: float
|
|
static_safe_cache_hits: int
|
|
hard_collision_cache_hits: int
|
|
congestion_cache_hits: int
|
|
congestion_cache_misses: int
|
|
congestion_presence_cache_hits: int
|
|
congestion_presence_cache_misses: int
|
|
congestion_presence_skips: int
|
|
congestion_candidate_precheck_hits: int
|
|
congestion_candidate_precheck_misses: int
|
|
congestion_candidate_precheck_skips: int
|
|
congestion_grid_net_cache_hits: int
|
|
congestion_grid_net_cache_misses: int
|
|
congestion_grid_span_cache_hits: int
|
|
congestion_grid_span_cache_misses: int
|
|
congestion_candidate_nets: int
|
|
congestion_net_envelope_cache_hits: int
|
|
congestion_net_envelope_cache_misses: int
|
|
dynamic_path_objects_added: int
|
|
dynamic_path_objects_removed: int
|
|
dynamic_tree_rebuilds: int
|
|
dynamic_grid_rebuilds: int
|
|
static_tree_rebuilds: int
|
|
static_raw_tree_rebuilds: int
|
|
static_net_tree_rebuilds: int
|
|
visibility_corner_index_builds: int
|
|
visibility_builds: int
|
|
visibility_corner_pairs_checked: int
|
|
visibility_corner_queries_exact: int
|
|
visibility_corner_hits_exact: int
|
|
visibility_point_queries: int
|
|
visibility_point_cache_hits: int
|
|
visibility_point_cache_misses: int
|
|
visibility_tangent_candidate_scans: int
|
|
visibility_tangent_candidate_corner_checks: int
|
|
visibility_tangent_candidate_ray_tests: int
|
|
ray_cast_calls: int
|
|
ray_cast_calls_straight_static: int
|
|
ray_cast_calls_expand_snap: int
|
|
ray_cast_calls_expand_forward: int
|
|
ray_cast_calls_visibility_build: int
|
|
ray_cast_calls_visibility_query: int
|
|
ray_cast_calls_visibility_tangent: int
|
|
ray_cast_calls_other: int
|
|
ray_cast_candidate_bounds: int
|
|
ray_cast_exact_geometry_checks: int
|
|
congestion_check_calls: int
|
|
congestion_lazy_resolutions: int
|
|
congestion_lazy_requeues: int
|
|
congestion_candidate_ids: int
|
|
congestion_exact_pair_checks: int
|
|
verify_path_report_calls: int
|
|
verify_static_buffer_ops: int
|
|
verify_dynamic_candidate_nets: int
|
|
verify_dynamic_exact_pair_checks: int
|
|
refinement_windows_considered: int
|
|
refinement_static_bounds_checked: int
|
|
refinement_dynamic_bounds_checked: int
|
|
refinement_candidate_side_extents: int
|
|
refinement_candidates_built: int
|
|
refinement_candidates_verified: int
|
|
refinement_candidates_accepted: int
|
|
pair_local_search_pairs_considered: int
|
|
pair_local_search_attempts: int
|
|
pair_local_search_accepts: int
|
|
pair_local_search_nodes_expanded: int
|
|
late_phase_capped_nets: int
|
|
late_phase_capped_fallbacks: int
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class RoutingResult:
|
|
net_id: str
|
|
path: tuple[ComponentResult, ...]
|
|
reached_target: bool = False
|
|
report: RoutingReport = field(default_factory=RoutingReport)
|
|
|
|
def __post_init__(self) -> None:
|
|
object.__setattr__(self, "path", tuple(self.path))
|
|
|
|
@property
|
|
def collisions(self) -> int:
|
|
return self.report.collision_count
|
|
|
|
@property
|
|
def outcome(self) -> RoutingOutcome:
|
|
if not self.path:
|
|
return "unroutable"
|
|
if not self.reached_target:
|
|
return "partial"
|
|
if self.report.collision_count > 0:
|
|
return "colliding"
|
|
return "completed"
|
|
|
|
@property
|
|
def is_valid(self) -> bool:
|
|
return self.outcome == "completed"
|
|
|
|
@property
|
|
def locked_geometry(self) -> tuple[Polygon, ...]:
|
|
polygons: list[Polygon] = []
|
|
for component in self.path:
|
|
polygons.extend(component.physical_geometry)
|
|
return tuple(polygons)
|
|
|
|
def as_seed(self) -> PathSeed:
|
|
return PathSeed(tuple(component.move_spec for component in self.path))
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class RoutingRunResult:
|
|
results_by_net: dict[str, RoutingResult]
|
|
metrics: RouteMetrics
|
|
expanded_nodes: tuple[tuple[int, int, int], ...] = ()
|
|
conflict_trace: tuple[ConflictTraceEntry, ...] = ()
|
|
frontier_trace: tuple[NetFrontierTrace, ...] = ()
|
|
pre_pair_frontier_trace: PrePairFrontierTraceEntry | None = None
|
|
iteration_trace: tuple[IterationTraceEntry, ...] = ()
|