GNU Octave 7.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
schur.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1996-2022 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
40OCTAVE_NAMESPACE_BEGIN
41
42template <typename Matrix>
43static octave_value
45{
46 octave_value retval = a;
47
48 octave_idx_type n = a.rows ();
49 assert (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 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. The diagonal elements of @var{S}
104(or the eigenvalues of the
105@tex
106$2 \times 2$
107@end tex
108@ifnottex
109@code{2 x 2}
110@end ifnottex
111blocks, when appropriate) are the eigenvalues of @var{A} and @var{S}.
112
113The default for real matrices is a real Schur@tie{}decomposition.
114A complex decomposition may be forced by passing the flag
115@qcode{"complex"}.
116
117The eigenvalues are optionally ordered along the diagonal according to the
118value of @var{opt}. @code{@var{opt} = "a"} indicates that all eigenvalues
119with negative real parts should be moved to the leading block of @var{S}
120(used in @code{are}), @code{@var{opt} = "d"} indicates that all
121eigenvalues with magnitude less than one should be moved to the leading
122block of @var{S} (used in @code{dare}), and @code{@var{opt} = "u"}, the
123default, indicates that no ordering of eigenvalues should occur. The
124leading @var{k} columns of @var{U} always span the @var{A}-invariant
125subspace corresponding to the @var{k} leading eigenvalues of @var{S}.
126
127The Schur@tie{}decomposition is used to compute eigenvalues of a square
128matrix, and has applications in the solution of algebraic @nospell{Riccati}
129equations in control (see @code{are} and @code{dare}).
130@seealso{rsf2csf, ordschur, ordeig, lu, chol, hess, qr, qz, svd}
131@end deftypefn */)
132{
133 int nargin = args.length ();
134
135 if (nargin < 1 || nargin > 2 || nargout > 2)
136 print_usage ();
137
138 octave_value arg = args(0);
139
140 std::string ord;
141 if (nargin == 2)
142 ord = args(1).xstring_value ("schur: second argument must be a string");
143
144 bool force_complex = false;
145
146 if (ord == "real")
147 {
148 ord = "";
149 }
150 else if (ord == "complex")
151 {
152 force_complex = true;
153 ord = "";
154 }
155 else
156 {
157 char ord_char = (ord.empty () ? 'U' : ord[0]);
158
159 if (ord_char != 'U' && ord_char != 'A' && ord_char != 'D'
160 && ord_char != 'u' && ord_char != 'a' && ord_char != 'd')
161 {
162 warning ("schur: incorrect ordered schur argument '%s'",
163 ord.c_str ());
164 return ovl ();
165 }
166 }
167
168 octave_idx_type nr = arg.rows ();
169 octave_idx_type nc = arg.columns ();
170
171 if (nr != nc)
172 err_square_matrix_required ("schur", "A");
173
174 if (! arg.isnumeric ())
175 err_wrong_type_arg ("schur", arg);
176
177 octave_value_list retval;
178
179 if (arg.is_single_type ())
180 {
181 if (! force_complex && arg.isreal ())
182 {
183 FloatMatrix tmp = arg.float_matrix_value ();
184
185 if (nargout <= 1)
186 {
187 math::schur<FloatMatrix> result (tmp, ord, false);
188 retval = ovl (result.schur_matrix ());
189 }
190 else
191 {
192 math::schur<FloatMatrix> result (tmp, ord, true);
193 retval = ovl (result.unitary_schur_matrix (),
194 result.schur_matrix ());
195 }
196 }
197 else
198 {
200
201 if (nargout <= 1)
202 {
203 math::schur<FloatComplexMatrix> result (ctmp, ord, false);
204 retval = ovl (mark_upper_triangular (result.schur_matrix ()));
205 }
206 else
207 {
208 math::schur<FloatComplexMatrix> result (ctmp, ord, true);
209 retval = ovl (result.unitary_schur_matrix (),
210 mark_upper_triangular (result.schur_matrix ()));
211 }
212 }
213 }
214 else
215 {
216 if (! force_complex && arg.isreal ())
217 {
218 Matrix tmp = arg.matrix_value ();
219
220 if (nargout <= 1)
221 {
222 math::schur<Matrix> result (tmp, ord, false);
223 retval = ovl (result.schur_matrix ());
224 }
225 else
226 {
227 math::schur<Matrix> result (tmp, ord, true);
228 retval = ovl (result.unitary_schur_matrix (),
229 result.schur_matrix ());
230 }
231 }
232 else
233 {
235
236 if (nargout <= 1)
237 {
238 math::schur<ComplexMatrix> result (ctmp, ord, false);
239 retval = ovl (mark_upper_triangular (result.schur_matrix ()));
240 }
241 else
242 {
243 math::schur<ComplexMatrix> result (ctmp, ord, true);
244 retval = ovl (result.unitary_schur_matrix (),
245 mark_upper_triangular (result.schur_matrix ()));
246 }
247 }
248 }
249
250 return retval;
251}
252
253/*
254%!test
255%! a = [1, 2, 3; 4, 5, 9; 7, 8, 6];
256%! [u, s] = schur (a);
257%! assert (u' * a * u, s, sqrt (eps));
258
259%!test
260%! a = single ([1, 2, 3; 4, 5, 9; 7, 8, 6]);
261%! [u, s] = schur (a);
262%! assert (u' * a * u, s, sqrt (eps ("single")));
263
264%!error schur ()
265%!error schur (1,2,3)
266%!error [a,b,c] = schur (1)
267%!error <must be a square matrix> schur ([1, 2, 3; 4, 5, 6])
268%!error <wrong type argument 'cell'> schur ({1})
269%!warning <incorrect ordered schur argument> schur ([1, 2; 3, 4], "bad_opt");
270
271*/
272
273DEFUN (rsf2csf, args, nargout,
274 doc: /* -*- texinfo -*-
275@deftypefn {} {[@var{U}, @var{T}] =} rsf2csf (@var{UR}, @var{TR})
276Convert a real, upper quasi-triangular Schur@tie{}form @var{TR} to a
277complex, upper triangular Schur@tie{}form @var{T}.
278
279Note that the following relations hold:
280
281@tex
282$UR \cdot TR \cdot {UR}^T = U T U^{\dagger}$ and
283$U^{\dagger} U$ is the identity matrix I.
284@end tex
285@ifnottex
286@tcode{@var{UR} * @var{TR} * @var{UR}' = @var{U} * @var{T} * @var{U}'} and
287@code{@var{U}' * @var{U}} is the identity matrix I.
288@end ifnottex
289
290Note also that @var{U} and @var{T} are not unique.
291@seealso{schur}
292@end deftypefn */)
293{
294 if (args.length () != 2 || nargout > 2)
295 print_usage ();
296
297 if (! args(0).isnumeric ())
298 err_wrong_type_arg ("rsf2csf", args(0));
299 if (! args(1).isnumeric ())
300 err_wrong_type_arg ("rsf2csf", args(1));
301 if (args(0).iscomplex () || args(1).iscomplex ())
302 error ("rsf2csf: UR and TR must be real matrices");
303
304 if (args(0).is_single_type () || args(1).is_single_type ())
305 {
306 FloatMatrix u = args(0).float_matrix_value ();
307 FloatMatrix t = args(1).float_matrix_value ();
308
309 math::schur<FloatComplexMatrix> cs
311
312 return ovl (cs.unitary_schur_matrix (), cs.schur_matrix ());
313 }
314 else
315 {
316 Matrix u = args(0).matrix_value ();
317 Matrix t = args(1).matrix_value ();
318
319 math::schur<ComplexMatrix> cs
321
322 return ovl (cs.unitary_schur_matrix (), cs.schur_matrix ());
323 }
324}
325
326/*
327%!test
328%! A = [1, 1, 1, 2; 1, 2, 1, 1; 1, 1, 3, 1; -2, 1, 1, 1];
329%! [u, t] = schur (A);
330%! [U, T] = rsf2csf (u, t);
331%! assert (norm (u * t * u' - U * T * U'), 0, 1e-12);
332%! assert (norm (A - U * T * U'), 0, 1e-12);
333
334%!test
335%! A = rand (10);
336%! [u, t] = schur (A);
337%! [U, T] = rsf2csf (u, t);
338%! assert (norm (tril (T, -1)), 0);
339%! assert (norm (U * U'), 1, 1e-14);
340
341%!test
342%! A = [0, 1;-1, 0];
343%! [u, t] = schur (A);
344%! [U, T] = rsf2csf (u,t);
345%! assert (U * T * U', A, 1e-14);
346*/
347
348OCTAVE_NAMESPACE_END
octave_idx_type rows(void) const
Definition: Array.h:449
octave_idx_type columns(void) const
Definition: Array.h:458
double element_type
Definition: Array.h:229
Definition: dMatrix.h:42
MatrixType matrix_type(void) const
Definition: ov.h:628
bool isreal(void) const
Definition: ov.h:783
ComplexMatrix complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:916
octave_idx_type rows(void) const
Definition: ov.h:590
bool isnumeric(void) const
Definition: ov.h:795
octave_idx_type columns(void) const
Definition: ov.h:592
FloatMatrix float_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:901
bool is_single_type(void) const
Definition: ov.h:743
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:898
FloatComplexMatrix float_complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:920
Definition: mx-defs.h:47
OCTINTERP_API 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:1055
void error(const char *fmt,...)
Definition: error.cc:980
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
static OCTAVE_NAMESPACE_BEGIN octave_value mark_upper_triangular(const Matrix &a)
Definition: schur.cc:44
OCTAVE_API schur< RT > rsf2csf(const AT &s, const AT &u)
OCTAVE_API schur< FloatComplexMatrix > rsf2csf< FloatComplexMatrix, FloatMatrix >(const FloatMatrix &s_arg, const FloatMatrix &u_arg)
Definition: schur.cc:475
OCTAVE_API schur< ComplexMatrix > rsf2csf< ComplexMatrix, Matrix >(const Matrix &s_arg, const Matrix &u_arg)
Definition: schur.cc:363
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211