Skip to main content

maple_runtime/fabrics/
coupling.rs

1//! Coupling Fabric - manages coupling relationships and topology
2//!
3//! Coupling describes the strength and character of interaction between Resonators.
4
5use crate::allocator::AttentionAllocator;
6use crate::runtime_core::DecouplingResult;
7use crate::types::*;
8use dashmap::DashMap;
9use petgraph::graph::{DiGraph, NodeIndex};
10use std::sync::Arc;
11use tokio::sync::RwLock;
12
13/// Coupling Fabric manages all coupling relationships
14pub struct CouplingFabric {
15    /// Active couplings
16    couplings: DashMap<CouplingId, Coupling>,
17
18    /// Coupling graph (directed, weighted)
19    graph: Arc<RwLock<CouplingGraph>>,
20
21    /// Attention allocator reference (coupling is bounded by attention)
22    attention: Arc<AttentionAllocator>,
23
24    /// Configuration
25    config: CouplingConfig,
26}
27
28struct CouplingGraph {
29    graph: DiGraph<ResonatorId, CouplingId>,
30    node_map: std::collections::HashMap<ResonatorId, NodeIndex>,
31}
32
33impl CouplingGraph {
34    fn new() -> Self {
35        Self {
36            graph: DiGraph::new(),
37            node_map: std::collections::HashMap::new(),
38        }
39    }
40
41    fn add_node(&mut self, resonator: ResonatorId) -> NodeIndex {
42        if let Some(&node) = self.node_map.get(&resonator) {
43            return node;
44        }
45
46        let node = self.graph.add_node(resonator);
47        self.node_map.insert(resonator, node);
48        node
49    }
50
51    fn add_edge(&mut self, coupling: &Coupling) {
52        let source_node = self.add_node(coupling.source);
53        let target_node = self.add_node(coupling.target);
54        self.graph.add_edge(source_node, target_node, coupling.id);
55    }
56
57    fn remove_edge(&mut self, coupling_id: &CouplingId) {
58        if let Some(edge_index) = self
59            .graph
60            .edge_indices()
61            .find(|&e| self.graph[e] == *coupling_id)
62        {
63            self.graph.remove_edge(edge_index);
64        }
65    }
66}
67
68impl CouplingFabric {
69    pub fn new(config: &CouplingConfig, attention: Arc<AttentionAllocator>) -> Self {
70        Self {
71            couplings: DashMap::new(),
72            graph: Arc::new(RwLock::new(CouplingGraph::new())),
73            attention,
74            config: config.clone(),
75        }
76    }
77
78    /// Register a Resonator in the coupling topology
79    pub async fn register(&self, resonator: &ResonatorId) -> Result<(), String> {
80        let mut graph = self.graph.write().await;
81        graph.add_node(*resonator);
82        Ok(())
83    }
84
85    /// Establish coupling between Resonators
86    ///
87    /// ARCHITECTURAL RULE: Couplings MUST strengthen gradually.
88    /// Abrupt coupling escalation is a design failure.
89    ///
90    /// Returns the coupling ID and attention token for handle creation by the runtime.
91    pub async fn establish_coupling(
92        &self,
93        params: CouplingParams,
94    ) -> Result<(CouplingId, AllocationToken), CouplingError> {
95        // Validate parameters
96        params
97            .validate()
98            .map_err(|e| CouplingError::ValidationFailed(e.to_string()))?;
99
100        // Check attention availability (INVARIANT: Coupling bounded by attention)
101        let attention_available = self
102            .attention
103            .available_for_coupling(&params.source)
104            .await?;
105
106        if attention_available < params.initial_attention_cost {
107            return Err(CouplingError::InsufficientAttention {
108                requested: params.initial_attention_cost,
109                available: attention_available,
110            });
111        }
112
113        // Validate coupling strength is not too aggressive initially
114        // (prevent abrupt escalation)
115        if params.initial_strength > self.config.max_initial_strength {
116            return Err(CouplingError::TooAggressiveInitialStrength);
117        }
118
119        // Allocate attention
120        let attention_token = self
121            .attention
122            .allocate(&params.source, params.initial_attention_cost)
123            .await?;
124
125        // Create coupling
126        let coupling = Coupling {
127            id: CouplingId::generate(),
128            source: params.source,
129            target: params.target,
130            strength: params.initial_strength,
131            persistence: params.persistence,
132            scope: params.scope,
133            symmetry: params.symmetry,
134            attention_allocated: params.initial_attention_cost,
135            meaning_convergence: 0.0,
136            interaction_count: 0,
137            created_at: TemporalAnchor::now(),
138            last_resonance: TemporalAnchor::now(),
139        };
140
141        let coupling_id = coupling.id;
142
143        // Add to topology
144        self.graph.write().await.add_edge(&coupling);
145        self.couplings.insert(coupling.id, coupling.clone());
146
147        tracing::debug!(
148            "Established coupling {} -> {} (strength: {})",
149            params.source,
150            params.target,
151            params.initial_strength
152        );
153
154        Ok((coupling_id, attention_token))
155    }
156
157    /// Strengthen coupling gradually
158    pub async fn strengthen(
159        &self,
160        coupling_id: CouplingId,
161        delta: f64,
162    ) -> Result<(), CouplingError> {
163        // RULE: Maximum strengthening rate
164        if delta > self.config.max_strengthening_rate {
165            return Err(CouplingError::StrengtheningTooRapid);
166        }
167
168        if let Some(mut coupling) = self.couplings.get_mut(&coupling_id) {
169            let new_strength = (coupling.strength + delta).min(1.0);
170            coupling.strength = new_strength;
171
172            // May need more attention for stronger coupling
173            let additional_attention = (delta * 10.0) as u64;
174            self.attention
175                .allocate_more(&coupling.source, additional_attention)
176                .await?;
177            coupling.attention_allocated += additional_attention;
178
179            tracing::debug!("Strengthened coupling {} to {}", coupling_id, new_strength);
180        }
181
182        Ok(())
183    }
184
185    /// Weaken coupling without severing
186    pub async fn weaken(&self, coupling_id: CouplingId, factor: f64) -> Result<(), CouplingError> {
187        if let Some(mut coupling) = self.couplings.get_mut(&coupling_id) {
188            coupling.strength *= 1.0 - factor;
189
190            // Release some attention
191            let attention_release = (coupling.attention_allocated as f64 * factor) as u64;
192            self.attention
193                .release_partial(&coupling.source, attention_release)
194                .await;
195            coupling.attention_allocated -= attention_release;
196
197            tracing::debug!("Weakened coupling {} by factor {}", coupling_id, factor);
198        }
199
200        Ok(())
201    }
202
203    /// Safe decoupling that preserves commitments
204    ///
205    /// ARCHITECTURAL RULE: Coupling MUST be severed safely without
206    /// violating existing commitments.
207    pub async fn decouple_safely(
208        &self,
209        coupling_id: CouplingId,
210    ) -> Result<DecouplingResult, CouplingError> {
211        let coupling = self
212            .couplings
213            .get(&coupling_id)
214            .ok_or(CouplingError::NotFound)?
215            .clone();
216
217        // Check for active commitments through this coupling
218        // (would be checked via CommitmentManager in real implementation)
219
220        // Remove from graph
221        self.graph.write().await.remove_edge(&coupling_id);
222
223        // Release attention
224        self.attention
225            .release_all(&coupling.source, coupling.attention_allocated)
226            .await;
227
228        // Remove coupling
229        self.couplings.remove(&coupling_id);
230
231        tracing::debug!("Decoupled {}", coupling_id);
232
233        Ok(DecouplingResult::Success)
234    }
235
236    /// Get coupling by ID
237    pub fn get_coupling(&self, id: &CouplingId) -> Option<Coupling> {
238        self.couplings.get(id).map(|r| r.clone())
239    }
240
241    /// Get all couplings for a Resonator
242    pub fn get_couplings_for(&self, resonator: &ResonatorId) -> Vec<Coupling> {
243        self.couplings
244            .iter()
245            .filter(|entry| entry.source == *resonator || entry.target == *resonator)
246            .map(|entry| entry.clone())
247            .collect()
248    }
249
250    /// Restore couplings from continuity record
251    pub async fn restore_couplings(
252        &self,
253        _resonator: &ResonatorId,
254        couplings: &[Coupling],
255    ) -> Result<(), String> {
256        for coupling in couplings {
257            self.graph.write().await.add_edge(coupling);
258            self.couplings.insert(coupling.id, coupling.clone());
259        }
260
261        tracing::debug!("Restored {} couplings", couplings.len());
262        Ok(())
263    }
264
265    /// Persist coupling topology
266    pub async fn persist_topology(&self) -> Result<(), String> {
267        // Placeholder: In real implementation, would persist to durable storage
268        tracing::info!("Persisting {} couplings", self.couplings.len());
269        Ok(())
270    }
271
272    /// Count of active couplings
273    pub fn count(&self) -> usize {
274        self.couplings.len()
275    }
276}