PyAntiGen 2.1.5__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.
- {pyantigen-2.1.5 → pyantigen-2.2.0}/PKG-INFO +5 -1
- {pyantigen-2.1.5 → pyantigen-2.2.0}/PyAntiGen.egg-info/PKG-INFO +5 -1
- {pyantigen-2.1.5 → pyantigen-2.2.0}/PyAntiGen.egg-info/SOURCES.txt +10 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/PyAntiGen.egg-info/entry_points.txt +1 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/PyAntiGen.egg-info/requires.txt +7 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/PyAntiGen.egg-info/scm_file_list.json +22 -0
- pyantigen-2.2.0/PyAntiGen.egg-info/scm_version.json +8 -0
- pyantigen-2.2.0/docs/BACKENDS.md +60 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/_version.py +3 -3
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Analytic_gradient.py +206 -21
- pyantigen-2.2.0/pyantigen/engine/Backend.py +308 -0
- pyantigen-2.2.0/pyantigen/engine/Fim.py +75 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Fit_cache.py +7 -0
- pyantigen-2.2.0/pyantigen/engine/Fit_lm.py +93 -0
- pyantigen-2.2.0/pyantigen/engine/Floors.py +93 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Model_optimize.py +14 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Optimize.py +355 -4
- pyantigen-2.2.0/pyantigen/engine/Profile.py +451 -0
- pyantigen-2.2.0/pyantigen/engine/Profile_amici.py +262 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Profile_checkpoint.py +25 -1
- pyantigen-2.2.0/pyantigen/engine/Profile_lm.py +292 -0
- pyantigen-2.2.0/pyantigen/engine/Profile_record.py +466 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Segmented.py +8 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Simulate.py +51 -5
- pyantigen-2.2.0/pyantigen/setup_env.py +72 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Model_run.py +15 -0
- pyantigen-2.2.0/pyantigen/template/Example/Modules/Solver_settings.py +33 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyproject.toml +3 -0
- pyantigen-2.1.5/PyAntiGen.egg-info/scm_version.json +0 -8
- pyantigen-2.1.5/pyantigen/template/Example/Modules/Solver_settings.py +0 -16
- {pyantigen-2.1.5 → pyantigen-2.2.0}/.github/workflows/publish.yml +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/.github/workflows/tests.yml +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/.gitignore +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/.vscode/launch.json +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/LICENSE +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/MANIFEST.in +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/PyAntiGen.egg-info/dependency_links.txt +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/PyAntiGen.egg-info/top_level.txt +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/README.md +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/docs/ANALYTIC_GRADIENT.md +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/docs/MULTISTART.md +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/docs/PROTOCOL_LAYER.md +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/docs/V2_DESIGN.md +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/.agents/skills/module_generation/SKILL.md +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/.agents/skills/ode_conversion_antimony/SKILL.md +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/.agents/skills/ode_extraction_pipeline/SKILL.md +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/.gitignore +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/__init__.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/cli.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Anchor_cache.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Deadline.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Differential_evolution.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Evaluator.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Event_times.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Fast_profile.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Identifiability.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Model_cache.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Model_simulate.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Multistart.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Nelder_mead.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Noise_floor.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Petab_export.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Preequil_cache.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Profile_plan.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/README.md +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Results.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Scaled_amici.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Sensitivity_analysis.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/Stop_rules.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/engine/__init__.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/AntimonyGen.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/RxnDict_to_antimony.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/TelluriumGen.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/__init__.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/antimony_utils.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/data_interpolation.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/input_schedule.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/isotopomer_tools.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/model_generation.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/models.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/module_base.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/pyantigen.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/rate_laws.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/reaction_creation.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/generate/scaled_time.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/__init__.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/assay.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/describe.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/design.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/lower_v1.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/optimization.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/params.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/quantity.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/reagent.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/refs.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/remarks.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/serialize.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/study/validate.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/AntiGen_paths.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Flipflop_reference.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Model_generate.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Modules/Data.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Modules/Events.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Modules/Experiment.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Modules/Loss_config.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Modules/Observed_species.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Modules/Optimizer_settings.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Modules/Plots.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Modules/Update_opt_parameters.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/Modules/Update_parameters.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/optimizations/Example1_ADneg.json +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/optimizations/Example1_ADpos.json +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/optimizations/Example3_joint.json +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/optimizations/Example4_flipflop.json +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/optimizations/example.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/studies/example.json +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/studies/example.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/Example/studies/flipflop.json +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/data/ADneg.csv +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/data/ADpos.csv +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/data/Flipflop.csv +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/pyantigen/template/data/make_flipflop_data.py +0 -0
- {pyantigen-2.1.5 → pyantigen-2.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyAntiGen
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Declarative generation, simulation and identifiability analysis of compartmental Antimony/SBML models
|
|
5
5
|
Author-email: Don Elbert <elbert5770@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -28,6 +28,10 @@ Requires-Dist: pypesto
|
|
|
28
28
|
Requires-Dist: SALib
|
|
29
29
|
Provides-Extra: test
|
|
30
30
|
Requires-Dist: pytest; extra == "test"
|
|
31
|
+
Provides-Extra: amici
|
|
32
|
+
Requires-Dist: amici>=1.1.0; sys_platform != "win32" and extra == "amici"
|
|
33
|
+
Requires-Dist: cmake; sys_platform != "win32" and extra == "amici"
|
|
34
|
+
Requires-Dist: swig; sys_platform != "win32" and extra == "amici"
|
|
31
35
|
Dynamic: license-file
|
|
32
36
|
|
|
33
37
|
# PyAntiGen
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyAntiGen
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Declarative generation, simulation and identifiability analysis of compartmental Antimony/SBML models
|
|
5
5
|
Author-email: Don Elbert <elbert5770@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -28,6 +28,10 @@ Requires-Dist: pypesto
|
|
|
28
28
|
Requires-Dist: SALib
|
|
29
29
|
Provides-Extra: test
|
|
30
30
|
Requires-Dist: pytest; extra == "test"
|
|
31
|
+
Provides-Extra: amici
|
|
32
|
+
Requires-Dist: amici>=1.1.0; sys_platform != "win32" and extra == "amici"
|
|
33
|
+
Requires-Dist: cmake; sys_platform != "win32" and extra == "amici"
|
|
34
|
+
Requires-Dist: swig; sys_platform != "win32" and extra == "amici"
|
|
31
35
|
Dynamic: license-file
|
|
32
36
|
|
|
33
37
|
# PyAntiGen
|
|
@@ -15,6 +15,7 @@ PyAntiGen.egg-info/scm_file_list.json
|
|
|
15
15
|
PyAntiGen.egg-info/scm_version.json
|
|
16
16
|
PyAntiGen.egg-info/top_level.txt
|
|
17
17
|
docs/ANALYTIC_GRADIENT.md
|
|
18
|
+
docs/BACKENDS.md
|
|
18
19
|
docs/MULTISTART.md
|
|
19
20
|
docs/PROTOCOL_LAYER.md
|
|
20
21
|
docs/V2_DESIGN.md
|
|
@@ -22,17 +23,22 @@ pyantigen/.gitignore
|
|
|
22
23
|
pyantigen/__init__.py
|
|
23
24
|
pyantigen/_version.py
|
|
24
25
|
pyantigen/cli.py
|
|
26
|
+
pyantigen/setup_env.py
|
|
25
27
|
pyantigen/.agents/skills/module_generation/SKILL.md
|
|
26
28
|
pyantigen/.agents/skills/ode_conversion_antimony/SKILL.md
|
|
27
29
|
pyantigen/.agents/skills/ode_extraction_pipeline/SKILL.md
|
|
28
30
|
pyantigen/engine/Analytic_gradient.py
|
|
29
31
|
pyantigen/engine/Anchor_cache.py
|
|
32
|
+
pyantigen/engine/Backend.py
|
|
30
33
|
pyantigen/engine/Deadline.py
|
|
31
34
|
pyantigen/engine/Differential_evolution.py
|
|
32
35
|
pyantigen/engine/Evaluator.py
|
|
33
36
|
pyantigen/engine/Event_times.py
|
|
34
37
|
pyantigen/engine/Fast_profile.py
|
|
38
|
+
pyantigen/engine/Fim.py
|
|
35
39
|
pyantigen/engine/Fit_cache.py
|
|
40
|
+
pyantigen/engine/Fit_lm.py
|
|
41
|
+
pyantigen/engine/Floors.py
|
|
36
42
|
pyantigen/engine/Identifiability.py
|
|
37
43
|
pyantigen/engine/Model_cache.py
|
|
38
44
|
pyantigen/engine/Model_optimize.py
|
|
@@ -43,8 +49,12 @@ pyantigen/engine/Noise_floor.py
|
|
|
43
49
|
pyantigen/engine/Optimize.py
|
|
44
50
|
pyantigen/engine/Petab_export.py
|
|
45
51
|
pyantigen/engine/Preequil_cache.py
|
|
52
|
+
pyantigen/engine/Profile.py
|
|
53
|
+
pyantigen/engine/Profile_amici.py
|
|
46
54
|
pyantigen/engine/Profile_checkpoint.py
|
|
55
|
+
pyantigen/engine/Profile_lm.py
|
|
47
56
|
pyantigen/engine/Profile_plan.py
|
|
57
|
+
pyantigen/engine/Profile_record.py
|
|
48
58
|
pyantigen/engine/README.md
|
|
49
59
|
pyantigen/engine/Results.py
|
|
50
60
|
pyantigen/engine/Scaled_amici.py
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"MANIFEST.in",
|
|
9
9
|
"README.md",
|
|
10
10
|
"docs/ANALYTIC_GRADIENT.md",
|
|
11
|
+
"docs/BACKENDS.md",
|
|
11
12
|
"docs/MULTISTART.md",
|
|
12
13
|
"docs/PROTOCOL_LAYER.md",
|
|
13
14
|
"docs/V2_DESIGN.md",
|
|
@@ -19,12 +20,16 @@
|
|
|
19
20
|
"pyantigen/cli.py",
|
|
20
21
|
"pyantigen/engine/Analytic_gradient.py",
|
|
21
22
|
"pyantigen/engine/Anchor_cache.py",
|
|
23
|
+
"pyantigen/engine/Backend.py",
|
|
22
24
|
"pyantigen/engine/Deadline.py",
|
|
23
25
|
"pyantigen/engine/Differential_evolution.py",
|
|
24
26
|
"pyantigen/engine/Evaluator.py",
|
|
25
27
|
"pyantigen/engine/Event_times.py",
|
|
26
28
|
"pyantigen/engine/Fast_profile.py",
|
|
29
|
+
"pyantigen/engine/Fim.py",
|
|
27
30
|
"pyantigen/engine/Fit_cache.py",
|
|
31
|
+
"pyantigen/engine/Fit_lm.py",
|
|
32
|
+
"pyantigen/engine/Floors.py",
|
|
28
33
|
"pyantigen/engine/Identifiability.py",
|
|
29
34
|
"pyantigen/engine/Model_cache.py",
|
|
30
35
|
"pyantigen/engine/Model_optimize.py",
|
|
@@ -35,8 +40,12 @@
|
|
|
35
40
|
"pyantigen/engine/Optimize.py",
|
|
36
41
|
"pyantigen/engine/Petab_export.py",
|
|
37
42
|
"pyantigen/engine/Preequil_cache.py",
|
|
43
|
+
"pyantigen/engine/Profile.py",
|
|
44
|
+
"pyantigen/engine/Profile_amici.py",
|
|
38
45
|
"pyantigen/engine/Profile_checkpoint.py",
|
|
46
|
+
"pyantigen/engine/Profile_lm.py",
|
|
39
47
|
"pyantigen/engine/Profile_plan.py",
|
|
48
|
+
"pyantigen/engine/Profile_record.py",
|
|
40
49
|
"pyantigen/engine/README.md",
|
|
41
50
|
"pyantigen/engine/Results.py",
|
|
42
51
|
"pyantigen/engine/Scaled_amici.py",
|
|
@@ -60,6 +69,7 @@
|
|
|
60
69
|
"pyantigen/generate/rate_laws.py",
|
|
61
70
|
"pyantigen/generate/reaction_creation.py",
|
|
62
71
|
"pyantigen/generate/scaled_time.py",
|
|
72
|
+
"pyantigen/setup_env.py",
|
|
63
73
|
"pyantigen/study/__init__.py",
|
|
64
74
|
"pyantigen/study/assay.py",
|
|
65
75
|
"pyantigen/study/describe.py",
|
|
@@ -109,14 +119,20 @@
|
|
|
109
119
|
"tests/engine/test_analytic_gradient.py",
|
|
110
120
|
"tests/engine/test_analytic_gradient_amici.py",
|
|
111
121
|
"tests/engine/test_anchor_cache.py",
|
|
122
|
+
"tests/engine/test_backend.py",
|
|
112
123
|
"tests/engine/test_better_point.py",
|
|
124
|
+
"tests/engine/test_block_title.py",
|
|
113
125
|
"tests/engine/test_de_fit.py",
|
|
114
126
|
"tests/engine/test_deadline.py",
|
|
115
127
|
"tests/engine/test_differential_evolution.py",
|
|
116
128
|
"tests/engine/test_event_parameter_reset.py",
|
|
117
129
|
"tests/engine/test_event_times.py",
|
|
118
130
|
"tests/engine/test_fast_profile.py",
|
|
131
|
+
"tests/engine/test_fim.py",
|
|
119
132
|
"tests/engine/test_fit_cache.py",
|
|
133
|
+
"tests/engine/test_fit_lm.py",
|
|
134
|
+
"tests/engine/test_fixed_sigma.py",
|
|
135
|
+
"tests/engine/test_floors.py",
|
|
120
136
|
"tests/engine/test_hessian_steps.py",
|
|
121
137
|
"tests/engine/test_model_cache.py",
|
|
122
138
|
"tests/engine/test_multistart_candidates.py",
|
|
@@ -127,10 +143,15 @@
|
|
|
127
143
|
"tests/engine/test_multistart_triage.py",
|
|
128
144
|
"tests/engine/test_nelder_mead.py",
|
|
129
145
|
"tests/engine/test_point_identity.py",
|
|
146
|
+
"tests/engine/test_profile.py",
|
|
147
|
+
"tests/engine/test_profile_amici.py",
|
|
130
148
|
"tests/engine/test_profile_grid_open.py",
|
|
149
|
+
"tests/engine/test_profile_lm.py",
|
|
131
150
|
"tests/engine/test_profile_quadratic.py",
|
|
151
|
+
"tests/engine/test_profile_record.py",
|
|
132
152
|
"tests/engine/test_profile_records.py",
|
|
133
153
|
"tests/engine/test_profile_stream.py",
|
|
154
|
+
"tests/engine/test_protocol_profile.py",
|
|
134
155
|
"tests/engine/test_results_csv_policy.py",
|
|
135
156
|
"tests/engine/test_schedule_bounds.py",
|
|
136
157
|
"tests/engine/test_search_mode.py",
|
|
@@ -138,6 +159,7 @@
|
|
|
138
159
|
"tests/engine/test_segment_sensitivity.py",
|
|
139
160
|
"tests/engine/test_segmented.py",
|
|
140
161
|
"tests/engine/test_sigma_floor.py",
|
|
162
|
+
"tests/engine/test_sigma_key_fingerprint.py",
|
|
141
163
|
"tests/engine/test_slice_screen.py",
|
|
142
164
|
"tests/engine/test_slice_window.py",
|
|
143
165
|
"tests/engine/test_solver_fingerprint.py",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Solver backends: RoadRunner and AMICI
|
|
2
|
+
|
|
3
|
+
| | RoadRunner | AMICI |
|
|
4
|
+
|---|---|---|
|
|
5
|
+
| install | core dependency (`pip install pyantigen`) | `pip install "pyantigen[amici]"`, then `pyantigen-setup` |
|
|
6
|
+
| platforms | Linux, macOS, Windows | Linux, macOS; **Windows only through WSL** |
|
|
7
|
+
| start-up | seconds | each model is compiled to C++ once (minutes to tens of minutes), then cached |
|
|
8
|
+
| run time | slower; no sensitivities | much faster per evaluation, with exact sensitivities |
|
|
9
|
+
| use for | simulate, quick fits | multistart, profile likelihood, FIM |
|
|
10
|
+
|
|
11
|
+
AMICI needs g++ >= 10.1 (or Apple clang), cmake and swig. `pyantigen-setup` checks all of these and
|
|
12
|
+
prints the fix for anything missing (it never uses sudo). `pyantigen-setup --install-amici` pip-installs
|
|
13
|
+
the package; it refuses on native Windows and on a cluster login node.
|
|
14
|
+
|
|
15
|
+
## Choosing
|
|
16
|
+
|
|
17
|
+
Per stage (`simulate`, `fit`, `multistart`, `profile`, `fim`), first match wins:
|
|
18
|
+
|
|
19
|
+
1. `Model_run.py --backend roadrunner|amici|auto`
|
|
20
|
+
2. environment variable `PYANTIGEN_BACKEND` (reaches spawned workers)
|
|
21
|
+
3. `BACKEND` in the project's `Modules/Solver_settings.py`
|
|
22
|
+
4. `amici`, if the optimization spec already sets `optimizer_kwargs["analytic_gradient"]`
|
|
23
|
+
5. the stage default: RoadRunner for `simulate` and `fit`, AMICI for `multistart`, `profile`, `fim`
|
|
24
|
+
|
|
25
|
+
`auto` at any level means no preference. A backend named at levels 1-4 that cannot run here stops the run
|
|
26
|
+
with the reason and the fix. Only the stage default falls back to RoadRunner, with a note.
|
|
27
|
+
|
|
28
|
+
`--backend amici` on `--optimize` switches on `analytic_gradient` for a spec that lacks it;
|
|
29
|
+
`--backend roadrunner` removes it. `--simulate` is always RoadRunner.
|
|
30
|
+
|
|
31
|
+
## Compiled-model cache
|
|
32
|
+
|
|
33
|
+
`Solver_settings.AMICI_CACHE_DIR`, else `PYANTIGEN_AMICI_CACHE`, else a per-user cache directory
|
|
34
|
+
(`~/.cache/pyantigen/amici_models`, `~/Library/Caches/...` on macOS). On a cluster, point it at group or
|
|
35
|
+
scratch storage: a home directory has a small quota and compiled models are large.
|
|
36
|
+
|
|
37
|
+
## Not covered yet
|
|
38
|
+
|
|
39
|
+
This layer chooses and checks the backend. The multistart, profile and FIM stages still run from the
|
|
40
|
+
project's `tools/` scripts; moving them into the engine is the next phase. BLAS is not checked.
|
|
41
|
+
|
|
42
|
+
## Sigma rule
|
|
43
|
+
|
|
44
|
+
How a data-floored block gets its sigma is a separate setting from the backend, but the AMICI long stages
|
|
45
|
+
depend on it.
|
|
46
|
+
|
|
47
|
+
| `sigma_floor` | sigma used | loss | for |
|
|
48
|
+
|---|---|---|---|
|
|
49
|
+
| `"fixed"` | the LOESS floor, as a known sigma, calibrated once | `const + 0.5 r.r` | AMICI multistart, profile, FIM |
|
|
50
|
+
| `"cap"` (engine default) | `min(sigma_hat, floor)`, re-estimated at every point | concentrated NLL | older RoadRunner fits; to be retired |
|
|
51
|
+
|
|
52
|
+
Precedence: the spec's `optimizer_kwargs["sigma_floor"]`, then `Solver_settings.SIGMA_FLOOR`, then the stage:
|
|
53
|
+
multistart, profile and FIM on AMICI get `"fixed"`. `"cap"` there is an error, because the residual form of
|
|
54
|
+
the loss (Levenberg-Marquardt, `J^T J` as the FIM) needs known sigmas.
|
|
55
|
+
|
|
56
|
+
In fixed mode the floors are calibrated once, at the first point evaluated (normally x0), and are not
|
|
57
|
+
recalibrated when a fit ends. `pyantigen.engine.Floors` saves them to a file and returns a sha256 stamp to put
|
|
58
|
+
on every result, loads them back into worker processes, and compares two calibrations. Recalibrating at the
|
|
59
|
+
optimum is a deliberate second pass: `Floors.calibrate`, refit, `Floors.compare`, and accept when
|
|
60
|
+
`Floors.stable` holds.
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2,
|
|
21
|
+
__version__ = version = '2.2.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 2, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g489275404'
|
|
@@ -32,6 +32,8 @@ engine's parameter updates is what AMICI is given.
|
|
|
32
32
|
AMICI is optional; on Windows it needs WSL. Nothing here imports it until a
|
|
33
33
|
gradient is built.
|
|
34
34
|
"""
|
|
35
|
+
import contextlib
|
|
36
|
+
import fcntl
|
|
35
37
|
import hashlib
|
|
36
38
|
import json
|
|
37
39
|
import os
|
|
@@ -63,7 +65,9 @@ class SimTable:
|
|
|
63
65
|
self._index = {c: i for i, c in enumerate(self.colnames)}
|
|
64
66
|
|
|
65
67
|
def __getitem__(self, key):
|
|
66
|
-
|
|
68
|
+
if isinstance(key, str):
|
|
69
|
+
return self.data[:, self._index[key]]
|
|
70
|
+
return self.data[key] # numpy-style, as RoadRunner's result array allows: table[:, j], table[i, j]
|
|
67
71
|
|
|
68
72
|
def __len__(self):
|
|
69
73
|
return self.data.shape[0]
|
|
@@ -134,6 +138,74 @@ class LossGradient:
|
|
|
134
138
|
raise AnalyticGradientError("the loss is not finite")
|
|
135
139
|
return v
|
|
136
140
|
|
|
141
|
+
def residual_vector(self, items, p_lin):
|
|
142
|
+
"""``(r, const)``: the scaled residuals ``sqrt(w) (y - yhat) / sigma`` of every datum, and ``sum n ln sigma``.
|
|
143
|
+
|
|
144
|
+
With every block's sigma known (``PYANTIGEN_SIGMA_FLOOR=fixed`` and declared sigmas) the loss is exactly
|
|
145
|
+
``const + 0.5 * r.r``, a nonlinear least-squares problem. Raises if any block still profiles its sigma.
|
|
146
|
+
"""
|
|
147
|
+
from pyantigen.engine import Optimize as O
|
|
148
|
+
p_lin = np.asarray(p_lin, dtype=float)
|
|
149
|
+
p_dict = dict(zip(self.param_names, p_lin.tolist()))
|
|
150
|
+
sink = []
|
|
151
|
+
O._RESIDUAL_SINK = sink
|
|
152
|
+
try:
|
|
153
|
+
blocks = O.collect_loss_blocks(items, self.groups, self.replicates, self.param_names, p_lin, p_dict,
|
|
154
|
+
frozen_sigmas=self.frozen_sigmas)
|
|
155
|
+
finally:
|
|
156
|
+
O._RESIDUAL_SINK = None
|
|
157
|
+
sig, const = {}, 0.0
|
|
158
|
+
for key, v in blocks.items():
|
|
159
|
+
sse, n, ks, fl = O._unpack_block(v)
|
|
160
|
+
if n <= 0:
|
|
161
|
+
continue
|
|
162
|
+
s, _cost, state = O._block_sigma_resolution(sse, n, ks, fl)
|
|
163
|
+
if state != "declared":
|
|
164
|
+
raise AnalyticGradientError(
|
|
165
|
+
f"block {key} profiles its sigma ({state}): the residual form needs every sigma known "
|
|
166
|
+
"(set PYANTIGEN_SIGMA_FLOOR=fixed)")
|
|
167
|
+
sig[key] = s
|
|
168
|
+
const += n * np.log(max(s, 1e-300))
|
|
169
|
+
parts = [v / sig[key] for key, v in sink if key in sig and v.size]
|
|
170
|
+
if not parts:
|
|
171
|
+
raise AnalyticGradientError("the loss has no data to score")
|
|
172
|
+
return np.concatenate(parts), float(const)
|
|
173
|
+
|
|
174
|
+
def residual_jacobian(self, items, directions, p_lin):
|
|
175
|
+
"""``(r, J, const)``: residuals, ``J[i, k] = d r_i / d theta_k`` (linear units), and ``sum n ln sigma``.
|
|
176
|
+
|
|
177
|
+
The derivative is taken the way :meth:`gradient` takes the loss's: central differences of the residual
|
|
178
|
+
vector along the sensitivity tables (Richardson), plus a partial for each parameter the loss names itself.
|
|
179
|
+
``J.T @ r`` is then the gradient of ``const + 0.5 r.r`` and ``J.T @ J`` its Gauss-Newton Hessian.
|
|
180
|
+
"""
|
|
181
|
+
p_lin = np.asarray(p_lin, dtype=float)
|
|
182
|
+
K = len(self.param_names)
|
|
183
|
+
r0, const = self.residual_vector(items, p_lin)
|
|
184
|
+
J = np.zeros((r0.size, K))
|
|
185
|
+
|
|
186
|
+
def res(it, p):
|
|
187
|
+
r, _c = self.residual_vector(it, p)
|
|
188
|
+
if r.size != r0.size:
|
|
189
|
+
raise AnalyticGradientError("the number of scored data changed between shifted evaluations")
|
|
190
|
+
return r
|
|
191
|
+
|
|
192
|
+
for k in range(K):
|
|
193
|
+
for units, h in self._step_groups(items, directions, k):
|
|
194
|
+
def central(x, units=units, k=k):
|
|
195
|
+
return (res(self._shift_units(items, directions, units, k, x), p_lin)
|
|
196
|
+
- res(self._shift_units(items, directions, units, k, -x), p_lin)) / (2.0 * x)
|
|
197
|
+
J[:, k] += (4.0 * central(h / 2.0) - central(h)) / 3.0
|
|
198
|
+
if k in self.explicit:
|
|
199
|
+
d = 1e-6 * max(abs(p_lin[k]), 1e-8)
|
|
200
|
+
|
|
201
|
+
def partial(s, k=k):
|
|
202
|
+
up, dn = p_lin.copy(), p_lin.copy()
|
|
203
|
+
up[k] += s
|
|
204
|
+
dn[k] -= s
|
|
205
|
+
return (res(items, up) - res(items, dn)) / (2.0 * s)
|
|
206
|
+
J[:, k] += (4.0 * partial(d / 2.0) - partial(d)) / 3.0
|
|
207
|
+
return r0, J, const
|
|
208
|
+
|
|
137
209
|
@staticmethod
|
|
138
210
|
def _shift(items, directions, k, h):
|
|
139
211
|
out = {}
|
|
@@ -143,10 +215,12 @@ class LossGradient:
|
|
|
143
215
|
out[sim] = {**item, "results": table}
|
|
144
216
|
return out
|
|
145
217
|
|
|
146
|
-
def _step(self, items, directions, k):
|
|
218
|
+
def _step(self, items, directions, k, sims=None):
|
|
147
219
|
"""The step along direction k that moves the smallest-scale entry by ``rel_step`` of itself."""
|
|
148
220
|
ratio = 0.0
|
|
149
221
|
for sim, item in items.items():
|
|
222
|
+
if sims is not None and sim not in sims:
|
|
223
|
+
continue
|
|
150
224
|
D = directions.get(sim)
|
|
151
225
|
if D is None:
|
|
152
226
|
continue
|
|
@@ -156,6 +230,68 @@ class LossGradient:
|
|
|
156
230
|
ratio = max(ratio, float(np.nanmax(np.abs(D[:, :, k]) / scale)) if D.size else 0.0)
|
|
157
231
|
return self.rel_step / ratio if ratio > 0 else None
|
|
158
232
|
|
|
233
|
+
# One finite-difference step for the whole loss is set by the table entry with the largest relative sensitivity in ANY
|
|
234
|
+
# column of ANY simulation. A column the loss never reads (45 states in a typical table) can have sensitivities of 1e12
|
|
235
|
+
# that fix a step far too small for the column it does read, and the central difference of that is lost in rounding:
|
|
236
|
+
# on aggregation_silk the Jacobian of one arm changed by a relative 1e-3 between two points 1e-9 apart while its residuals
|
|
237
|
+
# changed by 1e-10 (the gradient jumped by 1.9). The chain rule allows a step per (simulation, column) unit:
|
|
238
|
+
# dL/dtheta_k = sum_u dL/dR_u . D_u,k, each term by shifting only that column. Units whose steps are within SPLIT_RATIO
|
|
239
|
+
# of each other are shifted together, because splitting everything into single columns turns offsetting partial
|
|
240
|
+
# derivatives (mass balances) into rounding error. Only columns that are not read could be left out, and a probe for
|
|
241
|
+
# "read" columns proved unreliable (it missed columns on silk_appfull, error 18.7), so every column with a non-zero
|
|
242
|
+
# sensitivity is a unit. Measured (silk_appfull optimum, aggregation_silk traced points): SPLIT_RATIO from 3 to 1e5 gives
|
|
243
|
+
# the old gradient on silk_appfull (error vs forward differences 0.021) and no jump on aggregation_silk (3e-4, was 1.9);
|
|
244
|
+
# 1e7 is already too coarse there (8e-3).
|
|
245
|
+
SPLIT_RATIO = 1e3
|
|
246
|
+
|
|
247
|
+
def _unit_steps(self, items, directions, k):
|
|
248
|
+
"""``{(sim, column): h}``: the step that moves that column's most sensitive entry by ``rel_step`` of itself."""
|
|
249
|
+
steps = {}
|
|
250
|
+
for sim, item in items.items():
|
|
251
|
+
D = directions.get(sim)
|
|
252
|
+
if D is None or not D.size:
|
|
253
|
+
continue
|
|
254
|
+
R = item["results"].data
|
|
255
|
+
colmax = np.nanmax(np.abs(R), axis=0)
|
|
256
|
+
for c in range(R.shape[1]):
|
|
257
|
+
Dc = D[:, c, k]
|
|
258
|
+
if not np.any(Dc):
|
|
259
|
+
continue
|
|
260
|
+
scale = np.maximum(np.abs(R[:, c]), 1e-6 * colmax[c] + 1e-300)
|
|
261
|
+
ratio = float(np.nanmax(np.abs(Dc) / scale))
|
|
262
|
+
if ratio > 0:
|
|
263
|
+
steps[(sim, c)] = self.rel_step / ratio
|
|
264
|
+
return steps
|
|
265
|
+
|
|
266
|
+
def _step_groups(self, items, directions, k, read=None, p_lin=None):
|
|
267
|
+
"""``[(units, h)]``: (simulation, column) units that share a step. The smallest remaining step serves every unit
|
|
268
|
+
within SPLIT_RATIO of it; the rest are grouped the same way. (``read`` and ``p_lin`` are accepted and ignored.)"""
|
|
269
|
+
remaining = self._unit_steps(items, directions, k)
|
|
270
|
+
groups = []
|
|
271
|
+
while remaining:
|
|
272
|
+
h0 = min(remaining.values())
|
|
273
|
+
joint = {u for u, h in remaining.items() if h <= max(self.SPLIT_RATIO, 1.0) * h0} # always includes the smallest
|
|
274
|
+
groups.append((joint, h0))
|
|
275
|
+
for u in joint:
|
|
276
|
+
del remaining[u]
|
|
277
|
+
return groups
|
|
278
|
+
|
|
279
|
+
@staticmethod
|
|
280
|
+
def _shift_units(items, directions, units, k, h):
|
|
281
|
+
from collections import defaultdict
|
|
282
|
+
by_sim = defaultdict(list)
|
|
283
|
+
for sim, c in units:
|
|
284
|
+
by_sim[sim].append(c)
|
|
285
|
+
out = dict(items)
|
|
286
|
+
for sim, cols in by_sim.items():
|
|
287
|
+
table = items[sim]["results"]
|
|
288
|
+
data = table.data.copy()
|
|
289
|
+
D = directions[sim]
|
|
290
|
+
for c in cols:
|
|
291
|
+
data[:, c] += h * D[:, c, k]
|
|
292
|
+
out[sim] = {**items[sim], "results": type(table)(table.colnames, data)}
|
|
293
|
+
return out
|
|
294
|
+
|
|
159
295
|
def gradient(self, items, directions, p_lin):
|
|
160
296
|
"""``(nll, grad)`` at *p_lin*.
|
|
161
297
|
|
|
@@ -167,11 +303,10 @@ class LossGradient:
|
|
|
167
303
|
f0 = self.value(items, p_lin)
|
|
168
304
|
g = np.zeros(K)
|
|
169
305
|
for k in range(K):
|
|
170
|
-
h
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
- self.value(self._shift(items, directions, k, -s), p_lin)) / (2.0 * s)
|
|
306
|
+
for units, h in self._step_groups(items, directions, k):
|
|
307
|
+
def central(x, units=units, k=k):
|
|
308
|
+
return (self.value(self._shift_units(items, directions, units, k, x), p_lin)
|
|
309
|
+
- self.value(self._shift_units(items, directions, units, k, -x), p_lin)) / (2.0 * x)
|
|
175
310
|
g[k] += (4.0 * central(h / 2.0) - central(h)) / 3.0
|
|
176
311
|
if k in self.explicit: # the loss names this parameter itself
|
|
177
312
|
d = 1e-6 * max(abs(p_lin[k]), 1e-8)
|
|
@@ -263,6 +398,40 @@ class ParameterReplay:
|
|
|
263
398
|
# AMICI
|
|
264
399
|
# ---------------------------------------------------------------------------------------
|
|
265
400
|
|
|
401
|
+
@contextlib.contextmanager
|
|
402
|
+
def _build_lock(directory):
|
|
403
|
+
"""Hold an exclusive lock on one model's build, across processes (a Slurm array compiles the same model at once).
|
|
404
|
+
|
|
405
|
+
Whoever gets the lock first compiles; the rest wait, then find the finished module and import it. The lock file
|
|
406
|
+
sits beside the model directory, and ``flock`` is released if the holder dies.
|
|
407
|
+
"""
|
|
408
|
+
os.makedirs(os.path.dirname(directory) or ".", exist_ok=True)
|
|
409
|
+
with open(directory + ".lock", "a") as fh:
|
|
410
|
+
fcntl.flock(fh, fcntl.LOCK_EX)
|
|
411
|
+
try:
|
|
412
|
+
yield
|
|
413
|
+
finally:
|
|
414
|
+
fcntl.flock(fh, fcntl.LOCK_UN)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def _model_directory(cache_dir, name, tag):
|
|
418
|
+
"""Where a compiled model lives: the simulation's name made safe for a path and a shell, plus the model's hash.
|
|
419
|
+
|
|
420
|
+
A name such as ``Aducanumab 1 mg/kg (Q4W)`` would otherwise nest a directory at the slash and break the build's
|
|
421
|
+
shell command at the parentheses.
|
|
422
|
+
"""
|
|
423
|
+
return os.path.join(cache_dir, re.sub(r"\W", "_", name) + f"_{tag}")
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def _never_reached(param_names, lost_per_sim):
|
|
427
|
+
"""Fitted parameters that reach no plain parameter of the model in any simulation.
|
|
428
|
+
|
|
429
|
+
A parameter one arm does not use (another drug's binding rate) is ``lost`` there by design; only one that no arm
|
|
430
|
+
reaches would silently get a zero gradient.
|
|
431
|
+
"""
|
|
432
|
+
return [p for p in param_names if lost_per_sim and all(p in lost for lost in lost_per_sim)]
|
|
433
|
+
|
|
434
|
+
|
|
266
435
|
def _tracked_window(settings):
|
|
267
436
|
blocks = settings["simulation_blocks"]
|
|
268
437
|
blocks = list(blocks.values()) if isinstance(blocks, dict) else list(blocks)
|
|
@@ -399,12 +568,9 @@ class _SimGradient:
|
|
|
399
568
|
f"{name}: a hook sets species initial values that depend on the fitted parameters "
|
|
400
569
|
f"({sorted(set(found) | set(direct))}); their sensitivities are not available")
|
|
401
570
|
direct = found
|
|
402
|
-
|
|
403
|
-
if
|
|
404
|
-
|
|
405
|
-
f"{name}: fitted parameter(s) {lost} reach no plain parameter of the model (a species "
|
|
406
|
-
"initial value, or a parameter with an initial assignment?): the analytic gradient "
|
|
407
|
-
"would be zero for them")
|
|
571
|
+
# a parameter this arm does not use (another drug's binding rate) has a zero column here by design;
|
|
572
|
+
# AmiciGradient raises if no arm at all is reached by it
|
|
573
|
+
self.lost = [p for p, hit in zip(param_names, reach.any(axis=0)) if not hit]
|
|
408
574
|
self.free = [p for p, used in zip(plain, reach.any(axis=1)) if used]
|
|
409
575
|
|
|
410
576
|
columns = [c for c in replicate["Observed_species"](r) if c != "time"]
|
|
@@ -418,16 +584,18 @@ class _SimGradient:
|
|
|
418
584
|
outputs = concentration_outputs(sbml, usable)
|
|
419
585
|
tag = hashlib.sha256(json.dumps([scaled.sbml, sorted(self.free), list(outputs.items())],
|
|
420
586
|
sort_keys=True).encode()).hexdigest()[:16]
|
|
421
|
-
directory =
|
|
587
|
+
directory = _model_directory(cache_dir, name, tag)
|
|
422
588
|
module_name = re.sub(r"\W", "_", f"pag_{name}_{tag}")
|
|
423
589
|
import amici
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
590
|
+
init = os.path.join(directory, module_name, "__init__.py")
|
|
591
|
+
with _build_lock(directory): # an __init__.py seen without the lock may belong to a build still in progress
|
|
592
|
+
if os.path.exists(init):
|
|
593
|
+
module = amici.import_model_module(module_name, directory)
|
|
594
|
+
else:
|
|
595
|
+
if verbose:
|
|
596
|
+
print(f" [amici] compiling {name}: {len(self.free)} sensitivity parameter(s), "
|
|
597
|
+
f"{len(usable)} output(s) -> {directory}", flush=True)
|
|
598
|
+
module = compile_model(scaled, module_name, directory, free=self.free, outputs=outputs)
|
|
431
599
|
self.runner = AmiciSegmentRunner(scaled, schedule, module, states=[], rtol=rtol, atol=atol,
|
|
432
600
|
max_steps=max_steps)
|
|
433
601
|
self.t_start, self.t_first, self.t_last = _tracked_window(settings)
|
|
@@ -571,6 +739,12 @@ class AmiciGradient:
|
|
|
571
739
|
model_text=model_text, paths=paths, cache_dir=cache_dir, rtol=settings["rtol"],
|
|
572
740
|
atol=settings["atol"], max_steps=settings["max_steps"], verbose=settings["verbose"],
|
|
573
741
|
seed=settings["seed"], extra_times=(settings["extra_times"] or {}).get(name, ()))
|
|
742
|
+
never = _never_reached(self.param_names, [sim.lost for sim in self.sims.values()])
|
|
743
|
+
if never:
|
|
744
|
+
raise AnalyticGradientError(
|
|
745
|
+
f"fitted parameter(s) {never} reach no plain parameter of the model in any arm (a species "
|
|
746
|
+
"initial value, or a parameter with an initial assignment?): the analytic gradient "
|
|
747
|
+
"would be zero for them")
|
|
574
748
|
self.n_calls = 0
|
|
575
749
|
|
|
576
750
|
@staticmethod
|
|
@@ -605,6 +779,17 @@ class AmiciGradient:
|
|
|
605
779
|
self.n_calls += 1
|
|
606
780
|
return self.loss.gradient(items, dirs, p_lin)
|
|
607
781
|
|
|
782
|
+
def linear_residual_jacobian(self, p_lin):
|
|
783
|
+
"""``(r, J, const)`` on AMICI's own simulation; see :meth:`LossGradient.residual_jacobian`."""
|
|
784
|
+
items, dirs = {}, {}
|
|
785
|
+
for name, sim in self.sims.items():
|
|
786
|
+
table, D = sim.tables(p_lin, self.groups, self.replicates, self.models)
|
|
787
|
+
items[name] = {"results": table, "data": self.models[name]["df_dict"],
|
|
788
|
+
"replicate": self.replicates[name]}
|
|
789
|
+
dirs[name] = D
|
|
790
|
+
self.n_calls += 1
|
|
791
|
+
return self.loss.residual_jacobian(items, dirs, p_lin)
|
|
792
|
+
|
|
608
793
|
def __call__(self, x_opt):
|
|
609
794
|
"""The gradient in optimizer space (a log10 parameter's derivative is taken with respect to log10 p)."""
|
|
610
795
|
from pyantigen.engine.Optimize import _to_linear
|