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 test_als(self): """test for ALS""" # solve system of linear equations in matrix format solution_mat = np.linalg.solve(self.operator_mat, self.rhs_mat) # solve system of linear equations in TT format solution_tt_solve = sle.als(self.operator_tt, self.initial_tt, self.rhs_tt, repeats=1) solution_tt_lu = sle.als(self.operator_tt, self.initial_tt, self.rhs_tt, repeats=1, solver='lu') # compute relative error between exact and approximate solution rel_err_mat_solve = np.linalg.norm( solution_mat - solution_tt_solve.matricize()) / np.linalg.norm(solution_mat) rel_err_mat_lu = np.linalg.norm( solution_mat - solution_tt_lu.matricize()) / np.linalg.norm(solution_mat) # compute relative error of the system on linear equations in TT format rel_err_tt_solve = (self.operator_tt.dot(solution_tt_solve) - self.rhs_tt).norm() / self.rhs_tt.norm() rel_err_tt_lu = (self.operator_tt.dot(solution_tt_lu) - self.rhs_tt).norm() / self.rhs_tt.norm() # check if relative errors are smaller than tolerance self.assertLess(rel_err_mat_solve, self.tol) self.assertLess(rel_err_mat_lu, self.tol) self.assertLess(rel_err_tt_solve, self.tol) self.assertLess(rel_err_tt_lu, 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
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