gx2 1.0.0__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.
- gx2-1.0.0/LICENSE +21 -0
- gx2-1.0.0/PKG-INFO +150 -0
- gx2-1.0.0/README.md +116 -0
- gx2-1.0.0/gx2/__init__.py +53 -0
- gx2-1.0.0/gx2/_basic.py +144 -0
- gx2-1.0.0/gx2/_convert.py +106 -0
- gx2-1.0.0/gx2/_distribution.py +363 -0
- gx2-1.0.0/gx2/_helpers.py +210 -0
- gx2-1.0.0/gx2/_methods.py +430 -0
- gx2-1.0.0/gx2/_ray.py +399 -0
- gx2-1.0.0/gx2.egg-info/PKG-INFO +150 -0
- gx2-1.0.0/gx2.egg-info/SOURCES.txt +16 -0
- gx2-1.0.0/gx2.egg-info/dependency_links.txt +1 -0
- gx2-1.0.0/gx2.egg-info/requires.txt +13 -0
- gx2-1.0.0/gx2.egg-info/top_level.txt +1 -0
- gx2-1.0.0/pyproject.toml +51 -0
- gx2-1.0.0/setup.cfg +4 -0
- gx2-1.0.0/tests/test_gx2.py +127 -0
gx2-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Abhranil Das
|
|
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.
|
gx2-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gx2
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Statistics, pdf, cdf, inverse cdf and random numbers of the generalized chi-square distribution
|
|
5
|
+
Author-email: Abhranil Das <abhranil.das@utexas.edu>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Reference paper 1, https://arxiv.org/abs/2012.14331
|
|
8
|
+
Project-URL: Reference paper 2, https://arxiv.org/abs/2404.05062
|
|
9
|
+
Keywords: generalized chi-square,chi-square,quadratic form,normal distribution,statistics
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy>=1.21
|
|
24
|
+
Requires-Dist: scipy>=1.7
|
|
25
|
+
Requires-Dist: mpmath>=1.2
|
|
26
|
+
Provides-Extra: plot
|
|
27
|
+
Requires-Dist: matplotlib>=3.4; extra == "plot"
|
|
28
|
+
Provides-Extra: test
|
|
29
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
30
|
+
Provides-Extra: docs
|
|
31
|
+
Requires-Dist: jupyter; extra == "docs"
|
|
32
|
+
Requires-Dist: matplotlib>=3.4; extra == "docs"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<img src="https://raw.githubusercontent.com/abhranildas/gx2-py/main/gx2_icon.png" alt="gx2" width="260">
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
# gx2 — Generalized chi-square distribution
|
|
40
|
+
|
|
41
|
+
`gx2` computes the statistics, characteristic function, pdf, cdf, inverse cdf
|
|
42
|
+
and random numbers of the **generalized chi-square distribution**.
|
|
43
|
+
|
|
44
|
+
A generalized chi-square variable is a weighted sum of independent non-central
|
|
45
|
+
chi-square variables plus a normal variable — equivalently, the quadratic form
|
|
46
|
+
of a normal random vector. It is parametrized by:
|
|
47
|
+
|
|
48
|
+
| parameter | meaning |
|
|
49
|
+
|-----------|---------|
|
|
50
|
+
| `w` | weights of the non-central chi-square terms |
|
|
51
|
+
| `k` | their degrees of freedom |
|
|
52
|
+
| `lambda_` | their non-centralities (named `lambda_` because `lambda` is a Python keyword) |
|
|
53
|
+
| `s` | scale (standard deviation) of the added normal term |
|
|
54
|
+
| `m` | constant offset |
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install gx2
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Requires `numpy`, `scipy` and `mpmath`. `matplotlib` is optional, for plotting
|
|
63
|
+
in the getting-started notebook.
|
|
64
|
+
|
|
65
|
+
To install from a local clone instead:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install .
|
|
69
|
+
# or, for development (editable install with test/plot extras):
|
|
70
|
+
pip install -e ".[plot,test]"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Getting started
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import gx2
|
|
77
|
+
|
|
78
|
+
w, k, lambda_, s, m = [1, -5, 2], [1, 2, 3], [2, 3, 7], 0, 5
|
|
79
|
+
|
|
80
|
+
gx2.stat(w, k, lambda_, s, m) # mean and variance
|
|
81
|
+
gx2.cdf(25, w, k, lambda_, s, m) # cdf at x = 25
|
|
82
|
+
gx2.pdf(25, w, k, lambda_, s, m) # pdf at x = 25
|
|
83
|
+
gx2.inv(0.9, w, k, lambda_, s, m) # 90th percentile
|
|
84
|
+
gx2.rnd(w, k, lambda_, s, m, size=5) # random numbers
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Open [`GettingStarted.ipynb`](GettingStarted.ipynb) for an interactive tour
|
|
88
|
+
with worked examples and plots. For any function, see its full documentation
|
|
89
|
+
with `help(gx2.cdf)` (or `gx2.cdf?` in Jupyter).
|
|
90
|
+
|
|
91
|
+
## Public functions
|
|
92
|
+
|
|
93
|
+
| function | purpose |
|
|
94
|
+
|----------|---------|
|
|
95
|
+
| `stat(w, k, lambda_, s, m)` | mean and variance |
|
|
96
|
+
| `char(t, w, k, lambda_, s, m)` | characteristic function |
|
|
97
|
+
| `rnd(w, k, lambda_, s, m, size=, method=)` | random numbers |
|
|
98
|
+
| `cdf(x, w, k, lambda_, s, m, side=, method=, ...)` | cdf |
|
|
99
|
+
| `pdf(x, w, k, lambda_, s, m, side=, method=, ...)` | pdf |
|
|
100
|
+
| `inv(p, w, k, lambda_, s, m, side=, method=, ...)` | inverse cdf |
|
|
101
|
+
| `gx2_to_norm_quad_params(w, k, lambda_, s, m)` | gx2 → quadratic-form coefficients of a standard normal |
|
|
102
|
+
| `norm_quad_to_gx2_params(mu, v, quad, merge=)` | quadratic form of a normal → gx2 parameters |
|
|
103
|
+
|
|
104
|
+
The individual computation routines (`imhof`, `ruben`, `ifft`, `pearson`,
|
|
105
|
+
`tail`, `ellipse`, `cdf_ray`, `pdf_ray`, …) and numerical helpers
|
|
106
|
+
(`log_sum_exp`, `signed_log_sum_exp`, `phi_ray`, …) are also exposed.
|
|
107
|
+
|
|
108
|
+
## Computation methods for `cdf` / `pdf`
|
|
109
|
+
|
|
110
|
+
`method='auto'` (default) picks a good method for the given parameters. You can
|
|
111
|
+
also force one:
|
|
112
|
+
|
|
113
|
+
| method | notes |
|
|
114
|
+
|--------|-------|
|
|
115
|
+
| `'imhof'` | Imhof–Davies numerical integration (`precision='basic'` or `'vpa'`) |
|
|
116
|
+
| `'ray'` | ray-trace method (`precision='basic'`, `'log'` or `'vpa'`; tune with `n_rays`, `force_mc`) |
|
|
117
|
+
| `'ifft'` | inverse-FFT method; `x='full'` returns the cdf/pdf over a spanning grid |
|
|
118
|
+
| `'ruben'` | Ruben's series — requires all `w` the same sign and `s=0` |
|
|
119
|
+
| `'tail'` | infinite-tail approximation |
|
|
120
|
+
| `'pearson'` | Pearson's 3-moment approximation |
|
|
121
|
+
| `'ellipse'` | ellipse approximation near a finite tail — requires all `w` the same sign and `s=0` |
|
|
122
|
+
|
|
123
|
+
## Usage notes
|
|
124
|
+
|
|
125
|
+
* `cdf` and `pdf` return just the probability/density by default. Pass
|
|
126
|
+
`full_output=True` (auto-enabled for `x='full'`) to also receive the error
|
|
127
|
+
estimate and, for `x='full'`, the grid of x-values.
|
|
128
|
+
* In the far tails, probabilities can fall below double precision (~1e-308).
|
|
129
|
+
The `'tail'`, `'ellipse'` and `'ray'` methods then return the **base-10
|
|
130
|
+
logarithm** of such values (a negative number); `inv` likewise accepts a
|
|
131
|
+
negative `p` as a log10 probability. `precision='log'` (the ray default) is
|
|
132
|
+
the easiest way to reach this regime.
|
|
133
|
+
* The `'ray'` method runs on the CPU with NumPy and batches automatically over
|
|
134
|
+
rays to bound memory. `precision='vpa'` uses `mpmath` and returns
|
|
135
|
+
`mpmath.mpf` objects for sub-`realmin` values.
|
|
136
|
+
|
|
137
|
+
## Author and citation
|
|
138
|
+
|
|
139
|
+
Abhranil Das, Center for Perceptual Systems, The University of Texas at Austin.
|
|
140
|
+
Bugs / comments / questions / suggestions to abhranil.das@utexas.edu.
|
|
141
|
+
|
|
142
|
+
This is the Python port of the
|
|
143
|
+
[MATLAB toolbox](https://www.mathworks.com/matlabcentral/fileexchange/85028-generalized-chi-square-distribution).
|
|
144
|
+
If you use this code, please cite:
|
|
145
|
+
- [Methods to integrate multinormals and compute classification measures](https://arxiv.org/abs/2012.14331)
|
|
146
|
+
- New methods to compute the generalized chi-square distribution: [journal](https://www.tandfonline.com/doi/abs/10.1080/00949655.2025.2501401) / [arxiv](https://arxiv.org/abs/2404.05062)
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT — see [LICENSE](LICENSE).
|
gx2-1.0.0/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/abhranildas/gx2-py/main/gx2_icon.png" alt="gx2" width="260">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# gx2 — Generalized chi-square distribution
|
|
6
|
+
|
|
7
|
+
`gx2` computes the statistics, characteristic function, pdf, cdf, inverse cdf
|
|
8
|
+
and random numbers of the **generalized chi-square distribution**.
|
|
9
|
+
|
|
10
|
+
A generalized chi-square variable is a weighted sum of independent non-central
|
|
11
|
+
chi-square variables plus a normal variable — equivalently, the quadratic form
|
|
12
|
+
of a normal random vector. It is parametrized by:
|
|
13
|
+
|
|
14
|
+
| parameter | meaning |
|
|
15
|
+
|-----------|---------|
|
|
16
|
+
| `w` | weights of the non-central chi-square terms |
|
|
17
|
+
| `k` | their degrees of freedom |
|
|
18
|
+
| `lambda_` | their non-centralities (named `lambda_` because `lambda` is a Python keyword) |
|
|
19
|
+
| `s` | scale (standard deviation) of the added normal term |
|
|
20
|
+
| `m` | constant offset |
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install gx2
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Requires `numpy`, `scipy` and `mpmath`. `matplotlib` is optional, for plotting
|
|
29
|
+
in the getting-started notebook.
|
|
30
|
+
|
|
31
|
+
To install from a local clone instead:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install .
|
|
35
|
+
# or, for development (editable install with test/plot extras):
|
|
36
|
+
pip install -e ".[plot,test]"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Getting started
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import gx2
|
|
43
|
+
|
|
44
|
+
w, k, lambda_, s, m = [1, -5, 2], [1, 2, 3], [2, 3, 7], 0, 5
|
|
45
|
+
|
|
46
|
+
gx2.stat(w, k, lambda_, s, m) # mean and variance
|
|
47
|
+
gx2.cdf(25, w, k, lambda_, s, m) # cdf at x = 25
|
|
48
|
+
gx2.pdf(25, w, k, lambda_, s, m) # pdf at x = 25
|
|
49
|
+
gx2.inv(0.9, w, k, lambda_, s, m) # 90th percentile
|
|
50
|
+
gx2.rnd(w, k, lambda_, s, m, size=5) # random numbers
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Open [`GettingStarted.ipynb`](GettingStarted.ipynb) for an interactive tour
|
|
54
|
+
with worked examples and plots. For any function, see its full documentation
|
|
55
|
+
with `help(gx2.cdf)` (or `gx2.cdf?` in Jupyter).
|
|
56
|
+
|
|
57
|
+
## Public functions
|
|
58
|
+
|
|
59
|
+
| function | purpose |
|
|
60
|
+
|----------|---------|
|
|
61
|
+
| `stat(w, k, lambda_, s, m)` | mean and variance |
|
|
62
|
+
| `char(t, w, k, lambda_, s, m)` | characteristic function |
|
|
63
|
+
| `rnd(w, k, lambda_, s, m, size=, method=)` | random numbers |
|
|
64
|
+
| `cdf(x, w, k, lambda_, s, m, side=, method=, ...)` | cdf |
|
|
65
|
+
| `pdf(x, w, k, lambda_, s, m, side=, method=, ...)` | pdf |
|
|
66
|
+
| `inv(p, w, k, lambda_, s, m, side=, method=, ...)` | inverse cdf |
|
|
67
|
+
| `gx2_to_norm_quad_params(w, k, lambda_, s, m)` | gx2 → quadratic-form coefficients of a standard normal |
|
|
68
|
+
| `norm_quad_to_gx2_params(mu, v, quad, merge=)` | quadratic form of a normal → gx2 parameters |
|
|
69
|
+
|
|
70
|
+
The individual computation routines (`imhof`, `ruben`, `ifft`, `pearson`,
|
|
71
|
+
`tail`, `ellipse`, `cdf_ray`, `pdf_ray`, …) and numerical helpers
|
|
72
|
+
(`log_sum_exp`, `signed_log_sum_exp`, `phi_ray`, …) are also exposed.
|
|
73
|
+
|
|
74
|
+
## Computation methods for `cdf` / `pdf`
|
|
75
|
+
|
|
76
|
+
`method='auto'` (default) picks a good method for the given parameters. You can
|
|
77
|
+
also force one:
|
|
78
|
+
|
|
79
|
+
| method | notes |
|
|
80
|
+
|--------|-------|
|
|
81
|
+
| `'imhof'` | Imhof–Davies numerical integration (`precision='basic'` or `'vpa'`) |
|
|
82
|
+
| `'ray'` | ray-trace method (`precision='basic'`, `'log'` or `'vpa'`; tune with `n_rays`, `force_mc`) |
|
|
83
|
+
| `'ifft'` | inverse-FFT method; `x='full'` returns the cdf/pdf over a spanning grid |
|
|
84
|
+
| `'ruben'` | Ruben's series — requires all `w` the same sign and `s=0` |
|
|
85
|
+
| `'tail'` | infinite-tail approximation |
|
|
86
|
+
| `'pearson'` | Pearson's 3-moment approximation |
|
|
87
|
+
| `'ellipse'` | ellipse approximation near a finite tail — requires all `w` the same sign and `s=0` |
|
|
88
|
+
|
|
89
|
+
## Usage notes
|
|
90
|
+
|
|
91
|
+
* `cdf` and `pdf` return just the probability/density by default. Pass
|
|
92
|
+
`full_output=True` (auto-enabled for `x='full'`) to also receive the error
|
|
93
|
+
estimate and, for `x='full'`, the grid of x-values.
|
|
94
|
+
* In the far tails, probabilities can fall below double precision (~1e-308).
|
|
95
|
+
The `'tail'`, `'ellipse'` and `'ray'` methods then return the **base-10
|
|
96
|
+
logarithm** of such values (a negative number); `inv` likewise accepts a
|
|
97
|
+
negative `p` as a log10 probability. `precision='log'` (the ray default) is
|
|
98
|
+
the easiest way to reach this regime.
|
|
99
|
+
* The `'ray'` method runs on the CPU with NumPy and batches automatically over
|
|
100
|
+
rays to bound memory. `precision='vpa'` uses `mpmath` and returns
|
|
101
|
+
`mpmath.mpf` objects for sub-`realmin` values.
|
|
102
|
+
|
|
103
|
+
## Author and citation
|
|
104
|
+
|
|
105
|
+
Abhranil Das, Center for Perceptual Systems, The University of Texas at Austin.
|
|
106
|
+
Bugs / comments / questions / suggestions to abhranil.das@utexas.edu.
|
|
107
|
+
|
|
108
|
+
This is the Python port of the
|
|
109
|
+
[MATLAB toolbox](https://www.mathworks.com/matlabcentral/fileexchange/85028-generalized-chi-square-distribution).
|
|
110
|
+
If you use this code, please cite:
|
|
111
|
+
- [Methods to integrate multinormals and compute classification measures](https://arxiv.org/abs/2012.14331)
|
|
112
|
+
- New methods to compute the generalized chi-square distribution: [journal](https://www.tandfonline.com/doi/abs/10.1080/00949655.2025.2501401) / [arxiv](https://arxiv.org/abs/2404.05062)
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""gx2 - Generalized chi-square distribution
|
|
2
|
+
============================================
|
|
3
|
+
|
|
4
|
+
Python port of the MATLAB *Generalized chi-square distribution* toolbox by
|
|
5
|
+
Abhranil Das (Center for Perceptual Systems, The University of Texas at
|
|
6
|
+
Austin). It computes the statistics, characteristic function, pdf, cdf,
|
|
7
|
+
inverse cdf and random numbers of the generalized chi-square distribution --
|
|
8
|
+
the distribution of a weighted sum of non-central chi-square variables plus a
|
|
9
|
+
normal variable, equivalently the quadratic form of a normal vector.
|
|
10
|
+
|
|
11
|
+
A generalized chi-square is parametrised by:
|
|
12
|
+
|
|
13
|
+
w weights of the non-central chi-square terms
|
|
14
|
+
k their degrees of freedom
|
|
15
|
+
lambda_ their non-centralities (named ``lambda_`` since ``lambda`` is a
|
|
16
|
+
Python keyword)
|
|
17
|
+
s scale of the added normal term
|
|
18
|
+
m offset
|
|
19
|
+
|
|
20
|
+
If you use this code, please cite:
|
|
21
|
+
1. A method to integrate and classify normal distributions
|
|
22
|
+
(https://arxiv.org/abs/2012.14331)
|
|
23
|
+
2. New methods for computing the generalized chi-square distribution
|
|
24
|
+
(https://arxiv.org/abs/2404.05062)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from ._basic import stat, char, rnd
|
|
28
|
+
from ._convert import gx2_to_norm_quad_params, norm_quad_to_gx2_params
|
|
29
|
+
from ._distribution import cdf, pdf, inv, log_cdf
|
|
30
|
+
from ._methods import (imhof, imhof_integrand, ruben, ifft,
|
|
31
|
+
pearson, cdf_pearson, tail, ellipse)
|
|
32
|
+
from ._ray import (cdf_ray, pdf_ray, ray_integrand, int_norm_ray,
|
|
33
|
+
norm_prob_across_rays, norm_prob_across_angles)
|
|
34
|
+
from ._helpers import (log_sum_exp, signed_log_sum_exp, phi_ray,
|
|
35
|
+
Phibar_ray_split, Phibar_sym, prob_ray_sym, standard_quad)
|
|
36
|
+
|
|
37
|
+
__version__ = "1.0.0"
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
# core distribution API
|
|
41
|
+
"stat", "char", "rnd", "cdf", "pdf", "inv", "log_cdf",
|
|
42
|
+
# parameter conversions
|
|
43
|
+
"gx2_to_norm_quad_params", "norm_quad_to_gx2_params",
|
|
44
|
+
# individual methods
|
|
45
|
+
"imhof", "imhof_integrand", "ruben", "ifft", "pearson",
|
|
46
|
+
"cdf_pearson", "tail", "ellipse",
|
|
47
|
+
# ray method internals
|
|
48
|
+
"cdf_ray", "pdf_ray", "ray_integrand", "int_norm_ray",
|
|
49
|
+
"norm_prob_across_rays", "norm_prob_across_angles",
|
|
50
|
+
# numerical helpers
|
|
51
|
+
"log_sum_exp", "signed_log_sum_exp", "phi_ray", "Phibar_ray_split",
|
|
52
|
+
"Phibar_sym", "prob_ray_sym", "standard_quad",
|
|
53
|
+
]
|
gx2-1.0.0/gx2/_basic.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Mean/variance, characteristic function, and random number generation.
|
|
2
|
+
Mirrors ``gx2stat.m``, ``gx2char.m`` and ``gx2rnd.m``.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from scipy.stats import ncx2, chi2, norm
|
|
7
|
+
|
|
8
|
+
from ._helpers import asrow
|
|
9
|
+
from ._convert import gx2_to_norm_quad_params
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def stat(w, k, lambda_, s, m):
|
|
13
|
+
"""Mean and variance of a generalized chi-square distribution.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
w : array_like
|
|
18
|
+
Weights of the non-central chi-square terms.
|
|
19
|
+
k : array_like
|
|
20
|
+
Degrees of freedom of the non-central chi-square terms.
|
|
21
|
+
lambda_ : array_like
|
|
22
|
+
Non-centrality parameters of the non-central chi-square terms.
|
|
23
|
+
s : float
|
|
24
|
+
Scale (standard deviation) of the added normal term.
|
|
25
|
+
m : float
|
|
26
|
+
Constant offset added to the distribution.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
mu : float
|
|
31
|
+
Mean.
|
|
32
|
+
v : float
|
|
33
|
+
Variance.
|
|
34
|
+
"""
|
|
35
|
+
w = asrow(w)
|
|
36
|
+
k = asrow(k)
|
|
37
|
+
lambda_ = asrow(lambda_)
|
|
38
|
+
mu = float(np.dot(w, k + lambda_) + m)
|
|
39
|
+
v = float(2 * np.dot(w ** 2, k + 2 * lambda_) + s ** 2)
|
|
40
|
+
return mu, v
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def char(t, w, k, lambda_, s, m):
|
|
44
|
+
"""Characteristic function of a generalized chi-square distribution.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
t : array_like
|
|
49
|
+
Point(s) at which to evaluate the characteristic function.
|
|
50
|
+
w : array_like
|
|
51
|
+
Weights of the non-central chi-square terms.
|
|
52
|
+
k : array_like
|
|
53
|
+
Degrees of freedom of the non-central chi-square terms.
|
|
54
|
+
lambda_ : array_like
|
|
55
|
+
Non-centrality parameters of the non-central chi-square terms.
|
|
56
|
+
s : float
|
|
57
|
+
Scale (standard deviation) of the added normal term.
|
|
58
|
+
m : float
|
|
59
|
+
Constant offset added to the distribution.
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
phi : ndarray of complex
|
|
64
|
+
The characteristic function at each ``t``, shaped like ``t``.
|
|
65
|
+
"""
|
|
66
|
+
w = asrow(w)
|
|
67
|
+
k = asrow(k)
|
|
68
|
+
lambda_ = asrow(lambda_)
|
|
69
|
+
t = np.asarray(t, dtype=float)
|
|
70
|
+
tf = t.ravel()
|
|
71
|
+
tc = tf[:, None] # column
|
|
72
|
+
|
|
73
|
+
term = np.sum((w * lambda_) / (1 - 2j * tc * w), axis=1)
|
|
74
|
+
denom = np.prod((1 - 2j * w * tc) ** (k / 2), axis=1)
|
|
75
|
+
phi = np.exp(1j * m * tf + 1j * tf * term - s ** 2 * tf ** 2 / 2) / denom
|
|
76
|
+
return phi.reshape(t.shape)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def rnd(w, k, lambda_, s, m, size=None, method="sum"):
|
|
80
|
+
"""Generate generalized chi-square random numbers.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
w : array_like
|
|
85
|
+
Weights of the non-central chi-square terms.
|
|
86
|
+
k : array_like
|
|
87
|
+
Degrees of freedom of the non-central chi-square terms.
|
|
88
|
+
lambda_ : array_like
|
|
89
|
+
Non-centrality parameters of the non-central chi-square terms.
|
|
90
|
+
s : float
|
|
91
|
+
Scale (standard deviation) of the added normal term.
|
|
92
|
+
m : float
|
|
93
|
+
Constant offset added to the distribution.
|
|
94
|
+
size : int or tuple, optional
|
|
95
|
+
Output shape. A scalar ``n`` gives an ``n x n`` array; a tuple gives
|
|
96
|
+
that exact shape. If omitted, a single scalar is returned.
|
|
97
|
+
method : {'sum', 'norm_quad'}
|
|
98
|
+
``'sum'`` (default) generates non-central chi-square and normal numbers
|
|
99
|
+
and adds them. ``'norm_quad'`` generates standard normal vectors and
|
|
100
|
+
computes their quadratic form.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
r : float or ndarray
|
|
105
|
+
Random sample(s), of shape ``size`` (or a scalar if ``size`` is None).
|
|
106
|
+
"""
|
|
107
|
+
w = asrow(w)
|
|
108
|
+
k = asrow(k)
|
|
109
|
+
lambda_ = asrow(lambda_)
|
|
110
|
+
|
|
111
|
+
if size is None:
|
|
112
|
+
shape = ()
|
|
113
|
+
elif np.isscalar(size):
|
|
114
|
+
shape = (int(size), int(size))
|
|
115
|
+
else:
|
|
116
|
+
shape = tuple(int(x) for x in size)
|
|
117
|
+
|
|
118
|
+
method = str(method).lower()
|
|
119
|
+
if method == "sum":
|
|
120
|
+
r = np.zeros(shape)
|
|
121
|
+
for wi, ki, li in zip(w, k, lambda_):
|
|
122
|
+
if li == 0:
|
|
123
|
+
r = r + wi * chi2.rvs(df=ki, size=shape)
|
|
124
|
+
else:
|
|
125
|
+
r = r + wi * ncx2.rvs(df=ki, nc=li, size=shape)
|
|
126
|
+
if s:
|
|
127
|
+
r = r + norm.rvs(loc=m, scale=s, size=shape)
|
|
128
|
+
else:
|
|
129
|
+
r = r + m
|
|
130
|
+
return r
|
|
131
|
+
elif method == "norm_quad":
|
|
132
|
+
quad = gx2_to_norm_quad_params(w, k, lambda_, s, m)
|
|
133
|
+
q1 = np.asarray(quad["q1"]).ravel()
|
|
134
|
+
q2 = np.asarray(quad["q2"])
|
|
135
|
+
q0 = quad["q0"]
|
|
136
|
+
dim = q1.size
|
|
137
|
+
n = int(np.prod(shape)) if shape else 1
|
|
138
|
+
z = norm.rvs(loc=0, scale=1, size=(dim, n))
|
|
139
|
+
r = np.sum(z * (q2 @ z), axis=0) + q1 @ z + q0
|
|
140
|
+
if shape:
|
|
141
|
+
return r.reshape(shape)
|
|
142
|
+
return float(r[0])
|
|
143
|
+
else:
|
|
144
|
+
raise ValueError("method must be 'sum' or 'norm_quad'")
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Conversions between generalized chi-square parameters and the quadratic form
|
|
2
|
+
of a normal vector. Mirrors ``gx2_to_norm_quad_params.m`` and
|
|
3
|
+
``norm_quad_to_gx2_params.m``.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from ._helpers import asrow, uniquetol
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def gx2_to_norm_quad_params(w, k, lambda_, s, m):
|
|
11
|
+
"""Quadratic-form coefficients of the standard normal whose quadratic form
|
|
12
|
+
is the given generalized chi-square.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
w, k, lambda_ : array_like
|
|
17
|
+
Weights, degrees of freedom and non-centralities of the non-central
|
|
18
|
+
chi-square terms.
|
|
19
|
+
s : float
|
|
20
|
+
Scale of the normal term.
|
|
21
|
+
m : float
|
|
22
|
+
Offset.
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
quad : dict
|
|
27
|
+
``{'q2': matrix, 'q1': vector, 'q0': scalar}``. The dimension of the
|
|
28
|
+
standard normal is ``len(q1)``.
|
|
29
|
+
"""
|
|
30
|
+
w = asrow(w)
|
|
31
|
+
k = asrow(k)
|
|
32
|
+
lambda_ = asrow(lambda_)
|
|
33
|
+
|
|
34
|
+
q2_parts = []
|
|
35
|
+
q1_parts = []
|
|
36
|
+
for wi, ki, li in zip(w, k, lambda_):
|
|
37
|
+
ki = int(round(ki))
|
|
38
|
+
q2_parts.append(np.full(ki, wi))
|
|
39
|
+
q1_parts.append(np.concatenate(([wi * np.sqrt(li)], np.zeros(ki - 1))))
|
|
40
|
+
q2 = np.concatenate(q2_parts) if q2_parts else np.array([])
|
|
41
|
+
q1 = -2 * (np.concatenate(q1_parts) if q1_parts else np.array([]))
|
|
42
|
+
|
|
43
|
+
if s:
|
|
44
|
+
q2 = np.append(q2, 0.0)
|
|
45
|
+
q1 = np.append(q1, s)
|
|
46
|
+
|
|
47
|
+
return {"q2": np.diag(q2), "q1": q1.astype(float), "q0": float(np.dot(w, lambda_) + m)}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def norm_quad_to_gx2_params(mu, v, quad, merge=True):
|
|
51
|
+
"""Parameters of the generalized chi-square distribution of a quadratic
|
|
52
|
+
form ``q(x) = x' q2 x + q1' x + q0`` of a normal vector ``x ~ N(mu, v)``.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
mu : array_like
|
|
57
|
+
Column vector of the normal mean.
|
|
58
|
+
v : array_like
|
|
59
|
+
Normal covariance matrix.
|
|
60
|
+
quad : dict
|
|
61
|
+
``{'q2': matrix, 'q1': vector, 'q0': scalar}``.
|
|
62
|
+
merge : bool, optional
|
|
63
|
+
If True (default), merge non-central chi-square components with
|
|
64
|
+
close-enough weights into single components. Set False to return all
|
|
65
|
+
raw exact components.
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
w, k, lambda_, s, m
|
|
70
|
+
"""
|
|
71
|
+
mu = np.asarray(mu, dtype=float).ravel()
|
|
72
|
+
v = np.asarray(v, dtype=float)
|
|
73
|
+
q2_in = np.asarray(quad["q2"], dtype=float)
|
|
74
|
+
q1_in = np.asarray(quad["q1"], dtype=float).ravel()
|
|
75
|
+
q0_in = float(quad["q0"])
|
|
76
|
+
|
|
77
|
+
q2_sym = 0.5 * (q2_in + q2_in.T)
|
|
78
|
+
|
|
79
|
+
# sqrtm(v) avoiding small negative eigenvalues
|
|
80
|
+
d, R = np.linalg.eigh(v)
|
|
81
|
+
d = np.where(d < 0, 0.0, d)
|
|
82
|
+
sqrt_v = R @ np.diag(np.sqrt(d)) @ R.T
|
|
83
|
+
|
|
84
|
+
q2 = sqrt_v @ q2_sym @ sqrt_v
|
|
85
|
+
q2 = (q2 + q2.T) / 2
|
|
86
|
+
q1 = sqrt_v @ (2 * q2_sym @ mu + q1_in)
|
|
87
|
+
q0 = float(mu @ q2_sym @ mu + q1_in @ mu + q0_in)
|
|
88
|
+
|
|
89
|
+
d2, R2 = np.linalg.eigh(q2)
|
|
90
|
+
d = d2
|
|
91
|
+
b = (R2.T @ q1)
|
|
92
|
+
|
|
93
|
+
nz = d != 0
|
|
94
|
+
if merge:
|
|
95
|
+
w, ic = uniquetol(d[nz])
|
|
96
|
+
k = np.bincount(ic, minlength=w.size).astype(float)
|
|
97
|
+
b_sq_sum = np.bincount(ic, weights=b[nz] ** 2, minlength=w.size)
|
|
98
|
+
lambda_ = b_sq_sum / (4 * w ** 2)
|
|
99
|
+
else:
|
|
100
|
+
w = d[nz].copy()
|
|
101
|
+
k = np.ones(w.size)
|
|
102
|
+
lambda_ = b[nz] ** 2 / (4 * w ** 2)
|
|
103
|
+
|
|
104
|
+
m = q0 - np.dot(w, lambda_)
|
|
105
|
+
s = np.linalg.norm(b[~nz])
|
|
106
|
+
return w, k, lambda_, s, m
|