def add_state_updates(state_shape_variables_updates, neuron): # type: (map[str, str], ASTNeuron) -> ASTNeuron """ Adds all update instructions as contained in the solver output to the update block of the neuron. :param state_shape_variables_updates: map of variables to corresponding updates during the update step. :param neuron: a single neuron :return: a modified version of the neuron """ for variable in state_shape_variables_updates: declaration_statement = variable + '__tmp real = ' + state_shape_variables_updates[variable] add_declaration_to_update_block(ModelParser.parse_declaration(declaration_statement), neuron) for variable in state_shape_variables_updates: add_assignment_to_update_block(ModelParser.parse_assignment(variable + ' = ' + variable + '__tmp'), neuron) return neuron
def replace_integrate_call(neuron, update_instructions): # type: (...) -> ASTNeuron """ Replaces all integrate calls to the corresponding references to propagation. :param neuron: a single neuron instance :return: The neuron without an integrate calls. The function calls are replaced through an incremental exact solution, """ integrate_call = ASTUtils.get_function_call(neuron.get_update_blocks(), PredefinedFunctions.INTEGRATE_ODES) # by construction of a valid neuron, only a single integrate call should be there if isinstance(integrate_call, list): integrate_call = integrate_call[0] if integrate_call is not None: small_statement = neuron.get_parent(integrate_call) assert (small_statement is not None and isinstance(small_statement, ASTSmallStmt)) block = neuron.get_parent(neuron.get_parent(small_statement)) assert (block is not None and isinstance(block, ASTBlock)) for i in range(0, len(block.get_stmts())): if block.get_stmts()[i].equals(neuron.get_parent(small_statement)): del block.get_stmts()[i] block.get_stmts()[i:i] = list((ModelParser.parse_stmt(prop) for prop in update_instructions)) break return neuron
def apply_incoming_spikes(neuron): """ Adds a set of update instructions to the handed over neuron. :param neuron: a single neuron instance :type neuron: ASTNeuron :return: the modified neuron :rtype: ASTNeuron """ assert (neuron is not None and isinstance(neuron, ASTNeuron)), \ '(PyNestML.Solver.BaseTransformer) No or wrong type of neuron provided (%s)!' % type(neuron) conv_calls = OdeTransformer.get_sum_function_calls(neuron) printer = ExpressionsPrettyPrinter() spikes_updates = list() for convCall in conv_calls: shape = convCall.get_args()[0].get_variable().get_complete_name() buffer = convCall.get_args()[1].get_variable().get_complete_name() initial_values = ( neuron.get_initial_values_blocks().get_declarations() if neuron.get_initial_values_blocks() is not None else list()) for astDeclaration in initial_values: for variable in astDeclaration.get_variables(): if re.match(shape + "[\']*", variable.get_complete_name()) or re.match(shape + '__[\\d]+$', variable.get_complete_name()): spikes_updates.append(ModelParser.parse_assignment( variable.get_complete_name() + " += " + buffer + " * " + printer.print_expression( astDeclaration.get_expression()))) for update in spikes_updates: add_assignment_to_update_block(update, neuron) return neuron
def test_function_call_with_comments(self): function_call = '\n/* pre */\n' \ 'min(1,2) # in\n' \ '/* post */\n\n' model = ModelParser.parse_stmt(function_call) model_printer = ASTNestMLPrinter() self.assertEqual(function_call, model_printer.print_node(model))
def test_for_stmt_with_comments(self): stmt = '\n/*pre*/\n' \ 'for i in 10 - 3.14...10 + 3.14 step -1: # in\n' \ 'end\n' \ '/*post*/\n\n' model = ModelParser.parse_for_stmt(stmt) model_printer = ASTNestMLPrinter() self.assertEqual(stmt, model_printer.print_node(model))
def test(self): # Todo: this test is not yet complete, @ptraeder complete it Logger.init_logger(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'resources', 'MagnitudeCompatibilityTest.nestml')))) # Logger.setCurrentNeuron(model.getNeuronList()[0]) ExpressionTestVisitor().handle(model)
def test_update_block_with_comments(self): block = '\n/*pre*/\n' \ 'update: # in\n' \ 'end\n' \ '/*post*/\n\n' model = ModelParser.parse_update_block(block) model_printer = ASTNestMLPrinter() self.assertEqual(block, model_printer.print_node(model))
def test_while_stmt_with_comments(self): stmt = '\n/*pre*/\n' \ 'while true: # in \n' \ 'end\n' \ '/*post*/\n\n' model = ModelParser.parse_while_stmt(stmt) model_printer = ASTNestMLPrinter() self.assertEqual(stmt, model_printer.print_node(model))
def test_neuron_with_comments(self): neuron = '\n/*pre*/\n' \ 'neuron test: # in\n' \ 'end\n' \ '/*post*/\n\n' model = ModelParser.parse_neuron(neuron) model_printer = ASTNestMLPrinter() self.assertEqual(neuron, model_printer.print_node(model))
def test_function_without_comments(self): t_function = 'function test(Tau_1 ms) real:\n' \ ' exact_integration_adjustment real = ((1 / Tau_2) - (1 / Tau_1)) * ms\n' \ ' return normalisation_factor\n' \ 'end\n' model = ModelParser.parse_function(t_function) model_printer = ASTNestMLPrinter() self.assertEqual(t_function, model_printer.print_node(model))
def test_declaration_with_comments(self): declaration = '\n' \ '/*pre*/\n' \ 'test mV = 10mV # in\n' \ '/*post*/\n\n' model = ModelParser.parse_declaration(declaration) model_printer = ASTNestMLPrinter() self.assertEqual(declaration, model_printer.print_node(model))
def test_solve_odes_and_shapes(self): input_path = str(os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.join( os.pardir, 'models', 'iaf_psc_alpha.nestml')))) compilation_unit = ModelParser.parse_model(input_path) assert len(compilation_unit.get_neuron_list()) == 1 ast_neuron = compilation_unit.get_neuron_list()[0] nestCodeGenerator = NESTCodeGenerator() ast_neuron = nestCodeGenerator.transform_shapes_and_odes(ast_neuron, {})
def test_assignment_with_comments(self): assignment = '\n' \ '/* pre */\n' \ 'a = b # in\n' \ '/* post */\n' \ '\n' model = ModelParser.parse_assignment(assignment) model_printer = ASTNestMLPrinter() self.assertEqual(assignment, model_printer.print_node(model))
def test_invalid_element_defined_after_usage(self): model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'resources')), 'random_number_generators_test.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_neuron( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test(self): # Todo: this test is not yet complete, @ptraeder complete it Logger.init_logger(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'resources', 'MagnitudeCompatibilityTest.nestml')))) # Logger.setCurrentNeuron(model.getNeuronList()[0]) ExpressionTestVisitor().handle(model)
def test_block_with_variables_with_comments(self): block = '# pre1\n' \ '# pre2\n' \ 'state: # in\n' \ 'end\n' \ '# post1\n' \ '# post2\n\n' model = ModelParser.parse_block_with_variables(block) model_printer = ASTNestMLPrinter() self.assertEqual(block, model_printer.print_node(model))
def test_invalid_coco_state_variables_initialized(self): """ Test that the CoCo condition is applicable for all the variables in the state block not initialized """ Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoStateVariablesInitialized.nestml')) self.assertEqual(len( Logger.get_all_messages_of_level_and_or_node(model.get_neuron_list()[0], LoggingLevel.ERROR)), 2)
def test_invalid_coco_kernel_type_initial_values(self): """ Test the functionality of CoCoKernelType. """ Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoKernelTypeInitialValues.nestml')) self.assertEqual(len( Logger.get_all_messages_of_level_and_or_node(model.get_neuron_list()[0], LoggingLevel.ERROR)), 4)
def test_invalid_element_defined_after_usage(self): model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoVariableDefinedAfterUsage.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_neuron( model.get_neuron_list()[0], LoggingLevel.ERROR)), 2)
def add_declaration_to_initial_values(neuron, variable, initial_value): # type: (ASTNeuron, str, str) -> ASTNeuron """ Adds a single declaration to the initial values block of the neuron. :param neuron: a neuron :param variable: state variable to add :param initial_value: corresponding initial value :return: a modified neuron """ tmp = ModelParser.parse_expression(initial_value) vector_variable = ASTUtils.get_vectorized_variable(tmp, neuron.get_scope()) declaration_string = variable + ' real' + ( '[' + vector_variable.get_vector_parameter() + ']' if vector_variable is not None and vector_variable.has_vector_parameter() else '') + ' = ' + initial_value ast_declaration = ModelParser.parse_declaration(declaration_string) if vector_variable is not None: ast_declaration.set_size_parameter(vector_variable.get_vector_parameter()) neuron.add_to_initial_values_block(ast_declaration) return neuron
def test_solve_odes_and_shapes(self): path = str( os.path.realpath( os.path.join( os.path.dirname(__file__), os.path.join('..', 'models', 'iaf_psc_alpha.nestml')))) compilation_unit = ModelParser.parse_model(path) assert len(compilation_unit.get_neuron_list()) == 1 ast_neuron = compilation_unit.get_neuron_list()[0] ast_neuron = transform_shapes_and_odes(ast_neuron, {})
def process(): """ Returns ------- errors_occurred : bool Flag indicating whether errors occurred during processing """ errors_occurred = False # init log dir create_report_dir() # The handed over parameters seem to be correct, proceed with the main routine init_predefined() # now proceed to parse all models compilation_units = list() nestml_files = FrontendConfiguration.get_files() if not type(nestml_files) is list: nestml_files = [nestml_files] for nestml_file in nestml_files: parsed_unit = ModelParser.parse_model(nestml_file) if parsed_unit is not None: compilation_units.append(parsed_unit) if len(compilation_units) > 0: # generate a list of all neurons neurons = list() for compilationUnit in compilation_units: neurons.extend(compilationUnit.get_neuron_list()) # check if across two files two neurons with same name have been defined CoCosManager.check_not_two_neurons_across_units(compilation_units) # now exclude those which are broken, i.e. have errors. if not FrontendConfiguration.is_dev: for neuron in neurons: if Logger.has_errors(neuron): code, message = Messages.get_neuron_contains_errors( neuron.get_name()) Logger.log_message( node=neuron, code=code, message=message, error_position=neuron.get_source_position(), log_level=LoggingLevel.INFO) neurons.remove(neuron) errors_occurred = True # perform code generation _codeGenerator = CodeGenerator( target=FrontendConfiguration.get_target()) _codeGenerator.generate_code(neurons) for neuron in neurons: if Logger.has_errors(neuron): errors_occurred = True break if FrontendConfiguration.store_log: store_log_to_file() return errors_occurred
def add_declaration_to_internals(neuron, variable_name, init_expression): # type: (ASTNeuron, str, str) -> ASTNeuron """ Adds the variable as stored in the declaration tuple to the neuron. :param neuron: a single neuron instance :param variable_name: the name of the variable to add :param init_expression: initialization expression :return: the neuron extended by the variable """ tmp = ModelParser.parse_expression(init_expression) vector_variable = ASTUtils.get_vectorized_variable(tmp, neuron.get_scope()) declaration_string = variable_name + ' real' + ( '[' + vector_variable.get_vector_parameter() + ']' if vector_variable is not None and vector_variable.has_vector_parameter() else '') + ' = ' + init_expression ast_declaration = ModelParser.parse_declaration(declaration_string) if vector_variable is not None: ast_declaration.set_size_parameter(vector_variable.get_vector_parameter()) neuron.add_to_internal_block(ast_declaration) return neuron
def test(self): Logger.init_logger(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), 'resources', 'ExpressionTypeTest.nestml')))) Logger.set_current_neuron(model.get_neuron_list()[0]) model.accept(ExpressionTestVisitor()) # ExpressionTestVisitor().handle(model) Logger.set_current_neuron(None) self.assertEqual(len(Logger.get_all_messages_of_level_and_or_neuron(model.get_neuron_list()[0], LoggingLevel.ERROR)), 2)
def test_valid_element_in_same_line(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoElementInSameLine.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_invalid_integrate_odes_called_if_equations_defined(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoIntegrateOdesCalledIfEquationsDefined.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 1)
def test_valid_function_unique_and_defined(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoFunctionNotUnique.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_vector_in_non_vector_declaration_detected(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoVectorInNonVectorDeclaration.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_compound_expression_correctly_typed(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CompoundOperatorWithDifferentButCompatibleUnits.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_ode_correctly_typed(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoOdeCorrectlyTyped.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_no_values_assigned_to_input_ports(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoValueAssignedToInputPort.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_invalid_order_of_equations_correct(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoNoOrderOfEquations.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 2)
def test_valid_parameters_assigned_only_in_parameters_block(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoParameterAssignedOutsideBlock.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_no_nest_collision(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoNestNamespaceCollision.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_redundant_input_port_keywords_detected(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoInputPortWithRedundantTypes.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_inline_expression_has_several_lhs(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoInlineExpressionWithSeveralLhs.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_spike_input_port_without_datatype(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoSpikeInputPortWithoutType.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_function_with_wrong_arg_number_detected(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoFunctionCallNotConsistentWrongArgNumber.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_invalid_each_block_unique(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoEachBlockUnique.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 2)
def test_invalid_convolve_correctly_parameterized(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'invalid')), 'CoCoConvolveNotCorrectlyParametrized.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 1)
def test_valid_init_values_have_rhs_and_ode(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoInitValuesWithoutOde.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.WARNING)), 2)
def test_valid_incorrect_return_stmt_detected(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoIncorrectReturnStatement.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_ode_vars_outside_init_block_detected(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoOdeVarNotInInitialValues.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_continuous_input_ports_not_specified(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoContinuousInputPortQualifierSpecified.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def test_valid_numerator_of_unit_one(self): Logger.set_logging_level(LoggingLevel.INFO) model = ModelParser.parse_model( os.path.join( os.path.realpath( os.path.join(os.path.dirname(__file__), 'valid')), 'CoCoUnitNumeratorNotOne.nestml')) self.assertEqual( len( Logger.get_all_messages_of_level_and_or_node( model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)
def integrate_delta_solution(equations_block, neuron, shape, shape_to_buffers): def ode_is_lin_const_coeff(ode_symbol, ode_definition, shapes): """ TODO: improve the original code: the function should take a list of shape names, not objects :param ode_symbol string encoding the LHS :param ode_definition string encoding RHS :param shapes A list with shape names :return true iff the ode definition is a linear and constant coefficient ODE """ ode_symbol_sp = parse_expr(ode_symbol) ode_definition_sp = parse_expr(ode_definition) # Check linearity ddvar = diff(diff(ode_definition_sp, ode_symbol_sp), ode_symbol_sp) if simplify(ddvar) != 0: return False # Check coefficients dvar = diff(ode_definition_sp, ode_symbol_sp) for shape in shapes: for symbol in dvar.free_symbols: if shape == str(symbol): return False return True ode_lhs = str(equations_block.get_ode_equations()[0].get_lhs().get_name()) ode_rhs = str(equations_block.get_ode_equations()[0].get_rhs()) shape_name = shape.get_variable().get_name() if not (ode_is_lin_const_coeff(ode_lhs, ode_rhs, [shape_name])): raise Exception("Cannot handle delta shape in a not linear constant coefficient ODE.") ode_lhs = parse_expr(ode_lhs) ode_rhs = parse_expr(ode_rhs) # constant input const_input = simplify(1 / diff(ode_rhs, parse_expr(shape_name)) * (ode_rhs - diff(ode_rhs, ode_lhs) * ode_lhs) - (parse_expr(shape_name))) # ode var factor c1 = diff(ode_rhs, ode_lhs) # The symbol must be declared again. Otherwise, the right hand side will be used for the derivative c2 = diff(ode_rhs, parse_expr(shape_name)) # tau is passed as the second argument of the 'delta' function tau_constant = parse_expr(str(shape.get_expression().get_function_call().get_args()[1])) ode_var_factor = exp(-Symbol('__h') / tau_constant) ode_var_update_instructions = [ str(ode_lhs) + " = " + str(ode_var_factor * ode_lhs), str(ode_lhs) + " += " + str(simplify(c2 / c1 * (exp(Symbol('__h') * c1) - 1)) * const_input)] for k in shape_to_buffers: ode_var_update_instructions.append(str(ode_lhs) + " += " + shape_to_buffers[k]) neuron.add_to_internal_block(ModelParser.parse_declaration('__h ms = resolution()')) replace_integrate_call(neuron, ode_var_update_instructions) return neuron
def test_block_with_variables_with_comments(self): block = '\n' \ '/* pre1\n' \ '* pre2\n' \ '*/\n' \ 'state: # in\n' \ 'end\n' \ '/* post1\n' \ '* post2\n' \ '*/\n\n' model = ModelParser.parse_block_with_variables(block) model_printer = ASTNestMLPrinter() self.assertEqual(block, model_printer.print_node(model))
def test_function_with_comments(self): t_function = '\n' \ '/*pre func*/\n' \ 'function test(Tau_1 ms) real: # in func\n\n' \ ' /* decl pre */\n' \ ' exact_integration_adjustment real = ((1 / Tau_2) - (1 / Tau_1)) * ms # decl in\n' \ ' /* decl post */\n' \ '\n' \ ' return normalisation_factor\n' \ 'end\n' \ '/*post func*/\n\n' model = ModelParser.parse_function(t_function) model_printer = ASTNestMLPrinter() self.assertEqual(t_function, model_printer.print_node(model))
def apply_spikes_from_buffers(self, neuron, shape_to_buffers): """generate the equations that update the dynamical variables when incoming spikes arrive. For example, a resulting `assignment_string` could be "I_shape_in += (in_spikes/nS) * 1". The definition of the spike kernel shape is then set to 0. """ spike_updates = [] initial_values = neuron.get_initial_values_blocks() for declaration in initial_values.get_declarations(): variable = declaration.get_variables()[0] for shape in shape_to_buffers: matcher_computed_shape_odes = re.compile(shape + r"(__\d+)?") if re.match(matcher_computed_shape_odes, str(variable)): buffer_type = neuron.get_scope(). \ resolve_to_symbol(shape_to_buffers[shape], SymbolKind.VARIABLE).get_type_symbol() assignment_string = variable.get_complete_name() + " += (" + shape_to_buffers[ shape] + '/' + buffer_type.print_nestml_type() + ") * " + \ self._printer.print_expression(declaration.get_expression()) spike_updates.append(ModelParser.parse_assignment(assignment_string)) # the IV is applied. can be reset declaration.set_expression(ModelParser.parse_expression("0")) for assignment in spike_updates: add_assignment_to_update_block(assignment, neuron)
def make_functions_self_contained(self, functions): # type: (list(ASTOdeFunction)) -> list(ASTOdeFunction) """ TODO: it should be a method inside of the ASTOdeFunction TODO by KP: this should be done by means of a visitor Make function definition self contained, e.g. without any references to functions from `functions`. :param functions: A sorted list with entries ASTOdeFunction. :return: A list with ASTOdeFunctions. Defining expressions don't depend on each other. """ for source in functions: for target in functions: matcher = re.compile(self._variable_matching_template.format(source.get_variable_name())) target_definition = str(target.get_expression()) target_definition = re.sub(matcher, "(" + str(source.get_expression()) + ")", target_definition) target.expression = ModelParser.parse_expression(target_definition) return functions
def replace_functions_through_defining_expressions(self, definitions, functions): # type: (list(ASTOdeEquation), list(ASTOdeFunction)) -> list(ASTOdeFunction) """ Refractors symbols form `functions` in `definitions` with corresponding defining expressions from `functions`. :param definitions: A sorted list with entries {"symbol": "name", "definition": "expression"} that should be made free from. :param functions: A sorted list with entries {"symbol": "name", "definition": "expression"} with functions which must be replaced in `definitions`. :return: A list with definitions. Expressions in `definitions` don't depend on functions from `functions`. """ for fun in functions: for target in definitions: matcher = re.compile(self._variable_matching_template.format(fun.get_variable_name())) target_definition = str(target.get_rhs()) target_definition = re.sub(matcher, "(" + str(fun.get_expression()) + ")", target_definition) target.rhs = ModelParser.parse_expression(target_definition) return definitions
def test(self): model = ModelParser.parse_model( os.path.join( os.path.realpath(os.path.join(os.path.dirname(__file__), 'resources', 'ResolutionTest.nestml')))) scope = model.get_neuron_list()[0].get_scope() res1 = scope.resolve_to_all_scopes('test1', SymbolKind.VARIABLE) self.assertTrue(res1 is not None) res2 = scope.resolve_to_all_scopes('testNot', SymbolKind.VARIABLE) self.assertTrue(res2 is None) res3 = scope.resolve_to_all_scopes('test2', SymbolKind.VARIABLE) self.assertTrue(res3 is not None and res3.get_scope_type() == ScopeType.FUNCTION) res4 = scope.resolve_to_all_scopes('arg1', SymbolKind.VARIABLE) self.assertTrue(res4 is not None and res4.get_scope_type() == ScopeType.FUNCTION) res5 = scope.resolve_to_all_scopes('test3', SymbolKind.VARIABLE) self.assertTrue(res5 is not None and res5.get_scope_type() == ScopeType.FUNCTION) res6 = scope.resolve_to_all_scopes('test1', SymbolKind.FUNCTION) self.assertTrue(res6 is not None and res6.get_scope_type() == ScopeType.GLOBAL) res7 = scope.resolve_to_all_scopes('test6', SymbolKind.VARIABLE) self.assertTrue(res7 is not None and res7.get_scope_type() == ScopeType.UPDATE)
def test_model_preparation(self): input_path = str(os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.join( os.pardir, 'models', 'iaf_psc_alpha.nestml')))) compilation_unit = ModelParser.parse_model(input_path) assert len(compilation_unit.get_neuron_list()) == 1 ast_neuron = compilation_unit.get_neuron_list()[0] equations_block = ast_neuron.get_equations_block() # the idea here is to go through the rhs, print expressions, use the same mechanism as before, and reread them # again # TODO: add tests for this function # this function changes stuff inplace nestCodeGenerator = NESTCodeGenerator() nestCodeGenerator.make_functions_self_contained(equations_block.get_ode_functions()) nestCodeGenerator.replace_functions_through_defining_expressions(equations_block.get_ode_equations(), equations_block.get_ode_functions()) json_representation = nestCodeGenerator.transform_ode_and_shapes_to_json(equations_block) self.assertTrue("convolve(I_shape_in, in_spikes)" in json_representation["odes"][0]["definition"]) self.assertTrue("convolve(I_shape_ex, ex_spikes)" in json_representation["odes"][0]["definition"])
def integrate_exact_solution(neuron, exact_solution): # type: (ASTNeuron, map[str, list]) -> ASTNeuron """ Adds a set of instructions to the given neuron as stated in the solver output. :param neuron: a single neuron instance :param exact_solution: exact solution :return: a modified neuron with integrated exact solution and without equations block """ neuron.add_to_internal_block(ModelParser.parse_declaration('__h ms = resolution()')) neuron = add_declarations_to_internals(neuron, exact_solution["propagator"]) state_shape_variables_declarations = compute_state_shape_variables_declarations(exact_solution) neuron = add_declarations_to_initial_values(neuron, state_shape_variables_declarations) state_shape_variables_updates = compute_state_shape_variables_updates(exact_solution) neuron = add_state_updates(state_shape_variables_updates, neuron) neuron = replace_integrate_call(neuron, exact_solution["ode_updates"]) return neuron
def functional_shapes_to_odes(neuron, transformed_shapes): # type: (ASTNeuron, map[str, list]) -> ASTNeuron """ Adds a set of instructions to the given neuron as stated in the solver output. :param neuron: a single neuron instance :param transformed_shapes: ode-toolbox output that encodes transformation of functional shapes to odes with IV :return: the source neuron with modified equations block without functional shapes """ shape_names, shape_name_to_initial_values, shape_name_to_shape_state_variables, shape_state_variable_to_ode = \ _convert_to_shape_specific(transformed_shapes) # delete original functions shapes. they will be replaced though ode-shapes computed through ode-toolbox. shapes_to_delete = [] # you should not delete elements from list during iterating the list for declaration in neuron.get_equations_block().get_declarations(): if isinstance(declaration, ASTOdeShape): if declaration.get_variable().get_name() in shape_names: shapes_to_delete.append(declaration) for declaration in shapes_to_delete: neuron.get_equations_block().get_declarations().remove(declaration) state_shape_variables_declarations = {} for shape_name in shape_names: for variable, initial_value in zip( shape_name_to_shape_state_variables[shape_name], shape_name_to_initial_values[shape_name]): state_shape_variables_declarations[variable] = initial_value for variable, shape_state_ode in zip( shape_name_to_shape_state_variables[shape_name], shape_state_variable_to_ode[shape_name]): ode_shape = ModelParser.parse_ode_shape("shape " + variable + "' = " + shape_state_ode) neuron.add_shape(ode_shape) neuron = add_declarations_to_initial_values(neuron, state_shape_variables_declarations) return neuron
def test_block_with_variables_without_comments(self): block = 'state:\n' \ 'end' model = ModelParser.parse_block_with_variables(block) model_printer = ASTNestMLPrinter() self.assertEqual(block, model_printer.print_node(model))
def test_unit_type(self): unit = '1/(mV*kg**2)' model = ModelParser.parse_unit_type(unit) model_printer = ASTNestMLPrinter() self.assertEqual(unit, model_printer.print_node(model))
def test_unary_operator(self): ops = {'-', '+', '~'} for op in ops: model = ModelParser.parse_unary_operator(op) model_printer = ASTNestMLPrinter() self.assertEqual(op, model_printer.print_node(model))
def test_assignment_without_comments(self): assignment = 'a = b\n' model = ModelParser.parse_assignment(assignment) model_printer = ASTNestMLPrinter() self.assertEqual(assignment, model_printer.print_node(model))