Edmonds–Karp time complexity - python

I am trying to implement a version of the Edmonds–Karp algorithm for an undirected graph. The code below works, but it is very slow when working with big matrices.
Is it possible to get the Edmonds–Karp algorithm to run faster, or should I proceed to another algorithm, like "Push Relabel"? I have though of some kind of dequeue working with the bfs, but I don't know how to do that.
The code:
def bfs(C, F, s, t):
stack = [s]
paths={s:[]}
if s == t:
return paths[s]
while(stack):
u = stack.pop()
for v in range(len(C)):
if(C[u][v]-F[u][v]>0) and v not in paths:
paths[v] = paths[u]+[(u,v)]
if v == t:
return paths[v]
stack.append(v)
return None
def maxFlow(C, s, t):
n = len(C) # C is the capacity matrix
F = [[0] * n for i in range(n)]
path = bfs(C, F, s, t)
while path != None:
flow = min(C[u][v] - F[u][v] for u,v in path)
for u,v in path:
F[u][v] += flow
F[v][u] -= flow
path = bfs(C,F,s,t)
return sum(F[s][i] for i in range(n))
C = [[ 0, 3, 3, 0, 0, 0 ], # s
[ 3, 0, 2, 3, 0, 0 ], # o
[ 0, 2, 0, 0, 2, 0 ], # p
[ 0, 0, 0, 0, 4, 2 ], # q
[ 0, 0, 0, 2, 0, 2 ], # r
[ 0, 0, 0, 0, 2, 0 ]] # t
source = 0 # A
sink = 5 # F
maxVal = maxFlow(C, source, sink)
print("max_flow_value is: ", maxVal)

I think your solution can benefit from better graph representation. In particular try to keep a list of neighbours for the BFS. I actually wrote a quite long answer on the graph representation I use for flow algorithms here https://stackoverflow.com/a/23168107/812912
If your solution is still too slow I would recommend switching to Dinic's algorithm it has served me well in many tasks.

Related

Class method returns None

I did a few modifications to the Bellman-Ford to get some data I need to compare but I can't seem to be able to get some of the information I need from the return the PrintArr method, while it prints the 'dist_list' the return won't take it nor am I being able to create the object using list comprehension either.
Class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = []
def addEdge(self, u, v, w):
self.graph.append([u, v, w])
def printArr(self, dist, src):
#print("Source End Distance")
dist_list = []
for i in range(self.V):
#print("{0}\t{1}\t{2}".format(src, i, dist[i]))
#print(src, i, dist[i])
dist_list.append([src, i, dist[i]])
print(dist_list)
print(dist_list == [[src, i, dist[i]] for i in range(self.V)])
return [[src, i, dist[i]] for i in range(self.V)]
def BellmanFord(self, src):
dist = [float("Inf")] * self.V
dist[src] = 0
for _ in range(self.V - 1):
for u, v, w in self.graph:
if dist[u] != float("Inf") and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
for u, v, w in self.graph:
if dist[u] != float("Inf") and dist[u] + w < dist[v]:
print("Graph contains negative weight cycle")
return
self.printArr(dist, src)
matrix = [[0, 2, 2, 2, -1], [9, 0, 2, 2, -1], [9, 3, 0, 2, -1], [9, 3, 2, 0, -1], [9, 3, 2, 2, 0]]
g = Graph(len(matrix))
[[g.addEdge(i, j, element) for j, element in enumerate(array) if i != j] for i, array in enumerate(matrix)]
print(g.BellmanFord(0))
Output:
[[0, 0, 0], [0, 1, 2], [0, 2, 1], [0, 3, 1], [0, 4, -1]]
True
None
Print: OK
List A = List B
Why return None??? What am I missing?
The None comes from:
print(g.BellmanFord(0))
BellmanFord never returns anything useful under any circumstances (it either falls off the end and implicitly returns None, or executes a plain return, which also return None). Remove the print() around the call, and you'll avoid the None output.
Alternatively, change self.printArr(dist, src) to return self.printArr(dist, src) so it does return something useful (assuming the early return, which should probably be raising an exception rather than silently returning None, isn't invoked).

dcg and ndcg implementation in python

I have implemented the Discounted Cumulative Gain (DCG) and Normalized Discounted Cumulative Gain (NDCG) in python. I am not sure if the code is correct or did I forget some important criteria for DCG and NDCG. Here is my code so far:
import numpy as np
def get_dcg_score(predictions: np.ndarray, test_interaction_matrix: np.ndarray, topK = 10) -> float:
"""
predictions - np.ndarray - predictions of the recommendation algorithm for each user.
test_interaction_matrix - np.ndarray - test interaction matrix for each user.
returns - float - mean dcg score over all user.
"""
score = None
# TODO: YOUR IMPLEMENTATION.
score = []
for idx, (pred,test) in enumerate(zip(predictions,test_interaction_matrix)):
print(idx,pred,test)
for i, (j,jj) in enumerate(zip(pred[:topK], test[:topK])):
if i == 0 and jj == 1:
sc = jj
score.append(sc)
if i != 0 and jj == 1:
sc = jj / np.log2(j+2)
score.append(sc)
if (i != 0 and jj == 0) or ( i == 0 and jj == 0):
continue
score = sum(score)/len(predictions)
return score
I evaluate this on the two arrays.
predictions = np.array([[0, 1, 2, 3], [3, 2, 1, 0]])
test_interaction_matrix = np.array([[1, 0, 0, 0], [0, 0, 0, 1]])
dcg_score = get_dcg_score(predictions, test_interaction_matrix, topK=4)
print(dcg_score)
assert np.isclose(dcg_score, 1), "1 expected"
Now for the NDCG I need to implement Ideal Discounted Cumulative Gain (IDCG) first and then divide DCG by IDCG.
Here what I have for NDCG.
def get_ndcg_score(predictions: np.ndarray, test_interaction_matrix: np.ndarray, topK = 10) -> float:
"""
predictions - np.ndarray - predictions of the recommendation algorithm for each user.
test_interaction_matrix - np.ndarray - test interaction matrix for each user.
topK - int - topK recommendations should be evaluated.
returns - average ndcg score over all users.
"""
score = None
# TODO: YOUR IMPLEMENTATION.
score_idcg = []
for i, (vp, vt) in enumerate(zip(predictions,test_interaction_matrix)):
element_sorted = sorted(vp,reverse=True)
for j, (ele_p, ele_vt) in enumerate(zip(element_sorted, vt)):
if j == 0 and ele_vt == 1:
scr = ele_vt
score_idcg.append(scr)
if j != 0 and ele_vt == 1:
scr = ele_vt / np.log2(j+2)
score_idcg.append(scr)
if (j != 0 and ele_vt == 0) or (j == 0 and ele_vt == 0):
continue
print(score_idcg)
score_idcg = sum(score_idcg)/len(predictions)
print(score_idcg)
score_dcg = get_dcg_score(predictions, test_interaction_matrix, topK = 4)
score_ndcg = score_dcg / score_idcg
return score_ndcg
Again I test it on these two arrays:
predictions = np.array([[0, 1, 2, 3], [3, 2, 1, 0], [1, 2, 3, 0], [-1, -1, -1, -1]])
test_interaction_matrix = np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0]])
ndcg_score = get_ndcg_score(predictions, test_interaction_matrix, topK=4)
assert np.isclose(ndcg_score, 1), "ndcg score is not correct."
Could somebody please look at my code and find why I don't get the right result for ndcg test? I just can't figure it out. Please also look at dcg implementation as well if it is faulty. Sorry for the horrible code. Write me if you need more info. Any suggestion is appreciated.

Google ORTools CP-SAT | Get list of index of 1's from a list of ortools-variables

I want to convert [0, 0, 1, 0, 1, 0, 1, 0] to [2, 4, 6] using ortools.
Where "2", "4", "6" in the second list are the index of "1" in the first list.
Using the below code I could get a list [0, 0, 2, 0, 4, 0, 6, 0]. How can I get [2, 4, 6]?
from ortools.sat.python import cp_model
model = cp_model.CpModel()
solver = cp_model.CpSolver()
work = {}
days = 8
horizon = 7
for i in range(days):
work[i] = model.NewBoolVar("work(%i)" % (i))
model.Add(work[0] == 0)
model.Add(work[1] == 0)
model.Add(work[2] == 1)
model.Add(work[3] == 0)
model.Add(work[4] == 1)
model.Add(work[5] == 0)
model.Add(work[6] == 1)
model.Add(work[7] == 0)
v1 = [model.NewIntVar(0, horizon, "") for _ in range(days)]
for d in range(days):
model.Add(v1[d] == d * work[d])
status = solver.Solve(model)
print("status:", status)
vec = []
for i in range(days):
vec.append(solver.Value(work[i]))
print("work",vec)
vec = []
for v in v1:
vec.append(solver.Value(v))
print("vec1",vec)
You should see this output on the console,
status: 4
work [0, 0, 1, 0, 1, 0, 1, 0]
vec1 [0, 0, 2, 0, 4, 0, 6, 0]
Thank you.
Edit:
I also wish to get a result as [4, 6, 2].
For just 3 variables, this is easy. In pseudo code:
The max index is max(work[i] * i)
The min index is min(horizon - (horizon - i) * work[i])
The medium is sum(i * work[i]) - max_index - min_index
But that is cheating.
If you want more that 3 variable, you will need parallel arrays of Boolean variables that indicate the rank of each variable.
Let me sketch the full solution.
You need to build a graph. The X axis are the variables. The why axis are the ranks. You have horizontal arcs going right, and diagonal arcs going right and up. If the variable is selected, you need to use a diagonal arc, otherwise an horizontal arc.
If using a diagonal arc, you will assign the current variable to the rank of the tail of the arc.
Then you need to add constraints to make it a contiguous path:
mass conservation at each node
variable is selected -> one of the diagonal arc must be selected
variable is not selected -> one of the horizontal arc must be selected
bottom left node has one outgoing arc
top right node has one incoming arc

Backtracking pathinding problem in Python

Recently, I've found out about backtracking and without much thinking started on the book from the guy who has shown some Sudoku backtracking tricks (https://www.youtube.com/watch?v=G_UYXzGuqvM&ab_channel=Computerphile. Unfortunately, I'm stuck with the first backtracking problem without the solution.
The problem is formulated accordingly:
Use backtracking to calculate the number of all paths from the bottom left to the top right corner in a
x * y-grid. This includes paths like https://imgur.com/3t3Np4M. Note that every point can only be visited once. Write a function np(x,y) that returns the number of paths in a x*y-grid. E.g. np(2,3) should return 38. Hint: Create a grid of booleans where you mark the positions already visited.
Whatever I change in this short block of code I'm landing nowhere near 38.
```
grid = [[0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0]]
solution = 0
def number_of_paths(x, y):
global solution
global grid
for i in range(0, x):
for j in range(0, y):
if grid[i][j] == 0:
grid[i][j] = 1
number_of_paths(x, y)
grid[i][j] = 0
solution += 1
return
if __name__ == '__main__':
number_of_paths(2, 3)
print(grid)
print(solution)```
That's a sample solution with solution with Sudoku solver.
```
grid = [[5, 3, 0, 0, 7, 0, 0, 0, 0],
[6, 0, 0, 1, 9, 5, 0, 0, 0],
[0, 9, 8, 0, 0, 0, 0, 6, 0],
[8, 0, 0, 0, 6, 0, 0, 0, 3],
[4, 0, 0, 8, 0, 3, 0, 0, 1],
[7, 0, 0, 0, 2, 0, 0, 0, 6],
[0, 6, 0, 0, 0, 0, 2, 8, 0],
[0, 0, 0, 4, 1, 9, 0, 0, 5],
[0, 0, 0, 0, 8, 0, 0, 7, 9]]
import numpy as np
def possible(y, x, n):
global grid
for i in range(0, 9):
if grid[y][i] == n:
return False
for i in range(0, 9):
if grid[i][x] == n:
return False
x0 = (x // 3) * 3
y0 = (y // 3) * 3
for i in range(0, 3):
for j in range(0, 3):
if grid[y0 + i][x0 + j] == n:
return False
return True
def solve():
global grid
for y in range(9):
for x in range(9):
if grid[y][x] == 0:
for n in range(1, 10):
if possible(y, x, n):
grid[y][x] = n
solve()
# backtracking - bad choice
grid[y][x] = 0
return
print(np,matrix(grid))
input("More?")```
A few suggestions:
You might want to use a set for a grid, adding a square as soon as it is visited, if it is not a member of the set yet.
The counter and the grid can be global but it would probably be easier for you to take them as arguments for the function at first. After the solution is clearer you can worry about those details.
You are going about the problem the wrong way. It would be good to have one function calculating the number of paths from the origin to the destination (by calling the function for the neighbors that have not been visited yet. Make sure you update the grid). On top of that you can have a function that calls the path function for every combination of origin and destination. A small tip: You do not have to calculate the same path in reverse direction! You can have a map of calculate sums of paths. If the opposite direction has been calculate, don't bother. Later, double the amount of paths by 2.
Good luck!
I will show you a solution on a coordinate system where (0,0) is the topleft and (maxY,maxX) is the bot right. Going right increases x and going down increases y.
1- If you are trying to solve the exact maze in the image, then your grid array shape is wrong. Notice that you are travelling between corners of the squares, there are 4 points you can be horizontally and 3 points you can be vertically.
2- Hint is telling you about using a boolean mask for visited state, you already have a grid array so a separate array is not necessary.
3- The main problem with your code is how you are progressing in the maze. The loop structure
for i in range(0, x):
for j in range(0, y):
does not make sense because when you are in a position (x, y), you can only move in 4 main directions (right, up, left, down). However this loops make it look like you are trying to branch into all positions behind you, which is not valid. In my code I will explicity show about this traverse stuff.
grid = [[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 0, 0, 0]]
# number of solutions
solution = 0
# maximum values of x and y coordinates
maxX = len(grid[0])-1
maxY = len(grid)-1
# endpoint coordinates, top(y=0) right(x=maxX) of the maze
endX = maxX
endY = 0
# starting point coordinates, bottom(y=maxY) left(x=0) of the maze
mazeStartX = 0
mazeStartY = maxY
def number_of_paths(startX, startY):
global solution
global grid
global mask
# if we reached the goal, return at this point
if (startX == endX and startY == endY):
solution += 1
return
# possible directions are
#RIGHT (+1x, 0y)
#UP (0x, -1y)
#LEFT (-1x, 0y)
#DOWN (0x, +1y)
# I use a direction array like this to avoid nested ifs inside the for loop
dx = [1, 0, -1, 0]
dy = [0, -1, 0, 1]
for d in range(len(dx)):
newX = startX + dx[d]
newY = startY + dy[d]
# out of maze bounds
if (newX < 0 or newY < 0):
continue
# out of maze bounds
if (newX > maxX or newY > maxY):
continue
if (grid[newY][newX] == 1):
# this are is already visited
continue
else:
# branch from this point
grid[newY][newX] = 1
number_of_paths(newX, newY)
grid[newY][newX] = 0
if __name__ == '__main__':
number_of_paths(mazeStartX, mazeStartY)
print(grid)
print(solution)

Recurrence in ID3 algorithm

import numpy as np
training_set = np.array([[0, 1, 0, 1, 0, 1],[0, 0, 0, 1, 0, 0],[0, 0, 0, 0, 1, 0],[1, 0, 1, 0, 1, 0],[0, 1, 1, 1, 0, 1],[0, 1, 0, 0, 1, 1],[1, 1, 1, 0, 0, 0],[1, 1, 1, 1, 0, 1],[0, 1, 1, 0, 1, 0],[1, 1, 0, 0, 0, 1],[1, 0, 0, 0, 1, 0]])
def p(X):
Fx = X[:,X.shape[1]-1]
x0= 0
x1= 0
for i in range(len(Fx)):
if Fx[i-1] == 1:
x0 = x0+1
else:
x1 = x1+1
P0 = x0/len(Fx)
P1 = x1/len(Fx)
return(P0,P1)
def H(X):
result = -p(X)[0]*np.log(p(X)[0])-p(X)[1]*np.log(p(X)[1]) #needs to be log2
print("1 = pure, 0 = unpure 1/2 = decision can be random: Calculating Entropy: -" + str(p(X)[0]) + "*" + str(np.log(p(X)[0])) + "-" + str(p(X)[1]) + "*" + str(np.log(p(X)[1])) )
return result
def Q(X,i):
Xi = X[:,i]
result0= 0
result1= 0
for j in range(len(Xi)):
if Xi[j] == 1:
result1 = result1 + len(X[i,:])
else: result0 = result0 + len(X[i,:])
result1 = result1/len(X)
result0 = result0/len(X)
return(result0,result1)
def X_column(X,i,v):
list = X[np.where(X[:,i] == v)]
return list
def IG(X,i):
result = H(X)-Q(X,i)[0]*H(X_column(X,i,0))-Q(X,i)[1]*H(X_column(X,i,1))
return result
#To teach decision trees on learning set S, we will used following algorithm(ID3):
# 1. There is example set S
# 2. If |{f(x) : (x, f(x)) ∈ S}| = 1= 1 create leaf with label f(x)
# 3. For i = 1,2,...,n calculate value IG(S,i)
# 4. May j be an index o fthe biggest of calculated values
# 5. Set node with label Xj
# 6. For subsets:
# S0 = {(x, f(x)) ∈ S : xj = 0}
# and
# S1 = {(x, f(x)) ∈ S : xj = 1}
# run algorithm recurrent (for S ← S0 i S ← S1) and add new nodes as a childs for a node with label j
def ID3(S, recursion = 0, label = 0, tree = np.array()):
result = np.array()
recursion += 1
rows = S.shape[0]
columns = S.shape[1]
if S[:,columns-1] == True:
tree[recursion]= S[0,columns-1]
break
for i in range(rows):
result[i]= IG(S,i)
j = result.max()
tree[recursion]= 1 #czym jest etykieta xj
S0 = X_column(S,i,0)
tree[recursion+1] = ID3(S0,recursion = recursion )
S1 = X_column(S,i,1)
tree[recursion+2] = ID3(S1,recursion = recursion)
return tree
def pruning():
return tree
I have been working on implementing ID3 algorithm(decision tree), but I have no idea how to solve the recurrence. I've also translated algorithm steps from my laboratory list. Most of the necessary functions are already written, but I just can't grasp recurrency concept at that advanced level. And most of the tutorials are very trivial.

Categories

Resources