Быстрая матричная экспонента сложной симметричной трехдиагональной матрицы

В основном мне нужно выше. Я трал Googles и не могу найти способ реализовать это.

Я нашел эту функцию здесь http://www.guwi17.de/ublas/examples/ но это слишком медленно. Я даже написал свою собственную аппроксимацию Паде, следуя процедуре MATLAB, но она была лишь немного быстрее, чем та, что в ссылке.

Что меня поражает, так это то, как быстро Mathematica может вычислять матричные экспоненты (не знаю, волнует ли ее трехмерность матрицы).

Кто-нибудь может помочь?

EDIT: Вот что я придумал, есть комментарии? Надеюсь, будет полезно для будущих читателей

Я некоторое время не работал с С++, поэтому приведенный ниже код может быть немного обрывочным/медленным, поэтому, пожалуйста, просветите меня, если вы видите улучшения.

//Program will compute the matrix exponential expm( i gA ). where i = sqrt(-1) and gA is a matrix

#include <iostream>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_blas.h>


int main() {

    int n = 100;

    unsigned i = 0;
    unsigned j = 0;

    gsl_complex img = gsl_complex_rect (0.,1.);

    gsl_matrix * gA = gsl_matrix_alloc (n, n);


//Set gA:       
    for ( i = 0; i<n; i++) {
        for ( j = 0; j<=i; j++) {
            if( i == j ) {
                if( i == 0 || i == n-1 ) {
                    gsl_matrix_set (gA, i, j, 1.);
                } else {
                    gsl_matrix_set (gA, i, j, 2.);
                }
            } else if( j == i-1 ) {
                gsl_matrix_set (gA, i, j, 1.);
                gsl_matrix_set (gA, j, i, 1.);
            } else {
                gsl_matrix_set (gA, i, j, 0.);
                gsl_matrix_set (gA, j, i, 0.);
        }
    }
}


//get SVD of gA 
    gsl_matrix * gA_t = gsl_matrix_alloc (n, n);
    gsl_matrix_transpose_memcpy (gA_t, gA);                 

    gsl_matrix * U = gsl_matrix_alloc (n, n);
    gsl_matrix * V= gsl_matrix_alloc (n, n);
    gsl_vector * S = gsl_vector_alloc (n);


    gsl_vector * work = gsl_vector_alloc (n);
    gsl_linalg_SV_decomp (gA_t, V, S, work);
    gsl_vector_free(work);

    gsl_matrix_memcpy (U, gA_t);


//Take exponential of S and form matrix
    gsl_matrix_complex * Sp = gsl_matrix_complex_alloc (n, n);
    gsl_matrix_complex_set_zero (Sp);
    for (i = 0; i < n; i++) {
        gsl_matrix_complex_set (Sp, i, i, gsl_complex_exp ( gsl_complex_mul_real ( img, gsl_vector_get(S, i) ) ) ); // Vector 'S' to matrix 'Sp'
    }

    gsl_matrix_complex * Uc = gsl_matrix_complex_alloc (n, n);


//convert U to a complex matrix for next step   
    for (i = 0; i < n; i++) {
        for ( j = 0; j<n; j++) {
            gsl_matrix_complex_set (Uc, i, j, gsl_complex_rect ( gsl_matrix_get(U, i, j), 0. ) );       
        }
    }


//recombine U S Utranspose  
    gsl_matrix_complex * tA = gsl_matrix_complex_alloc (n, n);
    gsl_matrix_complex * eA = gsl_matrix_complex_alloc (n, n);
    gsl_blas_zgemm (CblasNoTrans, CblasNoTrans, GSL_COMPLEX_ONE, Uc, Sp, GSL_COMPLEX_ZERO, tA);
    gsl_blas_zgemm (CblasNoTrans, CblasTrans, GSL_COMPLEX_ONE, tA, Uc, GSL_COMPLEX_ZERO, eA);


//Print result  
    std::cout << "eA:" << std::endl;
    for (i = 0; i < n; i++)  
        for (j = 0; j < n; j++)
            printf ("%g + %g i\n", GSL_REAL(gsl_matrix_complex_get (eA, i, j)), GSL_IMAG(gsl_matrix_complex_get (eA, i, j)));
    std::cout << "\n" << std::endl;


//Free up
    gsl_matrix_free(gA_t);
    gsl_matrix_free(U);
    gsl_matrix_free(gA);
    gsl_matrix_free(V);
    gsl_vector_free(S);
    gsl_matrix_complex_free(Uc);
    gsl_matrix_complex_free(tA);    
    gsl_matrix_complex_free(eA);    

    return 0;
}        

person Quantum_Oli    schedule 12.04.2012    source источник
comment
Вам нужна только одна экспонента или много с разными множителями? В последнем случае целесообразно диагонализовать матрицу, возможно, Mathematica делает это автоматически.   -  person leftaroundabout    schedule 12.04.2012
comment
Много с разным множителем. У меня есть реальная трехдиагональная матрица U, и мне нужно вычислить expm(i z U), где i = sqrt(-1) для многих значений z. Если вы рекомендуете диагонализировать матрицу, можете ли вы порекомендовать для этого быструю библиотеку С++?   -  person Quantum_Oli    schedule 12.04.2012
comment
Это то, о чем я думал. Лично я использую для этого CULA, специализированную для графических процессоров Nvidia. GSL также имеет приличные алгоритмы, которые должны работать достаточно хорошо. Однако оба они довольно низкоуровневые — нет приятного интерфейса C++.   -  person leftaroundabout    schedule 12.04.2012
comment
Я сделал диагональ! См. выше. Это примерно в 100 раз быстрее, чем функция, использующая метод Паде, указанный в моей ссылке. Это должно быть достаточно быстро для моих средств. Кроме того, мне нужно сделать разложение только один раз. Спасибо за вашу помощь.   -  person Quantum_Oli    schedule 12.04.2012


Ответы (1)


Код выше, который я разместил в своем вопросе, работает очень хорошо. Еще одно улучшение, которое я обнаружил, заключалось в том, что столбцы в копии V масштабировались по собственным значениям, а не выполнялось полное матричное умножение. Поскольку zgemm является самой медленной частью этого кода, удаление одной из функций zgemm ускорило его почти в два раза. Вскоре я опубликую здесь полный листинг кода.

person Quantum_Oli    schedule 16.04.2012