PyAntiGen 2.1.2__tar.gz → 2.1.4__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.2 → pyantigen-2.1.4}/LICENSE +1 -1
- {pyantigen-2.1.2 → pyantigen-2.1.4}/PKG-INFO +1 -1
- {pyantigen-2.1.2 → pyantigen-2.1.4}/PyAntiGen.egg-info/PKG-INFO +1 -1
- {pyantigen-2.1.2 → pyantigen-2.1.4}/PyAntiGen.egg-info/SOURCES.txt +4 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/PyAntiGen.egg-info/scm_file_list.json +12 -0
- pyantigen-2.1.4/PyAntiGen.egg-info/scm_version.json +8 -0
- pyantigen-2.1.4/docs/ANALYTIC_GRADIENT.md +137 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/_version.py +3 -3
- pyantigen-2.1.4/pyantigen/engine/Analytic_gradient.py +632 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Multistart.py +65 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Optimize.py +152 -24
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Profile_plan.py +76 -15
- pyantigen-2.1.4/pyantigen/engine/Scaled_amici.py +269 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Segmented.py +28 -4
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Simulate.py +46 -5
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/input_schedule.py +393 -2
- pyantigen-2.1.4/pyantigen/generate/scaled_time.py +345 -0
- pyantigen-2.1.2/PyAntiGen.egg-info/scm_version.json +0 -8
- {pyantigen-2.1.2 → pyantigen-2.1.4}/.github/workflows/publish.yml +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/.github/workflows/tests.yml +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/.gitignore +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/.vscode/launch.json +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/MANIFEST.in +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/PyAntiGen.egg-info/dependency_links.txt +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/PyAntiGen.egg-info/entry_points.txt +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/PyAntiGen.egg-info/requires.txt +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/PyAntiGen.egg-info/top_level.txt +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/README.md +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/docs/MULTISTART.md +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/docs/PROTOCOL_LAYER.md +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/docs/V2_DESIGN.md +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/.agents/skills/module_generation/SKILL.md +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/.agents/skills/ode_conversion_antimony/SKILL.md +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/.agents/skills/ode_extraction_pipeline/SKILL.md +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/.gitignore +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/__init__.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/cli.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Anchor_cache.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Deadline.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Differential_evolution.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Evaluator.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Event_times.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Fast_profile.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Fit_cache.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Identifiability.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Model_cache.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Model_optimize.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Model_simulate.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Nelder_mead.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Noise_floor.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Petab_export.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Preequil_cache.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Profile_checkpoint.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/README.md +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Results.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Sensitivity_analysis.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/Stop_rules.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/engine/__init__.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/AntimonyGen.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/RxnDict_to_antimony.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/TelluriumGen.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/__init__.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/antimony_utils.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/data_interpolation.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/isotopomer_tools.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/model_generation.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/models.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/module_base.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/pyantigen.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/rate_laws.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/generate/reaction_creation.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/__init__.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/assay.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/describe.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/design.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/lower_v1.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/optimization.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/params.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/quantity.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/reagent.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/refs.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/remarks.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/serialize.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/study/validate.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/AntiGen_paths.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Flipflop_reference.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Model_generate.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Model_run.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Modules/Data.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Modules/Events.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Modules/Experiment.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Modules/Loss_config.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Modules/Observed_species.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Modules/Optimizer_settings.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Modules/Plots.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Modules/Solver_settings.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Modules/Update_opt_parameters.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/Modules/Update_parameters.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/optimizations/Example1_ADneg.json +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/optimizations/Example1_ADpos.json +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/optimizations/Example3_joint.json +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/optimizations/Example4_flipflop.json +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/optimizations/example.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/studies/example.json +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/studies/example.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/Example/studies/flipflop.json +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/data/ADneg.csv +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/data/ADpos.csv +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/data/Flipflop.csv +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyantigen/template/data/make_flipflop_data.py +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/pyproject.toml +0 -0
- {pyantigen-2.1.2 → pyantigen-2.1.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 University of Washington - Elbert Lab
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -14,6 +14,7 @@ PyAntiGen.egg-info/requires.txt
|
|
|
14
14
|
PyAntiGen.egg-info/scm_file_list.json
|
|
15
15
|
PyAntiGen.egg-info/scm_version.json
|
|
16
16
|
PyAntiGen.egg-info/top_level.txt
|
|
17
|
+
docs/ANALYTIC_GRADIENT.md
|
|
17
18
|
docs/MULTISTART.md
|
|
18
19
|
docs/PROTOCOL_LAYER.md
|
|
19
20
|
docs/V2_DESIGN.md
|
|
@@ -24,6 +25,7 @@ pyantigen/cli.py
|
|
|
24
25
|
pyantigen/.agents/skills/module_generation/SKILL.md
|
|
25
26
|
pyantigen/.agents/skills/ode_conversion_antimony/SKILL.md
|
|
26
27
|
pyantigen/.agents/skills/ode_extraction_pipeline/SKILL.md
|
|
28
|
+
pyantigen/engine/Analytic_gradient.py
|
|
27
29
|
pyantigen/engine/Anchor_cache.py
|
|
28
30
|
pyantigen/engine/Deadline.py
|
|
29
31
|
pyantigen/engine/Differential_evolution.py
|
|
@@ -45,6 +47,7 @@ pyantigen/engine/Profile_checkpoint.py
|
|
|
45
47
|
pyantigen/engine/Profile_plan.py
|
|
46
48
|
pyantigen/engine/README.md
|
|
47
49
|
pyantigen/engine/Results.py
|
|
50
|
+
pyantigen/engine/Scaled_amici.py
|
|
48
51
|
pyantigen/engine/Segmented.py
|
|
49
52
|
pyantigen/engine/Sensitivity_analysis.py
|
|
50
53
|
pyantigen/engine/Simulate.py
|
|
@@ -64,6 +67,7 @@ pyantigen/generate/module_base.py
|
|
|
64
67
|
pyantigen/generate/pyantigen.py
|
|
65
68
|
pyantigen/generate/rate_laws.py
|
|
66
69
|
pyantigen/generate/reaction_creation.py
|
|
70
|
+
pyantigen/generate/scaled_time.py
|
|
67
71
|
pyantigen/study/__init__.py
|
|
68
72
|
pyantigen/study/assay.py
|
|
69
73
|
pyantigen/study/describe.py
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
"LICENSE",
|
|
8
8
|
"MANIFEST.in",
|
|
9
9
|
"README.md",
|
|
10
|
+
"docs/ANALYTIC_GRADIENT.md",
|
|
10
11
|
"docs/MULTISTART.md",
|
|
11
12
|
"docs/PROTOCOL_LAYER.md",
|
|
12
13
|
"docs/V2_DESIGN.md",
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
"pyantigen/.gitignore",
|
|
17
18
|
"pyantigen/__init__.py",
|
|
18
19
|
"pyantigen/cli.py",
|
|
20
|
+
"pyantigen/engine/Analytic_gradient.py",
|
|
19
21
|
"pyantigen/engine/Anchor_cache.py",
|
|
20
22
|
"pyantigen/engine/Deadline.py",
|
|
21
23
|
"pyantigen/engine/Differential_evolution.py",
|
|
@@ -37,6 +39,7 @@
|
|
|
37
39
|
"pyantigen/engine/Profile_plan.py",
|
|
38
40
|
"pyantigen/engine/README.md",
|
|
39
41
|
"pyantigen/engine/Results.py",
|
|
42
|
+
"pyantigen/engine/Scaled_amici.py",
|
|
40
43
|
"pyantigen/engine/Segmented.py",
|
|
41
44
|
"pyantigen/engine/Sensitivity_analysis.py",
|
|
42
45
|
"pyantigen/engine/Simulate.py",
|
|
@@ -56,6 +59,7 @@
|
|
|
56
59
|
"pyantigen/generate/pyantigen.py",
|
|
57
60
|
"pyantigen/generate/rate_laws.py",
|
|
58
61
|
"pyantigen/generate/reaction_creation.py",
|
|
62
|
+
"pyantigen/generate/scaled_time.py",
|
|
59
63
|
"pyantigen/study/__init__.py",
|
|
60
64
|
"pyantigen/study/assay.py",
|
|
61
65
|
"pyantigen/study/describe.py",
|
|
@@ -99,6 +103,11 @@
|
|
|
99
103
|
"tests/README.md",
|
|
100
104
|
"tests/__init__.py",
|
|
101
105
|
"tests/conftest.py",
|
|
106
|
+
"tests/engine/test_amici_flux_outputs.py",
|
|
107
|
+
"tests/engine/test_amici_jumps.py",
|
|
108
|
+
"tests/engine/test_amici_sensitivity.py",
|
|
109
|
+
"tests/engine/test_analytic_gradient.py",
|
|
110
|
+
"tests/engine/test_analytic_gradient_amici.py",
|
|
102
111
|
"tests/engine/test_anchor_cache.py",
|
|
103
112
|
"tests/engine/test_better_point.py",
|
|
104
113
|
"tests/engine/test_de_fit.py",
|
|
@@ -123,7 +132,9 @@
|
|
|
123
132
|
"tests/engine/test_profile_records.py",
|
|
124
133
|
"tests/engine/test_profile_stream.py",
|
|
125
134
|
"tests/engine/test_results_csv_policy.py",
|
|
135
|
+
"tests/engine/test_schedule_bounds.py",
|
|
126
136
|
"tests/engine/test_search_mode.py",
|
|
137
|
+
"tests/engine/test_segment_jumps.py",
|
|
127
138
|
"tests/engine/test_segment_sensitivity.py",
|
|
128
139
|
"tests/engine/test_segmented.py",
|
|
129
140
|
"tests/engine/test_sigma_floor.py",
|
|
@@ -139,6 +150,7 @@
|
|
|
139
150
|
"tests/generate/test_gen_models.py",
|
|
140
151
|
"tests/generate/test_gen_pyantigen_api.py",
|
|
141
152
|
"tests/generate/test_gen_rxndict_to_antimony.py",
|
|
153
|
+
"tests/generate/test_gen_scaled_time.py",
|
|
142
154
|
"tests/silk_fixtures/antimony_models/Antimony_Bloomingdale_2021_1a_all_reactions.txt",
|
|
143
155
|
"tests/silk_fixtures/antimony_models/Antimony_Bloomingdale_2021_1a_rules.txt",
|
|
144
156
|
"tests/silk_fixtures/antimony_models/Antimony_Elbert_2022_1a_all_reactions.txt",
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Fitting with a schedule: the bounds check and the AMICI gradient
|
|
2
|
+
|
|
3
|
+
Two options of `optimizer_kwargs` for a spec whose simulations take their clock-driven inputs from
|
|
4
|
+
an `InputSchedule` (see `pyantigen/generate/input_schedule.py`). Both leave a spec without a
|
|
5
|
+
schedule exactly as it was.
|
|
6
|
+
|
|
7
|
+
## `schedule_bounds`: keep the box inside the region the schedule can be integrated in
|
|
8
|
+
|
|
9
|
+
A schedule can only be integrated while its switches keep their order (`InputSchedule.validate`):
|
|
10
|
+
in the SILK hour `0 < t_CSFdraw < V_LP/Q_CSF < 1`. A parameter set where they cross is a failed
|
|
11
|
+
evaluation, and a gradient method reads the failure as a cliff. Before the first evaluation the
|
|
12
|
+
optimizer checks the bounds of every fitted parameter that moves a switch time against each
|
|
13
|
+
simulation's schedule (`InputSchedule.check_box`: the corners of the box, the starting point, and
|
|
14
|
+
64 pseudo-random points inside it).
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
optimizer_kwargs = {"schedule_bounds": "warn"} # "warn" (default) | "error" | "clip" | "ignore"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
* `"warn"` prints which corner is infeasible and goes on. A box wider than the region is not
|
|
21
|
+
necessarily wrong, but those evaluations will fail.
|
|
22
|
+
* `"error"` raises `ScheduleError` with the same message.
|
|
23
|
+
* `"clip"` cuts the bounds of the switch-moving parameters toward x0, by the largest common factor
|
|
24
|
+
for which the box is feasible (`InputSchedule.shrink_box`), and prints what it did.
|
|
25
|
+
* A starting point that is itself infeasible stops the fit in every mode except `"ignore"`.
|
|
26
|
+
|
|
27
|
+
Only simulations whose solver settings carry a `schedule` are checked, and the fitted names are taken
|
|
28
|
+
to be the schedule's parameters: a parameter a hook derives from them is not followed.
|
|
29
|
+
|
|
30
|
+
## `analytic_gradient`: the gradient from AMICI's forward sensitivities
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
optimizer_kwargs = {
|
|
34
|
+
"method": "L-BFGS-B",
|
|
35
|
+
"analytic_gradient": True, # or a dict of the settings below
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Used by every gradient method (`L-BFGS-B`, `BFGS`, `CG`, `TNC`, `SLSQP`): in a single fit and in the
|
|
40
|
+
local fits of the multi-start stage, in place of the finite differences. With a gradient-free method it
|
|
41
|
+
is ignored (with a note). Needs AMICI >= 1.1 (on Windows, under WSL) and the simulations' inputs as an
|
|
42
|
+
`InputSchedule`, or as `at (time >= ...)` event lines `InputSchedule.from_antimony_events` can read.
|
|
43
|
+
A problem it cannot handle stops the run before any fit (`Multistart.GradientSetupError`).
|
|
44
|
+
|
|
45
|
+
| key | default | meaning |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| `rtol`, `atol` | 1e-10, 1e-12 | AMICI's tolerances (state and sensitivities) |
|
|
48
|
+
| `max_steps` | 2 000 000 | AMICI step cap; the 10 000 h pre-equilibration of SILK needs it |
|
|
49
|
+
| `rel_step` | 1e-5 | step of the loss differentiation (below) |
|
|
50
|
+
| `cache_dir` | `<plot_path>/amici_models` | compiled models; one directory per distinct model |
|
|
51
|
+
| `seed` | none | `{state: amount}` given to AMICI at the start of every run, on top of the model's own initial state (see "A model that starts at zero") |
|
|
52
|
+
|
|
53
|
+
### What it computes
|
|
54
|
+
|
|
55
|
+
The objective is the engine's concentrated NLL. Its derivative is not written out again; it is split by
|
|
56
|
+
the chain rule into the part AMICI knows and the part the loss knows:
|
|
57
|
+
|
|
58
|
+
1. AMICI integrates the event-free model of `generate/scaled_time.py` and differentiates every
|
|
59
|
+
observed column (concentrations as `species / compartment`, assigned variables, parameters) with
|
|
60
|
+
respect to the model parameters the fitted ones reach. That table is mapped to the fitted parameters by
|
|
61
|
+
the Jacobian of the engine's own parameter updates (`Update_parameters`, `parameter_hooks`,
|
|
62
|
+
`Update_opt_parameters`), obtained by replaying them on the RoadRunner model.
|
|
63
|
+
2. `LossGradient` differentiates the engine's own loss (`collect_loss_blocks`, `concentrated_nll`:
|
|
64
|
+
groups, pooled sigma blocks, sigma floors, observable expressions such as
|
|
65
|
+
`np.log10(np.maximum(predicted_B, 1e-12))`) along that table, by a Richardson central difference of the
|
|
66
|
+
loss alone. It re-does arithmetic on tables in memory: no integration, no noise. A parameter the loss
|
|
67
|
+
names (`SF*predicted_B`) gets its explicit partial derivative too.
|
|
68
|
+
|
|
69
|
+
The value in the fit is still the engine's (`Multistart.AnalyticObjective`: value from the objective,
|
|
70
|
+
gradient from the provider), so the best-so-far tracking, caches and failure handling are unchanged.
|
|
71
|
+
If AMICI fails at one point (a solver error, a non-finite sensitivity) that gradient is taken by forward
|
|
72
|
+
differences of the objective, counted, and reported with the fit; a zero gradient there would read as
|
|
73
|
+
convergence.
|
|
74
|
+
|
|
75
|
+
### It differentiates the engine's objective, not the model's formulas
|
|
76
|
+
|
|
77
|
+
Where the two differ, the gradient follows the engine:
|
|
78
|
+
|
|
79
|
+
* **RoadRunner's `reset()` re-derives a species' initial assignment from the current parameters but not a
|
|
80
|
+
parameter's.** A parameter defined by an initial assignment from a fitted one (SILK's `Q_glymph = Q_CSF`,
|
|
81
|
+
`Q_SN = Q_CSF*f_SN`) keeps the value it had when the model was built while the optimizer moves `Q_CSF`.
|
|
82
|
+
The AMICI model gets those parameters as plain numbers at the engine's values
|
|
83
|
+
(`freeze_stale_assignments`); a schedule input among them keeps its stale starting value when it
|
|
84
|
+
differs from the formula. The stale value is a property of the in-place simulation, and the gradient
|
|
85
|
+
matches it.
|
|
86
|
+
* **Every parameter with an initial assignment is frozen**, not only those that read a fitted one. A model
|
|
87
|
+
whose constants are written as expressions (`k = 9.56e-7*70`, `V = V_base`) has them as initial
|
|
88
|
+
assignments, and a project's `Update_parameters` or hooks may overwrite them on the RoadRunner model.
|
|
89
|
+
Each becomes a plain parameter at the value RoadRunner holds, so AMICI integrates what the engine
|
|
90
|
+
integrates, and a hook that moves it is seen by the replay. (Before, only those reading a fitted parameter
|
|
91
|
+
were frozen; the others stayed NaN placeholders that reached AMICI's parameter vector, and a value a hook
|
|
92
|
+
had overwritten was not the one AMICI used.) Variables the schedule assigns, and what their initial values
|
|
93
|
+
are built from, are left alone.
|
|
94
|
+
* A species a hook sets directly is handed to AMICI as an initial value (without sensitivities; a hook
|
|
95
|
+
whose value depends on the fitted parameters is refused).
|
|
96
|
+
|
|
97
|
+
### A model that starts at zero
|
|
98
|
+
|
|
99
|
+
AMICI differentiates the right-hand side symbolically and evaluates it at the first instant. A rate law
|
|
100
|
+
with a fractional power of an amount that is exactly zero there, `(Total/V)^0.8`, has an infinite derivative
|
|
101
|
+
at 0, and `0 * inf` is NaN: the run fails at t = 0 with status -42 ("NaN value for sxdot"). RoadRunner does
|
|
102
|
+
not differentiate, so the same model integrates without complaint. Give the states in question a negligible
|
|
103
|
+
amount, in AMICI only:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
optimizer_kwargs = {"analytic_gradient": {"seed": {"AB40_BrainISF": 1e-12, "AB42_BrainISF": 1e-12}}}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Read the AMICI log for the names: it lists the first states and rates that were NaN. The effect on the
|
|
110
|
+
trajectory is of the order of the seed, forgotten within the first steps of a stable system; check it for
|
|
111
|
+
yours by comparing the AMICI values with the engine's, as `Checks/wsl/` does. Unguarded ratios of two
|
|
112
|
+
zero amounts (`A / B` as an output) also print a NaN warning at t = 0 but do not stop the run unless the
|
|
113
|
+
dynamics read them.
|
|
114
|
+
|
|
115
|
+
### Limits
|
|
116
|
+
|
|
117
|
+
* Time points: the loss is differentiated at the data times and the ends of the tracked window; the
|
|
118
|
+
engine interpolates a dense grid linearly between them. The two differ by the interpolation error
|
|
119
|
+
(a few 1e-4 of the gradient's scale on SILK).
|
|
120
|
+
* The fitted parameters must reach the model through plain parameters (constants without rule or
|
|
121
|
+
initial assignment) or hooks onto them. A fitted species initial value, or a parameter with an initial
|
|
122
|
+
assignment, is refused with a message naming it.
|
|
123
|
+
* Columns that are not species, parameters or compartments (reaction rates) are not available to the
|
|
124
|
+
loss; the gradient is then zero for anything that depends only on them.
|
|
125
|
+
* The profile-likelihood stage keeps its own optimizers.
|
|
126
|
+
The gradient can differentiate what a profile point minimizes: `provider.freeze_sigmas(block_sigmas)` pins the floored blocks'
|
|
127
|
+
sigmas as `evaluate_nll_fixed(frozen_sigmas=...)` does (without it the gradient is off by 10 to 30% away from the fit);
|
|
128
|
+
`freeze_sigmas(None)` releases them. Nothing calls it yet.
|
|
129
|
+
* Cost: the gradient costs one AMICI forward-sensitivity run, which grows with the number of
|
|
130
|
+
parameters it differentiates. On SILK, 8 parameters: about 1.2 s a gradient against 9 objective
|
|
131
|
+
evaluations (about 1.4 s) for forward differences; with all 48 parameters AMICI needs about 40 s a
|
|
132
|
+
gradient against about 8 s for 49 evaluations. The case for it is a gradient without the noise of the
|
|
133
|
+
integrator tolerance and without a step to choose, not speed at every size. Fit only the parameters
|
|
134
|
+
that need fitting: the model is compiled for exactly those.
|
|
135
|
+
|
|
136
|
+
Checks on the real SILK model are in the examples repository,
|
|
137
|
+
`Projects/Elbert_2022_PyAntiGen_v4/Checks/wsl/fit_gradient.py`.
|
|
@@ -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.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 1,
|
|
21
|
+
__version__ = version = '2.1.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 1, 4)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g2e65abb87'
|