comparison scripts/linear-algebra/condest.m @ 28158:687d452070c9

condest.m: Overhaul function. Change algorithm to compute inverse matrix just once and cache result in presistent variable. * condest.m: Add more calling forms to documentation (they were always there but not shown explicitly). Rename "apply" to "Afcn" for clarity. Rename "solvefun" to "Ainvfcn" for clarity. Capitalize @var{A} in documentation. Correct documentation about what Ainvfcn must return. Add @seealso link to rcond. Remove "Author:", "Keywords:", "Version:" comments. Rename "have_apply_normest1" to "have_Afcn" for clarity. Rename "have_solve_normest1" to "have_Ainvfcn" for clarity. Rename BIST functions apply_fun, solve_fun to "__Afcn__" and "__Ainvfcn__" with internal prefix so they do not clash with other functions. Update BIST tests for new names. * condest.m (inv_sparse_fcn): Rename from "solve_sparse". Create persistent variables Ainv, Ainvt, n, isreal_op. Add case "init" to switch statement to initialize persistent variables just once per session. Add case "clear" to switch statement to clear persistent variables from memory. * condest.m (inv_full_fcn): Rename from "solve_not_sparse". Create persistent variables Ainv, Ainvt, n, isreal_op. Add case "init" to switch statement to initialize persistent variables just once per session. Add case "clear" to switch statement to clear persistent variables from memory.
author Rik <rik@octave.org>
date Fri, 13 Mar 2020 10:46:46 -0700
parents 4609d001daee
children a01ad9893641
comparison
equal deleted inserted replaced
28157:884a9f759eaa 28158:687d452070c9
24 ######################################################################## 24 ########################################################################
25 25
26 ## -*- texinfo -*- 26 ## -*- texinfo -*-
27 ## @deftypefn {} {@var{cest} =} condest (@var{A}) 27 ## @deftypefn {} {@var{cest} =} condest (@var{A})
28 ## @deftypefnx {} {@var{cest} =} condest (@var{A}, @var{t}) 28 ## @deftypefnx {} {@var{cest} =} condest (@var{A}, @var{t})
29 ## @deftypefnx {} {@var{cest} =} condest (@var{A}, @var{solvefun}, @var{t}, @var{p1}, @var{p2}, @dots{}) 29 ## @deftypefnx {} {@var{cest} =} condest (@var{A}, @var{Ainvfcn})
30 ## @deftypefnx {} {@var{cest} =} condest (@var{Afcn}, @var{solvefun}, @var{t}, @var{p1}, @var{p2}, @dots{}) 30 ## @deftypefnx {} {@var{cest} =} condest (@var{A}, @var{Ainvfcn}, @var{t})
31 ## @deftypefnx {} {@var{cest} =} condest (@var{A}, @var{Ainvfcn}, @var{t}, @var{p1}, @var{p2}, @dots{})
32 ## @deftypefnx {} {@var{cest} =} condest (@var{Afcn}, @var{Ainvfcn})
33 ## @deftypefnx {} {@var{cest} =} condest (@var{Afcn}, @var{Ainvfcn}, @var{t})
34 ## @deftypefnx {} {@var{cest} =} condest (@var{Afcn}, @var{Ainvfcn}, @var{t}, @var{p1}, @var{p2}, @dots{})
31 ## @deftypefnx {} {[@var{cest}, @var{v}] =} condest (@dots{}) 35 ## @deftypefnx {} {[@var{cest}, @var{v}] =} condest (@dots{})
32 ## 36 ##
33 ## Estimate the 1-norm condition number of a square matrix @var{A} using 37 ## Estimate the 1-norm condition number of a square matrix @var{A} using
34 ## @var{t} test vectors and a randomized 1-norm estimator. 38 ## @var{t} test vectors and a randomized 1-norm estimator.
35 ## 39 ##
36 ## The optional input @var{t} specifies the number of test vectors (default 5). 40 ## The optional input @var{t} specifies the number of test vectors (default 5).
37 ## 41 ##
38 ## If the matrix is not explicit, e.g., when estimating the condition number of 42 ## The input may be a matrix @var{A} (the algorithm is particularly
39 ## @var{A} given an LU@tie{}factorization, @code{condest} uses the following 43 ## appropriate for large, sparse matrices). Alternatively, the behavior of
40 ## functions: 44 ## the matrix can be defined implicitly by functions. When using an implicit
45 ## definition, @code{condest} requires the following functions:
41 ## 46 ##
42 ## @itemize @minus 47 ## @itemize @minus
43 ## @item @var{Afcn} which must return 48 ## @item @code{@var{Afcn} (@var{flag}, @var{x})} which must return
44 ## 49 ##
45 ## @itemize @bullet 50 ## @itemize @bullet
46 ## @item 51 ## @item
47 ## the dimension @var{n} of @var{a}, if @var{flag} is @qcode{"dim"} 52 ## the dimension @var{n} of @var{A}, if @var{flag} is @qcode{"dim"}
48 ## 53 ##
49 ## @item 54 ## @item
50 ## true if @var{a} is a real operator, if @var{flag} is @qcode{"real"} 55 ## true if @var{A} is a real operator, if @var{flag} is @qcode{"real"}
51 ## 56 ##
52 ## @item 57 ## @item
53 ## the result @code{@var{a} * @var{x}}, if @var{flag} is "notransp" 58 ## the result @code{@var{A} * @var{x}}, if @var{flag} is "notransp"
54 ## 59 ##
55 ## @item 60 ## @item
56 ## the result @code{@var{a}' * @var{x}}, if @var{flag} is "transp" 61 ## the result @code{@var{A}' * @var{x}}, if @var{flag} is "transp"
57 ## @end itemize 62 ## @end itemize
58 ## 63 ##
59 ## @item @var{solvefun} which must return 64 ## @item @code{@var{Ainvfcn} (@var{flag}, @var{x})} which must return
60 ## 65 ##
61 ## @itemize @bullet 66 ## @itemize @bullet
62 ## @item 67 ## @item
63 ## the dimension @var{n} of @var{a}, if @var{flag} is @qcode{"dim"} 68 ## the dimension @var{n} of @code{inv (@var{A})}, if @var{flag} is
64 ## 69 ## @qcode{"dim"}
65 ## @item 70 ##
66 ## true if @var{a} is a real operator, if @var{flag} is @qcode{"real"} 71 ## @item
67 ## 72 ## true if @code{inv (@var{A})} is a real operator, if @var{flag} is
68 ## @item 73 ## @qcode{"real"}
69 ## the result @code{@var{a} \ @var{x}}, if @var{flag} is "notransp" 74 ##
70 ## 75 ## @item
71 ## @item 76 ## the result @code{inv (@var{A}) * @var{x}}, if @var{flag} is "notransp"
72 ## the result @code{@var{a}' \ @var{x}}, if @var{flag} is "transp" 77 ##
78 ## @item
79 ## the result @code{inv (@var{A})' * @var{x}}, if @var{flag} is "transp"
73 ## @end itemize 80 ## @end itemize
74 ## @end itemize 81 ## @end itemize
75 ## 82 ##
76 ## The parameters @var{p1}, @var{p2}, @dots{} are arguments of 83 ## Any parameters @var{p1}, @var{p2}, @dots{} are additional arguments of
77 ## @code{@var{Afcn} (@var{flag}, @var{x}, @var{p1}, @var{p2}, @dots{})} 84 ## @code{@var{Afcn} (@var{flag}, @var{x}, @var{p1}, @var{p2}, @dots{})}
78 ## and @code{@var{solvefcn} (@var{flag}, @var{x}, @var{p1}, @var{p2}, 85 ## and @code{@var{Ainvfcn} (@var{flag}, @var{x}, @var{p1}, @var{p2}, @dots{})}.
79 ## @dots{})}.
80 ## 86 ##
81 ## The principal output is the 1-norm condition number estimate @var{cest}. 87 ## The principal output is the 1-norm condition number estimate @var{cest}.
82 ## 88 ##
83 ## The optional second output is an approximate null vector when @var{cest} is 89 ## The optional second output @var{v} is an approximate null vector; it
84 ## large; it satisfies the equation 90 ## satisfies the equation @code{norm (@var{A}*@var{v}, 1) ==
85 ## @code{norm (A*v, 1) == norm (A, 1) * norm (@var{v}, 1) / @var{est}}. 91 ## norm (@var{A}, 1) * norm (@var{v}, 1) / @var{cest}}.
86 ## 92 ##
87 ## Algorithm Note: @code{condest} uses a randomized algorithm to approximate 93 ## Algorithm Note: @code{condest} uses a randomized algorithm to approximate
88 ## the 1-norms. Therefore, if consistent results are required, the 94 ## the 1-norms. Therefore, if consistent results are required, the
89 ## @qcode{"state"} of the random generator should be fixed before invoking 95 ## @qcode{"state"} of the random generator should be fixed before invoking
90 ## @code{condest}. 96 ## @code{condest}.
102 ## @nospell{N.J. Higham and F. Tisseur}, @cite{A Block Algorithm 108 ## @nospell{N.J. Higham and F. Tisseur}, @cite{A Block Algorithm
103 ## for Matrix 1-Norm Estimation, with an Application to 1-Norm 109 ## for Matrix 1-Norm Estimation, with an Application to 1-Norm
104 ## Pseudospectra}. @url{https://citeseer.ist.psu.edu/223007.html} 110 ## Pseudospectra}. @url{https://citeseer.ist.psu.edu/223007.html}
105 ## @end itemize 111 ## @end itemize
106 ## 112 ##
107 ## @seealso{cond, norm, normest1, normest} 113 ## @seealso{cond, rcond, norm, normest1, normest}
108 ## @end deftypefn 114 ## @end deftypefn
109 115
110 ## Code originally licensed under: 116 ## Code originally licensed under:
111 ## 117 ##
112 ## Copyright (c) 2007, Regents of the University of California 118 ## Copyright (c) 2007, Regents of the University of California
140 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 146 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
141 ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 147 ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
142 ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 148 ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
143 ## SUCH DAMAGE. 149 ## SUCH DAMAGE.
144 150
145 ## Author: Jason Riedy <ejr@cs.berkeley.edu>
146 ## Keywords: linear-algebra norm estimation
147 ## Version: 0.2
148
149 function [cest, v] = condest (varargin) 151 function [cest, v] = condest (varargin)
150 152
151 if (nargin < 1 || nargin > 6) 153 if (nargin < 1 || nargin > 6)
152 print_usage (); 154 print_usage ();
153 endif 155 endif
154 156
155 have_A = false; 157 have_A = false;
156 have_t = false; 158 have_t = false;
157 have_apply_normest1 = false; 159 have_Afcn = false;
158 have_solve_normest1 = false; 160 have_Ainvfcn = false;
159 161
160 if (isnumeric (varargin{1})) 162 if (isnumeric (varargin{1}))
161 A = varargin{1}; 163 A = varargin{1};
162 if (! issquare (A)) 164 if (! issquare (A))
163 error ("condest: A must be square"); 165 error ("condest: A must be square");
164 endif 166 endif
165 have_A = true; 167 have_A = true;
166 n = rows (A); 168 n = rows (A);
167 if (nargin > 1) 169 if (nargin > 1)
168 if (is_function_handle (varargin{2})) 170 if (is_function_handle (varargin{2}))
169 solve = varargin{2}; 171 Ainvfcn = varargin{2};
170 have_solve_normest1 = true; 172 have_Ainvfcn = true;
171 if (nargin > 2) 173 if (nargin > 2)
172 t = varargin{3}; 174 t = varargin{3};
173 have_t = true; 175 have_t = true;
174 endif 176 endif
175 else 177 else
176 t = varargin{2}; 178 t = varargin{2};
177 have_t = true; 179 have_t = true;
178 real_op = isreal (A);
179 endif 180 endif
180 else
181 real_op = isreal (A);
182 endif 181 endif
183 elseif (is_function_handle (varargin{1})) 182 elseif (is_function_handle (varargin{1}))
184 if (nargin == 1) 183 if (nargin == 1)
185 error("condest: must provide SOLVEFCN when using AFCN"); 184 error("condest: must provide AINVFCN when using AFCN");
186 endif 185 endif
187 apply = varargin{1}; 186 Afcn = varargin{1};
188 have_apply_normest1 = true; 187 have_Afcn = true;
189 if (! is_function_handle (varargin{2})) 188 if (! is_function_handle (varargin{2}))
190 error("condest: SOLVEFCN must be a function handle"); 189 error("condest: AINVFCN must be a function handle");
191 endif 190 endif
192 solve = varargin{2}; 191 Ainvfcn = varargin{2};
193 have_solve_normest1 = true; 192 have_Ainvfcn = true;
194 n = apply ("dim", [], varargin{4:end}); 193 n = Afcn ("dim", [], varargin{4:end});
195 if (nargin > 2) 194 if (nargin > 2)
196 t = varargin{3}; 195 t = varargin{3};
197 have_t = true; 196 have_t = true;
198 endif 197 endif
199 else 198 else
205 endif 204 endif
206 205
207 ## Disable warnings which may be emitted during calculation process. 206 ## Disable warnings which may be emitted during calculation process.
208 warning ("off", "Octave:nearly-singular-matrix", "local"); 207 warning ("off", "Octave:nearly-singular-matrix", "local");
209 208
210 if (! have_solve_normest1) 209 if (! have_Ainvfcn)
211 ## prepare solve in normest1 form 210 ## Prepare Ainvfcn in normest1 form
212 if (issparse (A)) 211 if (issparse (A))
213 [L, U, P, Pc] = lu (A); 212 [L, U, P, Q] = lu (A);
214 solve = @(flag, x) solve_sparse (flag, x, n, real_op, L, U, P, Pc); 213 Ainvfcn = @inv_sparse_fcn;
215 else 214 else
216 [L, U, P] = lu (A); 215 [L, U, P] = lu (A);
217 solve = @(flag, x) solve_not_sparse (flag, x, n, real_op, L, U, P); 216 Q = [];
218 endif 217 Ainvfcn = @inv_full_fcn;
219 218 endif
220 ## Check for singular matrices before continuing 219
220 ## Check for singular matrices before continuing (bug #46737)
221 if (any (diag (U) == 0)) 221 if (any (diag (U) == 0))
222 cest = Inf; 222 cest = Inf;
223 v = []; 223 v = [];
224 return; 224 return;
225 endif 225 endif
226
227 ## Initialize solver
228 Ainvfcn ("init", A, L, U, P, Q);
229 clear L U P Q;
226 endif 230 endif
227 231
228 if (have_A) 232 if (have_A)
229 Anorm = norm (A, 1); 233 Anorm = norm (A, 1);
230 else 234 else
231 Anorm = normest1 (apply, t, [], varargin{4:end}); 235 Anorm = normest1 (Afcn, t, [], varargin{4:end});
232 endif 236 endif
233 [Ainv_norm, v, w] = normest1 (solve, t, [], varargin{4:end}); 237 [Ainv_norm, v, w] = normest1 (Ainvfcn, t, [], varargin{4:end});
234 238
235 cest = Anorm * Ainv_norm; 239 cest = Anorm * Ainv_norm;
236 if (isargout (2)) 240 if (isargout (2))
237 v = w / norm (w, 1); 241 v = w / norm (w, 1);
238 endif 242 endif
239 243
244 if (! have_Ainvfcn)
245 Ainvfcn ("clear"); # clear persistent memory in subfunction
246 endif
247
240 endfunction 248 endfunction
241 249
242 function value = solve_sparse (flag, x, n, real_op, L , U , P , Pc) 250 function retval = inv_sparse_fcn (flag, x, varargin)
243 ## FIXME: Sparse algorithm is less accurate than full matrix version 251 ## FIXME: Sparse algorithm is less accurate than full matrix version.
244 ## See BIST test for non-orthogonal matrix where relative tolerance 252 ## See BIST test for asymmetric matrix where relative tolerance
245 ## of 1e-12 is used for sparse, but 4e-16 for full matrix. 253 ## of 1e-12 is used for sparse, but 4e-16 for full matrix.
254 ## BUT, does it really matter for an "estimate"?
255 persistent Ainv Ainvt n isreal_op;
256
246 switch (flag) 257 switch (flag)
247 case "dim" 258 case "dim"
248 value = n; 259 retval = n;
249 case "real" 260 case "real"
250 value = real_op; 261 retval = isreal_op;
251 case "notransp" 262 case "notransp"
252 value = Pc' * (U \ (L \ (P * x))); 263 retval = Ainv * x;
253 case "transp" 264 case "transp"
254 value = P' * (L' \ (U' \ (Pc * x))); 265 retval = Ainvt * x;
266 case "init"
267 n = rows (x);
268 isreal_op = isreal (x);
269 [L, U, P, Q] = deal (varargin{1:4});
270 Ainv = Q * (U \ (L \ P));
271 Ainvt = P' * (L' \ (U' \ Q'));
272 case "clear" # called to free memory at end of condest function
273 clear Ainv Ainvt n isreal_op;
255 endswitch 274 endswitch
275
256 endfunction 276 endfunction
257 277
258 function value = solve_not_sparse (flag, x, n, real_op, L, U, P) 278 function retval = inv_full_fcn (flag, x, varargin)
279 persistent Ainv Ainvt n isreal_op;
280
259 switch (flag) 281 switch (flag)
260 case "dim" 282 case "dim"
261 value = n; 283 retval = n;
262 case "real" 284 case "real"
263 value = real_op; 285 retval = isreal_op;
264 case "notransp" 286 case "notransp"
265 value = U \ (L \ (P * x)); 287 retval = Ainv * x;
266 case "transp" 288 case "transp"
267 value = P' * (L' \ (U' \ x)); 289 retval = Ainvt \ x;
290 case "init"
291 n = rows (x);
292 isreal_op = isreal (x);
293 [L, U, P] = deal (varargin{1:3});
294 Ainv = U \ (L \ P);
295 Ainvt = P' * (L' \ U');
296 case "clear" # called to free memory at end of condest function
297 clear Ainv Ainvt n isreal_op;
268 endswitch 298 endswitch
299
269 endfunction 300 endfunction
270 301
271 302
272 ## Note: These test bounds are very loose. There is enough randomization to 303 ## Note: These test bounds are very loose. There is enough randomization to
273 ## trigger odd cases with hilb(). 304 ## trigger odd cases with hilb().
274 305
275 %!function value = apply_fun (flag, x, A, m) 306 %!function retval = __Afcn__ (flag, x, A, m)
276 %! if (nargin == 3) 307 %! if (nargin == 3)
277 %! m = 1; 308 %! m = 1;
278 %! endif 309 %! endif
279 %! switch (flag) 310 %! switch (flag)
280 %! case "dim" 311 %! case "dim"
281 %! value = length (A); 312 %! retval = length (A);
282 %! case "real" 313 %! case "real"
283 %! value = isreal (A); 314 %! retval = isreal (A);
284 %! case "notransp" 315 %! case "notransp"
285 %! value = x; for i = 1:m, value = A * value;, endfor 316 %! retval = x; for i = 1:m, retval = A * retval;, endfor
286 %! case "transp" 317 %! case "transp"
287 %! value = x; for i = 1:m, value = A' * value;, endfor 318 %! retval = x; for i = 1:m, retval = A' * retval;, endfor
288 %! endswitch 319 %! endswitch
289 %!endfunction 320 %!endfunction
290 %!function value = solve_fun (flag, x, A, m) 321 %!function retval = __Ainvfcn__ (flag, x, A, m)
291 %! if (nargin == 3) 322 %! if (nargin == 3)
292 %! m = 1; 323 %! m = 1;
293 %! endif 324 %! endif
294 %! switch (flag) 325 %! switch (flag)
295 %! case "dim" 326 %! case "dim"
296 %! value = length (A); 327 %! retval = length (A);
297 %! case "real" 328 %! case "real"
298 %! value = isreal (A); 329 %! retval = isreal (A);
299 %! case "notransp" 330 %! case "notransp"
300 %! value = x; for i = 1:m, value = A \ value;, endfor 331 %! retval = x; for i = 1:m, retval = A \ retval;, endfor
301 %! case "transp" 332 %! case "transp"
302 %! value = x; for i = 1:m, value = A' \ value;, endfor 333 %! retval = x; for i = 1:m, retval = A' \ retval;, endfor
303 %! endswitch 334 %! endswitch
304 %!endfunction 335 %!endfunction
305 336
306 %!test 337 %!test
307 %! N = 6; 338 %! N = 6;
318 %! assert (norm (x, inf), 0, eps); 349 %! assert (norm (x, inf), 0, eps);
319 350
320 %!test 351 %!test
321 %! N = 6; 352 %! N = 6;
322 %! A = hilb (N); 353 %! A = hilb (N);
323 %! solve = @(flag, x) solve_fun (flag, x, A); 354 %! Ainvfcn = @(flag, x) __Ainvfcn__ (flag, x, A);
324 %! cA = condest (A, solve); 355 %! cA = condest (A, Ainvfcn);
325 %! cA_test = norm (inv (A), 1) * norm (A, 1); 356 %! cA_test = norm (inv (A), 1) * norm (A, 1);
326 %! assert (cA, cA_test, -2^-6); 357 %! assert (cA, cA_test, -2^-6);
327 358
328 %!test 359 %!test
329 %! N = 6; 360 %! N = 6;
330 %! A = hilb (N); 361 %! A = hilb (N);
331 %! apply = @(flag, x) apply_fun (flag, x, A); 362 %! Afcn = @(flag, x) __Afcn__ (flag, x, A);
332 %! solve = @(flag, x) solve_fun (flag, x, A); 363 %! Ainvfcn = @(flag, x) __Ainvfcn__ (flag, x, A);
333 %! cA = condest (apply, solve); 364 %! cA = condest (Afcn, Ainvfcn);
334 %! cA_test = norm (inv (A), 1) * norm (A, 1); 365 %! cA_test = norm (inv (A), 1) * norm (A, 1);
335 %! assert (cA, cA_test, -2^-6); 366 %! assert (cA, cA_test, -2^-6);
336 367
337 %!test # parameters for apply and solve functions 368 %!test # parameters for apply and Ainvfcn functions
338 %! N = 6; 369 %! N = 6;
339 %! A = hilb (N); 370 %! A = hilb (N);
340 %! m = 2; 371 %! m = 2;
341 %! cA = condest (@apply_fun, @solve_fun, [], A, m); 372 %! cA = condest (@__Afcn__, @__Ainvfcn__, [], A, m);
342 %! cA_test = norm (inv (A^2), 1) * norm (A^2, 1); 373 %! cA_test = norm (inv (A^2), 1) * norm (A^2, 1);
343 %! assert (cA, cA_test, -2^-6); 374 %! assert (cA, cA_test, -2^-6);
344 375
345 ## Test singular matrices 376 ## Test singular matrices
346 %!test <*46737> 377 %!test <*46737>
349 %! 0 0.0833333 1.66667]; 380 %! 0 0.0833333 1.66667];
350 %! [cest, v] = condest (A); 381 %! [cest, v] = condest (A);
351 %! assert (cest, Inf); 382 %! assert (cest, Inf);
352 %! assert (v, []); 383 %! assert (v, []);
353 384
354 ## Test non-orthogonal matrices 385 ## Test asymmetric matrices
355 %!test <*57968> 386 %!test <*57968>
356 %! A = reshape (sqrt (0:15), 4, 4); 387 %! A = reshape (sqrt (0:15), 4, 4);
357 %! cexp = norm (A, 1) * norm (inv (A), 1); 388 %! cexp = norm (A, 1) * norm (inv (A), 1);
358 %! cest = condest (A); 389 %! cest = condest (A);
359 %! assert (cest, cexp, -2*eps); 390 %! assert (cest, cexp, -2*eps);
363 %! cexp = norm (As, 1) * norm (inv (As), 1); 394 %! cexp = norm (As, 1) * norm (inv (As), 1);
364 %! cest = condest (As); 395 %! cest = condest (As);
365 %! assert (cest, cexp, -1e-12); 396 %! assert (cest, cexp, -1e-12);
366 397
367 ## Test input validation 398 ## Test input validation
368 %!error condest () 399 %!error <Invalid call> condest ()
369 %!error condest (1,2,3,4,5,6,7) 400 %!error <Invalid call> condest (1,2,3,4,5,6,7)
370 %!error <A must be square> condest ([1 2]) 401 %!error <A must be square> condest ([1, 2])
371 %!error <must provide SOLVEFCN when using AFCN> condest (@sin) 402 %!error <must provide AINVFCN when using AFCN> condest (@sin)
372 %!error <SOLVEFCN must be a function handle> condest (@sin, 1) 403 %!error <AINVFCN must be a function handle> condest (@sin, 1)
373 %!error <argument must be a square matrix or function handle> condest ({1}) 404 %!error <argument must be a square matrix or function handle> condest ({1})