def apply_incoming_spikes(neuron: ASTNeuron): """ 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: kernel = 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(kernel + "[\']*", variable.get_complete_name()) or re.match( kernel + '__[\\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 add_declaration_to_initial_values(neuron: ASTNeuron, variable: str, initial_value: str) -> ASTNeuron: """ Adds a single declaration to the initial values 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_initial_values_block(ast_declaration) ast_declaration.update_scope( neuron.get_initial_values_blocks().get_scope()) symtable_visitor = ASTSymbolTableVisitor() symtable_visitor.block_type_stack.push(BlockType.INITIAL_VALUES) ast_declaration.accept(symtable_visitor) symtable_visitor.block_type_stack.pop() return neuron
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 declaration_in_initial_values(neuron: ASTNeuron, variable_name: str) -> bool: assert type(variable_name) is str for decl in neuron.get_initial_values_blocks().get_declarations(): for var in decl.get_variables(): if var.get_complete_name() == variable_name: return True return False
def analyse_neuron(self, neuron: ASTNeuron) -> List[ASTAssignment]: """ Analyse and transform a single neuron. :param neuron: a single neuron. :return: spike_updates: list of spike updates, see documentation for get_spike_update_expressions() for more information. """ code, message = Messages.get_start_processing_neuron(neuron.get_name()) Logger.log_message(neuron, code, message, neuron.get_source_position(), LoggingLevel.INFO) equations_block = neuron.get_equations_block() if equations_block is None: # add all declared state variables as none of them are used in equations block self.non_equations_state_variables[neuron.get_name()] = [] self.non_equations_state_variables[neuron.get_name()].extend(ASTUtils.all_variables_defined_in_block(neuron.get_initial_values_blocks())) self.non_equations_state_variables[neuron.get_name()].extend(ASTUtils.all_variables_defined_in_block(neuron.get_state_blocks())) return [] delta_factors = self.get_delta_factors_(neuron, equations_block) kernel_buffers = self.generate_kernel_buffers_(neuron, equations_block) self.replace_convolve_calls_with_buffers_(neuron, equations_block, kernel_buffers) self.make_inline_expressions_self_contained(equations_block.get_inline_expressions()) self.replace_inline_expressions_through_defining_expressions( equations_block.get_ode_equations(), equations_block.get_inline_expressions()) analytic_solver, numeric_solver = self.ode_toolbox_analysis(neuron, kernel_buffers) self.analytic_solver[neuron.get_name()] = analytic_solver self.numeric_solver[neuron.get_name()] = numeric_solver self.non_equations_state_variables[neuron.get_name()] = [] for decl in neuron.get_initial_values_blocks().get_declarations(): for var in decl.get_variables(): # check if this variable is not in equations if not neuron.get_equations_blocks(): self.non_equations_state_variables[neuron.get_name()].append(var) continue used_in_eq = False for ode_eq in neuron.get_equations_blocks().get_ode_equations(): if ode_eq.get_lhs().get_name() == var.get_name(): used_in_eq = True break for kern in neuron.get_equations_blocks().get_kernels(): for kern_var in kern.get_variables(): if kern_var.get_name() == var.get_name(): used_in_eq = True break if not used_in_eq: self.non_equations_state_variables[neuron.get_name()].append(var) self.remove_initial_values_for_kernels(neuron) kernels = self.remove_kernel_definitions_from_equations_block(neuron) self.update_initial_values_for_odes(neuron, [analytic_solver, numeric_solver], kernels) self.remove_ode_definitions_from_equations_block(neuron) self.create_initial_values_for_kernels(neuron, [analytic_solver, numeric_solver], kernels) self.replace_variable_names_in_expressions(neuron, [analytic_solver, numeric_solver]) self.add_timestep_symbol(neuron) if self.analytic_solver[neuron.get_name()] is not None: neuron = add_declarations_to_internals(neuron, self.analytic_solver[neuron.get_name()]["propagators"]) self.update_symbol_table(neuron, kernel_buffers) spike_updates = self.get_spike_update_expressions( neuron, kernel_buffers, [analytic_solver, numeric_solver], delta_factors) return spike_updates