GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
schur.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1996-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 <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
42template <typename Matrix>
43static octave_value
44mark_upper_triangular (const Matrix& a)
45{
46 octave_value retval = a;
47
48 octave_idx_type n = a.rows ();
49 panic_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
62DEFUN (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
70Compute the Schur@tie{}decomposition of @var{A}.
71
72The 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
85where @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
92and @var{S} is upper triangular. The eigenvalues of @var{A} (and @var{S})
93are the diagonal elements of @var{S}. If the matrix @var{A} is real, then
94the real Schur@tie{}decomposition is computed, in which the matrix @var{U}
95is orthogonal and @var{S} is block upper triangular with blocks of size at
96most
97@tex
98$2 \times 2$
99@end tex
100@ifnottex
101@code{2 x 2}
102@end ifnottex
103along the diagonal.
104
105The default for real matrices is a real Schur@tie{}decomposition. A complex
106decomposition may be forced by passing the flag @qcode{"complex"}.
107
108The eigenvalues are optionally ordered along the diagonal according to the
109value of @var{opt}:
110
111@table @asis
112@item @qcode{@var{opt} = "a"}
113Move eigenvalues with negative real parts to the leading block of @var{S}.
114Mnemonic: @qcode{"a"} for Algebraic @nospell{Riccati} Equations, where this
115ordering is useful.
116
117@item @qcode{@var{opt} = "d"}
118Move eigenvalues with magnitude less than one to the leading block of @var{S}.
119Mnemonic: @qcode{"d"} for Discrete Algebraic @nospell{Riccati} Equations,
120where this ordering is useful.
121
122@item @qcode{@var{opt} = "u"}
123Unordered. No particular ordering of eigenvalues (default).
124@end table
125
126The leading @var{k} columns of @var{U} always span the @var{A}-invariant
127subspace 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 {
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
271DEFUN (rsf2csf, args, nargout,
272 doc: /* -*- texinfo -*-
273@deftypefn {} {[@var{U}, @var{T}] =} rsf2csf (@var{UR}, @var{TR})
274Convert a real, upper quasi-triangular Schur@tie{}form @var{TR} to a
275complex, upper triangular Schur@tie{}form @var{T}.
276
277Note 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
288Note 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
308 = math::rsf2csf<FloatComplexMatrix, FloatMatrix> (t, u);
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
318 = math::rsf2csf<ComplexMatrix, Matrix> (t, u);
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
346OCTAVE_END_NAMESPACE(octave)
octave_idx_type rows() const
Definition Array.h:463
octave_idx_type columns() const
Definition Array.h:475
T element_type
Definition Array.h:234
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:877
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:862
octave_idx_type length() const
octave_idx_type columns() const
Definition ov.h:547
Matrix matrix_value(bool frc_str_conv=false) const
Definition ov.h:859
FloatComplexMatrix float_complex_matrix_value(bool frc_str_conv=false) const
Definition ov.h:881
Definition schur.h:46
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
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(const char *fmt,...)
Definition error.cc:1078
void error(const char *fmt,...)
Definition error.cc:1003
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
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217
#define panic_unless(cond)
Definition panic.h:59
schur< RT > rsf2csf(const AT &s, const AT &u)