GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
sqrtm.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2001-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include "schur.h"
31 #include "lo-ieee.h"
32 #include "lo-mappers.h"
33 #include "oct-norm.h"
34 
35 #include "defun.h"
36 #include "error.h"
37 #include "errwarn.h"
38 #include "utils.h"
39 #include "xnorm.h"
40 
42 
43 template <typename Matrix>
44 static void
45 sqrtm_utri_inplace (Matrix& T)
46 {
47  typedef typename Matrix::element_type element_type;
48 
49  const element_type zero = element_type ();
50 
51  bool singular = false;
52 
53  // The following code is equivalent to this triple loop:
54  //
55  // n = rows (T);
56  // for j = 1:n
57  // T(j,j) = sqrt (T(j,j));
58  // for i = j-1:-1:1
59  // if T(i,j) != 0
60  // T(i,j) /= (T(i,i) + T(j,j));
61  // endif
62  // k = 1:i-1;
63  // T(k,j) -= T(k,i) * T(i,j);
64  // endfor
65  // endfor
66  //
67  // this is an in-place, cache-aligned variant of the code
68  // given in Higham's paper.
69 
70  const octave_idx_type n = T.rows ();
71  element_type *Tp = T.fortran_vec ();
72  for (octave_idx_type j = 0; j < n; j++)
73  {
74  element_type *colj = Tp + n*j;
75  if (colj[j] != zero)
76  colj[j] = sqrt (colj[j]);
77  else
78  singular = true;
79 
80  for (octave_idx_type i = j-1; i >= 0; i--)
81  {
82  const element_type *coli = Tp + n*i;
83  if (colj[i] != zero)
84  colj[i] /= (coli[i] + colj[j]);
85  const element_type colji = colj[i];
86  for (octave_idx_type k = 0; k < i; k++)
87  colj[k] -= coli[k] * colji;
88  }
89  }
90 
91  if (singular)
92  warning_with_id ("Octave:sqrtm:SingularMatrix",
93  "sqrtm: matrix is singular, may not have a square root");
94 }
95 
96 template <typename Matrix, typename ComplexMatrix, typename ComplexSCHUR>
97 static octave_value
98 do_sqrtm (const octave_value& arg)
99 {
100 
101  octave_value retval;
102 
103  MatrixType mt = arg.matrix_type ();
104 
105  bool iscomplex = arg.iscomplex ();
106 
107  typedef typename Matrix::element_type real_type;
108 
109  real_type cutoff = 0;
110  real_type one = 1;
111  real_type eps = std::numeric_limits<real_type>::epsilon ();
112 
113  if (! iscomplex)
114  {
116 
117  if (mt.is_unknown ()) // if type is not known, compute it now.
118  arg.matrix_type (mt = MatrixType (x));
119 
120  switch (mt.type ())
121  {
122  case MatrixType::Upper:
124  if (! x.diag ().any_element_is_negative ())
125  {
126  // Do it in real arithmetic.
127  sqrtm_utri_inplace (x);
128  retval = x;
129  retval.matrix_type (mt);
130  }
131  else
132  iscomplex = true;
133  break;
134 
135  case MatrixType::Lower:
136  if (! x.diag ().any_element_is_negative ())
137  {
138  x = x.transpose ();
139  sqrtm_utri_inplace (x);
140  retval = x.transpose ();
141  retval.matrix_type (mt);
142  }
143  else
144  iscomplex = true;
145  break;
146 
147  default:
148  iscomplex = true;
149  break;
150  }
151 
152  if (iscomplex)
153  cutoff = 10 * x.rows () * eps * xnorm (x, one);
154  }
155 
156  if (iscomplex)
157  {
159 
160  if (mt.is_unknown ()) // if type is not known, compute it now.
161  arg.matrix_type (mt = MatrixType (x));
162 
163  switch (mt.type ())
164  {
165  case MatrixType::Upper:
167  sqrtm_utri_inplace (x);
168  retval = x;
169  retval.matrix_type (mt);
170  break;
171 
172  case MatrixType::Lower:
173  x = x.transpose ();
174  sqrtm_utri_inplace (x);
175  retval = x.transpose ();
176  retval.matrix_type (mt);
177  break;
178 
179  default:
180  {
181  ComplexMatrix u;
182 
183  do
184  {
185  ComplexSCHUR schur_fact (x, "", true);
186  x = schur_fact.schur_matrix ();
187  u = schur_fact.unitary_schur_matrix ();
188  }
189  while (0); // schur no longer needed.
190 
191  sqrtm_utri_inplace (x);
192 
193  x = u * x; // original x no longer needed.
195 
196  if (cutoff > 0 && xnorm (imag (res), one) <= cutoff)
197  retval = real (res);
198  else
199  retval = res;
200  }
201  break;
202  }
203  }
204 
205  return retval;
206 }
207 
208 DEFUN (sqrtm, args, nargout,
209  doc: /* -*- texinfo -*-
210 @deftypefn {} {@var{s} =} sqrtm (@var{A})
211 @deftypefnx {} {[@var{s}, @var{error_estimate}] =} sqrtm (@var{A})
212 Compute the matrix square root of the square matrix @var{A}.
213 
214 Ref: @nospell{N.J. Higham}. @cite{A New sqrtm for @sc{matlab}}. Numerical
215 Analysis Report No.@: 336, Manchester @nospell{Centre} for Computational
216 Mathematics, Manchester, England, January 1999.
217 @seealso{expm, logm}
218 @end deftypefn */)
219 {
220  if (args.length () != 1)
221  print_usage ();
222 
223  octave_value arg = args(0);
224 
225  octave_idx_type n = arg.rows ();
226  octave_idx_type nc = arg.columns ();
227 
228  if (n != nc || arg.ndims () > 2)
229  err_square_matrix_required ("sqrtm", "A");
230 
231  octave_value_list retval (nargout > 1 ? 3 : 1);
232 
233  if (nargout > 1)
234  {
235  // FIXME: Octave does not calculate a condition number with respect to
236  // sqrtm. Should this return NaN instead of -1?
237  retval(2) = -1.0;
238  }
239 
240  if (arg.is_diag_matrix ())
241  // sqrtm of a diagonal matrix is just sqrt.
242  retval(0) = arg.sqrt ();
243  else if (arg.is_single_type ())
244  retval(0) = do_sqrtm<FloatMatrix, FloatComplexMatrix,
245  math::schur<FloatComplexMatrix>> (arg);
246  else if (arg.isnumeric ())
247  retval(0) = do_sqrtm<Matrix, ComplexMatrix,
248  math::schur<ComplexMatrix>> (arg);
249 
250  if (nargout > 1)
251  {
252  // This corresponds to generic code
253  //
254  // norm (s*s - x, "fro") / norm (x, "fro");
255 
256  octave_value s = retval(0);
257  retval(1) = xfrobnorm (s*s - arg) / xfrobnorm (arg);
258  }
259 
260  return retval;
261 }
262 
263 /*
264 %!assert (sqrtm (2* ones (2)), ones (2), 3*eps)
265 %!assert <*60797> (sqrtm (ones (4))^2, ones (4), 5*eps)
266 
267 ## The following two tests are from the reference in the docstring above.
268 %!test
269 %! warning ("off", "Octave:sqrtm:SingularMatrix", "local");
270 %! x = [0 1; 0 0];
271 %! assert (any (isnan (sqrtm (x))(:)));
272 
273 %!test
274 %! x = eye (4); x(2,2) = x(3,3) = 2^-26; x(1,4) = 1;
275 %! z = eye (4); z(2,2) = z(3,3) = 2^-13; z(1,4) = 0.5;
276 %! [y, err] = sqrtm (x);
277 %! assert (y, z);
278 %! assert (err, 0); # Yes, this one has to hold exactly
279 */
280 
281 OCTAVE_END_NAMESPACE(octave)
ComplexMatrix xgemm(const ComplexMatrix &a, const ComplexMatrix &b, blas_trans_type transa, blas_trans_type transb)
Definition: CMatrix.cc:3346
T * fortran_vec()
Size of the specified dimension.
Definition: Array-base.cc:1764
octave_idx_type rows() const
Definition: Array.h:459
double element_type
Definition: Array.h:230
bool is_unknown() const
Definition: MatrixType.h:132
int type(bool quiet=true)
Definition: MatrixType.cc:656
Definition: dMatrix.h:42
bool is_diag_matrix() const
Definition: ov.h:631
octave_value sqrt() const
Definition: ov.h:1489
int ndims() const
Definition: ov.h:551
octave_idx_type rows() const
Definition: ov.h:545
bool isnumeric() const
Definition: ov.h:750
MatrixType matrix_type() const
Definition: ov.h:583
bool is_single_type() const
Definition: ov.h:698
bool iscomplex() const
Definition: ov.h:741
octave_idx_type columns() const
Definition: ov.h:547
ColumnVector real(const ComplexColumnVector &a)
Definition: dColVector.cc:137
ColumnVector imag(const ComplexColumnVector &a)
Definition: dColVector.cc:143
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
T eps(const T &x)
Definition: data.cc:4964
void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:1078
void err_square_matrix_required(const char *fcn, const char *name)
Definition: errwarn.cc:122
F77_RET_T const F77_DBLE * x
@ blas_no_trans
Definition: mx-defs.h:81
@ blas_conj_trans
Definition: mx-defs.h:83
octave_idx_type n
Definition: mx-inlines.cc:761
double xfrobnorm(const Matrix &x)
Definition: oct-norm.cc:610
double xnorm(const ColumnVector &x, double p)
Definition: oct-norm.cc:610
ComplexMatrix octave_value_extract< ComplexMatrix >(const octave_value &v)
Definition: ov.h:1748
Matrix octave_value_extract< Matrix >(const octave_value &v)
Definition: ov.h:1746