use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone)]
pub struct SubstrateRegion {
pub id: String,
pub nodes: Vec<NodeId>,
pub connections: HashMap<NodeId, Vec<NodeId>>,
pub states: HashMap<NodeId, NodeState>,
pub has_reentrant_architecture: bool,
}
pub type NodeId = u64;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NodeState {
pub activation: f64,
pub previous_activation: f64,
}
impl Default for NodeState {
fn default() -> Self {
Self {
activation: 0.0,
previous_activation: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub struct PhiResult {
pub phi: f64,
pub mip: Option<Partition>,
pub whole_ei: f64,
pub parts_ei: f64,
pub reentrant_detected: bool,
pub consciousness_level: ConsciousnessLevel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConsciousnessLevel {
None,
Minimal,
Low,
Moderate,
High,
}
impl ConsciousnessLevel {
pub fn from_phi(phi: f64) -> Self {
if phi <= 0.0 {
ConsciousnessLevel::None
} else if phi < 0.1 {
ConsciousnessLevel::Minimal
} else if phi < 1.0 {
ConsciousnessLevel::Low
} else if phi < 10.0 {
ConsciousnessLevel::Moderate
} else {
ConsciousnessLevel::High
}
}
}
#[derive(Debug, Clone)]
pub struct Partition {
pub parts: Vec<HashSet<NodeId>>,
}
impl Partition {
pub fn bipartition(nodes: &[NodeId], split_point: usize) -> Self {
let mut part1 = HashSet::new();
let mut part2 = HashSet::new();
for (i, &node) in nodes.iter().enumerate() {
if i < split_point {
part1.insert(node);
} else {
part2.insert(node);
}
}
Self {
parts: vec![part1, part2],
}
}
}
pub struct ConsciousnessCalculator {
pub num_perturbations: usize,
pub epsilon: f64,
}
impl Default for ConsciousnessCalculator {
fn default() -> Self {
Self {
num_perturbations: 100,
epsilon: 1e-6,
}
}
}
impl ConsciousnessCalculator {
pub fn new(num_perturbations: usize) -> Self {
Self {
num_perturbations,
epsilon: 1e-6,
}
}
pub fn with_epsilon(num_perturbations: usize, epsilon: f64) -> Self {
Self {
num_perturbations,
epsilon,
}
}
pub fn compute_phi(&self, region: &SubstrateRegion) -> PhiResult {
let reentrant = self.detect_reentrant_architecture(region);
if !reentrant {
return PhiResult {
phi: 0.0,
mip: None,
whole_ei: 0.0,
parts_ei: 0.0,
reentrant_detected: false,
consciousness_level: ConsciousnessLevel::None,
};
}
let whole_ei = self.compute_effective_information(region, ®ion.nodes);
let (mip, min_partition_ei) = self.find_mip(region);
let phi = (whole_ei - min_partition_ei).max(0.0);
PhiResult {
phi,
mip: Some(mip),
whole_ei,
parts_ei: min_partition_ei,
reentrant_detected: true,
consciousness_level: ConsciousnessLevel::from_phi(phi),
}
}
fn detect_reentrant_architecture(&self, region: &SubstrateRegion) -> bool {
if region.has_reentrant_architecture {
return true;
}
let node_set: HashSet<NodeId> = region.nodes.iter().cloned().collect();
let mut color: HashMap<NodeId, u8> = HashMap::with_capacity(region.nodes.len());
for &node in ®ion.nodes {
color.insert(node, 0); }
for &start in ®ion.nodes {
if color.get(&start) != Some(&0) {
continue; }
let mut stack: Vec<(NodeId, usize)> = vec![(start, 0)];
color.insert(start, 1);
while let Some((node, idx)) = stack.last_mut() {
let neighbors = region.connections.get(node);
if let Some(neighbors) = neighbors {
if *idx < neighbors.len() {
let neighbor = neighbors[*idx];
*idx += 1;
if !node_set.contains(&neighbor) {
continue;
}
match color.get(&neighbor) {
Some(1) => return true, Some(0) => {
color.insert(neighbor, 1); stack.push((neighbor, 0));
}
_ => {} }
} else {
color.insert(*node, 2); stack.pop();
}
} else {
color.insert(*node, 2); stack.pop();
}
}
}
false }
fn compute_effective_information(&self, region: &SubstrateRegion, nodes: &[NodeId]) -> f64 {
if nodes.is_empty() {
return 0.0;
}
let current_state: Vec<f64> = nodes
.iter()
.filter_map(|n| region.states.get(n))
.map(|s| s.activation)
.collect();
if current_state.is_empty() {
return 0.0;
}
let current_entropy = self.compute_entropy(¤t_state);
let mut total_mi = 0.0;
for _ in 0..self.num_perturbations {
let perturbed = self.perturb_state(¤t_state);
let evolved = self.evolve_state(region, nodes, &perturbed);
let conditional_entropy = self.compute_conditional_entropy(¤t_state, &evolved);
total_mi += current_entropy - conditional_entropy;
}
total_mi / self.num_perturbations as f64
}
fn find_mip(&self, region: &SubstrateRegion) -> (Partition, f64) {
let nodes = ®ion.nodes;
let n = nodes.len();
if n <= 1 {
return (
Partition {
parts: vec![nodes.iter().cloned().collect()],
},
0.0,
);
}
let mut min_ei = f64::INFINITY;
let mut best_partition = Partition::bipartition(nodes, n / 2);
let mut part1_nodes: Vec<NodeId> = Vec::with_capacity(n);
let mut part2_nodes: Vec<NodeId> = Vec::with_capacity(n);
let mut splits: Vec<usize> = Vec::with_capacity(n - 1);
for i in 1..n {
if i % 2 == 1 {
splits.push(i / 2 + 1);
} else {
splits.push(n - i / 2);
}
}
for split in splits {
if split >= n {
continue;
}
part1_nodes.clear();
part2_nodes.clear();
for (i, &node) in nodes.iter().enumerate() {
if i < split {
part1_nodes.push(node);
} else {
part2_nodes.push(node);
}
}
let ei1 = self.compute_effective_information(region, &part1_nodes);
if ei1 < self.epsilon {
let ei2 = self.compute_effective_information(region, &part2_nodes);
if ei2 < self.epsilon {
return (Partition::bipartition(nodes, split), 0.0);
}
}
let partition_ei = ei1 + self.compute_effective_information(region, &part2_nodes);
if partition_ei < min_ei {
min_ei = partition_ei;
best_partition = Partition::bipartition(nodes, split);
if min_ei < self.epsilon {
break;
}
}
}
(best_partition, min_ei)
}
#[inline]
fn compute_entropy(&self, state: &[f64]) -> f64 {
let n = state.len();
if n == 0 {
return 0.0;
}
let mut mean = 0.0;
let mut m2 = 0.0;
for (i, &x) in state.iter().enumerate() {
let delta = x - mean;
mean += delta / (i + 1) as f64;
let delta2 = x - mean;
m2 += delta * delta2;
}
let variance = if n > 1 { m2 / n as f64 } else { 0.0 };
if variance > self.epsilon {
0.5 * (variance.ln() + 1.4189385332)
} else {
0.0
}
}
fn compute_conditional_entropy(&self, x: &[f64], y: &[f64]) -> f64 {
if x.len() != y.len() || x.is_empty() {
return 0.0;
}
let residuals: Vec<f64> = x.iter().zip(y.iter()).map(|(a, b)| a - b).collect();
self.compute_entropy(&residuals)
}
fn perturb_state(&self, state: &[f64]) -> Vec<f64> {
state
.iter()
.map(|&x| {
let noise = (rand_simple() - 0.5) * 0.1;
(x + noise).clamp(0.0, 1.0)
})
.collect()
}
fn evolve_state(&self, region: &SubstrateRegion, nodes: &[NodeId], state: &[f64]) -> Vec<f64> {
let node_index: HashMap<NodeId, usize> =
nodes.iter().enumerate().map(|(i, &n)| (n, i)).collect();
const ALPHA: f64 = 0.1;
const ONE_MINUS_ALPHA: f64 = 1.0 - ALPHA;
nodes
.iter()
.enumerate()
.map(|(i, &node)| {
let current = state.get(i).cloned().unwrap_or(0.0);
let input: f64 = region
.connections
.get(&node)
.map(|neighbors| {
neighbors
.iter()
.filter_map(|n| node_index.get(n).and_then(|&j| state.get(j)))
.sum()
})
.unwrap_or(0.0);
(current * ONE_MINUS_ALPHA + input * ALPHA).clamp(0.0, 1.0)
})
.collect()
}
pub fn compute_phi_batch(&self, regions: &[SubstrateRegion]) -> Vec<PhiResult> {
regions.iter().map(|r| self.compute_phi(r)).collect()
}
}
thread_local! {
static XORSHIFT_STATE: RefCell<u64> = RefCell::new(0x853c_49e6_748f_ea9b);
}
#[inline]
fn rand_fast() -> f64 {
XORSHIFT_STATE.with(|state| {
let mut s = state.borrow_mut();
*s ^= *s << 13;
*s ^= *s >> 7;
*s ^= *s << 17;
(*s as f64) / (u64::MAX as f64)
})
}
pub fn seed_rng(seed: u64) {
XORSHIFT_STATE.with(|state| {
*state.borrow_mut() = if seed == 0 { 1 } else { seed };
});
}
#[inline]
fn rand_simple() -> f64 {
rand_fast()
}
#[cfg(test)]
mod tests {
use super::*;
fn create_reentrant_region() -> SubstrateRegion {
let nodes = vec![1, 2, 3];
let mut connections = HashMap::new();
connections.insert(1, vec![2]);
connections.insert(2, vec![3]);
connections.insert(3, vec![1]);
let mut states = HashMap::new();
states.insert(
1,
NodeState {
activation: 0.5,
previous_activation: 0.4,
},
);
states.insert(
2,
NodeState {
activation: 0.6,
previous_activation: 0.5,
},
);
states.insert(
3,
NodeState {
activation: 0.4,
previous_activation: 0.3,
},
);
SubstrateRegion {
id: "test_region".to_string(),
nodes,
connections,
states,
has_reentrant_architecture: true,
}
}
fn create_feedforward_region() -> SubstrateRegion {
let nodes = vec![1, 2, 3];
let mut connections = HashMap::new();
connections.insert(1, vec![2]);
connections.insert(2, vec![3]);
let mut states = HashMap::new();
states.insert(
1,
NodeState {
activation: 0.5,
previous_activation: 0.4,
},
);
states.insert(
2,
NodeState {
activation: 0.6,
previous_activation: 0.5,
},
);
states.insert(
3,
NodeState {
activation: 0.4,
previous_activation: 0.3,
},
);
SubstrateRegion {
id: "feedforward".to_string(),
nodes,
connections,
states,
has_reentrant_architecture: false,
}
}
#[test]
fn test_reentrant_has_positive_phi() {
let region = create_reentrant_region();
let calculator = ConsciousnessCalculator::new(10);
let result = calculator.compute_phi(®ion);
assert!(result.reentrant_detected);
assert!(result.phi >= 0.0);
}
#[test]
fn test_feedforward_has_zero_phi() {
let region = create_feedforward_region();
let calculator = ConsciousnessCalculator::new(10);
let result = calculator.compute_phi(®ion);
assert_eq!(result.phi, 0.0);
assert_eq!(result.consciousness_level, ConsciousnessLevel::None);
}
#[test]
fn test_consciousness_levels() {
assert_eq!(ConsciousnessLevel::from_phi(0.0), ConsciousnessLevel::None);
assert_eq!(
ConsciousnessLevel::from_phi(0.05),
ConsciousnessLevel::Minimal
);
assert_eq!(ConsciousnessLevel::from_phi(0.5), ConsciousnessLevel::Low);
assert_eq!(
ConsciousnessLevel::from_phi(5.0),
ConsciousnessLevel::Moderate
);
assert_eq!(ConsciousnessLevel::from_phi(15.0), ConsciousnessLevel::High);
}
}