graphrecords_query/optimizer/
phase.rs1use super::stats::Stats;
2pub use graphrecords_macros::PhaseLabel;
3use std::{
4 any::Any,
5 fmt::{self, Debug, Display, Formatter},
6 hash::{Hash, Hasher},
7 sync::Arc,
8};
9
10pub trait PhaseLabel: Any + Send + Sync {
11 fn dyn_eq(&self, other: &dyn PhaseLabel) -> bool;
12
13 fn dyn_hash(&self, state: &mut dyn Hasher);
14
15 fn dyn_debug(&self, formatter: &mut Formatter<'_>) -> fmt::Result;
16
17 fn as_any(&self) -> &dyn Any;
18}
19
20pub struct PhaseId(Arc<dyn PhaseLabel>);
21
22impl PhaseId {
23 #[must_use]
24 pub fn new(label: impl PhaseLabel) -> Self {
25 Self(Arc::new(label))
26 }
27}
28
29impl Clone for PhaseId {
30 fn clone(&self) -> Self {
31 Self(Arc::clone(&self.0))
32 }
33}
34
35impl PartialEq for PhaseId {
36 fn eq(&self, other: &Self) -> bool {
37 self.0.dyn_eq(other.0.as_ref())
38 }
39}
40
41impl Eq for PhaseId {}
42
43impl Hash for PhaseId {
44 fn hash<H: Hasher>(&self, state: &mut H) {
45 self.0.dyn_hash(state);
46 }
47}
48
49impl Debug for PhaseId {
50 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
51 self.0.dyn_debug(formatter)
52 }
53}
54
55pub const DEFAULT_MAX_ITERATIONS: usize = 100;
56
57#[derive(Clone, Copy, PartialEq, Eq, Debug)]
58pub enum FixpointPolicy {
59 Once,
60 Fixpoint { max_iterations: usize },
61}
62
63impl FixpointPolicy {
64 #[must_use]
65 pub const fn once() -> Self {
66 Self::Once
67 }
68
69 #[must_use]
70 pub const fn fixpoint() -> Self {
71 Self::Fixpoint {
72 max_iterations: DEFAULT_MAX_ITERATIONS,
73 }
74 }
75
76 #[must_use]
77 pub const fn fixpoint_with(max_iterations: usize) -> Self {
78 Self::Fixpoint { max_iterations }
79 }
80}
81
82#[derive(Clone, PartialEq, Eq, Debug)]
83pub enum StopReason {
84 CompletedOnce,
85 Converged { iterations: usize },
86 Oscillation { iterations: usize },
87 IterationLimit { iterations: usize },
88 Skipped,
89 Empty,
90}
91
92#[derive(Clone, Debug)]
93pub struct PhaseOutcome {
94 pub label: PhaseId,
95 pub stop: StopReason,
96}
97
98#[derive(Clone, Debug, Default)]
99pub struct OptimizationReport {
100 pub phases: Vec<PhaseOutcome>,
101}
102
103impl OptimizationReport {
104 #[must_use]
105 pub const fn display(&self) -> ReportDisplay<'_> {
106 ReportDisplay(self)
107 }
108}
109
110pub struct ReportDisplay<'a>(&'a OptimizationReport);
111
112impl Display for ReportDisplay<'_> {
113 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
114 for (index, outcome) in self.0.phases.iter().enumerate() {
115 if index > 0 {
116 writeln!(formatter)?;
117 }
118
119 write!(formatter, "{:?}: ", outcome.label)?;
120
121 match &outcome.stop {
122 StopReason::Converged { iterations } => {
123 write!(formatter, "converged ({iterations} iterations)")?;
124 }
125 StopReason::CompletedOnce => formatter.write_str("completed once")?,
126 StopReason::Oscillation { iterations } => {
127 write!(formatter, "oscillation after {iterations} iterations")?;
128 }
129 StopReason::IterationLimit { iterations } => {
130 write!(formatter, "did not converge within {iterations} iterations")?;
131 }
132 StopReason::Skipped => formatter.write_str("skipped (run condition false)")?,
133 StopReason::Empty => formatter.write_str("skipped (no rules)")?,
134 }
135 }
136
137 Ok(())
138 }
139}
140
141pub(super) type RunCondition = Box<dyn Fn(&Stats<'_>) -> bool + Send + Sync>;