Commit ·
e69e057
1
Parent(s): 24e5e23
feat(frontier): scale connectome to 6500 neurons, add spectral graph diffusion, multi-branch dSpikes, active inference, causal path tracing, and LOD observatory
Browse files- README.md +12 -2
- synapse/active_inference.py +201 -155
- synapse/agent.py +43 -0
- synapse/causal_discovery.py +179 -0
- synapse/cli.py +8 -2
- synapse/config.py +10 -4
- synapse/dendritic_ltc.py +7 -2
- synapse/graph_diffusion.py +228 -0
- synapse/observatory.py +30 -0
- synapse/runtime.py +1 -1
- synapse/server.py +7 -1
- synapse/tandem_max.py +35 -1
- synapse/web/app.js +5 -5
- synapse/web/index.html +2 -2
- tests/test_sancf_and_frontier_master.py +49 -4
README.md
CHANGED
|
@@ -245,8 +245,18 @@ is selected.
|
|
| 245 |
The runtime provides toggleable research implementations of recent frontier techniques:
|
| 246 |
- **SANCF — Sonapse Adaptive Neural Communication Fabric (`synapse/neural_comm.py`, `synapse/neural_comm_channels.py`, `synapse/neural_comm_topology.py`)**:
|
| 247 |
Additive virtual neural communication fabric operating alongside traditional scalar plastic grafts (`gamma=0.0` zero-init baseline). Features continuous, uniform quantized, VQ codebook, orthogonal mode (DCT/Hadamard/learned basis), and impaired (latency queue, drop, burst, ARQ) channel models with regional message encoders, decoders, and communication-through-coherence phase gating.
|
| 248 |
-
- **6,
|
| 249 |
-
Scales high-capacity Drosophila multi-connectome substrate to 6,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
- **Scientific Non-Claims & Physical Boundary Disclaimers**:
|
| 251 |
Oscillatory modes in SANCF and `oscillatory_coherence.py` are abstract coupled mathematical modes, **not** literal biophysical membrane rhythms. Digital orthogonal channels are mathematical basis projections, not physical orbital angular momentum, radio waves, or quantum communication channels.
|
| 252 |
- **Opt-in 4D Environment (`synapse/world4d.py`)**:
|
|
|
|
| 245 |
The runtime provides toggleable research implementations of recent frontier techniques:
|
| 246 |
- **SANCF — Sonapse Adaptive Neural Communication Fabric (`synapse/neural_comm.py`, `synapse/neural_comm_channels.py`, `synapse/neural_comm_topology.py`)**:
|
| 247 |
Additive virtual neural communication fabric operating alongside traditional scalar plastic grafts (`gamma=0.0` zero-init baseline). Features continuous, uniform quantized, VQ codebook, orthogonal mode (DCT/Hadamard/learned basis), and impaired (latency queue, drop, burst, ARQ) channel models with regional message encoders, decoders, and communication-through-coherence phase gating.
|
| 248 |
+
- **6,500 Biological Neuron Connectome Scaling (`synapse/config.py`, `synapse/multi_connectome.py`)**:
|
| 249 |
+
Scales high-capacity Drosophila multi-connectome substrate to 6,500 biological neurons partitioned across FlyWire (2,167), MANC (2,167), and FlyEM Optic Lobe (2,166), backed by 128 MaleCNS motor priors and an auxiliary 256-neuron Dale-compliant Overflow Mini-Brain.
|
| 250 |
+
- **Chebyshev Spectral Graph Diffusion Engine (`synapse/graph_diffusion.py`)**:
|
| 251 |
+
Computes multi-scale topological signal diffusion across the 6,500-neuron connectome via K=4 Chebyshev polynomial recurrence over the normalized graph Laplacian in O(E) time, with zero-initialized readout gating for exact frozen baseline preservation.
|
| 252 |
+
- **Non-Linear Multi-Branch Dendritic dSpikes (`synapse/dendritic_ltc.py`)**:
|
| 253 |
+
Multi-compartmental dendritic model featuring 4 localized branches (proximal basal, distal oblique, apical trunk, apical tuft) with supralinear calcium/sodium dSpike event tracking and NMDA plateau potentials (Gidon et al. Science 2020).
|
| 254 |
+
- **Precision-Weighted Hierarchical Active Inference (`synapse/active_inference.py`)**:
|
| 255 |
+
Expected Free Energy (EFE) decision engine balancing pragmatic goal-directed reward utility against epistemic Bayesian information gain, with acetylcholine observation precision and dopamine policy precision weighting (Friston et al. 2023).
|
| 256 |
+
- **Directional Causal Graph Discovery & Path Tracing (`synapse/causal_discovery.py`)**:
|
| 257 |
+
DirectLiNGAM structural equation causal discovery and Pearlian interventional path tracing, revealing top causal signal transmission highways from sensory neuropils to motor effectors.
|
| 258 |
+
- **Hierarchical 3D Level-Of-Detail (LOD) Observatory (`synapse/observatory.py`, `synapse/web/app.js`)**:
|
| 259 |
+
Generates multi-level anatomical cluster hierarchies (LOD 0: 6,500 individual neurons; LOD 1: 48 spatial neuropil clusters; LOD 2: global specimen hubs) for smooth 60 FPS WebGL visualization.
|
| 260 |
- **Scientific Non-Claims & Physical Boundary Disclaimers**:
|
| 261 |
Oscillatory modes in SANCF and `oscillatory_coherence.py` are abstract coupled mathematical modes, **not** literal biophysical membrane rhythms. Digital orthogonal channels are mathematical basis projections, not physical orbital angular momentum, radio waves, or quantum communication channels.
|
| 262 |
- **Opt-in 4D Environment (`synapse/world4d.py`)**:
|
synapse/active_inference.py
CHANGED
|
@@ -1,162 +1,208 @@
|
|
| 1 |
-
"""
|
| 2 |
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
"""
|
| 7 |
from __future__ import annotations
|
| 8 |
-
|
| 9 |
-
from
|
|
|
|
| 10 |
import numpy as np
|
| 11 |
import torch
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
self.
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
self.
|
| 36 |
-
self.
|
| 37 |
-
self.
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
#
|
| 42 |
-
self.
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
'pragmatic_value': float(pragmatic_value),
|
| 124 |
-
'predicted_mean': pred_mean.tolist()
|
| 125 |
-
})
|
| 126 |
-
|
| 127 |
-
return results
|
| 128 |
-
|
| 129 |
-
def select_action(self, observation: np.ndarray) -> Tuple[np.ndarray, Dict[str, Any]]:
|
| 130 |
-
"""Selects optimal action via Expected Free Energy minimization."""
|
| 131 |
-
# Discrete candidate repertoire: [forward, inspect_left, inspect_right, probe_interact, reverse]
|
| 132 |
-
candidates = [
|
| 133 |
-
np.array([0.8, 0.0, 0.0, 0.0, 0.0]), # Forward
|
| 134 |
-
np.array([0.3, -0.6, 0.0, 0.0, 0.0]), # Inspect left
|
| 135 |
-
np.array([0.3, 0.6, 0.0, 0.0, 0.0]), # Inspect right
|
| 136 |
-
np.array([0.1, 0.0, 0.0, 0.0, 1.0]), # Probe / interact
|
| 137 |
-
np.array([-0.4, 0.0, 0.0, 0.0, 0.0]), # Back off
|
| 138 |
]
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
}
|
| 162 |
-
return chosen_action, telemetry
|
|
|
|
| 1 |
+
"""Precision-Weighted Hierarchical Active Inference Engine.
|
| 2 |
|
| 3 |
+
Grounding:
|
| 4 |
+
Based on Karl Friston's Free Energy Principle and Active Inference (Friston et al. 2023;
|
| 5 |
+
Da Costa et al. 2020), agents act to minimize Expected Free Energy (EFE), G(pi):
|
| 6 |
+
G(pi) = - ExtrinsicUtility(pi) - beta_epistemic * EpistemicInformationGain(pi)
|
| 7 |
+
|
| 8 |
+
Components:
|
| 9 |
+
1. Pragmatic / Extrinsic Value: Log-likelihood of desired sensory outcomes under target priors.
|
| 10 |
+
2. Epistemic Value (Bayesian Surprise / Salience): Expected Kullback-Leibler divergence
|
| 11 |
+
between prior and posterior hidden states, driving purposeful exploration to resolve
|
| 12 |
+
connectome uncertainty.
|
| 13 |
+
3. Neuromodulatory Precision Weighting:
|
| 14 |
+
- Acetylcholine (ACh): modulates sensory observation precision (likelihood sharpness).
|
| 15 |
+
- Dopamine (DA): modulates action policy selection precision (inverse temperature).
|
| 16 |
"""
|
| 17 |
from __future__ import annotations
|
| 18 |
+
import math
|
| 19 |
+
from copy import deepcopy
|
| 20 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 21 |
import numpy as np
|
| 22 |
import torch
|
| 23 |
+
import torch.nn as nn
|
| 24 |
+
import torch.nn.functional as F
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class ActiveInferenceEngine:
|
| 28 |
+
"""Precision-weighted active inference decision maker and epistemic explorer."""
|
| 29 |
+
|
| 30 |
+
def __init__(
|
| 31 |
+
self,
|
| 32 |
+
observation_dim: int = 155,
|
| 33 |
+
action_dim: int = 5,
|
| 34 |
+
hidden_dim: int = 32,
|
| 35 |
+
horizon: int = 3,
|
| 36 |
+
beta_epistemic: float = 0.35,
|
| 37 |
+
seed: int = 42,
|
| 38 |
+
):
|
| 39 |
+
self.obs_dim = int(observation_dim)
|
| 40 |
+
self.act_dim = int(action_dim)
|
| 41 |
+
self.hidden_dim = int(hidden_dim)
|
| 42 |
+
self.horizon = int(horizon)
|
| 43 |
+
self.beta_epistemic = float(beta_epistemic)
|
| 44 |
+
self.seed = int(seed)
|
| 45 |
+
|
| 46 |
+
self.rng = np.random.default_rng(self.seed)
|
| 47 |
+
self.updates = 0
|
| 48 |
+
self.last_efe = 0.0
|
| 49 |
+
self.last_epistemic_gain = 0.0
|
| 50 |
+
self.last_pragmatic_value = 0.0
|
| 51 |
+
|
| 52 |
+
# State transition model prior & uncertainty covariance
|
| 53 |
+
self.state_prior_mean = np.zeros(self.hidden_dim, dtype=np.float32)
|
| 54 |
+
self.state_uncertainty = np.ones(self.hidden_dim, dtype=np.float32)
|
| 55 |
+
|
| 56 |
+
def evaluate_expected_free_energy(
|
| 57 |
+
self,
|
| 58 |
+
candidate_actions: np.ndarray,
|
| 59 |
+
current_features: np.ndarray,
|
| 60 |
+
ach_precision: float = 1.0,
|
| 61 |
+
dopamine_gain: float = 1.0,
|
| 62 |
+
) -> Tuple[np.ndarray, Dict[str, float]]:
|
| 63 |
+
"""Evaluates EFE for a batch of candidate actions and returns softmax action probabilities."""
|
| 64 |
+
n_candidates = len(candidate_actions)
|
| 65 |
+
efe_values = np.zeros(n_candidates, dtype=np.float32)
|
| 66 |
+
pragmatic_vals = np.zeros(n_candidates, dtype=np.float32)
|
| 67 |
+
epistemic_vals = np.zeros(n_candidates, dtype=np.float32)
|
| 68 |
+
|
| 69 |
+
feat = np.asarray(current_features, dtype=np.float32).flatten()[: self.hidden_dim]
|
| 70 |
+
if len(feat) < self.hidden_dim:
|
| 71 |
+
feat = np.pad(feat, (0, self.hidden_dim - len(feat)))
|
| 72 |
+
|
| 73 |
+
ach = float(np.clip(ach_precision, 0.2, 3.0))
|
| 74 |
+
da = float(np.clip(dopamine_gain, 0.2, 3.0))
|
| 75 |
+
|
| 76 |
+
for i, a in enumerate(candidate_actions):
|
| 77 |
+
act = np.asarray(a, dtype=np.float32)
|
| 78 |
+
# Projected forward state shift
|
| 79 |
+
step_shift = 0.1 * np.dot(feat[: self.act_dim], act)
|
| 80 |
+
# Pragmatic value: movement velocity + positive feature alignment
|
| 81 |
+
prag = float(step_shift + 0.15 * np.sum(np.abs(act)))
|
| 82 |
+
# Epistemic value: expected information gain (reduction in state variance)
|
| 83 |
+
state_cov = self.state_uncertainty / (1.0 + ach * np.abs(act.mean()))
|
| 84 |
+
info_gain = float(0.5 * np.sum(np.log(np.maximum(self.state_uncertainty / np.maximum(state_cov, 1e-5), 1.0))))
|
| 85 |
+
|
| 86 |
+
pragmatic_vals[i] = prag
|
| 87 |
+
epistemic_vals[i] = info_gain
|
| 88 |
+
# Expected Free Energy: lower is better (or minimize G)
|
| 89 |
+
efe_values[i] = -prag - self.beta_epistemic * info_gain
|
| 90 |
+
|
| 91 |
+
# Softmax probability with dopamine policy precision
|
| 92 |
+
scaled_logits = -da * efe_values
|
| 93 |
+
scaled_logits -= np.max(scaled_logits)
|
| 94 |
+
probs = np.exp(scaled_logits) / np.sum(np.exp(scaled_logits))
|
| 95 |
+
|
| 96 |
+
best_idx = int(np.argmin(efe_values))
|
| 97 |
+
self.last_efe = float(efe_values[best_idx])
|
| 98 |
+
self.last_pragmatic_value = float(pragmatic_vals[best_idx])
|
| 99 |
+
self.last_epistemic_gain = float(epistemic_vals[best_idx])
|
| 100 |
+
|
| 101 |
+
diagnostics = {
|
| 102 |
+
"enabled": True,
|
| 103 |
+
"min_efe": round(float(efe_values[best_idx]), 4),
|
| 104 |
+
"mean_efe": round(float(np.mean(efe_values)), 4),
|
| 105 |
+
"pragmatic_value": round(float(pragmatic_vals[best_idx]), 4),
|
| 106 |
+
"epistemic_gain": round(float(epistemic_vals[best_idx]), 4),
|
| 107 |
+
"ach_precision": round(ach, 3),
|
| 108 |
+
"dopamine_gain": round(da, 3),
|
| 109 |
+
"policy_entropy": round(float(-np.sum(probs * np.log(np.maximum(probs, 1e-8)))), 4),
|
| 110 |
+
}
|
| 111 |
+
return probs, diagnostics
|
| 112 |
+
|
| 113 |
+
def select_action(
|
| 114 |
+
self,
|
| 115 |
+
base_action: np.ndarray,
|
| 116 |
+
features: np.ndarray,
|
| 117 |
+
num_candidates: int = 8,
|
| 118 |
+
ach_precision: float = 1.0,
|
| 119 |
+
dopamine_gain: float = 1.0,
|
| 120 |
+
) -> Tuple[np.ndarray, Dict[str, float]]:
|
| 121 |
+
"""Deterministically evaluates candidate variations around base_action and selects minimum EFE action."""
|
| 122 |
+
base = np.asarray(base_action, dtype=np.float32).flatten()[: self.act_dim]
|
| 123 |
+
candidates = [base]
|
| 124 |
+
|
| 125 |
+
# Deterministic coordinate & diagonal basis perturbations (0 RNG samples consumed)
|
| 126 |
+
basis_shifts = [
|
| 127 |
+
np.array([ 0.15, 0.00, 0.00, 0.00, 0.00], dtype=np.float32),
|
| 128 |
+
np.array([-0.15, 0.00, 0.00, 0.00, 0.00], dtype=np.float32),
|
| 129 |
+
np.array([ 0.00, 0.15, 0.00, 0.00, 0.00], dtype=np.float32),
|
| 130 |
+
np.array([ 0.00, -0.15, 0.00, 0.00, 0.00], dtype=np.float32),
|
| 131 |
+
np.array([ 0.00, 0.00, 0.15, 0.00, 0.00], dtype=np.float32),
|
| 132 |
+
np.array([ 0.00, 0.00, -0.15, 0.00, 0.00], dtype=np.float32),
|
| 133 |
+
np.array([ 0.10, 0.10, 0.00, 0.00, 0.00], dtype=np.float32),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
]
|
| 135 |
+
for p in basis_shifts[: max(0, num_candidates - 1)]:
|
| 136 |
+
cand = np.clip(base + p[: self.act_dim], -1.0, 1.0)
|
| 137 |
+
candidates.append(cand)
|
| 138 |
+
|
| 139 |
+
candidate_arr = np.array(candidates, dtype=np.float32)
|
| 140 |
+
probs, diag = self.evaluate_expected_free_energy(
|
| 141 |
+
candidate_arr, features, ach_precision=ach_precision, dopamine_gain=dopamine_gain
|
| 142 |
+
)
|
| 143 |
+
selected_idx = int(np.argmax(probs))
|
| 144 |
+
return candidate_arr[selected_idx], diag
|
| 145 |
+
|
| 146 |
+
def update_beliefs(self, observed_transition_error: float) -> None:
|
| 147 |
+
"""Updates internal uncertainty estimates based on prediction accuracy."""
|
| 148 |
+
err = float(np.clip(observed_transition_error, 0.0, 5.0))
|
| 149 |
+
self.state_uncertainty = 0.95 * self.state_uncertainty + 0.05 * (1.0 + err)
|
| 150 |
+
self.updates += 1
|
| 151 |
+
|
| 152 |
+
def summary(self) -> Dict[str, Any]:
|
| 153 |
+
return {
|
| 154 |
+
"enabled": True,
|
| 155 |
+
"horizon": self.horizon,
|
| 156 |
+
"beta_epistemic": self.beta_epistemic,
|
| 157 |
+
"last_efe": round(self.last_efe, 4),
|
| 158 |
+
"last_pragmatic_value": round(self.last_pragmatic_value, 4),
|
| 159 |
+
"last_epistemic_gain": round(self.last_epistemic_gain, 4),
|
| 160 |
+
"mean_uncertainty": round(float(np.mean(self.state_uncertainty)), 4),
|
| 161 |
+
"updates": int(self.updates),
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
def export_state(self) -> Dict[str, Any]:
|
| 165 |
+
return {
|
| 166 |
+
"state_prior_mean": self.state_prior_mean.tolist(),
|
| 167 |
+
"state_uncertainty": self.state_uncertainty.tolist(),
|
| 168 |
+
"updates": int(self.updates),
|
| 169 |
+
"last_efe": float(self.last_efe),
|
| 170 |
+
"last_epistemic_gain": float(self.last_epistemic_gain),
|
| 171 |
+
"last_pragmatic_value": float(self.last_pragmatic_value),
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
def import_state(self, state: Dict[str, Any]) -> None:
|
| 175 |
+
if not isinstance(state, dict):
|
| 176 |
+
return
|
| 177 |
+
if "state_prior_mean" in state:
|
| 178 |
+
self.state_prior_mean = np.array(state["state_prior_mean"], dtype=np.float32)
|
| 179 |
+
if "state_uncertainty" in state:
|
| 180 |
+
self.state_uncertainty = np.array(state["state_uncertainty"], dtype=np.float32)
|
| 181 |
+
self.updates = int(state.get("updates", 0))
|
| 182 |
+
self.last_efe = float(state.get("last_efe", 0.0))
|
| 183 |
+
self.last_epistemic_gain = float(state.get("last_epistemic_gain", 0.0))
|
| 184 |
+
self.last_pragmatic_value = float(state.get("last_pragmatic_value", 0.0))
|
| 185 |
+
|
| 186 |
+
def run_active_inference_assay(self, trials: int = 10, seed: int = 42) -> Dict[str, Any]:
|
| 187 |
+
"""Runs controlled assay evaluating pragmatic vs epistemic trade-offs."""
|
| 188 |
+
rng = np.random.default_rng(seed)
|
| 189 |
+
feat = rng.standard_normal(self.hidden_dim).astype(np.float32)
|
| 190 |
+
base_act = np.zeros(self.act_dim, dtype=np.float32)
|
| 191 |
+
|
| 192 |
+
efes = []
|
| 193 |
+
epistemic_ratios = []
|
| 194 |
+
for _ in range(trials):
|
| 195 |
+
act, diag = self.select_action(base_act, feat, num_candidates=8, ach_precision=1.2, dopamine_gain=1.5)
|
| 196 |
+
efes.append(diag["min_efe"])
|
| 197 |
+
denom = abs(diag["pragmatic_value"]) + abs(diag["epistemic_gain"]) + 1e-5
|
| 198 |
+
epistemic_ratios.append(abs(diag["epistemic_gain"]) / denom)
|
| 199 |
+
|
| 200 |
+
return {
|
| 201 |
+
"schema": "active-inference-free-energy-v1",
|
| 202 |
+
"status": "completed",
|
| 203 |
+
"trials": trials,
|
| 204 |
+
"mean_min_efe": round(float(np.mean(efes)), 4),
|
| 205 |
+
"mean_epistemic_ratio": round(float(np.mean(epistemic_ratios)), 4),
|
| 206 |
+
"exploration_entropy_mean": round(float(np.mean([diag["policy_entropy"]])), 4),
|
| 207 |
+
"action_norm": round(float(np.linalg.norm(act)), 4),
|
| 208 |
}
|
|
|
synapse/agent.py
CHANGED
|
@@ -121,6 +121,8 @@ class IntegratedAgent:
|
|
| 121 |
self.intervention_planner=None
|
| 122 |
self.relational_memory=None
|
| 123 |
self.event_backend=None
|
|
|
|
|
|
|
| 124 |
self._setup_frontier_extensions()
|
| 125 |
self.preparation_updates=0; self.bridge_updates=0; self.episodic=[]; self.tool_memory=[]
|
| 126 |
self._camera_consumed_any=False
|
|
@@ -209,6 +211,25 @@ class IntegratedAgent:
|
|
| 209 |
if getattr(self.config, 'event_driven_backend', False) and self.event_backend is None:
|
| 210 |
from .event_driven_backend import EventDrivenSparseBackend
|
| 211 |
self.event_backend = EventDrivenSparseBackend(delta_threshold=0.05)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
|
| 213 |
def _setup_neural_graft(self):
|
| 214 |
if not self.config.neural_graft:
|
|
@@ -387,6 +408,10 @@ class IntegratedAgent:
|
|
| 387 |
if getattr(self, 'holographic_vsa', None) is not None:
|
| 388 |
vsa_res, vsa_info = self.holographic_vsa.step(pooled, obs, learn=learn)
|
| 389 |
pooled = pooled + 0.03 * torch.tanh(vsa_res)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
# Dormant candidates participate only when novelty recruits one; unused weights persist.
|
| 391 |
novelty=float((r-previous).abs().mean()); preferred_expert=int(self.intent.argmax())%2
|
| 392 |
if getattr(self.config, 'dormant_lifecycle', True):
|
|
@@ -454,6 +479,14 @@ class IntegratedAgent:
|
|
| 454 |
graft_receipt=None
|
| 455 |
if self.neural_graft is not None:
|
| 456 |
executed,graft_receipt=self.neural_graft.propose(obs,self.last_features,executed,learn=learn)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 457 |
graft_act=executed.copy()
|
| 458 |
planner_info={'mode':mode,'enabled':mode!='off','horizon':self.config.experience_horizon,
|
| 459 |
'scope':'experimental short-horizon planning; heuristic uncertainty'}
|
|
@@ -526,6 +559,8 @@ class IntegratedAgent:
|
|
| 526 |
self.last_telemetry['verifiable_dpo']={'enabled':True,'pairs':len(self.dpo_engine.preference_buffer),'last_margin':getattr(self.dpo_engine,'last_margin',0.0)}
|
| 527 |
if getattr(self, 'comm_fabric', None) is not None:
|
| 528 |
self.last_telemetry['sancf'] = self.comm_fabric.get_telemetry_dict()
|
|
|
|
|
|
|
| 529 |
if getattr(self, 'overflow_mini_brain', None) is not None:
|
| 530 |
self.last_telemetry['overflow_mini_brain'] = {'enabled': True, 'state_norm': float(torch.norm(self.overflow_mini_brain.state).item())}
|
| 531 |
if getattr(self, 'event_backend', None) is not None:
|
|
@@ -622,6 +657,8 @@ class IntegratedAgent:
|
|
| 622 |
diagnostics['causal_world'] = self.causal_world.observe(f_t, a_t, next_f)
|
| 623 |
if getattr(self, 'relational_memory', None) is not None:
|
| 624 |
diagnostics['relational_memory'] = self.relational_memory.bind_step(state, executed, next_state)
|
|
|
|
|
|
|
| 625 |
self.pending_transition=None
|
| 626 |
return diagnostics
|
| 627 |
def feedback(self,reward,done=False):
|
|
@@ -776,6 +813,8 @@ class IntegratedAgent:
|
|
| 776 |
state['event_backend']=self.event_backend.export_state() if getattr(self,'event_backend',None) is not None else None
|
| 777 |
state['multitimescale']=self.multitimescale.export_state() if getattr(self,'multitimescale',None) is not None else None
|
| 778 |
state['relational_memory']=self.relational_memory.export_state() if getattr(self,'relational_memory',None) is not None else None
|
|
|
|
|
|
|
| 779 |
state['torch_rng']=torch.random.get_rng_state()
|
| 780 |
state['programmed_prior_status']=deepcopy(self.programmed_prior_status)
|
| 781 |
state['source_provenance']=deepcopy(self.source_provenance)
|
|
@@ -893,6 +932,10 @@ class IntegratedAgent:
|
|
| 893 |
self.multitimescale.import_state(state['multitimescale'])
|
| 894 |
if getattr(self,'relational_memory',None) is not None and state.get('relational_memory') is not None:
|
| 895 |
self.relational_memory.import_state(state['relational_memory'])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 896 |
if 'torch_rng' in state:
|
| 897 |
torch.random.set_rng_state(state['torch_rng'])
|
| 898 |
self.programmed_prior_status=deepcopy(state.get('programmed_prior_status',self.programmed_prior_status))
|
|
|
|
| 121 |
self.intervention_planner=None
|
| 122 |
self.relational_memory=None
|
| 123 |
self.event_backend=None
|
| 124 |
+
self.graph_diffusion=None
|
| 125 |
+
self.active_inference=None
|
| 126 |
self._setup_frontier_extensions()
|
| 127 |
self.preparation_updates=0; self.bridge_updates=0; self.episodic=[]; self.tool_memory=[]
|
| 128 |
self._camera_consumed_any=False
|
|
|
|
| 211 |
if getattr(self.config, 'event_driven_backend', False) and self.event_backend is None:
|
| 212 |
from .event_driven_backend import EventDrivenSparseBackend
|
| 213 |
self.event_backend = EventDrivenSparseBackend(delta_threshold=0.05)
|
| 214 |
+
if getattr(self.config, 'graph_diffusion', False) and self.graph_diffusion is None:
|
| 215 |
+
from .graph_diffusion import ChebyshevGraphDiffusion
|
| 216 |
+
self.graph_diffusion = ChebyshevGraphDiffusion(
|
| 217 |
+
num_nodes=self.brain.n,
|
| 218 |
+
pre=torch.as_tensor(self.graph.base_pre),
|
| 219 |
+
post=torch.as_tensor(self.graph.base_post),
|
| 220 |
+
weights=torch.as_tensor(self.brain.base_w),
|
| 221 |
+
order=4,
|
| 222 |
+
seed=self.config.seed,
|
| 223 |
+
)
|
| 224 |
+
self.modules['graph_diffusion'] = self.graph_diffusion
|
| 225 |
+
if getattr(self.config, 'active_inference_planner', False) and self.active_inference is None:
|
| 226 |
+
from .active_inference import ActiveInferenceEngine
|
| 227 |
+
self.active_inference = ActiveInferenceEngine(
|
| 228 |
+
observation_dim=155,
|
| 229 |
+
action_dim=5,
|
| 230 |
+
hidden_dim=32,
|
| 231 |
+
seed=self.config.seed,
|
| 232 |
+
)
|
| 233 |
|
| 234 |
def _setup_neural_graft(self):
|
| 235 |
if not self.config.neural_graft:
|
|
|
|
| 408 |
if getattr(self, 'holographic_vsa', None) is not None:
|
| 409 |
vsa_res, vsa_info = self.holographic_vsa.step(pooled, obs, learn=learn)
|
| 410 |
pooled = pooled + 0.03 * torch.tanh(vsa_res)
|
| 411 |
+
graph_diff_info = None
|
| 412 |
+
if getattr(self, 'graph_diffusion', None) is not None:
|
| 413 |
+
gdiff_res, graph_diff_info = self.graph_diffusion(r, learn=learn)
|
| 414 |
+
pooled = pooled + 0.04 * torch.tanh(gdiff_res[:32])
|
| 415 |
# Dormant candidates participate only when novelty recruits one; unused weights persist.
|
| 416 |
novelty=float((r-previous).abs().mean()); preferred_expert=int(self.intent.argmax())%2
|
| 417 |
if getattr(self.config, 'dormant_lifecycle', True):
|
|
|
|
| 479 |
graft_receipt=None
|
| 480 |
if self.neural_graft is not None:
|
| 481 |
executed,graft_receipt=self.neural_graft.propose(obs,self.last_features,executed,learn=learn)
|
| 482 |
+
if getattr(self, 'active_inference', None) is not None:
|
| 483 |
+
if getattr(self.config, 'planner_algorithm', '') == 'active_inference':
|
| 484 |
+
executed, act_inf_info = self.active_inference.select_action(executed, self.last_features)
|
| 485 |
+
else:
|
| 486 |
+
_, act_inf_info = self.active_inference.evaluate_expected_free_energy(
|
| 487 |
+
np.array([executed]), self.last_features
|
| 488 |
+
)
|
| 489 |
+
self.last_telemetry['active_inference'] = act_inf_info
|
| 490 |
graft_act=executed.copy()
|
| 491 |
planner_info={'mode':mode,'enabled':mode!='off','horizon':self.config.experience_horizon,
|
| 492 |
'scope':'experimental short-horizon planning; heuristic uncertainty'}
|
|
|
|
| 559 |
self.last_telemetry['verifiable_dpo']={'enabled':True,'pairs':len(self.dpo_engine.preference_buffer),'last_margin':getattr(self.dpo_engine,'last_margin',0.0)}
|
| 560 |
if getattr(self, 'comm_fabric', None) is not None:
|
| 561 |
self.last_telemetry['sancf'] = self.comm_fabric.get_telemetry_dict()
|
| 562 |
+
if getattr(self, 'graph_diffusion', None) is not None and graph_diff_info is not None:
|
| 563 |
+
self.last_telemetry['graph_diffusion'] = graph_diff_info
|
| 564 |
if getattr(self, 'overflow_mini_brain', None) is not None:
|
| 565 |
self.last_telemetry['overflow_mini_brain'] = {'enabled': True, 'state_norm': float(torch.norm(self.overflow_mini_brain.state).item())}
|
| 566 |
if getattr(self, 'event_backend', None) is not None:
|
|
|
|
| 657 |
diagnostics['causal_world'] = self.causal_world.observe(f_t, a_t, next_f)
|
| 658 |
if getattr(self, 'relational_memory', None) is not None:
|
| 659 |
diagnostics['relational_memory'] = self.relational_memory.bind_step(state, executed, next_state)
|
| 660 |
+
if getattr(self, 'active_inference', None) is not None:
|
| 661 |
+
self.active_inference.update_beliefs(float(abs(reward)))
|
| 662 |
self.pending_transition=None
|
| 663 |
return diagnostics
|
| 664 |
def feedback(self,reward,done=False):
|
|
|
|
| 813 |
state['event_backend']=self.event_backend.export_state() if getattr(self,'event_backend',None) is not None else None
|
| 814 |
state['multitimescale']=self.multitimescale.export_state() if getattr(self,'multitimescale',None) is not None else None
|
| 815 |
state['relational_memory']=self.relational_memory.export_state() if getattr(self,'relational_memory',None) is not None else None
|
| 816 |
+
state['graph_diffusion']=self.graph_diffusion.export_state() if getattr(self,'graph_diffusion',None) is not None else None
|
| 817 |
+
state['active_inference']=self.active_inference.export_state() if getattr(self,'active_inference',None) is not None else None
|
| 818 |
state['torch_rng']=torch.random.get_rng_state()
|
| 819 |
state['programmed_prior_status']=deepcopy(self.programmed_prior_status)
|
| 820 |
state['source_provenance']=deepcopy(self.source_provenance)
|
|
|
|
| 932 |
self.multitimescale.import_state(state['multitimescale'])
|
| 933 |
if getattr(self,'relational_memory',None) is not None and state.get('relational_memory') is not None:
|
| 934 |
self.relational_memory.import_state(state['relational_memory'])
|
| 935 |
+
if getattr(self,'graph_diffusion',None) is not None and state.get('graph_diffusion') is not None:
|
| 936 |
+
self.graph_diffusion.import_state(state['graph_diffusion'])
|
| 937 |
+
if getattr(self,'active_inference',None) is not None and state.get('active_inference') is not None:
|
| 938 |
+
self.active_inference.import_state(state['active_inference'])
|
| 939 |
if 'torch_rng' in state:
|
| 940 |
torch.random.set_rng_state(state['torch_rng'])
|
| 941 |
self.programmed_prior_status=deepcopy(state.get('programmed_prior_status',self.programmed_prior_status))
|
synapse/causal_discovery.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Directional Causal Graph Discovery & Pearlian Path Tracing Engine.
|
| 2 |
+
|
| 3 |
+
Grounding:
|
| 4 |
+
Integrates structural equation discovery (DirectLiNGAM, Shimizu et al., 2006) and
|
| 5 |
+
Pearlian interventional path tracing (Pearl 2009; Peters et al. 2017) over
|
| 6 |
+
multi-connectome regional activity streams.
|
| 7 |
+
|
| 8 |
+
Key Capabilities:
|
| 9 |
+
1. DirectLiNGAM Causal Ordering: Identifies acyclic causal hierarchies between
|
| 10 |
+
connectome neuropils by iteratively selecting exogenous variables whose residuals
|
| 11 |
+
are maximally independent.
|
| 12 |
+
2. Causal Path Tracing: Traces the most influential causal pathways from sensory
|
| 13 |
+
neuropils (Optic Lobe / Antenna) through central neuropils (Central Complex /
|
| 14 |
+
Mushroom Body) to motor output neuropils (MANC).
|
| 15 |
+
3. Interventional Transmission Strength: Measures product of path coefficients
|
| 16 |
+
to determine signal throughput under hypothetical counterfactual interventions.
|
| 17 |
+
"""
|
| 18 |
+
from __future__ import annotations
|
| 19 |
+
import math
|
| 20 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 21 |
+
import numpy as np
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class CausalPathTracer:
|
| 25 |
+
"""Discovers directional causal DAGs and traces interventional pathways."""
|
| 26 |
+
|
| 27 |
+
def __init__(self, region_names: Optional[List[str]] = None, seed: int = 42):
|
| 28 |
+
self.region_names = region_names or [
|
| 29 |
+
"optic-lobe", "antennal_lobe", "mushroom_body", "central_complex", "flywire_central", "manc_vnc"
|
| 30 |
+
]
|
| 31 |
+
self.num_regions = len(self.region_names)
|
| 32 |
+
self.seed = int(seed)
|
| 33 |
+
self.rng = np.random.default_rng(self.seed)
|
| 34 |
+
|
| 35 |
+
def fit_direct_lingam(self, time_series_data: np.ndarray) -> Tuple[np.ndarray, List[int]]:
|
| 36 |
+
"""Fits DirectLiNGAM to discover causal DAG adjacency matrix B where X = B X + e.
|
| 37 |
+
|
| 38 |
+
Args:
|
| 39 |
+
time_series_data: array of shape (T, num_regions)
|
| 40 |
+
|
| 41 |
+
Returns:
|
| 42 |
+
(causal_adjacency_matrix, causal_topological_order)
|
| 43 |
+
"""
|
| 44 |
+
X = np.asarray(time_series_data, dtype=np.float64)
|
| 45 |
+
T, p = X.shape
|
| 46 |
+
if p != self.num_regions:
|
| 47 |
+
# Adjust to matching dimensions
|
| 48 |
+
p = min(p, self.num_regions)
|
| 49 |
+
X = X[:, :p]
|
| 50 |
+
|
| 51 |
+
# Standardize columns
|
| 52 |
+
X_centered = X - np.mean(X, axis=0, keepdims=True)
|
| 53 |
+
stds = np.std(X_centered, axis=0, keepdims=True)
|
| 54 |
+
X_norm = X_centered / np.maximum(stds, 1e-6)
|
| 55 |
+
|
| 56 |
+
remaining_indices = list(range(p))
|
| 57 |
+
causal_order = []
|
| 58 |
+
B = np.zeros((p, p), dtype=np.float32)
|
| 59 |
+
|
| 60 |
+
# Iterative selection of most exogenous variable
|
| 61 |
+
for _ in range(p):
|
| 62 |
+
best_idx = remaining_indices[0]
|
| 63 |
+
best_score = float("inf")
|
| 64 |
+
|
| 65 |
+
for cand in remaining_indices:
|
| 66 |
+
# Regress cand out of all other remaining variables and test non-Gaussianity
|
| 67 |
+
score = 0.0
|
| 68 |
+
x_cand = X_norm[:, cand]
|
| 69 |
+
for other in remaining_indices:
|
| 70 |
+
if other == cand:
|
| 71 |
+
continue
|
| 72 |
+
x_other = X_norm[:, other]
|
| 73 |
+
# Simple linear regression coefficient
|
| 74 |
+
slope = np.cov(x_other, x_cand)[0, 1] / max(1e-6, np.var(x_cand))
|
| 75 |
+
residual = x_other - slope * x_cand
|
| 76 |
+
# Kurtosis departure from Gaussianity as independence proxy
|
| 77 |
+
kurt = float(np.mean(residual ** 4) / max(1e-6, np.mean(residual ** 2) ** 2)) - 3.0
|
| 78 |
+
score += abs(kurt)
|
| 79 |
+
|
| 80 |
+
if score < best_score:
|
| 81 |
+
best_score = score
|
| 82 |
+
best_idx = cand
|
| 83 |
+
|
| 84 |
+
causal_order.append(best_idx)
|
| 85 |
+
remaining_indices.remove(best_idx)
|
| 86 |
+
|
| 87 |
+
# Regress each variable on all its causal predecessors to form B
|
| 88 |
+
for i, target in enumerate(causal_order):
|
| 89 |
+
predecessors = causal_order[:i]
|
| 90 |
+
if not predecessors:
|
| 91 |
+
continue
|
| 92 |
+
X_pred = X_norm[:, predecessors]
|
| 93 |
+
y = X_norm[:, target]
|
| 94 |
+
try:
|
| 95 |
+
# Ridge regression
|
| 96 |
+
reg = np.linalg.lstsq(
|
| 97 |
+
X_pred.T @ X_pred + 1e-4 * np.eye(len(predecessors)),
|
| 98 |
+
X_pred.T @ y,
|
| 99 |
+
rcond=None,
|
| 100 |
+
)[0]
|
| 101 |
+
for p_idx, coef in zip(predecessors, reg):
|
| 102 |
+
B[target, p_idx] = float(np.clip(coef, -2.0, 2.0))
|
| 103 |
+
except Exception:
|
| 104 |
+
pass
|
| 105 |
+
|
| 106 |
+
return B, causal_order
|
| 107 |
+
|
| 108 |
+
def trace_causal_paths(
|
| 109 |
+
self,
|
| 110 |
+
causal_matrix: np.ndarray,
|
| 111 |
+
source_idx: int = 0,
|
| 112 |
+
target_idx: Optional[int] = None,
|
| 113 |
+
max_depth: int = 4,
|
| 114 |
+
) -> List[Dict[str, Any]]:
|
| 115 |
+
"""Traces top causal pathways from source_idx to target_idx."""
|
| 116 |
+
p = len(causal_matrix)
|
| 117 |
+
target = target_idx if target_idx is not None else (p - 1)
|
| 118 |
+
|
| 119 |
+
paths = []
|
| 120 |
+
|
| 121 |
+
def dfs(current: int, current_path: List[int], current_gain: float, depth: int):
|
| 122 |
+
if current == target and len(current_path) > 1:
|
| 123 |
+
names = [self.region_names[i] if i < len(self.region_names) else f"node_{i}" for i in current_path]
|
| 124 |
+
paths.append({
|
| 125 |
+
"path_indices": current_path,
|
| 126 |
+
"path_names": names,
|
| 127 |
+
"path_length": len(current_path) - 1,
|
| 128 |
+
"causal_transmission_gain": round(float(current_gain), 4),
|
| 129 |
+
})
|
| 130 |
+
return
|
| 131 |
+
if depth >= max_depth:
|
| 132 |
+
return
|
| 133 |
+
|
| 134 |
+
# Follow outgoing edges (where target has non-zero incoming weight from current)
|
| 135 |
+
for next_node in range(p):
|
| 136 |
+
edge_weight = causal_matrix[next_node, current]
|
| 137 |
+
if abs(edge_weight) > 0.05 and next_node not in current_path:
|
| 138 |
+
dfs(next_node, current_path + [next_node], current_gain * edge_weight, depth + 1)
|
| 139 |
+
|
| 140 |
+
dfs(source_idx, [source_idx], 1.0, 0)
|
| 141 |
+
# Sort paths by absolute causal transmission gain
|
| 142 |
+
paths.sort(key=lambda x: abs(x["causal_transmission_gain"]), reverse=True)
|
| 143 |
+
return paths[:5]
|
| 144 |
+
|
| 145 |
+
def run_causal_path_discovery_assay(
|
| 146 |
+
self,
|
| 147 |
+
time_series_data: Optional[np.ndarray] = None,
|
| 148 |
+
steps: int = 64,
|
| 149 |
+
seed: int = 42,
|
| 150 |
+
) -> Dict[str, Any]:
|
| 151 |
+
"""Runs complete DirectLiNGAM and causal path tracing assay."""
|
| 152 |
+
if time_series_data is None:
|
| 153 |
+
rng = np.random.default_rng(seed)
|
| 154 |
+
# Generate synthetic 6-region cascading signal
|
| 155 |
+
T = steps
|
| 156 |
+
X = np.zeros((T, self.num_regions), dtype=np.float32)
|
| 157 |
+
# Exogenous source drive
|
| 158 |
+
X[:, 0] = rng.standard_normal(T)
|
| 159 |
+
for t in range(T):
|
| 160 |
+
for j in range(1, self.num_regions):
|
| 161 |
+
prev = X[t, j - 1] if t > 0 else 0.0
|
| 162 |
+
X[t, j] = 0.65 * prev + 0.2 * rng.standard_normal()
|
| 163 |
+
time_series_data = X
|
| 164 |
+
|
| 165 |
+
B, order = self.fit_direct_lingam(time_series_data)
|
| 166 |
+
top_paths = self.trace_causal_paths(B, source_idx=0, target_idx=min(len(B) - 1, self.num_regions - 1))
|
| 167 |
+
|
| 168 |
+
order_names = [self.region_names[i] if i < len(self.region_names) else f"node_{i}" for i in order]
|
| 169 |
+
|
| 170 |
+
return {
|
| 171 |
+
"schema": "direct-lingam-causal-path-discovery-v1",
|
| 172 |
+
"status": "completed",
|
| 173 |
+
"num_regions": self.num_regions,
|
| 174 |
+
"causal_topological_order": order_names,
|
| 175 |
+
"num_discovered_paths": len(top_paths),
|
| 176 |
+
"top_pathways": top_paths,
|
| 177 |
+
"max_causal_path_gain": top_paths[0]["causal_transmission_gain"] if top_paths else 0.0,
|
| 178 |
+
"is_strictly_acyclic": True,
|
| 179 |
+
}
|
synapse/cli.py
CHANGED
|
@@ -16,7 +16,7 @@ def main(argv=None):
|
|
| 16 |
parser.add_argument('--connectome-mode',choices=['proxy','prefer_authentic','strict_authentic'],help='connectome source mode')
|
| 17 |
size=parser.add_mutually_exclusive_group()
|
| 18 |
size.add_argument('--nodes',type=int,help='legacy neurons per source')
|
| 19 |
-
size.add_argument('--total-neurons',type=int,help='exact total live actor neurons (default:
|
| 20 |
parser.add_argument('--task',default='forage')
|
| 21 |
sub=parser.add_subparsers(dest='command',required=True)
|
| 22 |
sub.add_parser('status')
|
|
@@ -73,6 +73,7 @@ def main(argv=None):
|
|
| 73 |
sub.add_parser('meta-plasticity')
|
| 74 |
sub.add_parser('ablation')
|
| 75 |
sub.add_parser('checkpoint-migration')
|
|
|
|
| 76 |
p=sub.add_parser('omnibus'); p.add_argument('--steps-per-task',type=int,default=30); p.add_argument('--tasks',nargs='+',default=None); p.add_argument('--save-report',type=Path)
|
| 77 |
p=sub.add_parser('evidence'); p.add_argument('--save-report',type=Path)
|
| 78 |
p.add_argument('--receipt',type=Path,help='read and verify an existing measured adaptation receipt')
|
|
@@ -95,7 +96,7 @@ def main(argv=None):
|
|
| 95 |
p=sub.add_parser('planning-verify'); p.add_argument('path',type=Path)
|
| 96 |
args=parser.parse_args(argv)
|
| 97 |
try:
|
| 98 |
-
total=args.total_neurons if args.total_neurons is not None else (
|
| 99 |
if getattr(args, 'frontier_all', False):
|
| 100 |
config=SynapseConfig.frontier_apex(total_neurons=total,seed=args.seed,task=args.task)
|
| 101 |
else:
|
|
@@ -453,6 +454,11 @@ def main(argv=None):
|
|
| 453 |
dummy_state = {'config': {'seed': args.seed}, 'activations': [0.1, 0.2, 0.3], 'step_count': 100}
|
| 454 |
res = mig.simulate_migration(dummy_state)
|
| 455 |
result = dataclasses.asdict(res)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 456 |
if args.command in ('world-model-cem','dpo-align','unified-eval','coconut-think','multistep-plan','rsi-verify'):
|
| 457 |
result={**result,'measured':False,'evidence_kind':'illustrative',
|
| 458 |
'scope':'standalone synthetic inputs; no measured embodied performance; use planning-study or evidence for actual world trials'}
|
|
|
|
| 16 |
parser.add_argument('--connectome-mode',choices=['proxy','prefer_authentic','strict_authentic'],help='connectome source mode')
|
| 17 |
size=parser.add_mutually_exclusive_group()
|
| 18 |
size.add_argument('--nodes',type=int,help='legacy neurons per source')
|
| 19 |
+
size.add_argument('--total-neurons',type=int,help='exact total live actor neurons (default: 6500)')
|
| 20 |
parser.add_argument('--task',default='forage')
|
| 21 |
sub=parser.add_subparsers(dest='command',required=True)
|
| 22 |
sub.add_parser('status')
|
|
|
|
| 73 |
sub.add_parser('meta-plasticity')
|
| 74 |
sub.add_parser('ablation')
|
| 75 |
sub.add_parser('checkpoint-migration')
|
| 76 |
+
sub.add_parser('frontier-innovations')
|
| 77 |
p=sub.add_parser('omnibus'); p.add_argument('--steps-per-task',type=int,default=30); p.add_argument('--tasks',nargs='+',default=None); p.add_argument('--save-report',type=Path)
|
| 78 |
p=sub.add_parser('evidence'); p.add_argument('--save-report',type=Path)
|
| 79 |
p.add_argument('--receipt',type=Path,help='read and verify an existing measured adaptation receipt')
|
|
|
|
| 96 |
p=sub.add_parser('planning-verify'); p.add_argument('path',type=Path)
|
| 97 |
args=parser.parse_args(argv)
|
| 98 |
try:
|
| 99 |
+
total=args.total_neurons if args.total_neurons is not None else (6500 if args.nodes is None else None)
|
| 100 |
if getattr(args, 'frontier_all', False):
|
| 101 |
config=SynapseConfig.frontier_apex(total_neurons=total,seed=args.seed,task=args.task)
|
| 102 |
else:
|
|
|
|
| 454 |
dummy_state = {'config': {'seed': args.seed}, 'activations': [0.1, 0.2, 0.3], 'step_count': 100}
|
| 455 |
res = mig.simulate_migration(dummy_state)
|
| 456 |
result = dataclasses.asdict(res)
|
| 457 |
+
elif args.command=='frontier-innovations':
|
| 458 |
+
from .runtime import LabRuntime
|
| 459 |
+
from .tandem_max import run_frontier_innovations_assay
|
| 460 |
+
rt = LabRuntime(config)
|
| 461 |
+
result = run_frontier_innovations_assay(rt, seed=args.seed)
|
| 462 |
if args.command in ('world-model-cem','dpo-align','unified-eval','coconut-think','multistep-plan','rsi-verify'):
|
| 463 |
result={**result,'measured':False,'evidence_kind':'illustrative',
|
| 464 |
'scope':'standalone synthetic inputs; no measured embodied performance; use planning-study or evidence for actual world trials'}
|
synapse/config.py
CHANGED
|
@@ -68,6 +68,8 @@ class SynapseConfig:
|
|
| 68 |
relational_memory: bool = False
|
| 69 |
verifier_rsi: bool = False
|
| 70 |
event_driven_backend: bool = False
|
|
|
|
|
|
|
| 71 |
|
| 72 |
def __post_init__(self):
|
| 73 |
bool_keys = (
|
|
@@ -80,7 +82,7 @@ class SynapseConfig:
|
|
| 80 |
'neural_comm_enabled', 'neurowave_enabled', 'overflow_mini_brain',
|
| 81 |
'grafted_diffusion', 'selective_state_space', 'multitimescale_learning',
|
| 82 |
'meta_plasticity', 'causal_world_model', 'relational_memory',
|
| 83 |
-
'verifier_rsi', 'event_driven_backend'
|
| 84 |
)
|
| 85 |
for key in bool_keys:
|
| 86 |
if type(getattr(self, key)) is not bool:
|
|
@@ -155,7 +157,7 @@ class SynapseConfig:
|
|
| 155 |
return int(sum(self.source_node_counts()))
|
| 156 |
|
| 157 |
@classmethod
|
| 158 |
-
def frontier_apex(cls, total_neurons: int =
|
| 159 |
"""Omnibus configuration activating all frontier cognitive, neuromorphic, and SANCF communication features."""
|
| 160 |
cfg_dict = dict(
|
| 161 |
seed=seed,
|
|
@@ -197,6 +199,8 @@ class SynapseConfig:
|
|
| 197 |
relational_memory=True,
|
| 198 |
verifier_rsi=True,
|
| 199 |
event_driven_backend=True,
|
|
|
|
|
|
|
| 200 |
compute_threads=min(4, os.cpu_count() or 1),
|
| 201 |
connectome_mode='prefer_authentic',
|
| 202 |
)
|
|
@@ -204,7 +208,7 @@ class SynapseConfig:
|
|
| 204 |
return cls(**cfg_dict)
|
| 205 |
|
| 206 |
@classmethod
|
| 207 |
-
def max_tandem(cls, total_neurons: int =
|
| 208 |
"""Maximum tandem configuration activating every optional feature, hybrid planning, SANCF, and full CPU compute."""
|
| 209 |
cfg_dict = dict(
|
| 210 |
seed=seed,
|
|
@@ -248,6 +252,8 @@ class SynapseConfig:
|
|
| 248 |
relational_memory=True,
|
| 249 |
verifier_rsi=True,
|
| 250 |
event_driven_backend=True,
|
|
|
|
|
|
|
| 251 |
compute_threads=min(32, max(1, os.cpu_count() or 4)),
|
| 252 |
graft_batch_size=256,
|
| 253 |
graft_updates=4,
|
|
@@ -287,7 +293,7 @@ class SynapseConfig:
|
|
| 287 |
'neural_comm_latency_steps': 0, 'neurowave_enabled': False,
|
| 288 |
'overflow_mini_brain': False, 'grafted_diffusion': False, 'selective_state_space': False,
|
| 289 |
'multitimescale_learning': False, 'meta_plasticity': False, 'causal_world_model': False,
|
| 290 |
-
'relational_memory': False, 'verifier_rsi': False, 'event_driven_backend': False,
|
| 291 |
'connectome_sources': ('flywire', 'manc', 'optic-lobe'),
|
| 292 |
**saved
|
| 293 |
}
|
|
|
|
| 68 |
relational_memory: bool = False
|
| 69 |
verifier_rsi: bool = False
|
| 70 |
event_driven_backend: bool = False
|
| 71 |
+
graph_diffusion: bool = False
|
| 72 |
+
active_inference_planner: bool = False
|
| 73 |
|
| 74 |
def __post_init__(self):
|
| 75 |
bool_keys = (
|
|
|
|
| 82 |
'neural_comm_enabled', 'neurowave_enabled', 'overflow_mini_brain',
|
| 83 |
'grafted_diffusion', 'selective_state_space', 'multitimescale_learning',
|
| 84 |
'meta_plasticity', 'causal_world_model', 'relational_memory',
|
| 85 |
+
'verifier_rsi', 'event_driven_backend', 'graph_diffusion', 'active_inference_planner'
|
| 86 |
)
|
| 87 |
for key in bool_keys:
|
| 88 |
if type(getattr(self, key)) is not bool:
|
|
|
|
| 157 |
return int(sum(self.source_node_counts()))
|
| 158 |
|
| 159 |
@classmethod
|
| 160 |
+
def frontier_apex(cls, total_neurons: int = 6500, seed: int = 7, task: str = 'forage', **kwargs) -> 'SynapseConfig':
|
| 161 |
"""Omnibus configuration activating all frontier cognitive, neuromorphic, and SANCF communication features."""
|
| 162 |
cfg_dict = dict(
|
| 163 |
seed=seed,
|
|
|
|
| 199 |
relational_memory=True,
|
| 200 |
verifier_rsi=True,
|
| 201 |
event_driven_backend=True,
|
| 202 |
+
graph_diffusion=True,
|
| 203 |
+
active_inference_planner=True,
|
| 204 |
compute_threads=min(4, os.cpu_count() or 1),
|
| 205 |
connectome_mode='prefer_authentic',
|
| 206 |
)
|
|
|
|
| 208 |
return cls(**cfg_dict)
|
| 209 |
|
| 210 |
@classmethod
|
| 211 |
+
def max_tandem(cls, total_neurons: int = 6500, seed: int = 7, task: str = 'continental_biosphere', **kwargs) -> 'SynapseConfig':
|
| 212 |
"""Maximum tandem configuration activating every optional feature, hybrid planning, SANCF, and full CPU compute."""
|
| 213 |
cfg_dict = dict(
|
| 214 |
seed=seed,
|
|
|
|
| 252 |
relational_memory=True,
|
| 253 |
verifier_rsi=True,
|
| 254 |
event_driven_backend=True,
|
| 255 |
+
graph_diffusion=True,
|
| 256 |
+
active_inference_planner=True,
|
| 257 |
compute_threads=min(32, max(1, os.cpu_count() or 4)),
|
| 258 |
graft_batch_size=256,
|
| 259 |
graft_updates=4,
|
|
|
|
| 293 |
'neural_comm_latency_steps': 0, 'neurowave_enabled': False,
|
| 294 |
'overflow_mini_brain': False, 'grafted_diffusion': False, 'selective_state_space': False,
|
| 295 |
'multitimescale_learning': False, 'meta_plasticity': False, 'causal_world_model': False,
|
| 296 |
+
'relational_memory': False, 'verifier_rsi': False, 'event_driven_backend': False, 'graph_diffusion': False, 'active_inference_planner': False,
|
| 297 |
'connectome_sources': ('flywire', 'manc', 'optic-lobe'),
|
| 298 |
**saved
|
| 299 |
}
|
synapse/dendritic_ltc.py
CHANGED
|
@@ -96,8 +96,11 @@ class DendriticLiquidSubGraft(nn.Module):
|
|
| 96 |
i_basal = self.basal_proj(b)
|
| 97 |
i_apical = self.apical_proj(a)
|
| 98 |
nmda_gate = torch.sigmoid(i_apical)
|
| 99 |
-
#
|
| 100 |
-
|
|
|
|
|
|
|
|
|
|
| 101 |
|
| 102 |
decay = torch.exp(-self.dt / tau.clamp(min=1e-3))
|
| 103 |
next_v = decay * self.voltage + (1.0 - decay) * equilibrium_drive
|
|
@@ -111,12 +114,14 @@ class DendriticLiquidSubGraft(nn.Module):
|
|
| 111 |
|
| 112 |
delta = self.readout(next_v)
|
| 113 |
nmda_plateau = float((nmda_gate > 0.65).float().mean().item())
|
|
|
|
| 114 |
return delta, {
|
| 115 |
"enabled": True,
|
| 116 |
"mean_tau": round(float(tau.mean().item()), 4),
|
| 117 |
"min_tau": round(float(tau.min().item()), 4),
|
| 118 |
"max_tau": round(float(tau.max().item()), 4),
|
| 119 |
"nmda_plateau_fraction": round(nmda_plateau, 4),
|
|
|
|
| 120 |
"voltage_norm": round(float(next_v.norm().item()), 4),
|
| 121 |
"readout_norm": round(float(self.readout.weight.norm().item()), 5),
|
| 122 |
"updates": int(self.updates),
|
|
|
|
| 96 |
i_basal = self.basal_proj(b)
|
| 97 |
i_apical = self.apical_proj(a)
|
| 98 |
nmda_gate = torch.sigmoid(i_apical)
|
| 99 |
+
# Supralinear dendritic calcium/sodium spike (dSpike) initiation (Gidon et al. Science 2020)
|
| 100 |
+
d_spike_active = torch.relu(i_apical - 0.70) ** 2
|
| 101 |
+
d_spike_gain = 0.35 * torch.tanh(d_spike_active)
|
| 102 |
+
# Nonlinear multi-branch apical plateau potential amplification
|
| 103 |
+
equilibrium_drive = torch.tanh(i_basal * (1.0 + 0.65 * nmda_gate + d_spike_gain))
|
| 104 |
|
| 105 |
decay = torch.exp(-self.dt / tau.clamp(min=1e-3))
|
| 106 |
next_v = decay * self.voltage + (1.0 - decay) * equilibrium_drive
|
|
|
|
| 114 |
|
| 115 |
delta = self.readout(next_v)
|
| 116 |
nmda_plateau = float((nmda_gate > 0.65).float().mean().item())
|
| 117 |
+
d_spike_rate = float((d_spike_active > 0.1).float().mean().item())
|
| 118 |
return delta, {
|
| 119 |
"enabled": True,
|
| 120 |
"mean_tau": round(float(tau.mean().item()), 4),
|
| 121 |
"min_tau": round(float(tau.min().item()), 4),
|
| 122 |
"max_tau": round(float(tau.max().item()), 4),
|
| 123 |
"nmda_plateau_fraction": round(nmda_plateau, 4),
|
| 124 |
+
"d_spike_rate": round(d_spike_rate, 4),
|
| 125 |
"voltage_norm": round(float(next_v.norm().item()), 4),
|
| 126 |
"readout_norm": round(float(self.readout.weight.norm().item()), 5),
|
| 127 |
"updates": int(self.updates),
|
synapse/graph_diffusion.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Chebyshev Spectral Graph Diffusion Engine for Scaled Connectomes.
|
| 2 |
+
|
| 3 |
+
Grounding:
|
| 4 |
+
In a 6,500-neuron connectome spanning central brain (FlyWire), motor ventral nerve
|
| 5 |
+
cord (MANC), and visual neuropil (Optic-Lobe), signal transport between sensory and
|
| 6 |
+
motor neuropils requires multi-hop topological propagation.
|
| 7 |
+
|
| 8 |
+
Chebyshev polynomial spectral graph filtering (Defferrard et al., NeurIPS 2016;
|
| 9 |
+
Chamberlain et al., GRAND Graph Neural Diffusion 2021) evaluates multi-scale heat
|
| 10 |
+
kernel diffusion directly in O(E) time:
|
| 11 |
+
L = I - D^{-1/2} A D^{-1/2}
|
| 12 |
+
L_tilde = (2 / lambda_max) * L - I, lambda_max ~ 2.0
|
| 13 |
+
T_0(L_tilde) x = x
|
| 14 |
+
T_1(L_tilde) x = L_tilde x
|
| 15 |
+
T_k(L_tilde) x = 2 * L_tilde * T_{k-1}(L_tilde) x - T_{k-2}(L_tilde) x
|
| 16 |
+
x_diff = sum_{k=0}^K alpha_k * T_k(L_tilde) x
|
| 17 |
+
|
| 18 |
+
The module features a zero-initialized residual projection, strictly preserving
|
| 19 |
+
backward compatibility and frozen baseline invariance when untrained or disabled.
|
| 20 |
+
"""
|
| 21 |
+
from __future__ import annotations
|
| 22 |
+
import math
|
| 23 |
+
import time
|
| 24 |
+
from copy import deepcopy
|
| 25 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 26 |
+
import numpy as np
|
| 27 |
+
import torch
|
| 28 |
+
from torch import nn
|
| 29 |
+
import torch.nn.functional as F
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class ChebyshevGraphDiffusion(nn.Module):
|
| 33 |
+
"""Multi-scale Chebyshev spectral graph diffusion over connectome topologies."""
|
| 34 |
+
|
| 35 |
+
def __init__(
|
| 36 |
+
self,
|
| 37 |
+
num_nodes: int = 6500,
|
| 38 |
+
pre: Optional[torch.Tensor] = None,
|
| 39 |
+
post: Optional[torch.Tensor] = None,
|
| 40 |
+
weights: Optional[torch.Tensor] = None,
|
| 41 |
+
order: int = 4,
|
| 42 |
+
diffusion_coeff: float = 0.25,
|
| 43 |
+
seed: int = 42,
|
| 44 |
+
):
|
| 45 |
+
super().__init__()
|
| 46 |
+
self.num_nodes = int(num_nodes)
|
| 47 |
+
self.order = int(order)
|
| 48 |
+
self.diffusion_coeff = float(diffusion_coeff)
|
| 49 |
+
self.seed = int(seed)
|
| 50 |
+
|
| 51 |
+
# Build sparse normalized Laplacian
|
| 52 |
+
self._build_laplacian(pre, post, weights)
|
| 53 |
+
|
| 54 |
+
with torch.random.fork_rng(devices=[]):
|
| 55 |
+
torch.manual_seed(self.seed + 7719)
|
| 56 |
+
# Chebyshev spectral filter weights (order + 1 parameters)
|
| 57 |
+
self.filter_weights = nn.Parameter(torch.zeros(self.order + 1))
|
| 58 |
+
# Heat kernel decaying initial filter coefficients
|
| 59 |
+
with torch.no_grad():
|
| 60 |
+
for k in range(self.order + 1):
|
| 61 |
+
self.filter_weights[k] = math.exp(-0.5 * k) * (0.1 if k > 0 else 1.0)
|
| 62 |
+
# Zero-initialized projection back to residual current
|
| 63 |
+
self.readout_gate = nn.Parameter(torch.zeros(1))
|
| 64 |
+
|
| 65 |
+
self.register_buffer("last_diffusion_energy", torch.zeros(1))
|
| 66 |
+
self.register_buffer("last_dispersion", torch.zeros(1))
|
| 67 |
+
self.updates = 0
|
| 68 |
+
|
| 69 |
+
def _build_laplacian(
|
| 70 |
+
self,
|
| 71 |
+
pre: Optional[torch.Tensor],
|
| 72 |
+
post: Optional[torch.Tensor],
|
| 73 |
+
weights: Optional[torch.Tensor],
|
| 74 |
+
) -> None:
|
| 75 |
+
"""Constructs rescaled normalized Laplacian L_tilde as a sparse tensor."""
|
| 76 |
+
n = self.num_nodes
|
| 77 |
+
if pre is None or post is None or pre.numel() == 0:
|
| 78 |
+
# Fallback identity laplacian
|
| 79 |
+
indices = torch.arange(n, dtype=torch.long)
|
| 80 |
+
i_indices = torch.stack([indices, indices], dim=0)
|
| 81 |
+
self.register_buffer("laplacian_indices", i_indices)
|
| 82 |
+
self.register_buffer("laplacian_values", torch.zeros(n, dtype=torch.float32))
|
| 83 |
+
return
|
| 84 |
+
|
| 85 |
+
pre_t = pre.long()
|
| 86 |
+
post_t = post.long()
|
| 87 |
+
if weights is not None:
|
| 88 |
+
w_t = weights.float().abs()
|
| 89 |
+
else:
|
| 90 |
+
w_t = torch.ones(len(pre_t), dtype=torch.float32)
|
| 91 |
+
|
| 92 |
+
# Compute degrees (in-degrees + out-degrees)
|
| 93 |
+
deg = torch.zeros(n, dtype=torch.float32)
|
| 94 |
+
deg.scatter_add_(0, post_t, w_t)
|
| 95 |
+
deg.scatter_add_(0, pre_t, w_t)
|
| 96 |
+
deg_inv_sqrt = torch.pow(deg.clamp(min=1e-5), -0.5)
|
| 97 |
+
|
| 98 |
+
# Normalized adjacency: A_norm = D^{-1/2} A D^{-1/2}
|
| 99 |
+
norm_w = deg_inv_sqrt[pre_t] * w_t * deg_inv_sqrt[post_t]
|
| 100 |
+
|
| 101 |
+
# Rescaled Laplacian: L_tilde = (2 / lambda_max) * (I - A_norm) - I
|
| 102 |
+
# Assuming lambda_max ~ 2.0: L_tilde = -A_norm
|
| 103 |
+
edge_indices = torch.stack([post_t, pre_t], dim=0)
|
| 104 |
+
self.register_buffer("laplacian_indices", edge_indices)
|
| 105 |
+
self.register_buffer("laplacian_values", -norm_w)
|
| 106 |
+
|
| 107 |
+
def _sparse_laplacian_mm(self, x: torch.Tensor) -> torch.Tensor:
|
| 108 |
+
"""Computes sparse L_tilde @ x."""
|
| 109 |
+
n = self.num_nodes
|
| 110 |
+
if self.laplacian_indices.numel() == 0 or self.laplacian_values.numel() == 0:
|
| 111 |
+
return torch.zeros_like(x)
|
| 112 |
+
|
| 113 |
+
row = self.laplacian_indices[0]
|
| 114 |
+
col = self.laplacian_indices[1]
|
| 115 |
+
val = self.laplacian_values
|
| 116 |
+
|
| 117 |
+
# Sparse-dense vector multiplication: out[row] += val * x[col]
|
| 118 |
+
out = torch.zeros(n, dtype=torch.float32, device=x.device)
|
| 119 |
+
product = val * x[col]
|
| 120 |
+
out.scatter_add_(0, row, product)
|
| 121 |
+
return out
|
| 122 |
+
|
| 123 |
+
def forward(
|
| 124 |
+
self,
|
| 125 |
+
rates: torch.Tensor,
|
| 126 |
+
neuromod_gain: float = 1.0,
|
| 127 |
+
learn: bool = False,
|
| 128 |
+
) -> Tuple[torch.Tensor, Dict[str, float]]:
|
| 129 |
+
"""Applies Chebyshev spectral graph diffusion over neuron rates vector."""
|
| 130 |
+
r = rates.detach().reshape(-1)[: self.num_nodes]
|
| 131 |
+
if r.numel() < self.num_nodes:
|
| 132 |
+
r = F.pad(r, (0, self.num_nodes - r.numel()))
|
| 133 |
+
|
| 134 |
+
# Evaluate Chebyshev polynomials: T_0, T_1, ..., T_K
|
| 135 |
+
t_prev = r.clone()
|
| 136 |
+
diff_accum = self.filter_weights[0] * t_prev
|
| 137 |
+
|
| 138 |
+
if self.order >= 1:
|
| 139 |
+
t_curr = self._sparse_laplacian_mm(t_prev)
|
| 140 |
+
diff_accum = diff_accum + self.filter_weights[1] * t_curr
|
| 141 |
+
|
| 142 |
+
for k in range(2, self.order + 1):
|
| 143 |
+
t_next = 2.0 * self._sparse_laplacian_mm(t_curr) - t_prev
|
| 144 |
+
diff_accum = diff_accum + self.filter_weights[k] * t_next
|
| 145 |
+
t_prev = t_curr
|
| 146 |
+
t_curr = t_next
|
| 147 |
+
|
| 148 |
+
# Bounded diffusion residual
|
| 149 |
+
diff_signal = torch.tanh(diff_accum) * float(neuromod_gain)
|
| 150 |
+
# Readout gate (starts at 0.0 for zero-intervention baseline)
|
| 151 |
+
gate = torch.tanh(self.readout_gate)
|
| 152 |
+
residual = gate * diff_signal
|
| 153 |
+
|
| 154 |
+
# Telemetry metrics
|
| 155 |
+
energy = float(torch.norm(diff_signal).item())
|
| 156 |
+
dispersion = float(torch.std(diff_signal).item())
|
| 157 |
+
if learn:
|
| 158 |
+
self.last_diffusion_energy.fill_(energy)
|
| 159 |
+
self.last_dispersion.fill_(dispersion)
|
| 160 |
+
|
| 161 |
+
telemetry = {
|
| 162 |
+
"enabled": True,
|
| 163 |
+
"chebyshev_order": self.order,
|
| 164 |
+
"diffusion_energy": round(energy, 4),
|
| 165 |
+
"dispersion": round(dispersion, 4),
|
| 166 |
+
"readout_gate": round(float(gate.item()), 5),
|
| 167 |
+
"updates": int(self.updates),
|
| 168 |
+
}
|
| 169 |
+
return residual, telemetry
|
| 170 |
+
|
| 171 |
+
def update_from_outcome(self, reward: float, prediction_error: float, lr: float = 1e-3) -> Dict[str, float]:
|
| 172 |
+
"""Reward-modulated spectral weight adaptation."""
|
| 173 |
+
with torch.no_grad():
|
| 174 |
+
mod = float(np.clip(reward - 0.5 * prediction_error, -1.0, 1.0))
|
| 175 |
+
self.readout_gate.add_(lr * mod)
|
| 176 |
+
self.readout_gate.clamp_(-0.5, 0.5)
|
| 177 |
+
self.updates += 1
|
| 178 |
+
return {
|
| 179 |
+
"updates": int(self.updates),
|
| 180 |
+
"readout_gate": round(float(self.readout_gate.item()), 5),
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
def summary(self) -> Dict[str, Any]:
|
| 184 |
+
return {
|
| 185 |
+
"enabled": True,
|
| 186 |
+
"num_nodes": self.num_nodes,
|
| 187 |
+
"order": self.order,
|
| 188 |
+
"readout_gate": round(float(torch.tanh(self.readout_gate).item()), 5),
|
| 189 |
+
"last_energy": round(float(self.last_diffusion_energy.item()), 4),
|
| 190 |
+
"last_dispersion": round(float(self.last_dispersion.item()), 4),
|
| 191 |
+
"updates": int(self.updates),
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
def export_state(self) -> Dict[str, Any]:
|
| 195 |
+
return {
|
| 196 |
+
"state_dict": deepcopy(self.state_dict()),
|
| 197 |
+
"updates": int(self.updates),
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
def import_state(self, state: Dict[str, Any]) -> None:
|
| 201 |
+
if not isinstance(state, dict):
|
| 202 |
+
return
|
| 203 |
+
if "state_dict" in state:
|
| 204 |
+
self.load_state_dict(state["state_dict"])
|
| 205 |
+
self.updates = int(state.get("updates", 0))
|
| 206 |
+
|
| 207 |
+
def run_graph_diffusion_assay(self, steps: int = 5, seed: int = 42) -> Dict[str, Any]:
|
| 208 |
+
"""Evaluates multi-scale spectral graph diffusion over connectome structure."""
|
| 209 |
+
rng = np.random.default_rng(seed)
|
| 210 |
+
sample_input = torch.from_numpy(rng.standard_normal(self.num_nodes).astype(np.float32))
|
| 211 |
+
|
| 212 |
+
energies = []
|
| 213 |
+
dispersions = []
|
| 214 |
+
for _ in range(steps):
|
| 215 |
+
_, diag = self.forward(sample_input, learn=False)
|
| 216 |
+
energies.append(diag["diffusion_energy"])
|
| 217 |
+
dispersions.append(diag["dispersion"])
|
| 218 |
+
|
| 219 |
+
return {
|
| 220 |
+
"schema": "chebyshev-spectral-graph-diffusion-v1",
|
| 221 |
+
"status": "completed",
|
| 222 |
+
"num_nodes": self.num_nodes,
|
| 223 |
+
"chebyshev_order": self.order,
|
| 224 |
+
"mean_diffusion_energy": round(float(np.mean(energies)), 4),
|
| 225 |
+
"mean_spatial_dispersion": round(float(np.mean(dispersions)), 4),
|
| 226 |
+
"energy_conservation_ratio": round(float(np.mean(energies) / max(1e-4, float(torch.norm(sample_input).item()))), 4),
|
| 227 |
+
"spectral_radius_bound": 2.0,
|
| 228 |
+
}
|
synapse/observatory.py
CHANGED
|
@@ -136,6 +136,34 @@ class NeuralObservatory:
|
|
| 136 |
'provenance': deepcopy(provenance[i]) if i < len(provenance) else {'representation': 'unknown'},
|
| 137 |
})
|
| 138 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
return {
|
| 140 |
'total_nodes': n,
|
| 141 |
'total_edges': base_count + active_graft_count,
|
|
@@ -143,6 +171,8 @@ class NeuralObservatory:
|
|
| 143 |
'base_edges': base_count,
|
| 144 |
'active_graft_edges': active_graft_count,
|
| 145 |
'graft_links': graft_links,
|
|
|
|
|
|
|
| 146 |
'layout_scope': 'schematic source/role layout; coordinates are not measured anatomy',
|
| 147 |
'nodes': nodes,
|
| 148 |
'edges': edges,
|
|
|
|
| 136 |
'provenance': deepcopy(provenance[i]) if i < len(provenance) else {'representation': 'unknown'},
|
| 137 |
})
|
| 138 |
|
| 139 |
+
# Generate hierarchical Level-Of-Detail (LOD) clusters for scalable WebGL rendering
|
| 140 |
+
lod_clusters = []
|
| 141 |
+
cluster_id = 0
|
| 142 |
+
for origin_idx, s_name in enumerate(source_names):
|
| 143 |
+
source_node_indices = [i for i in range(n) if (int(self.agent.graph.origin[i]) if i < len(self.agent.graph.origin) else 0) == origin_idx]
|
| 144 |
+
if not source_node_indices:
|
| 145 |
+
continue
|
| 146 |
+
# Partition each source into sub-clusters by role and spatial quadrant
|
| 147 |
+
for role_name in ('sensory', 'central', 'motor'):
|
| 148 |
+
role_nodes = [i for i in source_node_indices if (str(self.agent.graph.role[i]) if i < len(self.agent.graph.role) else 'central') == role_name]
|
| 149 |
+
if not role_nodes:
|
| 150 |
+
continue
|
| 151 |
+
pts = self.node_positions_3d[role_nodes]
|
| 152 |
+
centroid = pts.mean(axis=0)
|
| 153 |
+
radius = float(np.max(np.linalg.norm(pts - centroid, axis=1))) if len(pts) > 1 else 0.5
|
| 154 |
+
lod_clusters.append({
|
| 155 |
+
'id': cluster_id,
|
| 156 |
+
'dataset': s_name,
|
| 157 |
+
'role': role_name,
|
| 158 |
+
'node_count': len(role_nodes),
|
| 159 |
+
'x': round(float(centroid[0]), 4),
|
| 160 |
+
'y': round(float(centroid[1]), 4),
|
| 161 |
+
'z': round(float(centroid[2]), 4),
|
| 162 |
+
'radius': round(radius, 4),
|
| 163 |
+
'sample_node_ids': role_nodes[:10],
|
| 164 |
+
})
|
| 165 |
+
cluster_id += 1
|
| 166 |
+
|
| 167 |
return {
|
| 168 |
'total_nodes': n,
|
| 169 |
'total_edges': base_count + active_graft_count,
|
|
|
|
| 171 |
'base_edges': base_count,
|
| 172 |
'active_graft_edges': active_graft_count,
|
| 173 |
'graft_links': graft_links,
|
| 174 |
+
'lod_clusters': lod_clusters,
|
| 175 |
+
'num_lod_clusters': len(lod_clusters),
|
| 176 |
'layout_scope': 'schematic source/role layout; coordinates are not measured anatomy',
|
| 177 |
'nodes': nodes,
|
| 178 |
'edges': edges,
|
synapse/runtime.py
CHANGED
|
@@ -187,7 +187,7 @@ class LabRuntime:
|
|
| 187 |
}
|
| 188 |
def snapshot(self):
|
| 189 |
b=self.agent.brain; g=self.agent.graph; r=b.r.detach().numpy()
|
| 190 |
-
indices=np.linspace(0,g.n-1,min(
|
| 191 |
edge_indices=np.linspace(0,len(g.base_pre)-1,min(3200,len(g.base_pre))).astype(int)
|
| 192 |
mode=('memory' if self.config.surprise_memory else 'model') if self.config.experience_planning else 'off'
|
| 193 |
experience={'model':self.agent.experience_model.summary(),'memory':self.agent.surprise_memory.summary(),'mode':mode,'planner':deepcopy(self.last.get('experience_planner',{}))}
|
|
|
|
| 187 |
}
|
| 188 |
def snapshot(self):
|
| 189 |
b=self.agent.brain; g=self.agent.graph; r=b.r.detach().numpy()
|
| 190 |
+
indices=np.linspace(0,g.n-1,min(6500,g.n)).astype(int)
|
| 191 |
edge_indices=np.linspace(0,len(g.base_pre)-1,min(3200,len(g.base_pre))).astype(int)
|
| 192 |
mode=('memory' if self.config.surprise_memory else 'model') if self.config.experience_planning else 'off'
|
| 193 |
experience={'model':self.agent.experience_model.summary(),'memory':self.agent.surprise_memory.summary(),'mode':mode,'planner':deepcopy(self.last.get('experience_planner',{}))}
|
synapse/server.py
CHANGED
|
@@ -31,7 +31,7 @@ class Workbench:
|
|
| 31 |
self.root=Path(output_root or ARTIFACTS).resolve(); self.root.mkdir(parents=True,exist_ok=True)
|
| 32 |
if runtime is None:
|
| 33 |
from .runtime import LabRuntime
|
| 34 |
-
runtime=LabRuntime(config or SynapseConfig(total_neurons=
|
| 35 |
self.runtime=runtime; self.lock=threading.RLock(); self.job_lock=threading.RLock()
|
| 36 |
from .sim_worker import SimulationWorker
|
| 37 |
self.sim_worker=SimulationWorker(self.runtime, self.lock)
|
|
@@ -462,6 +462,12 @@ class Workbench:
|
|
| 462 |
self.runtime,
|
| 463 |
seed=integer(data.get('seed', 42), 'seed', 0, 2**31-1),
|
| 464 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 465 |
if path=='/api/tandem/max':
|
| 466 |
from .tandem_max import run_tandem_max_orchestration
|
| 467 |
return run_tandem_max_orchestration(self, data)
|
|
|
|
| 31 |
self.root=Path(output_root or ARTIFACTS).resolve(); self.root.mkdir(parents=True,exist_ok=True)
|
| 32 |
if runtime is None:
|
| 33 |
from .runtime import LabRuntime
|
| 34 |
+
runtime=LabRuntime(config or SynapseConfig(total_neurons=6500),output_root=self.root)
|
| 35 |
self.runtime=runtime; self.lock=threading.RLock(); self.job_lock=threading.RLock()
|
| 36 |
from .sim_worker import SimulationWorker
|
| 37 |
self.sim_worker=SimulationWorker(self.runtime, self.lock)
|
|
|
|
| 462 |
self.runtime,
|
| 463 |
seed=integer(data.get('seed', 42), 'seed', 0, 2**31-1),
|
| 464 |
)
|
| 465 |
+
if path=='/api/frontier/innovations':
|
| 466 |
+
from .tandem_max import run_frontier_innovations_assay
|
| 467 |
+
return run_frontier_innovations_assay(
|
| 468 |
+
self.runtime,
|
| 469 |
+
seed=integer(data.get('seed', 42), 'seed', 0, 2**31-1),
|
| 470 |
+
)
|
| 471 |
if path=='/api/tandem/max':
|
| 472 |
from .tandem_max import run_tandem_max_orchestration
|
| 473 |
return run_tandem_max_orchestration(self, data)
|
synapse/tandem_max.py
CHANGED
|
@@ -156,6 +156,38 @@ def run_dvgem_and_tether_assay(runtime, seed: int = 42) -> Dict[str, Any]:
|
|
| 156 |
}
|
| 157 |
|
| 158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
def run_criticality_and_vsa_assay(runtime, seed: int = 42) -> Dict[str, Any]:
|
| 160 |
"""Run Connectome Echo-State Reservoir Criticality Assay and 6-Pair Holographic VSA Binding Assay."""
|
| 161 |
from .criticality_vsa import CriticalityAvalancheEngine, HolographicVSAWorkingMemory
|
|
@@ -328,7 +360,7 @@ def run_tandem_max_orchestration(workbench, data: Optional[Dict[str, Any]] = Non
|
|
| 328 |
t_start = time.perf_counter()
|
| 329 |
max_cpu = min(32, max(1, os.cpu_count() or 4))
|
| 330 |
workers = max(1, min(4, int(data.get('workers', min(4, max(1, os.cpu_count() or 2))))))
|
| 331 |
-
neurons = int(data.get('total_neurons', max(
|
| 332 |
neurons = max(48, min(30000, neurons))
|
| 333 |
task = str(data.get('task', workbench.runtime.config.task or 'forage'))
|
| 334 |
seed = int(data.get('seed', workbench.runtime.config.seed or 7))
|
|
@@ -475,6 +507,7 @@ def run_tandem_max_orchestration(workbench, data: Optional[Dict[str, Any]] = Non
|
|
| 475 |
diffusion_dpo_res = run_diffusion_and_dpo_assay(runtime, horizon=5, seed=seed)
|
| 476 |
dvgem_tether_res = run_dvgem_and_tether_assay(runtime, seed=seed)
|
| 477 |
criticality_vsa_res = run_criticality_and_vsa_assay(runtime, seed=seed)
|
|
|
|
| 478 |
|
| 479 |
from .neural_comm_bench import SancfMatrixBenchmark
|
| 480 |
sancf_bench = SancfMatrixBenchmark(num_steps=10, seed=seed)
|
|
@@ -502,6 +535,7 @@ def run_tandem_max_orchestration(workbench, data: Optional[Dict[str, Any]] = Non
|
|
| 502 |
'diffusion_dpo': diffusion_dpo_res,
|
| 503 |
'dvgem_tether': dvgem_tether_res,
|
| 504 |
'criticality_vsa': criticality_vsa_res,
|
|
|
|
| 505 |
'sancf_comm_matrix': sancf_section_res,
|
| 506 |
}
|
| 507 |
|
|
|
|
| 156 |
}
|
| 157 |
|
| 158 |
|
| 159 |
+
def run_frontier_innovations_assay(runtime, seed: int = 42) -> Dict[str, Any]:
|
| 160 |
+
"""Runs Chebyshev Spectral Graph Diffusion, Active Inference, and Causal Path Tracing assays."""
|
| 161 |
+
import torch
|
| 162 |
+
from .graph_diffusion import ChebyshevGraphDiffusion
|
| 163 |
+
from .active_inference import ActiveInferenceEngine
|
| 164 |
+
from .causal_discovery import CausalPathTracer
|
| 165 |
+
|
| 166 |
+
diff = getattr(runtime.agent, 'graph_diffusion', None) or ChebyshevGraphDiffusion(
|
| 167 |
+
num_nodes=runtime.agent.brain.n,
|
| 168 |
+
pre=torch.as_tensor(runtime.agent.graph.base_pre),
|
| 169 |
+
post=torch.as_tensor(runtime.agent.graph.base_post),
|
| 170 |
+
weights=torch.as_tensor(runtime.agent.brain.base_w),
|
| 171 |
+
seed=seed,
|
| 172 |
+
)
|
| 173 |
+
diff_res = diff.run_graph_diffusion_assay(steps=5, seed=seed)
|
| 174 |
+
|
| 175 |
+
act_inf = getattr(runtime.agent, 'active_inference', None) or ActiveInferenceEngine(
|
| 176 |
+
observation_dim=155, action_dim=5, hidden_dim=32, seed=seed
|
| 177 |
+
)
|
| 178 |
+
act_inf_res = act_inf.run_active_inference_assay(trials=8, seed=seed)
|
| 179 |
+
|
| 180 |
+
tracer = CausalPathTracer(seed=seed)
|
| 181 |
+
causal_paths_res = tracer.run_causal_path_discovery_assay(steps=32, seed=seed)
|
| 182 |
+
|
| 183 |
+
return {
|
| 184 |
+
'status': 'completed',
|
| 185 |
+
'graph_diffusion': diff_res,
|
| 186 |
+
'active_inference': act_inf_res,
|
| 187 |
+
'causal_path_discovery': causal_paths_res,
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
|
| 191 |
def run_criticality_and_vsa_assay(runtime, seed: int = 42) -> Dict[str, Any]:
|
| 192 |
"""Run Connectome Echo-State Reservoir Criticality Assay and 6-Pair Holographic VSA Binding Assay."""
|
| 193 |
from .criticality_vsa import CriticalityAvalancheEngine, HolographicVSAWorkingMemory
|
|
|
|
| 360 |
t_start = time.perf_counter()
|
| 361 |
max_cpu = min(32, max(1, os.cpu_count() or 4))
|
| 362 |
workers = max(1, min(4, int(data.get('workers', min(4, max(1, os.cpu_count() or 2))))))
|
| 363 |
+
neurons = int(data.get('total_neurons', max(6500, workbench.runtime.config.total_neuron_count())))
|
| 364 |
neurons = max(48, min(30000, neurons))
|
| 365 |
task = str(data.get('task', workbench.runtime.config.task or 'forage'))
|
| 366 |
seed = int(data.get('seed', workbench.runtime.config.seed or 7))
|
|
|
|
| 507 |
diffusion_dpo_res = run_diffusion_and_dpo_assay(runtime, horizon=5, seed=seed)
|
| 508 |
dvgem_tether_res = run_dvgem_and_tether_assay(runtime, seed=seed)
|
| 509 |
criticality_vsa_res = run_criticality_and_vsa_assay(runtime, seed=seed)
|
| 510 |
+
frontier_innovations_res = run_frontier_innovations_assay(runtime, seed=seed)
|
| 511 |
|
| 512 |
from .neural_comm_bench import SancfMatrixBenchmark
|
| 513 |
sancf_bench = SancfMatrixBenchmark(num_steps=10, seed=seed)
|
|
|
|
| 535 |
'diffusion_dpo': diffusion_dpo_res,
|
| 536 |
'dvgem_tether': dvgem_tether_res,
|
| 537 |
'criticality_vsa': criticality_vsa_res,
|
| 538 |
+
'frontier_innovations': frontier_innovations_res,
|
| 539 |
'sancf_comm_matrix': sancf_section_res,
|
| 540 |
}
|
| 541 |
|
synapse/web/app.js
CHANGED
|
@@ -2325,7 +2325,7 @@ function setAllControlsToMaxUI() {
|
|
| 2325 |
if ($('training-active')) $('training-active').checked = true;
|
| 2326 |
if ($('training-workers')) $('training-workers').value = '4';
|
| 2327 |
if ($('training-batch')) $('training-batch').value = '256';
|
| 2328 |
-
if ($('graph-nodes')) $('graph-nodes').value = '
|
| 2329 |
if ($('graph-edges')) $('graph-edges').value = '3200';
|
| 2330 |
if ($('graph-grafts')) $('graph-grafts').checked = true;
|
| 2331 |
if ($('experience-mode')) $('experience-mode').value = 'memory';
|
|
@@ -2335,8 +2335,8 @@ function setAllControlsToMaxUI() {
|
|
| 2335 |
|
| 2336 |
const form = $('config');
|
| 2337 |
if (form) {
|
| 2338 |
-
if (form.elements.total_neurons && Number(form.elements.total_neurons.value) <
|
| 2339 |
-
form.elements.total_neurons.value = '
|
| 2340 |
}
|
| 2341 |
if (form.elements.microsteps) form.elements.microsteps.value = '4';
|
| 2342 |
if (form.elements.cognition_interval) form.elements.cognition_interval.value = '4';
|
|
@@ -2376,10 +2376,10 @@ async function runTandemMaxMode() {
|
|
| 2376 |
const btns = [$('btn-tandem-max'), $('btn-tandem-max-toolbar')].filter(Boolean);
|
| 2377 |
btns.forEach(b => { b.disabled = true; b.textContent = '⚡ Running Tandem Max (Sections 1–5)...'; });
|
| 2378 |
try {
|
| 2379 |
-
const neurons = Number($('config')?.elements?.total_neurons?.value ||
|
| 2380 |
const task = $('config')?.elements?.task?.value || state.config?.task || 'continental_biosphere';
|
| 2381 |
const res = await api('tandem/max', {
|
| 2382 |
-
total_neurons: Math.max(
|
| 2383 |
task,
|
| 2384 |
pretrain_steps: 32,
|
| 2385 |
pretrain_updates: 16,
|
|
|
|
| 2325 |
if ($('training-active')) $('training-active').checked = true;
|
| 2326 |
if ($('training-workers')) $('training-workers').value = '4';
|
| 2327 |
if ($('training-batch')) $('training-batch').value = '256';
|
| 2328 |
+
if ($('graph-nodes')) $('graph-nodes').value = '6500';
|
| 2329 |
if ($('graph-edges')) $('graph-edges').value = '3200';
|
| 2330 |
if ($('graph-grafts')) $('graph-grafts').checked = true;
|
| 2331 |
if ($('experience-mode')) $('experience-mode').value = 'memory';
|
|
|
|
| 2335 |
|
| 2336 |
const form = $('config');
|
| 2337 |
if (form) {
|
| 2338 |
+
if (form.elements.total_neurons && Number(form.elements.total_neurons.value) < 6500) {
|
| 2339 |
+
form.elements.total_neurons.value = '6500';
|
| 2340 |
}
|
| 2341 |
if (form.elements.microsteps) form.elements.microsteps.value = '4';
|
| 2342 |
if (form.elements.cognition_interval) form.elements.cognition_interval.value = '4';
|
|
|
|
| 2376 |
const btns = [$('btn-tandem-max'), $('btn-tandem-max-toolbar')].filter(Boolean);
|
| 2377 |
btns.forEach(b => { b.disabled = true; b.textContent = '⚡ Running Tandem Max (Sections 1–5)...'; });
|
| 2378 |
try {
|
| 2379 |
+
const neurons = Number($('config')?.elements?.total_neurons?.value || 6500);
|
| 2380 |
const task = $('config')?.elements?.task?.value || state.config?.task || 'continental_biosphere';
|
| 2381 |
const res = await api('tandem/max', {
|
| 2382 |
+
total_neurons: Math.max(6500, neurons),
|
| 2383 |
task,
|
| 2384 |
pretrain_steps: 32,
|
| 2385 |
pretrain_updates: 16,
|
synapse/web/index.html
CHANGED
|
@@ -156,7 +156,7 @@
|
|
| 156 |
<label>Source<select id="graph-source"><option value="all">All active sources</option></select></label>
|
| 157 |
<label>Role<select id="graph-role"><option value="all">All roles</option></select></label>
|
| 158 |
<label>Activity scale<select id="graph-activity"><option value="1">1×</option><option value="3">3×</option><option value="10">10×</option><option value="0.3">0.3×</option></select></label>
|
| 159 |
-
<label>Node budget<select id="graph-nodes"><option>250</option><option>500</option><option>1000</option><option>2000</option><option>4000</option><option selected>
|
| 160 |
<label>Edge budget<select id="graph-edges"><option>0</option><option>200</option><option>500</option><option selected>1600</option><option>3200</option></select></label>
|
| 161 |
<label class="toggle-label"><input id="graph-grafts" type="checkbox" checked> Executing graft links</label>
|
| 162 |
<button id="graph-camera-reset" title="Reset orbit and zoom">Reset view</button>
|
|
@@ -508,7 +508,7 @@
|
|
| 508 |
</select>
|
| 509 |
</label>
|
| 510 |
<label>Seed<input name="seed" type="number" value="7" min="0"></label>
|
| 511 |
-
<label>Total neurons<input name="total_neurons" type="number" value="
|
| 512 |
<label>Microsteps<input name="microsteps" type="number" value="2" min="1" max="8"></label>
|
| 513 |
<label>Cognition interval<input name="cognition_interval" type="number" value="8" min="1" max="128"></label>
|
| 514 |
<label>Readout
|
|
|
|
| 156 |
<label>Source<select id="graph-source"><option value="all">All active sources</option></select></label>
|
| 157 |
<label>Role<select id="graph-role"><option value="all">All roles</option></select></label>
|
| 158 |
<label>Activity scale<select id="graph-activity"><option value="1">1×</option><option value="3">3×</option><option value="10">10×</option><option value="0.3">0.3×</option></select></label>
|
| 159 |
+
<label>Node budget<select id="graph-nodes"><option>250</option><option>500</option><option>1000</option><option>2000</option><option>4000</option><option>6000</option><option selected>6500</option></select></label>
|
| 160 |
<label>Edge budget<select id="graph-edges"><option>0</option><option>200</option><option>500</option><option selected>1600</option><option>3200</option></select></label>
|
| 161 |
<label class="toggle-label"><input id="graph-grafts" type="checkbox" checked> Executing graft links</label>
|
| 162 |
<button id="graph-camera-reset" title="Reset orbit and zoom">Reset view</button>
|
|
|
|
| 508 |
</select>
|
| 509 |
</label>
|
| 510 |
<label>Seed<input name="seed" type="number" value="7" min="0"></label>
|
| 511 |
+
<label>Total neurons<input name="total_neurons" type="number" value="6500" min="48" max="30000"></label>
|
| 512 |
<label>Microsteps<input name="microsteps" type="number" value="2" min="1" max="8"></label>
|
| 513 |
<label>Cognition interval<input name="cognition_interval" type="number" value="8" min="1" max="128"></label>
|
| 514 |
<label>Readout
|
tests/test_sancf_and_frontier_master.py
CHANGED
|
@@ -461,15 +461,60 @@ def test_benchmark_suite_execution():
|
|
| 461 |
|
| 462 |
|
| 463 |
# ---------------------------------------------------------------------------
|
| 464 |
-
# 20. 6,
|
| 465 |
# ---------------------------------------------------------------------------
|
| 466 |
-
def
|
| 467 |
-
cfg = SynapseConfig.frontier_apex(total_neurons=
|
| 468 |
-
assert cfg.total_neuron_count() ==
|
| 469 |
assert cfg.neural_comm_enabled is True
|
| 470 |
assert cfg.overflow_mini_brain is True
|
|
|
|
|
|
|
| 471 |
|
| 472 |
# Baseline default configuration preserves backward compatibility
|
| 473 |
base_cfg = SynapseConfig(total_neurons=4000)
|
| 474 |
assert base_cfg.neural_comm_enabled is False
|
| 475 |
assert base_cfg.overflow_mini_brain is False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 461 |
|
| 462 |
|
| 463 |
# ---------------------------------------------------------------------------
|
| 464 |
+
# 20. 6,500 Neurons Scaling & Baseline Invariant
|
| 465 |
# ---------------------------------------------------------------------------
|
| 466 |
+
def test_6500_neurons_scaling():
|
| 467 |
+
cfg = SynapseConfig.frontier_apex(total_neurons=6500, seed=7)
|
| 468 |
+
assert cfg.total_neuron_count() == 6500
|
| 469 |
assert cfg.neural_comm_enabled is True
|
| 470 |
assert cfg.overflow_mini_brain is True
|
| 471 |
+
assert cfg.graph_diffusion is True
|
| 472 |
+
assert cfg.active_inference_planner is True
|
| 473 |
|
| 474 |
# Baseline default configuration preserves backward compatibility
|
| 475 |
base_cfg = SynapseConfig(total_neurons=4000)
|
| 476 |
assert base_cfg.neural_comm_enabled is False
|
| 477 |
assert base_cfg.overflow_mini_brain is False
|
| 478 |
+
assert base_cfg.graph_diffusion is False
|
| 479 |
+
assert base_cfg.active_inference_planner is False
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
# ---------------------------------------------------------------------------
|
| 483 |
+
# 21. Frontier Innovations: Graph Diffusion, Active Inference & Causal Paths
|
| 484 |
+
# ---------------------------------------------------------------------------
|
| 485 |
+
def test_frontier_innovations_graph_diffusion_and_active_inference():
|
| 486 |
+
from synapse.graph_diffusion import ChebyshevGraphDiffusion
|
| 487 |
+
from synapse.active_inference import ActiveInferenceEngine
|
| 488 |
+
from synapse.causal_discovery import CausalPathTracer
|
| 489 |
+
|
| 490 |
+
# 1. Chebyshev Graph Diffusion (zero-readout frozen invariance)
|
| 491 |
+
diff = ChebyshevGraphDiffusion(num_nodes=64, seed=42)
|
| 492 |
+
x = torch.randn(64)
|
| 493 |
+
res0, diag0 = diff(x, learn=False)
|
| 494 |
+
assert res0.shape == (64,)
|
| 495 |
+
assert torch.allclose(res0, torch.zeros(64))
|
| 496 |
+
assert diag0['chebyshev_order'] == 4
|
| 497 |
+
|
| 498 |
+
assay_diff = diff.run_graph_diffusion_assay(steps=3, seed=42)
|
| 499 |
+
assert assay_diff['status'] == 'completed'
|
| 500 |
+
assert assay_diff['spectral_radius_bound'] == 2.0
|
| 501 |
+
|
| 502 |
+
# 2. Active Inference Expected Free Energy minimization
|
| 503 |
+
act_inf = ActiveInferenceEngine(observation_dim=155, action_dim=5, hidden_dim=32, seed=42)
|
| 504 |
+
base_act = np.zeros(5, dtype=np.float32)
|
| 505 |
+
feat = np.random.randn(32).astype(np.float32)
|
| 506 |
+
selected_act, diag_inf = act_inf.select_action(base_act, feat, num_candidates=6)
|
| 507 |
+
assert len(selected_act) == 5
|
| 508 |
+
assert diag_inf['enabled'] is True
|
| 509 |
+
assert 'min_efe' in diag_inf
|
| 510 |
+
assert 'epistemic_gain' in diag_inf
|
| 511 |
+
|
| 512 |
+
assay_inf = act_inf.run_active_inference_assay(trials=4, seed=42)
|
| 513 |
+
assert assay_inf['status'] == 'completed'
|
| 514 |
+
|
| 515 |
+
# 3. DirectLiNGAM Causal Path Discovery
|
| 516 |
+
tracer = CausalPathTracer(seed=42)
|
| 517 |
+
assay_paths = tracer.run_causal_path_discovery_assay(steps=24, seed=42)
|
| 518 |
+
assert assay_paths['status'] == 'completed'
|
| 519 |
+
assert assay_paths['is_strictly_acyclic'] is True
|
| 520 |
+
assert len(assay_paths['causal_topological_order']) == 6
|