Network Simulation Engine Demo¶

SC-NeuroCore v3.13.3 — Population-Projection-Network architecture

Build a balanced excitatory-inhibitory network, run it, record spikes, and analyze the output. Demonstrates the full Network API with spike monitors, Poisson input, and STDP plasticity.

© 1998–2026 Miroslav Šotek. All rights reserved.
License: GNU AFFERO GENERAL PUBLIC LICENSE v3 | Commercial Licensing Available
Contact: www.anulum.li | protoscience@anulum.li

In [1]:
import numpy as np
import matplotlib.pyplot as plt

from sc_neurocore.neurons.models.hodgkin_huxley import HodgkinHuxleyNeuron

from sc_neurocore.network.population import Population
from sc_neurocore.network.projection import Projection
from sc_neurocore.network.network import Network
from sc_neurocore.network.monitor import SpikeMonitor
from sc_neurocore.network.stimulus import PoissonInput

print("SC-NeuroCore Network Engine Demo")
Detected IPython. Loading juliacall extension. See https://juliapy.github.io/PythonCall.jl/stable/compat/#IPython
SC-NeuroCore Network Engine Demo

1. Create Populations¶

80 excitatory Hodgkin-Huxley neurons and 20 inhibitory Hodgkin-Huxley neurons.

In [2]:
exc = Population(HodgkinHuxleyNeuron, n=80, label="exc")
inh = Population(HodgkinHuxleyNeuron, n=20, label="inh")

print(f"Excitatory: {exc.n} Ă— {HodgkinHuxleyNeuron.__name__}")
print(f"Inhibitory: {inh.n} Ă— {HodgkinHuxleyNeuron.__name__}")
Excitatory: 80 Ă— HodgkinHuxleyNeuron
Inhibitory: 20 Ă— HodgkinHuxleyNeuron

2. Connect with Projections¶

Random connectivity with excitatory and inhibitory weights.

In [3]:
ee = Projection(exc, exc, weight=0.05, topology="random", probability=0.1, seed=1)
ei = Projection(exc, inh, weight=0.10, topology="random", probability=0.3, seed=2)
ie = Projection(inh, exc, weight=-0.15, topology="random", probability=0.3, seed=3)

print(f"E→E: p=0.1, w=+0.05")
print(f"E→I: p=0.3, w=+0.10")
print(f"I→E: p=0.3, w=-0.15")
E→E: p=0.1, w=+0.05
E→I: p=0.3, w=+0.10
I→E: p=0.3, w=-0.15

3. Add Stimulus and Monitors¶

In [4]:
drive = PoissonInput(n=80, rate_hz=100.0, weight=2.0, dt=0.001, seed=42)
mon_exc = SpikeMonitor(exc, label="exc_spikes")
mon_inh = SpikeMonitor(inh, label="inh_spikes")

print("Poisson input: 100 Hz, weight 2.0")
print("Monitors attached to both populations")
Poisson input: 100 Hz, weight 2.0
Monitors attached to both populations

4. Build and Run Network¶

In [5]:
net = Network(exc, inh, ee, ei, ie, drive, mon_exc, mon_inh)

# Run for 500 ms
net.run(duration=0.5, dt=0.001)

print(f"Excitatory spikes: {mon_exc.count}")
print(f"Inhibitory spikes: {mon_inh.count}")
print(f"Exc mean rate: {mon_exc.count / (0.5 * 80):.1f} Hz")
print(f"Inh mean rate: {mon_inh.count / (0.5 * 20):.1f} Hz")
Excitatory spikes: 0
Inhibitory spikes: 0
Exc mean rate: 0.0 Hz
Inh mean rate: 0.0 Hz

5. Spike Raster Plot¶

In [6]:
fig, axes = plt.subplots(2, 1, figsize=(12, 6), sharex=True)

# Excitatory raster
exc_trains = mon_exc.spike_trains
for unit_id, times in exc_trains.items():
    axes[0].scatter(times * 1000, [unit_id] * len(times), s=0.5, c='blue', alpha=0.5)
axes[0].set_ylabel("Exc neuron")
axes[0].set_title(f"Excitatory ({mon_exc.count} spikes)")

# Inhibitory raster
inh_trains = mon_inh.spike_trains
for unit_id, times in inh_trains.items():
    axes[1].scatter(times * 1000, [unit_id] * len(times), s=1.0, c='red', alpha=0.5)
axes[1].set_ylabel("Inh neuron")
axes[1].set_xlabel("Time (ms)")
axes[1].set_title(f"Inhibitory ({mon_inh.count} spikes)")

plt.tight_layout()
plt.show()
No description has been provided for this image

6. Population Firing Rate Over Time¶

In [7]:
# Compute instantaneous firing rate (10ms bins)
bin_width = 0.01  # 10 ms
n_bins = int(0.5 / bin_width)
exc_rate = np.zeros(n_bins)
inh_rate = np.zeros(n_bins)

for times in exc_trains.values():
    for t in times:
        b = min(int(t / bin_width), n_bins - 1)
        exc_rate[b] += 1

for times in inh_trains.values():
    for t in times:
        b = min(int(t / bin_width), n_bins - 1)
        inh_rate[b] += 1

exc_rate = exc_rate / (80 * bin_width)  # Hz
inh_rate = inh_rate / (20 * bin_width)  # Hz
t_bins = np.arange(n_bins) * bin_width * 1000  # ms

fig, ax = plt.subplots(figsize=(12, 4))
ax.plot(t_bins, exc_rate, label=f"Excitatory (mean {np.mean(exc_rate):.1f} Hz)", alpha=0.8)
ax.plot(t_bins, inh_rate, label=f"Inhibitory (mean {np.mean(inh_rate):.1f} Hz)", alpha=0.8)
ax.set_xlabel("Time (ms)")
ax.set_ylabel("Firing rate (Hz)")
ax.set_title("Population firing rates (10 ms bins)")
ax.legend()
plt.tight_layout()
plt.show()
No description has been provided for this image

Summary¶

Component Count
Populations 2 (80 exc + 20 inh)
Projections 3 (E→E, E→I, I→E)
Stimulus Poisson 100 Hz
Simulation 500 ms, dt=1 ms
Backend Python (auto)

The same code scales to 100K+ neurons with backend="rust" or millions with backend="mpi". See Tutorial 31 for the full API reference.