// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2016 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/. #pragma once #include "min_quad_with_fixed.h" #include "slice.h" #include "is_symmetric.h" #include "find.h" #include "sparse.h" #include "repmat.h" #include "EPS.h" #include "cat.h" //#include // Bug in unsupported/Eigen/SparseExtra needs iostream first #include #include #include #include #include "matlab_format.h" #include template IGL_INLINE bool igl::min_quad_with_fixed_precompute( const Eigen::SparseMatrix& A2, const Eigen::MatrixBase & known, const Eigen::SparseMatrix& Aeq, const bool pd, min_quad_with_fixed_data & data ) { //#define MIN_QUAD_WITH_FIXED_CPP_DEBUG using namespace Eigen; using namespace std; const Eigen::SparseMatrix A = 0.5*A2; #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG cout<<" pre"<= 0)&& "known indices should be in [0,n)"); assert((kr == 0 || known.maxCoeff() < n) && "known indices should be in [0,n)"); assert(neq <= n && "Number of equality constraints should be less than DOFs"); // cache known // FIXME: This is *NOT* generic and introduces a copy. data.known = known.template cast(); // get list of unknown indices data.unknown.resize(n-kr); std::vector unknown_mask; unknown_mask.resize(n,true); for(int i = 0;i 0) { data.unknown_lagrange.head(data.unknown.size()) = data.unknown; } if(data.lagrange.size() > 0) { data.unknown_lagrange.tail(data.lagrange.size()) = data.lagrange; } SparseMatrix Auu; slice(A,data.unknown,data.unknown,Auu); assert(Auu.size() != 0 && Auu.rows() > 0 && "There should be at least one unknown."); // Positive definiteness is *not* determined, rather it is given as a // parameter data.Auu_pd = pd; if(data.Auu_pd) { // PD implies symmetric data.Auu_sym = true; // This is an annoying assertion unless EPS can be chosen in a nicer way. //assert(is_symmetric(Auu,EPS())); assert(is_symmetric(Auu,1.0) && "Auu should be symmetric if positive definite"); }else { // determine if A(unknown,unknown) is symmetric and/or positive definite VectorXi AuuI,AuuJ; Matrix AuuV; find(Auu,AuuI,AuuJ,AuuV); data.Auu_sym = is_symmetric(Auu,EPS()*AuuV.maxCoeff()); } // Determine number of linearly independent constraints int nc = 0; if(neq>0) { #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG cout<<" qr"<(data.Aequ.transpose().eval()),"AeqT")< new_A; SparseMatrix AeqT = Aeq.transpose(); SparseMatrix Z(neq,neq); // This is a bit slower. But why isn't cat fast? new_A = cat(1, cat(2, A, AeqT ), cat(2, Aeq, Z )); // precompute RHS builders if(kr > 0) { SparseMatrix Aulk,Akul; // Slow slice(new_A,data.unknown_lagrange,data.known,Aulk); //// This doesn't work!!! //data.preY = Aulk + Akul.transpose(); // Slow if(data.Auu_sym) { data.preY = Aulk*2; }else { slice(new_A,data.known,data.unknown_lagrange,Akul); SparseMatrix AkulT = Akul.transpose(); data.preY = Aulk + AkulT; } }else { data.preY.resize(data.unknown_lagrange.size(),0); } // Positive definite and no equality constraints (Positive definiteness // implies symmetric) #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG cout<<" factorize"<::LLT; }else { #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG cout<<" ldlt/lu"< NA; slice(new_A,data.unknown_lagrange,data.unknown_lagrange,NA); data.NA = NA; if(data.Auu_pd) { #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG cout<<" ldlt"<::LDLT; }else { #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG cout<<" lu"<1/2 data.lu.compute(NA); //std::cout<<"NA=["<::LU; } } }else { #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG cout<<" Aeq_li=false"< AeqTR,AeqTQ; AeqTR = data.AeqTQR.matrixR(); // This shouldn't be necessary AeqTR.prune(static_cast(0.0)); #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG cout<<" matrixQ"<(0.0)); //cout<<"AeqTQ: "< I(neq,neq); I.setIdentity(); data.AeqTE = data.AeqTQR.colsPermutation() * I; data.AeqTET = data.AeqTQR.colsPermutation().transpose() * I; assert(AeqTR.rows() == nu && "#rows in AeqTR should match #unknowns"); assert(AeqTR.cols() == neq && "#cols in AeqTR should match #constraints"); assert(AeqTQ.rows() == nu && "#rows in AeqTQ should match #unknowns"); assert(AeqTQ.cols() == nu && "#cols in AeqTQ should match #unknowns"); //cout<<" slice"< QRAuu = data.AeqTQ2T * Auu * data.AeqTQ2; { #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG cout<<" factorize"<::QR_LLT; } #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG cout<<" smash"< Auk; slice(A,data.unknown,data.known,Auk); SparseMatrix Aku; slice(A,data.known,data.unknown,Aku); SparseMatrix AkuT = Aku.transpose(); data.preY = Auk + AkuT; // Needed during solve data.Auu = Auu; slice(Aeq,data.known,2,data.Aeqk); assert(data.Aeqk.rows() == neq); assert(data.Aeqk.cols() == data.known.size()); } return true; } template < typename T, typename DerivedB, typename DerivedY, typename DerivedBeq, typename DerivedZ, typename Derivedsol> IGL_INLINE bool igl::min_quad_with_fixed_solve( const min_quad_with_fixed_data & data, const Eigen::MatrixBase & B, const Eigen::MatrixBase & Y, const Eigen::MatrixBase & Beq, Eigen::PlainObjectBase & Z, Eigen::PlainObjectBase & sol) { using namespace std; using namespace Eigen; typedef Matrix MatrixXT; // number of known rows int kr = data.known.size(); if(kr!=0) { assert(kr == Y.rows()); } // number of columns to solve int cols = Y.cols(); assert(B.cols() == 1 || B.cols() == cols); assert(Beq.size() == 0 || Beq.cols() == 1 || Beq.cols() == cols); // resize output Z.resize(data.n,cols); // Set known values for(int i = 0;i < kr;i++) { for(int j = 0;j < cols;j++) { Z(data.known(i),j) = Y(i,j); } } if(data.Aeq_li) { // number of lagrange multipliers aka linear equality constraints int neq = data.lagrange.size(); // append lagrange multiplier rhs's MatrixXT BBeq(B.rows() + Beq.rows(),cols); if(B.size() > 0) { BBeq.topLeftCorner(B.rows(),cols) = B.replicate(1,B.cols()==cols?1:cols); } if(Beq.size() > 0) { BBeq.bottomLeftCorner(Beq.rows(),cols) = -2.0*Beq.replicate(1,Beq.cols()==cols?1:cols); } // Build right hand side MatrixXT BBequlcols = BBeq(data.unknown_lagrange,Eigen::all); MatrixXT NB; if(kr == 0) { NB = BBequlcols; }else { NB = data.preY * Y + BBequlcols; } //std::cout<<"NB=["<::LLT: sol = data.llt.solve(NB); break; case igl::min_quad_with_fixed_data::LDLT: sol = data.ldlt.solve(NB); break; case igl::min_quad_with_fixed_data::LU: // Not a bottleneck sol = data.lu.solve(NB); break; default: cerr<<"Error: invalid solver type"<::QR_LLT); MatrixXT eff_Beq; // Adjust Aeq rhs to include known parts eff_Beq = //data.AeqTQR.colsPermutation().transpose() * (-data.Aeqk * Y + Beq); data.AeqTET * (-data.Aeqk * Y + Beq.replicate(1,Beq.cols()==cols?1:cols)); // Where did this -0.5 come from? Probably the same place as above. MatrixXT Bu = B(data.unknown,Eigen::all); MatrixXT NB; NB = -0.5*(Bu.replicate(1,B.cols()==cols?1:cols) + data.preY * Y); // Trim eff_Beq const int nc = data.AeqTQR.rank(); const int neq = Beq.rows(); eff_Beq = eff_Beq.topLeftCorner(nc,cols).eval(); data.AeqTR1T.template triangularView().solveInPlace(eff_Beq); // Now eff_Beq = (data.AeqTR1T \ (data.AeqTET * (-data.Aeqk * Y + Beq))) MatrixXT lambda_0; lambda_0 = data.AeqTQ1 * eff_Beq; //cout<().solveInPlace(temp1); //cout< IGL_INLINE bool igl::min_quad_with_fixed_solve( const min_quad_with_fixed_data & data, const Eigen::MatrixBase & B, const Eigen::MatrixBase & Y, const Eigen::MatrixBase & Beq, Eigen::PlainObjectBase & Z) { Eigen::Matrix sol; return min_quad_with_fixed_solve(data,B,Y,Beq,Z,sol); } template < typename T, typename Derivedknown, typename DerivedB, typename DerivedY, typename DerivedBeq, typename DerivedZ> IGL_INLINE bool igl::min_quad_with_fixed( const Eigen::SparseMatrix& A, const Eigen::MatrixBase & B, const Eigen::MatrixBase & known, const Eigen::MatrixBase & Y, const Eigen::SparseMatrix& Aeq, const Eigen::MatrixBase & Beq, const bool pd, Eigen::PlainObjectBase & Z) { min_quad_with_fixed_data data; if(!min_quad_with_fixed_precompute(A,known,Aeq,pd,data)) { return false; } return min_quad_with_fixed_solve(data,B,Y,Beq,Z); } template IGL_INLINE Eigen::Matrix igl::min_quad_with_fixed( const Eigen::Matrix & H, const Eigen::Matrix & f, const Eigen::Array & k, const Eigen::Matrix & bc, const Eigen::Matrix & A, const Eigen::Matrix & b) { const auto dyn_n = n == Eigen::Dynamic ? H.rows() : n; const auto dyn_m = m == Eigen::Dynamic ? A.rows() : m; constexpr const int nn = n == Eigen::Dynamic ? Eigen::Dynamic : n+m; const auto dyn_nn = nn == Eigen::Dynamic ? dyn_n+dyn_m : nn; if(dyn_m == 0) { return igl::min_quad_with_fixed(H,f,k,bc); } // min_x ½ xᵀ H x + xᵀ f subject to A x = b and x(k) = bc(k) // let zᵀ = [xᵀ λᵀ] // min_z ½ zᵀ [H Aᵀ;A 0] z + zᵀ [f;-b] z(k) = bc(k) const auto make_HH = [&]() { // Windows can't remember that nn is const. constexpr const int nn = n == Eigen::Dynamic ? Eigen::Dynamic : n+m; Eigen::Matrix HH = Eigen::Matrix::Zero(dyn_nn,dyn_nn); HH.topLeftCorner(dyn_n,dyn_n) = H; HH.bottomLeftCorner(dyn_m,dyn_n) = A; HH.topRightCorner(dyn_n,dyn_m) = A.transpose(); return HH; }; const Eigen::Matrix HH = make_HH(); const auto make_ff = [&]() { // Windows can't remember that nn is const. constexpr const int nn = n == Eigen::Dynamic ? Eigen::Dynamic : n+m; Eigen::Matrix ff(dyn_nn); ff.head(dyn_n) = f; ff.tail(dyn_m) = -b; return ff; }; const Eigen::Matrix ff = make_ff(); const auto make_kk = [&]() { // Windows can't remember that nn is const. constexpr const int nn = n == Eigen::Dynamic ? Eigen::Dynamic : n+m; Eigen::Array kk = Eigen::Array::Constant(dyn_nn,1,false); kk.head(dyn_n) = k; return kk; }; const Eigen::Array kk = make_kk(); const auto make_bcbc= [&]() { // Windows can't remember that nn is const. constexpr const int nn = n == Eigen::Dynamic ? Eigen::Dynamic : n+m; Eigen::Matrix bcbc(dyn_nn); bcbc.head(dyn_n) = bc; return bcbc; }; const Eigen::Matrix bcbc = make_bcbc(); const Eigen::Matrix xx = min_quad_with_fixed(HH,ff,kk,bcbc); return xx.head(dyn_n); } template IGL_INLINE Eigen::Matrix igl::min_quad_with_fixed( const Eigen::Matrix & H, const Eigen::Matrix & f, const Eigen::Array & k, const Eigen::Matrix & bc) { assert(H.isApprox(H.transpose(),1e-7)); assert(H.rows() == H.cols()); assert(H.rows() == f.size()); assert(H.rows() == k.size()); assert(H.rows() == bc.size()); const auto kcount = k.count(); // Everything fixed if(kcount == (Eigen::Dynamic?H.rows():n)) { return bc; } // Nothing fixed if(kcount == 0) { // avoid function call typedef Eigen::Matrix MatrixSn; typedef typename std::conditional,Eigen::CompleteOrthogonalDecomposition>::type Solver; return Solver(H).solve(-f); } // All-but-one fixed if( (Eigen::Dynamic?H.rows():n)-kcount == 1) { // which one is not fixed? int u = -1; for(int i=0;i=0); // min ½ x(u) Huu x(u) + x(u)(fu + H(u,k)bc(k)) // Huu x(u) = -(fu + H(u,k) bc(k)) // x(u) = (-fu + ∑ -Huj bcj)/Huu Eigen::Matrix x = bc; x(u) = -f(u); for(int i=0;i(); // % Matlibberish for generating these case statements: // maxi=16;for i=1:maxi;fprintf(' case %d:\n {\n const bool D = (n-%d<=0)||(%d>=n)||(n>%d);\n return min_quad_with_fixed(H,f,k,bc);\n }\n',[i i i maxi i]);end case 1: { const bool D = (n-1<=0)||(1>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 2: { const bool D = (n-2<=0)||(2>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 3: { const bool D = (n-3<=0)||(3>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 4: { const bool D = (n-4<=0)||(4>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 5: { const bool D = (n-5<=0)||(5>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 6: { const bool D = (n-6<=0)||(6>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 7: { const bool D = (n-7<=0)||(7>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 8: { const bool D = (n-8<=0)||(8>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 9: { const bool D = (n-9<=0)||(9>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 10: { const bool D = (n-10<=0)||(10>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 11: { const bool D = (n-11<=0)||(11>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 12: { const bool D = (n-12<=0)||(12>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 13: { const bool D = (n-13<=0)||(13>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 14: { const bool D = (n-14<=0)||(14>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 15: { const bool D = (n-15<=0)||(15>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } case 16: { const bool D = (n-16<=0)||(16>=n)||(n>16); return min_quad_with_fixed(H,f,k,bc); } default: return min_quad_with_fixed(H,f,k,bc); } } template IGL_INLINE Eigen::Matrix igl::min_quad_with_fixed( const Eigen::Matrix & H, const Eigen::Matrix & f, const Eigen::Array & k, const Eigen::Matrix & bc) { // 0 and n should be handle outside this function static_assert(kcount==Eigen::Dynamic || kcount>0 ,""); static_assert(kcount==Eigen::Dynamic || kcount MatrixSuu; typedef Eigen::Matrix MatrixSuk; typedef Eigen::Matrix VectorSn; typedef Eigen::Matrix VectorSu; typedef Eigen::Matrix VectorSk; const auto dyn_n = n==Eigen::Dynamic ? H.rows() : n; const auto dyn_kcount = kcount==Eigen::Dynamic ? k.count() : kcount; const auto dyn_ucount = ucount==Eigen::Dynamic ? dyn_n- dyn_kcount : ucount; // For ucount==2 or kcount==2 this calls the coefficient initiliazer rather // than the size initilizer, but I guess that's ok. MatrixSuu Huu(dyn_ucount,dyn_ucount); MatrixSuk Huk(dyn_ucount,dyn_kcount); VectorSu mrhs(dyn_ucount); VectorSk bck(dyn_kcount); { int ui = 0; int ki = 0; for(int i = 0;i, // LDLT should be faster for indefinite problems but already found some // cases where it was too inaccurate when called via quadprog_primal. // Ideally this function takes LLT,LDLT, or // CompleteOrthogonalDecomposition as a template parameter. "template // template" parameters did work because LLT,LDLT have different number of // template parameters from CompleteOrthogonalDecomposition. Perhaps // there's a way to take advantage of LLT and LDLT's default template // parameters (I couldn't figure out how). Eigen::CompleteOrthogonalDecomposition>::type Solver; VectorSu xu = Solver(Huu).solve(-mrhs); VectorSn x(dyn_n); { int ui = 0; int ki = 0; for(int i = 0;i