Skip to content

feat(identification): combined zero-and-sign restrictions (ARW construction) - #218

Merged
thomaspinder merged 5 commits into
mainfrom
feat/144-zero-sign-restrictions
Jul 29, 2026
Merged

feat(identification): combined zero-and-sign restrictions (ARW construction)#218
thomaspinder merged 5 commits into
mainfrom
feat/144-zero-sign-restrictions

Conversation

@thomaspinder

Copy link
Copy Markdown
Owner

Summary

New ZeroSignRestriction identification scheme: named impact-zero constraints and horizon-specific sign constraints in one scheme, via the Arias-Rubio-Ramírez-Waggoner recursive null-space construction — not a penalty approximation.

  • Construction: per draw, columns are built recursively — the orthonormal null basis of the stacked zero rows plus previously-drawn columns, hit with a spherical Gaussian and normalised — giving exact zeros (SVD precision) and exact orthogonality by construction; sign restrictions are then accept/reject with the whole candidate abandoned on any failure (resuming mid-candidate would be a silent distribution bug; commented at the site and guarded by the equivalence test).
  • Regularity: the Rubio-Ramírez-Waggoner-Zha counting condition (z_j ≤ n − j on the sorted padded sequence) is checked deterministically at identify() entry — impossible specifications fail with a named error before any sampling, distinct from low-acceptance runs, which warn with counts and diagnostics (zero_sign_acceptance_rate, failed fractions, mean attempts, max zero violation).
  • Failure policy: unaccepted draws are NaN'd (never silently fall back to the Cholesky factor — that would violate the scheme's zero guarantee; a deliberate, documented divergence from SignRestriction), with on_failure="raise" opt-in.
  • Validation anchors: full triangular zeros reproduce |chol(Σ)| bit-exactly (acceptance 1.0), and with positive-diagonal signs, chol(Σ) itself; an n=2 single-zero case matches the closed-form column analytically; signs-only mode is distributionally equivalent to SignRestriction (acceptance diff 0.0000).
  • Honest scoping, stated in four places (docstring, explanation page, how-to, CONTEXT.md): draws are the standard recursive construction without ARW's volume-element importance weights (required for uniform-conditional set-identified inference; filed as a follow-up rather than shipping an unverifiable formula), per-θ retry follows the in-house convention, and the sampler is Haar on O(n).
  • Docs: explanation section with the counting rule, a climate how-to (an activity shock with zero contemporaneous impact on temperature — physical-lag contemporaneous exogeneity), reference entry, CONTEXT.md, ARW 2018 + RWZ 2010 bib entries.

SignRestriction is byte-untouched; the class is appended at end-of-file to stay out of the #183/#200/#208 conflict zones.

Closes #144

Review

Planned by a Fable-tier planning agent; independently reviewed (verdict: approve, nothing blocking) — the reviewer re-derived the recursion, re-implemented it from scratch with scipy's null_space and reproduced all inspected cell distributions within MC error, verified the early-abandon path has exactly one failure exit, and confirmed the bit-exactness argument for the Cholesky anchor.

Tests

35 tests: the degenerate-Cholesky anchor, the n=2 closed form, generic zero/Σ/sign satisfaction to machine precision, permuted-ordering invisibility, RWZ rejection, the failure policy three ways, diagnostics contract, and the SignRestriction equivalence. Fast suite: 562 passed, 29 deselected; ruff/ty clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Egjd7ToFeb9TQqFnfRQZxV

@thomaspinder
thomaspinder force-pushed the feat/144-zero-sign-restrictions branch from fbe444e to 5ca07cc Compare July 29, 2026 13:56
@codecov-commenter

codecov-commenter commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.66667% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.1%. Comparing base (866ca8f) to head (55956bc).

Files with missing lines Patch % Lines
src/impulso/identification.py 96.6% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@          Coverage Diff           @@
##            main    #218    +/-   ##
======================================
  Coverage   95.0%   95.1%            
======================================
  Files         45      45            
  Lines       3099    3279   +180     
  Branches     380     417    +37     
======================================
+ Hits        2945    3119   +174     
- Misses       111     114     +3     
- Partials      43      46     +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

thomaspinder and others added 4 commits July 29, 2026 23:06
…strictions (#144)

Implements the recursive orthogonalisation of Arias, Rubio-Ramirez &
Waggoner (2018). Writing P = L Q, an impact zero restriction "variable i
does not respond to shock j" is the linear condition e_i' L q_j = 0 on
the j-th column of Q. Columns are built one at a time, each drawn
uniformly from the unit sphere of the null space of

    R_k = [ Z_k L ; q_1' ; ... ; q_{k-1}' ]     (m = z_k + (k-1) rows)

so the zeros hold exactly (to SVD precision) and orthogonality to the
earlier columns holds by construction. Only the sign restrictions need
accept/reject.

Details worth flagging for review:

- Shocks are ordered internally by zero count descending (stable, so ties
  keep user order and the unidentified_* padding stays last); columns are
  permuted back to shock_names order before returning, and rows are never
  permuted. The Rubio-Ramirez, Waggoner & Zha (2010) rank condition
  z_j <= n - j is checked on that sorted padded sequence at identify()
  entry, before any sampling.
- On an early impact-sign failure the WHOLE candidate is abandoned and
  the recursion restarts from column 1. Redrawing only the offending
  column would be a distribution bug — q_k's law is conditional on
  q_1..q_{k-1}. Documented in a code comment at the failure site.
- Failed draws become NaN with one summary warning; there is no fallback
  to L, which would silently violate the zero guarantee. on_failure="raise"
  is opt-in. This diverges from SignRestriction deliberately.
- Diagnostics land on _last_diagnostics under a zero_sign_ prefix, not on
  _last_acceptance_rate, which the pipeline surfaces under the misleading
  name sign_restriction_acceptance_rate.
- Candidates are unweighted: no volume-element correction for the ARW
  uniform-conditional prior. Called out in the class docstring; the two
  test-anchored regimes (no zeros -> Haar; exact identification -> point
  up to signs) are weight-free.

Anchor test: with full triangular zeros every null space is
one-dimensional, so P must be lower triangular with P P' = Sigma, i.e.
the Cholesky factor up to column signs. Acceptance is 1.0 and |P|
reproduces |cholesky(Sigma)| to 0.0 (bit-exact — the SVD returns the
canonical basis vectors for that structure). Adding "+" diagonal signs
pins P == cholesky(Sigma) exactly.

SignRestriction is untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Egjd7ToFeb9TQqFnfRQZxV
…tion, how-to (#144)

- reference/identification.md: ZeroSignRestriction in the autosummary.
- explanation/identification.md: new section covering the ARW recursive
  construction (labelled equations for the zero condition, the recursion,
  and the rank condition), the exact-vs-set spectrum with Cholesky at one
  end and pure sign restrictions at the other, and a warning admonition
  stating the three distributional caveats honestly: draws are unweighted
  (no volume-element correction for the ARW uniform-conditional prior),
  retries are per-theta rather than joint, and sampling is over O(n)
  rather than SO(n).
- how-to/zero-sign-restrictions.md: climate worked example — an activity
  shock has zero contemporaneous impact on the temperature anomaly, which
  is a physical-lag exclusion rather than a sign — plus the admissible-zero
  counting rule, the diagnostics attrs, and a warning admonition spelling
  out that failed draws are NaN and never fall back to Cholesky.
- references.bib: ariasRubioRamirezWaggoner2018, rubioRamirezWaggonerZha2010.
- CONTEXT.md: "Zero-and-sign restrictions (ARW construction)" entry, with
  "penalty-function zeros" listed under _Avoid_ (that is a different,
  approximate construction); IdentificationScheme adapter list refreshed.

Sphinx build with warnings-as-errors reports no warnings from any of these
pages; equations, citations, and both new pages render.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Egjd7ToFeb9TQqFnfRQZxV
…coefficients (#144)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Egjd7ToFeb9TQqFnfRQZxV
@thomaspinder
thomaspinder force-pushed the feat/144-zero-sign-restrictions branch from 5ca07cc to b6dbcab Compare July 29, 2026 21:07
…base conflict

The rebase conflict resolution merged the new bibliography entry into the
blanchardQuah1989 entry, dropping its closing brace. sphinxcontrib-bibtex
then failed to parse references.bib, breaking build-docs and docs-linkcheck.
@thomaspinder
thomaspinder merged commit 89d9129 into main Jul 29, 2026
9 checks passed
@thomaspinder thomaspinder added the enhancement New feature or request label Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add combined zero-and-sign restrictions

2 participants