GNU Octave  4.4.1
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-2018 John W. Eaton
4 
5 This file is part of Octave.
6 
7 Octave is free software: you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 #if defined (HAVE_CONFIG_H)
24 # include "config.h"
25 #endif
26 
27 #include <string>
28 
29 #include <iomanip>
30 #include <iostream>
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 "pager.h"
39 #include "parse.h"
40 #include "ovl.h"
41 #include "ov-fcn.h"
42 #include "unwind-prot.h"
43 #include "utils.h"
44 #include "variables.h"
45 
46 #include "Quad-opts.cc"
47 
48 // Global pointer for user defined function required by quadrature functions.
50 
51 // Have we warned about imaginary values returned from user function?
52 static bool warned_imaginary = false;
53 
54 // Is this a recursive call?
55 static int call_depth = 0;
56 
57 double
59 {
60  double retval = 0.0;
61 
62  octave_value_list args;
63  args(0) = x;
64 
65  if (quad_fcn)
66  {
68 
69  try
70  {
71  tmp = octave::feval (quad_fcn, args, 1);
72  }
73  catch (octave::execution_exception& e)
74  {
75  err_user_supplied_eval (e, "quad");
76  }
77 
78  if (! tmp.length () || ! tmp(0).is_defined ())
79  err_user_supplied_eval ("quad");
80 
81  if (! warned_imaginary && tmp(0).iscomplex ())
82  {
83  warning ("quad: ignoring imaginary part returned from user-supplied function");
84  warned_imaginary = true;
85  }
86 
87  retval = tmp(0).xdouble_value ("quad: expecting user supplied function to return numeric value");
88  }
89 
90  return retval;
91 }
92 
93 float
95 {
96  float retval = 0.0;
97 
98  octave_value_list args;
99  args(0) = x;
100 
101  if (quad_fcn)
102  {
104 
105  try
106  {
107  tmp = octave::feval (quad_fcn, args, 1);
108  }
109  catch (octave::execution_exception& e)
110  {
111  err_user_supplied_eval (e, "quad");
112  }
113 
114  if (! tmp.length () || ! tmp(0).is_defined ())
115  err_user_supplied_eval ("quad");
116 
117  if (! warned_imaginary && tmp(0).iscomplex ())
118  {
119  warning ("quad: ignoring imaginary part returned from user-supplied function");
120  warned_imaginary = true;
121  }
122 
123  retval = tmp(0).xfloat_value ("quad: expecting user supplied function to return numeric value");
124  }
125 
126  return retval;
127 }
128 
129 DEFMETHODX ("quad", Fquad, interp, args, ,
130  doc: /* -*- texinfo -*-
131 @deftypefn {} {@var{q} =} quad (@var{f}, @var{a}, @var{b})
132 @deftypefnx {} {@var{q} =} quad (@var{f}, @var{a}, @var{b}, @var{tol})
133 @deftypefnx {} {@var{q} =} quad (@var{f}, @var{a}, @var{b}, @var{tol}, @var{sing})
134 @deftypefnx {} {[@var{q}, @var{ier}, @var{nfun}, @var{err}] =} quad (@dots{})
135 Numerically evaluate the integral of @var{f} from @var{a} to @var{b} using
136 Fortran routines from @w{@sc{quadpack}}.
137 
138 @var{f} is a function handle, inline function, or a string containing the
139 name of the function to evaluate. The function must have the form @code{y =
140 f (x)} where @var{y} and @var{x} are scalars.
141 
142 @var{a} and @var{b} are the lower and upper limits of integration. Either
143 or both may be infinite.
144 
145 The optional argument @var{tol} is a vector that specifies the desired
146 accuracy of the result. The first element of the vector is the desired
147 absolute tolerance, and the second element is the desired relative
148 tolerance. To choose a relative test only, set the absolute
149 tolerance to zero. To choose an absolute test only, set the relative
150 tolerance to zero. Both tolerances default to @code{sqrt (eps)} or
151 approximately 1.5e-8.
152 
153 The optional argument @var{sing} is a vector of values at which the
154 integrand is known to be singular.
155 
156 The result of the integration is returned in @var{q}.
157 
158 @var{ier} contains an integer error code (0 indicates a successful
159 integration).
160 
161 @var{nfun} indicates the number of function evaluations that were
162 made.
163 
164 @var{err} contains an estimate of the error in the solution.
165 
166 The function @code{quad_options} can set other optional parameters for
167 @code{quad}.
168 
169 Note: because @code{quad} is written in Fortran it cannot be called
170 recursively. This prevents its use in integrating over more than one
171 variable by routines @code{dblquad} and @code{triplequad}.
172 @seealso{quad_options, quadv, quadl, quadgk, quadcc, trapz, dblquad, triplequad}
173 @end deftypefn */)
174 {
175  int nargin = args.length ();
176 
178  print_usage ();
179 
180  warned_imaginary = false;
181 
183 
185  call_depth++;
186 
187  if (call_depth > 1)
188  error ("quad: invalid recursive call");
189 
190  std::string fcn_name;
191 
192  if (args(0).is_function_handle () || args(0).is_inline_function ())
193  quad_fcn = args(0).function_value ();
194  else
195  {
196  fcn_name = unique_symbol_name ("__quad_fcn__");
197  std::string fname = "function y = ";
198  fname.append (fcn_name);
199  fname.append ("(x) y = ");
200  quad_fcn = extract_function (args(0), "quad", fcn_name, fname,
201  "; endfunction");
202  octave::symbol_table& symtab = interp.get_symbol_table ();
204  }
205 
206  if (! quad_fcn)
207  error ("quad: FCN argument is not a valid function name or handle");
208 
210 
211  if (args(1).is_single_type () || args(2).is_single_type ())
212  {
213  float a = args(1).xfloat_value ("quad: lower limit of integration A must be a scalar");
214  float b = args(2).xfloat_value ("quad: upper limit of integration B must be a scalar");
215 
216  int indefinite = 0;
219  float bound = 0.0;
221  {
222  indefinite = 1;
223  indef_type = FloatIndefQuad::doubly_infinite;
224  }
225  else if (octave::math::isinf (a))
226  {
227  indefinite = 1;
228  bound = b;
230  }
231  else if (octave::math::isinf (b))
232  {
233  indefinite = 1;
234  bound = a;
235  indef_type = FloatIndefQuad::bound_to_inf;
236  }
237 
238  octave_idx_type ier = 0;
239  octave_idx_type nfun = 0;
240  float abserr = 0.0;
241  float val = 0.0;
242  bool have_sing = false;
243  FloatColumnVector sing;
244  FloatColumnVector tol;
245 
246  switch (nargin)
247  {
248  case 5:
249  if (indefinite)
250  error ("quad: singularities not allowed on infinite intervals");
251 
252  have_sing = true;
253 
254  sing = args(4).xfloat_vector_value ("quad: fifth argument SING must be a vector of singularities");
255  OCTAVE_FALLTHROUGH;
256 
257  case 4:
258  tol = args(3).xfloat_vector_value ("quad: TOL must be a 1 or 2-element vector");
259 
260  switch (tol.numel ())
261  {
262  case 2:
263  quad_opts.set_single_precision_relative_tolerance (tol (1));
264  OCTAVE_FALLTHROUGH;
265 
266  case 1:
267  quad_opts.set_single_precision_absolute_tolerance (tol (0));
268  break;
269 
270  default:
271  error ("quad: TOL must be a 1 or 2-element vector");
272  }
273  OCTAVE_FALLTHROUGH;
274 
275  case 3:
276  if (indefinite)
277  {
279  indef_type);
280  iq.set_options (quad_opts);
281  val = iq.float_integrate (ier, nfun, abserr);
282  }
283  else
284  {
285  if (have_sing)
286  {
288  dq.set_options (quad_opts);
289  val = dq.float_integrate (ier, nfun, abserr);
290  }
291  else
292  {
294  dq.set_options (quad_opts);
295  val = dq.float_integrate (ier, nfun, abserr);
296  }
297  }
298  break;
299 
300  default:
301  panic_impossible ();
302  break;
303  }
304 
305  retval = ovl (val, ier, nfun, abserr);
306 
307  }
308  else
309  {
310  double a = args(1).xdouble_value ("quad: lower limit of integration A must be a scalar");
311  double b = args(2).xdouble_value ("quad: upper limit of integration B must be a scalar");
312 
313  int indefinite = 0;
315  double bound = 0.0;
317  {
318  indefinite = 1;
319  indef_type = IndefQuad::doubly_infinite;
320  }
321  else if (octave::math::isinf (a))
322  {
323  indefinite = 1;
324  bound = b;
325  indef_type = IndefQuad::neg_inf_to_bound;
326  }
327  else if (octave::math::isinf (b))
328  {
329  indefinite = 1;
330  bound = a;
331  indef_type = IndefQuad::bound_to_inf;
332  }
333 
334  octave_idx_type ier = 0;
335  octave_idx_type nfun = 0;
336  double abserr = 0.0;
337  double val = 0.0;
338  bool have_sing = false;
339  ColumnVector sing;
340  ColumnVector tol;
341 
342  switch (nargin)
343  {
344  case 5:
345  if (indefinite)
346  error ("quad: singularities not allowed on infinite intervals");
347 
348  have_sing = true;
349 
350  sing = args(4).vector_value ("quad: fifth argument SING must be a vector of singularities");
351  OCTAVE_FALLTHROUGH;
352 
353  case 4:
354  tol = args(3).xvector_value ("quad: TOL must be a 1 or 2-element vector");
355 
356  switch (tol.numel ())
357  {
358  case 2:
359  quad_opts.set_relative_tolerance (tol (1));
360  OCTAVE_FALLTHROUGH;
361 
362  case 1:
363  quad_opts.set_absolute_tolerance (tol (0));
364  break;
365 
366  default:
367  error ("quad: TOL must be a 1 or 2-element vector");
368  }
369  OCTAVE_FALLTHROUGH;
370 
371  case 3:
372  if (indefinite)
373  {
374  IndefQuad iq (quad_user_function, bound, indef_type);
375  iq.set_options (quad_opts);
376  val = iq.integrate (ier, nfun, abserr);
377  }
378  else
379  {
380  if (have_sing)
381  {
382  DefQuad dq (quad_user_function, a, b, sing);
383  dq.set_options (quad_opts);
384  val = dq.integrate (ier, nfun, abserr);
385  }
386  else
387  {
388  DefQuad dq (quad_user_function, a, b);
389  dq.set_options (quad_opts);
390  val = dq.integrate (ier, nfun, abserr);
391  }
392  }
393  break;
394 
395  default:
396  panic_impossible ();
397  break;
398  }
399 
400  retval = ovl (val, ier, nfun, abserr);
401  }
402 
403  return retval;
404 }
405 
406 /*
407 %!function y = __f (x)
408 %! y = x + 1;
409 %!endfunction
410 
411 %!test
412 %! [v, ier, nfun, err] = quad ("__f", 0, 5);
413 %! assert (ier, 0);
414 %! assert (v, 17.5, sqrt (eps));
415 %! assert (nfun > 0);
416 %! assert (err < sqrt (eps));
417 
418 %!test
419 %! [v, ier, nfun, err] = quad ("__f", single (0), single (5));
420 %! assert (ier, 0);
421 %! assert (v, 17.5, sqrt (eps ("single")));
422 %! assert (nfun > 0);
423 %! assert (err < sqrt (eps ("single")));
424 
425 %!function y = __f (x)
426 %! y = x .* sin (1 ./ x) .* sqrt (abs (1 - x));
427 %!endfunction
428 
429 %!test
430 %! [v, ier, nfun, err] = quad ("__f", 0.001, 3);
431 %! assert (ier == 0 || ier == 1);
432 %! assert (v, 1.98194120273598, sqrt (eps));
433 %! assert (nfun > 0);
434 
435 %!test
436 %! [v, ier, nfun, err] = quad ("__f", single (0.001), single (3));
437 %! assert (ier == 0 || ier == 1);
438 %! assert (v, 1.98194120273598, sqrt (eps ("single")));
439 %! assert (nfun > 0);
440 
441 %!error quad ()
442 %!error quad ("__f", 1, 2, 3, 4, 5)
443 
444 %!test
445 %! quad_options ("absolute tolerance", eps);
446 %! assert (quad_options ("absolute tolerance") == eps);
447 
448 %!error quad_options (1, 2, 3)
449 */
OCTINTERP_API octave_value_list feval(const std::string &name, const octave_value_list &args=octave_value_list(), int nargout=0)
virtual double integrate(void)
Definition: Quad.h:55
fname
Definition: load-save.cc:767
virtual float float_integrate(void)
Definition: Quad.h:62
OCTINTERP_API void print_usage(void)
Definition: defun.cc:54
identity matrix If supplied two scalar respectively For allows like xample val
Definition: data.cc:4986
double quad_user_function(double x)
Definition: quad.cc:58
void error(const char *fmt,...)
Definition: error.cc:578
bool isinf(double x)
Definition: lo-mappers.h:225
IntegralType
Definition: Quad.h:162
#define DEFMETHODX(name, fname, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method with certain internal name.
Definition: defun.h:168
i e
Definition: data.cc:2591
std::string unique_symbol_name(const std::string &basename)
Definition: variables.cc:498
calling an anonymous function involves an overhead quite comparable to the overhead of an m file function Passing a handle to a built in function is because the interpreter is not involved in the internal loop For a
Definition: cellfun.cc:400
float quad_float_user_function(float x)
Definition: quad.cc:94
double tmp
Definition: data.cc:6252
octave_value retval
Definition: data.cc:6246
#define panic_impossible()
Definition: error.h:40
Definition: Quad.h:119
static bool warned_imaginary
Definition: quad.cc:52
static octave_function * quad_fcn
Definition: quad.cc:49
void warning(const char *fmt,...)
Definition: error.cc:801
OCTAVE_EXPORT octave_value_list Fquad(octave::interpreter &interp, const octave_value_list &args, int) ar
Definition: quad.cc:173
octave::unwind_protect frame
Definition: graphics.cc:12190
void err_user_supplied_eval(const char *name)
Definition: errwarn.cc:148
OCTAVE_EXPORT octave_value_list isa nd deftypefn *return ovl(args(0).isinteger())
octave_function * extract_function(const octave_value &arg, const std::string &warn_for, const std::string &fname, const std::string &header, const std::string &trailer)
Definition: variables.cc:125
static int call_depth
Definition: quad.cc:55
b
Definition: cellfun.cc:400
void add_method(T *obj, void(T::*method)(void))
args.length() nargin
Definition: file-io.cc:589
void clear_function(const std::string &name)
Definition: symtab.h:392
virtual octave_function * function_value(bool silent=false)
Definition: ov-base.cc:871
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:366
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888
F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE * x