phasmanet 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.
- phasmanet/__init__.py +23 -0
- phasmanet/_version.py +1 -0
- phasmanet/calibration.py +96 -0
- phasmanet/estimators.py +680 -0
- phasmanet/gw/__init__.py +20 -0
- phasmanet/gw/data/h1-aligo-xphm-1m.pt +0 -0
- phasmanet/gw/snr.py +220 -0
- phasmanet/model.py +197 -0
- phasmanet/py.typed +0 -0
- phasmanet-0.1.0.dist-info/METADATA +151 -0
- phasmanet-0.1.0.dist-info/RECORD +14 -0
- phasmanet-0.1.0.dist-info/WHEEL +5 -0
- phasmanet-0.1.0.dist-info/licenses/LICENSE +21 -0
- phasmanet-0.1.0.dist-info/top_level.txt +1 -0
phasmanet/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""PhasmaNet: a staged spectral token-refinement architecture for tabular data.
|
|
2
|
+
|
|
3
|
+
Quick start::
|
|
4
|
+
|
|
5
|
+
from phasmanet import PhasmaNetRegressor
|
|
6
|
+
reg = PhasmaNetRegressor(random_state=0).fit(X_train, y_train)
|
|
7
|
+
y_pred = reg.predict(X_test)
|
|
8
|
+
"""
|
|
9
|
+
from ._version import __version__
|
|
10
|
+
from .calibration import MonotoneCalibrator
|
|
11
|
+
from .estimators import PhasmaNetClassifier, PhasmaNetRegressor
|
|
12
|
+
from .model import PhasmaNet, PhiRefractor, PhiStage, convert_legacy_state_dict
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"__version__",
|
|
16
|
+
"PhasmaNet",
|
|
17
|
+
"PhiStage",
|
|
18
|
+
"PhiRefractor",
|
|
19
|
+
"PhasmaNetRegressor",
|
|
20
|
+
"PhasmaNetClassifier",
|
|
21
|
+
"MonotoneCalibrator",
|
|
22
|
+
"convert_legacy_state_dict",
|
|
23
|
+
]
|
phasmanet/_version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
phasmanet/calibration.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Rank-preserving monotonic (PCHIP) recalibration of point predictions.
|
|
2
|
+
|
|
3
|
+
Knots are the medians of (prediction, target) in quantile bins of the predictions,
|
|
4
|
+
made non-decreasing and extended to the observed range, then joined by a monotone
|
|
5
|
+
piecewise cubic Hermite interpolant. The calibrator is fitted on a validation split and
|
|
6
|
+
applied unchanged to new predictions.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Dict, Optional
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
__all__ = ["MonotoneCalibrator"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MonotoneCalibrator:
|
|
18
|
+
"""Smooth monotonic calibrator mapping raw predictions to calibrated predictions."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, xk: Optional[np.ndarray] = None, yk: Optional[np.ndarray] = None):
|
|
21
|
+
self.xk = None if xk is None else np.asarray(xk, dtype=np.float64)
|
|
22
|
+
self.yk = None if yk is None else np.asarray(yk, dtype=np.float64)
|
|
23
|
+
self._pchip = None
|
|
24
|
+
if self.xk is not None:
|
|
25
|
+
self._build()
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def is_identity(self) -> bool:
|
|
29
|
+
return self.xk is None
|
|
30
|
+
|
|
31
|
+
def _build(self) -> None:
|
|
32
|
+
from scipy.interpolate import PchipInterpolator
|
|
33
|
+
|
|
34
|
+
if self.xk.size < 3 or self.yk.size != self.xk.size:
|
|
35
|
+
raise ValueError("need at least 3 knots with matching xk and yk")
|
|
36
|
+
if np.any(np.diff(self.xk) <= 0):
|
|
37
|
+
raise ValueError("xk must be strictly increasing")
|
|
38
|
+
self._pchip = PchipInterpolator(self.xk, self.yk, extrapolate=True)
|
|
39
|
+
|
|
40
|
+
def fit(self, pred, true, bins: int = 60, min_bin: int = 20) -> "MonotoneCalibrator":
|
|
41
|
+
"""Fit knots from validation predictions ``pred`` and targets ``true``.
|
|
42
|
+
|
|
43
|
+
If there are too few distinct bins the calibrator stays the identity map.
|
|
44
|
+
"""
|
|
45
|
+
pred = np.asarray(pred, dtype=np.float64).ravel()
|
|
46
|
+
true = np.asarray(true, dtype=np.float64).ravel()
|
|
47
|
+
if pred.shape != true.shape:
|
|
48
|
+
raise ValueError("pred and true must have the same length")
|
|
49
|
+
self.xk = self.yk = self._pchip = None
|
|
50
|
+
|
|
51
|
+
edges = np.unique(np.quantile(pred, np.linspace(0.0, 1.0, bins + 1)))
|
|
52
|
+
if len(edges) < 4:
|
|
53
|
+
return self
|
|
54
|
+
|
|
55
|
+
xk, yk = [], []
|
|
56
|
+
for lo, hi in zip(edges[:-1], edges[1:]):
|
|
57
|
+
m = (pred >= lo) & (pred < hi)
|
|
58
|
+
if m.sum() >= min_bin:
|
|
59
|
+
xk.append(np.median(pred[m]))
|
|
60
|
+
yk.append(np.median(true[m]))
|
|
61
|
+
xk = np.asarray(xk, dtype=np.float64)
|
|
62
|
+
yk = np.asarray(yk, dtype=np.float64)
|
|
63
|
+
if xk.size < 3:
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
yk = np.maximum.accumulate(yk)
|
|
67
|
+
xk, keep = np.unique(xk, return_index=True)
|
|
68
|
+
yk = yk[keep]
|
|
69
|
+
|
|
70
|
+
x_min, x_max = pred.min(), pred.max()
|
|
71
|
+
y_min, y_max = true.min(), true.max()
|
|
72
|
+
if x_min < xk[0]:
|
|
73
|
+
xk = np.r_[x_min, xk]
|
|
74
|
+
yk = np.r_[min(y_min, yk[0]), yk]
|
|
75
|
+
if x_max > xk[-1]:
|
|
76
|
+
xk = np.r_[xk, x_max]
|
|
77
|
+
yk = np.r_[yk, max(y_max, yk[-1])]
|
|
78
|
+
self.xk, self.yk = xk, np.maximum.accumulate(yk)
|
|
79
|
+
self._build()
|
|
80
|
+
return self
|
|
81
|
+
|
|
82
|
+
def __call__(self, x) -> np.ndarray:
|
|
83
|
+
x = np.asarray(x, dtype=np.float64)
|
|
84
|
+
return x.copy() if self._pchip is None else self._pchip(x)
|
|
85
|
+
|
|
86
|
+
transform = __call__
|
|
87
|
+
|
|
88
|
+
def to_dict(self) -> Dict[str, list]:
|
|
89
|
+
if self.is_identity:
|
|
90
|
+
return {"xk": [], "yk": []}
|
|
91
|
+
return {"xk": self.xk.tolist(), "yk": self.yk.tolist()}
|
|
92
|
+
|
|
93
|
+
@classmethod
|
|
94
|
+
def from_dict(cls, d: Dict[str, list]) -> "MonotoneCalibrator":
|
|
95
|
+
xk, yk = d.get("xk") or [], d.get("yk") or []
|
|
96
|
+
return cls() if len(xk) == 0 else cls(xk, yk)
|