def euler_residuals(f, g, s, x, dr, dp, p_, diff=True, with_jres=False, set_dr=True, jres=None, S_ij=None): t1 = time.time() if set_dr: dr.set_values(x) N = s.shape[0] n_s = s.shape[1] n_x = x.shape[2] n_ms = max(dp.n_nodes, 1) # number of markov states n_im = dp.n_inodes(0) res = numpy.zeros_like(x) if with_jres: if jres is None: jres = numpy.zeros((n_ms, n_im, N, n_x, n_x)) if S_ij is None: S_ij = numpy.zeros((n_ms, n_im, N, n_s)) for i_ms in range(n_ms): m_ = dp.node(i_ms) xm = x[i_ms, :, :] for I_ms in range(n_im): M_ = dp.inode(i_ms, I_ms) w = dp.iweight(i_ms, I_ms) S = g(m_, s, xm, M_, p_, diff=False) XM = dr.eval_ijs(i_ms, I_ms, S) if with_jres: ff = SerialDifferentiableFunction( lambda u: f(m_, s, xm, M_, S, u, p_, diff=False)) rr, rr_XM = ff(XM) rr = f(m_, s, xm, M_, S, XM, p_, diff=False) jres[i_ms, I_ms, :, :, :] = w * rr_XM S_ij[i_ms, I_ms, :, :] = S else: rr = f(m_, s, xm, M_, S, XM, p_, diff=False) res[i_ms, :, :] += w * rr t2 = time.time() if with_jres: return res, jres, S_ij else: return res
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} )
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 )
def parameterized_expectations(model, verbose=False, dr0=None, pert_order=1, with_complementarities=True, grid={}, distribution={}, maxit=100, tol=1e-8, inner_maxit=10, direct=False): ''' Find global solution for ``model`` via parameterized expectations. Controls must be expressed as a direct function of equilibrium objects. Algorithm iterates over the expectations function in the arbitrage equation. Parameters: ---------- model : NumericModel ``dtcscc`` model to be solved verbose : boolean if True, display iterations dr0 : decision rule initial guess for the decision rule pert_order : {1} if no initial guess is supplied, the perturbation solution at order ``pert_order`` is used as initial guess grid : grid options distribution : distribution options maxit : maximum number of iterations tol : tolerance criterium for successive approximations inner_maxit : maximum number of iteration for inner solver direct : if True, solve with direct method. If false, solve indirectly Returns ------- decision rule : approximated solution ''' t1 = time.time() g = model.functions['transition'] h = model.functions['expectation'] d = model.functions['direct_response'] f = model.functions['arbitrage_exp'] # f(s, x, z, p, out) parms = model.calibration['parameters'] if dr0 is None: if pert_order == 1: dr0 = approximate_controls(model) if pert_order > 1: raise Exception("Perturbation order > 1 not supported (yet).") approx = model.get_endo_grid(**grid) grid = approx.grid interp_type = approx.interpolation dr = create_interpolator(approx, interp_type) expect = create_interpolator(approx, interp_type) distrib = model.get_distribution(**distribution) nodes, weights = distrib.discretize() N = grid.shape[0] z = np.zeros((N, len(model.symbols['expectations']))) x_0 = dr0(grid) x_0 = x_0.real # just in case ... h_0 = h(grid, x_0, parms) it = 0 err = 10 err_0 = 10 verbit = True if verbose == 'full' else False if with_complementarities is True: lbfun = model.functions['controls_lb'] ubfun = model.functions['controls_ub'] lb = lbfun(grid, parms) ub = ubfun(grid, parms) else: lb = None ub = None if verbose: headline = '|{0:^4} | {1:10} | {2:8} | {3:8} |' headline = headline.format('N', ' Error', 'Gain', 'Time') stars = '-' * len(headline) print(stars) print(headline) print(stars) # format string for within loop fmt_str = '|{0:4} | {1:10.3e} | {2:8.3f} | {3:8.3f} |' while err > tol and it <= maxit: it += 1 t_start = time.time() # dr.set_values(x_0) expect.set_values(h_0) # evaluate expectation over the future state z[...] = 0 for i in range(weights.shape[0]): e = nodes[i, :] S = g(grid, x_0, e, parms) z += weights[i] * expect(S) if direct is True: # Use control as direct function of arbitrage equation new_x = d(grid, z, parms) if with_complementarities is True: new_x = np.minimum(new_x, ub) new_x = np.maximum(new_x, lb) else: # Find control by solving arbitrage equation def fun(x): return f(grid, x, z, parms) sdfun = SerialDifferentiableFunction(fun) if with_complementarities is True: [new_x, nit] = ncpsolve(sdfun, lb, ub, x_0, verbose=verbit, maxit=inner_maxit) else: [new_x, nit] = serial_newton(sdfun, x_0, verbose=verbit) new_h = h(grid, new_x, parms) # update error err = (abs(new_h - h_0).max()) # Update guess for decision rule and expectations function x_0 = new_x h_0 = new_h # print error infomation if `verbose` err_SA = err / err_0 err_0 = err t_finish = time.time() elapsed = t_finish - t_start if verbose: print(fmt_str.format(it, err, err_SA, elapsed)) if it == maxit: import warnings warnings.warn(UserWarning("Maximum number of iterations reached")) # compute final fime and do final printout if `verbose` t2 = time.time() if verbose: print(stars) print('Elapsed: {} seconds.'.format(t2 - t1)) print(stars) # Interpolation for the decision rule dr.set_values(x_0) return dr
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
def simulate(model, dr, s0=None, n_exp=0, horizon=40, seed=1, discard=False, solve_expectations=False, nodes=None, weights=None, forcing_shocks=None, return_array=False): ''' Simulate a model using the specified decision rule. Parameters --------- model: NumericModel a "dtcscc" model dr: decision rule s0: ndarray initial state where all simulations start n_exp: int number of simulations. Use 0 for impulse-response functions horizon: int horizon for the simulations seed: int used to initialize the random number generator. Use it to replicate exact same results among simulations discard: boolean (False) if True, then all simulations containing at least one non finite value are discarded solve_expectations: boolean (False) if True, Euler equations are solved at each step using the controls to form expectations nodes: ndarray if `solve_expectations` is True use ``nodes`` for integration weights: ndarray if `solve_expectations` is True use ``weights`` for integration forcing_shocks: ndarray specify an exogenous process of shocks (requires ``n_exp<=1``) return_array: boolean (False) if True, then all return a numpy array containing simulated data, otherwise return a pandas DataFrame or Panel. Returns ------- ndarray or pandas.Dataframe: - if `n_exp<=1` returns a DataFrame object - if `n_exp>1` returns a ``horizon x n_exp x n_v`` array where ``n_v`` is the number of variables. ''' if n_exp == 0: irf = True n_exp = 1 else: irf = False calib = model.calibration parms = numpy.array(calib['parameters']) distrib = model.get_distribution() sigma = distrib.sigma if s0 is None: s0 = calib['states'] # s0 = numpy.atleast_2d(s0.flatten()).T x0 = dr(s0) s_simul = numpy.zeros((horizon, n_exp, s0.shape[0])) x_simul = numpy.zeros((horizon, n_exp, x0.shape[0])) s_simul[0, :, :] = s0[None, :] x_simul[0, :, :] = x0[None, :] fun = model.functions f = model.functions['arbitrage'] g = model.functions['transition'] numpy.random.seed(seed) for i in range(horizon): mean = numpy.zeros(sigma.shape[0]) if irf: if forcing_shocks is not None and i < forcing_shocks.shape[0]: epsilons = forcing_shocks[i, :] else: epsilons = numpy.zeros((1, sigma.shape[0])) else: epsilons = numpy.random.multivariate_normal(mean, sigma, n_exp) s = s_simul[i, :, :] x = dr(s) if solve_expectations: lbfun = model.functions['controls_lb'] ubfun = model.functions['controls_ub'] lb = lbfun(s, parms) ub = ubfun(s, parms) def fobj(t): return step_residual(s, t, dr, f, g, parms, nodes, weights) dfobj = SerialDifferentiableFunction(fobj) [x, nit] = ncpsolve(dfobj, lb, ub, x) x_simul[i, :, :] = x ss = g(s, x, epsilons, parms) if i < (horizon - 1): s_simul[i + 1, :, :] = ss if 'auxiliary' not in fun: # TODO: find a better test than this l = [s_simul, x_simul] varnames = model.symbols['states'] + model.symbols['controls'] else: aux = fun['auxiliary'] a_simul = aux(s_simul.reshape((n_exp * horizon, -1)), x_simul.reshape((n_exp * horizon, -1)), parms) a_simul = a_simul.reshape(horizon, n_exp, -1) l = [s_simul, x_simul, a_simul] varnames = model.symbols['states'] + model.symbols[ 'controls'] + model.symbols['auxiliaries'] simul = numpy.concatenate(l, axis=2) if discard: iA = -numpy.isnan(x_simul) valid = numpy.all(numpy.all(iA, axis=0), axis=1) simul = simul[:, valid, :] n_kept = s_simul.shape[1] if n_exp > n_kept: print('Discarded {}/{}'.format(n_exp - n_kept, n_exp)) if return_array: return simul if irf or (n_exp == 1): simul = simul[:, 0, :] ts = pandas.DataFrame(simul, columns=varnames) return ts else: panel = pandas.Panel(simul.swapaxes(0, 1), minor_axis=varnames) return panel
def time_iteration(model, verbose=False, initial_dr=None, pert_order=1, with_complementarities=True, grid={}, distribution={}, maxit=500, tol=1e-8, inner_maxit=10, 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 "dtcscc" model to be solved verbose : boolean if True, display iterations initial_dr : decision rule initial guess for the decision rule pert_order : {1} if no initial guess is supplied, the perturbation solution at order ``pert_order`` is used as initial guess with_complementarities : boolean (True) if False, complementarity conditions are ignored grid: grid options distribution: distribution 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 ''' def vprint(t): if verbose: print(t) f = model.functions['arbitrage'] g = model.functions['transition'] parms = model.calibration['parameters'] approx = model.get_grid(**grid) interp_type = approx.interpolation dr = create_interpolator(approx, interp_type) distrib = model.get_distribution(**distribution) nodes, weights = distrib.discretize() if initial_dr is None: if pert_order == 1: initial_dr = approximate_controls(model) if pert_order > 1: raise Exception("Perturbation order > 1 not supported (yet).") vprint('Starting time iteration') # TODO: transpose grid = dr.grid x_0 = initial_dr(grid) x_0 = x_0.real # just in case... # define objective function (residuals of arbitrage equations) def fun(x): return step_residual(grid, x, dr, f, g, parms, nodes, weights) ## t1 = time.time() err = 1 err_0 = 1 it = 0 verbit = True if verbose == 'full' else False if with_complementarities: lbfun = model.functions['controls_lb'] ubfun = model.functions['controls_ub'] lb = lbfun(grid, parms) ub = ubfun(grid, parms) else: lb = None ub = None if verbose: headline = '|{0:^4} | {1:10} | {2:8} | {3:8} | {4:3} |' headline = headline.format('N', ' Error', 'Gain', 'Time', 'nit') stars = '-'*len(headline) print(stars) print(headline) print(stars) # format string for within loop fmt_str = '|{0:4} | {1:10.3e} | {2:8.3f} | {3:8.3f} | {4:3} |' while err > tol and it < maxit: # update counters t_start = time.time() it += 1 # update interpolation coefficients (NOTE: filters through `fun`) dr.set_values(x_0) # Derivative of objective function sdfun = SerialDifferentiableFunction(fun) # Apply solver with current decision rule for controls if with_complementarities: [x, nit] = ncpsolve(sdfun, lb, ub, x_0, verbose=verbit, maxit=inner_maxit) else: [x, nit] = serial_newton(sdfun, x_0, verbose=verbit) # update error and print if `verbose` err = abs(x-x_0).max() err_SA = err/err_0 err_0 = err t_finish = time.time() elapsed = t_finish - t_start if verbose: print(fmt_str.format(it, err, err_SA, elapsed, nit)) # Update control vector x_0[:] = x # x0 = x0 + (x-x0) # call user supplied hook, if any if hook: hook(dr, it, err) # warn and bail if we get inf if False in np.isfinite(x_0): print('iteration {} failed : non finite value') return [x_0, x] if it == maxit: import warnings warnings.warn(UserWarning("Maximum number of iterations reached")) # compute final time and do final printout if `verbose` t2 = time.time() if verbose: print(stars) print('Elapsed: {} seconds.'.format(t2 - t1)) print(stars) return dr
def time_iteration(model, bounds=None, verbose=False, initial_dr=None, pert_order=1, with_complementarities=True, interp_type='smolyak', smolyak_order=3, interp_orders=None, maxit=500, tol=1e-8, integration='gauss-hermite', integration_orders=None, T=200, n_s=3, hook=None): """Finds a global solution for ``model`` using backward time-iteration. Parameters: ----------- model: NumericModel "fg" or "fga" model to be solved bounds: ndarray boundaries for approximations. First row contains minimum values. Second row contains maximum values. verbose: boolean if True, display iterations initial_dr: decision rule initial guess for the decision rule pert_order: {1} if no initial guess is supplied, the perturbation solution at order ``pert_order`` is used as initial guess with_complementarities: boolean (True) if False, complementarity conditions are ignored interp_type: {`smolyak`, `spline`} type of interpolation to use for future controls smolyak_orders: int parameter ``l`` for Smolyak interpolation interp_orders: 1d array-like list of integers specifying the number of nods in each dimension if ``interp_type="spline" `` Returns: -------- decision rule object (SmolyakGrid or MultivariateSplines) """ def vprint(t): if verbose: print(t) parms = model.calibration['parameters'] sigma = model.covariances if initial_dr == None: if pert_order == 1: from dolo.algos.perturbations import approximate_controls initial_dr = approximate_controls(model) if pert_order > 1: raise Exception("Perturbation order > 1 not supported (yet).") if interp_type == 'perturbations': return initial_dr if bounds is not None: pass elif model.options and 'approximation_space' in model.options: vprint('Using bounds specified by model') approx = model.options['approximation_space'] a = approx['a'] b = approx['b'] bounds = numpy.row_stack([a, b]) bounds = numpy.array(bounds, dtype=float) else: vprint('Using asymptotic bounds given by first order solution.') from dolo.numeric.timeseries import asymptotic_variance # this will work only if initial_dr is a Taylor expansion Q = asymptotic_variance(initial_dr.A.real, initial_dr.B.real, initial_dr.sigma, T=T) devs = numpy.sqrt(numpy.diag(Q)) bounds = numpy.row_stack([ initial_dr.S_bar - devs * n_s, initial_dr.S_bar + devs * n_s, ]) if interp_orders == None: interp_orders = [5] * bounds.shape[1] if interp_type == 'smolyak': from dolo.numeric.interpolation.smolyak import SmolyakGrid dr = SmolyakGrid(bounds[0, :], bounds[1, :], smolyak_order) elif interp_type == 'spline': from dolo.numeric.interpolation.splines import MultivariateSplines dr = MultivariateSplines(bounds[0, :], bounds[1, :], interp_orders) elif interp_type == 'multilinear': from dolo.numeric.interpolation.multilinear import MultilinearInterpolator dr = MultilinearInterpolator(bounds[0, :], bounds[1, :], interp_orders) if integration == 'optimal_quantization': from dolo.numeric.discretization import quantization_nodes # number of shocks [epsilons, weights] = quantization_nodes(N_e, sigma) elif integration == 'gauss-hermite': from dolo.numeric.discretization import gauss_hermite_nodes if not integration_orders: integration_orders = [3] * sigma.shape[0] [epsilons, weights] = gauss_hermite_nodes(integration_orders, sigma) vprint('Starting time iteration') # TODO: transpose grid = dr.grid xinit = initial_dr(grid) xinit = xinit.real # just in case... from dolo.algos.convert import get_fg_functions f, g = get_fg_functions(model) import time fun = lambda x: step_residual(grid, x, dr, f, g, parms, epsilons, weights) ## t1 = time.time() err = 1 x0 = xinit it = 0 verbit = True if verbose == 'full' else False if with_complementarities: lbfun = model.functions['arbitrage_lb'] ubfun = model.functions['arbitrage_ub'] lb = lbfun(grid, parms) ub = ubfun(grid, parms) else: lb = None ub = None 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) err_0 = 1 while err > tol and it < maxit: t_start = time.time() it += 1 dr.set_values(x0) from dolo.numeric.optimize.newton import serial_newton, SerialDifferentiableFunction from dolo.numeric.optimize.ncpsolve import ncpsolve sdfun = SerialDifferentiableFunction(fun) if with_complementarities: [x, nit] = ncpsolve(sdfun, lb, ub, x0, verbose=verbit) else: [x, nit] = serial_newton(sdfun, x0, verbose=verbit) err = abs(x - x0).max() err_SA = err / err_0 err_0 = err 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)) x0 = x0 + (x - x0) if hook: hook(dr, it, err) if False in np.isfinite(x0): print('iteration {} failed : non finite value') return [x0, x] if it == maxit: import warnings warnings.warn(UserWarning("Maximum number of iterations reached")) t2 = time.time() if verbose: print(stars) print('Elapsed: {} seconds.'.format(t2 - t1)) print(stars) return dr
def time_iteration(model, initial_guess=None, with_complementarities=True, verbose=True, orders=None, output_type='dr', maxit=1000, inner_maxit=10, tol=1e-6, hook=None): assert (model.model_type == 'dtmscc') def vprint(t): if verbose: print(t) [P, Q] = model.markov_chain n_ms = P.shape[0] # number of markov states n_mv = P.shape[1] # number of markov variables x0 = model.calibration['controls'] parms = model.calibration['parameters'] n_x = len(x0) n_s = len(model.symbols['states']) approx = model.options['approximation_space'] a = approx['a'] b = approx['b'] if orders is None: orders = approx['orders'] from dolo.numeric.decision_rules_markov import MarkovDecisionRule mdr = MarkovDecisionRule(n_ms, a, b, orders) grid = mdr.grid N = grid.shape[0] # if isinstance(initial_guess, numpy.ndarray): # print("Using initial guess (1)") # controls = initial_guess # elif isinstance(initial_guess, dict): # print("Using initial guess (2)") # controls_0 = initial_guess['controls'] # ap_space = initial_guess['approximation_space'] # if False in (approx['orders']==orders): # print("Interpolating initial guess") # old_dr = MarkovDecisionRule(controls_0.shape[0], ap_space['smin'], ap_space['smax'], ap_space['orders']) # old_dr.set_values(controls_0) # controls_0 = numpy.zeros( (n_ms, N, n_x) ) # for i in range(n_ms): # e = old_dr(i,grid) # controls_0[i,:,:] = e # else: # controls_0 = numpy.zeros((n_ms, N, n_x)) 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): m = P[i_m, :][None, :] 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 = P[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 # mdr.set_values(controls) 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(f, g, grid, x.reshape(sh_c), mdr, P, Q, parms ).reshape((-1, n_x)) dfn = SerialDifferentiableFunction(fn) 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 output_type == 'dr': return mdr elif output_type == 'controls': return controls_0 else: raise Exception("Unsupported ouput type {}.".format(output_type))
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)
def time_iteration(model, initial_guess=None, with_complementarities=True, verbose=True, grid={}, output_type='dr', 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 ''' assert (model.model_type == 'dtmscc') def vprint(t): if verbose: print(t) [P, Q] = model.markov_chain n_ms = P.shape[0] # number of markov states n_mv = P.shape[1] # number of markov variables x0 = model.calibration['controls'] parms = model.calibration['parameters'] n_x = len(x0) n_s = len(model.symbols['states']) approx = model.get_grid(**grid) a = approx.a b = approx.b orders = approx.orders interp_type = approx.interpolation # unused from dolo.numeric.decision_rules_markov import MarkovDecisionRule mdr = MarkovDecisionRule(n_ms, a, b, orders) grid = mdr.grid 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): m = P[i_m, :][None, :] 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 = P[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 # mdr.set_values(controls) 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(f, g, grid, x.reshape(sh_c), mdr, P, Q, parms ).reshape((-1, n_x)) dfn = SerialDifferentiableFunction(fn) 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 output_type == 'dr': return mdr elif output_type == 'controls': return controls_0 else: raise Exception("Unsupported ouput type {}.".format(output_type))
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} )