Skip to content

Commit

Permalink
Make cpp_standalone work (including variable initialization)
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcel Stimberg committed Oct 8, 2013
1 parent 6e23720 commit 45411be
Show file tree
Hide file tree
Showing 16 changed files with 126 additions and 116 deletions.
4 changes: 4 additions & 0 deletions brian2/codegen/languages/cpp_lang.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ def c_data_type(dtype):
dtype = 'float'
elif dtype == numpy.float64:
dtype = 'double'
elif dtype == numpy.int8:
dtype = 'int8_t'
elif dtype == numpy.int16:
dtype = 'int16_t'
elif dtype == numpy.int32:
dtype = 'int32_t'
elif dtype == numpy.int64:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ _vectorisation_idx = _idx = slice(None)
{{line}}
{% endfor %}

if _cond is True:
_cond = slice(None)
if _cond is False:
_cond = []

# Phase 2: having computed _cond, the boolean array of points where
# the setting is to be applied, we want to vectorise over idx being
# only these values.
Expand Down
81 changes: 47 additions & 34 deletions brian2/core/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
'ArrayVariable',
'DynamicArrayVariable',
'Subexpression',
'VariableView',
'AuxiliaryVariable'
]


Expand Down Expand Up @@ -304,51 +306,62 @@ def __getitem__(self, item):
else:
return Quantity(values, self.unit.dimensions)

def set_code_with_string_index(self, item, value):
check_units = self.unit is not None
template = 'group_variable_set_conditional'
self.group._set_with_code_conditional(self.variable, item, value,
template=template,
check_units=check_units,
level=self.level + 1)

def set_code_with_array_index(self, item, value):
indices = self.calc_indices(item)
check_units = self.unit is not None
template = 'group_variable_set'
self.group._set_with_code(self.variable, indices, value,
template=template,
check_units=check_units,
level=self.level + 1)

def set_array_with_array_index(self, item, value):
indices = self.calc_indices(item)
if not self.unit is None:
fail_for_dimension_mismatch(value, self.unit)
# We are not going via code generation so we have to take care
# of correct indexing (in particular for subgroups) explicitly
var_index = self.group.variable_indices[self.variable.name]
if var_index != '_idx':
indices = self.group.variables[var_index].get_value()[indices]
self.variable.value[indices] = value

def __setitem__(self, item, value):
variable = self.variable
if variable.read_only:
if self.variable.read_only:
raise TypeError('Variable %s is read-only.' % self.name)

if item == slice(None):
item = 'True'

# Both index and values are strings, use a single code object do deal
# with this situation
if isinstance(value, basestring) and isinstance(item, basestring):
check_units = self.unit is not None
template = 'group_variable_set_conditional'
self.group._set_with_code_conditional(variable, item, value,
template=template,
check_units=check_units,
level=self.level + 1)
self.set_code_with_string_index(item, value)
elif isinstance(item, basestring):
try:
value = float(value)
float(value) # only checks for the exception
except (TypeError, ValueError):
raise TypeError('When setting a variable based on a string ',
'index, the value has to be a string or a '
'scalar.')
check_units = self.unit is not None
template = 'group_variable_set_conditional'
self.group._set_with_code_conditional(variable, item, repr(value),
template=template,
check_units=check_units,
level=self.level + 1)
if not item == 'True':
raise TypeError('When setting a variable based on a string '
'index, the value has to be a string or a '
'scalar.')
else:
# Fall back to the general array-array pattern
self.set_array_with_array_index(slice(None), value)
return
self.set_code_with_string_index(item, repr(value))
elif isinstance(value, basestring):
indices = self.calc_indices(item)
check_units = self.unit is not None
template = 'group_variable_set'
self.group._set_with_code(variable, indices, value,
template=template,
check_units=check_units,
level=self.level + 1)
self.set_code_with_array_index(item, value)
else: # No string expressions involved
indices = self.calc_indices(item)
if not self.unit is None:
fail_for_dimension_mismatch(value, self.unit)
# We are not going via code generation so we have to take care
# of correct indexing (in particular for subgroups) explicitly
var_index = self.group.variable_indices[variable.name]
if var_index != '_idx':
indices = self.group.variables[var_index].get_value()[indices]
variable.value[indices] = value
self.set_array_with_array_index(item, value)

def __array__(self, dtype=None):
if dtype is not None and dtype != self.variable.dtype:
Expand Down
91 changes: 46 additions & 45 deletions brian2/devices/cpp_standalone/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
fail_for_dimension_mismatch,
have_same_dimensions,
)
from brian2.devices.device import smallest_inttype
from brian2.units import second

from .codeobject import CPPStandaloneCodeObject


__all__ = ['build', 'Network', 'run', 'reinit', 'stop']

def freeze(code, ns):
Expand All @@ -30,51 +32,26 @@ def freeze(code, ns):
code = word_substitute(code, {k: repr(v)})
return code

class StandaloneVariableView(object):
class StandaloneVariableView(VariableView):
'''
Will store information about how the variable was set in the original
`ArrayVariable` object.
'''
def __init__(self, name, variable, group, template,
unit=None, level=0):
self.name = name
self.variable = variable
self.group = group
super(StandaloneVariableView, self).__init__(name, variable, group,
unit=unit, level=level)
self.template = template
self.unit = unit
self.level = level

# def __setitem__(self, key, value):
# variable = self.variable
# self.variable.assignments.append((key, value))
def __setitem__(self, i, value):
variable = self.variable
if variable.scalar:
if not (i == slice(None) or i == 0 or (hasattr(i, '__len__') and len(i) == 0)):
raise IndexError('Variable is a scalar variable.')
indices = np.array([0])
else:
indices = self.group.indices[self.group.variable_indices[self.name]][i]
try:
iter(value)
sequence = True
except TypeError:
sequence = False
if not isinstance(value, basestring) and not sequence:
if not self.unit is None:
fail_for_dimension_mismatch(value, self.unit)
value = repr(value)
if isinstance(value, basestring):
check_units = self.unit is not None
self.group._set_with_code(variable, indices, value,
template=self.template,
check_units=check_units, level=self.level + 1)
else:
raise NotImplementedError("Setting variables with sequences not supported on devices.")

# Overwrite methods to signal that they are not available for standalone
def set_array_with_array_index(self, item, value):
raise NotImplementedError(('Cannot set variables this way in'
'standalone, try using string expressions.'))

def __getitem__(self, item):
raise NotImplementedError()


class StandaloneArrayVariable(ArrayVariable):

def __init__(self, name, unit, size, dtype, group_name=None, constant=False,
Expand Down Expand Up @@ -104,16 +81,16 @@ def set_value(self, value, index=None):
# def get_addressable_value_with_unit(self, group, level=0):
# return StandaloneVariableView(self)

def get_addressable_value(self, group, level=0):
def get_addressable_value(self, name, group, level=0):
template = getattr(group, '_set_with_code_template',
'group_variable_set')
return StandaloneVariableView(self.name, self, group, template=template,
return StandaloneVariableView(name, self, group, template=template,
unit=None, level=level)

def get_addressable_value_with_unit(self, group, level=0):
def get_addressable_value_with_unit(self, name, group, level=0):
template = getattr(group, '_set_with_code_template',
'group_variable_set')
return StandaloneVariableView(self.name, self, group, template=template,
return StandaloneVariableView(name, self, group, template=template,
unit=self.unit, level=level)


Expand All @@ -127,25 +104,47 @@ class CPPStandaloneDevice(Device):
'''
'''
def __init__(self):
#: List of all regular arrays with their type and size
self.array_specs = []
#: List of all dynamic arrays with their type
self.dynamic_array_specs = []
#: List of all arrays to be filled with zeros (with type and size)
self.zero_specs = []
#: List of all arrays to be filled with numbers (with type, size,
#: start, and stop)
self.arange_specs = []
self.code_objects = {}
self.main_queue = []

def array(self, owner, name, size, unit, dtype=None, constant=False,
is_bool=False):
is_bool=False, read_only=False):
if is_bool:
dtype = numpy.bool
elif dtype is None:
dtype = brian_prefs['core.default_scalar_dtype']
self.array_specs.append(('_array_%s_%s' % (owner.name, name),
c_data_type(dtype), size))
self.zero_specs.append(('_array_%s_%s' % (owner.name, name),
c_data_type(dtype), size))
return StandaloneArrayVariable(name, unit, size=size, dtype=dtype,
group_name=owner.name,
constant=constant, is_bool=is_bool)

def arange(self, owner, name, size, start=0, dtype=None, constant=True,
read_only=True):
if dtype is None:
dtype = smallest_inttype(size)
self.array_specs.append(('_array_%s_%s' % (owner.name, name),
c_data_type(dtype), size))
self.arange_specs.append(('_array_%s_%s' % (owner.name, name),
c_data_type(dtype), start, size))
return StandaloneArrayVariable(name, Unit(1), size=size, dtype=dtype,
group_name=owner.name,
constant=constant, is_bool=False)

def dynamic_array_1d(self, owner, name, size, unit, dtype=None,
constant=False, constant_size=True, is_bool=False):
constant=False, constant_size=True, is_bool=False,
read_only=False):
if is_bool:
dtype = numpy.bool
elif dtype is None:
Expand All @@ -163,10 +162,9 @@ def code_object_class(self, codeobj_class=None):
return CPPStandaloneCodeObject

def code_object(self, owner, name, abstract_code, namespace, variables, template_name,
indices, variable_indices, codeobj_class=None,
template_kwds=None):
variable_indices, codeobj_class=None, template_kwds=None):
codeobj = super(CPPStandaloneDevice, self).code_object(owner, name, abstract_code, namespace, variables,
template_name, indices, variable_indices,
template_name, variable_indices,
codeobj_class=codeobj_class,
template_kwds=template_kwds,
)
Expand All @@ -178,8 +176,11 @@ def build(self):
os.mkdir('output')

# Write the arrays
arr_tmp = CPPStandaloneCodeObject.templater.arrays(None, array_specs=self.array_specs,
dynamic_array_specs=self.dynamic_array_specs)
arr_tmp = CPPStandaloneCodeObject.templater.arrays(None,
array_specs=self.array_specs,
dynamic_array_specs=self.dynamic_array_specs,
zero_specs=self.zero_specs,
arange_specs=self.arange_specs)
open('output/arrays.cpp', 'w').write(arr_tmp.cpp_file)
open('output/arrays.h', 'w').write(arr_tmp.h_file)

Expand Down
9 changes: 8 additions & 1 deletion brian2/devices/cpp_standalone/templates/arrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,17 @@ std::vector<{{dtype_spec}}> {{varname}};

void _init_arrays()
{
{% for (varname, dtype_spec, N) in array_specs %}
// Arrays initialized to 0
{% for (varname, dtype_spec, N) in zero_specs %}
{{varname}} = new {{dtype_spec}}[{{N}}];
for(int i=0; i<{{N}}; i++) {{varname}}[i] = 0;
{% endfor %}

// Arrays initialized to an "arange"
{% for (varname, dtype_spec, start, stop) in arange_specs %}
{{varname}} = new {{dtype_spec}}[{{stop}}-{{start}}];
for(int i=0; i<{{stop}}-{{start}}; i++) {{varname}}[i] = {{start}} + i;
{% endfor %}
}

void _dealloc_arrays()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,8 @@ void _run_{{codeobj_name}}(double t)
{% endfor %}

//// MAIN CODE ////////////
// TODO: this hack only works when writing G.V = str, not e.g. G.v[str] = str.
const int _num_group_idx = _num_idx;
for(int _idx_group_idx=0; _idx_group_idx<_num_group_idx; _idx_group_idx++)
for(int _idx=0; _idx<N; _idx++)
{
//const int _idx = _group_idx[_idx_group_idx];
const int _idx = _idx_group_idx;
const int _vectorisation_idx = _idx;
{% for line in code_lines %}
{{line}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,8 @@ void _run_{{codeobj_name}}(double t)
{% endfor %}

//// MAIN CODE ////////////
const int _num_group_idx = _num_idx;
for(int _idx_group_idx=0; _idx_group_idx<_num_group_idx; _idx_group_idx++)
for(int _idx=0; _idx<N; _idx++)
{
//const int _idx = _group_idx[_idx_group_idx];
const int _idx = _idx_group_idx;
const int _vectorisation_idx = _idx;
{% for line in code_lines['condition'] %}
{{line}}
Expand Down
2 changes: 1 addition & 1 deletion brian2/devices/cpp_standalone/templates/reset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void _run_{{codeobj_name}}(double t)
{% endfor %}

const int *_spikes = {{_spikespace}};
const int _num_spikes = {{_spikespace}}[_num_idx];
const int _num_spikes = {{_spikespace}}[N];

//// MAIN CODE ////////////
for(int _index_spikes=0; _index_spikes<_num_spikes; _index_spikes++)
Expand Down
2 changes: 1 addition & 1 deletion brian2/devices/cpp_standalone/templates/spikemonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void _run_{{codeobj_name}}(double t)
for(int _i=_start_idx; _i<_num_spikes; _i++)
{
const int _idx = {{_spikespace}}[_i];
if (_idx >= _source_end) {
if (_idx >= _source_stop) {
_end_idx = _i;
break;
}
Expand Down
2 changes: 1 addition & 1 deletion brian2/devices/cpp_standalone/templates/stateupdate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ void _run_{{codeobj_name}}(double t)
{% endfor %}

//// MAIN CODE ////////////
for(int _idx=0; _idx<_num_idx; _idx++)
for(int _idx=0; _idx<N; _idx++)
{
// THIS MESSAGE IS JUST TO LET YOU KNOW WE'RE IN THE STANDALONE NOT WEAVE TEMPLATE
const int _vectorisation_idx = _idx;
Expand Down
4 changes: 2 additions & 2 deletions brian2/devices/cpp_standalone/templates/threshold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void _run_{{codeobj_name}}(double t)

//// MAIN CODE ////////////
long _cpp_numspikes = 0;
for(int _idx=0; _idx<_num_idx; _idx++)
for(int _idx=0; _idx<N; _idx++)
{
const int _vectorisation_idx = _idx;
{% for line in code_lines %}
Expand All @@ -51,7 +51,7 @@ void _run_{{codeobj_name}}(double t)
_ptr{{_array_lastspike}}[_idx] = t;
}
}
{{_spikespace}}[_num_idx] = _cpp_numspikes;
{{_spikespace}}[N] = _cpp_numspikes;
}
{% endmacro %}

Expand Down
Loading

0 comments on commit 45411be

Please sign in to comment.