multivariate-probit 0.3.2__tar.gz → 0.3.3__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.
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/CITATION.cff +2 -2
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/PKG-INFO +1 -1
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/api.md +3 -2
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/pyproject.toml +1 -1
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/__init__.py +1 -1
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/_mvn.py +64 -10
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/ifm.py +14 -4
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/model.py +10 -2
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/results.py +10 -2
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/.github/ISSUE_TEMPLATE/use-case.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/.gitignore +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/LICENSE +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/README.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/bench.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/benchmarks.ipynb +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_d12_n3000_100_rho0.4_s0.npz +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_d16_n3000_100_rho0.4_s0.npz +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_d3_n3000_100_rho0.4_s0.npz +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_d5_n3000_100_rho0.4_s0.npz +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_d8_n3000_100_rho0.4_s0.npz +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_pairwise_d12_n3000_100_s0.npz +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_pairwise_d16_n3000_100_s0.npz +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_pairwise_d3_n3000_100_s0.npz +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_pairwise_d5_n3000_100_s0.npz +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_pairwise_d8_n3000_100_s0.npz +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/results.csv +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/benchmarks.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/fiml.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/ifm.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/implementation.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/limitations.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/README.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/comparators.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/ghk.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/index-correlation-shortcut.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/log-score-evaluator.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/margin-gaps.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/mulan-multilabel.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/multilabel-benchmarks.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/sparse-label-transfer.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/uci-credit.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/use-cases/README.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/use-cases/claims-fast-track.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/use-cases/content-moderation.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/use-cases/cyber.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/use-cases/fraud-vendor-fusion.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/use-cases/hcc-codes.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/use-cases/property-imagery.md +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/hatch_build.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/_corr.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/_remote.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/inner.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/linear.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/orthant/__init__.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/conftest.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/test_calibration.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/test_conditional.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/test_contract.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/test_correlation_projection.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/test_evaluators.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/test_linear.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/test_modal_fit.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/test_rf.py +0 -0
- {multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/test_xgboost.py +0 -0
|
@@ -6,8 +6,8 @@ authors:
|
|
|
6
6
|
- family-names: Shipman
|
|
7
7
|
given-names: Mark
|
|
8
8
|
orcid: "https://orcid.org/0009-0008-7193-1263"
|
|
9
|
-
version: 0.3.
|
|
10
|
-
date-released: 2026-10-
|
|
9
|
+
version: 0.3.3
|
|
10
|
+
date-released: 2026-10-29
|
|
11
11
|
license: MIT
|
|
12
12
|
repository-code: "https://github.com/sign-of-fourier/multivariate-probit"
|
|
13
13
|
url: "https://quantecarlo.com/multivariate-probit"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: multivariate-probit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Joint probabilities for correlated binary outcomes: P(any), P(all), patterns and conditionals that agree. Any classifier per outcome; scikit-learn-style API.
|
|
5
5
|
Project-URL: Homepage, https://quantecarlo.com/multivariate-probit
|
|
6
6
|
Project-URL: Source, https://github.com/sign-of-fourier/multivariate-probit
|
|
@@ -35,10 +35,10 @@ MultivariateProbit(
|
|
|
35
35
|
| `optimizer` | `"Nelder-Mead"` | Passed to `scipy.optimize.minimize` for `dependence="joint"`. Derivative-free by design. |
|
|
36
36
|
| `project_correlation` | `True` | Project a pairwise estimate onto the nearest positive-definite correlation matrix. No effect when `dependence="joint"`. |
|
|
37
37
|
| `random_state` | `None` | Controls the cross-fitting split and `sample`. |
|
|
38
|
-
| `evaluator` | `"quadrature"` | Backend for the orthant probabilities behind `dependence="joint"` and every joint query: `"quadrature"`, `"scipy"` or `"
|
|
38
|
+
| `evaluator` | `"quadrature"` | Backend for the orthant probabilities behind `dependence="joint"` and every joint query: `"quadrature"`, `"scipy"`, `"orthant"` or `"ghk"`. See [Evaluators](#evaluators). |
|
|
39
39
|
| `resolution` | `"high"` | Passed to `evaluator="orthant"`; ignored otherwise. `"high"`, `"highest"` (most accurate, about 2–3× the time of `"high"`: median error under 0.00005 on an equicorrelated Σ against up to 0.0001 at `"high"`, and 0.0001–0.0031 on a near-singular Σ against 0.0006–0.0061; see [benchmarks.md](benchmarks.md)), `"smooth"` (the cost of `"highest"`, continuous in the parameters with no threshold where the method changes, for gradient-based fitting; not uniformly more accurate than `"high"`) or `"low"` (cheapest). |
|
|
40
40
|
| `fitter` | `"ifm"` | `"ifm"`: the local two-stage fit. `"modal"`: full maximum likelihood on a hosted GPU service ([fiml.md](fiml.md)) — `inner="linear"` only, `inner_params` may set only `alpha`; `dependence`, `cv`, `optimizer` and `project_correlation` do not apply. Uploads X, Y and `sample_weight`. An int `random_state` seeds its Sobol draws (default seed 0). |
|
|
41
|
-
| `n_draws` | `512` | Sobol draws per row for `fitter="modal"`; ignored otherwise. |
|
|
41
|
+
| `n_draws` | `512` | Sobol draws per row for `fitter="modal"` and `evaluator="ghk"`; ignored otherwise. Powers of 2 keep the Sobol set balanced. |
|
|
42
42
|
|
|
43
43
|
> **Reading `calibration_`.** This is the Cox calibration slope, a deliberately
|
|
44
44
|
> simple scale check: a probit of each outcome on its own fitted index. A slope
|
|
@@ -78,6 +78,7 @@ queries on a pairwise fit still use the chosen backend.
|
|
|
78
78
|
| `"quadrature"` | Genz's recursive conditioning with a fixed Gauss-Legendre rule of order `n_quad` (see [implementation.md](implementation.md)). | Deterministic and accurate. Cost grows as `n_quad ** (d - 2)`, so it becomes impractical well before d = 10. |
|
|
79
79
|
| `"scipy"` | `scipy.stats.multivariate_normal.cdf`, one row at a time. | Reaches any d, but is slow per row and randomised: a `dependence="joint"` fit is not exactly reproducible, and its noisy objective can stall the simplex. |
|
|
80
80
|
| `"orthant"` | An optional compiled package bundled as `multivariate_probit.orthant`. Install with `pip install multivariate-probit[orthant]`. | Built for CPython 3.11 and 3.12 on x86-64 Linux only; elsewhere it raises `ImportError`. Without a key it accepts d ≤ 3 at `resolution="low"` only, and raises `ValueError` outside that. A key is read from `$ORTHANT_KEY` or `~/.orthant/key` at import, or passed at any time with `orthant.activate(key)` (returns `"paid"`; a wrong key raises `ValueError`). Each process needs its own key: on Spark, call `activate` inside the function the workers run, or set the variable in the cluster environment; see https://quantecarlo.com/orthant_key. Accuracy and speed against GHK and SciPy: [studies/ghk.md](studies/ghk.md). |
|
|
81
|
+
| `"ghk"` | The GHK simulator: per row, variables ordered by ascending limit, sequential truncated-normal conditioning on `n_draws` scrambled Sobol points shared by all rows (fixed seed). Pure NumPy, no key. | Deterministic and smooth in the parameters, reaches any d, vectorised over rows. Error falls with `n_draws` (roughly 1e-3 max, 1e-5 median at 128-512 draws, d 3-5); cost is linear in `n_draws` and about quadratic in d. |
|
|
81
82
|
|
|
82
83
|
For test sets too large for one machine, the same evaluator runs as a hosted
|
|
83
84
|
GPU service, `quantecarlo.orthant_cdf`: about a second per million rows at
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "multivariate-probit"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.3"
|
|
8
8
|
description = "Joint probabilities for correlated binary outcomes: P(any), P(all), patterns and conditionals that agree. Any classifier per outcome; scikit-learn-style API."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -14,6 +14,7 @@ from __future__ import annotations
|
|
|
14
14
|
from functools import lru_cache
|
|
15
15
|
|
|
16
16
|
import numpy as np
|
|
17
|
+
from scipy.special import log_ndtr, ndtr, ndtri
|
|
17
18
|
from scipy.stats import norm
|
|
18
19
|
|
|
19
20
|
__all__ = [
|
|
@@ -36,9 +37,11 @@ _EPS = 1e-12
|
|
|
36
37
|
# The backends that can evaluate a lower-orthant probability. The choice is a
|
|
37
38
|
# speed/accuracy trade-off left to the caller: "quadrature" is the
|
|
38
39
|
# deterministic evaluator below, "scipy" is scipy.stats.multivariate_normal.cdf
|
|
39
|
-
# (randomised quasi-Monte Carlo, one row at a time),
|
|
40
|
-
# optional compiled package bundled under ``multivariate_probit.orthant
|
|
41
|
-
|
|
40
|
+
# (randomised quasi-Monte Carlo, one row at a time), "orthant" is the
|
|
41
|
+
# optional compiled package bundled under ``multivariate_probit.orthant``, and
|
|
42
|
+
# "ghk" is the GHK simulator below with fixed scrambled Sobol draws
|
|
43
|
+
# (deterministic, vectorised, any d).
|
|
44
|
+
EVALUATORS = ("quadrature", "scipy", "orthant", "ghk")
|
|
42
45
|
|
|
43
46
|
|
|
44
47
|
@lru_cache(maxsize=8)
|
|
@@ -136,6 +139,50 @@ def _scipy_orthant(A, corr_stack):
|
|
|
136
139
|
return out
|
|
137
140
|
|
|
138
141
|
|
|
142
|
+
@lru_cache(maxsize=8)
|
|
143
|
+
def _sobol(dim, n_draws, seed):
|
|
144
|
+
"""Scrambled Sobol points in (0, 1)^dim, cached so repeated calls (an
|
|
145
|
+
optimizer's objective) reuse the same draws."""
|
|
146
|
+
from scipy.stats import qmc
|
|
147
|
+
|
|
148
|
+
return qmc.Sobol(dim, scramble=True, seed=seed).random(n_draws)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _ghk_orthant(A, corr_stack, n_draws=512, seed=0, max_cells=2_000_000):
|
|
152
|
+
"""GHK (Geweke-Hajivassiliou-Keane) estimate of the lower orthant, row by row.
|
|
153
|
+
|
|
154
|
+
Per row the variables are reordered by ascending limit, the correlation is
|
|
155
|
+
Cholesky-factored, and each draw samples the variables in sequence from
|
|
156
|
+
their truncated conditionals; the estimate is the mean over draws of the
|
|
157
|
+
product of the conditional probabilities. The draws are one scrambled
|
|
158
|
+
Sobol set shared by all rows (seed fixed), so the result is deterministic
|
|
159
|
+
and smooth in A and the correlations. Error falls with ``n_draws``.
|
|
160
|
+
"""
|
|
161
|
+
n, d = A.shape
|
|
162
|
+
if d == 1:
|
|
163
|
+
return norm.cdf(A[:, 0])
|
|
164
|
+
S = np.moveaxis(corr_stack, 2, 0)
|
|
165
|
+
order = np.argsort(A, axis=1)
|
|
166
|
+
A = np.take_along_axis(A, order, axis=1)
|
|
167
|
+
S = S[np.arange(n)[:, None, None], order[:, :, None], order[:, None, :]]
|
|
168
|
+
L = np.linalg.cholesky(S)
|
|
169
|
+
U = _sobol(d - 1, int(n_draws), seed)
|
|
170
|
+
out = np.empty(n)
|
|
171
|
+
step = max(1, max_cells // (n_draws * d))
|
|
172
|
+
for lo in range(0, n, step):
|
|
173
|
+
a, Lc = A[lo:lo + step], L[lo:lo + step]
|
|
174
|
+
logp = np.zeros((len(a), n_draws))
|
|
175
|
+
z = np.zeros((len(a), n_draws, d - 1))
|
|
176
|
+
for j in range(d):
|
|
177
|
+
mu = z[:, :, :j] @ Lc[:, j, :j, None]
|
|
178
|
+
b = (a[:, j, None] - mu[:, :, 0]) / Lc[:, j, j, None]
|
|
179
|
+
logp += log_ndtr(b)
|
|
180
|
+
if j < d - 1:
|
|
181
|
+
z[:, :, j] = ndtri(np.clip(U[:, j] * ndtr(b), 1e-300, 1.0 - 1e-16))
|
|
182
|
+
out[lo:lo + step] = np.exp(logp).mean(axis=1)
|
|
183
|
+
return out
|
|
184
|
+
|
|
185
|
+
|
|
139
186
|
def _compiled_orthant(A, corr_stack, resolution):
|
|
140
187
|
try:
|
|
141
188
|
from . import orthant
|
|
@@ -153,11 +200,11 @@ def _compiled_orthant(A, corr_stack, resolution):
|
|
|
153
200
|
return np.asarray(prob, dtype=float)
|
|
154
201
|
|
|
155
202
|
|
|
156
|
-
def lower_orthant(A, corr_stack, evaluator="quadrature", n_quad=24, resolution="high"):
|
|
203
|
+
def lower_orthant(A, corr_stack, evaluator="quadrature", n_quad=24, resolution="high", n_draws=512):
|
|
157
204
|
""":func:`mvn_orthant` with a choice of backend.
|
|
158
205
|
|
|
159
|
-
``n_quad`` is used only by ``"quadrature"
|
|
160
|
-
``"orthant"``. A backend that cannot run as asked raises; nothing falls
|
|
206
|
+
``n_quad`` is used only by ``"quadrature"``, ``resolution`` only by
|
|
207
|
+
``"orthant"`` and ``n_draws`` only by ``"ghk"``. A backend that cannot run as asked raises; nothing falls
|
|
161
208
|
back to another backend. Results are clipped to [0, 1].
|
|
162
209
|
"""
|
|
163
210
|
A = np.atleast_2d(np.asarray(A, dtype=float))
|
|
@@ -167,6 +214,8 @@ def lower_orthant(A, corr_stack, evaluator="quadrature", n_quad=24, resolution="
|
|
|
167
214
|
prob = _scipy_orthant(A, corr_stack)
|
|
168
215
|
elif evaluator == "orthant":
|
|
169
216
|
prob = _compiled_orthant(A, corr_stack, resolution)
|
|
217
|
+
elif evaluator == "ghk":
|
|
218
|
+
prob = _ghk_orthant(A, corr_stack, n_draws=n_draws)
|
|
170
219
|
else:
|
|
171
220
|
raise ValueError(f"evaluator must be one of {EVALUATORS}; got {evaluator!r}")
|
|
172
221
|
return np.clip(prob, 0.0, 1.0)
|
|
@@ -192,14 +241,14 @@ def signed_corr_stack(corr, signs):
|
|
|
192
241
|
return stack
|
|
193
242
|
|
|
194
243
|
|
|
195
|
-
def orthant_prob(upper, corr, n_quad=24, evaluator="quadrature", resolution="high"):
|
|
244
|
+
def orthant_prob(upper, corr, n_quad=24, evaluator="quadrature", resolution="high", n_draws=512):
|
|
196
245
|
"""P(Z_j <= upper[i, j] for all j) for a single shared correlation matrix."""
|
|
197
246
|
upper = np.atleast_2d(np.asarray(upper, dtype=float))
|
|
198
247
|
stack = signed_corr_stack(corr, np.ones_like(upper))
|
|
199
|
-
return lower_orthant(upper, stack, evaluator, n_quad=n_quad, resolution=resolution)
|
|
248
|
+
return lower_orthant(upper, stack, evaluator, n_quad=n_quad, resolution=resolution, n_draws=n_draws)
|
|
200
249
|
|
|
201
250
|
|
|
202
|
-
def pattern_prob(eta, Y, corr, n_quad=24, evaluator="quadrature", resolution="high"):
|
|
251
|
+
def pattern_prob(eta, Y, corr, n_quad=24, evaluator="quadrature", resolution="high", n_draws=512):
|
|
203
252
|
"""``P(Y = y | x)`` for the multivariate probit, row by row.
|
|
204
253
|
|
|
205
254
|
With ``s = 2y - 1`` the event ``{Y = y}`` is the orthant
|
|
@@ -215,5 +264,10 @@ def pattern_prob(eta, Y, corr, n_quad=24, evaluator="quadrature", resolution="hi
|
|
|
215
264
|
|
|
216
265
|
signs = 2.0 * Y - 1.0
|
|
217
266
|
return lower_orthant(
|
|
218
|
-
signs * eta,
|
|
267
|
+
signs * eta,
|
|
268
|
+
signed_corr_stack(corr, signs),
|
|
269
|
+
evaluator,
|
|
270
|
+
n_quad=n_quad,
|
|
271
|
+
resolution=resolution,
|
|
272
|
+
n_draws=n_draws,
|
|
219
273
|
)
|
|
@@ -114,14 +114,16 @@ def free_from_corr(corr):
|
|
|
114
114
|
|
|
115
115
|
|
|
116
116
|
def joint_log_likelihood(
|
|
117
|
-
corr, eta, Y, weights=None, n_quad=24, evaluator="quadrature", resolution="high"
|
|
117
|
+
corr, eta, Y, weights=None, n_quad=24, evaluator="quadrature", resolution="high", n_draws=512
|
|
118
118
|
):
|
|
119
119
|
"""Full d-variate log-likelihood in ``corr``, with the margins held fixed.
|
|
120
120
|
|
|
121
|
-
``evaluator``, ``n_quad`` and ``
|
|
121
|
+
``evaluator``, ``n_quad``, ``resolution`` and ``n_draws`` select and tune the orthant
|
|
122
122
|
backend; see :func:`~multivariate_probit._mvn.lower_orthant`.
|
|
123
123
|
"""
|
|
124
|
-
prob = pattern_prob(
|
|
124
|
+
prob = pattern_prob(
|
|
125
|
+
eta, Y, corr, n_quad=n_quad, evaluator=evaluator, resolution=resolution, n_draws=n_draws
|
|
126
|
+
)
|
|
125
127
|
prob = np.clip(prob, _LL_EPS, None)
|
|
126
128
|
ll = np.log(prob)
|
|
127
129
|
if weights is None:
|
|
@@ -140,6 +142,7 @@ def joint_correlation(
|
|
|
140
142
|
options=None,
|
|
141
143
|
evaluator="quadrature",
|
|
142
144
|
resolution="high",
|
|
145
|
+
n_draws=512,
|
|
143
146
|
):
|
|
144
147
|
"""Estimate the latent correlation matrix by full-information ML in ``R``.
|
|
145
148
|
|
|
@@ -181,7 +184,14 @@ def joint_correlation(
|
|
|
181
184
|
if np.linalg.eigvalsh(corr).min() <= min_eigenvalue:
|
|
182
185
|
return 1e10 # not positive definite -- push the optimizer away
|
|
183
186
|
return -joint_log_likelihood(
|
|
184
|
-
corr,
|
|
187
|
+
corr,
|
|
188
|
+
eta,
|
|
189
|
+
Y,
|
|
190
|
+
weights=weights,
|
|
191
|
+
n_quad=n_quad,
|
|
192
|
+
evaluator=evaluator,
|
|
193
|
+
resolution=resolution,
|
|
194
|
+
n_draws=n_draws,
|
|
185
195
|
)
|
|
186
196
|
|
|
187
197
|
result = minimize(neg_ll, free_from_corr(start), method=optimizer, options=options)
|
|
@@ -144,7 +144,7 @@ class MultivariateProbit:
|
|
|
144
144
|
absorbed part of the noise, which attenuates the estimated
|
|
145
145
|
correlations toward zero. ``None`` skips cross-fitting -- reasonable
|
|
146
146
|
for the linear default, risky for anything that can overfit.
|
|
147
|
-
evaluator : {"quadrature", "scipy", "orthant"}, default="quadrature"
|
|
147
|
+
evaluator : {"quadrature", "scipy", "orthant", "ghk"}, default="quadrature"
|
|
148
148
|
Backend for the orthant probabilities behind ``dependence="joint"``
|
|
149
149
|
and every joint query. The speed/accuracy trade-off is the caller's:
|
|
150
150
|
|
|
@@ -158,6 +158,10 @@ class MultivariateProbit:
|
|
|
158
158
|
Linux; ``pip install multivariate-probit[orthant]``). Without a key
|
|
159
159
|
it accepts ``d <= 3`` and ``resolution="low"`` only; outside that,
|
|
160
160
|
and on any platform it was not built for, it raises.
|
|
161
|
+
- ``"ghk"``: the GHK simulator with ``n_draws`` fixed scrambled Sobol
|
|
162
|
+
draws per row. Deterministic, vectorised and free (no key), any
|
|
163
|
+
``d``; error falls with ``n_draws``. Not available in 0.3.2 and
|
|
164
|
+
earlier.
|
|
161
165
|
|
|
162
166
|
Results are clipped to [0, 1]. No backend ever falls back to another.
|
|
163
167
|
``dependence="pairwise"`` needs only bivariate probabilities, which
|
|
@@ -197,7 +201,8 @@ class MultivariateProbit:
|
|
|
197
201
|
``sample_weight`` are uploaded; only parameters come back, and every
|
|
198
202
|
prediction afterwards runs locally on ``evaluator``.
|
|
199
203
|
n_draws : int, default=512
|
|
200
|
-
Sobol draws per row for ``fitter="modal"
|
|
204
|
+
Sobol draws per row for ``fitter="modal"`` and ``evaluator="ghk"``;
|
|
205
|
+
ignored otherwise. Powers of 2 keep the Sobol set balanced.
|
|
201
206
|
|
|
202
207
|
Attributes
|
|
203
208
|
----------
|
|
@@ -320,6 +325,7 @@ class MultivariateProbit:
|
|
|
320
325
|
optimizer=self.optimizer,
|
|
321
326
|
evaluator=self.evaluator,
|
|
322
327
|
resolution=self.resolution,
|
|
328
|
+
n_draws=self.n_draws,
|
|
323
329
|
)
|
|
324
330
|
self.nll_ = None if self.optimize_result_ is None else float(self.optimize_result_.fun)
|
|
325
331
|
elif self.dependence == "pairwise":
|
|
@@ -525,6 +531,7 @@ class MultivariateProbit:
|
|
|
525
531
|
n_quad=self.n_quad,
|
|
526
532
|
evaluator=self.evaluator,
|
|
527
533
|
resolution=self.resolution,
|
|
534
|
+
n_draws=self.n_draws,
|
|
528
535
|
)
|
|
529
536
|
|
|
530
537
|
def predict_marginal_proba(self, X):
|
|
@@ -556,6 +563,7 @@ class MultivariateProbit:
|
|
|
556
563
|
n_quad=self.n_quad,
|
|
557
564
|
evaluator=self.evaluator,
|
|
558
565
|
resolution=self.resolution,
|
|
566
|
+
n_draws=self.n_draws,
|
|
559
567
|
)
|
|
560
568
|
|
|
561
569
|
def joint_log_proba(self, X, Y):
|
|
@@ -21,18 +21,24 @@ class MultivariateProbitProba:
|
|
|
21
21
|
correlation matrix was estimated in the first place.
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
def __init__(self, eta, corr, n_quad=24, evaluator="quadrature", resolution="high"):
|
|
24
|
+
def __init__(self, eta, corr, n_quad=24, evaluator="quadrature", resolution="high", n_draws=512):
|
|
25
25
|
self.eta = np.atleast_2d(np.asarray(eta, dtype=float))
|
|
26
26
|
self.marginal = norm.cdf(self.eta)
|
|
27
27
|
self.corr = np.asarray(corr, dtype=float)
|
|
28
28
|
self.n_quad = n_quad
|
|
29
29
|
self.evaluator = evaluator
|
|
30
30
|
self.resolution = resolution
|
|
31
|
+
self.n_draws = n_draws
|
|
31
32
|
|
|
32
33
|
def _orthant(self, upper, corr):
|
|
33
34
|
stack = signed_corr_stack(corr, np.ones_like(upper))
|
|
34
35
|
return lower_orthant(
|
|
35
|
-
upper,
|
|
36
|
+
upper,
|
|
37
|
+
stack,
|
|
38
|
+
self.evaluator,
|
|
39
|
+
n_quad=self.n_quad,
|
|
40
|
+
resolution=self.resolution,
|
|
41
|
+
n_draws=self.n_draws,
|
|
36
42
|
)
|
|
37
43
|
|
|
38
44
|
# ------------------------------------------------------ array behaviour
|
|
@@ -72,6 +78,7 @@ class MultivariateProbitProba:
|
|
|
72
78
|
n_quad=self.n_quad,
|
|
73
79
|
evaluator=self.evaluator,
|
|
74
80
|
resolution=self.resolution,
|
|
81
|
+
n_draws=self.n_draws,
|
|
75
82
|
)
|
|
76
83
|
|
|
77
84
|
def all(self, outcomes=None):
|
|
@@ -102,6 +109,7 @@ class MultivariateProbitProba:
|
|
|
102
109
|
n_quad=self.n_quad,
|
|
103
110
|
evaluator=self.evaluator,
|
|
104
111
|
resolution=self.resolution,
|
|
112
|
+
n_draws=self.n_draws,
|
|
105
113
|
)
|
|
106
114
|
|
|
107
115
|
def conditional(self, event, given):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_d12_n3000_100_rho0.4_s0.npz
RENAMED
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_d16_n3000_100_rho0.4_s0.npz
RENAMED
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_d3_n3000_100_rho0.4_s0.npz
RENAMED
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_d5_n3000_100_rho0.4_s0.npz
RENAMED
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_d8_n3000_100_rho0.4_s0.npz
RENAMED
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_pairwise_d12_n3000_100_s0.npz
RENAMED
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_pairwise_d16_n3000_100_s0.npz
RENAMED
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_pairwise_d3_n3000_100_s0.npz
RENAMED
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_pairwise_d5_n3000_100_s0.npz
RENAMED
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/benchmarks/ref_pairwise_d8_n3000_100_s0.npz
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/index-correlation-shortcut.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/multilabel-benchmarks.md
RENAMED
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/studies/sparse-label-transfer.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/use-cases/content-moderation.md
RENAMED
|
File without changes
|
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/docs/use-cases/fraud-vendor-fusion.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/src/multivariate_probit/orthant/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multivariate_probit-0.3.2 → multivariate_probit-0.3.3}/tests/test_correlation_projection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|