pymc-extras 0.6.0__py3-none-any.whl → 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymc_extras/distributions/timeseries.py +10 -10
- pymc_extras/inference/dadvi/dadvi.py +14 -83
- pymc_extras/inference/laplace_approx/laplace.py +187 -159
- pymc_extras/inference/pathfinder/pathfinder.py +12 -7
- pymc_extras/inference/smc/sampling.py +2 -2
- pymc_extras/model/marginal/distributions.py +4 -2
- pymc_extras/model/marginal/marginal_model.py +12 -2
- pymc_extras/prior.py +3 -3
- pymc_extras/statespace/core/properties.py +276 -0
- pymc_extras/statespace/core/statespace.py +182 -45
- pymc_extras/statespace/filters/distributions.py +19 -34
- pymc_extras/statespace/filters/kalman_filter.py +13 -12
- pymc_extras/statespace/filters/kalman_smoother.py +2 -2
- pymc_extras/statespace/models/DFM.py +179 -168
- pymc_extras/statespace/models/ETS.py +177 -151
- pymc_extras/statespace/models/SARIMAX.py +149 -152
- pymc_extras/statespace/models/VARMAX.py +134 -145
- pymc_extras/statespace/models/__init__.py +8 -1
- pymc_extras/statespace/models/structural/__init__.py +30 -8
- pymc_extras/statespace/models/structural/components/autoregressive.py +87 -45
- pymc_extras/statespace/models/structural/components/cycle.py +119 -80
- pymc_extras/statespace/models/structural/components/level_trend.py +95 -42
- pymc_extras/statespace/models/structural/components/measurement_error.py +27 -17
- pymc_extras/statespace/models/structural/components/regression.py +105 -68
- pymc_extras/statespace/models/structural/components/seasonality.py +138 -100
- pymc_extras/statespace/models/structural/core.py +397 -286
- pymc_extras/statespace/models/utilities.py +5 -20
- {pymc_extras-0.6.0.dist-info → pymc_extras-0.8.0.dist-info}/METADATA +4 -4
- {pymc_extras-0.6.0.dist-info → pymc_extras-0.8.0.dist-info}/RECORD +31 -30
- {pymc_extras-0.6.0.dist-info → pymc_extras-0.8.0.dist-info}/WHEEL +0 -0
- {pymc_extras-0.6.0.dist-info → pymc_extras-0.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
from collections.abc import Sequence
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
1
|
import numpy as np
|
|
5
2
|
import pytensor
|
|
6
3
|
import pytensor.tensor as pt
|
|
7
4
|
|
|
8
5
|
from pytensor.compile.mode import Mode
|
|
9
|
-
from pytensor.tensor.
|
|
10
|
-
|
|
6
|
+
from pytensor.tensor.linalg import solve_discrete_lyapunov
|
|
7
|
+
|
|
8
|
+
from pymc_extras.statespace.core.properties import (
|
|
9
|
+
Coord,
|
|
10
|
+
Data,
|
|
11
|
+
Parameter,
|
|
12
|
+
Shock,
|
|
13
|
+
State,
|
|
14
|
+
)
|
|
11
15
|
from pymc_extras.statespace.core.statespace import PyMCStateSpace
|
|
12
|
-
from pymc_extras.statespace.models.utilities import
|
|
16
|
+
from pymc_extras.statespace.models.utilities import validate_names
|
|
13
17
|
from pymc_extras.statespace.utils.constants import (
|
|
14
18
|
ALL_STATE_AUX_DIM,
|
|
15
19
|
ALL_STATE_DIM,
|
|
@@ -212,110 +216,96 @@ class BayesianVARMAX(PyMCStateSpace):
|
|
|
212
216
|
"sigma_obs": k_endog * self.measurement_error,
|
|
213
217
|
}
|
|
214
218
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
names.remove("P0")
|
|
220
|
-
names.remove("x0")
|
|
221
|
-
if not self.measurement_error:
|
|
222
|
-
names.remove("sigma_obs")
|
|
223
|
-
if self.p == 0:
|
|
224
|
-
names.remove("ar_params")
|
|
225
|
-
if self.q == 0:
|
|
226
|
-
names.remove("ma_params")
|
|
227
|
-
|
|
228
|
-
# Add exogenous regression coefficents rather than remove, since we might have to handle
|
|
229
|
-
# several (if self.exog_state_names is a dict)
|
|
230
|
-
if isinstance(self.exog_state_names, list):
|
|
231
|
-
names.append("beta_exog")
|
|
232
|
-
elif isinstance(self.exog_state_names, dict):
|
|
233
|
-
names.extend([f"beta_{name}" for name in self.exog_state_names.keys()])
|
|
219
|
+
def set_parameters(self) -> Parameter | tuple[Parameter, ...] | None:
|
|
220
|
+
k_endog = self.k_endog
|
|
221
|
+
k_states = self.k_states
|
|
222
|
+
k_posdef = self.k_posdef
|
|
234
223
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
@property
|
|
238
|
-
def param_info(self) -> dict[str, dict[str, Any]]:
|
|
239
|
-
info = {
|
|
240
|
-
"x0": {
|
|
241
|
-
"shape": (self.k_states,),
|
|
242
|
-
"constraints": None,
|
|
243
|
-
},
|
|
244
|
-
"P0": {
|
|
245
|
-
"shape": (self.k_states, self.k_states),
|
|
246
|
-
"constraints": "Positive Semi-definite",
|
|
247
|
-
},
|
|
248
|
-
"sigma_obs": {
|
|
249
|
-
"shape": (self.k_endog, self.k_endog),
|
|
250
|
-
"constraints": "Positive Semi-definite",
|
|
251
|
-
},
|
|
252
|
-
"state_cov": {
|
|
253
|
-
"shape": (self.k_posdef, self.k_posdef),
|
|
254
|
-
"constraints": "Positive Semi-definite",
|
|
255
|
-
},
|
|
256
|
-
"ar_params": {
|
|
257
|
-
"shape": (self.k_endog, self.p, self.k_endog),
|
|
258
|
-
"constraints": "None",
|
|
259
|
-
},
|
|
260
|
-
"ma_params": {
|
|
261
|
-
"shape": (self.k_endog, self.q, self.k_endog),
|
|
262
|
-
"constraints": "None",
|
|
263
|
-
},
|
|
264
|
-
}
|
|
224
|
+
parameters = []
|
|
265
225
|
|
|
266
|
-
if
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
226
|
+
if not self.stationary_initialization:
|
|
227
|
+
parameters.append(
|
|
228
|
+
Parameter(
|
|
229
|
+
name="x0",
|
|
230
|
+
shape=(k_states,),
|
|
231
|
+
dims=(ALL_STATE_DIM,),
|
|
232
|
+
constraints=None,
|
|
233
|
+
)
|
|
234
|
+
)
|
|
235
|
+
parameters.append(
|
|
236
|
+
Parameter(
|
|
237
|
+
name="P0",
|
|
238
|
+
shape=(k_states, k_states),
|
|
239
|
+
dims=(ALL_STATE_DIM, ALL_STATE_AUX_DIM),
|
|
240
|
+
constraints="Positive Semi-definite",
|
|
241
|
+
)
|
|
242
|
+
)
|
|
272
243
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
244
|
+
if self.p > 0:
|
|
245
|
+
parameters.append(
|
|
246
|
+
Parameter(
|
|
247
|
+
name="ar_params",
|
|
248
|
+
shape=(k_endog, self.p, k_endog),
|
|
249
|
+
dims=(OBS_STATE_DIM, AR_PARAM_DIM, OBS_STATE_AUX_DIM),
|
|
250
|
+
constraints=None,
|
|
251
|
+
)
|
|
252
|
+
)
|
|
280
253
|
|
|
281
|
-
|
|
282
|
-
|
|
254
|
+
if self.q > 0:
|
|
255
|
+
parameters.append(
|
|
256
|
+
Parameter(
|
|
257
|
+
name="ma_params",
|
|
258
|
+
shape=(k_endog, self.q, k_endog),
|
|
259
|
+
dims=(OBS_STATE_DIM, MA_PARAM_DIM, OBS_STATE_AUX_DIM),
|
|
260
|
+
constraints=None,
|
|
261
|
+
)
|
|
262
|
+
)
|
|
283
263
|
|
|
284
|
-
|
|
264
|
+
parameters.append(
|
|
265
|
+
Parameter(
|
|
266
|
+
name="state_cov",
|
|
267
|
+
shape=(k_posdef, k_posdef),
|
|
268
|
+
dims=(SHOCK_DIM, SHOCK_AUX_DIM),
|
|
269
|
+
constraints="Positive Semi-definite",
|
|
270
|
+
)
|
|
271
|
+
)
|
|
285
272
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
273
|
+
if self.measurement_error:
|
|
274
|
+
parameters.append(
|
|
275
|
+
Parameter(
|
|
276
|
+
name="sigma_obs",
|
|
277
|
+
shape=(k_endog,),
|
|
278
|
+
dims=(OBS_STATE_DIM,),
|
|
279
|
+
constraints="Positive",
|
|
280
|
+
)
|
|
281
|
+
)
|
|
289
282
|
|
|
283
|
+
# Handle exogenous parameters
|
|
290
284
|
if isinstance(self.exog_state_names, list):
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
"
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
285
|
+
k_exog = len(self.exog_state_names)
|
|
286
|
+
parameters.append(
|
|
287
|
+
Parameter(
|
|
288
|
+
name="beta_exog",
|
|
289
|
+
shape=(k_endog, k_exog),
|
|
290
|
+
dims=(OBS_STATE_DIM, EXOG_STATE_DIM),
|
|
291
|
+
constraints=None,
|
|
292
|
+
)
|
|
293
|
+
)
|
|
298
294
|
elif isinstance(self.exog_state_names, dict):
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
295
|
+
for name, exog_names in self.exog_state_names.items():
|
|
296
|
+
k_exog = len(exog_names)
|
|
297
|
+
parameters.append(
|
|
298
|
+
Parameter(
|
|
299
|
+
name=f"beta_{name}",
|
|
300
|
+
shape=(k_exog,),
|
|
301
|
+
dims=(f"{EXOG_STATE_DIM}_{name}",),
|
|
302
|
+
constraints=None,
|
|
303
|
+
)
|
|
304
|
+
)
|
|
306
305
|
|
|
307
|
-
return
|
|
306
|
+
return tuple(parameters)
|
|
308
307
|
|
|
309
|
-
|
|
310
|
-
def data_names(self) -> list[str]:
|
|
311
|
-
if isinstance(self.exog_state_names, list):
|
|
312
|
-
return ["exogenous_data"]
|
|
313
|
-
elif isinstance(self.exog_state_names, dict):
|
|
314
|
-
return [f"{endog_state}_exogenous_data" for endog_state in self.exog_state_names.keys()]
|
|
315
|
-
return []
|
|
316
|
-
|
|
317
|
-
@property
|
|
318
|
-
def state_names(self):
|
|
308
|
+
def set_states(self) -> State | tuple[State, ...] | None:
|
|
319
309
|
state_names = self.endog_names.copy()
|
|
320
310
|
state_names += [
|
|
321
311
|
f"L{i + 1}_{state}" for i in range(self.p - 1) for state in self.endog_names
|
|
@@ -324,66 +314,65 @@ class BayesianVARMAX(PyMCStateSpace):
|
|
|
324
314
|
f"L{i + 1}_{state}_innov" for i in range(self.q) for state in self.endog_names
|
|
325
315
|
]
|
|
326
316
|
|
|
327
|
-
|
|
317
|
+
hidden_states = [State(name=name, observed=False) for name in state_names]
|
|
328
318
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
return self.endog_names
|
|
319
|
+
# The first k_endog states are observed
|
|
320
|
+
observed_states = [State(name=name, observed=True) for name in self.endog_names]
|
|
332
321
|
|
|
333
|
-
|
|
334
|
-
def shock_names(self):
|
|
335
|
-
return self.endog_names
|
|
322
|
+
return *hidden_states, *observed_states
|
|
336
323
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
raise NotImplementedError
|
|
324
|
+
def set_shocks(self) -> Shock | tuple[Shock, ...] | None:
|
|
325
|
+
return tuple(Shock(name=name) for name in self.endog_names)
|
|
340
326
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
coords = make_default_coords(self)
|
|
344
|
-
if self.p > 0:
|
|
345
|
-
coords.update({AR_PARAM_DIM: list(range(1, self.p + 1))})
|
|
346
|
-
if self.q > 0:
|
|
347
|
-
coords.update({MA_PARAM_DIM: list(range(1, self.q + 1))})
|
|
327
|
+
def set_data_info(self) -> tuple[Data, ...] | None:
|
|
328
|
+
data = []
|
|
348
329
|
|
|
349
330
|
if isinstance(self.exog_state_names, list):
|
|
350
|
-
|
|
331
|
+
k_exog = len(self.exog_state_names)
|
|
332
|
+
data.append(
|
|
333
|
+
Data(
|
|
334
|
+
name="exogenous_data",
|
|
335
|
+
shape=(None, k_exog),
|
|
336
|
+
dims=(TIME_DIM, EXOG_STATE_DIM),
|
|
337
|
+
is_exogenous=True,
|
|
338
|
+
)
|
|
339
|
+
)
|
|
351
340
|
elif isinstance(self.exog_state_names, dict):
|
|
352
|
-
for
|
|
353
|
-
|
|
341
|
+
for endog_state, exog_names in self.exog_state_names.items():
|
|
342
|
+
k_exog = len(exog_names)
|
|
343
|
+
data.append(
|
|
344
|
+
Data(
|
|
345
|
+
name=f"{endog_state}_exogenous_data",
|
|
346
|
+
shape=(None, k_exog),
|
|
347
|
+
dims=(TIME_DIM, f"{EXOG_STATE_DIM}_{endog_state}"),
|
|
348
|
+
is_exogenous=True,
|
|
349
|
+
)
|
|
350
|
+
)
|
|
354
351
|
|
|
355
|
-
return
|
|
352
|
+
return tuple(data)
|
|
356
353
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
coord_map = {
|
|
360
|
-
"x0": (ALL_STATE_DIM,),
|
|
361
|
-
"P0": (ALL_STATE_DIM, ALL_STATE_AUX_DIM),
|
|
362
|
-
"sigma_obs": (OBS_STATE_DIM,),
|
|
363
|
-
"state_cov": (SHOCK_DIM, SHOCK_AUX_DIM),
|
|
364
|
-
"ar_params": (OBS_STATE_DIM, AR_PARAM_DIM, OBS_STATE_AUX_DIM),
|
|
365
|
-
"ma_params": (OBS_STATE_DIM, MA_PARAM_DIM, OBS_STATE_AUX_DIM),
|
|
366
|
-
}
|
|
354
|
+
def set_coords(self) -> Coord | tuple[Coord, ...] | None:
|
|
355
|
+
coords = list(self.default_coords())
|
|
367
356
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
if self.q
|
|
373
|
-
|
|
374
|
-
if self.stationary_initialization:
|
|
375
|
-
del coord_map["P0"]
|
|
376
|
-
del coord_map["x0"]
|
|
357
|
+
# AR/MA param coords
|
|
358
|
+
if self.p > 0:
|
|
359
|
+
coords.append(Coord(dimension=AR_PARAM_DIM, labels=tuple(range(1, self.p + 1))))
|
|
360
|
+
|
|
361
|
+
if self.q > 0:
|
|
362
|
+
coords.append(Coord(dimension=MA_PARAM_DIM, labels=tuple(range(1, self.q + 1))))
|
|
377
363
|
|
|
364
|
+
# Exogenous coords
|
|
378
365
|
if isinstance(self.exog_state_names, list):
|
|
379
|
-
|
|
366
|
+
coords.append(Coord(dimension=EXOG_STATE_DIM, labels=tuple(self.exog_state_names)))
|
|
380
367
|
elif isinstance(self.exog_state_names, dict):
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
for name in self.exog_state_names.keys():
|
|
384
|
-
coord_map[f"beta_{name}"] = (f"{EXOG_STATE_DIM}_{name}",)
|
|
368
|
+
for name, exog_names in self.exog_state_names.items():
|
|
369
|
+
coords.append(Coord(dimension=f"{EXOG_STATE_DIM}_{name}", labels=tuple(exog_names)))
|
|
385
370
|
|
|
386
|
-
return
|
|
371
|
+
return tuple(coords)
|
|
372
|
+
|
|
373
|
+
@property
|
|
374
|
+
def default_priors(self):
|
|
375
|
+
raise NotImplementedError
|
|
387
376
|
|
|
388
377
|
def add_default_priors(self):
|
|
389
378
|
raise NotImplementedError
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
from pymc_extras.statespace.models import structural
|
|
2
|
+
from pymc_extras.statespace.models.DFM import BayesianDynamicFactor
|
|
2
3
|
from pymc_extras.statespace.models.ETS import BayesianETS
|
|
3
4
|
from pymc_extras.statespace.models.SARIMAX import BayesianSARIMAX
|
|
4
5
|
from pymc_extras.statespace.models.VARMAX import BayesianVARMAX
|
|
5
6
|
|
|
6
|
-
__all__ = [
|
|
7
|
+
__all__ = [
|
|
8
|
+
"BayesianSARIMAX",
|
|
9
|
+
"BayesianVARMAX",
|
|
10
|
+
"BayesianETS",
|
|
11
|
+
"BayesianDynamicFactor",
|
|
12
|
+
"structural",
|
|
13
|
+
]
|
|
@@ -1,21 +1,43 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
1
3
|
from pymc_extras.statespace.models.structural.components.autoregressive import (
|
|
2
|
-
|
|
4
|
+
Autoregressive,
|
|
3
5
|
)
|
|
4
|
-
from pymc_extras.statespace.models.structural.components.cycle import
|
|
5
|
-
from pymc_extras.statespace.models.structural.components.level_trend import
|
|
6
|
+
from pymc_extras.statespace.models.structural.components.cycle import Cycle
|
|
7
|
+
from pymc_extras.statespace.models.structural.components.level_trend import LevelTrend
|
|
6
8
|
from pymc_extras.statespace.models.structural.components.measurement_error import MeasurementError
|
|
7
|
-
from pymc_extras.statespace.models.structural.components.regression import
|
|
9
|
+
from pymc_extras.statespace.models.structural.components.regression import Regression
|
|
8
10
|
from pymc_extras.statespace.models.structural.components.seasonality import (
|
|
9
11
|
FrequencySeasonality,
|
|
10
12
|
TimeSeasonality,
|
|
11
13
|
)
|
|
12
14
|
|
|
15
|
+
_DEPRECATED_NAMES = {
|
|
16
|
+
"LevelTrendComponent": LevelTrend,
|
|
17
|
+
"CycleComponent": Cycle,
|
|
18
|
+
"RegressionComponent": Regression,
|
|
19
|
+
"AutoregressiveComponent": Autoregressive,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def __getattr__(name: str):
|
|
24
|
+
if name in _DEPRECATED_NAMES:
|
|
25
|
+
warnings.warn(
|
|
26
|
+
f"{name} is deprecated and will be removed in a future release. "
|
|
27
|
+
f"Use {_DEPRECATED_NAMES[name].__name__} instead.",
|
|
28
|
+
FutureWarning,
|
|
29
|
+
stacklevel=2,
|
|
30
|
+
)
|
|
31
|
+
return _DEPRECATED_NAMES[name]
|
|
32
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
33
|
+
|
|
34
|
+
|
|
13
35
|
__all__ = [
|
|
14
|
-
"
|
|
15
|
-
"
|
|
36
|
+
"Autoregressive",
|
|
37
|
+
"Cycle",
|
|
16
38
|
"FrequencySeasonality",
|
|
17
|
-
"
|
|
39
|
+
"LevelTrend",
|
|
18
40
|
"MeasurementError",
|
|
19
|
-
"
|
|
41
|
+
"Regression",
|
|
20
42
|
"TimeSeasonality",
|
|
21
43
|
]
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
2
4
|
import pytensor.tensor as pt
|
|
3
5
|
|
|
6
|
+
from pymc_extras.statespace.core.properties import (
|
|
7
|
+
Coord,
|
|
8
|
+
Parameter,
|
|
9
|
+
Shock,
|
|
10
|
+
State,
|
|
11
|
+
)
|
|
4
12
|
from pymc_extras.statespace.models.structural.core import Component
|
|
5
13
|
from pymc_extras.statespace.models.structural.utils import order_to_mask
|
|
6
|
-
from pymc_extras.statespace.utils.constants import AR_PARAM_DIM
|
|
7
14
|
|
|
8
15
|
|
|
9
|
-
class
|
|
16
|
+
class Autoregressive(Component):
|
|
10
17
|
r"""
|
|
11
18
|
Autoregressive timeseries component
|
|
12
19
|
|
|
@@ -58,8 +65,8 @@ class AutoregressiveComponent(Component):
|
|
|
58
65
|
import pymc as pm
|
|
59
66
|
import pytensor.tensor as pt
|
|
60
67
|
|
|
61
|
-
trend = st.
|
|
62
|
-
ar = st.
|
|
68
|
+
trend = st.LevelTrend(order=1, innovations_order=0)
|
|
69
|
+
ar = st.Autoregressive(2)
|
|
63
70
|
ss_mod = (trend + ar).build()
|
|
64
71
|
|
|
65
72
|
with pm.Model(coords=ss_mod.coords) as model:
|
|
@@ -94,6 +101,8 @@ class AutoregressiveComponent(Component):
|
|
|
94
101
|
self.order = order
|
|
95
102
|
self.ar_lags = ar_lags
|
|
96
103
|
|
|
104
|
+
state_names = [f"L{i + 1}_{name}" for i in range(k_states)]
|
|
105
|
+
|
|
97
106
|
super().__init__(
|
|
98
107
|
name=name,
|
|
99
108
|
k_endog=k_endog,
|
|
@@ -101,61 +110,82 @@ class AutoregressiveComponent(Component):
|
|
|
101
110
|
k_posdef=k_posdef,
|
|
102
111
|
measurement_error=True,
|
|
103
112
|
combine_hidden_states=True,
|
|
104
|
-
|
|
113
|
+
base_observed_state_names=observed_state_names,
|
|
114
|
+
base_state_names=state_names,
|
|
105
115
|
obs_state_idxs=np.tile(np.r_[[1.0], np.zeros(k_states - 1)], k_endog_effective),
|
|
106
116
|
share_states=share_states,
|
|
107
117
|
)
|
|
108
118
|
|
|
109
|
-
def
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
k_states = self.k_states // k_endog_effective # this is also the number of AR lags
|
|
114
|
-
base_names = [f"L{i + 1}_{self.name}" for i in range(k_states)]
|
|
119
|
+
def set_states(self) -> State | tuple[State, ...] | None:
|
|
120
|
+
base_names = self.base_state_names
|
|
121
|
+
observed_state_names = self.base_observed_state_names
|
|
115
122
|
|
|
116
123
|
if self.share_states:
|
|
117
|
-
|
|
118
|
-
self.shock_names = [f"{self.name}[shared]"]
|
|
124
|
+
state_names = [f"{name}[shared]" for name in base_names]
|
|
119
125
|
else:
|
|
120
|
-
|
|
126
|
+
state_names = [
|
|
121
127
|
f"{name}[{state_name}]"
|
|
122
|
-
for state_name in
|
|
128
|
+
for state_name in observed_state_names
|
|
123
129
|
for name in base_names
|
|
124
130
|
]
|
|
125
|
-
self.shock_names = [
|
|
126
|
-
f"{self.name}[{obs_name}]" for obs_name in self.observed_state_names
|
|
127
|
-
]
|
|
128
131
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
hidden_states = [State(name=name, observed=False, shared=True) for name in state_names]
|
|
133
|
+
observed_states = [
|
|
134
|
+
State(name=name, observed=True, shared=False) for name in observed_state_names
|
|
135
|
+
]
|
|
136
|
+
return *hidden_states, *observed_states
|
|
132
137
|
|
|
133
|
-
|
|
134
|
-
|
|
138
|
+
def set_parameters(self) -> Parameter | tuple[Parameter, ...] | None:
|
|
139
|
+
k_endog = self.k_endog
|
|
140
|
+
k_endog_effective = 1 if self.share_states else k_endog
|
|
141
|
+
|
|
142
|
+
k_states = self.k_states // k_endog_effective # this is also the number of AR lags
|
|
143
|
+
|
|
144
|
+
ar_param = Parameter(
|
|
145
|
+
name=f"params_{self.name}",
|
|
146
|
+
shape=(k_endog_effective, k_states) if k_endog_effective > 1 else (k_states,),
|
|
147
|
+
dims=(f"lag_{self.name}",)
|
|
148
|
+
if k_endog_effective == 1
|
|
149
|
+
else (
|
|
135
150
|
f"endog_{self.name}",
|
|
136
151
|
f"lag_{self.name}",
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
152
|
+
),
|
|
153
|
+
constraints=None,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
sigma_param = Parameter(
|
|
157
|
+
name=f"sigma_{self.name}",
|
|
158
|
+
shape=(k_endog_effective,) if k_endog_effective > 1 else (),
|
|
159
|
+
dims=(f"endog_{self.name}",) if k_endog_effective > 1 else None,
|
|
160
|
+
constraints="Positive",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return ar_param, sigma_param
|
|
164
|
+
|
|
165
|
+
def set_shocks(self) -> Shock | tuple[Shock, ...] | None:
|
|
166
|
+
observed_state_names = self.observed_state_names
|
|
167
|
+
|
|
168
|
+
if self.share_states:
|
|
169
|
+
shock_names = [f"{self.name}[shared]"]
|
|
170
|
+
else:
|
|
171
|
+
shock_names = [f"{self.name}[{obs_name}]" for obs_name in observed_state_names]
|
|
172
|
+
|
|
173
|
+
return tuple(Shock(name=name) for name in shock_names)
|
|
174
|
+
|
|
175
|
+
def set_coords(self) -> Coord | tuple[Coord, ...] | None:
|
|
176
|
+
k_endog = self.k_endog
|
|
177
|
+
k_endog_effective = 1 if self.share_states else k_endog
|
|
178
|
+
observed_state_names = self.observed_state_names
|
|
179
|
+
|
|
180
|
+
lag_coord = Coord(dimension=f"lag_{self.name}", labels=self.ar_lags.tolist())
|
|
181
|
+
|
|
182
|
+
coord_container = [lag_coord]
|
|
183
|
+
|
|
184
|
+
if k_endog_effective > 1:
|
|
185
|
+
endog_coord = Coord(dimension=f"endog_{self.name}", labels=observed_state_names)
|
|
186
|
+
coord_container.append(endog_coord)
|
|
187
|
+
|
|
188
|
+
return tuple(coord_container)
|
|
159
189
|
|
|
160
190
|
def make_symbolic_graph(self) -> None:
|
|
161
191
|
k_endog = self.k_endog
|
|
@@ -211,3 +241,15 @@ class AutoregressiveComponent(Component):
|
|
|
211
241
|
|
|
212
242
|
cov_idx = ("state_cov", *np.diag_indices(k_posdef))
|
|
213
243
|
self.ssm[cov_idx] = sigma_ar**2
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def __getattr__(name: str):
|
|
247
|
+
if name == "AutoregressiveComponent":
|
|
248
|
+
warnings.warn(
|
|
249
|
+
"AutoregressiveComponent is deprecated and will be removed in a future release. "
|
|
250
|
+
"Use Autoregressive instead.",
|
|
251
|
+
FutureWarning,
|
|
252
|
+
stacklevel=2,
|
|
253
|
+
)
|
|
254
|
+
return Autoregressive
|
|
255
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|