Skip to content

feat(eq): Phase L-6 4-layer tests#92

Merged
k-yoshimi merged 1 commit into
developfrom
feature/eq-library-L6-tests
Apr 19, 2026
Merged

feat(eq): Phase L-6 4-layer tests#92
k-yoshimi merged 1 commit into
developfrom
feature/eq-library-L6-tests

Conversation

@k-yoshimi
Copy link
Copy Markdown
Owner

@k-yoshimi k-yoshimi commented Apr 19, 2026

Summary

  • Layer 1 equivalence: eq_iter01 / eq_tst2 fixtures (SCALARS/ARRAYS/STRINGS/UNREGISTERED_KEYS/BASELINE_NAME/MODE) replayed vs L-0 baselines via compare_metrics.py at tol 1e-10, gated on libeqapi.so via _ffi._candidate_paths() and on EQ_RUN_OK=1 (L-5 EqState schema overlap with L-0 baseline schema is L-7+ follow-up).
  • Layer 2 C ABI: new eq/tests/c_abi/test_negative.c covers pre-init / unknown name / malformed PSIB[abc] / bare PSIB rejection / double-finalize / post-finalize lockout / re-init.
  • Layer 4 sweep: 3x3 RR x BB grid, mode=1, fresh Eq() context per sample (mirrors trlib PR feat(trlib): plot API + TOML config runner (reference) #83 re-init-per-sample fix); asserts nrgmax/nzgmax/npsmax invariance.
  • eq/Makefile: new eq_api_check_all target bundling existing eq_api_check (smoke + param dispatch) + eq_api_check_so (run_so) + new test_negative; clean / clean_pic updated.
  • test_run/test_definitions.conf: appends 5 eqlib_* rows (c_abi, wrapper, ffi, equivalence, sweep) after the wrxlib_* 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 rows
  • make -n -C eq eq_api_check_all -> dry-run shows smoke + run_so + negative invocations
  • Full integration ./run_tests.sh eqlib_* after merging (requires PIC build + EQ_RUN_OK schema fix)

Constraints honored

  • No edits to eq/ Fortran sources or python/eqlib/ core wrapper code (only test additions).
  • Mirrors tr / fp / wrx L-6 patterns exactly.

@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_all target 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_negative C driver and exposes eq_api_check_all to run the full C ABI suite (smoke + dlopen run + negative lifecycle/validation checks), and clean targets remove the new artifacts. Python adds fixture modules for eq_iter01/eq_tst2, an opt-in Layer 1 equivalence test (gated on libeqapi.so and EQ_RUN_OK=1), and a Layer 4 3x3 (RR, BB) sweep smoke test to detect state leakage.

test_run/test_definitions.conf registers five new eqlib_* 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.

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>
@k-yoshimi
Copy link
Copy Markdown
Owner Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 k-yoshimi merged commit 7bbafe3 into develop Apr 19, 2026
1 check passed
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>
@k-yoshimi k-yoshimi deleted the feature/eq-library-L6-tests branch April 20, 2026 04:07
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant