Repository: statsmodels/statsmodels

Issue:
sirf_errband_mc / error message #svar
Hi,

I get error message when I try to find error band of my SVAR model. "shape mismatch: value array of shape (0,) could not be broadcast to indexing result of shape (0,4,4)" Can you guys help me out? 

> ValueError                                Traceback (most recent call last)
<ipython-input-208-fd4534490cf8> in <module>()
----> 1 result.sirf_errband_mc(orth=False, repl=1000, T=10,signif=0.05, seed=None, burn=100, cum=False)
~\Anaconda3\lib\site-packages\statsmodels\tsa\vector_ar\svar_model.py in sirf_errband_mc(self, orth, repl, T, signif, seed, burn, cum)
    721                 if i < 10:
    722                     sol = SVAR(sim, svar_type=s_type, A=A_pass,
--> 723                                B=B_pass).fit(maxlags=k_ar)
    724                     g_list.append(np.append(sol.A[A_mask].tolist(),
    725                                             sol.B[B_mask].tolist()))
~\Anaconda3\lib\site-packages\statsmodels\tsa\vector_ar\svar_model.py in fit(self, A_guess, B_guess, maxlags, method, ic, trend, verbose, s_method, solver, override, maxiter, maxfun)
    183         return self._estimate_svar(start_params, lags, trend=trend,
    184                                    solver=solver, override=override,
--> 185                                    maxiter=maxiter, maxfun=maxfun)
    186 
    187 
~\Anaconda3\lib\site-packages\statsmodels\tsa\vector_ar\svar_model.py in _estimate_svar(self, start_params, lags, maxiter, maxfun, trend, solver, override)
    253                                                     solver=solver,
    254                                                     maxiter=maxiter,
--> 255                                                     maxfun=maxfun)
    256         A_mask = self.A_mask
    257         B_mask = self.B_mask
~\Anaconda3\lib\site-packages\statsmodels\tsa\vector_ar\svar_model.py in _solve_AB(self, start_params, maxiter, maxfun, override, solver)
    357         A_len = len(A[A_mask])
    358 
--> 359         A[A_mask] = start_params[:A_len]
    360         B[B_mask] = start_params[A_len:]
    361 
ValueError: shape mismatch: value array of shape (0,) could not be broadcast to indexing result of shape (0,4,4)


Discussion:
Here is my model.

> `A = np.asarray([[1, 0, 0,0],['E', 1, 0,0],['E', 'E', 1,0],['E', 'E', 'E',1]])`
`B = np.asarray([['E', 0, 0,0], [0, 'E',0, 0], [0, 0, 'E',0],[0,0,0,'E']])`
`model = SVAR(df[['r','gm','gp','gy']], svar_type='AB', A=A, B=B)`
`result = model.fit(maxlags=1,maxiter=500, maxfun=500)`
`result.sirf_errband_mc(orth=False, repl=1000, T=10,signif=0.05, seed=None, burn=100, cum=False)`

so A_mask is going to be

> `array([[False, False, False, False],
       [ True, False, False, False],
       [ True,  True, False, False],
       [ True,  True,  True, False]])`

@mk1564 
I tried a simple example with your setting with random numbers, but for me it fails already earlier.
I'm using python 3.4 and have problems with bytes versus strings. my numpy is 1.11.2

```
(Pdb) A_pass
array([[b'1', b'0', b'0', b'0'],
       [b'E', b'1', b'0', b'0'],
       [b'E', b'E', b'1', b'0'],
       [b'E', b'E', b'E', b'1']], 
      dtype='|S1')
(Pdb) A_mask
array([[False, False, False, False],
       [ True, False, False, False],
       [ True,  True, False, False],
       [ True,  True,  True, False]], dtype=bool)
(Pdb) A_pass == 'E'
False
(Pdb) A_pass == b'E'
array([[False, False, False, False],
       [ True, False, False, False],
       [ True,  True, False, False],
       [ True,  True,  True, False]], dtype=bool)
```

What are your python, numpy and statsmodels version?

Given that there are no unit tests for sirf_errband_mc, it is likely that there are still some basic python 3 incompatibilities.
A changes dtype

the A array created by the user code is U(nicode)

```
array([['1', '0', '0', '0'],
       ['E', '1', '0', '0'],
       ['E', 'E', '1', '0'],
       ['E', 'E', 'E', '1']], 
      dtype='<U11')
```

SVARResult.sirf_errband_mc recreates it as S1 bytes array
If I recreate the model in `SVARResult.sirf_errband_mc` with dtype '<U11', then the 
`sirf_eb = result.sirf_errband_mc(orth=False, repl=100, T=10, signif=0.05, seed=None, burn=100, cum=False)`
finishes without exception
sirf_eb is a 2-tuple with 
```
(Pdb) sirf_eb[0].shape
(11, 4, 4)
(Pdb) sirf_eb[1].shape
(11, 4, 4)
```

irf plot seems to work also

```
>>> irf = result.irf()
>>> irf.plot()
<matplotlib.figure.Figure object at 0x00000000060BE828>
>>> irf.plot(stderr_type='mc')
<matplotlib.figure.Figure object at 0x000000000C8A3BE0>
```

But I have no idea whether the numbers are correct.

So quickfix seems to be to recreate A and B with the same dtype S or U in sirf_errband_mc.
The better fix would be to store the original user provided A and B and use it directly instead of recreating it.

(Based on quick bughunting in those lines that cause problems without trying to understand larger parts of SVAR. I haven't looked at it in quite some time, and never at IRF for SVAR.)

AFAICS, there is no caching of the MonteCarlo results. repeated calls to irf.plot perform a new simulation each time.
Because there are options for irf and irf.plot, we cannot simply store the results, i.e. they would only be valid for the last set of options, until we memoize for given arguments.
Thank you for your comment! So do you mean I need to create A like

`A1 = np.array([[1, 0, 0,0],['E', 1, 0,0],['E', 'E', 1,0],['E', 'E', 'E',1]],dtype='|S1')`? It seems like it does not work even at the model fitting step.

How did you change the model with dtype U?
Because we will be dropping Python 2.7 within the next one or two years, it is better to stick with unicode

The change that I made locally is in sirf_errband_mc  in svar_model.py lines 677 and 678

```
@@ -672,12 +672,12 @@ class SVARResults(SVARProcess, VARResults):
...
-        A_pass = np.zeros(A.shape, dtype='|S1')
-        B_pass = np.zeros(B.shape, dtype='|S1')
+        A_pass = np.zeros(A.shape, dtype='<U11') #'|S1')
+        B_pass = np.zeros(B.shape, dtype='<U11') #'|S1')
```
Thanks for your explanation, but after I changed the code as you said, I run the model again

`A = np.array([[1, 0, 0,0],['E', 1, 0,0],['E', 'E', 1,0],['E', 'E', 'E',1]])`
`B = np.array([['E', 0, 0,0], [0, 'E',0, 0], [0, 0, 'E',0],[0,0,0,'E']])`
`model = SVAR(df[['r','gm','gp','gy']], svar_type='AB', A=A, B=B)`
`result = model.fit()`

the last line causes an error like the following.. Do you know how to fix this?

>TypeError                                 Traceback (most recent call last)
<ipython-input-29-18fecb50fd4a> in <module>()
      4 #Bguess=np.array([0.59,8.75,0.15,0.54])
      5 model = SVAR(df[['r','gm','gp','gy']], svar_type='AB', A=A, B=B)
----> 6 result = model.fit()
      7 #A_guess=Aguess, B_guess=Bguess,

><ipython-input-18-dfe0cf0ba030> in fit(self, A_guess, B_guess, maxlags, method, ic, trend, verbose, s_method, solver, override, maxiter, maxfun)
    182         return self._estimate_svar(start_params, lags, trend=trend,
    183                                    solver=solver, override=override,
--> 184                                    maxiter=maxiter, maxfun=maxfun)
    185 
    186 

><ipython-input-18-dfe0cf0ba030> in _estimate_svar(self, start_params, lags, maxiter, maxfun, trend, solver, override)
    253                                                     solver=solver,
    254                                                     maxiter=maxiter,
--> 255                                                     maxfun=maxfun)
    256         A_mask = self.A_mask
    257         B_mask = self.B_mask

><ipython-input-18-dfe0cf0ba030> in _solve_AB(self, start_params, maxiter, maxfun, override, solver)
    362 
    363         if override == False:
--> 364             J = self._compute_J(A, B)
    365             self.check_order(J)
    366             self.check_rank(J)

><ipython-input-18-dfe0cf0ba030> in _compute_J(self, A_solve, B_solve)
    394         #first generate duplication matrix, see MN (1980) for notation
    395 
--> 396         D_nT=np.zeros([(1.0/2)*(neqs)*(neqs+1),neqs**2])
    397 
    398         for j in range(neqs):

>TypeError: 'float' object cannot be interpreted as an integer

Resolving patch:
diff --git a/statsmodels/tsa/vector_ar/svar_model.py b/statsmodels/tsa/vector_ar/svar_model.py
--- a/statsmodels/tsa/vector_ar/svar_model.py
+++ b/statsmodels/tsa/vector_ar/svar_model.py
@@ -71,8 +71,11 @@ def __init__(self, endog, svar_type, dates=None,
 
         svar_ckerr(svar_type, A, B)
 
-        #initialize A, B as I if not given
-        #Initialize SVAR masks
+        self.A_original = A
+        self.B_original = B
+
+        # initialize A, B as I if not given
+        # Initialize SVAR masks
         if A is None:
             A = np.identity(self.neqs)
             self.A_mask = A_mask = np.zeros(A.shape, dtype=bool)
@@ -670,64 +673,44 @@ def sirf_errband_mc(self, orth=False, repl=1000, T=10,
         B = self.B
         A_mask = self.A_mask
         B_mask = self.B_mask
-        A_pass = np.zeros(A.shape, dtype='|S1')
-        B_pass = np.zeros(B.shape, dtype='|S1')
-        A_pass[~A_mask] = A[~A_mask]
-        B_pass[~B_mask] = B[~B_mask]
-        A_pass[A_mask] = 'E'
-        B_pass[B_mask] = 'E'
-        if A_mask.sum() == 0:
-            s_type = 'B'
-        elif B_mask.sum() == 0:
-            s_type = 'A'
-        else:
-            s_type = 'AB'
+        A_pass = self.model.A_original
+        B_pass = self.model.B_original
+        s_type = self.model.svar_type
+
         g_list = []
 
+        def agg(impulses):
+            if cum:
+                return impulses.cumsum(axis=0)
+            return impulses
+
+        opt_A = A[A_mask]
+        opt_B = B[B_mask]
         for i in range(repl):
-            #discard first hundred to correct for starting bias
-            sim = util.varsim(coefs, intercept, sigma_u, steps=nobs+burn)
+            # discard first hundred to correct for starting bias
+            sim = util.varsim(coefs, intercept, sigma_u, seed=seed,
+                              steps=nobs + burn)
             sim = sim[burn:]
-            if cum == True:
-                if i < 10:
-                    sol = SVAR(sim, svar_type=s_type, A=A_pass,
-                               B=B_pass).fit(maxlags=k_ar)
-                    g_list.append(np.append(sol.A[sol.A_mask].\
-                                            tolist(),
-                                            sol.B[sol.B_mask].\
-                                            tolist()))
-                    ma_coll[i] = sol.svar_ma_rep(maxn=T).cumsum(axis=0)
-                elif i >= 10:
-                    if i == 10:
-                        mean_AB = np.mean(g_list, axis = 0)
-                        split = len(A_pass[A_mask])
-                        opt_A = mean_AB[:split]
-                        opt_B = mean_AB[split:]
-
-                    smod = SVAR(sim, svar_type=s_type, A=A_pass, B=B_pass)
-                    sres = smod.fit(maxlags=k_ar, A_guess=opt_A, B_guess=opt_B)
-                    ma_coll[i] = sres.svar_ma_rep(maxn=T).cumsum(axis=0)
-
-            elif cum == False:
-                if i < 10:
-                    sol = SVAR(sim, svar_type=s_type, A=A_pass,
-                               B=B_pass).fit(maxlags=k_ar)
-                    g_list.append(np.append(sol.A[A_mask].tolist(),
-                                            sol.B[B_mask].tolist()))
-                    ma_coll[i] = sol.svar_ma_rep(maxn=T)
-                elif i >= 10:
-                    if i == 10:
-                        mean_AB = np.mean(g_list, axis = 0)
-                        split = len(A[A_mask])
-                        opt_A = mean_AB[:split]
-                        opt_B = mean_AB[split:]
-
-                    smod = SVAR(sim, svar_type=s_type, A=A_pass, B=B_pass)
-                    sres = smod.fit(maxlags=k_ar, A_guess=opt_A, B_guess=opt_B)
-                    ma_coll[i] = sres.svar_ma_rep(maxn=T)
-
-        ma_sort = np.sort(ma_coll, axis=0) #sort to get quantiles
-        index = round(signif/2*repl)-1,round((1-signif/2)*repl)-1
-        lower = ma_sort[index[0],:, :, :]
-        upper = ma_sort[index[1],:, :, :]
+
+            smod = SVAR(sim, svar_type=s_type, A=A_pass, B=B_pass)
+            if i == 10:
+                # Use first 10 to update starting val for remainder of fits
+                mean_AB = np.mean(g_list, axis=0)
+                split = len(A[A_mask])
+                opt_A = mean_AB[:split]
+                opt_B = mean_AB[split:]
+
+            sres = smod.fit(maxlags=k_ar, A_guess=opt_A, B_guess=opt_B)
+
+            if i < 10:
+                # save estimates for starting val if in first 10
+                g_list.append(np.append(sres.A[A_mask].tolist(),
+                                        sres.B[B_mask].tolist()))
+            ma_coll[i] = agg(sres.svar_ma_rep(maxn=T))
+
+        ma_sort = np.sort(ma_coll, axis=0)  # sort to get quantiles
+        index = (int(round(signif / 2 * repl) - 1),
+                 int(round((1 - signif / 2) * repl) - 1))
+        lower = ma_sort[index[0], :, :, :]
+        upper = ma_sort[index[1], :, :, :]
         return lower, upper


Test patch:
diff --git a/statsmodels/tsa/vector_ar/tests/test_svar.py b/statsmodels/tsa/vector_ar/tests/test_svar.py
--- a/statsmodels/tsa/vector_ar/tests/test_svar.py
+++ b/statsmodels/tsa/vector_ar/tests/test_svar.py
@@ -1,11 +1,12 @@
 """
 Test SVAR estimation
 """
-
-import statsmodels.api as sm
-from statsmodels.tsa.vector_ar.svar_model import SVAR
 from numpy.testing import assert_almost_equal, assert_allclose
 import numpy as np
+import pytest
+
+import statsmodels.datasets.macrodata
+from statsmodels.tsa.vector_ar.svar_model import SVAR
 
 DECIMAL_6 = 6
 DECIMAL_5 = 5
@@ -15,11 +16,11 @@
 class TestSVAR(object):
     @classmethod
     def setup_class(cls):
-        mdata = sm.datasets.macrodata.load_pandas().data
-        mdata = mdata[['realgdp','realcons','realinv']]
+        mdata = statsmodels.datasets.macrodata.load_pandas().data
+        mdata = mdata[['realgdp', 'realcons', 'realinv']]
         data = mdata.values
         data = np.diff(np.log(data), axis=0)
-        A = np.asarray([[1, 0, 0],['E', 1, 0],['E', 'E', 1]])
+        A = np.asarray([[1, 0, 0], ['E', 1, 0], ['E', 'E', 1]])
         B = np.asarray([['E', 0, 0], [0, 'E', 0], [0, 0, 'E']])
         results = SVAR(data, svar_type='AB', A=A, B=B).fit(maxlags=3)
         cls.res1 = results
@@ -55,3 +56,16 @@ def test_llf_ic(self):
         assert_allclose(res1.aic - corr_const, res2.aic_var, atol=1e-12)
         assert_allclose(res1.bic - corr_const, res2.sbic_var, atol=1e-12)
         assert_allclose(res1.hqic - corr_const, res2.hqic_var, atol=1e-12)
+
+    @pytest.mark.smoke
+    def test_irf(self):
+        # mostly SMOKE, API test
+        # this only checks that the methods work and produce the same result
+        res1 = self.res1
+        errband1 = res1.sirf_errband_mc(orth=False, repl=50, T=10, signif=0.05,
+                                        seed=987123, burn=100, cum=False)
+
+        irf = res1.irf()
+        errband2 = irf.errband_mc(orth=False, svar=True, repl=50,
+                                  signif=0.05, seed=987123, burn=100)
+        assert_allclose(errband1, errband2, rtol=1e-8, atol=1e-20)