00001
00028 #include <string.h>
00029
00030 #include "isam/util.h"
00031 #include "isam/SparseMatrix.h"
00032 #include "isam/SparseSystem.h"
00033
00034 #include "isam/Cholesky.h"
00035
00036 #include "cs.h"
00037 #include "cholmod.h"
00038
00039
00040 const bool USE_CSPARSE = false;
00041
00042
00043 const bool USE_CSPARSE_QR = false;
00044
00045 using namespace std;
00046 using namespace Eigen;
00047
00048 namespace isam {
00049
00050 class CholeskyImpl : public Cholesky {
00051 cholmod_sparse* _L;
00052 cholmod_dense* _rhs;
00053 int* _order;
00054
00055 cholmod_common Common;
00056
00057 public:
00058
00059 CholeskyImpl() : _L(NULL), _rhs(NULL), _order(NULL) {
00060 cholmod_start(&Common);
00061 }
00062
00063 virtual ~CholeskyImpl() {
00064 reset();
00065 cholmod_finish(&Common);
00066 }
00067
00068 void factorize(const SparseSystem& Ab, VectorXd* delta = NULL, double lambda = 0) {
00069 tic("Cholesky");
00070
00071 reset();
00072
00073 cholmod_sparse* At = to_cholmod_transp(Ab);
00074 int nrow = At->ncol;
00075 int ncol = At->nrow;
00076
00077
00078
00079 cholmod_factor *L_factor;
00080 #if 0
00081
00082 Common.nmethods = 1;
00083 Common.method[0].ordering = CHOLMOD_AMD;
00084 Common.postorder = false;
00085 #endif
00086 if (lambda>0) {
00087 cholmod_sparse* A = cholmod_transpose(At, 1, &Common);
00088
00089 cholmod_sparse* AtA = cholmod_ssmult(At, A, 1, 1, 1, &Common);
00090
00091 int* AtAp = (int*)AtA->p;
00092
00093 double* AtAx = (double*)AtA->x;
00094 for (int i=0; i<ncol; i++) {
00095 int p = AtAp[i+1]-1;
00096 AtAx[p] *= (1+lambda);
00097 }
00098 L_factor = cholmod_analyze(AtA, &Common);
00099 tic("cholmod_factorize");
00100 cholmod_factorize(AtA, L_factor, &Common);
00101 toc("cholmod_factorize");
00102 cholmod_free_sparse(&AtA, &Common);
00103 cholmod_free_sparse(&A, &Common);
00104 } else {
00105 L_factor = cholmod_analyze(At, &Common);
00106 tic("cholmod_factorize");
00107 cholmod_factorize(At, L_factor, &Common);
00108 toc("cholmod_factorize");
00109 }
00110
00111 cholmod_change_factor(CHOLMOD_REAL, true, false, true, true, L_factor, &Common);
00112
00113
00114
00115 cholmod_dense* A_rhs = cholmod_zeros(nrow, 1, CHOLMOD_REAL, &Common);
00116 memcpy(A_rhs->x, Ab.rhs().data(), nrow*sizeof(double));
00117 cholmod_dense* Atb = cholmod_zeros(ncol, 1, CHOLMOD_REAL, &Common);
00118 double alpha[2] = {1., 0.};
00119 double beta[2] = {0., 0.};
00120 cholmod_sdmult(At, 0, alpha, beta, A_rhs, Atb, &Common);
00121
00122 cholmod_dense* Atb_perm = cholmod_solve(CHOLMOD_P, L_factor, Atb, &Common);
00123
00124 _rhs = cholmod_solve(CHOLMOD_L, L_factor, Atb_perm, &Common);
00125
00126
00127 if (delta) {
00128 cholmod_dense* delta_ = cholmod_solve(CHOLMOD_Lt, L_factor, _rhs, &Common);
00129 *delta = VectorXd(_rhs->nrow);
00130 memcpy(delta->data(), (double*)delta_->x, _rhs->nrow*sizeof(double));
00131 cholmod_free_dense(&delta_, &Common);
00132 }
00133
00134
00135
00136 _order = new int[ncol];
00137 memcpy(_order, (int*)L_factor->Perm, ncol*sizeof(int));
00138 _L = cholmod_factor_to_sparse(L_factor, &Common);
00139
00140 cholmod_free_dense(&Atb_perm, &Common);
00141 cholmod_free_dense(&Atb, &Common);
00142 cholmod_free_dense(&A_rhs, &Common);
00143 cholmod_free_factor(&L_factor, &Common);
00144 cholmod_free_sparse(&At, &Common);
00145
00146 toc("Cholesky");
00147 }
00148
00149 void get_R(SparseSystem& R) {
00150
00151 of_cholmod_transp(_L, R, _order);
00152 VectorXd tmp(_L->nrow);
00153 memcpy(tmp.data(), (double*)_rhs->x, _L->nrow*sizeof(double));
00154 R.set_rhs(tmp);
00155 }
00156
00157 int* get_order() {
00158 return _order;
00159 }
00160
00161 private:
00162
00163 void reset() {
00164 if (_L) cholmod_free_sparse(&_L, &Common);
00165 if (_rhs) cholmod_free_dense(&_rhs, &Common);
00166 if (_order) delete[] _order;
00167 }
00168
00169
00170
00171 cholmod_sparse* to_cholmod_transp(const SparseSystem& A) {
00172
00173 cholmod_sparse* T = cholmod_allocate_sparse(A.num_cols(), A.num_rows(), A.nnz(),
00174 true, true, 0, CHOLMOD_REAL, &Common);
00175
00176 int* p = (int*)T->p;
00177 int* i = (int*)T->i;
00178 double* x = (double*)T->x;
00179 int n = 0;
00180 *p = n;
00181 for (int row=0; row<A.num_rows(); row++) {
00182 const SparseVector& r = A.get_row(row);
00183 int nnz = r.nnz();
00184
00185 r.copy_raw(i, x);
00186 i += nnz;
00187 x += nnz;
00188 n += nnz;
00189 p++;
00190 *p = n;
00191 }
00192
00193 return T;
00194 }
00195
00196
00197 void of_cholmod_transp(const cholmod_sparse* T, SparseSystem& A, int* order) {
00198 int nrow = T->ncol;
00199 int ncol = T->nrow;
00200 SparseVector_p* rows = new SparseVector_p[nrow];
00201 int *p = (int*)T->p;
00202 int *i = (int*)T->i;
00203 double* x = (double*)T->x;
00204 for (int row = 0; row < nrow; row++) {
00205 int nnz = *(p+1) - *p;
00206 rows[row] = new SparseVector(i, x, nnz);
00207 i += nnz;
00208 x += nnz;
00209 p++;
00210 }
00211 A.import_rows_ordered(nrow, ncol, rows, order);
00212 delete [] rows;
00213 }
00214
00215 };
00216
00217 class CholeskyImplCSparse : public Cholesky {
00218 cs* _L;
00219 double* _rhs;
00220 int* _order;
00221
00222 public:
00223
00224 CholeskyImplCSparse() : _L(NULL), _rhs(NULL), _order(NULL) {
00225 }
00226
00227 virtual ~CholeskyImplCSparse() {
00228 reset();
00229 }
00230
00231
00232 int* qr(cs* csA, int n, css* S, csn* N) {
00233
00234 S = cs_sqr(3, csA, 1);
00235
00236 N = cs_qr(csA, S);
00237
00238
00239 cs* csTemp = cs_spalloc(n, n, N->U->nzmax, 1, 0);
00240 memcpy(csTemp->p, N->U->p, (n+1) * sizeof(int));
00241 memcpy(csTemp->i, N->U->i, N->U->nzmax * sizeof(int));
00242 memcpy(csTemp->x, N->U->x, N->U->nzmax * sizeof(double));
00243 _L = cs_transpose(csTemp, 1);
00244 cs_spfree(csTemp);
00245 return S->q;
00246 }
00247
00248 int* cholesky(cs* csA, cs* csAt, int n, double lambda, css* S, csn* N) {
00249
00250 cs* csAtA = cs_multiply(csAt, csA);
00251 if (lambda>0.) {
00252
00253 for (int i=0; i<n; i++) {
00254 int p = csAtA->p[i];
00255 while (csAtA->i[p]!=i) {p++;}
00256 csAtA->x[p] *= (1.+lambda);
00257 }
00258 }
00259 S = cs_schol(1, csAtA);
00260 N = cs_chol(csAtA, S);
00261
00262 _L = cs_spalloc(n, n, N->L->nzmax, 1, 0);
00263 memcpy(_L->p, N->L->p, (n+1) * sizeof(int));
00264 memcpy(_L->i, N->L->i, N->L->nzmax * sizeof(int));
00265 memcpy(_L->x, N->L->x, N->L->nzmax * sizeof(double));
00266 int* p = cs_pinv(S->pinv, csAtA->n);
00267 cs_spfree(csAtA);
00268 return p;
00269 }
00270
00271 void factorize(const SparseSystem& Ab, VectorXd* delta = NULL, double lambda = 0) {
00272 tic("Cholesky");
00273
00274 reset();
00275
00276
00277 cs* csAt = to_csparse_transp(Ab);
00278 cs* csA = cs_transpose(csAt, 1);
00279 int m = csA->m;
00280 int n = csA->n;
00281 css* S = NULL;
00282 csn* N = NULL;
00283 int* p = NULL;
00284 if (USE_CSPARSE_QR) {
00285 cout << "WARNING: Using slow QR factorization without LM support" << endl;
00286 p = qr(csA, n, S, N);
00287 } else {
00288 p = cholesky(csA, csAt, n, lambda, S, N);
00289 }
00290 _order = new int[n];
00291 memcpy(_order, p, n*sizeof(int));
00292
00293 const double* Ab_rhs = Ab.rhs().data();
00294
00295 double *Atb = new double[m];
00296 for (int i=0; i<n; i++) {
00297 Atb[i] = 0.;
00298 }
00299 cs_gaxpy(csAt, Ab_rhs, Atb);
00300
00301
00302 _rhs = new double[n];
00303 for (int i=0; i<n; i++) {
00304 _rhs[i] = Atb[p[i]];
00305 }
00306
00307
00308 cs_lsolve(_L, _rhs);
00309
00310
00311 if (delta) {
00312 double* delta_ = new double[n];
00313 memcpy(delta_, _rhs, n*sizeof(double));
00314 cs_ltsolve(_L, delta_);
00315 *delta = VectorXd(n);
00316 memcpy(delta->data(), delta_, n*sizeof(double));
00317 delete[] delta_;
00318 }
00319
00320
00321 if (USE_CSPARSE_QR) {
00322 cs_free(p);
00323 }
00324 cs_spfree(csA);
00325 cs_spfree(csAt);
00326 cs_sfree(S);
00327 cs_nfree(N);
00328 delete[] Atb;
00329 }
00330
00331 void get_R(SparseSystem& R) {
00332 of_csparse_transp(_L, R, _order);
00333 VectorXd tmp(_L->n);
00334 memcpy(tmp.data(), _rhs, _L->n);
00335 R.set_rhs(tmp);
00336 }
00337
00338 int* get_order() {
00339 return _order;
00340 }
00341
00342 private:
00343
00344 void reset() {
00345 if (_L) cs_spfree(_L);
00346 if (_rhs) delete[] _rhs;
00347 if (_order) delete[] _order;
00348 }
00349
00350 cs* to_csparse_transp(const SparseMatrix& A) const {
00351
00352 cs* T = cs_spalloc(A.num_cols(), A.num_rows(), A.nnz(), 1, 0);
00353 int* p = (int*)T->p;
00354 int* i = (int*)T->i;
00355 double* x = (double*)T->x;
00356 int n = 0;
00357 *p = n;
00358 for (int row=0; row<A.num_rows(); row++) {
00359 const SparseVector& r = A.get_row(row);
00360 int nnz = r.nnz();
00361
00362 r.copy_raw(i, x);
00363 i += nnz;
00364 x += nnz;
00365 n += nnz;
00366 p++;
00367 *p = n;
00368 }
00369
00370 return T;
00371 }
00372
00373 void of_csparse_transp(const cs* T, SparseSystem& A, int* order) {
00374 int nrow = T->n;
00375 int ncol = T->m;
00376 SparseVector_p rows[nrow];
00377 int *p = (int*)T->p;
00378 int *i = (int*)T->i;
00379 double* x = (double*)T->x;
00380 for (int row = 0; row < nrow; row++) {
00381 int nnz = *(p+1) - *p;
00382 rows[row] = new SparseVector(i, x, nnz);
00383 i += nnz;
00384 x += nnz;
00385 p++;
00386 }
00387 A.import_rows_ordered(nrow, ncol, rows, order);
00388 }
00389
00390 };
00391
00392
00393 Cholesky* Cholesky::Create() {
00394 if (USE_CSPARSE) {
00395 return new CholeskyImplCSparse();
00396 } else {
00397 return new CholeskyImpl();
00398 }
00399 }
00400
00401 }