Skip to main content

microscope_memory/
cognitive_morphogenesis.rs

1//! Kognitív Morfogenezis — Audit-infrastruktúra és integrációs motor.
2//!
3//! Ez a modul összekapcsolja a meglévő kognitív modulokat (Hebbian, Resonance,
4//! Epistemic, Predictive Cache, Emotion) egyetlen dinamikus élő hálózattá.
5//! A MorphogenField koordinátái kognitív állapotteret reprezentálnak.
6//!
7//! Lásd: docs/COGNITIVE_MORPHOGENESIS.md
8
9use std::path::Path;
10use std::time::{SystemTime, UNIX_EPOCH};
11
12use crate::absentia::AbsentiaState;
13use crate::emotional_contagion::EmotionalContagionState;
14use crate::epistemic::EvidenceLedger;
15use crate::hebbian::HebbianState;
16use crate::morphogenesis::{
17    mycelium_growth, GrowthConfig, MorphConnection, MorphNode, MorphogenField, Seed,
18};
19use crate::predictive_cache::PredictiveCache;
20use crate::resonance::ResonanceState;
21
22// ─── Helpers ────────────────────────────────────────
23
24fn now_ms() -> u64 {
25    SystemTime::now()
26        .duration_since(UNIX_EPOCH)
27        .unwrap_or_default()
28        .as_millis() as u64
29}
30
31fn cycle_id() -> u64 {
32    now_ms() ^ 0xC0FFEE
33}
34
35// ─── Phase ──────────────────────────────────────────
36
37/// A hálózat növekedési fázisa — a gradiens erőssége határozza meg.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum Phase {
40    /// Szabad exploráció — kaotikus, minden irányba.
41    Gas,
42    /// Gradiens-követés — alkalmazkodás, áramlás.
43    Liquid,
44    /// Rögzített útvonalak — stabil, megbízható asszociációk.
45    Solid,
46}
47
48impl std::fmt::Display for Phase {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        match self {
51            Phase::Gas => write!(f, "GAS"),
52            Phase::Liquid => write!(f, "LIQUID"),
53            Phase::Solid => write!(f, "SOLID"),
54        }
55    }
56}
57
58impl Phase {
59    pub fn from_gradient(avg_gradient: f64) -> Self {
60        if avg_gradient < 0.3 {
61            Phase::Gas
62        } else if avg_gradient < 0.7 {
63            Phase::Liquid
64        } else {
65            Phase::Solid
66        }
67    }
68
69    /// Fázis-specifikus GrowthConfig alkalmazása.
70    pub fn growth_config(&self, base: &GrowthConfig) -> GrowthConfig {
71        let mut cfg = base.clone();
72        match self {
73            Phase::Gas => {
74                cfg.branching_probability = 0.5;
75                cfg.energy_decay = 0.03;
76                cfg.anastomosis_probability = 0.02;
77            }
78            Phase::Liquid => {
79                cfg.branching_probability = 0.35;
80                cfg.energy_decay = 0.08;
81                cfg.anastomosis_probability = 0.08;
82            }
83            Phase::Solid => {
84                cfg.branching_probability = 0.1;
85                cfg.energy_decay = 0.02;
86                cfg.anastomosis_probability = 0.15;
87            }
88        }
89        cfg
90    }
91}
92
93// ─── CognitiveGradient ──────────────────────────────
94
95/// Kognitív gradiens — 7 komponensből álló súlyozott jelzőrendszer.
96pub struct CognitiveGradient {
97    /// Súlyok: (relevance, resonance, evidence, hebbian, prediction, emotion, execution)
98    pub weights: (f64, f64, f64, f64, f64, f64, f64),
99}
100
101impl Default for CognitiveGradient {
102    fn default() -> Self {
103        Self {
104            weights: (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0),
105        }
106    }
107}
108
109/// The seven components one gradient cycle weighs.
110///
111/// A struct because they are seven same-typed scalars. At the call sites a
112/// positional `compute(0.5, 0.3, 50, 0.5, 0.5, 0.0, 0.5)` says nothing about
113/// which is which, and in this file `hebbian_energy` and `prediction_hit_rate`
114/// held the same value at every call site, so a transposed pair would have
115/// changed no result and no test would have said so.
116#[derive(Debug, Clone, Copy, Default, PartialEq)]
117pub struct GradientInputs {
118    pub lexical_score: f32,
119    pub resonance_strength: f32,
120    /// 0-100. Divided by 100 before it is weighted.
121    pub evidence_confidence: u8,
122    pub hebbian_energy: f32,
123    pub prediction_hit_rate: f32,
124    /// -1.0 to 1.0. Mapped onto 0-1 before it is weighted.
125    pub emotional_valence: f32,
126    pub execution_success: f32,
127}
128
129impl GradientInputs {
130    /// Every component at its maximum: the strongest support a cycle can be
131    /// given. Four call sites want exactly this, and the shadow comparisons read
132    /// better against a name than against seven repeated literals.
133    pub fn saturated() -> Self {
134        Self {
135            lexical_score: 1.0,
136            resonance_strength: 1.0,
137            evidence_confidence: 100,
138            hebbian_energy: 1.0,
139            prediction_hit_rate: 1.0,
140            emotional_valence: 1.0,
141            execution_success: 1.0,
142        }
143    }
144}
145
146impl CognitiveGradient {
147    /// Kiszámolja a kognitív gradienst egy adott blokkhoz.
148    pub fn compute(&self, inputs: GradientInputs) -> f64 {
149        let (w1, w2, w3, w4, w5, w6, w7) = self.weights;
150
151        // clamp, not max().min(): the old pattern turned a NaN score into 0.0
152        // and hid it. clamp propagates it, so a broken score surfaces here
153        // instead of being silently zero-weighted in the sum below. The f32
154        // arithmetic is left as it was, so non-NaN inputs are bit-identical.
155        let relevance = inputs.lexical_score.clamp(0.0, 1.0) as f64;
156        let resonance = inputs.resonance_strength.clamp(0.0, 1.0) as f64;
157        let evidence = (inputs.evidence_confidence as f64 / 100.0).clamp(0.0, 1.0);
158        let hebbian = inputs.hebbian_energy.clamp(0.0, 1.0) as f64;
159        let prediction = inputs.prediction_hit_rate.clamp(0.0, 1.0) as f64;
160        let emotion = ((inputs.emotional_valence + 1.0) / 2.0).clamp(0.0, 1.0) as f64;
161        let execution = inputs.execution_success.clamp(0.0, 1.0) as f64;
162
163        w1 * relevance
164            + w2 * resonance
165            + w3 * evidence
166            + w4 * hebbian
167            + w5 * prediction
168            + w6 * emotion
169            + w7 * execution
170    }
171}
172
173// ─── MorphogenesisAuditEntry ────────────────────────
174
175/// Egyetlen kognitív morfogenezis ciklus audit-bejegyzése.
176#[derive(Debug, Clone)]
177pub struct MorphogenesisAuditEntry {
178    pub timestamp_ms: u64,
179    pub cycle_id: u64,
180
181    // T0: aktiváció
182    pub activated_blocks: Vec<(u32, f32)>,
183    pub query_hash: u64,
184
185    // T1: gradiens
186    pub gradient_avg: f64,
187    pub phase: Phase,
188    pub component_scores: GradientComponents,
189
190    // T2: növekedés
191    pub new_node_count: usize,
192    pub new_connection_count: usize,
193
194    // T3: anastomosis
195    pub anastomosis_count: usize,
196    pub anastomosis_validated: usize,
197
198    // T4: megerősítés
199    pub prediction_hit: bool,
200    pub evidence_confidence_before: u8,
201    pub evidence_confidence_after: u8,
202
203    // T5: konszolidáció
204    pub solidified_paths: usize,
205    pub pruned_paths: usize,
206}
207
208/// A kognitív gradiens komponens-pontszámai.
209#[derive(Debug, Clone, Default)]
210pub struct GradientComponents {
211    pub relevance: f64,
212    pub resonance: f64,
213    pub evidence: f64,
214    pub hebbian: f64,
215    pub prediction: f64,
216    pub emotion: f64,
217    pub execution: f64,
218}
219
220impl std::fmt::Display for GradientComponents {
221    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222        write!(
223            f,
224            "rel={:.3} res={:.3} evi={:.3} heb={:.3} pred={:.3} emo={:.3} exec={:.3}",
225            self.relevance,
226            self.resonance,
227            self.evidence,
228            self.hebbian,
229            self.prediction,
230            self.emotion,
231            self.execution
232        )
233    }
234}
235
236// ─── MorphogenesisMetrics ───────────────────────────
237
238/// Metrikák egy kognitív morfogenezis ciklushoz.
239#[derive(Debug, Clone)]
240pub struct MorphogenesisMetrics {
241    pub timestamp_ms: u64,
242    pub cycle_id: u64,
243    pub phase: Phase,
244
245    // Recall
246    pub recall_precision: f64,
247    pub recall_count: usize,
248
249    // Prediction
250    pub prediction_hit_rate: f64,
251    pub prediction_count: usize,
252
253    // Associations
254    pub total_associations: usize,
255    pub false_associations: usize,
256    pub false_association_rate: f64,
257
258    // Graph
259    pub graph_entropy: f64,
260    pub node_count: usize,
261    pub edge_count: usize,
262    pub avg_degree: f64,
263
264    // Paths
265    pub total_paths: usize,
266    pub stable_paths: usize,
267    pub path_stability: f64,
268
269    // Convergence
270    pub convergence_cycles: u32,
271
272    // Counterevidence
273    pub counterevidence_reaction_rate: f64,
274
275    // Restart
276    pub restart_continuity: f64,
277}
278
279impl MorphogenesisMetrics {
280    pub fn empty() -> Self {
281        Self {
282            timestamp_ms: now_ms(),
283            cycle_id: 0,
284            phase: Phase::Gas,
285            recall_precision: 0.0,
286            recall_count: 0,
287            prediction_hit_rate: 0.0,
288            prediction_count: 0,
289            total_associations: 0,
290            false_associations: 0,
291            false_association_rate: 0.0,
292            graph_entropy: 0.0,
293            node_count: 0,
294            edge_count: 0,
295            avg_degree: 0.0,
296            total_paths: 0,
297            stable_paths: 0,
298            path_stability: 0.0,
299            convergence_cycles: 0,
300            counterevidence_reaction_rate: 0.0,
301            restart_continuity: 0.0,
302        }
303    }
304}
305
306// ─── Audit log bináris formátum ─────────────────────
307
308const AUDIT_MAGIC: &[u8; 4] = b"MGA1"; // Morphogenesis Audit v1
309const AUDIT_ENTRY_FIXED: usize = 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8;
310
311// ─── CognitiveMorphogenesisEngine ───────────────────
312
313/// A kognitív morfogenezis integrációs motor.
314#[derive(Default)]
315pub struct CognitiveMorphogenesisEngine {
316    pub audit_log: Vec<MorphogenesisAuditEntry>,
317    pub metrics_log: Vec<MorphogenesisMetrics>,
318    pub gradient: CognitiveGradient,
319}
320
321impl CognitiveMorphogenesisEngine {
322    pub fn new() -> Self {
323        Self::default()
324    }
325
326    /// Betölti a meglévő audit-naplót a fájlból (ha van).
327    pub fn load_or_init(output_dir: &Path) -> Self {
328        let mut engine = Self::new();
329        let path = output_dir.join("morphogenesis_audit.bin");
330        if let Ok(data) = std::fs::read(&path) {
331            if data.len() >= 4 && &data[0..4] == AUDIT_MAGIC {
332                engine.audit_log = decode_audit_log(&data);
333            }
334        }
335        let metrics_path = output_dir.join("morphogenesis_metrics.bin");
336        if let Ok(data) = std::fs::read(&metrics_path) {
337            if data.len() >= 4 && &data[0..4] == b"MGM1" {
338                engine.metrics_log = decode_metrics_log(&data);
339            }
340        }
341        engine
342    }
343
344    /// Elmenti az audit-naplót és a metrikákat.
345    pub fn save(&self, output_dir: &Path) -> Result<(), String> {
346        // Audit log
347        let audit_path = output_dir.join("morphogenesis_audit.bin");
348        let audit_data = encode_audit_log(&self.audit_log);
349        let tmp = output_dir.join("morphogenesis_audit.bin.tmp");
350        std::fs::write(&tmp, &audit_data).map_err(|e| format!("write audit: {}", e))?;
351        std::fs::rename(&tmp, &audit_path).map_err(|e| format!("rename audit: {}", e))?;
352
353        // Metrics log
354        let metrics_path = output_dir.join("morphogenesis_metrics.bin");
355        let metrics_data = encode_metrics_log(&self.metrics_log);
356        let tmp2 = output_dir.join("morphogenesis_metrics.bin.tmp");
357        std::fs::write(&tmp2, &metrics_data).map_err(|e| format!("write metrics: {}", e))?;
358        std::fs::rename(&tmp2, &metrics_path).map_err(|e| format!("rename metrics: {}", e))?;
359
360        Ok(())
361    }
362
363    /// Egy teljes kognitív morfogenezis ciklus végrehajtása.
364    ///
365    /// T0→T5: aktiváció → gradiens → növekedés → anastomosis → megerősítés → konszolidáció
366    ///
367    /// The count is the lint's, not the code's: nine of these eleven parameters are
368    /// references to different subsystem states, so the types already say which is
369    /// which and a wrong order does not compile. A bundled inputs struct would add
370    /// a type without removing a mistake.
371    #[allow(clippy::too_many_arguments)]
372    pub fn run_cycle(
373        &mut self,
374        activated_blocks: &[(u32, f32)],
375        query_hash: u64,
376        hebb: &HebbianState,
377        resonance: &ResonanceState,
378        evidence_ledger: &EvidenceLedger,
379        predictive_cache: &PredictiveCache,
380        emotional_state: &EmotionalContagionState,
381        block_count: usize,
382        headers: &[(f32, f32, f32)],
383        absentia: &AbsentiaState,
384    ) -> MorphogenesisAuditEntry {
385        let ts = now_ms();
386        let cid = cycle_id();
387
388        // ─── T0: Aktiváció ─────────────────────────────
389        // A Hebbian state-ből kiválasztjuk a legaktívabb blokkokat
390
391        // T1: Gradiens számolás — minden blokkhoz
392        let mut gradient_sum = 0.0f64;
393        let mut gradient_count = 0usize;
394        let mut comp_sum = GradientComponents::default();
395
396        // ─── T1: Kognitív MorphogenField építése ────────
397        // Hebbian energy → attractorok a MorphogenFieldben
398        let mut field = MorphogenField::new();
399        sync_hebbian_to_field(hebb, &mut field, headers);
400        sync_resonance_to_field(resonance, &mut field);
401        apply_evidence_modulation(&mut field, evidence_ledger, headers);
402        apply_prediction_modulation(&mut field, predictive_cache);
403        apply_emotion_modulation(&mut field, emotional_state);
404
405        // Globális komponens-értékek (cikluson kívül)
406        let pred_hr = predictive_cache.stats.hit_rate();
407        let emo_valence = emotional_state
408            .local_snapshot
409            .as_ref()
410            .map(|s| s.valence)
411            .unwrap_or(0.0);
412        let evi_conf = evidence_ledger
413            .records
414            .values()
415            .map(|r| r.confidence as f64)
416            .sum::<f64>()
417            / evidence_ledger.records.len().max(1) as f64;
418        let evi_conf_u8 = evi_conf.clamp(0.0, 100.0) as u8;
419
420        // Absentia shadow term: effective_gradient = positive_gradient * (1 - absence_shadow)
421        // A shadow-t az evidence modulálja: ha van evidence, a shadow csökken
422        crate::absentia::apply_absentia_to_field(absentia, &mut field, evi_conf_u8);
423
424        for &(block_idx, _score) in activated_blocks {
425            let idx = block_idx as usize;
426            if idx >= block_count {
427                continue;
428            }
429
430            // Hebbian energy
431            let hebb_energy = if idx < hebb.activations.len() {
432                hebb.activations[idx].energy
433            } else {
434                0.0
435            };
436
437            // Resonance — a resonance field-ből (pozíció alapján kellene, de most egyszerűsített)
438            let res_strength =
439                resonance.field.values().sum::<f32>() / resonance.field.len().max(1) as f32;
440
441            // Relevance — nem áll rendelkezésre közvetlenül, a lexical_score kellene
442            // Most 0.0, mert a relevancia-t a recall már alkalmazta
443            let lexical = 0.0f32;
444
445            // Execution — alapértelmezett sikeres
446            let exec = 1.0f32;
447
448            let g = self.gradient.compute(GradientInputs {
449                lexical_score: lexical,
450                resonance_strength: res_strength,
451                evidence_confidence: evi_conf_u8,
452                hebbian_energy: hebb_energy,
453                prediction_hit_rate: pred_hr,
454                emotional_valence: emo_valence,
455                execution_success: exec,
456            });
457            gradient_sum += g;
458            gradient_count += 1;
459
460            // Komponens-összegzés
461            comp_sum.relevance += lexical as f64;
462            comp_sum.resonance += res_strength as f64;
463            comp_sum.evidence += evi_conf / 100.0;
464            comp_sum.hebbian += hebb_energy as f64;
465            comp_sum.prediction += pred_hr as f64;
466            comp_sum.emotion += ((emo_valence + 1.0) / 2.0) as f64;
467            comp_sum.execution += exec as f64;
468        }
469
470        let gradient_avg = if gradient_count > 0 {
471            gradient_sum / gradient_count as f64
472        } else {
473            0.0
474        };
475
476        // Komponens-átlagok
477        if gradient_count > 0 {
478            let n = gradient_count as f64;
479            comp_sum.relevance /= n;
480            comp_sum.resonance /= n;
481            comp_sum.evidence /= n;
482            comp_sum.hebbian /= n;
483            comp_sum.prediction /= n;
484            comp_sum.emotion /= n;
485            comp_sum.execution /= n;
486        }
487
488        let phase = Phase::from_gradient(gradient_avg);
489
490        // ─── T2: Növekedés ─────────────────────────────
491        // Seed-ek a legaktívabb blokkok pozícióiból
492        let phase_config = phase.growth_config(&GrowthConfig::mycelium_default());
493        let mut all_nodes: Vec<MorphNode> = Vec::new();
494        let mut all_connections: Vec<MorphConnection> = Vec::new();
495        let mut anastomosis_count = 0usize;
496        let mut solidified_paths = 0usize;
497
498        // Top 3 blokk → 3 seed → 3 párhuzamos mycelium
499        // Minden organizmus forrás-blokkjait tároljuk a co-aktiváció ellenőrzéshez
500        struct OrganismContext {
501            nodes: Vec<MorphNode>,
502            connections: Vec<MorphConnection>,
503            avg_confidence: f64,
504        }
505        let mut organisms: Vec<OrganismContext> = Vec::new();
506
507        let seed_count = activated_blocks.len().min(3);
508        for (i, &(block_idx, score)) in activated_blocks.iter().take(seed_count).enumerate() {
509            let idx = block_idx as usize;
510            if idx >= headers.len() {
511                continue;
512            }
513            let (hx, hy, hz) = headers[idx];
514
515            let seed = Seed::new(
516                &format!("seed_{}_{}", cid, i),
517                hx as f64,
518                hy as f64,
519                hz as f64,
520                &format!("block_{}", idx),
521            )
522            .with_energy((score as f64 * 100.0).max(10.0));
523
524            let organism = mycelium_growth(&seed, &field, &phase_config);
525
526            // Epistemic gate: átlag confidence a forrás-blokk evidence-jából
527            let avg_conf = if evidence_ledger.records.is_empty() {
528                0.5 // alapértelmezett
529            } else {
530                evidence_ledger
531                    .records
532                    .values()
533                    .map(|r| r.confidence as f64)
534                    .sum::<f64>()
535                    / evidence_ledger.records.len() as f64
536                    / 100.0
537            };
538
539            organisms.push(OrganismContext {
540                nodes: organism.nodes,
541                connections: organism.connections,
542                avg_confidence: avg_conf,
543            });
544        }
545
546        // ─── T3: Geometriai anastomosis ─────
547        // Két organizmus akkor anastomosizál, ha van közös node-ja 0.5
548        // egységen belül. A co-aktivació alapú validáció
549        // Phase 1-ben nem volt megkülönböztethető — az audit bejegyzés
550        // minden találkozást validnak rögzíti —, és az itt számlált
551        // validált darabszámnak nem volt olvasója.
552        for i in 0..organisms.len() {
553            for j in (i + 1)..organisms.len() {
554                // Geometriai anastomosis: node-ok közötti távolság < 0.5
555                let mut geo_anastomosis = 0usize;
556                for ni in &organisms[i].nodes {
557                    for nj in &organisms[j].nodes {
558                        let dx = ni.position.0 - nj.position.0;
559                        let dy = ni.position.1 - nj.position.1;
560                        let dz = ni.position.2 - nj.position.2;
561                        let dist = (dx * dx + dy * dy + dz * dz).sqrt();
562                        if dist < 0.5 {
563                            geo_anastomosis += 1;
564                        }
565                    }
566                }
567
568                if geo_anastomosis > 0 {
569                    anastomosis_count += geo_anastomosis;
570
571                    // Every meeting counts as validated in Phase 1, which is
572                    // why the audit entry below repeats anastomosis_count.
573                }
574            }
575        }
576
577        // ─── T4: Epistemic gate — útvonalak szilárdítása ───
578        // Magas confidence → solidified. Az alacsony confidence-ig nem számlált
579        // különbszám: a pruned_paths számlálónak nem volt olvasója.
580        for org in &organisms {
581            if org.avg_confidence > 0.5 {
582                solidified_paths += 1;
583            }
584        }
585
586        // Összesítés
587        for org in organisms {
588            all_nodes.extend(org.nodes);
589            all_connections.extend(org.connections);
590        }
591
592        // ─── T3-T5: Audit ──────────────────────────────
593        let entry = MorphogenesisAuditEntry {
594            timestamp_ms: ts,
595            cycle_id: cid,
596            activated_blocks: activated_blocks.to_vec(),
597            query_hash,
598            gradient_avg,
599            phase,
600            component_scores: comp_sum,
601            new_node_count: all_nodes.len(),
602            new_connection_count: all_connections.len(),
603            anastomosis_count,
604            anastomosis_validated: anastomosis_count, // Phase 1-ben mindegyik valid
605            prediction_hit: false,
606            evidence_confidence_before: 0,
607            evidence_confidence_after: 0,
608            solidified_paths: 0,
609            pruned_paths: 0,
610        };
611
612        self.audit_log.push(entry.clone());
613
614        // ─── Metrika-gyűjtés ─────────────────────────────
615        let metrics = MorphogenesisMetrics {
616            timestamp_ms: ts,
617            cycle_id: cid,
618            phase,
619            recall_precision: 0.0, // Phase 3-ban töltjük ki
620            recall_count: activated_blocks.len(),
621            prediction_hit_rate: pred_hr as f64,
622            prediction_count: predictive_cache.stats.total_predictions as usize,
623            total_associations: all_nodes.len(),
624            false_associations: 0,
625            false_association_rate: 0.0,
626            graph_entropy: graph_entropy(all_nodes.len(), all_connections.len()),
627            node_count: all_nodes.len(),
628            edge_count: all_connections.len(),
629            avg_degree: if all_nodes.is_empty() {
630                0.0
631            } else {
632                2.0 * all_connections.len() as f64 / all_nodes.len() as f64
633            },
634            total_paths: seed_count,
635            stable_paths: solidified_paths,
636            path_stability: if seed_count > 0 {
637                solidified_paths as f64 / seed_count as f64
638            } else {
639                0.0
640            },
641            convergence_cycles: 0,
642            counterevidence_reaction_rate: 0.0,
643            restart_continuity: 0.0,
644        };
645        self.metrics_log.push(metrics);
646
647        entry
648    }
649
650    /// Statisztikák az audit-naplóról.
651    pub fn stats(&self) -> CognitiveMorphogenesisStats {
652        let total_cycles = self.audit_log.len();
653        let gas_cycles = self
654            .audit_log
655            .iter()
656            .filter(|e| e.phase == Phase::Gas)
657            .count();
658        let liquid_cycles = self
659            .audit_log
660            .iter()
661            .filter(|e| e.phase == Phase::Liquid)
662            .count();
663        let solid_cycles = self
664            .audit_log
665            .iter()
666            .filter(|e| e.phase == Phase::Solid)
667            .count();
668
669        let avg_gradient = if total_cycles > 0 {
670            self.audit_log.iter().map(|e| e.gradient_avg).sum::<f64>() / total_cycles as f64
671        } else {
672            0.0
673        };
674
675        let total_anastomosis: usize = self.audit_log.iter().map(|e| e.anastomosis_count).sum();
676        let validated_anastomosis: usize =
677            self.audit_log.iter().map(|e| e.anastomosis_validated).sum();
678
679        CognitiveMorphogenesisStats {
680            total_cycles,
681            gas_cycles,
682            liquid_cycles,
683            solid_cycles,
684            avg_gradient,
685            total_anastomosis,
686            validated_anastomosis,
687            total_audit_entries: self.audit_log.len(),
688            total_metrics_entries: self.metrics_log.len(),
689        }
690    }
691}
692
693/// Statisztikák a kognitív morfogenezisről.
694pub struct CognitiveMorphogenesisStats {
695    pub total_cycles: usize,
696    pub gas_cycles: usize,
697    pub liquid_cycles: usize,
698    pub solid_cycles: usize,
699    pub avg_gradient: f64,
700    pub total_anastomosis: usize,
701    pub validated_anastomosis: usize,
702    pub total_audit_entries: usize,
703    pub total_metrics_entries: usize,
704}
705
706// ─── Bináris szerializás ────────────────────────────
707
708fn encode_audit_log(entries: &[MorphogenesisAuditEntry]) -> Vec<u8> {
709    let mut buf = Vec::new();
710    buf.extend_from_slice(AUDIT_MAGIC);
711    buf.extend_from_slice(&(entries.len() as u32).to_le_bytes());
712
713    for e in entries {
714        buf.extend_from_slice(&e.timestamp_ms.to_le_bytes());
715        buf.extend_from_slice(&e.cycle_id.to_le_bytes());
716        buf.extend_from_slice(&(e.activated_blocks.len() as u32).to_le_bytes());
717        buf.extend_from_slice(&e.query_hash.to_le_bytes());
718        buf.extend_from_slice(&e.gradient_avg.to_le_bytes());
719        buf.extend_from_slice(&(e.phase as u8).to_le_bytes());
720        // Component scores
721        buf.extend_from_slice(&e.component_scores.relevance.to_le_bytes());
722        buf.extend_from_slice(&e.component_scores.resonance.to_le_bytes());
723        buf.extend_from_slice(&e.component_scores.evidence.to_le_bytes());
724        buf.extend_from_slice(&e.component_scores.hebbian.to_le_bytes());
725        buf.extend_from_slice(&e.component_scores.prediction.to_le_bytes());
726        buf.extend_from_slice(&e.component_scores.emotion.to_le_bytes());
727        buf.extend_from_slice(&e.component_scores.execution.to_le_bytes());
728        // Counts
729        buf.extend_from_slice(&(e.new_node_count as u32).to_le_bytes());
730        buf.extend_from_slice(&(e.new_connection_count as u32).to_le_bytes());
731        buf.extend_from_slice(&(e.anastomosis_count as u32).to_le_bytes());
732        buf.extend_from_slice(&(e.anastomosis_validated as u32).to_le_bytes());
733        buf.extend_from_slice(&(e.prediction_hit as u8).to_le_bytes());
734        buf.extend_from_slice(&e.evidence_confidence_before.to_le_bytes());
735        buf.extend_from_slice(&e.evidence_confidence_after.to_le_bytes());
736        buf.extend_from_slice(&(e.solidified_paths as u32).to_le_bytes());
737        buf.extend_from_slice(&(e.pruned_paths as u32).to_le_bytes());
738        // Activated blocks (compact: idx + score pairs)
739        for &(idx, score) in &e.activated_blocks {
740            buf.extend_from_slice(&idx.to_le_bytes());
741            buf.extend_from_slice(&score.to_le_bytes());
742        }
743    }
744    buf
745}
746
747fn decode_audit_log(data: &[u8]) -> Vec<MorphogenesisAuditEntry> {
748    if data.len() < 8 {
749        return Vec::new();
750    }
751    let count = u32::from_le_bytes(data[4..8].try_into().unwrap()) as usize;
752    let mut entries = Vec::with_capacity(count);
753    let mut off = 8;
754
755    for _ in 0..count {
756        if off + AUDIT_ENTRY_FIXED > data.len() {
757            break;
758        }
759        let ts = u64::from_le_bytes(data[off..off + 8].try_into().unwrap());
760        off += 8;
761        let cid = u64::from_le_bytes(data[off..off + 8].try_into().unwrap());
762        off += 8;
763        let block_count = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
764        off += 4;
765        let qh = u64::from_le_bytes(data[off..off + 8].try_into().unwrap());
766        off += 8;
767        let ga = f64::from_le_bytes(data[off..off + 8].try_into().unwrap());
768        off += 8;
769        let phase_byte = data[off];
770        off += 1;
771        let phase = match phase_byte {
772            0 => Phase::Gas,
773            1 => Phase::Liquid,
774            _ => Phase::Solid,
775        };
776        let rel = f64::from_le_bytes(data[off..off + 8].try_into().unwrap());
777        off += 8;
778        let res = f64::from_le_bytes(data[off..off + 8].try_into().unwrap());
779        off += 8;
780        let evi = f64::from_le_bytes(data[off..off + 8].try_into().unwrap());
781        off += 8;
782        let heb = f64::from_le_bytes(data[off..off + 8].try_into().unwrap());
783        off += 8;
784        let pred = f64::from_le_bytes(data[off..off + 8].try_into().unwrap());
785        off += 8;
786        let emo = f64::from_le_bytes(data[off..off + 8].try_into().unwrap());
787        off += 8;
788        let exec = f64::from_le_bytes(data[off..off + 8].try_into().unwrap());
789        off += 8;
790        let nnc = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
791        off += 4;
792        let ncc = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
793        off += 4;
794        let ac = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
795        off += 4;
796        let av = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
797        off += 4;
798        let ph = data[off] != 0;
799        off += 1;
800        let ecb = data[off];
801        off += 1;
802        let eca = data[off];
803        off += 1;
804        let sp = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
805        off += 4;
806        let pp = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
807        off += 4;
808
809        let mut blocks = Vec::with_capacity(block_count);
810        for _ in 0..block_count {
811            if off + 8 > data.len() {
812                break;
813            }
814            let idx = u32::from_le_bytes(data[off..off + 4].try_into().unwrap());
815            let score = f32::from_le_bytes(data[off + 4..off + 8].try_into().unwrap());
816            blocks.push((idx, score));
817            off += 8;
818        }
819
820        entries.push(MorphogenesisAuditEntry {
821            timestamp_ms: ts,
822            cycle_id: cid,
823            activated_blocks: blocks,
824            query_hash: qh,
825            gradient_avg: ga,
826            phase,
827            component_scores: GradientComponents {
828                relevance: rel,
829                resonance: res,
830                evidence: evi,
831                hebbian: heb,
832                prediction: pred,
833                emotion: emo,
834                execution: exec,
835            },
836            new_node_count: nnc,
837            new_connection_count: ncc,
838            anastomosis_count: ac,
839            anastomosis_validated: av,
840            prediction_hit: ph,
841            evidence_confidence_before: ecb,
842            evidence_confidence_after: eca,
843            solidified_paths: sp,
844            pruned_paths: pp,
845        });
846    }
847    entries
848}
849
850fn encode_metrics_log(entries: &[MorphogenesisMetrics]) -> Vec<u8> {
851    let mut buf = Vec::new();
852    buf.extend_from_slice(b"MGM1");
853    buf.extend_from_slice(&(entries.len() as u32).to_le_bytes());
854
855    for e in entries {
856        buf.extend_from_slice(&e.timestamp_ms.to_le_bytes());
857        buf.extend_from_slice(&e.cycle_id.to_le_bytes());
858        buf.extend_from_slice(&(e.phase as u8).to_le_bytes());
859        buf.extend_from_slice(&e.recall_precision.to_le_bytes());
860        buf.extend_from_slice(&(e.recall_count as u32).to_le_bytes());
861        buf.extend_from_slice(&e.prediction_hit_rate.to_le_bytes());
862        buf.extend_from_slice(&(e.prediction_count as u32).to_le_bytes());
863        buf.extend_from_slice(&(e.total_associations as u32).to_le_bytes());
864        buf.extend_from_slice(&(e.false_associations as u32).to_le_bytes());
865        buf.extend_from_slice(&e.false_association_rate.to_le_bytes());
866        buf.extend_from_slice(&e.graph_entropy.to_le_bytes());
867        buf.extend_from_slice(&(e.node_count as u32).to_le_bytes());
868        buf.extend_from_slice(&(e.edge_count as u32).to_le_bytes());
869        buf.extend_from_slice(&e.avg_degree.to_le_bytes());
870        buf.extend_from_slice(&(e.total_paths as u32).to_le_bytes());
871        buf.extend_from_slice(&(e.stable_paths as u32).to_le_bytes());
872        buf.extend_from_slice(&e.path_stability.to_le_bytes());
873        buf.extend_from_slice(&e.convergence_cycles.to_le_bytes());
874        buf.extend_from_slice(&e.counterevidence_reaction_rate.to_le_bytes());
875        buf.extend_from_slice(&e.restart_continuity.to_le_bytes());
876    }
877    buf
878}
879
880fn decode_metrics_log(data: &[u8]) -> Vec<MorphogenesisMetrics> {
881    if data.len() < 8 {
882        return Vec::new();
883    }
884    let count = u32::from_le_bytes(data[4..8].try_into().unwrap()) as usize;
885    let mut entries = Vec::with_capacity(count);
886    let mut off = 8;
887
888    for _ in 0..count {
889        if off + 113 > data.len() {
890            break;
891        } // fixed size per entry
892        let ts = u64::from_le_bytes(data[off..off + 8].try_into().unwrap());
893        off += 8;
894        let cid = u64::from_le_bytes(data[off..off + 8].try_into().unwrap());
895        off += 8;
896        let phase = match data[off] {
897            0 => Phase::Gas,
898            1 => Phase::Liquid,
899            _ => Phase::Solid,
900        };
901        off += 1;
902        let rp = f64::from_le_bytes(data[off..off + 8].try_into().unwrap());
903        off += 8;
904        let rc = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
905        off += 4;
906        let phr = f64::from_le_bytes(data[off..off + 8].to_vec().try_into().unwrap());
907        off += 8;
908        let pc = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
909        off += 4;
910        let ta = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
911        off += 4;
912        let fa = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
913        off += 4;
914        let far = f64::from_le_bytes(data[off..off + 8].to_vec().try_into().unwrap());
915        off += 8;
916        let ge = f64::from_le_bytes(data[off..off + 8].to_vec().try_into().unwrap());
917        off += 8;
918        let nc = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
919        off += 4;
920        let ec = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
921        off += 4;
922        let ad = f64::from_le_bytes(data[off..off + 8].to_vec().try_into().unwrap());
923        off += 8;
924        let tp = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
925        off += 4;
926        let sp = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
927        off += 4;
928        let ps = f64::from_le_bytes(data[off..off + 8].to_vec().try_into().unwrap());
929        off += 8;
930        let cc = u32::from_le_bytes(data[off..off + 4].try_into().unwrap());
931        off += 4;
932        let crr = f64::from_le_bytes(data[off..off + 8].to_vec().try_into().unwrap());
933        off += 8;
934        let rsc = f64::from_le_bytes(data[off..off + 8].to_vec().try_into().unwrap());
935        off += 8;
936
937        entries.push(MorphogenesisMetrics {
938            timestamp_ms: ts,
939            cycle_id: cid,
940            phase,
941            recall_precision: rp,
942            recall_count: rc,
943            prediction_hit_rate: phr,
944            prediction_count: pc,
945            total_associations: ta,
946            false_associations: fa,
947            false_association_rate: far,
948            graph_entropy: ge,
949            node_count: nc,
950            edge_count: ec,
951            avg_degree: ad,
952            total_paths: tp,
953            stable_paths: sp,
954            path_stability: ps,
955            convergence_cycles: cc,
956            counterevidence_reaction_rate: crr,
957            restart_continuity: rsc,
958        });
959    }
960    entries
961}
962
963// ─── Sync helpers (Phase 1+ integráció) ─────────────
964
965/// Hebbian energy → MorphogenField attractorok szinkronizációja.
966pub fn sync_hebbian_to_field(
967    hebb: &HebbianState,
968    field: &mut MorphogenField,
969    headers: &[(f32, f32, f32)],
970) {
971    for (i, rec) in hebb.activations.iter().enumerate() {
972        if rec.energy > 0.1 && i < headers.len() {
973            let (x, y, z) = headers[i];
974            field.add_attractor(x as f64, y as f64, z as f64, rec.energy as f64);
975        }
976    }
977}
978
979/// Resonance field → MorphogenField gradiensek szinkronizációja.
980pub fn sync_resonance_to_field(res: &ResonanceState, field: &mut MorphogenField) {
981    for (&(x, y, z), &strength) in &res.field {
982        let fx = x as f64 * 0.05; // de-quantize
983        let fy = y as f64 * 0.05;
984        let fz = z as f64 * 0.05;
985        field.add_attractor(fx, fy, fz, strength as f64);
986    }
987}
988
989/// Evidence confidence modulálja a gradienst.
990pub fn apply_evidence_modulation(
991    field: &mut MorphogenField,
992    ledger: &EvidenceLedger,
993    _headers: &[(f32, f32, f32)],
994) {
995    if ledger.records.is_empty() {
996        return;
997    }
998    let avg_conf: f64 = ledger
999        .records
1000        .values()
1001        .map(|r| r.confidence as f64)
1002        .sum::<f64>()
1003        / ledger.records.len() as f64;
1004    let factor = 1.0 + avg_conf / 200.0; // enyhe boost
1005    for val in field.gradients.values_mut() {
1006        *val *= factor;
1007    }
1008}
1009
1010/// Prediction hit-rate modulálja a gradiens globális erősségét.
1011pub fn apply_prediction_modulation(field: &mut MorphogenField, cache: &PredictiveCache) {
1012    let hit_rate = cache.stats.hit_rate() as f64;
1013    let modulation = 1.0 + hit_rate;
1014    for val in field.gradients.values_mut() {
1015        *val *= modulation;
1016    }
1017}
1018
1019/// Emotion modulálja a gradiens dinamikáját.
1020pub fn apply_emotion_modulation(field: &mut MorphogenField, emo: &EmotionalContagionState) {
1021    if let Some(ref snap) = emo.local_snapshot {
1022        let valence_factor = (snap.valence as f64 + 1.0) / 2.0; // [-1,1] → [0,1]
1023        for val in field.gradients.values_mut() {
1024            *val *= 0.5 + valence_factor;
1025        }
1026    }
1027}
1028
1029// ─── Graph entropy ──────────────────────────────────
1030
1031/// Shannon-entrópia a gráf fokszám-eloszlásából.
1032pub fn graph_entropy(node_count: usize, edge_count: usize) -> f64 {
1033    if node_count == 0 || edge_count == 0 {
1034        return 0.0;
1035    }
1036    let avg_degree = (2.0 * edge_count as f64) / node_count as f64;
1037    let p = (avg_degree / (node_count as f64)).min(1.0);
1038    if p <= 0.0 || p >= 1.0 {
1039        return 0.0;
1040    }
1041    -(p * p.log2() + (1.0 - p) * (1.0 - p).log2())
1042}