Пример #1
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
Пример #2
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
Пример #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