示例#1
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)
示例#2
0
    def spatialneuron_segment(neuron, item):
        '''
        Selects a segment from `SpatialNeuron` neuron, where item is a slice of
        either compartment indexes or distances.
        Note a: segment is not a `SpatialNeuron`, only a `Group`.
        '''
        if isinstance(item, slice) and isinstance(item.start, Quantity):
            if item.step is not None:
                raise ValueError('Cannot specify a step size for slicing based'
                                 'on length.')
            start, stop = item.start, item.stop
            if (not have_same_dimensions(start, meter) or
                    not have_same_dimensions(stop, meter)):
                raise DimensionMismatchError('Start and stop should have units '
                                             'of meter', start, stop)
            # Convert to integers (compartment numbers)
            indices = neuron.morphology.indices[item]
            start, stop = indices[0], indices[-1] + 1
        elif not isinstance(item, slice) and hasattr(item, 'indices'):
            start, stop = to_start_stop(item.indices[:], neuron._N)
        else:
            start, stop = to_start_stop(item, neuron._N)

        if start >= stop:
            raise IndexError('Illegal start/end values for subgroup, %d>=%d' %
                             (start, stop))

        return Subgroup(neuron, start, stop)
示例#3
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
示例#4
0
    def spatialneuron_segment(neuron, item):
        """
        Selects a segment from `SpatialNeuron` neuron, where item is a slice of
        either compartment indexes or distances.
        Note a: segment is not a `SpatialNeuron`, only a `Group`.
        """
        if isinstance(item, slice) and isinstance(item.start, Quantity):
            if item.step is not None:
                raise ValueError("Cannot specify a step size for slicing based"
                                 "on length.")
            start, stop = item.start, item.stop
            if (not have_same_dimensions(start, meter) or
                    not have_same_dimensions(stop, meter)):
                raise DimensionMismatchError("Start and stop should have units "
                                             "of meter", start, stop)
            # Convert to integers (compartment numbers)
            indices = neuron.morphology.indices[item]
            start, stop = indices[0], indices[-1] + 1
        elif not isinstance(item, slice) and hasattr(item, 'indices'):
            start, stop = to_start_stop(item.indices[:], neuron._N)
        else:
            start, stop = to_start_stop(item, neuron._N)
            if isinstance(neuron, SpatialSubgroup):
                start += neuron.start
                stop += neuron.start

        if start >= stop:
            raise IndexError(f'Illegal start/end values for subgroup, {int(start)}>={int(stop)}')
        if isinstance(neuron, SpatialSubgroup):
            # Note that the start/stop values calculated above are always
            # absolute values, even for subgroups
            neuron = neuron.source
        return Subgroup(neuron, start, stop)
示例#5
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
示例#6
0
    def spatialneuron_segment(neuron, item):
        '''
        Selects a segment from `SpatialNeuron` neuron, where item is a slice of
        either compartment indexes or distances.
        Note a: segment is not a `SpatialNeuron`, only a `Group`.
        '''
        if not isinstance(item, slice):
            raise TypeError(
                'Subgroups can only be constructed using slicing syntax')
        start, stop, step = item.start, item.stop, item.step
        if step is None:
            step = 1
        if step != 1:
            raise IndexError('Subgroups have to be contiguous')

        if isinstance(start, Quantity):
            if not have_same_dimensions(
                    start, meter) or not have_same_dimensions(stop, meter):
                raise DimensionMismatchError(
                    'Start and stop should have units of meter', start, stop)
            # Convert to integers (compartment numbers)
            indices = neuron.morphology.indices[item]
            start, stop = indices[0], indices[-1] + 1

        if start >= stop:
            raise IndexError('Illegal start/end values for subgroup, %d>=%d' %
                             (start, stop))

        return Subgroup(neuron, start, stop)
示例#7
0
def assert_quantity(q, values, unit):
    assert isinstance(q, Quantity) or (isinstance(q, np.ndarray)
                                       and have_same_dimensions(unit, 1))
    assert_equal(np.asarray(q), values)
    assert have_same_dimensions(
        q, unit), ('Dimension mismatch: (%s) (%s)' %
                   (get_dimensions(q), get_dimensions(unit)))
示例#8
0
    def spatialneuron_segment(neuron, item):
        '''
        Selects a segment from `SpatialNeuron` neuron, where item is a slice of
        either compartment indexes or distances.
        Note a: segment is not a `SpatialNeuron`, only a `Group`.
        '''
        if not isinstance(item, slice):
            raise TypeError(
                'Subgroups can only be constructed using slicing syntax')
        start, stop, step = item.start, item.stop, item.step
        if step is None:
            step = 1
        if step != 1:
            raise IndexError('Subgroups have to be contiguous')

        if isinstance(start, Quantity):
            if not have_same_dimensions(start, meter) or not have_same_dimensions(stop, meter):
                raise DimensionMismatchError('Start and stop should have units of meter', start, stop)
            # Convert to integers (compartment numbers)
            indices = neuron.morphology.indices[item]
            start, stop = indices[0], indices[-1] + 1

        if start >= stop:
            raise IndexError('Illegal start/end values for subgroup, %d>=%d' %
                             (start, stop))

        return Subgroup(neuron, start, stop)
示例#9
0
    def spatialneuron_segment(neuron, item):
        '''
        Selects a segment from `SpatialNeuron` neuron, where item is a slice of
        either compartment indexes or distances.
        Note a: segment is not a `SpatialNeuron`, only a `Group`.
        '''
        if isinstance(item, slice) and isinstance(item.start, Quantity):
            if item.step is not None:
                raise ValueError('Cannot specify a step size for slicing based'
                                 'on length.')
            start, stop = item.start, item.stop
            if (not have_same_dimensions(start, meter) or
                    not have_same_dimensions(stop, meter)):
                raise DimensionMismatchError('Start and stop should have units '
                                             'of meter', start, stop)
            # Convert to integers (compartment numbers)
            indices = neuron.morphology.indices[item]
            start, stop = indices[0], indices[-1] + 1
        elif not isinstance(item, slice) and hasattr(item, 'indices'):
            start, stop = to_start_stop(item.indices[:], neuron._N)
        else:
            start, stop = to_start_stop(item, neuron._N)

        if start >= stop:
            raise IndexError('Illegal start/end values for subgroup, %d>=%d' %
                             (start, stop))

        return Subgroup(neuron, start, stop)
示例#10
0
def get_refractory_code(group):
    ref = group._refractory
    if ref is False:
        # No refractoriness
        abstract_code = ''
    elif isinstance(ref, Quantity):
        abstract_code = 'not_refractory = 1*((t - lastspike) > %f)\n' % ref
    else:
        namespace = group.namespace
        unit = parse_expression_unit(str(ref), namespace, group.variables)
        if have_same_dimensions(unit, second):
            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,
                                         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
            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))
    return abstract_code
示例#11
0
def assert_quantity(q, values, unit):
    assert isinstance(q, Quantity) or (have_same_dimensions(unit, 1) and
                                       (values.shape == () or
                                        isinstance(q, np.ndarray))), q
    assert_allclose(np.asarray(q), values)
    assert have_same_dimensions(q, unit), ('Dimension mismatch: (%s) (%s)' %
                                           (get_dimensions(q),
                                            get_dimensions(unit)))
示例#12
0
def assert_quantity(q, values, unit):
    assert isinstance(
        q, Quantity) or (have_same_dimensions(unit, 1) and
                         (values.shape == () or isinstance(q, np.ndarray))), q
    assert_allclose(np.asarray(q), values)
    assert have_same_dimensions(
        q, unit), ('Dimension mismatch: (%s) (%s)' %
                   (get_dimensions(q), get_dimensions(unit)))
示例#13
0
 def __call__(self, value):
     if not isinstance(value, self.value.__class__):
         return False
     if isinstance(self.value, Quantity):
         if not have_same_dimensions(self.value, value):
             return False
     return True
示例#14
0
    def __getattr__(self, item):
        # We do this because __setattr__ and __getattr__ are not active until
        # _group_attribute_access_active attribute is set, and if it is set,
        # then __getattr__ will not be called. Therefore, if getattr is called
        # with this name, it is because it hasn't been set yet and so this
        # method should raise an AttributeError to agree that it hasn't been
        # called yet.
        if item == '_group_attribute_access_active':
            raise AttributeError
        if not hasattr(self, '_group_attribute_access_active'):
            raise AttributeError

        # TODO: Decide about the interface
        if item == 't':
            return Quantity(self._t.data.copy(), dim=second.dim)
        elif item == 't_':
            return self._t.data.copy()
        elif item in self.record_variables:
            unit = self.variables[item].unit
            if have_same_dimensions(unit, 1):
                return self._values[item].data.T.copy()
            else:
                return Quantity(self._values[item].data.T.copy(),
                                dim=unit.dim)
        elif item.endswith('_') and item[:-1] in self.record_variables:
            return self._values[item[:-1]].data.T.copy()
        else:
            raise AttributeError('Unknown attribute %s' % item)
示例#15
0
    def __init__(self,
                 type,
                 varname,
                 unit,
                 var_type=FLOAT,
                 expr=None,
                 flags=None):
        self.type = type
        self.varname = varname
        self.unit = unit
        self.var_type = var_type
        if not have_same_dimensions(unit, 1):
            if var_type == BOOLEAN:
                raise TypeError(
                    'Boolean variables are necessarily dimensionless.')
            elif var_type == INTEGER:
                raise TypeError(
                    'Integer variables are necessarily dimensionless.')

        if type == DIFFERENTIAL_EQUATION:
            if var_type != FLOAT:
                raise TypeError(
                    'Differential equations can only define floating point variables'
                )
        self.expr = expr
        if flags is None:
            self.flags = []
        else:
            self.flags = flags

        # will be set later in the sort_subexpressions method of Equations
        self.update_order = -1
示例#16
0
    def __init__(self,
                 name,
                 unit,
                 dtype=None,
                 scalar=False,
                 constant=False,
                 read_only=False):

        #: The variable's unit.
        self.unit = unit

        #: The variable's name.
        self.name = name

        #: The dtype used for storing the variable.
        self.dtype = dtype
        if dtype is None:
            self.dtype = brian_prefs.core.default_float_dtype

        if self.is_boolean:
            if not have_same_dimensions(unit, 1):
                raise ValueError('Boolean variables can only be dimensionless')

        #: Whether the variable is a scalar
        self.scalar = scalar

        #: Whether the variable is constant during a run
        self.constant = constant

        #: Whether the variable is read-only
        self.read_only = read_only
示例#17
0
文件: variables.py 项目: yger/brian2
    def __init__(self, name, unit, dtype=None, scalar=False,
                 constant=False, is_bool=False, read_only=False):
        
        #: The variable's unit.
        self.unit = unit

        #: The variable's name.
        self.name = name

        #: The dtype used for storing the variable.
        self.dtype = default_dtype(dtype, is_bool)

        #: Whether this variable is a boolean
        self.is_bool = is_bool

        if is_bool:
            if not have_same_dimensions(unit, 1):
                raise ValueError('Boolean variables can only be dimensionless')

        #: Whether the variable is a scalar
        self.scalar = scalar

        #: Whether the variable is constant during a run
        self.constant = constant

        #: Whether the variable is read-only
        self.read_only = read_only
示例#18
0
def assert_allclose(actual, desired, rtol=4.5e8, atol=0, **kwds):
    '''
    Thin wrapper around numpy's `~numpy.testing.utils.assert_allclose` function. The tolerance depends on the floating
    point precision as defined by the `core.default_float_dtype` preference.

    Parameters
    ----------
    actual : `numpy.ndarray`
        The results to check.
    desired : `numpy.ndarray`
        The expected results.
    rtol : float, optional
        The relative tolerance which will be multiplied with the machine epsilon of the type set as
        `core.default_float_type`.
    atol : float, optional
        The absolute tolerance which will be multiplied with the machine epsilon of the type set as
        `core.default_float_type`.
    '''
    assert have_same_dimensions(actual, desired)
    eps = np.finfo(prefs['core.default_float_dtype']).eps
    rtol = eps * rtol
    atol = eps * atol
    numpy_allclose(np.asarray(actual),
                   np.asarray(desired),
                   rtol=rtol,
                   atol=atol,
                   **kwds)
示例#19
0
 def __call__(self, value):
     if not isinstance(value, self.value.__class__):
         return False
     if isinstance(self.value, Quantity):
         if not have_same_dimensions(self.value, value):
             return False
     return True
示例#20
0
def test_state_variable_access():
    for codeobj_class in codeobj_classes:
        G = NeuronGroup(10, 'v:volt', codeobj_class=codeobj_class)
        G.v = np.arange(10) * volt

        assert_equal(np.asarray(G.v[:]), np.arange(10))
        assert have_same_dimensions(G.v[:], volt)
        assert_equal(np.asarray(G.v[:]), G.v_[:])
        # Accessing single elements, slices and arrays
        assert G.v[5] == 5 * volt
        assert G.v_[5] == 5
        assert_equal(G.v[:5], np.arange(5) * volt)
        assert_equal(G.v_[:5], np.arange(5))
        assert_equal(G.v[[0, 5]], [0, 5] * volt)
        assert_equal(G.v_[[0, 5]], np.array([0, 5]))

        # Illegal indexing
        assert_raises(IndexError, lambda: G.v[0, 0])
        assert_raises(IndexError, lambda: G.v_[0, 0])
        assert_raises(TypeError, lambda: G.v[object()])
        assert_raises(TypeError, lambda: G.v_[object()])

        # A string representation should not raise any error
        assert len(str(G.v))
        assert len(repr(G.v))
        assert len(str(G.v_))
        assert len(repr(G.v_))
示例#21
0
def test_state_variable_access():
    G = NeuronGroup(10, 'v:volt')
    G.v = np.arange(10) * volt

    assert_equal(np.asarray(G.v[:]), np.arange(10))
    assert have_same_dimensions(G.v[:], volt)
    assert_equal(np.asarray(G.v[:]), G.v_[:])
    # Accessing single elements, slices and arrays
    assert G.v[5] == 5 * volt
    assert G.v_[5] == 5
    assert_equal(G.v[:5], np.arange(5) * volt)
    assert_equal(G.v_[:5], np.arange(5))
    assert_equal(G.v[[0, 5]], [0, 5] * volt)
    assert_equal(G.v_[[0, 5]], np.array([0, 5]))

    # Illegal indexing
    assert_raises(IndexError, lambda: G.v[0, 0])
    assert_raises(IndexError, lambda: G.v_[0, 0])
    assert_raises(TypeError, lambda: G.v[object()])
    assert_raises(TypeError, lambda: G.v_[object()])

    # Indexing with strings
    assert G.v['i==2'] == G.v[2]
    assert G.v_['i==2'] == G.v_[2]
    assert_equal(G.v['v >= 3*volt'], G.v[3:])
    assert_equal(G.v_['v >= 3*volt'], G.v_[3:])
    # Should also check for units
    assert_raises(DimensionMismatchError, lambda: G.v['v >= 3'])
    assert_raises(DimensionMismatchError, lambda: G.v['v >= 3*second'])
示例#22
0
    def _get_refractory_code(self, run_namespace):
        ref = self.group._refractory
        if ref is False:
            # No refractoriness
            abstract_code = ""
        elif isinstance(ref, Quantity):
            fail_for_dimension_mismatch(
                ref,
                second,
                ("Refractory period has to " "be specified in units " "of seconds but got " "{value}"),
                value=ref,
            )

            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)
            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
示例#23
0
def test_properties():
    '''
    Test accessing the various properties of equation objects
    '''
    tau = 10 * ms
    eqs = Equations('''dv/dt = -(v + I)/ tau : volt
                       I = sin(2 * 22/7. * f * t)* volt : volt
                       f = freq * Hz: Hz
                       freq : 1''')
    assert (len(eqs.diff_eq_expressions) == 1
            and eqs.diff_eq_expressions[0][0] == 'v'
            and isinstance(eqs.diff_eq_expressions[0][1], Expression))
    assert eqs.diff_eq_names == {'v'}
    assert (len(eqs.eq_expressions) == 3
            and set([name
                     for name, _ in eqs.eq_expressions]) == {'v', 'I', 'f'}
            and all((isinstance(expr, Expression)
                     for _, expr in eqs.eq_expressions)))
    assert len(eqs.eq_names) == 3 and eqs.eq_names == {'v', 'I', 'f'}
    assert set(eqs.keys()) == {'v', 'I', 'f', 'freq'}
    # test that the equations object is iterable itself
    assert all((isinstance(eq, SingleEquation) for eq in eqs.itervalues()))
    assert all((isinstance(eq, basestring) for eq in eqs))
    assert (len(eqs.ordered) == 4 and all(
        (isinstance(eq, SingleEquation) for eq in eqs.ordered))
            and [eq.varname for eq in eqs.ordered] == ['f', 'I', 'v', 'freq'])
    assert eqs.names == {'v', 'I', 'f', 'freq'}
    assert eqs.parameter_names == {'freq'}
    assert eqs.subexpr_names == {'I', 'f'}
    units = eqs.units
    assert set(units.keys()) == {'v', 'I', 'f', 'freq'}
    assert units['v'] == volt
    assert units['I'] == volt
    assert units['f'] == Hz
    assert have_same_dimensions(units['freq'], 1)
    assert eqs.names == set(eqs.units.keys())
    assert eqs.identifiers == {'tau', 'volt', 'Hz', 'sin', 't'}

    # stochastic equations
    assert len(eqs.stochastic_variables) == 0
    assert eqs.stochastic_type is None

    eqs = Equations('''dv/dt = -v / tau + 0.1*second**-.5*xi : 1''')
    assert eqs.stochastic_variables == {'xi'}
    assert eqs.stochastic_type == 'additive'

    eqs = Equations(
        '''dv/dt = -v / tau + 0.1*second**-.5*xi_1 +  0.1*second**-.5*xi_2: 1'''
    )
    assert eqs.stochastic_variables == {'xi_1', 'xi_2'}
    assert eqs.stochastic_type == 'additive'

    eqs = Equations('''dv/dt = -v / tau + 0.1*second**-1.5*xi*t : 1''')
    assert eqs.stochastic_type == 'multiplicative'

    eqs = Equations('''dv/dt = -v / tau + 0.1*second**-1.5*xi*v : 1''')
    assert eqs.stochastic_type == 'multiplicative'
示例#24
0
def test_list():
    '''
    Test converting to and from a list.
    '''
    values = [3 * mV, np.array([1, 2]) * mV, np.arange(12).reshape(4, 3) * mV]
    for value in values:
        l = value.tolist()
        from_list = Quantity(l)
        assert have_same_dimensions(from_list, value)
        assert_equal(from_list, value)
示例#25
0
    def __init__(self,
                 target,
                 target_var,
                 N,
                 rate,
                 weight,
                 when='synapses',
                 order=0):
        if target_var not in target.variables:
            raise KeyError('%s is not a variable of %s' %
                           (target_var, target.name))

        self._weight = weight
        self._target_var = target_var

        if isinstance(weight, str):
            weight = '(%s)' % weight
        else:
            weight_dims = get_dimensions(weight)
            target_dims = target.variables[target_var].dim
            # This will be checked automatically in the abstract code as well
            # but doing an explicit check here allows for a clearer error
            # message
            if not have_same_dimensions(weight_dims, target_dims):
                raise DimensionMismatchError(
                    ('The provided weight does not '
                     'have the same unit as the '
                     'target variable "%s"') % target_var, weight_dims,
                    target_dims)
            weight = repr(weight)
        self._N = N
        self._rate = rate
        binomial_sampling = BinomialFunction(N,
                                             rate * target.clock.dt,
                                             name='poissoninput_binomial*')

        code = '{targetvar} += {binomial}()*{weight}'.format(
            targetvar=target_var,
            binomial=binomial_sampling.name,
            weight=weight)
        self._stored_dt = target.dt_[:]  # make a copy
        # FIXME: we need an explicit reference here for on-the-fly subgroups
        # For example: PoissonInput(group[:N], ...)
        self._group = target
        CodeRunner.__init__(self,
                            group=target,
                            template='stateupdate',
                            code=code,
                            user_code='',
                            when=when,
                            order=order,
                            name='poissoninput*',
                            clock=target.clock)
        self.variables = Variables(self)
        self.variables._add_variable(binomial_sampling.name, binomial_sampling)
示例#26
0
def test_pickling():
    '''
    Test pickling of units.
    '''
    for q in [500 * mV, 500 * mV/mV, np.arange(10) * mV,
              np.arange(12).reshape(4, 3) * mV/ms]:
        pickled = pickle.dumps(q)
        unpickled = pickle.loads(pickled)
        assert isinstance(unpickled, type(q))
        assert have_same_dimensions(unpickled, q)
        assert_equal(unpickled, q)
示例#27
0
def test_list():
    '''
    Test converting to and from a list.
    '''
    values = [3 * mV, np.array([1, 2]) * mV,
              np.arange(12).reshape(4, 3) * mV]
    for value in values:
        l = value.tolist()
        from_list = Quantity(l)
        assert have_same_dimensions(from_list, value)
        assert_equal(from_list, value)
示例#28
0
def test_pickling():
    '''
    Test pickling of units.
    '''
    for q in [500 * mV, 500 * mV/mV, np.arange(10) * mV,
              np.arange(12).reshape(4, 3) * mV/ms]:
        pickled = pickle.dumps(q)
        unpickled = pickle.loads(pickled)
        assert isinstance(unpickled, type(q))
        assert have_same_dimensions(unpickled, q)
        assert_equal(unpickled, q)
示例#29
0
 def __getitem__(self, i):
     variable = self.variable
     if variable.scalar:
         if not (i == slice(None) or i == 0 or (hasattr(i, '__len__') and len(i) == 0)):
             raise IndexError('Variable is a scalar variable.')
         indices = 0
     else:
         indices = self.group.indices[self.group.variable_indices[self.name]][i]
     if self.unit is None or have_same_dimensions(self.unit, Unit(1)):
         return variable.get_value()[indices]
     else:
         return Quantity(variable.get_value()[indices], self.unit.dimensions)
示例#30
0
    def variableview_set_with_index_array(self, variableview, item, value,
                                          check_units):
        if isinstance(item, slice) and item == slice(None):
            item = 'True'
        value = Quantity(value)

        if (isinstance(item, int) or
            (isinstance(item, np.ndarray)
             and item.shape == ())) and value.size == 1:
            array_name = self.get_array_name(variableview.variable,
                                             access_data=False)
            # For a single assignment, generate a code line instead of storing the array
            self.main_queue.append(
                ('set_by_single_value', (array_name, item, float(value))))

        elif (value.size == 1 and item == 'True'
              and variableview.index_var_name == '_idx'):
            # set the whole array to a scalar value
            if have_same_dimensions(value, 1):
                # Avoid a representation as "Quantity(...)" or "array(...)"
                value = float(value)
            variableview.set_with_expression_conditional(
                cond=item, code=repr(value), check_units=check_units)
        # Simple case where we don't have to do any indexing
        elif (item == 'True' and variableview.index_var == '_idx'):
            self.fill_with_array(variableview.variable, value)
        else:
            # We have to calculate indices. This will not work for synaptic
            # variables
            try:
                indices = np.asarray(
                    variableview.indexing(item,
                                          index_var=variableview.index_var))
            except NotImplementedError:
                raise NotImplementedError(
                    ('Cannot set variable "%s" this way in '
                     'standalone, try using string '
                     'expressions.') % variableview.name)
            # Using the std::vector instead of a pointer to the underlying
            # data for dynamic arrays is fast enough here and it saves us some
            # additional work to set up the pointer
            arrayname = self.get_array_name(variableview.variable,
                                            access_data=False)
            staticarrayname_index = self.static_array('_index_' + arrayname,
                                                      indices)
            if (indices.shape != () and
                (value.shape == () or (value.size == 1 and indices.size > 1))):
                value = np.repeat(value, indices.size)
            staticarrayname_value = self.static_array('_value_' + arrayname,
                                                      value)
            self.main_queue.append(
                ('set_array_by_array', (arrayname, staticarrayname_index,
                                        staticarrayname_value)))
示例#31
0
def test_properties():
    '''
    Test accessing the various properties of equation objects
    '''
    tau = 10 * ms
    eqs = Equations('''dv/dt = -(v + I)/ tau : volt
                       I = sin(2 * 22/7. * f * t)* volt : volt
                       f = freq * Hz: Hz
                       freq : 1''')
    assert (len(eqs.diff_eq_expressions) == 1 and
            eqs.diff_eq_expressions[0][0] == 'v' and
            isinstance(eqs.diff_eq_expressions[0][1], Expression))
    assert eqs.diff_eq_names == {'v'}
    assert (len(eqs.eq_expressions) == 3 and
            set([name for name, _ in eqs.eq_expressions]) == {'v', 'I', 'f'} and
            all((isinstance(expr, Expression) for _, expr in eqs.eq_expressions)))
    assert len(eqs.eq_names) == 3 and eqs.eq_names == {'v', 'I', 'f'}
    assert set(eqs.keys()) == {'v', 'I', 'f', 'freq'}
    # test that the equations object is iterable itself
    assert all((isinstance(eq, SingleEquation) for eq in eqs.itervalues()))
    assert all((isinstance(eq, basestring) for eq in eqs))
    assert (len(eqs.ordered) == 4 and
            all((isinstance(eq, SingleEquation) for eq in eqs.ordered)) and
            [eq.varname for eq in eqs.ordered] == ['f', 'I', 'v', 'freq'])
    assert eqs.names == {'v', 'I', 'f', 'freq'}
    assert eqs.parameter_names == {'freq'}
    assert eqs.subexpr_names == {'I', 'f'}
    units = eqs.units
    assert set(units.keys()) == {'v', 'I', 'f', 'freq'}
    assert units['v'] == volt
    assert units['I'] == volt
    assert units['f'] == Hz
    assert have_same_dimensions(units['freq'], 1)
    assert eqs.names == set(eqs.units.keys())
    assert eqs.identifiers == {'tau', 'volt', 'Hz', 'sin', 't'}

    # stochastic equations
    assert len(eqs.stochastic_variables) == 0
    assert eqs.stochastic_type is None
    
    eqs = Equations('''dv/dt = -v / tau + 0.1*second**-.5*xi : 1''')
    assert eqs.stochastic_variables == {'xi'}
    assert eqs.stochastic_type == 'additive'
    
    eqs = Equations('''dv/dt = -v / tau + 0.1*second**-.5*xi_1 +  0.1*second**-.5*xi_2: 1''')
    assert eqs.stochastic_variables == {'xi_1', 'xi_2'}
    assert eqs.stochastic_type == 'additive'
    
    eqs = Equations('''dv/dt = -v / tau + 0.1*second**-1.5*xi*t : 1''')
    assert eqs.stochastic_type == 'multiplicative'

    eqs = Equations('''dv/dt = -v / tau + 0.1*second**-1.5*xi*v : 1''')
    assert eqs.stochastic_type == 'multiplicative'
示例#32
0
    def variableview_set_with_index_array(self, variableview, item,
                                          value, check_units):
        if isinstance(item, slice) and item == slice(None):
            item = 'True'
        value = Quantity(value)

        if (isinstance(item, int) or (isinstance(item, np.ndarray) and item.shape==())) and value.size == 1:
            array_name = self.get_array_name(variableview.variable, access_data=False)
            # For a single assignment, generate a code line instead of storing the array
            self.main_queue.append(('set_by_single_value', (array_name,
                                                            item,
                                                            float(value))))

        elif (value.size == 1 and
              item == 'True' and
              variableview.index_var_name == '_idx'):
            # set the whole array to a scalar value
            if have_same_dimensions(value, 1):
                # Avoid a representation as "Quantity(...)" or "array(...)"
                value = float(value)
            variableview.set_with_expression_conditional(cond=item,
                                                         code=repr(value),
                                                         check_units=check_units)
        # Simple case where we don't have to do any indexing
        elif (item == 'True' and variableview.index_var == '_idx'):
            self.fill_with_array(variableview.variable, value)
        else:
            # We have to calculate indices. This will not work for synaptic
            # variables
            try:
                indices = np.asarray(variableview.indexing(item,
                                                           index_var=variableview.index_var))
            except NotImplementedError:
                raise NotImplementedError(('Cannot set variable "%s" this way in '
                                           'standalone, try using string '
                                           'expressions.') % variableview.name)
            # Using the std::vector instead of a pointer to the underlying
            # data for dynamic arrays is fast enough here and it saves us some
            # additional work to set up the pointer
            arrayname = self.get_array_name(variableview.variable,
                                            access_data=False)
            staticarrayname_index = self.static_array('_index_'+arrayname,
                                                      indices)
            if (indices.shape != () and
                    (value.shape == () or
                         (value.size == 1 and indices.size > 1))):
                value = np.repeat(value, indices.size)
            staticarrayname_value = self.static_array('_value_'+arrayname,
                                                      value)
            self.main_queue.append(('set_array_by_array', (arrayname,
                                                           staticarrayname_index,
                                                           staticarrayname_value)))
示例#33
0
def test_switching_off_unit_checks():
    '''
    Check switching off unit checks (used for external functions).
    '''
    import brian2.units.fundamentalunits as fundamentalunits
    x = 3 * second
    y = 5 * volt    
    assert_raises(DimensionMismatchError, lambda: x + y)
    fundamentalunits.unit_checking = False
    # Now it should work
    assert np.asarray(x + y) == np.array(8)    
    assert have_same_dimensions(x, y)
    assert x.has_same_dimensions(y)
    fundamentalunits.unit_checking = True
示例#34
0
def test_switching_off_unit_checks():
    '''
    Check switching off unit checks (used for external functions).
    '''
    import brian2.units.fundamentalunits as fundamentalunits
    x = 3 * second
    y = 5 * volt
    assert_raises(DimensionMismatchError, lambda: x + y)
    fundamentalunits.unit_checking = False
    # Now it should work
    assert np.asarray(x + y) == np.array(8)
    assert have_same_dimensions(x, y)
    assert x.has_same_dimensions(y)
    fundamentalunits.unit_checking = True
示例#35
0
    def __init__(self, type, varname, unit, is_bool=False, expr=None,
                 flags=None):
        self.type = type
        self.varname = varname
        self.unit = unit
        self.is_bool = is_bool
        if is_bool and not have_same_dimensions(unit, 1):
            raise ValueError('Boolean variables are necessarily dimensionless.')
        self.expr = expr
        if flags is None:
            self.flags = []
        else:
            self.flags = flags

        # will be set later in the sort_static_equations method of Equations
        self.update_order = -1
示例#36
0
 def __call__(self, value):
     if not isinstance(value, self.value.__class__):
         try:
             valstr = str(value)
         except:
             valstr = '(undefined)'
         raise TypeError('Parameter {self.name} should be of class '
                         '{self.value.__class__.__name__}, value {valstr} '
                         'has class {value.__class__.__name__}'.format(
                                self=self, value=value, valstr=valstr))
     if isinstance(self.value, Quantity):
         if not have_same_dimensions(self.value, value):
             raise DimensionMismatchError('Parameter {self.name}, current '
                                          'value = {self.value}, tried to '
                                          'change to {value}'.format(
                                             self=self, value=value))
示例#37
0
    def __init__(self, target, target_var, N, rate, weight, when='synapses',
                 order=0):
        if target_var not in target.variables:
            raise KeyError('%s is not a variable of %s' % (target_var, target.name))

        if isinstance(weight, basestring):
            weight = '(%s)' % weight
        else:
            weight_unit = get_unit(weight)
            weight = repr(weight)
            target_unit = target.variables[target_var].unit
            # This will be checked automatically in the abstract code as well
            # but doing an explicit check here allows for a clearer error
            # message
            if not have_same_dimensions(weight_unit, target_unit):
                raise DimensionMismatchError(('The provided weight does not '
                                              'have the same unit as the '
                                              'target variable "%s"') % target_var,
                                             weight_unit.dim,
                                             target_unit.dim)


        binomial_sampling = BinomialFunction(N, rate*target.clock.dt,
                                             name='poissoninput_binomial*')

        code = '{targetvar} += {binomial}()*{weight}'.format(targetvar=target_var,
                                                             binomial=binomial_sampling.name,
                                                             weight=weight)
        self._stored_dt = target.dt_[:]  # make a copy
        # FIXME: we need an explicit reference here for on-the-fly subgroups
        # For example: PoissonInput(group[:N], ...)
        self._group = target
        CodeRunner.__init__(self,
                            group=target,
                            template='stateupdate',
                            code=code,
                            user_code='',
                            when=when,
                            order=order,
                            name='poissoninput*',
                            clock=target.clock
                            )
        self.variables = Variables(self)
        self.variables._add_variable(binomial_sampling.name, binomial_sampling)
示例#38
0
    def __init__(self, unit, value=None, dtype=None, scalar=None,
                 constant=False, is_bool=None):
        
        #: The variable's unit.
        self.unit = unit

        #: reference to a value of type `dtype`
        self.value = value

        if dtype is None:
            self.dtype = get_dtype(value)
        else:
            value_dtype = get_dtype(value)
            if value is not None and value_dtype != dtype:
                raise TypeError(('Conflicting dtype information: '
                                 'referred value has dtype %r, not '
                                 '%r.') % (value_dtype, dtype))
            #: The dtype used for storing the variable.
            self.dtype = dtype

        if is_bool is None:
            if value is None:
                self.is_bool = False
            self.is_bool = value is True or value is False
        else:
            #: Whether this variable is a boolean
            self.is_bool = is_bool

        if is_bool:
            if not have_same_dimensions(unit, 1):
                raise ValueError('Boolean variables can only be dimensionless')

        if scalar is None:
            if value is None:
                self.scalar = True
            self.scalar = is_scalar_type(value)
        else:
            #: Whether the variable is a scalar
            self.scalar = scalar

        #: Whether the variable is constant during a run
        self.constant = constant
示例#39
0
    def __init__(self, type, varname, unit, var_type=FLOAT, expr=None, flags=None):
        self.type = type
        self.varname = varname
        self.unit = unit
        self.var_type = var_type
        if not have_same_dimensions(unit, 1):
            if var_type == BOOLEAN:
                raise TypeError("Boolean variables are necessarily dimensionless.")
            elif var_type == INTEGER:
                raise TypeError("Integer variables are necessarily dimensionless.")

        if type == DIFFERENTIAL_EQUATION:
            if var_type != FLOAT:
                raise TypeError("Differential equations can only define floating point variables")
        self.expr = expr
        if flags is None:
            self.flags = []
        else:
            self.flags = flags

        # will be set later in the sort_subexpressions method of Equations
        self.update_order = -1
示例#40
0
    def __init__(self,
                 type,
                 varname,
                 unit,
                 is_bool=False,
                 expr=None,
                 flags=None):
        self.type = type
        self.varname = varname
        self.unit = unit
        self.is_bool = is_bool
        if is_bool and not have_same_dimensions(unit, 1):
            raise ValueError(
                'Boolean variables are necessarily dimensionless.')
        self.expr = expr
        if flags is None:
            self.flags = []
        else:
            self.flags = flags

        # will be set later in the sort_static_equations method of Equations
        self.update_order = -1
示例#41
0
def assert_allclose(actual, desired, rtol=4.5e8, atol=0, **kwds):
    '''
    Thin wrapper around numpy's `~numpy.testing.utils.assert_allclose` function. The tolerance depends on the floating
    point precision as defined by the `core.default_float_dtype` preference.

    Parameters
    ----------
    actual : `numpy.ndarray`
        The results to check.
    desired : `numpy.ndarray`
        The expected results.
    rtol : float, optional
        The relative tolerance which will be multiplied with the machine epsilon of the type set as
        `core.default_float_type`.
    atol : float, optional
        The absolute tolerance which will be multiplied with the machine epsilon of the type set as
        `core.default_float_type`.
    '''
    assert have_same_dimensions(actual, desired)
    eps = np.finfo(prefs['core.default_float_dtype']).eps
    rtol = eps*rtol
    atol = eps*atol
    numpy_allclose(np.asarray(actual), np.asarray(desired), rtol=rtol, atol=atol, **kwds)
示例#42
0
def test_properties():
    '''
    Test accessing the various properties of equation objects
    '''
    tau = 10 * ms
    eqs = Equations('''dv/dt = -(v + I)/ tau : volt
                       I = sin(2 * 22/7. * f * t)* volt : volt
                       f = freq * Hz: Hz
                       freq : 1''')
    assert (len(eqs.diff_eq_expressions) == 1 and
            eqs.diff_eq_expressions[0][0] == 'v' and
            isinstance(eqs.diff_eq_expressions[0][1], Expression))
    assert eqs.diff_eq_names == ['v']
    assert (len(eqs.eq_expressions) == 3 and
            set([name for name, _ in eqs.eq_expressions]) == set(['v', 'I', 'f']) and
            all((isinstance(expr, Expression) for _, expr in eqs.eq_expressions)))
    assert len(eqs.eq_names) == 3 and set(eqs.eq_names) == set(['v', 'I', 'f'])
    assert set(eqs.equations.keys()) == set(['v', 'I', 'f', 'freq'])
    assert all((isinstance(eq, SingleEquation) for eq in eqs.equations.itervalues()))
    # test that the equations object is iterable itself
    assert all((isinstance(eq, SingleEquation) for (_, eq) in eqs))
    assert (len(eqs.equations_ordered) == 4 and
            all((isinstance(eq, SingleEquation) for eq in eqs.equations_ordered)) and
            [eq.varname for eq in eqs.equations_ordered] == ['f', 'I', 'v', 'freq'])
    assert set(eqs.names) == set(['v', 'I', 'f', 'freq'])
    assert set(eqs.parameter_names) == set(['freq'])
    assert set(eqs.static_eq_names) == set(['I', 'f'])
    units = eqs.units
    assert set(units.keys()) == set(['v', 'I', 'f', 'freq', 't', 'dt', 'xi'])
    assert units['v'] == volt
    assert units['I'] == volt
    assert units['f'] == Hz
    assert have_same_dimensions(units['freq'], 1)
    assert units['t'] == second
    assert units['dt'] == second
    assert units['xi'] == second**-0.5
    assert set(eqs.variables) == set(eqs.units.keys())
示例#43
0
    def group_set_with_index_array(self, group, variable_name, variable, item,
                                   value, check_units):
        if isinstance(item, slice) and item == slice(None):
            item = 'True'
        value = Quantity(value)

        if value.size == 1 and item == 'True':  # set the whole array to a scalar value
            if have_same_dimensions(value, 1):
                # Avoid a representation as "Quantity(...)" or "array(...)"
                value = float(value)
            group.set_with_expression_conditional(variable_name, variable,
                                                  cond=item,
                                                  code=repr(value),
                                                  check_units=check_units)
        # Simple case where we don't have to do any indexing
        elif item == 'True' and group.variables.indices[variable_name] == '_idx':
            self.fill_with_array(variable, value)
        else:
            # We have to calculate indices. This will not work for synaptic
            # variables
            try:
                indices = group.calc_indices(item)
            except NotImplementedError:
                raise NotImplementedError(('Cannot set variable "%s" this way in '
                                           'standalone, try using string '
                                           'expressions.') % variable_name)
            # Using the std::vector instead of a pointer to the underlying
            # data for dynamic arrays is fast enough here and it saves us some
            # additional work to set up the pointer
            arrayname = self.get_array_name(variable, access_data=False)
            staticarrayname_index = self.static_array('_index_'+arrayname,
                                                      indices)
            staticarrayname_value = self.static_array('_value_'+arrayname,
                                                      value)
            self.main_queue.append(('set_array_by_array', (arrayname,
                                                           staticarrayname_index,
                                                           staticarrayname_value)))
示例#44
0
    def __init__(self, filterbank, targetvar, *args, **kwds):
        # Make sure we're not in standalone mode (which won't work)
        if not isinstance(get_device(), RuntimeDevice):
            raise RuntimeError("Cannot use standalone mode with brian2hears")

        self.targetvar = targetvar
        self.filterbank = filterbank
        filterbank.buffer_init()

        # Sanitize the clock - does it have the right dt value?
        if 'clock' in kwds:
            if int(1/kwds['clock'].dt)!=int(filterbank.samplerate):
                raise ValueError('Clock should have 1/dt=samplerate')
        elif 'dt' in kwds:
            if int(1 / kwds['dt']) != int(filterbank.samplerate):
                raise ValueError('Require 1/dt=samplerate')
        else:
            kwds['dt'] = 1/filterbank.samplerate
        
        buffersize = kwds.pop('buffersize', 32)
        if not isinstance(buffersize, int):
            if not have_same_dimensions(buffersize, second):
                raise DimensionMismatchError("buffersize argument should be an integer or in seconds")
            buffersize = int(buffersize*filterbank.samplerate)

        self.buffersize = buffersize

        self.apply_filterbank = ApplyFilterbank(self, targetvar, filterbank, buffersize)

        NeuronGroup.__init__(self, filterbank.nchannels, *args, **kwds)

        if self.variables[targetvar].dim is not DIMENSIONLESS:
            raise DimensionMismatchError("Target variable must be dimensionless")

        apply_filterbank_output = NetworkOperation(self.apply_filterbank.__call__, when='start', clock=self.clock)
        self.contained_objects.append(apply_filterbank_output)
示例#45
0
def parse_expression_dimensions(expr, variables):
    '''
    Returns the unit value of an expression, and checks its validity
    
    Parameters
    ----------
    expr : str
        The expression to check.
    variables : dict
        Dictionary of all variables used in the `expr` (including `Constant`
        objects for external variables)
    
    Returns
    -------
    unit : Quantity
        The output unit of the expression
    
    Raises
    ------
    SyntaxError
        If the expression cannot be parsed, or if it uses ``a**b`` for ``b``
        anything other than a constant number.
    DimensionMismatchError
        If any part of the expression is dimensionally inconsistent.
    '''

    # If we are working on a string, convert to the top level node
    if isinstance(expr, basestring):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body
    if expr.__class__ is getattr(ast, 'NameConstant', None):
        # new class for True, False, None in Python 3.4
        value = expr.value
        if value is True or value is False:
            return DIMENSIONLESS
        else:
            raise ValueError('Do not know how to handle value %s' % value)
    if expr.__class__ is ast.Name:
        name = expr.id
        # Raise an error if a function is called as if it were a variable
        # (most of the time this happens for a TimedArray)
        if name in variables and isinstance(variables[name], Function):
            raise SyntaxError(
                '%s was used like a variable/constant, but it is '
                'a function.' % name)
        if name in variables:
            return variables[name].dim
        elif name in ['True', 'False']:
            return DIMENSIONLESS
        else:
            raise KeyError('Unknown identifier %s' % name)
    elif (expr.__class__ is ast.Num
          or expr.__class__ is getattr(ast, 'Constant', None)):  # Python 3.8
        return DIMENSIONLESS
    elif expr.__class__ is ast.BoolOp:
        # check that the units are valid in each subexpression
        for node in expr.values:
            parse_expression_dimensions(node, variables)
        # but the result is a bool, so we just return 1 as the unit
        return DIMENSIONLESS
    elif expr.__class__ is ast.Compare:
        # check that the units are consistent in each subexpression
        subexprs = [expr.left] + expr.comparators
        subunits = []
        for node in subexprs:
            subunits.append(parse_expression_dimensions(node, variables))
        for left_dim, right_dim in zip(subunits[:-1], subunits[1:]):
            if not have_same_dimensions(left_dim, right_dim):
                msg = (
                    'Comparison of expressions with different units. Expression '
                    '"{}" has unit ({}), while expression "{}" has units ({})'
                ).format(NodeRenderer().render_node(expr.left),
                         get_dimensions(left_dim),
                         NodeRenderer().render_node(expr.comparators[0]),
                         get_dimensions(right_dim))
                raise DimensionMismatchError(msg)
        # but the result is a bool, so we just return 1 as the unit
        return DIMENSIONLESS
    elif expr.__class__ is ast.Call:
        if len(expr.keywords):
            raise ValueError("Keyword arguments not supported.")
        elif getattr(expr, 'starargs', None) is not None:
            raise ValueError("Variable number of arguments not supported")
        elif getattr(expr, 'kwargs', None) is not None:
            raise ValueError("Keyword arguments not supported")

        func = variables.get(expr.func.id, None)
        if func is None:
            raise SyntaxError('Unknown function %s' % expr.func.id)
        if not hasattr(func, '_arg_units') or not hasattr(
                func, '_return_unit'):
            raise ValueError(('Function %s does not specify how it '
                              'deals with units.') % expr.func.id)

        if len(func._arg_units) != len(expr.args):
            raise SyntaxError(
                'Function %s was called with %d parameters, '
                'needs %d.' %
                (expr.func.id, len(expr.args), len(func._arg_units)))

        for idx, (arg,
                  expected_unit) in enumerate(zip(expr.args, func._arg_units)):
            # A "None" in func._arg_units means: No matter what unit
            if expected_unit is None:
                continue
            elif expected_unit == bool:
                if not is_boolean_expression(arg, variables):
                    raise TypeError(
                        ('Argument number %d for function %s was '
                         'expected to be a boolean value, but is '
                         '"%s".') % (idx + 1, expr.func.id,
                                     NodeRenderer().render_node(arg)))
            else:
                arg_unit = parse_expression_dimensions(arg, variables)
                if not have_same_dimensions(arg_unit, expected_unit):
                    msg = (
                        'Argument number {} for function {} does not have the '
                        'correct units. Expression "{}" has units ({}), but '
                        'should be ({}).').format(
                            idx + 1, expr.func.id,
                            NodeRenderer().render_node(arg),
                            get_dimensions(arg_unit),
                            get_dimensions(expected_unit))
                    raise DimensionMismatchError(msg)

        if func._return_unit == bool:
            return DIMENSIONLESS
        elif isinstance(func._return_unit, (Unit, int)):
            # Function always returns the same unit
            return getattr(func._return_unit, 'dim', DIMENSIONLESS)
        else:
            # Function returns a unit that depends on the arguments
            arg_units = [
                parse_expression_dimensions(arg, variables)
                for arg in expr.args
            ]
            return func._return_unit(*arg_units).dim

    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left_dim = parse_expression_dimensions(expr.left, variables)
        right_dim = parse_expression_dimensions(expr.right, variables)
        if op in ['Add', 'Sub', 'Mod']:
            # dimensions should be the same
            if left_dim is not right_dim:
                op_symbol = {'Add': '+', 'Sub': '-', 'Mod': '%'}.get(op)
                left_str = NodeRenderer().render_node(expr.left)
                right_str = NodeRenderer().render_node(expr.right)
                left_unit = get_unit_for_display(left_dim)
                right_unit = get_unit_for_display(right_dim)
                error_msg = ('Expression "{left} {op} {right}" uses '
                             'inconsistent units ("{left}" has unit '
                             '{left_unit}; "{right}" '
                             'has unit {right_unit})').format(
                                 left=left_str,
                                 right=right_str,
                                 op=op_symbol,
                                 left_unit=left_unit,
                                 right_unit=right_unit)
                raise DimensionMismatchError(error_msg)
            u = left_dim
        elif op == 'Mult':
            u = left_dim * right_dim
        elif op == 'Div':
            u = left_dim / right_dim
        elif op == 'FloorDiv':
            if not (left_dim is DIMENSIONLESS and right_dim is DIMENSIONLESS):
                raise SyntaxError('Floor division can only be used on '
                                  'dimensionless values.')
            u = DIMENSIONLESS
        elif op == 'Pow':
            if left_dim is DIMENSIONLESS and right_dim is DIMENSIONLESS:
                return DIMENSIONLESS
            n = _get_value_from_expression(expr.right, variables)
            u = left_dim**n
        else:
            raise SyntaxError("Unsupported operation " + op)
        return u
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        # check validity of operand and get its unit
        u = parse_expression_dimensions(expr.operand, variables)
        if op == 'Not':
            return DIMENSIONLESS
        else:
            return u
    else:
        raise SyntaxError('Unsupported operation ' + str(expr.__class__))
示例#46
0
def parse_expression_unit(expr, namespace, variables):
    '''
    Returns the unit value of an expression, and checks its validity
    
    Parameters
    ----------
    expr : str
        The expression to check.
    namespace : dict-like
        The namespace of external variables.
    variables : dict of `Variable` objects
        The information about the internal variables
    
    Returns
    -------
    unit : Quantity
        The output unit of the expression
    
    Raises
    ------
    SyntaxError
        If the expression cannot be parsed, or if it uses ``a**b`` for ``b``
        anything other than a constant number.
    DimensionMismatchError
        If any part of the expression is dimensionally inconsistent.
        
    Notes
    -----
    
    Currently, functions do not work, see comments in function.
    '''
    # If we are working on a string, convert to the top level node
    if isinstance(expr, basestring):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body
    if expr.__class__ is ast.Name:
        name = expr.id
        if name in variables:
            return variables[name].unit
        elif name in namespace:
            return get_unit_fast(namespace[name])
        elif name in ['True', 'False']:
            return Unit(1)
        else:
            raise ValueError('Unknown identifier %s' % name)
    elif expr.__class__ is ast.Num:
        return get_unit_fast(1)
    elif expr.__class__ is ast.BoolOp:
        # check that the units are valid in each subexpression
        for node in expr.values:
            parse_expression_unit(node, namespace, variables)
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Compare:
        # check that the units are consistent in each subexpression
        subexprs = [expr.left] + expr.comparators
        subunits = []
        for node in subexprs:
            subunits.append(parse_expression_unit(node, namespace, variables))
        for left, right in zip(subunits[:-1], subunits[1:]):
            if not have_same_dimensions(left, right):
                raise DimensionMismatchError(
                    "Comparison of expressions with different units",
                    *[getattr(u, 'dim', 1) for u in subunits])
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Call:
        if len(expr.keywords):
            raise ValueError("Keyword arguments not supported.")
        elif expr.starargs is not None:
            raise ValueError("Variable number of arguments not supported")
        elif expr.kwargs is not None:
            raise ValueError("Keyword arguments not supported")

        arg_units = [
            parse_expression_unit(arg, namespace, variables)
            for arg in expr.args
        ]

        func = namespace.get(expr.func.id, variables.get(expr.func, None))
        if func is None:
            raise SyntaxError('Unknown function %s' % expr.func.id)
        if not hasattr(func, '_arg_units') or not hasattr(
                func, '_return_unit'):
            raise ValueError(('Function %s does not specify how it '
                              'deals with units.') % expr.func.id)

        for idx, arg_unit in enumerate(arg_units):
            # A "None" in func._arg_units means: No matter what unit
            if (func._arg_units[idx] is not None and
                    not have_same_dimensions(arg_unit, func._arg_units[idx])):
                raise DimensionMismatchError(
                    ('Argument number %d for function '
                     '%s does not have the correct '
                     'units' % (idx + 1, expr.func.id)), arg_unit,
                    func._arg_units[idx])

        if isinstance(func._return_unit, (Unit, int)):
            # Function always returns the same unit
            return get_unit_fast(func._return_unit)
        else:
            # Function returns a unit that depends on the arguments
            return func._return_unit(*arg_units)

    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left = parse_expression_unit(expr.left, namespace, variables)
        right = parse_expression_unit(expr.right, namespace, variables)
        if op == 'Add' or op == 'Sub':
            u = left + right
        elif op == 'Mult':
            u = left * right
        elif op == 'Div':
            u = left / right
        elif op == 'Pow':
            if have_same_dimensions(left, 1) and have_same_dimensions(
                    right, 1):
                return get_unit_fast(1)
            n = _get_value_from_expression(expr.right, namespace, variables)
            u = left**n
        elif op == 'Mod':
            u = left % right
        else:
            raise SyntaxError("Unsupported operation " + op)
        return u
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        # check validity of operand and get its unit
        u = parse_expression_unit(expr.operand, namespace, variables)
        if op == 'Not':
            return get_unit_fast(1)
        else:
            return u
    else:
        raise SyntaxError('Unsupported operation ' + str(expr.__class__))
示例#47
0
def parse_expression_unit(expr, variables):
    '''
    Returns the unit value of an expression, and checks its validity
    
    Parameters
    ----------
    expr : str
        The expression to check.
    variables : dict
        Dictionary of all variables used in the `expr` (including `Constant`
        objects for external variables)
    
    Returns
    -------
    unit : Quantity
        The output unit of the expression
    
    Raises
    ------
    SyntaxError
        If the expression cannot be parsed, or if it uses ``a**b`` for ``b``
        anything other than a constant number.
    DimensionMismatchError
        If any part of the expression is dimensionally inconsistent.
    '''

    # If we are working on a string, convert to the top level node    
    if isinstance(expr, basestring):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body
    if expr.__class__ is getattr(ast, 'NameConstant', None):
        # new class for True, False, None in Python 3.4
        value = expr.value
        if value is True or value is False:
            return Unit(1)
        else:
            raise ValueError('Do not know how to handle value %s' % value)
    if expr.__class__ is ast.Name:
        name = expr.id
        if name in variables:
            return variables[name].unit
        elif name in ['True', 'False']:
            return Unit(1)
        else:
            raise KeyError('Unknown identifier %s' % name)
    elif expr.__class__ is ast.Num:
        return get_unit_fast(1)
    elif expr.__class__ is ast.BoolOp:
        # check that the units are valid in each subexpression
        for node in expr.values:
            parse_expression_unit(node, variables)
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Compare:
        # check that the units are consistent in each subexpression
        subexprs = [expr.left]+expr.comparators
        subunits = []
        for node in subexprs:
            subunits.append(parse_expression_unit(node, variables))
        for left, right in zip(subunits[:-1], subunits[1:]):
            if not have_same_dimensions(left, right):
                raise DimensionMismatchError("Comparison of expressions with different units",
                                             *[getattr(u, 'dim', 1) for u in subunits])
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Call:
        if len(expr.keywords):
            raise ValueError("Keyword arguments not supported.")
        elif expr.starargs is not None:
            raise ValueError("Variable number of arguments not supported")
        elif expr.kwargs is not None:
            raise ValueError("Keyword arguments not supported")

        arg_units = [parse_expression_unit(arg, variables)
                     for arg in expr.args]

        func = variables.get(expr.func.id, None)
        if func is None:
            raise SyntaxError('Unknown function %s' % expr.func.id)
        if not hasattr(func, '_arg_units') or not hasattr(func, '_return_unit'):
            raise ValueError(('Function %s does not specify how it '
                              'deals with units.') % expr.func.id)

        for idx, arg_unit in enumerate(arg_units):
            # A "None" in func._arg_units means: No matter what unit
            if (func._arg_units[idx] is not None and
                    not have_same_dimensions(arg_unit, func._arg_units[idx])):
                raise DimensionMismatchError(('Argument number %d for function '
                                              '%s does not have the correct '
                                              'units' % (idx + 1, expr.func.id)),
                                             arg_unit, func._arg_units[idx])

        if isinstance(func._return_unit, (Unit, int)):
            # Function always returns the same unit
            return get_unit_fast(func._return_unit)
        else:
            # Function returns a unit that depends on the arguments
            return func._return_unit(*arg_units)

    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left = parse_expression_unit(expr.left, variables)
        right = parse_expression_unit(expr.right, variables)
        if op=='Add' or op=='Sub':
            u = left+right
        elif op=='Mult':
            u = left*right
        elif op=='Div':
            u = left/right
        elif op=='Pow':
            if have_same_dimensions(left, 1) and have_same_dimensions(right, 1):
                return get_unit_fast(1)
            n = _get_value_from_expression(expr.right, variables)
            u = left**n
        elif op=='Mod':
            u = left % right
        else:
            raise SyntaxError("Unsupported operation "+op)
        return u
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        # check validity of operand and get its unit
        u = parse_expression_unit(expr.operand, variables)
        if op=='Not':
            return get_unit_fast(1)
        else:
            return u
    else:
        raise SyntaxError('Unsupported operation ' + str(expr.__class__))
示例#48
0
    def __getitem__(self, x):
        """
        Returns the subtree named x.
        Ex.: ```neuron['axon']``` or ```neuron['11213']```
        ```neuron[10*um:20*um]``` returns the subbranch from 10 um to 20 um.
        ```neuron[10*um]``` returns one compartment.
        ```neuron[5]``` returns compartment number 5.
        """
        if isinstance(x, slice):  # neuron[10*um:20*um] or neuron[1:3]
            using_lengths = all([arg is None or have_same_dimensions(arg, meter)
                                 for arg in [x.start, x.stop]])
            using_ints = all([arg is None or int(arg) == float(arg)
                                 for arg in [x.start, x.stop]])
            if not (using_lengths or using_ints):
                raise TypeError('Index slice has to use lengths or integers')

            morpho = self._branch()
            if using_lengths:
                if x.step is not None:
                    raise TypeError(('Cannot provide a step argument when '
                                     'slicing with lengths'))
                l = cumsum(array(morpho.length))  # coordinate on the branch
                if x.start is None:
                    i = 0
                else:
                    i = searchsorted(l, float(x.start))
                if x.stop is None:
                    j = len(l)
                else:
                    j = searchsorted(l, float(x.stop))
            else:  # integers
                i, j, step = x.indices(len(morpho))
                if step != 1:
                    raise TypeError('Can only slice a contiguous segment')
        elif isinstance(x, Quantity) and have_same_dimensions(x, meter):  # neuron[10*um]
            morpho = self._branch()
            l = cumsum(array(morpho.length))
            i = searchsorted(l, x)
            j = i + 1
        elif isinstance(x, numbers.Integral):  # int: returns one compartment
            morpho = self._branch()
            if x < 0:  # allows e.g. to use -1 to get the last compartment
                x += len(morpho)
            if x >= len(morpho):
                raise IndexError(('Invalid index %d '
                                  'for %d compartments') % (x, len(morpho)))
            i = x
            j = i + 1
        elif x == 'main':
            return self._branch()
        elif isinstance(x, basestring):
            x = str(x)  # convert int to string
            if (len(x) > 1) and all([c in 'LR123456789' for c in
                                     x]):  # binary string of the form LLLRLR or 1213 (or mixed)
                return self._namedkid[x[0]][x[1:]]
            elif x in self._namedkid:
                return self._namedkid[x]
            else:
                raise AttributeError, "The subtree " + x + " does not exist"
        else:
            raise TypeError('Index of type %s not understood' % type(x))

        # Return the sub-morphology
        morpho.diameter = morpho.diameter[i:j]
        morpho.length = morpho.length[i:j]
        morpho.area = morpho.area[i:j]
        morpho.x = morpho.x[i:j]
        morpho.y = morpho.y[i:j]
        morpho.z = morpho.z[i:j]
        morpho.distance = morpho.distance[i:j]
        if hasattr(morpho, '_origin'):
            morpho._origin += i
        return morpho
示例#49
0
def test_special_case_numpy_functions():
    '''
    Test a couple of functions/methods that need special treatment.
    '''
    from brian2.units.unitsafefunctions import ravel, diagonal, trace, dot, where

    quadratic_matrix = np.reshape(np.arange(9), (3, 3)) * mV

    # Temporarily suppress warnings related to the matplotlib 1.3 bug
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        # Check that function and method do the same thing
        assert_equal(ravel(quadratic_matrix), quadratic_matrix.ravel())
        # Check that function gives the same result as on unitless arrays
        assert_equal(np.asarray(ravel(quadratic_matrix)),
                     ravel(np.asarray(quadratic_matrix)))
        # Check that the function gives the same results as the original numpy
        # function
        assert_equal(np.ravel(np.asarray(quadratic_matrix)),
                     ravel(np.asarray(quadratic_matrix)))

    # Do the same checks for diagonal, trace and dot
    assert_equal(diagonal(quadratic_matrix), quadratic_matrix.diagonal())
    assert_equal(np.asarray(diagonal(quadratic_matrix)),
                 diagonal(np.asarray(quadratic_matrix)))
    assert_equal(np.diagonal(np.asarray(quadratic_matrix)),
                 diagonal(np.asarray(quadratic_matrix)))

    assert_equal(trace(quadratic_matrix), quadratic_matrix.trace())
    assert_equal(np.asarray(trace(quadratic_matrix)),
                 trace(np.asarray(quadratic_matrix)))
    assert_equal(np.trace(np.asarray(quadratic_matrix)),
                 trace(np.asarray(quadratic_matrix)))

    assert_equal(dot(quadratic_matrix, quadratic_matrix),
                 quadratic_matrix.dot(quadratic_matrix))
    assert_equal(
        np.asarray(dot(quadratic_matrix, quadratic_matrix)),
        dot(np.asarray(quadratic_matrix), np.asarray(quadratic_matrix)))
    assert_equal(
        np.dot(np.asarray(quadratic_matrix), np.asarray(quadratic_matrix)),
        dot(np.asarray(quadratic_matrix), np.asarray(quadratic_matrix)))

    assert_equal(np.asarray(quadratic_matrix.prod()),
                 np.asarray(quadratic_matrix).prod())
    assert_equal(np.asarray(quadratic_matrix.prod(axis=0)),
                 np.asarray(quadratic_matrix).prod(axis=0))

    # Check for correct units
    if use_matplotlib_units_fix:
        assert have_same_dimensions(1, ravel(quadratic_matrix))
    else:
        assert have_same_dimensions(quadratic_matrix, ravel(quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix, trace(quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix, diagonal(quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix[0]**2,
                                dot(quadratic_matrix, quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix.prod(axis=0),
                                quadratic_matrix[0]**quadratic_matrix.shape[0])

    # check the where function
    # pure numpy array
    cond = [True, False, False]
    ar1 = np.array([1, 2, 3])
    ar2 = np.array([4, 5, 6])
    assert_equal(np.where(cond), where(cond))
    assert_equal(np.where(cond, ar1, ar2), where(cond, ar1, ar2))

    # dimensionless quantity
    assert_equal(np.where(cond, ar1, ar2),
                 np.asarray(where(cond, ar1 * mV / mV, ar2 * mV / mV)))

    # quantity with dimensions
    ar1 = ar1 * mV
    ar2 = ar2 * mV
    assert_equal(np.where(cond, np.asarray(ar1), np.asarray(ar2)),
                 np.asarray(where(cond, ar1, ar2)))

    # Check some error cases
    with pytest.raises(ValueError):
        where(cond, ar1)
    with pytest.raises(TypeError):
        where(cond, ar1, ar1, ar2)
    with pytest.raises(DimensionMismatchError):
        where(cond, ar1, ar1 / ms)

    # Check setasflat (for numpy < 1.7)
    if hasattr(Quantity, 'setasflat'):
        a = np.arange(10) * mV
        b = np.ones(10).reshape(5, 2) * volt
        c = np.ones(10).reshape(5, 2) * second
        with pytest.raises(DimensionMismatchError):
            a.setasflat(c)
        a.setasflat(b)
        assert_equal(a.flatten(), b.flatten())

    # Check cumprod
    a = np.arange(1, 10) * mV / mV
    assert_equal(a.cumprod(), np.asarray(a).cumprod())
    with pytest.raises(TypeError):
        (np.arange(1, 5) * mV).cumprod()
示例#50
0
def test_special_case_numpy_functions():
    '''
    Test a couple of functions/methods that need special treatment.
    '''
    from brian2.units.unitsafefunctions import ravel, diagonal, trace, dot, where
    
    quadratic_matrix = np.reshape(np.arange(9), (3, 3)) * mV

    # Temporarily suppress warnings related to the matplotlib 1.3 bug
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        # Check that function and method do the same thing
        assert_equal(ravel(quadratic_matrix), quadratic_matrix.ravel())
        # Check that function gives the same result as on unitless arrays
        assert_equal(np.asarray(ravel(quadratic_matrix)),
                     ravel(np.asarray(quadratic_matrix)))
        # Check that the function gives the same results as the original numpy
        # function
        assert_equal(np.ravel(np.asarray(quadratic_matrix)),
                     ravel(np.asarray(quadratic_matrix)))

    # Do the same checks for diagonal, trace and dot
    assert_equal(diagonal(quadratic_matrix), quadratic_matrix.diagonal())
    assert_equal(np.asarray(diagonal(quadratic_matrix)),
                 diagonal(np.asarray(quadratic_matrix)))
    assert_equal(np.diagonal(np.asarray(quadratic_matrix)),
                 diagonal(np.asarray(quadratic_matrix)))

    assert_equal(trace(quadratic_matrix), quadratic_matrix.trace())
    assert_equal(np.asarray(trace(quadratic_matrix)),
                 trace(np.asarray(quadratic_matrix)))
    assert_equal(np.trace(np.asarray(quadratic_matrix)),
                 trace(np.asarray(quadratic_matrix)))

    assert_equal(dot(quadratic_matrix, quadratic_matrix),
                 quadratic_matrix.dot(quadratic_matrix))
    assert_equal(np.asarray(dot(quadratic_matrix, quadratic_matrix)),
                 dot(np.asarray(quadratic_matrix), np.asarray(quadratic_matrix)))
    assert_equal(np.dot(np.asarray(quadratic_matrix), np.asarray(quadratic_matrix)),
                 dot(np.asarray(quadratic_matrix), np.asarray(quadratic_matrix)))
    
    assert_equal(np.asarray(quadratic_matrix.prod()),
                 np.asarray(quadratic_matrix).prod())
    assert_equal(np.asarray(quadratic_matrix.prod(axis=0)),
                 np.asarray(quadratic_matrix).prod(axis=0))
        
    # Check for correct units
    if use_matplotlib_units_fix:
        assert have_same_dimensions(1, ravel(quadratic_matrix))
    else:
        assert have_same_dimensions(quadratic_matrix, ravel(quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix, trace(quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix, diagonal(quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix[0] ** 2,
                                dot(quadratic_matrix, quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix.prod(axis=0),
                                quadratic_matrix[0] ** quadratic_matrix.shape[0])
    
    # check the where function
    # pure numpy array
    cond = [True, False, False]
    ar1 = np.array([1, 2, 3])
    ar2 = np.array([4, 5, 6])
    assert_equal(np.where(cond), where(cond))
    assert_equal(np.where(cond, ar1, ar2), where(cond, ar1, ar2))
    
    # dimensionless quantity
    assert_equal(np.where(cond, ar1, ar2),
                 np.asarray(where(cond, ar1 * mV/mV, ar2 * mV/mV)))
    
    # quantity with dimensions
    ar1 = ar1 * mV
    ar2 = ar2 * mV
    assert_equal(np.where(cond, np.asarray(ar1), np.asarray(ar2)),
                 np.asarray(where(cond, ar1, ar2)))    
    
    # Check some error cases
    assert_raises(ValueError, lambda: where(cond, ar1))
    assert_raises(TypeError, lambda: where(cond, ar1, ar1, ar2))
    assert_raises(DimensionMismatchError, lambda: where(cond, ar1, ar1 / ms))

    # Check setasflat (for numpy < 1.7)
    if hasattr(Quantity, 'setasflat'):
        a = np.arange(10) * mV
        b = np.ones(10).reshape(5, 2) * volt
        c = np.ones(10).reshape(5, 2) * second
        assert_raises(DimensionMismatchError, lambda: a.setasflat(c))
        a.setasflat(b)
        assert_equal(a.flatten(), b.flatten())

    # Check cumprod
    a = np.arange(1, 10) * mV/mV
    assert_equal(a.cumprod(), np.asarray(a).cumprod())
    assert_raises(TypeError, lambda: (np.arange(1, 5)*mV).cumprod())
示例#51
0
    def __getitem__(self, x):
        """
        Returns the subtree named x.
        Ex.: ```neuron['axon']``` or ```neuron['11213']```
        ```neuron[10*um:20*um]``` returns the subbranch from 10 um to 20 um.
        ```neuron[10*um]``` returns one compartment.
        ```neuron[5]``` returns compartment number 5.
        """
        if isinstance(x, slice):  # neuron[10*um:20*um] or neuron[1:3]
            using_lengths = all([
                arg is None or have_same_dimensions(arg, meter)
                for arg in [x.start, x.stop]
            ])
            using_ints = all([
                arg is None or int(arg) == float(arg)
                for arg in [x.start, x.stop]
            ])
            if not (using_lengths or using_ints):
                raise TypeError('Index slice has to use lengths or integers')

            morpho = self._branch()
            if using_lengths:
                if x.step is not None:
                    raise TypeError(('Cannot provide a step argument when '
                                     'slicing with lengths'))
                l = cumsum(array(morpho.length))  # coordinate on the branch
                if x.start is None:
                    i = 0
                else:
                    i = searchsorted(l, float(x.start))
                if x.stop is None:
                    j = len(l)
                else:
                    j = searchsorted(l, float(x.stop))
            else:  # integers
                i, j, step = x.indices(len(morpho))
                if step != 1:
                    raise TypeError('Can only slice a contiguous segment')
        elif isinstance(x, Quantity) and have_same_dimensions(
                x, meter):  # neuron[10*um]
            morpho = self._branch()
            l = cumsum(array(morpho.length))
            i = searchsorted(l, x)
            j = i + 1
        elif isinstance(x, numbers.Integral):  # int: returns one compartment
            morpho = self._branch()
            if x < 0:  # allows e.g. to use -1 to get the last compartment
                x += len(morpho)
            if x >= len(morpho):
                raise IndexError(('Invalid index %d '
                                  'for %d compartments') % (x, len(morpho)))
            i = x
            j = i + 1
        elif x == 'main':
            return self._branch()
        elif isinstance(x, basestring):
            x = str(x)  # convert int to string
            if (len(x) > 1) and all([
                    c in 'LR123456789' for c in x
            ]):  # binary string of the form LLLRLR or 1213 (or mixed)
                return self._namedkid[x[0]][x[1:]]
            elif x in self._namedkid:
                return self._namedkid[x]
            else:
                raise AttributeError, "The subtree " + x + " does not exist"
        else:
            raise TypeError('Index of type %s not understood' % type(x))

        # Return the sub-morphology
        morpho.diameter = morpho.diameter[i:j]
        morpho.length = morpho.length[i:j]
        morpho.area = morpho.area[i:j]
        morpho.x = morpho.x[i:j]
        morpho.y = morpho.y[i:j]
        morpho.z = morpho.z[i:j]
        morpho.distance = morpho.distance[i:j]
        if hasattr(morpho, '_origin'):
            morpho._origin += i
        return morpho
示例#52
0
def parse_expression_unit(expr, variables):
    '''
    Returns the unit value of an expression, and checks its validity
    
    Parameters
    ----------
    expr : str
        The expression to check.
    variables : dict
        Dictionary of all variables used in the `expr` (including `Constant`
        objects for external variables)
    
    Returns
    -------
    unit : Quantity
        The output unit of the expression
    
    Raises
    ------
    SyntaxError
        If the expression cannot be parsed, or if it uses ``a**b`` for ``b``
        anything other than a constant number.
    DimensionMismatchError
        If any part of the expression is dimensionally inconsistent.
    '''

    # If we are working on a string, convert to the top level node
    if isinstance(expr, basestring):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body
    if expr.__class__ is getattr(ast, 'NameConstant', None):
        # new class for True, False, None in Python 3.4
        value = expr.value
        if value is True or value is False:
            return Unit(1)
        else:
            raise ValueError('Do not know how to handle value %s' % value)
    if expr.__class__ is ast.Name:
        name = expr.id
        # Raise an error if a function is called as if it were a variable
        # (most of the time this happens for a TimedArray)
        if name in variables and isinstance(variables[name], Function):
            raise SyntaxError(
                '%s was used like a variable/constant, but it is '
                'a function.' % name)
        if name in variables:
            return variables[name].unit
        elif name in ['True', 'False']:
            return Unit(1)
        else:
            raise KeyError('Unknown identifier %s' % name)
    elif expr.__class__ is ast.Num:
        return get_unit_fast(1)
    elif expr.__class__ is ast.BoolOp:
        # check that the units are valid in each subexpression
        for node in expr.values:
            parse_expression_unit(node, variables)
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Compare:
        # check that the units are consistent in each subexpression
        subexprs = [expr.left] + expr.comparators
        subunits = []
        for node in subexprs:
            subunits.append(parse_expression_unit(node, variables))
        for left, right in zip(subunits[:-1], subunits[1:]):
            if not have_same_dimensions(left, right):
                raise DimensionMismatchError(
                    "Comparison of expressions with different units",
                    *[getattr(u, 'dim', 1) for u in subunits])
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Call:
        if len(expr.keywords):
            raise ValueError("Keyword arguments not supported.")
        elif getattr(expr, 'starargs', None) is not None:
            raise ValueError("Variable number of arguments not supported")
        elif getattr(expr, 'kwargs', None) is not None:
            raise ValueError("Keyword arguments not supported")

        arg_units = [
            parse_expression_unit(arg, variables) for arg in expr.args
        ]

        func = variables.get(expr.func.id, None)
        if func is None:
            raise SyntaxError('Unknown function %s' % expr.func.id)
        if not hasattr(func, '_arg_units') or not hasattr(
                func, '_return_unit'):
            raise ValueError(('Function %s does not specify how it '
                              'deals with units.') % expr.func.id)

        if len(func._arg_units) != len(arg_units):
            raise SyntaxError(
                'Function %s was called with %d parameters, '
                'needs %d.' %
                (expr.func.id, len(arg_units), len(func._arg_units)))
        for idx, arg_unit in enumerate(arg_units):
            # A "None" in func._arg_units means: No matter what unit
            if (func._arg_units[idx] is not None and
                    not have_same_dimensions(arg_unit, func._arg_units[idx])):
                raise DimensionMismatchError(
                    ('Argument number %d for function '
                     '%s does not have the correct '
                     'units' % (idx + 1, expr.func.id)), arg_unit,
                    func._arg_units[idx])

        if isinstance(func._return_unit, (Unit, int)):
            # Function always returns the same unit
            return get_unit_fast(func._return_unit)
        else:
            # Function returns a unit that depends on the arguments
            return func._return_unit(*arg_units)

    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left = parse_expression_unit(expr.left, variables)
        right = parse_expression_unit(expr.right, variables)
        if op == 'Add' or op == 'Sub':
            u = left + right
        elif op == 'Mult':
            u = left * right
        elif op == 'Div':
            u = left / right
        elif op == 'Pow':
            if have_same_dimensions(left, 1) and have_same_dimensions(
                    right, 1):
                return get_unit_fast(1)
            n = _get_value_from_expression(expr.right, variables)
            u = left**n
        elif op == 'Mod':
            u = left % right
        else:
            raise SyntaxError("Unsupported operation " + op)
        return u
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        # check validity of operand and get its unit
        u = parse_expression_unit(expr.operand, variables)
        if op == 'Not':
            return get_unit_fast(1)
        else:
            return u
    else:
        raise SyntaxError('Unsupported operation ' + str(expr.__class__))
示例#53
0
def _get_value_from_expression(expr, namespace, variables):
    '''
    Returns the scalar value of an expression, and checks its validity.

    Parameters
    ----------
    expr : str or `ast.Expression`
        The expression to check.
    namespace : dict-like
        The namespace of external variables.
    variables : dict of `Variable` objects
        The information about the internal variables

    Returns
    -------
    value : float
        The value of the expression

    Raises
    ------
    SyntaxError
        If the expression cannot be evaluated to a scalar value
    DimensionMismatchError
        If any part of the expression is dimensionally inconsistent.
    '''
    # If we are working on a string, convert to the top level node
    if isinstance(expr, basestring):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body

    if expr.__class__ is ast.Name:
        name = expr.id
        if name in variables:
            if not getattr(variables[name], 'constant', False):
                raise SyntaxError('Value %s is not constant' % name)
            if not getattr(variables[name], 'scalar', False):
                raise SyntaxError('Value %s is not scalar' % name)
            return variables[name].get_value()
        elif name in namespace:
            if not have_same_dimensions(namespace[name], 1):
                raise SyntaxError('Variable %s is not dimensionless' % name)
            try:
                val = float(namespace[name])
            except (TypeError, ValueError):
                raise SyntaxError('Cannot convert %s to a scalar value' % name)
            return val
        elif name in ['True', 'False']:
            return 1.0 if name == 'True' else 0.0
        else:
            raise ValueError('Unknown identifier %s' % name)
    elif expr.__class__ is ast.Num:
        return expr.n
    elif expr.__class__ is ast.BoolOp:
        raise SyntaxError(
            'Cannot determine the numerical value for a boolean operation.')
    elif expr.__class__ is ast.Compare:
        raise SyntaxError(
            'Cannot determine the numerical value for a boolean operation.')
    elif expr.__class__ is ast.Call:
        raise SyntaxError(
            'Cannot determine the numerical value for a function call.')
    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left = _get_value_from_expression(expr.left, namespace, variables)
        right = _get_value_from_expression(expr.right, namespace, variables)
        if op == 'Add' or op == 'Sub':
            v = left + right
        elif op == 'Mult':
            v = left * right
        elif op == 'Div':
            v = left / right
        elif op == 'Pow':
            v = left**right
        elif op == 'Mod':
            v = left % right
        else:
            raise SyntaxError("Unsupported operation " + op)
        return v
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        # check validity of operand and get its unit
        v = _get_value_from_expression(expr.operand, namespace, variables)
        if op == 'Not':
            raise SyntaxError(('Cannot determine the numerical value '
                               'for a boolean operation.'))
        if op == 'USub':
            return -v
        else:
            raise SyntaxError('Unknown unary operation ' + op)
    else:
        raise SyntaxError('Unsupported operation ' + str(expr.__class__))
示例#54
0
def test_special_case_numpy_functions():
    '''
    Test a couple of functions/methods that need special treatment.
    '''
    from brian2.units.unitsafefunctions import ravel, diagonal, trace, dot, where
    
    quadratic_matrix = np.reshape(np.arange(9), (3, 3)) * mV
    # Check that function and method do the same thing
    assert_equal(ravel(quadratic_matrix), quadratic_matrix.ravel())
    # Check that function gives the same result as on unitless arrays
    assert_equal(np.asarray(ravel(quadratic_matrix)),
                 ravel(np.asarray(quadratic_matrix)))
    # Check that the function gives the same results as the original numpy
    # function
    assert_equal(np.ravel(np.asarray(quadratic_matrix)),
                 ravel(np.asarray(quadratic_matrix)))

    # Do the same checks for diagonal, trace and dot
    assert_equal(diagonal(quadratic_matrix), quadratic_matrix.diagonal())
    assert_equal(np.asarray(diagonal(quadratic_matrix)),
                 diagonal(np.asarray(quadratic_matrix)))
    assert_equal(np.diagonal(np.asarray(quadratic_matrix)),
                 diagonal(np.asarray(quadratic_matrix)))

    assert_equal(trace(quadratic_matrix), quadratic_matrix.trace())
    assert_equal(np.asarray(trace(quadratic_matrix)),
                 trace(np.asarray(quadratic_matrix)))
    assert_equal(np.trace(np.asarray(quadratic_matrix)),
                 trace(np.asarray(quadratic_matrix)))

    assert_equal(dot(quadratic_matrix, quadratic_matrix),
                 quadratic_matrix.dot(quadratic_matrix))
    assert_equal(np.asarray(dot(quadratic_matrix, quadratic_matrix)),
                 dot(np.asarray(quadratic_matrix), np.asarray(quadratic_matrix)))
    assert_equal(np.dot(np.asarray(quadratic_matrix), np.asarray(quadratic_matrix)),
                 dot(np.asarray(quadratic_matrix), np.asarray(quadratic_matrix)))
    
    assert_equal(np.asarray(quadratic_matrix.prod()),
                 np.asarray(quadratic_matrix).prod())
    assert_equal(np.asarray(quadratic_matrix.prod(axis=0)),
                 np.asarray(quadratic_matrix).prod(axis=0))
        
    # Check for correct units
    assert have_same_dimensions(quadratic_matrix, ravel(quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix, trace(quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix, diagonal(quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix[0] ** 2,
                                dot(quadratic_matrix, quadratic_matrix))
    assert have_same_dimensions(quadratic_matrix.prod(axis=0),
                                quadratic_matrix[0] ** quadratic_matrix.shape[0])
    
    # check the where function
    # pure numpy array
    cond = [True, False, False]
    ar1 = np.array([1, 2, 3])
    ar2 = np.array([4, 5, 6])
    assert_equal(np.where(cond), where(cond))
    assert_equal(np.where(cond, ar1, ar2), where(cond, ar1, ar2))
    
    # dimensionless quantity
    assert_equal(np.where(cond, ar1, ar2),
                 np.asarray(where(cond, ar1 * mV/mV, ar2 * mV/mV)))
    
    # quantity with dimensions
    ar1 = ar1 * mV
    ar2 = ar2 * mV
    assert_equal(np.where(cond, np.asarray(ar1), np.asarray(ar2)),
                 np.asarray(where(cond, ar1, ar2)))    
    
    # Check some error cases
    assert_raises(ValueError, lambda: where(cond, ar1))
    assert_raises(TypeError, lambda: where(cond, ar1, ar1, ar2))
    assert_raises(DimensionMismatchError, lambda: where(cond, ar1, ar1 / ms))
示例#55
0
def assert_quantity(q, values, unit):
    assert isinstance(q, Quantity) or (isinstance(q, np.ndarray) and have_same_dimensions(unit, 1))
    assert_equal(np.asarray(q), values)
    assert have_same_dimensions(q, unit), ('Dimension mismatch: (%s) (%s)' %
                                           (get_dimensions(q),
                                            get_dimensions(unit)))