In this chapter, we will look at how to solve Integer Linear Optimization Problems (ILOPs) and Binary Linear Optimization Problems (BLOPs) using Julia.
We solved ProblemΒ 4.1.3 in SectionΒ 4.1 two different ways. The first involved cutting planes to trim the feasible set without eliminating integer solutions and the second was branch and bound, which successfully split the feasible set into pieces by eliminating non-integer solutions.
These were quite complex but important to understand how they work. However, when solving problems using software, we donβt need the details. As in the previous section, we will solve this using the JuMP and HiGHS packages of Julia.
Another common constraint on Linear Optimization problems is that variables may take on either 0 or 1. For example, the \(n\)-queens problem in ProblemΒ 2.1.10 is such an example.
In SubsubsectionΒ 2.1.2.1, the problem is written in mathematical form. First, if we look at this for \(n=4\text{,}\) the problem is a bit more reasonable.
First, the variable \(x_{i,j}\) will be a binary variable that will take on the value 1 if there is a queen on the \(i\)th row and \(j\)th column. To create the variables in Julia, we first create a model, then the variables, which we can do in bulk as the following:
m2 = Model(HiGHS.Optimizer)
n = 4
@variable(m2, x[1:n,1:n], Bin)
where the last term in the @variable macro shows the variable is binary. Also, note that this make a array variable that will match the size of the board.
There are a number of ways to do set up the constraints including typing all of the constraints out. However, the following is a nice way to do this in a very general way. First, letβs do the row and column sums with the mapslices command. Consider mapslices(sum, x, dims = [1]), which returns:
and this will give us the diagonal constraint. The form of the diagm function uses the argument of the form i = > v, where i is an integer that shows how far from the main diagonal the vector v should exist. If the integer is positive, then the diagonal will be on the upper right part of the matrix and negative values on the lower left. If we multiply this by x with diagm(1 => ones(3)).*x, the result is
P = Matrix(I,n,n)[n:-1:1, :]
@constraint(m2, c3, [sum(diagm(i= > ones(n-abs(i))).*x) for i=-(n-2):(n-2)] .<= ones(2n-3))
@constraint(m2, c4, [sum(P*diagm(i= > ones(n-abs(i))).*x) for i=-(n-2):(n-2)] .<= ones(2n-3))
where the first line is a permutation matrix that will flip all of the rows of a matrix upon right multiplication. The structure [. for i= -(n-2):(n-2)] is called a comprehension and builds a vector. See SectionΒ 5.2 for more examples.
and if carefully look at all of the constraints that they are all that there is at most one queen (sum of the x values along each row, column and diagonal), that all of the constraints are there.
and again, since true is returned that we have a feasible optimal solution. We can use the following shortcut (called broadcasting) to output the values with
Although we can read the solution from this, it seems odd that this is not an integer matrix. We can round this with round.(Int, value.(x)) which returns
And in FigureΒ 5.5.3, one can see that no queen can attack another one in a single move. Note that in general you get a single solution to a LOP when solving either via the simplex method or using software tools. There is another solution to this problem in which the board above can be flipped either vertically or horizontally. The software will not generate this other solution.
Subsection5.5.3Does the 3-queens problem have a solution?
Itβs probably not too hard to see that if you have a 3 by 3 chess board, that you cannot get 3 queens on the board. Play with it for a few minutes and youβll probably see this.
This shows that the optimal solution has 2 queens on the board. Recall that the goal is to maximize the objective function with the given constraints. This means that putting a queen on an edge and an opposite corner is the largest number of queens allowed. You probably noticed this if you played with it a bit.