temporal_field/
lib.rs

1//! Temporal Field - Floating-point ternary ring buffer with pub/sub
2//!
3//! The brain does not poll - one spark cascades.
4//!
5//! # Architecture: Field / Writer / Reader
6//!
7//! The system separates into three roles:
8//!
9//! 1. **Field** - The substrate: ring buffer with decay
10//! 2. **Writers** - Anything that writes to regions (cochlea, tokenizer, motor, etc.)
11//! 3. **Readers** - FieldObservers that receive events when thresholds cross
12//!
13//! Multiple writers can write to the same field (additive semantics).
14//! Multiple readers can subscribe to the same field (pub/sub).
15//! Writes automatically fire events to all readers when thresholds cross.
16//!
17//! # Core Concepts
18//!
19//! - **Ring buffer**: Fixed memory, oldest frames auto-evicted
20//! - **Decay per tick**: Time encoded in values, not metadata
21//! - **Regions**: Spatial partitioning for multi-channel integration
22//! - **Pub/sub**: Writes fire events to observers automatically
23//!
24//! # Evolution
25//!
26//! FloatingTernary → Temporal Binding Fields → Temporal Fields
27//!
28//! One system that evolved. Every write triggers downstream processing.
29//!
30//! # Example: Multimodal Binding Detection
31//!
32//! This example shows the pattern used for concept grounding - detecting when
33//! audio and text patterns co-occur (e.g., hearing "door" while seeing the word).
34//!
35//! ```rust
36//! use temporal_field::{TemporalField, FieldConfig, FieldEvent, MonitoredRegion, FnObserver};
37//! use std::sync::Arc;
38//!
39//! // Region definitions (like SensoryField's ModalityRegions)
40//! const AUDIO_REGION: std::ops::Range<usize> = 0..64;
41//! const TEXT_REGION: std::ops::Range<usize> = 64..128;
42//!
43//! // 1. Create the Field (the substrate)
44//! let config = FieldConfig::new(128, 50, 0.95); // 128 dims, 50 frames, 0.95 retention
45//! let mut field = TemporalField::new(config);
46//!
47//! // 2. Configure monitored regions (what triggers events)
48//! field.monitor_region(MonitoredRegion::new("audio", AUDIO_REGION, 0.1));
49//! field.monitor_region(MonitoredRegion::new("text", TEXT_REGION, 0.1));
50//! field.set_convergence_threshold(2); // Fire when 2+ regions active
51//!
52//! // 3. Subscribe Reader (binding detector)
53//! field.subscribe(Arc::new(FnObserver(|event| {
54//!     match event {
55//!         FieldEvent::Convergence { active_regions, total_energy } => {
56//!             // Binding opportunity! Audio + text co-occurred
57//!             println!("BINDING: {} regions, energy={}", active_regions.len(), total_energy);
58//!         }
59//!         FieldEvent::RegionActive { region, energy, .. } => {
60//!             println!("Region {:?} activated with energy {}", region, energy);
61//!         }
62//!         _ => {}
63//!     }
64//! })));
65//!
66//! // 4. Writers write to their regions
67//! // Cochlea writes audio features
68//! let audio_features = vec![0.5; 64];
69//! field.write_region(&audio_features, AUDIO_REGION);
70//!
71//! // Tokenizer writes text embedding
72//! let text_embedding = vec![0.3; 64];
73//! field.write_region(&text_embedding, TEXT_REGION);
74//! // ^ This triggers Convergence event because both regions are now active
75//!
76//! // 5. Time advances - decay happens automatically
77//! field.tick(); // All values decay by retention factor
78//! ```
79//!
80//! # Key Insight
81//!
82//! The field doesn't know what audio or text means. It just knows that patterns
83//! co-occurred within a temporal window. Meaning emerges from the binding.
84
85mod config;
86mod field;
87mod observer;
88mod vector;
89
90pub use config::FieldConfig;
91pub use field::TemporalField;
92pub use observer::{FieldEvent, FieldObserver, FnObserver, MonitoredRegion, TriggerConfig};
93pub use vector::FieldVector;