1use 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
22fn 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum Phase {
40 Gas,
42 Liquid,
44 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 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
93pub struct CognitiveGradient {
97 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#[derive(Debug, Clone, Copy, Default, PartialEq)]
117pub struct GradientInputs {
118 pub lexical_score: f32,
119 pub resonance_strength: f32,
120 pub evidence_confidence: u8,
122 pub hebbian_energy: f32,
123 pub prediction_hit_rate: f32,
124 pub emotional_valence: f32,
126 pub execution_success: f32,
127}
128
129impl GradientInputs {
130 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 pub fn compute(&self, inputs: GradientInputs) -> f64 {
149 let (w1, w2, w3, w4, w5, w6, w7) = self.weights;
150
151 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#[derive(Debug, Clone)]
177pub struct MorphogenesisAuditEntry {
178 pub timestamp_ms: u64,
179 pub cycle_id: u64,
180
181 pub activated_blocks: Vec<(u32, f32)>,
183 pub query_hash: u64,
184
185 pub gradient_avg: f64,
187 pub phase: Phase,
188 pub component_scores: GradientComponents,
189
190 pub new_node_count: usize,
192 pub new_connection_count: usize,
193
194 pub anastomosis_count: usize,
196 pub anastomosis_validated: usize,
197
198 pub prediction_hit: bool,
200 pub evidence_confidence_before: u8,
201 pub evidence_confidence_after: u8,
202
203 pub solidified_paths: usize,
205 pub pruned_paths: usize,
206}
207
208#[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#[derive(Debug, Clone)]
240pub struct MorphogenesisMetrics {
241 pub timestamp_ms: u64,
242 pub cycle_id: u64,
243 pub phase: Phase,
244
245 pub recall_precision: f64,
247 pub recall_count: usize,
248
249 pub prediction_hit_rate: f64,
251 pub prediction_count: usize,
252
253 pub total_associations: usize,
255 pub false_associations: usize,
256 pub false_association_rate: f64,
257
258 pub graph_entropy: f64,
260 pub node_count: usize,
261 pub edge_count: usize,
262 pub avg_degree: f64,
263
264 pub total_paths: usize,
266 pub stable_paths: usize,
267 pub path_stability: f64,
268
269 pub convergence_cycles: u32,
271
272 pub counterevidence_reaction_rate: f64,
274
275 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
306const AUDIT_MAGIC: &[u8; 4] = b"MGA1"; const AUDIT_ENTRY_FIXED: usize = 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8;
310
311#[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 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 pub fn save(&self, output_dir: &Path) -> Result<(), String> {
346 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 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 #[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 let mut gradient_sum = 0.0f64;
393 let mut gradient_count = 0usize;
394 let mut comp_sum = GradientComponents::default();
395
396 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 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 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 let hebb_energy = if idx < hebb.activations.len() {
432 hebb.activations[idx].energy
433 } else {
434 0.0
435 };
436
437 let res_strength =
439 resonance.field.values().sum::<f32>() / resonance.field.len().max(1) as f32;
440
441 let lexical = 0.0f32;
444
445 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 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 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 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 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 let avg_conf = if evidence_ledger.records.is_empty() {
528 0.5 } 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 for i in 0..organisms.len() {
553 for j in (i + 1)..organisms.len() {
554 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 }
574 }
575 }
576
577 for org in &organisms {
581 if org.avg_confidence > 0.5 {
582 solidified_paths += 1;
583 }
584 }
585
586 for org in organisms {
588 all_nodes.extend(org.nodes);
589 all_connections.extend(org.connections);
590 }
591
592 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, 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 let metrics = MorphogenesisMetrics {
616 timestamp_ms: ts,
617 cycle_id: cid,
618 phase,
619 recall_precision: 0.0, 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 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
693pub 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
706fn 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 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 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 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 } 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
963pub 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
979pub 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; 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
989pub 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; for val in field.gradients.values_mut() {
1006 *val *= factor;
1007 }
1008}
1009
1010pub 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
1019pub 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; for val in field.gradients.values_mut() {
1024 *val *= 0.5 + valence_factor;
1025 }
1026 }
1027}
1028
1029pub 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}