dfm-python 0.5.56__tar.gz → 0.5.57__tar.gz

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.
Files changed (142) hide show
  1. {dfm_python-0.5.56 → dfm_python-0.5.57}/PKG-INFO +4 -5
  2. {dfm_python-0.5.56 → dfm_python-0.5.57}/README.md +3 -3
  3. {dfm_python-0.5.56 → dfm_python-0.5.57}/config/ddfm_macro.yaml +22 -21
  4. {dfm_python-0.5.56 → dfm_python-0.5.57}/config/dfm_macro.yaml +24 -22
  5. {dfm_python-0.5.56 → dfm_python-0.5.57}/pyproject.toml +2 -1
  6. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/config/__init__.py +3 -10
  7. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/config/adapter.py +63 -7
  8. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/config/constants.py +5 -4
  9. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/config/schema/__init__.py +2 -6
  10. dfm_python-0.5.57/src/dfm_python/config/schema/block.py +122 -0
  11. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/config/schema/model.py +11 -2
  12. dfm_python-0.5.57/src/dfm_python/config/schema/params.py +279 -0
  13. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/config/schema/results.py +8 -74
  14. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/config/types.py +3 -11
  15. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/dataset/ddfm_dataset.py +35 -13
  16. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/dataset/dfm_dataset.py +9 -1
  17. dfm_python-0.5.57/src/dfm_python/functional/__init__.py +11 -0
  18. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/functional/dfm_block.py +65 -14
  19. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/functional/em.py +580 -223
  20. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/logger/__init__.py +6 -12
  21. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/logger/inference_logger.py +3 -17
  22. dfm_python-0.5.57/src/dfm_python/logger/logger.py +271 -0
  23. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/logger/train_logger.py +26 -34
  24. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/models/__init__.py +2 -2
  25. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/models/base.py +8 -29
  26. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/models/ddfm.py +103 -3
  27. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/models/dfm.py +486 -219
  28. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/numeric/estimator.py +146 -26
  29. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/numeric/stability.py +55 -151
  30. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/numeric/tent.py +16 -6
  31. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/numeric/validator.py +1 -2
  32. dfm_python-0.5.57/src/dfm_python/ssm/__init__.py +29 -0
  33. dfm_python-0.5.57/src/dfm_python/ssm/kalman.py +564 -0
  34. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/utils/checkpoint.py +1 -1
  35. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/utils/misc.py +4 -10
  36. dfm_python-0.5.57/src/dfm_python/utils/validation.py +54 -0
  37. dfm_python-0.5.57/src/test/config/schema/test_model.py +61 -0
  38. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/config/schema/test_results.py +41 -58
  39. dfm_python-0.5.57/src/test/config/test_adapter.py +231 -0
  40. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/functional/test_dfm_block.py +17 -20
  41. dfm_python-0.5.57/src/test/functional/test_em.py +878 -0
  42. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/logger/test_inference_logger.py +4 -4
  43. dfm_python-0.5.57/src/test/logger/test_logger.py +146 -0
  44. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/logger/test_train_logger.py +4 -4
  45. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/models/test_dfm.py +88 -0
  46. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/numeric/test_builder.py +2 -1
  47. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/numeric/test_estimator.py +4 -3
  48. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/numeric/test_stability.py +51 -50
  49. dfm_python-0.5.57/src/test/numeric/test_tent.py +135 -0
  50. {dfm_python-0.5.56 → dfm_python-0.5.57}/tutorial/tutorial_finance_dfm.py +1 -1
  51. {dfm_python-0.5.56 → dfm_python-0.5.57}/tutorial/tutorial_macro_dfm.py +1 -1
  52. {dfm_python-0.5.56 → dfm_python-0.5.57}/uv.lock +1 -1
  53. dfm_python-0.5.56/LICENSE +0 -21
  54. dfm_python-0.5.56/src/dfm_python/config/schema/block.py +0 -64
  55. dfm_python-0.5.56/src/dfm_python/config/schema/params.py +0 -94
  56. dfm_python-0.5.56/src/dfm_python/functional/__init__.py +0 -13
  57. dfm_python-0.5.56/src/dfm_python/functional/irf.py +0 -305
  58. dfm_python-0.5.56/src/dfm_python/functional/krylov.py +0 -124
  59. dfm_python-0.5.56/src/dfm_python/logger/logger.py +0 -147
  60. dfm_python-0.5.56/src/dfm_python/ssm/__init__.py +0 -19
  61. dfm_python-0.5.56/src/dfm_python/ssm/arma.py +0 -0
  62. dfm_python-0.5.56/src/dfm_python/ssm/companion.py +0 -995
  63. dfm_python-0.5.56/src/dfm_python/ssm/kalman.py +0 -433
  64. dfm_python-0.5.56/src/dfm_python/ssm/structural.py +0 -298
  65. dfm_python-0.5.56/src/dfm_python/utils/validation.py +0 -121
  66. dfm_python-0.5.56/src/test/config/schema/test_model.py +0 -22
  67. dfm_python-0.5.56/src/test/config/test_adapter.py +0 -90
  68. dfm_python-0.5.56/src/test/functional/test_em.py +0 -57
  69. dfm_python-0.5.56/src/test/logger/test_logger.py +0 -28
  70. dfm_python-0.5.56/src/test/numeric/test_tent.py +0 -69
  71. {dfm_python-0.5.56 → dfm_python-0.5.57}/.git +0 -0
  72. {dfm_python-0.5.56 → dfm_python-0.5.57}/.gitignore +0 -0
  73. {dfm_python-0.5.56 → dfm_python-0.5.57}/.python-version +0 -0
  74. {dfm_python-0.5.56 → dfm_python-0.5.57}/MANIFEST.in +0 -0
  75. {dfm_python-0.5.56 → dfm_python-0.5.57}/config/ddfm_exchange.yaml +0 -0
  76. {dfm_python-0.5.56 → dfm_python-0.5.57}/config/ddfm_finance.yaml +0 -0
  77. {dfm_python-0.5.56 → dfm_python-0.5.57}/config/dfm_finance.yaml +0 -0
  78. {dfm_python-0.5.56 → dfm_python-0.5.57}/data/exchange_rate.csv +0 -0
  79. {dfm_python-0.5.56 → dfm_python-0.5.57}/data/finance.csv +0 -0
  80. {dfm_python-0.5.56 → dfm_python-0.5.57}/data/macro.csv +0 -0
  81. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/__init__.py +0 -0
  82. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/dataset/__init__.py +0 -0
  83. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/dataset/time.py +0 -0
  84. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/decoder/__init__.py +0 -0
  85. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/decoder/base.py +0 -0
  86. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/decoder/linear.py +0 -0
  87. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/decoder/mlp.py +0 -0
  88. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/encoder/__init__.py +0 -0
  89. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/encoder/base.py +0 -0
  90. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/encoder/pca.py +0 -0
  91. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/encoder/simple_autoencoder.py +0 -0
  92. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/numeric/builder.py +0 -0
  93. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/numeric/statistic.py +0 -0
  94. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/utils/__init__.py +0 -0
  95. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/utils/errors.py +0 -0
  96. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/utils/helper.py +0 -0
  97. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/utils/loss.py +0 -0
  98. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/dfm_python/utils/metric.py +0 -0
  99. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/__init__.py +0 -0
  100. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/config/__init__.py +0 -0
  101. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/config/schema/__init__.py +0 -0
  102. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/config/schema/test_block.py +0 -0
  103. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/config/test_constants.py +0 -0
  104. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/config/test_types.py +0 -0
  105. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/conftest.py +0 -0
  106. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/dataset/__init__.py +0 -0
  107. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/dataset/test_dataloader.py +0 -0
  108. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/dataset/test_dataset.py +0 -0
  109. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/dataset/test_process.py +0 -0
  110. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/decoder/__init__.py +0 -0
  111. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/decoder/test_linear.py +0 -0
  112. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/decoder/test_mlp.py +0 -0
  113. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/encoder/__init__.py +0 -0
  114. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/encoder/test_base.py +0 -0
  115. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/encoder/test_pca.py +0 -0
  116. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/encoder/test_simple_encoder.py +0 -0
  117. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/functional/__init__.py +0 -0
  118. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/logger/__init__.py +0 -0
  119. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/models/__init__.py +0 -0
  120. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/models/test_base.py +0 -0
  121. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/models/test_ddfm.py +0 -0
  122. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/models/test_ddfm_batchnorm_placement.py +0 -0
  123. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/models/test_tensorflow_batchnorm_comparison.py +0 -0
  124. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/numeric/__init__.py +0 -0
  125. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/numeric/test_validator.py +0 -0
  126. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/ssm/__init__.py +0 -0
  127. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/ssm/test_arma.py +0 -0
  128. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/ssm/test_companion.py +0 -0
  129. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/ssm/test_kalman.py +0 -0
  130. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/ssm/test_structural.py +0 -0
  131. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/utils/__init__.py +0 -0
  132. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/utils/test_common.py +0 -0
  133. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/utils/test_errors.py +0 -0
  134. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/utils/test_helper.py +0 -0
  135. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/utils/test_metric.py +0 -0
  136. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/utils/test_misc.py +0 -0
  137. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/utils/test_tensor_utils.py +0 -0
  138. {dfm_python-0.5.56 → dfm_python-0.5.57}/src/test/utils/test_validation.py +0 -0
  139. {dfm_python-0.5.56 → dfm_python-0.5.57}/tutorial/tutorial_exchange_ddfm.py +0 -0
  140. {dfm_python-0.5.56 → dfm_python-0.5.57}/tutorial/tutorial_finance_ddfm.py +0 -0
  141. {dfm_python-0.5.56 → dfm_python-0.5.57}/tutorial/tutorial_illness_ddfm.py +0 -0
  142. {dfm_python-0.5.56 → dfm_python-0.5.57}/tutorial/tutorial_macro_ddfm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dfm-python
3
- Version: 0.5.56
3
+ Version: 0.5.57
4
4
  Summary: Dynamic Factor Model (DFM) estimation and forecasting in Python
5
5
  Project-URL: Homepage, https://github.com/yourusername/dfm-python
6
6
  Project-URL: Documentation, https://github.com/yourusername/dfm-python#readme
@@ -8,7 +8,6 @@ Project-URL: Repository, https://github.com/yourusername/dfm-python
8
8
  Project-URL: Issues, https://github.com/yourusername/dfm-python/issues
9
9
  Author: Minkey Chang
10
10
  License: MIT
11
- License-File: LICENSE
12
11
  Keywords: dfm,dynamic-factor-model,econometrics,forecasting,time-series
13
12
  Classifier: Development Status :: 4 - Beta
14
13
  Classifier: Intended Audience :: Science/Research
@@ -176,7 +175,7 @@ dfm-python/
176
175
  │ └── dfm_python/
177
176
  │ ├── models/ # Model implementations (DFM, DDFM, KDFM)
178
177
  │ ├── config/ # Configuration schemas
179
- │ ├── datamodule/ # PyTorch Lightning DataModules
178
+ │ ├── datamodule/ # Data loading and preprocessing modules
180
179
  │ ├── trainer/ # Training procedures
181
180
  │ ├── ssm/ # State-space model components
182
181
  │ ├── dataset/ # Data loading and preprocessing
@@ -194,7 +193,7 @@ dfm-python/
194
193
  - **Structural identification**: Learnable structural shock identification (Cholesky, full, low-rank)
195
194
  - **Efficient computation**: Krylov FFT for O(T log T) forward pass
196
195
  - **Direct IRF computation**: Avoids numerical error accumulation
197
- - **Gradient descent training**: Uses PyTorch Lightning for training
196
+ - **Gradient descent training**: Uses PyTorch for training with custom logging
198
197
 
199
198
  ### DFM (Dynamic Factor Model)
200
199
 
@@ -207,7 +206,7 @@ dfm-python/
207
206
 
208
207
  - **Nonlinear encoder**: Neural network encoder for nonlinear factor extraction
209
208
  - **MCMC training**: Denoising training procedure
210
- - **PyTorch-based**: Full PyTorch implementation with Lightning support
209
+ - **PyTorch-based**: Full PyTorch implementation with custom logging system
211
210
 
212
211
  ## IRF Analysis: KDFM's PRIMARY CONTRIBUTION
213
212
 
@@ -118,7 +118,7 @@ dfm-python/
118
118
  │ └── dfm_python/
119
119
  │ ├── models/ # Model implementations (DFM, DDFM, KDFM)
120
120
  │ ├── config/ # Configuration schemas
121
- │ ├── datamodule/ # PyTorch Lightning DataModules
121
+ │ ├── datamodule/ # Data loading and preprocessing modules
122
122
  │ ├── trainer/ # Training procedures
123
123
  │ ├── ssm/ # State-space model components
124
124
  │ ├── dataset/ # Data loading and preprocessing
@@ -136,7 +136,7 @@ dfm-python/
136
136
  - **Structural identification**: Learnable structural shock identification (Cholesky, full, low-rank)
137
137
  - **Efficient computation**: Krylov FFT for O(T log T) forward pass
138
138
  - **Direct IRF computation**: Avoids numerical error accumulation
139
- - **Gradient descent training**: Uses PyTorch Lightning for training
139
+ - **Gradient descent training**: Uses PyTorch for training with custom logging
140
140
 
141
141
  ### DFM (Dynamic Factor Model)
142
142
 
@@ -149,7 +149,7 @@ dfm-python/
149
149
 
150
150
  - **Nonlinear encoder**: Neural network encoder for nonlinear factor extraction
151
151
  - **MCMC training**: Denoising training procedure
152
- - **PyTorch-based**: Full PyTorch implementation with Lightning support
152
+ - **PyTorch-based**: Full PyTorch implementation with custom logging system
153
153
 
154
154
  ## IRF Analysis: KDFM's PRIMARY CONTRIBUTION
155
155
 
@@ -26,29 +26,30 @@ target_scaler: standard # Scaler for target series: 'standard', 'robust', 'minm
26
26
 
27
27
  # Frequency mapping for mixed-frequency macro data
28
28
  # Clock is weekly (w), so monthly series are slower frequency
29
- # All frequencies must be explicitly specified
29
+ # Grouped format: frequencies are specified as lists grouped by frequency code
30
30
  # Reduced set for faster testing (~50% reduction: 12 monthly series kept from original ~24)
31
31
  frequency:
32
32
  # Monthly series (slower than weekly clock) - typically end-of-month observations
33
33
  # Essential series for testing
34
- KOEMPTOTO: m # Total employment (monthly)
35
- KOHWRWEMP: m # Hours worked (monthly)
36
- KOIMPCONA: m # Investment, Construction (monthly)
37
- KOWRCCNSE: m # Consumption (monthly)
38
- KOCONPRCF: m # Production (monthly)
39
- KOCPCOREF: m # Consumer prices (monthly)
40
- KOCPEXFEF: m # Export prices (monthly)
41
- KOCREU..A: m # Credit (monthly)
42
- KOLRLTHH: m # Interest rates (monthly)
43
- KOBSIHAPR: m # Business survey (monthly)
44
- KOCNFCONR: m # Confidence (monthly)
45
- # Target series
46
- KOEQUIPTE: m # Investment, Equipment (monthly, target series)
34
+ m:
35
+ - KOEMPTOTO # Total employment (monthly)
36
+ - KOHWRWEMP # Hours worked (monthly)
37
+ - KOIMPCONA # Investment, Construction (monthly)
38
+ - KOWRCCNSE # Consumption (monthly)
39
+ - KOCONPRCF # Production (monthly)
40
+ - KOCPCOREF # Consumer prices (monthly)
41
+ - KOCPEXFEF # Export prices (monthly)
42
+ - KOCREU..A # Credit (monthly)
43
+ - KOLRLTHH # Interest rates (monthly)
44
+ - KOBSIHAPR # Business survey (monthly)
45
+ - KOCNFCONR # Confidence (monthly)
46
+ - KOEQUIPTE # Investment, Equipment (monthly, target series)
47
47
  # Weekly series (same as clock frequency)
48
- KORFBEV: w # Retail, Food & Beverages (weekly)
49
- KORWHLS: w # Retail, Wholesale (weekly)
50
- KORMCMP: w # Retail, Miscellaneous & Comparison (weekly)
51
- KORELEC: w # Retail, Electronics (weekly)
52
- KORCNST: w # Retail, Construction (weekly)
53
- KORMACH: w # Retail, Machinery (weekly)
54
- KOROCOINF: w # Retail, Other Consumer & Information (weekly)
48
+ w:
49
+ - KORFBEV # Retail, Food & Beverages (weekly)
50
+ - KORWHLS # Retail, Wholesale (weekly)
51
+ - KORMCMP # Retail, Miscellaneous & Comparison (weekly)
52
+ - KORELEC # Retail, Electronics (weekly)
53
+ - KORCNST # Retail, Construction (weekly)
54
+ - KORMACH # Retail, Machinery (weekly)
55
+ - KOROCOINF # Retail, Other Consumer & Information (weekly)
@@ -55,30 +55,32 @@ blocks:
55
55
 
56
56
  # Frequency mapping for mixed-frequency macro data
57
57
  # Clock is weekly (w), so monthly series are slower frequency
58
- # All frequencies must be explicitly specified
58
+ # Grouped format: frequencies are specified as lists grouped by frequency code
59
59
  # Reduced set for faster testing (~50% reduction: 18 monthly series kept from original ~36)
60
60
  frequency:
61
61
  # Monthly series (slower than weekly clock) - typically end-of-month observations
62
62
  # Block_Real series
63
- KOEMPTOTO: m # Total employment (monthly)
64
- KOHWRWEMP: m # Hours worked (monthly)
65
- KOIMPCONA: m # Investment, Construction (monthly)
66
- KOWRCCNSE: m # Consumption (monthly)
67
- KOCONPRCF: m # Production (monthly)
68
- # Block_Prices_Financial series
69
- KOCPCOREF: m # Consumer prices (monthly)
70
- KOCPEXFEF: m # Export prices (monthly)
71
- KOCREU..A: m # Credit (monthly)
72
- KOLRLTHH: m # Interest rates (monthly)
73
- KOBSIHAPR: m # Business survey (monthly)
74
- KOCNFCONR: m # Confidence (monthly)
75
- # Target series
76
- KOEQUIPTE: m # Investment, Equipment (monthly, target series)
63
+ m:
64
+ - KOEMPTOTO # Total employment (monthly)
65
+ - KOHWRWEMP # Hours worked (monthly)
66
+ - KOIMPCONA # Investment, Construction (monthly)
67
+ - KOWRCCNSE # Consumption (monthly)
68
+ - KOCONPRCF # Production (monthly)
69
+ # Block_Prices_Financial series
70
+ - KOCPCOREF # Consumer prices (monthly)
71
+ - KOCPEXFEF # Export prices (monthly)
72
+ - KOCREU..A # Credit (monthly)
73
+ - KOLRLTHH # Interest rates (monthly)
74
+ - KOBSIHAPR # Business survey (monthly)
75
+ - KOCNFCONR # Confidence (monthly)
76
+ # Target series
77
+ - KOEQUIPTE # Investment, Equipment (monthly, target series)
77
78
  # Weekly series (same as clock frequency)
78
- KORFBEV: w # Retail, Food & Beverages (weekly)
79
- KORWHLS: w # Retail, Wholesale (weekly)
80
- KORMCMP: w # Retail, Miscellaneous & Comparison (weekly)
81
- KORELEC: w # Retail, Electronics (weekly)
82
- KORCNST: w # Retail, Construction (weekly)
83
- KORMACH: w # Retail, Machinery (weekly)
84
- KOROCOINF: w # Retail, Other Consumer & Information (weekly)
79
+ w:
80
+ - KORFBEV # Retail, Food & Beverages (weekly)
81
+ - KORWHLS # Retail, Wholesale (weekly)
82
+ - KORMCMP # Retail, Miscellaneous & Comparison (weekly)
83
+ - KORELEC # Retail, Electronics (weekly)
84
+ - KORCNST # Retail, Construction (weekly)
85
+ - KORMACH # Retail, Machinery (weekly)
86
+ - KOROCOINF # Retail, Other Consumer & Information (weekly)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dfm-python"
3
- version = "0.5.56"
3
+ version = "0.5.57"
4
4
  description = "Dynamic Factor Model (DFM) estimation and forecasting in Python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -83,6 +83,7 @@ packages = [
83
83
  exclude = [
84
84
  "test_venv",
85
85
  "test_venv/**",
86
+ "LICENSE",
86
87
  ]
87
88
 
88
89
  [dependency-groups]
@@ -10,7 +10,7 @@ Note: Series are specified via frequency dict mapping column names to frequencie
10
10
  from .schema import (
11
11
  BaseModelConfig, DFMConfig, DDFMConfig,
12
12
  BaseResult, DFMResult, DDFMResult,
13
- DFMFitParams, DDFMFitParams, FitParams, # FitParams is backward compatibility alias
13
+ DFMStateSpaceParams, DDFMStateSpaceParams,
14
14
  )
15
15
  # DEFAULT_BLOCK_NAME is imported lazily where needed to avoid circular imports
16
16
  from .constants import (
@@ -46,8 +46,6 @@ from .types import (
46
46
  ForecastResult,
47
47
  CoefficientMatrix,
48
48
  CovarianceMatrix,
49
- IRFArray,
50
- CompanionMatrix,
51
49
  SeriesID,
52
50
  Frequency,
53
51
  DatasetName,
@@ -58,7 +56,6 @@ from .types import (
58
56
  ResultDict,
59
57
  ForecastDict,
60
58
  MetricsDict,
61
- IRFResultDict,
62
59
  CheckpointDict,
63
60
  ConfigDict,
64
61
  Device,
@@ -107,9 +104,8 @@ __all__ = [
107
104
  # 'DEFAULT_BLOCK_NAME', # Removed to avoid circular import - import directly from functional.dfm_block
108
105
  # Model-specific configs (from schema.py)
109
106
  'DFMConfig', 'DDFMConfig',
110
- # Parameter overrides
111
- 'DFMFitParams', 'DDFMFitParams',
112
- 'FitParams', # Backward compatibility alias
107
+ # State-space parameters
108
+ 'DFMStateSpaceParams', 'DDFMStateSpaceParams',
113
109
  # Model-specific results (from results.py)
114
110
  'DFMResult', 'DDFMResult',
115
111
  # Utilities
@@ -139,8 +135,6 @@ __all__ = [
139
135
  'ForecastResult',
140
136
  'CoefficientMatrix',
141
137
  'CovarianceMatrix',
142
- 'IRFArray',
143
- 'CompanionMatrix',
144
138
  'SeriesID',
145
139
  'Frequency',
146
140
  'DatasetName',
@@ -151,7 +145,6 @@ __all__ = [
151
145
  'ResultDict',
152
146
  'ForecastDict',
153
147
  'MetricsDict',
154
- 'IRFResultDict',
155
148
  'CheckpointDict',
156
149
  'ConfigDict',
157
150
  'Device',
@@ -118,7 +118,12 @@ def _extract_frequency_dict(
118
118
  data: Dict[str, Any],
119
119
  clock: str = DEFAULT_CLOCK_FREQUENCY
120
120
  ) -> Optional[Dict[str, str]]:
121
- """Extract frequency dict from data, handling both new API and legacy formats.
121
+ """Extract frequency dict from data, handling multiple formats.
122
+
123
+ Supports:
124
+ 1. Grouped format: {'w': [series1, series2, ...], 'm': [series3, ...]}
125
+ 2. Individual format: {'series1': 'w', 'series2': 'm', ...}
126
+ 3. Legacy format: {'series': [{'series_id': ..., 'frequency': ...}, ...]}
122
127
 
123
128
  Parameters
124
129
  ----------
@@ -132,12 +137,63 @@ def _extract_frequency_dict(
132
137
  Optional[Dict[str, str]]
133
138
  Frequency dict mapping column names to frequencies, or None if not found
134
139
  """
135
- if 'frequency' in data:
136
- # New API: frequency dict directly
137
- return data['frequency']
138
- elif 'series' in data:
139
- # Legacy: convert series list/dict to frequency dict
140
- return _convert_series_to_frequency_dict(data['series'], clock)
140
+ if 'frequency' not in data:
141
+ # Legacy: check for 'series' key
142
+ if 'series' in data:
143
+ return _convert_series_to_frequency_dict(data['series'], clock)
144
+ return None
145
+
146
+ freq_data = data['frequency']
147
+
148
+ # Check if it's in grouped format: {'w': [...], 'm': [...]}
149
+ if _is_dict_like(freq_data):
150
+ from .constants import VALID_FREQUENCIES
151
+
152
+ # Check if all keys are valid frequency codes (grouped format)
153
+ keys_are_frequencies = all(
154
+ isinstance(k, str) and k in VALID_FREQUENCIES
155
+ for k in freq_data.keys()
156
+ )
157
+
158
+ # Check if values are lists (grouped format)
159
+ values_are_lists = all(
160
+ isinstance(v, (list, tuple)) for v in freq_data.values()
161
+ )
162
+
163
+ if keys_are_frequencies and values_are_lists:
164
+ # Grouped format: convert to individual format
165
+ result = {}
166
+ for freq, series_list in freq_data.items():
167
+ if not isinstance(series_list, (list, tuple)):
168
+ _raise_config_error(
169
+ f"Frequency '{freq}' must map to a list of series names, got {type(series_list)}"
170
+ )
171
+ for series_name in series_list:
172
+ if not isinstance(series_name, str):
173
+ _raise_config_error(
174
+ f"Series names must be strings, got {type(series_name)} in frequency '{freq}'"
175
+ )
176
+ if series_name in result:
177
+ _raise_config_error(
178
+ f"Series '{series_name}' appears in multiple frequency groups"
179
+ )
180
+ result[series_name] = freq
181
+ return result
182
+ else:
183
+ # Individual format: already in correct format, validate structure
184
+ result = {}
185
+ for series_name, freq in freq_data.items():
186
+ if not isinstance(series_name, str):
187
+ _raise_config_error(
188
+ f"Frequency dict keys must be strings (series names), got {type(series_name)}"
189
+ )
190
+ if not isinstance(freq, str):
191
+ _raise_config_error(
192
+ f"Frequency dict values must be strings (frequency codes), got {type(freq)} for series '{series_name}'"
193
+ )
194
+ result[series_name] = freq
195
+ return result
196
+
141
197
  return None
142
198
 
143
199
 
@@ -23,7 +23,7 @@ DEFAULT_MIN_DELTA = 1e-6 # Minimum change for improvement
23
23
  # ============================================================================
24
24
 
25
25
  # Minimum eigenvalues and variances
26
- MIN_EIGENVALUE = 1e-8 # Minimum eigenvalue for positive definite matrices
26
+ MIN_EIGENVALUE = 1e-6 # Minimum eigenvalue for positive definite matrices (increased from 1e-8 for better conditioning)
27
27
  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
  DEFAULT_DDFM_OBSERVATION_NOISE = 1e-15 # Default observation noise for DDFM state-space (matches original TensorFlow)
@@ -33,7 +33,7 @@ MIN_STD_FOR_SCALE_CHECK = 1e-10 # Minimum standard deviation for scale ratio co
33
33
  DEFAULT_VARIANCE_FALLBACK = 1.0 # Default variance fallback value for numerical stability
34
34
 
35
35
  # Maximum eigenvalues
36
- MAX_EIGENVALUE = 1e6 # Maximum eigenvalue cap
36
+ MAX_EIGENVALUE = 1e3 # Maximum eigenvalue cap (reduced to 1e3 to improve condition number: 1e3/1e-6 = 1e9)
37
37
 
38
38
  # Eigenvalue stability thresholds
39
39
  DEFAULT_EIGENVALUE_MAX_MAGNITUDE = 1.0 # Default maximum eigenvalue magnitude for stability checks
@@ -217,6 +217,7 @@ DEFAULT_START_DATE = datetime(2000, 1, 1)
217
217
  # Default window size for DDFM
218
218
  DEFAULT_WINDOW_SIZE = 100
219
219
  DEFAULT_TENT_KERNEL_SIZE = 5 # Default tent kernel size for slower-frequency series aggregation
220
+ DEFAULT_TENT_KERNEL_REGULARIZATION_MULTIPLIER = 100.0 # Regularization multiplier for tent kernel series (handles ill-conditioning)
220
221
  # Note: DEFAULT_BATCH_SIZE is defined above (line 88) as 32 for general neural networks
221
222
  # Use DEFAULT_DDFM_WINDOW_SIZE (100) for DDFM-specific window size (time-step batch size)
222
223
 
@@ -284,7 +285,7 @@ DEFAULT_PROGRESS_LOG_INTERVAL = 5 # Default progress logging interval for EM al
284
285
  # Precision Defaults
285
286
  # ============================================================================
286
287
 
287
- DEFAULT_DTYPE = np.float32 # Default numpy dtype for arrays
288
+ DEFAULT_DTYPE = np.float64 # Default numpy dtype for arrays (float64 for better numerical stability)
288
289
 
289
290
  # PyTorch dtype (matches DEFAULT_DTYPE)
290
291
  try:
@@ -518,7 +519,6 @@ __all__ = [
518
519
  'MIN_VARIABLES',
519
520
  'MIN_SHAPE_FOR_AR2',
520
521
  'MIN_DDFM_TIME_STEPS',
521
- 'MIN_SHAPE_FOR_AR2',
522
522
  'MIN_DDFM_DATASET_SIZE_WARNING',
523
523
  'MIN_ITER_FOR_DELTA_COMPUTATION',
524
524
  'DEFAULT_MIN_ITER_FOR_CONVERGENCE_CHECK',
@@ -546,6 +546,7 @@ __all__ = [
546
546
  'DEFAULT_LOG_INTERVAL',
547
547
  'DEFAULT_PROGRESS_LOG_INTERVAL',
548
548
  'DEFAULT_TENT_KERNEL_SIZE',
549
+ 'DEFAULT_TENT_KERNEL_REGULARIZATION_MULTIPLIER',
549
550
  'MAX_ERROR_ITEMS',
550
551
  # IRF
551
552
  'DEFAULT_IRF_HORIZON',
@@ -9,15 +9,11 @@ Note: Series are specified via frequency dict mapping column names to frequencie
9
9
 
10
10
  from .model import BaseModelConfig, DFMConfig, DDFMConfig
11
11
  from .results import BaseResult, DFMResult, DDFMResult
12
- from .params import DFMFitParams, DDFMFitParams
13
-
14
- # Backward compatibility alias
15
- FitParams = DFMFitParams
12
+ from .params import DFMStateSpaceParams, DDFMStateSpaceParams
16
13
 
17
14
  __all__ = [
18
15
  'BaseModelConfig', 'DFMConfig', 'DDFMConfig',
19
16
  'BaseResult', 'DFMResult', 'DDFMResult',
20
- 'DFMFitParams', 'DDFMFitParams',
21
- 'FitParams', # Backward compatibility alias
17
+ 'DFMStateSpaceParams', 'DDFMStateSpaceParams',
22
18
  ]
23
19
 
@@ -0,0 +1,122 @@
1
+ """Block structure configuration for DFM.
2
+
3
+ This module defines the BlockStructure dataclass used to group block-related
4
+ parameters in EM algorithm, replacing long conditional checks with a single
5
+ optional parameter.
6
+ """
7
+
8
+ from dataclasses import dataclass, field
9
+ from typing import Optional, Dict, List
10
+ import numpy as np
11
+
12
+
13
+ @dataclass
14
+ class BlockStructure:
15
+ """Block structure configuration for EM algorithm.
16
+
17
+ Groups all block-related parameters together to simplify function signatures
18
+ and replace long conditional checks.
19
+
20
+ Attributes
21
+ ----------
22
+ blocks : np.ndarray
23
+ Block structure array (N x n_blocks)
24
+ r : np.ndarray
25
+ Number of factors per block (n_blocks,)
26
+ p : int
27
+ VAR lag order
28
+ p_plus_one : int
29
+ State dimension per factor. This is equal to max_lag_size = max(p + 1, tent_kernel_size).
30
+ The name "p_plus_one" is used for backward compatibility with the EM algorithm interface.
31
+ In the model code, this is called "max_lag_size" to more clearly indicate it accounts
32
+ for tent kernel size when tent_kernel_size > p + 1.
33
+ n_clock_freq : int
34
+ Number of clock-frequency series
35
+ idio_indicator : np.ndarray
36
+ Idiosyncratic component indicator (N,)
37
+ R_mat : np.ndarray, optional
38
+ Tent kernel constraint matrix for mixed-frequency data
39
+ q : np.ndarray, optional
40
+ Tent kernel constraint vector for mixed-frequency data
41
+ n_slower_freq : int, optional
42
+ Number of slower-frequency series
43
+ tent_weights_dict : dict, optional
44
+ Dictionary mapping frequency pairs to tent weights
45
+
46
+ Cached indices (computed once, reused across EM iterations):
47
+ _cached_unique_blocks : List[np.ndarray], optional
48
+ Unique block patterns (cached)
49
+ _cached_unique_indices : List[int], optional
50
+ Indices of unique block patterns (cached)
51
+ _cached_bl_idxM : List[np.ndarray], optional
52
+ Clock-frequency factor loadings indices per unique block pattern (cached)
53
+ _cached_bl_idxQ : List[np.ndarray], optional
54
+ Slower-frequency factor loadings indices per unique block pattern (cached)
55
+ _cached_R_con : np.ndarray, optional
56
+ Block diagonal constraint matrix (cached)
57
+ _cached_q_con : np.ndarray, optional
58
+ Constraint vector (cached)
59
+ _cached_total_factor_dim : int, optional
60
+ Total factor state dimension (cached)
61
+ _cached_idio_indicator_M : np.ndarray, optional
62
+ Clock-frequency idiosyncratic component indicator (cached)
63
+ _cached_n_idio_M : int, optional
64
+ Number of clock-frequency idiosyncratic components (cached)
65
+ _cached_c_idio_indicator : np.ndarray, optional
66
+ Cumulative sum of idiosyncratic component indicator (cached)
67
+ _cached_rp1 : int, optional
68
+ Start of idiosyncratic components in state space (cached)
69
+ """
70
+ blocks: np.ndarray
71
+ r: np.ndarray
72
+ p: int
73
+ p_plus_one: int
74
+ n_clock_freq: int
75
+ idio_indicator: np.ndarray
76
+ R_mat: Optional[np.ndarray] = None
77
+ q: Optional[np.ndarray] = None
78
+ n_slower_freq: Optional[int] = None
79
+ tent_weights_dict: Optional[Dict[str, np.ndarray]] = None
80
+
81
+ # Cached indices (computed once, reused across EM iterations)
82
+ _cached_unique_blocks: Optional[List[np.ndarray]] = field(default=None, init=False, repr=False)
83
+ _cached_unique_indices: Optional[List[int]] = field(default=None, init=False, repr=False)
84
+ _cached_bl_idxM: Optional[List[np.ndarray]] = field(default=None, init=False, repr=False)
85
+ _cached_bl_idxQ: Optional[List[np.ndarray]] = field(default=None, init=False, repr=False)
86
+ _cached_R_con: Optional[np.ndarray] = field(default=None, init=False, repr=False)
87
+ _cached_q_con: Optional[np.ndarray] = field(default=None, init=False, repr=False)
88
+ _cached_total_factor_dim: Optional[int] = field(default=None, init=False, repr=False)
89
+ _cached_idio_indicator_M: Optional[np.ndarray] = field(default=None, init=False, repr=False)
90
+ _cached_n_idio_M: Optional[int] = field(default=None, init=False, repr=False)
91
+ _cached_c_idio_indicator: Optional[np.ndarray] = field(default=None, init=False, repr=False)
92
+ _cached_rp1: Optional[int] = field(default=None, init=False, repr=False)
93
+
94
+ def is_valid(self) -> bool:
95
+ """Check if block structure is valid (all required fields are not None)."""
96
+ return (
97
+ self.blocks is not None
98
+ and self.r is not None
99
+ and self.p is not None
100
+ and self.p_plus_one is not None
101
+ and self.n_clock_freq is not None
102
+ and self.idio_indicator is not None
103
+ )
104
+
105
+ def has_cached_indices(self) -> bool:
106
+ """Check if block structure indices are cached."""
107
+ return self._cached_unique_blocks is not None
108
+
109
+ def clear_cache(self) -> None:
110
+ """Clear all cached indices to ensure clean state for new training runs."""
111
+ self._cached_unique_blocks = None
112
+ self._cached_unique_indices = None
113
+ self._cached_bl_idxM = None
114
+ self._cached_bl_idxQ = None
115
+ self._cached_R_con = None
116
+ self._cached_q_con = None
117
+ self._cached_total_factor_dim = None
118
+ self._cached_idio_indicator_M = None
119
+ self._cached_n_idio_M = None
120
+ self._cached_c_idio_indicator = None
121
+ self._cached_rp1 = None
122
+
@@ -91,7 +91,9 @@ class BaseModelConfig:
91
91
  - Data preprocessing (missing data handling)
92
92
 
93
93
  Series Configuration:
94
- - Provide `frequency` dict: {'column_name': 'frequency_code'} to specify per-series frequencies
94
+ - Provide `frequency` dict in one of two formats:
95
+ 1. Grouped format: {'w': [series1, series2, ...], 'm': [series3, ...]} (recommended for large configs)
96
+ 2. Individual format: {'series1': 'w', 'series2': 'm', ...} (backward compatible)
95
97
  - If `frequency` is None, all columns will use `clock` frequency
96
98
  - If a column is missing from `frequency` dict, it will use `clock` frequency
97
99
  - When data is loaded, missing columns in `frequency` dict are automatically added with `clock` frequency
@@ -103,7 +105,14 @@ class BaseModelConfig:
103
105
 
104
106
  Examples
105
107
  --------
106
- >>> # With explicit frequency mapping
108
+ >>> # With grouped frequency mapping (recommended for large configs)
109
+ >>> config = DFMConfig(
110
+ ... frequency={'q': ['gdp'], 'm': ['unemployment', 'interest_rate']},
111
+ ... clock='m',
112
+ ... blocks={...}
113
+ ... )
114
+ >>>
115
+ >>> # With individual frequency mapping (backward compatible)
107
116
  >>> config = DFMConfig(
108
117
  ... frequency={'gdp': 'q', 'unemployment': 'm', 'interest_rate': 'm'},
109
118
  ... clock='m',