GetFEM  5.5
gmm_range_basis.h
Go to the documentation of this file.
1 /* -*- c++ -*- (enables emacs c++ mode) */
2 /*===========================================================================
3 
4  Copyright (C) 2009-2026 Yves Renard
5 
6  This file is a part of GetFEM
7 
8  GetFEM is free software; you can redistribute it and/or modify it
9  under the terms of the GNU Lesser General Public License as published
10  by the Free Software Foundation; either version 3 of the License, or
11  (at your option) any later version along with the GCC Runtime Library
12  Exception either version 3.1 or (at your option) any later version.
13  This program is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16  License and GCC Runtime Library Exception for more details.
17  You should have received a copy of the GNU Lesser General Public License
18  along with this program. If not, see https://www.gnu.org/licenses/.
19 
20  As a special exception, you may use this file as it is a part of a free
21  software library without restriction. Specifically, if other files
22  instantiate templates or use macros or inline functions from this file,
23  or you compile this file and link it with other files to produce an
24  executable, this file does not by itself cause the resulting executable
25  to be covered by the GNU Lesser General Public License. This exception
26  does not however invalidate any other reasons why the executable file
27  might be covered by the GNU Lesser General Public License.
28 
29 ===========================================================================*/
30 
31 /**@file gmm_range_basis.h
32  @author Yves Renard <Yves.Renard@insa-lyon.fr>
33  @date March 10, 2009.
34  @brief Extract a basis of the range of a (large sparse) matrix from the
35  columns of this matrix.
36 */
37 #ifndef GMM_RANGE_BASIS_H
38 #define GMM_RANGE_BASIS_H
39 #include "gmm_dense_qr.h"
40 #include "gmm_dense_lu.h"
41 
42 #include "gmm_kernel.h"
43 #include "gmm_iter.h"
44 #include <set>
45 #include <list>
46 
47 
48 namespace gmm {
49 
50 
51  template <typename T, typename VECT, typename MAT1>
52  void tridiag_qr_algorithm
53  (std::vector<typename number_traits<T>::magnitude_type> diag,
54  std::vector<T> sdiag, const VECT &eigval_, const MAT1 &eigvect_,
55  bool compvect, tol_type_for_qr tol = default_tol_for_qr) {
56  VECT &eigval = const_cast<VECT &>(eigval_);
57  MAT1 &eigvect = const_cast<MAT1 &>(eigvect_);
58  typedef typename number_traits<T>::magnitude_type R;
59 
60  if (compvect) gmm::copy(identity_matrix(), eigvect);
61 
62  size_type n = diag.size(), q = 0, p, ite = 0;
63  if (n == 0) return;
64  if (n == 1) { eigval[0] = gmm::real(diag[0]); return; }
65 
66  symmetric_qr_stop_criterion(diag, sdiag, p, q, tol);
67 
68  while (q < n) {
69  sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(eigvect)), SUBK(p, n-p-q);
70  if (!compvect) SUBK = sub_interval(0,0);
71 
72  symmetric_Wilkinson_qr_step(sub_vector(diag, SUBI),
73  sub_vector(sdiag, SUBI),
74  sub_matrix(eigvect, SUBJ, SUBK), compvect);
75 
76  symmetric_qr_stop_criterion(diag, sdiag, p, q, tol*R(3));
77  ++ite;
78  GMM_ASSERT1(ite < n*100, "QR algorithm failed.");
79  }
80 
81  gmm::copy(diag, eigval);
82  }
83 
84  // Range basis with a restarted Lanczos method
85  template <typename Mat>
86  void range_basis_eff_Lanczos(const Mat &BB, std::set<size_type> &columns,
87  double EPS=1E-12) {
88  typedef std::set<size_type> TAB;
89  typedef typename linalg_traits<Mat>::value_type T;
90  typedef typename number_traits<T>::magnitude_type R;
91 
92  size_type nc_r = columns.size(), k;
93  col_matrix< rsvector<T> > B(mat_nrows(BB), mat_ncols(BB));
94 
95  k = 0;
96  for (TAB::iterator it = columns.begin(); it!=columns.end(); ++it, ++k)
97  gmm::copy(scaled(mat_col(BB, *it), T(1)/vect_norm2(mat_col(BB, *it))),
98  mat_col(B, *it));
99  std::vector<T> w(mat_nrows(B));
100  size_type restart = 120;
101  std::vector<T> sdiag(restart);
102  std::vector<R> eigval(restart), diag(restart);
103  dense_matrix<T> eigvect(restart, restart);
104 
105  R rho = R(-1), rho2;
106  while (nc_r) {
107 
108  std::vector<T> v(nc_r), v0(nc_r), wl(nc_r);
109  dense_matrix<T> lv(nc_r, restart);
110 
111  if (rho < R(0)) { // Estimate of the spectral radius of B^* B
112  gmm::fill_random(v);
113  for (size_type i = 0; i < 100; ++i) {
114  gmm::scale(v, T(1)/vect_norm2(v));
115  gmm::copy(v, v0);
116  k = 0; gmm::clear(w);
117  for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it, ++k)
118  add(scaled(mat_col(B, *it), v[k]), w);
119  k = 0;
120  for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it, ++k)
121  v[k] = vect_hp(w, mat_col(B, *it));
122  rho = gmm::abs(vect_hp(v, v0) / vect_hp(v0, v0));
123  }
124  rho *= R(2);
125  }
126 
127  // Computing vectors of the null space of de B^* B with restarted Lanczos
128  rho2 = 0;
129  gmm::fill_random(v);
130  size_type iter = 0;
131  for(;;++iter) {
132  R rho_old = rho2;
133  R beta = R(0), alpha;
134  gmm::scale(v, T(1)/vect_norm2(v));
135  size_type eff_restart = restart;
136  if (sdiag.size() != restart) {
137  sdiag.resize(restart);
138  eigval.resize(restart);
139  diag.resize(restart);
140  gmm::resize(eigvect, restart, restart);
141  gmm::resize(lv, nc_r, restart);
142  }
143 
144  for (size_type i = 0; i < restart; ++i) { // Lanczos iterations
145  gmm::copy(v, mat_col(lv, i));
146  gmm::clear(w);
147  k = 0;
148  for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it, ++k)
149  add(scaled(mat_col(B, *it), v[k]), w);
150 
151  k = 0;
152  for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it, ++k)
153  wl[k] = v[k]*rho - vect_hp(w, mat_col(B, *it)) - beta*v0[k];
154  alpha = gmm::real(vect_hp(wl, v));
155  diag[i] = alpha;
156  gmm::add(gmm::scaled(v, -alpha), wl);
157  sdiag[i] = beta = vect_norm2(wl);
158  gmm::copy(v, v0);
159  if (beta < EPS) { eff_restart = i+1; break; }
160  gmm::copy(gmm::scaled(wl, T(1) / beta), v);
161  }
162  if (eff_restart != restart) {
163  sdiag.resize(eff_restart);
164  eigval.resize(eff_restart);
165  diag.resize(eff_restart);
166  gmm::resize(eigvect, eff_restart, eff_restart);
167  gmm::resize(lv, nc_r, eff_restart);
168  }
169  tridiag_qr_algorithm(diag, sdiag, eigval, eigvect, true);
170 
171  size_type num = size_type(-1);
172  rho2 = R(0);
173  for (size_type j = 0; j < eff_restart; ++j) {
174  R nvp=gmm::abs(eigval[j]);
175  if (nvp > rho2) {
176  rho2=nvp;
177  num=j;
178  }
179  }
180 
181  GMM_ASSERT1(num != size_type(-1), "Internal error");
182 
183  gmm::mult(lv, mat_col(eigvect, num), v);
184 
185  if (gmm::abs(rho2-rho_old) < rho_old*R(EPS)) break;
186  // if (gmm::abs(rho-rho2) < rho*R(gmm::sqrt(EPS))) break;
187  if (gmm::abs(rho-rho2) < rho*R(EPS)*R(100)) break;
188  }
189 
190  if (gmm::abs(rho-rho2) < rho*R(EPS*10.)) {
191  size_type j_max = size_type(-1), j = 0;
192  R val_max = R(0);
193  for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it, ++j)
194  if (gmm::abs(v[j]) > val_max) {
195  val_max = gmm::abs(v[j]);
196  j_max = *it;
197  }
198  columns.erase(j_max);
199  nc_r = columns.size();
200  }
201  else
202  break;
203  }
204  }
205 
206  // Range basis with LU decomposition. Not stable from a numerical viewpoint.
207  // Complex version not verified
208  template <typename Mat>
209  void range_basis_eff_lu(const Mat &B, std::set<size_type> &columns,
210  std::vector<bool> &c_ortho, double EPS) {
211 
212  typedef std::set<size_type> TAB;
213  typedef typename linalg_traits<Mat>::value_type T;
214  typedef typename number_traits<T>::magnitude_type R;
215 
216  size_type nc_r = 0, nc_o = 0, nc = mat_ncols(B), nr = mat_nrows(B), i, j;
217 
218  for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it)
219  if (!(c_ortho[*it])) ++nc_r; else nc_o++;
220 
221  if (nc_r > 0) {
222 
223  gmm::row_matrix< gmm::rsvector<T> > Hr(nc, nc_r), Ho(nc, nc_o);
224  gmm::row_matrix< gmm::rsvector<T> > BBr(nr, nc_r), BBo(nr, nc_o);
225 
226  i = j = 0;
227  for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it)
228  if (!(c_ortho[*it]))
229  { Hr(*it, i) = T(1)/ vect_norminf(mat_col(B, *it)); ++i; }
230  else
231  { Ho(*it, j) = T(1)/ vect_norm2(mat_col(B, *it)); ++j; }
232 
233  gmm::mult(B, Hr, BBr);
234  gmm::mult(B, Ho, BBo);
235  gmm::dense_matrix<T> M(nc_r, nc_r), BBB(nc_r, nc_o), MM(nc_r, nc_r);
236  gmm::mult(gmm::conjugated(BBr), BBr, M);
237  gmm::mult(gmm::conjugated(BBr), BBo, BBB);
238  gmm::mult(BBB, gmm::conjugated(BBB), MM);
239  gmm::add(gmm::scaled(MM, T(-1)), M);
240 
241  std::vector<int> ipvt(nc_r);
242  gmm::lu_factor(M, ipvt);
243 
244  R emax = R(0);
245  for (i = 0; i < nc_r; ++i) emax = std::max(emax, gmm::abs(M(i,i)));
246 
247  i = 0;
248  std::set<size_type> c = columns;
249  for (TAB::iterator it = c.begin(); it != c.end(); ++it)
250  if (!(c_ortho[*it])) {
251  if (gmm::abs(M(i,i)) <= R(EPS)*emax) columns.erase(*it);
252  ++i;
253  }
254  }
255  }
256 
257 
258  // Range basis with Gram-Schmidt orthogonalization (sparse version)
259  // The sparse version is better when the sparsity is high and less efficient
260  // than the dense version for high degree elements (P3, P4 ...)
261  // Complex version not verified
262  template <typename Mat>
263  void range_basis_eff_Gram_Schmidt_sparse(const Mat &BB,
264  std::set<size_type> &columns,
265  std::vector<bool> &c_ortho,
266  double EPS) {
267 
268  typedef std::set<size_type> TAB;
269  typedef typename linalg_traits<Mat>::value_type T;
270  typedef typename number_traits<T>::magnitude_type R;
271 
272  size_type nc = mat_ncols(BB), nr = mat_nrows(BB);
273  std::set<size_type> c = columns, rc = columns;
274 
275  gmm::col_matrix< rsvector<T> > B(nr, nc);
276  for (std::set<size_type>::iterator it = columns.begin();
277  it != columns.end(); ++it) {
278  gmm::copy(mat_col(BB, *it), mat_col(B, *it));
279  gmm::scale(mat_col(B, *it), T(1)/vect_norm2(mat_col(B, *it)));
280  }
281 
282  for (std::set<size_type>::iterator it = c.begin(); it != c.end(); ++it)
283  if (c_ortho[*it]) {
284  for (std::set<size_type>::iterator it2 = rc.begin();
285  it2 != rc.end(); ++it2)
286  if (!(c_ortho[*it2])) {
287  T r = -vect_hp(mat_col(B, *it2), mat_col(B, *it));
288  if (r != T(0)) add(scaled(mat_col(B, *it), r), mat_col(B, *it2));
289  }
290  rc.erase(*it);
291  }
292 
293  while (rc.size()) {
294  R nmax = R(0); size_type cmax = size_type(-1);
295  for (std::set<size_type>::iterator it=rc.begin(); it != rc.end();) {
296  TAB::iterator itnext = it; ++itnext;
297  R n = vect_norm2(mat_col(B, *it));
298  if (nmax < n) { nmax = n; cmax = *it; }
299  if (n < R(EPS)) { columns.erase(*it); rc.erase(*it); }
300  it = itnext;
301  }
302 
303  if (nmax < R(EPS)) break;
304 
305  gmm::scale(mat_col(B, cmax), T(1)/vect_norm2(mat_col(B, cmax)));
306  rc.erase(cmax);
307  for (std::set<size_type>::iterator it=rc.begin(); it!=rc.end(); ++it) {
308  T r = -vect_hp(mat_col(B, *it), mat_col(B, cmax));
309  if (r != T(0)) add(scaled(mat_col(B, cmax), r), mat_col(B, *it));
310  }
311  }
312  for (std::set<size_type>::iterator it=rc.begin(); it!=rc.end(); ++it)
313  columns.erase(*it);
314  }
315 
316 
317  // Range basis with Gram-Schmidt orthogonalization (dense version)
318  template <typename Mat>
319  void range_basis_eff_Gram_Schmidt_dense(const Mat &B,
320  std::set<size_type> &columns,
321  std::vector<bool> &c_ortho,
322  double EPS) {
323 
324  typedef std::set<size_type> TAB;
325  typedef typename linalg_traits<Mat>::value_type T;
326  typedef typename number_traits<T>::magnitude_type R;
327 
328  size_type nc_r = columns.size(), nc = mat_ncols(B), nr = mat_nrows(B), i;
329  std::set<size_type> rc;
330 
331  row_matrix< gmm::rsvector<T> > H(nc, nc_r), BB(nr, nc_r);
332  std::vector<T> v(nc_r);
333  std::vector<size_type> ind(nc_r);
334 
335  i = 0;
336  for (TAB::iterator it = columns.begin(); it != columns.end(); ++it, ++i)
337  H(*it, i) = T(1) / vect_norm2(mat_col(B, *it));
338 
339  mult(B, H, BB);
340  dense_matrix<T> M(nc_r, nc_r);
341  mult(gmm::conjugated(BB), BB, M);
342 
343  i = 0;
344  for (TAB::iterator it = columns.begin(); it != columns.end(); ++it, ++i)
345  if (c_ortho[*it]) {
346  gmm::copy(mat_row(M, i), v);
347  rank_one_update(M, scaled(v, T(-1)), v);
348  M(i, i) = T(1);
349  }
350  else { rc.insert(i); ind[i] = *it; }
351 
352  while (rc.size() > 0) {
353 
354  // Next pivot
355  R nmax = R(0); size_type imax = size_type(-1);
356  for (TAB::iterator it = rc.begin(); it != rc.end();) {
357  TAB::iterator itnext = it; ++itnext;
358  R a = gmm::abs(M(*it, *it));
359  if (a > nmax) { nmax = a; imax = *it; }
360  if (a < R(EPS)) { columns.erase(ind[*it]); rc.erase(*it); }
361  it = itnext;
362  }
363 
364  if (nmax < R(EPS)) break;
365 
366  // Normalization
367  gmm::scale(mat_row(M, imax), T(1) / sqrt(nmax));
368  gmm::scale(mat_col(M, imax), T(1) / sqrt(nmax));
369 
370  // orthogonalization
371  copy(mat_row(M, imax), v);
372  rank_one_update(M, scaled(v, T(-1)), v);
373  M(imax, imax) = T(1);
374 
375  rc.erase(imax);
376  }
377  for (std::set<size_type>::iterator it=rc.begin(); it!=rc.end(); ++it)
378  columns.erase(ind[*it]);
379  }
380 
381 
382  template <typename Mat>
383  void range_basis(const Mat &B, std::set<size_type> &columns,
384  double EPS, col_major, bool skip_init=false) {
385 
386  typedef typename linalg_traits<Mat>::value_type T;
387  typedef typename number_traits<T>::magnitude_type R;
388 
389  size_type nc = mat_ncols(B), nr = mat_nrows(B);
390  std::vector<bool> c_ortho(nc);
391  if (!skip_init) {
392 
393  std::vector<R> norms(nc);
394  R norm_max = R(0);
395  for (size_type i = 0; i < nc; ++i) {
396  norms[i] = vect_norminf(mat_const_col(B, i));
397  norm_max = std::max(norm_max, norms[i]);
398  }
399 
400  columns.clear();
401  std::vector< std::set<size_type> > nnzs(nr+1); // 0,1,...,nr non-zeros
402  for (size_type i = 0; i < nc; ++i)
403  if (norms[i] > norm_max*R(EPS)) {
404  columns.insert(i);
405  size_type nnz_eps(0);
406  { // count how many non-zeros are in column i
407  const auto eps = R(EPS) * norms[i];
408  const auto col = mat_const_col(B, i);
409  const auto ite = vect_const_end(col);
410  for (auto it = vect_const_begin(col); it != ite; ++it)
411  if (gmm::abs(*it) >= eps)
412  ++nnz_eps;
413  }
414  nnzs[nnz_eps].insert(i);
415  }
416 
417  std::vector<bool> booked(nr);
418  for (size_type i = 1; i < nr; ++i)
419  for (std::set<size_type>::iterator it = nnzs[i].begin();
420  it != nnzs[i].end(); ++it) {
421  const auto eps = R(EPS) * norms[*it];
422  const auto col = mat_const_col(B, *it);
423  const auto ite = vect_const_end(col);
424  bool reserve__rb = true;
425  for (auto it1 = vect_const_begin(col); it1 != ite; ++it1)
426  if (gmm::abs(*it1) >= eps && booked[it1.index()])
427  reserve__rb = false;
428  if (reserve__rb) {
429  for (auto it1 = vect_const_begin(col); it1 != ite; ++it1)
430  if (gmm::abs(*it1) >= eps)
431  booked[it1.index()] = true;
432  c_ortho[*it] = true;
433  }
434  }
435  }
436 
437  size_type sizesm[7] = {125, 200, 350, 550, 800, 1100, 1500}, actsize;
438  for (int k = 0; k < 7; ++k) {
439  size_type nc_r = columns.size();
440  std::set<size_type> c1, cres;
441  actsize = sizesm[k];
442  for (std::set<size_type>::iterator it = columns.begin();
443  it != columns.end(); ++it) {
444  c1.insert(*it);
445  if (c1.size() >= actsize) {
446  range_basis_eff_Gram_Schmidt_dense(B, c1, c_ortho, EPS);
447  for (std::set<size_type>::iterator it2=c1.begin(); it2 != c1.end();
448  ++it2) cres.insert(*it2);
449  c1.clear();
450  }
451  }
452  if (c1.size() > 1)
453  range_basis_eff_Gram_Schmidt_dense(B, c1, c_ortho, EPS);
454  for (std::set<size_type>::iterator it = c1.begin(); it != c1.end(); ++it)
455  cres.insert(*it);
456  columns = cres;
457  if (nc_r <= actsize) return;
458  if (columns.size() == nc_r) break;
459  if (sizesm[k] >= 350 && columns.size() > (nc_r*19)/20) break;
460  }
461  if (columns.size() > std::max(size_type(10), actsize))
462  range_basis_eff_Lanczos(B, columns, EPS);
463  else
464  range_basis_eff_Gram_Schmidt_dense(B, columns, c_ortho, EPS);
465  }
466 
467 
468  template <typename Mat>
469  void range_basis(const Mat &B, std::set<size_type> &columns,
470  double EPS, row_major) {
471  typedef typename linalg_traits<Mat>::value_type T;
472  gmm::col_matrix< rsvector<T> > BB(mat_nrows(B), mat_ncols(B));
473  GMM_WARNING3("A copy of a row matrix is done into a column matrix "
474  "for range basis algorithm.");
475  gmm::copy(B, BB);
476  range_basis(BB, columns, EPS);
477  }
478 
479  /** Range Basis :
480  Extract a basis of the range of a (large sparse) matrix selecting some
481  column vectors of this matrix. This is in particular useful to select
482  an independent set of linear constraints.
483 
484  The algorithm is optimized for two cases :
485  - when the (non trivial) kernel is small. An iterativ algorithm
486  based on Lanczos method is applied
487  - when the (non trivial) kernel is large and most of the dependencies
488  can be detected locally. A block Gram-Schmidt is applied first then
489  a restarted Lanczos method when the remaining kernel is greatly
490  smaller.
491  The restarted Lanczos method could be improved or replaced by a block
492  Lanczos method, a block Wiedelann method (in order to be parallelized for
493  instance) or simply could compute more than one vector of the null
494  space at each iteration.
495  The LU decomposition has been tested for local elimination but gives bad
496  results : the algorithm is unstable and do not permit to give the right
497  number of vector at the end of the process. Moreover, the number of final
498  vectors depends greatly on the number of vectors in a block of the local
499  analysis.
500  */
501  template <typename Mat>
502  void range_basis(const Mat &B, std::set<size_type> &columns,
503  double EPS=1E-12) {
504  range_basis(B, columns, EPS,
505  typename principal_orientation_type
506  <typename linalg_traits<Mat>::sub_orientation>::potype());
507  }
508 
509 }
510 
511 #endif
void copy(const L1 &l1, L2 &l2)
*‍/
Definition: gmm_blas.h:976
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norm2(const V &v)
Euclidean norm of a vector.
Definition: gmm_blas.h:556
void fill_random(L &l)
fill a vector or matrix with random value (uniform [-1,1]).
Definition: gmm_blas.h:129
strongest_value_type< V1, V2 >::value_type vect_hp(const V1 &v1, const V2 &v2)
*‍/
Definition: gmm_blas.h:510
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norminf(const V &v)
Infinity norm of a vector.
Definition: gmm_blas.h:692
void clear(L &l)
clear (fill with zeros) a vector or matrix.
Definition: gmm_blas.h:58
void resize(V &v, size_type n)
*‍/
Definition: gmm_blas.h:209
void mult(const L1 &l1, const L2 &l2, L3 &l3)
*‍/
Definition: gmm_blas.h:1663
void add(const L1 &l1, L2 &l2)
*‍/
Definition: gmm_blas.h:1275
LU factorizations and determinant computation for dense matrices.
size_type lu_factor(DenseMatrix &A, Pvector &ipvt)
LU Factorization of a general (dense) matrix (real or complex).
Definition: gmm_dense_lu.h:92
Dense QR factorization.
Iteration object.
Include the base gmm files.
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:48
size_type alpha(short_type n, short_type d)
Return the value of which is the number of monomials of a polynomial of variables and degree .
Definition: bgeot_poly.cc:46