GetFEM  5.5
gmm_dense_matrix_functions.h
Go to the documentation of this file.
1 /* -*- c++ -*- (enables emacs c++ mode) */
2 /*===========================================================================
3 
4  Copyright (C) 2014-2026 Konstantinos Poulios
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_dense_matrix_functions.h
32  @author Konstantinos Poulios <poulios.konstantinos@gmail.com>
33  @date December 10, 2014.
34  @brief Common matrix functions for dense matrices.
35 */
36 #ifndef GMM_DENSE_MATRIX_FUNCTIONS_H
37 #define GMM_DENSE_MATRIX_FUNCTIONS_H
38 
39 
40 namespace gmm {
41 
42 
43  /**
44  Matrix square root for upper triangular matrices (from GNU Octave).
45  */
46  template <typename T>
47  void sqrtm_utri_inplace(dense_matrix<T>& A)
48  {
49  typedef typename number_traits<T>::magnitude_type R;
50  bool singular = false;
51 
52  // The following code is equivalent to this triple loop:
53  //
54  // n = rows (A);
55  // for j = 1:n
56  // A(j,j) = sqrt (A(j,j));
57  // for i = j-1:-1:1
58  // A(i,j) /= (A(i,i) + A(j,j));
59  // k = 1:i-1;
60  // t storing a A(k,j) -= A(k,i) * A(i,j);
61  // endfor
62  // endfor
63 
64  R tol = R(0); // default_tol(R()) * gmm::mat_maxnorm(A);
65 
66  const size_type n = mat_nrows(A);
67  for (int j=0; j < int(n); j++) {
68  typename dense_matrix<T>::iterator colj = A.begin() + j*n;
69  if (gmm::abs(colj[j]) > tol)
70  colj[j] = gmm::sqrt(colj[j]);
71  else
72  singular = true;
73 
74  for (int i=j-1; i >= 0; i--) {
75  typename dense_matrix<T>::const_iterator coli = A.begin() + i*n;
76  T colji = colj[i] = safe_divide(colj[i], (coli[i] + colj[j]));
77  for (int k = 0; k < i; k++)
78  colj[k] -= coli[k] * colji;
79  }
80  }
81 
82  if (singular)
83  GMM_WARNING1("Matrix is singular, may not have a square root");
84  }
85 
86 
87  template <typename T>
88  void sqrtm(const dense_matrix<std::complex<T> >& A,
89  dense_matrix<std::complex<T> >& SQRTMA)
90  {
91  GMM_ASSERT1(gmm::mat_nrows(A) == gmm::mat_ncols(A),
92  "Matrix square root requires a square matrix");
93  gmm::resize(SQRTMA, gmm::mat_nrows(A), gmm::mat_ncols(A));
94  dense_matrix<std::complex<T> > S(A), Q(A), TMP(A);
95  #if defined(GMM_USES_LAPACK)
96  schur(TMP, S, Q);
97  #else
98  GMM_ASSERT1(false, "Please recompile with lapack and blas librairies "
99  "to use sqrtm matrix function.");
100  #endif
101  sqrtm_utri_inplace(S);
102  gmm::mult(Q, S, TMP);
103  gmm::mult(TMP, gmm::transposed(Q), SQRTMA);
104  }
105 
106  template <typename T>
107  void sqrtm(const dense_matrix<T>& A,
108  dense_matrix<std::complex<T> >& SQRTMA)
109  {
110  dense_matrix<std::complex<T> > cA(mat_nrows(A), mat_ncols(A));
111  gmm::copy(A, gmm::real_part(cA));
112  sqrtm(cA, SQRTMA);
113  }
114 
115  template <typename T>
116  void sqrtm(const dense_matrix<T>& A, dense_matrix<T>& SQRTMA)
117  {
118  dense_matrix<std::complex<T> > cA(mat_nrows(A), mat_ncols(A));
119  gmm::copy(A, gmm::real_part(cA));
120  dense_matrix<std::complex<T> > cSQRTMA(cA);
121  sqrtm(cA, cSQRTMA);
122  gmm::resize(SQRTMA, gmm::mat_nrows(A), gmm::mat_ncols(A));
123  gmm::copy(gmm::real_part(cSQRTMA), SQRTMA);
124 // dense_matrix<std::complex<T1> >::const_reference
125 // it = cSQRTMA.begin(), ite = cSQRTMA.end();
126 // dense_matrix<std::complex<T1> >::reference
127 // rit = SQRTMA.begin();
128 // for (; it != ite; ++it, ++rit) *rit = it->real();
129  }
130 
131 
132  /**
133  Matrix logarithm for upper triangular matrices (from GNU/Octave)
134  */
135  template <typename T>
136  void logm_utri_inplace(dense_matrix<T>& S)
137  {
138  typedef typename number_traits<T>::magnitude_type R;
139 
140  size_type n = gmm::mat_nrows(S);
141  GMM_ASSERT1(n == gmm::mat_ncols(S),
142  "Matrix logarithm is not defined for non-square matrices");
143  for (size_type i=0; i < n-1; ++i)
144  if (gmm::abs(S(i+1,i)) > default_tol(T())) {
145  GMM_ASSERT1(false, "An upper triangular matrix is expected");
146  break;
147  }
148  for (size_type i=0; i < n-1; ++i)
149  if (gmm::real(S(i,i)) <= -default_tol(R()) &&
150  gmm::abs(gmm::imag(S(i,i))) <= default_tol(R())) {
151  GMM_ASSERT1(false, "Principal matrix logarithm is not defined "
152  "for matrices with negative eigenvalues");
153  break;
154  }
155 
156  // Algorithm 11.9 in "Function of matrices", by N. Higham
157  R theta[] = { R(0),R(0),R(1.61e-2),R(5.38e-2),R(1.13e-1),R(1.86e-1),R(2.6429608311114350e-1) };
158 
159  R scaling(1);
160  size_type p(0), m(6), opt_iters(100);
161  for (size_type k=0; k < opt_iters; ++k, scaling *= R(2)) {
162  dense_matrix<T> auxS(S);
163  for (size_type i = 0; i < n; ++i) auxS(i,i) -= R(1);
164  R tau = gmm::mat_norm1(auxS);
165  if (tau <= theta[6]) {
166  ++p;
167  size_type j1(6), j2(6);
168  for (size_type j=0; j < 6; ++j)
169  if (tau <= theta[j]) { j1 = j; break; }
170  for (size_type j=0; j < j1; ++j)
171  if (tau <= 2*theta[j]) { j2 = j; break; }
172  if (j1 - j2 <= 1 || p == 2) { m = j1; break; }
173  }
175  if (k == opt_iters-1)
176  GMM_WARNING1 ("Maximum number of square roots exceeded; "
177  "the calculated matrix logarithm may still be accurate");
178  }
179 
180  for (size_type i = 0; i < n; ++i) S(i,i) -= R(1);
181 
182  if (m > 0) {
183 
184  std::vector<R> nodes, wts;
185  switch(m) {
186  case 0: {
187  R nodes_[] = { R(0.5) };
188  R wts_[] = { R(1) };
189  nodes.assign(nodes_, nodes_+m+1);
190  wts.assign(wts_, wts_+m+1);
191  } break;
192  case 1: {
193  R nodes_[] = { R(0.211324865405187),R(0.788675134594813) };
194  R wts_[] = { R(0.5),R(0.5) };
195  nodes.assign(nodes_, nodes_+m+1);
196  wts.assign(wts_, wts_+m+1);
197  } break;
198  case 2: {
199  R nodes_[] = { R(0.112701665379258),R(0.500000000000000),R(0.887298334620742) };
200  R wts_[] = { R(0.277777777777778),R(0.444444444444444),R(0.277777777777778) };
201  nodes.assign(nodes_, nodes_+m+1);
202  wts.assign(wts_, wts_+m+1);
203  } break;
204  case 3: {
205  R nodes_[] = { R(0.0694318442029737),R(0.3300094782075718),R(0.6699905217924281),R(0.9305681557970263) };
206  R wts_[] = { R(0.173927422568727),R(0.326072577431273),R(0.326072577431273),R(0.173927422568727) };
207  nodes.assign(nodes_, nodes_+m+1);
208  wts.assign(wts_, wts_+m+1);
209  } break;
210  case 4: {
211  R nodes_[] = { R(0.0469100770306681),R(0.2307653449471584),R(0.5000000000000000),R(0.7692346550528415),R(0.9530899229693319) };
212  R wts_[] = { R(0.118463442528095),R(0.239314335249683),R(0.284444444444444),R(0.239314335249683),R(0.118463442528094) };
213  nodes.assign(nodes_, nodes_+m+1);
214  wts.assign(wts_, wts_+m+1);
215  } break;
216  case 5: {
217  R nodes_[] = { R(0.0337652428984240),R(0.1693953067668678),R(0.3806904069584015),R(0.6193095930415985),R(0.8306046932331322),R(0.9662347571015761) };
218  R wts_[] = { R(0.0856622461895853),R(0.1803807865240693),R(0.2339569672863452),R(0.2339569672863459),R(0.1803807865240693),R(0.0856622461895852) };
219  nodes.assign(nodes_, nodes_+m+1);
220  wts.assign(wts_, wts_+m+1);
221  } break;
222  case 6: {
223  R nodes_[] = { R(0.0254460438286208),R(0.1292344072003028),R(0.2970774243113015),R(0.4999999999999999),R(0.7029225756886985),R(0.8707655927996973),R(0.9745539561713792) };
224  R wts_[] = { R(0.0647424830844348),R(0.1398526957446384),R(0.1909150252525594),R(0.2089795918367343),R(0.1909150252525595),R(0.1398526957446383),R(0.0647424830844349) };
225  nodes.assign(nodes_, nodes_+m+1);
226  wts.assign(wts_, wts_+m+1);
227  } break;
228  }
229 
230  dense_matrix<T> auxS1(S), auxS2(S);
231  std::vector<T> auxvec(n);
232  gmm::clear(S);
233  for (size_type j=0; j <= m; ++j) {
234  gmm::copy(gmm::scaled(auxS1, nodes[j]), auxS2);
235  gmm::add(gmm::identity_matrix(), auxS2);
236  // S += wts[i] * auxS1 * inv(auxS2)
237  for (size_type i=0; i < n; ++i) {
238  gmm::copy(gmm::mat_row(auxS1, i), auxvec);
239  gmm::lower_tri_solve(gmm::transposed(auxS2), auxvec, false);
240  gmm::add(gmm::scaled(auxvec, wts[j]), gmm::mat_row(S, i));
241  }
242  }
243  }
244  gmm::scale(S, scaling);
245  }
246 
247  /**
248  Matrix logarithm (from GNU/Octave)
249  */
250  template <typename T>
251  void logm(const dense_matrix<T>& A, dense_matrix<T>& LOGMA)
252  {
253  typedef typename number_traits<T>::magnitude_type R;
254  size_type n = gmm::mat_nrows(A);
255  GMM_ASSERT1(n == gmm::mat_ncols(A),
256  "Matrix logarithm is not defined for non-square matrices");
257  dense_matrix<T> S(A), Q(A);
258  #if defined(GMM_USES_LAPACK)
259  schur(A, S, Q); // A = Q * S * Q^T
260  #else
261  GMM_ASSERT1(false, "Please recompile with lapack and blas libraries "
262  "to use logm matrix function.");
263  #endif
264 
265  bool convert_to_complex(false);
266  if (!is_complex(T()))
267  for (size_type i=0; i < n-1; ++i)
268  if (gmm::abs(S(i+1,i)) > default_tol(T())) {
269  convert_to_complex = true;
270  break;
271  }
272 
273  gmm::resize(LOGMA, n, n);
274  if (convert_to_complex) {
275  dense_matrix<std::complex<R> > cS(n,n), cQ(n,n), auxmat(n,n);
276  gmm::copy(gmm::real_part(S), gmm::real_part(cS));
277  gmm::copy(gmm::real_part(Q), gmm::real_part(cQ));
278  block2x2_reduction(cS, cQ, default_tol(R())*R(3));
279  for (size_type j=0; j < n-1; ++j)
280  for (size_type i=j+1; i < n; ++i)
281  cS(i,j) = T(0);
282  logm_utri_inplace(cS);
283  gmm::mult(cQ, cS, auxmat);
284  gmm::mult(auxmat, gmm::transposed(cQ), cS);
285  // Remove small complex values which may have entered calculation
286  gmm::copy(gmm::real_part(cS), LOGMA);
287 // GMM_ASSERT1(gmm::mat_norm1(gmm::imag_part(cS)) < n*default_tol(T()),
288 // "Internal error, imag part should be zero");
289  } else {
290  dense_matrix<T> auxmat(n,n);
292  gmm::mult(Q, S, auxmat);
293  gmm::mult(auxmat, gmm::transposed(Q), LOGMA);
294  }
295 
296  }
297 
298 }
299 
300 #endif
301 
void copy(const L1 &l1, L2 &l2)
*‍/
Definition: gmm_blas.h:976
number_traits< typename linalg_traits< M >::value_type >::magnitude_type mat_norm1(const M &m)
*‍/
Definition: gmm_blas.h:781
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
void logm_utri_inplace(dense_matrix< T > &S)
Matrix logarithm for upper triangular matrices (from GNU/Octave)
void sqrtm_utri_inplace(dense_matrix< T > &A)
Matrix square root for upper triangular matrices (from GNU Octave).
void logm(const dense_matrix< T > &A, dense_matrix< T > &LOGMA)
Matrix logarithm (from GNU/Octave)