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]