PEtab export ignores the postprocess key, so a job whose fit scores script-transformed simulations exports as a problem that scores the raw ones, without an error
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 76/100
調査の方向性
pybnf/petab/export.py:188-190 の prediction-transform 拒否チェックを読み、pybnf/config.py:4235 で conf['postprocess'] がどのように読み込まれるかと比較してください。edition-2 のデモ設定で再現し、現在の export が警告なしで完了することを確認してください。docs/petab.rst に記載された PEtab v2 の境界に従い、postprocessing を使用するジョブを export が拒否すれば完了です。
索引モデルが issue の本文から書いたものです。
説明
PEtab export ignores the postprocess key, so a job whose fit scores script-transformed simulations exports as a problem that scores the raw ones, without an error
export_job refuses the PyBNF prediction transforms that PEtab cannot express: _reject_cumulative, _reject_time_error and _reject_normalization (pybnf/petab/export.py:188-190). It never reads conf['postprocess'], and nothing under pybnf/petab/ mentions the key.
The fitter does apply the script:
Configuration._load_postprocessing(pybnf/config.py:4235) maps each(model, suffix)to the script. Under edition 2 the experiment name serves as the suffix.Result.postprocess_data(pybnf/algorithms/core.py:84-110) then replaces the simulation withpostprocess(data)before it is scored.
A user script is an arbitrary transform of the prediction, and PEtab has no way to represent one. Normalization is refused for the same reason. The export instead writes the bare model column as the observableFormula. The problem it emits has a different objective and a different optimum from the fit, and nothing warns about it. That breaks the contract in docs/petab.rst: anything PEtab v2 cannot express raises NotImplementedError naming the boundary, and nothing is dropped quietly.
Failure scenario
Take the edition-2 demo job (examples/demo/demo_bng_v2.conf) and add postprocess = pp.py par1, where pp.py multiplies the simulated y column by 10.
- At the model's values (v1, v2, v3) = (0.5, 1, 3), the fitter's chi_sq is 645387.75. The exported problem gives 0 there.
- At (0.05, 0.1, 0.3), the fitter's exact optimum (1.07e-33), the exported problem gives 6453.88.
export_jobcompletes with no exception and no warning.observables.tsvhasfunc_y y ... normalwith no transform, and no output file mentions the script.
Reproduction
In an empty directory, with PyBNF installed and BNGPATH set, copy examples/demo/parabola_v2.bngl and examples/demo/par1.exp from the repository and create these files.
pp.py:
def postprocess(data):
data.data[:, data.cols['y']] *= 10
return data
check.conf:
edition = 2
model: parabola_v2.bngl
job_type = check
objective = chi_sq
experiment: par1, data: par1.exp
postprocess = pp.py par1
output_dir = out_check
fit.conf (the demo job plus the postprocess line):
edition = 2
model: parabola_v2.bngl
job_type = de
objective = chi_sq
experiment: par1, data: par1.exp
uniform_var = v1 0 10
uniform_var = v2 0 10
uniform_var = v3 0 10
population_size = 20
max_iterations = 30
postprocess = pp.py par1
output_dir = out_fit
pybnf -c check.confprintsObjective value is 645387.75. With thepostprocessline deleted, it printsObjective value is 1.0650618739334988e-33. With the postprocess line kept and the model's values changed to v1 0.05, v2 0.1, v3 0.3 inparabola_v2.bngl, it printsObjective value is 1.0650618739334988e-33.- Run this export:
It printsimport warnings from pybnf.petab.export import export_job with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') export_job('fit.conf', 'petab_out') print(w)[]. Thefunc_yrow ofpetab_out/observables.tsvisfunc_y y noiseParameter1_func_y normal noiseParameter1_func_y. - Score the exported tables by hand. For this model, y = v1 x^2 + v2 x + v3 with x = t - 10:
It printsimport csv rows = list(csv.DictReader(open('petab_out/measurements.tsv'), delimiter='\t')) def chi_sq(v1, v2, v3, y_scale=1): tot = 0.0 for r in rows: x = float(r['time']) - 10 pred = x if r['observableId'] == 'obs_x' else y_scale * (v1*x*x + v2*x + v3) tot += (float(r['measurement']) - pred)**2 / (2 * float(r['noiseParameters'])**2) return tot print(chi_sq(0.5, 1, 3), chi_sq(0.5, 1, 3, y_scale=10), chi_sq(0.05, 0.1, 0.3))0.0 645387.75 6453.8775000000005. The middle value puts the script's ×10 back into the formula, and it equals the fitter's 645387.75 exactly. So the exported problem is the job without its postprocess script.- Its optimum is (0.5, 1, 3), where the fitter scores 645387.75.
- The fitter's optimum is (0.05, 0.1, 0.3), where the exported problem scores 6453.88.
The correct behaviour is to refuse the export, the same way a normalization is refused. That is what docs/petab.rst promises for anything PEtab v2 cannot express.
Reachability
This affects any edition-2 job that uses the documented postprocess key (docs/config_keys.rst, docs/advanced.rst) and is exported through pybnf.petab.export_job. The example script in docs/advanced.rst mean-centres an observable. That is the same kind of whole-trajectory reduction as the built-in normalizations, which _reject_normalization refuses.
Nothing upstream stops the export:
parse.py:208acceptspostprocessas a multi-string key.config.py:165whitelists it.- The exporter's refusals match only
cumulative,time_errorandnormalization/analytic_scale.
The earlier exporter audit behind #736 and #738 walked the structural tuple keys. postprocess is a plain list key, so the audit did not cover it.
A _reject_postprocess refusal beside _reject_normalization would close this.
Where
pybnf/petab/export.py:188-190: the prediction-transform refusals, with no postprocess counterpartpybnf/config.py:4235:_load_postprocessingpybnf/algorithms/core.py:84:Result.postprocess_data, called frompybnf/algorithms/core.py:394,pybnf/algorithms/base.py:934,pybnf/algorithms/base.py:2223andpybnf/algorithms/model_check.py:84
Related: #851, #912, #842, #898, #900, #901, #896, #894.
Found in a whole-codebase audit for silently wrong results (2026-09-23); the reproduction above was re-run independently of the original finding.
- 主要言語
- Python
- スター
- 25
- フォーク
- 25
- 平均マージ
- 2時間 38分
- マージ済み PR(30日)
- 98
環境構築
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
lanl/PyBNF のほかの issue
-
bug silent-incorrectness
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
documentation
難易度 1/5 1時間未満 初心者へのやさしさ 91/100
メンテナーはふだん 1 日以内に返信
-
bug silent-incorrectness
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
bug silent-incorrectness
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 1 日以内に返信
似ている issue
-
documentation
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
kristofdegrave/homeassistant-smart-charging#1413 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
nasa/earthdata-varinfo#113 ·
-
curriculum documentation quality
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
githubnext/gh-aw-workshop#3849 ·
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 90/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
メンテナーはふだん 1 日以内に返信