Skip to content

Commit

Permalink
Merge pull request #32 from brian-team/linear_equations
Browse files Browse the repository at this point in the history
Linear equations support
  • Loading branch information
thesamovar committed Apr 17, 2013
2 parents 47b9100 + 9045e6e commit ef4a663
Show file tree
Hide file tree
Showing 11 changed files with 358 additions and 128 deletions.
1 change: 1 addition & 0 deletions brian2/codegen/languages/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ def compile(self):

def __call__(self, **kwds):
# update the values in the namespace
# TODO: Make use of the constant property here
for name, spec in self.specifiers.iteritems():
if isinstance(spec, Value):
value = spec.get_value()
Expand Down
44 changes: 44 additions & 0 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
from numpy import float64

from brian2.core.specifiers import Value, ArrayVariable, Subexpression, Index
from brian2.utils.stringtools import (deindent, strip_empty_lines, indent,
Expand All @@ -34,6 +35,49 @@ def __init__(self, **kwds):
for k, v in kwds.iteritems():
setattr(self, k, v)


def analyse_identifiers(code, known=None):
'''
Analyses a code string (sequence of statements) to find all identifiers by type.
In a given code block, some variable names (identifiers) must be given as inputs to the code
block, and some are created by the code block. For example, the line::
a = b+c
This could mean to create a new variable a from b and c, or it could mean modify the existing
value of a from b or c, depending on whether a was previously known.
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.
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.
'''
if known is None:
known = set()
specifiers = dict((k, Value(k, 1, float64)) for k in known)
stmts = make_statements(code, specifiers, float64)
defined = set(stmt.var for stmt in stmts if stmt.op==':=')
allids = set(get_identifiers(code))
dependent = allids.difference(defined, known)
used_known = allids.intersection(known)
return defined, used_known, dependent


def make_statements(code, specifiers, dtype):
'''
Turn a series of abstract code statements into Statement objects, inferring
Expand Down
2 changes: 1 addition & 1 deletion brian2/core/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def __iter__(self):
yield entry

def __contains__(self, key):
for entries in self.namespace.itervalues():
for entries in self.namespaces.itervalues():
if key in entries:
return True

Expand Down
53 changes: 40 additions & 13 deletions brian2/core/specifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from brian2.units.allunits import second

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

__all__ = ['Specifier',
'VariableSpecifier',
Expand Down Expand Up @@ -53,17 +54,27 @@ class VariableSpecifier(Specifier):
The name of the variable.
unit : `Unit`
The unit of the variable
scalar : bool, optional
Whether the variable is a scalar value (``True``) or vector-valued, i.e.
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``.
See Also
--------
Value
'''
def __init__(self, name, unit):
def __init__(self, name, unit, scalar=True, constant=False):
Specifier.__init__(self, name)

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

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

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

class Value(VariableSpecifier):
'''
Expand All @@ -82,9 +93,15 @@ class Value(VariableSpecifier):
The unit of the variable
dtype: `numpy.dtype`
The dtype used for storing the variable.
scalar : bool, optional
Whether the variable is a scalar value (``True``) or vector-valued, i.e.
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``.
'''
def __init__(self, name, unit, dtype):
VariableSpecifier.__init__(self, name, unit)
def __init__(self, name, unit, dtype, scalar=True, constant=False):
VariableSpecifier.__init__(self, name, unit, scalar, constant)
#: The dtype used for storing the variable.
self.dtype = dtype

Expand Down Expand Up @@ -120,16 +137,19 @@ class ReadOnlyValue(Value):
The dtype used for storing the variable.
value : reference to a value of type `dtype`
Reference to the variable's value
Raises
------
TypeError
When trying to use the `set_value` method.
'''
def __init__(self, name, unit, dtype, value):
Value.__init__(self, name, unit, dtype)
#: Reference to the variable's value
self.value = value

scalar = is_scalar_type(value)

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

def get_value(self):
return self.value
Expand All @@ -150,7 +170,7 @@ class StochasticVariable(VariableSpecifier):
'''
def __init__(self, name):
# The units of stochastic variables is fixed
VariableSpecifier.__init__(self, name, second**(-.5))
VariableSpecifier.__init__(self, name, second**(-.5), scalar=False)


class AttributeValue(ReadOnlyValue):
Expand All @@ -175,19 +195,24 @@ class AttributeValue(ReadOnlyValue):
attribute : str
The name of the attribute storing the variable's value. `attribute` has
to be an attribute of `obj`.
constant : bool, optional
Whether the attribute's value is constant during a run.
Raises
------
AttributeError
If `obj` does not have an attribute `attribute`.
'''
def __init__(self, name, unit, dtype, obj, attribute):
Value.__init__(self, name, unit, dtype)
def __init__(self, name, unit, dtype, obj, attribute, constant=False):
if not hasattr(obj, attribute):
raise AttributeError(('Object %r does not have an attribute %r, '
'providing the value for %r') %
(obj, attribute, name))

scalar = is_scalar_type(getattr(obj, attribute))

Value.__init__(self, name, unit, dtype, scalar, constant)
#: A reference to the object storing the variable's value
self.obj = obj
#: The name of the attribute storing the variable's value
Expand Down Expand Up @@ -227,9 +252,11 @@ class ArrayVariable(Value):
index : str
The index that will be used in the generated code when looping over the
variable.
constant : bool, optional
Whether the variable's value is constant during a run.
'''
def __init__(self, name, unit, dtype, array, index):
Value.__init__(self, name, unit, dtype)
def __init__(self, name, unit, dtype, array, index, constant=False):
Value.__init__(self, name, unit, dtype, scalar=False, constant=constant)
#: 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 @@ -269,7 +296,7 @@ class Subexpression(Value):
variables/functions used in the expression
'''
def __init__(self, name, unit, dtype, expr, specifiers, namespace):
Value.__init__(self, name, unit, dtype)
Value.__init__(self, name, unit, dtype, scalar=False)
#: The expression defining the static equation.
self.expr = expr.strip()
#: The identifiers used in the expression
Expand Down

0 comments on commit ef4a663

Please sign in to comment.