PathFinder is a Matlab/Octave toolbox for the numerical evaluation of highly oscillatory integrals. Specifically, PathFinder can efficiently evaluate integrals of the form
where
PathFinder is based on steepest descent contour deformation, but it can be easily used without a deep understanding of the underlying mathematics; it is sufficient to understand the conditions in the previous paragraph. This document intends to provide a full explanation of how to use PathFinder, without giving details of the mathematical processes going on beneath. If you would like to understand how it all works, full details of the algorithm can be found in [1], with some additional information in the 'extra_notes' subfolder. Alternatively, a brief overview can be found below.
- Setup and installation
- Using PathFinder
- Examples
- Advanced usage
- The idea behind the Numerical Steepest Descent (NSD) method and PathFinder
After downloading the source code from GitHub, open Matlab/Octave and navigate to the PathFinder folder. Then run addPaths.m
to add all necessary paths to the Matlab search path.
The more computationally expensive parts of PathFinder have been written in C, to compile as MEX functions (Matlab executable). When running Matlab on Windows, this requires an extra step.
Compile the MEX codes for your system by running compileAll.m
. This works with Octave or Matlab. and only needs to be done once for each.
If there are any compilation issues:
- Please raise an issue on the GitHub issues page, stating your Matlab/Octave version and operating system.
- Set the optional argument
'use mex'
tofalse
in future PathFinder calls to avoid the use of MEX functions. More information about how to provide optional arguments is given below.
To test the code works, try running
PathFinder(-1, 1, @(x) sin(x), [1 0 0], 100, 10, 'plot')
This approximates the integral
and will produce a simple plot of the contour deformation.
You could also try running the codes in the 'examples' subfolder.
If compiling in Matlab on Windows, first download the MinGW-w64 Compiler. Then make sure it is set to default, by typing
mex -setup C
into the Matlab terminal and following on-screen instructions. Now run
CompileAll.m
In Octave, this shouldn't be an issue, as MinGW-w64 compiler is used by default.
When
I = PathFinder(a, b, f, polyCoeffs, freq, numPts);
Referring to the integral (1):
-
a
andb
correspond to endpoints$a$ and$b$ . For now, we assume that they are finite - the syntax changes slightly when they are infinite. -
f
is a vectorised function handle representing the function$f(z)$ -
polyCoeffs
are the coefficients of the phase function$g(z)$
Most of the PathFiner algorithm is spent constructing a quadrature rule. For some applications, you may want to reuse the same quadrature rule for multiple functions
To access the quadrature weights and nodes, a different routine can be called, with almost the same inputs:
[z, w] = PathFinderQuad(a, b, polyCoeffs, freq, numPts);
All optional inputs described throughout this document can be used in PathFinderQuad
.
Here we use the optional input infcontour
, followed by a boolean vector of two entries. If true, this tells PathFinder that the first and/or second inputs, respectively
For example, if
PathFinder(theta, b, f, poly_coeffs, freq, num_pts,'infcontour', [true false])
Suppose that
For example, the integral
has endpoints at
PathFinder(pi,0,[],[1 0 0],100,10,'infcontour',[true true])
and
PathFinder(5*pi/4,pi/4,[],[1 0 0],100,10,'infcontour',[true true])
However, if the infinite endpoints are in a sector of the complex plane where the integrand grows exponentially, the integral does not converge, and PathFinder will return an error.
An integral may be highly oscillatory for a frequency parameter
It is also worth noting that PathFinder is useful for many low-frequency integrals over unbounded complex contours. These can be problematic when approximating from scratch, even when 'brute force' numerical methods are used, as care must be taken to avoid regions where the integrand is growing exponentially.
It can be interesting to see the contour deformation being performed by PathFinder. Some understanding of the underlying mathematics is required to correctly interpret these plots, but in any case, they usually look quite nice. On the other hand, if you are trying to learn how PathFnder works, these plots can be a useful way to do that.
To produce a plot of the steepest descent deformation, use the optional input 'plot'
. For example:
PathFinderQuad(a, b, f, poly_coeffs, freq, num_pts, 'plot')
For example, the code
PathFinder(-1, 1, [], [1 -0.5 0.5 0 -1 0], 50, 25, 'plot');
produces the following plot: Here's a breakdown of the components:
- The contour approximations are in black.
- The balls are in grey
- The valleys are in grey
- The quadrature points are in red
- The stationary points are black stars
To produce the graph representation of the contour deformation, use
PathFinder(a, b, f, poly_coeffs, freq, num_pts,'plot graph')
For example, the code
PathFinder(-1, 1, [], [1 -0.5 0.5 0 -1 0], 50, 25, 'plot graph');
Further examples of plots can be found in the 'examples' subfolder, and in 1.
In the 'examples' subfolder, we provide sample codes using PathFinder. These are summarised below:
-
plotEGs.m
produces the contour deformation plot and graph shown above. -
stdComparison.m
is an example where the phase$g$ has coefficients based on the digits of$\pi$ . First, the contour deformation is plotted for a range of$\omega$ , to demonstrate how this is affected by frequency (in contrast to standard steepest descent approaches). Second, the performance of PathFinder is compared to Matlab/Octave'sintegral
routine. The digits of agreement and CPU time are compared. -
airyApprox
uses PathFinder to approximate the Airy function of the first and second kind, based on the integral representation [2, 9.5.4-9.5.5]. First, this approximation is validated against the Matlab/Octave routineairy
. Second, PathFinder's contour deformation is plotted for a range of input arguments, showing different topological behaviour. -
cuspCatastrophe.m
produces a plot of the Pearcey/Cusp canonical Catastrophe integral [2, 36.2.4]. This is an interesting application of PathFinder, as each point in$\mathbb{R}^2$ corresponds to a different phase function. -
swallowtailCatastrophe.m
produces various slice plots of the Swallowtail Catastrophe integral [2, 36.2.4]. Now each point in$\mathbb{R}^3$ corresponds to a different phase function. -
Octave_notebook_egs.ipynb
is an Octave notebook hosted by MyBinder, which can be run in a browser, without having to download any software. As we are unable to compile MEX functions in this remote container, these examples are much slower than they would be running locally.
The optional inputs 'plot'
, 'plot graph'
and 'infcontour'
are just three of many adjustable parameters. In fact, all of the algorithm parameters defined in 1, Table 4.1 can be easily modified, if the user wishes to do so. Here is a list of adjustable parameters - each can be adjusted by adding the optional input as a text string, followed by the new value.
Parameter | Meaning | Default |
---|---|---|
C_ball |
Governs maximum number of oscillations across each non-oscillatory ball (and hence the ball radius) | |
N_ball |
Number of rays used when determining the ball radius | 16 |
delta_ball |
Governs when overlapping balls should be amalgamated |
|
delta_ODE |
Governs the local step size in the ODE solver for SD path tracing | |
delta_coarse |
Tolerance for the increment in the Newton iteration in the SD path tracing | |
delta_fine |
Tolerance for the increment in the Newton iteration in the quadrature | |
delta_quad |
Governs when the contribution from an integral on the quasi-SD deformation is computed | |
inf quad rule |
Determines which quadrature rule is used for the SD contours, from a choice of Gauss-Laguerre 'laguerre' , or truncated Gauss-Legendre 'legendre'
|
'laguerre' |
use mex |
Determines if the MEX codes are to be used for higher performance. Should only be turned off if there is some issue with the MEX compiler, as described above. | true |
There is also the option to record information about the algorithm at each step. This can be achieved by adding the optional input 'log'
. A log file is then generated, and the name is based on the time and date that the file was created. For example:
PathFinder(-1, 1, @(x) x.^2, [1 -0.5 0.5 0 -1 0], 50, 10, 'log');
If you want to name it something different, use the option 'log name'
followed by a text string argument with the desired name. For example:
PathFinder(-1, 1, @(x) x.^2, [1 -0.5 0.5 0 -1 0], 50, 10, 'log name','el_murad');
For efficiency, PathFinder uses hard-coded Gaussian quadrature to evaluate contour integrals. By default, these hard-coded values have been stored for up to 100 points each in the subroutines src/gauss_quadrature_rules/gausLagHC.m
and src/gauss_quadrature_rules/gausLegHC.m
, corresponding to Gauss-Laguerre and Gauss-Legendre respectively.
If more points are requested by PathFinder, these are generated from scratch using the Golub-Welsch algorithm, with code supplied by Dirk Laurie (22/6/1998); later edited by Walter Gautschi (4/4/2002).
If desired, you can increase the number of hard-coded points, overwriting the subroutines gausLagHC
and gausLegHC
. To do this:
- Change the working directory to
src/gauss_quadrature_rules
- Open the script(s)
hardCodeQuadratureGaulag
and/orhardCodeQuadratureGauleg
as required - Adjust the parameter
maxNumHardCodedPts
as required, and run the script.
PathFinder is designed to be used by non-experts, who do not understand how it works. But if you'd like a brief explanation of what's going on underneath - here we briefly outline how PathFinder works.
Steepest descent contours are directed complex contours, along which
- Away from stationary points, PathFinder constructs steepest descent contours using an ODE solver combined with a Newton correction.
- Close to stationary points, where the integrand is non-oscillatory, PathFinder connects the endpoints of different steepest descent contours using straight line contours.
- The contours obtained are used to build a graph, the shortest path through which (connecting the endpoints
$a$ and$b$ ) is chosen via Dijkstra's algorithm. This step uses the code [3]. - Quadrature points are allocated along the contours in the shortest path (Gauss-Legendre for the straight-line contours near the stationary points and Gauss-Laguerre for the steepest descent contours).
In the paper [1], certain details of rootfinding processes are skipped for brevity. Full details of these can be found in documents/rootfinding_notes.md
.
I am very grateful for the guidance and advice of Daan Huybrechs and David Hewett throughout the development of this software. I am also grateful for financial support from KU Leuven project C14/15/05 and EPSRC projects EP/S01375X/1, EP/V053868/.
Some of the code in PathFinder relies on other projects. I am grateful to Dimas Aryo, whose code [3] is used for the Dijkstra shortest path algorithm. Copyright information for this code can be found in: src/shortest_path/dijkstra/license.txt
. I am also grateful to Dirk Laurie and Walter Gautschi for writing the Golub-Welsch algorithm used to generate Gaussian quadrature rules.