Skip to content

Commit

Permalink
Remove the necessity for the "Variable workaround" for dynamic arrays…
Browse files Browse the repository at this point in the history
…. For runtime targets, two symbols are inserted into the namespace "array" (underlying array) and "array_object" (the real object). Standalone generates code for each dynamic array.
  • Loading branch information
Marcel Stimberg committed Sep 11, 2013
1 parent 314db1d commit f1a4e1e
Show file tree
Hide file tree
Showing 20 changed files with 132 additions and 152 deletions.
7 changes: 6 additions & 1 deletion brian2/codegen/runtime/numpy_rt/numpy_rt.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
import numpy as np

from brian2.core.variables import Variable, Subexpression
from brian2.core.variables import Variable, Subexpression, DynamicArrayVariable

from ...codeobject import CodeObject
from ...templates import Templater
Expand Down Expand Up @@ -39,12 +39,17 @@ def variables_to_namespace(self):
if isinstance(var, Variable) and not isinstance(var, Subexpression):
if not var.constant:
self.nonconstant_values.append((name, 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
self.namespace[name] = value
if isinstance(var, DynamicArrayVariable):
self.namespace[name+'_object'] = var.get_object()

def update_namespace(self):
# update the values of the non-constant values in the namespace
Expand Down
9 changes: 5 additions & 4 deletions brian2/codegen/runtime/numpy_rt/templates/ratemonitor.py_
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# { 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)
_t[-1] = t
_rate[-1] = 1.0 * len(_spikes) / dt / _num_source_neurons
_t_object.resize(_new_len)
_rate_object.resize(_new_len)
# Note that _t refers directly to the underlying array which might have changed
_t_object[-1] = t
_rate_object[-1] = 1.0 * len(_spikes) / dt / _num_source_neurons
8 changes: 4 additions & 4 deletions brian2/codegen/runtime/numpy_rt/templates/spikemonitor.py_
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ if _n_spikes > 0:

_curlen = len(_t)
_newlen = _curlen + _n_spikes
_t.resize(_newlen)
_i.resize(_newlen)
_t[_curlen:_newlen] = t
_i[_curlen:_newlen] = _spikes
_t_object.resize(_newlen)
_i_object.resize(_newlen)
_t_object[_curlen:_newlen] = t
_i_object[_curlen:_newlen] = _spikes

# This is slow but correctly handles multiple spikes per neuron
_count += np.bincount(_spikes, minlength=_source_end-_source_start);
8 changes: 4 additions & 4 deletions brian2/codegen/runtime/numpy_rt/templates/statemonitor.py_
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

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

{% for _varname in _variable_names %}
_recorded_{{_varname}}.resize((_new_len, len(_indices)))
_recorded_{{_varname}}_object.resize((_new_len, len(_indices)))
{% endfor %}

# Store values
_t[-1] = _clock_t
_t_object[-1] = _clock_t

_vectorisation_idx = _indices
_idx = _indices[:]
Expand All @@ -18,6 +18,6 @@ _idx = _indices[:]
{% endfor %}

{% for _varname in _variable_names %}
_recorded_{{_varname}}[-1, :] = _to_record_{{_varname}}
_recorded_{{_varname}}_object[-1, :] = _to_record_{{_varname}}
{% endfor %}

10 changes: 5 additions & 5 deletions brian2/codegen/runtime/numpy_rt/templates/synapses_create.py_
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ for i in range(len(_source_neurons)):
elif _n != 1:
# We have a j-independent number
_cond_nonzero = _cond_nonzero.repeat(_n)
_cur_num_synapses = len(_synaptic_pre)
_cur_num_synapses = len(_synaptic_pre_object)
_numnew = len(_cond_nonzero)
_new_num_synapses = _cur_num_synapses + _numnew
_synaptic_pre.resize(_new_num_synapses)
_synaptic_post.resize(_new_num_synapses)
_synaptic_pre[_cur_num_synapses:] = _source_neurons[i]
_synaptic_post[_cur_num_synapses:] = _target_neurons[_cond_nonzero]
_synaptic_pre_object.resize(_new_num_synapses)
_synaptic_post_object.resize(_new_num_synapses)
_synaptic_pre_object[_cur_num_synapses:] = _source_neurons[i]
_synaptic_post_object[_cur_num_synapses:] = _target_neurons[_cond_nonzero]
_new_synapses = np.arange(_cur_num_synapses, _new_num_synapses)
_source_synapses = _pre_synaptic[_source_neurons[i]]
_cur_num_source_synapses = len(_source_synapses)
Expand Down
10 changes: 5 additions & 5 deletions brian2/codegen/runtime/weave_rt/templates/ratemonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
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;
const npy_int _new_len = (npy_int)(_t_object.attr("shape")[0]) + 1;

// Resize the arrays
PyObject_CallMethod(_t, "resize", "i", _new_len);
PyObject_CallMethod(_rate, "resize", "i", _new_len);
PyObject_CallMethod(_t_object, "resize", "i", _new_len);
PyObject_CallMethod(_rate_object, "resize", "i", _new_len);
// Get the potentially newly created underlying data arrays
double *_t_data = (double*)(((PyArrayObject*)(PyObject*)_t.attr("data"))->data);
double *_rate_data = (double*)(((PyArrayObject*)(PyObject*)_rate.attr("data"))->data);
double *_t_data = (double*)(((PyArrayObject*)(PyObject*)_t_object.attr("data"))->data);
double *_rate_data = (double*)(((PyArrayObject*)(PyObject*)_rate_object.attr("data"))->data);

//Set the new values
_t_data[_new_len - 1] = t;
Expand Down
30 changes: 15 additions & 15 deletions brian2/codegen/runtime/weave_rt/templates/spikemonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@
// TODO: Will this assumption ever be violated?
int _start_idx = 0;
int _end_idx = - 1;
for(int _i=0; _i<_num_spikes; _i++)
for(int _j=0; _j<_num_spikes; _j++)
{
const int _idx = _spikespace[_i];
const int _idx = _spikespace[_j];
if (_idx >= _source_start) {
_start_idx = _i;
_start_idx = _j;
break;
}
}
for(int _i=_start_idx; _i<_num_spikes; _i++)
for(int _j=_start_idx; _j<_num_spikes; _j++)
{
const int _idx = _spikespace[_i];
const int _idx = _spikespace[_j];
if (_idx >= _source_end) {
_end_idx = _i;
_end_idx = _j;
break;
}
}
Expand All @@ -31,23 +31,23 @@
_num_spikes = _end_idx - _start_idx;
if (_num_spikes > 0) {
// Get the current length and new length of t and i arrays
const int _curlen = _t.attr("shape")[0];
const int _curlen = _t_object.attr("shape")[0];
const int _newlen = _curlen + _num_spikes;
// Resize the arrays
py::tuple _newlen_tuple(1);
_newlen_tuple[0] = _newlen;
_t.mcall("resize", _newlen_tuple);
_i.mcall("resize", _newlen_tuple);
_t_object.mcall("resize", _newlen_tuple);
_i_object.mcall("resize", _newlen_tuple);
// Get the potentially newly created underlying data arrays
double *_t_data = (double*)(((PyArrayObject*)(PyObject*)_t.attr("data"))->data);
double *_t_data = (double*)(((PyArrayObject*)(PyObject*)_t_object.attr("data"))->data);
// TODO: How to get the correct datatype automatically here?
npy_int32 *_i_data = (npy_int32*)(((PyArrayObject*)(PyObject*)_i.attr("data"))->data);
npy_int32 *_i_data = (npy_int32*)(((PyArrayObject*)(PyObject*)_i_object.attr("data"))->data);
// Copy the values across
for(int _i=_start_idx; _i<_end_idx; _i++)
for(int _j=_start_idx; _j<_end_idx; _j++)
{
const int _idx = _spikespace[_i];
_t_data[_curlen + _i - _start_idx] = t;
_i_data[_curlen + _i - _start_idx] = _idx - _source_start;
const int _idx = _spikespace[_j];
_t_data[_curlen + _j - _start_idx] = t;
_i_data[_curlen + _j - _start_idx] = _idx - _source_start;
_count[_idx - _source_start]++;
}
}
Expand Down
10 changes: 5 additions & 5 deletions brian2/codegen/runtime/weave_rt/templates/statemonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@
{% block maincode %}
// USES_VARIABLES { _t, _clock_t, _indices }
// Get the current length and new length of t and value arrays
const int _curlen = _t.attr("shape")[0];
const int _curlen = _t_object.attr("shape")[0];
const int _new_len = _curlen + 1;
// Resize the arrays
PyObject_CallMethod(_t, "resize", "i", _new_len);
PyObject_CallMethod(_t_object, "resize", "i", _new_len);
{% for _varname in _variable_names %}

PyObject_CallMethod(_recorded_{{_varname}}, "resize", "((ii))",
PyObject_CallMethod(_recorded_{{_varname}}_object, "resize", "((ii))",
_new_len, _num_indices);
{% endfor %}

// Get the potentially newly created underlying data arrays and copy the
// data
double *_t_data = (double*)(((PyArrayObject*)(PyObject*)_t.attr("data"))->data);
double *_t_data = (double*)(((PyArrayObject*)(PyObject*)_t_object.attr("data"))->data);
_t_data[_new_len - 1] = _clock_t;

{% for _varname in _variable_names %}
{
PyArrayObject *_record_data = (((PyArrayObject*)(PyObject*)_recorded_{{_varname}}.attr("data")));
PyArrayObject *_record_data = (((PyArrayObject*)(PyObject*)_recorded_{{_varname}}_object.attr("data")));
const npy_intp* _record_strides = _record_data->strides;
for (int _i = 0; _i < _num_indices; _i++)
{
Expand Down
10 changes: 5 additions & 5 deletions brian2/codegen/runtime/weave_rt/templates/synapses_create.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
int *_synprebuf = new int[1];
int *_synpostbuf = new int[1];
int _curbuf = 0;
int _synapse_idx = _synaptic_pre.attr("shape")[0];
int _synapse_idx = _synaptic_pre_object.attr("shape")[0];
for(int i=0; i<_num_source_neurons; i++)
{
for(int j=0; j<_num_target_neurons; j++)
Expand All @@ -38,8 +38,8 @@
// Flush buffer
if(_curbuf==_buffer_size)
{
_flush_buffer(_prebuf, _synaptic_pre, _curbuf);
_flush_buffer(_postbuf, _synaptic_post, _curbuf);
_flush_buffer(_prebuf, _synaptic_pre_object, _curbuf);
_flush_buffer(_postbuf, _synaptic_post_object, _curbuf);
_curbuf = 0;
}
// Directly add the synapse numbers to the neuron->synapses
Expand All @@ -57,8 +57,8 @@

}
// Final buffer flush
_flush_buffer(_prebuf, _synaptic_pre, _curbuf);
_flush_buffer(_postbuf, _synaptic_post, _curbuf);
_flush_buffer(_prebuf, _synaptic_pre_object, _curbuf);
_flush_buffer(_postbuf, _synaptic_post_object, _curbuf);
delete [] _prebuf;
delete [] _postbuf;
delete [] _synprebuf;
Expand Down
8 changes: 7 additions & 1 deletion brian2/codegen/runtime/weave_rt/weave_rt.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# No weave for Python 3
weave = None

from brian2.core.variables import Variable, Subexpression
from brian2.core.variables import Variable, Subexpression, DynamicArrayVariable
from brian2.core.preferences import brian_prefs, BrianPreference

from ...codeobject import CodeObject
Expand Down Expand Up @@ -90,6 +90,9 @@ def variables_to_namespace(self):
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()
Expand All @@ -100,6 +103,9 @@ def variables_to_namespace(self):
# '_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()


def update_namespace(self):
# update the values of the non-constant values in the namespace
Expand Down
3 changes: 3 additions & 0 deletions brian2/core/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,9 @@ def get_value(self):
# The actual numpy array is accesible via DynamicArray1D.data
return self.value.data

def get_object(self):
return self.value


class Subexpression(Variable):
'''
Expand Down
30 changes: 4 additions & 26 deletions brian2/devices/cpp_standalone/codeobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,10 @@ class CPPStandaloneCodeObject(CodeObject):
language = CPPLanguage()

def variables_to_namespace(self):

# Variables can refer to values that are either constant (e.g. dt)
# or change every timestep (e.g. t). We add the values of the
# constant variables here and add the names of non-constant variables
# to a list

# A list containing tuples of name and a function giving the value
self.nonconstant_values = []

for name, var in self.variables.iteritems():
if isinstance(var, Variable) and not isinstance(var, Subexpression):
if not var.constant:
self.nonconstant_values.append((name, var.get_value))
if not var.scalar:
self.nonconstant_values.append(('_num' + name,
var.get_len))
else:
try:
value = var.get_value()
except TypeError: # A dummy Variable without value
continue
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()
# We only copy constant scalar values to the namespace here
for varname, var in self.variables.iteritems():
if var.constant and var.scalar:
self.namespace[varname] = var.get_value()

def run(self):
raise RuntimeError("Cannot run in C++ standalone mode")
49 changes: 17 additions & 32 deletions brian2/devices/cpp_standalone/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def dynamic_array_1d(self, owner, name, size, unit, dtype):
if dtype is None:
dtype = brian_prefs['core.default_scalar_dtype']
arr = DynamicArray1D(size, dtype=dtype)
self.dynamic_arrays['_array_%s_%s' % (owner.name, name)] = arr
self.dynamic_arrays['_dynamic_array_%s_%s' % (owner.name, name)] = arr
return arr

def dynamic_array(self):
Expand Down Expand Up @@ -96,37 +96,22 @@ def build(self, net):

# Generate data for non-constant values
code_object_defs = defaultdict(list)
for codeobj in self.code_objects.values():
for k, v in codeobj.nonconstant_values:
for codeobj in self.code_objects.itervalues():
for k, v in codeobj.variables.iteritems():
if k=='t':
pass
elif v.im_class is ArrayVariable:
# find the corresponding array
arr = v.im_self
arr_k = arr.name
arr_dtype = arr.dtype
arr_N = len(arr.value)
val = arr.value
if isinstance(val, int):
code_object_defs[id(codeobj)].append('int %s = %s;' % (k, arr_N))
elif k=='_spikespace':
pass
#code_object_defs[id(codeobj)].append('%s *%s = %s;' % (arr_dtype, k, arr_k))
elif isinstance(val, numpy.ndarray):
pass
elif isinstance(val, DynamicArray1D):
pass
else:
raise ValueError("Unknown")
elif v.im_class is Variable:
arr = v.im_self
val = arr.value
if isinstance(val, DynamicArray1D):
pass
else:
raise ValueError("Unknown")
else:
raise ValueError("Unknown")
elif isinstance(v, Subexpression):
pass
elif not v.scalar:
N = v.get_len()
code_object_defs[codeobj.name].append('const int _num%s = %s;' % (k, N))
if isinstance(v, DynamicArrayVariable):
c_type = c_data_type(v.dtype)
# Create an alias name for the underlying array
code = ('{c_type}* {arrayname} = '
'&(_dynamic{arrayname}[0]);').format(c_type=c_type,
arrayname=v.arrayname)
code_object_defs[codeobj.name].append(code)

# Generate the updaters
run_lines = []
Expand All @@ -135,9 +120,9 @@ def build(self, net):
if cls is CodeObjectUpdater:
codeobj = updater.owner
ns = codeobj.namespace
# TODO: fix these freeze/CONSTANTS hacks somehow - they work but not elegant.
# TODO: fix these freeze/CONSTANTS hacks somehow - they work but not elegant.
code = freeze(codeobj.code.cpp_file, ns)
code = code.replace('%CONSTANTS%', '\n'.join(code_object_defs[id(codeobj)]))
code = code.replace('%CONSTANTS%', '\n'.join(code_object_defs[codeobj.name]))
code = '#include "arrays.h"\n'+code

open('output/'+codeobj.name+'.cpp', 'w').write(code)
Expand Down
2 changes: 1 addition & 1 deletion brian2/devices/cpp_standalone/templates/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ int main(void)
{{run_line}}
{% endfor %}
}
cout << "Num spikes: " << _array_spikemonitor__i.size() << endl;
cout << "Num spikes: " << _dynamic_array_spikemonitor__i.size() << endl;
_dealloc_arrays();
return 0;
}
Loading

0 comments on commit f1a4e1e

Please sign in to comment.