def test_automatic_augmented_assignments(): # We test that statements that could be rewritten as augmented assignments # are correctly rewritten (using sympy to test for symbolic equality) variables = { 'x': ArrayVariable('x', owner=None, size=10, device=device), 'y': ArrayVariable('y', owner=None, size=10, device=device), 'z': ArrayVariable('y', owner=None, size=10, device=device), 'b': ArrayVariable('b', owner=None, size=10, dtype=bool, device=device), 'clip': DEFAULT_FUNCTIONS['clip'], 'inf': DEFAULT_CONSTANTS['inf'] } statements = [ # examples that should be rewritten # Note that using our approach, we will never get -= or /= but always # the equivalent += or *= statements ('x = x + 1.0', 'x += 1.0'), ('x = 2.0 * x', 'x *= 2.0'), ('x = x - 3.0', 'x += -3.0'), ('x = x/2.0', 'x *= 0.5'), ('x = y + (x + 1.0)', 'x += y + 1.0'), ('x = x + x', 'x *= 2.0'), ('x = x + y + z', 'x += y + z'), ('x = x + y + z', 'x += y + z'), # examples that should not be rewritten ('x = 1.0/x', 'x = 1.0/x'), ('x = 1.0', 'x = 1.0'), ('x = 2.0*(x + 1.0)', 'x = 2.0*(x + 1.0)'), ('x = clip(x + y, 0.0, inf)', 'x = clip(x + y, 0.0, inf)'), ('b = b or False', 'b = b or False') ] for orig, rewritten in statements: scalar, vector = make_statements(orig, variables, np.float32) try: # we augment the assertion error with the original statement assert len( scalar ) == 0, 'Did not expect any scalar statements but got ' + str( scalar) assert len( vector ) == 1, 'Did expect a single statement but got ' + str(vector) statement = vector[0] expected_var, expected_op, expected_expr, _ = parse_statement( rewritten) assert expected_var == statement.var, 'expected write to variable %s, not to %s' % ( expected_var, statement.var) assert expected_op == statement.op, 'expected operation %s, not %s' % ( expected_op, statement.op) # Compare the two expressions using sympy to allow for different order etc. sympy_expected = str_to_sympy(expected_expr) sympy_actual = str_to_sympy(statement.expr) assert sympy_expected == sympy_actual, ( 'RHS expressions "%s" and "%s" are not identical' % (sympy_to_str(sympy_expected), sympy_to_str(sympy_actual))) except AssertionError as ex: raise AssertionError( 'Transformation for statement "%s" gave an unexpected result: %s' % (orig, str(ex)))
def _create_variables(self): ''' Create the variables dictionary for this `NeuronGroup`, containing entries for the equation variables and some standard entries. ''' # Get the standard variables for all groups s = Group._create_variables(self) # Standard variables always present s.update({ '_spikespace': ArrayVariable('_spikespace', Unit(1), self._spikespace, group_name=self.name) }) s.update({ '_spikes': AttributeVariable(Unit(1), self, 'spikes', constant=False) }) for eq in self.equations.itervalues(): if eq.type in (DIFFERENTIAL_EQUATION, PARAMETER): array = self.arrays[eq.varname] constant = ('constant' in eq.flags) s.update({ eq.varname: ArrayVariable(eq.varname, eq.unit, array, group_name=self.name, constant=constant, is_bool=eq.is_bool) }) elif eq.type == STATIC_EQUATION: s.update({ eq.varname: Subexpression(eq.unit, brian_prefs['core.default_scalar_dtype'], str(eq.expr), variables=s, namespace=self.namespace, is_bool=eq.is_bool) }) else: raise AssertionError('Unknown type of equation: ' + eq.eq_type) # Stochastic variables for xi in self.equations.stochastic_variables: s.update({xi: StochasticVariable()}) return s
def test_illegal_calls(): eqs = Equations('dv/dt = -v / (10*ms) : 1') clock = Clock(dt=0.1 * ms) variables = { 'v': ArrayVariable(name='name', size=10, owner=None, device=None, dtype=np.float64, constant=False), 't': clock.variables['t'], 'dt': clock.variables['dt'] } assert_raises( TypeError, lambda: StateUpdateMethod.apply_stateupdater(eqs, variables, object())) assert_raises( TypeError, lambda: StateUpdateMethod.apply_stateupdater( eqs, variables, group_name='my_name', method=object())) assert_raises( TypeError, lambda: StateUpdateMethod.apply_stateupdater( eqs, variables, [object(), 'euler'])) assert_raises( TypeError, lambda: StateUpdateMethod.apply_stateupdater( eqs, variables, group_name='my_name', method=[object(), 'euler']))
def __init__(self, source, variables, record=None, when=None, name='statemonitor*', codeobj_class=None): self.source = weakref.proxy(source) self.codeobj_class = codeobj_class # run by default on source clock at the end scheduler = Scheduler(when) if not scheduler.defined_clock: scheduler.clock = source.clock if not scheduler.defined_when: scheduler.when = 'end' BrianObject.__init__(self, when=scheduler, name=name) # variables should always be a list of strings if variables is True: variables = source.equations.names elif isinstance(variables, str): variables = [variables] #: The variables to record self.record_variables = variables # record should always be an array of ints self.record_all = False if record is True: self.record_all = True record = source.item_mapping[:] elif record is None or record is False: record = np.array([], dtype=np.int32) elif isinstance(record, int): record = np.array([source.item_mapping[record]], dtype=np.int32) else: record = np.array(source.item_mapping[record], dtype=np.int32) #: The array of recorded indices self.indices = record # create data structures self.reinit() # Setup variables self.variables = {} for varname in variables: var = source.variables[varname] if not (np.issubdtype(var.dtype, np.float64) and np.issubdtype(np.float64, var.dtype)): raise NotImplementedError(('Cannot record %s with data type ' '%s, currently only values stored as ' 'doubles can be recorded.') % (varname, var.dtype)) self.variables[varname] = var self.variables['_recorded_'+varname] = Variable(Unit(1), self._values[varname]) self.variables['_t'] = Variable(Unit(1), self._t) self.variables['_clock_t'] = AttributeVariable(second, self.clock, 't_') self.variables['_indices'] = ArrayVariable('_indices', Unit(1), self.indices, constant=True) self._group_attribute_access_active = True
def __init__(self, N, offset, group): self.N = N self.offset = int(offset) self.group = weakref.proxy(group) self._indices = np.arange(self.N + self.offset) self.variables = {'i': ArrayVariable('i', Unit(1), self._indices - self.offset)} Variable.__init__(self, Unit(1), value=self, constant=True)
def check_permutation_code(code): from collections import defaultdict vars = get_identifiers(code) indices = defaultdict(lambda: '_idx') for var in vars: if var.endswith('_syn'): indices[var] = '_idx' elif var.endswith('_pre'): indices[var] = '_presynaptic_idx' elif var.endswith('_post'): indices[var] = '_postsynaptic_idx' variables = dict() for var in indices: variables[var] = ArrayVariable(var, 1, None, 10, device) variables['_presynaptic_idx'] = ArrayVariable(var, 1, None, 10, device) variables['_postsynaptic_idx'] = ArrayVariable(var, 1, None, 10, device) scalar_statements, vector_statements = make_statements( code, variables, float64) check_for_order_independence(vector_statements, variables, indices)
def _set_with_code(self, variable, group_indices, code, check_units=True, level=0): ''' Sets a variable using a string expression. Is called by `VariableView.__setitem__` for statements such as `S.var[:, :] = 'exp(-abs(i-j)/space_constant)*nS'` Parameters ---------- variable : `ArrayVariable` The `Variable` for the variable to be set group_indices : ndarray of int The indices of the elements that are to be set. code : str The code that should be executed to set the variable values. Can contain references to indices, such as `i` or `j` check_units : bool, optional Whether to check the units of the expression. level : int, optional How much farther to go down in the stack to find the namespace. Necessary so that both `X.var = ` and `X.var[:] = ` have access to the surrounding namespace. ''' abstract_code = variable.name + ' = ' + code namespace = get_local_namespace(level + 1) additional_namespace = ('implicit-namespace', namespace) # TODO: Find a name that makes sense for reset and variable setting # with code additional_variables = self.item_mapping.variables additional_variables['_spikes'] = ArrayVariable('_spikes', Unit(1), value=group_indices.astype(np.int32), group_name=self.name) # TODO: Have an additional argument to avoid going through the index # array for situations where iterate_all could be used codeobj = create_runner_codeobj(self, abstract_code, 'reset', additional_variables=additional_variables, additional_namespace=additional_namespace, check_units=check_units) codeobj()
def __init__(self, N, rates, when=None, name='poissongroup*'): # TODO: sort out the default values in Scheduler scheduler = Scheduler(when) scheduler.when = 'thresholds' BrianObject.__init__(self, when=scheduler, name=name) #: The array of spikes from the most recent time step self.spikes = np.array([], dtype=int) self._rates = np.asarray(rates) self.N = N = int(N) self.pthresh = self._calc_threshold() self.variables = { 'rates': ArrayVariable('rates', Hz, self._rates, group_name=self.name, constant=True) } Group.__init__(self)
from pylab import * from brian2 import * from brian2.codegen.generators.cython_generator import CythonCodeGenerator from brian2.codegen.statements import Statement from brian2.codegen.translation import make_statements from brian2.core.variables import Variable, ArrayVariable owner = None code = ''' x = a*b+c ''' variables = { 'a': ArrayVariable('a', Unit(1), owner, 10, get_device()), 'b': ArrayVariable('b', Unit(1), owner, 10, get_device()), 'x': ArrayVariable('x', Unit(1), owner, 10, get_device()), } namespace = {} variable_indices = {'a': '_idx', 'b': '_idx', 'x': '_idx'} gen = CythonCodeGenerator(variables, variable_indices, owner, iterate_all=True, codeobj_class=None) #print gen.translate_expression('a*b+c') #print gen.translate_statement(Statement('x', '=', 'a*b+c', '', float)) stmts = make_statements(code, variables, float)
def test_priority(): updater = ExplicitStateUpdater('x_new = x + dt * f(x, t)') # Equations that work for the state updater eqs = Equations('dv/dt = -v / (10*ms) : 1') clock = Clock(dt=0.1 * ms) variables = { 'v': ArrayVariable(name='name', size=10, owner=None, device=None, dtype=np.float64, constant=False), 'w': ArrayVariable(name='name', size=10, owner=None, device=None, dtype=np.float64, constant=False), 't': clock.variables['t'], 'dt': clock.variables['dt'] } updater(eqs, variables) # should not raise an error # External parameter in the coefficient, linear integration should work param = 1 eqs = Equations('dv/dt = -param * v / (10*ms) : 1') updater(eqs, variables) # should not raise an error can_integrate = { linear: True, euler: True, exponential_euler: True, rk2: True, rk4: True, heun: True, milstein: True } check_integration(eqs, variables, can_integrate) # Constant equation, should work for all except linear (see #1010) param = 1 eqs = Equations('''dv/dt = 10*Hz : 1 dw/dt = -v/(10*ms) : 1''') updater(eqs, variables) # should not raise an error can_integrate = { linear: None, euler: True, exponential_euler: True, rk2: True, rk4: True, heun: True, milstein: True } check_integration(eqs, variables, can_integrate) # Equations resulting in complex linear solution for older versions of sympy eqs = Equations('''dv/dt = (ge+gi-(v+49*mV))/(20*ms) : volt dge/dt = -ge/(5*ms) : volt dgi/dt = Dgi/(5*ms) : volt dDgi/dt = ((-2./5) * Dgi - (1./5**2)*gi)/(10*ms) : volt''') can_integrate = { linear: None, euler: True, exponential_euler: True, rk2: True, rk4: True, heun: True, milstein: True } check_integration(eqs, variables, can_integrate) # Equation with additive noise eqs = Equations('dv/dt = -v / (10*ms) + xi/(10*ms)**.5 : 1') assert_raises(UnsupportedEquationsException, lambda: updater(eqs, variables)) can_integrate = { linear: False, euler: True, exponential_euler: False, rk2: False, rk4: False, heun: True, milstein: True } check_integration(eqs, variables, can_integrate) # Equation with multiplicative noise eqs = Equations('dv/dt = -v / (10*ms) + v*xi/(10*ms)**.5 : 1') assert_raises(UnsupportedEquationsException, lambda: updater(eqs, variables)) can_integrate = { linear: False, euler: False, exponential_euler: False, rk2: False, rk4: False, heun: True, milstein: True } check_integration(eqs, variables, can_integrate)
def test_priority(): updater = ExplicitStateUpdater('x_new = x + dt * f(x, t)') # Equations that work for the state updater eqs = Equations('dv/dt = -v / (10*ms) : 1') # Fake clock class MyClock = namedtuple('MyClock', ['t_', 'dt_']) clock = MyClock(t_=0, dt_=0.0001) variables = {'v': ArrayVariable(name='name', unit=Unit(1), size=10, owner=None, device=None, dtype=np.float64, constant=False), 't': AttributeVariable(name='t', unit=second, obj=clock, attribute='t_', constant=False, dtype=np.float64), 'dt': AttributeVariable(name='dt', unit=second, obj=clock, attribute='dt_', constant=True, dtype=np.float64)} assert updater.can_integrate(eqs, variables) # Non-constant parameter in the coefficient, linear integration does not # work eqs = Equations('''dv/dt = -param * v / (10*ms) : 1 param : 1''') variables['param'] = ArrayVariable(name='name', unit=Unit(1), owner=None, size=10, dtype=np.float64, constant=False, device=None) assert updater.can_integrate(eqs, variables) can_integrate = {linear: False, euler: True, rk2: True, rk4: True, milstein: True} for integrator, able in can_integrate.iteritems(): assert integrator.can_integrate(eqs, variables) == able # Constant parameter in the coefficient, linear integration should # work eqs = Equations('''dv/dt = -param * v / (10*ms) : 1 param : 1 (constant)''') variables['param'] = ArrayVariable(name='name', unit=Unit(1), owner=None, size=10, dtype=np.float64, device=None, constant=True) assert updater.can_integrate(eqs, variables) can_integrate = {linear: True, euler: True, rk2: True, rk4: True, milstein: True} del variables['param'] for integrator, able in can_integrate.iteritems(): assert integrator.can_integrate(eqs, variables) == able # External parameter in the coefficient, linear integration *should* work # (external parameters don't change during a run) param = 1 eqs = Equations('dv/dt = -param * v / (10*ms) : 1') assert updater.can_integrate(eqs, variables) can_integrate = {linear: True, euler: True, rk2: True, rk4: True, milstein: True} for integrator, able in can_integrate.iteritems(): assert integrator.can_integrate(eqs, variables) == able # Equation with additive noise eqs = Equations('dv/dt = -v / (10*ms) + xi/(10*ms)**.5 : 1') assert not updater.can_integrate(eqs, variables) can_integrate = {linear: False, euler: True, rk2: False, rk4: False, milstein: True} for integrator, able in can_integrate.iteritems(): assert integrator.can_integrate(eqs, variables) == able # Equation with multiplicative noise eqs = Equations('dv/dt = -v / (10*ms) + v*xi/(10*ms)**.5 : 1') assert not updater.can_integrate(eqs, variables) can_integrate = {linear: False, euler: False, rk2: False, rk4: False, milstein: True} for integrator, able in can_integrate.iteritems(): assert integrator.can_integrate(eqs, variables) == able
def test_priority(): updater = ExplicitStateUpdater('x_new = x + dt * f(x, t)') # Equations that work for the state updater eqs = Equations('dv/dt = -v / (10*ms) : 1') clock = Clock(dt=0.1*ms) variables = {'v': ArrayVariable(name='name', unit=Unit(1), size=10, owner=None, device=None, dtype=np.float64, constant=False), 't': clock.variables['t'], 'dt': clock.variables['dt']} updater(eqs, variables) # should not raise an error # External parameter in the coefficient, linear integration should work param = 1 eqs = Equations('dv/dt = -param * v / (10*ms) : 1') updater(eqs, variables) # should not raise an error can_integrate = {linear: True, euler: True, rk2: True, rk4: True, heun: True, milstein: True} for integrator, able in can_integrate.iteritems(): try: integrator(eqs, variables) if not able: raise AssertionError('Should not be able to integrate these ' 'equations') except UnsupportedEquationsException: if able: raise AssertionError('Should be able to integrate these ' 'equations') # Equation with additive noise eqs = Equations('dv/dt = -v / (10*ms) + xi/(10*ms)**.5 : 1') assert_raises(UnsupportedEquationsException, lambda: updater(eqs, variables)) can_integrate = {linear: False, euler: True, rk2: False, rk4: False, heun: True, milstein: True} for integrator, able in can_integrate.iteritems(): try: integrator(eqs, variables) if not able: raise AssertionError('Should not be able to integrate these ' 'equations') except UnsupportedEquationsException: if able: raise AssertionError('Should be able to integrate these ' 'equations') # Equation with multiplicative noise eqs = Equations('dv/dt = -v / (10*ms) + v*xi/(10*ms)**.5 : 1') assert_raises(UnsupportedEquationsException, lambda: updater(eqs, variables)) can_integrate = {linear: False, euler: False, rk2: False, rk4: False, heun: True, milstein: True} for integrator, able in can_integrate.iteritems(): try: integrator(eqs, variables) if not able: raise AssertionError('Should not be able to integrate these ' 'equations') except UnsupportedEquationsException: if able: raise AssertionError('Should be able to integrate these ' 'equations')
# expr=statement.expr) # line = word_substitute(line, subs) # lines.append(line) # return '\n'.join(lines) if __name__ == '__main__': from brian2.codegen.translation import make_statements from brian2.core.variables import ArrayVariable from brian2 import device from numpy import float64 code = ''' w_syn = v_pre v_pre += -a_syn # change operator -= or += to see efficient/inefficient code x_post = y_post ''' indices = { 'w_syn': '_idx', 'a_syn': '_idx', 'u_pre': '_presynaptic_idx', 'v_pre': '_presynaptic_idx', 'x_post': '_postsynaptic_idx', 'y_post': '_postsynaptic_idx' } variables = dict() for var in indices: variables[var] = ArrayVariable(var, 1, None, 10, device) scalar_statements, vector_statements = make_statements( code, variables, float64) print vectorise_synapses_code(vector_statements, variables, indices)
def __init__(self, source, target=None, model=None, pre=None, post=None, connect=False, delay=None, namespace=None, dtype=None, codeobj_class=None, clock=None, method=None, name='synapses*'): BrianObject.__init__(self, when=clock, name=name) self.codeobj_class = codeobj_class self.source = weakref.proxy(source) if target is None: self.target = self.source else: self.target = weakref.proxy(target) ##### Prepare and validate equations if model is None: model = '' if isinstance(model, basestring): model = Equations(model) if not isinstance(model, Equations): raise TypeError(('model has to be a string or an Equations ' 'object, is "%s" instead.') % type(model)) # Check flags model.check_flags({ DIFFERENTIAL_EQUATION: ['event-driven', 'lumped'], STATIC_EQUATION: ['lumped'], PARAMETER: ['constant', 'lumped'] }) # Separate the equations into event-driven and continuously updated # equations event_driven = [] continuous = [] for single_equation in model.itervalues(): if 'event-driven' in single_equation.flags: if 'lumped' in single_equation.flags: raise ValueError( ('Event-driven variable %s cannot be ' 'a lumped variable.') % single_equation.varname) event_driven.append(single_equation) else: continuous.append(single_equation) # Add the lastupdate variable, used by event-driven equations continuous.append(SingleEquation(PARAMETER, 'lastupdate', second)) if len(event_driven): self.event_driven = Equations(event_driven) else: self.event_driven = None self.equations = Equations(continuous) ##### Setup the memory self.arrays = self._allocate_memory(dtype=dtype) # Setup the namespace self._given_namespace = namespace self.namespace = create_namespace(namespace) self._queues = {} self._delays = {} self.item_mapping = SynapticItemMapping(self) self.indices = { '_idx': self.item_mapping, '_presynaptic_idx': self.item_mapping.synaptic_pre, '_postsynaptic_idx': self.item_mapping.synaptic_post } # Allow S.i instead of S.indices.i, etc. self.i = self.item_mapping.i self.j = self.item_mapping.j self.k = self.item_mapping.k # Setup variables self.variables = self._create_variables() #: List of names of all updaters, e.g. ['pre', 'post'] self._updaters = [] for prepost, argument in zip(('pre', 'post'), (pre, post)): if not argument: continue if isinstance(argument, basestring): self._add_updater(argument, prepost) elif isinstance(argument, collections.Mapping): for key, value in argument.iteritems(): if not isinstance(key, basestring): err_msg = ('Keys for the "{}" argument' 'have to be strings, got ' '{} instead.').format(prepost, type(key)) raise TypeError(err_msg) self._add_updater(value, prepost, objname=key) # If we have a pathway called "pre" (the most common use case), provide # direct access to its delay via a delay attribute (instead of having # to use pre.delay) if 'pre' in self._updaters: self.variables['delay'] = self.pre.variables['delay'] if delay is not None: if isinstance(delay, Quantity): if not 'pre' in self._updaters: raise ValueError( ('Cannot set delay, no "pre" pathway exists.' 'Use a dictionary if you want to set the ' 'delay for a pathway with a different name.')) delay = {'pre': delay} if not isinstance(delay, collections.Mapping): raise TypeError('Delay argument has to be a quantity or a ' 'dictionary, is type %s instead.' % type(delay)) for pathway, pathway_delay in delay.iteritems(): if not pathway in self._updaters: raise ValueError(('Cannot set the delay for pathway ' '"%s": unknown pathway.') % pathway) if not isinstance(pathway_delay, Quantity): raise TypeError(('Cannot set the delay for pathway "%s": ' 'expected a quantity, got %s instead.') % (pathway, type(pathway_delay))) if pathway_delay.size != 1: raise TypeError(('Cannot set the delay for pathway "%s": ' 'expected a scalar quantity, got a ' 'quantity with shape %s instead.') % str(pathway_delay.shape)) fail_for_dimension_mismatch(pathway_delay, second, ('Delay has to be ' 'specified in units ' 'of seconds')) updater = getattr(self, pathway) self.item_mapping.unregister_variable(updater._delays) del updater._delays # For simplicity, store the delay as a one-element array # so that for example updater._delays[:] works. updater._delays = np.array([float(pathway_delay)]) variable = ArrayVariable('delay', second, updater._delays, group_name=self.name, scalar=True) updater.variables['delay'] = variable if pathway == 'pre': self.variables['delay'] = variable #: Performs numerical integration step self.state_updater = StateUpdater(self, method) self.contained_objects.append(self.state_updater) #: "Lumped variable" mechanism -- sum over all synapses of a #: postsynaptic target self.lumped_updaters = {} for single_equation in self.equations.itervalues(): if 'lumped' in single_equation.flags: varname = single_equation.varname # For a lumped variable, we need an equivalent parameter in the # target group if not varname in self.target.variables: raise ValueError( ('The lumped variable %s needs a variable ' 'of the same name in the target ' 'group ') % single_equation.varname) fail_for_dimension_mismatch(self.variables[varname].unit, self.target.variables[varname], ('Lumped variables need to have ' 'the same units in Synapses ' 'and the target group')) # TODO: Add some more stringent check about the type of # variable in the target group updater = LumpedUpdater(varname, self, self.target) self.lumped_updaters[varname] = updater self.contained_objects.append(updater) # Do an initial connect, if requested if not isinstance(connect, (bool, basestring)): raise TypeError( ('"connect" keyword has to be a boolean value or a ' 'string, is type %s instead.' % type(connect))) self._initial_connect = connect if not connect is False: self.connect(connect, level=1) # Activate name attribute access Group.__init__(self)
def __getitem__(self, index): ''' Returns synaptic indices for `index`, which can be a tuple of indices (including arrays and slices), a single index or a string. ''' if (not isinstance(index, (tuple, basestring)) and isinstance( index, (int, np.ndarray, slice, collections.Sequence))): index = (index, slice(None), slice(None)) if isinstance(index, tuple): if len(index) == 2: # two indices (pre- and postsynaptic cell) index = (index[0], index[1], slice(None)) elif len(index) > 3: raise IndexError('Need 1, 2 or 3 indices, got %d.' % len(index)) I, J, K = index pre_synapses = find_synapses(I, self.pre_synaptic, self.synaptic_pre) post_synapses = find_synapses(J, self.post_synaptic, self.synaptic_post) matching_synapses = np.intersect1d(pre_synapses, post_synapses, assume_unique=True) if K == slice(None): return matching_synapses elif isinstance(K, (int, slice)): test_k = slice_to_test(K) else: raise NotImplementedError(('Indexing synapses with arrays not' 'implemented yet')) pre_neurons = self.synaptic_pre[pre_synapses] post_neurons = self.synaptic_post[post_synapses] synapse_numbers = _synapse_numbers(pre_neurons, post_neurons) return np.intersect1d(matching_synapses, np.flatnonzero(test_k(synapse_numbers)), assume_unique=True) elif isinstance(index, basestring): # interpret the string expression identifiers = get_identifiers(index) variables = dict(self.variables) if 'k' in identifiers: synapse_numbers = _synapse_numbers(self.synaptic_pre[:], self.synaptic_post[:]) variables['k'] = ArrayVariable('k', Unit(1), synapse_numbers) namespace = get_local_namespace(1) additional_namespace = ('implicit-namespace', namespace) abstract_code = '_cond = ' + index codeobj = create_runner_codeobj( self.synapses, abstract_code, 'state_variable_indexing', additional_variables=variables, additional_namespace=additional_namespace, ) result = codeobj() return result else: raise IndexError( 'Unsupported index type {itype}'.format(itype=type(index)))
def _add_synapses(self, sources, targets, n, p, condition=None, level=0): if condition is None: sources = np.atleast_1d(sources) targets = np.atleast_1d(targets) n = np.atleast_1d(n) p = np.atleast_1d(p) if not len(p) == 1 or p != 1: use_connections = np.random.rand(len(sources)) < p sources = sources[use_connections] targets = targets[use_connections] n = n[use_connections] sources = sources.repeat(n) targets = targets.repeat(n) new_synapses = len(sources) old_N = self.N new_N = old_N + new_synapses self._resize(new_N) self.synaptic_pre[old_N:new_N] = sources self.synaptic_post[old_N:new_N] = targets synapse_idx = old_N for source, target in zip(sources, targets): synapses = self.pre_synaptic[source] synapses.resize(len(synapses) + 1) synapses[-1] = synapse_idx synapses = self.post_synaptic[target] synapses.resize(len(synapses) + 1) synapses[-1] = synapse_idx synapse_idx += 1 else: abstract_code = '_cond = ' + condition + '\n' abstract_code += '_n = ' + str(n) + '\n' abstract_code += '_p = ' + str(p) namespace = get_local_namespace(level + 1) additional_namespace = ('implicit-namespace', namespace) variables = { '_source_neurons': ArrayVariable('_source_neurons', Unit(1), self.source.item_mapping[:] - self.source.offset, constant=True), '_target_neurons': ArrayVariable('_target_neurons', Unit(1), self.target.item_mapping[:] - self.target.offset, constant=True), # The template needs to have access to the DynamicArray here, # having access to the underlying array (which would be much # faster), is not enough '_synaptic_pre': Variable(Unit(1), self.synaptic_pre, constant=True), '_synaptic_post': Variable(Unit(1), self.synaptic_post, constant=True), '_pre_synaptic': Variable(Unit(1), self.pre_synaptic, constant=True), '_post_synaptic': Variable(Unit(1), self.post_synaptic, constant=True), # Will be set in the template 'i': Variable(unit=Unit(1), constant=True), 'j': Variable(unit=Unit(1), constant=True) } codeobj = create_runner_codeobj( self.synapses, abstract_code, 'synapses_create', additional_variables=variables, additional_namespace=additional_namespace, check_units=False) codeobj() number = len(self.synaptic_pre) for variable in self._registered_variables: variable.resize(number)