statgpu 0.1.0__py3-none-any.whl

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.
Files changed (168) hide show
  1. statgpu/__init__.py +174 -0
  2. statgpu/_base.py +544 -0
  3. statgpu/_config.py +127 -0
  4. statgpu/anova/__init__.py +5 -0
  5. statgpu/anova/_oneway.py +194 -0
  6. statgpu/backends/__init__.py +83 -0
  7. statgpu/backends/_array_ops.py +529 -0
  8. statgpu/backends/_base.py +184 -0
  9. statgpu/backends/_cupy.py +453 -0
  10. statgpu/backends/_factory.py +65 -0
  11. statgpu/backends/_gpu_inference_cupy.py +214 -0
  12. statgpu/backends/_gpu_inference_torch.py +422 -0
  13. statgpu/backends/_numpy.py +324 -0
  14. statgpu/backends/_torch.py +685 -0
  15. statgpu/backends/_torch_safe.py +47 -0
  16. statgpu/backends/_utils.py +423 -0
  17. statgpu/core/__init__.py +10 -0
  18. statgpu/core/formula/__init__.py +33 -0
  19. statgpu/core/formula/_design.py +99 -0
  20. statgpu/core/formula/_parser.py +191 -0
  21. statgpu/core/formula/_terms.py +70 -0
  22. statgpu/core/formula/tests/__init__.py +0 -0
  23. statgpu/core/formula/tests/test_parser.py +194 -0
  24. statgpu/covariance/__init__.py +6 -0
  25. statgpu/covariance/_empirical.py +310 -0
  26. statgpu/covariance/_shrinkage.py +248 -0
  27. statgpu/cross_validation/__init__.py +31 -0
  28. statgpu/cross_validation/_base.py +410 -0
  29. statgpu/cross_validation/_engine.py +167 -0
  30. statgpu/diagnostics/__init__.py +7 -0
  31. statgpu/diagnostics/_regression_diagnostics.py +188 -0
  32. statgpu/feature_selection/__init__.py +24 -0
  33. statgpu/feature_selection/_knockoff.py +870 -0
  34. statgpu/feature_selection/_knockoff_utils.py +1003 -0
  35. statgpu/feature_selection/_stepwise.py +300 -0
  36. statgpu/glm_core/__init__.py +81 -0
  37. statgpu/glm_core/_base.py +202 -0
  38. statgpu/glm_core/_family.py +362 -0
  39. statgpu/glm_core/_fused.py +149 -0
  40. statgpu/glm_core/_gamma.py +111 -0
  41. statgpu/glm_core/_inverse_gaussian.py +62 -0
  42. statgpu/glm_core/_irls.py +561 -0
  43. statgpu/glm_core/_logistic.py +82 -0
  44. statgpu/glm_core/_negative_binomial.py +68 -0
  45. statgpu/glm_core/_poisson.py +60 -0
  46. statgpu/glm_core/_solver_legacy.py +100 -0
  47. statgpu/glm_core/_squared.py +53 -0
  48. statgpu/glm_core/_tweedie.py +74 -0
  49. statgpu/inference/__init__.py +239 -0
  50. statgpu/inference/_distributions_backend.py +2610 -0
  51. statgpu/inference/_multiple_testing.py +391 -0
  52. statgpu/inference/_resampling.py +1400 -0
  53. statgpu/inference/_results.py +265 -0
  54. statgpu/linear_model/__init__.py +75 -0
  55. statgpu/linear_model/_gaussian_inference.py +306 -0
  56. statgpu/linear_model/_glm_base.py +1261 -0
  57. statgpu/linear_model/_ordered_logit.py +52 -0
  58. statgpu/linear_model/_ordered_probit.py +50 -0
  59. statgpu/linear_model/_stats.py +170 -0
  60. statgpu/linear_model/cv/__init__.py +13 -0
  61. statgpu/linear_model/cv/_elasticnet_cv.py +892 -0
  62. statgpu/linear_model/cv/_lasso_cv.py +253 -0
  63. statgpu/linear_model/cv/_logistic_cv.py +895 -0
  64. statgpu/linear_model/cv/_ridge_cv.py +1160 -0
  65. statgpu/linear_model/legacy/__init__.py +1 -0
  66. statgpu/linear_model/legacy/_distributions_legacy_gpu.py +340 -0
  67. statgpu/linear_model/legacy/_elasticnet_legacy.py +936 -0
  68. statgpu/linear_model/legacy/_lasso_legacy.py +4876 -0
  69. statgpu/linear_model/legacy/_penalized_legacy.py +1174 -0
  70. statgpu/linear_model/legacy/_ridge_legacy.py +863 -0
  71. statgpu/linear_model/legacy/_solver_legacy.py +104 -0
  72. statgpu/linear_model/penalized/__init__.py +25 -0
  73. statgpu/linear_model/penalized/_base.py +437 -0
  74. statgpu/linear_model/penalized/_fit_mixin.py +1877 -0
  75. statgpu/linear_model/penalized/_inference_mixin.py +1179 -0
  76. statgpu/linear_model/penalized/_penalized_cv.py +2699 -0
  77. statgpu/linear_model/penalized/_penalized_gamma.py +86 -0
  78. statgpu/linear_model/penalized/_penalized_inverse_gaussian.py +62 -0
  79. statgpu/linear_model/penalized/_penalized_linear.py +236 -0
  80. statgpu/linear_model/penalized/_penalized_logistic.py +100 -0
  81. statgpu/linear_model/penalized/_penalized_negative_binomial.py +65 -0
  82. statgpu/linear_model/penalized/_penalized_poisson.py +62 -0
  83. statgpu/linear_model/penalized/_penalized_tweedie.py +65 -0
  84. statgpu/linear_model/penalized/_predict_mixin.py +182 -0
  85. statgpu/linear_model/wrappers/__init__.py +31 -0
  86. statgpu/linear_model/wrappers/_adaptive_lasso.py +63 -0
  87. statgpu/linear_model/wrappers/_elasticnet.py +75 -0
  88. statgpu/linear_model/wrappers/_gamma.py +67 -0
  89. statgpu/linear_model/wrappers/_inverse_gaussian.py +47 -0
  90. statgpu/linear_model/wrappers/_lasso.py +2124 -0
  91. statgpu/linear_model/wrappers/_linear.py +1127 -0
  92. statgpu/linear_model/wrappers/_logistic.py +1435 -0
  93. statgpu/linear_model/wrappers/_mcp.py +58 -0
  94. statgpu/linear_model/wrappers/_negative_binomial.py +58 -0
  95. statgpu/linear_model/wrappers/_poisson.py +48 -0
  96. statgpu/linear_model/wrappers/_ridge.py +166 -0
  97. statgpu/linear_model/wrappers/_scad.py +58 -0
  98. statgpu/linear_model/wrappers/_tweedie.py +57 -0
  99. statgpu/metrics/__init__.py +21 -0
  100. statgpu/metrics/_classification.py +591 -0
  101. statgpu/nonparametric/__init__.py +50 -0
  102. statgpu/nonparametric/kernel_methods/__init__.py +25 -0
  103. statgpu/nonparametric/kernel_methods/_kernels.py +246 -0
  104. statgpu/nonparametric/kernel_methods/_krr.py +234 -0
  105. statgpu/nonparametric/kernel_methods/_krr_cv.py +380 -0
  106. statgpu/nonparametric/kernel_smoothing/__init__.py +39 -0
  107. statgpu/nonparametric/kernel_smoothing/_bandwidth_selection.py +1083 -0
  108. statgpu/nonparametric/kernel_smoothing/_kde.py +761 -0
  109. statgpu/nonparametric/kernel_smoothing/_kernel_common.py +348 -0
  110. statgpu/nonparametric/kernel_smoothing/_kernel_regression.py +748 -0
  111. statgpu/nonparametric/splines/__init__.py +5 -0
  112. statgpu/nonparametric/splines/_bspline_basis.py +336 -0
  113. statgpu/nonparametric/splines/_penalized.py +349 -0
  114. statgpu/panel/__init__.py +19 -0
  115. statgpu/panel/_covariance.py +140 -0
  116. statgpu/panel/_fixed_effects.py +420 -0
  117. statgpu/panel/_random_effects.py +385 -0
  118. statgpu/panel/_utils.py +482 -0
  119. statgpu/penalties/__init__.py +139 -0
  120. statgpu/penalties/_adaptive_l1.py +313 -0
  121. statgpu/penalties/_base.py +261 -0
  122. statgpu/penalties/_categories.py +39 -0
  123. statgpu/penalties/_elasticnet.py +98 -0
  124. statgpu/penalties/_group_lasso.py +678 -0
  125. statgpu/penalties/_group_mcp.py +553 -0
  126. statgpu/penalties/_group_scad.py +605 -0
  127. statgpu/penalties/_l1.py +107 -0
  128. statgpu/penalties/_l2.py +77 -0
  129. statgpu/penalties/_mcp.py +237 -0
  130. statgpu/penalties/_scad.py +260 -0
  131. statgpu/semiparametric/__init__.py +5 -0
  132. statgpu/semiparametric/_gam.py +401 -0
  133. statgpu/solvers/__init__.py +24 -0
  134. statgpu/solvers/_admm.py +241 -0
  135. statgpu/solvers/_constants.py +15 -0
  136. statgpu/solvers/_convergence.py +6 -0
  137. statgpu/solvers/_fista.py +436 -0
  138. statgpu/solvers/_fista_bb.py +513 -0
  139. statgpu/solvers/_fista_lla.py +541 -0
  140. statgpu/solvers/_lbfgs.py +206 -0
  141. statgpu/solvers/_newton.py +149 -0
  142. statgpu/solvers/_utils.py +277 -0
  143. statgpu/survival/__init__.py +14 -0
  144. statgpu/survival/_cox.py +3974 -0
  145. statgpu/survival/_cox_breslow_triton_kernel.py +106 -0
  146. statgpu/survival/_cox_cv.py +1159 -0
  147. statgpu/survival/_cox_efron_cuda.py +1280 -0
  148. statgpu/survival/_cox_efron_triton.py +359 -0
  149. statgpu/unsupervised/__init__.py +29 -0
  150. statgpu/unsupervised/_agglomerative.py +307 -0
  151. statgpu/unsupervised/_dbscan.py +263 -0
  152. statgpu/unsupervised/_dbscan_cpu.pyx +125 -0
  153. statgpu/unsupervised/_gmm.py +332 -0
  154. statgpu/unsupervised/_incremental_pca.py +176 -0
  155. statgpu/unsupervised/_kmeans.py +261 -0
  156. statgpu/unsupervised/_minibatch_kmeans.py +299 -0
  157. statgpu/unsupervised/_minibatch_nmf.py +252 -0
  158. statgpu/unsupervised/_nmf.py +190 -0
  159. statgpu/unsupervised/_pca.py +189 -0
  160. statgpu/unsupervised/_truncated_svd.py +132 -0
  161. statgpu/unsupervised/_tsne.py +192 -0
  162. statgpu/unsupervised/_umap.py +224 -0
  163. statgpu/unsupervised/_utils.py +134 -0
  164. statgpu-0.1.0.dist-info/METADATA +245 -0
  165. statgpu-0.1.0.dist-info/RECORD +168 -0
  166. statgpu-0.1.0.dist-info/WHEEL +5 -0
  167. statgpu-0.1.0.dist-info/licenses/LICENSE +199 -0
  168. statgpu-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,68 @@
1
+ """
2
+ Negative Binomial loss: negative log-likelihood with log link.
3
+
4
+ For overdispersed count data:
5
+ Var(Y) = mu + alpha * mu^2
6
+ where mu = exp(X @ coef), alpha is the dispersion parameter.
7
+
8
+ Supports numpy / cupy / torch backends via _array_ops helpers.
9
+ """
10
+ import numpy as np
11
+
12
+ from statgpu.backends._array_ops import _clip, _exp, _log, _sum, _max_eigval_power
13
+ from statgpu.glm_core._base import GLMLoss, register_glm_loss
14
+
15
+
16
+ @register_glm_loss('negative_binomial')
17
+ class NegativeBinomialLoss(GLMLoss):
18
+ name = "negative_binomial"
19
+ y_type = "count"
20
+ smooth_gradient = True
21
+ has_hessian = True
22
+
23
+ _MU_LO = 1e-300
24
+
25
+ def __init__(self, alpha=1.0):
26
+ if not np.isfinite(alpha) or alpha <= 0.0:
27
+ raise ValueError("alpha must be a finite positive scalar for negative binomial loss")
28
+ self.alpha = alpha
29
+
30
+ def _mu_from_eta(self, eta):
31
+ return _clip(_exp(_clip(eta, -30, 30)), self._MU_LO, None)
32
+
33
+ # ── Per-sample formulas (single source of truth) ──────────────────
34
+
35
+ def per_sample_value(self, eta, y):
36
+ mu = self._mu_from_eta(eta)
37
+ a = self.alpha
38
+ one_plus_a_mu = 1.0 + a * mu
39
+ return -y * _log(mu / one_plus_a_mu) + (1.0 / a) * _log(one_plus_a_mu)
40
+
41
+ def per_sample_gradient(self, eta, y):
42
+ mu = self._mu_from_eta(eta)
43
+ return (mu - y) / (1.0 + self.alpha * mu)
44
+
45
+ def hessian(self, X, y, coef, sample_weight=None):
46
+ z = _clip(X @ coef, -30, 30)
47
+ mu = _exp(z)
48
+ W = _clip(mu, 1e-10, None) / (1.0 + self.alpha * _clip(mu, 1e-10, None))
49
+ if sample_weight is not None:
50
+ W = W * sample_weight
51
+ n_eff = float(sample_weight.sum()) if sample_weight is not None else X.shape[0]
52
+ return X.T @ (X * W[:, None]) / n_eff
53
+
54
+ _lipschitz_safety = 2.0 # NB Hessian varies moderately with mu
55
+
56
+ def lipschitz(self, X, coef, y=None, sample_weight=None):
57
+ z = _clip(X @ coef, -30, 30)
58
+ mu = _exp(z)
59
+ W = _clip(mu, 1e-10, 1e6) / (1.0 + self.alpha * _clip(mu, 1e-10, 1e6))
60
+ if sample_weight is not None:
61
+ W = W * sample_weight
62
+ n_eff = float(sample_weight.sum()) if sample_weight is not None else X.shape[0]
63
+ XtWX = X.T @ (X * W[:, None])
64
+ L = _max_eigval_power(XtWX) / n_eff
65
+ return max(L, 1e-8) # Safety factor applied by solver via _lipschitz_safety
66
+
67
+ def predict(self, X, coef):
68
+ return _exp(X @ coef)
@@ -0,0 +1,60 @@
1
+ """
2
+ Poisson loss: negative Poisson log-likelihood.
3
+
4
+ For count data:
5
+ loss = (1/n) * sum(mu - y*log(mu))
6
+ where mu = exp(X @ coef).
7
+
8
+ Supports numpy / cupy / torch backends via _backend helpers.
9
+ """
10
+ from statgpu.backends._array_ops import _clip, _exp, _log, _sum, _max_eigval_power
11
+ from statgpu.glm_core._base import GLMLoss, register_glm_loss
12
+
13
+
14
+ @register_glm_loss('poisson')
15
+ class PoissonLoss(GLMLoss):
16
+ name = "poisson"
17
+ y_type = "count"
18
+ smooth_gradient = True
19
+ has_hessian = True
20
+ _momentum_beta_cap = 0.5
21
+ _poisson_like = True
22
+
23
+ _MU_LO = 1e-10
24
+ _MU_HI = 1e6 # must exceed typical max(y); clip prevents extreme weights
25
+ _ETA_LO = -30
26
+ _ETA_HI = 30
27
+
28
+ # ── Per-sample formulas (single source of truth) ──────────────────
29
+
30
+ def _mu_from_eta(self, eta):
31
+ return _clip(_exp(_clip(eta, self._ETA_LO, self._ETA_HI)), self._MU_LO, self._MU_HI)
32
+
33
+ def per_sample_value(self, eta, y):
34
+ mu = self._mu_from_eta(eta)
35
+ return mu - y * _log(mu)
36
+
37
+ def per_sample_gradient(self, eta, y):
38
+ mu = self._mu_from_eta(eta)
39
+ return mu - y
40
+
41
+ # ── Hessian / Lipschitz ───────────────────────────────────────────
42
+
43
+ def hessian(self, X, y, coef, sample_weight=None):
44
+ z = _clip(X @ coef, -30, 30)
45
+ mu = _clip(_exp(z), self._MU_LO, self._MU_HI)
46
+ W = mu if sample_weight is None else mu * sample_weight
47
+ n_eff = float(sample_weight.sum()) if sample_weight is not None else X.shape[0]
48
+ return X.T @ (X * W[:, None]) / n_eff
49
+
50
+ def lipschitz(self, X, coef, y=None, sample_weight=None):
51
+ z = _clip(X @ coef, -30, 30)
52
+ mu = _clip(_exp(z), self._MU_LO, self._MU_HI)
53
+ W = mu if sample_weight is None else mu * sample_weight
54
+ n_eff = float(sample_weight.sum()) if sample_weight is not None else X.shape[0]
55
+ XtWX = X.T @ (X * W[:, None])
56
+ L = _max_eigval_power(XtWX) / n_eff
57
+ return max(L, 1e-8)
58
+
59
+ def predict(self, X, coef):
60
+ return _exp(X @ coef)
@@ -0,0 +1,100 @@
1
+ """Legacy solver methods from _solver.py.
2
+
3
+ DO NOT import in production code."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import numpy as np
8
+
9
+ def fista_sqerr_adaptive_l1_fused(
10
+ X, y, penalty_weights, alpha,
11
+ XtX, Xty, yty, n_samples,
12
+ L_init, max_iter, tol,
13
+ backend, no_momentum=False,
14
+ ):
15
+ """Fused FISTA for squared_error + AdaptiveL1 with pre-computed XtX/Xty.
16
+
17
+ Eliminates:
18
+ - Redundant X@coef matmul (uses XtX instead)
19
+ - GPU→CPU syncs (convergence check deferred)
20
+ - Element-wise kernel overhead (fused update+proximal+momentum)
21
+
22
+ Parameters
23
+ ----------
24
+ X, y : array (centered)
25
+ penalty_weights : array (p,) — LLA weights
26
+ alpha : float — penalty alpha
27
+ XtX, Xty, yty : pre-computed
28
+ n_samples : int
29
+ L_init : float — initial Lipschitz
30
+ max_iter, tol : FISTA params
31
+ backend : 'torch' or 'cupy'
32
+ no_momentum : bool
33
+
34
+ Returns
35
+ -------
36
+ coef : array (p,)
37
+ n_iter : int
38
+ """
39
+ p = XtX.shape[0]
40
+ step = 1.0 / L_init
41
+ L = L_init
42
+
43
+ if backend == "torch":
44
+ import torch
45
+ thresh = torch.tensor(
46
+ alpha * penalty_weights * step,
47
+ device=XtX.device, dtype=XtX.dtype,
48
+ )
49
+ coef = torch.zeros(p, device=XtX.device, dtype=XtX.dtype)
50
+ coef_old = coef.clone()
51
+ y_k = coef.clone()
52
+ _fused = _get_sqerr_proximal_torch()
53
+ # Pre-allocate for momentum-free case
54
+ _zero_beta = 0.0
55
+ else:
56
+ import cupy as cp
57
+ thresh = cp.asarray(alpha * penalty_weights * step, dtype=cp.float64)
58
+ coef = cp.zeros(p, dtype=cp.float64)
59
+ coef_old = coef.copy()
60
+ y_k = coef.copy()
61
+ _fused = _get_sqerr_proximal_cupy()
62
+ _zero_beta = 0.0
63
+
64
+ t_k = 1.0
65
+ _sync_interval = 10 # Only check convergence every N iterations
66
+
67
+ iteration = -1 # default if max_iter=0
68
+ for iteration in range(max_iter):
69
+ # Gradient: grad = (XtX @ y_k - Xty) / n
70
+ grad = (XtX @ y_k - Xty) / n_samples
71
+
72
+ # Clip gradients (avoid sync — do it on GPU)
73
+ if iteration % 10 == 0:
74
+ grad = _clip_grad_on_device(grad, coef_old, backend)
75
+
76
+ # Proximal gradient step (no backtracking — Lipschitz is exact for squared_error)
77
+ # Pre-compute momentum coefficient so the fused kernel can apply it in one pass.
78
+ if no_momentum:
79
+ beta_mom = 0.0
80
+ else:
81
+ t_new = (1.0 + np.sqrt(1.0 + 4.0 * t_k * t_k)) / 2.0
82
+ beta_mom = (t_k - 1.0) / t_new
83
+ coef_new, y_k = _fused(y_k, grad, step, thresh, coef_old, beta_mom)
84
+ coef = coef_new
85
+
86
+ # Momentum state update
87
+ if not no_momentum:
88
+ t_k = t_new
89
+
90
+ # Convergence check (device-side, minimal sync)
91
+ if iteration < 20 or iteration % _sync_interval == 0:
92
+ coef_diff_dev = _abs_sum_dev(coef - coef_old)
93
+ if _to_float_scalar(coef_diff_dev) < tol:
94
+ break
95
+
96
+ coef_old = _copy_arr(coef)
97
+
98
+ return _to_numpy(coef), iteration + 1
99
+
100
+
@@ -0,0 +1,53 @@
1
+ """
2
+ Squared error loss: (1/(2n)) * ||y - Xw||^2
3
+
4
+ Convention: loss = (1/(2n)) * sum(resid^2).
5
+ All penalties use alpha*n in the normal equations / CD updates,
6
+ matching the PenalizedGeneralizedLinearModel convention and sklearn.
7
+
8
+ sklearn compatibility mapping:
9
+ - Ridge: sklearn alpha = statgpu alpha * n (statgpu alpha = sklearn alpha / n)
10
+ - Lasso: statgpu alpha = sklearn alpha
11
+ - ElasticNet: statgpu alpha = sklearn alpha
12
+
13
+ Internal consistency: Ridge(alpha=a) == PGLM(alpha=a, penalty='l2')
14
+ for all alpha values (verified to machine precision).
15
+
16
+ Supports numpy / cupy / torch backends via _backend helpers.
17
+ """
18
+ from statgpu.backends._array_ops import _max_eigval_power
19
+ from statgpu.glm_core._base import GLMLoss, register_glm_loss
20
+
21
+
22
+ @register_glm_loss('squared_error')
23
+ class SquaredErrorLoss(GLMLoss):
24
+ name = "squared_error"
25
+ y_type = "continuous"
26
+ smooth_gradient = True
27
+ has_hessian = True
28
+ _is_quadratic = True
29
+ _supports_cholesky = True
30
+
31
+ # ── Per-sample formulas (single source of truth) ──────────────────
32
+
33
+ def per_sample_value(self, eta, y):
34
+ resid = y - eta
35
+ return 0.5 * resid * resid
36
+
37
+ def per_sample_gradient(self, eta, y):
38
+ return eta - y
39
+
40
+ # ── Hessian / Lipschitz (override for weighted support) ───────────
41
+
42
+ def hessian(self, X, y, coef, sample_weight=None):
43
+ if sample_weight is not None:
44
+ return X.T @ (X * sample_weight[:, None]) / sample_weight.sum()
45
+ return X.T @ X / X.shape[0]
46
+
47
+ def lipschitz(self, X, coef, y=None, sample_weight=None):
48
+ if sample_weight is not None:
49
+ sw = sample_weight[:, None] if hasattr(sample_weight, '__len__') else sample_weight
50
+ XtWX = X.T @ (X * sw)
51
+ return _max_eigval_power(XtWX) / sample_weight.sum()
52
+ XtX = X.T @ X
53
+ return _max_eigval_power(XtX) / X.shape[0]
@@ -0,0 +1,74 @@
1
+ """
2
+ Tweedie loss: negative Tweedie log-likelihood with log link.
3
+
4
+ For compound Poisson-Gamma (1 < p < 2) outcomes:
5
+ loss = (1/n) * sum(-y * mu^(1-p)/(1-p) + mu^(2-p)/(2-p))
6
+ where mu = exp(X @ coef), p is the Tweedie power parameter.
7
+
8
+ Supports numpy / cupy / torch backends via _array_ops helpers.
9
+ """
10
+ from statgpu.backends._array_ops import _clip, _exp, _sum, _max_eigval_power
11
+ from statgpu.glm_core._base import GLMLoss, register_glm_loss
12
+
13
+
14
+ @register_glm_loss('tweedie')
15
+ class TweedieLoss(GLMLoss):
16
+ name = "tweedie"
17
+ y_type = "nonnegative"
18
+ smooth_gradient = True
19
+ has_hessian = True
20
+ _lipschitz_uses_y = True
21
+ _lipschitz_safety = 5.0
22
+ _tweedie = True # Tweedie variance function requires large safety
23
+
24
+ # Clip z to [-50, 50] instead of [-500, 500] to prevent
25
+ # mu^(-0.5) explosion: mu >= exp(-50) ~ 1.9e-22 -> mu^(-0.5) <= 2.3e10.
26
+ _Z_CLIP = 50.0
27
+
28
+ _MU_LO = 1e-3
29
+ _MU_HI = 1e4
30
+
31
+ def __init__(self, power=1.5):
32
+ if not 1.0 < power < 2.0:
33
+ raise ValueError(f"Tweedie power must be in (1, 2), got {power}")
34
+ self.power = power
35
+
36
+ def _mu_from_eta(self, eta):
37
+ return _clip(_exp(_clip(eta, -self._Z_CLIP, self._Z_CLIP)), self._MU_LO, self._MU_HI)
38
+
39
+ # ── Per-sample formulas (single source of truth) ──────────────────
40
+
41
+ def per_sample_value(self, eta, y):
42
+ mu = self._mu_from_eta(eta)
43
+ p = self.power
44
+ return -y * mu ** (1.0 - p) / (1.0 - p) + mu ** (2.0 - p) / (2.0 - p)
45
+
46
+ def per_sample_gradient(self, eta, y):
47
+ mu = self._mu_from_eta(eta)
48
+ p = self.power
49
+ return mu ** (1.0 - p) * (mu - y)
50
+
51
+ def hessian(self, X, y, coef, sample_weight=None):
52
+ z = _clip(X @ coef, -self._Z_CLIP, self._Z_CLIP)
53
+ mu = _clip(_exp(z), self._MU_LO, self._MU_HI)
54
+ p = self.power
55
+ W = mu ** (2.0 - p)
56
+ if sample_weight is not None:
57
+ W = W * sample_weight
58
+ n_eff = float(sample_weight.sum()) if sample_weight is not None else X.shape[0]
59
+ return X.T @ (X * W[:, None]) / n_eff
60
+
61
+ def lipschitz(self, X, coef, y=None, sample_weight=None):
62
+ z = _clip(X @ coef, -self._Z_CLIP, self._Z_CLIP)
63
+ mu = _clip(_exp(z), self._MU_LO, self._MU_HI)
64
+ p = self.power
65
+ W = mu ** (2.0 - p)
66
+ if sample_weight is not None:
67
+ W = W * sample_weight
68
+ n_eff = float(sample_weight.sum()) if sample_weight is not None else X.shape[0]
69
+ XtWX = X.T @ (X * W[:, None])
70
+ L = _max_eigval_power(XtWX) / n_eff
71
+ return max(L, 1e-8)
72
+
73
+ def predict(self, X, coef):
74
+ return _exp(X @ coef)
@@ -0,0 +1,239 @@
1
+ """Inference helper utilities shared across models."""
2
+
3
+ from ._distributions_backend import (
4
+ BetaDistributionBase,
5
+ BinomDistributionBase,
6
+ CauchyDistributionBase,
7
+ Chi2DistributionBase,
8
+ ExponDistributionBase,
9
+ FDistributionBase,
10
+ GammaDistributionBase,
11
+ LaplaceDistributionBase,
12
+ LogisticDistributionBase,
13
+ LognormDistributionBase,
14
+ NormDistributionBase,
15
+ PoissonDistributionBase,
16
+ ScipyFallbackDistribution,
17
+ TDistributionBase,
18
+ UniformDistributionBase,
19
+ WeibullMinDistributionBase,
20
+ # Backward-compatible aliases
21
+ BetaDistributionBase as BetaDistributionGPU,
22
+ BinomDistributionBase as BinomDistributionGPU,
23
+ CauchyDistributionBase as CauchyDistributionGPU,
24
+ Chi2DistributionBase as Chi2DistributionGPU,
25
+ ExponDistributionBase as ExponDistributionGPU,
26
+ FDistributionBase as FDistributionGPU,
27
+ GammaDistributionBase as GammaDistributionGPU,
28
+ LaplaceDistributionBase as LaplaceDistributionGPU,
29
+ LogisticDistributionBase as LogisticDistributionGPU,
30
+ LognormDistributionBase as LognormDistributionGPU,
31
+ NormDistributionBase as NormDistributionGPU,
32
+ PoissonDistributionBase as PoissonDistributionGPU,
33
+ TDistributionBase as TDistributionGPU,
34
+ UniformDistributionBase as UniformDistributionGPU,
35
+ WeibullMinDistributionBase as WeibullMinDistributionGPU,
36
+ beta,
37
+ binom,
38
+ cauchy,
39
+ chi2,
40
+ expon,
41
+ f,
42
+ gamma,
43
+ get_distribution,
44
+ get_distribution_gpu,
45
+ laplace,
46
+ list_available_distributions,
47
+ list_available_distributions_gpu,
48
+ logistic,
49
+ lognorm,
50
+ norm,
51
+ poisson,
52
+ t,
53
+ uniform,
54
+ weibull_min,
55
+ )
56
+ from statgpu.linear_model.legacy._distributions_legacy_gpu import (
57
+ dbeta_gpu,
58
+ dbinom_gpu,
59
+ dchisq_gpu,
60
+ df_gpu,
61
+ dgamma_gpu,
62
+ dnorm_gpu,
63
+ dpois_gpu,
64
+ dt_gpu,
65
+ norm_cdf_gpu,
66
+ norm_isf_gpu,
67
+ norm_ppf_gpu,
68
+ norm_sf_gpu,
69
+ norm_two_sided_critical_value_gpu,
70
+ norm_two_sided_pvalue_gpu,
71
+ pbeta_gpu,
72
+ pbinom_gpu,
73
+ pchisq_gpu,
74
+ pf_gpu,
75
+ pgamma_gpu,
76
+ pnorm_gpu,
77
+ ppois_gpu,
78
+ pt_gpu,
79
+ qbeta_gpu,
80
+ qbinom_gpu,
81
+ qchisq_gpu,
82
+ qf_gpu,
83
+ qgamma_gpu,
84
+ qnorm_gpu,
85
+ qpois_gpu,
86
+ qt_gpu,
87
+ rbeta_gpu,
88
+ rbinom_gpu,
89
+ rchisq_gpu,
90
+ rf_gpu,
91
+ rgamma_gpu,
92
+ rnorm_gpu,
93
+ rpois_gpu,
94
+ rt_gpu,
95
+ t_cdf_gpu,
96
+ t_ppf_gpu,
97
+ t_sf_gpu,
98
+ t_two_sided_critical_value_gpu,
99
+ t_two_sided_pvalue_gpu,
100
+ )
101
+ from ._multiple_testing import adjust_pvalues, combine_pvalues, multipletests
102
+ from ._results import (
103
+ BaseInferenceResult,
104
+ DebiasedInferenceResult,
105
+ GaussianInferenceResult,
106
+ OracleActiveSetInferenceResult,
107
+ ParameterInferenceResult,
108
+ ResamplingInferenceResult,
109
+ )
110
+ from ._resampling import (
111
+ BootstrapResult,
112
+ PermutationTestResult,
113
+ bootstrap_statistic,
114
+ permutation_test,
115
+ )
116
+ try:
117
+ from statgpu.nonparametric.kernel_smoothing._kde import (
118
+ KDE,
119
+ KDEBootstrapResult,
120
+ fit_kde,
121
+ kde_pdf,
122
+ kde_bootstrap_confidence_interval,
123
+ )
124
+ except ImportError:
125
+ KDE = None
126
+ KDEBootstrapResult = None
127
+ fit_kde = None
128
+ kde_pdf = None
129
+ kde_bootstrap_confidence_interval = None
130
+
131
+ __all__ = [
132
+ "adjust_pvalues",
133
+ "combine_pvalues",
134
+ "multipletests",
135
+ # Inference results
136
+ "BaseInferenceResult",
137
+ "ParameterInferenceResult",
138
+ "GaussianInferenceResult",
139
+ "DebiasedInferenceResult",
140
+ "OracleActiveSetInferenceResult",
141
+ "ResamplingInferenceResult",
142
+ # Distribution base classes
143
+ "NormDistributionBase",
144
+ "TDistributionBase",
145
+ "UniformDistributionBase",
146
+ "ExponDistributionBase",
147
+ "CauchyDistributionBase",
148
+ "LaplaceDistributionBase",
149
+ "LogisticDistributionBase",
150
+ "Chi2DistributionBase",
151
+ "GammaDistributionBase",
152
+ "BetaDistributionBase",
153
+ "FDistributionBase",
154
+ "WeibullMinDistributionBase",
155
+ "LognormDistributionBase",
156
+ "PoissonDistributionBase",
157
+ "BinomDistributionBase",
158
+ "ScipyFallbackDistribution",
159
+ # Factory
160
+ "get_distribution",
161
+ "get_distribution_gpu",
162
+ "list_available_distributions",
163
+ "list_available_distributions_gpu",
164
+ # Module-level proxies
165
+ "norm", "t", "uniform", "expon", "cauchy", "laplace",
166
+ "logistic", "chi2", "gamma", "beta", "f",
167
+ "weibull_min", "lognorm", "poisson", "binom",
168
+ # Legacy GPU names (backward compat)
169
+ "NormDistributionGPU",
170
+ "TDistributionGPU",
171
+ "UniformDistributionGPU",
172
+ "ExponDistributionGPU",
173
+ "CauchyDistributionGPU",
174
+ "LaplaceDistributionGPU",
175
+ "LogisticDistributionGPU",
176
+ "Chi2DistributionGPU",
177
+ "GammaDistributionGPU",
178
+ "BetaDistributionGPU",
179
+ "FDistributionGPU",
180
+ "WeibullMinDistributionGPU",
181
+ "LognormDistributionGPU",
182
+ "PoissonDistributionGPU",
183
+ "BinomDistributionGPU",
184
+ # Legacy function names
185
+ "norm_cdf_gpu",
186
+ "norm_isf_gpu",
187
+ "norm_ppf_gpu",
188
+ "norm_sf_gpu",
189
+ "norm_two_sided_critical_value_gpu",
190
+ "norm_two_sided_pvalue_gpu",
191
+ "t_cdf_gpu",
192
+ "t_ppf_gpu",
193
+ "t_sf_gpu",
194
+ "t_two_sided_critical_value_gpu",
195
+ "t_two_sided_pvalue_gpu",
196
+ "dnorm_gpu",
197
+ "pnorm_gpu",
198
+ "qnorm_gpu",
199
+ "rnorm_gpu",
200
+ "dt_gpu",
201
+ "pt_gpu",
202
+ "qt_gpu",
203
+ "rt_gpu",
204
+ "dchisq_gpu",
205
+ "pchisq_gpu",
206
+ "qchisq_gpu",
207
+ "rchisq_gpu",
208
+ "dgamma_gpu",
209
+ "pgamma_gpu",
210
+ "qgamma_gpu",
211
+ "rgamma_gpu",
212
+ "dbeta_gpu",
213
+ "pbeta_gpu",
214
+ "qbeta_gpu",
215
+ "rbeta_gpu",
216
+ "df_gpu",
217
+ "pf_gpu",
218
+ "qf_gpu",
219
+ "rf_gpu",
220
+ "dpois_gpu",
221
+ "ppois_gpu",
222
+ "qpois_gpu",
223
+ "rpois_gpu",
224
+ "dbinom_gpu",
225
+ "pbinom_gpu",
226
+ "qbinom_gpu",
227
+ "rbinom_gpu",
228
+ # Resampling
229
+ "BootstrapResult",
230
+ "PermutationTestResult",
231
+ "bootstrap_statistic",
232
+ "permutation_test",
233
+ # KDE
234
+ "KDE",
235
+ "KDEBootstrapResult",
236
+ "fit_kde",
237
+ "kde_pdf",
238
+ "kde_bootstrap_confidence_interval",
239
+ ]