How to I get Gurobi to give only integer solutions? - python

I'm trying to optimize the following problem in python using Gurobi and the answer comes out as a decimal. How do I get the output to solve for optimal integers?
from gurobipy import *
def main():
pass
if __name__ == '__main__':
main()
try:
#Create a new model
m = Model("Investment");
#Create variables
x1 = m.addVar(vtype=GRB.CONTINUOUS, name="x1")
x2 = m.addVar(vtype=GRB.CONTINUOUS, name="x2")
x3 = m.addVar(vtype=GRB.CONTINUOUS, name="x3")
x4 = m.addVar(vtype=GRB.CONTINUOUS, name="x4")
x5 = m.addVar(vtype=GRB.CONTINUOUS, name="x5")
#Intigrate new variables
m.update()
#Set Objective
m.setObjective(160*x1 + 160*x2 + 160*x3 + 75*x4 + 75*x5, GRB.MINIMIZE)
m.addConstr( x1 + x2 + x3 >= 3, "c0")
m.addConstr( x1 >= 1, "c1")
m.addConstr( x2 >= 0, "c2")
m.addConstr( x3 >= 1, "c3")
m.addConstr( x4 >= 0, "c4")
m.addConstr( x5 >= 0, "c5")
m.addConstr(40*x1 + 40*x2 + 40*x3 + 25*x4 + 25*x5 >= 365,"c6")
m.optimize()
for v in m.getVars():
print v.varName, v.x
print "Obj:", m.objVal
except GurobiError:
print "Error reported"

Use .addVar(vtype=GRB.INTEGER, ...).
See http://www.gurobi.com/documentation/5.6/reference-manual/py_model_addvar

vtype = GRB.INTEGER
For binary vtype = GRB.BINARY, total 5 variables types

Related

SymPy - Is there a way to write a summation, that has a variable with an incrementing subscript?

I wanted to write this expression, in code:
x1 + x2 + x3 + x4 + x5
I can currently do this via:
import sympy as sp
x1, x2, x3, x4, x5 = sp.symbols('x1 x2 x3 x4 x5')
x1 + x2 + x3 + x4 + x5
But unfortunately, this doesn't scale very well, incase I wanted to go from x1 to say, x10,000
Any help would be sincerely appreciated.
I tried using SymPy's summation function, like this:
summation(x(i), (i, 0, n))
But unfortunately got a TypeError, stating:
'Symbol' object is not callable
You can use a generic IndexedBase symbol with a summation:
>>> i = Symbol('i'); x = IndexedBase('x')
>>> Sum(x[i],(i,1,10))
Sum(x[i], (i, 1, 10))
>>> _.doit()
x[10] + x[1] + x[2] + x[3] + x[4] + x[5] + x[6] + x[7] + x[8] + x[9]

Using Pulp for optimization giving only 0 as results

I am writing a code which maximizes the value for my objective function given a set of constraints. It has four variables labeled x1 to x4, with two equality constraints and two inequality constraints. Solving with Linprog gives me a proper result. But using pulp method is only giving me zero as results.
from pulp import LpMaximize, LpProblem, LpStatus, lpSum, LpVariable
import numpy as np
# Create the model
model = LpProblem(name="optimize", sense=LpMaximize)
# Initialize the decision variables
x1 = LpVariable(name="x1", lowBound= 0, upBound = None, cat='Continuous')
x2 = LpVariable(name="x2", lowBound= 0, upBound = 5, cat='Continuous')
x3 = LpVariable(name="x3", lowBound=None, upBound = 0.5, cat='Continuous')
x4 = LpVariable(name="x4", lowBound=-3, upBound = None, cat='Continuous')
#Objective function of the model
obj_func = (29 * x1 + 45 * x2)
model += obj_func
# Add the constraints to the model
model += (x1 - x2 - 3 * x3 <= 5, "Constraint_1")
model += (2 * x1 - 3 * x2 -7 * x3 + 3 * x4 >= 10, "Constraint_2")
model += (2 * x1 + 8 * x2 + x3 == 60, "Constraint_3")
model += (4 * x1 + 4 * x2 + x4 == 60, "Constraint_4")
model
# Solve the problem
status = model.solve()
LpStatus[model.status]
model.variables()
for var in model.variables():
print(f"{var.name}: {var.value()}")
I can see that the LpStatus[model.status] is saying that the solutions are Undefined.
Same set of equations gives me a solution in LinProg as
[ 6.60059411, 3.9736669 , -0.52664072, 1.09008012]
Your solution does not satisfy the 2nd constraint. Check:
2x6.60059411 - 3x3.9736669 - 7x(-0.52664072) + 3x1.09008012 = 8.2369 < 10

How to use AddBoolXOr in OR-tools

I try to understand OR-tools. I want to solve this equations system in modulo 2:
x1 + x2 + x3 + 0 = 0
x1 + x2 + 0 + x4 = 1
x1 + 0 + x3 + x4 = 1
0 + x2 + x3 + x4 = 1
this equates because of the modulo 2 to:
x1 ^ x2 ^ x3 = 0
x1 ^ x2 ^ x4 = 1
x1 ^ x3 ^ x4 = 1
x2 ^ x3 ^ x4 = 1
using the bitwise xor (see here).
So I tried the following code:
from ortools.sat.python import cp_model
# Creates the model.
model = cp_model.CpModel()
# Creates the variables.
x1 = model.NewBoolVar('x1')
x2 = model.NewBoolVar('x2')
x3 = model.NewBoolVar('x3')
x4 = model.NewBoolVar('x4')
# Creates the constraints.
model.AddBoolXOr(x1 ^ x2 ^ x3 == 0)
model.AddBoolXOr(x1 ^ x2 ^ x4 == 1)
model.AddBoolXOr(x1 ^ x3 ^ x4 == 1)
model.AddBoolXOr(x2 ^ x3 ^ x4 == 1)
# Creates a solver and solves the model.
solver = cp_model.CpSolver()
status = solver.Solve(model)
if status == cp_model.OPTIMAL:
print('x1 = %i' % solver.Value(x1))
print('x2 = %i' % solver.Value(x2))
print('x3 = %i' % solver.Value(x3))
print('x4 = %i' % solver.Value(x4))
but I get:
'calling xor on a linear expression is not supported, '
NotImplementedError: calling xor on a linear expression is not supported, please use CpModel.AddBoolXor
If I use AddBoolXor instead of AddBoolXOr I get:
AttributeError: 'CpModel' object has no attribute 'AddBoolXor'
AddBoolXor takes an array of Boolean literals.
model.AddBoolXOr([x1, x2.Not(), x3])
The semantic of AddBoolXor(xi) is sum(xi) % 2 == 1
So, in your case
# Creates the constraints.
model.AddBoolXOr([x1, x2, x3, True])
model.AddBoolXOr([x1, x2, x4])
model.AddBoolXOr([x1, x3, x4])
model.AddBoolXOr([x2, x3, x4])

Finding the 2 numbers closest to the number needed

I am trying to find a way to take 2 of the closest numbers to average, in avg_list.
Also, is there any other way that I could re-write my code so that x contains 10 items instead of 6 items?
x1 = 461
x2 = 336
x3 = 267
x4 = 262
x5 = 212
x6 = 318
avg = (x1 + x2 + x3 + x4 + x5 + x6)/6
avg1 = (x1 + x2 + x3)/3
avg2 = (x1 + x2 + x4)/3
avg3 = (x1 + x2 + x5)/3
avg4 = (x1 + x2 + x6)/3
avg5 = (x1 + x3 + x4)/3
avg6 = (x1 + x5 + x5)/3
avg7 = (x1 + x3 + x6)/3
avg8 = (x1 + x4 + x5)/3
avg9 = (x1 + x4 + x6)/3
avg10 = (x1 + x5 + x6)/3
avg11 = (x2 + x3 + x4)/3
avg12 = (x2 + x3 + x5)/3
avg13 = (x2 + x3 + x6)/3
avg14 = (x2 + x4 + x5)/3
avg15 = (x2 + x4 + x6)/3
avg16 = (x2 + x5 + x6)/3
avg17 = (x3 + x4 + x5)/3
avg18 = (x3 + x4 + x6)/3
avg19 = (x3 + x5 + x6)/3
avg20 = (x4 + x5 + x6)/3
avg_list = [avg1, avg2, avg3, avg4, avg5, avg6, avg7, avg8, avg9, avg10, avg11, avg12, avg13, avg14, avg15,
avg16, avg17, avg18, avg19, avg20]
Getting combinations with itertools.combinations will help. I've additionally done some stuff with numpy (fast arrays) instead of lists where convenient. A note that if your combinations list becomes very large, there are further efficiencies possible (don't sort the full output, numpy probably has a combinations function).
from itertools import combinations
import numpy as np
def get_closest_partial_avgs(x, n=3):
# returns array of partial averages for n elements
ave = np.mean(x)
partial_aves = []
for vals in combinations(x, n):
partial_avg = sum(vals)/n
partial_aves.append(partial_avg)
partial_aves = sorted(partial_aves, key=lambda x: np.abs(x -ave))[0:2]
return partial_aves
# example
x = [461, 336, 267, 262, 212, 318]
best_aves = get_closest_partial_avgs(x, n=3)
# best_aves = [307.0, 311.6666666666667]
import itertools
import statistics as sc
import numpy as np
def nearest_numbers_avg(x_list,n):
avg_list = []
diff = []
tot_avg = sc.mean(x_list)
x_sublist = list(itertools.permutations(x_list,int(len(x_list)/2)))
for i in x_sublist:
avg = sc.mean(i)
avg_list.append(avg)
diff = abs(np.asarray(avg_list)-tot_avg)
diff = diff.tolist()
for i in range(n):
lst.append(avg_list[diff.index(sorted(diff)[i])])
return(x_sublist)
There are things in computer programming named "arrays".
An "array" is like a row of squares on a sheet of graph paper.
There are many ways to create an array.
One of which is shown below:
x = [None]*6
print(x)
The console output is as follows:
[None, None, None, None, None, None]
If you write x = [None]*6 then x will contain 6 things.
If you write x = [None]*10 then x will contain 10 things.
You should not write variable names like x1, x2, x3
Instead, use square brackets [] like this:
x = [None]*7
x[1] = 461
x[2] = 336
x[3] = 267
x[4] = 262
x[5] = 212
x[6] = 318
print(x)
If you print x, you will notice that x is [None, 461, 336, 267, 262, 212, 318]
The left-most square in the row of squares of graph paper is x[0]
The first thing in the row of graph paper is named x[0], not x[1]
If you want to compute an average, there are different ways to do it:
average_of_x = sum(x)/len(x)
Sometimes you will get the following error:
Traceback (most recent call last):
File "D:/python_sandbox/fgfnxxfgh.py", line 12, in <module>
average_of_x = sum(x)/len(x)
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
That error means that some of the things in your row of squares are not numbers.
The computer does not understand 4 + "egg" or 98 + a house
The computer usually adds numbers to other numbers. 1 + 92 + 81 + 1 + 5
The following makes an error:
data = [None, 443, None, 27, 19, 19 , 8, 19]
total = sum(data)
The following are almost the same:
avg = (x[1] + x[2] + x[3] + x[4] + x[5] + x[6])/6
avg = sum(x)/len(x)
You should never write avg1, avg2, avg3, etc...
Instead, write, avg[1], avg[2], avg[3].
The following is not very good code, but it is better than what you wrote originally (relatively speaking):
import operator as op
from functools import reduce
def n_choose_r(n, r):
"""
If n == 6, and r == 3, then this function computes
the number of ways to draw three numbers from the set
{1, 2, 3, 4, 5, 6}
examples of sets of size 3 drawn from {1...6} are shown below:
{1, 2, 3}
{1, 2, 4}
{1, 2, 5}
{1, 2, 6}
{1, 3, 4}
"""
r = min(r, n-r)
numer = reduce(op.mul, range(n, n-r, -1), 1)
denom = reduce(op.mul, range(1, r+1), 1)
return numer // denom
avg = [None]*n_choose_r(len(x), 3)
max_ = len(x)
i = 0
for k1 in range(1, 1+ max_):
for k2 in range(k1 + 1, 1 + max_):
for k3 in range(k2 + 1, 1 + max_):
avg[i] = (x[k1] + x[k2] + x[k3])/3
i = i + 1

Mixed-Integer Quadratic Programming in Python

I would like to solve in Python the following Mixed-Integer Quadratic Programming in Python. Nevertheless, I'm not familiar with the optimization
toolboxes of Python.
Can someone provide an example of code with the vectors X1, X2, X3, X4 given as below ?
X1 = np.array([3,10,20,10])
X2 = np.array([5,1,3,4])
X3 = np.array([2,3,1,4])
X4 = np.array([10,0,1,2])
The MIQP is written as :
I tried to solve it with CVXPY but i encoutered problem with the boolean
variable x = cp.Variable(1, boolean=True):
import numpy
import numpy as np
import cvxpy as cp
X1 = np.array([3,10,20,10])
X2 = np.array([5,1,3,4])
X3 = np.array([2,3,1,4])
X4 = np.array([10,0,1,2])
M = 100
x = cp.Variable(1, boolean=True)
Y1 = cp.Parameter(4)
Y2 = cp.Parameter(4)
a = cp.Parameter(1)
b = cp.Parameter(1)
c = cp.Parameter(1)
d = cp.Parameter(1)
delta = cp.Variable(1)
constraints = [Y1 <= X1 - a,
Y1 <= X2 - b,
Y1 >= X1 - a - M*delta,
Y1 >= X2 - b - M*(1-delta),
Y2 <= X3 - c,
Y2 <= X4 - d,
Y2 >= X3 - c - M*delta,
Y2 >= X4 - d - M*(1-delta),
0 <= a, a <= 10,
0 <= b, b <= 5,
0 <= c, c <= 5,
0 <= d, d <= 10,
delta == x]
obj = cp.Minimize(cp.sum_squares(Y1-Y2))
prob = cp.Problem(obj, constraints)
print(prob.solve())
In cvxpy, parameter is something you have a value to set to it. In your problem, basically all symbols other than the X1 to X4 are variables. So do a global replace of cp.Parameter to cp.Variable will work.
Then, I found the result to be
$ python3 cvxtest.py
69.99998471073722
Gekko with the APOPT solver can handle MIQP problems in addition to more general Nonlinear Mixed Integer Programming (MINLP). The solution is:
---------------------------------------------------
Solver : APOPT (v1.0)
Solution time : 2.610000000277068E-002 sec
Objective : 70.0000000000000
Successful solution
---------------------------------------------------
x: 1.0
obj: 70.0
Here is the Python script:
import numpy as np
from gekko import GEKKO
m = GEKKO()
X1 = m.Param([3,10,20,10])
X2 = m.Param([5,1,3,4])
X3 = m.Param([2,3,1,4])
X4 = m.Param([10,0,1,2])
M = 100
p = m.Array(m.FV,4,lb=0,ub=10); a,b,c,d=p
b.upper = 5; c.upper = 5
for pi in p:
pi.STATUS=1
x = m.FV(lb=0,ub=1,integer=True); x.STATUS=1
Y1,Y2 = m.Array(m.Var,2)
delta = m.FV(); delta.STATUS=1
m.Equations([Y1 <= X1 - a,
Y1 <= X2 - b,
Y1 >= X1 - a - M*delta,
Y1 >= X2 - b - M*(1-delta),
Y2 <= X3 - c,
Y2 <= X4 - d,
Y2 >= X3 - c - M*delta,
Y2 >= X4 - d - M*(1-delta),
delta == x])
m.Minimize((Y1-Y2)**2)
m.options.IMODE=2
m.options.SOLVER=1
m.solve()
print('x: ', x.value[0])
print('obj: ', m.options.OBJFCNVAL)

Categories

Resources