Solving mixed-integer quadratic programming (MIQP) using Julia+JuMP
Shuvomoy Das Gupta
January 20, 2020
In this blog, we will discuss how to solve a mixed-integer quadratic programming problem (MIQP) using Julia and JuMP. My versions of Julia, JuMP, and Gurobi are 1.3.0, 0.20.1, and 0.7.4, respectively.
As an illustrative example, we will consider the sparse regression problem. The sparse regression is a nonconvex optimization problem with applications to gene expression analysis and signal processing etc.
Sparse regression problem. The sparse regression problem is concerned with approximating a vector with a linear combination of at most columns of a matrix with bounded coefficients. The problem can be written as the following optimization problem
where is the decision variable, and and are problem data. Here, is the number of nonzero components in .
Modeling sparse regression problem as a MIQP. The sparse regression problem can be modeled as the following MIQP:
where are decision variables.
We can write our objective function as
which takes the function in a quadratic form.
Rewriting the objective in a compatible format. If we define and then we can write the objective function as:
which is more compatible as an input for JuMP.
Rewriting the bound constraint in a more compatible format. Also, for JuMP, we write the bound constraint as two constraints: , and for .
The code is as follows.
In [1]:
# Load the necessary packages
# ---------------------------
using Gurobi, JuMP, LinearAlgebra
# if these packages are not installed then run:
# using Pkg
# Pkg.add("Gurobi")
# Pkg.add("JuMP")
# Also, keep in mind that Gurobi is a commercial solver, but it is free for academic use. In [2]:
# Data, change it accordingly
# ---------------------------
m = 5
n = 10
A = randn(m,n)
b = randn(m)
M = 1
k = convert(Int64, round(m/3))
# Renaming a bunch of variables
S = A'*A
c = -2*A'*b
d = norm(b)^2 Out[2]:
3.511217774252138
In [3]:
# Define the model
# ----------------
model = Model(with_optimizer(Gurobi.Optimizer)) # define name of the model, it could be anything, not necessarily "model"
# Variables
# ---------
@variable(model, x[1:n]) # define variable x
@variable(model, y[1:n], Bin) # define the binary variable y
# Objective
# ---------
sense = MOI.MIN_SENSE # by this command, we are programatically defining a quadratic objective to be minimized
@objective(model, sense, sum(S[i,j]*x[i]*x[j] for i in 1:n, j in 1:n)+ sum(c[i]*x[i] for i in 1:n) + d) # define the objective
# Constraints
# ------------
@constraint(model, con_lb[i=1:n], -M*y[i] <= x[i]) # lower bound constraint
@constraint(model, con_ub[i=1:n], x[i] <= M*y[i]) # upper bound constraint
@constraint(model, con_bd_sum, sum(y[i] for i in 1:n) <= k) # cardinality constraint in terms of y
# Run the optimizer
# -----------------
status=optimize!(model) # time to optimize!
# Let us look at the important outputs
# ------------------------------------
println("******************************************************")
println("optimal objective value is = ", objective_value(model))
println("optimal x is = ", value.(x))
println("optimal y is =", value.(y))
Academic license - for non-commercial use only
Academic license - for non-commercial use only
Optimize a model with 21 rows, 20 columns and 50 nonzeros
Model has 55 quadratic objective terms
Variable types: 10 continuous, 10 integer (10 binary)
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [6e-01, 7e+00]
QObjective range [2e-02, 3e+01]
Bounds range [0e+00, 0e+00]
RHS range [2e+00, 2e+00]
Found heuristic solution: objective 3.5112178
Presolve time: 0.00s
Presolved: 21 rows, 20 columns, 50 nonzeros
Presolved model has 55 quadratic objective terms
Variable types: 10 continuous, 10 integer (10 binary)
Root relaxation: objective 1.776357e-15, 45 iterations, 0.00 seconds
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 0.00000 0 6 3.51122 0.00000 100% - 0s
H 0 0 1.5146229 0.00000 100% - 0s
0 0 0.00000 0 6 1.51462 0.00000 100% - 0s
H 0 0 0.9576732 0.00000 100% - 0s
0 2 0.00000 0 6 0.95767 0.00000 100% - 0s
H 18 7 0.7568978 0.59570 21.3% 4.8 0s
Explored 25 nodes (161 simplex iterations) in 0.02 seconds
Thread count was 8 (of 8 available processors)
Solution count 4: 0.756898 0.957673 1.51462 3.51122
Optimal solution found (tolerance 1.00e-04)
Best objective 7.568977587066e-01, best bound 7.568977587066e-01, gap 0.0000%
******************************************************
optimal objective value is = 0.7568977587066126
optimal x is = [-1.0, 0.0, -0.9733324670061584, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
optimal y is =[1.0, -0.0, 1.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0]