dfm-python 0.5.58__tar.gz → 0.5.60__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 (158) hide show
  1. dfm_python-0.5.60/.cursor/agents/test-runner.md +118 -0
  2. dfm_python-0.5.60/=0.11.0 +1 -0
  3. dfm_python-0.5.60/PKG-INFO +56 -0
  4. {dfm_python-0.5.58 → dfm_python-0.5.60}/config/ddfm_exchange.yaml +1 -1
  5. {dfm_python-0.5.58 → dfm_python-0.5.60}/config/ddfm_finance.yaml +1 -1
  6. {dfm_python-0.5.58 → dfm_python-0.5.60}/config/ddfm_macro.yaml +1 -1
  7. dfm_python-0.5.60/config/dfm_exchange.yaml +32 -0
  8. {dfm_python-0.5.58 → dfm_python-0.5.60}/config/dfm_finance.yaml +2 -5
  9. {dfm_python-0.5.58 → dfm_python-0.5.60}/config/dfm_macro.yaml +2 -5
  10. {dfm_python-0.5.58 → dfm_python-0.5.60}/pyproject.toml +2 -2
  11. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/__init__.py +8 -3
  12. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/config/__init__.py +2 -8
  13. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/config/adapter.py +0 -1
  14. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/config/schema/model.py +9 -8
  15. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/config/schema/params.py +59 -89
  16. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/dataset/ddfm_dataset.py +85 -47
  17. dfm_python-0.5.60/src/dfm_python/dataset/dfm_dataset.py +187 -0
  18. dfm_python-0.5.60/src/dfm_python/functional/em.py +1114 -0
  19. dfm_python-0.5.60/src/dfm_python/logger/dfm_logger.py +39 -0
  20. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/models/__init__.py +8 -4
  21. dfm_python-0.5.60/src/dfm_python/models/base.py +73 -0
  22. {dfm_python-0.5.58/src/dfm_python/models → dfm_python-0.5.60/src/dfm_python/models/ddfm}/ddfm.py +281 -54
  23. dfm_python-0.5.60/src/dfm_python/models/dfm/__init__.py +39 -0
  24. dfm_python-0.5.60/src/dfm_python/models/dfm/dfm.py +991 -0
  25. dfm_python-0.5.60/src/dfm_python/models/dfm/initialization.py +926 -0
  26. dfm_python-0.5.60/src/dfm_python/models/dfm/mixed_freq.py +262 -0
  27. {dfm_python-0.5.58/src/dfm_python/numeric → dfm_python-0.5.60/src/dfm_python/models/dfm}/tent.py +4 -5
  28. dfm_python-0.5.60/src/dfm_python/numeric/builder.py +197 -0
  29. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/numeric/estimator.py +1 -94
  30. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/numeric/stability.py +6 -545
  31. dfm_python-0.5.60/src/dfm_python/numeric/validator.py +742 -0
  32. dfm_python-0.5.60/src/dfm_python/ssm/kalman.py +386 -0
  33. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/utils/__init__.py +32 -56
  34. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/utils/misc.py +36 -13
  35. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/utils/validation.py +1 -0
  36. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/config/test_tent_weights_config.py +1 -1
  37. dfm_python-0.5.60/src/test/dataset/test_ddfm_dataset.py +0 -0
  38. dfm_python-0.5.60/src/test/dataset/test_dfm_dataset.py +0 -0
  39. dfm_python-0.5.60/src/test/functional/test_em.py +136 -0
  40. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/models/test_ddfm.py +26 -32
  41. dfm_python-0.5.60/src/test/models/test_dfm.py +92 -0
  42. dfm_python-0.5.60/src/test/numeric/test_builder.py +40 -0
  43. dfm_python-0.5.60/src/test/numeric/test_validator.py +122 -0
  44. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/ssm/test_kalman.py +5 -5
  45. {dfm_python-0.5.58 → dfm_python-0.5.60}/tutorial/tutorial_exchange_ddfm.py +12 -13
  46. dfm_python-0.5.60/tutorial/tutorial_exchange_dfm.py +68 -0
  47. {dfm_python-0.5.58 → dfm_python-0.5.60}/tutorial/tutorial_finance_ddfm.py +6 -4
  48. dfm_python-0.5.60/tutorial/tutorial_finance_dfm.py +114 -0
  49. {dfm_python-0.5.58 → dfm_python-0.5.60}/tutorial/tutorial_macro_ddfm.py +13 -5
  50. dfm_python-0.5.60/tutorial/tutorial_macro_dfm.py +119 -0
  51. {dfm_python-0.5.58 → dfm_python-0.5.60}/uv.lock +28 -1
  52. dfm_python-0.5.58/PKG-INFO +0 -379
  53. dfm_python-0.5.58/README.md +0 -322
  54. dfm_python-0.5.58/src/dfm_python/dataset/dfm_dataset.py +0 -542
  55. dfm_python-0.5.58/src/dfm_python/functional/dfm_block.py +0 -485
  56. dfm_python-0.5.58/src/dfm_python/functional/em.py +0 -1356
  57. dfm_python-0.5.58/src/dfm_python/models/base.py +0 -292
  58. dfm_python-0.5.58/src/dfm_python/models/dfm.py +0 -1842
  59. dfm_python-0.5.58/src/dfm_python/numeric/builder.py +0 -187
  60. dfm_python-0.5.58/src/dfm_python/numeric/validator.py +0 -1110
  61. dfm_python-0.5.58/src/dfm_python/ssm/kalman.py +0 -628
  62. dfm_python-0.5.58/src/test/dataset/__init__.py +0 -2
  63. dfm_python-0.5.58/src/test/dataset/test_dataloader.py +0 -25
  64. dfm_python-0.5.58/src/test/dataset/test_dataset.py +0 -69
  65. dfm_python-0.5.58/src/test/dataset/test_process.py +0 -116
  66. dfm_python-0.5.58/src/test/functional/test_dfm_block.py +0 -140
  67. dfm_python-0.5.58/src/test/functional/test_em.py +0 -878
  68. dfm_python-0.5.58/src/test/models/test_base.py +0 -161
  69. dfm_python-0.5.58/src/test/models/test_ddfm_batchnorm_placement.py +0 -179
  70. dfm_python-0.5.58/src/test/models/test_dfm.py +0 -225
  71. dfm_python-0.5.58/src/test/models/test_dfm_fixes.py +0 -261
  72. dfm_python-0.5.58/src/test/models/test_dfm_result_caching.py +0 -309
  73. dfm_python-0.5.58/src/test/models/test_dfm_save_load.py +0 -261
  74. dfm_python-0.5.58/src/test/models/test_dfm_save_load_integration.py +0 -136
  75. dfm_python-0.5.58/src/test/models/test_dfm_save_load_mock.py +0 -340
  76. dfm_python-0.5.58/src/test/models/test_dfm_training_pipeline_save.py +0 -246
  77. dfm_python-0.5.58/src/test/models/test_tensorflow_batchnorm_comparison.py +0 -266
  78. dfm_python-0.5.58/src/test/numeric/test_builder.py +0 -27
  79. dfm_python-0.5.58/src/test/numeric/test_tent.py +0 -113
  80. dfm_python-0.5.58/src/test/numeric/test_validator.py +0 -279
  81. dfm_python-0.5.58/src/test/ssm/test_arma.py +0 -22
  82. dfm_python-0.5.58/src/test/ssm/test_companion.py +0 -103
  83. dfm_python-0.5.58/src/test/ssm/test_kalman_progress.py +0 -161
  84. dfm_python-0.5.58/src/test/ssm/test_progress_bar_exact_scenario.py +0 -226
  85. dfm_python-0.5.58/src/test/ssm/test_progress_bar_spam_fix.py +0 -297
  86. dfm_python-0.5.58/src/test/ssm/test_structural.py +0 -67
  87. dfm_python-0.5.58/src/test/utils/test_common.py +0 -458
  88. dfm_python-0.5.58/src/test/utils/test_tensor_utils.py +0 -57
  89. dfm_python-0.5.58/src/test/utils/test_validation.py +0 -137
  90. dfm_python-0.5.58/tutorial/tutorial_finance_dfm.py +0 -259
  91. dfm_python-0.5.58/tutorial/tutorial_illness_ddfm.py +0 -284
  92. dfm_python-0.5.58/tutorial/tutorial_macro_dfm.py +0 -306
  93. {dfm_python-0.5.58 → dfm_python-0.5.60}/.git +0 -0
  94. {dfm_python-0.5.58 → dfm_python-0.5.60}/.gitignore +0 -0
  95. {dfm_python-0.5.58 → dfm_python-0.5.60}/.python-version +0 -0
  96. {dfm_python-0.5.58 → dfm_python-0.5.60}/MANIFEST.in +0 -0
  97. {dfm_python-0.5.58 → dfm_python-0.5.60}/data/exchange_rate.csv +0 -0
  98. {dfm_python-0.5.58 → dfm_python-0.5.60}/data/finance.csv +0 -0
  99. {dfm_python-0.5.58 → dfm_python-0.5.60}/data/macro.csv +0 -0
  100. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/config/constants.py +0 -0
  101. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/config/schema/__init__.py +0 -0
  102. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/config/schema/block.py +0 -0
  103. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/config/schema/results.py +0 -0
  104. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/config/types.py +0 -0
  105. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/dataset/__init__.py +0 -0
  106. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/dataset/time.py +0 -0
  107. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/decoder/__init__.py +0 -0
  108. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/decoder/base.py +0 -0
  109. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/decoder/linear.py +0 -0
  110. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/decoder/mlp.py +0 -0
  111. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/encoder/__init__.py +0 -0
  112. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/encoder/base.py +0 -0
  113. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/encoder/pca.py +0 -0
  114. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/encoder/simple_autoencoder.py +0 -0
  115. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/functional/__init__.py +0 -0
  116. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/logger/__init__.py +0 -0
  117. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/logger/inference_logger.py +0 -0
  118. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/logger/logger.py +0 -0
  119. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/logger/train_logger.py +0 -0
  120. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/numeric/statistic.py +0 -0
  121. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/ssm/__init__.py +0 -0
  122. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/utils/checkpoint.py +0 -0
  123. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/utils/errors.py +0 -0
  124. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/utils/helper.py +0 -0
  125. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/utils/loss.py +0 -0
  126. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/dfm_python/utils/metric.py +0 -0
  127. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/__init__.py +0 -0
  128. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/config/__init__.py +0 -0
  129. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/config/schema/__init__.py +0 -0
  130. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/config/schema/test_block.py +0 -0
  131. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/config/schema/test_model.py +0 -0
  132. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/config/schema/test_results.py +0 -0
  133. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/config/test_adapter.py +0 -0
  134. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/config/test_constants.py +0 -0
  135. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/config/test_types.py +0 -0
  136. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/conftest.py +0 -0
  137. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/decoder/__init__.py +0 -0
  138. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/decoder/test_linear.py +0 -0
  139. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/decoder/test_mlp.py +0 -0
  140. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/encoder/__init__.py +0 -0
  141. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/encoder/test_base.py +0 -0
  142. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/encoder/test_pca.py +0 -0
  143. /dfm_python-0.5.58/src/test/encoder/test_simple_encoder.py → /dfm_python-0.5.60/src/test/encoder/test_simple_autoencoder.py +0 -0
  144. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/functional/__init__.py +0 -0
  145. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/logger/__init__.py +0 -0
  146. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/logger/test_inference_logger.py +0 -0
  147. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/logger/test_logger.py +0 -0
  148. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/logger/test_train_logger.py +0 -0
  149. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/models/__init__.py +0 -0
  150. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/numeric/__init__.py +0 -0
  151. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/numeric/test_estimator.py +0 -0
  152. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/numeric/test_stability.py +0 -0
  153. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/ssm/__init__.py +0 -0
  154. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/utils/__init__.py +0 -0
  155. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/utils/test_errors.py +0 -0
  156. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/utils/test_helper.py +0 -0
  157. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/utils/test_metric.py +0 -0
  158. {dfm_python-0.5.58 → dfm_python-0.5.60}/src/test/utils/test_misc.py +0 -0
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: test-runner
3
+ description: Testing specialist for running pytest, fixing test failures, and verifying implementations. Use proactively after code changes or when user requests testing.
4
+ ---
5
+
6
+ You are a testing specialist focused on running tests, diagnosing failures, and ensuring code correctness.
7
+
8
+ ## Core Responsibilities
9
+
10
+ When invoked:
11
+ 1. **Run tests** using `uv run pytest` (never use system pytest)
12
+ 2. **Analyze failures** with clear error messages and stack traces
13
+ 3. **Fix issues** directly in the code
14
+ 4. **Verify fixes** by re-running tests
15
+ 5. **Report results** concisely
16
+
17
+ ## Testing Workflow
18
+
19
+ ### 1. Environment Setup
20
+ - Always use `uv` and virtual environment (`.venv/`)
21
+ - Activate venv: `source .venv/bin/activate` OR use `uv run`
22
+ - Never use system `python` or `pytest` directly
23
+
24
+ ### 2. Running Tests
25
+
26
+ ```bash
27
+ # Run all tests
28
+ uv run pytest
29
+
30
+ # Run specific test file
31
+ uv run pytest src/test/models/test_dfm.py
32
+
33
+ # Run with verbose output
34
+ uv run pytest -v
35
+
36
+ # Run specific test function
37
+ uv run pytest src/test/models/test_dfm.py::TestDFM::test_dfm_fit
38
+ ```
39
+
40
+ ### 3. Analyzing Failures
41
+
42
+ For each failure:
43
+ - **Read the error message** carefully
44
+ - **Check the stack trace** to locate the issue
45
+ - **Identify root cause** (not just symptoms)
46
+ - **Check recent code changes** if applicable
47
+ - **Verify imports and dependencies**
48
+
49
+ ### 4. Fixing Issues
50
+
51
+ - **Fix directly** in the code (don't just report)
52
+ - **Make minimal changes** to resolve the issue
53
+ - **Preserve existing functionality** unless explicitly changing it
54
+ - **Update tests** if requirements changed (not just to make them pass)
55
+
56
+ ### 5. Verification
57
+
58
+ - **Re-run the specific failing test** first
59
+ - **Run the full test suite** if multiple tests might be affected
60
+ - **Verify no regressions** introduced
61
+
62
+ ## Common Issues and Fixes
63
+
64
+ ### Import Errors
65
+ - Check if module exists and path is correct
66
+ - Verify `__init__.py` files exist
67
+ - Check if function/class was moved or renamed
68
+ - Update imports to match current code structure
69
+
70
+ ### Assertion Failures
71
+ - Understand what the test expects vs what code produces
72
+ - Check if test expectations are outdated
73
+ - Verify logic correctness, not just make test pass
74
+
75
+ ### Missing Dependencies
76
+ - Install with `uv pip install <package>`
77
+ - Check `pyproject.toml` for required dependencies
78
+ - Verify virtual environment has all packages
79
+
80
+ ### Type Errors
81
+ - Check type hints match actual usage
82
+ - Verify imports for type annotations
83
+ - Update types if code behavior changed
84
+
85
+ ## Output Format
86
+
87
+ After running tests, provide:
88
+ 1. **Summary**: Total tests, passed, failed, skipped
89
+ 2. **Failures**: List each failure with:
90
+ - Test name and location
91
+ - Error message
92
+ - Root cause analysis
93
+ - Fix applied
94
+ 3. **Status**: All tests passing or remaining issues
95
+
96
+ ## Best Practices
97
+
98
+ - **Run tests immediately** when requested
99
+ - **Fix issues proactively** - don't just report them
100
+ - **Use uv and venv** always
101
+ - **Be thorough** but efficient
102
+ - **Verify fixes** before reporting success
103
+ - **Keep test output** concise but informative
104
+
105
+ ## Example Workflow
106
+
107
+ ```
108
+ User: "Run tests for DFM"
109
+ Assistant:
110
+ 1. Runs: `uv run pytest src/test/models/test_dfm.py -v`
111
+ 2. Sees failure: ImportError for validate_dfm_initialization
112
+ 3. Checks: Function exists in validator.py
113
+ 4. Fixes: Updates import path in test file
114
+ 5. Re-runs: `uv run pytest src/test/models/test_dfm.py::TestDFM::test_dfm_fit`
115
+ 6. Reports: "Fixed import error. All DFM tests passing."
116
+ ```
117
+
118
+ **Remember: Act quickly, fix directly, verify thoroughly.**
@@ -0,0 +1 @@
1
+ Audited 1 package in 1ms
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.4
2
+ Name: dfm-python
3
+ Version: 0.5.60
4
+ Summary: Dynamic Factor Model (DFM) estimation and forecasting in Python
5
+ Project-URL: Homepage, https://github.com/yourusername/dfm-python
6
+ Project-URL: Documentation, https://github.com/yourusername/dfm-python#readme
7
+ Project-URL: Repository, https://github.com/yourusername/dfm-python
8
+ Project-URL: Issues, https://github.com/yourusername/dfm-python/issues
9
+ Author: Minkey Chang
10
+ License: MIT
11
+ Keywords: dfm,dynamic-factor-model,econometrics,forecasting,time-series
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: build>=1.4.0
22
+ Requires-Dist: einops>=0.8.1
23
+ Requires-Dist: hydra-core>=1.3.2
24
+ Requires-Dist: matplotlib>=3.5.0
25
+ Requires-Dist: numpy>=1.24.0
26
+ Requires-Dist: pandas>=2.0.0
27
+ Requires-Dist: pyarrow>=22.0.0
28
+ Requires-Dist: pykalman>=0.11.0
29
+ Requires-Dist: pytorch-lightning>=2.0.0
30
+ Requires-Dist: scikit-learn>=1.7.2
31
+ Requires-Dist: scipy>=1.10.0
32
+ Requires-Dist: sktime>=0.40.1
33
+ Requires-Dist: torch>=2.0.0
34
+ Requires-Dist: twine>=6.2.0
35
+ Provides-Extra: all
36
+ Requires-Dist: hydra-core>=1.3.0; extra == 'all'
37
+ Requires-Dist: omegaconf>=2.3.0; extra == 'all'
38
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'all'
39
+ Requires-Dist: pytest>=7.0.0; extra == 'all'
40
+ Requires-Dist: scikit-learn>=1.0.0; extra == 'all'
41
+ Requires-Dist: sktime>=0.27.0; extra == 'all'
42
+ Requires-Dist: sqlalchemy>=2.0.0; extra == 'all'
43
+ Provides-Extra: db
44
+ Requires-Dist: sqlalchemy>=2.0.0; extra == 'db'
45
+ Provides-Extra: deep
46
+ Provides-Extra: dev
47
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
48
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
49
+ Requires-Dist: scikit-learn>=1.0.0; extra == 'dev'
50
+ Provides-Extra: forecasting
51
+ Requires-Dist: sktime[forecasting]>=0.40.1; extra == 'forecasting'
52
+ Provides-Extra: hydra
53
+ Requires-Dist: hydra-core>=1.3.0; extra == 'hydra'
54
+ Requires-Dist: omegaconf>=2.3.0; extra == 'hydra'
55
+ Provides-Extra: transform
56
+ Requires-Dist: sktime>=0.27.0; extra == 'transform'
@@ -22,7 +22,7 @@ seed: 3 # Random seed (matching original seed=3)
22
22
 
23
23
  # Data Handling (used by Dataset, not Model)
24
24
  clock: d # Daily clock frequency (all series are daily)
25
- target_scaler: null # No target scaler - data should be pre-scaled before passing to Dataset
25
+ scaler: null # No scaler - data should be pre-scaled before passing to Dataset
26
26
 
27
27
  # Frequency mapping (all series are daily)
28
28
  # If not specified, all series use clock frequency (d)
@@ -20,7 +20,7 @@ seed: null # Random seed (None uses default)
20
20
 
21
21
  # Data Handling (used by Dataset, not Model)
22
22
  clock: m # Monthly clock frequency
23
- target_scaler: standard # Scaler for target series: 'standard', 'robust', 'minmax', 'maxabs', 'quantile', or null
23
+ scaler: standard # Scaler for target series: 'standard', 'robust', 'minmax', 'maxabs', 'quantile', or null
24
24
 
25
25
  # Frequency mapping (optional)
26
26
  # If not specified, all series use clock frequency
@@ -22,7 +22,7 @@ seed: null # Random seed (None uses default)
22
22
 
23
23
  # Data Handling (used by Dataset, not Model)
24
24
  clock: w # Weekly clock frequency (base frequency for latent factors)
25
- target_scaler: standard # Scaler for target series: 'standard', 'robust', 'minmax', 'maxabs', 'quantile', or null
25
+ scaler: standard # Scaler for target series: 'standard', 'robust', 'minmax', 'maxabs', 'quantile', or null
26
26
 
27
27
  # Frequency mapping for mixed-frequency macro data
28
28
  # Clock is weekly (w), so monthly series are slower frequency
@@ -0,0 +1,32 @@
1
+ # DFM Configuration for Exchange Rate Datasets
2
+ # Clock: Daily (d) - base frequency for latent factors
3
+
4
+ # EM Algorithm Parameters
5
+ threshold: 1.0e-05
6
+ max_iter: 5000
7
+
8
+ # Numerical Stability Parameters
9
+ clip_ar_coefficients: true
10
+ ar_clip_min: -0.99
11
+ ar_clip_max: 0.99
12
+ use_regularization: true
13
+ regularization_scale: 1.0e-05
14
+
15
+ # Idiosyncratic Component Parameters
16
+ augment_idio: true
17
+ augment_idio_slow: false
18
+ idio_rho0: 0.1
19
+ idio_min_var: 1.0e-08
20
+
21
+ # Data Handling
22
+ clock: d
23
+ scaler: standard
24
+
25
+ # Block Structure
26
+ blocks:
27
+ Block_Global:
28
+ num_factors: 1
29
+ series: [] # Will be populated from data
30
+
31
+ # Frequency mapping
32
+ frequency: {} # Will be populated from data
@@ -6,7 +6,6 @@
6
6
  # Reduced series for testing purposes
7
7
 
8
8
  # EM Algorithm Parameters
9
- ar_lag: 1 # Global AR lag order (applies to all blocks)
10
9
  threshold: 1.0e-05 # EM convergence threshold
11
10
  max_iter: 5000 # Maximum EM iterations
12
11
 
@@ -25,10 +24,8 @@ idio_min_var: 1.0e-08
25
24
 
26
25
  # Data Handling
27
26
  clock: m # Monthly clock frequency (tutorial uses monthly, adjust to 'd' for daily or 'w' for weekly)
28
- # target_scaler: Pass sklearn scaler object directly (e.g., StandardScaler(), RobustScaler())
29
- # Example in Python:
30
- # from sklearn.preprocessing import StandardScaler
31
- # config.target_scaler = StandardScaler()
27
+ scaler: standard # Scaler for all series: 'standard', 'robust', 'minmax', 'maxabs', 'quantile', or null
28
+ # Note: scaler is handled internally by the model, not set on config object
32
29
  # Note: nan_method and nan_k are internal constants (not user-configurable)
33
30
 
34
31
  # Block Structure for Financial Data
@@ -5,7 +5,6 @@
5
5
  # Target: KOEQUIPTE (Investment, Equipment, Estimation, SA)
6
6
 
7
7
  # EM Algorithm Parameters
8
- ar_lag: 1 # Global AR lag order (applies to all blocks)
9
8
  threshold: 1.0e-05 # EM convergence threshold
10
9
  max_iter: 5000 # Maximum EM iterations
11
10
 
@@ -24,10 +23,8 @@ idio_min_var: 1.0e-08
24
23
 
25
24
  # Data Handling
26
25
  clock: w # Weekly clock frequency (base frequency for latent factors)
27
- # target_scaler: Pass sklearn scaler object directly (e.g., StandardScaler(), RobustScaler())
28
- # Example in Python:
29
- # from sklearn.preprocessing import StandardScaler
30
- # config.target_scaler = StandardScaler()
26
+ scaler: standard # Scaler for all series: 'standard', 'robust', 'minmax', 'maxabs', 'quantile', or null
27
+ # Note: scaler is handled internally by the model, not set on config object
31
28
  # Note: nan_method and nan_k are internal constants (not user-configurable)
32
29
 
33
30
  # Block Structure for Macroeconomic Data
@@ -1,8 +1,7 @@
1
1
  [project]
2
2
  name = "dfm-python"
3
- version = "0.5.58"
3
+ version = "0.5.60"
4
4
  description = "Dynamic Factor Model (DFM) estimation and forecasting in Python"
5
- readme = "README.md"
6
5
  requires-python = ">=3.10"
7
6
  license = {text = "MIT"}
8
7
  authors = [
@@ -20,6 +19,7 @@ classifiers = [
20
19
  "Topic :: Scientific/Engineering",
21
20
  ]
22
21
  dependencies = [
22
+ "build>=1.4.0",
23
23
  "einops>=0.8.1",
24
24
  "hydra-core>=1.3.2",
25
25
  "matplotlib>=3.5.0",
@@ -32,7 +32,9 @@ Example:
32
32
  >>> df_processed = df[[col for col in df.columns if col != 'date']]
33
33
  >>>
34
34
  >>> # Step 2: Create Dataset
35
- >>> dataset = DFMDataset(config_path='config/default.yaml', data=df_processed)
35
+ >>> from dfm_python.config import DFMConfig
36
+ >>> config = DFMConfig.from_yaml('config/default.yaml')
37
+ >>> dataset = DFMDataset(config=config, data=df_processed)
36
38
  >>>
37
39
  >>> # Step 3: Create model and load config
38
40
  >>> model = DFM()
@@ -61,7 +63,7 @@ Note: DFMConfig uses frequency dict to specify series (column names -> frequenci
61
63
  For detailed documentation, see the README.md file and the tutorial notebooks/scripts.
62
64
  """
63
65
 
64
- __version__ = "0.5.56"
66
+ __version__ = "0.5.60"
65
67
 
66
68
  # ============================================================================
67
69
  # PUBLIC API DEFINITION
@@ -102,7 +104,10 @@ from .models.base import BaseFactorModel
102
104
  from .models.dfm import DFM
103
105
 
104
106
  # DDFM high-level API (PyTorch is mandatory)
105
- from .models.ddfm import DDFM
107
+ try:
108
+ from .models.ddfm.ddfm import DDFM
109
+ except ImportError:
110
+ DDFM = None
106
111
 
107
112
  __all__ = [
108
113
  # Core classes
@@ -81,12 +81,7 @@ from .types import (
81
81
  )
82
82
  # Import lazily to avoid circular dependencies
83
83
  try:
84
- from ..numeric.builder import compute_idio_lengths
85
- except ImportError:
86
- compute_idio_lengths = None
87
-
88
- try:
89
- from ..numeric.tent import get_agg_structure, group_by_freq
84
+ from ..models.dfm.tent import get_agg_structure, group_by_freq
90
85
  except ImportError:
91
86
  get_agg_structure = None
92
87
  group_by_freq = None
@@ -100,7 +95,7 @@ def get_periods_per_year(frequency: str) -> int:
100
95
  __all__ = [
101
96
  # Base classes
102
97
  'BaseModelConfig', 'BaseResult',
103
- # 'DEFAULT_BLOCK_NAME', # Removed to avoid circular import - import directly from functional.dfm_block
98
+ # 'DEFAULT_BLOCK_NAME', # Removed to avoid circular import
104
99
  # Model-specific configs (from schema.py)
105
100
  'DFMConfig', 'DDFMConfig',
106
101
  # State-space parameters
@@ -117,7 +112,6 @@ __all__ = [
117
112
  'FREQUENCY_HIERARCHY',
118
113
  'PERIODS_PER_YEAR',
119
114
  'get_periods_per_year',
120
- 'compute_idio_lengths',
121
115
  'get_agg_structure',
122
116
  'group_by_freq',
123
117
  # Type definitions (from types.py)
@@ -303,7 +303,6 @@ class YamlSource:
303
303
  _EXCLUDED_KEYS = {
304
304
  'defaults', '_target_', '_recursive_', '_convert_',
305
305
  'series', 'blocks', 'data', 'output', 'description', 'name', 'target', 'model',
306
- 'nan_method', 'nan_k' # Internal constants, not user-configurable
307
306
  }
308
307
  main_settings = {k: v for k, v in cfg_dict.items() if k not in _EXCLUDED_KEYS}
309
308
 
@@ -65,8 +65,6 @@ from ..constants import (
65
65
  MIN_EIGENVALUE,
66
66
  MAX_EIGENVALUE,
67
67
  MIN_DIAGONAL_VARIANCE,
68
- DEFAULT_NAN_METHOD,
69
- DEFAULT_NAN_K,
70
68
  DEFAULT_CLOCK_FREQUENCY,
71
69
  DEFAULT_KDFM_AR_ORDER,
72
70
  DEFAULT_KDFM_MA_ORDER,
@@ -138,7 +136,6 @@ class BaseModelConfig:
138
136
  # ========================================================================
139
137
  clock: str = 'm' # Required: Base frequency for latent factors (global clock): 'd', 'w', 'm', 'q', 'sa', 'a' (defaults to 'm' for monthly)
140
138
  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.
141
- # Note: nan_method and nan_k are internal constants (DEFAULT_NAN_METHOD, DEFAULT_NAN_K) used during initialization only
142
139
 
143
140
  def __post_init__(self):
144
141
  """Validate basic model structure.
@@ -294,9 +291,6 @@ class DFMConfig(BaseModelConfig):
294
291
  # - If mixed frequencies detected: augment_idio=True, augment_idio_slow=True (required for tent kernel)
295
292
  # These are properties, not config fields - automatically determined from frequency configuration
296
293
 
297
- # Kalman Filter Type
298
- use_cholesky_filter: bool = False # Use CholeskyKalmanFilter for better numerical stability (default: False)
299
-
300
294
  # Tent Kernel Weights (for mixed-frequency aggregation)
301
295
  tent_weights: Optional[Union[Dict[str, List[float]], Dict[str, np.ndarray]]] = None # Required for mixed-frequency data: tent weights for frequency pairs. Format: {'freq_pair': [weights]} or {'freq': [weights]}. Example: {'m:w': [1, 2, 1]} or {'m': [1, 2, 1]}. Must be specified in config for all slower-frequency pairs.
302
296
 
@@ -379,7 +373,11 @@ class DFMConfig(BaseModelConfig):
379
373
  object.__setattr__(self, '_augment_idio_slow', is_mixed_freq)
380
374
 
381
375
  def to_em_config(self) -> 'EMConfig':
382
- """Create EMConfig from DFMConfig consolidated parameters."""
376
+ """Create EMConfig from DFMConfig consolidated parameters.
377
+
378
+ Automatically passes all numerical stability parameters (regularization, ar_clip,
379
+ damping_factor, data_clip) from DFMConfig to EMConfig for use in EM algorithm.
380
+ """
383
381
  from ...functional.em import EMConfig
384
382
  from ..constants import DEFAULT_REGULARIZATION, VAR_STABILITY_THRESHOLD
385
383
 
@@ -397,6 +395,10 @@ class DFMConfig(BaseModelConfig):
397
395
  regularization=reg_scale,
398
396
  min_norm=min_eigenval,
399
397
  max_eigenval=max_eigenval,
398
+ # Pass numerical stability parameters from DFMConfig
399
+ ar_clip=self.ar_clip,
400
+ damping_factor=self.damping_factor,
401
+ data_clip=self.data_clip,
400
402
  # Other parameters use defaults from EMConfig
401
403
  )
402
404
 
@@ -453,7 +455,6 @@ class DFMConfig(BaseModelConfig):
453
455
  'data_clip': data.get('data_clip', None),
454
456
  'regularization': data.get('regularization', None),
455
457
  'damping_factor': data.get('damping_factor', None),
456
- 'use_cholesky_filter': data.get('use_cholesky_filter', False),
457
458
  'tent_weights': data.get('tent_weights', None),
458
459
  }
459
460
 
@@ -8,97 +8,12 @@ import numpy as np
8
8
  from typing import Optional, Any
9
9
 
10
10
 
11
- @dataclass
12
- class DFMStateSpaceParams:
13
- """State-space model parameters for DFM (fitted during model training).
14
-
15
- These parameters are computed during DFM.fit() and represent the fitted
16
- state-space model structure. Contains only the state-space parameters,
17
- not training metadata (loglik, num_iter, converged) which are stored
18
- separately in checkpoint dicts.
19
-
20
- Parameters
21
- ----------
22
- A : np.ndarray
23
- Transition matrix (m x m) - VAR dynamics for factors
24
- C : np.ndarray
25
- Observation matrix (N x m) - factor loadings
26
- Q : np.ndarray
27
- Process noise covariance (m x m) - innovation covariance
28
- R : np.ndarray
29
- Observation noise covariance (N x N) - typically diagonal
30
- Z_0 : np.ndarray
31
- Initial state mean (m,) - initial factor values
32
- V_0 : np.ndarray
33
- Initial state covariance (m x m) - initial uncertainty
34
- """
35
- A: np.ndarray # Transition matrix (m x m)
36
- C: np.ndarray # Observation matrix (N x m)
37
- Q: np.ndarray # Process noise covariance (m x m)
38
- R: np.ndarray # Observation noise covariance (N x N)
39
- Z_0: np.ndarray # Initial state mean (m,)
40
- V_0: np.ndarray # Initial state covariance (m x m)
41
-
42
- @classmethod
43
- def from_model(cls, model: Any) -> 'DFMStateSpaceParams':
44
- """Create DFMStateSpaceParams from DFM model instance.
45
-
46
- Parameters
47
- ----------
48
- model : DFM
49
- DFM model instance with fitted parameters
50
-
51
- Returns
52
- -------
53
- DFMStateSpaceParams
54
- State-space parameters dataclass
55
- """
56
- # Get parameters from model (stored after fit)
57
- # First check if training_state is already DFMStateSpaceParams (new format)
58
- if hasattr(model, 'training_state') and model.training_state is not None:
59
- if isinstance(model.training_state, cls):
60
- # Already a DFMStateSpaceParams instance
61
- return model.training_state
62
- # Backward compatibility: If old format training_state (has A, C, Q, R, Z_0, V_0 attributes)
63
- if hasattr(model.training_state, 'A'):
64
- return cls(
65
- A=model.training_state.A,
66
- C=model.training_state.C,
67
- Q=model.training_state.Q,
68
- R=model.training_state.R,
69
- Z_0=model.training_state.Z_0,
70
- V_0=model.training_state.V_0
71
- )
72
-
73
- # Get from model attributes (after _update_parameters is called)
74
- return cls(
75
- A=getattr(model, 'A', None),
76
- C=getattr(model, 'C', None),
77
- Q=getattr(model, 'Q', None),
78
- R=getattr(model, 'R', None),
79
- Z_0=getattr(model, 'Z_0', None),
80
- V_0=getattr(model, 'V_0', None)
81
- )
82
-
83
- def apply_to_model(self, model: Any) -> None:
84
- """Apply state-space parameters to DFM model instance.
85
-
86
- Parameters
87
- ----------
88
- model : DFM
89
- DFM model instance to update
90
- """
91
- model._update_parameters(
92
- self.A, self.C, self.Q, self.R, self.Z_0, self.V_0
93
- )
94
-
95
-
96
11
  @dataclass
97
12
  class DFMModelState:
98
- """DFM model structure and mixed-frequency parameters.
13
+ """DFM model state including structure, mixed-frequency parameters, and state-space parameters.
99
14
 
100
- Consolidates model structure and mixed-frequency state for checkpointing.
101
- This allows save/load methods to be simplified by using a single dataclass.
15
+ Consolidates all DFM model state for checkpointing: model structure, mixed-frequency
16
+ configuration, and fitted state-space parameters.
102
17
 
103
18
  Attributes
104
19
  ----------
@@ -128,6 +43,18 @@ class DFMModelState:
128
43
  Indicator for idiosyncratic components
129
44
  max_lag_size : int, optional
130
45
  Maximum lag size for state dimension
46
+ A : np.ndarray, optional
47
+ Transition matrix (m x m) - VAR dynamics for factors
48
+ C : np.ndarray, optional
49
+ Observation matrix (N x m) - factor loadings
50
+ Q : np.ndarray, optional
51
+ Process noise covariance (m x m) - innovation covariance
52
+ R : np.ndarray, optional
53
+ Observation noise covariance (N x N) - typically diagonal
54
+ Z_0 : np.ndarray, optional
55
+ Initial state mean (m,) - initial factor values
56
+ V_0 : np.ndarray, optional
57
+ Initial state covariance (m x m) - initial uncertainty
131
58
  """
132
59
  num_factors: int
133
60
  r: np.ndarray
@@ -142,6 +69,13 @@ class DFMModelState:
142
69
  frequencies: Optional[np.ndarray] = None
143
70
  idio_indicator: Optional[np.ndarray] = None
144
71
  max_lag_size: Optional[int] = None
72
+ # State-space parameters (fitted during training)
73
+ A: Optional[np.ndarray] = None
74
+ C: Optional[np.ndarray] = None
75
+ Q: Optional[np.ndarray] = None
76
+ R: Optional[np.ndarray] = None
77
+ Z_0: Optional[np.ndarray] = None
78
+ V_0: Optional[np.ndarray] = None
145
79
 
146
80
  @classmethod
147
81
  def from_model(cls, model: Any) -> 'DFMModelState':
@@ -157,6 +91,27 @@ class DFMModelState:
157
91
  DFMModelState
158
92
  Model state dataclass
159
93
  """
94
+ # Get state-space parameters from training_state if available
95
+ A = C = Q = R = Z_0 = V_0 = None
96
+ if hasattr(model, 'training_state') and model.training_state is not None:
97
+ # Handle both old DFMStateSpaceParams format and new DFMModelState format
98
+ ts = model.training_state
99
+ if hasattr(ts, 'A'):
100
+ A = ts.A
101
+ C = ts.C
102
+ Q = ts.Q
103
+ R = ts.R
104
+ Z_0 = ts.Z_0
105
+ V_0 = ts.V_0
106
+ else:
107
+ # Fallback: get from model attributes directly
108
+ A = getattr(model, 'A', None)
109
+ C = getattr(model, 'C', None)
110
+ Q = getattr(model, 'Q', None)
111
+ R = getattr(model, 'R', None)
112
+ Z_0 = getattr(model, 'Z_0', None)
113
+ V_0 = getattr(model, 'V_0', None)
114
+
160
115
  return cls(
161
116
  num_factors=model.num_factors,
162
117
  r=model.r,
@@ -170,7 +125,13 @@ class DFMModelState:
170
125
  tent_weights_dict=getattr(model, '_tent_weights_dict', None),
171
126
  frequencies=getattr(model, '_frequencies', None),
172
127
  idio_indicator=getattr(model, '_idio_indicator', None),
173
- max_lag_size=getattr(model, '_max_lag_size', None)
128
+ max_lag_size=getattr(model, '_max_lag_size', None),
129
+ A=A,
130
+ C=C,
131
+ Q=Q,
132
+ R=R,
133
+ Z_0=Z_0,
134
+ V_0=V_0
174
135
  )
175
136
 
176
137
  def apply_to_model(self, model: Any) -> None:
@@ -194,6 +155,12 @@ class DFMModelState:
194
155
  model._frequencies = self.frequencies
195
156
  model._idio_indicator = self.idio_indicator
196
157
  model._max_lag_size = self.max_lag_size
158
+
159
+ # Apply state-space parameters if available
160
+ if self.A is not None and self.C is not None and self.Q is not None:
161
+ model._update_parameters(
162
+ self.A, self.C, self.Q, self.R, self.Z_0, self.V_0
163
+ )
197
164
 
198
165
  @dataclass
199
166
  class DDFMTrainingState:
@@ -277,3 +244,6 @@ class DDFMStateSpaceParams:
277
244
  H: np.ndarray # Observation matrix (N x m) - theta in paper
278
245
  R: np.ndarray # Observation noise covariance (N x N)
279
246
 
247
+ # Backward compatibility alias
248
+ DFMStateSpaceParams = DFMModelState
249
+