我正在编写一些 C 代码来在大约 4000 万个观测值的大型数据集上运行 OLS。我对使用不完全 Cholesky (ICC) 分解来获得预处理器的预处理共轭梯度算法 (PCG) 很感兴趣。我有一个 MATLAB 代码可以正确执行相同的操作,并将其用作基准以确保 C 脚本正确。简而言之,我想使用 C 复制 MATLAB 的“ichol”和“pcg”程序。
目前我正在为 PCG 和 ICC 使用 PETSc C 库。我使用 R 读入数据集并计算 X'X 和 X'Y,并将两者都输出到文本文件以供 C 使用。矩阵采用坐标稀疏格式(3 个向量 - 一个带有行索引,一个带有列索引,和一个具有所有非零矩阵条目的值)。完整矩阵的大小约为 800 万乘 800 万,其中包含约 1.66 亿个非零条目。
我在 C 中读入文件,构造矩阵和向量,并使用 PETSc 的程序 KSPSolve 运行 PCG,将线性系数 b 的向量输出到文本文件。代码顺利完成,但我得到的解决方案是荒谬的 - 系数约为 200 万,而我应该得到 2 或 3 之类的东西(通过运行 MATLAB 代码)。
但是,当我将数据集限制为较小的子样本(例如,前 300,000 个观察值)时,C 代码和 MATLAB 代码的结果非常吻合。经过反复试验,我确定结果一致,直到达到非常特定的样本大小 (363,855),此时 C 代码结果开始出现差异。分歧并不是突然爆发的——起初 C 代码和 MATLAB 代码之间的最大绝对差值在 700 左右,但很快增长到上面提到的 200 万。我查看了 PETSc 的内部收敛监控统计数据,程序本身似乎认为真实残差 (X'YX'Xb) 的范数下降到相当低的水平(10^(-3) 的顺序),而如果我计算使用 C 的输出在 MATLAB 中手动计算残差,不出所料,我得到了非常大的数字。
我正在寻找关于这里可能出现什么问题以及采取哪些步骤来诊断/修复它的建议。我对C相当陌生,所以基本错误绝对是可能的。
代码的相关部分粘贴在下面:
/* Read in matrix, rhs, and IA, JA, RA vectors from ascii files */
ierr = PetscPrintf(PETSC_COMM_SELF,"\n Read matrix in ascii format ...\n");CHKERRQ(ierr);
ierr = PetscFOpen(PETSC_COMM_SELF,Ain,"r",&Afile);CHKERRQ(ierr);
nsizes = 3;
fscanf(Afile,"%d,%d,%d\n",&m,&n,&nz);
ierr = PetscPrintf(PETSC_COMM_SELF,"m: %d, n: %d, nz: %d \n", m,n,nz);CHKERRQ(ierr);
/*Allocating matrix and vectors*/
ierr = MatCreate(PETSC_COMM_SELF,&A);CHKERRQ(ierr);
ierr = MatSetSizes(A,PETSC_DECIDE,PETSC_DECIDE,m,n);CHKERRQ(ierr);
ierr = MatSetFromOptions(A);CHKERRQ(ierr);
ierr = MatSeqAIJSetPreallocation(A,nz/m,nnz_array);CHKERRQ(ierr);
ierr = MatSetOption(A,MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_FALSE);CHKERRQ(ierr);
/*Building matrix*/
for (i=0; i<nz; i++) {
fscanf(Afile,"%g,%g,%le\n",&_row,&_col,(double*)&val);
row = (PetscInt)_row;
col = (PetscInt)_col;
ierr = MatSetValues(A,1,&row,1,&col,&val,INSERT_VALUES);
CHKERRQ(ierr);
}
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
ierr = MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY);CHKERRQ(ierr);
fflush(stdout);
fclose(Afile);
free(nnz_array);
/*Reading-in X'Y vector*/
ierr = VecCreate(PETSC_COMM_SELF,&b);CHKERRQ(ierr);
ierr = VecSetSizes(b,PETSC_DECIDE,n);CHKERRQ(ierr);
ierr = VecSetFromOptions(b);CHKERRQ(ierr);
ierr = PetscPrintf(PETSC_COMM_SELF,"\n Read rhs in ascii format ...\n");CHKERRQ(ierr);
ierr = PetscFOpen(PETSC_COMM_SELF,rhs,"r",&bfile);CHKERRQ(ierr);
for (i=0; i<n; i++) {
fscanf(bfile,"%d,%le\n",&dummy,(double*)&val);
ierr = VecSetValues(b,1,&i,&val,INSERT_VALUES);CHKERRQ(ierr);
}
ierr = VecAssemblyBegin(b);CHKERRQ(ierr);
ierr = VecAssemblyEnd(b);CHKERRQ(ierr);
fflush(stdout);
fclose(bfile);
/*Creating vector to store solution in*/
ierr = VecCreate(PETSC_COMM_WORLD,&x);CHKERRQ(ierr);
ierr = PetscObjectSetName((PetscObject) x, "Solution");CHKERRQ(ierr);
ierr = VecSetSizes(x,PETSC_DECIDE,n);CHKERRQ(ierr);
ierr = VecSetFromOptions(x);CHKERRQ(ierr);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create the linear solver and set various options
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
Create linear solver context
*/
ierr = KSPCreate(PETSC_COMM_WORLD,&ksp);CHKERRQ(ierr);
/*
Set operators. Here the matrix that defines the linear system
also serves as the preconditioning matrix.
*/
ierr = KSPSetOperators(ksp,A,A);CHKERRQ(ierr);
/*
Set linear solver defaults for this problem.
*/
ierr = KSPGetPC(ksp,&pc);CHKERRQ(ierr);
ierr = PCSetType(pc,PCICC);CHKERRQ(ierr);
ierr = KSPSetTolerances(ksp,1.e-10,1.e-10,PETSC_DEFAULT,1000);CHKERRQ(ierr);
ierr = KSPSetType(ksp,KSPCG);
/*
Set runtime options.
*/
ierr = KSPSetFromOptions(ksp);CHKERRQ(ierr);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Solve the linear system
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
Solve linear system
*/
printf("\nBeginning to solve system:\n");
ierr = KSPSolve(ksp,b,x);CHKERRQ(ierr);
/*
View solver info.
*/
ierr = KSPView(ksp,PETSC_VIEWER_STDOUT_WORLD);CHKERRQ(ierr);