How to restrict certain paths in NetworkX graphs? - python

I am trying to calculate shortest path between 2 points using Dijkstra and A Star algorithms (in a directed NetworkX graph).
At the moment it works fine and I can see the calculated path but I would like to find a way of restricting certain paths.
For example if we have following nodes:
nodes = [1,2,3,4]
With these edges:
edges = ( (1,2),(2,3),(3,4) )
Is there a way of blocking/restricting 1 -> 2 -> 3 but still allow 2 -> 3 & 1 -> 2.
This would mean that:
can travel from 1 to 2
can travel from 2 to 3
cannot travel from 1 to 3 .. directly or indirectly (i.e. restrict 1->2->3 path).
Can this be achieved in NetworkX.. if not is there another graph library in Python that would allow this ?
Thanks.

Interesting question, I never heard of this problem, probably because I don't have much background in this topic, nor much experience with NetworkX. However, I do have a idea for a algorithm. This may just be the most naive way to do this and I'd be glad to hear of a cleverer algorithm.
The idea is that you can use your restriction rules to transform you graph to a new graph where all edges are valid, using the following algorithm.
The restriction of path (1,2,3) can be split in two rules:
If you came over (1,2) then remove (2,3)
If you leave over (2,3) then remove (1,2)
To put this in the graph you can insert copies of node 2 for each case. I'll call the new nodes 1_2 and 2_3 after the valid edge in the respective case. For both nodes, you copy all incoming and outgoing edges minus the restricted edge.
For example:
Nodes = [1,2,3,4]
Edges = [(1,2),(2,3),(4,2)]
The valid path shall only be 4->2->3 not 1->2->3. So we expand the graph:
Nodes = [1,1_2,2_3,3,4] # insert the two states of 2
Edges = [ # first case: no (1_2,3) because of the restriction
(1,1_2), (4, 1_2)
# 2nd case, no (1,2_3)
(2_3,3), (4,2_3)]
The only valid path in this graph is 4->2_3->3. This simply maps to 4->2->3 in the original graph.
I hope this answer can at least help you if you find no existing solution. Longer restriction rules would blow up the graph with a exponentially growing number of state nodes, so either this algorithm is too simple, or the problem is hard ;-)

You could set your node data {color=['blue']} for node 1, node 2 has {color=['red','blue']} and node3 has {color=['red']}. Then use an networkx.algorithms. astar_path() approach setting the
heuristic is set to a function which returns a might_as_well_be_infinity when it encountered an node without the same color you are searching for
weight=less_than_infinity.

Related

Checking if a graph contain a given induced subgraph

I'm trying to detect some minimal patterns with properties in random digraphs. Namely, I have a list called patterns of adjacency matrix of various size. For instance, I have [0] (a sink), but also a [0100 0001 1000 0010] (cycle of size 4), [0100, 0010, 0001, 0000] a path of length 3, etc.
When I generate a digraph, I compute all sets that may be new patterns. However, in most of the case it is something that I don't care about: for instance, if the potential new pattern is a cycle of size 5, it does not teach me anything because it has a cycle of length 3 as an induced subgraph.
I suppose one way to do it would look like this:
#D is the adjacency matrix of a possible new pattern
new_pattern = True
for pi in patterns:
k = len(pi)
induced_subgraphs = all_induced_subgraphs(D, k)
for s in induced_subgraphs:
if isomorphic(s, pi):
new_pattern = False
break
where all_induced_subgraphs(D,k) gives all possible induced subgraphs of D of size k, and isomorphic(s,pi) determines if s and pi are isomorphic digraphs.
However, checking all induced subgraphs of a digraph seems absolutely horrible to do. Is there a clever thing to do there?
Thanks to #Stef I learned that this problem has a name
and can be solved using on netwokx with a function described on this page.
Personally I use igraph on my project so I will use this.

Python basic spanning tree algorithm

I cannot figure out how to implement a basic spanning tree in Python; an un-weighted spanning tree.
I've learned how to implement an adjacency list:
for edge in adj1:
x, y = edge[int(0)], edge[int(1)]
if x not in adj2: adj2[x] = set()
if y not in adj2: adj2[y] = set()
adj2[x].add(y)
adj2[y].add(x)
print(adj2)
But I don't know how to implement "finding nearest unconnected vertex".
You don't say which spanning-tree algorithm you need to use. DFS? BFS? Prim's with constant weights? Kruskal's with constant weights? Also, what do you mean by "nearest" unconnected vertex, since the graph is unweighted? All the vertices adjacent to a given vertex v will be at the same distance from v. Finally, are you supposed to start at an arbitrary vertex? at 0? at a user-specified vertex? I'll assume 0 and try to push you in the right direction.
You need to have some way to represent which vertices are already in the spanning tree, so that you don't re-add them to the tree along another edge. That would form a cycle, which can't happen in a tree. Since you definitely need a way to represent the tree, like the [ [1], [0,2,3], [1], [1] ] example you gave, one way to start things out is with [ [], [], [], [] ].
You also need to make sure that you eventually connect everything to a single tree, rather than, for example, finishing with two trees that, taken together, cover all the vertices, but that aren't connected to each other via any edge. There are two ways to do this: (1) Start with a single vertex and grow the tree incrementally until it covers all the nodes, so that you never have more than one tree. (2) Add edges in some other way, keeping track of the connected components, so that you can make sure eventually to connect all the components. Unless you know you need (2), I suggest sticking with (1).
Getting around to your specific question: If you have the input inputgraph = [[1,2],[0,2,3],[0,1],[1]], and you start with currtree = [ [], [], [], [] ], and you start at vertex 0, then you look at inputgraph[0], discover that 0 is adjacent to 1 and 2, and pick either (0,1) or (0,2) to be an edge in the tree. Let's say the algorithm you are using tells you to pick (0,1). Then you update currtree to be [ [1], [0], [], [] ]. Your algorithm then directs you either to pick another edge at 0, which in this inputgraph would have to be (0,2), or directs you to pick an edge at 1, which could be (1,2) or (1,3). Note that your algorithm has to keep track of the fact that (1,0) is not an acceptable choice at 1, since (0,1) is already in the tree.
You need to use one of the algorithms I listed above, or some other spanning-tree algorithm, to be systematic about which vertex to examine next, and which edge to pick next.
I hope that gave you an idea of the issues you have to consider and how you can map an abstract algorithm description to running code. I leave learning the algorithm to you!

How to create a set of neighboring nodes based on "from" and "to" arc data

Imagine that you have a set of nodes (1 2 3) and that these nodes are connected through arcs (1,2), (1,3) and (2,3). Together representing a network.
How can I create a subset of nodes, containing all neighboring nodes? i.e. I wan't the following subset to be something like:
NeighborNode
1 2 3
2 1 3
3 1 2
This Python code is far off, but maybe you get the idea:
def NNode_rule(model,i):
for i in model.Nodes:
model.NNodes[i].add(model.ToNode[i]) if model.Nodes[i]==model.FromNode[i]
model.NNodes = Set(model.Nodes, initialize=NNode_rule)
Do you know what Object-oriented programming is?
I think the easiest solution is to create a simple class (e.g. Node) that has an attribute neighbors which is a list of other nodes.
You also need a method that adds an edge between two nodes, something like this:
def add_edge(self, other_node):
self.neighbors.append(other_node)
other_node.neighbors.append(self)
Then every Node holds the information which neighbors it has.
Hope this helps ;)

Prune nodes not in networkx simple path?

I have a DiGrraph and I want to prune any node that's not contained in one of the simple paths between two of the nodes that I specify. (Another way to think of it is any node that can't reach both the start and end points should be trimmed).
The best way I've found to do this is to get all_simple_paths, then to rebuild a new graph using those, but I'm hoping for a more elegant and less error prone solution. For example, is there a way to determine what's NOT on a simple path, and to then delete those nodes?
You can use the method all_simple_paths which returns a generator but you only need the first path. Then you can use the G.subgraph(nbunch) to return the induced graph from your path.
EDIT: to return the subgraphs induced by all simple paths just concatenate the uniques nodes returned by all_simple_paths.
import networkx as nx
import itertools
G = nx.complete_graph(10) # or DiGraph, MultiGraph, MultiDiGraph, etc
# Concatenate all the paths and keep unique nodes (in one line)
all_path_nodes = set(itertools.chain(*list(nx.all_simple_paths(G, source=0, target=3))))
# Extract the induced subgraph from a given list of nodes
H = G.subgraph(all_path_nodes)
print(nx.info(H))
Output:
Name: complete_graph(10)
Type: Graph
Number of nodes: 10
Number of edges: 45
Average degree: 9.0000
I did make some progress on this while #kikohs was working to understand my question and provide his answer, so I'm posting this as an alternative solution to the problem. I do think his answer is superior though!
def _trim_branches(self, g, start, end):
"""Find all the paths from start to finish, and nuke any nodes that
aren't in those paths.
"""
good_nodes = set()
for path in networkx.all_simple_paths(
g,
source=start,
target=end):
[good_nodes.add(n) for n in path]
for node in g.nodes:
if node not in good_nodes:
g.remove_node(node)
return g
Using subgraph to do the second loop is clearly better, as is his one-liner using itertools.chain. Great stuff around these parts today!

NetworkX: Subgraph Isomorphism by edge and node attributes

Suppose I have 2 graphs A and B and I want to know if A is a subgraph of B.
The nodes contain attributes, say, 'size' and 'material'.
When I run:
GM = networkx.algorithms.isomorphism.GraphMatcher(B,A)
print networkx.algorithms.isomorphism.subgraph_is_isomorphic()
This only matches graph by edges only and not by edges and attribute.
Any clue on how check attributes?
Also, suppose B contains 2 connected graphs of A.
When I run:
GM.mapping
This will output only 1 of the subgraphs of A. Any idea on how to output every subgraph?
I've solved this by using:
print GM = iso.GraphMatcher(B,A,node_match=iso.categorical_node_match(['material', 'size'],['metal',1]))
What I didn't know before is that ['metal',1] is just a default and not a hard match.
You can iterate over all possible subgraphs in the following way
GM = networkx.algorithms.isomorphism.GraphMatcher(B,A)
for subgraph in GM.subgraph_isomorphisms_iter():
print subgraph
subgraph in this example is a dictionary that maps nodes of B to nodes of A.
For the question of attribute matching, drum's suggestion has worked for me. Additional attribute matching actually speeds up things significantly for large graphs.

Categories

Resources