PolyFEM
Loading...
Searching...
No Matches
MatrixUtils.cpp
Go to the documentation of this file.
1#include "MatrixUtils.hpp"
2
5
6#include <vector>
7
8void polyfem::utils::show_matrix_stats(const Eigen::MatrixXd &M)
9{
10 Eigen::FullPivLU<Eigen::MatrixXd> lu(M);
11 Eigen::JacobiSVD<Eigen::MatrixXd> svd(M);
12 double s1 = svd.singularValues()(0);
13 double s2 = svd.singularValues()(svd.singularValues().size() - 1);
14 double cond = s1 / s2;
15
16 logger().trace("----------------------------------------");
17 logger().trace("-- Determinant: {}", M.determinant());
18 logger().trace("-- Singular values: {} {}", s1, s2);
19 logger().trace("-- Cond: {}", cond);
20 logger().trace("-- Invertible: {}", lu.isInvertible());
21 logger().trace("----------------------------------------");
22 // logger().trace("{}", lu.solve(M) );
23}
24
25namespace
26{
27 inline bool nanproof_equals(const double x, const double y)
28 {
29 return x == y || (std::isnan(x) == std::isnan(y));
30 }
31} // namespace
32
33// Flatten rowwises
34Eigen::VectorXd polyfem::utils::flatten(const Eigen::MatrixXd &X)
35{
36 if (X.size() == 0)
37 return Eigen::VectorXd();
38
39 Eigen::VectorXd x(X.size());
40 for (int i = 0; i < X.rows(); ++i)
41 {
42 for (int j = 0; j < X.cols(); ++j)
43 {
44 x(i * X.cols() + j) = X(i, j);
45 }
46 }
47 assert(nanproof_equals(X(0, 0), x(0)));
48 assert(X.cols() <= 1 || nanproof_equals(X(0, 1), x(1)));
49 return x;
50}
51
52// Unflatten rowwises, so every dim elements in x become a row.
53Eigen::MatrixXd polyfem::utils::unflatten(const Eigen::VectorXd &x, int dim)
54{
55 if (x.size() == 0)
56 return Eigen::MatrixXd(0, dim);
57
58 assert(x.size() % dim == 0);
59 Eigen::MatrixXd X(x.size() / dim, dim);
60 for (int i = 0; i < x.size(); ++i)
61 {
62 X(i / dim, i % dim) = x(i);
63 }
64 assert(nanproof_equals(X(0, 0), x(0)));
65 assert(X.cols() <= 1 || nanproof_equals(X(0, 1), x(1)));
66 return X;
67}
68
69void polyfem::utils::vector2matrix(const Eigen::VectorXd &vec, Eigen::MatrixXd &mat)
70{
71 int size = 1;
72 if (vec.size() == 9)
73 size = 3;
74 else if (vec.size() == 4)
75 size = 2;
76 else
77 throw std::runtime_error("Invalid size in vector2matrix!");
78
79 assert(size * size == vec.size());
80
81 mat = unflatten(vec, size);
82}
83
84Eigen::SparseMatrix<double> polyfem::utils::lump_matrix(const Eigen::SparseMatrix<double> &M)
85{
86 std::vector<Eigen::Triplet<double>> triplets;
87
88 for (int k = 0; k < M.outerSize(); ++k)
89 {
90 for (Eigen::SparseMatrix<double>::InnerIterator it(M, k); it; ++it)
91 {
92 triplets.emplace_back(it.row(), it.row(), it.value());
93 }
94 }
95
96 Eigen::SparseMatrix<double> lumped(M.rows(), M.rows());
97 lumped.setFromTriplets(triplets.begin(), triplets.end());
98 lumped.makeCompressed();
99
100 return lumped;
101}
102
103Eigen::SparseMatrix<double> polyfem::utils::lump_matrix_hrz(const Eigen::SparseMatrix<double> &M)
104{
105 double total = 0, trace = 0;
106 for (int k = 0; k < M.outerSize(); ++k)
107 {
108 for (Eigen::SparseMatrix<double>::InnerIterator it(M, k); it; ++it)
109 {
110 total += it.value();
111 if (it.row() == it.col())
112 trace += it.value();
113 }
114 }
115 const double scale = trace > 0 ? total / trace : 1.0;
116
117 std::vector<Eigen::Triplet<double>> triplets;
118 triplets.reserve(M.rows());
119 for (int k = 0; k < M.outerSize(); ++k)
120 for (Eigen::SparseMatrix<double>::InnerIterator it(M, k); it; ++it)
121 if (it.row() == it.col())
122 triplets.emplace_back(it.row(), it.col(), it.value() * scale);
123
124 Eigen::SparseMatrix<double> lumped(M.rows(), M.rows());
125 lumped.setFromTriplets(triplets.begin(), triplets.end());
126 lumped.makeCompressed();
127 return lumped;
128}
129
131 const int full_size,
132 const int reduced_size,
133 const std::vector<int> &removed_vars,
134 const StiffnessMatrix &full,
135 StiffnessMatrix &reduced)
136{
137 POLYFEM_SCOPED_TIMER("full to reduced matrix");
138
139 if (reduced_size == full_size || reduced_size == full.rows())
140 {
141 assert(reduced_size == full.rows() && reduced_size == full.cols());
142 reduced = full;
143 return;
144 }
145 assert(full.rows() == full_size && full.cols() == full_size);
146
147 Eigen::VectorXi indices(full_size);
148 int index = 0;
149 size_t kk = 0;
150 for (int i = 0; i < full_size; ++i)
151 {
152 if (kk < removed_vars.size() && removed_vars[kk] == i)
153 {
154 ++kk;
155 indices(i) = -1;
156 }
157 else
158 {
159 indices(i) = index++;
160 }
161 }
162 assert(index == reduced_size);
163
164 std::vector<Eigen::Triplet<double>> entries;
165 entries.reserve(full.nonZeros()); // Conservative estimate
166 for (int k = 0; k < full.outerSize(); ++k)
167 {
168 if (indices(k) < 0)
169 continue;
170
171 for (StiffnessMatrix::InnerIterator it(full, k); it; ++it)
172 {
173 assert(it.col() == k);
174 if (indices(it.row()) < 0 || indices(it.col()) < 0)
175 continue;
176
177 assert(indices(it.row()) >= 0);
178 assert(indices(it.col()) >= 0);
179
180 entries.emplace_back(indices(it.row()), indices(it.col()), it.value());
181 }
182 }
183
184 reduced.resize(reduced_size, reduced_size);
185 reduced.setFromTriplets(entries.begin(), entries.end());
186 reduced.makeCompressed();
187}
188
190 const Eigen::MatrixXd &in,
191 const Eigen::VectorXi &in_to_out,
192 int out_blocks,
193 const int block_size)
194{
195 constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
196
197 assert(in.rows() % block_size == 0);
198 assert(in_to_out.size() == in.rows() / block_size);
199
200 if (out_blocks < 0)
201 out_blocks = in.rows() / block_size;
202
203 Eigen::MatrixXd out = Eigen::MatrixXd::Constant(
204 out_blocks * block_size, in.cols(), NaN);
205
206 const int in_blocks = in.rows() / block_size;
207 for (int i = 0; i < in_blocks; ++i)
208 {
209 const int j = in_to_out[i];
210 if (j < 0)
211 continue;
212
213 out.middleRows(block_size * j, block_size) =
214 in.middleRows(block_size * i, block_size);
215 }
216
217 return out;
218}
219
221 const Eigen::MatrixXd &out,
222 const Eigen::VectorXi &in_to_out,
223 int in_blocks,
224 const int block_size)
225{
226 constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
227
228 assert(out.rows() % block_size == 0);
229
230 if (in_blocks < 0)
231 in_blocks = out.rows() / block_size;
232 assert(in_to_out.size() == in_blocks);
233
234 Eigen::MatrixXd in = Eigen::MatrixXd::Constant(
235 in_blocks * block_size, out.cols(), NaN);
236
237 for (int i = 0; i < in_blocks; i++)
238 {
239 const int j = in_to_out[i];
240 if (j < 0)
241 continue;
242
243 in.middleRows(block_size * i, block_size) =
244 out.middleRows(block_size * j, block_size);
245 }
246
247 return in;
248}
249
251 const Eigen::MatrixXi &in,
252 const Eigen::VectorXi &index_mapping)
253{
254 Eigen::MatrixXi out(in.rows(), in.cols());
255 for (int i = 0; i < in.rows(); i++)
256 {
257 for (int j = 0; j < in.cols(); j++)
258 {
259 out(i, j) = index_mapping[in(i, j)];
260 }
261 }
262 return out;
263}
264
265void polyfem::utils::scatter_matrix(const int n_dofs,
266 const int dim,
267 const Eigen::MatrixXd &A,
268 const Eigen::MatrixXd &b,
269 const std::vector<int> &local_to_global,
270 StiffnessMatrix &Aout,
271 Eigen::MatrixXd &bout)
272{
273 assert(A.rows() == b.rows());
274 std::vector<Eigen::Triplet<double>> Ae;
275
276 if (b.cols() == dim)
277 {
278 for (int i = 0; i < A.rows(); ++i)
279 {
280 for (int j = 0; j < A.cols(); ++j)
281 {
282 const auto global_j = (local_to_global.empty() ? j : local_to_global[j]) * dim;
283
284 if (A(i, j) != 0)
285 {
286 for (int d = 0; d < dim; ++d)
287 {
288 Ae.push_back(Eigen::Triplet<double>(
289 i * dim + d,
290 global_j + d,
291 A(i, j)));
292 }
293 }
294 }
295 }
296
297 bout.resize(b.rows() * dim, 1);
298 for (int i = 0; i < b.rows(); ++i)
299 {
300 for (int d = 0; d < dim; ++d)
301 {
302 bout(i * dim + d) = b(i, d);
303 }
304 }
305 }
306 else
307 {
308 assert(b.cols() == 1);
309 assert(b.size() % dim == 0);
310
311 for (int i = 0; i < A.rows(); ++i)
312 {
313 for (int j = 0; j < A.cols(); ++j)
314 {
315 if (A(i, j) != 0)
316 {
317 const auto nid = j / dim;
318 const auto noffset = j % dim;
319
320 const auto global_j = (local_to_global.empty() ? nid : local_to_global[nid]) * dim;
321
322 Ae.push_back(Eigen::Triplet<double>(
323 i,
324 global_j + noffset,
325 A(i, j)));
326 }
327 }
328 }
329
330 bout = b;
331 }
332
333 Aout.resize(bout.size(), n_dofs);
334 Aout.setFromTriplets(Ae.begin(), Ae.end());
335 Aout.makeCompressed();
336}
337
339 const int dim,
340 const Eigen::MatrixXd &A,
341 const Eigen::MatrixXd &b,
342 const std::vector<int> &local_to_global,
343 StiffnessMatrix &Aout,
344 Eigen::MatrixXd &bout)
345{
346 log_and_throw_error("Not implemented");
347}
348
349void polyfem::utils::scatter_matrix(const int n_dofs,
350 const int dim,
351 const std::vector<long> &shape,
352 const std::vector<int> &rows,
353 const std::vector<int> &cols,
354 const std::vector<double> &vals,
355 const Eigen::MatrixXd &b,
356 const std::vector<int> &local_to_global,
357 StiffnessMatrix &Aout,
358 Eigen::MatrixXd &bout)
359{
360 assert(shape.size() == 2);
361 assert(rows.size() == cols.size());
362 assert(rows.size() == vals.size());
363
364 std::vector<Eigen::Triplet<double>> Ae;
365 if (b.cols() == dim)
366 {
367 for (int k = 0; k < rows.size(); ++k)
368 {
369 const auto i = rows[k];
370 const auto j = cols[k];
371 const auto val = vals[k];
372
373 const auto global_j = (local_to_global.empty() ? j : local_to_global[j]) * dim;
374
375 if (val != 0)
376 {
377 for (int d = 0; d < dim; ++d)
378 {
379 Ae.push_back(Eigen::Triplet<double>(
380 i * dim + d,
381 global_j + d,
382 val));
383 }
384 }
385 }
386
387 bout.resize(b.rows() * dim, 1);
388 for (int i = 0; i < b.rows(); ++i)
389 {
390 for (int d = 0; d < dim; ++d)
391 {
392 bout(i * dim + d) = b(i, d);
393 }
394 }
395 }
396 else
397 {
398 assert(b.cols() == 1);
399 assert(b.size() % dim == 0);
400
401 for (int k = 0; k < rows.size(); ++k)
402 {
403 const auto i = rows[k];
404 const auto j = cols[k];
405 const auto val = vals[k];
406
407 if (val != 0)
408 {
409 const auto nid = j / dim;
410 const auto noffset = j % dim;
411
412 const auto global_j = (local_to_global.empty() ? nid : local_to_global[nid]) * dim;
413
414 Ae.push_back(Eigen::Triplet<double>(
415 i,
416 global_j + noffset,
417 val));
418 }
419 }
420
421 bout = b;
422 }
423
424 Aout.resize(bout.size(), n_dofs);
425 Aout.setFromTriplets(Ae.begin(), Ae.end());
426 Aout.makeCompressed();
427}
428
430 const int dim,
431 const std::vector<long> &shape,
432 const std::vector<int> &rows,
433 const std::vector<int> &cols,
434 const std::vector<double> &vals,
435 const Eigen::MatrixXd &b,
436 const std::vector<int> &local_to_global,
437 StiffnessMatrix &Aout,
438 Eigen::MatrixXd &bout)
439{
440 assert(shape.size() == 2);
441 assert(rows.size() == cols.size());
442 assert(rows.size() == vals.size());
443
444 std::vector<Eigen::Triplet<double>> Ae;
445
446 int A_cols = -1;
447
448 if (b.cols() == dim)
449 {
450 assert(n_dofs == b.rows() * dim);
451
452 for (int k = 0; k < rows.size(); ++k)
453 {
454 const auto i = rows[k];
455 const auto j = cols[k];
456 const auto val = vals[k];
457
458 const auto global_i = (local_to_global.empty() ? i : local_to_global[i]) * dim;
459
460 if (val != 0)
461 {
462 for (int d = 0; d < dim; ++d)
463 {
464 Ae.push_back(Eigen::Triplet<double>(
465 global_i + d,
466 j * dim + d,
467 val));
468 }
469 }
470 }
471
472 bout.resize(b.rows() * dim, 1);
473 for (int i = 0; i < b.rows(); ++i)
474 {
475 const auto global_i = (local_to_global.empty() ? i : local_to_global[i]) * dim;
476
477 for (int d = 0; d < dim; ++d)
478 {
479 bout(global_i + d) = b(i, d);
480 }
481 }
482
483 A_cols = shape[1] * dim;
484 assert(shape[0] * dim == n_dofs);
485 }
486 else
487 {
488 assert(b.cols() == 1);
489 assert(b.size() % dim == 0);
490 assert(n_dofs == b.size());
491
492 for (int k = 0; k < rows.size(); ++k)
493 {
494 const auto i = rows[k];
495 const auto j = cols[k];
496 const auto val = vals[k];
497
498 if (val != 0)
499 {
500 const auto nid = i / dim;
501 const auto noffset = i % dim;
502
503 const auto global_i = (local_to_global.empty() ? nid : local_to_global[nid]) * dim;
504
505 Ae.push_back(Eigen::Triplet<double>(
506 global_i + noffset,
507 j,
508 val));
509 }
510 }
511 bout.resize(b.size(), 1);
512 for (int i = 0; i < b.size(); ++i)
513 {
514 const auto nid = i / dim;
515 const auto noffset = i % dim;
516
517 const auto global_i = (local_to_global.empty() ? nid : local_to_global[nid]) * dim;
518
519 bout(global_i + noffset) = b(i);
520 }
521
522 assert(shape[0] == n_dofs);
523 A_cols = shape[1];
524 }
525
526 Aout.resize(n_dofs, A_cols);
527 Aout.setFromTriplets(Ae.begin(), Ae.end());
528 Aout.makeCompressed();
529}
Eigen::MatrixXd vec
Definition Assembler.cpp:75
double val
Definition Assembler.cpp:89
ElementAssemblyValues vals
Definition Assembler.cpp:25
std::vector< Eigen::Triplet< double > > entries
int y
int x
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
Eigen::SparseMatrix< double > lump_matrix(const Eigen::SparseMatrix< double > &M)
Lump each row of a matrix into the diagonal.
void show_matrix_stats(const Eigen::MatrixXd &M)
Eigen::MatrixXd reorder_matrix(const Eigen::MatrixXd &in, const Eigen::VectorXi &in_to_out, int out_blocks=-1, const int block_size=1)
Reorder row blocks in a matrix.
Eigen::SparseMatrix< double > lump_matrix_hrz(const Eigen::SparseMatrix< double > &M)
Lump a (mass) matrix HRZ-style: keep the diagonal, scaled by a common factor so the total (sum of all...
void vector2matrix(const Eigen::VectorXd &vec, Eigen::MatrixXd &mat)
void scatter_matrix(const int n_dofs, const int dim, const Eigen::MatrixXd &A, const Eigen::MatrixXd &b, const std::vector< int > &local_to_global, StiffnessMatrix &Aout, Eigen::MatrixXd &bout)
Eigen::MatrixXd unreorder_matrix(const Eigen::MatrixXd &out, const Eigen::VectorXi &in_to_out, int in_blocks=-1, const int block_size=1)
Undo the reordering of row blocks in a matrix.
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
void scatter_matrix_col(const int n_dofs, const int dim, const Eigen::MatrixXd &A, const Eigen::MatrixXd &b, const std::vector< int > &local_to_global, StiffnessMatrix &Aout, Eigen::MatrixXd &bout)
Eigen::VectorXd flatten(const Eigen::MatrixXd &X)
Flatten rowwises.
Eigen::MatrixXi map_index_matrix(const Eigen::MatrixXi &in, const Eigen::VectorXi &index_mapping)
Map the entrys of an index matrix to new indices.
void full_to_reduced_matrix(const int full_size, const int reduced_size, const std::vector< int > &removed_vars, const StiffnessMatrix &full, StiffnessMatrix &reduced)
Map a full size matrix to a reduced one by dropping rows and columns.
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