Skip to content

SCPN Layers

Stochastic Coupled Phase-oscillator Network layer adapters.

Maps the 16-layer SCPN holonomic model (L1 Quantum through L16 Meta) into SC-NeuroCore's simulation engine. JAX-accelerated Kuramoto coupling, UPDE solvers, and phase coherence metrics.

from sc_neurocore.scpn import KuramotoCoupling, PhaseCoherenceMetric

sc_neurocore.scpn

sc_neurocore.scpn -- Tier: research (experimental / research).

L1_QuantumLayer

Stochastic implementation of the Quantum Cellular Field.

Source code in src/sc_neurocore/scpn/layers/l1_quantum.py
 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
124
125
126
127
128
129
130
131
132
class L1_QuantumLayer:
    """
    Stochastic implementation of the Quantum Cellular Field.
    """

    def __init__(self, params: Optional[L1_StochasticParameters] = None) -> None:
        self.params = params or L1_StochasticParameters()

        # The Core Engine
        if self.params.backend == "simulated":
            self.quantum_core: Any = QuantumStochasticLayer(
                n_qubits=self.params.n_qubits, length=self.params.bitstream_length
            )
        else:
            self.quantum_core = QuantumHardwareLayer(
                n_qubits=self.params.n_qubits,
                length=self.params.bitstream_length,
                backend_type=self.params.backend,
            )

        # State: Coherence represented as probabilities (0.0 to 1.0)
        # In SC, this will be encoded into bitstreams.
        # Initialize with max coherence (pure state)
        self.coherence_probs = np.ones(self.params.n_qubits) * 0.95

        # History for non-Markovian effects
        self.history: list[float] = []

    def step(
        self, dt: float, external_field: Optional[np.ndarray[Any, Any]] = None
    ) -> np.ndarray[Any, Any]:
        """
        Advance the layer by one time step.

        Args:
            dt: Time step in seconds.
            external_field: Optional coupling input from other layers (normalized 0-1).

        Returns:
            output_bitstreams: The stochastic state of the field.
        """
        # 1. Apply Decoherence (Classical Decay)
        # Adjusted by Non-Markovian factor
        effective_decay = self.params.decoherence_rate * dt / np.log10(self.params.F_non_Markov)
        self.coherence_probs *= 1.0 - effective_decay

        # 2. Apply External Coupling (e.g. from L2 Neurochemical)
        if external_field is not None:
            # Mix the field: coherence is modulated by external input
            # Simple convex combination for now
            self.coherence_probs = (
                1 - self.params.coupling_strength
            ) * self.coherence_probs + self.params.coupling_strength * external_field

        # 3. Quantum Rotation via Stochastic Core
        # The core takes the probabilities, rotates them (simulating evolution),
        # and returns collapsed bitstreams.
        # We assume the 'probability' maps to the quantum phase/amplitude.

        # Generate input bitstreams from current probabilities
        # (This is a simplified interface; ideally we keep state in bitstreams)
        # Using a simple generator for now:
        rands = np.random.random((self.params.n_qubits, self.params.bitstream_length))
        input_bits = (rands < self.coherence_probs[:, None]).astype(np.uint8)

        # Pass through Quantum Hybrid Layer
        output_bits = self.quantum_core.forward(input_bits)

        # 4. Update State from Measurement (Collapse/Update)
        # The output bits represent the measured state.
        # We update our internal probabilities based on the measurement (Bayesian update or similar)
        # For this simulation, we'll take the mean as the new base, but add some "Quantum Zeno" recovery
        measured_probs = np.mean(output_bits, axis=1)

        # "Zeno" effect: frequent measurement can freeze evolution or reset it.
        # Here we just blend it back.
        self.coherence_probs = 0.9 * self.coherence_probs + 0.1 * measured_probs

        res: np.ndarray[Any, Any] = output_bits
        return res

    def get_global_metric(self) -> float:
        """Return the global coherence metric (Phi-like)."""
        return float(np.mean(self.coherence_probs))

step(dt, external_field=None)

Advance the layer by one time step.

Parameters:

Name Type Description Default
dt float

Time step in seconds.

required
external_field Optional[ndarray[Any, Any]]

Optional coupling input from other layers (normalized 0-1).

None

Returns:

Name Type Description
output_bitstreams ndarray[Any, Any]

The stochastic state of the field.

Source code in src/sc_neurocore/scpn/layers/l1_quantum.py
 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
124
125
126
127
128
def step(
    self, dt: float, external_field: Optional[np.ndarray[Any, Any]] = None
) -> np.ndarray[Any, Any]:
    """
    Advance the layer by one time step.

    Args:
        dt: Time step in seconds.
        external_field: Optional coupling input from other layers (normalized 0-1).

    Returns:
        output_bitstreams: The stochastic state of the field.
    """
    # 1. Apply Decoherence (Classical Decay)
    # Adjusted by Non-Markovian factor
    effective_decay = self.params.decoherence_rate * dt / np.log10(self.params.F_non_Markov)
    self.coherence_probs *= 1.0 - effective_decay

    # 2. Apply External Coupling (e.g. from L2 Neurochemical)
    if external_field is not None:
        # Mix the field: coherence is modulated by external input
        # Simple convex combination for now
        self.coherence_probs = (
            1 - self.params.coupling_strength
        ) * self.coherence_probs + self.params.coupling_strength * external_field

    # 3. Quantum Rotation via Stochastic Core
    # The core takes the probabilities, rotates them (simulating evolution),
    # and returns collapsed bitstreams.
    # We assume the 'probability' maps to the quantum phase/amplitude.

    # Generate input bitstreams from current probabilities
    # (This is a simplified interface; ideally we keep state in bitstreams)
    # Using a simple generator for now:
    rands = np.random.random((self.params.n_qubits, self.params.bitstream_length))
    input_bits = (rands < self.coherence_probs[:, None]).astype(np.uint8)

    # Pass through Quantum Hybrid Layer
    output_bits = self.quantum_core.forward(input_bits)

    # 4. Update State from Measurement (Collapse/Update)
    # The output bits represent the measured state.
    # We update our internal probabilities based on the measurement (Bayesian update or similar)
    # For this simulation, we'll take the mean as the new base, but add some "Quantum Zeno" recovery
    measured_probs = np.mean(output_bits, axis=1)

    # "Zeno" effect: frequent measurement can freeze evolution or reset it.
    # Here we just blend it back.
    self.coherence_probs = 0.9 * self.coherence_probs + 0.1 * measured_probs

    res: np.ndarray[Any, Any] = output_bits
    return res

get_global_metric()

Return the global coherence metric (Phi-like).

Source code in src/sc_neurocore/scpn/layers/l1_quantum.py
130
131
132
def get_global_metric(self) -> float:
    """Return the global coherence metric (Phi-like)."""
    return float(np.mean(self.coherence_probs))

L2_NeurochemicalLayer

Stochastic implementation of the Neurochemical Signaling Layer.

Models receptor-ligand binding, neurotransmitter dynamics, and second messenger cascades using bitstream representations.

Source code in src/sc_neurocore/scpn/layers/l2_neurochemical.py
 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
class L2_NeurochemicalLayer:
    """
    Stochastic implementation of the Neurochemical Signaling Layer.

    Models receptor-ligand binding, neurotransmitter dynamics, and
    second messenger cascades using bitstream representations.
    """

    # Neurotransmitter indices
    DA = 0  # Dopamine
    SEROTONIN = 1  # 5-HT
    NE = 2  # Norepinephrine
    ACH = 3  # Acetylcholine

    def __init__(self, params: Optional[L2_StochasticParameters] = None):
        self.params = params or L2_StochasticParameters()

        # Receptor states: 0 = unbound, 1 = bound
        self.receptor_states = np.zeros(
            (self.params.n_neurotransmitter_types, self.params.n_receptors), dtype=np.float32
        )

        # Neurotransmitter concentrations (as probabilities for bitstream encoding)
        self.nt_concentrations = np.ones(self.params.n_neurotransmitter_types) * 0.5

        # Second messenger cascade state (cAMP, Ca2+, etc.)
        self.second_messenger_levels = np.zeros(self.params.n_neurotransmitter_types)

        # History for temporal dynamics
        self.history: list[Any] = []

    def step(
        self,
        dt: float,
        nt_release: Optional[np.ndarray[Any, Any]] = None,
        l1_input: Optional[np.ndarray[Any, Any]] = None,
    ) -> Dict[str, np.ndarray[Any, Any]]:
        """
        Advance the layer by one time step.

        Args:
            dt: Time step in seconds.
            nt_release: Neurotransmitter release rates [4] (0-1 normalized).
            l1_input: Quantum layer input (coherence modulation).

        Returns:
            Dict with receptor_activity, second_messengers, output_bitstreams
        """
        # 1. Update neurotransmitter concentrations from release
        if nt_release is not None:
            self.nt_concentrations = np.clip(
                self.nt_concentrations + nt_release * dt - self.params.reuptake_rate * dt, 0.0, 1.0
            )

        # 2. Receptor binding dynamics (stochastic)
        for nt_idx in range(self.params.n_neurotransmitter_types):
            nt_conc = self.nt_concentrations[nt_idx]

            # Binding: P(bind) = affinity * concentration * (1 - current_state)
            binding_prob = self.params.binding_affinity * nt_conc
            bind_mask = np.random.random(self.params.n_receptors) < binding_prob * dt

            # Unbinding: P(unbind) = unbinding_rate * current_state
            unbind_mask = (
                np.random.random(self.params.n_receptors) < self.params.unbinding_rate * dt
            )

            # Update states
            self.receptor_states[nt_idx] = np.where(
                bind_mask & (self.receptor_states[nt_idx] < 0.5), 1.0, self.receptor_states[nt_idx]
            )
            self.receptor_states[nt_idx] = np.where(
                unbind_mask & (self.receptor_states[nt_idx] > 0.5),
                0.0,
                self.receptor_states[nt_idx],
            )

        # 3. Second messenger cascade
        receptor_activity = np.mean(self.receptor_states, axis=1)
        self.second_messenger_levels = 0.9 * self.second_messenger_levels + 0.1 * receptor_activity

        # 4. Quantum coupling (L1 modulates receptor sensitivity)
        if l1_input is not None:
            quantum_mod = np.mean(l1_input) * self.params.quantum_coupling
            self.receptor_states *= 1.0 + quantum_mod
            self.receptor_states = np.clip(self.receptor_states, 0.0, 1.0)

        # 5. Generate output bitstreams
        output_probs = receptor_activity
        rands = np.random.random(
            (self.params.n_neurotransmitter_types, self.params.bitstream_length)
        )
        output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

        # Store history
        self.history.append(
            {
                "nt_concentrations": self.nt_concentrations.copy(),
                "receptor_activity": receptor_activity.copy(),
                "second_messengers": self.second_messenger_levels.copy(),
            }
        )
        if len(self.history) > 100:
            self.history.pop(0)

        return {
            "receptor_activity": receptor_activity,
            "second_messengers": self.second_messenger_levels.copy(),
            "output_bitstreams": output_bitstreams,
            "nt_concentrations": self.nt_concentrations.copy(),
        }

    def release_neurotransmitter(self, nt_type: int, amount: float) -> None:
        """Trigger neurotransmitter release."""
        if 0 <= nt_type < self.params.n_neurotransmitter_types:
            self.nt_concentrations[nt_type] = np.clip(
                self.nt_concentrations[nt_type] + amount, 0.0, 1.0
            )

    def get_global_metric(self) -> float:
        """Return the global neurochemical activity metric."""
        return float(np.mean(self.receptor_states))

    def get_neuromodulation_state(self) -> Dict[str, float]:
        """Return named neurotransmitter levels for external use."""
        return {
            "dopamine": float(self.nt_concentrations[self.DA]),
            "serotonin": float(self.nt_concentrations[self.SEROTONIN]),
            "norepinephrine": float(self.nt_concentrations[self.NE]),
            "acetylcholine": float(self.nt_concentrations[self.ACH]),
        }

step(dt, nt_release=None, l1_input=None)

Advance the layer by one time step.

Parameters:

Name Type Description Default
dt float

Time step in seconds.

required
nt_release Optional[ndarray[Any, Any]]

Neurotransmitter release rates [4] (0-1 normalized).

None
l1_input Optional[ndarray[Any, Any]]

Quantum layer input (coherence modulation).

None

Returns:

Type Description
Dict[str, ndarray[Any, Any]]

Dict with receptor_activity, second_messengers, output_bitstreams

Source code in src/sc_neurocore/scpn/layers/l2_neurochemical.py
 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
def step(
    self,
    dt: float,
    nt_release: Optional[np.ndarray[Any, Any]] = None,
    l1_input: Optional[np.ndarray[Any, Any]] = None,
) -> Dict[str, np.ndarray[Any, Any]]:
    """
    Advance the layer by one time step.

    Args:
        dt: Time step in seconds.
        nt_release: Neurotransmitter release rates [4] (0-1 normalized).
        l1_input: Quantum layer input (coherence modulation).

    Returns:
        Dict with receptor_activity, second_messengers, output_bitstreams
    """
    # 1. Update neurotransmitter concentrations from release
    if nt_release is not None:
        self.nt_concentrations = np.clip(
            self.nt_concentrations + nt_release * dt - self.params.reuptake_rate * dt, 0.0, 1.0
        )

    # 2. Receptor binding dynamics (stochastic)
    for nt_idx in range(self.params.n_neurotransmitter_types):
        nt_conc = self.nt_concentrations[nt_idx]

        # Binding: P(bind) = affinity * concentration * (1 - current_state)
        binding_prob = self.params.binding_affinity * nt_conc
        bind_mask = np.random.random(self.params.n_receptors) < binding_prob * dt

        # Unbinding: P(unbind) = unbinding_rate * current_state
        unbind_mask = (
            np.random.random(self.params.n_receptors) < self.params.unbinding_rate * dt
        )

        # Update states
        self.receptor_states[nt_idx] = np.where(
            bind_mask & (self.receptor_states[nt_idx] < 0.5), 1.0, self.receptor_states[nt_idx]
        )
        self.receptor_states[nt_idx] = np.where(
            unbind_mask & (self.receptor_states[nt_idx] > 0.5),
            0.0,
            self.receptor_states[nt_idx],
        )

    # 3. Second messenger cascade
    receptor_activity = np.mean(self.receptor_states, axis=1)
    self.second_messenger_levels = 0.9 * self.second_messenger_levels + 0.1 * receptor_activity

    # 4. Quantum coupling (L1 modulates receptor sensitivity)
    if l1_input is not None:
        quantum_mod = np.mean(l1_input) * self.params.quantum_coupling
        self.receptor_states *= 1.0 + quantum_mod
        self.receptor_states = np.clip(self.receptor_states, 0.0, 1.0)

    # 5. Generate output bitstreams
    output_probs = receptor_activity
    rands = np.random.random(
        (self.params.n_neurotransmitter_types, self.params.bitstream_length)
    )
    output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

    # Store history
    self.history.append(
        {
            "nt_concentrations": self.nt_concentrations.copy(),
            "receptor_activity": receptor_activity.copy(),
            "second_messengers": self.second_messenger_levels.copy(),
        }
    )
    if len(self.history) > 100:
        self.history.pop(0)

    return {
        "receptor_activity": receptor_activity,
        "second_messengers": self.second_messenger_levels.copy(),
        "output_bitstreams": output_bitstreams,
        "nt_concentrations": self.nt_concentrations.copy(),
    }

release_neurotransmitter(nt_type, amount)

Trigger neurotransmitter release.

Source code in src/sc_neurocore/scpn/layers/l2_neurochemical.py
165
166
167
168
169
170
def release_neurotransmitter(self, nt_type: int, amount: float) -> None:
    """Trigger neurotransmitter release."""
    if 0 <= nt_type < self.params.n_neurotransmitter_types:
        self.nt_concentrations[nt_type] = np.clip(
            self.nt_concentrations[nt_type] + amount, 0.0, 1.0
        )

get_global_metric()

Return the global neurochemical activity metric.

Source code in src/sc_neurocore/scpn/layers/l2_neurochemical.py
172
173
174
def get_global_metric(self) -> float:
    """Return the global neurochemical activity metric."""
    return float(np.mean(self.receptor_states))

get_neuromodulation_state()

Return named neurotransmitter levels for external use.

Source code in src/sc_neurocore/scpn/layers/l2_neurochemical.py
176
177
178
179
180
181
182
183
def get_neuromodulation_state(self) -> Dict[str, float]:
    """Return named neurotransmitter levels for external use."""
    return {
        "dopamine": float(self.nt_concentrations[self.DA]),
        "serotonin": float(self.nt_concentrations[self.SEROTONIN]),
        "norepinephrine": float(self.nt_concentrations[self.NE]),
        "acetylcholine": float(self.nt_concentrations[self.ACH]),
    }

L3_GenomicLayer

Stochastic implementation of the Genomic-Epigenomic Layer.

Models gene expression, epigenetic modifications, and bioelectric pattern formation using bitstream representations.

Source code in src/sc_neurocore/scpn/layers/l3_genomic.py
 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
class L3_GenomicLayer:
    """
    Stochastic implementation of the Genomic-Epigenomic Layer.

    Models gene expression, epigenetic modifications, and bioelectric
    pattern formation using bitstream representations.
    """

    def __init__(self, params: Optional[L3_StochasticParameters] = None):
        self.params = params or L3_StochasticParameters()

        # Gene expression levels (mRNA proxy, 0-1 normalized)
        self.expression_levels = np.random.random(self.params.n_genes) * 0.3

        # Protein concentrations
        self.protein_levels = np.random.random(self.params.n_genes) * 0.2

        # Chromatin state: 0 = closed (silenced), 1 = open (active)
        self.chromatin_state = np.random.random(self.params.n_genes) > 0.5
        self.chromatin_openness = self.chromatin_state.astype(np.float32)

        # Methylation pattern (0-1, higher = more methylated = silenced)
        self.methylation = np.random.random(self.params.n_genes) * 0.3

        # Bioelectric membrane potential grid
        self.membrane_potential = np.ones(self.params.n_genes) * self.params.membrane_potential_rest

        # CISS spin state (for quantum-genomic coupling)
        self.spin_polarization = np.zeros(self.params.n_genes)

        # Regulatory network (simplified adjacency)
        self.regulatory_matrix = self._init_regulatory_network()

    def _init_regulatory_network(self) -> np.ndarray[Any, Any]:
        """Initialize gene regulatory network with sparse connections."""
        # Sparse random regulatory matrix
        matrix = np.random.random((self.params.n_genes, self.params.n_regulatory_elements))
        matrix = np.where(matrix > 0.9, matrix, 0)  # Sparse
        # Add some inhibitory connections
        matrix[:, : self.params.n_regulatory_elements // 3] *= -1
        return matrix

    def step(
        self,
        dt: float,
        l2_input: Optional[Dict[str, Any]] = None,
        bioelectric_signal: Optional[np.ndarray[Any, Any]] = None,
    ) -> Dict[str, np.ndarray[Any, Any]]:
        """
        Advance the layer by one time step.

        Args:
            dt: Time step in seconds.
            l2_input: Neurochemical layer output (second messengers).
            bioelectric_signal: External bioelectric modulation.

        Returns:
            Dict with expression, protein_levels, chromatin_state, output_bitstreams
        """
        # 1. Update chromatin state (epigenetic dynamics)
        # Methylation silences genes
        demeth_prob = self.params.demethylation_rate * dt
        meth_prob = self.params.methylation_rate * dt

        demeth_mask = np.random.random(self.params.n_genes) < demeth_prob
        meth_mask = np.random.random(self.params.n_genes) < meth_prob

        self.methylation = np.where(demeth_mask, self.methylation * 0.9, self.methylation)
        self.methylation = np.where(meth_mask, self.methylation + 0.1, self.methylation)
        self.methylation = np.clip(self.methylation, 0.0, 1.0)

        # Chromatin openness inversely related to methylation
        self.chromatin_openness = (
            1.0 - self.methylation + np.random.normal(0, 0.05, self.params.n_genes)
        )
        self.chromatin_openness = np.clip(self.chromatin_openness, 0.0, 1.0)

        # 2. Gene expression (stochastic transcription)
        # Only open chromatin can be transcribed
        transcription_prob = self.params.transcription_rate * self.chromatin_openness * dt
        transcription = np.random.random(self.params.n_genes) < transcription_prob

        self.expression_levels = np.where(
            transcription,
            self.expression_levels + 0.1,
            self.expression_levels - self.params.degradation_rate * dt,
        )
        self.expression_levels = np.clip(self.expression_levels, 0.0, 1.0)

        # 3. Translation to protein
        translation_prob = self.params.translation_rate * self.expression_levels * dt
        translation = np.random.random(self.params.n_genes) < translation_prob

        self.protein_levels = np.where(
            translation,
            self.protein_levels + 0.05,
            self.protein_levels - self.params.degradation_rate * dt * 0.5,
        )
        self.protein_levels = np.clip(self.protein_levels, 0.0, 1.0)

        # 4. CISS effect (quantum spin filtering)
        # Spin polarization depends on DNA chirality and electron flow
        electron_flow = np.mean(self.expression_levels)  # Proxy for metabolic activity
        self.spin_polarization = (
            self.params.ciss_efficiency * self.params.dna_chirality * electron_flow
        )
        self.spin_polarization = np.clip(
            self.spin_polarization + np.random.normal(0, 0.1, self.params.n_genes), -1.0, 1.0
        )

        # 5. Neurochemical coupling (L2 input modulates expression)
        if l2_input is not None and "second_messengers" in l2_input:
            # cAMP from second messengers activates transcription factors
            camp_level = np.mean(l2_input["second_messengers"])
            activation_boost = camp_level * self.params.neurochemical_coupling
            self.expression_levels += activation_boost * dt
            self.expression_levels = np.clip(self.expression_levels, 0.0, 1.0)

        # 6. Bioelectric pattern formation
        if bioelectric_signal is not None:
            self.membrane_potential = (
                0.9 * self.membrane_potential + 0.1 * bioelectric_signal[: self.params.n_genes]
            )
        # Internal bioelectric dynamics (gap junction diffusion)
        diffusion = np.roll(self.membrane_potential, 1) - self.membrane_potential
        self.membrane_potential += diffusion * self.params.bioelectric_coupling * dt

        # 7. Generate output bitstreams
        output_probs = self.protein_levels
        rands = np.random.random((self.params.n_genes, self.params.bitstream_length))
        output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

        return {
            "expression_levels": self.expression_levels.copy(),
            "protein_levels": self.protein_levels.copy(),
            "chromatin_openness": self.chromatin_openness.copy(),
            "methylation": self.methylation.copy(),
            "spin_polarization": self.spin_polarization.copy(),
            "membrane_potential": self.membrane_potential.copy(),
            "output_bitstreams": output_bitstreams,
        }

    def get_global_metric(self) -> float:
        """Return the global genomic activity metric."""
        return float(np.mean(self.expression_levels))

    def get_ciss_coherence(self) -> float:
        """Return CISS spin coherence metric."""
        return float(np.abs(np.mean(self.spin_polarization)))

step(dt, l2_input=None, bioelectric_signal=None)

Advance the layer by one time step.

Parameters:

Name Type Description Default
dt float

Time step in seconds.

required
l2_input Optional[Dict[str, Any]]

Neurochemical layer output (second messengers).

None
bioelectric_signal Optional[ndarray[Any, Any]]

External bioelectric modulation.

None

Returns:

Type Description
Dict[str, ndarray[Any, Any]]

Dict with expression, protein_levels, chromatin_state, output_bitstreams

Source code in src/sc_neurocore/scpn/layers/l3_genomic.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
def step(
    self,
    dt: float,
    l2_input: Optional[Dict[str, Any]] = None,
    bioelectric_signal: Optional[np.ndarray[Any, Any]] = None,
) -> Dict[str, np.ndarray[Any, Any]]:
    """
    Advance the layer by one time step.

    Args:
        dt: Time step in seconds.
        l2_input: Neurochemical layer output (second messengers).
        bioelectric_signal: External bioelectric modulation.

    Returns:
        Dict with expression, protein_levels, chromatin_state, output_bitstreams
    """
    # 1. Update chromatin state (epigenetic dynamics)
    # Methylation silences genes
    demeth_prob = self.params.demethylation_rate * dt
    meth_prob = self.params.methylation_rate * dt

    demeth_mask = np.random.random(self.params.n_genes) < demeth_prob
    meth_mask = np.random.random(self.params.n_genes) < meth_prob

    self.methylation = np.where(demeth_mask, self.methylation * 0.9, self.methylation)
    self.methylation = np.where(meth_mask, self.methylation + 0.1, self.methylation)
    self.methylation = np.clip(self.methylation, 0.0, 1.0)

    # Chromatin openness inversely related to methylation
    self.chromatin_openness = (
        1.0 - self.methylation + np.random.normal(0, 0.05, self.params.n_genes)
    )
    self.chromatin_openness = np.clip(self.chromatin_openness, 0.0, 1.0)

    # 2. Gene expression (stochastic transcription)
    # Only open chromatin can be transcribed
    transcription_prob = self.params.transcription_rate * self.chromatin_openness * dt
    transcription = np.random.random(self.params.n_genes) < transcription_prob

    self.expression_levels = np.where(
        transcription,
        self.expression_levels + 0.1,
        self.expression_levels - self.params.degradation_rate * dt,
    )
    self.expression_levels = np.clip(self.expression_levels, 0.0, 1.0)

    # 3. Translation to protein
    translation_prob = self.params.translation_rate * self.expression_levels * dt
    translation = np.random.random(self.params.n_genes) < translation_prob

    self.protein_levels = np.where(
        translation,
        self.protein_levels + 0.05,
        self.protein_levels - self.params.degradation_rate * dt * 0.5,
    )
    self.protein_levels = np.clip(self.protein_levels, 0.0, 1.0)

    # 4. CISS effect (quantum spin filtering)
    # Spin polarization depends on DNA chirality and electron flow
    electron_flow = np.mean(self.expression_levels)  # Proxy for metabolic activity
    self.spin_polarization = (
        self.params.ciss_efficiency * self.params.dna_chirality * electron_flow
    )
    self.spin_polarization = np.clip(
        self.spin_polarization + np.random.normal(0, 0.1, self.params.n_genes), -1.0, 1.0
    )

    # 5. Neurochemical coupling (L2 input modulates expression)
    if l2_input is not None and "second_messengers" in l2_input:
        # cAMP from second messengers activates transcription factors
        camp_level = np.mean(l2_input["second_messengers"])
        activation_boost = camp_level * self.params.neurochemical_coupling
        self.expression_levels += activation_boost * dt
        self.expression_levels = np.clip(self.expression_levels, 0.0, 1.0)

    # 6. Bioelectric pattern formation
    if bioelectric_signal is not None:
        self.membrane_potential = (
            0.9 * self.membrane_potential + 0.1 * bioelectric_signal[: self.params.n_genes]
        )
    # Internal bioelectric dynamics (gap junction diffusion)
    diffusion = np.roll(self.membrane_potential, 1) - self.membrane_potential
    self.membrane_potential += diffusion * self.params.bioelectric_coupling * dt

    # 7. Generate output bitstreams
    output_probs = self.protein_levels
    rands = np.random.random((self.params.n_genes, self.params.bitstream_length))
    output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

    return {
        "expression_levels": self.expression_levels.copy(),
        "protein_levels": self.protein_levels.copy(),
        "chromatin_openness": self.chromatin_openness.copy(),
        "methylation": self.methylation.copy(),
        "spin_polarization": self.spin_polarization.copy(),
        "membrane_potential": self.membrane_potential.copy(),
        "output_bitstreams": output_bitstreams,
    }

get_global_metric()

Return the global genomic activity metric.

Source code in src/sc_neurocore/scpn/layers/l3_genomic.py
207
208
209
def get_global_metric(self) -> float:
    """Return the global genomic activity metric."""
    return float(np.mean(self.expression_levels))

get_ciss_coherence()

Return CISS spin coherence metric.

Source code in src/sc_neurocore/scpn/layers/l3_genomic.py
211
212
213
def get_ciss_coherence(self) -> float:
    """Return CISS spin coherence metric."""
    return float(np.abs(np.mean(self.spin_polarization)))

L4_CellularLayer

Stochastic implementation of the Cellular-Tissue Synchronization Layer.

Models collective cellular behavior, gap junction coupling, and tissue-level pattern formation using bitstream representations.

Source code in src/sc_neurocore/scpn/layers/l4_cellular.py
 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
class L4_CellularLayer:
    """
    Stochastic implementation of the Cellular-Tissue Synchronization Layer.

    Models collective cellular behavior, gap junction coupling, and
    tissue-level pattern formation using bitstream representations.
    """

    def __init__(self, params: Optional[L4_StochasticParameters] = None):
        self.params = params or L4_StochasticParameters()
        self.n_cells = self.params.grid_size[0] * self.params.grid_size[1]

        # Oscillator phases (Kuramoto-like model)
        self.phases = np.random.random(self.n_cells) * 2 * np.pi

        # Oscillator amplitudes
        self.amplitudes = np.ones(self.n_cells) * 0.5

        # Calcium concentrations
        self.calcium = np.random.random(self.n_cells) * 0.3

        # Gap junction states (0 = closed, 1 = open)
        self.gap_junctions = self._init_gap_junctions()

        # Tissue activity pattern
        self.activity_pattern = np.zeros(self.n_cells)

        # Build neighbor connectivity matrix
        self.neighbors = self._build_neighbor_matrix()

    def _init_gap_junctions(self) -> np.ndarray[Any, Any]:
        """Initialize gap junction connectivity."""
        # Random initial state with bias toward open
        return (np.random.random(self.n_cells) > 0.3).astype(np.float32)

    def _build_neighbor_matrix(self) -> np.ndarray[Any, Any]:
        """Build 2D grid neighbor connectivity matrix."""
        h, w = self.params.grid_size
        n = self.n_cells
        neighbors = np.zeros((n, n), dtype=np.float32)

        for i in range(n):
            row, col = i // w, i % w
            # 4-connectivity (von Neumann)
            for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                nr, nc = row + dr, col + dc
                if 0 <= nr < h and 0 <= nc < w:
                    j = nr * w + nc
                    neighbors[i, j] = 1.0

        return neighbors

    def step(
        self,
        dt: float,
        l3_input: Optional[Dict[str, Any]] = None,
        external_stimulus: Optional[np.ndarray[Any, Any]] = None,
    ) -> Dict[str, np.ndarray[Any, Any]]:
        """
        Advance the layer by one time step.

        Args:
            dt: Time step in seconds.
            l3_input: Genomic layer output (protein levels).
            external_stimulus: External stimulation pattern.

        Returns:
            Dict with phases, calcium, synchronization, output_bitstreams
        """
        # 1. Kuramoto oscillator dynamics
        # dθ/dt = ω + K/N * Σ sin(θ_j - θ_i)
        phase_diffs = np.sin(self.phases[None, :] - self.phases[:, None])
        coupling_term = (
            self.params.coupling_strength
            * np.sum(self.neighbors * phase_diffs, axis=1)
            / np.maximum(np.sum(self.neighbors, axis=1), 1)
        )

        noise = self.params.noise_amplitude * np.random.normal(0, 1, self.n_cells)

        self.phases += (2 * np.pi * self.params.natural_frequency + coupling_term + noise) * dt
        self.phases = self.phases % (2 * np.pi)

        # 2. Calcium wave dynamics
        # Diffusion via gap junctions
        ca_diff = np.zeros(self.n_cells)
        for i in range(self.n_cells):
            neighbor_indices = np.where(self.neighbors[i] > 0)[0]
            if len(neighbor_indices) > 0:
                # Diffusion weighted by gap junction state
                for j in neighbor_indices:
                    gj_state = (self.gap_junctions[i] + self.gap_junctions[j]) / 2
                    ca_diff[i] += gj_state * (self.calcium[j] - self.calcium[i])

        self.calcium += (
            self.params.ca_diffusion_rate * ca_diff - self.params.ca_decay_rate * self.calcium
        ) * dt

        # Calcium-induced calcium release (CICR)
        cicr_mask = self.calcium > self.params.ca_release_threshold
        self.calcium = np.where(cicr_mask, self.calcium + 0.2, self.calcium)
        self.calcium = np.clip(self.calcium, 0.0, 1.0)

        # 3. Gap junction dynamics
        # Gap junctions modulated by calcium and coupling
        gj_noise = self.params.gap_junction_noise * np.random.normal(0, 1, self.n_cells)
        self.gap_junctions = np.clip(
            self.gap_junctions + gj_noise * dt + 0.1 * (1 - self.calcium) * dt, 0.0, 1.0
        )

        # 4. Genomic input coupling (L3 proteins modulate oscillators)
        if l3_input is not None and "protein_levels" in l3_input:
            protein_mean = np.mean(l3_input["protein_levels"])
            self.amplitudes = np.clip(
                self.amplitudes + protein_mean * self.params.genomic_coupling * dt, 0.1, 1.0
            )

        # 5. External stimulus
        if external_stimulus is not None:
            self.calcium += external_stimulus[: self.n_cells] * dt
            self.calcium = np.clip(self.calcium, 0.0, 1.0)

        # 6. Compute activity pattern
        self.activity_pattern = self.amplitudes * (1 + np.cos(self.phases)) / 2

        # 7. Compute synchronization order parameter
        order_parameter = np.abs(np.mean(np.exp(1j * self.phases)))

        # 8. Generate output bitstreams
        output_probs = self.activity_pattern
        rands = np.random.random((self.n_cells, self.params.bitstream_length))
        output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

        return {
            "phases": self.phases.copy(),
            "amplitudes": self.amplitudes.copy(),
            "calcium": self.calcium.copy(),
            "gap_junctions": self.gap_junctions.copy(),
            "activity_pattern": self.activity_pattern.copy(),
            "synchronization": order_parameter,
            "output_bitstreams": output_bitstreams,
        }

    def get_global_metric(self) -> float:
        """Return the global synchronization metric (Kuramoto order parameter)."""
        return float(np.abs(np.mean(np.exp(1j * self.phases))))

    def get_tissue_pattern(self) -> np.ndarray[Any, Any]:
        """Return 2D tissue activity pattern."""
        return self.activity_pattern.reshape(self.params.grid_size)

step(dt, l3_input=None, external_stimulus=None)

Advance the layer by one time step.

Parameters:

Name Type Description Default
dt float

Time step in seconds.

required
l3_input Optional[Dict[str, Any]]

Genomic layer output (protein levels).

None
external_stimulus Optional[ndarray[Any, Any]]

External stimulation pattern.

None

Returns:

Type Description
Dict[str, ndarray[Any, Any]]

Dict with phases, calcium, synchronization, output_bitstreams

Source code in src/sc_neurocore/scpn/layers/l4_cellular.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def step(
    self,
    dt: float,
    l3_input: Optional[Dict[str, Any]] = None,
    external_stimulus: Optional[np.ndarray[Any, Any]] = None,
) -> Dict[str, np.ndarray[Any, Any]]:
    """
    Advance the layer by one time step.

    Args:
        dt: Time step in seconds.
        l3_input: Genomic layer output (protein levels).
        external_stimulus: External stimulation pattern.

    Returns:
        Dict with phases, calcium, synchronization, output_bitstreams
    """
    # 1. Kuramoto oscillator dynamics
    # dθ/dt = ω + K/N * Σ sin(θ_j - θ_i)
    phase_diffs = np.sin(self.phases[None, :] - self.phases[:, None])
    coupling_term = (
        self.params.coupling_strength
        * np.sum(self.neighbors * phase_diffs, axis=1)
        / np.maximum(np.sum(self.neighbors, axis=1), 1)
    )

    noise = self.params.noise_amplitude * np.random.normal(0, 1, self.n_cells)

    self.phases += (2 * np.pi * self.params.natural_frequency + coupling_term + noise) * dt
    self.phases = self.phases % (2 * np.pi)

    # 2. Calcium wave dynamics
    # Diffusion via gap junctions
    ca_diff = np.zeros(self.n_cells)
    for i in range(self.n_cells):
        neighbor_indices = np.where(self.neighbors[i] > 0)[0]
        if len(neighbor_indices) > 0:
            # Diffusion weighted by gap junction state
            for j in neighbor_indices:
                gj_state = (self.gap_junctions[i] + self.gap_junctions[j]) / 2
                ca_diff[i] += gj_state * (self.calcium[j] - self.calcium[i])

    self.calcium += (
        self.params.ca_diffusion_rate * ca_diff - self.params.ca_decay_rate * self.calcium
    ) * dt

    # Calcium-induced calcium release (CICR)
    cicr_mask = self.calcium > self.params.ca_release_threshold
    self.calcium = np.where(cicr_mask, self.calcium + 0.2, self.calcium)
    self.calcium = np.clip(self.calcium, 0.0, 1.0)

    # 3. Gap junction dynamics
    # Gap junctions modulated by calcium and coupling
    gj_noise = self.params.gap_junction_noise * np.random.normal(0, 1, self.n_cells)
    self.gap_junctions = np.clip(
        self.gap_junctions + gj_noise * dt + 0.1 * (1 - self.calcium) * dt, 0.0, 1.0
    )

    # 4. Genomic input coupling (L3 proteins modulate oscillators)
    if l3_input is not None and "protein_levels" in l3_input:
        protein_mean = np.mean(l3_input["protein_levels"])
        self.amplitudes = np.clip(
            self.amplitudes + protein_mean * self.params.genomic_coupling * dt, 0.1, 1.0
        )

    # 5. External stimulus
    if external_stimulus is not None:
        self.calcium += external_stimulus[: self.n_cells] * dt
        self.calcium = np.clip(self.calcium, 0.0, 1.0)

    # 6. Compute activity pattern
    self.activity_pattern = self.amplitudes * (1 + np.cos(self.phases)) / 2

    # 7. Compute synchronization order parameter
    order_parameter = np.abs(np.mean(np.exp(1j * self.phases)))

    # 8. Generate output bitstreams
    output_probs = self.activity_pattern
    rands = np.random.random((self.n_cells, self.params.bitstream_length))
    output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

    return {
        "phases": self.phases.copy(),
        "amplitudes": self.amplitudes.copy(),
        "calcium": self.calcium.copy(),
        "gap_junctions": self.gap_junctions.copy(),
        "activity_pattern": self.activity_pattern.copy(),
        "synchronization": order_parameter,
        "output_bitstreams": output_bitstreams,
    }

get_global_metric()

Return the global synchronization metric (Kuramoto order parameter).

Source code in src/sc_neurocore/scpn/layers/l4_cellular.py
203
204
205
def get_global_metric(self) -> float:
    """Return the global synchronization metric (Kuramoto order parameter)."""
    return float(np.abs(np.mean(np.exp(1j * self.phases))))

get_tissue_pattern()

Return 2D tissue activity pattern.

Source code in src/sc_neurocore/scpn/layers/l4_cellular.py
207
208
209
def get_tissue_pattern(self) -> np.ndarray[Any, Any]:
    """Return 2D tissue activity pattern."""
    return self.activity_pattern.reshape(self.params.grid_size)

L5_OrganismalLayer

Stochastic implementation of the Organismal-Psychoemotional Layer.

Models whole-organism integration, autonomic regulation, and emotional dynamics using bitstream representations.

Source code in src/sc_neurocore/scpn/layers/l5_organismal.py
 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
class L5_OrganismalLayer:
    """
    Stochastic implementation of the Organismal-Psychoemotional Layer.

    Models whole-organism integration, autonomic regulation, and
    emotional dynamics using bitstream representations.
    """

    # Emotional dimension indices
    VALENCE = 0  # Pleasant-Unpleasant
    AROUSAL = 1  # Activated-Deactivated
    DOMINANCE = 2  # Dominant-Submissive
    APPROACH = 3  # Approach-Avoid
    CERTAINTY = 4  # Certain-Uncertain
    ATTENTION = 5  # Focused-Diffuse
    FAIRNESS = 6  # Fair-Unfair
    SAFETY = 7  # Safe-Threatened

    def __init__(self, params: Optional[L5_StochasticParameters] = None):
        self.params = params or L5_StochasticParameters()

        # Emotional state vector
        self.emotional_state = np.zeros(self.params.n_emotional_dims)
        self.emotional_state[self.VALENCE] = 0.5  # Neutral valence
        self.emotional_state[self.AROUSAL] = 0.3  # Low arousal baseline
        self.emotional_state[self.SAFETY] = 0.7  # Safe baseline

        # Autonomic nervous system state
        self.sympathetic = self.params.sympathetic_baseline
        self.parasympathetic = self.params.parasympathetic_baseline

        # Heart dynamics
        self.heart_rate = self.params.base_heart_rate
        self.hrv_phase = 0.0
        self.rr_intervals: List[float] = []

        # Interoceptive state (body sense)
        self.interoceptive_state = np.random.random(self.params.n_autonomic_nodes) * 0.3

        # Attractor basins for emotional states
        self.attractors = self._init_emotional_attractors()

        # Time tracking
        self.time = 0.0

    def _init_emotional_attractors(self) -> np.ndarray[Any, Any]:
        """Initialize emotional attractor states."""
        # Define stable emotional configurations
        attractors = np.array(
            [
                [0.8, 0.3, 0.6, 0.7, 0.7, 0.5, 0.6, 0.8],  # Joy/contentment
                [0.2, 0.8, 0.3, 0.2, 0.3, 0.8, 0.3, 0.2],  # Fear/anxiety
                [0.2, 0.7, 0.7, 0.8, 0.6, 0.7, 0.2, 0.4],  # Anger
                [0.3, 0.2, 0.2, 0.2, 0.4, 0.3, 0.5, 0.5],  # Sadness
                [0.5, 0.4, 0.5, 0.5, 0.5, 0.5, 0.5, 0.6],  # Neutral
            ]
        )
        return attractors

    def step(
        self,
        dt: float,
        l4_input: Optional[Dict[str, Any]] = None,
        external_event: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, np.ndarray[Any, Any]]:
        """
        Advance the layer by one time step.

        Args:
            dt: Time step in seconds.
            l4_input: Cellular layer output (synchronization).
            external_event: External emotional trigger {valence, arousal, ...}.

        Returns:
            Dict with emotional_state, autonomic, heart_rate, output_bitstreams
        """
        self.time += dt

        # 1. Process external emotional events
        if external_event is not None:
            for dim, value in external_event.items():
                if isinstance(dim, int) and 0 <= dim < self.params.n_emotional_dims:
                    self.emotional_state[dim] += value * 0.3

        # 2. Attractor dynamics (emotional states converge to stable patterns)
        # Find nearest attractor
        distances = np.linalg.norm(self.attractors - self.emotional_state, axis=1)
        nearest_attractor = self.attractors[np.argmin(distances)]

        # Pull toward attractor
        self.emotional_state += (
            self.params.attractor_strength * (nearest_attractor - self.emotional_state) * dt
        )

        # Add noise
        self.emotional_state += (
            self.params.emotional_noise * np.random.normal(0, 1, self.params.n_emotional_dims) * dt
        )

        # Decay toward baseline
        baseline = np.array([0.5, 0.3, 0.5, 0.5, 0.5, 0.5, 0.5, 0.6])
        self.emotional_state += self.params.emotional_decay * (baseline - self.emotional_state) * dt

        self.emotional_state = np.clip(self.emotional_state, 0.0, 1.0)

        # 3. Autonomic nervous system dynamics
        # Sympathetic driven by arousal and threat
        target_symp = (
            self.emotional_state[self.AROUSAL] * 0.5 + (1 - self.emotional_state[self.SAFETY]) * 0.5
        )
        # Parasympathetic driven by valence and safety
        target_para = (
            self.emotional_state[self.VALENCE] * 0.3 + self.emotional_state[self.SAFETY] * 0.7
        )

        tau = self.params.autonomic_time_constant
        self.sympathetic += (target_symp - self.sympathetic) * dt / tau
        self.parasympathetic += (target_para - self.parasympathetic) * dt / tau

        self.sympathetic = np.clip(self.sympathetic, 0.0, 1.0)
        self.parasympathetic = np.clip(self.parasympathetic, 0.0, 1.0)

        # 4. Heart rate and HRV
        # RSA (Respiratory Sinus Arrhythmia)
        self.hrv_phase += 2 * np.pi * self.params.respiratory_frequency * dt
        rsa_component = self.params.hrv_amplitude * np.sin(self.hrv_phase) * self.parasympathetic

        # Sympathetic raises HR, parasympathetic lowers it
        target_hr = self.params.base_heart_rate + 20 * self.sympathetic - 15 * self.parasympathetic
        self.heart_rate += (target_hr - self.heart_rate) * dt * 0.5
        self.heart_rate += rsa_component * 10  # RSA effect

        # Track RR intervals
        rr = 60000.0 / self.heart_rate  # ms
        self.rr_intervals.append(rr)
        if len(self.rr_intervals) > 100:
            self.rr_intervals.pop(0)

        # 5. Cellular input coupling (L4 synchronization affects coherence)
        if l4_input is not None and "synchronization" in l4_input:
            sync = l4_input["synchronization"]
            # High cellular sync improves emotional stability
            self.emotional_state[self.CERTAINTY] += sync * self.params.cellular_coupling * dt
            self.emotional_state = np.clip(self.emotional_state, 0.0, 1.0)

        # 6. Update interoceptive state
        self.interoceptive_state = (
            0.8 * self.interoceptive_state
            + 0.2
            * np.tile(
                [self.sympathetic, self.parasympathetic, self.heart_rate / 100],
                self.params.n_autonomic_nodes // 3 + 1,
            )[: self.params.n_autonomic_nodes]
        )

        # 7. Generate output bitstreams
        output_probs = np.concatenate(
            [self.emotional_state, [self.sympathetic, self.parasympathetic, self.heart_rate / 100]]
        )
        output_probs = np.tile(output_probs, self.params.n_autonomic_nodes // len(output_probs) + 1)
        output_probs = output_probs[: self.params.n_autonomic_nodes]

        rands = np.random.random((self.params.n_autonomic_nodes, self.params.bitstream_length))
        output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

        return {
            "emotional_state": self.emotional_state.copy(),
            "sympathetic": self.sympathetic,  # type: ignore
            "parasympathetic": self.parasympathetic,  # type: ignore
            "heart_rate": self.heart_rate,  # type: ignore
            "hrv_rmssd": self._compute_rmssd(),  # type: ignore
            "interoceptive_state": self.interoceptive_state.copy(),
            "output_bitstreams": output_bitstreams,
        }

    def _compute_rmssd(self) -> float:
        """Compute RMSSD (root mean square of successive differences)."""
        if len(self.rr_intervals) < 2:
            return 0.0
        rr = np.array(self.rr_intervals)
        diff = np.diff(rr)
        return float(np.sqrt(np.mean(diff**2)))

    def get_global_metric(self) -> float:
        """Return the global organismal coherence metric."""
        # Combine HRV coherence with emotional stability
        hrv_coherence = self._compute_rmssd() / 100  # Normalize
        emotional_stability = 1.0 - np.std(self.emotional_state)
        return float(0.5 * hrv_coherence + 0.5 * emotional_stability)

    def get_emotional_valence(self) -> float:
        """Return current emotional valence."""
        return float(self.emotional_state[self.VALENCE])

step(dt, l4_input=None, external_event=None)

Advance the layer by one time step.

Parameters:

Name Type Description Default
dt float

Time step in seconds.

required
l4_input Optional[Dict[str, Any]]

Cellular layer output (synchronization).

None
external_event Optional[Dict[str, Any]]

External emotional trigger {valence, arousal, ...}.

None

Returns:

Type Description
Dict[str, ndarray[Any, Any]]

Dict with emotional_state, autonomic, heart_rate, output_bitstreams

Source code in src/sc_neurocore/scpn/layers/l5_organismal.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
def step(
    self,
    dt: float,
    l4_input: Optional[Dict[str, Any]] = None,
    external_event: Optional[Dict[str, Any]] = None,
) -> Dict[str, np.ndarray[Any, Any]]:
    """
    Advance the layer by one time step.

    Args:
        dt: Time step in seconds.
        l4_input: Cellular layer output (synchronization).
        external_event: External emotional trigger {valence, arousal, ...}.

    Returns:
        Dict with emotional_state, autonomic, heart_rate, output_bitstreams
    """
    self.time += dt

    # 1. Process external emotional events
    if external_event is not None:
        for dim, value in external_event.items():
            if isinstance(dim, int) and 0 <= dim < self.params.n_emotional_dims:
                self.emotional_state[dim] += value * 0.3

    # 2. Attractor dynamics (emotional states converge to stable patterns)
    # Find nearest attractor
    distances = np.linalg.norm(self.attractors - self.emotional_state, axis=1)
    nearest_attractor = self.attractors[np.argmin(distances)]

    # Pull toward attractor
    self.emotional_state += (
        self.params.attractor_strength * (nearest_attractor - self.emotional_state) * dt
    )

    # Add noise
    self.emotional_state += (
        self.params.emotional_noise * np.random.normal(0, 1, self.params.n_emotional_dims) * dt
    )

    # Decay toward baseline
    baseline = np.array([0.5, 0.3, 0.5, 0.5, 0.5, 0.5, 0.5, 0.6])
    self.emotional_state += self.params.emotional_decay * (baseline - self.emotional_state) * dt

    self.emotional_state = np.clip(self.emotional_state, 0.0, 1.0)

    # 3. Autonomic nervous system dynamics
    # Sympathetic driven by arousal and threat
    target_symp = (
        self.emotional_state[self.AROUSAL] * 0.5 + (1 - self.emotional_state[self.SAFETY]) * 0.5
    )
    # Parasympathetic driven by valence and safety
    target_para = (
        self.emotional_state[self.VALENCE] * 0.3 + self.emotional_state[self.SAFETY] * 0.7
    )

    tau = self.params.autonomic_time_constant
    self.sympathetic += (target_symp - self.sympathetic) * dt / tau
    self.parasympathetic += (target_para - self.parasympathetic) * dt / tau

    self.sympathetic = np.clip(self.sympathetic, 0.0, 1.0)
    self.parasympathetic = np.clip(self.parasympathetic, 0.0, 1.0)

    # 4. Heart rate and HRV
    # RSA (Respiratory Sinus Arrhythmia)
    self.hrv_phase += 2 * np.pi * self.params.respiratory_frequency * dt
    rsa_component = self.params.hrv_amplitude * np.sin(self.hrv_phase) * self.parasympathetic

    # Sympathetic raises HR, parasympathetic lowers it
    target_hr = self.params.base_heart_rate + 20 * self.sympathetic - 15 * self.parasympathetic
    self.heart_rate += (target_hr - self.heart_rate) * dt * 0.5
    self.heart_rate += rsa_component * 10  # RSA effect

    # Track RR intervals
    rr = 60000.0 / self.heart_rate  # ms
    self.rr_intervals.append(rr)
    if len(self.rr_intervals) > 100:
        self.rr_intervals.pop(0)

    # 5. Cellular input coupling (L4 synchronization affects coherence)
    if l4_input is not None and "synchronization" in l4_input:
        sync = l4_input["synchronization"]
        # High cellular sync improves emotional stability
        self.emotional_state[self.CERTAINTY] += sync * self.params.cellular_coupling * dt
        self.emotional_state = np.clip(self.emotional_state, 0.0, 1.0)

    # 6. Update interoceptive state
    self.interoceptive_state = (
        0.8 * self.interoceptive_state
        + 0.2
        * np.tile(
            [self.sympathetic, self.parasympathetic, self.heart_rate / 100],
            self.params.n_autonomic_nodes // 3 + 1,
        )[: self.params.n_autonomic_nodes]
    )

    # 7. Generate output bitstreams
    output_probs = np.concatenate(
        [self.emotional_state, [self.sympathetic, self.parasympathetic, self.heart_rate / 100]]
    )
    output_probs = np.tile(output_probs, self.params.n_autonomic_nodes // len(output_probs) + 1)
    output_probs = output_probs[: self.params.n_autonomic_nodes]

    rands = np.random.random((self.params.n_autonomic_nodes, self.params.bitstream_length))
    output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

    return {
        "emotional_state": self.emotional_state.copy(),
        "sympathetic": self.sympathetic,  # type: ignore
        "parasympathetic": self.parasympathetic,  # type: ignore
        "heart_rate": self.heart_rate,  # type: ignore
        "hrv_rmssd": self._compute_rmssd(),  # type: ignore
        "interoceptive_state": self.interoceptive_state.copy(),
        "output_bitstreams": output_bitstreams,
    }

get_global_metric()

Return the global organismal coherence metric.

Source code in src/sc_neurocore/scpn/layers/l5_organismal.py
245
246
247
248
249
250
def get_global_metric(self) -> float:
    """Return the global organismal coherence metric."""
    # Combine HRV coherence with emotional stability
    hrv_coherence = self._compute_rmssd() / 100  # Normalize
    emotional_stability = 1.0 - np.std(self.emotional_state)
    return float(0.5 * hrv_coherence + 0.5 * emotional_stability)

get_emotional_valence()

Return current emotional valence.

Source code in src/sc_neurocore/scpn/layers/l5_organismal.py
252
253
254
def get_emotional_valence(self) -> float:
    """Return current emotional valence."""
    return float(self.emotional_state[self.VALENCE])

L6_EcologicalLayer

Stochastic implementation of the Ecological-Planetary Layer.

Models planetary-scale electromagnetic fields, Schumann resonances, and biospheric network dynamics using bitstream representations.

Source code in src/sc_neurocore/scpn/layers/l6_ecological.py
 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
class L6_EcologicalLayer:
    """
    Stochastic implementation of the Ecological-Planetary Layer.

    Models planetary-scale electromagnetic fields, Schumann resonances,
    and biospheric network dynamics using bitstream representations.
    """

    def __init__(self, params: Optional[L6_StochasticParameters] = None):
        self.params = params or L6_StochasticParameters()

        # Schumann resonance field (superposition of modes)
        self.schumann_phases = np.zeros(len(self.params.schumann_frequencies))
        self.schumann_amplitudes = np.ones(len(self.params.schumann_frequencies))

        # Geomagnetic field state
        self.geomag_field = np.ones(self.params.n_field_nodes) * self.params.geomag_baseline

        # Circadian phase
        self.circadian_phase = 0.0

        # Biospheric network state (collective field)
        self.biospheric_field = np.random.random(self.params.n_field_nodes) * 0.3

        # Planetary consciousness coherence
        self.planetary_coherence = 0.5

        # History for temporal patterns
        self.history: List[Dict[str, Any]] = []

        # Time tracking
        self.time = 0.0

    def step(
        self,
        dt: float,
        l5_input: Optional[Dict[str, Any]] = None,
        solar_activity: float = 0.5,
        lunar_phase: float = 0.0,
    ) -> Dict[str, np.ndarray[Any, Any]]:
        """
        Advance the layer by one time step.

        Args:
            dt: Time step in seconds.
            l5_input: Organismal layer output (emotional coherence).
            solar_activity: Solar activity index (0-1).
            lunar_phase: Lunar phase (0 to 2π).

        Returns:
            Dict with schumann_field, geomag, circadian, output_bitstreams
        """
        self.time += dt

        # 1. Schumann resonance dynamics
        for i, freq in enumerate(self.params.schumann_frequencies):
            self.schumann_phases[i] += 2 * np.pi * freq * dt
            self.schumann_phases[i] = self.schumann_phases[i] % (2 * np.pi)

        # Compute Schumann field as superposition
        schumann_signal = np.zeros(self.params.n_field_nodes)
        for i, freq in enumerate(self.params.schumann_frequencies):
            spatial_pattern = np.sin(np.linspace(0, 2 * np.pi * (i + 1), self.params.n_field_nodes))
            schumann_signal += (
                self.schumann_amplitudes[i]
                * self.params.schumann_amplitude
                * np.cos(self.schumann_phases[i])
                * spatial_pattern
            )

        # Add noise
        schumann_signal += self.params.schumann_noise * np.random.normal(
            0, 1, self.params.n_field_nodes
        )

        # Normalize to [0, 1]
        schumann_field = (schumann_signal - schumann_signal.min()) / (
            schumann_signal.max() - schumann_signal.min() + 1e-8
        )

        # 2. Geomagnetic field dynamics
        # Solar activity modulates geomagnetic storms
        storm_factor = 1.0 + 0.5 * (solar_activity - 0.5)
        geomag_variation = (
            self.params.geomag_variation
            * storm_factor
            * np.random.normal(0, 1, self.params.n_field_nodes)
        )
        self.geomag_field = np.clip(
            self.geomag_field + geomag_variation * dt,
            self.params.geomag_baseline * 0.5,
            self.params.geomag_baseline * 1.5,
        )

        # 3. Circadian rhythm
        self.circadian_phase += 2 * np.pi * dt / self.params.circadian_period
        self.circadian_phase = self.circadian_phase % (2 * np.pi)
        circadian_signal = 0.5 + self.params.circadian_amplitude * np.cos(self.circadian_phase)

        # 4. Biospheric network dynamics
        # Coupling between nodes
        network_coupling = np.zeros(self.params.n_field_nodes)
        for i in range(self.params.n_field_nodes):
            neighbors = [(i - 1) % self.params.n_field_nodes, (i + 1) % self.params.n_field_nodes]
            neighbor_mean = np.mean([self.biospheric_field[j] for j in neighbors])
            network_coupling[i] = neighbor_mean - self.biospheric_field[i]

        self.biospheric_field += (
            self.params.network_coupling * network_coupling
            + self.params.network_noise * np.random.normal(0, 1, self.params.n_field_nodes)
        ) * dt

        # Modulate by Schumann and circadian
        self.biospheric_field *= (0.9 + 0.1 * schumann_field) * (0.8 + 0.2 * circadian_signal)
        self.biospheric_field = np.clip(self.biospheric_field, 0.0, 1.0)

        # 5. Organismal coupling (L5 collective emotional state affects field)
        if l5_input is not None:
            if "emotional_state" in l5_input:
                emotional_coherence = np.mean(l5_input["emotional_state"])
                self.biospheric_field += (
                    self.params.organismal_coupling * (emotional_coherence - 0.5) * dt
                )
                self.biospheric_field = np.clip(self.biospheric_field, 0.0, 1.0)

        # 6. Lunar phase modulation
        lunar_factor = 0.5 + 0.5 * np.cos(lunar_phase)
        self.schumann_amplitudes = np.ones(len(self.params.schumann_frequencies)) * (
            0.8 + 0.2 * lunar_factor
        )

        # 7. Compute planetary coherence
        self.planetary_coherence = float(
            np.abs(np.mean(np.exp(1j * 2 * np.pi * self.biospheric_field)))
        )

        # 8. Generate output bitstreams
        output_probs = self.biospheric_field * circadian_signal
        rands = np.random.random((self.params.n_field_nodes, self.params.bitstream_length))
        output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

        # Store history
        result = {
            "schumann_field": schumann_field,
            "schumann_phases": self.schumann_phases.copy(),
            "geomag_field": self.geomag_field.copy(),
            "circadian_phase": self.circadian_phase,
            "circadian_signal": circadian_signal,
            "biospheric_field": self.biospheric_field.copy(),
            "planetary_coherence": self.planetary_coherence,
            "output_bitstreams": output_bitstreams,
        }

        self.history.append(
            {
                "time": self.time,
                "coherence": self.planetary_coherence,
                "schumann_power": float(np.mean(schumann_field**2)),
            }
        )
        if len(self.history) > 100:
            self.history.pop(0)

        return result

    def get_global_metric(self) -> float:
        """Return the global planetary coherence metric."""
        return self.planetary_coherence

    def get_schumann_spectrum(self) -> Dict[float, float]:
        """Return current Schumann resonance spectrum."""
        return {
            freq: float(amp * np.cos(phase))
            for freq, amp, phase in zip(
                self.params.schumann_frequencies, self.schumann_amplitudes, self.schumann_phases
            )
        }

    def get_circadian_time(self) -> float:
        """Return current circadian time (0-24 hours)."""
        return (self.circadian_phase / (2 * np.pi)) * 24.0

step(dt, l5_input=None, solar_activity=0.5, lunar_phase=0.0)

Advance the layer by one time step.

Parameters:

Name Type Description Default
dt float

Time step in seconds.

required
l5_input Optional[Dict[str, Any]]

Organismal layer output (emotional coherence).

None
solar_activity float

Solar activity index (0-1).

0.5
lunar_phase float

Lunar phase (0 to 2π).

0.0

Returns:

Type Description
Dict[str, ndarray[Any, Any]]

Dict with schumann_field, geomag, circadian, output_bitstreams

Source code in src/sc_neurocore/scpn/layers/l6_ecological.py
 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def step(
    self,
    dt: float,
    l5_input: Optional[Dict[str, Any]] = None,
    solar_activity: float = 0.5,
    lunar_phase: float = 0.0,
) -> Dict[str, np.ndarray[Any, Any]]:
    """
    Advance the layer by one time step.

    Args:
        dt: Time step in seconds.
        l5_input: Organismal layer output (emotional coherence).
        solar_activity: Solar activity index (0-1).
        lunar_phase: Lunar phase (0 to 2π).

    Returns:
        Dict with schumann_field, geomag, circadian, output_bitstreams
    """
    self.time += dt

    # 1. Schumann resonance dynamics
    for i, freq in enumerate(self.params.schumann_frequencies):
        self.schumann_phases[i] += 2 * np.pi * freq * dt
        self.schumann_phases[i] = self.schumann_phases[i] % (2 * np.pi)

    # Compute Schumann field as superposition
    schumann_signal = np.zeros(self.params.n_field_nodes)
    for i, freq in enumerate(self.params.schumann_frequencies):
        spatial_pattern = np.sin(np.linspace(0, 2 * np.pi * (i + 1), self.params.n_field_nodes))
        schumann_signal += (
            self.schumann_amplitudes[i]
            * self.params.schumann_amplitude
            * np.cos(self.schumann_phases[i])
            * spatial_pattern
        )

    # Add noise
    schumann_signal += self.params.schumann_noise * np.random.normal(
        0, 1, self.params.n_field_nodes
    )

    # Normalize to [0, 1]
    schumann_field = (schumann_signal - schumann_signal.min()) / (
        schumann_signal.max() - schumann_signal.min() + 1e-8
    )

    # 2. Geomagnetic field dynamics
    # Solar activity modulates geomagnetic storms
    storm_factor = 1.0 + 0.5 * (solar_activity - 0.5)
    geomag_variation = (
        self.params.geomag_variation
        * storm_factor
        * np.random.normal(0, 1, self.params.n_field_nodes)
    )
    self.geomag_field = np.clip(
        self.geomag_field + geomag_variation * dt,
        self.params.geomag_baseline * 0.5,
        self.params.geomag_baseline * 1.5,
    )

    # 3. Circadian rhythm
    self.circadian_phase += 2 * np.pi * dt / self.params.circadian_period
    self.circadian_phase = self.circadian_phase % (2 * np.pi)
    circadian_signal = 0.5 + self.params.circadian_amplitude * np.cos(self.circadian_phase)

    # 4. Biospheric network dynamics
    # Coupling between nodes
    network_coupling = np.zeros(self.params.n_field_nodes)
    for i in range(self.params.n_field_nodes):
        neighbors = [(i - 1) % self.params.n_field_nodes, (i + 1) % self.params.n_field_nodes]
        neighbor_mean = np.mean([self.biospheric_field[j] for j in neighbors])
        network_coupling[i] = neighbor_mean - self.biospheric_field[i]

    self.biospheric_field += (
        self.params.network_coupling * network_coupling
        + self.params.network_noise * np.random.normal(0, 1, self.params.n_field_nodes)
    ) * dt

    # Modulate by Schumann and circadian
    self.biospheric_field *= (0.9 + 0.1 * schumann_field) * (0.8 + 0.2 * circadian_signal)
    self.biospheric_field = np.clip(self.biospheric_field, 0.0, 1.0)

    # 5. Organismal coupling (L5 collective emotional state affects field)
    if l5_input is not None:
        if "emotional_state" in l5_input:
            emotional_coherence = np.mean(l5_input["emotional_state"])
            self.biospheric_field += (
                self.params.organismal_coupling * (emotional_coherence - 0.5) * dt
            )
            self.biospheric_field = np.clip(self.biospheric_field, 0.0, 1.0)

    # 6. Lunar phase modulation
    lunar_factor = 0.5 + 0.5 * np.cos(lunar_phase)
    self.schumann_amplitudes = np.ones(len(self.params.schumann_frequencies)) * (
        0.8 + 0.2 * lunar_factor
    )

    # 7. Compute planetary coherence
    self.planetary_coherence = float(
        np.abs(np.mean(np.exp(1j * 2 * np.pi * self.biospheric_field)))
    )

    # 8. Generate output bitstreams
    output_probs = self.biospheric_field * circadian_signal
    rands = np.random.random((self.params.n_field_nodes, self.params.bitstream_length))
    output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

    # Store history
    result = {
        "schumann_field": schumann_field,
        "schumann_phases": self.schumann_phases.copy(),
        "geomag_field": self.geomag_field.copy(),
        "circadian_phase": self.circadian_phase,
        "circadian_signal": circadian_signal,
        "biospheric_field": self.biospheric_field.copy(),
        "planetary_coherence": self.planetary_coherence,
        "output_bitstreams": output_bitstreams,
    }

    self.history.append(
        {
            "time": self.time,
            "coherence": self.planetary_coherence,
            "schumann_power": float(np.mean(schumann_field**2)),
        }
    )
    if len(self.history) > 100:
        self.history.pop(0)

    return result

get_global_metric()

Return the global planetary coherence metric.

Source code in src/sc_neurocore/scpn/layers/l6_ecological.py
228
229
230
def get_global_metric(self) -> float:
    """Return the global planetary coherence metric."""
    return self.planetary_coherence

get_schumann_spectrum()

Return current Schumann resonance spectrum.

Source code in src/sc_neurocore/scpn/layers/l6_ecological.py
232
233
234
235
236
237
238
239
def get_schumann_spectrum(self) -> Dict[float, float]:
    """Return current Schumann resonance spectrum."""
    return {
        freq: float(amp * np.cos(phase))
        for freq, amp, phase in zip(
            self.params.schumann_frequencies, self.schumann_amplitudes, self.schumann_phases
        )
    }

get_circadian_time()

Return current circadian time (0-24 hours).

Source code in src/sc_neurocore/scpn/layers/l6_ecological.py
241
242
243
def get_circadian_time(self) -> float:
    """Return current circadian time (0-24 hours)."""
    return (self.circadian_phase / (2 * np.pi)) * 24.0

L7_SymbolicLayer

Stochastic implementation of the Geometric-Symbolic Layer.

Models sacred geometry patterns, symbolic resonances, and acupuncture point dynamics using bitstream representations.

Source code in src/sc_neurocore/scpn/layers/l7_symbolic.py
 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
class L7_SymbolicLayer:
    """
    Stochastic implementation of the Geometric-Symbolic Layer.

    Models sacred geometry patterns, symbolic resonances, and
    acupuncture point dynamics using bitstream representations.
    """

    # Platonic solid vertex counts
    PLATONIC_VERTICES = {
        "tetrahedron": 4,
        "cube": 8,
        "octahedron": 6,
        "dodecahedron": 20,
        "icosahedron": 12,
    }

    # Fibonacci sequence for alignment
    FIBONACCI = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]

    def __init__(self, params: Optional[L7_StochasticParameters] = None):
        self.params = params or L7_StochasticParameters()

        # Symbol activation states
        self.symbol_activations = np.random.random(self.params.n_symbols) * 0.3

        # Sacred geometry metrics
        self.phi_alignment = 0.5
        self.fibonacci_alignment = 0.5
        self.metatron_flow = 0.5
        self.platonic_coherence = 0.5
        self.e8_alignment = 0.5
        self.symbolic_health = 0.5

        # Meridian states (TCM)
        self.meridian_qi = np.ones(self.params.n_meridians) * 0.5

        # Acupuncture point activations
        self.acupoint_activations = np.zeros(self.params.n_acupoints)

        # Glyph vector (normalized output)
        self.glyph_vector = np.zeros(self.params.glyph_dimensions)

        # E8 lattice representation (simplified 8D projection)
        self.e8_state = np.random.random(8) * 0.5

        # Time
        self.time = 0.0

    def step(
        self,
        dt: float,
        l6_input: Optional[Dict[str, Any]] = None,
        symbol_input: Optional[np.ndarray[Any, Any]] = None,
        acupoint_stimulus: Optional[Dict[int, float]] = None,
    ) -> Dict[str, np.ndarray[Any, Any]]:
        """
        Advance the layer by one time step.

        Args:
            dt: Time step in seconds.
            l6_input: Ecological layer output (Schumann, circadian).
            symbol_input: External symbolic input vector.
            acupoint_stimulus: Dict of {point_id: intensity} for acupuncture.

        Returns:
            Dict with glyph_vector, meridian_qi, sacred_geometry, output_bitstreams
        """
        self.time += dt

        # 1. Process symbol input
        if symbol_input is not None:
            self.symbol_activations = np.clip(
                self.symbol_activations + symbol_input[: self.params.n_symbols] * 0.2, 0.0, 1.0
            )

        # 2. Compute Phi (Golden Ratio) alignment
        # Check how close symbol ratios are to Phi
        sorted_activations = np.sort(self.symbol_activations)[::-1]
        if sorted_activations[1] > 0.01:
            ratios = sorted_activations[:-1] / (sorted_activations[1:] + 1e-8)
            phi_distances = np.abs(ratios - PHI)
            self.phi_alignment = float(np.exp(-np.mean(phi_distances)))
        else:
            self.phi_alignment = 0.5

        # 3. Compute Fibonacci alignment
        # Check if activation levels follow Fibonacci ratios
        fib_normalized = np.array(self.FIBONACCI[:8]) / self.FIBONACCI[7]
        top_8 = sorted_activations[:8]
        if np.max(top_8) > 0.01:
            top_8_norm = top_8 / (np.max(top_8) + 1e-8)
            fib_corr = np.corrcoef(top_8_norm, fib_normalized)[0, 1]
            self.fibonacci_alignment = float(max(0, (fib_corr + 1) / 2))
        else:
            self.fibonacci_alignment = 0.5

        # 4. Compute Metatron's Cube flow
        # Based on 13-sphere / 78-line connectivity pattern
        metatron_nodes = 13
        active_nodes = np.sum(self.symbol_activations[:metatron_nodes] > 0.5)
        self.metatron_flow = active_nodes / metatron_nodes
        # Add flow dynamics
        self.metatron_flow = 0.9 * self.metatron_flow + 0.1 * np.random.random()

        # 5. Compute Platonic solid coherence
        platonic_metrics = []
        for solid, vertices in self.PLATONIC_VERTICES.items():
            solid_activations = self.symbol_activations[:vertices]
            coherence = np.std(solid_activations)  # Lower std = more coherent
            platonic_metrics.append(1.0 - coherence)
        self.platonic_coherence = float(np.mean(platonic_metrics))

        # 6. E8 lattice alignment
        # Simplified: check alignment of 8D state vector with E8 root system
        # E8 has 240 roots; we use a proxy
        e8_norm = np.linalg.norm(self.e8_state)
        if e8_norm > 0:
            e8_unit = self.e8_state / e8_norm
            # Check alignment with simple E8 roots (permutations of ±1)
            simple_roots = np.eye(8)
            alignments = np.abs(np.dot(simple_roots, e8_unit))
            self.e8_alignment = float(np.max(alignments))
        else:
            self.e8_alignment = 0.5

        # Update E8 state with noise
        self.e8_state += 0.1 * np.random.normal(0, 1, 8) * dt
        self.e8_state = np.clip(self.e8_state, -1, 1)

        # 7. Compute symbolic health
        self.symbolic_health = (
            self.params.phi_alignment_weight * self.phi_alignment
            + self.params.fibonacci_weight * self.fibonacci_alignment
            + self.params.metatron_weight * self.metatron_flow
            + self.params.platonic_weight * self.platonic_coherence
            + self.params.e8_weight * self.e8_alignment
        )

        # 8. Meridian Qi dynamics
        # Qi flows through meridians with circadian modulation
        qi_flow = np.roll(self.meridian_qi, 1) - self.meridian_qi
        self.meridian_qi += qi_flow * self.params.symbol_coupling * dt

        # Ecological coupling (Schumann affects Qi)
        if l6_input is not None and "schumann_field" in l6_input:
            schumann_mean = np.mean(l6_input["schumann_field"])
            self.meridian_qi *= 0.9 + 0.1 * schumann_mean

        self.meridian_qi = np.clip(self.meridian_qi, 0.0, 1.0)

        # 9. Acupuncture point activation
        if acupoint_stimulus is not None:
            for point_id, intensity in acupoint_stimulus.items():
                if 0 <= point_id < self.params.n_acupoints:
                    self.acupoint_activations[point_id] = np.clip(
                        self.acupoint_activations[point_id] + intensity, 0.0, 1.0
                    )

        # Decay acupoint activations
        self.acupoint_activations *= 1.0 - self.params.symbol_decay * dt

        # 10. Assemble glyph vector
        self.glyph_vector = np.array(
            [
                self.phi_alignment,
                self.fibonacci_alignment,
                self.metatron_flow,
                self.platonic_coherence,
                self.e8_alignment,
                self.symbolic_health,
            ]
        )

        # 11. Symbol dynamics (decay and coupling)
        # Coupling: nearby symbols influence each other
        coupling = np.roll(self.symbol_activations, 1) + np.roll(self.symbol_activations, -1)
        self.symbol_activations += (
            self.params.symbol_coupling * (coupling / 2 - self.symbol_activations) * dt
        )
        # Decay
        self.symbol_activations *= 1.0 - self.params.symbol_decay * dt
        self.symbol_activations = np.clip(self.symbol_activations, 0.0, 1.0)

        # 12. Generate output bitstreams
        output_probs = np.concatenate(
            [self.symbol_activations, self.meridian_qi, self.glyph_vector]
        )
        output_probs = output_probs[: self.params.n_symbols]

        rands = np.random.random((self.params.n_symbols, self.params.bitstream_length))
        output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

        return {
            "glyph_vector": self.glyph_vector.copy(),
            "phi_alignment": self.phi_alignment,
            "fibonacci_alignment": self.fibonacci_alignment,
            "metatron_flow": self.metatron_flow,
            "platonic_coherence": self.platonic_coherence,
            "e8_alignment": self.e8_alignment,
            "symbolic_health": self.symbolic_health,
            "meridian_qi": self.meridian_qi.copy(),
            "acupoint_activations": self.acupoint_activations.copy(),
            "e8_state": self.e8_state.copy(),
            "output_bitstreams": output_bitstreams,
        }

    def get_global_metric(self) -> float:
        """Return the global symbolic coherence metric."""
        return self.symbolic_health

    def get_glyph_vector_normalized(self) -> np.ndarray[Any, Any]:
        """Return normalized glyph vector for external use."""
        return self.glyph_vector / (np.max(self.glyph_vector) + 1e-8)

    def stimulate_meridian(self, meridian_id: int, intensity: float) -> None:
        """Stimulate a specific meridian."""
        if 0 <= meridian_id < self.params.n_meridians:
            self.meridian_qi[meridian_id] = np.clip(
                self.meridian_qi[meridian_id] + intensity, 0.0, 1.0
            )

    def get_acupoint_map(self) -> Dict[str, float]:
        """Return named acupoint activations (simplified set)."""
        # Classical acupoints (simplified)
        named_points = {
            "LI4_Hegu": 4,
            "ST36_Zusanli": 36,
            "SP6_Sanyinjiao": 60,
            "PC6_Neiguan": 96,
            "LV3_Taichong": 120,
            "GV20_Baihui": 200,
            "CV4_Guanyuan": 250,
            "BL23_Shenshu": 300,
        }
        return {
            name: float(self.acupoint_activations[idx])
            for name, idx in named_points.items()
            if idx < self.params.n_acupoints
        }

step(dt, l6_input=None, symbol_input=None, acupoint_stimulus=None)

Advance the layer by one time step.

Parameters:

Name Type Description Default
dt float

Time step in seconds.

required
l6_input Optional[Dict[str, Any]]

Ecological layer output (Schumann, circadian).

None
symbol_input Optional[ndarray[Any, Any]]

External symbolic input vector.

None
acupoint_stimulus Optional[Dict[int, float]]

Dict of {point_id: intensity} for acupuncture.

None

Returns:

Type Description
Dict[str, ndarray[Any, Any]]

Dict with glyph_vector, meridian_qi, sacred_geometry, output_bitstreams

Source code in src/sc_neurocore/scpn/layers/l7_symbolic.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
def step(
    self,
    dt: float,
    l6_input: Optional[Dict[str, Any]] = None,
    symbol_input: Optional[np.ndarray[Any, Any]] = None,
    acupoint_stimulus: Optional[Dict[int, float]] = None,
) -> Dict[str, np.ndarray[Any, Any]]:
    """
    Advance the layer by one time step.

    Args:
        dt: Time step in seconds.
        l6_input: Ecological layer output (Schumann, circadian).
        symbol_input: External symbolic input vector.
        acupoint_stimulus: Dict of {point_id: intensity} for acupuncture.

    Returns:
        Dict with glyph_vector, meridian_qi, sacred_geometry, output_bitstreams
    """
    self.time += dt

    # 1. Process symbol input
    if symbol_input is not None:
        self.symbol_activations = np.clip(
            self.symbol_activations + symbol_input[: self.params.n_symbols] * 0.2, 0.0, 1.0
        )

    # 2. Compute Phi (Golden Ratio) alignment
    # Check how close symbol ratios are to Phi
    sorted_activations = np.sort(self.symbol_activations)[::-1]
    if sorted_activations[1] > 0.01:
        ratios = sorted_activations[:-1] / (sorted_activations[1:] + 1e-8)
        phi_distances = np.abs(ratios - PHI)
        self.phi_alignment = float(np.exp(-np.mean(phi_distances)))
    else:
        self.phi_alignment = 0.5

    # 3. Compute Fibonacci alignment
    # Check if activation levels follow Fibonacci ratios
    fib_normalized = np.array(self.FIBONACCI[:8]) / self.FIBONACCI[7]
    top_8 = sorted_activations[:8]
    if np.max(top_8) > 0.01:
        top_8_norm = top_8 / (np.max(top_8) + 1e-8)
        fib_corr = np.corrcoef(top_8_norm, fib_normalized)[0, 1]
        self.fibonacci_alignment = float(max(0, (fib_corr + 1) / 2))
    else:
        self.fibonacci_alignment = 0.5

    # 4. Compute Metatron's Cube flow
    # Based on 13-sphere / 78-line connectivity pattern
    metatron_nodes = 13
    active_nodes = np.sum(self.symbol_activations[:metatron_nodes] > 0.5)
    self.metatron_flow = active_nodes / metatron_nodes
    # Add flow dynamics
    self.metatron_flow = 0.9 * self.metatron_flow + 0.1 * np.random.random()

    # 5. Compute Platonic solid coherence
    platonic_metrics = []
    for solid, vertices in self.PLATONIC_VERTICES.items():
        solid_activations = self.symbol_activations[:vertices]
        coherence = np.std(solid_activations)  # Lower std = more coherent
        platonic_metrics.append(1.0 - coherence)
    self.platonic_coherence = float(np.mean(platonic_metrics))

    # 6. E8 lattice alignment
    # Simplified: check alignment of 8D state vector with E8 root system
    # E8 has 240 roots; we use a proxy
    e8_norm = np.linalg.norm(self.e8_state)
    if e8_norm > 0:
        e8_unit = self.e8_state / e8_norm
        # Check alignment with simple E8 roots (permutations of ±1)
        simple_roots = np.eye(8)
        alignments = np.abs(np.dot(simple_roots, e8_unit))
        self.e8_alignment = float(np.max(alignments))
    else:
        self.e8_alignment = 0.5

    # Update E8 state with noise
    self.e8_state += 0.1 * np.random.normal(0, 1, 8) * dt
    self.e8_state = np.clip(self.e8_state, -1, 1)

    # 7. Compute symbolic health
    self.symbolic_health = (
        self.params.phi_alignment_weight * self.phi_alignment
        + self.params.fibonacci_weight * self.fibonacci_alignment
        + self.params.metatron_weight * self.metatron_flow
        + self.params.platonic_weight * self.platonic_coherence
        + self.params.e8_weight * self.e8_alignment
    )

    # 8. Meridian Qi dynamics
    # Qi flows through meridians with circadian modulation
    qi_flow = np.roll(self.meridian_qi, 1) - self.meridian_qi
    self.meridian_qi += qi_flow * self.params.symbol_coupling * dt

    # Ecological coupling (Schumann affects Qi)
    if l6_input is not None and "schumann_field" in l6_input:
        schumann_mean = np.mean(l6_input["schumann_field"])
        self.meridian_qi *= 0.9 + 0.1 * schumann_mean

    self.meridian_qi = np.clip(self.meridian_qi, 0.0, 1.0)

    # 9. Acupuncture point activation
    if acupoint_stimulus is not None:
        for point_id, intensity in acupoint_stimulus.items():
            if 0 <= point_id < self.params.n_acupoints:
                self.acupoint_activations[point_id] = np.clip(
                    self.acupoint_activations[point_id] + intensity, 0.0, 1.0
                )

    # Decay acupoint activations
    self.acupoint_activations *= 1.0 - self.params.symbol_decay * dt

    # 10. Assemble glyph vector
    self.glyph_vector = np.array(
        [
            self.phi_alignment,
            self.fibonacci_alignment,
            self.metatron_flow,
            self.platonic_coherence,
            self.e8_alignment,
            self.symbolic_health,
        ]
    )

    # 11. Symbol dynamics (decay and coupling)
    # Coupling: nearby symbols influence each other
    coupling = np.roll(self.symbol_activations, 1) + np.roll(self.symbol_activations, -1)
    self.symbol_activations += (
        self.params.symbol_coupling * (coupling / 2 - self.symbol_activations) * dt
    )
    # Decay
    self.symbol_activations *= 1.0 - self.params.symbol_decay * dt
    self.symbol_activations = np.clip(self.symbol_activations, 0.0, 1.0)

    # 12. Generate output bitstreams
    output_probs = np.concatenate(
        [self.symbol_activations, self.meridian_qi, self.glyph_vector]
    )
    output_probs = output_probs[: self.params.n_symbols]

    rands = np.random.random((self.params.n_symbols, self.params.bitstream_length))
    output_bitstreams = (rands < output_probs[:, None]).astype(np.uint8)

    return {
        "glyph_vector": self.glyph_vector.copy(),
        "phi_alignment": self.phi_alignment,
        "fibonacci_alignment": self.fibonacci_alignment,
        "metatron_flow": self.metatron_flow,
        "platonic_coherence": self.platonic_coherence,
        "e8_alignment": self.e8_alignment,
        "symbolic_health": self.symbolic_health,
        "meridian_qi": self.meridian_qi.copy(),
        "acupoint_activations": self.acupoint_activations.copy(),
        "e8_state": self.e8_state.copy(),
        "output_bitstreams": output_bitstreams,
    }

get_global_metric()

Return the global symbolic coherence metric.

Source code in src/sc_neurocore/scpn/layers/l7_symbolic.py
273
274
275
def get_global_metric(self) -> float:
    """Return the global symbolic coherence metric."""
    return self.symbolic_health

get_glyph_vector_normalized()

Return normalized glyph vector for external use.

Source code in src/sc_neurocore/scpn/layers/l7_symbolic.py
277
278
279
def get_glyph_vector_normalized(self) -> np.ndarray[Any, Any]:
    """Return normalized glyph vector for external use."""
    return self.glyph_vector / (np.max(self.glyph_vector) + 1e-8)

stimulate_meridian(meridian_id, intensity)

Stimulate a specific meridian.

Source code in src/sc_neurocore/scpn/layers/l7_symbolic.py
281
282
283
284
285
286
def stimulate_meridian(self, meridian_id: int, intensity: float) -> None:
    """Stimulate a specific meridian."""
    if 0 <= meridian_id < self.params.n_meridians:
        self.meridian_qi[meridian_id] = np.clip(
            self.meridian_qi[meridian_id] + intensity, 0.0, 1.0
        )

get_acupoint_map()

Return named acupoint activations (simplified set).

Source code in src/sc_neurocore/scpn/layers/l7_symbolic.py
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
def get_acupoint_map(self) -> Dict[str, float]:
    """Return named acupoint activations (simplified set)."""
    # Classical acupoints (simplified)
    named_points = {
        "LI4_Hegu": 4,
        "ST36_Zusanli": 36,
        "SP6_Sanyinjiao": 60,
        "PC6_Neiguan": 96,
        "LV3_Taichong": 120,
        "GV20_Baihui": 200,
        "CV4_Guanyuan": 250,
        "BL23_Shenshu": 300,
    }
    return {
        name: float(self.acupoint_activations[idx])
        for name, idx in named_points.items()
        if idx < self.params.n_acupoints
    }

create_full_stack(params=None)

Create a complete 16-layer SCPN stack.

Source code in src/sc_neurocore/scpn/layers/__init__.py
 98
 99
100
101
def create_full_stack(params: Optional[dict[str, Any]] = None) -> dict[str, Any]:
    """Create a complete 16-layer SCPN stack."""
    params = params or {}
    return {key: cls(params.get(key)) for key, cls in LAYER_REGISTRY.items()}

run_integrated_step(layers, dt, inputs=None)

Run one integrated time step across all SCPN layers with inter-layer coupling.

Source code in src/sc_neurocore/scpn/layers/__init__.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
def run_integrated_step(
    layers: dict[str, Any], dt: float, inputs: Optional[dict[str, Any]] = None
) -> dict[str, Any]:
    """
    Run one integrated time step across all SCPN layers with inter-layer coupling.
    """
    inputs = inputs or {}
    outputs = {}

    # L1: Quantum (foundation)
    l1_bitstreams = layers["l1"].step(dt, external_field=inputs.get("l1_field"))
    outputs["l1"] = {
        "output_bitstreams": l1_bitstreams,
        "coherence": layers["l1"].get_global_metric(),
    }

    # L2: Neurochemical (receives L1 quantum modulation)
    l2_out = layers["l2"].step(dt, nt_release=inputs.get("nt_release"), l1_input=l1_bitstreams)
    outputs["l2"] = l2_out

    # L3: Genomic (receives L2 second messengers)
    l3_out = layers["l3"].step(dt, l2_input=l2_out, bioelectric_signal=inputs.get("bioelectric"))
    outputs["l3"] = l3_out

    # L4: Cellular (receives L3 protein modulation)
    l4_out = layers["l4"].step(
        dt, l3_input=l3_out, external_stimulus=inputs.get("cellular_stimulus")
    )
    outputs["l4"] = l4_out

    # L5: Organismal (receives L4 synchronization)
    l5_out = layers["l5"].step(dt, l4_input=l4_out, external_event=inputs.get("emotional_event"))
    outputs["l5"] = l5_out

    # L6: Ecological (receives L5 organismal state)
    l6_out = layers["l6"].step(
        dt,
        l5_input=l5_out,
        solar_activity=inputs.get("solar", 0.5),
        lunar_phase=inputs.get("lunar", 0.0),
    )
    outputs["l6"] = l6_out

    # L7: Symbolic (receives L6 Schumann/ecological)
    l7_out = layers["l7"].step(
        dt,
        l6_input=l6_out,
        symbol_input=inputs.get("symbols"),
        acupoint_stimulus=inputs.get("acupoints"),
    )
    outputs["l7"] = l7_out

    # L8: Phase Field / Cosmic (receives L7 symbolic)
    l8_out = layers["l8"].step(dt, l7_input=l7_out)
    outputs["l8"] = l8_out

    # L9: Memory (receives L8 cosmic alignment)
    l9_out = layers["l9"].step(dt, l8_input=l8_out)
    outputs["l9"] = l9_out

    # L10: Boundary (receives L9 retrieval quality)
    l10_out = layers["l10"].step(dt, l9_input=l9_out)
    outputs["l10"] = l10_out

    # L11: Morphic (receives L10 integrity)
    l11_out = layers["l11"].step(dt, l10_input=l10_out)
    outputs["l11"] = l11_out

    # L12: Quantum Info (receives L11 info saturation)
    l12_out = layers["l12"].step(dt, l11_input=l11_out)
    outputs["l12"] = l12_out

    # L13: Temporal (receives L12 coherence)
    l13_out = layers["l13"].step(dt, l12_input=l12_out)
    outputs["l13"] = l13_out

    # L14: Integration (receives metrics from all layers)
    all_metrics = get_global_metrics(layers)
    l14_out = layers["l14"].step(dt, layer_metrics=all_metrics)
    outputs["l14"] = l14_out

    # L15: Meta-cognitive (receives L14 integrated coherence)
    l15_out = layers["l15"].step(dt, l14_input=l14_out)
    outputs["l15"] = l15_out

    # L16: Director (receives L15 GCI)
    l16_out = layers["l16"].step(dt, l15_input=l15_out)
    outputs["l16"] = l16_out

    return outputs

get_global_metrics(layers)

Get global coherence metrics from all layers.

Source code in src/sc_neurocore/scpn/layers/__init__.py
196
197
198
def get_global_metrics(layers: dict[str, Any]) -> dict[str, Any]:
    """Get global coherence metrics from all layers."""
    return {f"l{i}": layers[f"l{i}"].get_global_metric() for i in range(1, 17) if f"l{i}" in layers}

build_knm_matrix(n_layers=N_LAYERS)

Build the Knm inter-layer coupling matrix.

Construction: exponential decay baseline, calibration anchor overrides, cross-hierarchy boosts, symmetrisation, zero diagonal.

Source code in src/sc_neurocore/scpn/params.py
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
def build_knm_matrix(n_layers: int = N_LAYERS) -> np.ndarray:
    """
    Build the Knm inter-layer coupling matrix.

    Construction: exponential decay baseline, calibration anchor overrides,
    cross-hierarchy boosts, symmetrisation, zero diagonal.
    """
    K = np.zeros((n_layers, n_layers), dtype=np.float64)

    for n in range(n_layers):
        for m in range(n_layers):
            if n != m:
                K[n, m] = K_BASE * np.exp(-DECAY_ALPHA * abs(n - m))

    for (i, j), val in CALIBRATION_ANCHORS.items():
        if i <= n_layers and j <= n_layers:
            K[i - 1, j - 1] = val
            K[j - 1, i - 1] = val

    for (i, j), val in CROSS_BOOSTS.items():
        if i <= n_layers and j <= n_layers:
            K[i - 1, j - 1] = val
            K[j - 1, i - 1] = val

    K = 0.5 * (K + K.T)
    np.fill_diagonal(K, 0.0)

    return K