/* Orders rows and saves pointer to matrix.and model */ int ClpCholeskyUfl::order(ClpInterior * model) { int iRow; model_ = model; if (preOrder(false, true, doKKT_)) return -1; permuteInverse_ = new int [numberRows_]; permute_ = new int[numberRows_]; double Control[AMD_CONTROL]; double Info[AMD_INFO]; amd_defaults(Control); //amd_control(Control); int returnCode = amd_order(numberRows_, choleskyStart_, choleskyRow_, permute_, Control, Info); delete [] choleskyRow_; choleskyRow_ = NULL; delete [] choleskyStart_; choleskyStart_ = NULL; //amd_info(Info); if (returnCode != AMD_OK) { std::cout << "AMD ordering failed" << std::endl; return 1; } for (iRow = 0; iRow < numberRows_; iRow++) { permuteInverse_[permute_[iRow]] = iRow; } return 0; }
void amd_order1(int n, int A_ptr[], int A_ind[], int P_per[]) { /* approximate minimum degree ordering (AMD) */ int k, ret; double Control[AMD_CONTROL], Info[AMD_INFO]; /* get the default parameters */ amd_defaults(Control); #if 0 /* and print them */ amd_control(Control); #endif /* make all indices 0-based */ for (k = 1; k < A_ptr[n+1]; k++) A_ind[k]--; for (k = 1; k <= n+1; k++) A_ptr[k]--; /* call the ordering routine */ ret = amd_order(n, &A_ptr[1], &A_ind[1], &P_per[1], Control, Info) ; #if 0 amd_info(Info); #endif xassert(ret == AMD_OK || ret == AMD_OK_BUT_JUMBLED); /* retsore 1-based indices */ for (k = 1; k <= n+1; k++) A_ptr[k]++; for (k = 1; k < A_ptr[n+1]; k++) A_ind[k]++; /* patch up permutation matrix */ memset(&P_per[n+1], 0, n * sizeof(int)); for (k = 1; k <= n; k++) { P_per[k]++; xassert(1 <= P_per[k] && P_per[k] <= n); xassert(P_per[n+P_per[k]] == 0); P_per[n+P_per[k]] = k; } return; }
static scs_int _ldl_init(cs *A, scs_int P[], scs_float **info) { *info = (scs_float *)scs_malloc(AMD_INFO * sizeof(scs_float)); #ifdef DLONG return (amd_l_order(A->n, A->p, A->i, P, (scs_float *)SCS_NULL, *info)); #else return (amd_order(A->n, A->p, A->i, P, (scs_float *)SCS_NULL, *info)); #endif }
Vec<unsigned> symamd(const Vec<Vec<unsigned> > &ind) { Vec<int> lines; lines.resize( ind.size() + 1 ); Vec<int> indices; unsigned nb_ind = 0; for(unsigned i=0;i<ind.size();++i) nb_ind += ind[i].size(); indices.reserve( nb_ind ); lines[0] = 0; for(unsigned i=0;i<ind.size();++i) { for(unsigned j=0;j<ind[i].size();++j) indices.push_back( ind[i][j] ); lines[i+1] = indices.size(); } Vec<unsigned> P; P.resize( ind.size() ); amd_order( ind.size(), lines.begin(), indices.begin(), (int *)P.begin(), (double *)NULL, (double *)NULL ); return P; }
int main (void) #endif { /* ---------------------------------------------------------------------- */ /* local variables */ /* ---------------------------------------------------------------------- */ #ifdef USE_AMD double Info [AMD_INFO] ; #endif double r, rnorm, flops, maxrnorm = 0. ; double *Ax, *Lx, *B, *D, *X, *Y ; LDL_int matrix, *Ai, *Ap, *Li, *Lp, *P, *Pinv, *Perm, *PermInv, n, i, j, p, nz, *Flag, *Pattern, *Lnz, *Parent, trial, lnz, d, jumbled ; FILE *f ; char s [LEN] ; /* ---------------------------------------------------------------------- */ /* check the error-checking routines with null matrices */ /* ---------------------------------------------------------------------- */ i = 1 ; n = -1 ; if (LDL_valid_perm (n, (LDL_int *) NULL, &i) || !LDL_valid_perm (0, (LDL_int *) NULL, &i) || LDL_valid_matrix (n, (LDL_int *) NULL, (LDL_int *) NULL) || LDL_valid_matrix (0, &i, &i)) { printf (PROGRAM ": ldl error-checking routine failed\n") ; EXIT_ERROR ; } /* ---------------------------------------------------------------------- */ /* read in a factorize a set of matrices */ /* ---------------------------------------------------------------------- */ for (matrix = 1 ; matrix <= NMATRICES ; matrix++) { /* ------------------------------------------------------------------ */ /* read in the matrix and the permutation */ /* ------------------------------------------------------------------ */ sprintf (s, "../Matrix/A%02d", (int) matrix) ; if ((f = fopen (s, "r")) == (FILE *) NULL) { printf (PROGRAM ": could not open file: %s\n", s) ; EXIT_ERROR ; } fgets (s, LEN, f) ; printf ("\n\n--------------------------------------------------------"); printf ("\nInput matrix: %s", s) ; printf ("--------------------------------------------------------\n\n"); fscanf (f, LDL_ID " " LDL_ID, &n, &jumbled) ; n = (n < 0) ? (0) : (n) ; ALLOC_MEMORY (P, LDL_int, n) ; ALLOC_MEMORY (Ap, LDL_int, n+1) ; for (j = 0 ; j <= n ; j++) { fscanf (f, LDL_ID, &Ap [j]) ; } nz = Ap [n] ; ALLOC_MEMORY (Ai, LDL_int, nz) ; ALLOC_MEMORY (Ax, double, nz) ; for (p = 0 ; p < nz ; p++) { fscanf (f, LDL_ID , &Ai [p]) ; } for (p = 0 ; p < nz ; p++) { fscanf (f, "%lg", &Ax [p]) ; } for (j = 0 ; j < n ; j++) { fscanf (f, LDL_ID , &P [j]) ; } fclose (f) ; /* ------------------------------------------------------------------ */ /* check the matrix A and the permutation P */ /* ------------------------------------------------------------------ */ ALLOC_MEMORY (Flag, LDL_int, n) ; /* To test the error-checking routines, some of the input matrices * are not valid. So this error is expected to occur. */ if (!LDL_valid_matrix (n, Ap, Ai) || !LDL_valid_perm (n, P, Flag)) { printf (PROGRAM ": invalid matrix and/or permutation\n") ; FREE_MEMORY (P, LDL_int) ; FREE_MEMORY (Ap, LDL_int) ; FREE_MEMORY (Ai, LDL_int) ; FREE_MEMORY (Ax, double) ; FREE_MEMORY (Flag, LDL_int) ; continue ; } /* ------------------------------------------------------------------ */ /* get the AMD permutation, if available */ /* ------------------------------------------------------------------ */ #ifdef USE_AMD /* recompute the permutation with AMD */ /* Assume that AMD produces a valid permutation P. */ #ifdef LDL_LONG if (amd_l_order (n, Ap, Ai, P, (double *) NULL, Info) < AMD_OK) { printf (PROGRAM ": call to AMD failed\n") ; EXIT_ERROR ; } amd_l_control ((double *) NULL) ; amd_l_info (Info) ; #else if (amd_order (n, Ap, Ai, P, (double *) NULL, Info) < AMD_OK) { printf (PROGRAM ": call to AMD failed\n") ; EXIT_ERROR ; } amd_control ((double *) NULL) ; amd_info (Info) ; #endif #endif /* ------------------------------------------------------------------ */ /* allocate workspace and the first part of LDL factorization */ /* ------------------------------------------------------------------ */ ALLOC_MEMORY (Pinv, LDL_int, n) ; ALLOC_MEMORY (Y, double, n) ; ALLOC_MEMORY (Pattern, LDL_int, n) ; ALLOC_MEMORY (Lnz, LDL_int, n) ; ALLOC_MEMORY (Lp, LDL_int, n+1) ; ALLOC_MEMORY (Parent, LDL_int, n) ; ALLOC_MEMORY (D, double, n) ; ALLOC_MEMORY (B, double, n) ; ALLOC_MEMORY (X, double, n) ; /* ------------------------------------------------------------------ */ /* factorize twice, with and without permutation */ /* ------------------------------------------------------------------ */ for (trial = 1 ; trial <= 2 ; trial++) { if (trial == 1) { printf ("Factorize PAP'=LDL' and solve Ax=b\n") ; Perm = P ; PermInv = Pinv ; } else { printf ("Factorize A=LDL' and solve Ax=b\n") ; Perm = (LDL_int *) NULL ; PermInv = (LDL_int *) NULL ; } /* -------------------------------------------------------------- */ /* symbolic factorization to get Lp, Parent, Lnz, and Pinv */ /* -------------------------------------------------------------- */ LDL_symbolic (n, Ap, Ai, Lp, Parent, Lnz, Flag, Perm, PermInv) ; lnz = Lp [n] ; /* find # of nonzeros in L, and flop count for LDL_numeric */ flops = 0 ; for (j = 0 ; j < n ; j++) { flops += ((double) Lnz [j]) * (Lnz [j] + 2) ; } printf ("Nz in L: "LDL_ID" Flop count: %g\n", lnz, flops) ; /* -------------------------------------------------------------- */ /* allocate remainder of L, of size lnz */ /* -------------------------------------------------------------- */ ALLOC_MEMORY (Li, LDL_int, lnz) ; ALLOC_MEMORY (Lx, double, lnz) ; /* -------------------------------------------------------------- */ /* numeric factorization to get Li, Lx, and D */ /* -------------------------------------------------------------- */ d = LDL_numeric (n, Ap, Ai, Ax, Lp, Parent, Lnz, Li, Lx, D, Y, Flag, Pattern, Perm, PermInv) ; /* -------------------------------------------------------------- */ /* solve, or report singular case */ /* -------------------------------------------------------------- */ if (d != n) { printf ("Ax=b not solved since D("LDL_ID","LDL_ID") is zero.\n", d, d) ; } else { /* construct the right-hand-side, B */ for (i = 0 ; i < n ; i++) { B [i] = 1 + ((double) i) / 100 ; } /* solve Ax=b */ if (trial == 1) { /* the factorization is LDL' = PAP' */ LDL_perm (n, Y, B, P) ; /* y = Pb */ LDL_lsolve (n, Y, Lp, Li, Lx) ; /* y = L\y */ LDL_dsolve (n, Y, D) ; /* y = D\y */ LDL_ltsolve (n, Y, Lp, Li, Lx) ; /* y = L'\y */ LDL_permt (n, X, Y, P) ; /* x = P'y */ } else { /* the factorization is LDL' = A */ for (i = 0 ; i < n ; i++) /* x = b */ { X [i] = B [i] ; } LDL_lsolve (n, X, Lp, Li, Lx) ; /* x = L\x */ LDL_dsolve (n, X, D) ; /* x = D\x */ LDL_ltsolve (n, X, Lp, Li, Lx) ; /* x = L'\x */ } /* compute the residual y = Ax-b */ /* note that this code can tolerate a jumbled matrix */ for (i = 0 ; i < n ; i++) { Y [i] = -B [i] ; } for (j = 0 ; j < n ; j++) { for (p = Ap [j] ; p < Ap [j+1] ; p++) { Y [Ai [p]] += Ax [p] * X [j] ; } } /* rnorm = norm (y, inf) */ rnorm = 0 ; for (i = 0 ; i < n ; i++) { r = (Y [i] > 0) ? (Y [i]) : (-Y [i]) ; rnorm = (r > rnorm) ? (r) : (rnorm) ; } maxrnorm = (rnorm > maxrnorm) ? (rnorm) : (maxrnorm) ; printf ("relative maxnorm of residual: %g\n", rnorm) ; } /* -------------------------------------------------------------- */ /* free the size-lnz part of L */ /* -------------------------------------------------------------- */ FREE_MEMORY (Li, LDL_int) ; FREE_MEMORY (Lx, double) ; } /* free everything */ FREE_MEMORY (P, LDL_int) ; FREE_MEMORY (Ap, LDL_int) ; FREE_MEMORY (Ai, LDL_int) ; FREE_MEMORY (Ax, double) ; FREE_MEMORY (Pinv, LDL_int) ; FREE_MEMORY (Y, double) ; FREE_MEMORY (Flag, LDL_int) ; FREE_MEMORY (Pattern, LDL_int) ; FREE_MEMORY (Lnz, LDL_int) ; FREE_MEMORY (Lp, LDL_int) ; FREE_MEMORY (Parent, LDL_int) ; FREE_MEMORY (D, double) ; FREE_MEMORY (B, double) ; FREE_MEMORY (X, double) ; } printf ("\nLargest residual during all tests: %g\n", maxrnorm) ; if (maxrnorm < 1e-8) { printf ("\n" PROGRAM ": all tests passed\n") ; EXIT_OK ; } else { printf ("\n" PROGRAM ": one more tests failed (residual too high)\n") ; EXIT_ERROR ; } }
CrsGraph_AMD::NewTypeRef CrsGraph_AMD:: operator()( OriginalTypeRef orig ) { origObj_ = &orig; int n = orig.NumMyRows(); int nnz = orig.NumMyNonzeros(); //create std CRS format std::vector<int> ia(n+1,0); std::vector<int> ja(nnz); int cnt; for( int i = 0; i < n; ++i ) { int * tmpP = &ja[ia[i]]; orig.ExtractMyRowCopy( i, nnz-ia[i], cnt, tmpP ); ia[i+1] = ia[i] + cnt; } //trim down to local only std::vector<int> iat(n+1); std::vector<int> jat(nnz); int loc = 0; for( int i = 0; i < n; ++i ) { iat[i] = loc; for( int j = ia[i]; j < ia[i+1]; ++j ) { if( ja[j] < n ) jat[loc++] = ja[j]; else break; } } iat[n] = loc; if( verbose_ ) { std::cout << "Orig Graph\n"; std::cout << orig << std::endl; std::cout << "-----------------------------------------\n"; std::cout << "CRS Format Graph\n"; std::cout << "-----------------------------------------\n"; for( int i = 0; i < n; ++i ) { std::cout << i << ": " << iat[i+1] << ": "; for( int j = iat[i]; j<iat[i+1]; ++j ) std::cout << " " << jat[j]; std::cout << std::endl; } std::cout << "-----------------------------------------\n"; } std::vector<int> perm(n); std::vector<double> info(AMD_INFO); amd_order( n, &iat[0], &jat[0], &perm[0], NULL, &info[0] ); if( info[AMD_STATUS] == AMD_INVALID ) std::cout << "AMD ORDERING: Invalid!!!!\n"; if( verbose_ ) { std::cout << "-----------------------------------------\n"; std::cout << "AMD Output\n"; std::cout << "-----------------------------------------\n"; std::cout << "STATUS: " << info[AMD_STATUS] << std::endl; std::cout << "SYMM: " << info[AMD_SYMMETRY] << std::endl; std::cout << "N: " << info[AMD_N] << std::endl; std::cout << "NZ: " << info[AMD_NZ] << std::endl; std::cout << "SYMM: " << info[AMD_SYMMETRY] << std::endl; std::cout << "NZDIAG: " << info[AMD_NZDIAG] << std::endl; std::cout << "NZ A+At: " << info[AMD_NZ_A_PLUS_AT] << std::endl; std::cout << "NDENSE: " << info[AMD_SYMMETRY] << std::endl; std::cout << "Perm\n"; for( int i = 0; i<n; ++i ) std::cout << perm[i] << std::endl; std::cout << "-----------------------------------------\n"; } //Generate New Domain and Range Maps //for now, assume they start out as identical const Epetra_BlockMap & OldMap = orig.RowMap(); int nG = orig.NumGlobalRows(); std::vector<int> newElements( n ); for( int i = 0; i < n; ++i ) newElements[i] = OldMap.GID( perm[i] ); NewMap_ = new Epetra_Map( nG, n, &newElements[0], OldMap.IndexBase(), OldMap.Comm() ); if( verbose_ ) { std::cout << "Old Map\n"; std::cout << OldMap << std::endl; std::cout << "New Map\n"; std::cout << *NewMap_ << std::endl; } //Generate New Graph NewGraph_ = new Epetra_CrsGraph( Copy, *NewMap_, 0 ); Epetra_Import Importer( *NewMap_, OldMap ); NewGraph_->Import( orig, Importer, Insert ); NewGraph_->FillComplete(); if( verbose_ ) { std::cout << "New CrsGraph\n"; std::cout << *NewGraph_ << std::endl; } newObj_ = NewGraph_; return *NewGraph_; }
std::vector<int> cAMD_Interf::DoRank(bool show) { Im2D_Bits<1> aM(1,1); if (show) aM = Im2D_Bits<1>(mNb,mNb,0); std::vector<int> Ap,Ai,P; Ap.push_back(0); for (int aK=0 ; aK<mNb ; aK++) { std::vector<int> & aVK = mV[aK]; std::sort(aVK.begin(),aVK.end()); aVK.erase(std::unique(aVK.begin(),aVK.end()),aVK.end()); for (int aI=0;aI<int(aVK.size()) ; aI++) { Ai.push_back(aVK[aI]); if (show) { aM.set(aK,aVK[aI],1); } // std::cout << " " << aVK[aI] ; } Ap.push_back(Ap.back()+aVK.size()); P.push_back(0); // std::cout << "\n"; } double Control [AMD_CONTROL] , Info [AMD_INFO] ; amd_defaults (Control) ; // amd_control (Control) ; int result; result = amd_order(mNb,VData(Ap),VData(Ai), VData(P),Control,Info); ELISE_ASSERT(result==0,"amd_order"); //std::cout << "RES = " << result << "\n"; std::vector<int> aRes(mNb,-1); for (int aK=0 ; aK<mNb ; aK++) { aRes[P[aK]] = aK; } if (show) { for (int aK=0 ; aK<mNb ; aK++) { } std::cout << "\n"; } // result = amd_order (n, Ap, Ai, P, Control, Info) ; if (show) { Im2D_Bits<1> aM2(mNb,mNb); for (int aX=0 ; aX<mNb ; aX++) { for (int aY=0 ; aY<mNb ; aY++) { aM2.set(aRes[aX],aRes[aY],aM.get(aX,aY)); // aM2.set(aX,aY,aM.get(P[aX],P[aY])); } } ShowMat(aM); std::cout << "\n"; ShowMat(aM2); } return aRes; }
int amd_demo_1 (void) { /* The symmetric can_24 Harwell/Boeing matrix, including upper and lower * triangular parts, and the diagonal entries. Note that this matrix is * 0-based, with row and column indices in the range 0 to n-1. */ int n = 24, nz, Ap [ ] = { 0, 9, 15, 21, 27, 33, 39, 48, 57, 61, 70, 76, 82, 88, 94, 100, 106, 110, 119, 128, 137, 143, 152, 156, 160 }, Ai [ ] = { /* column 0: */ 0, 5, 6, 12, 13, 17, 18, 19, 21, /* column 1: */ 1, 8, 9, 13, 14, 17, /* column 2: */ 2, 6, 11, 20, 21, 22, /* column 3: */ 3, 7, 10, 15, 18, 19, /* column 4: */ 4, 7, 9, 14, 15, 16, /* column 5: */ 0, 5, 6, 12, 13, 17, /* column 6: */ 0, 2, 5, 6, 11, 12, 19, 21, 23, /* column 7: */ 3, 4, 7, 9, 14, 15, 16, 17, 18, /* column 8: */ 1, 8, 9, 14, /* column 9: */ 1, 4, 7, 8, 9, 13, 14, 17, 18, /* column 10: */ 3, 10, 18, 19, 20, 21, /* column 11: */ 2, 6, 11, 12, 21, 23, /* column 12: */ 0, 5, 6, 11, 12, 23, /* column 13: */ 0, 1, 5, 9, 13, 17, /* column 14: */ 1, 4, 7, 8, 9, 14, /* column 15: */ 3, 4, 7, 15, 16, 18, /* column 16: */ 4, 7, 15, 16, /* column 17: */ 0, 1, 5, 7, 9, 13, 17, 18, 19, /* column 18: */ 0, 3, 7, 9, 10, 15, 17, 18, 19, /* column 19: */ 0, 3, 6, 10, 17, 18, 19, 20, 21, /* column 20: */ 2, 10, 19, 20, 21, 22, /* column 21: */ 0, 2, 6, 10, 11, 19, 20, 21, 22, /* column 22: */ 2, 20, 21, 22, /* column 23: */ 6, 11, 12, 23 } ; int P [24], Pinv [24], i, j, k, jnew, p, inew, result ; double Control [AMD_CONTROL], Info [AMD_INFO] ; char A [24][24] ; /* here is an example of how to use AMD_VERSION. This code will work in * any version of AMD. */ #if defined(AMD_VERSION) && (AMD_VERSION >= AMD_VERSION_CODE(1,2)) printf ("AMD version %d.%d, date: %s\n", AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_DATE) ; #else printf ("AMD version: 1.1 or earlier\n") ; #endif printf ("AMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24:\n") ; /* get the default parameters, and print them */ amd_defaults (Control) ; amd_control (Control) ; /* print the input matrix */ nz = Ap [n] ; printf ("\nInput matrix: %d-by-%d, with %d entries.\n" " Note that for a symmetric matrix such as this one, only the\n" " strictly lower or upper triangular parts would need to be\n" " passed to AMD, since AMD computes the ordering of A+A'. The\n" " diagonal entries are also not needed, since AMD ignores them.\n" , n, n, nz) ; for (j = 0 ; j < n ; j++) { printf ("\nColumn: %d, number of entries: %d, with row indices in" " Ai [%d ... %d]:\n row indices:", j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; printf (" %d", i) ; } printf ("\n") ; } /* print a character plot of the input matrix. This is only reasonable * because the matrix is small. */ printf ("\nPlot of input matrix pattern:\n") ; for (j = 0 ; j < n ; j++) { for (i = 0 ; i < n ; i++) A [i][j] = '.' ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; A [i][j] = 'X' ; } } printf (" ") ; for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; printf ("\n") ; for (i = 0 ; i < n ; i++) { printf ("%2d: ", i) ; for (j = 0 ; j < n ; j++) { printf (" %c", A [i][j]) ; } printf ("\n") ; } /* order the matrix */ result = amd_order (n, Ap, Ai, P, Control, Info) ; printf ("return value from amd_order: %d (should be %d)\n", result, AMD_OK) ; /* print the statistics */ amd_info (Info) ; if (result != AMD_OK) { printf ("AMD failed\n") ; ElEXIT (1,"AMD DEMO") ; } /* print the permutation vector, P, and compute the inverse permutation */ printf ("Permutation vector:\n") ; for (k = 0 ; k < n ; k++) { /* row/column j is the kth row/column in the permuted matrix */ j = P [k] ; Pinv [j] = k ; printf (" %2d", j) ; } printf ("\n\n") ; printf ("Inverse permutation vector:\n") ; for (j = 0 ; j < n ; j++) { k = Pinv [j] ; printf (" %2d", k) ; } printf ("\n\n") ; /* print a character plot of the permuted matrix. */ printf ("\nPlot of permuted matrix pattern:\n") ; for (jnew = 0 ; jnew < n ; jnew++) { j = P [jnew] ; for (inew = 0 ; inew < n ; inew++) A [inew][jnew] = '.' ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { inew = Pinv [Ai [p]] ; A [inew][jnew] = 'X' ; } } printf (" ") ; for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; printf ("\n") ; for (i = 0 ; i < n ; i++) { printf ("%2d: ", i) ; for (j = 0 ; j < n ; j++) { printf (" %c", A [i][j]) ; } printf ("\n") ; } return (0) ; }
int order(int n, const int *Ap, const int *Ai, int *perm, double *control, double *info) { return (amd_order(n, Ap, Ai, perm, control, info)); }
int main (int argc, char **argv) { int i, j, k, n, nz, *Ap, *Ai, *Ti, *Tj, status, *Pamd, nrow, ncol, rhs ; double *Ax, *b, *x, Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], aij, *Tx, *r, amd_Control [AMD_CONTROL], amd_Info [AMD_INFO], tamd [2], stats [2], droptol ; void *Symbolic, *Numeric ; FILE *f, *f2 ; char s [SMAX] ; /* ---------------------------------------------------------------------- */ /* set controls */ /* ---------------------------------------------------------------------- */ printf ("\n===========================================================\n" "=== UMFPACK v%d.%d.%d ========================================\n" "===========================================================\n", UMFPACK_MAIN_VERSION, UMFPACK_SUB_VERSION, UMFPACK_SUBSUB_VERSION) ; umfpack_di_defaults (Control) ; Control [UMFPACK_PRL] = 3 ; Control [UMFPACK_BLOCK_SIZE] = 32 ; f = fopen ("tmp/control.umf4", "r") ; if (f != (FILE *) NULL) { printf ("Reading control file tmp/control.umf4\n") ; for (i = 0 ; i < UMFPACK_CONTROL ; i++) { fscanf (f, "%lg\n", & Control [i]) ; } fclose (f) ; } if (argc > 1) { char *t = argv [1] ; /* get the strategy */ if (t [0] == 'u') { Control [UMFPACK_STRATEGY] = UMFPACK_STRATEGY_UNSYMMETRIC ; } else if (t [0] == 'a') { Control [UMFPACK_STRATEGY] = UMFPACK_STRATEGY_AUTO ; } else if (t [0] == 's') { Control [UMFPACK_STRATEGY] = UMFPACK_STRATEGY_SYMMETRIC ; } else if (t [0] == '2') { printf ("unrecognized strategy: %s\n", argv [1]) ; } else if (t [0] == 'U') { Control [UMFPACK_STRATEGY] = UMFPACK_STRATEGY_UNSYMMETRIC ; Control [UMFPACK_SCALE] = UMFPACK_SCALE_MAX ; } else if (t [0] == 'A') { Control [UMFPACK_STRATEGY] = UMFPACK_STRATEGY_AUTO ; Control [UMFPACK_SCALE] = UMFPACK_SCALE_MAX ; } else if (t [0] == 'S') { Control [UMFPACK_STRATEGY] = UMFPACK_STRATEGY_SYMMETRIC ; Control [UMFPACK_SCALE] = UMFPACK_SCALE_MAX ; } else if (t [0] == 'T') { printf ("unrecognized strategy: %s\n", argv [1]) ; } else { printf ("unrecognized strategy: %s\n", argv [1]) ; } if (t [1] == 'n') { /* no aggressive absorption */ Control [UMFPACK_AGGRESSIVE] = FALSE ; } } if (argc > 2) { /* get the drop tolerance */ sscanf (argv [2], "%lg", &droptol) ; printf ("droptol %g\n", droptol) ; Control [UMFPACK_DROPTOL] = droptol ; } umfpack_di_report_control (Control) ; /* ---------------------------------------------------------------------- */ /* open the matrix file (tmp/A) */ /* ---------------------------------------------------------------------- */ printf ("File: tmp/A\n") ; f = fopen ("tmp/A", "r") ; if (!f) { printf ("Unable to open file\n") ; exit (1) ; } /* ---------------------------------------------------------------------- */ /* get n and nz */ /* ---------------------------------------------------------------------- */ printf ("File: tmp/Asize\n") ; f2 = fopen ("tmp/Asize", "r") ; if (f2) { fscanf (f2, "%d %d %d\n", &nrow, &ncol, &nz) ; fclose (f2) ; } else { nrow = 1 ; ncol = 1 ; } nz = 0 ; while (fgets (s, SMAX, f) != (char *) NULL) { sscanf (s, "%d %d %lg", &i, &j, &aij) ; #ifdef ZERO_BASED /* matrix is zero based */ i++ ; j++ ; #endif nrow = MAX (nrow, i) ; ncol = MAX (ncol, j) ; nz++ ; } fclose (f) ; n = MAX (nrow, ncol) ; printf ("n %d nrow %d ncol %d nz %d\n", n, nrow, ncol, nz) ; /* ---------------------------------------------------------------------- */ /* allocate space for the input triplet form */ /* ---------------------------------------------------------------------- */ Ti = (int *) malloc (nz * sizeof (int)) ; Tj = (int *) malloc (nz * sizeof (int)) ; Tx = (double *) malloc (nz * sizeof (double)) ; if (!Ti || !Tj || !Tx) { printf ("out of memory for input matrix\n") ; exit (1) ; } /* ---------------------------------------------------------------------- */ /* read in the triplet form */ /* ---------------------------------------------------------------------- */ f2 = fopen ("tmp/A", "r") ; if (!f2) { printf ("Unable to open file\n") ; exit (1) ; } k = 0 ; while (fgets (s, SMAX, f2) != (char *) NULL) { sscanf (s, "%d %d %lg", &i, &j, &aij) ; #ifndef ZERO_BASED i-- ; /* convert to 0-based */ j-- ; #endif if (k >= nz) { printf ("Error! Matrix size is wrong\n") ; exit (1) ; } Ti [k] = i ; Tj [k] = j ; Tx [k] = aij ; k++ ; } fclose (f2) ; (void) umfpack_di_report_triplet (nrow, ncol, nz, Ti, Tj, Tx, Control) ; /* ---------------------------------------------------------------------- */ /* convert to column form */ /* ---------------------------------------------------------------------- */ /* convert to column form */ Ap = (int *) malloc ((n+1) * sizeof (int)) ; Ai = (int *) malloc (nz * sizeof (int)) ; Ax = (double *) malloc (nz * sizeof (double)) ; b = (double *) malloc (n * sizeof (double)) ; r = (double *) malloc (n * sizeof (double)) ; x = (double *) malloc (n * sizeof (double)) ; if (!Ap || !Ai || !Ax || !b || !r) { printf ("out of memory") ; exit (1) ; } umfpack_tic (stats) ; status = umfpack_di_triplet_to_col (nrow, ncol, nz, Ti, Tj, Tx, Ap, Ai, Ax, (int *) NULL) ; umfpack_toc (stats) ; printf ("triplet-to-col time: wall %g cpu %g\n", stats [0], stats [1]) ; if (status != UMFPACK_OK) { umfpack_di_report_status (Control, status) ; printf ("umfpack_di_triplet_to_col failed") ; exit (1) ; } /* print the column-form of A */ (void) umfpack_di_report_matrix (nrow, ncol, Ap, Ai, Ax, 1, Control) ; /* b = A * xtrue */ rhs = FALSE ; if (nrow == ncol) { f = fopen ("tmp/b", "r") ; if (f != (FILE *) NULL) { printf ("Reading tmp/b\n") ; rhs = TRUE ; for (i = 0 ; i < n ; i++) { fscanf (f, "%lg\n", &b [i]) ; } fclose (f) ; } else { Atimesx (n, Ap, Ai, Ax, b, FALSE) ; } } /* ---------------------------------------------------------------------- */ /* free the triplet form */ /* ---------------------------------------------------------------------- */ free (Ti) ; free (Tj) ; free (Tx) ; /* ---------------------------------------------------------------------- */ /* symbolic factorization */ /* ---------------------------------------------------------------------- */ status = umfpack_di_symbolic (nrow, ncol, Ap, Ai, Ax, &Symbolic, Control, Info) ; umfpack_di_report_info (Control, Info) ; if (status != UMFPACK_OK) { umfpack_di_report_status (Control, status) ; printf ("umfpack_di_symbolic failed") ; exit (1) ; } /* print the symbolic factorization */ (void) umfpack_di_report_symbolic (Symbolic, Control) ; /* ---------------------------------------------------------------------- */ /* numeric factorization */ /* ---------------------------------------------------------------------- */ status = umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, Control, Info); if (status < UMFPACK_OK) { umfpack_di_report_info (Control, Info) ; umfpack_di_report_status (Control, status) ; fprintf (stderr, "umfpack_di_numeric failed: %d\n", status) ; printf ("umfpack_di_numeric failed\n") ; exit (1) ; } /* print the numeric factorization */ (void) umfpack_di_report_numeric (Numeric, Control) ; /* ---------------------------------------------------------------------- */ /* solve Ax=b */ /* ---------------------------------------------------------------------- */ if (nrow == ncol && status == UMFPACK_OK) { status = umfpack_di_solve (UMFPACK_A, Ap, Ai, Ax, x, b, Numeric, Control, Info) ; umfpack_di_report_info (Control, Info) ; umfpack_di_report_status (Control, status) ; if (status < UMFPACK_OK) { printf ("umfpack_di_solve failed\n") ; exit (1) ; } (void) umfpack_di_report_vector (n, x, Control) ; printf ("relative maxnorm of residual, ||Ax-b||/||b||: %g\n", resid (n, Ap, Ai, Ax, x, r, b, FALSE)) ; if (!rhs) { printf ("relative maxnorm of error, ||x-xtrue||/||xtrue||: %g\n\n", err (n, x)) ; } f = fopen ("tmp/x", "w") ; if (f != (FILE *) NULL) { printf ("Writing tmp/x\n") ; for (i = 0 ; i < n ; i++) { fprintf (f, "%30.20e\n", x [i]) ; } fclose (f) ; } else { printf ("Unable to write output x!\n") ; exit (1) ; } f = fopen ("tmp/info.umf4", "w") ; if (f != (FILE *) NULL) { printf ("Writing tmp/info.umf4\n") ; for (i = 0 ; i < UMFPACK_INFO ; i++) { fprintf (f, "%30.20e\n", Info [i]) ; } fclose (f) ; } else { printf ("Unable to write output info!\n") ; exit (1) ; } } else { /* don't solve, just report the results */ umfpack_di_report_info (Control, Info) ; umfpack_di_report_status (Control, status) ; } /* ---------------------------------------------------------------------- */ /* free the Symbolic and Numeric factorization */ /* ---------------------------------------------------------------------- */ umfpack_di_free_symbolic (&Symbolic) ; umfpack_di_free_numeric (&Numeric) ; printf ("umf4 done, strategy: %g\n", Control [UMFPACK_STRATEGY]) ; /* ---------------------------------------------------------------------- */ /* test just AMD ordering (not part of UMFPACK, but a separate test) */ /* ---------------------------------------------------------------------- */ /* first make the matrix square */ if (ncol < n) { for (j = ncol+1 ; j <= n ; j++) { Ap [j] = Ap [ncol] ; } } printf ( "\n\n===========================================================\n" "=== AMD ===================================================\n" "===========================================================\n") ; printf ("\n\n------- Now trying the AMD ordering. This not part of\n" "the UMFPACK analysis or factorization, above, but a separate\n" "test of just the AMD ordering routine.\n") ; Pamd = (int *) malloc (n * sizeof (int)) ; if (!Pamd) { printf ("out of memory\n") ; exit (1) ; } amd_defaults (amd_Control) ; amd_control (amd_Control) ; umfpack_tic (tamd) ; status = amd_order (n, Ap, Ai, Pamd, amd_Control, amd_Info) ; umfpack_toc (tamd) ; printf ("AMD ordering time: cpu %10.2f wall %10.2f\n", tamd [1], tamd [0]) ; if (status != AMD_OK) { printf ("amd failed: %d\n", status) ; exit (1) ; } amd_info (amd_Info) ; free (Pamd) ; printf ("AMD test done\n") ; free (Ap) ; free (Ai) ; free (Ax) ; free (b) ; free (r) ; free (x) ; return (0) ; }
void amd(csc *A, int *p){ amd_order(A->n, A->Ap, A->Ai, p, (double*)NULL, (double*)NULL); }
int main (int argc, char **argv) { /* The symmetric can_24 Harwell/Boeing matrix (jumbled, and not symmetric). * Since AMD operates on A+A', only A(i,j) or A(j,i) need to be specified, * or both. The diagonal entries are optional (some are missing). * There are many duplicate entries, which must be removed. */ int n = 24, nz, Ap [ ] = { 0, 9, 14, 20, 28, 33, 37, 44, 53, 58, 63, 63, 66, 69, 72, 75, 78, 82, 86, 91, 97, 101, 112, 112, 116 }, Ai [ ] = { /* column 0: */ 0, 17, 18, 21, 5, 12, 5, 0, 13, /* column 1: */ 14, 1, 8, 13, 17, /* column 2: */ 2, 20, 11, 6, 11, 22, /* column 3: */ 3, 3, 10, 7, 18, 18, 15, 19, /* column 4: */ 7, 9, 15, 14, 16, /* column 5: */ 5, 13, 6, 17, /* column 6: */ 5, 0, 11, 6, 12, 6, 23, /* column 7: */ 3, 4, 9, 7, 14, 16, 15, 17, 18, /* column 8: */ 1, 9, 14, 14, 14, /* column 9: */ 7, 13, 8, 1, 17, /* column 10: */ /* column 11: */ 2, 12, 23, /* column 12: */ 5, 11, 12, /* column 13: */ 0, 13, 17, /* column 14: */ 1, 9, 14, /* column 15: */ 3, 15, 16, /* column 16: */ 16, 4, 4, 15, /* column 17: */ 13, 17, 19, 17, /* column 18: */ 15, 17, 19, 9, 10, /* column 19: */ 17, 19, 20, 0, 6, 10, /* column 20: */ 22, 10, 20, 21, /* column 21: */ 6, 2, 10, 19, 20, 11, 21, 22, 22, 22, 22, /* column 22: */ /* column 23: */ 12, 11, 12, 23 } ; int Rp [25], Ri [116] ; int P [24], Pinv [24], i, j, k, jnew, p, inew, result ; double Control [AMD_CONTROL], Info [AMD_INFO] ; char A [24][24] ; printf ("AMD demo, with a jumbled version of the 24-by-24\n") ; printf ("Harwell/Boeing matrix, can_24:\n") ; /* get the default parameters, and print them */ amd_defaults (Control) ; amd_control (Control) ; /* print the input matrix */ nz = Ap [n] ; printf ("\nJumbled input matrix: %d-by-%d, with %d entries.\n" " Note that for a symmetric matrix such as this one, only the\n" " strictly lower or upper triangular parts would need to be\n" " passed to AMD, since AMD computes the ordering of A+A'. The\n" " diagonal entries are also not needed, since AMD ignores them.\n" " This version of the matrix has jumbled columns and duplicate\n" " row indices, and must be fixed by amd_preprocess prior to\n" " ordering it with amd_order.\n" , n, n, nz) ; for (j = 0 ; j < n ; j++) { printf ("\nColumn: %d, number of entries: %d, with row indices in" " Ai [%d ... %d]:\n row indices:", j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; printf (" %d", i) ; } printf ("\n") ; } /* print a character plot of the input matrix. This is only reasonable * because the matrix is small. */ printf ("\nPlot of (jumbled) input matrix pattern:\n") ; for (j = 0 ; j < n ; j++) { for (i = 0 ; i < n ; i++) A [i][j] = '.' ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; A [i][j] = 'X' ; } } printf (" ") ; for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; printf ("\n") ; for (i = 0 ; i < n ; i++) { printf ("%2d: ", i) ; for (j = 0 ; j < n ; j++) { printf (" %c", A [i][j]) ; } printf ("\n") ; } /* sort, remove duplicates, and transpose A to get R */ result = amd_preprocess (n, Ap, Ai, Rp, Ri) ; printf ("return value from amd_preprocess: %d (should be %d)\n", result, AMD_OK) ; if (result != AMD_OK) { printf ("AMD failed\n") ; exit (1) ; } /* print the sorted/transposed matrix R */ printf ("\nThe column-oriented form of the sorted/transposed matrix R:\n"); for (j = 0 ; j < n ; j++) { printf ("\nColumn: %d, number of entries: %d, with row indices in" " Ri [%d ... %d]:\n row indices:", j, Rp [j+1] - Rp [j], Rp [j], Rp [j+1]-1) ; for (p = Rp [j] ; p < Rp [j+1] ; p++) { i = Ri [p] ; printf (" %d", i) ; } printf ("\n") ; } /* print a character plot of the matrix R. */ printf ("\nPlot of the sorted/transposed matrix R:\n") ; for (j = 0 ; j < n ; j++) { for (i = 0 ; i < n ; i++) A [i][j] = '.' ; for (p = Rp [j] ; p < Rp [j+1] ; p++) { i = Ri [p] ; A [i][j] = 'X' ; } } printf (" ") ; for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; printf (" \n") ; for (i = 0 ; i < n ; i++) { printf ("%2d: ", i) ; for (j = 0 ; j < n ; j++) { printf (" %c", A [i][j]) ; } printf (" \n") ; } /* print a character plot of the matrix R+R'. */ printf ("\nPlot of symmetric matrix to be ordered by amd_order:\n") ; for (j = 0 ; j < n ; j++) { for (i = 0 ; i < n ; i++) A [i][j] = '.' ; } for (j = 0 ; j < n ; j++) { A [j][j] = 'X' ; for (p = Rp [j] ; p < Rp [j+1] ; p++) { i = Ri [p] ; A [i][j] = 'X' ; A [j][i] = 'X' ; } } printf (" ") ; for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; printf ("\n") ; for (i = 0 ; i < n ; i++) { printf ("%2d: ", i) ; for (j = 0 ; j < n ; j++) { printf (" %c", A [i][j]) ; } printf ("\n") ; } /* order the matrix */ result = amd_order (n, Rp, Ri, P, Control, Info) ; printf ("return value from amd_order: %d (should be %d)\n", result, AMD_OK) ; /* print the statistics */ amd_info (Info) ; if (result != AMD_OK) { printf ("AMD failed\n") ; exit (1) ; } /* print the permutation vector, P, and compute the inverse permutation */ printf ("Permutation vector:\n") ; for (k = 0 ; k < n ; k++) { /* row/column j is the kth row/column in the permuted matrix */ j = P [k] ; Pinv [j] = k ; printf (" %2d", j) ; } printf ("\n\n") ; printf ("Inverse permutation vector:\n") ; for (j = 0 ; j < n ; j++) { k = Pinv [j] ; printf (" %2d", k) ; } printf ("\n\n") ; /* print a character plot of the permuted matrix. */ printf ("\nPlot of (symmetrized) permuted matrix pattern:\n") ; for (j = 0 ; j < n ; j++) { for (i = 0 ; i < n ; i++) A [i][j] = '.' ; } for (jnew = 0 ; jnew < n ; jnew++) { j = P [jnew] ; A [jnew][jnew] = 'X' ; for (p = Rp [j] ; p < Rp [j+1] ; p++) { inew = Pinv [Ri [p]] ; A [inew][jnew] = 'X' ; A [jnew][inew] = 'X' ; } } printf (" ") ; for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; printf ("\n") ; for (i = 0 ; i < n ; i++) { printf ("%2d: ", i) ; for (j = 0 ; j < n ; j++) { printf (" %c", A [i][j]) ; } printf ("\n") ; } return (0) ; }
static int set_defaults(double *control) { int_t pos=0; int param_id; PyObject *param, *key, *value; #if PY_MAJOR_VERSION < 3 char *keystr; #endif char err_str[100]; amd_defaults(control); if (!(param = PyObject_GetAttrString(amd_module, "options")) || !PyDict_Check(param)){ PyErr_SetString(PyExc_AttributeError, "missing amd.options" "dictionary"); return 0; } while (PyDict_Next(param, &pos, &key, &value)) #if PY_MAJOR_VERSION >= 3 if ((PyUnicode_Check(key)) && get_param_idx(_PyUnicode_AsString(key),¶m_id)) { if (!PyLong_Check(value) && !PyFloat_Check(value)){ sprintf(err_str, "invalid value for AMD parameter: %-.20s", _PyUnicode_AsString(key)); #else if ((keystr = PyString_AsString(key)) && get_param_idx(keystr, ¶m_id)) { if (!PyInt_Check(value) && !PyFloat_Check(value)){ sprintf(err_str, "invalid value for AMD parameter: " "%-.20s", keystr); #endif PyErr_SetString(PyExc_ValueError, err_str); Py_DECREF(param); return 0; } control[param_id] = PyFloat_AsDouble(value); } Py_DECREF(param); return 1; } static char doc_order[] = "Computes the approximate minimum degree ordering of a square " "matrix.\n\n" "p = order(A, uplo='L')\n\n" "PURPOSE\n" "Computes a permutation p that reduces fill-in in the Cholesky\n" "factorization of A[p,p].\n\n" "ARGUMENTS\n" "A square sparse matrix\n\n" "uplo 'L' or 'U'. If uplo is 'L', the lower triangular part\n" " of A is used and the upper triangular is ignored. If\n" " uplo is 'U', the upper triangular part is used and the\n" " lower triangular part is ignored.\n\n" "p 'i' matrix of length equal to the order of A"; static PyObject* order_c(PyObject *self, PyObject *args, PyObject *kwrds) { spmatrix *A; matrix *perm; #if PY_MAJOR_VERSION >= 3 int uplo_ = 'L'; #endif char uplo = 'L'; int j, k, n, nnz, alloc=0, info; int_t *rowind=NULL, *colptr=NULL; double control[AMD_CONTROL]; char *kwlist[] = {"A", "uplo", NULL}; #if PY_MAJOR_VERSION >= 3 if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|C", kwlist, &A, &uplo_)) return NULL; uplo = (char) uplo_; #else if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|c", kwlist, &A, &uplo)) return NULL; #endif if (!set_defaults(control)) return NULL; if (!SpMatrix_Check(A) || SP_NROWS(A) != SP_NCOLS(A)){ PyErr_SetString(PyExc_TypeError, "A must be a square sparse " "matrix"); return NULL; } if (uplo != 'U' && uplo != 'L') err_char("uplo", "'L', 'U'"); if (!(perm = (matrix *) Matrix_New((int)SP_NROWS(A),1,INT))) return PyErr_NoMemory(); n = SP_NROWS(A); for (nnz=0, j=0; j<n; j++) { if (uplo == 'L'){ for (k=SP_COL(A)[j]; k<SP_COL(A)[j+1] && SP_ROW(A)[k]<j; k++); nnz += SP_COL(A)[j+1] - k; } else { for (k=SP_COL(A)[j]; k<SP_COL(A)[j+1] && SP_ROW(A)[k] <= j; k++); nnz += k - SP_COL(A)[j]; } } if (nnz == SP_NNZ(A)){ colptr = (int_t *) SP_COL(A); rowind = (int_t *) SP_ROW(A); } else { alloc = 1; colptr = (int_t *) calloc(n+1, sizeof(int_t)); rowind = (int_t *) calloc(nnz, sizeof(int_t)); if (!colptr || !rowind) { Py_XDECREF(perm); free(colptr); free(rowind); return PyErr_NoMemory(); } colptr[0] = 0; for (j=0; j<n; j++) { if (uplo == 'L'){ for (k=SP_COL(A)[j]; k<SP_COL(A)[j+1] && SP_ROW(A)[k] < j; k++); nnz = SP_COL(A)[j+1] - k; colptr[j+1] = colptr[j] + nnz; memcpy(rowind + colptr[j], (int_t *) SP_ROW(A) + k, nnz*sizeof(int_t)); } else { for (k=SP_COL(A)[j]; k<SP_COL(A)[j+1] && SP_ROW(A)[k] <= j; k++); nnz = k - SP_COL(A)[j]; colptr[j+1] = colptr[j] + nnz; memcpy(rowind + colptr[j], (int_t *) (SP_ROW(A) + SP_COL(A)[j]), nnz*sizeof(int_t)); } } } info = amd_order(n, colptr, rowind, MAT_BUFI(perm), control, NULL); if (alloc){ free(colptr); free(rowind); } switch (info) { case AMD_OUT_OF_MEMORY: Py_XDECREF(perm); return PyErr_NoMemory(); case AMD_INVALID: Py_XDECREF(perm); return Py_BuildValue(""); case AMD_OK: return (PyObject *) perm; } return Py_BuildValue(""); } static PyMethodDef amd_functions[] = { {"order", (PyCFunction) order_c, METH_VARARGS|METH_KEYWORDS, doc_order}, {NULL} /* Sentinel */ }; #if PY_MAJOR_VERSION >= 3 static PyModuleDef amd_module_def = { PyModuleDef_HEAD_INIT, "amd", amd__doc__, -1, amd_functions, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit_amd(void) { if (!(amd_module = PyModule_Create(&amd_module_def))) return NULL; PyModule_AddObject(amd_module, "options", PyDict_New()); if (import_cvxopt() < 0) return NULL; return amd_module; } #else PyMODINIT_FUNC initamd(void) { amd_module = Py_InitModule3("cvxopt.amd", amd_functions, amd__doc__); PyModule_AddObject(amd_module, "options", PyDict_New()); if (import_cvxopt() < 0) return; }