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 dashmap::DashMap;
6use std::sync::Arc;
7use tokio::sync::RwLock;
8use petgraph::graph::{DiGraph, NodeIndex};
9use crate::types::*;
10use crate::allocator::AttentionAllocator;
11use crate::runtime_core::CouplingHandle;
12use crate::runtime_core::DecouplingResult;
13
14
15/// Coupling Fabric manages all coupling relationships
16pub struct CouplingFabric {
17    /// Active couplings
18    couplings: DashMap<CouplingId, Coupling>,
19
20    /// Coupling graph (directed, weighted)
21    graph: Arc<RwLock<CouplingGraph>>,
22
23    /// Attention allocator reference (coupling is bounded by attention)
24    attention: Arc<AttentionAllocator>,
25
26    /// Configuration
27    config: CouplingConfig,
28}
29
30struct CouplingGraph {
31    graph: DiGraph<ResonatorId, CouplingId>,
32    node_map: std::collections::HashMap<ResonatorId, NodeIndex>,
33}
34
35impl CouplingGraph {
36    fn new() -> Self {
37        Self {
38            graph: DiGraph::new(),
39            node_map: std::collections::HashMap::new(),
40        }
41    }
42
43    fn add_node(&mut self, resonator: ResonatorId) -> NodeIndex {
44        if let Some(&node) = self.node_map.get(&resonator) {
45            return node;
46        }
47
48        let node = self.graph.add_node(resonator);
49        self.node_map.insert(resonator, node);
50        node
51    }
52
53    fn add_edge(&mut self, coupling: &Coupling) {
54        let source_node = self.add_node(coupling.source);
55        let target_node = self.add_node(coupling.target);
56        self.graph.add_edge(source_node, target_node, coupling.id);
57    }
58
59    fn remove_edge(&mut self, coupling_id: &CouplingId) {
60        if let Some(edge_index) = self
61            .graph
62            .edge_indices()
63            .find(|&e| self.graph[e] == *coupling_id)
64        {
65            self.graph.remove_edge(edge_index);
66        }
67    }
68}
69
70impl CouplingFabric {
71    pub fn new(config: &CouplingConfig, attention: Arc<AttentionAllocator>) -> Self {
72        Self {
73            couplings: DashMap::new(),
74            graph: Arc::new(RwLock::new(CouplingGraph::new())),
75            attention,
76            config: config.clone(),
77        }
78    }
79
80    /// Register a Resonator in the coupling topology
81    pub async fn register(&self, resonator: &ResonatorId) -> Result<(), String> {
82        let mut graph = self.graph.write().await;
83        graph.add_node(*resonator);
84        Ok(())
85    }
86
87    /// Establish coupling between Resonators
88    ///
89    /// ARCHITECTURAL RULE: Couplings MUST strengthen gradually.
90    /// Abrupt coupling escalation is a design failure.
91    ///
92    /// Returns the coupling ID and attention token for handle creation by the runtime.
93    pub async fn establish_coupling(
94        &self,
95        params: CouplingParams,
96    ) -> Result<(CouplingId, AllocationToken), CouplingError> {
97        // Validate parameters
98        params.validate().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!(
180                "Strengthened coupling {} to {}",
181                coupling_id,
182                new_strength
183            );
184        }
185
186        Ok(())
187    }
188
189    /// Weaken coupling without severing
190    pub async fn weaken(&self, coupling_id: CouplingId, factor: f64) -> Result<(), CouplingError> {
191        if let Some(mut coupling) = self.couplings.get_mut(&coupling_id) {
192            coupling.strength *= 1.0 - factor;
193
194            // Release some attention
195            let attention_release = (coupling.attention_allocated as f64 * factor) as u64;
196            self.attention
197                .release_partial(&coupling.source, attention_release)
198                .await;
199            coupling.attention_allocated -= attention_release;
200
201            tracing::debug!("Weakened coupling {} by factor {}", coupling_id, factor);
202        }
203
204        Ok(())
205    }
206
207    /// Safe decoupling that preserves commitments
208    ///
209    /// ARCHITECTURAL RULE: Coupling MUST be severed safely without
210    /// violating existing commitments.
211    pub async fn decouple_safely(
212        &self,
213        coupling_id: CouplingId,
214    ) -> Result<DecouplingResult, CouplingError> {
215        let coupling = self
216            .couplings
217            .get(&coupling_id)
218            .ok_or(CouplingError::NotFound)?
219            .clone();
220
221        // Check for active commitments through this coupling
222        // (would be checked via CommitmentManager in real implementation)
223
224        // Remove from graph
225        self.graph.write().await.remove_edge(&coupling_id);
226
227        // Release attention
228        self.attention
229            .release_all(&coupling.source, coupling.attention_allocated)
230            .await;
231
232        // Remove coupling
233        self.couplings.remove(&coupling_id);
234
235        tracing::debug!("Decoupled {}", coupling_id);
236
237        Ok(DecouplingResult::Success)
238    }
239
240    /// Get coupling by ID
241    pub fn get_coupling(&self, id: &CouplingId) -> Option<Coupling> {
242        self.couplings.get(id).map(|r| r.clone())
243    }
244
245    /// Get all couplings for a Resonator
246    pub fn get_couplings_for(&self, resonator: &ResonatorId) -> Vec<Coupling> {
247        self.couplings
248            .iter()
249            .filter(|entry| {
250                entry.source == *resonator || entry.target == *resonator
251            })
252            .map(|entry| entry.clone())
253            .collect()
254    }
255
256    /// Restore couplings from continuity record
257    pub async fn restore_couplings(
258        &self,
259        _resonator: &ResonatorId,
260        couplings: &[Coupling],
261    ) -> Result<(), String> {
262        for coupling in couplings {
263            self.graph.write().await.add_edge(coupling);
264            self.couplings.insert(coupling.id, coupling.clone());
265        }
266
267        tracing::debug!("Restored {} couplings", couplings.len());
268        Ok(())
269    }
270
271    /// Persist coupling topology
272    pub async fn persist_topology(&self) -> Result<(), String> {
273        // Placeholder: In real implementation, would persist to durable storage
274        tracing::info!("Persisting {} couplings", self.couplings.len());
275        Ok(())
276    }
277
278    /// Count of active couplings
279    pub fn count(&self) -> usize {
280        self.couplings.len()
281    }
282}