def improved_time_iteration(
    model,
    method="jac",
    dr0=None,
    dprocess=None,
    interp_method="cubic",
    mu=2,
    maxbsteps=10,
    verbose=False,
    tol=1e-8,
    smaxit=500,
    maxit=1000,
    complementarities=True,
    compute_radius=False,
    invmethod="iti",
    details=True,
):
    def vprint(*args, **kwargs):
        if verbose:
            print(*args, **kwargs)

    itprint = IterationsPrinter(
        ("N", int),
        ("f_x", float),
        ("d_x", float),
        ("Time_residuals", float),
        ("Time_inversion", float),
        ("Time_search", float),
        ("Lambda_0", float),
        ("N_invert", int),
        ("N_search", int),
        verbose=verbose,
    )
    itprint.print_header("Start Improved Time Iterations.")

    f = model.functions["arbitrage"]
    g = model.functions["transition"]
    x_lb = model.functions["arbitrage_lb"]
    x_ub = model.functions["arbitrage_ub"]

    parms = model.calibration["parameters"]

    if dprocess is not None:
        from dolo.numeric.grids import ProductGrid

        endo_grid = model.domain.discretize()
        grid = ProductGrid(dprocess.grid, endo_grid, names=["exo", "endo"])
    else:
        grid, dprocess = model.discretize()

    endo_grid = grid["endo"]
    exo_grid = grid["exo"]

    n_m = max(dprocess.n_nodes, 1)
    n_s = len(model.symbols["states"])

    if interp_method in ("cubic", "linear"):
        ddr = DecisionRule(
            exo_grid, endo_grid, dprocess=dprocess, interp_method=interp_method
        )
        ddr_filt = DecisionRule(
            exo_grid, endo_grid, dprocess=dprocess, interp_method=interp_method
        )
    else:
        raise Exception("Unsupported interpolation method.")

    # s = ddr.endo_grid
    s = endo_grid.nodes
    N = s.shape[0]
    n_x = len(model.symbols["controls"])
    x0 = (
        model.calibration["controls"][
            None,
            None,
        ]
        .repeat(n_m, axis=0)
        .repeat(N, axis=1)
    )

    if dr0 is not None:
        for i_m in range(n_m):
            x0[i_m, :, :] = dr0.eval_is(i_m, s)
    ddr.set_values(x0)

    steps = 0.5 ** numpy.arange(maxbsteps)

    lb = x0.copy()
    ub = x0.copy()
    for i_m in range(n_m):
        m = dprocess.node(i_m)
        lb[i_m, :] = x_lb(m, s, parms)
        ub[i_m, :] = x_ub(m, s, parms)

    x = x0

    # both affect the precision

    ddr.set_values(x)

    ## memory allocation

    n_im = dprocess.n_inodes(0)  # we assume it is constant for now

    jres = numpy.zeros((n_m, n_im, N, n_x, n_x))
    S_ij = numpy.zeros((n_m, n_im, N, n_s))

    for it in range(maxit):

        jres[...] = 0.0
        S_ij[...] = 0.0

        t1 = time.time()

        # compute derivatives and residuals:
        # res: residuals
        # dres: derivatives w.r.t. x
        # jres: derivatives w.r.t. ~x
        # fut_S: future states

        ddr.set_values(x)
        #
        # ub[ub>100000] = 100000
        # lb[lb<-100000] = -100000
        #
        # sh_x = x.shape
        # rr =euler_residuals(f,g,s,x,ddr,dp,parms, diff=False, with_jres=False,set_dr=True)
        # print(rr.shape)
        #
        # from iti.fb import smooth_
        # jj = smooth_(rr, x, lb, ub)
        #
        # print("Errors with complementarities")
        # print(abs(jj.max()))
        #
        # exit()
        #

        from dolo.numeric.optimize.newton import SerialDifferentiableFunction

        sh_x = x.shape
        ff = SerialDifferentiableFunction(
            lambda u: euler_residuals(
                f,
                g,
                s,
                u.reshape(sh_x),
                ddr,
                dprocess,
                parms,
                diff=False,
                with_jres=False,
                set_dr=False,
            ).reshape((-1, sh_x[2]))
        )
        res, dres = ff(x.reshape((-1, sh_x[2])))
        res = res.reshape(sh_x)
        dres = dres.reshape((sh_x[0], sh_x[1], sh_x[2], sh_x[2]))
        junk, jres, fut_S = euler_residuals(
            f,
            g,
            s,
            x,
            ddr,
            dprocess,
            parms,
            diff=False,
            with_jres=True,
            set_dr=False,
            jres=jres,
            S_ij=S_ij,
        )

        # if there are complementerities, we modify derivatives
        if complementarities:
            res, dres, jres = smooth(res, dres, jres, x - lb)
            res[...] *= -1
            dres[...] *= -1
            jres[...] *= -1
            res, dres, jres = smooth(res, dres, jres, ub - x, pos=-1.0)
            res[...] *= -1
            dres[...] *= -1
            jres[...] *= -1

        err_0 = abs(res).max()

        # premultiply by A
        jres[...] *= -1.0

        for i_m in range(n_m):
            for j_m in range(n_im):
                M = jres[i_m, j_m, :, :, :]
                X = dres[i_m, :, :, :].copy()
                sol = solve_tensor(X, M)

        t2 = time.time()

        # new version
        if invmethod == "gmres":
            ddx = solve_gu(dres.copy(), res.copy())
            L = Operator(jres, fut_S, ddr_filt)
            n0 = L.counter
            L.addid = True
            ttol = err_0 / 100
            sol = scipy.sparse.linalg.gmres(
                L, ddx.ravel(), tol=ttol
            )  # , maxiter=1, restart=smaxit)
            lam0 = 0.01
            nn = L.counter - n0
            tot = sol[0].reshape(ddx.shape)
        else:
            # compute inversion
            tot, nn, lam0 = invert_jac(
                res,
                dres,
                jres,
                fut_S,
                ddr_filt,
                tol=tol,
                maxit=smaxit,
                verbose=(verbose == "full"),
            )

        # lam, lam_max, lambdas = radius_jac(res,dres,jres,fut_S,tol=tol,maxit=1000,verbose=(verbose=='full'),filt=ddr_filt)

        # backsteps
        t3 = time.time()
        for i_bckstps, lam in enumerate(steps):
            new_x = x - tot * lam
            new_err = euler_residuals(
                f, g, s, new_x, ddr, dprocess, parms, diff=False, set_dr=True
            )

            if complementarities:
                new_err = smooth_nodiff(new_err, new_x - lb)
                new_err = smooth_nodiff(-new_err, ub - new_x)

            new_err = abs(new_err).max()
            if new_err < err_0:
                break

        err_2 = abs(tot).max()
        t4 = time.time()
        itprint.print_iteration(
            N=it,
            f_x=err_0,
            d_x=err_2,
            Time_residuals=t2 - t1,
            Time_inversion=t3 - t2,
            Time_search=t4 - t3,
            Lambda_0=lam0,
            N_invert=nn,
            N_search=i_bckstps,
        )
        if err_0 < tol:
            break

        x = new_x

    ddr.set_values(x)

    itprint.print_finished()

    # if compute_radius:
    #     return ddx,L
    #     lam, lam_max, lambdas = radius_jac(res,dres,jres,fut_S,ddr_filt,tol=tol,maxit=smaxit,verbose=(verbose=='full'))
    #     return ddr, lam, lam_max, lambdas
    # else:
    if not details:
        return ddr
    else:
        ddx = solve_gu(dres.copy(), res.copy())
        L = Operator(jres, fut_S, ddr_filt)

        if compute_radius:
            lam = scipy.sparse.linalg.eigs(L, k=1, return_eigenvectors=False)
            lam = abs(lam[0])
        else:
            lam = np.nan
        # lam, lam_max, lambdas = radius_jac(res,dres,jres,fut_S,ddr_filt,tol=tol,maxit=smaxit,verbose=(verbose=='full'))
        return ImprovedTimeIterationResult(
            ddr, it, err_0, err_2, err_0 < tol, complementarities, lam, None, L
        )
Beispiel #2
0
def time_iteration(model,
                   dr0=None,
                   dprocess=None,
                   with_complementarities=True,
                   verbose=True,
                   grid={},
                   maxit=1000,
                   inner_maxit=10,
                   tol=1e-6,
                   hook=None,
                   details=False,
                   interp_method='cubic'):
    '''
    Finds a global solution for ``model`` using backward time-iteration.

    This algorithm iterates on the residuals of the arbitrage equations

    Parameters
    ----------
    model : Model
        model to be solved
    verbose : boolean
        if True, display iterations
    dr0 : decision rule
        initial guess for the decision rule
    dprocess : DiscretizedProcess (model.exogenous.discretize())
        discretized process to be used
    with_complementarities : boolean (True)
        if False, complementarity conditions are ignored
    grid: grid options
        overload the values set in `options:grid` section
    maxit: maximum number of iterations
    inner_maxit: maximum number of iteration for inner solver
    tol: tolerance criterium for successive approximations
    hook: Callable
        function to be called within each iteration, useful for debugging purposes


    Returns
    -------
    decision rule :
        approximated solution
    '''

    from dolo import dprint

    def vprint(t):
        if verbose:
            print(t)

    if dprocess is None:
        dprocess = model.exogenous.discretize()

    n_ms = dprocess.n_nodes  # number of exogenous states
    n_mv = dprocess.n_inodes(
        0)  # this assume number of integration nodes is constant

    x0 = model.calibration['controls']
    parms = model.calibration['parameters']
    n_x = len(x0)
    n_s = len(model.symbols['states'])

    endo_grid = model.get_endo_grid(**grid)

    exo_grid = dprocess.grid

    mdr = DecisionRule(exo_grid,
                       endo_grid,
                       dprocess=dprocess,
                       interp_method=interp_method)

    grid = mdr.endo_grid.nodes
    N = grid.shape[0]

    controls_0 = numpy.zeros((n_ms, N, n_x))
    if dr0 is None:
        controls_0[:, :, :] = x0[None, None, :]
    else:
        if isinstance(dr0, AlgoResult):
            dr0 = dr0.dr
        try:
            for i_m in range(n_ms):
                controls_0[i_m, :, :] = dr0(i_m, grid)
        except Exception:
            for i_m in range(n_ms):
                m = dprocess.node(i_m)
                controls_0[i_m, :, :] = dr0(m, grid)

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

    if 'controls_lb' in model.functions and with_complementarities == True:
        lb_fun = model.functions['controls_lb']
        ub_fun = model.functions['controls_ub']
        lb = numpy.zeros_like(controls_0) * numpy.nan
        ub = numpy.zeros_like(controls_0) * numpy.nan
        for i_m in range(n_ms):
            m = dprocess.node(i_m)[None, :]
            p = parms[None, :]
            m = numpy.repeat(m, N, axis=0)
            p = numpy.repeat(p, N, axis=0)

            lb[i_m, :, :] = lb_fun(m, grid, p)
            ub[i_m, :, :] = ub_fun(m, grid, p)

    else:
        with_complementarities = False

    sh_c = controls_0.shape

    controls_0 = controls_0.reshape((-1, n_x))

    from dolo.numeric.optimize.newton import newton, SerialDifferentiableFunction
    from dolo.numeric.optimize.ncpsolve import ncpsolve

    err = 10
    it = 0

    if with_complementarities:
        lb = lb.reshape((-1, n_x))
        ub = ub.reshape((-1, n_x))

    if verbose:
        headline = '|{0:^4} | {1:10} | {2:8} | {3:8} | {4:3} |'.format(
            'N', ' Error', 'Gain', 'Time', 'nit')
        stars = '-' * len(headline)
        print(stars)
        print(headline)
        print(stars)

    import time
    t1 = time.time()

    err_0 = numpy.nan

    verbit = (verbose == 'full')

    while err > tol and it < maxit:

        it += 1

        t_start = time.time()

        mdr.set_values(controls_0.reshape(sh_c))

        fn = lambda x: residuals_simple(f, g, grid, x.reshape(sh_c), mdr,
                                        dprocess, parms).reshape((-1, n_x))
        dfn = SerialDifferentiableFunction(fn)

        res = fn(controls_0)

        if hook:
            hook()

        if with_complementarities:
            [controls, nit] = ncpsolve(dfn,
                                       lb,
                                       ub,
                                       controls_0,
                                       verbose=verbit,
                                       maxit=inner_maxit)
        else:
            [controls, nit] = newton(dfn,
                                     controls_0,
                                     verbose=verbit,
                                     maxit=inner_maxit)

        err = abs(controls - controls_0).max()

        err_SA = err / err_0
        err_0 = err

        controls_0 = controls

        t_finish = time.time()
        elapsed = t_finish - t_start

        if verbose:
            print('|{0:4} | {1:10.3e} | {2:8.3f} | {3:8.3f} | {4:3} |'.format(
                it, err, err_SA, elapsed, nit))

    controls_0 = controls.reshape(sh_c)

    mdr.set_values(controls_0)

    t2 = time.time()

    if verbose:
        print(stars)
        print("Elapsed: {} seconds.".format(t2 - t1))
        print(stars)

    if not details:
        return mdr

    return TimeIterationResult(
        mdr,
        it,
        with_complementarities,
        dprocess,
        err < tol,  # x_converged: bool
        tol,  # x_tol
        err,  #: float
        None,  # log: object # TimeIterationLog
        None  # trace: object #{Nothing,IterationTrace}
    )
Beispiel #3
0
def value_iteration(model,
                    grid={},
                    tol=1e-6,
                    maxit=500,
                    maxit_howard=20,
                    verbose=False):
    """
    Solve for the value function and associated Markov decision rule by iterating over
    the value function.

    Parameters:
    -----------
    model :
        "dtmscc" model. Must contain a 'felicity' function.
    grid :
        grid options
    dr :
        decision rule to evaluate

    Returns:
    --------
    mdr : Markov decision rule
        The solved decision rule/policy function
    mdrv: decision rule
        The solved value function
    """

    transition = model.functions['transition']
    felicity = model.functions['felicity']
    controls_lb = model.functions['controls_lb']
    controls_ub = model.functions['controls_ub']

    parms = model.calibration['parameters']
    discount = model.calibration['beta']

    x0 = model.calibration['controls']
    m0 = model.calibration['exogenous']
    s0 = model.calibration['states']
    r0 = felicity(m0, s0, x0, parms)

    process = model.exogenous
    dprocess = process.discretize()

    n_ms = dprocess.n_nodes()  # number of exogenous states
    n_mv = dprocess.n_inodes(
        0)  # this assume number of integration nodes is constant

    endo_grid = model.get_grid(**grid)

    exo_grid = dprocess.grid

    mdrv = DecisionRule(exo_grid, endo_grid)

    grid = mdrv.endo_grid.nodes()
    N = grid.shape[0]
    n_x = len(x0)

    mdr = constant_policy(model)
    controls_0 = np.zeros((n_ms, N, n_x))
    for i_ms in range(n_ms):
        controls_0[i_ms, :, :] = mdr.eval_is(i_ms, grid)

    values_0 = np.zeros((n_ms, N, 1))
    # for i_ms in range(n_ms):
    #     values_0[i_ms, :, :] = mdrv(i_ms, grid)

    mdr = DecisionRule(exo_grid, endo_grid)
    # mdr.set_values(controls_0)

    # THIRD: value function iterations until convergence
    it = 0
    err_v = 100
    err_v_0 = 0
    gain_v = 0.0
    err_x = 100
    err_x_0 = 0
    tol_x = 1e-5
    tol_v = 1e-7

    itprint = IterationsPrinter(
        ('N', int), ('Error_V', float), ('Gain_V', float), ('Error_x', float),
        ('Gain_x', float), ('Eval_n', int), ('Time', float),
        verbose=verbose)
    itprint.print_header('Start value function iterations.')

    while (it < maxit) and (err_v > tol or err_x > tol_x):

        t_start = time.time()
        it += 1

        mdr.set_values(controls_0)
        if it > 2:
            ev = evaluate_policy(model,
                                 mdr,
                                 initial_guess=mdrv,
                                 verbose=False,
                                 details=True)
        else:
            ev = evaluate_policy(model, mdr, verbose=False, details=True)

        mdrv = ev.solution
        for i_ms in range(n_ms):
            values_0[i_ms, :, :] = mdrv.eval_is(i_ms, grid)

        values = values_0.copy()
        controls = controls_0.copy()

        for i_m in range(n_ms):
            m = dprocess.node(i_m)
            for n in range(N):
                s = grid[n, :]
                x = controls[i_m, n, :]
                lb = controls_lb(m, s, parms)
                ub = controls_ub(m, s, parms)
                bnds = [e for e in zip(lb, ub)]

                def valfun(xx):
                    return -choice_value(transition, felicity, i_m, s, xx,
                                         mdrv, dprocess, parms, discount)[0]

                res = scipy.optimize.minimize(valfun, x, bounds=bnds)
                controls[i_m, n, :] = res.x
                values[i_m, n, 0] = -valfun(x)

        # compute error, update value and dr
        err_x = abs(controls - controls_0).max()
        err_v = abs(values - values_0).max()
        t_end = time.time()
        elapsed = t_end - t_start

        values_0 = values
        controls_0 = controls

        gain_x = err_x / err_x_0
        gain_v = err_v / err_v_0

        err_x_0 = err_x
        err_v_0 = err_v

        itprint.print_iteration(N=it,
                                Error_V=err_v,
                                Gain_V=gain_v,
                                Error_x=err_x,
                                Gain_x=gain_x,
                                Eval_n=ev.iterations,
                                Time=elapsed)

    itprint.print_finished()

    mdr = DecisionRule(exo_grid, endo_grid)
    mdr.set_values(controls)
    mdrv.set_values(values_0)

    return mdr, mdrv
Beispiel #4
0
def evaluate_policy(model,
                    mdr,
                    tol=1e-8,
                    maxit=2000,
                    grid={},
                    verbose=True,
                    initial_guess=None,
                    hook=None,
                    integration_orders=None,
                    details=False,
                    interp_type='cubic'):
    """Compute value function corresponding to policy ``dr``

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

    model:
        "dtcscc" model. Must contain a 'value' function.

    mdr:
        decision rule to evaluate

    Returns:
    --------

    decision rule:
        value function (a function of the space similar to a decision rule
        object)

    """

    process = model.exogenous
    dprocess = process.discretize()

    n_ms = dprocess.n_nodes()  # number of exogenous states
    n_mv = dprocess.n_inodes(
        0)  # this assume number of integration nodes is constant

    x0 = model.calibration['controls']
    v0 = model.calibration['values']
    parms = model.calibration['parameters']
    n_x = len(x0)
    n_v = len(v0)
    n_s = len(model.symbols['states'])

    endo_grid = model.get_grid(**grid)
    exo_grid = dprocess.grid

    if initial_guess is not None:
        mdrv = initial_guess
    else:
        mdrv = DecisionRule(exo_grid, endo_grid, interp_type=interp_type)

    grid = mdrv.endo_grid.nodes()
    N = grid.shape[0]

    if isinstance(mdr, np.ndarray):
        controls = mdr
    else:
        controls = np.zeros((n_ms, N, n_x))
        for i_m in range(n_ms):
            controls[i_m, :, :] = mdr.eval_is(i_m, grid)

    values_0 = np.zeros((n_ms, N, n_v))
    if initial_guess is None:
        for i_m in range(n_ms):
            values_0[i_m, :, :] = v0[None, :]
    else:
        for i_m in range(n_ms):
            values_0[i_m, :, :] = initial_guess.eval_is(i_m, grid)

    val = model.functions['value']
    g = model.functions['transition']

    sh_v = values_0.shape

    err = 10
    inner_maxit = 50
    it = 0

    if verbose:
        headline = '|{0:^4} | {1:10} | {2:8} | {3:8} |'.format(
            'N', ' Error', 'Gain', 'Time')
        stars = '-' * len(headline)
        print(stars)
        print(headline)
        print(stars)

    t1 = time.time()

    err_0 = np.nan

    verbit = (verbose == 'full')

    while err > tol and it < maxit:

        it += 1

        t_start = time.time()

        mdrv.set_values(values_0.reshape(sh_v))
        values = update_value(val, g, grid, controls, values_0, mdr, mdrv,
                              dprocess, parms).reshape((-1, n_v))
        err = abs(values.reshape(sh_v) - values_0).max()

        err_SA = err / err_0
        err_0 = err

        values_0 = values.reshape(sh_v)

        t_finish = time.time()
        elapsed = t_finish - t_start

        if verbose:
            print('|{0:4} | {1:10.3e} | {2:8.3f} | {3:8.3f} |'.format(
                it, err, err_SA, elapsed))

    # values_0 = values.reshape(sh_v)

    t2 = time.time()

    if verbose:
        print(stars)
        print("Elapsed: {} seconds.".format(t2 - t1))
        print(stars)

    if not details:
        return mdrv
    else:
        return EvaluationResult(mdrv, it, tol, err)
Beispiel #5
0
def evaluate_policy(
    model,
    mdr,
    tol=1e-8,
    maxit=2000,
    grid={},
    verbose=True,
    dr0=None,
    hook=None,
    integration_orders=None,
    details=False,
    interp_method="cubic",
):
    """Compute value function corresponding to policy ``dr``

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

    model:
        "dtcscc" model. Must contain a 'value' function.

    mdr:
        decision rule to evaluate

    Returns:
    --------

    decision rule:
        value function (a function of the space similar to a decision rule
        object)

    """

    process = model.exogenous
    grid, dprocess = model.discretize()
    endo_grid = grid["endo"]
    exo_grid = grid["exo"]

    n_ms = dprocess.n_nodes  # number of exogenous states
    n_mv = dprocess.n_inodes(
        0)  # this assume number of integration nodes is constant

    x0 = model.calibration["controls"]
    v0 = model.calibration["values"]
    parms = model.calibration["parameters"]
    n_x = len(x0)
    n_v = len(v0)
    n_s = len(model.symbols["states"])

    if dr0 is not None:
        mdrv = dr0
    else:
        mdrv = DecisionRule(exo_grid, endo_grid, interp_method=interp_method)

    s = mdrv.endo_grid.nodes
    N = s.shape[0]

    if isinstance(mdr, np.ndarray):
        controls = mdr
    else:
        controls = np.zeros((n_ms, N, n_x))
        for i_m in range(n_ms):
            controls[i_m, :, :] = mdr.eval_is(i_m, s)

    values_0 = np.zeros((n_ms, N, n_v))
    if dr0 is None:
        for i_m in range(n_ms):
            values_0[i_m, :, :] = v0[None, :]
    else:
        for i_m in range(n_ms):
            values_0[i_m, :, :] = dr0.eval_is(i_m, s)

    val = model.functions["value"]
    g = model.functions["transition"]

    sh_v = values_0.shape

    err = 10
    inner_maxit = 50
    it = 0

    if verbose:
        headline = "|{0:^4} | {1:10} | {2:8} | {3:8} |".format(
            "N", " Error", "Gain", "Time")
        stars = "-" * len(headline)
        print(stars)
        print(headline)
        print(stars)

    t1 = time.time()

    err_0 = np.nan

    verbit = verbose == "full"

    while err > tol and it < maxit:

        it += 1

        t_start = time.time()

        mdrv.set_values(values_0.reshape(sh_v))
        values = update_value(val, g, s, controls, values_0, mdr, mdrv,
                              dprocess, parms).reshape((-1, n_v))
        err = abs(values.reshape(sh_v) - values_0).max()

        err_SA = err / err_0
        err_0 = err

        values_0 = values.reshape(sh_v)

        t_finish = time.time()
        elapsed = t_finish - t_start

        if verbose:
            print("|{0:4} | {1:10.3e} | {2:8.3f} | {3:8.3f} |".format(
                it, err, err_SA, elapsed))

    # values_0 = values.reshape(sh_v)

    t2 = time.time()

    if verbose:
        print(stars)
        print("Elapsed: {} seconds.".format(t2 - t1))
        print(stars)

    if not details:
        return mdrv
    else:
        return EvaluationResult(mdrv, it, tol, err)
Beispiel #6
0
def time_iteration(model,
                   initial_guess=None,
                   with_complementarities=True,
                   verbose=True,
                   grid={},
                   maxit=1000,
                   inner_maxit=10,
                   tol=1e-6,
                   hook=None):
    '''
    Finds a global solution for ``model`` using backward time-iteration.

    This algorithm iterates on the residuals of the arbitrage equations

    Parameters
    ----------
    model : NumericModel
        "dtmscc" model to be solved
    verbose : boolean
        if True, display iterations
    initial_dr : decision rule
        initial guess for the decision rule
    with_complementarities : boolean (True)
        if False, complementarity conditions are ignored
    grid: grid options
    maxit: maximum number of iterations
    inner_maxit: maximum number of iteration for inner solver
    tol: tolerance criterium for successive approximations

    Returns
    -------
    decision rule :
        approximated solution
    '''

    from dolo import dprint

    def vprint(t):
        if verbose:
            print(t)

    process = model.exogenous
    dprocess = process.discretize()

    n_ms = dprocess.n_nodes()  # number of exogenous states
    n_mv = dprocess.n_inodes(
        0)  # this assume number of integration nodes is constant

    x0 = model.calibration['controls']
    parms = model.calibration['parameters']
    n_x = len(x0)
    n_s = len(model.symbols['states'])

    endo_grid = model.get_grid(**grid)

    interp_type = 'cubic'

    exo_grid = dprocess.grid

    mdr = DecisionRule(exo_grid, endo_grid)

    grid = mdr.endo_grid.nodes()
    N = grid.shape[0]

    controls_0 = numpy.zeros((n_ms, N, n_x))
    if initial_guess is None:
        controls_0[:, :, :] = x0[None, None, :]
    else:
        for i_m in range(n_ms):
            controls_0[i_m, :, :] = initial_guess(i_m, grid)

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

    if 'controls_lb' in model.functions and with_complementarities == True:
        lb_fun = model.functions['controls_lb']
        ub_fun = model.functions['controls_ub']
        lb = numpy.zeros_like(controls_0) * numpy.nan
        ub = numpy.zeros_like(controls_0) * numpy.nan
        for i_m in range(n_ms):
            m = dprocess.node(i_m)[None, :]
            p = parms[None, :]
            m = numpy.repeat(m, N, axis=0)
            p = numpy.repeat(p, N, axis=0)

            lb[i_m, :, :] = lb_fun(m, grid, p)
            ub[i_m, :, :] = ub_fun(m, grid, p)

    else:
        with_complementarities = False

    sh_c = controls_0.shape

    controls_0 = controls_0.reshape((-1, n_x))

    from dolo.numeric.optimize.newton import newton, SerialDifferentiableFunction
    from dolo.numeric.optimize.ncpsolve import ncpsolve

    err = 10
    it = 0

    if with_complementarities:
        vprint("Solving WITH complementarities.")
        lb = lb.reshape((-1, n_x))
        ub = ub.reshape((-1, n_x))

    if verbose:
        headline = '|{0:^4} | {1:10} | {2:8} | {3:8} | {4:3} |'.format(
            'N', ' Error', 'Gain', 'Time', 'nit')
        stars = '-' * len(headline)
        print(stars)
        print(headline)
        print(stars)

    import time
    t1 = time.time()

    err_0 = numpy.nan

    verbit = (verbose == 'full')

    while err > tol and it < maxit:

        it += 1

        t_start = time.time()

        mdr.set_values(controls_0.reshape(sh_c))

        fn = lambda x: residuals_simple(f, g, grid, x.reshape(sh_c), mdr,
                                        dprocess, parms).reshape((-1, n_x))
        dfn = SerialDifferentiableFunction(fn)

        res = fn(controls_0)

        if hook:
            hook()

        if with_complementarities:
            [controls, nit] = ncpsolve(dfn,
                                       lb,
                                       ub,
                                       controls_0,
                                       verbose=verbit,
                                       maxit=inner_maxit)
        else:
            [controls, nit] = newton(dfn,
                                     controls_0,
                                     verbose=verbit,
                                     maxit=inner_maxit)

        err = abs(controls - controls_0).max()

        err_SA = err / err_0
        err_0 = err

        controls_0 = controls

        t_finish = time.time()
        elapsed = t_finish - t_start

        if verbose:
            print('|{0:4} | {1:10.3e} | {2:8.3f} | {3:8.3f} | {4:3} |'.format(
                it, err, err_SA, elapsed, nit))

    controls_0 = controls.reshape(sh_c)

    t2 = time.time()

    if verbose:
        print(stars)
        print("Elapsed: {} seconds.".format(t2 - t1))
        print(stars)

    return mdr
Beispiel #7
0
def evaluate_policy(model,
                    mdr,
                    tol=1e-8,
                    maxit=2000,
                    grid={},
                    verbose=True,
                    initial_guess=None,
                    hook=None,
                    integration_orders=None,
                    details=False,
                    interp_type='cubic'):
    """Compute value function corresponding to policy ``dr``

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

    model:
        "dtcscc" model. Must contain a 'value' function.

    mdr:
        decision rule to evaluate

    Returns:
    --------

    decision rule:
        value function (a function of the space similar to a decision rule
        object)

    """

    process = model.exogenous
    dprocess = process.discretize()

    n_ms = dprocess.n_nodes()  # number of exogenous states
    n_mv = dprocess.n_inodes(
        0)  # this assume number of integration nodes is constant

    x0 = model.calibration['controls']
    v0 = model.calibration['values']
    parms = model.calibration['parameters']
    n_x = len(x0)
    n_v = len(v0)
    n_s = len(model.symbols['states'])

    endo_grid = model.get_grid(**grid)
    exo_grid = dprocess.grid

    if initial_guess is not None:
        mdrv = initial_guess
    else:
        mdrv = DecisionRule(exo_grid, endo_grid, interp_type=interp_type)

    grid = mdrv.endo_grid.nodes()
    N = grid.shape[0]

    if isinstance(mdr, np.ndarray):
        controls = mdr
    else:
        controls = np.zeros((n_ms, N, n_x))
        for i_m in range(n_ms):
            controls[i_m, :, :] = mdr.eval_is(i_m, grid)

    values_0 = np.zeros((n_ms, N, n_v))
    if initial_guess is None:
        for i_m in range(n_ms):
            values_0[i_m, :, :] = v0[None, :]
    else:
        for i_m in range(n_ms):
            values_0[i_m, :, :] = initial_guess.eval_is(i_m, grid)

    val = model.functions['value']
    g = model.functions['transition']

    sh_v = values_0.shape

    err = 10
    inner_maxit = 50
    it = 0

    if verbose:
        headline = '|{0:^4} | {1:10} | {2:8} | {3:8} |'.format(
            'N', ' Error', 'Gain', 'Time')
        stars = '-' * len(headline)
        print(stars)
        print(headline)
        print(stars)

    t1 = time.time()

    err_0 = np.nan

    verbit = (verbose == 'full')

    while err > tol and it < maxit:

        it += 1

        t_start = time.time()

        mdrv.set_values(values_0.reshape(sh_v))
        values = update_value(val, g, grid, controls, values_0, mdr, mdrv,
                              dprocess, parms).reshape((-1, n_v))
        err = abs(values.reshape(sh_v) - values_0).max()

        err_SA = err / err_0
        err_0 = err

        values_0 = values.reshape(sh_v)

        t_finish = time.time()
        elapsed = t_finish - t_start

        if verbose:
            print('|{0:4} | {1:10.3e} | {2:8.3f} | {3:8.3f} |'.format(
                it, err, err_SA, elapsed))

    # values_0 = values.reshape(sh_v)

    t2 = time.time()

    if verbose:
        print(stars)
        print("Elapsed: {} seconds.".format(t2 - t1))
        print(stars)

    if not details:
        return mdrv
    else:
        return EvaluationResult(mdrv, it, tol, err)
Beispiel #8
0
def value_iteration(
    model: Model,
    *,
    verbose: bool = False,  #
    details: bool = True,  #
    tol=1e-6,
    maxit=500,
    maxit_howard=20,
) -> ValueIterationResult:
    """
    Solve for the value function and associated Markov decision rule by iterating over
    the value function.

    Parameters:
    -----------
    model :
        model to be solved
    dr :
        decision rule to evaluate

    Returns:
    --------
    mdr : Markov decision rule
        The solved decision rule/policy function
    mdrv: decision rule
        The solved value function
    """

    transition = model.functions["transition"]
    felicity = model.functions["felicity"]
    controls_lb = model.functions["controls_lb"]
    controls_ub = model.functions["controls_ub"]

    parms = model.calibration["parameters"]
    discount = model.calibration["beta"]

    x0 = model.calibration["controls"]
    m0 = model.calibration["exogenous"]
    s0 = model.calibration["states"]
    r0 = felicity(m0, s0, x0, parms)

    process = model.exogenous

    grid, dprocess = model.discretize()
    endo_grid = grid["endo"]
    exo_grid = grid["exo"]

    n_ms = dprocess.n_nodes  # number of exogenous states
    n_mv = dprocess.n_inodes(
        0)  # this assume number of integration nodes is constant

    mdrv = DecisionRule(exo_grid, endo_grid)

    s = mdrv.endo_grid.nodes
    N = s.shape[0]
    n_x = len(x0)

    mdr = constant_policy(model)

    controls_0 = np.zeros((n_ms, N, n_x))
    for i_ms in range(n_ms):
        controls_0[i_ms, :, :] = mdr.eval_is(i_ms, s)

    values_0 = np.zeros((n_ms, N, 1))
    # for i_ms in range(n_ms):
    #     values_0[i_ms, :, :] = mdrv(i_ms, grid)

    mdr = DecisionRule(exo_grid, endo_grid)
    # mdr.set_values(controls_0)

    # THIRD: value function iterations until convergence
    it = 0
    err_v = 100
    err_v_0 = 0
    gain_v = 0.0
    err_x = 100
    err_x_0 = 0
    tol_x = 1e-5
    tol_v = 1e-7

    itprint = IterationsPrinter(
        ("N", int),
        ("Error_V", float),
        ("Gain_V", float),
        ("Error_x", float),
        ("Gain_x", float),
        ("Eval_n", int),
        ("Time", float),
        verbose=verbose,
    )
    itprint.print_header("Start value function iterations.")

    while (it < maxit) and (err_v > tol or err_x > tol_x):

        t_start = time.time()
        it += 1

        mdr.set_values(controls_0)
        if it > 2:
            ev = evaluate_policy(model,
                                 mdr,
                                 dr0=mdrv,
                                 verbose=False,
                                 details=True)
        else:
            ev = evaluate_policy(model, mdr, verbose=False, details=True)

        mdrv = ev.solution
        for i_ms in range(n_ms):
            values_0[i_ms, :, :] = mdrv.eval_is(i_ms, s)

        values = values_0.copy()
        controls = controls_0.copy()

        for i_m in range(n_ms):
            m = dprocess.node(i_m)
            for n in range(N):
                s_ = s[n, :]
                x = controls[i_m, n, :]
                lb = controls_lb(m, s_, parms)
                ub = controls_ub(m, s_, parms)
                bnds = [e for e in zip(lb, ub)]

                def valfun(xx):
                    return -choice_value(
                        transition,
                        felicity,
                        i_m,
                        s_,
                        xx,
                        mdrv,
                        dprocess,
                        parms,
                        discount,
                    )[0]

                res = scipy.optimize.minimize(valfun, x, bounds=bnds)
                controls[i_m, n, :] = res.x
                values[i_m, n, 0] = -valfun(x)

        # compute error, update value and dr
        err_x = abs(controls - controls_0).max()
        err_v = abs(values - values_0).max()
        t_end = time.time()
        elapsed = t_end - t_start

        values_0 = values
        controls_0 = controls

        gain_x = err_x / err_x_0
        gain_v = err_v / err_v_0

        err_x_0 = err_x
        err_v_0 = err_v

        itprint.print_iteration(
            N=it,
            Error_V=err_v,
            Gain_V=gain_v,
            Error_x=err_x,
            Gain_x=gain_x,
            Eval_n=ev.iterations,
            Time=elapsed,
        )

    itprint.print_finished()

    mdr = DecisionRule(exo_grid, endo_grid)

    mdr.set_values(controls)
    mdrv.set_values(values_0)

    if not details:
        return mdr, mdrv
    else:
        return ValueIterationResult(
            mdr,  #:AbstractDecisionRule
            mdrv,  #:AbstractDecisionRule
            it,  #:Int
            dprocess,  #:AbstractDiscretizedProcess
            err_x < tol_x,  #:Bool
            tol_x,  #:Float64
            err_x,  #:Float64
            err_v < tol_v,  #:Bool
            tol_v,  #:Float64
            err_v,  #:Float64
            None,  # log:     #:ValueIterationLog
            None,  # trace:   #:Union{Nothing,IterationTrace
        )
Beispiel #9
0
def value_iteration(model,
                    grid={},
                    tol=1e-6,
                    maxit=500,
                    maxit_howard=20,
                    verbose=False,
                    details=True):
    """
    Solve for the value function and associated Markov decision rule by iterating over
    the value function.

    Parameters:
    -----------
    model :
        "dtmscc" model. Must contain a 'felicity' function.
    grid :
        grid options
    dr :
        decision rule to evaluate

    Returns:
    --------
    mdr : Markov decision rule
        The solved decision rule/policy function
    mdrv: decision rule
        The solved value function
    """

    transition = model.functions['transition']
    felicity = model.functions['felicity']
    controls_lb = model.functions['controls_lb']
    controls_ub = model.functions['controls_ub']

    parms = model.calibration['parameters']
    discount = model.calibration['beta']

    x0 = model.calibration['controls']
    m0 = model.calibration['exogenous']
    s0 = model.calibration['states']
    r0 = felicity(m0, s0, x0, parms)

    process = model.exogenous
    dprocess = process.discretize()

    n_ms = dprocess.n_nodes()  # number of exogenous states
    n_mv = dprocess.n_inodes(
        0)  # this assume number of integration nodes is constant

    endo_grid = model.get_grid(**grid)

    exo_grid = dprocess.grid

    mdrv = DecisionRule(exo_grid, endo_grid)

    grid = mdrv.endo_grid.nodes()
    N = grid.shape[0]
    n_x = len(x0)

    mdr = constant_policy(model)
    
    controls_0 = np.zeros((n_ms, N, n_x))
    for i_ms in range(n_ms):
        controls_0[i_ms, :, :] = mdr.eval_is(i_ms, grid)

    values_0 = np.zeros((n_ms, N, 1))
    # for i_ms in range(n_ms):
    #     values_0[i_ms, :, :] = mdrv(i_ms, grid)

    mdr = DecisionRule(exo_grid, endo_grid)
    # mdr.set_values(controls_0)

    # THIRD: value function iterations until convergence
    it = 0
    err_v = 100
    err_v_0 = 0
    gain_v = 0.0
    err_x = 100
    err_x_0 = 0
    tol_x = 1e-5
    tol_v = 1e-7

    itprint = IterationsPrinter(
        ('N', int), ('Error_V', float), ('Gain_V', float), ('Error_x', float),
        ('Gain_x', float), ('Eval_n', int), ('Time', float),
        verbose=verbose)
    itprint.print_header('Start value function iterations.')

    while (it < maxit) and (err_v > tol or err_x > tol_x):

        t_start = time.time()
        it += 1

        mdr.set_values(controls_0)
        if it > 2:
            ev = evaluate_policy(
                model, mdr, initial_guess=mdrv, verbose=False, details=True)
        else:
            ev = evaluate_policy(model, mdr, verbose=False, details=True)

        mdrv = ev.solution
        for i_ms in range(n_ms):
            values_0[i_ms, :, :] = mdrv.eval_is(i_ms, grid)

        values = values_0.copy()
        controls = controls_0.copy()

        for i_m in range(n_ms):
            m = dprocess.node(i_m)
            for n in range(N):
                s = grid[n, :]
                x = controls[i_m, n, :]
                lb = controls_lb(m, s, parms)
                ub = controls_ub(m, s, parms)
                bnds = [e for e in zip(lb, ub)]

                def valfun(xx):
                    return -choice_value(transition, felicity, i_m, s, xx,
                                         mdrv, dprocess, parms, discount)[0]

                res = scipy.optimize.minimize(valfun, x, bounds=bnds)
                controls[i_m, n, :] = res.x
                values[i_m, n, 0] = -valfun(x)

        # compute error, update value and dr
        err_x = abs(controls - controls_0).max()
        err_v = abs(values - values_0).max()
        t_end = time.time()
        elapsed = t_end - t_start

        values_0 = values
        controls_0 = controls

        gain_x = err_x / err_x_0
        gain_v = err_v / err_v_0

        err_x_0 = err_x
        err_v_0 = err_v

        itprint.print_iteration(
            N=it,
            Error_V=err_v,
            Gain_V=gain_v,
            Error_x=err_x,
            Gain_x=gain_x,
            Eval_n=ev.iterations,
            Time=elapsed)

    itprint.print_finished()

    mdr = DecisionRule(exo_grid, endo_grid)
    mdr.set_values(controls)
    mdrv.set_values(values_0)

    if not details:
        return mdr, mdrv
    else:
        return ValueIterationResult(
            mdr,             #:AbstractDecisionRule
            mdrv,            #:AbstractDecisionRule
            it,              #:Int
            dprocess,        #:AbstractDiscretizedProcess
            err_x<tol_x,     #:Bool
            tol_x,           #:Float64
            err_x,           #:Float64
            err_v<tol_v,     #:Bool
            tol_v,           #:Float64
            err_v,           #:Float64
            None,            #log:     #:ValueIterationLog
            None             #trace:   #:Union{Nothing,IterationTrace
        )
def improved_time_iteration(model,
                            method='jac',
                            initial_dr=None,
                            interp_type='spline',
                            mu=2,
                            maxbsteps=10,
                            verbose=False,
                            tol=1e-8,
                            smaxit=500,
                            maxit=1000,
                            complementarities=True,
                            compute_radius=False,
                            invmethod='gmres',
                            details=True):
    def vprint(*args, **kwargs):
        if verbose:
            print(*args, **kwargs)

    itprint = IterationsPrinter(
        ('N', int), ('f_x', float), ('d_x', float), ('Time_residuals', float),
        ('Time_inversion', float), ('Time_search', float), ('Lambda_0', float),
        ('N_invert', int), ('N_search', int),
        verbose=verbose)
    itprint.print_header('Start Improved Time Iterations.')

    f = model.functions['arbitrage']
    g = model.functions['transition']
    x_lb = model.functions['controls_lb']
    x_ub = model.functions['controls_ub']

    parms = model.calibration['parameters']

    dp = model.exogenous.discretize()

    n_m = max(dp.n_nodes(), 1)

    n_s = len(model.symbols['states'])

    grid = model.get_grid()

    if interp_type == 'spline':
        ddr = DecisionRule(dp.grid, grid)
        ddr_filt = DecisionRule(dp.grid, grid)
    elif interp_type == 'smolyak':
        ddr = SmolyakDecisionRule(n_m, grid.min, grid.max, mu)
        ddr_filt = SmolyakDecisionRule(n_m, grid.min, grid.max, mu)
        derivative_type = 'numerical'

    # s = ddr.endo_grid
    s = grid.nodes()
    N = s.shape[0]
    n_x = len(model.symbols['controls'])
    x0 = model.calibration['controls'][None,
                                       None, ].repeat(n_m,
                                                      axis=0).repeat(N, axis=1)

    if initial_dr is not None:
        for i_m in range(n_m):
            x0[i_m, :, :] = initial_dr.eval_is(i_m, s)
    ddr.set_values(x0)

    steps = 0.5**numpy.arange(maxbsteps)

    lb = x0.copy()
    ub = x0.copy()
    for i_m in range(n_m):
        m = dp.node(i_m)
        lb[i_m, :] = x_lb(m, s, parms)
        ub[i_m, :] = x_ub(m, s, parms)

    x = x0

    # both affect the precision

    ddr.set_values(x)

    ## memory allocation

    n_im = dp.n_inodes(0)  # we assume it is constant for now

    jres = numpy.zeros((n_m, n_im, N, n_x, n_x))
    S_ij = numpy.zeros((n_m, n_im, N, n_s))

    for it in range(maxit):

        jres[...] = 0.0
        S_ij[...] = 0.0

        t1 = time.time()

        # compute derivatives and residuals:
        # res: residuals
        # dres: derivatives w.r.t. x
        # jres: derivatives w.r.t. ~x
        # fut_S: future states

        ddr.set_values(x)
        #
        # ub[ub>100000] = 100000
        # lb[lb<-100000] = -100000
        #
        # sh_x = x.shape
        # rr =euler_residuals(f,g,s,x,ddr,dp,parms, diff=False, with_jres=False,set_dr=True)
        # print(rr.shape)
        #
        # from iti.fb import smooth_
        # jj = smooth_(rr, x, lb, ub)
        #
        # print("Errors with complementarities")
        # print(abs(jj.max()))
        #
        # exit()
        #

        from dolo.numeric.optimize.newton import SerialDifferentiableFunction
        sh_x = x.shape
        ff = SerialDifferentiableFunction(
            lambda u: euler_residuals(f,
                                      g,
                                      s,
                                      u.reshape(sh_x),
                                      ddr,
                                      dp,
                                      parms,
                                      diff=False,
                                      with_jres=False,
                                      set_dr=False).reshape((-1, sh_x[2])))
        res, dres = ff(x.reshape((-1, sh_x[2])))
        res = res.reshape(sh_x)
        dres = dres.reshape((sh_x[0], sh_x[1], sh_x[2], sh_x[2]))
        junk, jres, fut_S = euler_residuals(f,
                                            g,
                                            s,
                                            x,
                                            ddr,
                                            dp,
                                            parms,
                                            diff=False,
                                            with_jres=True,
                                            set_dr=False,
                                            jres=jres,
                                            S_ij=S_ij)

        # if there are complementerities, we modify derivatives
        if complementarities:
            res, dres, jres = smooth(res, dres, jres, x - lb)
            res[...] *= -1
            dres[...] *= -1
            jres[...] *= -1
            res, dres, jres = smooth(res, dres, jres, ub - x, pos=-1.0)
            res[...] *= -1
            dres[...] *= -1
            jres[...] *= -1

        err_0 = (abs(res).max())

        # premultiply by A
        jres[...] *= -1.0

        for i_m in range(n_m):
            for j_m in range(n_im):
                M = jres[i_m, j_m, :, :, :]
                X = dres[i_m, :, :, :].copy()
                sol = solve_tensor(X, M)

        t2 = time.time()

        # new version
        if invmethod == 'gmres':
            import scipy.sparse.linalg
            ddx = solve_gu(dres.copy(), res.copy())
            L = Operator(jres, fut_S, ddr_filt)
            n0 = L.counter
            L.addid = True
            ttol = err_0 / 100
            sol = scipy.sparse.linalg.gmres(
                L, ddx.ravel(), tol=ttol)  #, maxiter=1, restart=smaxit)
            lam0 = 0.01
            nn = L.counter - n0
            tot = sol[0].reshape(ddx.shape)
        else:
            # compute inversion
            tot, nn, lam0 = invert_jac(res,
                                       dres,
                                       jres,
                                       fut_S,
                                       ddr_filt,
                                       tol=tol,
                                       maxit=smaxit,
                                       verbose=(verbose == 'full'))

        # lam, lam_max, lambdas = radius_jac(res,dres,jres,fut_S,tol=tol,maxit=1000,verbose=(verbose=='full'),filt=ddr_filt)

        # backsteps
        t3 = time.time()
        for i_bckstps, lam in enumerate(steps):
            new_x = x - tot * lam
            new_err = euler_residuals(f,
                                      g,
                                      s,
                                      new_x,
                                      ddr,
                                      dp,
                                      parms,
                                      diff=False,
                                      set_dr=True)

            if complementarities:
                new_err = smooth_nodiff(new_err, new_x - lb)
                new_err = smooth_nodiff(-new_err, ub - new_x)

            new_err = abs(new_err).max()
            if new_err < err_0:
                break

        err_2 = abs(tot).max()
        t4 = time.time()
        itprint.print_iteration(N=it,
                                f_x=err_0,
                                d_x=err_2,
                                Time_residuals=t2 - t1,
                                Time_inversion=t3 - t2,
                                Time_search=t4 - t3,
                                Lambda_0=lam0,
                                N_invert=nn,
                                N_search=i_bckstps)
        if err_0 < tol:
            break

        x = new_x

    ddr.set_values(x)

    itprint.print_finished()

    # if compute_radius:
    #     return ddx,L
    #     lam, lam_max, lambdas = radius_jac(res,dres,jres,fut_S,ddr_filt,tol=tol,maxit=smaxit,verbose=(verbose=='full'))
    #     return ddr, lam, lam_max, lambdas
    # else:
    if not details:
        return ddr
    else:
        ddx = solve_gu(dres.copy(), res.copy())
        L = Operator(jres, fut_S, ddr_filt)
        lam = scipy.sparse.linalg.eigs(L, k=1, return_eigenvectors=False)
        lam = abs(lam[0])
        # lam, lam_max, lambdas = radius_jac(res,dres,jres,fut_S,ddr_filt,tol=tol,maxit=smaxit,verbose=(verbose=='full'))
        return ImprovedTimeIterationResult(ddr, it, err_0, err_2, err_0 < tol,
                                           complementarities, lam, None, L)
Beispiel #11
0
def egm(
    model: Model,
    dr0: DecisionRule = None,
    verbose: bool = False,
    details: bool = True,
    a_grid=None,
    η_tol=1e-6,
    maxit=1000,
    grid=None,
    dp=None,
):
    """
    a_grid: (numpy-array) vector of points used to discretize poststates; must be increasing
    """

    assert len(model.symbols["states"]) == 1
    assert (len(model.symbols["controls"]) == 1
            )  # we probably don't need this restriction

    from dolo.numeric.processes import IIDProcess

    iid_process = isinstance(model.exogenous, IIDProcess)

    def vprint(t):
        if verbose:
            print(t)

    p = model.calibration["parameters"]

    if grid is None and dp is None:
        grid, dp = model.discretize()

    s = grid["endo"].nodes

    funs = model.__original_gufunctions__
    h = funs["expectation"]
    gt = funs["half_transition"]
    τ = funs["direct_response_egm"]
    aτ = funs["reverse_state"]
    lb = funs["arbitrage_lb"]
    ub = funs["arbitrage_ub"]

    if dr0 is None:
        x0 = model.calibration["controls"]
        dr0 = lambda i, s: x0[None, :].repeat(s.shape[0], axis=0)

    n_m = dp.n_nodes
    n_x = len(model.symbols["controls"])

    if a_grid is None:
        raise Exception("You must supply a grid for the post-states.")

    assert a_grid.ndim == 1
    a = a_grid[:, None]
    N_a = a.shape[0]

    N = s.shape[0]

    n_h = len(model.symbols["expectations"])

    xa = np.zeros((n_m, N_a, n_x))
    sa = np.zeros((n_m, N_a, 1))
    xa0 = np.zeros((n_m, N_a, n_x))
    sa0 = np.zeros((n_m, N_a, 1))

    z = np.zeros((n_m, N_a, n_h))

    if verbose:
        headline = "|{0:^4} | {1:10} |".format("N", " Error")
        stars = "-" * len(headline)
        print(stars)
        print(headline)
        print(stars)

    for it in range(0, maxit):

        if it == 0:
            drfut = dr0

        else:

            def drfut(i, ss):
                if iid_process:
                    i = 0
                m = dp.node(i)
                l_ = lb(m, ss, p)
                u_ = ub(m, ss, p)
                x = eval_linear((sa0[i, :, 0], ), xa0[i, :, 0], ss)[:, None]
                x = np.minimum(x, u_)
                x = np.maximum(x, l_)
                return x

        z[:, :, :] = 0

        for i_m in range(n_m):
            m = dp.node(i_m)
            for i_M in range(dp.n_inodes(i_m)):
                w = dp.iweight(i_m, i_M)
                M = dp.inode(i_m, i_M)
                S = gt(m, a, M, p)
                print(it, i_m, i_M)
                X = drfut(i_M, S)
                z[i_m, :, :] += w * h(M, S, X, p)
            xa[i_m, :, :] = τ(m, a, z[i_m, :, :], p)
            sa[i_m, :, :] = aτ(m, a, xa[i_m, :, :], p)

        if it > 1:
            η = abs(xa - xa0).max() + abs(sa - sa0).max()
        else:
            η = 1

        vprint("|{0:4} | {1:10.3e} |".format(it, η))

        if η < η_tol:
            break

        sa0[...] = sa
        xa0[...] = xa

    # resample the result on the standard grid
    endo_grid = grid["endo"]
    exo_grid = grid["exo"]
    mdr = DecisionRule(exo_grid, endo_grid, dprocess=dp, interp_method="cubic")

    mdr.set_values(
        np.concatenate([drfut(i, s)[None, :, :] for i in range(n_m)], axis=0))

    sol = EGMResult(mdr, it, dp, (η < η_tol), η_tol, η)

    return sol
Beispiel #12
0
def time_iteration(
    model: Model,
    *,  #
    dr0: DecisionRule = None,  #
    verbose: bool = True,  #
    details: bool = True,  #
    ignore_constraints: bool = False,  #
    trace: bool = False,  #
    dprocess=None,
    maxit=1000,
    inner_maxit=10,
    tol=1e-6,
    hook=None,
    interp_method="cubic",
    # obsolete
    with_complementarities=None,
) -> TimeIterationResult:
    """Finds a global solution for ``model`` using backward time-iteration.


    This algorithm iterates on the residuals of the arbitrage equations

    Parameters
    ----------
    model : Model
        model to be solved
    verbose : bool
        if True, display iterations
    dr0 : decision rule
        initial guess for the decision rule
    with_complementarities : bool (True)
        if False, complementarity conditions are ignored
    maxit: maximum number of iterations
    inner_maxit: maximum number of iteration for inner solver
    tol: tolerance criterium for successive approximations
    hook: Callable
        function to be called within each iteration, useful for debugging purposes


    Returns
    -------
    decision rule :
        approximated solution
    """

    # deal with obsolete options
    if with_complementarities is not None:
        # TODO warn
        pass
    else:
        with_complementarities = not ignore_constraints

    if trace:
        trace_details = []
    else:
        trace_details = None

    from dolo import dprint

    def vprint(t):
        if verbose:
            print(t)

    grid, dprocess_ = model.discretize()

    if dprocess is None:
        dprocess = dprocess_

    n_ms = dprocess.n_nodes  # number of exogenous states
    n_mv = dprocess.n_inodes(
        0)  # this assume number of integration nodes is constant

    x0 = model.calibration["controls"]
    parms = model.calibration["parameters"]
    n_x = len(x0)
    n_s = len(model.symbols["states"])

    endo_grid = grid["endo"]
    exo_grid = grid["exo"]

    mdr = DecisionRule(exo_grid,
                       endo_grid,
                       dprocess=dprocess,
                       interp_method=interp_method)

    s = mdr.endo_grid.nodes
    N = s.shape[0]

    controls_0 = numpy.zeros((n_ms, N, n_x))
    if dr0 is None:
        controls_0[:, :, :] = x0[None, None, :]
    else:
        if isinstance(dr0, AlgoResult):
            dr0 = dr0.dr
        try:
            for i_m in range(n_ms):
                controls_0[i_m, :, :] = dr0(i_m, s)
        except Exception:
            for i_m in range(n_ms):
                m = dprocess.node(i_m)
                controls_0[i_m, :, :] = dr0(m, s)

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

    if "arbitrage_lb" in model.functions and with_complementarities == True:
        lb_fun = model.functions["arbitrage_lb"]
        ub_fun = model.functions["arbitrage_ub"]
        lb = numpy.zeros_like(controls_0) * numpy.nan
        ub = numpy.zeros_like(controls_0) * numpy.nan
        for i_m in range(n_ms):
            m = dprocess.node(i_m)[None, :]
            p = parms[None, :]
            m = numpy.repeat(m, N, axis=0)
            p = numpy.repeat(p, N, axis=0)

            lb[i_m, :, :] = lb_fun(m, s, p)
            ub[i_m, :, :] = ub_fun(m, s, p)

    else:
        with_complementarities = False

    sh_c = controls_0.shape

    controls_0 = controls_0.reshape((-1, n_x))

    from dolo.numeric.optimize.newton import newton, SerialDifferentiableFunction
    from dolo.numeric.optimize.ncpsolve import ncpsolve

    err = 10
    it = 0

    if with_complementarities:
        lb = lb.reshape((-1, n_x))
        ub = ub.reshape((-1, n_x))

    itprint = IterationsPrinter(
        ("N", int),
        ("Error", float),
        ("Gain", float),
        ("Time", float),
        ("nit", int),
        verbose=verbose,
    )
    itprint.print_header("Start Time Iterations.")

    import time

    t1 = time.time()

    err_0 = numpy.nan

    verbit = verbose == "full"

    while err > tol and it < maxit:

        it += 1

        t_start = time.time()

        mdr.set_values(controls_0.reshape(sh_c))

        if trace:
            trace_details.append({"dr": copy.deepcopy(mdr)})

        fn = lambda x: residuals_simple(f, g, s, x.reshape(sh_c), mdr,
                                        dprocess, parms).reshape((-1, n_x))
        dfn = SerialDifferentiableFunction(fn)

        res = fn(controls_0)

        if hook:
            hook()

        if with_complementarities:
            [controls, nit] = ncpsolve(dfn,
                                       lb,
                                       ub,
                                       controls_0,
                                       verbose=verbit,
                                       maxit=inner_maxit)
        else:
            [controls, nit] = newton(dfn,
                                     controls_0,
                                     verbose=verbit,
                                     maxit=inner_maxit)

        err = abs(controls - controls_0).max()

        err_SA = err / err_0
        err_0 = err

        controls_0 = controls

        t_finish = time.time()
        elapsed = t_finish - t_start

        itprint.print_iteration(N=it,
                                Error=err_0,
                                Gain=err_SA,
                                Time=elapsed,
                                nit=nit),

    controls_0 = controls.reshape(sh_c)

    mdr.set_values(controls_0)
    if trace:
        trace_details.append({"dr": copy.deepcopy(mdr)})

    itprint.print_finished()

    if not details:
        return mdr

    return TimeIterationResult(
        mdr,
        it,
        with_complementarities,
        dprocess,
        err < tol,  # x_converged: bool
        tol,  # x_tol
        err,  #: float
        None,  # log: object # TimeIterationLog
        trace_details,  # trace: object #{Nothing,IterationTrace}
    )
Beispiel #13
0
def time_iteration(model, initial_guess=None, dprocess=None, with_complementarities=True,
                        verbose=True, grid={},
                        maxit=1000, inner_maxit=10, tol=1e-6, hook=None, details=False):

    '''
    Finds a global solution for ``model`` using backward time-iteration.

    This algorithm iterates on the residuals of the arbitrage equations

    Parameters
    ----------
    model : Model
        model to be solved
    verbose : boolean
        if True, display iterations
    initial_guess : decision rule
        initial guess for the decision rule
    dprocess : DiscretizedProcess (model.exogenous.discretize())
        discretized process to be used
    with_complementarities : boolean (True)
        if False, complementarity conditions are ignored
    grid: grid options
        overload the values set in `options:grid` section
    maxit: maximum number of iterations
    inner_maxit: maximum number of iteration for inner solver
    tol: tolerance criterium for successive approximations
    hook: Callable
        function to be called within each iteration, useful for debugging purposes


    Returns
    -------
    decision rule :
        approximated solution
    '''

    from dolo import dprint
    def vprint(t):
        if verbose:
            print(t)

    if dprocess is None:
        dprocess = model.exogenous.discretize()

    n_ms = dprocess.n_nodes() # number of exogenous states
    n_mv = dprocess.n_inodes(0) # this assume number of integration nodes is constant

    x0 = model.calibration['controls']
    parms = model.calibration['parameters']
    n_x = len(x0)
    n_s = len(model.symbols['states'])

    endo_grid = model.get_grid(**grid)

    exo_grid = dprocess.grid

    mdr = DecisionRule(exo_grid, endo_grid)

    grid = mdr.endo_grid.nodes()
    N = grid.shape[0]

    controls_0 = numpy.zeros((n_ms, N, n_x))
    if initial_guess is None:
        controls_0[:, :, :] = x0[None,None,:]
    else:
        if isinstance(initial_guess, AlgoResult):
            initial_guess = initial_guess.dr
        try:
            for i_m in range(n_ms):
                controls_0[i_m, :, :] = initial_guess(i_m, grid)
        except Exception:
            for i_m in range(n_ms):
                m = dprocess.node(i_m)
                controls_0[i_m, :, :] = initial_guess(m, grid)

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

    if 'controls_lb' in model.functions and with_complementarities==True:
        lb_fun = model.functions['controls_lb']
        ub_fun = model.functions['controls_ub']
        lb = numpy.zeros_like(controls_0)*numpy.nan
        ub = numpy.zeros_like(controls_0)*numpy.nan
        for i_m in range(n_ms):
            m = dprocess.node(i_m)[None,:]
            p = parms[None,:]
            m = numpy.repeat(m, N, axis=0)
            p = numpy.repeat(p, N, axis=0)

            lb[i_m,:,:] = lb_fun(m, grid, p)
            ub[i_m,:,:] = ub_fun(m, grid, p)

    else:
        with_complementarities = False

    sh_c = controls_0.shape

    controls_0 = controls_0.reshape( (-1,n_x) )

    from dolo.numeric.optimize.newton import newton, SerialDifferentiableFunction
    from dolo.numeric.optimize.ncpsolve import ncpsolve

    err = 10
    it = 0

    if with_complementarities:
        vprint("Solving WITH complementarities.")
        lb = lb.reshape((-1,n_x))
        ub = ub.reshape((-1,n_x))


    if verbose:
        headline = '|{0:^4} | {1:10} | {2:8} | {3:8} | {4:3} |'.format( 'N',' Error', 'Gain','Time',  'nit' )
        stars = '-'*len(headline)
        print(stars)
        print(headline)
        print(stars)

    import time
    t1 = time.time()

    err_0 = numpy.nan

    verbit = (verbose == 'full')

    while err>tol and it<maxit:

        it += 1

        t_start = time.time()

        mdr.set_values(controls_0.reshape(sh_c))

        fn = lambda x: residuals_simple(f, g, grid, x.reshape(sh_c), mdr, dprocess, parms).reshape((-1,n_x))
        dfn = SerialDifferentiableFunction(fn)

        res = fn(controls_0)

        if hook:
            hook()

        if with_complementarities:
            [controls,nit] = ncpsolve(dfn, lb, ub, controls_0, verbose=verbit, maxit=inner_maxit)
        else:
            [controls, nit] = newton(dfn, controls_0, verbose=verbit, maxit=inner_maxit)

        err = abs(controls-controls_0).max()

        err_SA = err/err_0
        err_0 = err

        controls_0 = controls

        t_finish = time.time()
        elapsed = t_finish - t_start

        if verbose:
            print('|{0:4} | {1:10.3e} | {2:8.3f} | {3:8.3f} | {4:3} |'.format( it, err, err_SA, elapsed, nit  ))

    controls_0 = controls.reshape(sh_c)

    t2 = time.time()

    if verbose:
        print(stars)
        print("Elapsed: {} seconds.".format(t2-t1))
        print(stars)

    if not details:
        return mdr

    return TimeIterationResult(
        mdr,
        it,
        with_complementarities,
        dprocess,
        err<tol, # x_converged: bool
        tol, # x_tol
        err, #: float
        None,  # log: object # TimeIterationLog
        None   # trace: object #{Nothing,IterationTrace}
    )