我正在使用SuiteSparse中的CHOLMOD通过相对稀疏的N大带对角矩阵来对一个N进行因子化,即它只包含一些非零的对角线。矩阵的稀疏性由协方差长度参数l设置。l越大,非零对角线元素的数量就越多.
当l变大,许多元素为非零时,超节点CHOLMOD分解突然开始失败,出现"CHOLMOD警告:矩阵不正定“的错误信息。从使用单独的Python实现检查数学,我知道矩阵应该是正定的。而且,当我从
Common->.supernodal = CHOLMOD_SUPERNODAL; 至
Common->supernodal = CHOLMOD_SIMPLICIAL;这样,分解就会成功。对于下面的代码示例,只要l < 2.5,超节点分解就会成功。如果增加l >= 3.0,则得到矩阵不是正定的误差。然而,如果我选择CHOMOD_SIMPLICIAL,那么分解就会成功。有人能帮我找出为什么CHOLMOD_SUPERNODAL突然在某个稀疏/密度之上失败吗?谢谢!
//file is cov.c
//Compile with $ gcc -Wall -o cov -Icholmod cov.c -lm -lcholmod -lamd -lcolamd -lblas -llapack -lsuitesparseconfig
#include <stdio.h>
#include <math.h>
#include "cholmod.h"
#define PI 3.14159265
float k_3_2 (float r, float a, float l, float r0)
{
return (0.5 + 0.5 * cos(PI * r/r0)) * pow(a, 2.) * (1 + sqrt(3) * r/l) * exp(-sqrt(3) * r/l) ;
}
// function to initialize an array of increasing wavelengths
void linspace (double *wl, int N, double start, double end)
{
double j; //double index
double Ndist = (double) N;
double increment = (end - start)/(Ndist -1.);
int i;
for (i = 0; i < N; i++) {
j = (double) i;
wl[i] = start + j * increment;
}
}
// create and return a sparse matrix using a wavelength array and parameters
// for a covariance kernel.
cholmod_sparse *create_sparse(double *wl, int N, double a, double l, cholmod_common *c)
{
double r0 = 6.0 * l; //Beyond r0, all entries will be 0
//Pairwise calculate all of the r distances
int i = 0, j = 0;
double r;
//First loop to determine the number non-zero elements
int M = 0; //number of non-zero elements
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
r = fabs(wl[i] - wl[j]);
if (r < r0) //Is the separation below our cutoff?
M++;
}
}
/* Initialize a cholmod_triplet matrix, which we will subsequently fill with
* values. This matrix is NxN sparse with M total non-zero elements. 1 means we
* want a square and symmetric matrix. */
cholmod_triplet *T = cholmod_allocate_triplet(N, N, M, 1, CHOLMOD_REAL, c);
if (T == NULL || T->stype == 0) /* T must be symmetric */
{
cholmod_free_triplet (&T, c) ;
cholmod_finish (c) ;
return (0) ;
}
//Do the loop again, this time to fill in the matrix
int * Ti = T->i;
int * Tj = T->j;
double * Tx = T->x;
int k = 0;
//This time, only fill in the lower entries (and diagonal).
for (i = 0; i < N; i++)
{
for (j = 0; j <= i; j++)
{
r = fabs(wl[i] - wl[j]);
if (r < r0) //If the distance is below our cutoff, initialize
{
Ti[k] = i;
Tj[k] = j;
Tx[k] = k_3_2(r, a, l, r0);
k++;
}
}
}
T->nnz = k;
//The conversion will transpose the entries and add to the upper half.
cholmod_sparse *A = cholmod_triplet_to_sparse(T, k, c);
cholmod_free_triplet(&T, c);
return A;
}
int main(void)
{
//Create a sample array of wavelengths for testing purposes.
int N = 3000;
double wl[N];
linspace(wl, N, 5100., 5200.); //initialize with wavelength values
cholmod_common c ; //c is actually a struct, not a pointer to it.
cholmod_start (&c) ; // start CHOLMOD
c.print = 5;
//c.supernodal = CHOLMOD_SIMPLICIAL;
c.supernodal = CHOLMOD_SUPERNODAL;
float l = 2.5;
cholmod_sparse *A = create_sparse(wl, N, 1.0, l, &c);
cholmod_factor *L ;
L = cholmod_analyze (A, &c) ;
cholmod_factorize (A, L, &c) ;
printf("L->minor = %d\n", (int) L->minor);
cholmod_dense *b, *x, *r;
//Create a vector with the same number of rows as A
b = cholmod_ones (A->nrow, 1, A->xtype, &c) ; // b = ones(n,1)
x = cholmod_solve (CHOLMOD_A, L, b, &c) ; // solve Ax=b
//the reason these are length two is because they can be complex
double alpha [2] = {1,0}, beta [2] = {0,0} ; // basic scalars
r = cholmod_copy_dense (b, &c) ; // r = b
cholmod_sdmult (A, 0, alpha, beta, x, r, &c) ; // r = Ax
cholmod_print_dense(r, "r", &c); //This should be equal to b
cholmod_free_sparse(&A, &c); // free all of the variables
cholmod_free_factor(&L, &c);
cholmod_free_dense(&b, &c);
cholmod_free_dense(&x, &c);
cholmod_free_dense(&r, &c);
cholmod_finish (&c) ; // finish CHOLMOD
return (0) ;
}发布于 2014-04-01 01:21:41
在处理代码时,您似乎正在创建条件相当恶劣的矩阵,并要求CHOLMOD处理它们。当我告诉它对失败的情况做一个简单的因式分解时,我得到了在1e-7附近的倒数条件数(用cholmod_rcond计算)。当l = 2.5时,我得到了2e-6附近的一个倒数条件数。注意float的机器感应器就在这附近..。
如果我在您的float声明中将所有的k_3_2替换为doubles,它就不会再失败了。(我还没有看过你的矩阵里面有什么,所以除了说pow(a, 2.)是解决问题的糟糕方法之外,我不会做进一步的评论。)
我不知道你为什么会因为矩阵的条件不太好而失败。我不完全确定CHOLMOD的超节点分解是如何实现的,但是我相信它需要BLAS的dpotrf来做小的密集分解,dsyrk来处理块的其余部分。一个快速的dpotrf或dsyrk可能会给你带来悲伤。此外,CHOLMOD为矩阵中的一些结构为零的元素创建了非零,这样它就可以使用BLAS,这可能会使您放弃一点。
https://stackoverflow.com/questions/22774929
复制相似问题