Skip to content

Commit a2904ff

Browse files
committed
Merge branch 'main' of github.com:scope-lab/pyest
2 parents 4903dec + 6df8215 commit a2904ff

File tree

2 files changed

+33
-12
lines changed

2 files changed

+33
-12
lines changed

pyest/filters/sigma_points.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class SigmaPointOptions:
3131
Compute the lambda parameter for the unscented transform
3232
3333
"""
34+
3435
def __init__(self, alpha=1e-3, beta=2, kappa=0):
3536
self.alpha = alpha
3637
self.beta = beta
@@ -40,7 +41,10 @@ def lambda_(self, nx):
4041
lam = self.alpha**2 * (nx + self.kappa) - nx # UT lambda parameter
4142
return lam
4243

43-
def unscented_transform(m, cov, g, sigma_pt_opts, cov_type='full', add_noise=None, residual_fun=None, mean_fun=None, g_args=()):
44+
45+
def unscented_transform(m, cov, g, sigma_pt_opts, cov_type='full',
46+
add_noise=None, residual_fun=None, mean_fun=None,
47+
g_args=()):
4448
"""
4549
Computes the unscented transform of an input vector and covariance.
4650
@@ -95,8 +99,9 @@ def unscented_transform(m, cov, g, sigma_pt_opts, cov_type='full', add_noise=Non
9599
elif cov_type == 'cholesky':
96100
S = cov
97101
if not np.allclose(S, np.tril(S)):
98-
raise BadCholeskyFactor('Provided cholesky factor is not lower-triangular')
99-
#assert np.allclose(S, np.tril(S)), 'Provided cholesky factor is not lower-triangular'
102+
raise BadCholeskyFactor(
103+
'Provided cholesky factor is not lower-triangular')
104+
# assert np.allclose(S, np.tril(S)), 'Provided cholesky factor is not lower-triangular'
100105
else:
101106
raise ValueError('Unrecognized covariance type.')
102107

@@ -125,16 +130,20 @@ def unscented_transform(m, cov, g, sigma_pt_opts, cov_type='full', add_noise=Non
125130
cov_t += add_noise
126131
elif cov_type == 'cholesky':
127132
if add_noise is not None:
128-
_, cov_t = qr(np.hstack([np.tile(np.sqrt(sigmas.wc[1:]),(len(y0),1)) * Dt[:, 1:].T, add_noise]).T, mode='economic')
133+
_, cov_t = qr(np.hstack([
134+
np.tile(np.sqrt(sigmas.wc[1:]), (len(y0), 1)) * Dt[:, 1:],
135+
add_noise]).T, mode='economic')
129136
else:
130-
_, cov_t = qr((np.tile(np.sqrt(sigmas.wc[1:]),(len(y0),1)) * Dt[:, 1:]).T, mode='economic')
137+
_, cov_t = qr(
138+
(np.tile(np.sqrt(sigmas.wc[1:]), (len(y0), 1)) * Dt[:, 1:]).T, mode='economic')
131139
cov_t = make_chol_diag_positive(cov_t.T).T
132-
cov_t = choldowndate(cov_t,np.sqrt(-sigmas.wc[0]) * Dt[:, 0]).T
140+
cov_t = choldowndate(cov_t, np.sqrt(-sigmas.wc[0]) * Dt[:, 0]).T
133141
else:
134142
raise ValueError('Unrecognized covariance type.')
135143

136144
return mt, cov_t, Dt, sigmas, y
137145

146+
138147
def angular_residual(y, m):
139148
''' Compute angular residuals between y and m
140149
@@ -155,6 +164,7 @@ def angular_residual(y, m):
155164
res[res < -np.pi] += 2*np.pi
156165
return res
157166

167+
158168
def optimize_angle(angles, weights, max_iterations=100, tolerance=1e-6):
159169

160170
theta_hat = angles[0]
@@ -179,6 +189,7 @@ def optimize_angle(angles, weights, max_iterations=100, tolerance=1e-6):
179189
theta_hat = theta_hat + 2*np.pi
180190
return theta_hat
181191

192+
182193
class SigmaPoints(object):
183194
"""
184195
Generate sigma points for the Unscented Kalman Filter.
@@ -197,17 +208,20 @@ class SigmaPoints(object):
197208
198209
Notes
199210
-----
200-
The function follows the 2n+1 sigma point rule, where n is the dimension of the mean vector x.
211+
The function follows the 2n+1 sigma point rule, where n is the dimension
212+
of the mean vector x.
201213
"""
214+
202215
def __init__(self, m, S, opts):
203216
n = len(m)
204217
lambda_ = opts.lambda_(n)
205218
gamma_squared = lambda_ + n
206219
# composite scaling factor for scaled unscented transform
207220
gamma = np.sqrt(gamma_squared)
208-
self.wm = np.hstack([lambda_/gamma_squared, np.repeat(1/(2*gamma_squared), 2*n)])
221+
self.wm = np.hstack(
222+
[lambda_/gamma_squared, np.repeat(1/(2*gamma_squared), 2*n)])
209223
self.wc = self.wm.copy()
210224
self.wc[0] += 1 - opts.alpha**2 + opts.beta
211225
delta = gamma*S
212226
y = np.tile(m, (n, 1)).T
213-
self.X = np.hstack([m[:, np.newaxis], y + delta, y - delta])
227+
self.X = np.hstack([m[:, np.newaxis], y + delta, y - delta])

tests/test_sigma_points.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ def test_sigma_points():
3232

3333
def test_unscented_transform():
3434
m = np.array([6, 7])
35-
n = m.shape[0]
35+
nx = m.shape[0]
3636
Stemp = np.array([[0.6, 0.5], [0.1, 1.0]])
3737
P = Stemp @ Stemp.T
38-
Schol = cholesky(P,lower=True)
38+
Schol = cholesky(P, lower=True)
3939

4040
alpha = 1e-3
4141
kappa = 0
4242
beta = 2
43-
opts = pysp.SigmaPointOptions(alpha,beta,kappa)
43+
opts = pysp.SigmaPointOptions(alpha, beta, kappa)
4444
f = np.sin
4545

4646
mt, Pt, Dt, SP, y = pysp.unscented_transform(
@@ -71,6 +71,13 @@ def test_unscented_transform():
7171
npt.assert_almost_equal(SP_des, SP.X, decimal=15)
7272
npt.assert_almost_equal(wc_des, SP.wc, decimal=15)
7373

74+
# repeat using square root factor with additive noise
75+
Qchol = np.array([[0.2, 0], [1, 1.4]])
76+
mt, St, Dt, SP, y = pysp.unscented_transform(
77+
m, Schol, f, opts, cov_type='cholesky', add_noise=Qchol)
78+
npt.assert_almost_equal(mt_des, mt, decimal=15)
79+
npt.assert_almost_equal(Pt_des + Qchol@Qchol.T, St@St.T, decimal=10)
80+
7481
# test checking of lower triangular cholesky factor
7582
fail(pysp.unscented_transform, BadCholeskyFactor, m, Schol.T, f, opts, cov_type='cholesky')
7683

0 commit comments

Comments
 (0)