示例#1
0
def expm(A, use_exact_onenorm="auto", verbose=False):
    from scipy.linalg import expm as expm_scipy
    return expm_scipy(A)
    # Core of expm, separated to allow testing exact and approximate
    # algorithms.
    # Hardcode a matrix order threshold for exact vs. estimated one-norms.
    use_exact_onenorm = A.shape[0] < 200
    h = _ExpmPadeHelper(A, use_exact_onenorm=use_exact_onenorm)
    # Use Pade order 13.
    eta_3 = max(h.d6_tight, h.d8_loose)
    eta_4 = max(h.d8_loose, h.d10_loose)
    eta_5 = min(eta_3, eta_4)
    theta_13 = 4.25
    # Choose smallest s>=0 such that 2**(-s) eta_5 <= theta_13
    if eta_5 == 0:
        # Nilpotent special case
        s = 0
    else:
        s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0)
    s = s + _ell(2**-s * h.A, 13)
    U, V = h.pade13_scaled(s)
    X = _solve_P_Q(U, V)
    # X = r_13(A)^(2^s) by repeated squaring.
    for i in range(s):
        X = X.dot(X)
    return X
示例#2
0
def _expm_SS(A, ssA, order):  # , use_exact_onenorm='auto'):
    # Track functions of A to help compute the matrix exponential.
    h = _ExpmPadeHelper_SS(A, ssA, order)
    structure = None

    # Try Pade order 3.
    eta_1 = max(h.d4_loose, h.d6_loose)
    if eta_1 < 1.495585217958292e-002 and mf._ell(h.A, 3) == 0:
        U, V = h.pade3()
        return mf._solve_P_Q(U, V, structure=structure)

    # Try Pade order 5.
    eta_2 = max(h.d4_tight, h.d6_loose)
    if eta_2 < 2.539398330063230e-001 and mf._ell(h.A, 5) == 0:
        U, V = h.pade5()
        return mf._solve_P_Q(U, V, structure=structure)

    # Try Pade orders 7 and 9.
    eta_3 = max(h.d6_tight, h.d8_loose)
    if eta_3 < 9.504178996162932e-001 and mf._ell(h.A, 7) == 0:
        U, V = h.pade7()
        return mf._solve_P_Q(U, V, structure=structure)
    if eta_3 < 2.097847961257068e000 and mf._ell(h.A, 9) == 0:
        U, V = h.pade9()
        return mf._solve_P_Q(U, V, structure=structure)

    # Use Pade order 13.
    eta_4 = max(h.d8_loose, h.d10_loose)
    eta_5 = min(eta_3, eta_4)
    theta_13 = 4.25
    s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0)
    s = s + mf._ell(2 ** -s * h.A, 13)
    U, V = h.pade13_scaled(s)
    X = mf._solve_P_Q(U, V, structure=structure)
    # X = r_13(A)^(2^s) by repeated squaring.
    for _ in range(s):
        X = X.dot(X)
    return X
示例#3
0
 def Return(U, V, P, Q, geti2, pade):
     E = mf._solve_P_Q(U, V, structure=structure)
     I = _solve_P_Q_2(P, Q, structure=structure)
     if geti2:
         return E, I, _geti2(H, E, I, h, pade)
     return E, I
示例#4
0
def expmint(A, h, geti2=False):
    """
    Compute the matrix exponential and its integral(s) using Pade
    approximation.

    Parameters
    ----------
    A : (M, M) array_like or sparse matrix
        2D Array or Matrix (sparse or dense) to be exponentiated
    h : scalar
        Time step
    geti2 : bool
        If True, also return the `I2` integral below. Useful for
        first order holds.

    Returns
    -------
    E : (M, M) ndarray
        Matrix exponential of `A*h`: exp(A*h)
    I : (M, M) ndarray
        Integral of exp(A*t) dt from 0 to h
    I2 : (M, M) ndarray; optional
        Integral of exp(A*t)*t dt from 0 to h. Only returned if
        `geti2` is True.

    Notes
    -----
    This routine is modeled after and augments
    :func:`scipy.linalg.expm`. The power series expansions for these
    matrices are (I = identity):

    .. code-block:: none

      E = I + A*h + (A*h)**2/2! + (A*h)**3/3! + ...
      I1 = h*(I + A*h/2 + (A*h)**2/3! + (A*h)**3/4! + ...)
      I2 = h*h*(I/2 + A*h/3 + (A*h)**2/(4*2!) + (A*h)**3/(5*3!) + ...)

    If `A` is non-singular, the exact solutions for `I1` and `I2` are::

        E = exp(A*h)
        I1 = inv(A)*(E-I)
        I2 = inv(A)*(E*h-I1)

    The Pade approximants for those power series are used for `E` and
    `I1`. If necessary, the 'squaring and scaling' method will be used
    such that a 13th order Pade approximation will be accurate. See
    references [#exp1]_, [#exp2]_, and [#exp3]_ for more information.

    For `I2`, a Pade approximation is used if a 3rd, 5th, 7th or 9th
    order is accurate. If not, `A` is checked for singularity. If
    non-singular, the exact solution show above is used. If A is
    singular, a the power series is used directly until it converges
    (and a warning message is printed about using a finer time step.)

    References
    ----------
    .. [#exp1] Awad H. Al-Mohy and Nicholas J. Higham (2009)
           "A New Scaling and Squaring Algorithm for the Matrix
           Exponential."
           SIAM Journal on Matrix Analysis and Applications.
           31 (3). pp. 970-989. ISSN 1095-7162

    .. [#exp2] Nicholas J. Higham (2005)
           "The Scaling and Squaring Method for the Matrix Exponential
           Revisited."
           SIAM Journal on Matrix Analysis and Applications.
           Vol 26, No. 4, pp 1179-1193.

    .. [#exp3] David Westreich (1990)
           "A Practical Method for Computing the Exponential of a Matrix
           and its Integral."
           Communications in Applied Numerical Methods, Vol 6, 375-380.

    Examples
    --------
    >>> from pyyeti import expmint
    >>> import numpy as np
    >>> import scipy.linalg as la
    >>> np.set_printoptions(4)
    >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    >>> e, i, i2 = expmint.expmint(a, .05, True)
    >>> e
    array([[ 1.0996,  0.1599,  0.2202],
           [ 0.3099,  1.3849,  0.46  ],
           [ 0.5202,  0.61  ,  1.6998]])
    >>> i
    array([[ 0.052 ,  0.0034,  0.0048],
           [ 0.0067,  0.0583,  0.01  ],
           [ 0.0114,  0.0133,  0.0651]])
    >>> i2
    array([[ 0.0013,  0.0001,  0.0002],
           [ 0.0002,  0.0015,  0.0003],
           [ 0.0004,  0.0005,  0.0018]])
    """
    # Avoid indiscriminate asarray() to allow sparse or other strange
    # arrays.
    if isinstance(A, (list, tuple)):
        A = np.asarray(A)
    if len(A.shape) != 2 or A.shape[0] != A.shape[1]:
        raise ValueError("expected a square matrix")

    # Detect upper triangularity.
    if mf._is_upper_triangular(A):
        structure = mf.UPPER_TRIANGULAR
    else:
        structure = None

    # Hardcode a matrix order threshold for exact vs. estimated
    # one-norms.
    use_exact_onenorm = A.shape[0] < 200

    # Track functions of A to help compute the matrix exponential.
    H = _ExpmIntPadeHelper(
        A * h, structure=structure, use_exact_onenorm=use_exact_onenorm
    )

    def Return(U, V, P, Q, geti2, pade):
        E = mf._solve_P_Q(U, V, structure=structure)
        I = _solve_P_Q_2(P, Q, structure=structure)
        if geti2:
            return E, I, _geti2(H, E, I, h, pade)
        return E, I

    # Try Pade order 3.
    eta_1 = max(H.d4_loose, H.d6_loose)
    if eta_1 < 1.495585217958292e-002 and mf._ell(H.A, 3) == 0:
        U, V, P, Q = H.pade3_i(h)
        return Return(U, V, P, Q, geti2, 3)

    # Try Pade order 5.
    eta_2 = max(H.d4_tight, H.d6_loose)
    if eta_2 < 2.539398330063230e-001 and mf._ell(H.A, 5) == 0:
        U, V, P, Q = H.pade5_i(h)
        return Return(U, V, P, Q, geti2, 5)

    # Try Pade orders 7 and 9.
    eta_3 = max(H.d6_tight, H.d8_loose)
    if eta_3 < 9.504178996162932e-001 and mf._ell(H.A, 7) == 0:
        U, V, P, Q = H.pade7_i(h)
        return Return(U, V, P, Q, geti2, 7)

    if eta_3 < 2.097847961257068e000 and mf._ell(H.A, 9) == 0:
        U, V, P, Q = H.pade9_i(h)
        return Return(U, V, P, Q, geti2, 9)

    # Use Pade order 13.
    eta_4 = max(H.d8_loose, H.d10_loose)
    eta_5 = min(eta_3, eta_4)
    theta_13 = 4.25
    s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0)
    s = s + mf._ell(2 ** -s * H.A, 13)
    U, V, P, Q = H.pade13_scaled_i(s, h)
    E = mf._solve_P_Q(U, V, structure=structure)
    I = _solve_P_Q_2(P, Q, structure=structure)
    # E = r_13(A)^(2^s) by repeated squaring.
    for _ in range(s):
        I += I.dot(E)
        E = E.dot(E)
    if geti2:
        return E, I, _geti2(H, E, I, h, 13)
    return E, I
示例#5
0
    def expm(self, A, max_mat_mult=100, balance=True):
        ''' max_mat_mult can be used to reduce the computational complexity of the exponential 
            6: at most a Pade order 13 can be used but with no scaling
            5: at most a Pade order 9 is used
            4: at most a Pade order 7 is used
            3: at most a Pade order 5 is used
            2: at most a Pade order 3 is used
        '''
        if (np.any(np.isnan(A))):
            print("Matrix A contains nan")

        # Compute expm(A)*v
        if balance:
            A_bal, D = matrix_balance(A, permute=False)
            Dinv = np.copy(D)
            for i in range(D.shape[0]):
                Dinv[i, i] = 1.0 / D[i, i]
#            assert(np.max(np.abs(A_bal-(Dinv@A@D)))==0.0)
        else:
            A_bal = A

        # Hardcode a matrix order threshold for exact vs. estimated one-norms.
        use_exact_onenorm = A.shape[0] < 200
        h = _ExpmPadeHelper(A_bal, use_exact_onenorm=use_exact_onenorm)
        structure = None

        # Compute the number of mat-mat multiplications needed in theory
        self.mat_mult_in_theory = self.compute_mat_mult(A_bal)
        self.mat_mult = min(self.mat_mult_in_theory, max_mat_mult)
        self.mat_norm = np.linalg.norm(A_bal, 1)

        if self.mat_mult <= 0:
            U, V = self.pade1(A_bal)
            X = _solve_P_Q(U, V, structure=structure)

        if self.mat_mult == 1:
            U, V = self.pade2(A_bal)
            X = _solve_P_Q(U, V, structure=structure)

        if self.mat_mult == 2:
            U, V = h.pade3()
            #            U_, V_ = self.pade3(A_bal)
            #            assert(np.max(np.abs(U-U_))==0.0)
            #            assert(np.max(np.abs(V-V_))==0.0)
            X = _solve_P_Q(U, V, structure=structure)

        # Try Pade order 5.
        if self.mat_mult == 3:
            U, V = h.pade5()
            X = _solve_P_Q(U, V, structure=structure)

        # Try Pade orders 7 and 9.
        if self.mat_mult == 4:
            U, V = h.pade7()
            X = _solve_P_Q(U, V, structure=structure)

        if self.mat_mult == 5:
            U, V = h.pade9()
            X = _solve_P_Q(U, V, structure=structure)

        if self.mat_mult > 5:
            s = self.mat_mult - 6
            U, V = h.pade13_scaled(s)
            X = _solve_P_Q(U, V)
            # X = r_13(A)^(2^s) by repeated squaring.
            for i in range(s):
                X = X.dot(X)

        if (balance):
            X = D @ X @ Dinv
#        assert(np.max(np.abs(expm(A) - X)) == 0.0)
        return X
示例#6
0
def expm(A, A2=A2):
    """
    Compute the matrix exponential using Pade approximation.

    .. versionadded:: 0.12.0

    Parameters
    ----------
    A : (M,M) array or sparse matrix
        2D Array or Matrix (sparse or dense) to be exponentiated

    Returns
    -------
    expA : (M,M) ndarray
        Matrix exponential of `A`

    Notes
    -----
    This is algorithm (6.1) which is a simplification of algorithm (5.1).

    References
    ----------
    .. [1] Awad H. Al-Mohy and Nicholas J. Higham (2009)
           "A New Scaling and Squaring Algorithm for the Matrix Exponential."
           SIAM Journal on Matrix Analysis and Applications.
           31 (3). pp. 970-989. ISSN 1095-7162

    """

    # Use Pade order 3, no matter what.
    multiply(A, A, out=A2)
    U, V = _pade3(A, ident, A2)
    return _solve_P_Q(U, V, structure=None)

    # Detect upper triangularity.
    #    structure = UPPER_TRIANGULAR if _is_upper_triangular(A) else None
    structure = None

    # Define the identity matrix depending on sparsity.
    #    ident = _ident_like(A)

    # Try Pade order 3.
    A2 = A.dot(A)
    d6 = _onenormest_matrix_power(A2, 3)**(1 / 6.)
    eta_1 = max(_onenormest_matrix_power(A2, 2)**(1 / 4.), d6)
    if eta_1 < 1.495585217958292e-002 and _ell(A, 3) == 0:
        U, V = _pade3(A, ident, A2)
        return _solve_P_Q(U, V, structure=structure)

    # Try Pade order 5.
    A4 = A2.dot(A2)
    d4 = _exact_1_norm(A4)**(1 / 4.)
    eta_2 = max(d4, d6)
    if eta_2 < 2.539398330063230e-001 and _ell(A, 5) == 0:
        U, V = _pade5(A, ident, A2, A4)
        return _solve_P_Q(U, V, structure=structure)

    # Try Pade orders 7 and 9.
    A6 = A2.dot(A4)
    d6 = _exact_1_norm(A6)**(1 / 6.)
    d8 = _onenormest_matrix_power(A4, 2)**(1 / 8.)
    eta_3 = max(d6, d8)
    if eta_3 < 9.504178996162932e-001 and _ell(A, 7) == 0:
        U, V = _pade7(A, ident, A2, A4, A6)
        return _solve_P_Q(U, V, structure=structure)
    if eta_3 < 2.097847961257068e+000 and _ell(A, 9) == 0:
        U, V = _pade9(A, ident, A2, A4, A6)
        return _solve_P_Q(U, V, structure=structure)

    # Use Pade order 13.
    d10 = _onenormest_product((A4, A6))**(1 / 10.)
    eta_4 = max(d8, d10)
    eta_5 = min(eta_3, eta_4)
    theta_13 = 4.25
    s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0)
    s = s + _ell(2**-s * A, 13)
    B = A * 2**-s
    B2 = A2 * 2**(-2 * s)
    B4 = A4 * 2**(-4 * s)
    B6 = A6 * 2**(-6 * s)
    U, V = _pade13(B, ident, B2, B4, B6)
    X = _solve_P_Q(U, V, structure=structure)
    if structure == UPPER_TRIANGULAR:
        # Invoke Code Fragment 2.1.
        X = _fragment_2_1(X, A, s)
    else:
        # X = r_13(A)^(2^s) by repeated squaring.
        for i in range(s):
            X = X.dot(X)
    return X
示例#7
0
def expm(A,A2 = A2):
    """
    Compute the matrix exponential using Pade approximation.

    .. versionadded:: 0.12.0

    Parameters
    ----------
    A : (M,M) array or sparse matrix
        2D Array or Matrix (sparse or dense) to be exponentiated

    Returns
    -------
    expA : (M,M) ndarray
        Matrix exponential of `A`

    Notes
    -----
    This is algorithm (6.1) which is a simplification of algorithm (5.1).

    References
    ----------
    .. [1] Awad H. Al-Mohy and Nicholas J. Higham (2009)
           "A New Scaling and Squaring Algorithm for the Matrix Exponential."
           SIAM Journal on Matrix Analysis and Applications.
           31 (3). pp. 970-989. ISSN 1095-7162

    """
 
    # Use Pade order 3, no matter what.
    multiply(A,A,out=A2)
    U, V = _pade3(A, ident, A2)
    return _solve_P_Q(U, V, structure=None)    
    
    # Detect upper triangularity.
#    structure = UPPER_TRIANGULAR if _is_upper_triangular(A) else None
    structure = None

    # Define the identity matrix depending on sparsity.
#    ident = _ident_like(A)

    # Try Pade order 3.
    A2 = A.dot(A)
    d6 = _onenormest_matrix_power(A2, 3)**(1/6.)
    eta_1 = max(_onenormest_matrix_power(A2, 2)**(1/4.), d6)
    if  eta_1 < 1.495585217958292e-002 and _ell(A, 3) == 0:
        U, V = _pade3(A, ident, A2)
        return _solve_P_Q(U, V, structure=structure)

    # Try Pade order 5.
    A4 = A2.dot(A2)
    d4 = _exact_1_norm(A4)**(1/4.)
    eta_2 = max(d4, d6)
    if eta_2 < 2.539398330063230e-001 and _ell(A, 5) == 0:
        U, V = _pade5(A, ident, A2, A4)
        return _solve_P_Q(U, V, structure=structure)

    # Try Pade orders 7 and 9.
    A6 = A2.dot(A4)
    d6 = _exact_1_norm(A6)**(1/6.)
    d8 = _onenormest_matrix_power(A4, 2)**(1/8.)
    eta_3 = max(d6, d8)
    if eta_3 < 9.504178996162932e-001 and _ell(A, 7) == 0:
        U, V = _pade7(A, ident, A2, A4, A6)
        return _solve_P_Q(U, V, structure=structure)
    if eta_3 < 2.097847961257068e+000 and _ell(A, 9) == 0:
        U, V = _pade9(A, ident, A2, A4, A6)
        return _solve_P_Q(U, V, structure=structure)

    # Use Pade order 13.
    d10 = _onenormest_product((A4, A6))**(1/10.)
    eta_4 = max(d8, d10)
    eta_5 = min(eta_3, eta_4)
    theta_13 = 4.25
    s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0)
    s = s + _ell(2**-s * A, 13)
    B = A * 2**-s
    B2 = A2 * 2**(-2*s)
    B4 = A4 * 2**(-4*s)
    B6 = A6 * 2**(-6*s)
    U, V = _pade13(B, ident, B2, B4, B6)
    X = _solve_P_Q(U, V, structure=structure)
    if structure == UPPER_TRIANGULAR:
        # Invoke Code Fragment 2.1.
        X = _fragment_2_1(X, A, s)
    else:
        # X = r_13(A)^(2^s) by repeated squaring.
        for i in range(s):
            X = X.dot(X)
    return X
示例#8
0
def expm_times_v(A, v, scaling_reduction=0, verbose=False):
    ''' scaling_reduction can be used to reduce the computational complexity of the exponential 
        1: no scaling can be performed
        2: at most a Pade order 9 is used
        3: at most a Pade order 7 is used
        4: at most a Pade order 5 is used
        5: at most a Pade order 3 is used
    '''
    # Compute expm(A)*v
    # Hardcode a matrix order threshold for exact vs. estimated one-norms.
    use_exact_onenorm = A.shape[0] < 200
    h = _ExpmPadeHelper(A, use_exact_onenorm=use_exact_onenorm)
    structure = None

    # Try Pade order 3.
    eta_1 = max(h.d4_loose, h.d6_loose)
    if scaling_reduction > 4 or (eta_1 < 1.495585217958292e-002
                                 and _ell(h.A, 3) == 0):
        U, V = h.pade3()
        X = _solve_P_Q(U, V, structure=structure)
        return X.dot(v)

    # Try Pade order 5.
    eta_2 = max(h.d4_tight, h.d6_loose)
    if scaling_reduction > 3 or (eta_2 < 2.539398330063230e-001
                                 and _ell(h.A, 5) == 0):
        U, V = h.pade5()
        X = _solve_P_Q(U, V, structure=structure)
        return X.dot(v)

    # Try Pade orders 7 and 9.
    eta_3 = max(h.d6_tight, h.d8_loose)
    if scaling_reduction > 2 or (eta_3 < 9.504178996162932e-001
                                 and _ell(h.A, 7) == 0):
        U, V = h.pade7()
        X = _solve_P_Q(U, V, structure=structure)
        return X.dot(v)

    if scaling_reduction > 1 or (eta_3 < 2.097847961257068e+000
                                 and _ell(h.A, 9) == 0):
        U, V = h.pade9()
        X = _solve_P_Q(U, V, structure=structure)
        return X.dot(v)

    # Use Pade order 13.
    eta_3 = max(h.d6_tight, h.d8_loose)
    eta_4 = max(h.d8_loose, h.d10_loose)
    eta_5 = min(eta_3, eta_4)
    theta_13 = 4.25
    # Choose smallest s>=0 such that 2**(-s) eta_5 <= theta_13
    if eta_5 == 0:
        # Nilpotent special case
        s = 0
    else:
        s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0)
    s = s + _ell(2**-s * h.A, 13)

    if (scaling_reduction > 0):
        s = 0

    U, V = h.pade13_scaled(s)
    X = _solve_P_Q(U, V)
    # X = r_13(A)^(2^s) by repeated squaring.
    for i in range(s):
        X = X.dot(X)
    res = X.dot(v)
    return res