Skip to content
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

Add/fix checks that inline expressions, parameters and internals cannot be assigned to #953

Merged
merged 17 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
44e5ae1
add/fix checks that inline expressions, parameters and internals cann…
Sep 12, 2023
a172ff9
add/fix checks that inline expressions, parameters and internals cann…
Sep 12, 2023
ddad860
add/fix checks that inline expressions, parameters and internals cann…
Sep 12, 2023
2d36470
add/fix checks that inline expressions, parameters and internals cann…
Sep 12, 2023
ab6f934
move model state initialization to Node::init_state_()
Sep 13, 2023
e72b21c
add/fix checks that inline expressions, parameters and internals cann…
Sep 13, 2023
509d511
add/fix checks that inline expressions, parameters and internals cann…
Sep 13, 2023
be71111
Merge remote-tracking branch 'upstream/master' into coco_set_internal
Sep 13, 2023
c6978c4
add/fix checks that inline expressions, parameters and internals cann…
Sep 13, 2023
bb7e518
add/fix checks that inline expressions, parameters and internals cann…
Sep 14, 2023
ba6bedf
improve handling of NEST synaptic delay parameter
Sep 14, 2023
4849881
add/fix checks that inline expressions, parameters and internals cann…
Sep 14, 2023
af78bc8
add/fix checks that inline expressions, parameters and internals cann…
Sep 14, 2023
727784b
Merge remote-tracking branch 'upstream/master' into coco_set_internal
Sep 15, 2023
d042e7d
Update doc/nestml_language/nestml_language_concepts.rst
clinssen Sep 15, 2023
b85a369
add/fix checks that inline expressions, parameters and internals cann…
Sep 15, 2023
1b29681
Merge remote-tracking branch 'upstream/master' into coco_set_internal
Sep 18, 2023
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
3 changes: 1 addition & 2 deletions models/neurons/aeif_cond_alpha.nestml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ neuron aeif_cond_alpha:
state:
V_m mV = E_L # Membrane potential
w pA = 0 pA # Spike-adaptation current
r integer = 0 # Counts number of tick during the refractory period

equations:
inline V_bounded mV = min(V_m, V_peak) # prevent exponential divergence
Expand Down Expand Up @@ -93,8 +94,6 @@ neuron aeif_cond_alpha:

# refractory time in steps
RefractoryCounts integer = steps(t_ref)
# counts number of tick during the refractory period
r integer

input:
inh_spikes <- inhibitory spike
Expand Down
3 changes: 1 addition & 2 deletions models/neurons/aeif_cond_exp.nestml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ neuron aeif_cond_exp:
state:
V_m mV = E_L # Membrane potential
w pA = 0 pA # Spike-adaptation current
r integer = 0 # Counts number of tick during the refractory period

equations:
inline V_bounded mV = min(V_m, V_peak) # prevent exponential divergence
Expand Down Expand Up @@ -88,8 +89,6 @@ neuron aeif_cond_exp:
internals:
# refractory time in steps
RefractoryCounts integer = steps(t_ref)
# counts number of tick during the refractory period
r integer

input:
inh_spikes <- inhibitory spike
Expand Down
10 changes: 5 additions & 5 deletions models/neurons/ignore_and_fire.nestml
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ Tetzlaff (February 2021; January 2022)
neuron ignore_and_fire:

state:
phase real = 1. ## relative time to next spike (in (0,1])
phase real = 1. # relative time to next spike (in (0,1])
phase_steps integer = steps(max(0., phase) / firing_rate) # firing phase in steps

parameters:
firing_rate Bq = 10. Bq ## firing rate
firing_rate Bq = 10. Bq # firing rate

internals:
firing_period_steps integer = steps( 1. / firing_rate ) ## firing period in steps
phase_steps integer = steps( max(0.,phase) / firing_rate ) ## firing phase in steps
firing_period_steps integer = steps( 1. / firing_rate ) # firing period in steps

input:
spikes <- spike ## the neuron receives spikes, but is not processing them
spikes <- spike # the neuron receives spikes, but is not processing them

output:
spike
Expand Down
54 changes: 54 additions & 0 deletions pynestml/cocos/co_co_inline_expression_not_assigned_to.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
#
# co_co_inline_expression_not_assigned_to.py
#
# 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/>.

from pynestml.cocos.co_co import CoCo
from pynestml.meta_model.ast_neuron import ASTNeuron
from pynestml.symbols.symbol import SymbolKind
from pynestml.symbols.variable_symbol import BlockType
from pynestml.utils.ast_utils import ASTUtils
from pynestml.utils.logger import LoggingLevel, Logger
from pynestml.utils.messages import Messages
from pynestml.visitors.ast_visitor import ASTVisitor


class CoCoInlineExpressionNotAssignedTo(CoCo):
"""
This coco ensures that no values are assigned to inline expressions.
"""

@classmethod
def check_co_co(cls, node: ASTNeuron):
"""
Ensures the coco for the handed over neuron.
:param node: a single neuron instance.
"""
visitor = NoInlineExpressionAssignedToVisitor()
visitor.neuron_ = node
node.accept(visitor)


class NoInlineExpressionAssignedToVisitor(ASTVisitor):
def visit_assignment(self, node):
if ASTUtils.get_inline_expression_by_name(self.neuron_, node.get_variable().get_name()):
code, message = Messages.get_assigning_to_inline()
Logger.log_message(code=code, message=message,
error_position=node.get_source_position(),
log_level=LoggingLevel.ERROR)
69 changes: 69 additions & 0 deletions pynestml/cocos/co_co_internals_assigned_only_in_internals_block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
#
# co_co_internals_assigned_only_in_internals_block.py
#
# 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/>.

from pynestml.meta_model.ast_assignment import ASTAssignment
from pynestml.meta_model.ast_neuron import ASTNeuron
from pynestml.meta_model.ast_synapse import ASTSynapse
from pynestml.cocos.co_co import CoCo
from pynestml.symbol_table.scope import ScopeType
from pynestml.symbols.symbol import SymbolKind
from pynestml.symbols.variable_symbol import BlockType
from pynestml.utils.ast_utils import ASTUtils
from pynestml.utils.logger import LoggingLevel, Logger
from pynestml.utils.messages import Messages
from pynestml.visitors.ast_visitor import ASTVisitor


class CoCoInternalsAssignedOnlyInInternalsBlock(CoCo):
"""
This coco checks that no internals are assigned outside the internals block.
"""

@classmethod
def check_co_co(cls, node):
"""
Ensures the coco for the handed over neuron.
:param node: a single neuron instance.
:type node: ASTNeuron
"""
assert (node is not None and (isinstance(node, ASTNeuron) or isinstance(node, ASTSynapse))), \
'(PyNestML.CoCo.BufferNotAssigned) No or wrong type of neuron provided (%s)!' % type(node)
visitor = InternalsAssignmentVisitor()
visitor.neuron_ = node
node.accept(visitor)


class InternalsAssignmentVisitor(ASTVisitor):
"""
This visitor checks that no internals have been assigned outside the internals block.
clinssen marked this conversation as resolved.
Show resolved Hide resolved
"""

def visit_assignment(self, node: ASTAssignment) -> None:
"""
Checks the coco on the current node.
:param node: a single node.
"""
internal = ASTUtils.get_internal_by_name(self.neuron_, node.get_variable().get_name())
if internal:
code, message = Messages.get_assignment_not_allowed(node.get_variable().get_complete_name())
Logger.log_message(error_position=node.get_source_position(),
code=code, message=message,
log_level=LoggingLevel.ERROR)
23 changes: 21 additions & 2 deletions pynestml/cocos/co_cos_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
# along with NEST. If not, see <http://www.gnu.org/licenses/>.

from typing import Union
from pynestml.cocos import co_co_internals_assigned_only_in_internals_block

from pynestml.cocos.co_co_all_variables_defined import CoCoAllVariablesDefined
from pynestml.cocos.co_co_inline_expression_not_assigned_to import CoCoInlineExpressionNotAssignedTo
from pynestml.cocos.co_co_input_port_not_assigned_to import CoCoInputPortNotAssignedTo
from pynestml.cocos.co_co_convolve_cond_correctly_built import CoCoConvolveCondCorrectlyBuilt
from pynestml.cocos.co_co_correct_numerator_of_unit import CoCoCorrectNumeratorOfUnit
Expand All @@ -44,8 +46,8 @@
from pynestml.cocos.co_co_simple_delta_function import CoCoSimpleDeltaFunction
from pynestml.cocos.co_co_ode_functions_have_consistent_units import CoCoOdeFunctionsHaveConsistentUnits
from pynestml.cocos.co_co_output_port_defined_if_emit_call import CoCoOutputPortDefinedIfEmitCall
from pynestml.cocos.co_co_parameters_assigned_only_in_parameter_block import \
CoCoParametersAssignedOnlyInParameterBlock
from pynestml.cocos.co_co_internals_assigned_only_in_internals_block import CoCoInternalsAssignedOnlyInInternalsBlock
from pynestml.cocos.co_co_parameters_assigned_only_in_parameter_block import CoCoParametersAssignedOnlyInParameterBlock
from pynestml.cocos.co_co_resolution_func_legally_used import CoCoResolutionFuncLegallyUsed
from pynestml.cocos.co_co_state_variables_initialized import CoCoStateVariablesInitialized
from pynestml.cocos.co_co_sum_has_correct_parameter import CoCoSumHasCorrectParameter
Expand Down Expand Up @@ -74,6 +76,13 @@ def check_function_defined(cls, neuron: ASTNeuron):
"""
CoCoFunctionUnique.check_co_co(neuron)

@classmethod
def check_inline_expression_not_assigned_to(cls, neuron: ASTNeuron):
"""
Checks for the handed over neuron that inline expressions are not assigned to.
"""
CoCoInlineExpressionNotAssignedTo.check_co_co(neuron)

@classmethod
def check_each_block_defined_at_most_once(cls, node: Union[ASTNeuron, ASTSynapse]):
"""
Expand Down Expand Up @@ -195,6 +204,14 @@ def check_parameters_not_assigned_outside_parameters_block(cls, neuron: ASTNeuro
"""
CoCoParametersAssignedOnlyInParameterBlock.check_co_co(neuron)

@classmethod
def check_internals_not_assigned_outside_internals_block(cls, neuron: ASTNeuron):
"""
Checks that internals are not assigned outside the internals block.
:param neuron: a single neuron object.
"""
CoCoInternalsAssignedOnlyInInternalsBlock.check_co_co(neuron)

@classmethod
def check_output_port_defined_if_emit_call(cls, neuron: ASTNeuron):
"""
Expand Down Expand Up @@ -358,6 +375,7 @@ def post_symbol_table_builder_checks(cls, neuron: ASTNeuron, after_ast_rewrite:
"""
cls.check_each_block_defined_at_most_once(neuron)
cls.check_function_defined(neuron)
cls.check_inline_expression_not_assigned_to(neuron)
cls.check_function_declared_and_correctly_typed(neuron)
cls.check_variables_unique_in_scope(neuron)
cls.check_state_variables_initialized(neuron)
Expand All @@ -370,6 +388,7 @@ def post_symbol_table_builder_checks(cls, neuron: ASTNeuron, after_ast_rewrite:
cls.check_no_nest_namespace_collisions(neuron)
cls.check_input_port_qualifier_unique(neuron)
cls.check_parameters_not_assigned_outside_parameters_block(neuron)
cls.check_internals_not_assigned_outside_internals_block(neuron)
cls.check_user_defined_function_correctly_built(neuron)
cls.check_initial_ode_initial_values(neuron)
cls.check_kernel_type(neuron)
Expand Down
13 changes: 13 additions & 0 deletions pynestml/utils/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class MessageCode(Enum):
INSTALL_PATH_INFO = 88
CREATING_INSTALL_PATH = 89
CREATING_TARGET_PATH = 90
ASSIGNING_TO_INLINE = 91


class Messages:
Expand Down Expand Up @@ -1039,6 +1040,18 @@ def get_analysing_transforming_neuron(cls, name):
message = 'Analysing/transforming neuron \'%s\'' % name
return MessageCode.ANALYSING_TRANSFORMING_NEURON, message

@classmethod
def get_assigning_to_inline(cls):
"""
Cannot assign to inline expression
:param name: the name of the neuron model
:type name: ASTNeuron
:return: a nes code,message tuple
:rtype: (MessageCode,str)
"""
message = "Cannot assign to inline expression."
return MessageCode.ASSIGNING_TO_INLINE, message

@classmethod
def templated_arg_types_inconsistent(cls, function_name, failing_arg_idx, other_args_idx, failing_arg_type_str, other_type_str):
"""
Expand Down
16 changes: 16 additions & 0 deletions tests/cocos_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,22 @@ def test_valid_parameters_assigned_only_in_parameters_block(self):
self.assertEqual(len(
Logger.get_all_messages_of_level_and_or_node(model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)

def test_invalid_internals_assigned_only_in_internals_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__), 'invalid')),
'CoCoInternalAssignedOutsideBlock.nestml'))
self.assertEqual(len(
Logger.get_all_messages_of_level_and_or_node(model.get_neuron_list()[0], LoggingLevel.ERROR)), 1)

def test_valid_internals_assigned_only_in_internals_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')),
'CoCoInternalAssignedOutsideBlock.nestml'))
self.assertEqual(len(
Logger.get_all_messages_of_level_and_or_node(model.get_neuron_list()[0], LoggingLevel.ERROR)), 0)

def test_invalid_function_with_wrong_arg_number_detected(self):
Logger.set_logging_level(LoggingLevel.INFO)
model = ModelParser.parse_model(
Expand Down
40 changes: 40 additions & 0 deletions tests/invalid/CoCoInternalAssignedOutsideBlock.nestml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""
CoCoInternalAssignedOutsideBlock.nestml
clinssen marked this conversation as resolved.
Show resolved Hide resolved
########################################


Description
+++++++++++

This model is used to check if all cocos work correctly by detecting corresponding broken context. Here, if
assignment of values to internals outside of internals blocks is detected.

Negative case.


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/>.
"""
neuron CoCoInternalAssignedOutsideBlock:
internals:
test mV = 10 mV

update:
test = 20 mV
37 changes: 37 additions & 0 deletions tests/valid/CoCoInternalAssignedOutsideBlock.nestml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
CoCoInternalAssignedOutsideBlock.nestml
########################################


Description
+++++++++++

This model is used to check if all cocos work correctly by detecting corresponding broken context. Here, if
assignment of values to internals outside of internals blocks is detected.

Positive example.


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/>.
"""
neuron CoCoInternalAssignedOutsideBlock:
internals: # internal is not assigned outside the block, thus everything is correct
test mV = 10 mV
Loading