Source code for mercurial.atlas.cicoria_empirical

"""Cicoria (Case C.7) – lightning‑induced NDE and acquired musical savantism."""

import matplotlib.pyplot as plt
import numpy as np

from mercurial.core.jansen_rit import JansenRitColumn, JansenRitParams
from mercurial.core.pattern_completion import HopfieldPatternCompletion
from mercurial.core.plasticity import PlasticWilsonCowan
from mercurial.core.wilson_cowan import WilsonCowanParams
from mercurial.simulation.engine import SimulationEngine


[docs] def run_cicoria(): engine = SimulationEngine(dim=20) engine.apply_empirical_parameters() # 1. Jansen‑Rit column for patient's EEG (lightning strike) jr_params = JansenRitParams() jr_column = JansenRitColumn(jr_params=jr_params, noise_amp=0.01) eeg_vals = [] # 2. Musical template (pattern to be acquired) n_neurons = 200 # Create a pattern representing a musical motif (e.g., a sequence of notes) musical_pattern = np.zeros(n_neurons) # Simulate a simple melodic contour: increasing then decreasing for i in range(n_neurons): musical_pattern[i] = np.sin(2 * np.pi * i / 50) * 0.5 + 0.5 # Store the pattern in a Hopfield network (to act as a template) template_net = HopfieldPatternCompletion( n_neurons=n_neurons, stored_patterns=[musical_pattern], tau_a=0.020, beta_sigmoid=10.0, hebb_params=engine.hebbian_params, ) # 3. Plastic Wilson‑Cowan network for the subject's auditory/motor cortex (learning) wc_params = WilsonCowanParams() plastic_net = PlasticWilsonCowan(wc_params=wc_params, hebb_params=engine.hebbian_params) dt = 0.001 t_span = (0.0, 5.0) # simulate 5 seconds steps = int((t_span[1] - t_span[0]) / dt) # Lightning strike occurs at t=0.1 s (simulate a huge input spike) lightning_time = 0.1 lightning_strength = 500.0 # arbitrary large input # Record the plastic network's output over time (simulated musical ability) plastic_output = [] for step in range(steps): t = step * dt # Jansen‑Rit: lightning causes a massive spike, then flat EEG (cardiac arrest) if abs(t - lightning_time) < 0.005: # 5 ms pulse jr_column.step(dt, p_ext=lightning_strength) else: # After lightning, no external input (cardiac arrest) jr_column.step(dt, p_ext=0.0) eeg = jr_column.get_EEG() eeg_vals.append(eeg) # During the NDE (right after lightning), inject the musical pattern into the plastic network if t > lightning_time and t < lightning_time + 0.5: # DPR injection of musical pattern injection = musical_pattern else: injection = np.zeros(n_neurons) # Update the plastic network with the injected pattern as external input # For simplicity, we treat the plastic network as a Wilson‑Cowan population # that receives the pattern as input to both excitatory and inhibitory populations. # (In a full implementation, we would map the pattern to the network's state space.) # Here we approximate: use the injection as a bias to the external input. # We'll feed the pattern as P_ext to the plastic Wilson‑Cowan. # However, PlasticWilsonCowan expects scalar P_ext, not a vector. # For this demonstration, we assume the plastic network learns to reproduce the pattern # via Hebbian plasticity after repeated exposure. We'll simulate a simplified version: # we run the Hopfield network to complete the pattern and then use its output to # train the plastic network via Hebbian rule (correlation learning). if np.any(injection != 0): # Let the Hopfield network converge to the stored pattern _ = template_net.step(dt, injection) completed = template_net.r # the recalled pattern (0..1) # Use the completed pattern as target for Hebbian learning # We'll update the plastic network's weights using the product of its own activity # and the completed pattern (simplified: Hebbian learning between external pattern and activity) # For PlasticWilsonCowan, we can directly set its activity to the completed pattern plastic_net.E = np.mean(completed) # approximate; real would be vectorised plastic_net.I = 0.0 else: # After injection, let the plastic network evolve naturally plastic_net.step(dt, P_ext=0.0, Q_ext=0.0) plastic_output.append(plastic_net.E) # After simulation, compute the correlation between the final plastic network state # and the musical pattern (indicating successful acquisition) # (Take the last 100 time steps to avoid initial transients) final_plastic = np.mean(plastic_output[-100:]) # The final plastic activity is a scalar; we need to compare to pattern. # Instead, we compute the correlation between the pattern and the plastic network's # weight matrix (simplified: use the Hopfield network's final state as a proxy) final_template = template_net.r correlation = np.corrcoef(final_template, musical_pattern)[0, 1] print(f"Correlation between acquired musical ability and template: {correlation:.3f}") # Plot EEG plt.figure() plt.plot(np.arange(len(eeg_vals)) * dt, eeg_vals) plt.xlabel("Time (s)") plt.ylabel("EEG (mV)") plt.title("Cicoria – Simulated EEG (lightning spike then flat)") plt.grid(True) plt.show() # Plot the acquired pattern (final Hopfield network state) plt.figure() plt.plot(final_template, label="Acquired musical pattern") plt.plot(musical_pattern, "--", label="Original template") plt.legend() plt.title("Musical pattern acquisition") plt.xlabel("Neuron index") plt.ylabel("Activity") plt.grid(True) plt.show() return correlation
if __name__ == "__main__": run_cicoria()