feat(eq): Phase L-6 4-layer tests#92
Merged
Merged
Conversation
Layer 1 equivalence (eq_iter01/eq_tst2 vs L-0, tol 1e-10, EQ_RUN_OK gated), Layer 2 C ABI test_negative (pre-init, unknown name, malformed PSIB[abc] / bare PSIB, double-finalize, post-finalize, re-init), Layer 4 3x3 RR/BB sweep with re-init per sample (mirror trlib PR #83). Adds eq_api_check_all bundling smoke + run_so + negative; appends 5 eqlib_* rows to test_definitions.conf. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Owner
Author
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 2b48c6d. Configure here.
k-yoshimi
added a commit
that referenced
this pull request
Apr 19, 2026
2026-04-19 改訂. tr/ti/wr/wrx/fp の 5 章はそのまま保持し, 新たに第 8 章 eq モジュール (6 関数 C ABI と PSIB 0-origin の 解説含む), 第 9 章 tot モジュール (名前空間プレフィックス設計) を追加. 完了モジュールを 5 → 7 に拡大. - 第 8 章 (eq): 概要, ビルド, hello-world, set_param_str (KNAMEQ), PSIB 0-origin / run() mode=1 デフォルトの FAQ, 約 60 件の パラメータ表, EqState フィールド, F90 modernization (F-1..F-5) への言及, 変更履歴 (PR #54/#65/#70/#78/#80/#86/#92/#89 と #71/#79/#81/#87/#93) - 第 9 章 (tot): 概要, ビルド, hello-world, namespace prefix ディスパッチ表, wr→wrx 別名, dict/set_param_str, 既知制約 (init/run/get_state/finalize は L-3/L-4/L-5 でスタブ), 変更履歴 (PR #43/#82/#85/#91/#94) - 状況章を更新: モジュール完了マトリクスを 7 行化, eq F90 modernization (F-1..F-5) 完了マトリクス追加, MCP サーバ群 (tr/ti/wr/wrx/fp + tot 進行中, 6 個) 追加 - 残作業: trlib のみ実装の plot/TOML runner 横展開 (deferred), チュートリアル Notebook 集 (deferred) - 付録 PR 一覧を 2026-04-19 (#96 まで) に最新化, eq/tot/MCP 追記 - 補足カタログに eq/tot の最短例 2 つを追加 - xelatex 2 回コンパイル成功, PDF 55 ページ (旧版から +拡張) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
k-yoshimi
added a commit
that referenced
this pull request
Apr 20, 2026
Mirror of the PR #121 (tr) / #123 (wrx) / #128 (fp) heap-reuse pattern for the eq library path. In libeqapi.so, finalize+reinit across the Python wrapper lifecycle can hand glibc malloc a previously-freed heap chunk whose bytes are the prior run's output; a fresh eq binary launch sees a kernel-zeroed heap instead. Unlike wrx / tr, eq stores its working state in COMMON blocks (via eqcom{0..3}_mod.f90) rather than MODULE-level allocatables, so the sweep here lands at every LOCAL ALLOCATE in SRCS_CORE subroutines that are on the eq_api_init -> eq_init path. Zero-init sites (all ALLOCATEs in library-scope subroutines, grouped immediately after the last ALLOCATE with a short PR #123-pattern comment): * eqsub.f90: EQAXIS, setup_psig -> PSIRG/PSIZG/PSIRZG * equread.f90: alloc_equ(mode=1) -> rg/zg/psi/rbp/pds/fds/vlv/qqv/ prv/csu/rsu/zsu/hiv/siv/siw/sdw/ ckv/ssv/aav/rrv/rbv/arv/bbv/biv/ r2b2v/shv/grbm2v/rov/aiv/brv/ epsv/elipv/trigv/ftv alloc_equ(mode=2) -> ieqout/ieqerr/icp/cp/ivac/ncoil/ cvac/rvac/zvac/rcoil/zcoil/ccoil/ rlimt/zlimt eqcq -> bmax/fint/flam/nsul/dll/zbl * eqintf.f90: GETRSU -> RSU1/ZSU1 (INTENT(OUT) args) * eqcalc.f90: EQSOLV -> FJT/PSIOLD EQSETF -> PSISX/PSITX/PSISTX * eqcalq.f90: EQCALQV -> PSIRG/PSIZG/PSIRZG/HJTRG/HJTZG/HJTRZG (vacuum-fill body) -> URPSW/UZPSW (chi-spline body) -> D01/D10/D11 * eq-qst.f90: EQJAEAR -> psi_temp, rc_xp/zc_xp/psic_xp Deliberately skipped for this pass: * eqbpsd.f90 allocates of equ1D%rho / equ1D%data / metric1D%rho / metric1D%data: (a) these live inside derived-type bpsd_equ1D_type members that were actively modified in the last five develop commits (784f96b, 0ce1777, 7df60e5, d5c4e38, faf9157) as part of Phase L-1 baseline restoration; touching them risks conflicts with the ongoing eqlib extraction. (b) The %rho plain-real arrays are fully rewritten in the immediately-following DO nr=1,nrmax loop, and the %data array-of-derived-type members are each assigned field-by-field in the same loop, so the heap-reuse window is minimal. Leave for the follow-up PR that retires the EQ_RUN_OK gate. * eq/*.f (fixed-form, e.g. eqcalx.f) and eq/eqgout.f90 / eq/eqmenu.f90: not part of libeqapi.so (SRCS_GRAPHICS / SRCS_MENU only; graphics paths are stubbed in eq_graphics_stubs.f90). * eqg2d.f / eqg3d.f: graphics-only, not in libeqapi.so. Verification: * `make -C eq libeqapi.so` builds clean (gfortran 13.x, -std=legacy, -fbounds-check -ffpe-trap=invalid,zero,overflow). * `python3 -m pytest python/eqlib/tests/` -> 28 passed, 3 skipped. The 3 skips (test_eq_iter01, test_eq_tst2, test_3x3_grid_completes) are pre-existing EQ_RUN_OK=1-gated Layer 1 equivalence tests that remain SKIPPED upstream on origin/develop; this PR does not change their status. Addressing those skips is the scope of the L-6 follow-up after the baseline regeneration in PR #92 stabilises. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
k-yoshimi
added a commit
that referenced
this pull request
Apr 20, 2026
* fix(eq): defensive zero-init sweep (libeqapi heap-reuse) Mirror of the PR #121 (tr) / #123 (wrx) / #128 (fp) heap-reuse pattern for the eq library path. In libeqapi.so, finalize+reinit across the Python wrapper lifecycle can hand glibc malloc a previously-freed heap chunk whose bytes are the prior run's output; a fresh eq binary launch sees a kernel-zeroed heap instead. Unlike wrx / tr, eq stores its working state in COMMON blocks (via eqcom{0..3}_mod.f90) rather than MODULE-level allocatables, so the sweep here lands at every LOCAL ALLOCATE in SRCS_CORE subroutines that are on the eq_api_init -> eq_init path. Zero-init sites (all ALLOCATEs in library-scope subroutines, grouped immediately after the last ALLOCATE with a short PR #123-pattern comment): * eqsub.f90: EQAXIS, setup_psig -> PSIRG/PSIZG/PSIRZG * equread.f90: alloc_equ(mode=1) -> rg/zg/psi/rbp/pds/fds/vlv/qqv/ prv/csu/rsu/zsu/hiv/siv/siw/sdw/ ckv/ssv/aav/rrv/rbv/arv/bbv/biv/ r2b2v/shv/grbm2v/rov/aiv/brv/ epsv/elipv/trigv/ftv alloc_equ(mode=2) -> ieqout/ieqerr/icp/cp/ivac/ncoil/ cvac/rvac/zvac/rcoil/zcoil/ccoil/ rlimt/zlimt eqcq -> bmax/fint/flam/nsul/dll/zbl * eqintf.f90: GETRSU -> RSU1/ZSU1 (INTENT(OUT) args) * eqcalc.f90: EQSOLV -> FJT/PSIOLD EQSETF -> PSISX/PSITX/PSISTX * eqcalq.f90: EQCALQV -> PSIRG/PSIZG/PSIRZG/HJTRG/HJTZG/HJTRZG (vacuum-fill body) -> URPSW/UZPSW (chi-spline body) -> D01/D10/D11 * eq-qst.f90: EQJAEAR -> psi_temp, rc_xp/zc_xp/psic_xp Deliberately skipped for this pass: * eqbpsd.f90 allocates of equ1D%rho / equ1D%data / metric1D%rho / metric1D%data: (a) these live inside derived-type bpsd_equ1D_type members that were actively modified in the last five develop commits (784f96b, 0ce1777, 7df60e5, d5c4e38, faf9157) as part of Phase L-1 baseline restoration; touching them risks conflicts with the ongoing eqlib extraction. (b) The %rho plain-real arrays are fully rewritten in the immediately-following DO nr=1,nrmax loop, and the %data array-of-derived-type members are each assigned field-by-field in the same loop, so the heap-reuse window is minimal. Leave for the follow-up PR that retires the EQ_RUN_OK gate. * eq/*.f (fixed-form, e.g. eqcalx.f) and eq/eqgout.f90 / eq/eqmenu.f90: not part of libeqapi.so (SRCS_GRAPHICS / SRCS_MENU only; graphics paths are stubbed in eq_graphics_stubs.f90). * eqg2d.f / eqg3d.f: graphics-only, not in libeqapi.so. Verification: * `make -C eq libeqapi.so` builds clean (gfortran 13.x, -std=legacy, -fbounds-check -ffpe-trap=invalid,zero,overflow). * `python3 -m pytest python/eqlib/tests/` -> 28 passed, 3 skipped. The 3 skips (test_eq_iter01, test_eq_tst2, test_3x3_grid_completes) are pre-existing EQ_RUN_OK=1-gated Layer 1 equivalence tests that remain SKIPPED upstream on origin/develop; this PR does not change their status. Addressing those skips is the scope of the L-6 follow-up after the baseline regeneration in PR #92 stabilises. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(eq): extend sweep to eqbpsd.f90 (eq_bpsd_init %rho zero-init) Pre-review flagged eqbpsd.f90 as having a heap-reuse gap. The four allocates there were initially skipped on derived-type- complexity grounds, but equ1D%rho and metric1D%rho are simple REAL(rkind) axes — trivially zero-initializable. Zero-init them inside the same `if(%.nrmax.ne.nrmax)` reallocate guard. %data is a 1D array of derived-type structs; all fields are populated by the subsequent eq_bpsd_put loop so the heap-reuse window is minimal. Left untouched to avoid derived-type zero-init awkwardness. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
eq_iter01/eq_tst2fixtures (SCALARS/ARRAYS/STRINGS/UNREGISTERED_KEYS/BASELINE_NAME/MODE) replayed vs L-0 baselines viacompare_metrics.pyat tol1e-10, gated onlibeqapi.sovia_ffi._candidate_paths()and onEQ_RUN_OK=1(L-5 EqState schema overlap with L-0 baseline schema is L-7+ follow-up).eq/tests/c_abi/test_negative.ccovers pre-init / unknown name / malformedPSIB[abc]/ barePSIBrejection / double-finalize / post-finalize lockout / re-init.mode=1, freshEq()context per sample (mirrors trlib PR feat(trlib): plot API + TOML config runner (reference) #83 re-init-per-sample fix); assertsnrgmax/nzgmax/npsmaxinvariance.eq/Makefile: neweq_api_check_alltarget bundling existingeq_api_check(smoke + param dispatch) +eq_api_check_so(run_so) + new test_negative; clean / clean_pic updated.test_run/test_definitions.conf: appends 5eqlib_*rows (c_abi, wrapper, ffi, equivalence, sweep) after thewrxlib_*block.Test plan
python3 -m unittest discover python/eqlib/tests -v-> 31 tests, pass-or-skip clean (skipped tests gated on libeqapi.so / EQ_RUN_OK)gcc -Wall -Wextra -I.. -c eq/tests/c_abi/test_negative.c-> clean (no warnings)bash -n test_run/run_tests.sh-> exit 0./test_run/run_tests.sh -l | grep eqlib_-> 5 rowsmake -n -C eq eq_api_check_all-> dry-run shows smoke + run_so + negative invocations./run_tests.sh eqlib_*after merging (requires PIC build + EQ_RUN_OK schema fix)Constraints honored
eq/Fortran sources orpython/eqlib/core wrapper code (only test additions).@cursor review
Generated with Claude Code
Note
Low Risk
Low risk: changes are limited to new test drivers/fixtures and Makefile targets, with no modifications to EQ runtime/Fortran sources. Main risk is CI/build breakage from the new
eq_api_check_alltarget and added test definitions.Overview
Adds Phase L-6 test coverage for the EQ C ABI and Python wrapper.
The EQ Makefile now builds a new
test_negativeC driver and exposeseq_api_check_allto run the full C ABI suite (smoke + dlopen run + negative lifecycle/validation checks), andcleantargets remove the new artifacts. Python adds fixture modules foreq_iter01/eq_tst2, an opt-in Layer 1 equivalence test (gated onlibeqapi.soandEQ_RUN_OK=1), and a Layer 4 3x3(RR, BB)sweep smoke test to detect state leakage.test_run/test_definitions.confregisters five neweqlib_*entries so the central test runner can invoke the EQ library C ABI, wrapper, FFI, equivalence, and sweep tests.Reviewed by Cursor Bugbot for commit 2b48c6d. Bugbot is set up for automated code reviews on this repo. Configure here.