docs: def relaxedEMDS(X0, N, d, C, b, KE, print_out=False, lamda=10):
      """ Find the set of points from an edge kernel with geometric constraints, using convex rank relaxation.
      """
      E = C.shape[1]
      X = Variable((E, E), PSD=True)
  
      constraints = [C[i, :] * X == b[i] for i in range(C.shape[0])]
  
      obj = Minimize(trace(X) + lamda * norm(KE - X))
      prob = Problem(obj, constraints)
  
      try:
          # CVXOPT is more accurate than SCS, even though slower.
          total_cost = prob.solve(solver='CVXOPT', verbose=print_out)
      except:
          try:
              print('CVXOPT with default cholesky failed. Trying kktsolver...')
              # kktsolver is more robust than default (cholesky), even though slower.
              total_cost = prob.solve(
                  solver='CVXOPT', verbose=print_out, kktsolver="robust")
          except:
              try:
                  print('CVXOPT with robust kktsovler failed. Trying SCS...')
                  # SCS is fast and robust, but inaccurate (last choice).
                  total_cost = prob.solve(solver='SCS', verbose=print_out)
              except:
                  print('SCS and CVXOPT solver with default and kktsolver failed .')
      if print_out:
          print('status:', prob.status)
      Xhat_KE, Vhat_KE = superMDS(X0, N, d, KE=X.value)
      return Xhat_KE, Vhat_KE