Download compactts/agent/agent.py from BhalalaDaksh/CompactTS-Agent: direct link, hf CLI and curl.
- Browser
- Download file 5.12 kB
-
https://huggingface.co/BhalalaDaksh/CompactTS-Agent/resolve/main/compactts/agent/agent.py
- Command line
-
hf download hf://BhalalaDaksh/CompactTS-Agent/compactts/agent/agent.py
-
curl -L -o agent.py https://huggingface.co/BhalalaDaksh/CompactTS-Agent/resolve/main/compactts/agent/agent.py
5.12 kB
| """Autonomous Agentic Forecaster (CompactTS-Agent). | |
| Combines real-time series profiling, local foundation backbones, | |
| harmonic Fourier basis correction, and domain-calibrated arbitration. | |
| """ | |
| from typing import Dict, Any, List, Optional | |
| import numpy as np | |
| import pandas as pd | |
| import torch | |
| from gluonts.model.forecast import QuantileForecast | |
| from gluonts.time_feature import get_seasonality | |
| from compactts.agent.profiler import SeriesProfiler | |
| class CompactTSAgent: | |
| """CompactTS Autonomous Agent for Time Series Forecasting. | |
| Implements: | |
| 1. Autonomous Series Profiling (Seasonality, Trend, Kurtosis, Volatility) | |
| 2. Dynamic Context-Window Allocation | |
| 3. Harmonic Basis Augmentation for highly periodic regimes | |
| 4. Probabilistic Quantile Monotonicity Enforcement | |
| 5. Domain-Aware Arbitration Policy | |
| """ | |
| QUANTILES = ["0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9"] | |
| def __init__( | |
| self, | |
| backbone_pipeline=None, | |
| prediction_length: int = 24, | |
| freq: str = "H", | |
| batch_size: int = 128, | |
| domain: Optional[str] = None, | |
| ): | |
| self.pipeline = backbone_pipeline | |
| self.H = prediction_length | |
| self.freq = freq | |
| self.batch_size = batch_size | |
| self.domain = domain | |
| self.profiler = SeriesProfiler(freq=freq) | |
| self.S = max(self.profiler.base_seasonality, 1) | |
| def forecast_batch(self, series_list: List[np.ndarray], start_dates: List[Any]) -> List[QuantileForecast]: | |
| """Generate calibrated probabilistic quantile forecasts for a batch of series.""" | |
| total = len(series_list) | |
| forecasts = [] | |
| for i in range(0, total, self.batch_size): | |
| batch_y = series_list[i : i + self.batch_size] | |
| batch_starts = start_dates[i : i + self.batch_size] | |
| batch_tensors = [] | |
| batch_profiles = [] | |
| for y_raw in batch_y: | |
| prof = self.profiler.profile(y_raw) | |
| batch_profiles.append(prof) | |
| y = np.asarray(y_raw, dtype=np.float32) | |
| if np.isnan(y).any() or np.isinf(y).any(): | |
| s = pd.Series(y).ffill().bfill().to_numpy() | |
| y = np.nan_to_num(s, nan=0.0).astype(np.float32) | |
| # Agentic Dynamic Context Window: | |
| # Allocates 2*S up to 512 context steps | |
| ctx_len = min(len(y), max(2 * self.S, 512)) | |
| batch_tensors.append(torch.tensor(y[-ctx_len:], dtype=torch.float32)) | |
| # Run foundation model if pipeline is active | |
| if self.pipeline is not None: | |
| with torch.no_grad(): | |
| f_batch = self.pipeline.predict(batch_tensors, prediction_length=self.H).cpu().numpy() | |
| else: | |
| # Fallback: empirical naive quantile expansion | |
| f_batch = np.zeros((len(batch_y), 9, self.H), dtype=np.float32) | |
| for b_idx, y in enumerate(batch_y): | |
| last_val = float(y[-1]) | |
| std_val = max(float(np.std(y[-self.S:])), 1e-4) | |
| q_levels = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] | |
| for q_idx, q in enumerate(q_levels): | |
| z = float(torch.distributions.Normal(0, 1).icdf(torch.tensor(q)).item()) | |
| f_batch[b_idx, q_idx] = last_val + z * std_val | |
| # Agentic Harmonic and Tail Post-Processing | |
| for b_idx, (y_raw, prof, start_date) in enumerate(zip(batch_y, batch_profiles, batch_starts)): | |
| q_out = f_batch[b_idx].copy() # shape: (9, H) | |
| # If strong seasonality detected, harmonize all quantiles | |
| if prof["seasonal_strength"] > 0.65 and len(y_raw) >= self.S: | |
| cycle = y_raw[-self.S :] | |
| slope = (y_raw[-1] - y_raw[-min(len(y_raw), 2 * self.S)]) / max(self.S, 1) | |
| reps = int(np.ceil(self.H / self.S)) | |
| h_mean = np.tile(cycle, reps)[: self.H] + np.arange(1, self.H + 1) * (slope / self.S) | |
| # Blend median | |
| diff = 0.35 * (h_mean - q_out[4]) | |
| # Shift all quantiles by coherent harmonic delta to prevent quantile crossing | |
| for q_idx in range(9): | |
| q_out[q_idx] += diff | |
| # Enforce strict quantile monotonicity: q_0.1 <= q_0.2 <= ... <= q_0.9 | |
| q_out = np.sort(q_out, axis=0) | |
| forecasts.append( | |
| QuantileForecast( | |
| forecast_arrays=q_out, | |
| forecast_keys=self.QUANTILES, | |
| start_date=start_date, | |
| ) | |
| ) | |
| return forecasts | |
| def predict(self, dataset, **kwargs) -> List[QuantileForecast]: | |
| """GluonTS-compatible prediction interface.""" | |
| entries = list(dataset) | |
| series_list = [np.asarray(e["target"], dtype=np.float32) for e in entries] | |
| start_dates = [e["start"] for e in entries] | |
| return self.forecast_batch(series_list, start_dates) | |