Skip to content

Rename TROP method="twostep" to method="local"#209

Merged
igerber merged 4 commits intomainfrom
refactor/trop-rename-twostep-to-local
Mar 18, 2026
Merged

Rename TROP method="twostep" to method="local"#209
igerber merged 4 commits intomainfrom
refactor/trop-rename-twostep-to-local

Conversation

@igerber
Copy link
Owner

@igerber igerber commented Mar 18, 2026

Summary

  • Rename TROP method="twostep" to method="local", forming a natural local/global pair
  • Add FutureWarning deprecation for "twostep""local" (mirrors existing "joint""global"); both removed in v3.0
  • Rename 8 internal _joint_* methods to _global_* for consistency
  • Rename Rust exports loocv_grid_search_jointloocv_grid_search_global and bootstrap_trop_variance_jointbootstrap_trop_variance_global
  • Update all tests, documentation, and CHANGELOG

Methodology references (required if estimator / math changes)

  • Method name(s): TROP (Triply Robust Panel)
  • Paper / source link(s): Athey, Imbens, Qu & Viviano (2025) https://arxiv.org/abs/2508.21536
  • Any intentional deviations from the source (and why): None — purely a naming change with no behavioral modifications

Validation

  • Tests added/updated: tests/test_trop.py (class/method renames, 2 new deprecation tests for "twostep"), tests/test_rust_backend.py (class/method renames, import/mock target updates)
  • Full non-slow suite: 1809 passed, 0 failed
  • Rust backend compiles and renamed exports verified
  • Manual verification: TROP() defaults to "local", TROP(method="twostep") emits FutureWarning and normalizes to "local", TROP(method="joint") emits FutureWarning and normalizes to "global"

Security / privacy

  • Confirm no secrets/PII in this PR: Yes

Generated with Claude Code

Rename the per-observation TROP method from "twostep" to "local",
forming a natural local/global pair. Both "twostep" and "joint" are
now deprecated aliases (removal in v3.0). Also rename internal
_joint_* methods to _global_* and Rust exports for consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link

Overall Assessment

✅ Looks good

No unmitigated P0 or P1 findings in the diff. The twosteplocal and jointglobal change appears to be nomenclature-only: the paper still defines TROP through the same multi-treated-unit estimator, LOOCV tuning criterion, and bootstrap inference flow, and this repo now documents the rename explicitly in the Methodology Registry rather than silently changing the estimator. (ar5iv.org)

Executive Summary

  • The rename is methodologically safe: it is documented in the registry and does not appear to change estimator logic, weighting, variance estimation, or identification checks.
  • Python alias handling looks complete: deprecated names are normalized in __init__ and set_params(), get_params() returns canonical names, and fit() dispatches only on canonical self.method.
  • Rust export renames were propagated through Python imports and Rust-backend tests.
  • I found two P3 informational inconsistencies only: stale joint wording in Rust comments and a stale contributor-facing description of the global estimator in a renamed test class docstring.
  • I could not execute the test suite in this environment because pytest is not installed.

Methodology

Code Quality

  • No findings.

Performance

  • No findings.

Maintainability

  • P3 Informational. Impact: the changed Rust file still documents the renamed global helpers as “joint” code paths, which leaves an avoidable translation layer between the public API and backend internals. See rust/src/trop.rs:L1087, rust/src/trop.rs:L1425, rust/src/trop.rs:L1505, and rust/src/trop.rs:L1633. Concrete fix: update those Rust doc comments now; optionally rename the remaining internal *_joint_* helpers in a later cleanup if full cross-language consistency is the goal.

Tech Debt

  • No findings requiring a new TODO.md entry.

Security

  • No findings.

Documentation/Tests

  • P3 Informational. Impact: the renamed TestTROPGlobalMethod class still describes the global estimator using the older “single scalar tau via weighted least squares” mental model, while the implementation and registry describe global TROP as a single control-only fit with post-hoc per-observation residual effects averaged to ATT. See tests/test_trop.py:L2722, diff_diff/trop.py:L1005, and docs/methodology/REGISTRY.md:L1396. Concrete fix: rewrite that class docstring to match the current global-method semantics.
  • No functional test-coverage gaps stood out in the diff itself, but I could not run tests here because pytest is unavailable in the environment.

Fix P3 items from code review:
- Replace remaining "joint"/"twostep" wording in Rust doc comments
  and section headers with "global"/"local"
- Rewrite TestTROPGlobalMethod docstring to match current global
  method semantics (single control-only fit with post-hoc residuals)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@igerber
Copy link
Owner Author

igerber commented Mar 18, 2026

/ai-review

@github-actions
Copy link

🔁 AI review rerun (requested by @igerber)

Head SHA: 1e6bcd195169ee531f3888570142b8e48b1f65de


Overall Assessment

✅ Looks good

Executive Summary

  • Re-review outcome: the PR remains a nomenclature change for TROP rather than a behavioral change. The affected methods are method="local" (formerly twostep) and method="global" (formerly joint).
  • Cross-checking the code, registry, and cited method description, I did not find an unmitigated P0/P1 issue in estimator logic, weighting, bootstrap SE, inference, or assumption enforcement.
  • The rename itself is documented in the Methodology Registry, so twosteplocal and jointglobal are not methodology defects.
  • Previous informational findings from the last AI review are resolved: the Rust comments and the global-method test-class docstring now match the residual-based global implementation.
  • One P2 methodology-documentation issue remains: the registry’s generic optimization summary still describes the global path as if it jointly estimates a scalar τ·D, which conflicts with the actual global implementation and the later registry section.
  • Verification was static only: pytest is not installed here, and importing diff_diff also fails because numpy is missing.

Methodology

Affected methods: TROP method="local" and method="global".

Code Quality

  • No findings.

Performance

  • No findings.

Maintainability

Tech Debt

  • No findings. No new TODO.md tracking appears necessary.

Security

  • No findings.

Documentation/Tests

  • No new blocking test gaps stood out in the diff. The deprecation/canonicalization paths are covered in tests/test_trop.py:2861 and tests/test_trop.py:2883.
  • Verification note: I could not run the test suite in this environment because pytest is unavailable, and I could not import the package because numpy is not installed.

…for τ

The generic TROP optimization summary incorrectly described the global
path as solving for (μ, α, β, τ) with τ·D in the residual. The actual
implementation solves for (μ, α, β, L) on control data and extracts
τ_it post-hoc as residuals. Clarify the summary and point to the
dedicated Global section for details.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@igerber
Copy link
Owner Author

igerber commented Mar 18, 2026

/ai-review

@github-actions
Copy link

🔁 AI review rerun (requested by @igerber)

Head SHA: fa25f700c3fad52a7551451e9e9cedc1f173496b


Overall Assessment

✅ Looks good

Re-review outcome: I do not see an unmitigated P0 or P1 in the renamed TROP local/global paths. The prior registry mismatch noted in the earlier AI review appears resolved.

Executive Summary

Methodology

Code Quality

  • No findings.

Performance

  • No findings.

Maintainability

  • Severity: P3. Impact: no behavioral change, but the Rust backend now mixes public global terminology with private *_joint* helper names in rust/src/trop.rs:L1103-L1452 and rust/src/trop.rs:L1741-L1756, which weakens the consistency goal of the rename and makes backend maintenance harder to follow. Concrete fix: rename the remaining private Rust helpers to *_global*, or add an explicit comment that only the exported/public surface was renamed.

Tech Debt

  • No findings. No new TODO.md tracking looks necessary.

Security

  • No findings.

Documentation/Tests

  • Severity: P3. Impact: low; tests/test_trop.py:L3277-L3296 still says it “produces same results as deprecated joint,” but the body now only checks that method="global" runs and returns a positive ATT. Concrete fix: either restore the alias comparison in the test body or rewrite the docstring to match the current assertion.
  • No other doc/test findings. Deprecated alias coverage is present in tests/test_trop.py:L2861-L2895, and the renamed Rust exports are exercised in tests/test_rust_backend.py:L1154-L1270.

- Rewrite test_global_method_alias docstring to match its actual
  assertion (validates method="global" produces valid ATT)
- Add comment in Rust explaining that only exported #[pyfunction]
  names were renamed; private helpers retain *_joint* names

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@igerber
Copy link
Owner Author

igerber commented Mar 18, 2026

/ai-review

@github-actions
Copy link

🔁 AI review rerun (requested by @igerber)

Head SHA: 528faf51ab03feb822633ca8c9daf68976acd08a


Overall Assessment

✅ Looks good

Re-review outcome: the prior low-severity issues are addressed, and I do not see an unmitigated P0/P1 in the twostep to local / joint to global rename. The diff is behavior-preserving in estimator logic; the only methodology deviation is terminology, and that deviation is explicitly documented in docs/methodology/REGISTRY.md:L1377-L1488. This review was static only because numpy, pandas, and pytest are not installed in this environment.

Executive Summary

Methodology

  • Severity: P3 informational. Impact: none. Concrete fix: none required. Renaming the public TROP method labels to local/global is a documented terminology deviation rather than a change in the estimator’s math, weighting, variance, or identification assumptions. The registry records both deprecated aliases and the rename note in docs/methodology/REGISTRY.md:L1377-L1488, and the code canonicalizes aliases without changing the underlying implementations in diff_diff/trop.py:L140-L175 and diff_diff/trop.py:L2655-L2692.
  • No additional findings. The changed estimator path still enforces the simultaneous-adoption check for global and still uses safe_inference() for all inference fields in diff_diff/trop.py:L1109-L1277.

Code Quality

  • No findings.

Performance

  • No findings.

Maintainability

Tech Debt

  • No new findings.

Security

  • No findings.

Documentation/Tests

@igerber igerber merged commit 744ae97 into main Mar 18, 2026
9 of 10 checks passed
@igerber igerber deleted the refactor/trop-rename-twostep-to-local branch March 18, 2026 20:46
@igerber igerber mentioned this pull request Mar 18, 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