def det_residual(model, guess, start, final, shocks, diff=True, jactype='sparse'): ''' Computes the residuals, the derivatives of the stacked-time system. :param model: an fga model :param guess: the guess for the simulated values. An `(n_s.n_x) x N` array, where n_s is the number of states, n_x the number of controls, and `N` the length of the simulation. :param start: initial boundary condition (initial value of the states) :param final: final boundary condition (last value of the controls) :param shocks: values for the exogenous shocks :param diff: if True, the derivatives are computes :return: a list with two elements: - an `(n_s.n_x) x N` array with the residuals of the system - a `(n_s.n_x) x N x (n_s.n_x) x N` array representing the jacobian of the system ''' # TODO: compute a sparse derivative and ensure the solvers can deal with it n_s = len(model.symbols['states']) n_x = len(model.symbols['controls']) n_e = len(model.symbols['shocks']) N = guess.shape[0] p = model.calibration['parameters'] from dolo.algos.convert import get_fg_functions [f, g] = get_fg_functions(model) vec = guess[:-1, :] vec_f = guess[1:, :] s = vec[:, :n_s] x = vec[:, n_s:] S = vec_f[:, :n_s] X = vec_f[:, n_s:] e = shocks[:-1, :] E = shocks[1:, :] if diff: SS, SS_s, SS_x, SS_e = g(s, x, e, p, diff=True) R, R_s, R_x, R_e, R_S, R_X = f(s, x, E, S, X, p, diff=True) else: SS = g(s, x, e, p) R = f(s, x, E, S, X, p) res_s = SS - S res_x = R res = numpy.zeros((N, n_s + n_x)) res[1:, :n_s] = res_s res[:-1, n_s:] = res_x res[0, :n_s] = -(guess[0, :n_s] - start) res[-1, n_s:] = -(guess[-1, n_s:] - guess[-2, n_s:]) if not diff: return res else: sparse_jac = False if not sparse_jac: # we compute the derivative matrix from dolo.numeric.serial_operations import serial_multiplication as smult res_s_s = SS_s res_s_x = SS_x # next block is probably very inefficient jac = numpy.zeros((N, n_s + n_x, N, n_s + n_x)) for i in range(N - 1): jac[i, n_s:, i, :n_s] = R_s[i, :, :] jac[i, n_s:, i, n_s:] = R_x[i, :, :] jac[i, n_s:, i + 1, :n_s] = R_S[i, :, :] jac[i, n_s:, i + 1, n_s:] = R_X[i, :, :] jac[i + 1, :n_s, i, :n_s] = SS_s[i, :, :] jac[i + 1, :n_s, i, n_s:] = SS_x[i, :, :] jac[i + 1, :n_s, i + 1, :n_s] = -numpy.eye(n_s) # jac[i,n_s:,i,:n_s] = R_s[i,:,:] # jac[i,n_s:,i,n_s:] = R_x[i,:,:] # jac[i+1,n_s:,i,:n_s] = R_S[i,:,:] # jac[i+1,n_s:,i,n_s:] = R_X[i,:,:] # jac[i,:n_s,i+1,:n_s] = SS_s[i,:,:] # jac[i,:n_s,i+1,n_s:] = SS_x[i,:,:] # jac[i+1,:n_s,i+1,:n_s] = -numpy.eye(n_s) jac[0, :n_s, 0, :n_s] = -numpy.eye(n_s) jac[-1, n_s:, -1, n_s:] = -numpy.eye(n_x) jac[-1, n_s:, -2, n_s:] = +numpy.eye(n_x) nn = jac.shape[0] * jac.shape[1] res = res.ravel() jac = jac.reshape((nn, nn)) if jactype == 'sparse': from scipy.sparse import csc_matrix, csr_matrix jac = csc_matrix(jac) # scipy bug ? I don't get the same with csr return [res, jac]
def approximate_controls(model, verbose=False, steady_state=None, eigmax=1.0, solve_steady_state=False, order=1): """Compute first order approximation of optimal controls Parameters: ----------- model: NumericModel Model to be solved verbose: boolean If True: displays number of contracting eigenvalues steady_state: ndarray Use supplied steady-state value to compute the approximation. The routine doesn't check whether it is really a solution or not. solve_steady_state: boolean Use nonlinear solver to find the steady-state orders: {1} Approximation order. (Currently, only first order is supported). Returns: -------- TaylorExpansion: Decision Rule for the optimal controls around the steady-state. """ if order>1: raise Exception("Not implemented.") # get steady_state import numpy # if model.model_type == 'fga': # model = GModel_fg_from_fga(model) # g = model.functions['transition'] # f = model.functions['arbitrage'] from dolo.algos.convert import get_fg_functions [f,g] = get_fg_functions(model) if steady_state is not None: calib = steady_state else: calib = model.calibration if solve_steady_state: from dolo.algos.steady_state import find_deterministic_equilibrium calib = find_deterministic_equilibrium(model) p = calib['parameters'] s = calib['states'] x = calib['controls'] e = calib['shocks'] if model.covariances is not None: sigma = model.covariances else: sigma = numpy.zeros((len(e), len(e))) from numpy.linalg import solve l = g(s,x,e,p, diff=True) [junk, g_s, g_x, g_e] = l[:4] # [el[0,...] for el in l[:4]] l = f(s,x,e,s,x,p, diff=True) [res, f_s, f_x, f_e, f_S, f_X] = l #[el[0,...] for el in l[:6]] n_s = g_s.shape[0] # number of controls n_x = g_x.shape[1] # number of states n_e = g_e.shape[1] n_v = n_s + n_x A = row_stack([ column_stack( [ eye(n_s), zeros((n_s,n_x)) ] ), column_stack( [ -f_S , -f_X ] ) ]) B = row_stack([ column_stack( [ g_s, g_x ] ), column_stack( [ f_s, f_x ] ) ]) from dolo.numeric.extern.qz import qzordered [S,T,Q,Z,eigval] = qzordered(A,B,n_s) Q = Q.real # is it really necessary ? Z = Z.real diag_S = numpy.diag(S) diag_T = numpy.diag(T) # Check Blanchard=Kahn conditions n_big_one = sum(eigval>eigmax) n_expected = n_x if verbose: print( "There are {} eigenvalues greater than {}. Expected: {}.".format( n_big_one, eigmax, n_x ) ) if n_expected != n_big_one: raise BlanchardKahnError(n_big_one, n_expected) tol_geneigvals = 1e-10 try: assert( sum( (abs( diag_S ) < tol_geneigvals) * (abs(diag_T) < tol_geneigvals) ) == 0) except Exception as e: print e print(numpy.column_stack([diag_S, diag_T])) # raise GeneralizedEigenvaluesError(diag_S, diag_T) Z11 = Z[:n_s,:n_s] Z12 = Z[:n_s,n_s:] Z21 = Z[n_s:,:n_s] Z22 = Z[n_s:,n_s:] S11 = S[:n_s,:n_s] T11 = T[:n_s,:n_s] # first order solution C = solve(Z11.T, Z21.T).T P = np.dot(solve(S11.T, Z11.T).T , solve(Z11.T, T11.T).T ) Q = g_e s = s.ravel() x = x.ravel() A = g_s + dot( g_x, C ) B = g_e dr = CDR([s, x, C]) dr.A = A dr.B = B dr.sigma = sigma return dr
def find_deterministic_equilibrium(model, constraints=None, return_jacobian=False): ''' Finds the steady state calibration. Parameters ---------- model: NumericModel an "fg" model. constraints: dict a dictionaries with forced values. Use it to set shocks to non-zero values or to add additional constraints to avoid unit roots. Returns: -------- dict: calibration dictionary ''' from dolo.algos.convert import get_fg_functions [f,g] = get_fg_functions(model) s0 = model.calibration['states'] x0 = model.calibration['controls'] p = model.calibration['parameters'] if 'shocks' in model.calibration: e0 = model.calibration['shocks'].copy() else: e0 = numpy.zeros( len(model.symbols['shocks']) ) n_e = len(e0) z = numpy.concatenate([s0, x0, e0]) symbs = model.symbols['states'] + model.symbols['controls'] addcons_ind = [] addcons_val = [] if constraints is None: constraints = dict() for k in constraints: if k in symbs: i = symbs.index(k) addcons_ind.append(i) addcons_val.append(constraints[k]) elif k in model.symbols['shocks']: i = model.symbols['shocks'].index(k) e0[i] = constraints[k] else: raise Exception("Invalid symbol '{}' for steady_state constraint".format(k)) def fobj(z): s = z[:len(s0)] x = z[len(s0):-n_e] e = z[-n_e:] S = g(s,x,e,p) r = f(s,x,e,s,x,p) d_e = e - e0 d_sx = z[addcons_ind] - addcons_val res = numpy.concatenate([S-s, r, d_e, d_sx ]) return res from dolo.numeric.misc import MyJacobian jac = MyJacobian(fobj)( z ) if return_jacobian: return jac rank = numpy.linalg.matrix_rank(jac) if rank < len(z): import warnings warnings.warn("There are {} equilibrium variables to find, but the jacobian matrix is only of rank {}. The solution is indeterminate.".format(len(z),rank)) from scipy.optimize import root sol = root(fobj, z, method='lm') steady_state = sol.x s = steady_state[:len(s0)] x = steady_state[len(s0):-n_e] e = steady_state[-n_e:] calib = OrderedDict( states = s, controls = x, shocks = e, parameters = p.copy() ) if 'auxiliary' in model.functions: a = model.functions['auxiliary'](s,x,p) calib['auxiliaries'] = a return calib
def find_deterministic_equilibrium(model, constraints=None, return_jacobian=False): ''' Finds the steady state calibration. Parameters ---------- model: NumericModel an "fg" model. constraints: dict a dictionaries with forced values. Use it to set shocks to non-zero values or to add additional constraints to avoid unit roots. Returns: -------- dict: calibration dictionary ''' from dolo.algos.convert import get_fg_functions [f, g] = get_fg_functions(model) s0 = model.calibration['states'] x0 = model.calibration['controls'] p = model.calibration['parameters'] if 'shocks' in model.calibration: e0 = model.calibration['shocks'].copy() else: e0 = numpy.zeros(len(model.symbols['shocks'])) n_e = len(e0) z = numpy.concatenate([s0, x0, e0]) symbs = model.symbols['states'] + model.symbols['controls'] addcons_ind = [] addcons_val = [] if constraints is None: constraints = dict() for k in constraints: if k in symbs: i = symbs.index(k) addcons_ind.append(i) addcons_val.append(constraints[k]) elif k in model.symbols['shocks']: i = model.symbols['shocks'].index(k) e0[i] = constraints[k] else: raise Exception( "Invalid symbol '{}' for steady_state constraint".format(k)) def fobj(z): s = z[:len(s0)] x = z[len(s0):-n_e] e = z[-n_e:] S = g(s, x, e, p) r = f(s, x, e, s, x, p) d_e = e - e0 d_sx = z[addcons_ind] - addcons_val res = numpy.concatenate([S - s, r, d_e, d_sx]) return res from dolo.numeric.misc import MyJacobian jac = MyJacobian(fobj)(z) if return_jacobian: return jac rank = numpy.linalg.matrix_rank(jac) if rank < len(z): import warnings warnings.warn( "There are {} equilibrium variables to find, but the jacobian matrix is only of rank {}. The solution is indeterminate." .format(len(z), rank)) from scipy.optimize import root sol = root(fobj, z, method='lm') steady_state = sol.x s = steady_state[:len(s0)] x = steady_state[len(s0):-n_e] e = steady_state[-n_e:] calib = OrderedDict(states=s, controls=x, shocks=e, parameters=p.copy()) if 'auxiliary' in model.functions: a = model.functions['auxiliary'](s, x, p) calib['auxiliaries'] = a return calib
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 simulate(model, dr, s0=None, n_exp=0, horizon=40, seed=1, discard=False, solve_expectations=False, nodes=None, weights=None, forcing_shocks=None): '''Simulate a model using the specified decision rule. Parameters --------- model: NumericModel an "fg" or "fga" 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``) 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'] ) sigma = model.covariances 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 if model.model_type == 'fga': from dolo.algos.convert import get_fg_functions [f,g] = get_fg_functions(model) else: 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['arbitrage_lb'] ubfun = model.functions['arbitrage_ub'] lb = lbfun(s, parms) ub = ubfun(s, parms) from dolo.numeric.optimize.newton import newton as newton_solver, SerialDifferentiableFunction from dolo.numeric.optimize.ncpsolve import ncpsolve fobj = lambda t: step_residual(s, t, dr, f, g, parms, nodes, weights) dfobj = SerialDifferentiableFunction(fobj) # [x,nit] = newton_solver(dfobj, x) [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 from numpy import isnan,all if not 'auxiliary' 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 = -isnan(x_simul) valid = all( 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 irf or (n_exp==1): simul = simul[:,0,:] import pandas ts = pandas.DataFrame(simul, columns=varnames) return ts return simul
def omega(model, dr, n_exp=10000, orders=None, bounds=None, n_draws=100, seed=0, horizon=50, s0=None, solve_expectations=False, time_discount=None): assert(model.model_type =='fga') [f,g] = get_fg_functions(model) sigma = model.covariances parms = model.calibration['parameters'] mean = numpy.zeros(sigma.shape[0]) numpy.random.seed(seed) epsilons = numpy.random.multivariate_normal(mean, sigma, n_draws) weights = np.ones(epsilons.shape[0])/n_draws if bounds is None: approx = model.options['approximation_space'] a = approx['a'] b = approx['b'] bounds = numpy.row_stack([a,b]) else: a,b =numpy.row_stack(bounds) if orders is None: orders = [100]*len(a) domain = RectangularDomain(a, b, orders) grid = domain.grid n_s = len(model.symbols['states']) errors = test_residuals( grid, dr, f, g, parms, epsilons, weights ) errors = abs(errors) if s0 is None: s0 = model.calibration['states'] from dolo.algos.simulations import simulate simul = simulate(model, dr, s0, n_exp=n_exp, horizon=horizon+1, discard=True, solve_expectations=solve_expectations) s_simul = simul[:,:,:n_s] densities = [domain.compute_density(s_simul[t,:,:]) for t in range(horizon)] ergo_dens = densities[-1] max_error = numpy.max(errors,axis=0) # maximum errors ergo_error = numpy.dot(ergo_dens,errors) # weighted by ergodic distribution d = dict( errors = errors, densities = densities, bounds = bounds, max_errors = max_error, ergodic = ergo_error, domain = domain ) if time_discount is not None: beta = time_discount time_weighted_errors = max_error*0 for i in range(horizon): err = numpy.dot(densities[i], errors) time_weighted_errors += beta**i * err time_weighted_errors /= (1-beta**(horizon-1))/(1-beta) d['time_weighted'] = time_weighted_errors return EulerErrors(d)
def det_residual(model, guess, start, final, shocks, diff=True, jactype='sparse'): ''' Computes the residuals, the derivatives of the stacked-time system. :param model: an fga model :param guess: the guess for the simulated values. An `(n_s.n_x) x N` array, where n_s is the number of states, n_x the number of controls, and `N` the length of the simulation. :param start: initial boundary condition (initial value of the states) :param final: final boundary condition (last value of the controls) :param shocks: values for the exogenous shocks :param diff: if True, the derivatives are computes :return: a list with two elements: - an `(n_s.n_x) x N` array with the residuals of the system - a `(n_s.n_x) x N x (n_s.n_x) x N` array representing the jacobian of the system ''' # TODO: compute a sparse derivative and ensure the solvers can deal with it n_s = len( model.symbols['states'] ) n_x = len( model.symbols['controls'] ) n_e = len( model.symbols['shocks'] ) N = guess.shape[0] p = model.calibration['parameters'] from dolo.algos.convert import get_fg_functions [f,g] = get_fg_functions(model) vec = guess[:-1,:] vec_f = guess[1:,:] s = vec[:,:n_s] x = vec[:,n_s:] S = vec_f[:,:n_s] X = vec_f[:,n_s:] e = shocks[:-1,:] E = shocks[1:,:] if diff: SS, SS_s, SS_x, SS_e = g(s,x,e,p, diff=True) R, R_s, R_x, R_e, R_S, R_X = f(s,x,E,S,X,p,diff=True) else: SS = g(s,x,e,p) R = f(s,x,E,S,X,p) res_s = SS - S res_x = R res = numpy.zeros( (N, n_s+n_x) ) res[1:,:n_s] = res_s res[:-1,n_s:] = res_x res[0,:n_s] = - (guess[0,:n_s] - start) res[-1,n_s:] = - (guess[-1,n_s:] - guess[-2,n_s:] ) if not diff: return res else: sparse_jac=False if not sparse_jac: # we compute the derivative matrix from dolo.numeric.serial_operations import serial_multiplication as smult res_s_s = SS_s res_s_x = SS_x # next block is probably very inefficient jac = numpy.zeros( (N, n_s+n_x, N, n_s+n_x) ) for i in range(N-1): jac[i,n_s:,i,:n_s] = R_s[i,:,:] jac[i,n_s:,i,n_s:] = R_x[i,:,:] jac[i,n_s:,i+1,:n_s] = R_S[i,:,:] jac[i,n_s:,i+1,n_s:] = R_X[i,:,:] jac[i+1,:n_s,i,:n_s] = SS_s[i,:,:] jac[i+1,:n_s,i,n_s:] = SS_x[i,:,:] jac[i+1,:n_s,i+1,:n_s] = -numpy.eye(n_s) # jac[i,n_s:,i,:n_s] = R_s[i,:,:] # jac[i,n_s:,i,n_s:] = R_x[i,:,:] # jac[i+1,n_s:,i,:n_s] = R_S[i,:,:] # jac[i+1,n_s:,i,n_s:] = R_X[i,:,:] # jac[i,:n_s,i+1,:n_s] = SS_s[i,:,:] # jac[i,:n_s,i+1,n_s:] = SS_x[i,:,:] # jac[i+1,:n_s,i+1,:n_s] = -numpy.eye(n_s) jac[ 0,:n_s,0,:n_s] = - numpy.eye(n_s) jac[-1,n_s:,-1,n_s:] = - numpy.eye(n_x) jac[-1,n_s:,-2,n_s:] = + numpy.eye(n_x) nn = jac.shape[0]*jac.shape[1] res = res.ravel() jac = jac.reshape((nn,nn)) if jactype == 'sparse': from scipy.sparse import csc_matrix, csr_matrix jac = csc_matrix(jac) # scipy bug ? I don't get the same with csr return [res,jac]
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