From e3f185426a29bf5982e6024de5d47ede0305f2ce Mon Sep 17 00:00:00 2001 From: thesamovar Date: Tue, 20 Aug 2013 18:53:55 -0400 Subject: [PATCH 1/6] Made CodeObject Nameable and gave better names to created CodeObjects --- brian2/codegen/_prefs.py | 2 + brian2/codegen/codeobject.py | 11 +++-- brian2/codegen/runtime/numpy_rt/numpy_rt.py | 4 +- brian2/codegen/runtime/weave_rt/weave_rt.py | 4 +- brian2/core/names.py | 52 +++++++++++---------- brian2/groups/group.py | 6 +-- brian2/monitors/ratemonitor.py | 2 +- brian2/monitors/spikemonitor.py | 2 +- brian2/monitors/statemonitor.py | 2 +- brian2/tests/__init__.py | 4 ++ 10 files changed, 52 insertions(+), 37 deletions(-) diff --git a/brian2/codegen/_prefs.py b/brian2/codegen/_prefs.py index d0f37c0d8..b14974ad7 100644 --- a/brian2/codegen/_prefs.py +++ b/brian2/codegen/_prefs.py @@ -1,3 +1,4 @@ +from .codeobject import CodeObject from brian2.core.preferences import brian_prefs, BrianPreference # Preferences @@ -19,5 +20,6 @@ Or it can be a ``CodeObject`` class. ''', + validator=lambda target: isinstance(target, str) or issubclass(target, CodeObject), ), ) diff --git a/brian2/codegen/codeobject.py b/brian2/codegen/codeobject.py index 78fcc69c6..59bf1818b 100644 --- a/brian2/codegen/codeobject.py +++ b/brian2/codegen/codeobject.py @@ -5,6 +5,7 @@ StochasticVariable) from .functions.base import Function from brian2.core.preferences import brian_prefs +from brian2.core.names import Nameable, find_name from brian2.utils.logger import get_logger from .translation import translate from .runtime.targets import runtime_targets @@ -79,11 +80,14 @@ def create_codeobject(name, abstract_code, namespace, variables, template_name, iterate_all=iterate_all) template_kwds.update(kwds) logger.debug(name + " inner code:\n" + str(innercode)) + + name = find_name(name) + code = template(innercode, **template_kwds) logger.debug(name + " code:\n" + str(code)) variables.update(indices) - codeobj = codeobj_class(code, namespace, variables) + codeobj = codeobj_class(code, namespace, variables, name=name) codeobj.compile() return codeobj @@ -97,7 +101,7 @@ def get_codeobject_template(name, codeobj_class=None): return getattr(codeobj_class.templater, name) -class CodeObject(object): +class CodeObject(Nameable): ''' Executable code object. @@ -114,7 +118,8 @@ class CodeObject(object): #: The `Language` used by this `CodeObject` language = None - def __init__(self, code, namespace, variables): + def __init__(self, code, namespace, variables, name='codeobject*'): + Nameable.__init__(self, name=name) self.code = code self.compile_methods = self.get_compile_methods(variables) self.namespace = namespace diff --git a/brian2/codegen/runtime/numpy_rt/numpy_rt.py b/brian2/codegen/runtime/numpy_rt/numpy_rt.py index 998713652..b597d7354 100644 --- a/brian2/codegen/runtime/numpy_rt/numpy_rt.py +++ b/brian2/codegen/runtime/numpy_rt/numpy_rt.py @@ -19,10 +19,10 @@ class NumpyCodeObject(CodeObject): 'templates')) language = NumpyLanguage() - def __init__(self, code, namespace, variables): + def __init__(self, code, namespace, variables, name='numpy_code_object*'): # TODO: This should maybe go somewhere else namespace['logical_not'] = np.logical_not - CodeObject.__init__(self, code, namespace, variables) + CodeObject.__init__(self, code, namespace, variables, name=name) def compile(self): super(NumpyCodeObject, self).compile() diff --git a/brian2/codegen/runtime/weave_rt/weave_rt.py b/brian2/codegen/runtime/weave_rt/weave_rt.py index 8203b8f66..f6aaa4300 100644 --- a/brian2/codegen/runtime/weave_rt/weave_rt.py +++ b/brian2/codegen/runtime/weave_rt/weave_rt.py @@ -66,8 +66,8 @@ class WeaveCodeObject(CodeObject): 'templates')) language = CPPLanguage(c_data_type=weave_data_type) - def __init__(self, code, namespace, variables): - super(WeaveCodeObject, self).__init__(code, namespace, variables) + def __init__(self, code, namespace, variables, name='weave_code_object*'): + super(WeaveCodeObject, self).__init__(code, namespace, variables, name=name) self.compiler = brian_prefs['codegen.runtime.weave.compiler'] self.extra_compile_args = brian_prefs['codegen.runtime.weave.extra_compile_args'] diff --git a/brian2/core/names.py b/brian2/core/names.py index 376d22de2..65fad6fba 100644 --- a/brian2/core/names.py +++ b/brian2/core/names.py @@ -1,11 +1,35 @@ from brian2.utils.logger import get_logger from brian2.core.tracking import Trackable +import re __all__ = ['Nameable'] logger = get_logger(__name__) +def find_name(name): + if name.endswith('*'): + name = name[:-1] + wildcard = True + else: + wildcard = False + instances = set(Nameable.__instances__()) + allnames = set(obj().name for obj in instances + if hasattr(obj(), 'name')) + + # Try the name without any additions first: + if name not in allnames: + return name + elif not wildcard: + raise ValueError("An object with name "+name+" is already defined.") + + # Name is already taken, try _1, _2, etc. + i = 1 + while name+'_'+str(i) in allnames: + i += 1 + return name+'_'+str(i) + + class Nameable(Trackable): ''' Base class to find a unique name for an object @@ -28,35 +52,15 @@ class Nameable(Trackable): ------ ValueError If the name is already taken. - ''' - def _find_name(self, name): - if name.endswith('*'): - name = name[:-1] - wildcard = True - else: - wildcard = False - instances = set(Nameable.__instances__()) - allnames = set(obj().name for obj in instances - if hasattr(obj(), 'name')) - - # Try the name without any additions first: - if name not in allnames: - return name - elif not wildcard: - raise ValueError("An object with name "+name+" is already defined.") - - # Name is already taken, try _1, _2, etc. - i = 1 - while name+'_'+str(i) in allnames: - i += 1 - return name+'_'+str(i) - + ''' def __init__(self, name): if not isinstance(name, basestring): raise TypeError(('"name" argument has to be a string, is type ' '{type} instead').format(type=repr(type(name)))) + if not re.match(r"[_A-Za-z][_a-zA-Z0-9]*\*?$", name): + raise ValueError("Name %s not valid variable name" % name) - self._name = self._find_name(name) + self._name = find_name(name) logger.debug("Created object of class "+self.__class__.__name__+" with name "+self._name) name = property(fget=lambda self:self._name, diff --git a/brian2/groups/group.py b/brian2/groups/group.py index 0cb9a443f..747ba49cc 100644 --- a/brian2/groups/group.py +++ b/brian2/groups/group.py @@ -351,9 +351,9 @@ def create_runner_codeobj(group, code, template_name, indices=None, if name is None: if group is not None: - name = group.name + '_codeobject*' + name = '%s_%s_codeobject*' % (group.name, template_name) else: - name = '_codeobject*' + name = '%s_codeobject*' % template_name if indices is None: indices = group.indices @@ -446,7 +446,7 @@ def pre_run(self, namespace): additional_variables, namespace) self.codeobj = create_runner_codeobj(self.group, self.abstract_code, self.template, - name=self.name, + name=self.name+'_codeobject*', check_units=self.check_units, additional_variables=additional_variables, additional_namespace=namespace, diff --git a/brian2/monitors/ratemonitor.py b/brian2/monitors/ratemonitor.py index bef76cc8a..c4ae45ec6 100644 --- a/brian2/monitors/ratemonitor.py +++ b/brian2/monitors/ratemonitor.py @@ -75,7 +75,7 @@ def reinit(self): np.dtype(type(self.clock.t)))) def pre_run(self, namespace): - self.codeobj = create_codeobject(self.name, + self.codeobj = create_codeobject(self.name+'_codeobject*', '', # No model-specific code {}, # no namespace self.variables, diff --git a/brian2/monitors/spikemonitor.py b/brian2/monitors/spikemonitor.py index b2f9dd081..076b6848a 100644 --- a/brian2/monitors/spikemonitor.py +++ b/brian2/monitors/spikemonitor.py @@ -85,7 +85,7 @@ def reinit(self): self.count = np.zeros(len(self.source), dtype=int) def pre_run(self, namespace): - self.codeobj = create_codeobject(self.name, + self.codeobj = create_codeobject(self.name+'_codeobject*', '', # No model-specific code {}, # no namespace self.variables, diff --git a/brian2/monitors/statemonitor.py b/brian2/monitors/statemonitor.py index aa0730323..ae2a093ca 100644 --- a/brian2/monitors/statemonitor.py +++ b/brian2/monitors/statemonitor.py @@ -211,7 +211,7 @@ def pre_run(self, namespace): self.codeobj = create_runner_codeobj(self.source, code, 'statemonitor', - name=self.name, + name=self.name+'_codeobject*', additional_variables=self.variables, additional_namespace=namespace, template_kwds={'_variable_names': diff --git a/brian2/tests/__init__.py b/brian2/tests/__init__.py index ed1877ea8..ae01f10bf 100644 --- a/brian2/tests/__init__.py +++ b/brian2/tests/__init__.py @@ -9,3 +9,7 @@ def run(): dirname = os.path.join(os.path.dirname(__file__), '..') return nose.run(argv=['', dirname, '--with-doctest']) + +if __name__=='__main__': + run() + \ No newline at end of file From 7ab5770f662796de13bd0139bcdd07137402db95 Mon Sep 17 00:00:00 2001 From: thesamovar Date: Tue, 20 Aug 2013 22:01:51 -0400 Subject: [PATCH 2/6] Added group_variable_set template because reset is hardcoded to _spikespace now, fixed reset to not use _spikes --- .../numpy_rt/templates/group_variable_set.py_ | 6 +++ .../runtime/numpy_rt/templates/reset.py_ | 4 +- .../weave_rt/templates/group_variable_set.cpp | 40 +++++++++++++++++++ .../runtime/weave_rt/templates/reset.cpp | 5 ++- brian2/groups/group.py | 10 ++--- 5 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 brian2/codegen/runtime/numpy_rt/templates/group_variable_set.py_ create mode 100644 brian2/codegen/runtime/weave_rt/templates/group_variable_set.cpp diff --git a/brian2/codegen/runtime/numpy_rt/templates/group_variable_set.py_ b/brian2/codegen/runtime/numpy_rt/templates/group_variable_set.py_ new file mode 100644 index 000000000..25f4b4edb --- /dev/null +++ b/brian2/codegen/runtime/numpy_rt/templates/group_variable_set.py_ @@ -0,0 +1,6 @@ +# USES_VARIABLES { _group_idx } +_idx = _group_idx +_vectorisation_idx = _idx +{% for line in code_lines %} +{{line}} +{% endfor %} diff --git a/brian2/codegen/runtime/numpy_rt/templates/reset.py_ b/brian2/codegen/runtime/numpy_rt/templates/reset.py_ index 6436dafc7..cb801c215 100644 --- a/brian2/codegen/runtime/numpy_rt/templates/reset.py_ +++ b/brian2/codegen/runtime/numpy_rt/templates/reset.py_ @@ -1,5 +1,5 @@ -# USES_VARIABLES { _spikes } -_idx = _spikes +# USES_VARIABLES { _spikespace } +_idx = _spikespace[:_spikespace[-1]] _vectorisation_idx = _idx {% for line in code_lines %} {{line}} diff --git a/brian2/codegen/runtime/weave_rt/templates/group_variable_set.cpp b/brian2/codegen/runtime/weave_rt/templates/group_variable_set.cpp new file mode 100644 index 000000000..c0c425c28 --- /dev/null +++ b/brian2/codegen/runtime/weave_rt/templates/group_variable_set.cpp @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////// +//// MAIN CODE ///////////////////////////////////////////////////////////// + +{% macro main() %} + // USES_VARIABLES { _group_idx } + + ////// HANDLE DENORMALS /// + {% for line in denormals_code_lines %} + {{line}} + {% endfor %} + + ////// HASH DEFINES /////// + {% for line in hashdefine_lines %} + {{line}} + {% endfor %} + + ///// POINTERS //////////// + {% for line in pointers_lines %} + {{line}} + {% endfor %} + + //// MAIN CODE //////////// + for(int _idx_group_idx=0; _idx_group_idx<_num_group_idx; _idx_group_idx++) + { + const int _idx = _group_idx[_idx_group_idx]; + const int _vectorisation_idx = _idx; + {% for line in code_lines %} + {{line}} + {% endfor %} + } +{% endmacro %} + +//////////////////////////////////////////////////////////////////////////// +//// SUPPORT CODE ////////////////////////////////////////////////////////// + +{% macro support_code() %} + {% for line in support_code_lines %} + {{line}} + {% endfor %} +{% endmacro %} diff --git a/brian2/codegen/runtime/weave_rt/templates/reset.cpp b/brian2/codegen/runtime/weave_rt/templates/reset.cpp index d413b53bb..934b8f6ed 100644 --- a/brian2/codegen/runtime/weave_rt/templates/reset.cpp +++ b/brian2/codegen/runtime/weave_rt/templates/reset.cpp @@ -2,7 +2,7 @@ //// MAIN CODE ///////////////////////////////////////////////////////////// {% macro main() %} - // USES_VARIABLES { _spikes } + // USES_VARIABLES { _spikespace } ////// HANDLE DENORMALS /// {% for line in denormals_code_lines %} @@ -20,9 +20,10 @@ {% endfor %} //// MAIN CODE //////////// + const int _num_spikes = _spikespace[_num_spikespace-1]; for(int _index_spikes=0; _index_spikes<_num_spikes; _index_spikes++) { - const int _idx = _spikes[_index_spikes]; + const int _idx = _spikespace[_index_spikes]; const int _vectorisation_idx = _idx; {% for line in code_lines %} {{line}} diff --git a/brian2/groups/group.py b/brian2/groups/group.py index 0cb9a443f..941992863 100644 --- a/brian2/groups/group.py +++ b/brian2/groups/group.py @@ -203,15 +203,15 @@ def _set_with_code(self, variable, group_indices, code, # TODO: Find a name that makes sense for reset and variable setting # with code additional_variables = self.item_mapping.variables - additional_variables['_spikes'] = ArrayVariable('_spikes', - Unit(1), - value=group_indices.astype(np.int32), - group_name=self.name) + additional_variables['_group_idx'] = ArrayVariable('_group_idx', + Unit(1), + value=group_indices.astype(np.int32), + group_name=self.name) # TODO: Have an additional argument to avoid going through the index # array for situations where iterate_all could be used codeobj = create_runner_codeobj(self, abstract_code, - 'reset', + 'group_variable_set', additional_variables=additional_variables, additional_namespace=additional_namespace, check_units=check_units) From 35867edbfc6a3bf219c9391f1014612c6b7b7025 Mon Sep 17 00:00:00 2001 From: thesamovar Date: Tue, 20 Aug 2013 22:09:15 -0400 Subject: [PATCH 3/6] Fixed SpikeMonitor to use _spikespace --- .../runtime/numpy_rt/templates/spikemonitor.py_ | 4 ++-- .../runtime/weave_rt/templates/spikemonitor.cpp | 10 +++++----- brian2/monitors/spikemonitor.py | 3 +-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/brian2/codegen/runtime/numpy_rt/templates/spikemonitor.py_ b/brian2/codegen/runtime/numpy_rt/templates/spikemonitor.py_ index 4ba82b17c..b9144cae0 100644 --- a/brian2/codegen/runtime/numpy_rt/templates/spikemonitor.py_ +++ b/brian2/codegen/runtime/numpy_rt/templates/spikemonitor.py_ @@ -1,7 +1,7 @@ -# { USES_VARIABLES _i, _t, _spikes, _count, t, _source_start, _source_end} +# { USES_VARIABLES _i, _t, _spikespace, _count, t, _source_start, _source_end} import numpy as np +_spikes = _spikespace[:_spikespace[-1]] # Take subgroups into account -_spikes = np.asarray(_spikes) _spikes = _spikes[(_spikes >= _source_start) & (_spikes < _source_end)] _spikes -= _source_start _n_spikes = len(_spikes) diff --git a/brian2/codegen/runtime/weave_rt/templates/spikemonitor.cpp b/brian2/codegen/runtime/weave_rt/templates/spikemonitor.cpp index 9d8fac46d..752d7855d 100644 --- a/brian2/codegen/runtime/weave_rt/templates/spikemonitor.cpp +++ b/brian2/codegen/runtime/weave_rt/templates/spikemonitor.cpp @@ -1,8 +1,8 @@ {% macro main() %} - // USES_VARIABLES { _t, _i, t, _spikes, _count, + // USES_VARIABLES { _t, _i, t, _spikespace, _count, // _source_start, _source_end} - + int _num_spikes = _spikespace[_num_spikespace-1]; if (_num_spikes > 0) { // For subgroups, we do not want to record all spikes @@ -12,7 +12,7 @@ int _end_idx = - 1; for(int _i=0; _i<_num_spikes; _i++) { - const int _idx = _spikes[_i]; + const int _idx = _spikespace[_i]; if (_idx >= _source_start) { _start_idx = _i; break; @@ -20,7 +20,7 @@ } for(int _i=_start_idx; _i<_num_spikes; _i++) { - const int _idx = _spikes[_i]; + const int _idx = _spikespace[_i]; if (_idx >= _source_end) { _end_idx = _i; break; @@ -45,7 +45,7 @@ // Copy the values across for(int _i=_start_idx; _i<_end_idx; _i++) { - const int _idx = _spikes[_i]; + const int _idx = _spikespace[_i]; _t_data[_curlen + _i - _start_idx] = t; _i_data[_curlen + _i - _start_idx] = _idx - _source_start; _count[_idx - _source_start]++; diff --git a/brian2/monitors/spikemonitor.py b/brian2/monitors/spikemonitor.py index b2f9dd081..90660dce4 100644 --- a/brian2/monitors/spikemonitor.py +++ b/brian2/monitors/spikemonitor.py @@ -58,8 +58,7 @@ def __init__(self, source, record=True, when=None, name='spikemonitor*', end = getattr(self.source, 'end', len(self.source)) self.variables = {'t': AttributeVariable(second, self.clock, 't'), - '_spikes': AttributeVariable(Unit(1), self.source, - 'spikes'), + '_spikespace': self.source.variables['_spikespace'], # The template needs to have access to the # DynamicArray here, having access to the underlying # array is not enough since we want to do the resize From 428dd99259f87fe7b23e039e507bb39f5fbd71db Mon Sep 17 00:00:00 2001 From: thesamovar Date: Tue, 20 Aug 2013 22:13:25 -0400 Subject: [PATCH 4/6] Fixed RateMonitor to use _spikespace --- brian2/codegen/runtime/numpy_rt/templates/ratemonitor.py_ | 4 ++-- brian2/codegen/runtime/weave_rt/templates/ratemonitor.cpp | 3 ++- brian2/monitors/ratemonitor.py | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/brian2/codegen/runtime/numpy_rt/templates/ratemonitor.py_ b/brian2/codegen/runtime/numpy_rt/templates/ratemonitor.py_ index ddd5606d4..cd0bb7440 100644 --- a/brian2/codegen/runtime/numpy_rt/templates/ratemonitor.py_ +++ b/brian2/codegen/runtime/numpy_rt/templates/ratemonitor.py_ @@ -1,5 +1,5 @@ -# { USES_VARIABLES _rate, _t, _spikes, _num_source_neurons, t, dt } - +# { USES_VARIABLES _rate, _t, _spikespace, _num_source_neurons, t, dt } +_spikes = _spikespace[:_spikespace[-1]] _new_len = len(_t) + 1 _t.resize(_new_len) _rate.resize(_new_len) diff --git a/brian2/codegen/runtime/weave_rt/templates/ratemonitor.cpp b/brian2/codegen/runtime/weave_rt/templates/ratemonitor.cpp index 9d43f00c1..ac9749117 100644 --- a/brian2/codegen/runtime/weave_rt/templates/ratemonitor.cpp +++ b/brian2/codegen/runtime/weave_rt/templates/ratemonitor.cpp @@ -1,6 +1,7 @@ {% macro main() %} - // USES_VARIABLES { _t, _rate, t, dt, _spikes } + // USES_VARIABLES { _t, _rate, t, dt, _spikespace } + const int _num_spikes = _spikespace[_num_spikespace-1]; // Calculate the new length for the arrays const npy_int _new_len = (npy_int)(_t.attr("shape")[0]) + 1; diff --git a/brian2/monitors/ratemonitor.py b/brian2/monitors/ratemonitor.py index bef76cc8a..1ee3b3bab 100644 --- a/brian2/monitors/ratemonitor.py +++ b/brian2/monitors/ratemonitor.py @@ -53,8 +53,7 @@ def __init__(self, source, when=None, name='ratemonitor*', self.variables = {'t': AttributeVariable(second, self.clock, 't'), 'dt': AttributeVariable(second, self.clock, 'dt', constant=True), - '_spikes': AttributeVariable(Unit(1), - self.source, 'spikes'), + '_spikespace': self.source.variables['_spikespace'], # The template needs to have access to the # DynamicArray here, having access to the underlying # array is not enough since we want to do the resize From 569253d3861248b7d79ad772b7c1817867764579 Mon Sep 17 00:00:00 2001 From: thesamovar Date: Tue, 20 Aug 2013 22:15:20 -0400 Subject: [PATCH 5/6] Removed all reference to _spikes Variable --- brian2/groups/neurongroup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/brian2/groups/neurongroup.py b/brian2/groups/neurongroup.py index 9005fdd40..843750c85 100644 --- a/brian2/groups/neurongroup.py +++ b/brian2/groups/neurongroup.py @@ -390,8 +390,6 @@ def _create_variables(self): s.update({'_spikespace': ArrayVariable('_spikespace', Unit(1), self._spikespace, group_name=self.name)}) - s.update({'_spikes': AttributeVariable(Unit(1), self, - 'spikes', constant=False)}) for eq in self.equations.itervalues(): if eq.type in (DIFFERENTIAL_EQUATION, PARAMETER): From a1cf422463a53924aad9880551526d849399605d Mon Sep 17 00:00:00 2001 From: thesamovar Date: Wed, 21 Aug 2013 01:06:41 -0400 Subject: [PATCH 6/6] Added some notes on passing variables to templates --- ...otes_on_passing_variables_to_templates.txt | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 dev/notes_on_passing_variables_to_templates.txt diff --git a/dev/notes_on_passing_variables_to_templates.txt b/dev/notes_on_passing_variables_to_templates.txt new file mode 100644 index 000000000..b4e8b3747 --- /dev/null +++ b/dev/notes_on_passing_variables_to_templates.txt @@ -0,0 +1,36 @@ +Template generation keywords: + +code block +multiple code blocks + +# Handling lumped variables using the standard mechanisms is not +# possible, we therefore also directly give the names of the arrays +# to the template. The dummy statement in the second line only serves +# the purpose of including the variable in the namespace +_target_var_array name (lumped updater) + +============= StateMonitor =========================== + +record variable names (statemonitor) + +group.variables ['_spikespace', 't', 'v', 'dt', 'lastspike', 'not_refractory'] +additional_variables { + '_t': >, dtype=, scalar=False, constant=False)>, + '_clock_t': AttributeVariable(unit=second, obj=Clock(dt=0.10000000000000001 * msecond, name='defaultclock'), attribute='t_', constant=False), + '_recorded_v': >, dtype=dtype('float64'), scalar=False, constant=False)>, + '_indices': >, dtype=dtype('int32'), scalar=False, constant=True)>, + 'v': >, dtype=dtype('float64'), scalar=False, constant=False)>} +resolved_namespace [] +resolved_namespace2 [] +variable_indices defaultdict( at 0x04172570>, + {'lastspike': '_idx', 'v': '_idx', 'not_refractory': '_idx'}) +template_kwds {'_variable_names': ['v']} + +======================================== + +# For C++ code, we need these names explicitly, since not_refractory +# and lastspike might also be used in the threshold condition -- the +# names will then refer to single (constant) values and cannot be used +# for assigning new values +array_not_refractory name (threshold) +array_lastspike name (threshold) \ No newline at end of file