Skip to content

Extract MA recursion and time-dim guard helpers #76

Closed

Description

@thomaspinder

Context

Audit reports/audit-2026-05-18.md findings #2 and #3.

#2 — The MA recursion Phi_h = sum_{j=1}^{min(h,p)} A_j @ Phi_{h-j}
is implemented twice with different shapes:

  • identified._ma_coefficients (src/impulso/identified.py:50-67):
    vectorised over (C, D) via einsum.
  • identification.SignRestriction._check_restrictions_at_horizons
    (src/impulso/identification.py:209-225): single (n, n) draw
    inside the rotation Python loop, using @.

The math is identical; the two copies will drift when one is fixed.

#3 — _guard_no_time_dim is copy-pasted between IRFResult and
FEVDResult (src/impulso/results.py), differing only in the
"irf" vs "fevd" literal and the class name in the error message
(~17 lines duplicated exactly).

Resolution

Per plans/2026-05-18-audit-cleanup-plan.md (Decisions ledger Q6, Q7):

  • New module src/impulso/_ma.py with one function
    compute_ma_phi(A: list[np.ndarray], horizon: int) -> np.ndarray.
    List-of-A_j signature. Numpy @ broadcasts over leading dims so
    the helper handles both batched (C, D, n, n) and single-draw
    (n, n) shapes without branching. Stacks with axis=-3 so the
    horizon axis lands just before (n, n) regardless of leading rank.
  • identified._ma_coefficients splits B_draws into the A list and
    delegates. identification._check_restrictions_at_horizons splits
    B_draw similarly, delegates, then loops over horizons applying
    restrictions to Phi[h] @ candidate.
  • _guard_no_time_dim lifts onto VARResultBase. Each subclass
    declares _PRIMARY_KEY: ClassVar[str] ("irf", "fevd"). Base
    helper reads it.

Acceptance

  1. make check && make test clean.
  2. No behavioural change visible to tests outside tests/test_ma.py —
    pure refactor.
  3. tests/test_ma.py covers single-draw, batched, edge cases
    (horizon=0, horizon < n_lags, numerical agreement against the
    pre-refactor recursion on a fixed seed).
  4. src/impulso/identified.py and src/impulso/identification.py
    each call compute_ma_phi exactly once; the old recursion bodies
    are deleted.

Shipping

PR3 of the 4-PR audit stack, stacked on top of #75 (PR2).

Activity

  1. added 2 commits that reference this issue on May 18, 2026
    f450066
    a24ae46
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