Numeric issues during ellipsoid fitting using with SCS to perform SDPs?

我怕爱的太早我们不能终老 提交于 2020-06-16 17:46:38

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!