def errors_trapezoidal(operator, solution, step_sizes): """Compute approximation errors of the trapezoidal rule Parameters ---------- operator: instance of TT class TT operator of the differential equation solution: list of instances of TT class approximate solution of the linear differential equation step_sizes: list of floats step sizes for the application of the implicit Euler method Returns ------- errors: list of floats approximation errors """ # define errors errors = [] # compute relative approximation errors for i in range(len(solution) - 1): errors.append(((tt.eye(operator.row_dims) - 0.5 * step_sizes[i] * operator).dot(solution[i + 1]) - (tt.eye(operator.row_dims) + 0.5 * step_sizes[i] * operator).dot(solution[i])).norm() / ((tt.eye(operator.row_dims) + 0.5 * step_sizes[i] * operator).dot(solution[i])).norm()) return errors
def sod(operator, initial_value, step_sizes, threshold=1e-12, max_rank=50, normalize=2, progress=True): """Second order differencing (time-symmetrized explicit Euler) for linear differential equations in the TT format Parameters ---------- operator: instance of TT class TT operator of the differential equation initial_value: instance of TT class initial value of the differential equation step_sizes: list of floats step sizes threshold: float, optional threshold for reduced SVD decompositions, default is 1e-12 max_rank: int, optional maximum rank of the solution, default is 50 normalize: int (0, 1, or 2) no normalization if 0, otherwise the solution is normalized in terms of Manhattan or Euclidean norm in each step progress: bool, optional whether to show the progress of the algorithm or not, default is True Returns ------- solution: list of instances of the TT class numerical solution of the differential equation """ # return current time start_time = utl.progress('Running time-symmetrized explicit Euler method', 0, show=progress) # initialize solution solution = [initial_value] # begin loop over time steps # -------------------------- for i in range(len(step_sizes)): if i == 0: # initialize: one expl. Euler backwards in time solution_prev = (tt.eye(operator.row_dims) - step_sizes[i]*operator).dot(solution[0]) else: solution_prev = solution[i-1].copy() # compute next time step from current and previous time step tt_tmp = solution_prev + 2*step_sizes[i]*operator.dot(solution[i]) # truncate ranks of the solution tt_tmp = tt_tmp.ortho(threshold=threshold, max_rank=max_rank) # normalize solution if normalize > 0: tt_tmp = (1 / tt_tmp.norm(p=normalize)) * tt_tmp # append solution solution.append(tt_tmp.copy()) # print progress utl.progress('Running time-symmetrized explicit Euler method', 100 * (i + 1) / len(step_sizes), show=progress, cpu_time=_time.time() - start_time) return solution
def power_method(operator, initial_guess, operator_gevp=None, repeats=10, sigma=0.999): """Inverse power iteration method Approximates eigenvalues and corresponding eigentensors of an (generalized) eigenvalue problem in the TT format. For details, see [1]_. Parameters ---------- operator: instance of TT class TT operator, left-hand side initial_guess: instance of TT class initial guess for the solution operator_gevp: instance of TT class, optional TT operator, right-hand side (for generalized eigenvalue problems), default is None repeats: int, optional number of iterations, default is 10 sigma: float, optional find eigenvalues near sigma, default is 1 Returns ------- eigenvalue: float approximated eigenvalue eigentensor: instance of TT class approximated eigentensors References ---------- ..[1] S. Klus, C. Schütte, "Towards tensor-based methods for the numerical approximation of the Perron-Frobenius and Koopman operator", Journal of Computational Dynamics 3 (2), 2016 """ # define shift operator if operator_gevp is None: operator_shift = operator - sigma * tt.eye(operator.row_dims) else: operator_shift = operator - sigma * operator_gevp # define eigenvalue and eigentensor eigenvalue = 0 eigentensor = initial_guess # start iteration for i in range(repeats): # solve system of linear equations in the TT format if operator_gevp is None: eigentensor = sle.als(operator_shift, eigentensor, eigentensor) else: eigentensor = sle.als(operator_shift, eigentensor, operator_gevp.dot(eigentensor)) # normalize eigentensor eigentensor *= (1 / eigentensor.norm()) # compute eigenvalue eigenvalue = (eigentensor.transpose().dot(operator).dot(eigentensor)) if operator_gevp is not None: eigenvalue *= 1 / (eigentensor.transpose().dot(operator_gevp).dot(eigentensor)) return eigenvalue, eigentensor
def errors_expl_euler(operator, solution, step_sizes): """ Compute approximation errors of the explicit Euler method. Parameters ---------- operator : TT TT operator of the differential equation solution : list[TT] approximate solution of the linear differential equation step_sizes : list[float] step sizes for the application of the implicit Euler method Returns ------- list[float] approximation errors """ # define errors errors = [] # compute relative approximation errors for i in range(len(solution) - 1): errors.append( (solution[i + 1] - (tt.eye(operator.row_dims) + step_sizes[i] * operator).dot( solution[i])).norm() / solution[i].norm()) return errors
def setUpClass(cls): super(TestEVP, cls).setUpClass() # set tolerance for the error of the eigenvalues cls.tol_eigval = 5e-3 # set tolerance for the error of the eigenvectors cls.tol_eigvec = 5e-2 # set number of eigenvalues to compute cls.number_ev = 3 # load data directory = os.path.dirname(os.path.dirname( os.path.realpath(__file__))) transitions = np.load( directory + '/examples/data/triple_well_transitions.npz')["transitions"] # coarse-grain data transitions = np.int64(np.ceil(np.true_divide(transitions, 2))) # generate operators in TT format cls.operator_tt = ulam.ulam_2d(transitions, [25, 25], 2000) cls.operator_gevp = tt.eye(cls.operator_tt.row_dims) # matricize TT operator cls.operator_mat = cls.operator_tt.matricize() # define initial tensor train for solving the eigenvalue problem cls.initial_tt = tt.ones(cls.operator_tt.row_dims, [1] * cls.operator_tt.order, ranks=15).ortho_right()
def explicit_euler(operator, initial_value, step_sizes, threshold=1e-12, max_rank=50, normalize=1, progress=True): """Explicit Euler method for linear differential equations in the TT format Parameters ---------- operator: instance of TT class TT operator of the differential equation initial_value: instance of TT class initial value of the differential equation step_sizes: list of floats step sizes for the application of the implicit Euler method threshold: float, optional threshold for reduced SVD decompositions, default is 1e-12 max_rank: int, optional maximum rank of the solution, default is 50 normalize: int (0, 1, or 2) no normalization if 0, otherwise the solution is normalized in terms of Manhattan or Euclidean norm in each step progress: bool, optional whether to show the progress of the algorithm or not, default is True Returns ------- solution: list of instances of the TT class numerical solution of the differential equation """ # return current time start_time = utl.progress('Running explicit Euler method', 0, show=progress) # define solution solution = [initial_value] # begin explicit Euler method # --------------------------- for i in range(len(step_sizes)): # compute next time step tt_tmp = (tt.eye(operator.row_dims) + step_sizes[i] * operator).dot(solution[i]) # truncate ranks of the solution tt_tmp = tt_tmp.ortho(threshold=threshold, max_rank=max_rank) # normalize solution if normalize > 0: tt_tmp = (1 / tt_tmp.norm(p=normalize)) * tt_tmp # append solution solution.append(tt_tmp.copy()) # print progress utl.progress('Running explicit Euler method', 100 * (i + 1) / len(step_sizes), show=progress, cpu_time=_time.time() - start_time) return solution
def test_eye(self): """test identity tensor train""" # construct identity tensor train, convert to full format, and flatten t_eye = tt.eye(self.row_dims).full().flatten() # construct identity matrix and flatten t_full = np.eye(np.int(np.prod(self.row_dims))).flatten() # compute relative error rel_err = np.linalg.norm(t_eye - t_full) / np.linalg.norm(t_full) # check if relative error is smaller than tolerance self.assertLess(rel_err, self.tol)
def adaptive_step_size(operator, initial_value, initial_guess, time_end, step_size_first=1e-10, repeats=1, solver='solve', error_tol=1e-1, closeness_tol=0.5, step_size_min=1e-14, step_size_max=10, closeness_min=1e-3, factor_max=2, factor_safe=0.9, second_method='two_step_Euler', normalize=1, progress=True): """Adaptive step size method Parameters ---------- operator: instance of TT class TT operator of the differential equation initial_value: instance of TT class initial value of the differential equation initial_guess: instance of TT class initial guess for the first step time_end: float time point to which the ODE should be integrated step_size_first: float, optional first time step, default is 1e-10 repeats: int, optional number of repeats of the ALS in each iteration step, default is 1 solver: string, optional algorithm for obtaining the solutions of the micro systems, can be 'solve' or 'lu', default is 'solve' error_tol: float, optional tolerance for relative local error, default is 1e-1 closeness_tol: float, optional tolerance for relative change in the closeness to the stationary distribution, default is 0.5 step_size_min: float, optional minimum step size, default is 1e-14 step_size_max: float, optional maximum step size, default is 10 closeness_min: float, optional minimum closeness value, default is 1e-3 factor_max: float, optional maximum factor for step size adaption, default is 2 factor_safe: float, optional safety factor for step size adaption, default is 0.9 second_method: string, optional which higher-order method should be used, can be 'two_step_Euler' or 'trapezoidal_rule', default is 'two_step_Euler' normalize: int (0, 1, or 2) no normalization if 0, otherwise the solution is normalized in terms of Manhattan or Euclidean norm in each step progress: bool, optional whether to show the progress of the algorithm or not, default is True Returns ------- solution: list of instances of the TT class numerical solution of the differential equation """ # return current time start_time = utl.progress('Running adaptive step size method', 0, show=progress) # define solution solution = [initial_value] # define variable for integration t_2 = [] # set closeness variables closeness_pre = (operator.dot(initial_value)).norm() # define tensor train for solving the systems of linear equations t_tmp = initial_guess # set time and step size time = 0 time_steps = [0] step_size = step_size_first # begin integration # ----------------- while (time < time_end) and (closeness_pre > closeness_min) and (step_size > step_size_min): # first method t_1 = sle.als(tt.eye(operator.row_dims) - step_size * operator, t_tmp.copy(), solution[-1], solver=solver, repeats=repeats) t_1 = (1 / t_1.norm(p=1)) * t_1 # second method if second_method == 'two_step_Euler': t_2 = sle.als(tt.eye(operator.row_dims) - 0.5 * step_size * operator, t_tmp.copy(), solution[-1], solver=solver, repeats=repeats) t_2 = sle.als(tt.eye(operator.row_dims) - 0.5 * step_size * operator, t_2.copy(), solution[-1], solver=solver, repeats=repeats) if second_method == 'trapezoidal_rule': t_2 = sle.als(tt.eye(operator.row_dims) - 0.5 * step_size * operator, t_tmp.copy(), (tt.eye(operator.row_dims) + 0.5 * step_size * operator).dot(solution[-1]), solver=solver, repeats=repeats) # normalize solution if normalize > 0: t_2 = (1 / t_2.norm(p=normalize)) * t_2 # compute closeness to staionary distribution closeness = (operator.dot(t_1)).norm() # compute relative local error and closeness change local_error = (t_1 - t_2).norm() / t_1.norm() closeness_difference = (closeness - closeness_pre) / closeness_pre # compute factors for step size adaption factor_local = error_tol / local_error factor_closeness = closeness_tol / np.abs(closeness_difference) # compute new step size step_size_new = np.amin([factor_max, factor_safe * factor_local, factor_safe * factor_closeness]) * step_size # accept or reject step if (factor_local > 1) and (factor_closeness > 1): time = np.min([time + step_size, time_end]) step_size = np.amin([step_size_new, time_end - time, step_size_max]) solution.append(t_1.copy()) time_steps.append(time) t_tmp = t_1 utl.progress('Running adaptive step size method', 100 * time / time_end, show=progress, cpu_time=_time.time() - start_time) closeness_pre = closeness else: step_size = step_size_new return solution, time_steps
def trapezoidal_rule(operator, initial_value, initial_guess, step_sizes, repeats=1, tt_solver='als', threshold=1e-12, max_rank=np.infty, micro_solver='solve', normalize=1, progress=True): """Trapezoidal rule for linear differential equations in the TT format Parameters ---------- operator: instance of TT class TT operator of the differential equation initial_value: instance of TT class initial value of the differential equation initial_guess: instance of TT class initial guess for the first step step_sizes: list of floats step sizes for the application of the trapezoidal rule repeats: int, optional number of repeats of the (M)ALS in each iteration step, default is 1 tt_solver: string, optional algorithm for solving the systems of linear equations in the TT format, default is 'als' threshold: float, optional threshold for reduced SVD decompositions, default is 1e-12 max_rank: int, optional maximum rank of the solution, default is infinity micro_solver: string, optional algorithm for obtaining the solutions of the micro systems, can be 'solve' or 'lu', default is 'solve' normalize: int (0, 1, or 2) no normalization if 0, otherwise the solution is normalized in terms of Manhattan or Euclidean norm in each step progress: bool, optional whether to show the progress of the algorithm or not, default is True Returns ------- solution: list of instances of the TT class numerical solution of the differential equation """ # return current time start_time = utl.progress('Running trapezoidal rule', 0, show=progress) # define solution solution = [initial_value] # define temporary tensor train tt_tmp = initial_guess # begin trapezoidal rule # ---------------------- for i in range(len(step_sizes)): # solve system of linear equations for current time step if tt_solver == 'als': tt_tmp = sle.als(tt.eye(operator.row_dims) - 0.5 * step_sizes[i] * operator, tt_tmp, (tt.eye(operator.row_dims) + 0.5 * step_sizes[i] * operator).dot(solution[i]), solver=micro_solver, repeats=repeats) if tt_solver == 'mals': tt_tmp = sle.mals(tt.eye(operator.row_dims) - 0.5 * step_sizes[i] * operator, tt_tmp, (tt.eye(operator.row_dims) + 0.5 * step_sizes[i] * operator).dot(solution[i]), solver=micro_solver, repeats=repeats, threshold=threshold, max_rank=max_rank) # normalize solution if normalize > 0: tt_tmp = (1 / tt_tmp.norm(p=normalize)) * tt_tmp # append solution solution.append(tt_tmp.copy()) # print progress utl.progress('Running trapezoidal rule', 100 * (i + 1) / len(step_sizes), show=progress, cpu_time=_time.time() - start_time) return solution
print('----------------------------------------------------------') print('p_CO in atm Method TT ranks Closeness CPU time') print('----------------------------------------------------------') # construct eigenvalue problems to find the stationary distributions # ------------------------------------------------------------------ for i in range(8): # construct and solve eigenvalue problem for current CO pressure operator = mdl.co_oxidation( 20, 10**(8 + p_CO_exp[i])).ortho_left().ortho_right() initial_guess = tt.ones(operator.row_dims, [1] * operator.order, ranks=R[i]).ortho_left().ortho_right() with utl.timer() as time: eigenvalues, solution = evp.als(tt.eye(operator.row_dims) + operator, initial_guess, repeats=10, solver='eigs') solution = (1 / solution.norm(p=1)) * solution # compute turn-over frequency of CO2 desorption tof.append(utl.two_cell_tof(solution, [2, 1], 1.7e5)) # print results string_p_CO = '10^' + (p_CO_exp[i] >= 0) * '+' + str("%.1f" % p_CO_exp[i]) string_method = ' ' * 9 + 'EVP' + ' ' * 9 string_rank = (R[i] < 10) * ' ' + str(R[i]) + ' ' * 8 string_closeness = str("%.2e" % (operator @ solution).norm()) + ' ' * 6 string_time = (time.elapsed < 10) * ' ' + str("%.2f" % time.elapsed) + 's' print(string_p_CO + string_method + string_rank + string_closeness +
def symmetric_euler(operator, initial_value, step_sizes, previous_value=None, threshold=1e-12, max_rank=50, normalize=1, progress=True): """ Time-symmetrized explicit Euler ('second order differencing' in quantum mechanics) for linear differential equations in the TT format, see [1]_. Parameters ---------- operator : TT TT operator of the differential equation initial_value : TT initial value of the differential equation step_sizes : list[float] step sizes previous_value: TT, optional, default is None previous step for symmetric Euler; if not given one explicit Euler step is computed backwards in time threshold : float, optional threshold for reduced SVD decompositions, default is 1e-12 max_rank : int, optional maximum rank of the solution, default is 50 normalize : {0, 1, 2}, optional no normalization if 0, otherwise the solution is normalized in terms of Manhattan or Euclidean norm in each step progress : bool, optional whether to show the progress of the algorithm or not, default is True Returns ------- list[TT] numerical solution of the differential equation References ---------- .. [1] A. Askar, A. S. Cakmak, "Explicit integration method for the time-dependent Schrodinger equation for collision problems", J. Chem. Phys. 68, 2794, 1978 """ # return current time start_time = utl.progress('Running time-symmetrized explicit Euler method', 0, show=progress) # initialize solution solution = [initial_value] # begin loop over time steps # -------------------------- for i in range(len(step_sizes)): if i == 0: # initialize: one expl. Euler backwards in time if previous step is not given if previous_value == None: solution_prev = (tt.eye(operator.row_dims) - step_sizes[0] * operator).dot(solution[0]) else: solution_prev = previous_value # normalize if normalize > 0: solution_prev = ( 1 / solution_prev.norm(p=normalize)) * solution_prev else: solution_prev = solution[i - 1].copy() # compute next time step from current and previous time step tt_tmp = solution_prev + 2 * step_sizes[i] * operator.dot(solution[i]) # truncate ranks of the solution tt_tmp = tt_tmp.ortho(threshold=threshold, max_rank=max_rank) # normalize solution if normalize > 0: tt_tmp = (1 / tt_tmp.norm(p=normalize)) * tt_tmp # append solution solution.append(tt_tmp.copy()) # print progress utl.progress('Running time-symmetrized explicit Euler method', 100 * (i + 1) / len(step_sizes), show=progress, cpu_time=_time.time() - start_time) return solution
def implicit_euler(operator, initial_value, initial_guess, step_sizes, repeats=1, tt_solver='als', threshold=1e-12, max_rank=np.infty, micro_solver='solve', progress=True): """Implicit Euler method for linear differential equations in the TT format Parameters ---------- operator: instance of TT class TT operator of the differential equation initial_value: instance of TT class initial value of the differential equation initial_guess: instance of TT class initial guess for the first step step_sizes: list of floats step sizes for the application of the implicit Euler method repeats: int, optional number of repeats of the (M)ALS in each iteration step, default is 1 tt_solver: string, optional algorithm for solving the systems of linear equations in the TT format, default is 'als' threshold: float, optional threshold for reduced SVD decompositions, default is 1e-12 max_rank: int, optional maximum rank of the solution, default is infinity micro_solver: string, optional algorithm for obtaining the solutions of the micro systems, can be 'solve' or 'lu', default is 'solve' progress: bool, optional whether to show the progress of the algorithm or not, default is True Returns ------- solution: list of instances of the TT class numerical solution of the differential equation """ # define solution solution = [initial_value] # define temporary tensor train tt_tmp = initial_guess # begin implicit Euler method # --------------------------- for i in range(len(step_sizes)): # solve system of linear equations for current time step if tt_solver == 'als': tt_tmp = sle.als(tt.eye(operator.row_dims) - step_sizes[i] * operator, tt_tmp, solution[i], solver=micro_solver, repeats=repeats) if tt_solver == 'mals': tt_tmp = sle.mals(tt.eye(operator.row_dims) - step_sizes[i] * operator, tt_tmp, solution[i], solver=micro_solver, threshold=threshold, repeats=repeats, max_rank=max_rank) # normalize solution tt_tmp = (1 / tt_tmp.norm(p=1)) * tt_tmp # append solution solution.append(tt_tmp.copy()) # print progress if progress is True: utl.progress('Running implicit Euler method', 100 * i / (len(step_sizes) - 1)) return solution
def adaptive_step_size(operator, initial_value, initial_guess, time_end, step_size_first=1e-10, repeats=1, solver='solve', error_tol=1e-1, closeness_tol=0.5, step_size_min=1e-14, step_size_max=10, closeness_min=1e-3, factor_max=2, factor_safe=0.9, second_method='two_step_Euler'): """Adaptive step size method Parameters ---------- operator: instance of TT class TT operator of the differential equation initial_value: instance of TT class initial value of the differential equation initial_guess: instance of TT class initial guess for the first step time_end: float time point to which the ODE should be integrated step_size_first: float, optional first time step, default is 1e-10 repeats: int, optional number of repeats of the ALS in each iteration step, default is 1 solver: string, optional algorithm for obtaining the solutions of the micro systems, can be 'solve' or 'lu', default is 'solve' error_tol: float, optional tolerance for relative local error, default is 1e-1 closeness_tol: float, optional tolerance for relative change in the closeness to the stationary distribution, default is 0.5 step_size_min: float, optional minimum step size, default is 1e-14 step_size_max: float, optional maximum step size, default is 10 closeness_min: float, optional minimum closeness value, default is 1e-3 factor_max: float, optional maximum factor for step size adaption, default is 2 factor_safe: float, optional safety factor for step size adaption, default is 0.9 second_method: string, optional which higher-order method should be used, can be 'two_step_Euler' or 'trapezoidal_rule', default is 'two_step_Euler' Returns ------- solution: list of instances of the TT class numerical solution of the differential equation """ # define solution solution = [initial_value] # define variable for integration t_2 = [] # set closeness variables closeness_pre = (operator @ initial_value).norm() # define tensor train for solving the systems of linear equations t_tmp = initial_guess # set time and step size time = 0 step_size = step_size_first # begin integration # ----------------- while (time < time_end) and (closeness_pre > closeness_min) and ( step_size > step_size_min): # first method t_1 = sle.als(tt.eye(operator.row_dims) - step_size * operator, t_tmp.copy(), solution[-1], solver=solver, repeats=repeats) t_1 = (1 / t_1.norm(p=1)) * t_1 # second method if second_method == 'two_step_Euler': t_2 = sle.als(tt.eye(operator.row_dims) - 0.5 * step_size * operator, t_tmp.copy(), solution[-1], solver=solver, repeats=repeats) t_2 = sle.als(tt.eye(operator.row_dims) - 0.5 * step_size * operator, t_2.copy(), solution[-1], solver=solver, repeats=repeats) if second_method == 'trapezoidal_rule': t_2 = sle.als( tt.eye(operator.row_dims) - 0.5 * step_size * operator, t_tmp.copy(), (tt.eye(operator.row_dims) + 0.5 * step_size * operator) @ solution[-1], solver=solver, repeats=repeats) t_2 = (1 / t_2.norm(p=1)) * t_2 # compute closeness to staionary distribution closeness = (operator @ t_1).norm() # compute relative local error and closeness change local_error = (t_1 - t_2).norm() / t_1.norm() closeness_difference = (closeness - closeness_pre) / closeness_pre # compute factors for step size adaption factor_local = error_tol / local_error factor_closeness = closeness_tol / np.abs(closeness_difference) # compute new step size step_size_new = np.amin([ factor_max, factor_safe * factor_local, factor_safe * factor_closeness ]) * step_size # accept or reject step if (factor_local > 1) and (factor_closeness > 1): time = time + step_size step_size = np.amin( [step_size_new, time_end - time, step_size_max]) solution.append(t_1.copy()) t_tmp = t_1 print('tau: ' + str("%.2e" % step_size) + ', closeness: ' + str("%.2e" % closeness)) closeness_pre = closeness else: step_size = step_size_new return solution