def analyse_neuron(self, neuron): # type: (ASTNeuron) -> None """ Analyse and transform a single neuron. :param neuron: a single neuron. """ code, message = Messages.get_start_processing_neuron(neuron.get_name()) Logger.log_message(neuron, code, message, neuron.get_source_position(), LoggingLevel.INFO) # make normalization # apply spikes to buffers # get rid of convolve, store them and apply then at the end equations_block = neuron.get_equations_block() shape_to_buffers = {} if neuron.get_equations_block() is not None: # extract function names and corresponding incoming buffers convolve_calls = OdeTransformer.get_sum_function_calls(equations_block) for convolve in convolve_calls: shape_to_buffers[str(convolve.get_args()[0])] = str(convolve.get_args()[1]) OdeTransformer.refactor_convolve_call(neuron.get_equations_block()) self.make_functions_self_contained(equations_block.get_ode_functions()) self.replace_functions_through_defining_expressions(equations_block.get_ode_equations(), equations_block.get_ode_functions()) # transform everything into gsl processable (e.g. no functional shapes) or exact form. self.transform_shapes_and_odes(neuron, shape_to_buffers) self.apply_spikes_from_buffers(neuron, shape_to_buffers) # update the symbol table symbol_table_visitor = ASTSymbolTableVisitor() symbol_table_visitor.after_ast_rewrite_ = True # ODE block might have been removed entirely: suppress warnings neuron.accept(symbol_table_visitor)
def analyse_neuron(self, neuron): # type: (ASTNeuron) -> None """ Analyse and transform a single neuron. :param neuron: a single neuron. """ code, message = Messages.get_start_processing_neuron(neuron.get_name()) Logger.log_message(neuron, code, message, neuron.get_source_position(), LoggingLevel.INFO) # make normalization # apply spikes to buffers # get rid of convolve, store them and apply then at the end equations_block = neuron.get_equations_block() shape_to_buffers = {} if neuron.get_equations_block() is not None: # extract function names and corresponding incoming buffers convolve_calls = OdeTransformer.get_sum_function_calls( equations_block) for convolve in convolve_calls: shape_to_buffers[str(convolve.get_args()[0])] = str( convolve.get_args()[1]) OdeTransformer.refactor_convolve_call(neuron.get_equations_block()) self.make_functions_self_contained( equations_block.get_ode_functions()) self.replace_functions_through_defining_expressions( equations_block.get_ode_equations(), equations_block.get_ode_functions()) # transform everything into gsl processable (e.g. no functional shapes) or exact form. self.transform_shapes_and_odes(neuron, shape_to_buffers) self.apply_spikes_from_buffers(neuron, shape_to_buffers) # update the symbol table symbol_table_visitor = ASTSymbolTableVisitor() symbol_table_visitor.after_ast_rewrite_ = True # ODE block might have been removed entirely: suppress warnings neuron.accept(symbol_table_visitor)
def get_spike_update_expressions(self, neuron: ASTNeuron, kernel_buffers, solver_dicts, delta_factors) -> List[ASTAssignment]: """ Generate the equations that update the dynamical variables when incoming spikes arrive. To be invoked after ode-toolbox. For example, a resulting `assignment_str` could be "I_kernel_in += (in_spikes/nS) * 1". The values are taken from the initial values for each corresponding dynamical variable, either from ode-toolbox or directly from user specification in the model. Note that for kernels, `initial_values` actually contains the increment upon spike arrival, rather than the initial value of the corresponding ODE dimension. """ spike_updates = [] initial_values = neuron.get_initial_values_blocks() for kernel, spike_input_port in kernel_buffers: if neuron.get_scope().resolve_to_symbol(str(spike_input_port), SymbolKind.VARIABLE) is None: continue buffer_type = neuron.get_scope().resolve_to_symbol(str(spike_input_port), SymbolKind.VARIABLE).get_type_symbol() if is_delta_kernel(kernel): continue for kernel_var in kernel.get_variables(): for var_order in range(get_kernel_var_order_from_ode_toolbox_result(kernel_var.get_name(), solver_dicts)): kernel_spike_buf_name = construct_kernel_X_spike_buf_name( kernel_var.get_name(), spike_input_port, var_order) expr = get_initial_value_from_ode_toolbox_result(kernel_spike_buf_name, solver_dicts) assert expr is not None, "Initial value not found for kernel " + kernel_var expr = str(expr) if expr in ["0", "0.", "0.0"]: continue # skip adding the statement if we're only adding zero assignment_str = kernel_spike_buf_name + " += " assignment_str += "(" + str(spike_input_port) + ")" if not expr in ["1.", "1.0", "1"]: assignment_str += " * (" + \ self._printer.print_expression(ModelParser.parse_expression(expr)) + ")" if not buffer_type.print_nestml_type() in ["1.", "1.0", "1"]: assignment_str += " / (" + buffer_type.print_nestml_type() + ")" ast_assignment = ModelParser.parse_assignment(assignment_str) ast_assignment.update_scope(neuron.get_scope()) ast_assignment.accept(ASTSymbolTableVisitor()) spike_updates.append(ast_assignment) for k, factor in delta_factors.items(): var = k[0] inport = k[1] assignment_str = var.get_name() + "'" * (var.get_differential_order() - 1) + " += " if not factor in ["1.", "1.0", "1"]: assignment_str += "(" + self._printer.print_expression(ModelParser.parse_expression(factor)) + ") * " assignment_str += str(inport) ast_assignment = ModelParser.parse_assignment(assignment_str) ast_assignment.update_scope(neuron.get_scope()) ast_assignment.accept(ASTSymbolTableVisitor()) spike_updates.append(ast_assignment) return spike_updates
def update_symbol_table(self, neuron, kernel_buffers): """ Update symbol table and scope. """ SymbolTable.delete_neuron_scope(neuron.get_name()) symbol_table_visitor = ASTSymbolTableVisitor() symbol_table_visitor.after_ast_rewrite_ = True neuron.accept(symbol_table_visitor) SymbolTable.add_neuron_scope(neuron.get_name(), neuron.get_scope())
def make_inline_expressions_self_contained( self, inline_expressions: List[ASTInlineExpression] ) -> List[ASTInlineExpression]: """ Make inline_expressions self contained, i.e. without any references to other inline_expressions. TODO: it should be a method inside of the ASTInlineExpression TODO: this should be done by means of a visitor :param inline_expressions: A sorted list with entries ASTInlineExpression. :return: A list with ASTInlineExpressions. Defining expressions don't depend on each other. """ for source in inline_expressions: source_position = source.get_source_position() for target in inline_expressions: 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) target.expression.update_scope(source.get_scope()) target.expression.accept(ASTSymbolTableVisitor()) def log_set_source_position(node): if node.get_source_position().is_added_source_position(): node.set_source_position(source_position) target.expression.accept( ASTHigherOrderVisitor(visit_funcs=log_set_source_position)) return inline_expressions
def replace_inline_expressions_through_defining_expressions( self, definitions, inline_expressions): # type: (list(ASTOdeEquation), list(ASTInlineExpression)) -> list(ASTInlineExpression) """ Replaces symbols from `inline_expressions` in `definitions` with corresponding defining expressions from `inline_expressions`. :param definitions: A sorted list with entries {"symbol": "name", "definition": "expression"} that should be made free from. :param inline_expressions: A sorted list with entries {"symbol": "name", "definition": "expression"} with inline_expressions which must be replaced in `definitions`. :return: A list with definitions. Expressions in `definitions` don't depend on inline_expressions from `inline_expressions`. """ for m in inline_expressions: source_position = m.get_source_position() for target in definitions: matcher = re.compile( self._variable_matching_template.format( m.get_variable_name())) target_definition = str(target.get_rhs()) target_definition = re.sub(matcher, "(" + str(m.get_expression()) + ")", target_definition) target.rhs = ModelParser.parse_expression(target_definition) target.update_scope(m.get_scope()) target.accept(ASTSymbolTableVisitor()) def log_set_source_position(node): if node.get_source_position().is_added_source_position(): node.set_source_position(source_position) target.accept( ASTHigherOrderVisitor(visit_funcs=log_set_source_position)) return definitions
def add_declaration_to_state_block(neuron: ASTNeuron, variable: str, initial_value: str) -> ASTNeuron: """ Adds a single declaration to the state block of the neuron. The declared variable is of type real. :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_state_block(ast_declaration) ast_declaration.update_scope(neuron.get_state_blocks().get_scope()) symtable_visitor = ASTSymbolTableVisitor() symtable_visitor.block_type_stack.push(BlockType.STATE) ast_declaration.accept(symtable_visitor) symtable_visitor.block_type_stack.pop() return neuron
def analyse_and_generate_neuron(neuron): # type: (ASTNeuron) -> None """ Analysis a single neuron, solves it and generates the corresponding code. :param neuron: a single neuron. """ code, message = Messages.get_start_processing_neuron(neuron.get_name()) Logger.log_message(neuron, code, message, neuron.get_source_position(), LoggingLevel.INFO) # make normalization # apply spikes to buffers # get rid of convolve, store them and apply then at the end equations_block = neuron.get_equations_block() shape_to_buffers = {} if neuron.get_equations_block() is not None: # extract function names and corresponding incoming buffers convolve_calls = OdeTransformer.get_sum_function_calls(equations_block) for convolve in convolve_calls: shape_to_buffers[str(convolve.get_args()[0])] = str(convolve.get_args()[1]) OdeTransformer.refactor_convolve_call(neuron.get_equations_block()) make_functions_self_contained(equations_block.get_ode_functions()) replace_functions_through_defining_expressions(equations_block.get_ode_equations(), equations_block.get_ode_functions()) # transform everything into gsl processable (e.g. no functional shapes) or exact form. transform_shapes_and_odes(neuron, shape_to_buffers) # update the symbol table neuron.accept(ASTSymbolTableVisitor()) generate_nest_code(neuron) # now store the transformed model store_transformed_model(neuron) # at that point all shapes are transformed into the ODE form and spikes can be applied code, message = Messages.get_code_generated(neuron.get_name(), FrontendConfiguration.get_target_path()) Logger.log_message(neuron, code, message, neuron.get_source_position(), LoggingLevel.INFO)
def move_decls(cls, var_name, from_block, to_block, var_name_suffix, block_type: BlockType, mode="move", scope=None) -> List[ASTDeclaration]: from pynestml.codegeneration.ast_transformers import ASTTransformers from pynestml.visitors.ast_symbol_table_visitor import ASTSymbolTableVisitor assert mode in ["move", "copy"] if not from_block \ or not to_block: return [] decls = ASTTransformers.get_declarations_from_block(var_name, from_block) if var_name.endswith(var_name_suffix): decls.extend(ASTTransformers.get_declarations_from_block(var_name.removesuffix(var_name_suffix), from_block)) if decls: Logger.log_message(None, -1, "Moving definition of " + var_name + " from synapse to neuron", None, LoggingLevel.INFO) for decl in decls: if mode == "move": from_block.declarations.remove(decl) if mode == "copy": decl = decl.clone() assert len(decl.get_variables()) <= 1 if not decl.get_variables()[0].name.endswith(var_name_suffix): ASTUtils.add_suffix_to_decl_lhs(decl, suffix=var_name_suffix) to_block.get_declarations().append(decl) decl.update_scope(to_block.get_scope()) ast_symbol_table_visitor = ASTSymbolTableVisitor() ast_symbol_table_visitor.block_type_stack.push(block_type) decl.accept(ast_symbol_table_visitor) ast_symbol_table_visitor.block_type_stack.pop() return decls
def replace_inline_expressions_through_defining_expressions( cls, definitions: Sequence[ASTOdeEquation], inline_expressions: Sequence[ASTInlineExpression] ) -> Sequence[ASTOdeEquation]: """ Replaces symbols from `inline_expressions` in `definitions` with corresponding defining expressions from `inline_expressions`. :param definitions: A list of ODE definitions (**updated in-place**). :param inline_expressions: A list of inline expression definitions. :return: A list of updated ODE definitions (same as the ``definitions`` parameter). """ for m in inline_expressions: source_position = m.get_source_position() for target in definitions: matcher = re.compile( cls._variable_matching_template.format( m.get_variable_name())) target_definition = str(target.get_rhs()) target_definition = re.sub(matcher, "(" + str(m.get_expression()) + ")", target_definition) target.rhs = ModelParser.parse_expression(target_definition) target.update_scope(m.get_scope()) target.accept(ASTSymbolTableVisitor()) def log_set_source_position(node): if node.get_source_position().is_added_source_position(): node.set_source_position(source_position) target.accept( ASTHigherOrderVisitor(visit_funcs=log_set_source_position)) return definitions
def test(self): for filename in os.listdir( os.path.realpath( os.path.join(os.path.dirname(__file__), os.path.join('..', 'models')))): if filename.endswith(".nestml"): input_file = FileStream( os.path.join( os.path.dirname(__file__), os.path.join(os.path.join('..', 'models'), filename))) lexer = PyNestMLLexer(input_file) # create a token stream stream = CommonTokenStream(lexer) stream.fill() # parse the file parser = PyNestMLParser(stream) # process the comments compilation_unit = parser.nestMLCompilationUnit() # create a new visitor and return the new AST ast_builder_visitor = ASTBuilderVisitor(stream.tokens) ast = ast_builder_visitor.visit(compilation_unit) # update the corresponding symbol tables SymbolTable.initialize_symbol_table(ast.get_source_position()) symbol_table_visitor = ASTSymbolTableVisitor() for neuron in ast.get_neuron_list(): neuron.accept(symbol_table_visitor) SymbolTable.add_neuron_scope(name=neuron.get_name(), scope=neuron.get_scope()) self.assertTrue(isinstance(ast, ASTNestMLCompilationUnit)) return
def add_declaration_to_internals(neuron: ASTNeuron, variable_name: str, init_expression: str) -> ASTNeuron: """ Adds the variable as stored in the declaration tuple to the neuron. The declared variable is of type real. :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) ast_declaration.update_scope(neuron.get_internals_blocks().get_scope()) symtable_visitor = ASTSymbolTableVisitor() symtable_visitor.block_type_stack.push(BlockType.INTERNALS) ast_declaration.accept(symtable_visitor) symtable_visitor.block_type_stack.pop() return neuron
def deconstruct_assignment(cls, lhs=None, is_plus=False, is_minus=False, is_times=False, is_divide=False, _rhs=None): """ From lhs and rhs it constructs a new rhs which corresponds to direct assignment. E.g.: a += b*c -> a = a + b*c :param lhs: a lhs rhs :type lhs: ast_expression or ast_simple_expression :param is_plus: is plus assignment :type is_plus: bool :param is_minus: is minus assignment :type is_minus: bool :param is_times: is times assignment :type is_times: bool :param is_divide: is divide assignment :type is_divide: bool :param _rhs: a rhs rhs :type _rhs: ASTExpression or ASTSimpleExpression :return: a new direct assignment rhs. :rtype: ASTExpression """ from pynestml.visitors.ast_symbol_table_visitor import ASTSymbolTableVisitor from pynestml.meta_model.ast_node_factory import ASTNodeFactory assert ((is_plus + is_minus + is_times + is_divide) == 1), \ '(PyNestML.CodeGeneration.Utils) Type of assignment not correctly specified!' if is_plus: op = ASTNodeFactory.create_ast_arithmetic_operator( is_plus_op=True, source_position=_rhs.get_source_position()) elif is_minus: op = ASTNodeFactory.create_ast_arithmetic_operator( is_minus_op=True, source_position=_rhs.get_source_position()) elif is_times: op = ASTNodeFactory.create_ast_arithmetic_operator( is_times_op=True, source_position=_rhs.get_source_position()) else: op = ASTNodeFactory.create_ast_arithmetic_operator( is_div_op=True, source_position=_rhs.get_source_position()) var_expr = ASTNodeFactory.create_ast_simple_expression( variable=lhs, source_position=lhs.get_source_position()) var_expr.update_scope(lhs.get_scope()) op.update_scope(lhs.get_scope()) rhs_in_brackets = ASTNodeFactory.create_ast_expression( is_encapsulated=True, expression=_rhs, source_position=_rhs.get_source_position()) rhs_in_brackets.update_scope(_rhs.get_scope()) expr = ASTNodeFactory.create_ast_compound_expression( lhs=var_expr, binary_operator=op, rhs=rhs_in_brackets, source_position=_rhs.get_source_position()) expr.update_scope(lhs.get_scope()) # update the symbols expr.accept(ASTSymbolTableVisitor()) return expr
def replace_var(_expr=None): if isinstance(_expr, ASTSimpleExpression) and _expr.is_variable(): var = _expr.get_variable() elif isinstance(_expr, ASTVariable): var = _expr else: return if var.get_name() != var_name: return ast_ext_var = ASTExternalVariable(var.get_name() + suffix, differential_order=var.get_differential_order(), source_position=var.get_source_position()) if alternate_name: ast_ext_var.set_alternate_name(alternate_name) ast_ext_var.update_alt_scope(new_scope) from pynestml.visitors.ast_symbol_table_visitor import ASTSymbolTableVisitor ast_ext_var.accept(ASTSymbolTableVisitor()) if isinstance(_expr, ASTSimpleExpression) and _expr.is_variable(): Logger.log_message(None, -1, "ASTSimpleExpression replacement made (var = " + str( ast_ext_var.get_name()) + ") in expression: " + str(node.get_parent(_expr)), None, LoggingLevel.INFO) _expr.set_variable(ast_ext_var) return if isinstance(_expr, ASTVariable): if isinstance(node.get_parent(_expr), ASTAssignment): node.get_parent(_expr).lhs = ast_ext_var Logger.log_message(None, -1, "ASTVariable replacement made in expression: " + str(node.get_parent(_expr)), None, LoggingLevel.INFO) elif isinstance(node.get_parent(_expr), ASTSimpleExpression) and node.get_parent(_expr).is_variable(): node.get_parent(_expr).set_variable(ast_ext_var) elif isinstance(node.get_parent(_expr), ASTDeclaration): # variable could occur on the left-hand side; ignore. Only replace if it occurs on the right-hand side. pass else: Logger.log_message(None, -1, "Error: unhandled use of variable " + var_name + " in expression " + str(_expr), None, LoggingLevel.INFO) raise Exception() return p = node.get_parent(var) Logger.log_message(None, -1, "Error: unhandled use of variable " + var_name + " in expression " + str(p), None, LoggingLevel.INFO) raise Exception()
def deconstruct_compound_assignment(self): """ From lhs and rhs it constructs a new expression which corresponds to direct assignment. E.g.: a += b*c -> a = a + b*c :return: the rhs for an equivalent direct assignment. :rtype: ast_expression """ from pynestml.visitors.ast_symbol_table_visitor import ASTSymbolTableVisitor # TODO: get rid of this through polymorphism? assert not self.is_direct_assignment, "Can only be invoked on a compound assignment." operator = self.extract_operator_from_compound_assignment() lhs_variable = self.get_lhs_variable_as_expression() rhs_in_brackets = self.get_bracketed_rhs_expression() result = self.construct_equivalent_direct_assignment_rhs( operator, lhs_variable, rhs_in_brackets) # create symbols for the new Expression: visitor = ASTSymbolTableVisitor() result.accept(visitor) return result
def add_to_internal_block(self, declaration, index=-1): """ Adds the handed over declaration the internal block :param declaration: a single declaration :type declaration: ast_declaration """ if self.get_internals_blocks() is None: ASTUtils.create_internal_block(self) n_declarations = len(self.get_internals_blocks().get_declarations()) if n_declarations == 0: index = 0 else: index = 1 + (index % len(self.get_internals_blocks().get_declarations())) self.get_internals_blocks().get_declarations().insert(index, declaration) declaration.update_scope(self.get_internals_blocks().get_scope()) from pynestml.visitors.ast_symbol_table_visitor import ASTSymbolTableVisitor symtable_vistor = ASTSymbolTableVisitor() symtable_vistor.block_type_stack.push(BlockType.INTERNALS) declaration.accept(symtable_vistor) symtable_vistor.block_type_stack.pop()
def add_to_state_block(self, declaration): """ Adds the handed over declaration to the state block. :param declaration: a single declaration. :type declaration: ast_declaration """ from pynestml.utils.ast_utils import ASTUtils if self.get_state_blocks() is None: ASTUtils.create_state_block(self) self.get_state_blocks().get_declarations().append(declaration) declaration.update_scope(self.get_state_blocks().get_scope()) from pynestml.visitors.ast_symbol_table_visitor import ASTSymbolTableVisitor symtable_vistor = ASTSymbolTableVisitor() symtable_vistor.block_type_stack.push(BlockType.STATE) declaration.accept(symtable_vistor) symtable_vistor.block_type_stack.pop() from pynestml.symbols.symbol import SymbolKind assert declaration.get_variables()[0].get_scope().resolve_to_symbol( declaration.get_variables()[0].get_name(), SymbolKind.VARIABLE) is not None assert declaration.get_scope().resolve_to_symbol(declaration.get_variables()[0].get_name(), SymbolKind.VARIABLE) is not None
def parse_model(cls, file_path=None): """ Parses a handed over model and returns the meta_model representation of it. :param file_path: the path to the file which shall be parsed. :type file_path: str :return: a new ASTNESTMLCompilationUnit object. :rtype: ASTNestMLCompilationUnit """ try: input_file = FileStream(file_path) except IOError: code, message = Messages.get_input_path_not_found(path=file_path) Logger.log_message(node=None, code=None, message=message, error_position=None, log_level=LoggingLevel.ERROR) return code, message = Messages.get_start_processing_file(file_path) Logger.log_message(node=None, code=code, message=message, error_position=None, log_level=LoggingLevel.INFO) # create a lexer and hand over the input lexer = PyNestMLLexer() lexer.removeErrorListeners() lexer.addErrorListener(ConsoleErrorListener()) lexerErrorListener = NestMLErrorListener() lexer.addErrorListener(lexerErrorListener) # lexer._errHandler = BailErrorStrategy() # N.B. uncomment this line and the next to halt immediately on lexer errors # lexer._errHandler.reset(lexer) lexer.inputStream = input_file # create a token stream stream = CommonTokenStream(lexer) stream.fill() if lexerErrorListener._error_occurred: code, message = Messages.get_lexer_error() Logger.log_message(node=None, code=None, message=message, error_position=None, log_level=LoggingLevel.ERROR) return # parse the file parser = PyNestMLParser(None) parser.removeErrorListeners() parser.addErrorListener(ConsoleErrorListener()) parserErrorListener = NestMLErrorListener() parser.addErrorListener(parserErrorListener) # parser._errHandler = BailErrorStrategy() # N.B. uncomment this line and the next to halt immediately on parse errors # parser._errHandler.reset(parser) parser.setTokenStream(stream) compilation_unit = parser.nestMLCompilationUnit() if parserErrorListener._error_occurred: code, message = Messages.get_parser_error() Logger.log_message(node=None, code=None, message=message, error_position=None, log_level=LoggingLevel.ERROR) return # create a new visitor and return the new AST ast_builder_visitor = ASTBuilderVisitor(stream.tokens) ast = ast_builder_visitor.visit(compilation_unit) # create and update the corresponding symbol tables SymbolTable.initialize_symbol_table(ast.get_source_position()) for neuron in ast.get_neuron_list(): neuron.accept(ASTSymbolTableVisitor()) SymbolTable.add_neuron_scope(neuron.get_name(), neuron.get_scope()) # store source paths for neuron in ast.get_neuron_list(): neuron.file_path = file_path ast.file_path = file_path return ast
def parse_model(cls, file_path=None): """ Parses a handed over model and returns the meta_model representation of it. :param file_path: the path to the file which shall be parsed. :type file_path: str :return: a new ASTNESTMLCompilationUnit object. :rtype: ASTNestMLCompilationUnit """ try: input_file = FileStream(file_path) except IOError: code, message = Messages.get_input_path_not_found(path=file_path) Logger.log_message(neuron=None, code=None, message=message, error_position=None, log_level=LoggingLevel.ERROR) return code, message = Messages.get_start_processing_file(file_path) Logger.log_message(neuron=None, code=code, message=message, error_position=None, log_level=LoggingLevel.INFO) # create a lexer and hand over the input lexer = PyNestMLLexer() lexer.removeErrorListeners() lexer.addErrorListener(ConsoleErrorListener()) lexerErrorListener = NestMLErrorListener() lexer.addErrorListener(lexerErrorListener) # lexer._errHandler = BailErrorStrategy() # N.B. uncomment this line and the next to halt immediately on lexer errors # lexer._errHandler.reset(lexer) lexer.inputStream = input_file # create a token stream stream = CommonTokenStream(lexer) stream.fill() if lexerErrorListener._error_occurred: code, message = Messages.get_lexer_error() Logger.log_message(neuron=None, code=None, message=message, error_position=None, log_level=LoggingLevel.ERROR) return # parse the file parser = PyNestMLParser(None) parser.removeErrorListeners() parser.addErrorListener(ConsoleErrorListener()) parserErrorListener = NestMLErrorListener() parser.addErrorListener(parserErrorListener) # parser._errHandler = BailErrorStrategy() # N.B. uncomment this line and the next to halt immediately on parse errors # parser._errHandler.reset(parser) parser.setTokenStream(stream) compilation_unit = parser.nestMLCompilationUnit() if parserErrorListener._error_occurred: code, message = Messages.get_parser_error() Logger.log_message(neuron=None, code=None, message=message, error_position=None, log_level=LoggingLevel.ERROR) return # create a new visitor and return the new AST ast_builder_visitor = ASTBuilderVisitor(stream.tokens) ast = ast_builder_visitor.visit(compilation_unit) # create and update the corresponding symbol tables SymbolTable.initialize_symbol_table(ast.get_source_position()) log_to_restore = copy.deepcopy(Logger.get_log()) counter = Logger.curr_message # replace all derived variables through a computer processable names: e.g. g_in''' -> g_in__ddd restore_differential_order = [] for ode in ASTUtils.get_all(ast, ASTOdeEquation): lhs_variable = ode.get_lhs() if lhs_variable.get_differential_order() > 0: lhs_variable.differential_order = lhs_variable.get_differential_order() - 1 restore_differential_order.append(lhs_variable) for shape in ASTUtils.get_all(ast, ASTOdeShape): lhs_variable = shape.get_variable() if lhs_variable.get_differential_order() > 0: lhs_variable.differential_order = lhs_variable.get_differential_order() - 1 restore_differential_order.append(lhs_variable) # than replace remaining variables for variable in ASTUtils.get_all(ast, ASTVariable): if variable.get_differential_order() > 0: variable.set_name(variable.get_name() + "__" + "d" * variable.get_differential_order()) variable.differential_order = 0 # now also equations have no ' at lhs. replace every occurrence of last d to ' to compensate for ode_variable in restore_differential_order: ode_variable.differential_order = 1 Logger.set_log(log_to_restore, counter) for neuron in ast.get_neuron_list(): neuron.accept(ASTSymbolTableVisitor()) SymbolTable.add_neuron_scope(neuron.get_name(), neuron.get_scope()) return ast
def setup_generation_helpers(self, neuron: ASTNeuron) -> Dict: """ Returns a standard namespace with often required functionality. :param neuron: a single neuron instance :type neuron: ASTNeuron :return: a map from name to functionality. :rtype: dict """ gsl_converter = GSLReferenceConverter() gsl_printer = UnitlessExpressionPrinter(gsl_converter) # helper classes and objects converter = NESTReferenceConverter(False) unitless_pretty_printer = UnitlessExpressionPrinter(converter) namespace = dict() namespace['neuronName'] = neuron.get_name() namespace['neuron'] = neuron namespace['moduleName'] = FrontendConfiguration.get_module_name() namespace['printer'] = NestPrinter(unitless_pretty_printer) namespace['assignments'] = NestAssignmentsHelper() namespace['names'] = NestNamesConverter() namespace['declarations'] = NestDeclarationsHelper() namespace['utils'] = ASTUtils() namespace['idemPrinter'] = UnitlessExpressionPrinter() namespace['outputEvent'] = namespace['printer'].print_output_event( neuron.get_body()) namespace['is_spike_input'] = ASTUtils.is_spike_input( neuron.get_body()) namespace['is_current_input'] = ASTUtils.is_current_input( neuron.get_body()) namespace['odeTransformer'] = OdeTransformer() namespace['printerGSL'] = gsl_printer namespace['now'] = datetime.datetime.utcnow() namespace['tracing'] = FrontendConfiguration.is_dev namespace[ 'PredefinedUnits'] = pynestml.symbols.predefined_units.PredefinedUnits namespace[ 'UnitTypeSymbol'] = pynestml.symbols.unit_type_symbol.UnitTypeSymbol namespace['initial_values'] = {} namespace['uses_analytic_solver'] = neuron.get_name() in self.analytic_solver.keys() \ and self.analytic_solver[neuron.get_name()] is not None if namespace['uses_analytic_solver']: namespace['analytic_state_variables'] = self.analytic_solver[ neuron.get_name()]["state_variables"] namespace['analytic_variable_symbols'] = { sym: neuron.get_equations_block().get_scope().resolve_to_symbol( sym, SymbolKind.VARIABLE) for sym in namespace['analytic_state_variables'] } namespace['update_expressions'] = {} for sym, expr in self.analytic_solver[ neuron.get_name()]["initial_values"].items(): namespace['initial_values'][sym] = expr for sym in namespace['analytic_state_variables']: expr_str = self.analytic_solver[ neuron.get_name()]["update_expressions"][sym] expr_ast = ModelParser.parse_expression(expr_str) # pretend that update expressions are in "equations" block, which should always be present, as differential equations must have been defined to get here expr_ast.update_scope( neuron.get_equations_blocks().get_scope()) expr_ast.accept(ASTSymbolTableVisitor()) namespace['update_expressions'][sym] = expr_ast namespace['propagators'] = self.analytic_solver[ neuron.get_name()]["propagators"] namespace['uses_numeric_solver'] = neuron.get_name() in self.analytic_solver.keys() \ and self.numeric_solver[neuron.get_name()] is not None if namespace['uses_numeric_solver']: namespace['numeric_state_variables'] = self.numeric_solver[ neuron.get_name()]["state_variables"] namespace['numeric_variable_symbols'] = { sym: neuron.get_equations_block().get_scope().resolve_to_symbol( sym, SymbolKind.VARIABLE) for sym in namespace['numeric_state_variables'] } assert not any([ sym is None for sym in namespace['numeric_variable_symbols'].values() ]) namespace['numeric_update_expressions'] = {} for sym, expr in self.numeric_solver[ neuron.get_name()]["initial_values"].items(): namespace['initial_values'][sym] = expr for sym in namespace['numeric_state_variables']: expr_str = self.numeric_solver[ neuron.get_name()]["update_expressions"][sym] expr_ast = ModelParser.parse_expression(expr_str) # pretend that update expressions are in "equations" block, which should always be present, as differential equations must have been defined to get here expr_ast.update_scope( neuron.get_equations_blocks().get_scope()) expr_ast.accept(ASTSymbolTableVisitor()) namespace['numeric_update_expressions'][sym] = expr_ast namespace['useGSL'] = namespace['uses_numeric_solver'] namespace['names'] = GSLNamesConverter() converter = NESTReferenceConverter(True) unitless_pretty_printer = UnitlessExpressionPrinter(converter) namespace['printer'] = NestPrinter(unitless_pretty_printer) namespace["spike_updates"] = neuron.spike_updates rng_visitor = ASTRandomNumberGeneratorVisitor() neuron.accept(rng_visitor) namespace['norm_rng'] = rng_visitor._norm_rng_is_used return namespace