Пример #1
0
static PyObject* splinsolve(PyObject *self, PyObject *args,
    PyObject *kwrds)
{
    spmatrix *A, *B, *X;
    matrix *P=NULL;
    int n, nnz;
    cholmod_sparse *Ac=NULL, *Bc=NULL, *Xc=NULL;
    cholmod_factor *L=NULL;
#if PY_MAJOR_VERSION >= 3
    int uplo_='L';
#endif
    char uplo='L';
    char *kwlist[] = {"A", "B", "p", "uplo", NULL};

    if (!set_options()) return NULL;
#if PY_MAJOR_VERSION >= 3
    if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|OC", kwlist, &A,
        &B, &P, &uplo_)) return NULL;
    uplo = (char) uplo_;
#else
    if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|Oc", kwlist, &A,
        &B, &P, &uplo)) return NULL;
#endif

    if (!SpMatrix_Check(A) || SP_NROWS(A) != SP_NCOLS(A))
        PY_ERR_TYPE("A is not a square sparse matrix");
    n = SP_NROWS(A);
    nnz = SP_NNZ(A);

    if (!SpMatrix_Check(B) || SP_ID(A) != SP_ID(B))
        PY_ERR_TYPE("B must be a sparse matrix of the same type as A");
    if (SP_NROWS(B) != n)
        PY_ERR(PyExc_ValueError, "incompatible dimensions for B");

    if (P) {
        if (!Matrix_Check(P) || MAT_ID(P) != INT) err_int_mtrx("p");
        if (MAT_LGT(P) != n) err_buf_len("p");
        if (!CHOL(check_perm)(P->buffer, n, n, &Common))
            PY_ERR(PyExc_ValueError, "not a valid permutation");
    }

    if (uplo != 'U' && uplo != 'L') err_char("uplo", "'L', 'U'");
    if (!(Ac = pack(A, uplo))) return PyErr_NoMemory();

    L = CHOL(analyze_p) (Ac, P ? MAT_BUFI(P): NULL, NULL, 0, &Common);
    if (Common.status != CHOLMOD_OK){
        CHOL(free_factor)(&L, &Common);
        CHOL(free_sparse)(&Ac, &Common);
        if (Common.status == CHOLMOD_OUT_OF_MEMORY)
            return PyErr_NoMemory();
        else {
            PyErr_SetString(PyExc_ValueError, "symbolic factorization "
                "failed");
            return NULL;
        }
    }

    CHOL(factorize) (Ac, L, &Common);
    CHOL(free_sparse)(&Ac, &Common);
    if (Common.status > 0) switch (Common.status) {
        case CHOLMOD_NOT_POSDEF:
            PyErr_SetObject(PyExc_ArithmeticError, Py_BuildValue("i",
                L->minor));
            CHOL(free_factor)(&L, &Common);
            return NULL;
            break;

        case CHOLMOD_DSMALL:
            /* This never happens unless we change the default value
             * of Common.dbound (0.0).  */
            if (L->is_ll)
                PyErr_Warn(PyExc_RuntimeWarning, "tiny diagonal "
                    "elements in L");
            else
                PyErr_Warn(PyExc_RuntimeWarning, "tiny diagonal "
                    "elements in D");
            break;

        default:
            PyErr_Warn(PyExc_UserWarning, "");
    }

    if (L->minor<n) {
        CHOL(free_factor)(&L, &Common);
        PY_ERR(PyExc_ArithmeticError, "singular matrix");
    }
    if (!(Bc = create_matrix(B))) {
      CHOL(free_factor)(&L, &Common);
      return PyErr_NoMemory();
    }

    Xc = CHOL(spsolve)(0, L, Bc, &Common);
    free_matrix(Bc);
    CHOL(free_factor)(&L, &Common);
    if (Common.status != CHOLMOD_OK){
        CHOL(free_sparse)(&Xc, &Common);
        if (Common.status == CHOLMOD_OUT_OF_MEMORY)
            return PyErr_NoMemory();
        else
            PY_ERR(PyExc_ValueError, "solve step failed");
    }

    if (!(X = SpMatrix_New(Xc->nrow, Xc->ncol,
        ((int_t*)Xc->p)[Xc->ncol], SP_ID(A)))) {
        CHOL(free_sparse)(&Xc, &Common);
        return PyErr_NoMemory();
    }
    memcpy(SP_COL(X), (int_t *) Xc->p, (Xc->ncol+1)*sizeof(int_t));
    memcpy(SP_ROW(X), (int_t *) Xc->i,
        ((int_t *) Xc->p)[Xc->ncol]*sizeof(int_t));
    memcpy(SP_VAL(X), (double *) Xc->x,
        ((int_t *) Xc->p)[Xc->ncol]*E_SIZE[SP_ID(X)]);
    CHOL(free_sparse)(&Xc, &Common);
    return (PyObject *) X;
}
Пример #2
0
static PyObject* linsolve(PyObject *self, PyObject *args,
    PyObject *kwrds)
{
    spmatrix *A;
    matrix *B, *P=NULL;
    int i, n, nnz, oB=0, ldB=0, nrhs=-1;
    cholmod_sparse *Ac=NULL;
    cholmod_factor *L=NULL;
    cholmod_dense *x=NULL, *b=NULL;
    void *b_old;
#if PY_MAJOR_VERSION >= 3
    int uplo_ = 'L';
#endif
    char uplo='L';
    char *kwlist[] = {"A", "B", "p", "uplo", "nrhs", "ldB", "offsetB",
        NULL};

    if (!set_options()) return NULL;
#if PY_MAJOR_VERSION >= 3
    if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|OCiii", kwlist,
        &A,  &B, &P, &uplo_, &nrhs, &ldB, &oB)) return NULL;
    uplo = (char) uplo_;
#else
    if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|Ociii", kwlist,
        &A,  &B, &P, &uplo, &nrhs, &ldB, &oB)) return NULL;
#endif

    if (!SpMatrix_Check(A) || SP_NROWS(A) != SP_NCOLS(A))
        PY_ERR_TYPE("A is not a sparse matrix");
    n = SP_NROWS(A);
    nnz = SP_NNZ(A);

    if (!Matrix_Check(B) || MAT_ID(B) != SP_ID(A))
        PY_ERR_TYPE("B must be a dense matrix of the same numerical "
            "type as A");
    if (nrhs < 0) nrhs = MAT_NCOLS(B);
    if (n == 0 || nrhs == 0) return Py_BuildValue("");
    if (ldB == 0) ldB = MAX(1,MAT_NROWS(B));
    if (ldB < MAX(1,n)) err_ld("ldB");
    if (oB < 0) err_nn_int("offsetB");
    if (oB + (nrhs-1)*ldB + n > MAT_LGT(B)) err_buf_len("B");

    if (P) {
        if (!Matrix_Check(P) || MAT_ID(P) != INT) err_int_mtrx("p");
        if (MAT_LGT(P) != n) err_buf_len("p");
        if (!CHOL(check_perm)(P->buffer, n, n, &Common))
            PY_ERR(PyExc_ValueError, "not a valid permutation");
    }
    if (uplo != 'U' && uplo != 'L') err_char("uplo", "'L', 'U'");

    if (!(Ac = pack(A, uplo))) return PyErr_NoMemory();
    L = CHOL(analyze_p)(Ac, P ? MAT_BUFI(P): NULL, NULL, 0, &Common);
    if (Common.status != CHOLMOD_OK){
        free_matrix(Ac);
        CHOL(free_sparse)(&Ac, &Common);
        CHOL(free_factor)(&L, &Common);
        if (Common.status == CHOLMOD_OUT_OF_MEMORY)
            return PyErr_NoMemory();
        else {
            PyErr_SetString(PyExc_ValueError, "symbolic factorization "
                "failed");
            return NULL;
        }
    }

    CHOL(factorize) (Ac, L, &Common);
    CHOL(free_sparse)(&Ac, &Common);
    if (Common.status < 0) {
        CHOL(free_factor)(&L, &Common);
        switch (Common.status) {
            case CHOLMOD_OUT_OF_MEMORY:
                return PyErr_NoMemory();

            default:
                PyErr_SetString(PyExc_ValueError, "factorization "
                    "failed");
                return NULL;
        }
    }
    if (Common.status > 0) switch (Common.status) {
        case CHOLMOD_NOT_POSDEF:
            PyErr_SetObject(PyExc_ArithmeticError,
                Py_BuildValue("i", L->minor));
            CHOL(free_factor)(&L, &Common);
            return NULL;
            break;

        case CHOLMOD_DSMALL:
            /* This never happens unless we change the default value
             * of Common.dbound (0.0).  */
            if (L->is_ll)
                PyErr_Warn(PyExc_RuntimeWarning, "tiny diagonal "
                    "elements in L");
            else
                PyErr_Warn(PyExc_RuntimeWarning, "tiny diagonal "
                    "elements in D");
            break;

        default:
            PyErr_Warn(PyExc_UserWarning, "");
    }

    if (L->minor<n) {
        CHOL(free_factor)(&L, &Common);
        PY_ERR(PyExc_ArithmeticError, "singular matrix");
    }
    b = CHOL(allocate_dense)(n, 1, n, (MAT_ID(B) == DOUBLE ?
        CHOLMOD_REAL : CHOLMOD_COMPLEX) , &Common);
    if (Common.status == CHOLMOD_OUT_OF_MEMORY) {
        CHOL(free_factor)(&L, &Common);
        CHOL(free_dense)(&b, &Common);
        return PyErr_NoMemory();
    }
    b_old = b->x;
    for (i=0; i<nrhs; i++) {
        b->x = MAT_BUF(B) + (i*ldB + oB)*E_SIZE[MAT_ID(B)];
        x = CHOL(solve) (CHOLMOD_A, L, b, &Common);
        if (Common.status != CHOLMOD_OK){
            PyErr_SetString(PyExc_ValueError, "solve step failed");
            CHOL(free_factor)(&L, &Common);
            b->x = b_old;
            CHOL(free_dense)(&b, &Common);
            CHOL(free_dense)(&x, &Common);
            return NULL;
        }
        memcpy(b->x, x->x, SP_NROWS(A)*E_SIZE[MAT_ID(B)]);
        CHOL(free_dense)(&x, &Common);
    }
    b->x = b_old;
    CHOL(free_dense)(&b, &Common);
    CHOL(free_factor)(&L, &Common);
    return Py_BuildValue("");
}
Пример #3
0
static PyObject* linsolve(PyObject *self, PyObject *args,
    PyObject *kwrds)
{
    spmatrix *A;
    matrix *B;
#if PY_MAJOR_VERSION >= 3
    int trans_ = 'N';
#endif
    char trans='N';
    double info[UMFPACK_INFO];
    int oB=0, n, nrhs=-1, ldB=0, k;
    void *symbolic, *numeric, *x;
    char *kwlist[] = {"A", "B", "trans", "nrhs", "ldB", "offsetB",
        NULL};

#if PY_MAJOR_VERSION >= 3
    if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|Ciii", kwlist,
        &A, &B, &trans_, &nrhs, &ldB, &oB)) return NULL;
    trans = (char) trans_;
#else
    if (!PyArg_ParseTupleAndKeywords(args, kwrds, "OO|ciii", kwlist,
        &A, &B, &trans, &nrhs, &ldB, &oB)) return NULL;
#endif

    if (!SpMatrix_Check(A) || SP_NROWS(A) != SP_NCOLS(A))
        PY_ERR_TYPE("A must be a square sparse matrix");
    n = SP_NROWS(A);
    if (!Matrix_Check(B) || MAT_ID(B) != SP_ID(A))
        PY_ERR_TYPE("B must a dense matrix of the same numeric type "
            "as A");

    if (nrhs < 0) nrhs = B->ncols;
    if (n == 0 || nrhs == 0) return Py_BuildValue("i", 0);
    if (ldB == 0) ldB = MAX(1,B->nrows);
    if (ldB < MAX(1,n)) err_ld("ldB");
    if (oB < 0) err_nn_int("offsetB");
    if (oB + (nrhs-1)*ldB + n > MAT_LGT(B)) err_buf_len("B");

    if (trans != 'N' && trans != 'T' && trans != 'C')
        err_char("trans", "'N', 'T', 'C'");

    if (SP_ID(A) == DOUBLE)
        UMFD(symbolic)(n, n, SP_COL(A), SP_ROW(A), SP_VAL(A), &symbolic,
            NULL, info);
    else
        UMFZ(symbolic)(n, n, SP_COL(A), SP_ROW(A), SP_VAL(A), NULL,
            &symbolic, NULL, info);

    if (info[UMFPACK_STATUS] != UMFPACK_OK){
        if (SP_ID(A) == DOUBLE)
            UMFD(free_symbolic)(&symbolic);
        else
            UMFZ(free_symbolic)(&symbolic);
        if (info[UMFPACK_STATUS] == UMFPACK_ERROR_out_of_memory)
            return PyErr_NoMemory();
        else {
            snprintf(umfpack_error,20,"%s %i","UMFPACK ERROR",
                (int) info[UMFPACK_STATUS]);
            PyErr_SetString(PyExc_ValueError, umfpack_error);
            return NULL;
        }
    }

    if (SP_ID(A) == DOUBLE) {
        UMFD(numeric)(SP_COL(A), SP_ROW(A), SP_VAL(A), symbolic,
            &numeric, NULL, info);
        UMFD(free_symbolic)(&symbolic);
    } else {
        UMFZ(numeric)(SP_COL(A), SP_ROW(A), SP_VAL(A), NULL, symbolic,
            &numeric, NULL, info);
        UMFZ(free_symbolic)(&symbolic);
    }
    if (info[UMFPACK_STATUS] != UMFPACK_OK){
        if (SP_ID(A) == DOUBLE)
            UMFD(free_numeric)(&numeric);
        else
            UMFZ(free_numeric)(&numeric);
        if (info[UMFPACK_STATUS] == UMFPACK_ERROR_out_of_memory)
            return PyErr_NoMemory();
        else {
            if (info[UMFPACK_STATUS] == UMFPACK_WARNING_singular_matrix)
                PyErr_SetString(PyExc_ArithmeticError, "singular "
                    "matrix");
            else {
                snprintf(umfpack_error,20,"%s %i","UMFPACK ERROR",
                    (int) info[UMFPACK_STATUS]);
                PyErr_SetString(PyExc_ValueError, umfpack_error);
            }
            return NULL;
        }
    }

    if (!(x = malloc(n*E_SIZE[SP_ID(A)]))) {
        if (SP_ID(A) == DOUBLE)
            UMFD(free_numeric)(&numeric);
        else
            UMFZ(free_numeric)(&numeric);
        return PyErr_NoMemory();
    }
    for (k=0; k<nrhs; k++){
        if (SP_ID(A) == DOUBLE)
            UMFD(solve)(trans == 'N' ? UMFPACK_A: UMFPACK_Aat,
                SP_COL(A), SP_ROW(A), SP_VAL(A), x,
                MAT_BUFD(B) + k*ldB + oB, numeric, NULL, info);
        else
            UMFZ(solve)(trans == 'N' ? UMFPACK_A: trans == 'C' ?
                UMFPACK_At : UMFPACK_Aat, SP_COL(A), SP_ROW(A),
                SP_VAL(A), NULL, x, NULL,
                (double *)(MAT_BUFZ(B) + k*ldB + oB), NULL, numeric,
                NULL, info);
        if (info[UMFPACK_STATUS] == UMFPACK_OK)
            memcpy(B->buffer + (k*ldB + oB)*E_SIZE[SP_ID(A)], x,
                n*E_SIZE[SP_ID(A)]);
        else
	    break;
    }
    free(x);
    if (SP_ID(A) == DOUBLE)
        UMFD(free_numeric)(&numeric);
    else
        UMFZ(free_numeric)(&numeric);

    if (info[UMFPACK_STATUS] != UMFPACK_OK){
        if (info[UMFPACK_STATUS] == UMFPACK_ERROR_out_of_memory)
            return PyErr_NoMemory();
        else {
            if (info[UMFPACK_STATUS] == UMFPACK_WARNING_singular_matrix)
                PyErr_SetString(PyExc_ArithmeticError, "singular "
                    "matrix");
            else {
                snprintf(umfpack_error,20,"%s %i","UMFPACK ERROR",
                    (int) info[UMFPACK_STATUS]);
                PyErr_SetString(PyExc_ValueError, umfpack_error);
            }
        return NULL;
        }
    }
    return Py_BuildValue("");
}
Пример #4
0
static PyObject* symbolic(PyObject *self, PyObject *args,
    PyObject *kwrds)
{
    spmatrix *A;
    cholmod_sparse *Ac = NULL;
    cholmod_factor *L;
    matrix *P=NULL;
#if PY_MAJOR_VERSION >= 3
    int uplo_='L';
#endif
    char uplo='L';
    int n;
    char *kwlist[] = {"A", "p", "uplo", NULL};

    if (!set_options()) return NULL;

#if PY_MAJOR_VERSION >= 3
    if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|OC", kwlist, &A,
        &P, &uplo_)) return NULL;
    uplo = (char) uplo_;
#else
    if (!PyArg_ParseTupleAndKeywords(args, kwrds, "O|Oc", kwlist, &A,
        &P, &uplo)) return NULL;
#endif
    if (!SpMatrix_Check(A) || SP_NROWS(A) != SP_NCOLS(A))
        PY_ERR_TYPE("A is not a square sparse matrix");
    n = SP_NROWS(A);

    if (P) {
        if (!Matrix_Check(P) || MAT_ID(P) != INT) err_int_mtrx("p");
        if (MAT_LGT(P) != n) err_buf_len("p");
        if (!CHOL(check_perm)(P->buffer, n, n, &Common))
            PY_ERR(PyExc_ValueError, "p is not a valid permutation");
    }
    if (uplo != 'U' && uplo != 'L') err_char("uplo", "'L', 'U'");
    if (!(Ac = pack(A, uplo))) return PyErr_NoMemory();
    L = CHOL(analyze_p)(Ac, P ? MAT_BUFI(P): NULL, NULL, 0, &Common);
    CHOL(free_sparse)(&Ac, &Common);

    if (Common.status != CHOLMOD_OK){
        if (Common.status == CHOLMOD_OUT_OF_MEMORY)
            return PyErr_NoMemory();
        else{
            PyErr_SetString(PyExc_ValueError, "symbolic factorization "
                "failed");
            return NULL;
        }
    }
#if PY_MAJOR_VERSION >= 3
    return (PyObject *) PyCapsule_New((void *) L, SP_ID(A)==DOUBLE ?  
        (uplo == 'L' ?  "CHOLMOD FACTOR D L" : "CHOLMOD FACTOR D U") :
        (uplo == 'L' ?  "CHOLMOD FACTOR Z L" : "CHOLMOD FACTOR Z U"),
        (PyCapsule_Destructor) &cvxopt_free_cholmod_factor); 
#else
    return (PyObject *) PyCObject_FromVoidPtrAndDesc((void *) L,
        SP_ID(A)==DOUBLE ?  
        (uplo == 'L' ?  "CHOLMOD FACTOR D L" : "CHOLMOD FACTOR D U") :
        (uplo == 'L' ?  "CHOLMOD FACTOR Z L" : "CHOLMOD FACTOR Z U"),
	cvxopt_free_cholmod_factor);
#endif
}
Пример #5
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),&param_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,
            &param_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;
}