qualia_core_db/modalities/manifold_logic.rs
1//! Wave-physics substrate **logic** (§20, legal_logic.md) — the continuous→discrete bridge.
2//!
3//! SCOPE (honest): this is the CPU reference *logic* that bridges a continuous physical signal
4//! to a discrete factual quin the epistemic layer can reason over — `∫Ψ > τ → Fact(p)`. The
5//! full GPU-enumerated 10D-tensor manifold *renderer* (`compute_universe.rs`) is a separate,
6//! larger effort (STELLAR tasks #11–13); this module deliberately does NOT implement that.
7//!
8//! Qualitative/quantitative realities (EMF, acoustic, visual evidence) are evaluated as
9//! continuous math here, then thresholded into discrete facts — so e.g. a measured signal
10//! exceeding a legal limit instantiates a factual quin that the deontic/epistemic engines use.
11
12use core::f64::consts::PI;
13
14/// The named physical coordinates of a wave sample Ψ(x,y,z,t,f,a,φ) — the axes of the manifold.
15#[derive(Debug, Clone, Copy)]
16pub struct WaveCoord {
17 pub x: f64,
18 pub y: f64,
19 pub z: f64,
20 pub t: f64,
21 pub f: f64,
22 pub a: f64,
23 pub phi: f64,
24}
25
26/// Evaluate the wave field Ψ at a coordinate: amplitude `a` oscillating at frequency `f` with
27/// phase `φ` at time `t`, attenuated by an inverse-square spatial envelope `1/(1+r²)`.
28/// Deterministic CPU reference.
29pub fn wave_eval(c: &WaveCoord) -> f64 {
30 let r2 = c.x * c.x + c.y * c.y + c.z * c.z;
31 let envelope = 1.0 / (1.0 + r2);
32 c.a * (2.0 * PI * c.f * c.t + c.phi).sin() * envelope
33}
34
35/// Trapezoidal integral of |Ψ| over an ordered sample series — the accumulated signal energy.
36pub fn integrate_abs(samples: &[f64]) -> f64 {
37 if samples.len() < 2 {
38 return samples.first().map(|v| v.abs()).unwrap_or(0.0);
39 }
40 let mut acc = 0.0;
41 for w in samples.windows(2) {
42 acc += (w[0].abs() + w[1].abs()) * 0.5;
43 }
44 acc
45}
46
47/// **Continuous → discrete**: if the integrated signal exceeds `threshold`, instantiate the
48/// discrete fact `fact_id` (`∫Ψ > τ → Fact(p)`); otherwise `None`. The bridge from the manifold
49/// substrate to `epistemic.rs`.
50pub fn continuous_to_fact(samples: &[f64], threshold: f64, fact_id: u64) -> Option<u64> {
51 if integrate_abs(samples) > threshold {
52 Some(fact_id)
53 } else {
54 None
55 }
56}
57
58// ─── Topological data analysis: Vietoris-Rips + persistent H0 ─────────────────────
59//
60// Detect topological features (connected components / clusters) in the continuous signal by
61// building a Vietoris-Rips complex at a scale ε and tracking how components are born and die as ε
62// grows (0-dimensional persistent homology). Bounded + zero-heap (fixed union-find arrays).
63
64/// Bound on points in one topological query.
65pub const MAX_MANIFOLD_POINTS: usize = 64;
66
67#[inline]
68fn uf_find(parent: &mut [usize; MAX_MANIFOLD_POINTS], mut x: usize) -> usize {
69 while parent[x] != x {
70 parent[x] = parent[parent[x]];
71 x = parent[x];
72 }
73 x
74}
75#[inline]
76fn uf_union(parent: &mut [usize; MAX_MANIFOLD_POINTS], a: usize, b: usize) {
77 let (ra, rb) = (uf_find(parent, a), uf_find(parent, b));
78 if ra != rb {
79 parent[ra] = rb;
80 }
81}
82
83/// **Betti-0** (number of connected components) of the **Vietoris-Rips** complex at scale
84/// `epsilon`: connect points `i,j` whenever `dist[i*n+j] <= epsilon` (`dist` is a flattened
85/// `n×n` distance matrix). `0` for invalid input. Bounded + zero-heap.
86pub fn vietoris_rips_b0(dist: &[f64], n: usize, epsilon: f64) -> usize {
87 if n == 0 || n > MAX_MANIFOLD_POINTS || dist.len() < n * n {
88 return 0;
89 }
90 let mut parent = [0usize; MAX_MANIFOLD_POINTS];
91 for (i, p) in parent.iter_mut().enumerate().take(n) {
92 *p = i;
93 }
94 for i in 0..n {
95 for j in (i + 1)..n {
96 if dist[i * n + j] <= epsilon {
97 uf_union(&mut parent, i, j);
98 }
99 }
100 }
101 let mut components = 0usize;
102 for i in 0..n {
103 if uf_find(&mut parent, i) == i {
104 components += 1;
105 }
106 }
107 components
108}
109
110/// **Persistent 0-dim homology**: write the Betti-0 (component count) at each scale in `epsilons`
111/// (assumed increasing) into `out_b0` — the barcode of connected features (born at ε=0, dying as
112/// they merge; b0 is monotonically non-increasing). Returns the count written. Zero-heap.
113pub fn persistent_h0(dist: &[f64], n: usize, epsilons: &[f64], out_b0: &mut [usize]) -> usize {
114 let m = epsilons.len().min(out_b0.len());
115 for k in 0..m {
116 out_b0[k] = vietoris_rips_b0(dist, n, epsilons[k]);
117 }
118 m
119}
120
121/// **Topological dimension-bridging**: lift a lower-dimensional sample `low` (e.g. a 1-D audio
122/// sample, or a 7-axis `WaveCoord`) into a higher `out`-dimensional manifold coordinate, zero-
123/// padding the new axes. Returns `false` if `out` is too small. The 1D→10D bridge.
124pub fn bridge_dimensions(low: &[f64], out: &mut [f64]) -> bool {
125 if out.len() < low.len() {
126 return false;
127 }
128 for (i, o) in out.iter_mut().enumerate() {
129 *o = if i < low.len() { low[i] } else { 0.0 };
130 }
131 true
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use crate::q_hash;
138
139 #[test]
140 fn vietoris_rips_and_persistent_h0() {
141 // 4 points on a line at 0,1,2,10 → pairwise |Δ| distance matrix.
142 let pts = [0.0f64, 1.0, 2.0, 10.0];
143 let n = 4;
144 let mut dist = [0.0f64; 16];
145 for i in 0..n {
146 for j in 0..n {
147 dist[i * n + j] = (pts[i] - pts[j]).abs();
148 }
149 }
150 // ε=0.5: nothing connects → 4 components.
151 assert_eq!(vietoris_rips_b0(&dist, n, 0.5), 4);
152 // ε=1.0: the 0-1-2 cluster connects (dist 1 each); 10 stays apart → 2 components.
153 assert_eq!(vietoris_rips_b0(&dist, n, 1.0), 2);
154 // ε=10: everything connects → 1 component.
155 assert_eq!(vietoris_rips_b0(&dist, n, 10.0), 1);
156 // The persistence barcode across an increasing filtration.
157 let mut b0 = [0usize; 3];
158 let m = persistent_h0(&dist, n, &[0.5, 1.0, 10.0], &mut b0);
159 assert_eq!(m, 3);
160 assert_eq!(
161 b0,
162 [4, 2, 1],
163 "b0 is monotonically non-increasing as ε grows"
164 );
165 }
166
167 #[test]
168 fn dimension_bridging_zero_pads() {
169 let low = [1.0f64, 2.0, 3.0]; // a 3-D sample
170 let mut out = [9.0f64; 10]; // into the 10-D manifold
171 assert!(bridge_dimensions(&low, &mut out));
172 assert_eq!(&out[..3], &[1.0, 2.0, 3.0]);
173 assert!(out[3..].iter().all(|&v| v == 0.0), "new axes zero-padded");
174 // Too-small target refuses.
175 assert!(!bridge_dimensions(&low, &mut [0.0; 2]));
176 }
177
178 #[test]
179 fn wave_eval_at_origin_peak() {
180 // origin (envelope=1), f=0, phi=π/2, t=0 → sin(π/2)=1 → Ψ = a.
181 let c = WaveCoord {
182 x: 0.0,
183 y: 0.0,
184 z: 0.0,
185 t: 0.0,
186 f: 0.0,
187 a: 2.0,
188 phi: PI / 2.0,
189 };
190 assert!((wave_eval(&c) - 2.0).abs() < 1e-9);
191 // Off-origin attenuates: same wave at r²=3 (x=y=z=1) → 2 * 1/(1+3) = 0.5.
192 let c2 = WaveCoord {
193 x: 1.0,
194 y: 1.0,
195 z: 1.0,
196 ..c
197 };
198 assert!((wave_eval(&c2) - 0.5).abs() < 1e-9);
199 }
200
201 #[test]
202 fn continuous_signal_crosses_into_a_fact() {
203 let samples = [1.0, 1.0, 1.0]; // trapezoid: 1 + 1 = 2
204 assert!((integrate_abs(&samples) - 2.0).abs() < 1e-9);
205 let fact = q_hash("fact:emfLimitExceeded");
206 // Over threshold → the fact is instantiated.
207 assert_eq!(continuous_to_fact(&samples, 1.5, fact), Some(fact));
208 // Under threshold → no fact.
209 assert_eq!(continuous_to_fact(&samples, 5.0, fact), None);
210 }
211}