Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/full-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Install basic Python dependencies
run: |
python -m pip install --upgrade pip
python -m pip install pytest pytest-cov coveralls
python -m pip install pytest pytest-cov coveralls flake8
python -m pip install mpi4py
python -m pip install -r requirements.txt
- name: Install Brian 2
Expand Down Expand Up @@ -60,6 +60,12 @@ jobs:
pushd pyNN/neuron/nmodl
nrnivmodl
popd
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 pyNN --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 pyNN --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Run unit and system tests
run: |
pytest -v --cov=pyNN --cov-report=term test
Expand Down
25 changes: 15 additions & 10 deletions pyNN/brian2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@
"""

import logging
import brian2
from pyNN import common, space
import brian2 # noqa: F401
from pyNN import common, space # noqa: F401
from pyNN.common.control import DEFAULT_MAX_DELAY, DEFAULT_TIMESTEP, DEFAULT_MIN_DELAY
from pyNN.connectors import *
from pyNN.standardmodels import StandardCellType
from pyNN.connectors import * # noqa: F401, F403
from pyNN.connectors import FixedProbabilityConnector
from pyNN.brian2 import simulator
from pyNN.brian2.standardmodels.cells import *
from pyNN.brian2.standardmodels.synapses import *
from pyNN.brian2.standardmodels.electrodes import *
from pyNN.brian2.standardmodels.receptors import (
from pyNN.brian2.standardmodels.cells import * # noqa: F401, F403
from pyNN.brian2.standardmodels.synapses import * # noqa: F401, F403
from pyNN.brian2.standardmodels.synapses import StaticSynapse
from pyNN.brian2.standardmodels.electrodes import * # noqa: F401, F403
from pyNN.brian2.standardmodels.electrodes import update_currents
from pyNN.brian2.standardmodels.receptors import ( # noqa: F401
CondAlphaPostSynapticResponse, AlphaPSR,
CondExpPostSynapticResponse, ExpPSR,
CurrExpPostSynapticResponse)
from pyNN.brian2.populations import Population, PopulationView, Assembly
from pyNN.brian2.populations import Population, PopulationView, Assembly # noqa: F401
from pyNN.brian2.projections import Projection
from pyNN.recording import get_io

Expand All @@ -27,7 +31,8 @@

def list_standard_models():
"""Return a list of all the StandardCellType classes available for this simulator."""
return [obj.__name__ for obj in globals().values() if isinstance(obj, type) and issubclass(obj, StandardCellType)]
return [obj.__name__ for obj in globals().values()
if isinstance(obj, type) and issubclass(obj, StandardCellType)]


def setup(timestep=DEFAULT_TIMESTEP, min_delay=DEFAULT_MIN_DELAY,
Expand All @@ -48,7 +53,7 @@ def setup(timestep=DEFAULT_TIMESTEP, min_delay=DEFAULT_MIN_DELAY,
simulator.state.num_processes = 1

simulator.state.network.add(
NetworkOperation(update_currents, when="start", clock=simulator.state.network.clock)
brian2.NetworkOperation(update_currents, when="start", clock=simulator.state.network.clock)
)
return rank()

Expand Down
44 changes: 2 additions & 42 deletions pyNN/brian2/cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import numpy as np
import brian2
from functools import reduce
from pyNN.parameters import Sequence, simplify
from pyNN.core import is_listlike
from pyNN import errors
Expand Down Expand Up @@ -156,13 +157,10 @@ def tau_refrac(self):

@tau_refrac.setter
def tau_refrac(self, tau_refrac_value):
#self._refracvalue = tau_refrac_value * ms
#brian2.NeuronGroup.__setattr__(self, 'tau_refrac', tau_refrac_value)
self._refractory = tau_refrac_value

@v_reset.setter
def v_reset(self, resetvalue):
#self._resetvalue = resetvalue * mV
self.event_codes['spike'] = 'v = {}*mV'.format(resetvalue / mV)


Expand All @@ -189,17 +187,6 @@ def __init__(self, n, equations, **parameters):
reset = 'v = {}*mV; g_r+= {}*nS; g_s+={}*nS'.format(
self._resetvalue / mV, self._q_rvalue / nS, self._q_svalue / nS)
refractory = self._refracvalue
'''
threshold = brian2.SimpleFunThreshold(self.check_threshold)
period = simplify(parameters['tau_refrac'])
assert not hasattr(period, "__len__"), "Brian2 does not support heterogenerous refractory periods with CustomRefractoriness"
reset = brian2.SimpleCustomRefractoriness(
AdaptiveReset2(parameters.pop('v_reset'),
parameters.pop('q_r'),
parameters.pop('q_s')),
period=period * second)
refractory = None
'''
BaseNeuronGroup.__init__(self, n, equations,
threshold, reset=reset, refractory=refractory,
method="rk2", **parameters)
Expand All @@ -222,24 +209,12 @@ def tau_refrac(self):

@tau_refrac.setter
def tau_refrac(self, tau_refrac_value):
#self._refracvalue = tau_refrac_value * ms
#brian2.NeuronGroup.__setattr__(self, 'tau_refrac', tau_refrac_value)
self._refractory = tau_refrac_value

@v_reset.setter
def v_reset(self, resetvalue):
#self._resetvalue = resetvalue * mV
self.event_codes['spike'] = 'v = {}*mV'.format(resetvalue / mV)

'''
tau_refrac = _new_property('', '_refractory_array', ms)
v_reset = _new_property('_resetfun.resetfun', 'v_reset', mV)
q_r = _new_property('_resetfun.resetfun', 'q_r', nA)
q_s = _new_property('_resetfun.resetfun', 'q_s', nA)

def check_threshold(self, v):
return v >= self.v_thresh
'''

# The below can be replaced by
# reset = '''v = v_reset
Expand All @@ -260,28 +235,14 @@ def __call__(self, P, spikes):
class IzhikevichNeuronGroup(BaseNeuronGroup):

def __init__(self, n, equations, **parameters):
#threshold = brian2.SimpleFunThreshold(self.check_threshold)
#threshold = 'v >= {}*mV'.format(parameters["v_thresh"][0]*1000)
threshold = 'v >= 30*mV'
self._resetvalue = parameters.pop('v_reset')[0]
self._dvalue = parameters.pop('d')[0]
reset = 'v = {}*mV; u+={}*mV/ms'.format(self._resetvalue / mV, self._dvalue / (mV/ms))
'''
reset = brian2.SimpleCustomRefractoriness(
IzhikevichReset(parameters['v_reset'],
parameters['d']),
period=0 * ms)
'''
refractory = 0 * ms
BaseNeuronGroup.__init__(self, n, equations,
threshold=threshold, reset=reset, refractory=refractory,
**parameters)
#self._variable_refractory_time = True
#self._refractory_variable = None
#self._S0 = self._S[:, 0]

#v_reset = _new_property('_resetfun.resetfun', 'Vr', mV)
#b = _new_property('_resetfun.resetfun', 'b', nA)

@property
def v_reset(self):
Expand All @@ -293,7 +254,6 @@ def d(self):

@v_reset.setter
def v_reset(self, resetvalue):
#self._resetvalue = resetvalue * mV
self.event_codes['spike'] = 'v = {}*mV'.format(resetvalue / mV)


Expand Down Expand Up @@ -339,7 +299,7 @@ def __init__(self, n, equations, spike_time_sequences=None):
def _convert_sequences_to_arrays(self, spike_time_sequences):
times = np.concatenate([seq.value for seq in spike_time_sequences])
indices = np.concatenate([i * np.ones(seq.value.size)
for i, seq in enumerate(spike_time_sequences)])
for i, seq in enumerate(spike_time_sequences)])
return indices, times * second
# todo: try to push the multiplication by seconds back into the translation step.
# note that the scaling from ms to seconds does take place during translation
Expand Down
26 changes: 12 additions & 14 deletions pyNN/brian2/populations.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,7 @@
from pyNN.parameters import ArrayParameter, ParameterSpace, simplify, LazyArray
from . import simulator
from .recording import Recorder
import numpy as np
from brian2.units.fundamentalunits import Quantity
#from brian2.units import *
#from quantities import *
from brian2.core.variables import VariableView
import brian2
#from brian2.groups.neurongroup import *
ms = brian2.ms
mV = brian2.mV

Expand All @@ -34,7 +28,8 @@ def _get_parameters(self, *names):
"""
def _get_component_parameters(component, names, component_label=None):
if component.computed_parameters_include(names):
native_names = component.get_native_names() # need all parameters in order to calculate values
# need all parameters in order to calculate values
native_names = component.get_native_names()
else:
native_names = component.get_native_names(*names, suffix=component_label)
native_parameter_space = self._get_native_parameters(*native_names)
Expand Down Expand Up @@ -67,10 +62,12 @@ def _get_component_parameters(component, names, component_label=None):
else:
parameter_space = ParameterSpace({})
for component_label, names in names_by_component.items():
parameter_space.add_child(component_label,
_get_component_parameters(self.celltype.post_synaptic_receptors[component_label],
names_by_component[component_label],
component_label))
parameter_space.add_child(
component_label,
_get_component_parameters(
self.celltype.post_synaptic_receptors[component_label],
names_by_component[component_label],
component_label))
else:
parameter_space = _get_component_parameters(self.celltype, names)
else:
Expand All @@ -85,7 +82,8 @@ class PopulationView(common.PopulationView, PopulationMixin):
def _get_parameters(self, *names):
if isinstance(self.celltype, StandardCellType):
if any(name in self.celltype.computed_parameters() for name in names):
native_names = self.celltype.get_native_names() # need all parameters in order to calculate values
# need all parameters in order to calculate values
native_names = self.celltype.get_native_names()
else:
native_names = self.celltype.get_native_names(*names)
native_parameter_space = self._get_native_parameters(*native_names)
Expand Down Expand Up @@ -136,9 +134,9 @@ class Population(common.Population, PopulationMixin):

def _create_cells(self):
id_range = np.arange(simulator.state.id_counter,
simulator.state.id_counter + self.size)
simulator.state.id_counter + self.size)
self.all_cells = np.array([simulator.ID(id) for id in id_range],
dtype=simulator.ID)
dtype=simulator.ID)
# all cells are local. This doesn't seem very efficient.
self._mask_local = np.ones((self.size,), bool)

Expand Down
18 changes: 12 additions & 6 deletions pyNN/brian2/projections.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
from collections import defaultdict
import numpy as np
import brian2
from brian2 import uS, nA, mV, ms, second
from brian2 import uS, nA, mV, ms
from pyNN import common
from pyNN.standardmodels.synapses import TsodyksMarkramSynapse
from pyNN.core import is_listlike
from pyNN.parameters import ParameterSpace, simplify
from pyNN.parameters import ParameterSpace
from pyNN.space import Space
from . import simulator
from .standardmodels.synapses import StaticSynapse
Expand Down Expand Up @@ -112,7 +112,10 @@ def __init__(self, presynaptic_population, postsynaptic_population,
# complete the synapse type equations according to the
# post-synaptic response type
psv = post.celltype.post_synaptic_variables[self.receptor_type]
if hasattr(post.celltype, "voltage_based_synapses") and post.celltype.voltage_based_synapses:
if (
hasattr(post.celltype, "voltage_based_synapses")
and post.celltype.voltage_based_synapses
):
weight_units = mV
else:
weight_units = post.celltype.conductance_based and uS or nA
Expand Down Expand Up @@ -222,10 +225,12 @@ def _convergent_connect(self, presynaptic_indices, postsynaptic_index,
except TypeError as err:
if "read-only" in str(err):
logger.info(
"Cannot set synaptic initial value for variable {}".format(name))
f"Cannot set synaptic initial value for variable {name}")
else:
raise
# brian2_var[i, j] = value # doesn't work with multiple connections between a given neuron pair. Need to understand the internals of Synapses and SynapticVariable better
# brian2_var[i, j] = value
# ^ doesn't work with multiple connections between a given neuron pair.
# Need to understand the internals of Synapses and SynapticVariable better

def _set_attributes(self, connection_parameters):
if isinstance(self.post, common.Assembly) or isinstance(self.pre, common.Assembly):
Expand Down Expand Up @@ -288,7 +293,8 @@ def _get_attributes_as_list(self, attribute_names):
value = getattr(syn_obj, name)[:]
# should really use the translated name
native_ps = ParameterSpace({name: value}, shape=value.shape)
# this whole "get attributes" thing needs refactoring in all backends to properly use translation
# todo: this whole "get attributes" thing needs refactoring
# in all backends to properly use translation
ps = self.synapse_type.reverse_translate(native_ps)
ps.evaluate()
value = ps[name]
Expand Down
11 changes: 4 additions & 7 deletions pyNN/brian2/recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
"""

import logging
from collections import defaultdict
import numpy as np
import quantities as pq
import brian2
from pyNN.core import is_listlike
from pyNN import recording
from . import simulator

Expand All @@ -27,7 +25,6 @@ class Recorder(recording.Recorder):
_simulator = simulator

def __init__(self, population=None, file=None):
__doc__ = recording.Recorder.__doc__
recording.Recorder.__init__(self, population, file)
self._devices = {} # defer creation until first call of run()

Expand All @@ -37,7 +34,8 @@ def _create_device(self, group, variable):
if variable == 'spikes':
self._devices[variable] = brian2.SpikeMonitor(group, record=self.recorded)
else:
varname = self.population.celltype.state_variable_translations[variable]['translated_name']
translations = self.population.celltype.state_variable_translations
varname = translations[variable]['translated_name']
neurons_to_record = np.sort(np.fromiter(
self.recorded[variable], dtype=int)) - self.population.first_id
self._devices[variable] = brian2.StateMonitor(group, varname,
Expand Down Expand Up @@ -89,8 +87,8 @@ def _get_all_signals(self, variable, ids, clear=False):
values = getattr(device, varname).T
else:
raise NotImplementedError # todo - construct a mask to get only the desired signals
values = self.population.celltype.state_variable_translations[variable]['reverse_transform'](
values)
translations = self.population.celltype.state_variable_translations
values = translations[variable]['reverse_transform'](values)
# because we use `when='end'`, need to add the value at the beginning of the run
tmp = np.empty((values.shape[0] + 1, values.shape[1]))
tmp[1:, :] = values
Expand All @@ -109,6 +107,5 @@ def _local_count(self, variable, filter_ids=None):
indices = np.fromiter(filtered_ids, dtype=int) - padding
spiky = self._devices['spikes'].spike_trains()
for i, id in zip(indices, filtered_ids):
#N[id] = len(self._devices['spikes'].spiketimes[i])
N[id] = len(spiky[i])
return N
4 changes: 2 additions & 2 deletions pyNN/brian2/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import brian2
import numpy as np
from pyNN import common
from pyNN.parameters import simplify


name = "Brian2"
Expand Down Expand Up @@ -113,7 +112,8 @@ def _get_min_delay(self):
if np.isinf(min_delay):
self._min_delay = self.dt
else:
self._min_delay = min_delay * self.dt # Synapses.delay is an integer, the number of time steps
# Synapses.delay is an integer, the number of time steps
self._min_delay = min_delay * self.dt
return self._min_delay

def _set_min_delay(self, delay):
Expand Down
Loading