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
schur.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-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 <string>
31 
32 #include "schur.h"
33 
34 #include "defun.h"
35 #include "error.h"
36 #include "errwarn.h"
37 #include "ovl.h"
38 #include "utils.h"
39 
41 
42 template <typename Matrix>
43 static octave_value
44 mark_upper_triangular (const Matrix& a)
45 {
46  octave_value retval = a;
47 
48  octave_idx_type n = a.rows ();
49  error_unless (a.columns () == n);
50 
51  const typename Matrix::element_type zero = typename Matrix::element_type ();
52 
53  for (octave_idx_type i = 0; i < n; i++)
54  if (a(i, i) == zero)
55  return retval;
56 
58 
59  return retval;
60 }
61 
62 DEFUN (schur, args, nargout,
63  doc: /* -*- texinfo -*-
64 @deftypefn {} {@var{S} =} schur (@var{A})
65 @deftypefnx {} {@var{S} =} schur (@var{A}, "real")
66 @deftypefnx {} {@var{S} =} schur (@var{A}, "complex")
67 @deftypefnx {} {@var{S} =} schur (@var{A}, @var{opt})
68 @deftypefnx {} {[@var{U}, @var{S}] =} schur (@dots{})
69 @cindex Schur decomposition
70 Compute the Schur@tie{}decomposition of @var{A}.
71 
72 The Schur@tie{}decomposition of a square matrix @var{A} is defined as
73 @tex
74 $$
75  S = U^T A U
76 $$
77 @end tex
78 @ifnottex
79 
80 @example
81 @code{@var{S} = @var{U}' * @var{A} * @var{U}}
82 @end example
83 
84 @end ifnottex
85 where @var{U} is a unitary matrix
86 @tex
87 ($U^T U$ is identity)
88 @end tex
89 @ifnottex
90 (@code{@var{U}'* @var{U}} is identity)
91 @end ifnottex
92 and @var{S} is upper triangular. The eigenvalues of @var{A} (and @var{S})
93 are the diagonal elements of @var{S}. If the matrix @var{A} is real, then
94 the real Schur@tie{}decomposition is computed, in which the matrix @var{U}
95 is orthogonal and @var{S} is block upper triangular with blocks of size at
96 most
97 @tex
98 $2 \times 2$
99 @end tex
100 @ifnottex
101 @code{2 x 2}
102 @end ifnottex
103 along the diagonal.
104 
105 The default for real matrices is a real Schur@tie{}decomposition. A complex
106 decomposition may be forced by passing the flag @qcode{"complex"}.
107 
108 The eigenvalues are optionally ordered along the diagonal according to the
109 value of @var{opt}:
110 
111 @table @asis
112 @item @qcode{@var{opt} = "a"}
113 Move eigenvalues with negative real parts to the leading block of @var{S}.
114 Mnemonic: @qcode{"a"} for Algebraic @nospell{Riccati} Equations, where this
115 ordering is useful.
116 
117 @item @qcode{@var{opt} = "d"}
118 Move eigenvalues with magnitude less than one to the leading block of @var{S}.
119 Mnemonic: @qcode{"d"} for Discrete Algebraic @nospell{Riccati} Equations,
120 where this ordering is useful.
121 
122 @item @qcode{@var{opt} = "u"}
123 Unordered. No particular ordering of eigenvalues (default).
124 @end table
125 
126 The leading @var{k} columns of @var{U} always span the @var{A}-invariant
127 subspace corresponding to the @var{k} leading eigenvalues of @var{S}.
128 @seealso{rsf2csf, ordschur, ordeig, lu, chol, hess, qr, qz, svd, eig}
129 @end deftypefn */)
130 {
131  int nargin = args.length ();
132 
133  if (nargin < 1 || nargin > 2 || nargout > 2)
134  print_usage ();
135 
136  octave_value arg = args(0);
137 
138  std::string ord;
139  if (nargin == 2)
140  ord = args(1).xstring_value ("schur: second argument must be a string");
141 
142  bool force_complex = false;
143 
144  if (ord == "real")
145  {
146  ord = "";
147  }
148  else if (ord == "complex")
149  {
150  force_complex = true;
151  ord = "";
152  }
153  else
154  {
155  char ord_char = (ord.empty () ? 'U' : ord[0]);
156 
157  if (ord_char != 'U' && ord_char != 'A' && ord_char != 'D'
158  && ord_char != 'u' && ord_char != 'a' && ord_char != 'd')
159  {
160  warning ("schur: incorrect ordered schur argument '%s'",
161  ord.c_str ());
162  return ovl ();
163  }
164  }
165 
166  octave_idx_type nr = arg.rows ();
167  octave_idx_type nc = arg.columns ();
168 
169  if (nr != nc)
170  err_square_matrix_required ("schur", "A");
171 
172  if (! arg.isnumeric ())
173  err_wrong_type_arg ("schur", arg);
174 
175  octave_value_list retval;
176 
177  if (arg.is_single_type ())
178  {
179  if (! force_complex && arg.isreal ())
180  {
181  FloatMatrix tmp = arg.float_matrix_value ();
182 
183  if (nargout <= 1)
184  {
185  math::schur<FloatMatrix> result (tmp, ord, false);
186  retval = ovl (result.schur_matrix ());
187  }
188  else
189  {
190  math::schur<FloatMatrix> result (tmp, ord, true);
191  retval = ovl (result.unitary_schur_matrix (),
192  result.schur_matrix ());
193  }
194  }
195  else
196  {
198 
199  if (nargout <= 1)
200  {
201  math::schur<FloatComplexMatrix> result (ctmp, ord, false);
202  retval = ovl (mark_upper_triangular (result.schur_matrix ()));
203  }
204  else
205  {
206  math::schur<FloatComplexMatrix> result (ctmp, ord, true);
207  retval = ovl (result.unitary_schur_matrix (),
208  mark_upper_triangular (result.schur_matrix ()));
209  }
210  }
211  }
212  else
213  {
214  if (! force_complex && arg.isreal ())
215  {
216  Matrix tmp = arg.matrix_value ();
217 
218  if (nargout <= 1)
219  {
220  math::schur<Matrix> result (tmp, ord, false);
221  retval = ovl (result.schur_matrix ());
222  }
223  else
224  {
225  math::schur<Matrix> result (tmp, ord, true);
226  retval = ovl (result.unitary_schur_matrix (),
227  result.schur_matrix ());
228  }
229  }
230  else
231  {
232  ComplexMatrix ctmp = arg.complex_matrix_value ();
233 
234  if (nargout <= 1)
235  {
236  math::schur<ComplexMatrix> result (ctmp, ord, false);
237  retval = ovl (mark_upper_triangular (result.schur_matrix ()));
238  }
239  else
240  {
241  math::schur<ComplexMatrix> result (ctmp, ord, true);
242  retval = ovl (result.unitary_schur_matrix (),
243  mark_upper_triangular (result.schur_matrix ()));
244  }
245  }
246  }
247 
248  return retval;
249 }
250 
251 /*
252 %!test
253 %! a = [1, 2, 3; 4, 5, 9; 7, 8, 6];
254 %! [u, s] = schur (a);
255 %! assert (u' * a * u, s, sqrt (eps));
256 
257 %!test
258 %! a = single ([1, 2, 3; 4, 5, 9; 7, 8, 6]);
259 %! [u, s] = schur (a);
260 %! assert (u' * a * u, s, sqrt (eps ("single")));
261 
262 %!error schur ()
263 %!error schur (1,2,3)
264 %!error [a,b,c] = schur (1)
265 %!error <must be a square matrix> schur ([1, 2, 3; 4, 5, 6])
266 %!error <wrong type argument 'cell'> schur ({1})
267 %!warning <incorrect ordered schur argument> schur ([1, 2; 3, 4], "bad_opt");
268 
269 */
270 
271 DEFUN (rsf2csf, args, nargout,
272  doc: /* -*- texinfo -*-
273 @deftypefn {} {[@var{U}, @var{T}] =} rsf2csf (@var{UR}, @var{TR})
274 Convert a real, upper quasi-triangular Schur@tie{}form @var{TR} to a
275 complex, upper triangular Schur@tie{}form @var{T}.
276 
277 Note that the following relations hold:
278 
279 @tex
280 $UR \cdot TR \cdot {UR}^T = U T U^{\dagger}$ and
281 $U^{\dagger} U$ is the identity matrix I.
282 @end tex
283 @ifnottex
284 @tcode{@var{UR} * @var{TR} * @var{UR}' = @var{U} * @var{T} * @var{U}'} and
285 @code{@var{U}' * @var{U}} is the identity matrix I.
286 @end ifnottex
287 
288 Note also that @var{U} and @var{T} are not unique.
289 @seealso{schur}
290 @end deftypefn */)
291 {
292  if (args.length () != 2 || nargout > 2)
293  print_usage ();
294 
295  if (! args(0).isnumeric ())
296  err_wrong_type_arg ("rsf2csf", args(0));
297  if (! args(1).isnumeric ())
298  err_wrong_type_arg ("rsf2csf", args(1));
299  if (args(0).iscomplex () || args(1).iscomplex ())
300  error ("rsf2csf: UR and TR must be real matrices");
301 
302  if (args(0).is_single_type () || args(1).is_single_type ())
303  {
304  FloatMatrix u = args(0).float_matrix_value ();
305  FloatMatrix t = args(1).float_matrix_value ();
306 
307  math::schur<FloatComplexMatrix> cs
309 
310  return ovl (cs.unitary_schur_matrix (), cs.schur_matrix ());
311  }
312  else
313  {
314  Matrix u = args(0).matrix_value ();
315  Matrix t = args(1).matrix_value ();
316 
317  math::schur<ComplexMatrix> cs
319 
320  return ovl (cs.unitary_schur_matrix (), cs.schur_matrix ());
321  }
322 }
323 
324 /*
325 %!test
326 %! A = [1, 1, 1, 2; 1, 2, 1, 1; 1, 1, 3, 1; -2, 1, 1, 1];
327 %! [u, t] = schur (A);
328 %! [U, T] = rsf2csf (u, t);
329 %! assert (norm (u * t * u' - U * T * U'), 0, 1e-12);
330 %! assert (norm (A - U * T * U'), 0, 1e-12);
331 
332 %!test
333 %! A = rand (10);
334 %! [u, t] = schur (A);
335 %! [U, T] = rsf2csf (u, t);
336 %! assert (norm (tril (T, -1)), 0);
337 %! assert (norm (U * U'), 1, 1e-14);
338 
339 %!test
340 %! A = [0, 1;-1, 0];
341 %! [u, t] = schur (A);
342 %! [U, T] = rsf2csf (u,t);
343 %! assert (U * T * U', A, 1e-14);
344 */
345 
346 OCTAVE_END_NAMESPACE(octave)
octave_idx_type rows() const
Definition: Array.h:459
octave_idx_type columns() const
Definition: Array.h:471
double element_type
Definition: Array.h:230
Definition: dMatrix.h:42
octave_idx_type rows() const
Definition: ov.h:545
bool isreal() const
Definition: ov.h:738
bool isnumeric() const
Definition: ov.h:750
ComplexMatrix complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:871
MatrixType matrix_type() const
Definition: ov.h:583
bool is_single_type() const
Definition: ov.h:698
FloatMatrix float_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:856
octave_idx_type columns() const
Definition: ov.h:547
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:853
FloatComplexMatrix float_complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:875
Definition: schur.h:47
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
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(const char *fmt,...)
Definition: error.cc:1063
void() error(const char *fmt,...)
Definition: error.cc:988
#define error_unless(cond)
Definition: error.h:530
void err_square_matrix_required(const char *fcn, const char *name)
Definition: errwarn.cc:122
void err_wrong_type_arg(const char *name, const char *s)
Definition: errwarn.cc:166
schur< FloatComplexMatrix > rsf2csf< FloatComplexMatrix, FloatMatrix >(const FloatMatrix &s_arg, const FloatMatrix &u_arg)
Definition: schur.cc:475
schur< ComplexMatrix > rsf2csf< ComplexMatrix, Matrix >(const Matrix &s_arg, const Matrix &u_arg)
Definition: schur.cc:363
octave_idx_type n
Definition: mx-inlines.cc:761
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:219
schur< RT > rsf2csf(const AT &s, const AT &u)