saddlepoint-expansions 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- saddlepoint_expansions-0.2.1/.github/workflows/ci.yml +55 -0
- saddlepoint_expansions-0.2.1/.github/workflows/publish.yml +42 -0
- saddlepoint_expansions-0.2.1/.gitignore +8 -0
- saddlepoint_expansions-0.2.1/LICENSE +21 -0
- saddlepoint_expansions-0.2.1/PKG-INFO +134 -0
- saddlepoint_expansions-0.2.1/README.md +84 -0
- saddlepoint_expansions-0.2.1/docs/api.md +58 -0
- saddlepoint_expansions-0.2.1/docs/conf.py +28 -0
- saddlepoint_expansions-0.2.1/docs/guide.md +185 -0
- saddlepoint_expansions-0.2.1/docs/index.md +60 -0
- saddlepoint_expansions-0.2.1/pyproject.toml +45 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/__init__.py +41 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/api.py +185 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/bivariate.py +332 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/cgf.py +265 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/cgfs.py +266 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/multivariate.py +636 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/normal.py +43 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/ratios.py +134 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/studentized.py +502 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/taylor.py +527 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/univariate.py +131 -0
- saddlepoint_expansions-0.2.1/src/saddlepoint_expansions/util.py +193 -0
- saddlepoint_expansions-0.2.1/tests/_dickey_fuller.py +124 -0
- saddlepoint_expansions-0.2.1/tests/reference_values.csv +410 -0
- saddlepoint_expansions-0.2.1/tests/test_against_julia.py +274 -0
- saddlepoint_expansions-0.2.1/tests/test_api.py +201 -0
- saddlepoint_expansions-0.2.1/tests/test_studentized.py +100 -0
- saddlepoint_expansions-0.2.1/tests/test_taylor.py +126 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
tags: ['*']
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
name: Python ${{ matrix.python-version }} - ${{ matrix.os }}
|
|
14
|
+
runs-on: ${{ matrix.os }}
|
|
15
|
+
timeout-minutes: 40
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
os: [ubuntu-latest]
|
|
20
|
+
python-version: ['3.10', '3.13']
|
|
21
|
+
include:
|
|
22
|
+
- os: macos-latest
|
|
23
|
+
python-version: '3.12'
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- uses: actions/setup-python@v5
|
|
27
|
+
with:
|
|
28
|
+
python-version: ${{ matrix.python-version }}
|
|
29
|
+
cache: pip
|
|
30
|
+
- name: Install
|
|
31
|
+
run: python -m pip install --upgrade pip && python -m pip install -e ".[test]"
|
|
32
|
+
- name: Test
|
|
33
|
+
run: pytest -q
|
|
34
|
+
docs:
|
|
35
|
+
name: Documentation
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
permissions:
|
|
38
|
+
contents: write
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- uses: actions/setup-python@v5
|
|
42
|
+
with:
|
|
43
|
+
python-version: '3.12'
|
|
44
|
+
cache: pip
|
|
45
|
+
- name: Install
|
|
46
|
+
run: python -m pip install --upgrade pip && python -m pip install -e ".[docs]"
|
|
47
|
+
- name: Build
|
|
48
|
+
run: sphinx-build -W --keep-going -b html docs docs/_build/html
|
|
49
|
+
- name: Deploy
|
|
50
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
51
|
+
uses: peaceiris/actions-gh-pages@v4
|
|
52
|
+
with:
|
|
53
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
54
|
+
publish_dir: docs/_build/html
|
|
55
|
+
force_orphan: true
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
# Trusted publishing: PyPI accepts the upload on the strength of this workflow's identity
|
|
3
|
+
# (repository s-broda/saddlepoint-expansions, file publish.yml, environment pypi), so no token is
|
|
4
|
+
# stored anywhere. Set up once on pypi.org under Publishing, as a pending publisher for the
|
|
5
|
+
# project name saddlepoint-expansions. Runs when a GitHub release is published, or by hand.
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [published]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
name: Build sdist and wheel
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: '3.12'
|
|
19
|
+
- name: Build
|
|
20
|
+
run: |
|
|
21
|
+
python -m pip install --upgrade build twine
|
|
22
|
+
python -m build
|
|
23
|
+
python -m twine check dist/*
|
|
24
|
+
- uses: actions/upload-artifact@v4
|
|
25
|
+
with:
|
|
26
|
+
name: dist
|
|
27
|
+
path: dist/
|
|
28
|
+
publish:
|
|
29
|
+
name: Upload to PyPI
|
|
30
|
+
needs: build
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
environment:
|
|
33
|
+
name: pypi
|
|
34
|
+
url: https://pypi.org/project/saddlepoint-expansions/
|
|
35
|
+
permissions:
|
|
36
|
+
id-token: write
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/download-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: dist
|
|
41
|
+
path: dist/
|
|
42
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Simon A. Broda and Raymond Kan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: saddlepoint-expansions
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Saddlepoint expansions of any order for multivariate tail probabilities and densities, from the cumulant generating function
|
|
5
|
+
Project-URL: Repository, https://github.com/s-broda/saddlepoint-expansions
|
|
6
|
+
Project-URL: Documentation, https://s-broda.github.io/saddlepoint-expansions/
|
|
7
|
+
Author: Simon A. Broda, Raymond Kan
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 Simon A. Broda and Raymond Kan
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: Taylor-mode automatic differentiation,asymptotic expansion,orthant probability,saddlepoint approximation,tail probability
|
|
31
|
+
Classifier: Development Status :: 3 - Alpha
|
|
32
|
+
Classifier: Intended Audience :: Science/Research
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
39
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
40
|
+
Requires-Python: >=3.10
|
|
41
|
+
Requires-Dist: numpy>=1.24
|
|
42
|
+
Requires-Dist: scipy>=1.10
|
|
43
|
+
Provides-Extra: docs
|
|
44
|
+
Requires-Dist: furo; extra == 'docs'
|
|
45
|
+
Requires-Dist: myst-parser; extra == 'docs'
|
|
46
|
+
Requires-Dist: sphinx>=7; extra == 'docs'
|
|
47
|
+
Provides-Extra: test
|
|
48
|
+
Requires-Dist: pytest>=7; extra == 'test'
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
# saddlepoint-expansions
|
|
52
|
+
|
|
53
|
+
[](https://github.com/s-broda/saddlepoint-expansions/actions/workflows/ci.yml)
|
|
54
|
+
[](https://s-broda.github.io/saddlepoint-expansions/)
|
|
55
|
+
|
|
56
|
+
Saddlepoint expansions of any order for tail probabilities and densities of random vectors,
|
|
57
|
+
from the cumulant generating function alone. Python port of the Julia package
|
|
58
|
+
[SaddlepointExpansions.jl](https://github.com/s-broda/SaddlepointExpansions.jl).
|
|
59
|
+
|
|
60
|
+
The package implements the expansions of Broda and Kan, *Uniform saddlepoint expansions for
|
|
61
|
+
multivariate tail probabilities*: the orthant probability `P(X ≥ x)` of a random vector in any
|
|
62
|
+
dimension (first-order term: Kolassa 2003), its density, and the density and distribution
|
|
63
|
+
function of a ratio, with the univariate expansions of Daniels and of Hauschildt and Robinson as
|
|
64
|
+
the one-dimensional case; and those of Bao, Broda and Kan, *Inversion formulas and saddlepoint
|
|
65
|
+
expansions for Studentized means*, for the density and distribution function of `X/sqrt(Y)`
|
|
66
|
+
(`studentized_density`, `studentized_tailprob`, `studentized_quantile`). The expansion of order `M` has relative error `O(n^-M)`, uniformly for
|
|
67
|
+
saddlepoints in the closed positive orthant; saddlepoints with negative components are handled
|
|
68
|
+
by inclusion-exclusion.
|
|
69
|
+
|
|
70
|
+
The cumulant generating function is supplied as an ordinary Python function written with numpy.
|
|
71
|
+
All its derivatives are generated by Taylor-mode automatic differentiation, with a small
|
|
72
|
+
truncated-polynomial algebra included in the package (no dependency beyond numpy and scipy),
|
|
73
|
+
directly in the whitened coordinates in which the expansions are evaluated.
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import numpy as np
|
|
77
|
+
from saddlepoint_expansions import CGF, sample_mean, tailprob, density
|
|
78
|
+
|
|
79
|
+
# X = (U0 + U1, U0 + U2), U_i independent unit exponentials; the mean of n = 5 observations
|
|
80
|
+
K = CGF(lambda t: -np.log(1 - t[0]) - np.log(1 - t[1]) - np.log(1 - t[0] - t[1]), 2,
|
|
81
|
+
domain=lambda t: t[0] < 1 and t[1] < 1 and t[0] + t[1] < 1)
|
|
82
|
+
Kn = sample_mean(K, 5)
|
|
83
|
+
|
|
84
|
+
tailprob(Kn, [3.0, 3.0], order=4)["F"] # P(X̄₁ ≥ 3, X̄₂ ≥ 3), orders 1 to 4; exact 0.02215196
|
|
85
|
+
density(Kn, [3.0, 3.0], order=4)["f"] # the joint density
|
|
86
|
+
tailprob(Kn, [3.0, 3.0], lower=True)["F"] # P(X̄₁ ≤ 3, X̄₂ ≤ 3)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
A library of cumulant generating functions (normal, gamma, noncentral chi-squared, quadratic
|
|
90
|
+
forms in normal variables, Wishart diagonals, sums of exponentials) and transformations
|
|
91
|
+
(reflection, marginals, linear combinations, sample means, independent sums) is included, and
|
|
92
|
+
the lower-level derivative interface (`spa_bi`, `spa_mv`, `spa_orthant`, ...) remains available
|
|
93
|
+
for models with closed-form derivatives. A `CGF` can also carry its Taylor polynomials in closed
|
|
94
|
+
form (`quadratic_forms_cgf` does, by the trace recursion of the paper), and `ratio_density` and
|
|
95
|
+
`ratio_tailprob` handle vectors of ratios.
|
|
96
|
+
|
|
97
|
+
## Installation
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
pip install git+https://github.com/s-broda/saddlepoint-expansions
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Python 3.10 or later, numpy and scipy.
|
|
104
|
+
|
|
105
|
+
## Differences from the Julia package
|
|
106
|
+
|
|
107
|
+
Everything is in double precision. The Julia package also runs in arbitrary precision
|
|
108
|
+
(`BigFloat`), uses an exact recursion for the orthant integrals in that case, and repeats
|
|
109
|
+
densities in three or more dimensions, and the Studentized-mean density, in higher precision when
|
|
110
|
+
the terms cancel strongly; here the cancellation indicator (`info["kappa_pdf"]`, `info["kappa"]`)
|
|
111
|
+
is reported instead. The
|
|
112
|
+
Julia package is the reference implementation: the test suite of this package compares every
|
|
113
|
+
function with values computed by it (`tests/reference_values.csv`, written by
|
|
114
|
+
`test/reference_values.jl` of the Julia package and shared with the MATLAB counterpart).
|
|
115
|
+
|
|
116
|
+
## Documentation and tests
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
pip install -e ".[docs,test]"
|
|
120
|
+
sphinx-build -b html docs docs/_build/html
|
|
121
|
+
pytest
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Citation
|
|
125
|
+
|
|
126
|
+
> Broda, S. A. and Kan, R. Uniform saddlepoint expansions for multivariate tail probabilities.
|
|
127
|
+
|
|
128
|
+
and, for the Studentized-mean functions,
|
|
129
|
+
|
|
130
|
+
> Bao, Y., Broda, S. A. and Kan, R. Inversion formulas and saddlepoint expansions for Studentized means.
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT; see `LICENSE`.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# saddlepoint-expansions
|
|
2
|
+
|
|
3
|
+
[](https://github.com/s-broda/saddlepoint-expansions/actions/workflows/ci.yml)
|
|
4
|
+
[](https://s-broda.github.io/saddlepoint-expansions/)
|
|
5
|
+
|
|
6
|
+
Saddlepoint expansions of any order for tail probabilities and densities of random vectors,
|
|
7
|
+
from the cumulant generating function alone. Python port of the Julia package
|
|
8
|
+
[SaddlepointExpansions.jl](https://github.com/s-broda/SaddlepointExpansions.jl).
|
|
9
|
+
|
|
10
|
+
The package implements the expansions of Broda and Kan, *Uniform saddlepoint expansions for
|
|
11
|
+
multivariate tail probabilities*: the orthant probability `P(X ≥ x)` of a random vector in any
|
|
12
|
+
dimension (first-order term: Kolassa 2003), its density, and the density and distribution
|
|
13
|
+
function of a ratio, with the univariate expansions of Daniels and of Hauschildt and Robinson as
|
|
14
|
+
the one-dimensional case; and those of Bao, Broda and Kan, *Inversion formulas and saddlepoint
|
|
15
|
+
expansions for Studentized means*, for the density and distribution function of `X/sqrt(Y)`
|
|
16
|
+
(`studentized_density`, `studentized_tailprob`, `studentized_quantile`). The expansion of order `M` has relative error `O(n^-M)`, uniformly for
|
|
17
|
+
saddlepoints in the closed positive orthant; saddlepoints with negative components are handled
|
|
18
|
+
by inclusion-exclusion.
|
|
19
|
+
|
|
20
|
+
The cumulant generating function is supplied as an ordinary Python function written with numpy.
|
|
21
|
+
All its derivatives are generated by Taylor-mode automatic differentiation, with a small
|
|
22
|
+
truncated-polynomial algebra included in the package (no dependency beyond numpy and scipy),
|
|
23
|
+
directly in the whitened coordinates in which the expansions are evaluated.
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import numpy as np
|
|
27
|
+
from saddlepoint_expansions import CGF, sample_mean, tailprob, density
|
|
28
|
+
|
|
29
|
+
# X = (U0 + U1, U0 + U2), U_i independent unit exponentials; the mean of n = 5 observations
|
|
30
|
+
K = CGF(lambda t: -np.log(1 - t[0]) - np.log(1 - t[1]) - np.log(1 - t[0] - t[1]), 2,
|
|
31
|
+
domain=lambda t: t[0] < 1 and t[1] < 1 and t[0] + t[1] < 1)
|
|
32
|
+
Kn = sample_mean(K, 5)
|
|
33
|
+
|
|
34
|
+
tailprob(Kn, [3.0, 3.0], order=4)["F"] # P(X̄₁ ≥ 3, X̄₂ ≥ 3), orders 1 to 4; exact 0.02215196
|
|
35
|
+
density(Kn, [3.0, 3.0], order=4)["f"] # the joint density
|
|
36
|
+
tailprob(Kn, [3.0, 3.0], lower=True)["F"] # P(X̄₁ ≤ 3, X̄₂ ≤ 3)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
A library of cumulant generating functions (normal, gamma, noncentral chi-squared, quadratic
|
|
40
|
+
forms in normal variables, Wishart diagonals, sums of exponentials) and transformations
|
|
41
|
+
(reflection, marginals, linear combinations, sample means, independent sums) is included, and
|
|
42
|
+
the lower-level derivative interface (`spa_bi`, `spa_mv`, `spa_orthant`, ...) remains available
|
|
43
|
+
for models with closed-form derivatives. A `CGF` can also carry its Taylor polynomials in closed
|
|
44
|
+
form (`quadratic_forms_cgf` does, by the trace recursion of the paper), and `ratio_density` and
|
|
45
|
+
`ratio_tailprob` handle vectors of ratios.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
pip install git+https://github.com/s-broda/saddlepoint-expansions
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Python 3.10 or later, numpy and scipy.
|
|
54
|
+
|
|
55
|
+
## Differences from the Julia package
|
|
56
|
+
|
|
57
|
+
Everything is in double precision. The Julia package also runs in arbitrary precision
|
|
58
|
+
(`BigFloat`), uses an exact recursion for the orthant integrals in that case, and repeats
|
|
59
|
+
densities in three or more dimensions, and the Studentized-mean density, in higher precision when
|
|
60
|
+
the terms cancel strongly; here the cancellation indicator (`info["kappa_pdf"]`, `info["kappa"]`)
|
|
61
|
+
is reported instead. The
|
|
62
|
+
Julia package is the reference implementation: the test suite of this package compares every
|
|
63
|
+
function with values computed by it (`tests/reference_values.csv`, written by
|
|
64
|
+
`test/reference_values.jl` of the Julia package and shared with the MATLAB counterpart).
|
|
65
|
+
|
|
66
|
+
## Documentation and tests
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
pip install -e ".[docs,test]"
|
|
70
|
+
sphinx-build -b html docs docs/_build/html
|
|
71
|
+
pytest
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Citation
|
|
75
|
+
|
|
76
|
+
> Broda, S. A. and Kan, R. Uniform saddlepoint expansions for multivariate tail probabilities.
|
|
77
|
+
|
|
78
|
+
and, for the Studentized-mean functions,
|
|
79
|
+
|
|
80
|
+
> Bao, Y., Broda, S. A. and Kan, R. Inversion formulas and saddlepoint expansions for Studentized means.
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT; see `LICENSE`.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# API reference
|
|
2
|
+
|
|
3
|
+
## Cumulant generating functions and the high-level interface
|
|
4
|
+
|
|
5
|
+
```{eval-rst}
|
|
6
|
+
.. autoclass:: saddlepoint_expansions.CGF
|
|
7
|
+
:members: __add__
|
|
8
|
+
|
|
9
|
+
.. autofunction:: saddlepoint_expansions.derivatives
|
|
10
|
+
.. autofunction:: saddlepoint_expansions.saddlepoint
|
|
11
|
+
.. autofunction:: saddlepoint_expansions.tailprob
|
|
12
|
+
.. autofunction:: saddlepoint_expansions.density
|
|
13
|
+
.. autofunction:: saddlepoint_expansions.ratio_density
|
|
14
|
+
.. autofunction:: saddlepoint_expansions.ratio_tailprob
|
|
15
|
+
.. autofunction:: saddlepoint_expansions.studentized_density
|
|
16
|
+
.. autofunction:: saddlepoint_expansions.studentized_tailprob
|
|
17
|
+
.. autofunction:: saddlepoint_expansions.studentized_quantile
|
|
18
|
+
.. autofunction:: saddlepoint_expansions.studentized_saddlepoint
|
|
19
|
+
.. autofunction:: saddlepoint_expansions.studentized_locus
|
|
20
|
+
.. autofunction:: saddlepoint_expansions.reflect
|
|
21
|
+
.. autofunction:: saddlepoint_expansions.marginal
|
|
22
|
+
.. autofunction:: saddlepoint_expansions.linear_combination
|
|
23
|
+
.. autofunction:: saddlepoint_expansions.sample_mean
|
|
24
|
+
.. autofunction:: saddlepoint_expansions.shift
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Library of cumulant generating functions
|
|
28
|
+
|
|
29
|
+
```{eval-rst}
|
|
30
|
+
.. automodule:: saddlepoint_expansions.cgfs
|
|
31
|
+
:members:
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## The derivative interface
|
|
35
|
+
|
|
36
|
+
```{eval-rst}
|
|
37
|
+
.. autofunction:: saddlepoint_expansions.spa_saddle
|
|
38
|
+
.. autofunction:: saddlepoint_expansions.spa_uni
|
|
39
|
+
.. autofunction:: saddlepoint_expansions.spa_ratio_pdf
|
|
40
|
+
.. autofunction:: saddlepoint_expansions.spa_bi
|
|
41
|
+
.. autofunction:: saddlepoint_expansions.spa_bi_at
|
|
42
|
+
.. autofunction:: saddlepoint_expansions.spa_orthant2
|
|
43
|
+
.. autofunction:: saddlepoint_expansions.spa_mv
|
|
44
|
+
.. autofunction:: saddlepoint_expansions.spa_mv_at
|
|
45
|
+
.. autofunction:: saddlepoint_expansions.spa_orthant
|
|
46
|
+
.. autofunction:: saddlepoint_expansions.orthant2
|
|
47
|
+
.. autofunction:: saddlepoint_expansions.orthant3
|
|
48
|
+
.. autofunction:: saddlepoint_expansions.kfun
|
|
49
|
+
.. autofunction:: saddlepoint_expansions.kwhite
|
|
50
|
+
.. autofunction:: saddlepoint_expansions.ratio_mv
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## The Taylor algebra
|
|
54
|
+
|
|
55
|
+
```{eval-rst}
|
|
56
|
+
.. automodule:: saddlepoint_expansions.taylor
|
|
57
|
+
:members: TPoly, Algebra, get_algebra, variables, from_coefficients, sincu, sinhc, DomainError
|
|
58
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
sys.path.insert(0, os.path.abspath("../src"))
|
|
5
|
+
|
|
6
|
+
project = "saddlepoint-expansions"
|
|
7
|
+
author = "Simon A. Broda and Raymond Kan"
|
|
8
|
+
copyright = "2026, Simon A. Broda and Raymond Kan"
|
|
9
|
+
release = "0.1.0"
|
|
10
|
+
|
|
11
|
+
extensions = [
|
|
12
|
+
"sphinx.ext.autodoc",
|
|
13
|
+
"sphinx.ext.napoleon",
|
|
14
|
+
"sphinx.ext.mathjax",
|
|
15
|
+
"sphinx.ext.intersphinx",
|
|
16
|
+
"myst_parser",
|
|
17
|
+
]
|
|
18
|
+
myst_enable_extensions = ["dollarmath", "amsmath"]
|
|
19
|
+
autodoc_member_order = "bysource"
|
|
20
|
+
autodoc_typehints = "none"
|
|
21
|
+
intersphinx_mapping = {
|
|
22
|
+
"python": ("https://docs.python.org/3", None),
|
|
23
|
+
"numpy": ("https://numpy.org/doc/stable/", None),
|
|
24
|
+
}
|
|
25
|
+
html_theme = "furo"
|
|
26
|
+
html_title = "saddlepoint-expansions"
|
|
27
|
+
source_suffix = {".rst": "restructuredtext", ".md": "markdown"}
|
|
28
|
+
exclude_patterns = ["_build"]
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Guide
|
|
2
|
+
|
|
3
|
+
## Cumulant generating functions
|
|
4
|
+
|
|
5
|
+
A {class}`~saddlepoint_expansions.CGF` wraps a Python function `K(t)` of a numpy vector `t` of
|
|
6
|
+
length $d$ (also for $d = 1$, where you write `t[0]`), together with the dimension and,
|
|
7
|
+
optionally, a predicate describing the domain:
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
K = CGF(lambda t: -3*np.log(1 - t[0]) - 2*np.log(1 - 2*t[1]), 2,
|
|
11
|
+
domain=lambda t: t[0] < 1 and t[1] < 1/2)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The function is evaluated on vectors of truncated multivariate Taylor polynomials
|
|
15
|
+
({class}`~saddlepoint_expansions.taylor.TPoly`) to generate its derivatives, so it must be
|
|
16
|
+
written generically: arithmetic, `np.exp`, `np.log`, `np.sqrt`, powers, `np.sum`, products
|
|
17
|
+
with constant matrices and vectors (`A @ t`), `np.sin`, `np.cos`, `np.tan`, `np.sinh`,
|
|
18
|
+
`np.cosh`, `np.tanh`, `np.arctan` all work, and {func}`~saddlepoint_expansions.sinhc` and
|
|
19
|
+
{func}`~saddlepoint_expansions.sincu` give $\sinh(x)/x$ and $\sin(x)/x$ with their removable
|
|
20
|
+
singularity at zero (write `np.log(sinhc(t[0]))` for the uniform distribution, not
|
|
21
|
+
`np.log(np.sinh(t[0])/t[0])`, which is $0/0$ at the origin where the saddlepoint iteration starts); `math.log`, `float(...)` and calls into
|
|
22
|
+
numerical libraries that require floats do not. For determinants and inverses of matrices that
|
|
23
|
+
depend on `t`, write the factorization in generic arithmetic as
|
|
24
|
+
{func}`~saddlepoint_expansions.quadratic_forms_cgf` does, or use that constructor.
|
|
25
|
+
|
|
26
|
+
Outside its domain, `K` may raise a {class}`~saddlepoint_expansions.DomainError` (`np.log` and
|
|
27
|
+
`np.sqrt` of a polynomial with a negative constant term do this automatically) or return a
|
|
28
|
+
non-finite value. When the formula is finite outside the true domain, as with $1/(1-t)$, the
|
|
29
|
+
`domain` predicate is needed; it is also cheaper than an exception.
|
|
30
|
+
|
|
31
|
+
Transformations: {func}`~saddlepoint_expansions.reflect` (sign changes),
|
|
32
|
+
{func}`~saddlepoint_expansions.marginal`, {func}`~saddlepoint_expansions.linear_combination`
|
|
33
|
+
($Y = B'X$), {func}`~saddlepoint_expansions.sample_mean` ($nK(t/n)$),
|
|
34
|
+
{func}`~saddlepoint_expansions.shift` ($X + c$), and `K1 + K2` for the sum of independent
|
|
35
|
+
vectors. The library: {func}`~saddlepoint_expansions.normal_cgf`,
|
|
36
|
+
{func}`~saddlepoint_expansions.gamma_cgf`, {func}`~saddlepoint_expansions.ncx2_cgf`,
|
|
37
|
+
{func}`~saddlepoint_expansions.exponential_sums_cgf`,
|
|
38
|
+
{func}`~saddlepoint_expansions.quadratic_forms_cgf`,
|
|
39
|
+
{func}`~saddlepoint_expansions.wishart_diag_cgf`,
|
|
40
|
+
{func}`~saddlepoint_expansions.common_component_cgf`, {func}`~saddlepoint_expansions.uniform_cgf`.
|
|
41
|
+
|
|
42
|
+
## Tail probabilities
|
|
43
|
+
|
|
44
|
+
{func}`~saddlepoint_expansions.tailprob`` (K, x, order=M)` returns the expansions of orders
|
|
45
|
+
$1, \dots, M$ of $P(X \ge x)$ in `F`, with the saddlepoint `tau`, a `status` string and the
|
|
46
|
+
full result of the underlying routine in `info`. The expansion of order $M$ keeps the terms of
|
|
47
|
+
orders $n^{-m/2}$, $m \le 2M - 1$, and has relative error $O(n^{-M})$; in practice each order
|
|
48
|
+
gains one to two digits at moderate sample sizes, and the first order is accurate to a few
|
|
49
|
+
percent.
|
|
50
|
+
|
|
51
|
+
`lower=True` gives $P(X \le x)$ and `signs` a mixed corner, e.g. `signs=[1, -1]` for
|
|
52
|
+
$P(X_1 \ge x_1, X_2 \le x_2)$. When the saddlepoint has negative components, the expansion is
|
|
53
|
+
applied through the inclusion-exclusion identity of the paper, which preserves the relative
|
|
54
|
+
error bound. In three dimensions the orthant integrals are computed by Gauss quadrature and in
|
|
55
|
+
four or more by randomized quasi-Monte Carlo (`info["err"]` is then a standard error); keyword
|
|
56
|
+
arguments are passed on to {func}`~saddlepoint_expansions.spa_mv` (`nG`, `nqmc`, ...) and, for
|
|
57
|
+
$d = 2$, to {func}`~saddlepoint_expansions.spa_orthant2` (`nG`, `logscale` for probabilities
|
|
58
|
+
below the double-precision range, ...). For $d = 1$, `x` may be a scalar.
|
|
59
|
+
|
|
60
|
+
## Densities
|
|
61
|
+
|
|
62
|
+
{func}`~saddlepoint_expansions.density`` (K, x, order=M)` returns the expansions of orders
|
|
63
|
+
$1, \dots, M$ of the density (the terms $m \le M-1$ of the expansion in powers of $n^{-1}$,
|
|
64
|
+
relative error $O(n^{-M})$); a finite sum of Hermite polynomials, no integration, any
|
|
65
|
+
dimension. In three or more dimensions the terms can cancel strongly at high orders; check
|
|
66
|
+
`info["kappa_pdf"]`, the ratio of the sum of the absolute terms to the sum (about $\log_{10}$
|
|
67
|
+
of it digits are lost). The Julia package repeats such computations in higher precision; this
|
|
68
|
+
package works in double precision.
|
|
69
|
+
|
|
70
|
+
## Ratios
|
|
71
|
+
|
|
72
|
+
For a bivariate `K` with a positive denominator $X_2 > 0$:
|
|
73
|
+
{func}`~saddlepoint_expansions.ratio_density`` (K, r)` is the Rice expansion of the density of
|
|
74
|
+
$X_1/X_2$ at $r$ through the saddlepoint of $K_W(s) = K(s, -rs)$, and
|
|
75
|
+
{func}`~saddlepoint_expansions.ratio_tailprob`` (K, r, lower=...)` is the univariate expansion
|
|
76
|
+
for $W = X_1 - rX_2$ at $0$. Example, the noncentral $F$ with $Q_1 \sim \chi^2_m(\lambda)$ and
|
|
77
|
+
$Q_2 \sim \chi^2_n$:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
K = CGF(lambda t: ncx2_cgf(m, lam)(t[:1]) + ncx2_cgf(n)(t[1:]), 2,
|
|
81
|
+
domain=lambda t: t[0] < 0.5 and t[1] < 0.5)
|
|
82
|
+
c = f*m/n
|
|
83
|
+
ratio_tailprob(K, c, lower=True)["F"] # P(F <= f), orders 1..3
|
|
84
|
+
ratio_density(K, c)["f"] * (m/n) # density of F at f
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
*Several ratios.* With a vector `r` of length $d$, `K` is the cgf of $(X_1, \dots, X_d, Y_1, \dots, Y_d)$,
|
|
88
|
+
or of $(X_1, \dots, X_d, Y)$ for a common denominator, and `ratio_density` returns the joint density
|
|
89
|
+
of $(X_1/Y_1, \dots, X_d/Y_d)$ at `r` (Theorem 4 with the amplitude expansion of Corollary 1 of
|
|
90
|
+
the paper, every Taylor coefficient from one polynomial of $K(s, -r \circ s + \tau)$), while
|
|
91
|
+
`ratio_tailprob` returns the orthant probability $P(R \ge r)$ with `lower` and `signs` as in
|
|
92
|
+
`tailprob`. The sample autocorrelations at lags one and two, for example, are two ratios of
|
|
93
|
+
quadratic forms with a common denominator:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
N = 50; Mc = np.eye(N) - np.ones((N, N))/N
|
|
97
|
+
A = lambda j: (np.diag(np.ones(N - j), j) + np.diag(np.ones(N - j), -j))/2
|
|
98
|
+
K = quadratic_forms_cgf([Mc @ A(1) @ Mc, Mc @ A(2) @ Mc, Mc])
|
|
99
|
+
ratio_density(K, [-1, -1]/np.sqrt(N), order=5)["f"]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Studentized means
|
|
103
|
+
|
|
104
|
+
For $Q = X/\sqrt{Y}$ with $Y > 0$, with `K` the joint cumulant generating function of $(X, Y)$,
|
|
105
|
+
the expansions of Bao, Broda and Kan, *Inversion formulas and saddlepoint expansions for
|
|
106
|
+
Studentized means*:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
studentized_density(K, q, order=3) # density of X/sqrt(Y) at q, orders 1..3
|
|
110
|
+
studentized_tailprob(K, q, order=3) # P(X/sqrt(Y) >= q); lower=True for P(X/sqrt(Y) <= q)
|
|
111
|
+
studentized_quantile(K, 0.05, order=3) # the q with P(X/sqrt(Y) <= q) = 0.05 under the expansion
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Unlike a ratio, $X - q\sqrt{Y}$ is not linear in $(X, Y)$, and the inversion is a double
|
|
115
|
+
integral with a kernel in $s$ and $t$. The saddlepoint $(\hat s, \hat t, \hat v)$ solves
|
|
116
|
+
$K_s = qv$, $K_t = v^2$, $qs + 2tv = 0$ ({func}`~saddlepoint_expansions.studentized_saddlepoint`);
|
|
117
|
+
the density is the bivariate amplitude expansion about it, and the distribution function is a
|
|
118
|
+
Hauschildt-Robinson type expansion in $s$ after the $t$-integral is expanded by Laplace's method,
|
|
119
|
+
all Taylor coefficients being generated from the Taylor polynomial of `K` at the saddlepoint by
|
|
120
|
+
Newton's method and composition on power series. `order=M` keeps the terms $m \le M - 1$ of the
|
|
121
|
+
density and $m \le 2M - 1$ of the distribution function (relative error $O(n^{-M})$);
|
|
122
|
+
`info["Fp"]` holds every half-order. The saddlepoints for all $q$ lie on the curve
|
|
123
|
+
$sK_s + 2tK_t = 0$ ({func}`~saddlepoint_expansions.studentized_locus`), which is how
|
|
124
|
+
`studentized_quantile` searches: along the curve each $\hat s$ gives its $q$ and its distribution
|
|
125
|
+
function directly, so that no saddlepoint equation is solved for a trial $q$.
|
|
126
|
+
|
|
127
|
+
The noncentral $t$ distribution, for example, with $X \sim N(\mu, 1)$ and
|
|
128
|
+
$Y \sim \chi^2_\nu/\nu$ independent, $n$ pairs: $\bar X/\sqrt{\bar Y} = T/\sqrt{n}$, $T$
|
|
129
|
+
noncentral $t$ with $n\nu$ degrees of freedom and noncentrality $\sqrt{n}\mu$:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
K = sample_mean(CGF(lambda t: mu*t[0] + t[0]**2/2 - nu/2*np.log(1 - 2*t[1]/nu), 2,
|
|
133
|
+
domain=lambda t: t[1] < nu/2), n)
|
|
134
|
+
studentized_tailprob(K, x/np.sqrt(n), lower=True)["F"] # P(T <= x)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The terms of high order of the density cancel by many orders of magnitude where $|\hat t|$ is
|
|
138
|
+
large; `info["kappa"]` is the cancellation indicator, and in double precision the terms beyond
|
|
139
|
+
the fourth are often unreliable there (the Julia package repeats the computation in higher
|
|
140
|
+
precision; here `status` reports `"coefficients inaccurate"`). The Taylor algebra carries complex
|
|
141
|
+
coefficients for these expansions (the Hessian of the exponent is indefinite where $\hat t < 0$).
|
|
142
|
+
|
|
143
|
+
## Closed-form Taylor polynomials
|
|
144
|
+
|
|
145
|
+
Automatic differentiation is the default, but a `CGF` can carry its Taylor polynomials in closed
|
|
146
|
+
form through the argument `taylor`, a function `taylor(t, N, M)` returning the polynomial of
|
|
147
|
+
degree `N` of $K(t + Mv)$ as a {class}`~saddlepoint_expansions.taylor.TPoly` (built from a
|
|
148
|
+
coefficient table with {func}`~saddlepoint_expansions.taylor.from_coefficients`), or `None`
|
|
149
|
+
outside the domain. {func}`~saddlepoint_expansions.quadratic_forms_cgf` uses this with the trace
|
|
150
|
+
recursion of the paper evaluated directly in the whitened directions, which makes large matrices
|
|
151
|
+
cheap, and the transformations carry the option along; pass `closedform=False` to differentiate
|
|
152
|
+
the generic formula instead. Comparisons between polynomials, or with numbers, compare the
|
|
153
|
+
constant terms, so a cgf written with an `if` on its argument can be differentiated.
|
|
154
|
+
|
|
155
|
+
## Derivatives and the saddlepoint
|
|
156
|
+
|
|
157
|
+
{func}`~saddlepoint_expansions.derivatives`` (K, t, N)` returns all derivatives
|
|
158
|
+
$K^{(\nu)}(t)$, $|\nu| \le N$, as a dict keyed by the multi-index, and
|
|
159
|
+
{func}`~saddlepoint_expansions.saddlepoint`` (K, x)` solves $K'(\tau) = x$ by Newton's method
|
|
160
|
+
with backtracking. The derivatives at the saddlepoint are generated once, to the degree $2M + 1$
|
|
161
|
+
needed by order $M$, and the whitened coefficients (of $K_Y(u) = K(L^{-T}u)$,
|
|
162
|
+
$K''(\tau) = LL'$) are generated by differentiating the composed function directly, which keeps
|
|
163
|
+
them as accurate as the original ones.
|
|
164
|
+
|
|
165
|
+
## The derivative interface
|
|
166
|
+
|
|
167
|
+
The engine can be used with derivatives supplied by the user, as `Kfun(nu, t)` returning the
|
|
168
|
+
derivative of multi-index order `nu` (a tuple) at `t`, NaN outside the domain:
|
|
169
|
+
{func}`~saddlepoint_expansions.spa_saddle`, {func}`~saddlepoint_expansions.spa_uni`,
|
|
170
|
+
{func}`~saddlepoint_expansions.spa_ratio_pdf`, {func}`~saddlepoint_expansions.spa_bi`,
|
|
171
|
+
{func}`~saddlepoint_expansions.spa_bi_at`, {func}`~saddlepoint_expansions.spa_orthant2`,
|
|
172
|
+
{func}`~saddlepoint_expansions.spa_mv`, {func}`~saddlepoint_expansions.spa_mv_at`,
|
|
173
|
+
{func}`~saddlepoint_expansions.spa_orthant`. These follow the Julia package and the MATLAB
|
|
174
|
+
programs of the paper. {func}`~saddlepoint_expansions.kfun` and
|
|
175
|
+
{func}`~saddlepoint_expansions.kwhite` turn a `CGF` into the functions they take.
|
|
176
|
+
|
|
177
|
+
## The Taylor algebra
|
|
178
|
+
|
|
179
|
+
{mod}`saddlepoint_expansions.taylor` is a self-contained implementation of truncated
|
|
180
|
+
multivariate Taylor polynomials: `variables(d, N)` returns the $d$ variables of degree $N$ as
|
|
181
|
+
an object array, and any generic function evaluated on them returns a
|
|
182
|
+
{class}`~saddlepoint_expansions.taylor.TPoly` whose `coeff(nu)` is $f^{(\nu)}(0)/\nu!$ and
|
|
183
|
+
`derivative(nu)` is $f^{(\nu)}(0)$. Products use precomputed index tables and `np.bincount`; a
|
|
184
|
+
product of two polynomials of degree 12 in three variables takes about 0.1 ms, an `exp` or `log`
|
|
185
|
+
about 1 ms.
|