Пример #1
0
def test_nested_subexpressions():
    '''
    This test checks that code translation works with nested subexpressions.
    '''
    code = '''
    x = a + b + c
    c = 1
    x = a + b + c
    d = 1
    x = a + b + c
    '''
    variables = {
        'a':
        Subexpression(name='a',
                      unit=Unit(1),
                      dtype=np.float32,
                      owner=FakeGroup(variables={}),
                      device=None,
                      expr='b*b+d'),
        'b':
        Subexpression(name='b',
                      unit=Unit(1),
                      dtype=np.float32,
                      owner=FakeGroup(variables={}),
                      device=None,
                      expr='c*c*c'),
        'c':
        Variable(unit=None, name='c'),
        'd':
        Variable(unit=None, name='d'),
    }
    stmts = make_statements(code, variables, np.float32)
    evalorder = ''.join(stmt.var for stmt in stmts)
    # This is the order that variables ought to be evaluated in
    assert evalorder == 'baxcbaxdax'
Пример #2
0
 def __init__(self, dt, name='clock*'):
     # We need a name right away because some devices (e.g. cpp_standalone)
     # need a name for the object when creating the variables
     Nameable.__init__(self, name=name)
     #: Note that right after a change of dt, this
     #: will not equal the new dt (which is stored in `Clock._new_dt`). Call
     #: `Clock._set_t_update_t` to update the internal clock representation.
     self._new_dt = None
     self.variables = Variables(self)
     self.variables.add_array('timestep',
                              unit=Unit(1),
                              size=1,
                              dtype=np.uint64,
                              read_only=True,
                              scalar=True)
     self.variables.add_array('t',
                              unit=second,
                              size=1,
                              dtype=np.double,
                              read_only=True,
                              scalar=True)
     self.variables.add_array('dt',
                              unit=second,
                              size=1,
                              values=float(dt),
                              dtype=np.float,
                              read_only=True,
                              constant=True,
                              scalar=True)
     self.variables.add_constant('N', unit=Unit(1), value=1)
     self._enable_group_attributes()
     self.dt = dt
     logger.diagnostic("Created clock {name} with dt={dt}".format(
         name=self.name, dt=self.dt))
Пример #3
0
def test_apply_loop_invariant_optimisation_boolean():
    variables = {'v1': Variable('v1', scalar=False),
                 'v2': Variable('v2', scalar=False),
                 'N': Constant('N', 10),
                 'b': Variable('b', scalar=True, dtype=bool),
                 'c': Variable('c', scalar=True, dtype=bool),
                 'int': DEFAULT_FUNCTIONS['int'],
                 'foo': Function(lambda x: None,
                                 arg_units=[Unit(1)], return_unit=Unit(1),
                                 arg_types=['boolean'], return_type='float',
                                 stateless=False)
                 }
    # The calls for "foo" cannot be pulled out, since foo is marked as stateful
    statements = [Statement('v1', '=', '1.0*int(b and c)', '', np.float32),
                  Statement('v1', '=', '1.0*foo(b and c)', '', np.float32),
                  Statement('v2', '=', 'int(not b and True)', '', np.float32),
                  Statement('v2', '=', 'foo(not b and True)', '', np.float32)
                  ]
    scalar, vector = optimise_statements([], statements, variables)
    assert len(scalar) == 4
    assert scalar[0].expr == '1.0 * int(b and c)'
    assert scalar[1].expr == 'b and c'
    assert scalar[2].expr == 'int((not b) and True)'
    assert scalar[3].expr == '(not b) and True'
    assert len(vector) == 4
    assert vector[0].expr == '_lio_1'
    assert vector[1].expr == 'foo(_lio_2)'
    assert vector[2].expr == '_lio_3'
    assert vector[3].expr == 'foo(_lio_4)'
Пример #4
0
    def __init__(self,
                 source,
                 when=None,
                 name='ratemonitor*',
                 codeobj_class=None):
        self.source = weakref.proxy(source)

        # 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'

        self.codeobj_class = codeobj_class
        BrianObject.__init__(self, when=scheduler, name=name)

        # create data structures
        self.reinit()

        self.variables = {
            't': AttributeVariable(second, self.clock, 't'),
            'dt': AttributeVariable(second, self.clock, 'dt', constant=True),
            '_spikes': AttributeVariable(Unit(1), self.source, 'spikes'),
            # The template needs to have access to the
            # DynamicArray here, having access to the underlying
            # array is not enough since we want to do the resize
            # in the template
            '_rate': Variable(Unit(1), self._rate),
            '_t': Variable(Unit(1), self._t),
            '_num_source_neurons': Variable(Unit(1), len(self.source))
        }
Пример #5
0
    def __init__(self, synapses):
        Variable.__init__(self, Unit(1), value=self, constant=True)
        self.source = synapses.source
        self.target = synapses.target
        source_len = len(synapses.source)
        target_len = len(synapses.target)
        self.synapses = weakref.proxy(synapses)
        dtype = smallest_inttype(MAX_SYNAPSES)
        self.synaptic_pre = DynamicArray1D(0, dtype=dtype)
        self.synaptic_post = DynamicArray1D(0, dtype=dtype)
        self.pre_synaptic = [
            DynamicArray1D(0, dtype=dtype) for _ in xrange(source_len)
        ]
        self.post_synaptic = [
            DynamicArray1D(0, dtype=dtype) for _ in xrange(target_len)
        ]

        self._registered_variables = []

        self.variables = {
            'i': DynamicArrayVariable('i', Unit(1), self.synaptic_pre),
            'j': DynamicArrayVariable('j', Unit(1), self.synaptic_post)
        }
        self.i = IndexView(self.synaptic_pre, self)
        self.j = IndexView(self.synaptic_post, self)
        self.k = SynapseIndexView(self)
Пример #6
0
 def __init__(self, dt, name='clock*'):
     # We need a name right away because some devices (e.g. cpp_standalone)
     # need a name for the object when creating the variables
     Nameable.__init__(self, name=name)
     self._old_dt = None
     self.variables = Variables(self)
     self.variables.add_array('timestep',
                              unit=Unit(1),
                              size=1,
                              dtype=np.uint64,
                              read_only=True,
                              scalar=True)
     self.variables.add_array('t',
                              unit=second,
                              size=1,
                              dtype=np.double,
                              read_only=True,
                              scalar=True)
     self.variables.add_array('dt',
                              unit=second,
                              size=1,
                              values=float(dt),
                              dtype=np.float,
                              read_only=True,
                              constant=True,
                              scalar=True)
     self.variables.add_constant('N', unit=Unit(1), value=1)
     self._enable_group_attributes()
     self.dt = dt
     logger.diagnostic("Created clock {name} with dt={dt}".format(
         name=self.name, dt=self.dt))
Пример #7
0
    def __getitem__(self, item):
        if isinstance(item, basestring):
            variables = Variables(None)
            variables.add_auxiliary_variable('_indices',
                                             unit=Unit(1),
                                             dtype=np.int32)
            variables.add_auxiliary_variable('_cond',
                                             unit=Unit(1),
                                             dtype=np.bool)

            abstract_code = '_cond = ' + item
            check_code_units(abstract_code,
                             self.group,
                             additional_variables=variables,
                             level=1)
            from brian2.devices.device import get_default_codeobject_class
            codeobj = create_runner_codeobj(
                self.group,
                abstract_code,
                'group_get_indices',
                additional_variables=variables,
                level=1,
                codeobj_class=get_default_codeobject_class(
                    'codegen.string_expression_target'))
            return codeobj()
        else:
            return self.indices(item)
Пример #8
0
def test_apply_loop_invariant_optimisation_no_optimisation():
    variables = {
        'v1': Variable('v1', Unit(1), scalar=False),
        'v2': Variable('v2', Unit(1), scalar=False),
        'N': Constant('N', Unit(1), 10),
        's1': Variable('s1', Unit(1), scalar=True, dtype=float),
        's2': Variable('s2', Unit(1), scalar=True, dtype=float),
        'rand': DEFAULT_FUNCTIONS['rand']
    }
    statements = [
        # This hould not be simplified to 0!
        Statement('v1', '=', 'rand() - rand()', '', np.float),
        Statement('v1', '=', '3*rand() - 3*rand()', '', np.float),
        Statement('v1', '=', '3*rand() - ((1+2)*rand())', '', np.float),
        # This should not pull out rand()*N
        Statement('v1', '=', 's1*rand()*N', '', np.float),
        Statement('v1', '=', 's2*rand()*N', '', np.float),
        # This is not important mathematically, but it would change the numbers
        # that are generated
        Statement('v1', '=', '0*rand()*N', '', np.float),
        Statement('v1', '=', '0/rand()*N', '', np.float)
    ]
    scalar, vector = optimise_statements([], statements, variables)
    for vs in vector[:3]:
        assert vs.expr.count(
            'rand()'
        ) == 2, 'Expression should still contain two rand() calls, but got ' + str(
            vs)
    for vs in vector[3:]:
        assert vs.expr.count(
            'rand()'
        ) == 1, 'Expression should still contain a rand() call, but got ' + str(
            vs)
Пример #9
0
    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
Пример #10
0
    def __init__(self, source, name='ratemonitor*',
                 codeobj_class=None):

        #: The group we are recording from
        self.source = source

        self.codeobj_class = codeobj_class
        CodeRunner.__init__(self, group=self, code='', template='ratemonitor',
                            clock=source.clock, when='end', order=0, name=name)

        self.add_dependency(source)

        self.variables = Variables(self)
        # Handle subgroups correctly
        start = getattr(source, 'start', 0)
        stop = getattr(source, 'stop', len(source))
        self.variables.add_constant('_source_start', Unit(1), start)
        self.variables.add_constant('_source_stop', Unit(1), stop)
        self.variables.add_reference('_spikespace', source)
        self.variables.add_dynamic_array('rate', size=0, unit=hertz)
        self.variables.add_dynamic_array('t', size=0, unit=second)
        self.variables.add_reference('_num_source_neurons', source, 'N')
        self.variables.add_array('N', unit=Unit(1), dtype=np.int32, size=1,
                                 scalar=True, read_only=True)
        self.variables.create_clock_variables(self._clock,
                                              prefix='_clock_')
        self._enable_group_attributes()
Пример #11
0
    def __init__(self,
                 N,
                 rates,
                 dt=None,
                 clock=None,
                 when='thresholds',
                 order=0,
                 name='poissongroup*',
                 codeobj_class=None):

        Group.__init__(self,
                       dt=dt,
                       clock=clock,
                       when=when,
                       order=order,
                       name=name)

        self.codeobj_class = codeobj_class

        self._N = N = int(N)

        # TODO: In principle, it would be nice to support Poisson groups with
        # refactoriness, but we can't currently, since the refractoriness
        # information is reset in the state updater which we are not using
        # We could either use a specific template or simply not bother and make
        # users write their own NeuronGroup (with threshold rand() < rates*dt)
        # for more complex use cases.

        self.variables = Variables(self)
        # standard variables
        self.variables.add_constant('N', unit=Unit(1), value=self._N)
        self.variables.add_arange('i', self._N, constant=True, read_only=True)
        self.variables.add_array('_spikespace',
                                 size=N + 1,
                                 unit=Unit(1),
                                 dtype=np.int32)
        self.variables.create_clock_variables(self._clock)

        # The firing rates
        self.variables.add_array('rates', size=N, unit=Hz)

        self.start = 0
        self.stop = N

        self._refractory = False

        # To avoid a warning about the local variable rates, we set the real
        # threshold condition only after creating the object
        self.events = {'spike': 'False'}
        self.thresholder = {'spike': Thresholder(self)}
        self.events = {'spike': 'rand() < rates * dt'}
        self.contained_objects.append(self.thresholder['spike'])

        self._enable_group_attributes()

        # Here we want to use the local namespace, but at the level where the
        # constructor was called
        self.rates.set_item(slice(None), rates, level=2)
Пример #12
0
    def __init__(self,
                 N,
                 indices,
                 times,
                 when=None,
                 name='spikegeneratorgroup*',
                 codeobj_class=None):
        if when is None:
            when = Scheduler(when='thresholds')
        Group.__init__(self, when=when, name=name)

        self.codeobj_class = codeobj_class

        if N < 1 or int(N) != N:
            raise ValueError('N has to be an integer >=1.')

        if len(indices) != len(times):
            raise ValueError(
                ('Length of the indices and times array must '
                 'match, but %d != %d') % (len(indices), len(times)))

        self.start = 0
        self.stop = N

        # sort times and indices first by time, then by indices
        rec = np.rec.fromarrays([times, indices], names=['t', 'i'])
        rec.sort()
        times = np.ascontiguousarray(rec.t)
        indices = np.ascontiguousarray(rec.i)

        self.variables = Variables(self)

        # standard variables
        self.variables.add_clock_variables(self.clock)
        self.variables.add_constant('N', unit=Unit(1), value=N)
        self.variables.add_arange('i', N)
        self.variables.add_arange('spike_number', len(indices))
        self.variables.add_array('neuron_index',
                                 values=indices,
                                 size=len(indices),
                                 unit=Unit(1),
                                 dtype=np.int32,
                                 index='spike_number',
                                 read_only=True)
        self.variables.add_array('spike_time',
                                 values=times,
                                 size=len(times),
                                 unit=second,
                                 index='spike_number',
                                 read_only=True)
        self.variables.add_array('_spikespace',
                                 size=N + 1,
                                 unit=Unit(1),
                                 dtype=np.int32)
        # Activate name attribute access
        self._enable_group_attributes()

        CodeRunner.__init__(self, self, 'spikegenerator', when=when, name=None)
Пример #13
0
 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)
Пример #14
0
    def __init__(self, source, start, stop, name=None):
        # First check if the source is itself a Subgroup
        # If so, then make this a Subgroup of the original Group
        if isinstance(source, Subgroup):
            source = source.source
            start = start + source.start
            stop = stop + source.start
            self.source = source
        else:
            self.source = weakproxy_with_fallback(source)

        if name is None:
            name = source.name + '_subgroup*'
        # We want to update the spikes attribute after it has been updated
        # by the parent, we do this in slot 'thresholds' with an order
        # one higher than the parent order to ensure it takes place after the
        # parent threshold operation
        schedule = Scheduler(clock=source.clock,
                             when='thresholds',
                             order=source.order + 1)
        Group.__init__(self, when=schedule, name=name)
        self._N = stop - start
        self.start = start
        self.stop = stop

        # All the variables have to go via the _sub_idx to refer to the
        # appropriate values in the source group
        self.variables = Variables(self, default_index='_sub_idx')

        # overwrite the meaning of N and i
        self.variables.add_constant('_offset', unit=Unit(1), value=self.start)
        self.variables.add_reference('_source_i', source, 'i')
        self.variables.add_subexpression('i',
                                         unit=Unit(1),
                                         dtype=source.variables['i'].dtype,
                                         expr='_source_i - _offset')
        self.variables.add_constant('N', unit=Unit(1), value=self._N)
        # add references for all variables in the original group
        self.variables.add_references(source, source.variables.keys())

        # Only the variable _sub_idx itself is stored in the subgroup
        # and needs the normal index for this group
        self.variables.add_arange('_sub_idx',
                                  size=self._N,
                                  start=self.start,
                                  index='_idx')

        for key, value in self.source.variables.indices.iteritems():
            if value not in ('_idx', '0'):
                raise ValueError(
                    ('Do not know how to deal with variable %s '
                     'using  index %s in a subgroup') % (key, value))

        self.namespace = self.source.namespace
        self.codeobj_class = self.source.codeobj_class

        self._enable_group_attributes()
Пример #15
0
def test_str_repr():
    '''
    Test that str representations do not raise any errors and that repr
    fullfills eval(repr(x)) == x.
    '''
    from numpy import array  # necessary for evaluating repr

    units_which_should_exist = [
        metre, meter, kilogram, kilogramme, second, amp, kelvin, mole, candle,
        radian, steradian, hertz, newton, pascal, joule, watt, coulomb, volt,
        farad, ohm, siemens, weber, tesla, henry, lumen, lux, becquerel, gray,
        sievert, katal, gram, gramme, molar, liter, litre
    ]

    # scaled versions of all these units should exist (we just check farad as an example)
    some_scaled_units = [
        Yfarad, Zfarad, Efarad, Pfarad, Tfarad, Gfarad, Mfarad, kfarad, hfarad,
        dafarad, dfarad, cfarad, mfarad, ufarad, nfarad, pfarad, ffarad,
        afarad, zfarad, yfarad
    ]

    # some powered units
    powered_units = [cmetre2, Yfarad3]

    # Combined units
    complex_units = [
        (kgram * metre2) / (amp * second3),
        5 * (kgram * metre2) / (amp * second3), metre * second**-1,
        10 * metre * second**-1,
        array([1, 2, 3]) * kmetre / second,
        np.ones(3) * nS / cm**2,
        Unit(1, dim=get_or_create_dimension(length=5,
                                            time=2)), 8000 * umetre**3,
        [0.0001, 10000] * umetre**3, 1 / metre, 1 / (coulomb * metre**2),
        Unit(1) / second, 3. * mM, 5 * mole / liter, 7 * liter / meter3
    ]

    unitless = [second / second, 5 * second / second, Unit(1)]

    for u in itertools.chain(units_which_should_exist, some_scaled_units,
                             powered_units, complex_units, unitless):
        assert (len(str(u)) > 0)
        assert_allclose(eval(repr(u)), u)

    # test the `DIMENSIONLESS` object
    assert str(DIMENSIONLESS) == '1'
    assert repr(DIMENSIONLESS) == 'Dimension()'

    # test DimensionMismatchError (only that it works without raising an error
    for error in [
            DimensionMismatchError('A description'),
            DimensionMismatchError('A description', DIMENSIONLESS),
            DimensionMismatchError('A description', DIMENSIONLESS, second.dim)
    ]:
        assert len(str(error))
        assert len(repr(error))
Пример #16
0
def test_apply_loop_invariant_optimisation_integer():
    variables = {
        'v': Variable('v', Unit(1), scalar=False),
        'N': Constant('N', Unit(1), 10)
    }
    statements = [Statement('v', '=', 'v % (2*3*N)', '', np.float32)]
    scalar, vector = apply_loop_invariant_optimisations(
        statements, variables, np.float64)
    # The optimisation should not pull out 2*N
    assert len(scalar) == 0
Пример #17
0
def test_construction():
    ''' Test the construction of quantity objects '''
    q = 500 * ms
    assert_quantity(q, 0.5, second)
    q = np.float64(500) * ms
    assert_quantity(q, 0.5, second)
    q = np.array(500) * ms
    assert_quantity(q, 0.5, second)
    q = np.array([500, 1000]) * ms
    assert_quantity(q, np.array([0.5, 1]), second)
    q = Quantity(500)
    assert_quantity(q, 500, 1)
    q = Quantity(500, dim=second.dim)
    assert_quantity(q, 500, second)
    q = Quantity([0.5, 1], dim=second.dim)
    assert_quantity(q, np.array([0.5, 1]), second)
    q = Quantity(np.array([0.5, 1]), dim=second.dim)
    assert_quantity(q, np.array([0.5, 1]), second)
    q = Quantity([500 * ms, 1 * second])
    assert_quantity(q, np.array([0.5, 1]), second)
    q = Quantity.with_dimensions(np.array([0.5, 1]), second=1)
    assert_quantity(q, np.array([0.5, 1]), second)
    q = [0.5, 1] * second
    assert_quantity(q, np.array([0.5, 1]), second)

    # dimensionless quantities
    q = Quantity([1, 2, 3])
    assert_quantity(q, np.array([1, 2, 3]), Unit(1))
    q = Quantity(np.array([1, 2, 3]))
    assert_quantity(q, np.array([1, 2, 3]), Unit(1))
    q = Quantity([])
    assert_quantity(q, np.array([]), Unit(1))

    # copying/referencing a quantity
    q1 = Quantity.with_dimensions(np.array([0.5, 1]), second=1)
    q2 = Quantity(q1)  # no copy
    assert_quantity(q2, np.asarray(q1), q1)
    q2[0] = 3 * second
    assert_equal(q1[0], 3 * second)

    q1 = Quantity.with_dimensions(np.array([0.5, 1]), second=1)
    q2 = Quantity(q1, copy=True)  # copy
    assert_quantity(q2, np.asarray(q1), q1)
    q2[0] = 3 * second
    assert_equal(q1[0], 0.5 * second)

    # Illegal constructor calls
    assert_raises(TypeError, lambda: Quantity([500 * ms, 1]))
    assert_raises(TypeError, lambda: Quantity(['some', 'nonsense']))
    assert_raises(DimensionMismatchError,
                  lambda: Quantity([500 * ms, 1 * volt]))
    assert_raises(DimensionMismatchError,
                  lambda: Quantity([500 * ms], dim=volt.dim))
    q = Quantity.with_dimensions(np.array([0.5, 1]), second=1)
    assert_raises(DimensionMismatchError, lambda: Quantity(q, dim=volt.dim))
Пример #18
0
    def __init__(self,
                 source,
                 record=True,
                 when=None,
                 name='spikemonitor*',
                 codeobj_class=None):
        self.record = bool(record)
        #: The source we are recording from
        self.source = source

        # 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'

        self.codeobj_class = codeobj_class
        CodeRunner.__init__(self,
                            group=self,
                            template='spikemonitor',
                            name=name,
                            when=scheduler)

        # Handle subgroups correctly
        start = getattr(source, 'start', 0)
        stop = getattr(source, 'stop', len(source))

        self.variables = Variables(self)
        self.variables.add_clock_variables(scheduler.clock, prefix='_clock_')
        self.variables.add_reference('_spikespace',
                                     source.variables['_spikespace'])
        self.variables.add_dynamic_array('i',
                                         size=0,
                                         unit=Unit(1),
                                         dtype=np.int32,
                                         constant_size=False)
        self.variables.add_dynamic_array('t',
                                         size=0,
                                         unit=second,
                                         constant_size=False)
        self.variables.add_array('_count',
                                 size=len(source),
                                 unit=Unit(1),
                                 dtype=np.int32)
        self.variables.add_constant('_source_start', Unit(1), start)
        self.variables.add_constant('_source_stop', Unit(1), stop)
        self.variables.add_attribute_variable('N',
                                              unit=Unit(1),
                                              obj=self,
                                              attribute='_N',
                                              dtype=np.int32)
        self._N = 0

        self._enable_group_attributes()
Пример #19
0
    def __init__(self,
                 source,
                 record=True,
                 when='end',
                 order=0,
                 name='spikemonitor*',
                 codeobj_class=None):
        self.record = bool(record)
        #: The source we are recording from
        self.source = source

        self.codeobj_class = codeobj_class
        CodeRunner.__init__(self,
                            group=self,
                            code='',
                            template='spikemonitor',
                            name=name,
                            clock=source.clock,
                            when=when,
                            order=order)

        self.add_dependency(source)

        # Handle subgroups correctly
        start = getattr(source, 'start', 0)
        stop = getattr(source, 'stop', len(source))

        self.variables = Variables(self)
        self.variables.add_reference('_spikespace', source)
        self.variables.add_dynamic_array('i',
                                         size=0,
                                         unit=Unit(1),
                                         dtype=np.int32,
                                         constant_size=False)
        self.variables.add_dynamic_array('t',
                                         size=0,
                                         unit=second,
                                         constant_size=False)
        self.variables.add_arange('_source_i', size=len(source))
        self.variables.add_array('_count',
                                 size=len(source),
                                 unit=Unit(1),
                                 dtype=np.int32,
                                 read_only=True,
                                 index='_source_i')
        self.variables.add_constant('_source_start', Unit(1), start)
        self.variables.add_constant('_source_stop', Unit(1), stop)
        self.variables.add_attribute_variable('N',
                                              unit=Unit(1),
                                              obj=self,
                                              attribute='_N',
                                              dtype=np.int32)
        self.variables.create_clock_variables(self._clock, prefix='_clock_')
        self._enable_group_attributes()
Пример #20
0
    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
Пример #21
0
def test_get_identifiers_recursively():
    '''
    Test finding identifiers including subexpressions.
    '''
    variables = {}
    variables['sub1'] = Subexpression(Unit(1), np.float32, 'sub2 * z',
                                      variables, {})
    variables['sub2'] = Subexpression(Unit(1), np.float32, '5 + y', variables,
                                      {})
    variables['x'] = Variable(unit=None)
    identifiers = get_identifiers_recursively('_x = sub1 + x', variables)
    assert identifiers == set(['x', '_x', 'y', 'z', 'sub1', 'sub2'])
Пример #22
0
def test_warning_internal_variables():
    N = 5
    group1 = SimpleGroup(namespace=None,
                         variables={'N': Constant('N', Unit(1), 5)})
    group2 = SimpleGroup(namespace=None,
                         variables={'N': Constant('N', Unit(1), 7)})
    with catch_logs() as l:
        group1.resolve('N')  # should not raise a warning
        assert len(l) == 0, 'got warnings: %s' % str(l)
    with catch_logs() as l:
        group2.resolve('N')  # should raise a warning
        assert len(l) == 1, 'got warnings: %s' % str(l)
        assert l[0][1].endswith('.resolution_conflict')
Пример #23
0
def test_apply_loop_invariant_optimisation_integer():
    variables = {
        'v': Variable('v', Unit(1), scalar=False),
        'N': Constant('N', Unit(1), 10),
        'b': Variable('b', Unit(1), scalar=True, dtype=int),
        'c': Variable('c', Unit(1), scalar=True, dtype=int),
        'd': Variable('d', Unit(1), scalar=True, dtype=int),
        'y': Variable('y', Unit(1), scalar=True, dtype=float),
        'z': Variable('z', Unit(1), scalar=True, dtype=float),
        'w': Variable('w', Unit(1), scalar=True, dtype=float),
    }
    statements = [
        Statement('v', '=', 'v % (2*3*N)', '', np.float32),
        # integer version doesn't get rewritten but float version does
        Statement('a', ':=', 'b/(c/d)', '', int),
        Statement('x', ':=', 'y/(z/w)', '', float),
    ]
    scalar, vector = optimise_statements([], statements, variables)
    assert len(scalar) == 3
    assert np.issubdtype(scalar[0].dtype, (np.integer, int))
    assert scalar[0].var == '_lio_1'
    expr = scalar[0].expr.replace(' ', '')
    assert expr == '6*N' or expr == 'N*6'
    assert np.issubdtype(scalar[1].dtype, (np.integer, int))
    assert scalar[1].var == '_lio_2'
    expr = scalar[1].expr.replace(' ', '')
    assert expr == 'b/(c/d)'
    assert np.issubdtype(scalar[2].dtype, (np.float, float))
    assert scalar[2].var == '_lio_3'
    expr = scalar[2].expr.replace(' ', '')
    assert expr == '(y*w)/z' or expr == '(w*y)/z'
Пример #24
0
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', unit=Unit(1), owner=None, size=10,
                           device=device),
        'y': ArrayVariable('y', unit=Unit(1), owner=None, size=10,
                           device=device),
        'z': ArrayVariable('y', unit=Unit(1), owner=None, size=10,
                           device=device),
        'b': ArrayVariable('b', unit=Unit(1), owner=None, size=10,
                           dtype=np.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', 'x += 1'),
        ('x = 2 * x', 'x *= 2'),
        ('x = x - 3', 'x += -3'),
        ('x = x/2', 'x *= 0.5'),
        ('x = y + (x + 1)', 'x += y + 1'),
        ('x = x + x', 'x *= 2'),
        ('x = x + y + z', 'x += y + z'),
        ('x = x + y + z', 'x += y + z'),
        # examples that should not be rewritten
        ('x = 1/x', 'x = 1/x'),
        ('x = 1', 'x = 1'),
        ('x = 2*(x + 1)', 'x = 2*(x + 1)'),
        ('x = clip(x + y, 0, inf)', 'x = clip(x + y, 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)))
Пример #25
0
def test_apply_loop_invariant_optimisation():
    variables = {'v': Variable('v', Unit(1), scalar=False),
                 'w': Variable('w', Unit(1), scalar=False),
                 'dt': Constant('dt', second, 0.1*ms),
                 'tau': Constant('tau', second, 10*ms),
                 'exp': DEFAULT_FUNCTIONS['exp']}
    statements = [Statement('v', '=', 'dt*w*exp(-dt/tau)/tau + v*exp(-dt/tau)', '', np.float32),
                  Statement('w', '=', 'w*exp(-dt/tau)', '', np.float32)]
    scalar, vector = optimise_statements([], statements, variables)
    # The optimisation should pull out at least exp(-dt / tau)
    assert len(scalar) >= 1
    assert np.issubdtype(scalar[0].dtype, (np.floating, float))
    assert scalar[0].var == '_lio_1'
    assert len(vector) == 2
    assert all('_lio_' in stmt.expr for stmt in vector)
Пример #26
0
    def update_abstract_code(self):

        # Update the not_refractory variable for the refractory period mechanism
        ref = self.group._refractory
        if ref is None:
            # No refractoriness
            self.abstract_code = ''
        elif isinstance(ref, Quantity):
            self.abstract_code = 'not_refractory = 1*((t - lastspike) > %f)\n' % ref
        else:
            namespace = self.group.namespace
            unit = parse_expression_unit(str(ref), namespace,
                                         self.group.variables)
            if have_same_dimensions(unit, second):
                self.abstract_code = 'not_refractory = 1*((t - lastspike) > %s)\n' % ref
            elif have_same_dimensions(unit, Unit(1)):
                if not is_boolean_expression(str(ref), namespace,
                                             self.group.variables):
                    raise TypeError(('Refractory expression is dimensionless '
                                     'but not a boolean value. It needs to '
                                     'either evaluate to a timespan or to a '
                                     'boolean value.'))
                # boolean condition
                # we have to be a bit careful here, we can't just use the given
                # condition as it is, because we only want to *leave*
                # refractoriness, based on the condition
                self.abstract_code = 'not_refractory = 1*(not_refractory or not (%s))\n' % ref
            else:
                raise TypeError(('Refractory expression has to evaluate to a '
                                 'timespan or a boolean value, expression'
                                 '"%s" has units %s instead') % (ref, unit))

        self.abstract_code += self.method(self.group.equations,
                                          self.group.variables)
Пример #27
0
 def render_node(self, node):
     '''
     Assumes that the node has already been fully processed by BrianASTRenderer
     '''
     # can we pull this out?
     if node.scalar and node.complexity > 0:
         expr = self.node_renderer.render_node(
             self.arithmetic_simplifier.render_node(node))
         if expr in self.loop_invariants:
             name = self.loop_invariants[expr]
         else:
             self.n += 1
             name = '_lio_' + str(self.n)
             self.loop_invariants[expr] = name
             self.loop_invariant_dtypes[name] = node.dtype
             numpy_dtype = {
                 'boolean': bool,
                 'integer': int,
                 'float': float
             }[node.dtype]
             self.variables[name] = AuxiliaryVariable(name,
                                                      Unit(1),
                                                      dtype=numpy_dtype,
                                                      scalar=True)
         # None is the expression context, we don't use it so we just set to None
         newnode = ast.Name(name, None)
         newnode.scalar = True
         newnode.dtype = node.dtype
         newnode.complexity = 0
         newnode.stateless = node.stateless
         return newnode
     # otherwise, render node as usual
     return super(Simplifier, self).render_node(node)
Пример #28
0
 def __init__(self, group, when='thresholds', event='spike'):
     self.event = event
     if group._refractory is False or event != 'spike':
         template_kwds = {'_uses_refractory': False}
         needed_variables = []
     else:
         template_kwds = {'_uses_refractory': True}
         needed_variables = ['t', 'not_refractory', 'lastspike']
     # Since this now works for general events not only spikes, we have to
     # pass the information about which variable to use to the template,
     # it can not longer simply refer to "_spikespace"
     eventspace_name = '_{}space'.format(event)
     template_kwds['eventspace_variable'] = group.variables[eventspace_name]
     needed_variables.append(eventspace_name)
     self.variables = Variables(self)
     self.variables.add_auxiliary_variable('_cond',
                                           unit=Unit(1),
                                           dtype=np.bool)
     CodeRunner.__init__(
         self,
         group,
         'threshold',
         code='',  # will be set in update_abstract_code
         clock=group.clock,
         when=when,
         order=group.order,
         name=group.name + '_thresholder*',
         needed_variables=needed_variables,
         template_kwds=template_kwds)
Пример #29
0
def test_get_identifiers_recursively():
    '''
    Test finding identifiers including subexpressions.
    '''
    variables = {'sub1': Subexpression(name='sub1', unit=Unit(1),
                                       dtype=np.float32, expr='sub2 * z',
                                       owner=FakeGroup(variables={}),
                                       device=None),
                 'sub2': Subexpression(name='sub2', unit=Unit(1),
                                       dtype=np.float32, expr='5 + y',
                                       owner=FakeGroup(variables={}),
                                       device=None),
                 'x': Variable(unit=None, name='x')}
    identifiers = get_identifiers_recursively(['_x = sub1 + x'],
                                              variables)
    assert identifiers == {'x', '_x', 'y', 'z', 'sub1', 'sub2'}
Пример #30
0
 def _get_refractory_code(self, run_namespace, level=0):
     ref = self.group._refractory
     if ref is False:
         # No refractoriness
         abstract_code = ''
     elif isinstance(ref, Quantity):
         abstract_code = 'not_refractory = (t - lastspike) > %f\n' % ref
     else:
         identifiers = get_identifiers(ref)
         variables = self.group.resolve_all(identifiers,
                                            identifiers,
                                            run_namespace=run_namespace,
                                            level=level + 1)
         unit = parse_expression_unit(str(ref), variables)
         if have_same_dimensions(unit, second):
             abstract_code = 'not_refractory = (t - lastspike) > %s\n' % ref
         elif have_same_dimensions(unit, Unit(1)):
             if not is_boolean_expression(str(ref), variables):
                 raise TypeError(('Refractory expression is dimensionless '
                                  'but not a boolean value. It needs to '
                                  'either evaluate to a timespan or to a '
                                  'boolean value.'))
             # boolean condition
             # we have to be a bit careful here, we can't just use the given
             # condition as it is, because we only want to *leave*
             # refractoriness, based on the condition
             abstract_code = 'not_refractory = not_refractory or not (%s)\n' % ref
         else:
             raise TypeError(('Refractory expression has to evaluate to a '
                              'timespan or a boolean value, expression'
                              '"%s" has units %s instead') % (ref, unit))
     return abstract_code