diff --git a/CHANGELOG.md b/CHANGELOG.md index b9e7c1c3b7..284a6b7f01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,11 @@ See also our [versioning policy](https://amici.readthedocs.io/en/latest/versioni can now be specified via the `AMICI_MODELS_ROOT` environment variable. See `amici.get_model_dir` for details. +**Fixes** + +* Fixed a bug that potentially results in incorrect handling of + discontinuities their location depends on a state variable + that is subject to an event assignment but otherwise constant. ## v0.X Series diff --git a/python/sdist/amici/de_model.py b/python/sdist/amici/de_model.py index 8a0adee9f6..c2821237b9 100644 --- a/python/sdist/amici/de_model.py +++ b/python/sdist/amici/de_model.py @@ -2236,26 +2236,31 @@ def conservation_law_has_multispecies(self, tcl: ConservationLaw) -> bool: def _expr_is_time_dependent(self, expr: sp.Expr) -> bool: """Determine whether an expression is time-dependent. + This function is solely for checking whether `expr` has a discontinuity + we need to track. Better report a false positive than miss a + time-dependence. + :param expr: The expression. :returns: Whether the expression is time-dependent. """ - # `expr.free_symbols` will be different to `self._states.keys()`, so - # it's easier to compare as `str`. - expr_syms = {str(sym) for sym in expr.free_symbols} + if not (free_syms := expr.free_symbols): + return False - # Check if the time variable is in the expression. - if amici_time_symbol.name in expr_syms: - return True + free_syms -= set(self.sym("p")) - # Check if any time-dependent states are in the expression. - state_syms = [str(sym) for sym in self.states()] - return any( - not self.state_is_constant(state_syms.index(state)) - for state in expr_syms.intersection(state_syms) - ) + if not free_syms: + return False + + free_syms -= set(self.sym("k")) + + # TODO(performance): handle static expressions, + # handle constant state variables, + # combine with other checks for time-dependence + # after https://github.com/AMICI-dev/AMICI/pull/3031 + return bool(free_syms) def _get_unique_root( self,