GNU Octave 7.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-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 "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
49OCTAVE_NAMESPACE_BEGIN
50
51// Global pointer for user defined function required by quadrature functions.
53
54// Have we warned about imaginary values returned from user function?
55static bool warned_imaginary = false;
56
57// Is this a recursive call?
58static int call_depth = 0;
59
60static double
62{
63 double retval = 0.0;
64
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 ())
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
96static float
98{
99 float retval = 0.0;
100
102 args(0) = x;
103
104 if (quad_fcn.is_defined ())
105 {
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
132DEFMETHODX ("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{nfun}, @var{err}] =} quad (@dots{})
138Numerically evaluate the integral of @var{f} from @var{a} to @var{b} using
139Fortran routines from @w{@sc{quadpack}}.
140
141@var{f} is a function handle, inline function, or a string containing the
142name of the function to evaluate. The function must have the form @code{y =
143f (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
146or both may be infinite.
147
148The optional argument @var{tol} is a vector that specifies the desired
149accuracy of the result. The first element of the vector is the desired
150absolute tolerance, and the second element is the desired relative
151tolerance. To choose a relative test only, set the absolute
152tolerance to zero. To choose an absolute test only, set the relative
153tolerance to zero. Both tolerances default to @code{sqrt (eps)} or
154approximately 1.5e-8.
155
156The optional argument @var{sing} is a vector of values at which the
157integrand is known to be singular.
158
159The result of the integration is returned in @var{q}.
160
161@var{ier} contains an integer error code (0 indicates a successful
162integration).
163
164@var{nfun} indicates the number of function evaluations that were
165made.
166
167@var{err} contains an estimate of the error in the solution.
168
169The function @code{quad_options} can set other optional parameters for
170@code{quad}.
171
172Note: because @code{quad} is written in Fortran it cannot be called
173recursively. This prevents its use in integrating over more than one
174variable 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;
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 nfun = 0;
224 float abserr = 0.0;
225 float val = 0.0;
226 bool have_sing = false;
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, nfun, 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, nfun, abserr);
274 }
275 else
276 {
278 dq.set_options (quad_opts);
279 val = dq.float_integrate (ier, nfun, abserr);
280 }
281 }
282 break;
283
284 default:
286 break;
287 }
288
289 retval = ovl (val, ier, nfun, 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 nfun = 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, nfun, 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, nfun, abserr);
369 }
370 else
371 {
372 DefQuad dq (quad_user_function, a, b);
373 dq.set_options (quad_opts);
374 val = dq.integrate (ier, nfun, abserr);
375 }
376 }
377 break;
378
379 default:
381 break;
382 }
383
384 retval = ovl (val, ier, nfun, 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, nfun, err] = quad ("__f", 0, 5);
397%! assert (ier, 0);
398%! assert (v, 17.5, sqrt (eps));
399%! assert (nfun > 0);
400%! assert (err < sqrt (eps));
401
402%!test
403%! [v, ier, nfun, err] = quad ("__f", single (0), single (5));
404%! assert (ier, 0);
405%! assert (v, 17.5, sqrt (eps ("single")));
406%! assert (nfun > 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, nfun, err] = quad ("__f", 0.001, 3);
415%! assert (ier == 0 || ier == 1);
416%! assert (v, 1.98194120273598, sqrt (eps));
417%! assert (nfun > 0);
418
419%!test
420%! [v, ier, nfun, err] = quad (@__f, 0.001, 3);
421%! assert (ier == 0 || ier == 1);
422%! assert (v, 1.98194120273598, sqrt (eps));
423%! assert (nfun > 0);
424
425%!test
426%! fstr = "x .* sin (1 ./ x) .* sqrt (abs (1 - x))";
427%! [v, ier, nfun, err] = quad (fstr, 0.001, 3);
428%! assert (ier == 0 || ier == 1);
429%! assert (v, 1.98194120273598, sqrt (eps));
430%! assert (nfun > 0);
431
432%!test
433%! anon_fcn = @(x) x .* sin (1 ./ x) .* sqrt (abs (1 - x));
434%! [v, ier, nfun, err] = quad (anon_fcn, 0.001, 3);
435%! assert (ier == 0 || ier == 1);
436%! assert (v, 1.98194120273598, sqrt (eps));
437%! assert (nfun > 0);
438
439%!test
440%! inline_fcn = inline ("x .* sin (1 ./ x) .* sqrt (abs (1 - x))", "x");
441%! [v, ier, nfun, err] = quad (inline_fcn, 0.001, 3);
442%! assert (ier == 0 || ier == 1);
443%! assert (v, 1.98194120273598, sqrt (eps));
444%! assert (nfun > 0);
445
446%!test
447%! [v, ier, nfun, err] = quad ("__f", single (0.001), single (3));
448%! assert (ier == 0 || ier == 1);
449%! assert (v, 1.98194120273598, sqrt (eps ("single")));
450%! assert (nfun > 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
462OCTAVE_NAMESPACE_END
static Quad_options quad_opts
Definition: Quad-opts.cc:21
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:411
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:1055
void error(const char *fmt,...)
Definition: error.cc:980
#define panic_impossible()
Definition: error.h:411
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:152
F77_RET_T const F77_DBLE * x
bool isinf(double x)
Definition: lo-mappers.h:203
octave_value get_function_handle(interpreter &interp, const octave_value &arg, const std::string &parameter_name)
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:10300
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
static OCTAVE_NAMESPACE_BEGIN octave_value quad_fcn
Definition: quad.cc:52
static float quad_float_user_function(float x)
Definition: quad.cc:97