GNU Octave  8.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
quad.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2023 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 "Quad.h"
33 #include "lo-mappers.h"
34 
35 #include "defun.h"
36 #include "error.h"
37 #include "errwarn.h"
38 #include "interpreter-private.h"
39 #include "pager.h"
40 #include "parse.h"
41 #include "ov.h"
42 #include "ovl.h"
43 #include "unwind-prot.h"
44 #include "utils.h"
45 #include "variables.h"
46 
47 #include "Quad-opts.cc"
48 
50 
51 // Global pointer for user defined function required by quadrature functions.
52 static octave_value quad_fcn;
53 
54 // Have we warned about imaginary values returned from user function?
55 static bool warned_imaginary = false;
56 
57 // Is this a recursive call?
58 static int call_depth = 0;
59 
60 static double
62 {
63  double retval = 0.0;
64 
65  octave_value_list args;
66  args(0) = x;
67 
68  if (quad_fcn.is_defined ())
69  {
71 
72  try
73  {
74  tmp = feval (quad_fcn, args, 1);
75  }
76  catch (execution_exception& ee)
77  {
78  err_user_supplied_eval (ee, "quad");
79  }
80 
81  if (! tmp.length () || ! tmp(0).is_defined ())
82  err_user_supplied_eval ("quad");
83 
84  if (! warned_imaginary && tmp(0).iscomplex ())
85  {
86  warning ("quad: ignoring imaginary part returned from user-supplied function");
87  warned_imaginary = true;
88  }
89 
90  retval = tmp(0).xdouble_value ("quad: expecting user supplied function to return numeric value");
91  }
92 
93  return retval;
94 }
95 
96 static float
98 {
99  float retval = 0.0;
100 
101  octave_value_list args;
102  args(0) = x;
103 
104  if (quad_fcn.is_defined ())
105  {
106  octave_value_list tmp;
107 
108  try
109  {
110  tmp = feval (quad_fcn, args, 1);
111  }
112  catch (execution_exception& ee)
113  {
114  err_user_supplied_eval (ee, "quad");
115  }
116 
117  if (! tmp.length () || ! tmp(0).is_defined ())
118  err_user_supplied_eval ("quad");
119 
120  if (! warned_imaginary && tmp(0).iscomplex ())
121  {
122  warning ("quad: ignoring imaginary part returned from user-supplied function");
123  warned_imaginary = true;
124  }
125 
126  retval = tmp(0).xfloat_value ("quad: expecting user supplied function to return numeric value");
127  }
128 
129  return retval;
130 }
131 
132 DEFMETHODX ("quad", Fquad, interp, args, ,
133  doc: /* -*- texinfo -*-
134 @deftypefn {} {@var{q} =} quad (@var{f}, @var{a}, @var{b})
135 @deftypefnx {} {@var{q} =} quad (@var{f}, @var{a}, @var{b}, @var{tol})
136 @deftypefnx {} {@var{q} =} quad (@var{f}, @var{a}, @var{b}, @var{tol}, @var{sing})
137 @deftypefnx {} {[@var{q}, @var{ier}, @var{nfev}, @var{err}] =} quad (@dots{})
138 Numerically evaluate the integral of @var{f} from @var{a} to @var{b} using
139 Fortran routines from @w{@sc{quadpack}}.
140 
141 @var{f} is a function handle, inline function, or a string containing the
142 name of the function to evaluate. The function must have the form @code{y =
143 f (x)} where @var{y} and @var{x} are scalars.
144 
145 @var{a} and @var{b} are the lower and upper limits of integration. Either
146 or both may be infinite.
147 
148 The optional argument @var{tol} is a vector that specifies the desired
149 accuracy of the result. The first element of the vector is the desired
150 absolute tolerance, and the second element is the desired relative
151 tolerance. To choose a relative test only, set the absolute
152 tolerance to zero. To choose an absolute test only, set the relative
153 tolerance to zero. Both tolerances default to @code{sqrt (eps)} or
154 approximately 1.5e-8.
155 
156 The optional argument @var{sing} is a vector of values at which the
157 integrand is known to be singular.
158 
159 The result of the integration is returned in @var{q}.
160 
161 @var{ier} contains an integer error code (0 indicates a successful
162 integration).
163 
164 @var{nfev} indicates the number of function evaluations that were
165 made.
166 
167 @var{err} contains an estimate of the error in the solution.
168 
169 The function @code{quad_options} can set other optional parameters for
170 @code{quad}.
171 
172 Note: because @code{quad} is written in Fortran it cannot be called
173 recursively. This prevents its use in integrating over more than one
174 variable by routines @code{dblquad} and @code{triplequad}.
175 @seealso{quad_options, quadv, quadl, quadgk, quadcc, trapz, dblquad, triplequad}
176 @end deftypefn */)
177 {
178  int nargin = args.length ();
179 
180  if (nargin < 3 || nargin > 5)
181  print_usage ();
182 
183  warned_imaginary = false;
184 
185  unwind_protect_var<int> restore_var (call_depth);
186  call_depth++;
187 
188  if (call_depth > 1)
189  error ("quad: invalid recursive call");
190 
191  quad_fcn = get_function_handle (interp, args(0), "x");
192 
193  octave_value_list retval;
194 
195  if (args(1).is_single_type () || args(2).is_single_type ())
196  {
197  float a = args(1).xfloat_value ("quad: lower limit of integration A must be a scalar");
198  float b = args(2).xfloat_value ("quad: upper limit of integration B must be a scalar");
199 
200  int indefinite = 0;
203  float bound = 0.0;
204  if (math::isinf (a) && math::isinf (b))
205  {
206  indefinite = 1;
207  indef_type = FloatIndefQuad::doubly_infinite;
208  }
209  else if (math::isinf (a))
210  {
211  indefinite = 1;
212  bound = b;
214  }
215  else if (math::isinf (b))
216  {
217  indefinite = 1;
218  bound = a;
219  indef_type = FloatIndefQuad::bound_to_inf;
220  }
221 
222  octave_idx_type ier = 0;
223  octave_idx_type nfev = 0;
224  float abserr = 0.0;
225  float val = 0.0;
226  bool have_sing = false;
227  FloatColumnVector sing;
228  FloatColumnVector tol;
229 
230  switch (nargin)
231  {
232  case 5:
233  if (indefinite)
234  error ("quad: singularities not allowed on infinite intervals");
235 
236  have_sing = true;
237 
238  sing = args(4).xfloat_vector_value ("quad: fifth argument SING must be a vector of singularities");
239  OCTAVE_FALLTHROUGH;
240 
241  case 4:
242  tol = args(3).xfloat_vector_value ("quad: TOL must be a 1 or 2-element vector");
243 
244  switch (tol.numel ())
245  {
246  case 2:
247  quad_opts.set_single_precision_relative_tolerance (tol (1));
248  OCTAVE_FALLTHROUGH;
249 
250  case 1:
251  quad_opts.set_single_precision_absolute_tolerance (tol (0));
252  break;
253 
254  default:
255  error ("quad: TOL must be a 1 or 2-element vector");
256  }
257  OCTAVE_FALLTHROUGH;
258 
259  case 3:
260  if (indefinite)
261  {
263  indef_type);
264  iq.set_options (quad_opts);
265  val = iq.float_integrate (ier, nfev, abserr);
266  }
267  else
268  {
269  if (have_sing)
270  {
271  FloatDefQuad dq (quad_float_user_function, a, b, sing);
272  dq.set_options (quad_opts);
273  val = dq.float_integrate (ier, nfev, abserr);
274  }
275  else
276  {
278  dq.set_options (quad_opts);
279  val = dq.float_integrate (ier, nfev, abserr);
280  }
281  }
282  break;
283 
284  default:
285  panic_impossible ();
286  break;
287  }
288 
289  retval = ovl (val, ier, nfev, abserr);
290 
291  }
292  else
293  {
294  double a = args(1).xdouble_value ("quad: lower limit of integration A must be a scalar");
295  double b = args(2).xdouble_value ("quad: upper limit of integration B must be a scalar");
296 
297  int indefinite = 0;
299  double bound = 0.0;
300  if (math::isinf (a) && math::isinf (b))
301  {
302  indefinite = 1;
303  indef_type = IndefQuad::doubly_infinite;
304  }
305  else if (math::isinf (a))
306  {
307  indefinite = 1;
308  bound = b;
309  indef_type = IndefQuad::neg_inf_to_bound;
310  }
311  else if (math::isinf (b))
312  {
313  indefinite = 1;
314  bound = a;
315  indef_type = IndefQuad::bound_to_inf;
316  }
317 
318  octave_idx_type ier = 0;
319  octave_idx_type nfev = 0;
320  double abserr = 0.0;
321  double val = 0.0;
322  bool have_sing = false;
323  ColumnVector sing;
324  ColumnVector tol;
325 
326  switch (nargin)
327  {
328  case 5:
329  if (indefinite)
330  error ("quad: singularities not allowed on infinite intervals");
331 
332  have_sing = true;
333 
334  sing = args(4).xvector_value ("quad: fifth argument SING must be a vector of singularities");
335  OCTAVE_FALLTHROUGH;
336 
337  case 4:
338  tol = args(3).xvector_value ("quad: TOL must be a 1 or 2-element vector");
339 
340  switch (tol.numel ())
341  {
342  case 2:
343  quad_opts.set_relative_tolerance (tol (1));
344  OCTAVE_FALLTHROUGH;
345 
346  case 1:
347  quad_opts.set_absolute_tolerance (tol (0));
348  break;
349 
350  default:
351  error ("quad: TOL must be a 1 or 2-element vector");
352  }
353  OCTAVE_FALLTHROUGH;
354 
355  case 3:
356  if (indefinite)
357  {
358  IndefQuad iq (quad_user_function, bound, indef_type);
359  iq.set_options (quad_opts);
360  val = iq.integrate (ier, nfev, abserr);
361  }
362  else
363  {
364  if (have_sing)
365  {
366  DefQuad dq (quad_user_function, a, b, sing);
367  dq.set_options (quad_opts);
368  val = dq.integrate (ier, nfev, abserr);
369  }
370  else
371  {
372  DefQuad dq (quad_user_function, a, b);
373  dq.set_options (quad_opts);
374  val = dq.integrate (ier, nfev, abserr);
375  }
376  }
377  break;
378 
379  default:
380  panic_impossible ();
381  break;
382  }
383 
384  retval = ovl (val, ier, nfev, abserr);
385  }
386 
387  return retval;
388 }
389 
390 /*
391 %!function y = __f (x)
392 %! y = x + 1;
393 %!endfunction
394 
395 %!test
396 %! [v, ier, nfev, err] = quad ("__f", 0, 5);
397 %! assert (ier, 0);
398 %! assert (v, 17.5, sqrt (eps));
399 %! assert (nfev > 0);
400 %! assert (err < sqrt (eps));
401 
402 %!test
403 %! [v, ier, nfev, err] = quad ("__f", single (0), single (5));
404 %! assert (ier, 0);
405 %! assert (v, 17.5, sqrt (eps ("single")));
406 %! assert (nfev > 0);
407 %! assert (err < sqrt (eps ("single")));
408 
409 %!function y = __f (x)
410 %! y = x .* sin (1 ./ x) .* sqrt (abs (1 - x));
411 %!endfunction
412 
413 %!test
414 %! [v, ier, nfev, err] = quad ("__f", 0.001, 3);
415 %! assert (ier == 0 || ier == 1);
416 %! assert (v, 1.98194120273598, sqrt (eps));
417 %! assert (nfev > 0);
418 
419 %!test
420 %! [v, ier, nfev, err] = quad (@__f, 0.001, 3);
421 %! assert (ier == 0 || ier == 1);
422 %! assert (v, 1.98194120273598, sqrt (eps));
423 %! assert (nfev > 0);
424 
425 %!test
426 %! fstr = "x .* sin (1 ./ x) .* sqrt (abs (1 - x))";
427 %! [v, ier, nfev, err] = quad (fstr, 0.001, 3);
428 %! assert (ier == 0 || ier == 1);
429 %! assert (v, 1.98194120273598, sqrt (eps));
430 %! assert (nfev > 0);
431 
432 %!test
433 %! anon_fcn = @(x) x .* sin (1 ./ x) .* sqrt (abs (1 - x));
434 %! [v, ier, nfev, err] = quad (anon_fcn, 0.001, 3);
435 %! assert (ier == 0 || ier == 1);
436 %! assert (v, 1.98194120273598, sqrt (eps));
437 %! assert (nfev > 0);
438 
439 %!test
440 %! inline_fcn = inline ("x .* sin (1 ./ x) .* sqrt (abs (1 - x))", "x");
441 %! [v, ier, nfev, err] = quad (inline_fcn, 0.001, 3);
442 %! assert (ier == 0 || ier == 1);
443 %! assert (v, 1.98194120273598, sqrt (eps));
444 %! assert (nfev > 0);
445 
446 %!test
447 %! [v, ier, nfev, err] = quad ("__f", single (0.001), single (3));
448 %! assert (ier == 0 || ier == 1);
449 %! assert (v, 1.98194120273598, sqrt (eps ("single")));
450 %! assert (nfev > 0);
451 
452 %!error quad ()
453 %!error quad ("__f", 1, 2, 3, 4, 5)
454 
455 %!test
456 %! quad_options ("absolute tolerance", eps);
457 %! assert (quad_options ("absolute tolerance") == eps);
458 
459 %!error quad_options (1, 2, 3)
460 */
461 
OCTAVE_END_NAMESPACE(octave)
static Quad_options quad_opts
Definition: Quad-opts.cc:21
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:414
Definition: Quad.h:120
@ doubly_infinite
Definition: Quad.h:229
@ bound_to_inf
Definition: Quad.h:229
@ neg_inf_to_bound
Definition: Quad.h:229
IntegralType
Definition: Quad.h:162
@ bound_to_inf
Definition: Quad.h:162
@ doubly_infinite
Definition: Quad.h:162
@ neg_inf_to_bound
Definition: Quad.h:162
virtual double integrate(void)
Definition: Quad.h:53
virtual float float_integrate(void)
Definition: Quad.h:60
octave_idx_type length(void) const
Definition: ovl.h:113
bool is_defined(void) const
Definition: ov.h:637
OCTINTERP_API void print_usage(void)
Definition: defun-int.h:72
void warning(const char *fmt,...)
Definition: error.cc:1054
void error(const char *fmt,...)
Definition: error.cc:979
#define panic_impossible()
Definition: error.h:508
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:152
octave_value get_function_handle(interpreter &interp, const octave_value &arg, const std::string &parameter_name)
bool isinf(double x)
Definition: lo-mappers.h:203
F77_RET_T const F77_DBLE * x
octave_value_list feval(const char *name, const octave_value_list &args, int nargout)
Evaluate an Octave function (built-in or interpreted) and return the list of result values.
Definition: oct-parse.cc:10370
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
DEFMETHODX("quad", Fquad, interp, args,, doc:)
Definition: quad.cc:132
static double quad_user_function(double x)
Definition: quad.cc:61
static int call_depth
Definition: quad.cc:58
static bool warned_imaginary
Definition: quad.cc:55
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value quad_fcn
static float quad_float_user_function(float x)
Definition: quad.cc:97