dfm-python 0.6.12__py3-none-any.whl → 0.6.14__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.
- dfm_python/__init__.py +1 -1
- dfm_python/config/constants.py +59 -0
- dfm_python/config/schema/__init__.py +6 -6
- dfm_python/config/schema/model.py +264 -0
- dfm_python/config/schema/params.py +183 -0
- dfm_python/config/schema/results.py +173 -84
- dfm_python/dataset/__init__.py +2 -0
- dfm_python/dataset/ddfm_dataset.py +25 -2
- dfm_python/dataset/ivdfm_dataset.py +368 -0
- dfm_python/functional/krylov.py +2 -61
- dfm_python/layer/mlp.py +274 -0
- dfm_python/layer/pca.py +304 -0
- dfm_python/logger/__init__.py +8 -0
- dfm_python/logger/ivdfm_logger.py +92 -0
- dfm_python/models/base.py +51 -1
- dfm_python/models/ddfm/__init__.py +12 -0
- dfm_python/models/ddfm/ddfm.py +18 -9
- dfm_python/models/ddfm/decoder.py +263 -0
- dfm_python/{encoder/simple_autoencoder.py → models/ddfm/encoder.py} +204 -43
- dfm_python/models/dfm/initialization.py +1 -1
- dfm_python/models/dfm/mixed_freq.py +24 -3
- dfm_python/models/ivdfm/__init__.py +16 -0
- dfm_python/models/ivdfm/decoder.py +137 -0
- dfm_python/models/ivdfm/encoder.py +193 -0
- dfm_python/models/ivdfm/ivdfm.py +933 -457
- dfm_python/models/ivdfm/prior.py +560 -0
- dfm_python/numeric/builder.py +209 -0
- dfm_python/ssm/__init__.py +6 -0
- dfm_python/ssm/base.py +208 -0
- dfm_python/ssm/companion.py +292 -0
- dfm_python/utils/__init__.py +2 -2
- dfm_python/utils/loss.py +294 -2
- {dfm_python-0.6.12.dist-info → dfm_python-0.6.14.dist-info}/METADATA +1 -1
- dfm_python-0.6.14.dist-info/RECORD +64 -0
- dfm_python/decoder/__init__.py +0 -16
- dfm_python/decoder/base.py +0 -78
- dfm_python/decoder/linear.py +0 -72
- dfm_python/decoder/mlp.py +0 -128
- dfm_python/encoder/__init__.py +0 -38
- dfm_python/encoder/base.py +0 -111
- dfm_python/encoder/pca.py +0 -271
- dfm_python/encoder/variational_autoencoder.py +0 -0
- dfm_python/functional/cauchy.py +0 -236
- dfm_python-0.6.12.dist-info/RECORD +0 -62
- {dfm_python-0.6.12.dist-info → dfm_python-0.6.14.dist-info}/WHEEL +0 -0
dfm_python/__init__.py
CHANGED
|
@@ -63,7 +63,7 @@ Note: DFMConfig uses frequency dict to specify series (column names -> frequenci
|
|
|
63
63
|
For detailed documentation, see the README.md file and the tutorial notebooks/scripts.
|
|
64
64
|
"""
|
|
65
65
|
|
|
66
|
-
__version__ = "0.
|
|
66
|
+
__version__ = "0.6.13"
|
|
67
67
|
|
|
68
68
|
# ============================================================================
|
|
69
69
|
# PUBLIC API DEFINITION
|
dfm_python/config/constants.py
CHANGED
|
@@ -344,6 +344,47 @@ COMPUTATION_ERROR_TYPES = (RuntimeError, ValueError, TypeError, AttributeError,
|
|
|
344
344
|
DEFAULT_KDFM_AR_ORDER = 1 # Default AR order (VAR lag order p) for KDFM
|
|
345
345
|
DEFAULT_KDFM_MA_ORDER = 0 # Default MA order (MA lag order q) for KDFM (0 = pure VAR)
|
|
346
346
|
|
|
347
|
+
# ============================================================================
|
|
348
|
+
# iVDFM Defaults
|
|
349
|
+
# ============================================================================
|
|
350
|
+
|
|
351
|
+
# iVDFM sequence and dimension defaults
|
|
352
|
+
DEFAULT_IVDFM_SEQUENCE_LENGTH = 100 # Default sequence length for sliding windows
|
|
353
|
+
DEFAULT_IVDFM_LATENT_DIM = 3 # Default number of factors
|
|
354
|
+
DEFAULT_IVDFM_AUX_DIM = 1 # Default auxiliary variable dimension
|
|
355
|
+
|
|
356
|
+
# iVDFM network architecture defaults
|
|
357
|
+
DEFAULT_IVDFM_ENCODER_HIDDEN_DIM = 200 # Default encoder hidden dimension
|
|
358
|
+
DEFAULT_IVDFM_ENCODER_N_LAYERS = 3 # Default encoder layers
|
|
359
|
+
DEFAULT_IVDFM_DECODER_HIDDEN_DIM = 200 # Default decoder hidden dimension
|
|
360
|
+
DEFAULT_IVDFM_DECODER_N_LAYERS = 3 # Default decoder layers
|
|
361
|
+
DEFAULT_IVDFM_PRIOR_HIDDEN_DIM = 100 # Default prior network hidden dimension
|
|
362
|
+
DEFAULT_IVDFM_PRIOR_N_LAYERS = 2 # Default prior network layers
|
|
363
|
+
|
|
364
|
+
# iVDFM dynamics and distribution defaults
|
|
365
|
+
DEFAULT_IVDFM_FACTOR_ORDER = 1 # Default AR order for factors
|
|
366
|
+
DEFAULT_IVDFM_INNOVATION_DIST = 'laplace' # Default innovation distribution
|
|
367
|
+
DEFAULT_IVDFM_DECODER_VAR = 0.01 # Default decoder variance
|
|
368
|
+
DEFAULT_IVDFM_ACTIVATION = 'lrelu' # Default activation function
|
|
369
|
+
DEFAULT_IVDFM_SLOPE = 0.1 # Default leaky ReLU slope
|
|
370
|
+
|
|
371
|
+
# iVDFM training defaults
|
|
372
|
+
DEFAULT_IVDFM_BATCH_SIZE = 32 # Default batch size
|
|
373
|
+
DEFAULT_IVDFM_MAX_EPOCHS = 100 # Default maximum epochs
|
|
374
|
+
DEFAULT_IVDFM_AUX_VARIABLE_TYPE = 'time' # Default auxiliary variable type
|
|
375
|
+
|
|
376
|
+
# iVDFM optimizer defaults
|
|
377
|
+
DEFAULT_IVDFM_OPTIMIZER_WEIGHT_DECAY = 0.0 # Default weight decay for optimizer
|
|
378
|
+
DEFAULT_IVDFM_OPTIMIZER_MOMENTUM = 0.9 # Default momentum for SGD
|
|
379
|
+
|
|
380
|
+
# iVDFM scheduler defaults
|
|
381
|
+
DEFAULT_IVDFM_SCHEDULER_TYPE = 'step' # Default scheduler type: 'step', 'plateau', 'cosine', 'exponential', None
|
|
382
|
+
DEFAULT_IVDFM_SCHEDULER_STEP_SIZE = None # Default step_size for StepLR (auto: max_epochs // 3)
|
|
383
|
+
DEFAULT_IVDFM_SCHEDULER_GAMMA = 0.5 # Default gamma for StepLR/ExponentialLR
|
|
384
|
+
DEFAULT_IVDFM_SCHEDULER_PATIENCE = 10 # Default patience for ReduceLROnPlateau
|
|
385
|
+
DEFAULT_IVDFM_SCHEDULER_FACTOR = 0.1 # Default factor for ReduceLROnPlateau
|
|
386
|
+
DEFAULT_IVDFM_SCHEDULER_MIN_LR = 0.0 # Default min_lr for ReduceLROnPlateau
|
|
387
|
+
|
|
347
388
|
# ============================================================================
|
|
348
389
|
# Tutorial Defaults
|
|
349
390
|
# ============================================================================
|
|
@@ -564,6 +605,24 @@ __all__ = [
|
|
|
564
605
|
# KDFM defaults
|
|
565
606
|
'DEFAULT_KDFM_AR_ORDER',
|
|
566
607
|
'DEFAULT_KDFM_MA_ORDER',
|
|
608
|
+
# iVDFM defaults
|
|
609
|
+
'DEFAULT_IVDFM_SEQUENCE_LENGTH',
|
|
610
|
+
'DEFAULT_IVDFM_LATENT_DIM',
|
|
611
|
+
'DEFAULT_IVDFM_AUX_DIM',
|
|
612
|
+
'DEFAULT_IVDFM_ENCODER_HIDDEN_DIM',
|
|
613
|
+
'DEFAULT_IVDFM_ENCODER_N_LAYERS',
|
|
614
|
+
'DEFAULT_IVDFM_DECODER_HIDDEN_DIM',
|
|
615
|
+
'DEFAULT_IVDFM_DECODER_N_LAYERS',
|
|
616
|
+
'DEFAULT_IVDFM_PRIOR_HIDDEN_DIM',
|
|
617
|
+
'DEFAULT_IVDFM_PRIOR_N_LAYERS',
|
|
618
|
+
'DEFAULT_IVDFM_FACTOR_ORDER',
|
|
619
|
+
'DEFAULT_IVDFM_INNOVATION_DIST',
|
|
620
|
+
'DEFAULT_IVDFM_DECODER_VAR',
|
|
621
|
+
'DEFAULT_IVDFM_ACTIVATION',
|
|
622
|
+
'DEFAULT_IVDFM_SLOPE',
|
|
623
|
+
'DEFAULT_IVDFM_BATCH_SIZE',
|
|
624
|
+
'DEFAULT_IVDFM_MAX_EPOCHS',
|
|
625
|
+
'DEFAULT_IVDFM_AUX_VARIABLE_TYPE',
|
|
567
626
|
# Tutorial defaults
|
|
568
627
|
'TUTORIAL_MAX_PERIODS',
|
|
569
628
|
'TUTORIAL_MAX_EPOCHS',
|
|
@@ -7,13 +7,13 @@ This package contains:
|
|
|
7
7
|
Note: Series are specified via frequency dict mapping column names to frequencies.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from .model import BaseModelConfig, DFMConfig, DDFMConfig
|
|
11
|
-
from .results import BaseResult, DFMResult, DDFMResult
|
|
12
|
-
from .params import DFMStateSpaceParams, DDFMStateSpaceParams
|
|
10
|
+
from .model import BaseModelConfig, DFMConfig, DDFMConfig, iVDFMConfig
|
|
11
|
+
from .results import BaseResult, DFMResult, DDFMResult, iVDFMResult
|
|
12
|
+
from .params import DFMStateSpaceParams, DDFMStateSpaceParams, iVDFMModelState
|
|
13
13
|
|
|
14
14
|
__all__ = [
|
|
15
|
-
'BaseModelConfig', 'DFMConfig', 'DDFMConfig',
|
|
16
|
-
'BaseResult', 'DFMResult', 'DDFMResult',
|
|
17
|
-
'DFMStateSpaceParams', 'DDFMStateSpaceParams',
|
|
15
|
+
'BaseModelConfig', 'DFMConfig', 'DDFMConfig', 'iVDFMConfig',
|
|
16
|
+
'BaseResult', 'DFMResult', 'DDFMResult', 'iVDFMResult',
|
|
17
|
+
'DFMStateSpaceParams', 'DDFMStateSpaceParams', 'iVDFMModelState',
|
|
18
18
|
]
|
|
19
19
|
|
|
@@ -48,6 +48,14 @@ from ..constants import (
|
|
|
48
48
|
DEFAULT_BATCH_SIZE,
|
|
49
49
|
DEFAULT_DDFM_WINDOW_SIZE,
|
|
50
50
|
DEFAULT_GRAD_CLIP_VAL,
|
|
51
|
+
DEFAULT_IVDFM_OPTIMIZER_WEIGHT_DECAY,
|
|
52
|
+
DEFAULT_IVDFM_OPTIMIZER_MOMENTUM,
|
|
53
|
+
DEFAULT_IVDFM_SCHEDULER_TYPE,
|
|
54
|
+
DEFAULT_IVDFM_SCHEDULER_STEP_SIZE,
|
|
55
|
+
DEFAULT_IVDFM_SCHEDULER_GAMMA,
|
|
56
|
+
DEFAULT_IVDFM_SCHEDULER_PATIENCE,
|
|
57
|
+
DEFAULT_IVDFM_SCHEDULER_FACTOR,
|
|
58
|
+
DEFAULT_IVDFM_SCHEDULER_MIN_LR,
|
|
51
59
|
DEFAULT_REGULARIZATION_SCALE,
|
|
52
60
|
DEFAULT_STRUCTURAL_REG_WEIGHT,
|
|
53
61
|
DEFAULT_CONVERGENCE_THRESHOLD,
|
|
@@ -70,6 +78,23 @@ from ..constants import (
|
|
|
70
78
|
DEFAULT_KDFM_MA_ORDER,
|
|
71
79
|
FREQUENCY_HIERARCHY,
|
|
72
80
|
DEFAULT_HIERARCHY_VALUE,
|
|
81
|
+
DEFAULT_IVDFM_SEQUENCE_LENGTH,
|
|
82
|
+
DEFAULT_IVDFM_LATENT_DIM,
|
|
83
|
+
DEFAULT_IVDFM_AUX_DIM,
|
|
84
|
+
DEFAULT_IVDFM_ENCODER_HIDDEN_DIM,
|
|
85
|
+
DEFAULT_IVDFM_ENCODER_N_LAYERS,
|
|
86
|
+
DEFAULT_IVDFM_DECODER_HIDDEN_DIM,
|
|
87
|
+
DEFAULT_IVDFM_DECODER_N_LAYERS,
|
|
88
|
+
DEFAULT_IVDFM_PRIOR_HIDDEN_DIM,
|
|
89
|
+
DEFAULT_IVDFM_PRIOR_N_LAYERS,
|
|
90
|
+
DEFAULT_IVDFM_FACTOR_ORDER,
|
|
91
|
+
DEFAULT_IVDFM_INNOVATION_DIST,
|
|
92
|
+
DEFAULT_IVDFM_DECODER_VAR,
|
|
93
|
+
DEFAULT_IVDFM_ACTIVATION,
|
|
94
|
+
DEFAULT_IVDFM_SLOPE,
|
|
95
|
+
DEFAULT_IVDFM_BATCH_SIZE,
|
|
96
|
+
DEFAULT_IVDFM_MAX_EPOCHS,
|
|
97
|
+
DEFAULT_IVDFM_AUX_VARIABLE_TYPE,
|
|
73
98
|
)
|
|
74
99
|
|
|
75
100
|
|
|
@@ -136,6 +161,7 @@ class BaseModelConfig:
|
|
|
136
161
|
# ========================================================================
|
|
137
162
|
clock: str = 'm' # Required: Base frequency for latent factors (global clock): 'd', 'w', 'm', 'q', 'sa', 'a' (defaults to 'm' for monthly)
|
|
138
163
|
target_scaler: Optional[ScalerType] = None # Fitted sklearn scaler instance (StandardScaler, RobustScaler, etc.) for target series only. Must be a fitted scaler object (call .fit() on target data first). Pass scaler object directly, not string. Feature series are assumed to be manually preprocessed. If None, target series are assumed to be already in the desired scale.
|
|
164
|
+
scaler: Optional[str] = None # Scaler type as string: 'standard', 'robust', 'minmax', 'maxabs', 'quantile', or null. Applied to targets only (not context/auxiliary variables). For iVDFM, this is the preferred way to specify scaler (like DDFM pattern).
|
|
139
165
|
|
|
140
166
|
def __post_init__(self):
|
|
141
167
|
"""Validate basic model structure.
|
|
@@ -626,6 +652,244 @@ class DDFMConfig(BaseModelConfig):
|
|
|
626
652
|
)
|
|
627
653
|
|
|
628
654
|
|
|
655
|
+
@dataclass
|
|
656
|
+
class iVDFMConfig(BaseModelConfig):
|
|
657
|
+
"""Identifiable Variational Dynamic Factor Model configuration.
|
|
658
|
+
|
|
659
|
+
Uses 'scaler' (string) pattern like DDFM, not 'target_scaler' (instance).
|
|
660
|
+
Scaler is applied to targets only, not to context/auxiliary variables.
|
|
661
|
+
"""
|
|
662
|
+
"""Identifiable Variational Dynamic Factor Model configuration.
|
|
663
|
+
|
|
664
|
+
This configuration class extends BaseModelConfig with parameters specific
|
|
665
|
+
to iVDFM models trained using variational inference with identifiable
|
|
666
|
+
innovation priors.
|
|
667
|
+
|
|
668
|
+
Note: iVDFM does NOT use block structure. Use num_factors directly to specify
|
|
669
|
+
the number of factors.
|
|
670
|
+
|
|
671
|
+
The configuration can be built from:
|
|
672
|
+
- Main settings (training parameters) from config files
|
|
673
|
+
- Series definitions via frequency dict (column names -> frequencies)
|
|
674
|
+
"""
|
|
675
|
+
# ========================================================================
|
|
676
|
+
# Model Structure
|
|
677
|
+
# ========================================================================
|
|
678
|
+
num_factors: Optional[int] = None # Number of factors (inferred from config if None)
|
|
679
|
+
sequence_length: int = DEFAULT_IVDFM_SEQUENCE_LENGTH # Sequence length for training
|
|
680
|
+
context: Optional[Union[List[str], List[int]]] = None # Column names (DataFrame) or indices (array) for context variables. If None, context_dim is used to generate time-based context.
|
|
681
|
+
context_dim: int = DEFAULT_IVDFM_AUX_DIM # Dimension of context. Used when context is None (generates time-based context) or when context is multivariate.
|
|
682
|
+
|
|
683
|
+
# ========================================================================
|
|
684
|
+
# Neural Network Architecture
|
|
685
|
+
# ========================================================================
|
|
686
|
+
encoder_hidden_dim: Union[int, List[int]] = DEFAULT_IVDFM_ENCODER_HIDDEN_DIM # Encoder architecture
|
|
687
|
+
encoder_n_layers: int = DEFAULT_IVDFM_ENCODER_N_LAYERS # Number of encoder layers
|
|
688
|
+
decoder_hidden_dim: Union[int, List[int]] = DEFAULT_IVDFM_DECODER_HIDDEN_DIM # Decoder architecture
|
|
689
|
+
decoder_n_layers: int = DEFAULT_IVDFM_DECODER_N_LAYERS # Number of decoder layers
|
|
690
|
+
prior_hidden_dim: Union[int, List[int]] = DEFAULT_IVDFM_PRIOR_HIDDEN_DIM # Prior network architecture
|
|
691
|
+
prior_n_layers: int = DEFAULT_IVDFM_PRIOR_N_LAYERS # Number of prior network layers
|
|
692
|
+
activation: str = DEFAULT_IVDFM_ACTIVATION # Activation function
|
|
693
|
+
slope: float = DEFAULT_IVDFM_SLOPE # Leaky ReLU slope
|
|
694
|
+
|
|
695
|
+
# ========================================================================
|
|
696
|
+
# Dynamics and Distribution Parameters
|
|
697
|
+
# ========================================================================
|
|
698
|
+
factor_order: int = DEFAULT_IVDFM_FACTOR_ORDER # AR order for factors
|
|
699
|
+
innovation_distribution: str = DEFAULT_IVDFM_INNOVATION_DIST # Innovation distribution type
|
|
700
|
+
decoder_var: float = DEFAULT_IVDFM_DECODER_VAR # Decoder variance
|
|
701
|
+
|
|
702
|
+
# ========================================================================
|
|
703
|
+
# Training Parameters
|
|
704
|
+
# ========================================================================
|
|
705
|
+
learning_rate: float = DEFAULT_LEARNING_RATE # Learning rate
|
|
706
|
+
optimizer: str = 'Adam' # Optimizer type
|
|
707
|
+
optimizer_weight_decay: float = DEFAULT_IVDFM_OPTIMIZER_WEIGHT_DECAY # Weight decay (L2 regularization)
|
|
708
|
+
optimizer_momentum: float = DEFAULT_IVDFM_OPTIMIZER_MOMENTUM # Momentum for SGD
|
|
709
|
+
batch_size: int = DEFAULT_IVDFM_BATCH_SIZE # Batch size
|
|
710
|
+
max_epochs: int = DEFAULT_IVDFM_MAX_EPOCHS # Maximum epochs
|
|
711
|
+
tolerance: float = DEFAULT_TOLERANCE # Convergence tolerance
|
|
712
|
+
seed: Optional[int] = None # Random seed
|
|
713
|
+
|
|
714
|
+
# ========================================================================
|
|
715
|
+
# Scheduler Parameters
|
|
716
|
+
# ========================================================================
|
|
717
|
+
scheduler_type: Optional[str] = DEFAULT_IVDFM_SCHEDULER_TYPE # Scheduler type: 'step', 'plateau', 'cosine', 'exponential', None
|
|
718
|
+
scheduler_step_size: Optional[int] = DEFAULT_IVDFM_SCHEDULER_STEP_SIZE # Step size for StepLR (None = auto: max_epochs // 3)
|
|
719
|
+
scheduler_gamma: float = DEFAULT_IVDFM_SCHEDULER_GAMMA # Gamma for StepLR/ExponentialLR
|
|
720
|
+
scheduler_patience: int = DEFAULT_IVDFM_SCHEDULER_PATIENCE # Patience for ReduceLROnPlateau
|
|
721
|
+
scheduler_factor: float = DEFAULT_IVDFM_SCHEDULER_FACTOR # Factor for ReduceLROnPlateau
|
|
722
|
+
scheduler_min_lr: float = DEFAULT_IVDFM_SCHEDULER_MIN_LR # Min learning rate for ReduceLROnPlateau
|
|
723
|
+
|
|
724
|
+
def __post_init__(self):
|
|
725
|
+
"""Validate iVDFM configuration."""
|
|
726
|
+
super().__post_init__() # Validate base config
|
|
727
|
+
|
|
728
|
+
from ...utils.errors import ConfigurationError
|
|
729
|
+
|
|
730
|
+
# Validate sequence_length
|
|
731
|
+
if self.sequence_length < 1:
|
|
732
|
+
raise ConfigurationError(
|
|
733
|
+
f"sequence_length must be >= 1, got {self.sequence_length}"
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
# Validate context_dim
|
|
737
|
+
if self.context_dim < 1:
|
|
738
|
+
raise ConfigurationError(
|
|
739
|
+
f"context_dim must be >= 1, got {self.context_dim}"
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
# Validate num_factors if provided
|
|
743
|
+
if self.num_factors is not None and self.num_factors < 1:
|
|
744
|
+
raise ConfigurationError(
|
|
745
|
+
f"num_factors must be >= 1, got {self.num_factors}"
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
# Validate factor_order
|
|
749
|
+
if self.factor_order < 1:
|
|
750
|
+
raise ConfigurationError(
|
|
751
|
+
f"factor_order must be >= 1, got {self.factor_order}"
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
# Validate innovation_distribution
|
|
755
|
+
valid_dists = {'laplace', 'gaussian', 'student_t', 'gamma', 'beta', 'exponential'}
|
|
756
|
+
if self.innovation_distribution not in valid_dists:
|
|
757
|
+
raise ConfigurationError(
|
|
758
|
+
f"innovation_distribution must be one of {valid_dists}, got '{self.innovation_distribution}'"
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
# Validate activation
|
|
762
|
+
valid_activations = {'relu', 'lrelu', 'tanh', 'sigmoid'}
|
|
763
|
+
if self.activation not in valid_activations:
|
|
764
|
+
raise ConfigurationError(
|
|
765
|
+
f"activation must be one of {valid_activations}, got '{self.activation}'"
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
# Validate optimizer
|
|
769
|
+
from ..constants import VALID_OPTIMIZERS
|
|
770
|
+
if self.optimizer not in VALID_OPTIMIZERS:
|
|
771
|
+
raise ConfigurationError(
|
|
772
|
+
f"optimizer must be one of {VALID_OPTIMIZERS}, got '{self.optimizer}'"
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
# Validate scheduler_type
|
|
776
|
+
valid_schedulers = {'step', 'plateau', 'cosine', 'exponential', None}
|
|
777
|
+
if self.scheduler_type not in valid_schedulers:
|
|
778
|
+
raise ConfigurationError(
|
|
779
|
+
f"scheduler_type must be one of {valid_schedulers}, got '{self.scheduler_type}'"
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
# Validate scheduler parameters
|
|
783
|
+
if self.scheduler_gamma <= 0 or self.scheduler_gamma > 1:
|
|
784
|
+
raise ConfigurationError(
|
|
785
|
+
f"scheduler_gamma must be in (0, 1], got {self.scheduler_gamma}"
|
|
786
|
+
)
|
|
787
|
+
|
|
788
|
+
if self.scheduler_patience < 0:
|
|
789
|
+
raise ConfigurationError(
|
|
790
|
+
f"scheduler_patience must be >= 0, got {self.scheduler_patience}"
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
if self.scheduler_factor <= 0 or self.scheduler_factor > 1:
|
|
794
|
+
raise ConfigurationError(
|
|
795
|
+
f"scheduler_factor must be in (0, 1], got {self.scheduler_factor}"
|
|
796
|
+
)
|
|
797
|
+
|
|
798
|
+
if self.scheduler_min_lr < 0:
|
|
799
|
+
raise ConfigurationError(
|
|
800
|
+
f"scheduler_min_lr must be >= 0, got {self.scheduler_min_lr}"
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
@classmethod
|
|
804
|
+
def _extract_ivdfm(cls, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
805
|
+
"""Extract iVDFM-specific parameters from config dict."""
|
|
806
|
+
from ..constants import (
|
|
807
|
+
DEFAULT_IVDFM_SEQUENCE_LENGTH,
|
|
808
|
+
DEFAULT_IVDFM_LATENT_DIM,
|
|
809
|
+
DEFAULT_IVDFM_AUX_DIM,
|
|
810
|
+
DEFAULT_IVDFM_ENCODER_HIDDEN_DIM,
|
|
811
|
+
DEFAULT_IVDFM_ENCODER_N_LAYERS,
|
|
812
|
+
DEFAULT_IVDFM_DECODER_HIDDEN_DIM,
|
|
813
|
+
DEFAULT_IVDFM_DECODER_N_LAYERS,
|
|
814
|
+
DEFAULT_IVDFM_PRIOR_HIDDEN_DIM,
|
|
815
|
+
DEFAULT_IVDFM_PRIOR_N_LAYERS,
|
|
816
|
+
DEFAULT_IVDFM_FACTOR_ORDER,
|
|
817
|
+
DEFAULT_IVDFM_INNOVATION_DIST,
|
|
818
|
+
DEFAULT_IVDFM_DECODER_VAR,
|
|
819
|
+
DEFAULT_IVDFM_ACTIVATION,
|
|
820
|
+
DEFAULT_IVDFM_SLOPE,
|
|
821
|
+
DEFAULT_IVDFM_BATCH_SIZE,
|
|
822
|
+
DEFAULT_IVDFM_MAX_EPOCHS,
|
|
823
|
+
DEFAULT_IVDFM_AUX_VARIABLE_TYPE,
|
|
824
|
+
DEFAULT_IVDFM_OPTIMIZER_WEIGHT_DECAY,
|
|
825
|
+
DEFAULT_IVDFM_OPTIMIZER_MOMENTUM,
|
|
826
|
+
DEFAULT_IVDFM_SCHEDULER_TYPE,
|
|
827
|
+
DEFAULT_IVDFM_SCHEDULER_STEP_SIZE,
|
|
828
|
+
DEFAULT_IVDFM_SCHEDULER_GAMMA,
|
|
829
|
+
DEFAULT_IVDFM_SCHEDULER_PATIENCE,
|
|
830
|
+
DEFAULT_IVDFM_SCHEDULER_FACTOR,
|
|
831
|
+
DEFAULT_IVDFM_SCHEDULER_MIN_LR,
|
|
832
|
+
)
|
|
833
|
+
|
|
834
|
+
ivdfm_params = cls._extract_params(data, {
|
|
835
|
+
'num_factors': None,
|
|
836
|
+
'sequence_length': DEFAULT_IVDFM_SEQUENCE_LENGTH,
|
|
837
|
+
'context': None,
|
|
838
|
+
'context_dim': DEFAULT_IVDFM_AUX_DIM,
|
|
839
|
+
'encoder_hidden_dim': DEFAULT_IVDFM_ENCODER_HIDDEN_DIM,
|
|
840
|
+
'encoder_n_layers': DEFAULT_IVDFM_ENCODER_N_LAYERS,
|
|
841
|
+
'decoder_hidden_dim': DEFAULT_IVDFM_DECODER_HIDDEN_DIM,
|
|
842
|
+
'decoder_n_layers': DEFAULT_IVDFM_DECODER_N_LAYERS,
|
|
843
|
+
'prior_hidden_dim': DEFAULT_IVDFM_PRIOR_HIDDEN_DIM,
|
|
844
|
+
'prior_n_layers': DEFAULT_IVDFM_PRIOR_N_LAYERS,
|
|
845
|
+
'activation': DEFAULT_IVDFM_ACTIVATION,
|
|
846
|
+
'slope': DEFAULT_IVDFM_SLOPE,
|
|
847
|
+
'factor_order': DEFAULT_IVDFM_FACTOR_ORDER,
|
|
848
|
+
'innovation_distribution': DEFAULT_IVDFM_INNOVATION_DIST,
|
|
849
|
+
'decoder_var': DEFAULT_IVDFM_DECODER_VAR,
|
|
850
|
+
'learning_rate': DEFAULT_LEARNING_RATE,
|
|
851
|
+
'optimizer': 'Adam',
|
|
852
|
+
'optimizer_weight_decay': DEFAULT_IVDFM_OPTIMIZER_WEIGHT_DECAY,
|
|
853
|
+
'optimizer_momentum': DEFAULT_IVDFM_OPTIMIZER_MOMENTUM,
|
|
854
|
+
'batch_size': DEFAULT_IVDFM_BATCH_SIZE,
|
|
855
|
+
'max_epochs': DEFAULT_IVDFM_MAX_EPOCHS,
|
|
856
|
+
'tolerance': DEFAULT_TOLERANCE,
|
|
857
|
+
'seed': None,
|
|
858
|
+
'scheduler_type': DEFAULT_IVDFM_SCHEDULER_TYPE,
|
|
859
|
+
'scheduler_step_size': DEFAULT_IVDFM_SCHEDULER_STEP_SIZE,
|
|
860
|
+
'scheduler_gamma': DEFAULT_IVDFM_SCHEDULER_GAMMA,
|
|
861
|
+
'scheduler_patience': DEFAULT_IVDFM_SCHEDULER_PATIENCE,
|
|
862
|
+
'scheduler_factor': DEFAULT_IVDFM_SCHEDULER_FACTOR,
|
|
863
|
+
'scheduler_min_lr': DEFAULT_IVDFM_SCHEDULER_MIN_LR,
|
|
864
|
+
'scaler': None, # Scaler string: 'standard', 'robust', 'minmax', 'maxabs', 'quantile', or null
|
|
865
|
+
})
|
|
866
|
+
|
|
867
|
+
return ivdfm_params
|
|
868
|
+
|
|
869
|
+
@classmethod
|
|
870
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'iVDFMConfig':
|
|
871
|
+
"""Create iVDFMConfig from dictionary.
|
|
872
|
+
|
|
873
|
+
Parameters
|
|
874
|
+
----------
|
|
875
|
+
data : Dict[str, Any]
|
|
876
|
+
Configuration dictionary
|
|
877
|
+
|
|
878
|
+
Returns
|
|
879
|
+
-------
|
|
880
|
+
iVDFMConfig
|
|
881
|
+
iVDFM configuration instance
|
|
882
|
+
"""
|
|
883
|
+
# Extract base params
|
|
884
|
+
base_params = cls._extract_base(data)
|
|
885
|
+
|
|
886
|
+
# Extract iVDFM-specific params
|
|
887
|
+
ivdfm_params = cls._extract_ivdfm(data)
|
|
888
|
+
|
|
889
|
+
# Combine and create config
|
|
890
|
+
return cls(**base_params, **ivdfm_params)
|
|
891
|
+
|
|
892
|
+
|
|
629
893
|
# ============================================================================
|
|
630
894
|
# Validation Functions
|
|
631
895
|
# ============================================================================
|
|
@@ -5,6 +5,7 @@ This module defines dataclasses for storing state-space parameters and model str
|
|
|
5
5
|
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
import numpy as np
|
|
8
|
+
import torch
|
|
8
9
|
from typing import Optional, Any
|
|
9
10
|
|
|
10
11
|
|
|
@@ -326,6 +327,188 @@ class DDFMModelState:
|
|
|
326
327
|
|
|
327
328
|
# Backward compatibility aliases
|
|
328
329
|
DDFMTrainingState = DDFMModelState
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@dataclass
|
|
333
|
+
class iVDFMModelState:
|
|
334
|
+
"""iVDFM model state including training state and SSM parameters.
|
|
335
|
+
|
|
336
|
+
Consolidates all iVDFM model state for checkpointing: training state (convergence,
|
|
337
|
+
loss, factors, innovations) and fitted state-space model parameters.
|
|
338
|
+
|
|
339
|
+
Attributes
|
|
340
|
+
----------
|
|
341
|
+
num_iter : int
|
|
342
|
+
Current training iteration/epoch.
|
|
343
|
+
loss_now : float, optional
|
|
344
|
+
Current training loss value.
|
|
345
|
+
elbo : float, optional
|
|
346
|
+
Final ELBO value.
|
|
347
|
+
converged : bool
|
|
348
|
+
Whether training has converged.
|
|
349
|
+
factors : np.ndarray, optional
|
|
350
|
+
Extracted factors (T x r) or (batch x T x r).
|
|
351
|
+
innovations : np.ndarray, optional
|
|
352
|
+
Innovations (T x r) or (batch x T x r).
|
|
353
|
+
factor_order : int
|
|
354
|
+
AR order for factor dynamics (p in AR(p)).
|
|
355
|
+
A : np.ndarray, optional
|
|
356
|
+
Transition matrix (r x r) for p=1, or AR coefficients (r x p) for p>1.
|
|
357
|
+
B : np.ndarray, optional
|
|
358
|
+
Innovation loading matrix (r x r).
|
|
359
|
+
f0 : np.ndarray, optional
|
|
360
|
+
Initial state (r,).
|
|
361
|
+
"""
|
|
362
|
+
# Training state
|
|
363
|
+
num_iter: int = 0
|
|
364
|
+
loss_now: Optional[float] = None
|
|
365
|
+
elbo: Optional[float] = None
|
|
366
|
+
converged: bool = False
|
|
367
|
+
factors: Optional[np.ndarray] = None
|
|
368
|
+
innovations: Optional[np.ndarray] = None
|
|
369
|
+
full_state: Optional[np.ndarray] = None # Augmented state for companion form (T x r*p) for p>1, (T x r) for p=1
|
|
370
|
+
|
|
371
|
+
# SSM parameters
|
|
372
|
+
factor_order: int = 1
|
|
373
|
+
A: Optional[np.ndarray] = None # Transition matrix or AR coefficients
|
|
374
|
+
B: Optional[np.ndarray] = None # Innovation loading matrix
|
|
375
|
+
f0: Optional[np.ndarray] = None # Initial state
|
|
376
|
+
|
|
377
|
+
@classmethod
|
|
378
|
+
def from_model(cls, model: Any) -> 'iVDFMModelState':
|
|
379
|
+
"""Create iVDFMModelState from iVDFM model instance.
|
|
380
|
+
|
|
381
|
+
Parameters
|
|
382
|
+
----------
|
|
383
|
+
model : iVDFM
|
|
384
|
+
iVDFM model instance
|
|
385
|
+
|
|
386
|
+
Returns
|
|
387
|
+
-------
|
|
388
|
+
iVDFMModelState
|
|
389
|
+
Model state dataclass
|
|
390
|
+
"""
|
|
391
|
+
# Get training state from model attributes
|
|
392
|
+
factors = getattr(model, 'factors', None)
|
|
393
|
+
innovations = getattr(model, 'innovations', None)
|
|
394
|
+
num_iter = getattr(model, '_num_iter', 0)
|
|
395
|
+
loss_now = getattr(model, 'loss_now', None)
|
|
396
|
+
elbo = getattr(model, '_elbo', None)
|
|
397
|
+
converged = getattr(model, '_converged', False)
|
|
398
|
+
|
|
399
|
+
# Get SSM parameters
|
|
400
|
+
factor_order = getattr(model, 'factor_order', 1)
|
|
401
|
+
A = B = f0 = None
|
|
402
|
+
|
|
403
|
+
# Extract from SSM if available
|
|
404
|
+
if hasattr(model, 'ssm') and model.ssm is not None:
|
|
405
|
+
ssm = model.ssm
|
|
406
|
+
if factor_order == 1:
|
|
407
|
+
# First-order: A is diagonal, stored as (r,)
|
|
408
|
+
if hasattr(ssm, 'A'):
|
|
409
|
+
A = ssm.A.data.cpu().numpy() if hasattr(ssm.A, 'data') else ssm.A
|
|
410
|
+
else:
|
|
411
|
+
# Higher-order: AR coefficients stored as (r, p)
|
|
412
|
+
if hasattr(ssm, 'ar_coeffs'):
|
|
413
|
+
A = ssm.ar_coeffs.data.cpu().numpy() if hasattr(ssm.ar_coeffs, 'data') else ssm.ar_coeffs
|
|
414
|
+
|
|
415
|
+
if hasattr(ssm, 'B'):
|
|
416
|
+
B = ssm.B.data.cpu().numpy() if hasattr(ssm.B, 'data') else ssm.B
|
|
417
|
+
|
|
418
|
+
if hasattr(ssm, 'f0'):
|
|
419
|
+
f0 = ssm.f0.data.cpu().numpy() if hasattr(ssm.f0, 'data') else ssm.f0
|
|
420
|
+
|
|
421
|
+
# Fallback: get from model attributes directly
|
|
422
|
+
if A is None:
|
|
423
|
+
A = getattr(model, 'A', None)
|
|
424
|
+
if A is not None and hasattr(A, 'data'):
|
|
425
|
+
A = A.data.cpu().numpy()
|
|
426
|
+
if B is None:
|
|
427
|
+
B = getattr(model, 'B', None)
|
|
428
|
+
if B is not None and hasattr(B, 'data'):
|
|
429
|
+
B = B.data.cpu().numpy()
|
|
430
|
+
if f0 is None:
|
|
431
|
+
f0 = getattr(model, 'f0', None)
|
|
432
|
+
if f0 is not None and hasattr(f0, 'data'):
|
|
433
|
+
f0 = f0.data.cpu().numpy()
|
|
434
|
+
|
|
435
|
+
# Compute full_state (augmented state for companion form)
|
|
436
|
+
full_state = None
|
|
437
|
+
if factors is not None:
|
|
438
|
+
factors_np = factors
|
|
439
|
+
if isinstance(factors_np, np.ndarray):
|
|
440
|
+
# Average over batch if needed
|
|
441
|
+
if factors_np.ndim == 3:
|
|
442
|
+
factors_np = np.mean(factors_np, axis=0) # (T, r)
|
|
443
|
+
|
|
444
|
+
T, r = factors_np.shape
|
|
445
|
+
if factor_order == 1:
|
|
446
|
+
# For p=1, full_state is just factors
|
|
447
|
+
full_state = factors_np # (T, r)
|
|
448
|
+
else:
|
|
449
|
+
# For p>1, construct augmented state (T, r*p)
|
|
450
|
+
# s_t[i*p : (i+1)*p] = [f_t[i], f_{t-1}[i], ..., f_{t-p+1}[i]]
|
|
451
|
+
full_state = np.zeros((T, r * factor_order), dtype=factors_np.dtype)
|
|
452
|
+
for t in range(T):
|
|
453
|
+
for i in range(r):
|
|
454
|
+
for lag in range(factor_order):
|
|
455
|
+
idx = t - lag
|
|
456
|
+
if idx >= 0:
|
|
457
|
+
full_state[t, i * factor_order + lag] = factors_np[idx, i]
|
|
458
|
+
else:
|
|
459
|
+
# Use f0 for negative indices
|
|
460
|
+
if f0 is not None and i < len(f0):
|
|
461
|
+
full_state[t, i * factor_order + lag] = f0[i]
|
|
462
|
+
|
|
463
|
+
return cls(
|
|
464
|
+
num_iter=num_iter,
|
|
465
|
+
loss_now=loss_now,
|
|
466
|
+
elbo=elbo,
|
|
467
|
+
converged=converged,
|
|
468
|
+
factors=factors,
|
|
469
|
+
innovations=innovations,
|
|
470
|
+
full_state=full_state,
|
|
471
|
+
factor_order=factor_order,
|
|
472
|
+
A=A,
|
|
473
|
+
B=B,
|
|
474
|
+
f0=f0
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
def apply_to_model(self, model: Any) -> None:
|
|
478
|
+
"""Apply this state to iVDFM model instance.
|
|
479
|
+
|
|
480
|
+
Parameters
|
|
481
|
+
----------
|
|
482
|
+
model : iVDFM
|
|
483
|
+
iVDFM model instance to update
|
|
484
|
+
"""
|
|
485
|
+
# Apply training state
|
|
486
|
+
if self.factors is not None:
|
|
487
|
+
model.factors = self.factors
|
|
488
|
+
if self.innovations is not None:
|
|
489
|
+
model.innovations = self.innovations
|
|
490
|
+
model._num_iter = self.num_iter
|
|
491
|
+
model.loss_now = self.loss_now
|
|
492
|
+
if self.elbo is not None:
|
|
493
|
+
model._elbo = self.elbo
|
|
494
|
+
model._converged = self.converged
|
|
495
|
+
|
|
496
|
+
# Apply SSM parameters if available
|
|
497
|
+
if self.A is not None and self.B is not None:
|
|
498
|
+
if hasattr(model, 'ssm') and model.ssm is not None:
|
|
499
|
+
ssm = model.ssm
|
|
500
|
+
if self.factor_order == 1:
|
|
501
|
+
if hasattr(ssm, 'A'):
|
|
502
|
+
ssm.A.data = torch.from_numpy(self.A).to(ssm.A.device)
|
|
503
|
+
else:
|
|
504
|
+
if hasattr(ssm, 'ar_coeffs'):
|
|
505
|
+
ssm.ar_coeffs.data = torch.from_numpy(self.A).to(ssm.ar_coeffs.device)
|
|
506
|
+
|
|
507
|
+
if hasattr(ssm, 'B'):
|
|
508
|
+
ssm.B.data = torch.from_numpy(self.B).to(ssm.B.device)
|
|
509
|
+
|
|
510
|
+
if self.f0 is not None and hasattr(ssm, 'f0'):
|
|
511
|
+
ssm.f0.data = torch.from_numpy(self.f0).to(ssm.f0.device)
|
|
329
512
|
DDFMStateSpaceParams = DDFMModelState
|
|
330
513
|
|
|
331
514
|
# Backward compatibility alias
|