File size: 28,428 Bytes
1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 1c8d125 96e1a32 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 |
from dataclasses import dataclass, field
from enum import Enum
import numpy as np
from src.data.frequency import Frequency
@dataclass
class GeneratorParams:
"""Base class for generator parameters."""
global_seed: int = 42
length: int = 2048
frequency: list[Frequency] | None = None
start: list[np.datetime64] | None = None
def update(self, **kwargs):
"""Update parameters from keyword arguments."""
for k, v in kwargs.items():
if hasattr(self, k):
setattr(self, k, v)
@dataclass
class ForecastPFNGeneratorParams(GeneratorParams):
"""Parameters for the ForecastPFNGenerator."""
trend_exp: bool = True
scale_noise: tuple[float, float] = (0.6, 0.3)
harmonic_scale_ratio: float = 0.5
harmonic_rate: float = 1.0
period_factor: float = 1.0
seasonal_only: bool = False
trend_additional: bool = True
transition_ratio: float = 1.0 # Probability of applying transition between two series
random_walk: bool = False
# Multivariate augmentation parameters (applied in wrapper)
mixup_prob: float = 0.1 # Probability of applying mixup augmentation
mixup_series: int = 4 # Maximum number of series to mix in mixup
damp_and_spike: bool = False # Whether to apply damping and spike augmentations
damping_noise_ratio: float = 0.05 # Ratio of batch to apply damping
spike_noise_ratio: float = 0.05 # Ratio of batch to apply spike noise
spike_signal_ratio: float = 0.05 # Probability of applying spike signal replacement
spike_batch_ratio: float = 0.05 # Ratio of batch for spike signal replacement
# Univariate augmentation parameters (applied in generator)
time_warp_prob: float = 0.1 # Probability of applying time warping
time_warp_strength: float = 0.05 # Strength of time warping effect
magnitude_scale_prob: float = 0.2 # Probability of applying magnitude scaling
magnitude_scale_range: tuple[float, float] = (
0.9,
1.1,
) # Range for magnitude scaling
damping_prob: float = 0.1 # Probability of applying damping augmentation
spike_prob: float = 0.15 # Probability of applying spike augmentation
pure_spike_prob: float = 0.02 # Probability of replacing with pure spike signal
# Built-in filtering parameters
max_absolute_spread: float = 300.0 # Maximum allowed spread (max - min) for generated series
max_absolute_value: float = 300.0
max_retries: int = 10
@dataclass
class GPGeneratorParams(GeneratorParams):
"""
Parameters for the Gaussian Process (GP) Prior synthetic data generator.
"""
max_kernels: int = 6
likelihood_noise_level: float = 0.1
noise_level: str = "low" # Options: ["random", "high", "moderate", "low"]
use_original_gp: bool = False
gaussians_periodic: bool = True
peak_spike_ratio: float = 0.1
subfreq_ratio: float = 0.2
periods_per_freq: float = 0.5
gaussian_sampling_ratio: float = 0.2
max_period_ratio: float = 0.5
kernel_periods: tuple[int, ...] = (4, 5, 7, 21, 24, 30, 60, 120)
kernel_bank: dict[str, float] = field(
default_factory=lambda: {
"matern_kernel": 1.5,
"linear_kernel": 1.0,
"periodic_kernel": 5.0,
"polynomial_kernel": 0.0,
"spectral_mixture_kernel": 0.0,
}
)
@dataclass
class KernelGeneratorParams(GeneratorParams):
"""Parameters for the KernelSynthGenerator."""
max_kernels: int = 5
@dataclass
class SineWaveGeneratorParams(GeneratorParams):
"""Parameters for the SineWaveGenerator - focused on diverse sinusoidal patterns."""
# Core sinusoidal parameters
num_components_range: tuple[int, int] = (1, 3)
period_range: tuple[float, float] | tuple[tuple[float, float], tuple[float, float]] = (10.0, 200.0)
amplitude_range: tuple[float, float] | tuple[tuple[float, float], tuple[float, float]] = (0.5, 3.0)
phase_range: tuple[float, float] | tuple[tuple[float, float], tuple[float, float]] = (0.0, 2.0 * np.pi)
# Trend parameters
trend_slope_range: tuple[float, float] = (-0.01, 0.01)
base_level_range: tuple[float, float] = (0.0, 2.0)
# Noise parameters
noise_probability: float = 0.7 # Probability of adding noise (70% of series have noise)
noise_level_range: tuple[float, float] = (
0.05,
0.2,
) # Small noise as fraction of amplitude (when noise is applied)
# Time-varying parameters (subtle modulation)
enable_amplitude_modulation: bool = True
amplitude_modulation_strength: float = 0.1 # Max 10% amplitude variation
enable_frequency_modulation: bool = True
frequency_modulation_strength: float = 0.05 # Max 5% frequency variation
@dataclass
class SawToothGeneratorParams(GeneratorParams):
"""Parameters for the SawToothGenerator."""
periods: tuple[int, int] = (2, 7) # Number of sawtooth periods in the series
amplitude_range: tuple[float, float] | tuple[tuple[float, float], tuple[float, float]] = (0.5, 3.0)
phase_range: tuple[float, float] | tuple[tuple[float, float], tuple[float, float]] = (
0.0,
1.0,
) # Phase shift as fraction of period
trend_slope_range: tuple[float, float] | tuple[tuple[float, float], tuple[float, float]] = (
-0.001,
0.001,
) # Slightly stronger linear trend slope for more straight lines
seasonality_amplitude_range: tuple[float, float] | tuple[tuple[float, float], tuple[float, float]] = (
0.0,
0.02,
) # Minimal seasonal component amplitude
add_trend: bool = True # Whether to add linear trend
add_seasonality: bool = True # Whether to add seasonal component
class StepPatternType(Enum):
"""Types of step patterns that can be generated."""
STABLE = "stable" # Flat line with minimal variation
GRADUAL_INCREASE = "gradual_increase" # Gradual upward steps
GRADUAL_DECREASE = "gradual_decrease" # Gradual downward steps
SPIKE_UP = "spike_up" # Sharp increase then gradual decrease
SPIKE_DOWN = "spike_down" # Sharp decrease then gradual increase
OSCILLATING = "oscillating" # Up and down pattern
RANDOM_WALK = "random_walk" # Random steps (current behavior)
@dataclass
class SubseriesConfig:
"""Configuration for a single subseries pattern."""
pattern_type: StepPatternType
length_range: tuple[int, int] # Min and max length for this subseries
num_changepoints_range: tuple[int, int] # Number of changepoints in this subseries
step_size_range: tuple[float, float] # Step size range for this pattern
level_drift_range: tuple[float, float] = (0.0, 0.0) # Overall level drift
step_size_decay: float = 1.0 # Decay factor for step sizes over time
weight: float = 1.0 # Probability weight for selecting this pattern
@dataclass
class StepGeneratorParams(GeneratorParams):
"""Parameters for the StepGenerator with subseries support."""
# Subseries configuration
subseries_configs: list[SubseriesConfig] = field(
default_factory=lambda: [
# Stable beginning (20-30% of series)
SubseriesConfig(
pattern_type=StepPatternType.STABLE,
length_range=(200, 600),
num_changepoints_range=(0, 3),
step_size_range=(-1.0, 1.0),
weight=0.8,
),
# Gradual increase pattern (15-25% of series)
SubseriesConfig(
pattern_type=StepPatternType.GRADUAL_INCREASE,
length_range=(300, 700),
num_changepoints_range=(5, 15),
step_size_range=(1.0, 5.0),
level_drift_range=(0.0, 0.1),
weight=0.6,
),
# Gradual decrease pattern (15-25% of series)
SubseriesConfig(
pattern_type=StepPatternType.GRADUAL_DECREASE,
length_range=(300, 700),
num_changepoints_range=(5, 15),
step_size_range=(-5.0, -1.0),
level_drift_range=(-0.1, 0.0),
weight=0.6,
),
# Spike up pattern (10-20% of series)
SubseriesConfig(
pattern_type=StepPatternType.SPIKE_UP,
length_range=(200, 500),
num_changepoints_range=(3, 8),
step_size_range=(3.0, 10.0),
step_size_decay=0.7,
weight=0.4,
),
# Spike down pattern (10-20% of series)
SubseriesConfig(
pattern_type=StepPatternType.SPIKE_DOWN,
length_range=(200, 500),
num_changepoints_range=(3, 8),
step_size_range=(-10.0, -3.0),
step_size_decay=0.7,
weight=0.4,
),
# Oscillating pattern (10-15% of series)
SubseriesConfig(
pattern_type=StepPatternType.OSCILLATING,
length_range=(400, 800),
num_changepoints_range=(8, 20),
step_size_range=(-4.0, 4.0),
weight=0.3,
),
# Random walk pattern (fallback)
SubseriesConfig(
pattern_type=StepPatternType.RANDOM_WALK,
length_range=(100, 400),
num_changepoints_range=(5, 20),
step_size_range=(-3.0, 3.0),
weight=0.2,
),
]
)
# Minimum number of subseries to combine
min_subseries: int = 10
max_subseries: int = 100
# Transition smoothing between subseries
enable_smooth_transitions: bool = False
transition_length: int = 5
# Base level and global parameters
base_level_range: tuple[float, float] = (5.0, 15.0)
noise_level_range: tuple[float, float] = (0.001, 0.01)
# Seasonal component parameters
add_seasonality: bool = True
daily_seasonality_amplitude_range: tuple[float, float] = (0.0, 0.8)
weekly_seasonality_amplitude_range: tuple[float, float] = (0.0, 0.7)
# Trend parameters
add_trend: bool = False
trend_slope_range: tuple[float, float] = (-0.005, 0.005)
# Scaling parameters
scale_range: tuple[float, float] = (0.1, 10.0)
# Anomaly injection parameters
inject_anomalies: bool = False
anomaly_probability: float = 0.02
anomaly_magnitude_range: tuple[float, float] = (2.0, 5.0)
# Level continuity between subseries
maintain_level_continuity: bool = True
max_level_jump_between_subseries: float = 5.0
class AnomalyType(Enum):
"""Types of anomalies that can be generated."""
SPIKE_UP = "spike_up"
SPIKE_DOWN = "spike_down"
class MagnitudePattern(Enum):
"""Spike magnitude patterns."""
CONSTANT = "constant" # All spikes have similar magnitude
INCREASING = "increasing" # Magnitude increases over time
DECREASING = "decreasing" # Magnitude decreases over time
CYCLICAL = "cyclical" # Magnitude follows a cyclical pattern
RANDOM_BOUNDED = "random_bounded" # Random within bounds but with some correlation
@dataclass
class AnomalyGeneratorParams(GeneratorParams):
"""Parameters for anomaly time series generation."""
# Base signal parameters
base_level_range: tuple[float, float] = (-100.0, 100.0)
# Spike direction (50% up-only, 50% down-only series)
spike_direction_probability: float = 0.5 # Probability of up-only vs down-only series
# Periodicity parameters (uniform singles are always generated; variance/jitter ignored for base schedule)
base_period_range: tuple[int, int] = (100, 300) # Base period between spike events
period_variance: float = 0.0 # Not used for base schedule anymore
# Series-level behavior probabilities
cluster_series_probability: float = 0.25 # 25% of series add clusters near base spikes
random_series_probability: float = 0.25 # 25% of series add random single spikes
# Cluster augmentation parameters (relative to base uniform spikes)
# Fraction of base spike events that will receive nearby extra spikes
cluster_event_fraction: float = 0.3
# Number of additional spikes to add per selected event (upper bound exclusive like np.random.randint)
cluster_additional_spikes_range: tuple[int, int] = (1, 4) # yields 1..3
# Offset window (in time steps) around the base spike for additional spikes (inclusive of negatives)
cluster_offset_range: tuple[int, int] = (-10, 11) # yields [-10..10]
# Random single spikes augmentation across the series (not tied to base events)
# Number of random spikes as a fraction of the number of base spikes
random_spike_fraction_of_base: float = 0.3
# Spike magnitude parameters
magnitude_pattern: MagnitudePattern = MagnitudePattern.RANDOM_BOUNDED
base_magnitude_range: tuple[float, float] = (10.0, 50.0)
magnitude_correlation: float = 0.7 # Correlation between consecutive spike magnitudes (0-1)
magnitude_trend_strength: float = 0.02 # Strength of increasing/decreasing trend
cyclical_period_ratio: float = 0.3 # Ratio of cyclical period to series length
# Noise parameters
magnitude_noise: float = 0.1 # Random noise added to magnitude (as fraction of base magnitude)
timing_jitter: float = 0.0 # Not used for base schedule anymore
def __post_init__(self):
"""Validate parameters after initialization."""
if not (0 <= self.spike_direction_probability <= 1):
raise ValueError("spike_direction_probability must be between 0 and 1")
if not (0 <= self.period_variance <= 0.5):
raise ValueError("period_variance must be between 0 and 0.5")
if not (0 <= self.magnitude_correlation <= 1):
raise ValueError("magnitude_correlation must be between 0 and 1")
if self.base_period_range[0] >= self.base_period_range[1]:
raise ValueError("base_period_range must have min < max")
# Validate series-type probabilities
if not (0.0 <= self.cluster_series_probability <= 1.0):
raise ValueError("cluster_series_probability must be between 0 and 1")
if not (0.0 <= self.random_series_probability <= 1.0):
raise ValueError("random_series_probability must be between 0 and 1")
if self.cluster_series_probability + self.random_series_probability > 1.0:
raise ValueError("Sum of cluster_series_probability and random_series_probability must be <= 1")
# Validate cluster augmentation
if not (0.0 <= self.cluster_event_fraction <= 1.0):
raise ValueError("cluster_event_fraction must be between 0 and 1")
if self.cluster_additional_spikes_range[0] >= self.cluster_additional_spikes_range[1]:
raise ValueError("cluster_additional_spikes_range must have min < max")
if self.cluster_offset_range[0] >= self.cluster_offset_range[1]:
raise ValueError("cluster_offset_range must have min < max")
# Validate random augmentation
if not (0.0 <= self.random_spike_fraction_of_base <= 1.0):
raise ValueError("random_spike_fraction_of_base must be between 0 and 1")
class SpikeShape(Enum):
"""Enumeration of spike shapes."""
V_SHAPE = "v"
INVERTED_V = "inverted_v"
CHOPPED_V = "chopped_v"
CHOPPED_INVERTED_V = "chopped_inverted_v"
@dataclass
class SpikesGeneratorParams(GeneratorParams):
"""Parameters for spike time series generation."""
# Separate spike counts for different modes
spike_count_burst: tuple[int, int] = (2, 4)
spike_count_uniform: tuple[int, int] = (4, 7)
# Spike amplitude parameters (absolute values, sign determined per series)
spike_amplitude: float | tuple[float, float] = (50.0, 300.0)
# Spike angle range in degrees (controls steepness) - sampled once per series
spike_angle_range: tuple[float, float] = (70.0, 85.0)
# Probability of burst mode vs spread mode (5% burst, 95% spread)
burst_mode_probability: float = 0.05
# Plateau duration for chopped spikes (in time steps)
plateau_duration: tuple[int, int] = (30, 50)
# Baseline value (should be close to zero)
baseline: float | tuple[float, float] = (-200, 200)
# Burst clustering parameters - fraction of series length for burst width
burst_width_fraction: tuple[float, float] = (0.1, 0.25)
# Spread mode edge margin ratio: edges are set to this fraction of the
# inter-spike spacing. Smaller values yield smaller left/right margins and
# larger spacing between spikes. Example: 0.2 => edge margins are 20% of
# the spacing between spikes.
edge_margin_ratio: float = 0.2
# Probability of spikes being above baseline (vs below baseline) per series
spikes_above_baseline_probability: float = 0.5
# Probability of each series type
series_type_probabilities: dict[str, float] = field(
default_factory=lambda: {
"v_only": 0.4,
"chopped_only": 0.3,
"mixed": 0.3,
}
)
# Minimum spike width in time steps (to ensure visible spikes)
min_spike_width: int = 30
# Maximum spike width in time steps (to prevent overly wide spikes)
max_spike_width: int = 100
# Minimum margin between spikes (only used in burst mode)
min_spike_margin: int = 10
# Noise parameters - applied to entire signal
noise_std: float = 2
noise_probability: float = 0.5
brown_noise_alpha: float = 2.0 # Power law exponent (2.0 = brown noise)
noise_cutoff_freq: float = 0.1 # Relative to Nyquist frequency
@dataclass
class CauKerGeneratorParams(GeneratorParams):
"""Parameters for the CauKer (SCM-GP) generator."""
# Number of channels (features) to sample per series. If a tuple(range)
# or list is provided, the wrapper will pick a single value for the whole batch.
num_channels: int | tuple[int, int] | list[int] = 6
# Maximum number of parents per node in the DAG
max_parents: int = 3
# Total number of nodes in the underlying DAG
num_nodes: int = 6
class TrendType(Enum):
"""Types of trends that can be applied to the OU process."""
NONE = "none" # No trend, classic OU behavior
LINEAR = "linear" # Linear drift in mu over time
EXPONENTIAL = "exponential" # Exponential growth/decay in mu
LOGISTIC = "logistic" # S-curve growth pattern
SINUSOIDAL = "sinusoidal" # Cyclical trend
PIECEWISE_LINEAR = "piecewise_linear" # Multiple linear segments
POLYNOMIAL = "polynomial" # Polynomial trend (quadratic/cubic)
@dataclass
class TrendConfig:
"""Configuration for time-varying trends in OU process parameters."""
trend_type: TrendType = TrendType.NONE
# Linear trend parameters
linear_slope_range: tuple[float, float] = (-0.01, 0.01)
# Exponential trend parameters
exp_rate_range: tuple[float, float] = (-0.005, 0.005)
exp_asymptote_range: tuple[float, float] = (-5.0, 5.0)
# Logistic trend parameters
logistic_growth_rate_range: tuple[float, float] = (0.01, 0.1)
logistic_capacity_range: tuple[float, float] = (5.0, 20.0)
logistic_midpoint_ratio_range: tuple[float, float] = (
0.3,
0.7,
) # As fraction of series length
# Sinusoidal trend parameters
sin_amplitude_range: tuple[float, float] = (1.0, 5.0)
sin_period_ratio_range: tuple[float, float] = (
0.1,
0.5,
) # As fraction of series length
sin_phase_range: tuple[float, float] = (0.0, 2.0 * np.pi)
# Piecewise linear parameters
num_segments_range: tuple[int, int] = (2, 5)
segment_slope_range: tuple[float, float] = (-0.02, 0.02)
# Polynomial trend parameters
poly_degree_range: tuple[int, int] = (2, 3)
poly_coeff_range: tuple[float, float] = (
-1e-6,
1e-6,
) # Small coefficients for stability
# Structural change parameters
enable_structural_changes: bool = True
num_structural_changes_range: tuple[int, int] = (0, 3)
structural_change_magnitude_range: tuple[float, float] = (1.0, 5.0)
min_segment_length: int = 200 # Minimum length between structural changes
@dataclass
class OrnsteinUhlenbeckProcessGeneratorParams(GeneratorParams):
"""Parameters for the Regime-Switching Ornstein-Uhlenbeck generator.
The generator samples concrete values per series using these ranges.
Enhanced with time-varying parameter support for realistic non-stationary behavior.
"""
# Integration step size used inside the generator
dt: float = 0.01
# Regime 0 parameter distributions
regime0_theta_range: tuple[float, float] = (1.0, 5.0)
regime0_mu_mean_std: tuple[float, float] = (-2.0, 1.0)
regime0_sigma_lognormal_params: tuple[float, float] = (float(np.log(0.3)), 0.3)
# Regime 0 volatility process parameters
regime0_vol_reversion_range: tuple[float, float] = (2.0, 5.0) # kappa_v
regime0_vol_mean_range: tuple[float, float] = (0.2, 0.4) # theta_v
regime0_vol_vol_range: tuple[float, float] = (0.1, 0.3) # xi_v
# Regime 1 parameter distributions
regime1_theta_range: tuple[float, float] = (0.05, 0.5)
regime1_mu_mean_std: tuple[float, float] = (2.0, 1.0)
regime1_sigma_lognormal_params: tuple[float, float] = (float(np.log(1.5)), 0.5)
# Regime 1 volatility process parameters
regime1_vol_reversion_range: tuple[float, float] = (0.5, 2.0) # kappa_v
regime1_vol_mean_range: tuple[float, float] = (0.8, 1.2) # theta_v
regime1_vol_vol_range: tuple[float, float] = (0.3, 0.5) # xi_v
# Initial value distributions
x0_mean_std: tuple[float, float] = (0.0, 2.0)
# Transition matrix diagonal probabilities (allow more frequent regime changes)
p00_range: tuple[float, float] = (0.85, 0.999) # Allow more frequent transitions
p11_range: tuple[float, float] = (0.85, 0.999)
# Time-varying parameter support
trend_config: TrendConfig = field(default_factory=TrendConfig)
# Probability of applying trends to different parameters
mu_trend_probability: float = 0.7 # High probability for realistic non-stationarity
theta_trend_probability: float = 0.2 # Occasional changes in mean reversion speed
sigma_trend_probability: float = 0.3 # Occasional changes in volatility
# Global scaling and level parameters for real-world applicability
global_level_range: tuple[float, float] = (
-100.0,
100.0,
) # Base level around which process evolves
global_scale_range: tuple[float, float] = (
0.1,
50.0,
) # Scale factor for entire series
# Noise injection for additional realism
measurement_noise_std_range: tuple[float, float] = (
0.0,
0.1,
) # Additive measurement noise
# Long-term memory parameters (for more realistic autocorrelation)
enable_long_memory: bool = False
hurst_exponent_range: tuple[float, float] = (
0.3,
0.8,
) # Fractional Brownian motion component
# Seasonality parameters
enable_seasonality: bool = True
num_seasonal_components_range: tuple[int, int] = (
1,
3,
) # Number of seasonal components
seasonal_periods: tuple[float, ...] = (
7.0, # Weekly
30.0, # Monthly
90.0, # Quarterly
365.25, # Yearly
182.625, # Semi-annual
) # Available seasonal periods (in time units)
seasonal_amplitude_range: tuple[float, float] = (
0.5,
3.0,
) # Amplitude of seasonal components
seasonal_phase_range: tuple[float, float] = (0.0, 2.0 * np.pi) # Phase shift range
seasonal_period_jitter: float = 0.05 # Jitter applied to periods for realism (±5%)
# Probability of applying seasonality to different parameters
mu_seasonality_probability: float = 0.6 # Probability of seasonal mean
sigma_seasonality_probability: float = 0.3 # Probability of seasonal volatility
# Seasonal component decay/growth over time
enable_seasonal_evolution: bool = True
seasonal_amplitude_trend_range: tuple[float, float] = (
-0.001,
0.001,
) # Trend in seasonal amplitude
def __post_init__(self):
if self.dt <= 0:
raise ValueError("dt must be positive for OU process simulation")
if not (0.0 <= self.mu_trend_probability <= 1.0):
raise ValueError("mu_trend_probability must be between 0 and 1")
if not (0.0 <= self.theta_trend_probability <= 1.0):
raise ValueError("theta_trend_probability must be between 0 and 1")
if not (0.0 <= self.sigma_trend_probability <= 1.0):
raise ValueError("sigma_trend_probability must be between 0 and 1")
if self.global_level_range[0] >= self.global_level_range[1]:
raise ValueError("global_level_range must have min < max")
if self.global_scale_range[0] <= 0:
raise ValueError("global_scale_range values must be positive")
# =====================
# Audio generator params
# =====================
@dataclass
class AudioGeneratorParams(GeneratorParams):
"""Common parameters for audio-based time series generators (pyo-backed)."""
# Offline pyo rendering configuration
server_duration: float = 2.0 # seconds
sample_rate: int = 44100 # Hz
# Output post-processing
normalize_output: bool = True # Normalize to unit max abs before returning
@dataclass
class FinancialVolatilityAudioParams(AudioGeneratorParams):
"""Parameters for the FinancialVolatility audio generator."""
# Trend LFO controlling slow drift
trend_lfo_freq_range: tuple[float, float] = (0.1, 0.5)
trend_lfo_mul_range: tuple[float, float] = (0.2, 0.5)
# Volatility clustering
volatility_carrier_freq_range: tuple[float, float] = (1.0, 5.0)
follower_freq_range: tuple[float, float] = (1.0, 4.0)
volatility_range: tuple[float, float] = (0.1, 0.8)
# Market jumps/shocks
jump_metro_time_range: tuple[float, float] = (0.3, 1.0)
jump_env_start_range: tuple[float, float] = (0.5, 1.0)
jump_env_decay_time_range: tuple[float, float] = (0.05, 0.2)
jump_freq_range: tuple[float, float] = (20.0, 80.0)
jump_direction_up_probability: float = 0.5
@dataclass
class MultiScaleFractalAudioParams(AudioGeneratorParams):
"""Parameters for the Multi-Scale Fractal audio generator."""
base_noise_mul_range: tuple[float, float] = (0.3, 0.8)
num_scales_range: tuple[int, int] = (3, 6)
scale_freq_base_range: tuple[float, float] = (20.0, 2000.0)
q_factor_range: tuple[float, float] = (0.5, 3.0)
per_scale_attenuation_range: tuple[float, float] = (
0.5,
0.8,
) # multiplier per scale index
@dataclass
class StochasticRhythmAudioParams(AudioGeneratorParams):
"""Parameters for the Stochastic Rhythm audio generator."""
base_tempo_hz_range: tuple[float, float] = (2.0, 8.0)
num_layers_range: tuple[int, int] = (3, 5)
subdivisions: tuple[int, ...] = (1, 2, 3, 4, 6, 8)
attack_range: tuple[float, float] = (0.001, 0.01)
decay_range: tuple[float, float] = (0.05, 0.3)
tone_freq_range: tuple[float, float] = (50.0, 800.0)
tone_mul_range: tuple[float, float] = (0.2, 0.5)
@dataclass
class NetworkTopologyAudioParams(AudioGeneratorParams):
"""Parameters for the Network Topology audio generator."""
# Base traffic flow
traffic_lfo_freq_range: tuple[float, float] = (0.2, 1.0)
traffic_lfo_mul_range: tuple[float, float] = (0.2, 0.5)
# Packet bursts
burst_rate_hz_range: tuple[float, float] = (3.0, 12.0)
burst_duration_range: tuple[float, float] = (0.02, 0.1)
burst_mul_range: tuple[float, float] = (0.2, 0.6)
# Periodic congestion
congestion_period_range: tuple[float, float] = (1.0, 3.0) # seconds between events
congestion_depth_range: tuple[float, float] = (-0.6, -0.2)
congestion_release_time_range: tuple[float, float] = (0.3, 0.8)
# Protocol overhead
overhead_lfo_freq_range: tuple[float, float] = (20.0, 50.0)
overhead_mul_range: tuple[float, float] = (0.05, 0.15)
# DDoS-like spikes / attacks
attack_period_range: tuple[float, float] = (2.0, 5.0)
attack_env_points: tuple[tuple[float, float], tuple[float, float], tuple[float, float]] = (
(0.0, 1.2),
(0.1, 0.8),
(0.8, 0.0),
)
attack_mul_range: tuple[float, float] = (0.4, 0.8)
|