#include "quadprog.h" #include "min_quad_with_fixed.h" #include template IGL_INLINE Eigen::Matrix igl::quadprog( const Eigen::Matrix & H, const Eigen::Matrix & f, const Eigen::Matrix & A, const Eigen::Matrix & b, const Eigen::Matrix & lb, const Eigen::Matrix & ub) { // Alec 16/2/2021: // igl::quadprog implements a very simple primal active set method. The new // igl::min_quad_with_fixed is very fast for small dense problems so the // iterations of igl::quadprog become very fast. Even if it ends up doing many // more iterations than igl::copyleft::quadprog it would be much faster (in // reality it doesn't do that many more iterations). It's a healthy 10-100x // faster than igl::copyleft::quadprog for specific cases of QPs. // // Unfortunately, that set is limited. igl::quadprog is really good at tiny // box-constrained QPs with a positive definite objective (like the kind that show // up in dual contouring). igl::copyleft::quadprog handles more general problems // (and also starts to beat igl::quadprog when the number of variables gets over // ~20). I tried extending igl::quadprog so that we could use it for // igl::copyleft::progressive_hulls and drop igl::copyleft::quadprog but it was // trickier than I thought. Something like qpmad or the non GPL version of // quadrog++ would be good future PR. // typedef Eigen::Matrix VectorSn; typedef Eigen::Array Arraybn; assert( (lb.array() < ub.array() ).all() ); const int dyn_n = n == Eigen::Dynamic ? H.rows() : n; VectorSn x(dyn_n); VectorSn bc = VectorSn::Constant(dyn_n,1,-1e26); Arraybn k = Arraybn::Constant(dyn_n,1,false); Eigen::Index iter; // n³ is probably way too conservative. for(iter = 0;iter(H,f,k,bc,A,b); // constraint violations VectorSn vl = lb-x; VectorSn vu = x-ub; // try to add/remove constraints Eigen::Index best_add = -1; Scalar worst_offense = 0; bool add_lower; Eigen::Index best_remove = -1; Scalar worst_lambda = 0; for(Eigen::Index i = 0;iworst_offense) { best_add = i; add_lower = true; worst_offense = vl(i); } if(vu(i)>worst_offense) { best_add = i; add_lower = false; worst_offense = vu(i); } // bias toward adding constraints if(best_add<0 && k(i)) { const Scalar sign = bc(i)==ub(i)?1:-1; const Scalar lambda_i = sign * (H.row(i)*x+f(i)); if(lambda_i > worst_lambda) { best_remove = i; worst_lambda = lambda_i; } } } // bias toward adding constraints if(best_add >= 0) { const auto i = best_add; assert(!k(i)); bc(i) = add_lower ? lb(i) : ub(i); k(i) = true; }else if(best_remove >= 0) { const auto i = best_remove; assert(k(i)); k(i) = false; }else /*if(best_add < 0 && best_remove < 0)*/ { return x; } } // Should never happen. assert(false && "quadprog failed after too many iterations"); return VectorSn::Zero(dyn_n); } template IGL_INLINE Eigen::Matrix igl::quadprog( const Eigen::Matrix & H, const Eigen::Matrix & f, const Eigen::Matrix & lb, const Eigen::Matrix & ub) { const int m = n == Eigen::Dynamic ? Eigen::Dynamic : 0; // Windows needs template parameters spelled out return quadprog( H,f, Eigen::Matrix(0,H.cols()), Eigen::Matrix(0,1), lb,ub); } #ifdef IGL_STATIC_LIBRARY // Explicit template instantiation template Eigen::Matrix igl::quadprog(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); template Eigen::Matrix igl::quadprog(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); template Eigen::Matrix igl::quadprog(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); template Eigen::Matrix igl::quadprog(Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&, Eigen::Matrix const&); #endif