Skip to content

Commit

Permalink
Clarify the use of the "constant" attribute.
Browse files Browse the repository at this point in the history
If a Variable object is set to constant, this is now meant to have the intuitive meaning: The values stored do not change during a run (a code generation target could use this for optimizations). Only AttributeVariable objects that are non-constant are updated in the namespace at every timestep. There's a new attribute "constant_size" for DynamicArrayVariable, stating whether the size of the array can change during a run (e.g. in monitors), this is necessary to update the reference to the underlying array.
  • Loading branch information
Marcel Stimberg committed Oct 1, 2013
1 parent 68d5be3 commit a4828b8
Show file tree
Hide file tree
Showing 13 changed files with 109 additions and 81 deletions.
45 changes: 25 additions & 20 deletions brian2/codegen/runtime/numpy_rt/numpy_rt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
import numpy as np

from brian2.core.preferences import brian_prefs, BrianPreference
from brian2.core.variables import (Variable, Subexpression,
DynamicArrayVariable, ArrayVariable)
from brian2.core.variables import (DynamicArrayVariable, ArrayVariable,
AttributeVariable)

from ...codeobject import CodeObject

from ...templates import Templater
from ...languages.numpy_lang import NumpyLanguage
from ...targets import codegen_targets
Expand Down Expand Up @@ -51,27 +52,31 @@ def variables_to_namespace(self):
self.nonconstant_values = []

for name, var in self.variables.iteritems():
if isinstance(var, Subexpression):
try:
value = var.get_value()
except TypeError: # A dummy Variable without value or a Subexpression
continue

if not var.constant:
self.namespace[name] = value

if isinstance(var, ArrayVariable):
self.namespace[var.arrayname] = var.get_value()

if isinstance(var, DynamicArrayVariable):
self.namespace[var.name+'_object'] = var.get_object()

# There are two kinds of objects that we have to inject into the
# namespace with their current value at each time step:
# * non-constant AttributeValue (this might be removed since it only
# applies to "t" currently)
# * Dynamic arrays that change in size during runs (i.e. not
# synapses but e.g. the structures used in monitors)
if isinstance(var, AttributeVariable) and not var.constant:
self.nonconstant_values.append((name, var.get_value))
if isinstance(var, ArrayVariable):
self.nonconstant_values.append((var.arrayname,
var.get_value))
if isinstance(var, DynamicArrayVariable):
self.nonconstant_values.append((name+'_object',
var.get_object))
else:
try:
value = var.get_value()
except TypeError: # A dummy Variable without value
continue
if isinstance(var, ArrayVariable):
self.namespace[var.arrayname] = var.get_value()
self.namespace[name] = value
if isinstance(var, DynamicArrayVariable):
self.namespace[var.name+'_object'] = var.get_object()
elif (isinstance(var, DynamicArrayVariable) and
not var.constant_size):
self.nonconstant_values.append((var.arrayname,
var.get_value))

def update_namespace(self):
# update the values of the non-constant values in the namespace
Expand Down
2 changes: 1 addition & 1 deletion brian2/codegen/runtime/numpy_rt/templates/ratemonitor.py_
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# { USES_VARIABLES _rate, _t, _spikespace, _num_source_neurons, t, dt }
_spikes = _spikespace[:_spikespace[-1]]
_new_len = len(_t) + 1
_new_len = len(_t_object) + 1
_t_object.resize(_new_len)
_rate_object.resize(_new_len)
# Note that _t refers directly to the underlying array which might have changed
Expand Down
2 changes: 1 addition & 1 deletion brian2/codegen/runtime/numpy_rt/templates/spikemonitor.py_
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ _spikes -= _source_start
_n_spikes = len(_spikes)
if _n_spikes > 0:

_curlen = len(_t)
_curlen = len(_t_object)
_newlen = _curlen + _n_spikes
_t_object.resize(_newlen)
_i_object.resize(_newlen)
Expand Down
2 changes: 1 addition & 1 deletion brian2/codegen/runtime/numpy_rt/templates/statemonitor.py_
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# USES_VARIABLES { _t, _clock_t, _indices }

# Resize dynamic arrays
_new_len = len(_t) + 1
_new_len = len(_t_object) + 1
_t_object.resize(_new_len)

{% for _varname in _variable_names %}
Expand Down
58 changes: 31 additions & 27 deletions brian2/codegen/runtime/weave_rt/weave_rt.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
# No weave for Python 3
weave = None

from brian2.core.variables import (Variable, Subexpression,
DynamicArrayVariable, ArrayVariable)
from brian2.core.variables import (DynamicArrayVariable, ArrayVariable,
AttributeVariable)
from brian2.core.preferences import brian_prefs, BrianPreference
from brian2.core.functions import DEFAULT_FUNCTIONS, FunctionImplementation

Expand Down Expand Up @@ -86,32 +86,36 @@ def variables_to_namespace(self):
self.nonconstant_values = []

for name, var in self.variables.iteritems():
if isinstance(var, Variable) and not isinstance(var, Subexpression):
if not var.constant:
if isinstance(var, ArrayVariable):
self.nonconstant_values.append((var.arrayname, var.get_value))
self.nonconstant_values.append((name, var.get_value))
if not var.scalar:
self.nonconstant_values.append(('_num' + name,
var.get_len))
if isinstance(var, DynamicArrayVariable):
self.nonconstant_values.append((name+'_object',
var.get_object))
else:
try:
value = var.get_value()
except TypeError: # A dummy Variable without value
continue
if isinstance(var, ArrayVariable):
self.namespace[var.arrayname] = value
self.namespace[name] = value
# if it is a type that has a length, add a variable called
# '_num'+name with its length
if not var.scalar:
self.namespace['_num' + name] = var.get_len()
if isinstance(value, DynamicArrayVariable):
self.namespace[name+'_object'] = value.get_object()

try:
value = var.get_value()
except TypeError: # A dummy Variable without value or a Subexpression
continue

self.namespace[name] = value

if isinstance(var, ArrayVariable):
self.namespace[var.arrayname] = value
self.namespace['_num'+name] = var.get_len()

if isinstance(var, DynamicArrayVariable):
self.namespace[var.name+'_object'] = var.get_object()

# There are two kinds of objects that we have to inject into the
# namespace with their current value at each time step:
# * non-constant AttributeValue (this might be removed since it only
# applies to "t" currently)
# * Dynamic arrays that change in size during runs (i.e. not
# synapses but e.g. the structures used in monitors)
if isinstance(var, AttributeVariable) and not var.constant:
self.nonconstant_values.append((name, var.get_value))
if not var.scalar:
self.nonconstant_values.append(('_num'+name, var.get_len))
elif (isinstance(var, DynamicArrayVariable) and
not var.constant_size):
self.nonconstant_values.append((var.arrayname,
var.get_value))
self.nonconstant_values.append(('_num'+name, var.get_len))

def update_namespace(self):
# update the values of the non-constant values in the namespace
Expand Down
17 changes: 16 additions & 1 deletion brian2/core/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,21 @@ class DynamicArrayVariable(ArrayVariable):
An object providing information about a model variable stored in a dynamic
array (used in `Synapses`).
'''

def __init__(self, name, unit, value, group_name=None,
constant=False, constant_size=True,
scalar=False, is_bool=False):
if constant and not constant_size:
raise ValueError('A variable cannot be constant and change in size')
self.constant_size = constant_size
super(DynamicArrayVariable, self).__init__(name=name,
unit=unit,
value=value,
group_name=group_name,
constant=constant,
scalar=scalar,
is_bool=is_bool)

def get_value(self):
# The actual numpy array is accesible via DynamicArray1D.data
return self.value.data
Expand Down Expand Up @@ -493,7 +508,7 @@ def __init__(self, unit, dtype, expr, is_bool=False):
self.identifiers = get_identifiers(expr)

def get_value(self):
raise AssertionError('get_value should never be called for a Subexpression')
raise TypeError('get_value should never be called for a Subexpression')

def __contains__(self, var):
return var in self.identifiers
Expand Down
6 changes: 1 addition & 5 deletions brian2/devices/cpp_standalone/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def array(self, owner, name, size, unit, dtype=None, constant=False,
constant=constant, is_bool=is_bool)

def dynamic_array_1d(self, owner, name, size, unit, dtype=None,
constant=False, is_bool=False):
constant=False, constant_size=True, is_bool=False):
if is_bool:
dtype = numpy.bool
elif dtype is None:
Expand All @@ -156,10 +156,6 @@ def dynamic_array_1d(self, owner, name, size, unit, dtype=None,
dtype=dtype,
group_name=owner.name,
constant=constant, is_bool=is_bool)

def dynamic_array(self, owner, name, size, unit, dtype=None,
constant=False, is_bool=False):
raise NotImplementedError()

def code_object_class(self, codeobj_class=None):
if codeobj_class is not None:
Expand Down
16 changes: 10 additions & 6 deletions brian2/devices/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ def array(self, owner, name, size, unit, dtype=None, constant=False,
raise NotImplementedError()

def dynamic_array_1d(self, owner, name, size, unit, dtype=None,
constant=False, is_bool=False):
constant=False, constant_size=True, is_bool=False):
raise NotImplementedError()

def dynamic_array(self, owner, name, size, unit, dtype=None,
constant=False, is_bool=False):
constant=False, constant_size=True, is_bool=False):
raise NotImplementedError()

def code_object_class(self, codeobj_class=None):
Expand Down Expand Up @@ -87,24 +87,28 @@ def array(self, owner, name, size, unit, dtype=None,
constant=constant, is_bool=is_bool)

def dynamic_array_1d(self, owner, name, size, unit, dtype=None,
constant=False, is_bool=False):
constant=False,constant_size=True, is_bool=False):
if is_bool:
dtype = np.bool
if dtype is None:
dtype = brian_prefs['core.default_scalar_dtype']
array = DynamicArray1D(size, dtype=dtype)
return DynamicArrayVariable(name, unit, array, group_name=owner.name,
constant=constant, is_bool=is_bool)
constant=constant,
constant_size=constant_size,
is_bool=is_bool)

def dynamic_array(self, owner, name, size, unit, dtype=None,
constant=False, is_bool=False):
constant=False, constant_size=True, is_bool=False):
if is_bool:
dtype = np.bool
if dtype is None:
dtype = brian_prefs['core.default_scalar_dtype']
array = DynamicArray(size, dtype=dtype)
return DynamicArrayVariable(name, unit, array, group_name=owner.name,
constant=constant, is_bool=is_bool)
constant=constant,
constant_size=constant_size,
is_bool=is_bool)


runtime_device = RuntimeDevice()
Expand Down
6 changes: 4 additions & 2 deletions brian2/groups/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
logger = get_logger(__name__)


class GroupItemMapping(Variable):
class GroupItemMapping(ArrayVariable):

def __init__(self, N, offset, group):
self.N = N
Expand All @@ -33,7 +33,9 @@ def __init__(self, N, offset, group):
self.variables = {'i': ArrayVariable('i',
Unit(1),
self._indices - self.offset)}
Variable.__init__(self, Unit(1), value=self, constant=True)
ArrayVariable.__init__(self, '_idx', Unit(1), value=self,
group_name=group.name,
constant=True)

def __len__(self):
return self.N
Expand Down
3 changes: 1 addition & 2 deletions brian2/groups/poissongroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ def __init__(self, N, rates, clock=None, name='poissongroup*',

self.variables = Group._create_variables(self)
self.variables.update({'rates': ArrayVariable('rates', Hz, self._rates,
group_name=self.name,
constant=True),
group_name=self.name),
'_spikespace': get_device().array(self,
'_spikespace',
N+1,
Expand Down
6 changes: 4 additions & 2 deletions brian2/monitors/ratemonitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ def __init__(self, source, when=None, name='ratemonitor*',
'dt': AttributeVariable(second, self.clock,
'dt_', constant=True),
'_spikespace': self.source.variables['_spikespace'],
'_rate': dev.dynamic_array_1d(self, '_rate', 0, 1),
'_rate': dev.dynamic_array_1d(self, '_rate', 0, 1,
constant_size=False),
'_t': dev.dynamic_array_1d(self, '_t', 0, second,
dtype=getattr(self.clock.t, 'dtype',
np.dtype(type(self.clock.t)))),
np.dtype(type(self.clock.t))),
constant_size=False),
'_num_source_neurons': Variable(Unit(1),
len(self.source))}

Expand Down
6 changes: 4 additions & 2 deletions brian2/monitors/spikemonitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@ def __init__(self, source, record=True, when=None, name='spikemonitor*',
self.variables = {'t': AttributeVariable(second, self.clock, 't_'),
'_spikespace': self.source.variables['_spikespace'],
'_i': device.dynamic_array_1d(self, '_i', 0, Unit(1),
dtype=np.int32),
dtype=np.int32,
constant_size=False),
'_t': device.dynamic_array_1d(self, '_t', 0,
Unit(1)),
Unit(1),
constant_size=False),
'_count': device.array(self, '_count',
len(self.source),
Unit(1),
Expand Down
21 changes: 10 additions & 11 deletions brian2/synapses/synapses.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ def _synapse_numbers(pre_neurons, post_neurons):
return synapse_numbers


class SynapticItemMapping(Variable):
class SynapticItemMapping(ArrayVariable):
'''
Convenience object to store the synaptic indices.
Expand All @@ -339,7 +339,8 @@ class SynapticItemMapping(Variable):
Reference to the main `Synapses object`
'''
def __init__(self, synapses):
Variable.__init__(self, Unit(1), value=self, constant=True)
ArrayVariable.__init__(self, '_idx', Unit(1), value=self,
group_name=synapses.name, constant=True)
self.source = synapses.source
self.target = synapses.target
self.synapses = weakref.proxy(synapses)
Expand Down Expand Up @@ -723,15 +724,11 @@ def __init__(self, source, target=None, model=None, pre=None, post=None,
'of seconds'))
updater = getattr(self, pathway)
self.item_mapping.unregister_variable(updater._delays)
del updater._delays
# For simplicity, store the delay as a one-element array
# so that for example updater._delays[:] works.
updater._delays = np.array([float(pathway_delay)])
variable = ArrayVariable('delay', second, updater._delays,
group_name=self.name, scalar=True)
updater.variables['delay'] = variable
if pathway == 'pre':
self.variables['delay'] = variable
updater._delays.resize(1)
updater._delays[0] = float(pathway_delay)
updater._delays.scalar = True

#: Performs numerical integration step
self.state_updater = StateUpdater(self, method)
Expand Down Expand Up @@ -879,9 +876,11 @@ def _create_variables(self, dtype=None):
'_target_offset': Variable(Unit(1), self.target.offset,
constant=True),
'_synaptic_pre': dev.dynamic_array_1d(self, '_synaptic_pre',
0, Unit(1), dtype=np.int32),
0, Unit(1), dtype=np.int32,
constant_size=True),
'_synaptic_post': dev.dynamic_array_1d(self, '_synaptic_post',
0, Unit(1), dtype=np.int32),
0, Unit(1), dtype=np.int32,
constant_size=True),
# We don't need "proper" specifier for these -- they go
# back to Python code currently
'_pre_synaptic': Variable(Unit(1), self.pre_synaptic),
Expand Down

0 comments on commit a4828b8

Please sign in to comment.