Ex27: Multi-Synapses

AMPA + NMDA synapses, individually and as doubleSynapse composite

Model: Multi-Synapses

Five iafCells receiving spikes: cells get AMPA-only, NMDA-only, both separately, and a doubleSynapse (AMPA+NMDA composite) with delay. Demonstrates doubleSynapse which references two child synapses by name.

Reference: NeuroML2 LEMS_NML2_Ex27_MultiSynapses.xml


1. Define Network in TVBO

from tvbo import SimulationExperiment

exp = SimulationExperiment.from_string("""
label: "NeuroML Ex27: Multi-Synapses"
dynamics:
  name: iafCell
  iri: neuroml:iafCell
network:
  dynamics:
    iaf:
      name: iaf
      iri: neuroml:iafCell
      parameters:
        leakReversal: {value: -70, unit: mV}
        thresh: {value: -55, unit: mV}
        reset: {value: -70, unit: mV}
        C: {value: 0.2, unit: nF}
        leakConductance: {value: 10, unit: nS}
    spks:
      name: spks
      iri: neuroml:spikeArray
      events:
        s:
          event_type: preset_time
          trigger_times: [50, 200, 350, 360]
    # AMPA: fast excitatory synapse
    AMPA:
      name: AMPA
      iri: neuroml:expTwoSynapse
      parameters:
        gbase: {value: 1, unit: nS}
        erev: {value: 0, unit: mV}
        tauRise: {value: 1, unit: ms}
        tauDecay: {value: 2, unit: ms}
    # NMDA: slow excitatory synapse with Mg2+ block
    NMDA:
      name: NMDA
      iri: neuroml:blockingPlasticSynapse
      parameters:
        gbase: {value: 4, unit: nS}
        erev: {value: 0, unit: mV}
        tauRise: {value: 2, unit: ms}
        tauDecay: {value: 20, unit: ms}
      modes:
        blockMechanism:
          name: blockMechanism
          iri: neuroml:voltageConcDepBlockMechanism
          parameters:
            species: {description: mg}
            blockConcentration: {value: 1.2, unit: mM}
            scalingConc: {value: 1.920544, unit: mM}
            scalingVolt: {value: 16.129, unit: mV}
    # doubleSynapse: composite of AMPA + NMDA
    AMPA_NMDA:
      name: AMPA_NMDA
      iri: neuroml:doubleSynapse
      parameters:
        synapse1: {description: AMPA}
        synapse1Path: {description: ./AMPA}
        synapse2: {description: NMDA}
        synapse2Path: {description: ./NMDA}
  nodes:
    - {id: 0, dynamics: iaf}
    - {id: 1, dynamics: iaf}
    - {id: 2, dynamics: iaf}
    - {id: 3, dynamics: iaf}
    - {id: 4, dynamics: iaf}
    - {id: 100, dynamics: spks}
  edges:
    # Cell 0: AMPA only
    - {source: 100, target: 0, coupling: AMPA, parameters: {weight: {value: 0.5}, delay: {value: 0, unit: ms}}}
    # Cell 1: NMDA only
    - {source: 100, target: 1, coupling: NMDA, parameters: {weight: {value: 0.5}, delay: {value: 0, unit: ms}}}
    # Cell 2: AMPA + NMDA separately
    - {source: 100, target: 2, coupling: AMPA, parameters: {weight: {value: 0.5}, delay: {value: 0, unit: ms}}}
    - {source: 100, target: 2, coupling: NMDA, parameters: {weight: {value: 0.5}, delay: {value: 0, unit: ms}}}
    # Cell 3: doubleSynapse (AMPA+NMDA composite) with 5ms delay
    - {source: 100, target: 3, coupling: AMPA_NMDA, parameters: {weight: {value: 0.5}, delay: {value: 5, unit: ms}}}
integration:
  method: euler
  step_size: 0.001
  duration: 600.0
  time_scale: ms
""")
print(f"Model: {exp.dynamics.name if exp.dynamics else 'network'}")
Model: iafCell

2. Render LEMS XML

xml = exp.render("lems")
print(xml[:3000])
<Lems>

  <!-- Tell jLEMS/jNeuroML which component is the simulation entry point. -->
  <Target component="sim_NeuroML_Ex27__Multi_Synapses"/>

  <!-- ════════════════════════════════════════════════════════════════
       Standard NeuroML2 type includes for network mode.
       Provides all standard dimensions, units, synapse types, input
       types, network infrastructure, and simulation types.
       ════════════════════════════════════════════════════════════════ -->
  <Include file="Cells.xml"/>
  <Include file="Networks.xml"/>
  <Include file="Simulation.xml"/>

  <!-- ════════════════════════════════════════════════════════════════
       Dynamics ComponentType & Component instances
       ════════════════════════════════════════════════════════════════ -->


  <!-- ── ComponentType: iaf ── -->
  <ComponentType name="iaf" extends="baseCellMembPot">
    <Parameter name="tau" dimension="none"/>
    <Parameter name="leakReversal" dimension="voltage"/>
    <Parameter name="thresh" dimension="voltage"/>
    <Parameter name="reset" dimension="voltage"/>
    <Parameter name="C" dimension="capacitance"/>
    <Parameter name="leakConductance" dimension="conductance"/>
    <Parameter name="refract" dimension="time"/>
    <Parameter name="v_0" dimension="none" />
    <!-- Dynamically attached synapses/inputs from network connections -->
    <Attachments name="synapses" type="basePointCurrent"/>

    <Dynamics>
      <StateVariable name="v" dimension="none" exposure="v"/>
      <StateVariable name="lastSpikeTime" dimension="time"/>
      <ConditionalDerivedVariable name="I_ext" dimension="none">
        <Case condition="t .geq. 0.1 .and. t .lt. 0.2" value="0.300000000000000"/>
        <Case value="0.0"/>
      </ConditionalDerivedVariable>
      <!-- ── Regime-based dynamics (spike model) ── -->
      <OnStart>
        <StateAssignment variable="v" value="v_0"/>
      </OnStart>

      <Regime name="integrating" initial="true">
        <TimeDerivative variable="v" value="I_ext + (leakReversal - v)/tau"/>
        <OnCondition test="v .gt. thresh">
          <EventOut port="spike"/>
          <Transition regime="refractory"/>
        </OnCondition>
      </Regime>

      <Regime name="refractory">
        <OnEntry>
          <StateAssignment variable="lastSpikeTime" value="t"/>
          <StateAssignment variable="v" value="reset"/>
        </OnEntry>
        <OnCondition test="t .gt. lastSpikeTime + refract">
          <Transition regime="integrating"/>
        </OnCondition>
      </Regime>
    </Dynamics>
  </ComponentType>

  <Component id="iaf_inst" type="iaf" tau="20.0" leakReversal="-70 mV" thresh="-55 mV" reset="-70 mV" C="0.2 nF" leakConductance="10 nS" refract="0 ms" v_0="-60.0"/>


  <!-- ════════════════════════════════════════════════════════════════
       Built-in/named synapse definitions (no custom ODE dynamics)
       ════════════════════════════════════════════════════════════════ -->
  <!-- Synapse: AMPA (AMPA) -->
  <AMPA id="AMPA

3. Run Reference

from tvbo.adapters.neuroml import run_lems_example

ref_outputs = run_lems_example("LEMS_NML2_Ex27_MultiSynapses.xml")
for name, arr in ref_outputs.items():
    print(f"  {name}: shape={arr.shape}")
  ex27_v.dat: shape=(600001, 5)

4. Run TVBO

result = exp.run("neuroml")
da = result.integration.data
print(f"TVBO: {da.dims}, shape={da.shape}")
pyNeuroML >>> 12:59:17 - INFO - Loading LEMS file: tvbo_lems_sim.xml and running with jNeuroML
pyNeuroML >>> 12:59:17 - INFO - Executing: (java -Xmx400M  -Djava.awt.headless=true -jar  "/Users/leonmartin_bih/tools/tvbo/.venv/lib/python3.12/site-packages/pyneuroml/utils/./../lib/jNeuroML-0.14.0-jar-with-dependencies.jar"  tvbo_lems_sim.xml  -nogui -I '') in directory: /var/folders/ym/9kw1g21j1nd7kwfn8c0z3st40000gn/T/tmp4zjh5rmc
pyNeuroML >>> 12:59:20 - INFO - Command completed successfully!
TVBO: ('time', 'quantity'), shape=(600001, 5)

5. Compare & Plot

from tvbo.adapters.neuroml import plot_lems_comparison
plot_lems_comparison("LEMS_NML2_Ex27_MultiSynapses.xml", ref_outputs, result.integration.data, title_prefix="Ex27")