示例#1
0
def second_order_solver(FF,GG,HH, eigmax=1.0+1e-6):

    # from scipy.linalg import qz
    from dolo.numeric.extern.qz import qzordered

    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)]]

    [Delta_up,Xi_up,UUU,VVV,eigval] = qzordered(Delta_mat, Xi_mat,)

    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

    return [eigval,PP]
示例#2
0
def classical_perturbation(g_s, g_x, f_s, f_x, f_S, f_X):
    n_s = g_s.shape[0]  # number of controls
    n_x = g_x.shape[1]  # number of states
    n_v = n_s + n_x

    A = row_stack([
        column_stack([eye(n_s), zeros((n_s, n_x))]),
        column_stack([-f_S, -f_X])
    ])

    B = row_stack([column_stack([g_s, g_x]), column_stack([f_s, f_x])])

    [S, T, Q, Z, eigval] = qzordered(A, B, 1.0 - 1e-8)

    Q = Q.real  # is it really necessary ?
    Z = Z.real

    diag_S = np.diag(S)
    diag_T = np.diag(T)

    tol_geneigvals = 1e-10

    Z11 = Z[:n_s, :n_s]
    # Z12 = Z[:n_s, n_s:]
    Z21 = Z[n_s:, :n_s]
    # Z22 = Z[n_s:, n_s:]
    # S11 = S[:n_s, :n_s]
    # T11 = T[:n_s, :n_s]

    # first order solution
    # P = (solve(S11.T, Z11.T).T @ solve(Z11.T, T11.T).T)
    C = solve(Z11.T, Z21.T).T
    return C, eigval
示例#3
0
def second_order_solver(FF,GG,HH, eigmax=1.0+1e-6):

    # from scipy.linalg import qz
    from dolo.numeric.extern.qz import qzordered

    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)]]

    [Delta_up,Xi_up,UUU,VVV,eigval] = qzordered(Delta_mat, Xi_mat,)

    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

    return [eigval,PP]
示例#4
0
def classical_perturbation(g_s, g_x, f_s, f_x, f_S, f_X):
    n_s = g_s.shape[0]           # number of controls
    n_x = g_x.shape[1]   # number of states
    n_v = n_s + n_x

    A = row_stack([
        column_stack([eye(n_s), zeros((n_s, n_x))]),
        column_stack([-f_S    , -f_X             ])
    ])

    B = row_stack([
        column_stack([g_s, g_x]),
        column_stack([f_s, f_x])
    ])


    [S, T, Q, Z, eigval] = qzordered(A, B, 1.0-1e-8)

    Q = Q.real  # is it really necessary ?
    Z = Z.real

    diag_S = np.diag(S)
    diag_T = np.diag(T)

    tol_geneigvals = 1e-10

    Z11 = Z[:n_s, :n_s]
    # Z12 = Z[:n_s, n_s:]
    Z21 = Z[n_s:, :n_s]
    # Z22 = Z[n_s:, n_s:]
    # S11 = S[:n_s, :n_s]
    # T11 = T[:n_s, :n_s]

    # first order solution
    # P = (solve(S11.T, Z11.T).T @ solve(Z11.T, T11.T).T)
    C = solve(Z11.T, Z21.T).T
    return C, eigval
示例#5
0
def approximate_controls(model, verbose=False, steady_state=None, eigmax=1.0, solve_steady_state=False, order=1):
    """Compute first order approximation of optimal controls

    Parameters:
    -----------

    model: NumericModel
        Model to be solved

    verbose: boolean
        If True: displays number of contracting eigenvalues

    steady_state: ndarray
        Use supplied steady-state value to compute the approximation. The routine doesn't check whether it is really
        a solution or not.

    solve_steady_state: boolean
        Use nonlinear solver to find the steady-state

    orders: {1}
        Approximation order. (Currently, only first order is supported).

    Returns:
    --------

    TaylorExpansion:
        Decision Rule for the optimal controls around the steady-state.

    """


    if order>1:
        raise Exception("Not implemented.")

    # get steady_state
    import numpy

    # if model.model_type == 'fga':
    #     model = GModel_fg_from_fga(model)

    # g = model.functions['transition']
    # f = model.functions['arbitrage']
    from dolo.algos.convert import get_fg_functions
    [f,g] = get_fg_functions(model)

    if steady_state is not None:
        calib = steady_state
    else:
        calib = model.calibration

    if solve_steady_state:
        from dolo.algos.steady_state import find_deterministic_equilibrium
        calib = find_deterministic_equilibrium(model)

    p = calib['parameters']
    s = calib['states']
    x = calib['controls']
    e = calib['shocks']

    if model.covariances is not None:
        sigma = model.covariances
    else:
        sigma = numpy.zeros((len(e), len(e)))

    from numpy.linalg import solve


    l = g(s,x,e,p, diff=True)
    [junk, g_s, g_x, g_e] = l[:4] # [el[0,...] for el in l[:4]]

    l = f(s,x,e,s,x,p, diff=True)
    [res, f_s, f_x, f_e, f_S, f_X] = l #[el[0,...] for el in l[:6]]

    n_s = g_s.shape[0]           # number of controls
    n_x = g_x.shape[1]   # number of states
    n_e = g_e.shape[1]
    n_v = n_s + n_x

    A = row_stack([
        column_stack( [ eye(n_s), zeros((n_s,n_x)) ] ),
        column_stack( [ -f_S    , -f_X             ] )
    ])

    B = row_stack([
        column_stack( [ g_s, g_x ] ),
        column_stack( [ f_s, f_x ] )
    ])

    from dolo.numeric.extern.qz import qzordered
    [S,T,Q,Z,eigval] = qzordered(A,B,n_s)
    Q = Q.real # is it really necessary ?
    Z = Z.real

    diag_S = numpy.diag(S)
    diag_T = numpy.diag(T)

    # Check Blanchard=Kahn conditions
    n_big_one = sum(eigval>eigmax)
    n_expected = n_x
    if verbose:
        print( "There are {} eigenvalues greater than {}. Expected: {}.".format( n_big_one, eigmax, n_x ) )
    if n_expected != n_big_one:
        raise BlanchardKahnError(n_big_one, n_expected)


    tol_geneigvals = 1e-10
    try:
        assert( sum(  (abs( diag_S ) < tol_geneigvals) * (abs(diag_T) < tol_geneigvals) ) == 0)
    except Exception as e:
        print e
        print(numpy.column_stack([diag_S, diag_T]))
        # raise GeneralizedEigenvaluesError(diag_S, diag_T)


    Z11 = Z[:n_s,:n_s]
    Z12 = Z[:n_s,n_s:]
    Z21 = Z[n_s:,:n_s]
    Z22 = Z[n_s:,n_s:]
    S11 = S[:n_s,:n_s]
    T11 = T[:n_s,:n_s]

    # first order solution
    C = solve(Z11.T, Z21.T).T
    P = np.dot(solve(S11.T, Z11.T).T , solve(Z11.T, T11.T).T )
    Q = g_e

    s = s.ravel()
    x = x.ravel()

    A = g_s + dot( g_x, C )
    B = g_e

    dr = CDR([s, x, C])
    dr.A = A
    dr.B = B
    dr.sigma = sigma
   
    return dr
示例#6
0
def approximate_1st_order(g_s, g_x, g_e, f_s, f_x, f_S, f_X):


    n_s = g_s.shape[0]           # number of controls
    n_x = g_x.shape[1]   # number of states
    n_e = g_e.shape[1]
    n_v = n_s + n_x

    A = row_stack([
        column_stack([eye(n_s), zeros((n_s, n_x))]),
        column_stack([-f_S    , -f_X             ])
    ])

    B = row_stack([
        column_stack([g_s, g_x]),
        column_stack([f_s, f_x])
    ])


    [S, T, Q, Z, eigval] = qzordered(A, B, 1.0-1e-8)

    Z = Z.real

    diag_S = np.diag(S)
    diag_T = np.diag(T)

    tol_geneigvals = 1e-10

    try:
        ok = sum((abs(diag_S) < tol_geneigvals) *
                 (abs(diag_T) < tol_geneigvals)) == 0
        assert(ok)
    except Exception as e:
        raise GeneralizedEigenvaluesError(diag_S=diag_S, diag_T=diag_T)

    eigval_s = sorted(eigval, reverse=False)
    if max(eigval[:n_s]) >= 1 and min(eigval[n_s:]) < 1:
        # BK conditions are met
        pass
    else:
        ev_a = eigval_s[n_s-1]
        ev_b = eigval_s[n_s]
        cutoff = (ev_a - ev_b)/2
        if not ev_a > ev_b:
            raise GeneralizedEigenvaluesSelectionError(
                    A=A, B=B, eigval=eigval, cutoff=cutoff,
                    diag_S=diag_S, diag_T=diag_T, n_states=n_s
                )
        import warnings
        if cutoff > 1:
            warnings.warn("Solution is not convergent.")
        else:
            warnings.warn("There are multiple convergent solutions. The one with the smaller eigenvalues was selected.")
        [S, T, Q, Z, eigval] = qzordered(A, B, cutoff)

    Z11 = Z[:n_s, :n_s]
    # Z12 = Z[:n_s, n_s:]
    Z21 = Z[n_s:, :n_s]
    # Z22 = Z[n_s:, n_s:]
    # S11 = S[:n_s, :n_s]
    # T11 = T[:n_s, :n_s]

    # first order solution
    # P = (solve(S11.T, Z11.T).T @ solve(Z11.T, T11.T).T)
    C = solve(Z11.T, Z21.T).T

    A = g_s + g_x @ C
    B = g_e

    return C, eigval_s
示例#7
0
def state_perturb(problem: PerturbationProblem, verbose=True):
    """Computes a Taylor approximation of decision rules, given the supplied derivatives.

    The original system is assumed to be in the the form:

    .. math::

        E_t f(s_t,x_t,s_{t+1},x_{t+1})

        s_t = g(s_{t-1},x_{t-1}, \\lambda \\epsilon_t)

    where :math:`\\lambda` is a scalar scaling down the risk.  the solution is a function :math:`\\varphi` such that:

    .. math::

        x_t = \\varphi ( s_t, \\sigma )

    The user supplies, a list of derivatives of f and g.

    :param f_fun: list of derivatives of f [order0, order1, order2, ...]
    :param g_fun: list of derivatives of g [order0, order1, order2, ...]
    :param sigma: covariance matrix of :math:`\\epsilon_t`


    Assuming :math:`s_t` ,  :math:`x_t` and :math:`\\epsilon_t` are vectors of size
    :math:`n_s`, :math:`n_x`  and :math:`n_x`  respectively.
    In general the derivative of order :math:`i` of :math:`f`  is a multimensional array of size :math:`n_x \\times (N, ..., N)`
    with :math:`N=2(n_s+n_x)` repeated :math:`i` times (possibly 0).
    Similarly the derivative of order :math:`i` of :math:`g`  is a multidimensional array of size :math:`n_s \\times (M, ..., M)`
    with :math:`M=n_s+n_x+n_2` repeated :math:`i` times (possibly 0).
    """

    import numpy as np
    from numpy.linalg import solve

    approx_order = problem.order  # order of approximation

    [f0, f1] = problem.f[:2]
    [g0, g1] = problem.g[:2]
    sigma = problem.sigma

    n_x = f1.shape[0]  # number of controls
    n_s = f1.shape[1] // 2 - n_x  # number of states
    n_e = g1.shape[1] - n_x - n_s
    n_v = n_s + n_x

    f_s = f1[:, :n_s]
    f_x = f1[:, n_s:n_s + n_x]
    f_snext = f1[:, n_v:n_v + n_s]
    f_xnext = f1[:, n_v + n_s:]

    g_s = g1[:, :n_s]
    g_x = g1[:, n_s:n_s + n_x]
    g_e = g1[:, n_v:]

    A = np.row_stack([
        np.column_stack([np.eye(n_s), np.zeros((n_s, n_x))]),
        np.column_stack([-f_snext, -f_xnext])
    ])
    B = np.row_stack(
        [np.column_stack([g_s, g_x]),
         np.column_stack([f_s, f_x])])

    [S, T, Q, Z, eigval] = qzordered(A, B, 1.0 - 1e-8)

    Q = Q.real  # is it really necessary ?
    Z = Z.real

    diag_S = np.diag(S)
    diag_T = np.diag(T)

    tol_geneigvals = 1e-10

    try:
        ok = sum((abs(diag_S) < tol_geneigvals) *
                 (abs(diag_T) < tol_geneigvals)) == 0
        assert (ok)
    except Exception as e:
        raise GeneralizedEigenvaluesError(diag_S=diag_S, diag_T=diag_T)

    if max(eigval[:n_s]) >= 1 and min(eigval[n_s:]) < 1:
        # BK conditions are met
        pass
    else:
        eigval_s = sorted(eigval, reverse=True)
        ev_a = eigval_s[n_s - 1]
        ev_b = eigval_s[n_s]
        cutoff = (ev_a - ev_b) / 2
        if not ev_a > ev_b:
            raise GeneralizedEigenvaluesSelectionError(A=A,
                                                       B=B,
                                                       eigval=eigval,
                                                       cutoff=cutoff,
                                                       diag_S=diag_S,
                                                       diag_T=diag_T,
                                                       n_states=n_s)
        import warnings
        if cutoff > 1:
            warnings.warn("Solution is not convergent.")
        else:
            warnings.warn(
                "There are multiple convergent solutions. The one with the smaller eigenvalues was selected."
            )
        [S, T, Q, Z, eigval] = qzordered(A, B, cutoff)

    Z11 = Z[:n_s, :n_s]
    # Z12 = Z[:n_s, n_s:]
    Z21 = Z[n_s:, :n_s]
    # Z22 = Z[n_s:, n_s:]
    S11 = S[:n_s, :n_s]
    T11 = T[:n_s, :n_s]

    # first order solution
    C = solve(Z11.T, Z21.T).T
    P = np.dot(solve(S11.T, Z11.T).T, solve(Z11.T, T11.T).T)
    Q = g_e

    # if False:
    #     from numpy import dot
    #     test = f_s + f_x @ C + f_snext @ (g_s + g_x @ C) + f_xnext @ C @ (g_s + g_x @ C)
    #     print('Error: ' + str(abs(test).max()))

    if approx_order == 1:
        return [C]

    # second order solution
    from dolo.numeric.tensor import sdot, mdot
    from numpy import dot
    from dolo.numeric.matrix_equations import solve_sylvester

    f2 = problem.f[2]
    g2 = problem.g[2]
    g_ss = g2[:, :n_s, :n_s]
    g_sx = g2[:, :n_s, n_s:n_v]
    g_xx = g2[:, n_s:n_v, n_s:n_v]

    X_s = C

    V1_3 = g_s + dot(g_x, X_s)
    V1 = np.row_stack([np.eye(n_s), X_s, V1_3, X_s @ V1_3])

    K2 = g_ss + 2 * sdot(g_sx, X_s) + mdot(g_xx, X_s, X_s)
    A = f_x + dot(f_snext + dot(f_xnext, X_s), g_x)
    B = f_xnext
    C = V1_3
    D = mdot(f2, V1, V1) + sdot(f_snext + dot(f_xnext, X_s), K2)

    X_ss = solve_sylvester(A, B, C, D)

    #    test = sdot( A, X_ss ) + sdot( B,  mdot(X_ss,V1_3,V1_3) ) + D

    g_ee = g2[:, n_v:, n_v:]

    v = np.row_stack([g_e, dot(X_s, g_e)])

    K_tt = mdot(f2[:, n_v:, n_v:], v, v)
    K_tt += sdot(f_snext + dot(f_xnext, X_s), g_ee)
    K_tt += mdot(sdot(f_xnext, X_ss), g_e, g_e)
    K_tt = np.tensordot(K_tt, sigma, axes=((1, 2), (0, 1)))

    L_tt = f_x + dot(f_snext, g_x) + dot(f_xnext, dot(X_s, g_x) + np.eye(n_x))
    X_tt = solve(L_tt, -K_tt)

    if approx_order == 2:
        return [[X_s, X_ss], [X_tt]]

    # third order solution

    f3 = problem.f[3]
    g3 = problem.g[3]
    g_sss = g3[:, :n_s, :n_s, :n_s]
    g_ssx = g3[:, :n_s, :n_s, n_s:n_v]
    g_sxx = g3[:, :n_s, n_s:n_v, n_s:n_v]
    g_xxx = g3[:, n_s:n_v, n_s:n_v, n_s:n_v]

    V2_3 = K2 + sdot(g_x, X_ss)
    V2 = np.row_stack([
        np.zeros((n_s, n_s, n_s)), X_ss, V2_3,
        dot(X_s, V2_3) + mdot(X_ss, V1_3, V1_3)
    ])

    K3 = g_sss + 3 * sdot(g_ssx, X_s) + 3 * mdot(g_sxx, X_s, X_s) + 2 * sdot(
        g_sx, X_ss)
    K3 += 3 * mdot(g_xx, X_ss, X_s) + mdot(g_xxx, X_s, X_s, X_s)
    L3 = 3 * mdot(X_ss, V1_3, V2_3)

    # A = f_x + dot( f_snext + dot(f_xnext,X_s), g_x) # same as before
    # B = f_xnext # same
    # C = V1_3 # same
    D = mdot(f3, V1, V1, V1) + 3 * mdot(f2, V2, V1) + sdot(
        f_snext + dot(f_xnext, X_s), K3)
    D += sdot(f_xnext, L3)

    X_sss = solve_sylvester(A, B, C, D)

    # now doing sigma correction with sigma replaced by l in the subscripts

    g_se = g2[:, :n_s, n_v:]
    g_xe = g2[:, n_s:n_v, n_v:]

    g_see = g3[:, :n_s, n_v:, n_v:]
    g_xee = g3[:, n_s:n_v, n_v:, n_v:]

    W_l = np.row_stack([g_e, dot(X_s, g_e)])

    I_e = np.eye(n_e)

    V_sl = g_se + mdot(g_xe, X_s, np.eye(n_e))

    W_sl = np.row_stack([V_sl, mdot(X_ss, V1_3, g_e) + sdot(X_s, V_sl)])

    K_ee = mdot(f3[:, :, n_v:, n_v:], V1, W_l, W_l)
    K_ee += 2 * mdot(f2[:, n_v:, n_v:], W_sl, W_l)

    # stochastic part of W_ll

    SW_ll = np.row_stack([g_ee, mdot(X_ss, g_e, g_e) + sdot(X_s, g_ee)])

    DW_ll = np.concatenate(
        [X_tt, dot(g_x, X_tt),
         dot(X_s, sdot(g_x, X_tt)) + X_tt])

    K_ee += mdot(f2[:, :, n_v:], V1, SW_ll)

    K_ = np.tensordot(K_ee, sigma, axes=((2, 3), (0, 1)))

    K_ += mdot(f2[:, :, n_s:], V1, DW_ll)

    def E(vec):
        n = len(vec.shape)
        return np.tensordot(vec, sigma, axes=((n - 2, n - 1), (0, 1)))

    L = sdot(g_sx, X_tt) + mdot(g_xx, X_s, X_tt)

    L += E(g_see + mdot(g_xee, X_s, I_e, I_e))

    M = E(mdot(X_sss, V1_3, g_e, g_e) + 2 * mdot(X_ss, V_sl, g_e))
    M += mdot(X_ss, V1_3, E(g_ee) + sdot(g_x, X_tt))

    A = f_x + dot(f_snext + dot(f_xnext, X_s), g_x)  # same as before
    B = f_xnext  # same
    C = V1_3  # same
    D = K_ + dot(f_snext + dot(f_xnext, X_s), L) + dot(f_xnext, M)

    X_stt = solve_sylvester(A, B, C, D)

    if approx_order == 3:
        # if sigma is None:
        #     return [X_s,X_ss,X_sss]
        # else:
        #     return [[X_s,X_ss,X_sss],[X_tt, X_stt]]
        return [[X_s, X_ss, X_sss], [X_tt, X_stt]]
def state_perturb(f_fun, g_fun, sigma, sigma2_correction=None, verbose=True, eigmax=1.00000):
    """Computes a Taylor approximation of decision rules, given the supplied derivatives.

    The original system is assumed to be in the the form:

    .. math::

        E_t f(s_t,x_t,s_{t+1},x_{t+1})

        s_t = g(s_{t-1},x_{t-1}, \\lambda \\epsilon_t)

    where :math:`\\lambda` is a scalar scaling down the risk.  the solution is a function :math:`\\varphi` such that:

    .. math::

        x_t = \\varphi ( s_t, \\sigma )

    The user supplies, a list of derivatives of f and g.

    :param f_fun: list of derivatives of f [order0, order1, order2, ...]
    :param g_fun: list of derivatives of g [order0, order1, order2, ...]
    :param sigma: covariance matrix of :math:`\\epsilon_t`
    :param sigma2_correction: (optional) first and second derivatives of g w.r.t. sigma if :math:`g` explicitely depends
        :math:`sigma`


    Assuming :math:`s_t` ,  :math:`x_t` and :math:`\\epsilon_t` are vectors of size
    :math:`n_s`, :math:`n_x`  and :math:`n_x`  respectively.
    In general the derivative of order :math:`i` of :math:`f`  is a multimensional array of size :math:`n_x \\times (N, ..., N)`
    with :math:`N=2(n_s+n_x)` repeated :math:`i` times (possibly 0).
    Similarly the derivative of order :math:`i` of :math:`g`  is a multidimensional array of size :math:`n_s \\times (M, ..., M)`
    with :math:`M=n_s+n_x+n_2` repeated :math:`i` times (possibly 0).



    """

    import numpy as np
    from numpy.linalg import solve

    approx_order = len(f_fun) - 1 # order of approximation

    [f0,f1] = f_fun[:2]

    [g0,g1] = g_fun[:2]
    n_x = f1.shape[0]           # number of controls
    n_s = f1.shape[1]/2 - n_x   # number of states
    n_e = g1.shape[1] - n_x - n_s
    n_v = n_s + n_x

    f_s = f1[:,:n_s]
    f_x = f1[:,n_s:n_s+n_x]
    f_snext = f1[:,n_v:n_v+n_s]
    f_xnext = f1[:,n_v+n_s:]

    g_s = g1[:,:n_s]
    g_x = g1[:,n_s:n_s+n_x]
    g_e = g1[:,n_v:]

    A = np.row_stack([
        np.column_stack( [ np.eye(n_s), np.zeros((n_s,n_x)) ] ),
        np.column_stack( [ -f_snext    , -f_xnext             ] )
    ])
    B = np.row_stack([
        np.column_stack( [ g_s, g_x ] ),
        np.column_stack( [ f_s, f_x ] )
    ])



    from dolo.numeric.extern.qz import qzordered
    [S,T,Q,Z,eigval] = qzordered(A,B,n_s)

    # Check Blanchard=Kahn conditions
    n_big_one = sum(eigval>eigmax)
    n_expected = n_x
    if verbose:
        print( "There are {} eigenvalues greater than 1. Expected: {}.".format( n_big_one, n_x ) )

    if n_big_one != n_expected:
        raise Exception("There should be exactly {} eigenvalues greater than one. Not {}.".format(n_x, n_big_one))

    Q = Q.real # is it really necessary ?
    Z = Z.real

    Z11 = Z[:n_s,:n_s]
    Z12 = Z[:n_s,n_s:]
    Z21 = Z[n_s:,:n_s]
    Z22 = Z[n_s:,n_s:]
    S11 = S[:n_s,:n_s]
    T11 = T[:n_s,:n_s]

    # first order solution
    C = solve(Z11.T, Z21.T).T
    P = np.dot(solve(S11.T, Z11.T).T , solve(Z11.T, T11.T).T )
    Q = g_e

    if False:
        from numpy import dot
        test = f_s + dot(f_x,C) + dot( f_snext, g_s + dot(g_x,C) ) + dot(f_xnext, dot( C, g_s + dot(g_x,C) ) )
        print('Error: ' + str(abs(test).max()))

    if approx_order == 1:
        return [C]

    # second order solution
    from dolo.numeric.tensor import sdot, mdot
    from numpy import dot
    from dolo.numeric.matrix_equations import solve_sylvester

    f2 = f_fun[2]
    g2 = g_fun[2]
    g_ss = g2[:,:n_s,:n_s]
    g_sx = g2[:,:n_s,n_s:n_v]
    g_xx = g2[:,n_s:n_v,n_s:n_v]

    X_s = C



    V1_3 = g_s + dot(g_x,X_s)
    V1 = np.row_stack([
        np.eye(n_s),
        X_s,
        V1_3,
        dot( X_s, V1_3 )
    ])

    K2 = g_ss + 2 * sdot(g_sx,X_s) + mdot(g_xx,[X_s,X_s])
    #L2 =
    A = f_x + dot( f_snext + dot(f_xnext,X_s), g_x)
    B = f_xnext
    C = V1_3
    D = mdot(f2,[V1,V1]) + sdot(f_snext + dot(f_xnext,X_s),K2)

    X_ss = solve_sylvester(A,B,C,D)

#    test = sdot( A, X_ss ) + sdot( B,  mdot(X_ss,[V1_3,V1_3]) ) + D


    # if sigma is not None:
    if True:
        g_ee = g2[:,n_v:,n_v:]

        v = np.row_stack([
            g_e,
            dot(X_s,g_e)
        ])

        K_tt = mdot( f2[:,n_v:,n_v:], [v,v] )
        K_tt += sdot( f_snext + dot(f_xnext,X_s) , g_ee )
        K_tt += mdot( sdot( f_xnext, X_ss), [g_e, g_e] )
        K_tt = np.tensordot( K_tt, sigma, axes=((1,2),(0,1)))

        if sigma2_correction is not None:
            K_tt += sdot( f_snext + dot(f_xnext,X_s) , sigma2_correction[0] )

        L_tt = f_x  + dot(f_snext, g_x) + dot(f_xnext, dot(X_s, g_x) + np.eye(n_x) )
        X_tt = solve( L_tt, - K_tt)

    if approx_order == 2:
        return [[X_s,X_ss],[X_tt]]

    # third order solution

    f3 = f_fun[3]
    g3 = g_fun[3]
    g_sss = g3[:,:n_s,:n_s,:n_s]
    g_ssx = g3[:,:n_s,:n_s,n_s:n_v]
    g_sxx = g3[:,:n_s,n_s:n_v,n_s:n_v]
    g_xxx = g3[:,n_s:n_v,n_s:n_v,n_s:n_v]

    V2_3 = K2 + sdot(g_x,X_ss)
    V2 = np.row_stack([
        np.zeros( (n_s,n_s,n_s) ),
        X_ss,
        V2_3,
        dot( X_s, V2_3 ) + mdot(X_ss,[V1_3,V1_3])
    ])

    K3 = g_sss + 3*sdot(g_ssx,X_s) + 3*mdot(g_sxx,[X_s,X_s]) + 2*sdot(g_sx,X_ss)
    K3 += 3*mdot( g_xx,[X_ss,X_s] ) + mdot(g_xxx,[X_s,X_s,X_s])
    L3 = 3*mdot(X_ss,[V1_3,V2_3])

    # A = f_x + dot( f_snext + dot(f_xnext,X_s), g_x) # same as before
    # B = f_xnext # same
    # C = V1_3 # same
    D = mdot(f3,[V1,V1,V1]) + 3*mdot(f2,[ V2,V1 ]) + sdot(f_snext + dot(f_xnext,X_s),K3)
    D += sdot( f_xnext, L3 )

    X_sss = solve_sylvester(A,B,C,D)

    # now doing sigma correction with sigma replaced by l in the subscripts

    # if not sigma is None:
    if True:
        g_se= g2[:,:n_s,n_v:]
        g_xe= g2[:,n_s:n_v,n_v:]

        g_see= g3[:,:n_s,n_v:,n_v:]
        g_xee= g3[:,n_s:n_v,n_v:,n_v:]


        W_l = np.row_stack([
            g_e,
            dot(X_s,g_e)
        ])

        I_e = np.eye(n_e)

        V_sl = g_se + mdot( g_xe, [X_s, np.eye(n_e)])

        W_sl = np.row_stack([
            V_sl,
            mdot( X_ss, [ V1_3, g_e ] ) + sdot( X_s, V_sl)
        ])

        K_ee = mdot(f3[:,:,n_v:,n_v:], [V1, W_l, W_l ])
        K_ee += 2 * mdot( f2[:,n_v:,n_v:], [W_sl, W_l])

        # stochastic part of W_ll

        SW_ll = np.row_stack([
            g_ee,
            mdot(X_ss, [g_e, g_e]) + sdot(X_s, g_ee)
        ])

        DW_ll = np.concatenate([
            X_tt,
            dot(g_x, X_tt),
            dot(X_s, sdot(g_x,X_tt )) + X_tt
        ])

        K_ee += mdot( f2[:,:,n_v:], [V1, SW_ll])

        K_ = np.tensordot(K_ee, sigma, axes=((2,3),(0,1)))

        K_ += mdot(f2[:,:,n_s:], [V1, DW_ll])

        def E(vec):
            n = len(vec.shape)
            return np.tensordot(vec,sigma,axes=((n-2,n-1),(0,1)))

        L = sdot(g_sx,X_tt) + mdot(g_xx,[X_s,X_tt])

        L += E(g_see + mdot(g_xee,[X_s,I_e,I_e]) )

        M = E( mdot(X_sss,[V1_3, g_e, g_e]) + 2*mdot(X_ss, [V_sl,g_e]) )
        M += mdot( X_ss, [V1_3, E( g_ee ) + sdot(g_x, X_tt)] )


        A = f_x + dot( f_snext + dot(f_xnext,X_s), g_x) # same as before
        B = f_xnext # same
        C = V1_3 # same
        D = K_ + dot( f_snext + dot(f_xnext,X_s), L) + dot( f_xnext, M )

        if sigma2_correction is not None:
            g_sl = sigma2_correction[1][:,:n_s]
            g_xl = sigma2_correction[1][:,n_s:(n_s+n_x)]
            D += dot( f_snext + dot(f_xnext,X_s), g_sl + dot(g_xl,X_s) )   # constant

        X_stt = solve_sylvester(A,B,C,D)

    if approx_order == 3:
        # if sigma is None:
        #     return [X_s,X_ss,X_sss]
        # else:
        #     return [[X_s,X_ss,X_sss],[X_tt, X_stt]]
        return [[X_s,X_ss,X_sss],[X_tt, X_stt]]
def state_perturb(f_fun, g_fun, sigma, sigma2_correction=None):
    """
    Compute the perturbation of a system in the form:
    $E_t f(s_t,x_t,s_{t+1},x_{t+1})$
    $s_t = g(s_{t-1},x_{t-1},\\epsilon_t$
    
    :param f_fun: list of derivatives of f [order0, order1, order2, ...]
    :param g_fun: list of derivatives of g [order0, order1, order2, ...]
    """
    import numpy as np
    from dolo.numeric.extern.qz import qzordered
    from numpy.linalg import solve

    approx_order = len(f_fun) - 1 # order of approximation

    [f0,f1] = f_fun[:2]

    [g0,g1] = g_fun[:2]
    n_x = f1.shape[0]           # number of controls
    n_s = f1.shape[1]/2 - n_x   # number of states
    n_e = g1.shape[1] - n_x - n_s
    n_v = n_s + n_x

    f_s = f1[:,:n_s]
    f_x = f1[:,n_s:n_s+n_x]
    f_snext = f1[:,n_v:n_v+n_s]
    f_xnext = f1[:,n_v+n_s:]

    g_s = g1[:,:n_s]
    g_x = g1[:,n_s:n_s+n_x]
    g_e = g1[:,n_v:]

    A = np.row_stack([
        np.column_stack( [ np.eye(n_s), np.zeros((n_s,n_x)) ] ),
        np.column_stack( [ -f_snext    , -f_xnext             ] )
    ])
    B = np.row_stack([
        np.column_stack( [ g_s, g_x ] ),
        np.column_stack( [ f_s, f_x ] )
    ])

    [S,T,Q,Z,eigval] = qzordered(A,B,n_s)
    
    Z11 = Z[:n_s,:n_s]
    Z12 = Z[:n_s,n_s:]
    Z21 = Z[n_s:,:n_s]
    Z22 = Z[n_s:,n_s:]
    S11 = S[:n_s,:n_s]
    T11 = T[:n_s,:n_s]

    # first order solution
    C = solve(Z11.T, Z21.T).T
    P = np.dot(solve(S11.T, Z11.T).T , solve(Z11.T, T11.T).T )
    Q = g_e

    if False:
        from numpy import dot
        test = f_s + dot(f_x,C) + dot( f_snext, g_s + dot(g_x,C) ) + dot(f_xnext, dot( C, g_s + dot(g_x,C) ) )
        print('Error: ' + str(abs(test).max()))

    if approx_order == 1:
        return [C]

    # second order solution
    from dolo.numeric.tensor import sdot, mdot
    from numpy import dot
    from dolo.numeric.matrix_equations import solve_sylvester

    f2 = f_fun[2]
    g2 = g_fun[2]
    g_ss = g2[:,:n_s,:n_s]
    g_sx = g2[:,:n_s,n_s:n_v]
    g_xx = g2[:,n_s:n_v,n_s:n_v]

    X_s = C

    V1_3 = g_s + dot(g_x,X_s)
    V1 = np.row_stack([
        np.eye(n_s),
        X_s,
        V1_3,
        dot( X_s, V1_3 )
    ])

    K2 = g_ss + 2 * sdot(g_sx,X_s) + mdot(g_xx,[X_s,X_s])
    #L2 =
    A = f_x + dot( f_snext + dot(f_xnext,X_s), g_x)
    B = f_xnext
    C = V1_3
    D = mdot(f2,[V1,V1]) + sdot(f_snext + dot(f_xnext,X_s),K2)
    
    X_ss = solve_sylvester(A,B,C,D)

#    test = sdot( A, X_ss ) + sdot( B,  mdot(X_ss,[V1_3,V1_3]) ) + D

    
    if not sigma == None:
        g_ee = g2[:,n_v:,n_v:]

        v = np.row_stack([
            g_e,
            dot(X_s,g_e)
        ])

        K_tt = mdot( f2[:,n_v:,n_v:], [v,v] )
        K_tt += sdot( f_snext + dot(f_xnext,X_s) , g_ee )
        K_tt += mdot( sdot( f_xnext, X_ss), [g_e, g_e] )
        K_tt = np.tensordot( K_tt, sigma, axes=((1,2),(0,1)))

        if sigma2_correction is not None:
            K_tt += sdot( f_snext + dot(f_xnext,X_s) , sigma2_correction[0] )

        L_tt = f_x  + dot(f_snext, g_x) + dot(f_xnext, dot(X_s, g_x) + np.eye(n_x) )
        from numpy.linalg import det
        X_tt = solve( L_tt, - K_tt)

    if approx_order == 2:
        if sigma == None:
            return [X_s,X_ss]  # here, we don't approximate the law of motion of the states
        else:
            return [[X_s,X_ss],[X_tt]]  # here, we don't approximate the law of motion of the states

    # third order solution

    f3 = f_fun[3]
    g3 = g_fun[3]
    g_sss = g3[:,:n_s,:n_s,:n_s]
    g_ssx = g3[:,:n_s,:n_s,n_s:n_v]
    g_sxx = g3[:,:n_s,n_s:n_v,n_s:n_v]
    g_xxx = g3[:,n_s:n_v,n_s:n_v,n_s:n_v]

    V2_3 = K2 + sdot(g_x,X_ss)
    V2 = np.row_stack([
        np.zeros( (n_s,n_s,n_s) ),
        X_ss,
        V2_3,
        dot( X_s, V2_3 ) + mdot(X_ss,[V1_3,V1_3])
    ])

    K3 = g_sss + 3*sdot(g_ssx,X_s) + 3*mdot(g_sxx,[X_s,X_s]) + 2*sdot(g_sx,X_ss)
    K3 += 3*mdot( g_xx,[X_ss,X_s] ) + mdot(g_xxx,[X_s,X_s,X_s])
    L3 = 3*mdot(X_ss,[V1_3,V2_3])

    # A = f_x + dot( f_snext + dot(f_xnext,X_s), g_x) # same as before
    # B = f_xnext # same
    # C = V1_3 # same
    D = mdot(f3,[V1,V1,V1]) + 3*mdot(f2,[ V2,V1 ]) + sdot(f_snext + dot(f_xnext,X_s),K3)
    D += sdot( f_xnext, L3 )

    X_sss = solve_sylvester(A,B,C,D)

    # now doing sigma correction with sigma replaced by l in the subscripts

    if not sigma is None:
        g_se= g2[:,:n_s,n_v:]
        g_xe= g2[:,n_s:n_v,n_v:]

        g_see= g3[:,:n_s,n_v:,n_v:]
        g_xee= g3[:,n_s:n_v,n_v:,n_v:]


        W_l = np.row_stack([
            g_e,
            dot(X_s,g_e)
        ])

        I_e = np.eye(n_e)

        V_sl = g_se + mdot( g_xe, [X_s, np.eye(n_e)])

        W_sl = np.row_stack([
            V_sl,
            mdot( X_ss, [ V1_3, g_e ] ) + sdot( X_s, V_sl)
        ])

        K_ee = mdot(f3[:,:,n_v:,n_v:], [V1, W_l, W_l ])
        K_ee += 2 * mdot( f2[:,n_v:,n_v:], [W_sl, W_l])

        # stochastic part of W_ll

        SW_ll = np.row_stack([
            g_ee,
            mdot(X_ss, [g_e, g_e]) + sdot(X_s, g_ee)
        ])

        DW_ll = np.concatenate([
            X_tt,
            dot(g_x, X_tt),
            dot(X_s, sdot(g_x,X_tt )) + X_tt
        ])

        K_ee += mdot( f2[:,:,n_v:], [V1, SW_ll])

        K_ = np.tensordot(K_ee, sigma, axes=((2,3),(0,1)))

        K_ += mdot(f2[:,:,n_s:], [V1, DW_ll])

        def E(vec):
            n = len(vec.shape)
            return np.tensordot(vec,sigma,axes=((n-2,n-1),(0,1)))

        L = sdot(g_sx,X_tt) + mdot(g_xx,[X_s,X_tt])

        L += E(g_see + mdot(g_xee,[X_s,I_e,I_e]) )

        M = E( mdot(X_sss,[V1_3, g_e, g_e]) + 2*mdot(X_ss, [V_sl,g_e]) )
        M += mdot( X_ss, [V1_3, E( g_ee ) + sdot(g_x, X_tt)] )


        A = f_x + dot( f_snext + dot(f_xnext,X_s), g_x) # same as before
        B = f_xnext # same
        C = V1_3 # same
        D = K_ + dot( f_snext + dot(f_xnext,X_s), L) + dot( f_xnext, M )

        if sigma2_correction is not None:
            g_sl = sigma2_correction[1][:,:n_s]
            g_xl = sigma2_correction[1][:,n_s:(n_s+n_x)]
            D += dot( f_snext + dot(f_xnext,X_s), g_sl + dot(g_xl,X_s) )   # constant

        X_stt = solve_sylvester(A,B,C,D)

    if approx_order == 3:
        if sigma is None:
            return [X_s,X_ss,X_sss]
        else:
            return [[X_s,X_ss,X_sss],[X_tt, X_stt]]
示例#10
0
def state_perturb(f_fun, g_fun, sigma, sigma2_correction=None, verbose=True):
    """Computes a Taylor approximation of decision rules, given the supplied derivatives.

    The original system is assumed to be in the the form:

    .. math::

        E_t f(s_t,x_t,s_{t+1},x_{t+1})

        s_t = g(s_{t-1},x_{t-1}, \\lambda \\epsilon_t)

    where :math:`\\lambda` is a scalar scaling down the risk.  the solution is a function :math:`\\varphi` such that:

    .. math::

        x_t = \\varphi ( s_t, \\sigma )

    The user supplies, a list of derivatives of f and g.

    :param f_fun: list of derivatives of f [order0, order1, order2, ...]
    :param g_fun: list of derivatives of g [order0, order1, order2, ...]
    :param sigma: covariance matrix of :math:`\\epsilon_t`
    :param sigma2_correction: (optional) first and second derivatives of g w.r.t. sigma if :math:`g` explicitely depends
        :math:`sigma`


    Assuming :math:`s_t` ,  :math:`x_t` and :math:`\\epsilon_t` are vectors of size
    :math:`n_s`, :math:`n_x`  and :math:`n_x`  respectively.
    In general the derivative of order :math:`i` of :math:`f`  is a multimensional array of size :math:`n_x \\times (N, ..., N)`
    with :math:`N=2(n_s+n_x)` repeated :math:`i` times (possibly 0).
    Similarly the derivative of order :math:`i` of :math:`g`  is a multidimensional array of size :math:`n_s \\times (M, ..., M)`
    with :math:`M=n_s+n_x+n_2` repeated :math:`i` times (possibly 0).

    

    """

    import numpy as np
    from numpy.linalg import solve

    approx_order = len(f_fun) - 1  # order of approximation

    [f0, f1] = f_fun[:2]

    [g0, g1] = g_fun[:2]
    n_x = f1.shape[0]  # number of controls
    n_s = f1.shape[1] / 2 - n_x  # number of states
    n_e = g1.shape[1] - n_x - n_s
    n_v = n_s + n_x

    f_s = f1[:, :n_s]
    f_x = f1[:, n_s:n_s + n_x]
    f_snext = f1[:, n_v:n_v + n_s]
    f_xnext = f1[:, n_v + n_s:]

    g_s = g1[:, :n_s]
    g_x = g1[:, n_s:n_s + n_x]
    g_e = g1[:, n_v:]

    A = np.row_stack([
        np.column_stack([np.eye(n_s), np.zeros((n_s, n_x))]),
        np.column_stack([-f_snext, -f_xnext])
    ])
    B = np.row_stack(
        [np.column_stack([g_s, g_x]),
         np.column_stack([f_s, f_x])])

    from dolo.numeric.extern.qz import qzordered
    [S, T, Q, Z, eigval] = qzordered(A, B, n_s)

    # Check Blanchard=Kahn conditions
    n_big_one = sum(eigval > 1.0)
    n_expected = n_x
    if verbose:
        print("There are {} eigenvalues greater than 1. Expected: {}.".format(
            n_big_one, n_x))

    if n_big_one != n_expected:
        raise Exception(
            "There should be exactly {} eigenvalues greater than one. Not {}.".
            format(n_x, n_big_one))

    Q = Q.real  # is it really necessary ?
    Z = Z.real

    Z11 = Z[:n_s, :n_s]
    Z12 = Z[:n_s, n_s:]
    Z21 = Z[n_s:, :n_s]
    Z22 = Z[n_s:, n_s:]
    S11 = S[:n_s, :n_s]
    T11 = T[:n_s, :n_s]

    # first order solution
    C = solve(Z11.T, Z21.T).T
    P = np.dot(solve(S11.T, Z11.T).T, solve(Z11.T, T11.T).T)
    Q = g_e

    if False:
        from numpy import dot
        test = f_s + dot(f_x, C) + dot(f_snext, g_s + dot(g_x, C)) + dot(
            f_xnext, dot(C, g_s + dot(g_x, C)))
        print('Error: ' + str(abs(test).max()))

    if approx_order == 1:
        return [C]

    # second order solution
    from dolo.numeric.tensor import sdot, mdot
    from numpy import dot
    from dolo.numeric.matrix_equations import solve_sylvester

    f2 = f_fun[2]
    g2 = g_fun[2]
    g_ss = g2[:, :n_s, :n_s]
    g_sx = g2[:, :n_s, n_s:n_v]
    g_xx = g2[:, n_s:n_v, n_s:n_v]

    X_s = C

    V1_3 = g_s + dot(g_x, X_s)
    V1 = np.row_stack([np.eye(n_s), X_s, V1_3, dot(X_s, V1_3)])

    K2 = g_ss + 2 * sdot(g_sx, X_s) + mdot(g_xx, [X_s, X_s])
    #L2 =
    A = f_x + dot(f_snext + dot(f_xnext, X_s), g_x)
    B = f_xnext
    C = V1_3
    D = mdot(f2, [V1, V1]) + sdot(f_snext + dot(f_xnext, X_s), K2)

    X_ss = solve_sylvester(A, B, C, D)

    #    test = sdot( A, X_ss ) + sdot( B,  mdot(X_ss,[V1_3,V1_3]) ) + D

    if not sigma == None:
        g_ee = g2[:, n_v:, n_v:]

        v = np.row_stack([g_e, dot(X_s, g_e)])

        K_tt = mdot(f2[:, n_v:, n_v:], [v, v])
        K_tt += sdot(f_snext + dot(f_xnext, X_s), g_ee)
        K_tt += mdot(sdot(f_xnext, X_ss), [g_e, g_e])
        K_tt = np.tensordot(K_tt, sigma, axes=((1, 2), (0, 1)))

        if sigma2_correction is not None:
            K_tt += sdot(f_snext + dot(f_xnext, X_s), sigma2_correction[0])

        L_tt = f_x + dot(f_snext, g_x) + dot(f_xnext,
                                             dot(X_s, g_x) + np.eye(n_x))
        X_tt = solve(L_tt, -K_tt)

    if approx_order == 2:
        if sigma == None:
            return [
                X_s, X_ss
            ]  # here, we don't approximate the law of motion of the states
        else:
            return [[X_s, X_ss], [
                X_tt
            ]]  # here, we don't approximate the law of motion of the states

    # third order solution

    f3 = f_fun[3]
    g3 = g_fun[3]
    g_sss = g3[:, :n_s, :n_s, :n_s]
    g_ssx = g3[:, :n_s, :n_s, n_s:n_v]
    g_sxx = g3[:, :n_s, n_s:n_v, n_s:n_v]
    g_xxx = g3[:, n_s:n_v, n_s:n_v, n_s:n_v]

    V2_3 = K2 + sdot(g_x, X_ss)
    V2 = np.row_stack([
        np.zeros((n_s, n_s, n_s)), X_ss, V2_3,
        dot(X_s, V2_3) + mdot(X_ss, [V1_3, V1_3])
    ])

    K3 = g_sss + 3 * sdot(g_ssx, X_s) + 3 * mdot(g_sxx, [X_s, X_s]) + 2 * sdot(
        g_sx, X_ss)
    K3 += 3 * mdot(g_xx, [X_ss, X_s]) + mdot(g_xxx, [X_s, X_s, X_s])
    L3 = 3 * mdot(X_ss, [V1_3, V2_3])

    # A = f_x + dot( f_snext + dot(f_xnext,X_s), g_x) # same as before
    # B = f_xnext # same
    # C = V1_3 # same
    D = mdot(f3, [V1, V1, V1]) + 3 * mdot(f2, [V2, V1]) + sdot(
        f_snext + dot(f_xnext, X_s), K3)
    D += sdot(f_xnext, L3)

    X_sss = solve_sylvester(A, B, C, D)

    # now doing sigma correction with sigma replaced by l in the subscripts

    if not sigma is None:
        g_se = g2[:, :n_s, n_v:]
        g_xe = g2[:, n_s:n_v, n_v:]

        g_see = g3[:, :n_s, n_v:, n_v:]
        g_xee = g3[:, n_s:n_v, n_v:, n_v:]

        W_l = np.row_stack([g_e, dot(X_s, g_e)])

        I_e = np.eye(n_e)

        V_sl = g_se + mdot(g_xe, [X_s, np.eye(n_e)])

        W_sl = np.row_stack([V_sl, mdot(X_ss, [V1_3, g_e]) + sdot(X_s, V_sl)])

        K_ee = mdot(f3[:, :, n_v:, n_v:], [V1, W_l, W_l])
        K_ee += 2 * mdot(f2[:, n_v:, n_v:], [W_sl, W_l])

        # stochastic part of W_ll

        SW_ll = np.row_stack([g_ee, mdot(X_ss, [g_e, g_e]) + sdot(X_s, g_ee)])

        DW_ll = np.concatenate(
            [X_tt, dot(g_x, X_tt),
             dot(X_s, sdot(g_x, X_tt)) + X_tt])

        K_ee += mdot(f2[:, :, n_v:], [V1, SW_ll])

        K_ = np.tensordot(K_ee, sigma, axes=((2, 3), (0, 1)))

        K_ += mdot(f2[:, :, n_s:], [V1, DW_ll])

        def E(vec):
            n = len(vec.shape)
            return np.tensordot(vec, sigma, axes=((n - 2, n - 1), (0, 1)))

        L = sdot(g_sx, X_tt) + mdot(g_xx, [X_s, X_tt])

        L += E(g_see + mdot(g_xee, [X_s, I_e, I_e]))

        M = E(mdot(X_sss, [V1_3, g_e, g_e]) + 2 * mdot(X_ss, [V_sl, g_e]))
        M += mdot(X_ss, [V1_3, E(g_ee) + sdot(g_x, X_tt)])

        A = f_x + dot(f_snext + dot(f_xnext, X_s), g_x)  # same as before
        B = f_xnext  # same
        C = V1_3  # same
        D = K_ + dot(f_snext + dot(f_xnext, X_s), L) + dot(f_xnext, M)

        if sigma2_correction is not None:
            g_sl = sigma2_correction[1][:, :n_s]
            g_xl = sigma2_correction[1][:, n_s:(n_s + n_x)]
            D += dot(f_snext + dot(f_xnext, X_s),
                     g_sl + dot(g_xl, X_s))  # constant

        X_stt = solve_sylvester(A, B, C, D)

    if approx_order == 3:
        if sigma is None:
            return [X_s, X_ss, X_sss]
        else:
            return [[X_s, X_ss, X_sss], [X_tt, X_stt]]
示例#11
0
def approximate_controls(model, verbose=False, steady_state=None, eigmax=1.0-1e-6,
                         solve_steady_state=False, order=1):
    """Compute first order approximation of optimal controls

    Parameters:
    -----------

    model: NumericModel
        Model to be solved

    verbose: boolean
        If True: displays number of contracting eigenvalues

    steady_state: ndarray
        Use supplied steady-state value to compute the approximation.
        The routine doesn't check whether it is really a solution or not.

    solve_steady_state: boolean
        Use nonlinear solver to find the steady-state

    orders: {1}
        Approximation order. (Currently, only first order is supported).

    Returns:
    --------

    TaylorExpansion:
        Decision Rule for the optimal controls around the steady-state.

    """

    if order > 1:
        raise Exception("Not implemented.")

    f = model.functions['arbitrage']
    g = model.functions['transition']

    if steady_state is not None:
        calib = steady_state
    else:
        calib = model.calibration

    if solve_steady_state:
        calib = find_deterministic_equilibrium(model)

    p = calib['parameters']
    s = calib['states']
    x = calib['controls']
    e = calib['shocks']

    distrib = model.get_distribution()
    sigma = distrib.sigma

    l = g(s, x, e, p, diff=True)
    [junk, g_s, g_x, g_e] = l[:4]  # [el[0,...] for el in l[:4]]

    l = f(s, x, e, s, x, p, diff=True)
    [res, f_s, f_x, f_e, f_S, f_X] = l  # [el[0,...] for el in l[:6]]

    n_s = g_s.shape[0]           # number of controls
    n_x = g_x.shape[1]   # number of states
    n_e = g_e.shape[1]
    n_v = n_s + n_x

    A = row_stack([
        column_stack([eye(n_s), zeros((n_s, n_x))]),
        column_stack([-f_S    , -f_X             ])
    ])

    B = row_stack([
        column_stack([g_s, g_x]),
        column_stack([f_s, f_x])
    ])


    [S, T, Q, Z, eigval] = qzordered(A, B, 1.0-1e-8)

    Q = Q.real  # is it really necessary ?
    Z = Z.real

    diag_S = np.diag(S)
    diag_T = np.diag(T)

    tol_geneigvals = 1e-10

    try:
        ok = sum((abs(diag_S) < tol_geneigvals) *
                 (abs(diag_T) < tol_geneigvals)) == 0
        assert(ok)
    except Exception as e:
        raise GeneralizedEigenvaluesError(diag_S=diag_S, diag_T=diag_T)

    if max(eigval[:n_s]) >= 1 and min(eigval[n_s:]) < 1:
        # BK conditions are met
        pass
    else:
        eigval_s = sorted(eigval, reverse=True)
        ev_a = eigval_s[n_s-1]
        ev_b = eigval_s[n_s]
        cutoff = (ev_a - ev_b)/2
        if not ev_a > ev_b:
            raise GeneralizedEigenvaluesSelectionError(
                    A=A, B=B, eigval=eigval, cutoff=cutoff,
                    diag_S=diag_S, diag_T=diag_T, n_states=n_s
                )
        import warnings
        if cutoff > 1:
            warnings.warn("Solution is not convergent.")
        else:
            warnings.warn("There are multiple convergent solutions. The one with the smaller eigenvalues was selected.")
        [S, T, Q, Z, eigval] = qzordered(A, B, cutoff)

    Z11 = Z[:n_s, :n_s]
    # Z12 = Z[:n_s, n_s:]
    Z21 = Z[n_s:, :n_s]
    # Z22 = Z[n_s:, n_s:]
    # S11 = S[:n_s, :n_s]
    # T11 = T[:n_s, :n_s]

    # first order solution
    # P = (solve(S11.T, Z11.T).T @ solve(Z11.T, T11.T).T)
    C = solve(Z11.T, Z21.T).T
    Q = g_e

    s = s.ravel()
    x = x.ravel()

    A = g_s + g_x @ C
    B = g_e

    dr = CDR([s, x, C])
    dr.A = A
    dr.B = B
    dr.sigma = sigma

    return dr
示例#12
0
def approximate_controls(model,
                         verbose=False,
                         steady_state=None,
                         eigmax=1.0 - 1e-6,
                         solve_steady_state=False,
                         order=1):
    """Compute first order approximation of optimal controls

    Parameters:
    -----------

    model: NumericModel
        Model to be solved

    verbose: boolean
        If True: displays number of contracting eigenvalues

    steady_state: ndarray
        Use supplied steady-state value to compute the approximation.
        The routine doesn't check whether it is really a solution or not.

    solve_steady_state: boolean
        Use nonlinear solver to find the steady-state

    orders: {1}
        Approximation order. (Currently, only first order is supported).

    Returns:
    --------

    TaylorExpansion:
        Decision Rule for the optimal controls around the steady-state.

    """

    if order > 1:
        raise Exception("Not implemented.")

    f = model.functions['arbitrage']
    g = model.functions['transition']

    if steady_state is not None:
        calib = steady_state
    else:
        calib = model.calibration

    if solve_steady_state:
        calib = find_deterministic_equilibrium(model)

    p = calib['parameters']
    s = calib['states']
    x = calib['controls']
    e = calib['shocks']

    distrib = model.get_distribution()
    sigma = distrib.sigma

    l = g(s, x, e, p, diff=True)
    [junk, g_s, g_x, g_e] = l[:4]  # [el[0,...] for el in l[:4]]

    l = f(s, x, e, s, x, p, diff=True)
    [res, f_s, f_x, f_e, f_S, f_X] = l  # [el[0,...] for el in l[:6]]

    n_s = g_s.shape[0]  # number of controls
    n_x = g_x.shape[1]  # number of states
    n_e = g_e.shape[1]
    n_v = n_s + n_x

    A = row_stack([
        column_stack([eye(n_s), zeros((n_s, n_x))]),
        column_stack([-f_S, -f_X])
    ])

    B = row_stack([column_stack([g_s, g_x]), column_stack([f_s, f_x])])

    [S, T, Q, Z, eigval] = qzordered(A, B, 1.0 - 1e-8)

    Q = Q.real  # is it really necessary ?
    Z = Z.real

    diag_S = np.diag(S)
    diag_T = np.diag(T)

    tol_geneigvals = 1e-10

    try:
        ok = sum((abs(diag_S) < tol_geneigvals) *
                 (abs(diag_T) < tol_geneigvals)) == 0
        assert (ok)
    except Exception as e:
        raise GeneralizedEigenvaluesError(diag_S=diag_S, diag_T=diag_T)

    if max(eigval[:n_s]) >= 1 and min(eigval[n_s:]) < 1:
        # BK conditions are met
        pass
    else:
        eigval_s = sorted(eigval, reverse=True)
        ev_a = eigval_s[n_s - 1]
        ev_b = eigval_s[n_s]
        cutoff = (ev_a - ev_b) / 2
        if not ev_a > ev_b:
            raise GeneralizedEigenvaluesSelectionError(A=A,
                                                       B=B,
                                                       eigval=eigval,
                                                       cutoff=cutoff,
                                                       diag_S=diag_S,
                                                       diag_T=diag_T,
                                                       n_states=n_s)
        import warnings
        if cutoff > 1:
            warnings.warn("Solution is not convergent.")
        else:
            warnings.warn(
                "There are multiple convergent solutions. The one with the smaller eigenvalues was selected."
            )
        [S, T, Q, Z, eigval] = qzordered(A, B, cutoff)

    Z11 = Z[:n_s, :n_s]
    # Z12 = Z[:n_s, n_s:]
    Z21 = Z[n_s:, :n_s]
    # Z22 = Z[n_s:, n_s:]
    # S11 = S[:n_s, :n_s]
    # T11 = T[:n_s, :n_s]

    # first order solution
    # P = (solve(S11.T, Z11.T).T @ solve(Z11.T, T11.T).T)
    C = solve(Z11.T, Z21.T).T
    Q = g_e

    s = s.ravel()
    x = x.ravel()

    A = g_s + g_x @ C
    B = g_e

    dr = CDR([s, x, C])
    dr.A = A
    dr.B = B
    dr.sigma = sigma

    return dr
def state_perturb(problem: PerturbationProblem, verbose=True):
    """Computes a Taylor approximation of decision rules, given the supplied derivatives.

    The original system is assumed to be in the the form:

    .. math::

        E_t f(s_t,x_t,s_{t+1},x_{t+1})

        s_t = g(s_{t-1},x_{t-1}, \\lambda \\epsilon_t)

    where :math:`\\lambda` is a scalar scaling down the risk.  the solution is a function :math:`\\varphi` such that:

    .. math::

        x_t = \\varphi ( s_t, \\sigma )

    The user supplies, a list of derivatives of f and g.

    :param f_fun: list of derivatives of f [order0, order1, order2, ...]
    :param g_fun: list of derivatives of g [order0, order1, order2, ...]
    :param sigma: covariance matrix of :math:`\\epsilon_t`


    Assuming :math:`s_t` ,  :math:`x_t` and :math:`\\epsilon_t` are vectors of size
    :math:`n_s`, :math:`n_x`  and :math:`n_x`  respectively.
    In general the derivative of order :math:`i` of :math:`f`  is a multimensional array of size :math:`n_x \\times (N, ..., N)`
    with :math:`N=2(n_s+n_x)` repeated :math:`i` times (possibly 0).
    Similarly the derivative of order :math:`i` of :math:`g`  is a multidimensional array of size :math:`n_s \\times (M, ..., M)`
    with :math:`M=n_s+n_x+n_2` repeated :math:`i` times (possibly 0).
    """

    import numpy as np
    from numpy.linalg import solve

    approx_order = problem.order  # order of approximation

    [f0, f1] = problem.f[:2]
    [g0, g1] = problem.g[:2]
    sigma = problem.sigma

    n_x = f1.shape[0]           # number of controls
    n_s = f1.shape[1]//2 - n_x   # number of states
    n_e = g1.shape[1] - n_x - n_s
    n_v = n_s + n_x

    f_s = f1[:, :n_s]
    f_x = f1[:, n_s:n_s+n_x]
    f_snext = f1[:, n_v:n_v+n_s]
    f_xnext = f1[:, n_v+n_s:]

    g_s = g1[:, :n_s]
    g_x = g1[:, n_s:n_s+n_x]
    g_e = g1[:, n_v:]

    A = np.row_stack([
        np.column_stack([np.eye(n_s), np.zeros((n_s, n_x))]),
        np.column_stack([-f_snext    , -f_xnext           ])
    ])
    B = np.row_stack([
        np.column_stack([g_s, g_x]),
        np.column_stack([f_s, f_x])
    ])

    [S, T, Q, Z, eigval] = qzordered(A, B, 1.0-1e-8)

    Q = Q.real  # is it really necessary ?
    Z = Z.real

    diag_S = np.diag(S)
    diag_T = np.diag(T)

    tol_geneigvals = 1e-10

    try:
        ok = sum((abs(diag_S) < tol_geneigvals) *
                 (abs(diag_T) < tol_geneigvals)) == 0
        assert(ok)
    except Exception as e:
        raise GeneralizedEigenvaluesError(diag_S=diag_S, diag_T=diag_T)

    if max(eigval[:n_s]) >= 1 and min(eigval[n_s:]) < 1:
        # BK conditions are met
        pass
    else:
        eigval_s = sorted(eigval, reverse=True)
        ev_a = eigval_s[n_s-1]
        ev_b = eigval_s[n_s]
        cutoff = (ev_a - ev_b)/2
        if not ev_a > ev_b:
            raise GeneralizedEigenvaluesSelectionError(
                    A=A, B=B, eigval=eigval, cutoff=cutoff,
                    diag_S=diag_S, diag_T=diag_T, n_states=n_s
                )
        import warnings
        if cutoff > 1:
            warnings.warn("Solution is not convergent.")
        else:
            warnings.warn("There are multiple convergent solutions. The one with the smaller eigenvalues was selected.")
        [S, T, Q, Z, eigval] = qzordered(A, B, cutoff)

    Z11 = Z[:n_s, :n_s]
    # Z12 = Z[:n_s, n_s:]
    Z21 = Z[n_s:, :n_s]
    # Z22 = Z[n_s:, n_s:]
    S11 = S[:n_s, :n_s]
    T11 = T[:n_s, :n_s]

    # first order solution
    C = solve(Z11.T, Z21.T).T
    P = np.dot(solve(S11.T, Z11.T).T, solve(Z11.T, T11.T).T)
    Q = g_e

    # if False:
    #     from numpy import dot
    #     test = f_s + f_x @ C + f_snext @ (g_s + g_x @ C) + f_xnext @ C @ (g_s + g_x @ C)
    #     print('Error: ' + str(abs(test).max()))

    if approx_order == 1:
        return [C]

    # second order solution
    from dolo.numeric.tensor import sdot, mdot
    from numpy import dot
    from dolo.numeric.matrix_equations import solve_sylvester

    f2 = problem.f[2]
    g2 = problem.g[2]
    g_ss = g2[:, :n_s, :n_s]
    g_sx = g2[:, :n_s, n_s:n_v]
    g_xx = g2[:, n_s:n_v, n_s:n_v]

    X_s = C

    V1_3 = g_s + dot(g_x, X_s)
    V1 = np.row_stack([
        np.eye(n_s),
        X_s,
        V1_3,
        X_s @ V1_3
    ])

    K2 = g_ss + 2 * sdot(g_sx, X_s) + mdot(g_xx, X_s, X_s)
    A = f_x + dot(f_snext + dot(f_xnext, X_s), g_x)
    B = f_xnext
    C = V1_3
    D = mdot(f2, V1, V1) + sdot(f_snext + dot(f_xnext, X_s), K2)

    X_ss = solve_sylvester(A, B, C, D)

#    test = sdot( A, X_ss ) + sdot( B,  mdot(X_ss,V1_3,V1_3) ) + D

    g_ee = g2[:, n_v:, n_v:]

    v = np.row_stack([
        g_e,
        dot(X_s, g_e)
    ])

    K_tt = mdot(f2[:, n_v:, n_v:], v, v)
    K_tt += sdot(f_snext + dot(f_xnext, X_s), g_ee)
    K_tt += mdot(sdot(f_xnext, X_ss), g_e, g_e)
    K_tt = np.tensordot(K_tt, sigma, axes=((1, 2), (0, 1)))

    L_tt = f_x + dot(f_snext, g_x) + dot(f_xnext, dot(X_s, g_x) + np.eye(n_x))
    X_tt = solve(L_tt, - K_tt)

    if approx_order == 2:
        return [[X_s, X_ss], [X_tt]]

    # third order solution

    f3 = problem.f[3]
    g3 = problem.g[3]
    g_sss = g3[:, :n_s, :n_s, :n_s]
    g_ssx = g3[:, :n_s, :n_s, n_s:n_v]
    g_sxx = g3[:, :n_s, n_s:n_v, n_s:n_v]
    g_xxx = g3[:, n_s:n_v, n_s:n_v, n_s:n_v]

    V2_3 = K2 + sdot(g_x, X_ss)
    V2 = np.row_stack([
        np.zeros((n_s, n_s, n_s)),
        X_ss,
        V2_3,
        dot(X_s, V2_3) + mdot(X_ss, V1_3, V1_3)
    ])

    K3 = g_sss + 3*sdot(g_ssx, X_s) + 3*mdot(g_sxx, X_s, X_s) + 2*sdot(g_sx, X_ss)
    K3 += 3*mdot(g_xx, X_ss, X_s) + mdot(g_xxx, X_s, X_s, X_s)
    L3 = 3*mdot(X_ss, V1_3, V2_3)

    # A = f_x + dot( f_snext + dot(f_xnext,X_s), g_x) # same as before
    # B = f_xnext # same
    # C = V1_3 # same
    D = mdot(f3, V1, V1, V1) + 3*mdot(f2, V2, V1) + sdot(f_snext + dot(f_xnext, X_s), K3)
    D += sdot(f_xnext, L3)

    X_sss = solve_sylvester(A, B, C, D)

    # now doing sigma correction with sigma replaced by l in the subscripts

    g_se = g2[:, :n_s, n_v:]
    g_xe = g2[:, n_s:n_v, n_v:]

    g_see = g3[:, :n_s, n_v:, n_v:]
    g_xee = g3[:, n_s:n_v, n_v:, n_v:]

    W_l = np.row_stack([
        g_e,
        dot(X_s, g_e)
    ])

    I_e = np.eye(n_e)

    V_sl = g_se + mdot(g_xe, X_s, np.eye(n_e))

    W_sl = np.row_stack([
        V_sl,
        mdot(X_ss, V1_3, g_e) + sdot(X_s, V_sl)
    ])

    K_ee = mdot(f3[:, :, n_v:, n_v:], V1, W_l, W_l)
    K_ee += 2 * mdot(f2[:, n_v:, n_v:], W_sl, W_l)

    # stochastic part of W_ll

    SW_ll = np.row_stack([
        g_ee,
        mdot(X_ss, g_e, g_e) + sdot(X_s, g_ee)
    ])

    DW_ll = np.concatenate([
        X_tt,
        dot(g_x, X_tt),
        dot(X_s, sdot(g_x, X_tt)) + X_tt
    ])

    K_ee += mdot(f2[:, :, n_v:], V1, SW_ll)

    K_ = np.tensordot(K_ee, sigma, axes=((2,3), (0,1)))

    K_ += mdot(f2[:, :, n_s:], V1, DW_ll)

    def E(vec):
        n = len(vec.shape)
        return np.tensordot(vec, sigma, axes=((n-2, n-1), (0, 1)))

    L = sdot(g_sx, X_tt) + mdot(g_xx, X_s, X_tt)

    L += E(g_see + mdot(g_xee, X_s, I_e, I_e))

    M = E(mdot(X_sss, V1_3, g_e, g_e) + 2*mdot(X_ss, V_sl, g_e))
    M += mdot(X_ss, V1_3, E(g_ee) + sdot(g_x, X_tt))

    A = f_x + dot(f_snext + dot(f_xnext, X_s), g_x)  # same as before
    B = f_xnext  # same
    C = V1_3     # same
    D = K_ + dot(f_snext + dot(f_xnext, X_s), L) + dot(f_xnext, M)

    X_stt = solve_sylvester(A, B, C, D)

    if approx_order == 3:
        # if sigma is None:
        #     return [X_s,X_ss,X_sss]
        # else:
        #     return [[X_s,X_ss,X_sss],[X_tt, X_stt]]
        return [[X_s, X_ss, X_sss], [X_tt, X_stt]]
示例#14
0
def approximate_controls(model, return_dr=True):

    # get steady_state
    import numpy

    p = model.calibration['parameters']
    sigma = model.calibration['covariances']
    s = model.calibration['states'][:, None]
    x = model.calibration['controls'][:, None]
    e = model.calibration['shocks'][:, None]

    from numpy.linalg import solve

    g = model.functions['transition']
    f = model.functions['arbitrage']

    l = g(s, x, e, p, derivs=True)
    [junk, g_s, g_x, g_e] = [el[..., 0] for el in l]

    if model.model_type == "fg2":
        l = f(s, x, e, s, x, p, derivs=True)
        [res, f_s, f_x, f_e, f_S, f_X] = [el[..., 0] for el in l]
    else:
        l = f(s, x, s, x, p, derivs=True)
        [res, f_s, f_x, f_S, f_X] = [el[..., 0] for el in l]

    n_s = g_s.shape[0]  # number of controls
    n_x = g_x.shape[1]  # number of states
    n_e = g_e.shape[1]

    n_v = n_s + n_x

    A = row_stack([
        column_stack([eye(n_s), zeros((n_s, n_x))]),
        column_stack([-f_S, -f_X])
    ])

    B = row_stack([column_stack([g_s, g_x]), column_stack([f_s, f_x])])

    from dolo.numeric.extern.qz import qzordered
    [S, T, Q, Z, eigval] = qzordered(A, B, n_s)
    Q = Q.real  # is it really necessary ?
    Z = Z.real

    Z11 = Z[:n_s, :n_s]
    Z12 = Z[:n_s, n_s:]
    Z21 = Z[n_s:, :n_s]
    Z22 = Z[n_s:, n_s:]
    S11 = S[:n_s, :n_s]
    T11 = T[:n_s, :n_s]

    # first order solution
    C = solve(Z11.T, Z21.T).T
    P = np.dot(solve(S11.T, Z11.T).T, solve(Z11.T, T11.T).T)
    Q = g_e

    s = s.ravel()
    x = x.ravel()

    A = g_s + dot(g_x, C)
    B = g_e

    dr = CDR([s, x, C])
    dr.A = A
    dr.B = B
    dr.sigma = sigma

    return dr