I am using OR-Tools to solve a MIP with SCIP. OR-Tools returns optimal values for continuous and integer (binary) variables of my problem.
When I then fix the binary variables of this MIP to the optimal values returned by OR-Tools (for the MIP) and I solve the corresponding LP with GLOP, OR-Tools returns new values for the optimal values of the continuous variables.
My understanding is that the initial problem does not have a unique solution (in terms of optimal values of variables).
So my question is: How can I make OR-tools return every optimal solution, and not just one?
Please find the code bellow:
from ortools.linear_solver import pywraplp
#Fixed parameters
K = 4
L = 3
J = {}
J[0,0] = 3
J[0,1] = 2
J[0,2] = 1
J[1,0] = 3
J[1,1] = 1
J[1,2] = 2
J[2,0] = 2
J[2,1] = 2
J[2,2] = 2
J[3,0] = 1
J[3,1] = 1
J[3,2] = 1
S_up = {}
S_lw = {}
U = {}
S_up[0,0,0] = 20
S_up[0,1,0] = 40
S_up[0,2,0] = 60
S_up[0,0,1] = 15
S_up[0,1,1] = 40
S_up[0,0,2] = 50
S_lw[0,0,0] = 0
S_lw[0,1,0] = 21
S_lw[0,2,0] = 41
S_lw[0,0,1] = 5
S_lw[0,1,1] = 16
S_lw[0,0,2] = 10
U[0,0,0] = 5
U[0,1,0] = 7
U[0,2,0] = 8
U[0,0,1] = 2
U[0,1,1] = 5
U[0,0,2] = 6
S_up[1,0,0] = 25
S_up[1,1,0] = 35
S_up[1,2,0] = 50
S_up[1,0,1] = 30
S_up[1,0,2] = 30
S_up[1,1,2] = 30
S_lw[1,0,0] = 5
S_lw[1,1,0] = 26
S_lw[1,2,0] = 36
S_lw[1,0,1] = 10
S_lw[1,0,2] = 5
S_lw[1,1,2] = 31
U[1,0,0] = 6
U[1,1,0] = 8
U[1,2,0] = 9
U[1,0,1] = 3
U[1,0,2] = 5
U[1,1,2] = 7
S_up[2,0,0] = 40
S_up[2,1,0] = 60
S_up[2,0,1] = 60
S_up[2,1,1] = 80
S_up[2,0,2] = 40
S_up[2,1,2] = 60
S_lw[2,0,0] = 5
S_lw[2,1,0] = 41
S_lw[2,0,1] = 5
S_lw[2,1,1] = 61
S_lw[2,0,2] = 5
S_lw[2,1,2] = 41
U[2,0,0] = 5
U[2,1,0] = 6
U[2,0,1] = 4
U[2,1,1] = 5
U[2,0,2] = 5
U[2,1,2] = 6
S_up[3,0,0] = 100
S_up[3,0,1] = 90
S_up[3,0,2] = 90
S_lw[3,0,0] = 0
S_lw[3,0,1] = 0
S_lw[3,0,2] = 0
U[3,0,0] = 5
U[3,0,1] = 4
U[3,0,2] = 5
D = [100,100,90]
P = [50,30,20]
Q_up = [0,1,1,1]
Q_lw = [0,0,0.1,0]
#Declare MIP solver
solver_mip = pywraplp.Solver.CreateSolver('SCIP')
#Define variables
infinity = solver_mip.infinity()
y = {}
for k in range(K):
for l in range(L):
for j in range(J[k,l]):
y[k, j, l] = solver_mip.NumVar(0, infinity, '')
x = {}
for k in range(K):
for l in range(L):
for j in range(J[k,l]):
x[k, j, l] = solver_mip.IntVar(0, 1, '')
print('Number of variables =', solver_mip.NumVariables())
#Define constraints
for k in range(K):
for l in range(L):
for j in range(J[k,l]):
solver_mip.Add(y[k, j, l] <= x[k, j, l]*S_up[k, j, l])
solver_mip.Add(x[k, j, l]*S_lw[k, j, l] <= y[k, j, l])
for k in range(K):
for l in range(L):
solver_mip.Add(sum([x[k, j, l] for j in range(J[k,l])]) <= 1)
for l in range(L):
solver_mip.Add(sum([sum([y[k, j, l] for j in range(J[k,l])]) for k in range(K)]) == D[l])
for k in range(K):
solver_mip.Add(sum([sum([y[k, j, l]*P[l] for j in range(J[k,l])]) for l in range(L)]) <= Q_up[k]*sum([D[l]*P[l] for l in range(L)]))
solver_mip.Add(Q_lw[k]*sum([D[l]*P[l] for l in range(L)]) <= sum([sum([y[k, j, l]*P[l] for j in range(J[k,l])]) for l in range(L)]))
print('Number of constraints =', solver_mip.NumConstraints())
#Define objective
solver_mip.Minimize(sum([sum([sum([y[k,j,l]*U[k,j,l] for j in range(J[k,l])]) for k in range(K)]) for l in range(L)]))
#Call MIP solver
status = solver_mip.Solve()
#Display solution
if status == pywraplp.Solver.OPTIMAL:
print('Solution of MIP:')
print('Objective value =', solver_mip.Objective().Value())
x_opt = {} #store optimal values of binary variable
for k in range(K):
for l in range(L):
for j in range(J[k,l]):
x_opt[k,j,l] = x[k,j,l].solution_value()
if x[k,j,l].solution_value() == 1:
print('y[',k,',',j,',',l,']=',y[k,j,l].solution_value())
else:
print('The problem does not have an optimal solution.')
print('\nAdvanced usage:')
print('Problem solved in %f milliseconds' % solver_mip.wall_time())
print('Problem solved in %d iterations' % solver_mip.iterations())
print('Problem solved in %d branch-and-bound nodes' % solver_mip.nodes())
#Primal problem with fixed binary variables to optimal value becomes a LP
#Declare LP solver
solver_lp = pywraplp.Solver.CreateSolver('GLOP')
##Quantity variable
y_fixed_binary = {}
for k in range(K):
for l in range(L):
for j in range(J[k,l]):
y_fixed_binary[k, j, l] = solver_lp.NumVar(0, infinity, '')
#Define constraints
##Quantity should be in bounds defined by reinsurer
for k in range(K):
for l in range(L):
for j in range(J[k,l]):
solver_lp.Add(y_fixed_binary[k, j, l] <= x_opt[k, j, l]*S_up[k, j, l])
solver_lp.Add(x_opt[k, j, l]*S_lw[k, j, l] <= y_fixed_binary[k, j, l])
for l in range(L):
solver_lp.Add(sum([sum([y_fixed_binary[k, j, l] for j in range(J[k,l])]) for k in range(K)]) == D[l])
for k in range(K):
solver_lp.Add(sum([sum([y_fixed_binary[k, j, l]*P[l] for j in range(J[k,l])]) for l in range(L)]) <= Q_up[k]*sum([D[l]*P[l] for l in range(L)]))
solver_lp.Add(Q_lw[k]*sum([D[l]*P[l] for l in range(L)]) <= sum([sum([y_fixed_binary[k ,j ,l]*P[l] for j in range(J[k,l])]) for l in range(L)]))
#Define objective
solver_lp.Minimize(sum([sum([sum([y_fixed_binary[k,j,l]*U[k,j,l] for j in range(J[k,l])]) for k in range(K)]) for l in range(L)]))
status = solver_lp.Solve()
if status == pywraplp.Solver.OPTIMAL:
print('Solution of LP:')
print('Objective value =', solver_mip.Objective().Value())
for k in range(K):
for l in range(L):
for j in range(J[k,l]):
if x_opt[k,j,l] == 1:
print('y[',k,',',j,',',l,']=',y_fixed_binary[k,j,l].solution_value())
else:
print('The LP does not have an optimal solution.')
print('Advanced usage:')
print('Problem solved in %f milliseconds' % solver_lp.wall_time())
And the output is
Solution of MIP:
Objective value = 1280.0
y[ 1 , 0 , 1 ]= 30.0
y[ 2 , 0 , 0 ]= 13.999999999999998
y[ 2 , 0 , 1 ]= 5.0
y[ 2 , 0 , 2 ]= 5.0
y[ 3 , 0 , 0 ]= 86.0
y[ 3 , 0 , 1 ]= 55.0
y[ 3 , 0 , 2 ]= 85.0
Solution of LP:
Objective value = 1280.0
y[ 1 , 0 , 1 ]= 30.0
y[ 2 , 0 , 0 ]= 40.0
y[ 2 , 0 , 1 ]= 60.0
y[ 2 , 0 , 2 ]= 40.0
y[ 3 , 0 , 0 ]= 60.0
y[ 3 , 0 , 1 ]= 0.0
y[ 3 , 0 , 2 ]= 50.0
You can get all optimal solutions by calling NextSolution() after the first Solve()
I am trying to convert the code by replacing for loop with while loop.
This is the code which I want to be converted
Functions for the code in the below snippet. Asked by #Sameer
from random import sample
def shuffle(s): return sample(s,len(s))
rBase = range(base)
base = 2
side = base*base
def pattern(r,c):
return (base*(r%base)+r//base+c)%side
Code in which I have the problem
rows = []
cols = []
data = []
for r in shuffle(rBase):
for g in shuffle(rBase):
rows.append(g*base+r)
for c in shuffle(rBase):
for g in shuffle(rBase):
cols.append(g*base+c)
nums = shuffle(range(1,side+1))
for r in rows:
for c in cols:
data.append(nums[pattern(r,c)])
Here is my attempted code
rows = []
cols = []
data = []
s = shuffle(rBase)
i,j = 0,0
while i < len(s):
r = s[i]
while j < len(s):
s2 = shuffle(rBase)
g = s2[i]
rows.append(g*base+r)
j+=1
i += 1
i,j = 0,0
s = shuffle(rBase)
while i < len(s):
c = s[i]
while j < len(s):
s2 = shuffle(rBase)
g = s2[i]
cols.append(g*base+c)
j += 1
i += 1
nums = shuffle(range(1,side+1))
i,j =0,0
while i < len(rows):
r = rows[i]
while j < len(cols):
c = cols[i]
data.append(nums[pattern(r,c)])
j += 1
i += 1
Some part of the code is omitted due to privacy reasons.
Expected output, ie output of for loop snippet is a list with length (base ** 4) containing each number from 1 to base ** 2 exactly base ** 2 times.
For Eg:
if base is 2:
expected output would be like [3, 2, 1, 4, 4, 1, 3, 2, 1, 4, 2, 3, 2, 3, 4, 1]
Actual output is a list of length base with each element is a random number between 1 and base ** 2.(Including base ** 2)
For Eg:
if base is 2:
Actual output is like [1,1] or [2,2] or [3,3] or [4,4]
Variable j = 0 should be defined before the inner while loop. Currently, your inner while is running 'len(s)' times only also you are indexing with 'i' in your second loop, you should have used j there.
This Should Work
from random import sample
def shuffle(s): return sample(s,len(s))
base = 2
rBase = range(base)
side = base*base
def pattern(r,c):
return (base*(r%base)+r//base+c)%side
rows = []
cols = []
data = []
for r in shuffle(rBase):
for g in shuffle(rBase):
rows.append(g*base+r)
for c in shuffle(rBase):
for g in shuffle(rBase):
cols.append(g*base+c)
print(rows)
print(cols)
nums = shuffle(range(1,side+1))
for r in rows:
for c in cols:
data.append(nums[pattern(r,c)])
print(data)
rows = []
cols = []
data = []
s = shuffle(rBase)
s2 = shuffle(rBase)
i = 0
while i < len(s):
r = s[i]
j = 0
while j < len(s):
g = s2[j]
j = j + 1
rows.append(g*base+r)
i = i + 1
i = 0
s = shuffle(rBase)
s2 = shuffle(rBase)
while i < len(s):
c = s[i]
j = 0
while j < len(s):
g = s2[j]
cols.append(g*base+c)
j += 1
i += 1
nums = shuffle(range(1,side+1))
i =0
while i < len(rows):
r = rows[i]
j = 0
while j < len(cols):
c = cols[j]
data.append(nums[pattern(r,c)])
j += 1
i += 1
print(rows)
print(cols)
print(data)
This is probably a simple problem, but I am trying to created a nested loop that would count up from 0 to 9 in the outer loop, and in the inner loop, start from the value (or index. They are the same in this case) of the outer loop and count backwards.
Here's an example:
i= 0
k= 0
i= 1
k= 1
k= 0
i= 2
k= 2
k= 1
k= 0
i= 3
k= 3
k= 2
k= 1
k= 0
I got this far:
x = range(0,10)
for i in x:
print 'i = ',x[i]
for k in x:
print 'k = ', x[i::-1]
Obviously, the code above doesn't do what I want it to do. For one, the second for loop doesn't start from the value of i in the outer loop and counts backwards. For another, it doesn't print a new k = for every new value.
I think this should be like this:
x = range(0,10)
for i in x:
print 'i = ',x[i]
for k in x[i::-1]:
print 'k = ', k
print("\n")
The result is:
i = 0
k = 0
i = 1
k = 1
k = 0
i = 2
k = 2
k = 1
k = 0
i = 3
k = 3
k = 2
k = 1
k = 0
i = 4
k = 4
k = 3
k = 2
k = 1
k = 0
i = 5
k = 5
k = 4
k = 3
k = 2
k = 1
k = 0
i = 6
k = 6
k = 5
k = 4
k = 3
k = 2
k = 1
k = 0
i = 7
k = 7
k = 6
k = 5
k = 4
k = 3
k = 2
k = 1
k = 0
i = 8
k = 8
k = 7
k = 6
k = 5
k = 4
k = 3
k = 2
k = 1
k = 0
i = 9
k = 9
k = 8
k = 7
k = 6
k = 5
k = 4
k = 3
k = 2
k = 1
k = 0
Basicly, x[i::-1] should be in the for not in the print.
What about just manipulate it with print function?
i = 0
k = 0
while True:
print (i)
print (k)
if 1<k: #tricky part
print ("\n".join([str(h) for h in range(0,k+1)][::-1]))
print ("")
i += 1
k += 1
if i == 10:
break
You are very close. If you are new to the world of python you can take some inspiration from this example where I use list comprehension.
list = lambda k: [ [ i for i in reversed(xrange(j+1)) ] for j in xrange(k+1) ]
Note: If you are using python 3 "xrange" is changed to "range"
Now call:
list(3)
And you'll see that the result is similar to what you are looking for.
[[0], [1, 0], [2, 1, 0], [3, 2, 1, 0]]