xlog_logic/compiler_config.rs
1//! Compile-time configuration for the W2.1 variable-ordering cost
2//! model.
3//!
4//! `CompilerConfig` is a per-call argument to
5//! [`crate::compile::Compiler::compile_with_config_and_stats_snapshot`].
6//! `CompilerConfig::default()` disables W2.1 — slice 1/2/4 + W2.2
7//! dispatch behavior is bit-identical when the default config is in
8//! effect.
9//!
10//! Activation requires explicitly constructing a `CompilerConfig`
11//! with [`WcojVarOrderingKind::LeaderCardinality`]. There is no
12//! environment override on this path; env-driven activation is out
13//! of W2.1 scope.
14//!
15//! # Threshold contract
16//!
17//! `wcoj_var_ordering_threshold` is `pub` to allow struct-literal
18//! construction, but the promoter MUST go through
19//! [`CompilerConfig::effective_wcoj_var_ordering_threshold`] so
20//! out-of-range struct-literal values fall back to
21//! [`CompilerConfig::DEFAULT_THRESHOLD`] rather than silently
22//! widening the gate.
23
24/// Selector for the W2.1 variable-ordering cost model.
25///
26/// `Disabled` is the load-bearing default: when set, the promoter
27/// never emits `RirNode::MultiWayJoin::var_order`, and slice
28/// 1/2/4/W2.2 dispatch + row-set semantics are bit-identical.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum WcojVarOrderingKind {
31 /// W2.1 disabled: promoter never sets `var_order`. Bit-identical
32 /// to slice 1/2/4 + W2.2.
33 Disabled,
34 /// Use the default `LeaderCardinalityModel` to pick a
35 /// stats-driven leader for triangle / 4-cycle WCOJ inputs.
36 LeaderCardinality,
37 /// W2.6: use `HeatAwareLeaderModel` — combines cardinality,
38 /// access heat, and observed join selectivity into a
39 /// composite score. Hot relations and rels in tight (low
40 /// selectivity) edges get demoted from the leader slot;
41 /// cold extensional rels are preferred as leader. Same
42 /// threshold gate as `LeaderCardinality` via
43 /// `effective_wcoj_var_ordering_threshold()`.
44 HeatAware,
45}
46
47/// Compile-time configuration for the W2.1 variable-ordering cost
48/// model.
49///
50/// See module docs for activation semantics + threshold contract.
51#[derive(Debug, Clone, PartialEq)]
52pub struct CompilerConfig {
53 /// Variable-ordering cost-model selector. Default `Disabled`.
54 pub wcoj_variable_ordering: WcojVarOrderingKind,
55
56 /// Raw threshold field. Public to keep struct-literal
57 /// construction available, but the promoter MUST NOT read this
58 /// field directly. Use
59 /// [`CompilerConfig::effective_wcoj_var_ordering_threshold`] so
60 /// out-of-range values are clamped at use, not silently honored.
61 pub wcoj_var_ordering_threshold: f64,
62}
63
64impl Default for CompilerConfig {
65 fn default() -> Self {
66 Self {
67 wcoj_variable_ordering: WcojVarOrderingKind::Disabled,
68 wcoj_var_ordering_threshold: Self::DEFAULT_THRESHOLD,
69 }
70 }
71}
72
73impl CompilerConfig {
74 /// Default ratio at or below which a leader candidate triggers
75 /// `var_order = Some(...)`. The gate fires on
76 /// `min_card / default_leader_card ≤ threshold`. A smaller
77 /// threshold demands a clearer win.
78 pub const DEFAULT_THRESHOLD: f64 = 0.5;
79
80 /// Resolve the threshold the promoter actually uses.
81 ///
82 /// Out-of-range values fall back to [`Self::DEFAULT_THRESHOLD`]:
83 /// * `NaN`
84 /// * non-finite (`±INFINITY`)
85 /// * `≤ 0.0` (would never fire — clamps to default to keep the
86 /// gate honest)
87 /// * `> 1.0` (would always fire — clamps to default to prevent
88 /// silent gate-disable via struct-literal)
89 pub fn effective_wcoj_var_ordering_threshold(&self) -> f64 {
90 let t = self.wcoj_var_ordering_threshold;
91 if !t.is_finite() || t <= 0.0 || t > 1.0 {
92 Self::DEFAULT_THRESHOLD
93 } else {
94 t
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 //! W2.1 step 4: 4 resolver unit tests pinning the
102 //! out-of-range fallback contract.
103 use super::*;
104
105 #[test]
106 fn default_threshold_is_half() {
107 let c = CompilerConfig::default();
108 assert_eq!(c.wcoj_var_ordering_threshold, 0.5);
109 assert_eq!(c.effective_wcoj_var_ordering_threshold(), 0.5);
110 assert_eq!(c.wcoj_variable_ordering, WcojVarOrderingKind::Disabled);
111 }
112
113 #[test]
114 fn resolver_passes_through_valid_in_range() {
115 let c = CompilerConfig {
116 wcoj_var_ordering_threshold: 0.3,
117 ..CompilerConfig::default()
118 };
119 assert_eq!(c.effective_wcoj_var_ordering_threshold(), 0.3);
120 }
121
122 #[test]
123 fn resolver_clamps_zero_and_negative_to_default() {
124 // `0.0` boundary: the gate would never fire — clamp.
125 let zero = CompilerConfig {
126 wcoj_var_ordering_threshold: 0.0,
127 ..CompilerConfig::default()
128 };
129 assert_eq!(
130 zero.effective_wcoj_var_ordering_threshold(),
131 CompilerConfig::DEFAULT_THRESHOLD
132 );
133 let neg = CompilerConfig {
134 wcoj_var_ordering_threshold: -0.5,
135 ..CompilerConfig::default()
136 };
137 assert_eq!(
138 neg.effective_wcoj_var_ordering_threshold(),
139 CompilerConfig::DEFAULT_THRESHOLD
140 );
141 }
142
143 #[test]
144 fn resolver_clamps_above_one_and_nonfinite_to_default() {
145 let above = CompilerConfig {
146 wcoj_var_ordering_threshold: 1.5,
147 ..CompilerConfig::default()
148 };
149 assert_eq!(
150 above.effective_wcoj_var_ordering_threshold(),
151 CompilerConfig::DEFAULT_THRESHOLD
152 );
153 let nan = CompilerConfig {
154 wcoj_var_ordering_threshold: f64::NAN,
155 ..CompilerConfig::default()
156 };
157 assert_eq!(
158 nan.effective_wcoj_var_ordering_threshold(),
159 CompilerConfig::DEFAULT_THRESHOLD
160 );
161 let inf = CompilerConfig {
162 wcoj_var_ordering_threshold: f64::INFINITY,
163 ..CompilerConfig::default()
164 };
165 assert_eq!(
166 inf.effective_wcoj_var_ordering_threshold(),
167 CompilerConfig::DEFAULT_THRESHOLD
168 );
169 }
170}