dfm-python 0.5.58__tar.gz → 0.5.59__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 (134) hide show
  1. {dfm_python-0.5.58 → dfm_python-0.5.59}/PKG-INFO +2 -1
  2. {dfm_python-0.5.58 → dfm_python-0.5.59}/pyproject.toml +2 -1
  3. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/functional/em.py +1 -1
  4. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/models/dfm.py +64 -9
  5. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/ssm/kalman.py +3 -3
  6. {dfm_python-0.5.58 → dfm_python-0.5.59}/uv.lock +28 -1
  7. {dfm_python-0.5.58 → dfm_python-0.5.59}/.git +0 -0
  8. {dfm_python-0.5.58 → dfm_python-0.5.59}/.gitignore +0 -0
  9. {dfm_python-0.5.58 → dfm_python-0.5.59}/.python-version +0 -0
  10. {dfm_python-0.5.58 → dfm_python-0.5.59}/MANIFEST.in +0 -0
  11. {dfm_python-0.5.58 → dfm_python-0.5.59}/README.md +0 -0
  12. {dfm_python-0.5.58 → dfm_python-0.5.59}/config/ddfm_exchange.yaml +0 -0
  13. {dfm_python-0.5.58 → dfm_python-0.5.59}/config/ddfm_finance.yaml +0 -0
  14. {dfm_python-0.5.58 → dfm_python-0.5.59}/config/ddfm_macro.yaml +0 -0
  15. {dfm_python-0.5.58 → dfm_python-0.5.59}/config/dfm_finance.yaml +0 -0
  16. {dfm_python-0.5.58 → dfm_python-0.5.59}/config/dfm_macro.yaml +0 -0
  17. {dfm_python-0.5.58 → dfm_python-0.5.59}/data/exchange_rate.csv +0 -0
  18. {dfm_python-0.5.58 → dfm_python-0.5.59}/data/finance.csv +0 -0
  19. {dfm_python-0.5.58 → dfm_python-0.5.59}/data/macro.csv +0 -0
  20. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/__init__.py +0 -0
  21. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/config/__init__.py +0 -0
  22. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/config/adapter.py +0 -0
  23. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/config/constants.py +0 -0
  24. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/config/schema/__init__.py +0 -0
  25. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/config/schema/block.py +0 -0
  26. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/config/schema/model.py +0 -0
  27. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/config/schema/params.py +0 -0
  28. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/config/schema/results.py +0 -0
  29. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/config/types.py +0 -0
  30. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/dataset/__init__.py +0 -0
  31. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/dataset/ddfm_dataset.py +0 -0
  32. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/dataset/dfm_dataset.py +0 -0
  33. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/dataset/time.py +0 -0
  34. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/decoder/__init__.py +0 -0
  35. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/decoder/base.py +0 -0
  36. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/decoder/linear.py +0 -0
  37. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/decoder/mlp.py +0 -0
  38. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/encoder/__init__.py +0 -0
  39. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/encoder/base.py +0 -0
  40. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/encoder/pca.py +0 -0
  41. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/encoder/simple_autoencoder.py +0 -0
  42. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/functional/__init__.py +0 -0
  43. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/functional/dfm_block.py +0 -0
  44. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/logger/__init__.py +0 -0
  45. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/logger/inference_logger.py +0 -0
  46. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/logger/logger.py +0 -0
  47. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/logger/train_logger.py +0 -0
  48. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/models/__init__.py +0 -0
  49. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/models/base.py +0 -0
  50. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/models/ddfm.py +0 -0
  51. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/numeric/builder.py +0 -0
  52. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/numeric/estimator.py +0 -0
  53. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/numeric/stability.py +0 -0
  54. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/numeric/statistic.py +0 -0
  55. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/numeric/tent.py +0 -0
  56. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/numeric/validator.py +0 -0
  57. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/ssm/__init__.py +0 -0
  58. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/utils/__init__.py +0 -0
  59. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/utils/checkpoint.py +0 -0
  60. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/utils/errors.py +0 -0
  61. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/utils/helper.py +0 -0
  62. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/utils/loss.py +0 -0
  63. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/utils/metric.py +0 -0
  64. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/utils/misc.py +0 -0
  65. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/dfm_python/utils/validation.py +0 -0
  66. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/__init__.py +0 -0
  67. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/config/__init__.py +0 -0
  68. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/config/schema/__init__.py +0 -0
  69. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/config/schema/test_block.py +0 -0
  70. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/config/schema/test_model.py +0 -0
  71. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/config/schema/test_results.py +0 -0
  72. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/config/test_adapter.py +0 -0
  73. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/config/test_constants.py +0 -0
  74. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/config/test_tent_weights_config.py +0 -0
  75. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/config/test_types.py +0 -0
  76. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/conftest.py +0 -0
  77. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/dataset/__init__.py +0 -0
  78. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/dataset/test_dataloader.py +0 -0
  79. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/dataset/test_dataset.py +0 -0
  80. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/dataset/test_process.py +0 -0
  81. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/decoder/__init__.py +0 -0
  82. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/decoder/test_linear.py +0 -0
  83. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/decoder/test_mlp.py +0 -0
  84. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/encoder/__init__.py +0 -0
  85. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/encoder/test_base.py +0 -0
  86. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/encoder/test_pca.py +0 -0
  87. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/encoder/test_simple_encoder.py +0 -0
  88. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/functional/__init__.py +0 -0
  89. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/functional/test_dfm_block.py +0 -0
  90. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/functional/test_em.py +0 -0
  91. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/logger/__init__.py +0 -0
  92. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/logger/test_inference_logger.py +0 -0
  93. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/logger/test_logger.py +0 -0
  94. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/logger/test_train_logger.py +0 -0
  95. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/__init__.py +0 -0
  96. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_base.py +0 -0
  97. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_ddfm.py +0 -0
  98. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_ddfm_batchnorm_placement.py +0 -0
  99. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_dfm.py +0 -0
  100. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_dfm_fixes.py +0 -0
  101. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_dfm_result_caching.py +0 -0
  102. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_dfm_save_load.py +0 -0
  103. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_dfm_save_load_integration.py +0 -0
  104. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_dfm_save_load_mock.py +0 -0
  105. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_dfm_training_pipeline_save.py +0 -0
  106. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/models/test_tensorflow_batchnorm_comparison.py +0 -0
  107. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/numeric/__init__.py +0 -0
  108. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/numeric/test_builder.py +0 -0
  109. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/numeric/test_estimator.py +0 -0
  110. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/numeric/test_stability.py +0 -0
  111. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/numeric/test_tent.py +0 -0
  112. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/numeric/test_validator.py +0 -0
  113. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/ssm/__init__.py +0 -0
  114. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/ssm/test_arma.py +0 -0
  115. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/ssm/test_companion.py +0 -0
  116. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/ssm/test_kalman.py +0 -0
  117. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/ssm/test_kalman_progress.py +0 -0
  118. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/ssm/test_progress_bar_exact_scenario.py +0 -0
  119. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/ssm/test_progress_bar_spam_fix.py +0 -0
  120. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/ssm/test_structural.py +0 -0
  121. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/utils/__init__.py +0 -0
  122. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/utils/test_common.py +0 -0
  123. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/utils/test_errors.py +0 -0
  124. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/utils/test_helper.py +0 -0
  125. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/utils/test_metric.py +0 -0
  126. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/utils/test_misc.py +0 -0
  127. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/utils/test_tensor_utils.py +0 -0
  128. {dfm_python-0.5.58 → dfm_python-0.5.59}/src/test/utils/test_validation.py +0 -0
  129. {dfm_python-0.5.58 → dfm_python-0.5.59}/tutorial/tutorial_exchange_ddfm.py +0 -0
  130. {dfm_python-0.5.58 → dfm_python-0.5.59}/tutorial/tutorial_finance_ddfm.py +0 -0
  131. {dfm_python-0.5.58 → dfm_python-0.5.59}/tutorial/tutorial_finance_dfm.py +0 -0
  132. {dfm_python-0.5.58 → dfm_python-0.5.59}/tutorial/tutorial_illness_ddfm.py +0 -0
  133. {dfm_python-0.5.58 → dfm_python-0.5.59}/tutorial/tutorial_macro_ddfm.py +0 -0
  134. {dfm_python-0.5.58 → dfm_python-0.5.59}/tutorial/tutorial_macro_dfm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dfm-python
3
- Version: 0.5.58
3
+ Version: 0.5.59
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
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Topic :: Scientific/Engineering
20
20
  Requires-Python: >=3.10
21
+ Requires-Dist: build>=1.4.0
21
22
  Requires-Dist: einops>=0.8.1
22
23
  Requires-Dist: hydra-core>=1.3.2
23
24
  Requires-Dist: matplotlib>=3.5.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dfm-python"
3
- version = "0.5.58"
3
+ version = "0.5.59"
4
4
  description = "Dynamic Factor Model (DFM) estimation and forecasting in Python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -20,6 +20,7 @@ classifiers = [
20
20
  "Topic :: Scientific/Engineering",
21
21
  ]
22
22
  dependencies = [
23
+ "build>=1.4.0",
23
24
  "einops>=0.8.1",
24
25
  "hydra-core>=1.3.2",
25
26
  "matplotlib>=3.5.0",
@@ -1233,7 +1233,7 @@ def em_step(
1233
1233
  _logger.info(f" E-step: Running Kalman filter + smoother (T={T}, N={N}, m={m}, ~{ops_estimate:.1f}B ops)...")
1234
1234
  _logger.info(f" E-step: Filter is fast (~1s), but smooth may take 5-7 minutes (bottleneck)")
1235
1235
  EZ, V_smooth, VVsmooth, loglik = kalman_filter.filter_and_smooth(X_masked)
1236
-
1236
+
1237
1237
  # Keep float64 for numerical stability (prevents V_smooth accumulation overflow)
1238
1238
  # Do NOT convert to float32 - maintain float64 precision throughout M-step
1239
1239
  # Float64 is critical for large state spaces (m=183) and long time series (T=2135)
@@ -1319,7 +1319,33 @@ class DFM(BaseFactorModel):
1319
1319
 
1320
1320
  # Run filter and smooth on new data
1321
1321
  y_masked = np.ma.masked_invalid(data_new)
1322
- Z_new, V_smooth_new, _, _ = kalman_new.filter_and_smooth(y_masked)
1322
+
1323
+ # ------------------------------------------------------------------
1324
+ # IMPORTANT (pykalman quirk):
1325
+ # pykalman.KalmanFilter._parse_observations() transposes when input has
1326
+ # shape (1, n_dim_obs), interpreting it as (n_dim_obs, 1). For our
1327
+ # update() case, (1, N) means 1 timestep with N series. This transpose
1328
+ # causes n_dim_obs=1 and breaks CholeskyKalmanFilter internals.
1329
+ #
1330
+ # Workaround: for a single new timestep, use filter_update() directly
1331
+ # with a 1D observation vector (n_dim_obs,), avoiding _parse_observations.
1332
+ # ------------------------------------------------------------------
1333
+ if hasattr(data_new, "shape") and len(data_new.shape) == 2 and int(data_new.shape[0]) == 1:
1334
+ pk = getattr(kalman_new, "_pykalman", None)
1335
+ if pk is None:
1336
+ raise ModelNotTrainedError("Kalman filter not initialized for update()")
1337
+
1338
+ # 1D observation vector for this timestep
1339
+ obs = np.ma.masked_invalid(data_new[0])
1340
+ next_mean, _next_cov = pk.filter_update(
1341
+ filtered_state_mean=Z_last,
1342
+ filtered_state_covariance=V_last,
1343
+ observation=obs,
1344
+ )
1345
+ Z_new = np.asarray(next_mean, dtype=DEFAULT_DTYPE)[np.newaxis, :]
1346
+ V_smooth_new = None
1347
+ else:
1348
+ Z_new, V_smooth_new, _, _ = kalman_new.filter_and_smooth(y_masked)
1323
1349
 
1324
1350
  # Update model state: append new factors and data
1325
1351
  # Concatenate new factors to existing result.Z
@@ -1509,6 +1535,18 @@ class DFM(BaseFactorModel):
1509
1535
  except (ModelNotInitializedError, AttributeError):
1510
1536
  pass
1511
1537
  target_series, target_indices = resolve_target_series(dataset, series_ids, result, self.__class__.__name__)
1538
+
1539
+ # Fallback: If no Dataset is attached, use config.target_series.
1540
+ # This is needed for checkpoints used in external pipelines where dataset.pkl
1541
+ # is missing or not attached to the model instance.
1542
+ if (target_series is None or len(target_series) == 0 or target_indices is None) and getattr(self, "_config", None) is not None:
1543
+ cfg_ts = getattr(self._config, "target_series", None)
1544
+ if cfg_ts is not None:
1545
+ cfg_ts_list = cfg_ts if isinstance(cfg_ts, list) else [cfg_ts]
1546
+ cfg_indices = [series_ids.index(t) for t in cfg_ts_list if series_ids is not None and t in series_ids]
1547
+
1548
+ if cfg_indices:
1549
+ target_series, target_indices = cfg_ts_list, cfg_indices
1512
1550
 
1513
1551
  if target_series is None or len(target_series) == 0:
1514
1552
  raise ValueError(
@@ -1747,19 +1785,36 @@ class DFM(BaseFactorModel):
1747
1785
  raise FileNotFoundError(f"Checkpoint file not found: {path}")
1748
1786
 
1749
1787
  # Try loading with error handling for corrupted files
1788
+ # NOTE: Our training pipeline typically saves with joblib (for speed/compression),
1789
+ # but older checkpoints may be plain pickle. Support both.
1790
+ checkpoint = None
1750
1791
  try:
1751
- with open(path, 'rb') as f:
1752
- checkpoint = pickle.load(f)
1753
- except (pickle.UnpicklingError, EOFError, ValueError) as e:
1754
- raise RuntimeError(
1755
- f"Failed to load checkpoint from {path}. "
1756
- f"The file may be corrupted. Error: {e}. "
1757
- f"Try re-training the model to generate a new checkpoint."
1758
- ) from e
1792
+ import joblib # local import to avoid hard dependency at import-time
1793
+ checkpoint = joblib.load(path)
1794
+ except Exception:
1795
+ # Fallback to plain pickle
1796
+ try:
1797
+ with open(path, 'rb') as f:
1798
+ checkpoint = pickle.load(f)
1799
+ except (pickle.UnpicklingError, EOFError, ValueError) as e:
1800
+ raise RuntimeError(
1801
+ f"Failed to load checkpoint from {path}. "
1802
+ f"The file may be corrupted or saved in an unsupported format. Error: {e}. "
1803
+ f"Try re-training the model to generate a new checkpoint."
1804
+ ) from e
1759
1805
 
1760
1806
  # Use checkpoint config if not provided
1761
1807
  if config is None:
1762
1808
  config = checkpoint.get('config')
1809
+
1810
+ # If target_series was stored separately in the checkpoint (common in our pipeline),
1811
+ # inject it into config so predict() can resolve targets without a Dataset attached.
1812
+ try:
1813
+ ckpt_target_series = checkpoint.get('target_series')
1814
+ if config is not None and getattr(config, 'target_series', None) is None and ckpt_target_series is not None:
1815
+ config.target_series = ckpt_target_series if isinstance(ckpt_target_series, list) else [ckpt_target_series]
1816
+ except Exception:
1817
+ pass
1763
1818
 
1764
1819
  # Get model state (new format preferred, fallback to old format)
1765
1820
  model_state = checkpoint.get('model_state')
@@ -204,7 +204,7 @@ class DFMKalmanFilter:
204
204
  "DFMKalmanFilter parameters not initialized. "
205
205
  "Call update_parameters() first."
206
206
  )
207
-
207
+
208
208
  # Get filtered states first (needed for smoother)
209
209
  transition_offsets = getattr(self._pykalman, 'transition_offsets', None)
210
210
  observation_offsets = getattr(self._pykalman, 'observation_offsets', None)
@@ -241,7 +241,7 @@ class DFMKalmanFilter:
241
241
 
242
242
  # Run filter
243
243
  predicted_state_means, predicted_state_covariances, _, filtered_state_means, filtered_state_covariances = run_filter()
244
-
244
+
245
245
  filter_time = time_module.time() - filter_start
246
246
  _logger.info(f" Filter: Completed in {filter_time:.2f}s ({filter_time/T*1000:.2f}ms/timestep)")
247
247
 
@@ -266,7 +266,7 @@ class DFMKalmanFilter:
266
266
  # This is much cheaper than full eigendecomposition (O(m²) vs O(m³))
267
267
  from ..config.constants import MIN_EIGENVALUE
268
268
  regularization = max(1e-6, MIN_EIGENVALUE * 100) # 1e-4 for stability
269
-
269
+
270
270
  _logger.info(f" Smooth: Stabilizing {len(predicted_state_covariances)} covariance matrices "
271
271
  f"(regularization={regularization:.2e})")
272
272
 
@@ -270,6 +270,22 @@ wheels = [
270
270
  { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" },
271
271
  ]
272
272
 
273
+ [[package]]
274
+ name = "build"
275
+ version = "1.4.0"
276
+ source = { registry = "https://pypi.org/simple" }
277
+ dependencies = [
278
+ { name = "colorama", marker = "os_name == 'nt'" },
279
+ { name = "importlib-metadata", marker = "python_full_version < '3.10.2'" },
280
+ { name = "packaging" },
281
+ { name = "pyproject-hooks" },
282
+ { name = "tomli", marker = "python_full_version < '3.11'" },
283
+ ]
284
+ sdist = { url = "https://files.pythonhosted.org/packages/42/18/94eaffda7b329535d91f00fe605ab1f1e5cd68b2074d03f255c7d250687d/build-1.4.0.tar.gz", hash = "sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936", size = 50054, upload-time = "2026-01-08T16:41:47.696Z" }
285
+ wheels = [
286
+ { url = "https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl", hash = "sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596", size = 24141, upload-time = "2026-01-08T16:41:46.453Z" },
287
+ ]
288
+
273
289
  [[package]]
274
290
  name = "certifi"
275
291
  version = "2025.11.12"
@@ -859,9 +875,10 @@ wheels = [
859
875
 
860
876
  [[package]]
861
877
  name = "dfm-python"
862
- version = "0.5.56"
878
+ version = "0.5.59"
863
879
  source = { editable = "." }
864
880
  dependencies = [
881
+ { name = "build" },
865
882
  { name = "einops" },
866
883
  { name = "hydra-core" },
867
884
  { name = "matplotlib" },
@@ -914,6 +931,7 @@ dev = [
914
931
 
915
932
  [package.metadata]
916
933
  requires-dist = [
934
+ { name = "build", specifier = ">=1.4.0" },
917
935
  { name = "dfm-python", extras = ["hydra", "db", "dev", "deep", "transform"], marker = "extra == 'all'" },
918
936
  { name = "einops", specifier = ">=0.8.1" },
919
937
  { name = "hydra-core", specifier = ">=1.3.2" },
@@ -2801,6 +2819,15 @@ wheels = [
2801
2819
  { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" },
2802
2820
  ]
2803
2821
 
2822
+ [[package]]
2823
+ name = "pyproject-hooks"
2824
+ version = "1.2.0"
2825
+ source = { registry = "https://pypi.org/simple" }
2826
+ sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" }
2827
+ wheels = [
2828
+ { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" },
2829
+ ]
2830
+
2804
2831
  [[package]]
2805
2832
  name = "pytest"
2806
2833
  version = "9.0.1"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes