# Contrast the output of a ring network with unidirectional inhibition with one that has IE reciprocity import brian2 as br import matplotlib.pyplot as plt import numpy as np from brian2.units import * import scripts.models as modellib from brian2 import seed seed(2) N_E = 100 ex_angles = np.arange(-np.pi, np.pi, 2 * np.pi / N_E) N_I = 20 in_angles = np.arange(-np.pi, np.pi, 2 * np.pi / N_I) ''' Excitatory neurons ''' excitatory_eqs = modellib.hodgkin_huxley_eqs_with_synaptic_conductance + modellib.eqs_ih excitatory_params = modellib.hodgkin_huxley_params excitatory_params.update(modellib.ih_params) excitatory_params.update({"E_i": -80 * mV}) excitatory_neurons = br.NeuronGroup(N_E, excitatory_eqs, namespace=excitatory_params, threshold='v>0*mV', refractory='v>0*mV', method='exponential_euler') ex_input_baseline = 0.05 * nA excitatory_neurons.I = ex_input_baseline excitatory_neurons.v = modellib.hodgkin_huxley_params["El"] * np.random.normal(1, 0.1, N_E) excitatory_spike_monitor = br.SpikeMonitor(excitatory_neurons) recorded_excitatory_neuron_idx = int(N_E / 2) excitatory_trace_recorder = br.StateMonitor(excitatory_neurons, 'v', record=[recorded_excitatory_neuron_idx]) excitatory_params['ghbar'] = 20. * nS ''' Inhibitory neurons ''' lif_interneuron_eqs = """ dv/dt =1.0/tau* (-v + u_ext) :volt (unless refractory) u_ext = u_ext_const : volt """ lif_interneuron_params = { "tau": 7 * ms, "v_threshold": -40 * mV, "v_reset": -60 * mV, "tau_refractory": 0.0 * ms, "u_ext_const": -50 * mV } lif_interneuron_options = { "threshold": "v>v_threshold", "reset": "v=v_reset", "refractory": "tau_refractory", 'method': 'euler' } inhibitory_neurons = br.NeuronGroup(N_I, lif_interneuron_eqs, namespace=lif_interneuron_params, **lif_interneuron_options) inhibitory_neurons.v = lif_interneuron_params["u_ext_const"] recorded_interneuron_idx = int(N_I / 2) inhibitory_trace_recorder = br.StateMonitor(inhibitory_neurons, 'v', record=[recorded_interneuron_idx]) inhibitory_spike_monitor = br.SpikeMonitor(inhibitory_neurons) ''' Connectivities ''' # EI Profile from Brunel: every excitatory neuron projects on inhibitory neurons of the same direction with a spread # of 80 degrees def get_phase_difference(total_difference): """ Map accumulated phase difference to shortest possible difference :param total_difference: :return: relative_difference """ return (total_difference + np.pi) % (2 * np.pi) - np.pi ex_in_differences = np.tile(ex_angles, (N_I, 1)).T - np.tile(in_angles, (N_E, 1)) ei_offset = 0 ei_offset_in_rad = ei_offset / 180.0 * np.pi ei_width = 80.0 / 180.0 * np.pi ei_weight_threshold = 0.1 max_excitatory_strength = 1 * mV ex_in_weights = np.exp(-(get_phase_difference((ex_in_differences + ei_offset_in_rad))) ** 2 / 2 / ei_width ** 2) ex_in_weights[np.where(ex_in_weights < ei_weight_threshold)] = 0.0 ex_indices, in_indices = ex_in_weights.nonzero() ei_synapses = br.Synapses(source=excitatory_neurons, target=inhibitory_neurons, model=modellib.delta_synapse_model, on_pre=modellib.delta_synapse_on_pre, namespace=modellib.delta_synapse_param) ei_synapses.connect() for ex_idx, in_idx in zip(ex_indices, in_indices): ei_synapses.synaptic_strength[ex_idx, in_idx] = max_excitatory_strength * ex_in_weights[ex_idx, in_idx] ie_offset = 0 ie_offset_in_rad = ie_offset / 180.0 * np.pi in_ex_difference = ex_in_differences.T ie_width = 160.0 / 180.0 * np.pi in_ex_weights = np.exp( -get_phase_difference(in_ex_difference + ie_offset_in_rad) ** 2 / 2 / ie_width ** 2) ie_synapses = br.Synapses(source=inhibitory_neurons, target=excitatory_neurons, model=modellib.exponential_synapse, on_pre=modellib.exponential_synapse_on_pre, namespace=modellib.exponential_synapse_params) ie_synapses.connect() ie_synapses.tau_syn = 2 * ms max_inhibitory_strength = 50 * nS in_indices, ex_indices = in_ex_weights.nonzero() for in_idx, ex_idx in zip(in_indices, ex_indices): ie_synapses.synaptic_strength[in_idx, ex_idx] = max_inhibitory_strength * in_ex_weights[in_idx, ex_idx] ''' Net ''' net = br.Network(excitatory_neurons) net.add(inhibitory_neurons) net.add(ei_synapses) net.add(ie_synapses) net.add(excitatory_spike_monitor) net.add(excitatory_trace_recorder) net.add(inhibitory_spike_monitor) net.add(inhibitory_trace_recorder) ''' Transient head direction input ''' start = 1000 * ms length = 1000 * ms phase_dispersion = 4 head_direction_input_amplitude = 1 * nA def get_head_direction_input(peak_phase, phase_dispersion, excitatory_direction_preference): """ Returns drive to a neuron with a certain preferred direction assuming that the input is distributed as a von Mises ( Gaussian on a ring) distribution with a peak phase and an uncertainty given by the phase dispersion. Normalized to one at the peak phase. :param peak_phase: :param phase_dispersion: :param excitatory_direction_preference: :return: """ head_direction_input_shape = np.exp( phase_dispersion * np.cos(excitatory_direction_preference - peak_phase)) / np.exp(phase_dispersion) return head_direction_input_shape ''' Run simulation ''' max_head_direction_input_amplitude = 0.5 * nA first_peak_phase = np.pi/2.0 second_peak_phase = -np.pi / 2.0 input_dispersion = 3 length_of_input = 200 * ms length_of_pause = 500 * ms events = [] events.append((0*ms, 'Start')) net.run(length_of_pause) events.append((excitatory_trace_recorder.t[-1], "Input at {:.1f}".format(first_peak_phase/np.pi*180.0))) excitatory_neurons.I = ex_input_baseline + max_head_direction_input_amplitude * get_head_direction_input( first_peak_phase, input_dispersion, ex_angles) net.run(length_of_input) events.append((excitatory_trace_recorder.t[-1], "No input".format(first_peak_phase/np.pi*180.0))) excitatory_neurons.I = ex_input_baseline net.run(length_of_pause) events.append((excitatory_trace_recorder.t[-1], "Input at {:.1f}".format(second_peak_phase/np.pi*180.0))) excitatory_neurons.I = ex_input_baseline + max_head_direction_input_amplitude * get_head_direction_input( second_peak_phase, input_dispersion, ex_angles) net.run(length_of_input) events.append((excitatory_trace_recorder.t[-1], "No input".format(first_peak_phase/np.pi*180.0))) excitatory_neurons.I = ex_input_baseline net.run(length_of_pause) ### Set synapses to zero to compare events.append((excitatory_trace_recorder.t[-1], "Set synapses to zero".format(first_peak_phase/np.pi*180.0))) ei_synapses.synaptic_strength = 0*mV net.run(length_of_pause) excitatory_neurons.I = ex_input_baseline + max_head_direction_input_amplitude * get_head_direction_input( first_peak_phase, input_dispersion, ex_angles) net.run(length_of_input) events.append((excitatory_trace_recorder.t[-1], "Input at {:.1f}".format(second_peak_phase/np.pi*180.0))) excitatory_neurons.I = ex_input_baseline net.run(length_of_pause) ''' Plot ''' duration = excitatory_trace_recorder.t[-1] fig, axes = plt.subplots(5, 1, sharex=True) ax_text = axes[0] for t, comment in events: ax_text.text(t/ms, 0.5, " "+comment,fontsize=12 ) ax_text.axis('off') ax_ex_trace = axes[1] ax_ex_trace.plot(excitatory_trace_recorder.t / ms, excitatory_trace_recorder.v[0] / mV, 'r') ax_ex_trace.set_ylabel("V ex (mV)") ax_ex_trace.set_ylabel("E {:.0f}° (mV)".format(ex_angles[recorded_excitatory_neuron_idx])) ax_in_trace = axes[2] ax_in_trace.plot(inhibitory_trace_recorder.t / ms, inhibitory_trace_recorder.v[0] / mV, 'b') ax_in_trace.set_ylabel("I {:.0f}° (mV)".format(in_angles[recorded_interneuron_idx])) ax_ex = axes[3] ax_ex.set_ylim(-180, 180) ax_ex.set_ylabel("E angles") ax_ex_raster = ax_ex.twinx() ex_spike_trains = excitatory_spike_monitor.spike_trains() for neuron_idx, spike_times in ex_spike_trains.items(): ax_ex_raster.plot(spike_times / ms, neuron_idx + 1 * np.ones(spike_times.shape), 'r|') ax_ex_raster.set_xlim(0, duration / ms) ax_ex_raster.set_ylim(0, N_E + 1) ax_ex_raster.set_ylabel("Excitatory neurons") ax_in = axes[4] ax_in.set_ylim(-180, 180) ax_in.set_ylabel("I angles") ax_in_raster = ax_in.twinx() in_spike_trains = inhibitory_spike_monitor.spike_trains() for neuron_idx, spike_times in in_spike_trains.items(): ax_in_raster.plot(spike_times / ms, neuron_idx + 1 * np.ones(spike_times.shape), 'b|') ax_in_raster.set_xlim(0, duration / ms) ax_in_raster.set_ylim(0, N_I + 1) ax_in_raster.set_ylabel("Inhibitory neurons") for ax in axes: ymin, ymax = ax.get_ylim() for t, _ in events: ax.vlines(t/ms, ymin, ymax, 'k', linestyles='--' ) plt.show()