Skip to content

Commit 24c6a12

Browse files
committed
feat: Introduce paper and figure generation script for PSID simulations, alongside minor test and documentation improvements.
1 parent 67200a9 commit 24c6a12

5 files changed

Lines changed: 23 additions & 0 deletions

File tree

source/PSID/tests/test_IPSID.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ def test_IPSID(self):
158158
allZp2, allYp2, allXp2 = sId3_cp.predict(Y, U=U)
159159

160160
try:
161+
# Verify identification errors are within tolerance
161162
np.testing.assert_array_less(
162163
[err[p] for p in params],
163164
5e-2,
@@ -179,6 +180,7 @@ def test_IPSID(self):
179180
err_msg="Error too large for some params {}".format(params),
180181
)
181182

183+
# Verify decoding accuracy
182184
np.testing.assert_allclose(allXp2, allXp, rtol=1e-3)
183185
np.testing.assert_allclose(allZp2, allZp - ZMean, atol=1e-6)
184186
np.testing.assert_allclose(allYp2, allYp - YMean, atol=1e-6)
@@ -263,6 +265,7 @@ def test_IPSID_enforce_stability(self):
263265
else:
264266
print("Ok")
265267
print("Done")
268+
# Verify eigenvalues are within unit circle
266269
np.testing.assert_array_less(eigsAbs, 1)
267270

268271
if failInds:

source/PSID/tests/test_LSSM.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ def test_generateRealizationWithKF(self):
8181
plt.legend()
8282
"""
8383
mse.append(np.mean((Y - Yp) ** 2))
84+
85+
# Verify stochastic and predictor realizations match
8486
np.testing.assert_almost_equal(Y, Yp, decimal=10)
8587
# np.testing.assert_equal(Y, Yp)
8688
# np.testing.assert_allclose(Y, Yp, rtol=1e-3)
@@ -105,6 +107,8 @@ def test_solveric(self):
105107
out = (np.array([[0.6479, 0.0861], [0.6025, -0.1736]]), True)
106108

107109
P, has_solution = solveric(**inp)
110+
111+
# Verify solveric result against known solution
108112
np.testing.assert_equal(out[1], has_solution)
109113
np.testing.assert_allclose(out[0], P, rtol=1e-3)
110114

@@ -128,6 +132,7 @@ def test_solve_discrete_are_iterative(self):
128132
s.A.T, s.C.T, s.Q, s.R, s=s.S, return_log=True
129133
)
130134

135+
# Verify iterative solver matches scipy implementation
131136
np.testing.assert_almost_equal(Pp1, Pp2)
132137
np.testing.assert_almost_equal(0, PpNormChanges[-1])
133138

@@ -153,6 +158,7 @@ def test_LSSM_randomize_in_predictor_form(self):
153158
etcBlock = np.copy(s.A_KC)
154159
etcBlock[:n1, n1:] = 0
155160
try:
161+
# Verify predictor form structure
156162
np.testing.assert_almost_equal(trBlock, np.zeros_like(trBlock))
157163
np.testing.assert_allclose(
158164
(s.A_KC - etcBlock) / np.linalg.norm(etcBlock),
@@ -313,6 +319,7 @@ def test_kalmanFilterAndSmoothingForS0Nu0(self):
313319
filtered_state_means_2, filtered_state_covariances_2 = kf.filter(Y)
314320
smoothed_state_means_2, smoothed_state_covariances_2 = kf.smooth(Y)
315321

322+
# Verify against pykalman
316323
np.testing.assert_allclose(
317324
filtered_state_means, filtered_state_means_2, rtol=1e-3
318325
)
@@ -511,6 +518,7 @@ def test_forwardBackwardSmootherBeingTheSameAsRTS(self):
511518
# """
512519

513520
try:
521+
# Verify Forward-Backward smoother matches RTS
514522
np.testing.assert_allclose(allXp, allXpFB, rtol=1e-4)
515523
np.testing.assert_allclose(allXs, allXsFB, rtol=1e-4)
516524
np.testing.assert_allclose(allPs, allPsFB, rtol=1e-4)

source/PSID/tests/test_PSID.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ def test_PSID(self):
120120
allZp2, allYp2, allXp2 = sId3_cp.predict(Y, U=None)
121121

122122
try:
123+
# Verify identification errors are within tolerance
123124
np.testing.assert_array_less(
124125
[err[p] for p in params],
125126
5e-2,
@@ -141,6 +142,7 @@ def test_PSID(self):
141142
err_msg="Error too large for some params {}".format(params),
142143
)
143144

145+
# Verify decoding accuracy
144146
np.testing.assert_allclose(allXp2, allXp, rtol=1e-3)
145147
np.testing.assert_allclose(allZp2, allZp - ZMean, atol=1e-6)
146148
np.testing.assert_allclose(allYp2, allYp - YMean, atol=1e-6)
@@ -192,6 +194,7 @@ def test_PSID_doest_change_inputs(self):
192194
UBU = copy.deepcopy(U)
193195

194196
sId = SubspacePSID(Y, Z, nx=nx, n1=n1, i=horizon, time_first=True)
197+
# Verify input data remains unchanged
195198
np.testing.assert_equal(Y, YBU)
196199
np.testing.assert_equal(Z, ZBU)
197200
np.testing.assert_equal(U, UBU)

source/PSID/tests/test_PSIDModel.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def test_PSID(self):
130130
err2.update(computeLSSMIdError(s, sId2.model))
131131

132132
try:
133+
# Verify identification errors are within tolerance
133134
np.testing.assert_array_less(
134135
[err[p] for p in params],
135136
5e-2,
@@ -151,6 +152,7 @@ def test_PSID(self):
151152
err_msg="Error too large for some params {}".format(params),
152153
)
153154

155+
# Verify decoding accuracy
154156
np.testing.assert_allclose(allXp2, allXp, rtol=1e-3)
155157
np.testing.assert_allclose(allZp2, allZp - ZMean, atol=1e-6)
156158
np.testing.assert_allclose(allYp2, allYp - YMean, atol=1e-6)

source/PSID/tests/test_PrepModel.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def test_preprocessing(self):
5454
sm = PrepModel()
5555
sm.fit(data, time_first=time_first, **args)
5656

57+
# Verify input data remains unchanged
5758
np.testing.assert_equal(data, dataCopy)
5859

5960
ddof = args["std_ddof"] if "std_ddof" in args else 1
@@ -66,16 +67,19 @@ def test_preprocessing(self):
6667
newDataStd = np.std(newData, axis=1, ddof=ddof)
6768

6869
if args["zscore"] or args["remove_mean"]:
70+
# Verify mean is zero
6971
np.testing.assert_almost_equal(
7072
newDataMean, np.zeros_like(newDataMean)
7173
)
7274
if args["zscore"]:
75+
# Verify std is one
7376
np.testing.assert_almost_equal(
7477
newDataStd, np.ones_like(newDataStd)
7578
)
7679

7780
recoveredData = sm.apply_inverse(newData, time_first=time_first)
7881

82+
# Verify inverse transform recovers original data
7983
np.testing.assert_almost_equal(recoveredData, dataCopy)
8084

8185
def test_preprocessing_for_segmented_data(self):
@@ -133,17 +137,20 @@ def test_preprocessing_for_segmented_data(self):
133137
newDataStd = np.std(newDataCat, axis=1, ddof=ddof)
134138

135139
if args["zscore"] or args["remove_mean"]:
140+
# Verify mean is zero
136141
np.testing.assert_almost_equal(
137142
newDataMean, np.zeros_like(newDataMean)
138143
)
139144
if args["zscore"]:
145+
# Verify std is one
140146
np.testing.assert_almost_equal(
141147
newDataStd, np.ones_like(newDataStd)
142148
)
143149

144150
recoveredData = sm.apply_inverse(newData, time_first)
145151

146152
for t in range(len(dataCopy)):
153+
# Verify recovery of segmented data
147154
np.testing.assert_almost_equal(
148155
recoveredData[t], dataCopy[t]
149156
)

0 commit comments

Comments
 (0)