I am using the latest version of graph_tool installed in it's own conda environment, as per the installation guide.
I ran into some perplexing behavior with this library recently. When I run the following code:
import graph_tool
graph = graph_tool.Graph(directed=False)
graph.add_vertex(10)
subgraph = graph_tool.GraphView(graph, graph.get_vertices())
print(graph.get_vertices())
print(subgraph.get_vertices())
The output is:
[0 1 2 3 4 5 6 7 8 9]
[1 2 3 4 5 6 7 8 9]
I thought a GraphView was supposed to act like a subgraph induced on the specified vertices (so in the case of my sample code, the entire set of vertices). So why does a GraphView omit the 0th vertex?
Or, if this is actually a bug in graph_tool, what would be a good way to work around it, provided I wanted to work with subgraphs that include the 0th vertex?
You posted the documentation in your answer, but it seems you did not read it carefully enough (emphasis added):
The argument g must be an instance of a Graph class. If specified, vfilt and efilt select which vertices and edges are filtered, respectively. These parameters can either be a boolean-valued PropertyMap or a ndarray, which specify which vertices/edges are selected, or an unary function that returns True if a given vertex/edge is to be selected, or False otherwise.
If you pass a property map or an array, it must be boolean valued, not a list of vertices. This means it must have the form [True, False, False, True, ... ], where True means the corresponding vertex is kept, otherwise it's filtered out. That is why the vertex with index 0 (i.e. False) is removed from your example, and all remaining ones are kept.
So I found a workaround. From the documentation of GraphView:
The argument g must be an instance of a Graph class. If specified,
vfilt and efilt select which vertices and edges are filtered,
respectively. These parameters can either be a boolean-valued
PropertyMap or a ndarray, which specify which vertices/edges are
selected, or an unary function that returns True if a given
vertex/edge is to be selected, or False otherwise.
So the vertex mask can also be specified by a unary function that says whether or not the vertex is part of the subgraph:
def get_subgraph(graph, vertices):
f = lambda x: x in vertices
return graph_tool.GraphView(graph, f)
And, somehow, this version actually works!
subgraph = get_subgraph(graph, graph.get_vertices())
print(graph.get_vertices())
print(subgraph.get_vertices())
Output:
[0 1 2 3 4 5 6 7 8 9]
[0 1 2 3 4 5 6 7 8 9]
So it's not actually impossible to make a GraphView that includes 0 as a vertex, it just apparently doesn't work if you try to do it with a numpy array.
This answer works for me, but I would still be interested if anyone has a better workaround (especially since this one makes it much slower to return the subgraph for a large graph), or if somebody knows why this odd behavior arises in the first place.
EDIT:
This implementation leverages numpy to calculate the vertex mask instead of the native python "in" operation, and is therefore much faster for larger graphs:
def get_subgraph(graph, vertices):
property_map = graph.new_vertex_property("bool")
property_map.a = np.isin(graph.get_vertices(), vertices)
return graph_tool.GraphView(graph, property_map)
Related
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 ;)
I'm very new at this and have to do this for a project so keep that in mind.
I need to write a function sumOfDiagonal that has one parameter of type list.
The list is a 4x4 2-dimensional array of integers (4 rows and 4 columns of integers).
The function must return the sum of the integers in the diagonal positions from top right to bottom left.
I have not tried anything because I have no idea where to begin, so would appreciate some guidance.
Since you haven't specified a language (and this is probably classwork anyway), I'll have to provide pseudo-code. Given the 4x4 2d array, the basic idea is to use a loop specifying the index, and use that index to get the correct elements in both dimensions. Say we had the array:
[][0] [][1] [][2] [][3]
----- ----- ----- -----
[0][] 1 2 3 4
[1][] 5 6 7 8
[2][] 9 10 11 12
[3][] 13 14 15 16
and we wanted to sum the top-left-to-bottom-right diagonal (1+6+11+16)(1). That would be something like:
def sumOfDiagonal (arr, sz):
sum = 0
for i = 0 to sz - 1 inclusive:
sum = sum + arr[i][i]
return sum
That's using the normal means of accessing an array. If, as may be given the ambiguity in the question, your array is actually a list of some description (such as a linked list of sixteen elements), you'll just need to adjust how you get the "array" elements.
For example, a 16-element list would need to get nodes 0, 5, 10 and 15 so you could run through the list skipping four nodes after each accumulation.
By way of example, here's some Python code(2) for doing the top-left-to-bottom-right variant, which outputs 34 (1+6+11+16) as expected:
def sumOfDiagonals(arr):
sum = 0
for i in range(len(arr)):
sum += arr[i][i]
return sum
print(sumOfDiagonals([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]))
(1) To do top right to bottom left simply requires you to change the second term into sz - i - 1.
(2) Python is the ideal pseudo-code language when you want to be able to test your pseudo-code, provided you stay away from its more complex corners :-)
I'm curious in Python why x[0] retrieves the first element of x while x[-1] retrieves the first element when reading in the reverse order. The syntax seems inconsistent to me since in the one case we're counting distance from the first element, whereas we don't count distance from the last element when reading backwards. Wouldn't something like x[-0] make more sense? One thought I have is that intervals in Python are generally thought of as inclusive with respect to the lower bound but exclusive for the upper bound, and so the index could maybe be interpreted as distance from a lower or upper bound element. Any ideas on why this notation was chosen? (I'm also just curious why zero indexing is preferred at all.)
The case for zero-based indexing in general is succinctly described by Dijkstra here. On the other hand, you have to think about how Python array indexes are calculated. As the array indexes are first calculated:
x = arr[index]
will first resolve and calculate index, and -0 obviously evaluates to 0, it would be quite impossible to have arr[-0] to indicate the last element.
y = -0 (??)
x = arr[y]
would hardly make sense.
EDIT:
Let's have a look at the following function:
def test():
y = x[-1]
Assume x has been declared above in a global scope. Now let's have a look at the bytecode:
0 LOAD_GLOBAL 0 (x)
3 LOAD_CONST 1 (-1)
6 BINARY_SUBSCR
7 STORE_FAST 0 (y)
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
Basically the global constant x (more precisely its address) is pushed on the stack. Then the array index is evaluated and pushed on the stack. Then the instruction BINARY_SUBSCR which implements TOS = TOS1[TOS] (where TOS means Top of Stack). Then the top of the stack is popped into the variable y.
As the BINARY_SUBSCR handles negative array indices, and that -0 will be evaluated to 0 before being pushed to the top of the stack, it would take major changes (and unnecessary changes) to the interpreter to have arr[-0] indicate the last element of the array.
Its mostly for a couple reasons:
Computers work with 0-based numbers
Older programming languages used 0-based indexing since they were low-level and closer to machine code
Newer, Higher-level languages use it for consistency and the same reasons
For more information: https://en.wikipedia.org/wiki/Zero-based_numbering#Usage_in_programming_languages
In many other languages that use 0-based indexes but without negative index implemented as python, to access the last element of a list (array) requires finding the length of the list and subtracting 1 for the last element, like so:
items[len(items) - 1]
In python the len(items) part can simply be omitted with support for negative index, consider:
>>> items = list(range(10))
>>> items[len(items) - 1]
9
>>> items[-1]
9
In python: 0 == -0, so x[0] == x[-0].
Why is sequence indexing zero based instead of one based? It is a choice the language designer should do. Most languages I know of use 0 based indexing. Xpath uses 1 based for selection.
Using negative indexing is also a convention for the language. Not sure why it was chosen, but it allows for circling or looping the sequence by simple addition (subtraction) on the index.
Suppose you have a 10x10 numpy array of intensity values extracted from an image. The exact numbers do not matter right now. I would like to take this matrix, and generate vertices for a graph using only the vertex locations of the upper half of the matrix. More specifically, if our matrix dimensions are defined as (MxN), we could possibly write something like this:
for x in range(M1,M2,M3...M10):
for y in range(N1,N2,N3...N10):
if (x-y) >=0:
graph.addVertex(x,y)
The graph class definition and addVertex definition are NOT important to me as I already have them written. I am only concerned about a method in which I can only consider vertices which are above the diagonal. Open to any and all suggestions, my suggestion above is merely a starting point that may possibly be helpful. Thanks!
EDIT: SOLUTION
Sorry if my clarity issues were atrocious, as I'm somewhat new to coding in Python, but this is the solution to my issue:
g=Graph()
L=4
outerindex=np.arange(L**2*L**2).reshape((L**2,L**2))
outerindex=np.triu(outerindex,k=0)
for i in range(len(outerindex)):
if outerindex.any()>0:
g.addVertex(i)
In this manner, when adding vertices to our newly formed graph, the only new vertices formed will be those that reside in locations above the main diagonal.
I think what you want is something like this:
import numpy as np
a = np.arange(16).reshape((4,4))
print a
for i in range(4):
for j in range(i, 4):
print a[i,j],
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]
# [12 13 14 15]]
# 0 1 2 3 5 6 7 10 11 15
That is, the key point here is to make the index of the inner loop dependent on the outer loop.
If you don't want to include the diagonal, use the inner loop with range(i+1,4).
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.