GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
sqrtm.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2001-2025 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
43template <typename T>
44static void
45sqrtm_utri_inplace (T& m)
46{
47 typedef typename T::element_type element_type;
48 typedef typename T::real_matrix_type real_matrix_type;
49 typedef typename T::real_elt_type real_elt_type;
50
51 const element_type zero = element_type ();
52
53 bool singular = false;
54 bool diagonal = true;
55
56 // The Schur matrix of Hermitian matrices is diagonal.
57 // check for off-diagonal elements above tolerance
58 const octave_idx_type n = m.rows ();
59 real_matrix_type abs_m = m.abs ();
60
61 real_elt_type max_abs_diag = abs_m.diag ().max ()(0);
62
63 const real_elt_type tol = n * max_abs_diag
64 * std::numeric_limits<real_elt_type>::epsilon ();
65
66 for (octave_idx_type j = 0; j < n && diagonal; j++)
67 {
68 for (octave_idx_type i = j-1; i >= 0; i--)
69 {
70 if (abs_m(i,j) > tol)
71 {
72 diagonal = false;
73 break;
74 }
75 }
76 }
77
78 element_type *mp = m.rwdata ();
79 if (diagonal)
80 {
81 // shortcut for diagonal Schur matrices
82 for (octave_idx_type i = 0; i < n; i++)
83 {
84 octave_idx_type idx_diag = i*(n+1);
85 if (mp[idx_diag] != zero)
86 mp[idx_diag] = sqrt (mp[idx_diag]);
87 else
88 singular = true;
89 }
90 }
91 else
92 {
93 // The following code is equivalent to this triple loop:
94 //
95 // n = rows (m);
96 // for j = 1:n
97 // m(j,j) = sqrt (m(j,j));
98 // for i = j-1:-1:1
99 // if m(i,j) != 0
100 // m(i,j) /= (m(i,i) + m(j,j));
101 // endif
102 // k = 1:i-1;
103 // m(k,j) -= m(k,i) * m(i,j);
104 // endfor
105 // endfor
106 //
107 // this is an in-place, cache-aligned variant of the code
108 // given in Higham's paper.
109
110 for (octave_idx_type j = 0; j < n; j++)
111 {
112 element_type *colj = mp + n*j;
113 if (colj[j] != zero)
114 colj[j] = sqrt (colj[j]);
115 else
116 singular = true;
117
118 for (octave_idx_type i = j-1; i >= 0; i--)
119 {
120 const element_type *coli = mp + n*i;
121 if (colj[i] != zero)
122 colj[i] /= (coli[i] + colj[j]);
123 const element_type colji = colj[i];
124 for (octave_idx_type k = 0; k < i; k++)
125 colj[k] -= coli[k] * colji;
126 }
127 }
128 }
129
130 if (singular)
131 warning_with_id ("Octave:sqrtm:SingularMatrix",
132 "sqrtm: matrix is singular, may not have a square root");
133}
134
135template <typename Matrix, typename ComplexMatrix, typename ComplexSCHUR>
136static octave_value
137do_sqrtm (const octave_value& arg)
138{
139
140 octave_value retval;
141
142 MatrixType mt = arg.matrix_type ();
143
144 bool iscomplex = arg.iscomplex ();
145
146 typedef typename Matrix::element_type real_type;
147
148 real_type cutoff = 0;
149 real_type one = 1;
150 real_type eps = std::numeric_limits<real_type>::epsilon ();
151
152 if (! iscomplex)
153 {
155
156 if (mt.is_unknown ()) // if type is not known, compute it now.
157 arg.matrix_type (mt = MatrixType (x));
158
159 switch (mt.type ())
160 {
163 if (! x.diag ().any_element_is_negative ())
164 {
165 // Do it in real arithmetic.
166 sqrtm_utri_inplace (x);
167 retval = x;
168 retval.matrix_type (mt);
169 }
170 else
171 iscomplex = true;
172 break;
173
175 if (! x.diag ().any_element_is_negative ())
176 {
177 x = x.transpose ();
178 sqrtm_utri_inplace (x);
179 retval = x.transpose ();
180 retval.matrix_type (mt);
181 }
182 else
183 iscomplex = true;
184 break;
185
186 default:
187 iscomplex = true;
188 break;
189 }
190
191 if (iscomplex)
192 cutoff = 10 * x.rows () * eps * xnorm (x, one);
193 }
194
195 if (iscomplex)
196 {
198
199 if (mt.is_unknown ()) // if type is not known, compute it now.
200 arg.matrix_type (mt = MatrixType (x));
201
202 switch (mt.type ())
203 {
206 sqrtm_utri_inplace (x);
207 retval = x;
208 retval.matrix_type (mt);
209 break;
210
212 x = x.transpose ();
213 sqrtm_utri_inplace (x);
214 retval = x.transpose ();
215 retval.matrix_type (mt);
216 break;
217
218 default:
219 {
221
222 do
223 {
224 ComplexSCHUR schur_fact (x, "", true);
225 x = schur_fact.schur_matrix ();
226 u = schur_fact.unitary_schur_matrix ();
227 }
228 while (0); // schur_fact no longer needed.
229
230 sqrtm_utri_inplace (x);
231
232 x = u * x; // original x no longer needed.
234
235 if (cutoff > 0 && xnorm (imag (res), one) <= cutoff)
236 retval = real (res);
237 else
238 retval = res;
239 }
240 break;
241 }
242 }
243
244 return retval;
245}
246
247DEFUN (sqrtm, args, nargout,
248 doc: /* -*- texinfo -*-
249@deftypefn {} {@var{s} =} sqrtm (@var{A})
250@deftypefnx {} {[@var{s}, @var{error_estimate}] =} sqrtm (@var{A})
251Compute the matrix square root of the square matrix @var{A}.
252
253Ref: @nospell{N.J. Higham}. @cite{A New sqrtm for @sc{matlab}}. Numerical
254Analysis Report No.@: 336, Manchester @nospell{Centre} for Computational
255Mathematics, Manchester, England, January 1999.
256@seealso{expm, logm}
257@end deftypefn */)
258{
259 if (args.length () != 1)
260 print_usage ();
261
262 octave_value arg = args(0);
263
264 octave_idx_type n = arg.rows ();
265 octave_idx_type nc = arg.columns ();
266
267 if (n != nc || arg.ndims () > 2)
268 err_square_matrix_required ("sqrtm", "A");
269
270 octave_value_list retval (nargout > 1 ? 3 : 1);
271
272 if (nargout > 1)
273 {
274 // FIXME: Octave does not calculate a condition number with respect to
275 // sqrtm. Should this return NaN instead of -1?
276 retval(2) = -1.0;
277 }
278
279 if (arg.is_diag_matrix ())
280 // sqrtm of a diagonal matrix is just sqrt.
281 retval(0) = arg.sqrt ();
282 else if (arg.is_single_type ())
283 retval(0) = do_sqrtm<FloatMatrix, FloatComplexMatrix,
284 math::schur<FloatComplexMatrix>> (arg);
285 else if (arg.isnumeric ())
286 retval(0) = do_sqrtm<Matrix, ComplexMatrix,
287 math::schur<ComplexMatrix>> (arg);
288
289 if (nargout > 1)
290 {
291 // This corresponds to generic code
292 //
293 // norm (s*s - x, "fro") / norm (x, "fro");
294
295 octave_value s = retval(0);
296 retval(1) = xfrobnorm (s*s - arg) / xfrobnorm (arg);
297 }
298
299 return retval;
300}
301
302/*
303%!assert (sqrtm (2* ones (2)), ones (2), 3*eps)
304%!assert <*60797> (sqrtm (ones (4))^2, ones (4), 5*eps)
305
306## The following two tests are from the reference in the docstring above.
307%!test
308%! warning ("off", "Octave:sqrtm:SingularMatrix", "local");
309%! x = [0 1; 0 0];
310%! assert (any (isnan (sqrtm (x))(:)));
311
312%!test
313%! x = eye (4); x(2,2) = x(3,3) = 2^-26; x(1,4) = 1;
314%! z = eye (4); z(2,2) = z(3,3) = 2^-13; z(1,4) = 0.5;
315%! [y, err] = sqrtm (x);
316%! assert (y, z);
317%! assert (err, 0); # Yes, this one has to hold exactly
318*/
319
320OCTAVE_END_NAMESPACE(octave)
ComplexMatrix xgemm(const ComplexMatrix &a, const ComplexMatrix &b, blas_trans_type transa, blas_trans_type transb)
Definition CMatrix.cc:3347
T element_type
Definition Array.h:234
bool is_unknown() const
Definition MatrixType.h:131
MatrixType transpose() const
int type(bool quiet=true)
bool is_diag_matrix() const
Definition ov.h:631
octave_value sqrt() const
Definition ov.h:1504
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)
ColumnVector imag(const ComplexColumnVector &a)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
T eps(const T &x)
Definition data.cc:5008
void print_usage()
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:1093
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
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:1767
Matrix octave_value_extract< Matrix >(const octave_value &v)
Definition ov.h:1765