About
A permissively licensed modern implementation of Gauss-Jacobi quadrature which returns the weights and nodes over the standard interval [-1, 1]
.
Usage
The most automated approach is to use the conda
environment and fpm
build:
micromamba create -f environment.yml
micromamba activate gaussjacquad
fpm build
Code
An analytic result can be obtained from the scripts
folder.
python scripts/sympy_gauss_jac.py --npts <npoints> --alpha <alpha> --beta <beta> --n_dig <precision>
fpm test
Code
Running the implemented recursion based Gauss-Jacobi can be done via:
fpm run gjp_quad_rec -- <npoints> <alpha> <beta>
fpm run gjp_quad -- <npoints> <alpha> <beta> <method>
Code
Currently the only supported methods are "rec" and "gw" (Golub Welsch).
Meson Support
A meson
build backend is also present, which makes it easier to incorporate as subprojects of projects other than those supported by fpm
.
meson setup bbdir
meson compile -C bbdir
./bbdir/gjp_quad <npoints> <alpha> <beta> <method>
Code
External Fortran libaries
We support and encourage users to generate single file versions of the algorithms here to include in their code bases. This can be done with the scripts/add_headers.py
script:
# Strips comments by default
python scripts/export_single.py --modulename "gjp_algo665"
python scripts/export_single.py --modulename "gjp_gw" --keep-comments
Code
These can be dropped into any code base or compiled as is into a shared library.
gfortran dist/gjp_algo665_single.f90 --shared -fPIC -o libgjp_algo665.so
Code
Interfaces
C/C++ header interface
We provide a header only interface, which bypasses passing strings between Fortran and C. In order to do this, there is some duplication logic in GaussJacobiQuad
and GaussJacobiQuadCInterp
.
There is also a CLI interface to the C
bound interface, c_cli_gjpq
. However, this will not be compiled by fpm
.
meson setup bbdir
meson compile -C bbdir
./bbdir/c_cli_gjpq <npoints> <alpha> <beta> <method>
Code
The C
CLI might be more pleasant in that decimals do not need to be provided explicitly for alpha
and beta
.
f2py generated interface
The ISO_C_BINDING
variant of the code is also used for a python
interface generated with f2py
. It is easiest to use with the new meson
back-end:
cd interfaces/PyInterface
f2py -c --backend meson gjquadpy.pyf \
--dep lapack \
../../src/GaussJacobiQuadCCompat.f90 \
../../src/GaussJacobiQuad.f90 \
../../src/gjp_constants.f90 \
../../src/gjp_types.f90 \
../../src/gjp_rec.f90 \
../../src/gjp_common.f90 \
../../src/gjp_lapack.f90 \
../../src/gjp_gw.f90 \
../../src/gjp_algo665.f90
Code
Once compiled, the gjpquad_cli.py
script can be used to run the code:
python gjquad_cli.py --npts 5 --alpha 2 --beta 3
Root: -6.90457750126761027E-01 Weight: 2.74101780663370022E-02
Root: -3.26519931349000647E-01 Weight: 2.12917860603648035E-01
Root: 8.23378495520349085E-02 Weight: 4.39084379443950568E-01
Root: 4.75178870612831761E-01 Weight: 3.22206565472217876E-01
Root: 7.92794294644228348E-01 Weight: 6.50476830805121059E-02
Code
Notes
- The
rec
method fails for high values of beta
so the gw
method
should be used in such situations.
algo665
is an in-place variant of gw
and is much faster when many points are needed
Benchmarks
A very preliminary set can be run once all the interfaces have been compiled:
# From $GITROOT
mv interfaces/PyInterface/gjquadpy*.so .
hyperfine --warmup 10 --export-markdown gjp_benchmarks.md \
'bbdir/gjp_quad 5 2. 3. "gw"' \
'bbdir/c_cli_gjpq 5 2 3 gw' \
'PYTHONPATH=$(pwd) python interfaces/PyInterface/gjquad_cli.py --npts 5 --alpha 2 --beta 3' \
'python scripts/scipy_gauss_jac.py --npts 5 --alpha 2 --beta 3' \
'python scripts/sympy_gauss_jac.py --npts 5 --alpha 2 --beta 3 --n_dig 15'
Code
Which gives:
ummary
bbdir/c_cli_gjpq 5 2 3 gw ran
1.03 ± 0.45 times faster than bbdir/gjp_quad 5 2. 3. "gw"
39.08 ± 11.48 times faster than PYTHONPATH=$(pwd) python interfaces/PyInterface/gjquad_cli.py --npts 5 --alpha 2 --beta 3
55.71 ± 16.51 times faster than python scripts/scipy_gauss_jac.py --npts 5 --alpha 2 --beta 3
86.73 ± 24.86 times faster than python scripts/sympy_gauss_jac.py --npts 5 --alpha 2 --beta 3 --n_dig 15
hyperfine --warmup 10 --export-markdown gjp_benchmarks.md 32.65s user 61.31s system 469% cpu 20.004 total
Code
Or in other words:
Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
gjp_quad 5 2. 3. "gw" | 2.6 ± 0.8 | 1.8 | 7.8 | 1.03 ± 0.45 |
c_cli_gjpq 5 2 3 gw | 2.5 ± 0.7 | 1.8 | 8.4 | 1.00 |
gjquad_cli.py --npts 5 --alpha 2 --beta 3 | 97.5 ± 6.3 | 95.4 | 130.8 | 39.08 ± 11.48 |
scipy_gauss_jac.py --npts 5 --alpha 2 --beta 3 | 139.0 ± 10.6 | 132.4 | 173.4 | 55.71 ± 16.51 |
sympy_gauss_jac.py --npts 5 --alpha 2 --beta 3 --n_dig 15 | 216.4 ± 1.7 | 214.7 | 219.7 | 86.73 ± 24.86 |
Which suggests what one might suspect, that there is a large overhead in calling python
, and that the C
and Fortran
variants are almost exactly as fast as each other. However, the f2py
variant is still way faster than the existing python
implementations.
hyperfine --warmup 10 --export-markdown gjp_benchmarks.md \
'PYTHONPATH=$(pwd) python interfaces/PyInterface/gjquad_cli.py --npts 5 --alpha 2 --beta 3' \
'python scripts/scipy_gauss_jac.py --npts 5 --alpha 2 --beta 3'
Summary
PYTHONPATH=$(pwd) python interfaces/PyInterface/gjquad_cli.py --npts 5 --alpha 2 --beta 3 ran
1.38 ± 0.11 times faster than python scripts/scipy_gauss_jac.py --npts 5 --alpha 2 --beta 3
Code
Development
Developing locally
A pre-commit
job is setup on CI to enforce consistent styles, so it is best to set it up locally as well (using pipx for isolation):
# Run before commiting
pipx run pre-commit run --all-files
# Or install the git hook to enforce this
pipx run pre-commit install
Code
Updating licenses
When the headers in the sources need to be updated modify add_headers.py
and run:
python scripts/add_headers.py --dirs src/ interfaces/ --ftypes "f90,f77" --cchar '!'
python scripts/add_headers.py --dirs interfaces --ftypes "c,h" --cchar '//'
python scripts/add_headers.py --dirs interfaces scripts --ftypes "py" --cchar '#'
Code
Remember to do this before exporting the code into other projects (e.g. featom).
License
MIT.
Citation
If you use this library (including the interfaces) please remember to cite it as:
Rohit Goswami. (2023). HaoZeke/GaussJacobiQuad: GaussJacobiQuad I (v0.1.0). Zenodo. https://doi.org/10.5281/ZENODO.8285112
Or use the bibtex
entry:
@software{rgGaussQuad23,
author = {Rohit Goswami},
title = {HaoZeke/GaussJacobiQuad: GaussJacobiQuad I},
month = aug,
year = 2023,
publisher = {Zenodo},
version = {v0.1.0},
doi = {10.5281/zenodo.8285112},
url = {https://doi.org/10.5281/zenodo.8285112}
}
Code
An ArXiv –> JOSS paper is in the works.