PyAntiGen 1.0.11__tar.gz → 1.0.12__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-1.0.11 → pyantigen-1.0.12}/PKG-INFO +1 -1
- {pyantigen-1.0.11 → pyantigen-1.0.12}/PyAntiGen.egg-info/PKG-INFO +1 -1
- pyantigen-1.0.12/PyAntiGen.egg-info/scm_version.json +8 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/_version.py +3 -3
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Evaluator.py +26 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Model_optimize.py +20 -206
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Optimize.py +454 -889
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Simulate.py +18 -1
- pyantigen-1.0.11/PyAntiGen.egg-info/scm_version.json +0 -8
- {pyantigen-1.0.11 → pyantigen-1.0.12}/.github/workflows/publish.yml +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/.gitignore +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/.vscode/launch.json +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/LICENSE +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/MANIFEST.in +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/PyAntiGen.egg-info/SOURCES.txt +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/PyAntiGen.egg-info/dependency_links.txt +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/PyAntiGen.egg-info/entry_points.txt +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/PyAntiGen.egg-info/requires.txt +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/PyAntiGen.egg-info/scm_file_list.json +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/PyAntiGen.egg-info/top_level.txt +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/README.md +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/.agents/skills/module_generation/SKILL.md +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/.agents/skills/ode_conversion_antimony/SKILL.md +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/.agents/skills/ode_extraction_pipeline/SKILL.md +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/.gitignore +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/AntimonyGen.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/RxnDict_to_antimony.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/TelluriumGen.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/__init__.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/antimony_utils.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/cli.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/data_interpolation.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/isotopomer_tools.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/model_generation.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/models.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/module_base.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/pyantigen.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/rate_laws.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/reaction_creation.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/AntiGen_paths.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Anchor_cache.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Deadline.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Event_times.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Fast_profile.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Fit_cache.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Identifiability.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Model_simulate.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Nuisance_sensitivity.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Petab_export.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Preequil_cache.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Profile_checkpoint.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Results.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Engine/Sensitivity_analysis.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Flipflop_reference.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Model_generate.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Model_run.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Modules/Data.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Modules/Events.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Modules/Experiment.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Modules/Loss_config.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Modules/Observed_species.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Modules/Optimizer_settings.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Modules/Plots.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Modules/Solver_settings.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Modules/Update_opt_parameters.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/Example/Modules/Update_parameters.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/data/ADneg.csv +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/data/ADpos.csv +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/data/Flipflop.csv +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/framework/template/data/make_flipflop_data.py +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/pyproject.toml +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/setup.cfg +0 -0
- {pyantigen-1.0.11 → pyantigen-1.0.12}/setup.py +0 -0
|
@@ -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 = '1.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 0,
|
|
21
|
+
__version__ = version = '1.0.12'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 12)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g3450403b3'
|
|
@@ -131,6 +131,17 @@ class EvalSpec:
|
|
|
131
131
|
# in the parent, which runs the invariance check once; a worker must never
|
|
132
132
|
# make that call on its own, or 40 of them would each re-derive it.
|
|
133
133
|
preequil_cache: bool = False
|
|
134
|
+
# Modules.utils.noise_floor.export_cache() snapshot, taken in the parent
|
|
135
|
+
# AFTER its own calibration (see Engine.Optimize.run_optimization_from_groups,
|
|
136
|
+
# clear_cache() + the post-optimum re-evaluation). Workers seed their own
|
|
137
|
+
# (otherwise empty, since spawn shares no memory) floor cache from this in
|
|
138
|
+
# _init_worker, so every worker scores every floored observable against
|
|
139
|
+
# the SAME calibrated sigma the parent settled on, rather than each one
|
|
140
|
+
# independently calibrating against whatever parameter vector it happens
|
|
141
|
+
# to be handed first -- an arbitrary profile-grid point or Sobol sample,
|
|
142
|
+
# not the converged optimum. Same reasoning as fixed_sigmas above, one
|
|
143
|
+
# mechanism down: compute once where it's meaningful, ship the answer.
|
|
144
|
+
floor_cache: dict = field(default_factory=dict)
|
|
134
145
|
# Reserved for future use by the profile grid (Stage 2).
|
|
135
146
|
meta: dict = field(default_factory=dict)
|
|
136
147
|
|
|
@@ -149,8 +160,16 @@ def _init_worker(spec_blob):
|
|
|
149
160
|
from framework.TelluriumGen import TelluriumGen
|
|
150
161
|
from Engine.Event_times import attach_event_times
|
|
151
162
|
from Engine.Optimize import OptRoadRunnerProxy
|
|
163
|
+
from Modules.utils.noise_floor import seed_cache
|
|
152
164
|
|
|
153
165
|
spec = _serializer.loads(spec_blob)
|
|
166
|
+
# Before any task runs: this worker's own Modules.utils.noise_floor
|
|
167
|
+
# module was just re-imported fresh (spawn shares no memory with the
|
|
168
|
+
# parent), so its floor cache starts empty. Seed it from the parent's
|
|
169
|
+
# already-calibrated snapshot so every worker agrees with the parent --
|
|
170
|
+
# and with each other -- on every floored observable's sigma, instead of
|
|
171
|
+
# each recalibrating independently against whichever task it draws first.
|
|
172
|
+
seed_cache(spec.floor_cache)
|
|
154
173
|
models = {}
|
|
155
174
|
t0 = time.time()
|
|
156
175
|
for sim_name, replicate in spec.replicates.items():
|
|
@@ -1125,6 +1144,7 @@ def build_eval_spec(
|
|
|
1125
1144
|
):
|
|
1126
1145
|
"""Convenience constructor mirroring the spec-route local variables."""
|
|
1127
1146
|
from Engine.Event_times import without_event_times
|
|
1147
|
+
from Modules.utils.noise_floor import export_cache
|
|
1128
1148
|
|
|
1129
1149
|
return EvalSpec(
|
|
1130
1150
|
model_text=model_text,
|
|
@@ -1150,6 +1170,12 @@ def build_eval_spec(
|
|
|
1150
1170
|
for_inference=bool(for_inference),
|
|
1151
1171
|
concentrated=bool(concentrated),
|
|
1152
1172
|
preequil_cache=bool(preequil_cache),
|
|
1173
|
+
# Captured HERE, at spec-build time -- called in the parent after its
|
|
1174
|
+
# own clear_cache()-and-recalibrate pass (see run_optimization_from_
|
|
1175
|
+
# groups), so this snapshot is the same calibration the parent's own
|
|
1176
|
+
# subsequent diagnostics use, not whatever was cached earlier in the
|
|
1177
|
+
# run (e.g. during the live optimize()).
|
|
1178
|
+
floor_cache=export_cache(),
|
|
1153
1179
|
)
|
|
1154
1180
|
|
|
1155
1181
|
|
|
@@ -5,8 +5,8 @@ High-level optimization setup. Two entry points:
|
|
|
5
5
|
Original flat-dict path: experiments is a plain dict of treatment dicts.
|
|
6
6
|
|
|
7
7
|
setup_optimization_from_groups(settings, optimization_settings, EXPERIMENT_dict)
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Which replicates contribute to the objective is read off the
|
|
9
|
+
Optimization spec: each group's loss_elements name their simulations.
|
|
10
10
|
"""
|
|
11
11
|
import os
|
|
12
12
|
import pandas as pd
|
|
@@ -1166,15 +1166,6 @@ def setup_optimization(settings, optimization_settings, experiment_dict):
|
|
|
1166
1166
|
# Group-aware entry point
|
|
1167
1167
|
# ---------------------------------------------------------------------------
|
|
1168
1168
|
|
|
1169
|
-
def _is_per_group_settings(optimization_settings):
|
|
1170
|
-
"""Return True when optimization_settings contains per-group sub-dicts
|
|
1171
|
-
(e.g. the PK block keyed by drug name), False for a flat shared dict."""
|
|
1172
|
-
return any(
|
|
1173
|
-
isinstance(v, dict) and "param_names" in v
|
|
1174
|
-
for v in optimization_settings.values()
|
|
1175
|
-
)
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
1169
|
def setup_optimization_from_groups(settings, optimization_settings, EXPERIMENT_dict):
|
|
1179
1170
|
"""
|
|
1180
1171
|
Run parameter optimization using Experiment.opt_groups.
|
|
@@ -1183,13 +1174,9 @@ def setup_optimization_from_groups(settings, optimization_settings, EXPERIMENT_d
|
|
|
1183
1174
|
Replicates whose Loss_config is ``no_optimization()`` are simulated at the
|
|
1184
1175
|
end with the optimal parameters for use by the plot function.
|
|
1185
1176
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
Per-group mode — optimization_settings has per-group sub-dicts each
|
|
1190
|
-
containing param_names/x0/bounds (e.g. the PK block keyed by drug):
|
|
1191
|
-
one independent optimization is run per group, results accumulated,
|
|
1192
|
-
then plot_function is called once.
|
|
1177
|
+
optimization_settings must be an Optimization spec. The flat and
|
|
1178
|
+
per-group dict modes were removed on 2026-09-09; run one invocation per
|
|
1179
|
+
fit instead.
|
|
1193
1180
|
"""
|
|
1194
1181
|
MODEL_NAME = settings.get("MODEL_NAME", AntiGen_paths.MODEL_NAME)
|
|
1195
1182
|
model_text, paths = AntimonyGen(MODEL_NAME, repo_root=REPO_ROOT)
|
|
@@ -1253,201 +1240,28 @@ def setup_optimization_from_groups(settings, optimization_settings, EXPERIMENT_d
|
|
|
1253
1240
|
_shutdown_evaluator(opt)
|
|
1254
1241
|
return opt
|
|
1255
1242
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
known_opt_groups = {
|
|
1265
|
-
name for name, val in optimization_settings.items()
|
|
1266
|
-
if isinstance(val, dict) and val.get("param_names")
|
|
1267
|
-
}
|
|
1268
|
-
for group_name, group_settings in optimization_settings.items():
|
|
1269
|
-
if not isinstance(group_settings, dict) or not group_settings.get("param_names"):
|
|
1270
|
-
continue
|
|
1271
|
-
if allowed_groups is not None and group_name not in allowed_groups:
|
|
1272
|
-
continue
|
|
1273
|
-
param_names = group_settings["param_names"]
|
|
1274
|
-
method = group_settings.get("method", "Nelder-Mead")
|
|
1275
|
-
|
|
1276
|
-
try:
|
|
1277
|
-
opt = run_optimization_from_groups(
|
|
1278
|
-
model_text, paths, experiment,
|
|
1279
|
-
param_names=param_names,
|
|
1280
|
-
x0=group_settings["x0"],
|
|
1281
|
-
bounds=group_settings.get("bounds"),
|
|
1282
|
-
group_names=[group_name],
|
|
1283
|
-
method=method,
|
|
1284
|
-
optimizer_kwargs=group_settings.get("optimizer_kwargs", {}),
|
|
1285
|
-
wald_analysis=group_settings.get("wald_analysis", False),
|
|
1286
|
-
slice_analysis=group_settings.get("slice_analysis", False),
|
|
1287
|
-
profile_likelihood_analysis=group_settings.get("profile_likelihood_analysis", False),
|
|
1288
|
-
fast_profile_likelihood_analysis=group_settings.get("fast_profile_likelihood_analysis", False),
|
|
1289
|
-
sobol_analysis=group_settings.get("sobol_analysis", False),
|
|
1290
|
-
sobol_kwargs={"N": group_settings.get("sobol_N", 128), "mode": group_settings.get("sobol_mode", "loss")},
|
|
1291
|
-
fit_mode=group_settings.get("fit_mode", settings.get("fit_mode")),
|
|
1292
|
-
n_workers=group_settings.get("n_workers", settings.get("n_workers")),
|
|
1293
|
-
)
|
|
1294
|
-
except Exception as e:
|
|
1295
|
-
print(f"Warning: optimization for '{group_name}' failed: {e}")
|
|
1296
|
-
continue
|
|
1297
|
-
|
|
1298
|
-
if opt.get("results_dict"):
|
|
1299
|
-
filtered_results = {}
|
|
1300
|
-
for req_id, item in opt["results_dict"].items():
|
|
1301
|
-
item_group = item.get("replicate", {}).get("Opt_group")
|
|
1302
|
-
if item_group == group_name or item_group not in known_opt_groups:
|
|
1303
|
-
filtered_results[req_id] = item
|
|
1304
|
-
all_results.update(filtered_results)
|
|
1305
|
-
|
|
1306
|
-
group_optimizations[group_name] = opt
|
|
1307
|
-
|
|
1308
|
-
# Run analysis plots first so profile_ci is populated before CSV write
|
|
1309
|
-
if group_settings.get("slice_analysis") and opt.get("stats", {}).get("likelihood_slice"):
|
|
1310
|
-
_save_likelihood_slice_plot(opt, param_names, paths["plot_path"],
|
|
1311
|
-
MODEL_NAME, tag=group_name)
|
|
1312
|
-
if (group_settings.get("profile_likelihood_analysis") or group_settings.get("fast_profile_likelihood_analysis")) and opt.get("stats", {}).get("profile_likelihood"):
|
|
1313
|
-
_save_profile_likelihood_plot(
|
|
1314
|
-
opt, param_names, paths["plot_path"], MODEL_NAME,
|
|
1315
|
-
tag=group_name,
|
|
1316
|
-
profile_kwargs=_profile_kwargs(group_settings))
|
|
1317
|
-
if group_settings.get("sobol_analysis") and opt.get("stats", {}).get("sobol"):
|
|
1318
|
-
from Engine.Sensitivity_analysis import save_sobol_plot
|
|
1319
|
-
save_sobol_plot(opt.get("stats", {}).get("sobol"), paths["plot_path"],
|
|
1320
|
-
MODEL_NAME, tag=group_name)
|
|
1321
|
-
|
|
1322
|
-
print(f"\nOptimization Summary for {group_name}:")
|
|
1323
|
-
print("-" * 85)
|
|
1324
|
-
print(f"{'Parameter':<30} | {'Optimized Value':<15} | {'Wald SE':<15} | {'Wald 95% CI':<20}")
|
|
1325
|
-
print("-" * 85)
|
|
1326
|
-
x_vals = opt.get("x", [])
|
|
1327
|
-
se = opt.get("stats", {}).get("wald_se")
|
|
1328
|
-
ci = opt.get("stats", {}).get("wald_ci")
|
|
1329
|
-
for i, p_name in enumerate(param_names):
|
|
1330
|
-
val = x_vals[i] if i < len(x_vals) else float('nan')
|
|
1331
|
-
std_err = se[i] if se is not None and i < len(se) else "N/A"
|
|
1332
|
-
std_err_str = f"{std_err:.4g}" if isinstance(std_err, (int, float)) else std_err
|
|
1333
|
-
conf_int = ci[i] if ci is not None and i < len(ci) else ("N/A", "N/A")
|
|
1334
|
-
if isinstance(conf_int, tuple) and len(conf_int) == 2:
|
|
1335
|
-
if isinstance(conf_int[0], (int, float)) and isinstance(conf_int[1], (int, float)):
|
|
1336
|
-
conf_int_str = f"[{conf_int[0]:.4g}, {conf_int[1]:.4g}]"
|
|
1337
|
-
else:
|
|
1338
|
-
conf_int_str = f"[{conf_int[0]}, {conf_int[1]}]"
|
|
1339
|
-
else:
|
|
1340
|
-
conf_int_str = str(conf_int)
|
|
1341
|
-
print(f"{p_name:<30} | {val:<15.4g} | {std_err_str:<15} | {conf_int_str:<20}")
|
|
1342
|
-
print("-" * 85)
|
|
1343
|
-
print(f"Final Objective Value (NLL): {opt.get('fun', 'N/A'):.6g}\n")
|
|
1344
|
-
|
|
1345
|
-
corr_matrix = opt.get("stats", {}).get("wald_correlation")
|
|
1346
|
-
if corr_matrix is not None:
|
|
1347
|
-
print(f"\nWald Correlation Matrix:")
|
|
1348
|
-
print("-" * 85)
|
|
1349
|
-
header_str = f"{'':<25} | " + " | ".join(f"{p[:10]:<10}" for p in param_names)
|
|
1350
|
-
print(header_str)
|
|
1351
|
-
print("-" * 85)
|
|
1352
|
-
for i, p_row in enumerate(param_names):
|
|
1353
|
-
row_str = f"{p_row[:25]:<25} | "
|
|
1354
|
-
row_vals = []
|
|
1355
|
-
for j in range(len(param_names)):
|
|
1356
|
-
if i < len(corr_matrix) and j < len(corr_matrix[i]):
|
|
1357
|
-
row_vals.append(f"{corr_matrix[i][j]:<10.4g}")
|
|
1358
|
-
else:
|
|
1359
|
-
row_vals.append(f"{'N/A':<10}")
|
|
1360
|
-
row_str += " | ".join(row_vals)
|
|
1361
|
-
print(row_str)
|
|
1362
|
-
print("-" * 85)
|
|
1363
|
-
print()
|
|
1364
|
-
|
|
1365
|
-
csv_path = os.path.join(
|
|
1366
|
-
paths["plot_path"],
|
|
1367
|
-
f"{MODEL_NAME}_{group_name}_optimization_results.csv",
|
|
1368
|
-
)
|
|
1369
|
-
log_optimization_results(opt, param_names, csv_path,
|
|
1370
|
-
model_name=MODEL_NAME, experiment_id=group_name,
|
|
1371
|
-
method=method)
|
|
1372
|
-
|
|
1373
|
-
if optimization_settings.get("petab_export"):
|
|
1374
|
-
_write_petab_archive(paths, MODEL_NAME, experiment,
|
|
1375
|
-
optimization_settings, group_optimizations)
|
|
1376
|
-
|
|
1377
|
-
if all_results and plot_function:
|
|
1378
|
-
plot_function(paths, all_results)
|
|
1379
|
-
for _o in group_optimizations.values():
|
|
1380
|
-
_shutdown_evaluator(_o)
|
|
1381
|
-
return group_optimizations
|
|
1382
|
-
|
|
1383
|
-
else:
|
|
1384
|
-
# ── Flat (shared) mode ────────────────────────────────────────────
|
|
1385
|
-
param_names = optimization_settings["param_names"]
|
|
1386
|
-
x0 = optimization_settings["x0"]
|
|
1387
|
-
bounds = optimization_settings.get("bounds")
|
|
1388
|
-
method = optimization_settings.get("method", "Nelder-Mead")
|
|
1389
|
-
opt_kwargs = optimization_settings.get("optimizer_kwargs", {})
|
|
1390
|
-
group_names = optimization_settings.get("group_names", None)
|
|
1391
|
-
|
|
1392
|
-
if not param_names:
|
|
1393
|
-
print("Error: No parameters to optimize. Set param_names and x0 in optimization_settings.")
|
|
1394
|
-
return
|
|
1395
|
-
|
|
1396
|
-
opt = run_optimization_from_groups(
|
|
1397
|
-
model_text, paths, experiment,
|
|
1398
|
-
param_names=param_names,
|
|
1399
|
-
x0=x0,
|
|
1400
|
-
bounds=bounds,
|
|
1401
|
-
group_names=group_names,
|
|
1402
|
-
method=method,
|
|
1403
|
-
optimizer_kwargs=opt_kwargs,
|
|
1404
|
-
wald_analysis=optimization_settings.get("wald_analysis", False),
|
|
1405
|
-
slice_analysis=optimization_settings.get("slice_analysis", False),
|
|
1406
|
-
profile_likelihood_analysis=optimization_settings.get("profile_likelihood_analysis", False),
|
|
1407
|
-
fast_profile_likelihood_analysis=optimization_settings.get("fast_profile_likelihood_analysis", False),
|
|
1408
|
-
sobol_analysis=optimization_settings.get("sobol_analysis", False),
|
|
1409
|
-
sobol_kwargs={"N": optimization_settings.get("sobol_N", 128), "mode": optimization_settings.get("sobol_mode", "loss")},
|
|
1410
|
-
fit_mode=optimization_settings.get("fit_mode", settings.get("fit_mode")),
|
|
1411
|
-
n_workers=optimization_settings.get("n_workers", settings.get("n_workers")),
|
|
1412
|
-
)
|
|
1413
|
-
|
|
1414
|
-
groups_tag = "_".join(opt.get("groups", ["ALL"]))
|
|
1415
|
-
|
|
1416
|
-
# Run analysis plots first so profile_ci is populated before CSV write
|
|
1417
|
-
if optimization_settings.get("slice_analysis") and opt["stats"].get("likelihood_slice"):
|
|
1418
|
-
_save_likelihood_slice_plot(opt, param_names, paths["plot_path"],
|
|
1419
|
-
MODEL_NAME, tag=groups_tag)
|
|
1420
|
-
if (optimization_settings.get("profile_likelihood_analysis") or optimization_settings.get("fast_profile_likelihood_analysis")) and opt["stats"].get("profile_likelihood"):
|
|
1421
|
-
_save_profile_likelihood_plot(
|
|
1422
|
-
opt, param_names, paths["plot_path"], MODEL_NAME,
|
|
1423
|
-
tag=groups_tag,
|
|
1424
|
-
profile_kwargs=_profile_kwargs(optimization_settings))
|
|
1425
|
-
if optimization_settings.get("sobol_analysis") and opt["stats"].get("sobol"):
|
|
1426
|
-
from Engine.Sensitivity_analysis import save_sobol_plot
|
|
1427
|
-
save_sobol_plot(opt["stats"]["sobol"], paths["plot_path"],
|
|
1428
|
-
MODEL_NAME, tag=groups_tag)
|
|
1429
|
-
|
|
1430
|
-
csv_path = os.path.join(
|
|
1431
|
-
paths["plot_path"],
|
|
1432
|
-
f"{MODEL_NAME}_{groups_tag}_optimization_results.csv",
|
|
1433
|
-
)
|
|
1434
|
-
log_optimization_results(opt, param_names, csv_path,
|
|
1435
|
-
model_name=MODEL_NAME, experiment_id=groups_tag, method=method)
|
|
1243
|
+
raise TypeError(
|
|
1244
|
+
"setup_optimization_from_groups needs an Optimization spec from "
|
|
1245
|
+
"Modules/Optimizer_settings.py; got "
|
|
1246
|
+
f"{type(optimization_settings).__name__}. The per-group and flat "
|
|
1247
|
+
"dict routes were removed on 2026-09-09: nothing used them, and they "
|
|
1248
|
+
"selected replicates by a per-replicate 'Opt_group' key that no longer "
|
|
1249
|
+
"exists. To run several fits, run several invocations."
|
|
1250
|
+
)
|
|
1436
1251
|
|
|
1437
|
-
if optimization_settings.get("petab_export"):
|
|
1438
|
-
_write_petab_archive(paths, MODEL_NAME, experiment,
|
|
1439
|
-
optimization_settings, {"__flat__": opt})
|
|
1440
|
-
|
|
1441
|
-
if opt.get("results_dict") is not None and plot_function:
|
|
1442
|
-
plot_function(paths, opt["results_dict"])
|
|
1443
|
-
_shutdown_evaluator(opt)
|
|
1444
|
-
return opt
|
|
1445
1252
|
|
|
1446
1253
|
|
|
1447
1254
|
def _write_petab_archive(paths, model_name, experiment,
|
|
1448
1255
|
optimization_settings, group_optimizations):
|
|
1449
1256
|
"""Write a PEtab v2 archive to results/<model>/petab/<expid>/.
|
|
1450
1257
|
|
|
1258
|
+
NOT CALLED as of 2026-09-09. Its only caller was the per-group dict route,
|
|
1259
|
+
removed that day along with the flat route because nothing could reach
|
|
1260
|
+
either. The function is kept because PEtab export is a feature rather than
|
|
1261
|
+
plumbing, and rewiring it to the spec route is a small job: it wants a
|
|
1262
|
+
{group_name: opt} mapping, which for a spec fit is a single entry. Delete
|
|
1263
|
+
it if PEtab export is not wanted.
|
|
1264
|
+
|
|
1451
1265
|
``expid`` is built from the sorted union of optimized group names so
|
|
1452
1266
|
successive runs against different groups land in distinct subdirs.
|
|
1453
1267
|
"""
|