示例#1
0
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
示例#2
0
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
示例#3
0
文件: evp.py 项目: kiwikuma/scikit_tt
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
示例#4
0
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
示例#5
0
    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()
示例#6
0
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
示例#7
0
    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)
示例#8
0
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
示例#9
0
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
示例#10
0
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 +
示例#11
0
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
示例#12
0
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
示例#13
0
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