robust-adapt 1.0.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 (77) hide show
  1. robust_adapt/__init__.py +265 -0
  2. robust_adapt/ala/__init__.py +54 -0
  3. robust_adapt/ala/adwin.py +243 -0
  4. robust_adapt/ala/classifier.py +544 -0
  5. robust_adapt/ala/concept_memory.py +457 -0
  6. robust_adapt/ala/delayed_labels.py +206 -0
  7. robust_adapt/ala/detector.py +297 -0
  8. robust_adapt/ala/drift_classifier.py +222 -0
  9. robust_adapt/ala/ensemble.py +103 -0
  10. robust_adapt/ala/replay.py +285 -0
  11. robust_adapt/ala/tree.py +433 -0
  12. robust_adapt/backends/__init__.py +25 -0
  13. robust_adapt/backends/base.py +42 -0
  14. robust_adapt/backends/numpy_backend.py +37 -0
  15. robust_adapt/backends/torch_backend.py +51 -0
  16. robust_adapt/core/__init__.py +121 -0
  17. robust_adapt/core/config.py +71 -0
  18. robust_adapt/core/constants.py +27 -0
  19. robust_adapt/core/diagnostics.py +169 -0
  20. robust_adapt/core/exceptions.py +56 -0
  21. robust_adapt/core/telemetry.py +92 -0
  22. robust_adapt/core/types.py +54 -0
  23. robust_adapt/core/validation.py +114 -0
  24. robust_adapt/integrations/__init__.py +50 -0
  25. robust_adapt/integrations/data.py +251 -0
  26. robust_adapt/integrations/sklearn.py +351 -0
  27. robust_adapt/integrations/torch_callback.py +157 -0
  28. robust_adapt/integrations/torch_module.py +125 -0
  29. robust_adapt/integrations/torch_optim.py +263 -0
  30. robust_adapt/minority/__init__.py +26 -0
  31. robust_adapt/minority/calibration.py +264 -0
  32. robust_adapt/minority/learner.py +210 -0
  33. robust_adapt/minority/prior.py +291 -0
  34. robust_adapt/minority/replay.py +303 -0
  35. robust_adapt/minority/threshold.py +454 -0
  36. robust_adapt/py.typed +1 -0
  37. robust_adapt/rao/__init__.py +42 -0
  38. robust_adapt/rao/accumulator.py +79 -0
  39. robust_adapt/rao/adamw.py +366 -0
  40. robust_adapt/rao/budget.py +159 -0
  41. robust_adapt/rao/device.py +72 -0
  42. robust_adapt/rao/memory.py +45 -0
  43. robust_adapt/rao/profiler.py +87 -0
  44. robust_adapt/rao/quantizer.py +163 -0
  45. robust_adapt/rao/recovery.py +178 -0
  46. robust_adapt/rla/__init__.py +48 -0
  47. robust_adapt/rla/label_correction.py +135 -0
  48. robust_adapt/rla/losses.py +293 -0
  49. robust_adapt/rla/noise_classifier.py +185 -0
  50. robust_adapt/rla/pipeline.py +272 -0
  51. robust_adapt/rla/reliability.py +342 -0
  52. robust_adapt/rla/scorer.py +159 -0
  53. robust_adapt/rla/weighting.py +64 -0
  54. robust_adapt/state/__init__.py +10 -0
  55. robust_adapt/state/checkpoint.py +109 -0
  56. robust_adapt/state/serializer.py +243 -0
  57. robust_adapt/ulc/__init__.py +66 -0
  58. robust_adapt/ulc/arbitrator.py +79 -0
  59. robust_adapt/ulc/automaton.py +167 -0
  60. robust_adapt/ulc/confidence.py +66 -0
  61. robust_adapt/ulc/conflict_resolver.py +280 -0
  62. robust_adapt/ulc/controller.py +196 -0
  63. robust_adapt/ulc/directives.py +42 -0
  64. robust_adapt/ulc/evidence.py +115 -0
  65. robust_adapt/ulc/explainer.py +62 -0
  66. robust_adapt/ulc/spatial.py +80 -0
  67. robust_adapt/ulc/v4_1_controller.py +536 -0
  68. robust_adapt/ulc/v4_automaton.py +307 -0
  69. robust_adapt/ulc/v4_controller.py +250 -0
  70. robust_adapt/ulc/v4_evidence.py +118 -0
  71. robust_adapt/ulc/v5_controller.py +403 -0
  72. robust_adapt-1.0.0.dist-info/METADATA +568 -0
  73. robust_adapt-1.0.0.dist-info/RECORD +77 -0
  74. robust_adapt-1.0.0.dist-info/WHEEL +5 -0
  75. robust_adapt-1.0.0.dist-info/licenses/LICENSE +176 -0
  76. robust_adapt-1.0.0.dist-info/licenses/NOTICE +13 -0
  77. robust_adapt-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,265 @@
1
+ """ROBUST-ADAPT: Robust and Adaptive Learning with Resource-Efficient Optimization.
2
+
3
+ Adaptive. Robust. Resource-Efficient.
4
+
5
+ An open-source Python machine-learning algorithm library designed to improve learning
6
+ under changing data distributions, unreliable training data and computational constraints.
7
+ It complements existing machine-learning frameworks rather than replacing them.
8
+
9
+ Four Independent Pillars:
10
+ - ALA: Adaptive Learning Algorithms (concept drift detection, streaming decision trees, balanced replay)
11
+ - RLA: Robust Learning Algorithms (sample reliability scoring, robust loss portfolio, label correction)
12
+ - RAO: Resource-Aware Optimization (8-bit block-quantized AdamW, multi-dimensional budget management)
13
+ - ULC: Unified Learning Controller (cross-pillar Pareto arbitration, ULC-v4.1/v5 hybrid controller)
14
+
15
+ Additional Capability:
16
+ - Minority: Adaptive Minority Intelligence (online prior tracking, cost-sensitive threshold adjustment)
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ # Package version
22
+ __version__ = "1.0.0"
23
+
24
+ # Core Exceptions & Configurations
25
+ from robust_adapt.core.exceptions import (
26
+ AdaptiveMLError,
27
+ ConfigurationError,
28
+ DriftStateError,
29
+ NumericalInstabilityError,
30
+ QuantizationError,
31
+ ResourceBudgetExceededError,
32
+ RobustAdaptError,
33
+ SerializationError,
34
+ ValidationError,
35
+ )
36
+
37
+ # 1. Adaptive Learning Algorithms (ALA)
38
+ from robust_adapt.ala.adwin import ReliabilityWeightedADWIN
39
+ from robust_adapt.ala.classifier import ALA30AdaptiveLearner, AdaptiveClassifier
40
+ from robust_adapt.ala.concept_memory import (
41
+ ConceptFingerprinter,
42
+ ConceptMemory,
43
+ ConceptSignature,
44
+ ConceptSnapshot,
45
+ RecurringConceptMemory,
46
+ )
47
+ from robust_adapt.ala.delayed_labels import DelayedLabelQueue
48
+ from robust_adapt.ala.detector import (
49
+ BaseDriftDetector,
50
+ CUSUMDetector,
51
+ MultiDetectorConsensus,
52
+ PageHinkleyDetector,
53
+ )
54
+ from robust_adapt.ala.drift_classifier import (
55
+ DriftAnalysisResult,
56
+ DriftClassifier,
57
+ DriftNature,
58
+ DriftType,
59
+ )
60
+ from robust_adapt.ala.replay import ClassBalancedReservoirReplay
61
+ from robust_adapt.ala.tree import VectorizedHoeffdingTree
62
+
63
+ # Canonical ergonomic aliases
64
+ ADWIN = ReliabilityWeightedADWIN
65
+ HoeffdingTree = VectorizedHoeffdingTree
66
+
67
+ # 2. Robust Learning Algorithms (RLA)
68
+ from robust_adapt.rla.label_correction import (
69
+ CorrectionAuditRecord,
70
+ EvidenceBasedLabelCorrector,
71
+ )
72
+ from robust_adapt.rla.losses import (
73
+ FocalLoss,
74
+ GeneralizedCrossEntropyLoss,
75
+ ReliabilityWeightedLoss,
76
+ SymmetricCrossEntropyLoss,
77
+ )
78
+ from robust_adapt.rla.noise_classifier import (
79
+ NoiseTaxonomyClassifier,
80
+ NoiseType,
81
+ PatternType,
82
+ )
83
+ from robust_adapt.rla.pipeline import RLA30RobustLearner, RobustLearningPipeline
84
+ from robust_adapt.rla.reliability import (
85
+ AsymmetricTransitionDetector,
86
+ ClassConditionalReliabilityEstimator,
87
+ MultiSignalReliabilityEngine,
88
+ )
89
+ from robust_adapt.rla.scorer import DensityAwareMarginScorer
90
+ from robust_adapt.rla.weighting import ClassBalancedWeights
91
+
92
+ # 3. Resource-Aware Optimization (RAO)
93
+ from robust_adapt.rao.accumulator import AdaptiveGradientAccumulator
94
+ from robust_adapt.rao.budget import BudgetState, DynamicResourceBudgetManager
95
+ from robust_adapt.rao.device import DeviceCapabilityDetector, DeviceProfile
96
+ from robust_adapt.rao.profiler import ExecutionProfiler
97
+ from robust_adapt.rao.quantizer import BlockQuantizer
98
+ from robust_adapt.rao.recovery import ResilientCheckpointManager
99
+
100
+ # 4. Adaptive Minority Intelligence
101
+ from robust_adapt.minority.calibration import ClassAwareCalibrator
102
+ from robust_adapt.minority.learner import MinorityIntelligence20Learner
103
+ from robust_adapt.minority.prior import (
104
+ BetaBinomialPriorTracker,
105
+ OnlineClassPriorTracker,
106
+ )
107
+ from robust_adapt.minority.replay import MinorityAwareReplayBuffer
108
+ from robust_adapt.minority.threshold import (
109
+ AdaptiveThresholdManager,
110
+ DynamicFPBudgetThresholdManager,
111
+ compute_sublinear_cost_weights,
112
+ )
113
+
114
+ # 5. Unified Learning Controller (ULC)
115
+ from robust_adapt.ulc.arbitrator import MultiObjectiveArbitrator
116
+ from robust_adapt.ulc.confidence import ConfidenceEngine, ConfidenceScores
117
+ from robust_adapt.ulc.conflict_resolver import CrossPillarConflictResolver
118
+ from robust_adapt.ulc.controller import AdaptiveLearningController
119
+ from robust_adapt.ulc.directives import ControllerDirective, ControllerState
120
+ from robust_adapt.ulc.spatial import compute_spatial_discordance
121
+ from robust_adapt.ulc.v4_automaton import (
122
+ ArbitrationAutomatonV4,
123
+ AutomatonStateV4,
124
+ DirectiveV4,
125
+ )
126
+ from robust_adapt.ulc.v4_controller import UnifiedLearningControllerV4
127
+ from robust_adapt.ulc.v4_evidence import EvidenceSynthesizerV4, EvidenceVectorV4
128
+ from robust_adapt.ulc.v4_1_controller import ULCv41Config, UnifiedLearningControllerV4_1
129
+ from robust_adapt.ulc.v5_controller import (
130
+ InterventionTier,
131
+ ULCv5Config,
132
+ ULCv5Directive,
133
+ UnifiedLearningControllerV5,
134
+ UtilityScorer,
135
+ )
136
+
137
+ # 6. State Management & Checkpointing
138
+ from robust_adapt.state.checkpoint import CheckpointManager
139
+ from robust_adapt.state.serializer import is_safetensors_available, load_state, save_state
140
+
141
+ __all__ = [
142
+ # Metadata
143
+ "__version__",
144
+ # Core Exceptions
145
+ "RobustAdaptError",
146
+ "AdaptiveMLError",
147
+ "ConfigurationError",
148
+ "ValidationError",
149
+ "DriftStateError",
150
+ "QuantizationError",
151
+ "ResourceBudgetExceededError",
152
+ "NumericalInstabilityError",
153
+ "SerializationError",
154
+ # ALA
155
+ "AdaptiveClassifier",
156
+ "ALA30AdaptiveLearner",
157
+ "HoeffdingTree",
158
+ "VectorizedHoeffdingTree",
159
+ "ADWIN",
160
+ "ReliabilityWeightedADWIN",
161
+ "BaseDriftDetector",
162
+ "CUSUMDetector",
163
+ "PageHinkleyDetector",
164
+ "MultiDetectorConsensus",
165
+ "DriftClassifier",
166
+ "DriftType",
167
+ "DriftNature",
168
+ "DriftAnalysisResult",
169
+ "ConceptMemory",
170
+ "ConceptSignature",
171
+ "ConceptFingerprinter",
172
+ "RecurringConceptMemory",
173
+ "ConceptSnapshot",
174
+ "DelayedLabelQueue",
175
+ "ClassBalancedReservoirReplay",
176
+ # RLA
177
+ "RobustLearningPipeline",
178
+ "RLA30RobustLearner",
179
+ "MultiSignalReliabilityEngine",
180
+ "DensityAwareMarginScorer",
181
+ "AsymmetricTransitionDetector",
182
+ "ClassConditionalReliabilityEstimator",
183
+ "SymmetricCrossEntropyLoss",
184
+ "GeneralizedCrossEntropyLoss",
185
+ "FocalLoss",
186
+ "ReliabilityWeightedLoss",
187
+ "ClassBalancedWeights",
188
+ "NoiseTaxonomyClassifier",
189
+ "NoiseType",
190
+ "PatternType",
191
+ "EvidenceBasedLabelCorrector",
192
+ "CorrectionAuditRecord",
193
+ # RAO
194
+ "LowMemoryAdamW",
195
+ "VectorizedLowMemoryAdamW",
196
+ "BlockQuantizer",
197
+ "AdaptiveGradientAccumulator",
198
+ "DynamicResourceBudgetManager",
199
+ "BudgetState",
200
+ "ExecutionProfiler",
201
+ "DeviceCapabilityDetector",
202
+ "DeviceProfile",
203
+ "ResilientCheckpointManager",
204
+ # Minority Intelligence
205
+ "OnlineClassPriorTracker",
206
+ "BetaBinomialPriorTracker",
207
+ "AdaptiveThresholdManager",
208
+ "DynamicFPBudgetThresholdManager",
209
+ "compute_sublinear_cost_weights",
210
+ "MinorityIntelligence20Learner",
211
+ "MinorityAwareReplayBuffer",
212
+ "ClassAwareCalibrator",
213
+ # ULC
214
+ "AdaptiveLearningController",
215
+ "ControllerState",
216
+ "ControllerDirective",
217
+ "UnifiedLearningControllerV4",
218
+ "AutomatonStateV4",
219
+ "DirectiveV4",
220
+ "EvidenceVectorV4",
221
+ "EvidenceSynthesizerV4",
222
+ "ConfidenceEngine",
223
+ "ConfidenceScores",
224
+ "MultiObjectiveArbitrator",
225
+ "ArbitrationAutomatonV4",
226
+ "CrossPillarConflictResolver",
227
+ "UnifiedLearningControllerV4_1",
228
+ "ULCv41Config",
229
+ "compute_spatial_discordance",
230
+ "UnifiedLearningControllerV5",
231
+ "ULCv5Config",
232
+ "InterventionTier",
233
+ "ULCv5Directive",
234
+ "UtilityScorer",
235
+ # State Management
236
+ "save_state",
237
+ "load_state",
238
+ "CheckpointManager",
239
+ "is_safetensors_available",
240
+ # Integrations
241
+ "ScikitLearnAdaptiveClassifier",
242
+ "SklearnAdaptiveClassifier",
243
+ ]
244
+
245
+
246
+ def __getattr__(name: str):
247
+ if name == "LowMemoryAdamW":
248
+ from robust_adapt.rao.adamw import LowMemoryAdamW
249
+
250
+ return LowMemoryAdamW
251
+ if name == "VectorizedLowMemoryAdamW":
252
+ try:
253
+ from robust_adapt.integrations.torch_optim import VectorizedLowMemoryAdamW
254
+
255
+ return VectorizedLowMemoryAdamW
256
+ except Exception as e:
257
+ raise AttributeError(f"VectorizedLowMemoryAdamW requires PyTorch optional integration: {e}") from e
258
+ if name in ("ScikitLearnAdaptiveClassifier", "SklearnAdaptiveClassifier"):
259
+ try:
260
+ from robust_adapt.integrations.sklearn import SklearnAdaptiveClassifier
261
+
262
+ return SklearnAdaptiveClassifier
263
+ except Exception as e:
264
+ raise AttributeError(f"{name} requires scikit-learn: {e}") from e
265
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,54 @@
1
+ """Adaptive Learning Algorithms (ALA) module.
2
+
3
+ Provides streaming classifiers, vector Hoeffding trees, multi-detector drift ensembles,
4
+ drift taxonomy classification, concept memory, and delayed-label streaming.
5
+ """
6
+
7
+ from robust_adapt.ala.adwin import ReliabilityWeightedADWIN
8
+ from robust_adapt.ala.classifier import ALA30AdaptiveLearner, AdaptiveClassifier
9
+ from robust_adapt.ala.concept_memory import (
10
+ ConceptFingerprinter,
11
+ ConceptMemory,
12
+ ConceptSignature,
13
+ ConceptSnapshot,
14
+ RecurringConceptMemory,
15
+ )
16
+ from robust_adapt.ala.delayed_labels import DelayedLabelQueue
17
+ from robust_adapt.ala.detector import (
18
+ BaseDriftDetector,
19
+ CUSUMDetector,
20
+ MultiDetectorConsensus,
21
+ PageHinkleyDetector,
22
+ )
23
+ from robust_adapt.ala.drift_classifier import (
24
+ DriftAnalysisResult,
25
+ DriftClassifier,
26
+ DriftNature,
27
+ DriftType,
28
+ )
29
+ from robust_adapt.ala.ensemble import StreamingEnsemble
30
+ from robust_adapt.ala.replay import ClassBalancedReservoirReplay
31
+ from robust_adapt.ala.tree import VectorizedHoeffdingTree
32
+
33
+ __all__ = [
34
+ "VectorizedHoeffdingTree",
35
+ "ReliabilityWeightedADWIN",
36
+ "ClassBalancedReservoirReplay",
37
+ "StreamingEnsemble",
38
+ "AdaptiveClassifier",
39
+ "BaseDriftDetector",
40
+ "CUSUMDetector",
41
+ "PageHinkleyDetector",
42
+ "MultiDetectorConsensus",
43
+ "DriftClassifier",
44
+ "DriftType",
45
+ "DriftNature",
46
+ "DriftAnalysisResult",
47
+ "ConceptMemory",
48
+ "ConceptSignature",
49
+ "ConceptFingerprinter",
50
+ "RecurringConceptMemory",
51
+ "ConceptSnapshot",
52
+ "ALA30AdaptiveLearner",
53
+ "DelayedLabelQueue",
54
+ ]
@@ -0,0 +1,243 @@
1
+ """Reliability-Weighted ADWIN (RW-ADWIN) concept drift detector.
2
+
3
+ Mathematical formulation specified in DOC-DES-P6-003 and DOC-DES-P6-009 (Algorithm 3).
4
+ """
5
+
6
+ import math
7
+ from typing import Any, Dict, List, Optional, Tuple
8
+ from robust_adapt.core.constants import EPS_DIV, EPS_RELIABILITY
9
+ from robust_adapt.core.exceptions import ValidationError
10
+
11
+
12
+ class _Bucket:
13
+ """Compressed exponential histogram bucket."""
14
+
15
+ __slots__ = ("w", "val", "w_sq", "count")
16
+
17
+ def __init__(self, w: float, val: float, w_sq: float, count: int) -> None:
18
+ self.w = float(w) # Sum of weights (reliabilities)
19
+ self.val = float(val) # Sum of weighted values (w * loss)
20
+ self.w_sq = float(w_sq) # Sum of squared weights
21
+ self.count = int(count) # Number of original observations
22
+
23
+
24
+ class ReliabilityWeightedADWIN:
25
+ """Reliability-Weighted Adaptive Sliding Window (RW-ADWIN) drift detector.
26
+
27
+ Maintains a variable-length window partitioned into sub-windows W = W0 . W1,
28
+ weighting observations by sample reliability to suppress false-positive drift alarms
29
+ caused by label corruption while preserving rapid detection of true concept shifts.
30
+
31
+ Parameters
32
+ ----------
33
+ delta : float, default=0.002
34
+ Confidence value for the Hoeffding cut bound (1 - delta detection confidence).
35
+ min_subwindow_weight : float, default=5.0
36
+ Minimum cumulative sample reliability required in sub-windows to test split.
37
+ eps_r : float, default=1e-4
38
+ Lower-bound epsilon guard on reliability weights.
39
+ max_buckets_per_level : int, default=5
40
+ Maximum number of buckets of equal size before compression.
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ delta: float = 0.002,
46
+ min_subwindow_weight: float = 5.0,
47
+ eps_r: float = EPS_RELIABILITY,
48
+ max_buckets_per_level: int = 5,
49
+ ) -> None:
50
+ if delta <= 0.0 or delta >= 1.0:
51
+ raise ValidationError(f"delta must be in (0, 1), got {delta}")
52
+ if min_subwindow_weight <= 0.0:
53
+ raise ValidationError(f"min_subwindow_weight must be positive, got {min_subwindow_weight}")
54
+
55
+ self.delta = float(delta)
56
+ self.min_subwindow_weight = float(min_subwindow_weight)
57
+ self.eps_r = float(eps_r)
58
+ self.max_buckets_per_level = int(max_buckets_per_level)
59
+
60
+ self._buckets: List[_Bucket] = []
61
+ self.window_total_count: int = 0
62
+ self.window_weight_sum: float = 0.0
63
+ self.window_weighted_val_sum: float = 0.0
64
+ self.window_weight_sq_sum: float = 0.0
65
+ self.last_delta_drift: float = 0.0
66
+
67
+ @property
68
+ def total_samples(self) -> int:
69
+ """Total observations currently in the active window."""
70
+ return self.window_total_count
71
+
72
+ @property
73
+ def weighted_mean(self) -> float:
74
+ """Current reliability-weighted mean of observations in the active window."""
75
+ if self.window_weight_sum <= 0.0:
76
+ return 0.0
77
+ return self.window_weighted_val_sum / self.window_weight_sum
78
+
79
+ def update(self, loss: float, reliability: float = 1.0) -> bool:
80
+ """Ingest a new observation and return True if concept drift is detected.
81
+
82
+ Parameters
83
+ ----------
84
+ loss : float
85
+ Instantaneous error or loss value, typically in [0.0, 1.0].
86
+ reliability : float, default=1.0
87
+ Sample reliability score in [eps_r, 1.0].
88
+
89
+ Returns
90
+ -------
91
+ drift_detected : bool
92
+ True if statistical drift bound was violated and window cut was performed.
93
+ """
94
+ loss = float(loss)
95
+ reliability = float(reliability)
96
+
97
+ # Numerical bounds validation
98
+ if math.isnan(loss) or math.isinf(loss):
99
+ raise ValidationError(f"Loss must be finite, got {loss}")
100
+ if reliability < self.eps_r:
101
+ reliability = self.eps_r
102
+ elif reliability > 1.0:
103
+ reliability = 1.0
104
+
105
+ # Step 1: Insert new observation at the head (newest end)
106
+ w = reliability
107
+ v = loss
108
+ self.window_total_count += 1
109
+ self.window_weight_sum += w
110
+ self.window_weighted_val_sum += w * v
111
+ self.window_weight_sq_sum += w * w
112
+
113
+ self._buckets.append(_Bucket(w=w, val=w * v, w_sq=w * w, count=1))
114
+ self._compress_buckets()
115
+
116
+ # Step 2: Check sub-window partitions W = W0 . W1 from tail (oldest end)
117
+ drift_detected = False
118
+ self.last_delta_drift = 0.0
119
+
120
+ if self.window_weight_sum < 2.0 * self.min_subwindow_weight:
121
+ return False
122
+
123
+ W0_w = 0.0
124
+ W0_val = 0.0
125
+ W0_w_sq = 0.0
126
+ W_total_w = self.window_weight_sum
127
+ W_total_val = self.window_weighted_val_sum
128
+
129
+ cut_idx = -1
130
+ n_buckets = len(self._buckets)
131
+
132
+ for i in range(n_buckets - 1):
133
+ b = self._buckets[i]
134
+ W0_w += b.w
135
+ W0_val += b.val
136
+ W0_w_sq += b.w_sq
137
+
138
+ W1_w = W_total_w - W0_w
139
+ W1_val = W_total_val - W0_val
140
+ W1_w_sq = self.window_weight_sq_sum - W0_w_sq
141
+
142
+ if W0_w < self.min_subwindow_weight or W1_w < self.min_subwindow_weight:
143
+ continue
144
+
145
+ # Effective sample sizes via Kish's formula
146
+ N_eff_0 = (W0_w * W0_w) / (W0_w_sq + EPS_DIV)
147
+ N_eff_1 = (W1_w * W1_w) / (W1_w_sq + EPS_DIV)
148
+ m_eff = (N_eff_0 * N_eff_1) / (N_eff_0 + N_eff_1 + EPS_DIV)
149
+
150
+ if m_eff <= 0.0:
151
+ continue
152
+
153
+ mu_0 = W0_val / W0_w
154
+ mu_1 = W1_val / W1_w
155
+ abs_diff = abs(mu_0 - mu_1)
156
+
157
+ # Hoeffding cut bound with effective sample size
158
+ eps_cut = math.sqrt((1.0 / (2.0 * m_eff)) * math.log(4.0 * self.window_total_count / self.delta))
159
+
160
+ delta_drift = abs_diff / (eps_cut + EPS_DIV)
161
+ if delta_drift > self.last_delta_drift:
162
+ self.last_delta_drift = delta_drift
163
+
164
+ if abs_diff >= eps_cut:
165
+ drift_detected = True
166
+ cut_idx = i
167
+ break
168
+
169
+ # Step 3: Execute window cut if drift detected
170
+ if drift_detected and cut_idx >= 0:
171
+ # Delete buckets 0..cut_idx (oldest sub-window W0)
172
+ dropped = self._buckets[: cut_idx + 1]
173
+ self._buckets = self._buckets[cut_idx + 1 :]
174
+
175
+ for b in dropped:
176
+ self.window_total_count -= b.count
177
+ self.window_weight_sum -= b.w
178
+ self.window_weighted_val_sum -= b.val
179
+ self.window_weight_sq_sum -= b.w_sq
180
+
181
+ # Clean precision drift
182
+ if self.window_total_count <= 0 or len(self._buckets) == 0:
183
+ self.reset()
184
+
185
+ return drift_detected
186
+
187
+ def _compress_buckets(self) -> None:
188
+ """Merge buckets of identical count if level exceeds max_buckets_per_level."""
189
+ i = 0
190
+ while i < len(self._buckets) - 2:
191
+ b1 = self._buckets[i]
192
+ b2 = self._buckets[i + 1]
193
+ b3 = self._buckets[i + 2]
194
+
195
+ # If three consecutive buckets share the same count, merge the oldest two
196
+ if b1.count == b2.count == b3.count:
197
+ merged = _Bucket(
198
+ w=b1.w + b2.w,
199
+ val=b1.val + b2.val,
200
+ w_sq=b1.w_sq + b2.w_sq,
201
+ count=b1.count + b2.count,
202
+ )
203
+ self._buckets[i] = merged
204
+ del self._buckets[i + 1]
205
+ else:
206
+ i += 1
207
+
208
+ def reset(self) -> None:
209
+ """Reset the sliding window to empty state."""
210
+ self._buckets.clear()
211
+ self.window_total_count = 0
212
+ self.window_weight_sum = 0.0
213
+ self.window_weighted_val_sum = 0.0
214
+ self.window_weight_sq_sum = 0.0
215
+ self.last_delta_drift = 0.0
216
+
217
+ def state_dict(self) -> Dict[str, Any]:
218
+ """Serialize window state to dictionary."""
219
+ return {
220
+ "delta": self.delta,
221
+ "min_subwindow_weight": self.min_subwindow_weight,
222
+ "eps_r": self.eps_r,
223
+ "window_total_count": self.window_total_count,
224
+ "window_weight_sum": self.window_weight_sum,
225
+ "window_weighted_val_sum": self.window_weighted_val_sum,
226
+ "window_weight_sq_sum": self.window_weight_sq_sum,
227
+ "last_delta_drift": self.last_delta_drift,
228
+ "buckets": [{"w": b.w, "val": b.val, "w_sq": b.w_sq, "count": b.count} for b in self._buckets],
229
+ }
230
+
231
+ def load_state_dict(self, state: Dict[str, Any]) -> None:
232
+ """Restore window state from dictionary."""
233
+ self.delta = float(state["delta"])
234
+ self.min_subwindow_weight = float(state["min_subwindow_weight"])
235
+ self.eps_r = float(state["eps_r"])
236
+ self.window_total_count = int(state["window_total_count"])
237
+ self.window_weight_sum = float(state["window_weight_sum"])
238
+ self.window_weighted_val_sum = float(state["window_weighted_val_sum"])
239
+ self.window_weight_sq_sum = float(state["window_weight_sq_sum"])
240
+ self.last_delta_drift = float(state.get("last_delta_drift", 0.0))
241
+ self._buckets = [
242
+ _Bucket(w=b["w"], val=b["val"], w_sq=b["w_sq"], count=b["count"]) for b in state.get("buckets", [])
243
+ ]