#include "pardiso_interface.h" #define MKL_INT c_int // Single Dynamic library interface #define MKL_INTERFACE_LP64 0x0 #define MKL_INTERFACE_ILP64 0x1 // Solver Phases #define PARDISO_SYMBOLIC (11) #define PARDISO_NUMERIC (22) #define PARDISO_SOLVE (33) #define PARDISO_CLEANUP (-1) // Prototypes for Pardiso functions void pardiso(void**, // pt const c_int*, // maxfct const c_int*, // mnum const c_int*, // mtype const c_int*, // phase const c_int*, // n const c_float*, // a const c_int*, // ia const c_int*, // ja c_int*, // perm const c_int*, //nrhs c_int*, // iparam const c_int*, //msglvl c_float*, // b c_float*, // x c_int* // error ); c_int mkl_set_interface_layer(c_int); c_int mkl_get_max_threads(); // Free LDL Factorization structure void free_linsys_solver_pardiso(pardiso_solver *s) { if (s) { // Free pardiso solver using internal function s->phase = PARDISO_CLEANUP; pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase), &(s->nKKT), &(s->fdum), s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs), s->iparm, &(s->msglvl), &(s->fdum), &(s->fdum), &(s->error)); if ( s->error != 0 ){ #ifdef PRINTING c_eprint("Error during MKL Pardiso cleanup: %d", (int)s->error); #endif } // Check each attribute of the structure and free it if it exists if (s->KKT) csc_spfree(s->KKT); if (s->KKT_i) c_free(s->KKT_i); if (s->KKT_p) c_free(s->KKT_p); if (s->bp) c_free(s->bp); if (s->sol) c_free(s->sol); if (s->rho_inv_vec) c_free(s->rho_inv_vec); // These are required for matrix updates if (s->Pdiag_idx) c_free(s->Pdiag_idx); if (s->PtoKKT) c_free(s->PtoKKT); if (s->AtoKKT) c_free(s->AtoKKT); if (s->rhotoKKT) c_free(s->rhotoKKT); c_free(s); } } // Initialize factorization structure c_int init_linsys_solver_pardiso(pardiso_solver ** sp, const csc * P, const csc * A, c_float sigma, const c_float * rho_vec, c_int polish){ c_int i; // loop counter c_int nnzKKT; // Number of nonzeros in KKT // Define Variables c_int n_plus_m; // n_plus_m dimension // Allocate private structure to store KKT factorization pardiso_solver *s; s = c_calloc(1, sizeof(pardiso_solver)); *sp = s; // Size of KKT s->n = P->n; s->m = A->m; n_plus_m = s->n + s->m; s->nKKT = n_plus_m; // Sigma parameter s->sigma = sigma; // Polishing flag s->polish = polish; // Link Functions s->solve = &solve_linsys_pardiso; s->free = &free_linsys_solver_pardiso; s->update_matrices = &update_linsys_solver_matrices_pardiso; s->update_rho_vec = &update_linsys_solver_rho_vec_pardiso; // Assign type s->type = MKL_PARDISO_SOLVER; // Working vector s->bp = (c_float *)c_malloc(sizeof(c_float) * n_plus_m); // Solution vector s->sol = (c_float *)c_malloc(sizeof(c_float) * n_plus_m); // Parameter vector s->rho_inv_vec = (c_float *)c_malloc(sizeof(c_float) * n_plus_m); // Form KKT matrix if (polish){ // Called from polish() // Use s->rho_inv_vec for storing param2 = vec(delta) for (i = 0; i < A->m; i++){ s->rho_inv_vec[i] = sigma; } s->KKT = form_KKT(P, A, 1, sigma, s->rho_inv_vec, OSQP_NULL, OSQP_NULL, OSQP_NULL, OSQP_NULL, OSQP_NULL); } else { // Called from ADMM algorithm // Allocate vectors of indices s->PtoKKT = c_malloc((P->p[P->n]) * sizeof(c_int)); s->AtoKKT = c_malloc((A->p[A->n]) * sizeof(c_int)); s->rhotoKKT = c_malloc((A->m) * sizeof(c_int)); // Use s->rho_inv_vec for storing param2 = rho_inv_vec for (i = 0; i < A->m; i++){ s->rho_inv_vec[i] = 1. / rho_vec[i]; } s->KKT = form_KKT(P, A, 1, sigma, s->rho_inv_vec, s->PtoKKT, s->AtoKKT, &(s->Pdiag_idx), &(s->Pdiag_n), s->rhotoKKT); } // Check if matrix has been created if (!(s->KKT)) { #ifdef PRINTING c_eprint("Error in forming KKT matrix"); #endif free_linsys_solver_pardiso(s); return OSQP_LINSYS_SOLVER_INIT_ERROR; } else { // Adjust indexing for Pardiso nnzKKT = s->KKT->p[s->KKT->m]; s->KKT_i = c_malloc((nnzKKT) * sizeof(c_int)); s->KKT_p = c_malloc((s->KKT->m + 1) * sizeof(c_int)); for(i = 0; i < nnzKKT; i++){ s->KKT_i[i] = s->KKT->i[i] + 1; } for(i = 0; i < n_plus_m+1; i++){ s->KKT_p[i] = s->KKT->p[i] + 1; } } // Set MKL interface layer (Long integers if activated) #ifdef DLONG mkl_set_interface_layer(MKL_INTERFACE_ILP64); #else mkl_set_interface_layer(MKL_INTERFACE_LP64); #endif // Set Pardiso variables s->mtype = -2; // Real symmetric indefinite matrix s->nrhs = 1; // Number of right hand sides s->maxfct = 1; // Maximum number of numerical factorizations s->mnum = 1; // Which factorization to use s->msglvl = 0; // Do not print statistical information s->error = 0; // Initialize error flag for ( i = 0; i < 64; i++ ) { s->iparm[i] = 0; // Setup Pardiso control parameters s->pt[i] = 0; // Initialize the internal solver memory pointer } s->iparm[0] = 1; // No solver default s->iparm[1] = 3; // Fill-in reordering from OpenMP if (polish) { s->iparm[5] = 1; // Write solution into b } else { s->iparm[5] = 0; // Do NOT write solution into b } /* s->iparm[7] = 2; // Max number of iterative refinement steps */ s->iparm[7] = 0; // Number of iterative refinement steps (auto, performs them only if perturbed pivots are obtained) s->iparm[9] = 13; // Perturb the pivot elements with 1E-13 s->iparm[34] = 0; // Use Fortran-style indexing for indices /* s->iparm[34] = 1; // Use C-style indexing for indices */ // Print number of threads s->nthreads = mkl_get_max_threads(); // Reordering and symbolic factorization s->phase = PARDISO_SYMBOLIC; pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase), &(s->nKKT), s->KKT->x, s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs), s->iparm, &(s->msglvl), &(s->fdum), &(s->fdum), &(s->error)); if ( s->error != 0 ){ #ifdef PRINTING c_eprint("Error during symbolic factorization: %d", (int)s->error); #endif free_linsys_solver_pardiso(s); *sp = OSQP_NULL; return OSQP_LINSYS_SOLVER_INIT_ERROR; } // Numerical factorization s->phase = PARDISO_NUMERIC; pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase), &(s->nKKT), s->KKT->x, s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs), s->iparm, &(s->msglvl), &(s->fdum), &(s->fdum), &(s->error)); if ( s->error ){ #ifdef PRINTING c_eprint("Error during numerical factorization: %d", (int)s->error); #endif free_linsys_solver_pardiso(s); *sp = OSQP_NULL; return OSQP_LINSYS_SOLVER_INIT_ERROR; } // No error return 0; } // Returns solution to linear system Ax = b with solution stored in b c_int solve_linsys_pardiso(pardiso_solver * s, c_float * b) { c_int j; // Back substitution and iterative refinement s->phase = PARDISO_SOLVE; pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase), &(s->nKKT), s->KKT->x, s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs), s->iparm, &(s->msglvl), b, s->sol, &(s->error)); if ( s->error != 0 ){ #ifdef PRINTING c_eprint("Error during linear system solution: %d", (int)s->error); #endif return 1; } if (!(s->polish)) { /* copy x_tilde from s->sol */ for (j = 0 ; j < s->n ; j++) { b[j] = s->sol[j]; } /* compute z_tilde from b and s->sol */ for (j = 0 ; j < s->m ; j++) { b[j + s->n] += s->rho_inv_vec[j] * s->sol[j + s->n]; } } return 0; } // Update solver structure with new P and A c_int update_linsys_solver_matrices_pardiso(pardiso_solver * s, const csc *P, const csc *A) { // Update KKT matrix with new P update_KKT_P(s->KKT, P, s->PtoKKT, s->sigma, s->Pdiag_idx, s->Pdiag_n); // Update KKT matrix with new A update_KKT_A(s->KKT, A, s->AtoKKT); // Perform numerical factorization s->phase = PARDISO_NUMERIC; pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase), &(s->nKKT), s->KKT->x, s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs), s->iparm, &(s->msglvl), &(s->fdum), &(s->fdum), &(s->error)); // Return exit flag return s->error; } c_int update_linsys_solver_rho_vec_pardiso(pardiso_solver * s, const c_float * rho_vec) { c_int i; // Update internal rho_inv_vec for (i = 0; i < s->m; i++){ s->rho_inv_vec[i] = 1. / rho_vec[i]; } // Update KKT matrix with new rho_vec update_KKT_param2(s->KKT, s->rho_inv_vec, s->rhotoKKT, s->m); // Perform numerical factorization s->phase = PARDISO_NUMERIC; pardiso (s->pt, &(s->maxfct), &(s->mnum), &(s->mtype), &(s->phase), &(s->nKKT), s->KKT->x, s->KKT_p, s->KKT_i, &(s->idum), &(s->nrhs), s->iparm, &(s->msglvl), &(s->fdum), &(s->fdum), &(s->error)); // Return exit flag return s->error; }