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}