From aef2e5e8453af0564538e6cad70a9d9a28aa6393 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 13 Jan 2025 14:16:18 +0200 Subject: [PATCH 01/10] Add ruff configuration --- .editorconfig | 1 + pyproject.toml | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/.editorconfig b/.editorconfig index 8d3905d..e269037 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,7 @@ root = true [*] end_of_line = lf insert_final_newline = true +trim_trailing_whitespace = false # Matches multiple files with brace expansion notation # Set default charset diff --git a/pyproject.toml b/pyproject.toml index 2b9a3d4..1976822 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,3 +54,47 @@ jitcode = [ [tool.setuptools_scm] write_to = "jitcode/version.py" + +[tool.ruff] +target-version = "py37" +line-length = 320 + +[tool.ruff.lint] +select = [ + "A", # flake8-builtins + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "E", # flake8 + "F", # flake8 + "I", # flake8-isort + "NPY", # numpy + "Q", # flake8-quotes + "RUF", # ruff + "UP", # pyupgrade + "W", # flake8 +] +ignore = [ + "A001", # shadowing-builtin-variable + "A005", # shadowing-builtin-module + "C409", # incorrectly-parenthesized-tuple-in-subscript + "E203", # whitespace-before-punctuation + "E402", # module-import-not-at-top-of-file + "E501", # line-too-long + "E731", # assign-to-lambda + "RUF001", # ambiguous-unicode-character-string + "RUF002", # ambiguous-unicode-character-docstring + "RUF003", # ambiguous-unicode-character-comment + "W191", # indentation-contains-tabs + "W293", # blank-line-with-whitespace +] + +[tool.ruff.lint.flake8-quotes] +docstring-quotes = "double" +inline-quotes = "double" +multiline-quotes = "double" + +[tool.ruff.lint.isort] +combine-as-imports = true +known-local-folder = [ "jitcode", "scenarios" ] +known-first-party = [ "jitcxde_common" ] +lines-after-imports = 2 From fafdd053c64f6b4a6e2196542d40bb2f6e6342ae Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 13 Jan 2025 14:16:46 +0200 Subject: [PATCH 02/10] Add ruff CI job --- .github/workflows/ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1df0059..b29faa9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,20 @@ concurrency: cancel-in-progress: true jobs: + typos: + name: Spelling (typos) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: crate-ci/typos@master + + ruff: + name: Linting (ruff) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: chartboost/ruff-action@v1 + test: name: Unittest (${{ matrix.os }}-${{ matrix.compiler }}-py${{ matrix.python-version }}) runs-on: ${{ matrix.os }} From b0a22bf3684c26b9b66074222530ed9dd7aaf0a4 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 13 Jan 2025 14:18:34 +0200 Subject: [PATCH 03/10] Fix ruff I (isort) errors --- docs/conf.py | 4 +++- examples/SW_of_Roesslers.py | 3 ++- examples/benchmark.py | 9 ++++++--- examples/double_fhn_example.py | 3 ++- examples/double_fhn_lyapunov.py | 5 +++-- examples/double_fhn_restricted_lyap.py | 4 +++- examples/double_fhn_transversal_lyap.py | 5 +++-- examples/kuramoto_network.py | 8 +++++--- examples/lotka_volterra.py | 3 ++- jitcode/__init__.py | 15 ++++++++++----- jitcode/_jitcode.py | 15 ++++++++------- jitcode/integrator_tools.py | 5 +++-- jitcode/sympy_symbols.py | 1 + tests/scenarios.py | 7 +++++-- tests/test_generation.py | 6 +----- tests/test_integrator_tools.py | 6 +++--- tests/test_jitcode.py | 6 ++---- tests/test_order.py | 7 +++++-- tests/test_sympy_input.py | 6 ++++-- tests/test_transversal_lyap.py | 8 ++++++-- 20 files changed, 77 insertions(+), 49 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a305e49..70d6feb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,8 +1,10 @@ -import sys import os +import sys from unittest.mock import MagicMock as Mock + from setuptools_scm import get_version + MOCK_MODULES = [ 'numpy', 'numpy.testing', 'numpy.random', 'scipy', 'scipy.integrate', 'scipy.integrate._ode', 'scipy.stats', 'scipy.integrate._ivp.ivp', diff --git a/examples/SW_of_Roesslers.py b/examples/SW_of_Roesslers.py index eac9a08..0e9c1c1 100644 --- a/examples/SW_of_Roesslers.py +++ b/examples/SW_of_Roesslers.py @@ -60,9 +60,10 @@ def small_world_network(number_of_nodes, nearest_neighbours, rewiring_probabilit return A # example-start - from jitcode import jitcode, y, t import numpy as np import symengine + + from jitcode import jitcode, t, y # parameters # ---------- diff --git a/examples/benchmark.py b/examples/benchmark.py index 38efbf6..5fe31fc 100644 --- a/examples/benchmark.py +++ b/examples/benchmark.py @@ -1,11 +1,14 @@ +from time import process_time + import numpy as np from numpy.random import choice, uniform -from time import process_time -from scipy.integrate import ode, solve_ivp, odeint +from scipy.integrate import ode, odeint, solve_ivp from scipy.integrate._ivp.ivp import METHODS -from jitcode import jitcode, y from symengine import sin +from jitcode import jitcode, y + + solver_ode = "dopri5" solver_ivp = "RK45" diff --git a/examples/double_fhn_example.py b/examples/double_fhn_example.py index 9b7c5df..78ab88a 100644 --- a/examples/double_fhn_example.py +++ b/examples/double_fhn_example.py @@ -20,8 +20,9 @@ if __name__ == "__main__": # example-start - from jitcode import jitcode, y import numpy as np + + from jitcode import jitcode, y a = -0.025794 b1 = 0.0065 diff --git a/examples/double_fhn_lyapunov.py b/examples/double_fhn_lyapunov.py index c73555c..f149ea8 100644 --- a/examples/double_fhn_lyapunov.py +++ b/examples/double_fhn_lyapunov.py @@ -7,9 +7,10 @@ if __name__ == "__main__": # example-start - from jitcode import jitcode_lyap, y - from scipy.stats import sem import numpy as np + from scipy.stats import sem + + from jitcode import jitcode_lyap, y a = -0.025794 b1 = 0.0065 diff --git a/examples/double_fhn_restricted_lyap.py b/examples/double_fhn_restricted_lyap.py index f76d555..e42832b 100644 --- a/examples/double_fhn_restricted_lyap.py +++ b/examples/double_fhn_restricted_lyap.py @@ -1,10 +1,12 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- -from jitcode import jitcode_restricted_lyap, y import numpy as np from scipy.stats import sem +from jitcode import jitcode_restricted_lyap, y + + a = -0.025794 b1 = 0.01 b2 = 0.01 diff --git a/examples/double_fhn_transversal_lyap.py b/examples/double_fhn_transversal_lyap.py index 0afb4e1..623ef30 100644 --- a/examples/double_fhn_transversal_lyap.py +++ b/examples/double_fhn_transversal_lyap.py @@ -15,9 +15,10 @@ if __name__ == "__main__": # example-start - from jitcode import jitcode_transversal_lyap, y - from scipy.stats import sem import numpy as np + from scipy.stats import sem + + from jitcode import jitcode_transversal_lyap, y a = -0.025794 b = 0.01 diff --git a/examples/kuramoto_network.py b/examples/kuramoto_network.py index c34479a..78cc485 100755 --- a/examples/kuramoto_network.py +++ b/examples/kuramoto_network.py @@ -1,7 +1,9 @@ -from numpy import pi, vstack, savetxt -from jitcode import jitcode, y -from symengine import sin +from numpy import pi, savetxt, vstack from numpy.random import choice, uniform +from symengine import sin + +from jitcode import jitcode, y + n = 100 c = 3.0 diff --git a/examples/lotka_volterra.py b/examples/lotka_volterra.py index 357562e..c332a6d 100644 --- a/examples/lotka_volterra.py +++ b/examples/lotka_volterra.py @@ -90,8 +90,9 @@ if __name__ == "__main__": # example-start - from jitcode import y, jitcode import numpy as np + + from jitcode import jitcode, y γ = 0.6 φ = 1.0 diff --git a/jitcode/__init__.py b/jitcode/__init__.py index b77aed5..61234ab 100644 --- a/jitcode/__init__.py +++ b/jitcode/__init__.py @@ -1,9 +1,14 @@ from ._jitcode import ( - t, y, - jitcode, jitcode_lyap, jitcode_restricted_lyap, jitcode_transversal_lyap, - UnsuccessfulIntegration, - test, - ) + UnsuccessfulIntegration, + jitcode, + jitcode_lyap, + jitcode_restricted_lyap, + jitcode_transversal_lyap, + t, + test, + y, +) + try: from .version import version as __version__ diff --git a/jitcode/_jitcode.py b/jitcode/_jitcode.py index 874d3e1..ef6ab18 100644 --- a/jitcode/_jitcode.py +++ b/jitcode/_jitcode.py @@ -1,22 +1,23 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- -from warnings import warn -from types import FunctionType, BuiltinFunctionType from inspect import signature from itertools import count +from types import BuiltinFunctionType, FunctionType +from warnings import warn -from numpy import hstack, log import numpy as np import symengine +from numpy import hstack, log -from jitcxde_common import jitcxde, checker -from jitcxde_common.helpers import sympify_helpers, sort_helpers, find_dependent_helpers -from jitcxde_common.numerical import random_direction, orthonormalise_qr +from jitcxde_common import checker, jitcxde +from jitcxde_common.helpers import find_dependent_helpers, sort_helpers, sympify_helpers +from jitcxde_common.numerical import orthonormalise_qr, random_direction from jitcxde_common.symbolic import collect_arguments, ordered_subs, replace_function from jitcxde_common.transversal import GroupHandler -from jitcode.integrator_tools import empty_integrator, IVP_wrapper, IVP_wrapper_no_interpolation, ODE_wrapper, integrator_info, UnsuccessfulIntegration +from jitcode.integrator_tools import IVP_wrapper, IVP_wrapper_no_interpolation, ODE_wrapper, UnsuccessfulIntegration, empty_integrator, integrator_info + #: the symbol for the state that must be used to define the differential equation. It is a function and the integer argument denotes the component. You may just as well define an analogous function directly with SymEngine or SymPy, but using this function is the best way to get the most of future versions of JiTCODE, in particular avoiding incompatibilities. You can import a SymPy variant from the submodule `sympy_symbols` instead (see `SymPy vs. SymEngine`_ for details). y = symengine.Function("y") diff --git a/jitcode/integrator_tools.py b/jitcode/integrator_tools.py index 858c253..7621159 100644 --- a/jitcode/integrator_tools.py +++ b/jitcode/integrator_tools.py @@ -1,9 +1,10 @@ from inspect import signature +from numpy import inf from scipy.integrate import ode -from scipy.integrate._ode import find_integrator from scipy.integrate._ivp.ivp import METHODS as ivp_methods -from numpy import inf +from scipy.integrate._ode import find_integrator + class UnsuccessfulIntegration(Exception): """ diff --git a/jitcode/sympy_symbols.py b/jitcode/sympy_symbols.py index b97c692..65d9688 100644 --- a/jitcode/sympy_symbols.py +++ b/jitcode/sympy_symbols.py @@ -1,5 +1,6 @@ import sympy + #: the symbol for the state that must be used to define the differential equation. It is a function and the integer argument denotes the component. This one is different from the one you can import from jitcode directly by being defined via SymPy and thus being better suited for some symbolic processing techniques that are not available in SymEngine yet. y = sympy.Function("y",real=True) diff --git a/tests/scenarios.py b/tests/scenarios.py index bb4e177..ff135d2 100644 --- a/tests/scenarios.py +++ b/tests/scenarios.py @@ -1,10 +1,13 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- +from random import shuffle + import numpy as np -from symengine import symbols, Function +from symengine import Function, symbols + from jitcode import y -from random import shuffle + n = 4 diff --git a/tests/test_generation.py b/tests/test_generation.py index 6c8c774..6f28ad8 100644 --- a/tests/test_generation.py +++ b/tests/test_generation.py @@ -12,12 +12,8 @@ from jitcode import jitcode, jitcode_lyap, y from jitcode._jitcode import _is_C, _is_lambda +from scenarios import callback, f_of_y0, jac_of_y0, n, params_args, vanilla, with_dictionary, with_generator, with_helpers, with_params, y0 -from scenarios import ( - y0, f_of_y0, jac_of_y0, - vanilla, with_params, with_helpers, with_generator, with_dictionary, callback, - n, params_args - ) class TestBasic(unittest.TestCase): init_params = () diff --git a/tests/test_integrator_tools.py b/tests/test_integrator_tools.py index 3030d12..5610a03 100644 --- a/tests/test_integrator_tools.py +++ b/tests/test_integrator_tools.py @@ -4,13 +4,13 @@ import unittest import numpy as np -from numpy.testing import assert_allclose from numpy.random import random +from numpy.testing import assert_allclose from jitcode import jitcode -from jitcode.integrator_tools import empty_integrator, IVP_wrapper, IVP_wrapper_no_interpolation, ODE_wrapper, UnsuccessfulIntegration +from jitcode.integrator_tools import IVP_wrapper, IVP_wrapper_no_interpolation, ODE_wrapper, UnsuccessfulIntegration, empty_integrator +from scenarios import n, vanilla, y0, y1 -from scenarios import y0, y1, vanilla, n # Generating compiled functions diff --git a/tests/test_jitcode.py b/tests/test_jitcode.py index d035386..2870608 100644 --- a/tests/test_jitcode.py +++ b/tests/test_jitcode.py @@ -8,12 +8,10 @@ from scipy.stats import sem as standard_error from symengine import symbols -from jitcode import jitcode, y, jitcode_lyap, UnsuccessfulIntegration, test +from jitcode import UnsuccessfulIntegration, jitcode, jitcode_lyap, test, y from jitcode._jitcode import _is_C, _is_lambda +from scenarios import f_of_y0, jac_of_y0, lyaps, n, vanilla, y0, y1 -from scenarios import ( - y0, f_of_y0, jac_of_y0, y1, lyaps, vanilla, n, - ) class TestOrders(unittest.TestCase): """ diff --git a/tests/test_order.py b/tests/test_order.py index 682a064..00344d9 100644 --- a/tests/test_order.py +++ b/tests/test_order.py @@ -1,7 +1,10 @@ -from jitcode import jitcode -from symengine import Symbol from itertools import permutations +from symengine import Symbol + +from jitcode import jitcode + + p = Symbol('p') f = [1/p] diff --git a/tests/test_sympy_input.py b/tests/test_sympy_input.py index 263dede..421042e 100644 --- a/tests/test_sympy_input.py +++ b/tests/test_sympy_input.py @@ -2,10 +2,12 @@ Tests whether things works independent of where symbols are imported from. """ +import symengine +import sympy + import jitcode import jitcode.sympy_symbols -import sympy -import symengine + symengine_manually = [ symengine.Symbol("t",real=True), diff --git a/tests/test_transversal_lyap.py b/tests/test_transversal_lyap.py index f6bd61b..597aa9d 100644 --- a/tests/test_transversal_lyap.py +++ b/tests/test_transversal_lyap.py @@ -6,12 +6,16 @@ """ from itertools import combinations -from jitcode import jitcode_restricted_lyap, y, jitcode_transversal_lyap -from jitcxde_common import DEFAULT_COMPILE_ARGS + import numpy as np from scipy.stats import sem from symengine import Symbol +from jitcxde_common import DEFAULT_COMPILE_ARGS + +from jitcode import jitcode_restricted_lyap, jitcode_transversal_lyap, y + + a = -0.025794 b = 0.01 c = 0.02 From 07797cd8603c2726b82195cabf2f166c315b59bd Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 13 Jan 2025 14:33:13 +0200 Subject: [PATCH 04/10] Fix ruff UP (pyupgrade) errors --- docs/conf.py | 12 ++++++--- examples/SW_of_Roesslers.py | 1 - examples/benchmark.py | 18 ++++++------- examples/double_fhn_example.py | 3 +-- examples/double_fhn_lyapunov.py | 3 +-- examples/double_fhn_restricted_lyap.py | 1 - examples/double_fhn_transversal_lyap.py | 3 +-- examples/lotka_volterra.py | 1 - jitcode/_jitcode.py | 35 ++++++++++++------------- jitcode/integrator_tools.py | 6 ++--- tests/scenarios.py | 4 +-- tests/test_generation.py | 1 - tests/test_integrator_tools.py | 3 +-- tests/test_jitcode.py | 1 - tests/test_transversal_lyap.py | 3 +-- 15 files changed, 43 insertions(+), 52 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 70d6feb..820f0ab 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,9 +13,13 @@ ] sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) -class ode_mock(object): pass +class ode_mock: + pass + +class GroupHandler_mock: + pass + sys.modules['scipy.integrate'] = Mock(ode=ode_mock) -class GroupHandler_mock(object): pass sys.modules['jitcxde_common.transversal'] = Mock(GroupHandler=GroupHandler_mock) sys.path.insert(0,os.path.abspath("../examples")) @@ -35,8 +39,8 @@ class GroupHandler_mock(object): pass master_doc = 'index' -project = u'JiTCODE' -copyright = u'2016, Gerrit Ansmann' +project = 'JiTCODE' +copyright = '2016, Gerrit Ansmann' release = version = get_version(root='..', relative_to=__file__) diff --git a/examples/SW_of_Roesslers.py b/examples/SW_of_Roesslers.py index 0e9c1c1..5a3656e 100644 --- a/examples/SW_of_Roesslers.py +++ b/examples/SW_of_Roesslers.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- """ This example showcases several advanced features of JiTCODE that are relevant for an efficient integration of more complex systems as well as how to deal with some special situations. Therefore it is pretty bizarre from a dynamical perspective. diff --git a/examples/benchmark.py b/examples/benchmark.py index 5fe31fc..df3848d 100644 --- a/examples/benchmark.py +++ b/examples/benchmark.py @@ -13,7 +13,7 @@ solver_ivp = "RK45" # Context manager for timing -class timer(object): +class timer: def __init__(self,name): self.name = name @@ -23,13 +23,13 @@ def __enter__(self): def __exit__(self,*args): end = process_time() duration = end-self.start - print("%s took %.5f s" % (self.name,duration)) + print(f"{self.name} took {duration:.5f}s") # The actual test def test_scenario(name,fun,initial,times,rtol,atol): print(40*"-",name,40*"-",sep="\n") - with timer("ode (%s)"%solver_ode): + with timer(f"ode ({solver_ode})"): I = ode(fun) I.set_integrator(solver_ode,rtol=rtol,atol=atol,nsteps=10**8) I.set_initial_value(initial,0.0) @@ -45,7 +45,7 @@ def test_scenario(name,fun,initial,times,rtol,atol): mxstep=10**8 ) - with timer("solve_ivp (%s) without result"%solver_ivp): + with timer(f"solve_ivp ({solver_ivp}) without result"): I = solve_ivp( fun, t_span=(0,times[-1]), @@ -54,7 +54,7 @@ def test_scenario(name,fun,initial,times,rtol,atol): ) assert I.status != -1 - with timer("solve_ivp (%s)"%solver_ivp): + with timer(f"solve_ivp ({solver_ivp})"): I = solve_ivp( fun, t_span=(0,times[-1]), t_eval=times, @@ -64,7 +64,7 @@ def test_scenario(name,fun,initial,times,rtol,atol): result = I.y assert I.status != -1 - with timer("solve_ivp (%s) with dense_output"%solver_ivp): + with timer(f"solve_ivp ({solver_ivp}) with dense_output"): I = solve_ivp( fun, t_span=(0,times[-1]), @@ -75,7 +75,7 @@ def test_scenario(name,fun,initial,times,rtol,atol): result = np.vstack([I.sol(time) for time in times]) assert I.status != -1 - with timer("%s with dense output"%solver_ivp): + with timer(f"{solver_ivp} with dense output"): I = METHODS[solver_ivp]( fun=fun, y0=initial, t0=0.0, t_bound=times[-1], @@ -89,7 +89,7 @@ def solutions(): result = np.vstack(list(solutions())) assert I.status != "failed" - with timer("%s with manual resetting"%solver_ivp): + with timer(f"{solver_ivp} with manual resetting"): I = METHODS[solver_ivp]( fun=fun, y0=initial, t0=0.0, t_bound=times[-1], @@ -105,7 +105,7 @@ def solutions(): result = np.vstack(list(solutions())) assert I.status != "failed" - with timer("%s with reinitialising"%solver_ivp): + with timer(f"{solver_ivp} with reinitialising"): def solutions(): current_time = 0.0 state = initial diff --git a/examples/double_fhn_example.py b/examples/double_fhn_example.py index 78ab88a..eb12361 100644 --- a/examples/double_fhn_example.py +++ b/examples/double_fhn_example.py @@ -1,7 +1,6 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- -""" +r""" Suppose our differential equation is :math:`\dot{y} = f(y)` with :math:`y∈ℝ^4`, .. math:: diff --git a/examples/double_fhn_lyapunov.py b/examples/double_fhn_lyapunov.py index f149ea8..4f5caf0 100644 --- a/examples/double_fhn_lyapunov.py +++ b/examples/double_fhn_lyapunov.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- """ For instance, we can calculate and print the Lyapunov exponents for the system from `example` as follows (changes highlighted): @@ -43,4 +42,4 @@ for i in range(n): lyap = np.average(lyaps[1000:,i]) stderr = sem(lyaps[1000:,i]) # Note that this only an estimate - print("%i. Lyapunov exponent: % .4f ± %.4f" % (i+1,lyap,stderr)) + print(f"{i+1}. Lyapunov exponent: {lyap:.4f} ± {stderr:.4f}") diff --git a/examples/double_fhn_restricted_lyap.py b/examples/double_fhn_restricted_lyap.py index e42832b..ba82846 100644 --- a/examples/double_fhn_restricted_lyap.py +++ b/examples/double_fhn_restricted_lyap.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- import numpy as np from scipy.stats import sem diff --git a/examples/double_fhn_transversal_lyap.py b/examples/double_fhn_transversal_lyap.py index 623ef30..e03942b 100644 --- a/examples/double_fhn_transversal_lyap.py +++ b/examples/double_fhn_transversal_lyap.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- """ For instance, let’s interpret the system from `example` as two oscillators (which is what it is), one consisting of the first and second and one of the third and fourth component. Furthermore, let’s change the control parameters a bit to make the two oscillators identical. We can then calculate the transversal Lyapunov exponents to the synchronisation manifold as follows (important changes are highlighted): @@ -46,5 +45,5 @@ lyap = np.average(lyaps[1000:]) stderr = sem(lyaps[1000:]) # Note that this only an estimate - print("transversal Lyapunov exponent: % .4f ± %.4f" % (lyap,stderr)) + print(f"transversal Lyapunov exponent: {lyap:.4f} ± {stderr:.4f}") diff --git a/examples/lotka_volterra.py b/examples/lotka_volterra.py index c332a6d..5a1dee7 100644 --- a/examples/lotka_volterra.py +++ b/examples/lotka_volterra.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- """ Suppose, we want to implement the Lotka–Volterra model, which is described by the following equations: diff --git a/jitcode/_jitcode.py b/jitcode/_jitcode.py index ef6ab18..2d1ca2f 100644 --- a/jitcode/_jitcode.py +++ b/jitcode/_jitcode.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- from inspect import signature from itertools import count @@ -138,11 +137,11 @@ def _check_valid_arguments(self): for argument in collect_arguments(entry,y): self._check_assert( argument[0] >= 0, - "y is called with a negative argument (%i) in equation %i." % (argument[0],i), + f"y is called with a negative argument ({argument[0]}) in equation {i}.", ) self._check_assert( argument[0] < self.n, - "y is called with an argument (%i) higher than the system’s dimension (%i) in equation %i." % (argument[0],self.n,i) + f"y is called with an argument ({argument[0]}) higher than the system’s dimension ({self.n}) in equation {i}." ) @checker @@ -153,7 +152,7 @@ def _check_valid_symbols(self): for symbol in entry.atoms(symengine.Symbol): self._check_assert( symbol in valid_symbols, - "Invalid symbol (%s) in equation %i." % (symbol.name,i), + f"Invalid symbol ({symbol.name}) in equation {i}.", ) @property @@ -430,7 +429,7 @@ def _prepare_lambdas(self): self._lambda_subs = list(reversed(self.helpers)) self._lambda_args = [t] for i in range(self.n): - symbol = symengine.Symbol("dummy_argument_%i"%i) + symbol = symengine.Symbol(f"dummy_argument_{i}") self._lambda_args.append(symbol) self._lambda_subs.append((y(i),symbol)) self._lambda_args.extend(self.control_pars) @@ -583,7 +582,7 @@ def set_integrator(self,name,nsteps=10**6,interpolate=True,**integrator_params): The `solve_ivp` methods are usually slightly faster for large differential equations, but they come with a massive overhead that makes them considerably slower for small differential equations. Implicit solvers are slower than explicit ones, except for stiff problems. If you don’t know what to choose, start with `"dopri5"`. nsteps: integer - Same as the respective parameter of the `ode` solvers, but with a higher default value to avoid annoying errors when getting rid of transients. + Same as the respective parameter of the `ode` solvers, but with a higher default value to avoid annoying errors when getting rid of transients. interpolate: boolean Whether the sampled solutions for `solve_ivp` solvers shall be obtained using interpolation. If your sampling step is small, this may make things faster; otherwise it depends. This may also make the results slightly less accurate. @@ -690,7 +689,7 @@ def __init__( self, f_sym=(), n_lyap=-1, simplify=None, **kwargs ): f_basic = self._handle_input(f_sym,n_basic=True) self._n_lyap = n_lyap if (0<=n_lyap<=self.n_basic) else self.n_basic if self._n_lyap>10: - warn("You are about to calculate %i Lyapunov exponents. This is very likely more than you need and may lead to severe difficulties with compilation and integration. Unless you really know what you are doing, consider how many Lyapunov exponents you actually need and set the parameter `n_lyap` accordingly." % self._n_lyap) + warn(f"You are about to calculate {self._n_lyap} Lyapunov exponents. This is very likely more than you need and may lead to severe difficulties with compilation and integration. Unless you really know what you are doing, consider how many Lyapunov exponents you actually need and set the parameter `n_lyap` accordingly.") if simplify is None: simplify = self.n_basic<=10 @@ -716,7 +715,7 @@ def f_lyap(): expression = expression.simplify(ratio=1.0) yield expression - super(jitcode_lyap, self).__init__( + super().__init__( f_lyap, helpers = helpers, n = self.n_basic*(self._n_lyap+1), @@ -731,7 +730,7 @@ def set_initial_value(self, y, time=0.0): for _ in range(self._n_lyap): new_y.append(random_direction(self.n_basic)) - super(jitcode_lyap, self).set_initial_value(hstack(new_y), time) + super().set_initial_value(hstack(new_y), time) def set_integrator(self,name,interpolate=None,**kwargs): """ @@ -741,7 +740,7 @@ def set_integrator(self,name,interpolate=None,**kwargs): interpolate = name in ["RK45","Radau"] if name == "LSODA": warn("Using LSODA for Lyapunov exponents is discouraged since interpolation errors may accumulate.") - super(jitcode_lyap,self).set_integrator(name,interpolate,**kwargs) + super().set_integrator(name,interpolate,**kwargs) def norms(self): n = self.n_basic @@ -768,12 +767,12 @@ def integrate(self, *args, **kwargs): """ old_t = self.t - super(jitcode_lyap, self).integrate(*args, **kwargs) + super().integrate(*args, **kwargs) delta_t = self.t-old_t norms, tangent_vectors = self.norms() lyaps = log(norms) / delta_t self._y[self.n_basic:] = tangent_vectors.flatten() - super(jitcode_lyap, self).set_initial_value(self._y, self.t) + super().set_initial_value(self._y, self.t) return self._y[:self.n_basic], lyaps, tangent_vectors @@ -856,7 +855,7 @@ def f_lyap(): helpers = ((helper[0],finalise(helper[1])) for helper in helpers) - super(jitcode_transversal_lyap, self).__init__( + super().__init__( f_lyap, helpers = helpers, n = self.n, @@ -874,7 +873,7 @@ def set_initial_value(self, y, time=0.0): new_y = np.empty(self.n) new_y[self.main_indices] = y new_y[self.tangent_indices] = random_direction(len(self.tangent_indices)) - super(jitcode_transversal_lyap, self).set_initial_value(new_y, time) + super().set_initial_value(new_y, time) def set_integrator(self,name,interpolate=False,**kwargs): """ @@ -884,7 +883,7 @@ def set_integrator(self,name,interpolate=False,**kwargs): interpolate = name in ["RK45","Radau"] if name == "LSODA": warn("Using LSODA for Lyapunov exponents is discouraged since interpolation errors may accumulate.") - super(jitcode_transversal_lyap,self).set_integrator(name,interpolate,**kwargs) + super().set_integrator(name,interpolate,**kwargs) def norm(self): tangent_vector = self._y[self.tangent_indices] @@ -909,11 +908,11 @@ def integrate(self, *args, **kwargs): """ old_t = self.t - super(jitcode_transversal_lyap, self).integrate(*args, **kwargs) + super().integrate(*args, **kwargs) delta_t = self.t-old_t norm = self.norm() lyap = log(norm) / delta_t - super(jitcode_transversal_lyap, self).set_initial_value(self._y, self.t) + super().set_initial_value(self._y, self.t) return self._y[self.main_indices], lyap @@ -934,7 +933,7 @@ class jitcode_restricted_lyap(jitcode_lyap): def __init__(self, f_sym=(), vectors=(), **kwargs): kwargs["n_lyap"] = 1 - super(jitcode_restricted_lyap,self).__init__(f_sym,**kwargs) + super().__init__(f_sym,**kwargs) self.vectors = [ vector/np.linalg.norm(vector) for vector in vectors ] def norms(self): diff --git a/jitcode/integrator_tools.py b/jitcode/integrator_tools.py index 7621159..16eed24 100644 --- a/jitcode/integrator_tools.py +++ b/jitcode/integrator_tools.py @@ -36,7 +36,7 @@ def integrator_info(name): "integrator": integrator } -class IVP_wrapper(object): +class IVP_wrapper: """ This is a wrapper around the integrators from scipy.integrate.solve_ivp making them work like scipy.integrate.ode (mostly). """ @@ -133,7 +133,7 @@ class ODE_wrapper(ode): """ def integrate(self,t,step=False,relax=False): if t>self.t or step or relax: - result = super(ODE_wrapper,self).integrate(t,step,relax) + result = super().integrate(t,step,relax) if self.successful(): return result else: @@ -150,7 +150,7 @@ def params(self): def set_params(self,*args): raise NotImplementedError("This method should not be called anymore") -class empty_integrator(object): +class empty_integrator: """ This is a dummy class that mimicks some basic properties of scipy.integrate.ode or the above wrappers, respectively. It exists to store states and parameters and to raise exceptions in the same interface. """ diff --git a/tests/scenarios.py b/tests/scenarios.py index ff135d2..55b7b1d 100644 --- a/tests/scenarios.py +++ b/tests/scenarios.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- from random import shuffle @@ -62,8 +61,7 @@ # with generator def f_generator(): - for entry in f: - yield entry + yield from f with_generator = { "f_sym":f_generator, "n":n } diff --git a/tests/test_generation.py b/tests/test_generation.py index 6f28ad8..c3062b2 100644 --- a/tests/test_generation.py +++ b/tests/test_generation.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- """ Tests that the code-generation and compilaton or lambdification, respectively, of the derivative and Jacobian works as intended in all kinds of scenarios. diff --git a/tests/test_integrator_tools.py b/tests/test_integrator_tools.py index 5610a03..65d50e8 100644 --- a/tests/test_integrator_tools.py +++ b/tests/test_integrator_tools.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- import unittest @@ -30,7 +29,7 @@ # ----------------------------- -class TestSkeleton(object): +class TestSkeleton: """ This class exists to be inherited by a test that adds self.initialise to intialise self.integrator. """ diff --git a/tests/test_jitcode.py b/tests/test_jitcode.py index 2870608..5fc57ed 100644 --- a/tests/test_jitcode.py +++ b/tests/test_jitcode.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- import unittest diff --git a/tests/test_transversal_lyap.py b/tests/test_transversal_lyap.py index 597aa9d..750fced 100644 --- a/tests/test_transversal_lyap.py +++ b/tests/test_transversal_lyap.py @@ -1,5 +1,4 @@ #!/usr/bin/python3 -# -*- coding: utf-8 -*- """ Integration test of jitcode_restricted_lyap and jitcode_transversal_lyap by comparing their results to each other for a synchronised scenario. @@ -130,7 +129,7 @@ sign2 = np.sign(Lyap2) if abs(Lyap2)>margin2 else 0 assert sign1==coupling["sign"] assert sign2==coupling["sign"] - assert abs(Lyap1-Lyap2) Date: Mon, 13 Jan 2025 14:36:56 +0200 Subject: [PATCH 05/10] Fix ruff Q (quotes) errors --- docs/conf.py | 48 ++++++++++++++++++------------------- examples/SW_of_Roesslers.py | 2 +- jitcode/__init__.py | 2 +- jitcode/integrator_tools.py | 2 +- tests/test_order.py | 2 +- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 820f0ab..431b03f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,10 +6,10 @@ MOCK_MODULES = [ - 'numpy', 'numpy.testing', 'numpy.random', - 'scipy', 'scipy.integrate', 'scipy.integrate._ode', 'scipy.stats', 'scipy.integrate._ivp.ivp', - 'symengine', 'symengine.printing', 'symengine.lib.symengine_wrapper', - 'jitcxde_common.helpers','jitcxde_common.numerical','jitcxde_common.symbolic','jitcxde_common.transversal', + "numpy", "numpy.testing", "numpy.random", + "scipy", "scipy.integrate", "scipy.integrate._ode", "scipy.stats", "scipy.integrate._ivp.ivp", + "symengine", "symengine.printing", "symengine.lib.symengine_wrapper", + "jitcxde_common.helpers","jitcxde_common.numerical","jitcxde_common.symbolic","jitcxde_common.transversal", ] sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) @@ -19,30 +19,30 @@ class ode_mock: class GroupHandler_mock: pass -sys.modules['scipy.integrate'] = Mock(ode=ode_mock) -sys.modules['jitcxde_common.transversal'] = Mock(GroupHandler=GroupHandler_mock) +sys.modules["scipy.integrate"] = Mock(ode=ode_mock) +sys.modules["jitcxde_common.transversal"] = Mock(GroupHandler=GroupHandler_mock) sys.path.insert(0,os.path.abspath("../examples")) sys.path.insert(0,os.path.abspath("../jitcode")) -needs_sphinx = '1.3' +needs_sphinx = "1.3" extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.mathjax', - 'numpydoc', - 'sphinx.ext.graphviz' + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.mathjax", + "sphinx.ext.graphviz" + "numpydoc", ] -source_suffix = '.rst' +source_suffix = ".rst" -master_doc = 'index' +master_doc = "index" -project = 'JiTCODE' -copyright = '2016, Gerrit Ansmann' +project = "JiTCODE" +copyright = "2016, Gerrit Ansmann" -release = version = get_version(root='..', relative_to=__file__) +release = version = get_version(root="..", relative_to=__file__) default_role = "any" @@ -50,22 +50,22 @@ class GroupHandler_mock: add_module_names = False -html_theme = 'nature' -pygments_style = 'colorful' -htmlhelp_basename = 'JiTCODEdoc' +html_theme = "nature" +pygments_style = "colorful" +htmlhelp_basename = "JiTCODEdoc" numpydoc_show_class_members = False -autodoc_member_order = 'bysource' +autodoc_member_order = "bysource" graphviz_output_format = "svg" -toc_object_entries_show_parents = 'hide' +toc_object_entries_show_parents = "hide" def on_missing_reference(app, env, node, contnode): - if node['reftype'] == 'any': + if node["reftype"] == "any": return contnode else: return None def setup(app): - app.connect('missing-reference', on_missing_reference) + app.connect("missing-reference", on_missing_reference) diff --git a/examples/SW_of_Roesslers.py b/examples/SW_of_Roesslers.py index 5a3656e..774b94f 100644 --- a/examples/SW_of_Roesslers.py +++ b/examples/SW_of_Roesslers.py @@ -103,7 +103,7 @@ def f(): ODE = jitcode(f, helpers=helpers, n=3*N) ODE.generate_f_C(simplify=False, do_cse=False, chunk_size=150) - ODE.set_integrator('dopri5') + ODE.set_integrator("dopri5") ODE.set_initial_value(initial_state,0.0) # data structure: x[0], v[0], z[0], x[1], …, x[N], v[N], z[N] diff --git a/jitcode/__init__.py b/jitcode/__init__.py index 61234ab..92ee5b4 100644 --- a/jitcode/__init__.py +++ b/jitcode/__init__.py @@ -14,4 +14,4 @@ from .version import version as __version__ except ImportError: from warnings import warn - warn('Failed to find (autogenerated) version.py. Do not worry about this unless you really need to know the version.') + warn("Failed to find (autogenerated) version.py. Do not worry about this unless you really need to know the version.") diff --git a/jitcode/integrator_tools.py b/jitcode/integrator_tools.py index 16eed24..25f598c 100644 --- a/jitcode/integrator_tools.py +++ b/jitcode/integrator_tools.py @@ -16,7 +16,7 @@ def integrator_info(name): """ Finds out the integrator from a given name, what backend it uses, and whether it can use a Jacobian. """ - if name == 'zvode': + if name == "zvode": raise NotImplementedError("JiTCODE does not natively support complex numbers yet.") if name in ivp_methods.keys(): diff --git a/tests/test_order.py b/tests/test_order.py index 00344d9..3384539 100644 --- a/tests/test_order.py +++ b/tests/test_order.py @@ -5,7 +5,7 @@ from jitcode import jitcode -p = Symbol('p') +p = Symbol("p") f = [1/p] From 32487818d07321cdd926e4fae3abeaeccb38da59 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 13 Jan 2025 14:46:00 +0200 Subject: [PATCH 06/10] Fix ruff F+E (flake8) errors --- examples/benchmark.py | 70 ++++++++++++++++++------------------ examples/kuramoto_network.py | 8 ++--- jitcode/__init__.py | 14 ++------ jitcode/_jitcode.py | 11 +++--- tests/scenarios.py | 4 +-- tests/test_generation.py | 4 +-- tests/test_jitcode.py | 6 ++-- 7 files changed, 54 insertions(+), 63 deletions(-) diff --git a/examples/benchmark.py b/examples/benchmark.py index df3848d..8fa8c87 100644 --- a/examples/benchmark.py +++ b/examples/benchmark.py @@ -30,98 +30,98 @@ def test_scenario(name,fun,initial,times,rtol,atol): print(40*"-",name,40*"-",sep="\n") with timer(f"ode ({solver_ode})"): - I = ode(fun) - I.set_integrator(solver_ode,rtol=rtol,atol=atol,nsteps=10**8) - I.set_initial_value(initial,0.0) - result = np.vstack([I.integrate(time) for time in times]) - assert I.successful() + solver = ode(fun) + solver.set_integrator(solver_ode,rtol=rtol,atol=atol,nsteps=10**8) + solver.set_initial_value(initial,0.0) + _result = np.vstack([solver.integrate(time) for time in times]) + assert solver.successful() inv_fun = lambda y,t: fun(t,y) with timer("odeint with suboptimal function (LSODA)"): - result = odeint( + _result = odeint( func=inv_fun, - y0=initial, t=[0.0]+list(times), + y0=initial, t=[0.0, *times], rtol=rtol, atol=atol, mxstep=10**8 ) with timer(f"solve_ivp ({solver_ivp}) without result"): - I = solve_ivp( + solver = solve_ivp( fun, t_span=(0,times[-1]), y0=initial, method=solver_ivp, rtol=rtol, atol=atol ) - assert I.status != -1 + assert solver.status != -1 with timer(f"solve_ivp ({solver_ivp})"): - I = solve_ivp( + solver = solve_ivp( fun, t_span=(0,times[-1]), t_eval=times, y0=initial, method=solver_ivp, rtol=rtol, atol=atol ) - result = I.y - assert I.status != -1 + _result = solver.y + assert solver.status != -1 with timer(f"solve_ivp ({solver_ivp}) with dense_output"): - I = solve_ivp( + solver = solve_ivp( fun, t_span=(0,times[-1]), y0=initial, method=solver_ivp, rtol=rtol, atol=atol, dense_output=True ) - result = np.vstack([I.sol(time) for time in times]) - assert I.status != -1 + _result = np.vstack([solver.sol(time) for time in times]) + assert solver.status != -1 with timer(f"{solver_ivp} with dense output"): - I = METHODS[solver_ivp]( + solver = METHODS[solver_ivp]( fun=fun, y0=initial, t0=0.0, t_bound=times[-1], rtol=rtol, atol=atol ) def solutions(): for time in times: - while I.t < time: - I.step() - yield I.dense_output()(time) - result = np.vstack(list(solutions())) - assert I.status != "failed" + while solver.t < time: + solver.step() + yield solver.dense_output()(time) + _result = np.vstack(list(solutions())) + assert solver.status != "failed" with timer(f"{solver_ivp} with manual resetting"): - I = METHODS[solver_ivp]( + solver = METHODS[solver_ivp]( fun=fun, y0=initial, t0=0.0, t_bound=times[-1], rtol=rtol, atol=atol ) def solutions(): for time in times: - I.t_bound = time - I.status = "running" - while I.status == "running": - I.step() - yield I.y - result = np.vstack(list(solutions())) - assert I.status != "failed" + solver.t_bound = time + solver.status = "running" + while solver.status == "running": + solver.step() + yield solver.y + _result = np.vstack(list(solutions())) + assert solver.status != "failed" with timer(f"{solver_ivp} with reinitialising"): def solutions(): current_time = 0.0 state = initial for time in times: - I = METHODS[solver_ivp]( + solver = METHODS[solver_ivp]( fun=fun, y0=state, t0=current_time, t_bound=time, rtol=rtol, atol=atol ) - while I.status == "running": - I.step() - assert I.status != "failed" + while solver.status == "running": + solver.step() + assert solver.status != "failed" current_time = time - state = I.y + state = solver.y yield state - result = np.vstack(list(solutions())) + _result = np.vstack(list(solutions())) # Using compiled functions to make things faster def get_compiled_function(f): diff --git a/examples/kuramoto_network.py b/examples/kuramoto_network.py index 78cc485..a4eb259 100755 --- a/examples/kuramoto_network.py +++ b/examples/kuramoto_network.py @@ -24,14 +24,14 @@ def kuramotos_f(): ) yield omega[i] + c/(n-1)*coupling_sum -I = jitcode(kuramotos_f,n=n) -I.set_integrator("dop853",atol=1e-6,rtol=0) +solver = jitcode(kuramotos_f,n=n) +solver.set_integrator("dop853",atol=1e-6,rtol=0) initial_state = uniform(0,2*pi,n) -I.set_initial_value(initial_state,time=0.0) +solver.set_initial_value(initial_state,time=0.0) times = range(0,2001) -data = vstack ([ I.integrate(time) for time in times ]) +data = vstack ([ solver.integrate(time) for time in times ]) data %= 2*pi savetxt("kuramotos.dat",data) diff --git a/jitcode/__init__.py b/jitcode/__init__.py index 92ee5b4..be52710 100644 --- a/jitcode/__init__.py +++ b/jitcode/__init__.py @@ -1,17 +1,9 @@ -from ._jitcode import ( - UnsuccessfulIntegration, - jitcode, - jitcode_lyap, - jitcode_restricted_lyap, - jitcode_transversal_lyap, - t, - test, - y, -) +from ._jitcode import jitcode, jitcode_lyap, jitcode_restricted_lyap, jitcode_transversal_lyap, t, test, y # noqa: F401 +from .integrator_tools import UnsuccessfulIntegration # noqa: F401 try: - from .version import version as __version__ + from .version import version as __version__ # noqa: F401 except ImportError: from warnings import warn warn("Failed to find (autogenerated) version.py. Do not worry about this unless you really need to know the version.") diff --git a/jitcode/_jitcode.py b/jitcode/_jitcode.py index 2d1ca2f..756039e 100644 --- a/jitcode/_jitcode.py +++ b/jitcode/_jitcode.py @@ -1,6 +1,5 @@ #!/usr/bin/python3 -from inspect import signature from itertools import count from types import BuiltinFunctionType, FunctionType from warnings import warn @@ -15,7 +14,7 @@ from jitcxde_common.symbolic import collect_arguments, ordered_subs, replace_function from jitcxde_common.transversal import GroupHandler -from jitcode.integrator_tools import IVP_wrapper, IVP_wrapper_no_interpolation, ODE_wrapper, UnsuccessfulIntegration, empty_integrator, integrator_info +from jitcode.integrator_tools import IVP_wrapper, IVP_wrapper_no_interpolation, ODE_wrapper, empty_integrator, integrator_info #: the symbol for the state that must be used to define the differential equation. It is a function and the integer argument denotes the component. You may just as well define an analogous function directly with SymEngine or SymPy, but using this function is the best way to get the most of future versions of JiTCODE, in particular avoiding incompatibilities. You can import a SymPy variant from the submodule `sympy_symbols` instead (see `SymPy vs. SymEngine`_ for details). @@ -245,7 +244,7 @@ def generate_f_C(self, simplify=None, do_cse=False, chunk_size=100): (set_dy(i,entry) for i,entry in enumerate(f_sym_wc)), name = "f", chunk_size = chunk_size, - arguments = arguments+[("dY", "PyArrayObject *__restrict const")] + arguments = [*arguments, ("dY", "PyArrayObject *__restrict const")] ) self._f_C_source = True @@ -321,7 +320,7 @@ def generate_jac_C(self, do_cse=False, chunk_size=100, sparse=True): ), name = "jac", chunk_size = chunk_size, - arguments = arguments+[("dfdY", "PyArrayObject *__restrict const")] + arguments = [*arguments, ("dfdY", "PyArrayObject *__restrict const")] ) self._jac_C_source = True @@ -355,7 +354,7 @@ def generate_helpers_C(self, chunk_size=100): (set_helper(i, helper[1].subs(self.general_subs)) for i,helper in enumerate(self.helpers)), name = "general_helpers", chunk_size = chunk_size, - arguments = self._default_arguments() + [("general_helper","double *__restrict const")], + arguments = [*self._default_arguments(), ("general_helper","double *__restrict const")], omp = False, ) @@ -844,7 +843,7 @@ def finalise(entry): def f_lyap(): for entry in self.iterate(tangent_vector_f()): - if type(entry)==int: # i.e., if main index + if type(entry) is int: # i.e., if main index if average_dynamics: group = groups[entry] yield sum( finalise(f_list[i]) for i in group )/len(group) diff --git a/tests/scenarios.py b/tests/scenarios.py index 55b7b1d..4d7ea7f 100644 --- a/tests/scenarios.py +++ b/tests/scenarios.py @@ -29,9 +29,9 @@ ]) y1 = np.array([ - 0.0011789485114731, + 0.0011789485114731, -0.0021947158873226, - 0.0195744683782066, + 0.0195744683782066, -0.0057801623466600, ]) diff --git a/tests/test_generation.py b/tests/test_generation.py index c3062b2..8913bba 100644 --- a/tests/test_generation.py +++ b/tests/test_generation.py @@ -9,7 +9,7 @@ from numpy.random import random from numpy.testing import assert_allclose -from jitcode import jitcode, jitcode_lyap, y +from jitcode import jitcode, jitcode_lyap from jitcode._jitcode import _is_C, _is_lambda from scenarios import callback, f_of_y0, jac_of_y0, n, params_args, vanilla, with_dictionary, with_generator, with_helpers, with_params, y0 @@ -55,7 +55,7 @@ def tearDown(self): self.assertIsNotNone(self.ODE.f) self.ODE.set_parameters(*self.init_params) assert_allclose( self.ODE.f(0.0,y0), f_of_y0, rtol=1e-5 ) - if not self.ODE.jac is None: + if self.ODE.jac is not None: assert_allclose( self.ODE.jac(0.0,y0), jac_of_y0, rtol=1e-5) class TestHelpers(TestBasic): diff --git a/tests/test_jitcode.py b/tests/test_jitcode.py index 5fc57ed..c5d8285 100644 --- a/tests/test_jitcode.py +++ b/tests/test_jitcode.py @@ -7,7 +7,7 @@ from scipy.stats import sem as standard_error from symengine import symbols -from jitcode import UnsuccessfulIntegration, jitcode, jitcode_lyap, test, y +from jitcode import jitcode, jitcode_lyap, y from jitcode._jitcode import _is_C, _is_lambda from scenarios import f_of_y0, jac_of_y0, lyaps, n, vanilla, y0, y1 @@ -88,7 +88,7 @@ def tearDown(self): self.ODE.check() self.assertIsNotNone(self.ODE.f) assert_allclose( self.ODE.f(0.0,y0), f_of_y0, rtol=1e-5 ) - if not self.ODE.jac is None: + if self.ODE.jac is not None: assert_allclose( self.ODE.jac(0.0,y0), jac_of_y0, rtol=1e-5) assert_allclose( self.ODE.integrate(1.0), y1, rtol=1e-4 ) for i in reversed(range(n)): @@ -171,7 +171,7 @@ def test_duplicate_error(self): def test_wrong_n(self): with self.assertRaises(ValueError): - ODE = jitcode(**vanilla,n=2*n) + _ODE = jitcode(**vanilla,n=2*n) def test_dimension_mismatch(self): ODE = jitcode(**vanilla) From 83e3585aa563631cf94cf7a94765666d46dcf3ee Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 13 Jan 2025 14:52:25 +0200 Subject: [PATCH 07/10] Fix ruff NPY (numpy) errors --- examples/SW_of_Roesslers.py | 9 +++++---- examples/benchmark.py | 12 ++++++------ examples/double_fhn_restricted_lyap.py | 4 +++- examples/double_fhn_transversal_lyap.py | 1 - examples/kuramoto_network.py | 17 +++++++++-------- jitcode/_jitcode.py | 1 - jitcode/integrator_tools.py | 1 - tests/scenarios.py | 2 -- tests/test_generation.py | 9 +++++---- tests/test_integrator_tools.py | 12 ++++++------ tests/test_jitcode.py | 1 - tests/test_transversal_lyap.py | 10 ++++++---- 12 files changed, 40 insertions(+), 39 deletions(-) diff --git a/examples/SW_of_Roesslers.py b/examples/SW_of_Roesslers.py index 774b94f..648a31f 100644 --- a/examples/SW_of_Roesslers.py +++ b/examples/SW_of_Roesslers.py @@ -46,10 +46,10 @@ def small_world_network(number_of_nodes, nearest_neighbours, rewiring_probabilit # rewiring for i in range(n): for j in range(i): - if A[i,j] and (np.random.random() < rewiring_probability): + if A[i,j] and (rng.random() < rewiring_probability): A[j,i] = A[i,j] = False while True: - i_new,j_new = np.random.randint(0,n,2) + i_new,j_new = rng.integers(0,n,2) if A[i_new,j_new] or i_new==j_new: continue else: @@ -67,8 +67,9 @@ def small_world_network(number_of_nodes, nearest_neighbours, rewiring_probabilit # parameters # ---------- + rng = np.random.default_rng() N = 500 - ω = np.random.uniform(0.8,1.0,N) + ω = rng.uniform(0.8,1.0,N) a = 0.165 b = 0.2 c = 10.0 @@ -99,7 +100,7 @@ def f(): # integrate # --------- - initial_state = np.random.random(3*N) + initial_state = rng.random(3*N) ODE = jitcode(f, helpers=helpers, n=3*N) ODE.generate_f_C(simplify=False, do_cse=False, chunk_size=150) diff --git a/examples/benchmark.py b/examples/benchmark.py index 8fa8c87..ac29330 100644 --- a/examples/benchmark.py +++ b/examples/benchmark.py @@ -1,7 +1,6 @@ from time import process_time import numpy as np -from numpy.random import choice, uniform from scipy.integrate import ode, odeint, solve_ivp from scipy.integrate._ivp.ivp import METHODS from symengine import sin @@ -145,9 +144,12 @@ def get_compiled_function(f): ) +rng = np.random.default_rng() + n, c, q = 100, 3.0, 0.2 -A = choice( [1,0], size=(n,n), p=[q,1-q] ) -omega = uniform(-0.5,0.5,n) +A = rng.choice( [1,0], size=(n,n), p=[q,1-q] ) +omega = rng.uniform(-0.5,0.5,n) + def kuramotos_f(): for i in range(n): coupling_sum = sum( @@ -160,10 +162,8 @@ def kuramotos_f(): test_scenario( name = "random network of Kuramoto oscillators", fun = get_compiled_function(kuramotos_f), - initial = uniform(0,2*np.pi,n), + initial = rng.uniform(0,2*np.pi,n), times = range(1,10001,10), rtol = 1e-13, atol = 1e-6, ) - - diff --git a/examples/double_fhn_restricted_lyap.py b/examples/double_fhn_restricted_lyap.py index ba82846..6f88efc 100644 --- a/examples/double_fhn_restricted_lyap.py +++ b/examples/double_fhn_restricted_lyap.py @@ -6,6 +6,8 @@ from jitcode import jitcode_restricted_lyap, y +rng = np.random.default_rng() + a = -0.025794 b1 = 0.01 b2 = 0.01 @@ -19,7 +21,7 @@ b2*y(2) - c*y(3) ] -initial_state = np.random.random(4) +initial_state = rng.random(4) vectors = [ np.array([1.,0.,1.,0.]), diff --git a/examples/double_fhn_transversal_lyap.py b/examples/double_fhn_transversal_lyap.py index e03942b..dedeb43 100644 --- a/examples/double_fhn_transversal_lyap.py +++ b/examples/double_fhn_transversal_lyap.py @@ -46,4 +46,3 @@ lyap = np.average(lyaps[1000:]) stderr = sem(lyaps[1000:]) # Note that this only an estimate print(f"transversal Lyapunov exponent: {lyap:.4f} ± {stderr:.4f}") - diff --git a/examples/kuramoto_network.py b/examples/kuramoto_network.py index a4eb259..8a9e194 100755 --- a/examples/kuramoto_network.py +++ b/examples/kuramoto_network.py @@ -1,17 +1,18 @@ -from numpy import pi, savetxt, vstack -from numpy.random import choice, uniform +import numpy as np from symengine import sin from jitcode import jitcode, y +rng = np.random.default_rng() + n = 100 c = 3.0 q = 0.2 -A = choice( [1,0], size=(n,n), p=[q,1-q] ) +A = rng.choice( [1,0], size=(n,n), p=[q,1-q] ) -omega = uniform(-0.5,0.5,n) +omega = rng.uniform(-0.5,0.5,n) # Sorting does not effect the qualitative dynamics, but only the resulting plots: omega.sort() @@ -27,11 +28,11 @@ def kuramotos_f(): solver = jitcode(kuramotos_f,n=n) solver.set_integrator("dop853",atol=1e-6,rtol=0) -initial_state = uniform(0,2*pi,n) +initial_state = rng.uniform(0,2*np.pi,n) solver.set_initial_value(initial_state,time=0.0) times = range(0,2001) -data = vstack ([ solver.integrate(time) for time in times ]) -data %= 2*pi -savetxt("kuramotos.dat",data) +data = np.vstack ([ solver.integrate(time) for time in times ]) +data %= 2*np.pi +np.savetxt("kuramotos.dat",data) diff --git a/jitcode/_jitcode.py b/jitcode/_jitcode.py index 756039e..d604002 100644 --- a/jitcode/_jitcode.py +++ b/jitcode/_jitcode.py @@ -964,4 +964,3 @@ def test(omp=True,sympy=True): ODE.set_integrator("dopri5") ODE.set_initial_value([1,2]) ODE.integrate(0.1) - diff --git a/jitcode/integrator_tools.py b/jitcode/integrator_tools.py index 25f598c..98c7eb7 100644 --- a/jitcode/integrator_tools.py +++ b/jitcode/integrator_tools.py @@ -182,4 +182,3 @@ def integrate(self,*args,**kwargs): def successful(self): raise RuntimeError("You must call set_integrator first.") - diff --git a/tests/scenarios.py b/tests/scenarios.py index 4d7ea7f..7744079 100644 --- a/tests/scenarios.py +++ b/tests/scenarios.py @@ -133,5 +133,3 @@ def first_component(Y): (call_c_times,c_times,1) ], } - - diff --git a/tests/test_generation.py b/tests/test_generation.py index 8913bba..57e25f6 100644 --- a/tests/test_generation.py +++ b/tests/test_generation.py @@ -6,7 +6,7 @@ import unittest -from numpy.random import random +import numpy as np from numpy.testing import assert_allclose from jitcode import jitcode, jitcode_lyap @@ -64,7 +64,8 @@ def setUp(self): class FurtherHelpersTests(unittest.TestCase): def test_identity_of_jacs(self): - x = random(n) + rng = np.random.default_rng() + x = rng.random(n) def evaluate(scenario): ODE = jitcode(**scenario) @@ -82,7 +83,8 @@ def evaluate(scenario): ) def test_identity_of_lyaps(self): - x = random((n+1)*n) + rng = np.random.default_rng() + x = rng.random((n+1)*n) def evaluate(scenario): ODE = jitcode_lyap(**scenario,n_lyap=n) @@ -125,4 +127,3 @@ def test_lambdas(self): if __name__ == "__main__": unittest.main(buffer=True) - diff --git a/tests/test_integrator_tools.py b/tests/test_integrator_tools.py index 65d50e8..5055c83 100644 --- a/tests/test_integrator_tools.py +++ b/tests/test_integrator_tools.py @@ -3,7 +3,6 @@ import unittest import numpy as np -from numpy.random import random from numpy.testing import assert_allclose from jitcode import jitcode @@ -11,6 +10,8 @@ from scenarios import n, vanilla, y0, y1 +rng = np.random.default_rng() + # Generating compiled functions ODE = jitcode(**vanilla,verbose=False,wants_jacobian=True) @@ -45,13 +46,13 @@ def test_no_params(self): def test_initial_twice(self): self.initialise(f,jac,rtol=1e-5) - self.integrator.set_initial_value(random(n)) + self.integrator.set_initial_value(rng.random(n)) self.integrator.set_initial_value(y0) self.control_result() def test_zero_integration(self): self.initialise(f,jac) - initial = random(n) + initial = rng.random(n) self.integrator.set_initial_value(initial) assert_allclose(initial,self.integrator.integrate(0)) @@ -142,7 +143,7 @@ def setUp(self): def test_t(self): with self.assertRaises(RuntimeError): - self.integrator.t + _ = self.integrator.t def test_set_integrator(self): with self.assertRaises(RuntimeError): @@ -153,11 +154,10 @@ def test_integrate(self): self.integrator.integrate(2.3) def test_set_initial(self): - initial = random(5) + initial = rng.random(5) self.integrator.set_initial_value(initial,1.2) assert np.all( self.integrator._y == initial ) assert self.integrator.t == 1.2 if __name__ == "__main__": unittest.main(buffer=True) - diff --git a/tests/test_jitcode.py b/tests/test_jitcode.py index c5d8285..754ed30 100644 --- a/tests/test_jitcode.py +++ b/tests/test_jitcode.py @@ -206,4 +206,3 @@ def test_no_interpolation_LSODA(self): if __name__ == "__main__": unittest.main(buffer=True) - diff --git a/tests/test_transversal_lyap.py b/tests/test_transversal_lyap.py index 750fced..baa6bfa 100644 --- a/tests/test_transversal_lyap.py +++ b/tests/test_transversal_lyap.py @@ -15,6 +15,8 @@ from jitcode import jitcode_restricted_lyap, jitcode_transversal_lyap, y +rng = np.random.default_rng() + a = -0.025794 b = 0.01 c = 0.02 @@ -83,7 +85,7 @@ ) # Simplification or compiler optimisation would lead to trajectories diverging from the synchronisation manifold due to numerical noise. ODE1.generate_f_C(simplify=False) - ODE1.compile_C( extra_compile_args = DEFAULT_COMPILE_ARGS + ["-O2"] ) + ODE1.compile_C( extra_compile_args = [*DEFAULT_COMPILE_ARGS, "-O2"] ) ODE1.set_integrator("dopri5") ODE2 = jitcode_transversal_lyap( @@ -100,16 +102,16 @@ ODE2.set_parameters(coupling["k"]) if coupling["sign"]<0: - initial_state = np.random.random(n) + initial_state = rng.random(n) else: - single = np.random.random(2) + single = rng.random(2) initial_state = np.empty(n) for j,group in enumerate(scenario["groups"]): for i in group: initial_state[i] = single[j] ODE1.set_initial_value(initial_state,0.0) - ODE2.set_initial_value(np.random.random(2),0.0) + ODE2.set_initial_value(rng.random(2),0.0) times = range(100,100000,100) lyaps1 = np.hstack([ODE1.integrate(time)[1] for time in times]) From 67e66da62ab2b4a9fce16b313d625f4dac154f32 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 13 Jan 2025 14:54:20 +0200 Subject: [PATCH 08/10] Fix ruff B (bugbear) errors --- jitcode/__init__.py | 2 +- jitcode/_jitcode.py | 18 +++++++++--------- tests/test_order.py | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jitcode/__init__.py b/jitcode/__init__.py index be52710..cab0ab8 100644 --- a/jitcode/__init__.py +++ b/jitcode/__init__.py @@ -6,4 +6,4 @@ from .version import version as __version__ # noqa: F401 except ImportError: from warnings import warn - warn("Failed to find (autogenerated) version.py. Do not worry about this unless you really need to know the version.") + warn("Failed to find (autogenerated) version.py. Do not worry about this unless you really need to know the version.", stacklevel=1) diff --git a/jitcode/_jitcode.py b/jitcode/_jitcode.py index d604002..4f72085 100644 --- a/jitcode/_jitcode.py +++ b/jitcode/_jitcode.py @@ -423,7 +423,7 @@ def _prepare_lambdas(self): if not hasattr(self,"_lambda_subs") or not hasattr(self,"_lambda_args"): if self.helpers: - warn("Lambdification handles helpers by plugging them in. This may be very inefficient") + warn("Lambdification handles helpers by plugging them in. This may be very inefficient", stacklevel=3) self._lambda_subs = list(reversed(self.helpers)) self._lambda_args = [t] @@ -656,11 +656,11 @@ def set_parameters(self,*args): self.initialise() def set_f_params(self, *args): - warn("This function has been replaced by `set_parameters`") + warn("This function has been replaced by `set_parameters`", stacklevel=2) self.set_parameters(*args) def set_jac_params(self, *args): - warn("This function has been replaced by `set_parameters`") + warn("This function has been replaced by `set_parameters`", stacklevel=2) self.set_parameters(*args) def integrate(self,*args,**kwargs): @@ -688,7 +688,7 @@ def __init__( self, f_sym=(), n_lyap=-1, simplify=None, **kwargs ): f_basic = self._handle_input(f_sym,n_basic=True) self._n_lyap = n_lyap if (0<=n_lyap<=self.n_basic) else self.n_basic if self._n_lyap>10: - warn(f"You are about to calculate {self._n_lyap} Lyapunov exponents. This is very likely more than you need and may lead to severe difficulties with compilation and integration. Unless you really know what you are doing, consider how many Lyapunov exponents you actually need and set the parameter `n_lyap` accordingly.") + warn(f"You are about to calculate {self._n_lyap} Lyapunov exponents. This is very likely more than you need and may lead to severe difficulties with compilation and integration. Unless you really know what you are doing, consider how many Lyapunov exponents you actually need and set the parameter `n_lyap` accordingly.", stacklevel=2) if simplify is None: simplify = self.n_basic<=10 @@ -738,7 +738,7 @@ def set_integrator(self,name,interpolate=None,**kwargs): if interpolate is None: interpolate = name in ["RK45","Radau"] if name == "LSODA": - warn("Using LSODA for Lyapunov exponents is discouraged since interpolation errors may accumulate.") + warn("Using LSODA for Lyapunov exponents is discouraged since interpolation errors may accumulate.", stacklevel=2) super().set_integrator(name,interpolate,**kwargs) def norms(self): @@ -746,7 +746,7 @@ def norms(self): tangent_vectors = self._y[n:].reshape(self._n_lyap,n) tangent_vectors,norms = orthonormalise_qr(tangent_vectors) if not np.all(np.isfinite(norms)): - warn("Norms of perturbation vectors for Lyapunov exponents out of numerical bounds. You probably waited too long before renormalising and should call integrate with smaller intervals between steps (as renormalisations happen once with every call of integrate).") + warn("Norms of perturbation vectors for Lyapunov exponents out of numerical bounds. You probably waited too long before renormalising and should call integrate with smaller intervals between steps (as renormalisations happen once with every call of integrate).", stacklevel=2) return norms, tangent_vectors def integrate(self, *args, **kwargs): @@ -881,7 +881,7 @@ def set_integrator(self,name,interpolate=False,**kwargs): if interpolate is None: interpolate = name in ["RK45","Radau"] if name == "LSODA": - warn("Using LSODA for Lyapunov exponents is discouraged since interpolation errors may accumulate.") + warn("Using LSODA for Lyapunov exponents is discouraged since interpolation errors may accumulate.", stacklevel=2) super().set_integrator(name,interpolate,**kwargs) def norm(self): @@ -889,7 +889,7 @@ def norm(self): norm = np.linalg.norm(tangent_vector) tangent_vector /= norm if not np.isfinite(norm): - warn("Norm of perturbation vector for Lyapunov exponent out of numerical bounds. You probably waited too long before renormalising and should call integrate with smaller intervals between steps (as renormalisations happen once with every call of integrate).") + warn("Norm of perturbation vector for Lyapunov exponent out of numerical bounds. You probably waited too long before renormalising and should call integrate with smaller intervals between steps (as renormalisations happen once with every call of integrate).", stacklevel=2) self._y[self.tangent_indices] = tangent_vector return norm @@ -943,7 +943,7 @@ def norms(self): norm = np.linalg.norm(tangent_vector) tangent_vector /= norm if not np.isfinite(norm): - warn("Norm of perturbation vector for Lyapunov exponent out of numerical bounds. You probably waited too long before renormalising and should call integrate with smaller intervals between steps (as renormalisations happen once with every call of integrate).") + warn("Norm of perturbation vector for Lyapunov exponent out of numerical bounds. You probably waited too long before renormalising and should call integrate with smaller intervals between steps (as renormalisations happen once with every call of integrate).", stacklevel=2) return norm, tangent_vector def test(omp=True,sympy=True): diff --git a/tests/test_order.py b/tests/test_order.py index 3384539..cd24206 100644 --- a/tests/test_order.py +++ b/tests/test_order.py @@ -10,7 +10,7 @@ f = [1/p] for integrator in ["dopri5","RK45"]: - def set_integrator(ODE): + def set_integrator(ODE, integrator=integrator): ODE.set_integrator(integrator) def set_parameters(ODE): @@ -37,7 +37,7 @@ def set_initial_value(ODE): f_2 = [1] for integrator in ["dopri5","RK45"]: - def set_integrator(ODE): + def set_integrator(ODE, integrator=integrator): ODE.set_integrator(integrator) def set_initial_value(ODE): From 1bd2bc98a13d483c9ee679164fe13721bf844248 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 13 Jan 2025 14:56:45 +0200 Subject: [PATCH 09/10] Fix some typos --- examples/SW_of_Roesslers.py | 2 +- jitcode/_jitcode.py | 2 +- jitcode/integrator_tools.py | 2 +- tests/test_generation.py | 2 +- tests/test_integrator_tools.py | 2 +- tests/test_jitcode.py | 2 +- tests/test_transversal_lyap.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/SW_of_Roesslers.py b/examples/SW_of_Roesslers.py index 648a31f..1401ffe 100644 --- a/examples/SW_of_Roesslers.py +++ b/examples/SW_of_Roesslers.py @@ -12,7 +12,7 @@ \\dot{z}_i &= b + z_i (x_i -c) + k \\sum_{j=0}^N (z_j-z_i) \\end{alignedat} -The control parameters shall be :math:`a = 0.165`, :math:`b = 0.2`, :math:`c = 10.0`, and :math:`k = 0.01`. The (frequency) parameter :math:`ω_i` shall be picked randomly from the uniform distribution on :math:`[0.8,1.0]` for each :math:`i`. :math:`A∈ℝ^{N×N}` shall be the adjacency matrix of a one-dimensional small-world network (which shall be provided by a function `small_world_network` in the following example code). So, the :math:`x` compenents are coupled diffusively with a small-world coupling topology, while the :math:`z` components are coupled diffusively to their mean field, with the coupling term being modulated with :math:`\\sin(t)`. +The control parameters shall be :math:`a = 0.165`, :math:`b = 0.2`, :math:`c = 10.0`, and :math:`k = 0.01`. The (frequency) parameter :math:`ω_i` shall be picked randomly from the uniform distribution on :math:`[0.8,1.0]` for each :math:`i`. :math:`A∈ℝ^{N×N}` shall be the adjacency matrix of a one-dimensional small-world network (which shall be provided by a function `small_world_network` in the following example code). So, the :math:`x` components are coupled diffusively with a small-world coupling topology, while the :math:`z` components are coupled diffusively to their mean field, with the coupling term being modulated with :math:`\\sin(t)`. Without further ado, here is the example code (`complete running example `_); highlighted lines will be commented upon below: diff --git a/jitcode/_jitcode.py b/jitcode/_jitcode.py index 4f72085..f4b85fa 100644 --- a/jitcode/_jitcode.py +++ b/jitcode/_jitcode.py @@ -71,7 +71,7 @@ class jitcode(jitcxde): * A SymEngine function object used in `f_sym` to represent the function call. If you want to use any JiTCODE features that need the derivative, this must have a properly defined `f_diff` method with the derivative being another callback function (or constant). * The Python function to be called. This function will receive the state array (`y`) as the first argument. All further arguments are whatever you use as arguments of the SymEngine function in `f_sym`. These can be any expression that you might use in the definition of the derivative and contain, e.g., dynamical variables, time, control parameters, and helpers. The only restriction is that the arguments are floats (and not vectors or similar). The return value must also be a float (or something castable to float). It is your responsibility to ensure that this function adheres to these criteria, is deterministic and sufficiently smooth with respect its arguments; expect nasty errors otherwise. - * The number of arguments, **excluding** the state array as mandatory first argument. This means if you have a variadic Python function, you cannot just call it with different numbers of arguments in `f_sym`, but you have to define separate callbacks for each of numer of arguments. + * The number of arguments, **excluding** the state array as mandatory first argument. This means if you have a variadic Python function, you cannot just call it with different numbers of arguments in `f_sym`, but you have to define separate callbacks for each of number of arguments. See `this example `_ (for JiTCDDE) for how to use this. diff --git a/jitcode/integrator_tools.py b/jitcode/integrator_tools.py index 98c7eb7..45ac317 100644 --- a/jitcode/integrator_tools.py +++ b/jitcode/integrator_tools.py @@ -152,7 +152,7 @@ def set_params(self,*args): class empty_integrator: """ - This is a dummy class that mimicks some basic properties of scipy.integrate.ode or the above wrappers, respectively. It exists to store states and parameters and to raise exceptions in the same interface. + This is a dummy class that mimics some basic properties of scipy.integrate.ode or the above wrappers, respectively. It exists to store states and parameters and to raise exceptions in the same interface. """ def __init__(self): diff --git a/tests/test_generation.py b/tests/test_generation.py index 57e25f6..8df0529 100644 --- a/tests/test_generation.py +++ b/tests/test_generation.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 """ -Tests that the code-generation and compilaton or lambdification, respectively, of the derivative and Jacobian works as intended in all kinds of scenarios. +Tests that the code-generation and compilation or lambdification, respectively, of the derivative and Jacobian works as intended in all kinds of scenarios. """ import unittest diff --git a/tests/test_integrator_tools.py b/tests/test_integrator_tools.py index 5055c83..0fcecb2 100644 --- a/tests/test_integrator_tools.py +++ b/tests/test_integrator_tools.py @@ -32,7 +32,7 @@ class TestSkeleton: """ - This class exists to be inherited by a test that adds self.initialise to intialise self.integrator. + This class exists to be inherited by a test that adds self.initialise to initialise self.integrator. """ def control_result(self): diff --git a/tests/test_jitcode.py b/tests/test_jitcode.py index 754ed30..8f5f622 100644 --- a/tests/test_jitcode.py +++ b/tests/test_jitcode.py @@ -36,7 +36,7 @@ def test_initialise_first(self): self.ODE.set_integrator("dop853") self.assertTrue(_is_C(self.ODE.f)) - def test_initalise_with_dict(self): + def test_initialise_with_dict(self): self.ODE = jitcode(**vanilla) initial_value = {y(i):y0[i] for i in range(n)} self.ODE.set_initial_value(initial_value) diff --git a/tests/test_transversal_lyap.py b/tests/test_transversal_lyap.py index baa6bfa..d2e5554 100644 --- a/tests/test_transversal_lyap.py +++ b/tests/test_transversal_lyap.py @@ -118,7 +118,7 @@ lyaps2 = np.hstack([ODE2.integrate(time)[1] for time in times]) # Check that we are still on the synchronisation manifold: - message = f"The dynamics left the synchronisation manifold when {scenario['name']} with coupling {coupling}. If this fails, this is a problem with the test and not with what is tested or any software involved.\n\nSpecifically, this test only works when the backend (Symengine plus compiler) implents certain computations completely symmetrically. This needs not and cannot be reasonably controlled (and no, turning off compiler optimisation doesn’t necessarily help as it often restores symmetries broken by Symengine). It’s only something exploited by this test to make it work in the first place." + message = f"The dynamics left the synchronisation manifold when {scenario['name']} with coupling {coupling}. If this fails, this is a problem with the test and not with what is tested or any software involved.\n\nSpecifically, this test only works when the backend (Symengine plus compiler) implements certain computations completely symmetrically. This needs not and cannot be reasonably controlled (and no, turning off compiler optimisation doesn’t necessarily help as it often restores symmetries broken by Symengine). It’s only something exploited by this test to make it work in the first place." for group in scenario["groups"]: for i,j in combinations(group,2): assert ODE1.y[i]==ODE1.y[j], message From cb3572e52ae17a6a28bfb2811b263435ad30d8e1 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 15 Jan 2025 14:23:20 +0200 Subject: [PATCH 10/10] Use idiomatic numpy imports --- jitcode/_jitcode.py | 7 +++---- jitcode/integrator_tools.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/jitcode/_jitcode.py b/jitcode/_jitcode.py index f4b85fa..d8efc81 100644 --- a/jitcode/_jitcode.py +++ b/jitcode/_jitcode.py @@ -6,7 +6,6 @@ import numpy as np import symengine -from numpy import hstack, log from jitcxde_common import checker, jitcxde from jitcxde_common.helpers import find_dependent_helpers, sort_helpers, sympify_helpers @@ -729,7 +728,7 @@ def set_initial_value(self, y, time=0.0): for _ in range(self._n_lyap): new_y.append(random_direction(self.n_basic)) - super().set_initial_value(hstack(new_y), time) + super().set_initial_value(np.hstack(new_y), time) def set_integrator(self,name,interpolate=None,**kwargs): """ @@ -769,7 +768,7 @@ def integrate(self, *args, **kwargs): super().integrate(*args, **kwargs) delta_t = self.t-old_t norms, tangent_vectors = self.norms() - lyaps = log(norms) / delta_t + lyaps = np.log(norms) / delta_t self._y[self.n_basic:] = tangent_vectors.flatten() super().set_initial_value(self._y, self.t) @@ -910,7 +909,7 @@ def integrate(self, *args, **kwargs): super().integrate(*args, **kwargs) delta_t = self.t-old_t norm = self.norm() - lyap = log(norm) / delta_t + lyap = np.log(norm) / delta_t super().set_initial_value(self._y, self.t) return self._y[self.main_indices], lyap diff --git a/jitcode/integrator_tools.py b/jitcode/integrator_tools.py index 45ac317..7b2fa57 100644 --- a/jitcode/integrator_tools.py +++ b/jitcode/integrator_tools.py @@ -1,6 +1,6 @@ from inspect import signature -from numpy import inf +import numpy as np from scipy.integrate import ode from scipy.integrate._ivp.ivp import METHODS as ivp_methods from scipy.integrate._ode import find_integrator @@ -57,7 +57,7 @@ def __init__( # Dictionary to be passed as arguments to the integrator and store stuff self.kwargs = { - "t_bound": inf, + "t_bound": np.inf, "vectorized": False, "fun": self.f }