Skip to main content

ruvector_consciousness/
types.rs

1//! Core types for consciousness computation.
2//!
3//! Provides transition probability matrices, partition representations,
4//! and result types for Φ and emergence metrics.
5
6use serde::{Deserialize, Serialize};
7use std::time::Duration;
8
9// ---------------------------------------------------------------------------
10// Transition Probability Matrix (TPM)
11// ---------------------------------------------------------------------------
12
13/// A row-major transition probability matrix for a discrete system.
14///
15/// Entry `tpm[i][j]` = P(state j at t+1 | state i at t).
16/// Rows must sum to 1.0. Stored as a flat Vec for cache locality.
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct TransitionMatrix {
19    /// Flat row-major storage: `data[i * cols + j]`.
20    pub data: Vec<f64>,
21    /// Number of states (rows = cols for square TPM).
22    pub n: usize,
23}
24
25impl TransitionMatrix {
26    /// Create from a flat row-major vec. Panics if `data.len() != n * n`.
27    #[inline]
28    pub fn new(n: usize, data: Vec<f64>) -> Self {
29        assert_eq!(data.len(), n * n, "TPM data length must be n*n");
30        Self { data, n }
31    }
32
33    /// Create an identity TPM (each state maps to itself).
34    pub fn identity(n: usize) -> Self {
35        let mut data = vec![0.0; n * n];
36        for i in 0..n {
37            data[i * n + i] = 1.0;
38        }
39        Self { data, n }
40    }
41
42    /// Get element at (row, col).
43    #[inline(always)]
44    pub fn get(&self, row: usize, col: usize) -> f64 {
45        self.data[row * self.n + col]
46    }
47
48    /// Get element without bounds checking.
49    ///
50    /// # Safety
51    /// `row < self.n && col < self.n` must hold.
52    #[inline(always)]
53    pub unsafe fn get_unchecked(&self, row: usize, col: usize) -> f64 {
54        *self.data.get_unchecked(row * self.n + col)
55    }
56
57    /// Set element at (row, col).
58    #[inline(always)]
59    pub fn set(&mut self, row: usize, col: usize, val: f64) {
60        self.data[row * self.n + col] = val;
61    }
62
63    /// Number of states.
64    #[inline(always)]
65    pub fn size(&self) -> usize {
66        self.n
67    }
68
69    /// Raw data slice.
70    #[inline(always)]
71    pub fn as_slice(&self) -> &[f64] {
72        &self.data
73    }
74
75    /// Extract a sub-TPM for the given element indices (marginalize).
76    pub fn marginalize(&self, indices: &[usize]) -> TransitionMatrix {
77        let k = indices.len();
78        let mut sub = vec![0.0; k * k];
79        for (si, &i) in indices.iter().enumerate() {
80            let mut row_sum = 0.0;
81            for (sj, &j) in indices.iter().enumerate() {
82                let val = self.get(i, j);
83                sub[si * k + sj] = val;
84                row_sum += val;
85            }
86            // Re-normalize row.
87            if row_sum > 0.0 {
88                for sj in 0..k {
89                    sub[si * k + sj] /= row_sum;
90                }
91            }
92        }
93        TransitionMatrix { data: sub, n: k }
94    }
95}
96
97// ---------------------------------------------------------------------------
98// Partition
99// ---------------------------------------------------------------------------
100
101/// A bipartition of system elements into two non-empty sets.
102#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
103pub struct Bipartition {
104    /// Bitmask: bit i = 1 means element i is in set A, 0 means set B.
105    pub mask: u64,
106    /// Total number of elements.
107    pub n: usize,
108}
109
110impl Bipartition {
111    /// Elements in set A.
112    pub fn set_a(&self) -> Vec<usize> {
113        (0..self.n).filter(|&i| self.mask & (1 << i) != 0).collect()
114    }
115
116    /// Elements in set B.
117    pub fn set_b(&self) -> Vec<usize> {
118        (0..self.n)
119            .filter(|&i| self.mask & (1 << i) == 0)
120            .collect()
121    }
122
123    /// Check if this is a valid bipartition (both sets non-empty).
124    #[inline]
125    pub fn is_valid(&self) -> bool {
126        let full = (1u64 << self.n) - 1;
127        self.mask != 0 && self.mask != full
128    }
129}
130
131/// Iterator over all valid bipartitions of n elements.
132pub struct BipartitionIter {
133    current: u64,
134    max: u64,
135    n: usize,
136}
137
138impl BipartitionIter {
139    pub fn new(n: usize) -> Self {
140        assert!(n <= 63, "bipartition iter supports at most 63 elements");
141        Self {
142            current: 1, // skip mask=0 (empty set A)
143            max: (1u64 << n) - 1,
144            n,
145        }
146    }
147}
148
149impl Iterator for BipartitionIter {
150    type Item = Bipartition;
151
152    fn next(&mut self) -> Option<Self::Item> {
153        // Skip masks where set B is empty (mask == max).
154        while self.current < self.max {
155            let mask = self.current;
156            self.current += 1;
157            return Some(Bipartition { mask, n: self.n });
158        }
159        None
160    }
161
162    fn size_hint(&self) -> (usize, Option<usize>) {
163        let remaining = (self.max - self.current) as usize;
164        (remaining, Some(remaining))
165    }
166}
167
168// ---------------------------------------------------------------------------
169// Result types
170// ---------------------------------------------------------------------------
171
172/// Algorithm used for Φ computation.
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
174pub enum PhiAlgorithm {
175    /// Exact: enumerate all 2^n - 2 bipartitions.
176    Exact,
177    /// Greedy bisection approximation.
178    GreedyBisection,
179    /// Spectral approximation via Fiedler vector.
180    Spectral,
181    /// Stochastic sampling of partition space.
182    Stochastic,
183    /// Hierarchical approximation for large systems.
184    Hierarchical,
185    /// GeoMIP: hypercube BFS with automorphism pruning.
186    GeoMIP,
187    /// Quantum-inspired collapse search.
188    Collapse,
189}
190
191impl std::fmt::Display for PhiAlgorithm {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        match self {
194            Self::Exact => write!(f, "exact"),
195            Self::GreedyBisection => write!(f, "greedy-bisection"),
196            Self::Spectral => write!(f, "spectral"),
197            Self::Stochastic => write!(f, "stochastic"),
198            Self::Hierarchical => write!(f, "hierarchical"),
199            Self::GeoMIP => write!(f, "geomip"),
200            Self::Collapse => write!(f, "collapse"),
201        }
202    }
203}
204
205/// Result of a Φ (integrated information) computation.
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct PhiResult {
208    /// The integrated information value Φ.
209    pub phi: f64,
210    /// The minimum information partition (MIP) that achieves Φ.
211    pub mip: Bipartition,
212    /// Number of partitions evaluated.
213    pub partitions_evaluated: u64,
214    /// Total partitions in search space.
215    pub total_partitions: u64,
216    /// Algorithm used.
217    pub algorithm: PhiAlgorithm,
218    /// Wall-clock time for computation.
219    pub elapsed: Duration,
220    /// Convergence history (Φ estimate per iteration for approximate methods).
221    pub convergence: Vec<f64>,
222}
223
224/// Result of a causal emergence computation.
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct EmergenceResult {
227    /// Effective information at the micro level.
228    pub ei_micro: f64,
229    /// Effective information at the macro level.
230    pub ei_macro: f64,
231    /// Causal emergence = EI_macro - EI_micro.
232    pub causal_emergence: f64,
233    /// The coarse-graining that maximizes emergence.
234    pub coarse_graining: Vec<usize>,
235    /// Determinism component.
236    pub determinism: f64,
237    /// Degeneracy component.
238    pub degeneracy: f64,
239    /// Wall-clock time.
240    pub elapsed: Duration,
241}
242
243// ---------------------------------------------------------------------------
244// IIT 4.0 types
245// ---------------------------------------------------------------------------
246
247/// A mechanism is a subset of system elements that has causal power.
248#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct Mechanism {
250    /// Bitmask of mechanism elements.
251    pub elements: u64,
252    /// Total number of system elements.
253    pub n: usize,
254}
255
256impl Mechanism {
257    pub fn new(elements: u64, n: usize) -> Self {
258        Self { elements, n }
259    }
260
261    /// Number of elements in the mechanism.
262    pub fn size(&self) -> usize {
263        self.elements.count_ones() as usize
264    }
265
266    /// Indices of mechanism elements.
267    pub fn indices(&self) -> Vec<usize> {
268        (0..self.n).filter(|&i| self.elements & (1 << i) != 0).collect()
269    }
270}
271
272/// A purview is the set of elements a mechanism has causal power over.
273pub type Purview = Mechanism;
274
275/// A distinction (concept in IIT 3.0) specifies how a mechanism
276/// constrains its cause and effect purviews.
277#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct Distinction {
279    /// The mechanism (subset of elements).
280    pub mechanism: Mechanism,
281    /// Cause repertoire: distribution over past states.
282    pub cause_repertoire: Vec<f64>,
283    /// Effect repertoire: distribution over future states.
284    pub effect_repertoire: Vec<f64>,
285    /// Cause purview (the elements the mechanism has causal power over in the past).
286    pub cause_purview: Purview,
287    /// Effect purview (elements causally constrained in the future).
288    pub effect_purview: Purview,
289    /// φ_cause: intrinsic information of the cause.
290    pub phi_cause: f64,
291    /// φ_effect: intrinsic information of the effect.
292    pub phi_effect: f64,
293    /// φ = min(φ_cause, φ_effect): the distinction's integrated information.
294    pub phi: f64,
295}
296
297/// A relation specifies how multiple distinctions overlap in cause-effect space.
298#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct Relation {
300    /// Indices into the CES distinctions vec.
301    pub distinction_indices: Vec<usize>,
302    /// Relation φ (irreducibility of the overlap).
303    pub phi: f64,
304    /// Order of the relation (number of distinctions involved).
305    pub order: usize,
306}
307
308/// The Cause-Effect Structure (CES): the full quale / experience.
309///
310/// In IIT 4.0, the CES is the set of all distinctions and relations
311/// specified by a system in a state — the "shape" of experience.
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct CauseEffectStructure {
314    /// System size (number of elements).
315    pub n: usize,
316    /// Current state of the system.
317    pub state: usize,
318    /// All distinctions (mechanisms with non-zero φ).
319    pub distinctions: Vec<Distinction>,
320    /// Relations between distinctions.
321    pub relations: Vec<Relation>,
322    /// System-level Φ (big phi — irreducibility of the whole CES).
323    pub big_phi: f64,
324    /// Sum of all distinction φ values (structure integrated information).
325    pub sum_phi: f64,
326    /// Computation time.
327    pub elapsed: Duration,
328}
329
330/// Result of Integrated Information Decomposition (ΦID).
331#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct PhiIdResult {
333    /// Redundant information: shared across all sources.
334    pub redundancy: f64,
335    /// Unique information per source.
336    pub unique: Vec<f64>,
337    /// Synergistic information: only available from the whole.
338    pub synergy: f64,
339    /// Total mutual information.
340    pub total_mi: f64,
341    /// Transfer entropy (directional information flow).
342    pub transfer_entropy: f64,
343    /// Computation time.
344    pub elapsed: Duration,
345}
346
347/// Result of Partial Information Decomposition (PID).
348#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct PidResult {
350    /// Redundant information (shared by all sources about the target).
351    pub redundancy: f64,
352    /// Unique information per source about the target.
353    pub unique: Vec<f64>,
354    /// Synergistic information (only available from all sources jointly).
355    pub synergy: f64,
356    /// Total mutual information I(sources; target).
357    pub total_mi: f64,
358    /// Number of sources.
359    pub num_sources: usize,
360    /// Computation time.
361    pub elapsed: Duration,
362}
363
364/// Result of streaming (online) Φ computation.
365#[derive(Debug, Clone, Serialize, Deserialize)]
366pub struct StreamingPhiResult {
367    /// Current Φ estimate.
368    pub phi: f64,
369    /// Number of time steps processed.
370    pub time_steps: usize,
371    /// Exponentially weighted moving average of Φ.
372    pub phi_ewma: f64,
373    /// Variance of Φ estimates.
374    pub phi_variance: f64,
375    /// Change-point detected in Φ trajectory.
376    pub change_detected: bool,
377    /// History of Φ estimates (most recent window).
378    pub history: Vec<f64>,
379}
380
381/// Approximation bound for Φ estimation.
382#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct PhiBound {
384    /// Lower bound on Φ.
385    pub lower: f64,
386    /// Upper bound on Φ.
387    pub upper: f64,
388    /// Confidence level (e.g., 0.95 for 95% confidence).
389    pub confidence: f64,
390    /// Number of samples used.
391    pub samples: u64,
392    /// Bound source (which method produced this bound).
393    pub method: String,
394}
395
396/// Compute budget for consciousness computations.
397#[derive(Debug, Clone, Serialize, Deserialize)]
398pub struct ComputeBudget {
399    /// Maximum wall-clock time.
400    pub max_time: Duration,
401    /// Maximum partitions to evaluate (0 = unlimited).
402    pub max_partitions: u64,
403    /// Maximum memory in bytes (0 = unlimited).
404    pub max_memory: usize,
405    /// Target approximation ratio (1.0 = exact, <1.0 = approximate).
406    pub approximation_ratio: f64,
407}
408
409impl Default for ComputeBudget {
410    fn default() -> Self {
411        Self {
412            max_time: Duration::from_secs(30),
413            max_partitions: 0,
414            max_memory: 0,
415            approximation_ratio: 1.0,
416        }
417    }
418}
419
420impl ComputeBudget {
421    /// Budget for exact computation (generous limits).
422    pub fn exact() -> Self {
423        Self::default()
424    }
425
426    /// Budget for fast approximate computation.
427    pub fn fast() -> Self {
428        Self {
429            max_time: Duration::from_millis(100),
430            max_partitions: 1000,
431            max_memory: 64 * 1024 * 1024,
432            approximation_ratio: 0.9,
433        }
434    }
435}