11.10 Semidefinite Relaxation of MIQCQO Problems¶
In this case study we will discuss a fairly common application for Semidefinite Optimization: to define a continuous semidefinite relaxation of a mixed-integer quadratic optimization problem. This section is based on the method by Park and Boyd [PB15].
We will focus on problems of the form:
where
integer least squares: minimize
subject to ,closest vector problem: minimize
subject to .
Following [PB15], we can derive a relaxed continuous model. We first relax the integrality constraint
The last constraint is still non-convex. We introduce a new variable
To get a conic problem we relax the last constraint and apply the Schur complement. The final relaxation follows:
Fusion Implementation
Implementing model (11.41) in Fusion is very simple. We assume the input
Model M = new Model();
The important step is to define a single PSD variable
Our code will create
Variable Z = M.Variable("Z", Domain.InPSDCone(n + 1));
Variable X = Z.Slice(new int[] {0, 0}, new int[] {n, n});
Variable x = Z.Slice(new int[] {0, n}, new int[] {n, n + 1});
Then we define the constraints:
M.Constraint( Expr.Sub(X.Diag(), x), Domain.GreaterThan(0.0) );
M.Constraint( Z.Index(n, n), Domain.EqualsTo(1.0) );
The objective function uses several available linear expressions:
M.Objective( ObjectiveSense.Minimize, Expr.Add(
Expr.Sum( Expr.MulElm( P, X ) ),
Expr.Mul( 2.0, Expr.Dot(x, q) )
) );
Note that the trace operator is not directly available in Fusion, but it can easily be defined from scratch.
Complete code
public static Model miqcqp_sdo_relaxation(int n, Matrix P, double[] q) {
Model M = new Model();
Variable Z = M.Variable("Z", Domain.InPSDCone(n + 1));
Variable X = Z.Slice(new int[] {0, 0}, new int[] {n, n});
Variable x = Z.Slice(new int[] {0, n}, new int[] {n, n + 1});
M.Constraint( Expr.Sub(X.Diag(), x), Domain.GreaterThan(0.0) );
M.Constraint( Z.Index(n, n), Domain.EqualsTo(1.0) );
M.Objective( ObjectiveSense.Minimize, Expr.Add(
Expr.Sum( Expr.MulElm( P, X ) ),
Expr.Mul( 2.0, Expr.Dot(x, q) )
) );
return M;
}
Numerical Examples
We present now some simple numerical experiments for the integer least squares problem:
It corresponds to the problem (11.40) with
We implement the linear algebra operations using the LinAlg
module available in MOSEK.
An integer rounding xRound
of the solution to (11.41) is a feasible integer solution to (11.42). We can compare it to the actual optimal integer solution xOpt
, whenever the latter is available. Of course it is very simple to formulate the integer least squares problem in Fusion:
public static Model int_least_squares(int n, Matrix A, double[] b) {
Model M = new Model();
Variable x = M.Variable("x", n, Domain.Integral(Domain.Unbounded()));
Variable t = M.Variable("t", 1, Domain.Unbounded());
M.Constraint( Expr.Vstack(t, Expr.Sub(Expr.Mul(A, x), b)), Domain.InQCone() );
M.Objective( ObjectiveSense.Minimize, t );
return M;
}
All that remains is to compare the values of the objective function
// problem dimensions
int n = 20;
int m = 2 * n;
// problem data
double[] A = new double[m * n];
double[] b = new double[m];
double[] c = new double[n];
double[] P = new double[n * n];
double[] q = new double[n];
for (int j = 0; j < n; j++) {
for (int i = 0; i < m; i++) {
double u1 = rnd.NextDouble(); //these are uniform(0,1) random doubles
double u2 = rnd.NextDouble();
A[i * n + j] = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2);
}
c[j] = rnd.NextDouble();
}
// P = A^T A
LinAlg.syrk(mosek.uplo.lo, mosek.transpose.yes,
n, m, 1.0, A, 0.0, P);
for (int j = 0; j < n; j++) for (int i = j + 1; i < n; i++) P[i * n + j] = P[j * n + i];
// q = -P c, b = A c
LinAlg.gemv(mosek.transpose.no, n, n, -1.0, P, c, 0.0, q);
LinAlg.gemv(mosek.transpose.no, m, n, 1.0, A, c, 0.0, b);
// Solve the problems
Model M = miqcqp_sdo_relaxation(n, Matrix.Dense(n, n, P), q);
Model Mint = int_least_squares(n, Matrix.Dense(n, m, A).Transpose(), b);
M.Solve();
Mint.Solve();
// rounded and optimal solution
double[] xRound = M.GetVariable("Z").Slice(new int[] {0, n}, new int[] {n, n + 1}).Level();
for (int i = 0; i < n; i++) xRound[i] = Math.Round(xRound[i]);
double[] yRound = (double[]) b.Clone();
double[] xOpt = Mint.GetVariable("x").Level();
double[] yOpt = (double[]) b.Clone();
LinAlg.gemv(mosek.transpose.no, m, n, 1.0, A, xRound, -1.0, yRound); // Ax_round-b
LinAlg.gemv(mosek.transpose.no, m, n, 1.0, A, xOpt, -1.0, yOpt); // Ax_opt-b
Console.WriteLine(M.GetSolverDoubleInfo("optimizerTime") + " " + Mint.GetSolverDoubleInfo("optimizerTime"));
Console.WriteLine(Math.Sqrt(LinAlg.dot(m, yRound, yRound)) + " " +
Math.Sqrt(LinAlg.dot(m, yOpt, yOpt)));
Experimentally the objective value for xRound
approximates the optimal solution with a factor of