Skip to content

Commit

Permalink
Merge 316d8c9 into a2fab76
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcel Stimberg committed Jul 9, 2013
2 parents a2fab76 + 316d8c9 commit 51c745d
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 105 deletions.
31 changes: 1 addition & 30 deletions brian2/codegen/functions/numpyfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,34 +111,6 @@ def on_compile_cpp(self, namespace, language, var):
pass


class BoolFunction(Function):
''' A specifier for the `bool` function. To make sure that they are
interpreted as boolean values, references to state variables that are
meant as boolean (e.g. ``not_refractory``) should be wrapped in this
function to make sure it is interpreted correctly.
'''
def __init__(self):
Function.__init__(self, pyfunc=np.bool_, arg_units=[1], return_unit=1)

def __call__(self, value):
return np.bool_(value)

def code_cpp(self, language, var):

support_code = '''
double _bool(float value)
{
return value == 0 ? false : true;
}
'''

return {'support_code': support_code,
'hashdefine_code': ''}

def on_compile_cpp(self, namespace, language, var):
pass


class FunctionWrapper(Function):
'''
Simple wrapper for functions that have exist both in numpy and C++
Expand Down Expand Up @@ -245,8 +217,7 @@ def _get_default_functions():
'mod': FunctionWrapper(np.mod, py_name='mod',
cpp_name='fmod',
sympy_func=sympy_mod.Mod,
arg_units=[None, None], return_unit=lambda u,v : u),
'bool': BoolFunction()
arg_units=[None, None], return_unit=lambda u,v : u)
}

return functions
Expand Down
74 changes: 56 additions & 18 deletions brian2/core/specifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
TODO: have a single global dtype rather than specify for each variable?
'''
import numpy as np

from brian2.units.allunits import second

from brian2.utils.stringtools import get_identifiers
from brian2.units.fundamentalunits import is_scalar_type
from brian2.units.fundamentalunits import is_scalar_type, have_same_dimensions

__all__ = ['Specifier',
'VariableSpecifier',
Expand Down Expand Up @@ -63,16 +64,26 @@ class VariableSpecifier(Specifier):
constant: bool, optional
Whether the value of this variable can change during a run. Defaults
to ``False``.
is_bool: bool, optional
Whether this is a boolean variable (also implies it is dimensionless).
Defaults to ``False``
See Also
--------
Value
'''
def __init__(self, name, unit, scalar=True, constant=False):
def __init__(self, name, unit, scalar=True, constant=False, is_bool=False):
Specifier.__init__(self, name)

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

#: Whether this is a boolean variable
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 value is a scalar
self.scalar = scalar

Expand Down Expand Up @@ -111,10 +122,15 @@ class Value(VariableSpecifier):
defined for every neuron (``False``). Defaults to ``True``.
constant: bool, optional
Whether the value of this variable can change during a run. Defaults
to ``False``.
to ``False``.
is_bool: bool, optional
Whether this is a boolean variable (also implies it is dimensionless).
Defaults to ``False``
'''
def __init__(self, name, unit, dtype, scalar=True, constant=False):
VariableSpecifier.__init__(self, name, unit, scalar, constant)
def __init__(self, name, unit, dtype, scalar=True, constant=False,
is_bool=False):
VariableSpecifier.__init__(self, name, unit, scalar, constant, is_bool)
#: The dtype used for storing the variable.
self.dtype = dtype

Expand Down Expand Up @@ -146,6 +162,7 @@ def __repr__(self):
scalar=repr(self.scalar),
constant=repr(self.constant))


###############################################################################
# Concrete classes that are used as specifiers in practice.
###############################################################################
Expand Down Expand Up @@ -177,8 +194,11 @@ def __init__(self, name, unit, dtype, value):
self.value = value

scalar = is_scalar_type(value)

Value.__init__(self, name, unit, dtype, scalar, constant=True)

is_bool = value is True or value is False

Value.__init__(self, name, unit, dtype, scalar, constant=True,
is_bool=is_bool)

def get_value(self):
return self.value
Expand Down Expand Up @@ -235,28 +255,33 @@ class AttributeValue(ReadOnlyValue):
to be an attribute of `obj`.
constant : bool, optional
Whether the attribute's value is constant during a run.
is_bool: bool, optional
Whether this is a boolean variable (also implies it is dimensionless).
Defaults to ``False``
Raises
------
AttributeError
If `obj` does not have an attribute `attribute`.
'''
def __init__(self, name, unit, dtype, obj, attribute, constant=False):
def __init__(self, name, unit, dtype, obj, attribute, constant=False,
is_bool=False):
if not hasattr(obj, attribute):
raise AttributeError(('Object %r does not have an attribute %r, '
'providing the value for %r') %
(obj, attribute, name))

value = getattr(obj, attribute)
scalar = is_scalar_type(value)

is_bool = value is True or value is False

scalar = is_scalar_type(getattr(obj, attribute))

Value.__init__(self, name, unit, dtype, scalar, constant)
Value.__init__(self, name, unit, dtype, scalar, constant, is_bool)
#: A reference to the object storing the variable's value
self.obj = obj
#: The name of the attribute storing the variable's value
self.attribute = attribute


def get_value(self):
return getattr(self.obj, self.attribute)

Expand Down Expand Up @@ -303,9 +328,18 @@ class ArrayVariable(Value):
variable.
constant : bool, optional
Whether the variable's value is constant during a run.
is_bool: bool, optional
Whether this is a boolean variable (also implies it is dimensionless).
Defaults to ``False``
'''
def __init__(self, name, unit, dtype, array, index, constant=False):
Value.__init__(self, name, unit, dtype, scalar=False, constant=constant)
def __init__(self, name, unit, dtype, array, index, constant=False,
is_bool=False):
if is_bool:
if not dtype == np.bool:
raise ValueError(('Boolean variables have to be stored with '
'boolean dtype'))
Value.__init__(self, name, unit, dtype, scalar=False,
constant=constant, is_bool=is_bool)
#: The reference to the array storing the data for the variable.
self.array = array
#: The name for the array used in generated code
Expand Down Expand Up @@ -353,9 +387,13 @@ class Subexpression(Value):
namespace : dict
The namespace dictionary, containing identifiers for all the external
variables/functions used in the expression
is_bool: bool, optional
Whether this is a boolean variable (also implies it is dimensionless).
Defaults to ``False``
'''
def __init__(self, name, unit, dtype, expr, specifiers, namespace):
Value.__init__(self, name, unit, dtype, scalar=False)
def __init__(self, name, unit, dtype, expr, specifiers, namespace,
is_bool=False):
Value.__init__(self, name, unit, dtype, scalar=False, is_bool=is_bool)
#: The expression defining the static equation.
self.expr = expr.strip()
#: The identifiers used in the expression
Expand Down Expand Up @@ -424,4 +462,4 @@ def __init__(self, name, iterate_all=True):
def __repr__(self):
return '%s(name=%r, iterate_all=%r)' % (self.__class__.__name__,
self.name,
self.iterate_all)
self.iterate_all)
20 changes: 15 additions & 5 deletions brian2/equations/equations.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
import sympy
from pyparsing import (Group, ZeroOrMore, OneOrMore, Optional, Word, CharsNotIn,
Combine, Suppress, restOfLine, LineEnd, ParseException)
import sympy

from brian2.codegen.parsing import sympy_to_str, str_to_sympy
from brian2.units.fundamentalunits import DimensionMismatchError
from brian2.units.fundamentalunits import Unit, have_same_dimensions
from brian2.units.allunits import second
from brian2.utils.logger import get_logger

Expand Down Expand Up @@ -189,7 +188,9 @@ def parse_string_equations(eqns):

# Convert unit string to Unit object
unit = unit_from_string(eq_content['unit'])

is_bool = unit is True
if is_bool:
unit = Unit(1)
expression = eq_content.get('expression', None)
if not expression is None:
# Replace multiple whitespaces (arising from joining multiline
Expand All @@ -198,7 +199,8 @@ def parse_string_equations(eqns):
expression = Expression(p.sub(' ', expression))
flags = list(eq_content.get('flags', []))

equation = SingleEquation(eq_type, identifier, unit, expression, flags)
equation = SingleEquation(eq_type, identifier, unit, is_bool=is_bool,
expr=expression, flags=flags)

if identifier in equations:
raise EquationError('Duplicate definition of variable "%s"' %
Expand All @@ -225,6 +227,9 @@ class SingleEquation(object):
The variable that is defined by this equation.
unit : Unit
The unit of the variable
is_bool : bool, optional
Whether this variable is a boolean variable (implies it is
dimensionless as well). Defaults to ``False``.
expr : `Expression`, optional
The expression defining the variable (or ``None`` for parameters).
flags: list of str, optional
Expand All @@ -233,10 +238,14 @@ class SingleEquation(object):
context.
'''
def __init__(self, type, varname, unit, expr=None, flags=None):
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 = []
Expand Down Expand Up @@ -314,6 +323,7 @@ def _repr_pretty_(self, p, cycle):
def _repr_latex_(self):
return '$' + sympy.latex(self) + '$'


class Equations(collections.Mapping):
"""
Container that stores equations from which models can be created.
Expand Down
5 changes: 3 additions & 2 deletions brian2/equations/refractory.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,14 @@ def add_refractoriness(eqs):
new_code = 'not_refractory*(' + eq.expr.code + ')'
new_equations.append(SingleEquation(DIFFERENTIAL_EQUATION,
eq.varname, eq.unit,
Expression(new_code),
expr=Expression(new_code),
flags=eq.flags))
else:
new_equations.append(eq)

# add new parameters
new_equations.append(SingleEquation(PARAMETER, 'not_refractory', Unit(1)))
new_equations.append(SingleEquation(PARAMETER, 'not_refractory', Unit(1),
is_bool=True))
new_equations.append(SingleEquation(PARAMETER, 'lastspike', second))

return Equations(new_equations)
6 changes: 3 additions & 3 deletions brian2/equations/unitcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def unit_from_string(unit_string):
'''
Returns the unit that results from evaluating a string like
"siemens / metre ** 2", allowing for the special string "1" to signify
dimensionless units.
dimensionless units and the string "bool" to mark a boolean variable.
Parameters
----------
Expand All @@ -35,8 +35,8 @@ def unit_from_string(unit_string):
Returns
-------
u : Unit
The resulting unit
u : Unit or bool
The resulting unit or ``True`` for a boolean parameter.
Raises
------
Expand Down
Loading

0 comments on commit 51c745d

Please sign in to comment.