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 void fill_cholmod(Eigen::SparseMatrix<double, Eigen::ColMajor, long> &mat, cholmod_sparse &cmat)
45 {
46 long *p = mat.outerIndexPtr();
47
48 cmat.nzmax = mat.nonZeros();
49 cmat.nrow = mat.rows();
50 cmat.ncol = mat.cols();
51 cmat.p = p;
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 std::shared_ptr<utils::PeriodicBoundary> &periodic_bc,
115 const double t,
116 const std::vector<std::shared_ptr<Form>> &forms,
117 const std::vector<std::shared_ptr<AugmentedLagrangianForm>> &penalty_forms,
118 const std::shared_ptr<polysolve::linear::Solver> &solver,
119 const double char_length,
120 const double char_force,
121 StiffnessMatrix lumped_mass,
122 const int dimension,
123 const bool is_residual)
124 : FullNLProblem(forms, is_residual),
125 full_size_(full_size),
126 t_(t),
127 F0(char_force),
128 L(char_length),
129 dim(dimension),
130 lumped_mass_(lumped_mass.diagonal().asDiagonal()),
131 penalty_forms_(penalty_forms),
132 solver_(solver)
133 {
136
137 double total_lumped_mass = 0;
138 int num_nonzero_mass_entries = 0;
139 for (int i = 0; i < lumped_mass_.diagonal().size(); i++)
140 {
141 if (lumped_mass_.diagonal()[i] > 0)
142 {
143 total_lumped_mass += lumped_mass_.diagonal()[i];
144 num_nonzero_mass_entries++;
145 }
146 }
147 const double avg_lumped_mass = total_lumped_mass / num_nonzero_mass_entries;
148 for (int i = 0; i < lumped_mass_.diagonal().size(); i++)
149 {
150 if (lumped_mass_.diagonal()[i] == 0)
151 {
152 lumped_mass_.diagonal()[i] = avg_lumped_mass;
153 }
154 }
155 }
156
158 {
159 return 1;
160
161 // double total_weight = 0;
162 // for (const auto &f : forms_)
163 // total_weight += f->weight();
164 // if (full_size() == current_size())
165 // {
166 // for (const auto &f : penalty_forms_)
167 // total_weight += f->weight() * f->lagrangian_weight();
168 // }
169
170 // logger().debug("Normalizing forms with scale: {}", total_weight);
171
172 // for (auto &f : forms_)
173 // f->set_scale(total_weight);
174 // for (auto &f : penalty_forms_)
175 // f->set_scale(total_weight);
176
177 // return total_weight;
178 }
179
180 double NLProblem::grad_norm_rescaling(const polysolve::nonlinear::NormType norm_type) const
181 {
182 if (is_residual())
183 return 1;
184
185 switch (norm_type)
186 {
187 case polysolve::nonlinear::NormType::EUCLIDEAN:
188 return 1;
189 case polysolve::nonlinear::NormType::L2:
190 return F0 * (dim == 2 ? L : std::pow(L, 1.5));
191 case polysolve::nonlinear::NormType::Linf:
192 return F0;
193 default:
194 break;
195 }
196 log_and_throw_error("Unrecognized norm type!");
197 }
198
199 double NLProblem::step_norm_rescaling(const polysolve::nonlinear::NormType norm_type) const
200 {
201 switch (norm_type)
202 {
203 case polysolve::nonlinear::NormType::EUCLIDEAN:
204 return 1;
205 case polysolve::nonlinear::NormType::L2:
206 return dim == 2 ? L * L : std::pow(L, 2.5);
207 case polysolve::nonlinear::NormType::Linf:
208 return L;
209 default:
210 break;
211 }
212 log_and_throw_error("Unrecognized norm type!");
213 }
214
215 double NLProblem::energy_norm_rescaling(const polysolve::nonlinear::NormType norm_type) const
216 {
217 if (is_residual())
218 return 1;
219
220 const double density_scale = dim == 2 ? L * L : L * L * L;
221 return F0 * density_scale * L;
222 }
223
224 double NLProblem::grad_norm(const TVector &grad, const polysolve::nonlinear::NormType norm_type) const
225 {
226 switch (norm_type)
227 {
228 case polysolve::nonlinear::NormType::EUCLIDEAN:
229 return grad.norm();
230 case polysolve::nonlinear::NormType::L2:
231 return sqrt(grad.transpose() * current_lumped_mass().inverse() * grad);
232 case polysolve::nonlinear::NormType::Linf:
233 return (current_lumped_mass().inverse() * grad).cwiseAbs().maxCoeff();
234 default:
235 break;
236 }
237 log_and_throw_error("Unrecognized norm type!");
238 }
239
240 double NLProblem::step_norm(const TVector &x, const polysolve::nonlinear::NormType norm_type) const
241 {
242 switch (norm_type)
243 {
244 case polysolve::nonlinear::NormType::EUCLIDEAN:
245 return x.norm();
246 case polysolve::nonlinear::NormType::L2:
247 return sqrt(x.transpose() * current_lumped_mass() * x);
248 case polysolve::nonlinear::NormType::Linf:
249 return x.cwiseAbs().maxCoeff();
250 default:
251 break;
252 }
253 log_and_throw_error("Unrecognized norm type!");
254 }
255
257 {
258 if (penalty_forms_.empty())
259 {
262 return;
263 }
264 igl::Timer timer;
265
266 if (penalty_forms_.size() == 1 && penalty_forms_.front()->has_projection())
267 {
268 Q2_ = penalty_forms_.front()->constraint_projection_matrix();
269 Q2t_ = Q2_.transpose();
270
271 reduced_size_ = Q2_.cols();
272 if (reduced_size_ == 0)
273 return;
275
276 timer.start();
277 StiffnessMatrix Q2tQ2 = Q2t_ * Q2_;
278 solver_->analyze_pattern(Q2tQ2, Q2tQ2.rows());
279 solver_->factorize(Q2tQ2);
280 timer.stop();
281 logger().debug("Factorization and computation of Q2tQ2 took: {}", timer.getElapsedTime());
282
283 std::vector<std::shared_ptr<Form>> tmp;
284 tmp.insert(tmp.end(), penalty_forms_.begin(), penalty_forms_.end());
285 penalty_problem_ = std::make_shared<FullNLProblem>(tmp);
286
288
289 return;
290 }
291
292 std::vector<Eigen::Triplet<double>> Ae;
293 int index = 0;
294 for (const auto &f : penalty_forms_)
295 {
296 const auto &tmp = f->constraint_matrix();
297 for (int i = 0; i < tmp.outerSize(); i++)
298 {
299 for (typename StiffnessMatrix::InnerIterator it(tmp, i); it; ++it)
300 {
301 Ae.emplace_back(index + it.row(), it.col(), it.value());
302 }
303 }
304 index += tmp.rows();
305 }
306 StiffnessMatrix A(index, full_size_);
307 A.setFromTriplets(Ae.begin(), Ae.end());
308 A.makeCompressed();
309
310 if (A.rows() == 0)
311 {
314 return;
315 }
316
317 int constraint_size = A.rows();
318 num_penalty_constraints_ = A.rows();
319 Eigen::SparseMatrix<double, Eigen::ColMajor, long> At = A.transpose();
320 At.makeCompressed();
321
322 logger().debug("Constraint size: {} x {}", A.rows(), A.cols());
323
324#ifdef POLYSOLVE_WITH_SPQR
325 timer.start();
326
327 cholmod_common cc;
328 cholmod_l_start(&cc); // start CHOLMOD
329
330 // const int ordering = 0; // all, except 3:given treated as 0:fixed
331 const int ordering = SPQR_ORDERING_DEFAULT; // all, except 3:given treated as 0:fixed
332 const double tol = SPQR_DEFAULT_TOL;
333 SuiteSparse_long econ = At.rows();
334 SuiteSparse_long *E; // permutation of 0:n-1, NULL if identity
335
336 cholmod_sparse Ac, *Qc, *Rc;
337
338 fill_cholmod(At, Ac);
339
340 const auto rank = SuiteSparseQR<double>(ordering, tol, econ, &Ac,
341 // outputs
342 &Qc, &Rc, &E, &cc);
343
344 if (!Rc)
345 log_and_throw_error("Failed to factorize constraints matrix");
346
347 const auto n = Rc->ncol;
348 P_.resize(n);
349 if (E)
350 {
351 for (long j = 0; j < n; j++)
352 P_.indices()(j) = E[j];
353 }
354 else
355 P_.setIdentity();
356
357 if (Qc->stype != 0 || Qc->sorted != 1 || Qc->packed != 1 || Rc->stype != 0 || Rc->sorted != 1 || Rc->packed != 1)
358 log_and_throw_error("Q and R must be unsymmetric sorted and packed");
359
360 const StiffnessMatrix Q = Eigen::Map<Eigen::SparseMatrix<double, Eigen::ColMajor, long>>(
361 Qc->nrow, Qc->ncol, Qc->nzmax,
362 static_cast<long *>(Qc->p), static_cast<long *>(Qc->i), static_cast<double *>(Qc->x));
363
364 const StiffnessMatrix R = Eigen::Map<Eigen::SparseMatrix<double, Eigen::ColMajor, long>>(
365 Rc->nrow, Rc->ncol, Rc->nzmax,
366 static_cast<long *>(Rc->p), static_cast<long *>(Rc->i), static_cast<double *>(Rc->x));
367
368 cholmod_l_free_sparse(&Qc, &cc);
369 cholmod_l_free_sparse(&Rc, &cc);
370 std::free(E);
371 cholmod_l_finish(&cc);
372
373 timer.stop();
374 logger().debug("QR took: {}", timer.getElapsedTime());
375#else
376 timer.start();
377
378 // Eigen::SparseQR<StiffnessMatrix, Eigen::NaturalOrdering<int>> QR(At);
379 Eigen::SparseQR<StiffnessMatrix, Eigen::COLAMDOrdering<int>> QR(At);
380
381 timer.stop();
382 logger().debug("QR took: {}", timer.getElapsedTime());
383
384 if (QR.info() != Eigen::Success)
385 log_and_throw_error("Failed to factorize constraints matrix");
386
387 timer.start();
389 Q = QR.matrixQ();
390 timer.stop();
391 logger().debug("Computation of Q took: {}", timer.getElapsedTime());
392
393 const Eigen::SparseMatrix<double, Eigen::RowMajor> R = QR.matrixR();
394
395 P_ = QR.colsPermutation();
396#endif
397
398 while (constraint_size > 0)
399 {
400 const StiffnessMatrix tmp = R.row(constraint_size - 1);
401 if (tmp.nonZeros() != 0)
402 break;
403 --constraint_size;
404 }
405 if (constraint_size != num_penalty_constraints_)
406 logger().warn("Matrix A is not full rank, constraint size: {} instead of {}", constraint_size, num_penalty_constraints_);
407
408 reduced_size_ = full_size_ - constraint_size;
409
410 timer.start();
411
412 Q1_ = Q.leftCols(constraint_size);
413 assert(Q1_.rows() == full_size_);
414 assert(Q1_.cols() == constraint_size);
415
416 Q2_ = Q.rightCols(reduced_size_);
417 Q2t_ = Q2_.transpose();
418
419 assert(Q2_.rows() == full_size_);
420 assert(Q2_.cols() == reduced_size_);
421
422 R1_ = R.topRows(constraint_size);
423 assert(R1_.rows() == constraint_size);
424 assert(R1_.cols() == num_penalty_constraints_);
425
426 assert((Q1_.transpose() * Q2_).norm() < 1e-10);
427
428 timer.stop();
429 logger().debug("Getting Q1 Q2, R1 took: {}", timer.getElapsedTime());
430
431 timer.start();
432
433 // arma::sp_mat q2a = fill_arma(Q2_);
434 // arma::sp_mat q2tq2 = q2a.t() * q2a;
435 // const StiffnessMatrix Q2tQ2 = fill_eigen(q2tq2);
436 StiffnessMatrix Q2tQ2 = Q2t_ * Q2_;
437 timer.stop();
438 logger().debug("Getting Q2'*Q2, took: {}", timer.getElapsedTime());
439
440 timer.start();
441 solver_->analyze_pattern(Q2tQ2, Q2tQ2.rows());
442 solver_->factorize(Q2tQ2);
443 timer.stop();
444 logger().debug("Factorization of Q2'*Q2 took: {}", timer.getElapsedTime());
445
446#ifndef NDEBUG
447 StiffnessMatrix test = R.bottomRows(reduced_size_);
448 assert(test.nonZeros() == 0);
449
450 StiffnessMatrix test1 = R1_.row(R1_.rows() - 1);
451 assert(test1.nonZeros() != 0);
452#endif
453
454 // assert((Q1_ * R1_ - At * P_).norm() < 1e-10);
455
456 std::vector<std::shared_ptr<Form>> tmp;
457 tmp.insert(tmp.end(), penalty_forms_.begin(), penalty_forms_.end());
458 penalty_problem_ = std::make_shared<FullNLProblem>(tmp);
459
461 }
462
464 {
465 if (penalty_forms_.empty())
466 {
467 assert(num_penalty_constraints_ == 0);
468 return;
469 }
471 return;
472
473 if (penalty_forms_.size() == 1 && penalty_forms_.front()->has_projection())
474 {
475 Q1R1iTb_ = penalty_forms_.front()->constraint_projection_vector();
476 return;
477 }
478
479 igl::Timer timer;
480 timer.start();
481 // x = Q1 * R1^(-T) * P^T b + Q2 * y
482 int index = 0;
483 TVector constraint_values(num_penalty_constraints_);
484 for (const auto &f : penalty_forms_)
485 {
486 constraint_values.segment(index, f->constraint_value().rows()) = f->constraint_value();
487 index += f->constraint_value().rows();
488 }
489 constraint_values = P_.transpose() * constraint_values;
490
491 Eigen::VectorXd sol;
492
493 if (R1_.rows() == R1_.cols())
494 {
495 sol = R1_.transpose().triangularView<Eigen::Lower>().solve(constraint_values);
496 }
497 else
498 {
499
500#ifdef POLYSOLVE_WITH_SPQR
501 Eigen::SparseMatrix<double, Eigen::ColMajor, long> R1t = R1_.transpose();
502 cholmod_common cc;
503 cholmod_l_start(&cc); // start CHOLMOD
504 cholmod_sparse R1tc;
505 fill_cholmod(R1t, R1tc);
506
507 cholmod_dense b;
508 b.nrow = constraint_values.size();
509 b.ncol = 1;
510 b.nzmax = constraint_values.size();
511 b.d = constraint_values.size();
512 b.x = constraint_values.data();
513 b.z = 0;
514 b.xtype = CHOLMOD_REAL;
515 b.dtype = 0;
516
517 const int ordering = SPQR_ORDERING_DEFAULT; // all, except 3:given treated as 0:fixed
518 const double tol = SPQR_DEFAULT_TOL;
519
520 cholmod_dense *solc = SuiteSparseQR<double>(ordering, tol, &R1tc, &b, &cc);
521
522 sol = Eigen::Map<Eigen::VectorXd>(static_cast<double *>(solc->x), solc->nrow);
523
524 cholmod_l_free_dense(&solc, &cc);
525 cholmod_l_finish(&cc);
526#else
527 Eigen::SparseQR<StiffnessMatrix, Eigen::COLAMDOrdering<int>> solver;
528 solver.compute(R1_.transpose());
529 if (solver.info() != Eigen::Success)
530 {
531 log_and_throw_error("Failed to factorize R1^T");
532 }
533 sol = solver.solve(constraint_values);
534#endif
535 }
536
537 assert((R1_.transpose() * sol - constraint_values).norm() < 1e-10);
538
539 Q1R1iTb_ = Q1_ * sol;
540
541 timer.stop();
542 logger().debug("Computing Q1R1iTb took: {}", timer.getElapsedTime());
543 }
544
545 void NLProblem::init_lagging(const TVector &x)
546 {
548
550 penalty_problem_->init_lagging(x);
551 }
552
553 void NLProblem::update_lagging(const TVector &x, const int iter_num)
554 {
556
558 penalty_problem_->update_lagging(x, iter_num);
559 }
560
561 void NLProblem::update_quantities(const double t, const TVector &x)
562 {
563 t_ = t;
564 const TVector full = reduced_to_full(x);
565 assert(full.size() == full_size_);
566 for (auto &f : forms_)
567 f->update_quantities(t, full);
568
569 for (auto &f : penalty_forms_)
570 f->update_quantities(t, x);
571
572 if (!penalty_forms_.empty())
574 }
575
576 void NLProblem::line_search_begin(const TVector &x0, const TVector &x1)
577 {
579
581 penalty_problem_->line_search_begin(x0, x1);
582 }
583
584 double NLProblem::max_step_size(const TVector &x0, const TVector &x1)
585 {
587
589 max_step = std::min(max_step, penalty_problem_->max_step_size(x0, x1));
590
591 return max_step;
592 }
593
594 bool NLProblem::is_step_valid(const TVector &x0, const TVector &x1)
595 {
597
598 if (penalty_problem_ && valid && full_size() == current_size())
599 valid = penalty_problem_->is_step_valid(x0, x1);
600
601 return valid;
602 }
603
604 bool NLProblem::is_step_collision_free(const TVector &x0, const TVector &x1)
605 {
607
608 if (penalty_problem_ && free && full_size() == current_size())
609 free = penalty_problem_->is_step_collision_free(x0, x1);
610
611 return free;
612 }
613
614 double NLProblem::value(const TVector &x)
615 {
616 if (is_residual())
617 {
618 TVector residual;
619 gradient(x, residual);
620 return residual.squaredNorm();
621 }
622
623 // TODO: removed fearure const bool only_elastic
625
627 {
628 res += penalty_problem_->value(x);
629 }
630
631 return res;
632 }
633
634 void NLProblem::gradient(const TVector &x, TVector &grad)
635 {
637
638 if (full_size() != current_size())
639 {
640 if (penalty_forms_.size() == 1 && penalty_forms_.front()->can_project())
641 penalty_forms_.front()->project_gradient(grad);
642 else
643 grad = Q2t_ * grad;
644 }
645 else if (penalty_problem_)
646 {
647 TVector tmp;
648 penalty_problem_->gradient(x, tmp);
649 grad += tmp;
650 }
651 }
652
653 void NLProblem::hessian(const TVector &x, THessian &hessian)
654 {
656
657 if (full_size() != current_size())
658 {
660 }
661 else if (penalty_problem_)
662 {
663 THessian tmp;
664 penalty_problem_->hessian(x, tmp);
665 hessian += tmp;
666 }
667 }
668
669 void NLProblem::solution_changed(const TVector &newX)
670 {
672
674 penalty_problem_->solution_changed(newX);
675 }
676
677 void NLProblem::post_step(const polysolve::nonlinear::PostStepData &data)
678 {
679 FullNLProblem::post_step(polysolve::nonlinear::PostStepData(data.iter_num, data.solver_info, reduced_to_full(data.x), reduced_to_full(data.grad)));
680
682 penalty_problem_->post_step(data);
683
684 // TODO: add me back
685 static int nsolves = 0;
686 if (data.iter_num == 0)
687 nsolves++;
688 // if (state && state->args["output"]["advanced"]["save_nl_solve_sequence"])
689 // {
690 // const Eigen::MatrixXd displacements = utils::unflatten(reduced_to_full(data.x), state->mesh->dimension());
691 // io::OBJWriter::write(
692 // state->resolve_output_path(fmt::format("nonlinear_solve{:04d}_iter{:04d}.obj", nsolves, data.iter_num)),
693 // state->collision_mesh.displace_vertices(displacements),
694 // state->collision_mesh.edges(), state->collision_mesh.faces());
695 // }
696 }
697
698 NLProblem::TVector NLProblem::full_to_reduced(const TVector &full) const
699 {
700 // Reduced is already at the full size
701 if (full_size() == current_size() || full.size() == current_size())
702 {
703 return full;
704 }
705
706 TVector reduced(reduced_size());
707 const TVector k = full - Q1R1iTb_;
708 const TVector rhs = Q2t_ * k;
709 solver_->solve(rhs, reduced);
710
711#ifndef NDEBUG
712 StiffnessMatrix Q2tQ2 = Q2t_ * Q2_;
713 // std::cout << "err " << (Q2tQ2 * reduced - rhs).norm() << std::endl;
714 assert((Q2tQ2 * reduced - rhs).norm() < 1e-8);
715#endif
716
717 return reduced;
718 }
719
720 NLProblem::TVector NLProblem::full_to_reduced_grad(const TVector &full) const
721 {
722 TVector grad = full;
723 if (penalty_forms_.size() == 1 && penalty_forms_.front()->can_project())
724 penalty_forms_.front()->project_gradient(grad);
725 else
726 grad = Q2t_ * grad;
727
728 return grad;
729 }
730
731 NLProblem::TVector NLProblem::full_to_reduced_diag(const TVector &full_diag) const
732 {
733 if (full_size() == current_size() || full_diag.size() == current_size())
734 {
735 return full_diag;
736 }
737
738 TVector diag = full_diag;
739 if (penalty_forms_.size() == 1 && penalty_forms_.front()->can_project())
740 penalty_forms_.front()->project_diag(diag);
741 else
742 {
743 Eigen::SparseMatrix<double> reduced_mat = Q2t_ * diag.asDiagonal() * Q2_;
744 diag = reduced_mat.diagonal();
745 }
746
747 return diag;
748 }
749
750 NLProblem::TVector NLProblem::reduced_to_full(const TVector &reduced) const
751 {
752 // Full is already at the reduced size
753 if (full_size() == current_size() || full_size() == reduced.size())
754 {
755 return reduced;
756 }
757
758 // x = Q1 * R1^(-T) * P^T b + Q2 * y
759
760 const TVector full = Q1R1iTb_ + Q2_ * reduced;
761
762#ifndef NDEBUG
763 for (const auto &f : penalty_forms_)
764 {
765 // std::cout << f->compute_error(full) << std::endl;
766 assert(f->compute_error(full) < 1e-8);
767 }
768#endif
769
770 return full;
771 }
772
774 {
775 if (penalty_forms_.size() == 1 && penalty_forms_.front()->can_project())
776 penalty_forms_.front()->project_hessian(hessian);
777 else
778 {
779 // arma::sp_mat q2a = fill_arma(Q2_);
780 // arma::sp_mat ha = fill_arma(hessian);
781 // arma::sp_mat q2thq2 = q2a.t() * ha * q2a;
782 // hessian = fill_eigen(q2thq2);
783 hessian = Q2t_ * hessian * Q2_;
784 // remove numerical zeros
785 hessian.prune([](const Eigen::Index &row, const Eigen::Index &col, const Scalar &value) {
786 return std::abs(value) > 1e-10;
787 });
788 }
789 }
790} // 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:86
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:87
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
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