|
1 | | -%% Copyright (C) 2014, 2016 Colin B. Macdonald |
| 1 | +%% Copyright (C) 2014, 2016-2017 Colin B. Macdonald |
2 | 2 | %% |
3 | 3 | %% This file is part of OctSymPy. |
4 | 4 | %% |
|
20 | 20 | %% @documentencoding UTF-8 |
21 | 21 | %% @deftypemethod @@sym {@var{S} =} svd (@var{A}) |
22 | 22 | %% @deftypemethodx @@sym {[@var{U}, @var{S}, @var{V}] =} svd (@var{A}) |
| 23 | +%% @deftypemethodx @@sym {[@var{U}, @var{S}, @var{V}] =} svd (@var{A}, 'econ') |
23 | 24 | %% Symbolic singular value decomposition. |
24 | 25 | %% |
25 | | -%% The SVD: U*S*V' = A |
| 26 | +%% The SVD is the factorization of matrix |
| 27 | +%% @iftex |
| 28 | +%% @math{A} into @math{U S V^T = A}, |
| 29 | +%% where @math{S} is a diagonal matrix of @emph{singular values}, |
| 30 | +%% and @math{U} and @math{V} |
| 31 | +%% @end iftex |
| 32 | +%% @ifnottex |
| 33 | +%% A into U*S*V' = A, |
| 34 | +%% where S is a diagonal matrix of @emph{singular values}, |
| 35 | +%% and U and V |
| 36 | +%% @end ifnottex |
| 37 | +%% are orthogonal matrices whose columns form the left and right |
| 38 | +%% @emph{singular vectors}. |
26 | 39 | %% |
27 | | -%% Singular values example: |
| 40 | +%% When the matrix contains symbols, expressions, rational numbers, and other |
| 41 | +%% things, this command finds the singular values via symbolic manipulation: |
28 | 42 | %% @example |
29 | 43 | %% @group |
30 | 44 | %% A = sym([1 0; 3 0]); |
|
37 | 51 | %% |
38 | 52 | %% @end group |
39 | 53 | %% @end example |
| 54 | +%% Currently only the singular values (not the singular vectors) are supported |
| 55 | +%% in symbolic mode. |
40 | 56 | %% |
41 | | -%% FIXME: currently only singular values, not singular vectors. |
42 | | -%% Should add full SVD to sympy. |
43 | 57 | %% |
| 58 | +%% If the matrix contains Float entries (@pxref{vpa}) (and possibly Integers), |
| 59 | +%% the SVD is computing numerically in variable precision |
| 60 | +%% arithmetic in the precision given by @pxref{digits}. |
| 61 | +%% The singular values and singular vectors can be computed in this mode. |
| 62 | +%% Example: |
| 63 | +%% @example |
| 64 | +%% @group |
| 65 | +%% A = vpa (3*hilb (sym(3))); |
| 66 | +%% [U, S, V] = svd (A) |
| 67 | +%% @result{} U = (sym 3×3 matrix) |
| 68 | +%% ... |
| 69 | +%% @result{} S = (sym 3×3 matrix) |
| 70 | +%% ... |
| 71 | +%% @result{} V = (sym 3×3 matrix) |
| 72 | +%% ... |
| 73 | +%% @end group |
| 74 | +%% |
| 75 | +%% @group |
| 76 | +%% diag(S) |
| 77 | +%% @result{} (sym 3×1 matrix) |
| 78 | +%% ⎡ 4.2249567813709618726124675083017 ⎤ |
| 79 | +%% ⎢ ⎥ |
| 80 | +%% ⎢ 0.3669811975617175396944034278698 ⎥ |
| 81 | +%% ⎢ ⎥ |
| 82 | +%% ⎣0.008062021067320587693129063828546⎦ |
| 83 | +%% @end group |
| 84 | +%% @end example |
| 85 | +%% |
| 86 | +%% Next, extract one singular value and associated left/right |
| 87 | +%% singular vectors: |
| 88 | +%% @example |
| 89 | +%% @group |
| 90 | +%% sv = S(1, 1) |
| 91 | +%% u = U(:, 1) |
| 92 | +%% v = V(:, 1) |
| 93 | +%% @result{} sv = (sym) 4.2249567813709618726124675083017 |
| 94 | +%% @result{} u = (sym 3×1 matrix) |
| 95 | +%% ⎡-0.82704492697200940922027703647284⎤ |
| 96 | +%% ⎢ ⎥ |
| 97 | +%% ⎢-0.45986390436554392104852568981886⎥ |
| 98 | +%% ⎢ ⎥ |
| 99 | +%% ⎣-0.32329843524449897629157179151973⎦ |
| 100 | +%% @result{} v = (sym 3×1 matrix) |
| 101 | +%% ⎡-0.82704492697200940922027703647284⎤ |
| 102 | +%% ⎢ ⎥ |
| 103 | +%% ⎢-0.45986390436554392104852568981886⎥ |
| 104 | +%% ⎢ ⎥ |
| 105 | +%% ⎣-0.32329843524449897629157179151973⎦ |
| 106 | +%% @end group |
| 107 | +%% @end example |
| 108 | +%% |
| 109 | +%% Check the SVD is satisfied to high-precision: |
| 110 | +%% @example |
| 111 | +%% @group |
| 112 | +%% sv*u - A*v |
| 113 | +%% @result{} (sym 3×1 matrix) |
| 114 | +%% ⎡-9.2444637330587320946686941244077e-33⎤ |
| 115 | +%% ⎢ ⎥ |
| 116 | +%% ⎢-3.0814879110195773648895647081359e-33⎥ |
| 117 | +%% ⎢ ⎥ |
| 118 | +%% ⎣-3.0814879110195773648895647081359e-33⎦ |
| 119 | +%% @end group |
| 120 | +%% @end example |
| 121 | +%% |
| 122 | +%% If the @qcode{'econ'} keyboard is passed, an ``economy size'' |
| 123 | +%% SVD is returned (@pxref{svd}). |
44 | 124 | %% @seealso{svd, @@sym/eig} |
45 | 125 | %% @end deftypemethod |
46 | 126 |
|
47 | 127 |
|
48 | | -function [S, varargout] = svd(A) |
| 128 | +function [S, varargout] = svd(A, econ) |
49 | 129 |
|
50 | | - if (nargin >= 2) |
51 | | - error('svd: economy-size not supported yet') |
| 130 | + if (nargin == 1) |
| 131 | + econ = false; |
| 132 | + elseif (nargin == 2) |
| 133 | + if (isnumeric(econ) && econ == 0) |
| 134 | + error('svd: auto econ mode ("0") is not yet supported') |
| 135 | + else |
| 136 | + econ = true; |
| 137 | + end |
| 138 | + else |
| 139 | + print_usage (); |
52 | 140 | end |
53 | 141 |
|
54 | | - if (nargout >= 2) |
55 | | - error('svd: singular vectors not yet computed by sympy') |
| 142 | + if (nargout <= 1) |
| 143 | + svecs = false; |
| 144 | + elseif (nargout == 3) |
| 145 | + svecs = true; |
| 146 | + else |
| 147 | + print_usage (); |
56 | 148 | end |
57 | 149 |
|
| 150 | + |
| 151 | + cmd = { 'A, = _ins' |
| 152 | + 'A = A if A.is_Matrix else Matrix([A])' |
| 153 | + 'return (any([x.is_Float for x in A]) and' |
| 154 | + ' all([x.is_Float or x.is_Integer for x in A]))' }; |
| 155 | + is_vpa_matrix = python_cmd (cmd, sym(A)); |
| 156 | + |
| 157 | + if (is_vpa_matrix) |
| 158 | + myd = digits (); % TODO: or take from the object itself |
| 159 | + cmd = { '(A, svecs, econ, digits) = _ins' |
| 160 | + 'A = A if A.is_Matrix else Matrix([A])' |
| 161 | + 'import mpmath' |
| 162 | + 'mpmath.mp.dps = digits' |
| 163 | + 'tmp = mpmath.svd(mpmath.matrix(A), full_matrices=(not econ), compute_uv=svecs)' |
| 164 | + 'if svecs:' |
| 165 | + ' (U, S, Vt) = tmp' |
| 166 | + ' U = Matrix(U.rows, U.cols, lambda i,j: U[i, j])' |
| 167 | + ' S = Matrix(S)' |
| 168 | + ' m, n = A.shape' |
| 169 | + ' r, c = (m, n) if not econ else (min(m,n),)*2' |
| 170 | + ' S = Matrix(r, c, lambda i,j: S[i] if i == j else 0)' |
| 171 | + ' V = Vt.transpose()' % TODO: or transpose_conj? |
| 172 | + ' V = Matrix(V.rows, V.cols, lambda i,j: V[i, j])' |
| 173 | + 'else:' |
| 174 | + ' S = Matrix(tmp)' |
| 175 | + ' U, V = None, None' |
| 176 | + 'return (U, S, V)' }; |
| 177 | + [U, S, V] = python_cmd (cmd, sym(A), svecs, econ, myd); |
| 178 | + |
| 179 | + if (nargout >= 2) |
| 180 | + varargout{1} = S; |
| 181 | + varargout{2} = V; |
| 182 | + S = U; |
| 183 | + end |
| 184 | + |
| 185 | + else |
| 186 | + if (svecs) |
| 187 | + error ('svd: singular vectors not yet implemented for non-vpa matrices') |
| 188 | + end |
| 189 | + if (econ) |
| 190 | + error ('svd: "economy size" not yet implemented for non-vpa matrices') |
| 191 | + end |
| 192 | + |
58 | 193 | cmd = { '(A,) = _ins' |
59 | 194 | 'if not A.is_Matrix:' |
60 | 195 | ' A = sp.Matrix([A])' |
61 | 196 | 'L = sp.Matrix(A.singular_values())' |
62 | 197 | 'return L,' }; |
63 | 198 |
|
64 | 199 | S = python_cmd (cmd, sym(A)); |
65 | | - |
| 200 | + end |
66 | 201 | end |
67 | 202 |
|
68 | 203 |
|
| 204 | +%!error <Invalid> svd (sym(1), 2, 3) |
| 205 | +%!error <Invalid> [a, b] = svd (sym(1)) |
| 206 | + |
69 | 207 | %!test |
70 | 208 | %! % basic |
71 | 209 | %! A = [1 2; 3 4]; |
|
99 | 237 | %%! A = [x 0; sym(0) 2*x] |
100 | 238 | %%! [u,s,v] = cond(A) |
101 | 239 | %%! assert (false) |
| 240 | + |
| 241 | +%!test |
| 242 | +%! % econ & non-square matrices |
| 243 | +%! A = vpa([1 2 4; 1 2 4]); |
| 244 | +%! S = svd (A); |
| 245 | +%! assert (size (S), [2 1]) |
| 246 | +%! [U, S, V] = svd (A); |
| 247 | +%! assert (size (U), [2 2]) |
| 248 | +%! assert (size (S), [2 3]) |
| 249 | +%! assert (size (V), [3 3]) |
| 250 | +%! [U, S, V] = svd (A, 'econ'); |
| 251 | +%! assert (size (U), [2 2]) |
| 252 | +%! assert (size (S), [2 2]) |
| 253 | +%! assert (size (V), [3 2]) |
| 254 | +%! A = A'; |
| 255 | +%! S = svd (A); |
| 256 | +%! assert (size (S), [2 1]) |
| 257 | +%! [U, S, V] = svd (A, 'econ'); |
| 258 | +%! assert (size (U), [3 2]) |
| 259 | +%! assert (size (S), [2 2]) |
| 260 | +%! assert (size (V), [2 2]) |
| 261 | +%! [U, S, V] = svd (A); |
| 262 | +%! assert (size (U), [3 3]) |
| 263 | +%! assert (size (S), [3 2]) |
| 264 | +%! assert (size (V), [2 2]) |
0 commit comments