示例#1
0
def spline_basis(x, knots_dict):
    knots = knots_dict[0]
    n_a = len(x)
    n_knots = len(knots)

    first_interp_mat = np.zeros([n_a, n_knots + 1])
    aux_mat = np.zeros([n_a, n_knots])

    for i in range(n_a):
        loc = sum(knots <= x[i])
        if loc == n_knots:
            loc = n_knots - 1
        first_interp_mat[i, loc - 1] = 1 - (x[i] - knots[loc - 1])**2 / (
            knots[loc] - knots[loc - 1])**2
        first_interp_mat[i, loc] = (x[i] - knots[loc - 1])**2 / (
            knots[loc] - knots[loc - 1])**2
        aux_mat[i, loc - 1] = (x[i] - knots[loc - 1]) - (
            x[i] - knots[loc - 1])**2 / (knots[loc] - knots[loc - 1])

    aux_mat2 = spdiag(np.ones(n_knots), offsets=0, shape=(n_knots, n_knots), format="csc") \
              +spdiag(np.ones(n_knots), offsets=1, shape=(n_knots, n_knots), format="csc")
    aux_mat2[-1, -1] = 0
    aux_mat2[n_knots - 1, 0] = 1
    aux_mat3 = spdiag(np.hstack([-2/np.diff(knots), 0.0]), offsets=0, shape=(n_knots, n_knots+1), format="csc") \
              +spdiag(np.hstack([ 2/np.diff(knots), 1.0]), offsets=1, shape=(n_knots, n_knots+1), format="csc")

    from_knots = csc_matrix(first_interp_mat) + csc_matrix(aux_mat) * (spsolve(
        aux_mat2, aux_mat3))
    to_knots = spsolve((from_knots.conj().T * from_knots),
                       from_knots.conj().T) * speye(n_a, format="csc")

    return from_knots, to_knots
示例#2
0
def projection_for_subset(from_small, to_small, n_pre, n_post):

    n_full, n_red = from_small.shape

    spdiag_internal = lambda n: (*spdiag(np.ones(n)).nonzero(), np.ones(n))
    I, J, V = spdiag_internal(n_red + n_pre)
    from_approx = csc_matrix((V, (I, J)),
                             shape=(n_full + n_pre, n_pre + n_red))
    I, J, V = spdiag_internal(n_red + n_pre)
    to_approx = csc_matrix((V, (I, J)), shape=(n_pre + n_red, n_full + n_pre))

    from_approx[n_pre:n_pre + n_full, n_pre:n_pre + n_red] = from_small
    to_approx[n_pre:n_pre + n_red, n_pre:n_pre + n_full] = to_small

    # Expand matrices and add needed values
    dim1_from_approx, dim2_from_approx = from_approx.shape
    from_approx = hstack([
        from_approx,
        csc_matrix(np.zeros((dim1_from_approx, n_post)), dtype=float)
    ],
                         format='csc')
    from_approx = vstack([
        from_approx,
        csc_matrix(np.zeros((n_post, dim2_from_approx + n_post)), dtype=float)
    ],
                         format='csc')

    to_approx = hstack([
        to_approx,
        csc_matrix(np.zeros((dim2_from_approx, n_post)), dtype=float)
    ],
                       format='csc')
    to_approx = vstack([
        to_approx,
        csc_matrix(np.zeros((n_post, dim1_from_approx + n_post)), dtype=float)
    ],
                       format='csc')
    from_approx[dim1_from_approx:, dim2_from_approx:] = speye(n_post)
    to_approx[dim2_from_approx:, dim1_from_approx:] = speye(n_post)

    return from_approx, to_approx
示例#3
0
    def _get_residuals(x):
        #x = x.toarray().flatten()
        # Prepare steady state deviations
        V = (x[:n_v - 1] + V_ss).reshape(I, J, order='F')
        inflation = x[n_v - 1] + inflation_ss
        gg = x[n_v:n_v + n_g - 1] + g_ss[:-1]
        MP = x[n_v + n_g - 1]
        w = x[n_v + n_g] + w_ss
        hours = x[n_v + n_g + 1] + N_ss
        consumption = x[n_v + n_g + 2] + C_ss
        output = x[n_v + n_g + 3] + Y_ss
        assets = x[n_v + n_g + 4] + B_ss
        #government  = x[n_v + n_g + 5]       + G_ss

        V_dot = x[nstates:nstates + n_v - 1]
        inflation_dot = x[nstates + n_v - 1]
        g_dot = x[nstates + n_v:nstates + n_v + n_g - 1]
        mp_dot = x[nstates + n_g + n_v - 1]
        #fp_dot          = x[nstates + n_g + n_v + 5]
        #ps_dot          = x[nstates + n_g + n_v + 3]
        VEErrors = x[2 * nstates:2 * nstates + n_v - 1]
        inflation_error = x[2 * nstates + n_v - 1]
        mp_shock = x[2 * nstates + n_v]
        #fp_shock        = x[2*nstates + n_v + 1]
        #ps_shock        = x[2*nstates + n_v + 2]

        g_end = (1 - gg @ azdelta[:-1]) / azdelta[-1]
        g = np.append(gg, g_end)
        g[g < 1e-19] = 0.0

        #-----------------------------------------------------------------
        # Get equilibrium values, given steady state values
        normalized_wage = w / TFP
        profshare = (zz / meanlabeff) * ((1.0 - normalized_wage) * output)
        r_nominal = r_ss + taylor_inflation * inflation \
                    + taylor_outputgap * (np.log(output)-np.log(Y_ss)) + MP
        r = r_nominal - inflation
        lumptransfer    = labtax * w * hours - G_ss - \
                            (r_nominal - (1-govbcrule_fixnomB) * inflation) * assets

        #-----------------------------------------------------------------
        # Compute one iteration of the HJB
        ## Get flow utility, income, and labor hour functions
        util, income, labor = \
            aux.construct_household_problem_functions(V, w, coefrra, frisch, labtax, labdisutil)

        ## Initialize other variables, using V to ensure everything is a dual number
        Vaf = np.copy(V)
        Vab = np.copy(V)
        #h0 = np.array([h_ss[i, j] for i in range(I) for j in range(J)]).reshape(I, J)
        h0 = np.copy(h_ss)

        #-----------------------------------------------------------------
        # Construct Initial Difference Matrices
        #         hf = np.empty_like(V)
        #         hb = np.empty_like(V)

        Vaf[-1, :] = income(h_ss[-1, :], zz[-1, :], profshare[-1, :],
                            lumptransfer, r, amax)**(-coefrra)
        Vab[-1, :] = (V[-1, :] - V[-2, :]) / dab[-1]

        Vaf[0, :] = (V[1, :] - V[0, :]) / daf[0]
        Vab[0, :] = income(h_ss[0, :], zz[0, :], profshare[0, :], lumptransfer,
                           r, amin)**(-coefrra)

        Vaf[1:-1] = (V[2:, :] - V[1:-1, :]) / daf[0]
        Vab[1:-1] = (V[1:-1, :] - V[:-2, :]) / dab[0]

        # idx = ((i,j) for i in range(I) for j in range(J))
        # for t in idx:
        #     hf[t] = min(abs(labor(zz[t], Vaf[t])), maxhours)
        #     hb[t] = min(abs(labor(zz[t], Vab[t])), maxhours)

        hf = np.minimum(abs(labor(zz, Vaf)), maxhours)
        hb = np.minimum(abs(labor(zz, Vab)), maxhours)

        cf = Vaf**(-1 / coefrra)
        cb = Vab**(-1 / coefrra)

        #-----------------------------------------------------------------
        # Hours Iteration
        idx = ((i, j) for i in range(I) for j in range(J))
        for ih in range(niter_hours):
            for t in idx:
                if t[0] == I - 1:
                    cf[t] = income(hf[t], zz[t], profshare[t], lumptransfer, r,
                                   aa[t])
                    hf[t] = labor(zz[t], cf[t]**(-coefrra))
                    hf[t] = min(abs(hf[t]), maxhours)
                    if ih == niter_hours - 1:
                        Vaf[t] = cf[t]**(-coefrra)

                elif t[0] == 0:
                    cb[t] = income(hb[t], zz[t], profshare[t], lumptransfer, r,
                                   aa[t])
                    hb[t] = labor(zz[t], cb[t]**(-coefrra))
                    hb[t] = min(abs(hb[t]), maxhours)
                    if ih == niter_hours:
                        Vab[t] = cb[t]**(-coefrra)

            c0 = income(h0, zz, profshare, lumptransfer, r, aa)
            h0 = labor(zz, c0**(-coefrra))
            h0 = np.minimum(abs(h0.flatten()), maxhours).reshape(I, J)

        c0 = income(h0, zz, profshare, lumptransfer, r, aa)
        sf = income(hf, zz, profshare, lumptransfer, r, aa) - cf
        sb = income(hb, zz, profshare, lumptransfer, r, aa) - cb

        #-----------------------------------------------------------------
        # Upwind
        #T = sb[0,0].dtype

        Vf = (cf > 0) * (util(cf, hf) + sf * Vaf) + (cf <= 0) * (-1e12)
        Vb = (cb > 0) * (util(cb, hb) + sb * Vab) + (cb <= 0) * (-1e12)
        V0 = (c0 > 0) * util(c0, h0) + (c0 <= 0) * (-1e12)

        Iunique = (sb < 0) * (1 - (sf > 0)) + (1 - (sb < 0)) * (sf > 0)
        Iboth = (sb < 0) * (sf > 0)
        Ib = Iunique * (sb < 0) * (Vb > V0) + Iboth * (Vb == np.maximum(
            np.maximum(Vb, Vf), V0))
        If = Iunique * (sf > 0) * (Vf > V0) + Iboth * (Vf == np.maximum(
            np.maximum(Vb, Vf), V0))
        I0 = 1 - Ib - If

        h = hf * If + hb * Ib + h0 * I0
        c = cf * If + cb * Ib + c0 * I0
        s = sf * If + sb * Ib
        u = util(c, h)

        X = -Ib * sb / np.array([dab, dab]).reshape(I, J)
        Z = If * sf / np.array([daf, daf]).reshape(I, J)
        Y = -Z - X

        X[0, :] = 0.
        Z[I - 1, :] = 0.

        A = spdiag([
            X.reshape(I * J, order='F')[1:],
            Y.reshape(I * J, order='F'),
            Z.reshape(I * J, order='F')[:I * J - 1]
        ],
                   offsets=[-1, 0, 1],
                   shape=(I * J, I * J),
                   format='csc') + A_switch

        #-----------------------------------------------------------------
        # Collect/calculate Residuals
        hjb_residual = u.flatten(order='F') + A * V.flatten(order='F') \
                        + V_dot + VEErrors  - rho_ss * V.flatten(order='F')

        pc_residual  = -((r - 0) * inflation - (ceselast / priceadjust * \
                                                (w / TFP - (ceselast-1) / ceselast) + \
                                                inflation_dot - inflation_error))

        g_azdelta = g.flatten() * azdelta.flatten()
        g_intermediate = spdiag(1 / azdelta) * A.T @ g_azdelta
        g_residual = g_dot - g_intermediate[:-1]

        mp_residual = mp_dot - (-theta_MP * MP + sig_MP * mp_shock)

        realsav = sum(
            aa.flatten(order='F') * g.flatten(order='F') *
            azdelta.flatten(order='F'))
        realsav_dot = sum(
            s.flatten(order='F') * g.flatten(order='F') *
            azdelta.flatten(order='F'))
        bondmarket_residual = realsav_dot / realsav + govbcrule_fixnomB * inflation

        labmarket_residual = sum(zz.flatten(order='F') * h.flatten(order='F') \
                                 * g.flatten(order='F') * azdelta.flatten(order='F')) - hours

        consumption_residual = sum(c.flatten(order='F') * g.flatten(order='F') \
                                   * azdelta.flatten(order='F')) - consumption

        output_residual = TFP * hours - output
        #output_residual = TFP * hours - (-theta_PS * output + sig_PS * ps_shock)

        assets_residual = assets - realsav

        #government_residual = fp_dot - (-theta_FP * government + sig_FP * fp_shock)

        # Return equilibrium conditions
        return np.hstack(
            (hjb_residual, pc_residual, g_residual, mp_residual,
             bondmarket_residual, labmarket_residual, consumption_residual,
             output_residual, assets_residual))
示例#4
0
def steadystate(model):
    # Read in parameters
    coefrra = model.params['coefrra'].value
    frisch = model.params['frisch'].value
    meanlabeff = model.params['meanlabeff'].value
    maxhours = model.params['maxhours'].value
    ceselast = model.params['ceselast'].value
    labtax = model.params['labtax'].value
    govbondtarget = model.params['govbondtarget'].value
    labdisutil = model.params['labdisutil'].value
    lumptransferpc = model.params['lumptransferpc'].value

    # Read in grids
    I = model.settings['I'].value
    J = model.settings['J'].value
    a = model.settings['a'].value
    g_z = model.settings['g_z'].value
    zz = model.settings['zz'].value
    ymarkov_combined = model.settings['ymarkov_combined'].value

    # Set necessary variables
    aa = np.repeat(a.reshape(-1, 1), J, axis=1)
    amax = np.max(a)
    amin = np.min(a)

    # Read in initial rates
    iterate_r = model.settings['iterate_r'].value
    r = model.settings['r0'].value
    rmin = model.settings['rmin'].value
    rmax = model.settings['rmax'].value
    iterate_rho = model.settings['iterate_rho'].value
    rho = model.settings['rho0'].value
    rhomin = model.settings['rhomin'].value
    rhomax = model.settings['rhomax'].value

    # Read in approximation parameters
    Ir = model.settings['Ir'].value
    maxit_HJB = model.settings['maxit_HJB'].value
    tol_HJB = model.settings['tol_HJB'].value
    d_HJB = model.settings['d_HJB'].value
    maxit_kfe = model.settings['maxit_kfe'].value
    tol_kfe = model.settings['tol_kfe'].value
    d_kfe = model.settings['d_kfe'].value
    niter_hours = model.settings['niter_hours'].value
    crit_S = model.settings['crit_S'].value

    # Initializing equilibrium objects
    labor_share_ss = (ceselast - 1) / ceselast
    w = w_ss = labor_share_ss

    # compute initial guesses at steady state values given zz, labor_share_ss, etc.
    N_ss, Y_ss, B_ss, profit_ss, profshare, lumptransfer = \
        aux.calculate_ss_equil_vars_init(zz, labor_share_ss,
                                         meanlabeff, lumptransferpc, govbondtarget)

    # Initialize matrices for finite differences
    Vaf = np.empty((I, J), dtype=np.complex64)
    Vab = np.empty((I, J), dtype=np.complex64)

    cf = np.empty((I, J), dtype=np.complex64)  # forward consumption difference
    hf = np.empty((I, J), dtype=np.complex64)  # forward hours difference
    sf = np.empty((I, J), dtype=np.complex64)  # forward saving difference
    cb = np.empty((I, J),
                  dtype=np.complex64)  # backward consumption difference
    hb = np.empty((I, J), dtype=np.complex64)  # backward hours difference
    sb = np.empty((I, J), dtype=np.complex64)  # backward saving difference
    c0 = np.empty((I, J), dtype=np.complex64)
    A = np.empty((I * J, J * J), dtype=np.complex64)

    Aswitch = spkron(ymarkov_combined, speye(I, dtype='complex64'))

    # Initialize steady state variables
    V = np.empty((I, J), dtype=np.complex64)  # value function
    u = np.empty((I, J), dtype=np.complex64)  # flow utility across state space
    s = np.empty((I, J), dtype=np.complex64)  # savings across state space
    c = np.empty((I, J), dtype=np.complex64)  # flow consumption
    h = np.empty((I, J), dtype=np.complex64)  # flow hours of labor
    h0 = np.empty((I, J), dtype=np.complex64)  # guess of what h will be

    # Creates functions for computing flow utility, income earned, and labor done given
    # CRRA + frisch elasticity style labor disutility
    util, income, labor = \
        aux.construct_household_problem_functions(V, w, coefrra, frisch, labtax, labdisutil)

    # Setting up forward/backward difference grids for a.
    daf, dab, azdelta = aux.initialize_diff_grids(a, I, J)

    for ir in range(Ir):
        c.fill(np.complex(0.))
        h.fill(np.complex(1 / 3))
        h0.fill(np.complex(1.))

        # Initial guess
        inc = income(h, zz, profshare, lumptransfer, r, aa)  # get income
        v = util(inc, h) / rho  # value function guess

        # Iterate HJB
        for ihjb in range(maxit_HJB):
            V = v
            Vaf, Vab, cf, hf, cb, hb = aux.construct_initial_diff_matrices(
                V, Vaf, Vab, income, labor, h, h0, zz, profshare, lumptransfer,
                amax, amin, coefrra, r, daf, dab, maxhours)
            # Iterative method to find consistent forward/backward/neutral
            # difference matrices for c and h
            cf, hf, cb, hb, c0, h0 = aux.hours_iteration(
                income, labor, zz, profshare, lumptransfer, aa, coefrra, r, cf,
                hf, cb, hb, c0, h0, maxhours, niter_hours)

            c0 = income(h0, zz, profshare, lumptransfer, r, aa)
            sf = income(hf, zz, profshare, lumptransfer, r, aa) - cf
            sb = income(hb, zz, profshare, lumptransfer, r, aa) - cb

            Vaf[I - 1, :] = cf[I - 1, :]**(
                -coefrra
            )  # Forward difference for value function w.r.t. wealth
            Vab[0, :] = cb[0, :]**(
                -coefrra
            )  # Backward difference for value function w.r.t. wealth

            V, A, u, h, c, s = aux.upwind(rho,
                                          V,
                                          util,
                                          Aswitch,
                                          cf,
                                          cb,
                                          c0,
                                          hf,
                                          hb,
                                          h0,
                                          sf,
                                          sb,
                                          Vaf,
                                          Vab,
                                          daf,
                                          dab,
                                          d_HJB=d_HJB)

            # Check for convergence
            Vchange = V - v
            v = V

            err_HJB = np.max(np.abs(Vchange))
            if err_HJB < tol_HJB:
                break
        # Create initial guess for g0
        g0 = np.zeros((I, J), dtype=np.complex64)
        # Assign stationary income distribution weight at a = 0, zero elsewhere
        g0[a == 0, :] = g_z
        # g_z is marginal distribution, so re-weight by some multiplier of Lebesgue measure
        g0 = g0 / azdelta.reshape(I, J)

        # Solve for distribution
        g = aux.solve_kfe(A,
                          g0,
                          spdiag(azdelta),
                          maxit_kfe=maxit_kfe,
                          tol_kfe=tol_kfe,
                          d_kfe=d_kfe)
        # Back out static conditions/steady state values given our value function and distribution
        N_ss, Y_ss, B_ss, profit_ss, profshare, lumptransfer, bond_err = \
            aux.calculate_ss_equil_vars(zz, h, g, azdelta, aa, labor_share_ss, meanlabeff,
                                        lumptransferpc, govbondtarget)

        # Check bond market for market clearing
        r, rmin, rmax, rho, rhomin, rhomax, clear_cond = \
            aux.check_bond_market_clearing(bond_err, crit_S, r, rmin, rmax, rho, rhomin, rhomax,
                                           iterate_r, iterate_rho)
        if clear_cond:
            # Set steady state values
            model.steady_state['V_ss'].value = np.real(V.flatten(order='F'))
            model.steady_state['inflation_ss'].value = 0.
            model.steady_state['g_ss'].value = np.real(g.flatten(order='F'))
            model.steady_state['r_ss'].value = r
            model.steady_state['u_ss'].value = np.real(u.flatten(order='F'))
            model.steady_state['c_ss'].value = np.real(c.flatten(order='F'))
            model.steady_state['h_ss'].value = np.real(h.flatten(order='F'))
            model.steady_state['s_ss'].value = np.real(s.flatten(order='F'))
            model.steady_state['rnom_ss'].value        = model.steady_state['r_ss'].value \
                                                         + model.steady_state['inflation_ss'].value
            model.steady_state['B_ss'].value = sum(
                model.steady_state['g_ss'].value * aa.flatten(order='F') *
                azdelta)
            model.steady_state['N_ss'].value           = np.real(sum(zz.flatten(order='F') * model.steady_state['h_ss'].value \
                                                                     * model.steady_state['g_ss'].value * azdelta))
            model.steady_state['Y_ss'].value = model.steady_state['N_ss'].value
            model.steady_state['labor_share_ss'].value = (ceselast -
                                                          1) / ceselast
            model.steady_state['w_ss'].value = model.steady_state[
                'labor_share_ss'].value
            model.steady_state['profit_ss'].value      = (1 - model.steady_state['labor_share_ss'].value) \
                                                          * model.steady_state['Y_ss'].value
            model.steady_state['C_ss'].value = sum(
                model.steady_state['c_ss'].value *
                model.steady_state['g_ss'].value * azdelta)
            model.steady_state['T_ss'].value = np.real(lumptransfer)
            model.steady_state['rho_ss'].value = rho
            model.steady_state['G_ss'].value           = labtax * model.steady_state['w_ss'].value * model.steady_state['N_ss'].value \
                                                         - model.steady_state['T_ss'].value - model.steady_state['r_ss'].value * model.steady_state['B_ss'].value

            break
    return model
示例#5
0
def upwind(rho: float,
           V: np.ndarray,
           util: object,
           A_switch: spmatrix,
           cf: np.ndarray,
           cb: np.ndarray,
           c0: np.ndarray,
           hf: np.ndarray,
           hb: np.ndarray,
           h0: np.ndarray,
           sf: np.ndarray,
           sb: np.ndarray,
           Vaf: np.ndarray,
           Vab: np.ndarray,
           daf: np.ndarray,
           dab: np.ndarray,
           d_HJB: float = 1e6):

    T = sb[0, 0].dtype
    I, J = sb.shape
    #     h = np.empty_like(sb)
    #     c = np.empty_like(sb)
    #     s = np.empty_like(sb)
    #     u = np.empty_like(sb)
    #     X = np.empty_like(sb)
    #     Z = np.empty_like(sb)
    #     Y = np.empty_like(sb)

    Vf = (cf > 0) * (util(cf, hf) + sf * Vaf) + (cf <= 0) * (-1e12)
    Vb = (cb > 0) * (util(cb, hb) + sb * Vab) + (cb <= 0) * (-1e12)
    V0 = (c0 > 0) * util(c0, h0) + (c0 <= 0) * (-1e12)

    Iunique = (sb < 0) * (1 - (sf > 0)) + (1 - (sb < 0)) * (sf > 0)
    Iboth = (sb < 0) * (sf > 0)
    Ib = Iunique * (sb < 0) * (Vb > V0) + Iboth * (Vb == np.maximum(
        np.maximum(Vb, Vf), V0))
    If = Iunique * (sf > 0) * (Vf > V0) + Iboth * (Vf == np.maximum(
        np.maximum(Vb, Vf), V0))
    I0 = 1 - Ib - If

    h = hf * If + hb * Ib + h0 * I0
    c = cf * If + cb * Ib + c0 * I0
    s = sf * If + sb * Ib
    u = util(c, h)

    X = -Ib * sb / np.array([dab, dab]).reshape(I, J)
    Z = If * sf / np.array([daf, daf]).reshape(I, J)
    Y = -Z - X

    X[0, :] = complex(0.) if T == np.complex else 0
    Z[I - 1, :] = complex(0.) if T == np.complex else 0

    A = spdiag([
        X.reshape(I * J, order='F')[1:],
        Y.reshape(I * J, order='F'),
        Z.reshape(I * J, order='F')[:I * J - 1]
    ],
               offsets=[-1, 0, 1],
               shape=(I * J, I * J)) + A_switch

    I, J = u.shape
    B = (1 / d_HJB + rho) * speye(I * J, dtype=T) - A
    b = u.reshape(I * J, order='F') + V.reshape(I * J, order='F') / d_HJB

    V = spsolve(B, b).reshape(I, J, order='F')
    #V = scipy.sparse.linalg.lsqr(B,b)[0].reshape(I, J,order='F')

    return V, A, u, h, c, s
示例#6
0
 def E(self):
     return spdiag(np.ones(len(self._poles)))
示例#7
0
 def A(self):
     return spdiag(self._poles)
def gensys_hank(GAM1, C, PSI, PI,
               check_existence=True, check_uniqueness=True,
               eps = np.sqrt(np.finfo(float).eps)*10, div=-1.0):
    """
    Solve a liner rational expectations model by Sims(2002)
    Generate state-space solution to DSGE model system given as:

        GAM0 s(t) = GAM1 s(t-1) + c + PSI eps(t) + PI eta(t)

    Return system is:

        s(t) = G1 * s(t-1) + c + impact * eps(t)

    Input
    -----
    GAM0:  n x n matrix
    GAM1:  n x n matrix
    PSI: n x m matrix
    PI:  n x p

    Return
    -----
    G1: n x n matrix
    impact: n x m matrix
    eu:
        eu[0] = 1 for existence
        eu[1] = 1 for uniqueness
        eu  = [-2, -2] for coincident zeros
    """

    eu          = [0, 0]
    T, U = schur(GAM1)
    n           = U.shape[0]
    g_eigs      = np.real(eigvals(T))
    stable_eigs = lambda eigs :eigs <= 0
    nunstab     = n - sum(stable_eigs(g_eigs))

    T, U, _ = schur(GAM1, sort=stable_eigs)

    U1 = U[:, :n - nunstab].T
    U2 = U[:, n - nunstab : n].T
    etawt = U2 @ PI

    _, ueta, deta, veta = decomposition_svdct(etawt, eps=eps)

    if check_existence:
        zwt = U2 @ PSI
        bigev, uz, dz, vz = decomposition_svdct(zwt, eps=eps)
        if all(bigev) == False:
            eu[0] = 1
        else:
            eu[0] = np.linalg.norm(uz-(ueta @ ueta.conj().T) @ uz) < eps * n

        # if (eu[0] == 0) & (div == -1):
        #     warnings.warn('Solution does not exist')
        impact = np. real(-PI @ veta @ solve(deta, ueta.T) @ uz @ dz @ vz.T + PSI)
    else:
        eu[0] = 1
        impact = np. real(-PI @ veta @ solve(deta, ueta.T) @ U2 + PSI)

    if check_uniqueness:
        etawt1 = U1 @ PI
        bigev, _, deta1, veta1 = decomposition_svdct(etawt1)
        if all(bigev) == False:
            eu[1] = 1
        else:
            eu[1] = np.linalg.norm(veta1 - (veta @ veta.conj().T) @ veta1) < eps * n

#     spdiag_internal = lambda n1, n2: (*spdiag(np.hstack([np.ones(n1),np.zeros(n2)])).nonzero(), \
#                                       np.hstack([np.ones(n1),np.zeros(n2)]))
#     I, J, V = spdiag_internal(n-nunstab, nunstab)
#     diag_m = csc_matrix((V, (I, J)), shape=(n, n))
    diag_m = spdiag(np.hstack([np.ones(n-nunstab),np.zeros(nunstab)]))
    G1 = np.real(U @ T @ diag_m @ U.T)
    F = U1[:, :nunstab].T @ np.linalg.inv(U1[:, nunstab:].T)
    impact = np.vstack([F @ PSI[nunstab:, :], PSI[nunstab:, :]])
    C = np.real(U @ C) * np.ones([U.shape[0], 1])

    return G1, C, impact, U, T, eu