pymc-extras 0.7.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/inference/laplace_approx/laplace.py +2 -2
- pymc_extras/inference/pathfinder/pathfinder.py +1 -1
- pymc_extras/prior.py +3 -3
- pymc_extras/statespace/core/properties.py +276 -0
- pymc_extras/statespace/core/statespace.py +180 -44
- pymc_extras/statespace/filters/distributions.py +12 -29
- pymc_extras/statespace/filters/kalman_filter.py +1 -1
- 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.7.0.dist-info → pymc_extras-0.8.0.dist-info}/METADATA +3 -3
- {pymc_extras-0.7.0.dist-info → pymc_extras-0.8.0.dist-info}/RECORD +25 -24
- {pymc_extras-0.7.0.dist-info → pymc_extras-0.8.0.dist-info}/WHEEL +0 -0
- {pymc_extras-0.7.0.dist-info → pymc_extras-0.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,13 +1,22 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
2
4
|
|
|
3
5
|
from pytensor import tensor as pt
|
|
4
6
|
|
|
7
|
+
from pymc_extras.statespace.core.properties import (
|
|
8
|
+
Coord,
|
|
9
|
+
Data,
|
|
10
|
+
Parameter,
|
|
11
|
+
Shock,
|
|
12
|
+
State,
|
|
13
|
+
)
|
|
5
14
|
from pymc_extras.statespace.models.structural.core import Component
|
|
6
15
|
from pymc_extras.statespace.models.utilities import validate_names
|
|
7
16
|
from pymc_extras.statespace.utils.constants import TIME_DIM
|
|
8
17
|
|
|
9
18
|
|
|
10
|
-
class
|
|
19
|
+
class Regression(Component):
|
|
11
20
|
r"""
|
|
12
21
|
Regression component for exogenous variables in a structural time series model
|
|
13
22
|
|
|
@@ -64,8 +73,8 @@ class RegressionComponent(Component):
|
|
|
64
73
|
import pymc as pm
|
|
65
74
|
import pytensor.tensor as pt
|
|
66
75
|
|
|
67
|
-
trend = st.
|
|
68
|
-
regression = st.
|
|
76
|
+
trend = st.LevelTrend(order=1, innovations_order=1)
|
|
77
|
+
regression = st.Regression(k_exog=2, state_names=['intercept', 'slope'])
|
|
69
78
|
ss_mod = (trend + regression).build()
|
|
70
79
|
|
|
71
80
|
with pm.Model(coords=ss_mod.coords) as model:
|
|
@@ -85,7 +94,7 @@ class RegressionComponent(Component):
|
|
|
85
94
|
|
|
86
95
|
.. code:: python
|
|
87
96
|
|
|
88
|
-
regression = st.
|
|
97
|
+
regression = st.Regression(
|
|
89
98
|
k_exog=2,
|
|
90
99
|
state_names=['price_effect', 'income_effect'],
|
|
91
100
|
observed_state_names=['sales', 'revenue'],
|
|
@@ -118,7 +127,6 @@ class RegressionComponent(Component):
|
|
|
118
127
|
self.innovations = innovations
|
|
119
128
|
validate_names(state_names, var_name="state_names", optional=False)
|
|
120
129
|
k_exog = len(state_names)
|
|
121
|
-
self.state_names = state_names
|
|
122
130
|
|
|
123
131
|
k_states = k_exog
|
|
124
132
|
k_endog = len(observed_state_names)
|
|
@@ -129,15 +137,94 @@ class RegressionComponent(Component):
|
|
|
129
137
|
k_endog=k_endog,
|
|
130
138
|
k_states=k_states * k_endog if not share_states else k_states,
|
|
131
139
|
k_posdef=k_posdef * k_endog if not share_states else k_posdef,
|
|
132
|
-
|
|
140
|
+
base_state_names=state_names,
|
|
133
141
|
share_states=share_states,
|
|
134
|
-
|
|
142
|
+
base_observed_state_names=observed_state_names,
|
|
135
143
|
measurement_error=False,
|
|
136
144
|
combine_hidden_states=False,
|
|
137
|
-
exog_names=[f"data_{name}"],
|
|
138
145
|
obs_state_idxs=np.ones(k_states),
|
|
139
146
|
)
|
|
140
147
|
|
|
148
|
+
def set_states(self) -> State | tuple[State, ...] | None:
|
|
149
|
+
base_names = self.base_state_names
|
|
150
|
+
observed_state_names = self.base_observed_state_names
|
|
151
|
+
|
|
152
|
+
if self.share_states:
|
|
153
|
+
state_names = [f"{name}[{self.name}_shared]" for name in base_names]
|
|
154
|
+
else:
|
|
155
|
+
state_names = [
|
|
156
|
+
f"{name}[{obs_name}]" for obs_name in observed_state_names for name in base_names
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
hidden_states = [State(name=name, observed=False, shared=True) for name in state_names]
|
|
160
|
+
observed_states = [
|
|
161
|
+
State(name=name, observed=True, shared=False) for name in observed_state_names
|
|
162
|
+
]
|
|
163
|
+
return *hidden_states, *observed_states
|
|
164
|
+
|
|
165
|
+
def set_parameters(self) -> Parameter | tuple[Parameter, ...] | None:
|
|
166
|
+
k_endog = self.k_endog
|
|
167
|
+
k_endog_effective = 1 if self.share_states else k_endog
|
|
168
|
+
k_states = self.k_states // k_endog_effective
|
|
169
|
+
|
|
170
|
+
beta_parameter = Parameter(
|
|
171
|
+
name=f"beta_{self.name}",
|
|
172
|
+
shape=(k_endog_effective, k_states) if k_endog_effective > 1 else (k_states,),
|
|
173
|
+
dims=(
|
|
174
|
+
(f"endog_{self.name}", f"state_{self.name}")
|
|
175
|
+
if k_endog_effective > 1
|
|
176
|
+
else (f"state_{self.name}",)
|
|
177
|
+
),
|
|
178
|
+
constraints=None,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
params_container = [beta_parameter]
|
|
182
|
+
|
|
183
|
+
if self.innovations:
|
|
184
|
+
sigma_parameter = Parameter(
|
|
185
|
+
name=f"sigma_beta_{self.name}",
|
|
186
|
+
shape=(k_states,),
|
|
187
|
+
dims=(f"state_{self.name}",),
|
|
188
|
+
constraints="Positive",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
params_container.append(sigma_parameter)
|
|
192
|
+
|
|
193
|
+
return tuple(params_container)
|
|
194
|
+
|
|
195
|
+
def set_data_info(self) -> Data | tuple[Data, ...] | None:
|
|
196
|
+
k_endog = self.k_endog
|
|
197
|
+
k_endog_effective = 1 if self.share_states else k_endog
|
|
198
|
+
k_states = self.k_states // k_endog_effective
|
|
199
|
+
|
|
200
|
+
data_prop = Data(
|
|
201
|
+
name=f"data_{self.name}",
|
|
202
|
+
shape=(None, k_states),
|
|
203
|
+
dims=(TIME_DIM, f"state_{self.name}"),
|
|
204
|
+
is_exogenous=True,
|
|
205
|
+
)
|
|
206
|
+
return (data_prop,)
|
|
207
|
+
|
|
208
|
+
def set_shocks(self) -> Shock | tuple[Shock, ...] | None:
|
|
209
|
+
base_names = self.base_state_names
|
|
210
|
+
|
|
211
|
+
if self.share_states:
|
|
212
|
+
shock_names = [f"{state_name}_shared" for state_name in base_names]
|
|
213
|
+
else:
|
|
214
|
+
shock_names = base_names
|
|
215
|
+
|
|
216
|
+
return tuple(Shock(name=name) for name in shock_names)
|
|
217
|
+
|
|
218
|
+
def set_coords(self) -> tuple[Coord, ...] | None:
|
|
219
|
+
regression_state_coord = Coord(
|
|
220
|
+
dimension=f"state_{self.name}", labels=tuple(self.base_state_names)
|
|
221
|
+
)
|
|
222
|
+
endogenous_state_coord = Coord(
|
|
223
|
+
dimension=f"endog_{self.name}", labels=self.observed_state_names
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
return regression_state_coord, endogenous_state_coord
|
|
227
|
+
|
|
141
228
|
def make_symbolic_graph(self) -> None:
|
|
142
229
|
k_endog = self.k_endog
|
|
143
230
|
k_endog_effective = 1 if self.share_states else k_endog
|
|
@@ -172,64 +259,14 @@ class RegressionComponent(Component):
|
|
|
172
259
|
row_idx, col_idx = np.diag_indices(self.k_states)
|
|
173
260
|
self.ssm["state_cov", row_idx, col_idx] = sigma_beta.ravel() ** 2
|
|
174
261
|
|
|
175
|
-
def populate_component_properties(self) -> None:
|
|
176
|
-
k_endog = self.k_endog
|
|
177
|
-
k_endog_effective = 1 if self.share_states else k_endog
|
|
178
|
-
|
|
179
|
-
k_states = self.k_states // k_endog_effective
|
|
180
|
-
|
|
181
|
-
if self.share_states:
|
|
182
|
-
self.shock_names = [f"{state_name}_shared" for state_name in self.state_names]
|
|
183
|
-
else:
|
|
184
|
-
self.shock_names = self.state_names
|
|
185
|
-
|
|
186
|
-
self.param_names = [f"beta_{self.name}"]
|
|
187
|
-
self.data_names = [f"data_{self.name}"]
|
|
188
|
-
self.param_dims = {
|
|
189
|
-
f"beta_{self.name}": (f"endog_{self.name}", f"state_{self.name}")
|
|
190
|
-
if k_endog_effective > 1
|
|
191
|
-
else (f"state_{self.name}",)
|
|
192
|
-
}
|
|
193
262
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
self.param_info = {
|
|
206
|
-
f"beta_{self.name}": {
|
|
207
|
-
"shape": (k_endog_effective, k_states) if k_endog_effective > 1 else (k_states,),
|
|
208
|
-
"constraints": None,
|
|
209
|
-
"dims": (f"endog_{self.name}", f"state_{self.name}")
|
|
210
|
-
if k_endog_effective > 1
|
|
211
|
-
else (f"state_{self.name}",),
|
|
212
|
-
},
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
self.data_info = {
|
|
216
|
-
f"data_{self.name}": {
|
|
217
|
-
"shape": (None, k_states),
|
|
218
|
-
"dims": (TIME_DIM, f"state_{self.name}"),
|
|
219
|
-
},
|
|
220
|
-
}
|
|
221
|
-
self.coords = {
|
|
222
|
-
f"state_{self.name}": base_names,
|
|
223
|
-
f"endog_{self.name}": self.observed_state_names,
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if self.innovations:
|
|
227
|
-
self.param_names += [f"sigma_beta_{self.name}"]
|
|
228
|
-
self.param_dims[f"sigma_beta_{self.name}"] = (f"state_{self.name}",)
|
|
229
|
-
self.param_info[f"sigma_beta_{self.name}"] = {
|
|
230
|
-
"shape": (k_states,),
|
|
231
|
-
"constraints": "Positive",
|
|
232
|
-
"dims": (f"state_{self.name}",)
|
|
233
|
-
if k_endog_effective == 1
|
|
234
|
-
else (f"endog_{self.name}", f"state_{self.name}"),
|
|
235
|
-
}
|
|
263
|
+
def __getattr__(name: str):
|
|
264
|
+
if name == "RegressionComponent":
|
|
265
|
+
warnings.warn(
|
|
266
|
+
"RegressionComponent is deprecated and will be removed in a future release. "
|
|
267
|
+
"Use Regression instead.",
|
|
268
|
+
FutureWarning,
|
|
269
|
+
stacklevel=2,
|
|
270
|
+
)
|
|
271
|
+
return Regression
|
|
272
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
2
4
|
|
|
3
5
|
from pytensor import tensor as pt
|
|
4
6
|
|
|
7
|
+
from pymc_extras.statespace.core.properties import (
|
|
8
|
+
Coord,
|
|
9
|
+
Parameter,
|
|
10
|
+
Shock,
|
|
11
|
+
State,
|
|
12
|
+
)
|
|
5
13
|
from pymc_extras.statespace.models.structural.core import Component
|
|
6
14
|
from pymc_extras.statespace.models.structural.utils import _frequency_transition_block
|
|
7
15
|
|
|
@@ -202,7 +210,7 @@ class TimeSeasonality(Component):
|
|
|
202
210
|
state_names = pd.date_range('1900-01-01', '1900-12-31', freq='MS').month_name().tolist()
|
|
203
211
|
|
|
204
212
|
# Build the structural model
|
|
205
|
-
grw = st.
|
|
213
|
+
grw = st.LevelTrend(order=1, innovations_order=1)
|
|
206
214
|
annual_season = st.TimeSeasonality(
|
|
207
215
|
season_length=12, name="annual", state_names=state_names, innovations=False
|
|
208
216
|
)
|
|
@@ -265,7 +273,7 @@ class TimeSeasonality(Component):
|
|
|
265
273
|
raise ValueError(
|
|
266
274
|
f"state_names must be a list of length season_length*duration, got {len(state_names)}"
|
|
267
275
|
)
|
|
268
|
-
state_names = state_names
|
|
276
|
+
state_names = list(state_names)
|
|
269
277
|
|
|
270
278
|
self.share_states = share_states
|
|
271
279
|
self.innovations = innovations
|
|
@@ -290,7 +298,7 @@ class TimeSeasonality(Component):
|
|
|
290
298
|
k_endog=k_endog,
|
|
291
299
|
k_states=k_states if share_states else k_states * k_endog,
|
|
292
300
|
k_posdef=k_posdef if share_states else k_posdef * k_endog,
|
|
293
|
-
|
|
301
|
+
base_observed_state_names=observed_state_names,
|
|
294
302
|
measurement_error=False,
|
|
295
303
|
combine_hidden_states=True,
|
|
296
304
|
obs_state_idxs=np.tile(
|
|
@@ -299,64 +307,77 @@ class TimeSeasonality(Component):
|
|
|
299
307
|
share_states=share_states,
|
|
300
308
|
)
|
|
301
309
|
|
|
302
|
-
def
|
|
303
|
-
|
|
304
|
-
k_endog_effective = 1 if self.share_states else k_endog
|
|
305
|
-
|
|
306
|
-
k_states = self.k_states // k_endog_effective
|
|
310
|
+
def set_states(self) -> State | tuple[State, ...] | None:
|
|
311
|
+
observed_state_names = self.base_observed_state_names
|
|
307
312
|
|
|
308
313
|
if self.share_states:
|
|
309
|
-
|
|
314
|
+
state_names = [
|
|
310
315
|
f"{state_name}[{self.name}_shared]" for state_name in self.provided_state_names
|
|
311
316
|
]
|
|
312
317
|
else:
|
|
313
|
-
|
|
318
|
+
state_names = [
|
|
314
319
|
f"{state_name}[{endog_name}]"
|
|
315
|
-
for endog_name in
|
|
320
|
+
for endog_name in observed_state_names
|
|
316
321
|
for state_name in self.provided_state_names
|
|
317
322
|
]
|
|
318
323
|
|
|
319
|
-
|
|
324
|
+
hidden_states = [State(name=name, observed=False, shared=True) for name in state_names]
|
|
325
|
+
observed_states = [
|
|
326
|
+
State(name=name, observed=True, shared=False) for name in observed_state_names
|
|
327
|
+
]
|
|
328
|
+
return *hidden_states, *observed_states
|
|
320
329
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
"dims": (f"state_{self.name}",)
|
|
326
|
-
if k_endog_effective == 1
|
|
327
|
-
else (f"endog_{self.name}", f"state_{self.name}"),
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
self.param_dims = {
|
|
332
|
-
f"params_{self.name}": (f"state_{self.name}",)
|
|
333
|
-
if k_endog_effective == 1
|
|
334
|
-
else (f"endog_{self.name}", f"state_{self.name}")
|
|
335
|
-
}
|
|
330
|
+
def set_parameters(self) -> Parameter | tuple[Parameter, ...] | None:
|
|
331
|
+
k_endog = self.k_endog
|
|
332
|
+
k_endog_effective = 1 if self.share_states else k_endog
|
|
333
|
+
k_states = self.k_states // k_endog_effective
|
|
336
334
|
|
|
337
|
-
|
|
338
|
-
|
|
335
|
+
seasonal_param = Parameter(
|
|
336
|
+
name=f"params_{self.name}",
|
|
337
|
+
shape=(k_states,) if k_endog == 1 else (k_endog, k_states),
|
|
338
|
+
dims=(f"state_{self.name}",)
|
|
339
339
|
if k_endog_effective == 1
|
|
340
|
-
else {
|
|
341
|
-
|
|
342
|
-
f"state_{self.name}": self.provided_state_names,
|
|
343
|
-
}
|
|
340
|
+
else (f"endog_{self.name}", f"state_{self.name}"),
|
|
341
|
+
constraints=None,
|
|
344
342
|
)
|
|
345
343
|
|
|
344
|
+
params_container = [seasonal_param]
|
|
345
|
+
|
|
346
|
+
if self.innovations:
|
|
347
|
+
sigma_param = Parameter(
|
|
348
|
+
name=f"sigma_{self.name}",
|
|
349
|
+
shape=() if k_endog_effective == 1 else (k_endog,),
|
|
350
|
+
dims=None if k_endog_effective == 1 else (f"endog_{self.name}",),
|
|
351
|
+
constraints="Positive",
|
|
352
|
+
)
|
|
353
|
+
params_container.append(sigma_param)
|
|
354
|
+
|
|
355
|
+
return tuple(params_container)
|
|
356
|
+
|
|
357
|
+
def set_shocks(self) -> Shock | tuple[Shock, ...] | None:
|
|
358
|
+
observed_state_names = self.observed_state_names
|
|
346
359
|
if self.innovations:
|
|
347
|
-
self.param_names += [f"sigma_{self.name}"]
|
|
348
|
-
self.param_info[f"sigma_{self.name}"] = {
|
|
349
|
-
"shape": () if k_endog_effective == 1 else (k_endog,),
|
|
350
|
-
"constraints": "Positive",
|
|
351
|
-
"dims": None if k_endog_effective == 1 else (f"endog_{self.name}",),
|
|
352
|
-
}
|
|
353
360
|
if self.share_states:
|
|
354
|
-
|
|
361
|
+
shock_names = [f"{self.name}[shared]"]
|
|
355
362
|
else:
|
|
356
|
-
|
|
363
|
+
shock_names = [f"{self.name}[{name}]" for name in observed_state_names]
|
|
364
|
+
|
|
365
|
+
return tuple(Shock(name=name) for name in shock_names)
|
|
366
|
+
return None
|
|
367
|
+
|
|
368
|
+
def set_coords(self) -> Coord | tuple[Coord, ...] | None:
|
|
369
|
+
k_endog = self.k_endog
|
|
370
|
+
k_endog_effective = 1 if self.share_states else k_endog
|
|
371
|
+
observed_state_names = self.observed_state_names
|
|
372
|
+
|
|
373
|
+
state_coord = Coord(dimension=f"state_{self.name}", labels=tuple(self.provided_state_names))
|
|
374
|
+
coords_container = [state_coord]
|
|
357
375
|
|
|
358
|
-
|
|
359
|
-
|
|
376
|
+
if k_endog_effective > 1:
|
|
377
|
+
endog_coord = Coord(dimension=f"endog_{self.name}", labels=observed_state_names)
|
|
378
|
+
coords_container.append(endog_coord)
|
|
379
|
+
|
|
380
|
+
return tuple(coords_container)
|
|
360
381
|
|
|
361
382
|
def make_symbolic_graph(self) -> None:
|
|
362
383
|
k_endog = self.k_endog
|
|
@@ -490,7 +511,7 @@ class FrequencySeasonality(Component):
|
|
|
490
511
|
n: int | None = None,
|
|
491
512
|
name: str | None = None,
|
|
492
513
|
innovations: bool = True,
|
|
493
|
-
observed_state_names:
|
|
514
|
+
observed_state_names: Sequence[str] | None = None,
|
|
494
515
|
share_states: bool = False,
|
|
495
516
|
):
|
|
496
517
|
if observed_state_names is None:
|
|
@@ -527,12 +548,84 @@ class FrequencySeasonality(Component):
|
|
|
527
548
|
if share_states
|
|
528
549
|
else k_states * int(self.innovations) * k_endog,
|
|
529
550
|
share_states=share_states,
|
|
530
|
-
|
|
551
|
+
base_observed_state_names=observed_state_names,
|
|
531
552
|
measurement_error=False,
|
|
532
553
|
combine_hidden_states=True,
|
|
533
554
|
obs_state_idxs=obs_state_idx,
|
|
534
555
|
)
|
|
535
556
|
|
|
557
|
+
def set_states(self) -> State | tuple[State, ...] | None:
|
|
558
|
+
observed_state_names = self.base_observed_state_names
|
|
559
|
+
base_names = [f"{f}_{i}_{self.name}" for i in range(self.n) for f in ["Cos", "Sin"]]
|
|
560
|
+
|
|
561
|
+
if self.share_states:
|
|
562
|
+
state_names = [f"{name}[shared]" for name in base_names]
|
|
563
|
+
else:
|
|
564
|
+
state_names = [
|
|
565
|
+
f"{name}[{obs_state_name}]"
|
|
566
|
+
for obs_state_name in self.base_observed_state_names
|
|
567
|
+
for name in base_names
|
|
568
|
+
]
|
|
569
|
+
|
|
570
|
+
hidden_states = [State(name=name, observed=False, shared=True) for name in state_names]
|
|
571
|
+
observed_states = [
|
|
572
|
+
State(name=name, observed=True, shared=False) for name in observed_state_names
|
|
573
|
+
]
|
|
574
|
+
return *hidden_states, *observed_states
|
|
575
|
+
|
|
576
|
+
def set_parameters(self) -> Parameter | tuple[Parameter, ...] | None:
|
|
577
|
+
k_endog = self.k_endog
|
|
578
|
+
k_endog_effective = 1 if self.share_states else k_endog
|
|
579
|
+
n_coefs = self.n_coefs
|
|
580
|
+
|
|
581
|
+
freq_param = Parameter(
|
|
582
|
+
name=f"params_{self.name}",
|
|
583
|
+
shape=(n_coefs,) if k_endog_effective == 1 else (k_endog_effective, n_coefs),
|
|
584
|
+
dims=(f"state_{self.name}",)
|
|
585
|
+
if k_endog_effective == 1
|
|
586
|
+
else (f"endog_{self.name}", f"state_{self.name}"),
|
|
587
|
+
constraints=None,
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
params_container = [freq_param]
|
|
591
|
+
|
|
592
|
+
if self.innovations:
|
|
593
|
+
sigma_param = Parameter(
|
|
594
|
+
name=f"sigma_{self.name}",
|
|
595
|
+
shape=() if k_endog_effective == 1 else (k_endog_effective, n_coefs),
|
|
596
|
+
dims=None if k_endog_effective == 1 else (f"endog_{self.name}",),
|
|
597
|
+
constraints="Positive",
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
params_container.append(sigma_param)
|
|
601
|
+
|
|
602
|
+
return tuple(params_container)
|
|
603
|
+
|
|
604
|
+
def set_shocks(self) -> Shock | tuple[Shock, ...] | None:
|
|
605
|
+
if self.innovations:
|
|
606
|
+
return tuple(Shock(name=name) for name in self.state_names)
|
|
607
|
+
return None
|
|
608
|
+
|
|
609
|
+
def set_coords(self) -> Coord | tuple[Coord, ...] | None:
|
|
610
|
+
k_endog = self.k_endog
|
|
611
|
+
n_coefs = self.n_coefs
|
|
612
|
+
observed_state_names = self.observed_state_names
|
|
613
|
+
|
|
614
|
+
base_names = [f"{f}_{i}_{self.name}" for i in range(self.n) for f in ["Cos", "Sin"]]
|
|
615
|
+
|
|
616
|
+
# Trim state names if the model is saturated
|
|
617
|
+
param_state_names = base_names[:n_coefs]
|
|
618
|
+
|
|
619
|
+
state_coords = Coord(dimension=f"state_{self.name}", labels=tuple(param_state_names))
|
|
620
|
+
|
|
621
|
+
coord_container = [state_coords]
|
|
622
|
+
|
|
623
|
+
if k_endog > 1:
|
|
624
|
+
endog_coords = Coord(dimension=f"endog_{self.name}", labels=observed_state_names)
|
|
625
|
+
coord_container.append(endog_coords)
|
|
626
|
+
|
|
627
|
+
return tuple(coord_container)
|
|
628
|
+
|
|
536
629
|
def make_symbolic_graph(self) -> None:
|
|
537
630
|
k_endog = self.k_endog
|
|
538
631
|
k_endog_effective = 1 if self.share_states else k_endog
|
|
@@ -571,58 +664,3 @@ class FrequencySeasonality(Component):
|
|
|
571
664
|
self.ssm["state_cov", :, :] = pt.eye(self.k_posdef) * pt.repeat(
|
|
572
665
|
sigma_season**2, k_posdef
|
|
573
666
|
)
|
|
574
|
-
|
|
575
|
-
def populate_component_properties(self):
|
|
576
|
-
k_endog = self.k_endog
|
|
577
|
-
k_endog_effective = 1 if self.share_states else k_endog
|
|
578
|
-
n_coefs = self.n_coefs
|
|
579
|
-
|
|
580
|
-
base_names = [f"{f}_{i}_{self.name}" for i in range(self.n) for f in ["Cos", "Sin"]]
|
|
581
|
-
|
|
582
|
-
if self.share_states:
|
|
583
|
-
self.state_names = [f"{name}[shared]" for name in base_names]
|
|
584
|
-
else:
|
|
585
|
-
self.state_names = [
|
|
586
|
-
f"{name}[{obs_state_name}]"
|
|
587
|
-
for obs_state_name in self.observed_state_names
|
|
588
|
-
for name in base_names
|
|
589
|
-
]
|
|
590
|
-
|
|
591
|
-
# Trim state names if the model is saturated
|
|
592
|
-
param_state_names = base_names[:n_coefs]
|
|
593
|
-
|
|
594
|
-
self.param_names = [f"params_{self.name}"]
|
|
595
|
-
self.param_dims = {
|
|
596
|
-
f"params_{self.name}": (f"state_{self.name}",)
|
|
597
|
-
if k_endog_effective == 1
|
|
598
|
-
else (f"endog_{self.name}", f"state_{self.name}")
|
|
599
|
-
}
|
|
600
|
-
self.param_info = {
|
|
601
|
-
f"params_{self.name}": {
|
|
602
|
-
"shape": (n_coefs,) if k_endog_effective == 1 else (k_endog_effective, n_coefs),
|
|
603
|
-
"constraints": None,
|
|
604
|
-
"dims": (f"state_{self.name}",)
|
|
605
|
-
if k_endog_effective == 1
|
|
606
|
-
else (f"endog_{self.name}", f"state_{self.name}"),
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
self.coords = (
|
|
611
|
-
{f"state_{self.name}": param_state_names}
|
|
612
|
-
if k_endog == 1
|
|
613
|
-
else {
|
|
614
|
-
f"endog_{self.name}": self.observed_state_names,
|
|
615
|
-
f"state_{self.name}": param_state_names,
|
|
616
|
-
}
|
|
617
|
-
)
|
|
618
|
-
|
|
619
|
-
if self.innovations:
|
|
620
|
-
self.param_names += [f"sigma_{self.name}"]
|
|
621
|
-
self.shock_names = self.state_names.copy()
|
|
622
|
-
self.param_info[f"sigma_{self.name}"] = {
|
|
623
|
-
"shape": () if k_endog_effective == 1 else (k_endog_effective, n_coefs),
|
|
624
|
-
"constraints": "Positive",
|
|
625
|
-
"dims": None if k_endog_effective == 1 else (f"endog_{self.name}",),
|
|
626
|
-
}
|
|
627
|
-
if k_endog_effective > 1:
|
|
628
|
-
self.param_dims[f"sigma_{self.name}"] = (f"endog_{self.name}",)
|