inire/inire/results.py

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, ...] = ()