Ejemplo n.º 1
0
def get_sys(self, par=None, reduce_sys=None, verbose=False):

    st = time.time()

    if reduce_sys is None:
        try:
            reduce_sys = self.fdict['reduce_sys']
        except KeyError:
            reduce_sys = False

    self.fdict['reduce_sys'] = reduce_sys

    if par is None:
        par = self.p0()

    if not self.const_var:
        raise NotImplementedError('Pakage is only meant to work with OBCs')

    vv_v = np.array([v.name for v in self.variables])
    vv_x = np.array(self.variables)

    dim_v = len(vv_v)

    # obtain matrices from pydsge
    # this could be further accelerated by getting them directly from the equations in pydsge
    AA = self.AA(par)  # forward
    BB = self.BB(par)  # contemp
    CC = self.CC(par)  # backward
    bb = self.bb(par).flatten()  # constraint

    # the special case in which the constraint is just a cut-off of another variable requires
    b = bb.astype(float)

    # define transition shocks -> state
    D = self.PSI(par)

    # mask those vars that are either forward looking or part of the constraint
    in_x = ~fast0(AA, 0) | ~fast0(b[:dim_v])

    # reduce x vector
    vv_x2 = vv_x[in_x]
    A1 = AA[:, in_x]
    b1 = np.hstack((b[:dim_v][in_x], b[dim_v:]))

    dim_x = len(vv_x2)

    # define actual matrices
    M = np.block([[np.zeros(A1.shape), CC],
                  [np.eye(dim_x), np.zeros((dim_x, dim_v))]])

    P = np.block([[A1, -BB], [np.zeros((dim_x, dim_x)), np.eye(dim_v)[in_x]]])

    c_arg = list(vv_x2).index(self.const_var)

    # c contains information on how the constraint var affects the system
    c_M = M[:, c_arg]
    c_P = P[:, c_arg]

    # get rid of constrained var
    b2 = np.delete(b1, c_arg)
    M1 = np.delete(M, c_arg, 1)
    P1 = np.delete(P, c_arg, 1)
    vv_x3 = np.delete(vv_x2, c_arg)

    # decompose P in singular & nonsingular rows
    U, s, V = nl.svd(P1)
    s0 = fast0(s)

    P2 = np.diag(s) @ V
    M2 = U.T @ M1

    c1 = U.T @ c_M

    if not fast0(c1[s0], 2) or not fast0(U.T[s0] @ c_P, 2):
        NotImplementedError(
            'The system depends directly or indirectly on whether the constraint holds in the future or not.\n'
        )

    # actual desingularization by iterating equations in M forward
    P2[s0] = M2[s0]

    if 'x_bar' in [p.name for p in self.parameters]:
        x_bar = par[[p.name for p in self.parameters].index('x_bar')]
    elif 'x_bar' in self.parafunc[0]:
        pf = self.parafunc
        x_bar = pf[1](par)[pf[0].index('x_bar')]
    else:
        print(
            "Parameter `x_bar` (maximum value of the constraint) not specified. Assuming x_bar = -1 for now."
        )
        x_bar = -1

    # create the stuff that the algorithm needs
    N = nl.inv(P2) @ M2
    A = nl.inv(P2) @ (M2 + np.outer(c1, b2))

    if sum(eig(A).round(3) >= 1) - len(vv_x3):
        raise ValueError('BC *not* satisfied.')

    dim_x = len(vv_x3)
    OME = re_bc(A, dim_x)
    J = np.hstack((np.eye(dim_x), -OME))

    try:
        cx = nl.inv(P2) @ c1 * x_bar
    except ParafuncError:
        raise SyntaxError(
            "At least one parameter should rather be a function of parameters ('parafunc')..."
        )

    # check condition:
    n1 = N[:dim_x, :dim_x]
    n3 = N[dim_x:, :dim_x]
    cc1 = cx[:dim_x]
    cc2 = cx[dim_x:]
    bb1 = b2[:dim_x]

    out_msk = fast0(N, 0) & fast0(A, 0) & fast0(b2) & fast0(cx)
    out_msk[-len(vv_v):] = out_msk[-len(vv_v):] & fast0(self.ZZ(par), 0)
    # store those that are/could be reduced
    self.out_msk = out_msk[-len(vv_v):].copy()

    if not reduce_sys:
        out_msk[-len(vv_v):] = False

    s_out_msk = out_msk[-len(vv_v):]

    if hasattr(self, 'P'):
        if self.P.shape[0] < sum(~s_out_msk):
            P_new = np.zeros((len(self.out_msk), len(self.out_msk)))
            if P_new[~self.out_msk][:, ~self.out_msk].shape != self.P.shape:
                print(
                    '[get_sys:]'.ljust(15, ' ') +
                    ' Shape missmatch of P-matrix, number of states seems to differ!'
                )
            P_new[~self.out_msk][:, ~self.out_msk] = self.P
            self.P = P_new
        elif self.P.shape[0] > sum(~s_out_msk):
            self.P = self.P[~s_out_msk][:, ~s_out_msk]

    # add everything to the DSGE object
    self.vv = vv_v[~s_out_msk]
    self.vx = np.array([v.name for v in vv_x3])
    self.dim_x = dim_x
    self.dim_v = len(self.vv)

    self.par = par

    self.hx = self.ZZ(par)[:, ~s_out_msk], self.DD(par).squeeze()
    self.obs_arg = np.where(self.hx[0])[1]

    N2 = N[~out_msk][:, ~out_msk]
    A2 = A[~out_msk][:, ~out_msk]
    J2 = J[:, ~out_msk]

    self.SIG = (BB.T @ D)[~s_out_msk]

    self.sys = N2, A2, J2, cx[~out_msk], b2[~out_msk], x_bar

    if verbose:
        print('[get_sys:]'.ljust(15, ' ') +
              'Creation of system matrices finished in %ss.' %
              np.round(time.time() - st, 3))

    return
Ejemplo n.º 2
0
def irfs(self, shocklist, wannasee=None, linear=False, verbose=False):

    # REWRITE!!
    # returns time series of impule responses
    # shocklist: takes list of tuples of (shock, size, timing)
    # wannasee: list of strings of the variables to be plotted and stored

    slabels = self.vv
    olabels = self.observables

    args_sts = []
    args_obs = []

    if wannasee not in (None, 'all', 'full'):
        for v_raw in wannasee:
            v = v_raw.replace('_', '')
            if v in slabels:
                args_sts.append(list(slabels).index(v))
            elif v in olabels:
                args_obs.append(olabels.index(v))
            else:
                raise Exception(
                    "Variable %s neither in states nor observables. You might want to call self.get_sys() with the 'reduce_sys = False' argument. Note that underscores '_' are discarged."
                    % v)

    st_vec = np.zeros(len(self.vv))

    Y = []
    K = []
    L = []
    superflag = False

    st = time.time()

    for t in range(30):

        shk_vec = np.zeros(len(self.shocks))
        for vec in shocklist:
            if vec[2] == t:

                shock = vec[0]
                shocksize = vec[1]

                shock_arg = self.shocks.index(shock)
                shk_vec[shock_arg] = shocksize

                shk_process = (self.SIG @ shk_vec).nonzero()

                for shk in shk_process:
                    args_sts += list(shk)

        st_vec, (l, k), flag = self.t_func(st_vec,
                                           shk_vec,
                                           linear=linear,
                                           return_k=True)

        if flag:
            superflag = True

        Y.append(st_vec)
        K.append(k)
        L.append(l)

    if superflag and verbose:
        print('[irfs:]'.ljust(15, ' ') +
              ' No rational expectations solution found.')

    Y = np.array(Y)
    K = np.array(K)
    L = np.array(L)

    care_for_sts = np.unique(args_sts)
    care_for_obs = np.unique(args_obs)

    if wannasee is None:
        Z = (self.hx[0] @ Y.T).T + self.hx[1]
        tt = ~fast0(Z - Z.mean(axis=0), 0)
        llabels = list(np.array(self.observables)[tt]) + list(
            self.vv[care_for_sts])
        X2 = Y[:, care_for_sts]
        X = np.hstack((Z[:, tt], X2))
    elif wannasee is 'full':
        llabels = list(self.vv)
        X = Y
    elif wannasee is 'all':
        tt = ~fast0(Y - Y.mean(axis=0), 0)
        llabels = list(self.vv[tt])
        X = Y[:, tt]
    else:
        llabels = list(self.vv[care_for_sts])
        X = Y[:, care_for_sts]
        if care_for_obs.size:
            llabels = list(np.array(self.observables)[care_for_obs]) + llabels
            Z = ((self.hx[0] @ Y.T).T + self.hx[1])[:, care_for_obs]
            X = np.hstack((Z, X))

    if verbose:
        print('[irfs:]'.ljust(15, ' ') + 'Simulation took ',
              np.round((time.time() - st), 5), ' seconds.')

    labels = []
    for l in llabels:
        if not isinstance(l, str):
            l = l.name
        labels.append(l)

    return X, labels, (Y, K, L)
Ejemplo n.º 3
0
def get_sys(self,
            par=None,
            reduce_sys=None,
            l_max=None,
            k_max=None,
            ignore_tests=False,
            verbose=False):
    """Creates the transition function given a set of parameters. 

    If no parameters are given this will default to the calibration in the `yaml` file.

    Parameters
    ----------
    par : array or list, optional
        The parameters to parse into the transition function. (defaults to calibration in `yaml`)
    reduce_sys : bool, optional
        If true, the state space is reduced. This speeds up computation.
    l_max : int, optional
        The expected number of periods *until* the constraint binds (defaults to 3).
    k_max : int, optional
        The expected number of periods for which the constraint binds (defaults to 17).
    """

    st = time.time()

    reduce_sys = reduce_sys if reduce_sys is not None else self.fdict.get(
        'reduce_sys')
    ignore_tests = ignore_tests if ignore_tests is not None else self.fdict.get(
        'ignore_tests')

    l_max = 3 if l_max is None else l_max
    k_max = 17 if k_max is None else k_max

    self.fdict['reduce_sys'] = reduce_sys
    self.fdict['ignore_tests'] = ignore_tests

    par = self.p0() if par is None else list(par)
    ppar = self.compile(par)  # parsed par

    if not self.const_var:
        raise NotImplementedError('Pakage is only meant to work with OBCs')

    vv_v = np.array([v.name for v in self.variables])
    vv_x = np.array(self.variables)

    dim_v = len(vv_v)

    # obtain matrices from pydsge
    # this could be further accelerated by getting them directly from the equations in pydsge
    AA = self.AA(ppar)  # forward
    BB = self.BB(ppar)  # contemp
    CC = self.CC(ppar)  # backward
    bb = self.bb(ppar).flatten()  # constraint

    # the special case in which the constraint is just a cut-off of another variable requires
    b = bb.astype(float)

    # define transition shocks -> state
    D = self.PSI(ppar)

    # mask those vars that are either forward looking or part of the constraint
    in_x = ~fast0(AA, 0) | ~fast0(b[:dim_v])

    # reduce x vector
    vv_x2 = vv_x[in_x]
    A1 = AA[:, in_x]
    b1 = np.hstack((b[:dim_v][in_x], b[dim_v:]))

    dim_x = len(vv_x2)

    # define actual matrices
    M = np.block([[np.zeros(A1.shape), CC],
                  [np.eye(dim_x), np.zeros((dim_x, dim_v))]])

    P = np.block([[A1, -BB], [np.zeros((dim_x, dim_x)), np.eye(dim_v)[in_x]]])

    c_arg = list(vv_x2).index(self.const_var)

    # c contains information on how the constraint var affects the system
    c_M = M[:, c_arg]
    c_P = P[:, c_arg]

    # get rid of constrained var
    b2 = np.delete(b1, c_arg)
    M1 = np.delete(M, c_arg, 1)
    P1 = np.delete(P, c_arg, 1)
    vv_x3 = np.delete(vv_x2, c_arg)

    # decompose P in singular & nonsingular rows
    U, s, V = nl.svd(P1)
    s0 = fast0(s)

    P2 = np.diag(s) @ V
    M2 = U.T @ M1

    c1 = U.T @ c_M

    if not fast0(c1[s0], 2) or not fast0(U.T[s0] @ c_P, 2):
        raise NotImplementedError(
            'The system depends directly or indirectly on whether the constraint holds in the future or not.\n'
        )

    # actual desingularization by iterating equations in M forward
    P2[s0] = M2[s0]

    if 'x_bar' in [p.name for p in self.parameters]:
        x_bar = par[[p.name for p in self.parameters].index('x_bar')]
    elif 'x_bar' in self.parafunc[0]:
        pf = self.parafunc
        x_bar = pf[1](par)[pf[0].index('x_bar')]
    else:
        print(
            "Parameter `x_bar` (maximum value of the constraint) not specified. Assuming x_bar = -1 for now."
        )
        x_bar = -1

    # create the stuff that the algorithm needs
    N = nl.inv(P2) @ M2
    A = nl.inv(P2) @ (M2 + np.outer(c1, b2))

    # rounding here to allow for small numeric errors during SVD & inversion
    if not ignore_tests:
        if sum(eig(A).round(3) < 1) > len(vv_v):
            raise ValueError(
                'B-K condition *not* satisfied (too many EV < 1).')
        if sum(eig(A).round(3) >= 1) > len(vv_x3):
            raise ValueError(
                'B-K condition *not* satisfied (too many EV > 1).')

    dim_x = len(vv_x3)
    OME = re_bc(A, dim_x)
    J = np.hstack((np.eye(dim_x), -OME))

    try:
        cx = nl.inv(P2) @ c1 * x_bar
    except ParafuncError:
        raise SyntaxError(
            "At least one parameter is a function of other parameters, and should be declared in `parafunc`."
        )

    # check condition:
    n1 = N[:dim_x, :dim_x]
    n3 = N[dim_x:, :dim_x]
    cc1 = cx[:dim_x]
    cc2 = cx[dim_x:]
    bb1 = b2[:dim_x]

    out_msk = fast0(N, 0) & fast0(A, 0) & fast0(b2) & fast0(cx)
    out_msk[-len(vv_v):] = out_msk[-len(vv_v):] & fast0(self.ZZ(ppar), 0)
    # store those that are/could be reduced
    self.out_msk = out_msk[-len(vv_v):].copy()

    if not reduce_sys:
        out_msk[-len(vv_v):] = False

    s_out_msk = out_msk[-len(vv_v):]

    if hasattr(self, 'P'):
        if self.P.shape[0] < sum(~s_out_msk):
            P_new = np.zeros((len(self.out_msk), len(self.out_msk)))
            if P_new[~self.out_msk][:, ~self.out_msk].shape != self.P.shape:
                print(
                    '[get_sys:]'.ljust(15, ' ') +
                    ' Shape missmatch of P-matrix, number of states seems to differ!'
                )
            P_new[~self.out_msk][:, ~self.out_msk] = self.P
            self.P = P_new
        elif self.P.shape[0] > sum(~s_out_msk):
            self.P = self.P[~s_out_msk][:, ~s_out_msk]

    # add everything to the DSGE object
    self.vv = vv_v[~s_out_msk]
    self.vx = np.array([v.name for v in vv_x3])
    self.dim_x = dim_x
    self.dim_v = len(self.vv)

    self.par = par
    self.ppar = ppar

    self.hx = self.ZZ(ppar)[:, ~s_out_msk], self.DD(ppar).squeeze()
    self.obs_arg = np.where(self.hx[0])[1]

    N2 = N[~out_msk][:, ~out_msk]
    A2 = A[~out_msk][:, ~out_msk]
    J2 = J[:, ~out_msk]

    self.SIG = (BB.T @ D)[~s_out_msk]

    self.sys = N2, A2, J2, cx[~out_msk], b2[~out_msk], x_bar

    if verbose:
        print('[get_sys:]'.ljust(15, ' ') +
              ' Creation of system matrices finished in %ss.' %
              np.round(time.time() - st, 3))

    preprocess(self, l_max, k_max, verbose)

    test = self.precalc_mat[0][1, 0, 1]
    if not ignore_tests and (eig(test[-test.shape[1]:]) > 1).any():
        raise ValueError('Explosive dynamics detected.')

    return
Ejemplo n.º 4
0
def get_sys(self,
            par=None,
            reduce_sys=None,
            l_max=None,
            k_max=None,
            linear=False,
            tol=1e-8,
            ignore_tests=False,
            verbose=False):
    """Creates the transition function given a set of parameters. 

    If no parameters are given this will default to the calibration in the `yaml` file.

    Parameters
    ----------
    par : array or list, optional
        The parameters to parse into the transition function. (defaults to calibration in `yaml`)
    reduce_sys : bool, optional
        If true, the state space is reduced. This speeds up computation.
    l_max : int, optional
        The expected number of periods *until* the constraint binds (defaults to 3).
    k_max : int, optional
        The expected number of periods for which the constraint binds (defaults to 17).
    """

    st = time.time()

    reduce_sys = reduce_sys if reduce_sys is not None else self.fdict.get(
        'reduce_sys')
    ignore_tests = ignore_tests if ignore_tests is not None else self.fdict.get(
        'ignore_tests')

    if l_max is not None:
        if l_max < 2:
            print('[get_sys:]'.ljust(15, ' ') +
                  ' `l_max` must be at least 2 (is %s). Correcting...' % l_max)
            l_max = 2
        # effective l_max is one lower because algorithm exists on l_max
        l_max += 1

    elif hasattr(self, 'lks'):
        l_max = self.lks[0]
    else:
        l_max = 1 if linear else 3

    if k_max is not None:
        pass
    elif hasattr(self, 'lks'):
        k_max = self.lks[1]
    else:
        k_max = 0 if linear else 17

    self.lks = [l_max, k_max]

    self.fdict['reduce_sys'] = reduce_sys
    self.fdict['ignore_tests'] = ignore_tests

    par = self.p0() if par is None else list(par)
    try:
        ppar = self.pcompile(par)  # parsed par
    except AttributeError:
        ppar = self.compile(par)  # parsed par

    self.par = par
    self.ppar = ppar

    if not self.const_var:
        raise NotImplementedError('Package is only meant to work with OBCs')

    vv_v = np.array([v.name for v in self.variables])
    vv_x = np.array(self.variables)

    dim_v = len(vv_v)

    # obtain matrices
    AA = self.AA(ppar)  # forward
    BB = self.BB(ppar)  # contemp
    CC = self.CC(ppar)  # backward
    bb = self.bb(ppar).flatten().astype(float)  # constraint

    # define transition shocks -> state
    D = self.PSI(ppar)

    # mask those vars that are either forward looking or part of the constraint
    in_x = ~fast0(AA, 0) | ~fast0(bb[:dim_v])

    # reduce x vector
    vv_x2 = vv_x[in_x]
    A1 = AA[:, in_x]
    b1 = np.hstack((bb[:dim_v][in_x], bb[dim_v:]))

    dim_x = len(vv_x2)

    # define actual matrices
    N = np.block([[np.zeros(A1.shape), CC],
                  [np.eye(dim_x), np.zeros((dim_x, dim_v))]])

    P = np.block([[-A1, -BB], [np.zeros((dim_x, dim_x)), np.eye(dim_v)[in_x]]])

    c_arg = list(vv_x2).index(self.const_var)

    # c contains information on how the constraint var affects the system
    c1 = N[:, c_arg]
    c_P = P[:, c_arg]

    # get rid of constrained var
    b2 = np.delete(b1, c_arg)
    N1 = np.delete(N, c_arg, 1)
    P1 = np.delete(P, c_arg, 1)
    vv_x3 = np.delete(vv_x2, c_arg)
    dim_x = len(vv_x3)

    M1 = N1 + np.outer(c1, b2)

    # solve using Klein's method
    OME = re_bk(M1, P1, d_endo=dim_x)
    J = np.hstack((np.eye(dim_x), -OME))

    # desingularization of P
    U, s, V = nl.svd(P1)

    s0 = s < tol

    P2 = U.T @ P1
    N2 = U.T @ N1
    c2 = U.T @ c1

    # actual desingularization by iterating equations in M forward
    P2[s0] = N2[s0]

    # I could possible create auxiallary variables to make this work. Or I get the stuff directly from the boehlgo
    if not fast0(c2[s0], 2) or not fast0(U.T[s0] @ c_P, 2):
        raise NotImplementedError(
            'The system depends directly or indirectly on whether the constraint holds in the future or not.\n'
        )

    if verbose > 1:
        print('[get_sys:]'.ljust(15, ' ') +
              ' determinant of `P` is %1.2e.' % nl.det(P2))

    if 'x_bar' in [p.name for p in self.parameters]:
        x_bar = par[[p.name for p in self.parameters].index('x_bar')]
    elif 'x_bar' in self.parafunc[0]:
        pf = self.parafunc
        x_bar = pf[1](par)[pf[0].index('x_bar')]
    else:
        print(
            "Parameter `x_bar` (maximum value of the constraint) not specified. Assuming x_bar = -1 for now."
        )
        x_bar = -1

    try:
        cx = nl.inv(P2) @ c2 * x_bar
    except ParafuncError:
        raise SyntaxError(
            "At least one parameter is a function of other parameters, and should be declared in `parafunc`."
        )

    # create the stuff that the algorithm needs
    N = nl.inv(P2) @ N2
    A = nl.inv(P2) @ (N2 + np.outer(c2, b2))

    out_msk = fast0(N, 0) & fast0(A, 0) & fast0(b2) & fast0(cx)
    out_msk[-len(vv_v):] = out_msk[-len(vv_v):] & fast0(self.ZZ(ppar), 0)
    # store those that are/could be reduced
    self.out_msk = out_msk[-len(vv_v):].copy()

    if not reduce_sys:
        out_msk[-len(vv_v):] = False

    s_out_msk = out_msk[-len(vv_v):]

    if hasattr(self, 'P'):
        if self.P.shape[0] < sum(~s_out_msk):
            P_new = np.zeros((len(self.out_msk), len(self.out_msk)))
            if P_new[~self.out_msk][:, ~self.out_msk].shape != self.P.shape:
                print(
                    '[get_sys:]'.ljust(15, ' ') +
                    ' Shape missmatch of P-matrix, number of states seems to differ!'
                )
            P_new[~self.out_msk][:, ~self.out_msk] = self.P
            self.P = P_new
        elif self.P.shape[0] > sum(~s_out_msk):
            self.P = self.P[~s_out_msk][:, ~s_out_msk]

    # add everything to the DSGE object
    self.vv = vv_v[~s_out_msk]
    self.vx = np.array([v.name for v in vv_x3])
    self.dim_x = dim_x
    self.dim_v = len(self.vv)

    self.hx = self.ZZ(ppar)[:, ~s_out_msk], self.DD(ppar).squeeze()
    self.obs_arg = np.where(self.hx[0])[1]

    N2 = N[~out_msk][:, ~out_msk]
    A2 = A[~out_msk][:, ~out_msk]
    J2 = J[:, ~out_msk]

    self.SIG = (BB.T @ D)[~s_out_msk]

    self.sys = N2, A2, J2, cx[~out_msk], b2[~out_msk], x_bar

    if verbose:
        print('[get_sys:]'.ljust(15, ' ') +
              ' Creation of system matrices finished in %ss.' %
              np.round(time.time() - st, 3))

    preprocess(self, self.lks[0], self.lks[1], verbose)

    if not ignore_tests:
        test_obj = self.precalc_mat[0][1, 0, 1]
        test_con = eig(test_obj[-test_obj.shape[1]:]) > 1
        if test_con.any():
            raise ValueError('Explosive dynamics detected: %s EV(s) > 1' %
                             sum(test_con))

    return
Ejemplo n.º 5
0
def gen_sys(self, AA0, BB0, CC0, DD0, fb0, fc0, fd0, ZZ0, ZZ1, l_max, k_max,
            get_hx_only, parallel, verbose):
    """Generate system matrices expressed in the one-sided, first-order compressed dimensionality reduction given a set of parameters. 

    Details can be found in "Efficient Solution of Models with Occasionally Binding Constraints" (Gregor Boehl).
    If no parameters are given this will default to the calibration in the `yaml` file.

    Parameters
    ----------
    par : array or list, optional
        The parameters to parse into the transition function. (defaults to calibration in `yaml`)
    l_max : int, optional
        The expected number of periods *until* the constraint binds (defaults to 3).
    k_max : int, optional
        The expected number of periods for which the constraint binds (defaults to 17).
    verbose : bool or int, optional
        Level of verbosity
    """

    st = time.time()

    # set default values of l_max & k_max
    if l_max is not None:
        if l_max < 2 and k_max > 0:
            print('[get_sys:]'.ljust(15, ' ') +
                  ' `l_max` must be at least 2 (is %s). Correcting...' % l_max)
            l_max = 2
        # effective l_max is one lower because algorithm exists on l_max
        l_max += 1
        # TODO: test if true

    elif hasattr(self, 'lks'):
        l_max = self.lks[0]
    else:
        l_max = 3

    if k_max is not None:
        pass
    elif hasattr(self, 'lks'):
        k_max = self.lks[1]
    else:
        k_max = 17

    self.lks = np.array([l_max, k_max])

    # start
    vv0 = self.vv

    # z-space is the space of the original variables
    dimx = len(vv0)
    dimeps = len(self.shocks)

    # y-space is the space of the original variables augmented by the shocks
    c_arg = list(vv0).index(str(self.const_var))
    fc0 = -fc0 / fb0[c_arg]
    fb0 = -fb0 / fb0[c_arg]

    # create auxiliry vars for those both in A & C
    inall = ~fast0(AA0, 0) & ~fast0(CC0, 0)
    if np.any(inall):
        vv0 = np.hstack((vv0, [v + '_lag' for v in vv0[inall]]))
        AA0 = np.pad(AA0, ((0, sum(inall)), (0, sum(inall))))
        BB0 = np.pad(BB0, ((0, sum(inall)), (0, sum(inall))))
        CC0 = np.pad(CC0, ((0, sum(inall)), (0, sum(inall))))
        DD0 = np.pad(DD0, ((0, sum(inall)), (0, 0)))
        fb0 = np.pad(fb0, (0, sum(inall)))
        fc0 = np.pad(fc0, (0, sum(inall)))

        if ZZ0 is not None:
            ZZ0 = np.pad(ZZ0, ((0, 0), (0, sum(inall))))

        BB0[-sum(inall):, -sum(inall):] = np.eye(sum(inall))
        BB0[-sum(inall):, :-sum(inall)][:, inall] = -np.eye(sum(inall))
        CC0[:, -sum(inall):] = CC0[:, :-sum(inall)][:, inall]
        CC0[:, :-sum(inall)][:, inall] = 0

    # create representation in y-space
    AA0 = np.pad(AA0, ((0, dimeps), (0, dimeps)))
    BB0 = sl.block_diag(BB0, np.eye(dimeps))
    CC0 = np.block([[CC0, DD0], [np.zeros((dimeps, AA0.shape[1]))]])
    fb0 = np.pad(fb0, (0, dimeps))
    if fd0 is not None:
        fc0 = -np.hstack((fc0, fd0))
    else:
        fc0 = np.pad(fc0, (0, dimeps))

    inq = ~fast0(CC0, 0) | ~fast0(fc0)
    inp = (~fast0(AA0, 0) | ~fast0(BB0, 0)) & ~inq

    # check dimensionality
    dimq = sum(inq)
    dimp = sum(inp)

    # create hx. Do this early so that the procedure can be stopped if get_hx_only
    if ZZ0 is None:
        # must create dummies
        zp = np.empty(dimp)
        zq = np.empty(dimq)
        zc = np.empty(1)
    else:
        zp = ZZ0[:, inp[:-dimeps]]
        zq = ZZ0[:, inq[:-dimeps]]
        zc = ZZ1

    AA = np.pad(AA0, ((0, 1), (0, 0)))
    BBU = np.vstack((BB0, fb0))
    CCU = np.vstack((CC0, fc0))
    BBR = np.pad(BB0, ((0, 1), (0, 0)))
    CCR = np.pad(CC0, ((0, 1), (0, 0)))
    BBR[-1, list(vv0).index(str(self.const_var))] = -1

    fb0[list(vv0).index(str(self.const_var))] = 0

    self.svv = vv0[inq[:-dimeps]]
    self.cvv = vv0[inp[:-dimeps]]
    self.vv = np.hstack((self.cvv, self.svv))

    self.dimx = len(self.vv)
    self.dimq = dimq
    self.dimp = dimp
    self.dimy = dimp + dimq
    self.dimeps = dimeps
    self.hx = zp, zq, zc

    if get_hx_only:
        return self

    PU = -np.hstack((BBU[:, inq], AA[:, inp]))
    MU = np.hstack((CCU[:, inq], BBU[:, inp]))

    PR = -np.hstack((BBR[:, inq], AA[:, inp]))
    MR = np.hstack((CCR[:, inq], BBR[:, inp]))
    gg = np.pad([float(self.x_bar)], (dimp + dimq - 1, 0))

    # avoid QL in jitted funcs
    R, Q = sl.rq(MU.T)
    MU = R.T
    PU = Q @ aca(PU)

    R, Q = sl.rq(MR.T)
    MR = R.T
    PR = Q @ aca(PR)
    gg = Q @ gg

    solver = 'klein'  # to be implemented

    if solver == 'speed_kills':
        omg, lam = speed_kills(PU, MU, dimp, dimq, tol=1e-4)
    else:
        omg, lam = klein(PU, MU, nstates=dimq, verbose=verbose, force=False)

    # finally add relevant stuff to the class

    fq0 = fc0[inq]
    fp1 = fb0[inp]
    fq1 = fb0[inq]

    self.sys = omg, lam, self.x_bar
    self.ff = fq1, fp1, fq0

    # preprocess all system matrices until (l_max, k_max)
    preprocess(self, PU, MU, PR, MR, gg, fq1, fp1, fq0, parallel, verbose)

    if verbose:
        print('[get_sys:]'.ljust(15, ' ') +
              ' Creation of system matrices finished in %ss.' %
              np.round(time.time() - st, 3))

    return self