4.24. The Unit-Sphere Indicator¶
This example shows how to encode a fixed Euclidean norm with an indicator UDF. The model is
where \(\delta_{\{\|x\|_2 = 1\}}\) is the indicator function of the unit sphere:
Minimizing this model projects \(y\) onto the unit sphere, so it can be used to obtain a unit-norm solution. Because the sphere is nonconvex, the solver acts as a practical local method and the result should be interpreted as a locally optimal solution or stationary point.
The value returned by UDFBase.eval() is the indicator itself:
So eval only checks whether the current vector has unit norm:
def eval(self, arglist):
x = np.asarray(arglist[0], dtype=float)
norm = np.linalg.norm(x)
return 0.0 if abs(norm - 1.0) <= 1e-9 else float("inf")
For \(v \neq 0\), the proximal step returned by UDFBase.argmin() is projection onto the sphere:
The implementation also handles the degenerate case \(v = 0\) by returning a fixed unit vector:
def argmin(self, lamb, arglist):
v = np.asarray(arglist[0], dtype=float)
norm = np.linalg.norm(v)
if norm <= 1e-12:
prox = np.zeros_like(v)
prox[0] = 1.0
return [prox.tolist()]
return [(v / norm).tolist()]
The UDFBase.arguments() method says this indicator depends on one symbolic vector:
def arguments(self):
return [self.arg]
Complete runnable example:
import numpy as np
import admm
class UnitSphereIndicator(admm.UDFBase):
def __init__(self, arg):
self.arg = arg
def arguments(self):
return [self.arg]
def eval(self, arglist):
x = np.asarray(arglist[0], dtype=float)
norm = np.linalg.norm(x)
return 0.0 if abs(norm - 1.0) <= 1e-9 else float("inf")
def argmin(self, lamb, arglist):
v = np.asarray(arglist[0], dtype=float)
norm = np.linalg.norm(v)
if norm <= 1e-12:
prox = np.zeros_like(v)
prox[0] = 1.0
return [prox.tolist()]
return [(v / norm).tolist()]
y = np.array([0.1, 0.0])
model = admm.Model()
x = admm.Var("x", 2)
model.setObjective(0.5 * admm.sum(admm.square(x - y)) + UnitSphereIndicator(x))
model.optimize()
print(" * x: ", np.asarray(x.X)) # Expected: ≈ [1, 0]
print(" * model.ObjVal: ", model.ObjVal) # Expected: ≈ 0.405
This example is available as a standalone script in the examples/ folder of the ADMM repository:
python examples/udf_unit_sphere.py
In this concrete example,
Since \(y \neq 0\), projection onto the unit sphere simply normalizes the vector:
The point \(x^\star\) lies on the sphere, so the indicator term is zero:
The main point of the example is geometric: the vector is simply normalized onto the sphere. The printed
model.ObjVal reports the final objective value directly.