How to get all solutions for an integer program in ortools? - linear-programming

I am trying to get all solutions for a Mixed Integer program through ortools. I have two lists x and y of size 4. I want to get all solutions which satisfy sum(x) = 4 * sum(y). I created a function which takes list of past solutions as input and returns next solution. I am able to get only 2 solutions even though there are more. What am I doing wrong here?
I am expecting the following solutions
Solution 1:
xs1 = [0,0,0,0], ys1 = [0,0,0,0]
Solution 2:
xs2 = [4,0,0,0], ys2 = [1,0,0,0]
Solution 3:
xs3 = [0,4,0,0], ys3 = [1,0,0,0]
Solution 4:
xs4 = [0,0,4,0], ys4 = [0,0,1,0]
and soon on
from ortools.linear_solver import pywraplp
def opt(xs, ys):
solver = pywraplp.Solver.CreateSolver('SCIP')
infinity = solver.infinity()
# x and y are integer non-negative variables.
n = 4
M = 20
x = [0]* n
y = [0]* n
w = [[0]* n]*len(xs)
δ = [[0]* n]*len(xs)
for i in range(0,n):
x[i] = solver.IntVar(0, 20, 'x'+str(i))
y[i] = solver.IntVar(0, 20, 'y'+str(i))
for j in range(len(xs)):
w[j][i] = solver.IntVar(0, 20, 'zp'+str(j)+ '-' + str(i))
δ[j][i] = solver.IntVar(0, 1, 'δ'+str(j)+ '-' + str(i))
for j in (range(len(xs))):
for i in range(0,n):
solver.Add((w[j][i] - x[i] + xs[j][i]) >=0)
solver.Add((w[j][i] - x[i] + xs[j][i]) <= M*(1-δ[j][i]))
solver.Add((w[j][i] + x[i] - xs[j][i]) >=0)
solver.Add((w[j][i] + x[i] - xs[j][i]) <= M*δ[j][i])
for j in range(len(xs)):
solver.Add(solver.Sum([w[j][i] for i in range(0,n)]) >= 1)
solver.Add(solver.Sum([x[i] for i in range(0, n)]) - 4 * solver.Sum([y[i] for i in range(0, n)]) == 0)
solver.Minimize(solver.Sum([x[i] for i in range(0, n)]))
status = solver.Solve()
if status == pywraplp.Solver.OPTIMAL:
solver_x = [0]*n
solver_y = [0]*n
for i in range(0,n):
solver_x[i] = x[i].solution_value()
solver_y[i] = y[i].solution_value()
return ([solver_x, solver_y, solver.Objective().Value()])
else:
print('No Solution')
return ([[0], [0]], -1)
psx = [[0,0,0,0], [0,4,0,0]]
psy = [[0,0,0,0], [1,0,0,0]]
ns = opt(psx, psy)
print(ns)
Output:
No Solution
([[0], [0]], -1)
Reference:
Finding multiple solutions to general integer linear programs
How to write constraints for sum of absolutes

If you have a pure integer programming model, you can use the CP-SAT solver which allows you to print all the solutions [See this].

Related

Invalid syntax python problem (if statement many actions)

I am encountering a problem on my python code. I'm trying to increment and decrement two values after an if statement. However, this couldn't be achieved (Error: invalid syntax). Knowing that when I increment only a single value (e.g. A_p), it works well. Does the error come from the and operator? Is there a way to perform multiple actions after an if statement?
Thank you.
Omer
k1 = 0.2
k2 = 0.2
Delta_t = 2
A_p = 4
B_p = 3
list_nm = [0.800, 0.801, 0.752, 0.661, 0.169, 0.956, 0.949, 0.003, 0.201, 0.291, 0.615, 0.131, 0.241, 0.685, 0.116, 0.241, 0.849]
for i in range(7):
if list_nm[i] < (A_p/7):
print("Particle A was choosen")
if list_nm[i] < k1*Delta_t:
A_p -= 1 and B_p += 1
else:
A_p = A_p and B_p = B_p
elif list_nm[i] < (B_p/7):
print("Particle B was choosen")
if list_nm[i] < k2*Delta_t:
A_p +=1 and B_p -=1
else:
A_p = A_p and B_p = B_p
else:
print("No choice was made")
A_p = A_p and B_p = B_p
print(B_p)
A_p -= 1 and B_p += 1
is not valid Python, and is a specific keyword used for combining booleans, such as:
if a > 3 and a < 7:
pass
What you're after instead is two separate statements:
A_p -= 1
B_p += 1
or, if you must have it on a single line (and I'd question why you think this is necessary), one of the following, depending on what you prefer:
A_p -= 1 ; B_p += 1
(A_p, B_p) = (A_p - 1, B_p + 1)
In addition, the line:
A_p = A_p and B_p = B_p
even when you split it into two statements, is effectively doing nothing. You may as well remove it totally, along with the else: line preceding it if that's the only thing in the else block.
In other words, start with something this:
(k1, k2, Delta_t) = (0.2, 0.2, 2)
(A_p, B_p) = (4, 3)
list_nm = [0.800, 0.801, 0.752, 0.661, 0.169, 0.956, 0.949, 0.003, 0.201, 0.291, 0.615, 0.131, 0.241, 0.685, 0.116, 0.241, 0.849]
for i in range(7):
if list_nm[i] < (A_p / 7):
print("Particle A was choosen")
if list_nm[i] < k1 * Delta_t:
A_p -= 1
B_p += 1
elif list_nm[i] < (B_p / 7):
print("Particle B was choosen")
if list_nm[i] < k2 * Delta_t:
A_p += 1
B_p -= 1
else:
print("No choice was made")
print(B_p)

Plot specific lines for specific values with Pyplot

I'm trying to plot two files of data of this type:
name1.fits 0 0 2.40359218172
name2.fits 0 0 2.15961244263
The third column has values from 0 to 5. I want to plot column 2 vs column 4, but, for lines with values in col 3 less than 2 (0 and 1), I want to shift col 2 by -0.1, and for lines with values greater than 3 (4 and 5) I want to shift col 2 by +0.1.
However my code seems to be shifting all values by +0.1. Here is what I have so far:
import matplotlib.pyplot as plt
import numpy as np
with open('file1.txt') as data, open('file2.txt') as stds:
lines1 = data.readlines()
lines2 = stds.readlines()
x1a = []
x2a = []
x1b = []
x2b = []
x1c = []
x2c = []
y1a = []
y2a = []
y1b = []
y2b = []
y1c = []
y2c = []
for line1 in lines1:
p = line1.split()
if p[2] < 2:
x1a.append(float(p[1]))
y1a.append(float(p[3]))
elif 1 < p[2] < 4:
x1b.append(float(p[1]))
y1b.append(float(p[3]))
elif p[2] > 3:
x1c.append(float(p[1]))
y1c.append(float(p[3]))
for line2 in lines2:
q = line2.split()
if q[2] < 2:
x2a.append(float(q[1]))
y2a.append(float(q[3]))
elif 1 < q[2] < 4:
x2b.append(float(q[1]))
y2b.append(float(q[3]))
elif q[2] > 3:
x2c.append(float(q[1]))
y2c.append(float(q[3]))
x1a = np.array(x1a)
x2a = np.array(x2a)
x1b = np.array(x1b)
x2b = np.array(x2b)
x1c = np.array(x1c)
x2c = np.array(x2c)
y1a = np.array(y1a)
y2a = np.array(y2a)
y1b = np.array(y1b)
y2b = np.array(y2b)
y1c = np.array(y1c)
y2c = np.array(y2c)
minorLocator = AutoMinorLocator(5)
fig, ax = plt.subplots(figsize=(8, 8))
fig.subplots_adjust(left=0.11, right=0.95, top=0.94)
plt.plot(x1a-0.1,y1a,'b^',mec='blue',label=r'B0',ms=8)
plt.plot(x2a-0.1,y2a,'r^',mec='red',fillstyle='none',mew=0.8,ms=8)
plt.plot(x1b,y1b,'bo',mec='blue',label=r'B0',ms=8)
plt.plot(x2b,y2b,'ro',mec='red',fillstyle='none',mew=0.8,ms=8)
plt.plot(x1c+0.1,y1c,'bx',mec='blue',label=r'B0',ms=8)
plt.plot(x2c+0.1,y2c,'rx',mec='red',fillstyle='none',mew=0.8,ms=8)
plt.axis([-1.0, 3.0, 0., 4])
ax.xaxis.set_tick_params(labeltop='on')
ax.yaxis.set_minor_locator(minorLocator)
plt.show()
Here is the plot:
plot
I'm pretty sure the problem is in my "ifs". I hope you can clear the way and/or show me a better option for this.
When you do your queries (if) you must ensure the conversion happens before the question so:
for line1 in lines1:
p = line1.split()
if p[2] < 2:
x1a.append(float(p[1]))
y1a.append(float(p[3]))
elif 1 < p[2] < 4:
x1b.append(float(p[1]))
y1b.append(float(p[3]))
elif p[2] > 3:
x1c.append(float(p[1]))
y1c.append(float(p[3]))
, should actually be:
for line1 in lines1:
p = line1.split()
if float(p[2]) < 2: # changed here
x1a.append(float(p[1]))
y1a.append(float(p[3]))
elif 1 < float(p[2]) < 4: # There seems to be a problem with this if
x1b.append(float(p[1]))
y1b.append(float(p[3]))
elif float(p[2]) > 3: # changed here
x1c.append(float(p[1]))
y1c.append(float(p[3]))
The same for your q variables. Also notice that asking 1 < x < 4 will intercept with x > 3 and x < 2. You should also correct this.

'function' object has no attribute '__getitem__'

This is my first time coding. I'm doing it as ab elective module. I have to program an ai_player to go from playing randomly to winning and I'm stuck. Any advice would be appreciated. The game is Connect 4. i keep getting "object has no attribute" error.
import random
import time
def board():
for i in range(0, 8, 1):
for j in range(0, 10, 1):
board[i][j] = 0
return board
def move(board, valid_move):
start_time = time.time()
x = 0
while x == 0:
i = range(7, -1, -1)
j = range(0, 10, 1)
first_move = board[i][j]
board[7][4] = 1
if board[i-1][j] == 0: #above
first_move = [i, j]
x = 1
print " valid above"
return j
elif (board[i][j+1] == 0 and (i <= 7 and j <= 9)) or (board[i-1][j+1] == 0 and (i <= 7 and j <= 9)) or (board[i-1][j+1] == 0 and (i <= 7 and j <= 9)): #right
first_move = [i, (j+1)]
x = 1
print " valid right"
return (j+1)
elif board[i][j-1] == 0 or board[i-1][j-1] == 0 or board[i-1][j-1] == 0: #left
first_move = [i, (j-1)]
x = 1
print " valid left"
return (j-1)
else:
r = random.randint(0, 7)
c = random.randint(0, 9)
first_move = [r, c]
x = 1
print " random move"
return c
end_time = time.time() - start_time
print end_time
return first_move
File "F:/5. Fifth year/1st Semester/MPR 213 2016/Project 2016/attempts.py", line 20, in board
board[i][j] = 0
TypeError: 'function' object has no attribute '__getitem__'
It looks like you're trying to create a multidimensional list called board. This is not how you do that though, what you've actually done is created a function called board, and then you try to index that function, which fails since it's not a list.
To create board, use something like
board = [[0] * 10 for i in range(0, 8)]

Out of range error - merge sort algorithm

I am trying to implement the merge sort algorithm using the following code but am getting a list index is out of range error.
def mergeSort (unSortedList):
if len(unSortedList) == 1 :
return unSortedList
else:
midpoint = len(unSortedList)//2
A = mergeSort (unSortedList[:midpoint] )
B = mergeSort (unSortedList[midpoint:] )
i = 0
j = 0
C = []
for k in range(len(unSortedList)):
if A[i] >= B[j]:
C.append(A[i])
if i == len(A):
C.append(B[j:])
else:
i += 1
elif A[i] < B[j] :
C.append(B[j])
if j == len(B):
C.append(A[i:])
else:
j += 1
return C
testlist = [2,1,4,2,5,6,8,9]
print (mergeSort(testlist))
Any help would be appreciated.
Here is my version of your mergeSort, with the merge function extracted:
def mergeSort (unSortedList):
if len(unSortedList) == 1 :
return unSortedList
else:
midpoint = len(unSortedList)//2
A = mergeSort (unSortedList[:midpoint] )
B = mergeSort (unSortedList[midpoint:] )
return merge(A, B)
def merge(a, b):
i = 0
j = 0
c = []
while True:
if a[i] < b[j]:
c.append(b[j])
j += 1
elif a[i] >= b[j]:
c.append(a[i])
i += 1
if i == len(a):
c.extend(b[j:])
break
if j == len(b):
c.extend(a[i:])
break
return c
Output:
>>> testlist = [2,1,4,2,5,6,8,9]
>>> mergeSort(testlist)
[9, 8, 6, 5, 4, 2, 2, 1]
Couple of things to note:
Appending a list to a list. When you do C.append(A[j:]) you end up with nested lists. That is because A[j:] always returns a list. You either need to use list addition - C += A[j:] - or call extend - C.extend(A[j:])
Missing breaks. When your i or j got to the end of their lists you correctly appended the rest of the other list but you did not terminate the loop. That is what caused the range error because in the next iteration (which should not happen) you tried to get an item at the index equal to the length of the list which is out of range.

Merge Sort returns the same array in Python

for merge sort i wrote this code:
I have tested merge function that works correctly. but in mergeSort function i coudn't handle the arrays. it returns the same list as the input list.
def mergeSort(a):
l, h = 0, len(a)-1
mid = (l+h)/2
if (l<h-1): #the lowest length must be 2
mergeSort(a[l:mid+1])
mergeSort(a[mid+1:h+1])
return merge(a[l:mid+1],a[mid+1:h+1])
def merge(a,b):
n_a = len(a)
n_b = len(b)
c = [[] for i in range(n_a + n_b)]
i,j,k=0,0,0
while (i<n_a and j<n_b):
if a[i]<b[j]:
c[k] = a[i]
i += 1
else:
c[k]= b[j]
j += 1
k += 1
while(i<n_a):
c[k] = a[i]
k+=1
i+=1
while(j< n_b):
c[k] = b[j]
k+=1
j+=1
return c
I would rewrite merge as so:
def mergeSort(a):
h = len(a)
mid = h / 2
if h >= 2:
return merge(mergeSort(a[:mid]), mergeSort(a[mid:]))
else:
return a
A few notes:
l is always 0, might as well remove it
h is len(a) - 1, but then you use h + 1, might as well use h = len(a)
Actually writting h >= 2 makes it clearer that you need at least 2 items in your list
mid can be len(a) / 2.
when taking the complete beginning/end of an array in a slice, the first/last bound is not required