Skip to content

feat: Owen-approximate posterior-predictive EPV item selection (van der Linden 1998, reduced scope) - #255

Closed
seonghobae wants to merge 2 commits into
seonghobae-ccatfrom
seonghobae-epv
Closed

feat: Owen-approximate posterior-predictive EPV item selection (van der Linden 1998, reduced scope)#255
seonghobae wants to merge 2 commits into
seonghobae-ccatfrom
seonghobae-epv

Conversation

@seonghobae

@seonghobae seonghobae commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Iteration 24 of the autonomous paper-implementation loop. Stacked on #254 (seonghobae-ccat). ## What Owen-approximate posterior-predictive EPV item selectionfast_mlsirm.epv_select, Rust core mlsirm_core::exposure::epv_select (+ PyO3 py_epv_select). For each unadministered item under the Owen normal posterior N(mu, sig2): - predictive success probability p*_i = c_i + (1 - c_i) Phi((mu - b_i) / sqrt(1/a_i^2 + sig2)) (identical d to owen_update) - EPV_i = p*_i sig2_i^+ + (1 - p*_i) sig2_i^- with both outcome variances from owen_update - select argmin over unadministered items; lowest-index ties; epv/predictive returned for the whole pool. ## Adversarial spec-verify (BEFORE implementing): REDUCED-SCOPE The reviewer confirmed the posterior-predictive formula is mathematically correct but ruled that van der Linden (1998), catR EPV.R, and mirtCAT define MEPV with point probabilities P_i(mu) and exact numerical posterior variances, not posterior-predictive weights + Owen updates. Accepted contract: an explicitly labeled "Owen-approximate posterior-predictive EPV" with prescribed doc wording (used verbatim in code, docstring, and CHANGELOG). This is deliberately NOT exact vdL-98 MEPV. Source status: van der Linden (1998) READ as ERIC ED424235 (Research Report 96-01); catR EPV.R and mirtCAT selection_criteria.R READ; Owen (1975) and the Psychometrika body NOT read (documented in code). ## Evidence - Pinned crate-parity oracles (spec-review exact arithmetic, 17 digits, 5e-7 abs tol) for both predictive and epv over a 5-item pool; argmin = item 1. - Delegation discriminator (spec-review pool): argmin EPV = 2 while max-Fisher-info = 4 and Owen b-matching = 1 — a delegating mutant cannot satisfy all three. - Administered masking: scoring covers the whole pool; masking affects selection only (runner-up promotion pinned, vectors unchanged to 1e-15). - Error paths: empty pool, length mismatch, nonpositive a, non-finite b/c/mu, c out of [0,1), sig2 <= 0, all-administered, degenerate Owen posterior propagation. - MC-500 #[ignore] invariant suite: predictive finite in [c_i, 1] (closed — erfc saturation), EPV finite > 0, selected always unadministered, ties lowest-index. (Strict EPV < sig2 deliberately NOT asserted: single-outcome Owen variance can genuinely increase and roundoff inflation of 8.9e-16 was observed.) - 4 executed mutation kills (temp-file backup/restore, re-run verified green): - M1 argmin -> argmax: 3 tests fail - M2 drop guessing floor (p* = Phi(d)): 2 fail - M3 swap sig2+/sig2- weights: 2 fail - M5 posterior-predictive -> point probability P_i(mu): 2 fail - Python wrapper validates arrays BEFORE lossy casts; 4 pytest parity tests green. cargo test -p mlsirm-core --lib -- epv: 4 passed (+1 ignored MC-500 passed with --ignored). pytest -k EpvSelect: 4 passed. ## References - van der Linden, W. J. (1998). Bayesian item selection criteria for adaptive testing. Psychometrika, 63(2), 201-216. https://doi.org/10.1007/BF02294775 - Owen, R. J. (1975). A Bayesian sequential procedure for quantal response in the context of adaptive mental testing. JASA, 70(350), 351-356. - Magis, D., & Raiche, G. (2012). catR. JSS, 48(8). https://doi.org/10.18637/jss.v048.i08

Adversarial impl-review (AFTER implementing): CLEAN after fixes (b2a711a)

  • CONFIRMED-DEFECT: complex a/b/c were laundered through the float64 cast (imaginary parts silently dropped). Fixed: np.iscomplexobj rejection before any cast + 3 regression error-path tests.
  • DOC-DEFECT: research-report citation said 98-01; the read ERIC ED424235 source is Research Report 96-01. Fixed.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head b2a711a40544ea21465e028a1e233ed0eaa4713c.

  • Head SHA: b2a711a40544ea21465e028a1e233ed0eaa4713c

  • Workflow run: 30159258014

  • Workflow attempt: 1

Coverage evidence

Coverage Decision

  • Result: FAIL
  • Test evidence: not proven passing
  • Docstring evidence: not proven passing when configured
  • Failure count: 2

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Changed file (5 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (5 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Test (2 files)"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test (2 files)"]
  R2 --> V2["targeted test run"]
Loading

@opencode-agent

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

  • Head SHA: b2a711a40544ea21465e028a1e233ed0eaa4713c
  • Workflow run: 30159258014
  • Workflow attempt: 1
  • Gate result: REQUEST_CHANGES (approval step)

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head b2a711a40544ea21465e028a1e233ed0eaa4713c.

  • Head SHA: b2a711a40544ea21465e028a1e233ed0eaa4713c

  • Workflow run: 30159258014

  • Workflow attempt: 1

Coverage evidence

Coverage Decision

  • Result: FAIL
  • Test evidence: not proven passing
  • Docstring evidence: not proven passing when configured
  • Failure count: 2

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Changed file (5 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (5 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Test (2 files)"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test (2 files)"]
  R2 --> V2["targeted test run"]
Loading

seonghobae and others added 2 commits July 25, 2026 23:23
…er Linden, 1998; reduced scope)

Adds epv_select to mlsirm_core::exposure: for each unadministered item,
the predictive success probability p* = c + (1-c) Phi((mu-b)/sqrt(1/a^2+sig2))
weights the two Owen-update outcome posterior variances,
EPV = p* sig2+ + (1-p*) sig2-, and the argmin (lowest-index ties) is
selected. Explicitly NOT vdL-98 exact MEPV (which uses point
probabilities and exact numerical posteriors); the reduced scope is
documented in code, docstring, and CHANGELOG per the adversarial spec
review. PyO3 binding py_epv_select + Python wrapper fast_mlsirm.epv_select
(arrays validated before lossy casts).

Tests: pinned crate-parity oracles (5-item pool, 17-digit spec-review
values, 5e-7 abs), delegation discriminator (argmin EPV=2 vs max-info=4
vs b-matching=1), administered-masking invariance, error paths, and a
500-rep Monte-Carlo invariant suite (#[ignore]). Mutation kills executed:
argmin->argmax (3 fail), drop guessing floor (2 fail), swap sig2+/- weights
(2 fail), posterior-predictive->point probability (2 fail).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…citation

Adversarial impl-review findings: (1) complex inputs were laundered
through the float64 cast, silently discarding imaginary parts — now
rejected with ValueError before any cast, regression-tested; (2) the
ERIC ED424235 research-report citation said 98-01 but the read source
is Research Report 96-01.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.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