phasmanet 0.1.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.
- phasmanet-0.1.0/LICENSE +21 -0
- phasmanet-0.1.0/PKG-INFO +151 -0
- phasmanet-0.1.0/README.md +119 -0
- phasmanet-0.1.0/pyproject.toml +58 -0
- phasmanet-0.1.0/setup.cfg +4 -0
- phasmanet-0.1.0/src/phasmanet/__init__.py +23 -0
- phasmanet-0.1.0/src/phasmanet/_version.py +1 -0
- phasmanet-0.1.0/src/phasmanet/calibration.py +96 -0
- phasmanet-0.1.0/src/phasmanet/estimators.py +680 -0
- phasmanet-0.1.0/src/phasmanet/gw/__init__.py +20 -0
- phasmanet-0.1.0/src/phasmanet/gw/data/h1-aligo-xphm-1m.pt +0 -0
- phasmanet-0.1.0/src/phasmanet/gw/snr.py +220 -0
- phasmanet-0.1.0/src/phasmanet/model.py +197 -0
- phasmanet-0.1.0/src/phasmanet/py.typed +0 -0
- phasmanet-0.1.0/src/phasmanet.egg-info/PKG-INFO +151 -0
- phasmanet-0.1.0/src/phasmanet.egg-info/SOURCES.txt +21 -0
- phasmanet-0.1.0/src/phasmanet.egg-info/dependency_links.txt +1 -0
- phasmanet-0.1.0/src/phasmanet.egg-info/requires.txt +11 -0
- phasmanet-0.1.0/src/phasmanet.egg-info/top_level.txt +1 -0
- phasmanet-0.1.0/tests/test_calibration.py +43 -0
- phasmanet-0.1.0/tests/test_estimators.py +147 -0
- phasmanet-0.1.0/tests/test_gw.py +56 -0
- phasmanet-0.1.0/tests/test_model.py +44 -0
phasmanet-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ioannis Tsioulis, Anastasios Tefas, Nikolaos Stergioulas
|
|
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.
|
phasmanet-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phasmanet
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PhasmaNet: a staged spectral token-refinement architecture for tabular regression and classification, with a pretrained gravitational-wave SNR surrogate.
|
|
5
|
+
Author: Ioannis Tsioulis, Anastasios Tefas, Nikolaos Stergioulas
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/giannis00/PhasmaNet-SNR
|
|
8
|
+
Project-URL: Source, https://github.com/giannis00/PhasmaNet-SNR
|
|
9
|
+
Project-URL: Issues, https://github.com/giannis00/PhasmaNet-SNR/issues
|
|
10
|
+
Keywords: deep learning,tabular data,neural surrogate,pytorch,gravitational waves,signal-to-noise ratio,selection effects
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: numpy>=1.21
|
|
23
|
+
Requires-Dist: scipy>=1.7
|
|
24
|
+
Requires-Dist: torch>=1.13
|
|
25
|
+
Provides-Extra: sklearn
|
|
26
|
+
Requires-Dist: scikit-learn>=1.0; extra == "sklearn"
|
|
27
|
+
Provides-Extra: test
|
|
28
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
29
|
+
Requires-Dist: scikit-learn>=1.0; extra == "test"
|
|
30
|
+
Requires-Dist: pandas>=1.3; extra == "test"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<img src="https://raw.githubusercontent.com/giannis00/PhasmaNet-SNR/main/assets/phasmanet_symbol.png" alt="PhasmaNet symbol" width="140">
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
<h1 align="center">PhasmaNet</h1>
|
|
38
|
+
|
|
39
|
+
PhasmaNet (from Greek *φάσμα*, spectrum) is a compact neural architecture for structured
|
|
40
|
+
tabular data. Learned input tokens are refined by an ordered cascade of attention-based
|
|
41
|
+
**Φ-Stages**, each followed by a **Φ-Refractor** subspace-mixing module, and then revisited
|
|
42
|
+
through gated top-down **Φ-Feedback** passes.
|
|
43
|
+
|
|
44
|
+
This package provides:
|
|
45
|
+
|
|
46
|
+
* `PhasmaNetRegressor` and `PhasmaNetClassifier`: estimators with the familiar
|
|
47
|
+
`fit` / `predict` interface, usable on any table of numerical features and compatible
|
|
48
|
+
with scikit-learn tools (`cross_val_score`, `Pipeline`, `clone`).
|
|
49
|
+
* `phasmanet.gw.optimal_snr`: a pretrained surrogate for the phase-averaged optimal
|
|
50
|
+
signal-to-noise ratio of precessing, higher-mode binary black hole signals.
|
|
51
|
+
* `PhasmaNet`, the PyTorch module itself, and `MonotoneCalibrator`, the rank-preserving
|
|
52
|
+
PCHIP calibration used in the paper.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install phasmanet
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
PhasmaNet needs PyTorch. For GPU support, install the PyTorch build that matches your
|
|
61
|
+
system first, following https://pytorch.org/get-started/locally/, and then install
|
|
62
|
+
`phasmanet`. scikit-learn is optional (`pip install "phasmanet[sklearn]"`).
|
|
63
|
+
|
|
64
|
+
## Regression and classification on your own data
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from phasmanet import PhasmaNetRegressor
|
|
68
|
+
|
|
69
|
+
reg = PhasmaNetRegressor(random_state=0) # architecture of the paper by default
|
|
70
|
+
reg.fit(X_train, y_train) # early stopping on a 15% validation split
|
|
71
|
+
y_pred = reg.predict(X_test)
|
|
72
|
+
print(reg.score(X_test, y_test)) # R^2
|
|
73
|
+
|
|
74
|
+
reg.save("model.pt")
|
|
75
|
+
reg = PhasmaNetRegressor.load("model.pt")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from phasmanet import PhasmaNetClassifier
|
|
80
|
+
|
|
81
|
+
clf = PhasmaNetClassifier(random_state=0).fit(X_train, y_train)
|
|
82
|
+
proba = clf.predict_proba(X_test)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Inputs are standardised internally, so raw features can be passed directly.
|
|
86
|
+
|
|
87
|
+
### Hyperparameters
|
|
88
|
+
|
|
89
|
+
Every hyperparameter is a constructor argument; the defaults are the configuration used in
|
|
90
|
+
the paper (about 0.73 M parameters).
|
|
91
|
+
|
|
92
|
+
| Group | Argument (default) |
|
|
93
|
+
|---|---|
|
|
94
|
+
| Architecture | `num_stages` (3), `num_feedback` (2), `num_tokens` (8), `hidden_dim` (128), `heads` (4), `num_refractor_subspaces` (4), `use_refractor` (True), `ffn_mult` (4), `activation` ("gelu"; also "relu", "silu", "tanh", "elu", "leaky_relu"), `dropout` (0.1), `attn_dropout` (0.0) |
|
|
95
|
+
| Optimisation | `optimizer` ("adamw", "adam", "sgd"), `lr` (1e-3), `weight_decay` (1e-4), `momentum` (0.9, SGD), `batch_size` (128), `grad_clip` (None) |
|
|
96
|
+
| Schedule and stopping | `max_epochs` (150), `patience` (20), `min_delta` (1e-6), `scheduler` ("plateau", "cosine", "none"), `lr_patience` (5), `lr_factor` (0.5), `validation_fraction` (0.15) |
|
|
97
|
+
| Regression only | `loss` ("mse" or "huber"), `huber_delta` (1.0), `weighting` ("none", "by_hist", "inverse_freq"), `hist_bins` (20), `wmin` (0.2), `wmax` (5.0), `calibrate` (False), `calibration_bins` (60), `calibration_min_bin` (20) |
|
|
98
|
+
| Classification only | `class_weight` (None or "balanced") |
|
|
99
|
+
| Other | `device` ("auto"), `random_state` (None), `verbose` (0) |
|
|
100
|
+
|
|
101
|
+
`fit` also accepts `sample_weight` and `validation_data=(X_val, y_val)`. After fitting,
|
|
102
|
+
`history_` holds the training and validation loss and the learning rate per epoch.
|
|
103
|
+
Because the estimators follow the scikit-learn conventions, hyperparameters can be
|
|
104
|
+
searched with `GridSearchCV`, `RandomizedSearchCV` or Optuna.
|
|
105
|
+
|
|
106
|
+
The defaults were chosen for smooth, low-noise targets such as simulator outputs; for
|
|
107
|
+
other data, tune at least `hidden_dim`, `num_tokens`, `lr` and `max_epochs`.
|
|
108
|
+
|
|
109
|
+
## Optimal SNR of binary black hole signals
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
import numpy as np
|
|
113
|
+
from phasmanet.gw import optimal_snr, chirp_mass, mass_ratio, spins_from_spherical
|
|
114
|
+
|
|
115
|
+
m1, m2 = np.array([36.0, 10.0]), np.array([29.0, 8.0]) # detector-frame masses, Msun
|
|
116
|
+
rho = optimal_snr(
|
|
117
|
+
mchirp=chirp_mass(m1, m2),
|
|
118
|
+
q=mass_ratio(m1, m2), # m_heavy / m_light >= 1
|
|
119
|
+
cos_inclination=np.array([0.3, -0.8]),
|
|
120
|
+
spin1=spins_from_spherical([0.5, 0.1], [0.4, 2.0], [1.0, 3.0]),
|
|
121
|
+
spin2=spins_from_spherical([0.2, 0.0], [1.0, 0.0], [0.0, 0.0]),
|
|
122
|
+
f_plus=np.array([0.4, -0.2]),
|
|
123
|
+
f_cross=np.array([0.1, 0.6]),
|
|
124
|
+
distance=np.array([410.0, 800.0]), # luminosity distance, Mpc
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Configuration of the pretrained model: single LIGO-Hanford detector, Advanced LIGO
|
|
129
|
+
zero-detuned high-power PSD, IMRPhenomXPHM waveforms, f_low = 10 Hz, SNR averaged over
|
|
130
|
+
the coalescence phase. Training covered component masses 3 to 170 Msun (detector frame),
|
|
131
|
+
spin magnitudes up to 0.99 and SNR below 200 at 1000 Mpc. The optimal SNR scales exactly
|
|
132
|
+
as 1/d_L, which `optimal_snr` applies for the given distance. Inputs outside the training
|
|
133
|
+
domain produce a warning. Throughput depends strongly on the hardware: a GPU is
|
|
134
|
+
recommended for catalogue-scale evaluations (millions of injections). Antenna patterns `f_plus`, `f_cross` can be computed with
|
|
135
|
+
PyCBC, Bilby or LALSuite for any sky position, polarisation and time.
|
|
136
|
+
|
|
137
|
+
## Reproducing the paper
|
|
138
|
+
|
|
139
|
+
The training scripts, baselines and diagnostics used in the paper are in the
|
|
140
|
+
repository: https://github.com/giannis00/PhasmaNet-SNR
|
|
141
|
+
|
|
142
|
+
## Citation
|
|
143
|
+
|
|
144
|
+
If you use PhasmaNet, please cite the paper (see `CITATION.cff`):
|
|
145
|
+
|
|
146
|
+
> I. Tsioulis, A. Tefas, N. Stergioulas, *ΦasmaNet: A Staged Spectral Token-Refinement
|
|
147
|
+
> Architecture for Fast Gravitational-Wave Signal-to-Noise Ratio Prediction*, arXiv (2026).
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/giannis00/PhasmaNet-SNR/main/assets/phasmanet_symbol.png" alt="PhasmaNet symbol" width="140">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">PhasmaNet</h1>
|
|
6
|
+
|
|
7
|
+
PhasmaNet (from Greek *φάσμα*, spectrum) is a compact neural architecture for structured
|
|
8
|
+
tabular data. Learned input tokens are refined by an ordered cascade of attention-based
|
|
9
|
+
**Φ-Stages**, each followed by a **Φ-Refractor** subspace-mixing module, and then revisited
|
|
10
|
+
through gated top-down **Φ-Feedback** passes.
|
|
11
|
+
|
|
12
|
+
This package provides:
|
|
13
|
+
|
|
14
|
+
* `PhasmaNetRegressor` and `PhasmaNetClassifier`: estimators with the familiar
|
|
15
|
+
`fit` / `predict` interface, usable on any table of numerical features and compatible
|
|
16
|
+
with scikit-learn tools (`cross_val_score`, `Pipeline`, `clone`).
|
|
17
|
+
* `phasmanet.gw.optimal_snr`: a pretrained surrogate for the phase-averaged optimal
|
|
18
|
+
signal-to-noise ratio of precessing, higher-mode binary black hole signals.
|
|
19
|
+
* `PhasmaNet`, the PyTorch module itself, and `MonotoneCalibrator`, the rank-preserving
|
|
20
|
+
PCHIP calibration used in the paper.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install phasmanet
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
PhasmaNet needs PyTorch. For GPU support, install the PyTorch build that matches your
|
|
29
|
+
system first, following https://pytorch.org/get-started/locally/, and then install
|
|
30
|
+
`phasmanet`. scikit-learn is optional (`pip install "phasmanet[sklearn]"`).
|
|
31
|
+
|
|
32
|
+
## Regression and classification on your own data
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from phasmanet import PhasmaNetRegressor
|
|
36
|
+
|
|
37
|
+
reg = PhasmaNetRegressor(random_state=0) # architecture of the paper by default
|
|
38
|
+
reg.fit(X_train, y_train) # early stopping on a 15% validation split
|
|
39
|
+
y_pred = reg.predict(X_test)
|
|
40
|
+
print(reg.score(X_test, y_test)) # R^2
|
|
41
|
+
|
|
42
|
+
reg.save("model.pt")
|
|
43
|
+
reg = PhasmaNetRegressor.load("model.pt")
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from phasmanet import PhasmaNetClassifier
|
|
48
|
+
|
|
49
|
+
clf = PhasmaNetClassifier(random_state=0).fit(X_train, y_train)
|
|
50
|
+
proba = clf.predict_proba(X_test)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Inputs are standardised internally, so raw features can be passed directly.
|
|
54
|
+
|
|
55
|
+
### Hyperparameters
|
|
56
|
+
|
|
57
|
+
Every hyperparameter is a constructor argument; the defaults are the configuration used in
|
|
58
|
+
the paper (about 0.73 M parameters).
|
|
59
|
+
|
|
60
|
+
| Group | Argument (default) |
|
|
61
|
+
|---|---|
|
|
62
|
+
| Architecture | `num_stages` (3), `num_feedback` (2), `num_tokens` (8), `hidden_dim` (128), `heads` (4), `num_refractor_subspaces` (4), `use_refractor` (True), `ffn_mult` (4), `activation` ("gelu"; also "relu", "silu", "tanh", "elu", "leaky_relu"), `dropout` (0.1), `attn_dropout` (0.0) |
|
|
63
|
+
| Optimisation | `optimizer` ("adamw", "adam", "sgd"), `lr` (1e-3), `weight_decay` (1e-4), `momentum` (0.9, SGD), `batch_size` (128), `grad_clip` (None) |
|
|
64
|
+
| Schedule and stopping | `max_epochs` (150), `patience` (20), `min_delta` (1e-6), `scheduler` ("plateau", "cosine", "none"), `lr_patience` (5), `lr_factor` (0.5), `validation_fraction` (0.15) |
|
|
65
|
+
| Regression only | `loss` ("mse" or "huber"), `huber_delta` (1.0), `weighting` ("none", "by_hist", "inverse_freq"), `hist_bins` (20), `wmin` (0.2), `wmax` (5.0), `calibrate` (False), `calibration_bins` (60), `calibration_min_bin` (20) |
|
|
66
|
+
| Classification only | `class_weight` (None or "balanced") |
|
|
67
|
+
| Other | `device` ("auto"), `random_state` (None), `verbose` (0) |
|
|
68
|
+
|
|
69
|
+
`fit` also accepts `sample_weight` and `validation_data=(X_val, y_val)`. After fitting,
|
|
70
|
+
`history_` holds the training and validation loss and the learning rate per epoch.
|
|
71
|
+
Because the estimators follow the scikit-learn conventions, hyperparameters can be
|
|
72
|
+
searched with `GridSearchCV`, `RandomizedSearchCV` or Optuna.
|
|
73
|
+
|
|
74
|
+
The defaults were chosen for smooth, low-noise targets such as simulator outputs; for
|
|
75
|
+
other data, tune at least `hidden_dim`, `num_tokens`, `lr` and `max_epochs`.
|
|
76
|
+
|
|
77
|
+
## Optimal SNR of binary black hole signals
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import numpy as np
|
|
81
|
+
from phasmanet.gw import optimal_snr, chirp_mass, mass_ratio, spins_from_spherical
|
|
82
|
+
|
|
83
|
+
m1, m2 = np.array([36.0, 10.0]), np.array([29.0, 8.0]) # detector-frame masses, Msun
|
|
84
|
+
rho = optimal_snr(
|
|
85
|
+
mchirp=chirp_mass(m1, m2),
|
|
86
|
+
q=mass_ratio(m1, m2), # m_heavy / m_light >= 1
|
|
87
|
+
cos_inclination=np.array([0.3, -0.8]),
|
|
88
|
+
spin1=spins_from_spherical([0.5, 0.1], [0.4, 2.0], [1.0, 3.0]),
|
|
89
|
+
spin2=spins_from_spherical([0.2, 0.0], [1.0, 0.0], [0.0, 0.0]),
|
|
90
|
+
f_plus=np.array([0.4, -0.2]),
|
|
91
|
+
f_cross=np.array([0.1, 0.6]),
|
|
92
|
+
distance=np.array([410.0, 800.0]), # luminosity distance, Mpc
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Configuration of the pretrained model: single LIGO-Hanford detector, Advanced LIGO
|
|
97
|
+
zero-detuned high-power PSD, IMRPhenomXPHM waveforms, f_low = 10 Hz, SNR averaged over
|
|
98
|
+
the coalescence phase. Training covered component masses 3 to 170 Msun (detector frame),
|
|
99
|
+
spin magnitudes up to 0.99 and SNR below 200 at 1000 Mpc. The optimal SNR scales exactly
|
|
100
|
+
as 1/d_L, which `optimal_snr` applies for the given distance. Inputs outside the training
|
|
101
|
+
domain produce a warning. Throughput depends strongly on the hardware: a GPU is
|
|
102
|
+
recommended for catalogue-scale evaluations (millions of injections). Antenna patterns `f_plus`, `f_cross` can be computed with
|
|
103
|
+
PyCBC, Bilby or LALSuite for any sky position, polarisation and time.
|
|
104
|
+
|
|
105
|
+
## Reproducing the paper
|
|
106
|
+
|
|
107
|
+
The training scripts, baselines and diagnostics used in the paper are in the
|
|
108
|
+
repository: https://github.com/giannis00/PhasmaNet-SNR
|
|
109
|
+
|
|
110
|
+
## Citation
|
|
111
|
+
|
|
112
|
+
If you use PhasmaNet, please cite the paper (see `CITATION.cff`):
|
|
113
|
+
|
|
114
|
+
> I. Tsioulis, A. Tefas, N. Stergioulas, *ΦasmaNet: A Staged Spectral Token-Refinement
|
|
115
|
+
> Architecture for Fast Gravitational-Wave Signal-to-Noise Ratio Prediction*, arXiv (2026).
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "phasmanet"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "PhasmaNet: a staged spectral token-refinement architecture for tabular regression and classification, with a pretrained gravitational-wave SNR surrogate."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Ioannis Tsioulis" },
|
|
15
|
+
{ name = "Anastasios Tefas" },
|
|
16
|
+
{ name = "Nikolaos Stergioulas" },
|
|
17
|
+
]
|
|
18
|
+
keywords = [
|
|
19
|
+
"deep learning", "tabular data", "neural surrogate", "pytorch",
|
|
20
|
+
"gravitational waves", "signal-to-noise ratio", "selection effects",
|
|
21
|
+
]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 4 - Beta",
|
|
24
|
+
"Intended Audience :: Science/Research",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
28
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
29
|
+
"Topic :: Scientific/Engineering :: Astronomy",
|
|
30
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
31
|
+
]
|
|
32
|
+
dependencies = [
|
|
33
|
+
"numpy>=1.21",
|
|
34
|
+
"scipy>=1.7",
|
|
35
|
+
"torch>=1.13",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
sklearn = ["scikit-learn>=1.0"]
|
|
40
|
+
test = ["pytest>=7", "scikit-learn>=1.0", "pandas>=1.3"]
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/giannis00/PhasmaNet-SNR"
|
|
44
|
+
Source = "https://github.com/giannis00/PhasmaNet-SNR"
|
|
45
|
+
Issues = "https://github.com/giannis00/PhasmaNet-SNR/issues"
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.dynamic]
|
|
48
|
+
version = { attr = "phasmanet._version.__version__" }
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
where = ["src"]
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.package-data]
|
|
54
|
+
phasmanet = ["py.typed"]
|
|
55
|
+
"phasmanet.gw" = ["data/*.pt"]
|
|
56
|
+
|
|
57
|
+
[tool.pytest.ini_options]
|
|
58
|
+
testpaths = ["tests"]
|
|
@@ -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
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -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)
|