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)
102 full_size_(full_size),
104 penalty_forms_(penalty_forms),
113 const std::shared_ptr<utils::PeriodicBoundary> &periodic_bc,
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)
119 full_size_(full_size),
121 penalty_forms_(penalty_forms),
172 solver_->analyze_pattern(Q2tQ2, Q2tQ2.rows());
175 logger().debug(
"Factorization and computation of Q2tQ2 took: {}", timer.getElapsedTime());
177 std::vector<std::shared_ptr<Form>> tmp;
186 std::vector<Eigen::Triplet<double>> Ae;
190 const auto &tmp = f->constraint_matrix();
191 for (
int i = 0; i < tmp.outerSize(); i++)
193 for (
typename StiffnessMatrix::InnerIterator it(tmp, i); it; ++it)
195 Ae.emplace_back(index + it.row(), it.col(), it.value());
201 A.setFromTriplets(Ae.begin(), Ae.end());
204 int constraint_size = A.rows();
206 Eigen::SparseMatrix<double, Eigen::ColMajor, long> At = A.transpose();
209 logger().debug(
"Constraint size: {} x {}", A.rows(), A.cols());
211#ifdef POLYSOLVE_WITH_SPQR
215 cholmod_l_start(&cc);
218 const int ordering = SPQR_ORDERING_DEFAULT;
219 const double tol = SPQR_DEFAULT_TOL;
220 SuiteSparse_long econ = At.rows();
223 cholmod_sparse Ac, *Qc, *Rc;
225 fill_cholmod(At, Ac);
227 const auto rank = SuiteSparseQR<double>(ordering, tol, econ, &Ac,
234 const auto n = Rc->ncol;
238 for (
long j = 0; j < n; j++)
239 P_.indices()(j) = E[j];
244 if (Qc->stype != 0 || Qc->sorted != 1 || Qc->packed != 1 || Rc->stype != 0 || Rc->sorted != 1 || Rc->packed != 1)
247 const StiffnessMatrix Q = Eigen::Map<Eigen::SparseMatrix<double, Eigen::ColMajor, long>>(
248 Qc->nrow, Qc->ncol, Qc->nzmax,
249 static_cast<long *
>(Qc->p),
static_cast<long *
>(Qc->i),
static_cast<double *
>(Qc->x));
251 const StiffnessMatrix R = Eigen::Map<Eigen::SparseMatrix<double, Eigen::ColMajor, long>>(
252 Rc->nrow, Rc->ncol, Rc->nzmax,
253 static_cast<long *
>(Rc->p),
static_cast<long *
>(Rc->i),
static_cast<double *
>(Rc->x));
255 cholmod_l_free_sparse(&Qc, &cc);
256 cholmod_l_free_sparse(&Rc, &cc);
258 cholmod_l_finish(&cc);
261 logger().debug(
"QR took: {}", timer.getElapsedTime());
266 Eigen::SparseQR<StiffnessMatrix, Eigen::COLAMDOrdering<int>> QR(At);
269 logger().debug(
"QR took: {}", timer.getElapsedTime());
271 if (QR.info() != Eigen::Success)
278 logger().debug(
"Computation of Q took: {}", timer.getElapsedTime());
280 const Eigen::SparseMatrix<double, Eigen::RowMajor> R = QR.matrixR();
282 P_ = QR.colsPermutation();
285 for (; constraint_size >= 0; --constraint_size)
288 if (tmp.nonZeros() != 0)
301 Q1_ = Q.leftCols(constraint_size);
303 assert(
Q1_.cols() == constraint_size);
311 R1_ = R.topRows(constraint_size);
312 assert(
R1_.rows() == constraint_size);
315 assert((
Q1_.transpose() *
Q2_).norm() < 1e-10);
318 logger().debug(
"Getting Q1 Q2, R1 took: {}", timer.getElapsedTime());
327 logger().debug(
"Getting Q2'*Q2, took: {}", timer.getElapsedTime());
330 solver_->analyze_pattern(Q2tQ2, Q2tQ2.rows());
333 logger().debug(
"Factorization of Q2'*Q2 took: {}", timer.getElapsedTime());
337 assert(test.nonZeros() == 0);
340 assert(test1.nonZeros() != 0);
345 std::vector<std::shared_ptr<Form>> tmp;
367 constraint_values.segment(index, f->constraint_value().rows()) = f->constraint_value();
368 index += f->constraint_value().rows();
370 constraint_values =
P_.transpose() * constraint_values;
374 if (
R1_.rows() ==
R1_.cols())
376 sol =
R1_.transpose().triangularView<Eigen::Lower>().solve(constraint_values);
381#ifdef POLYSOLVE_WITH_SPQR
382 Eigen::SparseMatrix<double, Eigen::ColMajor, long> R1t =
R1_.transpose();
384 cholmod_l_start(&cc);
386 fill_cholmod(R1t, R1tc);
389 b.nrow = constraint_values.size();
391 b.nzmax = constraint_values.size();
392 b.d = constraint_values.size();
393 b.x = constraint_values.data();
395 b.xtype = CHOLMOD_REAL;
398 const int ordering = SPQR_ORDERING_DEFAULT;
399 const double tol = SPQR_DEFAULT_TOL;
401 cholmod_dense *solc = SuiteSparseQR<double>(ordering, tol, &R1tc, &b, &cc);
403 sol = Eigen::Map<Eigen::VectorXd>(
static_cast<double *
>(solc->x), solc->nrow);
405 cholmod_l_free_dense(&solc, &cc);
406 cholmod_l_finish(&cc);
408 Eigen::SparseQR<StiffnessMatrix, Eigen::COLAMDOrdering<int>> solver;
409 solver.compute(
R1_.transpose());
410 if (solver.info() != Eigen::Success)
414 sol = solver.solve(constraint_values);
418 assert((
R1_.transpose() * sol - constraint_values).norm() < 1e-10);
423 logger().debug(
"Computing Q1R1iTb took: {}", timer.getElapsedTime());
448 f->update_quantities(t, full);
451 f->update_quantities(t,
x);
558 static int nsolves = 0;
559 if (data.iter_num == 0)
581 const TVector rhs =
Q2t_ * k;
587 assert((Q2tQ2 * reduced - rhs).norm() < 1e-8);
620 assert(f->compute_error(full) < 1e-8);
639 hessian.prune([](
const Eigen::Index &row,
const Eigen::Index &col,
const Scalar &
value) {
640 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
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.
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.
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 void post_step(const polysolve::nonlinear::PostStepData &data) override
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)
virtual TVector full_to_reduced_grad(const TVector &full) const
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
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_
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
std::shared_ptr< FullNLProblem > penalty_problem_
int num_penalty_constraints_
TVector Q1R1iTb_
Q1_ * (R1_.transpose().triangularView<Eigen::Upper>().solve(constraint_values_))
void full_hessian_to_reduced_hessian(StiffnessMatrix &hessian) const
spdlog::logger & logger()
Retrieves the current logger.
void log_and_throw_error(const std::string &msg)
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix