матричная инверсия для комплексных чисел методом Гаусса-Джордана в cuda

Я пытаюсь инвертировать матрицу, составленную из комплексных чисел, где я использую код инверсии матрицы для действительных чисел, размещенных по следующей ссылке пользователем
матрица cuda обратная гауссовская иордания

код компилируется, ошибок нет, но проблема в выводе неверна! Я не знаю, где я ошибся.
Может кто-нибудь, пожалуйста, помогите.
Заранее спасибо!

вот полный код:

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#pragma comment(lib, "cuda.lib")
#pragma comment(lib, "cudart.lib")
#include <cuda.h>
#include <math.h>
#include <cuda_runtime.h>
#include <cuda_runtime_api.h>
#include "device_launch_parameters.h"#include <cublas_v2.h>
#include "cuComplex.h"#include <complex>

__device__ __host__ cuDoubleComplex  operator*(cuDoubleComplex a, cuDoubleComplex b) { return cuCmul(a,b); }
__device__ __host__ cuDoubleComplex  operator+(cuDoubleComplex a, cuDoubleComplex b) { return cuCadd(a,b); }
__device__ __host__ cuDoubleComplex  operator/(cuDoubleComplex a, cuDoubleComplex b) { return cuCdiv(a,b); }
__device__ __host__ cuDoubleComplex  operator-(cuDoubleComplex a, cuDoubleComplex b) { return cuCsub(a,b); }

using namespace std;

__global__ void gaussjordan(cuDoubleComplex *A,  cuDoubleComplex *I,int n, int i)
{
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
cuDoubleComplex P;

if(x<n && y<n)
if(x>i){
P=A[x*n+i]/A[i*n+i];
I[x*n+y] = I[x*n+y] - I[i*n+y]*P;
if(y>=i){
A[x*n+y] = A[x*n+y] - A[i*n+y]*P;
}
}
}__global__ void dev(cuDoubleComplex *d_A,  cuDoubleComplex *dI, int h)
{
cuDoubleComplex temp = make_cuDoubleComplex(0,0);
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
if(x<h && y<h)
if( cuCimag(d_A[x*h+x]) != cuCimag(temp)){
if( cuCreal(d_A[x*h+x]) != cuCreal(temp)){

dI[x*h+y]  = dI[x*h+y]/d_A[x*h+x];
d_A[x*h+y] = d_A[x*h+y]/d_A[x*h+x];
}
}
__syncthreads();

}

int main()
{
int const n = 3;
// creating input
cuDoubleComplex iL[n*n],L[n*n], I[n*n];

for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j ) L[i*n+j] =make_cuDoubleComplex(0,1);
else L[i*n+j] = make_cuDoubleComplex(0,0);

printf("%.2f  ", cuCimag(L[i*n+j]));
}
printf("\n");
}
printf("\n");

cuDoubleComplex *d_A, *d_L, *dI;
float time;
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
int ddsize = n*n*sizeof(cuDoubleComplex);

dim3 threadsPerBlock(n/16,n/16); //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
dim3 numBlocks(16,16);     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// memory allocation
cudaMalloc( (void**)  &d_A, ddsize);
cudaMalloc( (void**)   &dI, ddsize);

for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j) I[i*n+i]=make_cuDoubleComplex(1,0);
else I[i*n+j]=make_cuDoubleComplex(0,0);
}
}

//copy data from GPU to CPU
cudaMemcpy(  d_A,    L, ddsize, cudaMemcpyHostToDevice);
cudaMemcpy(   dI,    I, ddsize, cudaMemcpyHostToDevice);
//timer start
cudaEventRecord( start, 0);
// L^(-1)
for(int i=0;i<n;i++){
gaussjordan<<<numBlocks,threadsPerBlock>>>(d_A, dI, n, i);
}
dev<<<numBlocks,  threadsPerBlock>>>(d_A, dI, n);

cudaMemcpy(iL, dI, ddsize, cudaMemcpyDeviceToHost );
cudaMemcpy(L, d_A, ddsize, cudaMemcpyDeviceToHost );

cudaEventRecord( stop, 0 );
cudaEventSynchronize( stop );
cudaEventElapsedTime( &time, start, stop );
cudaEventDestroy( start );
cudaEventDestroy( stop );for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%.2f  ", cuCimag(iL[i*n+j]));
}
printf("\n");
}
printf("\n");std::cout<<"Cuda Time - inverse: "<< time <<"ms\n";

cudaFree(d_A);
cudaFree(dI);

system("Pause");
return 0;
}

Спасибо @RobertCrovella за ваше быстрое и очень проницательное предложение! Что касается вашего ответа на мой вопрос: я изменил свои threadsPerBlock (4,4) и numBlocks (1,1), поэтому я буду использовать 1 блок с 16 потоками для моей матрицы 4×4. Моя входная матрица следующая

1  0  0  0
0  2  0  0
0  0  3  0
0  0  0  4

здесь все числа действительны, тогда ожидаемая перевернутая матрица должна выглядеть

1   0    0   0
0   1/2  0   0
0   0    1/3 0
0   0    0   1/4

и я не получаю это вообще. Я ввел инструмент cuda memcheck, чтобы проверить, не работает ли мое ядро
но он не показал никаких ошибок массажа. Я начал изучать CUDA совсем недавно и не имею большого опыта. Кто-нибудь может дать более подробный ответ? Благодарю вас!

вот мой модифицированный код

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#pragma comment(lib, "cuda.lib")
#pragma comment(lib, "cudart.lib")
#include <cuda.h>
#include <math.h>
#include <cuda_runtime.h>
#include <cuda_runtime_api.h>
#include "device_launch_parameters.h"#include <cublas_v2.h>
#include "cuComplex.h"#include <complex>

__device__ __host__ cuDoubleComplex  operator*(cuDoubleComplex a, cuDoubleComplex b) { return cuCmul(a,b); }
__device__ __host__ cuDoubleComplex  operator+(cuDoubleComplex a, cuDoubleComplex b) { return cuCadd(a,b); }
__device__ __host__ cuDoubleComplex  operator/(cuDoubleComplex a, cuDoubleComplex b) { return cuCdiv(a,b); }
__device__ __host__ cuDoubleComplex  operator-(cuDoubleComplex a, cuDoubleComplex b) { return cuCsub(a,b); }

using namespace std;

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}__global__ void gaussjordan(cuDoubleComplex *A,  cuDoubleComplex *I,int n, int i)
{
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
cuDoubleComplex P;

if(x<n && y<n)
if(x>i){
P=A[x*n+i]/A[i*n+i];
I[x*n+y] = I[x*n+y] - I[i*n+y]*P;
if(y>=i){
A[x*n+y] = A[x*n+y] - A[i*n+y]*P;
}
}
}__global__ void dev(cuDoubleComplex *d_A,  cuDoubleComplex *dI, int h)
{
cuDoubleComplex temp = make_cuDoubleComplex(0,0);
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
if(x<h && y<h)
if( cuCimag(d_A[x*h+x]) != 0 ){
if( cuCreal(d_A[x*h+x]) != 0 ){

dI[x*h+y]  = dI[x*h+y]/d_A[x*h+x];
d_A[x*h+y] = d_A[x*h+y]/d_A[x*h+x];
}
}
__syncthreads();
}

int main()
{
int const n= 4;
// creating input
cuDoubleComplex iL[n*n],L[n*n], I[n*n];

for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j ) L[i*n+j] =make_cuDoubleComplex(i+1,0);
else L[i*n+j] = make_cuDoubleComplex(0,0);

printf("%.2f ", cuCreal(L[i*n+j]));
}
printf("\n");
}
printf("\n");

cuDoubleComplex *d_A, *dI;
float time;
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
int ddsize = n*n*sizeof(cuDoubleComplex);

dim3 threadsPerBlock(n,n); //!!!!!!!!!!!!!!!!!!
dim3 numBlocks(1,1);       //!!!!!!!!!!!!!!!!!!

// memory allocation
cudaMalloc( (void**)  &d_A, ddsize);
cudaMalloc( (void**)   &dI, ddsize);

for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j) I[i*n+i]=make_cuDoubleComplex(1,0);
else I[i*n+j]=make_cuDoubleComplex(0,0);
}
}

//copy data from GPU to CPU
cudaMemcpy(  d_A,    L, ddsize, cudaMemcpyHostToDevice);
cudaMemcpy(   dI,    I, ddsize, cudaMemcpyHostToDevice);
//timer start
cudaEventRecord( start, 0);
// L^(-1)
for(int i=0;i<n;i++){
gaussjordan<<<numBlocks,threadsPerBlock>>>(d_A, dI, n, i);
gpuErrchk( cudaPeekAtLastError() );
}
dev<<<numBlocks,  threadsPerBlock>>>(d_A, dI, n);

gpuErrchk( cudaPeekAtLastError() );

gpuErrchk(cudaMemcpy(iL, dI, ddsize, cudaMemcpyDeviceToHost ));
gpuErrchk(cudaMemcpy(L, d_A, ddsize, cudaMemcpyDeviceToHost ));

cudaEventRecord( stop, 0 );
cudaEventSynchronize( stop );
cudaEventElapsedTime( &time, start, stop );
cudaEventDestroy( start );
cudaEventDestroy( stop );for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%.2f ", cuCreal(iL[i*n+j]));
}
printf("\n");
}
printf("\n");std::cout<<"Cuda Time - inverse: "<< time <<"ms\n";

cudaFree(d_A);
cudaFree(dI);

system("Pause");
return 0;
}

0

Решение

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я не эксперт по инверсии матриц. Я не работал над деталями различий между реальной матричной инверсией и сложной матричной инверсией (не должно быть много различий, я не думаю). Как уже предлагалось, вероятно, есть лучшие / более быстрые способы инвертировать матрицы.

Непосредственная проблема, кажется, в вашем dev ядро, особенно здесь:

    if( cuCimag(d_A[x*h+x]) != cuCimag(temp)){
if( cuCreal(d_A[x*h+x]) != cuCreal(temp)){

Это требует, чтобы и то и другое действительная и мнимая части d_A рассматриваемый матричный элемент не равен нулю для того, чтобы dev ядро для любой работы. Однако я не думаю, что это условие должно быть необходимым. Для деления мы, вероятно, только требуем, чтобы или действительная или мнимая часть должна быть ненулевой. Я думаю, что в сложной области мы на самом деле делим на ноль, только если и действительная, и мнимая части равны нулю. Если вы осмотрите cuCdiv функция предусмотрена в cuComplex.hВы можете сами определить, при каких условиях он «взорвется» и, следовательно, какие условия необходимо проверить и избежать. Я уверен, что ваш тест не верен.

Следующий измененный код работает правильно для меня, для вашего простого теста:

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <math.h>
#include "cuComplex.h"#include <complex>

__device__ __host__ cuDoubleComplex  operator*(cuDoubleComplex a, cuDoubleComplex b) { return cuCmul(a,b); }
__device__ __host__ cuDoubleComplex  operator+(cuDoubleComplex a, cuDoubleComplex b) { return cuCadd(a,b); }
__device__ __host__ cuDoubleComplex  operator/(cuDoubleComplex a, cuDoubleComplex b) { return cuCdiv(a,b); }
__device__ __host__ cuDoubleComplex  operator-(cuDoubleComplex a, cuDoubleComplex b) { return cuCsub(a,b); }

using namespace std;

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}__global__ void gaussjordan(cuDoubleComplex *A,  cuDoubleComplex *I,int n, int i)
{
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
cuDoubleComplex P;

if(x<n && y<n)
if(x>i){
P=A[x*n+i]/A[i*n+i];
I[x*n+y] = I[x*n+y] - I[i*n+y]*P;
if(y>=i){
A[x*n+y] = A[x*n+y] - A[i*n+y]*P;
}
}
}__global__ void dev(cuDoubleComplex *d_A,  cuDoubleComplex *dI, int h)
{
cuDoubleComplex temp = make_cuDoubleComplex(0,0);
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
if(x<h && y<h)
if(( cuCimag(d_A[x*h+x]) != 0 ) || ( cuCreal(d_A[x*h+x]) != 0 )){

dI[x*h+y]  = dI[x*h+y]/d_A[x*h+x];
d_A[x*h+y] = d_A[x*h+y]/d_A[x*h+x];

}
__syncthreads();
}

int main()
{
int const n= 4;
// creating input
cuDoubleComplex iL[n*n],L[n*n], I[n*n];

for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j ) L[i*n+j] =make_cuDoubleComplex(i+1,0);
else L[i*n+j] = make_cuDoubleComplex(0,0);

printf("%.2f ", cuCreal(L[i*n+j]));
}
printf("\n");
}
printf("\n");

cuDoubleComplex *d_A, *dI;
float time;
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
int ddsize = n*n*sizeof(cuDoubleComplex);

dim3 threadsPerBlock(n,n); //!!!!!!!!!!!!!!!!!!
dim3 numBlocks(1,1);       //!!!!!!!!!!!!!!!!!!

// memory allocation
cudaMalloc( (void**)  &d_A, ddsize);
cudaMalloc( (void**)   &dI, ddsize);

for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j) I[i*n+i]=make_cuDoubleComplex(1,0);
else I[i*n+j]=make_cuDoubleComplex(0,0);
}
}

//copy data from GPU to CPU
cudaMemcpy(  d_A,    L, ddsize, cudaMemcpyHostToDevice);
cudaMemcpy(   dI,    I, ddsize, cudaMemcpyHostToDevice);
//timer start
cudaEventRecord( start, 0);
// L^(-1)
for(int i=0;i<n;i++){
gaussjordan<<<numBlocks,threadsPerBlock>>>(d_A, dI, n, i);
gpuErrchk( cudaPeekAtLastError() );
}
dev<<<numBlocks,  threadsPerBlock>>>(d_A, dI, n);

gpuErrchk( cudaPeekAtLastError() );

gpuErrchk(cudaMemcpy(iL, dI, ddsize, cudaMemcpyDeviceToHost ));
gpuErrchk(cudaMemcpy(L, d_A, ddsize, cudaMemcpyDeviceToHost ));

cudaEventRecord( stop, 0 );
cudaEventSynchronize( stop );
cudaEventElapsedTime( &time, start, stop );
cudaEventDestroy( start );
cudaEventDestroy( stop );for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%.2f ", cuCreal(iL[i*n+j]));
}
printf("\n");
}
printf("\n");std::cout<<"Cuda Time - inverse: "<< time <<"ms\n";

cudaFree(d_A);
cudaFree(dI);

return 0;
}

ЗАКЛЮЧИТЕЛЬНЫЙ ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я не говорю, что это полностью проверенный подход к инверсии матриц произвольных измерений. Я просто указываю на критическую ошибку, которая, по-видимому, приводит к сбою в вашем простом тестовом примере. Я также высказал некоторые оговорки в предыдущем вопросе, который вы связали.

1

Другие решения


По вопросам рекламы [email protected]