min_quad_with_fixed.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2013 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. #include "min_quad_with_fixed.h"
  9. #include "slice.h"
  10. #include "is_symmetric.h"
  11. #include "find.h"
  12. #include "sparse.h"
  13. #include "repmat.h"
  14. #include "lu_lagrange.h"
  15. #include "full.h"
  16. #include "matlab_format.h"
  17. #include "EPS.h"
  18. #include "cat.h"
  19. //#include <Eigen/SparseExtra>
  20. // Bug in unsupported/Eigen/SparseExtra needs iostream first
  21. #include <iostream>
  22. #include <unsupported/Eigen/SparseExtra>
  23. #include <cassert>
  24. #include <cstdio>
  25. #include <iostream>
  26. template <typename T, typename Derivedknown>
  27. IGL_INLINE bool igl::min_quad_with_fixed_precompute(
  28. const Eigen::SparseMatrix<T>& A,
  29. const Eigen::PlainObjectBase<Derivedknown> & known,
  30. const Eigen::SparseMatrix<T>& Aeq,
  31. const bool pd,
  32. min_quad_with_fixed_data<T> & data
  33. )
  34. {
  35. //#define MIN_QUAD_WITH_FIXED_CPP_DEBUG
  36. using namespace Eigen;
  37. using namespace std;
  38. using namespace igl;
  39. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  40. cout<<" pre"<<endl;
  41. #endif
  42. // number of rows
  43. int n = A.rows();
  44. // cache problem size
  45. data.n = n;
  46. int neq = Aeq.rows();
  47. // default is to have 0 linear equality constraints
  48. if(Aeq.size() != 0)
  49. {
  50. assert(n == Aeq.cols());
  51. }
  52. assert(A.rows() == n);
  53. assert(A.cols() == n);
  54. // number of known rows
  55. int kr = known.size();
  56. assert(kr == 0 || known.minCoeff() >= 0);
  57. assert(kr == 0 || known.maxCoeff() < n);
  58. assert(neq <= n);
  59. // cache known
  60. data.known = known;
  61. // get list of unknown indices
  62. data.unknown.resize(n-kr);
  63. std::vector<bool> unknown_mask;
  64. unknown_mask.resize(n,true);
  65. for(int i = 0;i<kr;i++)
  66. {
  67. unknown_mask[known(i)] = false;
  68. }
  69. int u = 0;
  70. for(int i = 0;i<n;i++)
  71. {
  72. if(unknown_mask[i])
  73. {
  74. data.unknown(u) = i;
  75. u++;
  76. }
  77. }
  78. // get list of lagrange multiplier indices
  79. data.lagrange.resize(neq);
  80. for(int i = 0;i<neq;i++)
  81. {
  82. data.lagrange(i) = n + i;
  83. }
  84. // cache unknown followed by lagrange indices
  85. data.unknown_lagrange.resize(data.unknown.size()+data.lagrange.size());
  86. data.unknown_lagrange << data.unknown, data.lagrange;
  87. SparseMatrix<T> Auu;
  88. slice(A,data.unknown,data.unknown,Auu);
  89. // Positive definiteness is *not* determined, rather it is given as a
  90. // parameter
  91. data.Auu_pd = pd;
  92. if(data.Auu_pd)
  93. {
  94. // PD implies symmetric
  95. data.Auu_sym = true;
  96. // This is an annoying assertion unless EPS can be chosen in a nicer way.
  97. //assert(is_symmetric(Auu,EPS<double>()));
  98. assert(is_symmetric(Auu,1.0));
  99. }else
  100. {
  101. // determine if A(unknown,unknown) is symmetric and/or positive definite
  102. data.Auu_sym = is_symmetric(Auu,EPS<double>());
  103. }
  104. // Determine number of linearly independent constraints
  105. int nc = 0;
  106. if(neq>0)
  107. {
  108. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  109. cout<<" qr"<<endl;
  110. #endif
  111. // QR decomposition to determine row rank in Aequ
  112. slice(Aeq,data.unknown,2,data.Aequ);
  113. assert(data.Aequ.rows() == neq);
  114. assert(data.Aequ.cols() == data.unknown.size());
  115. data.AeqTQR.compute(data.Aequ.transpose().eval());
  116. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  117. cout<<endl<<matlab_format(SparseMatrix<T>(data.Aequ.transpose().eval()),"AeqT")<<endl<<endl;
  118. #endif
  119. switch(data.AeqTQR.info())
  120. {
  121. case Eigen::Success:
  122. break;
  123. case Eigen::NumericalIssue:
  124. cerr<<"Error: Numerical issue."<<endl;
  125. return false;
  126. case Eigen::InvalidInput:
  127. cerr<<"Error: Invalid input."<<endl;
  128. return false;
  129. default:
  130. cerr<<"Error: Other."<<endl;
  131. return false;
  132. }
  133. nc = data.AeqTQR.rank();
  134. assert(nc<=neq);
  135. data.Aeq_li = nc == neq;
  136. }else
  137. {
  138. data.Aeq_li = true;
  139. }
  140. if(data.Aeq_li)
  141. {
  142. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  143. cout<<" Aeq_li=true"<<endl;
  144. #endif
  145. // Append lagrange multiplier quadratic terms
  146. SparseMatrix<T> new_A;
  147. SparseMatrix<T> AeqT = Aeq.transpose();
  148. SparseMatrix<T> Z(neq,neq);
  149. // This is a bit slower. But why isn't cat fast?
  150. new_A = cat(1, cat(2, A, AeqT ),
  151. cat(2, Aeq, Z ));
  152. // precompute RHS builders
  153. if(kr > 0)
  154. {
  155. SparseMatrix<T> Aulk,Akul;
  156. // Slow
  157. slice(new_A,data.unknown_lagrange,data.known,Aulk);
  158. //// This doesn't work!!!
  159. //data.preY = Aulk + Akul.transpose();
  160. // Slow
  161. if(data.Auu_sym)
  162. {
  163. data.preY = Aulk*2;
  164. }else
  165. {
  166. slice(new_A,data.known,data.unknown_lagrange,Akul);
  167. SparseMatrix<T> AkulT = Akul.transpose();
  168. data.preY = Aulk + AkulT;
  169. }
  170. }else
  171. {
  172. data.preY.resize(data.unknown_lagrange.size(),0);
  173. }
  174. // Positive definite and no equality constraints (Postive definiteness
  175. // implies symmetric)
  176. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  177. cout<<" factorize"<<endl;
  178. #endif
  179. if(data.Auu_pd && neq == 0)
  180. {
  181. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  182. cout<<" llt"<<endl;
  183. #endif
  184. data.llt.compute(Auu);
  185. switch(data.llt.info())
  186. {
  187. case Eigen::Success:
  188. break;
  189. case Eigen::NumericalIssue:
  190. cerr<<"Error: Numerical issue."<<endl;
  191. return false;
  192. default:
  193. cerr<<"Error: Other."<<endl;
  194. return false;
  195. }
  196. data.solver_type = min_quad_with_fixed_data<T>::LLT;
  197. }else
  198. {
  199. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  200. cout<<" ldlt"<<endl;
  201. #endif
  202. // Either not PD or there are equality constraints
  203. SparseMatrix<T> NA;
  204. slice(new_A,data.unknown_lagrange,data.unknown_lagrange,NA);
  205. data.NA = NA;
  206. // Ideally we'd use LDLT but Eigen doesn't support positive semi-definite
  207. // matrices:
  208. // http://forum.kde.org/viewtopic.php?f=74&t=106962&p=291990#p291990
  209. if(data.Auu_sym && false)
  210. {
  211. data.ldlt.compute(NA);
  212. switch(data.ldlt.info())
  213. {
  214. case Eigen::Success:
  215. break;
  216. case Eigen::NumericalIssue:
  217. cerr<<"Error: Numerical issue."<<endl;
  218. return false;
  219. default:
  220. cerr<<"Error: Other."<<endl;
  221. return false;
  222. }
  223. data.solver_type = min_quad_with_fixed_data<T>::LDLT;
  224. }else
  225. {
  226. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  227. cout<<" lu"<<endl;
  228. #endif
  229. // Resort to LU
  230. // Bottleneck >1/2
  231. data.lu.compute(NA);
  232. //std::cout<<"NA=["<<std::endl<<NA<<std::endl<<"];"<<std::endl;
  233. switch(data.lu.info())
  234. {
  235. case Eigen::Success:
  236. break;
  237. case Eigen::NumericalIssue:
  238. cerr<<"Error: Numerical issue."<<endl;
  239. return false;
  240. case Eigen::InvalidInput:
  241. cerr<<"Error: Invalid Input."<<endl;
  242. return false;
  243. default:
  244. cerr<<"Error: Other."<<endl;
  245. return false;
  246. }
  247. data.solver_type = min_quad_with_fixed_data<T>::LU;
  248. }
  249. }
  250. }else
  251. {
  252. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  253. cout<<" Aeq_li=false"<<endl;
  254. #endif
  255. data.neq = neq;
  256. const int nu = data.unknown.size();
  257. //cout<<"nu: "<<nu<<endl;
  258. //cout<<"neq: "<<neq<<endl;
  259. //cout<<"nc: "<<nc<<endl;
  260. //cout<<" matrixR"<<endl;
  261. SparseMatrix<T> AeqTR,AeqTQ;
  262. AeqTR = data.AeqTQR.matrixR();
  263. // This shouldn't be necessary
  264. AeqTR.prune(0.0);
  265. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  266. cout<<" matrixQ"<<endl;
  267. #endif
  268. // THIS IS ESSENTIALLY DENSE AND THIS IS BY FAR THE BOTTLENECK
  269. // http://forum.kde.org/viewtopic.php?f=74&t=117500
  270. AeqTQ = data.AeqTQR.matrixQ();
  271. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  272. cout<<" prune"<<endl;
  273. cout<<" nnz: "<<AeqTQ.nonZeros()<<endl;
  274. #endif
  275. // This shouldn't be necessary
  276. AeqTQ.prune(0.0);
  277. //cout<<"AeqTQ: "<<AeqTQ.rows()<<" "<<AeqTQ.cols()<<endl;
  278. //cout<<matlab_format(AeqTQ,"AeqTQ")<<endl;
  279. //cout<<" perms"<<endl;
  280. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  281. cout<<" nnz: "<<AeqTQ.nonZeros()<<endl;
  282. cout<<" perm"<<endl;
  283. #endif
  284. SparseMatrix<double> I(neq,neq);
  285. I.setIdentity();
  286. data.AeqTE = data.AeqTQR.colsPermutation() * I;
  287. data.AeqTET = data.AeqTQR.colsPermutation().transpose() * I;
  288. assert(AeqTR.rows() == neq);
  289. assert(AeqTQ.rows() == nu);
  290. assert(AeqTQ.cols() == nu);
  291. assert(AeqTR.cols() == neq);
  292. //cout<<" slice"<<endl;
  293. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  294. cout<<" slice"<<endl;
  295. #endif
  296. data.AeqTQ1 = AeqTQ.topLeftCorner(nu,nc);
  297. data.AeqTQ1T = data.AeqTQ1.transpose().eval();
  298. // ALREADY TRIM (Not 100% sure about this)
  299. data.AeqTR1 = AeqTR.topLeftCorner(nc,nc);
  300. data.AeqTR1T = data.AeqTR1.transpose().eval();
  301. //cout<<"AeqTR1T.size() "<<data.AeqTR1T.rows()<<" "<<data.AeqTR1T.cols()<<endl;
  302. // Null space
  303. data.AeqTQ2 = AeqTQ.bottomRightCorner(nu,nu-nc);
  304. data.AeqTQ2T = data.AeqTQ2.transpose().eval();
  305. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  306. cout<<" proj"<<endl;
  307. #endif
  308. // Projected hessian
  309. SparseMatrix<T> QRAuu = data.AeqTQ2T * Auu * data.AeqTQ2;
  310. {
  311. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  312. cout<<" factorize"<<endl;
  313. #endif
  314. // QRAuu should always be PD
  315. data.llt.compute(QRAuu);
  316. switch(data.llt.info())
  317. {
  318. case Eigen::Success:
  319. break;
  320. case Eigen::NumericalIssue:
  321. cerr<<"Error: Numerical issue."<<endl;
  322. return false;
  323. default:
  324. cerr<<"Error: Other."<<endl;
  325. return false;
  326. }
  327. data.solver_type = min_quad_with_fixed_data<T>::QR_LLT;
  328. }
  329. #ifdef MIN_QUAD_WITH_FIXED_CPP_DEBUG
  330. cout<<" smash"<<endl;
  331. #endif
  332. // Known value multiplier
  333. SparseMatrix<T> Auk;
  334. slice(A,data.unknown,data.known,Auk);
  335. SparseMatrix<T> Aku;
  336. slice(A,data.known,data.unknown,Aku);
  337. SparseMatrix<T> AkuT = Aku.transpose();
  338. data.preY = Auk + AkuT;
  339. // Needed during solve
  340. data.Auu = Auu;
  341. slice(Aeq,data.known,2,data.Aeqk);
  342. assert(data.Aeqk.rows() == neq);
  343. assert(data.Aeqk.cols() == data.known.size());
  344. }
  345. return true;
  346. }
  347. template <
  348. typename T,
  349. typename DerivedB,
  350. typename DerivedY,
  351. typename DerivedBeq,
  352. typename DerivedZ,
  353. typename Derivedsol>
  354. IGL_INLINE bool igl::min_quad_with_fixed_solve(
  355. const min_quad_with_fixed_data<T> & data,
  356. const Eigen::PlainObjectBase<DerivedB> & B,
  357. const Eigen::PlainObjectBase<DerivedY> & Y,
  358. const Eigen::PlainObjectBase<DerivedBeq> & Beq,
  359. Eigen::PlainObjectBase<DerivedZ> & Z,
  360. Eigen::PlainObjectBase<Derivedsol> & sol)
  361. {
  362. using namespace std;
  363. using namespace Eigen;
  364. using namespace igl;
  365. // number of known rows
  366. int kr = data.known.size();
  367. if(kr!=0)
  368. {
  369. assert(kr == Y.rows());
  370. }
  371. // number of columns to solve
  372. int cols = Y.cols();
  373. assert(B.cols() == 1);
  374. assert(Beq.size() == 0 || Beq.cols() == 1);
  375. // resize output
  376. Z.resize(data.n,cols);
  377. // Set known values
  378. for(int i = 0;i < kr;i++)
  379. {
  380. for(int j = 0;j < cols;j++)
  381. {
  382. Z(data.known(i),j) = Y(i,j);
  383. }
  384. }
  385. if(data.Aeq_li)
  386. {
  387. // number of lagrange multipliers aka linear equality constraints
  388. int neq = data.lagrange.size();
  389. // append lagrange multiplier rhs's
  390. Eigen::Matrix<T,Eigen::Dynamic,1> BBeq(B.size() + Beq.size());
  391. BBeq << B, (Beq*-2.0);
  392. // Build right hand side
  393. Eigen::Matrix<T,Eigen::Dynamic,1> BBequl;
  394. igl::slice(BBeq,data.unknown_lagrange,BBequl);
  395. Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> BBequlcols;
  396. igl::repmat(BBequl,1,cols,BBequlcols);
  397. Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> NB;
  398. if(kr == 0)
  399. {
  400. NB = BBequlcols;
  401. }else
  402. {
  403. NB = data.preY * Y + BBequlcols;
  404. }
  405. //std::cout<<"NB=["<<std::endl<<NB<<std::endl<<"];"<<std::endl;
  406. //cout<<matlab_format(NB,"NB")<<endl;
  407. switch(data.solver_type)
  408. {
  409. case igl::min_quad_with_fixed_data<T>::LLT:
  410. sol = data.llt.solve(NB);
  411. break;
  412. case igl::min_quad_with_fixed_data<T>::LDLT:
  413. sol = data.ldlt.solve(NB);
  414. break;
  415. case igl::min_quad_with_fixed_data<T>::LU:
  416. // Not a bottleneck
  417. sol = data.lu.solve(NB);
  418. break;
  419. default:
  420. cerr<<"Error: invalid solver type"<<endl;
  421. return false;
  422. }
  423. //std::cout<<"sol=["<<std::endl<<sol<<std::endl<<"];"<<std::endl;
  424. // Now sol contains sol/-0.5
  425. sol *= -0.5;
  426. // Now sol contains solution
  427. // Place solution in Z
  428. for(int i = 0;i<(sol.rows()-neq);i++)
  429. {
  430. for(int j = 0;j<sol.cols();j++)
  431. {
  432. Z(data.unknown_lagrange(i),j) = sol(i,j);
  433. }
  434. }
  435. }else
  436. {
  437. assert(data.solver_type == min_quad_with_fixed_data<T>::QR_LLT);
  438. PlainObjectBase<DerivedBeq> eff_Beq;
  439. // Adjust Aeq rhs to include known parts
  440. eff_Beq =
  441. //data.AeqTQR.colsPermutation().transpose() * (-data.Aeqk * Y + Beq);
  442. data.AeqTET * (-data.Aeqk * Y + Beq);
  443. // Where did this -0.5 come from? Probably the same place as above.
  444. PlainObjectBase<DerivedB> Bu;
  445. slice(B,data.unknown,Bu);
  446. PlainObjectBase<DerivedB> NB;
  447. NB = -0.5*(Bu + data.preY * Y);
  448. // Trim eff_Beq
  449. const int nc = data.AeqTQR.rank();
  450. const int neq = Beq.rows();
  451. eff_Beq = eff_Beq.topLeftCorner(nc,1).eval();
  452. data.AeqTR1T.template triangularView<Lower>().solveInPlace(eff_Beq);
  453. // Now eff_Beq = (data.AeqTR1T \ (data.AeqTET * (-data.Aeqk * Y + Beq)))
  454. PlainObjectBase<DerivedB> lambda_0;
  455. lambda_0 = data.AeqTQ1 * eff_Beq;
  456. //cout<<matlab_format(lambda_0,"lambda_0")<<endl;
  457. PlainObjectBase<DerivedB> QRB;
  458. QRB = -data.AeqTQ2T * (data.Auu * lambda_0) + data.AeqTQ2T * NB;
  459. PlainObjectBase<Derivedsol> lambda;
  460. lambda = data.llt.solve(QRB);
  461. // prepare output
  462. PlainObjectBase<Derivedsol> solu;
  463. solu = data.AeqTQ2 * lambda + lambda_0;
  464. // http://www.math.uh.edu/~rohop/fall_06/Chapter3.pdf
  465. PlainObjectBase<Derivedsol> solLambda;
  466. {
  467. PlainObjectBase<Derivedsol> temp1,temp2;
  468. temp1 = (data.AeqTQ1T * NB - data.AeqTQ1T * data.Auu * solu);
  469. data.AeqTR1.template triangularView<Upper>().solveInPlace(temp1);
  470. //cout<<matlab_format(temp1,"temp1")<<endl;
  471. temp2 = PlainObjectBase<Derivedsol>::Zero(neq,1);
  472. temp2.topLeftCorner(nc,1) = temp1;
  473. //solLambda = data.AeqTQR.colsPermutation() * temp2;
  474. solLambda = data.AeqTE * temp2;
  475. }
  476. // sol is [Z(unknown);Lambda]
  477. assert(data.unknown.size() == solu.rows());
  478. assert(cols == solu.cols());
  479. assert(data.neq == neq);
  480. assert(data.neq == solLambda.rows());
  481. assert(cols == solLambda.cols());
  482. sol.resize(data.unknown.size()+data.neq,cols);
  483. sol.block(0,0,solu.rows(),solu.cols()) = solu;
  484. sol.block(solu.rows(),0,solLambda.rows(),solLambda.cols()) = solLambda;
  485. for(int u = 0;u<data.unknown.size();u++)
  486. {
  487. for(int j = 0;j<Z.cols();j++)
  488. {
  489. Z(data.unknown(u),j) = solu(u,j);
  490. }
  491. }
  492. }
  493. return true;
  494. }
  495. template <
  496. typename T,
  497. typename DerivedB,
  498. typename DerivedY,
  499. typename DerivedBeq,
  500. typename DerivedZ>
  501. IGL_INLINE bool igl::min_quad_with_fixed_solve(
  502. const min_quad_with_fixed_data<T> & data,
  503. const Eigen::PlainObjectBase<DerivedB> & B,
  504. const Eigen::PlainObjectBase<DerivedY> & Y,
  505. const Eigen::PlainObjectBase<DerivedBeq> & Beq,
  506. Eigen::PlainObjectBase<DerivedZ> & Z)
  507. {
  508. Eigen::PlainObjectBase<DerivedZ> sol;
  509. return min_quad_with_fixed_solve(data,B,Y,Beq,Z,sol);
  510. }
  511. #ifndef IGL_HEADER_ONLY
  512. // Explicit template specialization
  513. template bool igl::min_quad_with_fixed_solve<double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(igl::min_quad_with_fixed_data<double> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
  514. template bool igl::min_quad_with_fixed_precompute<double, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<double, 0, int> const&, bool, igl::min_quad_with_fixed_data<double>&);
  515. template bool igl::min_quad_with_fixed_solve<double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(igl::min_quad_with_fixed_data<double> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
  516. #endif