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