5
5
from numpy .testing import assert_allclose
6
6
from pytensor import config
7
7
from pytensor import tensor as pt
8
+ from pytensor .graph .basic import explicit_graph_inputs
8
9
9
10
from pymc_extras .statespace .models import structural as st
10
11
from tests .statespace .models .structural .conftest import _assert_basic_coords_correct
@@ -25,7 +26,7 @@ def test_exogenous_component(rng):
25
26
# Check that the generated data is just a linear regression
26
27
assert_allclose (y , data @ params ["beta_exog" ], atol = ATOL , rtol = RTOL )
27
28
28
- mod .build (verbose = False )
29
+ mod = mod .build (verbose = False )
29
30
_assert_basic_coords_correct (mod )
30
31
assert mod .coords ["exog_state" ] == ["feature_1" , "feature_2" ]
31
32
@@ -42,6 +43,73 @@ def test_adding_exogenous_component(rng):
42
43
assert_allclose (mod .ssm ["design" , 5 , 0 , :2 ].eval ({"data_exog" : data }), data [5 ])
43
44
44
45
46
+ def test_regression_with_multiple_observed_states (rng ):
47
+ from scipy .linalg import block_diag
48
+
49
+ data = rng .normal (size = (100 , 2 )).astype (config .floatX )
50
+ mod = st .RegressionComponent (
51
+ state_names = ["feature_1" , "feature_2" ],
52
+ name = "exog" ,
53
+ observed_state_names = ["data_1" , "data_2" ],
54
+ )
55
+
56
+ params = {"beta_exog" : np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]], dtype = config .floatX )}
57
+ exog_data = {"data_exog" : data }
58
+ x , y = simulate_from_numpy_model (mod , rng , params , exog_data )
59
+
60
+ assert x .shape == (100 , 4 ) # 2 features, 2 states
61
+ assert y .shape == (100 , 2 )
62
+
63
+ # Check that the generated data are two independent linear regressions
64
+ assert_allclose (y [:, 0 ], data @ params ["beta_exog" ][0 ], atol = ATOL , rtol = RTOL )
65
+ assert_allclose (y [:, 1 ], data @ params ["beta_exog" ][1 ], atol = ATOL , rtol = RTOL )
66
+
67
+ mod = mod .build (verbose = False )
68
+ assert mod .coords ["exog_state" ] == [
69
+ "feature_1[data_1]" ,
70
+ "feature_2[data_1]" ,
71
+ "feature_1[data_2]" ,
72
+ "feature_2[data_2]" ,
73
+ ]
74
+
75
+ Z = mod .ssm ["design" ].eval ({"data_exog" : data })
76
+ vec_block_diag = np .vectorize (block_diag , signature = "(n,m),(o,p)->(q,r)" )
77
+ assert Z .shape == (100 , 2 , 4 )
78
+ assert np .allclose (Z , vec_block_diag (data [:, None , :], data [:, None , :]))
79
+
80
+
81
+ def test_add_regression_components_with_multiple_observed_states (rng ):
82
+ from scipy .linalg import block_diag
83
+
84
+ data_1 = rng .normal (size = (100 , 2 )).astype (config .floatX )
85
+ data_2 = rng .normal (size = (100 , 1 )).astype (config .floatX )
86
+
87
+ reg1 = st .RegressionComponent (
88
+ state_names = ["a" , "b" ], name = "exog1" , observed_state_names = ["data_1" , "data_2" ]
89
+ )
90
+ reg2 = st .RegressionComponent (state_names = ["c" ], name = "exog2" , observed_state_names = ["data_3" ])
91
+
92
+ mod = (reg1 + reg2 ).build (verbose = False )
93
+ assert mod .coords ["exog1_state" ] == ["a[data_1]" , "b[data_1]" , "a[data_2]" , "b[data_2]" ]
94
+ assert mod .coords ["exog2_state" ] == ["c[data_3]" ]
95
+
96
+ Z = mod .ssm ["design" ].eval ({"data_exog1" : data_1 , "data_exog2" : data_2 })
97
+ vec_block_diag = np .vectorize (block_diag , signature = "(n,m),(o,p)->(q,r)" )
98
+ assert Z .shape == (100 , 3 , 5 )
99
+ assert np .allclose (
100
+ Z ,
101
+ vec_block_diag (vec_block_diag (data_1 [:, None , :], data_1 [:, None , :]), data_2 [:, None , :]),
102
+ )
103
+
104
+ x0 = mod .ssm ["initial_state" ].eval (
105
+ {
106
+ "beta_exog1" : np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]], dtype = config .floatX ),
107
+ "beta_exog2" : np .array ([5.0 ], dtype = config .floatX ),
108
+ }
109
+ )
110
+ np .testing .assert_allclose (x0 , np .array ([1.0 , 2.0 , 3.0 , 4.0 , 5.0 ], dtype = config .floatX ))
111
+
112
+
45
113
def test_filter_scans_time_varying_design_matrix (rng ):
46
114
time_idx = pd .date_range (start = "2000-01-01" , freq = "D" , periods = 100 )
47
115
data = pd .DataFrame (rng .normal (size = (100 , 2 )), columns = ["a" , "b" ], index = time_idx )
0 commit comments