GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
pinv.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 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 "defun.h"
31 #include "error.h"
32 #include "errwarn.h"
33 #include "ovl.h"
34 #include "ops.h"
35 #include "ov-re-diag.h"
36 #include "ov-cx-diag.h"
37 #include "ov-flt-re-diag.h"
38 #include "ov-flt-cx-diag.h"
39 #include "ov-perm.h"
40 
41 DEFUN (pinv, args, ,
42  doc: /* -*- texinfo -*-
43 @deftypefn {} {} pinv (@var{x})
44 @deftypefnx {} {} pinv (@var{x}, @var{tol})
45 Return the @nospell{Moore-Penrose} pseudoinverse of @var{x}.
46 
47 Singular values less than @var{tol} are ignored.
48 
49 If the second argument is omitted, it is taken to be
50 
51 @example
52 tol = max ([rows(@var{x}), columns(@var{x})]) * norm (@var{x}) * eps
53 @end example
54 
55 @seealso{inv, ldivide}
56 @end deftypefn */)
57 {
58  int nargin = args.length ();
59 
60  if (nargin < 1 || nargin > 2)
61  print_usage ();
62 
63  octave_value arg = args(0);
64 
65  if (arg.isempty ())
66  return ovl (Matrix ());
67 
69 
70  bool isfloat = arg.is_single_type ();
71 
72  if (arg.is_diag_matrix ())
73  {
74  if (isfloat)
75  {
76  float tol = 0.0;
77  if (nargin == 2)
78  tol = args(1).float_value ();
79 
80  if (tol < 0.0)
81  error ("pinv: TOL must be greater than zero");
82 
83  if (arg.isreal ())
85  else
87  }
88  else
89  {
90  double tol = 0.0;
91  if (nargin == 2)
92  tol = args(1).double_value ();
93 
94  if (tol < 0.0)
95  error ("pinv: TOL must be greater than zero");
96 
97  if (arg.isreal ())
99  else
101  }
102  }
103  else if (arg.is_perm_matrix ())
104  {
105  retval = arg.perm_matrix_value ().inverse ();
106  }
107  else if (isfloat)
108  {
109  float tol = 0.0;
110  if (nargin == 2)
111  tol = args(1).float_value ();
112 
113  if (tol < 0.0)
114  error ("pinv: TOL must be greater than zero");
115 
116  if (arg.isreal ())
117  {
119 
120  retval = m.pseudo_inverse (tol);
121  }
122  else if (arg.iscomplex ())
123  {
125 
126  retval = m.pseudo_inverse (tol);
127  }
128  else
129  err_wrong_type_arg ("pinv", arg);
130  }
131  else
132  {
133  double tol = 0.0;
134  if (nargin == 2)
135  tol = args(1).double_value ();
136 
137  if (tol < 0.0)
138  error ("pinv: TOL must be greater than zero");
139 
140  if (arg.isreal ())
141  {
142  Matrix m = arg.matrix_value ();
143 
144  retval = m.pseudo_inverse (tol);
145  }
146  else if (arg.iscomplex ())
147  {
149 
150  retval = m.pseudo_inverse (tol);
151  }
152  else
153  err_wrong_type_arg ("pinv", arg);
154  }
155 
156  return retval;
157 }
158 
159 /*
160 %!shared a, b, tol, hitol, d, u, x, y
161 %! old_state = rand ("state");
162 %! restore_state = onCleanup (@() rand ("state", old_state));
163 %! rand ("state", 42); # initialize generator to make behavior reproducible
164 %! a = reshape (rand*[1:16], 4, 4); # Rank 2 matrix
165 %! b = pinv (a);
166 %! tol = 4e-14;
167 %! hitol = 40*sqrt (eps);
168 %! d = diag ([rand, rand, hitol, hitol]);
169 %! u = rand (4); # Could be singular by freak accident
170 %! x = inv (u)*d*u;
171 %! y = pinv (x, sqrt (eps));
172 
173 ## Verify Penrose conditions for pseudoinverse
174 %!assert (a*b*a, a, tol)
175 %!assert (b*a*b, b, tol)
176 %!assert ((b*a)', b*a, tol)
177 %!assert ((a*b)', a*b, tol)
178 %!assert (x*y*x, x, -hitol)
179 %!assert (y*x*y, y, -hitol)
180 %!assert ((x*y)', x*y, hitol)
181 %!assert ((y*x)', y*x, hitol)
182 
183 ## Clear shared variables
184 %!shared
185 
186 ## Test pinv for Diagonal matrices
187 %!test
188 %! x = diag ([3 2 1 0 -0.5]);
189 %! y = pinv (x);
190 %! assert (typeinfo (y)(1:8), "diagonal");
191 %! assert (isa (y, "double"));
192 %! assert (diag (y), [1/3, 1/2, 1, 0 1/-0.5]');
193 %! y = pinv (x, 1);
194 %! assert (diag (y), [1/3 1/2 1 0 0]');
195 %! y = pinv (x, 2);
196 %! assert (diag (y), [1/3 1/2 0 0 0]');
197 
198 ## Test special case of 0 scalars and vectors
199 %!assert (pinv (0), 0)
200 %!assert (pinv ([0, 0, 0]), [0; 0; 0])
201 %!assert (pinv (single (0)), single (0))
202 %!assert (pinv (single ([0, 0, 0])), single ([0; 0; 0]))
203 %!assert (pinv (complex (0,0)), 0)
204 %!assert (pinv (complex ([0,0,0], [0,0,0])), [0; 0; 0])
205 %!assert (pinv (complex (single (0),0)), single (0))
206 %!assert (pinv (complex (single ([0,0,0]), [0,0,0])), single ([0; 0; 0]))
207 */
ComplexDiagMatrix pseudo_inverse(double tol=0.0) const
Definition: CDiagMatrix.cc:356
DiagMatrix pseudo_inverse(double tol=0.0) const
Definition: dDiagMatrix.cc:279
FloatComplexDiagMatrix pseudo_inverse(float tol=0.0f) const
FloatDiagMatrix pseudo_inverse(float tol=0.0f) const
Definition: fDiagMatrix.cc:257
Definition: dMatrix.h:42
PermMatrix inverse(void) const
Definition: PermMatrix.cc:114
bool isreal(void) const
Definition: ov.h:691
DiagMatrix diag_matrix_value(bool force=false) const
Definition: ov.h:863
FloatDiagMatrix float_diag_matrix_value(bool force=false) const
Definition: ov.h:866
ComplexMatrix complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:824
FloatComplexDiagMatrix float_complex_diag_matrix_value(bool force=false) const
Definition: ov.h:873
bool is_diag_matrix(void) const
Definition: ov.h:587
ComplexDiagMatrix complex_diag_matrix_value(bool force=false) const
Definition: ov.h:869
FloatMatrix float_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:809
PermMatrix perm_matrix_value(void) const
Definition: ov.h:876
bool isempty(void) const
Definition: ov.h:557
bool is_single_type(void) const
Definition: ov.h:651
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:806
bool is_perm_matrix(void) const
Definition: ov.h:590
FloatComplexMatrix float_complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:828
bool iscomplex(void) const
Definition: ov.h:694
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void error(const char *fmt,...)
Definition: error.cc:968
void err_wrong_type_arg(const char *name, const char *s)
Definition: errwarn.cc:166
T octave_idx_type m
Definition: mx-inlines.cc:773
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211