Example #1
0
def qz_solver(B, A):
    # A - \lambda B = Q(S - \lambda T)Z*
    S, T, Q, Z = la.qz(A=A, B=B)
    eigenvalues = np.array(S).diagonal() / np.array(T).diagonal()

    # TODO: Calculate eigenvectors
    return eigenvalues, Q
Example #2
0
File: qz.py Project: npalmer/dolo
def qzordered(A,B,m_states):

    TOL = 1e-10
    from numpy import real_if_close,where


    from scipy.linalg import qz
    # [S,T,Q,Z,nev] = qz_scipy(A,B,sort='ouc') # wait until sorted output is activated in scipy
    [S,T,Q,Z] = qz(A,B)

    S,T,Q,Z = [real_if_close(mm) for mm in (S,T,Q,Z)]
    u = np.diag(S)
    v = np.diag(T)
    eigval = v / where( u >TOL, u, TOL)
    sortindex = abs(eigval).argsort()
    # (Xi_sortabs doesn't really seem to be needed)
    sortval = eigval[sortindex]
    select = slice(0, m_states)
    stake = (abs(sortval[select])).max() + TOL
    S,T,Q,Z = qzdiv(stake,S,T,Q,Z)
    return [S,T,Q,Z,eigval]
Example #3
0
def qz_BB2roots(bb):
    n = len(bb)-1
    bbt = [bb[k].T for k in range(n+1)] # transposition ! dim x DIM
    dim, DIM = bbt[0].shape
    q, r, p = la.qr(bbt[0], pivoting=True)
    qr_BB = []
    for k in range(n+1):
        qb = q.T.dot(bbt[k])
        qbp = qb[:, p]
        qr_BB.append(qbp[:, 0:dim])
    chow_mat = np.zeros((dim, dim))
    for k in range(n):
        chow_mat += np.random.randn()*qr_BB[k+1]
    b0 = qr_BB[0]
    chowchow, b0b0, Q, Z = la.qz(chow_mat, b0, output='complex')
    qzBB = []
    for k in range(n+1):
        qzBB.append( Q.T.conjugate().dot(qr_BB[k]).dot(Z) )
    roots = np.zeros((dim, n), dtype=complex)
    for j in range(n):
        roots[:, j] = np.diag(qzBB[j+1])/np.diag(qzBB[0])
    return roots
Example #4
0
def qzordered(A, B):
    """
    QZORDERED QZ decomposition ordered by the absolute value of the generalized eigenvalues
    See QZ

    Based on code by Pedro Oviedo & Chris Sims
    """
    n = A.shape[0]
    S, T, Q, Z = qz(A, B)
    Q = Q.T

    i = 0
    while i < n - 1:
        if abs(T[i, i] * S[i+1, i+1]) > abs(S[i, i] * T[i+1, i+1]):
            qzswitch(i,S, T, Q, Z)
            if i > 0:
                i -=1
            else:
                i += 1
        else:
            i += 1

    return S, T, Q, Z
Example #5
0



# DNT ==== First-order derivatives of the functions g and h ===================


        stake=1.0

        #Create system matrices A,B
        AA = c_[-nfxp,-nfyp]
        BB = c_[nfx,nfy]
        NK = nfx.shape[1]

        #Complex Schur Decomposition
        ss,tt,qq,zz = linalg.qz(AA,BB);

        #Pick non-explosive (stable) eigenvalues
        slt = (abs(diag(tt))<stake*abs(diag(ss)));
        noslt=logical_not(slt)
        nk=sum(slt);



        # Prep for QZ decomposition- qzswitch()

        def qzswitch(i,ss,tt,qq,zz):
            ssout = ss.copy(); ttout = tt.copy(); qqout = qq.copy(); zzout = zz.copy()
            ix = i-1 # from 1-based to 0-based indexing...

            # use all 1x1-matrices for convenient conjugate-transpose even if real:
Example #6
0
def _solve_discrete_generalized_lyapunov(A, E, Y, tol=1e-12):
    '''
    Solves

                A.T X A - E.T X E + Y = 0

    for symmetric Y
    '''
    mat33 = np.zeros((3, 3), dtype=float)
    mat44 = np.zeros((4, 4), dtype=float)

    def mini_sylvester(S, V, Yt, R=None, U=None):
        '''
        A helper function to solve the 1x1 or 2x2 Sylvester equations
        arising in the solution of the generalized continuous-time
        Lyapunov equations

        Note that, this doesn't have any protection against LinAlgError
        hence the caller needs to `try` to see whether it is properly
        executed.
        '''
        if R is None:
            if S.size == 1:
                return -Yt / (S ** 2 - V ** 2)
            else:
                a, b, c, d = S.ravel().tolist()
                e, f, g, h = V.ravel().tolist()

                mat33[0, :] = [a*a - e*e, 2 * (a*c - e*g), c*c - g*g]
                mat33[1, :] = [a*b - e*f, a*d - e*h + c*b - g*f, c*d - g*h]
                mat33[2, :] = [b*b - f*f, 2 * (b*d - f*h), d*d - h*h]

                a, b, c = solve(mat33, -Yt.reshape(-1, 1)[[0, 1, 3], :]
                                ).ravel().tolist()

                return np.array([[a, b], [b, c]], dtype=float)

        elif S.size == 4:
            if R.size == 4:
                a, b, c, d = R.ravel().tolist()
                e, f, g, h = S.ravel().tolist()
                k, l, m, n = U.ravel().tolist()
                p, q, r, s = V.ravel().tolist()

                mat44[0, :] = [a*e - k*p, a*g - k*r, c*e - m*p, c*g - m*r]
                mat44[1, :] = [a*f - k*q, a*h - k*s, c*f - m*q, c*h - m*s]
                mat44[2, :] = [b*e - l*p, b*g - l*r, d*e - n*p, d*g - n*r]
                mat44[3, :] = [b*f - l*q, b*h - l*s, d*f - n*q, d*h - n*s]

                return solve(mat44, -Yt.reshape(-1, 1)).reshape(2, 2)

            else:
                return solve(R[0, 0]*S.T - U[0, 0]*V.T, -Yt.T).T
        elif R.size == 4:
            return solve(S[0, 0]*R.T - V[0, 0]*U.T, -Yt)
        else:
            return -Yt / (R * S - U * V)

    # =============================
    # Prepare the data
    # =============================
    # if the problem is small then solve directly
    if A.shape[0] < 3:
        return mini_sylvester(A, E, Y)

    As, Es, Q, Z = qz(A, E, overwrite_a=True, overwrite_b=True)
    Ys = Z.T @ Y @ Z
    n = As.shape[0]
    # If there are nontrivial entries on the subdiagonal, we have a 2x2 block.
    # Based on that we have the block sizes `bz` and starting positions `bs`.

    subdiag_entries = np.abs(As[range(1, n), range(0, n-1)]) > tol
    subdiag_indices = [ind for ind, x in enumerate(subdiag_entries) if x]
    bz = np.ones(n)
    for x in subdiag_indices:
        bz[x] = 2
        bz[x+1] = np.nan

    bz = bz[~np.isnan(bz)].astype(int)
    bs = [0] + np.cumsum(bz[:-1]).tolist() + [None]
    total_blk = bz.size
    Xs = np.empty_like(Y)

    # =============================
    #  Main Loop
    # =============================

    # Now we know how the matrices should be partitioned. We then start
    # from the uppper left corner and alternate between updating the
    # Y term and solving the next entry of X. We walk over X row-wise

    for row in range(total_blk):

        thisr = bs[row]
        nextr = bs[row+1]

        # This block is executed at the second and further spins of the
        # for loop. Humans should start reading from (**)
        if row is not 0:
            Ys[thisr:nextr, thisr:nextr] +=  \
                As[thisr:nextr, thisr:nextr].T @ \
                Xs[thisr:nextr, :thisr] @ \
                As[:thisr, thisr:nextr] - \
                Es[thisr:nextr, thisr:nextr].T @ \
                Xs[thisr:nextr, :thisr] @ \
                Es[:thisr, thisr:nextr]

        # (**) Solve for the diagonal via Akk , Ekk , Ykk and place it in Xkk
        tempx = mini_sylvester(As[thisr:nextr, thisr:nextr],
                               Es[thisr:nextr, thisr:nextr],
                               Ys[thisr:nextr, thisr:nextr])

        # Place it in the data
        Xs[thisr:nextr, thisr:nextr] = tempx

        # Form the common products of X * E and X * A
        tempx = Xs[thisr:nextr, :nextr]
        XE_of_row = tempx @ Es[:nextr, thisr:]
        XA_of_row = tempx @ As[:nextr, thisr:]

        # Update Y terms right of the diagonal
        Ys[thisr:nextr, thisr:] += \
            As[thisr:nextr, thisr:nextr].T @ XA_of_row - \
            Es[thisr:nextr, thisr:nextr].T @ XE_of_row

        # Walk over upper triangular terms
        for col in range(row + 1, total_blk):

            thisc = bs[col]
            nextc = bs[col+1]

            # The corresponding Y term has already been updated, solve for X
            tempx = mini_sylvester(As[thisc:nextc, thisc:nextc],
                                   Es[thisc:nextc, thisc:nextc],
                                   Ys[thisr:nextr, thisc:nextc],
                                   As[thisr:nextr, thisr:nextr],
                                   Es[thisr:nextr, thisr:nextr])

            # Place it in the data
            Xs[thisr:nextr, thisc:nextc] = tempx
            Xs[thisc:nextc, thisr:nextr] = tempx.T

            # Post column solution Y update

            # XA and XE terms
            tempe = tempx @ Es[thisc:nextc, thisc:]
            tempa = tempx @ As[thisc:nextc, thisc:]
            # Update Y towards left
            Ys[thisr:nextr, thisc:] += \
                As[thisr:nextr, thisr:nextr].T @ tempa - \
                Es[thisr:nextr, thisr:nextr].T @ tempe
            # Update Y downwards
            XE_of_row[:, (thisc - thisr):] += tempe
            XA_of_row[:, (thisc - thisr):] += tempa

            ugly_sl = slice(thisc - thisr,
                            nextc - thisr if nextc is not None else None)

            Ys[nextr:nextc, thisc:nextc] += \
                As[thisr:nextr, nextr:nextc].T @ XA_of_row[:, ugly_sl] - \
                Es[thisr:nextr, nextr:nextc].T @ XE_of_row[:, ugly_sl]

    return Q @ Xs @ Q.T
for j in range(len(V)):
	First_Derivatives.append([nFx[i][j] for i in range(len(F))])

nfxp = First_Derivatives[2]
nfyp = First_Derivatives[3]
nfx  = First_Derivatives[0]
nfy  = First_Derivatives[1]

nfxp = np.asarray(nfxp)

A  = np.concatenate((nfxp,nfyp),axis=1)
A  = (-1)*A
B  = np.concatenate((nfx,nfy),axis=1)
NK = np.shape(nfx)[1]

s, t, q, z = linalg.qz(-A, B)

stake = 1

slt = abs(np.diag(t)) < stake*abs(np.diag(s))
nk = sum(slt)

def qzdiv(stake, A, B, Q, Z):
	# translation of qzdiv by Chris Sims
	# Takes U.T. matrices A, B, orthonormal matrices Q,Z, rearranges them
	# so that all cases of abs(B(i,i)/A(i,i))>stake are in lower right 
	# corner, while preserving U.T. and orthonormal properties and Q'AZ' and
	# Q'BZ'.
	[n, jnk] = np.shape(A)
	root = np.asarray([abs(np.diag(A)), abs(np.diag(B))])
	root[0] = root[0] - 1 * (root[0] < 1.e-13) * (root[0] + root[1])
Example #8
0
def solve_sylv_schur(A, Ar, E=None, Er=None, B=None, Br=None, C=None, Cr=None):
    r"""Solve Sylvester equation by Schur decomposition.

    Solves Sylvester equation

    .. math::
        A V E_r^T + E V A_r^T + B B_r^T = 0

    or

    .. math::
        A^T W E_r + E^T W A_r + C^T C_r = 0

    or both using (generalized) Schur decomposition (Algorithms 3 and 4
    in [BKS11]_), if the necessary parameters are given.

    Parameters
    ----------
    A
        Real |Operator|.
    Ar
        Real |Operator|.
        It is converted into a |NumPy array| using
        :func:`~pymor.algorithms.to_matrix.to_matrix`.
    E
        Real |Operator| or `None` (then assumed to be the identity).
    Er
        Real |Operator| or `None` (then assumed to be the identity).
        It is converted into a |NumPy array| using
        :func:`~pymor.algorithms.to_matrix.to_matrix`.
    B
        Real |Operator| or `None`.
    Br
        Real |Operator| or `None`.
        It is assumed that `Br.range.from_numpy` is implemented.
    C
        Real |Operator| or `None`.
    Cr
        Real |Operator| or `None`.
        It is assumed that `Cr.source.from_numpy` is implemented.

    Returns
    -------
    V
        Returned if `B` and `Br` are given, |VectorArray| from
        `A.source`.
    W
        Returned if `C` and `Cr` are given, |VectorArray| from
        `A.source`.

    Raises
    ------
    ValueError
        If `V` and `W` cannot be returned.
    """
    # check types
    assert isinstance(A, OperatorInterface) and A.linear and A.source == A.range
    assert isinstance(Ar, OperatorInterface) and Ar.linear and Ar.source == Ar.range

    assert E is None or isinstance(E, OperatorInterface) and E.linear and E.source == E.range == A.source
    if E is None:
        E = IdentityOperator(A.source)
    assert Er is None or isinstance(Er, OperatorInterface) and Er.linear and Er.source == Er.range == Ar.source

    compute_V = B is not None and Br is not None
    compute_W = C is not None and Cr is not None

    if not compute_V and not compute_W:
        raise ValueError('Not enough parameters are given to solve a Sylvester equation.')

    if compute_V:
        assert isinstance(B, OperatorInterface) and B.linear and B.range == A.source
        assert isinstance(Br, OperatorInterface) and Br.linear and Br.range == Ar.source
        assert B.source == Br.source

    if compute_W:
        assert isinstance(C, OperatorInterface) and C.linear and C.source == A.source
        assert isinstance(Cr, OperatorInterface) and Cr.linear and Cr.source == Ar.source
        assert C.range == Cr.range

    # convert reduced operators
    Ar = to_matrix(Ar, format='dense')
    r = Ar.shape[0]
    if Er is not None:
        Er = to_matrix(Er, format='dense')

    # (Generalized) Schur decomposition
    if Er is None:
        TAr, Z = spla.schur(Ar, output='complex')
        Q = Z
    else:
        TAr, TEr, Q, Z = spla.qz(Ar, Er, output='complex')

    # solve for V, from the last column to the first
    if compute_V:
        V = A.source.empty(reserve=r)

        BrTQ = Br.apply_adjoint(Br.range.from_numpy(Q.T))
        BBrTQ = B.apply(BrTQ)
        for i in range(-1, -r - 1, -1):
            rhs = -BBrTQ[i].copy()
            if i < -1:
                if Er is not None:
                    rhs -= A.apply(V.lincomb(TEr[i, :i:-1].conjugate()))
                rhs -= E.apply(V.lincomb(TAr[i, :i:-1].conjugate()))
            TErii = 1 if Er is None else TEr[i, i]
            eAaE = TErii.conjugate() * A + TAr[i, i].conjugate() * E
            V.append(eAaE.apply_inverse(rhs))

        V = V.lincomb(Z.conjugate()[:, ::-1])
        V = V.real

    # solve for W, from the first column to the last
    if compute_W:
        W = A.source.empty(reserve=r)

        CrZ = Cr.apply(Cr.source.from_numpy(Z.T))
        CTCrZ = C.apply_adjoint(CrZ)
        for i in range(r):
            rhs = -CTCrZ[i].copy()
            if i > 0:
                if Er is not None:
                    rhs -= A.apply_adjoint(W.lincomb(TEr[:i, i]))
                rhs -= E.apply_adjoint(W.lincomb(TAr[:i, i]))
            TErii = 1 if Er is None else TEr[i, i]
            eAaE = TErii.conjugate() * A + TAr[i, i].conjugate() * E
            W.append(eAaE.apply_inverse_adjoint(rhs))

        W = W.lincomb(Q.conjugate())
        W = W.real

    if compute_V and compute_W:
        return V, W
    elif compute_V:
        return V
    else:
        return W
Example #9
0
def second_order_solver(FF,GG,HH):

    from scipy.linalg import qz
    from dolo.numeric.extern.qz import qzdiv
    
    from numpy import array,mat,c_,r_,eye,zeros,real_if_close,diag,allclose,where,diagflat
    from numpy.linalg import solve
    
    Psi_mat = array(FF)
    Gamma_mat = array(-GG)
    Theta_mat = array(-HH)
    m_states = FF.shape[0]
        
    Xi_mat = r_[c_[Gamma_mat, Theta_mat],
                c_[eye(m_states), zeros((m_states, m_states))]]

        
    Delta_mat = r_[c_[Psi_mat, zeros((m_states, m_states))], 
                   c_[zeros((m_states, m_states)), eye(m_states)]]


    AAA,BBB,Q,Z = qz(Delta_mat, Xi_mat)
        
    Delta_up,Xi_up,UUU,VVV = [real_if_close(mm) for mm in (AAA,BBB,Q,Z)]
       
    Xi_eigval = diag(Xi_up)/where(diag(Delta_up)>TOL, diag(Delta_up), TOL)

    Xi_sortindex = abs(Xi_eigval).argsort()
    # (Xi_sortabs doesn't really seem to be needed)
    
    Xi_sortval = Xi_eigval[Xi_sortindex]
    
    Xi_select = slice(0, m_states)
    
    stake = (abs(Xi_sortval[Xi_select])).max() + TOL
    
    Delta_up,Xi_up,UUU,VVV = qzdiv(stake,Delta_up,Xi_up,UUU,VVV)

    
    try:
        # check that all unused roots are unstable
        assert abs(Xi_sortval[m_states]) > (1-TOL)
        # check that all used roots are stable
        assert abs(Xi_sortval[Xi_select]).max() < 1+TOL
    except:
        raise BKError('generic')


    
    # check for unit roots anywhere

#    assert (abs((abs(Xi_sortval) - 1)) > TOL).all()

    Lambda_mat = diagflat(Xi_sortval[Xi_select])
    VVVH = VVV.T
    VVV_2_1 = VVVH[m_states:2*m_states, :m_states]
    VVV_2_2 = VVVH[m_states:2*m_states, m_states:2*m_states]
    UUU_2_1 = UUU[m_states:2*m_states, :m_states]

    PP = - solve(VVV_2_1, VVV_2_2)
    
    # slightly different check than in the original toolkit:
    assert allclose(real_if_close(PP), PP.real)
    PP = PP.real
    ## end of solve_qz!


    return [Xi_sortval[Xi_select],PP]
def LinApp_Solve(AA, BB, CC, DD, FF, GG, HH, JJ, KK, LL, MM, WWW, TT, NN, Z0,
                 Sylv):
    """
    This code takes Uhlig's original code and puts it in the form of a
    function.  This version outputs the policy function coefficients: PP,
    QQ and UU for X, and RR, SS and VV for Y.

    Inputs overview:
    The matrices of derivatives: AA - TT.
    The autoregression coefficient matrix NN from the law of motion for Z.
    Z0 is the Z-point about which the linearization is taken.  For
    linearizing about the steady state this is Zbar and normally Zbar = 0.
    Sylv is an indicator variable telling the program to use the built-in
    function sylvester() to solve for QQ and SS, if possible.  Default is
    to use Sylv=1.

    Parameters
    ----------
    AA : array_like, dtype=float, shape=(ny, nx)
        The matrix represented above by :math:`A`. It is the matrix of
        derivatives of the Y equations with repsect to :math:`X_t`
    BB : array_like, dtype=float, shape=(ny, nx)
        The matrix represented above by :math:`B`. It is the matrix of
        derivatives of the Y equations with repsect to
        :math:`X_{t-1}`.
    CC : array_like, dtype=float, shape=(ny, ny)
        The matrix represented above by :math:`C`. It is the matrix of
        derivatives of the Y equations with repsect to :math:`Y_t`
    DD : array_like, dtype=float, shape=(ny, nz)
        The matrix represented above by :math:`C`. It is the matrix of
        derivatives of the Y equations with repsect to :math:`Z_t`
    FF : array_like, dtype=float, shape=(nx, nx)
        The matrix represetned above by :math:`F`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`X_{t+1}`
    GG : array_like, dtype=float, shape=(nx, nx)
        The matrix represetned above by :math:`G`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`X_t`
    HH : array_like, dtype=float, shape=(nx, nx)
        The matrix represetned above by :math:`H`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`X_{t-1}`
    JJ : array_like, dtype=float, shape=(nx, ny)
        The matrix represetned above by :math:`J`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Y_{t+1}`
    KK : array_like, dtype=float, shape=(nx, ny)
        The matrix represetned above by :math:`K`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Y_t`
    LL : array_like, dtype=float, shape=(nx, nz)
        The matrix represetned above by :math:`L`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Z_{t+1}`
    MM : array_like, dtype=float, shape=(nx, nz)
        The matrix represetned above by :math:`M`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Z_t`
    WWW : array, dtype=float, shape=(ny,)
        The vector of the numberial errors of first ny characterizing
        equations
    TT : array, dtype=float, shape=(nx,)
        The vector of the numberial errors of the next nx characterizing
        equations following the first ny equations
    NN : array_like, dtype=float, shape=(nz, nz)
        The autocorrelation matrix for the exogenous state vector z.
    Z0 : array, dtype=float, shape=(nz,)
        the Z-point about which the linearization is taken.  For linearizing 
        about the steady state this is Zbar and normally Zbar = 0.
        QQ if true.
    Sylv: binary, dtype=int 
        an indicator variable telling the program to use the built-in
        function sylvester() to solve for QQ and SS, if possible.  Default is
        to use Sylv=1.

    Returns
    -------
    P : 2D-array, dtype=float, shape=(nx, nx)
        The matrix :math:`P` in the law of motion for endogenous state
        variables described above.
    Q : 2D-array, dtype=float, shape=(nx, nz)
        The matrix :math:`Q` in the law of motion for exogenous state
        variables described above.
    U : array, dtype=float, shape=(nx,)
        ??????????
    R : 2D-array, dtype=float, shape=(ny, nx)
        The matrix :math:`R` in the law of motion for endogenous state
        variables described above.
    S : 2D-array, dtype=float, shape=(ny, nz)
        The matrix :math:`S` in the law of motion for exogenous state
        variables described above.
    V : array, dtype=float, shape=(ny,)
        ???????????
    References
    ----------
    .. [1] Uhlig, H. (1999): "A toolkit for analyzing nonlinear dynamic
       stochastic models easily," in Computational Methods for the Study
       of Dynamic Economies, ed. by R. Marimon, pp. 30-61. Oxford
       University Press.

    """
    #The original coding we did used the np.matrix form for our matrices so we
    #make sure to set our inputs to numpy matrices.
    AA = np.matrix(AA)
    BB = np.matrix(BB)
    CC = np.matrix(CC)
    DD = np.matrix(DD)
    FF = np.matrix(FF)
    GG = np.matrix(GG)
    HH = np.matrix(HH)
    JJ = np.matrix(JJ)
    KK = np.matrix(KK)
    LL = np.matrix(LL)
    MM = np.matrix(MM)
    NN = np.matrix(NN)
    WWW = np.array(WWW)
    TT = np.array(TT)
    Z0 = np.array(Z0)
    #Tolerance level to use
    TOL = .000001

    # Here we use matrices to get pertinent dimensions.
    nx = FF.shape[1]
    l_equ = CC.shape[0]
    ny = CC.shape[1]
    nz = min(NN.shape)

    # The following if and else blocks form the
    # Psi, Gamma, Theta Xi, Delta mats
    if l_equ == 0:
        if CC.any():
            # This blcok makes sure you don't throw an error with an empty CC.
            CC_plus = la.pinv(CC)
            CC_0 = _nullSpaceBasis(CC.T)
        else:
            CC_plus = np.mat([])
            CC_0 = np.mat([])
        Psi_mat = FF
        Gamma_mat = -GG
        Theta_mat = -HH
        Xi_mat = np.mat(
            vstack((hstack(
                (Gamma_mat, Theta_mat)), hstack((eye(nx), zeros((nx, nx)))))))
        Delta_mat = np.mat(
            vstack((hstack((Psi_mat, zeros(
                (nx, nx)))), hstack((zeros((nx, nx)), eye(nx))))))

    else:
        CC_plus = la.pinv(CC)
        CC_0 = _nullSpaceBasis(CC.T)
        if l_equ != ny:
            Psi_mat = vstack((zeros((l_equ - ny, nx)), FF \
                            - dot(dot(JJ, CC_plus), AA)))
            Gamma_mat = vstack((dot(CC_0, AA), dot(dot(JJ, CC_plus), BB) \
                        - GG + dot(dot(KK, CC_plus), AA)))
            Theta_mat = vstack((dot(CC_0, BB), dot(dot(KK, CC_plus), BB) - HH))
        else:
            CC_inv = la.inv(CC)
            Psi_mat = FF - dot(JJ.dot(CC_inv), AA)
            Gamma_mat = dot(JJ.dot(CC_inv), BB) - GG + dot(dot(KK, CC_inv), AA)
            Theta_mat = dot(KK.dot(CC_inv), BB) - HH
        Xi_mat = vstack((hstack((Gamma_mat, Theta_mat)), \
                            hstack((eye(nx), zeros((nx, nx))))))
        Delta_mat = vstack((hstack((Psi_mat, np.mat(zeros((nx, nx))))),\
                                hstack((zeros((nx, nx)), eye(nx)))))

    # Now we need the generalized eigenvalues/vectors for Xi with respect to
    # Delta. That is eVals and eVecs below.

    eVals, eVecs = la.eig(Xi_mat, Delta_mat)
    if npla.matrix_rank(eVecs) < nx:
        print("Error: Xi is not diagonalizable, stopping...")

    # From here to line 158 we Diagonalize Xi, form Lambda/Omega and find P.
    else:
        Xi_sortabs = np.sort(abs(eVals))
        Xi_sortindex = np.argsort(abs(eVals))
        Xi_sortedVec = np.array([eVecs[:, i] for i in Xi_sortindex]).T
        Xi_sortval = eVals[Xi_sortindex]
        Xi_select = np.arange(0, nx)
        if np.imag(Xi_sortval[nx - 1]).any():
            if (abs(Xi_sortval[nx - 1] - sp.conj(Xi_sortval[nx])) < TOL):
                drop_index = 1
                cond_1 = (abs(np.imag(Xi_sortval[drop_index - 1])) > TOL)
                cond_2 = drop_index < nx
                while cond_1 and cond_2:
                    drop_index += 1
                if drop_index >= nx:
                    print("There is an error. Too many complex eigenvalues." +
                          " Quitting...")
                else:
                    print("Droping the lowest real eigenvalue. Beware of" +
                          " sunspots!")
                    Xi_select = np.array([np.arange(0, drop_index - 1),\
                                          np.arange(drop_index, nx + 1)])
        # Here Uhlig computes stuff if user chose "Manual roots" I skip it.
        if max(abs(Xi_sortval[Xi_select])) > 1 + TOL:
            print(
                "It looks like we have unstable roots. This might not work...")
        if abs(max(abs(Xi_sortval[Xi_select])) - 1) < TOL:
            print("Check the model to make sure you have a unique steady" +
                  " state we are having problems with convergence.")
        Lambda_mat = np.diag(Xi_sortval[Xi_select])
        Omega_mat = Xi_sortedVec[nx:2 * nx, Xi_select]

        if npla.matrix_rank(Omega_mat) < nx:
            print("Omega matrix is not invertible, Can't solve for P; we" +
                  " proceed with QZ-method instead.")

            #~~~~~~~~~ QZ-method codes from SOLVE_QZ ~~~~~~~~#
            Delta_up, Xi_up, UUU, VVV = la.qz(Delta_mat,
                                              Xi_mat,
                                              output='complex')
            UUU = UUU.T
            Xi_eigval = np.diag(
                np.diag(Xi_up) / np.maximum(np.diag(Delta_up), TOL))
            Xi_sortabs = np.sort(abs(np.diag(Xi_eigval)))
            Xi_sortindex = np.argsort(abs(np.diag(Xi_eigval)))
            Xi_sortval = Xi_eigval[Xi_sortindex, Xi_sortindex]
            Xi_select = np.arange(0, nx)
            stake = max(abs(Xi_sortval[Xi_select])) + TOL

            Delta_up, Xi_up, UUU, VVV = qzdiv(stake, Delta_up, Xi_up, UUU, VVV)

            #Check conditions from line 49-109
            if np.imag(Xi_sortval[nx - 1]).any():
                if (abs(Xi_sortval[nx - 1] - sp.conj(Xi_sortval[nx])) < TOL):
                    print(
                        "Problem: You have complex eigenvalues! And this means"
                        +
                        " PP matrix will contain complex numbers by this method."
                    )
                drop_index = 1
                cond_1 = (abs(np.imag(Xi_sortval[drop_index - 1])) > TOL)
                cond_2 = drop_index < nx
                while cond_1 and cond_2:
                    drop_index += 1
                if drop_index >= nx:
                    print("There is an error. Too many complex eigenvalues." +
                          " Quitting...")
                else:
                    print("Dropping the lowest real eigenvalue. Beware of" +
                          " sunspots!")
                    for i in xrange(drop_index, nx + 1):
                        Delta_up, Xi_up, UUU, VVV = qzswitch(
                            i, Delta_up, Xi_up, UUU, VVV)
                    Xi_select1 = np.arange(0, drop_index - 1)
                    Xi_select = np.append(Xi_select1,
                                          np.arange(drop_index, nx + 1))

            if Xi_sortval[max(Xi_select)] < 1 - TOL:
                print('There are stable roots NOT used. Proceeding with the' +
                      ' smallest root.')
            if max(abs(Xi_sortval[Xi_select])) > 1 + TOL:
                print(
                    "It looks like we have unstable roots. This might not work..."
                )
            if abs(max(abs(Xi_sortval[Xi_select])) - 1) < TOL:
                print("Check the model to make sure you have a unique steady" +
                      " state we are having problems with convergence.")
            #End of checking conditions
            #Lambda_mat = np.diag(Xi_sortval[Xi_select]) # to help sol_out.m

            VVV = VVV.conj().T
            VVV_2_1 = VVV[nx:2 * nx, 0:nx]
            VVV_2_2 = VVV[nx:2 * nx, nx:2 * nx]
            UUU_2_1 = UUU[nx:2 * nx, 0:nx]
            VVV = VVV.conj().T

            if abs(la.det(UUU_2_1)) < TOL:
                print(
                    "One necessary condition for computing P is NOT satisfied,"
                    + " but we proceed anyways...")
            if abs(la.det(VVV_2_1)) < TOL:
                print(
                    "VVV_2_1 matrix, used to compute for P, is not invertible; we"
                    + " are in trouble but we proceed anyways...")

            PP = np.matrix(la.solve(-VVV_2_1, VVV_2_2))
            PP_imag = np.imag(PP)
            PP = np.real(PP)
            if (sum(sum(abs(PP_imag))) / sum(sum(abs(PP))) > .000001).any():
                print(
                    "A lot of P is complex. We will continue with the" +
                    " real part and hope we don't lose too much information.")
            #~~~~~~~~~ End of QZ-method ~~~~~~~~~#

        #This follows the original uhlig.py file
        else:
            PP = dot(dot(Omega_mat, Lambda_mat), la.inv(Omega_mat))
            PP_imag = np.imag(PP)
            PP = np.real(PP)
            if (sum(sum(abs(PP_imag))) / sum(sum(abs(PP))) > .000001).any():
                print(
                    "A lot of P is complex. We will continue with the" +
                    " real part and hope we don't lose too much information.")
    # The code from here to the end was from he Uhlig file calc_qrs.m.
    # I think for python it fits better here than in a separate file.

    # The if and else below make RR and VV depending on our model's setup.
    if l_equ == 0:
        RR = zeros((0, nx))
        VV = hstack((kron(NN.T, FF) + kron(eye(nz), \
            (dot(FF, PP) + GG)), kron(NN.T, JJ) + kron(eye(nz), KK)))

    else:
        RR = -dot(CC_plus, (dot(AA, PP) + BB))
        VV = sp.vstack((hstack((kron(eye(nz), AA), \
                        kron(eye(nz), CC))), hstack((kron(NN.T, FF) +\
                        kron(eye(nz), dot(FF, PP) + dot(JJ, RR) + GG),\
                        kron(NN.T, JJ) + kron(eye(nz), KK)))))

    # Now we use LL, NN, RR, VV to get the QQ, RR, SS, VV matrices.
    # first try using Sylvester equation solver
    if ny > 0:
        PM = (FF - la.solve(JJ.dot(CC), AA))
        if npla.matrix_rank(PM) < nx + ny:
            Sylv = 0
            print("Sylvester equation solver condition is not satisfied;"\
                    +" proceed with the original method...")
    else:
        if npla.matrix_rank(FF) < nx:
            Sylv = 0
            print("Sylvester equation solver condition is not satisfied;"\
                    +" proceed with the original method...")
    if Sylv:
        print("Using Sylvester equation solver...")
        if ny > 0:
            Anew = la.solve(PM, (FF.dot(PP)+GG+JJ.dot(RR)-\
                    la.solve(KK.dot(CC), AA)) )
            Bnew = NN
            Cnew1 = la.solve(JJ.dot(CC),DD.dot(NN))+la.solve(KK.dot(CC), DD)-\
                    LL.dot(NN)-MM
            Cnew = la.solve(PM, Cnew1)
            QQ = la.solve_sylvester(Anew, Bnew, Cnew)
            SS = la.solve(-CC, (AA.dot(QQ) + DD))
        else:
            Anew = la.solve(FF, (FF.dot(PP) + GG))
            Bnew = NN
            Cnew = la.solve(FF, (-LL.dot(NN) - MM))
            QQ = la.solve_sylvester(Anew, Bnew, Cnew)
            SS = np.zeros((0, nz))  #empty matrix
    # then the Uhlig's way
    else:
        if (npla.matrix_rank(VV) < nz * (nx + ny)):
            print("Sorry but V is not invertible. Can't solve for Q and S;" +
                  " but we proceed anyways...")

        LL = sp.mat(LL)
        NN = sp.mat(NN)
        LLNN_plus_MM = dot(LL, NN) + MM

        if DD.any():
            impvec = vstack(
                [DD.T, np.reshape(LLNN_plus_MM, (nx * nz, 1), 'F')])
        else:
            impvec = np.reshape(LLNN_plus_MM, (nx * nz, 1), 'F')

        QQSS_vec = np.matrix(la.solve(-VV, impvec))

        if (max(abs(QQSS_vec)) == sp.inf).any():
            print("We have issues with Q and S. Entries are undefined." +
                  " Probably because V is no inverible.")

        #Build QQ SS
        QQ = np.reshape(np.matrix(QQSS_vec[0:nx * nz, 0]), (nx, nz), 'F')

        SS = np.reshape(QQSS_vec[(nx * nz):((nx + ny) * nz), 0],\
                            (ny, nz), 'F')

    #Build WW - WW has the property [x(t)',y(t)',z(t)']=WW [x(t)',z(t)'].
    WW = sp.vstack(
        (hstack((eye(nx), zeros((nx, nz)))),
         hstack((dot(RR, la.pinv(PP)), (SS - dot(dot(RR, la.pinv(PP)), QQ)))),
         hstack((zeros((nz, nx)), eye(nz)))))

    # find constant terms
    # redefine matrix to be 2D-array for generating vectors UU and VVV
    AA = np.array(AA)
    CC = np.array(CC)
    FF = np.array(FF)
    GG = np.array(GG)
    JJ = np.array(JJ)
    KK = np.array(KK)
    LL = np.array(LL)
    NN = np.array(NN)
    RR = np.array(RR)
    QQ = np.array(QQ)
    SS = np.array(SS)
    if ny > 0:
        UU1 = -(FF.dot(PP) + GG + JJ.dot(RR) + FF -
                (JJ + KK).dot(la.solve(CC, AA)))
        UU2 = (TT+(FF.dot(QQ)+JJ.dot(SS)+LL).dot(NN.dot(Z0)-Z0)- \
            (JJ+KK).dot(la.solve(CC,WWW)))
        UU = la.solve(UU1, UU2)
        VVV = la.solve(-CC, (WWW + AA.dot(UU)))
    else:
        UU = la.solve(-(FF.dot(PP) + FF + GG),
                      (TT + (FF.dot(QQ) + LL).dot(NN.dot(Z0) - Z0)))
        VVV = np.array([])

    return np.array(PP), np.array(QQ), np.array(UU), np.array(RR), np.array(SS),\
             np.array(VVV)
def SGU_solver(Xss, Yss, Gamma_state, Gamma_control, InvGamma, Copula, par,
               mpar, grid, targets, P_H, aggrshock, oc):  #

    State = np.zeros((mpar['numstates'], 1))
    State_m = State.copy()
    Contr = np.zeros((mpar['numcontrols'], 1))
    Contr_m = Contr.copy()

    #        F = lambda S, S_m, C, C_m : Fsys(S, S_m, C, C_m,
    #                                         Xss,Yss,Gamma_state,Gamma_control,InvGamma,
    #                                         self.Copula,self.par,self.mpar,self.grid,self.targets,self.P_H,aggrshock,oc)
    F = lambda S, S_m, C, C_m: Fsys(S, S_m, C, C_m, Xss, Yss, Gamma_state,
                                    Gamma_control, InvGamma, Copula, par, mpar,
                                    grid, targets, P_H, aggrshock, oc)

    start_time = time.clock()
    result_F = F(State, State_m, Contr, Contr_m)
    end_time = time.clock()
    print('Elapsed time is ', (end_time - start_time), ' seconds.')
    Fb = result_F['Difference']

    pool = cpu_count() / 2 - 1

    F1 = np.zeros((mpar['numstates'] + mpar['numcontrols'], mpar['numstates']))
    F2 = np.zeros(
        (mpar['numstates'] + mpar['numcontrols'], mpar['numcontrols']))
    F3 = np.zeros((mpar['numstates'] + mpar['numcontrols'], mpar['numstates']))
    F4 = np.asmatrix(
        np.vstack((np.zeros((mpar['numstates'], mpar['numcontrols'])),
                   np.eye(mpar['numcontrols'], mpar['numcontrols']))))

    print('Use Schmitt Grohe Uribe Algorithm')
    print(' A *E[xprime uprime] =B*[x u]')
    print(' A = (dF/dxprimek dF/duprime), B =-(dF/dx dF/du)')

    numscale = 1
    pnum = pool
    packagesize = int(ceil(mpar['numstates'] / float(3 * pnum)))
    blocks = int(ceil(mpar['numstates'] / float(packagesize)))

    par['scaleval1'] = 1e-9
    par['scaleval2'] = 1e-4

    start_time = time.clock()
    print('Computing Jacobian F1=DF/DXprime F3 =DF/DX')
    print('Total number of parallel blocks: ', str(blocks), '.')

    FF1 = []
    FF3 = []

    for bl in range(0, blocks):
        range_ = range(bl * packagesize,
                       min(packagesize * (bl + 1), mpar['numstates']))
        DF1 = np.asmatrix(np.zeros((len(Fb), len(range_))))
        DF3 = np.asmatrix(np.zeros((len(Fb), len(range_))))
        cc = np.zeros((mpar['numcontrols'], 1))
        ss = np.zeros((mpar['numstates'], 1))
        for Xct in range_:
            X = np.zeros((mpar['numstates'], 1))
            h = par['scaleval1']
            X[Xct] = h
            Fx = F(ss, X, cc, cc)
            DF3[:, Xct - bl * packagesize] = (Fx['Difference'] - Fb) / h
            Fx = F(X, ss, cc, cc)
            DF1[:, Xct - bl * packagesize] = (Fx['Difference'] - Fb) / h
        if sum(range_ == mpar['numstates'] - 2) == 1:
            Xct = mpar['numstates'] - 2
            X = np.zeros((mpar['numstates'], 1))
            h = par['scaleval2']
            X[Xct] = h
            Fx = F(ss, X, cc, cc)
            DF3[:, Xct - bl * packagesize] = (Fx['Difference'] - Fb) / h
            Fx = F(X, ss, cc, cc)
            DF1[:, Xct - bl * packagesize] = (Fx['Difference'] - Fb) / h
        if sum(range_ == mpar['numstates'] - 1) == 1:
            Xct = mpar['numstates'] - 1
            X = np.zeros((mpar['numstates'], 1))
            h = par['scaleval2']
            X[Xct] = h
            Fx = F(ss, X, cc, cc)
            DF3[:, Xct - bl * packagesize] = (Fx['Difference'] - Fb) / h
            Fx = F(X, ss, cc, cc)
            DF1[:, Xct - bl * packagesize] = (Fx['Difference'] - Fb) / h
        FF1.append(DF1.copy())
        FF3.append(DF3.copy())
        print('Block number: ', str(bl), ' done.')

    for i in range(0, int(ceil(mpar['numstates'] / float(packagesize)))):
        range_ = range(i * packagesize,
                       min(packagesize * (i + 1), mpar['numstates']))
        F1[:, range_] = FF1[i]
        F3[:, range_] = FF3[i]

    end_time = time.clock()
    print('Elapsed time is ', (end_time - start_time), ' seconds.')

    # jacobian wrt Y'
    packagesize = int(ceil(mpar['numcontrols'] / (3.0 * pnum)))
    blocks = int(ceil(mpar['numcontrols'] / float(packagesize)))
    print('Computing Jacobian F2 - DF/DYprime')
    print('Total number of parallel blocks: ', str(blocks), '.')

    FF = []

    start_time = time.clock()

    for bl in range(0, blocks):
        range_ = range(bl * packagesize,
                       min(packagesize * (bl + 1), mpar['numcontrols']))
        DF2 = np.asmatrix(np.zeros((len(Fb), len(range_))))
        cc = np.zeros((mpar['numcontrols'], 1))
        ss = np.zeros((mpar['numstates'], 1))
        for Yct in range_:
            Y = np.zeros((mpar['numcontrols'], 1))
            h = par['scaleval2']
            Y[Yct] = h
            Fx = F(ss, ss, Y, cc)
            DF2[:, Yct - bl * packagesize] = (Fx['Difference'] - Fb) / h
        FF.append(DF2.copy())
        print('Block number: ', str(bl), ' done.')

    for i in range(0, int(ceil(mpar['numcontrols'] / float(packagesize)))):
        range_ = range(i * packagesize,
                       min(packagesize * (i + 1), mpar['numcontrols']))
        F2[:, range_] = FF[i]

    end_time = time.clock()
    print('Elapsed time is ', (end_time - start_time), ' seconds.')

    FF = []
    FF1 = []
    FF3 = []

    cc = np.zeros((mpar['numcontrols'], 1))
    ss = np.zeros((mpar['numstates'], 1))

    for Yct in range(0, oc):
        Y = np.zeros((mpar['numcontrols'], 1))
        h = par['scaleval2']
        Y[-1 - Yct] = h
        Fx = F(ss, ss, cc, Y)
        F4[:, -1 - Yct] = (Fx['Difference'] - Fb) / h

    s, t, Q, Z = linalg.qz(np.hstack((F1, F2)),
                           -np.hstack((F3, F4)),
                           output='complex')

    relev = np.divide(abs(np.diag(s)), abs(np.diag(t)))
    ll = sorted(relev)
    slt = relev >= 1
    nk = sum(slt)
    slt = 1 * slt
    mpar['overrideEigen'] = 1

    if nk > mpar['numstates']:
        if mpar['overrideEigen']:
            print(
                'Warning: The Equilibrium is Locally Indeterminate, critical eigenvalue shifted to: ',
                str(ll[-1 - mpar['numstates']]))
            slt = relev > ll[-1 - mpar['numstates']]
            nk = sum(slt)
        else:
            print('No Local Equilibrium Exists, last eigenvalue: ',
                  str(ll[-1 - mpar['numstates']]))

    elif nk < mpar['numstates']:
        if mpar.overrideEigen:
            print(
                'Warning: No Local Equilibrium Exists, critical eigenvalue shifted to: ',
                str(ll[-1 - mpar['numstates']]))
            slt = relev > ll[-1 - mpar['numstates']]
            nk = sum(slt)
        else:
            print('No Local Equilibrium Exists, last eigenvalue: ',
                  str(ll[-1 - mpar['numstates']]))

    s_ord, t_ord, __, __, __, Z_ord = linalg.ordqz(np.hstack((F1, F2)),
                                                   -np.hstack((F3, F4)),
                                                   sort='ouc',
                                                   output='complex')

    z21 = Z_ord[nk:, 0:nk]
    z11 = Z_ord[0:nk, 0:nk]
    s11 = s_ord[0:nk, 0:nk]
    t11 = t_ord[0:nk, 0:nk]

    if matrix_rank(z11) < nk:
        print('Warning: invertibility condition violated')


#    z11i=linalg.solve(z11,np.eye(nk)) # A\B, Ax=B
#    gx_= np.dot(z21,z11i)
#    gx=gx_.real
#    hx_=np.dot(z11,np.dot(linalg.solve(s11,t11),z11i))
#    hx=hx_.real

    z11i = np.dot(np.linalg.inv(z11), np.eye(nk))  # compute the solution

    gx = np.real(np.dot(z21, z11i))
    hx = np.real(np.dot(z11, np.dot(np.dot(np.linalg.inv(s11), t11), z11i)))

    return {
        'hx': hx,
        'gx': gx,
        'F1': F1,
        'F2': F2,
        'F3': F3,
        'F4': F4,
        'par': par
    }
Example #12
0
def LinApp_Solve(AA, BB, CC, DD, FF, GG, HH, JJ, KK, LL, MM, NN, Z0, Sylv):
    """
    This code takes Uhlig's original code and puts it in the form of a
    function.  This version outputs the policy function coefficients: PP,
    QQ and UU for X, and RR, SS and VV for Y.

    Inputs overview:
    The matrices of derivatives: AA - MM.
    The autoregression coefficient matrix NN from the law of motion for Z.
    Z0 is the Z-point about which the linearization is taken.  For
    linearizing about the steady state this is Zbar and normally Zbar = 0.
    Sylv is an indicator variable telling the program to use the built-in
    function sylvester() to solve for QQ and SS, if possible.  Default is
    to use Sylv=1.  This option is now disabled and we always set Sylv=1.

    Parameters
    ----------
    AA : array_like, dtype=float, shape=(ny, nx)
        The matrix represented above by :math:`A`. It is the matrix of
        derivatives of the Y equations with repsect to :math:`X_t`
    BB : array_like, dtype=float, shape=(ny, nx)
        The matrix represented above by :math:`B`. It is the matrix of
        derivatives of the Y equations with repsect to
        :math:`X_{t-1}`.
    CC : array_like, dtype=float, shape=(ny, ny)
        The matrix represented above by :math:`C`. It is the matrix of
        derivatives of the Y equations with repsect to :math:`Y_t`
    DD : array_like, dtype=float, shape=(ny, nz)
        The matrix represented above by :math:`C`. It is the matrix of
        derivatives of the Y equations with repsect to :math:`Z_t`
    FF : array_like, dtype=float, shape=(nx, nx)
        The matrix represetned above by :math:`F`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`X_{t+1}`
    GG : array_like, dtype=float, shape=(nx, nx)
        The matrix represetned above by :math:`G`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`X_t`
    HH : array_like, dtype=float, shape=(nx, nx)
        The matrix represetned above by :math:`H`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`X_{t-1}`
    JJ : array_like, dtype=float, shape=(nx, ny)
        The matrix represetned above by :math:`J`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Y_{t+1}`
    KK : array_like, dtype=float, shape=(nx, ny)
        The matrix represetned above by :math:`K`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Y_t`
    LL : array_like, dtype=float, shape=(nx, nz)
        The matrix represetned above by :math:`L`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Z_{t+1}`
    MM : array_like, dtype=float, shape=(nx, nz)
        The matrix represetned above by :math:`M`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Z_t`
    NN : array_like, dtype=float, shape=(nz, nz)
        The autocorrelation matrix for the exogenous state vector z.
    Z0 : array, dtype=float, shape=(nz,)
        the Z-point about which the linearization is taken.  For linearizing 
        about the steady state this is Zbar and normally Zbar = 0.
        QQ if true.
    Sylv: binary, dtype=int 
        an indicator variable telling the program to use the built-in
        function sylvester() to solve for QQ and SS, if possible.  Default is
        to use Sylv=1.

    Returns
    -------
    P : 2D-array, dtype=float, shape=(nx, nx)
        The matrix :math:`P` in the law of motion for endogenous state
        variables described above.
    Q : 2D-array, dtype=float, shape=(nx, nz)
        The matrix :math:`Q` in the law of motion for exogenous state
        variables described above.
    R : 2D-array, dtype=float, shape=(ny, nx)
        The matrix :math:`R` in the law of motion for endogenous state
        variables described above.
    S : 2D-array, dtype=float, shape=(ny, nz)
        The matrix :math:`S` in the law of motion for exogenous state
        variables described above.
        
    References
    ----------
    .. [1] Uhlig, H. (1999): "A toolkit for analyzing nonlinear dynamic
       stochastic models easily," in Computational Methods for the Study
       of Dynamic Economies, ed. by R. Marimon, pp. 30-61. Oxford
       University Press.

    """
    # The coding for Uhlig's solution for QQ and SS gives incorrect results
    # So we will use numpy's Sylvester equation solver regardless of the value
    #  chosen for Sylv

    #The original coding we did used the np.matrix form for our matrices so we
    #make sure to set our inputs to numpy matrices.
    AA = np.matrix(AA)
    BB = np.matrix(BB)
    CC = np.matrix(CC)
    DD = np.matrix(DD)
    FF = np.matrix(FF)
    GG = np.matrix(GG)
    HH = np.matrix(HH)
    JJ = np.matrix(JJ)
    KK = np.matrix(KK)
    LL = np.matrix(LL)
    MM = np.matrix(MM)
    NN = np.matrix(NN)
    Z0 = np.array(Z0)
    #Tolerance level to use
    TOL = .000001

    # Here we use matrices to get pertinent dimensions.
    nx = FF.shape[1]
    l_equ = CC.shape[0]
    ny = CC.shape[1]
    nz = min(NN.shape)

    # The following if and else blocks form the
    # Psi, Gamma, Theta Xi, Delta mats
    if l_equ == 0:
        if CC.any():
            # This blcok makes sure you don't throw an error with an empty CC.
            CC_plus = la.pinv(CC)
            CC_0 = _nullSpaceBasis(CC.T)
        else:
            CC_plus = np.mat([])
            CC_0 = np.mat([])
        Psi_mat = FF
        Gamma_mat = -GG
        Theta_mat = -HH
        Xi_mat = np.mat(
            vstack((hstack(
                (Gamma_mat, Theta_mat)), hstack((eye(nx), zeros((nx, nx)))))))
        Delta_mat = np.mat(
            vstack((hstack((Psi_mat, zeros(
                (nx, nx)))), hstack((zeros((nx, nx)), eye(nx))))))

    else:
        CC_plus = la.pinv(CC)
        CC_0 = _nullSpaceBasis(CC.T)
        if l_equ != ny:
            Psi_mat = vstack((zeros((l_equ - ny, nx)), FF \
                            - dot(dot(JJ, CC_plus), AA)))
            Gamma_mat = vstack((dot(CC_0, AA), dot(dot(JJ, CC_plus), BB) \
                        - GG + dot(dot(KK, CC_plus), AA)))
            Theta_mat = vstack((dot(CC_0, BB), dot(dot(KK, CC_plus), BB) - HH))
        else:
            CC_inv = la.inv(CC)
            Psi_mat = FF - dot(JJ.dot(CC_inv), AA)
            Gamma_mat = dot(JJ.dot(CC_inv), BB) - GG + dot(dot(KK, CC_inv), AA)
            Theta_mat = dot(KK.dot(CC_inv), BB) - HH
        Xi_mat = vstack((hstack((Gamma_mat, Theta_mat)), \
                            hstack((eye(nx), zeros((nx, nx))))))
        Delta_mat = vstack((hstack((Psi_mat, np.mat(zeros((nx, nx))))),\
                                hstack((zeros((nx, nx)), eye(nx)))))

    # Now we need the generalized eigenvalues/vectors for Xi with respect to
    # Delta. That is eVals and eVecs below.

    eVals, eVecs = la.eig(Xi_mat, Delta_mat)
    if npla.matrix_rank(eVecs) < nx:
        print("Error: Xi is not diagonalizable, stopping...")

    # From here to line 158 we Diagonalize Xi, form Lambda/Omega and find P.
    else:
        Xi_sortindex = np.argsort(abs(eVals))
        Xi_sortedVec = np.array([eVecs[:, i] for i in Xi_sortindex]).T
        Xi_sortval = eVals[Xi_sortindex]
        Xi_select = np.arange(0, nx)
        if np.imag(Xi_sortval[nx - 1]).any():
            if (abs(Xi_sortval[nx - 1] - sp.conj(Xi_sortval[nx])) < TOL):
                drop_index = 1
                cond_1 = (abs(np.imag(Xi_sortval[drop_index - 1])) > TOL)
                cond_2 = drop_index < nx
                while cond_1 and cond_2:
                    drop_index += 1
                if drop_index >= nx:
                    print("There is an error. Too many complex eigenvalues." +
                          " Quitting...")
                else:
                    print("Droping the lowest real eigenvalue. Beware of" +
                          " sunspots!")
                    Xi_select = np.array([np.arange(0, drop_index - 1),\
                                          np.arange(drop_index, nx + 1)])
        # Here Uhlig computes stuff if user chose "Manual roots" I skip it.
        if max(abs(Xi_sortval[Xi_select])) > 1 + TOL:
            print(
                "It looks like we have unstable roots. This might not work...")
        if abs(max(abs(Xi_sortval[Xi_select])) - 1) < TOL:
            print("Check the model to make sure you have a unique steady" +
                  " state we are having problems with convergence.")
        Lambda_mat = np.diag(Xi_sortval[Xi_select])
        Omega_mat = Xi_sortedVec[nx:2 * nx, Xi_select]

        if npla.matrix_rank(Omega_mat) < nx:
            print("Omega matrix is not invertible, Can't solve for P; we" +
                  " proceed with QZ-method instead.")

            #~~~~~~~~~ QZ-method codes from SOLVE_QZ ~~~~~~~~#
            Delta_up, Xi_up, UUU, VVV = la.qz(Delta_mat,
                                              Xi_mat,
                                              output='complex')
            UUU = UUU.T
            Xi_eigval = np.diag(
                np.diag(Xi_up) / np.maximum(np.diag(Delta_up), TOL))
            Xi_sortindex = np.argsort(abs(np.diag(Xi_eigval)))
            Xi_sortval = Xi_eigval[Xi_sortindex, Xi_sortindex]
            Xi_select = np.arange(0, nx)
            stake = max(abs(Xi_sortval[Xi_select])) + TOL

            Delta_up, Xi_up, UUU, VVV = qzdiv(stake, Delta_up, Xi_up, UUU, VVV)

            #Check conditions from line 49-109
            if np.imag(Xi_sortval[nx - 1]).any():
                if (abs(Xi_sortval[nx - 1] - sp.conj(Xi_sortval[nx])) < TOL):
                    print(
                        "Problem: You have complex eigenvalues! And this means"
                        +
                        " PP matrix will contain complex numbers by this method."
                    )
                drop_index = 1
                cond_1 = (abs(np.imag(Xi_sortval[drop_index - 1])) > TOL)
                cond_2 = drop_index < nx
                while cond_1 and cond_2:
                    drop_index += 1
                if drop_index >= nx:
                    print("There is an error. Too many complex eigenvalues." +
                          " Quitting...")
                else:
                    print("Dropping the lowest real eigenvalue. Beware of" +
                          " sunspots!")
                    for i in range(drop_index, nx + 1):
                        Delta_up, Xi_up, UUU, VVV = qzswitch(
                            i, Delta_up, Xi_up, UUU, VVV)
                    Xi_select1 = np.arange(0, drop_index - 1)
                    Xi_select = np.append(Xi_select1,
                                          np.arange(drop_index, nx + 1))

            if Xi_sortval[max(Xi_select)] < 1 - TOL:
                print('There are stable roots NOT used. Proceeding with the' +
                      ' smallest root.')
            if max(abs(Xi_sortval[Xi_select])) > 1 + TOL:
                print(
                    "It looks like we have unstable roots. This might not work..."
                )
            if abs(max(abs(Xi_sortval[Xi_select])) - 1) < TOL:
                print("Check the model to make sure you have a unique steady" +
                      " state we are having problems with convergence.")
            #End of checking conditions
            #Lambda_mat = np.diag(Xi_sortval[Xi_select]) # to help sol_out.m

            VVV = VVV.conj().T
            VVV_2_1 = VVV[nx:2 * nx, 0:nx]
            VVV_2_2 = VVV[nx:2 * nx, nx:2 * nx]
            UUU_2_1 = UUU[nx:2 * nx, 0:nx]
            VVV = VVV.conj().T

            if abs(la.det(UUU_2_1)) < TOL:
                print(
                    "One necessary condition for computing P is NOT satisfied,"
                    + " but we proceed anyways...")
            if abs(la.det(VVV_2_1)) < TOL:
                print(
                    "VVV_2_1 matrix, used to compute for P, is not invertible; we"
                    + " are in trouble but we proceed anyways...")

            PP = np.matrix(la.solve(-VVV_2_1, VVV_2_2))
            PP_imag = np.imag(PP)
            PP = np.real(PP)
            if (sum(sum(abs(PP_imag))) / sum(sum(abs(PP))) > .000001).any():
                print(
                    "A lot of P is complex. We will continue with the" +
                    " real part and hope we don't lose too much information.")
            #~~~~~~~~~ End of QZ-method ~~~~~~~~~#

        #This follows the original uhlig.py file
        else:
            PP = dot(dot(Omega_mat, Lambda_mat), la.inv(Omega_mat))
            PP_imag = np.imag(PP)
            PP = np.real(PP)
            if (sum(sum(abs(PP_imag))) / sum(sum(abs(PP))) > .000001).any():
                print(
                    "A lot of P is complex. We will continue with the" +
                    " real part and hope we don't lose too much information.")

    # The if and else below make RR depending on our model's setup.
    if l_equ == 0:
        RR = zeros((0, nx))  #empty matrix
    else:
        RR = -dot(CC_plus, (dot(AA, PP) + BB))

    # Now we use Sylvester equation solver to find QQ and SS matrices.
    '''
    This code written by Kerk Phillips 2020
    '''

    CCinv = npla.inv(CC)
    if ny > 0:
        PM = npla.inv(FF - np.matmul(np.matmul(JJ, CCinv), AA))
        if npla.matrix_rank(PM) < nx + ny:
            print("Sylvester equation solver condition is not satisfied")
    else:
        PM = npla.inv(FF)
        if npla.matrix_rank(FF) < nx:
            print("Sylvester equation solver condition is not satisfied")
    if ny > 0:
        JCAP = np.matmul(np.matmul(JJ, CCinv), np.matmul(AA, PP))
        JCB = np.matmul(np.matmul(JJ, CCinv), BB)
        KCA = np.matmul(np.matmul(KK, CCinv), AA)
        KCD = np.matmul(np.matmul(KK, CCinv), DD)
        JCDN = np.matmul(np.matmul(JJ, CCinv), np.matmul(DD, NN))
        Anew = PM.dot(FF.dot(PP) + GG - JCAP - JCB - KCA)
        Bnew = NN
        Cnew = PM.dot(KCD - LL.dot(NN) + JCDN - MM)
        QQ = la.solve_sylvester(Anew, Bnew, Cnew)
        SS = la.solve(-CC, (AA.dot(QQ) + DD))
    else:
        Anew = PM.dot(FF.dot(PP) + GG)
        Bnew = NN
        Cnew = PM.dot(-LL.dot(NN) - MM)
        QQ = la.solve_sylvester(Anew, Bnew, Cnew)
        SS = np.zeros((0, nz))  #empty matrix

    return np.array(PP), np.array(QQ), np.array(RR), np.array(SS)
Example #13
0
def LinApp_Solve(AA,BB,CC,DD,FF,GG,HH,JJ,KK,LL,MM,WWW,TT,NN,Z0,Sylv):
    """
    This code takes Uhlig's original code and puts it in the form of a
    function.  This version outputs the policy function coefficients: PP,
    QQ and UU for X, and RR, SS and VV for Y.

    Inputs overview:
    The matrices of derivatives: AA - TT.
    The autoregression coefficient matrix NN from the law of motion for Z.
    Z0 is the Z-point about which the linearization is taken.  For
    linearizing about the steady state this is Zbar and normally Zbar = 0.
    Sylv is an indicator variable telling the program to use the built-in
    function sylvester() to solve for QQ and SS, if possible.  Default is
    to use Sylv=1.

    Parameters
    ----------
    AA : array_like, dtype=float, shape=(ny, nx)
        The matrix represented above by :math:`A`. It is the matrix of
        derivatives of the Y equations with repsect to :math:`X_t`
    BB : array_like, dtype=float, shape=(ny, nx)
        The matrix represented above by :math:`B`. It is the matrix of
        derivatives of the Y equations with repsect to
        :math:`X_{t-1}`.
    CC : array_like, dtype=float, shape=(ny, ny)
        The matrix represented above by :math:`C`. It is the matrix of
        derivatives of the Y equations with repsect to :math:`Y_t`
    DD : array_like, dtype=float, shape=(ny, nz)
        The matrix represented above by :math:`C`. It is the matrix of
        derivatives of the Y equations with repsect to :math:`Z_t`
    FF : array_like, dtype=float, shape=(nx, nx)
        The matrix represetned above by :math:`F`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`X_{t+1}`
    GG : array_like, dtype=float, shape=(nx, nx)
        The matrix represetned above by :math:`G`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`X_t`
    HH : array_like, dtype=float, shape=(nx, nx)
        The matrix represetned above by :math:`H`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`X_{t-1}`
    JJ : array_like, dtype=float, shape=(nx, ny)
        The matrix represetned above by :math:`J`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Y_{t+1}`
    KK : array_like, dtype=float, shape=(nx, ny)
        The matrix represetned above by :math:`K`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Y_t`
    LL : array_like, dtype=float, shape=(nx, nz)
        The matrix represetned above by :math:`L`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Z_{t+1}`
    MM : array_like, dtype=float, shape=(nx, nz)
        The matrix represetned above by :math:`M`. It is the matrix of
        derivatives of the model's characterizing equations with
        respect to :math:`Z_t`
    WWW : array, dtype=float, shape=(ny,)
        The vector of the numerical errors of first ny characterizing
        equations
    TT : array, dtype=float, shape=(nx,)
        The vector of the numerical errors of the next nx characterizing
        equations following the first ny equations
    NN : array_like, dtype=float, shape=(nz, nz)
        The autocorrelation matrix for the exogenous state vector z.
    Z0 : array, dtype=float, shape=(nz,)
        The Z-point about which the linearization is taken.  For linearizing 
        about the steady state this is Zbar and normally Zbar = 0.
        QQ if true.
    Sylv: binary, dtype=int 
        An indicator variable telling the program to use the built-in
        function sylvester() to solve for QQ and SS, if possible.  Default is
        to use Sylv=1.

    Returns
    -------
    P : 2D-array, dtype=float, shape=(nx, nx)
        The matrix :math:`P` in the law of motion for endogenous state
        variables described above.
    Q : 2D-array, dtype=float, shape=(nx, nz)
        The matrix :math:`Q` in the law of motion for exogenous state
        variables described above.
    U : array, dtype=float, shape=(nx,)
        The vector of the constant term of the policy function for X, 
        the endogenous state variables
    R : 2D-array, dtype=float, shape=(ny, nx)
        The matrix :math:`R` in the law of motion for endogenous state
        variables described above.
    S : 2D-array, dtype=float, shape=(ny, nz)
        The matrix :math:`S` in the law of motion for exogenous state
        variables described above.
    V : array, dtype=float, shape=(ny,)
        The vector of the constant term of the policy function for Y, 
        the endogenous non-state variables
    References
    ----------
    .. [1] Uhlig, H. (1999): "A toolkit for analyzing nonlinear dynamic
       stochastic models easily," in Computational Methods for the Study
       of Dynamic Economies, ed. by R. Marimon, pp. 30-61. Oxford
       University Press.

    """
    #The original coding we did used the np.matrix form for our matrices so we
    #make sure to set our inputs to numpy matrices.
    AA = np.matrix(AA)
    BB = np.matrix(BB)
    CC = np.matrix(CC)
    DD = np.matrix(DD)
    FF = np.matrix(FF)
    GG = np.matrix(GG)
    HH = np.matrix(HH)
    JJ = np.matrix(JJ)
    KK = np.matrix(KK)
    LL = np.matrix(LL)
    MM = np.matrix(MM)
    NN = np.matrix(NN)
    WWW = np.array(WWW)
    TT = np.array(TT)
    Z0 = np.array(Z0)
    #Tolerance level to use
    TOL = .000001

    # Here we use matrices to get pertinent dimensions.
    nx = FF.shape[1]
    l_equ = CC.shape[0]
    ny = CC.shape[1]
    nz = min(NN.shape)

    # The following if and else blocks form the
    # Psi, Gamma, Theta Xi, Delta mats
    if l_equ == 0:
        if CC.any():
            # This blcok makes sure you don't throw an error with an empty CC.
            CC_plus = la.pinv(CC)
            CC_0 = _nullSpaceBasis(CC.T)
        else:
            CC_plus = np.mat([])
            CC_0 = np.mat([])
        Psi_mat = FF
        Gamma_mat = -GG
        Theta_mat = -HH
        Xi_mat = np.mat(vstack((hstack((Gamma_mat, Theta_mat)),
                        hstack((eye(nx), zeros((nx, nx)))))))
        Delta_mat = np.mat(vstack((hstack((Psi_mat, zeros((nx, nx)))),
                           hstack((zeros((nx, nx)), eye(nx))))))

    else:
        CC_plus = la.pinv(CC)
        CC_0 = _nullSpaceBasis(CC.T)
        if l_equ != ny:
            Psi_mat = vstack((zeros((l_equ - ny, nx)), FF \
                            - dot(dot(JJ, CC_plus), AA)))
            Gamma_mat = vstack((dot(CC_0, AA), dot(dot(JJ, CC_plus), BB) \
                        - GG + dot(dot(KK, CC_plus), AA)))
            Theta_mat = vstack((dot(CC_0, BB), dot(dot(KK, CC_plus), BB) - HH))
        else:
            CC_inv = la.inv(CC)
            Psi_mat = FF - dot(JJ.dot(CC_inv), AA)
            Gamma_mat = dot(JJ.dot(CC_inv), BB) - GG + dot(dot(KK, CC_inv), AA)
            Theta_mat = dot(KK.dot(CC_inv), BB) - HH
        Xi_mat = vstack((hstack((Gamma_mat, Theta_mat)), \
                            hstack((eye(nx), zeros((nx, nx))))))
        Delta_mat = vstack((hstack((Psi_mat, np.mat(zeros((nx, nx))))),\
                                hstack((zeros((nx, nx)), eye(nx)))))

    # Now we need the generalized eigenvalues/vectors for Xi with respect to
    # Delta. That is eVals and eVecs below.

    eVals, eVecs = la.eig(Xi_mat, Delta_mat)
    if npla.matrix_rank(eVecs) < nx:
        print("Error: Xi is not diagonalizable, stopping...")

    # From here to line 158 we Diagonalize Xi, form Lambda/Omega and find P.
    else:
        Xi_sortabs = np.sort(abs(eVals))
        Xi_sortindex = np.argsort(abs(eVals))
        Xi_sortedVec = np.array([eVecs[:, i] for i in Xi_sortindex]).T
        Xi_sortval = eVals[Xi_sortindex]
        Xi_select = np.arange(0, nx)
        if np.imag(Xi_sortval[nx - 1]).any():
            if (abs(Xi_sortval[nx - 1] - sp.conj(Xi_sortval[nx])) < TOL):
                drop_index = 1
                cond_1 = (abs(np.imag(Xi_sortval[drop_index-1])) > TOL)
                cond_2 = drop_index < nx
                while cond_1 and cond_2:
                    drop_index += 1
                if drop_index >= nx:
                    print("There is an error. Too many complex eigenvalues."
                          +" Quitting...")
                else:
                    print("Droping the lowest real eigenvalue. Beware of" +
                          " sunspots!")
                    Xi_select = np.array([np.arange(0, drop_index - 1),\
                                          np.arange(drop_index, nx + 1)])
        # Here Uhlig computes stuff if user chose "Manual roots" I skip it.
        if max(abs(Xi_sortval[Xi_select])) > 1 + TOL:
            print("It looks like we have unstable roots. This might not work...")
        if abs(max(abs(Xi_sortval[Xi_select])) - 1) < TOL:
            print("Check the model to make sure you have a unique steady" +
                  " state we are having problems with convergence.")
        Lambda_mat = np.diag(Xi_sortval[Xi_select])
        Omega_mat = Xi_sortedVec[nx:2 * nx, Xi_select]

        if npla.matrix_rank(Omega_mat) < nx:
            print("Omega matrix is not invertible, Can't solve for P; we" +
                    " proceed with the alternative, QZ-method, to get P...")

            #~~~~~~~~~ QZ-method codes from SOLVE_QZ ~~~~~~~~#
            Delta_up,Xi_up,UUU,VVV=la.qz(Delta_mat,Xi_mat, output='complex')
            UUU=UUU.T
            Xi_eigval = np.diag( np.diag(Xi_up)/np.maximum(np.diag(Delta_up),TOL))
            Xi_sortabs= np.sort(abs(np.diag(Xi_eigval)))
            Xi_sortindex= np.argsort(abs(np.diag(Xi_eigval)))
            Xi_sortval = Xi_eigval[Xi_sortindex, Xi_sortindex]
            Xi_select = np.arange(0, nx)
            stake = max(abs(Xi_sortval[Xi_select])) + TOL

            Delta_up, Xi_up, UUU, VVV = qzdiv(stake,Delta_up,Xi_up,UUU,VVV)
                    
            #Check conditions from line 49-109
            if np.imag(Xi_sortval[nx - 1]).any():
                if (abs(Xi_sortval[nx - 1] - sp.conj(Xi_sortval[nx])) < TOL):
                    print("Problem: You have complex eigenvalues! And this means"+
                        " PP matrix will contain complex numbers by this method." )
                drop_index = 1
                cond_1 = (abs(np.imag(Xi_sortval[drop_index-1])) > TOL)
                cond_2 = drop_index < nx
                while cond_1 and cond_2:
                    drop_index += 1
                if drop_index >= nx:
                    print("There is an error. Too many complex eigenvalues."
                              +" Quitting...")
                else:
                    print("Dropping the lowest real eigenvalue. Beware of" +
                          " sunspots!")
                    for i in xrange(drop_index,nx+1):
                        Delta_up,Xi_up,UUU,VVV = qzswitch(i,Delta_up,Xi_up,UUU,VVV)
                    Xi_select1 = np.arange(0,drop_index-1)
                    Xi_select = np.append(Xi_select1, np.arange(drop_index,nx+1))

            if Xi_sortval[max(Xi_select)] < 1 - TOL:
                print('There are stable roots NOT used. Proceeding with the' +
                        ' smallest root.')
            if max(abs(Xi_sortval[Xi_select])) > 1 + TOL:
                print("It looks like we have unstable roots. This might not work...")
            if abs(max(abs(Xi_sortval[Xi_select])) - 1) < TOL:
                print("Check the model to make sure you have a unique steady" +
                          " state we are having problems with convergence.")
            #End of checking conditions
            
            #Lambda_mat = np.diag(Xi_sortval[Xi_select]) # to help sol_out.m
            
            VVV=VVV.conj().T
            VVV_2_1 = VVV[nx : 2*nx, 0 : nx]
            VVV_2_2 = VVV[nx : 2*nx, nx :2*nx]
            UUU_2_1 = UUU[nx : 2*nx, 0 : nx]
            VVV = VVV.conj().T
            
            if abs(la.det(UUU_2_1))< TOL:
                print("One necessary condition for computing P is NOT satisfied,"+
                    " but we proceed anyways...")
            if abs(la.det(VVV_2_1))< TOL:
                print("VVV_2_1 matrix, used to compute for P, is not invertible; we"+
                    " are in trouble but we proceed anyways...")
            
            PP = np.matrix( la.solve(- VVV_2_1, VVV_2_2) )
            PP_imag = np.imag(PP)
            PP = np.real(PP)
            if (sum(sum(abs(PP_imag))) / sum(sum(abs(PP))) > .000001).any():
                print("A lot of P is complex. We will continue with the" +
                      " real part and hope we don't lose too much information.")
            #~~~~~~~~~ End of QZ-method ~~~~~~~~~#

        #This follows the original uhlig.py file
        else:
            PP = dot(dot(Omega_mat, Lambda_mat), la.inv(Omega_mat))
            PP_imag = np.imag(PP)
            PP = np.real(PP)
            if (sum(sum(abs(PP_imag))) / sum(sum(abs(PP))) > .000001).any():
                print("A lot of P is complex. We will continue with the" +
                      " real part and hope we don't lose too much information.")
    
    # The code from here to the end was from the Uhlig file calc_qrs.m.
    # I think for python it fits better here than in a separate file.

    # The if and else below make RR and VV depending on our model's setup.
    if l_equ == 0:
        RR = zeros((0, nx))
        VV = hstack((kron(NN.T, FF) + kron(eye(nz), \
            (dot(FF, PP) + GG)), kron(NN.T, JJ) + kron(eye(nz), KK))) 

    else:
        RR = - dot(CC_plus, (dot(AA, PP) + BB))
        VV = sp.vstack((hstack((kron(eye(nz), AA), \
                        kron(eye(nz), CC))), hstack((kron(NN.T, FF) +\
                        kron(eye(nz), dot(FF, PP) + dot(JJ, RR) + GG),\
                        kron(NN.T, JJ) + kron(eye(nz), KK)))))

    # Now we use LL, NN, RR, VV to get the QQ, RR, SS, VV matrices.
    # first try using Sylvester equation solver
    if ny>0:
        PM = (FF-la.solve(JJ.dot(CC),AA))
        if npla.matrix_rank(PM)< nx+ny:
            Sylv=0
            print("Sylvester equation solver condition is not satisfied;"\
                    +" proceed with the original method...")
    else:
        if npla.matrix_rank(FF)< nx:
            Sylv=0
            print("Sylvester equation solver condition is not satisfied;"\
                    +" proceed with the original method...")
    if Sylv:
        print("Using Sylvester equation solver...")
        if ny>0:
            Anew = la.solve(PM, (FF.dot(PP)+GG+JJ.dot(RR)-\
                    la.solve(KK.dot(CC), AA)) )
            Bnew = NN
            Cnew1 = la.solve(JJ.dot(CC),DD.dot(NN))+la.solve(KK.dot(CC), DD)-\
                    LL.dot(NN)-MM
            Cnew = la.solve(PM, Cnew1)
            QQ = la.solve_sylvester(Anew,Bnew,Cnew)
            SS = la.solve(-CC, (AA.dot(QQ)+DD))
        else:
            Anew = la.solve(FF, (FF.dot(PP)+GG))
            Bnew = NN
            Cnew = la.solve(FF, (-LL.dot(NN)-MM))
            QQ = la.solve_sylvester(Anew,Bnew,Cnew)
            SS = np.zeros((0,nz)) #empty matrix
    # then the Uhlig's way
    else:
        if (npla.matrix_rank(VV) < nz * (nx + ny)):
            print("Sorry but V is not invertible. Can't solve for Q and S;"+
                     " but we proceed anyways...")
        
        LL = sp.mat(LL)
        NN = sp.mat(NN)
        LLNN_plus_MM = dot(LL, NN) + MM

        if DD.any():
            impvec = vstack([DD.T, np.reshape(LLNN_plus_MM,
                                                  (nx * nz, 1), 'F')])
        else:
            impvec = np.reshape(LLNN_plus_MM, (nx * nz, 1), 'F')

        QQSS_vec = np.matrix(la.solve(-VV, impvec))

        if (max(abs(QQSS_vec)) == sp.inf).any():
            print("We have issues with Q and S. Entries are undefined." +
                      " Probably because V is no inverible.")

        #Build QQ SS
        QQ = np.reshape(np.matrix(QQSS_vec[0:nx * nz, 0]),
                            (nx, nz), 'F')

        SS = np.reshape(QQSS_vec[(nx * nz):((nx + ny) * nz), 0],\
                            (ny, nz), 'F')

    #Build WW - WW has the property [x(t)',y(t)',z(t)']=WW [x(t)',z(t)'].
    WW = sp.vstack((
        hstack((eye(nx), zeros((nx, nz)))),
        hstack((dot(RR, la.pinv(PP)), (SS - dot(dot(RR, la.pinv(PP)), QQ)))),
        hstack((zeros((nz, nx)), eye(nz)))))

    # find constant terms
    # redefine matrices to be 2D-arrays for generating vector UU and VVV
    AA = np.array(AA)
    CC = np.array(CC)
    FF = np.array(FF)
    GG = np.array(GG)
    JJ = np.array(JJ)
    KK = np.array(KK)
    LL = np.array(LL)
    NN = np.array(NN)
    RR = np.array(RR)
    QQ = np.array(QQ)
    SS = np.array(SS)
    if ny>0:
        UU1 = -(FF.dot(PP)+GG+JJ.dot(RR)+FF-(JJ+KK).dot(la.solve(CC,AA)))
        UU2 = (TT+(FF.dot(QQ)+JJ.dot(SS)+LL).dot(NN.dot(Z0)-Z0)- \
            (JJ+KK).dot(la.solve(CC,WWW)))
        UU = la.solve(UU1, UU2)
        VVV = la.solve(- CC, (WWW+AA.dot(UU)) )
    else:
        UU = la.solve( -(FF.dot(PP)+FF+GG), (TT+(FF.dot(QQ)+LL).dot(NN.dot(Z0)-Z0)) )
        VVV = np.array([])

    return np.array(PP), np.array(QQ), np.array(UU), np.array(RR), np.array(SS),\
             np.array(VVV)
Example #14
0
def second_order_solver(FF, GG, HH):

    from scipy.linalg import qz
    from dolo.numeric.extern.qz import qzdiv

    from numpy import array, mat, c_, r_, eye, zeros, real_if_close, diag, allclose, where, diagflat
    from numpy.linalg import solve

    Psi_mat = array(FF)
    Gamma_mat = array(-GG)
    Theta_mat = array(-HH)
    m_states = FF.shape[0]

    Xi_mat = r_[c_[Gamma_mat, Theta_mat], c_[eye(m_states),
                                             zeros((m_states, m_states))]]

    Delta_mat = r_[c_[Psi_mat, zeros((m_states, m_states))], c_[zeros(
        (m_states, m_states)), eye(m_states)]]

    AAA, BBB, Q, Z = qz(Delta_mat, Xi_mat)

    Delta_up, Xi_up, UUU, VVV = [real_if_close(mm) for mm in (AAA, BBB, Q, Z)]

    Xi_eigval = diag(Xi_up) / where(diag(Delta_up) > TOL, diag(Delta_up), TOL)

    Xi_sortindex = abs(Xi_eigval).argsort()
    # (Xi_sortabs doesn't really seem to be needed)

    Xi_sortval = Xi_eigval[Xi_sortindex]

    Xi_select = slice(0, m_states)

    stake = (abs(Xi_sortval[Xi_select])).max() + TOL

    Delta_up, Xi_up, UUU, VVV = qzdiv(stake, Delta_up, Xi_up, UUU, VVV)

    try:
        # check that all unused roots are unstable
        assert abs(Xi_sortval[m_states]) > (1 - TOL)
        # check that all used roots are stable
        assert abs(Xi_sortval[Xi_select]).max() < 1 + TOL
    except:
        raise BKError('generic')

    # check for unit roots anywhere


#    assert (abs((abs(Xi_sortval) - 1)) > TOL).all()

    Lambda_mat = diagflat(Xi_sortval[Xi_select])
    VVVH = VVV.T
    VVV_2_1 = VVVH[m_states:2 * m_states, :m_states]
    VVV_2_2 = VVVH[m_states:2 * m_states, m_states:2 * m_states]
    UUU_2_1 = UUU[m_states:2 * m_states, :m_states]

    PP = -solve(VVV_2_1, VVV_2_2)

    # slightly different check than in the original toolkit:
    assert allclose(real_if_close(PP), PP.real)
    PP = PP.real
    ## end of solve_qz!

    return [Xi_sortval[Xi_select], PP]
Example #15
0
def solve_sylv_schur(A, Ar, E=None, Er=None, B=None, Br=None, C=None, Cr=None):
    r"""Solve Sylvester equation by Schur decomposition.

    Solves Sylvester equation

    .. math::
        A V E_r^T + E V A_r^T + B B_r^T = 0

    or

    .. math::
        A^T W E_r + E^T W A_r + C^T C_r = 0

    or both using (generalized) Schur decomposition (Algorithms 3 and 4
    in [BKS11]_), if the necessary parameters are given.

    Parameters
    ----------
    A
        Real |Operator|.
    Ar
        Real |Operator|.
        It is converted into a |NumPy array| using
        :func:`~pymor.algorithms.to_matrix.to_matrix`.
    E
        Real |Operator| or `None` (then assumed to be the identity).
    Er
        Real |Operator| or `None` (then assumed to be the identity).
        It is converted into a |NumPy array| using
        :func:`~pymor.algorithms.to_matrix.to_matrix`.
    B
        Real |Operator| or `None`.
    Br
        Real |Operator| or `None`.
        It is converted into a |VectorArray| using
        `Br.as_source_array()`.
    C
        Real |Operator| or `None`.
    Cr
        Real |Operator| or `None`.
        It is converted into a |VectorArray| using
        `Cr.as_range_array()`.

    Returns
    -------
    V
        Returned if `B` and `Br` are given, |VectorArray| from
        `A.source`.
    W
        Returned if `C` and `Cr` are given, |VectorArray| from
        `A.source`.

    Raises
    ------
    ValueError
        If `V` and `W` cannot be returned.
    """
    # check types
    assert isinstance(A,
                      OperatorInterface) and A.linear and A.source == A.range
    assert isinstance(
        Ar, OperatorInterface) and Ar.linear and Ar.source == Ar.range

    assert E is None or isinstance(
        E, OperatorInterface) and E.linear and E.source == E.range == A.source
    if E is None:
        E = IdentityOperator(A.source)
    assert Er is None or isinstance(
        Er,
        OperatorInterface) and Er.linear and Er.source == Er.range == Ar.source

    compute_V = B is not None and Br is not None
    compute_W = C is not None and Cr is not None

    if not compute_V and not compute_W:
        raise ValueError(
            'Not enough parameters are given to solve a Sylvester equation.')

    if compute_V:
        assert isinstance(
            B, OperatorInterface) and B.linear and B.range == A.source
        assert isinstance(
            Br, OperatorInterface) and Br.linear and Br.range == Ar.source
        assert B.source == Br.source

    if compute_W:
        assert isinstance(
            C, OperatorInterface) and C.linear and C.source == A.source
        assert isinstance(
            Cr, OperatorInterface) and Cr.linear and Cr.source == Ar.source
        assert C.range == Cr.range

    # convert reduced operators
    Ar = to_matrix(Ar, format='dense')
    r = Ar.shape[0]
    if Er is not None:
        Er = to_matrix(Er, format='dense')
    if Br is not None:
        Br = Br.as_source_array()
    if Cr is not None:
        Cr = Cr.as_range_array()

    # (Generalized) Schur decomposition
    if Er is None:
        TAr, Z = spla.schur(Ar, output='complex')
        Q = Z
    else:
        TAr, TEr, Q, Z = spla.qz(Ar, Er, output='complex')

    # solve for V, from the last column to the first
    if compute_V:
        V = A.source.empty(reserve=r)

        Br2 = Br.lincomb(Q.T)
        BBr2 = B.apply(Br2)
        for i in range(-1, -r - 1, -1):
            rhs = -BBr2[i].copy()
            if i < -1:
                if Er is not None:
                    rhs -= A.apply(V.lincomb(TEr[i, :i:-1].conjugate()))
                rhs -= E.apply(V.lincomb(TAr[i, :i:-1].conjugate()))
            TErii = 1 if Er is None else TEr[i, i]
            eAaE = TErii.conjugate() * A + TAr[i, i].conjugate() * E
            V.append(eAaE.apply_inverse(rhs))

        V = V.lincomb(Z.conjugate()[:, ::-1])
        V = V.real

    # solve for W, from the first column to the last
    if compute_W:
        W = A.source.empty(reserve=r)

        Cr2 = Cr.lincomb(Z.T)
        CTCr2 = C.apply_adjoint(Cr2)
        for i in range(r):
            rhs = -CTCr2[i].copy()
            if i > 0:
                if Er is not None:
                    rhs -= A.apply_adjoint(W.lincomb(TEr[:i, i]))
                rhs -= E.apply_adjoint(W.lincomb(TAr[:i, i]))
            TErii = 1 if Er is None else TEr[i, i]
            eAaE = TErii.conjugate() * A + TAr[i, i].conjugate() * E
            W.append(eAaE.apply_inverse_adjoint(rhs))

        W = W.lincomb(Q.conjugate())
        W = W.real

    if compute_V and compute_W:
        return V, W
    elif compute_V:
        return V
    else:
        return W
Example #16
0
                            for ii in range(n)])(*SSval)).reshape(n, nx, nxp)
        nfxx = array(
            lambdify(args, [fx[ii, :].jacobian(x) for ii in range(n)],
                     'numpy')(*SSval)).reshape(n, nx, nx)

        # DNT ==== First-order derivatives of the functions g and h ===================

        stake = 1.0

        #Create system matrices A,B
        AA = c_[-nfxp, -nfyp]
        BB = c_[nfx, nfy]
        NK = nfx.shape[1]

        #Complex Schur Decomposition
        ss, tt, qq, zz = linalg.qz(AA, BB)

        #Pick non-explosive (stable) eigenvalues
        slt = (abs(diag(tt)) < stake * abs(diag(ss)))
        noslt = logical_not(slt)
        nk = sum(slt)

        # Prep for QZ decomposition- qzswitch()


        def qzswitch(i, ss, tt, qq, zz):
            ssout = ss.copy()
            ttout = tt.copy()
            qqout = qq.copy()
            zzout = zz.copy()
            ix = i - 1  # from 1-based to 0-based indexing...