use super::*;
use amari_holographic::optical::{CodebookConfig, LeeEncoderConfig, OpticalFieldAlgebra};
use std::time::Duration;
use tempfile::tempdir;
fn test_encoder_config() -> LeeEncoderConfig {
LeeEncoderConfig {
carrier_frequency: 0.25,
carrier_angle: 0.0,
dimensions: (256, 256),
}
}
fn test_codebook_config() -> CodebookConfig {
CodebookConfig {
dimensions: (256, 256),
base_seed: 12345,
}
}
#[test]
fn test_symbolic_expression_roundtrip() {
let expr = SymbolicExpression::bind(
SymbolicExpression::symbol("AGENT"),
SymbolicExpression::symbol("John"),
);
let json = serde_json::to_string(&expr).unwrap();
let restored: SymbolicExpression = serde_json::from_str(&json).unwrap();
assert_eq!(expr, restored);
}
#[test]
fn test_journal_replay_consistency() {
let mut journal = MemoryJournal::new(test_encoder_config(), test_codebook_config());
journal.ops.push(MemoryOp::RegisterSymbol {
symbol: amari_holographic::optical::SymbolId::new("AGENT"),
seed: Some(12345),
timestamp: 1000,
});
journal.ops.push(MemoryOp::Store {
key: SymbolicExpression::symbol("test_key"),
value: SymbolicExpression::symbol("test_value"),
strength: 1.0,
timestamp: 2000,
});
let state = journal.replay_to_state();
assert_eq!(state.associations.len(), 1);
assert!(state
.symbol_seeds
.contains_key(&amari_holographic::optical::SymbolId::new("AGENT")));
}
#[test]
fn test_checkpoint_restore_same_hardware() {
let dir = tempdir().unwrap();
let journal_path = dir.path().join("test_journal.bin");
let hardware = MockOpticalHardware::new(42);
let config = CheckpointConfig {
journal_path: journal_path.clone(),
interval: Duration::from_secs(3600), ..Default::default()
};
let mut memory = CheckpointedOpticalMemory::new(
hardware,
test_encoder_config(),
test_codebook_config(),
config.clone(),
)
.unwrap();
memory
.store(
SymbolicExpression::role_filler("AGENT", "John"),
SymbolicExpression::role_filler("ACTION", "run"),
)
.unwrap();
memory.checkpoint().unwrap();
drop(memory);
let same_hardware = MockOpticalHardware::new(42);
let mut restored = CheckpointedOpticalMemory::restore(same_hardware, config).unwrap();
let result = restored
.retrieve(&SymbolicExpression::role_filler("AGENT", "John"))
.unwrap();
assert!(result.is_some());
}
#[test]
fn test_checkpoint_restore_different_hardware() {
let dir = tempdir().unwrap();
let journal_path = dir.path().join("test_journal.bin");
let hardware = MockOpticalHardware::new(42);
let config = CheckpointConfig {
journal_path: journal_path.clone(),
interval: Duration::from_secs(3600),
..Default::default()
};
let mut memory = CheckpointedOpticalMemory::new(
hardware,
test_encoder_config(),
test_codebook_config(),
config.clone(),
)
.unwrap();
memory
.store(
SymbolicExpression::role_filler("AGENT", "John"),
SymbolicExpression::role_filler("ACTION", "run"),
)
.unwrap();
memory.checkpoint().unwrap();
drop(memory);
let different_hardware = MockOpticalHardware::new(999); let mut restored = CheckpointedOpticalMemory::restore(different_hardware, config).unwrap();
let result = restored
.retrieve(&SymbolicExpression::role_filler("AGENT", "John"))
.unwrap();
assert!(result.is_some());
}
#[test]
fn test_fingerprint_detects_drift() {
let mut hardware = MockOpticalHardware::new(42);
let fingerprint = TMatrixFingerprint::capture(&mut hardware, 5).unwrap();
let validation = fingerprint.validate(&mut hardware).unwrap();
assert!(matches!(validation, FingerprintValidation::Valid));
hardware.drift_t_matrix(0.5);
let validation = fingerprint.validate(&mut hardware).unwrap();
assert!(!matches!(validation, FingerprintValidation::Valid));
}
#[test]
fn test_fingerprint_detects_different_hardware() {
let mut hardware1 = MockOpticalHardware::new(42);
let fingerprint = TMatrixFingerprint::capture(&mut hardware1, 5).unwrap();
let mut hardware2 = MockOpticalHardware::new(999);
let validation = fingerprint.validate(&mut hardware2).unwrap();
assert!(matches!(
validation,
FingerprintValidation::DifferentHardware { .. }
));
}
#[test]
fn test_store_retrieve_basic() {
let dir = tempdir().unwrap();
let journal_path = dir.path().join("test_journal.bin");
let hardware = MockOpticalHardware::new(42);
let config = CheckpointConfig {
journal_path,
interval: Duration::from_secs(3600),
..Default::default()
};
let mut memory = CheckpointedOpticalMemory::new(
hardware,
test_encoder_config(),
test_codebook_config(),
config,
)
.unwrap();
memory
.store(
SymbolicExpression::symbol("cat"),
SymbolicExpression::symbol("meow"),
)
.unwrap();
memory
.store(
SymbolicExpression::symbol("dog"),
SymbolicExpression::symbol("bark"),
)
.unwrap();
let cat_result = memory.retrieve(&SymbolicExpression::symbol("cat")).unwrap();
assert!(cat_result.is_some());
assert_eq!(
cat_result.unwrap().value,
SymbolicExpression::symbol("meow")
);
let dog_result = memory.retrieve(&SymbolicExpression::symbol("dog")).unwrap();
assert!(dog_result.is_some());
assert_eq!(
dog_result.unwrap().value,
SymbolicExpression::symbol("bark")
);
}
#[test]
fn test_memory_decay() {
let dir = tempdir().unwrap();
let journal_path = dir.path().join("test_journal.bin");
let hardware = MockOpticalHardware::new(42);
let config = CheckpointConfig {
journal_path,
interval: Duration::from_secs(3600),
..Default::default()
};
let mut memory = CheckpointedOpticalMemory::new(
hardware,
test_encoder_config(),
test_codebook_config(),
config,
)
.unwrap();
memory
.store(
SymbolicExpression::symbol("key"),
SymbolicExpression::symbol("value"),
)
.unwrap();
memory.decay(0.5).unwrap();
let stats = memory.stats();
assert_eq!(stats.n_associations, 1);
memory.decay(0.01).unwrap();
let stats = memory.stats();
assert_eq!(stats.n_associations, 0);
}
#[test]
fn test_memory_forget() {
let dir = tempdir().unwrap();
let journal_path = dir.path().join("test_journal.bin");
let hardware = MockOpticalHardware::new(42);
let config = CheckpointConfig {
journal_path,
interval: Duration::from_secs(3600),
..Default::default()
};
let mut memory = CheckpointedOpticalMemory::new(
hardware,
test_encoder_config(),
test_codebook_config(),
config,
)
.unwrap();
memory
.store(
SymbolicExpression::symbol("key1"),
SymbolicExpression::symbol("value1"),
)
.unwrap();
memory
.store(
SymbolicExpression::symbol("key2"),
SymbolicExpression::symbol("value2"),
)
.unwrap();
assert_eq!(memory.stats().n_associations, 2);
memory.forget(&SymbolicExpression::symbol("key1")).unwrap();
assert_eq!(memory.stats().n_associations, 1);
let result = memory
.retrieve(&SymbolicExpression::symbol("key1"))
.unwrap();
assert!(result.is_none());
}
#[test]
fn test_hardware_info() {
let dir = tempdir().unwrap();
let journal_path = dir.path().join("test_journal.bin");
let hardware = MockOpticalHardware::new(42);
let config = CheckpointConfig {
journal_path,
..Default::default()
};
let memory = CheckpointedOpticalMemory::new(
hardware,
test_encoder_config(),
test_codebook_config(),
config,
)
.unwrap();
let info = memory.hardware_info();
assert!(info.is_ready);
assert!(info.is_calibrated);
assert_eq!(info.dimensions, (256, 256));
assert_eq!(info.n_modes, 100);
}
#[test]
fn test_journal_compaction() {
let mut journal = MemoryJournal::new(test_encoder_config(), test_codebook_config());
for i in 0..100 {
journal.append(MemoryOp::store(
SymbolicExpression::symbol(format!("key{}", i)),
SymbolicExpression::symbol(format!("value{}", i)),
1.0,
));
}
assert_eq!(journal.ops.len(), 100);
assert!(journal.base_state.is_none());
journal.compact();
assert!(journal.ops.is_empty());
assert!(journal.base_state.is_some());
assert_eq!(journal.base_state.as_ref().unwrap().associations.len(), 100);
}
#[test]
fn test_journal_save_load() {
let dir = tempdir().unwrap();
let path = dir.path().join("test_journal.bin");
let mut journal = MemoryJournal::new(test_encoder_config(), test_codebook_config());
journal.append(MemoryOp::store(
SymbolicExpression::symbol("key"),
SymbolicExpression::symbol("value"),
1.0,
));
journal.save(&path).unwrap();
let loaded = MemoryJournal::load(&path).unwrap();
assert_eq!(loaded.ops.len(), 1);
}
fn make_memory(dir: &tempfile::TempDir) -> CheckpointedOpticalMemory<MockOpticalHardware> {
let hardware = MockOpticalHardware::new(42);
let config = CheckpointConfig {
journal_path: dir.path().join("journal.bin"),
interval: Duration::from_secs(3600),
..Default::default()
};
CheckpointedOpticalMemory::new(
hardware,
test_encoder_config(),
test_codebook_config(),
config,
)
.expect("memory constructs")
}
#[test]
fn ws5_memory_trace_starts_at_identity() {
let dir = tempdir().unwrap();
let memory = make_memory(&dir);
let algebra = OpticalFieldAlgebra::new(test_encoder_config().dimensions);
let identity = algebra.identity();
assert!((algebra.similarity(memory.memory_trace(), &identity) - 1.0).abs() < 1e-5);
}
#[test]
fn ws5_store_writes_the_trace() {
let dir = tempdir().unwrap();
let mut memory = make_memory(&dir);
let algebra = OpticalFieldAlgebra::new(test_encoder_config().dimensions);
let identity = algebra.identity();
assert!(
(algebra.similarity(memory.memory_trace(), &identity) - 1.0).abs() < 1e-5,
"baseline: trace starts at identity"
);
memory
.store(
SymbolicExpression::role_filler("AGENT", "John"),
SymbolicExpression::role_filler("ACTION", "run"),
)
.unwrap();
assert!(
algebra.similarity(memory.memory_trace(), &identity) < 0.999,
"trace must change after a store (bind+bundle ran)"
);
}
#[test]
fn ws5_trace_accumulates_across_stores() {
let dir = tempdir().unwrap();
let mut memory = make_memory(&dir);
memory
.store(
SymbolicExpression::role_filler("AGENT", "John"),
SymbolicExpression::role_filler("ACTION", "run"),
)
.unwrap();
let trace_after_one = memory.memory_trace().clone();
memory
.store(
SymbolicExpression::role_filler("AGENT", "Mary"),
SymbolicExpression::role_filler("ACTION", "walk"),
)
.unwrap();
let trace_after_two = memory.memory_trace().clone();
let algebra = OpticalFieldAlgebra::new(test_encoder_config().dimensions);
assert!(
algebra.similarity(&trace_after_one, &trace_after_two) < 0.999,
"trace must accumulate: two stores differ from one"
);
}
#[test]
fn ws5_bound_key_value_is_present_in_trace() {
let dir = tempdir().unwrap();
let mut memory = make_memory(&dir);
let key_expr = SymbolicExpression::role_filler("AGENT", "John");
let value_expr = SymbolicExpression::role_filler("ACTION", "run");
memory.store(key_expr.clone(), value_expr.clone()).unwrap();
let key_field = memory.instantiate(&key_expr).unwrap();
let value_field = memory.instantiate(&value_expr).unwrap();
let algebra = OpticalFieldAlgebra::new(test_encoder_config().dimensions);
let bound = algebra.bind(&key_field, &value_field);
let sim_to_bound = algebra.similarity(memory.memory_trace(), &bound);
let sim_to_random = algebra.similarity(memory.memory_trace(), &algebra.random(99));
assert!(sim_to_bound > sim_to_random,
"trace must contain key⊛value (sim_to_bound={sim_to_bound}) more than random (sim_to_random={sim_to_random})");
}
#[test]
fn ws5_measure_via_hardware_roundtrips() {
let dir = tempdir().unwrap();
let mut memory = make_memory(&dir);
let m0 = memory.measure_via_hardware().unwrap();
assert!(!m0.mode_amplitudes.is_empty());
assert!(m0.total_intensity >= 0.0);
memory
.store(
SymbolicExpression::role_filler("AGENT", "John"),
SymbolicExpression::role_filler("ACTION", "run"),
)
.unwrap();
let m1 = memory.measure_via_hardware().unwrap();
assert_eq!(
m1.mode_amplitudes.len(),
m0.mode_amplitudes.len(),
"mode count is fixed by the hardware"
);
assert!(m1.total_intensity >= 0.0);
}