@mstimberg Thanks. This is really an interesting workaround. Before I elaborate concerning “option 1,” – thanks for trusting my programming skills – but for now, I am still on the end-user side
I am experimenting with your suggestion. However, two issues seem to emerge, plus a third one that actually falls in a side question.
First: device.get_array_name
Looking into the device.get_array_name
docs, the method requires a global
variable to retrieve the actual array name from. What if I am actually nesting my cell
definition inside a method that returns cell
as an argument of a class
? What is the actual global variable? Global wherein? Python or CPP? And global concerning what scope? Global the whole script scope? Or global to a parent method? The following is the hierarchy I am dealing with:
def cell_compartment(N,pars):
...
compartment = NeuronGroup(N,...,events={'transitioned': 'transition', 'no_transition': 'not transition'}))
code_upon_transition = """
rval = rand()
p_O = q_DP/Q
X_O = X_O + (rval<=X_O*p_O)*1 - (rval>X_O*p_O)*1
fraction = 1 - tau/(Q*dt/second)
dummy = nMGA(dt,Q,p_O,i) <----- Referring to this line also in the **Second point**
"""
code_upon_notransition = """
tau -= Q*dt/second
"""
compartment.run_on_event('transitioned',code_upon_transition)
compartment.run_on_event('notransition',code_upon_notransition)
return compartment
def compartment_geometry(N,...):
comps = cell_compartment(N,...)
...
return Network([comps,...])
class ComplexCell(object):
def __init__(self, N,...):
# Pre-defs of standalone
set_device('cpp_standalone', directory=code_dir, build_on_run=False)
device.delete(force=True) # Clean codegen directory
self._code_dir = code_dir
self.__build = False
# Generate model
self.celltree = ComplexCell(N,...)
def simulate(self,duration=1*second,build=True):
prefs.devices.cpp_standalone.openmp_threads = 10
prefs.logging.file_log = False
prefs.logging.delete_log_on_exit = True
defaultclock.dt = 0.01*msecond
## Run simulation
self.celltree.run(duration, report='text')
if build:
# Build at the end
device.build(directory=self._code_dir,clean=clean)
self.__build = True
Hence if my CPP code is now:
@implementation('cpp', '''
double nMGA(double dt,double Q,double p_O,int index) {
while(true) {
%tau_NAME%[index] = -log(_rand(index));
if (%tau_NAME%[index]>=%fraction_NAME%[index]*Q*dt_){
%tau_NAME%[index] -= %fraction_NAME%[index]*Q*dt_;
break;
}
else {
%rval_NAME%[index] = _rand(index);
%X_0_NAME%[index] += int(%rval_NAME%[index]<=%X_0_NAME%[index]*p_O) - int(%rval_NAME%[index]>%X_0_NAME%[index]*p_O);
%fraction_NAME%[index] -= %tau_NAME%[index]/(Q*dt_);
}
}
return 1.0;
}
'''.replace('%tau_NAME%', device.get_array_name(<???>.variables['tau'])).replace('%fraction_NAME%',\
device.get_array_name(<???>.variables['fraction'])).replace('%rval_NAME%',\
device.get_array_name(<???>.variables['rval'])).replace('%X_0_NAME%',\
device.get_array_name(<???>.variables['X_0'])),
dependencies={'_rand': DEFAULT_FUNCTIONS['rand']})
@check_units(dt=second, Q=1, p_O=1, index='integer', result=1)
def nMGA(dt, Q, p_O, index):
raise NotImplementedError('use standalone mode')
if __name__=='__main__':
astro = ComplexCell(10,...)
astro.simulate(duration=5*second)
What is my cell
global variable (replaced by <???>
)?
Second: index
error
I am not sure. But my understanding from your explanation is that ‘i’ is a reserved symbol in the Brian scope or within the code and always refers to the index of elements in the NeuronGroup
.
Then when I attempt to compile the above, I get and error when the parser gets to the the line where I am supposed to call the nMGA
function in the code_upon_transition
. Here is the full error:
Traceback (most recent call last):
File "/home/maurizio/local/brian2/brian2/core/network.py", line 897, in before_run
obj.before_run(run_namespace)
File "/home/maurizio/local/brian2/brian2/groups/group.py", line 1143, in before_run
self.create_code_objects(run_namespace)
File "/home/maurizio/local/brian2/brian2/groups/group.py", line 1136, in create_code_objects
code_object = self.create_default_code_object(run_namespace)
File "/home/maurizio/local/brian2/brian2/groups/group.py", line 1118, in create_default_code_object
self.codeobj = create_runner_codeobj(group=self.group,
File "/home/maurizio/local/brian2/brian2/codegen/codeobject.py", line 396, in create_runner_codeobj
check_units_statements(c, variables)
File "/home/maurizio/local/brian2/brian2/equations/unitcheck.py", line 99, in check_units_statements
expr_unit = parse_expression_dimensions(expr, variables)
File "/home/maurizio/local/brian2/brian2/parsing/expressions.py", line 315, in parse_expression_dimensions
arg_idx = func._arg_names.index(expected_unit)
AttributeError: 'NoneType' object has no attribute 'index'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/maurizio/Ongoing.Projects/FSU.Project/code/astrocyte_model_class.py", line 574, in <module>
cell.simulate(duration=5*second)
File "/home/maurizio/Ongoing.Projects/FSU.Project/code/astrocyte_model_class.py", line 554, in simulate
self.celltree.run(duration, report='text')
File "/home/maurizio/local/brian2/brian2/core/base.py", line 276, in device_override_decorated_function
return getattr(curdev, name)(*args, **kwds)
File "/home/maurizio/local/brian2/brian2/devices/cpp_standalone/device.py", line 1375, in network_run
net.before_run(namespace)
File "/home/maurizio/local/brian2/brian2/core/base.py", line 278, in device_override_decorated_function
return func(*args, **kwds)
File "/home/maurizio/local/brian2/brian2/core/network.py", line 899, in before_run
raise BrianObjectException("An error occurred when preparing an object.", obj) from ex
brian2.core.base.BrianObjectException: Error encountered with object named "comp_resetter".
Object was created here (most recent call only, full details in debug log):
File "/home/maurizio/Ongoing.Projects/FSU.Project/code/astrocyte_model_class.py", line 406, in AstroCompartment
astro_compartment.run_on_event('transitioned',code_upon_transition)
So for some reason, it seems that the index is NoneType
– could that be because right not my N
in terms of comps
instantiation is 1
– which would imply treatment of scalar NumPy arrays differently from actual arrays (i.e., not atleast_1d
for scalar arrays).
Third: Replacing internal variables by unitless versions
Looking at your suggestion, I recall that Brian offers this option of retrieving the unitless variables by appending the underscore _
character to the variable name. However, if this allegedly works in the CPP code, when I replaced dt/second
by dt_
, for example, in my above code_upon_transition
I get the unidentified identifier
error. So it seems that Brian does not like the underscored version of its internal variables. Is that right? I am a bit confused.