9#include <polysolve/linear/Solver.hpp>
10#ifdef POLYSOLVE_WITH_SPQR
11#include <Eigen/SPQRSupport>
12#include <SuiteSparseQR.hpp>
15#ifdef POLYFEM_WITH_ARMADILLO
43#ifdef POLYSOLVE_WITH_SPQR
44 void fill_cholmod(Eigen::SparseMatrix<double, Eigen::ColMajor, long> &mat, cholmod_sparse &cmat)
46 long *p = mat.outerIndexPtr();
48 cmat.nzmax = mat.nonZeros();
49 cmat.nrow = mat.rows();
50 cmat.ncol = mat.cols();
52 cmat.i = mat.innerIndexPtr();
53 cmat.x = mat.valuePtr();
60 cmat.xtype = CHOLMOD_REAL;
61 cmat.dtype = CHOLMOD_DOUBLE;
63 cmat.itype = CHOLMOD_LONG;
67#ifdef POLYFEM_WITH_ARMADILLO
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());
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);
78 arma::sp_mat amat(rowind, colptr, values, mat.rows(), mat.cols(),
false);
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);
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);
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)
103 full_size_(full_size),
105 penalty_forms_(penalty_forms),
114 const std::shared_ptr<utils::PeriodicBoundary> &periodic_bc,
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,
123 const bool is_residual)
125 full_size_(full_size),
130 lumped_mass_(lumped_mass.diagonal().asDiagonal()),
131 penalty_forms_(penalty_forms),
137 double total_lumped_mass = 0;
138 int num_nonzero_mass_entries = 0;
139 for (
int i = 0; i <
lumped_mass_.diagonal().size(); i++)
144 num_nonzero_mass_entries++;
147 const double avg_lumped_mass = total_lumped_mass / num_nonzero_mass_entries;
148 for (
int i = 0; i <
lumped_mass_.diagonal().size(); i++)
187 case polysolve::nonlinear::NormType::EUCLIDEAN:
189 case polysolve::nonlinear::NormType::L2:
190 return F0 * (
dim == 2 ?
L : std::pow(
L, 1.5));
191 case polysolve::nonlinear::NormType::Linf:
203 case polysolve::nonlinear::NormType::EUCLIDEAN:
205 case polysolve::nonlinear::NormType::L2:
206 return dim == 2 ?
L *
L : std::pow(
L, 2.5);
207 case polysolve::nonlinear::NormType::Linf:
220 const double density_scale =
dim == 2 ?
L *
L :
L *
L *
L;
221 return F0 * density_scale *
L;
228 case polysolve::nonlinear::NormType::EUCLIDEAN:
230 case polysolve::nonlinear::NormType::L2:
232 case polysolve::nonlinear::NormType::Linf:
244 case polysolve::nonlinear::NormType::EUCLIDEAN:
246 case polysolve::nonlinear::NormType::L2:
248 case polysolve::nonlinear::NormType::Linf:
249 return x.cwiseAbs().maxCoeff();
278 solver_->analyze_pattern(Q2tQ2, Q2tQ2.rows());
281 logger().debug(
"Factorization and computation of Q2tQ2 took: {}", timer.getElapsedTime());
283 std::vector<std::shared_ptr<Form>> tmp;
292 std::vector<Eigen::Triplet<double>> Ae;
296 const auto &tmp = f->constraint_matrix();
297 for (
int i = 0; i < tmp.outerSize(); i++)
299 for (
typename StiffnessMatrix::InnerIterator it(tmp, i); it; ++it)
301 Ae.emplace_back(index + it.row(), it.col(), it.value());
307 A.setFromTriplets(Ae.begin(), Ae.end());
317 int constraint_size = A.rows();
319 Eigen::SparseMatrix<double, Eigen::ColMajor, long> At = A.transpose();
322 logger().debug(
"Constraint size: {} x {}", A.rows(), A.cols());
324#ifdef POLYSOLVE_WITH_SPQR
328 cholmod_l_start(&cc);
331 const int ordering = SPQR_ORDERING_DEFAULT;
332 const double tol = SPQR_DEFAULT_TOL;
333 SuiteSparse_long econ = At.rows();
336 cholmod_sparse Ac, *Qc, *Rc;
338 fill_cholmod(At, Ac);
340 const auto rank = SuiteSparseQR<double>(ordering, tol, econ, &Ac,
347 const auto n = Rc->ncol;
351 for (
long j = 0; j < n; j++)
352 P_.indices()(j) = E[j];
357 if (Qc->stype != 0 || Qc->sorted != 1 || Qc->packed != 1 || Rc->stype != 0 || Rc->sorted != 1 || Rc->packed != 1)
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));
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));
368 cholmod_l_free_sparse(&Qc, &cc);
369 cholmod_l_free_sparse(&Rc, &cc);
371 cholmod_l_finish(&cc);
374 logger().debug(
"QR took: {}", timer.getElapsedTime());
379 Eigen::SparseQR<StiffnessMatrix, Eigen::COLAMDOrdering<int>> QR(At);
382 logger().debug(
"QR took: {}", timer.getElapsedTime());
384 if (QR.info() != Eigen::Success)
391 logger().debug(
"Computation of Q took: {}", timer.getElapsedTime());
393 const Eigen::SparseMatrix<double, Eigen::RowMajor> R = QR.matrixR();
395 P_ = QR.colsPermutation();
398 while (constraint_size > 0)
401 if (tmp.nonZeros() != 0)
412 Q1_ = Q.leftCols(constraint_size);
414 assert(
Q1_.cols() == constraint_size);
422 R1_ = R.topRows(constraint_size);
423 assert(
R1_.rows() == constraint_size);
426 assert((
Q1_.transpose() *
Q2_).norm() < 1e-10);
429 logger().debug(
"Getting Q1 Q2, R1 took: {}", timer.getElapsedTime());
438 logger().debug(
"Getting Q2'*Q2, took: {}", timer.getElapsedTime());
441 solver_->analyze_pattern(Q2tQ2, Q2tQ2.rows());
444 logger().debug(
"Factorization of Q2'*Q2 took: {}", timer.getElapsedTime());
448 assert(test.nonZeros() == 0);
451 assert(test1.nonZeros() != 0);
456 std::vector<std::shared_ptr<Form>> tmp;
486 constraint_values.segment(index, f->constraint_value().rows()) = f->constraint_value();
487 index += f->constraint_value().rows();
489 constraint_values =
P_.transpose() * constraint_values;
493 if (
R1_.rows() ==
R1_.cols())
495 sol =
R1_.transpose().triangularView<Eigen::Lower>().solve(constraint_values);
500#ifdef POLYSOLVE_WITH_SPQR
501 Eigen::SparseMatrix<double, Eigen::ColMajor, long> R1t =
R1_.transpose();
503 cholmod_l_start(&cc);
505 fill_cholmod(R1t, R1tc);
508 b.nrow = constraint_values.size();
510 b.nzmax = constraint_values.size();
511 b.d = constraint_values.size();
512 b.x = constraint_values.data();
514 b.xtype = CHOLMOD_REAL;
517 const int ordering = SPQR_ORDERING_DEFAULT;
518 const double tol = SPQR_DEFAULT_TOL;
520 cholmod_dense *solc = SuiteSparseQR<double>(ordering, tol, &R1tc, &b, &cc);
522 sol = Eigen::Map<Eigen::VectorXd>(
static_cast<double *
>(solc->x), solc->nrow);
524 cholmod_l_free_dense(&solc, &cc);
525 cholmod_l_finish(&cc);
527 Eigen::SparseQR<StiffnessMatrix, Eigen::COLAMDOrdering<int>> solver;
528 solver.compute(
R1_.transpose());
529 if (solver.info() != Eigen::Success)
533 sol = solver.solve(constraint_values);
537 assert((
R1_.transpose() * sol - constraint_values).norm() < 1e-10);
542 logger().debug(
"Computing Q1R1iTb took: {}", timer.getElapsedTime());
567 f->update_quantities(t, full);
570 f->update_quantities(t,
x);
620 return residual.squaredNorm();
685 static int nsolves = 0;
686 if (data.iter_num == 0)
708 const TVector rhs =
Q2t_ * k;
714 assert((Q2tQ2 * reduced - rhs).norm() < 1e-8);
738 TVector diag = full_diag;
743 Eigen::SparseMatrix<double> reduced_mat =
Q2t_ * diag.asDiagonal() *
Q2_;
744 diag = reduced_mat.diagonal();
766 assert(f->compute_error(full) < 1e-8);
785 hessian.prune([](
const Eigen::Index &row,
const Eigen::Index &col,
const Scalar &
value) {
786 return std::abs(
value) > 1e-10;
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.
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.
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
void update_constraint_values()
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_
int num_penalty_constraints_
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)
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.
void log_and_throw_error(const std::string &msg)
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix