pymc-extras 0.2.5__py3-none-any.whl → 0.2.7__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/__init__.py +5 -1
- pymc_extras/deserialize.py +224 -0
- pymc_extras/distributions/continuous.py +3 -2
- pymc_extras/distributions/discrete.py +3 -1
- pymc_extras/inference/find_map.py +62 -17
- pymc_extras/inference/laplace.py +10 -7
- pymc_extras/prior.py +1356 -0
- pymc_extras/statespace/core/statespace.py +191 -52
- pymc_extras/statespace/filters/distributions.py +15 -16
- pymc_extras/statespace/filters/kalman_filter.py +1 -18
- pymc_extras/statespace/filters/kalman_smoother.py +2 -6
- pymc_extras/statespace/models/ETS.py +10 -0
- pymc_extras/statespace/models/SARIMAX.py +26 -5
- pymc_extras/statespace/models/VARMAX.py +12 -2
- pymc_extras/statespace/models/structural.py +18 -5
- pymc_extras-0.2.7.dist-info/METADATA +321 -0
- pymc_extras-0.2.7.dist-info/RECORD +66 -0
- {pymc_extras-0.2.5.dist-info → pymc_extras-0.2.7.dist-info}/WHEEL +1 -2
- pymc_extras/utils/pivoted_cholesky.py +0 -69
- pymc_extras/version.py +0 -11
- pymc_extras/version.txt +0 -1
- pymc_extras-0.2.5.dist-info/METADATA +0 -112
- pymc_extras-0.2.5.dist-info/RECORD +0 -108
- pymc_extras-0.2.5.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -13
- tests/distributions/__init__.py +0 -19
- tests/distributions/test_continuous.py +0 -185
- tests/distributions/test_discrete.py +0 -210
- tests/distributions/test_discrete_markov_chain.py +0 -258
- tests/distributions/test_multivariate.py +0 -304
- tests/distributions/test_transform.py +0 -77
- tests/model/__init__.py +0 -0
- tests/model/marginal/__init__.py +0 -0
- tests/model/marginal/test_distributions.py +0 -132
- tests/model/marginal/test_graph_analysis.py +0 -182
- tests/model/marginal/test_marginal_model.py +0 -967
- tests/model/test_model_api.py +0 -38
- tests/statespace/__init__.py +0 -0
- tests/statespace/test_ETS.py +0 -411
- tests/statespace/test_SARIMAX.py +0 -405
- tests/statespace/test_VARMAX.py +0 -184
- tests/statespace/test_coord_assignment.py +0 -181
- tests/statespace/test_distributions.py +0 -270
- tests/statespace/test_kalman_filter.py +0 -326
- tests/statespace/test_representation.py +0 -175
- tests/statespace/test_statespace.py +0 -872
- tests/statespace/test_statespace_JAX.py +0 -156
- tests/statespace/test_structural.py +0 -836
- tests/statespace/utilities/__init__.py +0 -0
- tests/statespace/utilities/shared_fixtures.py +0 -9
- tests/statespace/utilities/statsmodel_local_level.py +0 -42
- tests/statespace/utilities/test_helpers.py +0 -310
- tests/test_blackjax_smc.py +0 -222
- tests/test_find_map.py +0 -103
- tests/test_histogram_approximation.py +0 -109
- tests/test_laplace.py +0 -281
- tests/test_linearmodel.py +0 -208
- tests/test_model_builder.py +0 -306
- tests/test_pathfinder.py +0 -297
- tests/test_pivoted_cholesky.py +0 -24
- tests/test_printing.py +0 -98
- tests/test_prior_from_trace.py +0 -172
- tests/test_splines.py +0 -77
- tests/utils.py +0 -0
- {pymc_extras-0.2.5.dist-info → pymc_extras-0.2.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
from contextlib import nullcontext as does_not_raise
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
import pandas as pd
|
|
5
|
-
import pymc as pm
|
|
6
|
-
import pytensor
|
|
7
|
-
import pytensor.tensor as pt
|
|
8
|
-
import pytest
|
|
9
|
-
|
|
10
|
-
from pymc_extras.statespace.models import structural
|
|
11
|
-
from pymc_extras.statespace.models.structural import LevelTrendComponent
|
|
12
|
-
from pymc_extras.statespace.utils.constants import (
|
|
13
|
-
FILTER_OUTPUT_DIMS,
|
|
14
|
-
FILTER_OUTPUT_NAMES,
|
|
15
|
-
SMOOTHER_OUTPUT_NAMES,
|
|
16
|
-
TIME_DIM,
|
|
17
|
-
)
|
|
18
|
-
from pymc_extras.statespace.utils.data_tools import (
|
|
19
|
-
NO_FREQ_INFO_WARNING,
|
|
20
|
-
NO_TIME_INDEX_WARNING,
|
|
21
|
-
)
|
|
22
|
-
from tests.statespace.utilities.test_helpers import load_nile_test_data
|
|
23
|
-
|
|
24
|
-
function_names = ["pandas_date_freq", "pandas_date_nofreq", "pandas_nodate", "numpy", "pytensor"]
|
|
25
|
-
expected_warning = [
|
|
26
|
-
does_not_raise(),
|
|
27
|
-
pytest.warns(UserWarning, match=NO_FREQ_INFO_WARNING),
|
|
28
|
-
pytest.warns(UserWarning, match=NO_TIME_INDEX_WARNING),
|
|
29
|
-
pytest.warns(UserWarning, match=NO_TIME_INDEX_WARNING),
|
|
30
|
-
pytest.warns(UserWarning, match=NO_TIME_INDEX_WARNING),
|
|
31
|
-
]
|
|
32
|
-
func_inputs = list(zip(function_names, expected_warning))
|
|
33
|
-
floatX = pytensor.config.floatX
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@pytest.fixture
|
|
37
|
-
def load_dataset():
|
|
38
|
-
data = load_nile_test_data()
|
|
39
|
-
|
|
40
|
-
def _load_dataset(f):
|
|
41
|
-
if f == "pandas_date_freq":
|
|
42
|
-
data.index.freq = data.index.inferred_freq
|
|
43
|
-
return data
|
|
44
|
-
if f == "pandas_date_nofreq":
|
|
45
|
-
data.index.freq = None
|
|
46
|
-
return data
|
|
47
|
-
elif f == "pandas_nodate":
|
|
48
|
-
return data.reset_index(drop=True)
|
|
49
|
-
elif f == "numpy":
|
|
50
|
-
return data.values
|
|
51
|
-
elif f == "pytensor":
|
|
52
|
-
return pt.as_tensor_variable(data.values)
|
|
53
|
-
else:
|
|
54
|
-
raise ValueError
|
|
55
|
-
|
|
56
|
-
return _load_dataset
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@pytest.fixture()
|
|
60
|
-
def generate_timeseries():
|
|
61
|
-
def _generate_timeseries(freq):
|
|
62
|
-
index = pd.date_range(start="2000-01-01", freq=freq, periods=100)
|
|
63
|
-
data = np.random.normal(size=100).astype(floatX)
|
|
64
|
-
df = pd.DataFrame(data, index=index, columns=["level"])
|
|
65
|
-
return df
|
|
66
|
-
|
|
67
|
-
return _generate_timeseries
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
@pytest.fixture()
|
|
71
|
-
def create_model(load_dataset):
|
|
72
|
-
ss_mod = structural.LevelTrendComponent(order=2).build("data", verbose=False)
|
|
73
|
-
|
|
74
|
-
def _create_model(f):
|
|
75
|
-
data = load_dataset(f)
|
|
76
|
-
with pm.Model(coords=ss_mod.coords) as mod:
|
|
77
|
-
P0_diag = pm.Exponential(
|
|
78
|
-
"P0_diag",
|
|
79
|
-
1,
|
|
80
|
-
dims="state",
|
|
81
|
-
)
|
|
82
|
-
P0 = pm.Deterministic("P0", pt.diag(P0_diag), dims=("state", "state_aux"))
|
|
83
|
-
initial_trend = pm.Normal("initial_trend", dims="trend_state")
|
|
84
|
-
sigma_trend = pm.Exponential("sigma_trend", 1, dims="trend_shock")
|
|
85
|
-
ss_mod.build_statespace_graph(data, save_kalman_filter_outputs_in_idata=True)
|
|
86
|
-
return mod
|
|
87
|
-
|
|
88
|
-
return _create_model
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@pytest.mark.parametrize("f, warning", func_inputs, ids=function_names)
|
|
92
|
-
def test_filter_output_coord_assignment(f, warning, create_model):
|
|
93
|
-
with warning:
|
|
94
|
-
pymc_model = create_model(f)
|
|
95
|
-
|
|
96
|
-
for output in FILTER_OUTPUT_NAMES + SMOOTHER_OUTPUT_NAMES + ["predicted_observed_state"]:
|
|
97
|
-
assert pymc_model.named_vars_to_dims[output] == FILTER_OUTPUT_DIMS[output]
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def test_model_build_without_coords(load_dataset):
|
|
101
|
-
ss_mod = structural.LevelTrendComponent().build(verbose=False)
|
|
102
|
-
data = load_dataset("numpy")
|
|
103
|
-
with pm.Model() as mod:
|
|
104
|
-
P0_diag = pm.Exponential("P0_diag", 1, shape=(2,))
|
|
105
|
-
P0 = pm.Deterministic("P0", pt.diag(P0_diag))
|
|
106
|
-
initial_trend = pm.Normal("initial_trend", shape=(2,))
|
|
107
|
-
sigma_trend = pm.Exponential("sigma_trend", 1, shape=(2,))
|
|
108
|
-
ss_mod.build_statespace_graph(data, register_data=False)
|
|
109
|
-
|
|
110
|
-
assert mod.coords == {}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
@pytest.mark.parametrize("f, warning", func_inputs, ids=function_names)
|
|
114
|
-
def test_data_index_is_coord(f, warning, create_model):
|
|
115
|
-
with warning:
|
|
116
|
-
pymc_model = create_model(f)
|
|
117
|
-
assert TIME_DIM in pymc_model.coords
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def make_model(index):
|
|
121
|
-
n = len(index)
|
|
122
|
-
a = pd.DataFrame(index=index, columns=["A", "B", "C", "D"], data=np.arange(n * 4).reshape(n, 4))
|
|
123
|
-
|
|
124
|
-
mod = LevelTrendComponent(order=2, innovations_order=[0, 1])
|
|
125
|
-
ss_mod = mod.build(name="a", verbose=False)
|
|
126
|
-
|
|
127
|
-
initial_trend_dims, sigma_trend_dims, P0_dims = ss_mod.param_dims.values()
|
|
128
|
-
coords = ss_mod.coords
|
|
129
|
-
|
|
130
|
-
with pm.Model(coords=coords) as model:
|
|
131
|
-
P0_diag = pm.Gamma("P0_diag", alpha=5, beta=5)
|
|
132
|
-
P0 = pm.Deterministic("P0", pt.eye(ss_mod.k_states) * P0_diag, dims=P0_dims)
|
|
133
|
-
|
|
134
|
-
initial_trend = pm.Normal("initial_trend", dims=initial_trend_dims)
|
|
135
|
-
sigma_trend = pm.Gamma("sigma_trend", alpha=2, beta=50, dims=sigma_trend_dims)
|
|
136
|
-
|
|
137
|
-
with pytest.warns(UserWarning, match="No time index found on the supplied data"):
|
|
138
|
-
ss_mod.build_statespace_graph(
|
|
139
|
-
a["A"],
|
|
140
|
-
mode="JAX",
|
|
141
|
-
)
|
|
142
|
-
return model
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def test_integer_index():
|
|
146
|
-
index = np.arange(8).astype(int)
|
|
147
|
-
model = make_model(index)
|
|
148
|
-
assert TIME_DIM in model.coords
|
|
149
|
-
np.testing.assert_allclose(model.coords[TIME_DIM], index)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def test_float_index_raises():
|
|
153
|
-
index = np.linspace(0, 1, 8)
|
|
154
|
-
|
|
155
|
-
with pytest.raises(IndexError, match="Provided index is not an integer index"):
|
|
156
|
-
make_model(index)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def test_non_strictly_monotone_index_raises():
|
|
160
|
-
# Decreases
|
|
161
|
-
index = [0, 1, 2, 1, 2, 3]
|
|
162
|
-
with pytest.raises(IndexError, match="Provided index is not monotonic increasing"):
|
|
163
|
-
make_model(index)
|
|
164
|
-
|
|
165
|
-
# Has gaps
|
|
166
|
-
index = [0, 1, 2, 3, 5, 6]
|
|
167
|
-
with pytest.raises(IndexError, match="Provided index is not monotonic increasing"):
|
|
168
|
-
make_model(index)
|
|
169
|
-
|
|
170
|
-
# Has duplicates
|
|
171
|
-
index = [0, 1, 1, 2, 3, 4]
|
|
172
|
-
with pytest.raises(IndexError, match="Provided index is not monotonic increasing"):
|
|
173
|
-
make_model(index)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
def test_multiindex_raises():
|
|
177
|
-
index = pd.MultiIndex.from_tuples([(0, 0), (1, 1), (2, 2), (3, 3)])
|
|
178
|
-
with pytest.raises(
|
|
179
|
-
NotImplementedError, match="MultiIndex panel data is not currently supported"
|
|
180
|
-
):
|
|
181
|
-
make_model(index)
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
import pymc as pm
|
|
3
|
-
import pytensor
|
|
4
|
-
import pytensor.tensor as pt
|
|
5
|
-
import pytest
|
|
6
|
-
|
|
7
|
-
from numpy.testing import assert_allclose
|
|
8
|
-
from scipy.stats import multivariate_normal
|
|
9
|
-
|
|
10
|
-
from pymc_extras.statespace import structural
|
|
11
|
-
from pymc_extras.statespace.filters.distributions import (
|
|
12
|
-
LinearGaussianStateSpace,
|
|
13
|
-
SequenceMvNormal,
|
|
14
|
-
_LinearGaussianStateSpace,
|
|
15
|
-
)
|
|
16
|
-
from pymc_extras.statespace.utils.constants import (
|
|
17
|
-
ALL_STATE_DIM,
|
|
18
|
-
OBS_STATE_DIM,
|
|
19
|
-
TIME_DIM,
|
|
20
|
-
)
|
|
21
|
-
from tests.statespace.utilities.shared_fixtures import ( # pylint: disable=unused-import
|
|
22
|
-
rng,
|
|
23
|
-
)
|
|
24
|
-
from tests.statespace.utilities.test_helpers import (
|
|
25
|
-
delete_rvs_from_model,
|
|
26
|
-
fast_eval,
|
|
27
|
-
load_nile_test_data,
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
floatX = pytensor.config.floatX
|
|
31
|
-
|
|
32
|
-
# TODO: These are pretty loose because of all the stabilizing of covariance matrices that is done inside the kalman
|
|
33
|
-
# filters. When that is improved, this should be tightened.
|
|
34
|
-
ATOL = 1e-5 if floatX.endswith("64") else 1e-4
|
|
35
|
-
RTOL = 1e-5 if floatX.endswith("64") else 1e-4
|
|
36
|
-
|
|
37
|
-
filter_names = [
|
|
38
|
-
"standard",
|
|
39
|
-
"cholesky",
|
|
40
|
-
"univariate",
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@pytest.fixture(scope="session")
|
|
45
|
-
def data():
|
|
46
|
-
return load_nile_test_data()
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@pytest.fixture(scope="session")
|
|
50
|
-
def pymc_model(data):
|
|
51
|
-
with pm.Model() as mod:
|
|
52
|
-
data = pm.Data("data", data.values)
|
|
53
|
-
P0_diag = pm.Exponential("P0_diag", 1, shape=(2,))
|
|
54
|
-
P0 = pm.Deterministic("P0", pt.diag(P0_diag))
|
|
55
|
-
initial_trend = pm.Normal("initial_trend", shape=(2,))
|
|
56
|
-
sigma_trend = pm.Exponential("sigma_trend", 1, shape=(2,))
|
|
57
|
-
|
|
58
|
-
return mod
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
@pytest.fixture(scope="session")
|
|
62
|
-
def pymc_model_2(data):
|
|
63
|
-
coords = {
|
|
64
|
-
ALL_STATE_DIM: ["level", "trend"],
|
|
65
|
-
OBS_STATE_DIM: ["level"],
|
|
66
|
-
TIME_DIM: np.arange(101, dtype="int"),
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
with pm.Model(coords=coords) as mod:
|
|
70
|
-
P0_diag = pm.Exponential("P0_diag", 1, shape=(2,))
|
|
71
|
-
P0 = pm.Deterministic("P0", pt.diag(P0_diag))
|
|
72
|
-
initial_trend = pm.Normal("initial_trend", shape=(2,))
|
|
73
|
-
sigma_trend = pm.Exponential("sigma_trend", 1, shape=(2,))
|
|
74
|
-
sigma_me = pm.Exponential("sigma_error", 1)
|
|
75
|
-
|
|
76
|
-
return mod
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
@pytest.fixture(scope="session")
|
|
80
|
-
def ss_mod_me():
|
|
81
|
-
ss_mod = structural.LevelTrendComponent(order=2)
|
|
82
|
-
ss_mod += structural.MeasurementError(name="error")
|
|
83
|
-
ss_mod = ss_mod.build("data", verbose=False)
|
|
84
|
-
|
|
85
|
-
return ss_mod
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
@pytest.fixture(scope="session")
|
|
89
|
-
def ss_mod_no_me():
|
|
90
|
-
ss_mod = structural.LevelTrendComponent(order=2)
|
|
91
|
-
ss_mod = ss_mod.build("data", verbose=False)
|
|
92
|
-
|
|
93
|
-
return ss_mod
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@pytest.mark.parametrize("kfilter", filter_names, ids=filter_names)
|
|
97
|
-
def test_loglike_vectors_agree(kfilter, pymc_model):
|
|
98
|
-
# TODO: This test might be flakey, I've gotten random failures
|
|
99
|
-
ss_mod = structural.LevelTrendComponent(order=2).build(
|
|
100
|
-
"data", verbose=False, filter_type=kfilter
|
|
101
|
-
)
|
|
102
|
-
with pymc_model:
|
|
103
|
-
ss_mod._insert_random_variables()
|
|
104
|
-
matrices = ss_mod.unpack_statespace()
|
|
105
|
-
|
|
106
|
-
filter_outputs = ss_mod.kalman_filter.build_graph(pymc_model["data"], *matrices)
|
|
107
|
-
filter_mus, pred_mus, obs_mu, filter_covs, pred_covs, obs_cov, ll = filter_outputs
|
|
108
|
-
|
|
109
|
-
test_ll = fast_eval(ll)
|
|
110
|
-
|
|
111
|
-
# TODO: BUG: Why does fast eval end up with a 2d output when filter is "single"?
|
|
112
|
-
obs_mu_np = obs_mu.eval()
|
|
113
|
-
obs_cov_np = fast_eval(obs_cov)
|
|
114
|
-
data_np = fast_eval(pymc_model["data"])
|
|
115
|
-
|
|
116
|
-
scipy_lls = []
|
|
117
|
-
for y, mu, cov in zip(data_np, obs_mu_np, obs_cov_np):
|
|
118
|
-
scipy_lls.append(multivariate_normal.logpdf(y, mean=mu, cov=cov))
|
|
119
|
-
assert_allclose(test_ll, np.array(scipy_lls).ravel(), atol=ATOL, rtol=RTOL)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def test_sequence_mvn_distribution():
|
|
123
|
-
# Base Case
|
|
124
|
-
mu_sequence = pt.tensor("mu_sequence", shape=(100, 3))
|
|
125
|
-
cov_sequence = pt.tensor("cov_sequence", shape=(100, 3, 3))
|
|
126
|
-
logp = pt.tensor("logp", shape=(100,))
|
|
127
|
-
|
|
128
|
-
dist = SequenceMvNormal.dist(mu_sequence, cov_sequence, logp)
|
|
129
|
-
assert dist.type.shape == (100, 3)
|
|
130
|
-
|
|
131
|
-
# With batch dimension
|
|
132
|
-
mu_sequence = pt.tensor("mu_sequence", shape=(10, 100, 3))
|
|
133
|
-
cov_sequence = pt.tensor("cov_sequence", shape=(10, 100, 3, 3))
|
|
134
|
-
logp = pt.tensor(
|
|
135
|
-
"logp",
|
|
136
|
-
shape=(
|
|
137
|
-
10,
|
|
138
|
-
100,
|
|
139
|
-
),
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
dist = SequenceMvNormal.dist(mu_sequence, cov_sequence, logp)
|
|
143
|
-
assert dist.type.shape == (10, 100, 3)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
@pytest.mark.parametrize("output_name", ["states_latent", "states_observed"])
|
|
147
|
-
def test_lgss_distribution_from_steps(output_name, ss_mod_me, pymc_model_2):
|
|
148
|
-
with pymc_model_2:
|
|
149
|
-
ss_mod_me._insert_random_variables()
|
|
150
|
-
matrices = ss_mod_me.unpack_statespace()
|
|
151
|
-
|
|
152
|
-
# pylint: disable=unpacking-non-sequence
|
|
153
|
-
latent_states, obs_states = LinearGaussianStateSpace("states", *matrices, steps=100)
|
|
154
|
-
# pylint: enable=unpacking-non-sequence
|
|
155
|
-
|
|
156
|
-
idata = pm.sample_prior_predictive(draws=10)
|
|
157
|
-
delete_rvs_from_model(["states_latent", "states_observed", "states_combined"])
|
|
158
|
-
|
|
159
|
-
assert idata.prior.coords["states_latent_dim_0"].shape == (101,)
|
|
160
|
-
assert not np.any(np.isnan(idata.prior[output_name].values))
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
@pytest.mark.parametrize("output_name", ["states_latent", "states_observed"])
|
|
164
|
-
def test_lgss_distribution_with_dims(output_name, ss_mod_me, pymc_model_2):
|
|
165
|
-
with pymc_model_2:
|
|
166
|
-
ss_mod_me._insert_random_variables()
|
|
167
|
-
matrices = ss_mod_me.unpack_statespace()
|
|
168
|
-
|
|
169
|
-
# pylint: disable=unpacking-non-sequence
|
|
170
|
-
latent_states, obs_states = LinearGaussianStateSpace(
|
|
171
|
-
"states",
|
|
172
|
-
*matrices,
|
|
173
|
-
steps=100,
|
|
174
|
-
dims=[TIME_DIM, ALL_STATE_DIM, OBS_STATE_DIM],
|
|
175
|
-
sequence_names=[],
|
|
176
|
-
k_endog=ss_mod_me.k_endog,
|
|
177
|
-
)
|
|
178
|
-
# pylint: enable=unpacking-non-sequence
|
|
179
|
-
idata = pm.sample_prior_predictive(draws=10)
|
|
180
|
-
delete_rvs_from_model(["states_latent", "states_observed", "states_combined"])
|
|
181
|
-
|
|
182
|
-
assert idata.prior.coords["time"].shape == (101,)
|
|
183
|
-
assert all(
|
|
184
|
-
[dim in idata.prior.states_latent.coords.keys() for dim in [TIME_DIM, ALL_STATE_DIM]]
|
|
185
|
-
)
|
|
186
|
-
assert all(
|
|
187
|
-
[dim in idata.prior.states_observed.coords.keys() for dim in [TIME_DIM, OBS_STATE_DIM]]
|
|
188
|
-
)
|
|
189
|
-
assert not np.any(np.isnan(idata.prior[output_name].values))
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
@pytest.mark.parametrize("output_name", ["states_latent", "states_observed"])
|
|
193
|
-
def test_lgss_with_time_varying_inputs(output_name, rng):
|
|
194
|
-
X = rng.random(size=(10, 3), dtype=floatX)
|
|
195
|
-
ss_mod = structural.LevelTrendComponent() + structural.RegressionComponent(
|
|
196
|
-
name="exog", k_exog=3
|
|
197
|
-
)
|
|
198
|
-
mod = ss_mod.build("data", verbose=False)
|
|
199
|
-
|
|
200
|
-
coords = {
|
|
201
|
-
ALL_STATE_DIM: ["level", "trend", "beta_1", "beta_2", "beta_3"],
|
|
202
|
-
OBS_STATE_DIM: ["level"],
|
|
203
|
-
TIME_DIM: np.arange(10, dtype="int"),
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
with pm.Model(coords=coords):
|
|
207
|
-
exog_data = pm.Data("data_exog", X)
|
|
208
|
-
P0_diag = pm.Exponential("P0_diag", 1, shape=(mod.k_states,))
|
|
209
|
-
P0 = pm.Deterministic("P0", pt.diag(P0_diag))
|
|
210
|
-
initial_trend = pm.Normal("initial_trend", shape=(2,))
|
|
211
|
-
sigma_trend = pm.Exponential("sigma_trend", 1, shape=(2,))
|
|
212
|
-
beta_exog = pm.Normal("beta_exog", shape=(3,))
|
|
213
|
-
|
|
214
|
-
mod._insert_random_variables()
|
|
215
|
-
mod._insert_data_variables()
|
|
216
|
-
matrices = mod.unpack_statespace()
|
|
217
|
-
|
|
218
|
-
# pylint: disable=unpacking-non-sequence
|
|
219
|
-
latent_states, obs_states = LinearGaussianStateSpace(
|
|
220
|
-
"states",
|
|
221
|
-
*matrices,
|
|
222
|
-
steps=9,
|
|
223
|
-
sequence_names=["d", "Z"],
|
|
224
|
-
dims=[TIME_DIM, ALL_STATE_DIM, OBS_STATE_DIM],
|
|
225
|
-
)
|
|
226
|
-
# pylint: enable=unpacking-non-sequence
|
|
227
|
-
idata = pm.sample_prior_predictive(draws=10)
|
|
228
|
-
|
|
229
|
-
assert idata.prior.coords["time"].shape == (10,)
|
|
230
|
-
assert all(
|
|
231
|
-
[dim in idata.prior.states_latent.coords.keys() for dim in [TIME_DIM, ALL_STATE_DIM]]
|
|
232
|
-
)
|
|
233
|
-
assert all(
|
|
234
|
-
[dim in idata.prior.states_observed.coords.keys() for dim in [TIME_DIM, OBS_STATE_DIM]]
|
|
235
|
-
)
|
|
236
|
-
assert not np.any(np.isnan(idata.prior[output_name].values))
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def test_lgss_signature():
|
|
240
|
-
# Base case
|
|
241
|
-
x0 = pt.tensor("x0", shape=(None,))
|
|
242
|
-
P0 = pt.tensor("P0", shape=(None, None))
|
|
243
|
-
c = pt.tensor("c", shape=(None,))
|
|
244
|
-
d = pt.tensor("d", shape=(None,))
|
|
245
|
-
T = pt.tensor("T", shape=(None, None))
|
|
246
|
-
Z = pt.tensor("Z", shape=(None, None))
|
|
247
|
-
R = pt.tensor("R", shape=(None, None))
|
|
248
|
-
H = pt.tensor("H", shape=(None, None))
|
|
249
|
-
Q = pt.tensor("Q", shape=(None, None))
|
|
250
|
-
|
|
251
|
-
lgss = _LinearGaussianStateSpace.dist(x0, P0, c, d, T, Z, R, H, Q, steps=100)
|
|
252
|
-
assert (
|
|
253
|
-
lgss.owner.op.extended_signature
|
|
254
|
-
== "(s),(s,s),(s),(p),(s,s),(p,s),(s,r),(p,p),(r,r),[rng]->[rng],(t,n)"
|
|
255
|
-
)
|
|
256
|
-
assert lgss.owner.op.ndim_supp == 2
|
|
257
|
-
assert lgss.owner.op.ndims_params == [1, 2, 1, 1, 2, 2, 2, 2, 2]
|
|
258
|
-
|
|
259
|
-
# Case with time-varying matrices
|
|
260
|
-
T = pt.tensor("T", shape=(None, None, None))
|
|
261
|
-
lgss = _LinearGaussianStateSpace.dist(
|
|
262
|
-
x0, P0, c, d, T, Z, R, H, Q, steps=100, sequence_names=["T"]
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
assert (
|
|
266
|
-
lgss.owner.op.extended_signature
|
|
267
|
-
== "(s),(s,s),(s),(p),(t,s,s),(p,s),(s,r),(p,p),(r,r),[rng]->[rng],(t,n)"
|
|
268
|
-
)
|
|
269
|
-
assert lgss.owner.op.ndim_supp == 2
|
|
270
|
-
assert lgss.owner.op.ndims_params == [1, 2, 1, 1, 3, 2, 2, 2, 2]
|