gotranx 2.1.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gotranx-2.1.0/src/gotranx.egg-info → gotranx-2.2.0}/PKG-INFO +2 -1
- {gotranx-2.1.0 → gotranx-2.2.0}/pyproject.toml +3 -2
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/codegen/ufl.py +22 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/ode.py +4 -1
- {gotranx-2.1.0 → gotranx-2.2.0/src/gotranx.egg-info}/PKG-INFO +2 -1
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx.egg-info/SOURCES.txt +1 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx.egg-info/requires.txt +1 -0
- gotranx-2.2.0/tests/test_determinism.py +89 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_ufl_codegen.py +78 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/LICENSE +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/README.md +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/setup.cfg +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/__init__.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/__main__.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/_enum.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/atoms.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/cli/__init__.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/cli/cellml2ode.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/cli/gotran2c.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/cli/gotran2julia.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/cli/gotran2md.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/cli/gotran2mtk.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/cli/gotran2py.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/cli/gotran2ufl.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/cli/ode2cellml.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/cli/utils.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/codegen/__init__.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/codegen/base.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/codegen/c.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/codegen/jax.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/codegen/julia.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/codegen/markdown.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/codegen/mtk.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/codegen/ode.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/codegen/python.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/exceptions.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/expressions.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/linearization.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/load.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/myokit.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/ode.lark +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/ode_component.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/parser.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/py.typed +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/save.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/schemes.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/singularities.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/sympytools.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/templates/__init__.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/templates/c.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/templates/jax.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/templates/julia.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/templates/markdown.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/templates/mtk.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/templates/python.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/templates/ufl.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/transformer.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx/units.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx.egg-info/dependency_links.txt +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx.egg-info/entry_points.txt +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx.egg-info/not-zip-safe +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/src/gotranx.egg-info/top_level.txt +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_atoms.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_benchmarks.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_c_codegen.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_cli.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_comments.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_conditionals.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_deep_linearization_models.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_demos.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_expressions.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_julia_codegen.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_linearization.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_load.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_markdown_codegen.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_mtk_codegen.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_myokit.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_ode.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_ode_component.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_operations.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_python_codegen.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_save.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_schemes.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_singularities.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_singularity_models.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_subodes.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_sympytools.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_transformer_assignments.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_transformer_parameters.py +0 -0
- {gotranx-2.1.0 → gotranx-2.2.0}/tests/test_transformer_states.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gotranx
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: A declarative language describing ordinary differential equations
|
|
5
5
|
Author-email: Henrik Finsberg <henriknf@simula.no>
|
|
6
6
|
License: MIT
|
|
@@ -47,6 +47,7 @@ Requires-Dist: pytest; extra == "test"
|
|
|
47
47
|
Requires-Dist: pytest-cov; extra == "test"
|
|
48
48
|
Requires-Dist: jax; extra == "test"
|
|
49
49
|
Requires-Dist: cmake; extra == "test"
|
|
50
|
+
Requires-Dist: fenics-ufl; extra == "test"
|
|
50
51
|
Requires-Dist: gotranx[formatters]; extra == "test"
|
|
51
52
|
Provides-Extra: benchmark
|
|
52
53
|
Requires-Dist: pytest-benchmark>=4.0.0; extra == "benchmark"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gotranx"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.2.0"
|
|
8
8
|
description = "A declarative language describing ordinary differential equations"
|
|
9
9
|
authors = [{name = "Henrik Finsberg", email = "henriknf@simula.no"}]
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -62,6 +62,7 @@ test = [
|
|
|
62
62
|
"pytest-cov",
|
|
63
63
|
"jax",
|
|
64
64
|
"cmake",
|
|
65
|
+
"fenics-ufl",
|
|
65
66
|
"gotranx[formatters]",
|
|
66
67
|
]
|
|
67
68
|
benchmark = [
|
|
@@ -171,7 +172,7 @@ tag = true
|
|
|
171
172
|
sign_tags = false
|
|
172
173
|
tag_name = "v{new_version}"
|
|
173
174
|
tag_message = "Bump version: {current_version} → {new_version}"
|
|
174
|
-
current_version = "2.
|
|
175
|
+
current_version = "2.2.0"
|
|
175
176
|
|
|
176
177
|
|
|
177
178
|
[[tool.bumpversion.files]]
|
|
@@ -74,6 +74,28 @@ class UFLPrinter(GotranPythonCodePrinter):
|
|
|
74
74
|
|
|
75
75
|
return "".join(result)
|
|
76
76
|
|
|
77
|
+
# Comparisons are printed as calls to the ufl functions, not as Python
|
|
78
|
+
# operators. With two numbers as operands (say a parameter passed as a
|
|
79
|
+
# float) an operator gives a Python bool, which ufl.conditional, ufl.And
|
|
80
|
+
# and ufl.Or refuse; the functions convert both operands with ufl.as_ufl.
|
|
81
|
+
# `==` and `!=` never give a condition: UFL keeps them for structural
|
|
82
|
+
# equality of expressions.
|
|
83
|
+
_comparisons = {
|
|
84
|
+
"==": "ufl.eq",
|
|
85
|
+
"!=": "ufl.ne",
|
|
86
|
+
"<": "ufl.lt",
|
|
87
|
+
"<=": "ufl.le",
|
|
88
|
+
">": "ufl.gt",
|
|
89
|
+
">=": "ufl.ge",
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
def _print_Relational(self, expr):
|
|
93
|
+
func = self._comparisons[expr.rel_op]
|
|
94
|
+
return f"{func}({self._print(expr.lhs)}, {self._print(expr.rhs)})"
|
|
95
|
+
|
|
96
|
+
# The Python printer's own _print_Equality would otherwise take precedence
|
|
97
|
+
_print_Equality = _print_Relational
|
|
98
|
+
|
|
77
99
|
def _print_And(self, expr):
|
|
78
100
|
args = [self._print(arg) for arg in expr.args]
|
|
79
101
|
return functools.reduce(lambda x, y: f"ufl.And({x}, {y})", args)
|
|
@@ -280,7 +280,10 @@ def sort_assignments(
|
|
|
280
280
|
"Try to save the ODE to an .ode file first and load it again"
|
|
281
281
|
)
|
|
282
282
|
raise exceptions.GotranxError(msg)
|
|
283
|
-
|
|
283
|
+
# sorted(): dependencies is a frozenset[str], and graphlib breaks ties in
|
|
284
|
+
# insertion order, so an unsorted splat leaks PYTHONHASHSEED into the
|
|
285
|
+
# emitted order of states, monitors and intermediates.
|
|
286
|
+
sorter.add(assignment.name, *sorted(assignment.value.dependencies))
|
|
284
287
|
|
|
285
288
|
static_order = tuple(sorter.static_order())
|
|
286
289
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gotranx
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: A declarative language describing ordinary differential equations
|
|
5
5
|
Author-email: Henrik Finsberg <henriknf@simula.no>
|
|
6
6
|
License: MIT
|
|
@@ -47,6 +47,7 @@ Requires-Dist: pytest; extra == "test"
|
|
|
47
47
|
Requires-Dist: pytest-cov; extra == "test"
|
|
48
48
|
Requires-Dist: jax; extra == "test"
|
|
49
49
|
Requires-Dist: cmake; extra == "test"
|
|
50
|
+
Requires-Dist: fenics-ufl; extra == "test"
|
|
50
51
|
Requires-Dist: gotranx[formatters]; extra == "test"
|
|
51
52
|
Provides-Extra: benchmark
|
|
52
53
|
Requires-Dist: pytest-benchmark>=4.0.0; extra == "benchmark"
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Generated output must not depend on Python's per-process hash seed.
|
|
2
|
+
|
|
3
|
+
``sort_assignments`` feeds ``graphlib.TopologicalSorter``, which breaks ties in
|
|
4
|
+
insertion order. Any set of strings iterated on the way in therefore leaks
|
|
5
|
+
``PYTHONHASHSEED`` into the emitted order of states, monitors and intermediates,
|
|
6
|
+
and from there into the generated code.
|
|
7
|
+
|
|
8
|
+
The seed must be set before the interpreter starts, so every check here shells
|
|
9
|
+
out to a fresh interpreter rather than running in-process.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
import pytest
|
|
21
|
+
|
|
22
|
+
here = Path(__file__).parent
|
|
23
|
+
|
|
24
|
+
# Models with enough independent branches for a tie-break to be observable.
|
|
25
|
+
# Small models have no ties to break, so they cannot expose this. These two produce a
|
|
26
|
+
# distinct order on every seed tried.
|
|
27
|
+
MODELS = ["ToRORd_dyn_chloride.ode", "ORdmm_Land.ode"]
|
|
28
|
+
SEEDS = ("1", "2", "7")
|
|
29
|
+
|
|
30
|
+
_ORDER_SCRIPT = """
|
|
31
|
+
import json, sys
|
|
32
|
+
import gotranx
|
|
33
|
+
|
|
34
|
+
ode = gotranx.load_ode(sys.argv[1])
|
|
35
|
+
# Marker-delimited: gotranx logs to stdout, so the payload cannot be the whole stream.
|
|
36
|
+
print("<<<ORDER>>>" + json.dumps({
|
|
37
|
+
"assignments": [a.name for a in ode.sorted_assignments()],
|
|
38
|
+
"states": [s.name for s in ode.sorted_states()],
|
|
39
|
+
}))
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
_CODEGEN_SCRIPT = """
|
|
43
|
+
import sys
|
|
44
|
+
import gotranx
|
|
45
|
+
|
|
46
|
+
ode = gotranx.load_ode(sys.argv[1])
|
|
47
|
+
from gotranx.codegen import JuliaCodeGenerator
|
|
48
|
+
codegen = JuliaCodeGenerator(ode)
|
|
49
|
+
sys.stdout.write("<<<CODE>>>" + codegen.rhs() + codegen.monitor_index() + codegen.state_index())
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _run(script: str, odefile: Path, seed: str, marker: str) -> str:
|
|
54
|
+
env = {**os.environ, "PYTHONHASHSEED": seed}
|
|
55
|
+
out = subprocess.run(
|
|
56
|
+
[sys.executable, "-c", script, str(odefile)],
|
|
57
|
+
env=env,
|
|
58
|
+
capture_output=True,
|
|
59
|
+
text=True,
|
|
60
|
+
check=True,
|
|
61
|
+
)
|
|
62
|
+
assert marker in out.stdout, f"marker missing; stderr was:\n{out.stderr}"
|
|
63
|
+
return out.stdout.split(marker, 1)[1]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.mark.parametrize("model", MODELS)
|
|
67
|
+
def test_assignment_and_state_order_are_independent_of_hash_seed(model):
|
|
68
|
+
odefile = here / "odefiles" / model
|
|
69
|
+
orders = [json.loads(_run(_ORDER_SCRIPT, odefile, s, "<<<ORDER>>>")) for s in SEEDS]
|
|
70
|
+
|
|
71
|
+
for seed, other in zip(SEEDS[1:], orders[1:]):
|
|
72
|
+
assert other["assignments"] == orders[0]["assignments"], (
|
|
73
|
+
f"assignment order under PYTHONHASHSEED={seed} differs from {SEEDS[0]}"
|
|
74
|
+
)
|
|
75
|
+
assert other["states"] == orders[0]["states"], (
|
|
76
|
+
f"state order under PYTHONHASHSEED={seed} differs from {SEEDS[0]}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@pytest.mark.parametrize("model", MODELS)
|
|
81
|
+
def test_generated_code_is_independent_of_hash_seed(model):
|
|
82
|
+
"""The end-to-end property users actually depend on."""
|
|
83
|
+
odefile = here / "odefiles" / model
|
|
84
|
+
code = [_run(_CODEGEN_SCRIPT, odefile, s, "<<<CODE>>>") for s in SEEDS]
|
|
85
|
+
|
|
86
|
+
for seed, other in zip(SEEDS[1:], code[1:]):
|
|
87
|
+
assert other == code[0], (
|
|
88
|
+
f"generated rhs under PYTHONHASHSEED={seed} differs from {SEEDS[0]}"
|
|
89
|
+
)
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import math
|
|
1
2
|
import pytest
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
import gotranx
|
|
5
|
+
from gotranx.cli import gotran2py, gotran2ufl
|
|
4
6
|
from gotranx.codegen.ufl import UFLCodeGenerator
|
|
7
|
+
from gotranx.schemes import Scheme
|
|
5
8
|
|
|
6
9
|
here = Path(__file__).parent.absolute()
|
|
7
10
|
|
|
@@ -117,3 +120,78 @@ def test_ufl_codegen_ordmm_land():
|
|
|
117
120
|
# generated successfully as state array index mappings
|
|
118
121
|
assert "hL = states[" in code
|
|
119
122
|
assert "a = states[" in code
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _generated(module, ode, scheme=None):
|
|
126
|
+
code = module.get_code(ode, scheme=scheme)
|
|
127
|
+
namespace: dict = {}
|
|
128
|
+
exec(compile(code, f"<{module.__name__}>", "exec"), namespace)
|
|
129
|
+
return namespace
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_ufl_generalized_rush_larsen_with_float_parameters():
|
|
133
|
+
"""The generated UFL scheme accepts parameters that are plain Python floats.
|
|
134
|
+
|
|
135
|
+
The linearized rate of ``x`` is the parameter ``-k``, so the scheme's
|
|
136
|
+
zero-division guard ``|dx_dt_linearized| > delta`` compares two numbers.
|
|
137
|
+
Printed as ``>`` that is a Python ``bool``, which ``ufl.Or`` refuses.
|
|
138
|
+
"""
|
|
139
|
+
pytest.importorskip("ufl")
|
|
140
|
+
from ufl.core.expr import Expr
|
|
141
|
+
|
|
142
|
+
ode = gotranx.load.ode_from_string(
|
|
143
|
+
"""
|
|
144
|
+
parameters(k=2.0, c=1.0)
|
|
145
|
+
states(x=0.2)
|
|
146
|
+
dx_dt = c - k * x
|
|
147
|
+
"""
|
|
148
|
+
)
|
|
149
|
+
scheme = [Scheme.generalized_rush_larsen]
|
|
150
|
+
ufl_code = _generated(gotran2ufl, ode, scheme)
|
|
151
|
+
py_code = _generated(gotran2py, ode, scheme)
|
|
152
|
+
|
|
153
|
+
states = py_code["init_state_values"]()
|
|
154
|
+
parameters = py_code["init_parameter_values"]()
|
|
155
|
+
dt = 0.1
|
|
156
|
+
expected = py_code["generalized_rush_larsen"](states, 0.0, dt, parameters)
|
|
157
|
+
|
|
158
|
+
values = ufl_code["generalized_rush_larsen"](
|
|
159
|
+
[float(s) for s in states], 0.0, dt, [float(p) for p in parameters]
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
assert isinstance(values[0], Expr)
|
|
163
|
+
assert float(values[0]) == pytest.approx(expected[0], rel=1e-12)
|
|
164
|
+
# The scheme is exact for a linear ODE: x(dt) = c/k + (x0 - c/k) exp(-k dt)
|
|
165
|
+
assert float(values[0]) == pytest.approx(0.5 - 0.3 * math.exp(-0.2), rel=1e-12)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@pytest.mark.parametrize("b", [1.0, 2.0])
|
|
169
|
+
@pytest.mark.parametrize(
|
|
170
|
+
"condition", ["Lt(a, b)", "Le(a, b)", "Gt(a, b)", "Ge(a, b)", "Eq(a, b)", "Not(Eq(a, b))"]
|
|
171
|
+
)
|
|
172
|
+
def test_ufl_relational_with_float_operands(condition, b):
|
|
173
|
+
"""Every relational prints to a UFL condition, whatever its operands are.
|
|
174
|
+
|
|
175
|
+
Comparing two Python numbers gives a Python ``bool``, which
|
|
176
|
+
``ufl.conditional`` refuses. ``==`` and ``!=`` never give a UFL condition,
|
|
177
|
+
even on UFL operands, since UFL reserves them for structural equality.
|
|
178
|
+
"""
|
|
179
|
+
pytest.importorskip("ufl")
|
|
180
|
+
|
|
181
|
+
ode = gotranx.load.ode_from_string(
|
|
182
|
+
f"""
|
|
183
|
+
parameters(a=1.0, b=2.0)
|
|
184
|
+
states(x=0.0)
|
|
185
|
+
dx_dt = Conditional({condition}, 1.0, -1.0)
|
|
186
|
+
"""
|
|
187
|
+
)
|
|
188
|
+
ufl_code = _generated(gotran2ufl, ode)
|
|
189
|
+
py_code = _generated(gotran2py, ode)
|
|
190
|
+
|
|
191
|
+
states = py_code["init_state_values"]()
|
|
192
|
+
parameters = py_code["init_parameter_values"](b=b)
|
|
193
|
+
expected = py_code["rhs"](0.0, states, parameters)
|
|
194
|
+
|
|
195
|
+
values = ufl_code["rhs"](0.0, [float(s) for s in states], [float(p) for p in parameters])
|
|
196
|
+
|
|
197
|
+
assert float(values[0]) == expected[0]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|