robustsignalmaker 0.2.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.
@@ -0,0 +1,188 @@
1
+ """RobustSignalMaker: NaN-aware, leakage-free stability selection for signals.
2
+
3
+ Third sibling of RobustModelMaker (tabular columns) and RobustPixelMaker
4
+ (image patches): RSM identifies the important parts of scientific signals and
5
+ spectra (time-, space-, mass-series) to retain and removes the rest, with
6
+ missing data treated as absent evidence rather than something to fabricate.
7
+ See RSM_PLAN.md for the design and build order.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from .config import RSMConfig, fit_count
12
+ from .metrics import (
13
+ BaselineComparison,
14
+ adjusted_jaccard,
15
+ expected_jaccard,
16
+ jaccard,
17
+ mean_pairwise_jaccard,
18
+ paired_comparison,
19
+ rmse_from_score,
20
+ score_predictions,
21
+ )
22
+ from .masking import (
23
+ SoftMaskSelector,
24
+ loss_and_dz,
25
+ mask_data_gradient,
26
+ renormalised_gated_forward,
27
+ )
28
+ from .nested_cv import (
29
+ BootstrapMaskFoldEstimator,
30
+ EnsembleMaskFoldEstimator,
31
+ FoldEstimator,
32
+ FullSignalFoldEstimator,
33
+ NestedCV,
34
+ SoftMaskFoldEstimator,
35
+ infer_task,
36
+ make_inner_splitter,
37
+ make_outer_splitter,
38
+ )
39
+ from .representations import (
40
+ GaussianScale1D,
41
+ RandomConv1D,
42
+ SavGolDerivative,
43
+ SegmentMean,
44
+ SegmentStats,
45
+ default_ensemble,
46
+ )
47
+ from .reproducibility import (
48
+ BOOTSTRAP_OFFSET,
49
+ INNER_OFFSET,
50
+ REPEAT_STRIDE,
51
+ REPRESENTATION_OFFSET,
52
+ Seeds,
53
+ set_global_seed,
54
+ )
55
+ from .models import (
56
+ ALGORITHMS,
57
+ ExternalRefit,
58
+ PLSDA,
59
+ make_refit_model,
60
+ refit_features,
61
+ )
62
+ from .results import RSMResult
63
+ from .segments import SegmentGrid
64
+ from .selection import (
65
+ BootstrapMaskSelector,
66
+ RepresentationEnsembleSelector,
67
+ complementary_pair,
68
+ observation_groups,
69
+ resample_indices,
70
+ )
71
+ from .tuning import lam_for_coverage, lam_frontier
72
+ from .synthetic import (
73
+ SignalControl,
74
+ drop_labels,
75
+ make_signal_control,
76
+ mask_band_by_group,
77
+ mask_dropout_stretches,
78
+ mask_saturation_censor,
79
+ mask_scattered,
80
+ )
81
+ from .validity import (
82
+ InsufficientEvidenceError,
83
+ MissingnessInformativeWarning,
84
+ StandardisationStats,
85
+ as_validity,
86
+ fabricated_edge,
87
+ fill_mean,
88
+ fill_zero,
89
+ is_contrast_filter,
90
+ masked_mean,
91
+ masked_standardise,
92
+ masked_standardise_fit,
93
+ masked_std,
94
+ missingness_association,
95
+ observation_support,
96
+ renormalised_convolve1d,
97
+ renormalised_dot,
98
+ )
99
+
100
+ __version__ = "0.2.0"
101
+
102
+ __all__ = [
103
+ "__version__",
104
+ # config
105
+ "RSMConfig",
106
+ "fit_count",
107
+ # reproducibility
108
+ "Seeds",
109
+ "set_global_seed",
110
+ "BOOTSTRAP_OFFSET",
111
+ "REPRESENTATION_OFFSET",
112
+ "INNER_OFFSET",
113
+ "REPEAT_STRIDE",
114
+ # validity
115
+ "InsufficientEvidenceError",
116
+ "MissingnessInformativeWarning",
117
+ "StandardisationStats",
118
+ "as_validity",
119
+ "masked_mean",
120
+ "masked_std",
121
+ "masked_standardise_fit",
122
+ "masked_standardise",
123
+ "renormalised_convolve1d",
124
+ "renormalised_dot",
125
+ "is_contrast_filter",
126
+ "observation_support",
127
+ "missingness_association",
128
+ "fill_zero",
129
+ "fill_mean",
130
+ "fabricated_edge",
131
+ # segments
132
+ "SegmentGrid",
133
+ # masking
134
+ "SoftMaskSelector",
135
+ "renormalised_gated_forward",
136
+ "mask_data_gradient",
137
+ "loss_and_dz",
138
+ # representations
139
+ "SegmentMean",
140
+ "SegmentStats",
141
+ "SavGolDerivative",
142
+ "GaussianScale1D",
143
+ "RandomConv1D",
144
+ "default_ensemble",
145
+ # nested CV and results
146
+ "NestedCV",
147
+ "FoldEstimator",
148
+ "SoftMaskFoldEstimator",
149
+ "BootstrapMaskFoldEstimator",
150
+ "FullSignalFoldEstimator",
151
+ "infer_task",
152
+ "make_outer_splitter",
153
+ "make_inner_splitter",
154
+ "RSMResult",
155
+ # refit model zoo
156
+ "ALGORITHMS",
157
+ "make_refit_model",
158
+ "PLSDA",
159
+ "ExternalRefit",
160
+ "refit_features",
161
+ # selection
162
+ "BootstrapMaskSelector",
163
+ "RepresentationEnsembleSelector",
164
+ "EnsembleMaskFoldEstimator",
165
+ "resample_indices",
166
+ "complementary_pair",
167
+ "observation_groups",
168
+ # tuning
169
+ "lam_frontier",
170
+ "lam_for_coverage",
171
+ # synthetic
172
+ "SignalControl",
173
+ "make_signal_control",
174
+ "mask_scattered",
175
+ "mask_dropout_stretches",
176
+ "mask_band_by_group",
177
+ "mask_saturation_censor",
178
+ "drop_labels",
179
+ # metrics
180
+ "score_predictions",
181
+ "rmse_from_score",
182
+ "jaccard",
183
+ "expected_jaccard",
184
+ "adjusted_jaccard",
185
+ "mean_pairwise_jaccard",
186
+ "BaselineComparison",
187
+ "paired_comparison",
188
+ ]
@@ -0,0 +1,91 @@
1
+ """Configuration: user-facing knobs with robust defaults, and the fit-cost model.
2
+
3
+ Design constraints (RSM_PLAN.md sec.6):
4
+ * numpy-only: no torch, no device management. RPM's device detection is
5
+ deliberately not ported; every fit is a hand-derived-gradient numpy loop.
6
+ * User-defined where possible with robust defaults: every knob here has a
7
+ sane default. The evidence thresholds are scientific choices adopted with
8
+ the user (RSM_PLAN.md sec.11) and are scheduled for a sensitivity sweep in
9
+ Milestone 8 before being considered final.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass, asdict
14
+
15
+
16
+ @dataclass
17
+ class RSMConfig:
18
+ """All user-facing knobs with robust defaults.
19
+
20
+ Fields beyond the Milestone-1 scaffold are declared here so the config
21
+ surface is stable as later milestones land (RPM convention).
22
+ """
23
+
24
+ # --- selection unit (sec.2) ---
25
+ segment: int = 16 # points per segment; 1 gives exact point-wise mode
26
+ channel_mode: str = "joint" # joint | per_channel (per_channel is a later extension)
27
+
28
+ # --- cross-validation ---
29
+ k_outer: int = 5
30
+ l_inner: int = 5
31
+ repeated_outer_cv: int = 1
32
+ n_iter: int = 20 # randomized hyperparameter configurations per inner CV
33
+
34
+ # --- stability selection ---
35
+ n_bootstrap: int = 20
36
+ n_representations: int = 4
37
+ tau: float = 0.7 # stability threshold (RMM default)
38
+ lam: float = 0.03 # mask sparsity strength
39
+ subsample: str = "bootstrap" # bootstrap | half | complementary
40
+
41
+ # --- masking / gates ---
42
+ gate: str = "hardconcrete" # hardconcrete | sigmoid
43
+ tv: float = 0.0 # 1D total-variation prior strength (contiguous bands)
44
+
45
+ # --- evidence thresholds (adopted defaults; M8 sensitivity sweep) ---
46
+ min_valid_frac: float = 0.5 # segment observation fraction below which a
47
+ # (sample, segment) feature is ineligible
48
+ rho_min: float = 0.1 # per-sample observed-evidence floor; below it a
49
+ # prediction is nan, never an amplified guess
50
+ o_min: float = 0.1 # per-resample segment support floor for assessability
51
+ a_min: float = 0.5 # fraction of resamples that must assess a segment
52
+ # before pi is reported as a number rather than nan.
53
+ # Measured (FINDINGS sec.10): this binds only when
54
+ # the observation patterns are too varied to group.
55
+ # With groups detected, resampling draws a fixed
56
+ # count per stratum, support is deterministic, and
57
+ # a_min cannot act.
58
+ o_floor: float = 0.2 # floor for observation-scaled sparsity penalty
59
+ n_min_hard: int = 8 # effective sample size below which a segment-channel
60
+ # is unassessable (excluded, reported)
61
+ n_min_warn: int = 30 # effective sample size below which it is flagged
62
+
63
+ # --- missing y ---
64
+ use_unlabelled_stats: bool = False # opt-in: unlabelled TRAINING rows may
65
+ # contribute to X-only statistics
66
+
67
+ # --- execution ---
68
+ n_jobs: int = 1
69
+ random_state: int = 0
70
+
71
+ def to_dict(self) -> dict:
72
+ return asdict(self)
73
+
74
+
75
+ def fit_count(
76
+ k_outer: int,
77
+ repeated_outer_cv: int,
78
+ n_representations: int,
79
+ n_bootstrap: int,
80
+ n_iter: int,
81
+ l_inner: int,
82
+ ) -> int:
83
+ """Total model-fit count (RPM's extension of RMM Eq.3, unchanged for 1D).
84
+
85
+ F = K * R_out * (R_rep * B + n_iter * L + 1) + (R_rep * B + n_iter * L + 1)
86
+
87
+ The trailing term is the final selection + final inner search + final
88
+ refit on all training data. Use this to size runs before launching.
89
+ """
90
+ per = n_representations * n_bootstrap + n_iter * l_inner + 1
91
+ return k_outer * repeated_outer_cv * per + per