def als(operator, initial_guess, previous=[], shift=0, operator_gevp=None, number_ev=1, repeats=1, conv_eps=1e-8, solver='eig', sigma=1, real=True): """ Alternating linear scheme. Approximates eigenvalues and corresponding eigentensors of an (generalized) eigenvalue problem in the TT format. For details, see [1]_. Parameters ---------- operator : TT TT operator, left-hand side initial_guess : TT initial guess for the solution previous : list of TT, optional list of known eigentensors whose eigenvalues should be shifted shift : float, optional shift parameter for known eigenpairs operator_gevp : TT, optional TT operator, right-hand side (for generalized eigenvalue problems), default is None number_ev : int, optional number of eigenvalues and corresponding eigentensor to compute, default is 1 repeats : int, optional number of repeats of the ALS, default is 1 conv_eps : float, optional threshold for convergence of the eigenvalue, default is 0 solver : string, optional algorithm for obtaining the solutions of the micro systems, can be 'eig', 'eigs' or 'eigh', default is 'eig' sigma : float, optional find eigenvalues near sigma, default is 1 real : bool, optional whether to compute only real eigenvalues and eigentensors or not, default is True Returns ------- eigenvalues: float or list[float] approximated eigenvalues, if number_ev>1 eigenvalues is a list[float] eigentensors: TT or list[TT] approximated eigentensors, if number_ev>1 eigentensors is a list of tensor trains References ---------- ..[1] S. Holtz, T. Rohwedder, R. Schneider, "The Alternating Linear Scheme for Tensor Optimization in the Tensor Train Format", SIAM Journal on Scientific Computing 34 (2), 2012 """ # define tensor trains trains = Object() trains.operator = operator trains.operator_gevp = operator_gevp trains.solution = initial_guess.copy() trains.previous = previous # define stacks stacks = Object() stacks.op_left = [None] * operator.order stacks.op_right = [None] * operator.order stacks.op_gevp_left = [None] * operator.order stacks.op_gevp_right = [None] * operator.order stacks.previous_left = [[None] * operator.order for _ in range(len(previous))] stacks.previous_right = [[None] * operator.order for _ in range(len(previous))] # construct right stacks for the left-hand side for i in range(operator.order - 1, -1, -1): __construct_right_stacks(i, trains, stacks) # define iteration number current_iteration = 1 # initialize variables for convergence detection eigenvalues_pre = np.array([np.infty] * number_ev)[None, :] conv_tf = False # begin ALS while current_iteration <= repeats and not conv_tf: # first half sweep for i in range(operator.order): # update left stack for the left-hand side __construct_left_stacks(i, trains, stacks) if i < operator.order - 1: # construct micro system micro_op, micro_op_gevp = __construct_micro_matrices( i, trains, stacks, shift) # update solution eigenvalues = __update_core(i, micro_op, micro_op_gevp, number_ev, trains.solution, solver, sigma, real, 'forward') # second half sweep for i in range(operator.order - 1, -1, -1): # update right stack for the left-hand side __construct_right_stacks(i, trains, stacks) # construct micro system micro_op, micro_op_gevp = __construct_micro_matrices( i, trains, stacks, shift) # update solution eigenvalues = __update_core(i, micro_op, micro_op_gevp, number_ev, trains.solution, solver, sigma, real, 'backward') # increase iteration number current_iteration += 1 # check for convergence last = eigenvalues_pre[-np.amin([3, eigenvalues_pre.shape[0]]):, :] # last_rel_diff = np.abs(np.dot(last - eigenvalues, np.diag(np.reciprocal(eigenvalues)))) last_diff = np.abs(last - eigenvalues) if np.amax(last_diff) < conv_eps: conv_tf = True eigenvalues_pre = np.vstack((eigenvalues_pre, eigenvalues)) # define form of the final solution depending on the number of eigenvalues to compute if number_ev == 1: eigentensors = TT([trains.solution.cores[0][:, :, :, :, 0]] + trains.solution.cores[1:]) eigenvalues = eigenvalues[0] else: eigentensors = [] for i in range(number_ev): eigentensors.append( TT([trains.solution.cores[0][:, :, :, :, i]] + trains.solution.cores[1:])) return eigenvalues, eigentensors
def als(operator, initial_guess, operator_gevp=None, number_ev=1, repeats=1, solver='eig', sigma=1, real=True): """Alternating linear scheme 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 number_ev: int, optional number of eigenvalues and corresponding eigentensor to compute, default is 1 repeats: int, optional number of repeats of the ALS, default is 1 solver: string, optional algorithm for obtaining the solutions of the micro systems, can be 'eig', 'eigs' or 'eigh', default is 'eig' sigma: float, optional find eigenvalues near sigma, default is 1 real: bool, optional whether to compute only real eigenvalues and eigentensors or not, default is True Returns ------- eigenvalues: float or list of floats approximated eigenvalues, if number_ev>1 eigenvalues is a list of floats eigentensors: instance of TT class or list of instances of TT class approximated eigentensors, if number_ev>1 eigentensors is a list of tensor trains References ---------- ..[1] S. Holtz, T. Rohwedder, R. Schneider, "The Alternating Linear Scheme for Tensor Optimization in the Tensor Train Format", SIAM Journal on Scientific Computing 34 (2), 2012 """ # define solution tensor and eigenvalues solution = initial_guess.copy() eigenvalues = None # define stacks stack_left_op = [None] * operator.order stack_right_op = [None] * operator.order stack_left_op_gevp = [None] * operator.order stack_right_op_gevp = [None] * operator.order # define micro operator for generalized eigenvalue problems micro_op_gevp = None # construct right stacks for the left-hand side for i in range(operator.order - 1, -1, -1): __construct_stack_right_op(i, stack_right_op, operator, solution) # construct right stacks for the right-hand side if operator_gevp is not None: for i in range(operator.order - 1, -1, -1): __construct_stack_right_op(i, stack_right_op_gevp, operator_gevp, solution) # define iteration number current_iteration = 1 # begin ALS while current_iteration <= repeats: # first half sweep for i in range(operator.order): # update left stack for the left-hand side __construct_stack_left_op(i, stack_left_op, operator, solution) # update left stack for the right-hand side if operator_gevp is not None: __construct_stack_left_op(i, stack_left_op_gevp, operator_gevp, solution) if i < operator.order - 1: # construct micro system micro_op = __construct_micro_matrix_als(i, stack_left_op, stack_right_op, operator, solution) if operator_gevp is not None: micro_op_gevp = __construct_micro_matrix_als(i, stack_left_op_gevp, stack_right_op_gevp, operator_gevp, solution) # update solution eigenvalues = __update_core_als(i, micro_op, micro_op_gevp, number_ev, solution, solver, sigma, real, 'forward') # second half sweep for i in range(operator.order - 1, -1, -1): # update right stack for the left-hand side __construct_stack_right_op(i, stack_right_op, operator, solution) # update right stack for the right-hand side if operator_gevp is not None: __construct_stack_right_op(i, stack_right_op_gevp, operator_gevp, solution) # construct micro system micro_op = __construct_micro_matrix_als(i, stack_left_op, stack_right_op, operator, solution) if operator_gevp is not None: micro_op_gevp = __construct_micro_matrix_als(i, stack_left_op_gevp, stack_right_op_gevp, operator_gevp, solution) # update solution eigenvalues = __update_core_als(i, micro_op, micro_op_gevp, number_ev, solution, solver, sigma, real, 'backward') # increase iteration number current_iteration += 1 # define form of the final solution depending on the number of eigenvalues to compute if number_ev == 1: eigentensors = TT([solution.cores[0][:, :, :, :, 0]] + solution.cores[1:]) eigenvalues = eigenvalues[0] else: eigentensors = [] for i in range(number_ev): eigentensors.append(TT([solution.cores[0][:, :, :, :, i]] + solution.cores[1:])) return eigenvalues, eigentensors