BhalalaDaksh commited on
Commit
34adaa0
·
verified ·
1 Parent(s): d78f825

docs: update license header in compactts/agent/post_processor.py to BSL-1.1

Browse files
Files changed (1) hide show
  1. compactts/agent/post_processor.py +64 -0
compactts/agent/post_processor.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Calibrated Post-Processor for CompactTS-Agent-v2.
2
+
3
+ Enforces physical validity, quantile monotonicity, and probabilistic
4
+ interval sharpness for state-of-the-art CRPS calibration.
5
+
6
+ Attribution: Daksh Bhalala
7
+ License: Business Source License 1.1 (BSL-1.1)
8
+ """
9
+
10
+ from typing import Optional
11
+ import numpy as np
12
+
13
+
14
+ class CalibratedPostProcessor:
15
+ """Post-processing pipeline for probabilistic forecasts.
16
+
17
+ Ensures:
18
+ 1. Strict quantile non-crossing: q_0.1 <= q_0.2 <= ... <= q_0.9
19
+ 2. Physical support constraints: non-negativity for energy, volume, counts, solar
20
+ 3. Probabilistic quantile calibration via dispersion blending
21
+ """
22
+
23
+ def __init__(self, timestral_quantile_weight: float = 0.0):
24
+ self.w_tms = timestral_quantile_weight
25
+
26
+ def process_quantiles(
27
+ self,
28
+ quantiles: np.ndarray,
29
+ aux_quantiles: Optional[np.ndarray] = None,
30
+ non_negative: bool = False,
31
+ ) -> np.ndarray:
32
+ """Process and calibrate a 9-quantile forecast array.
33
+
34
+ Parameters
35
+ ----------
36
+ quantiles : np.ndarray
37
+ Array of shape (9, H) representing quantile levels [0.1..0.9].
38
+ aux_quantiles : Optional[np.ndarray]
39
+ Secondary quantile array (e.g., Timestral-209M) for uncertainty calibration.
40
+ non_negative : bool
41
+ Whether to clip negative values to 0.0.
42
+
43
+ Returns
44
+ -------
45
+ np.ndarray
46
+ Calibrated and monotonic quantile array of shape (9, H).
47
+ """
48
+ out = quantiles.copy()
49
+
50
+ # If auxiliary quantiles are available, blend for sharp, calibrated uncertainty
51
+ if aux_quantiles is not None and self.w_tms > 0:
52
+ median = out[4] # Preserve point prediction median
53
+ out = (1.0 - self.w_tms) * out + self.w_tms * aux_quantiles
54
+ # Shift back so median is anchored
55
+ out[4] = median
56
+
57
+ # Physical non-negativity
58
+ if non_negative:
59
+ out = np.maximum(out, 0.0)
60
+
61
+ # Enforce strict quantile monotonicity
62
+ out = np.sort(out, axis=0)
63
+
64
+ return out