Filtering a subgraph in graph-tool - python

This is a ridiculously basic question about graph-tool which should be trivial to figure out how to solve using the documentation, but I'm spinning in circles. I don't doubt that documentation comprehensive, but it certainly isn't making this easy.
GOAL: Given a graph G, extract the induced subgraph based on a list of vertices of G.
I know I should be doing this with a GraphView somehow. I get that. I also understand that I need to make a vertex PropertyMap for this. But what exactly should I be creating?
The documentation is very lacking here. For example, the page on PropertyMaps says that each PropertyMap can be of a certain type, but I haven't figured out what that means. What do the types represent? When would I want to use one type over another? Given apparently how important PropertyMaps are to the efficient usage of graph-tool, I'm a little bewildered at how unclear the docs are.
For this problem, I get the vague sense that I need to use the Boolean type, because maybe I want to set the vertices I want in the subgraph to "true" while the vertices I don't want in the subgraph to "false." But does that mean the PropertyMap I create needs to have the same length as the number of nodes in my original graph G? Or can I just provide a list of nodes and somehow let it be understood that those are the only ones to be set to True?

You are right. You have to use GraphView. In the following example a induced subgraph with vertices 0, 1, 3 are created from a complete graph with 5 vertices
from graph_tool import GraphView, generation
g = generation.complete_graph(5)
# select some vertices
vfilt = g.new_vertex_property('bool');
vfilt[0] = True
vfilt[1] = True
vfilt[3] = True
sub = GraphView(g, vfilt)
print [(g.vertex_index[e.source()], g.vertex_index[e.target()])
for e in sub.edges()]
Output
[(0, 1), (0, 3), (1, 3)]

Related

Networkx tools in Python

Hi i am still quite new to networkx python.
does anyone know how i can get all of the intersection?
Intersection nodes are the red circled. This is for undirected Graph, I would also very keen to know if there is also a way to get intersection for directed graph
Once i got the intersection, I also would like to get the start and the end node. the reason is I wanted to put different group to the path (from start node to end node)
Can I achieve this result too :<
If with intersections you mean nodes with more than two neighbors than you can use that exact logic like
intersections = [node for node in G.nodes() if len(list(G.neighbors(node)))>2]
H = G.copy()
H.remove_nodes_from(intersections)
components = nx.connected_components(H)
For "spotting" intersection you can issue them an attribute like this:
intersections_subgraph = nx.subgraph(G, intersetions)
nx.set_node_attributes(intersections_subgraph, True, 'intersection')
Adding intersections back into the sections should works by looping through the intersections neighbors and adding the intersection to the section the neighbors are in.
For sorting I would loop through the section to find a start point (only one edge/neighbor) and then move from node to node. Note that sets are always unordered so you will have to switch over to an ordered structure like a list.
Ill leave implementing those to you

How to create two dimensional set objects under pyomo.environ module

I tried to create a LP model by using pyomo.environ. However, I'm having a hard time on creating sets. For my problem, I have to create two sets. One set is from a bunch of nodes, and the other one is from several arcs between nodes. I create a network by using Networkx to store my nodes and arcs.
The node data is saved like (Longitude, Latitude) in tuple form. The arcs are saved as (nodeA, nodeB), where nodeA and nodeB are both coordinates in tuple.
So, a node is something like:
(-97.97516252657978, 30.342243012086083)
And, an arc is something like:
((-97.97516252657978, 30.342243012086083),
(-97.976196300350608, 30.34247219922803))
The way I tried to create a set is as following:
# import pyomo.envrion as pe
# create a model m
m = pe.ConcreteModel()
# network is an object I created by Networkx module
m.node_set = pe.Set(initialize= self.network.nodes())
m.arc_set = pe.Set(initialize= self.network.edges())
However, I kept getting an error message on arc_set.
ValueError: The value=(-97.97516252657978, 30.342243012086083,
-97.976196300350608, 30.34247219922803) does not have dimension=2,
which is needed for set=arc_set
I found it's weird that somehow my arc_set turned into one tuple instead of two. Then I tried to convert my nodes and arcs into string but still got the error.
Could somebody show me some hint? Or how do delete this bug?
Thanks!
Underneath the hood, Pyomo "flattens" all indexing sets. That is, it removes nested tuples so that each set member is a single tuple of scalar values. This is generally consistent with other algebraic modeling languages, and helps to make sure that we can consistently (and correctly) retrieve component members regardless of how the user attempted to query them.
In your case, Pyomo will want each member of the the arc set as a single 4-member tuple. There is a utility in PyUtilib that you can use to flatten your tuples when constructing the set:
from pyutilib.misc import flatten
m.arc_set = pe.Set(initialize=(tuple(flatten(x)) for x in self.network.edges())
You can also perform some error checking, in this case to make sure that all edges start and end at known nodes:
from pyutilib.misc import flatten
m.node_set = pe.Set( initialize=self.network.nodes() )
m.arc_set = pe.Set(
within=m.node_set*m.node_set,
initialize=(tuple(flatten(x)) for x in self.network.edges() )
This is particularly important for models like this where you are using floating point numbers as indices, and subtle round-off errors can produce indices that are nearly the same but not mathematically equal.
There has been some discussion among the developers to support both structured and flattened indices, but we have not quite reached consensus on how to best support it in a backwards compatible manner.

How do I find the number of vertices in a graph created by iGraph in python?

I'm writing a function that receives a graph as input.
The very first thing I need to do is determine the order of the graph (that is, the number of vertices in the graph).
I mean, I could use g.summary() (which returns a string that includes the number of vertices), but then I'd have parse the string to get at the number of vertices -- and that's just nasty.
To get the number of edges I'm using len(g.get_edgelist()), which works. But there is no g.get_vertexlist(), so I can't use the same method.
Surely there is an easy way to do this that doesn't involve parsing strings.
g.vcount() is a dedicated function in igraph that returns the number of vertices. Similarly, g.ecount() returns the number of edges, and it is way faster than len(g.get_edgelist()) as it does not have to construct the full edge list in advance.
As some functions in igraph have been renamed in meantime, I found the answers here out of date. What the docs suggest now is calling
gorder(g)
which works for me. Analogy for ecount is
gsize(g) # vcount(g) still works, but not g.vcount()
It's useful to note that help pages are cleverly redirected, so
?vcount
brings you to gorder docs etc.
g.vs should return the sequence of vertices as an igraph.VertexSeq object:
>>> from igraph import Graph
>>> g = Graph.Formula("A-B")
>>> g.vs["name"]
['A', 'B']
>>> len(g.vs)
2
>>> g.vcount()
2
Edit: As #Tamas mentions below, g.vcount() will also return the number of vertices. Example edited to account for this.

How to get edge indices from selection?

Is there's a way to get the indices properly from a Pymel/Maya API function?
I know Pymel has a function called getEdges() however according to their docs this get's them from the selected face, however I just need them from the selected edges.
Is this possible?
While your answer will work theodox, I did find a much simpler resolution, after some serious digging!
Turns out, hiding and not very well documented, was a function ironically called indices(), I did search for this but nothing came up in the docs.
Pymel
selection[0].indices()[0]
The above will give us the integer of the selected edge. Simple and elegant!
Do you mean you just the expanded list of selected edges? That's just FilterExpand -sm 32 or cmds.filterExpand(sm=32) or pm.filterExpand(sm=32) on an edge selection. Those commands are always strings, you grab the indices out them with a regular expression:
# where objs is a list of edges, for example cmds.ls(sl=True) on an edge selection
cList = "".join(cmds.filterExpand( *objs, sm=32))
outList = set(map ( int, re.findall('\[([0-9]+)\]', cList ) ) )
which will give you a set containing the integer indices of the edges (I use sets so its easy to do things like find edges common to two groups without for loops or if tests)

Find all vertices with the same edge attribute

I would like to find all vertices (vertex id's) sharing the same edge attribute (so there can be tons of vertices like this) by using Igraph. This would be very convenient when I want to find all "villages" (the vertices of my graph) on a "road", let's say "Route 69" (an edge attribute).
Is there a simple way in Igraph to do this? Maybe I've overcomplicated it.
Actually what I need is the opposite of: g.es.select(_within=g.vs[2:5]) or
>>> men = g.vs.select(gender="m")
>>> women = g.vs.select(gender="f")
>>> g.es.select(_between=(men, women))
because I know the edge attribute but I don't know the vertices.
I will select the edge and hope that it will return all related vertices.
Maybe I'm only tired now, but I don't find my way around this problem. I appreciate if somebody helps me out with the right way. Or maybe there is a method I miss in tutorial and documentation. It smells like there is a very simple method to this. Thank you in advance for any advice!
First, select all the edges on Route 69:
edges = g.es.select(name="Route69")
Then iterate through the selected edges and collect the endpoints of the vertices:
vertices = set()
for edge in edges:
vertices.update(edge.tuple)
This will give you a set containing the vertex IDs of all the vertices that are incident on at least one edge with name Route69. If you need a VertexSeq, you can simply do this:
vertices = g.vs[sorted(vertices)]
You should be able to do the following:
r69edges = g.es.select(name_eq='Route69') #Select all edges where name=='Route69'
sg = r69edges.subgraph() #Create a subgraph containing only the selected edges (and attached vertices)
village_verts = sg.vs.select(name_eq='villages') #Select the vertices where name=='villages'
This assumes that 'villages' and 'Route69' are stored in an attribute called 'name' on the vertices and edges... Adjust appropriately to match your attributes.
Of course - you can squash this all into one line if you want:
village_verts = g.es.select(name_eq='Route69').subgraph().vs.select(name_eq='villages')
Not sure if this is the most efficient way (though I'm not seeing any shortcuts in the documentation), but it should get you what you're after.

Categories