mailr24907 - /trunk/lib/dispersion/matrix_exponential.py


Others Months | Index by Date | Thread Index
>>   [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Header


Content

Posted by tlinnet on August 01, 2014 - 18:09:
Author: tlinnet
Date: Fri Aug  1 18:09:32 2014
New Revision: 24907

URL: http://svn.gna.org/viewcvs/relax?rev=24907&view=rev
Log:
Made new general stride helper function and matrix_exponential function.

Modified:
    trunk/lib/dispersion/matrix_exponential.py

Modified: trunk/lib/dispersion/matrix_exponential.py
URL: 
http://svn.gna.org/viewcvs/relax/trunk/lib/dispersion/matrix_exponential.py?rev=24907&r1=24906&r2=24907&view=diff
==============================================================================
--- trunk/lib/dispersion/matrix_exponential.py  (original)
+++ trunk/lib/dispersion/matrix_exponential.py  Fri Aug  1 18:09:32 2014
@@ -23,7 +23,7 @@
 """Module for the calculation of the matrix exponential, for higher 
dimensional data."""
 
 # Python module imports.
-from numpy import array, any, dot, einsum, eye, exp, iscomplex, int16, 
newaxis, multiply, tile, sqrt, version, zeros
+from numpy import array, any, complex128, dot, einsum, eye, exp, iscomplex, 
int16, newaxis, multiply, tile, sqrt, version, zeros
 from numpy.lib.stride_tricks import as_strided
 from numpy.linalg import eig, inv
 
@@ -153,6 +153,196 @@
     return data_view
 
 
+def data_view_via_striding_array_row_col(data_array=None):
+    """Method to stride through the data matrix, extracting the outer matrix 
with nr of elements as Row X Column length.  Row and Col should have same 
size.
+
+    @keyword data:  The numpy data array to stride through.
+    @type data:     numpy array of rank [NE][NS][NM][NO][ND][Row][Col] or 
[NS][NM][NO][ND][Row][Col].
+    @return:        The data view of the full numpy array, returned as a 
numpy array with number of small numpy arrays corresponding to 
Nr_mat=NE*NS*NM*NO*ND or Nr_mat=NS*NM*NO*ND, where each small array has size 
Col.
+    @rtype:         numpy array of rank [NE*NS*NM*NO*ND][Col] or 
[NS*NM*NO*ND][Col].
+    """
+
+    # Get the expected shape of the higher dimensional column numpy array.
+    if len(data_array.shape) == 7:
+        # Extract shapes from data.
+        NE, NS, NM, NO, ND, Row, Col = data_array.shape
+
+    else:
+        # Extract shapes from data.
+        NS, NM, NO, ND, Ros, Col = data_array.shape
+
+        # Set NE to 1.
+        NE = 1
+
+    # Calculate how many small matrices.
+    Nr_mat = NE * NS * NM * NO * ND
+
+    # Define the shape for the stride view.
+    shape = (Nr_mat, Row, Col)
+
+    # Get itemsize, Length of one array element in bytes. Depends on dtype. 
float64=8, complex128=16.
+    itz = data_array.itemsize
+
+    # Bytes_between_elements
+    bbe = 1 * itz
+
+    # Bytes between row. The distance in bytes to next row is number of 
Columns elements multiplied with itemsize.
+    bbr = Col * itz
+
+    # Bytes between matrices.  The byte distance is separated by the number 
of rows.
+    bbm = Row * bbr
+
+    # Make a tuple of the strides.
+    strides = (bbm, bbr, bbe)
+
+    # Make the stride view.
+    data_view = as_strided(data_array, shape=shape, strides=strides)
+
+    return data_view
+
+
+def matrix_exponential(A, dtype=None):
+    """Calculate the exact matrix exponential using the eigenvalue 
decomposition approach, for higher dimensional data.  This of dimension 
[NE][NS][NM][NO][ND][X][X] or [NS][NM][NO][ND][X][X].
+
+    Here X is the Row and Column length, of the outer square matrix.
+
+    @param A:               The square matrix to calculate the matrix 
exponential of.
+    @type A:                numpy float array of rank 
[NE][NS][NM][NO][ND][X][X]
+    @param dtype:           If provided, forces the calculation to use the 
data type specified.
+    @type type:             data-type, optional
+    @return:                The matrix exponential.  This will have the same 
dimensionality as the A matrix.
+    @rtype:                 numpy float array of rank 
[NE][NS][NM][NO][ND][X][X]
+    """
+
+    # Get the expected shape of the higher dimensional column numpy array.
+    if len(A.shape) == 7:
+        # Extract shapes from data.
+        NE, NS, NM, NO, ND, Row, Col = A.shape
+
+    else:
+        # Extract shapes from data.
+        NS, NM, NO, ND, Row, Col = A.shape
+
+        # Set NE to None.
+        NE = None
+
+    # Convert dtype, if specified.
+    if dtype != None:
+        dtype_mat = A.dtype
+
+        # If the dtype is different from the input.
+        if dtype_mat != dtype:
+            # This needs to be made as a copy.
+            A = A.astype(dtype)
+
+    # Is the original matrix real?
+    complex_flag = any(iscomplex(A))
+
+    # If numpy is under 1.8, there would be a need to do eig(A) per matrix.
+    if float(version.version[:3]) < 1.9:
+        # Make array to store results
+        if NE != None:
+            if dtype != None:
+                eA = zeros([NE, NS, NM, NO, ND, Row, Col], dtype=dtype)
+            else:
+                eA = zeros([NE, NS, NM, NO, ND, Row, Col], dtype=complex128)
+        else:
+            if dtype != None:
+                eA = zeros([NS, NM, NO, ND, Row, Col], dtype=dtype)
+            else:
+                eA = zeros([NS, NM, NO, ND, Row, Col], dtype=complex128)
+
+        # Get the data view, from the helper function.
+        A_view = data_view_via_striding_array_row_col(data_array=A)
+
+        # Create index view.
+        index = create_index(NE=NE, NS=NS, NM=NM, NO=NO, ND=ND)
+        index_view = data_view_via_striding_array_col(data_array=index)
+
+        # Zip them together and iterate over them.
+        for A_i, index_i in zip(A_view, index_view):
+            # The eigenvalue decomposition.
+            W_i, V_i = eig(A_i)
+
+            # Calculate the exponential.
+            W_exp_i = exp(W_i)
+
+            # Make a eye matrix.
+            eye_mat_i = eye(Row)
+
+            # Transform it to a diagonal matrix, with elements from vector 
down the diagonal.
+            # Use the dtype, if specified.
+            if dtype != None:
+                W_exp_diag_i = multiply(W_exp_i, eye_mat_i, dtype=dtype )
+            else:
+                W_exp_diag_i = multiply(W_exp_i, eye_mat_i)
+
+            # Make dot product.
+            dot_V_W_i = dot( V_i, W_exp_diag_i)
+
+            # Compute the (multiplicative) inverse of a matrix.
+            inv_V_i = inv(V_i)
+
+            # Calculate the exact exponential.
+            eA_i = dot(dot_V_W_i, inv_V_i)
+
+            # Save results.
+            # Extract index from index_view.
+            ei, si, mi, oi, di = index_i
+
+            # Store the result.
+            eA[ei, si, mi, oi, di, :] = eA_i
+
+    else:
+        # The eigenvalue decomposition.
+        W, V = eig(A)
+
+        # W: The eigenvalues, each repeated according to its multiplicity.
+        # The eigenvalues are not necessarily ordered.
+        # The resulting array will be always be of complex type. Shape 
[NE][NS][NM][NO][ND][X]
+        # V: The normalized (unit 'length') eigenvectors, such that the 
column v[:,i]
+        # is the eigenvector corresponding to the eigenvalue w[i]. Shape 
[NE][NS][NM][NO][ND][X][X]
+
+        # Calculate the exponential of all elements in the input array. 
Shape [NE][NS][NM][NO][ND][X]
+        # Add one axis, to allow for broadcasting multiplication.
+        if NE != None:
+            W_exp = exp(W).reshape(NE, NS, NM, NO, ND, Row, 1)
+        else:
+            W_exp = exp(W).reshape(NS, NM, NO, ND, Row, 1)
+
+        # Make a eye matrix, with Shape [NE][NS][NM][NO][ND][X][X]
+        if NE != None:
+            eye_mat = tile(eye(Row)[newaxis, newaxis, newaxis, newaxis, 
newaxis, ...], (NE, NS, NM, NO, ND, 1, 1) )
+        else:
+            eye_mat = tile(eye(Row)[newaxis, newaxis, newaxis, newaxis, 
newaxis, ...], (NS, NM, NO, ND, 1, 1) )
+
+        # Transform it to a diagonal matrix, with elements from vector down 
the diagonal.
+        # Use the dtype, if specified.
+        if dtype != None:
+            W_exp_diag = multiply(W_exp, eye_mat, dtype=dtype )
+        else:
+            W_exp_diag = multiply(W_exp, eye_mat)
+
+        # Make dot products for higher dimension.
+        # "...", the Ellipsis notation, is designed to mean to insert as 
many full slices (:)
+        # to extend the multi-dimensional slice to all dimensions.
+        dot_V_W = einsum('...ij, ...jk', V, W_exp_diag)
+
+        # Compute the (multiplicative) inverse of a matrix.
+        inv_V = inv(V)
+
+        # Calculate the exact exponential.
+        eA = einsum('...ij, ...jk', dot_V_W, inv_V)
+
+    # Return the complex matrix.
+    if complex_flag:
+        return array(eA)
+
+    # Return only the real part.
+    else:
+        return array(eA.real)
+
+
 def matrix_exponential_rank_NE_NS_NM_NO_ND_x_x(A, dtype=None):
     """Calculate the exact matrix exponential using the eigenvalue 
decomposition approach, for higher dimensional data.  This of dimension 
[NS][NS][NM][NO][ND][X][X].
 




Related Messages


Powered by MHonArc, Updated Fri Aug 01 18:20:03 2014