dfm-python 0.5.53__py3-none-any.whl → 0.5.54__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 +3 -3
- dfm_python/config/adapter.py +17 -1
- dfm_python/config/constants.py +97 -11
- dfm_python/config/schema/model.py +125 -59
- dfm_python/config/schema/results.py +101 -1
- dfm_python/config/types.py +19 -4
- dfm_python/datamodule/base.py +61 -55
- dfm_python/datamodule/ddfm_dm.py +34 -49
- dfm_python/datamodule/dfm_dm.py +55 -28
- dfm_python/datamodule/kdfm_dm.py +9 -8
- dfm_python/dataset/process.py +63 -64
- dfm_python/decoder/mlp.py +12 -5
- dfm_python/encoder/pca.py +15 -17
- dfm_python/encoder/simple_encoder.py +80 -37
- dfm_python/functional/dfm_block.py +9 -8
- dfm_python/functional/em.py +211 -78
- dfm_python/functional/irf.py +25 -17
- dfm_python/models/base.py +144 -385
- dfm_python/models/ddfm.py +273 -270
- dfm_python/models/dfm.py +291 -196
- dfm_python/models/kdfm.py +344 -224
- dfm_python/numeric/builder.py +38 -82
- dfm_python/numeric/estimator.py +73 -299
- dfm_python/numeric/stability.py +128 -72
- dfm_python/numeric/tent.py +8 -3
- dfm_python/numeric/validator.py +81 -34
- dfm_python/ssm/companion.py +37 -24
- dfm_python/ssm/kalman.py +11 -9
- dfm_python/ssm/structural.py +1 -1
- dfm_python/trainer/__init__.py +12 -9
- dfm_python/trainer/ddfm.py +77 -67
- dfm_python/trainer/dfm.py +4 -3
- dfm_python/trainer/kdfm.py +5 -4
- dfm_python/utils/__init__.py +3 -4
- dfm_python/utils/common.py +96 -10
- dfm_python/utils/helper.py +32 -12
- dfm_python/utils/metric.py +14 -8
- dfm_python/utils/misc.py +6 -43
- dfm_python/utils/tensor_utils.py +14 -7
- dfm_python/utils/validation.py +32 -3
- {dfm_python-0.5.53.dist-info → dfm_python-0.5.54.dist-info}/METADATA +1 -1
- dfm_python-0.5.54.dist-info/RECORD +66 -0
- dfm_python-0.5.53.dist-info/RECORD +0 -66
- {dfm_python-0.5.53.dist-info → dfm_python-0.5.54.dist-info}/WHEEL +0 -0
- {dfm_python-0.5.53.dist-info → dfm_python-0.5.54.dist-info}/licenses/LICENSE +0 -0
dfm_python/__init__.py
CHANGED
|
@@ -40,7 +40,7 @@ Example (Standard Lightning Pattern):
|
|
|
40
40
|
>>> model.load_config('config/default.yaml')
|
|
41
41
|
>>>
|
|
42
42
|
>>> # Step 4: Create trainer and fit (standard Lightning pattern)
|
|
43
|
-
>>> trainer = DFMTrainer(max_epochs=100)
|
|
43
|
+
>>> trainer = DFMTrainer(max_epochs=100) # DEFAULT_MAX_EPOCHS
|
|
44
44
|
>>> trainer.fit(model, dm)
|
|
45
45
|
>>>
|
|
46
46
|
>>> # Step 5: Predict
|
|
@@ -55,7 +55,7 @@ Example (Standard Lightning Pattern):
|
|
|
55
55
|
>>> ddfm_model = DDFM(encoder_layers=[64, 32], num_factors=2)
|
|
56
56
|
>>> ddfm_model.load_config('config/default.yaml')
|
|
57
57
|
>>>
|
|
58
|
-
>>> trainer_ddfm = DDFMTrainer(max_epochs=100)
|
|
58
|
+
>>> trainer_ddfm = DDFMTrainer(max_epochs=100) # DEFAULT_MAX_EPOCHS
|
|
59
59
|
>>> trainer_ddfm.fit(ddfm_model, dm_ddfm)
|
|
60
60
|
>>> Xf, Zf = ddfm_model.predict(horizon=6)
|
|
61
61
|
|
|
@@ -65,7 +65,7 @@ Note: DFMConfig uses frequency dict to specify series (column names -> frequenci
|
|
|
65
65
|
For detailed documentation, see the README.md file and the tutorial notebooks/scripts.
|
|
66
66
|
"""
|
|
67
67
|
|
|
68
|
-
__version__ = "0.5.
|
|
68
|
+
__version__ = "0.5.54"
|
|
69
69
|
|
|
70
70
|
# ============================================================================
|
|
71
71
|
# PUBLIC API DEFINITION
|
dfm_python/config/adapter.py
CHANGED
|
@@ -28,7 +28,7 @@ from .constants import DEFAULT_CLOCK_FREQUENCY
|
|
|
28
28
|
# Error helper to reduce repetitive imports
|
|
29
29
|
def _get_config_error():
|
|
30
30
|
"""Get ConfigurationError (lazy import to avoid circular dependencies)."""
|
|
31
|
-
from
|
|
31
|
+
from dfm_python.utils.errors import ConfigurationError
|
|
32
32
|
return ConfigurationError
|
|
33
33
|
|
|
34
34
|
|
|
@@ -58,6 +58,22 @@ def _raise_config_error(message: str, details: Optional[str] = None) -> NoReturn
|
|
|
58
58
|
# Helper Functions for Config Parsing
|
|
59
59
|
# ============================================================================
|
|
60
60
|
|
|
61
|
+
def _is_dict_like(obj: Any) -> bool:
|
|
62
|
+
"""Check if object is dict-like (dict, OrderedDict, etc.).
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
obj : Any
|
|
67
|
+
Object to check
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
bool
|
|
72
|
+
True if object is dict-like, False otherwise
|
|
73
|
+
"""
|
|
74
|
+
return isinstance(obj, dict) or hasattr(obj, 'keys') and hasattr(obj, '__getitem__')
|
|
75
|
+
|
|
76
|
+
|
|
61
77
|
def _convert_series_to_frequency_dict(
|
|
62
78
|
series_data: Any,
|
|
63
79
|
default_clock: str = DEFAULT_CLOCK_FREQUENCY
|
dfm_python/config/constants.py
CHANGED
|
@@ -28,10 +28,15 @@ MIN_DIAGONAL_VARIANCE = 1e-8 # Minimum variance for diagonal elements
|
|
|
28
28
|
MIN_OBSERVATION_NOISE = 1e-4 # Minimum observation noise for measurement error (used in EM updates)
|
|
29
29
|
MIN_FACTOR_VARIANCE = 1e-10 # Minimum variance for factors
|
|
30
30
|
MIN_STD = 1e-8 # Minimum standard deviation
|
|
31
|
+
DEFAULT_VARIANCE_FALLBACK = 1.0 # Default variance fallback value for numerical stability
|
|
31
32
|
|
|
32
33
|
# Maximum eigenvalues
|
|
33
34
|
MAX_EIGENVALUE = 1e6 # Maximum eigenvalue cap
|
|
34
35
|
|
|
36
|
+
# Eigenvalue stability thresholds
|
|
37
|
+
DEFAULT_EIGENVALUE_MAX_MAGNITUDE = 1.0 # Default maximum eigenvalue magnitude for stability checks
|
|
38
|
+
DEFAULT_EIGENVALUE_WARN_THRESHOLD = 0.99 # Default warning threshold for near-unstable eigenvalues
|
|
39
|
+
|
|
35
40
|
# Matrix cleaning defaults
|
|
36
41
|
DEFAULT_CLEAN_NAN = 0.0 # Default value for NaN replacement in clean_matrix
|
|
37
42
|
DEFAULT_CLEAN_INF = MAX_EIGENVALUE # Default value for Inf replacement in clean_matrix (uses MAX_EIGENVALUE)
|
|
@@ -39,6 +44,12 @@ DEFAULT_CLEAN_INF = MAX_EIGENVALUE # Default value for Inf replacement in clean
|
|
|
39
44
|
# Identity matrix defaults
|
|
40
45
|
DEFAULT_IDENTITY_SCALE = 1.0 # Default scale for create_scaled_identity(n, 1.0)
|
|
41
46
|
DEFAULT_ZERO_VALUE = 0.0 # Default zero value for explicit zero assignments
|
|
47
|
+
DEFAULT_XAVIER_GAIN = 1.0 # Default gain for Xavier initialization
|
|
48
|
+
DEFAULT_OUTPUT_LAYER_GAIN = 0.1 # Default gain for output layer Xavier initialization (smaller for stability)
|
|
49
|
+
|
|
50
|
+
# Companion matrix initialization defaults
|
|
51
|
+
DEFAULT_INIT_SCALE = 0.01 # Default initialization scale for companion matrix B and C matrices
|
|
52
|
+
DEFAULT_KERNEL_INIT_SCALE = 0.1 # Default initialization scale for companion matrix coefficient matrices
|
|
42
53
|
|
|
43
54
|
# Regularization scales
|
|
44
55
|
DEFAULT_REGULARIZATION_SCALE = 1e-5 # Default ridge regularization scale
|
|
@@ -57,7 +68,6 @@ DEFAULT_MAX_ITER = 100 # Default maximum EM iterations (general)
|
|
|
57
68
|
DEFAULT_EM_MAX_ITER = 5000 # Default maximum EM iterations (DFM-specific)
|
|
58
69
|
DEFAULT_MAX_EPOCHS = 100 # Default maximum training epochs
|
|
59
70
|
DEFAULT_MAX_MCMC_ITER = 200 # Default maximum MCMC iterations
|
|
60
|
-
DEFAULT_EPOCHS_PER_ITER = 10 # Default epochs per MCMC iteration
|
|
61
71
|
|
|
62
72
|
# Batch size defaults
|
|
63
73
|
DEFAULT_BATCH_SIZE = 32 # Default batch size for neural networks
|
|
@@ -78,6 +88,10 @@ DEFAULT_LR_DECAY_RATE = 0.96 # Default exponential decay rate for learning rate
|
|
|
78
88
|
|
|
79
89
|
# Loss function defaults
|
|
80
90
|
DEFAULT_HUBER_DELTA = 1.0 # Default delta parameter for Huber loss
|
|
91
|
+
HUBER_QUADRATIC_COEFF = 0.5 # Quadratic coefficient for Huber loss (0.5 * a^2 term)
|
|
92
|
+
|
|
93
|
+
# Matrix computation defaults
|
|
94
|
+
SYMMETRY_AVERAGE_FACTOR = 0.5 # Averaging factor for symmetric matrix computation (0.5 * (M + M.T))
|
|
81
95
|
|
|
82
96
|
# Data clipping defaults
|
|
83
97
|
DEFAULT_DDFM_CLIP_RANGE_DEEP = 8.0 # Clipping range for deep networks (>2 layers)
|
|
@@ -86,11 +100,15 @@ DEFAULT_DDFM_CLIP_RANGE_SHALLOW = 10.0 # Clipping range for shallow networks (<
|
|
|
86
100
|
# Numerical stability for division
|
|
87
101
|
DEFAULT_EPSILON = 1e-8 # Default epsilon for division operations to prevent division by zero
|
|
88
102
|
|
|
103
|
+
# Random seed defaults
|
|
104
|
+
DEFAULT_SEED = 3 # Default random seed for reproducibility
|
|
105
|
+
|
|
89
106
|
# Structural identification defaults
|
|
90
107
|
DEFAULT_STRUCTURAL_REG_WEIGHT = 0.1 # Default weight for structural regularization loss
|
|
91
108
|
DEFAULT_STRUCTURAL_INIT_SCALE = 0.1 # Default initialization scale for structural matrices
|
|
92
109
|
DEFAULT_STRUCTURAL_DIAG_SCALE = 1.0 # Default diagonal scale for structural matrices (FIXED Iteration 7: was 0.1, caused near-singular S)
|
|
93
110
|
DEFAULT_CHOLESKY_EPS = 1e-6 # Default epsilon for Cholesky decomposition stability
|
|
111
|
+
CHOLESKY_LOG_DET_FACTOR = 2.0 # Factor for log determinant computation from Cholesky decomposition (log det = 2.0 * sum(log(diag(L))))
|
|
94
112
|
|
|
95
113
|
# ============================================================================
|
|
96
114
|
# Network Architecture Defaults
|
|
@@ -112,17 +130,27 @@ DEFAULT_START_DATE = datetime(2000, 1, 1)
|
|
|
112
130
|
|
|
113
131
|
# Default window size for DDFM
|
|
114
132
|
DEFAULT_WINDOW_SIZE = 100
|
|
133
|
+
DEFAULT_TENT_KERNEL_SIZE = 5 # Default tent kernel size for slower-frequency series aggregation
|
|
115
134
|
# Note: DEFAULT_BATCH_SIZE is defined above (line 52) as 32 for general neural networks
|
|
116
135
|
# Use DEFAULT_DDFM_BATCH_SIZE (100) for DDFM-specific batch size
|
|
117
136
|
|
|
118
137
|
# Warning/display limits
|
|
119
138
|
MAX_WARNING_ITEMS = 5 # Maximum number of items to show in warning messages
|
|
139
|
+
MAX_ERROR_ITEMS = 20 # Maximum number of items to show in error message details
|
|
120
140
|
|
|
121
141
|
# Minimum observations
|
|
122
142
|
DEFAULT_MIN_OBS = 5 # Default minimum observations for estimation
|
|
123
143
|
DEFAULT_MIN_OBS_IDIO = 5 # Default minimum observations for idio estimation
|
|
124
144
|
DEFAULT_MIN_OBS_VAR = 7 # Minimum observations for VAR estimation (order + 5)
|
|
125
145
|
|
|
146
|
+
# Dimension validation bounds
|
|
147
|
+
MIN_TIME_STEPS = 1 # Minimum number of time steps (T) required for data
|
|
148
|
+
MIN_VARIABLES = 1 # Minimum number of variables (N) required for data
|
|
149
|
+
MIN_DDFM_TIME_STEPS = 2 # Minimum number of time steps (T) required for DDFM training (DDFM-specific, different from general MIN_TIME_STEPS=1)
|
|
150
|
+
MIN_DDFM_DATASET_SIZE_WARNING = 10 # Minimum dataset size (T) below which DDFM denoising training warns about potential instability
|
|
151
|
+
MIN_ITER_FOR_DELTA_COMPUTATION = 1 # Minimum iteration count required before computing delta (MSE change) in DDFM denoising training
|
|
152
|
+
MIN_EPS_SHAPE_FOR_IDIO = 1 # Minimum eps shape dimension required for idiosyncratic component processing in DDFM
|
|
153
|
+
|
|
126
154
|
# Idiosyncratic component defaults
|
|
127
155
|
DEFAULT_IDIO_STD = 0.1 # Default idiosyncratic standard deviation (when estimation fails)
|
|
128
156
|
DEFAULT_IDIO_RHO0 = 0.1 # Default initial AR coefficient for idiosyncratic components
|
|
@@ -130,9 +158,7 @@ DEFAULT_AR_COEF = 0.5 # Default AR coefficient for initialization (conservative
|
|
|
130
158
|
DEFAULT_PROCESS_NOISE = 0.1 # Default process noise for initialization
|
|
131
159
|
DEFAULT_TRANSITION_COEF = 0.9 # Default transition coefficient for DFM initialization
|
|
132
160
|
|
|
133
|
-
# Standardization defaults
|
|
134
|
-
DEFAULT_WX_VALUE = 1.0 # Default standard deviation when Wx is missing (unit scale)
|
|
135
|
-
DEFAULT_MX_VALUE = 0.0 # Default mean when Mx is missing (zero-centered)
|
|
161
|
+
# Standardization defaults removed - now using sklearn scalers directly
|
|
136
162
|
|
|
137
163
|
# VAR stability and clipping
|
|
138
164
|
VAR_STABILITY_THRESHOLD = 0.99 # Maximum eigenvalue for VAR stability
|
|
@@ -153,18 +179,20 @@ DEFAULT_DAMPING_FACTOR = 0.5 # Default damping factor for parameter updates (us
|
|
|
153
179
|
|
|
154
180
|
# Numerical thresholds
|
|
155
181
|
MIN_CONDITION_NUMBER = 1e-12 # Minimum value for condition number calculations
|
|
182
|
+
MAX_CONDITION_NUMBER = 1e8 # Maximum condition number threshold for regularization (ill-conditioned matrix threshold)
|
|
156
183
|
|
|
157
184
|
# ============================================================================
|
|
158
185
|
# Display and Logging Defaults
|
|
159
186
|
# ============================================================================
|
|
160
187
|
|
|
161
188
|
DEFAULT_DISP = 10 # Default display interval for progress
|
|
189
|
+
DEFAULT_LOG_INTERVAL = 10 # Default logging interval divisor (log every num_epochs // DEFAULT_LOG_INTERVAL epochs)
|
|
190
|
+
DEFAULT_PROGRESS_LOG_INTERVAL = 5 # Default progress logging interval for EM algorithm
|
|
162
191
|
|
|
163
192
|
# ============================================================================
|
|
164
193
|
# Precision Defaults
|
|
165
194
|
# ============================================================================
|
|
166
195
|
|
|
167
|
-
DEFAULT_PRECISION = 32 # Default training precision
|
|
168
196
|
DEFAULT_DTYPE = np.float32 # Default numpy dtype for arrays
|
|
169
197
|
|
|
170
198
|
# PyTorch dtype (matches DEFAULT_DTYPE)
|
|
@@ -180,6 +208,34 @@ except ImportError:
|
|
|
180
208
|
|
|
181
209
|
DEFAULT_IRF_HORIZON = 20 # Default horizon for IRF computation
|
|
182
210
|
|
|
211
|
+
# ============================================================================
|
|
212
|
+
# Forecast Defaults
|
|
213
|
+
# ============================================================================
|
|
214
|
+
|
|
215
|
+
DEFAULT_FORECAST_HORIZON = 6 # Default horizon for forecast computation
|
|
216
|
+
|
|
217
|
+
# ============================================================================
|
|
218
|
+
# Error Handling Constants
|
|
219
|
+
# ============================================================================
|
|
220
|
+
|
|
221
|
+
# Common exception types for computation error handling
|
|
222
|
+
# Used to consolidate duplicate exception handling patterns across models
|
|
223
|
+
COMPUTATION_ERROR_TYPES = (RuntimeError, ValueError, TypeError, AttributeError, KeyError)
|
|
224
|
+
|
|
225
|
+
# ============================================================================
|
|
226
|
+
# KDFM Defaults
|
|
227
|
+
# ============================================================================
|
|
228
|
+
|
|
229
|
+
DEFAULT_KDFM_AR_ORDER = 1 # Default AR order (VAR lag order p) for KDFM
|
|
230
|
+
DEFAULT_KDFM_MA_ORDER = 0 # Default MA order (MA lag order q) for KDFM (0 = pure VAR)
|
|
231
|
+
|
|
232
|
+
# ============================================================================
|
|
233
|
+
# Tutorial Defaults
|
|
234
|
+
# ============================================================================
|
|
235
|
+
|
|
236
|
+
TUTORIAL_MAX_PERIODS = 100 # Default maximum periods for tutorial data (reduced for faster execution)
|
|
237
|
+
TUTORIAL_MAX_EPOCHS = 10 # Default maximum epochs for tutorial training (reduced for faster execution)
|
|
238
|
+
|
|
183
239
|
# ============================================================================
|
|
184
240
|
# Matrix Type Constants
|
|
185
241
|
# ============================================================================
|
|
@@ -248,6 +304,8 @@ TENT_WEIGHTS_LOOKUP: Dict[Tuple[str, str], np.ndarray] = {
|
|
|
248
304
|
('a', 'm'): np.array([1, 2, 3, 4, 5, 4, 3, 2, 1]), # 9 periods: annual -> monthly
|
|
249
305
|
('m', 'w'): np.array([1, 2, 3, 2, 1]), # 5 periods: monthly -> weekly
|
|
250
306
|
('q', 'w'): np.array([1, 2, 3, 4, 5, 4, 3, 2, 1]), # 9 periods: quarterly -> weekly
|
|
307
|
+
('sa', 'w'): np.array([1, 2, 3, 4, 3, 2, 1]), # 7 periods: semi-annual -> weekly
|
|
308
|
+
('a', 'w'): np.array([1, 2, 3, 4, 5, 4, 3, 2, 1]), # 9 periods: annual -> weekly
|
|
251
309
|
('sa', 'q'): np.array([1, 2, 1]), # 3 periods: semi-annual -> quarterly
|
|
252
310
|
('a', 'q'): np.array([1, 2, 3, 2, 1]), # 5 periods: annual -> quarterly
|
|
253
311
|
('a', 'sa'): np.array([1, 2, 1]), # 3 periods: annual -> semi-annual
|
|
@@ -271,19 +329,26 @@ __all__ = [
|
|
|
271
329
|
'MIN_FACTOR_VARIANCE',
|
|
272
330
|
'MIN_STD',
|
|
273
331
|
'MAX_EIGENVALUE',
|
|
332
|
+
'DEFAULT_EIGENVALUE_MAX_MAGNITUDE',
|
|
333
|
+
'DEFAULT_EIGENVALUE_WARN_THRESHOLD',
|
|
274
334
|
'DEFAULT_CLEAN_NAN',
|
|
275
335
|
'DEFAULT_CLEAN_INF',
|
|
276
336
|
'DEFAULT_IDENTITY_SCALE',
|
|
277
337
|
'DEFAULT_ZERO_VALUE',
|
|
338
|
+
'DEFAULT_XAVIER_GAIN',
|
|
339
|
+
'DEFAULT_OUTPUT_LAYER_GAIN',
|
|
340
|
+
'DEFAULT_INIT_SCALE',
|
|
341
|
+
'DEFAULT_KERNEL_INIT_SCALE',
|
|
278
342
|
'DEFAULT_REGULARIZATION_SCALE',
|
|
279
343
|
'DEFAULT_REGULARIZATION',
|
|
280
344
|
'DEFAULT_CLIP_THRESHOLD',
|
|
281
345
|
'DEFAULT_DATA_CLIP_THRESHOLD',
|
|
346
|
+
'MIN_CONDITION_NUMBER',
|
|
347
|
+
'MAX_CONDITION_NUMBER',
|
|
282
348
|
# Training
|
|
283
349
|
'DEFAULT_MAX_ITER',
|
|
284
350
|
'DEFAULT_MAX_EPOCHS',
|
|
285
351
|
'DEFAULT_MAX_MCMC_ITER',
|
|
286
|
-
'DEFAULT_EPOCHS_PER_ITER',
|
|
287
352
|
'DEFAULT_BATCH_SIZE', # General neural network default (32)
|
|
288
353
|
'DEFAULT_DDFM_BATCH_SIZE', # DDFM-specific default (100)
|
|
289
354
|
'DEFAULT_LEARNING_RATE',
|
|
@@ -292,9 +357,11 @@ __all__ = [
|
|
|
292
357
|
'DEFAULT_WEIGHT_DECAY',
|
|
293
358
|
'DEFAULT_LR_DECAY_RATE',
|
|
294
359
|
'DEFAULT_HUBER_DELTA',
|
|
360
|
+
'HUBER_QUADRATIC_COEFF',
|
|
295
361
|
'DEFAULT_DDFM_CLIP_RANGE_DEEP',
|
|
296
362
|
'DEFAULT_DDFM_CLIP_RANGE_SHALLOW',
|
|
297
363
|
'DEFAULT_EPSILON',
|
|
364
|
+
'DEFAULT_SEED',
|
|
298
365
|
# Structural identification
|
|
299
366
|
'DEFAULT_STRUCTURAL_REG_WEIGHT',
|
|
300
367
|
'DEFAULT_STRUCTURAL_INIT_SCALE',
|
|
@@ -311,6 +378,13 @@ __all__ = [
|
|
|
311
378
|
'DEFAULT_MIN_OBS',
|
|
312
379
|
'DEFAULT_MIN_OBS_IDIO',
|
|
313
380
|
'DEFAULT_MIN_OBS_VAR',
|
|
381
|
+
# Dimension validation
|
|
382
|
+
'MIN_TIME_STEPS',
|
|
383
|
+
'MIN_VARIABLES',
|
|
384
|
+
'MIN_DDFM_TIME_STEPS',
|
|
385
|
+
'MIN_DDFM_DATASET_SIZE_WARNING',
|
|
386
|
+
'MIN_ITER_FOR_DELTA_COMPUTATION',
|
|
387
|
+
'MIN_EPS_SHAPE_FOR_IDIO',
|
|
314
388
|
'DEFAULT_IDIO_STD',
|
|
315
389
|
'DEFAULT_IDIO_RHO0',
|
|
316
390
|
'DEFAULT_AR_COEF',
|
|
@@ -325,19 +399,28 @@ __all__ = [
|
|
|
325
399
|
'DEFAULT_SLOWER_FREQ_VARIANCE_DENOMINATOR',
|
|
326
400
|
'DEFAULT_EXTREME_FORECAST_THRESHOLD',
|
|
327
401
|
'DEFAULT_MAX_VARIANCE',
|
|
328
|
-
# Standardization
|
|
329
|
-
'DEFAULT_WX_VALUE',
|
|
330
|
-
'DEFAULT_MX_VALUE',
|
|
331
402
|
# Correlation and validation
|
|
332
403
|
'PERFECT_CORR_THRESHOLD',
|
|
333
404
|
'HIGH_CORR_THRESHOLD',
|
|
334
405
|
'DEFAULT_DAMPING_FACTOR',
|
|
335
406
|
# Display
|
|
336
407
|
'DEFAULT_DISP',
|
|
337
|
-
|
|
338
|
-
'
|
|
408
|
+
'DEFAULT_LOG_INTERVAL',
|
|
409
|
+
'DEFAULT_PROGRESS_LOG_INTERVAL',
|
|
410
|
+
'DEFAULT_TENT_KERNEL_SIZE',
|
|
411
|
+
'MAX_ERROR_ITEMS',
|
|
339
412
|
# IRF
|
|
340
413
|
'DEFAULT_IRF_HORIZON',
|
|
414
|
+
# Forecast
|
|
415
|
+
'DEFAULT_FORECAST_HORIZON',
|
|
416
|
+
# Error handling
|
|
417
|
+
'COMPUTATION_ERROR_TYPES',
|
|
418
|
+
# KDFM defaults
|
|
419
|
+
'DEFAULT_KDFM_AR_ORDER',
|
|
420
|
+
'DEFAULT_KDFM_MA_ORDER',
|
|
421
|
+
# Tutorial defaults
|
|
422
|
+
'TUTORIAL_MAX_PERIODS',
|
|
423
|
+
'TUTORIAL_MAX_EPOCHS',
|
|
341
424
|
# Matrix types
|
|
342
425
|
'MATRIX_TYPE_GENERAL',
|
|
343
426
|
'MATRIX_TYPE_COVARIANCE',
|
|
@@ -345,6 +428,9 @@ __all__ = [
|
|
|
345
428
|
'MATRIX_TYPE_LOADING',
|
|
346
429
|
# Log-determinant
|
|
347
430
|
'MAX_LOG_DETERMINANT',
|
|
431
|
+
'CHOLESKY_LOG_DET_FACTOR',
|
|
432
|
+
# Matrix computation
|
|
433
|
+
'SYMMETRY_AVERAGE_FACTOR',
|
|
348
434
|
# Default frequency
|
|
349
435
|
'DEFAULT_CLOCK_FREQUENCY',
|
|
350
436
|
'DEFAULT_HIERARCHY_VALUE',
|
|
@@ -68,6 +68,10 @@ from ..constants import (
|
|
|
68
68
|
DEFAULT_NAN_METHOD,
|
|
69
69
|
DEFAULT_NAN_K,
|
|
70
70
|
DEFAULT_CLOCK_FREQUENCY,
|
|
71
|
+
DEFAULT_KDFM_AR_ORDER,
|
|
72
|
+
DEFAULT_KDFM_MA_ORDER,
|
|
73
|
+
FREQUENCY_HIERARCHY,
|
|
74
|
+
DEFAULT_HIERARCHY_VALUE,
|
|
71
75
|
)
|
|
72
76
|
|
|
73
77
|
|
|
@@ -124,7 +128,7 @@ class BaseModelConfig:
|
|
|
124
128
|
# Shared Data Handling Parameters
|
|
125
129
|
# ========================================================================
|
|
126
130
|
clock: str = 'm' # Required: Base frequency for latent factors (global clock): 'd', 'w', 'm', 'q', 'sa', 'a' (defaults to 'm' for monthly)
|
|
127
|
-
target_scaler: Optional[ScalerType] = None #
|
|
131
|
+
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.
|
|
128
132
|
# Note: nan_method and nan_k are internal constants (DEFAULT_NAN_METHOD, DEFAULT_NAN_K) used during initialization only
|
|
129
133
|
|
|
130
134
|
def __post_init__(self):
|
|
@@ -254,47 +258,38 @@ class DFMConfig(BaseModelConfig):
|
|
|
254
258
|
# ========================================================================
|
|
255
259
|
# EM Algorithm Parameters (HOW - controls the algorithm)
|
|
256
260
|
# ========================================================================
|
|
257
|
-
|
|
261
|
+
# Note: ar_lag removed - factors always use AR(1) dynamics (simplified)
|
|
258
262
|
threshold: float = DEFAULT_EM_THRESHOLD # EM convergence threshold
|
|
259
263
|
max_iter: int = DEFAULT_EM_MAX_ITER # Maximum EM iterations
|
|
260
264
|
|
|
261
265
|
# ========================================================================
|
|
262
266
|
# Numerical Stability Parameters (transparent and configurable)
|
|
263
267
|
# ========================================================================
|
|
264
|
-
# AR Coefficient Clipping
|
|
265
|
-
|
|
266
|
-
ar_clip_min: float = AR_CLIP_MIN # Minimum AR coefficient (must be > -1 for stationarity)
|
|
267
|
-
ar_clip_max: float = AR_CLIP_MAX # Maximum AR coefficient (must be < 1 for stationarity)
|
|
268
|
-
warn_on_ar_clip: bool = True # Warn when AR coefficients are clipped (indicates near-unit root)
|
|
268
|
+
# AR Coefficient Clipping: If provided (not None), automatically enables clipping and always warns
|
|
269
|
+
ar_clip: Optional[Dict[str, float]] = None # {"min": float, "max": float} - AR coefficient clipping bounds. If None, no clipping. If provided, clipping enabled and warnings always shown.
|
|
269
270
|
|
|
270
|
-
# Data Value Clipping
|
|
271
|
-
|
|
272
|
-
data_clip_threshold: float = 100.0 # Clip values beyond this many standard deviations
|
|
273
|
-
warn_on_data_clip: bool = True # Warn when data values are clipped (indicates outliers)
|
|
271
|
+
# Data Value Clipping: If provided (not None), automatically enables clipping and always warns
|
|
272
|
+
data_clip: Optional[float] = None # Clip values beyond this many standard deviations. If None, no clipping. If provided, clipping enabled and warnings always shown.
|
|
274
273
|
|
|
275
|
-
# Regularization
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
# Damped Updates
|
|
283
|
-
use_damped_updates: bool = True # Enable damped updates when likelihood decreases
|
|
284
|
-
damping_factor: float = 0.8 # Damping factor (0.8 = 80% new, 20% old)
|
|
285
|
-
warn_on_damped_update: bool = True # Warn when damped updates are used
|
|
286
|
-
|
|
287
|
-
# Idiosyncratic Component Augmentation
|
|
288
|
-
augment_idio: bool = True # Enable state augmentation with idiosyncratic components (default: True)
|
|
289
|
-
augment_idio_slow: bool = True # Enable tent-length chains for slower-frequency series (default: True)
|
|
274
|
+
# Regularization: If provided (not None), automatically enables regularization and always warns
|
|
275
|
+
regularization: Optional[Dict[str, float]] = None # {"scale": float, "min_eigenvalue": float, "max_eigenvalue": float} - Regularization parameters. If None, no regularization. If provided, regularization enabled and warnings always shown.
|
|
276
|
+
|
|
277
|
+
# Damped Updates: If provided (not None), automatically enables damping and always warns
|
|
278
|
+
damping_factor: Optional[float] = None # Damping factor (0.8 = 80% new, 20% old). If None, no damping. If provided, damping enabled and warnings always shown.
|
|
279
|
+
|
|
280
|
+
# Idiosyncratic Component Parameters (auto-detected from frequencies)
|
|
290
281
|
idio_rho0: float = DEFAULT_IDIO_RHO0 # Initial AR coefficient for idiosyncratic components (default: 0.1)
|
|
291
|
-
idio_min_var: float =
|
|
282
|
+
idio_min_var: float = MIN_DIAGONAL_VARIANCE # Minimum variance for idiosyncratic innovation covariance (defaults to MIN_DIAGONAL_VARIANCE)
|
|
283
|
+
# Note: augment_idio and augment_idio_slow are auto-detected from frequency configuration
|
|
284
|
+
# - If all series use clock frequency: augment_idio=False, augment_idio_slow=False
|
|
285
|
+
# - If mixed frequencies detected: augment_idio=True, augment_idio_slow=True (auto-enabled)
|
|
292
286
|
|
|
293
287
|
def __post_init__(self):
|
|
294
288
|
"""Validate blocks structure and derive block properties."""
|
|
295
289
|
super().__post_init__()
|
|
296
290
|
|
|
297
291
|
from ...config.adapter import _raise_config_error
|
|
292
|
+
from ..constants import FREQUENCY_HIERARCHY, DEFAULT_HIERARCHY_VALUE
|
|
298
293
|
|
|
299
294
|
if not self.blocks:
|
|
300
295
|
_raise_config_error("DFM configuration must contain at least one block.")
|
|
@@ -327,6 +322,67 @@ class DFMConfig(BaseModelConfig):
|
|
|
327
322
|
from ...config.adapter import _raise_config_error
|
|
328
323
|
if any(f < 1 for f in self.factors_per_block):
|
|
329
324
|
_raise_config_error("factors_per_block must contain positive integers >= 1")
|
|
325
|
+
|
|
326
|
+
# Auto-detect mixed frequencies and set augment_idio/augment_idio_slow
|
|
327
|
+
# If frequency dict exists, check if all frequencies match clock
|
|
328
|
+
if self.frequency is not None and len(self.frequency) > 0:
|
|
329
|
+
frequencies = list(self.frequency.values())
|
|
330
|
+
clock_hierarchy = FREQUENCY_HIERARCHY.get(self.clock, DEFAULT_HIERARCHY_VALUE)
|
|
331
|
+
is_mixed_freq = any(
|
|
332
|
+
FREQUENCY_HIERARCHY.get(freq, DEFAULT_HIERARCHY_VALUE) != clock_hierarchy
|
|
333
|
+
for freq in frequencies
|
|
334
|
+
)
|
|
335
|
+
# Auto-set augment_idio and augment_idio_slow based on frequency detection
|
|
336
|
+
# Store as internal attributes (not in __init__ signature)
|
|
337
|
+
object.__setattr__(self, '_augment_idio', is_mixed_freq)
|
|
338
|
+
object.__setattr__(self, '_augment_idio_slow', is_mixed_freq)
|
|
339
|
+
else:
|
|
340
|
+
# No frequency info yet - will be auto-detected when data is loaded
|
|
341
|
+
object.__setattr__(self, '_augment_idio', False)
|
|
342
|
+
object.__setattr__(self, '_augment_idio_slow', False)
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def augment_idio(self) -> bool:
|
|
346
|
+
"""Auto-detected: True if mixed frequencies detected, False if single frequency."""
|
|
347
|
+
return getattr(self, '_augment_idio', False)
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def augment_idio_slow(self) -> bool:
|
|
351
|
+
"""Auto-detected: True if mixed frequencies detected, False if single frequency."""
|
|
352
|
+
return getattr(self, '_augment_idio_slow', False)
|
|
353
|
+
|
|
354
|
+
def _update_idio_flags_from_frequencies(self, frequencies: List[str]) -> None:
|
|
355
|
+
"""Update augment_idio flags based on detected frequencies (called when data is loaded)."""
|
|
356
|
+
from ..constants import FREQUENCY_HIERARCHY, DEFAULT_HIERARCHY_VALUE
|
|
357
|
+
clock_hierarchy = FREQUENCY_HIERARCHY.get(self.clock, DEFAULT_HIERARCHY_VALUE)
|
|
358
|
+
is_mixed_freq = any(
|
|
359
|
+
FREQUENCY_HIERARCHY.get(freq, DEFAULT_HIERARCHY_VALUE) != clock_hierarchy
|
|
360
|
+
for freq in frequencies
|
|
361
|
+
)
|
|
362
|
+
object.__setattr__(self, '_augment_idio', is_mixed_freq)
|
|
363
|
+
object.__setattr__(self, '_augment_idio_slow', is_mixed_freq)
|
|
364
|
+
|
|
365
|
+
def to_em_config(self) -> 'EMConfig':
|
|
366
|
+
"""Create EMConfig from DFMConfig consolidated parameters."""
|
|
367
|
+
from ...functional.em import EMConfig
|
|
368
|
+
from ..constants import DEFAULT_REGULARIZATION, VAR_STABILITY_THRESHOLD
|
|
369
|
+
|
|
370
|
+
# Extract regularization parameters directly from dict
|
|
371
|
+
if self.regularization is not None and isinstance(self.regularization, dict):
|
|
372
|
+
reg_scale = self.regularization.get('scale', DEFAULT_REGULARIZATION_SCALE)
|
|
373
|
+
min_eigenval = self.regularization.get('min_eigenvalue', MIN_EIGENVALUE)
|
|
374
|
+
max_eigenval = self.regularization.get('max_eigenvalue', VAR_STABILITY_THRESHOLD)
|
|
375
|
+
else:
|
|
376
|
+
reg_scale = DEFAULT_REGULARIZATION
|
|
377
|
+
min_eigenval = MIN_EIGENVALUE
|
|
378
|
+
max_eigenval = VAR_STABILITY_THRESHOLD
|
|
379
|
+
|
|
380
|
+
return EMConfig(
|
|
381
|
+
regularization=reg_scale,
|
|
382
|
+
min_norm=min_eigenval,
|
|
383
|
+
max_eigenval=max_eigenval,
|
|
384
|
+
# Other parameters use defaults from EMConfig
|
|
385
|
+
)
|
|
330
386
|
|
|
331
387
|
def get_blocks_array(self, columns: Optional[List[str]] = None) -> np.ndarray:
|
|
332
388
|
"""Get blocks as numpy array (N x B) where N is number of series and B is number of blocks.
|
|
@@ -357,38 +413,43 @@ class DFMConfig(BaseModelConfig):
|
|
|
357
413
|
return self._cached_blocks
|
|
358
414
|
|
|
359
415
|
@classmethod
|
|
360
|
-
def _extract_dfm_params(cls, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
361
|
-
"""Extract DFM-specific parameters from config dict.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
'
|
|
377
|
-
'
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
416
|
+
def _extract_dfm_params(cls, data: Dict[str, Any], base_params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
417
|
+
"""Extract DFM-specific parameters from config dict.
|
|
418
|
+
|
|
419
|
+
Parameters
|
|
420
|
+
----------
|
|
421
|
+
data : Dict[str, Any]
|
|
422
|
+
Config dictionary
|
|
423
|
+
base_params : Dict[str, Any], optional
|
|
424
|
+
Pre-extracted base parameters. If provided, avoids duplicate extraction.
|
|
425
|
+
If None, extracts base params internally.
|
|
426
|
+
"""
|
|
427
|
+
if base_params is None:
|
|
428
|
+
base_params = cls._extract_base(data)
|
|
429
|
+
|
|
430
|
+
# Extract consolidated parameters (new format only)
|
|
431
|
+
dfm_params = {
|
|
432
|
+
'threshold': data.get('threshold', DEFAULT_EM_THRESHOLD),
|
|
433
|
+
'max_iter': data.get('max_iter', DEFAULT_EM_MAX_ITER),
|
|
434
|
+
'idio_rho0': data.get('idio_rho0', DEFAULT_IDIO_RHO0),
|
|
435
|
+
'idio_min_var': data.get('idio_min_var', MIN_DIAGONAL_VARIANCE),
|
|
436
|
+
'ar_clip': data.get('ar_clip', None),
|
|
437
|
+
'data_clip': data.get('data_clip', None),
|
|
438
|
+
'regularization': data.get('regularization', None),
|
|
439
|
+
'damping_factor': data.get('damping_factor', None),
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
result = base_params.copy()
|
|
443
|
+
result.update(dfm_params)
|
|
444
|
+
return result
|
|
381
445
|
|
|
382
446
|
@classmethod
|
|
383
447
|
def from_dict(cls, data: Dict[str, Any]) -> Union['DFMConfig', 'DDFMConfig']:
|
|
384
448
|
"""Create DFMConfig or DDFMConfig from dictionary.
|
|
385
449
|
|
|
386
|
-
|
|
387
|
-
1. New format: {'frequency': {'column_name': 'frequency'}, ...}
|
|
388
|
-
2. Legacy format (Hydra): {'series': {'series_id': {...}}, 'blocks': {...}}
|
|
389
|
-
3. Legacy format (list): {'series': [{'series_id': ..., 'frequency': ...}], ...}
|
|
450
|
+
Expected format: {'frequency': {'column_name': 'frequency'}, 'blocks': {...}, ...}
|
|
390
451
|
|
|
391
|
-
Also accepts estimation parameters:
|
|
452
|
+
Also accepts estimation parameters: threshold, max_iter, etc.
|
|
392
453
|
"""
|
|
393
454
|
from ...config.adapter import detect_config_type, MODEL_TYPE_DDFM, _normalize_blocks_dict
|
|
394
455
|
from ...utils.errors import ConfigurationError
|
|
@@ -411,7 +472,9 @@ class DFMConfig(BaseModelConfig):
|
|
|
411
472
|
_raise_config_error(f"blocks must be a dict, got {type(blocks_dict)}")
|
|
412
473
|
|
|
413
474
|
blocks_dict_normalized = _normalize_blocks_dict(blocks_dict)
|
|
414
|
-
|
|
475
|
+
# Pass base_params to _extract_dfm_params to avoid duplicate extraction
|
|
476
|
+
dfm_params = DFMConfig._extract_dfm_params(data, base_params=base_params)
|
|
477
|
+
return DFMConfig(blocks=blocks_dict_normalized, **dfm_params)
|
|
415
478
|
|
|
416
479
|
|
|
417
480
|
@dataclass
|
|
@@ -438,7 +501,7 @@ class DDFMConfig(BaseModelConfig):
|
|
|
438
501
|
learning_rate: float = 0.001 # Learning rate for Adam optimizer (default: 0.001)
|
|
439
502
|
epochs: int = 100 # Number of training epochs (default: 100)
|
|
440
503
|
batch_size: int = 100 # Batch size for training (default: 100 to match original DDFM)
|
|
441
|
-
|
|
504
|
+
# Note: factor_order removed - factors always use AR(1) dynamics (simplified)
|
|
442
505
|
use_idiosyncratic: bool = True # Model idio components with AR(1) dynamics (default: True)
|
|
443
506
|
min_obs_idio: int = 5 # Minimum observations for idio AR(1) estimation (default: 5)
|
|
444
507
|
|
|
@@ -465,7 +528,6 @@ class DDFMConfig(BaseModelConfig):
|
|
|
465
528
|
'learning_rate': DEFAULT_LEARNING_RATE,
|
|
466
529
|
'epochs': DEFAULT_MAX_EPOCHS,
|
|
467
530
|
'batch_size': DEFAULT_DDFM_BATCH_SIZE,
|
|
468
|
-
'factor_order': 1,
|
|
469
531
|
'use_idiosyncratic': True,
|
|
470
532
|
'min_obs_idio': DEFAULT_MIN_OBS_IDIO,
|
|
471
533
|
'max_iter': DEFAULT_MAX_MCMC_ITER,
|
|
@@ -482,7 +544,11 @@ class DDFMConfig(BaseModelConfig):
|
|
|
482
544
|
result = DFMConfig.from_dict(data)
|
|
483
545
|
if isinstance(result, DDFMConfig):
|
|
484
546
|
return result
|
|
485
|
-
|
|
547
|
+
from ..utils.errors import ConfigurationError
|
|
548
|
+
raise ConfigurationError(
|
|
549
|
+
"Expected DDFMConfig but got DFMConfig",
|
|
550
|
+
details=f"Result type: {type(result).__name__}, expected: DDFMConfig"
|
|
551
|
+
)
|
|
486
552
|
|
|
487
553
|
|
|
488
554
|
@dataclass
|
|
@@ -493,8 +559,8 @@ class KDFMConfig(BaseModelConfig):
|
|
|
493
559
|
It inherits from BaseModelConfig and adds KDFM-specific parameters.
|
|
494
560
|
"""
|
|
495
561
|
# VARMA parameters
|
|
496
|
-
ar_order: int =
|
|
497
|
-
ma_order: int =
|
|
562
|
+
ar_order: int = DEFAULT_KDFM_AR_ORDER # VAR order p
|
|
563
|
+
ma_order: int = DEFAULT_KDFM_MA_ORDER # MA order q (0 = pure VAR)
|
|
498
564
|
|
|
499
565
|
# Structural identification
|
|
500
566
|
structural_method: str = 'cholesky' # 'cholesky', 'full', 'low_rank'
|