Skip to content

refactor(cat): move ability estimation arithmetic to Rust - #628

Closed
seonghobae wants to merge 1 commit into
mainfrom
feat/rust-cat-ability-scoring
Closed

refactor(cat): move ability estimation arithmetic to Rust#628
seonghobae wants to merge 1 commit into
mainfrom
feat/rust-cat-ability-scoring

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Buyer-visible architecture gap

Issue #624 identifies a production numerical-ownership violation: the public CAT MLE, EAP, and ability-standard-error paths still compute psychometric estimates and uncertainty in Python/NumPy. This Draft begins the bounded test-first migration to one Rust/PyO3 numerical authority without changing the public CAT semantics.

Exact fail-first RED evidence

Fail-first source head: 5f8ab14d8e2f83c9caaac9d64e906fd5e9111074
Protected main used by the run: f43a466b1441b27bd2ac4a71be8aa3e4f08c8e8f
Synthetic merge checked out by CI: 55b53d8be097fee6e0283d4658584470c87fd4f4
CI run: 31351541489; Python job: 93343213036.

The full suite reached the intended production boundary and completed with exactly 3 failed, 2948 passed, 2 skipped. The only failures were:

  • test_public_mle_delegates_numerical_result_to_rust;
  • test_public_eap_delegates_numerical_result_to_rust; and
  • test_public_standard_error_delegates_numerical_result_to_rust.

Each fails because the proposed Rust entrypoint receives zero calls (assert len(calls) == 1, actual 0), proving the current public Python CAT layer still owns the numerical result. The existing CAT behavioral suite is GREEN. Package/reinstall/release-acceptance and explicit GPU-no-skip jobs are GREEN on the same fail-first integration; SAST Semgrep is GREEN. The separately tracked marginal.py::_log_sigmoid RuntimeWarning belongs to #583/#564 and is not a #628 defect.

Planned minimum GREEN

Keep Python responsible for bounded validation, immutable marshalling, and AbilityEstimate result construction only. Move the existing CAT arithmetic into mlsirm-core and expose thin PyO3 entrypoints named cat_ability_mle, cat_ability_eap, and cat_ability_standard_error.

The Rust implementation must preserve the current public semantics exactly:

  • block-separable simple-structure MLE Newton/Fisher updates, max_iter, tol, and [-bound, bound] clipping;
  • all-identical response dimensions remain finite=False with infinite MLE SE;
  • fixed-grid EAP over the current linspace(-quad_range, quad_range, n_quad) + prior_mean contract and population-mean bank latent-space position;
  • no-administered-item EAP returns the prior mean/SD;
  • asymptotic ability SE is 1/sqrt(sum item information) by trait dimension;
  • current model identities and MIRT/latent-space/bifactor predictor semantics remain unchanged;
  • input arrays are immutable from the caller's perspective; and
  • missing/invalid compiled-core capability does not silently select an independent Python numerical implementation.

Reuse existing Rust scoring/bank-information primitives where they preserve the exact public CAT estimand. Do not widen this PR into item-selection/test-assembly ownership, directional multidimensional CAT, a new estimator, GPU work, hosted state, providers, or a new canonical documentation line. This per-administration kernel remains CPU-f64 unless a separately reviewed benchmark demonstrates material GPU value.

Scientific basis and validation

Preserve the current CAT contract grounded in Bock & Mislevy (1982), Lord (1980), and van der Linden & Pashley (2010). This migration changes implementation ownership, not the estimand or interpretation. Add direct Python↔Rust field parity over ordinary, multidimensional simple-structure, no-item/prior, all-correct, all-incorrect, extreme-logit and malformed cases, plus realistic true-theta recovery using bias/MAE/RMSE, finite/convergence behavior, and SE/interval calibration where identified. Correlation alone is insufficient.

Remaining Draft gate

  • implement the smallest Rust core + PyO3 + thin-Python delegation path;
  • focused ownership/parity/recovery tests GREEN after the preserved fail-first history;
  • exact changed production statement/branch coverage and complete rustdoc/docstrings;
  • full Python, Rust/PyO3, package/reinstall, GPU-existing, fuzz, Security Scan and SAST evidence on one unchanged head;
  • canonical Architecture/TRD/ADR/traceability updates belong in the sole documentation line docs: establish canonical product and architecture baseline #604 (or its reviewed successor), not a parallel architecture PR;
  • authoritative changelog fragment and deterministic render/check before Ready;
  • current-head automated review and zero valid unresolved findings; and
  • repository approval/branch-protection policy satisfied on the unchanged head.

Keep Draft until all gates are complete. Advances #624.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b1bd573-c092-449c-b80b-d59277a68492

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Contributor Author

@opencode-agent address

Exact bounded implementation handoff for issue #624 / Draft #628. Before any write, refetch the PR source head and independently resolve protected main; proceed only if source head is still 5f8ab14d8e2f83c9caaac9d64e906fd5e9111074 and live main is still compatible. Preserve the fail-first commit/history; no force push, destructive rebase, temporary writer workflow, or competing documentation branch.

Fail-first proof is complete: CI run 31351541489, Python job 93343213036, synthetic merge 55b53d8be097fee6e0283d4658584470c87fd4f4 completed 3 failed, 2948 passed, 2 skipped. The ONLY failures are the three new CAT ownership tests, each because the package-owned Rust entrypoint was called zero times. Existing CAT behavior is green. Package/reinstall/release acceptance, explicit GPU-no-skip, and SAST are green on the fail-first integration. The unrelated _log_sigmoid RuntimeWarning is #583/#564 and must not be changed here.

Implement the minimum GREEN only:

  1. In mlsirm-core (prefer the existing scoring.rs/ItemBank/PriorSpec boundary where appropriate), add CPU-f64 Rust numerical ownership for the current public CAT MLE, fixed-grid EAP, and information-based ability SE. Preserve exact current semantics: simple-structure per-dimension MLE Newton/Fisher updates, max_iter/tol/bound clipping, all-identical finite flag and infinite MLE SE, current population-mean xi convention, the exact fixed linspace EAP grid contract, no-item prior result, and current model predictor identities including latent-space/bifactor behavior.
  2. Expose thin PyO3 wrappers registered exactly as _core.cat_ability_mle, _core.cat_ability_eap, _core.cat_ability_standard_error; marshal immutable arrays only and normalize Rust errors without reflecting caller-controlled content.
  3. Make python/fast_mlsirm/cat.py validate/marshal/construct AbilityEstimate only. Remove independent Python MLE/EAP/SE numerical implementation rather than retaining it as fallback. Missing/incompatible core capability must fail closed rather than silently recompute psychometric arithmetic in Python.
  4. Add focused Rust unit tests and Python direct field parity/immutability/malformed/extreme/no-item/multidimensional tests. Preserve and run existing CAT true-theta recovery, then add/strengthen realistic bias/MAE/RMSE and SE/coverage evidence where identified; correlation alone is not acceptance.
  5. Do NOT widen to item selection, test_design.py, ATA, directional multidimensional CAT, a new estimator, provider/LLM code, hosted persistence, or GPU. Per-administration CAT scoring stays CPU-f64 unless a later benchmark-backed PR proves GPU benefit.
  6. Add current primary-source APA 7 doctoring for Bock & Mislevy (1982), Lord (1980), and van der Linden & Pashley (2010) as applicable, plus an authoritative docs/changelog.d fragment; render/check managed CHANGELOG.md only after the feature is green.
  7. Cross-cutting Architecture/TRD/ADR/UML/ERD/traceability changes belong in canonical docs PR docs: establish canonical product and architecture baseline #604 (or its reviewed successor), not this branch. If docs: establish canonical product and architecture baseline #604 cannot be safely updated under its writer lease, leave an exact traceability handoff instead of creating parallel authority.
  8. Before declaring Ready, run focused tests and full relevant Python/Rust/PyO3/package/reinstall/GPU-existing/fuzz/security/SAST gates on one unchanged head, prove changed production statement/branch coverage and public rustdoc/docstrings, update the PR body with exact evidence, and keep Draft while any gate/finding remains.

If source or main moves, stop writing, reconcile fresh state non-destructively, and preserve the fail-first evidence as predecessor history rather than overwriting it.

Copy link
Copy Markdown
Contributor Author

@opencode-agent address

Superseding bounded implementation handoff after protected main advanced. Fresh identities immediately before dispatch: Draft #628 exact head 5f8ab14d8e2f83c9caaac9d64e906fd5e9111074; live protected main dccd1f281ec95216b3fcfd682d872c3ace74c68b; compare = diverged, ahead 1 / behind 1, with the only unique PR path tests/test_cat_rust_ownership.py. Prior handoff 5235455064 required stopping if main moved, so it is predecessor evidence. Final-refetch head/base/comments before writing and abort if either ref moves or another current-head source writer appears.

Preserve the exact fail-first proof from predecessor integration: the three CAT ownership tests fail because Rust entrypoints are called zero times; existing CAT behavior is otherwise green. First reconcile current protected main non-destructively, preserving accepted #630 CodeQL analyze pin and the fail-first test. Then implement the same minimum GREEN from issue #624: Rust CPU-f64 ownership for current public CAT MLE, fixed-grid EAP and ability SE; thin PyO3 _core.cat_ability_mle, _core.cat_ability_eap, _core.cat_ability_standard_error; Python validation/marshalling/AbilityEstimate construction only with no silent numerical fallback; focused Rust/Python parity, immutability, malformed/extreme/no-item/multidimensional and true-theta bias/MAE/RMSE/SE-coverage evidence; complete rustdoc/docstrings/owned coverage; primary APA 7 doctoring and authoritative changelog fragment/render/check.

Do not widen into item selection/ATA/directional CAT/GPU/provider/hosted persistence or canonical docs #604. Keep Draft. Stop after one coherent verified source update; fresh exact-head CI/Security/SAST/review belongs to the maintainer loop.

Copy link
Copy Markdown
Contributor Author

@opencode-agent address

Superseding CAT Rust-ownership handoff after #631 merged. Fresh pre-dispatch identities: Draft #628 head 5f8ab14d8e2f83c9caaac9d64e906fd5e9111074; protected main a7be9eb8ca0b807364173f8471fc300e3cf9350f. Prior handoffs required stopping when main moved and are predecessor evidence. Refetch head/main/comments before writing and abort if either moves or another current-head source writer appears.

Preserve the exact fail-first evidence: the three public CAT ownership tests reach production and fail only because Rust entrypoints are called zero times. First reconcile current protected main non-destructively, preserving #630/#631 CodeQL analyze/init 4.37.6 pins and the fail-first test. Then implement issue #624's minimum GREEN only: Rust CPU-f64 ownership of current MLE, fixed-grid EAP and ability SE semantics; thin PyO3 _core.cat_ability_mle, _core.cat_ability_eap, _core.cat_ability_standard_error; Python validation/marshalling/AbilityEstimate construction only, no silent numerical fallback; direct Rust↔Python parity, immutability, malformed/extreme/no-item/multidimensional and true-theta bias/MAE/RMSE/SE-coverage evidence; complete rustdoc/docstrings and owned coverage; primary APA 7 doctoring plus authoritative changelog fragment/render/check.

Keep Draft. Do not widen to CAT item selection/ATA/directional CAT/GPU/provider/persistence/canonical docs #604/dependencies/workflows/version/release/Ready/approval/merge. Stop source writes after one coherent verified implementation update; fresh exact-head full CI/Security/SAST/review belongs to the maintainer loop.

Copy link
Copy Markdown
Contributor Author

@opencode-agent address

Superseding current-main implementation handoff. The earlier handoff 5235455064 was bound to protected main f43a466b...; live protected main is now a7be9eb8ca0b807364173f8471fc300e3cf9350f, so that older writer instruction is stale by its own abort condition. Before writing, refetch exact PR head, live main, comments/reviews and affected blobs; proceed only if source is still 5f8ab14d8e2f83c9caaac9d64e906fd5e9111074, live main is still a7be9eb8..., and no newer source writer appears.

Fresh compare is diverged, ahead 1 / behind 2, merge base f43a466b1441b27bd2ac4a71be8aa3e4f08c8e8f; the branch-unique diff remains only fail-first tests/test_cat_rust_ownership.py. Reconcile current protected main non-destructively first and preserve both integrated supply-chain commits. The original exact RED remains predecessor scientific evidence (CI 31351541489, Python 93343213036: exactly 3 CAT ownership failures and all pre-existing CAT behavior green), but all acceptance/check evidence must be recreated on the reconciled final head.

Then implement only #624's bounded minimum GREEN: Rust CPU-f64 ownership of public CAT MLE, fixed-grid EAP and information-based ability SE; thin PyO3 entrypoints _core.cat_ability_mle, _core.cat_ability_eap, _core.cat_ability_standard_error; Python validation/marshalling/result construction only; missing/incompatible core fails closed. Preserve exact existing MIRT/latent-space/bifactor predictor semantics, simple-structure update/bound/tolerance behavior, all-identical finite/infinite-SE semantics, population-mean xi convention, exact EAP grid, no-item prior result and caller immutability. Add direct Rust/Python parity, malformed/extreme/no-item/multidimensional tests and realistic true-theta bias/MAE/RMSE plus uncertainty/coverage evidence where identified. No item-selection/test-assembly (#629), GPU, provider, hosted state, new estimator, workflow/dependency/version/release or parallel canonical docs.

Add APA 7 doctoring/changelog, render/check managed CHANGELOG after GREEN, and require focused plus complete Python/Rust/PyO3/package/GPU-existing/fuzz/Security/SAST/coverage/docstring evidence on one unchanged head. Keep Draft; current-head review/Ready/merge returns to the maintainer loop. Stop immediately if source/main/writer identity changes.

Copy link
Copy Markdown
Contributor Author

@jules address

Fallback sole branch writer for exact Draft #628 head 5f8ab14d8e2f83c9caaac9d64e906fd5e9111074 over live protected main a7be9eb8ca0b807364173f8471fc300e3cf9350f. Fresh compare remains diverged, ahead 1 / behind 2, merge base f43a466b1441b27bd2ac4a71be8aa3e4f08c8e8f; the branch-unique diff remains only the fail-first CAT Rust-ownership tests. The latest exact-base OpenCode handoff 5235995316 has no receipt/reaction and has produced no source movement for more than six hours. Treat it as inactive for this bounded fallback. Immediately refetch head/main/comments before writing and abort if either ref moves or any other current writer appears.

Preserve the exact production-boundary RED: public CAT MLE, fixed-grid EAP, and ability-SE ownership tests fail only because package-owned Rust entrypoints receive zero calls; existing CAT behavior is predecessor-GREEN evidence. Reconcile the two accepted CodeQL 4.37.6 protected-main commits non-destructively, then implement issue #624's minimum GREEN only: CPU-f64 Rust ownership of the existing MLE/EAP/SE estimands, thin PyO3 _core.cat_ability_mle, _core.cat_ability_eap, _core.cat_ability_standard_error, and Python validation/marshalling/AbilityEstimate construction only with fail-closed missing/incompatible core. Preserve current simple-structure update/bound/tolerance behavior, all-identical finite/infinite-SE semantics, population-mean xi, exact EAP grid/no-item prior behavior, MIRT/latent-space/bifactor predictor semantics and caller immutability. Add direct Rust↔Python field parity, malformed/extreme/no-item/multidimensional tests plus realistic true-theta bias/MAE/RMSE and uncertainty/coverage evidence where identified; complete rustdoc/docstrings/owned coverage and primary APA 7 doctoring; render/check managed CHANGELOG through the repository renderer after GREEN.

Keep Draft and stop source writes after one coherent verified update. Do not widen into item selection/test assembly (#629), directional CAT, GPU, provider/hosted state, dependencies/workflows/version/release, canonical docs #604, Ready/approval/merge, or create another PR. Fresh unchanged-head full CI/Security/SAST/current-head review belongs to the maintainer loop.

Copy link
Copy Markdown
Contributor Author

Superseded by merged PR #709. Fresh protected-main evidence now contains merge commit de7483a69bfed9578d2b86e8d1055c90427609e2, and #709 explicitly restores the #628 ownership regressions while moving CAT MLE/EAP/ability-SE arithmetic into Rust/PyO3. The old #628 writer handoff was bound to predecessor main and cannot authorize further writes after base movement. Closing this stale merge path; preserve #628 as fail-first lineage only.

@seonghobae seonghobae closed this Aug 11, 2026
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