I was implementing the Kruskel's algorithm in Python. But it isn't giving correct answers for dense graphs. Is there any flaw in the logic??
The algo I am using is this:
1) Store all vertices in visited
2) Sort all the edges wrt their weights
3) Keep picking the smallest edge until all vertices, v, have visited[v]=1
This is what I have tried:
def Kruskal(weighted_graph):
visited = {}
for u,v,_ in weighted_graph:
visited[u] = 0
visited[v] = 0
sorted_edges = deque(sorted(weighted_graph, key = lambda x:x[2]))
mstlist = []
sumi=0
while 0 in visited.values():
u,v,w = sorted_edges.popleft()
if visited[u] == 0 or visited[v] == 0:
mstlist.append((u,v))
visited[u] = 1
visited[v] = 1
sumi += w
return (sumi,mstlist)
input is a list of tuples..a single tuple looks like this (source,neighbor,weight)
The Minimum spanning tree sum which I am calculating is coming out to be wrong for dense graphs. Please help. Thank you!
Your condition for adding the edge is if visited[u] == 0 or visited[v] == 0, so you require that one of the adjacent nodes is not connected to any edge you have added to your MST so far. For the algorithm to work correctly, however, it is sometimes necessary to add edges even if you have already "visited" both nodes. Consider this very simple graph:
[
(A, B, 2),
(B, C, 3),
(C, D, 1),
]
visual representation:
[A]---(2)---[B]---(3)---[C]---(1)---[D]
Your algorithm would first add the edge (C, D), marking C and D as visited.
Then, it would add the edge (A, B), marking A and B as visited.
Now, you're only left with the edge (B, C). The MST for this graph obviously contains this edge. But your condition fails -- both B and C are marked as visited. So, your algorithm doesn't add that edge.
In conclusion, you need to replace that check. You should check whether the two nodes that the current edge connects are already connected by the edges that you have added to your MST so far. If they are, you skip it, otherwise you add it.
Usually, disjoint-set data structures are used for implementing this with a good run time complexity (see the pseudocode on wikipedia).
However, your code so far already has bad run time complexity as 0 in visited.values() has to linearly search through the values of the dictionary until it either reaches the end or finds an element with value 0, so it might be enough for you to do something simpler.
You can find some implementations of the algorithm using disjoint-set data structures on the internet, e.g. here.
Related
I want to find out if a given subgraph of G is a complete graph. I was expecting to find a built in function, like is_complete_graph(G), but I can't see anything like that.
My current solution is to create a new helper function:
def is_complete(G):
n = G.order()
return n*(n-1)/2 == G.size()
I imagine this is probably fast but I feel wrong implementing this kind of thing myself, and I feel there must be a 'right' way to do it in NetworkX.
I only need a solution for simple undirected graphs.
edit
The answer at the bottom is relatively clean. However, it appears that the following is faster:
def is_subclique(G,nodelist):
H = G.subgraph(nodelist)
n = len(nodelist)
return H.size() == n*(n-1)/2
I have to admit, I don't entirely understand. But clearly creating the subgraph is faster than checking whether each edge exists.
Slower alternative I expected to be faster:
We'll check to see if all of the edges are there. We'll use combinations to generate the pairs we check. Note that if combinations returns (1,2), then it will not return (2,1).
from itertools import combinations
import networkx as nx
def is_subclique(G, nodelist):
r'''For each pair of nodes in nodelist whether there is an edge
if any edge is missing, we know that it's not a subclique.
if all edges are there, it is a subclique
'''
for (u,v) in combinations(nodelist,2): #check each possible pair
if not G.has_edge(u,v):
return False #if any edge is missing we're done
return True #if we get to here, then every edge was there. It's True.
G = nx.Graph()
G.add_edges_from([(1,2), (2,3), (3,1), (4,1)])
is_subclique(G, [1,2,3])
> True
is_subclique(G, [1,4])
> True
is_subclique(G, [2,3,4])
> False
You actually need to check more than the number of edges because selfloops aren't allowed for a complete_graph. Also, they potentially change the expected count of edges.
Here's a very fast function -- especially if it isn't a complete graph. Notice that it avoids even counting the edges. It just makes sure each node has the right neighbors.
def is_complete_graph(G):
N = len(G) - 1
return not any(n in nbrdict or len(nbrdict)!=N for n, nbrdict in G.adj.items())
This is for my research in protein folding (So I guess technically a school project)
Summary:
I have the edges of an weighted undirected graph. Each vertex of the graph has anywhere from 1 to 20-ish edges. I would like to trim this graph down such that no vertex has more than 6 edges. I would also like the graph to retain as much connectivity as possible (maximize the degree).
Background:
I have a Delaunay Tesselation of the atoms (pointcloud essentially) in a protein using the scipy library. I use this to create a list of all pairs of residues that are in contact with each other (I store the distance between them). This list contains every pair (twice), and the distance between the pairs. (The residue contains many atoms so I use the average position of them to get the position of the residue)
pairs
[(ALA 1, GLU 2, 2.7432), (ALA 1, GLU 2, 2.7432), (ALA 4, ASP 27, 4.8938), (ALA 4, ASP 27, 4.8938) ... ]
What I have tried (which works but isn't exactly what I want) is to only store the six closest contacts. (I sort the residue names so I can use collections later)
for contact in residue.contacts[:6]:
pairs.append( tuple( sorted([residue.name, contact.name], key=lambda r: r.name) + [residue.dist[contact]] ) )
And then remove any contacts that are not reciprocated. (I guess technically add contacts that are)
new_pairs = []
counter=collections.Counter(pairs)
for key, val in counter.items():
if val == 2:
new_pairs.append(key)
This works, but I lose some information that I would like to keep. I phrased the question as a graph theory problem because I feel like this problem has already been solved in that field.
I was thinking that greedy algorithm might work:
while run_greedy:
# find the residue with the maximum number of neighbors
# find that residues pair with the maximum number of neighbors but only if the pair exists in pairs
# remove that pair from pairs
# if maximum_degree <= 6: run_greedy = False
Does the greedy algorithm work? Are there known algorithms that do this well? Is there a library that can do this (I am more than willing to change the format of the data to fit the library)?
I hope this is enough information, Thanks in advance for the help.
EDIT this is an variant of the knapsack problem: you add edges one by one, and want to maximize the number of edges while the graph built doesn't exceed a given degree.
The following solution uses dynamic programming.
Let m[i, d] the maximum subset of edges in e_0, ..., e_{i-1} creating a subgraph of maximium degree <= d.
m[i, 0] = {}
m[0, d] = {}
m[i, d] = m[i-1, d] + {e_i} if the degree of the graph is <= d
m[i, d] = m[i-1, d-1] + {e_i} if it has more edges than m[i-1][d], else m[i-1][d].
Hence the algorithm (not tested):
for i in 0..N:
m[i][0] = {}
for d in 1..K:
m[0][d] = {}
for d in 1..K:
for i in 1..N:
G1 = m[i-1][d] + {e_i}
if D(G1) == d: # can add e_i with degree <= k
m[i][d] = G1
else:
m[i][d] = max(m[i-1][d-1] + {e_i}, m[i-1][d]) # key=cardinal
Solution is: m[N-1][K-1]. Time complexity is O(K N^2) (imbricated loops : K N + maximum degre of the graph in N or less)
Previous answer
TLDR; I don't know how to find an optimal solution, but a greedy algorithm might give you acceptable result.
The problem
Let me rephrase the problem, based on your question and your code: you want to remove a minimum number of edges from your graph in order to reduce the maximum degree the graph to 6. That is to get the maximal subgraph G' from G with D(u) <= 6 for all u in G'.
The closest idea I found is the K-core of a graph, but that's not exactly the same problem.
Your method
Your method is clearly not optimal, since you keep at most 6 edges of every vertex and recreate the graph with those edges. Take the graph A-B-C:
A -> 1. B, 2. C
B -> 1. C, 2. A
C -> 1. A, 2. B
If you try to reduce the maximum degree of this graph to 1 using your method, the first pass will remove A-B (B is the 2nd neighbor of A), B-A (A is the 2nd neighbor of B) and C-B (B is the 2nd neighbor of C):
A -> 1. B
B -> 1. C
C -> 1. A
The second pass, to insure that the graph is undirected, will remove all the remaining edges (and vertices).
An optimal reduction would be:
A -> 1. B
B -> 1. A
Or any other pair of vertices in A, B, C.
Some strategy
Let:
k = 6
D(u) = max(d(u)-k, 0): the number of neighbors above k, or 0
w(u-v) (resp s(u-v)) = the weak (resp. strong) endpoint of the edge: having the lowest (resp. highest) degree
m(u-v) = min(D(u), D(v))
M(u-v) = max(D(u), D(v))
Let S = sum(D(u) for u in G). The goal is to make S = 0 while removing a minimum number of edges. If you remove:
(1) a floating edge: m(u-v) > 0, then S is decreased by 2 (both endpoints loose 1 degree)
(2) a sinking edge: m(u-v) = 0 and M(u-v) > 0, then S is decreased by 1 (the degree of the weak endpoint is already <= 6)
(3) a sunk edge: M(u-v) = 0, then S is unchanged
Note that a floating edge may become a sinking edge if: 1. its weak endpoint has a degree of k+1; 2. you remove another edge connected to this endpoint. Similarly, a sinking edge can sunk.
You have to remove floating edges while avoid creating sinking edges, because removing a floating edges is more efficient to reduce S. Let K the number of floating edges removed, and L the number of sinking edges removed (we don't remove sunk edges) to make S = 0. We want 2*K + L >= S. Obviously, the idea is to make L as small a possible, because we want a small number of edges removed (K + L).
I doubt you'll find an optimal greedy algorithm, because everything depends on the order of removing and the remote consequences of the current removing are hard to predict.
But you can use a general strategy to limit the creation of sinking edges:
do not remove edges with m(u-v) = 1 unless you have no choice.
if you have to remove an edge with m(u-v) = 1, choose the one whose weak endpoint has the less floating edges (they will become sinking edges).
An algorithm
Here's a greedy algorithm that implements this strategy:
while {u, v in G | m(u-v) > 0} is not empty: // remove floating edges first
remove the edge u-v with:
1. the maxmimum m(u-v)
2. w(u-v) has the minimum of neighbors t with D(t) > 0
3. s(u-v) has the minimum of neighbors t with D(t) > 0
remove all edges from {u, v in G | M(u-v) > 0} // clean up sinking edges
clean orphan vertices
Termination the algorithm terminates because we remove an edge on each iteration, thus {u in G | D(u) > 0} will become empty at some point.
Note: you can use a heap and update m(u-v) after each removing.
Say, I have a set of unique, discrete parameter values, stored in a variable 'para'.
para=[1,2,3,4,5,6,7,8,9,10]
Each element in this list has 'K' number of neighbors (given: each neighbor ϵ para).
EDIT: This 'K' is obviously not the same for each element.
And to clarify the actual size of my problem: I need a neighborhood of close to 50-100 neighbors on average, given that my para list is around 1000 elements large.
NOTE: A neighbor of an element, is another possible 'element value' to which it can jump, by a single mutation.
neighbors_of_1 = [2,4,5,9] #contains all possible neighbors of 1 (i.e para[0])
Question: How can I define each of the other element's
neighbors randomly from 'para', but, keeping in mind the previously
assigned neighbors/relations?
eg:
neighbors_of_5=[1,3,7,10] #contains all possible neighbors of 5 (i.e para[4])
NOTE: '1' has been assigned as a neighbor of '5', keeping the values of 'neighbors_of_1' in mind. They are 'mutual' neighbors.
I know the inefficient way of doing this would be, to keep looping through the previously assigned lists and check if the current state is a neighbor of another state, and if True, store the value of that state as one of the new neighbors.
Is there a cleaner/more pythonic way of doing this? (By maybe using the concept of linked-lists or any other method? Or are lists redundant?)
This solution does what you want, I believe. It is not the most efficient, as it generates quite a bit of extra elements and data, but the run time was still short on my computer and I assume you won't run this repeatedly in a tight, inner loop?
import itertools as itools
import random
# Generating a random para variable:
#para=[1,2,3,4,5,6,7,8,9,10]
para = list(range(10000))
random.shuffle(para)
para = para[:1000]
# Generate all pais in para (in random order)
pairs = [(a,b) for a, b in itools.product(para, para) if a < b]
random.shuffle(pairs)
K = 50 # average number of neighbors
N = len(para)*K//2 # total connections
# Generating a neighbors dict, holding all the neighbors of an element
neighbors = dict()
for elem in para:
neighbors[elem] = []
# append the neighbors to eachother
for pair in pairs[:N]:
neighbors[pair[0]].append(pair[1])
neighbors[pair[1]].append(pair[0])
# sort each neighbor list
for neighbor in neighbors.values():
neighbor.sort()
I hope you understand my solution. Otherwise feel free to ask for a few pointers.
Neighborhood can be represented by a graph. If N is a neighbor of B does not necessarily implies that B is a neighbor of A, it is directed. Else it is undirected. I'm guessing you want a undirected graph since you want to "keep in mind the relationship between the nodes".
Besides the obvious choice of using a third party library for graphs, you can solve your issue by using a set of edges between the graph vertices. Edges can be represented by the pair of their two extremities. Since they are undirected, either you use a tuple (A,B), such that A < B or you use a frozenset((A,B)).
Note there are considerations to take about what neighbor to randomly choose from when in the middle of the algorithm, like discouraging to pick nodes with a lot of neighbor to avoid to go over your limits.
Here is a pseudo-code of what I'd do.
edges = set()
arities = [ 0 for p in para ]
for i in range(len(para)):
p = para[i]
arity = arities[i]
n = random.randrange(50, 100)
k = n
while k > 0:
w = list(map(lambda x : 1/x, arities))
#note: test what random scheme suits you best
j = random.choices(para, weight = w )
#note: I'm storing the vertices index in the edges rather than the nodes.
#But if the nodes are unique, you could store the nodes.
e = frozenset((i,j))
if e not in edges:
edges.add(e)
#instead of arities, you could have a list of list of the neighbours.
#arity[i] would be len(neighbors[i]), then
arities[i] += 1
arities[j] += 1
k-=1
I am learning about topological sort, and graphs in general. I implemented a version below using DFS but I am having trouble understanding why the wikipedia page says this is O(|V|+|E|) and analyzing its time complexity, and the difference between |V|+|E| and n^2 in general.
Firstly, I have two for loops, logic says that it would be (n^2) but also isnt it true that in any DAG(or Tree), there is n-1 edges, and n vertexes? How is this any different from n^2 if we can remove the "-1" for non significant value?
graph = {
1:[4, 5, 7],
2:[3,5,6],
3:[4],
4:[5],
5:[6,7],
6:[7],
7:[]
}
from collections import defaultdict
def topological_sort(graph):
ordered, marked = [], defaultdict(int)
while len(ordered) < len(graph):
for vertex in graph:
if marked[vertex]==0:
visit(graph, vertex, ordered, marked)
return ordered
def visit(graph, n, ordered, marked):
if marked[n] == 1:
raise 'Not a DAG'
marked[n] = 1
for neighbor in graph.get(n):
if marked[neighbor]!=2:
visit(graph, neighbor, ordered, marked)
marked[n] = 2
ordered.insert(0, n)
def main():
print(topological_sort(graph))
main()
The proper implementation works in O(|V| + |E|) time because it goes through every edge and every vertex at most once. It's the same thing as O(|V|^2) for a complete (or almost complete graph). However, it's much better when the graph is sparse.
You implementation is O(|V|^2), not O(|V| + |E|). These two nested loops:
while len(ordered) < len(graph):
for vertex in graph:
if marked[vertex]==0:
visit(graph, vertex, ordered, marked)
do 1 + 2 ... + |V| = O(|V|^2) iterations in the worst case (for instance, for an empty graph). You can easily fix by getting rid of the outer loop (it's that simple: just remove the while loop. You don't need it).
Given a graph H, I have to find all graphs g that fulfill a special requirement (which I call requirement2). All these graphs are stored in a set s. The graphs g must have the same number of vertices as H and all graphs are directed. So in essence, I am finding edges that I can add to g to satisfy requirement2. I notate my graphs using a dictionary so for example:
H = {
'1': {'1': set([(0, 1)])},
'3': {'3': set([(0, 1)]), '2': set([(0, 1)])},
'2': {'1': set([(0, 1)]), '3': set([(0, 1)]), '2': set([(0, 1)])},
}
means that there is an edge from vertex 1 to 1 and an edge from 3 to 2 etc...Please ignore the set([(0, 1)])'s for now for they are not relevant to my question. To find these g's that satisfy requirement2, I add an edge to an empty graph g with the same number of vertices as H and then use requirement1 to get an array of edges that I know might potentially work for satisfying requirement2. Then I add one of those edges deemed ok by requirement1 to g and test requirement2. Why I do not directly use requirement2 is that it would be way too time consuming to add every edge that g does not have and then test requirement2 on it. Early pruning saves time for me. I proceed with a Depth first search and if requirement 1 returns an empty array, I know I have to backtrack. The current code I have uses memoization to store subproblem solutions but this code is still not fast enough...With 7 nodes, this code can take 2500 seconds to run. Any suggestions on how to speed this up would be appreciated.
A little more on some functions this code uses:
addanedge(g,e) adds edge e to g
delanedge(g,e) deletes edge e in g
edgelist(g) returns a list of edges of g
complement(g) returns a list of edges that g does not have
requirement1(H,g) returns T if g fulfills some requirement
involving H and F otherwise. It's purpose is for pruning down the number of edges I need to add for each round.
requirement2(H,g) returns T if g fulfills some requirement involving H and F otherwise. If true, I add g to s
memo stores my subproblem solutions
gsig simply converts the graph g to a form that I can add to a set.
def findgs(H):
g = {n:{} for n in H}
s = set()
#memo
def addedges(g,H,edges):
if edges:
masks = []
for e in edges:
addanedge(g,e)
if requirement1(H,g):
masks.append(False)
else:
masks.append(True)
delanedge(g,e)
nedges = [edges[i] for i in range(len(edges)) if masks[i]]
n = len(nedges)
if n:
for i in range(n):
addanedge(g,nedges[i])
if requirement2(H,g):
s.add(gsig(g))
addedges(g,H,nedges[:i]+nedges[i+1:])
delanedge(g,nedges[i])
edges = edgelist(complement(g))
addedges(g,H,edges)
return s
def memo(func):
cache = {}
#wraps(func)
def wrap(*args):
s = tool.signature(args[0],args[2])
if s not in cache:
cache[s] = func(*args)
return cache[s]
return wrap
One thing I have noticed is that the same graphs that satisfy requirement2 appear MANY times and are cut out because s is a set and duplicates are not allowed.