问题
I've attempted to implement the Calafiore (2002) method of fitting ellipsoids to data using CVXPY as a wrapper to SCS to perform the semi-definite programming.
When I run this implementation, I get decent agreement with the first ellipsoid of Section IVB (Fig 6a). However, I don't get numeric agreement with the second ellipsoid of that section (Fig 6b), although the results are visually close.
Worse, when I try to fit a very well defined ellipse (the points below) I get an obviously poor fit (the line below):
Given the good agreement with the first ellipse, I suspect a numerical issue with SCS, but I'm not sure how to debug or correct this.
My code is as follows:
#!/usr/bin/env python3
import cvxpy as cp
import matplotlib.pyplot as plt
import numpy as np
import sklearn.datasets
#Equation 19
def build_omega(A,b,d,dim):
rows = []
for i in range(dim):
rows.append(cp.hstack([A[i], b[i]]))
rows.append(cp.hstack([b,d]))
return cp.vstack(rows)
#Equation 19
def gifunc(omega, xi):
return cp.quad_form(cp.hstack([xi,1]),omega)
def BestFitEllipsoid_L2(points):
dim = points.shape[1]
A = cp.Variable((dim,dim), PSD=True)
b = cp.Variable(dim)
d = cp.Variable()
gamma = cp.Variable(len(points), pos=True)
omega = build_omega(A,b,d,dim)
constraints = [cp.trace(A)==1] #Equation 22
for idx,pt in enumerate(points):
gi = gifunc(omega,pt)
temp = cp.bmat([[1,gi],[gi,gamma[idx]]])
constraints.append(temp>>0) #Equation 21
prob = cp.Problem(cp.Minimize(cp.sum(gamma)), constraints) #Equation 20
optval = prob.solve(solver=cp.SCS, verbose=True, eps=1e-6)
print(f"Optimal value: {optval}")
c = -np.linalg.solve(A.value,b.value)
r2 = np.abs(c@A.value@c-d.value)
return A.value/r2, c
def get_ellipse_points(A, c, count=100, upper=2*np.pi):
#plots an ellipse of the form xEx = 1
R = np.linalg.cholesky(A);
t = np.linspace(0, upper, count) #Or any high number to make curve smooth
cosv = np.cos(t)
sinv = np.sin(t)
z = np.vstack([cosv, sinv])
ellipse = np.linalg.solve(R,z).T
return ellipse+c
def Test1():
points = np.array([
[1,7],
[2,6],
[3,7],
[5,8],
[6,2],
[7,7],
[8,4],
[9,5],
])
A, c = BestFitEllipsoid_L2(points)
assert np.allclose(A, np.array([[0.3784, 0.1770], [0.1770, 0.6216]])/4.3763, rtol=1e-3)
assert np.allclose(c, np.array([5.1978, 5.1093]), rtol=1e-3)
def Test2():
points = np.array([
[ 2.0143, 10.5575],
[ 17.3465, 3.2690],
[ -8.5257, -7.2959],
[ -7.9109, -7.6447],
[ 16.3705, -3.8815],
[-15.3434, 5.0513],
[-21.5840, -0.6013],
[ 9.4111, -9.0697],
])
A, c = BestFitEllipsoid_L2(points)
# DOESN'T PASS!
# assert np.alllose(A, np.array([[0.1944, 0.0276], [0.0276, 0.8056]])/73.3422, rtol=1e-3)
# DOESN'T PASS!
# assert np.allclose(c, np.array([-0.3973, 0.5149]), rtol=1e-3)
Test1()
Test2()
rW = sklearn.datasets.make_spd_matrix(2, random_state=123451)
rd = np.array([2,3])
points = get_ellipse_points(rW, rd, count=50)
points += np.random.multivariate_normal(mean=[0]*2, cov=0.0005*np.eye(2), size=len(points))
A, c = BestFitEllipsoid_L2(points)
fig, ax = plt.subplots()
ax.scatter(points[:,0],points[:,1])
ellipse = get_ellipse_points(A, c)
ax.plot(ellipse[:,0],ellipse[:,1])
plt.show()
来源:https://stackoverflow.com/questions/61992441/numeric-issues-during-ellipsoid-fitting-using-with-scs-to-perform-sdps