libigl v2.5.0
Loading...
Searching...
No Matches
min_quad_with_fixed.impl.h
Go to the documentation of this file.
1// This file is part of libigl, a simple c++ geometry processing library.
2//
3// Copyright (C) 2016 Alec Jacobson <alecjacobson@gmail.com>
4//
5// This Source Code Form is subject to the terms of the Mozilla Public License
6// v. 2.0. If a copy of the MPL was not distributed with this file, You can
7// obtain one at http://mozilla.org/MPL/2.0/.
8#pragma once
9
10#include "min_quad_with_fixed.h"
11
12#include "slice.h"
13#include "is_symmetric.h"
14#include "find.h"
15#include "sparse.h"
16#include "repmat.h"
17#include "EPS.h"
18#include "cat.h"
19#include "placeholders.h"
20
21//#include <Eigen/SparseExtra>
22// Bug in unsupported/Eigen/SparseExtra needs iostream first
23#include <iostream>
24#include <unsupported/Eigen/SparseExtra>
25#include <cassert>
26#include <cstdio>
27#include "matlab_format.h"
28#include <type_traits>
29
30template <typename T, typename Derivedknown>
32 const Eigen::SparseMatrix<T>& A2,
33 const Eigen::MatrixBase<Derivedknown> & known,
34 const Eigen::SparseMatrix<T>& Aeq,
35 const bool pd,
37 )
38{
39//#define MIN_QUAD_WITH_FIXED_CPP_DEBUG
40 const Eigen::SparseMatrix<T> A = 0.5*A2;
41#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
42 cout<<" pre"<<endl;
43#endif
44 // number of rows
45 int n = A.rows();
46 // cache problem size
47 data.n = n;
48
49 int neq = Aeq.rows();
50 // default is to have 0 linear equality constraints
51 if(Aeq.size() != 0)
52 {
53 assert(n == Aeq.cols() && "#Aeq.cols() should match A.rows()");
54 }
55
56 assert(known.cols() == 1 && "known should be a vector");
57 assert(A.rows() == n && "A should be square");
58 assert(A.cols() == n && "A should be square");
59
60 // number of known rows
61 int kr = known.size();
62
63 assert((kr == 0 || known.minCoeff() >= 0)&& "known indices should be in [0,n)");
64 assert((kr == 0 || known.maxCoeff() < n) && "known indices should be in [0,n)");
65 assert(neq <= n && "Number of equality constraints should be less than DOFs");
66
67
68 // cache known
69 // FIXME: This is *NOT* generic and introduces a copy.
70 data.known = known.template cast<int>();
71
72 // get list of unknown indices
73 data.unknown.resize(n-kr);
74 std::vector<bool> unknown_mask;
75 unknown_mask.resize(n,true);
76 for(int i = 0;i<kr;i++)
77 {
78 unknown_mask[known(i, 0)] = false;
79 }
80 int u = 0;
81 for(int i = 0;i<n;i++)
82 {
83 if(unknown_mask[i])
84 {
85 data.unknown(u) = i;
86 u++;
87 }
88 }
89 // get list of lagrange multiplier indices
90 data.lagrange.resize(neq);
91 for(int i = 0;i<neq;i++)
92 {
93 data.lagrange(i) = n + i;
94 }
95 // cache unknown followed by lagrange indices
96 data.unknown_lagrange.resize(data.unknown.size()+data.lagrange.size());
97 // Would like to do:
98 //data.unknown_lagrange << data.unknown, data.lagrange;
99 // but Eigen can't handle empty vectors in comma initialization
100 // https://forum.kde.org/viewtopic.php?f=74&t=107974&p=364947#p364947
101 if(data.unknown.size() > 0)
102 {
103 data.unknown_lagrange.head(data.unknown.size()) = data.unknown;
104 }
105 if(data.lagrange.size() > 0)
106 {
107 data.unknown_lagrange.tail(data.lagrange.size()) = data.lagrange;
108 }
109
110 Eigen::SparseMatrix<T> Auu;
111 slice(A,data.unknown,data.unknown,Auu);
112 assert(Auu.size() != 0 && Auu.rows() > 0 && "There should be at least one unknown.");
113
114 // Positive definiteness is *not* determined, rather it is given as a
115 // parameter
116 data.Auu_pd = pd;
117 if(data.Auu_pd)
118 {
119 // PD implies symmetric
120 data.Auu_sym = true;
121 // This is an annoying assertion unless EPS can be chosen in a nicer way.
122 //assert(is_symmetric(Auu,EPS<T>()));
123 assert(is_symmetric(Auu,1.0) &&
124 "Auu should be symmetric if positive definite");
125 }else
126 {
127 // determine if A(unknown,unknown) is symmetric and/or positive definite
128 Eigen::VectorXi AuuI,AuuJ;
129 Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> AuuV;
130 find(Auu,AuuI,AuuJ,AuuV);
131 data.Auu_sym = is_symmetric(Auu,EPS<T>()*AuuV.maxCoeff());
132 }
133
134 // Determine number of linearly independent constraints
135 int nc = 0;
136 if(neq>0)
137 {
138#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
139 cout<<" qr"<<endl;
140#endif
141 // QR decomposition to determine row rank in Aequ
142 slice(Aeq,data.unknown,2,data.Aequ);
143 assert(data.Aequ.rows() == neq &&
144 "#Rows in Aequ should match #constraints");
145 assert(data.Aequ.cols() == data.unknown.size() &&
146 "#cols in Aequ should match #unknowns");
147 data.AeqTQR.compute(data.Aequ.transpose().eval());
148#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
149 //cout<<endl<<matlab_format(SparseMatrix<T>(data.Aequ.transpose().eval()),"AeqT")<<endl<<endl;
150#endif
151 switch(data.AeqTQR.info())
152 {
153 case Eigen::Success:
154 break;
155 case Eigen::NumericalIssue:
156#ifdef IGL_MIN_QUAD_WITH_FIXED_CPP_DEBUG
157 cerr<<"Error: Numerical issue."<<endl;
158#endif
159 return false;
160 case Eigen::InvalidInput:
161#ifdef IGL_MIN_QUAD_WITH_FIXED_CPP_DEBUG
162 cerr<<"Error: Invalid input."<<endl;
163#endif
164 return false;
165 default:
166#ifdef IGL_MIN_QUAD_WITH_FIXED_CPP_DEBUG
167 cerr<<"Error: Other."<<endl;
168#endif
169 return false;
170 }
171 nc = data.AeqTQR.rank();
172 assert(nc<=neq &&
173 "Rank of reduced constraints should be <= #original constraints");
174 data.Aeq_li = nc == neq;
175 //cout<<"data.Aeq_li: "<<data.Aeq_li<<endl;
176 }else
177 {
178 data.Aeq_li = true;
179 }
180
181 if(data.Aeq_li)
182 {
183#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
184 cout<<" Aeq_li=true"<<endl;
185#endif
186 // Append lagrange multiplier quadratic terms
187 Eigen::SparseMatrix<T> new_A;
188 Eigen::SparseMatrix<T> AeqT = Aeq.transpose();
189 Eigen::SparseMatrix<T> Z(neq,neq);
190 // This is a bit slower. But why isn't cat fast?
191 new_A = cat(1, cat(2, A, AeqT ),
192 cat(2, Aeq, Z ));
193
194 // precompute RHS builders
195 if(kr > 0)
196 {
197 Eigen::SparseMatrix<T> Aulk,Akul;
198 // Slow
199 slice(new_A,data.unknown_lagrange,data.known,Aulk);
201 //data.preY = Aulk + Akul.transpose();
202 // Slow
203 if(data.Auu_sym)
204 {
205 data.preY = Aulk*2;
206 }else
207 {
208 slice(new_A,data.known,data.unknown_lagrange,Akul);
209 Eigen::SparseMatrix<T> AkulT = Akul.transpose();
210 data.preY = Aulk + AkulT;
211 }
212 }else
213 {
214 data.preY.resize(data.unknown_lagrange.size(),0);
215 }
216
217 // Positive definite and no equality constraints (Positive definiteness
218 // implies symmetric)
219#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
220 cout<<" factorize"<<endl;
221#endif
222 if(data.Auu_pd && neq == 0)
223 {
224#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
225 cout<<" llt"<<endl;
226#endif
227 data.llt.compute(Auu);
228 switch(data.llt.info())
229 {
230 case Eigen::Success:
231 break;
232 case Eigen::NumericalIssue:
233#ifdef IGL_MIN_QUAD_WITH_FIXED_CPP_DEBUG
234 cerr<<"Error: Numerical issue."<<endl;
235#endif
236 return false;
237 default:
238#ifdef IGL_MIN_QUAD_WITH_FIXED_CPP_DEBUG
239 cerr<<"Error: Other."<<endl;
240#endif
241 return false;
242 }
244 }else
245 {
246#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
247 cout<<" ldlt/lu"<<endl;
248#endif
249 // Either not PD or there are equality constraints
250 Eigen::SparseMatrix<T> NA;
251 slice(new_A,data.unknown_lagrange,data.unknown_lagrange,NA);
252 data.NA = NA;
253 if(data.Auu_pd)
254 {
255#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
256 cout<<" ldlt"<<endl;
257#endif
258 data.ldlt.compute(NA);
259 switch(data.ldlt.info())
260 {
261 case Eigen::Success:
262 break;
263 case Eigen::NumericalIssue:
264#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
265 cerr<<"Error: Numerical issue."<<endl;
266#endif
267 return false;
268 default:
269#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
270 cerr<<"Error: Other."<<endl;
271#endif
272 return false;
273 }
275 }else
276 {
277#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
278 cout<<" lu"<<endl;
279#endif
280 // Resort to LU
281 // Bottleneck >1/2
282 data.lu.compute(NA);
283 //std::cout<<"NA=["<<std::endl<<NA<<std::endl<<"];"<<std::endl;
284 switch(data.lu.info())
285 {
286 case Eigen::Success:
287 break;
288 case Eigen::NumericalIssue:
289#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
290 cerr<<"Error: Numerical issue."<<endl;
291 return false;
292#endif
293 case Eigen::InvalidInput:
294#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
295 cerr<<"Error: Invalid Input."<<endl;
296#endif
297 return false;
298 default:
299#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
300 cerr<<"Error: Other."<<endl;
301#endif
302 return false;
303 }
305 }
306 }
307 }else
308 {
309#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
310 cout<<" Aeq_li=false"<<endl;
311#endif
312 data.neq = neq;
313 const int nu = data.unknown.size();
314 //cout<<"nu: "<<nu<<endl;
315 //cout<<"neq: "<<neq<<endl;
316 //cout<<"nc: "<<nc<<endl;
317 //cout<<" matrixR"<<endl;
318 Eigen::SparseMatrix<T> AeqTR,AeqTQ;
319 AeqTR = data.AeqTQR.matrixR();
320 // This shouldn't be necessary
321 AeqTR.prune(static_cast<T>(0.0));
322#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
323 cout<<" matrixQ"<<endl;
324#endif
325 // THIS IS ESSENTIALLY DENSE AND THIS IS BY FAR THE BOTTLENECK
326 // http://forum.kde.org/viewtopic.php?f=74&t=117500
327 AeqTQ = data.AeqTQR.matrixQ();
328#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
329 cout<<" prune"<<endl;
330 cout<<" nnz: "<<AeqTQ.nonZeros()<<endl;
331#endif
332 // This shouldn't be necessary
333 AeqTQ.prune(static_cast<T>(0.0));
334 //cout<<"AeqTQ: "<<AeqTQ.rows()<<" "<<AeqTQ.cols()<<endl;
335 //cout<<matlab_format(AeqTQ,"AeqTQ")<<endl;
336 //cout<<" perms"<<endl;
337#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
338 cout<<" nnz: "<<AeqTQ.nonZeros()<<endl;
339 cout<<" perm"<<endl;
340#endif
341 Eigen::SparseMatrix<T> I(neq,neq);
342 I.setIdentity();
343 data.AeqTE = data.AeqTQR.colsPermutation() * I;
344 data.AeqTET = data.AeqTQR.colsPermutation().transpose() * I;
345 assert(AeqTR.rows() == nu && "#rows in AeqTR should match #unknowns");
346 assert(AeqTR.cols() == neq && "#cols in AeqTR should match #constraints");
347 assert(AeqTQ.rows() == nu && "#rows in AeqTQ should match #unknowns");
348 assert(AeqTQ.cols() == nu && "#cols in AeqTQ should match #unknowns");
349 //cout<<" slice"<<endl;
350#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
351 cout<<" slice"<<endl;
352#endif
353 data.AeqTQ1 = AeqTQ.topLeftCorner(nu,nc);
354 data.AeqTQ1T = data.AeqTQ1.transpose().eval();
355 // ALREADY TRIM (Not 100% sure about this)
356 data.AeqTR1 = AeqTR.topLeftCorner(nc,nc);
357 data.AeqTR1T = data.AeqTR1.transpose().eval();
358 //cout<<"AeqTR1T.size() "<<data.AeqTR1T.rows()<<" "<<data.AeqTR1T.cols()<<endl;
359 // Null space
360 data.AeqTQ2 = AeqTQ.bottomRightCorner(nu,nu-nc);
361 data.AeqTQ2T = data.AeqTQ2.transpose().eval();
362#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
363 cout<<" proj"<<endl;
364#endif
365 // Projected hessian
366 Eigen::SparseMatrix<T> QRAuu = data.AeqTQ2T * Auu * data.AeqTQ2;
367 {
368#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
369 cout<<" factorize"<<endl;
370#endif
371 // QRAuu should always be PD
372 data.llt.compute(QRAuu);
373 switch(data.llt.info())
374 {
375 case Eigen::Success:
376 break;
377 case Eigen::NumericalIssue:
378#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
379 cerr<<"Error: Numerical issue."<<endl;
380#endif
381 return false;
382 default:
383#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
384 cerr<<"Error: Other."<<endl;
385#endif
386 return false;
387 }
389 }
390#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
391 cout<<" smash"<<endl;
392#endif
393 // Known value multiplier
394 Eigen::SparseMatrix<T> Auk;
395 slice(A,data.unknown,data.known,Auk);
396 Eigen::SparseMatrix<T> Aku;
397 slice(A,data.known,data.unknown,Aku);
398 Eigen::SparseMatrix<T> AkuT = Aku.transpose();
399 data.preY = Auk + AkuT;
400 // Needed during solve
401 data.Auu = Auu;
402 slice(Aeq,data.known,2,data.Aeqk);
403 assert(data.Aeqk.rows() == neq);
404 assert(data.Aeqk.cols() == data.known.size());
405 }
406 return true;
407}
408
409
410template <
411 typename T,
412 typename DerivedB,
413 typename DerivedY,
414 typename DerivedBeq,
415 typename DerivedZ,
416 typename Derivedsol>
418 const min_quad_with_fixed_data<T> & data,
419 const Eigen::MatrixBase<DerivedB> & B,
420 const Eigen::MatrixBase<DerivedY> & Y,
421 const Eigen::MatrixBase<DerivedBeq> & Beq,
422 Eigen::PlainObjectBase<DerivedZ> & Z,
423 Eigen::PlainObjectBase<Derivedsol> & sol)
424{
425 typedef Eigen::Matrix<T ,Eigen::Dynamic ,Eigen::Dynamic> MatrixXT;
426 // number of known rows
427 int kr = data.known.size();
428 if(kr!=0)
429 {
430 assert(kr == Y.rows());
431 }
432 // number of columns to solve
433 int cols = Y.cols();
434 assert(B.cols() == 1 || B.cols() == cols);
435 assert(Beq.size() == 0 || Beq.cols() == 1 || Beq.cols() == cols);
436
437 // resize output
438 Z.resize(data.n,cols);
439 // Set known values
440 for(int i = 0;i < kr;i++)
441 {
442 for(int j = 0;j < cols;j++)
443 {
444 Z(data.known(i),j) = Y(i,j);
445 }
446 }
447
448 if(data.Aeq_li)
449 {
450 // number of lagrange multipliers aka linear equality constraints
451 int neq = data.lagrange.size();
452 // append lagrange multiplier rhs's
453 MatrixXT BBeq(B.rows() + Beq.rows(),cols);
454 if(B.size() > 0)
455 {
456 BBeq.topLeftCorner(B.rows(),cols) = B.replicate(1,B.cols()==cols?1:cols);
457 }
458 if(Beq.size() > 0)
459 {
460 BBeq.bottomLeftCorner(Beq.rows(),cols) = -2.0*Beq.replicate(1,Beq.cols()==cols?1:cols);
461 }
462
463 // Build right hand side
464 MatrixXT BBequlcols = BBeq(data.unknown_lagrange,igl::placeholders::all);
465 MatrixXT NB;
466 if(kr == 0)
467 {
468 NB = BBequlcols;
469 }else
470 {
471 NB = data.preY * Y + BBequlcols;
472 }
473
474 //std::cout<<"NB=["<<std::endl<<NB<<std::endl<<"];"<<std::endl;
475 //cout<<matlab_format(NB,"NB")<<endl;
476 switch(data.solver_type)
477 {
479 sol = data.llt.solve(NB);
480 break;
482 sol = data.ldlt.solve(NB);
483 break;
485 // Not a bottleneck
486 sol = data.lu.solve(NB);
487 break;
488 default:
489#ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
490 cerr<<"Error: invalid solver type"<<endl;
491#endif
492 return false;
493 }
494 //std::cout<<"sol=["<<std::endl<<sol<<std::endl<<"];"<<std::endl;
495 // Now sol contains sol/-0.5
496 sol *= -0.5;
497 // Now sol contains solution
498 // Place solution in Z
499 for(int i = 0;i<(sol.rows()-neq);i++)
500 {
501 for(int j = 0;j<sol.cols();j++)
502 {
503 Z(data.unknown_lagrange(i),j) = sol(i,j);
504 }
505 }
506 }else
507 {
509 MatrixXT eff_Beq;
510 // Adjust Aeq rhs to include known parts
511 eff_Beq =
512 //data.AeqTQR.colsPermutation().transpose() * (-data.Aeqk * Y + Beq);
513 data.AeqTET * (-data.Aeqk * Y + Beq.replicate(1,Beq.cols()==cols?1:cols));
514 // Where did this -0.5 come from? Probably the same place as above.
515 MatrixXT Bu = B(data.unknown,igl::placeholders::all);
516 MatrixXT NB;
517 NB = -0.5*(Bu.replicate(1,B.cols()==cols?1:cols) + data.preY * Y);
518 // Trim eff_Beq
519 const int nc = data.AeqTQR.rank();
520 const int neq = Beq.rows();
521 eff_Beq = eff_Beq.topLeftCorner(nc,cols).eval();
522 data.AeqTR1T.template triangularView<Eigen::Lower>().solveInPlace(eff_Beq);
523 // Now eff_Beq = (data.AeqTR1T \ (data.AeqTET * (-data.Aeqk * Y + Beq)))
524 MatrixXT lambda_0;
525 lambda_0 = data.AeqTQ1 * eff_Beq;
526 //cout<<matlab_format(lambda_0,"lambda_0")<<endl;
527 MatrixXT QRB;
528 QRB = -data.AeqTQ2T * (data.Auu * lambda_0) + data.AeqTQ2T * NB;
529 Derivedsol lambda;
530 lambda = data.llt.solve(QRB);
531 // prepare output
532 Derivedsol solu;
533 solu = data.AeqTQ2 * lambda + lambda_0;
534 // http://www.math.uh.edu/~rohop/fall_06/Chapter3.pdf
535 Derivedsol solLambda;
536 {
537 Derivedsol temp1,temp2;
538 temp1 = (data.AeqTQ1T * NB - data.AeqTQ1T * data.Auu * solu);
539 data.AeqTR1.template triangularView<Eigen::Upper>().solveInPlace(temp1);
540 //cout<<matlab_format(temp1,"temp1")<<endl;
541 temp2 = Derivedsol::Zero(neq,cols);
542 temp2.topLeftCorner(nc,cols) = temp1;
543 //solLambda = data.AeqTQR.colsPermutation() * temp2;
544 solLambda = data.AeqTE * temp2;
545 }
546 // sol is [Z(unknown);Lambda]
547 assert(data.unknown.size() == solu.rows());
548 assert(cols == solu.cols());
549 assert(data.neq == neq);
550 assert(data.neq == solLambda.rows());
551 assert(cols == solLambda.cols());
552 sol.resize(data.unknown.size()+data.neq,cols);
553 sol.block(0,0,solu.rows(),solu.cols()) = solu;
554 sol.block(solu.rows(),0,solLambda.rows(),solLambda.cols()) = solLambda;
555 for(int u = 0;u<data.unknown.size();u++)
556 {
557 for(int j = 0;j<Z.cols();j++)
558 {
559 Z(data.unknown(u),j) = solu(u,j);
560 }
561 }
562 }
563 return true;
564}
565
566template <
567 typename T,
568 typename DerivedB,
569 typename DerivedY,
570 typename DerivedBeq,
571 typename DerivedZ>
573 const min_quad_with_fixed_data<T> & data,
574 const Eigen::MatrixBase<DerivedB> & B,
575 const Eigen::MatrixBase<DerivedY> & Y,
576 const Eigen::MatrixBase<DerivedBeq> & Beq,
577 Eigen::PlainObjectBase<DerivedZ> & Z)
578{
579 Eigen::Matrix<typename DerivedZ::Scalar, Eigen::Dynamic, Eigen::Dynamic> sol;
580 return min_quad_with_fixed_solve(data,B,Y,Beq,Z,sol);
581}
582
583template <
584 typename T,
585 typename Derivedknown,
586 typename DerivedB,
587 typename DerivedY,
588 typename DerivedBeq,
589 typename DerivedZ>
591 const Eigen::SparseMatrix<T>& A,
592 const Eigen::MatrixBase<DerivedB> & B,
593 const Eigen::MatrixBase<Derivedknown> & known,
594 const Eigen::MatrixBase<DerivedY> & Y,
595 const Eigen::SparseMatrix<T>& Aeq,
596 const Eigen::MatrixBase<DerivedBeq> & Beq,
597 const bool pd,
598 Eigen::PlainObjectBase<DerivedZ> & Z)
599{
601 if(!min_quad_with_fixed_precompute(A,known,Aeq,pd,data))
602 {
603 return false;
604 }
605 return min_quad_with_fixed_solve(data,B,Y,Beq,Z);
606}
607
608
609template <typename Scalar, int n, int m, bool Hpd>
610IGL_INLINE Eigen::Matrix<Scalar,n,1> igl::min_quad_with_fixed(
611 const Eigen::Matrix<Scalar,n,n> & H,
612 const Eigen::Matrix<Scalar,n,1> & f,
613 const Eigen::Array<bool,n,1> & k,
614 const Eigen::Matrix<Scalar,n,1> & bc,
615 const Eigen::Matrix<Scalar,m,n> & A,
616 const Eigen::Matrix<Scalar,m,1> & b)
617{
618 const auto dyn_n = n == Eigen::Dynamic ? H.rows() : n;
619 const auto dyn_m = m == Eigen::Dynamic ? A.rows() : m;
620 constexpr const int nn = n == Eigen::Dynamic ? Eigen::Dynamic : n+m;
621 const auto dyn_nn = nn == Eigen::Dynamic ? dyn_n+dyn_m : nn;
622 if(dyn_m == 0)
623 {
625 }
626 // min_x ½ xᵀ H x + xᵀ f subject to A x = b and x(k) = bc(k)
627 // let zᵀ = [xᵀ λᵀ]
628 // min_z ½ zᵀ [H Aᵀ;A 0] z + zᵀ [f;-b] z(k) = bc(k)
629 const auto make_HH = [&]()
630 {
631 // Windows can't remember that nn is const.
632 constexpr const int nn = n == Eigen::Dynamic ? Eigen::Dynamic : n+m;
633 Eigen::Matrix<Scalar,nn,nn> HH =
634 Eigen::Matrix<Scalar,nn,nn>::Zero(dyn_nn,dyn_nn);
635 HH.topLeftCorner(dyn_n,dyn_n) = H;
636 HH.bottomLeftCorner(dyn_m,dyn_n) = A;
637 HH.topRightCorner(dyn_n,dyn_m) = A.transpose();
638 return HH;
639 };
640 const Eigen::Matrix<Scalar,nn,nn> HH = make_HH();
641 const auto make_ff = [&]()
642 {
643 // Windows can't remember that nn is const.
644 constexpr const int nn = n == Eigen::Dynamic ? Eigen::Dynamic : n+m;
645 Eigen::Matrix<Scalar,nn,1> ff(dyn_nn);
646 ff.head(dyn_n) = f;
647 ff.tail(dyn_m) = -b;
648 return ff;
649 };
650 const Eigen::Matrix<Scalar,nn,1> ff = make_ff();
651 const auto make_kk = [&]()
652 {
653 // Windows can't remember that nn is const.
654 constexpr const int nn = n == Eigen::Dynamic ? Eigen::Dynamic : n+m;
655 Eigen::Array<bool,nn,1> kk =
656 Eigen::Array<bool,nn,1>::Constant(dyn_nn,1,false);
657 kk.head(dyn_n) = k;
658 return kk;
659 };
660 const Eigen::Array<bool,nn,1> kk = make_kk();
661 const auto make_bcbc= [&]()
662 {
663 // Windows can't remember that nn is const.
664 constexpr const int nn = n == Eigen::Dynamic ? Eigen::Dynamic : n+m;
665 Eigen::Matrix<Scalar,nn,1> bcbc(dyn_nn);
666 bcbc.head(dyn_n) = bc;
667 return bcbc;
668 };
669 const Eigen::Matrix<Scalar,nn,1> bcbc = make_bcbc();
670 const Eigen::Matrix<Scalar,nn,1> xx =
672 return xx.head(dyn_n);
673}
674
675template <typename Scalar, int n, bool Hpd>
676IGL_INLINE Eigen::Matrix<Scalar,n,1> igl::min_quad_with_fixed(
677 const Eigen::Matrix<Scalar,n,n> & H,
678 const Eigen::Matrix<Scalar,n,1> & f,
679 const Eigen::Array<bool,n,1> & k,
680 const Eigen::Matrix<Scalar,n,1> & bc)
681{
682 assert(H.isApprox(H.transpose(),1e-7));
683 assert(H.rows() == H.cols());
684 assert(H.rows() == f.size());
685 assert(H.rows() == k.size());
686 assert(H.rows() == bc.size());
687 const auto kcount = k.count();
688 // Everything fixed
689 if(kcount == (Eigen::Dynamic?H.rows():n))
690 {
691 return bc;
692 }
693 // Nothing fixed
694 if(kcount == 0)
695 {
696 // avoid function call
697 typedef Eigen::Matrix<Scalar,n,n> MatrixSn;
698 typedef typename
699 std::conditional<Hpd,Eigen::LLT<MatrixSn>,Eigen::CompleteOrthogonalDecomposition<MatrixSn>>::type
700 Solver;
701 return Solver(H).solve(-f);
702 }
703 // All-but-one fixed
704 if( (Eigen::Dynamic?H.rows():n)-kcount == 1)
705 {
706 // which one is not fixed?
707 int u = -1;
708 for(int i=0;i<k.size();i++){ if(!k(i)){ u=i; break; } }
709 assert(u>=0);
710 // min ½ x(u) Huu x(u) + x(u)(fu + H(u,k)bc(k))
711 // Huu x(u) = -(fu + H(u,k) bc(k))
712 // x(u) = (-fu + ∑ -Huj bcj)/Huu
713 Eigen::Matrix<Scalar,n,1> x = bc;
714 x(u) = -f(u);
715 for(int i=0;i<k.size();i++){ if(i!=u){ x(u)-=bc(i)*H(i,u); } }
716 x(u) /= H(u,u);
717 return x;
718 }
719 // Alec: Is there a smart template way to do this?
720 // jdumas: I guess you could do a templated for-loop starting from 16, and
721 // dispatching to the appropriate templated function when the argument matches
722 // (with a fallback to the dynamic version). Cf this example:
723 // https://gist.github.com/disconnect3d/13c2d035bb31b244df14
724 switch(kcount)
725 {
726 case 0: assert(false && "Handled above."); return Eigen::Matrix<Scalar,n,1>();
727 // % Matlibberish for generating these case statements:
728 // 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<Scalar,D?Eigen::Dynamic:n,D?Eigen::Dynamic:%d,Hpd>(H,f,k,bc);\n }\n',[i i i maxi i]);end
729 case 1:
730 {
731 const bool D = (n-1<=0)||(1>=n)||(n>16);
733 }
734 case 2:
735 {
736 const bool D = (n-2<=0)||(2>=n)||(n>16);
738 }
739 case 3:
740 {
741 const bool D = (n-3<=0)||(3>=n)||(n>16);
743 }
744 case 4:
745 {
746 const bool D = (n-4<=0)||(4>=n)||(n>16);
748 }
749 case 5:
750 {
751 const bool D = (n-5<=0)||(5>=n)||(n>16);
753 }
754 case 6:
755 {
756 const bool D = (n-6<=0)||(6>=n)||(n>16);
758 }
759 case 7:
760 {
761 const bool D = (n-7<=0)||(7>=n)||(n>16);
763 }
764 case 8:
765 {
766 const bool D = (n-8<=0)||(8>=n)||(n>16);
768 }
769 case 9:
770 {
771 const bool D = (n-9<=0)||(9>=n)||(n>16);
773 }
774 case 10:
775 {
776 const bool D = (n-10<=0)||(10>=n)||(n>16);
778 }
779 case 11:
780 {
781 const bool D = (n-11<=0)||(11>=n)||(n>16);
783 }
784 case 12:
785 {
786 const bool D = (n-12<=0)||(12>=n)||(n>16);
788 }
789 case 13:
790 {
791 const bool D = (n-13<=0)||(13>=n)||(n>16);
793 }
794 case 14:
795 {
796 const bool D = (n-14<=0)||(14>=n)||(n>16);
798 }
799 case 15:
800 {
801 const bool D = (n-15<=0)||(15>=n)||(n>16);
803 }
804 case 16:
805 {
806 const bool D = (n-16<=0)||(16>=n)||(n>16);
808 }
809 default:
811 }
812}
813
814template <typename Scalar, int n, int kcount, bool Hpd>
815IGL_INLINE Eigen::Matrix<Scalar,n,1> igl::min_quad_with_fixed(
816 const Eigen::Matrix<Scalar,n,n> & H,
817 const Eigen::Matrix<Scalar,n,1> & f,
818 const Eigen::Array<bool,n,1> & k,
819 const Eigen::Matrix<Scalar,n,1> & bc)
820{
821 // 0 and n should be handle outside this function
822 static_assert(kcount==Eigen::Dynamic || kcount>0 ,"");
823 static_assert(kcount==Eigen::Dynamic || kcount<n ,"");
824 const int ucount = n==Eigen::Dynamic ? Eigen::Dynamic : n-kcount;
825 static_assert(kcount==Eigen::Dynamic || ucount+kcount == n ,"");
826 static_assert((n==Eigen::Dynamic) == (ucount==Eigen::Dynamic),"");
827 static_assert((kcount==Eigen::Dynamic) == (ucount==Eigen::Dynamic),"");
828 assert((n==Eigen::Dynamic) || n == H.rows());
829 assert((kcount==Eigen::Dynamic) || kcount == k.count());
830 typedef Eigen::Matrix<Scalar,ucount,ucount> MatrixSuu;
831 typedef Eigen::Matrix<Scalar,ucount,kcount> MatrixSuk;
832 typedef Eigen::Matrix<Scalar,n,1> VectorSn;
833 typedef Eigen::Matrix<Scalar,ucount,1> VectorSu;
834 typedef Eigen::Matrix<Scalar,kcount,1> VectorSk;
835 const auto dyn_n = n==Eigen::Dynamic ? H.rows() : n;
836 const auto dyn_kcount = kcount==Eigen::Dynamic ? k.count() : kcount;
837 const auto dyn_ucount = ucount==Eigen::Dynamic ? dyn_n- dyn_kcount : ucount;
838 // For ucount==2 or kcount==2 this calls the coefficient initiliazer rather
839 // than the size initilizer, but I guess that's ok.
840 MatrixSuu Huu(dyn_ucount,dyn_ucount);
841 MatrixSuk Huk(dyn_ucount,dyn_kcount);
842 VectorSu mrhs(dyn_ucount);
843 VectorSk bck(dyn_kcount);
844 {
845 int ui = 0;
846 int ki = 0;
847 for(int i = 0;i<dyn_n;i++)
848 {
849 if(k(i))
850 {
851 bck(ki) = bc(i);
852 ki++;
853 }else
854 {
855 mrhs(ui) = f(i);
856 int uj = 0;
857 int kj = 0;
858 for(int j = 0;j<dyn_n;j++)
859 {
860 if(k(j))
861 {
862 Huk(ui,kj) = H(i,j);
863 kj++;
864 }else
865 {
866 Huu(ui,uj) = H(i,j);
867 uj++;
868 }
869 }
870 ui++;
871 }
872 }
873 }
874 mrhs += Huk * bck;
875 typedef typename
876 std::conditional<Hpd,
877 Eigen::LLT<MatrixSuu>,
878 // LDLT should be faster for indefinite problems but already found some
879 // cases where it was too inaccurate when called via quadprog_primal.
880 // Ideally this function takes LLT,LDLT, or
881 // CompleteOrthogonalDecomposition as a template parameter. "template
882 // template" parameters did work because LLT,LDLT have different number of
883 // template parameters from CompleteOrthogonalDecomposition. Perhaps
884 // there's a way to take advantage of LLT and LDLT's default template
885 // parameters (I couldn't figure out how).
886 Eigen::CompleteOrthogonalDecomposition<MatrixSuu>>::type
887 Solver;
888 VectorSu xu = Solver(Huu).solve(-mrhs);
889 VectorSn x(dyn_n);
890 {
891 int ui = 0;
892 int ki = 0;
893 for(int i = 0;i<dyn_n;i++)
894 {
895 if(k(i))
896 {
897 x(i) = bck(ki);
898 ki++;
899 }else
900 {
901 x(i) = xu(ui);
902 ui++;
903 }
904 }
905 }
906 return x;
907}
#define IGL_INLINE
Definition igl_inline.h:15
const auto all
Definition placeholders.h:14
bool is_symmetric(const Eigen::SparseMatrix< AT > &A)
Returns true if the given matrix is symmetric.
void slice(const Eigen::SparseMatrix< TX > &X, const Eigen::DenseBase< DerivedR > &R, const Eigen::DenseBase< DerivedC > &C, Eigen::SparseMatrix< TY > &Y)
Act like the matlab X(row_indices,col_indices) operator, where row_indices, col_indices are non-negat...
bool min_quad_with_fixed(const Eigen::SparseMatrix< T > &A, const Eigen::MatrixBase< DerivedB > &B, const Eigen::MatrixBase< Derivedknown > &known, const Eigen::MatrixBase< DerivedY > &Y, const Eigen::SparseMatrix< T > &Aeq, const Eigen::MatrixBase< DerivedBeq > &Beq, const bool pd, Eigen::PlainObjectBase< DerivedZ > &Z)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition min_quad_with_fixed.impl.h:590
S_type EPS()
Function returning EPS for corresponding type.
void find(const Eigen::SparseMatrix< T > &X, Eigen::DenseBase< DerivedI > &I, Eigen::DenseBase< DerivedJ > &J, Eigen::DenseBase< DerivedV > &V)
Find the non-zero entries and there respective indices in a sparse matrix.
void cat(const int dim, const Eigen::SparseMatrix< Scalar > &A, const Eigen::SparseMatrix< Scalar > &B, Eigen::SparseMatrix< Scalar > &C)
Perform concatenation of a two sparse matrices along a single dimension If dim == 1,...
bool min_quad_with_fixed_solve(const min_quad_with_fixed_data< T > &data, const Eigen::MatrixBase< DerivedB > &B, const Eigen::MatrixBase< DerivedY > &Y, const Eigen::MatrixBase< DerivedBeq > &Beq, Eigen::PlainObjectBase< DerivedZ > &Z, Eigen::PlainObjectBase< Derivedsol > &sol)
Solves a system previously factored using min_quad_with_fixed_precompute.
Definition min_quad_with_fixed.impl.h:417
bool min_quad_with_fixed_precompute(const Eigen::SparseMatrix< T > &A, const Eigen::MatrixBase< Derivedknown > &known, const Eigen::SparseMatrix< T > &Aeq, const bool pd, min_quad_with_fixed_data< T > &data)
Minimize a convex quadratic energy subject to fixed value and linear equality constraints.
Definition min_quad_with_fixed.impl.h:31
Parameters and precomputed values for min_quad_with_fixed.
Definition min_quad_with_fixed.h:166
Eigen::VectorXi known
Indices of known variables.
Definition min_quad_with_fixed.h:174
Eigen::SparseQR< Eigen::SparseMatrix< T >, Eigen::COLAMDOrdering< int > > AeqTQR
Definition min_quad_with_fixed.h:201
@ LU
Definition min_quad_with_fixed.h:188
@ LDLT
Definition min_quad_with_fixed.h:187
@ QR_LLT
Definition min_quad_with_fixed.h:189
@ LLT
Definition min_quad_with_fixed.h:186
Eigen::SimplicialLDLT< Eigen::SparseMatrix< T > > ldlt
Definition min_quad_with_fixed.h:194
Eigen::SparseMatrix< T > preY
Matrix multiplied against Y when constructing right hand side.
Definition min_quad_with_fixed.h:182
Eigen::VectorXi unknown
Indices of unknown variables.
Definition min_quad_with_fixed.h:176
Eigen::SimplicialLLT< Eigen::SparseMatrix< T > > llt
Solver data (factorization).
Definition min_quad_with_fixed.h:193
Eigen::SparseMatrix< T > AeqTQ2
Definition min_quad_with_fixed.h:207
Eigen::SparseMatrix< T > AeqTQ1
Definition min_quad_with_fixed.h:205
Eigen::SparseLU< Eigen::SparseMatrix< T, Eigen::ColMajor >, Eigen::COLAMDOrdering< int > > lu
Definition min_quad_with_fixed.h:195
int n
Size of original system: number of unknowns + number of knowns.
Definition min_quad_with_fixed.h:168
bool Aeq_li
QR factorization Are rows of Aeq linearly independent?
Definition min_quad_with_fixed.h:198
Eigen::VectorXi lagrange
Indices of lagrange variables.
Definition min_quad_with_fixed.h:178
bool Auu_sym
Whether A(unknown,unknown) is symmetric.
Definition min_quad_with_fixed.h:172
int neq
Columns of Aeq corresponding to unknowns.
Definition min_quad_with_fixed.h:200
Eigen::SparseMatrix< T > AeqTR1
Definition min_quad_with_fixed.h:209
Eigen::VectorXi unknown_lagrange
Indices of unknown variable followed by Indices of lagrange variables.
Definition min_quad_with_fixed.h:180
Eigen::SparseMatrix< T > AeqTET
Definition min_quad_with_fixed.h:212
bool Auu_pd
Whether A(unknown,unknown) is positive definite.
Definition min_quad_with_fixed.h:170
Eigen::SparseMatrix< T > AeqTR1T
Definition min_quad_with_fixed.h:210
Eigen::SparseMatrix< T > AeqTQ2T
Definition min_quad_with_fixed.h:208
Eigen::SparseMatrix< T > Aeqk
Definition min_quad_with_fixed.h:202
Eigen::SparseMatrix< T > Auu
Definition min_quad_with_fixed.h:204
enum igl::min_quad_with_fixed_data::SolverType solver_type
Eigen::SparseMatrix< T > Aequ
Definition min_quad_with_fixed.h:203
Eigen::SparseMatrix< T > AeqTE
Definition min_quad_with_fixed.h:211
Eigen::SparseMatrix< T > AeqTQ1T
Definition min_quad_with_fixed.h:206