I am currently trying to implement a piecewise McCormick envelope in Drake (c++). The current issue I am having is that the solution produced by the optimization does not produce a valid $x$ and $y$ values (outside my specified bounds or infeasible) and "$w$" is not close to $x*y$
I have used several references such as A Global Quasi-Dynamic Model for Contact-Trajectory Optimization, McCormick envelopes Northwestern Wiki, and more, but I have based off the code below from Mixed-Integer Convex Optimization for Planning Aggressive Motions of Legged Robots Over Rough Terrain.
A few things that I felt were missing from this particular paper was the sum of the binary decision variable $z$ equal to 1. If I'm not mistaken, one piece of the piecewise McCormick envelope gets chosen by the optimization, thus, the sum of $z$ should be equal to 1.
Gurobi was used to solve this problem.
I would like to know if I am missing any major components to the piecewise McCormick envelope.
solvers::MathematicalProgram prog = solvers::MathematicalProgram();
// For this problem, M < 4 gives a solution. However, it may be incorrect.
// M >= 4 makes the problem infeasible
const int M = 6; //number of partitions
solvers::VectorDecisionVariable<1> x = prog.NewContinuousVariables<1>("x");
solvers::VectorDecisionVariable<1> y = prog.NewContinuousVariables<1>("y");
solvers::VectorXDecisionVariable x_hat = prog.NewContinuousVariables(M, "x_hat");
solvers::VectorXDecisionVariable y_hat = prog.NewContinuousVariables(M, "y_hat");
solvers::VectorDecisionVariable<1> w = prog.NewContinuousVariables<1>("w");
solvers::VectorXDecisionVariable z = prog.NewBinaryVariables(M, "z");
//solvers::VectorDecisionVariable<1> lorentz = prog.NewContinuousVariables<1>("lorentz");
const double x_upper = 1;
const double x_lower = 0;
// Replacing quadratic cost with rotated lorentz cone in the future. (developer's suggesting for better results)
// prog.AddLinearCost(lorentz[0]);
// prog.AddRotatedLorentzConeConstraint(
// symbolic::Expression(lorentz[0]), symbolic::Expression(1), (0.4-w[0])*2*(0.4-w[0]) + (0.5-x[0])*10*(0.5-x[0])
// );
//Min 2*(0.4-w)^2 + 10*(0.5-x)^2 --> solution should be x:0.5, y:0.8, w:0.4
prog.AddCost((0.4-w[0])*2*(0.4-w[0]) + (0.5-x[0])*10*(0.5-x[0]));
VectorXd x_bounds(M+1);
x_bounds = VectorXd::Zero(M+1);
// Generate evenly distributed upper and lower bounds
for(int i = 0; i <= M; i++){
x_bounds(i) = i*(x_upper-x_lower)/M + x_lower;
}
Expression e_x; //Sum of x_hat
Expression e_y; //Sum of y_hat
Expression e_z; //Sum of z
Expression e_1;
Expression e_2;
Expression e_3;
Expression e_4;
for(int k = 0; k < M; k++){
// Bound constraints for all x_hat and y_hat values
prog.AddLinearConstraint(
x_bounds(k)*z[k] <= x_hat[k] && x_hat[k] <= x_bounds(k+1)*z[k] &&
0 <= y_hat[k] && y_hat[k] <= z[k]
);
// The constraint below has been removed.
// y_bounds(k)*z[k] <= y_hat[k] && y_hat[k] <= y_bounds(k+1)*z[k]
// Add sum of lower and upper bounds
e_1 += x_bounds(k)*y_hat[k];
e_2 += x_bounds(k+1)*y_hat[k] + x_hat[k] - x_bounds(k+1);
e_3 += x_bounds(k)*y_hat[k] + x_hat[k] - x_bounds(k);
e_4 += x_bounds(k+1)*y_hat[k];
e_x += x_hat[k]; // e_x += x_hat[M-1]; The paper has capital M but I assumed that this is a mistake.
e_y += y_hat[k]; // e_y += y_hat[M-1];
e_z += z[k]; //Sum of binary decision variable z
}
prog.AddLinearConstraint(
w[0] >= e_1 &&
w[0] >= e_2 &&
w[0] <= e_3 &&
w[0] <= e_4 &&
x[0] == e_x &&
y[0] == e_y &&
e_z == 1
);