def translate(self, code, dtype): ''' Translates an abstract code block into the target language. ''' scalar_statements = {} vector_statements = {} for ac_name, ac_code in code.iteritems(): scalar_statements[ac_name], vector_statements[ ac_name] = make_statements(ac_code, self.variables, dtype) for vs in vector_statements.itervalues(): # Check that the statements are meaningful independent on the order of # execution (e.g. for synapses) try: check_for_order_independence(vs, self.variables, self.variable_indices) except OrderDependenceError: # If the abstract code is only one line, display it in full if len(vs) <= 1: error_msg = 'Abstract code: "%s"\n' % vs[0] else: error_msg = ('%d lines of abstract code, first line is: ' '"%s"\n') % (len(vs), vs[0]) logger.warn(('Came across an abstract code block that is not ' 'well-defined: the outcome may depend on the ' 'order of execution. ' + error_msg)) return self.translate_statement_sequence(scalar_statements, vector_statements)
def translate(self, code, dtype): ''' Translates an abstract code block into the target language. ''' scalar_statements = {} vector_statements = {} for ac_name, ac_code in code.iteritems(): statements = make_statements(ac_code, self.variables, dtype, optimise=True, blockname=ac_name) scalar_statements[ac_name], vector_statements[ac_name] = statements for vs in vector_statements.itervalues(): # Check that the statements are meaningful independent on the order of # execution (e.g. for synapses) try: if self.has_repeated_indices(vs): # only do order dependence if there are repeated indices check_for_order_independence(vs, self.variables, self.variable_indices) except OrderDependenceError: # If the abstract code is only one line, display it in full if len(vs) <= 1: error_msg = 'Abstract code: "%s"\n' % vs[0] else: error_msg = ('%d lines of abstract code, first line is: ' '"%s"\n') % (len(vs), vs[0]) logger.warn(('Came across an abstract code block that may not be ' 'well-defined: the outcome may depend on the ' 'order of execution. You can ignore this warning if ' 'you are sure that the order of operations does not ' 'matter. ' + error_msg)) return self.translate_statement_sequence(scalar_statements, vector_statements)
def translate(self, code, dtype): ''' Translates an abstract code block into the target language. ''' scalar_statements = {} vector_statements = {} for ac_name, ac_code in code.iteritems(): scalar_statements[ac_name], vector_statements[ac_name] = make_statements(ac_code, self.variables, dtype) for vs in vector_statements.itervalues(): # Check that the statements are meaningful independent on the order of # execution (e.g. for synapses) try: check_for_order_independence(vs, self.variables, self.variable_indices) except OrderDependenceError: # If the abstract code is only one line, display it in full if len(vs) <= 1: error_msg = 'Abstract code: "%s"\n' % vs[0] else: error_msg = ('%d lines of abstract code, first line is: ' '"%s"\n') % (len(vs), vs[0]) logger.warn(('Came across an abstract code block that is not ' 'well-defined: the outcome may depend on the ' 'order of execution. ' + error_msg)) return self.translate_statement_sequence(scalar_statements, vector_statements)
def check_permutation_code(code): from collections import defaultdict vars = get_identifiers(code) indices = defaultdict(lambda: '_idx') for var in vars: if var.endswith('_syn'): indices[var] = '_idx' elif var.endswith('_pre'): indices[var] ='_presynaptic_idx' elif var.endswith('_post'): indices[var] = '_postsynaptic_idx' variables = dict() for var in indices: variables[var] = ArrayVariable(var, 1, None, 10, device) variables['_presynaptic_idx'] = ArrayVariable(var, 1, None, 10, device) variables['_postsynaptic_idx'] = ArrayVariable(var, 1, None, 10, device) scalar_statements, vector_statements = make_statements(code, variables, float64) check_for_order_independence(vector_statements, variables, indices)
def check_permutation_code(code): from collections import defaultdict vars = get_identifiers(code) indices = defaultdict(lambda: '_idx') for var in vars: if var.endswith('_syn'): indices[var] = '_idx' elif var.endswith('_pre'): indices[var] = '_presynaptic_idx' elif var.endswith('_post'): indices[var] = '_postsynaptic_idx' variables = dict() for var in indices: variables[var] = ArrayVariable(var, 1, None, 10, device) variables['_presynaptic_idx'] = ArrayVariable(var, 1, None, 10, device) variables['_postsynaptic_idx'] = ArrayVariable(var, 1, None, 10, device) scalar_statements, vector_statements = make_statements( code, variables, float64) check_for_order_independence(vector_statements, variables, indices)
def translate(self, code, dtype): """ Translates an abstract code block into the target language. """ scalar_statements = {} vector_statements = {} for ac_name, ac_code in code.items(): statements = make_statements(ac_code, self.variables, dtype, optimise=True, blockname=ac_name) scalar_statements[ac_name], vector_statements[ac_name] = statements for vs in vector_statements.values(): # Check that the statements are meaningful independent on the order of # execution (e.g. for synapses) try: if self.has_repeated_indices( vs ): # only do order dependence if there are repeated indices check_for_order_independence(vs, self.variables, self.variable_indices) except OrderDependenceError: # If the abstract code is only one line, display it in full if len(vs) <= 1: error_msg = f"Abstract code: '{vs[0]}'\n" else: error_msg = ( f"{len(vs)} lines of abstract code, first line is: " f"'{vs[0]}'\n") logger.warn( ('Came across an abstract code block that may not be ' 'well-defined: the outcome may depend on the ' 'order of execution. You can ignore this warning if ' 'you are sure that the order of operations does not ' 'matter. ' + error_msg)) translated = self.translate_statement_sequence(scalar_statements, vector_statements) return translated
def translate( self, code, dtype ): # TODO: it's not so nice we have to copy the contents of this function.. ''' Translates an abstract code block into the target language. ''' # first check if user code is not using variables that are also used by GSL reserved_variables = [ '_dataholder', '_fill_y_vector', '_empty_y_vector', '_GSL_dataholder', '_GSL_y', '_GSL_func' ] if any([var in self.variables for var in reserved_variables]): # import here to avoid circular import raise ValueError(("The variables %s are reserved for the GSL " "internal code." % (str(reserved_variables)))) # if the following statements are not added, Brian translates the # differential expressions in the abstract code for GSL to scalar statements # in the case no non-scalar variables are used in the expression diff_vars = self.find_differential_variables(code.values()) self.add_gsl_variables_as_non_scalar(diff_vars) # add arrays we want to use in generated code before self.generator.translate() so # brian does namespace unpacking for us pointer_names = self.add_meta_variables(self.method_options) scalar_statements = {} vector_statements = {} for ac_name, ac_code in code.iteritems(): statements = make_statements(ac_code, self.variables, dtype, optimise=True, blockname=ac_name) scalar_statements[ac_name], vector_statements[ac_name] = statements for vs in vector_statements.itervalues(): # Check that the statements are meaningful independent on the order of # execution (e.g. for synapses) try: if self.has_repeated_indices( vs ): # only do order dependence if there are repeated indices check_for_order_independence( vs, self.generator.variables, self.generator.variable_indices) except OrderDependenceError: # If the abstract code is only one line, display it in ful l if len(vs) <= 1: error_msg = 'Abstract code: "%s"\n' % vs[0] else: error_msg = ( '%_GSL_driver lines of abstract code, first line is: ' '"%s"\n') % (len(vs), vs[0]) # save function names because self.generator.translate_statement_sequence # deletes these from self.variables but we need to know which identifiers # we can safely ignore (i.e. we can ignore the functions because they are # handled by the original generator) self.function_names = self.find_function_names() scalar_code, vector_code, kwds = self.generator.translate_statement_sequence( scalar_statements, vector_statements) ############ translate code for GSL # first check if any indexing other than '_idx' is used (currently not supported) for code_list in scalar_code.values() + vector_code.values(): for code in code_list: m = re.search('\[(\w+)\]', code) if m is not None: if m.group(1) != '0' and m.group(1) != '_idx': from brian2.stateupdaters.base import UnsupportedEquationsException raise UnsupportedEquationsException( ("Equations result in state " "updater code with indexing " "other than '_idx', which " "is currently not supported " "in combination with the " "GSL stateupdater.")) # differential variable specific operations to_replace = self.diff_var_to_replace(diff_vars) GSL_support_code = self.get_dimension_code(len(diff_vars)) GSL_support_code += self.yvector_code(diff_vars) # analyze all needed variables; if not in self.variables: put in separate dic. # also keep track of variables needed for scalar statements and vector statements other_variables = self.find_undefined_variables( scalar_statements[None] + vector_statements[None]) variables_in_scalar = self.find_used_variables(scalar_statements[None], other_variables) variables_in_vector = self.find_used_variables(vector_statements[None], other_variables) # so that _dataholder holds diff_vars as well, even if they don't occur # in the actual statements for var in diff_vars.keys(): if not var in variables_in_vector: variables_in_vector[var] = self.variables[var] # lets keep track of the variables that eventually need to be added to # the _GSL_dataholder somehow self.variables_to_be_processed = variables_in_vector.keys() # add code for _dataholder struct GSL_support_code = self.write_dataholder( variables_in_vector) + GSL_support_code # add e.g. _lio_1 --> _GSL_dataholder._lio_1 to replacer to_replace.update( self.to_replace_vector_vars(variables_in_vector, ignore=diff_vars.keys())) # write statements that unpack (python) namespace to _dataholder struct # or local namespace GSL_main_code = self.unpack_namespace(variables_in_vector, variables_in_scalar, ['t']) # rewrite actual calculations described by vector_code and put them in _GSL_func func_code = self.translate_one_statement_sequence( vector_statements[None], scalar=False) GSL_support_code += self.make_function_code( self.translate_vector_code(func_code, to_replace)) scalar_func_code = self.translate_one_statement_sequence( scalar_statements[None], scalar=True) # rewrite scalar code, keep variables that are needed in scalar code normal # and add variables to _dataholder for vector_code GSL_main_code += '\n' + self.translate_scalar_code( scalar_func_code, variables_in_scalar, variables_in_vector) if len(self.variables_to_be_processed) > 0: raise AssertionError( ("Not all variables that will be used in the vector " "code have been added to the _GSL_dataholder. This " "might mean that the _GSL_func is using unitialized " "variables." "\nThe unprocessed variables " "are: %s" % (str(self.variables_to_be_processed)))) scalar_code['GSL'] = GSL_main_code kwds['define_GSL_scale_array'] = self.scale_array_code( diff_vars, self.method_options) kwds['n_diff_vars'] = len(diff_vars) kwds['GSL_settings'] = dict(self.method_options) kwds['GSL_settings']['integrator'] = self.integrator kwds['support_code_lines'] += GSL_support_code.split('\n') kwds['t_array'] = self.get_array_name(self.variables['t']) + '[0]' kwds['dt_array'] = self.get_array_name(self.variables['dt']) + '[0]' kwds['define_dt'] = 'dt' not in variables_in_scalar kwds['cpp_standalone'] = self.is_cpp_standalone() for key, value in pointer_names.items(): kwds[key] = value return scalar_code, vector_code, kwds
def translate(self, code, dtype): # TODO: it's not so nice we have to copy the contents of this function.. ''' Translates an abstract code block into the target language. ''' # first check if user code is not using variables that are also used by GSL reserved_variables = ['_dataholder', '_fill_y_vector', '_empty_y_vector', '_GSL_dataholder', '_GSL_y', '_GSL_func'] if any([var in self.variables for var in reserved_variables]): # import here to avoid circular import raise ValueError(("The variables %s are reserved for the GSL " "internal code."%(str(reserved_variables)))) # if the following statements are not added, Brian translates the # differential expressions in the abstract code for GSL to scalar statements # in the case no non-scalar variables are used in the expression diff_vars = self.find_differential_variables(code.values()) self.add_gsl_variables_as_non_scalar(diff_vars) # add arrays we want to use in generated code before self.generator.translate() so # brian does namespace unpacking for us pointer_names = self.add_meta_variables(self.method_options) scalar_statements = {} vector_statements = {} for ac_name, ac_code in code.iteritems(): statements = make_statements(ac_code, self.variables, dtype, optimise=True, blockname=ac_name) scalar_statements[ac_name], vector_statements[ac_name] = statements for vs in vector_statements.itervalues(): # Check that the statements are meaningful independent on the order of # execution (e.g. for synapses) try: if self.has_repeated_indices(vs): # only do order dependence if there are repeated indices check_for_order_independence(vs, self.generator.variables, self.generator.variable_indices) except OrderDependenceError: # If the abstract code is only one line, display it in ful l if len(vs) <= 1: error_msg = 'Abstract code: "%s"\n' % vs[0] else: error_msg = ('%_GSL_driver lines of abstract code, first line is: ' '"%s"\n') % (len(vs), vs[0]) # save function names because self.generator.translate_statement_sequence # deletes these from self.variables but we need to know which identifiers # we can safely ignore (i.e. we can ignore the functions because they are # handled by the original generator) self.function_names = self.find_function_names() scalar_code, vector_code, kwds = self.generator.translate_statement_sequence(scalar_statements, vector_statements) ############ translate code for GSL # first check if any indexing other than '_idx' is used (currently not supported) for code_list in scalar_code.values()+vector_code.values(): for code in code_list: m = re.search('\[(\w+)\]', code) if m is not None: if m.group(1) != '0' and m.group(1) != '_idx': from brian2.stateupdaters.base import UnsupportedEquationsException raise UnsupportedEquationsException(("Equations result in state " "updater code with indexing " "other than '_idx', which " "is currently not supported " "in combination with the " "GSL stateupdater.")) # differential variable specific operations to_replace = self.diff_var_to_replace(diff_vars) GSL_support_code = self.get_dimension_code(len(diff_vars)) GSL_support_code += self.yvector_code(diff_vars) # analyze all needed variables; if not in self.variables: put in separate dic. # also keep track of variables needed for scalar statements and vector statements other_variables = self.find_undefined_variables(scalar_statements[None] + vector_statements[None]) variables_in_scalar = self.find_used_variables(scalar_statements[None], other_variables) variables_in_vector = self.find_used_variables(vector_statements[None], other_variables) # so that _dataholder holds diff_vars as well, even if they don't occur # in the actual statements for var in diff_vars.keys(): if not var in variables_in_vector: variables_in_vector[var] = self.variables[var] # lets keep track of the variables that eventually need to be added to # the _GSL_dataholder somehow self.variables_to_be_processed = variables_in_vector.keys() # add code for _dataholder struct GSL_support_code = self.write_dataholder(variables_in_vector) + GSL_support_code # add e.g. _lio_1 --> _GSL_dataholder._lio_1 to replacer to_replace.update(self.to_replace_vector_vars(variables_in_vector, ignore=diff_vars.keys())) # write statements that unpack (python) namespace to _dataholder struct # or local namespace GSL_main_code = self.unpack_namespace(variables_in_vector, variables_in_scalar, ['t']) # rewrite actual calculations described by vector_code and put them in _GSL_func func_code = self.translate_one_statement_sequence(vector_statements[None], scalar=False) GSL_support_code += self.make_function_code(self.translate_vector_code(func_code, to_replace)) scalar_func_code = self.translate_one_statement_sequence(scalar_statements[None], scalar=True) # rewrite scalar code, keep variables that are needed in scalar code normal # and add variables to _dataholder for vector_code GSL_main_code += '\n' + self.translate_scalar_code(scalar_func_code, variables_in_scalar, variables_in_vector) if len(self.variables_to_be_processed) > 0: raise AssertionError(("Not all variables that will be used in the vector " "code have been added to the _GSL_dataholder. This " "might mean that the _GSL_func is using unitialized " "variables." "\nThe unprocessed variables " "are: %s" % (str(self.variables_to_be_processed)))) scalar_code['GSL'] = GSL_main_code kwds['define_GSL_scale_array'] = self.scale_array_code(diff_vars, self.method_options) kwds['n_diff_vars'] = len(diff_vars) kwds['GSL_settings'] = dict(self.method_options) kwds['GSL_settings']['integrator'] = self.integrator kwds['support_code_lines'] += GSL_support_code.split('\n') kwds['t_array'] = self.get_array_name(self.variables['t']) + '[0]' kwds['dt_array'] = self.get_array_name(self.variables['dt']) + '[0]' kwds['define_dt'] = 'dt' not in variables_in_scalar kwds['cpp_standalone'] = self.is_cpp_standalone() for key, value in pointer_names.items(): kwds[key] = value return scalar_code, vector_code, kwds