Skip to content

Add a Transformer to support multiple backends with different preferred prefixes a.k.a Non-dimensionalisation-transformer #1234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 49 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
4bde5db
initial commit
mwhy Apr 1, 2025
a6d3918
Update .gitignore
mwhy Apr 2, 2025
91b085a
Merge branch 'master' of https://github.com/nest/nestml
mwhy Apr 28, 2025
2e4679c
add model transformer for non-dimensionalisation
May 21, 2025
7d56517
add model transformer for non-dimensionalisation
May 26, 2025
df6119d
Update necessary SpiNNaker2 code generation templates
mwhy Jun 2, 2025
2f58435
Add setter for ASTVariables and ASTDataType
mwhy Jun 2, 2025
69e0c85
Add a more robust dynamic import function for generated python package
mwhy Jun 2, 2025
8551db8
Add generation of parameter - value dict needed for initialisation of…
mwhy Jun 2, 2025
1a4bcac
Uncomment synapse section even if not used at the moment
mwhy Jun 2, 2025
770748d
Add SpiNNaker2 to use AddTimestepToInternalsTransformer
mwhy Jun 2, 2025
ed1a6e5
Add SpiNNaker2CodeGeneratorUtils analogous to SpiNNaker1 implementation
mwhy Jun 2, 2025
6ff5d6c
Update spinnaker2_code_generator.py to use namespaces
mwhy Jun 2, 2025
7a0a043
Add filepath to ASTModel as it's needed for use of python_standalone_…
mwhy Jun 2, 2025
06dd673
Remove np.int from list of datatypes during cloning of numeric litera…
mwhy Jun 2, 2025
cbcf018
Add generation of updated state dict - dict with initial values for n…
mwhy Jun 2, 2025
54d460a
Update SpiNNaker2 builder
mwhy Jun 2, 2025
ab5304a
Add iaf_psc_exp neuron model without additional external I_stim curre…
mwhy Jun 2, 2025
0e9e9c6
Add c function call and variable printers for SpiNNaker2
mwhy Jun 2, 2025
2f4769f
Add code generation resources for C and Python
mwhy Jun 2, 2025
83ed260
Add SpiNNaker2TargetTools
mwhy Jun 2, 2025
9bad001
Add test for iaf_psc_exp_neuron_NO_ISTIM to NEST tests
mwhy Jun 2, 2025
976c1db
WIP: development of non-dimensionalisation transformer
mwhy Jun 3, 2025
f91fda6
add factors to the AST (proof-of-concept)
Jun 3, 2025
7d894ad
WIP: add tests for Non- Dimensionalisation- Transformer and package i…
mwhy Jun 12, 2025
4aeddbb
WIP: split up .NESTML test files for Non- Dimensionalisation- Transfo…
mwhy Jun 16, 2025
f06a2c4
WIP: split up PyTest test files for Non- Dimensionalisation- Transfor…
mwhy Jun 16, 2025
91da378
WIP: add a numpy refernce implementation for non dimensinalisation tr…
mwhy Jun 16, 2025
ff34c41
WIP: non dimensinalisation transformer add isinstance() checks
mwhy Jun 16, 2025
cabe11c
Move weight_scaling_factor and simulation_timestep_in_s to neuron par…
mwhy Jun 18, 2025
8ad064e
WIP: Update tests and Unit Transformer
mwhy Jun 19, 2025
7503eac
update tests for non dimensionalisation transformer
mwhy Jun 22, 2025
225d9eb
WIP: split transformer into multiple parts to prevent errors during m…
mwhy Jun 22, 2025
119f3f8
Comment out unused transformer import
mwhy Jun 23, 2025
9ab2422
Set correct memory addresses for code generation
mwhy Jun 27, 2025
f3e651a
Refactoring, put functions in ast_utils and ast_model instead of NEST…
mwhy Jul 1, 2025
d1504d1
merge upstream master
mwhy Jul 1, 2025
04aa8f8
Merge remote-tracking branch 'origin/master' into non-dim-trafo
mwhy Jul 1, 2025
145bec2
update transformer, split into multiple visitors implementing differe…
mwhy Jul 2, 2025
f5d4162
code cleanup
mwhy Jul 3, 2025
888c464
Update non-dim-transformer and test-cases
mwhy Jul 6, 2025
0896801
update non-dim-trafo and tests
mwhy Jul 10, 2025
308dc5c
update non-dim-trafo and tests
mwhy Jul 10, 2025
6865ad0
update transformer to correctly distinguish between variables and uni…
mwhy Jul 13, 2025
283857f
make changing of data type in input port more explicit
mwhy Jul 13, 2025
2d819f4
add Izhikevich neuron numerical integrtation test to non-dim-transformer
mwhy Jul 13, 2025
ad2fb61
comment out functionality in attempt_magnitude_cast() - function shou…
mwhy Jul 13, 2025
96cd7e8
make nest_code_generator consistent with main branch
mwhy Jul 13, 2025
a8c6f08
update gitignore
mwhy Jul 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@ target/
build/
buildNest/
venv/
spinnaker-install/
tests/spinnaker_tests/bak/spinnaker-install/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove this file from the PR and also all of the SpiNNaker-2 related files from this PR? Cheers!

spinnaker-target/
report/
reports/
nestml-*/
*.egg-info
stash/

tests/spinnaker2_tests/spinnaker2-install
tests/spinnaker2_tests/spinnaker2-target
tests/spinnaker_tests/report
tests/spinnaker_tests/spinnaker-install
tests/spinnaker_tests/spinnaker-target
tests/nest_tests/target_*/
tests/spinnaker2_tests/nestml_*/

__*

Expand All @@ -35,3 +41,27 @@ venv
*.gdf
*~
*.iml
tests/nest_tests/non_dimensionalisation_transformer/resources/non_dimensionalisation_transformer_test_neuron.nestml.bak
tests/nest_tests/non_dimensionalisation_transformer/tests/reference_test_non_dim_transformer_function_call_in_equation_block.png
tests/nest_tests/non_dimensionalisation_transformer/tests/transformed_model_test_exp_in_equation_block.txt
tests/nest_tests/non_dimensionalisation_transformer/tests/transformed_model.txt
pynestml/codegeneration/resources_spinnaker2/bak/@[email protected]
pynestml/codegeneration/resources_spinnaker2/bak/@[email protected]
pynestml/codegeneration/resources_spinnaker2/bak/decay.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/global_params.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/maths-util.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/neuron_model_@NEURON_NAME@_impl.c.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/neuron_model_@NEURON_NAME@_impl.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/neuron_model.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/neuron-typedefs.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/neuron.c.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/neuron.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/param_defs.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/population_table.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/regions.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/simulation.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/synapse_row.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/synapse_types_exponential_impl.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/synapse_types.h.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/synapses.c.jinja2
pynestml/codegeneration/resources_spinnaker2/bak/synapses.h.jinja2
130 changes: 130 additions & 0 deletions models/neurons/iaf_psc_exp_neuron_NO_ISTIM.nestml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# iaf_psc_exp_NO_ISTIM - Leaky integrate-and-fire neuron model
# ###################################################
#
# Description
# +++++++++++
#
# iaf_psc_exp is an implementation of a leaky integrate-and-fire model
# with exponentially decaying synaptic currents according to [1]_.
# Thus, postsynaptic currents have an infinitely short rise time.
# The input current I_stim is removed for code generation testing purposes.
#
# The threshold crossing is followed by an absolute refractory period
# during which the membrane potential is clamped to the resting potential
# and spiking is prohibited.
#
# The general framework for the consistent formulation of systems with
# neuron like dynamics interacting by point events is described in
# [1]_. A flow chart can be found in [2]_.
#
# Critical tests for the formulation of the neuron model are the
# comparisons of simulation results for different computation step
# sizes.
#
# .. note::
#
# If tau_m is very close to tau_syn_exc or tau_syn_inh, numerical problems
# may arise due to singularities in the propagator matrics. If this is
# the case, replace equal-valued parameters by a single parameter.
#
# For details, please see ``IAF_neurons_singularity.ipynb`` in
# the NEST source code (``docs/model_details``).
#
#
# References
# ++++++++++
#
# .. [1] Rotter S, Diesmann M (1999). Exact simulation of
# time-invariant linear systems with applications to neuronal
# modeling. Biologial Cybernetics 81:381-402.
# DOI: https://doi.org/10.1007/s004220050570
# .. [2] Diesmann M, Gewaltig M-O, Rotter S, & Aertsen A (2001). State
# space analysis of synchronous spiking in cortical neural
# networks. Neurocomputing 38-40:565-571.
# DOI: https://doi.org/10.1016/S0925-2312(01)00409-X
# .. [3] Morrison A, Straube S, Plesser H E, Diesmann M (2006). Exact
# subthreshold integration with continuous spike times in discrete time
# neural network simulations. Neural Computation, in press
# DOI: https://doi.org/10.1162/neco.2007.19.1.47
#
#
# See also
# ++++++++
#
# iaf_psc_delta, iaf_psc_alpha, iaf_cond_exp
#
#
# Copyright statement
# +++++++++++++++++++
#
# This file is part of NEST.
#
# Copyright (C) 2004 The NEST Initiative
#
# NEST is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# NEST is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see <http://www.gnu.org/licenses/>.
#
#
model iaf_psc_exp_neuron_NO_ISTIM:

state:
V_m mV = E_L # Membrane potential
refr_t ms = 0 ms # Refractory period timer
I_syn_exc pA = 0 pA
I_syn_inh pA = 0 pA

equations:
I_syn_exc' = -I_syn_exc / tau_syn_exc
I_syn_inh' = -I_syn_inh / tau_syn_inh
V_m' = -(V_m - E_L) / tau_m + (I_syn_exc - I_syn_inh + I_e) / C_m
refr_t' = -1e3 * ms/s # refractoriness is implemented as an ODE, representing a timer counting back down to zero. XXX: TODO: This should simply read ``refr_t' = -1 / s`` (see https://github.com/nest/nestml/issues/984)

parameters:
C_m pF = 250 pF # Capacitance of the membrane
tau_m ms = 10 ms # Membrane time constant
tau_syn_inh ms = 2 ms # Time constant of inhibitory synaptic current
tau_syn_exc ms = 2 ms # Time constant of excitatory synaptic current
refr_T ms = 2 ms # Duration of refractory period
E_L mV = -70 mV # Resting potential
V_reset mV = -70 mV # Reset value of the membrane potential
V_th mV = -55 mV # Spike threshold potential

# constant external input current
I_e pA = 0 pA

input:
exc_spikes <- excitatory spike
inh_spikes <- inhibitory spike

output:
spike

update:
if refr_t > 0 ms:
# neuron is absolute refractory, do not evolve V_m
integrate_odes(I_syn_exc, I_syn_inh, refr_t)
else:
# neuron not refractory
integrate_odes(I_syn_exc, I_syn_inh, V_m)

onReceive(exc_spikes):
I_syn_exc += exc_spikes * pA * s

onReceive(inh_spikes):
I_syn_inh += inh_spikes * pA * s

onCondition(refr_t <= 0 ms and V_m >= V_th):
# threshold crossing
refr_t = refr_T # start of the refractory period
V_m = V_reset
emit_spike()
5 changes: 2 additions & 3 deletions pynestml/cocos/co_co_function_calls_consistent.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from pynestml.utils.ast_utils import ASTUtils
from pynestml.utils.logger import Logger, LoggingLevel
from pynestml.utils.messages import Messages
from pynestml.utils.type_caster import TypeCaster
from pynestml.visitors.ast_visitor import ASTVisitor


Expand Down Expand Up @@ -109,5 +108,5 @@ def visit_function_call(self, node):
# variadic type symbol accepts anything
return

if not actual_type.equals(expected_type) and not isinstance(expected_type, TemplateTypeSymbol):
TypeCaster.try_to_recover_or_error(expected_type, actual_type, actual_arg)
# if not actual_type.equals(expected_type) and not isinstance(expected_type, TemplateTypeSymbol):
# TypeCaster.try_to_recover_or_error(expected_type, actual_type, actual_arg)
41 changes: 19 additions & 22 deletions pynestml/cocos/co_co_illegal_expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from pynestml.utils.logger import LoggingLevel, Logger
from pynestml.utils.logging_helper import LoggingHelper
from pynestml.utils.messages import Messages
from pynestml.utils.type_caster import TypeCaster
from pynestml.visitors.ast_visitor import ASTVisitor


Expand Down Expand Up @@ -68,9 +67,8 @@ def visit_declaration(self, node):
if isinstance(rhs_type, ErrorTypeSymbol):
LoggingHelper.drop_missing_type_error(node)
return
if self.__types_do_not_match(lhs_type, rhs_type):
TypeCaster.try_to_recover_or_error(lhs_type, rhs_type, node.get_expression())
return
# if self.__types_do_not_match(lhs_type, rhs_type):
# TypeCaster.try_to_recover_or_error(lhs_type, rhs_type, node.get_expression())

def visit_inline_expression(self, node):
"""
Expand All @@ -82,8 +80,8 @@ def visit_inline_expression(self, node):
if isinstance(rhs_type, ErrorTypeSymbol):
LoggingHelper.drop_missing_type_error(node)
return
if self.__types_do_not_match(lhs_type, rhs_type):
TypeCaster.try_to_recover_or_error(lhs_type, rhs_type, node.get_expression())
# if self.__types_do_not_match(lhs_type, rhs_type):
# TypeCaster.try_to_recover_or_error(lhs_type, rhs_type, node.get_expression())

def visit_assignment(self, node):
"""
Expand Down Expand Up @@ -120,23 +118,23 @@ def handle_compound_assignment(self, node):
lhs_type_symbol = lhs_variable_symbol.get_type_symbol()

if node.is_compound_product:
if self.__types_do_not_match(lhs_type_symbol, lhs_type_symbol * rhs_type_symbol):
TypeCaster.try_to_recover_or_error(lhs_type_symbol, lhs_type_symbol * rhs_type_symbol,
node.get_expression())
return
# if self.__types_do_not_match(lhs_type_symbol, lhs_type_symbol * rhs_type_symbol):
# TypeCaster.try_to_recover_or_error(lhs_type_symbol, lhs_type_symbol * rhs_type_symbol,
# node.get_expression())
# return
return

if node.is_compound_quotient:
if self.__types_do_not_match(lhs_type_symbol, lhs_type_symbol / rhs_type_symbol):
TypeCaster.try_to_recover_or_error(lhs_type_symbol, lhs_type_symbol / rhs_type_symbol,
node.get_expression())
return
# if self.__types_do_not_match(lhs_type_symbol, lhs_type_symbol / rhs_type_symbol):
# TypeCaster.try_to_recover_or_error(lhs_type_symbol, lhs_type_symbol / rhs_type_symbol,
# node.get_expression())
# return
return

assert node.is_compound_sum or node.is_compound_minus
if self.__types_do_not_match(lhs_type_symbol, rhs_type_symbol):
TypeCaster.try_to_recover_or_error(lhs_type_symbol, rhs_type_symbol,
node.get_expression())
# if self.__types_do_not_match(lhs_type_symbol, rhs_type_symbol):
# TypeCaster.try_to_recover_or_error(lhs_type_symbol, rhs_type_symbol,
# node.get_expression())

@staticmethod
def __types_do_not_match(lhs_type_symbol, rhs_type_symbol):
Expand All @@ -154,11 +152,10 @@ def handle_simple_assignment(self, node):
LoggingHelper.drop_missing_type_error(node)
return

if lhs_variable_symbol is not None and self.__types_do_not_match(lhs_variable_symbol.get_type_symbol(),
rhs_type_symbol):
TypeCaster.try_to_recover_or_error(lhs_variable_symbol.get_type_symbol(), rhs_type_symbol,
node.get_expression())
return
# if lhs_variable_symbol is not None and self.__types_do_not_match(lhs_variable_symbol.get_type_symbol(),
# rhs_type_symbol):
# TypeCaster.try_to_recover_or_error(lhs_variable_symbol.get_type_symbol(), rhs_type_symbol,
# node.get_expression())

def visit_if_clause(self, node):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from pynestml.symbols.symbol import SymbolKind
from pynestml.utils.logger import LoggingLevel, Logger
from pynestml.utils.messages import Messages
from pynestml.utils.type_caster import TypeCaster


class CoCoUserDefinedFunctionCorrectlyDefined(CoCo):
Expand Down Expand Up @@ -128,9 +127,9 @@ def __check_return_recursively(cls, type_symbol=None, stmts=None, ret_defined=Fa
code, message = Messages.get_type_could_not_be_derived(cls.processed_function.get_name())
Logger.log_message(error_position=stmt.get_source_position(),
code=code, message=message, log_level=LoggingLevel.ERROR)
elif not type_of_return.equals(type_symbol):
TypeCaster.try_to_recover_or_error(type_symbol, type_of_return,
stmt.get_return_stmt().get_expression())
# elif not type_of_return.equals(type_symbol):
# TypeCaster.try_to_recover_or_error(type_symbol, type_of_return,
# stmt.get_return_stmt().get_expression())
elif isinstance(stmt, ASTCompoundStmt):
# otherwise it is a compound stmt, thus check recursively
if stmt.is_if_stmt():
Expand Down
23 changes: 20 additions & 3 deletions pynestml/codegeneration/nest_code_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see <http://www.gnu.org/licenses/>.

from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple

import datetime
Expand Down Expand Up @@ -312,11 +311,14 @@ def analyse_transform_neurons(self, neurons: List[ASTModel]) -> None:
for neuron in neurons:
code, message = Messages.get_analysing_transforming_model(neuron.get_name())
Logger.log_message(None, code, message, None, LoggingLevel.INFO)
spike_updates, post_spike_updates, equations_with_delay_vars, equations_with_vector_vars = self.analyse_neuron(neuron)
spike_updates, post_spike_updates, equations_with_delay_vars, equations_with_vector_vars = self.analyse_neuron(neuron) # , parameter_value_dict, updated_state_dict = self.analyse_neuron(neuron)
neuron.spike_updates = spike_updates
neuron.post_spike_updates = post_spike_updates
neuron.equations_with_delay_vars = equations_with_delay_vars
neuron.equations_with_vector_vars = equations_with_vector_vars
# neuron.analytic_solver = analytic_solver
# neuron.parameter_value_dict = parameter_value_dict
# neuron.updated_state_dict = updated_state_dict

def analyse_transform_synapses(self, synapses: List[ASTModel]) -> None:
"""
Expand Down Expand Up @@ -362,6 +364,15 @@ def analyse_neuron(self, neuron: ASTModel) -> Tuple[Dict[str, ASTAssignment], Di
neuron.accept(equations_with_delay_vars_visitor)
equations_with_delay_vars = equations_with_delay_vars_visitor.equations

# Collect all parameters and their attached values
# parameter_block = neuron.get_parameters_blocks()[0]
# parameter_value_dict = ASTUtils.generate_parameter_value_dict(neuron, parameter_block)
# state_block = neuron.get_state_blocks()[0]
# updated_state_dict = ASTUtils.generate_updated_state_dict(neuron, state_block, parameter_value_dict)




# Collect all the equations with vector variables
eqns_with_vector_vars_visitor = ASTEquationsWithVectorVariablesVisitor()
neuron.accept(eqns_with_vector_vars_visitor)
Expand Down Expand Up @@ -413,7 +424,7 @@ def analyse_neuron(self, neuron: ASTModel) -> Tuple[Dict[str, ASTAssignment], Di

spike_updates, post_spike_updates = self.get_spike_update_expressions(neuron, kernel_buffers, [analytic_solver, numeric_solver], delta_factors)

return spike_updates, post_spike_updates, equations_with_delay_vars, equations_with_vector_vars
return spike_updates, post_spike_updates, equations_with_delay_vars, equations_with_vector_vars# , analytic_solver , parameter_value_dict, updated_state_dict

def analyse_synapse(self, synapse: ASTModel) -> Dict[str, ASTAssignment]:
"""
Expand Down Expand Up @@ -915,6 +926,12 @@ def ode_toolbox_analysis(self, neuron: ASTModel, kernel_buffers: Mapping[ASTKern
odetoolbox_indict["options"]["output_timestep_symbol"] = "__h"
odetoolbox_indict["options"]["simplify_expression"] = self.get_option("simplify_expression")
disable_analytic_solver = self.get_option("solver") != "analytic"
# solver_result = odetoolbox.analysis(odetoolbox_indict,
# disable_stiffness_check=True,
# disable_analytic_solver=disable_analytic_solver,
# preserve_expressions=self.get_option("preserve_expressions"),
# simplify_expression=self.get_option("simplify_expression"),
# log_level=FrontendConfiguration.logging_level)
solver_result = odetoolbox.analysis(odetoolbox_indict,
disable_stiffness_check=True,
disable_analytic_solver=disable_analytic_solver,
Expand Down
Loading
Loading