Repository navigation
FIX: delayed ARMA impulse response and simulation when p > q - #976
Conversation
|
Hi @mmcky, a gentle ping on this one when you have a moment. Happy to adjust anything if needed. Thanks! |
|
Thanks for this, and apologies for the slow reply. This is a correct and well-explained fix: I reproduced it locally, and both new tests fail on One quick request that we now make of every pull request. QuantEcon has adopted a Code of AI Use, which welcomes AI-assisted contributions and asks each pull request to say whether AI tools were used. Our pull-request template doesn't show this yet, so we're asking by hand. Could you:
We're asking everyone, so this isn't a flag on your pull request. Thanks! |
There was a problem hiding this comment.
🔵 Needs a closer look
It intentionally changes the numeric output of two public library methods (impulse_response and simulation) for a released package, which warrants human sign-off even though the fix is correct and well-tested.
0 open findings
What changed in this PR
This PR fixes a latent bug in quantecon/_arma.py where ARMA.impulse_response and ARMA.simulation were shifted by p - q periods whenever the AR polynomial is longer than the MA polynomial (which includes every pure AR(p) with p ≥ 2 built with the default theta=0). The root cause is that set_params passes (ma_poly, ar_poly, 1) to scipy.signal.dimpulse/dlsim, which interpret the coefficients as a transfer function in powers of z; when the numerator (ma_poly) is shorter, the system acquires len(ar_poly) - len(ma_poly) pure delays. The previous code only padded ar_poly when ma_poly was longer; the fix symmetrically pads ma_poly as well so both polynomials match in length. spectral_density/autocovariance use freqz, which is unaffected by trailing zeros, so their output is unchanged.
Changes:
- Pad
ma_polywith trailing zeros whenar_polyis longer, so impulse response/simulation are no longer delayed byp - q. - Update the
set_paramsdocstring to document the equal-length requirement and rationale. - Add two function-based tests validating
impulse_responseand seededsimulationagainst an independently-computed MA(∞) recursion across AR(1), AR(2), AR(3), ARMA(2,1), ARMA(1,2), and the existing test model.
| File | Description |
|---|---|
| quantecon/_arma.py | Symmetric zero-padding of ma_poly/ar_poly in set_params to eliminate the p − q delay; docstring updated to explain the equal-length requirement. |
| quantecon/tests/test_arma.py | New oracle helper _ma_coefficients plus two tests checking impulse-response and seeded simulation against the MA(∞) representation over several AR/ARMA orders. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@oyamad once QEP-5 compliance lands this is looking 👍 to me. |
|
Thanks for reproducing it! I've added the |
oyamad
left a comment
There was a problem hiding this comment.
@nicoloangileri thanks, it now matches the Julia version:
julia> using QuantEcon
julia> println(impulse_response(ARMA([0.5, -0.2]); impulse_length=5))
[1.0, 0.5, 0.04999999999999999, -0.07500000000000001, -0.0475]|
thanks @nicoloangileri for your PR. Looks good to go 👍 Thanks @oyamad for review. |
|
@nicoloangileri thanks for the ping. Always appreciated. |
quantecon 0.13.1 fixes the Markov-jump Riccati recursion in LQMarkov (QuantEcon/QuantEcon.py#982), so remove the notes in markov_jump_lq and policy_regimes saying that it uses the averaged recursion. In arma, say that ma_poly and ar_poly must be padded to the same length before they are passed to dimpulse, the bug QuantEcon/QuantEcon.py#976 fixed. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ARMA.impulse_responseandARMA.simulationare shifted byp - qperiods when the AR polynomial is longer than the MA polynomial. This includes every pure AR(p) with p >= 2 built with the defaulttheta=0.The docstring says
psi[0]is unity, andstatsmodels'ArmaProcess(...).arma2magives the second line.qe.ARMA([0.5, 0.1, 0.05])is delayed by two periods, andsimulationstarts withp - qexact zeros for the same reason.The cause is in
set_params:(ma_poly, ar_poly, 1)is passed toscipy.signal.dimpulseanddlsim, which read the coefficients as a transfer function in positive powers ofz. Whenar_polyis longer, the numerator has a lower degree and the system getslen(ar_poly) - len(ma_poly)pure delays.set_paramsalready paddedar_polywhenma_polywas longer; it now also padsma_polywith zeros, so both polynomials have the same length.spectral_densityandautocovarianceusefreqz, which is not affected by trailing zeros, and their output is unchanged.The existing tests use
theta = np.zeros(3)with three AR coefficients, so the polynomials already had the same length there. The new tests check, for AR(1), AR(2), AR(3), ARMA(2, 1), ARMA(1, 2) and the existing test model:impulse_responseagainst the MA(inf) coefficients from the recursionpsi_j = theta_j + sum_i phi_i psi_{j-i};simulationagainstsum_j psi_j u_{t-j}built from the same shocks.Both fail on
mainand pass here. The full test suite passes locally (745 tests).Related to #888 (test coverage of the simulation and impulse-response paths in
_arma).Assisted-by: Claude Code (Claude Opus 5.5)