Skip to content

Commit

Permalink
Merge b61cca6 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 + b61cca6 commit 6d9dd74
Show file tree
Hide file tree
Showing 13 changed files with 303 additions and 140 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
68 changes: 48 additions & 20 deletions brian2/codegen/translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* The language to translate to
'''
import re
import collections

from numpy import float64

Expand All @@ -25,10 +26,12 @@
from .statements import Statement
from .parsing import parse_statement

__all__ = ['translate', 'make_statements']
__all__ = ['translate', 'make_statements', 'analyse_identifiers',
'get_identifiers_recursively']

DEBUG = False


class LineInfo(object):
'''
A helper class, just used to store attributes.
Expand All @@ -37,8 +40,11 @@ def __init__(self, **kwds):
for k, v in kwds.iteritems():
setattr(self, k, v)

# TODO: This information should go somewhere else, I guess
STANDARD_IDENTIFIERS = set(['and', 'or', 'not', 'True', 'False'])


def analyse_identifiers(code, known=None):
def analyse_identifiers(code, specifiers, recursive=False):
'''
Analyses a code string (sequence of statements) to find all identifiers by type.
Expand All @@ -52,38 +58,59 @@ def analyse_identifiers(code, known=None):
Parameters
----------
code : str
The code string, a sequence of statements one per line.
known : list, set, None
A list or set of known (already created) variables.
specifiers : dict of `Specifier`, set of names
Specifiers for the model variables or a set of known names
recursive : bool, optional
Whether to recurse down into subexpressions (defaults to ``False``).
Returns
-------
newly_defined : set
A set of variables that are created by the code block.
used_known : set
A set of variables that are used and already known, a subset of the ``known`` parameter.
dependent : set
A set of variables which are used by the code block but not defined by it and not
previously known. If this set is nonempty it may indicate an error, for example.
A set of variables that are used and already known, a subset of the
``known`` parameter.
unknown : set
A set of variables which are used by the code block but not defined by
it and not previously known. Should correspond to variables in the
external namespace.
'''
if known is None:
known = set()
known = set(known)
# TODO: This information should go somewhere else, I guess
standard_identifiers = set(['and', 'or', 'not', 'True', 'False'])
known |= standard_identifiers
specifiers = dict((k, Value(k, 1, float64)) for k in known)
if isinstance(specifiers, collections.Mapping):
known = set(specifiers.keys())
else:
known = set(specifiers)
specifiers = dict((k, Value(k, 1, float64)) for k in known)

known |= STANDARD_IDENTIFIERS
stmts = make_statements(code, specifiers, float64)
defined = set(stmt.var for stmt in stmts if stmt.op==':=')
allids = set(get_identifiers(code))
if recursive:
if not isinstance(specifiers, collections.Mapping):
raise TypeError('Have to specify a specifiers dictionary.')
allids = get_identifiers_recursively(code, specifiers)
else:
allids = get_identifiers(code)
dependent = allids.difference(defined, known)
used_known = allids.intersection(known) - standard_identifiers
used_known = allids.intersection(known) - STANDARD_IDENTIFIERS

return defined, used_known, dependent


def get_identifiers_recursively(expr, specifiers):
'''
Gets all the identifiers in a code, recursing down into subexpressions.
'''
identifiers = get_identifiers(expr)
for name in set(identifiers):
if name in specifiers and isinstance(specifiers[name], Subexpression):
s_identifiers = get_identifiers_recursively(specifiers[name].expr,
specifiers)
identifiers |= s_identifiers
return identifiers


def make_statements(code, specifiers, dtype):
'''
Turn a series of abstract code statements into Statement objects, inferring
Expand Down Expand Up @@ -116,7 +143,7 @@ def make_statements(code, specifiers, dtype):
# for each line will give the variable being written to
line.write = var
# each line will give a set of variables which are read
line.read = set(get_identifiers(expr))
line.read = get_identifiers_recursively(expr, specifiers)

if DEBUG:
print 'PARSED STATEMENTS:'
Expand Down Expand Up @@ -153,6 +180,7 @@ def make_statements(code, specifiers, dtype):
# of the variables appearing in it has changed). All subexpressions start
# as invalid, and are invalidated whenever one of the variables appearing
# in the RHS changes value.
#subexpressions = get_all_subexpressions()
subexpressions = dict((name, val) for name, val in specifiers.items() if isinstance(val, Subexpression))
if DEBUG:
print 'SUBEXPRESSIONS:', subexpressions.keys()
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)
Loading

0 comments on commit 6d9dd74

Please sign in to comment.