GNU Octave  9.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-2024 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.
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
61 quad_user_function (double x)
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  interpreter& interp = __get_interpreter__ ();
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 ())
84  err_user_supplied_eval ("quad");
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 
98 static float
99 quad_float_user_function (float x)
100 {
101  float retval = 0.0;
102 
103  octave_value_list args;
104  args(0) = x;
105 
106  if (quad_fcn.is_defined ())
107  {
108  octave_value_list tmp;
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 
136 DEFMETHODX ("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{})
142 Numerically evaluate the integral of @var{f} from @var{a} to @var{b} using
143 Fortran routines from @w{@sc{quadpack}}.
144 
145 @var{f} is a function handle, inline function, or a string containing the
146 name of the function to evaluate. The function must have the form @code{y =
147 f (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
150 or both may be infinite.
151 
152 The optional argument @var{tol} is a vector that specifies the desired
153 accuracy of the result. The first element of the vector is the desired
154 absolute tolerance, and the second element is the desired relative
155 tolerance. To choose a relative test only, set the absolute
156 tolerance to zero. To choose an absolute test only, set the relative
157 tolerance to zero. Both tolerances default to @code{sqrt (eps)} or
158 approximately 1.5e-8.
159 
160 The optional argument @var{sing} is a vector of values at which the
161 integrand is known to be singular.
162 
163 The result of the integration is returned in @var{q}.
164 
165 @var{ier} contains an integer error code (0 indicates a successful
166 integration).
167 
168 @var{nfev} indicates the number of function evaluations that were
169 made.
170 
171 @var{err} contains an estimate of the error in the solution.
172 
173 The function @code{quad_options} can set other optional parameters for
174 @code{quad}.
175 
176 Note: because @code{quad} is written in Fortran it cannot be called
177 recursively. This prevents its use in integrating over more than one
178 variable 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;
211  indef_type = FloatIndefQuad::doubly_infinite;
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;
231  FloatColumnVector sing;
232  FloatColumnVector tol;
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  panic_impossible ();
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  panic_impossible ();
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 
466 OCTAVE_END_NAMESPACE(octave)
octave_value_list Fquad(octave::interpreter &, const octave_value_list &=octave_value_list(), int=0)
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
Definition: Quad.h:124
@ doubly_infinite
Definition: Quad.h:239
@ bound_to_inf
Definition: Quad.h:239
@ neg_inf_to_bound
Definition: Quad.h:239
IntegralType
Definition: Quad.h:168
@ bound_to_inf
Definition: Quad.h:168
@ doubly_infinite
Definition: Quad.h:168
@ neg_inf_to_bound
Definition: Quad.h:168
void set_options(const Quad_options &opt)
Definition: Quad-opts.h:61
virtual float float_integrate()
Definition: Quad.h:64
virtual double integrate()
Definition: Quad.h:57
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:113
bool is_defined() const
Definition: ov.h:592
void print_usage(void)
Definition: defun-int.h:72
void warning(const char *fmt,...)
Definition: error.cc:1063
void() error(const char *fmt,...)
Definition: error.cc:988
#define panic_impossible()
Definition: error.h:503
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)
bool isinf(double x)
Definition: lo-mappers.h:203
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:219
DEFMETHODX("quad", Fquad, interp, args,, doc:)
Definition: quad.cc:136
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value quad_fcn