Skip to content

feat(two-tier): orthogonal-primary identification (Phi fixed to I) for fit_two_tier_grm #2111

Description

@seonghobae

Gap

fit_two_tier_grm (0.11.4, e44b52d) always estimates Phi, the correlation among primary factors (theta_P ~ MVN(0, Phi)). Its signature has no option to fix Phi = I (orthogonal primaries).

#2077 adds expected_total_score_two_tier_from_fit, which is correct to fail closed unless fit.phi == I exactly and the caller confirms orthogonal_primary_identification=True. With the current fitter, a real fit has phi != I, so this path always refuses. expected_total_score_two_tier_given_primary can only be fed parameters estimated under orthogonal identification, and no fast-mlsirm fitter produces those today. A consumer whose measurement contract fixes orthogonal primaries (general + method factor + specifics) therefore has no library path to expected raw scores. A near-identity estimated Phi must not be relabelled as orthogonal.

Requested change (a complete model-design PR per AGENTS.md formula-scope rule)

  • fit_two_tier_grm(..., primary_correlation="estimate" | "identity") as a required or explicitly defaulted, documented argument. "identity" fixes Phi = I, so primaries are orthogonal and the Phi M-step is skipped.
  • Record the identification on TwoTierGrmFit (e.g. primary_identification = "orthogonal"). feat(two-tier): expected raw total score E[T|theta_focal] at a fixed primary #2077's from_fit can then read it instead of relying on a caller flag. That is a small follow-up to feat(two-tier): expected raw total score E[T|theta_focal] at a fixed primary #2077 and does not block it.
  • Likelihood and gradient: E-step uses MVN(0, I) for primaries; all item M-steps are unchanged.
  • Rust core + PyO3 + Python, NumPy reference if one exists, Rust/NumPy parity.
  • Tests:
    • reduces to the bifactor model when n_primary = 1;
    • with a data-generating Phi = I, the "identity" and "estimate" fits agree within sampling error;
    • log-likelihood is monotone under EM;
    • n_parameters drops by n_primary*(n_primary-1)/2;
    • Oakes SE (two_tier_oakes_se) respects the fixed Phi (no Phi rows in the information matrix).
  • Citations (read full texts only): Cai (2010, Psychometrika 75, 581–612) two-tier model; Cai, Yang & Hansen (2011) for the orthogonal specific-factor structure.

Why now

This is the narrowest library blocker for producing expected-raw scores for an orthogonal two-tier measurement model through the public API. It needs #2077 (curve API) plus this option in one immutable release. Research item maps stay out of the library.

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions