-
Notifications
You must be signed in to change notification settings - Fork 182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal for quadrature #112
Comments
I would suggest to concentrate on the API. We can use QUADPACK as an implementation, or we can use something else; either way I would consider that secondary. The API however, is essential. Here is an implementation of Gaussian quadrature that I have been using in all my codes: https://github.com/certik/hfsolver/blob/b4c50c1979fb7e468b1852b144ba756f5a51788d/src/quadrature.f90, it returns the points |
Scipy is using the QUADPACK routines and is itself under the BSD license, so it should be possible. There is a great Python package with tons of references for different quadratures: https://github.com/nschloe/quadpy For generating classic Gaussian quadratures there are a lot of legacy codes available, unfortunately none of them have permissive licenses:
Given the large palette of Gaussian quadratures perhaps they would fit better into a separate package, but simple rules like the trapezoidal and Simpson methods could be part of stdlib. |
@ivan-pi Thanks for the research! I am in the process of writing up my "vision" for this module, but I want to respond to one specific point. I think the huge variety of existing implementations should not stop us from defining canonical interfaces. For instance, we should not provide routines that are explicitly Gauss-Kronrod integrators or Clenshaw-Curtis integrators, or Bill-and-Ted integrators. We should have routines for "integrate f(x) from a to b, with both a and b finite" and "integrate w(x)*f(x) from a to infinity with w(x)=sin(c*x)". Then specific implementations can decide how to map the different cases to actual quadrature rules. Obviously, we will have to provide reference implementations, but that's OK. On the other hand, I agree that fancy quadrature routines are a good candidate for splitting off into a separate package once stdlib matures. At that point, we can open the doors to specializations like "clenshaw_curtis_quadrature.f90" and "gauss_kronrod_quadrature.f90". |
Here's a first cut at some interfaces. Trapezoidal rule for arraysIntegrate arrays with trapezoidal rule, either with implicit abscissas (assumed equally spaced) or explicit abscissas
Trapezoidal weights for given abscissas
Simpson's rule for arraysIntegrating arrays with Simpson's rule should look the same as the trapezoidal rule. The only catch is the need to do something for the case of even-length arrays. There are three approaches to choose from:
Option 1 is the simplest but also most restrictive. Option 2 is the most flexible, but a good API probably requires that all the Adaptive integration of functionsAdaptive integration of functions can be implemented in many, many ways. This is where it's most useful to specify a fairly general API and let specific implementations decide what algorithms to use. I think adaptive 1-D integration should let users request
An actual implementation does not need to honor all requests literally. For instance, if an integrator detects that the integrand decays rapidly as x->infinity, it should be allowed to truncate at a "big enough" x that it thinks will satisfy the requested tolerance. Or, if the user specifies a weight function, it should not be required that the implementation actually use that information in deciding what specific quadrature rule to use. Here are some examples of what I think adaptive integration calls should look like (not being careful about real kinds for now)
In all cases,
This is the simplest thing to start with and can be changed depending on the findings in #117. There are two further non-obvious things illustrated: weights and infinite integration limits. Weighted integrandsI suggest that weights be specified as instances of
and the definition of
There are lots of other weight functions one could consider including, e.g., the weights corresponding to the classical orthogonal polynomials. Making each one a distinct type makes it easier to implement weight-specific quadrature rules. Infinite integration bounds"Infinity" is a non-standard concept outside the IEEE modules. I opened some discussion on different mechanisms for representing infinite values in #118. Some consensus needs to be reached there before we can decide what do to for (semi-)infinite integration domains here. Non-adaptive integration of functionsThe approach @certik shared for non-adaptive quadrature looks good to me. The strategy of returning the abscissas and weights make a lot of sense. Not only does it avoid recalculation for the case of doing many similar integrals, but it also allows more flexibility in the integrand function. I think we should also offer corresponding all-in-one functions that generate the weights and abscissas internally, akin to having both One case that doesn't work well with the "explicit summation" approach is when the caller wants a rule for infinite or semi-infinite domains. This is because there is no standard way to refer to "infinity" (again, see #118). |
I think generally this API looks good to me. Regarding your last point, I agree we should do both --- returning the points and weights as well as have a higher level API that hides it from the user and instead requests the user to provide a callback. Regarding returning points and weights for an infinite domain, I've used exactly the same API as for a finite domain. Here is how it works: I only implemented a few orders. It returns the points on an infinite interval |
Hi, I've just come across this proposal. I've been working on trying to produce something similar with some of the methods I've been in contact. My idea is/was to make an interface similar to numpy/scipy in a modern fortran, with good documentation and examples. The idea is that the project is open and driven by a community, but at this point is just starting with my vision of it. So, If possible, I'd rather merge/contribute in this ongoing project with a shared vision. Currently, for integration I implemented:
The project is at https://github.com/numericfor/numfor with documentation at https://numericfor.github.io/numfor/ Of course, working alone, I've made some decision about some of the topics that are being discussed here (API, treatment of infinite, etc). |
Hi @fiolj, welcome! Yes, that would be awesome if you could contribute. Indeed, the main value of If you want to help, go ahead and help us reach an agreement regarding APIs and feel free to propose everything that you already implemented. You can follow the WORKFLOW. |
Thanks @certik, I agree with all points by @nshaffer, the above API seems sensible. I would also add that may be useful to integrate not only real but also complex functions
Other decisions (infinite limits, f(x, *args)) depend on other issues. Regarding the implementation, besides wrapping, I've been doing some refactoring of the QUADPACK routines. Probably there are some things to modify but I could help to put them in the preferred way starting from the version already in |
@fiolj That is an impressive effort! I don't have the time right now to look carefully through the repo, but it looks like there are good ideas in there I haven't considered yet. Is it the case that the real "meat" of your computational routines lives inside @certik I will get the ball rolling with a PR that includes the simplest things. For features we're still working out (e.g., adaptive quadrature), is it OK to have stub functions that just illustrate the proposed API? |
I think so. It's about getting the ball rolling. |
@nshaffer I was thinking on starting a project just like this... |
Sorry about the noise, but rethinking the API for trapz (and may be simps) may be it would make sense to implement those functions for arrays of rank larger than one, like Numpy trapz. I was thinking that may be we could use something like this for the interface:
and, similarly for the implementation:
|
@fiolj I've been thinking about that too. I support In general, though, I think we should be judicious about which routines we make rank-generic, at least for now. Implementing generic ranks makes the source very difficult to read. One also has a great many more tests to write (in principle). We are still in early stages of developing, trying different APIs and such. I think it is not a good use of our time and effort to add generic ranks to all routines that could conceivably take in a multi-dimensional array (but which cannot be made elemental). Again, for Even so, in my initial PR, I intend to only show the 1-d API. The code will be easy to read and review. With that in place, the subsequent PR (yours, if you like) to implement generic ranks will be easier to understand compared with doing it all in one shot. |
I agree with that strategy. I just didn't think on doing trapz and simps rank-generic at the time we discussed the interface, and wanted to open it for discussion.
I still am not sure what is the best strategy for collaboration on medium-sizes branches like this. I don't know if it is possilbe/desirable to update two forks in parallell and make intermediate PRs between those, or just make several PRs with small pieces of code to the main repository. |
Thinking on it some more, there are some API questions we need to resolve before implementing generic kinds for
With that established, here are two questions I'd like opinions on:
|
I like your API and implementation as it is. Regarding your two questions:
I think the combinations of option 2 are too many to make something universally useful and we, at least at this stage, keep it simple. I would not support masked integration now. |
Overview
Let's get numerical integration into stdlib. As a baseline, here's what I'd want in a general-purpose integration module.
scipy.integrate.quad
.The idea would be to provide easy-to-use and reasonably general numerical integration routines that cover 95% of what people need. Some things which are out of scope in my opinion:
I envision having two modules: one being a light f90 wrapper for QUADPACK which expert users can use, the other being the main module with a nicer interface to QUADPACK's functionality, as well as array-integrating routines.
Integrating functions
I've already written wrappers and tests for "non-expert" QUADPACK routines (link). All these do is create explicit interfaces and give names to magic numbers, e.g., error codes and integrand weight selectors. It will need a little refactoring to port to stdlib, but I am pretty confident that my module
quadpack_interface
uses all the right types, kinds, and intents to agree with the F77 routines. No need to duplicate that effort here.From a modern programmer's perspective, QUADPACK has some unattractive qualities:
qaws
implements weight functions that remove algebraic/logarithmic singularities at the integration endpoints. The type of singularity is controlled by an integer argument, whose value affects the meaning of other arguments.Solving these issues should be the motivating goal of a module that delivers high-level integration functions that call down to the appropriate QUADPACK subroutines, internally handling the magic numbers, workspace array allocation, and translating return codes to proper runtime errors if necessary. I have some design sketches that I'll talk about in a later post.
Integrating arrays
Integrating arrays is a simpler task. We just need to decide how featureful we want to be with this. For reference, NumPy/SciPy provide
numpy.trapz
: trapezoidal rule with either equidistant or arbitrary abscissas.scipy.simps
: Simpson's rule with either equidistant or arbitrary abscissas.scipy.romb
: Romberg integration, which is restricted to equidistant abscissas whose length is2**k + 1
, e.g., 1, 3, 5, 9, 17...scipy.cumtrapz
: cumulative trapezoidal rule with optional initial valueWe should have all these, except maybe Romberg integration. I don't think I have ever used it "for real", but maybe other people can make a case for keeping it. To this basic set of routines, I'd also like to add companion functions to
trapz
andsimps
that return the weights for a particular array of abscissas. This is useful for integrating many different arrays that represent functions tabulated on a common grid. Once you have the weights from, e.g.,w = trapz_weights(x)
, then computing the integral is as simple asintegral = dot_product(y, w)
.Details
real
anddouble precision
to appropriate real kindsexternal
proceduresave
?)The text was updated successfully, but these errors were encountered: