PolyFEM
Loading...
Searching...
No Matches
NLProblem.cpp
Go to the documentation of this file.
1#include "NLProblem.hpp"
2
6
8
9#include <polysolve/linear/Solver.hpp>
10#ifdef POLYSOLVE_WITH_SPQR
11#include <Eigen/SPQRSupport>
12#include <SuiteSparseQR.hpp>
13#endif
14
15#ifdef POLYFEM_WITH_ARMADILLO
16#include <armadillo>
17#endif
18
19#include <igl/cat.h>
20#include <igl/Timer.h>
21
22#include <set>
23
24/*
25m \frac{\partial^2 u}{\partial t^2} = \psi = \text{div}(\sigma[u])\newline
26u^{t+1} = u(t+\Delta t)\approx u(t) + \Delta t \dot u + \frac{\Delta t^2} 2 \ddot u \newline
27= u(t) + \Delta t \dot u + \frac{\Delta t^2}{2} \psi\newline
28M u^{t+1}_h \approx M u^t_h + \Delta t M v^t_h + \frac{\Delta t^2} {2} A u^{t+1}_h \newline
29%
30M (u^{t+1}_h - (u^t_h + \Delta t v^t_h)) - \frac{\Delta t^2} {2} A u^{t+1}_h
31*/
32// mü = ψ = div(σ[u])
33// uᵗ⁺¹ = u(t + Δt) ≈ u(t) + Δtu̇ + ½Δt²ü = u(t) + Δtu̇ + ½Δt²ψ
34// Muₕᵗ⁺¹ ≈ Muₕᵗ + ΔtMvₕᵗ ½Δt²Auₕᵗ⁺¹
35// Root-finding form:
36// M(uₕᵗ⁺¹ - (uₕᵗ + Δtvₕᵗ)) - ½Δt²Auₕᵗ⁺¹ = 0
37
38namespace polyfem::solver
39{
40
41 namespace
42 {
43#ifdef POLYSOLVE_WITH_SPQR
44 using SuiteSparseMatrix = Eigen::SparseMatrix<double, Eigen::ColMajor, SuiteSparse_long>;
45
46 void fill_cholmod(SuiteSparseMatrix &mat, cholmod_sparse &cmat)
47 {
48 cmat.nzmax = mat.nonZeros();
49 cmat.nrow = mat.rows();
50 cmat.ncol = mat.cols();
51 cmat.p = mat.outerIndexPtr();
52 cmat.i = mat.innerIndexPtr();
53 cmat.x = mat.valuePtr();
54 cmat.z = 0;
55 cmat.sorted = 1;
56 cmat.packed = 1;
57 cmat.nz = 0;
58 cmat.dtype = 0;
59 cmat.stype = -1;
60 cmat.xtype = CHOLMOD_REAL;
61 cmat.dtype = CHOLMOD_DOUBLE;
62 cmat.stype = 0;
63 cmat.itype = CHOLMOD_LONG;
64 }
65#endif
66
67#ifdef POLYFEM_WITH_ARMADILLO
68 arma::sp_mat fill_arma(const StiffnessMatrix &mat)
69 {
70 std::vector<unsigned long long> rowind_vect(mat.innerIndexPtr(), mat.innerIndexPtr() + mat.nonZeros());
71 std::vector<unsigned long long> colptr_vect(mat.outerIndexPtr(), mat.outerIndexPtr() + mat.outerSize() + 1);
72 std::vector<double> values_vect(mat.valuePtr(), mat.valuePtr() + mat.nonZeros());
73
74 arma::dvec values(values_vect.data(), values_vect.size(), false);
75 arma::uvec rowind(rowind_vect.data(), rowind_vect.size(), false);
76 arma::uvec colptr(colptr_vect.data(), colptr_vect.size(), false);
77
78 arma::sp_mat amat(rowind, colptr, values, mat.rows(), mat.cols(), false);
79
80 return amat;
81 }
82
83 StiffnessMatrix fill_eigen(const arma::sp_mat &mat)
84 {
85 // convert to eigen sparse
86 std::vector<long> outerIndexPtr(mat.row_indices, mat.row_indices + mat.n_nonzero);
87 std::vector<long> innerIndexPtr(mat.col_ptrs, mat.col_ptrs + mat.n_cols + 1);
88
89 const StiffnessMatrix out = Eigen::Map<const Eigen::SparseMatrix<double, Eigen::ColMajor, long>>(
90 mat.n_rows, mat.n_cols, mat.n_nonzero, innerIndexPtr.data(), outerIndexPtr.data(), mat.values);
91 return out;
92 }
93#endif
94
95 } // namespace
97 const int full_size,
98 const std::vector<std::shared_ptr<Form>> &forms,
99 const std::vector<std::shared_ptr<AugmentedLagrangianForm>> &penalty_forms,
100 const std::shared_ptr<polysolve::linear::Solver> &solver,
101 const bool is_residual)
102 : FullNLProblem(forms, is_residual),
103 full_size_(full_size),
104 t_(0),
105 penalty_forms_(penalty_forms),
106 solver_(solver)
107 {
110 }
111
113 const int full_size,
114 const double t,
115 const std::vector<std::shared_ptr<Form>> &forms,
116 const std::vector<std::shared_ptr<AugmentedLagrangianForm>> &penalty_forms,
117 const std::shared_ptr<polysolve::linear::Solver> &solver,
118 const double char_length,
119 const double char_force,
120 StiffnessMatrix lumped_mass,
121 const int dimension,
122 const bool is_residual)
123 : FullNLProblem(forms, is_residual),
124 full_size_(full_size),
125 t_(t),
126 F0(char_force),
127 L(char_length),
128 dim(dimension),
129 lumped_mass_(lumped_mass.diagonal().asDiagonal()),
130 penalty_forms_(penalty_forms),
131 solver_(solver)
132 {
135
136 double total_lumped_mass = 0;
137 int num_nonzero_mass_entries = 0;
138 for (int i = 0; i < lumped_mass_.diagonal().size(); i++)
139 {
140 if (lumped_mass_.diagonal()[i] > 0)
141 {
142 total_lumped_mass += lumped_mass_.diagonal()[i];
143 num_nonzero_mass_entries++;
144 }
145 }
146 const double avg_lumped_mass = total_lumped_mass / num_nonzero_mass_entries;
147 for (int i = 0; i < lumped_mass_.diagonal().size(); i++)
148 {
149 if (lumped_mass_.diagonal()[i] == 0)
150 {
151 lumped_mass_.diagonal()[i] = avg_lumped_mass;
152 }
153 }
154 }
155
157 {
158 return 1;
159
160 // double total_weight = 0;
161 // for (const auto &f : forms_)
162 // total_weight += f->weight();
163 // if (full_size() == current_size())
164 // {
165 // for (const auto &f : penalty_forms_)
166 // total_weight += f->weight() * f->lagrangian_weight();
167 // }
168
169 // logger().debug("Normalizing forms with scale: {}", total_weight);
170
171 // for (auto &f : forms_)
172 // f->set_scale(total_weight);
173 // for (auto &f : penalty_forms_)
174 // f->set_scale(total_weight);
175
176 // return total_weight;
177 }
178
179 double NLProblem::grad_norm_rescaling(const polysolve::nonlinear::NormType norm_type) const
180 {
181 if (is_residual())
182 return 1;
183
184 switch (norm_type)
185 {
186 case polysolve::nonlinear::NormType::EUCLIDEAN:
187 return 1;
188 case polysolve::nonlinear::NormType::L2:
189 return F0 * (dim == 2 ? L : std::pow(L, 1.5));
190 case polysolve::nonlinear::NormType::Linf:
191 return F0;
192 default:
193 break;
194 }
195 log_and_throw_error("Unrecognized norm type!");
196 }
197
198 double NLProblem::step_norm_rescaling(const polysolve::nonlinear::NormType norm_type) const
199 {
200 switch (norm_type)
201 {
202 case polysolve::nonlinear::NormType::EUCLIDEAN:
203 return 1;
204 case polysolve::nonlinear::NormType::L2:
205 return dim == 2 ? L * L : std::pow(L, 2.5);
206 case polysolve::nonlinear::NormType::Linf:
207 return L;
208 default:
209 break;
210 }
211 log_and_throw_error("Unrecognized norm type!");
212 }
213
214 double NLProblem::energy_norm_rescaling(const polysolve::nonlinear::NormType norm_type) const
215 {
216 if (is_residual())
217 return 1;
218
219 const double density_scale = dim == 2 ? L * L : L * L * L;
220 return F0 * density_scale * L;
221 }
222
223 double NLProblem::grad_norm(const TVector &grad, const polysolve::nonlinear::NormType norm_type) const
224 {
225 switch (norm_type)
226 {
227 case polysolve::nonlinear::NormType::EUCLIDEAN:
228 return grad.norm();
229 case polysolve::nonlinear::NormType::L2:
230 return sqrt(grad.transpose() * current_lumped_mass().inverse() * grad);
231 case polysolve::nonlinear::NormType::Linf:
232 return (current_lumped_mass().inverse() * grad).cwiseAbs().maxCoeff();
233 default:
234 break;
235 }
236 log_and_throw_error("Unrecognized norm type!");
237 }
238
239 double NLProblem::step_norm(const TVector &x, const polysolve::nonlinear::NormType norm_type) const
240 {
241 switch (norm_type)
242 {
243 case polysolve::nonlinear::NormType::EUCLIDEAN:
244 return x.norm();
245 case polysolve::nonlinear::NormType::L2:
246 return sqrt(x.transpose() * current_lumped_mass() * x);
247 case polysolve::nonlinear::NormType::Linf:
248 return x.cwiseAbs().maxCoeff();
249 default:
250 break;
251 }
252 log_and_throw_error("Unrecognized norm type!");
253 }
254
256 {
257 if (penalty_forms_.empty())
258 {
261 return;
262 }
263 igl::Timer timer;
264
265 if (penalty_forms_.size() == 1 && penalty_forms_.front()->has_projection())
266 {
267 Q2_ = penalty_forms_.front()->constraint_projection_matrix();
268 Q2t_ = Q2_.transpose();
269
270 reduced_size_ = Q2_.cols();
271 if (reduced_size_ == 0)
272 return;
274
275 timer.start();
276 StiffnessMatrix Q2tQ2 = Q2t_ * Q2_;
277 solver_->analyze_pattern(Q2tQ2, Q2tQ2.rows());
278 solver_->factorize(Q2tQ2);
279 timer.stop();
280 logger().debug("Factorization and computation of Q2tQ2 took: {}", timer.getElapsedTime());
281
282 std::vector<std::shared_ptr<Form>> tmp;
283 tmp.insert(tmp.end(), penalty_forms_.begin(), penalty_forms_.end());
284 penalty_problem_ = std::make_shared<FullNLProblem>(tmp);
285
287
288 return;
289 }
290
291 std::vector<Eigen::Triplet<double>> Ae;
292 int index = 0;
293 for (const auto &f : penalty_forms_)
294 {
295 const auto &tmp = f->constraint_matrix();
296 for (int i = 0; i < tmp.outerSize(); i++)
297 {
298 for (typename StiffnessMatrix::InnerIterator it(tmp, i); it; ++it)
299 {
300 Ae.emplace_back(index + it.row(), it.col(), it.value());
301 }
302 }
303 index += tmp.rows();
304 }
305 StiffnessMatrix A(index, full_size_);
306 A.setFromTriplets(Ae.begin(), Ae.end());
307 A.makeCompressed();
308
309 if (A.rows() == 0)
310 {
313 return;
314 }
315
316 int constraint_size = A.rows();
317 num_penalty_constraints_ = A.rows();
318#ifdef POLYSOLVE_WITH_SPQR
319 SuiteSparseMatrix At = A.transpose();
320#else
321 StiffnessMatrix At = A.transpose();
322#endif
323 At.makeCompressed();
324
325 logger().debug("Constraint size: {} x {}", A.rows(), A.cols());
326
327#ifdef POLYSOLVE_WITH_SPQR
328 timer.start();
329
330 cholmod_common cc;
331 cholmod_l_start(&cc); // start CHOLMOD
332
333 // const int ordering = 0; // all, except 3:given treated as 0:fixed
334 const int ordering = SPQR_ORDERING_DEFAULT; // all, except 3:given treated as 0:fixed
335 const double tol = SPQR_DEFAULT_TOL;
336 SuiteSparse_long econ = At.rows();
337 SuiteSparse_long *E; // permutation of 0:n-1, NULL if identity
338
339 cholmod_sparse Ac, *Qc, *Rc;
340
341 fill_cholmod(At, Ac);
342
343 const auto rank = SuiteSparseQR<double>(ordering, tol, econ, &Ac,
344 // outputs
345 &Qc, &Rc, &E, &cc);
346
347 if (!Rc)
348 log_and_throw_error("Failed to factorize constraints matrix");
349
350 const auto n = Rc->ncol;
351 P_.resize(n);
352 if (E)
353 {
354 for (long j = 0; j < n; j++)
355 P_.indices()(j) = E[j];
356 }
357 else
358 P_.setIdentity();
359
360 if (Qc->stype != 0 || Qc->sorted != 1 || Qc->packed != 1 || Rc->stype != 0 || Rc->sorted != 1 || Rc->packed != 1)
361 log_and_throw_error("Q and R must be unsymmetric sorted and packed");
362
363 const SuiteSparseMatrix Q = Eigen::Map<SuiteSparseMatrix>(
364 Qc->nrow, Qc->ncol, Qc->nzmax,
365 static_cast<SuiteSparse_long *>(Qc->p), static_cast<SuiteSparse_long *>(Qc->i), static_cast<double *>(Qc->x));
366
367 const SuiteSparseMatrix R = Eigen::Map<SuiteSparseMatrix>(
368 Rc->nrow, Rc->ncol, Rc->nzmax,
369 static_cast<SuiteSparse_long *>(Rc->p), static_cast<SuiteSparse_long *>(Rc->i), static_cast<double *>(Rc->x));
370
371 cholmod_l_free_sparse(&Qc, &cc);
372 cholmod_l_free_sparse(&Rc, &cc);
373 std::free(E);
374 cholmod_l_finish(&cc);
375
376 timer.stop();
377 logger().debug("QR took: {}", timer.getElapsedTime());
378#else
379 timer.start();
380
381 // Eigen::SparseQR<StiffnessMatrix, Eigen::NaturalOrdering<int>> QR(At);
382 Eigen::SparseQR<StiffnessMatrix, Eigen::COLAMDOrdering<int>> QR(At);
383
384 timer.stop();
385 logger().debug("QR took: {}", timer.getElapsedTime());
386
387 if (QR.info() != Eigen::Success)
388 log_and_throw_error("Failed to factorize constraints matrix");
389
390 timer.start();
392 Q = QR.matrixQ();
393 timer.stop();
394 logger().debug("Computation of Q took: {}", timer.getElapsedTime());
395
396 const Eigen::SparseMatrix<double, Eigen::RowMajor> R = QR.matrixR();
397
398 P_ = QR.colsPermutation();
399#endif
400
401 while (constraint_size > 0)
402 {
403 const StiffnessMatrix tmp = R.row(constraint_size - 1);
404 if (tmp.nonZeros() != 0)
405 break;
406 --constraint_size;
407 }
408 if (constraint_size != num_penalty_constraints_)
409 logger().warn("Matrix A is not full rank, constraint size: {} instead of {}", constraint_size, num_penalty_constraints_);
410
411 reduced_size_ = full_size_ - constraint_size;
412
413 timer.start();
414
415 Q1_ = Q.leftCols(constraint_size);
416 assert(Q1_.rows() == full_size_);
417 assert(Q1_.cols() == constraint_size);
418
419 Q2_ = Q.rightCols(reduced_size_);
420 Q2t_ = Q2_.transpose();
421
422 assert(Q2_.rows() == full_size_);
423 assert(Q2_.cols() == reduced_size_);
424
425 R1_ = R.topRows(constraint_size);
426 assert(R1_.rows() == constraint_size);
427 assert(R1_.cols() == num_penalty_constraints_);
428
429 assert((Q1_.transpose() * Q2_).norm() < 1e-10);
430
431 timer.stop();
432 logger().debug("Getting Q1 Q2, R1 took: {}", timer.getElapsedTime());
433
434 timer.start();
435
436 // arma::sp_mat q2a = fill_arma(Q2_);
437 // arma::sp_mat q2tq2 = q2a.t() * q2a;
438 // const StiffnessMatrix Q2tQ2 = fill_eigen(q2tq2);
439 StiffnessMatrix Q2tQ2 = Q2t_ * Q2_;
440 timer.stop();
441 logger().debug("Getting Q2'*Q2, took: {}", timer.getElapsedTime());
442
443 timer.start();
444 solver_->analyze_pattern(Q2tQ2, Q2tQ2.rows());
445 solver_->factorize(Q2tQ2);
446 timer.stop();
447 logger().debug("Factorization of Q2'*Q2 took: {}", timer.getElapsedTime());
448
449#ifndef NDEBUG
450 StiffnessMatrix test = R.bottomRows(reduced_size_);
451 assert(test.nonZeros() == 0);
452
453 StiffnessMatrix test1 = R1_.row(R1_.rows() - 1);
454 assert(test1.nonZeros() != 0);
455#endif
456
457 // assert((Q1_ * R1_ - At * P_).norm() < 1e-10);
458
459 std::vector<std::shared_ptr<Form>> tmp;
460 tmp.insert(tmp.end(), penalty_forms_.begin(), penalty_forms_.end());
461 penalty_problem_ = std::make_shared<FullNLProblem>(tmp);
462
464 }
465
467 {
468 if (penalty_forms_.empty())
469 {
470 assert(num_penalty_constraints_ == 0);
471 return;
472 }
474 return;
475
476 if (penalty_forms_.size() == 1 && penalty_forms_.front()->has_projection())
477 {
478 Q1R1iTb_ = penalty_forms_.front()->constraint_projection_vector();
479 return;
480 }
481
482 igl::Timer timer;
483 timer.start();
484 // x = Q1 * R1^(-T) * P^T b + Q2 * y
485 int index = 0;
486 TVector constraint_values(num_penalty_constraints_);
487 for (const auto &f : penalty_forms_)
488 {
489 constraint_values.segment(index, f->constraint_value().rows()) = f->constraint_value();
490 index += f->constraint_value().rows();
491 }
492 constraint_values = P_.transpose() * constraint_values;
493
494 Eigen::VectorXd sol;
495
496 if (R1_.rows() == R1_.cols())
497 {
498 sol = R1_.transpose().triangularView<Eigen::Lower>().solve(constraint_values);
499 }
500 else
501 {
502
503#ifdef POLYSOLVE_WITH_SPQR
504 SuiteSparseMatrix R1t = R1_.transpose();
505 cholmod_common cc;
506 cholmod_l_start(&cc); // start CHOLMOD
507 cholmod_sparse R1tc;
508 fill_cholmod(R1t, R1tc);
509
510 cholmod_dense b;
511 b.nrow = constraint_values.size();
512 b.ncol = 1;
513 b.nzmax = constraint_values.size();
514 b.d = constraint_values.size();
515 b.x = constraint_values.data();
516 b.z = 0;
517 b.xtype = CHOLMOD_REAL;
518 b.dtype = 0;
519
520 const int ordering = SPQR_ORDERING_DEFAULT; // all, except 3:given treated as 0:fixed
521 const double tol = SPQR_DEFAULT_TOL;
522
523 cholmod_dense *solc = SuiteSparseQR<double>(ordering, tol, &R1tc, &b, &cc);
524
525 sol = Eigen::Map<Eigen::VectorXd>(static_cast<double *>(solc->x), solc->nrow);
526
527 cholmod_l_free_dense(&solc, &cc);
528 cholmod_l_finish(&cc);
529#else
530 Eigen::SparseQR<StiffnessMatrix, Eigen::COLAMDOrdering<int>> solver;
531 solver.compute(R1_.transpose());
532 if (solver.info() != Eigen::Success)
533 {
534 log_and_throw_error("Failed to factorize R1^T");
535 }
536 sol = solver.solve(constraint_values);
537#endif
538 }
539
540 assert((R1_.transpose() * sol - constraint_values).norm() < 1e-10);
541
542 Q1R1iTb_ = Q1_ * sol;
543
544 timer.stop();
545 logger().debug("Computing Q1R1iTb took: {}", timer.getElapsedTime());
546 }
547
548 void NLProblem::init_lagging(const TVector &x)
549 {
551
553 penalty_problem_->init_lagging(x);
554 }
555
556 void NLProblem::update_lagging(const TVector &x, const int iter_num)
557 {
559
561 penalty_problem_->update_lagging(x, iter_num);
562 }
563
564 void NLProblem::update_quantities(const double t, const TVector &x)
565 {
566 t_ = t;
567 const TVector full = reduced_to_full(x);
568 assert(full.size() == full_size_);
569 for (auto &f : forms_)
570 f->update_quantities(t, full);
571
572 for (auto &f : penalty_forms_)
573 f->update_quantities(t, x);
574
575 if (!penalty_forms_.empty())
577 }
578
579 void NLProblem::line_search_begin(const TVector &x0, const TVector &x1)
580 {
582
584 penalty_problem_->line_search_begin(x0, x1);
585 }
586
587 double NLProblem::max_step_size(const TVector &x0, const TVector &x1)
588 {
590
592 max_step = std::min(max_step, penalty_problem_->max_step_size(x0, x1));
593
594 return max_step;
595 }
596
597 bool NLProblem::is_step_valid(const TVector &x0, const TVector &x1)
598 {
600
601 if (penalty_problem_ && valid && full_size() == current_size())
602 valid = penalty_problem_->is_step_valid(x0, x1);
603
604 return valid;
605 }
606
607 bool NLProblem::is_step_collision_free(const TVector &x0, const TVector &x1)
608 {
610
611 if (penalty_problem_ && free && full_size() == current_size())
612 free = penalty_problem_->is_step_collision_free(x0, x1);
613
614 return free;
615 }
616
617 double NLProblem::value(const TVector &x)
618 {
619 if (is_residual())
620 {
621 TVector residual;
622 gradient(x, residual);
623 return residual.squaredNorm();
624 }
625
626 // TODO: removed fearure const bool only_elastic
628
630 {
631 res += penalty_problem_->value(x);
632 }
633
634 return res;
635 }
636
637 void NLProblem::gradient(const TVector &x, TVector &grad)
638 {
640
641 if (full_size() != current_size())
642 {
643 if (penalty_forms_.size() == 1 && penalty_forms_.front()->can_project())
644 penalty_forms_.front()->project_gradient(grad);
645 else
646 grad = Q2t_ * grad;
647 }
648 else if (penalty_problem_)
649 {
650 TVector tmp;
651 penalty_problem_->gradient(x, tmp);
652 grad += tmp;
653 }
654 }
655
656 void NLProblem::hessian(const TVector &x, THessian &hessian)
657 {
659
660 if (full_size() != current_size())
661 {
663 }
664 else if (penalty_problem_)
665 {
666 THessian tmp;
667 penalty_problem_->hessian(x, tmp);
668 hessian += tmp;
669 }
670 }
671
672 void NLProblem::solution_changed(const TVector &newX)
673 {
675
677 penalty_problem_->solution_changed(newX);
678 }
679
680 void NLProblem::post_step(const polysolve::nonlinear::PostStepData &data)
681 {
682 FullNLProblem::post_step(polysolve::nonlinear::PostStepData(data.iter_num, data.solver_info, reduced_to_full(data.x), reduced_to_full(data.grad)));
683
685 penalty_problem_->post_step(data);
686
687 // TODO: add me back
688 static int nsolves = 0;
689 if (data.iter_num == 0)
690 nsolves++;
691 // if (state && state->args["output"]["advanced"]["save_nl_solve_sequence"])
692 // {
693 // const Eigen::MatrixXd displacements = utils::unflatten(reduced_to_full(data.x), state->mesh->dimension());
694 // io::OBJWriter::write(
695 // state->resolve_output_path(fmt::format("nonlinear_solve{:04d}_iter{:04d}.obj", nsolves, data.iter_num)),
696 // state->collision_mesh.displace_vertices(displacements),
697 // state->collision_mesh.edges(), state->collision_mesh.faces());
698 // }
699 }
700
701 NLProblem::TVector NLProblem::full_to_reduced(const TVector &full) const
702 {
703 // Reduced is already at the full size
704 if (full_size() == current_size() || full.size() == current_size())
705 {
706 return full;
707 }
708
709 TVector reduced(reduced_size());
710 const TVector k = full - Q1R1iTb_;
711 const TVector rhs = Q2t_ * k;
712 solver_->solve(rhs, reduced);
713
714#ifndef NDEBUG
715 StiffnessMatrix Q2tQ2 = Q2t_ * Q2_;
716 // std::cout << "err " << (Q2tQ2 * reduced - rhs).norm() << std::endl;
717 assert((Q2tQ2 * reduced - rhs).norm() < 1e-8);
718#endif
719
720 return reduced;
721 }
722
723 NLProblem::TVector NLProblem::full_to_reduced_grad(const TVector &full) const
724 {
725 TVector grad = full;
726 if (penalty_forms_.size() == 1 && penalty_forms_.front()->can_project())
727 penalty_forms_.front()->project_gradient(grad);
728 else
729 grad = Q2t_ * grad;
730
731 return grad;
732 }
733
734 NLProblem::TVector NLProblem::full_to_reduced_diag(const TVector &full_diag) const
735 {
736 if (full_size() == current_size() || full_diag.size() == current_size())
737 {
738 return full_diag;
739 }
740
741 TVector diag = full_diag;
742 if (penalty_forms_.size() == 1 && penalty_forms_.front()->can_project())
743 penalty_forms_.front()->project_diag(diag);
744 else
745 {
746 Eigen::SparseMatrix<double> reduced_mat = Q2t_ * diag.asDiagonal() * Q2_;
747 diag = reduced_mat.diagonal();
748 }
749
750 return diag;
751 }
752
753 NLProblem::TVector NLProblem::reduced_to_full(const TVector &reduced) const
754 {
755 // Full is already at the reduced size
756 if (full_size() == current_size() || full_size() == reduced.size())
757 {
758 return reduced;
759 }
760
761 // x = Q1 * R1^(-T) * P^T b + Q2 * y
762
763 const TVector full = Q1R1iTb_ + Q2_ * reduced;
764
765#ifndef NDEBUG
766 for (const auto &f : penalty_forms_)
767 {
768 // std::cout << f->compute_error(full) << std::endl;
769 assert(f->compute_error(full) < 1e-8);
770 }
771#endif
772
773 return full;
774 }
775
777 {
778 if (penalty_forms_.size() == 1 && penalty_forms_.front()->can_project())
779 penalty_forms_.front()->project_hessian(hessian);
780 else
781 {
782 // arma::sp_mat q2a = fill_arma(Q2_);
783 // arma::sp_mat ha = fill_arma(hessian);
784 // arma::sp_mat q2thq2 = q2a.t() * ha * q2a;
785 // hessian = fill_eigen(q2thq2);
786 hessian = Q2t_ * hessian * Q2_;
787 // remove numerical zeros
788 hessian.prune([](const Eigen::Index &row, const Eigen::Index &col, const Scalar &value) {
789 return std::abs(value) > 1e-10;
790 });
791 }
792 }
793} // namespace polyfem::solver
int x
virtual double max_step_size(const TVector &x0, const TVector &x1) override
virtual void init_lagging(const TVector &x)
virtual void hessian(const TVector &x, THessian &hessian) override
virtual bool is_step_collision_free(const TVector &x0, const TVector &x1)
std::vector< std::shared_ptr< Form > > forms_
virtual void post_step(const polysolve::nonlinear::PostStepData &data) override
virtual void update_lagging(const TVector &x, const int iter_num)
virtual bool is_step_valid(const TVector &x0, const TVector &x1) override
bool is_residual() const override
virtual double value(const TVector &x) override
virtual void solution_changed(const TVector &new_x) override
virtual void gradient(const TVector &x, TVector &gradv) override
virtual void line_search_begin(const TVector &x0, const TVector &x1) override
const int full_size_
Size of the full problem.
Definition NLProblem.hpp:84
virtual double grad_norm_rescaling(const polysolve::nonlinear::NormType norm_type) const override
StiffnessMatrix R1_
R1 block of the QR decomposition of the constraints matrix.
void line_search_begin(const TVector &x0, const TVector &x1) override
Eigen::PermutationMatrix< Eigen::Dynamic, Eigen::Dynamic > P_
Permutation matrix of the QR decomposition of the constraints matrix.
virtual double energy_norm_rescaling(const polysolve::nonlinear::NormType norm_type) const override
double normalize_forms() override
StiffnessMatrix Q2_
Q2 block of the QR decomposition of the constraints matrix.
virtual bool is_step_valid(const TVector &x0, const TVector &x1) override
int reduced_size_
Size of the reduced problem.
Definition NLProblem.hpp:85
virtual double step_norm_rescaling(const polysolve::nonlinear::NormType norm_type) const override
virtual void post_step(const polysolve::nonlinear::PostStepData &data) override
virtual TVector full_to_reduced_grad(const TVector &full) const
virtual TVector full_to_reduced_diag(const TVector &full_diag) const
virtual double step_norm(const TVector &x, const polysolve::nonlinear::NormType norm_type) const override
StiffnessMatrix Q2t_
Q2 transpose.
TVector full_to_reduced(const TVector &full) const
virtual void update_quantities(const double t, const TVector &x)
virtual void gradient(const TVector &x, TVector &gradv) override
void init_lagging(const TVector &x) override
Eigen::DiagonalMatrix< double, Eigen::Dynamic > lumped_mass_
virtual bool is_step_collision_free(const TVector &x0, const TVector &x1) override
TVector reduced_to_full(const TVector &reduced) const
void update_lagging(const TVector &x, const int iter_num) override
std::vector< std::shared_ptr< AugmentedLagrangianForm > > penalty_forms_
Eigen::DiagonalMatrix< double, Eigen::Dynamic > current_lumped_mass() const
Definition NLProblem.hpp:98
virtual double value(const TVector &x) override
StiffnessMatrix Q1_
Q1 block of the QR decomposition of the constraints matrix.
virtual double max_step_size(const TVector &x0, const TVector &x1) override
virtual void hessian(const TVector &x, THessian &hessian) override
std::shared_ptr< polysolve::linear::Solver > solver_
void solution_changed(const TVector &new_x) override
virtual double grad_norm(const TVector &grad, const polysolve::nonlinear::NormType norm_type) const override
std::shared_ptr< FullNLProblem > penalty_problem_
TVector Q1R1iTb_
Q1_ * (R1_.transpose().triangularView<Eigen::Upper>().solve(constraint_values_))
NLProblem(const int full_size, const std::vector< std::shared_ptr< Form > > &forms, const std::vector< std::shared_ptr< AugmentedLagrangianForm > > &penalty_forms, const std::shared_ptr< polysolve::linear::Solver > &solver, const bool is_residual=false)
Definition NLProblem.cpp:96
void full_hessian_to_reduced_hessian(StiffnessMatrix &hessian) const
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic, 0, 3, 3 > inverse(const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic, 0, 3, 3 > &mat)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24