Skip to content

Commit 9c8c35b

Browse files
committed
Initial commit (uploading existing code)
0 parents  commit 9c8c35b

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__pychache__/

cuttingplanes.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import simplex
2+
import numpy as np
3+
import math
4+
5+
EPSILON = 0.0001
6+
7+
def solve(c, b, A, debug_output=False, blands_rule=False):
8+
n = len(c)
9+
m = len(b)
10+
11+
simplex_tableau = simplex.make_tableau(c, b, A)
12+
13+
return solve_tableau(n, m, simplex_tableau, debug_output, blands_rule)
14+
15+
def solve_tableau(n, m, simplex_tableau, debug_output=False, blands_rule=False):
16+
while True:
17+
if debug_output:
18+
print("Current simplex + cutting planes tableau :")
19+
print(simplex_tableau)
20+
21+
# Solving the relaxed linear program
22+
solved_simplex_tableau = simplex.solve_tableau(n, m, simplex_tableau.copy(), False, blands_rule)
23+
24+
if debug_output:
25+
print("Solved simplex + cutting planes tableau :")
26+
print(solved_simplex_tableau)
27+
28+
# Finding a non integer basic variable
29+
basic_variables = simplex.get_basic_variables(solved_simplex_tableau)
30+
basic_variable_row = -1
31+
basic_variable_column = -1
32+
for row, column, value in basic_variables:
33+
# This is how we test if a value is close to an integer (we have to because of floating point precision errors)
34+
if not -EPSILON < value - round(value) < EPSILON:
35+
basic_variable_row = row
36+
basic_variable_column = column
37+
break
38+
else:
39+
# We found a solution
40+
if debug_output:
41+
print("Optimal cutting planes + simplex tableau found")
42+
simplex_tableau = solved_simplex_tableau
43+
break
44+
45+
# Creating a linear constraint (used inequality : x_i + sum_j(floor(a_i,j)x_j) <= floor(b_i))
46+
constraint_row = np.zeros(n + m)
47+
for c in range(n + m):
48+
if c != basic_variable_column:
49+
constraint_row[c] = math.floor(solved_simplex_tableau[basic_variable_row, c])
50+
constraint_row[basic_variable_column] = 1.0
51+
constraint_b_value = math.floor(solved_simplex_tableau[basic_variable_row, -1])
52+
53+
simplex_tableau = simplex.add_constraint(simplex_tableau, constraint_row, constraint_b_value)
54+
m += 1
55+
56+
return simplex_tableau
57+
58+
# Test case : max x, -x <= 0
59+
# Output should be : Unbounded tableau exception
60+
61+
# Test case : max x, x <= 1.5
62+
# Output should be x = 1
63+
64+
# Test case : max x + y, x + y/3 <= 3, x/3 + y <= 3
65+
# Output should be x = 2, y = 2
66+
67+
# Test case : max y, -x + y <= 1, 3x + 2y <= 12, 2x + 3y <= 12
68+
# Output should be x = 1, y = 2 or x = 2, y = 2
69+
70+
if __name__ == '__main__':
71+
print("Cutting planes + simplex algorithm")
72+
print("Maximize <c, x> subject to constraints Ax <= b, x_i >= 0, x_i integer, where A is an m*n matrix")
73+
print("==================")
74+
75+
n = int(input("Size n (size of vector x) : "))
76+
m = int(input("Size m (amount of constraints) : "))
77+
78+
print("c vector :")
79+
c = np.array([float(input("c_" + str(i) + " : ")) for i in range(n)])
80+
print("b vector :")
81+
b = np.array([float(input("b_" + str(i) + " : ")) for i in range(m)])
82+
print("A matrix :")
83+
A = []
84+
for i in range(m):
85+
A.append([float(input("A_" + str(i) + "," + str(j) + " : ")) for j in range(n)])
86+
A = np.array(A)
87+
88+
simplex_tableau = solve(c, b, A, True)
89+
90+
exit(0)

simplex.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import numpy as np
2+
3+
def make_tableau(c, b, A):
4+
n = len(c)
5+
m = len(b)
6+
7+
simplex_tableau = np.hstack((A, np.identity(m), np.zeros((m, 1)), np.array([b]).T))
8+
simplex_tableau = np.vstack((simplex_tableau, np.array([np.hstack((-c, np.zeros(m), np.ones(1), np.zeros(1)))])))
9+
10+
return simplex_tableau
11+
12+
def solve(c, b, A, debug_output=False, blands_rule=False):
13+
n = len(c)
14+
m = len(b)
15+
16+
simplex_tableau = make_tableau(c, b, A)
17+
18+
return solve_tableau(n, m, simplex_tableau, debug_output, blands_rule)
19+
20+
def solve_tableau(n, m, simplex_tableau, debug_output=False, blands_rule=False):
21+
if debug_output:
22+
print("Simplex tableau :")
23+
print(simplex_tableau)
24+
25+
while True:
26+
# Column selection
27+
# Equivalent to searching for the variable in the base that would increase the output function the most
28+
optimal = True
29+
column = 0
30+
if blands_rule:
31+
for i, e in enumerate(simplex_tableau[-1]):
32+
if e < 0:
33+
optimal = False
34+
column = i
35+
break
36+
else:
37+
for i, e in enumerate(simplex_tableau[-1]):
38+
if e < 0:
39+
optimal = False
40+
if e < simplex_tableau[-1, column]:
41+
column = i
42+
43+
if optimal:
44+
if debug_output:
45+
print("Optimal simplex tableau found")
46+
break
47+
48+
# Row selection
49+
# Equivalent to searching for the variable outside the base that constraints the augmentation of the output function the most (smallest b_i / leaving variable coefficient)
50+
row = -1
51+
for i in range(m):
52+
# Only positive entries are considered
53+
if simplex_tableau[i, column] <= 0:
54+
continue
55+
if row == -1 or simplex_tableau[row, -1] / simplex_tableau[row, column] > simplex_tableau[i, -1] / simplex_tableau[i, column]:
56+
row = i
57+
if row == -1:
58+
raise Exception(f"Unbounded solutions in tableau (selected column : {column})", simplex_tableau)
59+
60+
pivot = simplex_tableau[row, column]
61+
simplex_tableau[row] /= pivot
62+
63+
for r in range(len(simplex_tableau)):
64+
if r != row:
65+
simplex_tableau[r] -= simplex_tableau[row] * simplex_tableau[r, column]
66+
67+
if debug_output:
68+
print("Current simplex tableau :")
69+
print(simplex_tableau)
70+
71+
return simplex_tableau
72+
73+
# Returns an array of basic variables (row, column, value)
74+
def get_basic_variables(simplex_tableau):
75+
x = []
76+
77+
# We are excluding the last two columns as they do not contain variables
78+
for i in range(len(simplex_tableau.T) - 2):
79+
nonzeros = 0
80+
nonones = 0
81+
oneidx = -1
82+
for cidx, c in enumerate(simplex_tableau.T[i]):
83+
if c != 0:
84+
nonzeros += 1
85+
if c != 1:
86+
nonones += 1
87+
if c == 1:
88+
oneidx = cidx
89+
90+
if nonzeros == 1 and nonones == len(simplex_tableau.T[i]) - 1:
91+
# This is a basic variable
92+
x.append((oneidx, i, simplex_tableau[oneidx, -1]))
93+
94+
return x
95+
96+
def add_constraint(simplex_tableau, constraint_row, constraint_b_value):
97+
rows, columns = simplex_tableau.shape
98+
99+
constraint_row = np.concatenate((constraint_row, [1.0, 0.0, constraint_b_value]))
100+
101+
simplex_tableau = np.insert(simplex_tableau, -2, np.zeros(rows), axis=1)
102+
simplex_tableau = np.insert(simplex_tableau, -1, constraint_row, axis=0)
103+
104+
slack_variable = np.zeros(rows)
105+
slack_variable[-2] = 1
106+
slack_variable = np.array([slack_variable])
107+
108+
return simplex_tableau
109+
110+
if __name__ == '__main__':
111+
print("Simplex algorithm")
112+
print("Maximize <c, x> subject to constraints Ax <= b, x_i >= 0, where A is an m*n matrix")
113+
print("==================")
114+
115+
n = int(input("Size n (size of vector x) : "))
116+
m = int(input("Size m (amount of constraints) : "))
117+
118+
print("c vector :")
119+
c = np.array([float(input("c_" + str(i) + " : ")) for i in range(n)])
120+
print("b vector :")
121+
b = np.array([float(input("b_" + str(i) + " : ")) for i in range(m)])
122+
print("A matrix :")
123+
A = []
124+
for i in range(m):
125+
A.append([float(input("A_" + str(i) + "," + str(j) + " : ")) for j in range(n)])
126+
A = np.array(A)
127+
128+
solve(c, b, A, True)

0 commit comments

Comments
 (0)