// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2013 Alec Jacobson // // This Source Code Form is subject to the terms of the Mozilla Public License // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at http://mozilla.org/MPL/2.0/. #include "mosek_quadprog.h" #include "mosek_guarded.h" #include #include "../find.h" #include "../verbose.h" #include "../speye.h" #include "../matrix_to_list.h" #include "../list_to_matrix.h" #include "../harwell_boeing.h" #include "../EPS.h" igl::mosek::MosekData::MosekData() { // These are the default settings that worked well for BBW. Your miles may // very well be kilometers. // >1e0 NONSOLUTION // 1e-1 artifacts in deformation // 1e-3 artifacts in isolines // 1e-4 seems safe // 1e-8 MOSEK DEFAULT SOLUTION douparam[MSK_DPAR_INTPNT_TOL_REL_GAP]=1e-8; #if MSK_VERSION_MAJOR >= 8 douparam[MSK_DPAR_INTPNT_QO_TOL_REL_GAP]=1e-12; #endif // Force using multiple threads, not sure if MOSEK is properly destroying //extra threads... #if MSK_VERSION_MAJOR >= 7 intparam[MSK_IPAR_NUM_THREADS] = 6; #elif MSK_VERSION_MAJOR == 6 intparam[MSK_IPAR_INTPNT_NUM_THREADS] = 6; #endif #if MSK_VERSION_MAJOR == 6 // Force turn off data check intparam[MSK_IPAR_DATA_CHECK]=MSK_OFF; #endif // Turn off presolving // intparam[MSK_IPAR_PRESOLVE_USE] = MSK_PRESOLVE_MODE_OFF; // Force particular matrix reordering method // MSK_ORDER_METHOD_NONE cuts time in half roughly, since half the time is // usually spent reordering the matrix // !! WARNING Setting this parameter to anything but MSK_ORDER_METHOD_FREE // seems to have the effect of setting it to MSK_ORDER_METHOD_NONE // *Or maybe Mosek is spending a bunch of time analyzing the matrix to // choose the right ordering method when really any of them are // instantaneous intparam[MSK_IPAR_INTPNT_ORDER_METHOD] = MSK_ORDER_METHOD_NONE; // 1.0 means optimizer is very lenient about declaring model infeasible douparam[MSK_DPAR_INTPNT_TOL_INFEAS] = 1e-8; // Hard to say if this is doing anything, probably nothing dramatic douparam[MSK_DPAR_INTPNT_TOL_PSAFE]= 1e2; // Turn off convexity check intparam[MSK_IPAR_CHECK_CONVEXITY] = MSK_CHECK_CONVEXITY_NONE; } template IGL_INLINE bool igl::mosek::mosek_quadprog( const Index n, std::vector & Qi, std::vector & Qj, std::vector & Qv, const std::vector & c, const Scalar cf, const Index m, std::vector & Av, std::vector & Ari, const std::vector & Acp, const std::vector & lc, const std::vector & uc, const std::vector & lx, const std::vector & ux, MosekData & mosek_data, std::vector & x) { // I J V vectors of Q should all be same length assert(Qv.size() == Qi.size()); assert(Qv.size() == Qj.size()); // number of columns in linear constraint matrix must be ≤ number of // variables assert( (int)Acp.size() == (n+1)); // linear bound vectors must be size of number of constraints or empty assert( ((int)lc.size() == m) || ((int)lc.size() == 0)); assert( ((int)uc.size() == m) || ((int)uc.size() == 0)); // constant bound vectors must be size of number of variables or empty assert( ((int)lx.size() == n) || ((int)lx.size() == 0)); assert( ((int)ux.size() == n) || ((int)ux.size() == 0)); // allocate space for solution in x x.resize(n); // variables for mosek task, env and result code MSKenv_t env; MSKtask_t task; // Create the MOSEK environment #if MSK_VERSION_MAJOR >= 7 mosek_guarded(MSK_makeenv(&env,NULL)); #elif MSK_VERSION_MAJOR == 6 mosek_guarded(MSK_makeenv(&env,NULL,NULL,NULL,NULL)); #endif ///* Directs the log stream to the 'printstr' function. */ //// Little function mosek needs in order to know how to print to std out //const auto & printstr = [](void *handle, char str[]) //{ // printf("%s",str); //} //mosek_guarded(MSK_linkfunctoenvstream(env,MSK_STREAM_LOG,NULL,printstr)); // initialize mosek environment #if MSK_VERSION_MAJOR <= 7 mosek_guarded(MSK_initenv(env)); #endif // Create the optimization task mosek_guarded(MSK_maketask(env,m,n,&task)); verbose("Creating task with %ld linear constraints and %ld variables...\n",m,n); //// Tell mosek how to print to std out //mosek_guarded(MSK_linkfunctotaskstream(task,MSK_STREAM_LOG,NULL,printstr)); // Give estimate of number of variables mosek_guarded(MSK_putmaxnumvar(task,n)); if(m>0) { // Give estimate of number of constraints mosek_guarded(MSK_putmaxnumcon(task,m)); // Give estimate of number of non zeros in A mosek_guarded(MSK_putmaxnumanz(task,Av.size())); } // Give estimate of number of non zeros in Q mosek_guarded(MSK_putmaxnumqnz(task,Qv.size())); if(m>0) { // Append 'm' empty constraints, the constrainst will initially have no // bounds #if MSK_VERSION_MAJOR >= 7 mosek_guarded(MSK_appendcons(task,m)); #elif MSK_VERSION_MAJOR == 6 mosek_guarded(MSK_append(task,MSK_ACC_CON,m)); #endif } // Append 'n' variables #if MSK_VERSION_MAJOR >= 7 mosek_guarded(MSK_appendvars(task,n)); #elif MSK_VERSION_MAJOR == 6 mosek_guarded(MSK_append(task,MSK_ACC_VAR,n)); #endif // add a contant term to the objective mosek_guarded(MSK_putcfix(task,cf)); // loop over variables for(int j = 0;j 0) { // Set linear term c_j in the objective mosek_guarded(MSK_putcj(task,j,c[j])); } // Set constant bounds on variable j if(lx[j] == ux[j]) { mosek_guarded(MSK_putbound(task,MSK_ACC_VAR,j,MSK_BK_FX,lx[j],ux[j])); }else { mosek_guarded(MSK_putbound(task,MSK_ACC_VAR,j,MSK_BK_RA,lx[j],ux[j])); } if(m>0) { // Input column j of A #if MSK_VERSION_MAJOR >= 7 mosek_guarded( MSK_putacol( task, j, Acp[j+1]-Acp[j], &Ari[Acp[j]], &Av[Acp[j]]) ); #elif MSK_VERSION_MAJOR == 6 mosek_guarded( MSK_putavec( task, MSK_ACC_VAR, j, Acp[j+1]-Acp[j], &Ari[Acp[j]], &Av[Acp[j]]) ); #endif } } // loop over constraints for(int i = 0;i::iterator pit = mosek_data.intparam.begin(); pit != mosek_data.intparam.end(); pit++) { mosek_guarded(MSK_putintparam(task,pit->first,pit->second)); } for( std::map::iterator pit = mosek_data.douparam.begin(); pit != mosek_data.douparam.end(); pit++) { mosek_guarded(MSK_putdouparam(task,pit->first,pit->second)); } // Now the optimizer has been prepared MSKrescodee trmcode; // run the optimizer mosek_guarded(MSK_optimizetrm(task,&trmcode)); //// Print a summary containing information about the solution for debugging //// purposes //MSK_solutionsummary(task,MSK_STREAM_LOG); // Get status of solution MSKsolstae solsta; #if MSK_VERSION_MAJOR >= 7 MSK_getsolsta (task,MSK_SOL_ITR,&solsta); #elif MSK_VERSION_MAJOR == 6 MSK_getsolutionstatus(task,MSK_SOL_ITR,NULL,&solsta); #endif bool success = false; switch(solsta) { case MSK_SOL_STA_OPTIMAL: case MSK_SOL_STA_NEAR_OPTIMAL: MSK_getsolutionslice(task,MSK_SOL_ITR,MSK_SOL_ITEM_XX,0,n,&x[0]); //printf("Optimal primal solution\n"); //for(size_t j=0; j & Q, const Eigen::VectorXd & c, const double cf, const Eigen::SparseMatrix & A, const Eigen::VectorXd & lc, const Eigen::VectorXd & uc, const Eigen::VectorXd & lx, const Eigen::VectorXd & ux, MosekData & mosek_data, Eigen::VectorXd & x) { using namespace Eigen; using namespace std; typedef int Index; typedef double Scalar; // Q should be square assert(Q.rows() == Q.cols()); // Q should be symmetric #ifdef EIGEN_HAS_A_BUG_AND_FAILS_TO_LET_ME_COMPUTE_Q_MINUS_Q_TRANSPOSE assert( (Q-Q.transpose()).sum() < FLOAT_EPS); #endif // Only keep lower triangular part of Q SparseMatrix QL; //QL = Q.template triangularView(); QL = Q.triangularView(); VectorXi Qi,Qj; VectorXd Qv; find(QL,Qi,Qj,Qv); vector vQi = matrix_to_list(Qi); vector vQj = matrix_to_list(Qj); vector vQv = matrix_to_list(Qv); // Convert linear term vector vc = matrix_to_list(c); assert(lc.size() == A.rows()); assert(uc.size() == A.rows()); // Convert A to harwell boeing format vector vAv; vector vAr,vAc; Index nr; harwell_boeing(A,nr,vAv,vAr,vAc); assert(lx.size() == Q.rows()); assert(ux.size() == Q.rows()); vector vlc = matrix_to_list(lc); vector vuc = matrix_to_list(uc); vector vlx = matrix_to_list(lx); vector vux = matrix_to_list(ux); vector vx; bool ret = mosek_quadprog( Q.rows(),vQi,vQj,vQv, vc, cf, nr, vAv, vAr, vAc, vlc,vuc, vlx,vux, mosek_data, vx); list_to_matrix(vx,x); return ret; } #ifdef IGL_STATIC_LIBRARY // Explicit template declarations #endif