Files
wifi-densepose/vendor/ruvector/examples/exo-ai-2025/research/docs/07-causal-emergence.md

7.6 KiB

07 - Causal Emergence

Overview

Implementation of causal emergence theory for detecting when macro-level descriptions have more causal power than micro-level ones, using effective information metrics and coarse-graining optimization.

Key Innovation

Causal Emergence Detection: Automatically find the level of description at which a system has maximum causal power, revealing emergent macro-dynamics.

pub struct CausalEmergence {
    /// Transition probability matrix (micro level)
    micro_tpm: TransitionMatrix,
    /// Coarse-graining mappings
    coarse_grainings: Vec<CoarseGraining>,
    /// Effective information at each level
    ei_levels: Vec<f64>,
}

Architecture

┌─────────────────────────────────────────┐
│         Micro-Level System              │
│  ┌─────────────────────────────────┐   │
│  │  States: s₁, s₂, ..., sₙ        │   │
│  │  TPM: P(sⱼ|sᵢ)                  │   │
│  │  EI_micro = I(effect|cause)     │   │
│  └─────────────────────────────────┘   │
├─────────────────────────────────────────┤
│         Coarse-Graining                 │
│  ┌─────────────────────────────────┐   │
│  │  Macro states: S₁, S₂, ..., Sₘ  │   │
│  │  Mapping: μ: micro → macro      │   │
│  │  Macro TPM: P(Sⱼ|Sᵢ)            │   │
│  └─────────────────────────────────┘   │
├─────────────────────────────────────────┤
│         Emergence Detection             │
│  ┌─────────────────────────────────┐   │
│  │  EI_macro > EI_micro ?          │   │
│  │  Causal emergence = YES!        │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Effective Information

impl EffectiveInformation {
    /// Compute EI for a transition matrix
    pub fn compute(&self, tpm: &TransitionMatrix) -> f64 {
        let n = tpm.size();
        let mut ei = 0.0;

        // EI = average mutual information between cause and effect
        // under maximum entropy intervention
        for i in 0..n {
            for j in 0..n {
                let p_joint = tpm.get(i, j) / n as f64; // Max entropy cause
                let p_effect = tpm.column_sum(j) / n as f64;
                let p_cause = 1.0 / n as f64;

                if p_joint > 0.0 && p_effect > 0.0 {
                    ei += p_joint * (p_joint / (p_cause * p_effect)).log2();
                }
            }
        }

        ei
    }

    /// Compute causal emergence
    pub fn causal_emergence(
        &self,
        micro_tpm: &TransitionMatrix,
        macro_tpm: &TransitionMatrix,
    ) -> f64 {
        let ei_micro = self.compute(micro_tpm);
        let ei_macro = self.compute(macro_tpm);

        // Normalize by log of state space size
        let norm_micro = ei_micro / (micro_tpm.size() as f64).log2();
        let norm_macro = ei_macro / (macro_tpm.size() as f64).log2();

        norm_macro - norm_micro
    }
}

Coarse-Graining Optimization

impl CoarseGraining {
    /// Find optimal coarse-graining that maximizes EI
    pub fn optimize(&mut self, micro_tpm: &TransitionMatrix) -> CoarseGraining {
        let n = micro_tpm.size();
        let mut best_cg = self.clone();
        let mut best_ei = 0.0;

        // Try different numbers of macro states
        for m in 2..n {
            // Use spectral clustering to find groupings
            let grouping = self.spectral_partition(micro_tpm, m);

            // Compute macro TPM
            let macro_tpm = self.induce_macro_tpm(micro_tpm, &grouping);

            // Compute EI
            let ei = EffectiveInformation::new().compute(&macro_tpm);

            if ei > best_ei {
                best_ei = ei;
                best_cg = CoarseGraining::from_grouping(grouping);
            }
        }

        best_cg
    }

    /// Spectral clustering for state grouping
    fn spectral_partition(&self, tpm: &TransitionMatrix, k: usize) -> Vec<usize> {
        // Compute Laplacian
        let laplacian = tpm.laplacian();

        // Find k smallest eigenvectors
        let eigenvecs = laplacian.eigenvectors(k);

        // K-means on eigenvector space
        kmeans(&eigenvecs, k)
    }
}

Causal Hierarchy

pub struct CausalHierarchy {
    /// Levels of description
    levels: Vec<HierarchyLevel>,
    /// EI at each level
    ei_profile: Vec<f64>,
    /// Emergence peaks
    peaks: Vec<usize>,
}

impl CausalHierarchy {
    /// Build hierarchy and detect emergence peaks
    pub fn build(&mut self, micro_tpm: &TransitionMatrix) {
        let mut current_tpm = micro_tpm.clone();

        for level in 0..self.max_levels {
            // Compute EI at this level
            let ei = EffectiveInformation::new().compute(&current_tpm);
            self.ei_profile.push(ei);

            // Find optimal coarse-graining for next level
            let cg = CoarseGraining::new().optimize(&current_tpm);
            current_tpm = cg.induce_macro_tpm(&current_tpm);

            self.levels.push(HierarchyLevel {
                tpm: current_tpm.clone(),
                coarse_graining: cg,
                effective_info: ei,
            });
        }

        // Find peaks (levels with local maximum EI)
        self.peaks = self.find_peaks(&self.ei_profile);
    }
}

Performance

Micro States Optimization Time Peak Detection
16 10ms 1ms
64 200ms 5ms
256 5s 50ms
1024 2min 500ms

Usage

use causal_emergence::{CausalEmergence, EffectiveInformation, CausalHierarchy};

// Define micro-level system
let micro_tpm = TransitionMatrix::from_data(&state_transitions);

// Compute effective information
let ei = EffectiveInformation::new();
let ei_micro = ei.compute(&micro_tpm);

// Find optimal coarse-graining
let cg = CoarseGraining::new().optimize(&micro_tpm);
let macro_tpm = cg.induce_macro_tpm(&micro_tpm);
let ei_macro = ei.compute(&macro_tpm);

// Check for causal emergence
let emergence = ei_macro - ei_micro;
if emergence > 0.0 {
    println!("Causal emergence detected! Δ EI = {:.3} bits", emergence);
}

// Build full hierarchy
let mut hierarchy = CausalHierarchy::new(10);
hierarchy.build(&micro_tpm);

for (level, ei) in hierarchy.ei_profile.iter().enumerate() {
    let marker = if hierarchy.peaks.contains(&level) { " ← PEAK" } else { "" };
    println!("Level {}: EI = {:.3}{}", level, ei, marker);
}

Interpretation

Emergence Value Interpretation
< 0 Micro level is more causal
= 0 No emergence
0 to 0.5 Weak emergence
0.5 to 1.0 Moderate emergence
> 1.0 Strong causal emergence

References

  • Hoel, E.P. et al. (2013). "Quantifying causal emergence shows that macro can beat micro"
  • Tononi, G. & Sporns, O. (2003). "Measuring information integration"
  • Klein, B. & Hoel, E.P. (2020). "The Emergence of Informative Higher Scales"