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