python-igraph-0.7.1.post6/0000755000076500000240000000000012534343010015763 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/COPYING0000644000076500000240000004313312453613472017036 0ustar ntamasstaff00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. python-igraph-0.7.1.post6/igraph/0000755000076500000240000000000012534343010017235 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/igraph/__init__.py0000644000076500000240000052665512534342712021401 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """ IGraph library. @undocumented: deprecated, _graphmethod, _add_proxy_methods, _layout_method_wrapper, _3d_version_for """ from __future__ import with_statement __license__ = u""" Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ # pylint: disable-msg=W0401 # W0401: wildcard import from igraph._igraph import * from igraph._igraph import __version__, __build_date__ from igraph.clustering import * from igraph.cut import * from igraph.configuration import Configuration from igraph.drawing import * from igraph.drawing.colors import * from igraph.datatypes import * from igraph.formula import * from igraph.layout import * from igraph.matching import * from igraph.remote.nexus import * from igraph.statistics import * from igraph.summary import * from igraph.utils import * import os import math import gzip import sys import operator from collections import defaultdict from itertools import izip from shutil import copyfileobj from tempfile import mkstemp from warnings import warn def deprecated(message): """Prints a warning message related to the deprecation of some igraph feature.""" warn(message, DeprecationWarning, stacklevel=3) # pylint: disable-msg=E1101 class Graph(GraphBase): """Generic graph. This class is built on top of L{GraphBase}, so the order of the methods in the Epydoc documentation is a little bit obscure: inherited methods come after the ones implemented directly in the subclass. L{Graph} provides many functions that L{GraphBase} does not, mostly because these functions are not speed critical and they were easier to implement in Python than in pure C. An example is the attribute handling in the constructor: the constructor of L{Graph} accepts three dictionaries corresponding to the graph, vertex and edge attributes while the constructor of L{GraphBase} does not. This extension was needed to make L{Graph} serializable through the C{pickle} module. L{Graph} also overrides some functions from L{GraphBase} to provide a more convenient interface; e.g., layout functions return a L{Layout} instance from L{Graph} instead of a list of coordinate pairs. Graphs can also be indexed by strings or pairs of vertex indices or vertex names. When a graph is indexed by a string, the operation translates to the retrieval, creation, modification or deletion of a graph attribute: >>> g = Graph.Full(3) >>> g["name"] = "Triangle graph" >>> g["name"] 'Triangle graph' >>> del g["name"] When a graph is indexed by a pair of vertex indices or names, the graph itself is treated as an adjacency matrix and the corresponding cell of the matrix is returned: >>> g = Graph.Full(3) >>> g.vs["name"] = ["A", "B", "C"] >>> g[1, 2] 1 >>> g["A", "B"] 1 >>> g["A", "B"] = 0 >>> g.ecount() 2 Assigning values different from zero or one to the adjacency matrix will be translated to one, unless the graph is weighted, in which case the numbers will be treated as weights: >>> g.is_weighted() False >>> g["A", "B"] = 2 >>> g["A", "B"] 1 >>> g.es["weight"] = 1.0 >>> g.is_weighted() True >>> g["A", "B"] = 2 >>> g["A", "B"] 2 >>> g.es["weight"] [1.0, 1.0, 2] """ # Some useful aliases omega = GraphBase.clique_number alpha = GraphBase.independence_number shell_index = GraphBase.coreness cut_vertices = GraphBase.articulation_points blocks = GraphBase.biconnected_components evcent = GraphBase.eigenvector_centrality vertex_disjoint_paths = GraphBase.vertex_connectivity edge_disjoint_paths = GraphBase.edge_connectivity cohesion = GraphBase.vertex_connectivity adhesion = GraphBase.edge_connectivity # Compatibility aliases shortest_paths_dijkstra = GraphBase.shortest_paths subgraph = GraphBase.induced_subgraph def __init__(self, *args, **kwds): """__init__(n=0, edges=None, directed=False, graph_attrs=None, vertex_attrs=None, edge_attrs=None) Constructs a graph from scratch. @keyword n: the number of vertices. Can be omitted, the default is zero. Note that if the edge list contains vertices with indexes larger than or equal to M{m}, then the number of vertices will be adjusted accordingly. @keyword edges: the edge list where every list item is a pair of integers. If any of the integers is larger than M{n-1}, the number of vertices is adjusted accordingly. C{None} means no edges. @keyword directed: whether the graph should be directed @keyword graph_attrs: the attributes of the graph as a dictionary. @keyword vertex_attrs: the attributes of the vertices as a dictionary. Every dictionary value must be an iterable with exactly M{n} items. @keyword edge_attrs: the attributes of the edges as a dictionary. Every dictionary value must be an iterable with exactly M{m} items where M{m} is the number of edges. """ # Set up default values for the parameters. This should match the order # in *args kwd_order = ["n", "edges", "directed", "graph_attrs", "vertex_attrs", \ "edge_attrs"] params = [0, [], False, {}, {}, {}] # Is there any keyword argument in kwds that we don't know? If so, # freak out. unknown_kwds = set(kwds.keys()) unknown_kwds.difference_update(kwd_order) if unknown_kwds: raise TypeError("{0}.__init__ got an unexpected keyword argument {1!r}".format( self.__class__.__name__, unknown_kwds.pop() )) # If the first argument is a list, assume that the number of vertices # were omitted args = list(args) if len(args) > 0: if isinstance(args[0], list) or isinstance(args[0], tuple): args.insert(0, params[0]) # Override default parameters from args params[:len(args)] = args # Override default parameters from keywords for idx, k in enumerate(kwd_order): if k in kwds: params[idx] = kwds[k] # Now, translate the params list to argument names nverts, edges, directed, graph_attrs, vertex_attrs, edge_attrs = params # When the number of vertices is None, assume that the user meant zero if nverts is None: nverts = 0 # When 'edges' is None, assume that the user meant an empty list if edges is None: edges = [] # Initialize the graph GraphBase.__init__(self, nverts, edges, directed) # Set the graph attributes for key, value in graph_attrs.iteritems(): if isinstance(key, (int, long)): key = str(key) self[key] = value # Set the vertex attributes for key, value in vertex_attrs.iteritems(): if isinstance(key, (int, long)): key = str(key) self.vs[key] = value # Set the edge attributes for key, value in edge_attrs.iteritems(): if isinstance(key, (int, long)): key = str(key) self.es[key] = value def add_edge(self, source, target, **kwds): """add_edge(source, target, **kwds) Adds a single edge to the graph. Keyword arguments (except the source and target arguments) will be assigned to the edge as attributes. @param source: the source vertex of the edge or its name. @param target: the target vertex of the edge or its name. """ if not kwds: return self.add_edges([(source, target)]) eid = self.ecount() result = self.add_edges([(source, target)]) edge = self.es[eid] for key, value in kwds.iteritems(): edge[key] = value return result def add_edges(self, es): """add_edges(es) Adds some edges to the graph. @param es: the list of edges to be added. Every edge is represented with a tuple containing the vertex IDs or names of the two endpoints. Vertices are enumerated from zero. """ return GraphBase.add_edges(self, es) def add_vertex(self, name=None, **kwds): """add_vertex(name=None, **kwds) Adds a single vertex to the graph. Keyword arguments will be assigned as vertex attributes. Note that C{name} as a keyword argument is treated specially; if a graph has C{name} as a vertex attribute, it allows one to refer to vertices by their names in most places where igraph expects a vertex ID. """ if not kwds and name is None: return self.add_vertices(1) vid = self.vcount() result = self.add_vertices(1) vertex = self.vs[vid] for key, value in kwds.iteritems(): vertex[key] = value if name is not None: vertex["name"] = name return result def add_vertices(self, n): """add_vertices(n) Adds some vertices to the graph. @param n: the number of vertices to be added, or the name of a single vertex to be added, or an iterable of strings, each corresponding to the name of a vertex to be added. Names will be assigned to the C{name} vertex attribute. """ if isinstance(n, basestring): # Adding a single vertex with a name m = self.vcount() result = GraphBase.add_vertices(self, 1) self.vs[m]["name"] = n return result elif hasattr(n, "__iter__"): m = self.vcount() if not hasattr(n, "__len__"): names = list(n) else: names = n result = GraphBase.add_vertices(self, len(names)) self.vs[m:]["name"] = names return result return GraphBase.add_vertices(self, n) def adjacent(self, *args, **kwds): """adjacent(vertex, mode=OUT) Returns the edges a given vertex is incident on. @deprecated: replaced by L{Graph.incident()} since igraph 0.6 """ deprecated("Graph.adjacent() is deprecated since igraph 0.6, please use " "Graph.incident() instead") return self.incident(*args, **kwds) def as_directed(self, *args, **kwds): """as_directed(*args, **kwds) Returns a directed copy of this graph. Arguments are passed on to L{Graph.to_directed()} that is invoked on the copy. """ copy = self.copy() copy.to_directed(*args, **kwds) return copy def as_undirected(self, *args, **kwds): """as_undirected(*args, **kwds) Returns an undirected copy of this graph. Arguments are passed on to L{Graph.to_undirected()} that is invoked on the copy. """ copy = self.copy() copy.to_undirected(*args, **kwds) return copy def delete_edges(self, *args, **kwds): """Deletes some edges from the graph. The set of edges to be deleted is determined by the positional and keyword arguments. If any keyword argument is present, or the first positional argument is callable, an edge sequence is derived by calling L{EdgeSeq.select} with the same positional and keyword arguments. Edges in the derived edge sequence will be removed. Otherwise the first positional argument is considered as follows: - C{None} - deletes all edges - a single integer - deletes the edge with the given ID - a list of integers - deletes the edges denoted by the given IDs - a list of 2-tuples - deletes the edges denoted by the given source-target vertex pairs. When multiple edges are present between a given source-target vertex pair, only one is removed. """ if len(args) == 0 and len(kwds) == 0: raise ValueError("expected at least one argument") if len(kwds)>0 or (hasattr(args[0], "__call__") and \ not isinstance(args[0], EdgeSeq)): edge_seq = self.es(*args, **kwds) else: edge_seq = args[0] return GraphBase.delete_edges(self, edge_seq) def indegree(self, *args, **kwds): """Returns the in-degrees in a list. See L{degree} for possible arguments. """ kwds['mode'] = IN return self.degree(*args, **kwds) def outdegree(self, *args, **kwds): """Returns the out-degrees in a list. See L{degree} for possible arguments. """ kwds['mode'] = OUT return self.degree(*args, **kwds) def all_st_cuts(self, source, target): """\ Returns all the cuts between the source and target vertices in a directed graph. This function lists all edge-cuts between a source and a target vertex. Every cut is listed exactly once. @param source: the source vertex ID @param target: the target vertex ID @return: a list of L{Cut} objects. @newfield ref: Reference @ref: JS Provan and DR Shier: A paradigm for listing (s,t)-cuts in graphs. Algorithmica 15, 351--372, 1996. """ return [Cut(self, cut=cut, partition=part) for cut, part in izip(*GraphBase.all_st_cuts(self, source, target))] def all_st_mincuts(self, source, target, capacity=None): """\ Returns all the mincuts between the source and target vertices in a directed graph. This function lists all minimum edge-cuts between a source and a target vertex. @param source: the source vertex ID @param target: the target vertex ID @param capacity: the edge capacities (weights). If C{None}, all edges have equal weight. May also be an attribute name. @return: a list of L{Cut} objects. @newfield ref: Reference @ref: JS Provan and DR Shier: A paradigm for listing (s,t)-cuts in graphs. Algorithmica 15, 351--372, 1996. """ value, cuts, parts = GraphBase.all_st_mincuts(self, source, target, capacity) return [Cut(self, value, cut=cut, partition=part) for cut, part in izip(cuts, parts)] def biconnected_components(self, return_articulation_points=False): """\ Calculates the biconnected components of the graph. @param return_articulation_points: whether to return the articulation points as well @return: a L{VertexCover} object describing the biconnected components, and optionally the list of articulation points as well """ if return_articulation_points: trees, aps = GraphBase.biconnected_components(self, True) else: trees = GraphBase.biconnected_components(self, False) clusters = [] for tree in trees: cluster = set() for edge in self.es[tree]: cluster.update(edge.tuple) clusters.append(cluster) clustering = VertexCover(self, clusters) if return_articulation_points: return clustering, aps else: return clustering blocks = biconnected_components def cohesive_blocks(self): """cohesive_blocks() Calculates the cohesive block structure of the graph. Cohesive blocking is a method of determining hierarchical subsets of graph vertices based on their structural cohesion (i.e. vertex connectivity). For a given graph G, a subset of its vertices S is said to be maximally k-cohesive if there is no superset of S with vertex connectivity greater than or equal to k. Cohesive blocking is a process through which, given a k-cohesive set of vertices, maximally l-cohesive subsets are recursively identified with l > k. Thus a hierarchy of vertex subsets is obtained in the end, with the entire graph G at its root. @return: an instance of L{CohesiveBlocks}. See the documentation of L{CohesiveBlocks} for more information. @see: L{CohesiveBlocks} """ return CohesiveBlocks(self, *GraphBase.cohesive_blocks(self)) def clusters(self, mode=STRONG): """clusters(mode=STRONG) Calculates the (strong or weak) clusters (connected components) for a given graph. @param mode: must be either C{STRONG} or C{WEAK}, depending on the clusters being sought. Optional, defaults to C{STRONG}. @return: a L{VertexClustering} object""" return VertexClustering(self, GraphBase.clusters(self, mode)) components = clusters def degree_distribution(self, bin_width = 1, *args, **kwds): """degree_distribution(bin_width=1, ...) Calculates the degree distribution of the graph. Unknown keyword arguments are directly passed to L{degree()}. @param bin_width: the bin width of the histogram @return: a histogram representing the degree distribution of the graph. """ result = Histogram(bin_width, self.degree(*args, **kwds)) return result def dyad_census(self, *args, **kwds): """dyad_census() Calculates the dyad census of the graph. Dyad census means classifying each pair of vertices of a directed graph into three categories: mutual (there is an edge from I{a} to I{b} and also from I{b} to I{a}), asymmetric (there is an edge from I{a} to I{b} or from I{b} to I{a} but not the other way round) and null (there is no connection between I{a} and I{b}). @return: a L{DyadCensus} object. @newfield ref: Reference @ref: Holland, P.W. and Leinhardt, S. (1970). A Method for Detecting Structure in Sociometric Data. American Journal of Sociology, 70, 492-513. """ return DyadCensus(GraphBase.dyad_census(self, *args, **kwds)) def get_adjacency(self, type=GET_ADJACENCY_BOTH, attribute=None, \ default=0, eids=False): """Returns the adjacency matrix of a graph. @param type: either C{GET_ADJACENCY_LOWER} (uses the lower triangle of the matrix) or C{GET_ADJACENCY_UPPER} (uses the upper triangle) or C{GET_ADJACENCY_BOTH} (uses both parts). Ignored for directed graphs. @param attribute: if C{None}, returns the ordinary adjacency matrix. When the name of a valid edge attribute is given here, the matrix returned will contain the default value at the places where there is no edge or the value of the given attribute where there is an edge. Multiple edges are not supported, the value written in the matrix in this case will be unpredictable. This parameter is ignored if I{eids} is C{True} @param default: the default value written to the cells in the case of adjacency matrices with attributes. @param eids: specifies whether the edge IDs should be returned in the adjacency matrix. Since zero is a valid edge ID, the cells in the matrix that correspond to unconnected vertex pairs will contain -1 instead of 0 if I{eids} is C{True}. If I{eids} is C{False}, the number of edges will be returned in the matrix for each vertex pair. @return: the adjacency matrix as a L{Matrix}. """ if type != GET_ADJACENCY_LOWER and type != GET_ADJACENCY_UPPER and \ type != GET_ADJACENCY_BOTH: # Maybe it was called with the first argument as the attribute name type, attribute = attribute, type if type is None: type = GET_ADJACENCY_BOTH if eids: result = Matrix(GraphBase.get_adjacency(self, type, eids)) result -= 1 return result if attribute is None: return Matrix(GraphBase.get_adjacency(self, type)) if attribute not in self.es.attribute_names(): raise ValueError("Attribute does not exist") data = [[default] * self.vcount() for _ in xrange(self.vcount())] if self.is_directed(): for edge in self.es: data[edge.source][edge.target] = edge[attribute] return Matrix(data) if type == GET_ADJACENCY_BOTH: for edge in self.es: source, target = edge.tuple data[source][target] = edge[attribute] data[target][source] = edge[attribute] elif type == GET_ADJACENCY_UPPER: for edge in self.es: data[min(edge.tuple)][max(edge.tuple)] = edge[attribute] else: for edge in self.es: data[max(edge.tuple)][min(edge.tuple)] = edge[attribute] return Matrix(data) def get_adjlist(self, mode=OUT): """get_adjlist(mode=OUT) Returns the adjacency list representation of the graph. The adjacency list representation is a list of lists. Each item of the outer list belongs to a single vertex of the graph. The inner list contains the neighbors of the given vertex. @param mode: if L{OUT}, returns the successors of the vertex. If L{IN}, returns the predecessors of the vertex. If L{ALL}, both the predecessors and the successors will be returned. Ignored for undirected graphs. """ return [self.neighbors(idx, mode) for idx in xrange(self.vcount())] def get_adjedgelist(self, *args, **kwds): """get_adjedgelist(mode=OUT) Returns the incidence list representation of the graph. @deprecated: replaced by L{Graph.get_inclist()} since igraph 0.6 @see: Graph.get_inclist() """ deprecated("Graph.get_adjedgelist() is deprecated since igraph 0.6, " "please use Graph.get_inclist() instead") return self.get_inclist(*args, **kwds) def get_inclist(self, mode=OUT): """get_inclist(mode=OUT) Returns the incidence list representation of the graph. The incidence list representation is a list of lists. Each item of the outer list belongs to a single vertex of the graph. The inner list contains the IDs of the incident edges of the given vertex. @param mode: if L{OUT}, returns the successors of the vertex. If L{IN}, returns the predecessors of the vertex. If L{ALL}, both the predecessors and the successors will be returned. Ignored for undirected graphs. """ return [self.incident(idx, mode) for idx in xrange(self.vcount())] def gomory_hu_tree(self, capacity=None, flow="flow"): """gomory_hu_tree(capacity=None, flow="flow") Calculates the Gomory-Hu tree of an undirected graph with optional edge capacities. The Gomory-Hu tree is a concise representation of the value of all the maximum flows (or minimum cuts) in a graph. The vertices of the tree correspond exactly to the vertices of the original graph in the same order. Edges of the Gomory-Hu tree are annotated by flow values. The value of the maximum flow (or minimum cut) between an arbitrary (u,v) vertex pair in the original graph is then given by the minimum flow value (i.e. edge annotation) along the shortest path between u and v in the Gomory-Hu tree. @param capacity: the edge capacities (weights). If C{None}, all edges have equal weight. May also be an attribute name. @param flow: the name of the edge attribute in the returned graph in which the flow values will be stored. @return: the Gomory-Hu tree as a L{Graph} object. """ graph, flow_values = GraphBase.gomory_hu_tree(self, capacity) graph.es[flow] = flow_values return graph def is_named(self): """is_named() Returns whether the graph is named, i.e. whether it has a "name" vertex attribute. """ return "name" in self.vertex_attributes() def is_weighted(self): """is_weighted() Returns whether the graph is weighted, i.e. whether it has a "weight" edge attribute. """ return "weight" in self.edge_attributes() def maxflow(self, source, target, capacity=None): """maxflow(source, target, capacity=None) Returns a maximum flow between the given source and target vertices in a graph. A maximum flow from I{source} to I{target} is an assignment of non-negative real numbers to the edges of the graph, satisfying two properties: 1. For each edge, the flow (i.e. the assigned number) is not more than the capacity of the edge (see the I{capacity} argument) 2. For every vertex except the source and the target, the incoming flow is the same as the outgoing flow. The value of the flow is the incoming flow of the target or the outgoing flow of the source (which are equal). The maximum flow is the maximum possible such value. @param capacity: the edge capacities (weights). If C{None}, all edges have equal weight. May also be an attribute name. @return: a L{Flow} object describing the maximum flow """ return Flow(self, *GraphBase.maxflow(self, source, target, capacity)) def mincut(self, source=None, target=None, capacity=None): """mincut(source=None, target=None, capacity=None) Calculates the minimum cut between the given source and target vertices or within the whole graph. The minimum cut is the minimum set of edges that needs to be removed to separate the source and the target (if they are given) or to disconnect the graph (if neither the source nor the target are given). The minimum is calculated using the weights (capacities) of the edges, so the cut with the minimum total capacity is calculated. For undirected graphs and no source and target, the method uses the Stoer-Wagner algorithm. For a given source and target, the method uses the push-relabel algorithm; see the references below. @param source: the source vertex ID. If C{None}, the target must also be C{None} and the calculation will be done for the entire graph (i.e. all possible vertex pairs). @param target: the target vertex ID. If C{None}, the source must also be C{None} and the calculation will be done for the entire graph (i.e. all possible vertex pairs). @param capacity: the edge capacities (weights). If C{None}, all edges have equal weight. May also be an attribute name. @return: a L{Cut} object describing the minimum cut """ return Cut(self, *GraphBase.mincut(self, source, target, capacity)) def st_mincut(self, source, target, capacity=None): """st_mincut(source, target, capacity=None) Calculates the minimum cut between the source and target vertices in a graph. @param source: the source vertex ID @param target: the target vertex ID @param capacity: the capacity of the edges. It must be a list or a valid attribute name or C{None}. In the latter case, every edge will have the same capacity. @return: the value of the minimum cut, the IDs of vertices in the first and second partition, and the IDs of edges in the cut, packed in a 4-tuple """ return Cut(self, *GraphBase.st_mincut(self, source, target, capacity)) def modularity(self, membership, weights=None): """modularity(membership, weights=None) Calculates the modularity score of the graph with respect to a given clustering. The modularity of a graph w.r.t. some division measures how good the division is, or how separated are the different vertex types from each other. It's defined as M{Q=1/(2m)*sum(Aij-ki*kj/(2m)delta(ci,cj),i,j)}. M{m} is the number of edges, M{Aij} is the element of the M{A} adjacency matrix in row M{i} and column M{j}, M{ki} is the degree of node M{i}, M{kj} is the degree of node M{j}, and M{Ci} and C{cj} are the types of the two vertices (M{i} and M{j}). M{delta(x,y)} is one iff M{x=y}, 0 otherwise. If edge weights are given, the definition of modularity is modified as follows: M{Aij} becomes the weight of the corresponding edge, M{ki} is the total weight of edges adjacent to vertex M{i}, M{kj} is the total weight of edges adjacent to vertex M{j} and M{m} is the total edge weight in the graph. @param membership: a membership list or a L{VertexClustering} object @param weights: optional edge weights or C{None} if all edges are weighed equally. Attribute names are also allowed. @return: the modularity score @newfield ref: Reference @ref: MEJ Newman and M Girvan: Finding and evaluating community structure in networks. Phys Rev E 69 026113, 2004. """ if isinstance(membership, VertexClustering): if membership.graph != self: raise ValueError("clustering object belongs to another graph") return GraphBase.modularity(self, membership.membership, weights) else: return GraphBase.modularity(self, membership, weights) def path_length_hist(self, directed=True): """path_length_hist(directed=True) Returns the path length histogram of the graph @param directed: whether to consider directed paths. Ignored for undirected graphs. @return: a L{Histogram} object. The object will also have an C{unconnected} attribute that stores the number of unconnected vertex pairs (where the second vertex can not be reached from the first one). The latter one will be of type long (and not a simple integer), since this can be I{very} large. """ data, unconn = GraphBase.path_length_hist(self, directed) hist = Histogram(bin_width=1) for i, length in enumerate(data): hist.add(i+1, length) hist.unconnected = long(unconn) return hist def pagerank(self, vertices=None, directed=True, damping=0.85, weights=None, arpack_options=None, implementation="prpack", niter=1000, eps=0.001): """Calculates the Google PageRank values of a graph. @param vertices: the indices of the vertices being queried. C{None} means all of the vertices. @param directed: whether to consider directed paths. @param damping: the damping factor. M{1-damping} is the PageRank value for nodes with no incoming links. It is also the probability of resetting the random walk to a uniform distribution in each step. @param weights: edge weights to be used. Can be a sequence or iterable or even an edge attribute name. @param arpack_options: an L{ARPACKOptions} object used to fine-tune the ARPACK eigenvector calculation. If omitted, the module-level variable called C{arpack_options} is used. This argument is ignored if not the ARPACK implementation is used, see the I{implementation} argument. @param implementation: which implementation to use to solve the PageRank eigenproblem. Possible values are: - C{"prpack"}: use the PRPACK library. This is a new implementation in igraph 0.7 - C{"arpack"}: use the ARPACK library. This implementation was used from version 0.5, until version 0.7. - C{"power"}: use a simple power method. This is the implementation that was used before igraph version 0.5. @param niter: The number of iterations to use in the power method implementation. It is ignored in the other implementations @param eps: The power method implementation will consider the calculation as complete if the difference of PageRank values between iterations change less than this value for every node. It is ignored by the other implementations. @return: a list with the Google PageRank values of the specified vertices.""" if arpack_options is None: arpack_options = _igraph.arpack_options return self.personalized_pagerank(vertices, directed, damping, None,\ None, weights, arpack_options, \ implementation, niter, eps) def spanning_tree(self, weights=None, return_tree=True): """Calculates a minimum spanning tree for a graph. @param weights: a vector containing weights for every edge in the graph. C{None} means that the graph is unweighted. @param return_tree: whether to return the minimum spanning tree (when C{return_tree} is C{True}) or to return the IDs of the edges in the minimum spanning tree instead (when C{return_tree} is C{False}). The default is C{True} for historical reasons as this argument was introduced in igraph 0.6. @return: the spanning tree as a L{Graph} object if C{return_tree} is C{True} or the IDs of the edges that constitute the spanning tree if C{return_tree} is C{False}. @newfield ref: Reference @ref: Prim, R.C.: I{Shortest connection networks and some generalizations}. Bell System Technical Journal 36:1389-1401, 1957. """ result = GraphBase._spanning_tree(self, weights) if return_tree: return self.subgraph_edges(result, delete_vertices=False) return result def transitivity_avglocal_undirected(self, mode="nan", weights=None): """Calculates the average of the vertex transitivities of the graph. In the unweighted case, the transitivity measures the probability that two neighbors of a vertex are connected. In case of the average local transitivity, this probability is calculated for each vertex and then the average is taken. Vertices with less than two neighbors require special treatment, they will either be left out from the calculation or they will be considered as having zero transitivity, depending on the I{mode} parameter. The calculation is slightly more involved for weighted graphs; in this case, weights are taken into account according to the formula of Barrat et al (see the references). Note that this measure is different from the global transitivity measure (see L{transitivity_undirected()}) as it simply takes the average local transitivity across the whole network. @param mode: defines how to treat vertices with degree less than two. If C{TRANSITIVITY_ZERO} or C{"zero"}, these vertices will have zero transitivity. If C{TRANSITIVITY_NAN} or C{"nan"}, these vertices will be excluded from the average. @param weights: edge weights to be used. Can be a sequence or iterable or even an edge attribute name. @see: L{transitivity_undirected()}, L{transitivity_local_undirected()} @newfield ref: Reference @ref: Watts DJ and Strogatz S: I{Collective dynamics of small-world networks}. Nature 393(6884):440-442, 1998. @ref: Barrat A, Barthelemy M, Pastor-Satorras R and Vespignani A: I{The architecture of complex weighted networks}. PNAS 101, 3747 (2004). U{http://arxiv.org/abs/cond-mat/0311416}. """ if weights is None: return GraphBase.transitivity_avglocal_undirected(self, mode) xs = self.transitivity_local_undirected(mode=mode, weights=weights) return sum(xs) / float(len(xs)) def triad_census(self, *args, **kwds): """triad_census() Calculates the triad census of the graph. @return: a L{TriadCensus} object. @newfield ref: Reference @ref: Davis, J.A. and Leinhardt, S. (1972). The Structure of Positive Interpersonal Relations in Small Groups. In: J. Berger (Ed.), Sociological Theories in Progress, Volume 2, 218-251. Boston: Houghton Mifflin. """ return TriadCensus(GraphBase.triad_census(self, *args, **kwds)) # Automorphisms def count_automorphisms_vf2(self, color=None, edge_color=None, node_compat_fn=None, edge_compat_fn=None): """Returns the number of automorphisms of the graph. This function simply calls C{count_isomorphisms_vf2} with the graph itself. See C{count_isomorphisms_vf2} for an explanation of the parameters. @return: the number of automorphisms of the graph @see: Graph.count_isomorphisms_vf2 """ return self.count_isomorphisms_vf2(self, color1=color, color2=color, edge_color1=edge_color, edge_color2=edge_color, node_compat_fn=node_compat_fn, edge_compat_fn=edge_compat_fn) def get_automorphisms_vf2(self, color=None, edge_color=None, node_compat_fn=None, edge_compat_fn=None): """Returns all the automorphisms of the graph This function simply calls C{get_isomorphisms_vf2} with the graph itself. See C{get_isomorphisms_vf2} for an explanation of the parameters. @return: a list of lists, each item containing a possible mapping of the graph vertices to itself according to the automorphism @see: Graph.get_isomorphisms_vf2 """ return self.get_isomorphisms_vf2(self, color1=color, color2=color, edge_color1=edge_color, edge_color2=edge_color, node_compat_fn=node_compat_fn, edge_compat_fn=edge_compat_fn) # Various clustering algorithms -- mostly wrappers around GraphBase def community_fastgreedy(self, weights=None): """Community structure based on the greedy optimization of modularity. This algorithm merges individual nodes into communities in a way that greedily maximizes the modularity score of the graph. It can be proven that if no merge can increase the current modularity score, the algorithm can be stopped since no further increase can be achieved. This algorithm is said to run almost in linear time on sparse graphs. @param weights: edge attribute name or a list containing edge weights @return: an appropriate L{VertexDendrogram} object. @newfield ref: Reference @ref: A Clauset, MEJ Newman and C Moore: Finding community structure in very large networks. Phys Rev E 70, 066111 (2004). """ merges, qs = GraphBase.community_fastgreedy(self, weights) # qs may be shorter than |V|-1 if we are left with a few separated # communities in the end; take this into account diff = self.vcount() - len(qs) qs.reverse() if qs: optimal_count = qs.index(max(qs)) + diff + 1 else: optimal_count = diff return VertexDendrogram(self, merges, optimal_count, modularity_params=dict(weights=weights)) def community_infomap(self, edge_weights=None, vertex_weights=None, trials=10): """Finds the community structure of the network according to the Infomap method of Martin Rosvall and Carl T. Bergstrom. @param edge_weights: name of an edge attribute or a list containing edge weights. @param vertex_weights: name of an vertex attribute or a list containing vertex weights. @param trials: the number of attempts to partition the network. @return: an appropriate L{VertexClustering} object with an extra attribute called C{codelength} that stores the code length determined by the algorithm. @newfield ref: Reference @ref: M. Rosvall and C. T. Bergstrom: Maps of information flow reveal community structure in complex networks, PNAS 105, 1118 (2008). U{http://dx.doi.org/10.1073/pnas.0706851105}, U{http://arxiv.org/abs/0707.0609}. @ref: M. Rosvall, D. Axelsson, and C. T. Bergstrom: The map equation, Eur. Phys. J. Special Topics 178, 13 (2009). U{http://dx.doi.org/10.1140/epjst/e2010-01179-1}, U{http://arxiv.org/abs/0906.1405}. """ membership, codelength = \ GraphBase.community_infomap(self, edge_weights, vertex_weights, trials) return VertexClustering(self, membership, \ params={"codelength": codelength}, \ modularity_params={"weights": edge_weights} ) def community_leading_eigenvector_naive(self, clusters = None, \ return_merges = False): """community_leading_eigenvector_naive(clusters=None, return_merges=False) A naive implementation of Newman's eigenvector community structure detection. This function splits the network into two components according to the leading eigenvector of the modularity matrix and then recursively takes the given number of steps by splitting the communities as individual networks. This is not the correct way, however, see the reference for explanation. Consider using the correct L{community_leading_eigenvector} method instead. @param clusters: the desired number of communities. If C{None}, the algorithm tries to do as many splits as possible. Note that the algorithm won't split a community further if the signs of the leading eigenvector are all the same, so the actual number of discovered communities can be less than the desired one. @param return_merges: whether the returned object should be a dendrogram instead of a single clustering. @return: an appropriate L{VertexClustering} or L{VertexDendrogram} object. @newfield ref: Reference @ref: MEJ Newman: Finding community structure in networks using the eigenvectors of matrices, arXiv:physics/0605087""" if clusters is None: clusters = -1 cl, merges, q = GraphBase.community_leading_eigenvector_naive(self, \ clusters, return_merges) if merges is None: return VertexClustering(self, cl, modularity = q) else: return VertexDendrogram(self, merges, safemax(cl)+1) def community_leading_eigenvector(self, clusters=None, weights=None, \ arpack_options=None): """community_leading_eigenvector(clusters=None, weights=None, arpack_options=None) Newman's leading eigenvector method for detecting community structure. This is the proper implementation of the recursive, divisive algorithm: each split is done by maximizing the modularity regarding the original network. @param clusters: the desired number of communities. If C{None}, the algorithm tries to do as many splits as possible. Note that the algorithm won't split a community further if the signs of the leading eigenvector are all the same, so the actual number of discovered communities can be less than the desired one. @param weights: name of an edge attribute or a list containing edge weights. @param arpack_options: an L{ARPACKOptions} object used to fine-tune the ARPACK eigenvector calculation. If omitted, the module-level variable called C{arpack_options} is used. @return: an appropriate L{VertexClustering} object. @newfield ref: Reference @ref: MEJ Newman: Finding community structure in networks using the eigenvectors of matrices, arXiv:physics/0605087""" if clusters is None: clusters = -1 kwds = dict(weights=weights) if arpack_options is not None: kwds["arpack_options"] = arpack_options membership, _, q = GraphBase.community_leading_eigenvector(self, clusters, **kwds) return VertexClustering(self, membership, modularity = q) def community_label_propagation(self, weights = None, initial = None, \ fixed = None): """community_label_propagation(weights=None, initial=None, fixed=None) Finds the community structure of the graph according to the label propagation method of Raghavan et al. Initially, each vertex is assigned a different label. After that, each vertex chooses the dominant label in its neighbourhood in each iteration. Ties are broken randomly and the order in which the vertices are updated is randomized before every iteration. The algorithm ends when vertices reach a consensus. Note that since ties are broken randomly, there is no guarantee that the algorithm returns the same community structure after each run. In fact, they frequently differ. See the paper of Raghavan et al on how to come up with an aggregated community structure. @param weights: name of an edge attribute or a list containing edge weights @param initial: name of a vertex attribute or a list containing the initial vertex labels. Labels are identified by integers from zero to M{n-1} where M{n} is the number of vertices. Negative numbers may also be present in this vector, they represent unlabeled vertices. @param fixed: a list of booleans for each vertex. C{True} corresponds to vertices whose labeling should not change during the algorithm. It only makes sense if initial labels are also given. Unlabeled vertices cannot be fixed. @return: an appropriate L{VertexClustering} object. @newfield ref: Reference @ref: Raghavan, U.N. and Albert, R. and Kumara, S. Near linear time algorithm to detect community structures in large-scale networks. Phys Rev E 76:036106, 2007. U{http://arxiv.org/abs/0709.2938}. """ if isinstance(fixed, basestring): fixed = [bool(o) for o in g.vs[fixed]] cl = GraphBase.community_label_propagation(self, \ weights, initial, fixed) return VertexClustering(self, cl, modularity_params=dict(weights=weights)) def community_multilevel(self, weights=None, return_levels=False): """Community structure based on the multilevel algorithm of Blondel et al. This is a bottom-up algorithm: initially every vertex belongs to a separate community, and vertices are moved between communities iteratively in a way that maximizes the vertices' local contribution to the overall modularity score. When a consensus is reached (i.e. no single move would increase the modularity score), every community in the original graph is shrank to a single vertex (while keeping the total weight of the adjacent edges) and the process continues on the next level. The algorithm stops when it is not possible to increase the modularity any more after shrinking the communities to vertices. This algorithm is said to run almost in linear time on sparse graphs. @param weights: edge attribute name or a list containing edge weights @param return_levels: if C{True}, the communities at each level are returned in a list. If C{False}, only the community structure with the best modularity is returned. @return: a list of L{VertexClustering} objects, one corresponding to each level (if C{return_levels} is C{True}), or a L{VertexClustering} corresponding to the best modularity. @newfield ref: Reference @ref: VD Blondel, J-L Guillaume, R Lambiotte and E Lefebvre: Fast unfolding of community hierarchies in large networks, J Stat Mech P10008 (2008), http://arxiv.org/abs/0803.0476 """ if self.is_directed(): raise ValueError("input graph must be undirected") if return_levels: levels, qs = GraphBase.community_multilevel(self, weights, True) result = [] for level, q in zip(levels, qs): result.append(VertexClustering(self, level, q, modularity_params=dict(weights=weights))) else: membership = GraphBase.community_multilevel(self, weights, False) result = VertexClustering(self, membership, modularity_params=dict(weights=weights)) return result def community_optimal_modularity(self, *args, **kwds): """Calculates the optimal modularity score of the graph and the corresponding community structure. This function uses the GNU Linear Programming Kit to solve a large integer optimization problem in order to find the optimal modularity score and the corresponding community structure, therefore it is unlikely to work for graphs larger than a few (less than a hundred) vertices. Consider using one of the heuristic approaches instead if you have such a large graph. @return: the calculated membership vector and the corresponding modularity in a tuple.""" membership, modularity = \ GraphBase.community_optimal_modularity(self, *args, **kwds) return VertexClustering(self, membership, modularity) def community_edge_betweenness(self, clusters=None, directed=True, weights=None): """Community structure based on the betweenness of the edges in the network. The idea is that the betweenness of the edges connecting two communities is typically high, as many of the shortest paths between nodes in separate communities go through them. So we gradually remove the edge with the highest betweenness and recalculate the betweennesses after every removal. This way sooner or later the network falls of to separate components. The result of the clustering will be represented by a dendrogram. @param clusters: the number of clusters we would like to see. This practically defines the "level" where we "cut" the dendrogram to get the membership vector of the vertices. If C{None}, the dendrogram is cut at the level which maximizes the modularity. @param directed: whether the directionality of the edges should be taken into account or not. @param weights: name of an edge attribute or a list containing edge weights. @return: a L{VertexDendrogram} object, initally cut at the maximum modularity or at the desired number of clusters. """ merges, qs = GraphBase.community_edge_betweenness(self, directed, weights) qs.reverse() if clusters is None: if qs: clusters = qs.index(max(qs))+1 else: clusters = 1 return VertexDendrogram(self, merges, clusters, modularity_params=dict(weights=weights)) def community_spinglass(self, *args, **kwds): """community_spinglass(weights=None, spins=25, parupdate=False, start_temp=1, stop_temp=0.01, cool_fact=0.99, update_rule="config", gamma=1, implementation="orig", lambda_=1) Finds the community structure of the graph according to the spinglass community detection method of Reichardt & Bornholdt. @keyword weights: edge weights to be used. Can be a sequence or iterable or even an edge attribute name. @keyword spins: integer, the number of spins to use. This is the upper limit for the number of communities. It is not a problem to supply a (reasonably) big number here, in which case some spin states will be unpopulated. @keyword parupdate: whether to update the spins of the vertices in parallel (synchronously) or not @keyword start_temp: the starting temperature @keyword stop_temp: the stop temperature @keyword cool_fact: cooling factor for the simulated annealing @keyword update_rule: specifies the null model of the simulation. Possible values are C{"config"} (a random graph with the same vertex degrees as the input graph) or C{"simple"} (a random graph with the same number of edges) @keyword gamma: the gamma argument of the algorithm, specifying the balance between the importance of present and missing edges within a community. The default value of 1.0 assigns equal importance to both of them. @keyword implementation: currently igraph contains two implementations of the spinglass community detection algorithm. The faster original implementation is the default. The other implementation is able to take into account negative weights, this can be chosen by setting C{implementation} to C{"neg"} @keyword lambda_: the lambda argument of the algorithm, which specifies the balance between the importance of present and missing negatively weighted edges within a community. Smaller values of lambda lead to communities with less negative intra-connectivity. If the argument is zero, the algorithm reduces to a graph coloring algorithm, using the number of spins as colors. This argument is ignored if the original implementation is used. Note the underscore at the end of the argument name; this is due to the fact that lambda is a reserved keyword in Python. @return: an appropriate L{VertexClustering} object. @newfield ref: Reference @ref: Reichardt J and Bornholdt S: Statistical mechanics of community detection. Phys Rev E 74:016110 (2006). U{http://arxiv.org/abs/cond-mat/0603718}. @ref: Traag VA and Bruggeman J: Community detection in networks with positive and negative links. Phys Rev E 80:036115 (2009). U{http://arxiv.org/abs/0811.2329}. """ membership = GraphBase.community_spinglass(self, *args, **kwds) if "weights" in kwds: modularity_params=dict(weights=kwds["weights"]) else: modularity_params={} return VertexClustering(self, membership, modularity_params=modularity_params) def community_walktrap(self, weights=None, steps=4): """Community detection algorithm of Latapy & Pons, based on random walks. The basic idea of the algorithm is that short random walks tend to stay in the same community. The result of the clustering will be represented as a dendrogram. @param weights: name of an edge attribute or a list containing edge weights @param steps: length of random walks to perform @return: a L{VertexDendrogram} object, initially cut at the maximum modularity. @newfield ref: Reference @ref: Pascal Pons, Matthieu Latapy: Computing communities in large networks using random walks, U{http://arxiv.org/abs/physics/0512106}. """ merges, qs = GraphBase.community_walktrap(self, weights, steps) qs.reverse() if qs: optimal_count = qs.index(max(qs))+1 else: optimal_count = 1 return VertexDendrogram(self, merges, optimal_count, modularity_params=dict(weights=weights)) def k_core(self, *args): """Returns some k-cores of the graph. The method accepts an arbitrary number of arguments representing the desired indices of the M{k}-cores to be returned. The arguments can also be lists or tuples. The result is a single L{Graph} object if an only integer argument was given, otherwise the result is a list of L{Graph} objects representing the desired k-cores in the order the arguments were specified. If no argument is given, returns all M{k}-cores in increasing order of M{k}. """ if len(args) == 0: indices = xrange(self.vcount()) return_single = False else: return_single = True indices = [] for arg in args: try: indices.extend(arg) except: indices.append(arg) if len(indices)>1 or hasattr(args[0], "__iter__"): return_single = False corenesses = self.coreness() result = [] vidxs = xrange(self.vcount()) for idx in indices: core_idxs = [vidx for vidx in vidxs if corenesses[vidx] >= idx] result.append(self.subgraph(core_idxs)) if return_single: return result[0] return result def layout(self, layout=None, *args, **kwds): """Returns the layout of the graph according to a layout algorithm. Parameters and keyword arguments not specified here are passed to the layout algorithm directly. See the documentation of the layout algorithms for the explanation of these parameters. Registered layout names understood by this method are: - C{auto}, C{automatic}: automatic layout (see L{Graph.layout_auto}) - C{bipartite}: bipartite layout (see L{Graph.layout_bipartite}) - C{circle}, C{circular}: circular layout (see L{Graph.layout_circle}) - C{drl}: DrL layout for large graphs (see L{Graph.layout_drl}) - C{drl_3d}: 3D DrL layout for large graphs (see L{Graph.layout_drl}) - C{fr}, C{fruchterman_reingold}: Fruchterman-Reingold layout (see L{Graph.layout_fruchterman_reingold}). - C{fr_3d}, C{fr3d}, C{fruchterman_reingold_3d}: 3D Fruchterman- Reingold layout (see L{Graph.layout_fruchterman_reingold}). - C{grid}: regular grid layout in 2D (see L{Graph.layout_grid}) - C{grid_3d}: regular grid layout in 3D (see L{Graph.layout_grid_3d}) - C{graphopt}: the graphopt algorithm (see L{Graph.layout_graphopt}) - C{kk}, C{kamada_kawai}: Kamada-Kawai layout (see L{Graph.layout_kamada_kawai}) - C{kk_3d}, C{kk3d}, C{kamada_kawai_3d}: 3D Kamada-Kawai layout (see L{Graph.layout_kamada_kawai}) - C{lgl}, C{large}, C{large_graph}: Large Graph Layout (see L{Graph.layout_lgl}) - C{mds}: multidimensional scaling layout (see L{Graph.layout_mds}) - C{random}: random layout (see L{Graph.layout_random}) - C{random_3d}: random 3D layout (see L{Graph.layout_random}) - C{rt}, C{tree}, C{reingold_tilford}: Reingold-Tilford tree layout (see L{Graph.layout_reingold_tilford}) - C{rt_circular}, C{reingold_tilford_circular}: circular Reingold-Tilford tree layout (see L{Graph.layout_reingold_tilford_circular}) - C{sphere}, C{spherical}, C{circle_3d}, C{circular_3d}: spherical layout (see L{Graph.layout_circle}) - C{star}: star layout (see L{Graph.layout_star}) - C{sugiyama}: Sugiyama layout (see L{Graph.layout_sugiyama}) @param layout: the layout to use. This can be one of the registered layout names or a callable which returns either a L{Layout} object or a list of lists containing the coordinates. If C{None}, uses the value of the C{plotting.layout} configuration key. @return: a L{Layout} object. """ if layout is None: layout = config["plotting.layout"] if hasattr(layout, "__call__"): method = layout else: layout = layout.lower() if layout[-3:] == "_3d": kwds["dim"] = 3 layout = layout[:-3] elif layout[-2:] == "3d": kwds["dim"] = 3 layout = layout[:-2] method = getattr(self.__class__, self._layout_mapping[layout]) if not hasattr(method, "__call__"): raise ValueError("layout method must be callable") l = method(self, *args, **kwds) if not isinstance(l, Layout): l = Layout(l) return l def layout_auto(self, *args, **kwds): """Chooses and runs a suitable layout function based on simple topological properties of the graph. This function tries to choose an appropriate layout function for the graph using the following rules: 1. If the graph has an attribute called C{layout}, it will be used. It may either be a L{Layout} instance, a list of coordinate pairs, the name of a layout function, or a callable function which generates the layout when called with the graph as a parameter. 2. Otherwise, if the graph has vertex attributes called C{x} and C{y}, these will be used as coordinates in the layout. When a 3D layout is requested (by setting C{dim} to 3), a vertex attribute named C{z} will also be needed. 3. Otherwise, if the graph is connected and has at most 100 vertices, the Kamada-Kawai layout will be used (see L{Graph.layout_kamada_kawai()}). 4. Otherwise, if the graph has at most 1000 vertices, the Fruchterman-Reingold layout will be used (see L{Graph.layout_fruchterman_reingold()}). 5. If everything else above failed, the DrL layout algorithm will be used (see L{Graph.layout_drl()}). All the arguments of this function except C{dim} are passed on to the chosen layout function (in case we have to call some layout function). @keyword dim: specifies whether we would like to obtain a 2D or a 3D layout. @return: a L{Layout} object. """ if "layout" in self.attributes(): layout = self["layout"] if isinstance(layout, Layout): # Layouts are used intact return layout if isinstance(layout, (list, tuple)): # Lists/tuples are converted to layouts return Layout(layout) if hasattr(layout, "__call__"): # Callables are called return Layout(layout(*args, **kwds)) # Try Graph.layout() return self.layout(layout, *args, **kwds) dim = kwds.get("dim", 2) vattrs = self.vertex_attributes() if "x" in vattrs and "y" in vattrs: if dim == 3 and "z" in vattrs: return Layout(zip(self.vs["x"], self.vs["y"], self.vs["z"])) else: return Layout(zip(self.vs["x"], self.vs["y"])) if self.vcount() <= 100 and self.is_connected(): algo = "kk" elif self.vcount() <= 1000: algo = "fr" else: algo = "drl" return self.layout(algo, *args, **kwds) def layout_grid_fruchterman_reingold(self, *args, **kwds): """layout_grid_fruchterman_reingold(*args, **kwds) Compatibility alias to the Fruchterman-Reingold layout with the grid option turned on. @see: Graph.layout_fruchterman_reingold() """ deprecated("Graph.layout_grid_fruchterman_reingold() is deprecated since "\ "igraph 0.8, please use Graph.layout_fruchterman_reingold(grid=True) instead") kwds["grid"] = True return self.layout_fruchterman_reingold(*args, **kwds) def layout_sugiyama(self, layers=None, weights=None, hgap=1, vgap=1, maxiter=100, return_extended_graph=False): """layout_sugiyama(layers=None, weights=None, hgap=1, vgap=1, maxiter=100, return_extended_graph=False) Places the vertices using a layered Sugiyama layout. This is a layered layout that is most suitable for directed acyclic graphs, although it works on undirected or cyclic graphs as well. Each vertex is assigned to a layer and each layer is placed on a horizontal line. Vertices within the same layer are then permuted using the barycenter heuristic that tries to minimize edge crossings. Dummy vertices will be added on edges that span more than one layer. The returned layout therefore contains more rows than the number of nodes in the original graph; the extra rows correspond to the dummy vertices. @param layers: a vector specifying a non-negative integer layer index for each vertex, or the name of a numeric vertex attribute that contains the layer indices. If C{None}, a layering will be determined automatically. For undirected graphs, a spanning tree will be extracted and vertices will be assigned to layers using a breadth first search from the node with the largest degree. For directed graphs, cycles are broken by reversing the direction of edges in an approximate feedback arc set using the heuristic of Eades, Lin and Smyth, and then using longest path layering to place the vertices in layers. @param weights: edge weights to be used. Can be a sequence or iterable or even an edge attribute name. @param hgap: minimum horizontal gap between vertices in the same layer. @param vgap: vertical gap between layers. The layer index will be multiplied by I{vgap} to obtain the Y coordinate. @param maxiter: maximum number of iterations to take in the crossing reduction step. Increase this if you feel that you are getting too many edge crossings. @param return_extended_graph: specifies that the extended graph with the added dummy vertices should also be returned. When this is C{True}, the result will be a tuple containing the layout and the extended graph. The first |V| nodes of the extended graph will correspond to the nodes of the original graph, the remaining ones are dummy nodes. Plotting the extended graph with the returned layout and hidden dummy nodes will produce a layout that is similar to the original graph, but with the added edge bends. The extended graph also contains an edge attribute called C{_original_eid} which specifies the ID of the edge in the original graph from which the edge of the extended graph was created. @return: the calculated layout, which may (and usually will) have more rows than the number of vertices; the remaining rows correspond to the dummy nodes introduced in the layering step. When C{return_extended_graph} is C{True}, it will also contain the extended graph. @newfield ref: Reference @ref: K Sugiyama, S Tagawa, M Toda: Methods for visual understanding of hierarchical system structures. IEEE Systems, Man and Cybernetics\ 11(2):109-125, 1981. @ref: P Eades, X Lin and WF Smyth: A fast effective heuristic for the feedback arc set problem. Information Processing Letters 47:319-323, 1993. """ if not return_extended_graph: return Layout(GraphBase._layout_sugiyama(self, layers, weights, hgap, vgap, maxiter, return_extended_graph)) layout, extd_graph, extd_to_orig_eids = \ GraphBase._layout_sugiyama(self, layers, weights, hgap, vgap, maxiter, return_extended_graph) extd_graph.es["_original_eid"] = extd_to_orig_eids return Layout(layout), extd_graph def maximum_bipartite_matching(self, types="type", weights=None, eps=None): """Finds a maximum matching in a bipartite graph. A maximum matching is a set of edges such that each vertex is incident on at most one matched edge and the number (or weight) of such edges in the set is as large as possible. @param types: vertex types in a list or the name of a vertex attribute holding vertex types. Types should be denoted by zeros and ones (or C{False} and C{True}) for the two sides of the bipartite graph. If omitted, it defaults to C{type}, which is the default vertex type attribute for bipartite graphs. @param weights: edge weights to be used. Can be a sequence or iterable or even an edge attribute name. @param eps: a small real number used in equality tests in the weighted bipartite matching algorithm. Two real numbers are considered equal in the algorithm if their difference is smaller than this value. This is required to avoid the accumulation of numerical errors. If you pass C{None} here, igraph will try to determine an appropriate value automatically. @return: an instance of L{Matching}.""" if eps is None: eps = -1 matches = GraphBase._maximum_bipartite_matching(self, types, weights, eps) return Matching(self, matches, types=types) ############################################# # Auxiliary I/O functions def write_adjacency(self, f, sep=" ", eol="\n", *args, **kwds): """Writes the adjacency matrix of the graph to the given file All the remaining arguments not mentioned here are passed intact to L{Graph.get_adjacency}. @param f: the name of the file to be written. @param sep: the string that separates the matrix elements in a row @param eol: the string that separates the rows of the matrix. Please note that igraph is able to read back the written adjacency matrix if and only if this is a single newline character """ if isinstance(f, basestring): f = open(f, "w") matrix = self.get_adjacency(*args, **kwds) for row in matrix: f.write(sep.join(map(str, row))) f.write(eol) f.close() @classmethod def Read_Adjacency(klass, f, sep=None, comment_char = "#", attribute=None, *args, **kwds): """Constructs a graph based on an adjacency matrix from the given file Additional positional and keyword arguments not mentioned here are passed intact to L{Graph.Adjacency}. @param f: the name of the file to be read or a file object @param sep: the string that separates the matrix elements in a row. C{None} means an arbitrary sequence of whitespace characters. @param comment_char: lines starting with this string are treated as comments. @param attribute: an edge attribute name where the edge weights are stored in the case of a weighted adjacency matrix. If C{None}, no weights are stored, values larger than 1 are considered as edge multiplicities. @return: the created graph""" if isinstance(f, basestring): f = open(f) matrix, ri, weights = [], 0, {} for line in f: line = line.strip() if len(line) == 0: continue if line.startswith(comment_char): continue row = [float(x) for x in line.split(sep)] matrix.append(row) ri += 1 f.close() if attribute is None: graph=klass.Adjacency(matrix, *args, **kwds) else: kwds["attr"] = attribute graph=klass.Weighted_Adjacency(matrix, *args, **kwds) return graph def write_dimacs(self, f, source=None, target=None, capacity="capacity"): """Writes the graph in DIMACS format to the given file. @param f: the name of the file to be written or a Python file handle. @param source: the source vertex ID. If C{None}, igraph will try to infer it from the C{source} graph attribute. @param target: the target vertex ID. If C{None}, igraph will try to infer it from the C{target} graph attribute. @param capacity: the capacities of the edges in a list or the name of an edge attribute that holds the capacities. If there is no such edge attribute, every edge will have a capacity of 1. """ if source is None: source = self["source"] if target is None: target = self["target"] if isinstance(capacity, basestring) and \ capacity not in self.edge_attributes(): warn("'%s' edge attribute does not exist" % capacity) capacity = None return GraphBase.write_dimacs(self, f, source, target, capacity) def write_graphmlz(self, f, compresslevel=9): """Writes the graph to a zipped GraphML file. The library uses the gzip compression algorithm, so the resulting file can be unzipped with regular gzip uncompression (like C{gunzip} or C{zcat} from Unix command line) or the Python C{gzip} module. Uses a temporary file to store intermediate GraphML data, so make sure you have enough free space to store the unzipped GraphML file as well. @param f: the name of the file to be written. @param compresslevel: the level of compression. 1 is fastest and produces the least compression, and 9 is slowest and produces the most compression.""" from igraph.utils import named_temporary_file with named_temporary_file() as tmpfile: self.write_graphml(tmpfile) outf = gzip.GzipFile(f, "wb", compresslevel) copyfileobj(open(tmpfile, "rb"), outf) outf.close() @classmethod def Read_DIMACS(cls, f, directed=False): """Read_DIMACS(f, directed=False) Reads a graph from a file conforming to the DIMACS minimum-cost flow file format. For the exact definition of the format, see U{http://lpsolve.sourceforge.net/5.5/DIMACS.htm}. Restrictions compared to the official description of the format are as follows: - igraph's DIMACS reader requires only three fields in an arc definition, describing the edge's source and target node and its capacity. - Source vertices are identified by 's' in the FLOW field, target vertices are identified by 't'. - Node indices start from 1. Only a single source and target node is allowed. @param f: the name of the file or a Python file handle @param directed: whether the generated graph should be directed. @return: the generated graph. The indices of the source and target vertices are attached as graph attributes C{source} and C{target}, the edge capacities are stored in the C{capacity} edge attribute. """ graph, source, target, cap = super(Graph, cls).Read_DIMACS(f, directed) graph.es["capacity"] = cap graph["source"] = source graph["target"] = target return graph @classmethod def Read_GraphMLz(cls, f, *params, **kwds): """Read_GraphMLz(f, directed=True, index=0) Reads a graph from a zipped GraphML file. @param f: the name of the file @param index: if the GraphML file contains multiple graphs, specified the one that should be loaded. Graph indices start from zero, so if you want to load the first graph, specify 0 here. @return: the loaded graph object""" from igraph.utils import named_temporary_file with named_temporary_file() as tmpfile: outf = open(tmpfile, "wb") copyfileobj(gzip.GzipFile(f, "rb"), outf) outf.close() return cls.Read_GraphML(tmpfile) def write_pickle(self, fname=None, version=-1): """Saves the graph in Python pickled format @param fname: the name of the file or a stream to save to. If C{None}, saves the graph to a string and returns the string. @param version: pickle protocol version to be used. If -1, uses the highest protocol available @return: C{None} if the graph was saved successfully to the given file, or a string if C{fname} was C{None}. """ import cPickle as pickle if fname is None: return pickle.dumps(self, version) if not hasattr(fname, "write"): file_was_opened = True fname = open(fname, 'wb') else: file_was_opened=False result=pickle.dump(self, fname, version) if file_was_opened: fname.close() return result def write_picklez(self, fname=None, version=-1): """Saves the graph in Python pickled format, compressed with gzip. Saving in this format is a bit slower than saving in a Python pickle without compression, but the final file takes up much less space on the hard drive. @param fname: the name of the file or a stream to save to. @param version: pickle protocol version to be used. If -1, uses the highest protocol available @return: C{None} if the graph was saved successfully to the given file. """ import cPickle as pickle if not hasattr(fname, "write"): file_was_opened = True fname = gzip.open(fname, "wb") elif not isinstance(fname, gzip.GzipFile): file_was_opened = True fname = gzip.GzipFile(mode="wb", fileobj=fname) else: file_Was_opened = False result = pickle.dump(self, fname, version) if file_was_opened: fname.close() return result @classmethod def Read_Pickle(klass, fname=None): """Reads a graph from Python pickled format @param fname: the name of the file, a stream to read from, or a string containing the pickled data. The string is assumed to hold pickled data if it is longer than 40 characters and contains a substring that's peculiar to pickled versions of an C{igraph} Graph object. @return: the created graph object. """ import cPickle as pickle if hasattr(fname, "read"): # Probably a file or a file-like object result = pickle.load(fname) else: fp = None try: fp = open(fname, "rb") except IOError: # No file with the given name, try unpickling directly result = pickle.loads(fname) if fp is not None: result = pickle.load(fp) fp.close() return result @classmethod def Read_Picklez(klass, fname, *args, **kwds): """Reads a graph from compressed Python pickled format, uncompressing it on-the-fly. @param fname: the name of the file or a stream to read from. @return: the created graph object. """ import cPickle as pickle if hasattr(fname, "read"): # Probably a file or a file-like object if isinstance(fname, gzip.GzipFile): result = pickle.load(fname) else: result = pickle.load(gzip.GzipFile(mode="rb", fileobj=fname)) else: result = pickle.load(gzip.open(fname, "rb")) return result @classmethod def Read_Picklez(klass, fname, *args, **kwds): """Reads a graph from compressed Python pickled format, uncompressing it on-the-fly. @param fname: the name of the file or a stream to read from. @return: the created graph object. """ import cPickle as pickle if hasattr(fname, "read"): # Probably a file or a file-like object if isinstance(fname, gzip.GzipFile): result = pickle.load(fname) else: result = pickle.load(gzip.GzipFile(mode="rb", fileobj=fname)) else: result = pickle.load(gzip.open(fname, "rb")) if not isinstance(result, klass): raise TypeError("unpickled object is not a %s" % klass.__name__) return result # pylint: disable-msg=C0301,C0323 # C0301: line too long. # C0323: operator not followed by a space - well, print >>f looks OK def write_svg(self, fname, layout="auto", width=None, height=None, \ labels="label", colors="color", shapes="shape", \ vertex_size=10, edge_colors="color", \ edge_stroke_widths="width", \ font_size=16, *args, **kwds): """Saves the graph as an SVG (Scalable Vector Graphics) file The file will be Inkscape (http://inkscape.org) compatible. In Inkscape, as nodes are rearranged, the edges auto-update. @param fname: the name of the file or a Python file handle @param layout: the layout of the graph. Can be either an explicitly specified layout (using a list of coordinate pairs) or the name of a layout algorithm (which should refer to a method in the L{Graph} object, but without the C{layout_} prefix. @param width: the preferred width in pixels (default: 400) @param height: the preferred height in pixels (default: 400) @param labels: the vertex labels. Either it is the name of a vertex attribute to use, or a list explicitly specifying the labels. It can also be C{None}. @param colors: the vertex colors. Either it is the name of a vertex attribute to use, or a list explicitly specifying the colors. A color can be anything acceptable in an SVG file. @param shapes: the vertex shapes. Either it is the name of a vertex attribute to use, or a list explicitly specifying the shapes as integers. Shape 0 means hidden (nothing is drawn), shape 1 is a circle, shape 2 is a rectangle and shape 3 is a rectangle that automatically sizes to the inner text. @param vertex_size: vertex size in pixels @param edge_colors: the edge colors. Either it is the name of an edge attribute to use, or a list explicitly specifying the colors. A color can be anything acceptable in an SVG file. @param edge_stroke_widths: the stroke widths of the edges. Either it is the name of an edge attribute to use, or a list explicitly specifying the stroke widths. The stroke width can be anything acceptable in an SVG file. @param font_size: font size. If it is a string, it is written into the SVG file as-is (so you can specify anything which is valid as the value of the C{font-size} style). If it is a number, it is interpreted as pixel size and converted to the proper attribute value accordingly. """ if width is None and height is None: width = 400 height = 400 elif width is None: width = height elif height is None: height = width if width <= 0 or height <= 0: raise ValueError("width and height must be positive") if isinstance(layout, str): layout = self.layout(layout, *args, **kwds) if isinstance(labels, str): try: labels = self.vs.get_attribute_values(labels) except KeyError: labels = [x+1 for x in xrange(self.vcount())] elif labels is None: labels = [""] * self.vcount() if isinstance(colors, str): try: colors = self.vs.get_attribute_values(colors) except KeyError: colors = ["red"] * self.vcount() if isinstance(shapes, str): try: shapes = self.vs.get_attribute_values(shapes) except KeyError: shapes = [1] * self.vcount() if isinstance(edge_colors, str): try: edge_colors = self.es.get_attribute_values(edge_colors) except KeyError: edge_colors = ["black"] * self.ecount() if isinstance(edge_stroke_widths, str): try: edge_stroke_widths = self.es.get_attribute_values(edge_stroke_widths) except KeyError: edge_stroke_widths = [2] * self.ecount() if not isinstance(font_size, str): font_size = "%spx" % str(font_size) else: if ";" in font_size: raise ValueError("font size can't contain a semicolon") vcount = self.vcount() labels.extend(str(i+1) for i in xrange(len(labels), vcount)) colors.extend(["red"] * (vcount - len(colors))) if isinstance(fname, basestring): f = open(fname, "w") our_file = True else: f = fname our_file = False bbox = BoundingBox(layout.bounding_box()) sizes = [width-2*vertex_size, height-2*vertex_size] w, h = bbox.width, bbox.height ratios = [] if w == 0: ratios.append(1.0) else: ratios.append(sizes[0] / w) if h == 0: ratios.append(1.0) else: ratios.append(sizes[1] / h) layout = [[(row[0] - bbox.left) * ratios[0] + vertex_size, \ (row[1] - bbox.top) * ratios[1] + vertex_size] \ for row in layout] directed = self.is_directed() print >> f, '' print >> f, '' print >> f print >> f, '> f, 'width="{0}px" height="{1}px">'.format(width, height), edge_color_dict = {} print >> f, '' for e_col in set(edge_colors): if e_col == "#000000": marker_index = "" else: marker_index = str(len(edge_color_dict)) # Print an arrow marker for each possible line color # This is a copy of Inkscape's standard Arrow 2 marker print >> f, '> f, ' inkscape:stockid="Arrow2Lend{0}"'.format(marker_index) print >> f, ' orient="auto"' print >> f, ' refY="0.0"' print >> f, ' refX="0.0"' print >> f, ' id="Arrow2Lend{0}"'.format(marker_index) print >> f, ' style="overflow:visible;">' print >> f, ' > f, ' id="pathArrow{0}"'.format(marker_index) print >> f, ' style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;fill:{0}"'.format(e_col) print >> f, ' d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "' print >> f, ' transform="scale(1.1) rotate(180) translate(1,0)" />' print >> f, '' edge_color_dict[e_col] = "Arrow2Lend{0}".format(marker_index) print >> f, '' print >> f, '' for eidx, edge in enumerate(self.es): vidxs = edge.tuple x1 = layout[vidxs[0]][0] y1 = layout[vidxs[0]][1] x2 = layout[vidxs[1]][0] y2 = layout[vidxs[1]][1] angle = math.atan2(y2 - y1, x2 - x1) x2 = x2 - vertex_size * math.cos(angle) y2 = y2 - vertex_size * math.sin(angle) print >> f, '> f, ' style="fill:none;stroke:{0};stroke-width:{2};stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none{1}"'\ .format(edge_colors[eidx], ";marker-end:url(#{0})".\ format(edge_color_dict[edge_colors[eidx]]) \ if directed else "", edge_stroke_widths[eidx]) print >> f, ' d="M {0},{1} {2},{3}"'.format(x1, y1, x2, y2) print >> f, ' id="path{0}"'.format(eidx) print >> f, ' inkscape:connector-type="polyline"' print >> f, ' inkscape:connector-curvature="0"' print >> f, ' inkscape:connection-start="#g{0}"'.format(edge.source) print >> f, ' inkscape:connection-start-point="d4"' print >> f, ' inkscape:connection-end="#g{0}"'.format(edge.target) print >> f, ' inkscape:connection-end-point="d4" />' print >> f, " " print >> f print >> f, ' ' print >> f, ' ' if any(x == 3 for x in shapes): # Only import tkFont if we really need it. Unfortunately, this will # flash up an unneccesary Tk window in some cases import tkFont import Tkinter as tk # This allows us to dynamically size the width of the nodes font = tkFont.Font(root=tk.Tk(), font=("Sans", font_size, tkFont.NORMAL)) for vidx in range(self.vcount()): print >> f, ' '.\ format(vidx, layout[vidx][0], layout[vidx][1]) if shapes[vidx] == 1: # Undocumented feature: can handle two colors but only for circles c = str(colors[vidx]) if " " in c: c = c.split(" ") vs = str(vertex_size) print >> f, ' '.format(vs, c[0]) print >> f, ' '.format(vs, c[1]) print >> f, ' '\ .format(vs) else: print >> f, ' '.\ format(str(vertex_size), str(colors[vidx])) elif shapes[vidx] == 2: print >> f, ' '.\ format(vertex_size, vertex_size * 2, vidx, colors[vidx]) elif shapes[vidx] == 3: (vertex_width, vertex_height) = (font.measure(str(labels[vidx])) + 2, font.metrics("linespace") + 2) print >> f, ' '.\ format(vertex_width / 2., vertex_height / 2., vertex_width, vertex_height, vidx, colors[vidx]) print >> f, ' '.format(vertex_size / 2.,vidx, font_size) print >> f, '{2}'.format(vertex_size / 2.,vidx, str(labels[vidx])) print >> f, ' ' print >> f, '' print >> f print >> f, '' if our_file: f.close() @classmethod def _identify_format(klass, filename): """_identify_format(filename) Tries to identify the format of the graph stored in the file with the given filename. It identifies most file formats based on the extension of the file (and not on syntactic evaluation). The only exception is the adjacency matrix format and the edge list format: the first few lines of the file are evaluated to decide between the two. @note: Internal function, should not be called directly. @param filename: the name of the file or a file object whose C{name} attribute is set. @return: the format of the file as a string. """ import os.path if hasattr(filename, "name") and hasattr(filename, "read"): # It is most likely a file try: filename=filename.name except: return None root, ext = os.path.splitext(filename) ext = ext.lower() if ext == ".gz": _, ext2 = os.path.splitext(root) ext2 = ext2.lower() if ext2 == ".pickle": return "picklez" elif ext2 == ".graphml": return "graphmlz" if ext in [".graphml", ".graphmlz", ".lgl", ".ncol", ".pajek", ".gml", ".dimacs", ".edgelist", ".edges", ".edge", ".net", ".pickle", ".picklez", ".dot", ".gw", ".lgr", ".dl"]: return ext[1:] if ext == ".txt" or ext == ".dat": # Most probably an adjacency matrix or an edge list f = open(filename, "r") line = f.readline() if line is None: return "edges" parts = line.strip().split() if len(parts) == 2: line = f.readline() if line is None: return "edges" parts = line.strip().split() if len(parts) == 2: line = f.readline() if line is None: # This is a 2x2 matrix, it can be a matrix or an edge # list as well and we cannot decide return None else: parts = line.strip().split() if len(parts) == 0: return None return "edges" else: # Not a matrix return None else: return "adjacency" @classmethod def Read(klass, f, format=None, *args, **kwds): """Unified reading function for graphs. This method tries to identify the format of the graph given in the first parameter and calls the corresponding reader method. The remaining arguments are passed to the reader method without any changes. @param f: the file containing the graph to be loaded @param format: the format of the file (if known in advance). C{None} means auto-detection. Possible values are: C{"ncol"} (NCOL format), C{"lgl"} (LGL format), C{"graphdb"} (GraphDB format), C{"graphml"}, C{"graphmlz"} (GraphML and gzipped GraphML format), C{"gml"} (GML format), C{"net"}, C{"pajek"} (Pajek format), C{"dimacs"} (DIMACS format), C{"edgelist"}, C{"edges"} or C{"edge"} (edge list), C{"adjacency"} (adjacency matrix), C{"dl"} (DL format used by UCINET), C{"pickle"} (Python pickled format), C{"picklez"} (gzipped Python pickled format) @raises IOError: if the file format can't be identified and none was given. """ if format is None: format = klass._identify_format(f) try: reader = klass._format_mapping[format][0] except (KeyError, IndexError): raise IOError("unknown file format: %s" % str(format)) if reader is None: raise IOError("no reader method for file format: %s" % str(format)) reader = getattr(klass, reader) return reader(f, *args, **kwds) Load = Read def write(self, f, format=None, *args, **kwds): """Unified writing function for graphs. This method tries to identify the format of the graph given in the first parameter (based on extension) and calls the corresponding writer method. The remaining arguments are passed to the writer method without any changes. @param f: the file containing the graph to be saved @param format: the format of the file (if one wants to override the format determined from the filename extension, or the filename itself is a stream). C{None} means auto-detection. Possible values are: - C{"adjacency"}: adjacency matrix format - C{"dimacs"}: DIMACS format - C{"dot"}, C{"graphviz"}: GraphViz DOT format - C{"edgelist"}, C{"edges"} or C{"edge"}: numeric edge list format - C{"gml"}: GML format - C{"graphml"} and C{"graphmlz"}: standard and gzipped GraphML format - C{"gw"}, C{"leda"}, C{"lgr"}: LEDA native format - C{"lgl"}: LGL format - C{"ncol"}: NCOL format - C{"net"}, C{"pajek"}: Pajek format - C{"pickle"}, C{"picklez"}: standard and gzipped Python pickled format - C{"svg"}: SVG format @raises IOError: if the file format can't be identified and none was given. """ if format is None: format = self._identify_format(f) try: writer = self._format_mapping[format][1] except (KeyError, IndexError): raise IOError("unknown file format: %s" % str(format)) if writer is None: raise IOError("no writer method for file format: %s" % str(format)) writer = getattr(self, writer) return writer(f, *args, **kwds) save = write ##################################################### # Constructor for dict-like representation of graphs @classmethod def DictList(klass, vertices, edges, directed=False, \ vertex_name_attr="name", edge_foreign_keys=("source", "target"), \ iterative=False): """Constructs a graph from a list-of-dictionaries representation. This representation assumes that vertices and edges are encoded in two lists, each list containing a Python dict for each vertex and each edge, respectively. A distinguished element of the vertex dicts contain a vertex ID which is used in the edge dicts to refer to source and target vertices. All the remaining elements of the dict are considered vertex and edge attributes. Note that the implementation does not assume that the objects passed to this method are indeed lists of dicts, but they should be iterable and they should yield objects that behave as dicts. So, for instance, a database query result is likely to be fit as long as it's iterable and yields dict-like objects with every iteration. @param vertices: the data source for the vertices or C{None} if there are no special attributes assigned to vertices and we should simply use the edge list of dicts to infer vertex names. @param edges: the data source for the edges. @param directed: whether the constructed graph will be directed @param vertex_name_attr: the name of the distinguished key in the dicts in the vertex data source that contains the vertex names. Ignored if C{vertices} is C{None}. @param edge_foreign_keys: the name of the attributes in the dicts in the edge data source that contain the source and target vertex names. @param iterative: whether to add the edges to the graph one by one, iteratively, or to build a large edge list first and use that to construct the graph. The latter approach is faster but it may not be suitable if your dataset is large. The default is to add the edges in a batch from an edge list. @return: the graph that was constructed """ def create_list_from_indices(l, n): result = [None] * n for i, v in l: result[i] = v return result # Construct the vertices vertex_attrs, n = {}, 0 if vertices: for idx, vertex_data in enumerate(vertices): for k, v in vertex_data.iteritems(): try: vertex_attrs[k].append((idx, v)) except KeyError: vertex_attrs[k] = [(idx, v)] n += 1 for k, v in vertex_attrs.iteritems(): vertex_attrs[k] = create_list_from_indices(v, n) else: vertex_attrs[vertex_name_attr] = [] vertex_names = vertex_attrs[vertex_name_attr] # Check for duplicates in vertex_names if len(vertex_names) != len(set(vertex_names)): raise ValueError("vertex names are not unique") # Create a reverse mapping from vertex names to indices vertex_name_map = UniqueIdGenerator(initial = vertex_names) # Construct the edges efk_src, efk_dest = edge_foreign_keys if iterative: g = klass(n, [], directed, {}, vertex_attrs) for idx, edge_data in enumerate(edges): src_name, dst_name = edge_data[efk_src], edge_data[efk_dest] v1 = vertex_name_map[src_name] if v1 == n: g.add_vertices(1) g.vs[n][vertex_name_attr] = src_name n += 1 v2 = vertex_name_map[dst_name] if v2 == n: g.add_vertices(1) g.vs[n][vertex_name_attr] = dst_name n += 1 g.add_edge(v1, v2) for k, v in edge_data.iteritems(): g.es[idx][k] = v return g else: edge_list, edge_attrs, m = [], {}, 0 for idx, edge_data in enumerate(edges): v1 = vertex_name_map[edge_data[efk_src]] v2 = vertex_name_map[edge_data[efk_dest]] edge_list.append((v1, v2)) for k, v in edge_data.iteritems(): try: edge_attrs[k].append((idx, v)) except KeyError: edge_attrs[k] = [(idx, v)] m += 1 for k, v in edge_attrs.iteritems(): edge_attrs[k] = create_list_from_indices(v, m) # It may have happened that some vertices were added during # the process if len(vertex_name_map) > n: diff = len(vertex_name_map) - n more = [None] * diff for k, v in vertex_attrs.iteritems(): v.extend(more) vertex_attrs[vertex_name_attr] = vertex_name_map.values() n = len(vertex_name_map) # Create the graph return klass(n, edge_list, directed, {}, vertex_attrs, edge_attrs) ##################################################### # Constructor for tuple-like representation of graphs @classmethod def TupleList(klass, edges, directed=False, \ vertex_name_attr="name", edge_attrs=None, weights=False): """Constructs a graph from a list-of-tuples representation. This representation assumes that the edges of the graph are encoded in a list of tuples (or lists). Each item in the list must have at least two elements, which specify the source and the target vertices of the edge. The remaining elements (if any) specify the edge attributes of that edge, where the names of the edge attributes originate from the C{edge_attrs} list. The names of the vertices will be stored in the vertex attribute given by C{vertex_name_attr}. The default parameters of this function are suitable for creating unweighted graphs from lists where each item contains the source vertex and the target vertex. If you have a weighted graph, you can use items where the third item contains the weight of the edge by setting C{edge_attrs} to C{"weight"} or C{["weight"]}. If you have even more edge attributes, add them to the end of each item in the C{edges} list and also specify the corresponding edge attribute names in C{edge_attrs} as a list. @param edges: the data source for the edges. This must be a list where each item is a tuple (or list) containing at least two items: the name of the source and the target vertex. Note that names will be assigned to the C{name} vertex attribute (or another vertex attribute if C{vertex_name_attr} is specified), even if all the vertex names in the list are in fact numbers. @param directed: whether the constructed graph will be directed @param vertex_name_attr: the name of the vertex attribute that will contain the vertex names. @param edge_attrs: the names of the edge attributes that are filled with the extra items in the edge list (starting from index 2, since the first two items are the source and target vertices). C{None} means that only the source and target vertices will be extracted from each item. If you pass a string here, it will be wrapped in a list for convenience. @param weights: alternative way to specify that the graph is weighted. If you set C{weights} to C{true} and C{edge_attrs} is not given, it will be assumed that C{edge_attrs} is C{["weight"]} and igraph will parse the third element from each item into an edge weight. If you set C{weights} to a string, it will be assumed that C{edge_attrs} contains that string only, and igraph will store the edge weights in that attribute. @return: the graph that was constructed """ if edge_attrs is None: if not weights: edge_attrs = () else: if not isinstance(weights, basestring): weights = "weight" edge_attrs = [weights] else: if weights: raise ValueError("`weights` must be False if `edge_attrs` is " "not None") if isinstance(edge_attrs, basestring): edge_attrs = [edge_attrs] # Set up a vertex ID generator idgen = UniqueIdGenerator() # Construct the edges and the edge attributes edge_list = [] edge_attributes = {} for name in edge_attrs: edge_attributes[name] = [] for item in edges: edge_list.append((idgen[item[0]], idgen[item[1]])) for index, name in enumerate(edge_attrs, 2): try: edge_attributes[name].append(item[index]) except IndexError: edge_attributes[name].append(None) # Set up the "name" vertex attribute vertex_attributes = {} vertex_attributes[vertex_name_attr] = idgen.values() n = len(idgen) # Construct the graph return klass(n, edge_list, directed, {}, vertex_attributes, edge_attributes) ################################# # Constructor for graph formulae Formula=classmethod(construct_graph_from_formula) ########################### # Vertex and edge sequence @property def vs(self): """The vertex sequence of the graph""" return VertexSeq(self) @property def es(self): """The edge sequence of the graph""" return EdgeSeq(self) ############################################# # Friendlier interface for bipartite methods @classmethod def Bipartite(klass, types, *args, **kwds): """Bipartite(types, edges, directed=False) Creates a bipartite graph with the given vertex types and edges. This is similar to the default constructor of the graph, the only difference is that it checks whether all the edges go between the two vertex classes and it assigns the type vector to a C{type} attribute afterwards. Examples: >>> g = Graph.Bipartite([0, 1, 0, 1], [(0, 1), (2, 3), (0, 3)]) >>> g.is_bipartite() True >>> g.vs["type"] [False, True, False, True] @param types: the vertex types as a boolean list. Anything that evaluates to C{False} will denote a vertex of the first kind, anything that evaluates to C{True} will denote a vertex of the second kind. @param edges: the edges as a list of tuples. @param directed: whether to create a directed graph. Bipartite networks are usually undirected, so the default is C{False} @return: the graph with a binary vertex attribute named C{"type"} that stores the vertex classes. """ result = klass._Bipartite(types, *args, **kwds) result.vs["type"] = [bool(x) for x in types] return result @classmethod def Full_Bipartite(klass, *args, **kwds): """Full_Bipartite(n1, n2, directed=False, mode=ALL) Generates a full bipartite graph (directed or undirected, with or without loops). >>> g = Graph.Full_Bipartite(2, 3) >>> g.is_bipartite() True >>> g.vs["type"] [False, False, True, True, True] @param n1: the number of vertices of the first kind. @param n2: the number of vertices of the second kind. @param directed: whether tp generate a directed graph. @param mode: if C{OUT}, then all vertices of the first kind are connected to the others; C{IN} specifies the opposite direction, C{ALL} creates mutual edges. Ignored for undirected graphs. @return: the graph with a binary vertex attribute named C{"type"} that stores the vertex classes. """ result, types = klass._Full_Bipartite(*args, **kwds) result.vs["type"] = types return result @classmethod def Random_Bipartite(klass, *args, **kwds): """Random_Bipartite(n1, n2, p=None, m=None, directed=False, neimode=ALL) Generates a random bipartite graph with the given number of vertices and edges (if m is given), or with the given number of vertices and the given connection probability (if p is given). If m is given but p is not, the generated graph will have n1 vertices of type 1, n2 vertices of type 2 and m randomly selected edges between them. If p is given but m is not, the generated graph will have n1 vertices of type 1 and n2 vertices of type 2, and each edge will exist between them with probability p. @param n1: the number of vertices of type 1. @param n2: the number of vertices of type 2. @param p: the probability of edges. If given, C{m} must be missing. @param m: the number of edges. If given, C{p} must be missing. @param directed: whether to generate a directed graph. @param neimode: if the graph is directed, specifies how the edges will be generated. If it is C{"all"}, edges will be generated in both directions (from type 1 to type 2 and vice versa) independently. If it is C{"out"} edges will always point from type 1 to type 2. If it is C{"in"}, edges will always point from type 2 to type 1. This argument is ignored for undirected graphs. """ result, types = klass._Random_Bipartite(*args, **kwds) result.vs["type"] = types return result @classmethod def GRG(klass, n, radius, torus=False): """GRG(n, radius, torus=False, return_coordinates=False) Generates a random geometric graph. The algorithm drops the vertices randomly on the 2D unit square and connects them if they are closer to each other than the given radius. The coordinates of the vertices are stored in the vertex attributes C{x} and C{y}. @param n: The number of vertices in the graph @param radius: The given radius @param torus: This should be C{True} if we want to use a torus instead of a square. """ result, xs, ys = klass._GRG(n, radius, torus) result.vs["x"] = xs result.vs["y"] = ys return result @classmethod def Incidence(klass, *args, **kwds): """Incidence(matrix, directed=False, mode=ALL, multiple=False) Creates a bipartite graph from an incidence matrix. Example: >>> g = Graph.Incidence([[0, 1, 1], [1, 1, 0]]) @param matrix: the incidence matrix. @param directed: whether to create a directed graph. @param mode: defines the direction of edges in the graph. If C{OUT}, then edges go from vertices of the first kind (corresponding to rows of the matrix) to vertices of the second kind (the columns of the matrix). If C{IN}, the opposite direction is used. C{ALL} creates mutual edges. Ignored for undirected graphs. @param multiple: defines what to do with non-zero entries in the matrix. If C{False}, non-zero entries will create an edge no matter what the value is. If C{True}, non-zero entries are rounded up to the nearest integer and this will be the number of multiple edges created. @return: the graph with a binary vertex attribute named C{"type"} that stores the vertex classes. """ result, types = klass._Incidence(*args, **kwds) result.vs["type"] = types return result def bipartite_projection(self, types="type", multiplicity=True, probe1=-1, which="both"): """Projects a bipartite graph into two one-mode graphs. Edge directions are ignored while projecting. Examples: >>> g = Graph.Full_Bipartite(10, 5) >>> g1, g2 = g.bipartite_projection() >>> g1.isomorphic(Graph.Full(10)) True >>> g2.isomorphic(Graph.Full(5)) True @param types: an igraph vector containing the vertex types, or an attribute name. Anything that evalulates to C{False} corresponds to vertices of the first kind, everything else to the second kind. @param multiplicity: if C{True}, then igraph keeps the multiplicity of the edges in the projection in an edge attribute called C{"weight"}. E.g., if there is an A-C-B and an A-D-B triplet in the bipartite graph and there is no other X (apart from X=B and X=D) for which an A-X-B triplet would exist in the bipartite graph, the multiplicity of the A-B edge in the projection will be 2. @param probe1: this argument can be used to specify the order of the projections in the resulting list. If given and non-negative, then it is considered as a vertex ID; the projection containing the vertex will be the first one in the result. @param which: this argument can be used to specify which of the two projections should be returned if only one of them is needed. Passing 0 here means that only the first projection is returned, while 1 means that only the second projection is returned. (Note that we use 0 and 1 because Python indexing is zero-based). C{False} is equivalent to 0 and C{True} is equivalent to 1. Any other value means that both projections will be returned in a tuple. @return: a tuple containing the two projected one-mode graphs if C{which} is not 1 or 2, or the projected one-mode graph specified by the C{which} argument if its value is 0, 1, C{False} or C{True}. """ superclass_meth = super(Graph, self).bipartite_projection if which == False: which = 0 elif which == True: which = 1 if which != 0 and which != 1: which = -1 if multiplicity: if which == 0: g1, w1 = superclass_meth(types, True, probe1, which) g2, w2 = None, None elif which == 1: g1, w1 = None, None g2, w2 = superclass_meth(types, True, probe1, which) else: g1, g2, w1, w2 = superclass_meth(types, True, probe1, which) if g1 is not None: g1.es["weight"] = w1 if g2 is not None: g2.es["weight"] = w2 return g1, g2 else: return g1 else: g2.es["weight"] = w2 return g2 else: return superclass_meth(types, False, probe1, which) def bipartite_projection_size(self, types="type", *args, **kwds): """bipartite_projection(types="type") Calculates the number of vertices and edges in the bipartite projections of this graph according to the specified vertex types. This is useful if you have a bipartite graph and you want to estimate the amount of memory you would need to calculate the projections themselves. @param types: an igraph vector containing the vertex types, or an attribute name. Anything that evalulates to C{False} corresponds to vertices of the first kind, everything else to the second kind. @return: a 4-tuple containing the number of vertices and edges in the first projection, followed by the number of vertices and edges in the second projection. """ return super(Graph, self).bipartite_projection_size(types, \ *args, **kwds) def get_incidence(self, types="type", *args, **kwds): """get_incidence(self, types="type") Returns the incidence matrix of a bipartite graph. The incidence matrix is an M{n} times M{m} matrix, where M{n} and M{m} are the number of vertices in the two vertex classes. @param types: an igraph vector containing the vertex types, or an attribute name. Anything that evalulates to C{False} corresponds to vertices of the first kind, everything else to the second kind. @return: the incidence matrix and two lists in a triplet. The first list defines the mapping between row indices of the matrix and the original vertex IDs. The second list is the same for the column indices. """ return super(Graph, self).get_incidence(types, *args, **kwds) ########################### # ctypes support @property def _as_parameter_(self): return self._raw_pointer() ################### # Custom operators def __iadd__(self, other): """In-place addition (disjoint union). @see: L{__add__} """ if isinstance(other, (int, basestring)): self.add_vertices(other) return self elif isinstance(other, tuple) and len(other) == 2: self.add_edges([other]) return self elif isinstance(other, list): if not other: return self if isinstance(other[0], tuple): self.add_edges(other) return self if isinstance(other[0], basestring): self.add_vertices(other) return self return NotImplemented def __add__(self, other): """Copies the graph and extends the copy depending on the type of the other object given. @param other: if it is an integer, the copy is extended by the given number of vertices. If it is a string, the copy is extended by a single vertex whose C{name} attribute will be equal to the given string. If it is a tuple with two elements, the copy is extended by a single edge. If it is a list of tuples, the copy is extended by multiple edges. If it is a L{Graph}, a disjoint union is performed. """ if isinstance(other, (int, basestring)): g = self.copy() g.add_vertices(other) elif isinstance(other, tuple) and len(other) == 2: g = self.copy() g.add_edges([other]) elif isinstance(other, list): if len(other)>0: if isinstance(other[0], tuple): g = self.copy() g.add_edges(other) elif isinstance(other[0], basestring): g = self.copy() g.add_vertices(other) elif isinstance(other[0], Graph): return self.disjoint_union(other) else: return NotImplemented else: return self.copy() elif isinstance(other, Graph): return self.disjoint_union(other) else: return NotImplemented return g def __isub__(self, other): """In-place subtraction (difference). @see: L{__sub__}""" if isinstance(other, int): self.delete_vertices([other]) elif isinstance(other, tuple) and len(other) == 2: self.delete_edges([other]) elif isinstance(other, list): if len(other)>0: if isinstance(other[0], tuple): self.delete_edges(other) elif isinstance(other[0], (int, long, basestring)): self.delete_vertices(other) else: return NotImplemented elif isinstance(other, _igraph.Vertex): self.delete_vertices(other) elif isinstance(other, _igraph.VertexSeq): self.delete_vertices(other) elif isinstance(other, _igraph.Edge): self.delete_edges(other) elif isinstance(other, _igraph.EdgeSeq): self.delete_edges(other) else: return NotImplemented return self def __sub__(self, other): """Removes the given object(s) from the graph @param other: if it is an integer, removes the vertex with the given ID from the graph (note that the remaining vertices will get re-indexed!). If it is a tuple, removes the given edge. If it is a graph, takes the difference of the two graphs. Accepts lists of integers or lists of tuples as well, but they can't be mixed! Also accepts L{Edge} and L{EdgeSeq} objects. """ if isinstance(other, Graph): return self.difference(other) result = self.copy() if isinstance(other, (int, long, basestring)): result.delete_vertices([other]) elif isinstance(other, tuple) and len(other) == 2: result.delete_edges([other]) elif isinstance(other, list): if len(other)>0: if isinstance(other[0], tuple): result.delete_edges(other) elif isinstance(other[0], (int, long, basestring)): result.delete_vertices(other) else: return NotImplemented else: return result elif isinstance(other, _igraph.Vertex): result.delete_vertices(other) elif isinstance(other, _igraph.VertexSeq): result.delete_vertices(other) elif isinstance(other, _igraph.Edge): result.delete_edges(other) elif isinstance(other, _igraph.EdgeSeq): result.delete_edges(other) else: return NotImplemented return result def __mul__(self, other): """Copies exact replicas of the original graph an arbitrary number of times. @param other: if it is an integer, multiplies the graph by creating the given number of identical copies and taking the disjoint union of them. """ if isinstance(other, int): if other == 0: return Graph() elif other == 1: return self elif other > 1: return self.disjoint_union([self]*(other-1)) else: return NotImplemented return NotImplemented def __nonzero__(self): """Returns True if the graph has at least one vertex, False otherwise. """ return self.vcount() > 0 def __coerce__(self, other): """Coercion rules. This method is needed to allow the graph to react to additions with lists, tuples, integers, strings, vertices, edges and so on. """ if isinstance(other, (int, tuple, list, basestring)): return self, other if isinstance(other, _igraph.Vertex): return self, other if isinstance(other, _igraph.VertexSeq): return self, other if isinstance(other, _igraph.Edge): return self, other if isinstance(other, _igraph.EdgeSeq): return self, other return NotImplemented @classmethod def _reconstruct(cls, attrs, *args, **kwds): """Reconstructs a Graph object from Python's pickled format. This method is for internal use only, it should not be called directly.""" result = cls(*args, **kwds) result.__dict__.update(attrs) return result def __reduce__(self): """Support for pickling.""" constructor = self.__class__ gattrs, vattrs, eattrs = {}, {}, {} for attr in self.attributes(): gattrs[attr] = self[attr] for attr in self.vs.attribute_names(): vattrs[attr] = self.vs[attr] for attr in self.es.attribute_names(): eattrs[attr] = self.es[attr] parameters = (self.vcount(), self.get_edgelist(), \ self.is_directed(), gattrs, vattrs, eattrs) return (constructor, parameters, self.__dict__) def __plot__(self, context, bbox, palette, *args, **kwds): """Plots the graph to the given Cairo context in the given bounding box The visual style of vertices and edges can be modified at three places in the following order of precedence (lower indices override higher indices): 1. Keyword arguments of this function (or of L{plot()} which is passed intact to C{Graph.__plot__()}. 2. Vertex or edge attributes, specified later in the list of keyword arguments. 3. igraph-wide plotting defaults (see L{igraph.config.Configuration}) 4. Built-in defaults. E.g., if the C{vertex_size} keyword attribute is not present, but there exists a vertex attribute named C{size}, the sizes of the vertices will be specified by that attribute. Besides the usual self-explanatory plotting parameters (C{context}, C{bbox}, C{palette}), it accepts the following keyword arguments: - C{autocurve}: whether to use curves instead of straight lines for multiple edges on the graph plot. This argument may be C{True} or C{False}; when omitted, C{True} is assumed for graphs with less than 10.000 edges and C{False} otherwise. - C{drawer_factory}: a subclass of L{AbstractCairoGraphDrawer} which will be used to draw the graph. You may also provide a function here which takes two arguments: the Cairo context to draw on and a bounding box (an instance of L{BoundingBox}). If this keyword argument is missing, igraph will use the default graph drawer which should be suitable for most purposes. It is safe to omit this keyword argument unless you need to use a specific graph drawer. - C{keep_aspect_ratio}: whether to keep the aspect ratio of the layout that igraph calculates to place the nodes. C{True} means that the layout will be scaled proportionally to fit into the bounding box where the graph is to be drawn but the aspect ratio will be kept the same (potentially leaving empty space next to, below or above the graph). C{False} means that the layout will be scaled independently along the X and Y axis in order to fill the entire bounding box. The default is C{False}. - C{layout}: the layout to be used. If not an instance of L{Layout}, it will be passed to L{Graph.layout} to calculate the layout. Note that if you want a deterministic layout that does not change with every plot, you must either use a deterministic layout function (like L{Graph.layout_circle}) or calculate the layout in advance and pass a L{Layout} object here. - C{margin}: the top, right, bottom, left margins as a 4-tuple. If it has less than 4 elements or is a single float, the elements will be re-used until the length is at least 4. - C{mark_groups}: whether to highlight some of the vertex groups by colored polygons. This argument can be one of the following: - C{False}: no groups will be highlighted - A dict mapping tuples of vertex indices to color names. The given vertex groups will be highlighted by the given colors. - A list containing pairs or an iterable yielding pairs, where the first element of each pair is a list of vertex indices and the second element is a color. In place of lists of vertex indices, you may also use L{VertexSeq} instances. In place of color names, you may also use color indices into the current palette. C{None} as a color name will mean that the corresponding group is ignored. - C{vertex_size}: size of the vertices. The corresponding vertex attribute is called C{size}. The default is 10. Vertex sizes are measured in the unit of the Cairo context on which igraph is drawing. - C{vertex_color}: color of the vertices. The corresponding vertex attribute is C{color}, the default is red. Colors can be specified either by common X11 color names (see the source code of L{igraph.drawing.colors} for a list of known colors), by 3-tuples of floats (ranging between 0 and 255 for the R, G and B components), by CSS-style string specifications (C{#rrggbb}) or by integer color indices of the specified palette. - C{vertex_frame_color}: color of the frame (i.e. stroke) of the vertices. The corresponding vertex attribute is C{frame_color}, the default is black. See C{vertex_color} for the possible ways of specifying a color. - C{vertex_frame_width}: the width of the frame (i.e. stroke) of the vertices. The corresponding vertex attribute is C{frame_width}. The default is 1. Vertex frame widths are measured in the unit of the Cairo context on which igraph is drawing. - C{vertex_shape}: shape of the vertices. Alternatively it can be specified by the C{shape} vertex attribute. Possibilities are: C{square}, {circle}, {triangle}, {triangle-down} or C{hidden}. See the source code of L{igraph.drawing} for a list of alternative shape names that are also accepted and mapped to these. - C{vertex_label}: labels drawn next to the vertices. The corresponding vertex attribute is C{label}. - C{vertex_label_dist}: distance of the midpoint of the vertex label from the center of the corresponding vertex. The corresponding vertex attribute is C{label_dist}. - C{vertex_label_color}: color of the label. Corresponding vertex attribute: C{label_color}. See C{vertex_color} for color specification syntax. - C{vertex_label_size}: font size of the label, specified in the unit of the Cairo context on which we are drawing. Corresponding vertex attribute: C{label_size}. - C{vertex_label_angle}: the direction of the line connecting the midpoint of the vertex with the midpoint of the label. This can be used to position the labels relative to the vertices themselves in conjunction with C{vertex_label_dist}. Corresponding vertex attribute: C{label_angle}. The default is C{-math.pi/2}. - C{vertex_order}: drawing order of the vertices. This must be a list or tuple containing vertex indices; vertices are then drawn according to this order. - C{vertex_order_by}: an alternative way to specify the drawing order of the vertices; this attribute is interpreted as the name of a vertex attribute, and vertices are drawn such that those with a smaller attribute value are drawn first. You may also reverse the order by passing a tuple here; the first element of the tuple should be the name of the attribute, the second element specifies whether the order is reversed (C{True}, C{False}, C{"asc"} and C{"desc"} are accepted values). - C{edge_color}: color of the edges. The corresponding edge attribute is C{color}, the default is red. See C{vertex_color} for color specification syntax. - C{edge_curved}: whether the edges should be curved. Positive numbers correspond to edges curved in a counter-clockwise direction, negative numbers correspond to edges curved in a clockwise direction. Zero represents straight edges. C{True} is interpreted as 0.5, C{False} is interpreted as 0. The default is 0 which makes all the edges straight. - C{edge_width}: width of the edges in the default unit of the Cairo context on which we are drawing. The corresponding edge attribute is C{width}, the default is 1. - C{edge_arrow_size}: arrow size of the edges. The corresponding edge attribute is C{arrow_size}, the default is 1. - C{edge_arrow_width}: width of the arrowhead on the edge. The corresponding edge attribute is C{arrow_width}, the default is 1. - C{edge_order}: drawing order of the edges. This must be a list or tuple containing edge indices; edges are then drawn according to this order. - C{edge_order_by}: an alternative way to specify the drawing order of the edges; this attribute is interpreted as the name of an edge attribute, and edges are drawn such that those with a smaller attribute value are drawn first. You may also reverse the order by passing a tuple here; the first element of the tuple should be the name of the attribute, the second element specifies whether the order is reversed (C{True}, C{False}, C{"asc"} and C{"desc"} are accepted values). """ drawer_factory = kwds.get("drawer_factory", DefaultGraphDrawer) if "drawer_factory" in kwds: del kwds["drawer_factory"] drawer = drawer_factory(context, bbox) drawer.draw(self, palette, *args, **kwds) def __str__(self): """Returns a string representation of the graph. Behind the scenes, this method constructs a L{GraphSummary} instance and invokes its C{__str__} method with a verbosity of 1 and attribute printing turned off. See the documentation of L{GraphSummary} for more details about the output. """ params = dict( verbosity=1, width=78, print_graph_attributes=False, print_vertex_attributes=False, print_edge_attributes=False ) return self.summary(**params) def summary(self, verbosity=0, width=None, *args, **kwds): """Returns the summary of the graph. The output of this method is similar to the output of the C{__str__} method. If I{verbosity} is zero, only the header line is returned (see C{__str__} for more details), otherwise the header line and the edge list is printed. Behind the scenes, this method constructs a L{GraphSummary} object and invokes its C{__str__} method. @param verbosity: if zero, only the header line is returned (see C{__str__} for more details), otherwise the header line and the full edge list is printed. @param width: the number of characters to use in one line. If C{None}, no limit will be enforced on the line lengths. @return: the summary of the graph. """ return str(GraphSummary(self, verbosity, width, *args, **kwds)) _format_mapping = { "ncol": ("Read_Ncol", "write_ncol"), "lgl": ("Read_Lgl", "write_lgl"), "graphdb": ("Read_GraphDB", None), "graphmlz": ("Read_GraphMLz", "write_graphmlz"), "graphml": ("Read_GraphML", "write_graphml"), "gml": ("Read_GML", "write_gml"), "dot": (None, "write_dot"), "graphviz": (None, "write_dot"), "net": ("Read_Pajek", "write_pajek"), "pajek": ("Read_Pajek", "write_pajek"), "dimacs": ("Read_DIMACS", "write_dimacs"), "adjacency": ("Read_Adjacency", "write_adjacency"), "adj": ("Read_Adjacency", "write_adjacency"), "edgelist": ("Read_Edgelist", "write_edgelist"), "edge": ("Read_Edgelist", "write_edgelist"), "edges": ("Read_Edgelist", "write_edgelist"), "pickle": ("Read_Pickle", "write_pickle"), "picklez": ("Read_Picklez", "write_picklez"), "svg": (None, "write_svg"), "gw": (None, "write_leda"), "leda": (None, "write_leda"), "lgr": (None, "write_leda"), "dl": ("Read_DL", None) } _layout_mapping = { "auto": "layout_auto", "automatic": "layout_auto", "bipartite": "layout_bipartite", "circle": "layout_circle", "circular": "layout_circle", "drl": "layout_drl", "fr": "layout_fruchterman_reingold", "fruchterman_reingold": "layout_fruchterman_reingold", "gfr": "layout_grid_fruchterman_reingold", "graphopt": "layout_graphopt", "grid": "layout_grid", "grid_fr": "layout_grid_fruchterman_reingold", "grid_fruchterman_reingold": "layout_grid_fruchterman_reingold", "kk": "layout_kamada_kawai", "kamada_kawai": "layout_kamada_kawai", "lgl": "layout_lgl", "large": "layout_lgl", "large_graph": "layout_lgl", "mds": "layout_mds", "random": "layout_random", "rt": "layout_reingold_tilford", "tree": "layout_reingold_tilford", "reingold_tilford": "layout_reingold_tilford", "rt_circular": "layout_reingold_tilford_circular", "reingold_tilford_circular": "layout_reingold_tilford_circular", "sphere": "layout_sphere", "spherical": "layout_sphere", "star": "layout_star", "sugiyama": "layout_sugiyama", } # After adjusting something here, don't forget to update the docstring # of Graph.layout if necessary! ############################################################## class VertexSeq(_igraph.VertexSeq): """Class representing a sequence of vertices in the graph. This class is most easily accessed by the C{vs} field of the L{Graph} object, which returns an ordered sequence of all vertices in the graph. The vertex sequence can be refined by invoking the L{VertexSeq.select()} method. L{VertexSeq.select()} can also be accessed by simply calling the L{VertexSeq} object. An alternative way to create a vertex sequence referring to a given graph is to use the constructor directly: >>> g = Graph.Full(3) >>> vs = VertexSeq(g) >>> restricted_vs = VertexSeq(g, [0, 1]) The individual vertices can be accessed by indexing the vertex sequence object. It can be used as an iterable as well, or even in a list comprehension: >>> g=Graph.Full(3) >>> for v in g.vs: ... v["value"] = v.index ** 2 ... >>> [v["value"] ** 0.5 for v in g.vs] [0.0, 1.0, 2.0] The vertex set can also be used as a dictionary where the keys are the attribute names. The values corresponding to the keys are the values of the given attribute for every vertex selected by the sequence. >>> g=Graph.Full(3) >>> for idx, v in enumerate(g.vs): ... v["weight"] = idx*(idx+1) ... >>> g.vs["weight"] [0, 2, 6] >>> g.vs.select(1,2)["weight"] = [10, 20] >>> g.vs["weight"] [0, 10, 20] If you specify a sequence that is shorter than the number of vertices in the VertexSeq, the sequence is reused: >>> g = Graph.Tree(7, 2) >>> g.vs["color"] = ["red", "green"] >>> g.vs["color"] ['red', 'green', 'red', 'green', 'red', 'green', 'red'] You can even pass a single string or integer, it will be considered as a sequence of length 1: >>> g.vs["color"] = "red" >>> g.vs["color"] ['red', 'red', 'red', 'red', 'red', 'red', 'red'] Some methods of the vertex sequences are simply proxy methods to the corresponding methods in the L{Graph} object. One such example is L{VertexSeq.degree()}: >>> g=Graph.Tree(7, 2) >>> g.vs.degree() [2, 3, 3, 1, 1, 1, 1] >>> g.vs.degree() == g.degree() True """ def attributes(self): """Returns the list of all the vertex attributes in the graph associated to this vertex sequence.""" return self.graph.vertex_attributes() def find(self, *args, **kwds): """Returns the first vertex of the vertex sequence that matches some criteria. The selection criteria are equal to the ones allowed by L{VertexSeq.select}. See L{VertexSeq.select} for more details. For instance, to find the first vertex with name C{foo} in graph C{g}: >>> g.vs.find(name="foo") #doctest:+SKIP To find an arbitrary isolated vertex: >>> g.vs.find(_degree=0) #doctest:+SKIP """ # Shortcut: if "name" is in kwds, we try that first because that # attribute is indexed if "name" in kwds: name = kwds.pop("name") elif "name_eq" in kwds: name = kwds.pop("name_eq") else: name = None if name is not None: if args: args.insert(0, name) else: args = [name] if args: # Selecting first based on positional arguments, then checking # the criteria specified by the (remaining) keyword arguments vertex = _igraph.VertexSeq.find(self, *args) if not kwds: return vertex vs = self.graph.vs[vertex.index] else: vs = self # Selecting based on keyword arguments vs = vs.select(**kwds) if vs: return vs[0] raise ValueError("no such vertex") def select(self, *args, **kwds): """Selects a subset of the vertex sequence based on some criteria The selection criteria can be specified by the positional and the keyword arguments. Positional arguments are always processed before keyword arguments. - If the first positional argument is C{None}, an empty sequence is returned. - If the first positional argument is a callable object, the object will be called for every vertex in the sequence. If it returns C{True}, the vertex will be included, otherwise it will be excluded. - If the first positional argument is an iterable, it must return integers and they will be considered as indices of the current vertex set (NOT the whole vertex set of the graph -- the difference matters when one filters a vertex set that has already been filtered by a previous invocation of L{VertexSeq.select()}. In this case, the indices do not refer directly to the vertices of the graph but to the elements of the filtered vertex sequence. - If the first positional argument is an integer, all remaining arguments are expected to be integers. They are considered as indices of the current vertex set again. Keyword arguments can be used to filter the vertices based on their attributes. The name of the keyword specifies the name of the attribute and the filtering operator, they should be concatenated by an underscore (C{_}) character. Attribute names can also contain underscores, but operator names don't, so the operator is always the largest trailing substring of the keyword name that does not contain an underscore. Possible operators are: - C{eq}: equal to - C{ne}: not equal to - C{lt}: less than - C{gt}: greater than - C{le}: less than or equal to - C{ge}: greater than or equal to - C{in}: checks if the value of an attribute is in a given list - C{notin}: checks if the value of an attribute is not in a given list For instance, if you want to filter vertices with a numeric C{age} property larger than 200, you have to write: >>> g.vs.select(age_gt=200) #doctest: +SKIP Similarly, to filter vertices whose C{type} is in a list of predefined types: >>> list_of_types = ["HR", "Finance", "Management"] >>> g.vs.select(type_in=list_of_types) #doctest: +SKIP If the operator is omitted, it defaults to C{eq}. For instance, the following selector selects vertices whose C{cluster} property equals to 2: >>> g.vs.select(cluster=2) #doctest: +SKIP In the case of an unknown operator, it is assumed that the recognized operator is part of the attribute name and the actual operator is C{eq}. Attribute names inferred from keyword arguments are treated specially if they start with an underscore (C{_}). These are not real attributes but refer to specific properties of the vertices, e.g., its degree. The rule is as follows: if an attribute name starts with an underscore, the rest of the name is interpreted as a method of the L{Graph} object. This method is called with the vertex sequence as its first argument (all others left at default values) and vertices are filtered according to the value returned by the method. For instance, if you want to exclude isolated vertices: >>> g = Graph.Famous("zachary") >>> non_isolated = g.vs.select(_degree_gt=0) For properties that take a long time to be computed (e.g., betweenness centrality for large graphs), it is advised to calculate the values in advance and store it in a graph attribute. The same applies when you are selecting based on the same property more than once in the same C{select()} call to avoid calculating it twice unnecessarily. For instance, the following would calculate betweenness centralities twice: >>> edges = g.vs.select(_betweenness_gt=10, _betweenness_lt=30) It is advised to use this instead: >>> g.vs["bs"] = g.betweenness() >>> edges = g.vs.select(bs_gt=10, bs_lt=30) @return: the new, filtered vertex sequence""" vs = _igraph.VertexSeq.select(self, *args) operators = { "lt": operator.lt, \ "gt": operator.gt, \ "le": operator.le, \ "ge": operator.ge, \ "eq": operator.eq, \ "ne": operator.ne, \ "in": lambda a, b: a in b, \ "notin": lambda a, b: a not in b } for keyword, value in kwds.iteritems(): if "_" not in keyword or keyword.rindex("_") == 0: keyword = keyword+"_eq" attr, _, op = keyword.rpartition("_") try: func = operators[op] except KeyError: # No such operator, assume that it's part of the attribute name attr, func = keyword, operators["eq"] if attr[0] == '_': # Method call, not an attribute values = getattr(vs.graph, attr[1:])(vs) else: values = vs[attr] filtered_idxs=[i for i, v in enumerate(values) if func(v, value)] vs = vs.select(filtered_idxs) return vs def __call__(self, *args, **kwds): """Shorthand notation to select() This method simply passes all its arguments to L{VertexSeq.select()}. """ return self.select(*args, **kwds) ############################################################## class EdgeSeq(_igraph.EdgeSeq): """Class representing a sequence of edges in the graph. This class is most easily accessed by the C{es} field of the L{Graph} object, which returns an ordered sequence of all edges in the graph. The edge sequence can be refined by invoking the L{EdgeSeq.select()} method. L{EdgeSeq.select()} can also be accessed by simply calling the L{EdgeSeq} object. An alternative way to create an edge sequence referring to a given graph is to use the constructor directly: >>> g = Graph.Full(3) >>> es = EdgeSeq(g) >>> restricted_es = EdgeSeq(g, [0, 1]) The individual edges can be accessed by indexing the edge sequence object. It can be used as an iterable as well, or even in a list comprehension: >>> g=Graph.Full(3) >>> for e in g.es: ... print e.tuple ... (0, 1) (0, 2) (1, 2) >>> [max(e.tuple) for e in g.es] [1, 2, 2] The edge sequence can also be used as a dictionary where the keys are the attribute names. The values corresponding to the keys are the values of the given attribute of every edge in the graph: >>> g=Graph.Full(3) >>> for idx, e in enumerate(g.es): ... e["weight"] = idx*(idx+1) ... >>> g.es["weight"] [0, 2, 6] >>> g.es["weight"] = range(3) >>> g.es["weight"] [0, 1, 2] If you specify a sequence that is shorter than the number of edges in the EdgeSeq, the sequence is reused: >>> g = Graph.Tree(7, 2) >>> g.es["color"] = ["red", "green"] >>> g.es["color"] ['red', 'green', 'red', 'green', 'red', 'green'] You can even pass a single string or integer, it will be considered as a sequence of length 1: >>> g.es["color"] = "red" >>> g.es["color"] ['red', 'red', 'red', 'red', 'red', 'red'] Some methods of the edge sequences are simply proxy methods to the corresponding methods in the L{Graph} object. One such example is L{EdgeSeq.is_multiple()}: >>> g=Graph(3, [(0,1), (1,0), (1,2)]) >>> g.es.is_multiple() [False, True, False] >>> g.es.is_multiple() == g.is_multiple() True """ def attributes(self): """Returns the list of all the edge attributes in the graph associated to this edge sequence.""" return self.graph.edge_attributes() def find(self, *args, **kwds): """Returns the first edge of the edge sequence that matches some criteria. The selection criteria are equal to the ones allowed by L{VertexSeq.select}. See L{VertexSeq.select} for more details. For instance, to find the first edge with weight larger than 5 in graph C{g}: >>> g.es.find(weight_gt=5) #doctest:+SKIP """ if args: # Selecting first based on positional arguments, then checking # the criteria specified by the keyword arguments edge = _igraph.EdgeSeq.find(self, *args) if not kwds: return edge es = self.graph.es[edge.index] else: es = self # Selecting based on positional arguments es = es.select(**kwds) if es: return es[0] raise ValueError("no such edge") def select(self, *args, **kwds): """Selects a subset of the edge sequence based on some criteria The selection criteria can be specified by the positional and the keyword arguments. Positional arguments are always processed before keyword arguments. - If the first positional argument is C{None}, an empty sequence is returned. - If the first positional argument is a callable object, the object will be called for every edge in the sequence. If it returns C{True}, the edge will be included, otherwise it will be excluded. - If the first positional argument is an iterable, it must return integers and they will be considered as indices of the current edge set (NOT the whole edge set of the graph -- the difference matters when one filters an edge set that has already been filtered by a previous invocation of L{EdgeSeq.select()}. In this case, the indices do not refer directly to the edges of the graph but to the elements of the filtered edge sequence. - If the first positional argument is an integer, all remaining arguments are expected to be integers. They are considered as indices of the current edge set again. Keyword arguments can be used to filter the edges based on their attributes and properties. The name of the keyword specifies the name of the attribute and the filtering operator, they should be concatenated by an underscore (C{_}) character. Attribute names can also contain underscores, but operator names don't, so the operator is always the largest trailing substring of the keyword name that does not contain an underscore. Possible operators are: - C{eq}: equal to - C{ne}: not equal to - C{lt}: less than - C{gt}: greater than - C{le}: less than or equal to - C{ge}: greater than or equal to - C{in}: checks if the value of an attribute is in a given list - C{notin}: checks if the value of an attribute is not in a given list For instance, if you want to filter edges with a numeric C{weight} property larger than 50, you have to write: >>> g.es.select(weight_gt=50) #doctest: +SKIP Similarly, to filter edges whose C{type} is in a list of predefined types: >>> list_of_types = ["inhibitory", "excitatory"] >>> g.es.select(type_in=list_of_types) #doctest: +SKIP If the operator is omitted, it defaults to C{eq}. For instance, the following selector selects edges whose C{type} property is C{intracluster}: >>> g.es.select(type="intracluster") #doctest: +SKIP In the case of an unknown operator, it is assumed that the recognized operator is part of the attribute name and the actual operator is C{eq}. Keyword arguments are treated specially if they start with an underscore (C{_}). These are not real attributes but refer to specific properties of the edges, e.g., their centrality. The rules are as follows: 1. C{_source} or {_from} means the source vertex of an edge. 2. C{_target} or {_to} means the target vertex of an edge. 3. C{_within} ignores the operator and checks whether both endpoints of the edge lie within a specified set. 4. C{_between} ignores the operator and checks whether I{one} endpoint of the edge lies within a specified set and the I{other} endpoint lies within another specified set. The two sets must be given as a tuple. 5. Otherwise, the rest of the name is interpreted as a method of the L{Graph} object. This method is called with the edge sequence as its first argument (all others left at default values) and edges are filtered according to the value returned by the method. For instance, if you want to exclude edges with a betweenness centrality less than 2: >>> g = Graph.Famous("zachary") >>> excl = g.es.select(_edge_betweenness_ge = 2) To select edges originating from vertices 2 and 4: >>> edges = g.es.select(_source_in = [2, 4]) To select edges lying entirely within the subgraph spanned by vertices 2, 3, 4 and 7: >>> edges = g.es.select(_within = [2, 3, 4, 7]) To select edges with one endpoint in the vertex set containing vertices 2, 3, 4 and 7 and the other endpoint in the vertex set containing vertices 8 and 9: >>> edges = g.es.select(_between = ([2, 3, 4, 7], [8, 9])) For properties that take a long time to be computed (e.g., betweenness centrality for large graphs), it is advised to calculate the values in advance and store it in a graph attribute. The same applies when you are selecting based on the same property more than once in the same C{select()} call to avoid calculating it twice unnecessarily. For instance, the following would calculate betweenness centralities twice: >>> edges = g.es.select(_edge_betweenness_gt=10, # doctest:+SKIP ... _edge_betweenness_lt=30) It is advised to use this instead: >>> g.es["bs"] = g.edge_betweenness() >>> edges = g.es.select(bs_gt=10, bs_lt=30) @return: the new, filtered edge sequence""" es = _igraph.EdgeSeq.select(self, *args) def _ensure_set(value): if isinstance(value, VertexSeq): value = set(v.index for v in value) elif not isinstance(value, (set, frozenset)): value = set(value) return value operators = { "lt": operator.lt, \ "gt": operator.gt, \ "le": operator.le, \ "ge": operator.ge, \ "eq": operator.eq, \ "ne": operator.ne, \ "in": lambda a, b: a in b, \ "notin": lambda a, b: a not in b } for keyword, value in kwds.iteritems(): if "_" not in keyword or keyword.rindex("_") == 0: keyword = keyword+"_eq" pos = keyword.rindex("_") attr, op = keyword[0:pos], keyword[pos+1:] try: func = operators[op] except KeyError: # No such operator, assume that it's part of the attribute name attr = "%s_%s" % (attr,op) func = operators["eq"] if attr[0] == '_': if attr == "_source" or attr == "_from": values = [e.source for e in es] if op == "in" or op == "notin": value = _ensure_set(value) elif attr == "_target" or attr == "_to": values = [e.target for e in es] if op == "in" or op == "notin": value = _ensure_set(value) elif attr == "_within": func = None # ignoring function, filtering here value = _ensure_set(value) # Fetch all the edges that are incident on at least one of # the vertices specified candidates = set() for v in value: candidates.update(es.graph.incident(v)) if not es.is_all(): # Find those where both endpoints are OK filtered_idxs = [i for i, e in enumerate(es) if e.index in candidates and e.source in value and e.target in value] else: # Optimized version when the edge sequence contains all the edges # exactly once in increasing order of edge IDs filtered_idxs = [i for i in candidates if es[i].source in value and es[i].target in value] elif attr == "_between": if len(value) != 2: raise ValueError("_between selector requires two vertex ID lists") func = None # ignoring function, filtering here set1 = _ensure_set(value[0]) set2 = _ensure_set(value[1]) # Fetch all the edges that are incident on at least one of # the vertices specified candidates = set() for v in set1: candidates.update(es.graph.incident(v)) for v in set2: candidates.update(es.graph.incident(v)) if not es.is_all(): # Find those where both endpoints are OK filtered_idxs = [i for i, e in enumerate(es) if (e.source in set1 and e.target in set2) or (e.target in set1 and e.source in set2)] else: # Optimized version when the edge sequence contains all the edges # exactly once in increasing order of edge IDs filtered_idxs = [i for i in candidates if (es[i].source in set1 and es[i].target in set2) or (es[i].target in set1 and es[i].source in set2)] else: # Method call, not an attribute values = getattr(es.graph, attr[1:])(es) else: values = es[attr] # If we have a function to apply on the values, do that; otherwise # we assume that filtered_idxs has already been calculated. if func is not None: filtered_idxs=[i for i, v in enumerate(values) \ if func(v, value)] es = es.select(filtered_idxs) return es def __call__(self, *args, **kwds): """Shorthand notation to select() This method simply passes all its arguments to L{EdgeSeq.select()}. """ return self.select(*args, **kwds) ############################################################## # Additional methods of VertexSeq and EdgeSeq that call Graph methods def _graphmethod(func=None, name=None): """Auxiliary decorator This decorator allows some methods of L{VertexSeq} and L{EdgeSeq} to call their respective counterparts in L{Graph} to avoid code duplication. @param func: the function being decorated. This function will be called on the results of the original L{Graph} method. If C{None}, defaults to the identity function. @param name: the name of the corresponding method in L{Graph}. If C{None}, it defaults to the name of the decorated function. @return: the decorated function """ if name is None: name = func.__name__ method = getattr(Graph, name) if hasattr(func, "__call__"): def decorated(*args, **kwds): self = args[0].graph return func(args[0], method(self, *args, **kwds)) else: def decorated(*args, **kwds): self = args[0].graph return method(self, *args, **kwds) decorated.__name__ = name decorated.__doc__ = """Proxy method to L{Graph.%(name)s()} This method calls the C{%(name)s()} method of the L{Graph} class restricted to this sequence, and returns the result. @see: Graph.%(name)s() for details. """ % { "name": name } return decorated def _add_proxy_methods(): # Proxy methods for VertexSeq and EdgeSeq that forward their arguments to # the corresponding Graph method are constructed here. Proxy methods for # Vertex and Edge are added in the C source code. Make sure that you update # the C source whenever you add a proxy method here if that makes sense for # an individual vertex or edge decorated_methods = {} decorated_methods[VertexSeq] = \ ["degree", "betweenness", "bibcoupling", "closeness", "cocitation", "constraint", "diversity", "eccentricity", "get_shortest_paths", "maxdegree", "pagerank", "personalized_pagerank", "shortest_paths", "similarity_dice", "similarity_jaccard", "subgraph", "indegree", "outdegree", "isoclass", "delete_vertices", "is_separator", "is_minimal_separator"] decorated_methods[EdgeSeq] = \ ["count_multiple", "delete_edges", "is_loop", "is_multiple", "is_mutual", "subgraph_edges"] rename_methods = {} rename_methods[VertexSeq] = { "delete_vertices": "delete" } rename_methods[EdgeSeq] = { "delete_edges": "delete", "subgraph_edges": "subgraph" } for klass, methods in decorated_methods.iteritems(): for method in methods: new_method_name = rename_methods[klass].get(method, method) setattr(klass, new_method_name, _graphmethod(None, method)) setattr(EdgeSeq, "edge_betweenness", _graphmethod( \ lambda self, result: [result[i] for i in self.indices], "edge_betweenness")) _add_proxy_methods() ############################################################## # Making sure that layout methods always return a Layout def _layout_method_wrapper(func): """Wraps an existing layout method to ensure that it returns a Layout instead of a list of lists. @param func: the method to wrap. Must be a method of the Graph object. @return: a new method """ def result(*args, **kwds): layout = func(*args, **kwds) if not isinstance(layout, Layout): layout = Layout(layout) return layout result.__name__ = func.__name__ result.__doc__ = func.__doc__ return result for name in dir(Graph): if not name.startswith("layout_"): continue if name in ("layout_auto", "layout_sugiyama"): continue setattr(Graph, name, _layout_method_wrapper(getattr(Graph, name))) ############################################################## # Adding aliases for the 3D versions of the layout methods def _3d_version_for(func): """Creates an alias for the 3D version of the given layout algoritm. This function is a decorator that creates a method which calls I{func} after attaching C{dim=3} to the list of keyword arguments. @param func: must be a method of the Graph object. @return: a new method """ def result(*args, **kwds): kwds["dim"] = 3 return func(*args, **kwds) result.__name__ = "%s_3d" % func.__name__ result.__doc__ = """Alias for L{%s()} with dim=3.\n\n@see: Graph.%s()""" \ % (func.__name__, func.__name__) return result Graph.layout_fruchterman_reingold_3d=_3d_version_for(Graph.layout_fruchterman_reingold) Graph.layout_kamada_kawai_3d=_3d_version_for(Graph.layout_kamada_kawai) Graph.layout_random_3d=_3d_version_for(Graph.layout_random) Graph.layout_grid_3d=_3d_version_for(Graph.layout_grid) Graph.layout_sphere=_3d_version_for(Graph.layout_circle) ############################################################## def autocurve(graph, attribute="curved", default=0): """Calculates curvature values for each of the edges in the graph to make sure that multiple edges are shown properly on a graph plot. This function checks the multiplicity of each edge in the graph and assigns curvature values (numbers between -1 and 1, corresponding to CCW (-1), straight (0) and CW (1) curved edges) to them. The assigned values are either stored in an edge attribute or returned as a list, depending on the value of the I{attribute} argument. @param graph: the graph on which the calculation will be run @param attribute: the name of the edge attribute to save the curvature values to. The default value is C{curved}, which is the name of the edge attribute the default graph plotter checks to decide whether an edge should be curved on the plot or not. If I{attribute} is C{None}, the result will not be stored. @param default: the default curvature for single edges. Zero means that single edges will be straight. If you want single edges to be curved as well, try passing 0.5 or -0.5 here. @return: the list of curvature values if I{attribute} is C{None}, otherwise C{None}. """ # The following loop could be re-written in C if it turns out to be a # bottleneck. Unfortunately we cannot use Graph.count_multiple() here # because we have to ignore edge directions. multiplicities = defaultdict(list) for edge in graph.es: u, v = edge.tuple if u > v: multiplicities[v, u].append(edge.index) else: multiplicities[u, v].append(edge.index) result = [default] * graph.ecount() for pair, eids in multiplicities.iteritems(): # Is it a single edge? if len(eids) < 2: continue if len(eids) % 2 == 1: # Odd number of edges; the last will be straight result[eids.pop()] = 0 # Arrange the remaining edges curve = 2.0 / (len(eids) + 2) dcurve, sign = curve, 1 for idx, eid in enumerate(eids): edge = graph.es[eid] if edge.source > edge.target: result[eid] = -sign*curve else: result[eid] = sign*curve if idx % 2 == 1: curve += dcurve sign *= -1 if attribute is None: return result graph.es[attribute] = result def read(filename, *args, **kwds): """Loads a graph from the given filename. This is just a convenience function, calls L{Graph.Read} directly. All arguments are passed unchanged to L{Graph.Read} @param filename: the name of the file to be loaded """ return Graph.Read(filename, *args, **kwds) load=read def write(graph, filename, *args, **kwds): """Saves a graph to the given file. This is just a convenience function, calls L{Graph.write} directly. All arguments are passed unchanged to L{Graph.write} @param graph: the graph to be saved @param filename: the name of the file to be written """ return graph.write(filename, *args, **kwds) save=write def summary(obj, stream=None, *args, **kwds): """Prints a summary of object o to a given stream Positional and keyword arguments not explicitly mentioned here are passed on to the underlying C{summary()} method of the object if it has any. @param obj: the object about which a human-readable summary is requested. @param stream: the stream to be used. If C{None}, the standard output will be used. """ if stream is None: stream = sys.stdout if hasattr(obj, "summary"): stream.write(obj.summary(*args, **kwds)) else: stream.write(str(obj)) stream.write("\n") config = configuration.init() del construct_graph_from_formula python-igraph-0.7.1.post6/igraph/app/0000755000076500000240000000000012534343010020015 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/igraph/app/__init__.py0000644000076500000240000000004112453614202022125 0ustar ntamasstaff00000000000000"""User interfaces of igraph""" python-igraph-0.7.1.post6/igraph/app/shell.py0000644000076500000240000004374312460534506021523 0ustar ntamasstaff00000000000000"""Command-line user interface of igraph The command-line interface launches a Python shell with the igraph module automatically imported into the main namespace. This is mostly a convenience module and it is used only by the C{igraph} command line script which executes a suitable Python shell and automatically imports C{igraph}'s classes and functions in the top-level namespace. Supported Python shells are: - IDLE shell (class L{IDLEShell}) - IPython shell (class L{IPythonShell}) - Classic Python shell (class L{ClassicPythonShell}) The shells are tried in the above mentioned preference order one by one, unless the C{global.shells} configuration key is set which overrides the default order. IDLE shell is only tried in Windows unless explicitly stated by C{global.shells}, since Linux and Mac OS X users are likely to invoke igraph from the command line. """ # pylint: disable-msg=W0401 # W0401: wildcard import. That's exactly what we need for the shell. from igraph import __version__, set_progress_handler, set_status_handler from igraph.configuration import Configuration import sys, re # pylint: disable-msg=C0103,R0903 # C0103: invalid name. Disabled because this is a third-party class. # R0903: too few public methods. class TerminalController: """ A class that can be used to portably generate formatted output to a terminal. `TerminalController` defines a set of instance variables whose values are initialized to the control sequence necessary to perform a given action. These can be simply included in normal output to the terminal: >>> term = TerminalController() >>> print 'This is '+term.GREEN+'green'+term.NORMAL This is green Alternatively, the `render()` method can used, which replaces '${action}' with the string required to perform 'action': >>> term = TerminalController() >>> print term.render('This is ${GREEN}green${NORMAL}') This is green If the terminal doesn't support a given action, then the value of the corresponding instance variable will be set to ''. As a result, the above code will still work on terminals that do not support color, except that their output will not be colored. Also, this means that you can test whether the terminal supports a given action by simply testing the truth value of the corresponding instance variable: >>> term = TerminalController() >>> if term.CLEAR_SCREEN: ... print 'This terminal supports clearning the screen.' ... Finally, if the width and height of the terminal are known, then they will be stored in the `COLS` and `LINES` attributes. @author: Edward Loper """ # Cursor movement: BOL = '' #: Move the cursor to the beginning of the line UP = '' #: Move the cursor up one line DOWN = '' #: Move the cursor down one line LEFT = '' #: Move the cursor left one char RIGHT = '' #: Move the cursor right one char # Deletion: CLEAR_SCREEN = '' #: Clear the screen and move to home position CLEAR_EOL = '' #: Clear to the end of the line. CLEAR_BOL = '' #: Clear to the beginning of the line. CLEAR_EOS = '' #: Clear to the end of the screen # Output modes: BOLD = '' #: Turn on bold mode BLINK = '' #: Turn on blink mode DIM = '' #: Turn on half-bright mode REVERSE = '' #: Turn on reverse-video mode NORMAL = '' #: Turn off all modes # Cursor display: HIDE_CURSOR = '' #: Make the cursor invisible SHOW_CURSOR = '' #: Make the cursor visible # Terminal size: COLS = None #: Width of the terminal (None for unknown) LINES = None #: Height of the terminal (None for unknown) # Foreground colors: BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' # Background colors: BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = '' BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = '' _STRING_CAPABILITIES = """ BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0 HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split() _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() def __init__(self, term_stream=sys.stdout): """ Create a `TerminalController` and initialize its attributes with appropriate values for the current terminal. `term_stream` is the stream that will be used for terminal output; if this stream is not a tty, then the terminal is assumed to be a dumb terminal (i.e., have no capabilities). """ # Curses isn't available on all platforms try: import curses except ImportError: return # If the stream isn't a tty, then assume it has no capabilities. if not term_stream.isatty(): return # Check the terminal type. If we fail, then assume that the # terminal has no capabilities. try: curses.setupterm() except StandardError: return # Look up numeric capabilities. self.COLS = curses.tigetnum('cols') self.LINES = curses.tigetnum('lines') # Look up string capabilities. for capability in self._STRING_CAPABILITIES: (attrib, cap_name) = capability.split('=') setattr(self, attrib, self._tigetstr(cap_name) or '') # Colors set_fg = self._tigetstr('setf') if set_fg: for i, color in zip(range(len(self._COLORS)), self._COLORS): setattr(self, color, curses.tparm(set_fg, i) or '') set_fg_ansi = self._tigetstr('setaf') if set_fg_ansi: for i, color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): setattr(self, color, curses.tparm(set_fg_ansi, i) or '') set_bg = self._tigetstr('setb') if set_bg: for i, color in zip(range(len(self._COLORS)), self._COLORS): setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '') set_bg_ansi = self._tigetstr('setab') if set_bg_ansi: for i, color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '') @staticmethod def _tigetstr(cap_name): """Rewrites string capabilities to remove "delays" which are not required for modern terminals""" # String capabilities can include "delays" of the form "$<2>". # For any modern terminal, we should be able to just ignore # these, so strip them out. import curses cap = curses.tigetstr(cap_name) or '' return re.sub(r'\$<\d+>[/*]?', '', cap) def render(self, template): """ Replace each $-substitutions in the given template string with the corresponding terminal control string (if it's defined) or '' (if it's not). """ return re.sub(r'\$\$|\${\w+}', self._render_sub, template) def _render_sub(self, match): """Helper function for L{render}""" s = match.group() if s == '$$': return s else: return getattr(self, s[2:-1]) class ProgressBar: """ A 2-line progress bar, which looks like:: Header 20% [===========----------------------------------] The progress bar is colored, if the terminal supports color output; and adjusts to the width of the terminal. """ BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}' HEADER = '${BOLD}${CYAN}%s${NORMAL}\n' def __init__(self, term): self.term = term if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL): raise ValueError("Terminal isn't capable enough -- you " "should use a simpler progress display.") self.width = self.term.COLS or 75 self.progress_bar = term.render(self.BAR) self.header = self.term.render(self.HEADER % "".center(self.width)) self.cleared = True #: true if we haven't drawn the bar yet. self.last_percent = 0 self.last_message = "" def update(self, percent=None, message=None): """Updates the progress bar. @param percent: the percentage to be shown. If C{None}, the previous value will be used. @param message: the message to be shown above the progress bar. If C{None}, the previous message will be used. """ if self.cleared: sys.stdout.write("\n"+self.header) self.cleared = False if message is None: message = self.last_message else: self.last_message = message if percent is None: percent = self.last_percent else: self.last_percent = percent n = int((self.width-10)*(percent/100.0)) sys.stdout.write( self.term.BOL + self.term.UP + self.term.UP + self.term.CLEAR_EOL + self.term.render(self.HEADER % message.center(self.width)) + (self.progress_bar % (percent, '='*n, '-'*(self.width-10-n))) + "\n" ) def update_message(self, message): """Updates the message of the progress bar. @param message: the message to be shown above the progress bar """ return self.update(message=message.strip()) def clear(self): """Clears the progress bar (i.e. removes it from the screen)""" if not self.cleared: sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL + self.term.UP + self.term.CLEAR_EOL + self.term.UP + self.term.CLEAR_EOL) self.cleared = True self.last_percent = 0 self.last_message = "" class Shell(object): """Superclass of the embeddable shells supported by igraph""" def __init__(self): pass def __call__(self): raise NotImplementedError("abstract class") def supports_progress_bar(self): """Checks whether the shell supports progress bars. This is done by checking for the existence of an attribute called C{_progress_handler}.""" return hasattr(self, "_progress_handler") def supports_status_messages(self): """Checks whether the shell supports status messages. This is done by checking for the existence of an attribute called C{_status_handler}.""" return hasattr(self, "_status_handler") # pylint: disable-msg=E1101 def get_progress_handler(self): """Returns the progress handler (if exists) or None (if not).""" if self.supports_progress_bar(): return self._progress_handler return None # pylint: disable-msg=E1101 def get_status_handler(self): """Returns the status handler (if exists) or None (if not).""" if self.supports_status_messages(): return self._status_handler return None class IDLEShell(Shell): """IDLE embedded shell interface. This class allows igraph to be embedded in IDLE (the Tk Python IDE). @todo: no progress bar support yet. Shell/Restart Shell command should re-import igraph again.""" def __init__(self): """Constructor. Imports IDLE's embedded shell. The implementation of this method is ripped from idlelib.PyShell.main() after removing the unnecessary parts.""" Shell.__init__(self) import idlelib.PyShell idlelib.PyShell.use_subprocess = True try: sys.ps1 except AttributeError: sys.ps1 = '>>> ' root = idlelib.PyShell.Tk(className="Idle") idlelib.PyShell.fixwordbreaks(root) root.withdraw() flist = idlelib.PyShell.PyShellFileList(root) if not flist.open_shell(): raise NotImplementedError self._shell = flist.pyshell self._root = root def __call__(self): """Starts the shell""" self._shell.interp.execsource("from igraph import *") self._root.mainloop() self._root.destroy() class ConsoleProgressBarMixin(object): """Mixin class for console shells that support a progress bar.""" def __init__(self): try: self.__class__.progress_bar = ProgressBar(TerminalController()) except ValueError: # Terminal is not capable enough, disable progress handler self._disable_handlers() except TypeError: # Probably running in Python 3.x and we hit a str/bytes issue; # as a temporary solution, disable the progress handler self._disable_handlers() def _disable_handlers(self): """Disables the status and progress handlers if the terminal is not capable enough.""" try: del self.__class__._progress_handler except AttributeError: pass try: del self.__class__._status_handler except AttributeError: pass @classmethod def _progress_handler(cls, message, percentage): """Progress bar handler, called when C{igraph} reports the progress of an operation @param message: message provided by C{igraph} @param percentage: percentage provided by C{igraph} """ if percentage >= 100: cls.progress_bar.clear() else: cls.progress_bar.update(percentage, message) @classmethod def _status_handler(cls, message): """Status message handler, called when C{igraph} sends a status message to be displayed. @param message: message provided by C{igraph} """ cls.progress_bar.update_message(message) class IPythonShell(Shell, ConsoleProgressBarMixin): """IPython embedded shell interface. This class allows igraph to be embedded in IPython's interactive shell.""" def __init__(self): """Constructor. Imports IPython's embedded shell with separator lines removed.""" Shell.__init__(self) ConsoleProgressBarMixin.__init__(self) # We cannot use IPShellEmbed here because generator expressions do not # work there (e.g., set(g.degree(x) for x in [1,2,3])) where g comes # from an external context import sys from IPython import __version__ as ipython_version self.ipython_version = ipython_version try: # IPython >= 0.11 supports this from IPython.frontend.terminal.ipapp import TerminalIPythonApp self._shell = TerminalIPythonApp.instance() sys.argv.append("--nosep") except ImportError: # IPython 0.10 and earlier import IPython.Shell self._shell = IPython.Shell.start() self._shell.IP.runsource("from igraph import *") sys.argv.append("-nosep") def __call__(self): """Starts the embedded shell.""" print "igraph %s running inside " % __version__, if self._shell.__class__.__name__ == "TerminalIPythonApp": self._shell.initialize() self._shell.shell.ex("from igraph import *") self._shell.start() else: self._shell.mainloop() class ClassicPythonShell(Shell, ConsoleProgressBarMixin): """Classic Python shell interface. This class allows igraph to be embedded in Python's shell.""" def __init__(self): """Constructor. Imports Python's classic shell""" Shell.__init__(self) ConsoleProgressBarMixin.__init__(self) self._shell = None def __call__(self): """Starts the embedded shell.""" if self._shell is None: from code import InteractiveConsole self._shell = InteractiveConsole() print >> sys.stderr, "igraph %s running inside " % __version__, self._shell.runsource("from igraph import *") self._shell.interact() def main(): """The main entry point for igraph when invoked from the command line shell""" config = Configuration.instance() if config.filename: print >> sys.stderr, "Using configuration from %s" % config.filename else: print >> sys.stderr, "No configuration file, using defaults" if config.has_key("shells"): parts = [part.strip() for part in config["shells"].split(",")] shell_classes = [] available_classes = dict([(k, v) for k, v in globals().iteritems() \ if isinstance(v, type) and issubclass(v, Shell)]) for part in parts: klass = available_classes.get(part, None) if klass is None: print >> sys.stderr, "Warning: unknown shell class `%s'" % part continue shell_classes.append(klass) else: shell_classes = [IPythonShell, ClassicPythonShell] import platform if platform.system() == "Windows": shell_classes.insert(0, IDLEShell) shell = None for shell_class in shell_classes: # pylint: disable-msg=W0703 # W0703: catch "Exception" try: shell = shell_class() break except StandardError: # Try the next one if "Classic" in str(shell_class): raise pass if isinstance(shell, Shell): if config["verbose"]: if shell.supports_progress_bar(): set_progress_handler(shell.get_progress_handler()) if shell.supports_status_messages(): set_status_handler(shell.get_status_handler()) shell() else: print >> sys.stderr, "No suitable Python shell was found." print >> sys.stderr, "Check configuration variable `general.shells'." if __name__ == '__main__': sys.exit(main()) python-igraph-0.7.1.post6/igraph/clustering.py0000644000076500000240000020075112534143111021773 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """Classes related to graph clustering. @undocumented: _handle_mark_groups_arg_for_clustering, _prepare_community_comparison""" __license__ = u""" Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ from copy import deepcopy from itertools import izip from math import pi from cStringIO import StringIO from igraph import community_to_membership from igraph.compat import property from igraph.configuration import Configuration from igraph.datatypes import UniqueIdGenerator from igraph.drawing.colors import ClusterColoringPalette from igraph.statistics import Histogram from igraph.summary import _get_wrapper_for_width from igraph.utils import str_to_orientation class Clustering(object): """Class representing a clustering of an arbitrary ordered set. This is now used as a base for L{VertexClustering}, but it might be useful for other purposes as well. Members of an individual cluster can be accessed by the C{[]} operator: >>> cl = Clustering([0,0,0,0,1,1,1,2,2,2,2]) >>> cl[0] [0, 1, 2, 3] The membership vector can be accessed by the C{membership} property: >>> cl.membership [0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2] The number of clusters can be retrieved by the C{len} function: >>> len(cl) 3 You can iterate over the clustering object as if it were a regular list of clusters: >>> for cluster in cl: ... print " ".join(str(idx) for idx in cluster) ... 0 1 2 3 4 5 6 7 8 9 10 If you need all the clusters at once as lists, you can simply convert the clustering object to a list: >>> cluster_list = list(cl) >>> print cluster_list [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10]] @undocumented: _formatted_cluster_iterator """ def __init__(self, membership, params = None): """Constructor. @param membership: the membership list -- that is, the cluster index in which each element of the set belongs to. @param params: additional parameters to be stored in this object's dictionary.""" self._membership = list(membership) if len(self._membership)>0: self._len = max(m for m in self._membership if m is not None)+1 else: self._len = 0 if params: self.__dict__.update(params) def __getitem__(self, idx): """Returns the members of the specified cluster. @param idx: the index of the cluster @return: the members of the specified cluster as a list @raise IndexError: if the index is out of bounds""" if idx < 0 or idx >= self._len: raise IndexError("cluster index out of range") return [i for i, e in enumerate(self._membership) if e == idx] def __iter__(self): """Iterates over the clusters in this clustering. This method will return a generator that generates the clusters one by one.""" clusters = [[] for _ in xrange(self._len)] for idx, cluster in enumerate(self._membership): clusters[cluster].append(idx) return iter(clusters) def __len__(self): """Returns the number of clusters. @return: the number of clusters """ return self._len def __str__(self): return self.summary(verbosity=1, width=78) def as_cover(self): """Returns a L{Cover} that contains the same clusters as this clustering.""" return Cover(self._graph, self) def compare_to(self, other, *args, **kwds): """Compares this clustering to another one using some similarity or distance metric. This is a convenience method that simply calls L{compare_communities} with the two clusterings as arguments. Any extra positional or keyword argument is also forwarded to L{compare_communities}.""" return compare_communities(self, other, *args, **kwds) @property def membership(self): """Returns the membership vector.""" return self._membership[:] @property def n(self): """Returns the number of elements covered by this clustering.""" return len(self._membership) def size(self, idx): """Returns the size of a given cluster. @param idx: the cluster in which we are interested. """ return len(self[idx]) def sizes(self, *args): """Returns the size of given clusters. The indices are given as positional arguments. If there are no positional arguments, the function will return the sizes of all clusters. """ counts = [0] * len(self) for x in self._membership: counts[x] += 1 if args: return [counts[idx] for idx in args] return counts def size_histogram(self, bin_width = 1): """Returns the histogram of cluster sizes. @param bin_width: the bin width of the histogram @return: a L{Histogram} object """ return Histogram(bin_width, self.sizes()) def summary(self, verbosity=0, width=None): """Returns the summary of the clustering. The summary includes the number of items and clusters, and also the list of members for each of the clusters if the verbosity is nonzero. @param verbosity: determines whether the cluster members should be printed. Zero verbosity prints the number of items and clusters only. @return: the summary of the clustering as a string. """ out = StringIO() print >>out, "Clustering with %d elements and %d clusters" % \ (len(self._membership), len(self)) if verbosity < 1: return out.getvalue().strip() ndigits = len(str(len(self))) wrapper = _get_wrapper_for_width(width, subsequent_indent = " " * (ndigits+3)) for idx, cluster in enumerate(self._formatted_cluster_iterator()): wrapper.initial_indent = "[%*d] " % (ndigits, idx) print >>out, "\n".join(wrapper.wrap(cluster)) return out.getvalue().strip() def _formatted_cluster_iterator(self): """Iterates over the clusters and formats them into a string to be presented in the summary.""" for cluster in self: yield ", ".join(str(member) for member in cluster) class VertexClustering(Clustering): """The clustering of the vertex set of a graph. This class extends L{Clustering} by linking it to a specific L{Graph} object and by optionally storing the modularity score of the clustering. It also provides some handy methods like getting the subgraph corresponding to a cluster and such. @note: since this class is linked to a L{Graph}, destroying the graph by the C{del} operator does not free the memory occupied by the graph if there exists a L{VertexClustering} that references the L{Graph}. @undocumented: _formatted_cluster_iterator """ # Allow None to be passed to __plot__ as the "palette" keyword argument _default_palette = None def __init__(self, graph, membership = None, modularity = None, \ params = None, modularity_params = None): """Creates a clustering object for a given graph. @param graph: the graph that will be associated to the clustering @param membership: the membership list. The length of the list must be equal to the number of vertices in the graph. If C{None}, every vertex is assumed to belong to the same cluster. @param modularity: the modularity score of the clustering. If C{None}, it will be calculated when needed. @param params: additional parameters to be stored in this object. @param modularity_params: arguments that should be passed to L{Graph.modularity} when the modularity is (re)calculated. If the original graph was weighted, you should pass a dictionary containing a C{weight} key with the appropriate value here. """ if membership is None: Clustering.__init__(self, [0]*graph.vcount(), params) else: if len(membership) != graph.vcount(): raise ValueError("membership list has invalid length") Clustering.__init__(self, membership, params) self._graph = graph self._modularity = modularity self._modularity_dirty = modularity is None if modularity_params is None: self._modularity_params = {} else: self._modularity_params = dict(modularity_params) # pylint: disable-msg=C0103 @classmethod def FromAttribute(cls, graph, attribute, intervals=None, params=None): """Creates a vertex clustering based on the value of a vertex attribute. Vertices having the same attribute will correspond to the same cluster. @param graph: the graph on which we are working @param attribute: name of the attribute on which the clustering is based. @param intervals: for numeric attributes, you can either pass a single number or a list of numbers here. A single number means that the vertices will be put in bins of that width and vertices ending up in the same bin will be in the same cluster. A list of numbers specify the bin positions explicitly; e.g., C{[10, 20, 30]} means that there will be four categories: vertices with the attribute value less than 10, between 10 and 20, between 20 and 30 and over 30. Intervals are closed from the left and open from the right. @param params: additional parameters to be stored in this object. @return: a new VertexClustering object """ from bisect import bisect def safeintdiv(x, y): """Safe integer division that handles None gracefully""" if x is None: return None return int(x / y) def safebisect(intervals, x): """Safe list bisection that handles None gracefully""" if x is None: return None return bisect(intervals, x) try: _ = iter(intervals) iterable = True except TypeError: iterable = False if intervals is None: vec = graph.vs[attribute] elif iterable: intervals = list(intervals) vec = [safebisect(intervals, x) for x in graph.vs[attribute]] else: intervals = float(intervals) vec = [safeintdiv(x, intervals) for x in graph.vs[attribute]] idgen = UniqueIdGenerator() idgen[None] = None vec = [idgen[i] for i in vec] return cls(graph, vec, None, params) def as_cover(self): """Returns a L{VertexCover} that contains the same clusters as this clustering.""" return VertexCover(self._graph, self) def cluster_graph(self, combine_vertices=None, combine_edges=None): """Returns a graph where each cluster is contracted into a single vertex. In the resulting graph, vertex M{i} represents cluster M{i} in this clustering. Vertex M{i} and M{j} will be connected if there was at least one connected vertex pair M{(a, b)} in the original graph such that vertex M{a} was in cluster M{i} and vertex M{b} was in cluster M{j}. @param combine_vertices: specifies how to derive the attributes of the vertices in the new graph from the attributes of the old ones. See L{Graph.contract_vertices()} for more details. @param combine_edges: specifies how to derive the attributes of the edges in the new graph from the attributes of the old ones. See L{Graph.simplify()} for more details. If you specify C{False} here, edges will not be combined, and the number of edges between the vertices representing the original clusters will be equal to the number of edges between the members of those clusters in the original graph. @return: the new graph. """ result = self.graph.copy() result.contract_vertices(self.membership, combine_vertices) if combine_edges != False: result.simplify(combine_edges=combine_edges) return result def crossing(self): """Returns a boolean vector where element M{i} is C{True} iff edge M{i} lies between clusters, C{False} otherwise.""" membership = self.membership return [membership[v1] != membership[v2] \ for v1, v2 in self.graph.get_edgelist()] @property def modularity(self): """Returns the modularity score""" if self._modularity_dirty: return self._recalculate_modularity_safe() return self._modularity q = modularity @property def graph(self): """Returns the graph belonging to this object""" return self._graph def recalculate_modularity(self): """Recalculates the stored modularity value. This method must be called before querying the modularity score of the clustering through the class member C{modularity} or C{q} if the graph has been modified (edges have been added or removed) since the creation of the L{VertexClustering} object. @return: the new modularity score """ self._modularity = self._graph.modularity(self._membership, **self._modularity_params) self._modularity_dirty = False return self._modularity def _recalculate_modularity_safe(self): """Recalculates the stored modularity value and swallows all exceptions raised by the modularity function (if any). @return: the new modularity score or C{None} if the modularity function could not be calculated. """ try: return self.recalculate_modularity() except: return None finally: self._modularity_dirty = False def subgraph(self, idx): """Get the subgraph belonging to a given cluster. @param idx: the cluster index @return: a copy of the subgraph @precondition: the vertex set of the graph hasn't been modified since the moment the clustering was constructed. """ return self._graph.subgraph(self[idx]) def subgraphs(self): """Gets all the subgraphs belonging to each of the clusters. @return: a list containing copies of the subgraphs @precondition: the vertex set of the graph hasn't been modified since the moment the clustering was constructed. """ return [self._graph.subgraph(cl) for cl in self] def giant(self): """Returns the giant community of the clustered graph. The giant component a community for which no larger community exists. @note: there can be multiple giant communities, this method will return the copy of an arbitrary one if there are multiple giant communities. @return: a copy of the giant community. @precondition: the vertex set of the graph hasn't been modified since the moment the clustering was constructed. """ ss = self.sizes() max_size = max(ss) return self.subgraph(ss.index(max_size)) def __plot__(self, context, bbox, palette, *args, **kwds): """Plots the clustering to the given Cairo context in the given bounding box. This is done by calling L{Graph.__plot__()} with the same arguments, but coloring the graph vertices according to the current clustering (unless overridden by the C{vertex_color} argument explicitly). This method understands all the positional and keyword arguments that are understood by L{Graph.__plot__()}, only the differences will be highlighted here: - C{mark_groups}: whether to highlight some of the vertex groups by colored polygons. Besides the values accepted by L{Graph.__plot__} (i.e., a dict mapping colors to vertex indices, a list containing lists of vertex indices, or C{False}), the following are also accepted: - C{True}: all the groups will be highlighted, the colors matching the corresponding color indices from the current palette (see the C{palette} keyword argument of L{Graph.__plot__}. - A dict mapping cluster indices or tuples of vertex indices to color names. The given clusters or vertex groups will be highlighted by the given colors. - A list of cluster indices. This is equivalent to passing a dict mapping numeric color indices from the current palette to cluster indices; therefore, the cluster referred to by element I{i} of the list will be highlighted by color I{i} from the palette. The value of the C{plotting.mark_groups} configuration key is also taken into account here; if that configuration key is C{True} and C{mark_groups} is not given explicitly, it will automatically be set to C{True}. In place of lists of vertex indices, you may also use L{VertexSeq} instances. In place of color names, you may also use color indices into the current palette. C{None} as a color name will mean that the corresponding group is ignored. - C{palette}: the palette used to resolve numeric color indices to RGBA values. By default, this is an instance of L{ClusterColoringPalette}. @see: L{Graph.__plot__()} for more supported keyword arguments. """ if "edge_color" not in kwds and "color" not in self.graph.edge_attributes(): # Set up a default edge coloring based on internal vs external edges colors = ["grey20", "grey80"] kwds["edge_color"] = [colors[is_crossing] for is_crossing in self.crossing()] if palette is None: palette = ClusterColoringPalette(len(self)) if "mark_groups" not in kwds: if Configuration.instance()["plotting.mark_groups"]: kwds["mark_groups"] = ( (group, color) for color, group in enumerate(self) ) else: kwds["mark_groups"] = _handle_mark_groups_arg_for_clustering( kwds["mark_groups"], self) if "vertex_color" not in kwds: kwds["vertex_color"] = self.membership return self._graph.__plot__(context, bbox, palette, *args, **kwds) def _formatted_cluster_iterator(self): """Iterates over the clusters and formats them into a string to be presented in the summary.""" if self._graph.is_named(): names = self._graph.vs["name"] for cluster in self: yield ", ".join(str(names[member]) for member in cluster) else: for cluster in self: yield ", ".join(str(member) for member in cluster) ############################################################################### class Dendrogram(object): """The hierarchical clustering (dendrogram) of some dataset. A hierarchical clustering means that we know not only the way the elements are separated into groups, but also the exact history of how individual elements were joined into larger subgroups. This class internally represents the hierarchy by a matrix with n rows and 2 columns -- or more precisely, a list of lists of size 2. This is exactly the same as the original format used by C{igraph}'s C core. The M{i}th row of the matrix contains the indices of the two clusters being joined in time step M{i}. The joint group will be represented by the ID M{n+i}, with M{i} starting from one. The ID of the joint group will be referenced in the upcoming steps instead of any of its individual members. So, IDs less than or equal to M{n} (where M{n} is the number of rows in the matrix) mean the original members of the dataset (with ID from 0 to M{n}), while IDs up from M{n+1} mean joint groups. As an example, take a look at the dendrogram and the internal representation of a given clustering of five nodes:: 0 -+ | 1 -+-+ | 2 ---+-+ <====> [[0, 1], [3, 4], [2, 5], [6, 7]] | 3 -+ | | | 4 -+---+--- @undocumented: _item_box_size, _plot_item, _traverse_inorder """ def __init__(self, merges): """Creates a hierarchical clustering. @param merges: the merge history either in matrix or tuple format""" self._merges = [tuple(pair) for pair in merges] self._nmerges = len(self._merges) if self._nmerges: self._nitems = max(self._merges[-1])-self._nmerges+2 else: self._nitems = 0 self._names = None @staticmethod def _convert_matrix_to_tuple_repr(merges, n=None): """Converts the matrix representation of a clustering to a tuple representation. @param merges: the matrix representation of the clustering @return: the tuple representation of the clustering """ if n is None: n = len(merges)+1 tuple_repr = range(n) idxs = range(n) for rowidx, row in enumerate(merges): i, j = row try: idxi, idxj = idxs[i], idxs[j] tuple_repr[idxi] = (tuple_repr[idxi], tuple_repr[idxj]) tuple_repr[idxj] = None except IndexError: raise ValueError("malformed matrix, subgroup referenced "+ "before being created in step %d" % rowidx) idxs.append(j) return [x for x in tuple_repr if x is not None] def _traverse_inorder(self): """Conducts an inorder traversal of the merge tree. The inorder traversal returns the nodes on the last level in the order they should be drawn so that no edges cross each other. @return: the result of the inorder traversal in a list.""" result = [] seen_nodes = set() for node_index in reversed(xrange(self._nitems+self._nmerges)): if node_index in seen_nodes: continue stack = [node_index] while stack: last = stack.pop() seen_nodes.add(last) if last < self._nitems: # 'last' is a regular node so the traversal ends here, we # can append it to the results result.append(last) else: # 'last' is a merge node, so let us proceed with the entry # where this merge node was created stack.extend(self._merges[last-self._nitems]) return result def __str__(self): return self.summary(verbosity=1) def format(self, format="newick"): """Formats the dendrogram in a foreign format. Currently only the Newick format is supported. Example: >>> d = Dendrogram([(2, 3), (0, 1), (4, 5)]) >>> d.format() '((2,3)4,(0,1)5)6;' >>> d.names = list("ABCDEFG") >>> d.format() '((C,D)E,(A,B)F)G;' """ if format == "newick": n = self._nitems + self._nmerges if self._names is None: nodes = range(n) else: nodes = list(self._names) if len(nodes) < n: nodes.extend("" for _ in xrange(n - len(nodes))) for k, (i, j) in enumerate(self._merges, self._nitems): nodes[k] = "(%s,%s)%s" % (nodes[i], nodes[j], nodes[k]) nodes[i] = nodes[j] = None return nodes[-1] + ";" raise ValueError("unsupported format: %r" % format) def summary(self, verbosity=0, max_leaf_count=40): """Returns the summary of the dendrogram. The summary includes the number of leafs and branches, and also an ASCII art representation of the dendrogram unless it is too large. @param verbosity: determines whether the ASCII representation of the dendrogram should be printed. Zero verbosity prints only the number of leafs and branches. @param max_leaf_count: the maximal number of leafs to print in the ASCII representation. If the dendrogram has more leafs than this limit, the ASCII representation will not be printed even if the verbosity is larger than or equal to 1. @return: the summary of the dendrogram as a string. """ out = StringIO() print >>out, "Dendrogram, %d elements, %d merges" % \ (self._nitems, self._nmerges) if self._nitems == 0 or verbosity < 1 or self._nitems > max_leaf_count: return out.getvalue().strip() print >>out positions = [None] * self._nitems inorder = self._traverse_inorder() distance = 2 level_distance = 2 nextp = 0 for idx, element in enumerate(inorder): positions[element] = nextp inorder[idx] = str(element) nextp += max(distance, len(inorder[idx])+1) width = max(positions)+1 # Print the nodes on the lowest level print >>out, (" " * (distance-1)).join(inorder) midx = 0 max_community_idx = self._nitems while midx < self._nmerges: char_array = [" "] * width for position in positions: if position >= 0: char_array[position] = "|" char_str = "".join(char_array) for _ in xrange(level_distance-1): print >>out, char_str # Print the lines cidx_incr = 0 while midx < self._nmerges: id1, id2 = self._merges[midx] if id1 >= max_community_idx or id2 >= max_community_idx: break midx += 1 pos1, pos2 = positions[id1], positions[id2] positions[id1], positions[id2] = -1, -1 if pos1 > pos2: pos1, pos2 = pos2, pos1 positions.append((pos1+pos2) // 2) dashes = "-" * (pos2 - pos1 - 1) char_array[pos1:(pos2+1)] = "`%s'" % dashes cidx_incr += 1 max_community_idx += cidx_incr print >>out, "".join(char_array) return out.getvalue().strip() def _item_box_size(self, context, horiz, idx): """Calculates the amount of space needed for drawing an individual vertex at the bottom of the dendrogram.""" if self._names is None or self._names[idx] is None: x_bearing, _, _, height, x_advance, _ = context.text_extents("") else: x_bearing, _, _, height, x_advance, _ = context.text_extents(str(self._names[idx])) if horiz: return x_advance - x_bearing, height return height, x_advance - x_bearing # pylint: disable-msg=R0913 def _plot_item(self, context, horiz, idx, x, y): """Plots a dendrogram item to the given Cairo context @param context: the Cairo context we are plotting on @param horiz: whether the dendrogram is horizontally oriented @param idx: the index of the item @param x: the X position of the item @param y: the Y position of the item """ if self._names is None or self._names[idx] is None: return height = self._item_box_size(context, True, idx)[1] if horiz: context.move_to(x, y+height) context.show_text(str(self._names[idx])) else: context.save() context.translate(x, y) context.rotate(-pi/2.) context.move_to(0, height) context.show_text(str(self._names[idx])) context.restore() # pylint: disable-msg=C0103,W0613 # W0613 = unused argument 'palette' def __plot__(self, context, bbox, palette, *args, **kwds): """Draws the dendrogram on the given Cairo context Supported keyword arguments are: - C{orientation}: the orientation of the dendrogram. Must be one of the following values: C{left-right}, C{bottom-top}, C{right-left} or C{top-bottom}. Individual elements are always placed at the former edge and merges are performed towards the latter edge. Possible aliases: C{horizontal} = C{left-right}, C{vertical} = C{bottom-top}, C{lr} = C{left-right}, C{rl} = C{right-left}, C{tb} = C{top-bottom}, C{bt} = C{bottom-top}. The default is C{left-right}. """ from igraph.layout import Layout if self._names is None: self._names = [str(x) for x in xrange(self._nitems)] orientation = str_to_orientation(kwds.get("orientation", "lr"), reversed_vertical=True) horiz = orientation in ("lr", "rl") # Get the font height font_height = context.font_extents()[2] # Calculate space needed for individual items at the # bottom of the dendrogram item_boxes = [self._item_box_size(context, horiz, idx) \ for idx in xrange(self._nitems)] # Small correction for cases when the right edge of the labels is # aligned with the tips of the dendrogram branches ygap = 2 if orientation == "bt" else 0 xgap = 2 if orientation == "lr" else 0 item_boxes = [(x+xgap, y+ygap) for x, y in item_boxes] # Calculate coordinates layout = Layout([(0, 0)] * self._nitems, dim=2) inorder = self._traverse_inorder() if not horiz: x, y = 0, 0 for idx, element in enumerate(inorder): layout[element] = (x, 0) x += max(font_height, item_boxes[element][0]) for id1, id2 in self._merges: y += 1 layout.append(((layout[id1][0]+layout[id2][0])/2., y)) # Mirror or rotate the layout if necessary if orientation == "bt": layout.mirror(1) else: x, y = 0, 0 for idx, element in enumerate(inorder): layout[element] = (0, y) y += max(font_height, item_boxes[element][1]) for id1, id2 in self._merges: x += 1 layout.append((x, (layout[id1][1]+layout[id2][1])/2.)) # Mirror or rotate the layout if necessary if orientation == "rl": layout.mirror(0) # Rescale layout to the bounding box maxw = max(e[0] for e in item_boxes) maxh = max(e[1] for e in item_boxes) # w, h: width and height of the area containing the dendrogram # tree without the items. # delta_x, delta_y: displacement of the dendrogram tree width, height = float(bbox.width), float(bbox.height) delta_x, delta_y = 0, 0 if horiz: width -= maxw if orientation == "lr": delta_x = maxw else: height -= maxh if orientation == "tb": delta_y = maxh if horiz: delta_y += font_height / 2. else: delta_x += font_height / 2. layout.fit_into((delta_x, delta_y, width - delta_x, height - delta_y), keep_aspect_ratio=False) context.save() context.translate(bbox.left, bbox.top) context.set_source_rgb(0., 0., 0.) context.set_line_width(1) # Draw items if horiz: sgn = 0 if orientation == "rl" else -1 for idx in xrange(self._nitems): x = layout[idx][0] + sgn * item_boxes[idx][0] y = layout[idx][1] - item_boxes[idx][1]/2. self._plot_item(context, horiz, idx, x, y) else: sgn = 1 if orientation == "bt" else 0 for idx in xrange(self._nitems): x = layout[idx][0] - item_boxes[idx][0]/2. y = layout[idx][1] + sgn * item_boxes[idx][1] self._plot_item(context, horiz, idx, x, y) # Draw dendrogram lines if not horiz: for idx, (id1, id2) in enumerate(self._merges): x0, y0 = layout[id1] x1, y1 = layout[id2] x2, y2 = layout[idx + self._nitems] context.move_to(x0, y0) context.line_to(x0, y2) context.line_to(x1, y2) context.line_to(x1, y1) context.stroke() else: for idx, (id1, id2) in enumerate(self._merges): x0, y0 = layout[id1] x1, y1 = layout[id2] x2, y2 = layout[idx + self._nitems] context.move_to(x0, y0) context.line_to(x2, y0) context.line_to(x2, y1) context.line_to(x1, y1) context.stroke() context.restore() @property def merges(self): """Returns the performed merges in matrix format""" return deepcopy(self._merges) @property def names(self): """Returns the names of the nodes in the dendrogram""" return self._names @names.setter def names(self, items): """Sets the names of the nodes in the dendrogram""" if items is None: self._names = None return items = list(items) if len(items) < self._nitems: raise ValueError("must specify at least %d names" % self._nitems) n = self._nitems + self._nmerges self._names = items[:n] if len(self._names) < n: self._names.extend("" for _ in xrange(n-len(self._names))) class VertexDendrogram(Dendrogram): """The dendrogram resulting from the hierarchical clustering of the vertex set of a graph.""" def __init__(self, graph, merges, optimal_count = None, params = None, modularity_params = None): """Creates a dendrogram object for a given graph. @param graph: the graph that will be associated to the clustering @param merges: the merges performed given in matrix form. @param optimal_count: the optimal number of clusters where the dendrogram should be cut. This is a hint usually provided by the clustering algorithm that produces the dendrogram. C{None} means that such a hint is not available; the optimal count will then be selected based on the modularity in such a case. @param params: additional parameters to be stored in this object. @param modularity_params: arguments that should be passed to L{Graph.modularity} when the modularity is (re)calculated. If the original graph was weighted, you should pass a dictionary containing a C{weight} key with the appropriate value here. """ Dendrogram.__init__(self, merges) self._graph = graph self._optimal_count = optimal_count if modularity_params is None: self._modularity_params = {} else: self._modularity_params = dict(modularity_params) def as_clustering(self, n=None): """Cuts the dendrogram at the given level and returns a corresponding L{VertexClustering} object. @param n: the desired number of clusters. Merges are replayed from the beginning until the membership vector has exactly M{n} distinct elements or until there are no more recorded merges, whichever happens first. If C{None}, the optimal count hint given by the clustering algorithm will be used If the optimal count was not given either, it will be calculated by selecting the level where the modularity is maximal. @return: a new L{VertexClustering} object. """ if n is None: n = self.optimal_count num_elts = self._graph.vcount() idgen = UniqueIdGenerator() membership = community_to_membership(self._merges, num_elts, \ num_elts - n) membership = [idgen[m] for m in membership] return VertexClustering(self._graph, membership, modularity_params=self._modularity_params) @property def optimal_count(self): """Returns the optimal number of clusters for this dendrogram. If an optimal count hint was given at construction time, this property simply returns the hint. If such a count was not given, this method calculates the optimal number of clusters by maximizing the modularity along all the possible cuts in the dendrogram. """ if self._optimal_count is not None: return self._optimal_count n = self._graph.vcount() max_q, optimal_count = 0, 1 for step in xrange(min(n-1, len(self._merges))): membs = community_to_membership(self._merges, n, step) q = self._graph.modularity(membs, **self._modularity_params) if q > max_q: optimal_count = n-step max_q = q self._optimal_count = optimal_count return optimal_count @optimal_count.setter def optimal_count(self, value): self._optimal_count = max(int(value), 1) def __plot__(self, context, bbox, palette, *args, **kwds): """Draws the vertex dendrogram on the given Cairo context See L{Dendrogram.__plot__} for the list of supported keyword arguments.""" from igraph.drawing.metamagic import AttributeCollectorBase class VisualVertexBuilder(AttributeCollectorBase): _kwds_prefix = "vertex_" label = None builder = VisualVertexBuilder(self._graph.vs, kwds) self._names = [vertex.label for vertex in builder] self._names = [name if name is not None else str(idx) for idx, name in enumerate(self._names)] result = Dendrogram.__plot__(self, context, bbox, palette, \ *args, **kwds) del self._names return result ############################################################################### class Cover(object): """Class representing a cover of an arbitrary ordered set. Covers are similar to clusterings, but each element of the set may belong to more than one cluster in a cover, and elements not belonging to any cluster are also allowed. L{Cover} instances provide a similar API as L{Clustering} instances; for instance, iterating over a L{Cover} will iterate over the clusters just like with a regular L{Clustering} instance. However, they are not derived from each other or from a common superclass, and there might be functions that exist only in one of them or the other. Clusters of an individual cover can be accessed by the C{[]} operator: >>> cl = Cover([[0,1,2,3], [2,3,4], [0,1,6]]) >>> cl[0] [0, 1, 2, 3] The membership vector can be accessed by the C{membership} property. Note that contrary to L{Clustering} instances, the membership vector will contain lists that contain the cluster indices each item belongs to: >>> cl.membership [[0, 2], [0, 2], [0, 1], [0, 1], [1], [], [2]] The number of clusters can be retrieved by the C{len} function: >>> len(cl) 3 You can iterate over the cover as if it were a regular list of clusters: >>> for cluster in cl: ... print " ".join(str(idx) for idx in cluster) ... 0 1 2 3 2 3 4 0 1 6 If you need all the clusters at once as lists, you can simply convert the cover to a list: >>> cluster_list = list(cl) >>> print cluster_list [[0, 1, 2, 3], [2, 3, 4], [0, 1, 6]] L{Clustering} objects can readily be converted to L{Cover} objects using the constructor: >>> clustering = Clustering([0, 0, 0, 0, 1, 1, 1, 2, 2, 2]) >>> cover = Cover(clustering) >>> list(clustering) == list(cover) True @undocumented: _formatted_cluster_iterator """ def __init__(self, clusters, n=0): """Constructs a cover with the given clusters. @param clusters: the clusters in this cover, as a list or iterable. Each cluster is specified by a list or tuple that contains the IDs of the items in this cluster. IDs start from zero. @param n: the total number of elements in the set that is covered by this cover. If it is less than the number of unique elements found in all the clusters, we will simply use the number of unique elements, so it is safe to leave this at zero. You only have to specify this parameter if there are some elements that are covered by none of the clusters. """ self._clusters = [list(cluster) for cluster in clusters] try: self._n = max(max(cluster)+1 for cluster in self._clusters if cluster) except ValueError: self._n = 0 self._n = max(n, self._n) def __getitem__(self, index): """Returns the cluster with the given index.""" return self._clusters[index] def __iter__(self): """Iterates over the clusters in this cover.""" return iter(self._clusters) def __len__(self): """Returns the number of clusters in this cover.""" return len(self._clusters) def __str__(self): """Returns a string representation of the cover.""" return self.summary(verbosity=1, width=78) @property def membership(self): """Returns the membership vector of this cover. The membership vector of a cover covering I{n} elements is a list of length I{n}, where element I{i} contains the cluster indices of the I{i}th item. """ result = [[] for _ in xrange(self._n)] for idx, cluster in enumerate(self): for item in cluster: result[item].append(idx) return result @property def n(self): """Returns the number of elements in the set covered by this cover.""" return self._n def size(self, idx): """Returns the size of a given cluster. @param idx: the cluster in which we are interested. """ return len(self[idx]) def sizes(self, *args): """Returns the size of given clusters. The indices are given as positional arguments. If there are no positional arguments, the function will return the sizes of all clusters. """ if args: return [len(self._clusters[idx]) for idx in args] return [len(cluster) for cluster in self] def size_histogram(self, bin_width = 1): """Returns the histogram of cluster sizes. @param bin_width: the bin width of the histogram @return: a L{Histogram} object """ return Histogram(bin_width, self.sizes()) def summary(self, verbosity=0, width=None): """Returns the summary of the cover. The summary includes the number of items and clusters, and also the list of members for each of the clusters if the verbosity is nonzero. @param verbosity: determines whether the cluster members should be printed. Zero verbosity prints the number of items and clusters only. @return: the summary of the cover as a string. """ out = StringIO() print >>out, "Cover with %d clusters" % len(self) if verbosity < 1: return out.getvalue().strip() ndigits = len(str(len(self))) wrapper = _get_wrapper_for_width(width, subsequent_indent = " " * (ndigits+3)) for idx, cluster in enumerate(self._formatted_cluster_iterator()): wrapper.initial_indent = "[%*d] " % (ndigits, idx) print >>out, "\n".join(wrapper.wrap(cluster)) return out.getvalue().strip() def _formatted_cluster_iterator(self): """Iterates over the clusters and formats them into a string to be presented in the summary.""" for cluster in self: yield ", ".join(str(member) for member in cluster) class VertexCover(Cover): """The cover of the vertex set of a graph. This class extends L{Cover} by linking it to a specific L{Graph} object. It also provides some handy methods like getting the subgraph corresponding to a cluster and such. @note: since this class is linked to a L{Graph}, destroying the graph by the C{del} operator does not free the memory occupied by the graph if there exists a L{VertexCover} that references the L{Graph}. @undocumented: _formatted_cluster_iterator """ def __init__(self, graph, clusters = None): """Creates a cover object for a given graph. @param graph: the graph that will be associated to the cover @param clusters: the list of clusters. If C{None}, it is assumed that there is only a single cluster that covers the whole graph. """ if clusters is None: clusters = [range(graph.vcount())] Cover.__init__(self, clusters, n = graph.vcount()) if self._n > graph.vcount(): raise ValueError("cluster list contains vertex ID larger than the " "number of vertices in the graph") self._graph = graph def crossing(self): """Returns a boolean vector where element M{i} is C{True} iff edge M{i} lies between clusters, C{False} otherwise.""" membership = [frozenset(cluster) for cluster in self.membership] return [membership[v1].isdisjoint(membership[v2]) \ for v1, v2 in self.graph.get_edgelist()] @property def graph(self): """Returns the graph belonging to this object""" return self._graph def subgraph(self, idx): """Get the subgraph belonging to a given cluster. @param idx: the cluster index @return: a copy of the subgraph @precondition: the vertex set of the graph hasn't been modified since the moment the cover was constructed. """ return self._graph.subgraph(self[idx]) def subgraphs(self): """Gets all the subgraphs belonging to each of the clusters. @return: a list containing copies of the subgraphs @precondition: the vertex set of the graph hasn't been modified since the moment the cover was constructed. """ return [self._graph.subgraph(cl) for cl in self] def __plot__(self, context, bbox, palette, *args, **kwds): """Plots the cover to the given Cairo context in the given bounding box. This is done by calling L{Graph.__plot__()} with the same arguments, but drawing nice colored blobs around the vertex groups. This method understands all the positional and keyword arguments that are understood by L{Graph.__plot__()}, only the differences will be highlighted here: - C{mark_groups}: whether to highlight the vertex clusters by colored polygons. Besides the values accepted by L{Graph.__plot__} (i.e., a dict mapping colors to vertex indices, a list containing lists of vertex indices, or C{False}), the following are also accepted: - C{True}: all the clusters will be highlighted, the colors matching the corresponding color indices from the current palette (see the C{palette} keyword argument of L{Graph.__plot__}. - A dict mapping cluster indices or tuples of vertex indices to color names. The given clusters or vertex groups will be highlighted by the given colors. - A list of cluster indices. This is equivalent to passing a dict mapping numeric color indices from the current palette to cluster indices; therefore, the cluster referred to by element I{i} of the list will be highlighted by color I{i} from the palette. The value of the C{plotting.mark_groups} configuration key is also taken into account here; if that configuration key is C{True} and C{mark_groups} is not given explicitly, it will automatically be set to C{True}. In place of lists of vertex indices, you may also use L{VertexSeq} instances. In place of color names, you may also use color indices into the current palette. C{None} as a color name will mean that the corresponding group is ignored. - C{palette}: the palette used to resolve numeric color indices to RGBA values. By default, this is an instance of L{ClusterColoringPalette}. @see: L{Graph.__plot__()} for more supported keyword arguments. """ if "edge_color" not in kwds and "color" not in self.graph.edge_attributes(): # Set up a default edge coloring based on internal vs external edges colors = ["grey20", "grey80"] kwds["edge_color"] = [colors[is_crossing] for is_crossing in self.crossing()] if "palette" in kwds: palette = kwds["palette"] else: palette = ClusterColoringPalette(len(self)) if "mark_groups" not in kwds: if Configuration.instance()["plotting.mark_groups"]: kwds["mark_groups"] = enumerate(self) else: kwds["mark_groups"] = _handle_mark_groups_arg_for_clustering( kwds["mark_groups"], self) return self._graph.__plot__(context, bbox, palette, *args, **kwds) def _formatted_cluster_iterator(self): """Iterates over the clusters and formats them into a string to be presented in the summary.""" if self._graph.is_named(): names = self._graph.vs["name"] for cluster in self: yield ", ".join(str(names[member]) for member in cluster) else: for cluster in self: yield ", ".join(str(member) for member in cluster) class CohesiveBlocks(VertexCover): """The cohesive block structure of a graph. Instances of this type are created by L{Graph.cohesive_blocks()}. See the documentation of L{Graph.cohesive_blocks()} for an explanation of what cohesive blocks are. This class provides a few more methods that make handling of cohesive block structures easier. """ def __init__(self, graph, blocks = None, cohesion = None, parent = None): """Constructs a new cohesive block structure for the given graph. If any of I{blocks}, I{cohesion} or I{parent} is C{None}, all the arguments will be ignored and L{Graph.cohesive_blocks()} will be called to calculate the cohesive blocks. Otherwise, these three variables should describe the *result* of a cohesive block structure calculation. Chances are that you never have to construct L{CohesiveBlocks} instances directly, just use L{Graph.cohesive_blocks()}. @param graph: the graph itself @param blocks: a list containing the blocks; each block is described as a list containing vertex IDs. @param cohesion: the cohesion of each block. The length of this list must be equal to the length of I{blocks}. @param parent: the parent block of each block. Negative values or C{None} mean that there is no parent block for that block. There should be only one parent block, which covers the entire graph. @see: Graph.cohesive_blocks() """ if blocks is None or cohesion is None or parent is None: blocks, cohesion, parent = graph.cohesive_blocks() VertexCover.__init__(self, graph, blocks) self._cohesion = cohesion self._parent = parent for idx, p in enumerate(self._parent): if p < 0: self._parent[idx] = None def cohesion(self, idx): """Returns the cohesion of the group with the given index.""" return self._cohesion[idx] def cohesions(self): """Returns the list of cohesion values for each group.""" return self._cohesion[:] def hierarchy(self): """Returns a new graph that describes the hierarchical relationships between the groups. The new graph will be a directed tree; an edge will point from vertex M{i} to vertex M{j} if group M{i} is a superset of group M{j}. In other words, the edges point downwards. """ from igraph import Graph edges = [pair for pair in izip(self._parent, xrange(len(self))) if pair[0] is not None] return Graph(edges, directed=True) def max_cohesion(self, idx): """Finds the maximum cohesion score among all the groups that contain the given vertex.""" result = 0 for cohesion, cluster in izip(self._cohesion, self._clusters): if idx in cluster: result = max(result, cohesion) return result def max_cohesions(self): """For each vertex in the graph, returns the maximum cohesion score among all the groups that contain the vertex.""" result = [0] * self._graph.vcount() for cohesion, cluster in izip(self._cohesion, self._clusters): for idx in cluster: result[idx] = max(result[idx], cohesion) return result def parent(self, idx): """Returns the parent group index of the group with the given index or C{None} if the given group is the root.""" return self._parent[idx] def parents(self): """Returns the list of parent group indices for each group or C{None} if the given group is the root.""" return self._parent[:] def __plot__(self, context, bbox, palette, *args, **kwds): """Plots the cohesive block structure to the given Cairo context in the given bounding box. Since a L{CohesiveBlocks} instance is also a L{VertexCover}, keyword arguments accepted by L{VertexCover.__plot__()} are also accepted here. The only difference is that the vertices are colored according to their maximal cohesions by default, and groups are marked by colored blobs except the last group which encapsulates the whole graph. See the documentation of L{VertexCover.__plot__()} for more details. """ prepare_groups = False if "mark_groups" not in kwds: if Configuration.instance()["plotting.mark_groups"]: prepare_groups = True elif kwds["mark_groups"] == True: prepare_groups = True if prepare_groups: colors = [pair for pair in enumerate(self.cohesions()) if pair[1] > 1] kwds["mark_groups"] = colors if "vertex_color" not in kwds: kwds["vertex_color"] = self.max_cohesions() return VertexCover.__plot__(self, context, bbox, palette, *args, **kwds) def _handle_mark_groups_arg_for_clustering(mark_groups, clustering): """Handles the mark_groups=... keyword argument in plotting methods of clusterings. This is an internal method, you shouldn't need to mess around with it. Its purpose is to handle the extended semantics of the mark_groups=... keyword argument in the C{__plot__} method of L{VertexClustering} and L{VertexCover} instances, namely the feature that numeric IDs are resolved to clusters automatically. """ # Handle the case of mark_groups = True, mark_groups containing a list or # tuple of cluster IDs, and and mark_groups yielding (cluster ID, color) # pairs if mark_groups is True: group_iter = ((group, color) for color, group in enumerate(clustering)) elif isinstance(mark_groups, dict): group_iter = mark_groups.iteritems() elif hasattr(mark_groups, "__getitem__") and hasattr(mark_groups, "__len__"): # Lists, tuples try: first = mark_groups[0] except: # Hmm. Maybe not a list or tuple? first = None if first is not None: # Okay. Is the first element of the list a single number? if isinstance(first, (int, long)): # Yes. Seems like we have a list of cluster indices. # Assign color indices automatically. group_iter = ((group, color) for color, group in enumerate(mark_groups)) else: # No. Seems like we have good ol' group-color pairs. group_iter = mark_groups else: group_iter = mark_groups elif hasattr(mark_groups, "__iter__"): # Iterators etc group_iter = mark_groups else: group_iter = {}.iteritems() def cluster_index_resolver(): for group, color in group_iter: if isinstance(group, (int, long)): group = clustering[group] yield group, color return cluster_index_resolver() ############################################################## def _prepare_community_comparison(comm1, comm2, remove_none=False): """Auxiliary method that takes two community structures either as membership lists or instances of L{Clustering}, and returns a tuple whose two elements are membership lists. This is used by L{compare_communities} and L{split_join_distance}. @param comm1: the first community structure as a membership list or as a L{Clustering} object. @param comm2: the second community structure as a membership list or as a L{Clustering} object. @param remove_none: whether to remove C{None} entries from the membership lists. If C{remove_none} is C{False}, a C{None} entry in either C{comm1} or C{comm2} will result in an exception. If C{remove_none} is C{True}, C{None} values are filtered away and only the remaining lists are compared. """ def _ensure_list(obj): if isinstance(obj, Clustering): return obj.membership return list(obj) vec1, vec2 = _ensure_list(comm1), _ensure_list(comm2) if len(vec1) != len(vec2): raise ValueError("the two membership vectors must be equal in length") if remove_none and (None in vec1 or None in vec2): idxs_to_remove = [i for i in xrange(len(vec1)) \ if vec1[i] is None or vec2[i] is None] idxs_to_remove.reverse() n = len(vec1) for i in idxs_to_remove: n -= 1 vec1[i], vec1[n] = vec1[n], vec1[i] vec2[i], vec2[n] = vec2[n], vec2[i] del vec1[n:] del vec2[n:] return vec1, vec2 def compare_communities(comm1, comm2, method="vi", remove_none=False): """Compares two community structures using various distance measures. @param comm1: the first community structure as a membership list or as a L{Clustering} object. @param comm2: the second community structure as a membership list or as a L{Clustering} object. @param method: the measure to use. C{"vi"} or C{"meila"} means the variation of information metric of Meila (2003), C{"nmi"} or C{"danon"} means the normalized mutual information as defined by Danon et al (2005), C{"split-join"} means the split-join distance of van Dongen (2000), C{"rand"} means the Rand index of Rand (1971), C{"adjusted_rand"} means the adjusted Rand index of Hubert and Arabie (1985). @param remove_none: whether to remove C{None} entries from the membership lists. This is handy if your L{Clustering} object was constructed using L{VertexClustering.FromAttribute} using an attribute which was not defined for all the vertices. If C{remove_none} is C{False}, a C{None} entry in either C{comm1} or C{comm2} will result in an exception. If C{remove_none} is C{True}, C{None} values are filtered away and only the remaining lists are compared. @return: the calculated measure. @newfield ref: Reference @ref: Meila M: Comparing clusterings by the variation of information. In: Scholkopf B, Warmuth MK (eds). Learning Theory and Kernel Machines: 16th Annual Conference on Computational Learning Theory and 7th Kernel Workship, COLT/Kernel 2003, Washington, DC, USA. Lecture Notes in Computer Science, vol. 2777, Springer, 2003. ISBN: 978-3-540-40720-1. @ref: Danon L, Diaz-Guilera A, Duch J, Arenas A: Comparing community structure identification. J Stat Mech P09008, 2005. @ref: van Dongen D: Performance criteria for graph clustering and Markov cluster experiments. Technical Report INS-R0012, National Research Institute for Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000. @ref: Rand WM: Objective criteria for the evaluation of clustering methods. J Am Stat Assoc 66(336):846-850, 1971. @ref: Hubert L and Arabie P: Comparing partitions. Journal of Classification 2:193-218, 1985. """ import igraph._igraph vec1, vec2 = _prepare_community_comparison(comm1, comm2, remove_none) return igraph._igraph._compare_communities(vec1, vec2, method) def split_join_distance(comm1, comm2, remove_none=False): """Calculates the split-join distance between two community structures. The split-join distance is a distance measure defined on the space of partitions of a given set. It is the sum of the projection distance of one partition from the other and vice versa, where the projection number of A from B is if calculated as follows: 1. For each set in A, find the set in B with which it has the maximal overlap, and take note of the size of the overlap. 2. Take the sum of the maximal overlap sizes for each set in A. 3. Subtract the sum from M{n}, the number of elements in the partition. Note that the projection distance is asymmetric, that's why it has to be calculated in both directions and then added together. This function returns the projection distance of C{comm1} from C{comm2} and the projection distance of C{comm2} from C{comm1}, and returns them in a pair. The actual split-join distance is the sum of the two distances. The reason why it is presented this way is that one of the elements being zero then implies that one of the partitions is a subpartition of the other (and if it is close to zero, then one of the partitions is close to being a subpartition of the other). @param comm1: the first community structure as a membership list or as a L{Clustering} object. @param comm2: the second community structure as a membership list or as a L{Clustering} object. @param remove_none: whether to remove C{None} entries from the membership lists. This is handy if your L{Clustering} object was constructed using L{VertexClustering.FromAttribute} using an attribute which was not defined for all the vertices. If C{remove_none} is C{False}, a C{None} entry in either C{comm1} or C{comm2} will result in an exception. If C{remove_none} is C{True}, C{None} values are filtered away and only the remaining lists are compared. @return: the projection distance of C{comm1} from C{comm2} and vice versa in a tuple. The split-join distance is the sum of the two. @newfield ref: Reference @ref: van Dongen D: Performance criteria for graph clustering and Markov cluster experiments. Technical Report INS-R0012, National Research Institute for Mathematics and Computer Science in the Netherlands, Amsterdam, May 2000. @see: L{compare_communities()} with C{method = "split-join"} if you are not interested in the individual projection distances but only the sum of them. """ import igraph._igraph vec1, vec2 = _prepare_community_comparison(comm1, comm2, remove_none) return igraph._igraph._split_join_distance(vec1, vec2) python-igraph-0.7.1.post6/igraph/compat.py0000644000076500000240000000432512460534506021110 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """ Compatibility methods and backported versions of newer Python features to enable igraph to run on Python 2.5. """ import sys __license__ = u"""\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ ############################################################################# # Simulating math.isnan try: from math import isnan except ImportError: def isnan(num): return num != num ############################################################################# # Providing @property.setter syntax for Python 2.5 if sys.version_info < (2, 6): _property = property class property(property): def __init__(self, fget, *args, **kwds): self.__doc__ = fget.__doc__ super(property, self).__init__(fget, *args, **kwds) def setter(self, fset): cls_ns = sys._getframe(1).f_locals for k, v in cls_ns.iteritems(): if v == self: propname = k break cls_ns[propname] = property(self.fget, fset, self.fdel, self.__doc__) return cls_ns[propname] else: property = __builtins__["property"] ############################################################################# # Providing BytesIO for Python 2.5 try: from io import BytesIO except ImportError: # We are on Python 2.5 or earlier because Python 2.6 has a BytesIO # class already from cStringIO import StringIO BytesIO = StringIO python-igraph-0.7.1.post6/igraph/configuration.py0000644000076500000240000004117612461463661022505 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """ Configuration framework for igraph. igraph has some parameters which usually affect the behaviour of many functions. This module provides the framework for altering and querying igraph parameters as well as saving them to and retrieving them from disk. """ __license__ = """\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ from ConfigParser import SafeConfigParser import platform import os.path def get_platform_image_viewer(): """Returns the path of an image viewer on the given platform""" plat = platform.system() if plat == "Darwin": # Most likely Mac OS X return "open" elif plat == "Linux": # Linux has a whole lot of choices, try to find one choices = ["eog", "gthumb", "gqview", "kuickshow", "xnview", "display", "gpicview", "gwenview", "qiv", "gimv", "ristretto"] paths = ["/usr/bin", "/bin"] for path in paths: for choice in choices: full_path = os.path.join(path, choice) if os.path.isfile(full_path): return full_path return "" elif plat == "Windows" or plat == "Microsoft": # Thanks to Dale Hunscher # Use the built-in Windows image viewer, if available return "start" else: # Unknown system return "" class Configuration(object): """Class representing igraph configuration details. General ideas ============= The configuration of igraph is stored in the form of name-value pairs. This object provides an interface to the configuration data using the syntax known from dict: >>> c=Configuration() >>> c["general.verbose"] = True >>> print c["general.verbose"] True Configuration keys are organized into sections, and the name to be used for a given key is always in the form C{section.keyname}, like C{general.verbose} in the example above. In that case, C{general} is the name of the configuration section, and C{verbose} is the name of the key. If the name of the section is omitted, it defaults to C{general}, so C{general.verbose} can be referred to as C{verbose}: >>> c=Configuration() >>> c["verbose"] = True >>> print c["general.verbose"] True User-level configuration is stored in C{~/.igraphrc} per default on Linux and Mac OS X systems, or in C{C:\\Documents and Settings\\username\\.igraphrc} on Windows systems. However, this configuration is read only when C{igraph} is launched through its shell interface defined in L{igraph.app.shell}. This behaviour might change before version 1.0. Known configuration keys ======================== The known configuration keys are presented below, sorted by section. When referring to them in program code, don't forget to add the section name, expect in the case of section C{general}. General settings ---------------- These settings are all stored in section C{general}. - B{shells}: the list of preferred Python shells to be used with the command-line C{igraph} script. The shells in the list are tried one by one until any of them is found on the system. C{igraph} functions are then imported into the main namespace of the shell and the shell is launched. Known shells and their respective class names to be used can be found in L{igraph.app.shell}. Example: C{IPythonShell, ClassicPythonShell}. This is the default, by the way. - B{verbose}: whether L{igraph} should talk more than really necessary. For instance, if set to C{True}, some functions display progress bars. Application settings -------------------- These settings specify the external applications that are possibly used by C{igraph}. They are all stored in section C{apps}. - B{image_viewer}: image viewer application. If set to an empty string, it will be determined automatically from the platform C{igraph} runs on. On Mac OS X, it defaults to the Preview application. On Linux, it chooses a viewer from several well-known Linux viewers like C{gthumb}, C{kuickview} and so on (see the source code for the full list). On Windows, it defaults to the system's built-in image viewer. Plotting settings ----------------- These settings specify the default values used by plotting functions. They are all stored in section C{plotting}. - B{layout}: default graph layout algorithm to be used. - B{mark_groups}: whether to mark the clusters by polygons when plotting a clustering object. - B{palette}: default palette to be used for converting integer numbers to colors. See L{colors.Palette} for more information. Valid palette names are stored in C{colors.palettes}. - B{wrap_labels}: whether to try to wrap the labels of the vertices automatically if they don't fit within the vertex. Default: C{False}. Remote repository settings -------------------------- These settings specify how igraph should access remote graph repositories. Currently only the Nexus repository is supported. All these settings are stored in section C{remote}. - B{nexus.url}: the root URL of the Nexus repository. Default: C{http://nexus.igraph.org}. Shell settings -------------- These settings specify options for external environments in which igraph is embedded (e.g., IPython and its Qt console). These settings are stored in section C{shell}. - B{ipython.inlining.Plot}: whether to show instances of the L{Plot} class inline in IPython's console if the console supports it. Default: C{True} @undocumented: _item_to_section_key, _types, _sections, _definitions, _instance """ # pylint: disable-msg=R0903 # R0903: too few public methods class Types(object): """Static class for the implementation of custom getter/setter functions for configuration keys""" def __init__(self): pass @staticmethod def setboolean(obj, section, key, value): """Sets a boolean value in the given configuration object. @param obj: a configuration object @param section: the section of the value to be set @param key: the key of the value to be set @param value: the value itself. C{0}, C{false}, C{no} and C{off} means false, C{1}, C{true}, C{yes} and C{on} means true, everything else results in a C{ValueError} being thrown. Values are case insensitive """ value = str(value).lower() if value in ("0", "false", "no", "off"): value = "false" elif value in ("1", "true", "yes", "on"): value = "true" else: raise ValueError("value cannot be coerced to boolean type") obj.set(section, key, value) @staticmethod def setint(obj, section, key, value): """Sets an integer value in the given configuration object. @param obj: a configuration object @param section: the section of the value to be set @param key: the key of the value to be set @param value: the value itself. """ obj.set(section, key, str(int(value))) @staticmethod def setfloat(obj, section, key, value): """Sets a float value in the given configuration object. Note that float values are converted to strings in the configuration object, which may lead to some precision loss. @param obj: a configuration object @param section: the section of the value to be set @param key: the key of the value to be set @param value: the value itself. """ obj.set(section, key, str(float(value))) _types = { "boolean": { "getter": SafeConfigParser.getboolean, "setter": Types.setboolean }, "int": { "getter": SafeConfigParser.getint, "setter": Types.setint }, "float": { "getter": SafeConfigParser.getfloat, "setter": Types.setfloat } } _sections = ("general", "apps", "plotting", "remote", "shell") _definitions = { "general.shells": { "default": "IPythonShell,ClassicPythonShell" }, "general.verbose": { "default": True, "type": "boolean" }, "apps.image_viewer": { "default": get_platform_image_viewer() }, "plotting.layout": { "default": "auto" }, "plotting.mark_groups": { "default": False, "type": "boolean" }, "plotting.palette": { "default": "gray" }, "plotting.wrap_labels": { "default": False, "type": "boolean" }, "remote.nexus.url": { "default": "http://nexus.igraph.org" }, "shell.ipython.inlining.Plot": { "default": True, "type": "boolean" } } # The singleton instance we are using throughout other modules _instance = None def __init__(self, filename=None): """Creates a new configuration instance. @param filename: file or file pointer to be read. Can be omitted. """ self._config = SafeConfigParser() self._filename = None # Create default sections for sec in self._sections: self._config.add_section(sec) # Create default values for name, definition in self._definitions.iteritems(): if "default" in definition: self[name] = definition["default"] if filename is not None: self.load(filename) @property def filename(self): """Returns the filename associated to the object. It is usually the name of the configuration file that was used when creating the object. L{Configuration.load} always overwrites it with the filename given to it. If C{None}, the configuration was either created from scratch or it was updated from a stream without name information.""" return self._filename def _get(self, section, key): """Internal function that returns the value of a given key in a given section.""" definition = self._definitions.get("%s.%s" % (section, key), {}) getter = None if "type" in definition: getter = self._types[definition["type"]].get("getter") if getter is None: getter = self._config.__class__.get return getter(self._config, section, key) @staticmethod def _item_to_section_key(item): """Converts an item description to a section-key pair. @param item: the item to be converted @return: if C{item} contains a period (C{.}), it is splitted into two parts at the first period, then the two parts are returned, so the part before the period is the section. If C{item} does not contain a period, the section is assumed to be C{general}, and the second part of the returned pair contains C{item} unchanged""" if "." in item: section, key = item.split(".", 1) else: section, key = "general", item return section, key def __contains__(self, item): """Checks whether the given configuration item is set. @param item: the configuration key to check. @return: C{True} if the key has an associated value, C{False} otherwise. """ section, key = self._item_to_section_key(item) return self._config.has_option(section, key) def __getitem__(self, item): """Returns the given configuration item. @param item: the configuration key to retrieve. @return: the configuration value""" section, key = self._item_to_section_key(item) if key == "*": # Special case: retrieving all the keys within a section and # returning it in a dict keys = self._config.items(section) return dict((key, self._get(section, key)) for key, _ in keys) else: return self._get(section, key) def __setitem__(self, item, value): """Sets the given configuration item. @param item: the configuration key to set @param value: the new value of the configuration key """ section, key = self._item_to_section_key(item) definition = self._definitions.get("%s.%s" % (section, key), {}) setter = None if "type" in definition: setter = self._types[definition["type"]].get("setter", None) if setter is None: setter = self._config.__class__.set return setter(self._config, section, key, value) def __delitem__(self, item): """Deletes the given item from the configuration. If the item has a default value, the default value is written back instead of the current value. Without a default value, the item is really deleted. """ section, key = self._item_to_section_key(item) definition = self._definitions.get("%s.%s" % (section, key), {}) if "default" in definition: self[item] = definition["default"] else: self._config.remove_option(section, key) def has_key(self, item): """Checks if the configuration has a given key. @param item: the key being sought""" if "." in item: section, key = item.split(".", 1) else: section, key = "general", item return self._config.has_option(section, key) def load(self, stream=None): """Loads the configuration from the given file. @param stream: name of a file or a file object. The configuration will be loaded from here. Can be omitted, in this case, the user-level configuration is loaded. """ stream = stream or get_user_config_file() if isinstance(stream, basestring): stream = open(stream, "r") file_was_open = True self._config.readfp(stream) self._filename = getattr(stream, "name", None) if file_was_open: stream.close() def save(self, stream=None): """Saves the configuration. @param stream: name of a file or a file object. The configuration will be saved there. Can be omitted, in this case, the user-level configuration file will be overwritten. """ stream = stream or get_user_config_file() if not hasattr(stream, "write") or not hasattr(stream, "close"): stream = open(stream, "w") file_was_open = True self._config.write(stream) if file_was_open: stream.close() @classmethod def instance(cls): """Returns the single instance of the configuration object.""" if cls._instance is None: cfile = get_user_config_file() try: config = cls(cfile) except IOError: # No config file yet, whatever config = cls() cls._instance = config return cls._instance def get_user_config_file(): """Returns the path where the user-level configuration file is stored""" return os.path.expanduser("~/.igraphrc") def init(): """Default mechanism to initiate igraph configuration This method loads the user-specific configuration file from the user's home directory, or if it does not exist, creates a default configuration. The method is safe to be called multiple times, it will not parse the configuration file twice. @return: the L{Configuration} object loaded or created.""" return Configuration.instance() python-igraph-0.7.1.post6/igraph/cut.py0000644000076500000240000001456612453614202020422 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """Classes representing cuts and flows on graphs.""" from igraph.clustering import VertexClustering __license__ = """\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ class Cut(VertexClustering): """A cut of a given graph. This is a simple class used to represent cuts returned by L{Graph.mincut()}, L{Graph.all_st_cuts()} and other functions that calculate cuts. A cut is a special vertex clustering with only two clusters. Besides the usual L{VertexClustering} methods, it also has the following attributes: - C{value} - the value (capacity) of the cut. It is equal to the number of edges if there are no capacities on the edges. - C{partition} - vertex IDs in the parts created after removing edges in the cut - C{cut} - edge IDs in the cut - C{es} - an edge selector restricted to the edges in the cut. You can use indexing on this object to obtain lists of vertex IDs for both sides of the partition. This class is usually not instantiated directly, everything is taken care of by the functions that return cuts. Examples: >>> from igraph import Graph >>> g = Graph.Ring(20) >>> mc = g.mincut() >>> print mc.value 2.0 >>> print min(map(len, mc)) 1 >>> mc.es["color"] = "red" """ # pylint: disable-msg=R0913 def __init__(self, graph, value=None, cut=None, partition=None, partition2=None): """Initializes the cut. This should not be called directly, everything is taken care of by the functions that return cuts. """ # Input validation if partition is None or cut is None: raise ValueError("partition and cut must be given") # Set up a membership vector, initialize parent class membership = [1] * graph.vcount() for vid in partition: membership[vid] = 0 VertexClustering.__init__(self, graph, membership) if value is None: # Value of the cut not given, count the number of edges value = len(cut) self._value = float(value) self._partition = sorted(partition) self._cut = cut def __repr__(self): return "%s(%r, %r, %r, %r)" % \ (self.__class__.__name__, self._graph, \ self._value, self._cut, self._partition) def __str__(self): return "Graph cut (%d edges, %d vs %d vertices, value=%.4f)" % \ (len(self._cut), len(self._partition), self._graph.vcount() - len(self._partition), self._value) # pylint: disable-msg=C0103 @property def es(self): """Returns an edge selector restricted to the cut""" return self._graph.es.select(self.cut) @property def partition(self): """Returns the vertex IDs partitioned according to the cut""" return list(self) @property def cut(self): """Returns the edge IDs in the cut""" return self._cut @property def value(self): """Returns the sum of edge capacities in the cut""" return self._value class Flow(Cut): """A flow of a given graph. This is a simple class used to represent flows returned by L{Graph.maxflow}. It has the following attributes: - C{graph} - the graph on which this flow is defined - C{value} - the value (capacity) of the flow - C{flow} - the flow values on each edge. For directed graphs, this is simply a list where element M{i} corresponds to the flow on edge M{i}. For undirected graphs, the direction of the flow is not constrained (since the edges are undirected), hence positive flow always means a flow from the smaller vertex ID to the larger, while negative flow means a flow from the larger vertex ID to the smaller. - C{cut} - edge IDs in the minimal cut corresponding to the flow. - C{partition} - vertex IDs in the parts created after removing edges in the cut - C{es} - an edge selector restricted to the edges in the cut. This class is usually not instantiated directly, everything is taken care of by L{Graph.maxflow}. Examples: >>> from igraph import Graph >>> g = Graph.Ring(20) >>> mf = g.maxflow(0, 10) >>> print mf.value 2.0 >>> mf.es["color"] = "red" """ # pylint: disable-msg=R0913 def __init__(self, graph, value, flow, cut, partition): """Initializes the flow. This should not be called directly, everything is taken care of by L{Graph.maxflow}. """ super(Flow, self).__init__(graph, value, cut, partition) self._flow = flow def __repr__(self): return "%s(%r, %r, %r, %r, %r)" % \ (self.__class__.__name__, self._graph, \ self._value, self._flow, self._cut, self._partition) def __str__(self): return "Graph flow (%d edges, %d vs %d vertices, value=%.4f)" % \ (len(self._cut), len(self._partition), self._graph.vcount() - len(self._partition), self._value) @property def flow(self): """Returns the flow values for each edge. For directed graphs, this is simply a list where element M{i} corresponds to the flow on edge M{i}. For undirected graphs, the direction of the flow is not constrained (since the edges are undirected), hence positive flow always means a flow from the smaller vertex ID to the larger, while negative flow means a flow from the larger vertex ID to the smaller. """ return self._flow python-igraph-0.7.1.post6/igraph/datatypes.py0000644000076500000240000006752412453614202021627 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """Additional auxiliary data types""" from itertools import islice __license__ = """\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ class Matrix(object): """Simple matrix data type. Of course there are much more advanced matrix data types for Python (for instance, the C{ndarray} data type of Numeric Python) and this implementation does not want to compete with them. The only role of this data type is to provide a convenient interface for the matrices returned by the C{Graph} object (for instance, allow indexing with tuples in the case of adjacency matrices and so on). """ def __init__(self, data=None): """Initializes a matrix. @param data: the elements of the matrix as a list of lists, or C{None} to create a 0x0 matrix. """ self._nrow, self._ncol, self._data = 0, 0, [] self.data = data # pylint: disable-msg=C0103 @classmethod def Fill(cls, value, *args): """Creates a matrix filled with the given value @param value: the value to be used @keyword shape: the shape of the matrix. Can be a single integer, two integers or a tuple. If a single integer is given here, the matrix is assumed to be square-shaped. """ if len(args) < 1: raise TypeError("expected an integer or a tuple") if len(args) == 1: if hasattr(args[0], "__len__"): height, width = int(args[0][0]), int(args[0][1]) else: height, width = int(args[0]), int(args[0]) else: height, width = int(args[0]), int(args[1]) mtrx = [[value]*width for _ in xrange(height)] return cls(mtrx) # pylint: disable-msg=C0103 @classmethod def Zero(cls, *args): """Creates a matrix filled with zeros. @keyword shape: the shape of the matrix. Can be a single integer, two integers or a tuple. If a single integer is given here, the matrix is assumed to be square-shaped. """ result = cls.Fill(0, *args) return result # pylint: disable-msg=C0103 @classmethod def Identity(cls, *args): """Creates an identity matrix. @keyword shape: the shape of the matrix. Can be a single integer, two integers or a tuple. If a single integer is given here, the matrix is assumed to be square-shaped. """ # pylint: disable-msg=W0212 result = cls.Fill(0, *args) for i in xrange(min(result.shape)): result._data[i][i] = 1 return result def _set_data(self, data=None): """Sets the data stored in the matrix""" if data is not None: self._data = [list(row) for row in data] self._nrow = len(self._data) if self._nrow > 0: self._ncol = max(len(row) for row in self._data) else: self._ncol = 0 for row in self._data: if len(row) < self._ncol: row.extend([0]*(self._ncol-len(row))) def _get_data(self): """Returns the data stored in the matrix as a list of lists""" return [list(row) for row in self._data] data = property(_get_data, _set_data) @property def shape(self): """Returns the shape of the matrix as a tuple""" return self._nrow, self._ncol def __add__(self, other): """Adds the given value to the matrix. @param other: either a scalar or a matrix. Scalars will be added to each element of the matrix. Matrices will be added together elementwise. @return: the result matrix """ if isinstance(other, Matrix): if self.shape != other.shape: raise ValueError("matrix shapes do not match") return self.__class__([ [a+b for a, b in izip(row_a, row_b)] for row_a, row_b in izip(self, other) ]) else: return self.__class__([ [item+other for item in row] for row in self]) def __eq__(self, other): """Checks whether a given matrix is equal to another one""" return isinstance(other, Matrix) and \ self._nrow == other._nrow and \ self._ncol == other._ncol and \ self._data == other._data def __getitem__(self, i): """Returns a single item, a row or a column of the matrix @param i: if a single integer, returns the M{i}th row as a list. If a slice, returns the corresponding rows as another L{Matrix} object. If a 2-tuple, the first element of the tuple is used to select a row and the second is used to select a column. """ if isinstance(i, int): return list(self._data[i]) elif isinstance(i, slice): return self.__class__(self._data[i]) elif isinstance(i, tuple): try: first = i[0] except IndexError: first = slice(None) try: second = i[1] except IndexError: second = slice(None) if type(first) == slice and type(second) == slice: return self.__class__(row[second] for row in self._data[first]) elif type(first) == slice: return [row[second] for row in self._data[first]] else: return self._data[first][second] else: raise IndexError("invalid matrix index") def __hash__(self): """Returns a hash value for a matrix.""" return hash(self._nrow, self._ncol, self._data) def __iadd__(self, other): """In-place addition of a matrix or scalar.""" if isinstance(other, Matrix): if self.shape != other.shape: raise ValueError("matrix shapes do not match") for row_a, row_b in izip(self._data, other): for i in xrange(len(row_a)): row_a[i] += row_b[i] else: for row in self._data: for i in xrange(len(row)): row[i] += other return self def __isub__(self, other): """In-place subtraction of a matrix or scalar.""" if isinstance(other, Matrix): if self.shape != other.shape: raise ValueError("matrix shapes do not match") for row_a, row_b in izip(self._data, other): for i in xrange(len(row_a)): row_a[i] -= row_b[i] else: for row in self._data: for i in xrange(len(row)): row[i] -= other return self def __ne__(self, other): """Checks whether a given matrix is not equal to another one""" return not self == other def __setitem__(self, i, value): """Sets a single item, a row or a column of the matrix @param i: if a single integer, sets the M{i}th row as a list. If a slice, sets the corresponding rows from another L{Matrix} object. If a 2-tuple, the first element of the tuple is used to select a row and the second is used to select a column. @param value: the new value """ if isinstance(i, int): # Setting a row if len(value) != len(self._data[i]): raise ValueError("new value must have %d items" % self._ncol) self._data[i] = list(value) elif isinstance(i, slice): # Setting multiple rows if len(value) != len(self._data[i]): raise ValueError("new value must have %d items" % self._ncol) if any(len(row) != self._ncol for row in value): raise ValueError("rows of new value must have %d items" % \ self._ncol) self._data[i] = [list(row) for row in value] elif isinstance(i, tuple): try: first = i[0] except IndexError: first = slice(None) try: second = i[1] except IndexError: second = slice(None) if type(first) == slice and type(second) == slice: # Setting a submatrix # TODO raise NotImplementedError elif type(first) == slice: # Setting a submatrix raise NotImplementedError else: # Setting a single element self._data[first][second] = value else: raise IndexError("invalid matrix index") def __sub__(self, other): """Subtracts the given value from the matrix. @param other: either a scalar or a matrix. Scalars will be subtracted from each element of the matrix. Matrices will be subtracted together elementwise. @return: the result matrix """ if isinstance(other, Matrix): if self.shape != other.shape: raise ValueError("matrix shapes do not match") return self.__class__([ [a-b for a, b in izip(row_a, row_b)] for row_a, row_b in izip(self, other) ]) else: return self.__class__([ [item-other for item in row] for row in self]) def __repr__(self): class_name = self.__class__.__name__ rows = ("[%s]" % ", ".join(repr(item) for item in row) for row in self) return "%s([%s])" % (class_name, ", ".join(rows)) def __str__(self): rows = ("[%s]" % ", ".join(repr(item) for item in row) for row in self) return "[%s]" % "\n ".join(rows) def __iter__(self): """Support for iteration. This is actually implemented as a generator, so there is no need for a separate iterator class. The generator returns I{copies} of the rows in the matrix as lists to avoid messing around with the internals. Feel free to do anything with the copies, the changes won't be reflected in the original matrix.""" return (list(row) for row in self._data) def __plot__(self, context, bbox, palette, **kwds): """Plots the matrix to the given Cairo context in the given box Besides the usual self-explanatory plotting parameters (C{context}, C{bbox}, C{palette}), it accepts the following keyword arguments: - C{style}: the style of the plot. C{boolean} is useful for plotting matrices with boolean (C{True}/C{False} or 0/1) values: C{False} will be shown with a white box and C{True} with a black box. C{palette} uses the given palette to represent numbers by colors, the minimum will be assigned to palette color index 0 and the maximum will be assigned to the length of the palette. C{None} draws transparent cell backgrounds only. The default style is C{boolean} (but it may change in the future). C{None} values in the matrix are treated specially in both cases: nothing is drawn in the cell corresponding to C{None}. - C{square}: whether the cells of the matrix should be square or not. Default is C{True}. - C{grid_width}: line width of the grid shown on the matrix. If zero or negative, the grid is turned off. The grid is also turned off if the size of a cell is less than three times the given line width. Default is C{1}. Fractional widths are also allowed. - C{border_width}: line width of the border drawn around the matrix. If zero or negative, the border is turned off. Default is C{1}. - C{row_names}: the names of the rows - C{col_names}: the names of the columns. - C{values}: values to be displayed in the cells. If C{None} or C{False}, no values are displayed. If C{True}, the values come from the matrix being plotted. If it is another matrix, the values of that matrix are shown in the cells. In this case, the shape of the value matrix must match the shape of the matrix being plotted. - C{value_format}: a format string or a callable that specifies how the values should be plotted. If it is a callable, it must be a function that expects a single value and returns a string. Example: C{"%#.2f"} for floating-point numbers with always exactly two digits after the decimal point. See the Python documentation of the C{%} operator for details on the format string. If the format string is not given, it defaults to the C{str} function. If only the row names or the column names are given and the matrix is square-shaped, the same names are used for both column and row names. """ # pylint: disable-msg=W0142 # pylint: disable-msg=C0103 grid_width = float(kwds.get("grid_width", 1.)) border_width = float(kwds.get("border_width", 1.)) style = kwds.get("style", "boolean") row_names = kwds.get("row_names") col_names = kwds.get("col_names", row_names) values = kwds.get("values") value_format = kwds.get("value_format", str) # Validations if style not in ("boolean", "palette", "none", None): raise ValueError("invalid style") if style == "none": style = None if row_names is None and col_names is not None: row_names = col_names if row_names is not None: row_names = [str(name) for name in islice(row_names, self._nrow)] if len(row_names) < self._nrow: row_names.extend([""]*(self._nrow-len(row_names))) if col_names is not None: col_names = [str(name) for name in islice(col_names, self._ncol)] if len(col_names) < self._ncol: col_names.extend([""]*(self._ncol-len(col_names))) if values == False: values = None if values == True: values = self if isinstance(values, list): values = Matrix(list) if values is not None and not isinstance(values, Matrix): raise TypeError("values must be None, False, True or a matrix") if values is not None and values.shape != self.shape: raise ValueError("values must be a matrix of size %s" % self.shape) # Calculate text extents if needed if row_names is not None or col_names is not None: te = context.text_extents space_width = te(" ")[4] max_row_name_width = max([te(s)[4] for s in row_names])+space_width max_col_name_width = max([te(s)[4] for s in col_names])+space_width else: max_row_name_width, max_col_name_width = 0, 0 # Calculate sizes total_width = float(bbox.width)-max_row_name_width total_height = float(bbox.height)-max_col_name_width dx = total_width / self.shape[1] dy = total_height / self.shape[0] if kwds.get("square", True): dx, dy = min(dx, dy), min(dx, dy) total_width, total_height = dx*self.shape[1], dy*self.shape[0] ox = bbox.left + (bbox.width - total_width - max_row_name_width) / 2.0 oy = bbox.top + (bbox.height - total_height - max_col_name_width) / 2.0 ox += max_row_name_width oy += max_col_name_width # Determine rescaling factors for the palette if needed if style == "palette": mi, ma = self.min(), self.max() color_offset = mi color_ratio = (len(palette)-1) / float(ma-mi) # Validate grid width if dx < 3*grid_width or dy < 3*grid_width: grid_width = 0. if grid_width > 0: context.set_line_width(grid_width) else: # When the grid width is zero, we will still stroke the # rectangles, but with the same color as the fill color # of the cell - otherwise we would get thin white lines # between the cells as a drawing artifact context.set_line_width(1) # Draw row names (if any) context.set_source_rgb(0., 0., 0.) if row_names is not None: x, y = ox, oy for heading in row_names: _, _, _, h, xa, _ = context.text_extents(heading) context.move_to(x-xa-space_width, y + (dy+h)/2.) context.show_text(heading) y += dy # Draw column names (if any) if col_names is not None: context.save() context.translate(ox, oy) context.rotate(-1.5707963285) # pi/2 x, y = 0., 0. for heading in col_names: _, _, _, h, _, _ = context.text_extents(heading) context.move_to(x+space_width, y + (dx+h)/2.) context.show_text(heading) y += dx context.restore() # Draw matrix x, y = ox, oy if style is None: fill = lambda: None else: fill = context.fill_preserve for row in self: for item in row: if item is None: x += dx continue if style == "boolean": if item: context.set_source_rgb(0., 0., 0.) else: context.set_source_rgb(1., 1., 1.) elif style == "palette": cidx = int((item-color_offset)*color_ratio) if cidx < 0: cidx = 0 context.set_source_rgba(*palette.get(cidx)) context.rectangle(x, y, dx, dy) if grid_width > 0: fill() context.set_source_rgb(0.5, 0.5, 0.5) context.stroke() else: fill() context.stroke() x += dx x, y = ox, y+dy # Draw cell values if values is not None: x, y = ox, oy context.set_source_rgb(0., 0., 0.) for row in values.data: if hasattr(value_format, "__call__"): values = [value_format(item) for item in row] else: values = [value_format % item for item in row] for item in values: th, tw = context.text_extents(item)[3:5] context.move_to(x+(dx-tw)/2., y+(dy+th)/2.) context.show_text(item) x += dx x, y = ox, y+dy # Draw borders if border_width > 0: context.set_line_width(border_width) context.set_source_rgb(0., 0., 0.) context.rectangle(ox, oy, dx*self.shape[1], dy*self.shape[0]) context.stroke() def min(self, dim=None): """Returns the minimum of the matrix along the given dimension @param dim: the dimension. 0 means determining the column minimums, 1 means determining the row minimums. If C{None}, the global minimum is returned. """ if dim == 1: return [min(row) for row in self._data] if dim == 0: return [min(row[idx] for row in self._data) \ for idx in xrange(self._ncol)] return min(min(row) for row in self._data) def max(self, dim=None): """Returns the maximum of the matrix along the given dimension @param dim: the dimension. 0 means determining the column maximums, 1 means determining the row maximums. If C{None}, the global maximum is returned. """ if dim == 1: return [max(row) for row in self._data] if dim == 0: return [max(row[idx] for row in self._data) \ for idx in xrange(self._ncol)] return max(max(row) for row in self._data) class DyadCensus(tuple): """Dyad census of a graph. This is a pretty simple class - basically it is a tuple, but it allows the user to refer to its individual items by the names C{mutual} (or C{mut}), C{asymmetric} (or C{asy} or C{asym} or C{asymm}) and C{null}. Examples: >>> from igraph import Graph >>> g=Graph.Erdos_Renyi(100, 0.2, directed=True) >>> dc=g.dyad_census() >>> print dc.mutual #doctest:+SKIP 179 >>> print dc["asym"] #doctest:+SKIP 1609 >>> print tuple(dc), list(dc) #doctest:+SKIP (179, 1609, 3162) [179, 1609, 3162] >>> print sorted(dc.as_dict().items()) #doctest:+ELLIPSIS [('asymmetric', ...), ('mutual', ...), ('null', ...)] @undocumented: _remap """ _remap = {"mutual": 0, "mut": 0, "sym": 0, "symm": 0, "asy": 1, "asym": 1, "asymm": 1, "asymmetric": 1, "null": 2} def __getitem__(self, idx): return tuple.__getitem__(self, self._remap.get(idx, idx)) def __getattr__(self, attr): if attr in self._remap: return tuple.__getitem__(self, self._remap[attr]) raise AttributeError("no such attribute: %s" % attr) def __repr__(self): return "DyadCensus((%d, %d, %d))" % self def __str__(self): return "%d mutual, %d asymmetric, %d null dyads" % self def as_dict(self): """Converts the dyad census to a dict using the known dyad names.""" return {"mutual": self[0], "asymmetric": self[1], "null": self[2]} class TriadCensus(tuple): """Triad census of a graph. This is a pretty simple class - basically it is a tuple, but it allows the user to refer to its individual items by the following triad names: - C{003} -- the empty graph - C{012} -- a graph with a single directed edge (C{A --> B, C}) - C{102} -- a graph with a single mutual edge (C{A <-> B, C}) - C{021D} -- the binary out-tree (C{A <-- B --> C}) - C{021U} -- the binary in-tree (C{A --> B <-- C}) - C{021C} -- the directed line (C{A --> B --> C}) - C{111D} -- C{A <-> B <-- C} - C{111U} -- C{A <-> B --> C} - C{030T} -- C{A --> B <-- C, A --> C} - C{030C} -- C{A <-- B <-- C, A --> C} - C{201} -- C{A <-> B <-> C} - C{120D} -- C{A <-- B --> C, A <-> C} - C{120U} -- C{A --> B <-- C, A <-> C} - C{120C} -- C{A --> B --> C, A <-> C} - C{210C} -- C{A --> B <-> C, A <-> C} - C{300} -- the complete graph (C{A <-> B <-> C, A <-> C}) Attribute and item accessors are provided. Due to the syntax of Python, attribute names are not allowed to start with a number, therefore the triad names must be prepended with a lowercase C{t} when accessing them as attributes. This is not necessary with the item accessor syntax. Examples: >>> from igraph import Graph >>> g=Graph.Erdos_Renyi(100, 0.2, directed=True) >>> tc=g.triad_census() >>> print tc.t003 #doctest:+SKIP 39864 >>> print tc["030C"] #doctest:+SKIP 1206 """ _remap = {"003": 0, "012": 1, "102": 2, "021D": 3, "021U": 4, "021C": 5, \ "111D": 6, "111U": 7, "030T": 8, "030C": 9, "201": 10, "120D": 11, \ "120U": 12, "120C": 13, "210": 14, "300": 15} def __getitem__(self, idx): if isinstance(idx, basestring): idx = idx.upper() return tuple.__getitem__(self, self._remap.get(idx, idx)) def __getattr__(self, attr): if isinstance(attr, basestring) and attr[0] == 't' \ and attr[1:].upper() in self._remap: return tuple.__getitem__(self, self._remap[attr[1:].upper()]) raise AttributeError("no such attribute: %s" % attr) def __repr__(self): return "TriadCensus((%s))" % ", ".join(str(item) for item in self) def __str__(self): maxidx = len(self) maxcount = max(self) numwidth = len(str(maxcount)) captionwidth = max(len(key) for key in self._remap) colcount = 4 rowcount = maxidx / colcount if rowcount * colcount < maxidx: rowcount += 1 invmap = dict((v, k) for k, v in self._remap.iteritems()) result, row, idx = [], [], 0 for _ in xrange(rowcount): for _ in xrange(colcount): if idx >= maxidx: break row.append("%-*s: %*d" % (captionwidth, invmap.get(idx, ""), numwidth, self[idx])) idx += 1 result.append(" | ".join(row)) row = [] return "\n".join(result) class UniqueIdGenerator(object): """A dictionary-like class that can be used to assign unique IDs to names (say, vertex names). Usage: >>> gen = UniqueIdGenerator() >>> gen["A"] 0 >>> gen["B"] 1 >>> gen["C"] 2 >>> gen["A"] # Retrieving already existing ID 0 >>> gen.add("D") # Synonym of gen["D"] 3 >>> len(gen) # Number of already used IDs 4 >>> "C" in gen True >>> "E" in gen False """ def __init__(self, id_generator=None, initial=None): """Creates a new unique ID generator. `id_generator` specifies how do we assign new IDs to elements that do not have an ID yet. If it is `None`, elements will be assigned integer identifiers starting from 0. If it is an integer, elements will be assigned identifiers starting from the given integer. If it is an iterator or generator, its `next` method will be called every time a new ID is needed.""" if id_generator is None: id_generator = 0 if isinstance(id_generator, int): import itertools self._generator = itertools.count(id_generator) else: self._generator = id_generator self._ids = {} if initial: for value in initial: self.add(value) def __contains__(self, item): """Checks whether `item` already has an ID or not.""" return item in self._ids def __getitem__(self, item): """Retrieves the ID corresponding to `item`. Generates a new ID for `item` if it is the first time we request an ID for it.""" try: return self._ids[item] except KeyError: self._ids[item] = self._generator.next() return self._ids[item] def __setitem__(self, item, value): """Overrides the ID for `item`.""" self._ids[item] = value def __len__(self): """"Returns the number of items""" return len(self._ids) def reverse_dict(self): """Returns the reverse mapping, i.e., the one that maps from generated IDs to their corresponding objects""" return dict((v, k) for k, v in self._ids.iteritems()) def values(self): """Returns the values stored so far. If the generator generates items according to the standard sorting order, the values returned will be exactly in the order they were added. This holds for integer IDs for instance (but for many other ID generators as well).""" return sorted(self._ids.keys(), key = self._ids.__getitem__) add = __getitem__ python-igraph-0.7.1.post6/igraph/drawing/0000755000076500000240000000000012534343010020670 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/igraph/drawing/__init__.py0000644000076500000240000004702112534305077023020 0ustar ntamasstaff00000000000000""" Drawing and plotting routines for IGraph. Plotting is dependent on the C{pycairo} library which provides Python bindings to the popular U{Cairo library}. This means that if you don't have U{pycairo} installed, you won't be able to use the plotting capabilities. However, you can still use L{Graph.write_svg} to save the graph to an SVG file and view it from U{Mozilla Firefox} (free) or edit it in U{Inkscape} (free), U{Skencil} (formerly known as Sketch, also free) or Adobe Illustrator (not free, therefore I'm not linking to it :)). """ from __future__ import with_statement from cStringIO import StringIO from warnings import warn import os import platform import time from igraph.compat import property, BytesIO from igraph.configuration import Configuration from igraph.drawing.colors import Palette, palettes from igraph.drawing.graph import DefaultGraphDrawer from igraph.drawing.utils import BoundingBox, Point, Rectangle, find_cairo from igraph.utils import _is_running_in_ipython, named_temporary_file __all__ = ["BoundingBox", "DefaultGraphDrawer", "Plot", "Point", "Rectangle", "plot"] __license__ = "GPL" cairo = find_cairo() ##################################################################### class Plot(object): """Class representing an arbitrary plot Every plot has an associated surface object where the plotting is done. The surface is an instance of C{cairo.Surface}, a member of the C{pycairo} library. The surface itself provides a unified API to various plotting targets like SVG files, X11 windows, PostScript files, PNG files and so on. C{igraph} usually does not know on which surface it is plotting right now, since C{pycairo} takes care of the actual drawing. Everything that's supported by C{pycairo} should be supported by this class as well. Current Cairo surfaces that I'm aware of are: - C{cairo.GlitzSurface} -- OpenGL accelerated surface for the X11 Window System. - C{cairo.ImageSurface} -- memory buffer surface. Can be written to a C{PNG} image file. - C{cairo.PDFSurface} -- PDF document surface. - C{cairo.PSSurface} -- PostScript document surface. - C{cairo.SVGSurface} -- SVG (Scalable Vector Graphics) document surface. - C{cairo.Win32Surface} -- Microsoft Windows screen rendering. - C{cairo.XlibSurface} -- X11 Window System screen rendering. If you create a C{Plot} object with a string given as the target surface, the string will be treated as a filename, and its extension will decide which surface class will be used. Please note that not all surfaces might be available, depending on your C{pycairo} installation. A C{Plot} has an assigned default palette (see L{igraph.drawing.colors.Palette}) which is used for plotting objects. A C{Plot} object also has a list of objects to be plotted with their respective bounding boxes, palettes and opacities. Palettes assigned to an object override the default palette of the plot. Objects can be added by the L{Plot.add} method and removed by the L{Plot.remove} method. """ # pylint: disable-msg=E1101 # E1101: Module 'cairo' has no 'foo' member - of course it has! :) def __init__(self, target=None, bbox=None, palette=None, background=None): """Creates a new plot. @param target: the target surface to write to. It can be one of the following types: - C{None} -- an appropriate surface will be created and the object will be plotted there. - C{cairo.Surface} -- the given Cairo surface will be used. - C{string} -- a file with the given name will be created and an appropriate Cairo surface will be attached to it. @param bbox: the bounding box of the surface. It is interpreted differently with different surfaces: PDF and PS surfaces will treat it as points (1 point = 1/72 inch). Image surfaces will treat it as pixels. SVG surfaces will treat it as an abstract unit, but it will mostly be interpreted as pixels when viewing the SVG file in Firefox. @param palette: the palette primarily used on the plot if the added objects do not specify a private palette. Must be either an L{igraph.drawing.colors.Palette} object or a string referring to a valid key of C{igraph.drawing.colors.palettes} (see module L{igraph.drawing.colors}) or C{None}. In the latter case, the default palette given by the configuration key C{plotting.palette} is used. @param background: the background color. If C{None}, the background will be transparent. You can use any color specification here that is understood by L{igraph.drawing.colors.color_name_to_rgba}. """ self._filename = None self._surface_was_created = not isinstance(target, cairo.Surface) self._need_tmpfile = False # Several Windows-specific hacks will be used from now on, thanks # to Dale Hunscher for debugging and fixing all that stuff self._windows_hacks = "Windows" in platform.platform() if bbox is None: self.bbox = BoundingBox(600, 600) elif isinstance(bbox, tuple) or isinstance(bbox, list): self.bbox = BoundingBox(bbox) else: self.bbox = bbox if palette is None: config = Configuration.instance() palette = config["plotting.palette"] if not isinstance(palette, Palette): palette = palettes[palette] self._palette = palette if target is None: self._need_tmpfile = True self._surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \ int(self.bbox.width), int(self.bbox.height)) elif isinstance(target, cairo.Surface): self._surface = target else: self._filename = target _, ext = os.path.splitext(target) ext = ext.lower() if ext == ".pdf": self._surface = cairo.PDFSurface(target, self.bbox.width, \ self.bbox.height) elif ext == ".ps" or ext == ".eps": self._surface = cairo.PSSurface(target, self.bbox.width, \ self.bbox.height) elif ext == ".png": self._surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \ int(self.bbox.width), int(self.bbox.height)) elif ext == ".svg": self._surface = cairo.SVGSurface(target, self.bbox.width, \ self.bbox.height) else: raise ValueError("image format not handled by Cairo: %s" % ext) self._ctx = cairo.Context(self._surface) self._objects = [] self._is_dirty = False self.background = background def add(self, obj, bbox=None, palette=None, opacity=1.0, *args, **kwds): """Adds an object to the plot. Arguments not specified here are stored and passed to the object's plotting function when necessary. Since you are most likely interested in the arguments acceptable by graphs, see L{Graph.__plot__} for more details. @param obj: the object to be added @param bbox: the bounding box of the object. If C{None}, the object will fill the entire area of the plot. @param palette: the color palette used for drawing the object. If the object tries to get a color assigned to a positive integer, it will use this palette. If C{None}, defaults to the global palette of the plot. @param opacity: the opacity of the object being plotted, in the range 0.0-1.0 @see: Graph.__plot__ """ if opacity < 0.0 or opacity > 1.0: raise ValueError("opacity must be between 0.0 and 1.0") if bbox is None: bbox = self.bbox if not isinstance(bbox, BoundingBox): bbox = BoundingBox(bbox) self._objects.append((obj, bbox, palette, opacity, args, kwds)) self.mark_dirty() @property def background(self): """Returns the background color of the plot. C{None} means a transparent background. """ return self._background @background.setter def background(self, color): """Sets the background color of the plot. C{None} means a transparent background. You can use any color specification here that is understood by the C{get} method of the current palette or by L{igraph.colors.color_name_to_rgb}. """ if color is None: self._background = None else: self._background = self._palette.get(color) def remove(self, obj, bbox=None, idx=1): """Removes an object from the plot. If the object has been added multiple times and no bounding box was specified, it removes the instance which occurs M{idx}th in the list of identical instances of the object. @param obj: the object to be removed @param bbox: optional bounding box specification for the object. If given, only objects with exactly this bounding box will be considered. @param idx: if multiple objects match the specification given by M{obj} and M{bbox}, only the M{idx}th occurrence will be removed. @return: C{True} if the object has been removed successfully, C{False} if the object was not on the plot at all or M{idx} was larger than the count of occurrences """ for i in xrange(len(self._objects)): current_obj, current_bbox = self._objects[i][0:2] if current_obj is obj and (bbox is None or current_bbox == bbox): idx -= 1 if idx == 0: self._objects[i:(i+1)] = [] self.mark_dirty() return True return False def mark_dirty(self): """Marks the plot as dirty (should be redrawn)""" self._is_dirty = True # pylint: disable-msg=W0142 # W0142: used * or ** magic def redraw(self, context=None): """Redraws the plot""" ctx = context or self._ctx if self._background is not None: ctx.set_source_rgba(*self._background) ctx.rectangle(0, 0, self.bbox.width, self.bbox.height) ctx.fill() for obj, bbox, palette, opacity, args, kwds in self._objects: if palette is None: palette = getattr(obj, "_default_palette", self._palette) plotter = getattr(obj, "__plot__", None) if plotter is None: warn("%s does not support plotting" % obj) else: if opacity < 1.0: ctx.push_group() else: ctx.save() plotter(ctx, bbox, palette, *args, **kwds) if opacity < 1.0: ctx.pop_group_to_source() ctx.paint_with_alpha(opacity) else: ctx.restore() self._is_dirty = False def save(self, fname=None): """Saves the plot. @param fname: the filename to save to. It is ignored if the surface of the plot is not an C{ImageSurface}. """ if self._is_dirty: self.redraw() if isinstance(self._surface, cairo.ImageSurface): if fname is None and self._need_tmpfile: with named_temporary_file(prefix="igraph", suffix=".png") as fname: self._surface.write_to_png(fname) return None fname = fname or self._filename if fname is None: raise ValueError("no file name is known for the surface " + \ "and none given") return self._surface.write_to_png(fname) if fname is not None: warn("filename is ignored for surfaces other than ImageSurface") self._ctx.show_page() self._surface.finish() def show(self): """Saves the plot to a temporary file and shows it.""" if not isinstance(self._surface, cairo.ImageSurface): sur = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(self.bbox.width), int(self.bbox.height)) ctx = cairo.Context(sur) self.redraw(ctx) else: sur = self._surface ctx = self._ctx if self._is_dirty: self.redraw(ctx) with named_temporary_file(prefix="igraph", suffix=".png") as tmpfile: sur.write_to_png(tmpfile) config = Configuration.instance() imgviewer = config["apps.image_viewer"] if not imgviewer: # No image viewer was given and none was detected. This # should only happen on unknown platforms. plat = platform.system() raise NotImplementedError("showing plots is not implemented " + \ "on this platform: %s" % plat) else: os.system("%s %s" % (imgviewer, tmpfile)) if platform.system() == "Darwin" or self._windows_hacks: # On Mac OS X and Windows, launched applications are likely to # fork and give control back to Python immediately. # Chances are that the temporary image file gets removed # before the image viewer has a chance to open it, so # we wait here a little bit. Yes, this is quite hackish :( time.sleep(5) def _repr_svg_(self): """Returns an SVG representation of this plot as a string. This method is used by IPython to display this plot inline. """ io = BytesIO() # Create a new SVG surface and use that to get the SVG representation, # which will end up in io surface = cairo.SVGSurface(io, self.bbox.width, self.bbox.height) context = cairo.Context(surface) # Plot the graph on this context self.redraw(context) # No idea why this is needed but python crashes without context.show_page() surface.finish() # Return the raw SVG representation return io.getvalue().encode("utf-8") @property def bounding_box(self): """Returns the bounding box of the Cairo surface as a L{BoundingBox} object""" return BoundingBox(self.bbox) @property def height(self): """Returns the height of the Cairo surface on which the plot is drawn""" return self.bbox.height @property def surface(self): """Returns the Cairo surface on which the plot is drawn""" return self._surface @property def width(self): """Returns the width of the Cairo surface on which the plot is drawn""" return self.bbox.width ##################################################################### def plot(obj, target=None, bbox=(0, 0, 600, 600), *args, **kwds): """Plots the given object to the given target. Positional and keyword arguments not explicitly mentioned here will be passed down to the C{__plot__} method of the object being plotted. Since you are most likely interested in the keyword arguments available for graph plots, see L{Graph.__plot__} as well. @param obj: the object to be plotted @param target: the target where the object should be plotted. It can be one of the following types: - C{None} -- an appropriate surface will be created and the object will be plotted there. - C{cairo.Surface} -- the given Cairo surface will be used. This can refer to a PNG image, an arbitrary window, an SVG file, anything that Cairo can handle. - C{string} -- a file with the given name will be created and an appropriate Cairo surface will be attached to it. The supported image formats are: PNG, PDF, SVG and PostScript. @param bbox: the bounding box of the plot. It must be a tuple with either two or four integers, or a L{BoundingBox} object. If this is a tuple with two integers, it is interpreted as the width and height of the plot (in pixels for PNG images and on-screen plots, or in points for PDF, SVG and PostScript plots, where 72 pt = 1 inch = 2.54 cm). If this is a tuple with four integers, the first two denotes the X and Y coordinates of a corner and the latter two denoting the X and Y coordinates of the opposite corner. @keyword opacity: the opacity of the object being plotted. It can be used to overlap several plots of the same graph if you use the same layout for them -- for instance, you might plot a graph with opacity 0.5 and then plot its spanning tree over it with opacity 0.1. To achieve this, you'll need to modify the L{Plot} object returned with L{Plot.add}. @keyword palette: the palette primarily used on the plot if the added objects do not specify a private palette. Must be either an L{igraph.drawing.colors.Palette} object or a string referring to a valid key of C{igraph.drawing.colors.palettes} (see module L{igraph.drawing.colors}) or C{None}. In the latter case, the default palette given by the configuration key C{plotting.palette} is used. @keyword margin: the top, right, bottom, left margins as a 4-tuple. If it has less than 4 elements or is a single float, the elements will be re-used until the length is at least 4. The default margin is 20 on each side. @keyword inline: whether to try and show the plot object inline in the current IPython notebook. Passing ``None`` here or omitting this keyword argument will look up the preferred behaviour from the C{shell.ipython.inlining.Plot} configuration key. Note that this keyword argument has an effect only if igraph is run inside IPython and C{target} is C{None}. @return: an appropriate L{Plot} object. @see: Graph.__plot__ """ if not isinstance(bbox, BoundingBox): bbox = BoundingBox(bbox) result = Plot(target, bbox, background=kwds.get("background", "white")) if "margin" in kwds: bbox = bbox.contract(kwds["margin"]) del kwds["margin"] else: bbox = bbox.contract(20) result.add(obj, bbox, *args, **kwds) if target is None and _is_running_in_ipython(): # Get the default value of the `inline` argument from the configuration if # needed inline = kwds.get("inline") if inline is None: config = Configuration.instance() inline = config["shell.ipython.inlining.Plot"] # If we requested an inline plot, just return the result and IPython will # call its _repr_svg_ method. If we requested a non-inline plot, show the # plot in a separate window and return nothing if inline: return result else: result.show() return # We are either not in IPython or the user specified an explicit plot target, # so just show or save the result if target is None: result.show() elif isinstance(target, basestring): result.save() # Also return the plot itself return result ##################################################################### python-igraph-0.7.1.post6/igraph/drawing/baseclasses.py0000644000076500000240000001220012453614202023531 0ustar ntamasstaff00000000000000""" Abstract base classes for the drawing routines. """ from igraph.compat import property from igraph.drawing.utils import BoundingBox from math import pi ##################################################################### # pylint: disable-msg=R0903 # R0903: too few public methods class AbstractDrawer(object): """Abstract class that serves as a base class for anything that draws an igraph object.""" def draw(self, *args, **kwds): """Abstract method, must be implemented in derived classes.""" raise NotImplementedError("abstract class") ##################################################################### # pylint: disable-msg=R0903 # R0903: too few public methods class AbstractCairoDrawer(AbstractDrawer): """Abstract class that serves as a base class for anything that draws on a Cairo context within a given bounding box. A subclass of L{AbstractCairoDrawer} is guaranteed to have an attribute named C{context} that represents the Cairo context to draw on, and an attribute named C{bbox} for the L{BoundingBox} of the drawing area. """ def __init__(self, context, bbox): """Constructs the drawer and associates it to the given Cairo context and the given L{BoundingBox}. @param context: the context on which we will draw @param bbox: the bounding box within which we will draw. Can be anything accepted by the constructor of L{BoundingBox} (i.e., a 2-tuple, a 4-tuple or a L{BoundingBox} object). """ self.context = context self._bbox = None self.bbox = bbox @property def bbox(self): """The bounding box of the drawing area where this drawer will draw.""" return self._bbox @bbox.setter def bbox(self, bbox): """Sets the bounding box of the drawing area where this drawer will draw.""" if not isinstance(bbox, BoundingBox): self._bbox = BoundingBox(bbox) else: self._bbox = bbox def draw(self, *args, **kwds): """Abstract method, must be implemented in derived classes.""" raise NotImplementedError("abstract class") def _mark_point(self, x, y, color=0, size=4): """Marks the given point with a small circle on the canvas. Used primarily for debugging purposes. @param x: the X coordinate of the point to mark @param y: the Y coordinate of the point to mark @param color: the color of the marker. It can be a 3-tuple (RGB components, alpha=0.5), a 4-tuple (RGBA components) or an index where zero means red, 1 means green, 2 means blue and so on. @param size: the diameter of the marker. """ if isinstance(color, int): colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 1, 0), (0, 1, 1), (1, 0, 1)] color = colors[color % len(colors)] if len(color) == 3: color += (0.5, ) ctx = self.context ctx.save() ctx.set_source_rgba(*color) ctx.arc(x, y, size / 2.0, 0, 2*pi) ctx.fill() ctx.restore() ##################################################################### class AbstractXMLRPCDrawer(AbstractDrawer): """Abstract drawer that uses a remote service via XML-RPC to draw something on a remote display. """ def __init__(self, url, service=None): """Constructs an abstract drawer using the XML-RPC service at the given URL. @param url: the URL where the XML-RPC calls for the service should be addressed to. @param service: the name of the service at the XML-RPC address. If C{None}, requests will be directed to the server proxy object constructed by C{xmlrpclib.ServerProxy}; if not C{None}, the given attribute will be looked up in the server proxy object. """ import xmlrpclib url = self._resolve_hostname(url) self.server = xmlrpclib.ServerProxy(url) if service is None: self.service = self.server else: self.service = getattr(self.server, service) @staticmethod def _resolve_hostname(url): """Parses the given URL, resolves the hostname to an IP address and returns a new URL with the resolved IP address. This speeds up things big time on Mac OS X where an IP lookup would be performed for every XML-RPC call otherwise.""" from urlparse import urlparse, urlunparse import re url_parts = urlparse(url) hostname = url_parts.netloc if re.match("[0-9.:]+$", hostname): # the hostname is already an IP address, possibly with a port return url from socket import gethostbyname if ":" in hostname: hostname = hostname[0:hostname.index(":")] hostname = gethostbyname(hostname) if url_parts.port is not None: hostname = "%s:%d" % (hostname, url_parts.port) url_parts = list(url_parts) url_parts[1] = hostname return urlunparse(url_parts) python-igraph-0.7.1.post6/igraph/drawing/colors.py0000644000076500000240000035722512460534507022574 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """ Color handling functions. """ __license__ = u"""\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ from igraph.datatypes import Matrix from igraph.utils import str_to_orientation from math import ceil __all__ = ["Palette", "GradientPalette", "AdvancedGradientPalette", \ "RainbowPalette", "PrecalculatedPalette", "ClusterColoringPalette", \ "color_name_to_rgb", "color_name_to_rgba", \ "hsv_to_rgb", "hsva_to_rgba", "hsl_to_rgb", "hsla_to_rgba", \ "rgb_to_hsv", "rgba_to_hsva", "rgb_to_hsl", "rgba_to_hsla", \ "palettes", "known_colors"] class Palette(object): """Base class of color palettes. Color palettes are mappings that assign integers from the range 0..M{n-1} to colors (4-tuples). M{n} is called the size or length of the palette. C{igraph} comes with a number of predefined palettes, so this class is useful for you only if you want to define your own palette. This can be done by subclassing this class and implementing the L{Palette._get} method as necessary. Palettes can also be used as lists or dicts, for the C{__getitem__} method is overridden properly to call L{Palette.get}. """ def __init__(self, n): self._length = n self._cache = {} def clear_cache(self): """Clears the result cache. The return values of L{Palette.get} are cached. Use this method to clear the cache. """ self._cache = {} def get(self, v): """Returns the given color from the palette. Values are cached: if the specific value given has already been looked up, its value will be returned from the cache instead of calculating it again. Use L{Palette.clear_cache} to clear the cache if necessary. @note: you shouldn't override this method in subclasses, override L{_get} instead. If you override this method, lookups in the L{known_colors} dict won't work, so you won't be able to refer to colors by names or RGBA quadruplets, only by integer indices. The caching functionality will disappear as well. However, feel free to override this method if this is exactly the behaviour you want. @param v: the color to be retrieved. If it is an integer, it is passed to L{Palette._get} to be translated to an RGBA quadruplet. Otherwise it is passed to L{color_name_to_rgb()} to determine the RGBA values. @return: the color as an RGBA quadruplet""" if isinstance(v, list): v = tuple(v) try: return self._cache[v] except KeyError: pass if isinstance(v, (int, long)): if v < 0: raise IndexError("color index must be non-negative") if v >= self._length: raise IndexError("color index too large") result = self._get(v) else: result = color_name_to_rgba(v) self._cache[v] = result return result def get_many(self, colors): """Returns multiple colors from the palette. Values are cached: if the specific value given has already been looked upon, its value will be returned from the cache instead of calculating it again. Use L{Palette.clear_cache} to clear the cache if necessary. @param colors: the list of colors to be retrieved. The palette class tries to make an educated guess here: if it is not possible to interpret the value you passed here as a list of colors, the class will simply try to interpret it as a single color by forwarding the value to L{Palette.get}. @return: the colors as a list of RGBA quadruplets. The result will be a list even if you passed a single color index or color name. """ if isinstance(colors, (basestring, int, long)): # Single color name or index return [self.get(colors)] # Multiple colors return [self.get(color) for color in colors] def _get(self, v): """Override this method in a subclass to create a custom palette. You can safely assume that v is an integer in the range 0..M{n-1} where M{n} is the size of the palette. @param v: numerical index of the color to be retrieved @return: a 4-tuple containing the RGBA values""" raise NotImplementedError("abstract class") __getitem__ = get @property def length(self): """Returns the number of colors in this palette""" return self._length def __len__(self): """Returns the number of colors in this palette""" return self._length def __plot__(self, context, bbox, palette, *args, **kwds): """Plots the colors of the palette on the given Cairo context Supported keyword arguments are: - C{border_width}: line width of the border shown around the palette. If zero or negative, the border is turned off. Default is C{1}. - C{grid_width}: line width of the grid that separates palette cells. If zero or negative, the grid is turned off. The grid is also turned off if the size of a cell is less than three times the given line width. Default is C{0}. Fractional widths are also allowed. - C{orientation}: the orientation of the palette. Must be one of the following values: C{left-right}, C{bottom-top}, C{right-left} or C{top-bottom}. Possible aliases: C{horizontal} = C{left-right}, C{vertical} = C{bottom-top}, C{lr} = C{left-right}, C{rl} = C{right-left}, C{tb} = C{top-bottom}, C{bt} = C{bottom-top}. The default is C{left-right}. """ border_width = float(kwds.get("border_width", 1.)) grid_width = float(kwds.get("grid_width", 0.)) orientation = str_to_orientation(kwds.get("orientation", "lr")) # Construct a matrix and plot that indices = range(len(self)) if orientation in ("rl", "bt"): indices.reverse() if orientation in ("lr", "rl"): matrix = Matrix([indices]) else: matrix = Matrix([[i] for i in indices]) return matrix.__plot__(context, bbox, self, style="palette", square=False, grid_width=grid_width, border_width=border_width) def __repr__(self): return "<%s with %d colors>" % (self.__class__.__name__, self._length) class GradientPalette(Palette): """Base class for gradient palettes Gradient palettes contain a gradient between two given colors. Example: >>> pal = GradientPalette("red", "blue", 5) >>> pal.get(0) (1.0, 0.0, 0.0, 1.0) >>> pal.get(2) (0.5, 0.0, 0.5, 1.0) >>> pal.get(4) (0.0, 0.0, 1.0, 1.0) """ def __init__(self, color1, color2, n=256): """Creates a gradient palette. @param color1: the color where the gradient starts. @param color2: the color where the gradient ends. @param n: the number of colors in the palette. """ Palette.__init__(self, n) self._color1 = color_name_to_rgba(color1) self._color2 = color_name_to_rgba(color2) def _get(self, v): """Returns the color corresponding to the given color index. @param v: numerical index of the color to be retrieved @return: a 4-tuple containing the RGBA values""" ratio = float(v)/(len(self)-1) return tuple(self._color1[x]*(1-ratio) + \ self._color2[x]*ratio for x in range(4)) class AdvancedGradientPalette(Palette): """Advanced gradient that consists of more than two base colors. Example: >>> pal = AdvancedGradientPalette(["red", "black", "blue"], n=9) >>> pal.get(2) (0.5, 0.0, 0.0, 1.0) >>> pal.get(7) (0.0, 0.0, 0.75, 1.0) """ def __init__(self, colors, indices=None, n=256): """Creates an advanced gradient palette @param colors: the colors in the gradient. @param indices: the color indices belonging to the given colors. If C{None}, the colors are distributed equidistantly @param n: the total number of colors in the palette """ Palette.__init__(self, n) if indices is None: diff = float(n-1) / (len(colors)-1) indices = [i * diff for i in xrange(len(colors))] elif not hasattr(indices, "__iter__"): indices = [float(x) for x in indices] self._indices, self._colors = zip(*sorted(zip(indices, colors))) self._colors = [color_name_to_rgba(color) for color in self._colors] self._dists = [curr-prev for curr, prev in \ zip(self._indices[1:], self._indices)] def _get(self, v): """Returns the color corresponding to the given color index. @param v: numerical index of the color to be retrieved @return: a 4-tuple containing the RGBA values""" colors = self._colors for i in xrange(len(self._indices)-1): if self._indices[i] <= v and self._indices[i+1] >= v: dist = self._dists[i] ratio = float(v-self._indices[i])/dist return tuple([colors[i][x]*(1-ratio)+colors[i+1][x]*ratio \ for x in range(4)]) return (0., 0., 0., 1.) class RainbowPalette(Palette): """A palette that varies the hue of the colors along a scale. Colors in a rainbow palette all have the same saturation, value and alpha components, while the hue is varied between two given extremes linearly. This palette has the advantage that it wraps around nicely if the hue is varied between zero and one (which is the default). Example: >>> pal = RainbowPalette(n=120) >>> pal.get(0) (1.0, 0.0, 0.0, 1.0) >>> pal.get(20) (1.0, 1.0, 0.0, 1.0) >>> pal.get(40) (0.0, 1.0, 0.0, 1.0) >>> pal = RainbowPalette(n=120, s=1, v=0.5, alpha=0.75) >>> pal.get(60) (0.0, 0.5, 0.5, 0.75) >>> pal.get(80) (0.0, 0.0, 0.5, 0.75) >>> pal.get(100) (0.5, 0.0, 0.5, 0.75) >>> pal = RainbowPalette(n=120) >>> pal2 = RainbowPalette(n=120, start=0.5, end=0.5) >>> pal.get(60) == pal2.get(0) True >>> pal.get(90) == pal2.get(30) True This palette was modeled after the C{rainbow} command of R. """ def __init__(self, n=256, s=1, v=1, start=0, end=1, alpha=1): """Creates a rainbow palette. @param n: the number of colors in the palette. @param s: the saturation of the colors in the palette. @param v: the value component of the colors in the palette. @param start: the hue at which the rainbow begins (between 0 and 1). @param end: the hue at which the rainbow ends (between 0 and 1). @param alpha: the alpha component of the colors in the palette. """ Palette.__init__(self, n) self._s = float(clamp(s, 0, 1)) self._v = float(clamp(v, 0, 1)) self._alpha = float(clamp(alpha, 0, 1)) self._start = float(start) if end == self._start: end += 1 self._dh = (end - self._start) / n def _get(self, v): """Returns the color corresponding to the given color index. @param v: numerical index of the color to be retrieved @return: a 4-tuple containing the RGBA values""" return hsva_to_rgba(self._start + v * self._dh, self._s, self._v, self._alpha) class PrecalculatedPalette(Palette): """A palette that returns colors from a pre-calculated list of colors""" def __init__(self, l): """Creates the palette backed by the given list. The list must contain RGBA quadruplets or color names, which will be resolved first by L{color_name_to_rgba()}. Anything that is understood by L{color_name_to_rgba()} is OK here.""" Palette.__init__(self, len(l)) for idx, color in enumerate(l): if isinstance(color, basestring): color = color_name_to_rgba(color) self._cache[idx] = color def _get(self, v): """This method will only be called if the requested color index is outside the size of the palette. In that case, we throw an exception""" raise ValueError("palette index outside bounds: %s" % v) class ClusterColoringPalette(PrecalculatedPalette): """A palette suitable for coloring vertices when plotting a clustering. This palette tries to make sure that the colors are easily distinguishable. This is achieved by using a set of base colors and their lighter and darker variants, depending on the number of elements in the palette. When the desired size of the palette is less than or equal to the number of base colors (denoted by M{n}), only the bsae colors will be used. When the size of the palette is larger than M{n} but less than M{2*n}, the base colors and their lighter variants will be used. Between M{2*n} and M{3*n}, the base colors and their lighter and darker variants will be used. Above M{3*n}, more darker and lighter variants will be generated, but this makes the individual colors less and less distinguishable. """ def __init__(self, n): base_colors = ["red", "green", "blue", "yellow", \ "magenta", "cyan", "#808080"] base_colors = [color_name_to_rgba(name) for name in base_colors] num_base_colors = len(base_colors) colors = base_colors[:] blocks_to_add = ceil(float(n - num_base_colors) / num_base_colors) ratio_increment = 1.0 / (ceil(blocks_to_add / 2.0) + 1) adding_darker = True ratio = ratio_increment while len(colors) < n: if adding_darker: new_block = [darken(color, ratio) for color in base_colors] else: new_block = [lighten(color, ratio) for color in base_colors] ratio += ratio_increment colors.extend(new_block) adding_darker = not adding_darker colors = colors[0:n] PrecalculatedPalette.__init__(self, colors) def clamp(value, min_value, max_value): """Clamps the given value between min and max""" if value > max_value: return max_value if value < min_value: return min_value return value def color_name_to_rgb(color, palette=None): """Converts a color given in one of the supported color formats to R-G-B values. This is done by calling L{color_name_to_rgba} and then throwing away the alpha value. @see: color_name_to_rgba for more details about what formats are understood by this function. """ return color_name_to_rgba(color, palette)[:3] def color_name_to_rgba(color, palette=None): """Converts a color given in one of the supported color formats to R-G-B-A values. Examples: >>> color_name_to_rgba("red") (1.0, 0.0, 0.0, 1.0) >>> color_name_to_rgba("#ff8000") == (1.0, 128/255.0, 0.0, 1.0) True >>> color_name_to_rgba("#ff800080") == (1.0, 128/255.0, 0.0, 128/255.0) True >>> color_name_to_rgba("#08f") == (0.0, 136/255.0, 1.0, 1.0) True >>> color_name_to_rgba("rgb(100%, 50%, 0%)") (1.0, 0.5, 0.0, 1.0) >>> color_name_to_rgba("rgba(100%, 50%, 0%, 25%)") (1.0, 0.5, 0.0, 0.25) >>> color_name_to_rgba("hsla(120, 100%, 50%, 0.5)") (0.0, 1.0, 0.0, 0.5) >>> color_name_to_rgba("hsl(60, 100%, 50%)") (1.0, 1.0, 0.0, 1.0) >>> color_name_to_rgba("hsv(60, 100%, 100%)") (1.0, 1.0, 0.0, 1.0) @param color: the color to be converted in one of the following formats: - B{CSS3 color specification}: C{#rrggbb}, C{#rgb}, C{#rrggbbaa}, C{#rgba}, C{rgb(red, green, blue)}, C{rgba(red, green, blue, alpha)}, C{hsl(hue, saturation, lightness)}, C{hsla(hue, saturation, lightness, alpha)}, C{hsv(hue, saturation, value)} and C{hsva(hue, saturation, value, alpha)} where the components are given as hexadecimal numbers in the first four cases and as decimals or percentages (0%-100%) in the remaining cases. Red, green and blue components are between 0 and 255; hue is between 0 and 360; saturation, lightness and value is between 0 and 100; alpha is between 0 and 1. - B{Valid HTML color names}, i.e. those that are present in the HTML 4.0 specification - B{Valid X11 color names}, see U{http://en.wikipedia.org/wiki/X11_color_names} - B{Red-green-blue components} given separately in either a comma-, slash- or whitespace-separated string or a list or a tuple, in the range of 0-255. An alpha value of 255 (maximal opacity) will be assumed. - B{Red-green-blue-alpha components} given separately in either a comma-, slash- or whitespace-separated string or a list or a tuple, in the range of 0-255 - B{A single palette index} given either as a string or a number. Uses the palette given in the C{palette} parameter of the method call. @param palette: the palette to be used if a single number is passed to the method. Must be an instance of L{colors.Palette}. @return: the RGBA values corresponding to the given color in a 4-tuple. Since these colors are primarily used by Cairo routines, the tuples contain floats in the range 0.0-1.0 """ if not isinstance(color, basestring): if hasattr(color, "__iter__"): components = list(color) else: # A single index is given as a number try: components = palette.get(color) except AttributeError: raise ValueError("palette index used when no palette was given") if len(components) < 4: components += [1.] * (4 - len(components)) else: if color[0] == '#': color = color[1:] if len(color) == 3: components = [int(i, 16) * 17. / 255. for i in color] components.append(1.0) elif len(color) == 4: components = [int(i, 16) * 17. / 255. for i in color] elif len(color) == 6: components = [int(color[i:i+2], 16) / 255. for i in (0, 2, 4)] components.append(1.0) elif len(color) == 8: components = [int(color[i:i+2], 16) / 255. for i in (0, 2, 4, 6)] elif color.lower() in known_colors: components = known_colors[color.lower()] else: color_mode = "rgba" maximums = (255.0, 255.0, 255.0, 1.0) for mode in ["rgb(", "rgba(", "hsv(", "hsva(", "hsl(", "hsla("]: if color.startswith(mode) and color[-1] == ")": color = color[len(mode):-1] color_mode = mode[:-1] if mode[0] == "h": maximums = (360.0, 100.0, 100.0, 1.0) break if " " in color or "/" in color or "," in color: color = color.replace(",", " ").replace("/", " ") components = color.split() for idx, comp in enumerate(components): if comp[-1] == "%": components[idx] = float(comp[:-1])/100. else: components[idx] = float(comp)/maximums[idx] if len(components) < 4: components += [1.] * (4 - len(components)) if color_mode[:3] == "hsv": components = hsva_to_rgba(*components) elif color_mode[:3] == "hsl": components = hsla_to_rgba(*components) else: components = palette.get(int(color)) # At this point, the components are floats return tuple(clamp(val, 0., 1.) for val in components) def color_to_html_format(color): """Formats a color given as a 3-tuple or 4-tuple in HTML format. The HTML format is simply given by C{#rrggbbaa}, where C{rr} gives the red component in hexadecimal format, C{gg} gives the green component C{bb} gives the blue component and C{gg} gives the alpha level. The alpha level is optional. """ color = [int(clamp(component * 256, 0, 255)) for component in color] if len(color) == 4: return "#{0:02X}{1:02X}{2:02X}{3:02X}".format(*color) return "#{0:02X}{1:02X}{2:02X}".format(*color) def darken(color, ratio=0.5): """Creates a darker version of a color given by an RGB triplet. This is done by mixing the original color with black using the given ratio. A ratio of 1.0 will yield a completely black color, a ratio of 0.0 will yield the original color. The alpha values are left intact. """ ratio = 1.0 - ratio red, green, blue, alpha = color return (red * ratio, green * ratio, blue * ratio, alpha) def hsla_to_rgba(h, s, l, alpha = 1.0): """Converts a color given by its HSLA coordinates (hue, saturation, lightness, alpha) to RGBA coordinates. Each of the HSLA coordinates must be in the range [0, 1]. """ # This is based on the formulae found at: # http://en.wikipedia.org/wiki/HSL_and_HSV c = s*(1 - 2*abs(l - 0.5)) h1 = (h*6) % 6 x = c*(1 - abs(h1 % 2 - 1)) m = l - c/2. h1 = int(h1) if h1 < 3: if h1 < 1: return (c+m, x+m, m, alpha) elif h1 < 2: return (x+m, c+m, m, alpha) else: return (m, c+m, x+m, alpha) else: if h1 < 4: return (m, x+m, c+m, alpha) elif h1 < 5: return (x+m, m, c+m, alpha) else: return (c+m, m, x+m, alpha) def hsl_to_rgb(h, s, l): """Converts a color given by its HSL coordinates (hue, saturation, lightness) to RGB coordinates. Each of the HSL coordinates must be in the range [0, 1]. """ return hsla_to_rgba(h, s, l)[:3] def hsva_to_rgba(h, s, v, alpha = 1.0): """Converts a color given by its HSVA coordinates (hue, saturation, value, alpha) to RGB coordinates. Each of the HSVA coordinates must be in the range [0, 1]. """ # This is based on the formulae found at: # http://en.wikipedia.org/wiki/HSL_and_HSV c = v*s h1 = (h*6) % 6 x = c*(1 - abs(h1 % 2 - 1)) m = v-c h1 = int(h1) if h1 < 3: if h1 < 1: return (c+m, x+m, m, alpha) elif h1 < 2: return (x+m, c+m, m, alpha) else: return (m, c+m, x+m, alpha) else: if h1 < 4: return (m, x+m, c+m, alpha) elif h1 < 5: return (x+m, m, c+m, alpha) else: return (c+m, m, x+m, alpha) def hsv_to_rgb(h, s, v): """Converts a color given by its HSV coordinates (hue, saturation, value) to RGB coordinates. Each of the HSV coordinates must be in the range [0, 1]. """ return hsva_to_rgba(h, s, v)[:3] def rgba_to_hsla(r, g, b, alpha=1.0): """Converts a color given by its RGBA coordinates to HSLA coordinates (hue, saturation, lightness, alpha). Each of the RGBA coordinates must be in the range [0, 1]. """ alpha = float(alpha) rgb_min, rgb_max = float(min(r, g, b)), float(max(r, g, b)) if rgb_min == rgb_max: return 0.0, 0.0, rgb_min, alpha lightness = (rgb_min + rgb_max) / 2.0 d = rgb_max - rgb_min if lightness > 0.5: sat = d / (2 - rgb_max - rgb_min) else: sat = d / (rgb_max + rgb_min) d *= 6.0 if rgb_max == r: hue = (g - b) / d if g < b: hue += 1 elif rgb_max == g: hue = 1/3.0 + (b - r) / d else: hue = 2/3.0 + (r - g) / d return hue, sat, lightness, alpha def rgba_to_hsva(r, g, b, alpha=1.0): """Converts a color given by its RGBA coordinates to HSVA coordinates (hue, saturation, value, alpha). Each of the RGBA coordinates must be in the range [0, 1]. """ # This is based on the formulae found at: # http://en.literateprograms.org/RGB_to_HSV_color_space_conversion_(C) rgb_min, rgb_max = float(min(r, g, b)), float(max(r, g, b)) alpha = float(alpha) value = float(rgb_max) if value <= 0: return 0.0, 0.0, 0.0, alpha sat = 1.0 - rgb_min / value if sat <= 0: return 0.0, 0.0, value, alpha d = rgb_max - rgb_min r = (r - rgb_min) / d g = (g - rgb_min) / d b = (b - rgb_min) / d rgb_max = max(r, g, b) if rgb_max == r: hue = 0.0 + (g - b) / 6.0 if hue < 0: hue += 1 elif rgb_max == g: hue = 1/3.0 + (b - r) / 6.0 else: hue = 2/3.0 + (r - g) / 6.0 return hue, sat, value, alpha def rgb_to_hsl(r, g, b): """Converts a color given by its RGB coordinates to HSL coordinates (hue, saturation, lightness). Each of the RGB coordinates must be in the range [0, 1]. """ return rgba_to_hsla(r, g, b)[:3] def rgb_to_hsv(r, g, b): """Converts a color given by its RGB coordinates to HSV coordinates (hue, saturation, value). Each of the RGB coordinates must be in the range [0, 1]. """ return rgba_to_hsva(r, g, b)[:3] def lighten(color, ratio=0.5): """Creates a lighter version of a color given by an RGB triplet. This is done by mixing the original color with white using the given ratio. A ratio of 1.0 will yield a completely white color, a ratio of 0.0 will yield the original color. """ red, green, blue, alpha = color return (red + (1.0 - red) * ratio, green + (1.0 - green) * ratio, blue + (1.0 - blue) * ratio, alpha) known_colors = \ { 'alice blue': (0.94117647058823528, 0.97254901960784312, 1.0, 1.0), 'aliceblue': (0.94117647058823528, 0.97254901960784312, 1.0, 1.0), 'antique white': ( 0.98039215686274506, 0.92156862745098034, 0.84313725490196079, 1.0), 'antiquewhite': ( 0.98039215686274506, 0.92156862745098034, 0.84313725490196079, 1.0), 'antiquewhite1': (1.0, 0.93725490196078431, 0.85882352941176465, 1.0), 'antiquewhite2': ( 0.93333333333333335, 0.87450980392156863, 0.80000000000000004, 1.0), 'antiquewhite3': ( 0.80392156862745101, 0.75294117647058822, 0.69019607843137254, 1.0), 'antiquewhite4': ( 0.54509803921568623, 0.51372549019607838, 0.47058823529411764, 1.0), 'aqua': (0.0, 1.0, 1.0, 1.0), 'aquamarine': (0.49803921568627452, 1.0, 0.83137254901960789, 1.0), 'aquamarine1': (0.49803921568627452, 1.0, 0.83137254901960789, 1.0), 'aquamarine2': ( 0.46274509803921571, 0.93333333333333335, 0.77647058823529413, 1.0), 'aquamarine3': ( 0.40000000000000002, 0.80392156862745101, 0.66666666666666663, 1.0), 'aquamarine4': ( 0.27058823529411763, 0.54509803921568623, 0.45490196078431372, 1.0), 'azure': (0.94117647058823528, 1.0, 1.0, 1.0), 'azure1': (0.94117647058823528, 1.0, 1.0, 1.0), 'azure2': ( 0.8784313725490196, 0.93333333333333335, 0.93333333333333335, 1.0), 'azure3': ( 0.75686274509803919, 0.80392156862745101, 0.80392156862745101, 1.0), 'azure4': ( 0.51372549019607838, 0.54509803921568623, 0.54509803921568623, 1.0), 'beige': ( 0.96078431372549022, 0.96078431372549022, 0.86274509803921573, 1.0), 'bisque': (1.0, 0.89411764705882357, 0.7686274509803922, 1.0), 'bisque1': (1.0, 0.89411764705882357, 0.7686274509803922, 1.0), 'bisque2': ( 0.93333333333333335, 0.83529411764705885, 0.71764705882352942, 1.0), 'bisque3': ( 0.80392156862745101, 0.71764705882352942, 0.61960784313725492, 1.0), 'bisque4': ( 0.54509803921568623, 0.49019607843137253, 0.41960784313725491, 1.0), 'black': (0.0, 0.0, 0.0, 1.0), 'blanched almond': (1.0, 0.92156862745098034, 0.80392156862745101, 1.0), 'blanchedalmond': (1.0, 0.92156862745098034, 0.80392156862745101, 1.0), 'blue': (0.0, 0.0, 1.0, 1.0), 'blue violet': ( 0.54117647058823526, 0.16862745098039217, 0.88627450980392153, 1.0), 'blue1': (0.0, 0.0, 1.0, 1.0), 'blue2': (0.0, 0.0, 0.93333333333333335, 1.0), 'blue3': (0.0, 0.0, 0.80392156862745101, 1.0), 'blue4': (0.0, 0.0, 0.54509803921568623, 1.0), 'blueviolet': ( 0.54117647058823526, 0.16862745098039217, 0.88627450980392153, 1.0), 'brown': ( 0.6470588235294118, 0.16470588235294117, 0.16470588235294117, 1.0), 'brown1': (1.0, 0.25098039215686274, 0.25098039215686274, 1.0), 'brown2': ( 0.93333333333333335, 0.23137254901960785, 0.23137254901960785, 1.0), 'brown3': ( 0.80392156862745101, 0.20000000000000001, 0.20000000000000001, 1.0), 'brown4': ( 0.54509803921568623, 0.13725490196078433, 0.13725490196078433, 1.0), 'burlywood': ( 0.87058823529411766, 0.72156862745098038, 0.52941176470588236, 1.0), 'burlywood1': (1.0, 0.82745098039215681, 0.60784313725490191, 1.0), 'burlywood2': ( 0.93333333333333335, 0.77254901960784317, 0.56862745098039214, 1.0), 'burlywood3': ( 0.80392156862745101, 0.66666666666666663, 0.49019607843137253, 1.0), 'burlywood4': ( 0.54509803921568623, 0.45098039215686275, 0.33333333333333331, 1.0), 'cadet blue': ( 0.37254901960784315, 0.61960784313725492, 0.62745098039215685, 1.0), 'cadetblue': ( 0.37254901960784315, 0.61960784313725492, 0.62745098039215685, 1.0), 'cadetblue1': (0.59607843137254901, 0.96078431372549022, 1.0, 1.0), 'cadetblue2': ( 0.55686274509803924, 0.89803921568627454, 0.93333333333333335, 1.0), 'cadetblue3': ( 0.47843137254901963, 0.77254901960784317, 0.80392156862745101, 1.0), 'cadetblue4': ( 0.32549019607843138, 0.52549019607843139, 0.54509803921568623, 1.0), 'chartreuse': (0.49803921568627452, 1.0, 0.0, 1.0), 'chartreuse1': (0.49803921568627452, 1.0, 0.0, 1.0), 'chartreuse2': (0.46274509803921571, 0.93333333333333335, 0.0, 1.0), 'chartreuse3': (0.40000000000000002, 0.80392156862745101, 0.0, 1.0), 'chartreuse4': (0.27058823529411763, 0.54509803921568623, 0.0, 1.0), 'chocolate': ( 0.82352941176470584, 0.41176470588235292, 0.11764705882352941, 1.0), 'chocolate1': (1.0, 0.49803921568627452, 0.14117647058823529, 1.0), 'chocolate2': ( 0.93333333333333335, 0.46274509803921571, 0.12941176470588237, 1.0), 'chocolate3': ( 0.80392156862745101, 0.40000000000000002, 0.11372549019607843, 1.0), 'chocolate4': ( 0.54509803921568623, 0.27058823529411763, 0.074509803921568626, 1.0), 'coral': (1.0, 0.49803921568627452, 0.31372549019607843, 1.0), 'coral1': (1.0, 0.44705882352941179, 0.33725490196078434, 1.0), 'coral2': ( 0.93333333333333335, 0.41568627450980394, 0.31372549019607843, 1.0), 'coral3': ( 0.80392156862745101, 0.35686274509803922, 0.27058823529411763, 1.0), 'coral4': ( 0.54509803921568623, 0.24313725490196078, 0.18431372549019609, 1.0), 'cornflower blue': ( 0.39215686274509803, 0.58431372549019611, 0.92941176470588238, 1.0), 'cornflowerblue': ( 0.39215686274509803, 0.58431372549019611, 0.92941176470588238, 1.0), 'cornsilk': (1.0, 0.97254901960784312, 0.86274509803921573, 1.0), 'cornsilk1': (1.0, 0.97254901960784312, 0.86274509803921573, 1.0), 'cornsilk2': ( 0.93333333333333335, 0.90980392156862744, 0.80392156862745101, 1.0), 'cornsilk3': ( 0.80392156862745101, 0.78431372549019607, 0.69411764705882351, 1.0), 'cornsilk4': ( 0.54509803921568623, 0.53333333333333333, 0.47058823529411764, 1.0), 'cyan': (0.0, 1.0, 1.0, 1.0), 'cyan1': (0.0, 1.0, 1.0, 1.0), 'cyan2': (0.0, 0.93333333333333335, 0.93333333333333335, 1.0), 'cyan3': (0.0, 0.80392156862745101, 0.80392156862745101, 1.0), 'cyan4': (0.0, 0.54509803921568623, 0.54509803921568623, 1.0), 'dark blue': (0.0, 0.0, 0.54509803921568623, 1.0), 'dark cyan': (0.0, 0.54509803921568623, 0.54509803921568623, 1.0), 'dark goldenrod': ( 0.72156862745098038, 0.52549019607843139, 0.043137254901960784, 1.0), 'dark gray': ( 0.66274509803921566, 0.66274509803921566, 0.66274509803921566, 1.0), 'dark green': (0.0, 0.39215686274509803, 0.0, 1.0), 'dark grey': ( 0.66274509803921566, 0.66274509803921566, 0.66274509803921566, 1.0), 'dark khaki': ( 0.74117647058823533, 0.71764705882352942, 0.41960784313725491, 1.0), 'dark magenta': (0.54509803921568623, 0.0, 0.54509803921568623, 1.0), 'dark olive green': ( 0.33333333333333331, 0.41960784313725491, 0.18431372549019609, 1.0), 'dark orange': (1.0, 0.5490196078431373, 0.0, 1.0), 'dark orchid': ( 0.59999999999999998, 0.19607843137254902, 0.80000000000000004, 1.0), 'dark red': (0.54509803921568623, 0.0, 0.0, 1.0), 'dark salmon': ( 0.9137254901960784, 0.58823529411764708, 0.47843137254901963, 1.0), 'dark sea green': ( 0.5607843137254902, 0.73725490196078436, 0.5607843137254902, 1.0), 'dark slate blue': ( 0.28235294117647058, 0.23921568627450981, 0.54509803921568623, 1.0), 'dark slate gray': ( 0.18431372549019609, 0.30980392156862746, 0.30980392156862746, 1.0), 'dark slate grey': ( 0.18431372549019609, 0.30980392156862746, 0.30980392156862746, 1.0), 'dark turquoise': (0.0, 0.80784313725490198, 0.81960784313725488, 1.0), 'dark violet': (0.58039215686274515, 0.0, 0.82745098039215681, 1.0), 'darkblue': (0.0, 0.0, 0.54509803921568623, 1.0), 'darkcyan': (0.0, 0.54509803921568623, 0.54509803921568623, 1.0), 'darkgoldenrod': ( 0.72156862745098038, 0.52549019607843139, 0.043137254901960784, 1.0), 'darkgoldenrod1': (1.0, 0.72549019607843135, 0.058823529411764705, 1.0), 'darkgoldenrod2': ( 0.93333333333333335, 0.67843137254901964, 0.054901960784313725, 1.0), 'darkgoldenrod3': ( 0.80392156862745101, 0.58431372549019611, 0.047058823529411764, 1.0), 'darkgoldenrod4': ( 0.54509803921568623, 0.396078431372549, 0.031372549019607843, 1.0), 'darkgray': ( 0.66274509803921566, 0.66274509803921566, 0.66274509803921566, 1.0), 'darkgreen': (0.0, 0.39215686274509803, 0.0, 1.0), 'darkgrey': ( 0.66274509803921566, 0.66274509803921566, 0.66274509803921566, 1.0), 'darkkhaki': ( 0.74117647058823533, 0.71764705882352942, 0.41960784313725491, 1.0), 'darkmagenta': (0.54509803921568623, 0.0, 0.54509803921568623, 1.0), 'darkolivegreen': ( 0.33333333333333331, 0.41960784313725491, 0.18431372549019609, 1.0), 'darkolivegreen1': (0.792156862745098, 1.0, 0.4392156862745098, 1.0), 'darkolivegreen2': ( 0.73725490196078436, 0.93333333333333335, 0.40784313725490196, 1.0), 'darkolivegreen3': ( 0.63529411764705879, 0.80392156862745101, 0.35294117647058826, 1.0), 'darkolivegreen4': ( 0.43137254901960786, 0.54509803921568623, 0.23921568627450981, 1.0), 'darkorange': (1.0, 0.5490196078431373, 0.0, 1.0), 'darkorange1': (1.0, 0.49803921568627452, 0.0, 1.0), 'darkorange2': (0.93333333333333335, 0.46274509803921571, 0.0, 1.0), 'darkorange3': (0.80392156862745101, 0.40000000000000002, 0.0, 1.0), 'darkorange4': (0.54509803921568623, 0.27058823529411763, 0.0, 1.0), 'darkorchid': ( 0.59999999999999998, 0.19607843137254902, 0.80000000000000004, 1.0), 'darkorchid1': (0.74901960784313726, 0.24313725490196078, 1.0, 1.0), 'darkorchid2': ( 0.69803921568627447, 0.22745098039215686, 0.93333333333333335, 1.0), 'darkorchid3': ( 0.60392156862745094, 0.19607843137254902, 0.80392156862745101, 1.0), 'darkorchid4': ( 0.40784313725490196, 0.13333333333333333, 0.54509803921568623, 1.0), 'darkred': (0.54509803921568623, 0.0, 0.0, 1.0), 'darksalmon': ( 0.9137254901960784, 0.58823529411764708, 0.47843137254901963, 1.0), 'darkseagreen': ( 0.5607843137254902, 0.73725490196078436, 0.5607843137254902, 1.0), 'darkseagreen1': (0.75686274509803919, 1.0, 0.75686274509803919, 1.0), 'darkseagreen2': ( 0.70588235294117652, 0.93333333333333335, 0.70588235294117652, 1.0), 'darkseagreen3': ( 0.60784313725490191, 0.80392156862745101, 0.60784313725490191, 1.0), 'darkseagreen4': ( 0.41176470588235292, 0.54509803921568623, 0.41176470588235292, 1.0), 'darkslateblue': ( 0.28235294117647058, 0.23921568627450981, 0.54509803921568623, 1.0), 'darkslategray': ( 0.18431372549019609, 0.30980392156862746, 0.30980392156862746, 1.0), 'darkslategray1': (0.59215686274509804, 1.0, 1.0, 1.0), 'darkslategray2': ( 0.55294117647058827, 0.93333333333333335, 0.93333333333333335, 1.0), 'darkslategray3': ( 0.47450980392156861, 0.80392156862745101, 0.80392156862745101, 1.0), 'darkslategray4': ( 0.32156862745098042, 0.54509803921568623, 0.54509803921568623, 1.0), 'darkslategrey': ( 0.18431372549019609, 0.30980392156862746, 0.30980392156862746, 1.0), 'darkturquoise': (0.0, 0.80784313725490198, 0.81960784313725488, 1.0), 'darkviolet': (0.58039215686274515, 0.0, 0.82745098039215681, 1.0), 'deep pink': (1.0, 0.078431372549019607, 0.57647058823529407, 1.0), 'deep sky blue': (0.0, 0.74901960784313726, 1.0, 1.0), 'deeppink': (1.0, 0.078431372549019607, 0.57647058823529407, 1.0), 'deeppink1': (1.0, 0.078431372549019607, 0.57647058823529407, 1.0), 'deeppink2': ( 0.93333333333333335, 0.070588235294117646, 0.53725490196078429, 1.0), 'deeppink3': ( 0.80392156862745101, 0.062745098039215685, 0.46274509803921571, 1.0), 'deeppink4': ( 0.54509803921568623, 0.039215686274509803, 0.31372549019607843, 1.0), 'deepskyblue': (0.0, 0.74901960784313726, 1.0, 1.0), 'deepskyblue1': (0.0, 0.74901960784313726, 1.0, 1.0), 'deepskyblue2': (0.0, 0.69803921568627447, 0.93333333333333335, 1.0), 'deepskyblue3': (0.0, 0.60392156862745094, 0.80392156862745101, 1.0), 'deepskyblue4': (0.0, 0.40784313725490196, 0.54509803921568623, 1.0), 'dim gray': ( 0.41176470588235292, 0.41176470588235292, 0.41176470588235292, 1.0), 'dim grey': ( 0.41176470588235292, 0.41176470588235292, 0.41176470588235292, 1.0), 'dimgray': ( 0.41176470588235292, 0.41176470588235292, 0.41176470588235292, 1.0), 'dimgrey': ( 0.41176470588235292, 0.41176470588235292, 0.41176470588235292, 1.0), 'dodger blue': (0.11764705882352941, 0.56470588235294117, 1.0, 1.0), 'dodgerblue': (0.11764705882352941, 0.56470588235294117, 1.0, 1.0), 'dodgerblue1': (0.11764705882352941, 0.56470588235294117, 1.0, 1.0), 'dodgerblue2': ( 0.10980392156862745, 0.52549019607843139, 0.93333333333333335, 1.0), 'dodgerblue3': ( 0.094117647058823528, 0.45490196078431372, 0.80392156862745101, 1.0), 'dodgerblue4': ( 0.062745098039215685, 0.30588235294117649, 0.54509803921568623, 1.0), 'firebrick': ( 0.69803921568627447, 0.13333333333333333, 0.13333333333333333, 1.0), 'firebrick1': (1.0, 0.18823529411764706, 0.18823529411764706, 1.0), 'firebrick2': ( 0.93333333333333335, 0.17254901960784313, 0.17254901960784313, 1.0), 'firebrick3': ( 0.80392156862745101, 0.14901960784313725, 0.14901960784313725, 1.0), 'firebrick4': ( 0.54509803921568623, 0.10196078431372549, 0.10196078431372549, 1.0), 'floral white': (1.0, 0.98039215686274506, 0.94117647058823528, 1.0), 'floralwhite': (1.0, 0.98039215686274506, 0.94117647058823528, 1.0), 'forest green': ( 0.13333333333333333, 0.54509803921568623, 0.13333333333333333, 1.0), 'forestgreen': ( 0.13333333333333333, 0.54509803921568623, 0.13333333333333333, 1.0), 'fuchsia': (1.0, 0.0, 1.0, 1.0), 'gainsboro': ( 0.86274509803921573, 0.86274509803921573, 0.86274509803921573, 1.0), 'ghost white': (0.97254901960784312, 0.97254901960784312, 1.0, 1.0), 'ghostwhite': (0.97254901960784312, 0.97254901960784312, 1.0, 1.0), 'gold': (1.0, 0.84313725490196079, 0.0, 1.0), 'gold1': (1.0, 0.84313725490196079, 0.0, 1.0), 'gold2': (0.93333333333333335, 0.78823529411764703, 0.0, 1.0), 'gold3': (0.80392156862745101, 0.67843137254901964, 0.0, 1.0), 'gold4': (0.54509803921568623, 0.45882352941176469, 0.0, 1.0), 'goldenrod': ( 0.85490196078431369, 0.6470588235294118, 0.12549019607843137, 1.0), 'goldenrod1': (1.0, 0.75686274509803919, 0.14509803921568629, 1.0), 'goldenrod2': ( 0.93333333333333335, 0.70588235294117652, 0.13333333333333333, 1.0), 'goldenrod3': ( 0.80392156862745101, 0.60784313725490191, 0.11372549019607843, 1.0), 'goldenrod4': ( 0.54509803921568623, 0.41176470588235292, 0.078431372549019607, 1.0), 'gray': ( 0.74509803921568629, 0.74509803921568629, 0.74509803921568629, 1.0), 'gray0': (0.0, 0.0, 0.0, 1.0), 'gray1': ( 0.011764705882352941, 0.011764705882352941, 0.011764705882352941, 1.0), 'gray10': ( 0.10196078431372549, 0.10196078431372549, 0.10196078431372549, 1.0), 'gray100': (1.0, 1.0, 1.0, 1.0), 'gray11': ( 0.10980392156862745, 0.10980392156862745, 0.10980392156862745, 1.0), 'gray12': ( 0.12156862745098039, 0.12156862745098039, 0.12156862745098039, 1.0), 'gray13': ( 0.12941176470588237, 0.12941176470588237, 0.12941176470588237, 1.0), 'gray14': ( 0.14117647058823529, 0.14117647058823529, 0.14117647058823529, 1.0), 'gray15': ( 0.14901960784313725, 0.14901960784313725, 0.14901960784313725, 1.0), 'gray16': ( 0.16078431372549021, 0.16078431372549021, 0.16078431372549021, 1.0), 'gray17': ( 0.16862745098039217, 0.16862745098039217, 0.16862745098039217, 1.0), 'gray18': ( 0.1803921568627451, 0.1803921568627451, 0.1803921568627451, 1.0), 'gray19': ( 0.18823529411764706, 0.18823529411764706, 0.18823529411764706, 1.0), 'gray2': ( 0.019607843137254902, 0.019607843137254902, 0.019607843137254902, 1.0), 'gray20': ( 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 1.0), 'gray21': ( 0.21176470588235294, 0.21176470588235294, 0.21176470588235294, 1.0), 'gray22': ( 0.2196078431372549, 0.2196078431372549, 0.2196078431372549, 1.0), 'gray23': ( 0.23137254901960785, 0.23137254901960785, 0.23137254901960785, 1.0), 'gray24': ( 0.23921568627450981, 0.23921568627450981, 0.23921568627450981, 1.0), 'gray25': ( 0.25098039215686274, 0.25098039215686274, 0.25098039215686274, 1.0), 'gray26': ( 0.25882352941176473, 0.25882352941176473, 0.25882352941176473, 1.0), 'gray27': ( 0.27058823529411763, 0.27058823529411763, 0.27058823529411763, 1.0), 'gray28': ( 0.27843137254901962, 0.27843137254901962, 0.27843137254901962, 1.0), 'gray29': ( 0.29019607843137257, 0.29019607843137257, 0.29019607843137257, 1.0), 'gray3': ( 0.031372549019607843, 0.031372549019607843, 0.031372549019607843, 1.0), 'gray30': ( 0.30196078431372547, 0.30196078431372547, 0.30196078431372547, 1.0), 'gray31': ( 0.30980392156862746, 0.30980392156862746, 0.30980392156862746, 1.0), 'gray32': ( 0.32156862745098042, 0.32156862745098042, 0.32156862745098042, 1.0), 'gray33': ( 0.32941176470588235, 0.32941176470588235, 0.32941176470588235, 1.0), 'gray34': ( 0.3411764705882353, 0.3411764705882353, 0.3411764705882353, 1.0), 'gray35': ( 0.34901960784313724, 0.34901960784313724, 0.34901960784313724, 1.0), 'gray36': ( 0.36078431372549019, 0.36078431372549019, 0.36078431372549019, 1.0), 'gray37': ( 0.36862745098039218, 0.36862745098039218, 0.36862745098039218, 1.0), 'gray38': ( 0.38039215686274508, 0.38039215686274508, 0.38039215686274508, 1.0), 'gray39': ( 0.38823529411764707, 0.38823529411764707, 0.38823529411764707, 1.0), 'gray4': ( 0.039215686274509803, 0.039215686274509803, 0.039215686274509803, 1.0), 'gray40': ( 0.40000000000000002, 0.40000000000000002, 0.40000000000000002, 1.0), 'gray41': ( 0.41176470588235292, 0.41176470588235292, 0.41176470588235292, 1.0), 'gray42': ( 0.41960784313725491, 0.41960784313725491, 0.41960784313725491, 1.0), 'gray43': ( 0.43137254901960786, 0.43137254901960786, 0.43137254901960786, 1.0), 'gray44': ( 0.4392156862745098, 0.4392156862745098, 0.4392156862745098, 1.0), 'gray45': ( 0.45098039215686275, 0.45098039215686275, 0.45098039215686275, 1.0), 'gray46': ( 0.45882352941176469, 0.45882352941176469, 0.45882352941176469, 1.0), 'gray47': ( 0.47058823529411764, 0.47058823529411764, 0.47058823529411764, 1.0), 'gray48': ( 0.47843137254901963, 0.47843137254901963, 0.47843137254901963, 1.0), 'gray49': ( 0.49019607843137253, 0.49019607843137253, 0.49019607843137253, 1.0), 'gray5': ( 0.050980392156862744, 0.050980392156862744, 0.050980392156862744, 1.0), 'gray50': ( 0.49803921568627452, 0.49803921568627452, 0.49803921568627452, 1.0), 'gray51': ( 0.50980392156862742, 0.50980392156862742, 0.50980392156862742, 1.0), 'gray52': ( 0.52156862745098043, 0.52156862745098043, 0.52156862745098043, 1.0), 'gray53': ( 0.52941176470588236, 0.52941176470588236, 0.52941176470588236, 1.0), 'gray54': ( 0.54117647058823526, 0.54117647058823526, 0.54117647058823526, 1.0), 'gray55': ( 0.5490196078431373, 0.5490196078431373, 0.5490196078431373, 1.0), 'gray56': ( 0.5607843137254902, 0.5607843137254902, 0.5607843137254902, 1.0), 'gray57': ( 0.56862745098039214, 0.56862745098039214, 0.56862745098039214, 1.0), 'gray58': ( 0.58039215686274515, 0.58039215686274515, 0.58039215686274515, 1.0), 'gray59': ( 0.58823529411764708, 0.58823529411764708, 0.58823529411764708, 1.0), 'gray6': ( 0.058823529411764705, 0.058823529411764705, 0.058823529411764705, 1.0), 'gray60': ( 0.59999999999999998, 0.59999999999999998, 0.59999999999999998, 1.0), 'gray61': ( 0.61176470588235299, 0.61176470588235299, 0.61176470588235299, 1.0), 'gray62': ( 0.61960784313725492, 0.61960784313725492, 0.61960784313725492, 1.0), 'gray63': ( 0.63137254901960782, 0.63137254901960782, 0.63137254901960782, 1.0), 'gray64': ( 0.63921568627450975, 0.63921568627450975, 0.63921568627450975, 1.0), 'gray65': ( 0.65098039215686276, 0.65098039215686276, 0.65098039215686276, 1.0), 'gray66': ( 0.6588235294117647, 0.6588235294117647, 0.6588235294117647, 1.0), 'gray67': ( 0.6705882352941176, 0.6705882352941176, 0.6705882352941176, 1.0), 'gray68': ( 0.67843137254901964, 0.67843137254901964, 0.67843137254901964, 1.0), 'gray69': ( 0.69019607843137254, 0.69019607843137254, 0.69019607843137254, 1.0), 'gray7': ( 0.070588235294117646, 0.070588235294117646, 0.070588235294117646, 1.0), 'gray70': ( 0.70196078431372544, 0.70196078431372544, 0.70196078431372544, 1.0), 'gray71': ( 0.70980392156862748, 0.70980392156862748, 0.70980392156862748, 1.0), 'gray72': ( 0.72156862745098038, 0.72156862745098038, 0.72156862745098038, 1.0), 'gray73': ( 0.72941176470588232, 0.72941176470588232, 0.72941176470588232, 1.0), 'gray74': ( 0.74117647058823533, 0.74117647058823533, 0.74117647058823533, 1.0), 'gray75': ( 0.74901960784313726, 0.74901960784313726, 0.74901960784313726, 1.0), 'gray76': ( 0.76078431372549016, 0.76078431372549016, 0.76078431372549016, 1.0), 'gray77': ( 0.7686274509803922, 0.7686274509803922, 0.7686274509803922, 1.0), 'gray78': ( 0.7803921568627451, 0.7803921568627451, 0.7803921568627451, 1.0), 'gray79': ( 0.78823529411764703, 0.78823529411764703, 0.78823529411764703, 1.0), 'gray8': ( 0.078431372549019607, 0.078431372549019607, 0.078431372549019607, 1.0), 'gray80': ( 0.80000000000000004, 0.80000000000000004, 0.80000000000000004, 1.0), 'gray81': ( 0.81176470588235294, 0.81176470588235294, 0.81176470588235294, 1.0), 'gray82': ( 0.81960784313725488, 0.81960784313725488, 0.81960784313725488, 1.0), 'gray83': ( 0.83137254901960789, 0.83137254901960789, 0.83137254901960789, 1.0), 'gray84': ( 0.83921568627450982, 0.83921568627450982, 0.83921568627450982, 1.0), 'gray85': ( 0.85098039215686272, 0.85098039215686272, 0.85098039215686272, 1.0), 'gray86': ( 0.85882352941176465, 0.85882352941176465, 0.85882352941176465, 1.0), 'gray87': ( 0.87058823529411766, 0.87058823529411766, 0.87058823529411766, 1.0), 'gray88': ( 0.8784313725490196, 0.8784313725490196, 0.8784313725490196, 1.0), 'gray89': ( 0.8901960784313725, 0.8901960784313725, 0.8901960784313725, 1.0), 'gray9': ( 0.090196078431372548, 0.090196078431372548, 0.090196078431372548, 1.0), 'gray90': ( 0.89803921568627454, 0.89803921568627454, 0.89803921568627454, 1.0), 'gray91': ( 0.90980392156862744, 0.90980392156862744, 0.90980392156862744, 1.0), 'gray92': ( 0.92156862745098034, 0.92156862745098034, 0.92156862745098034, 1.0), 'gray93': ( 0.92941176470588238, 0.92941176470588238, 0.92941176470588238, 1.0), 'gray94': ( 0.94117647058823528, 0.94117647058823528, 0.94117647058823528, 1.0), 'gray95': ( 0.94901960784313721, 0.94901960784313721, 0.94901960784313721, 1.0), 'gray96': ( 0.96078431372549022, 0.96078431372549022, 0.96078431372549022, 1.0), 'gray97': ( 0.96862745098039216, 0.96862745098039216, 0.96862745098039216, 1.0), 'gray98': ( 0.98039215686274506, 0.98039215686274506, 0.98039215686274506, 1.0), 'gray99': ( 0.9882352941176471, 0.9882352941176471, 0.9882352941176471, 1.0), 'green': (0.0, 1.0, 0.0, 1.0), 'green yellow': (0.67843137254901964, 1.0, 0.18431372549019609, 1.0), 'green1': (0.0, 1.0, 0.0, 1.0), 'green2': (0.0, 0.93333333333333335, 0.0, 1.0), 'green3': (0.0, 0.80392156862745101, 0.0, 1.0), 'green4': (0.0, 0.54509803921568623, 0.0, 1.0), 'greenyellow': (0.67843137254901964, 1.0, 0.18431372549019609, 1.0), 'grey': ( 0.74509803921568629, 0.74509803921568629, 0.74509803921568629, 1.0), 'grey0': (0.0, 0.0, 0.0, 1.0), 'grey1': ( 0.011764705882352941, 0.011764705882352941, 0.011764705882352941, 1.0), 'grey10': ( 0.10196078431372549, 0.10196078431372549, 0.10196078431372549, 1.0), 'grey100': (1.0, 1.0, 1.0, 1.0), 'grey11': ( 0.10980392156862745, 0.10980392156862745, 0.10980392156862745, 1.0), 'grey12': ( 0.12156862745098039, 0.12156862745098039, 0.12156862745098039, 1.0), 'grey13': ( 0.12941176470588237, 0.12941176470588237, 0.12941176470588237, 1.0), 'grey14': ( 0.14117647058823529, 0.14117647058823529, 0.14117647058823529, 1.0), 'grey15': ( 0.14901960784313725, 0.14901960784313725, 0.14901960784313725, 1.0), 'grey16': ( 0.16078431372549021, 0.16078431372549021, 0.16078431372549021, 1.0), 'grey17': ( 0.16862745098039217, 0.16862745098039217, 0.16862745098039217, 1.0), 'grey18': ( 0.1803921568627451, 0.1803921568627451, 0.1803921568627451, 1.0), 'grey19': ( 0.18823529411764706, 0.18823529411764706, 0.18823529411764706, 1.0), 'grey2': ( 0.019607843137254902, 0.019607843137254902, 0.019607843137254902, 1.0), 'grey20': ( 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 1.0), 'grey21': ( 0.21176470588235294, 0.21176470588235294, 0.21176470588235294, 1.0), 'grey22': ( 0.2196078431372549, 0.2196078431372549, 0.2196078431372549, 1.0), 'grey23': ( 0.23137254901960785, 0.23137254901960785, 0.23137254901960785, 1.0), 'grey24': ( 0.23921568627450981, 0.23921568627450981, 0.23921568627450981, 1.0), 'grey25': ( 0.25098039215686274, 0.25098039215686274, 0.25098039215686274, 1.0), 'grey26': ( 0.25882352941176473, 0.25882352941176473, 0.25882352941176473, 1.0), 'grey27': ( 0.27058823529411763, 0.27058823529411763, 0.27058823529411763, 1.0), 'grey28': ( 0.27843137254901962, 0.27843137254901962, 0.27843137254901962, 1.0), 'grey29': ( 0.29019607843137257, 0.29019607843137257, 0.29019607843137257, 1.0), 'grey3': ( 0.031372549019607843, 0.031372549019607843, 0.031372549019607843, 1.0), 'grey30': ( 0.30196078431372547, 0.30196078431372547, 0.30196078431372547, 1.0), 'grey31': ( 0.30980392156862746, 0.30980392156862746, 0.30980392156862746, 1.0), 'grey32': ( 0.32156862745098042, 0.32156862745098042, 0.32156862745098042, 1.0), 'grey33': ( 0.32941176470588235, 0.32941176470588235, 0.32941176470588235, 1.0), 'grey34': ( 0.3411764705882353, 0.3411764705882353, 0.3411764705882353, 1.0), 'grey35': ( 0.34901960784313724, 0.34901960784313724, 0.34901960784313724, 1.0), 'grey36': ( 0.36078431372549019, 0.36078431372549019, 0.36078431372549019, 1.0), 'grey37': ( 0.36862745098039218, 0.36862745098039218, 0.36862745098039218, 1.0), 'grey38': ( 0.38039215686274508, 0.38039215686274508, 0.38039215686274508, 1.0), 'grey39': ( 0.38823529411764707, 0.38823529411764707, 0.38823529411764707, 1.0), 'grey4': ( 0.039215686274509803, 0.039215686274509803, 0.039215686274509803, 1.0), 'grey40': ( 0.40000000000000002, 0.40000000000000002, 0.40000000000000002, 1.0), 'grey41': ( 0.41176470588235292, 0.41176470588235292, 0.41176470588235292, 1.0), 'grey42': ( 0.41960784313725491, 0.41960784313725491, 0.41960784313725491, 1.0), 'grey43': ( 0.43137254901960786, 0.43137254901960786, 0.43137254901960786, 1.0), 'grey44': ( 0.4392156862745098, 0.4392156862745098, 0.4392156862745098, 1.0), 'grey45': ( 0.45098039215686275, 0.45098039215686275, 0.45098039215686275, 1.0), 'grey46': ( 0.45882352941176469, 0.45882352941176469, 0.45882352941176469, 1.0), 'grey47': ( 0.47058823529411764, 0.47058823529411764, 0.47058823529411764, 1.0), 'grey48': ( 0.47843137254901963, 0.47843137254901963, 0.47843137254901963, 1.0), 'grey49': ( 0.49019607843137253, 0.49019607843137253, 0.49019607843137253, 1.0), 'grey5': ( 0.050980392156862744, 0.050980392156862744, 0.050980392156862744, 1.0), 'grey50': ( 0.49803921568627452, 0.49803921568627452, 0.49803921568627452, 1.0), 'grey51': ( 0.50980392156862742, 0.50980392156862742, 0.50980392156862742, 1.0), 'grey52': ( 0.52156862745098043, 0.52156862745098043, 0.52156862745098043, 1.0), 'grey53': ( 0.52941176470588236, 0.52941176470588236, 0.52941176470588236, 1.0), 'grey54': ( 0.54117647058823526, 0.54117647058823526, 0.54117647058823526, 1.0), 'grey55': ( 0.5490196078431373, 0.5490196078431373, 0.5490196078431373, 1.0), 'grey56': ( 0.5607843137254902, 0.5607843137254902, 0.5607843137254902, 1.0), 'grey57': ( 0.56862745098039214, 0.56862745098039214, 0.56862745098039214, 1.0), 'grey58': ( 0.58039215686274515, 0.58039215686274515, 0.58039215686274515, 1.0), 'grey59': ( 0.58823529411764708, 0.58823529411764708, 0.58823529411764708, 1.0), 'grey6': ( 0.058823529411764705, 0.058823529411764705, 0.058823529411764705, 1.0), 'grey60': ( 0.59999999999999998, 0.59999999999999998, 0.59999999999999998, 1.0), 'grey61': ( 0.61176470588235299, 0.61176470588235299, 0.61176470588235299, 1.0), 'grey62': ( 0.61960784313725492, 0.61960784313725492, 0.61960784313725492, 1.0), 'grey63': ( 0.63137254901960782, 0.63137254901960782, 0.63137254901960782, 1.0), 'grey64': ( 0.63921568627450975, 0.63921568627450975, 0.63921568627450975, 1.0), 'grey65': ( 0.65098039215686276, 0.65098039215686276, 0.65098039215686276, 1.0), 'grey66': ( 0.6588235294117647, 0.6588235294117647, 0.6588235294117647, 1.0), 'grey67': ( 0.6705882352941176, 0.6705882352941176, 0.6705882352941176, 1.0), 'grey68': ( 0.67843137254901964, 0.67843137254901964, 0.67843137254901964, 1.0), 'grey69': ( 0.69019607843137254, 0.69019607843137254, 0.69019607843137254, 1.0), 'grey7': ( 0.070588235294117646, 0.070588235294117646, 0.070588235294117646, 1.0), 'grey70': ( 0.70196078431372544, 0.70196078431372544, 0.70196078431372544, 1.0), 'grey71': ( 0.70980392156862748, 0.70980392156862748, 0.70980392156862748, 1.0), 'grey72': ( 0.72156862745098038, 0.72156862745098038, 0.72156862745098038, 1.0), 'grey73': ( 0.72941176470588232, 0.72941176470588232, 0.72941176470588232, 1.0), 'grey74': ( 0.74117647058823533, 0.74117647058823533, 0.74117647058823533, 1.0), 'grey75': ( 0.74901960784313726, 0.74901960784313726, 0.74901960784313726, 1.0), 'grey76': ( 0.76078431372549016, 0.76078431372549016, 0.76078431372549016, 1.0), 'grey77': ( 0.7686274509803922, 0.7686274509803922, 0.7686274509803922, 1.0), 'grey78': ( 0.7803921568627451, 0.7803921568627451, 0.7803921568627451, 1.0), 'grey79': ( 0.78823529411764703, 0.78823529411764703, 0.78823529411764703, 1.0), 'grey8': ( 0.078431372549019607, 0.078431372549019607, 0.078431372549019607, 1.0), 'grey80': ( 0.80000000000000004, 0.80000000000000004, 0.80000000000000004, 1.0), 'grey81': ( 0.81176470588235294, 0.81176470588235294, 0.81176470588235294, 1.0), 'grey82': ( 0.81960784313725488, 0.81960784313725488, 0.81960784313725488, 1.0), 'grey83': ( 0.83137254901960789, 0.83137254901960789, 0.83137254901960789, 1.0), 'grey84': ( 0.83921568627450982, 0.83921568627450982, 0.83921568627450982, 1.0), 'grey85': ( 0.85098039215686272, 0.85098039215686272, 0.85098039215686272, 1.0), 'grey86': ( 0.85882352941176465, 0.85882352941176465, 0.85882352941176465, 1.0), 'grey87': ( 0.87058823529411766, 0.87058823529411766, 0.87058823529411766, 1.0), 'grey88': ( 0.8784313725490196, 0.8784313725490196, 0.8784313725490196, 1.0), 'grey89': ( 0.8901960784313725, 0.8901960784313725, 0.8901960784313725, 1.0), 'grey9': ( 0.090196078431372548, 0.090196078431372548, 0.090196078431372548, 1.0), 'grey90': ( 0.89803921568627454, 0.89803921568627454, 0.89803921568627454, 1.0), 'grey91': ( 0.90980392156862744, 0.90980392156862744, 0.90980392156862744, 1.0), 'grey92': ( 0.92156862745098034, 0.92156862745098034, 0.92156862745098034, 1.0), 'grey93': ( 0.92941176470588238, 0.92941176470588238, 0.92941176470588238, 1.0), 'grey94': ( 0.94117647058823528, 0.94117647058823528, 0.94117647058823528, 1.0), 'grey95': ( 0.94901960784313721, 0.94901960784313721, 0.94901960784313721, 1.0), 'grey96': ( 0.96078431372549022, 0.96078431372549022, 0.96078431372549022, 1.0), 'grey97': ( 0.96862745098039216, 0.96862745098039216, 0.96862745098039216, 1.0), 'grey98': ( 0.98039215686274506, 0.98039215686274506, 0.98039215686274506, 1.0), 'grey99': ( 0.9882352941176471, 0.9882352941176471, 0.9882352941176471, 1.0), 'honeydew': (0.94117647058823528, 1.0, 0.94117647058823528, 1.0), 'honeydew1': (0.94117647058823528, 1.0, 0.94117647058823528, 1.0), 'honeydew2': ( 0.8784313725490196, 0.93333333333333335, 0.8784313725490196, 1.0), 'honeydew3': ( 0.75686274509803919, 0.80392156862745101, 0.75686274509803919, 1.0), 'honeydew4': ( 0.51372549019607838, 0.54509803921568623, 0.51372549019607838, 1.0), 'hot pink': (1.0, 0.41176470588235292, 0.70588235294117652, 1.0), 'hotpink': (1.0, 0.41176470588235292, 0.70588235294117652, 1.0), 'hotpink1': (1.0, 0.43137254901960786, 0.70588235294117652, 1.0), 'hotpink2': ( 0.93333333333333335, 0.41568627450980394, 0.65490196078431373, 1.0), 'hotpink3': ( 0.80392156862745101, 0.37647058823529411, 0.56470588235294117, 1.0), 'hotpink4': ( 0.54509803921568623, 0.22745098039215686, 0.3843137254901961, 1.0), 'indian red': ( 0.80392156862745101, 0.36078431372549019, 0.36078431372549019, 1.0), 'indianred': ( 0.80392156862745101, 0.36078431372549019, 0.36078431372549019, 1.0), 'indianred1': (1.0, 0.41568627450980394, 0.41568627450980394, 1.0), 'indianred2': ( 0.93333333333333335, 0.38823529411764707, 0.38823529411764707, 1.0), 'indianred3': ( 0.80392156862745101, 0.33333333333333331, 0.33333333333333331, 1.0), 'indianred4': ( 0.54509803921568623, 0.22745098039215686, 0.22745098039215686, 1.0), 'ivory': (1.0, 1.0, 0.94117647058823528, 1.0), 'ivory1': (1.0, 1.0, 0.94117647058823528, 1.0), 'ivory2': ( 0.93333333333333335, 0.93333333333333335, 0.8784313725490196, 1.0), 'ivory3': ( 0.80392156862745101, 0.80392156862745101, 0.75686274509803919, 1.0), 'ivory4': ( 0.54509803921568623, 0.54509803921568623, 0.51372549019607838, 1.0), 'khaki': ( 0.94117647058823528, 0.90196078431372551, 0.5490196078431373, 1.0), 'khaki1': (1.0, 0.96470588235294119, 0.5607843137254902, 1.0), 'khaki2': ( 0.93333333333333335, 0.90196078431372551, 0.52156862745098043, 1.0), 'khaki3': ( 0.80392156862745101, 0.77647058823529413, 0.45098039215686275, 1.0), 'khaki4': ( 0.54509803921568623, 0.52549019607843139, 0.30588235294117649, 1.0), 'lavender': ( 0.90196078431372551, 0.90196078431372551, 0.98039215686274506, 1.0), 'lavender blush': (1.0, 0.94117647058823528, 0.96078431372549022, 1.0), 'lavenderblush': (1.0, 0.94117647058823528, 0.96078431372549022, 1.0), 'lavenderblush1': (1.0, 0.94117647058823528, 0.96078431372549022, 1.0), 'lavenderblush2': ( 0.93333333333333335, 0.8784313725490196, 0.89803921568627454, 1.0), 'lavenderblush3': ( 0.80392156862745101, 0.75686274509803919, 0.77254901960784317, 1.0), 'lavenderblush4': ( 0.54509803921568623, 0.51372549019607838, 0.52549019607843139, 1.0), 'lawn green': (0.48627450980392156, 0.9882352941176471, 0.0, 1.0), 'lawngreen': (0.48627450980392156, 0.9882352941176471, 0.0, 1.0), 'lemon chiffon': (1.0, 0.98039215686274506, 0.80392156862745101, 1.0), 'lemonchiffon': (1.0, 0.98039215686274506, 0.80392156862745101, 1.0), 'lemonchiffon1': (1.0, 0.98039215686274506, 0.80392156862745101, 1.0), 'lemonchiffon2': ( 0.93333333333333335, 0.9137254901960784, 0.74901960784313726, 1.0), 'lemonchiffon3': ( 0.80392156862745101, 0.78823529411764703, 0.6470588235294118, 1.0), 'lemonchiffon4': ( 0.54509803921568623, 0.53725490196078429, 0.4392156862745098, 1.0), 'light blue': ( 0.67843137254901964, 0.84705882352941175, 0.90196078431372551, 1.0), 'light coral': ( 0.94117647058823528, 0.50196078431372548, 0.50196078431372548, 1.0), 'light cyan': (0.8784313725490196, 1.0, 1.0, 1.0), 'light goldenrod': ( 0.93333333333333335, 0.8666666666666667, 0.50980392156862742, 1.0), 'light goldenrod yellow': ( 0.98039215686274506, 0.98039215686274506, 0.82352941176470584, 1.0), 'light gray': ( 0.82745098039215681, 0.82745098039215681, 0.82745098039215681, 1.0), 'light green': ( 0.56470588235294117, 0.93333333333333335, 0.56470588235294117, 1.0), 'light grey': ( 0.82745098039215681, 0.82745098039215681, 0.82745098039215681, 1.0), 'light pink': (1.0, 0.71372549019607845, 0.75686274509803919, 1.0), 'light salmon': (1.0, 0.62745098039215685, 0.47843137254901963, 1.0), 'light sea green': ( 0.12549019607843137, 0.69803921568627447, 0.66666666666666663, 1.0), 'light sky blue': ( 0.52941176470588236, 0.80784313725490198, 0.98039215686274506, 1.0), 'light slate blue': (0.51764705882352946, 0.4392156862745098, 1.0, 1.0), 'light slate gray': ( 0.46666666666666667, 0.53333333333333333, 0.59999999999999998, 1.0), 'light slate grey': ( 0.46666666666666667, 0.53333333333333333, 0.59999999999999998, 1.0), 'light steel blue': ( 0.69019607843137254, 0.7686274509803922, 0.87058823529411766, 1.0), 'light yellow': (1.0, 1.0, 0.8784313725490196, 1.0), 'lightblue': ( 0.67843137254901964, 0.84705882352941175, 0.90196078431372551, 1.0), 'lightblue1': (0.74901960784313726, 0.93725490196078431, 1.0, 1.0), 'lightblue2': ( 0.69803921568627447, 0.87450980392156863, 0.93333333333333335, 1.0), 'lightblue3': ( 0.60392156862745094, 0.75294117647058822, 0.80392156862745101, 1.0), 'lightblue4': ( 0.40784313725490196, 0.51372549019607838, 0.54509803921568623, 1.0), 'lightcoral': ( 0.94117647058823528, 0.50196078431372548, 0.50196078431372548, 1.0), 'lightcyan': (0.8784313725490196, 1.0, 1.0, 1.0), 'lightcyan1': (0.8784313725490196, 1.0, 1.0, 1.0), 'lightcyan2': ( 0.81960784313725488, 0.93333333333333335, 0.93333333333333335, 1.0), 'lightcyan3': ( 0.70588235294117652, 0.80392156862745101, 0.80392156862745101, 1.0), 'lightcyan4': ( 0.47843137254901963, 0.54509803921568623, 0.54509803921568623, 1.0), 'lightgoldenrod': ( 0.93333333333333335, 0.8666666666666667, 0.50980392156862742, 1.0), 'lightgoldenrod1': (1.0, 0.92549019607843142, 0.54509803921568623, 1.0), 'lightgoldenrod2': ( 0.93333333333333335, 0.86274509803921573, 0.50980392156862742, 1.0), 'lightgoldenrod3': ( 0.80392156862745101, 0.74509803921568629, 0.4392156862745098, 1.0), 'lightgoldenrod4': ( 0.54509803921568623, 0.50588235294117645, 0.29803921568627451, 1.0), 'lightgoldenrodyellow': ( 0.98039215686274506, 0.98039215686274506, 0.82352941176470584, 1.0), 'lightgray': ( 0.82745098039215681, 0.82745098039215681, 0.82745098039215681, 1.0), 'lightgreen': ( 0.56470588235294117, 0.93333333333333335, 0.56470588235294117, 1.0), 'lightgrey': ( 0.82745098039215681, 0.82745098039215681, 0.82745098039215681, 1.0), 'lightpink': (1.0, 0.71372549019607845, 0.75686274509803919, 1.0), 'lightpink1': (1.0, 0.68235294117647061, 0.72549019607843135, 1.0), 'lightpink2': ( 0.93333333333333335, 0.63529411764705879, 0.67843137254901964, 1.0), 'lightpink3': ( 0.80392156862745101, 0.5490196078431373, 0.58431372549019611, 1.0), 'lightpink4': ( 0.54509803921568623, 0.37254901960784315, 0.396078431372549, 1.0), 'lightsalmon': (1.0, 0.62745098039215685, 0.47843137254901963, 1.0), 'lightsalmon1': (1.0, 0.62745098039215685, 0.47843137254901963, 1.0), 'lightsalmon2': ( 0.93333333333333335, 0.58431372549019611, 0.44705882352941179, 1.0), 'lightsalmon3': ( 0.80392156862745101, 0.50588235294117645, 0.3843137254901961, 1.0), 'lightsalmon4': ( 0.54509803921568623, 0.3411764705882353, 0.25882352941176473, 1.0), 'lightseagreen': ( 0.12549019607843137, 0.69803921568627447, 0.66666666666666663, 1.0), 'lightskyblue': ( 0.52941176470588236, 0.80784313725490198, 0.98039215686274506, 1.0), 'lightskyblue1': (0.69019607843137254, 0.88627450980392153, 1.0, 1.0), 'lightskyblue2': ( 0.64313725490196083, 0.82745098039215681, 0.93333333333333335, 1.0), 'lightskyblue3': ( 0.55294117647058827, 0.71372549019607845, 0.80392156862745101, 1.0), 'lightskyblue4': ( 0.37647058823529411, 0.4823529411764706, 0.54509803921568623, 1.0), 'lightslateblue': (0.51764705882352946, 0.4392156862745098, 1.0, 1.0), 'lightslategray': ( 0.46666666666666667, 0.53333333333333333, 0.59999999999999998, 1.0), 'lightslategrey': ( 0.46666666666666667, 0.53333333333333333, 0.59999999999999998, 1.0), 'lightsteelblue': ( 0.69019607843137254, 0.7686274509803922, 0.87058823529411766, 1.0), 'lightsteelblue1': (0.792156862745098, 0.88235294117647056, 1.0, 1.0), 'lightsteelblue2': ( 0.73725490196078436, 0.82352941176470584, 0.93333333333333335, 1.0), 'lightsteelblue3': ( 0.63529411764705879, 0.70980392156862748, 0.80392156862745101, 1.0), 'lightsteelblue4': ( 0.43137254901960786, 0.4823529411764706, 0.54509803921568623, 1.0), 'lightyellow': (1.0, 1.0, 0.8784313725490196, 1.0), 'lightyellow1': (1.0, 1.0, 0.8784313725490196, 1.0), 'lightyellow2': ( 0.93333333333333335, 0.93333333333333335, 0.81960784313725488, 1.0), 'lightyellow3': ( 0.80392156862745101, 0.80392156862745101, 0.70588235294117652, 1.0), 'lightyellow4': ( 0.54509803921568623, 0.54509803921568623, 0.47843137254901963, 1.0), 'lime': (0.0, 1.0, 0.0, 1.0), 'lime green': ( 0.19607843137254902, 0.80392156862745101, 0.19607843137254902, 1.0), 'limegreen': ( 0.19607843137254902, 0.80392156862745101, 0.19607843137254902, 1.0), 'linen': ( 0.98039215686274506, 0.94117647058823528, 0.90196078431372551, 1.0), 'magenta': (1.0, 0.0, 1.0, 1.0), 'magenta1': (1.0, 0.0, 1.0, 1.0), 'magenta2': (0.93333333333333335, 0.0, 0.93333333333333335, 1.0), 'magenta3': (0.80392156862745101, 0.0, 0.80392156862745101, 1.0), 'magenta4': (0.54509803921568623, 0.0, 0.54509803921568623, 1.0), 'maroon': ( 0.69019607843137254, 0.18823529411764706, 0.37647058823529411, 1.0), 'maroon1': (1.0, 0.20392156862745098, 0.70196078431372544, 1.0), 'maroon2': ( 0.93333333333333335, 0.18823529411764706, 0.65490196078431373, 1.0), 'maroon3': ( 0.80392156862745101, 0.16078431372549021, 0.56470588235294117, 1.0), 'maroon4': ( 0.54509803921568623, 0.10980392156862745, 0.3843137254901961, 1.0), 'medium aquamarine': ( 0.40000000000000002, 0.80392156862745101, 0.66666666666666663, 1.0), 'medium blue': (0.0, 0.0, 0.80392156862745101, 1.0), 'medium orchid': ( 0.72941176470588232, 0.33333333333333331, 0.82745098039215681, 1.0), 'medium purple': ( 0.57647058823529407, 0.4392156862745098, 0.85882352941176465, 1.0), 'medium sea green': ( 0.23529411764705882, 0.70196078431372544, 0.44313725490196076, 1.0), 'medium slate blue': ( 0.4823529411764706, 0.40784313725490196, 0.93333333333333335, 1.0), 'medium spring green': ( 0.0, 0.98039215686274506, 0.60392156862745094, 1.0), 'medium turquoise': ( 0.28235294117647058, 0.81960784313725488, 0.80000000000000004, 1.0), 'medium violet red': ( 0.7803921568627451, 0.082352941176470587, 0.52156862745098043, 1.0), 'mediumaquamarine': ( 0.40000000000000002, 0.80392156862745101, 0.66666666666666663, 1.0), 'mediumblue': (0.0, 0.0, 0.80392156862745101, 1.0), 'mediumorchid': ( 0.72941176470588232, 0.33333333333333331, 0.82745098039215681, 1.0), 'mediumorchid1': (0.8784313725490196, 0.40000000000000002, 1.0, 1.0), 'mediumorchid2': ( 0.81960784313725488, 0.37254901960784315, 0.93333333333333335, 1.0), 'mediumorchid3': ( 0.70588235294117652, 0.32156862745098042, 0.80392156862745101, 1.0), 'mediumorchid4': ( 0.47843137254901963, 0.21568627450980393, 0.54509803921568623, 1.0), 'mediumpurple': ( 0.57647058823529407, 0.4392156862745098, 0.85882352941176465, 1.0), 'mediumpurple1': (0.6705882352941176, 0.50980392156862742, 1.0, 1.0), 'mediumpurple2': ( 0.62352941176470589, 0.47450980392156861, 0.93333333333333335, 1.0), 'mediumpurple3': ( 0.53725490196078429, 0.40784313725490196, 0.80392156862745101, 1.0), 'mediumpurple4': ( 0.36470588235294116, 0.27843137254901962, 0.54509803921568623, 1.0), 'mediumseagreen': ( 0.23529411764705882, 0.70196078431372544, 0.44313725490196076, 1.0), 'mediumslateblue': ( 0.4823529411764706, 0.40784313725490196, 0.93333333333333335, 1.0), 'mediumspringgreen': (0.0, 0.98039215686274506, 0.60392156862745094, 1.0), 'mediumturquoise': ( 0.28235294117647058, 0.81960784313725488, 0.80000000000000004, 1.0), 'mediumvioletred': ( 0.7803921568627451, 0.082352941176470587, 0.52156862745098043, 1.0), 'midnight blue': ( 0.098039215686274508, 0.098039215686274508, 0.4392156862745098, 1.0), 'midnightblue': ( 0.098039215686274508, 0.098039215686274508, 0.4392156862745098, 1.0), 'mint cream': (0.96078431372549022, 1.0, 0.98039215686274506, 1.0), 'mintcream': (0.96078431372549022, 1.0, 0.98039215686274506, 1.0), 'misty rose': (1.0, 0.89411764705882357, 0.88235294117647056, 1.0), 'mistyrose': (1.0, 0.89411764705882357, 0.88235294117647056, 1.0), 'mistyrose1': (1.0, 0.89411764705882357, 0.88235294117647056, 1.0), 'mistyrose2': ( 0.93333333333333335, 0.83529411764705885, 0.82352941176470584, 1.0), 'mistyrose3': ( 0.80392156862745101, 0.71764705882352942, 0.70980392156862748, 1.0), 'mistyrose4': ( 0.54509803921568623, 0.49019607843137253, 0.4823529411764706, 1.0), 'moccasin': (1.0, 0.89411764705882357, 0.70980392156862748, 1.0), 'navajo white': (1.0, 0.87058823529411766, 0.67843137254901964, 1.0), 'navajowhite': (1.0, 0.87058823529411766, 0.67843137254901964, 1.0), 'navajowhite1': (1.0, 0.87058823529411766, 0.67843137254901964, 1.0), 'navajowhite2': ( 0.93333333333333335, 0.81176470588235294, 0.63137254901960782, 1.0), 'navajowhite3': ( 0.80392156862745101, 0.70196078431372544, 0.54509803921568623, 1.0), 'navajowhite4': ( 0.54509803921568623, 0.47450980392156861, 0.36862745098039218, 1.0), 'navy': (0.0, 0.0, 0.50196078431372548, 1.0), 'navy blue': (0.0, 0.0, 0.50196078431372548, 1.0), 'navyblue': (0.0, 0.0, 0.50196078431372548, 1.0), 'old lace': ( 0.99215686274509807, 0.96078431372549022, 0.90196078431372551, 1.0), 'oldlace': ( 0.99215686274509807, 0.96078431372549022, 0.90196078431372551, 1.0), 'olive': (0.5, 0.5, 0.0, 1.0), 'olive drab': ( 0.41960784313725491, 0.55686274509803924, 0.13725490196078433, 1.0), 'olivedrab': ( 0.41960784313725491, 0.55686274509803924, 0.13725490196078433, 1.0), 'olivedrab1': (0.75294117647058822, 1.0, 0.24313725490196078, 1.0), 'olivedrab2': ( 0.70196078431372544, 0.93333333333333335, 0.22745098039215686, 1.0), 'olivedrab3': ( 0.60392156862745094, 0.80392156862745101, 0.19607843137254902, 1.0), 'olivedrab4': ( 0.41176470588235292, 0.54509803921568623, 0.13333333333333333, 1.0), 'orange': (1.0, 0.6470588235294118, 0.0, 1.0), 'orange red': (1.0, 0.27058823529411763, 0.0, 1.0), 'orange1': (1.0, 0.6470588235294118, 0.0, 1.0), 'orange2': (0.93333333333333335, 0.60392156862745094, 0.0, 1.0), 'orange3': (0.80392156862745101, 0.52156862745098043, 0.0, 1.0), 'orange4': (0.54509803921568623, 0.35294117647058826, 0.0, 1.0), 'orangered': (1.0, 0.27058823529411763, 0.0, 1.0), 'orangered1': (1.0, 0.27058823529411763, 0.0, 1.0), 'orangered2': (0.93333333333333335, 0.25098039215686274, 0.0, 1.0), 'orangered3': (0.80392156862745101, 0.21568627450980393, 0.0, 1.0), 'orangered4': (0.54509803921568623, 0.14509803921568629, 0.0, 1.0), 'orchid': ( 0.85490196078431369, 0.4392156862745098, 0.83921568627450982, 1.0), 'orchid1': (1.0, 0.51372549019607838, 0.98039215686274506, 1.0), 'orchid2': ( 0.93333333333333335, 0.47843137254901963, 0.9137254901960784, 1.0), 'orchid3': ( 0.80392156862745101, 0.41176470588235292, 0.78823529411764703, 1.0), 'orchid4': ( 0.54509803921568623, 0.27843137254901962, 0.53725490196078429, 1.0), 'pale goldenrod': ( 0.93333333333333335, 0.90980392156862744, 0.66666666666666663, 1.0), 'pale green': ( 0.59607843137254901, 0.98431372549019602, 0.59607843137254901, 1.0), 'pale turquoise': ( 0.68627450980392157, 0.93333333333333335, 0.93333333333333335, 1.0), 'pale violet red': ( 0.85882352941176465, 0.4392156862745098, 0.57647058823529407, 1.0), 'palegoldenrod': ( 0.93333333333333335, 0.90980392156862744, 0.66666666666666663, 1.0), 'palegreen': ( 0.59607843137254901, 0.98431372549019602, 0.59607843137254901, 1.0), 'palegreen1': (0.60392156862745094, 1.0, 0.60392156862745094, 1.0), 'palegreen2': ( 0.56470588235294117, 0.93333333333333335, 0.56470588235294117, 1.0), 'palegreen3': ( 0.48627450980392156, 0.80392156862745101, 0.48627450980392156, 1.0), 'palegreen4': ( 0.32941176470588235, 0.54509803921568623, 0.32941176470588235, 1.0), 'paleturquoise': ( 0.68627450980392157, 0.93333333333333335, 0.93333333333333335, 1.0), 'paleturquoise1': (0.73333333333333328, 1.0, 1.0, 1.0), 'paleturquoise2': ( 0.68235294117647061, 0.93333333333333335, 0.93333333333333335, 1.0), 'paleturquoise3': ( 0.58823529411764708, 0.80392156862745101, 0.80392156862745101, 1.0), 'paleturquoise4': ( 0.40000000000000002, 0.54509803921568623, 0.54509803921568623, 1.0), 'palevioletred': ( 0.85882352941176465, 0.4392156862745098, 0.57647058823529407, 1.0), 'palevioletred1': (1.0, 0.50980392156862742, 0.6705882352941176, 1.0), 'palevioletred2': ( 0.93333333333333335, 0.47450980392156861, 0.62352941176470589, 1.0), 'palevioletred3': ( 0.80392156862745101, 0.40784313725490196, 0.53725490196078429, 1.0), 'palevioletred4': ( 0.54509803921568623, 0.27843137254901962, 0.36470588235294116, 1.0), 'papaya whip': (1.0, 0.93725490196078431, 0.83529411764705885, 1.0), 'papayawhip': (1.0, 0.93725490196078431, 0.83529411764705885, 1.0), 'peach puff': (1.0, 0.85490196078431369, 0.72549019607843135, 1.0), 'peachpuff': (1.0, 0.85490196078431369, 0.72549019607843135, 1.0), 'peachpuff1': (1.0, 0.85490196078431369, 0.72549019607843135, 1.0), 'peachpuff2': ( 0.93333333333333335, 0.79607843137254897, 0.67843137254901964, 1.0), 'peachpuff3': ( 0.80392156862745101, 0.68627450980392157, 0.58431372549019611, 1.0), 'peachpuff4': ( 0.54509803921568623, 0.46666666666666667, 0.396078431372549, 1.0), 'peru': ( 0.80392156862745101, 0.52156862745098043, 0.24705882352941178, 1.0), 'pink': (1.0, 0.75294117647058822, 0.79607843137254897, 1.0), 'pink1': (1.0, 0.70980392156862748, 0.77254901960784317, 1.0), 'pink2': ( 0.93333333333333335, 0.66274509803921566, 0.72156862745098038, 1.0), 'pink3': ( 0.80392156862745101, 0.56862745098039214, 0.61960784313725492, 1.0), 'pink4': ( 0.54509803921568623, 0.38823529411764707, 0.42352941176470588, 1.0), 'plum': (0.8666666666666667, 0.62745098039215685, 0.8666666666666667, 1.0), 'plum1': (1.0, 0.73333333333333328, 1.0, 1.0), 'plum2': ( 0.93333333333333335, 0.68235294117647061, 0.93333333333333335, 1.0), 'plum3': ( 0.80392156862745101, 0.58823529411764708, 0.80392156862745101, 1.0), 'plum4': ( 0.54509803921568623, 0.40000000000000002, 0.54509803921568623, 1.0), 'powder blue': ( 0.69019607843137254, 0.8784313725490196, 0.90196078431372551, 1.0), 'powderblue': ( 0.69019607843137254, 0.8784313725490196, 0.90196078431372551, 1.0), 'purple': ( 0.62745098039215685, 0.12549019607843137, 0.94117647058823528, 1.0), 'purple1': (0.60784313725490191, 0.18823529411764706, 1.0, 1.0), 'purple2': ( 0.56862745098039214, 0.17254901960784313, 0.93333333333333335, 1.0), 'purple3': ( 0.49019607843137253, 0.14901960784313725, 0.80392156862745101, 1.0), 'purple4': ( 0.33333333333333331, 0.10196078431372549, 0.54509803921568623, 1.0), 'red': (1.0, 0.0, 0.0, 1.0), 'red1': (1.0, 0.0, 0.0, 1.0), 'red2': (0.93333333333333335, 0.0, 0.0, 1.0), 'red3': (0.80392156862745101, 0.0, 0.0, 1.0), 'red4': (0.54509803921568623, 0.0, 0.0, 1.0), 'rosy brown': ( 0.73725490196078436, 0.5607843137254902, 0.5607843137254902, 1.0), 'rosybrown': ( 0.73725490196078436, 0.5607843137254902, 0.5607843137254902, 1.0), 'rosybrown1': (1.0, 0.75686274509803919, 0.75686274509803919, 1.0), 'rosybrown2': ( 0.93333333333333335, 0.70588235294117652, 0.70588235294117652, 1.0), 'rosybrown3': ( 0.80392156862745101, 0.60784313725490191, 0.60784313725490191, 1.0), 'rosybrown4': ( 0.54509803921568623, 0.41176470588235292, 0.41176470588235292, 1.0), 'royal blue': ( 0.25490196078431371, 0.41176470588235292, 0.88235294117647056, 1.0), 'royalblue': ( 0.25490196078431371, 0.41176470588235292, 0.88235294117647056, 1.0), 'royalblue1': (0.28235294117647058, 0.46274509803921571, 1.0, 1.0), 'royalblue2': ( 0.2627450980392157, 0.43137254901960786, 0.93333333333333335, 1.0), 'royalblue3': ( 0.22745098039215686, 0.37254901960784315, 0.80392156862745101, 1.0), 'royalblue4': ( 0.15294117647058825, 0.25098039215686274, 0.54509803921568623, 1.0), 'saddle brown': ( 0.54509803921568623, 0.27058823529411763, 0.074509803921568626, 1.0), 'saddlebrown': ( 0.54509803921568623, 0.27058823529411763, 0.074509803921568626, 1.0), 'salmon': ( 0.98039215686274506, 0.50196078431372548, 0.44705882352941179, 1.0), 'salmon1': (1.0, 0.5490196078431373, 0.41176470588235292, 1.0), 'salmon2': ( 0.93333333333333335, 0.50980392156862742, 0.3843137254901961, 1.0), 'salmon3': ( 0.80392156862745101, 0.4392156862745098, 0.32941176470588235, 1.0), 'salmon4': ( 0.54509803921568623, 0.29803921568627451, 0.22352941176470589, 1.0), 'sandy brown': ( 0.95686274509803926, 0.64313725490196083, 0.37647058823529411, 1.0), 'sandybrown': ( 0.95686274509803926, 0.64313725490196083, 0.37647058823529411, 1.0), 'sea green': ( 0.1803921568627451, 0.54509803921568623, 0.3411764705882353, 1.0), 'seagreen': ( 0.1803921568627451, 0.54509803921568623, 0.3411764705882353, 1.0), 'seagreen1': (0.32941176470588235, 1.0, 0.62352941176470589, 1.0), 'seagreen2': ( 0.30588235294117649, 0.93333333333333335, 0.58039215686274515, 1.0), 'seagreen3': ( 0.2627450980392157, 0.80392156862745101, 0.50196078431372548, 1.0), 'seagreen4': ( 0.1803921568627451, 0.54509803921568623, 0.3411764705882353, 1.0), 'seashell': (1.0, 0.96078431372549022, 0.93333333333333335, 1.0), 'seashell1': (1.0, 0.96078431372549022, 0.93333333333333335, 1.0), 'seashell2': ( 0.93333333333333335, 0.89803921568627454, 0.87058823529411766, 1.0), 'seashell3': ( 0.80392156862745101, 0.77254901960784317, 0.74901960784313726, 1.0), 'seashell4': ( 0.54509803921568623, 0.52549019607843139, 0.50980392156862742, 1.0), 'sienna': ( 0.62745098039215685, 0.32156862745098042, 0.17647058823529413, 1.0), 'sienna1': (1.0, 0.50980392156862742, 0.27843137254901962, 1.0), 'sienna2': ( 0.93333333333333335, 0.47450980392156861, 0.25882352941176473, 1.0), 'sienna3': ( 0.80392156862745101, 0.40784313725490196, 0.22352941176470589, 1.0), 'sienna4': ( 0.54509803921568623, 0.27843137254901962, 0.14901960784313725, 1.0), 'silver': (0.75, 0.75, 0.75, 1.0), 'sky blue': ( 0.52941176470588236, 0.80784313725490198, 0.92156862745098034, 1.0), 'skyblue': ( 0.52941176470588236, 0.80784313725490198, 0.92156862745098034, 1.0), 'skyblue1': (0.52941176470588236, 0.80784313725490198, 1.0, 1.0), 'skyblue2': ( 0.49411764705882355, 0.75294117647058822, 0.93333333333333335, 1.0), 'skyblue3': ( 0.42352941176470588, 0.65098039215686276, 0.80392156862745101, 1.0), 'skyblue4': ( 0.29019607843137257, 0.4392156862745098, 0.54509803921568623, 1.0), 'slate blue': ( 0.41568627450980394, 0.35294117647058826, 0.80392156862745101, 1.0), 'slate gray': ( 0.4392156862745098, 0.50196078431372548, 0.56470588235294117, 1.0), 'slate grey': ( 0.4392156862745098, 0.50196078431372548, 0.56470588235294117, 1.0), 'slateblue': ( 0.41568627450980394, 0.35294117647058826, 0.80392156862745101, 1.0), 'slateblue1': (0.51372549019607838, 0.43529411764705883, 1.0, 1.0), 'slateblue2': ( 0.47843137254901963, 0.40392156862745099, 0.93333333333333335, 1.0), 'slateblue3': ( 0.41176470588235292, 0.34901960784313724, 0.80392156862745101, 1.0), 'slateblue4': ( 0.27843137254901962, 0.23529411764705882, 0.54509803921568623, 1.0), 'slategray': ( 0.4392156862745098, 0.50196078431372548, 0.56470588235294117, 1.0), 'slategray1': (0.77647058823529413, 0.88627450980392153, 1.0, 1.0), 'slategray2': ( 0.72549019607843135, 0.82745098039215681, 0.93333333333333335, 1.0), 'slategray3': ( 0.62352941176470589, 0.71372549019607845, 0.80392156862745101, 1.0), 'slategray4': ( 0.42352941176470588, 0.4823529411764706, 0.54509803921568623, 1.0), 'slategrey': ( 0.4392156862745098, 0.50196078431372548, 0.56470588235294117, 1.0), 'snow': (1.0, 0.98039215686274506, 0.98039215686274506, 1.0), 'snow1': (1.0, 0.98039215686274506, 0.98039215686274506, 1.0), 'snow2': ( 0.93333333333333335, 0.9137254901960784, 0.9137254901960784, 1.0), 'snow3': ( 0.80392156862745101, 0.78823529411764703, 0.78823529411764703, 1.0), 'snow4': ( 0.54509803921568623, 0.53725490196078429, 0.53725490196078429, 1.0), 'spring green': (0.0, 1.0, 0.49803921568627452, 1.0), 'springgreen': (0.0, 1.0, 0.49803921568627452, 1.0), 'springgreen1': (0.0, 1.0, 0.49803921568627452, 1.0), 'springgreen2': (0.0, 0.93333333333333335, 0.46274509803921571, 1.0), 'springgreen3': (0.0, 0.80392156862745101, 0.40000000000000002, 1.0), 'springgreen4': (0.0, 0.54509803921568623, 0.27058823529411763, 1.0), 'steel blue': ( 0.27450980392156865, 0.50980392156862742, 0.70588235294117652, 1.0), 'steelblue': ( 0.27450980392156865, 0.50980392156862742, 0.70588235294117652, 1.0), 'steelblue1': (0.38823529411764707, 0.72156862745098038, 1.0, 1.0), 'steelblue2': ( 0.36078431372549019, 0.67450980392156867, 0.93333333333333335, 1.0), 'steelblue3': ( 0.30980392156862746, 0.58039215686274515, 0.80392156862745101, 1.0), 'steelblue4': ( 0.21176470588235294, 0.39215686274509803, 0.54509803921568623, 1.0), 'tan': (0.82352941176470584, 0.70588235294117652, 0.5490196078431373, 1.0), 'tan1': (1.0, 0.6470588235294118, 0.30980392156862746, 1.0), 'tan2': ( 0.93333333333333335, 0.60392156862745094, 0.28627450980392155, 1.0), 'tan3': ( 0.80392156862745101, 0.52156862745098043, 0.24705882352941178, 1.0), 'tan4': ( 0.54509803921568623, 0.35294117647058826, 0.16862745098039217, 1.0), 'teal': (0.0, 0.5, 0.5, 1.0), 'thistle': ( 0.84705882352941175, 0.74901960784313726, 0.84705882352941175, 1.0), 'thistle1': (1.0, 0.88235294117647056, 1.0, 1.0), 'thistle2': ( 0.93333333333333335, 0.82352941176470584, 0.93333333333333335, 1.0), 'thistle3': ( 0.80392156862745101, 0.70980392156862748, 0.80392156862745101, 1.0), 'thistle4': ( 0.54509803921568623, 0.4823529411764706, 0.54509803921568623, 1.0), 'tomato': (1.0, 0.38823529411764707, 0.27843137254901962, 1.0), 'tomato1': (1.0, 0.38823529411764707, 0.27843137254901962, 1.0), 'tomato2': ( 0.93333333333333335, 0.36078431372549019, 0.25882352941176473, 1.0), 'tomato3': ( 0.80392156862745101, 0.30980392156862746, 0.22352941176470589, 1.0), 'tomato4': ( 0.54509803921568623, 0.21176470588235294, 0.14901960784313725, 1.0), 'turquoise': ( 0.25098039215686274, 0.8784313725490196, 0.81568627450980391, 1.0), 'turquoise1': (0.0, 0.96078431372549022, 1.0, 1.0), 'turquoise2': (0.0, 0.89803921568627454, 0.93333333333333335, 1.0), 'turquoise3': (0.0, 0.77254901960784317, 0.80392156862745101, 1.0), 'turquoise4': (0.0, 0.52549019607843139, 0.54509803921568623, 1.0), 'violet': ( 0.93333333333333335, 0.50980392156862742, 0.93333333333333335, 1.0), 'violet red': ( 0.81568627450980391, 0.12549019607843137, 0.56470588235294117, 1.0), 'violetred': ( 0.81568627450980391, 0.12549019607843137, 0.56470588235294117, 1.0), 'violetred1': (1.0, 0.24313725490196078, 0.58823529411764708, 1.0), 'violetred2': ( 0.93333333333333335, 0.22745098039215686, 0.5490196078431373, 1.0), 'violetred3': ( 0.80392156862745101, 0.19607843137254902, 0.47058823529411764, 1.0), 'violetred4': ( 0.54509803921568623, 0.13333333333333333, 0.32156862745098042, 1.0), 'wheat': ( 0.96078431372549022, 0.87058823529411766, 0.70196078431372544, 1.0), 'wheat1': (1.0, 0.90588235294117647, 0.72941176470588232, 1.0), 'wheat2': ( 0.93333333333333335, 0.84705882352941175, 0.68235294117647061, 1.0), 'wheat3': ( 0.80392156862745101, 0.72941176470588232, 0.58823529411764708, 1.0), 'wheat4': ( 0.54509803921568623, 0.49411764705882355, 0.40000000000000002, 1.0), 'white': (1.0, 1.0, 1.0, 1.0), 'white smoke': ( 0.96078431372549022, 0.96078431372549022, 0.96078431372549022, 1.0), 'whitesmoke': ( 0.96078431372549022, 0.96078431372549022, 0.96078431372549022, 1.0), 'yellow': (1.0, 1.0, 0.0, 1.0), 'yellow green': ( 0.60392156862745094, 0.80392156862745101, 0.19607843137254902, 1.0), 'yellow1': (1.0, 1.0, 0.0, 1.0), 'yellow2': (0.93333333333333335, 0.93333333333333335, 0.0, 1.0), 'yellow3': (0.80392156862745101, 0.80392156862745101, 0.0, 1.0), 'yellow4': (0.54509803921568623, 0.54509803921568623, 0.0, 1.0), 'yellowgreen': ( 0.60392156862745094, 0.80392156862745101, 0.19607843137254902, 1.0)} palettes = { "gray": GradientPalette("black", "white"), "red-blue": GradientPalette("red", "blue"), "red-purple-blue": AdvancedGradientPalette(["red", "purple", "blue"]), "red-green": GradientPalette("red", "green"), "red-yellow-green": AdvancedGradientPalette(["red", "yellow", "green"]), "red-black-green": AdvancedGradientPalette(["red", "black", "green"]), "rainbow": RainbowPalette(), "heat": AdvancedGradientPalette(["red", "yellow", "white"], indices=[0, 192, 255]), "terrain": AdvancedGradientPalette(["hsv(120, 100%, 65%)", "hsv(60, 100%, 90%)", "hsv(0, 0%, 95%)"]) } python-igraph-0.7.1.post6/igraph/drawing/coord.py0000644000076500000240000000766112453614202022366 0ustar ntamasstaff00000000000000""" Coordinate systems and related plotting routines """ from igraph.compat import property from igraph.drawing.baseclasses import AbstractCairoDrawer from igraph.drawing.utils import BoundingBox __license__ = "GPL" ##################################################################### # pylint: disable-msg=R0922 # R0922: Abstract class is only referenced 1 times class CoordinateSystem(AbstractCairoDrawer): """Class implementing a coordinate system object. Coordinate system objects are used when drawing plots which 2D or 3D coordinate system axes. This is an abstract class which must be extended in order to use it. In general, you'll only need the documentation of this class if you intend to implement an own coordinate system not present in igraph yet. """ def __init__(self, context, bbox): """Initializes the coordinate system. @param context: the context on which the coordinate system will be drawn. @param bbox: the bounding box that will contain the coordinate system. """ AbstractCairoDrawer.__init__(self, context, bbox) def draw(self): """Draws the coordinate system. This method must be overridden in derived classes. """ raise NotImplementedError("abstract class") def local_to_context(self, x, y): """Converts local coordinates to the context coordinate system (given by the bounding box). This method must be overridden in derived classes.""" raise NotImplementedError("abstract class") class DescartesCoordinateSystem(CoordinateSystem): """Class implementing a 2D Descartes coordinate system object.""" def __init__(self, context, bbox, bounds): """Initializes the coordinate system. @param context: the context on which the coordinate system will be drawn. @param bbox: the bounding box that will contain the coordinate system. @param bounds: minimum and maximum X and Y values in a 4-tuple. """ self._bounds, self._bbox = None, None self._sx, self._sy = None, None self._ox, self._oy, self._ox2, self._oy2 = None, None, None, None CoordinateSystem.__init__(self, context, bbox) self.bbox = bbox self.bounds = bounds @property def bbox(self): """Returns the bounding box of the coordinate system""" return BoundingBox(self._bbox.coords) @bbox.setter def bbox(self, bbox): """Sets the bounding box of the coordinate system""" self._bbox = bbox self._recalc_scale_factors() @property def bounds(self): """Returns the lower and upper bounds of the X and Y values""" return self._bounds.coords @bounds.setter def bounds(self, bounds): """Sets the lower and upper bounds of the X and Y values""" self._bounds = BoundingBox(bounds) self._recalc_scale_factors() def _recalc_scale_factors(self): """Recalculates some cached scale factors used within the class""" if self._bounds is None: return self._sx = self._bbox.width / self._bounds.width self._sy = self._bbox.height / self._bounds.height self._ox = self._bounds.left self._oy = self._bounds.top self._ox2 = self._bbox.left self._oy2 = self._bbox.bottom def draw(self): """Draws the coordinate system.""" # Draw the frame coords = self.bbox.coords self.context.set_source_rgb(0., 0., 0.) self.context.set_line_width(1) self.context.rectangle(coords[0], coords[1], \ coords[2]-coords[0], coords[3]-coords[1]) self.context.stroke() def local_to_context(self, x, y): """Converts local coordinates to the context coordinate system (given by the bounding box). """ return (x-self._ox)*self._sx+self._ox2, self._oy2-(y-self._oy)*self._sy python-igraph-0.7.1.post6/igraph/drawing/edge.py0000644000076500000240000004246212534342712022166 0ustar ntamasstaff00000000000000""" Drawers for various edge styles in graph plots. """ __all__ = ["AbstractEdgeDrawer", "AlphaVaryingEdgeDrawer", "ArrowEdgeDrawer", "DarkToLightEdgeDrawer", "LightToDarkEdgeDrawer", "TaperedEdgeDrawer"] __license__ = "GPL" from igraph.drawing.colors import clamp from igraph.drawing.metamagic import AttributeCollectorBase from igraph.drawing.text import TextAlignment from igraph.drawing.utils import find_cairo from math import atan2, cos, pi, sin, sqrt cairo = find_cairo() class AbstractEdgeDrawer(object): """Abstract edge drawer object from which all concrete edge drawer implementations are derived.""" def __init__(self, context, palette): """Constructs the edge drawer. @param context: a Cairo context on which the edges will be drawn. @param palette: the palette that can be used to map integer color indices to colors when drawing edges """ self.context = context self.palette = palette self.VisualEdgeBuilder = self._construct_visual_edge_builder() @staticmethod def _curvature_to_float(value): """Converts values given to the 'curved' edge style argument in plotting calls to floating point values.""" if value is None or value is False: return 0.0 if value is True: return 0.5 return float(value) def _construct_visual_edge_builder(self): """Construct the visual edge builder that will collect the visual attributes of an edge when it is being drawn.""" class VisualEdgeBuilder(AttributeCollectorBase): """Builder that collects some visual properties of an edge for drawing""" _kwds_prefix = "edge_" arrow_size = 1.0 arrow_width = 1.0 color = ("#444", self.palette.get) curved = (0.0, self._curvature_to_float) label = None label_color = ("black", self.palette.get) label_size = 12.0 width = 1.0 return VisualEdgeBuilder def draw_directed_edge(self, edge, src_vertex, dest_vertex): """Draws a directed edge. @param edge: the edge to be drawn. Visual properties of the edge are defined by the attributes of this object. @param src_vertex: the source vertex. Visual properties are given again as attributes. @param dest_vertex: the target vertex. Visual properties are given again as attributes. """ raise NotImplementedError() def draw_loop_edge(self, edge, vertex): """Draws a loop edge. The default implementation draws a small circle. @param edge: the edge to be drawn. Visual properties of the edge are defined by the attributes of this object. @param vertex: the vertex to which the edge is attached. Visual properties are given again as attributes. """ ctx = self.context ctx.set_source_rgba(*edge.color) ctx.set_line_width(edge.width) radius = vertex.size * 1.5 center_x = vertex.position[0] + cos(pi/4) * radius / 2. center_y = vertex.position[1] - sin(pi/4) * radius / 2. ctx.arc(center_x, center_y, radius/2., 0, pi * 2) ctx.stroke() def draw_undirected_edge(self, edge, src_vertex, dest_vertex): """Draws an undirected edge. The default implementation of this method draws undirected edges as straight lines. Loop edges are drawn as small circles. @param edge: the edge to be drawn. Visual properties of the edge are defined by the attributes of this object. @param src_vertex: the source vertex. Visual properties are given again as attributes. @param dest_vertex: the target vertex. Visual properties are given again as attributes. """ if src_vertex == dest_vertex: # TODO return self.draw_loop_edge(edge, src_vertex) ctx = self.context ctx.set_source_rgba(*edge.color) ctx.set_line_width(edge.width) ctx.move_to(*src_vertex.position) if edge.curved: (x1, y1), (x2, y2) = src_vertex.position, dest_vertex.position aux1 = (2*x1+x2) / 3.0 - edge.curved * 0.5 * (y2-y1), \ (2*y1+y2) / 3.0 + edge.curved * 0.5 * (x2-x1) aux2 = (x1+2*x2) / 3.0 - edge.curved * 0.5 * (y2-y1), \ (y1+2*y2) / 3.0 + edge.curved * 0.5 * (x2-x1) ctx.curve_to(aux1[0], aux1[1], aux2[0], aux2[1], *dest_vertex.position) else: ctx.line_to(*dest_vertex.position) ctx.stroke() def get_label_position(self, edge, src_vertex, dest_vertex): """Returns the position where the label of an edge should be drawn. The default implementation returns the midpoint of the edge and an alignment that tries to avoid overlapping the label with the edge. @param edge: the edge to be drawn. Visual properties of the edge are defined by the attributes of this object. @param src_vertex: the source vertex. Visual properties are given again as attributes. @param dest_vertex: the target vertex. Visual properties are given again as attributes. @return: a tuple containing two more tuples: the desired position of the label and the desired alignment of the label, where the position is given as C{(x, y)} and the alignment is given as C{(horizontal, vertical)}. Members of the alignment tuple are taken from constants in the L{TextAlignment} class. """ # Determine the angle of the line dx = dest_vertex.position[0] - src_vertex.position[0] dy = dest_vertex.position[1] - src_vertex.position[1] if dx != 0 or dy != 0: # Note that we use -dy because the Y axis points downwards angle = atan2(-dy, dx) % (2*pi) else: angle = None # Determine the midpoint pos = ((src_vertex.position[0] + dest_vertex.position[0]) / 2., \ (src_vertex.position[1] + dest_vertex.position[1]) / 2) # Determine the alignment based on the angle pi4 = pi / 4 if angle is None: halign, valign = TextAlignment.CENTER, TextAlignment.CENTER else: index = int((angle / pi4) % 8) halign = [TextAlignment.RIGHT, TextAlignment.RIGHT, TextAlignment.RIGHT, TextAlignment.RIGHT, TextAlignment.LEFT, TextAlignment.LEFT, TextAlignment.LEFT, TextAlignment.LEFT][index] valign = [TextAlignment.BOTTOM, TextAlignment.CENTER, TextAlignment.CENTER, TextAlignment.TOP, TextAlignment.TOP, TextAlignment.CENTER, TextAlignment.CENTER, TextAlignment.BOTTOM][index] return pos, (halign, valign) class ArrowEdgeDrawer(AbstractEdgeDrawer): """Edge drawer implementation that draws undirected edges as straight lines and directed edges as arrows. """ def draw_directed_edge(self, edge, src_vertex, dest_vertex): if src_vertex == dest_vertex: # TODO return self.draw_loop_edge(edge, src_vertex) ctx = self.context (x1, y1), (x2, y2) = src_vertex.position, dest_vertex.position (x_src, y_src), (x_dest, y_dest) = src_vertex.position, dest_vertex.position def bezier_cubic(x0,y0, x1,y1, x2,y2, x3,y3, t): """ Computes the Bezier curve from point (x0,y0) to (x3,y3) via control points (x1,y1) and (x2,y2) with parameter t. """ xt = (1.0 - t) ** 3 * x0 + 3. *t * (1.0 - t) ** 2 * x1 + 3. * t**2 * (1. - t) * x2 + t**3 * x3 yt = (1.0 - t) ** 3 * y0 + 3. *t * (1.0 - t) ** 2 * y1 + 3. * t**2 * (1. - t) * y2 + t**3 * y3 return xt,yt def euclidean_distance(x1,y1,x2,y2): """ Computes the Euclidean distance between points (x1,y1) and (x2,y2). """ return sqrt( (1.0*x1-x2) **2 + (1.0*y1-y2) **2 ) def intersect_bezier_circle(x0,y0, x1,y1, x2,y2, x3,y3, radius): """ Binary search solver for finding the intersection of a Bezier curve and a circle centered at the curve's end point. Returns the x,y of the intersection point. TODO: implement safeguard to ensure convergence in ALL possible cases. """ precision = radius / 20.0 source_target_distance = euclidean_distance(x0,y0,x3,y3) radius = float(radius) t0 = 1.0 t1 = 1.0 - radius / source_target_distance xt0, yt0 = x3, y3 xt1, yt1 = bezier_cubic(x0,y0, x1,y1, x2,y2, x3,y3, t1) distance_t0 = 0 distance_t1 = euclidean_distance(x3,y3, xt1,yt1) counter = 0 while abs(distance_t1 - radius) > precision: if ((distance_t1-radius) > 0) != ((distance_t0-radius) > 0): t_new = (t0 + t1)/2.0 else: if (abs(distance_t1 - radius) < abs(distance_t0 - radius)): # If t1 gets us closer to the circumference step in the same direction t_new = t1 + (t1 - t0)/ 2.0 else: t_new = t1 - (t1 - t0) t_new = 1 if t_new > 1 else (0 if t_new < 0 else t_new) t0,t1 = t1,t_new distance_t0 = distance_t1 xt1, yt1 = bezier_cubic(x0,y0, x1,y1, x2,y2, x3,y3, t1) distance_t1 = euclidean_distance(x3,y3, xt1,yt1) counter += 1 return bezier_cubic(x0,y0, x1,y1, x2,y2, x3,y3, t1) # Draw the edge ctx.set_source_rgba(*edge.color) ctx.set_line_width(edge.width) ctx.move_to(x1, y1) if edge.curved: # Calculate the curve aux1 = (2*x1+x2) / 3.0 - edge.curved * 0.5 * (y2-y1), \ (2*y1+y2) / 3.0 + edge.curved * 0.5 * (x2-x1) aux2 = (x1+2*x2) / 3.0 - edge.curved * 0.5 * (y2-y1), \ (y1+2*y2) / 3.0 + edge.curved * 0.5 * (x2-x1) # Coordinates of the control points of the Bezier curve xc1, yc1 = aux1 xc2, yc2 = aux2 # Determine where the edge intersects the circumference of the # vertex shape: Tip of the arrow x2, y2 = intersect_bezier_circle(x_src,y_src, xc1,yc1, xc2,yc2, x_dest,y_dest, dest_vertex.size/2.0) # Calculate the arrow head coordinates angle = atan2(y_dest - y2, x_dest - x2) # navid arrow_size = 15. * edge.arrow_size arrow_width = 10. / edge.arrow_width aux_points = [ (x2 - arrow_size * cos(angle - pi/arrow_width), y2 - arrow_size * sin(angle - pi/arrow_width)), (x2 - arrow_size * cos(angle + pi/arrow_width), y2 - arrow_size * sin(angle + pi/arrow_width)), ] # Midpoint of the base of the arrow triangle x_arrow_mid , y_arrow_mid = (aux_points [0][0] + aux_points [1][0]) / 2.0, (aux_points [0][1] + aux_points [1][1]) / 2.0 # Vector representing the base of the arrow triangle x_arrow_base_vec, y_arrow_base_vec = (aux_points [0][0] - aux_points [1][0]) , (aux_points [0][1] - aux_points [1][1]) # Recalculate the curve such that it lands on the base of the arrow triangle aux1 = (2*x_src+x_arrow_mid) / 3.0 - edge.curved * 0.5 * (y_arrow_mid-y_src), \ (2*y_src+y_arrow_mid) / 3.0 + edge.curved * 0.5 * (x_arrow_mid-x_src) aux2 = (x_src+2*x_arrow_mid) / 3.0 - edge.curved * 0.5 * (y_arrow_mid-y_src), \ (y_src+2*y_arrow_mid) / 3.0 + edge.curved * 0.5 * (x_arrow_mid-x_src) # Offset the second control point (aux2) such that it falls precisely on the normal to the arrow base vector # Strictly speaking, offset_length is the offset length divided by the length of the arrow base vector. offset_length = (x_arrow_mid - aux2[0]) * x_arrow_base_vec + (y_arrow_mid - aux2[1]) * y_arrow_base_vec offset_length /= euclidean_distance(0,0, x_arrow_base_vec, y_arrow_base_vec) ** 2 aux2 = aux2[0] + x_arrow_base_vec * offset_length, \ aux2[1] + y_arrow_base_vec * offset_length # Draw tthe curve from the first vertex to the midpoint of the base of the arrow head ctx.curve_to(aux1[0], aux1[1], aux2[0], aux2[1], x_arrow_mid, y_arrow_mid) else: # Determine where the edge intersects the circumference of the # vertex shape. x2, y2 = dest_vertex.shape.intersection_point( x2, y2, x1, y1, dest_vertex.size) # Draw the arrowhead angle = atan2(y_dest - y2, x_dest - x2) arrow_size = 15. * edge.arrow_size arrow_width = 10. / edge.arrow_width aux_points = [ (x2 - arrow_size * cos(angle - pi/arrow_width), y2 - arrow_size * sin(angle - pi/arrow_width)), (x2 - arrow_size * cos(angle + pi/arrow_width), y2 - arrow_size * sin(angle + pi/arrow_width)), ] # Midpoint of the base of the arrow triangle x_arrow_mid , y_arrow_mid = (aux_points [0][0] + aux_points [1][0]) / 2.0, (aux_points [0][1] + aux_points [1][1]) / 2.0 # Draw the line ctx.line_to(x_arrow_mid, y_arrow_mid) # Draw the edge ctx.stroke() # Draw the arrow head ctx.move_to(x2, y2) ctx.line_to(*aux_points[0]) ctx.line_to(*aux_points[1]) ctx.line_to(x2, y2) ctx.fill() class TaperedEdgeDrawer(AbstractEdgeDrawer): """Edge drawer implementation that draws undirected edges as straight lines and directed edges as tapered lines that are wider at the source and narrow at the destination. """ def draw_directed_edge(self, edge, src_vertex, dest_vertex): if src_vertex == dest_vertex: # TODO return self.draw_loop_edge(edge, src_vertex) # Determine where the edge intersects the circumference of the # destination vertex. src_pos, dest_pos = src_vertex.position, dest_vertex.position dest_pos = dest_vertex.shape.intersection_point( dest_pos[0], dest_pos[1], src_pos[0], src_pos[1], dest_vertex.size ) ctx = self.context # Draw the edge ctx.set_source_rgba(*edge.color) ctx.set_line_width(edge.width) angle = atan2(dest_pos[1]-src_pos[1], dest_pos[0]-src_pos[0]) arrow_size = src_vertex.size / 4. aux_points = [ (src_pos[0] + arrow_size * cos(angle + pi/2), src_pos[1] + arrow_size * sin(angle + pi/2)), (src_pos[0] + arrow_size * cos(angle - pi/2), src_pos[1] + arrow_size * sin(angle - pi/2)) ] ctx.move_to(*dest_pos) ctx.line_to(*aux_points[0]) ctx.line_to(*aux_points[1]) ctx.line_to(*dest_pos) ctx.fill() class AlphaVaryingEdgeDrawer(AbstractEdgeDrawer): """Edge drawer implementation that draws undirected edges as straight lines and directed edges by varying the alpha value of the specified edge color between the source and the destination. """ def __init__(self, context, alpha_at_src, alpha_at_dest): super(AlphaVaryingEdgeDrawer, self).__init__(context) self.alpha_at_src = (clamp(float(alpha_at_src), 0., 1.), ) self.alpha_at_dest = (clamp(float(alpha_at_dest), 0., 1.), ) def draw_directed_edge(self, edge, src_vertex, dest_vertex): if src_vertex == dest_vertex: # TODO return self.draw_loop_edge(edge, src_vertex) src_pos, dest_pos = src_vertex.position, dest_vertex.position ctx = self.context # Set up the gradient lg = cairo.LinearGradient(src_pos[0], src_pos[1], dest_pos[0], dest_pos[1]) edge_color = edge.color[:3] + self.alpha_at_src edge_color_end = edge_color[:3] + self.alpha_at_dest lg.add_color_stop_rgba(0, *edge_color) lg.add_color_stop_rgba(1, *edge_color_end) # Draw the edge ctx.set_source(lg) ctx.set_line_width(edge.width) ctx.move_to(*src_pos) ctx.line_to(*dest_pos) ctx.stroke() class LightToDarkEdgeDrawer(AlphaVaryingEdgeDrawer): """Edge drawer implementation that draws undirected edges as straight lines and directed edges by using an alpha value of zero (total transparency) at the source and an alpha value of one (full opacity) at the destination. The alpha value is interpolated in-between. """ def __init__(self, context): super(LightToDarkEdgeDrawer, self).__init__(context, 0.0, 1.0) class DarkToLightEdgeDrawer(AlphaVaryingEdgeDrawer): """Edge drawer implementation that draws undirected edges as straight lines and directed edges by using an alpha value of one (full opacity) at the source and an alpha value of zero (total transparency) at the destination. The alpha value is interpolated in-between. """ def __init__(self, context): super(DarkToLightEdgeDrawer, self).__init__(context, 1.0, 0.0) python-igraph-0.7.1.post6/igraph/drawing/graph.py0000644000076500000240000011615112534342712022360 0ustar ntamasstaff00000000000000""" Drawing routines to draw graphs. This module contains routines to draw graphs on: - Cairo surfaces (L{DefaultGraphDrawer}) - UbiGraph displays (L{UbiGraphDrawer}, see U{http://ubietylab.net/ubigraph}) It also contains routines to send an igraph graph directly to (U{Cytoscape}) using the (U{CytoscapeRPC plugin}), see L{CytoscapeGraphDrawer}. L{CytoscapeGraphDrawer} can also fetch the current network from Cytoscape and convert it to igraph format. """ from collections import defaultdict from itertools import izip from math import atan2, cos, pi, sin, tan from warnings import warn from igraph._igraph import convex_hull, VertexSeq from igraph.compat import property from igraph.configuration import Configuration from igraph.drawing.baseclasses import AbstractDrawer, AbstractCairoDrawer, \ AbstractXMLRPCDrawer from igraph.drawing.colors import color_to_html_format, color_name_to_rgb from igraph.drawing.edge import ArrowEdgeDrawer from igraph.drawing.text import TextAlignment, TextDrawer from igraph.drawing.metamagic import AttributeCollectorBase from igraph.drawing.shapes import PolygonDrawer from igraph.drawing.utils import find_cairo, Point from igraph.drawing.vertex import DefaultVertexDrawer from igraph.layout import Layout __all__ = ["DefaultGraphDrawer", "UbiGraphDrawer", "CytoscapeGraphDrawer"] __license__ = "GPL" cairo = find_cairo() ##################################################################### # pylint: disable-msg=R0903 # R0903: too few public methods class AbstractGraphDrawer(AbstractDrawer): """Abstract class that serves as a base class for anything that draws an igraph.Graph.""" # pylint: disable-msg=W0221 # W0221: argument number differs from overridden method # E1101: Module 'cairo' has no 'foo' member - of course it does :) def draw(self, graph, *args, **kwds): """Abstract method, must be implemented in derived classes.""" raise NotImplementedError("abstract class") def ensure_layout(self, layout, graph = None): """Helper method that ensures that I{layout} is an instance of L{Layout}. If it is not, the method will try to convert it to a L{Layout} according to the following rules: - If I{layout} is a string, it is assumed to be a name of an igraph layout, and it will be passed on to the C{layout} method of the given I{graph} if I{graph} is not C{None}. - If I{layout} is C{None}, the C{layout} method of I{graph} will be invoked with no parameters, which will call the default layout algorithm. - Otherwise, I{layout} will be passed on to the constructor of L{Layout}. This handles lists of lists, lists of tuples and such. If I{layout} is already a L{Layout} instance, it will still be copied and a copy will be returned. This is because graph drawers are allowed to transform the layout for their purposes, and we don't want the transformation to propagate back to the caller. """ if isinstance(layout, Layout): layout = Layout(layout.coords) elif isinstance(layout, str) or layout is None: layout = graph.layout(layout) else: layout = Layout(layout) return layout ##################################################################### class AbstractCairoGraphDrawer(AbstractGraphDrawer, AbstractCairoDrawer): """Abstract base class for graph drawers that draw on a Cairo canvas. """ def __init__(self, context, bbox): """Constructs the graph drawer and associates it to the given Cairo context and the given L{BoundingBox}. @param context: the context on which we will draw @param bbox: the bounding box within which we will draw. Can be anything accepted by the constructor of L{BoundingBox} (i.e., a 2-tuple, a 4-tuple or a L{BoundingBox} object). """ AbstractCairoDrawer.__init__(self, context, bbox) AbstractGraphDrawer.__init__(self) ##################################################################### class DefaultGraphDrawer(AbstractCairoGraphDrawer): """Class implementing the default visualisation of a graph. The default visualisation of a graph draws the nodes on a 2D plane according to a given L{Layout}, then draws a straight or curved edge between nodes connected by edges. This is the visualisation used when one invokes the L{plot()} function on a L{Graph} object. See L{Graph.__plot__()} for the keyword arguments understood by this drawer.""" def __init__(self, context, bbox, \ vertex_drawer_factory = DefaultVertexDrawer, edge_drawer_factory = ArrowEdgeDrawer, label_drawer_factory = TextDrawer): """Constructs the graph drawer and associates it to the given Cairo context and the given L{BoundingBox}. @param context: the context on which we will draw @param bbox: the bounding box within which we will draw. Can be anything accepted by the constructor of L{BoundingBox} (i.e., a 2-tuple, a 4-tuple or a L{BoundingBox} object). @param vertex_drawer_factory: a factory method that returns an L{AbstractCairoVertexDrawer} instance bound to a given Cairo context. The factory method must take three parameters: the Cairo context, the bounding box of the drawing area and the palette to be used for drawing colored vertices. The default vertex drawer is L{DefaultVertexDrawer}. @param edge_drawer_factory: a factory method that returns an L{AbstractEdgeDrawer} instance bound to a given Cairo context. The factory method must take two parameters: the Cairo context and the palette to be used for drawing colored edges. You can use any of the actual L{AbstractEdgeDrawer} implementations here to control the style of edges drawn by igraph. The default edge drawer is L{ArrowEdgeDrawer}. @param label_drawer_factory: a factory method that returns a L{TextDrawer} instance bound to a given Cairo context. The method must take one parameter: the Cairo context. The default label drawer is L{TextDrawer}. """ AbstractCairoGraphDrawer.__init__(self, context, bbox) self.vertex_drawer_factory = vertex_drawer_factory self.edge_drawer_factory = edge_drawer_factory self.label_drawer_factory = label_drawer_factory def _determine_edge_order(self, graph, kwds): """Returns the order in which the edge of the given graph have to be drawn, assuming that the relevant keyword arguments (C{edge_order} and C{edge_order_by}) are given in C{kwds} as a dictionary. If neither C{edge_order} nor C{edge_order_by} is present in C{kwds}, this function returns C{None} to indicate that the graph drawer is free to choose the most convenient edge ordering.""" if "edge_order" in kwds: # Edge order specified explicitly return kwds["edge_order"] if kwds.get("edge_order_by") is None: # No edge order specified return None # Order edges by the value of some attribute edge_order_by = kwds["edge_order_by"] reverse = False if isinstance(edge_order_by, tuple): edge_order_by, reverse = edge_order_by if isinstance(reverse, basestring): reverse = reverse.lower().startswith("desc") attrs = graph.es[edge_order_by] edge_order = sorted(range(len(attrs)), key=attrs.__getitem__, reverse=bool(reverse)) return edge_order def _determine_vertex_order(self, graph, kwds): """Returns the order in which the vertices of the given graph have to be drawn, assuming that the relevant keyword arguments (C{vertex_order} and C{vertex_order_by}) are given in C{kwds} as a dictionary. If neither C{vertex_order} nor C{vertex_order_by} is present in C{kwds}, this function returns C{None} to indicate that the graph drawer is free to choose the most convenient vertex ordering.""" if "vertex_order" in kwds: # Vertex order specified explicitly return kwds["vertex_order"] if kwds.get("vertex_order_by") is None: # No vertex order specified return None # Order vertices by the value of some attribute vertex_order_by = kwds["vertex_order_by"] reverse = False if isinstance(vertex_order_by, tuple): vertex_order_by, reverse = vertex_order_by if isinstance(reverse, basestring): reverse = reverse.lower().startswith("desc") attrs = graph.vs[vertex_order_by] vertex_order = sorted(range(len(attrs)), key=attrs.__getitem__, reverse=bool(reverse)) return vertex_order # pylint: disable-msg=W0142,W0221,E1101 # W0142: Used * or ** magic # W0221: argument number differs from overridden method # E1101: Module 'cairo' has no 'foo' member - of course it does :) def draw(self, graph, palette, *args, **kwds): # Some abbreviations for sake of simplicity directed = graph.is_directed() context = self.context # Calculate/get the layout of the graph layout = self.ensure_layout(kwds.get("layout", None), graph) # Determine the size of the margin on each side margin = kwds.get("margin", 0) try: margin = list(margin) except TypeError: margin = [margin] while len(margin)<4: margin.extend(margin) # Contract the drawing area by the margin and fit the layout bbox = self.bbox.contract(margin) layout.fit_into(bbox, keep_aspect_ratio=kwds.get("keep_aspect_ratio", False)) # Decide whether we need to calculate the curvature of edges # automatically -- and calculate them if needed. autocurve = kwds.get("autocurve", None) if autocurve or (autocurve is None and \ "edge_curved" not in kwds and "curved" not in graph.edge_attributes() \ and graph.ecount() < 10000): from igraph import autocurve default = kwds.get("edge_curved", 0) if default is True: default = 0.5 default = float(default) kwds["edge_curved"] = autocurve(graph, attribute=None, default=default) # Construct the vertex, edge and label drawers vertex_drawer = self.vertex_drawer_factory(context, bbox, palette, layout) edge_drawer = self.edge_drawer_factory(context, palette) label_drawer = self.label_drawer_factory(context) # Construct the visual vertex/edge builders based on the specifications # provided by the vertex_drawer and the edge_drawer vertex_builder = vertex_drawer.VisualVertexBuilder(graph.vs, kwds) edge_builder = edge_drawer.VisualEdgeBuilder(graph.es, kwds) # Determine the order in which we will draw the vertices and edges vertex_order = self._determine_vertex_order(graph, kwds) edge_order = self._determine_edge_order(graph, kwds) # Draw the highlighted groups (if any) if "mark_groups" in kwds: mark_groups = kwds["mark_groups"] # Figure out what to do with mark_groups in order to be able to # iterate over it and get memberlist-color pairs if isinstance(mark_groups, dict): group_iter = mark_groups.iteritems() elif hasattr(mark_groups, "__iter__"): # Lists, tuples, iterators etc group_iter = iter(mark_groups) else: # False group_iter = {}.iteritems() # We will need a polygon drawer to draw the convex hulls polygon_drawer = PolygonDrawer(context, bbox) # Iterate over color-memberlist pairs for group, color_id in group_iter: if not group or color_id is None: continue color = palette.get(color_id) if isinstance(group, VertexSeq): group = [vertex.index for vertex in group] if not hasattr(group, "__iter__"): raise TypeError("group membership list must be iterable") # Get the vertex indices that constitute the convex hull hull = [group[i] for i in convex_hull([layout[idx] for idx in group])] # Calculate the preferred rounding radius for the corners corner_radius = 1.25 * max(vertex_builder[idx].size for idx in hull) # Construct the polygon polygon = [layout[idx] for idx in hull] if len(polygon) == 2: # Expand the polygon (which is a flat line otherwise) a, b = Point(*polygon[0]), Point(*polygon[1]) c = corner_radius * (a-b).normalized() n = Point(-c[1], c[0]) polygon = [a + n, b + n, b - c, b - n, a - n, a + c] else: # Expand the polygon around its center of mass center = Point(*[sum(coords) / float(len(coords)) for coords in zip(*polygon)]) polygon = [Point(*point).towards(center, -corner_radius) for point in polygon] # Draw the hull context.set_source_rgba(color[0], color[1], color[2], color[3]*0.25) polygon_drawer.draw_path(polygon, corner_radius=corner_radius) context.fill_preserve() context.set_source_rgba(*color) context.stroke() # Construct the iterator that we will use to draw the edges es = graph.es if edge_order is None: # Default edge order edge_coord_iter = izip(es, edge_builder) else: # Specified edge order edge_coord_iter = ((es[i], edge_builder[i]) for i in edge_order) # Draw the edges if directed: drawer_method = edge_drawer.draw_directed_edge else: drawer_method = edge_drawer.draw_undirected_edge for edge, visual_edge in edge_coord_iter: src, dest = edge.tuple src_vertex, dest_vertex = vertex_builder[src], vertex_builder[dest] drawer_method(visual_edge, src_vertex, dest_vertex) # Construct the iterator that we will use to draw the vertices vs = graph.vs if vertex_order is None: # Default vertex order vertex_coord_iter = izip(vs, vertex_builder, layout) else: # Specified vertex order vertex_coord_iter = ((vs[i], vertex_builder[i], layout[i]) for i in vertex_order) # Draw the vertices drawer_method = vertex_drawer.draw context.set_line_width(1) for vertex, visual_vertex, coords in vertex_coord_iter: drawer_method(visual_vertex, vertex, coords) # Set the font we will use to draw the labels context.select_font_face("sans-serif", cairo.FONT_SLANT_NORMAL, \ cairo.FONT_WEIGHT_NORMAL) # Decide whether the labels have to be wrapped wrap = kwds.get("wrap_labels") if wrap is None: wrap = Configuration.instance()["plotting.wrap_labels"] wrap = bool(wrap) # Construct the iterator that we will use to draw the vertex labels if vertex_order is None: # Default vertex order vertex_coord_iter = izip(vertex_builder, layout) else: # Specified vertex order vertex_coord_iter = ((vertex_builder[i], layout[i]) for i in vertex_order) # Draw the vertex labels for vertex, coords in vertex_coord_iter: if vertex.label is None: continue context.set_font_size(vertex.label_size) context.set_source_rgba(*vertex.label_color) label_drawer.text = vertex.label if vertex.label_dist: # Label is displaced from the center of the vertex. _, yb, w, h, _, _ = label_drawer.text_extents() w, h = w/2.0, h/2.0 radius = vertex.label_dist * vertex.size / 2. # First we find the reference point that is at distance `radius' # from the vertex in the direction given by `label_angle'. # Then we place the label in a way that the line connecting the # center of the bounding box of the label with the center of the # vertex goes through the reference point and the reference # point lies exactly on the bounding box of the vertex. alpha = vertex.label_angle % (2*pi) cx = coords[0] + radius * cos(alpha) cy = coords[1] - radius * sin(alpha) # Now we have the reference point. We have to decide which side # of the label box will intersect with the line that connects # the center of the label with the center of the vertex. if w > 0: beta = atan2(h, w) % (2*pi) else: beta = pi/2. gamma = pi - beta if alpha > 2*pi-beta or alpha <= beta: # Intersection at left edge of label cx += w cy -= tan(alpha) * w elif alpha > beta and alpha <= gamma: # Intersection at bottom edge of label try: cx += h / tan(alpha) except: pass # tan(alpha) == inf cy -= h elif alpha > gamma and alpha <= gamma + 2*beta: # Intersection at right edge of label cx -= w cy += tan(alpha) * w else: # Intersection at top edge of label try: cx -= h / tan(alpha) except: pass # tan(alpha) == inf cy += h # Draw the label label_drawer.draw_at(cx-w, cy-h-yb, wrap=wrap) else: # Label is exactly in the center of the vertex cx, cy = coords half_size = vertex.size / 2. label_drawer.bbox = (cx - half_size, cy - half_size, cx + half_size, cy + half_size) label_drawer.draw(wrap=wrap) # Construct the iterator that we will use to draw the edge labels es = graph.es if edge_order is None: # Default edge order edge_coord_iter = izip(es, edge_builder) else: # Specified edge order edge_coord_iter = ((es[i], edge_builder[i]) for i in edge_order) # Draw the edge labels for edge, visual_edge in edge_coord_iter: if visual_edge.label is None: continue # Set the font size, color and text context.set_font_size(visual_edge.label_size) context.set_source_rgba(*visual_edge.label_color) label_drawer.text = visual_edge.label # Ask the edge drawer to propose an anchor point for the label src, dest = edge.tuple src_vertex, dest_vertex = vertex_builder[src], vertex_builder[dest] (x, y), (halign, valign) = \ edge_drawer.get_label_position(edge, src_vertex, dest_vertex) # Measure the text _, yb, w, h, _, _ = label_drawer.text_extents() w /= 2.0 h /= 2.0 # Place the text relative to the edge if halign == TextAlignment.RIGHT: x -= w elif halign == TextAlignment.LEFT: x += w if valign == TextAlignment.BOTTOM: y -= h - yb / 2.0 elif valign == TextAlignment.TOP: y += h # Draw the edge label label_drawer.halign = halign label_drawer.valign = valign label_drawer.bbox = (x-w, y-h, x+w, y+h) label_drawer.draw(wrap=wrap) ##################################################################### class UbiGraphDrawer(AbstractXMLRPCDrawer, AbstractGraphDrawer): """Graph drawer that draws a given graph on an UbiGraph display using the XML-RPC API of UbiGraph. The following vertex attributes are supported: C{color}, C{label}, C{shape}, C{size}. See the Ubigraph documentation for supported shape names. Sizes are relative to the default Ubigraph size. The following edge attributes are supported: C{color}, C{label}, C{width}. Edge widths are relative to the default Ubigraph width. All color specifications supported by igraph (e.g., color names, palette indices, RGB triplets, RGBA quadruplets, HTML format) are understood by the Ubigraph graph drawer. The drawer also has two attributes, C{vertex_defaults} and C{edge_defaults}. These are dictionaries that can be used to set default values for the vertex/edge attributes in Ubigraph. """ def __init__(self, url="http://localhost:20738/RPC2"): """Constructs an UbiGraph drawer using the display at the given URL.""" super(UbiGraphDrawer, self).__init__(url, "ubigraph") self.vertex_defaults = dict( color="#ff0000", shape="cube", size=1.0 ) self.edge_defaults = dict( color="#ffffff", width=1.0 ) def draw(self, graph, *args, **kwds): """Draws the given graph on an UbiGraph display. @keyword clear: whether to clear the current UbiGraph display before plotting. Default: C{True}.""" display = self.service # Clear the display and set the default visual attributes if kwds.get("clear", True): display.clear() for k, v in self.vertex_defaults.iteritems(): display.set_vertex_style_attribute(0, k, str(v)) for k, v in self.edge_defaults.iteritems(): display.set_edge_style_attribute(0, k, str(v)) # Custom color converter function def color_conv(color): return color_to_html_format(color_name_to_rgb(color)) # Construct the visual vertex/edge builders class VisualVertexBuilder(AttributeCollectorBase): """Collects some visual properties of a vertex for drawing""" _kwds_prefix = "vertex_" color = (str(self.vertex_defaults["color"]), color_conv) label = None shape = str(self.vertex_defaults["shape"]) size = float(self.vertex_defaults["size"]) class VisualEdgeBuilder(AttributeCollectorBase): """Collects some visual properties of an edge for drawing""" _kwds_prefix = "edge_" color = (str(self.edge_defaults["color"]), color_conv) label = None width = float(self.edge_defaults["width"]) vertex_builder = VisualVertexBuilder(graph.vs, kwds) edge_builder = VisualEdgeBuilder(graph.es, kwds) # Add the vertices n = graph.vcount() new_vertex = display.new_vertex vertex_ids = [new_vertex() for _ in xrange(n)] # Add the edges new_edge = display.new_edge eids = [new_edge(vertex_ids[edge.source], vertex_ids[edge.target]) \ for edge in graph.es] # Add arrowheads if needed if graph.is_directed(): display.set_edge_style_attribute(0, "arrow", "true") # Set the vertex attributes set_attr = display.set_vertex_attribute vertex_defaults = self.vertex_defaults for vertex_id, vertex in izip(vertex_ids, vertex_builder): if vertex.color != vertex_defaults["color"]: set_attr(vertex_id, "color", vertex.color) if vertex.label: set_attr(vertex_id, "label", str(vertex.label)) if vertex.shape != vertex_defaults["shape"]: set_attr(vertex_id, "shape", vertex.shape) if vertex.size != vertex_defaults["size"]: set_attr(vertex_id, "size", str(vertex.size)) # Set the edge attributes set_attr = display.set_edge_attribute edge_defaults = self.edge_defaults for edge_id, edge in izip(eids, edge_builder): if edge.color != edge_defaults["color"]: set_attr(edge_id, "color", edge.color) if edge.label: set_attr(edge_id, "label", edge.label) if edge.width != edge_defaults["width"]: set_attr(edge_id, "width", str(edge.width)) ##################################################################### class CytoscapeGraphDrawer(AbstractXMLRPCDrawer, AbstractGraphDrawer): """Graph drawer that sends/receives graphs to/from Cytoscape using CytoscapeRPC. This graph drawer cooperates with U{Cytoscape} using U{CytoscapeRPC}. You need to install the CytoscapeRPC plugin first and start the XML-RPC server on a given port (port 9000 by default) from the appropriate Plugins submenu in Cytoscape. Graph, vertex and edge attributes are transferred to Cytoscape whenever possible (i.e. when a suitable mapping exists between a Python type and a Cytoscape type). If there is no suitable Cytoscape type for a Python type, the drawer will use a string attribute on the Cytoscape side and invoke C{str()} on the Python attributes. If an attribute to be created on the Cytoscape side already exists with a different type, an underscore will be appended to the attribute name to resolve the type conflict. You can use the C{network_id} attribute of this class to figure out the network ID of the last graph drawn with this drawer. """ def __init__(self, url="http://localhost:9000/Cytoscape"): """Constructs a Cytoscape graph drawer using the XML-RPC interface of Cytoscape at the given URL.""" super(CytoscapeGraphDrawer, self).__init__(url, "Cytoscape") self.network_id = None def draw(self, graph, name="Network from igraph", create_view=True, *args, **kwds): """Sends the given graph to Cytoscape as a new network. @param name: the name of the network in Cytoscape. @param create_view: whether to create a view for the network in Cytoscape.The default is C{True}. @keyword node_ids: specifies the identifiers of the nodes to be used in Cytoscape. This must either be the name of a vertex attribute or a list specifying the identifiers, one for each node in the graph. The default is C{None}, which simply uses the vertex index for each vertex.""" from xmlrpclib import Fault cy = self.service # Create the network if not create_view: try: network_id = cy.createNetwork(name, False) except Fault: warn("CytoscapeRPC too old, cannot create network without view." " Consider upgrading CytoscapeRPC to use this feature.") network_id = cy.createNetwork(name) else: network_id = cy.createNetwork(name) self.network_id = network_id # Create the nodes if "node_ids" in kwds: node_ids = kwds["node_ids"] if isinstance(node_ids, basestring): node_ids = graph.vs[node_ids] else: node_ids = xrange(graph.vcount()) node_ids = [str(identifier) for identifier in node_ids] cy.createNodes(network_id, node_ids) # Create the edges edgelists = [[], []] for v1, v2 in graph.get_edgelist(): edgelists[0].append(node_ids[v1]) edgelists[1].append(node_ids[v2]) edge_ids = cy.createEdges(network_id, edgelists[0], edgelists[1], ["unknown"] * graph.ecount(), [graph.is_directed()] * graph.ecount(), False ) if "layout" in kwds: # Calculate/get the layout of the graph layout = self.ensure_layout(kwds["layout"], graph) size = 100 * graph.vcount() ** 0.5 layout.fit_into((size, size), keep_aspect_ratio=True) layout.translate(-size/2., -size/2.) cy.setNodesPositions(network_id, node_ids, *zip(*list(layout))) else: # Ask Cytoscape to perform the default layout so the user can # at least see something in Cytoscape while the attributes are # being transferred cy.performDefaultLayout(network_id) # Send the network attributes attr_names = set(cy.getNetworkAttributeNames()) for attr in graph.attributes(): cy_type, value = self.infer_cytoscape_type([graph[attr]]) value = value[0] if value is None: continue # Resolve type conflicts (if any) try: while attr in attr_names and \ cy.getNetworkAttributeType(attr) != cy_type: attr += "_" except Fault: # getNetworkAttributeType is not available in some older versions # so we simply pass here pass cy.addNetworkAttributes(attr, cy_type, {network_id: value}) # Send the node attributes attr_names = set(cy.getNodeAttributeNames()) for attr in graph.vertex_attributes(): cy_type, values = self.infer_cytoscape_type(graph.vs[attr]) values = dict(pair for pair in izip(node_ids, values) if pair[1] is not None) # Resolve type conflicts (if any) while attr in attr_names and \ cy.getNodeAttributeType(attr) != cy_type: attr += "_" # Send the attribute values cy.addNodeAttributes(attr, cy_type, values, True) # Send the edge attributes attr_names = set(cy.getEdgeAttributeNames()) for attr in graph.edge_attributes(): cy_type, values = self.infer_cytoscape_type(graph.es[attr]) values = dict(pair for pair in izip(edge_ids, values) if pair[1] is not None) # Resolve type conflicts (if any) while attr in attr_names and \ cy.getEdgeAttributeType(attr) != cy_type: attr += "_" # Send the attribute values cy.addEdgeAttributes(attr, cy_type, values) def fetch(self, name = None, directed = False, keep_canonical_names = False): """Fetches the network with the given name from Cytoscape. When fetching networks from Cytoscape, the C{canonicalName} attributes of vertices and edges are not converted by default. Use the C{keep_canonical_names} parameter to retrieve these attributes as well. @param name: the name of the network in Cytoscape. @param directed: whether the network is directed. @param keep_canonical_names: whether to keep the C{canonicalName} vertex/edge attributes that are added automatically by Cytoscape @return: an appropriately constructed igraph L{Graph}.""" from igraph import Graph cy = self.service # Check the version number. Anything older than 1.3 is bad. version = cy.version() if " " in version: version = version.split(" ")[0] version = tuple(map(int, version.split(".")[:2])) if version < (1, 3): raise NotImplementedError("CytoscapeGraphDrawer requires " "Cytoscape-RPC 1.3 or newer") # Find out the ID of the network we are interested in if name is None: network_id = cy.getNetworkID() else: network_id = [k for k, v in cy.getNetworkList().iteritems() if v == name] if not network_id: raise ValueError("no such network: %r" % name) elif len(network_id) > 1: raise ValueError("more than one network exists with name: %r" % name) network_id = network_id[0] # Fetch the list of all the nodes and edges vertices = cy.getNodes(network_id) edges = cy.getEdges(network_id) n, m = len(vertices), len(edges) # Fetch the graph attributes graph_attrs = cy.getNetworkAttributes(network_id) # Fetch the vertex attributes vertex_attr_names = cy.getNodeAttributeNames() vertex_attrs = {} for attr_name in vertex_attr_names: if attr_name == "canonicalName" and not keep_canonical_names: continue has_attr = cy.nodesHaveAttribute(attr_name, vertices) filtered = [idx for idx, ok in enumerate(has_attr) if ok] values = cy.getNodesAttributes(attr_name, [name for name, ok in izip(vertices, has_attr) if ok] ) attrs = [None] * n for idx, value in izip(filtered, values): attrs[idx] = value vertex_attrs[attr_name] = attrs # Fetch the edge attributes edge_attr_names = cy.getEdgeAttributeNames() edge_attrs = {} for attr_name in edge_attr_names: if attr_name == "canonicalName" and not keep_canonical_names: continue has_attr = cy.edgesHaveAttribute(attr_name, edges) filtered = [idx for idx, ok in enumerate(has_attr) if ok] values = cy.getEdgesAttributes(attr_name, [name for name, ok in izip(edges, has_attr) if ok] ) attrs = [None] * m for idx, value in izip(filtered, values): attrs[idx] = value edge_attrs[attr_name] = attrs # Create a vertex name index vertex_name_index = dict((v, k) for k, v in enumerate(vertices)) del vertices # Remap the edges list to numeric IDs edge_list = [] for edge in edges: parts = edge.split() edge_list.append((vertex_name_index[parts[0]], vertex_name_index[parts[2]])) del edges return Graph(n, edge_list, directed=directed, graph_attrs=graph_attrs, vertex_attrs=vertex_attrs, edge_attrs=edge_attrs) @staticmethod def infer_cytoscape_type(values): """Returns a Cytoscape type that can be used to represent all the values in `values` and an appropriately converted copy of `values` that is suitable for an XML-RPC call. Note that the string type in Cytoscape is used as a catch-all type; if no other type fits, attribute values will be converted to string and then posted to Cytoscape. ``None`` entries are allowed in `values`, they will be ignored on the Cytoscape side. """ types = [type(value) for value in values if value is not None] if all(t == bool for t in types): return "BOOLEAN", values if all(issubclass(t, (int, long)) for t in types): return "INTEGER", values if all(issubclass(t, float) for t in types): return "FLOATING", values return "STRING", [ str(value) if not isinstance(value, basestring) else value for value in values ] ##################################################################### class GephiGraphStreamingDrawer(AbstractGraphDrawer): """Graph drawer that sends a graph to a file-like object (e.g., socket, URL connection, file) using the Gephi graph streaming format. The Gephi graph streaming format is a simple JSON-based format that can be used to post mutations to a graph (i.e. node and edge additions, removals and updates) to a remote component. For instance, one can open up Gephi (U{http://www.gephi.org}), install the Gephi graph streaming plugin and then send a graph from igraph straight into the Gephi window by using C{GephiGraphStreamingDrawer} with the appropriate URL where Gephi is listening. The C{connection} property exposes the L{GephiConnection} that the drawer uses. The drawer also has a property called C{streamer} which exposes the underlying L{GephiGraphStreamer} that is responsible for generating the JSON objects, encoding them and writing them to a file-like object. If you want to customize the encoding process, this is the object where you can tweak things to your taste. """ def __init__(self, conn=None, *args, **kwds): """Constructs a Gephi graph streaming drawer that will post graphs to the given Gephi connection. If C{conn} is C{None}, the remaining arguments of the constructor are forwarded intact to the constructor of L{GephiConnection} in order to create a connection. This means that any of the following are valid: - C{GephiGraphStreamingDrawer()} will construct a drawer that connects to workspace 0 of the local Gephi instance on port 8080. - C{GephiGraphStreamingDrawer(workspace=2)} will connect to workspace 2 of the local Gephi instance on port 8080. - C{GephiGraphStreamingDrawer(port=1234)} will connect to workspace 0 of the local Gephi instance on port 1234. - C{GephiGraphStreamingDrawer(host="remote", port=1234, workspace=7)} will connect to workspace 7 of the Gephi instance on host C{remote}, port 1234. - C{GephiGraphStreamingDrawer(url="http://remote:1234/workspace7)} is the same as above, but with an explicit URL. """ super(GephiGraphStreamingDrawer, self).__init__() from igraph.remote.gephi import GephiGraphStreamer, GephiConnection self.connection = conn or GephiConnection(*args, **kwds) self.streamer = GephiGraphStreamer() def draw(self, graph, *args, **kwds): """Draws (i.e. sends) the given graph to the destination of the drawer using the Gephi graph streaming API. The following keyword arguments are allowed: - ``encoder`` lets one specify an instance of ``json.JSONEncoder`` that will be used to encode the JSON objects. """ self.streamer.post(graph, self.connection, encoder=kwds.get("encoder")) python-igraph-0.7.1.post6/igraph/drawing/metamagic.py0000644000076500000240000003370212453614202023202 0ustar ntamasstaff00000000000000"""Auxiliary classes for the default graph drawer in igraph. This module contains heavy metaclass magic. If you don't understand the logic behind these classes, probably you don't need them either. igraph's default graph drawer uses various data sources to determine the visual appearance of vertices and edges. These data sources are the following (in order of precedence): - The keyword arguments passed to the L{igraph.plot()} function (or to L{igraph.Graph.__plot__()} as a matter of fact, since L{igraph.plot()} just passes these attributes on). For instance, a keyword argument named C{vertex_label} can be used to set the labels of vertices. - The attributes of the vertices/edges being drawn. For instance, a vertex that has a C{label} attribute will use that label when drawn by the default graph drawer. - The global configuration of igraph. For instance, if the global L{igraph.config.Configuration} instance has a key called C{plotting.vertex_color}, that will be used as a default color for the vertices. - If all else fails, there is a built-in default; for instance, the default vertex color is C{"red"}. This is hard-wired in the source code. The logic above can be useful in other graph drawers as well, not only in the default one, therefore it is refactored into the classes found in this module. Different graph drawers may inspect different vertex or edge attributes, hence the classes that collect the attributes from the various data sources are generated in run-time using a metaclass called L{AttributeCollectorMeta}. You don't have to use L{AttributeCollectorMeta} directly, just implement a subclass of L{AttributeCollectorBase} and it will ensure that the appropriate metaclass is used. With L{AttributeCollectorBase}, you can use a simple declarative syntax to specify which attributes you are interested in. For example:: class VisualEdgeBuilder(AttributeCollectorBase): arrow_size = 1.0 arrow_width = 1.0 color = ("black", palette.get) width = 1.0 for edge in VisualEdgeBuilder(graph.es): print edge.color The above class is a visual edge builder -- a class that gives the visual attributes of the edges of a graph that is specified at construction time. It specifies that the attributes we are interested in are C{arrow_size}, C{arrow_width}, C{color} and C{width}; the default values are also given. For C{color}, we also specify that a method called {palette.get} should be called on every attribute value to translate color names to RGB values. For the other three attributes, C{float} will implicitly be called on all attribute values, this is inferred from the type of the default value itself. @see: AttributeCollectorMeta, AttributeCollectorBase """ from ConfigParser import NoOptionError from itertools import izip from igraph.configuration import Configuration __all__ = ["AttributeSpecification", "AttributeCollectorBase"] # pylint: disable-msg=R0903 # R0903: too few public methods class AttributeSpecification(object): """Class that describes how the value of a given attribute should be retrieved. The class contains the following members: - C{name}: the name of the attribute. This is also used when we are trying to get its value from a vertex/edge attribute of a graph. - C{alt_name}: alternative name of the attribute. This is used when we are trying to get its value from a Python dict or an L{igraph.Configuration} object. If omitted at construction time, it will be equal to C{name}. - C{default}: the default value of the attribute when none of the sources we try can provide a meaningful value. - C{transform}: optional transformation to be performed on the attribute value. If C{None} or omitted, it defaults to the type of the default value. - C{func}: when given, this function will be called with an index in order to derive the value of the attribute. """ __slots__ = ("name", "alt_name", "default", "transform", "accessor", "func") def __init__(self, name, default=None, alt_name=None, transform=None, func=None): if isinstance(default, tuple): default, transform = default self.name = name self.default = default self.alt_name = alt_name or name self.transform = transform or None self.func = func self.accessor = None if self.transform and not hasattr(self.transform, "__call__"): raise TypeError, "transform must be callable" if self.transform is None and self.default is not None: self.transform = type(self.default) class AttributeCollectorMeta(type): """Metaclass for attribute collector classes Classes that use this metaclass are intended to collect vertex/edge attributes from various sources (a Python dict, a vertex/edge sequence, default values from the igraph configuration and such) in a given order of precedence. See the module documentation for more details. This metaclass enables the user to use a simple declarative syntax to specify which attributes he is interested in. For each vertex/edge attribute, a corresponding class attribute must be defined with a value that describes the default value of that attribute if no other data source provides us with any suitable value. The default value can also be a tuple; in that case, the first element of the tuple is the actual default value, the second element is a converter function that will convert the attribute values to a format expected by the caller who uses the class being defined. There is a special class attribute called C{_kwds_prefix}; this is not used as an attribute declaration. It can contain a string which will be used to derive alternative names for the attributes when the attribute is accessed in a Python dict. This is useful in many situations; for instance, the default graph drawer would want to access the vertex colors using the C{color} vertex attribute, but when it looks at the keyword arguments passed to the original call of L{igraph.Graph.__plot__}, the C{vertex_color} keyword argument should be looked up because we also have colors for the edges. C{_kwds_prefix} will be prepended to the attribute names when they are looked up in a dict of keyword arguments. If you require a more fine-tuned behaviour, you can assign an L{AttributeSpecification} instance to a class attribute directly. @see: AttributeCollectorBase """ def __new__(mcs, name, bases, attrs): attr_specs = [] for attr, value in attrs.iteritems(): if attr.startswith("_") or hasattr(value, "__call__"): continue if isinstance(value, AttributeSpecification): attr_spec = value elif isinstance(value, dict): attr_spec = AttributeSpecification(attr, **value) else: attr_spec = AttributeSpecification(attr, value) attr_specs.append(attr_spec) prefix = attrs.get("_kwds_prefix", None) if prefix: for attr_spec in attr_specs: if attr_spec.name == attr_spec.alt_name: attr_spec.alt_name = "%s%s" % (prefix, attr_spec.name) attrs["_attributes"] = attr_specs attrs["Element"] = mcs.record_generator( "%s.Element" % name, (attr_spec.name for attr_spec in attr_specs) ) return super(AttributeCollectorMeta, mcs).__new__(mcs, \ name, bases, attrs) @classmethod def record_generator(mcs, name, slots): """Generates a simple class that has the given slots and nothing else""" class Element(object): """A simple class that holds the attributes collected by the attribute collector""" __slots__ = tuple(slots) def __init__(self, attrs=()): for attr, value in attrs: setattr(self, attr, value) Element.__name__ = name return Element class AttributeCollectorBase(object): """Base class for attribute collector subclasses. Classes that inherit this class may use a declarative syntax to specify which vertex or edge attributes they intend to collect. See L{AttributeCollectorMeta} for the details. """ __metaclass__ = AttributeCollectorMeta def __init__(self, seq, kwds = None): """Constructs a new attribute collector that uses the given vertex/edge sequence and the given dict as data sources. @param seq: an L{igraph.VertexSeq} or L{igraph.EdgeSeq} class that will be used as a data source for attributes. @param kwds: a Python dict that will be used to override the attributes collected from I{seq} if necessary. """ elt = self.__class__.Element self._cache = [elt() for _ in xrange(len(seq))] self.seq = seq self.kwds = kwds or {} for attr_spec in self._attributes: values = self._collect_attributes(attr_spec) attr_name = attr_spec.name for cache_elt, val in izip(self._cache, values): setattr(cache_elt, attr_name, val) def _collect_attributes(self, attr_spec, config=None): """Collects graph visualization attributes from various sources. This method can be used to collect the attributes required for graph visualization from various sources. Attribute value sources are: - A specific value of a Python dict belonging to a given key. This dict is given by the argument M{self.kwds} at construction time, and the name of the key is determined by the argument specification given in M{attr_spec}. - A vertex or edge sequence of a graph, given in M{self.seq} - The global configuration, given in M{config} - A default value when all other sources fail to provide the value. This is also given in M{attr_spec}. @param attr_spec: an L{AttributeSpecification} object which contains the name of the attribute when it is coming from a list of Python keyword arguments, the name of the attribute when it is coming from the graph attributes directly, the default value of the attribute and an optional callable transformation to call on the values. This can be used to ensure that the attributes are of a given type. @param config: a L{Configuration} object to be used for determining the defaults if all else fails. If C{None}, the global igraph configuration will be used @return: the collected attributes """ kwds = self.kwds seq = self.seq n = len(seq) # Special case if the attribute name is "label" if attr_spec.name == "label": if attr_spec.alt_name in kwds and kwds[attr_spec.alt_name] is None: return [None] * n # If the attribute uses an external callable to derive the attribute # values, call it and store the results if attr_spec.func is not None: func = attr_spec.func result = [func(i) for i in xrange(n)] return result # Get the configuration object if config is None: config = Configuration.instance() # Fetch the defaults from the vertex/edge sequence try: attrs = seq[attr_spec.name] except KeyError: attrs = None # Override them from the keyword arguments (if any) result = kwds.get(attr_spec.alt_name, None) if attrs: if not result: result = attrs else: if isinstance(result, str): result = [result] * n try: len(result) except TypeError: result = [result] * n result = [result[idx] or attrs[idx] \ for idx in xrange(len(result))] # Special case for string overrides, strings are not treated # as sequences here if isinstance(result, str): result = [result] * n # If the result is still not a sequence, make it one try: len(result) except TypeError: result = [result] * n # If it is not a list, ensure that it is a list if not hasattr(result, "extend"): result = list(result) # Ensure that the length is n while len(result) < n: if len(result) <= n/2: result.extend(result) else: result.extend(result[0:(n-len(result))]) # By now, the length of the result vector should be n as requested # Get the configuration defaults try: default = config["plotting.%s" % attr_spec.alt_name] except NoOptionError: default = None if default is None: default = attr_spec.default # Fill the None values with the default values for idx in xrange(len(result)): if result[idx] is None: result[idx] = default # Finally, do the transformation if attr_spec.transform is not None: transform = attr_spec.transform result = [transform(x) for x in result] return result def __getitem__(self, index): """Returns the collected attributes of the vertex/edge with the given index.""" # pylint: disable-msg=E1101 # E1101: instance has no '_attributes' member return self._cache[index] def __len__(self): return len(self.seq) python-igraph-0.7.1.post6/igraph/drawing/shapes.py0000644000076500000240000004157312460534506022551 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """ Shape drawing classes for igraph Vertex shapes in igraph are usually referred to by short names like C{"rect"} or C{"circle"}. This module contains the classes that implement the actual drawing routines for these shapes, and a resolver class that determines the appropriate shape drawer class given the short name. Classes that are derived from L{ShapeDrawer} in this module are automatically registered by L{ShapeDrawerDirectory}. If you implement a custom shape drawer, you must register it in L{ShapeDrawerDirectory} manually if you wish to refer to it by a name in the C{shape} attribute of vertices. """ from __future__ import division __all__ = ["ShapeDrawerDirectory"] __license__ = u"""\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ from math import atan2, copysign, cos, pi, sin import sys from igraph.drawing.baseclasses import AbstractCairoDrawer from igraph.drawing.utils import Point from igraph.utils import consecutive_pairs class ShapeDrawer(object): """Static class, the ancestor of all vertex shape drawer classes. Custom shapes must implement at least the C{draw_path} method of the class. The method I{must not} stroke or fill, it should just set up the current Cairo path appropriately.""" @staticmethod def draw_path(ctx, center_x, center_y, width, height=None): """Draws the path of the shape on the given Cairo context, without stroking or filling it. This method must be overridden in derived classes implementing custom shapes and declared as a static method using C{staticmethod(...)}. @param ctx: the context to draw on @param center_x: the X coordinate of the center of the object @param center_y: the Y coordinate of the center of the object @param width: the width of the object @param height: the height of the object. If C{None}, equals to the width. """ raise NotImplementedError("abstract class") # pylint: disable-msg=W0613 @staticmethod def intersection_point(center_x, center_y, source_x, source_y, \ width, height=None): """Determines where the shape centered at (center_x, center_y) intersects with a line drawn from (source_x, source_y) to (center_x, center_y). Can be overridden in derived classes. Must always be defined as a static method using C{staticmethod(...)} @param width: the width of the shape @param height: the height of the shape. If C{None}, defaults to the width @return: the intersection point (the closest to (source_x, source_y) if there are more than one) or (center_x, center_y) if there is no intersection """ return center_x, center_y class NullDrawer(ShapeDrawer): """Static drawer class which draws nothing. This class is used for graph vertices with unknown shapes""" names = ["null", "none", "empty", "hidden", ""] @staticmethod def draw_path(ctx, center_x, center_y, width, height=None): """Draws nothing.""" pass class RectangleDrawer(ShapeDrawer): """Static class which draws rectangular vertices""" names = "rectangle rect rectangular square box" @staticmethod def draw_path(ctx, center_x, center_y, width, height=None): """Draws a rectangle-shaped path on the Cairo context without stroking or filling it. @see: ShapeDrawer.draw_path""" height = height or width ctx.rectangle(center_x - width/2, center_y - height/2, width, height) # pylint: disable-msg=C0103, R0911 # R0911: too many return statements @staticmethod def intersection_point(center_x, center_y, source_x, source_y, \ width, height=None): """Determines where the rectangle centered at (center_x, center_y) having the given width and height intersects with a line drawn from (source_x, source_y) to (center_x, center_y). @see: ShapeDrawer.intersection_point""" height = height or width delta_x, delta_y = center_x-source_x, center_y-source_y if delta_x == 0 and delta_y == 0: return center_x, center_y if delta_y > 0 and delta_x <= delta_y and delta_x >= -delta_y: # this is the top edge ry = center_y - height/2 ratio = (height/2) / delta_y return center_x-ratio*delta_x, ry if delta_y < 0 and delta_x <= -delta_y and delta_x >= delta_y: # this is the bottom edge ry = center_y + height/2 ratio = (height/2) / -delta_y return center_x-ratio*delta_x, ry if delta_x > 0 and delta_y <= delta_x and delta_y >= -delta_x: # this is the left edge rx = center_x - width/2 ratio = (width/2) / delta_x return rx, center_y-ratio*delta_y if delta_x < 0 and delta_y <= -delta_x and delta_y >= delta_x: # this is the right edge rx = center_x + width/2 ratio = (width/2) / -delta_x return rx, center_y-ratio*delta_y if delta_x == 0: if delta_y > 0: return center_x, center_y - height/2 return center_x, center_y + height/2 if delta_y == 0: if delta_x > 0: return center_x - width/2, center_y return center_x + width/2, center_y class CircleDrawer(ShapeDrawer): """Static class which draws circular vertices""" names = "circle circular" @staticmethod def draw_path(ctx, center_x, center_y, width, height=None): """Draws a circular path on the Cairo context without stroking or filling it. Height is ignored, it is the width that determines the diameter of the circle. @see: ShapeDrawer.draw_path""" ctx.arc(center_x, center_y, width/2, 0, 2*pi) @staticmethod def intersection_point(center_x, center_y, source_x, source_y, \ width, height=None): """Determines where the circle centered at (center_x, center_y) intersects with a line drawn from (source_x, source_y) to (center_x, center_y). @see: ShapeDrawer.intersection_point""" height = height or width angle = atan2(center_y-source_y, center_x-source_x) return center_x-width/2 * cos(angle), \ center_y-height/2* sin(angle) class UpTriangleDrawer(ShapeDrawer): """Static class which draws upright triangles""" names = "triangle triangle-up up-triangle arrow arrow-up up-arrow" @staticmethod def draw_path(ctx, center_x, center_y, width, height=None): """Draws an upright triangle on the Cairo context without stroking or filling it. @see: ShapeDrawer.draw_path""" height = height or width ctx.move_to(center_x-width/2, center_y+height/2) ctx.line_to(center_x, center_y-height/2) ctx.line_to(center_x+width/2, center_y+height/2) ctx.close_path() @staticmethod def intersection_point(center_x, center_y, source_x, source_y, \ width, height=None): """Determines where the triangle centered at (center_x, center_y) intersects with a line drawn from (source_x, source_y) to (center_x, center_y). @see: ShapeDrawer.intersection_point""" # TODO: finish it properly height = height or width return center_x, center_y class DownTriangleDrawer(ShapeDrawer): """Static class which draws triangles pointing down""" names = "down-triangle triangle-down arrow-down down-arrow" @staticmethod def draw_path(ctx, center_x, center_y, width, height=None): """Draws a triangle on the Cairo context without stroking or filling it. @see: ShapeDrawer.draw_path""" height = height or width ctx.move_to(center_x-width/2, center_y-height/2) ctx.line_to(center_x, center_y+height/2) ctx.line_to(center_x+width/2, center_y-height/2) ctx.close_path() @staticmethod def intersection_point(center_x, center_y, source_x, source_y, \ width, height=None): """Determines where the triangle centered at (center_x, center_y) intersects with a line drawn from (source_x, source_y) to (center_x, center_y). @see: ShapeDrawer.intersection_point""" # TODO: finish it properly height = height or width return center_x, center_y class DiamondDrawer(ShapeDrawer): """Static class which draws diamonds (i.e. rhombuses)""" names = "diamond rhombus" @staticmethod def draw_path(ctx, center_x, center_y, width, height=None): """Draws a rhombus on the Cairo context without stroking or filling it. @see: ShapeDrawer.draw_path""" height = height or width ctx.move_to(center_x-width/2, center_y) ctx.line_to(center_x, center_y+height/2) ctx.line_to(center_x+width/2, center_y) ctx.line_to(center_x, center_y-height/2) ctx.close_path() @staticmethod def intersection_point(center_x, center_y, source_x, source_y, \ width, height=None): """Determines where the rhombus centered at (center_x, center_y) intersects with a line drawn from (source_x, source_y) to (center_x, center_y). @see: ShapeDrawer.intersection_point""" height = height or width if height == 0 and width == 0: return center_x, center_y delta_x, delta_y = source_x - center_x, source_y - center_y # Treat edge case when delta_x = 0 if delta_x == 0: if delta_y == 0: return center_x, center_y else: return center_x, center_y + copysign(height / 2, delta_y) width = copysign(width, delta_x) height = copysign(height, delta_y) f = height / (height + width * delta_y / delta_x) return center_x + f * width / 2, center_y + (1-f) * height / 2 ##################################################################### class PolygonDrawer(AbstractCairoDrawer): """Class that is used to draw polygons. The corner points of the polygon can be set by the C{points} property of the drawer, or passed at construction time. Most drawing methods in this class also have an extra C{points} argument that can be used to override the set of points in the C{points} property.""" def __init__(self, context, bbox=(1, 1), points = []): """Constructs a new polygon drawer that draws on the given Cairo context. @param context: the Cairo context to draw on @param bbox: ignored, leave it at its default value @param points: the list of corner points """ super(PolygonDrawer, self).__init__(context, bbox) self.points = points def draw_path(self, points=None, corner_radius=0): """Sets up a Cairo path for the outline of a polygon on the given Cairo context. @param points: the coordinates of the corners of the polygon, in clockwise or counter-clockwise order, or C{None} if we are about to use the C{points} property of the class. @param corner_radius: if zero, an ordinary polygon will be drawn. If positive, the corners of the polygon will be rounded with the given radius. """ if points is None: points = self.points self.context.new_path() if len(points) < 2: # Well, a polygon must have at least two corner points return ctx = self.context if corner_radius <= 0: # No rounded corners, this is simple ctx.move_to(*points[-1]) for point in points: ctx.line_to(*point) return # Rounded corners. First, we will take each side of the # polygon and find what the corner radius should be on # each corner. If the side is longer than 2r (where r is # equal to corner_radius), the radius allowed by that side # is r; if the side is shorter, the radius is the length # of the side / 2. For each corner, the final corner radius # is the smaller of the radii on the two sides adjacent to # the corner. points = [Point(*point) for point in points] side_vecs = [v-u for u, v in consecutive_pairs(points, circular=True)] half_side_lengths = [side.length() / 2 for side in side_vecs] corner_radii = [corner_radius] * len(points) for idx in xrange(len(corner_radii)): prev_idx = -1 if idx == 0 else idx - 1 radii = [corner_radius, half_side_lengths[prev_idx], half_side_lengths[idx]] corner_radii[idx] = min(radii) # Okay, move to the last corner, adjusted by corner_radii[-1] # towards the first corner ctx.move_to(*(points[-1].towards(points[0], corner_radii[-1]))) # Now, for each point in points, draw a line towards the # corner, stopping before it in a distance of corner_radii[idx], # then draw the corner u = points[-1] for idx, (v, w) in enumerate(consecutive_pairs(points, True)): radius = corner_radii[idx] ctx.line_to(*v.towards(u, radius)) aux1 = v.towards(u, radius / 2) aux2 = v.towards(w, radius / 2) ctx.curve_to(aux1.x, aux1.y, aux2.x, aux2.y, *v.towards(w, corner_radii[idx])) u = v def draw(self, points=None): """Draws the polygon using the current stroke of the Cairo context. @param points: the coordinates of the corners of the polygon, in clockwise or counter-clockwise order, or C{None} if we are about to use the C{points} property of the class. """ self.draw_path(points) self.context.stroke() ##################################################################### class ShapeDrawerDirectory(object): """Static class that resolves shape names to their corresponding shape drawer classes. Classes that are derived from L{ShapeDrawer} in this module are automatically registered by L{ShapeDrawerDirectory} when the module is loaded for the first time. """ known_shapes = {} @classmethod def register(cls, drawer_class): """Registers the given shape drawer class under the given names. @param drawer_class: the shape drawer class to be registered """ names = drawer_class.names if isinstance(names, (str, unicode)): names = names.split() for name in names: cls.known_shapes[name] = drawer_class @classmethod def register_namespace(cls, namespace): """Registers all L{ShapeDrawer} classes in the given namespace @param namespace: a Python dict mapping names to Python objects.""" for name, value in namespace.iteritems(): if name.startswith("__"): continue if isinstance(value, type): if issubclass(value, ShapeDrawer) and value != ShapeDrawer: cls.register(value) @classmethod def resolve(cls, shape): """Given a shape name, returns the corresponding shape drawer class @param shape: the name of the shape @return: the corresponding shape drawer class @raise ValueError: if the shape is unknown """ try: return cls.known_shapes[shape] except KeyError: raise ValueError("unknown shape: %s" % shape) @classmethod def resolve_default(cls, shape, default=NullDrawer): """Given a shape name, returns the corresponding shape drawer class or the given default shape drawer if the shape name is unknown. @param shape: the name of the shape @param default: the default shape drawer to return when the shape is unknown @return: the shape drawer class corresponding to the given name or the default shape drawer class if the name is unknown """ return cls.known_shapes.get(shape, default) ShapeDrawerDirectory.register_namespace(sys.modules[__name__].__dict__) python-igraph-0.7.1.post6/igraph/drawing/text.py0000644000076500000240000003253512460534506022250 0ustar ntamasstaff00000000000000""" Drawers for labels on plots. @undocumented: test """ import re from igraph.compat import property from igraph.drawing.baseclasses import AbstractCairoDrawer from warnings import warn __all__ = ["TextAlignment", "TextDrawer"] __license__ = "GPL" __docformat__ = "restructuredtext en" ##################################################################### class TextAlignment(object): """Text alignment constants.""" LEFT, CENTER, RIGHT = "left", "center", "right" TOP, BOTTOM = "top", "bottom" ##################################################################### class TextAlignment(object): """Text alignment constants.""" LEFT, CENTER, RIGHT = "left", "center", "right" TOP, BOTTOM = "top", "bottom" ##################################################################### class TextDrawer(AbstractCairoDrawer): """Class that draws text on a Cairo context. This class supports multi-line text unlike the original Cairo text drawing methods.""" LEFT, CENTER, RIGHT = "left", "center", "right" TOP, BOTTOM = "top", "bottom" def __init__(self, context, text="", halign="center", valign="center"): """Constructs a new instance that will draw the given `text` on the given Cairo `context`.""" super(TextDrawer, self).__init__(context, (0, 0)) self.text = text self.halign = halign self.valign = valign def draw(self, wrap=False): """Draws the text in the current bounding box of the drawer. Since the class itself is an instance of `AbstractCairoDrawer`, it has an attribute named ``bbox`` which will be used as a bounding box. :Parameters: wrap : boolean whether to allow re-wrapping of the text if it does not fit within the bounding box horizontally. """ ctx = self.context bbox = self.bbox text_layout = self.get_text_layout(bbox.left, bbox.top, bbox.width, wrap) if not text_layout: return _, font_descent, line_height = ctx.font_extents()[:3] yb = ctx.text_extents(text_layout[0][2])[1] total_height = len(text_layout) * line_height if self.valign == self.BOTTOM: # Bottom vertical alignment dy = bbox.height - total_height - yb + font_descent elif self.valign == self.CENTER: # Centered vertical alignment dy = (bbox.height - total_height - yb + font_descent + line_height) / 2. else: # Top vertical alignment dy = line_height for ref_x, ref_y, line in text_layout: ctx.move_to(ref_x, ref_y + dy) ctx.show_text(line) ctx.new_path() def get_text_layout(self, x = None, y = None, width = None, wrap = False): """Calculates the layout of the current text. `x` and `y` denote the coordinates where the drawing should start. If they are both ``None``, the current position of the context will be used. Vertical alignment settings are not taken into account in this method as the text is not drawn within a box. :Parameters: x : float or ``None`` The X coordinate of the reference point where the layout should start. y : float or ``None`` The Y coordinate of the reference point where the layout should start. width : float or ``None`` The width of the box in which the text will be fitted. It matters only when the text is right-aligned or centered. The text will overflow the box if any of the lines is longer than the box width and `wrap` is ``False``. wrap : boolean whether to allow re-wrapping of the text if it does not fit within the given width. :Returns: a list consisting of ``(x, y, line)`` tuples where ``x`` and ``y`` refer to reference points on the Cairo canvas and ``line`` refers to the corresponding text that should be plotted there. """ ctx = self.context if x is None or y is None: x, y = ctx.get_current_point() line_height = ctx.font_extents()[2] if wrap: if width and width > 0: iterlines = self._iterlines_wrapped(width) else: warn("ignoring wrap=True as no width was specified") else: iterlines = self._iterlines() result = [] if self.halign == self.CENTER: # Centered alignment if width is None: width = self.text_extents()[2] for line, line_width, x_bearing in iterlines: result.append((x + (width-line_width)/2. - x_bearing, y, line)) y += line_height elif self.halign == self.RIGHT: # Right alignment if width is None: width = self.text_extents()[2] x += width for line, line_width, x_bearing in iterlines: result.append((x - line_width - x_bearing, y, line)) y += line_height else: # Left alignment for line, _, x_bearing in iterlines: result.append((x-x_bearing, y, line)) y += line_height return result def draw_at(self, x = None, y = None, width = None, wrap = False): """Draws the text by setting up an appropriate path on the Cairo context and filling it. `x` and `y` denote the coordinates where the drawing should start. If they are both ``None``, the current position of the context will be used. Vertical alignment settings are not taken into account in this method as the text is not drawn within a box. :Parameters: x : float or ``None`` The X coordinate of the reference point where the drawing should start. y : float or ``None`` The Y coordinate of the reference point where the drawing should start. width : float or ``None`` The width of the box in which the text will be fitted. It matters only when the text is right-aligned or centered. The text will overflow the box if any of the lines is longer than the box width. wrap : boolean whether to allow re-wrapping of the text if it does not fit within the given width. """ ctx = self.context for ref_x, ref_y, line in self.get_text_layout(x, y, width, wrap): ctx.move_to(ref_x, ref_y) ctx.show_text(line) ctx.new_path() def _iterlines(self): """Iterates over the label line by line and returns a tuple containing the folloing for each line: the line itself, the width of the line and the X-bearing of the line.""" ctx = self.context for line in self._text.split("\n"): xb, _, line_width, _, _, _ = ctx.text_extents(line) yield (line, line_width, xb) def _iterlines_wrapped(self, width): """Iterates over the label line by line and returns a tuple containing the folloing for each line: the line itself, the width of the line and the X-bearing of the line. The difference between this method and `_iterlines()` is that this method is allowed to re-wrap the line if necessary. :Parameters: width : float or ``None`` The width of the box in which the text will be fitted. Lines will be wrapped if they are wider than this width. """ ctx = self.context for line in self._text.split("\n"): xb, _, line_width, _, _, _ = ctx.text_extents(line) if line_width <= width: yield (line, line_width, xb) continue # We have to wrap the line current_line, current_width, last_sep_width = [], 0, 0 for match in re.finditer(r"(\S+)(\s+)?", line): word, sep = match.groups() word_width = ctx.text_extents(word)[4] if sep: sep_width = ctx.text_extents(sep)[4] else: sep_width = 0 current_width += word_width if current_width >= width and current_line: yield ("".join(current_line), current_width - word_width, 0) # Starting a new line current_line, current_width = [word], word_width if sep is not None: current_line.append(sep) else: current_width += last_sep_width current_line.append(word) if sep is not None: current_line.append(sep) last_sep_width = sep_width if current_line: yield ("".join(current_line), current_width, 0) @property def text(self): """Returns the text to be drawn.""" return self._text @text.setter def text(self, text): """Sets the text that will be drawn. If `text` is ``None``, it will be mapped to an empty string; otherwise, it will be converted to a string.""" if text is None: self._text = "" else: self._text = str(text) def text_extents(self): """Returns the X-bearing, Y-bearing, width, height, X-advance and Y-advance of the text. For multi-line text, the X-bearing and Y-bearing correspond to the first line, while the X-advance is extracted from the last line. and the Y-advance is the sum of all the Y-advances. The width and height correspond to the entire bounding box of the text.""" lines = self.text.split("\n") if len(lines) <= 1: return self.context.text_extents(self.text) x_bearing, y_bearing, width, height, x_advance, y_advance = \ self.context.text_extents(lines[0]) line_height = self.context.font_extents()[2] for line in lines[1:]: _, _, w, _, x_advance, ya = self.context.text_extents(line) width = max(width, w) height += line_height y_advance += ya return x_bearing, y_bearing, width, height, x_advance, y_advance def test(): """Testing routine for L{TextDrawer}""" import math from igraph.drawing.utils import find_cairo cairo = find_cairo() text = "The quick brown fox\njumps over a\nlazy dog" width, height = (600, 1000) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) context = cairo.Context(surface) drawer = TextDrawer(context, text) context.set_source_rgb(1, 1, 1) context.set_font_size(16.) context.rectangle(0, 0, width, height) context.fill() context.set_source_rgb(0.5, 0.5, 0.5) for i in range(200, width, 200): context.move_to(i, 0) context.line_to(i, height) context.stroke() for i in range(200, height, 200): context.move_to(0, i) context.line_to(width, i) context.stroke() context.set_source_rgb(0.75, 0.75, 0.75) context.set_line_width(0.5) for i in range(100, width, 200): context.move_to(i, 0) context.line_to(i, height) context.stroke() for i in range(100, height, 200): context.move_to(0, i) context.line_to(width, i) context.stroke() def mark_point(red, green, blue): """Marks the current point on the canvas by the given color""" x, y = context.get_current_point() context.set_source_rgba(red, green, blue, 0.5) context.arc(x, y, 4, 0, 2 * math.pi) context.fill() # Testing drawer.draw_at() for i, halign in enumerate(("left", "center", "right")): # Mark the reference points context.move_to(i * 200, 40) mark_point(0, 0, 1) context.move_to(i * 200, 140) mark_point(0, 0, 1) # Draw the text context.set_source_rgb(0, 0, 0) drawer.halign = halign drawer.draw_at(i * 200, 40) drawer.draw_at(i * 200, 140, width=200) # Mark the new reference point mark_point(1, 0, 0) # Testing TextDrawer.draw() for i, halign in enumerate(("left", "center", "right")): for j, valign in enumerate(("top", "center", "bottom")): # Draw the text context.set_source_rgb(0, 0, 0) drawer.halign = halign drawer.valign = valign drawer.bbox = (i*200, j*200+200, i*200+200, j*200+400) drawer.draw() # Mark the new reference point mark_point(1, 0, 0) # Testing TextDrawer.wrap() drawer.text = "Jackdaws love my big sphinx of quartz. Yay, wrapping! " + \ "Jackdaws love my big sphinx of quartz.\n\n" + \ "Jackdaws love my big sphinx of quartz." drawer.valign = TextDrawer.TOP for i, halign in enumerate(("left", "center", "right")): context.move_to(i * 200, 840) # Mark the reference point mark_point(0, 0, 1) # Draw the text context.set_source_rgb(0, 0, 0) drawer.halign = halign drawer.draw_at(i * 200, 840, width=199, wrap=True) # Mark the new reference point mark_point(1, 0, 0) surface.write_to_png("test.png") if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/drawing/utils.py0000644000076500000240000004276312460534506022430 0ustar ntamasstaff00000000000000""" Utility classes for drawing routines. """ from igraph.compat import property from itertools import izip from math import atan2, cos, sin from operator import itemgetter __all__ = ["BoundingBox", "FakeModule", "Point", "Rectangle"] __license__ = "GPL" ##################################################################### class Rectangle(object): """Class representing a rectangle.""" __slots__ = ("_left", "_top", "_right", "_bottom") def __init__(self, *args): """Creates a rectangle. The corners of the rectangle can be specified by either a tuple (four items, two for each corner, respectively), four separate numbers (X and Y coordinates for each corner) or two separate numbers (width and height, the upper left corner is assumed to be at (0,0))""" coords = None if len(args) == 1: if isinstance(args[0], Rectangle): coords = args[0].coords elif len(args[0]) >= 4: coords = tuple(args[0])[0:4] elif len(args[0]) == 2: coords = (0, 0, args[0][0], args[0][1]) elif len(args) == 4: coords = tuple(args) elif len(args) == 2: coords = (0, 0, args[0], args[1]) if coords is None: raise ValueError("invalid coordinate format") try: coords = tuple(float(coord) for coord in coords) except ValueError: raise ValueError("invalid coordinate format, numbers expected") self.coords = coords @property def coords(self): """The coordinates of the corners. The coordinates are returned as a 4-tuple in the following order: left edge, top edge, right edge, bottom edge. """ return self._left, self._top, self._right, self._bottom @coords.setter def coords(self, coords): """Sets the coordinates of the corners. @param coords: a 4-tuple with the coordinates of the corners """ self._left, self._top, self._right, self._bottom = coords if self._left > self._right: self._left, self._right = self._right, self._left if self._top > self._bottom: self._bottom, self._top = self._top, self._bottom @property def width(self): """The width of the rectangle""" return self._right - self._left @width.setter def width(self, value): """Sets the width of the rectangle by adjusting the right edge.""" self._right = self._left + value @property def height(self): """The height of the rectangle""" return self._bottom - self._top @height.setter def height(self, value): """Sets the height of the rectangle by adjusting the bottom edge.""" self._bottom = self._top + value @property def left(self): """The X coordinate of the left side of the box""" return self._left @left.setter def left(self, value): """Sets the X coordinate of the left side of the box""" self._left = float(value) self._right = max(self._left, self._right) @property def right(self): """The X coordinate of the right side of the box""" return self._right @right.setter def right(self, value): """Sets the X coordinate of the right side of the box""" self._right = float(value) self._left = min(self._left, self._right) @property def top(self): """The Y coordinate of the top edge of the box""" return self._top @top.setter def top(self, value): """Sets the Y coordinate of the top edge of the box""" self._top = value self._bottom = max(self._bottom, self._top) @property def bottom(self): """The Y coordinate of the bottom edge of the box""" return self._bottom @bottom.setter def bottom(self, value): """Sets the Y coordinate of the bottom edge of the box""" self._bottom = value self._top = min(self._bottom, self._top) @property def midx(self): """The X coordinate of the center of the box""" return (self._left + self._right) / 2.0 @midx.setter def midx(self, value): """Moves the center of the box to the given X coordinate""" dx = value - (self._left + self._right) / 2.0 self._left += dx self._right += dx @property def midy(self): """The Y coordinate of the center of the box""" return (self._top + self._bottom) / 2.0 @midy.setter def midy(self, value): """Moves the center of the box to the given Y coordinate""" dy = value - (self._top + self._bottom) / 2.0 self._top += dy self._bottom += dy @property def shape(self): """The shape of the rectangle (width, height)""" return self._right - self._left, self._bottom - self._top @shape.setter def shape(self, shape): """Sets the shape of the rectangle (width, height).""" self.width, self.height = shape def contract(self, margins): """Contracts the rectangle by the given margins. @return: a new L{Rectangle} object. """ if isinstance(margins, int) or isinstance(margins, float): margins = [float(margins)] * 4 if len(margins) != 4: raise ValueError("margins must be a 4-tuple or a single number") nx1, ny1 = self._left+margins[0], self._top+margins[1] nx2, ny2 = self._right-margins[2], self._bottom-margins[3] if nx1 > nx2: nx1 = (nx1+nx2)/2. nx2 = nx1 if ny1 > ny2: ny1 = (ny1+ny2)/2. ny2 = ny1 return self.__class__(nx1, ny1, nx2, ny2) def expand(self, margins): """Expands the rectangle by the given margins. @return: a new L{Rectangle} object. """ if isinstance(margins, int) or isinstance(margins, float): return self.contract(-float(margins)) return self.contract([-float(margin) for margin in margins]) def isdisjoint(self, other): """Returns ``True`` if the two rectangles have no intersection. Example:: >>> r1 = Rectangle(10, 10, 30, 30) >>> r2 = Rectangle(20, 20, 50, 50) >>> r3 = Rectangle(70, 70, 90, 90) >>> r1.isdisjoint(r2) False >>> r2.isdisjoint(r1) False >>> r1.isdisjoint(r3) True >>> r3.isdisjoint(r1) True """ return self._left > other._right or self._right < other._left \ or self._top > other._bottom or self._bottom < other._top def isempty(self): """Returns ``True`` if the rectangle is empty (i.e. it has zero width and height). Example:: >>> r1 = Rectangle(10, 10, 30, 30) >>> r2 = Rectangle(70, 70, 90, 90) >>> r1.isempty() False >>> r2.isempty() False >>> r1.intersection(r2).isempty() True """ return self._left == self._right and self._top == self._bottom def intersection(self, other): """Returns the intersection of this rectangle with another. Example:: >>> r1 = Rectangle(10, 10, 30, 30) >>> r2 = Rectangle(20, 20, 50, 50) >>> r3 = Rectangle(70, 70, 90, 90) >>> r1.intersection(r2) Rectangle(20.0, 20.0, 30.0, 30.0) >>> r2 & r1 Rectangle(20.0, 20.0, 30.0, 30.0) >>> r2.intersection(r1) == r1.intersection(r2) True >>> r1.intersection(r3) Rectangle(0.0, 0.0, 0.0, 0.0) """ if self.isdisjoint(other): return Rectangle(0, 0, 0, 0) return Rectangle(max(self._left, other._left), max(self._top, other._top), min(self._right, other._right), min(self._bottom, other._bottom)) __and__ = intersection def translate(self, dx, dy): """Translates the rectangle in-place. Example: >>> r = Rectangle(10, 20, 50, 70) >>> r.translate(30, -10) >>> r Rectangle(40.0, 10.0, 80.0, 60.0) @param dx: the X coordinate of the translation vector @param dy: the Y coordinate of the translation vector """ self._left += dx self._right += dx self._top += dy self._bottom += dy def union(self, other): """Returns the union of this rectangle with another. The resulting rectangle is the smallest rectangle that contains both rectangles. Example:: >>> r1 = Rectangle(10, 10, 30, 30) >>> r2 = Rectangle(20, 20, 50, 50) >>> r3 = Rectangle(70, 70, 90, 90) >>> r1.union(r2) Rectangle(10.0, 10.0, 50.0, 50.0) >>> r2 | r1 Rectangle(10.0, 10.0, 50.0, 50.0) >>> r2.union(r1) == r1.union(r2) True >>> r1.union(r3) Rectangle(10.0, 10.0, 90.0, 90.0) """ return Rectangle(min(self._left, other._left), min(self._top, other._top), max(self._right, other._right), max(self._bottom, other._bottom)) __or__ = union def __ior__(self, other): """Expands this rectangle to include itself and another completely while still being as small as possible. Example:: >>> r1 = Rectangle(10, 10, 30, 30) >>> r2 = Rectangle(20, 20, 50, 50) >>> r3 = Rectangle(70, 70, 90, 90) >>> r1 |= r2 >>> r1 Rectangle(10.0, 10.0, 50.0, 50.0) >>> r1 |= r3 >>> r1 Rectangle(10.0, 10.0, 90.0, 90.0) """ self._left = min(self._left, other._left) self._top = min(self._top, other._top) self._right = max(self._right, other._right) self._bottom = max(self._bottom, other._bottom) return self def __repr__(self): return "%s(%s, %s, %s, %s)" % (self.__class__.__name__, \ self._left, self._top, self._right, self._bottom) def __eq__(self, other): return self.coords == other.coords def __ne__(self, other): return self.coords != other.coords def __bool__(self): return self._left != self._right or self._top != self._bottom def __nonzero__(self): return self._left != self._right or self._top != self._bottom def __hash__(self): return hash(self.coords) ##################################################################### class BoundingBox(Rectangle): """Class representing a bounding box (a rectangular area) that encloses some objects.""" def __ior__(self, other): """Replaces this bounding box with the union of itself and another. Example:: >>> box1 = BoundingBox(10, 20, 50, 60) >>> box2 = BoundingBox(70, 40, 100, 90) >>> box1 |= box2 >>> print(box1) BoundingBox(10.0, 20.0, 100.0, 90.0) """ self._left = min(self._left, other._left) self._top = min(self._top, other._top) self._right = max(self._right, other._right) self._bottom = max(self._bottom, other._bottom) return self def __or__(self, other): """Takes the union of this bounding box with another. The result is a bounding box which encloses both bounding boxes. Example:: >>> box1 = BoundingBox(10, 20, 50, 60) >>> box2 = BoundingBox(70, 40, 100, 90) >>> box1 | box2 BoundingBox(10.0, 20.0, 100.0, 90.0) """ return self.__class__( min(self._left, other._left), min(self._top, other._top), max(self._right, other._right), max(self._bottom, other._bottom) ) ##################################################################### # pylint: disable-msg=R0903 # R0903: too few public methods class FakeModule(object): """Fake module that raises an exception for everything""" def __getattr__(self, _): raise TypeError("plotting not available") def __call__(self, _): raise TypeError("plotting not available") def __setattr__(self, key, value): raise TypeError("plotting not available") ##################################################################### def find_cairo(): """Tries to import the ``cairo`` Python module if it is installed, also trying ``cairocffi`` (a drop-in replacement of ``cairo``). Returns a fake module if everything fails. """ module_names = ["cairo", "cairocffi"] module = FakeModule() for module_name in module_names: try: module = __import__(module_name) break except ImportError: pass return module ##################################################################### class Point(tuple): """Class representing a point on the 2D plane.""" __slots__ = () _fields = ('x', 'y') def __new__(cls, x, y): """Creates a new point with the given coordinates""" return tuple.__new__(cls, (x, y)) # pylint: disable-msg=W0622 # W0622: redefining built-in 'len' @classmethod def _make(cls, iterable, new = tuple.__new__, len = len): """Creates a new point from a sequence or iterable""" result = new(cls, iterable) if len(result) != 2: raise TypeError('Expected 2 arguments, got %d' % len(result)) return result def __repr__(self): """Returns a nicely formatted representation of the point""" return 'Point(x=%r, y=%r)' % self def _asdict(self): """Returns a new dict which maps field names to their values""" return dict(zip(self._fields, self)) # pylint: disable-msg=W0141 # W0141: used builtin function 'map' def _replace(self, **kwds): """Returns a new point object replacing specified fields with new values""" result = self._make(map(kwds.pop, ('x', 'y'), self)) if kwds: raise ValueError('Got unexpected field names: %r' % kwds.keys()) return result def __getnewargs__(self): """Return self as a plain tuple. Used by copy and pickle.""" return tuple(self) x = property(itemgetter(0), doc="Alias for field number 0") y = property(itemgetter(1), doc="Alias for field number 1") def __add__(self, other): """Adds the coordinates of a point to another one""" return self.__class__(x = self.x + other.x, y = self.y + other.y) def __sub__(self, other): """Subtracts the coordinates of a point to another one""" return self.__class__(x = self.x - other.x, y = self.y - other.y) def __mul__(self, scalar): """Multiplies the coordinates by a scalar""" return self.__class__(x = self.x * scalar, y = self.y * scalar) __rmul__ = __mul__ def __div__(self, scalar): """Divides the coordinates by a scalar""" return self.__class__(x = self.x / scalar, y = self.y / scalar) def as_polar(self): """Returns the polar coordinate representation of the point. @return: the radius and the angle in a tuple. """ return len(self), atan2(self.y, self.x) def distance(self, other): """Returns the distance of the point from another one. Example: >>> p1 = Point(5, 7) >>> p2 = Point(8, 3) >>> p1.distance(p2) 5.0 """ dx, dy = self.x - other.x, self.y - other.y return (dx * dx + dy * dy) ** 0.5 def interpolate(self, other, ratio = 0.5): """Linearly interpolates between the coordinates of this point and another one. @param other: the other point @param ratio: the interpolation ratio between 0 and 1. Zero will return this point, 1 will return the other point. """ ratio = float(ratio) return Point(x = self.x * (1.0 - ratio) + other.x * ratio, \ y = self.y * (1.0 - ratio) + other.y * ratio) def length(self): """Returns the length of the vector pointing from the origin to this point.""" return (self.x ** 2 + self.y ** 2) ** 0.5 def normalized(self): """Normalizes the coordinates of the point s.t. its length will be 1 after normalization. Returns the normalized point.""" len = self.length() if len == 0: return Point(x = self.x, y = self.y) return Point(x = self.x / len, y = self.y / len) def sq_length(self): """Returns the squared length of the vector pointing from the origin to this point.""" return (self.x ** 2 + self.y ** 2) def towards(self, other, distance = 0): """Returns the point that is at a given distance from this point towards another one.""" if not distance: return self angle = atan2(other.y - self.y, other.x - self.x) return Point(self.x + distance * cos(angle), self.y + distance * sin(angle)) @classmethod def FromPolar(cls, radius, angle): """Constructs a point from polar coordinates. `radius` is the distance of the point from the origin; `angle` is the angle between the X axis and the vector pointing to the point from the origin. """ return cls(radius * cos(angle), radius * sin(angle)) python-igraph-0.7.1.post6/igraph/drawing/vertex.py0000644000076500000240000001002212534342712022562 0ustar ntamasstaff00000000000000""" Drawing routines to draw the vertices of graphs. This module provides implementations of vertex drawers, i.e. drawers that the default graph drawer will use to draw vertices. """ from igraph.drawing.baseclasses import AbstractDrawer, AbstractCairoDrawer from igraph.drawing.metamagic import AttributeCollectorBase from igraph.drawing.shapes import ShapeDrawerDirectory from math import pi __all__ = ["AbstractVertexDrawer", "AbstractCairoVertexDrawer", \ "DefaultVertexDrawer"] __license__ = "GPL" class AbstractVertexDrawer(AbstractDrawer): """Abstract vertex drawer object from which all concrete vertex drawer implementations are derived.""" def __init__(self, palette, layout): """Constructs the vertex drawer and associates it to the given palette. @param palette: the palette that can be used to map integer color indices to colors when drawing vertices @param layout: the layout of the vertices in the graph being drawn """ self.layout = layout self.palette = palette def draw(self, visual_vertex, vertex, coords): """Draws the given vertex. @param visual_vertex: object specifying the visual properties of the vertex. Its structure is defined by the VisualVertexBuilder of the L{DefaultGraphDrawer}; see its source code. @param vertex: the raw igraph vertex being drawn @param coords: the X and Y coordinates of the vertex as specified by the layout algorithm, scaled into the bounding box. """ raise NotImplementedError("abstract class") class AbstractCairoVertexDrawer(AbstractVertexDrawer, AbstractCairoDrawer): """Abstract base class for vertex drawers that draw on a Cairo canvas.""" def __init__(self, context, bbox, palette, layout): """Constructs the vertex drawer and associates it to the given Cairo context and the given L{BoundingBox}. @param context: the context on which we will draw @param bbox: the bounding box within which we will draw. Can be anything accepted by the constructor of L{BoundingBox} (i.e., a 2-tuple, a 4-tuple or a L{BoundingBox} object). @param palette: the palette that can be used to map integer color indices to colors when drawing vertices @param layout: the layout of the vertices in the graph being drawn """ AbstractCairoDrawer.__init__(self, context, bbox) AbstractVertexDrawer.__init__(self, palette, layout) class DefaultVertexDrawer(AbstractCairoVertexDrawer): """The default vertex drawer implementation of igraph.""" def __init__(self, context, bbox, palette, layout): AbstractCairoVertexDrawer.__init__(self, context, bbox, palette, layout) self.VisualVertexBuilder = self._construct_visual_vertex_builder() def _construct_visual_vertex_builder(self): class VisualVertexBuilder(AttributeCollectorBase): """Collects some visual properties of a vertex for drawing""" _kwds_prefix = "vertex_" color = ("red", self.palette.get) frame_color = ("black", self.palette.get) frame_width = 1.0 label = None label_angle = -pi/2 label_dist = 0.0 label_color = ("black", self.palette.get) label_size = 14.0 position = dict(func=self.layout.__getitem__) shape = ("circle", ShapeDrawerDirectory.resolve_default) size = 20.0 return VisualVertexBuilder def draw(self, visual_vertex, vertex, coords): context = self.context visual_vertex.shape.draw_path(context, \ coords[0], coords[1], visual_vertex.size) context.set_source_rgba(*visual_vertex.color) context.fill_preserve() context.set_source_rgba(*visual_vertex.frame_color) context.set_line_width(visual_vertex.frame_width) context.stroke() python-igraph-0.7.1.post6/igraph/formula.py0000644000076500000240000002107212453614202021262 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """ Implementation of `igraph.Graph.Formula()` You should use this module directly only if you have a very strong reason to do so. In almost all cases, you are better off with calling `igraph.Graph.Formula()`. """ from cStringIO import StringIO from igraph.datatypes import UniqueIdGenerator import tokenize import token __all__ = ["construct_graph_from_formula"] __license__ = u"""\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ def generate_edges(formula): """Parses an edge specification from the head of the given formula part and yields the following: - startpoint(s) of the edge by vertex names - endpoint(s) of the edge by names or an empty list if the vertices are isolated - a pair of bools to denote whether we had arrowheads at the start and end vertices """ if formula == "": yield [], [""], [False, False] return name_tokens = set([token.NAME, token.NUMBER, token.STRING]) edge_chars = "<>-+" start_names, end_names, arrowheads = [], [], [False, False] parsing_vertices = True # Tokenize the formula token_gen = tokenize.generate_tokens(StringIO(formula).next) for token_info in token_gen: # Do the state transitions token_type, tok, _, _, _ = token_info if parsing_vertices: if all(ch in edge_chars for ch in tok) and token_type == token.OP: parsing_vertices = False # Check the edge we currently have if start_names and end_names: # We have a whole edge yield start_names, end_names, arrowheads start_names, end_names = end_names, [] arrowheads = [False, False] else: if any(ch not in edge_chars for ch in tok): parsing_vertices = True if parsing_vertices: # We are parsing vertex names at the moment if token_type in name_tokens: # We found a vertex name if token_type == token.STRING: end_names.append(eval(tok)) else: end_names.append(str(tok)) elif tok == ":" and token_type == token.OP: # Separating semicolon between vertex names, we just go on continue elif token_type == token.ENDMARKER: # End markers are fine pass else: msg = "invalid token found in edge specification: %s" % formula raise SyntaxError(msg) else: # We are parsing an edge operator if "<" in tok: if ">" in tok: arrowheads = [True, True] else: arrowheads[0] = True elif ">" in tok: arrowheads[1] = True elif "+" in tok: if tok[0] == "+": arrowheads[0] = True if len(tok) > 1 and tok[-1] == "+": arrowheads[1] = True # The final edge yield start_names, end_names, arrowheads def construct_graph_from_formula(cls, formula = None, attr = "name", simplify = True): """Graph.Formula(formula = None, attr = "name", simplify = True) Generates a graph from a graph formula A graph formula is a simple string representation of a graph. It is very handy for creating small graphs quickly. The string consists of vertex names separated by edge operators. An edge operator is a sequence of dashes (C{-}) that may or may not start with an arrowhead (C{<} at the beginning of the sequence or C{>} at the end of the sequence). The edge operators can be arbitrarily long, i.e., you may use as many dashes to draw them as you like. This makes a total of four different edge operators: - C{-----} makes an undirected edge - C{<----} makes a directed edge pointing from the vertex on the right hand side of the operator to the vertex on the left hand side - C{---->} is the opposite of C{<----} - C{<--->} creates a mutual directed edge pair between the two vertices If you only use the undirected edge operator (C{-----}), the graph will be undirected. Otherwise it will be directed. Vertex names used in the formula will be assigned to the C{name} vertex attribute of the graph. Some simple examples: >>> from igraph import Graph >>> print Graph.Formula() # empty graph IGRAPH UN-- 0 0 -- + attr: name (v) >>> g = Graph.Formula("A-B") # undirected graph >>> g.vs["name"] ['A', 'B'] >>> print g IGRAPH UN-- 2 1 -- + attr: name (v) + edges (vertex names): A--B >>> g.get_edgelist() [(0, 1)] >>> g2 = Graph.Formula("A-----------B") >>> g2.isomorphic(g) True >>> g = Graph.Formula("A ---> B") # directed graph >>> g.vs["name"] ['A', 'B'] >>> print g IGRAPH DN-- 2 1 -- + attr: name (v) + edges (vertex names): A->B If you have may disconnected componnets, you can separate them with commas. You can also specify isolated vertices: >>> g = Graph.Formula("A--B, C--D, E--F, G--H, I, J, K") >>> print ", ".join(g.vs["name"]) A, B, C, D, E, F, G, H, I, J, K >>> g.clusters().membership [0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 6] The colon (C{:}) operator can be used to specify vertex sets. If an edge operator connects two vertex sets, then every vertex from the first vertex set will be connected to every vertex in the second set: >>> g = Graph.Formula("A:B:C:D --- E:F:G") >>> g.isomorphic(Graph.Full_Bipartite(4, 3)) True Note that you have to quote vertex names if they include spaces or special characters: >>> g = Graph.Formula('"this is" +- "a silly" -+ "graph here"') >>> g.vs["name"] ['this is', 'a silly', 'graph here'] @param formula: the formula itself @param attr: name of the vertex attribute where the vertex names will be stored @param simplify: whether the simplify the constructed graph @return: the constructed graph: """ # If we have no formula, return an empty graph if formula is None: return cls(0, vertex_attrs = {attr: []}) vertex_ids, edges, directed = UniqueIdGenerator(), [], False # Loop over each part in the formula for part in formula.split(","): # Drop newlines from the part part = part.strip().replace("\n", "").replace("\t", "") # Parse the first vertex specification from the formula for start_names, end_names, arrowheads in generate_edges(part): start_ids = [vertex_ids[name] for name in start_names] end_ids = [vertex_ids[name] for name in end_names] if not arrowheads[0] and not arrowheads[1]: # This is an undirected edge. Do we have a directed graph? if not directed: # Nope, add the edge edges.extend((id1, id2) for id1 in start_ids \ for id2 in end_ids) else: # This is a directed edge directed = True if arrowheads[1]: edges.extend((id1, id2) for id1 in start_ids \ for id2 in end_ids) if arrowheads[0]: edges.extend((id2, id1) for id1 in start_ids \ for id2 in end_ids) # Grab the vertex names into a list vertex_attrs = {} vertex_attrs[attr] = vertex_ids.values() # Construct and return the graph result = cls(len(vertex_ids), edges, directed, vertex_attrs=vertex_attrs) if simplify: result.simplify() return result python-igraph-0.7.1.post6/igraph/layout.py0000644000076500000240000004262712460534506021151 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """ Layout-related code in the IGraph library. This package contains the implementation of the L{Layout} object. """ from itertools import izip from math import sin, cos, pi from igraph.drawing.utils import BoundingBox from igraph.statistics import RunningMean __license__ = u"""\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ class Layout(object): """Represents the layout of a graph. A layout is practically a list of coordinates in an n-dimensional space. This class is generic in the sense that it can store coordinates in any n-dimensional space. Layout objects are not associated directly with a graph. This is deliberate: there were times when I worked with almost identical copies of the same graph, the only difference was that they had different colors assigned to the vertices. It was particularly convenient for me to use the same layout for all of them, especially when I made figures for a paper. However, C{igraph} will of course refuse to draw a graph with a layout that has less coordinates than the node count of the graph. Layouts behave exactly like lists when they are accessed using the item index operator (C{[...]}). They can even be iterated through. Items returned by the index operator are only copies of the coordinates, but the stored coordinates can be modified by directly assigning to an index. >>> layout = Layout([(0, 1), (0, 2)]) >>> coords = layout[1] >>> print coords [0, 2] >>> coords = (0, 3) >>> print layout[1] [0, 2] >>> layout[1] = coords >>> print layout[1] [0, 3] """ def __init__(self, coords=None, dim=None): """Constructor. @param coords: the coordinates to be stored in the layout. @param dim: the number of dimensions. If C{None}, the number of dimensions is determined automatically from the length of the first item of the coordinate list. If there are no entries in the coordinate list, the default will be 2. Generally, this should be given if the length of the coordinate list is zero, otherwise it should be left as is. """ if coords: self._coords = [list(coord) for coord in coords] else: self._coords = [] if dim is None: if len(self._coords) == 0: self._dim = 2 else: self._dim = len(self._coords[0]) else: self._dim = int(dim) for row in self._coords: if len(row) != self._dim: raise ValueError("all items in the coordinate list "+ "must have a length of %d" % self._dim) def __len__(self): return len(self._coords) def __getitem__(self, idx): return self._coords[idx] def __setitem__(self, idx, value): if len(value) != self._dim: raise ValueError("assigned item must have %d elements" % self._dim) self._coords[idx] = list(value) def __delitem__(self, idx): del self._coords[idx] def __copy__(self): return self.__class__(self.coords, self.dim) def __repr__(self): if not self.coords: vertex_count = "no vertices" elif len(self.coords) == 1: vertex_count = "1 vertex" else: vertex_count = "%d vertices" % len(self.coords) if self.dim == 1: dim_count = "1 dimension" else: dim_count = "%d dimensions" % self.dim return "<%s with %s and %s>" % (self.__class__.__name__, vertex_count, dim_count) @property def dim(self): """Returns the number of dimensions""" return self._dim @property def coords(self): """The coordinates as a list of lists""" return [row[:] for row in self._coords] def append(self, value): """Appends a new point to the layout""" if len(value) < self._dim: raise ValueError("appended item must have %d elements" % self._dim) self._coords.append([float(coord) for coord in value[0:self._dim]]) def mirror(self, dim): """Mirrors the layout along the given dimension(s) @param dim: the list of dimensions or a single dimension """ if isinstance(dim, int): dim = [dim] else: dim = [int(x) for x in dim] for current_dim in dim: for row in self._coords: row[current_dim] *= -1 def rotate(self, angle, dim1=0, dim2=1, **kwds): """Rotates the layout by the given degrees on the plane defined by the given two dimensions. @param angle: the angle of the rotation, specified in degrees. @param dim1: the first axis of the plane of the rotation. @param dim2: the second axis of the plane of the rotation. @keyword origin: the origin of the rotation. If not specified, the origin will be the origin of the coordinate system. """ origin = list(kwds.get("origin", [0.]*self._dim)) if len(origin) != self._dim: raise ValueError("origin must have %d dimensions" % self._dim) radian = angle * pi / 180. cos_alpha, sin_alpha = cos(radian), sin(radian) for idx, row in enumerate(self._coords): x, y = row[dim1] - origin[dim1], row[dim2] - origin[dim2] row[dim1] = cos_alpha*x - sin_alpha*y + origin[dim1] row[dim2] = sin_alpha*x + cos_alpha*y + origin[dim2] def scale(self, *args, **kwds): """Scales the layout. Scaling parameters can be provided either through the C{scale} keyword argument or through plain unnamed arguments. If a single integer or float is given, it is interpreted as a uniform multiplier to be applied on all dimensions. If it is a list or tuple, its length must be equal to the number of dimensions in the layout, and each element must be an integer or float describing the scaling coefficient in one of the dimensions. @keyword scale: scaling coefficients (integer, float, list or tuple) @keyword origin: the origin of scaling (this point will stay in place). Optional, defaults to the origin of the coordinate system being used. """ origin = list(kwds.get("origin", [0.]*self._dim)) if len(origin) != self._dim: raise ValueError("origin must have %d dimensions" % self._dim) scaling = kwds.get("scale") or args if isinstance(scaling, (int, float)): scaling = [scaling] if len(scaling) == 0: raise ValueError("scaling factor must be given") elif len(scaling) == 1: if type(scaling[0]) == int or type(scaling[0]) == float: scaling = scaling*self._dim else: scaling = scaling[0] if len(scaling) != self._dim: raise ValueError("scaling factor list must have %d elements" \ % self._dim) for idx, row in enumerate(self._coords): self._coords[idx] = [(row[d]-origin[d])*scaling[d]+origin[d] \ for d in xrange(self._dim)] def translate(self, *args, **kwds): """Translates the layout. The translation vector can be provided either through the C{v} keyword argument or through plain unnamed arguments. If unnamed arguments are used, the vector can be supplied as a single list (or tuple) or just as a series of arguments. In all cases, the translation vector must have the same number of dimensions as the layout. @keyword v: the translation vector """ v = kwds.get("v") or args if len(v) == 0: raise ValueError("translation vector must be given") elif len(v) == 1 and type(v[0]) != int and type(v[0]) != float: v = v[0] if len(v) != self._dim: raise ValueError("translation vector must have %d dimensions" \ % self._dim) for idx, row in enumerate(self._coords): self._coords[idx] = [row[d]+v[d] for d in xrange(self._dim)] def to_radial(self, min_angle = 100, max_angle = 80, \ min_radius=0.0, max_radius=1.0): """Converts a planar layout to a radial one This method applies only to 2D layouts. The X coordinate of the layout is transformed to an angle, with min(x) corresponding to the parameter called I{min_angle} and max(y) corresponding to I{max_angle}. Angles are given in degrees, zero degree corresponds to the direction pointing upwards. The Y coordinate is interpreted as a radius, with min(y) belonging to the minimum and max(y) to the maximum radius given in the arguments. This is not a fully generic polar coordinate transformation, but it is fairly useful in creating radial tree layouts from ordinary top-down ones (that's why the Y coordinate belongs to the radius). It can also be used in conjunction with the Fruchterman-Reingold layout algorithm via its I{miny} and I{maxy} parameters (see L{Graph.layout_fruchterman_reingold}) to produce radial layouts where the radius belongs to some property of the vertices. @param min_angle: the angle corresponding to the minimum X value @param max_angle: the angle corresponding to the maximum X value @param min_radius: the radius corresponding to the minimum Y value @param max_radius: the radius corresponding to the maximum Y value """ if self._dim != 2: raise TypeError("implemented only for 2D layouts") bbox = self.bounding_box() while min_angle > max_angle: max_angle += 360 while min_angle > 360: min_angle -= 360 max_angle -= 360 while min_angle < 0: min_angle += 360 max_angle += 360 ratio_x = (max_angle - min_angle) / bbox.width ratio_x *= pi / 180. min_angle *= pi / 180. ratio_y = (max_radius - min_radius) / bbox.height for idx, (x, y) in enumerate(self._coords): alpha = (x-bbox.left) * ratio_x + min_angle radius = (y-bbox.top) * ratio_y + min_radius self._coords[idx] = [cos(alpha)*radius, -sin(alpha)*radius] def transform(self, function, *args, **kwds): """Performs an arbitrary transformation on the layout Additional positional and keyword arguments are passed intact to the given function. @param function: a function which receives the coordinates as a tuple and returns the transformed tuple. """ self._coords = [list(function(tuple(row), *args, **kwds)) \ for row in self._coords] def centroid(self): """Returns the centroid of the layout. The centroid of the layout is the arithmetic mean of the points in the layout. @return: the centroid as a list of floats""" centroid = [RunningMean() for _ in xrange(self._dim)] for row in self._coords: for dim in xrange(self._dim): centroid[dim].add(row[dim]) return [rm.mean for rm in centroid] def boundaries(self, border=0): """Returns the boundaries of the layout. The boundaries are the minimum and maximum coordinates along all dimensions. @param border: this value gets subtracted from the minimum bounds and gets added to the maximum bounds before returning the coordinates of the box. Defaults to zero. @return: the minimum and maximum coordinates along all dimensions, in a tuple containing two lists, one for the minimum coordinates, the other one for the maximum. @raises ValueError: if the layout contains no layout items """ if not self._coords: raise ValueError("layout contains no layout items") mins, maxs = [], [] for dim in xrange(self._dim): col = [row[dim] for row in self._coords] mins.append(min(col)-border) maxs.append(max(col)+border) return mins, maxs def bounding_box(self, border=0): """Returns the bounding box of the layout. The bounding box of the layout is the smallest box enclosing all the points in the layout. @param border: this value gets subtracted from the minimum bounds and gets added to the maximum bounds before returning the coordinates of the box. Defaults to zero. @return: the coordinates of the lower left and the upper right corner of the box. "Lower left" means the minimum coordinates and "upper right" means the maximum. These are encapsulated in a L{BoundingBox} object. """ if self._dim != 2: raise ValueError("Layout.boundary_box() supports 2D layouts only") try: (x0, y0), (x1, y1) = self.boundaries(border) return BoundingBox(x0, y0, x1, y1) except ValueError: return BoundingBox(0, 0, 0, 0) def center(self, *args, **kwds): """Centers the layout around the given point. The point itself can be supplied as multiple unnamed arguments, as a simple unnamed list or as a keyword argument. This operation moves the centroid of the layout to the given point. If no point is supplied, defaults to the origin of the coordinate system. @keyword p: the point where the centroid of the layout will be after the operation.""" center = kwds.get("p") or args if len(center) == 0: center = [0.] * self._dim elif len(center) == 1 and type(center[0]) != int \ and type(center[0]) != float: center = center[0] if len(center) != self._dim: raise ValueError("the given point must have %d dimensions" \ % self._dim) centroid = self.centroid() vec = [center[d]-centroid[d] for d in xrange(self._dim)] self.translate(vec) def copy(self): """Creates an exact copy of the layout.""" return self.__copy__() def fit_into(self, bbox, keep_aspect_ratio=True): """Fits the layout into the given bounding box. The layout will be modified in-place. @param bbox: the bounding box in which to fit the layout. If the dimension of the layout is d, it can either be a d-tuple (defining the sizes of the box), a 2d-tuple (defining the coordinates of the top left and the bottom right point of the box), or a L{BoundingBox} object (for 2D layouts only). @param keep_aspect_ratio: whether to keep the aspect ratio of the current layout. If C{False}, the layout will be rescaled to fit exactly into the bounding box. If C{True}, the original aspect ratio of the layout will be kept and it will be centered within the bounding box. """ if isinstance(bbox, BoundingBox): if self._dim != 2: raise TypeError("bounding boxes work for 2D layouts only") corner, target_sizes = [bbox.left, bbox.top], [bbox.width, bbox.height] elif len(bbox) == self._dim: corner, target_sizes = [0.] * self._dim, list(bbox) elif len(bbox) == 2 * self._dim: corner, opposite_corner = list(bbox[0:self._dim]), list(bbox[self._dim:]) for i in xrange(self._dim): if corner[i] > opposite_corner[i]: corner[i], opposite_corner[i] = opposite_corner[i], corner[i] target_sizes = [max_val-min_val \ for min_val, max_val in izip(corner, opposite_corner)] try: mins, maxs = self.boundaries() except ValueError: mins, maxs = [0.0] * self._dim, [0.0] * self._dim sizes = [max_val - min_val for min_val, max_val in izip(mins, maxs)] for i, size in enumerate(sizes): if size == 0: sizes[i] = 2 mins[i] -= 1 maxs[i] += 1 ratios = [float(target_size) / current_size \ for current_size, target_size in izip(sizes, target_sizes)] if keep_aspect_ratio: min_ratio = min(ratios) ratios = [min_ratio] * self._dim translations = [] for i in xrange(self._dim): trans = (target_sizes[i] - ratios[i] * sizes[i]) / 2. trans -= mins[i] * ratios[i] - corner[i] translations.append(trans) self.scale(*ratios) self.translate(*translations) python-igraph-0.7.1.post6/igraph/matching.py0000644000076500000240000001530112453614202021405 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """Classes representing matchings on graphs.""" from igraph.clustering import VertexClustering from igraph._igraph import Vertex __license__ = u"""\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ class Matching(object): """A matching of vertices in a graph. A matching of an undirected graph is a set of edges such that each vertex is incident on at most one matched edge. When each vertex is incident on I{exactly} one matched edge, the matching called I{perfect}. This class is used in C{igraph} to represent non-perfect and perfect matchings in undirected graphs. This class is usually not instantiated directly, everything is taken care of by the functions that return matchings. Examples: >>> from igraph import Graph >>> g = Graph.Famous("noperfectmatching") >>> matching = g.maximum_matching() """ def __init__(self, graph, matching, types=None): """Initializes the matching. @param graph: the graph the matching belongs to @param matching: a numeric vector where element I{i} corresponds to vertex I{i} of the graph. Element I{i} is -1 or if the corresponding vertex is unmatched, otherwise it contains the index of the vertex to which vertex I{i} is matched. @param types: the types of the vertices if the graph is bipartite. It must either be the name of a vertex attribute (which will be retrieved for all vertices) or a list. Elements in the list will be converted to boolean values C{True} or C{False}, and this will determine which part of the bipartite graph a given vertex belongs to. @raise ValueError: if the matching vector supplied does not describe a valid matching of the graph. """ self._graph = graph self._matching = None self._num_matched = 0 self._types = None if isinstance(types, basestring): types = graph.vs[types] self.types = types self.matching = matching def __len__(self): return self._num_matched def __repr__(self): if self._types is not None: return "%s(%r,%r,types=%r)" % \ (self.__class__.__name__, self._graph, self._matching, self._types) else: return "%s(%r,%r)" % \ (self.__class__.__name__, self._graph, self._matching) def __str__(self): if self._types is not None: return "Bipartite graph matching (%d matched vertex pairs)" % len(self) else: return "Graph matching (%d matched vertex pairs)" % len(self) def edges(self): """Returns an edge sequence that contains the edges in the matching. If there are multiple edges between a pair of matched vertices, only one of them will be returned. """ get_eid = self._graph.get_eid eidxs = [get_eid(u, v, directed=False) \ for u, v in enumerate(self._matching) if v != -1 and u <= v] return self._graph.es[eidxs] @property def graph(self): """Returns the graph corresponding to the matching.""" return self._graph def is_maximal(self): """Returns whether the matching is maximal. A matching is maximal when it is not possible to extend it any more with extra edges; in other words, unmatched vertices in the graph must be adjacent to matched vertices only. """ return self._graph._is_maximal_matching(self._matching, types=self._types) def is_matched(self, vertex): """Returns whether the given vertex is matched to another one.""" if isinstance(vertex, Vertex): vertex = vertex.index return self._matching[vertex] >= 0 def match_of(self, vertex): """Returns the vertex a given vertex is matched to. @param vertex: the vertex we are interested in; either an integer index or an instance of L{Vertex}. @return: the index of the vertex matched to the given vertex, either as an integer index (if I{vertex} was integer) or as an instance of L{Vertex}. When the vertex is unmatched, returns C{None}. """ if isinstance(vertex, Vertex): matched = self._matching[vertex.index] if matched < 0: return None return self._graph.vs[matched] matched = self._matching[vertex] if matched < 0: return None return matched @property def matching(self): """Returns the matching vector where element I{i} contains the ID of the vertex that vertex I{i} is matched to. The matching vector will contain C{-1} for unmatched vertices. """ return self._matching @matching.setter def matching(self, value): """Sets the matching vector. @param value: the matching vector which must contain the ID of the vertex matching vertex I{i} at the I{i}th position, or C{-1} if the vertex is unmatched. @raise ValueError: if the matching vector supplied does not describe a valid matching of the graph. """ if not self.graph._is_matching(value, types=self._types): raise ValueError("not a valid matching") self._matching = list(value) self._num_matched = sum(1 for i in self._matching if i >= 0) // 2 @property def types(self): """Returns the type vector if the graph is bipartite. Element I{i} of the type vector will be C{False} or C{True} depending on which side of the bipartite graph vertex I{i} belongs to. For non-bipartite graphs, this property returns C{None}. """ return self._types @types.setter def types(self, value): types = [bool(x) for x in value] if len(types) < self._graph.vcount(): raise ValueError("type vector too short") self._types = types python-igraph-0.7.1.post6/igraph/remote/0000755000076500000240000000000012534343010020530 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/igraph/remote/__init__.py0000644000076500000240000000010612453614202022642 0ustar ntamasstaff00000000000000"""Classes that help igraph communicate with remote applications.""" python-igraph-0.7.1.post6/igraph/remote/gephi.py0000644000076500000240000002631412453614202022210 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """Classes that help igraph communicate with Gephi (http://www.gephi.org).""" from igraph.compat import property import urllib2 try: # JSON is optional so we don't blow up with Python < 2.6 import json except ImportError: try: # Try with simplejson for Python < 2.6 import simplejson as json except ImportError: # No simplejson either from igraph.drawing.utils import FakeModule json = FakeModule() __all__ = ["GephiConnection", "GephiGraphStreamer", "GephiGraphStreamingAPIFormat"] __docformat__ = "restructuredtext en" __license__ = u"""\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ class GephiConnection(object): """Object that represents a connection to a Gephi master server.""" def __init__(self, url=None, host="127.0.0.1", port=8080, workspace=0): """Constructs a connection to a Gephi master server. The connection object can be constructed either by specifying the `url` directly, or by specifying the `host`, `port` and `workspace` arguments. The latter three are evaluated only if `url` is None; otherwise the `url` will take precedence. The `url` argument does not have to include the operation (e.g., ``?operation=updateGraph``); the connection will take care of it. E.g., if you wish to connect to workspace 2 in a local Gephi instance on port 7341, the correct form to use for the `url` is as follows:: http://localhost:7341/workspace0 """ self._pending_operations = [] self._autoflush_threshold = 1024 self.url = url or self._construct_default_url(host, port, workspace) def __del__(self): try: self.close() except urllib2.URLError: # Happens when Gephi is closed before the connection is destroyed pass def _construct_default_url(self, host, port, workspace): return "http://%s:%d/workspace%d" % (host, port, workspace) def close(self): """Flushes all the pending operations to the Gephi master server in a single request.""" self.flush() def flush(self): """Flushes all the pending operations to the Gephi master server in a single request.""" data = "".join(self._pending_operations) self._pending_operations = [] conn = urllib2.urlopen(self._update_url, data=data) return conn.read() @property def url(self): """The URL of the Gephi workspace where the data will be sent.""" return self._url_root @url.setter def url(self, value): self._url_root = value self._get_url = self._url_root + "?operation=getGraph" self._update_url = self._url_root + "?operation=updateGraph" def write(self, data): """Sends the given raw data to the Gephi streaming master server in an HTTP POST request.""" self._pending_operations.append(data) if len(self._pending_operations) >= self._autoflush_threshold: self.flush() def __repr__(self): return "%s(url=%r)" % (self.__class__.__name__, self.url) class GephiGraphStreamingAPIFormat(object): """Object that implements the Gephi graph streaming API format and returns Python objects corresponding to the events defined in the API. """ def get_add_node_event(self, identifier, attributes={}): """Generates a Python object corresponding to the event that adds a node with the given identifier and attributes in the Gephi graph streaming API. Example:: >>> api = GephiGraphStreamingAPIFormat() >>> api.get_add_node_event("spam") {'an': {'spam': {}}} >>> api.get_add_node_event("spam", dict(ham="eggs")) {'an': {'spam': {'ham': 'eggs'}}} """ return {"an": {identifier: attributes}} def get_add_edge_event(self, identifier, source, target, directed, attributes={}): """Generates a Python object corresponding to the event that adds an edge with the given source, target, directednessr and attributes in the Gephi graph streaming API. """ result = dict(attributes) result["source"] = source result["target"] = target result["directed"] = bool(directed) return {"ae": {identifier: result}} def get_change_node_event(self, identifier, attributes): """Generates a Python object corresponding to the event that changes the attributes of some node in the Gephi graph streaming API. The given attributes are merged into the existing ones; use C{None} as the attribute value to delete a given attribute. Example:: >>> api = GephiGraphStreamingAPIFormat() >>> api.get_change_node_event("spam", dict(ham="eggs")) {'cn': {'spam': {'ham': 'eggs'}}} >>> api.get_change_node_event("spam", dict(ham=None)) {'cn': {'spam': {'ham': None}}} """ return {"cn": {identifier: attributes}} def get_change_edge_event(self, identifier, attributes): """Generates a Python object corresponding to the event that changes the attributes of some edge in the Gephi graph streaming API. The given attributes are merged into the existing ones; use C{None} as the attribute value to delete a given attribute. Example:: >>> api = GephiGraphStreamingAPIFormat() >>> api.get_change_edge_event("spam", dict(ham="eggs")) {'ce': {'spam': {'ham': 'eggs'}}} >>> api.get_change_edge_event("spam", dict(ham=None)) {'ce': {'spam': {'ham': None}}} """ return {"ce": {identifier: attributes}} def get_delete_node_event(self, identifier): """Generates a Python object corresponding to the event that deletes a node with the given identifier in the Gephi graph streaming API. Example:: >>> api = GephiGraphStreamingAPIFormat() >>> api.get_delete_node_event("spam") {'dn': {'spam': {}}} """ return {"dn": {identifier: {}}} def get_delete_edge_event(self, identifier): """Generates a Python object corresponding to the event that deletes an edge with the given identifier in the Gephi graph streaming API. Example:: >>> api = GephiGraphStreamingAPIFormat() >>> api.get_delete_edge_event("spam:ham") {'de': {'spam:ham': {}}} """ return {"de": {identifier: {}}} class GephiGraphStreamer(object): """Class that produces JSON event objects that stream an igraph graph to Gephi using the Gephi Graph Streaming API. The Gephi graph streaming format is a simple JSON-based format that can be used to post mutations to a graph (i.e. node and edge additions, removals and updates) to a remote component. For instance, one can open up Gephi (http://www.gephi.org}), install the Gephi graph streaming plugin and then send a graph from igraph straight into the Gephi window by using `GephiGraphStreamer` with the appropriate URL where Gephi is listening. Example:: >>> from cStringIO import StringIO >>> from igraph import Graph >>> buf = StringIO() >>> streamer = GephiGraphStreamer() >>> graph = Graph.Formula("A --> B, B --> C") >>> streamer.post(graph, buf) >>> print buf.getvalue() # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE {"an": {"igraph:...:v:0": {"name": "A"}}} {"an": {"igraph:...:v:1": {"name": "B"}}} {"an": {"igraph:...:v:2": {"name": "C"}}} {"ae": {"igraph:...:e:0:1": {...}}} {"ae": {"igraph:...:e:1:2": {...}}} """ def __init__(self, encoder=None): """Constructs a Gephi graph streamer that will post graphs to a given file-like object or a Gephi connection. `encoder` must either be ``None`` or an instance of ``json.JSONEncoder`` and it must contain the JSON encoder to be used when posting JSON objects. """ self.encoder = encoder or json.JSONEncoder(ensure_ascii=True) self.format = GephiGraphStreamingAPIFormat() def iterjsonobj(self, graph): """Iterates over the JSON objects that build up the graph using the Gephi graph streaming API. The objects returned from this function are Python objects; they must be formatted with ``json.dumps`` before sending them to the destination.""" # Construct a unique ID prefix id_prefix = "igraph:%s" % (hex(id(graph)), ) # Add the vertices add_node = self.format.get_add_node_event for vertex in graph.vs: yield add_node("%s:v:%d" % (id_prefix, vertex.index), vertex.attributes()) # Add the edges add_edge = self.format.get_add_edge_event directed = graph.is_directed() for edge in graph.es: yield add_edge("%s:e:%d:%d" % (id_prefix, edge.source, edge.target), "%s:v:%d" % (id_prefix, edge.source), "%s:v:%d" % (id_prefix, edge.target), directed, edge.attributes()) def post(self, graph, destination, encoder=None): """Posts the given graph to the destination of the streamer using the given JSON encoder. When `encoder` is ``None``, it falls back to the default JSON encoder of the streamer in the `encoder` property. `destination` must be a file-like object or an instance of `GephiConnection`. """ encoder = encoder or self.encoder for jsonobj in self.iterjsonobj(graph): self.send_event(jsonobj, destination, encoder=encoder, flush=False) destination.flush() def send_event(self, event, destination, encoder=None, flush=True): """Sends a single JSON event to the given destination using the given JSON encoder. When `encoder` is ``None``, it falls back to the default JSON encoder of the streamer in the `encoder` property. `destination` must be a file-like object or an instance of `GephiConnection`. The method flushes the destination after sending the event. If you want to avoid this (e.g., because you are sending many events), set `flush` to ``False``. """ encoder = encoder or self.encoder destination.write(encoder.encode(event)) destination.write("\r\n") if flush: destination.flush() python-igraph-0.7.1.post6/igraph/remote/nexus.py0000644000076500000240000005251112460534506022262 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """Interface to the Nexus online graph repository. The classes in this file facilitate access to the Nexus online graph repository at U{http://nexus.igraph.org}. The main entry point of this package is the C{Nexus} variable, which is an instance of L{NexusConnection}. Use L{NexusConnection.get} to get a particular network from Nexus, L{NexusConnection.list} to list networks having a given set of tags, L{NexusConnection.search} to search in the dataset descriptions, or L{NexusConnection.info} to show the info sheet of a dataset.""" from gzip import GzipFile from itertools import izip from textwrap import TextWrapper from urllib import urlencode from urlparse import urlparse, urlunparse from textwrap import TextWrapper from igraph.compat import property, BytesIO from igraph.configuration import Configuration from igraph.utils import multidict import re import urllib2 __all__ = ["Nexus", "NexusConnection"] __license__ = u"""\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ class NexusConnection(object): """Connection to a remote Nexus server. In most cases, you will not have to instantiate this object, just use the global L{Nexus} variable which is an instance of L{NexusConnection} and connects to the Nexus repository at U{http://nexus.igraph.org}. Example: >>> print Nexus.info("karate") #doctest:+ELLIPSIS Nexus dataset 'karate' (#1) vertices/edges: 34/78 name: Zachary's karate club tags: social network; undirected; weighted ... >>> karate = Nexus.get("karate") >>> from igraph import summary >>> summary(karate) IGRAPH UNW- 34 78 -- Zachary's karate club network + attr: Author (g), Citation (g), name (g), Faction (v), id (v), name (v), weight (e) @undocumented: _get_response, _parse_dataset_id, _parse_text_response, _ensure_uncompressed""" def __init__(self, nexus_url=None): """Constructs a connection to a remote Nexus server. @param nexus_url: the root URL of the remote server. Leave it at its default value (C{None}) unless you have set up your own Nexus server and you want to connect to that. C{None} fetches the URL from igraph's configuration file or uses the default URL if no URL is specified in the configuration file. """ self.debug = False self.url = nexus_url self._opener = urllib2.build_opener() def get(self, id): """Retrieves the dataset with the given ID from Nexus. Dataset IDs are formatted as follows: the name of a dataset on its own means that a single network should be returned if the dataset contains a single network, or multiple networks should be returned if the dataset contains multiple networks. When the name is followed by a dot and a network ID, only a single network will be returned: the one that has the given network ID. When the name is followed by a dot and a star, a dictionary mapping network IDs to networks will be returned even if the original dataset contains a single network only. E.g., getting C{"karate"} would return a single network since the Zachary karate club dataset contains one network only. Getting C{"karate.*"} on the other hand would return a dictionary with one entry that contains the Zachary karate club network. @param id: the ID of the dataset to retrieve. @return: an instance of L{Graph} (if a single graph has to be returned) or a dictionary mapping network IDs to instances of L{Graph}. """ from igraph import load dataset_id, network_id = self._parse_dataset_id(id) params = dict(format="Python-igraph", id=dataset_id) response = self._get_response("/api/dataset", params, compressed=True) response = self._ensure_uncompressed(response) result = load(response, format="pickle") if network_id is None: # If result contains a single network only, return that network. # Otherwise return the whole dictionary if not isinstance(result, dict): return result if len(result) == 1: return result[result.keys()[0]] return result if network_id == "*": # Return a dict no matter what if not isinstance(result, dict): result = dict(dataset_id=result) return result return result[network_id] def info(self, id): """Retrieves informations about the dataset with the given numeric or string ID from Nexus. @param id: the numeric or string ID of the dataset to retrieve. @return: an instance of L{NexusDatasetInfo}. """ params = dict(format="text", id=id) response = self._get_response("/api/dataset_info", params) return NexusDatasetInfo.FromMultiDict(self._parse_text_response(response)) def list(self, tags=None, operator="or", order="date"): """Retrieves a list of datasets matching a set of tags from Nexus. @param tags: the tags the returned datasets should have. C{None} retrieves all the datasets, a single string retrieves datasets having that given tag. Multiple tags may also be specified as a list, tuple or any other iterable. @param operator: when multiple tags are given, this argument specifies whether the retrieved datasets should match all the tags (C{"and"}) or any of them (C{"or"}). @param order: the order of entries; it must be one of C{"date"}, C{"name"} or C{"popularity"}. @return: a L{NexusDatasetInfoList} object, which basically acts like a list and yields L{NexusDatasetInfo} objects. The list is populated lazily; i.e. the requests will be fired only when needed. """ params = dict(format="text", order=order) if tags is not None: if not hasattr(tags, "__iter__") or isinstance(tags, basestring): params["tag"] = str(tags) else: params["tag"] = "|".join(str(tag) for tag in tags) params["operator"] = operator return NexusDatasetInfoList(self, "/api/dataset_info", params) def search(self, query, order="date"): """Retrieves a list of datasets matching a query string from Nexus. @param query: the query string. Searches are case insensitive and Nexus searches for complete words only. The special word OR can be used to find datasets that contain any of the given words (instead of all of them). Exact phrases must be enclosed in quotes in the search string. See the Nexus webpage for more information at U{http://nexus.igraph.org/web/docs#searching}. @param order: the order of entries; it must be one of C{"date"}, C{"name"} or C{"popularity"}. @return: a L{NexusDatasetInfoList} object, which basically acts like a list and yields L{NexusDatasetInfo} objects. The list is populated lazily; i.e. the requests will be fired only when needed. """ params = dict(q=query, order=order, format="text") return NexusDatasetInfoList(self, "/api/search", params) @staticmethod def _ensure_uncompressed(response): """Expects an HTTP response object, checks its Content-Encoding header, decompresses the data and returns an in-memory buffer holding the uncompressed data.""" compressed = response.headers.get("Content-Encoding") == "gzip" if not compressed: content_disp = response.headers.get("Content-Disposition", "") compressed = bool(re.match(r'attachment; *filename=.*\.gz\"?$', content_disp)) if compressed: return GzipFile(fileobj=BytesIO(response.read()), mode="rb") return response def _get_response(self, path, params={}, compressed=False): """Sends a request to Nexus at the given path with the given parameters and returns a file-like object for the response. `compressed` denotes whether we accept compressed responses.""" if self.url is None: url = Configuration.instance()["remote.nexus.url"] else: url = self.url url = "%s%s?%s" % (url, path, urlencode(params)) request = urllib2.Request(url) if compressed: request.add_header("Accept-Encoding", "gzip") if self.debug: print "[debug] Sending request: %s" % url return self._opener.open(request) @staticmethod def _parse_dataset_id(id): """Parses a dataset ID used in the `get` request. Returns the dataset ID and the network ID (the latter being C{None} if the original ID did not contain a network ID ). """ dataset_id, _, network_id = str(id).partition(".") if not network_id: network_id = None return dataset_id, network_id @staticmethod def _parse_text_response(response): """Parses a plain text formatted response from Nexus. Plain text formatted responses consist of key-value pairs, separated by C{":"}. Values may span multiple lines; in this case, the key is omitted after the first line and the extra lines start with whitespace. Examples: >>> d = Nexus._parse_text_response("Id: 17\\nName: foo") >>> sorted(d.items()) [('Id', '17'), ('Name', 'foo')] >>> d = Nexus._parse_text_response("Id: 42\\nName: foo\\n .\\n bar") >>> sorted(d.items()) [('Id', '42'), ('Name', 'foo\\n\\nbar')] """ if isinstance(response, basestring): response = response.split("\n") result = multidict() key, value = None, [] for line in response: line = line.rstrip() if not line: continue if key is not None and line[0] in ' \t': # Line continuation line = line.lstrip() if line == '.': line = '' value.append(line) else: # Key-value pair if key is not None: result.add(key, "\n".join(value)) key, value = line.split(":", 1) value = [value.strip()] if key is not None: result.add(key, "\n".join(value)) return result @property def url(self): """Returns the root URL of the Nexus repository the connection is communicating with.""" return self._url @url.setter def url(self, value): """Sets the root URL of the Nexus repository the connection is communicating with.""" if value is None: self._url = None else: value = str(value) parts = urlparse(value, "http", False) self._url = urlunparse(parts) if self._url and self._url[-1] == "/": self._url = self._url[:-1] class NexusDatasetInfo(object): """Information about a dataset in the Nexus repository. @undocumented: _update_from_multidict, vertices_edges""" def __init__(self, id=None, sid=None, name=None, networks=None, vertices=None, edges=None, tags=None, attributes=None, rest=None): self._conn = None self.id = id self.sid = sid self.name = name self.vertices = vertices self.edges = edges self.tags = tags self.attributes = attributes if networks is None: self.networks = [] elif not isinstance(networks, (str, unicode)): self.networks = list(networks) else: self.networks = [networks] if rest: self.rest = multidict(rest) else: self.rest = None @property def vertices_edges(self): if self.vertices is None or self.edges is None: return "" elif isinstance(self.vertices, (list, tuple)) and isinstance(self.edges, (list, tuple)): return " ".join("%s/%s" % (v,e) for v, e in izip(self.vertices, self.edges)) else: return "%s/%s" % (self.vertices, self.edges) @vertices_edges.setter def vertices_edges(self, value): if value is None: self.vertices, self.edges = None, None return value = value.strip().split(" ") if len(value) == 0: self.vertices, self.edges = None, None elif len(value) == 1: self.vertices, self.edges = map(int, value[0].split("/")) else: self.vertices = [] self.edges = [] for ve in value: v, e = ve.split("/", 1) self.vertices.append(int(v)) self.edges.append(int(e)) def __repr__(self): params = "(id=%(id)r, sid=%(sid)r, name=%(name)r, networks=%(networks)r, "\ "vertices=%(vertices)r, edges=%(edges)r, tags=%(tags)r, "\ "attributes=%(attributes)r, rest=%(rest)r)" % self.__dict__ return "%s%s" % (self.__class__.__name__, params) def __str__(self): if self.networks and len(self.networks) > 1: lines = ["Nexus dataset '%s' (#%s) with %d networks" % \ (self.sid, self.id, len(self.networks))] else: lines = ["Nexus dataset '%(sid)s' (#%(id)s)" % self.__dict__] lines.append("vertices/edges: %s" % self.vertices_edges) if self.name: lines.append("name: %s" % self.name) if self.tags: lines.append("tags: %s" % "; ".join(self.tags)) if self.rest: wrapper = TextWrapper(width=76, subsequent_indent=' ') keys = sorted(self.rest.iterkeys()) if "attribute" in self.rest: keys.remove("attribute") keys.append("attribute") for key in keys: for value in self.rest.getlist(key): paragraphs = str(value).splitlines() wrapper.initial_indent = "%s: " % key for paragraph in paragraphs: ls = wrapper.wrap(paragraph) if ls: lines.extend(wrapper.wrap(paragraph)) else: lines.append(" .") wrapper.initial_indent = " " return "\n".join(lines) def _update_from_multidict(self, params): """Updates the dataset object from a multidict representation of key-value pairs, similar to the ones provided by the Nexus API in plain text response.""" self.id = params.get("id") self.sid = params.get("sid") self.name = params.get("name") self.vertices = params.get("vertices") self.edges = params.get("edges") self.tags = params.get("tags") networks = params.get("networks") if networks: self.networks = networks.split() keys_to_ignore = set("id sid name vertices edges tags networks".split()) if self.vertices is None and self.edges is None: # Try "vertices/edges" self.vertices_edges = params.get("vertices/edges") keys_to_ignore.add("vertices/edges") if self.rest is None: self.rest = multidict() for k in set(params.iterkeys()) - keys_to_ignore: for v in params.getlist(k): self.rest.add(k, v) if self.id: self.id = int(self.id) if self.vertices and not isinstance(self.vertices, (list, tuple)): self.vertices = int(self.vertices) if self.edges and not isinstance(self.edges, (list, tuple)): self.edges = int(self.edges) if self.tags is not None: self.tags = self.tags.split(";") @classmethod def FromMultiDict(cls, dict): """Constructs a Nexus dataset object from a multidict representation of key-value pairs, similar to the ones provided by the Nexus API in plain text response.""" result = cls() result._update_from_multidict(dict) return result def download(self, network_id=None): """Retrieves the actual dataset from Nexus. @param network_id: if the dataset contains multiple networks, the ID of the network to be retrieved. C{None} returns a single network if the dataset contains a single network, or a dictionary of networks if the dataset contains more than one network. C{"*"} retrieves a dictionary even if the dataset contains a single network only. @return: a L{Graph} instance or a dictionary mapping network names to L{Graph} instances. """ if self.id is None: raise ValueError("dataset ID is empty") conn = self._conn or Nexus if network_id is None: return conn.get(self.id) return conn.get("%s.%s" % (self.id, network_id)) get = download class NexusDatasetInfoList(object): """A read-only list-like object that can be used to retrieve the items from a Nexus search result. """ def __init__(self, connection, method, params): """Constructs a Nexus dataset list that will use the given connection and the given parameters to retrieve the search results. @param connection: a Nexus connection object @param method: the URL of the Nexus API method to call @param params: the parameters to pass in the GET requests, in the form of a Python dictionary. """ self._conn = connection self._method = str(method) self._params = params self._length = None self._datasets = [] self._blocksize = 10 def _fetch_results(self, index): """Fetches the results from Nexus such that the result item with the given index will be available (unless the result list is shorter than the given index of course).""" # Calculate the start offset page = index // self._blocksize offset = page * self._blocksize self._params["offset"] = offset self._params["limit"] = self._blocksize # Ensure that self._datasets has the necessary length diff = (page+1) * self._blocksize - len(self._datasets) if diff > 0: self._datasets.extend([None] * diff) response = self._conn._get_response(self._method, self._params) current_dataset = None for line in response: key, value = line.strip().split(": ", 1) key = key.lower() if key == "totalsize": # Total number of items in the search result self._length = int(value) elif key == "id": # Starting a new dataset if current_dataset: self._datasets[offset] = current_dataset offset += 1 current_dataset = NexusDatasetInfo(id=int(value)) current_dataset._conn = self._conn elif key == "sid": current_dataset.sid = value elif key == "name": current_dataset.name = value elif key == "vertices": current_dataset.vertices = int(value) elif key == "edges": current_dataset.edges = int(value) elif key == "vertices/edges": current_dataset.vertices_edges = value elif key == "tags": current_dataset.tags = value.split(";") if current_dataset: self._datasets[offset] = current_dataset def __getitem__(self, index): if len(self._datasets) <= index: self._fetch_results(index) elif self._datasets[index] is None: self._fetch_results(index) return self._datasets[index] def __iter__(self): for i in xrange(len(self)): yield self[i] def __len__(self): """Returns the number of result items.""" if self._length is None: self._fetch_results(0) return self._length def __str__(self): """Converts the Nexus result list into a nice human-readable format.""" max_index_length = len(str(len(self))) + 2 indent = "\n" + " " * (max_index_length+1) result = [] for index, item in enumerate(self): formatted_item = ("[%d]" % index).rjust(max_index_length) + " " + \ str(item).replace("\n", indent) result.append(formatted_item) return "\n".join(result) Nexus = NexusConnection()python-igraph-0.7.1.post6/igraph/statistics.py0000644000076500000240000005536012453614202022016 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """ Statistics related stuff in igraph """ __license__ = u"""\ Copyright (C) 2006-2012 Tamas Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ import math __all__ = ["FittedPowerLaw", "Histogram", "RunningMean", "mean", "median", \ "percentile", "quantile", "power_law_fit"] class FittedPowerLaw(object): """Result of fitting a power-law to a vector of samples Example: >>> result = power_law_fit([1, 2, 3, 4, 5, 6]) >>> result # doctest:+ELLIPSIS FittedPowerLaw(continuous=False, alpha=2.425828..., xmin=3.0, L=-7.54633..., D=0.2138..., p=0.99311...) >>> print result # doctest:+ELLIPSIS Fitted power-law distribution on discrete data Exponent (alpha) = 2.425828 Cutoff (xmin) = 3.000000 Log-likelihood = -7.546337 H0: data was drawn from the fitted distribution KS test statistic = 0.213817 p-value = 0.993111 H0 could not be rejected at significance level 0.05 >>> result.alpha # doctest:+ELLIPSIS 2.425828... >>> result.xmin 3.0 >>> result.continuous False """ def __init__(self, continuous, alpha, xmin, L, D, p): self.continuous = continuous self.xmin = xmin self.alpha = alpha self.L = L self.D = D self.p = p def __repr__(self): return "%s(continuous=%r, alpha=%r, xmin=%r, L=%r, D=%r, p=%r)" % \ (self.__class__.__name__, self.continuous, self.alpha, \ self.xmin, self.L, self.D, self.p) def __str__(self): return self.summary(significance=0.05) def summary(self, significance=0.05): """Returns the summary of the power law fit. @param significance: the significance level of the Kolmogorov-Smirnov test used to decide whether the input data could have come from the fitted distribution @return: the summary as a string """ result = ["Fitted power-law distribution on %s data" % \ ("discrete", "continuous")[bool(self.continuous)]] result.append("") result.append("Exponent (alpha) = %f" % self.alpha) result.append("Cutoff (xmin) = %f" % self.xmin) result.append("") result.append("Log-likelihood = %f" % self.L) result.append("") result.append("H0: data was drawn from the fitted distribution") result.append("") result.append("KS test statistic = %f" % self.D) result.append("p-value = %f" % self.p) result.append("") if self.p < significance: result.append("H0 rejected at significance level %g" \ % significance) else: result.append("H0 could not be rejected at significance "\ "level %g" % significance) return "\n".join(result) class Histogram(object): """Generic histogram class for real numbers Example: >>> h = Histogram(5) # Initializing, bin width = 5 >>> h << [2,3,2,7,8,5,5,0,7,9] # Adding more items >>> print h N = 10, mean +- sd: 4.8000 +- 2.9740 [ 0, 5): **** (4) [ 5, 10): ****** (6) """ def __init__(self, bin_width = 1, data = None): """Initializes the histogram with the given data set. @param bin_width: the bin width of the histogram. @param data: the data set to be used. Must contain real numbers. """ self._bin_width = float(bin_width) self._bins = None self._min, self._max = None, None self._running_mean = RunningMean() self.clear() if data: self.add_many(data) def _get_bin(self, num, create = False): """Returns the bin index corresponding to the given number. @param num: the number for which the bin is being sought @param create: whether to create a new bin if no bin exists yet. @return: the index of the bin or C{None} if no bin exists yet and {create} is C{False}.""" if len(self._bins) == 0: if not create: result = None else: self._min = int(num/self._bin_width)*self._bin_width self._max = self._min+self._bin_width self._bins = [0] result = 0 return result if num >= self._min: binidx = int((num-self._min)/self._bin_width) if binidx < len(self._bins): return binidx if not create: return None extra_bins = binidx-len(self._bins)+1 self._bins.extend([0]*extra_bins) self._max = self._min + len(self._bins)*self._bin_width return binidx if not create: return None extra_bins = int(math.ceil((self._min-num)/self._bin_width)) self._bins[0:0] = [0]*extra_bins self._min -= extra_bins*self._bin_width self._max = self._min + len(self._bins)*self._bin_width return 0 @property def n(self): """Returns the number of elements in the histogram""" return len(self._running_mean) @property def mean(self): """Returns the mean of the elements in the histogram""" return self._running_mean.mean # pylint: disable-msg=C0103 @property def sd(self): """Returns the standard deviation of the elements in the histogram""" return self._running_mean.sd @property def var(self): """Returns the variance of the elements in the histogram""" return self._running_mean.var def add(self, num, repeat=1): """Adds a single number to the histogram. @param num: the number to be added @param repeat: number of repeated additions """ num = float(num) binidx = self._get_bin(num, True) self._bins[binidx] += repeat self._running_mean.add(num, repeat) def add_many(self, data): """Adds a single number or the elements of an iterable to the histogram. @param data: the data to be added""" try: iterator = iter(data) except TypeError: iterator = iter([data]) for x in iterator: self.add(x) __lshift__ = add_many def clear(self): """Clears the collected data""" self._bins = [] self._min, self._max = None, None self._running_mean = RunningMean() def bins(self): """Generator returning the bins of the histogram in increasing order @return: a tuple with the following elements: left bound, right bound, number of elements in the bin""" x = self._min for elem in self._bins: yield (x, x+self._bin_width, elem) x += self._bin_width def __plot__(self, context, bbox, _, **kwds): """Plotting support""" from igraph.drawing.coord import DescartesCoordinateSystem coord_system = DescartesCoordinateSystem(context, bbox, \ (kwds.get("min", self._min), 0, \ kwds.get("max", self._max), kwds.get("max_value", max(self._bins)) )) # Draw the boxes context.set_line_width(1) context.set_source_rgb(1., 0., 0.) x = self._min for value in self._bins: top_left_x, top_left_y = coord_system.local_to_context(x, value) x += self._bin_width bottom_right_x, bottom_right_y = coord_system.local_to_context(x, 0) context.rectangle(top_left_x, top_left_y, \ bottom_right_x - top_left_x, \ bottom_right_y - top_left_y) context.fill() # Draw the axes coord_system.draw() def to_string(self, max_width=78, show_bars=True, show_counts=True): """Returns the string representation of the histogram. @param max_width: the maximal width of each line of the string This value may not be obeyed if it is too small. @param show_bars: specify whether the histogram bars should be shown @param show_counts: specify whether the histogram counts should be shown. If both I{show_bars} and I{show_counts} are C{False}, only a general descriptive statistics (number of elements, mean and standard deviation) is shown. """ if self._min is None or self._max is None: return "N = 0" # Determine how many decimal digits should we use if int(self._min) == self._min and int(self._bin_width) == self._bin_width: number_format = "%d" else: number_format = "%.3f" num_length = max(len(number_format % self._min), \ len(number_format % self._max)) number_format = "%" + str(num_length) + number_format[1:] format_string = "[%s, %s): %%s" % (number_format, number_format) # Calculate the scale of the bars on the histogram if show_bars: maxval = max(self._bins) if show_counts: maxval_length = len(str(maxval)) scale = maxval // (max_width-2*num_length-maxval_length-9) else: scale = maxval // (max_width-2*num_length-6) scale = max(scale, 1) result = ["N = %d, mean +- sd: %.4f +- %.4f" % \ (self.n, self.mean, self.sd)] if show_bars: # Print the bars if scale > 1: result.append("Each * represents %d items" % scale) if show_counts: format_string += " (%d)" for left, right, cnt in self.bins(): result.append(format_string % (left, right, '*'*(cnt//scale), cnt)) else: for left, right, cnt in self.bins(): result.append(format_string % (left, right, '*'*(cnt//scale))) elif show_counts: # Print the counts only for left, right, cnt in self.bins(): result.append(format_string % (left, right, cnt)) return "\n".join(result) def __str__(self): return self.to_string() class RunningMean(object): """Running mean calculator. This class can be used to calculate the mean of elements from a list, tuple, iterable or any other data source. The mean is calculated on the fly without explicitly summing the values, so it can be used for data sets with arbitrary item count. Also capable of returning the standard deviation (also calculated on the fly) """ # pylint: disable-msg=C0103 def __init__(self, items=None, n=0.0, mean=0.0, sd=0.0): """RunningMean(items=None, n=0.0, mean=0.0, sd=0.0) Initializes the running mean calculator. There are two possible ways to initialize the calculator. First, one can provide an iterable of items; alternatively, one can specify the number of items, the mean and the standard deviation if we want to continue an interrupted calculation. @param items: the items that are used to initialize the running mean calcuator. If C{items} is given, C{n}, C{mean} and C{sd} must be zeros. @param n: the initial number of elements already processed. If this is given, C{items} must be C{None}. @param mean: the initial mean. If this is given, C{items} must be C{None}. @param sd: the initial standard deviation. If this is given, C{items} must be C{None}.""" if items is not None: if n != 0 or mean != 0 or sd != 0: raise ValueError("n, mean and sd must be zeros if items is not None") self.clear() self.add_many(items) else: self._nitems = float(n) self._mean = float(mean) if n > 1: self._sqdiff = float(sd) ** 2 * float(n-1) self._sd = float(sd) else: self._sqdiff = 0.0 self._sd = 0.0 def add(self, value, repeat=1): """RunningMean.add(value, repeat=1) Adds the given value to the elements from which we calculate the mean and the standard deviation. @param value: the element to be added @param repeat: number of repeated additions """ repeat = int(repeat) self._nitems += repeat delta = value - self._mean self._mean += (repeat*delta / self._nitems) self._sqdiff += (repeat*delta) * (value - self._mean) if self._nitems > 1: self._sd = (self._sqdiff / (self._nitems-1)) ** 0.5 def add_many(self, values): """RunningMean.add(values) Adds the values in the given iterable to the elements from which we calculate the mean. Can also accept a single number. The left shift (C{<<}) operator is aliased to this function, so you can use it to add elements as well: >>> rm=RunningMean() >>> rm << [1,2,3,4] >>> rm.result # doctest:+ELLIPSIS (2.5, 1.290994...) @param values: the element(s) to be added @type values: iterable""" try: iterator = iter(values) except TypeError: iterator = iter([values]) for value in iterator: self.add(value) def clear(self): """Resets the running mean calculator.""" self._nitems, self._mean = 0.0, 0.0 self._sqdiff, self._sd = 0.0, 0.0 @property def result(self): """Returns the current mean and standard deviation as a tuple""" return self._mean, self._sd @property def mean(self): """Returns the current mean""" return self._mean @property def sd(self): """Returns the current standard deviation""" return self._sd @property def var(self): """Returns the current variation""" return self._sd ** 2 def __repr__(self): return "%s(n=%r, mean=%r, sd=%r)" % \ (self.__class__.__name__, int(self._nitems), self._mean, self._sd) def __str__(self): return "Running mean (N=%d, %f +- %f)" % \ (self._nitems, self._mean, self._sd) __lshift__ = add_many def __float__(self): return float(self._mean) def __int__(self): return int(self._mean) def __long__(self): return long(self._mean) def __complex__(self): return complex(self._mean) def __len__(self): return int(self._nitems) def mean(xs): """Returns the mean of an iterable. Example: >>> mean([1, 4, 7, 11]) 5.75 @param xs: an iterable yielding numbers. @return: the mean of the numbers provided by the iterable. @see: RunningMean() if you also need the variance or the standard deviation """ return RunningMean(xs).mean def median(xs, sort=True): """Returns the median of an unsorted or sorted numeric vector. @param xs: the vector itself. @param sort: whether to sort the vector. If you know that the vector is sorted already, pass C{False} here. @return: the median, which will always be a float, even if the vector contained integers originally. """ if sort: xs = sorted(xs) mid = int(len(xs) / 2) if 2 * mid == len(xs): return float(xs[mid-1] + xs[mid]) / 2 else: return float(xs[mid]) def percentile(xs, p=(25, 50, 75), sort=True): """Returns the pth percentile of an unsorted or sorted numeric vector. This is equivalent to calling quantile(xs, p/100.0); see L{quantile} for more details on the calculation. Example: >>> round(percentile([15, 20, 40, 35, 50], 40), 2) 26.0 >>> for perc in percentile([15, 20, 40, 35, 50], (0, 25, 50, 75, 100)): ... print "%.2f" % perc ... 15.00 17.50 35.00 45.00 50.00 @param xs: the vector itself. @param p: the percentile we are looking for. It may also be a list if you want to calculate multiple quantiles with a single call. The default value calculates the 25th, 50th and 75th percentile. @param sort: whether to sort the vector. If you know that the vector is sorted already, pass C{False} here. @return: the pth percentile, which will always be a float, even if the vector contained integers originally. If p is a list, the result will also be a list containing the percentiles for each item in the list. """ if hasattr(p, "__iter__"): return quantile(xs, (x/100.0 for x in p), sort) return quantile(xs, p/100.0, sort) def power_law_fit(data, xmin=None, method="auto", return_alpha_only=False): """Fitting a power-law distribution to empirical data @param data: the data to fit, a list containing integer values @param xmin: the lower bound for fitting the power-law. If C{None}, the optimal xmin value will be estimated as well. Zero means that the smallest possible xmin value will be used. @param method: the fitting method to use. The following methods are implemented so far: - C{continuous}, C{hill}: exact maximum likelihood estimation when the input data comes from a continuous scale. This is known as the Hill estimator. The statistical error of this estimator is M{(alpha-1) / sqrt(n)}, where alpha is the estimated exponent and M{n} is the number of data points above M{xmin}. The estimator is known to exhibit a small finite sample-size bias of order M{O(n^-1)}, which is small when M{n > 100}. igraph will try to compensate for the finite sample size if n is small. - C{discrete}: exact maximum likelihood estimation when the input comes from a discrete scale (see Clauset et al among the references). - C{auto}: exact maximum likelihood estimation where the continuous method is used if the input vector contains at least one fractional value and the discrete method is used if the input vector contains integers only. @return: a L{FittedPowerLaw} object. The fitted C{xmin} value and the power-law exponent can be queried from the C{xmin} and C{alpha} properties of the returned object. @newfield ref: Reference @ref: MEJ Newman: Power laws, Pareto distributions and Zipf's law. Contemporary Physics 46, 323-351 (2005) @ref: A Clauset, CR Shalizi, MEJ Newman: Power-law distributions in empirical data. E-print (2007). arXiv:0706.1062""" from igraph._igraph import _power_law_fit if xmin is None or xmin < 0: xmin = -1 method = method.lower() if method not in ("continuous", "hill", "discrete", "auto"): raise ValueError("unknown method: %s" % method) force_continuous = method in ("continuous", "hill") fit = FittedPowerLaw(*_power_law_fit(data, xmin, force_continuous)) if return_alpha_only: from igraph import deprecated deprecated("The return_alpha_only keyword argument of power_law_fit is "\ "deprecated from igraph 0.7 and will be removed in igraph 0.8") return fit.alpha else: return fit def quantile(xs, q=(0.25, 0.5, 0.75), sort=True): """Returns the qth quantile of an unsorted or sorted numeric vector. There are a number of different ways to calculate the sample quantile. The method implemented by igraph is the one recommended by NIST. First we calculate a rank n as q(N+1), where N is the number of items in xs, then we split n into its integer component k and decimal component d. If k <= 1, we return the first element; if k >= N, we return the last element, otherwise we return the linear interpolation between xs[k-1] and xs[k] using a factor d. Example: >>> round(quantile([15, 20, 40, 35, 50], 0.4), 2) 26.0 @param xs: the vector itself. @param q: the quantile we are looking for. It may also be a list if you want to calculate multiple quantiles with a single call. The default value calculates the 25th, 50th and 75th percentile. @param sort: whether to sort the vector. If you know that the vector is sorted already, pass C{False} here. @return: the qth quantile, which will always be a float, even if the vector contained integers originally. If q is a list, the result will also be a list containing the quantiles for each item in the list. """ if not xs: raise ValueError("xs must not be empty") if sort: xs = sorted(xs) if hasattr(q, "__iter__"): qs = q return_single = False else: qs = [q] return_single = True result = [] for q in qs: if q < 0 or q > 1: raise ValueError("q must be between 0 and 1") n = float(q) * (len(xs)+1) k, d = int(n), n-int(n) if k >= len(xs): result.append(xs[-1]) elif k < 1: result.append(xs[0]) else: result.append((1-d) * xs[k-1] + d * xs[k]) if return_single: result = result[0] return result def sd(xs): """Returns the standard deviation of an iterable. Example: >>> sd([1, 4, 7, 11]) #doctest:+ELLIPSIS 4.2720... @param xs: an iterable yielding numbers. @return: the standard deviation of the numbers provided by the iterable. @see: RunningMean() if you also need the mean """ return RunningMean(xs).sd def var(xs): """Returns the variance of an iterable. Example: >>> var([1, 4, 8, 11]) #doctest:+ELLIPSIS 19.333333... @param xs: an iterable yielding numbers. @return: the variance of the numbers provided by the iterable. @see: RunningMean() if you also need the mean """ return RunningMean(xs).var python-igraph-0.7.1.post6/igraph/summary.py0000644000076500000240000003477112524461072021330 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """Summary representation of a graph. @undocumented: _get_wrapper_for_width, FakeWrapper """ from igraph.vendor import vendor_import from igraph.statistics import median from itertools import islice from math import ceil from textwrap import TextWrapper __all__ = ["GraphSummary"] __license__ = u"""\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ texttable = vendor_import("texttable") class FakeWrapper(object): """Object whose interface is compatible with C{textwrap.TextWrapper} but does no wrapping.""" def __init__(self, *args, **kwds): pass def fill(self, text): return [text] def wrap(self, text): return [text] def _get_wrapper_for_width(width, *args, **kwds): """Returns a text wrapper that wraps text for the given width. @param width: the maximal width of each line that the text wrapper produces. C{None} means that no wrapping will be performed. """ if width is None: return FakeWrapper(*args, **kwds) return TextWrapper(width=width, *args, **kwds) class GraphSummary(object): """Summary representation of a graph. The summary representation includes a header line and the list of edges. The header line consists of C{IGRAPH}, followed by a four-character long code, the number of vertices, the number of edges, two dashes (C{--}) and the name of the graph (i.e. the contents of the C{name} attribute, if any). For instance, a header line may look like this:: IGRAPH U--- 4 5 -- The four-character code describes some basic properties of the graph. The first character is C{U} if the graph is undirected, C{D} if it is directed. The second letter is C{N} if the graph has a vertex attribute called C{name}, or a dash otherwise. The third letter is C{W} if the graph is weighted (i.e. it has an edge attribute called C{weight}), or a dash otherwise. The fourth letter is C{B} if the graph has a vertex attribute called C{type}; this is usually used for bipartite graphs. Edges may be presented as an ordinary edge list or an adjacency list. By default, this depends on the number of edges; however, you can control it with the appropriate constructor arguments. @undocumented: _construct_edgelist_adjlist, _construct_edgelist_compressed, _construct_edgelist_edgelist, _construct_graph_attributes, _construct_vertex_attributes, _construct_header, _edge_attribute_iterator, _infer_column_alignment, _new_table, _vertex_attribute_iterator """ def __init__(self, graph, verbosity=0, width=78, edge_list_format="auto", max_rows=99999, print_graph_attributes=False, print_vertex_attributes=False, print_edge_attributes=False, full=False): """Constructs a summary representation of a graph. @param verbosity: the verbosity of the summary. If zero, only the header line will be returned. If one, the header line and the list of edges will both be returned. @param width: the maximal width of each line in the summary. C{None} means that no limit will be enforced. @param max_rows: the maximal number of rows to print in a single table (e.g., vertex attribute table or edge attribute table) @param edge_list_format: format of the edge list in the summary. Supported formats are: C{compressed}, C{adjlist}, C{edgelist}, C{auto}, which selects automatically from the other three based on some simple criteria. @param print_graph_attributes: whether to print graph attributes if there are any. @param print_vertex_attributes: whether to print vertex attributes if there are any. @param print_edge_attributes: whether to print edge attributes if there are any. @param full: False has no effect; True turns on the attribute printing for graph, vertex and edge attributes with verbosity 1. """ if full: print_graph_attributes = True print_vertex_attributes = True print_edge_attributes = True verbosity = max(verbosity, 1) self._graph = graph self.edge_list_format = edge_list_format.lower() self.max_rows = int(max_rows) self.print_graph_attributes = print_graph_attributes self.print_vertex_attributes = print_vertex_attributes self.print_edge_attributes = print_edge_attributes self.verbosity = verbosity self.width = width self.wrapper = _get_wrapper_for_width(self.width, break_on_hyphens=False) if self._graph.is_named(): self._edges_header = "+ edges (vertex names):" else: self._edges_header = "+ edges:" self._arrow = ["--", "->"][self._graph.is_directed()] self._arrow_format = "%%s%s%%s" % self._arrow def _construct_edgelist_adjlist(self): """Constructs the part in the summary that prints the edge list in an adjacency list format.""" result = [self._edges_header] arrow = self._arrow_format if self._graph.vcount() == 0: return if self._graph.is_named(): names = self._graph.vs["name"] maxlen = max(len(name) for name in names) format_str = "%%%ds %s %%s" % (maxlen, self._arrow) for v1, name in enumerate(names): neis = self._graph.successors(v1) neis = ", ".join(str(names[v2]) for v2 in neis) result.append(format_str % (name, neis)) else: maxlen = len(str(self._graph.vcount())) num_format = "%%%dd" % maxlen format_str = "%s %s %%s" % (num_format, self._arrow) for v1 in xrange(self._graph.vcount()): neis = self._graph.successors(v1) neis = " ".join(num_format % v2 for v2 in neis) result.append(format_str % (v1, neis)) # Try to wrap into multiple columns if that works with the given width if self.width is not None: maxlen = max(len(line) for line in result[1:]) colcount = int(self.width + 3) / int(maxlen + 3) if colcount > 1: # Rewrap to multiple columns nrows = len(result) - 1 colheight = int(ceil(nrows / float(colcount))) newrows = [[] for _ in xrange(colheight)] for i, row in enumerate(result[1:]): newrows[i % colheight].append(row.ljust(maxlen)) result[1:] = [" ".join(row) for row in newrows] return result def _construct_edgelist_compressed(self): """Constructs the part in the summary that prints the edge list in a compressed format suitable for graphs with mostly small degrees.""" result = [self._edges_header] arrow = self._arrow_format if self._graph.is_named(): names = self._graph.vs["name"] edges = ", ".join(arrow % (names[edge.source], names[edge.target]) for edge in self._graph.es) else: edges = " ".join(arrow % edge.tuple for edge in self._graph.es) result.append(edges) return result def _construct_edgelist_edgelist(self): """Constructs the part in the summary that prints the edge list in a full edge list format.""" attrs = sorted(self._graph.edge_attributes()) table = self._new_table(headers=["", "edge"] + attrs) table.add_rows(islice(self._edge_attribute_iterator(attrs), 0, self.max_rows), header=False) table.set_cols_align(["l", "l"] + self._infer_column_alignment(edge_attrs=attrs)) result = [self._edges_header] result.extend(table.draw().split("\n")) return result def _construct_graph_attributes(self): """Constructs the part in the summary that lists the graph attributes.""" attrs = self._graph.attributes() if not attrs: return [] result = ["+ graph attributes:"] attrs.sort() for attr in attrs: result.append("[[%s]]" % (attr, )) result.append(str(self._graph[attr])) return result def _construct_vertex_attributes(self): """Constructs the part in the summary that lists the vertex attributes.""" attrs = sorted(self._graph.vertex_attributes()) if not attrs or (len(attrs) == 1 and "name" in attrs): return [] table = self._new_table(headers=[""] + attrs) table.add_rows(islice(self._vertex_attribute_iterator(attrs), 0, self.max_rows), header=False) table.set_cols_align(["l"] + self._infer_column_alignment(vertex_attrs=attrs)) result = ["+ vertex attributes:"] result.extend(table.draw().split("\n")) return result def _construct_header(self): """Constructs the header part of the summary.""" graph = self._graph params = dict( directed="UD"[graph.is_directed()], named="-N"[graph.is_named()], weighted="-W"[graph.is_weighted()], typed="-T"["type" in graph.vertex_attributes()], vcount=graph.vcount(), ecount=graph.ecount(), ) if "name" in graph.attributes(): params["name"] = graph["name"] else: params["name"] = "" result = ["IGRAPH %(directed)s%(named)s%(weighted)s%(typed)s "\ "%(vcount)d %(ecount)d -- %(name)s" % params] attrs = ["%s (g)" % (name, ) for name in sorted(graph.attributes())] attrs.extend("%s (v)" % (name, ) for name in sorted(graph.vertex_attributes())) attrs.extend("%s (e)" % (name, ) for name in sorted(graph.edge_attributes())) if attrs: result.append("+ attr: %s" % ", ".join(attrs)) if self.wrapper is not None: self.wrapper.subsequent_indent = ' ' result[-1:] = self.wrapper.wrap(result[-1]) self.wrapper.subsequent_indent = '' return result def _edge_attribute_iterator(self, attribute_order): """Returns an iterator that yields the rows of the edge attribute table in the summary. `attribute_order` must be a list containing the names of the attributes to be presented in this table.""" arrow = self._arrow_format if self._graph.is_named(): names = self._graph.vs["name"] for edge in self._graph.es: formatted_edge = arrow % (names[edge.source], names[edge.target]) yield ["[%d]" % edge.index, formatted_edge] + \ [edge[attr] for attr in attribute_order] else: for edge in self._graph.es: formatted_edge = arrow % edge.tuple yield ["[%d]" % edge.index, formatted_edge] + \ [edge[attr] for attr in attribute_order] def _infer_column_alignment(self, vertex_attrs=None, edge_attrs=None): """Infers the preferred alignment for the given vertex and edge attributes in the tables by peeking into the attribute values of the first 100 vertices or edges. Numeric attributes will be aligned right, everything else will be aligned left.""" values = [] if vertex_attrs is not None: vs = self._graph.vs[:100] values.extend(vs[attr] for attr in vertex_attrs) if edge_attrs is not None: es = self._graph.es[:100] values.extend(es[attr] for attr in edge_attrs) result = [] for vs in values: is_numeric = True try: [float(x) for x in vs] except ValueError: is_numeric = False if is_numeric: result.append("r") else: result.append("l") return result def _new_table(self, headers=None): """Constructs a new table to pretty-print vertex and edge attributes""" table = texttable.Texttable(max_width=0) table.set_deco(0) if headers is not None: table.header(headers) return table def _vertex_attribute_iterator(self, attribute_order): """Returns an iterator that yields the rows of the vertex attribute table in the summary. `attribute_order` must be a list containing the names of the attributes to be presented in this table.""" for vertex in self._graph.vs: yield ["[%d]" % vertex.index] + [vertex[attr] for attr in attribute_order] def __str__(self): """Returns the summary representation as a string.""" output = self._construct_header() if self.print_graph_attributes: output.extend(self._construct_graph_attributes()) if self.print_vertex_attributes: output.extend(self._construct_vertex_attributes()) if self.verbosity <= 0: return "\n".join(output) if self._graph.ecount() > 0: # Add the edge list if self.edge_list_format == "auto": if (self.print_edge_attributes and self._graph.edge_attributes()): format = "edgelist" elif median(self._graph.degree(mode="out")) < 3: format = "compressed" else: format = "adjlist" else: format = self.edge_list_format method_name = "_construct_edgelist_%s" % format if hasattr(self, method_name): output.extend(getattr(self, method_name)()) if self.wrapper is not None: return "\n".join("\n".join(self.wrapper.wrap(line)) for line in output) return "\n".join(output) python-igraph-0.7.1.post6/igraph/test/0000755000076500000240000000000012534343010020214 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/igraph/test/__init__.py0000644000076500000240000000254312534342712022341 0ustar ntamasstaff00000000000000import unittest from igraph.test import basic, layouts, games, foreign, structural, flow, \ spectral, attributes, cliques, decomposition, operators, generators, \ isomorphism, colortests, vertexseq, edgeseq, iterators, bipartite, \ conversion, rng, separators, indexing, atlas, matching, homepage def suite(): return unittest.TestSuite([ basic.suite(), layouts.suite(), generators.suite(), games.suite(), foreign.suite(), structural.suite(), flow.suite(), spectral.suite(), attributes.suite(), vertexseq.suite(), edgeseq.suite(), cliques.suite(), decomposition.suite(), conversion.suite(), operators.suite(), isomorphism.suite(), iterators.suite(), bipartite.suite(), colortests.suite(), rng.suite(), separators.suite(), indexing.suite(), atlas.suite(), matching.suite(), homepage.suite() ]) def run_tests(verbosity=1): try: # Support for testoob to have nice colored output import testoob runner = testoob.main except ImportError: runner = unittest.TextTestRunner(verbosity=verbosity).run runner(suite()) # Make nosetest skip run_tests run_tests.__test__ = False if __name__ == "__main__": run_tests(255) python-igraph-0.7.1.post6/igraph/test/atlas.py0000644000076500000240000001036612453614202021704 0ustar ntamasstaff00000000000000from __future__ import division import warnings import unittest from igraph import * class TestBase(unittest.TestCase): def testPageRank(self): for idx, g in enumerate(self.__class__.graphs): try: pr = g.pagerank() except Exception, ex: self.assertTrue(False, msg="PageRank calculation threw exception for graph #%d: %s" % (idx, ex)) raise if g.vcount() == 0: self.assertEqual([], pr) continue self.assertAlmostEqual(1.0, sum(pr), places=5, \ msg="PageRank sum is not 1.0 for graph #%d (%r)" % (idx, pr)) self.assertTrue(min(pr) >= 0, \ msg="Minimum PageRank is less than 0 for graph #%d (%r)" % (idx, pr)) def testEigenvectorCentrality(self): # Temporarily turn off the warning handler because g.evcent() will print # a warning for DAGs warnings.simplefilter("ignore") try: for idx, g in enumerate(self.__class__.graphs): try: ec, eval = g.evcent(return_eigenvalue=True) except Exception, ex: self.assertTrue(False, msg="Eigenvector centrality threw exception for graph #%d: %s" % (idx, ex)) raise if g.vcount() == 0: self.assertEqual([], ec) continue if not g.is_connected(): # Skip disconnected graphs; this will be fixed in igraph 0.7 continue n = g.vcount() if abs(eval) < 1e-4: self.assertTrue(min(ec) >= -1e-10, msg="Minimum eigenvector centrality is smaller than 0 for graph #%d" % idx) self.assertTrue(max(ec) <= 1, msg="Maximum eigenvector centrality is greater than 1 for graph #%d" % idx) continue self.assertAlmostEqual(max(ec), 1, places=7, \ msg="Maximum eigenvector centrality is %r (not 1) for graph #%d (%r)" % \ (max(ec), idx, ec)) self.assertTrue(min(ec) >= 0, \ msg="Minimum eigenvector centrality is less than 0 for graph #%d" % idx) ec2 = [sum(ec[u.index] for u in v.predecessors()) for v in g.vs] for i in xrange(n): self.assertAlmostEqual(ec[i] * eval, ec2[i], places=7, \ msg="Eigenvector centrality in graph #%d seems to be invalid "\ "for vertex %d" % (idx, i)) finally: # Reset the warning handler warnings.resetwarnings() def testHubScore(self): for idx, g in enumerate(self.__class__.graphs): sc = g.hub_score() if g.vcount() == 0: self.assertEqual([], sc) continue self.assertAlmostEqual(max(sc), 1, places=7, \ msg="Maximum authority score is not 1 for graph #%d" % idx) self.assertTrue(min(sc) >= 0, \ msg="Minimum hub score is less than 0 for graph #%d" % idx) def testAuthorityScore(self): for idx, g in enumerate(self.__class__.graphs): sc = g.authority_score() if g.vcount() == 0: self.assertEqual([], sc) continue self.assertAlmostEqual(max(sc), 1, places=7, \ msg="Maximum authority score is not 1 for graph #%d" % idx) self.assertTrue(min(sc) >= 0, \ msg="Minimum authority score is less than 0 for graph #%d" % idx) class GraphAtlasTests(TestBase): graphs = [Graph.Atlas(i) for i in xrange(1253)] class IsoclassTests(TestBase): graphs = [Graph.Isoclass(3, i, directed=True) for i in xrange(16)] + \ [Graph.Isoclass(4, i, directed=True) for i in xrange(218)] def suite(): atlas_suite = unittest.makeSuite(GraphAtlasTests) isoclass_suite = unittest.makeSuite(IsoclassTests) return unittest.TestSuite([atlas_suite, isoclass_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/attributes.py0000644000076500000240000002225412460534506022773 0ustar ntamasstaff00000000000000# vim:ts=4 sw=4 sts=4: import unittest from igraph import * class AttributeTests(unittest.TestCase): def testGraphAttributes(self): g = Graph.Full(5) g["date"] = "2005-12-17" self.assertTrue(g["date"] == "2005-12-17") del g["date"] self.assertRaises(KeyError, g.__getitem__, "date") def testVertexAttributes(self): g = Graph.Full(5) g.vs[0]["name"] = "first" self.assertTrue(g.vs[0]["name"] == "first") del g.vs["name"] self.assertRaises(KeyError, g.vs.__getitem__, "name") g.vs[0]["name"] = "second" g.vs[0]["date"] = "2007-12-17" ans = g.vs[0].attribute_names() ans.sort() self.assertTrue(ans == ["date", "name"]) attrs = g.vs[0].attributes() self.assertTrue(attrs == {"name": "second", "date": "2007-12-17"}) def testEdgeAttributes(self): g = Graph.Full(5) g.es[0]["name"] = "first" self.assertTrue(g.es[0]["name"] == "first") del g.es["name"] self.assertRaises(KeyError, g.es.__getitem__, "name") g.es[0]["name"] = "second" g.es[0]["date"] = "2007-12-17" ans = g.es[0].attribute_names() ans.sort() self.assertTrue(ans == ["date", "name"]) attrs = g.es[0].attributes() self.assertTrue(attrs == {"name": "second", "date": "2007-12-17"}) def testMassVertexAttributeAssignment(self): g = Graph.Full(5) g.vs.set_attribute_values("name", range(5)) self.assertTrue(g.vs.get_attribute_values("name") == range(5)) g.vs["name"] = range(5,10) self.assertTrue(g.vs["name"] == range(5,10)) g.vs["name2"] = (1,2,3,4,6) self.assertTrue(g.vs["name2"] == [1,2,3,4,6]) g.vs.set_attribute_values("name", [2]) self.assertTrue(g.vs["name"] == [2]*5) def testMassEdgeAttributeAssignment(self): g = Graph.Full(5) g.es.set_attribute_values("name", range(10)) self.assertTrue(g.es.get_attribute_values("name") == range(10)) g.es["name"] = range(10,20) self.assertTrue(g.es["name"] == range(10,20)) g.es["name2"] = (1,2,3,4,6,1,2,3,4,6) self.assertTrue(g.es["name2"] == [1,2,3,4,6,1,2,3,4,6]) g.es.set_attribute_values("name", [2]) self.assertTrue(g.es["name"] == [2]*10) def testVertexNameIndexing(self): g = Graph.Famous("bull") g.vs["name"] = ["foo", "bar", "baz", "fred", "thud"] self.assertTrue(g.degree("bar") == 3) self.assertTrue(g.degree(["bar", "fred", 0]) == [3, 1, 2]) g.vs[2]["name"] = "quack" self.assertRaises(ValueError, g.degree, "baz") self.assertTrue(g.degree("quack") == 3) self.assertTrue(g.degree(u"quack") == 3) self.assertTrue(g.degree([u"bar", u"thud", 0]) == [3, 1, 2]) del g.vs["name"] self.assertRaises(ValueError, g.degree, [u"bar", u"thud", 0]) def testInvalidAttributeNames(self): g = Graph.Famous("bull") for attr_name in [None, 2.654, unittest, str]: self.assertRaises(TypeError, g.vs.__setitem__, attr_name, "foo") self.assertRaises(TypeError, g.vs.__getitem__, attr_name, "foo") self.assertRaises(TypeError, g.vs[0].__setitem__, attr_name, "foo") self.assertRaises(TypeError, g.vs[0].__getitem__, attr_name, "foo") self.assertRaises(TypeError, g.es.__setitem__, attr_name, "foo") self.assertRaises(TypeError, g.es.__getitem__, attr_name, "foo") self.assertRaises(TypeError, g.es[0].__setitem__, attr_name, "foo") self.assertRaises(TypeError, g.es[0].__getitem__, attr_name, "foo") class AttributeCombinationTests(unittest.TestCase): def setUp(self): el = [(0,1), (1,0), (1,2), (2,3), (2,3), (2,3), (3,3)] self.g = Graph(el) self.g.es["weight"] = [1, 2, 3, 4, 5, 6, 7] self.g.es["weight2"] = [1, 2, 3, 4, 5, 6, 7] def testCombinationMax(self): g = self.g g.simplify(combine_edges="max") self.assertTrue(g.es["weight"] == [2, 3, 6]) self.assertTrue(g.es["weight2"] == [2, 3, 6]) def testCombinationMin(self): g = self.g g.simplify(combine_edges="min") self.assertTrue(g.es["weight"] == [1, 3, 4]) self.assertTrue(g.es["weight2"] == [1, 3, 4]) def testCombinationRandom(self): g = self.g g.simplify(combine_edges="random") del g.es["weight2"] for i in xrange(100): self.assertTrue(g.es[0]["weight"] in (1, 2)) self.assertTrue(g.es[1]["weight"] == 3) self.assertTrue(g.es[2]["weight"] in (4, 5, 6)) def testCombinationMean(self): g = self.g g.simplify(combine_edges="mean") self.assertTrue(g.es["weight"] == [1.5, 3, 5]) self.assertTrue(g.es["weight2"] == [1.5, 3, 5]) def testCombinationMedian(self): g = self.g g.es["weight2"] = [1, 0, 2, 4, 8, 6, 7] g.simplify(combine_edges="median") self.assertTrue(g.es["weight"] == [1.5, 3, 5]) self.assertTrue(g.es["weight2"] == [0.5, 2, 6]) def testCombinationSum(self): g = self.g g.simplify(combine_edges="sum") self.assertTrue(g.es["weight"] == [3, 3, 15]) self.assertTrue(g.es["weight2"] == [3, 3, 15]) def testCombinationProd(self): g = self.g g.simplify(combine_edges="prod") self.assertTrue(g.es["weight"] == [2, 3, 120]) self.assertTrue(g.es["weight2"] == [2, 3, 120]) def testCombinationMedian(self): g = self.g g.es["weight2"] = [1, 0, 2, 4, 8, 6, 7] g.simplify(combine_edges="median") self.assertTrue(g.es["weight"] == [1.5, 3, 5]) self.assertTrue(g.es["weight2"] == [0.5, 2, 6]) def testCombinationFirst(self): g = self.g g.es["weight2"] = [1, 0, 2, 6, 8, 4, 7] g.simplify(combine_edges="first") self.assertTrue(g.es["weight"] == [1, 3, 4]) self.assertTrue(g.es["weight2"] == [1, 2, 6]) def testCombinationLast(self): g = self.g g.es["weight2"] = [1, 0, 2, 6, 8, 4, 7] g.simplify(combine_edges="last") self.assertTrue(g.es["weight"] == [2, 3, 6]) self.assertTrue(g.es["weight2"] == [0, 2, 4]) def testCombinationConcat(self): g = self.g g.es["name"] = list("ABCDEFG") g.simplify(combine_edges=dict(name="concat")) self.assertFalse("weight" in g.edge_attributes()) self.assertFalse("weight2" in g.edge_attributes()) self.assertTrue(g.es["name"] == ["AB", "C", "DEF"]) def testCombinationMaxMinIgnore(self): g = self.g g.es["name"] = list("ABCDEFG") g.simplify(combine_edges={"weight": "min", "weight2": "max", "name": "ignore"}) self.assertTrue(g.es["weight"] == [1, 3, 4]) self.assertTrue(g.es["weight2"] == [2, 3, 6]) self.assertFalse("name" in g.edge_attributes()) def testCombinationIgnoreAsNone(self): g = self.g g.es["name"] = list("ABCDEFG") g.simplify(combine_edges={"weight": "min", "name": None}) self.assertTrue(g.es["weight"] == [1, 3, 4]) self.assertFalse("weight2" in g.edge_attributes()) self.assertFalse("name" in g.edge_attributes()) def testCombinationFunction(self): g = self.g def join_dash(l): return "-".join(l) g.es["name"] = list("ABCDEFG") g.simplify(combine_edges={"weight": max, "name": join_dash}) self.assertTrue(g.es["weight"] == [2, 3, 6]) self.assertFalse("weight2" in g.edge_attributes()) self.assertTrue(g.es["name"] == ["A-B", "C", "D-E-F"]) def testCombinationDefault(self): g = self.g g.simplify(combine_edges={None: "max"}) self.assertTrue(g.es["weight"] == [2, 3, 6]) self.assertTrue(g.es["weight2"] == [2, 3, 6]) def testCombinationDefaultOverride(self): g = self.g g.simplify(combine_edges={None: "max", "weight": "sum"}) self.assertTrue(g.es["weight"] == [3, 3, 15]) self.assertTrue(g.es["weight2"] == [2, 3, 6]) def testCombinationTypeMismatch(self): g = self.g g.es["weight"] = list("ABCDEFG") self.assertRaises(TypeError, g.simplify, combine_edges={"weight": "mean"}) def testCombinationNonexistentAttribute(self): g = self.g g.simplify(combine_edges={"nonexistent": max}) self.assertTrue(g.edge_attributes() == []) def testCombinationNone(self): g = self.g g.simplify() self.assertTrue(sorted(g.edge_attributes()) == []) class UnicodeAttributeTests(unittest.TestCase): def testUnicodeAttributeNameCombination(self): g = Graph.Erdos_Renyi(n=9, m=20) g.es[u"test"] = 1 g.contract_vertices([0,0,0,1,1,1,2,2,2]) def suite(): attribute_suite = unittest.makeSuite(AttributeTests) attribute_combination_suite = unittest.makeSuite(AttributeCombinationTests) unicode_attributes_suite = unittest.makeSuite(UnicodeAttributeTests) return unittest.TestSuite([attribute_suite, attribute_combination_suite, unicode_attributes_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/basic.py0000644000076500000240000005001312460535606021662 0ustar ntamasstaff00000000000000import unittest from igraph import * class BasicTests(unittest.TestCase): def testGraphCreation(self): g = Graph() self.assertTrue(isinstance(g, Graph)) self.assertTrue(g.vcount() == 0 and g.ecount() == 0 and g.is_directed() == False) g=Graph(3, [(0,1), (1,2), (2,0)]) self.assertTrue(g.vcount() == 3 and g.ecount() == 3 and g.is_directed() == False and g.is_simple() == True) g=Graph(2, [(0,1), (1,2), (2,3)], True) self.assertTrue(g.vcount() == 4 and g.ecount() == 3 and g.is_directed() == True and g.is_simple()) g=Graph([(0,1), (1,2), (2,1)]) self.assertTrue(g.vcount() == 3 and g.ecount() == 3 and g.is_directed() == False and g.is_simple() == False) g=Graph([(0,1), (0,0), (1,2)]) self.assertTrue(g.vcount() == 3 and g.ecount() == 3 and g.is_directed() == False and g.is_simple() == False) g=Graph(8, None) self.assertEqual(8, g.vcount()) self.assertEqual(0, g.ecount()) self.assertFalse(g.is_directed()) g=Graph(edges=None) self.assertEqual(0, g.vcount()) self.assertEqual(0, g.ecount()) self.assertFalse(g.is_directed()) self.assertRaises(TypeError, Graph, edgelist=[(1,2)]) def testAddVertex(self): g = Graph() g.add_vertex() self.assertTrue(g.vcount() == 1 and g.ecount() == 0) self.assertFalse("name" in g.vertex_attributes()) g.add_vertex("foo") self.assertTrue(g.vcount() == 2 and g.ecount() == 0) self.assertTrue("name" in g.vertex_attributes()) self.assertEqual(g.vs["name"], [None, "foo"]) g.add_vertex(3) self.assertTrue(g.vcount() == 3 and g.ecount() == 0) self.assertTrue("name" in g.vertex_attributes()) self.assertEqual(g.vs["name"], [None, "foo", 3]) g.add_vertex(name="bar") self.assertTrue(g.vcount() == 4 and g.ecount() == 0) self.assertTrue("name" in g.vertex_attributes()) self.assertEqual(g.vs["name"], [None, "foo", 3, "bar"]) g.add_vertex(name="frob", spam="cheese", ham=42) self.assertTrue(g.vcount() == 5 and g.ecount() == 0) self.assertEqual(sorted(g.vertex_attributes()), ["ham", "name", "spam"]) self.assertEqual(g.vs["spam"], [None]*4 + ["cheese"]) self.assertEqual(g.vs["ham"], [None]*4 + [42]) def testAddVertices(self): g = Graph() g.add_vertices(2) self.assertTrue(g.vcount() == 2 and g.ecount() == 0) g.add_vertices("spam") self.assertTrue(g.vcount() == 3 and g.ecount() == 0) self.assertEqual(g.vs[2]["name"], "spam") g.add_vertices(["bacon", "eggs"]) self.assertTrue(g.vcount() == 5 and g.ecount() == 0) self.assertEqual(g.vs[2:]["name"], ["spam", "bacon", "eggs"]) def testDeleteVertices(self): g = Graph([(0,1), (1,2), (2,3), (0,2), (3,4), (4,5)]) self.assertEqual(6, g.vcount()) self.assertEqual(6, g.ecount()) # Delete a single vertex g.delete_vertices(4) self.assertEqual(5, g.vcount()) self.assertEqual(4, g.ecount()) # Delete multiple vertices g.delete_vertices([1,3]) self.assertEqual(3, g.vcount()) self.assertEqual(1, g.ecount()) # Delete a vertex sequence g.delete_vertices(g.vs[:2]) self.assertEqual(1, g.vcount()) self.assertEqual(0, g.ecount()) # Delete a single vertex object g.vs[0].delete() self.assertEqual(0, g.vcount()) self.assertEqual(0, g.ecount()) # Delete vertices by name g = Graph.Full(4) g.vs["name"] = ["spam", "bacon", "eggs", "ham"] self.assertEqual(4, g.vcount()) g.delete_vertices("spam") self.assertEqual(3, g.vcount()) g.delete_vertices(["bacon", "ham"]) self.assertEqual(1, g.vcount()) # Deleting a nonexistent vertex self.assertRaises(ValueError, g.delete_vertices, "no-such-vertex") self.assertRaises(InternalError, g.delete_vertices, 2) def testAddEdges(self): g = Graph() g.add_vertices(["spam", "bacon", "eggs", "ham"]) g.add_edges([(0, 1)]) self.assertEqual(g.vcount(), 4) self.assertEqual(g.get_edgelist(), [(0, 1)]) g.add_edges([(1, 2), (2, 3), (1, 3)]) self.assertEqual(g.vcount(), 4) self.assertEqual(g.get_edgelist(), [(0, 1), (1, 2), (2, 3), (1, 3)]) g.add_edges([("spam", "eggs"), ("spam", "ham")]) self.assertEqual(g.vcount(), 4) self.assertEqual(g.get_edgelist(), [(0, 1), (1, 2), (2, 3), (1, 3), (0, 2), (0, 3)]) def testDeleteEdges(self): g = Graph.Famous("petersen") g.vs["name"] = list("ABCDEFGHIJ") el = g.get_edgelist() self.assertEqual(15, g.ecount()) # Deleting single edge g.delete_edges(14) el[14:] = [] self.assertEqual(14, g.ecount()) self.assertEqual(el, g.get_edgelist()) # Deleting multiple edges g.delete_edges([2,5,7]) el[7:8] = []; el[5:6] = []; el[2:3] = [] self.assertEqual(11, g.ecount()) self.assertEqual(el, g.get_edgelist()) # Deleting edge object g.es[6].delete() el[6:7] = [] self.assertEqual(10, g.ecount()) self.assertEqual(el, g.get_edgelist()) # Deleting edge sequence object g.es[1:4].delete() el[1:4] = [] self.assertEqual(7, g.ecount()) self.assertEqual(el, g.get_edgelist()) # Deleting edges by IDs g.delete_edges([(2,7), (5,8)]) el[4:5] = []; el[1:2] = [] self.assertEqual(5, g.ecount()) self.assertEqual(el, g.get_edgelist()) # Deleting edges by names g.delete_edges([("D", "I"), ("G", "I")]) el[3:4] = []; el[1:2] = [] self.assertEqual(3, g.ecount()) self.assertEqual(el, g.get_edgelist()) # Deleting nonexistent edges self.assertRaises(ValueError, g.delete_edges, [(0,2)]) self.assertRaises(ValueError, g.delete_edges, [("A", "C")]) self.assertRaises(ValueError, g.delete_edges, [(0,15)]) def testGraphGetEid(self): g = Graph.Famous("petersen") g.vs["name"] = list("ABCDEFGHIJ") edges_to_ids = dict((v, k) for k, v in enumerate(g.get_edgelist())) for (source, target), edge_id in edges_to_ids.iteritems(): source_name, target_name = g.vs[(source, target)]["name"] self.assertEqual(edge_id, g.get_eid(source, target)) self.assertEqual(edge_id, g.get_eid(source_name, target_name)) self.assertRaises(InternalError, g.get_eid, 0, 11) self.assertRaises(ValueError, g.get_eid, "A", "K") def testGraphGetEids(self): g = Graph.Famous("petersen") eids = g.get_eids(pairs=[(0,1), (0,5), (1, 6), (4, 9), (8, 6)]) self.assertTrue(eids == [0, 2, 4, 9, 12]) eids = g.get_eids(path=[0, 1, 2, 3, 4]) self.assertTrue(eids == [0, 3, 5, 7]) eids = g.get_eids(pairs=[(7,9), (9,6)], path = [7, 9, 6]) self.assertTrue(eids == [14, 13, 14, 13]) self.assertRaises(InternalError, g.get_eids, pairs=[(0,1), (0,2)]) def testAdjacency(self): g=Graph(4, [(0,1), (1,2), (2,0), (2,3)], directed=True) self.assertTrue(g.neighbors(2) == [0, 1, 3]) self.assertTrue(g.predecessors(2) == [1]) self.assertTrue(g.successors(2) == [0, 3]) self.assertTrue(g.get_adjlist() == [[1], [2], [0,3], []]) self.assertTrue(g.get_adjlist(IN) == [[2], [0], [1], [2]]) self.assertTrue(g.get_adjlist(ALL) == [[1,2], [0,2], [0,1,3], [2]]) def testEdgeIncidency(self): g=Graph(4, [(0,1), (1,2), (2,0), (2,3)], directed=True) self.assertTrue(g.incident(2) == [2, 3]) self.assertTrue(g.incident(2, IN) == [1]) self.assertTrue(g.incident(2, ALL) == [2, 3, 1]) self.assertTrue(g.get_inclist() == [[0], [1], [2,3], []]) self.assertTrue(g.get_inclist(IN) == [[2], [0], [1], [3]]) self.assertTrue(g.get_inclist(ALL) == [[0,2], [1,0], [2,3,1], [3]]) def testMultiplesLoops(self): g=Graph.Tree(7, 2) # has_multiple self.assertFalse(g.has_multiple()) g.add_vertices(1) g.add_edges([(0,1), (7,7), (6,6), (6,6), (6,6)]) # is_loop self.assertTrue(g.is_loop() == [False, False, False, False, \ False, False, False, True, True, True, True]) self.assertTrue(g.is_loop(g.ecount()-2)) self.assertTrue(g.is_loop(range(6,8)) == [False, True]) # is_multiple self.assertTrue(g.is_multiple() == [False, False, False, False, \ False, False, True, False, False, True, True]) # has_multiple self.assertTrue(g.has_multiple()) # count_multiple self.assertTrue(g.count_multiple() == [2, 1, 1, 1, 1, 1, 2, 1, 3, 3, 3]) self.assertTrue(g.count_multiple(g.ecount()-1) == 3) self.assertTrue(g.count_multiple(range(2,5)) == [1, 1, 1]) # check if a mutual directed edge pair is reported as multiple g=Graph(2, [(0,1), (1,0)], directed=True) self.assertTrue(g.is_multiple() == [False, False]) def testPickling(self): import pickle g=Graph([(0,1), (1,2)]) g["data"]="abcdef" g.vs["data"]=[3,4,5] g.es["data"]=["A", "B"] g.custom_data = None pickled=pickle.dumps(g) g2=pickle.loads(pickled) self.assertTrue(g["data"] == g2["data"]) self.assertTrue(g.vs["data"] == g2.vs["data"]) self.assertTrue(g.es["data"] == g2.es["data"]) self.assertTrue(g.vcount() == g2.vcount()) self.assertTrue(g.ecount() == g2.ecount()) self.assertTrue(g.is_directed() == g2.is_directed()) self.assertTrue(g2.custom_data == g.custom_data) class DatatypeTests(unittest.TestCase): def testMatrix(self): m = Matrix([[1,2,3], [4,5], [6,7,8]]) self.assertTrue(m.shape == (3, 3)) # Reading data self.assertTrue(m.data == [[1,2,3], [4,5,0], [6,7,8]]) self.assertTrue(m[1,1] == 5) self.assertTrue(m[0] == [1,2,3]) self.assertTrue(m[0,:] == [1,2,3]) self.assertTrue(m[:,0] == [1,4,6]) self.assertTrue(m[2,0:2] == [6,7]) self.assertTrue(m[:,:].data == [[1,2,3], [4,5,0], [6,7,8]]) self.assertTrue(m[:,1:3].data == [[2,3], [5,0], [7,8]]) # Writing data m[1,1] = 10 self.assertTrue(m[1,1] == 10) m[1] = (6,5,4) self.assertTrue(m[1] == [6,5,4]) m[1:3] = [[4,5,6], (7,8,9)] self.assertTrue(m[1:3].data == [[4,5,6], [7,8,9]]) # Minimums and maximums self.assertTrue(m.min() == 1) self.assertTrue(m.max() == 9) self.assertTrue(m.min(0) == [1,2,3]) self.assertTrue(m.max(0) == [7,8,9]) self.assertTrue(m.min(1) == [1,4,7]) self.assertTrue(m.max(1) == [3,6,9]) # Special constructors m=Matrix.Fill(2, (3,3)) self.assertTrue(m.min() == 2 and m.max() == 2 and m.shape == (3,3)) m=Matrix.Zero(5, 4) self.assertTrue(m.min() == 0 and m.max() == 0 and m.shape == (5,4)) m=Matrix.Identity(3) self.assertTrue(m.data == [[1,0,0], [0,1,0], [0,0,1]]) m=Matrix.Identity(3, 2) self.assertTrue(m.data == [[1,0], [0,1], [0,0]]) # Conversion to string m=Matrix.Identity(3) self.assertTrue(str(m) == "[[1, 0, 0]\n [0, 1, 0]\n [0, 0, 1]]") self.assertTrue(repr(m) == "Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])") class GraphDictListTests(unittest.TestCase): def setUp(self): self.vertices = [ \ {"name": "Alice", "age": 48, "gender": "F"}, {"name": "Bob", "age": 33, "gender": "M"}, {"name": "Cecil", "age": 45, "gender": "F"}, {"name": "David", "age": 34, "gender": "M"} ] self.edges = [ \ {"source": "Alice", "target": "Bob", "friendship": 4, "advice": 4}, {"source": "Cecil", "target": "Bob", "friendship": 5, "advice": 5}, {"source": "Cecil", "target": "Alice", "friendship": 5, "advice": 5}, {"source": "David", "target": "Alice", "friendship": 2, "advice": 4}, {"source": "David", "target": "Bob", "friendship": 1, "advice": 2} ] def testGraphFromDictList(self): g = Graph.DictList(self.vertices, self.edges) self.checkIfOK(g, "name") g = Graph.DictList(self.vertices, self.edges, iterative=True) self.checkIfOK(g, "name") def testGraphFromDictIterator(self): g = Graph.DictList(iter(self.vertices), iter(self.edges)) self.checkIfOK(g, "name") g = Graph.DictList(iter(self.vertices), iter(self.edges), iterative=True) self.checkIfOK(g, "name") def testGraphFromDictIteratorNoVertices(self): g = Graph.DictList(None, iter(self.edges)) self.checkIfOK(g, "name", check_vertex_attrs=False) g = Graph.DictList(None, iter(self.edges), iterative=True) self.checkIfOK(g, "name", check_vertex_attrs=False) def testGraphFromDictListExtraVertexName(self): del self.vertices[2:] # No data for "Cecil" and "David" g = Graph.DictList(self.vertices, self.edges) self.assertTrue(g.vcount() == 4 and g.ecount() == 5 and g.is_directed() == False) self.assertTrue(g.vs["name"] == ["Alice", "Bob", "Cecil", "David"]) self.assertTrue(g.vs["age"] == [48, 33, None, None]) self.assertTrue(g.vs["gender"] == ["F", "M", None, None]) self.assertTrue(g.es["friendship"] == [4, 5, 5, 2, 1]) self.assertTrue(g.es["advice"] == [4, 5, 5, 4, 2]) self.assertTrue(g.get_edgelist() == [(0, 1), (1, 2), (0, 2), (0, 3), (1, 3)]) def testGraphFromDictListAlternativeName(self): for vdata in self.vertices: vdata["name_alternative"] = vdata["name"] del vdata["name"] g = Graph.DictList(self.vertices, self.edges, vertex_name_attr="name_alternative") self.checkIfOK(g, "name_alternative") g = Graph.DictList(self.vertices, self.edges, vertex_name_attr="name_alternative", \ iterative=True) self.checkIfOK(g, "name_alternative") def checkIfOK(self, g, name_attr, check_vertex_attrs=True): self.assertTrue(g.vcount() == 4 and g.ecount() == 5 and g.is_directed() == False) self.assertTrue(g.get_edgelist() == [(0, 1), (1, 2), (0, 2), (0, 3), (1, 3)]) self.assertTrue(g.vs[name_attr] == ["Alice", "Bob", "Cecil", "David"]) if check_vertex_attrs: self.assertTrue(g.vs["age"] == [48, 33, 45, 34]) self.assertTrue(g.vs["gender"] == ["F", "M", "F", "M"]) self.assertTrue(g.es["friendship"] == [4, 5, 5, 2, 1]) self.assertTrue(g.es["advice"] == [4, 5, 5, 4, 2]) class GraphTupleListTests(unittest.TestCase): def setUp(self): self.edges = [ \ ("Alice", "Bob", 4, 4), ("Cecil", "Bob", 5, 5), ("Cecil", "Alice", 5, 5), ("David", "Alice", 2, 4), ("David", "Bob", 1, 2) ] def testGraphFromTupleList(self): g = Graph.TupleList(self.edges) self.checkIfOK(g, "name", ()) def testGraphFromTupleListWithEdgeAttributes(self): g = Graph.TupleList(self.edges, edge_attrs=("friendship", "advice")) self.checkIfOK(g, "name", ("friendship", "advice")) g = Graph.TupleList(self.edges, edge_attrs=("friendship", )) self.checkIfOK(g, "name", ("friendship", )) g = Graph.TupleList(self.edges, edge_attrs="friendship") self.checkIfOK(g, "name", ("friendship", )) def testGraphFromTupleListWithDifferentNameAttribute(self): g = Graph.TupleList(self.edges, vertex_name_attr="spam") self.checkIfOK(g, "spam", ()) def testGraphFromTupleListWithWeights(self): g = Graph.TupleList(self.edges, weights=True) self.checkIfOK(g, "name", ("weight", )) g = Graph.TupleList(self.edges, weights="friendship") self.checkIfOK(g, "name", ("friendship", )) g = Graph.TupleList(self.edges, weights=False) self.checkIfOK(g, "name", ()) self.assertRaises(ValueError, Graph.TupleList, [self.edges], weights=True, edge_attrs="friendship") def testNoneForMissingAttributes(self): g = Graph.TupleList(self.edges, edge_attrs=("friendship", "advice", "spam")) self.checkIfOK(g, "name", ("friendship", "advice", "spam")) def checkIfOK(self, g, name_attr, edge_attrs): self.assertTrue(g.vcount() == 4 and g.ecount() == 5 and g.is_directed() == False) self.assertTrue(g.get_edgelist() == [(0, 1), (1, 2), (0, 2), (0, 3), (1, 3)]) self.assertTrue(g.attributes() == []) self.assertTrue(g.vertex_attributes() == [name_attr]) self.assertTrue(g.vs[name_attr] == ["Alice", "Bob", "Cecil", "David"]) if edge_attrs: self.assertTrue(sorted(g.edge_attributes()) == sorted(edge_attrs)) self.assertTrue(g.es[edge_attrs[0]] == [4, 5, 5, 2, 1]) if len(edge_attrs) > 1: self.assertTrue(g.es[edge_attrs[1]] == [4, 5, 5, 4, 2]) if len(edge_attrs) > 2: self.assertTrue(g.es[edge_attrs[2]] == [None] * 5) else: self.assertTrue(g.edge_attributes() == []) class DegreeSequenceTests(unittest.TestCase): def testIsDegreeSequence(self): self.assertTrue(is_degree_sequence([])) self.assertTrue(is_degree_sequence([], [])) self.assertTrue(is_degree_sequence([0])) self.assertTrue(is_degree_sequence([0], [0])) self.assertFalse(is_degree_sequence([1])) self.assertTrue(is_degree_sequence([1], [1])) self.assertTrue(is_degree_sequence([2])) self.assertFalse(is_degree_sequence([2, 1, 1, 1])) self.assertTrue(is_degree_sequence([2, 1, 1, 1], [1, 1, 1, 2])) self.assertFalse(is_degree_sequence([2, 1, -2])) self.assertFalse(is_degree_sequence([2, 1, 1, 1], [1, 1, 1, -2])) self.assertTrue(is_degree_sequence([3, 3, 3, 3, 3, 3, 3, 3, 3, 3])) self.assertTrue(is_degree_sequence([3, 3, 3, 3, 3, 3, 3, 3, 3, 3], None)) self.assertFalse(is_degree_sequence([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3])) self.assertTrue(is_degree_sequence([3, 3, 3, 3, 3, 3, 3, 3, 3, 3], [4, 3, 2, 3, 4, 4, 2, 2, 4, 2])) def testIsGraphicalSequence(self): self.assertTrue(is_graphical_degree_sequence([])) self.assertTrue(is_graphical_degree_sequence([], [])) self.assertTrue(is_graphical_degree_sequence([0])) self.assertTrue(is_graphical_degree_sequence([0], [0])) self.assertFalse(is_graphical_degree_sequence([1])) self.assertTrue(is_graphical_degree_sequence([1], [1])) self.assertFalse(is_graphical_degree_sequence([2])) self.assertFalse(is_graphical_degree_sequence([2, 1, 1, 1])) self.assertTrue(is_graphical_degree_sequence([2, 1, 1, 1], [1, 1, 1, 2])) self.assertFalse(is_graphical_degree_sequence([2, 1, -2])) self.assertFalse(is_graphical_degree_sequence([2, 1, 1, 1], [1, 1, 1, -2])) self.assertTrue(is_graphical_degree_sequence([3, 3, 3, 3, 3, 3, 3, 3, 3, 3])) self.assertTrue(is_graphical_degree_sequence([3, 3, 3, 3, 3, 3, 3, 3, 3, 3], None)) self.assertFalse(is_graphical_degree_sequence([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3])) self.assertTrue(is_graphical_degree_sequence([3, 3, 3, 3, 3, 3, 3, 3, 3, 3], [4, 3, 2, 3, 4, 4, 2, 2, 4, 2])) self.assertTrue(is_graphical_degree_sequence([3, 3, 3, 3, 4])) def suite(): basic_suite = unittest.makeSuite(BasicTests) datatype_suite = unittest.makeSuite(DatatypeTests) graph_dict_list_suite = unittest.makeSuite(GraphDictListTests) graph_tuple_list_suite = unittest.makeSuite(GraphTupleListTests) degree_sequence_suite = unittest.makeSuite(DegreeSequenceTests) return unittest.TestSuite([basic_suite, datatype_suite, graph_dict_list_suite, graph_tuple_list_suite, degree_sequence_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/bipartite.py0000644000076500000240000001334012453614202022556 0ustar ntamasstaff00000000000000import unittest from igraph import * class BipartiteTests(unittest.TestCase): def testCreateBipartite(self): g = Graph.Bipartite([0, 1]*5, [(0,1),(2,3),(4,5),(6,7),(8,9)]) self.assertTrue(g.vcount() == 10 and g.ecount() == 5 and g.is_directed() == False) self.assertTrue(g.is_bipartite()) self.assertTrue(g.vs["type"] == [False, True]*5) def testFullBipartite(self): g = Graph.Full_Bipartite(10, 5) self.assertTrue(g.vcount() == 15 and g.ecount() == 50 and g.is_directed() == False) expected = sorted([(i, j) for i in xrange(10) for j in xrange(10, 15)]) self.assertTrue(sorted(g.get_edgelist()) == expected) self.assertTrue(g.vs["type"] == [False]*10 + [True]*5) g = Graph.Full_Bipartite(10, 5, directed=True, mode=OUT) self.assertTrue(g.vcount() == 15 and g.ecount() == 50 and g.is_directed() == True) self.assertTrue(sorted(g.get_edgelist()) == expected) self.assertTrue(g.vs["type"] == [False]*10 + [True]*5) g = Graph.Full_Bipartite(10, 5, directed=True, mode=IN) self.assertTrue(g.vcount() == 15 and g.ecount() == 50 and g.is_directed() == True) self.assertTrue(sorted(g.get_edgelist()) == sorted([(i,j) for j, i in expected])) self.assertTrue(g.vs["type"] == [False]*10 + [True]*5) g = Graph.Full_Bipartite(10, 5, directed=True) self.assertTrue(g.vcount() == 15 and g.ecount() == 100 and g.is_directed() == True) expected.extend([(j, i) for i, j in expected]) expected.sort() self.assertTrue(sorted(g.get_edgelist()) == expected) self.assertTrue(g.vs["type"] == [False]*10 + [True]*5) def testIncidence(self): g = Graph.Incidence([[0, 1, 1], [1, 2, 0]]) self.assertTrue(g.vcount() == 5 and g.ecount() == 4 and g.is_directed() == False) self.assertTrue(g.vs["type"] == [False]*2 + [True]*3) self.assertTrue(sorted(g.get_edgelist()) == [(0,3),(0,4),(1,2),(1,3)]) g = Graph.Incidence([[0, 1, 1], [1, 2, 0]], multiple=True) self.assertTrue(g.vcount() == 5 and g.ecount() == 5 and g.is_directed() == False) self.assertTrue(g.vs["type"] == [False]*2 + [True]*3) self.assertTrue(sorted(g.get_edgelist()) == [(0,3),(0,4),(1,2),(1,3),(1,3)]) g = Graph.Incidence([[0, 1, 1], [1, 2, 0]], directed=True) self.assertTrue(g.vcount() == 5 and g.ecount() == 4 and g.is_directed() == True) self.assertTrue(g.vs["type"] == [False]*2 + [True]*3) self.assertTrue(sorted(g.get_edgelist()) == [(0,3),(0,4),(1,2),(1,3)]) g = Graph.Incidence([[0, 1, 1], [1, 2, 0]], directed=True, mode="in") self.assertTrue(g.vcount() == 5 and g.ecount() == 4 and g.is_directed() == True) self.assertTrue(g.vs["type"] == [False]*2 + [True]*3) self.assertTrue(sorted(g.get_edgelist()) == [(2,1),(3,0),(3,1),(4,0)]) def testGetIncidence(self): mat = [[0, 1, 1], [1, 1, 0]] v1, v2 = [0, 1], [2, 3, 4] g = Graph.Incidence(mat) self.assertTrue(g.get_incidence() == (mat, v1, v2)) g.vs["type2"] = g.vs["type"] self.assertTrue(g.get_incidence("type2") == (mat, v1, v2)) self.assertTrue(g.get_incidence(g.vs["type2"]) == (mat, v1, v2)) def testBipartiteProjection(self): g = Graph.Full_Bipartite(10, 5) g1, g2 = g.bipartite_projection() self.assertTrue(g1.isomorphic(Graph.Full(10))) self.assertTrue(g2.isomorphic(Graph.Full(5))) self.assertTrue(g.bipartite_projection(which=0).isomorphic(g1)) self.assertTrue(g.bipartite_projection(which=1).isomorphic(g2)) self.assertTrue(g.bipartite_projection(which=False).isomorphic(g1)) self.assertTrue(g.bipartite_projection(which=True).isomorphic(g2)) self.assertTrue(g1.es["weight"] == [5] * 45) self.assertTrue(g2.es["weight"] == [10] * 10) self.assertTrue(g.bipartite_projection_size() == (10, 45, 5, 10)) g1, g2 = g.bipartite_projection(probe1=10) self.assertTrue(g1.isomorphic(Graph.Full(5))) self.assertTrue(g2.isomorphic(Graph.Full(10))) self.assertTrue(g.bipartite_projection(which=0).isomorphic(g2)) self.assertTrue(g.bipartite_projection(which=1).isomorphic(g1)) self.assertTrue(g.bipartite_projection(which=False).isomorphic(g2)) self.assertTrue(g.bipartite_projection(which=True).isomorphic(g1)) g1, g2 = g.bipartite_projection(multiplicity=False) self.assertTrue(g1.isomorphic(Graph.Full(10))) self.assertTrue(g2.isomorphic(Graph.Full(5))) self.assertTrue(g.bipartite_projection(which=0).isomorphic(g1)) self.assertTrue(g.bipartite_projection(which=1).isomorphic(g2)) self.assertTrue(g.bipartite_projection(which=False).isomorphic(g1)) self.assertTrue(g.bipartite_projection(which=True).isomorphic(g2)) self.assertTrue("weight" not in g1.edge_attributes()) self.assertTrue("weight" not in g2.edge_attributes()) def testIsBipartite(self): g = Graph.Star(10) self.assertTrue(g.is_bipartite() == True) self.assertTrue(g.is_bipartite(True) == (True, [False] + [True]*9)) g = Graph.Tree(100, 3) self.assertTrue(g.is_bipartite() == True) g = Graph.Ring(9) self.assertTrue(g.is_bipartite() == False) self.assertTrue(g.is_bipartite(True) == (False, None)) g = Graph.Ring(10) self.assertTrue(g.is_bipartite() == True) g += (2, 0) self.assertTrue(g.is_bipartite(True) == (False, None)) def suite(): bipartite_suite = unittest.makeSuite(BipartiteTests) return unittest.TestSuite([bipartite_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/cliques.py0000644000076500000240000002006512476153444022255 0ustar ntamasstaff00000000000000import unittest from igraph import * from igraph.test.foreign import temporary_file class CliqueTests(unittest.TestCase): def setUp(self): self.g=Graph.Full(6) self.g.delete_edges([(0, 1), (0, 2), (3, 5)]) def testCliques(self): tests = {(4, -1): [[1, 2, 3, 4], [1, 2, 4, 5]], (2, 2): [[0, 3], [0, 4], [0, 5], [1, 2], [1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 4], [4, 5]], (-1, -1): [[0], [1], [2], [3], [4], [5], [0, 3], [0, 4], [0, 5], [1, 2], [1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 4], [4, 5], [0, 3, 4], [0, 4, 5], [1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 4, 5], [2, 3, 4], [2, 4, 5], [1, 2, 3, 4], [1, 2, 4, 5]]} for (lo, hi), exp in tests.iteritems(): self.assertEqual(sorted(exp), sorted(map(sorted, self.g.cliques(lo, hi)))) def testLargestCliques(self): self.assertEqual(sorted(map(sorted, self.g.largest_cliques())), [[1, 2, 3, 4], [1, 2, 4, 5]]) def testMaximalCliques(self): self.assertEqual(sorted(map(sorted, self.g.maximal_cliques())), [[0, 3, 4], [0, 4, 5], [1, 2, 3, 4], [1, 2, 4, 5]]) self.assertEqual(sorted(map(sorted, self.g.maximal_cliques(min=4))), [[1, 2, 3, 4], [1, 2, 4, 5]]) self.assertEqual(sorted(map(sorted, self.g.maximal_cliques(max=3))), [[0, 3, 4], [0, 4, 5]]) def testMaximalCliquesFile(self): def read_cliques(fname): with open(fname) as fp: return sorted(sorted(int(item) for item in line.split()) for line in fp) with temporary_file() as fname: self.g.maximal_cliques(file=fname) self.assertEqual([[0, 3, 4], [0, 4, 5], [1, 2, 3, 4], [1, 2, 4, 5]], read_cliques(fname)) with temporary_file() as fname: self.g.maximal_cliques(min=4, file=fname) self.assertEqual([[1, 2, 3, 4], [1, 2, 4, 5]], read_cliques(fname)) with temporary_file() as fname: self.g.maximal_cliques(max=3, file=fname) self.assertEqual([[0, 3, 4], [0, 4, 5]], read_cliques(fname)) def testCliqueNumber(self): self.assertEqual(self.g.clique_number(), 4) self.assertEqual(self.g.omega(), 4) class IndependentVertexSetTests(unittest.TestCase): def setUp(self): self.g1=Graph.Tree(5, 2, TREE_UNDIRECTED) self.g2=Graph.Tree(10, 2, TREE_UNDIRECTED) def testIndependentVertexSets(self): tests = {(4, -1): [], (2, 2): [(0, 3), (0, 4), (1, 2), (2, 3), (2, 4), (3, 4)], (-1, -1): [(0,), (1,), (2,), (3,), (4,), (0, 3), (0, 4), (1, 2), (2, 3), (2, 4), (3, 4), (0, 3, 4), (2, 3, 4)]} for (lo, hi), exp in tests.iteritems(): self.assertEqual(exp, self.g1.independent_vertex_sets(lo, hi)) def testLargestIndependentVertexSets(self): self.assertEqual(self.g1.largest_independent_vertex_sets(), [(0, 3, 4), (2, 3, 4)]) def testMaximalIndependentVertexSets(self): self.assertEqual(self.g2.maximal_independent_vertex_sets(), [(0, 3, 4, 5, 6), (0, 3, 5, 6, 9), (0, 4, 5, 6, 7, 8), (0, 5, 6, 7, 8, 9), (1, 2, 7, 8, 9), (1, 5, 6, 7, 8, 9), (2, 3, 4), (2, 3, 9), (2, 4, 7, 8)]) def testIndependenceNumber(self): self.assertEqual(self.g2.independence_number(), 6) self.assertEqual(self.g2.alpha(), 6) class MotifTests(unittest.TestCase): def setUp(self): self.g = Graph.Erdos_Renyi(100, 0.2, directed=True) def testDyads(self): """ @note: this test is not exhaustive, it only checks whether the L{DyadCensus} objects "understand" attribute and item accessors """ dc = self.g.dyad_census() accessors = ["mut", "mutual", "asym", "asymm", "asymmetric", "null"] for a in accessors: self.assertTrue(isinstance(getattr(dc, a), int)) self.assertTrue(isinstance(dc[a], int)) self.assertTrue(isinstance(list(dc), list)) self.assertTrue(isinstance(tuple(dc), tuple)) self.assertTrue(len(list(dc)) == 3) self.assertTrue(len(tuple(dc)) == 3) def testTriads(self): """ @note: this test is not exhaustive, it only checks whether the L{TriadCensus} objects "understand" attribute and item accessors """ tc = self.g.triad_census() accessors = ["003", "012", "021d", "030C"] for a in accessors: self.assertTrue(isinstance(getattr(tc, "t"+a), int)) self.assertTrue(isinstance(tc[a], int)) self.assertTrue(isinstance(list(tc), list)) self.assertTrue(isinstance(tuple(tc), tuple)) self.assertTrue(len(list(tc)) == 16) self.assertTrue(len(tuple(tc)) == 16) class CliqueBenchmark(object): """This is a benchmark, not a real test case. You can run it using: >>> from igraph.test.cliques import CliqueBenchmark >>> CliqueBenchmark().run() """ def __init__(self): from time import time import gc self.time = time self.gc_collect = gc.collect def run(self): self.printIntro() self.testRandom() self.testMoonMoser() self.testGRG() def printIntro(self): print "n = number of vertices" print "#cliques = number of maximal cliques found" print "t1 = time required to determine the clique number" print "t2 = time required to determine and save all maximal cliques" print def timeit(self, g): start = self.time() omega = g.clique_number() mid = self.time() cl = g.maximal_cliques() end = self.time() self.gc_collect() return len(cl), mid-start, end-mid def testRandom(self): np = {100: [0.6, 0.7], 300: [0.1, 0.2, 0.3, 0.4], 500: [0.1, 0.2, 0.3], 700: [0.1, 0.2], 1000:[0.1, 0.2], 10000: [0.001, 0.003, 0.005, 0.01, 0.02]} print print "Erdos-Renyi random graphs" print " n p #cliques t1 t2" for n in sorted(np.keys()): for p in np[n]: g = Graph.Erdos_Renyi(n, p) result = self.timeit(g) print "%8d %8.3f %8d %8.4fs %8.4fs" % \ tuple([n, p] + list(result)) def testMoonMoser(self): ns = [15, 27, 33] print print "Moon-Moser graphs" print " n exp_clqs #cliques t1 t2" for n in ns: n3 = n/3 types = range(n3) * 3 el = [(i, j) for i in range(n) for j in range(i+1,n) if types[i] != types[j]] g = Graph(n, el) result = self.timeit(g) print "%8d %8d %8d %8.4fs %8.4fs" % \ tuple([n, (3**(n/3))] + list(result)) def testGRG(self): ns = [100, 1000, 5000, 10000, 25000, 50000] print print "Geometric random graphs" print " n d #cliques t1 t2" for n in ns: d = 2. / (n ** 0.5) g = Graph.GRG(n, d) result = self.timeit(g) print "%8d %8.3f %8d %8.4fs %8.4fs" % \ tuple([n, d] + list(result)) def suite(): clique_suite = unittest.makeSuite(CliqueTests) indvset_suite = unittest.makeSuite(IndependentVertexSetTests) motif_suite = unittest.makeSuite(MotifTests) return unittest.TestSuite([clique_suite, indvset_suite, motif_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/colortests.py0000644000076500000240000000733312453614202023001 0ustar ntamasstaff00000000000000import unittest from itertools import izip from igraph import * class ColorTests(unittest.TestCase): def assertAlmostEqualMany(self, items1, items2, eps): for idx, (item1, item2) in enumerate(izip(items1, items2)): self.assertAlmostEqual(item1, item2, places=eps, msg="mismatch at index %d, %r != %r with %d digits" % (idx, items1, items2, eps)) def setUp(self): columns = ["r", "g", "b", "h", "v", "l", "s_hsv", "s_hsl", "alpha"] # Examples taken from http://en.wikipedia.org/wiki/HSL_and_HSV values = [ (1, 1, 1, 0, 1, 1, 0, 0, 1), (0.5, 0.5, 0.5, 0, 0.5, 0.5, 0, 0, 0.5), (0, 0, 0, 0, 0, 0, 0, 0, 1), (1, 0, 0, 0, 1, 0.5, 1, 1, 0.5), (0.75, 0.75, 0, 60, 0.75, 0.375, 1, 1, 0.25), (0, 0.5, 0, 120, 0.5, 0.25, 1, 1, 0.75), (0.5, 1, 1, 180, 1, 0.75, 0.5, 1, 1), (0.5, 0.5, 1, 240, 1, 0.75, 0.5, 1, 1), (0.75, 0.25, 0.75, 300, 0.75, 0.5, 0.666666667, 0.5, 0.25), (0.211, 0.149, 0.597, 248.3, 0.597, 0.373, 0.750, 0.601, 1), (0.495, 0.493, 0.721, 240.5, 0.721, 0.607, 0.316, 0.290, 0.75), ] self.data = [dict(zip(columns, value)) for value in values] for row in self.data: row["h"] /= 360. def _testGeneric(self, method, args1, args2=("r", "g", "b")): if len(args1) == len(args2)+1: args2 += ("alpha", ) for data in self.data: vals1 = [data.get(arg, 0.0) for arg in args1] vals2 = [data.get(arg, 0.0) for arg in args2] self.assertAlmostEqualMany(method(*vals1), vals2, 2) def testHSVtoRGB(self): self._testGeneric(hsv_to_rgb, "h s_hsv v".split()) def testHSVAtoRGBA(self): self._testGeneric(hsva_to_rgba, "h s_hsv v alpha".split()) def testHSLtoRGB(self): self._testGeneric(hsl_to_rgb, "h s_hsl l".split()) def testHSLAtoRGBA(self): self._testGeneric(hsla_to_rgba, "h s_hsl l alpha".split()) def testRGBtoHSL(self): self._testGeneric(rgb_to_hsl, "r g b".split(), "h s_hsl l".split()) def testRGBAtoHSLA(self): self._testGeneric(rgba_to_hsla, "r g b alpha".split(), "h s_hsl l alpha".split()) def testRGBtoHSV(self): self._testGeneric(rgb_to_hsv, "r g b".split(), "h s_hsv v".split()) def testRGBAtoHSVA(self): self._testGeneric(rgba_to_hsva, "r g b alpha".split(), "h s_hsv v alpha".split()) class PaletteTests(unittest.TestCase): def testGradientPalette(self): gp = GradientPalette("red", "blue", 3) self.assertTrue(gp.get(0) == (1., 0., 0., 1.)) self.assertTrue(gp.get(1) == (0.5, 0., 0.5, 1.)) self.assertTrue(gp.get(2) == (0., 0., 1., 1.)) def testAdvancedGradientPalette(self): agp = AdvancedGradientPalette(["red", "black", "blue"], n=9) self.assertTrue(agp.get(0) == (1., 0., 0., 1.)) self.assertTrue(agp.get(2) == (0.5, 0., 0., 1.)) self.assertTrue(agp.get(4) == (0., 0., 0., 1.)) self.assertTrue(agp.get(5) == (0., 0., 0.25, 1.)) self.assertTrue(agp.get(8) == (0., 0., 1., 1.)) agp = AdvancedGradientPalette(["red", "black", "blue"], [0, 8, 2], 9) self.assertTrue(agp.get(0) == (1., 0., 0., 1.)) self.assertTrue(agp.get(1) == (0.5, 0., 0.5, 1.)) self.assertTrue(agp.get(5) == (0., 0., 0.5, 1.)) def suite(): color_suite = unittest.makeSuite(ColorTests) palette_suite = unittest.makeSuite(PaletteTests) return unittest.TestSuite([color_suite, palette_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/conversion.py0000644000076500000240000000704112453614202022761 0ustar ntamasstaff00000000000000import unittest from igraph import * class DirectedUndirectedTests(unittest.TestCase): def testToUndirected(self): graph = Graph([(0,1), (0,2), (1,0)], directed=True) graph2 = graph.copy() graph2.to_undirected(mode=False) self.assertTrue(graph2.vcount() == graph.vcount()) self.assertTrue(graph2.is_directed() == False) self.assertTrue(sorted(graph2.get_edgelist()) == [(0,1), (0,1), (0,2)]) graph2 = graph.copy() graph2.to_undirected() self.assertTrue(graph2.vcount() == graph.vcount()) self.assertTrue(graph2.is_directed() == False) self.assertTrue(sorted(graph2.get_edgelist()) == [(0,1), (0,2)]) graph2 = graph.copy() graph2.es["weight"] = [1,2,3] graph2.to_undirected(mode="collapse", combine_edges="sum") self.assertTrue(graph2.vcount() == graph.vcount()) self.assertTrue(graph2.is_directed() == False) self.assertTrue(sorted(graph2.get_edgelist()) == [(0,1), (0,2)]) self.assertTrue(graph2.es["weight"] == [4,2]) graph = Graph([(0,1),(1,0),(0,1),(1,0),(2,1),(1,2)], directed=True) graph2 = graph.copy() graph2.es["weight"] = [1,2,3,4,5,6] graph2.to_undirected(mode="mutual", combine_edges="sum") self.assertTrue(graph2.vcount() == graph.vcount()) self.assertTrue(graph2.is_directed() == False) self.assertTrue(sorted(graph2.get_edgelist()) == [(0,1), (0,1), (1,2)]) self.assertTrue(graph2.es["weight"] == [7,3,11] or graph2.es["weight"] == [3,7,11]) def testToDirected(self): graph = Graph([(0,1), (0,2), (2,3), (2,4)], directed=False) graph.to_directed() self.assertTrue(graph.is_directed()) self.assertTrue(graph.vcount() == 5) self.assertTrue(sorted(graph.get_edgelist()) == \ [(0,1), (0,2), (1,0), (2,0), (2,3), (2,4), (3,2), (4,2)] ) class GraphRepresentationTests(unittest.TestCase): def testGetAdjacency(self): # Undirected case g = Graph.Tree(6, 3) g.es["weight"] = range(5) self.assertTrue(g.get_adjacency() == Matrix([ [0, 1, 1, 1, 0, 0], [1, 0, 0, 0, 1, 1], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0] ])) self.assertTrue(g.get_adjacency(attribute="weight") == Matrix([ [0, 0, 1, 2, 0, 0], [0, 0, 0, 0, 3, 4], [1, 0, 0, 0, 0, 0], [2, 0, 0, 0, 0, 0], [0, 3, 0, 0, 0, 0], [0, 4, 0, 0, 0, 0] ])) self.assertTrue(g.get_adjacency(eids=True) == Matrix([ [0, 1, 2, 3, 0, 0], [1, 0, 0, 0, 4, 5], [2, 0, 0, 0, 0, 0], [3, 0, 0, 0, 0, 0], [0, 4, 0, 0, 0, 0], [0, 5, 0, 0, 0, 0] ])-1) # Directed case g = Graph.Tree(6, 3, "tree_out") g.add_edges([(0,1), (1,0)]) self.assertTrue(g.get_adjacency() == Matrix([ [0, 2, 1, 1, 0, 0], [1, 0, 0, 0, 1, 1], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0] ])) def suite(): direction_suite = unittest.makeSuite(DirectedUndirectedTests) representation_suite = unittest.makeSuite(GraphRepresentationTests) return unittest.TestSuite([direction_suite, representation_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/decomposition.py0000644000076500000240000004025212453614202023451 0ustar ntamasstaff00000000000000import random import unittest import math from igraph import * try: set, frozenset except NameError: import sets set, frozenset = sets.Set, sets.ImmutableSet class SubgraphTests(unittest.TestCase): def testSubgraph(self): g = Graph.Lattice([10, 10], circular=False, mutual=False) g.vs["id"] = range(g.vcount()) vs = [0, 1, 2, 10, 11, 12, 20, 21, 22] sg = g.subgraph(vs) self.assertTrue(sg.isomorphic(Graph.Lattice([3, 3], circular=False, mutual=False))) self.assertTrue(sg.vs["id"] == vs) def testSubgraphEdges(self): g = Graph.Lattice([10, 10], circular=False, mutual=False) g.es["id"] = range(g.ecount()) es = [0, 1, 2, 5, 20, 21, 22, 24, 38, 40] sg = g.subgraph_edges(es) exp = Graph.Lattice([3, 3], circular=False, mutual=False) exp.delete_edges([7, 8]) self.assertTrue(sg.isomorphic(exp)) self.assertTrue(sg.es["id"] == es) class DecompositionTests(unittest.TestCase): def testKCores(self): g = Graph(11, [(0,1), (0,2), (0,3), (1,2), (1,3), (2,3), (2,4), (2,5), (3,6), (3,7), (1,7), (7,8), (1,9), (1,10), (9,10)]) self.assertTrue(g.coreness() == [3,3,3,3,1,1,1,2,1,2,2]) self.assertTrue(g.shell_index() == g.coreness()) l=g.k_core(3).get_edgelist() l.sort() self.assertTrue(l == [(0,1), (0,2), (0,3), (1,2), (1,3), (2,3)]) class ClusteringTests(unittest.TestCase): def setUp(self): self.cl = Clustering([0,0,0,1,1,2,1,1,4,4]) def testClusteringIndex(self): self.assertTrue(self.cl[0] == [0, 1, 2]) self.assertTrue(self.cl[1] == [3, 4, 6, 7]) self.assertTrue(self.cl[2] == [5]) self.assertTrue(self.cl[3] == []) self.assertTrue(self.cl[4] == [8, 9]) def testClusteringLength(self): self.assertTrue(len(self.cl) == 5) def testClusteringMembership(self): self.assertTrue(self.cl.membership == [0,0,0,1,1,2,1,1,4,4]) def testClusteringSizes(self): self.assertTrue(self.cl.sizes() == [3, 4, 1, 0, 2]) self.assertTrue(self.cl.sizes(2, 4, 1) == [1, 2, 4]) self.assertTrue(self.cl.size(2) == 1) def testClusteringHistogram(self): self.assertTrue(isinstance(self.cl.size_histogram(), Histogram)) class VertexClusteringTests(unittest.TestCase): def setUp(self): self.graph = Graph.Full(10) self.graph.vs["string"] = list("aaabbcccab") self.graph.vs["int"] = [17, 41, 23, 25, 64, 33, 3, 24, 47, 15] def testFromStringAttribute(self): cl = VertexClustering.FromAttribute(self.graph, "string") self.assertTrue(cl.membership == [0,0,0,1,1,2,2,2,0,1]) def testFromIntAttribute(self): cl = VertexClustering.FromAttribute(self.graph, "int") self.assertTrue(cl.membership == list(range(10))) cl = VertexClustering.FromAttribute(self.graph, "int", 15) self.assertTrue(cl.membership == [0, 1, 0, 0, 2, 1, 3, 0, 4, 0]) cl = VertexClustering.FromAttribute(self.graph, "int", [10, 20, 30]) self.assertTrue(cl.membership == [0, 1, 2, 2, 1, 1, 3, 2, 1, 0]) def testClusterGraph(self): cl = VertexClustering(self.graph, [0, 0, 0, 1, 1, 1, 2, 2, 2, 2]) self.graph.delete_edges(self.graph.es.select(_between=([0,1,2], [3,4,5]))) clg = cl.cluster_graph(dict(string="concat", int=max)) self.assertTrue(sorted(clg.get_edgelist()) == [(0, 2), (1, 2)]) self.assertTrue(not clg.is_directed()) self.assertTrue(clg.vs["string"] == ["aaa", "bbc", "ccab"]) self.assertTrue(clg.vs["int"] == [41, 64, 47]) clg = cl.cluster_graph(dict(string="concat", int=max), False) self.assertTrue(sorted(clg.get_edgelist()) == \ [(0, 0)]*3 + [(0, 2)]*12 + [(1, 1)]*3 + [(1, 2)]*12 + [(2, 2)]*6) self.assertTrue(not clg.is_directed()) self.assertTrue(clg.vs["string"] == ["aaa", "bbc", "ccab"]) self.assertTrue(clg.vs["int"] == [41, 64, 47]) class CoverTests(unittest.TestCase): def setUp(self): self.cl = Cover([(0,1,2,3), (3,4,5,6,9), (), (8,9)]) def testCoverIndex(self): self.assertTrue(self.cl[0] == [0, 1, 2, 3]) self.assertTrue(self.cl[1] == [3, 4, 5, 6, 9]) self.assertTrue(self.cl[2] == []) self.assertTrue(self.cl[3] == [8, 9]) def testCoverLength(self): self.assertTrue(len(self.cl) == 4) def testCoverSizes(self): self.assertTrue(self.cl.sizes() == [4, 5, 0, 2]) self.assertTrue(self.cl.sizes(1, 3, 0) == [5, 2, 4]) self.assertTrue(self.cl.size(1) == 5) self.assertTrue(self.cl.size(2) == 0) def testCoverHistogram(self): self.assertTrue(isinstance(self.cl.size_histogram(), Histogram)) def testCoverConstructorWithN(self): self.assertTrue(self.cl.n == 10) cl = Cover(self.cl, n = 15) self.assertTrue(cl.n == 15) cl = Cover(self.cl, n = 1) self.assertTrue(cl.n == 10) class CommunityTests(unittest.TestCase): def reindexMembership(self, cl): idgen = UniqueIdGenerator() return [idgen[i] for i in cl.membership] def testClauset(self): # Two cliques of size 5 with one connecting edge g = Graph.Full(5) + Graph.Full(5) g.add_edges([(0, 5)]) cl = g.community_fastgreedy().as_clustering() self.assertEqual(cl.membership, [0,0,0,0,0,1,1,1,1,1]) self.assertAlmostEqual(cl.q, 0.4523, places=3) # Lollipop, weighted g = Graph.Full(4) + Graph.Full(2) g.add_edges([(3,4)]) weights = [1, 1, 1, 1, 1, 1, 10, 10] cl = g.community_fastgreedy(weights).as_clustering() self.assertEqual(cl.membership, [0, 0, 0, 1, 1, 1]) self.assertAlmostEqual(cl.q, 0.1708, places=3) # Same graph, different weights g.es["weight"] = [3] * g.ecount() cl = g.community_fastgreedy("weight").as_clustering() self.assertEqual(cl.membership, [0, 0, 0, 0, 1, 1]) self.assertAlmostEqual(cl.q, 0.1796, places=3) # Disconnected graph g = Graph.Full(4) + Graph.Full(4) + Graph.Full(3) + Graph.Full(2) cl = g.community_fastgreedy().as_clustering() self.assertEqual(cl.membership, [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3]) # Empty graph g = Graph(20) cl = g.community_fastgreedy().as_clustering() self.assertEqual(cl.membership, range(g.vcount())) def testEdgeBetweenness(self): # Full graph, no weights g = Graph.Full(5) cl = g.community_edge_betweenness().as_clustering() self.assertEqual(cl.membership, [0]*5) # Full graph with weights g.es["weight"] = 1 g[0,1] = g[1,2] = g[2,0] = g[3,4] = 10 cl = g.community_edge_betweenness(weights="weight").as_clustering() self.assertEqual(cl.membership, [0,0,0,1,1]) self.assertAlmostEqual(cl.q, 0.2750, places=3) def testEigenvector(self): g = Graph.Full(5) + Graph.Full(5) g.add_edges([(0, 5)]) cl = g.community_leading_eigenvector() self.assertTrue(cl.membership == [0,0,0,0,0,1,1,1,1,1]) self.assertAlmostEqual(cl.q, 0.4523, places=3) cl = g.community_leading_eigenvector(2) self.assertTrue(cl.membership == [0,0,0,0,0,1,1,1,1,1]) self.assertAlmostEqual(cl.q, 0.4523, places=3) def testInfomap(self): g = Graph.Famous("zachary") cl = g.community_infomap() self.assertAlmostEqual(cl.codelength, 4.60605, places=3) self.assertAlmostEqual(cl.q, 0.40203, places=3) self.assertTrue(cl.membership == [1,1,1,1,2,2,2,1,0,1,2,1,1,1,0,0,2,1,0,1,0,1] + [0]*12) # Smoke testing with vertex and edge weights v_weights = [random.randint(1, 5) for _ in xrange(g.vcount())] e_weights = [random.randint(1, 5) for _ in xrange(g.ecount())] cl = g.community_infomap(edge_weights=e_weights) cl = g.community_infomap(vertex_weights=v_weights) cl = g.community_infomap(edge_weights=e_weights, vertex_weights=v_weights) def testLabelPropagation(self): # Nothing to test there really, since the algorithm # is pretty nondeterministic. We just do a quick smoke # test. g = Graph.GRG(100, 0.2) cl = g.community_label_propagation() g = Graph([(0,1),(1,2),(2,3)]) g.es["weight"] = [2, 1, 2] g.vs["initial"] = [0, -1, -1, 1] cl = g.community_label_propagation("weight", "initial", [1,0,0,1]) self.assertTrue(cl.membership == [0, 0, 1, 1]) cl = g.community_label_propagation(initial="initial", fixed=[1,0,0,1]) self.assertTrue(cl.membership == [0, 0, 1, 1] or \ cl.membership == [0, 1, 1, 1] or \ cl.membership == [0, 0, 0, 1]) def testMultilevel(self): # Example graph from the paper g = Graph(16) g += [(0,2), (0,3), (0,4), (0,5), (1,2), (1,4), (1,7), (2,4), (2,5), (2,6), (3,7), (4,10), (5,7), (5,11), (6,7), (6,11), (8,9), (8,10), (8,11), (8,14), (8,15), (9,12), (9,14), (10,11), (10,12), (10,13), (10,14), (11,13)] cls = g.community_multilevel(return_levels=True) self.assertTrue(len(cls) == 2) self.assertTrue(cls[0].membership == [0,0,0,1,0,0,1,1,2,2,2,3,2,3,2,2]) self.assertTrue(cls[1].membership == [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1]) self.assertAlmostEqual(cls[0].q, 0.346301, places=5) self.assertAlmostEqual(cls[1].q, 0.392219, places=5) def testOptimalModularity(self): try: g = Graph.Famous("bull") cl = g.community_optimal_modularity() self.assertTrue(len(cl) == 2) self.assertTrue(cl.membership == [0, 0, 1, 0, 1]) self.assertAlmostEqual(cl.q, 0.08, places=7) ws = [i % 5 for i in xrange(g.ecount())] cl = g.community_optimal_modularity(weights=ws) self.assertAlmostEqual(cl.q, g.modularity(cl.membership, weights=ws), places=7) g = Graph.Famous("zachary") cl = g.community_optimal_modularity() self.assertTrue(len(cl) == 4) self.assertTrue(cl.membership == [0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 1, \ 0, 0, 0, 2, 2, 1, 0, 2, 0, 2, 0, 2, 3, 3, 3, 2, 3, 3, \ 2, 2, 3, 2, 2]) self.assertAlmostEqual(cl.q, 0.4197896, places=7) ws = [2+(i % 3) for i in xrange(g.ecount())] cl = g.community_optimal_modularity(weights=ws) self.assertAlmostEqual(cl.q, g.modularity(cl.membership, weights=ws), places=7) except NotImplementedError: # Well, meh pass def testSpinglass(self): g = Graph.Full(5) + Graph.Full(5) + Graph.Full(5) g += [(0,5), (5,10), (10, 0)] # Spinglass community detection is a bit unstable, so run it three times ok = False for i in xrange(3): cl = g.community_spinglass() if self.reindexMembership(cl) == [0,0,0,0,0,1,1,1,1,1,2,2,2,2,2]: ok = True break self.assertTrue(ok) def testWalktrap(self): g = Graph.Full(5) + Graph.Full(5) + Graph.Full(5) g += [(0,5), (5,10), (10, 0)] cl = g.community_walktrap().as_clustering() self.assertTrue(cl.membership == [0,0,0,0,0,1,1,1,1,1,2,2,2,2,2]) cl = g.community_walktrap(steps=3).as_clustering() self.assertTrue(cl.membership == [0,0,0,0,0,1,1,1,1,1,2,2,2,2,2]) class CohesiveBlocksTests(unittest.TestCase): def genericTests(self, cbs): self.assertTrue(isinstance(cbs, CohesiveBlocks)) self.assertTrue(all(cbs.cohesion(i) == c for i, c in enumerate(cbs.cohesions()))) self.assertTrue(all(cbs.parent(i) == c for i, c in enumerate(cbs.parents()))) self.assertTrue(all(cbs.max_cohesion(i) == c for i, c in enumerate(cbs.max_cohesions()))) def testCohesiveBlocks1(self): # Taken from the igraph R manual g = Graph.Full(4) + Graph(2) + [(3, 4), (4, 5), (4, 2)] g *= 3 g += [(0, 6), (1, 7), (0, 12), (4, 0), (4, 1)] cbs = g.cohesive_blocks() self.genericTests(cbs) self.assertEqual(sorted(list(cbs)), [range(0, 5), range(18), [0, 1, 2, 3, 4, 6, 7, 8, 9, 10], range(6, 10), range(12, 16), range(12, 17)]) self.assertEqual(cbs.cohesions(), [1, 2, 2, 4, 3, 3]) self.assertEqual(cbs.max_cohesions(), [4, 4, 4, 4, 4, 1, 3, 3, 3, 3, 2, 1, 3, 3, 3, 3, 2, 1]) self.assertEqual(cbs.parents(), [None, 0, 0, 1, 2, 1]) def testCohesiveBlocks2(self): # Taken from the Moody-White paper g = Graph.Formula("1-2:3:4:5:6, 2-3:4:5:7, 3-4:6:7, 4-5:6:7, " "5-6:7:21, 6-7, 7-8:11:14:19, 8-9:11:14, 9-10, " "10-12:13, 11-12:14, 12-16, 13-16, 14-15, 15-16, " "17-18:19:20, 18-20:21, 19-20:22:23, 20-21, " "21-22:23, 22-23") cbs = g.cohesive_blocks() self.genericTests(cbs) expected_blocks = [range(7), range(23), range(7)+range(16, 23), range(6, 16), [6, 7, 10, 13]] observed_blocks = sorted(sorted(int(x)-1 for x in g.vs[bl]["name"]) for bl in cbs) self.assertEqual(expected_blocks, observed_blocks) self.assertTrue(cbs.cohesions() == [1, 2, 2, 5, 3]) self.assertTrue(cbs.parents() == [None, 0, 0, 1, 2]) self.assertTrue(sorted(cbs.hierarchy().get_edgelist()) == [(0, 1), (0, 2), (1, 3), (2, 4)]) def testCohesiveBlockingErrors(self): g = Graph.GRG(100, 0.2) g.to_directed() self.assertRaises(InternalError, g.cohesive_blocks) class ComparisonTests(unittest.TestCase): def setUp(self): self.clusterings = [ ([1, 1, 1, 2, 2, 2], [2, 2, 2, 1, 1, 1]), ([1, 1, 1, 2, 2, 2], [1, 1, 2, 2, 3, 3]), ([1, 1, 1, 1, 1, 1], [1, 2, 3, 5, 6, 7]), ([1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3], [3, 1, 2, 1, 3, 1, 3, 1, 2, 1, 4, 2]) ] def _testMethod(self, method, expected): for clusters, result in zip(self.clusterings, expected): self.assertAlmostEqual(compare_communities(method=method, *clusters), result, places=3) def testCompareVI(self): expected = [0, 0.8675, math.log(6)] self._testMethod(None, expected) self._testMethod("vi", expected) def testCompareNMI(self): expected = [1, 0.5158, 0] self._testMethod("nmi", expected) def testCompareSplitJoin(self): expected = [0, 3, 5, 11] self._testMethod("split", expected) l1 = [1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3] l2 = [3, 1, 2, 1, 3, 1, 3, 1, 2, 1, 4, 2] self.assertEqual(split_join_distance(l1, l2), (6, 5)) def testCompareRand(self): expected = [1, 2/3., 0, 0.590909] self._testMethod("rand", expected) def testCompareAdjustedRand(self): expected = [1, 0.242424, 0, -0.04700353] self._testMethod("adjusted_rand", expected) def testRemoveNone(self): l1 = Clustering([1, 1, 1, None, None, 2, 2, 2, 2]) l2 = Clustering([1, 1, 2, 2, None, 2, 3, 3, None]) self.assertAlmostEqual(compare_communities(l1, l2, "nmi", remove_none=True), \ 0.5158, places=3) def suite(): decomposition_suite = unittest.makeSuite(DecompositionTests) clustering_suite = unittest.makeSuite(ClusteringTests) vertex_clustering_suite = unittest.makeSuite(VertexClusteringTests) cover_suite = unittest.makeSuite(CoverTests) community_suite = unittest.makeSuite(CommunityTests) cohesive_blocks_suite = unittest.makeSuite(CohesiveBlocksTests) comparison_suite = unittest.makeSuite(ComparisonTests) return unittest.TestSuite([decomposition_suite, clustering_suite, \ vertex_clustering_suite, cover_suite, community_suite, \ cohesive_blocks_suite, comparison_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/edgeseq.py0000644000076500000240000002751112460535544022226 0ustar ntamasstaff00000000000000# vim:ts=4 sw=4 sts=4: import unittest from igraph import * from igraph.test.utils import skipIf try: import numpy as np except ImportError: np = None class EdgeTests(unittest.TestCase): def setUp(self): self.g = Graph.Full(10) def testHash(self): data = {} n = self.g.ecount() for i in xrange(n): code1 = hash(self.g.es[i]) code2 = hash(self.g.es[i]) self.assertEqual(code1, code2) data[self.g.es[i]] = i for i in xrange(n): self.assertEqual(i, data[self.g.es[i]]) def testRichCompare(self): idxs = [2,5,9,13,42] g2 = Graph.Full(10) for i in idxs: for j in idxs: self.assertEqual(i == j, self.g.es[i] == self.g.es[j]) self.assertEqual(i != j, self.g.es[i] != self.g.es[j]) self.assertEqual(i < j, self.g.es[i] < self.g.es[j]) self.assertEqual(i > j, self.g.es[i] > self.g.es[j]) self.assertEqual(i <= j, self.g.es[i] <= self.g.es[j]) self.assertEqual(i >= j, self.g.es[i] >= self.g.es[j]) self.assertFalse(self.g.es[i] == g2.es[j]) self.assertFalse(self.g.es[i] != g2.es[j]) self.assertFalse(self.g.es[i] < g2.es[j]) self.assertFalse(self.g.es[i] > g2.es[j]) self.assertFalse(self.g.es[i] <= g2.es[j]) self.assertFalse(self.g.es[i] >= g2.es[j]) self.assertFalse(self.g.es[2] == self.g.vs[2]) def testRepr(self): output = repr(self.g.es[0]) self.assertEqual(output, "igraph.Edge(%r, 0, {})" % self.g) self.g.es["weight"] = range(10, 0, -1) output = repr(self.g.es[3]) self.assertEqual(output, "igraph.Edge(%r, 3, {'weight': 7})" % self.g) def testUpdateAttributes(self): e = self.g.es[0] e.update_attributes(a=2) self.assertEqual(e["a"], 2) e.update_attributes([("a", 3), ("b", 4)], c=5, d=6) self.assertEqual(e.attributes(), dict(a=3, b=4, c=5, d=6)) e.update_attributes(dict(b=44, c=55)) self.assertEqual(e.attributes(), dict(a=3, b=44, c=55, d=6)) def testPhantomEdge(self): e = self.g.es[self.g.ecount()-1] e.delete() # v is now a phantom edge; try to freak igraph out now :) self.assertRaises(ValueError, e.update_attributes, a=2) self.assertRaises(ValueError, e.__getitem__, "a") self.assertRaises(ValueError, e.__setitem__, "a", 4) self.assertRaises(ValueError, e.__delitem__, "a") self.assertRaises(ValueError, e.attributes) self.assertRaises(ValueError, getattr, e, "source") self.assertRaises(ValueError, getattr, e, "target") self.assertRaises(ValueError, getattr, e, "tuple") def testProxyMethods(self): g = Graph.GRG(10, 0.5) e = g.es[0] # - delete() is ignored because it mutates the graph ignore = "delete" ignore = set(ignore.split()) # Methods not listed here are expected to return an int or a float return_types = { } for name in Edge.__dict__: if name in ignore: continue func = getattr(e, name) docstr = func.__doc__ if not docstr.startswith("Proxy method"): continue result = func() self.assertEqual(getattr(g, name)(e.index), result, msg=("Edge.%s proxy method misbehaved" % name)) return_type = return_types.get(name, (int, float)) self.assertTrue(isinstance(result, return_type), msg=("Edge.%s proxy method did not return %s" % (name, return_type)) ) class EdgeSeqTests(unittest.TestCase): def setUp(self): self.g = Graph.Full(10) self.g.es["test"] = range(45) def testCreation(self): self.assertTrue(len(EdgeSeq(self.g)) == 45) self.assertTrue(len(EdgeSeq(self.g, 2)) == 1) self.assertTrue(len(EdgeSeq(self.g, [1,2,3])) == 3) self.assertTrue(EdgeSeq(self.g, [1,2,3]).indices == [1,2,3]) self.assertRaises(ValueError, EdgeSeq, self.g, 112) self.assertRaises(ValueError, EdgeSeq, self.g, [112]) self.assertTrue(self.g.es.graph == self.g) def testIndexing(self): for i in xrange(self.g.ecount()): self.assertEqual(i, self.g.es[i].index) self.assertRaises(IndexError, self.g.es.__getitem__, -1) self.assertRaises(TypeError, self.g.es.__getitem__, 1.5) @skipIf(np is None, "test case depends on NumPy") def testNumPyIndexing(self): for i in xrange(self.g.ecount()): arr = np.array([i]) self.assertEqual(i, self.g.es[arr[0]].index) arr = np.array([-1]) self.assertRaises(IndexError, self.g.es.__getitem__, arr[0]) arr = np.array([1.5]) self.assertRaises(TypeError, self.g.es.__getitem__, arr[0]) def testPartialAttributeAssignment(self): only_even = self.g.es.select(lambda e: (e.index % 2 == 0)) only_even["test"] = [0]*len(only_even) expected = [[0,i][i % 2] for i in xrange(self.g.ecount())] self.assertTrue(self.g.es["test"] == expected) only_even["test2"] = range(23) expected = [[i//2, None][i % 2] for i in xrange(self.g.ecount())] self.assertTrue(self.g.es["test2"] == expected) def testSequenceReusing(self): if "test" in self.g.edge_attributes(): del self.g.es["test"] self.g.es["test"] = ["A", "B", "C"] self.assertTrue(self.g.es["test"] == ["A", "B", "C"]*15) self.g.es["test"] = "ABC" self.assertTrue(self.g.es["test"] == ["ABC"] * 45) only_even = self.g.es.select(lambda e: (e.index % 2 == 0)) only_even["test"] = ["D", "E"] expected = ["D", "ABC", "E", "ABC"] * 12 expected = expected[0:45] self.assertTrue(self.g.es["test"] == expected) del self.g.es["test"] only_even["test"] = ["D", "E"] expected = ["D", None, "E", None] * 12 expected = expected[0:45] self.assertTrue(self.g.es["test"] == expected) def testAllSequence(self): self.assertTrue(len(self.g.es) == 45) self.assertTrue(self.g.es["test"] == range(45)) def testEmptySequence(self): empty_es = self.g.es.select(None) self.assertTrue(len(empty_es) == 0) self.assertRaises(IndexError, empty_es.__getitem__, 0) self.assertRaises(KeyError, empty_es.__getitem__, "nonexistent") self.assertTrue(empty_es["test"] == []) empty_es = self.g.es[[]] self.assertTrue(len(empty_es) == 0) empty_es = self.g.es[()] self.assertTrue(len(empty_es) == 0) def testCallableFilteringFind(self): edge = self.g.es.find(lambda e: (e.index % 2 == 1)) self.assertTrue(edge.index == 1) self.assertRaises(IndexError, self.g.es.find, lambda e: (e.index % 2 == 3)) def testCallableFilteringSelect(self): only_even = self.g.es.select(lambda e: (e.index % 2 == 0)) self.assertTrue(len(only_even) == 23) self.assertRaises(KeyError, only_even.__getitem__, "nonexistent") self.assertTrue(only_even["test"] == [i*2 for i in xrange(23)]) def testChainedCallableFilteringSelect(self): only_div_six = self.g.es.select(lambda e: (e.index % 2 == 0), lambda e: (e.index % 3 == 0)) self.assertTrue(len(only_div_six) == 8) self.assertTrue(only_div_six["test"] == [0, 6, 12, 18, 24, 30, 36, 42]) only_div_six = self.g.es.select(lambda e: (e.index % 2 == 0)).select(\ lambda e: (e.index % 3 == 0)) self.assertTrue(len(only_div_six) == 8) self.assertTrue(only_div_six["test"] == [0, 6, 12, 18, 24, 30, 36, 42]) def testIntegerFilteringFind(self): self.assertEqual(self.g.es.find(3).index, 3) self.assertEqual(self.g.es.select(2,3,4,2).find(3).index, 2) self.assertRaises(IndexError, self.g.es.find, 178) def testIntegerFilteringSelect(self): subset = self.g.es.select(2,3,4,2) self.assertTrue(len(subset) == 4) self.assertTrue(subset["test"] == [2,3,4,2]) self.assertRaises(TypeError, self.g.es.select, 2, 3, 4, 2, None) subset = self.g.es[2,3,4,2] self.assertTrue(len(subset) == 4) self.assertTrue(subset["test"] == [2,3,4,2]) def testIterableFilteringSelect(self): subset = self.g.es.select(xrange(5,8)) self.assertTrue(len(subset) == 3) self.assertTrue(subset["test"] == [5,6,7]) def testSliceFilteringSelect(self): subset = self.g.es.select(slice(5, 8)) self.assertTrue(len(subset) == 3) self.assertTrue(subset["test"] == [5,6,7]) subset = self.g.es[40:56:2] self.assertTrue(len(subset) == 3) self.assertTrue(subset["test"] == [40,42,44]) def testKeywordFilteringSelect(self): g = Graph.Barabasi(1000, 2) g.es["betweenness"] = g.edge_betweenness() g.es["parity"] = [i % 2 for i in xrange(g.ecount())] self.assertTrue(len(g.es(betweenness_gt=10)) < 2000) self.assertTrue(len(g.es(betweenness_gt=10, parity=0)) < 2000) def testSourceTargetFiltering(self): g = Graph.Barabasi(1000, 2) es1 = set(e.source for e in g.es.select(_target_in = [2,4])) es2 = set(v1 for v1, v2 in g.get_edgelist() if v2 in [2, 4]) self.assertTrue(es1 == es2) def testWithinFiltering(self): g = Graph.Lattice([10, 10]) vs = [0, 1, 2, 10, 11, 12, 20, 21, 22] vs2 = (0, 1, 10, 11) es1 = g.es.select(_within = vs) es2 = g.es.select(_within = VertexSeq(g, vs)) for es in [es1, es2]: self.assertTrue(len(es) == 12) self.assertTrue(all(e.source in vs and e.target in vs for e in es)) es_filtered = es.select(_within = vs2) self.assertTrue(len(es_filtered) == 4) self.assertTrue(all(e.source in vs2 and e.target in vs2 for e in es_filtered)) def testBetweenFiltering(self): g = Graph.Lattice([10, 10]) vs1, vs2 = [10, 11, 12], [20, 21, 22] es1 = g.es.select(_between = (vs1, vs2)) es2 = g.es.select(_between = (VertexSeq(g, vs1), VertexSeq(g, vs2))) for es in [es1, es2]: self.assertTrue(len(es) == 3) self.assertTrue(all((e.source in vs1 and e.target in vs2) or \ (e.target in vs1 and e.source in vs2) for e in es)) def testIndexOutOfBoundsSelect(self): g = Graph.Full(3) self.assertRaises(ValueError, g.es.select, 4) self.assertRaises(ValueError, g.es.select, 4, 5) self.assertRaises(ValueError, g.es.select, (4, 5)) self.assertRaises(ValueError, g.es.select, 2, -1) self.assertRaises(ValueError, g.es.select, (2, -1)) self.assertRaises(ValueError, g.es.__getitem__, (0, 1000000)) def testGraphMethodProxying(self): idxs = [1, 3, 5, 7, 9] g = Graph.Barabasi(100) es = g.es(*idxs) ebs = g.edge_betweenness() self.assertEqual([ebs[i] for i in idxs], es.edge_betweenness()) idxs = [1, 3] g = Graph([(0, 1), (1, 2), (2, 0), (1, 0)], directed=True) es = g.es(*idxs) mutual = g.is_mutual(es) self.assertEqual(mutual, es.is_mutual()) for e, m in zip(es, mutual): self.assertEqual(e.is_mutual(), m) def testIsAll(self): g = Graph.Full(5) self.assertTrue(g.es.is_all()) self.assertFalse(g.es.select(1,2,3).is_all()) self.assertFalse(g.es.select(_within=[1,2,3]).is_all()) def suite(): edge_suite = unittest.makeSuite(EdgeTests) es_suite = unittest.makeSuite(EdgeSeqTests) return unittest.TestSuite([edge_suite, es_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/flow.py0000644000076500000240000002301112453614202021536 0ustar ntamasstaff00000000000000import unittest from igraph import * from itertools import combinations from random import randint class MaxFlowTests(unittest.TestCase): def setUp(self): self.g = Graph(4, [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)]) self.capacities = [4, 2, 10, 2, 2] self.g.es["capacity"] = self.capacities def testCapacities(self): self.assertTrue(self.capacities == \ self.g.es.get_attribute_values("capacity")) def testEdgeConnectivity(self): self.assertTrue(self.g.edge_connectivity(0, 3) == 2) self.assertTrue(Graph.Barabasi(50).edge_connectivity() == 1) self.assertTrue(self.g.adhesion() == 2) self.assertTrue(Graph.Tree(10, 3).adhesion() == 1) self.assertTrue(Graph.Tree(10, 3, TREE_OUT).adhesion() == 0) self.assertRaises(ValueError, self.g.edge_connectivity, 0) def testVertexConnectivity(self): self.assertTrue(self.g.vertex_connectivity(0, 3) == 2) self.assertTrue(Graph.Barabasi(50).vertex_connectivity() == 1) self.assertTrue(self.g.cohesion() == 2) self.assertTrue(Graph.Tree(10, 3).cohesion() == 1) self.assertTrue(Graph.Tree(10, 3, TREE_OUT).cohesion() == 0) self.assertRaises(ValueError, self.g.vertex_connectivity, 0) self.assertRaises(InternalError, self.g.vertex_connectivity, 0, 1) self.assertTrue(self.g.vertex_connectivity(0, 1, neighbors="nodes") == 4) self.assertTrue(self.g.vertex_connectivity(0, 1, neighbors="negative") == -1) def testMaxFlowValue(self): self.assertTrue(self.g.maxflow_value(0, 3) == 2) self.assertTrue(self.g.maxflow_value(0, 3, self.capacities) == 4) self.assertTrue(self.g.maxflow_value(0, 3, "capacity") == 4) self.assertRaises(KeyError, self.g.maxflow_value, 0, 3, "unknown") def testMaxFlow(self): flow = self.g.maxflow(0, 3) self.assertEqual(flow.value, 2) self.assertEqual(flow.flow, [1, 1, 0, 1, 1]) flow = self.g.maxflow(0, 3, "capacity") self.assertEqual(flow.value, 4) self.assertEqual(flow.cut, [3, 4]) self.assertEqual([e.index for e in flow.es], [3, 4]) self.assertTrue(set(flow.partition[0]).union(flow.partition[1]) == \ set(range(self.g.vcount()))) self.assertRaises(KeyError, self.g.maxflow, 0, 3, "unknown") class CutTests(unittest.TestCase): def constructSimpleGraph(self, directed=False): g = Graph(4, [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)], directed) g.es["capacity"] = [4, 2, 10, 2, 2] return g def constructLadderGraph(self, directed=False): el = zip(range(0, 5), range(1, 6)) el += zip(range(6, 11), range(7, 12)) el += zip(range(0, 6), range(6, 12)) g = Graph(el, directed=directed) return g def testMinCutValue(self): g = self.constructSimpleGraph() self.assertTrue(g.mincut_value(0, 3) == 2) self.assertTrue(g.mincut_value(0, 3, g.es["capacity"]) == 4) self.assertTrue(g.mincut_value(0, 3, "capacity") == 4) self.assertRaises(KeyError, g.mincut_value, 0, 3, "unknown") self.assertTrue(g.mincut_value() == 2) self.assertTrue(g.mincut_value(source=0) == 2) self.assertTrue(g.mincut_value(target=2) == 2) def testMinCut(self): g = self.constructSimpleGraph() mc = g.mincut() self.assertTrue(isinstance(mc, Cut)) self.assertTrue(mc.value == 2) self.assertTrue(set(mc.partition[0]).union(mc.partition[1]) == \ set(range(g.vcount()))) self.assertTrue(isinstance(str(mc), str)) self.assertTrue(isinstance(repr(mc), str)) self.assertTrue(isinstance(mc.es, EdgeSeq)) self.assertTrue(len(mc.es) == 2) mc = g.mincut(capacity="capacity") self.assertTrue(mc.value == 4) self.assertRaises(KeyError, g.mincut, capacity="unknown") def testMinCutWithSourceAndTarget(self): g = self.constructSimpleGraph() mc = g.mincut(0, 3, "capacity") self.assertTrue(isinstance(mc, Cut)) self.assertTrue(mc.cut == [3, 4]) self.assertTrue(mc.value == 4) self.assertTrue(set(mc.partition[0]).union(mc.partition[1]) == \ set(range(g.vcount()))) mc = g.mincut(0, 3) self.assertTrue(isinstance(mc, Cut)) self.assertTrue(mc.cut == [3, 4]) self.assertTrue(mc.value == 2) mc = g.mincut(2, 0, "capacity") self.assertTrue(isinstance(mc, Cut)) self.assertTrue(mc.cut == [0, 1]) self.assertTrue(mc.value == 6) self.assertRaises(ValueError, g.mincut, 2, capacity="capacity") def testStMinCut(self): g = self.constructSimpleGraph() mc = g.st_mincut(0, 3, "capacity") self.assertTrue(isinstance(mc, Cut)) self.assertTrue(mc.cut == [3, 4]) self.assertTrue(mc.value == 4) self.assertTrue(set(mc.partition[0]).union(mc.partition[1]) == \ set(range(g.vcount()))) mc = g.st_mincut(0, 3) self.assertTrue(isinstance(mc, Cut)) self.assertTrue(mc.cut == [3, 4]) self.assertTrue(mc.value == 2) mc = g.st_mincut(2, 0, "capacity") self.assertTrue(isinstance(mc, Cut)) self.assertTrue(mc.cut == [0, 1]) self.assertTrue(mc.value == 6) self.assertRaises(KeyError, g.st_mincut, 2, 0, capacity="unknown") def testAllSTCuts1(self): # Simple graph with four vertices g = self.constructSimpleGraph(directed=True) partitions = [((0, 1, 1, 1), 2), ((0, 0, 1, 1), 3), ((0, 1, 0, 1), 2), ((0, 0, 0, 1), 2)] values = dict(partitions) partitions = [partition for partition, _ in partitions] for cut in g.all_st_cuts(0,3): membership = tuple(cut.membership) self.assertTrue(membership in partitions, "%r not found among expected partitions" % (membership,)) self.assertEqual(cut.value, values[membership]) self.assertEqual(len(cut.es), values[membership]) partitions.remove(membership) self.assertTrue(partitions == [], "expected partitions not seen: %r" % (partitions, )) def testAllSTCuts2(self): # "Ladder graph" g = self.constructLadderGraph(directed=True) cuts = g.all_st_cuts(0, 11) self.assertEqual(len(cuts), 36) self.assertEqual(len(set(tuple(cut.membership) for cut in cuts)), 36) for cut in cuts: g2 = g.copy() g2.delete_edges(cut.es) self.assertFalse(g2.is_connected(), "%r is not a real cut" % (cut.membership,)) self.assertFalse(cut.value < 2 or cut.value > 6) def testAllSTMinCuts2(self): # "Ladder graph" g = self.constructLadderGraph() g.to_directed("mutual") cuts = g.all_st_mincuts(0, 11) self.assertEqual(len(cuts), 7) self.assertEqual(len(set(tuple(cut.membership) for cut in cuts)), 7) for cut in cuts: self.assertEqual(cut.value, 2) g2 = g.copy() g2.delete_edges(cut.es) self.assertFalse(g2.is_connected(), "%r is not a real cut" % (cut.membership,)) g.es["capacity"] = [2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1] cuts = g.all_st_mincuts(0, 11, "capacity") self.assertEqual(len(cuts), 2) self.assertEqual(cuts[0].membership, [0,0,1,1,1,1,0,0,1,1,1,1]) self.assertEqual(cuts[1].membership, [0,0,0,0,1,1,0,0,0,0,1,1]) self.assertEqual(cuts[0].value, 2) self.assertEqual(cuts[1].value, 2) class GomoryHuTests(unittest.TestCase): def testEmpty(self): g = Graph() t = g.gomory_hu_tree() self.assertEqual(0, t.vcount()) self.assertEqual(0, t.ecount()) def testSimpleExample(self): g = Graph(6, [(0,1),(0,2),(1,2),(1,3),(1,4),(2,4),(3,4),(3,5),(4,5)], \ directed=False) g.es["capacity"] = [1,7,1,3,2,4,1,6,2] t = g.gomory_hu_tree("capacity") self.validate_gomory_hu_tree(g, t) def testDirected(self): g = Graph(6, [(0,1),(0,2),(1,2),(1,3),(1,4),(2,4),(3,4),(3,5),(4,5)], \ directed=True) g.es["capacity"] = [1,7,1,3,2,4,1,6,2] self.assertRaises(InternalError, g.gomory_hu_tree, "capacity") def testRandomGRG(self): g = Graph.GRG(25, 0.4) self.validate_gomory_hu_tree(g, g.gomory_hu_tree()) g.es["capacity"] = [randint(0, 10) for _ in xrange(g.ecount())] self.validate_gomory_hu_tree(g, g.gomory_hu_tree("capacity")) def validate_gomory_hu_tree(self, g, t): n = g.vcount() self.assertEqual(n, t.vcount()) self.assertEqual(n-1, t.ecount()) self.assertFalse(t.is_directed()) if "capacity" in g.edge_attributes(): capacities = g.es["capacity"] else: capacities = None for i, j in combinations(range(n), 2): path = t.get_shortest_paths(i, j, output="epath") if path: path = path[0] expected_flow = min(t.es[path]["flow"]) observed_flow = g.maxflow_value(i, j, capacities) self.assertEqual(observed_flow, expected_flow) def suite(): flow_suite = unittest.makeSuite(MaxFlowTests) cut_suite = unittest.makeSuite(CutTests) gomory_hu_suite = unittest.makeSuite(GomoryHuTests) return unittest.TestSuite([flow_suite, cut_suite, gomory_hu_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/foreign.py0000644000076500000240000001532712460534506022241 0ustar ntamasstaff00000000000000from __future__ import with_statement import unittest from igraph import * from igraph.test.utils import temporary_file class ForeignTests(unittest.TestCase): def testDIMACS(self): with temporary_file(u"""\ c c This is a simple example file to demonstrate the c DIMACS input file format for minimum-cost flow problems. c c problem line : p max 4 5 c c node descriptor lines : n 1 s n 4 t c c arc descriptor lines : a 1 2 4 a 1 3 2 a 2 3 2 a 2 4 3 a 3 4 5 """) as tmpfname: graph = Graph.Read_DIMACS(tmpfname, False) self.assertTrue(isinstance(graph, Graph)) self.assertTrue(graph.vcount() == 4 and graph.ecount() == 5) self.assertTrue(graph["source"] == 0 and graph["target"] == 3) self.assertTrue(graph.es["capacity"] == [4,2,2,3,5]) graph.write_dimacs(tmpfname) def testDL(self): with temporary_file(u"""\ dl n=5 format = fullmatrix labels embedded data: larry david lin pat russ Larry 0 1 1 1 0 david 1 0 0 0 1 Lin 1 0 0 1 0 Pat 1 0 1 0 1 russ 0 1 0 1 0 """) as tmpfname: g = Graph.Read_DL(tmpfname) self.assertTrue(isinstance(g, Graph)) self.assertTrue(g.vcount() == 5 and g.ecount() == 12) self.assertTrue(g.is_directed()) self.assertTrue(sorted(g.get_edgelist()) == \ [(0,1),(0,2),(0,3),(1,0),(1,4),(2,0),(2,3),(3,0),\ (3,2),(3,4),(4,1),(4,3)]) with temporary_file(u"""\ dl n=5 format = fullmatrix labels: barry,david lin,pat russ data: 0 1 1 1 0 1 0 0 0 1 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 """) as tmpfname: g = Graph.Read_DL(tmpfname) self.assertTrue(isinstance(g, Graph)) self.assertTrue(g.vcount() == 5 and g.ecount() == 12) self.assertTrue(g.is_directed()) self.assertTrue(sorted(g.get_edgelist()) == \ [(0,1),(0,2),(0,3),(1,0),(1,4),(2,0),(2,3),(3,0),\ (3,2),(3,4),(4,1),(4,3)]) with temporary_file(u"""\ DL n=5 format = edgelist1 labels: george, sally, jim, billy, jane labels embedded: data: george sally 2 george jim 3 sally jim 4 billy george 5 jane jim 6 """) as tmpfname: g = Graph.Read_DL(tmpfname, False) self.assertTrue(isinstance(g, Graph)) self.assertTrue(g.vcount() == 5 and g.ecount() == 5) self.assertTrue(not g.is_directed()) self.assertTrue(sorted(g.get_edgelist()) == \ [(0,1),(0,2),(0,3),(1,2),(2,4)]) def _testNCOLOrLGL(self, func, fname): g = func(fname, names=False, weights=False, \ directed=False) self.assertTrue(isinstance(g, Graph)) self.assertTrue(g.vcount() == 4 and g.ecount() == 5) self.assertTrue(not g.is_directed()) self.assertTrue(sorted(g.get_edgelist()) == \ [(0,1),(0,2),(1,1),(1,3),(2,3)]) self.assertTrue("name" not in g.vertex_attributes() and \ "weight" not in g.edge_attributes()) g = func(fname, names=False, \ directed=False) self.assertTrue("name" not in g.vertex_attributes() and \ "weight" in g.edge_attributes()) self.assertTrue(g.es["weight"] == [1, 2, 0, 3, 0]) g = func(fname, directed=False) self.assertTrue("name" in g.vertex_attributes() and \ "weight" in g.edge_attributes()) self.assertTrue(g.vs["name"] == ["eggs", "spam", "ham", "bacon"]) self.assertTrue(g.es["weight"] == [1, 2, 0, 3, 0]) def testNCOL(self): with temporary_file(u"""\ eggs spam 1 ham eggs 2 ham bacon bacon spam 3 spam spam""") as tmpfname: self._testNCOLOrLGL(func=Graph.Read_Ncol, fname=tmpfname) with temporary_file(u"""\ eggs spam ham eggs ham bacon bacon spam spam spam""") as tmpfname: g = Graph.Read_Ncol(tmpfname) self.assertTrue("name" in g.vertex_attributes() and \ "weight" not in g.edge_attributes()) def testLGL(self): with temporary_file(u"""\ # eggs spam 1 # ham eggs 2 bacon # bacon spam 3 # spam spam""") as tmpfname: self._testNCOLOrLGL(func=Graph.Read_Lgl, fname=tmpfname) with temporary_file(u"""\ # eggs spam # ham eggs bacon # bacon spam # spam spam""") as tmpfname: g = Graph.Read_Lgl(tmpfname) self.assertTrue("name" in g.vertex_attributes() and \ "weight" not in g.edge_attributes()) def testAdjacency(self): with temporary_file(u"""\ # Test comment line 0 1 1 0 0 0 1 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 2 2 0 0 0 2 0 2 0 0 0 2 2 0 """) as tmpfname: g = Graph.Read_Adjacency(tmpfname) self.assertTrue(isinstance(g, Graph)) self.assertTrue(g.vcount() == 6 and g.ecount() == 18 and g.is_directed() and "weight" not in g.edge_attributes()) g = Graph.Read_Adjacency(tmpfname, attribute="weight") self.assertTrue(isinstance(g, Graph)) self.assertTrue(g.vcount() == 6 and g.ecount() == 12 and g.is_directed() and g.es["weight"] == [1,1,1,1,1,1,2,2,2,2,2,2]) g.write_adjacency(tmpfname) def testPickle(self): pickle = [128, 2, 99, 105, 103, 114, 97, 112, 104, 10, 71, 114, 97, 112, 104, 10, 113, 1, 40, 75, 3, 93, 113, 2, 75, 1, 75, 2, 134, 113, 3, 97, 137, 125, 125, 125, 116, 82, 113, 4, 125, 98, 46] if sys.version_info > (3, 0): pickle = bytes(pickle) else: pickle = "".join(map(chr, pickle)) with temporary_file(pickle, "wb") as tmpfname: g = Graph.Read_Pickle(pickle) self.assertTrue(isinstance(g, Graph)) self.assertTrue(g.vcount() == 3 and g.ecount() == 1 and not g.is_directed()) g.write_pickle(tmpfname) def suite(): foreign_suite = unittest.makeSuite(ForeignTests) return unittest.TestSuite([foreign_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/games.py0000644000076500000240000001434612453614202021676 0ustar ntamasstaff00000000000000import unittest from igraph import * class GameTests(unittest.TestCase): def testGRG(self): g = Graph.GRG(50, 0.2) self.assertTrue(isinstance(g, Graph)) g = Graph.GRG(50, 0.2, True) self.assertTrue(isinstance(g, Graph)) self.assertTrue("x" in g.vertex_attributes()) self.assertTrue("y" in g.vertex_attributes()) self.assertTrue(isinstance(Layout(zip(g.vs["x"], g.vs["y"])), Layout)) def testForestFire(self): g=Graph.Forest_Fire(100, 0.1) self.assertTrue(isinstance(g, Graph) and g.is_directed() == False) g=Graph.Forest_Fire(100, 0.1, directed=True) self.assertTrue(isinstance(g, Graph) and g.is_directed() == True) def testRecentDegree(self): g=Graph.Recent_Degree(100, 5, 10) self.assertTrue(isinstance(g, Graph)) def testPreference(self): g=Graph.Preference(100, [1, 1], [[1, 0], [0, 1]]) self.assertTrue(isinstance(g, Graph) and len(g.clusters()) == 2) g=Graph.Preference(100, [1, 1], [[1, 0], [0, 1]], attribute="type") l=g.vs.get_attribute_values("type") self.assertTrue(min(l) == 0 and max(l) == 1) def testAsymmetricPreference(self): g=Graph.Asymmetric_Preference(100, [[0, 1], [1, 0]], [[0, 1], [1, 0]]) self.assertTrue(isinstance(g, Graph) and len(g.clusters()) == 2) g=Graph.Asymmetric_Preference(100, [[0, 1], [1, 0]], [[1, 0], [0, 1]],\ attribute="type") l=g.vs.get_attribute_values("type") l1=[i[0] for i in l] l2=[i[1] for i in l] self.assertTrue(min(l1) == 0 and max(l1) == 1 and min(l2) == 0 and max(l2) == 1) g=Graph.Asymmetric_Preference(100, [[0, 1], [1, 0]], [[1, 0], [0, 1]]) self.assertTrue(isinstance(g, Graph) and len(g.clusters()) == 1) def testWattsStrogatz(self): g=Graph.Watts_Strogatz(1, 20, 1, 0.2) self.assertTrue(isinstance(g, Graph) and g.vcount()==20 and g.ecount()==20) def testRandomBipartiteNP(self): # Test np mode, undirected g = Graph.Random_Bipartite(10, 20, p=0.25) self.assertTrue(g.is_simple()) self.assertTrue(g.is_bipartite()) self.assertFalse(g.is_directed()) self.assertEqual([False]*10 + [True]*20, g.vs["type"]) # Test np mode, directed, "out" g = Graph.Random_Bipartite(10, 20, p=0.25, directed=True, neimode="out") self.assertTrue(g.is_simple()) self.assertTrue(g.is_bipartite()) self.assertTrue(g.is_directed()) self.assertEqual([False]*10 + [True]*20, g.vs["type"]) self.assertTrue(all(g.vs[e.tuple]["type"] == [False, True] for e in g.es)) # Test np mode, directed, "in" g = Graph.Random_Bipartite(10, 20, p=0.25, directed=True, neimode="in") self.assertTrue(g.is_simple()) self.assertTrue(g.is_bipartite()) self.assertTrue(g.is_directed()) self.assertEqual([False]*10 + [True]*20, g.vs["type"]) self.assertTrue(all(g.vs[e.tuple]["type"] == [True, False] for e in g.es)) # Test np mode, directed, "all" g = Graph.Random_Bipartite(10, 20, p=0.25, directed=True, neimode="all") self.assertTrue(g.is_simple()) self.assertTrue(g.is_bipartite()) self.assertTrue(g.is_directed()) self.assertEqual([False]*10 + [True]*20, g.vs["type"]) def testRandomBipartiteNM(self): # Test np mode, undirected g = Graph.Random_Bipartite(10, 20, m=50) self.assertTrue(g.is_simple()) self.assertTrue(g.is_bipartite()) self.assertFalse(g.is_directed()) self.assertEqual(50, g.ecount()) self.assertEqual([False]*10 + [True]*20, g.vs["type"]) # Test np mode, directed, "out" g = Graph.Random_Bipartite(10, 20, m=50, directed=True, neimode="out") self.assertTrue(g.is_simple()) self.assertTrue(g.is_bipartite()) self.assertTrue(g.is_directed()) self.assertEqual(50, g.ecount()) self.assertEqual([False]*10 + [True]*20, g.vs["type"]) self.assertTrue(all(g.vs[e.tuple]["type"] == [False, True] for e in g.es)) # Test np mode, directed, "in" g = Graph.Random_Bipartite(10, 20, m=50, directed=True, neimode="in") self.assertTrue(g.is_simple()) self.assertTrue(g.is_bipartite()) self.assertTrue(g.is_directed()) self.assertEqual(50, g.ecount()) self.assertEqual([False]*10 + [True]*20, g.vs["type"]) self.assertTrue(all(g.vs[e.tuple]["type"] == [True, False] for e in g.es)) # Test np mode, directed, "all" g = Graph.Random_Bipartite(10, 20, m=50, directed=True, neimode="all") self.assertTrue(g.is_simple()) self.assertTrue(g.is_bipartite()) self.assertTrue(g.is_directed()) self.assertEqual(50, g.ecount()) self.assertEqual([False]*10 + [True]*20, g.vs["type"]) def testRewire(self): # Undirected graph g=Graph.GRG(25, 0.4) degrees=g.degree() # Rewiring without loops g.rewire(10000) self.assertEqual(degrees, g.degree()) self.assertTrue(g.is_simple()) # Rewiring with loops (1) g.rewire(10000, mode="loops") self.assertEqual(degrees, g.degree()) self.assertFalse(any(g.is_multiple())) # Rewiring with loops (2) g = Graph.Full(4) g[1,3] = 0 degrees = g.degree() g.rewire(100, mode="loops") self.assertEqual(degrees, g.degree()) self.assertFalse(any(g.is_multiple())) # Directed graph g=Graph.GRG(25, 0.4) g.to_directed("mutual") indeg, outdeg = g.indegree(), g.outdegree() g.rewire(10000) self.assertEqual(indeg, g.indegree()) self.assertEqual(outdeg, g.outdegree()) self.assertTrue(g.is_simple()) # Directed graph with loops g.rewire(10000, mode="loops") self.assertEqual(indeg, g.indegree()) self.assertEqual(outdeg, g.outdegree()) self.assertFalse(any(g.is_multiple())) def suite(): game_suite = unittest.makeSuite(GameTests) return unittest.TestSuite([game_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/generators.py0000644000076500000240000001531312453614202022746 0ustar ntamasstaff00000000000000import unittest from igraph import * class GeneratorTests(unittest.TestCase): def testStar(self): g=Graph.Star(5, "in") el=[(1,0),(2,0),(3,0),(4,0)] self.assertTrue(g.is_directed()) self.assertTrue(g.get_edgelist() == el) g=Graph.Star(5, "out", center=2) el=[(2,0),(2,1),(2,3),(2,4)] self.assertTrue(g.is_directed()) self.assertTrue(g.get_edgelist() == el) g=Graph.Star(5, "mutual", center=2) el=[(0,2),(1,2),(2,0),(2,1),(2,3),(2,4),(3,2),(4,2)] self.assertTrue(g.is_directed()) self.assertTrue(sorted(g.get_edgelist()) == el) g=Graph.Star(5, center=3) el=[(0,3),(1,3),(2,3),(3,4)] self.assertTrue(not g.is_directed()) self.assertTrue(sorted(g.get_edgelist()) == el) def testFamous(self): g=Graph.Famous("tutte") self.assertTrue(g.vcount() == 46 and g.ecount() == 69) self.assertRaises(InternalError, Graph.Famous, "unknown") def testFormula(self): tests = [ (None, [], []), ("", [""], []), ("A", ["A"], []), ("A-B", ["A", "B"], [(0, 1)]), ("A --- B", ["A", "B"], [(0, 1)]), ("A--B, C--D, E--F, G--H, I, J, K", ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"], [(0,1), (2,3), (4,5), (6,7)] ), ("A:B:C:D -- A:B:C:D", ["A", "B", "C", "D"], [(0,1), (0,2), (0,3), (1,2), (1,3), (2,3)] ), ("A -> B -> C", ["A", "B", "C"], [(0,1), (1,2)]), ("A <- B -> C", ["A", "B", "C"], [(1,0), (1,2)]), ("A <- B -- C", ["A", "B", "C"], [(1,0)]), ("A <-> B <---> C <> D", ["A", "B", "C", "D"], [(0,1), (1,0), (1,2), (2,1), (2,3), (3,2)]), ("'this is' <- 'a silly' -> 'graph here'", ["this is", "a silly", "graph here"], [(1,0), (1,2)]), ("Alice-Bob-Cecil-Alice, Daniel-Cecil-Eugene, Cecil-Gordon", ["Alice", "Bob", "Cecil", "Daniel", "Eugene", "Gordon"], [(0,1),(1,2),(0,2),(2,3),(2,4),(2,5)] ), ("Alice-Bob:Cecil:Daniel, Cecil:Daniel-Eugene:Gordon", ["Alice", "Bob", "Cecil", "Daniel", "Eugene", "Gordon"], [(0,1),(0,2),(0,3),(2,4),(2,5),(3,4),(3,5)] ), ("Alice <-> Bob --> Cecil <-- Daniel, Eugene --> Gordon:Helen", ["Alice", "Bob", "Cecil", "Daniel", "Eugene", "Gordon", "Helen"], [(0,1),(1,0),(1,2),(3,2),(4,5),(4,6)] ), ("Alice -- Bob -- Daniel, Cecil:Gordon, Helen", ["Alice", "Bob", "Daniel", "Cecil", "Gordon", "Helen"], [(0,1),(1,2)] ), ('"+" -- "-", "*" -- "/", "%%" -- "%/%"', ["+", "-", "*", "/", "%%", "%/%"], [(0,1),(2,3),(4,5)] ) ] for formula, names, edges in tests: g = Graph.Formula(formula) self.assertEqual(g.vs["name"], names) self.assertEqual(g.get_edgelist(), sorted(edges)) def testFull(self): g=Graph.Full(20, directed=True) el=g.get_edgelist() el.sort() self.assertTrue(g.get_edgelist() == [(x, y) for x in range(20) for y in range(20) if x!=y]) def testFullCitation(self): g=Graph.Full_Citation(20) el=g.get_edgelist() el.sort() self.assertTrue(not g.is_directed()) self.assertTrue(el == [(x, y) for x in xrange(19) for y in xrange(x+1, 20)]) g=Graph.Full_Citation(20, True) el=g.get_edgelist() el.sort() self.assertTrue(g.is_directed()) self.assertTrue(el == [(x, y) for x in xrange(1, 20) for y in xrange(x)]) self.assertRaises(InternalError, Graph.Full_Citation, -2) def testLCF(self): g1=Graph.LCF(12, (5, -5), 6) g2=Graph.Famous("Franklin") self.assertTrue(g1.isomorphic(g2)) self.assertRaises(InternalError, Graph.LCF, 12, (5, -5), -3) def testKautz(self): g=Graph.Kautz(2, 2) deg_in=g.degree(mode=IN) deg_out=g.degree(mode=OUT) # This is not a proper test, but should spot most errors self.assertTrue(g.is_directed() and deg_in==[2]*12 and deg_out==[2]*12) def testDeBruijn(self): g=Graph.De_Bruijn(2, 3) deg_in=g.degree(mode=IN, loops=True) deg_out=g.degree(mode=OUT, loops=True) # This is not a proper test, but should spot most errors self.assertTrue(g.is_directed() and deg_in==[2]*8 and deg_out==[2]*8) def testSBM(self): pref_matrix = [[0.5, 0, 0], [0, 0, 0.5], [0, 0.5, 0]] n = 60 types = [20, 20, 20] g = Graph.SBM(n, pref_matrix, types) # Simple smoke tests for the expected structure of the graph self.assertTrue(g.is_simple()) self.assertFalse(g.is_directed()) self.assertEqual([0]*20 + [1]*40, g.clusters().membership) g2 = g.subgraph(range(20, 60)) self.assertTrue(not any(e.source // 20 == e.target // 20 for e in g2.es)) # Check loops argument g = Graph.SBM(n, pref_matrix, types, loops=True) self.assertFalse(g.is_simple()) self.assertTrue(sum(g.is_loop()) > 0) # Check directedness g = Graph.SBM(n, pref_matrix, types, directed=True) self.assertTrue(g.is_directed()) self.assertTrue(sum(g.is_mutual()) < g.ecount()) self.assertTrue(sum(g.is_loop()) == 0) # Check error conditions self.assertRaises(InternalError, Graph.SBM, -1, pref_matrix, types) self.assertRaises(InternalError, Graph.SBM, 61, pref_matrix, types) pref_matrix[0][1] = 0.7 self.assertRaises(InternalError, Graph.SBM, 60, pref_matrix, types) def testWeightedAdjacency(self): mat = [[0, 1, 2, 0], [2, 0, 0, 0], [0, 0, 2.5, 0], [0, 1, 0, 0]] g = Graph.Weighted_Adjacency(mat, attr="w0") el = g.get_edgelist() self.assertTrue(el == [(0,1), (0,2), (1,0), (2,2), (3,1)]) self.assertTrue(g.es["w0"] == [1, 2, 2, 2.5, 1]) g = Graph.Weighted_Adjacency(mat, mode="plus") el = g.get_edgelist() self.assertTrue(el == [(0,1), (0,2), (1,3), (2,2)]) self.assertTrue(g.es["weight"] == [3, 2, 1, 2.5]) g = Graph.Weighted_Adjacency(mat, attr="w0", loops=False) el = g.get_edgelist() self.assertTrue(el == [(0,1), (0,2), (1,0), (3,1)]) self.assertTrue(g.es["w0"] == [1, 2, 2, 1]) def suite(): generator_suite = unittest.makeSuite(GeneratorTests) return unittest.TestSuite([generator_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/homepage.py0000644000076500000240000000273512453614202022366 0ustar ntamasstaff00000000000000import unittest from igraph import * class HomepageExampleTests(unittest.TestCase): """Smoke tests for the Python examples found on the homepage to ensure that they do not break.""" def testErdosRenyiComponents(self): g = Graph.Erdos_Renyi(n=300, m=250) colors = ["lightgray", "cyan", "magenta", "yellow", "blue", "green", "red"] components = g.components() for component in components: color = colors[min(6, len(components)-1)] g.vs[component]["color"] = color # No plotting here, but we calculate the FR layout fr = g.layout("fr") def testKautz(self): g = Graph.Kautz(m=3, n=2) adj = g.get_adjacency() # Plotting omitted def testMSTofGRG(self): def distance(p1, p2): return ((p1[0]-p2[0]) ** 2 + (p1[1]-p2[1]) ** 2) ** 0.5 g = Graph.GRG(100, 0.2) layout = Layout(zip(g.vs["x"], g.vs["y"])) weights = [distance(layout[edge.source], layout[edge.target]) \ for edge in g.es] max_weight = max(weights) g.es["width"] = [6 - 5*weight / max_weight for weight in weights] mst = g.spanning_tree(weights) # Plotting omitted def suite(): homepage_example_suite = unittest.makeSuite(HomepageExampleTests) return unittest.TestSuite([homepage_example_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/indexing.py0000644000076500000240000000352412453614202022403 0ustar ntamasstaff00000000000000# vim:ts=4 sw=4 sts=4: import unittest from igraph import * class GraphAdjacencyMatrixLikeIndexingTests(unittest.TestCase): def testSingleEdgeRetrieval(self): g = Graph.Famous("krackhardt_kite") for v1, v2 in g.get_edgelist(): self.assertEqual(g[v1, v2], 1) self.assertEqual(g[v2, v1], 1) for v1 in xrange(g.vcount()): for v2 in set(range(g.vcount())) - set(g.neighbors(v1)): self.assertEqual(g[v1, v2], 0) self.assertEqual(g[v2, v1], 0) g.add_edge(1, 1) self.assertEqual(g[1, 1], 1) def testSingleEdgeRetrievalWeights(self): g = Graph.Famous("krackhardt_kite") g.es["weight"] = range(g.ecount()) for idx, (v1, v2) in enumerate(g.get_edgelist()): self.assertEqual(g[v1, v2], idx) self.assertEqual(g[v2, v1], idx) for v1 in xrange(g.vcount()): for v2 in set(range(g.vcount())) - set(g.neighbors(v1)): self.assertEqual(g[v1, v2], 0) self.assertEqual(g[v2, v1], 0) def testSingleEdgeRetrievalAttrName(self): g = Graph.Famous("krackhardt_kite") g.es["value"] = range(20, g.ecount()+20) for idx, (v1, v2) in enumerate(g.get_edgelist()): self.assertEqual(g[v1, v2, "value"], idx+20) self.assertEqual(g[v2, v1, "value"], idx+20) for v1 in xrange(g.vcount()): for v2 in set(range(g.vcount())) - set(g.neighbors(v1)): self.assertEqual(g[v1, v2, "value"], 0) self.assertEqual(g[v2, v1, "value"], 0) def suite(): adjacency_suite = unittest.makeSuite(GraphAdjacencyMatrixLikeIndexingTests) return unittest.TestSuite([adjacency_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/isomorphism.py0000644000076500000240000002641012453614202023146 0ustar ntamasstaff00000000000000import unittest from igraph import * from itertools import permutations from random import shuffle def node_compat(g1, g2, v1, v2): """Node compatibility function for isomorphism tests""" return g1.vs[v1]["color"] == g2.vs[v2]["color"] def edge_compat(g1, g2, e1, e2): """Edge compatibility function for isomorphism tests""" return g1.es[e1]["color"] == g2.es[e2]["color"] class IsomorphismTests(unittest.TestCase): def testIsomorphic(self): g1 = Graph(8, [(0, 4), (0, 5), (0, 6), \ (1, 4), (1, 5), (1, 7), \ (2, 4), (2, 6), (2, 7), \ (3, 5), (3, 6), (3, 7)]) g2 = Graph(8, [(0, 1), (0, 3), (0, 4), \ (2, 3), (2, 1), (2, 6), \ (5, 1), (5, 4), (5, 6), \ (7, 3), (7, 6), (7, 4)]) # Test the isomorphy of g1 and g2 self.assertTrue(g1.isomorphic(g2)) self.assertTrue(g2.isomorphic_vf2(g1, return_mapping_21=True) \ == (True, None, [0, 2, 5, 7, 1, 3, 4, 6])) self.assertTrue(g2.isomorphic_bliss(g1, return_mapping_21=True, sh2="fl")\ == (True, None, [0, 2, 5, 7, 1, 3, 4, 6])) self.assertRaises(ValueError, g2.isomorphic_bliss, g1, sh2="nonexistent") # Test the automorphy of g1 self.assertTrue(g1.isomorphic()) self.assertTrue(g1.isomorphic_vf2(return_mapping_21=True) \ == (True, None, [0, 1, 2, 3, 4, 5, 6, 7])) # Test VF2 with colors self.assertTrue(g1.isomorphic_vf2(g2, color1=[0,1,0,1,0,1,0,1], color2=[0,0,1,1,0,0,1,1])) g1.vs["color"] = [0,1,0,1,0,1,0,1] g2.vs["color"] = [0,0,1,1,0,1,1,0] self.assertTrue(not g1.isomorphic_vf2(g2, "color", "color")) # Test VF2 with vertex and edge colors self.assertTrue(g1.isomorphic_vf2(g2, color1=[0,1,0,1,0,1,0,1], color2=[0,0,1,1,0,0,1,1])) g1.es["color"] = range(12) g2.es["color"] = [0]*6 + [1]*6 self.assertTrue(not g1.isomorphic_vf2(g2, "color", "color", "color", "color")) # Test VF2 with node compatibility function g2.vs["color"] = [0,0,1,1,0,0,1,1] self.assertTrue(g1.isomorphic_vf2(g2, node_compat_fn=node_compat)) g2.vs["color"] = [0,0,1,1,0,1,1,0] self.assertTrue(not g1.isomorphic_vf2(g2, node_compat_fn=node_compat)) # Test VF2 with node edge compatibility function g2.vs["color"] = [0,0,1,1,0,0,1,1] g1.es["color"] = range(12) g2.es["color"] = [0]*6 + [1]*6 self.assertTrue(not g1.isomorphic_vf2(g2, node_compat_fn=node_compat, edge_compat_fn=edge_compat)) def testIsomorphicCallback(self): maps = [] def callback(g1, g2, map1, map2): maps.append(map1) return True # Test VF2 callback g = Graph(6, [(0,1), (2,3), (4,5), (0,2), (2,4), (1,3), (3,5)]) g.isomorphic_vf2(g, callback=callback) expected_maps = [[0,1,2,3,4,5], [1,0,3,2,5,4], [4,5,2,3,0,1], [5,4,3,2,1,0]] self.assertTrue(sorted(maps) == expected_maps) maps[:] = [] g3 = Graph.Full(4) g3.vs["color"] = [0,1,1,0] g3.isomorphic_vf2(callback=callback, color1="color", color2="color") expected_maps = [[0,1,2,3], [0,2,1,3], [3,1,2,0], [3,2,1,0]] self.assertTrue(sorted(maps) == expected_maps) def testCountIsomorphisms(self): g = Graph.Full(4) self.assertTrue(g.count_automorphisms_vf2() == 24) g = Graph(6, [(0,1), (2,3), (4,5), (0,2), (2,4), (1,3), (3,5)]) self.assertTrue(g.count_automorphisms_vf2() == 4) # Some more tests with colors g3 = Graph.Full(4) g3.vs["color"] = [0,1,1,0] self.assertTrue(g3.count_isomorphisms_vf2() == 24) self.assertTrue(g3.count_isomorphisms_vf2(color1="color", color2="color") == 4) self.assertTrue(g3.count_isomorphisms_vf2(color1=[0,1,2,0], color2=(0,1,2,0)) == 2) self.assertTrue(g3.count_isomorphisms_vf2(edge_color1=[0,1,0,0,0,1], edge_color2=[0,1,0,0,0,1]) == 2) # Test VF2 with node/edge compatibility function g3.vs["color"] = [0,1,1,0] self.assertTrue(g3.count_isomorphisms_vf2(node_compat_fn=node_compat) == 4) g3.vs["color"] = [0,1,2,0] self.assertTrue(g3.count_isomorphisms_vf2(node_compat_fn=node_compat) == 2) g3.es["color"] = [0,1,0,0,0,1] self.assertTrue(g3.count_isomorphisms_vf2(edge_compat_fn=edge_compat) == 2) def testGetIsomorphisms(self): g = Graph(6, [(0,1), (2,3), (4,5), (0,2), (2,4), (1,3), (3,5)]) maps = g.get_automorphisms_vf2() expected_maps = [[0,1,2,3,4,5], [1,0,3,2,5,4], [4,5,2,3,0,1], [5,4,3,2,1,0]] self.assertTrue(maps == expected_maps) g3 = Graph.Full(4) g3.vs["color"] = [0,1,1,0] expected_maps = [[0,1,2,3], [0,2,1,3], [3,1,2,0], [3,2,1,0]] self.assertTrue(sorted(g3.get_automorphisms_vf2(color="color")) == expected_maps) class SubisomorphismTests(unittest.TestCase): def testSubisomorphicLAD(self): g = Graph.Lattice([3,3], circular=False) g2 = Graph([(0,1), (1,2), (1,3)]) g3 = g + [(0,4), (2,4), (6,4), (8,4), (3,1), (1,5), (5,7), (7,3)] self.assertTrue(g.subisomorphic_lad(g2)) self.assertFalse(g2.subisomorphic_lad(g)) # Test 'induced' self.assertFalse(g3.subisomorphic_lad(g, induced=True)) self.assertTrue(g3.subisomorphic_lad(g, induced=False)) self.assertTrue(g3.subisomorphic_lad(g)) self.assertTrue(g3.subisomorphic_lad(g2, induced=True)) self.assertTrue(g3.subisomorphic_lad(g2, induced=False)) self.assertTrue(g3.subisomorphic_lad(g2)) # Test with limited vertex matching domains = [[4], [0,1,2,3,5,6,7,8], [0,1,2,3,5,6,7,8], [0,1,2,3,5,6,7,8]] self.assertTrue(g.subisomorphic_lad(g2, domains=domains)) domains = [[], [0,1,2,3,5,6,7,8], [0,1,2,3,5,6,7,8], [0,1,2,3,5,6,7,8]] self.assertTrue(not g.subisomorphic_lad(g2, domains=domains)) def testGetSubisomorphismsLAD(self): g = Graph.Lattice([3,3], circular=False) g2 = Graph([(0,1), (1,2), (2,3), (3,0)]) g3 = g + [(0,4), (2,4), (6,4), (8,4), (3,1), (1,5), (5,7), (7,3)] all_subiso = "0143 0341 1034 1254 1430 1452 2145 2541 3014 3410 3476 \ 3674 4103 4125 4301 4367 4521 4587 4763 4785 5214 5412 5478 5874 6347 \ 6743 7436 7458 7634 7854 8547 8745" all_subiso = sorted([int(x) for x in item] for item in all_subiso.split()) self.assertEqual(all_subiso, sorted(g.get_subisomorphisms_lad(g2))) self.assertEqual([], sorted(g2.get_subisomorphisms_lad(g))) # Test 'induced' induced_subiso = "1375 1573 3751 5731 7513 7315 5137 3157" induced_subiso = sorted([int(x) for x in item] for item in induced_subiso.split()) all_subiso_extra = sorted(all_subiso + induced_subiso) self.assertEqual(induced_subiso, sorted(g3.get_subisomorphisms_lad(g2, induced=True))) self.assertEqual([], g3.get_subisomorphisms_lad(g, induced=True)) # Test with limited vertex matching limited_subiso = [iso for iso in all_subiso if iso[0] == 4] domains = [[4], [0,1,2,3,5,6,7,8], [0,1,2,3,5,6,7,8], [0,1,2,3,5,6,7,8]] self.assertEqual(limited_subiso, sorted(g.get_subisomorphisms_lad(g2, domains=domains))) domains = [[], [0,1,2,3,5,6,7,8], [0,1,2,3,5,6,7,8], [0,1,2,3,5,6,7,8]] self.assertEqual([], sorted(g.get_subisomorphisms_lad(g2, domains=domains))) def testSubisomorphicVF2(self): g = Graph.Lattice([3,3], circular=False) g2 = Graph([(0,1), (1,2), (1,3)]) self.assertTrue(g.subisomorphic_vf2(g2)) self.assertTrue(not g2.subisomorphic_vf2(g)) # Test with vertex colors g.vs["color"] = [0,0,0,0,1,0,0,0,0] g2.vs["color"] = [1,0,0,0] self.assertTrue(g.subisomorphic_vf2(g2, node_compat_fn=node_compat)) g2.vs["color"] = [2,0,0,0] self.assertTrue(not g.subisomorphic_vf2(g2, node_compat_fn=node_compat)) # Test with edge colors g.es["color"] = [1] + [0]*(g.ecount()-1) g2.es["color"] = [1] + [0]*(g2.ecount()-1) self.assertTrue(g.subisomorphic_vf2(g2, edge_compat_fn=edge_compat)) g2.es[0]["color"] = [2] self.assertTrue(not g.subisomorphic_vf2(g2, node_compat_fn=node_compat)) def testCountSubisomorphisms(self): g = Graph.Lattice([3,3], circular=False) g2 = Graph.Lattice([2,2], circular=False) self.assertTrue(g.count_subisomorphisms_vf2(g2) == 4*4*2) self.assertTrue(g2.count_subisomorphisms_vf2(g) == 0) # Test with vertex colors g.vs["color"] = [0,0,0,0,1,0,0,0,0] g2.vs["color"] = [1,0,0,0] self.assertTrue(g.count_subisomorphisms_vf2(g2, "color", "color") == 4*2) self.assertTrue(g.count_subisomorphisms_vf2(g2, node_compat_fn=node_compat) == 4*2) # Test with edge colors g.es["color"] = [1] + [0]*(g.ecount()-1) g2.es["color"] = [1] + [0]*(g2.ecount()-1) self.assertTrue(g.count_subisomorphisms_vf2(g2, edge_color1="color", edge_color2="color") == 2) self.assertTrue(g.count_subisomorphisms_vf2(g2, edge_compat_fn=edge_compat) == 2) class PermutationTests(unittest.TestCase): def testCanonicalPermutation(self): # Simple case: two ring graphs g1 = Graph(4, [(0, 1), (1, 2), (2, 3), (3, 0)]) g2 = Graph(4, [(0, 1), (1, 3), (3, 2), (2, 0)]) cp = g1.canonical_permutation() g3 = g1.permute_vertices(cp) cp = g2.canonical_permutation() g4 = g2.permute_vertices(cp) self.assertTrue(g3.vcount() == g4.vcount()) self.assertTrue(sorted(g3.get_edgelist()) == sorted(g4.get_edgelist())) # More complicated one: small GRG, random permutation g = Graph.GRG(10, 0.5) perm = range(10) shuffle(perm) g2 = g.permute_vertices(perm) g3 = g.permute_vertices(g.canonical_permutation()) g4 = g2.permute_vertices(g2.canonical_permutation()) self.assertTrue(g3.vcount() == g4.vcount()) self.assertTrue(sorted(g3.get_edgelist()) == sorted(g4.get_edgelist())) def testPermuteVertices(self): g1 = Graph(8, [(0, 4), (0, 5), (0, 6), \ (1, 4), (1, 5), (1, 7), \ (2, 4), (2, 6), (2, 7), \ (3, 5), (3, 6), (3, 7)]) g2 = Graph(8, [(0, 1), (0, 3), (0, 4), \ (2, 3), (2, 1), (2, 6), \ (5, 1), (5, 4), (5, 6), \ (7, 3), (7, 6), (7, 4)]) _, _, mapping = g1.isomorphic_vf2(g2, return_mapping_21=True) g3 = g2.permute_vertices(mapping) self.assertTrue(g3.vcount() == g2.vcount() and g3.ecount() == g2.ecount()) self.assertTrue(set(g3.get_edgelist()) == set(g1.get_edgelist())) def suite(): isomorphism_suite = unittest.makeSuite(IsomorphismTests) subisomorphism_suite = unittest.makeSuite(SubisomorphismTests) permutation_suite = unittest.makeSuite(PermutationTests) return unittest.TestSuite([isomorphism_suite, subisomorphism_suite, \ permutation_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/iterators.py0000644000076500000240000000133712453614202022612 0ustar ntamasstaff00000000000000import unittest from igraph import * class IteratorTests(unittest.TestCase): def testBFS(self): g=Graph.Tree(10, 2) vs=[v.index for v in g.bfsiter(0)] self.assertEqual(vs, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) vs=[(v.index,dist,parent) for v,dist,parent in g.bfsiter(0, advanced=True)] vs=[(v,d,p.index) for v,d,p in vs if p != None] self.assertEqual(vs, [(1,1,0), (2,1,0), (3,2,1), (4,2,1), \ (5,2,2), (6,2,2), (7,3,3), (8,3,3), (9,3,4)]) def suite(): iterator_suite = unittest.makeSuite(IteratorTests) return unittest.TestSuite([iterator_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/layouts.py0000644000076500000240000002346512534342712022310 0ustar ntamasstaff00000000000000import unittest from igraph import Graph, Layout, BoundingBox class LayoutTests(unittest.TestCase): def testConstructor(self): layout = Layout([(0,0,1), (0,1,0), (1,0,0)]) self.assertEqual(layout.dim, 3) layout = Layout([(0,0,1), (0,1,0), (1,0,0)], 3) self.assertEqual(layout.dim, 3) self.assertRaises(ValueError, Layout, [(0,1), (1,0)], 3) def testIndexing(self): layout = Layout([(0,0,1), (0,1,0), (1,0,0), (2,1,3)]) self.assertEqual(len(layout), 4) self.assertEqual(layout[1], [0, 1, 0]) self.assertEqual(layout[3], [2, 1, 3]) row = layout[2] row[2] = 1 self.assertEqual(layout[2], [1, 0, 1]) del layout[1] self.assertEqual(len(layout), 3) def testScaling(self): layout = Layout([(0,0,1), (0,1,0), (1,0,0), (2,1,3)]) layout.scale(1.5) self.assertEqual(layout.coords, [[0., 0., 1.5], \ [0., 1.5, 0.], \ [1.5, 0., 0.], \ [3., 1.5, 4.5]]) layout = Layout([(0,0,1), (0,1,0), (1,0,0), (2,1,3)]) layout.scale(1, 1, 3) self.assertEqual(layout.coords, [[0, 0, 3], \ [0, 1, 0], \ [1, 0, 0], \ [2, 1, 9]]) layout = Layout([(0,0,1), (0,1,0), (1,0,0), (2,1,3)]) layout.scale((2, 2, 1)) self.assertEqual(layout.coords, [[0, 0, 1], \ [0, 2, 0], \ [2, 0, 0], \ [4, 2, 3]]) self.assertRaises(ValueError, layout.scale, 2, 3) def testTranslation(self): layout = Layout([(0,0,1), (0,1,0), (1,0,0), (2,1,3)]) layout2 = layout.copy() layout.translate(1,3,2) self.assertEqual(layout.coords, [[1, 3, 3], \ [1, 4, 2], \ [2, 3, 2], \ [3, 4, 5]]) layout.translate((-1,-3,-2)) self.assertEqual(layout.coords, layout2.coords) self.assertRaises(ValueError, layout.translate, v=[3]) def testCentroid(self): layout = Layout([(0,0,1), (0,1,0), (1,0,0), (2,1,3)]) centroid = layout.centroid() self.assertEqual(len(centroid), 3) self.assertAlmostEqual(centroid[0], 0.75) self.assertAlmostEqual(centroid[1], 0.5) self.assertAlmostEqual(centroid[2], 1.) def testBoundaries(self): layout = Layout([(0,0,1), (0,1,0), (1,0,0), (2,1,3)]) self.assertEqual(layout.boundaries(), ([0,0,0],[2,1,3])) self.assertEqual(layout.boundaries(1), ([-1,-1,-1],[3,2,4])) layout = Layout([]) self.assertRaises(ValueError, layout.boundaries) layout = Layout([], dim=3) self.assertRaises(ValueError, layout.boundaries) def testBoundingBox(self): layout = Layout([(0,1), (2,7)]) self.assertEqual(layout.bounding_box(), BoundingBox(0,1,2,7)) self.assertEqual(layout.bounding_box(1), BoundingBox(-1,0,3,8)) layout = Layout([]) self.assertEqual(layout.bounding_box(), BoundingBox(0,0,0,0)) def testCenter(self): layout = Layout([(-2,0), (-2,-2), (0,-2), (0,0)]) layout.center() self.assertEqual(layout.coords, [[-1,1], [-1,-1], [1,-1], [1,1]]) layout.center(5,5) self.assertEqual(layout.coords, [[4,6], [4,4], [6,4], [6,6]]) self.assertRaises(ValueError, layout.center, 3) self.assertRaises(TypeError, layout.center, p=6) def testFitInto(self): layout = Layout([(-2,0), (-2,-2), (0,-2), (0,0)]) layout.fit_into(BoundingBox(5,5,8,10), keep_aspect_ratio=False) self.assertEqual(layout.coords, [[5, 10], [5, 5], [8, 5], [8, 10]]) layout = Layout([(-2,0), (-2,-2), (0,-2), (0,0)]) layout.fit_into(BoundingBox(5,5,8,10)) self.assertEqual(layout.coords, [[5, 9], [5, 6], [8, 6], [8, 9]]) layout = Layout([(-1,-1,-1), (0,0,0), (1,1,1), (2,2,0), (3,3,-1)]) layout.fit_into((0,0,0,8,8,4)) self.assertEqual(layout.coords, \ [[0, 0, 0], [2, 2, 2], [4, 4, 4], [6, 6, 2], [8, 8, 0]] ) layout = Layout([]) layout.fit_into((6,7,8,11)) self.assertEqual(layout.coords, []) def testToPolar(self): layout = Layout([(0, 0), (-1, 1), (0, 1), (1, 1)]) layout.to_radial(min_angle=180, max_angle=0, max_radius=2) exp = [[0., 0.], [-2., 0.], [0., 2.], [2, 0.]] for idx in xrange(4): self.assertAlmostEqual(layout.coords[idx][0], exp[idx][0], places=3) self.assertAlmostEqual(layout.coords[idx][1], exp[idx][1], places=3) def testTransform(self): def tr(coord, dx, dy): return coord[0]+dx, coord[1]+dy layout = Layout([(1, 2), (3, 4)]) layout.transform(tr, 2, -1) self.assertEqual(layout.coords, [[3, 1], [5, 3]]) class LayoutAlgorithmTests(unittest.TestCase): def testAuto(self): def layout_test(graph, test_with_dims=(2, 3)): lo = graph.layout("auto") self.assertTrue(isinstance(lo, Layout)) self.assertEqual(len(lo[0]), 2) for dim in test_with_dims: lo = graph.layout("auto", dim=dim) self.assertTrue(isinstance(lo, Layout)) self.assertEqual(len(lo[0]), dim) return lo g = Graph.Barabasi(10) layout_test(g) g = Graph.GRG(101, 0.2) del g.vs["x"] del g.vs["y"] layout_test(g) g = Graph.Full(10) * 2 layout_test(g) g["layout"] = "graphopt" layout_test(g, test_with_dims=()) g.vs["x"] = range(20) g.vs["y"] = range(20, 40) layout_test(g, test_with_dims=()) del g["layout"] lo = layout_test(g, test_with_dims=(2,)) self.assertEqual([tuple(item) for item in lo], zip(range(20), range(20, 40))) g.vs["z"] = range(40, 60) lo = layout_test(g) self.assertEqual([tuple(item) for item in lo], zip(range(20), range(20, 40), range(40, 60))) def testCircle(self): def test_is_proper_circular_layout(graph, layout): xs, ys = zip(*layout) n = graph.vcount() self.assertEquals(n, len(xs)) self.assertEquals(n, len(ys)) self.assertAlmostEquals(0, sum(xs)) self.assertAlmostEquals(0, sum(ys)) for x, y in zip(xs, ys): self.assertAlmostEquals(1, x**2+y**2) g = Graph.Ring(8) layout = g.layout("circle") test_is_proper_circular_layout(g, g.layout("circle")) def testFruchtermanReingold(self): g = Graph.Barabasi(100) lo = g.layout("fr") self.assertTrue(isinstance(lo, Layout)) lo = g.layout("fr", miny=range(100)) self.assertTrue(isinstance(lo, Layout)) self.assertTrue(all(lo[i][1] >= i for i in xrange(100))) lo = g.layout("fr", miny=range(100), maxy=range(100)) self.assertTrue(isinstance(lo, Layout)) self.assertTrue(all(lo[i][1] == i for i in xrange(100))) lo = g.layout("fr", miny=[2]*100, maxy=[3]*100, minx=[4]*100, maxx=[6]*100) self.assertTrue(isinstance(lo, Layout)) bbox = lo.bounding_box() self.assertTrue(bbox.top >= 2) self.assertTrue(bbox.bottom <= 3) self.assertTrue(bbox.left >= 4) self.assertTrue(bbox.right <= 6) def testKamadaKawai(self): g = Graph.Barabasi(100) lo = g.layout("kk", miny=[2]*100, maxy=[3]*100, minx=[4]*100, maxx=[6]*100) self.assertTrue(isinstance(lo, Layout)) bbox = lo.bounding_box() self.assertTrue(bbox.top >= 2) self.assertTrue(bbox.bottom <= 3) self.assertTrue(bbox.left >= 4) self.assertTrue(bbox.right <= 6) def testMDS(self): g = Graph.Tree(10, 2) lo = g.layout("mds") self.assertTrue(isinstance(lo, Layout)) dists = g.shortest_paths() lo = g.layout("mds", dists) self.assertTrue(isinstance(lo, Layout)) g += Graph.Tree(10, 2) lo = g.layout("mds") self.assertTrue(isinstance(lo, Layout)) def testReingoldTilford(self): g = Graph.Barabasi(100) lo = g.layout("rt") ys = [coord[1] for coord in lo] root = ys.index(0.0) self.assertEqual(ys, g.shortest_paths(root)[0]) g = Graph.Barabasi(100) + Graph.Barabasi(50) lo = g.layout("rt", root=[0, 100]) self.assertEqual(lo[100][1]-lo[0][1], 0) lo = g.layout("rt", root=[0, 100], rootlevel=[2, 10]) self.assertEqual(lo[100][1]-lo[0][1], 8) def testBipartite(self): g = Graph.Full_Bipartite(3, 2) lo = g.layout("bipartite") ys = [coord[1] for coord in lo] self.assertEqual([1, 1, 1, 0, 0], ys) lo = g.layout("bipartite", vgap=3) ys = [coord[1] for coord in lo] self.assertEqual([3, 3, 3, 0, 0], ys) lo = g.layout("bipartite", hgap=5) self.assertEqual(set([0, 5, 10]), set(coord[0] for coord in lo if coord[1] == 1)) self.assertEqual(set([2.5, 7.5]), set(coord[0] for coord in lo if coord[1] == 0)) def testDRL(self): # Regression test for bug #1091891 g = Graph.Ring(10, circular=False) + 1 lo = g.layout("drl") self.assertTrue(isinstance(lo, Layout)) def suite(): layout_suite = unittest.makeSuite(LayoutTests) layout_algorithm_suite = unittest.makeSuite(LayoutAlgorithmTests) return unittest.TestSuite([layout_suite, layout_algorithm_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/matching.py0000644000076500000240000000522512453614202022370 0ustar ntamasstaff00000000000000import unittest from igraph import * def powerset(iterable): items_powers = [(item, 1 << i) for i, item in enumerate(iterable)] for i in range(1 << len(items_powers)): for item, power in items_powers: if i & power: yield item leda_graph = Graph([ (0,8),(0,12),(0,14),(1,9),(1,10),(1,13), (2,8),(2,9),(3,10),(3,11),(3,13),(4,9),(4,14), (5,14),(6,9),(6,14),(7,8),(7,12),(7,14)]) leda_graph.vs["type"] = [0]*8+[1]*7 class MatchingTests(unittest.TestCase): def setUp(self): self.matching = Matching(leda_graph, [12, 10, 8, 13, -1, 14, 9, -1, 2, 6, 1, -1, 0, 3, 5], "type") def testIsMaximal(self): self.assertTrue(self.matching.is_maximal()) self.matching.matching[0] = -1 self.matching.matching[12] = -1 self.assertFalse(self.matching.is_maximal()) def testMatchingRetrieval(self): m = [12, 10, 8, 13, -1, 14, 9, -1, 2, 6, 1, -1, 0, 3, 5] self.assertEqual(self.matching.matching, m) for i, mate in enumerate(m): if mate == -1: self.assertFalse(self.matching.is_matched(i)) self.assertEqual(self.matching.match_of(i), None) else: self.assertTrue(self.matching.is_matched(i)) self.assertEqual(self.matching.match_of(i), mate) self.assertEqual(self.matching.match_of( leda_graph.vs[i]).index, leda_graph.vs[mate].index) class MaximumBipartiteMatchingTests(unittest.TestCase): def testBipartiteMatchingSimple(self): # Specifying the "type" attribute explicitly matching = leda_graph.maximum_bipartite_matching("type") self.assertEqual(len(matching), 6) self.assertTrue(matching.is_maximal()) # Using the default attribute matching = leda_graph.maximum_bipartite_matching() self.assertEqual(len(matching), 6) self.assertTrue(matching.is_maximal()) def testBipartiteMatchingErrors(self): # Type vector too short g = Graph([(0, 1), (1, 2), (2, 3)]) self.assertRaises(InternalError, g.maximum_bipartite_matching, types=[0,1,0]) # Graph not bipartite self.assertRaises(ValueError, g.maximum_bipartite_matching, types=[0,1,1,1]) def suite(): matching_suite = unittest.makeSuite(MatchingTests) bipartite_unweighted_suite = unittest.makeSuite(MaximumBipartiteMatchingTests) return unittest.TestSuite([matching_suite, bipartite_unweighted_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/operators.py0000644000076500000240000001621712460535443022626 0ustar ntamasstaff00000000000000import unittest from igraph import * from igraph.test.utils import skipIf try: import numpy as np except ImportError: np = None class OperatorTests(unittest.TestCase): def testMultiplication(self): g = Graph.Full(3)*3 self.assertTrue(g.vcount() == 9 and g.ecount() == 9 and g.clusters().membership == [0,0,0,1,1,1,2,2,2]) def testIntersection(self): g = Graph.Tree(7, 2) & Graph.Lattice([7]) self.assertTrue(g.get_edgelist() == [(0, 1)]) def testUnion(self): g = Graph.Tree(7, 2) | Graph.Lattice([7]) self.assertTrue(g.vcount() == 7 and g.ecount() == 12) def testInPlaceAddition(self): g = Graph.Full(3) orig = g # Adding vertices g += 2 self.assertTrue(g.vcount() == 5 and g.ecount() == 3 and g.clusters().membership == [0,0,0,1,2]) # Adding a vertex by name g += "spam" self.assertTrue(g.vcount() == 6 and g.ecount() == 3 and g.clusters().membership == [0,0,0,1,2,3]) # Adding a single edge g += (2, 3) self.assertTrue(g.vcount() == 6 and g.ecount() == 4 and g.clusters().membership == [0,0,0,0,1,2]) # Adding two edges g += [(3, 4), (2, 4), (4, 5)] self.assertTrue(g.vcount() == 6 and g.ecount() == 7 and g.clusters().membership == [0]*6) # Adding two more vertices g += ["eggs", "bacon"] self.assertEqual(g.vs["name"], [None, None, None, None, None, "spam", "eggs", "bacon"]) # Did we really use the original graph so far? # TODO: disjoint union should be modified so that this assertion # could be moved to the end self.assertTrue(id(g) == id(orig)) # Adding another graph g += Graph.Full(3) self.assertTrue(g.vcount() == 11 and g.ecount() == 10 and g.clusters().membership == [0,0,0,0,0,0,1,2,3,3,3]) # Adding two graphs g += [Graph.Full(3), Graph.Full(2)] self.assertTrue(g.vcount() == 16 and g.ecount() == 14 and g.clusters().membership == [0,0,0,0,0,0,1,2,3,3,3,4,4,4,5,5]) def testAddition(self): g0 = Graph.Full(3) # Adding vertices g = g0+2 self.assertTrue(g.vcount() == 5 and g.ecount() == 3 and g.clusters().membership == [0,0,0,1,2]) g0 = g # Adding vertices by name g = g0+"spam" self.assertTrue(g.vcount() == 6 and g.ecount() == 3 and g.clusters().membership == [0,0,0,1,2,3]) g0 = g # Adding a single edge g = g0+(2,3) self.assertTrue(g.vcount() == 6 and g.ecount() == 4 and g.clusters().membership == [0,0,0,0,1,2]) g0 = g # Adding two edges g = g0+[(3, 4), (2, 4), (4, 5)] self.assertTrue(g.vcount() == 6 and g.ecount() == 7 and g.clusters().membership == [0]*6) g0 = g # Adding another graph g = g0+Graph.Full(3) self.assertTrue(g.vcount() == 9 and g.ecount() == 10 and g.clusters().membership == [0,0,0,0,0,0,1,1,1]) def testInPlaceSubtraction(self): g = Graph.Full(8) orig = g # Deleting a vertex by vertex selector g -= 7 self.assertTrue(g.vcount() == 7 and g.ecount() == 21 and g.clusters().membership == [0,0,0,0,0,0,0]) # Deleting a vertex g -= g.vs[6] self.assertTrue(g.vcount() == 6 and g.ecount() == 15 and g.clusters().membership == [0,0,0,0,0,0]) # Deleting two vertices g -= [4, 5] self.assertTrue(g.vcount() == 4 and g.ecount() == 6 and g.clusters().membership == [0,0,0,0]) # Deleting an edge g -= (1, 2) self.assertTrue(g.vcount() == 4 and g.ecount() == 5 and g.clusters().membership == [0,0,0,0]) # Deleting three more edges g -= [(1, 3), (0, 2), (0, 3)] self.assertTrue(g.vcount() == 4 and g.ecount() == 2 and g.clusters().membership == [0,0,1,1]) # Did we really use the original graph so far? self.assertTrue(id(g) == id(orig)) # Subtracting a graph g2 = Graph.Tree(3, 2) g -= g2 self.assertTrue(g.vcount() == 4 and g.ecount() == 1 and g.clusters().membership == [0,1,2,2]) def testNonzero(self): self.assertTrue(Graph(1)) self.assertFalse(Graph(0)) def testLength(self): self.assertRaises(TypeError, len, Graph(15)) self.assertTrue(len(Graph(15).vs) == 15) self.assertTrue(len(Graph.Full(5).es) == 10) def testSimplify(self): el = [(0,1), (1,0), (1,2), (2,3), (2,3), (2,3), (3,3)] g = Graph(el) g.es["weight"] = [1, 2, 3, 4, 5, 6, 7] g2 = g.copy() g2.simplify() self.assertTrue(g2.vcount() == g.vcount()) self.assertTrue(g2.ecount() == 3) g2 = g.copy() g2.simplify(loops=False) self.assertTrue(g2.vcount() == g.vcount()) self.assertTrue(g2.ecount() == 4) g2 = g.copy() g2.simplify(multiple=False) self.assertTrue(g2.vcount() == g.vcount()) self.assertTrue(g2.ecount() == g.ecount() - 1) def testContractVertices(self): g = Graph.Full(4) + Graph.Full(4) + [(0, 5), (1, 4)] g2 = g.copy() g2.contract_vertices([0, 1, 2, 3, 1, 0, 4, 5]) self.assertEqual(g2.vcount(), 6) self.assertEqual(g2.ecount(), g.ecount()) self.assertEqual(sorted(g2.get_edgelist()), [(0, 0), (0, 1), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (4, 5)]) g2 = g.copy() g2.contract_vertices([0, 1, 2, 3, 1, 0, 6, 7]) self.assertEqual(g2.vcount(), 8) self.assertEqual(g2.ecount(), g.ecount()) self.assertEqual(sorted(g2.get_edgelist()), [(0, 0), (0, 1), (0, 1), (0, 2), (0, 3), (0, 6), (0, 7), (1, 1), (1, 2), (1, 3), (1, 6), (1, 7), (2, 3), (6, 7)]) g2 = Graph(10) g2.contract_vertices([0, 0, 1, 1, 2, 2, 3, 3, 4, 4]) self.assertEqual(g2.vcount(), 5) self.assertEqual(g2.ecount(), 0) @skipIf(np is None, "test case depends on NumPy") def testContractVerticesWithNumPyIntegers(self): g = Graph.Full(4) + Graph.Full(4) + [(0, 5), (1, 4)] g2 = g.copy() g2.contract_vertices([np.int32(x) for x in [0, 1, 2, 3, 1, 0, 6, 7]]) self.assertEqual(g2.vcount(), 8) self.assertEqual(g2.ecount(), g.ecount()) self.assertEqual(sorted(g2.get_edgelist()), [(0, 0), (0, 1), (0, 1), (0, 2), (0, 3), (0, 6), (0, 7), (1, 1), (1, 2), (1, 3), (1, 6), (1, 7), (2, 3), (6, 7)]) def suite(): operator_suite = unittest.makeSuite(OperatorTests) return unittest.TestSuite([operator_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/rng.py0000644000076500000240000000233012453614202021356 0ustar ntamasstaff00000000000000import random import unittest from igraph import * class FakeRNG(object): @staticmethod def random(): return 0.1 @staticmethod def randint(a, b): return a @staticmethod def gauss(mu, sigma): return 0.3 class InvalidRNG(object): pass class RandomNumberGeneratorTests(unittest.TestCase): def tearDown(self): set_random_number_generator(random) def testSetRandomNumberGenerator(self): set_random_number_generator(FakeRNG) graph = Graph.GRG(10, 0.2) self.assertEqual(graph.vs["x"], [0.1] * 10) self.assertEqual(graph.vs["y"], [0.1] * 10) self.assertRaises(AttributeError, set_random_number_generator, InvalidRNG) def testSeeding(self): state = random.getstate() g1 = Graph.Erdos_Renyi(n=1000, m=5000) random.setstate(state) g2 = Graph.Erdos_Renyi(n=1000, m=5000) self.assertTrue(g1.get_edgelist() == g2.get_edgelist()) def suite(): random_suite = unittest.makeSuite(RandomNumberGeneratorTests) return unittest.TestSuite([random_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/separators.py0000644000076500000240000000466512453614202022770 0ustar ntamasstaff00000000000000import unittest from igraph import * def powerset(iterable): items_powers = [(item, 1 << i) for i, item in enumerate(iterable)] for i in range(1 << len(items_powers)): for item, power in items_powers: if i & power: yield item class IsSeparatorTests(unittest.TestCase): def testIsSeparator(self): g = Graph.Lattice([8, 4], circular=False) self.assertTrue(g.is_separator([3, 11, 19, 27])) self.assertFalse(g.is_separator([10, 11, 18, 19])) self.assertTrue(g.is_separator([29, 20, 11, 2])) self.assertTrue(g.is_separator([16, 25, 17])) g = Graph.Lattice([8, 4], circular=True) self.assertFalse(g.is_separator([3, 11, 19, 27])) self.assertFalse(g.is_separator([29, 20, 11, 2])) self.assertRaises(InternalError, g.is_separator, range(32)) def testIsMinimalSeparator(self): g = Graph.Lattice([8, 4], circular=False) self.assertTrue(g.is_minimal_separator([3, 11, 19, 27])) self.assertFalse(g.is_minimal_separator([3, 11, 19, 27, 28])) self.assertFalse(g.is_minimal_separator([16, 25, 17])) self.assertTrue(g.is_minimal_separator([16, 25])) self.assertRaises(InternalError, g.is_minimal_separator, range(32)) def testAllMinimalSTSeparators(self): g = Graph.Famous("petersen") min_st_seps = set(tuple(x) for x in g.all_minimal_st_separators()) for vs in powerset(range(g.vcount())): if vs in min_st_seps: self.assertTrue(g.is_minimal_separator(vs)) else: self.assertFalse(g.is_minimal_separator(vs)) def testMinimumSizeSeparators(self): g = Graph.Famous("zachary") min_st_seps = set(tuple(x) for x in g.all_minimal_st_separators()) min_size_seps = [tuple(x) for x in g.minimum_size_separators()] self.assertTrue(set(min_size_seps).issubset(min_st_seps)) self.assertTrue(len(set(min_size_seps)) == len(min_size_seps)) size = len(min_size_seps[0]) self.assertTrue(len(s) != size for s in min_size_seps) self.assertTrue(sum(1 for s in min_st_seps if len(s) == size) == len(min_size_seps)) def suite(): is_separator_suite = unittest.makeSuite(IsSeparatorTests) return unittest.TestSuite([is_separator_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/spectral.py0000644000076500000240000000313012453614202022404 0ustar ntamasstaff00000000000000# vim:set ts=4 sw=4 sts=4 et: import unittest from igraph import * class SpectralTests(unittest.TestCase): def assertAlmostEqualMatrix(self, mat1, mat2, eps = 1e-7): self.assertTrue(all( abs(obs-exp) < eps for obs, exp in zip(sum(mat1, []), sum(mat2, [])) )) def testLaplacian(self): g=Graph.Full(3) g.es["weight"] = [1, 2, 3] self.assertTrue(g.laplacian() == [[ 2, -1, -1],\ [-1, 2, -1],\ [-1, -1, 2]]) self.assertAlmostEqualMatrix(g.laplacian(normalized=True), [[1, -0.5, -0.5], [-0.5, 1, -0.5], [-0.5, -0.5, 1]]) mx0 = [[1., -1/(12**0.5), -2/(15**0.5)], [-1/(12**0.5), 1., -3/(20**0.5)], [-2/(15**0.5), -3/(20**0.5), 1.]] self.assertAlmostEqualMatrix(g.laplacian("weight", True), mx0) g=Graph.Tree(5, 2) g.add_vertices(1) self.assertTrue(g.laplacian() == [[ 2, -1, -1, 0, 0, 0],\ [-1, 3, 0, -1, -1, 0],\ [-1, 0, 1, 0, 0, 0],\ [ 0, -1, 0, 1, 0, 0],\ [ 0, -1, 0, 0, 1, 0],\ [ 0, 0, 0, 0, 0, 0]]) def suite(): spectral_suite = unittest.makeSuite(SpectralTests) return unittest.TestSuite([spectral_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/structural.py0000644000076500000240000005170312534342712023014 0ustar ntamasstaff00000000000000from __future__ import division import unittest from igraph import * from igraph.compat import isnan class SimplePropertiesTests(unittest.TestCase): gfull = Graph.Full(10) gempty = Graph(10) g = Graph(4, [(0, 1), (0, 2), (1, 2), (0, 3), (1, 3)]) gdir = Graph(4, [(0, 1), (0, 2), (1, 2), (2, 1), (0, 3), (1, 3), (3, 0)], directed=True) tree = Graph.Tree(14, 3) def testDensity(self): self.assertAlmostEqual(1.0, self.gfull.density(), places=5) self.assertAlmostEqual(0.0, self.gempty.density(), places=5) self.assertAlmostEqual(5/6, self.g.density(), places=5) self.assertAlmostEqual(1/2, self.g.density(True), places=5) self.assertAlmostEqual(7/12, self.gdir.density(), places=5) self.assertAlmostEqual(7/16, self.gdir.density(True), places=5) self.assertAlmostEqual(1/7, self.tree.density(), places=5) def testDiameter(self): self.assertTrue(self.gfull.diameter() == 1) self.assertTrue(self.gempty.diameter(unconn=False) == 10) self.assertTrue(self.gempty.diameter(unconn=False, weights=[]) \ == float('inf')) self.assertTrue(self.g.diameter() == 2) self.assertTrue(self.gdir.diameter(False) == 2) self.assertTrue(self.gdir.diameter() == 3) self.assertTrue(self.tree.diameter() == 5) s, t, d = self.tree.farthest_points() self.assertTrue((s == 13 or t == 13) and d == 5) self.assertTrue(self.gempty.farthest_points(unconn=False) == (None, None, 10)) d = self.tree.get_diameter() self.assertTrue(d[0] == 13 or d[-1] == 13) weights = [1, 1, 1, 5, 1, 5, 1, 1, 1, 1, 1, 1, 5] self.assertTrue(self.tree.diameter(weights=weights) == 15) d = self.tree.farthest_points(weights=weights) self.assertTrue(d == (13, 6, 15) or d == (6, 13, 15)) def testEccentricity(self): self.assertEqual(self.gfull.eccentricity(), [1] * self.gfull.vcount()) self.assertEqual(self.gempty.eccentricity(), [0] * self.gempty.vcount()) self.assertEqual(self.g.eccentricity(), [1, 1, 2, 2]) self.assertEqual(self.gdir.eccentricity(), [1, 2, 3, 2]) self.assertEqual(self.tree.eccentricity(), [3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5]) self.assertEqual(Graph().eccentricity(), []) def testRadius(self): self.assertEqual(self.gfull.radius(), 1) self.assertEqual(self.gempty.radius(), 0) self.assertEqual(self.g.radius(), 1) self.assertEqual(self.gdir.radius(), 1) self.assertEqual(self.tree.radius(), 3) self.assertTrue(isnan(Graph().radius())) def testTransitivity(self): self.assertTrue(self.gfull.transitivity_undirected() == 1.0) self.assertTrue(self.tree.transitivity_undirected() == 0.0) self.assertTrue(self.g.transitivity_undirected() == 0.75) def testLocalTransitivity(self): self.assertTrue(self.gfull.transitivity_local_undirected() == [1.0] * self.gfull.vcount()) self.assertTrue(self.tree.transitivity_local_undirected(mode="zero") == [0.0] * self.tree.vcount()) l = self.g.transitivity_local_undirected(mode="zero") self.assertAlmostEqual(2/3, l[0], places=4) self.assertAlmostEqual(2/3, l[1], places=4) self.assertEqual(1, l[2]) self.assertEqual(1, l[3]) g = Graph.Full(4) + 1 + [(0, 4)] g.es["weight"] = [1, 1, 1, 1, 1, 1, 5] self.assertAlmostEqual( g.transitivity_local_undirected(0, weights="weight"), 0.25, places=4) def testAvgLocalTransitivity(self): self.assertTrue(self.gfull.transitivity_avglocal_undirected() == 1.0) self.assertTrue(self.tree.transitivity_avglocal_undirected() == 0.0) self.assertAlmostEqual(self.g.transitivity_avglocal_undirected(), 5/6., places=4) def testModularity(self): g = Graph.Full(5)+Graph.Full(5) g.add_edges([(0,5)]) cl = [0]*5+[1]*5 self.assertAlmostEqual(g.modularity(cl), 0.4523, places=3) ws = [1]*21 self.assertAlmostEqual(g.modularity(cl, ws), 0.4523, places=3) ws = [2]*21 self.assertAlmostEqual(g.modularity(cl, ws), 0.4523, places=3) ws = [2]*10+[1]*11 self.assertAlmostEqual(g.modularity(cl, ws), 0.4157, places=3) self.assertRaises(InternalError, g.modularity, cl, ws[0:20]) class DegreeTests(unittest.TestCase): gfull = Graph.Full(10) gempty = Graph(10) g = Graph(4, [(0, 1), (0, 2), (1, 2), (0, 3), (1, 3), (0, 0)]) gdir = Graph(4, [(0, 1), (0, 2), (1, 2), (2, 1), (0, 3), (1, 3), (3, 0)], directed=True) tree = Graph.Tree(10, 3) def testKnn(self): knn, knnk = self.gfull.knn() self.assertTrue(knn == [9.] * 10) self.assertAlmostEqual(knnk[8], 9.0, places=6) # knn works for simple graphs only -- self.g is not simple self.assertRaises(InternalError, self.g.knn) # Okay, simplify it and then go on g = self.g.copy() g.simplify() knn, knnk = g.knn() diff = max(abs(a-b) for a, b in zip(knn, [7/3., 7/3., 3, 3])) self.assertAlmostEqual(diff, 0., places=6) self.assertEqual(len(knnk), 3) self.assertAlmostEqual(knnk[1], 3, places=6) self.assertAlmostEqual(knnk[2], 7/3., places=6) def testDegree(self): self.assertTrue(self.gfull.degree() == [9] * 10) self.assertTrue(self.gempty.degree() == [0] * 10) self.assertTrue(self.g.degree(loops=False) == [3, 3, 2, 2]) self.assertTrue(self.g.degree() == [5, 3, 2, 2]) self.assertTrue(self.gdir.degree(mode=IN) == [1, 2, 2, 2]) self.assertTrue(self.gdir.degree(mode=OUT) == [3, 2, 1, 1]) self.assertTrue(self.gdir.degree(mode=ALL) == [4, 4, 3, 3]) vs = self.gdir.vs.select(0, 2) self.assertTrue(self.gdir.degree(vs, mode=ALL) == [4, 3]) self.assertTrue(self.gdir.degree(self.gdir.vs[1], mode=ALL) == 4) def testMaxDegree(self): self.assertTrue(self.gfull.maxdegree() == 9) self.assertTrue(self.gempty.maxdegree() == 0) self.assertTrue(self.g.maxdegree() == 3) self.assertTrue(self.g.maxdegree(loops=True) == 5) self.assertTrue(self.g.maxdegree([1, 2], loops=True) == 3) self.assertTrue(self.gdir.maxdegree(mode=IN) == 2) self.assertTrue(self.gdir.maxdegree(mode=OUT) == 3) self.assertTrue(self.gdir.maxdegree(mode=ALL) == 4) def testStrength(self): # Turn off warnings about calling strength without weights import warnings warnings.filterwarnings("ignore", "No edge weights for strength calculation", \ RuntimeWarning) # No weights self.assertTrue(self.gfull.strength() == [9] * 10) self.assertTrue(self.gempty.strength() == [0] * 10) self.assertTrue(self.g.degree(loops=False) == [3, 3, 2, 2]) self.assertTrue(self.g.degree() == [5, 3, 2, 2]) # With weights ws = [1, 2, 3, 4, 5, 6] self.assertTrue(self.g.strength(weights=ws, loops=False) == \ [7, 9, 5, 9]) self.assertTrue(self.g.strength(weights=ws) == [19, 9, 5, 9]) ws = [1, 2, 3, 4, 5, 6, 7] self.assertTrue(self.gdir.strength(mode=IN, weights=ws) == \ [7, 5, 5, 11]) self.assertTrue(self.gdir.strength(mode=OUT, weights=ws) == \ [8, 9, 4, 7]) self.assertTrue(self.gdir.strength(mode=ALL, weights=ws) == \ [15, 14, 9, 18]) vs = self.gdir.vs.select(0, 2) self.assertTrue(self.gdir.strength(vs, mode=ALL, weights=ws) == \ [15, 9]) self.assertTrue(self.gdir.strength(self.gdir.vs[1], \ mode=ALL, weights=ws) == 14) class LocalTransitivityTests(unittest.TestCase): def testLocalTransitivityFull(self): trans = Graph.Full(10).transitivity_local_undirected() self.assertTrue(trans == [1.0]*10) def testLocalTransitivityTree(self): trans = Graph.Tree(10, 3).transitivity_local_undirected() self.assertTrue(trans[0:3] == [0.0, 0.0, 0.0]) def testLocalTransitivityHalf(self): g = Graph(4, [(0, 1), (0, 2), (1, 2), (0, 3), (1, 3)]) trans = g.transitivity_local_undirected() trans = [round(x, 3) for x in trans] self.assertTrue(trans == [0.667, 0.667, 1.0, 1.0]) def testLocalTransitivityPartial(self): g = Graph(4, [(0, 1), (0, 2), (1, 2), (0, 3), (1, 3)]) trans = g.transitivity_local_undirected([1,2]) trans = [round(x, 3) for x in trans] self.assertTrue(trans == [0.667, 1.0]) class BiconnectedComponentTests(unittest.TestCase): g1 = Graph.Full(10) g2 = Graph(5, [(0,1),(1,2),(2,3),(3,4)]) g3 = Graph(6, [(0,1),(1,2),(2,3),(3,0),(2,4),(2,5),(4,5)]) def testBiconnectedComponents(self): s = self.g1.biconnected_components() self.assertTrue(len(s) == 1 and s[0]==range(10)) s, ap = self.g1.biconnected_components(True) self.assertTrue(len(s) == 1 and s[0]==range(10)) s = self.g3.biconnected_components() self.assertTrue(len(s) == 2 and s[0]==[2,4,5] and s[1]==[0,1,2,3]) s, ap = self.g3.biconnected_components(True) self.assertTrue(len(s) == 2 and s[0]==[2,4,5] and \ s[1]==[0,1,2,3] and ap == [2]) def testArticulationPoints(self): self.assertTrue(self.g1.articulation_points() == []) self.assertTrue(self.g2.cut_vertices() == [1,2,3]) self.assertTrue(self.g3.articulation_points() == [2]) class CentralityTests(unittest.TestCase): def testBetweennessCentrality(self): g = Graph.Star(5) self.assertTrue(g.betweenness() == [6., 0., 0., 0., 0.]) g = Graph(5, [(0, 1), (0, 2), (0, 3), (1, 4)]) self.assertTrue(g.betweenness() == [5., 3., 0., 0., 0.]) self.assertTrue(g.betweenness(cutoff=2) == [3., 1., 0., 0., 0.]) self.assertTrue(g.betweenness(cutoff=1) == [0., 0., 0., 0., 0.]) g = Graph.Lattice([3, 3], circular=False) self.assertTrue(g.betweenness(cutoff=2) == [0.5, 2.0, 0.5, 2.0, 4.0, 2.0, 0.5, 2.0, 0.5]) def testEdgeBetweennessCentrality(self): g = Graph.Star(5) self.assertTrue(g.edge_betweenness() == [4., 4., 4., 4.]) g = Graph(5, [(0, 1), (0, 2), (0, 3), (1, 4)]) self.assertTrue(g.edge_betweenness() == [6., 4., 4., 4.]) self.assertTrue(g.edge_betweenness(cutoff=2) == [4., 3., 3., 2.]) self.assertTrue(g.edge_betweenness(cutoff=1) == [1., 1., 1., 1.]) g = Graph.Ring(5) self.assertTrue(g.edge_betweenness() == [3., 3., 3., 3., 3.]) self.assertTrue(g.edge_betweenness(weights=[4, 1, 1, 1, 1]) == \ [0.5, 3.5, 5.5, 5.5, 3.5]) def testClosenessCentrality(self): g = Graph.Star(5) cl = g.closeness() cl2 = [1., 0.57142, 0.57142, 0.57142, 0.57142] for idx in xrange(g.vcount()): self.assertAlmostEqual(cl[idx], cl2[idx], places=3) g = Graph.Star(5) cl = g.closeness(cutoff=1) cl2 = [1., 0.25, 0.25, 0.25, 0.25] for idx in xrange(g.vcount()): self.assertAlmostEqual(cl[idx], cl2[idx], places=3) weights = [1] * 4 g = Graph.Star(5) cl = g.closeness(weights=weights) cl2 = [1., 0.57142, 0.57142, 0.57142, 0.57142] for idx in xrange(g.vcount()): self.assertAlmostEqual(cl[idx], cl2[idx], places=3) g = Graph.Star(5) cl = g.closeness(cutoff=1, weights=weights) cl2 = [1., 0.25, 0.25, 0.25, 0.25] for idx in xrange(g.vcount()): self.assertAlmostEqual(cl[idx], cl2[idx], places=3) def testPageRank(self): g = Graph.Star(11) cent = g.pagerank() self.assertTrue(cent.index(max(cent)) == 0) self.assertAlmostEqual(max(cent), 0.4668, places=3) def testPersonalizedPageRank(self): g = Graph.Star(11) self.assertRaises(InternalError, g.personalized_pagerank, reset=[0]*11) cent = g.personalized_pagerank(reset=[0,10]+[0]*9, damping=0.5) self.assertTrue(cent.index(max(cent)) == 1) self.assertAlmostEqual(cent[0], 0.3333, places=3) self.assertAlmostEqual(cent[1], 0.5166, places=3) self.assertAlmostEqual(cent[2], 0.0166, places=3) cent2 = g.personalized_pagerank(reset_vertices=g.vs[1], damping=0.5) self.assertTrue(max(abs(x-y) for x, y in zip(cent, cent2)) < 0.001) def testEigenvectorCentrality(self): g = Graph.Star(11) cent = g.evcent() self.assertTrue(cent.index(max(cent)) == 0) self.assertAlmostEqual(max(cent), 1.0, places=3) self.assertTrue(min(cent) >= 0) cent, ev = g.evcent(scale=False, return_eigenvalue=True) if cent[0]<0: cent = [-x for x in cent] self.assertTrue(cent.index(max(cent)) == 0) self.assertAlmostEqual(cent[1]/cent[0], 0.3162, places=3) self.assertAlmostEqual(ev, 3.162, places=3) def testAuthorityScore(self): g = Graph.Tree(15, 2, TREE_IN) asc = g.authority_score() self.assertAlmostEqual(max(asc), 1.0, places=3) asc, ev = g.hub_score(scale=False, return_eigenvalue=True) if asc[0]<0: hs = [-x for x in asc] def testHubScore(self): g = Graph.Tree(15, 2, TREE_IN) hsc = g.hub_score() self.assertAlmostEqual(max(hsc), 1.0, places=3) hsc, ev = g.hub_score(scale=False, return_eigenvalue=True) if hsc[0]<0: hsc = [-x for x in hsc] def testCoreness(self): g = Graph.Full(4) + Graph(4) + [(0,4), (1,5), (2,6), (3,7)] self.assertEqual(g.coreness("A"), [3,3,3,3,1,1,1,1]) class NeighborhoodTests(unittest.TestCase): def testNeighborhood(self): g = Graph.Ring(10, circular=False) self.assertTrue(map(sorted, g.neighborhood()) == \ [[0,1], [0,1,2], [1,2,3], [2,3,4], [3,4,5], [4,5,6], \ [5,6,7], [6,7,8], [7,8,9], [8,9]]) self.assertTrue(map(sorted, g.neighborhood(order=3)) == \ [[0,1,2,3], [0,1,2,3,4], [0,1,2,3,4,5], [0,1,2,3,4,5,6], \ [1,2,3,4,5,6,7], [2,3,4,5,6,7,8], [3,4,5,6,7,8,9], \ [4,5,6,7,8,9], [5,6,7,8,9], [6,7,8,9]]) def testNeighborhoodSize(self): g = Graph.Ring(10, circular=False) self.assertTrue(g.neighborhood_size() == [2,3,3,3,3,3,3,3,3,2]) self.assertTrue(g.neighborhood_size(order=3) == [4,5,6,7,7,7,7,6,5,4]) class MiscTests(unittest.TestCase): def testConstraint(self): g = Graph(4, [(0, 1), (0, 2), (1, 2), (0, 3), (1, 3)]) self.assertTrue(isinstance(g.constraint(), list)) # TODO check more def testTopologicalSorting(self): g = Graph(5, [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)], directed=True) self.assertTrue(g.topological_sorting() == [0, 4, 1, 2, 3]) self.assertTrue(g.topological_sorting(IN) == [3, 4, 2, 1, 0]) g.to_undirected() self.assertRaises(InternalError, g.topological_sorting) def testIsDAG(self): g = Graph(5, [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)], directed=True) self.assertTrue(g.is_dag()) g.to_undirected() self.assertFalse(g.is_dag()) g = Graph.Barabasi(1000, 2, directed=True) self.assertTrue(g.is_dag()) g = Graph.GRG(100, 0.2) self.assertFalse(g.is_dag()) g = Graph.Ring(10, directed=True, mutual=False) self.assertFalse(g.is_dag()) def testLineGraph(self): g = Graph(4, [(0, 1), (0, 2), (1, 2), (0, 3), (1, 3)]) el = g.linegraph().get_edgelist() el.sort() self.assertTrue(el == [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (2, 4), (3, 4)]) g = Graph(4, [(0, 1), (0, 2), (1, 2), (0, 3), (1, 3)], directed=True) el = g.linegraph().get_edgelist() el.sort() self.assertTrue(el == [(0, 2), (0, 4)]) class PathTests(unittest.TestCase): def testShortestPaths(self): g = Graph(10, [(0,1), (0,2), (0,3), (1,2), (1,4), (1,5), (2,3), (2,6), \ (3,2), (3,6), (4,5), (4,7), (5,6), (5,8), (5,9), (7,5), (7,8), \ (8,9), (5,2), (2,1)], directed=True) ws = [0,2,1,0,5,2,1,1,0,2,2,8,1,1,3,1,1,4,2,1] g.es["weight"] = ws inf = float('inf') expected = [ [0, 0, 0, 1, 5, 2, 1, 13, 3, 5], [inf, 0, 0, 1, 5, 2, 1, 13, 3, 5], [inf, 1, 0, 1, 6, 3, 1, 14, 4, 6], [inf, 1, 0, 0, 6, 3, 1, 14, 4, 6], [inf, 5, 4, 5, 0, 2, 3, 8, 3, 5], [inf, 3, 2, 3, 8, 0, 1, 16, 1, 3], [inf, inf, inf, inf, inf, inf, 0, inf, inf, inf], [inf, 4, 3, 4, 9, 1, 2, 0, 1, 4], [inf, inf, inf, inf, inf, inf, inf, inf, 0, 4], [inf, inf, inf, inf, inf, inf, inf, inf, inf, 0] ] self.assertTrue(g.shortest_paths(weights=ws) == expected) self.assertTrue(g.shortest_paths(weights="weight") == expected) self.assertTrue(g.shortest_paths(weights="weight", target=[2,3]) == [row[2:4] for row in expected]) def testGetShortestPaths(self): g = Graph(4, [(0,1), (0,2), (1,3), (3,2), (2,1)], directed=True) sps = g.get_shortest_paths(0) expected = [[0], [0, 1], [0, 2], [0, 1, 3]] self.assertTrue(sps == expected) sps = g.get_shortest_paths(0, output="vpath") expected = [[0], [0, 1], [0, 2], [0, 1, 3]] self.assertTrue(sps == expected) sps = g.get_shortest_paths(0, output="epath") expected = [[], [0], [1], [0, 2]] self.assertTrue(sps == expected) self.assertRaises(ValueError, g.get_shortest_paths, 0, output="x") def testGetAllShortestPaths(self): g = Graph(4, [(0,1), (1, 2), (1, 3), (2, 4), (3, 4), (4, 5)], directed=True) sps = sorted(g.get_all_shortest_paths(0, 0)) expected = [[0]] self.assertEqual(expected, sps) sps = sorted(g.get_all_shortest_paths(0, 5)) expected = [[0, 1, 2, 4, 5], [0, 1, 3, 4, 5]] self.assertEqual(expected, sps) sps = sorted(g.get_all_shortest_paths(1, 4)) expected = [[1, 2, 4], [1, 3, 4]] self.assertEqual(expected, sps) g = Graph.Lattice([5, 5], circular=False) sps = sorted(g.get_all_shortest_paths(0, 12)) expected = [[0, 1, 2, 7, 12], [0, 1, 6, 7, 12], [0, 1, 6, 11, 12], \ [0, 5, 6, 7, 12], [0, 5, 6, 11, 12], [0, 5, 10, 11, 12]] self.assertEqual(expected, sps) g = Graph.Lattice([100, 100], circular=False) sps = sorted(g.get_all_shortest_paths(0, 202)) expected = [[0, 1, 2, 102, 202], [0, 1, 101, 102, 202], [0, 1, 101, 201, 202], \ [0, 100, 101, 102, 202], [0, 100, 101, 201, 202], [0, 100, 200, 201, 202]] self.assertEqual(expected, sps) g = Graph.Lattice([100, 100], circular=False) sps = sorted(g.get_all_shortest_paths(0, [0, 202])) self.assertEqual([[0]] + expected, sps) g = Graph([(0,1), (1,2), (0,2)]) g.es["weight"] = [0.5, 0.5, 1] sps = sorted(g.get_all_shortest_paths(0, weights="weight")) self.assertEqual([[0], [0,1], [0,1,2], [0,2]], sps) g = Graph.Lattice([4, 4], circular=False) g.es["weight"] = 1 g.es[2,8]["weight"] = 100 sps = sorted(g.get_all_shortest_paths(0, [3, 12, 15], weights="weight")) self.assertEqual(20, len(sps)) self.assertEqual(4, sum(1 for path in sps if path[-1] == 3)) self.assertEqual(4, sum(1 for path in sps if path[-1] == 12)) self.assertEqual(12, sum(1 for path in sps if path[-1] == 15)) def testPathLengthHist(self): g = Graph.Tree(15, 2) h = g.path_length_hist() self.assertTrue(h.unconnected == 0L) self.assertTrue([(int(l),x) for l,_,x in h.bins()] == \ [(1,14),(2,19),(3,20),(4,20),(5,16),(6,16)]) g = Graph.Full(5)+Graph.Full(4) h = g.path_length_hist() self.assertTrue(h.unconnected == 20) g.to_directed() h = g.path_length_hist() self.assertTrue(h.unconnected == 40) h = g.path_length_hist(False) self.assertTrue(h.unconnected == 20) def suite(): simple_suite = unittest.makeSuite(SimplePropertiesTests) degree_suite = unittest.makeSuite(DegreeTests) local_transitivity_suite = unittest.makeSuite(LocalTransitivityTests) biconnected_suite = unittest.makeSuite(BiconnectedComponentTests) centrality_suite = unittest.makeSuite(CentralityTests) neighborhood_suite = unittest.makeSuite(NeighborhoodTests) path_suite = unittest.makeSuite(PathTests) misc_suite = unittest.makeSuite(MiscTests) return unittest.TestSuite([simple_suite, degree_suite, local_transitivity_suite, biconnected_suite, centrality_suite, neighborhood_suite, path_suite, misc_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/test/utils.py0000644000076500000240000000323312460535410021734 0ustar ntamasstaff00000000000000"""Utility functions for unit testing.""" import functools import os import sys import tempfile import types from contextlib import contextmanager from textwrap import dedent __all__ = ["skip", "skipIf", "temporary_file"] def _id(obj): return obj try: from unittest import skip except ImportError: # Provide basic replacement for unittest.skip def skip(reason): """Unconditionally skip a test.""" def decorator(test_item): if not isinstance(test_item, (type, types.ClassType)): @functools.wraps(test_item) def skip_wrapper(*args, **kwds): if reason: sys.stderr.write("skipped, %s ... " % reason) else: sys.stderr.write("skipped, ") return test_item = skip_wrapper return test_item return decorator try: from unittest import skipIf except ImportError: # Provide basic replacement for unittest.skipIf def skipIf(condition, reason): """Skip a test if the condition is true.""" if condition: return skip(reason) return _id @contextmanager def temporary_file(content=None, mode=None): tmpf, tmpfname = tempfile.mkstemp() os.close(tmpf) if mode is None: if content is None: mode = "rb" else: mode = "wb" tmpf = open(tmpfname, mode) if content is not None: if isinstance(content, unicode): tmpf.write(dedent(content).encode("utf8")) else: tmpf.write(content) tmpf.close() yield tmpfname os.unlink(tmpfname) python-igraph-0.7.1.post6/igraph/test/vertexseq.py0000644000076500000240000002527712460535336022645 0ustar ntamasstaff00000000000000# vim:ts=4 sw=4 sts=4: import unittest from igraph import * from igraph.test.utils import skipIf try: import numpy as np except ImportError: np = None class VertexTests(unittest.TestCase): def setUp(self): self.g = Graph.Full(10) def testHash(self): data = {} n = self.g.vcount() for i in xrange(n): code1 = hash(self.g.vs[i]) code2 = hash(self.g.vs[i]) self.assertEqual(code1, code2) data[self.g.vs[i]] = i for i in xrange(n): self.assertEqual(i, data[self.g.vs[i]]) def testRichCompare(self): g2 = Graph.Full(10) for i in xrange(self.g.vcount()): for j in xrange(self.g.vcount()): self.assertEqual(i == j, self.g.vs[i] == self.g.vs[j]) self.assertEqual(i != j, self.g.vs[i] != self.g.vs[j]) self.assertEqual(i < j, self.g.vs[i] < self.g.vs[j]) self.assertEqual(i > j, self.g.vs[i] > self.g.vs[j]) self.assertEqual(i <= j, self.g.vs[i] <= self.g.vs[j]) self.assertEqual(i >= j, self.g.vs[i] >= self.g.vs[j]) self.assertFalse(self.g.vs[i] == g2.vs[j]) self.assertFalse(self.g.vs[i] != g2.vs[j]) self.assertFalse(self.g.vs[i] < g2.vs[j]) self.assertFalse(self.g.vs[i] > g2.vs[j]) self.assertFalse(self.g.vs[i] <= g2.vs[j]) self.assertFalse(self.g.vs[i] >= g2.vs[j]) self.assertFalse(self.g.es[i] == self.g.vs[j]) def testUpdateAttributes(self): v = self.g.vs[0] v.update_attributes(a=2) self.assertEqual(v["a"], 2) v.update_attributes([("a", 3), ("b", 4)], c=5, d=6) self.assertEqual(v.attributes(), dict(a=3, b=4, c=5, d=6)) v.update_attributes(dict(b=44, c=55)) self.assertEqual(v.attributes(), dict(a=3, b=44, c=55, d=6)) def testPhantomVertex(self): v = self.g.vs[9] v.delete() # v is now a phantom vertex; try to freak igraph out now :) self.assertRaises(ValueError, v.update_attributes, a=2) self.assertRaises(ValueError, v.__getitem__, "a") self.assertRaises(ValueError, v.__setitem__, "a", 4) self.assertRaises(ValueError, v.__delitem__, "a") self.assertRaises(ValueError, v.attributes) def testProxyMethods(self): # We only test with connected graphs because disconnected graphs might # print a warning when shortest_paths() is invoked on them and we want # to avoid that in the test output. while True: g = Graph.GRG(10, 0.6) if g.is_connected(): break v = g.vs[0] # - neighbors(), predecessors() and succesors() are ignored because they # return vertex lists while the methods in Graph return vertex index # lists. # - pagerank() and personalized_pagerank() are ignored because of numerical # inaccuracies # - delete() is ignored because it mutates the graph ignore = "neighbors predecessors successors pagerank personalized_pagerank"\ " delete" ignore = set(ignore.split()) # Methods not listed here are expected to return an int or a float return_types = { "get_shortest_paths": list, "shortest_paths": list } for name in Vertex.__dict__: if name in ignore: continue func = getattr(v, name) docstr = func.__doc__ if not docstr.startswith("Proxy method"): continue result = func() self.assertEqual(getattr(g, name)(v.index), result, msg=("Vertex.%s proxy method misbehaved" % name)) return_type = return_types.get(name, (int, float)) self.assertTrue(isinstance(result, return_type), msg=("Vertex.%s proxy method did not return %s" % (name, return_type)) ) class VertexSeqTests(unittest.TestCase): def setUp(self): self.g = Graph.Full(10) self.g.vs["test"] = range(10) self.g.vs["name"] = list("ABCDEFGHIJ") def testCreation(self): self.assertTrue(len(VertexSeq(self.g)) == 10) self.assertTrue(len(VertexSeq(self.g, 2)) == 1) self.assertTrue(len(VertexSeq(self.g, [1,2,3])) == 3) self.assertTrue(VertexSeq(self.g, [1,2,3]).indices == [1,2,3]) self.assertRaises(ValueError, VertexSeq, self.g, 12) self.assertRaises(ValueError, VertexSeq, self.g, [12]) self.assertTrue(self.g.vs.graph == self.g) def testIndexing(self): for i in xrange(self.g.vcount()): self.assertEqual(i, self.g.vs[i].index) self.assertRaises(IndexError, self.g.vs.__getitem__, -1) self.assertRaises(TypeError, self.g.vs.__getitem__, 1.5) @skipIf(np is None, "test case depends on NumPy") def testNumPyIndexing(self): if np is None: return for i in xrange(self.g.vcount()): arr = np.array([i]) self.assertEqual(i, self.g.vs[arr[0]].index) arr = np.array([-1]) self.assertRaises(IndexError, self.g.vs.__getitem__, arr[0]) arr = np.array([1.5]) self.assertRaises(TypeError, self.g.vs.__getitem__, arr[0]) def testPartialAttributeAssignment(self): only_even = self.g.vs.select(lambda v: (v.index % 2 == 0)) only_even["test"] = [0]*len(only_even) self.assertTrue(self.g.vs["test"] == [0,1,0,3,0,5,0,7,0,9]) only_even["test2"] = range(5) self.assertTrue(self.g.vs["test2"] == [0,None,1,None,2,None,3,None,4,None]) def testSequenceReusing(self): if "test" in self.g.vertex_attributes(): del self.g.vs["test"] self.g.vs["test"] = ["A", "B", "C"] self.assertTrue(self.g.vs["test"] == ["A", "B", "C", "A", "B", "C", "A", "B", "C", "A"]) self.g.vs["test"] = "ABC" self.assertTrue(self.g.vs["test"] == ["ABC"] * 10) only_even = self.g.vs.select(lambda v: (v.index % 2 == 0)) only_even["test"] = ["D", "E"] self.assertTrue(self.g.vs["test"] == ["D", "ABC", "E", "ABC", "D", "ABC", "E", "ABC", "D", "ABC"]) del self.g.vs["test"] only_even["test"] = ["D", "E"] self.assertTrue(self.g.vs["test"] == ["D", None, "E", None, "D", None, "E", None, "D", None]) def testAllSequence(self): self.assertTrue(len(self.g.vs) == 10) self.assertTrue(self.g.vs["test"] == range(10)) def testEmptySequence(self): empty_vs = self.g.vs.select(None) self.assertTrue(len(empty_vs) == 0) self.assertRaises(IndexError, empty_vs.__getitem__, 0) self.assertRaises(KeyError, empty_vs.__getitem__, "nonexistent") self.assertTrue(empty_vs["test"] == []) empty_vs = self.g.vs[[]] self.assertTrue(len(empty_vs) == 0) empty_vs = self.g.vs[()] self.assertTrue(len(empty_vs) == 0) def testCallableFilteringFind(self): vertex = self.g.vs.find(lambda v: (v.index % 2 == 1)) self.assertTrue(vertex.index == 1) self.assertRaises(IndexError, self.g.vs.find, lambda v: (v.index % 2 == 3)) def testCallableFilteringSelect(self): only_even = self.g.vs.select(lambda v: (v.index % 2 == 0)) self.assertTrue(len(only_even) == 5) self.assertRaises(KeyError, only_even.__getitem__, "nonexistent") self.assertTrue(only_even["test"] == [0, 2, 4, 6, 8]) def testChainedCallableFilteringSelect(self): only_div_six = self.g.vs.select(lambda v: (v.index % 2 == 0), lambda v: (v.index % 3 == 0)) self.assertTrue(len(only_div_six) == 2) self.assertTrue(only_div_six["test"] == [0, 6]) only_div_six = self.g.vs.select(lambda v: (v.index % 2 == 0)).select(\ lambda v: (v.index % 3 == 0)) self.assertTrue(len(only_div_six) == 2) self.assertTrue(only_div_six["test"] == [0, 6]) def testIntegerFilteringFind(self): self.assertEqual(self.g.vs.find(3).index, 3) self.assertEqual(self.g.vs.select(2,3,4,2).find(3).index, 2) self.assertRaises(IndexError, self.g.vs.find, 17) def testIntegerFilteringSelect(self): subset = self.g.vs.select(2,3,4,2) self.assertEqual(len(subset), 4) self.assertEqual(subset["test"], [2,3,4,2]) self.assertRaises(TypeError, self.g.vs.select, 2, 3, 4, 2, None) subset = self.g.vs[2,3,4,2] self.assertTrue(len(subset) == 4) self.assertTrue(subset["test"] == [2,3,4,2]) def testStringFilteringFind(self): self.assertEqual(self.g.vs.find("D").index, 3) self.assertEqual(self.g.vs.select(2,3,4,2).find("C").index, 2) self.assertRaises(ValueError, self.g.vs.select(2,3,4,2).find, "F") self.assertRaises(ValueError, self.g.vs.find, "NoSuchName") def testIterableFilteringSelect(self): subset = self.g.vs.select(xrange(5,8)) self.assertTrue(len(subset) == 3) self.assertTrue(subset["test"] == [5,6,7]) def testSliceFilteringSelect(self): subset = self.g.vs.select(slice(5, 8)) self.assertTrue(len(subset) == 3) self.assertTrue(subset["test"] == [5,6,7]) subset = self.g.vs[5:16:2] self.assertTrue(len(subset) == 3) self.assertTrue(subset["test"] == [5,7,9]) def testKeywordFilteringSelect(self): g = Graph.Barabasi(10000) g.vs["degree"] = g.degree() g.vs["parity"] = [i % 2 for i in xrange(g.vcount())] l = len(g.vs(degree_gt=30)) self.assertTrue(l < 1000) self.assertTrue(len(g.vs(degree_gt=30, parity=0)) <= 500) del g.vs["degree"] self.assertTrue(len(g.vs(_degree_gt=30)) == l) def testIndexOutOfBoundsSelect(self): g = Graph.Full(3) self.assertRaises(ValueError, g.vs.select, 4) self.assertRaises(ValueError, g.vs.select, 4, 5) self.assertRaises(ValueError, g.vs.select, (4, 5)) self.assertRaises(ValueError, g.vs.select, 2, -1) self.assertRaises(ValueError, g.vs.select, (2, -1)) self.assertRaises(ValueError, g.vs.__getitem__, (0, 1000000)) def testGraphMethodProxying(self): g = Graph.Barabasi(100) vs = g.vs(1,3,5,7,9) self.assertEqual(vs.degree(), g.degree(vs)) self.assertEqual(g.degree(vs), g.degree(vs.indices)) for v, d in zip(vs, vs.degree()): self.assertEqual(v.degree(), d) def suite(): vertex_suite = unittest.makeSuite(VertexTests) vs_suite = unittest.makeSuite(VertexSeqTests) return unittest.TestSuite([vertex_suite, vs_suite]) def test(): runner = unittest.TextTestRunner() runner.run(suite()) if __name__ == "__main__": test() python-igraph-0.7.1.post6/igraph/utils.py0000644000076500000240000002771512476153444021002 0ustar ntamasstaff00000000000000# vim:ts=4:sw=4:sts=4:et # -*- coding: utf-8 -*- """Utility functions that cannot be categorised anywhere else. @undocumented: _is_running_in_ipython """ from contextlib import contextmanager from collections import MutableMapping from itertools import chain import os import tempfile __all__ = ["dbl_epsilon", "multidict", "named_temporary_file", "rescale", \ "safemin", "safemax"] __docformat__ = "restructuredtext en" __license__ = u"""\ Copyright (C) 2006-2012 Tamás Nepusz Pázmány Péter sétány 1/a, 1117 Budapest, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ def _is_running_in_ipython(): """Internal function that determines whether igraph is running inside IPython or not.""" try: # get_ipython is injected into the Python builtins by IPython so # this should succeed in IPython but throw a NameError otherwise get_ipython return True except NameError: return False @contextmanager def named_temporary_file(*args, **kwds): """Context manager that creates a named temporary file and returns its name. All parameters are passed on to ``tempfile.mkstemp``, see its documentation for more info. """ handle, tmpfile = tempfile.mkstemp(*args, **kwds) os.close(handle) try: yield tmpfile finally: os.unlink(tmpfile) def rescale(values, out_range = (0., 1.), in_range = None, clamp = False, scale = None): """Rescales a list of numbers into a given range. `out_range` gives the range of the output values; by default, the minimum of the original numbers in the list will be mapped to the first element in the output range and the maximum will be mapped to the second element. Elements between the minimum and maximum values in the input list will be interpolated linearly between the first and second values of the output range. `in_range` may be used to override which numbers are mapped to the first and second values of the output range. This must also be a tuple, where the first element will be mapped to the first element of the output range and the second element to the second. If `clamp` is ``True``, elements which are outside the given `out_range` after rescaling are clamped to the output range to ensure that no number will be outside `out_range` in the result. If `scale` is not ``None``, it will be called for every element of `values` and the rescaling will take place on the results instead. This can be used, for instance, to transform the logarithm of the original values instead of the actual values. A typical use-case is to map a range of values to color identifiers on a logarithmic scale. Scaling also applies to the `in_range` parameter if present. Examples: >>> rescale(range(5), (0, 8)) [0.0, 2.0, 4.0, 6.0, 8.0] >>> rescale(range(5), (2, 10)) [2.0, 4.0, 6.0, 8.0, 10.0] >>> rescale(range(5), (0, 4), (1, 3)) [-2.0, 0.0, 2.0, 4.0, 6.0] >>> rescale(range(5), (0, 4), (1, 3), clamp=True) [0.0, 0.0, 2.0, 4.0, 4.0] >>> rescale([0]*5, (1, 3)) [2.0, 2.0, 2.0, 2.0, 2.0] >>> from math import log10 >>> rescale([1, 10, 100, 1000, 10000], (0, 8), scale=log10) [0.0, 2.0, 4.0, 6.0, 8.0] >>> rescale([1, 10, 100, 1000, 10000], (0, 4), (10, 1000), scale=log10) [-2.0, 0.0, 2.0, 4.0, 6.0] """ if scale is not None: values = [scale(value) for value in values] if in_range is None: mi, ma = min(values), max(values) else: mi, ma = in_range if scale is not None: mi, ma = scale(mi), scale(ma) ratio = float(ma - mi) if not ratio: return [(out_range[0] + out_range[1]) / 2.] * len(values) min_out, max_out = map(float, out_range) ratio = (max_out - min_out) / ratio result = [(x - mi) * ratio + min_out for x in values] if clamp: return [max(min(x, max_out), min_out) for x in result] else: return result def str_to_orientation(value, reversed_horizontal=False, reversed_vertical=False): """Tries to interpret a string as an orientation value. The following basic values are understood: ``left-right``, ``bottom-top``, ``right-left``, ``top-bottom``. Possible aliases are: - ``horizontal``, ``horiz``, ``h`` and ``lr`` for ``left-right`` - ``vertical``, ``vert``, ``v`` and ``tb`` for top-bottom. - ``lr`` for ``left-right``. - ``rl`` for ``right-left``. ``reversed_horizontal`` reverses the meaning of ``horizontal``, ``horiz`` and ``h`` to ``rl`` (instead of ``lr``); similarly, ``reversed_vertical`` reverses the meaning of ``vertical``, ``vert`` and ``v`` to ``bt`` (instead of ``tb``). Returns one of ``lr``, ``rl``, ``tb`` or ``bt``, or throws ``ValueError`` if the string cannot be interpreted as an orientation. """ aliases = {"left-right": "lr", "right-left": "rl", "top-bottom": "tb", "bottom-top": "bt", "top-down": "tb", "bottom-up": "bt", "top-bottom": "tb", "bottom-top": "bt", "td": "tb", "bu": "bt"} dir = ["lr", "rl"][reversed_horizontal] aliases.update(horizontal=dir, horiz=dir, h=dir) dir = ["tb", "bt"][reversed_vertical] aliases.update(vertical=dir, vert=dir, v=dir) result = aliases.get(value, value) if result not in ("lr", "rl", "tb", "bt"): raise ValueError("unknown orientation: %s" % result) return result def consecutive_pairs(iterable, circular=False): """Returns consecutive pairs of items from the given iterable. When `circular` is ``True``, the pair consisting of the last and first elements is also returned. Example: >>> list(consecutive_pairs(range(5))) [(0, 1), (1, 2), (2, 3), (3, 4)] >>> list(consecutive_pairs(range(5), circular=True)) [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)] >>> list(consecutive_pairs([])) [] >>> list(consecutive_pairs([], circular=True)) [] >>> list(consecutive_pairs([0])) [] >>> list(consecutive_pairs([0], circular=True)) [(0, 0)] """ it = iter(iterable) try: prev = it.next() except StopIteration: return first = prev for item in it: yield prev, item prev = item if circular: try: yield item, first except UnboundLocalError: yield first, first class multidict(MutableMapping): """A dictionary-like object that is customized to deal with multiple values for the same key. Each value in this dictionary will be a list. Methods which emulate the methods of a standard Python `dict` object will return or manipulate the first items of the lists only. Special methods are provided to deal with keys having multiple values. """ def __init__(self, *args, **kwds): self._dict = {} if len(args) > 1: raise ValueError("%r expected at most 1 argument, got %d" % \ (self.__class__.__name__, len(args))) if args: args = args[0] self.update(args) self.update(kwds) def __contains__(self, key): """Returns whether there are any items associated to the given `key`.""" try: return len(self._dict[key]) > 0 except KeyError: return False def __delitem__(self, key): """Removes all the items associated to the given `key`.""" del self._dict[key] def __getitem__(self, key): """Returns an arbitrary item associated to the given key. Raises ``KeyError`` if no such key exists. Example: >>> d = multidict([("spam", "eggs"), ("spam", "bacon")]) >>> d["spam"] 'eggs' """ try: return self._dict[key][0] except IndexError: raise KeyError(key) def __iter__(self): """Iterates over the keys of the multidict.""" return iter(self._dict) def __len__(self): """Returns the number of distinct keys in this multidict.""" return len(self._dict) def __setitem__(self, key, value): """Sets the item associated to the given `key`. Any values associated to the key will be erased and replaced by `value`. Example: >>> d = multidict([("spam", "eggs"), ("spam", "bacon")]) >>> d["spam"] = "ham" >>> d["spam"] 'ham' """ self._dict[key] = [value] def add(self, key, value): """Adds `value` to the list of items associated to `key`. Example: >>> d = multidict() >>> d.add("spam", "ham") >>> d["spam"] 'ham' >>> d.add("spam", "eggs") >>> d.getlist("spam") ['ham', 'eggs'] """ try: self._dict[key].append(value) except KeyError: self._dict[key] = [value] def clear(self): """Removes all the items from the multidict.""" self._dict.clear() def get(self, key, default=None): """Returns an arbitrary item associated to the given `key`. If `key` does not exist or has zero associated items, `default` will be returned.""" try: items = self._dict[key] return items[0] except (KeyError, IndexError): return default def getlist(self, key): """Returns the list of values for the given `key`. An empty list will be returned if there is no such key.""" try: return self._dict[key] except KeyError: return [] def iterlists(self): """Iterates over ``(key, values)`` pairs where ``values`` is the list of values associated with ``key``.""" return self._dict.iteritems() def lists(self): """Returns a list of ``(key, values)`` pairs where ``values`` is the list of values associated with ``key``.""" return self._dict.items() def update(self, arg, **kwds): if hasattr(arg, "keys") and callable(arg.keys): for key in arg.keys(): self.add(key, arg[key]) else: for key, value in arg: self.add(key, value) for key, value in kwds.iteritems(): self.add(key, value) def safemax(iterable, default=0): """Safer variant of ``max()`` that returns a default value if the iterable is empty. Example: >>> safemax([-5, 6, 4]) 6 >>> safemax([]) 0 >>> safemax((), 2) 2 """ it = iter(iterable) try: first = it.next() except StopIteration: return default else: return max(chain([first], it)) def safemin(iterable, default=0): """Safer variant of ``min()`` that returns a default value if the iterable is empty. Example: >>> safemin([-5, 6, 4]) -5 >>> safemin([]) 0 >>> safemin((), 2) 2 """ it = iter(iterable) try: first = it.next() except StopIteration: return default else: return min(chain([first], it)) def dbl_epsilon(): """Approximates the machine epsilon value for doubles.""" epsilon = 1.0 while 1.0 + epsilon / 2.0 != 1.0: epsilon /= 2 return epsilon dbl_epsilon = dbl_epsilon() python-igraph-0.7.1.post6/igraph/vendor/0000755000076500000240000000000012534343010020532 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/igraph/vendor/__init__.py0000644000076500000240000000233612453614202022653 0ustar ntamasstaff00000000000000""" This package contains third party libraries that igraph depends on and that are small enough to be distributed with igraph itself. The primary entry point of this module is ``vendor_import``, a function that first tries to import a particular library using the standard Python mechanism and falls back to the version of the library provided within ``igraph.vendor`` if the standard Python import fails. The libraries contained within igraph are as follows: - `texttable`, a library to print ASCII tables, by Gerome Fournier. See . """ __license__ = "GPL" __all__ = ["vendor_import"] __docformat__ = "restructuredtext en" def vendor_import(module_name): """Tries to import a module name ``module_name`` using the standard Python `import` statement and return the imported module. If the import fails, tries to import a module of the same name from within ``igraph.vendor`` and return that module instead. """ parts = module_name.split(".") try: result = __import__(module_name, level=0) except ImportError: result = __import__("igraph.vendor.%s" % module_name, level=0) parts[0:0] = ["igraph", "vendor"] parts.pop(0) while parts: result = getattr(result, parts.pop(0)) return result python-igraph-0.7.1.post6/igraph/vendor/texttable.py0000644000076500000240000004377312453614202023122 0ustar ntamasstaff00000000000000#!/usr/bin/env python # # texttable - module for creating simple ASCII tables # Copyright (C) 2003-2011 Gerome Fournier # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """module for creating simple ASCII tables Example: table = Texttable() table.set_cols_align(["l", "r", "c"]) table.set_cols_valign(["t", "m", "b"]) table.add_rows([ ["Name", "Age", "Nickname"], ["Mr\\nXavier\\nHuon", 32, "Xav'"], ["Mr\\nBaptiste\\nClement", 1, "Baby"] ]) print table.draw() + "\\n" table = Texttable() table.set_deco(Texttable.HEADER) table.set_cols_dtype(['t', # text 'f', # float (decimal) 'e', # float (exponent) 'i', # integer 'a']) # automatic table.set_cols_align(["l", "r", "r", "r", "l"]) table.add_rows([["text", "float", "exp", "int", "auto"], ["abcd", "67", 654, 89, 128.001], ["efghijk", 67.5434, .654, 89.6, 12800000000000000000000.00023], ["lmn", 5e-78, 5e-78, 89.4, .000000000000128], ["opqrstu", .023, 5e+78, 92., 12800000000000000000000]]) print table.draw() Result: +----------+-----+----------+ | Name | Age | Nickname | +==========+=====+==========+ | Mr | | | | Xavier | 32 | | | Huon | | Xav' | +----------+-----+----------+ | Mr | | | | Baptiste | 1 | | | Clement | | Baby | +----------+-----+----------+ text float exp int auto =========================================== abcd 67.000 6.540e+02 89 128.001 efgh 67.543 6.540e-01 90 1.280e+22 ijkl 0.000 5.000e-78 89 0.000 mnop 0.023 5.000e+78 92 1.280e+22 """ __all__ = ["Texttable", "ArraySizeError"] __author__ = 'Gerome Fournier ' __license__ = 'GPL' __version__ = '0.8.1' __credits__ = """\ Jeff Kowalczyk: - textwrap improved import - comment concerning header output Anonymous: - add_rows method, for adding rows in one go Sergey Simonenko: - redefined len() function to deal with non-ASCII characters Roger Lew: - columns datatype specifications Brian Peterson: - better handling of unicode errors """ import sys import string try: if sys.version >= '2.3': import textwrap elif sys.version >= '2.2': from optparse import textwrap else: from optik import textwrap except ImportError: sys.stderr.write("Can't import textwrap module!\n") raise def len(iterable): """Redefining len here so it will be able to work with non-ASCII characters """ if not isinstance(iterable, str): return iterable.__len__() try: return len(unicode(iterable, 'utf')) except: return iterable.__len__() class ArraySizeError(Exception): """Exception raised when specified rows don't fit the required size """ def __init__(self, msg): self.msg = msg Exception.__init__(self, msg, '') def __str__(self): return self.msg class Texttable: BORDER = 1 HEADER = 1 << 1 HLINES = 1 << 2 VLINES = 1 << 3 def __init__(self, max_width=80): """Constructor - max_width is an integer, specifying the maximum width of the table - if set to 0, size is unlimited, therefore cells won't be wrapped """ if max_width <= 0: max_width = False self._max_width = max_width self._precision = 3 self._deco = Texttable.VLINES | Texttable.HLINES | Texttable.BORDER | \ Texttable.HEADER self.set_chars(['-', '|', '+', '=']) self.reset() def reset(self): """Reset the instance - reset rows and header """ self._hline_string = None self._row_size = None self._header = [] self._rows = [] def set_chars(self, array): """Set the characters used to draw lines between rows and columns - the array should contain 4 fields: [horizontal, vertical, corner, header] - default is set to: ['-', '|', '+', '='] """ if len(array) != 4: raise ArraySizeError, "array should contain 4 characters" array = [ x[:1] for x in [ str(s) for s in array ] ] (self._char_horiz, self._char_vert, self._char_corner, self._char_header) = array def set_deco(self, deco): """Set the table decoration - 'deco' can be a combinaison of: Texttable.BORDER: Border around the table Texttable.HEADER: Horizontal line below the header Texttable.HLINES: Horizontal lines between rows Texttable.VLINES: Vertical lines between columns All of them are enabled by default - example: Texttable.BORDER | Texttable.HEADER """ self._deco = deco def set_cols_align(self, array): """Set the desired columns alignment - the elements of the array should be either "l", "c" or "r": * "l": column flushed left * "c": column centered * "r": column flushed right """ self._check_row_size(array) self._align = array def set_cols_valign(self, array): """Set the desired columns vertical alignment - the elements of the array should be either "t", "m" or "b": * "t": column aligned on the top of the cell * "m": column aligned on the middle of the cell * "b": column aligned on the bottom of the cell """ self._check_row_size(array) self._valign = array def set_cols_dtype(self, array): """Set the desired columns datatype for the cols. - the elements of the array should be either "a", "t", "f", "e" or "i": * "a": automatic (try to use the most appropriate datatype) * "t": treat as text * "f": treat as float in decimal format * "e": treat as float in exponential format * "i": treat as int - by default, automatic datatyping is used for each column """ self._check_row_size(array) self._dtype = array def set_cols_width(self, array): """Set the desired columns width - the elements of the array should be integers, specifying the width of each column. For example: [10, 20, 5] """ self._check_row_size(array) try: array = map(int, array) if reduce(min, array) <= 0: raise ValueError except ValueError: sys.stderr.write("Wrong argument in column width specification\n") raise self._width = array def set_precision(self, width): """Set the desired precision for float/exponential formats - width must be an integer >= 0 - default value is set to 3 """ if not type(width) is int or width < 0: raise ValueError('width must be an integer greater then 0') self._precision = width def header(self, array): """Specify the header of the table """ self._check_row_size(array) self._header = map(str, array) def add_row(self, array): """Add a row in the rows stack - cells can contain newlines and tabs """ self._check_row_size(array) if not hasattr(self, "_dtype"): self._dtype = ["a"] * self._row_size cells = [] for i,x in enumerate(array): cells.append(self._str(i,x)) self._rows.append(cells) def add_rows(self, rows, header=True): """Add several rows in the rows stack - The 'rows' argument can be either an iterator returning arrays, or a by-dimensional array - 'header' specifies if the first row should be used as the header of the table """ # nb: don't use 'iter' on by-dimensional arrays, to get a # usable code for python 2.1 if header: if hasattr(rows, '__iter__') and hasattr(rows, 'next'): self.header(rows.next()) else: self.header(rows[0]) rows = rows[1:] for row in rows: self.add_row(row) def draw(self): """Draw the table - the table is returned as a whole string """ if not self._header and not self._rows: return self._compute_cols_width() self._check_align() out = "" if self._has_border(): out += self._hline() if self._header: out += self._draw_line(self._header, isheader=True) if self._has_header(): out += self._hline_header() length = 0 for row in self._rows: length += 1 out += self._draw_line(row) if self._has_hlines() and length < len(self._rows): out += self._hline() if self._has_border(): out += self._hline() return out[:-1] def _str(self, i, x): """Handles string formatting of cell data i - index of the cell datatype in self._dtype x - cell data to format """ try: f = float(x) except: return str(x) n = self._precision dtype = self._dtype[i] if dtype == 'i': return str(int(round(f))) elif dtype == 'f': return '%.*f' % (n, f) elif dtype == 'e': return '%.*e' % (n, f) elif dtype == 't': return str(x) else: if f - round(f) == 0: if abs(f) > 1e8: return '%.*e' % (n, f) else: return str(int(round(f))) else: if abs(f) > 1e8: return '%.*e' % (n, f) else: return '%.*f' % (n, f) def _check_row_size(self, array): """Check that the specified array fits the previous rows size """ if not self._row_size: self._row_size = len(array) elif self._row_size != len(array): raise ArraySizeError, "array should contain %d elements" \ % self._row_size def _has_vlines(self): """Return a boolean, if vlines are required or not """ return self._deco & Texttable.VLINES > 0 def _has_hlines(self): """Return a boolean, if hlines are required or not """ return self._deco & Texttable.HLINES > 0 def _has_border(self): """Return a boolean, if border is required or not """ return self._deco & Texttable.BORDER > 0 def _has_header(self): """Return a boolean, if header line is required or not """ return self._deco & Texttable.HEADER > 0 def _hline_header(self): """Print header's horizontal line """ return self._build_hline(True) def _hline(self): """Print an horizontal line """ if not self._hline_string: self._hline_string = self._build_hline() return self._hline_string def _build_hline(self, is_header=False): """Return a string used to separated rows or separate header from rows """ horiz = self._char_horiz if (is_header): horiz = self._char_header # compute cell separator s = "%s%s%s" % (horiz, [horiz, self._char_corner][self._has_vlines()], horiz) # build the line l = string.join([horiz * n for n in self._width], s) # add border if needed if self._has_border(): l = "%s%s%s%s%s\n" % (self._char_corner, horiz, l, horiz, self._char_corner) else: l += "\n" return l def _len_cell(self, cell): """Return the width of the cell Special characters are taken into account to return the width of the cell, such like newlines and tabs """ cell_lines = cell.split('\n') maxi = 0 for line in cell_lines: length = 0 parts = line.split('\t') for part, i in zip(parts, range(1, len(parts) + 1)): length = length + len(part) if i < len(parts): length = (length/8 + 1) * 8 maxi = max(maxi, length) return maxi def _compute_cols_width(self): """Return an array with the width of each column If a specific width has been specified, exit. If the total of the columns width exceed the table desired width, another width will be computed to fit, and cells will be wrapped. """ if hasattr(self, "_width"): return maxi = [] if self._header: maxi = [ self._len_cell(x) for x in self._header ] for row in self._rows: for cell,i in zip(row, range(len(row))): try: maxi[i] = max(maxi[i], self._len_cell(cell)) except (TypeError, IndexError): maxi.append(self._len_cell(cell)) items = len(maxi) length = reduce(lambda x,y: x+y, maxi) if self._max_width and length + items * 3 + 1 > self._max_width: maxi = [(self._max_width - items * 3 -1) / items \ for n in range(items)] self._width = maxi def _check_align(self): """Check if alignment has been specified, set default one if not """ if not hasattr(self, "_align"): self._align = ["l"] * self._row_size if not hasattr(self, "_valign"): self._valign = ["t"] * self._row_size def _draw_line(self, line, isheader=False): """Draw a line Loop over a single cell length, over all the cells """ line = self._splitit(line, isheader) space = " " out = "" for i in range(len(line[0])): if self._has_border(): out += "%s " % self._char_vert length = 0 for cell, width, align in zip(line, self._width, self._align): length += 1 cell_line = cell[i] fill = width - len(cell_line) if isheader: align = "c" if align == "r": out += "%s " % (fill * space + cell_line) elif align == "c": out += "%s " % (fill/2 * space + cell_line \ + (fill/2 + fill%2) * space) else: out += "%s " % (cell_line + fill * space) if length < len(line): out += "%s " % [space, self._char_vert][self._has_vlines()] out += "%s\n" % ['', self._char_vert][self._has_border()] return out def _splitit(self, line, isheader): """Split each element of line to fit the column width Each element is turned into a list, result of the wrapping of the string to the desired width """ line_wrapped = [] for cell, width in zip(line, self._width): array = [] for c in cell.split('\n'): try: c = unicode(c, 'utf') except UnicodeDecodeError, strerror: sys.stderr.write("UnicodeDecodeError exception for string '%s': %s\n" % (c, strerror)) c = unicode(c, 'utf', 'replace') array.extend(textwrap.wrap(c, width)) line_wrapped.append(array) max_cell_lines = reduce(max, map(len, line_wrapped)) for cell, valign in zip(line_wrapped, self._valign): if isheader: valign = "t" if valign == "m": missing = max_cell_lines - len(cell) cell[:0] = [""] * (missing / 2) cell.extend([""] * (missing / 2 + missing % 2)) elif valign == "b": cell[:0] = [""] * (max_cell_lines - len(cell)) else: cell.extend([""] * (max_cell_lines - len(cell))) return line_wrapped if __name__ == '__main__': table = Texttable() table.set_cols_align(["l", "r", "c"]) table.set_cols_valign(["t", "m", "b"]) table.add_rows([ ["Name", "Age", "Nickname"], ["Mr\nXavier\nHuon", 32, "Xav'"], ["Mr\nBaptiste\nClement", 1, "Baby"] ]) print table.draw() + "\n" table = Texttable() table.set_deco(Texttable.HEADER) table.set_cols_dtype(['t', # text 'f', # float (decimal) 'e', # float (exponent) 'i', # integer 'a']) # automatic table.set_cols_align(["l", "r", "r", "r", "l"]) table.add_rows([["text", "float", "exp", "int", "auto"], ["abcd", "67", 654, 89, 128.001], ["efghijk", 67.5434, .654, 89.6, 12800000000000000000000.00023], ["lmn", 5e-78, 5e-78, 89.4, .000000000000128], ["opqrstu", .023, 5e+78, 92., 12800000000000000000000]]) print table.draw() python-igraph-0.7.1.post6/MANIFEST.in0000644000076500000240000000025312453614202017525 0ustar ntamasstaff00000000000000include setup.cfg include src/*.h include MANIFEST.in include COPYING include scripts/mkdoc.sh include scripts/epydoc-patched include scripts/epydoc.cfg include test/*.py python-igraph-0.7.1.post6/PKG-INFO0000644000076500000240000000431312534343010017061 0ustar ntamasstaff00000000000000Metadata-Version: 1.1 Name: python-igraph Version: 0.7.1.post6 Summary: High performance graph data structures and algorithms Home-page: http://pypi.python.org/pypi/python-igraph Author: Tamas Nepusz Author-email: tamas@cs.rhul.ac.uk License: GNU General Public License (GPL) Description: Python interface to the igraph high performance graph library, primarily aimed at complex network research and analysis. Graph plotting functionality is provided by the Cairo library, so make sure you install the Python bindings of Cairo if you want to generate publication-quality graph plots. See the `Cairo homepage `_ for details. From release 0.5, the C core of the igraph library is **not** included in the Python distribution - you must compile and install the C core separately. Windows installers already contain a compiled igraph DLL, so they should work out of the box. Linux users should refer to the `igraph homepage `_ for compilation instructions (but check your distribution first, maybe there are pre-compiled packages available). OS X users may benefit from the disk images in the Python Package Index. Unofficial installers for 64-bit Windows machines and/or different Python versions can also be found `here `_. Many thanks to the maintainers of this page! Keywords: graph,network,mathematics,math,graph theory,discrete mathematics Platform: ALL Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: Operating System :: OS Independent Classifier: Programming Language :: C Classifier: Programming Language :: Python Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Scientific/Engineering :: Information Analysis Classifier: Topic :: Scientific/Engineering :: Mathematics Classifier: Topic :: Scientific/Engineering :: Physics Classifier: Topic :: Scientific/Engineering :: Bio-Informatics Classifier: Topic :: Software Development :: Libraries :: Python Modules python-igraph-0.7.1.post6/python_igraph.egg-info/0000755000076500000240000000000012534343010022330 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/python_igraph.egg-info/dependency_links.txt0000644000076500000240000000000112534343010026376 0ustar ntamasstaff00000000000000 python-igraph-0.7.1.post6/python_igraph.egg-info/PKG-INFO0000644000076500000240000000431312534343010023426 0ustar ntamasstaff00000000000000Metadata-Version: 1.1 Name: python-igraph Version: 0.7.1.post6 Summary: High performance graph data structures and algorithms Home-page: http://pypi.python.org/pypi/python-igraph Author: Tamas Nepusz Author-email: tamas@cs.rhul.ac.uk License: GNU General Public License (GPL) Description: Python interface to the igraph high performance graph library, primarily aimed at complex network research and analysis. Graph plotting functionality is provided by the Cairo library, so make sure you install the Python bindings of Cairo if you want to generate publication-quality graph plots. See the `Cairo homepage `_ for details. From release 0.5, the C core of the igraph library is **not** included in the Python distribution - you must compile and install the C core separately. Windows installers already contain a compiled igraph DLL, so they should work out of the box. Linux users should refer to the `igraph homepage `_ for compilation instructions (but check your distribution first, maybe there are pre-compiled packages available). OS X users may benefit from the disk images in the Python Package Index. Unofficial installers for 64-bit Windows machines and/or different Python versions can also be found `here `_. Many thanks to the maintainers of this page! Keywords: graph,network,mathematics,math,graph theory,discrete mathematics Platform: ALL Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: Operating System :: OS Independent Classifier: Programming Language :: C Classifier: Programming Language :: Python Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Scientific/Engineering :: Information Analysis Classifier: Topic :: Scientific/Engineering :: Mathematics Classifier: Topic :: Scientific/Engineering :: Physics Classifier: Topic :: Scientific/Engineering :: Bio-Informatics Classifier: Topic :: Software Development :: Libraries :: Python Modules python-igraph-0.7.1.post6/python_igraph.egg-info/SOURCES.txt0000644000076500000240000000423612534343010024221 0ustar ntamasstaff00000000000000COPYING MANIFEST.in setup.py igraph/__init__.py igraph/clustering.py igraph/compat.py igraph/configuration.py igraph/cut.py igraph/datatypes.py igraph/formula.py igraph/layout.py igraph/matching.py igraph/statistics.py igraph/summary.py igraph/utils.py igraph/app/__init__.py igraph/app/shell.py igraph/drawing/__init__.py igraph/drawing/baseclasses.py igraph/drawing/colors.py igraph/drawing/coord.py igraph/drawing/edge.py igraph/drawing/graph.py igraph/drawing/metamagic.py igraph/drawing/shapes.py igraph/drawing/text.py igraph/drawing/utils.py igraph/drawing/vertex.py igraph/remote/__init__.py igraph/remote/gephi.py igraph/remote/nexus.py igraph/test/__init__.py igraph/test/atlas.py igraph/test/attributes.py igraph/test/basic.py igraph/test/bipartite.py igraph/test/cliques.py igraph/test/colortests.py igraph/test/conversion.py igraph/test/decomposition.py igraph/test/edgeseq.py igraph/test/flow.py igraph/test/foreign.py igraph/test/games.py igraph/test/generators.py igraph/test/homepage.py igraph/test/indexing.py igraph/test/isomorphism.py igraph/test/iterators.py igraph/test/layouts.py igraph/test/matching.py igraph/test/operators.py igraph/test/rng.py igraph/test/separators.py igraph/test/spectral.py igraph/test/structural.py igraph/test/utils.py igraph/test/vertexseq.py igraph/vendor/__init__.py igraph/vendor/texttable.py python_igraph.egg-info/PKG-INFO python_igraph.egg-info/SOURCES.txt python_igraph.egg-info/dependency_links.txt python_igraph.egg-info/top_level.txt scripts/epydoc-patched scripts/epydoc.cfg scripts/igraph scripts/mkdoc.sh src/arpackobject.c src/arpackobject.h src/attributes.c src/attributes.h src/bfsiter.c src/bfsiter.h src/common.c src/common.h src/convert.c src/convert.h src/edgeobject.c src/edgeobject.h src/edgeseqobject.c src/edgeseqobject.h src/error.c src/error.h src/filehandle.c src/filehandle.h src/graphobject.c src/graphobject.h src/igraphmodule.c src/igraphmodule_api.h src/indexing.c src/indexing.h src/platform.h src/py2compat.c src/py2compat.h src/pyhelpers.c src/pyhelpers.h src/random.c src/random.h src/vertexobject.c src/vertexobject.h src/vertexseqobject.c src/vertexseqobject.h test/cytoscape_test.py test/doctests.py test/unittests.pypython-igraph-0.7.1.post6/python_igraph.egg-info/top_level.txt0000644000076500000240000000000712534343010025057 0ustar ntamasstaff00000000000000igraph python-igraph-0.7.1.post6/scripts/0000755000076500000240000000000012534343010017452 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/scripts/epydoc-patched0000755000076500000240000000112612453614202022275 0ustar ntamasstaff00000000000000#!/usr/bin/env python """Patched version of Epydoc that does not blow up with `docutils` newer than 0.6 when reST is used as a markup language""" from epydoc.cli import cli from epydoc.markup.restructuredtext import parse_docstring from epydoc.docwriter.latex import LatexWriter # Check whether Epydoc needs patching doc = parse_docstring("aaa", []) try: doc.summary() except AttributeError: # Monkey-patching docutils so that Text nodes have a "data" property, # which is always empty from docutils.nodes import Text Text.data="" LatexWriter.PREAMBLE += [r'\usepackage[T1]{fontenc}'] cli()python-igraph-0.7.1.post6/scripts/epydoc.cfg0000644000076500000240000000031112476153444021430 0ustar ntamasstaff00000000000000[epydoc] name: igraph library url: http://igraph.org modules: igraph, igraph.app, igraph.app.shell, igraph.statistics exclude: igraph.compat, igraph.formula, igraph.test, igraph.vendor imports: yes python-igraph-0.7.1.post6/scripts/igraph0000755000076500000240000000017712453614202020663 0ustar ntamasstaff00000000000000#!/usr/bin/env python """Small script to execute the igraph command line interface""" from igraph.app.shell import main main() python-igraph-0.7.1.post6/scripts/mkdoc.sh0000755000076500000240000000273012465507034021123 0ustar ntamasstaff00000000000000#!/bin/sh # # Creates documentation for igraph's Python interface using epydoc # # Usage: ./mkdoc.sh [--sync] [directory] SCRIPTS_FOLDER=`dirname $0` cd ${SCRIPTS_FOLDER}/.. ROOT_FOLDER=`pwd` DOC_API_FOLDER=${ROOT_FOLDER}/doc/api CONFIG=${ROOT_FOLDER}/scripts/epydoc.cfg cd ${ROOT_FOLDER} mkdir -p ${DOC_API_FOLDER}/pdf mkdir -p ${DOC_API_FOLDER}/html EPYDOC="${ROOT_FOLDER}/scripts/epydoc-patched" python -m epydoc.__init__ if [ $? -gt 0 ]; then echo "Epydoc not installed, exiting..." exit 1 fi PWD=`pwd` SYNC=0 if [ x$1 = x--sync ]; then SYNC=1 shift fi if [ x$1 != x ]; then cd $1 || exit 1 fi echo "Checking symlinked _igraph.so in ${ROOT_FOLDER}/igraph..." if [ ! -e ${ROOT_FOLDER}/igraph/_igraph.so -o ! -L ${ROOT_FOLDER}/igraph/_igraph.so ]; then rm -f ${ROOT_FOLDER}/igraph/_igraph.so cd ${ROOT_FOLDER}/igraph ln -s ../build/lib*/igraph/_igraph.so . cd ${ROOT_FOLDER} fi echo "Removing existing documentation..." rm -rf html echo "Generating HTML documentation..." ${EPYDOC} --html -v -o ${DOC_API_FOLDER}/html --config ${CONFIG} PDF=0 which latex >/dev/null && PDF=1 if [ $PDF -eq 1 ]; then echo "Generating PDF documentation..." ${EPYDOC} --pdf -v -o ${DOC_API_FOLDER}/pdf --config ${CONFIG} fi if [ $SYNC -eq 1 ]; then echo "Syncing documentation to web" cp ${DOC_API_FOLDER}/pdf/igraph.pdf ${DOC_API_FOLDER}/html rsync --delete -avz ${DOC_API_FOLDER}/html/ csardi@igraph.org:2222:www/doc/python/ rm ${DOC_API_FOLDER}/html/igraph.pdf fi cd "$PWD" python-igraph-0.7.1.post6/setup.cfg0000644000076500000240000000007312534343010017604 0ustar ntamasstaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 python-igraph-0.7.1.post6/setup.py0000644000076500000240000010444012534342745017516 0ustar ntamasstaff00000000000000#!/usr/bin/env python import os import sys ########################################################################### # Global version number. Keep the format of the next line intact. VERSION = '0.7.1.post6' # Check Python's version info and exit early if it is too old if sys.version_info < (2, 5): print("This module requires Python >= 2.5") sys.exit(0) ########################################################################### ## Here be ugly workarounds. These must be run before setuptools ## is imported. class Workaround(object): """Base class for platform-specific workarounds and hacks that are needed to get the Python interface compile with as little user intervention as possible.""" def required(self): """Returns ``True`` if the workaround is required on the platform of the user and ``False`` otherwise.""" raise NotImplementedError def hack(self): """Installs the workaround. This method will get called if and only if the ``required()`` method returns ``True``. """ raise NotImplementedError def update_buildcfg(self, cfg): """Allows the workaround to update the build configuration of the igraph extension. This method will get called if and only if the ``required()`` method returns ``True``. """ pass def _extend_compiler_customization(self, func): """Helper function that extends ``distutils.sysconfig.customize_compiler`` and ``setuptools.command.build_ext.customize_compiler`` with new, user-defined code at the end.""" from distutils import sysconfig old_func = sysconfig.customize_compiler def replaced(*args, **kwds): old_func(*args, **kwds) return func(*args, **kwds) self._replace_compiler_customization_distutils(replaced) self._replace_compiler_customization_setuptools(replaced) def _replace_compiler_customization_distutils(self, new_func): from distutils import ccompiler, sysconfig sysconfig.customize_compiler = new_func ccompiler.customize_compiler = new_func def _replace_compiler_customization_setuptools(self, new_func): if "setuptools.command.build_ext" in sys.modules: sys.modules["setuptools.command.build_ext"].customize_compiler = new_func class OSXClangAndSystemPythonWorkaround(Workaround): """Removes ``-mno-fused-madd`` from the arguments used to compile Python extensions if the user is running OS X.""" @staticmethod def remove_compiler_args(compiler): while "-mno-fused-madd" in compiler.compiler: compiler.compiler.remove("-mno-fused-madd") while "-mno-fused-madd" in compiler.compiler_so: compiler.compiler_so.remove("-mno-fused-madd") while "-mno-fused-madd" in compiler.linker_so: compiler.linker_so.remove("-mno-fused-madd") def required(self): return sys.platform.startswith("darwin") def hack(self): self._extend_compiler_customization(self.remove_compiler_args) class OSXAnacondaPythonIconvWorkaround(Workaround): """Anaconda Python contains a file named libxml2.la which refers to /usr/lib/libiconv.la -- but such a file does not exist in OS X. This hack ensures that we link to libxml2 from OS X itself and not from Anaconda Python (after all, this is what would have happened if the C core of igraph was compiled independently).""" def required(self): from distutils.spawn import find_executable if not sys.platform.startswith("darwin"): return False if "Anaconda" not in sys.version: return False self.xml2_config_path = find_executable("xml2-config") if not self.xml2_config_path: return False xml2_config_path_abs = os.path.abspath(self.xml2_config_path) return xml2_config_path_abs.startswith(os.path.abspath(sys.prefix)) def hack(self): path = os.environ["PATH"].split(os.pathsep) dir_to_remove = os.path.dirname(self.xml2_config_path) if dir_to_remove in path: path.remove(dir_to_remove) os.environ["PATH"] = os.pathsep.join(path) def update_buildcfg(self, cfg): anaconda_libdir = os.path.join(sys.prefix, "lib") cfg.extra_link_args.append("-Wl,-rpath,%s" % anaconda_libdir) cfg.post_build_hooks.append(self.fix_install_name) def fix_install_name(self, cfg): """Fixes the install name of the libxml2 library in _igraph.so to ensure that it loads libxml2 from Anaconda Python.""" for outputfile in cfg.get_outputs(): lines, retcode = get_output(["otool", "-L", outputfile]) if retcode: raise OSError("otool -L %s failed with error code: %s" % (outputfile, retcode)) for line in lines.split("\n"): if "libxml2" in line: libname = line.strip().split(" ")[0] subprocess.call(["install_name_tool", "-change", libname, "@rpath/" + os.path.basename(libname), outputfile]) class WorkaroundSet(object): def __init__(self, workaround_classes): self.each = [cls() for cls in workaround_classes] self.executed = [] def execute(self): for workaround in self.each: if workaround.required(): workaround.hack() self.executed.append(workaround) workarounds = WorkaroundSet([ OSXClangAndSystemPythonWorkaround, OSXAnacondaPythonIconvWorkaround ]) workarounds.execute() ########################################################################### try: from setuptools import setup from setuptools.command.build_ext import build_ext build_py = None except ImportError: from distutils.core import setup try: from distutils.command.build_py import build_py_2to3 as build_py except ImportError: from distutils.command.build_py import build_py import atexit import distutils.ccompiler import glob import shutil import subprocess import sys import tarfile import tempfile from select import select try: from urllib import urlretrieve from urllib2 import Request, urlopen, URLError except: # Maybe Python 3? from urllib.request import Request, urlopen, urlretrieve from urllib.error import URLError from distutils.core import Extension from distutils.util import get_platform ########################################################################### LIBIGRAPH_FALLBACK_INCLUDE_DIRS = ['/usr/include/igraph', '/usr/local/include/igraph'] LIBIGRAPH_FALLBACK_LIBRARIES = ['igraph'] LIBIGRAPH_FALLBACK_LIBRARY_DIRS = [] ########################################################################### def cleanup_tmpdir(dirname): """Removes the given temporary directory if it exists.""" if dirname is not None and os.path.exists(dirname): shutil.rmtree(dirname) def create_dir_unless_exists(*args): """Creates a directory unless it exists already.""" path = os.path.join(*args) if not os.path.isdir(path): os.makedirs(path) def ensure_dir_does_not_exist(*args): """Ensures that the given directory does not exist.""" path = os.path.join(*args) if os.path.isdir(path): shutil.rmtree(path) def exclude_from_list(items, items_to_exclude): """Excludes certain items from a list, keeping the original order of the remaining items.""" itemset = set(items_to_exclude) return [item for item in items if item not in itemset] def find_static_library(library_name, library_path): """Given the raw name of a library in `library_name`, tries to find a static library with this name in the given `library_path`. `library_path` is automatically extended with common library directories on Linux and Mac OS X.""" variants = ["lib{0}.a", "{0}.a", "{0}.lib", "lib{0}.lib"] if is_unix_like(): extra_libdirs = ["/usr/local/lib64", "/usr/local/lib", "/usr/lib64", "/usr/lib", "/lib64", "/lib"] else: extra_libdirs = [] for path in extra_libdirs: if path not in library_path and os.path.isdir(path): library_path.append(path) for path in library_path: for variant in variants: full_path = os.path.join(path, variant.format(library_name)) if os.path.isfile(full_path): return full_path def find_temporary_directory(): """Finds a suitable temporary directory where the installer can download the C core of igraph if needed and returns its full path.""" script_file = sys.modules[__name__].__file__ if not script_file.endswith("setup.py"): # We are probably running within an easy_install sandbox. Luckily this # provides a temporary directory for us so we can use that result = tempfile.gettempdir() else: # Use a temporary directory next to setup.py. We cannot blindly use # the default (given by tempfile.tempdir) because it might be on a # RAM disk that has not enough space result = os.path.join(os.path.dirname(script_file), "tmp") return os.path.abspath(result) def first(iterable): """Returns the first element from the given iterable.""" for item in iterable: return item raise ValueError("iterable is empty") def get_output(args, encoding="utf-8"): """Returns the output of a command returning a single line of output.""" PIPE = subprocess.PIPE try: p = subprocess.Popen(args, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() returncode = p.returncode except OSError: stdout, stderr = None, None returncode = 77 if encoding and type(stdout).__name__ == "bytes": stdout = str(stdout, encoding=encoding) if encoding and type(stderr).__name__ == "bytes": stderr = str(stderr, encoding=encoding) return stdout, returncode def get_output_single_line(args, encoding="utf-8"): """Returns the output of a command returning a single line of output, stripped from any trailing newlines.""" stdout, returncode = get_output(args, encoding=encoding) if stdout is not None: line, _, _ = stdout.partition("\n") else: line = None return line, returncode def http_url_exists(url): """Returns whether the given HTTP URL 'exists' in the sense that it is returning an HTTP error code or not. A URL is considered to exist if it does not return an HTTP error code.""" class HEADRequest(Request): def get_method(self): return "HEAD" try: response = urlopen(HEADRequest(url)) return True except URLError: return False def is_unix_like(platform=None): """Returns whether the given platform is a Unix-like platform with the usual Unix filesystem. When the parameter is omitted, it defaults to ``sys.platform`` """ platform = platform or sys.platform platform = platform.lower() return platform.startswith("linux") or platform.startswith("darwin") or \ platform.startswith("cygwin") def preprocess_fallback_config(): """Preprocesses the fallback include and library paths depending on the platform.""" global LIBIGRAPH_FALLBACK_INCLUDE_DIRS global LIBIGRAPH_FALLBACK_LIBRARY_DIRS global LIBIGRAPH_FALLBACK_LIBRARIES if os.name == 'nt' and distutils.ccompiler.get_default_compiler() == 'msvc': # if this setup is run in the source checkout *and* the igraph msvc was build, # this code adds the right library and include dir all_msvc_dirs = glob.glob(os.path.join('..', '..', 'igraph-*-msvc')) if len(all_msvc_dirs) > 0: if len(all_msvc_dirs) > 1: print("More than one MSVC build directory (..\\..\\igraph-*-msvc) found!") print("It could happen that setup.py uses the wrong one! Please remove all but the right one!\n\n") msvc_builddir = all_msvc_dirs[-1] if not os.path.exists(os.path.join(msvc_builddir, "Release")): print("There is no 'Release' dir in the MSVC build directory\n(%s)" % msvc_builddir) print("Please build the MSVC build first!\n") else: print("Using MSVC build dir as a fallback: %s\n\n" % msvc_builddir) LIBIGRAPH_FALLBACK_INCLUDE_DIRS = [os.path.join(msvc_builddir, "include")] LIBIGRAPH_FALLBACK_LIBRARY_DIRS = [os.path.join(msvc_builddir, "Release")] def version_variants(version): """Given an igraph version number, returns a list of possible version number variants to try when looking for a suitable nightly build of the C core to download from igraph.org.""" result = [version] # Strip any release tags version, _, _ = version.partition(".post") result.append(version) # Add trailing ".0" as needed to ensure that we have at least # major.minor.patch parts = version.split(".") while len(parts) < 3: parts.append("0") result.append(".".join(parts)) return result ########################################################################### class IgraphCCoreBuilder(object): """Class responsible for downloading and building the C core of igraph if it is not installed yet.""" def __init__(self, versions_to_try, remote_url=None, show_progress_bar=True, tmproot=None): self.versions_to_try = versions_to_try self.remote_url = remote_url self.show_progress_bar = show_progress_bar self.tmproot = tmproot self._tmpdir = None if self.tmproot is None: self.tmproot = find_temporary_directory() @property def tmpdir(self): """The temporary directory in which igraph is downloaded and extracted.""" if self._tmpdir is None: create_dir_unless_exists(self.tmproot) self._tmpdir = tempfile.mkdtemp(prefix="igraph.", dir=self.tmproot) atexit.register(cleanup_tmpdir, self._tmpdir) return self._tmpdir def download_and_compile(self): """Downloads and compiles the C core of igraph.""" def _progress_hook(count, block_size, total_size): if total_size < 0: sys.stdout.write("\rDownloading %s... please wait." % local_file) else: percentage = count * block_size * 100.0 / total_size percentage = min(percentage, 100.0) sys.stdout.write("\rDownloading %s... %.2f%%" % (local_file, percentage)) sys.stdout.flush() # Determine the remote URL if needed if self.remote_url is None: self.version, remote_url = self.find_first_version() if not self.version: print("Version %s of the C core of igraph is not found among the " "nightly builds." % self.versions_to_try[0]) print("Use the --c-core-version switch to try a different version.") print("") return False local_file = "igraph-%s.tar.gz" % self.version else: remote_url = self.remote_url local_file = remote_url.rsplit("/", 1)[1] print("Using temporary directory: %s" % self.tmpdir) # Now determine the full path where the C core will be downloaded local_file_full_path = os.path.join(self.tmpdir, local_file) # Download the C core if self.show_progress_bar: urlretrieve(remote_url, local_file_full_path, reporthook=_progress_hook) print("") else: print("Downloading %s... " % local_file) urlretrieve(remote_url, local_file_full_path) # Extract it in the temporary directory print("Extracting %s..." % local_file) archive = tarfile.open(local_file_full_path, "r:gz") archive.extractall(self.tmpdir) # Determine the name of the build directory self.builddir = None for name in os.listdir(self.tmpdir): full_path = os.path.join(self.tmpdir, name) if name.startswith("igraph") and os.path.isdir(full_path): self.builddir = full_path break if not self.builddir: print("Downloaded tarball did not contain a directory whose name " "started with igraph; giving up build.") return False # Patch ltmain.sh so it does not freak out on OS X when the build # directory contains spaces infile = os.path.join(self.builddir, "ltmain.sh") outfile = os.path.join(self.builddir, "ltmain.sh.new") with open(infile) as infp: with open(outfile, "w") as outfp: for line in infp: if line.endswith("cd $darwin_orig_dir\n"): line = line.replace("cd $darwin_orig_dir\n", "cd \"$darwin_orig_dir\"\n") outfp.write(line) os.rename(outfile, infile) # Try to compile cwd = os.getcwd() try: print("Configuring igraph...") os.chdir(self.builddir) retcode = subprocess.call("CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --disable-tls --disable-gmp", shell=True) if retcode: return False retcode = subprocess.call("make", shell=True) if retcode: return False libraries = [] for line in open(os.path.join(self.builddir, "igraph.pc")): if line.startswith("Libs: ") or line.startswith("Libs.private: "): words = line.strip().split() libraries.extend(word[2:] for word in words if word.startswith("-l")) if not libraries: # Educated guess libraries = ["igraph"] finally: os.chdir(cwd) # Compilation succeeded; copy everything into igraphcore create_dir_unless_exists("igraphcore") ensure_dir_does_not_exist("igraphcore", "include") ensure_dir_does_not_exist("igraphcore", "lib") shutil.copytree(os.path.join(self.builddir, "include"), os.path.join("igraphcore", "include")) shutil.copytree(os.path.join(self.builddir, "src", ".libs"), os.path.join("igraphcore", "lib")) f = open(os.path.join("igraphcore", "build.cfg"), "w") f.write(repr(libraries)) f.close() return True def find_first_version(self): """Finds the first version of igraph that exists in the nightly build repo from the version numbers provided in ``self.versions_to_try``.""" for version in self.versions_to_try: remote_url = self.get_download_url(version=version) if http_url_exists(remote_url): return version, remote_url return None, None def get_download_url(self, version): return "http://igraph.org/nightly/get/c/igraph-%s.tar.gz" % version def run(self): return self.download_and_compile() class BuildConfiguration(object): def __init__(self): global VERSION self.c_core_versions = version_variants(VERSION) self.c_core_url = None self.include_dirs = [] self.library_dirs = [] self.runtime_library_dirs = [] self.libraries = [] self.extra_compile_args = [] self.extra_link_args = [] self.extra_objects = [] self.show_progress_bar = True self.static_extension = False self.download_igraph_if_needed = True self.use_pkgconfig = True self._has_pkgconfig = None self.excluded_include_dirs = [] self.excluded_library_dirs = [] self.pre_build_hooks = [] self.post_build_hooks = [] self.wait = True @property def has_pkgconfig(self): """Returns whether ``pkg-config`` is available on the current system and it knows about igraph or not.""" if self._has_pkgconfig is None: if self.use_pkgconfig: line, exit_code = get_output_single_line(["pkg-config", "igraph"]) self._has_pkgconfig = (exit_code == 0) else: self._has_pkgconfig = False return self._has_pkgconfig @property def build_ext(self): """Returns a class that can be used as a replacement for the ``build_ext`` command in ``distutils`` and that will download and compile the C core of igraph if needed.""" try: from setuptools.command.build_ext import build_ext except ImportError: from distutils.command.build_ext import build_ext buildcfg = self class custom_build_ext(build_ext): def run(self): # Print a warning if pkg-config is not available or does not know about igraph if buildcfg.use_pkgconfig: detected = buildcfg.detect_from_pkgconfig() else: detected = False # Check whether we have already compiled igraph in a previous run. # If so, it should be found in igraphcore/include and # igraphcore/lib if os.path.exists("igraphcore"): buildcfg.use_built_igraph() detected = True # Download and compile igraph if the user did not disable it and # we do not know the libraries from pkg-config yet if not detected: if buildcfg.download_igraph_if_needed and is_unix_like(): detected = buildcfg.download_and_compile_igraph() if detected: buildcfg.use_built_igraph() else: sys.exit(1) # Fall back to an educated guess if everything else failed if not detected: buildcfg.use_educated_guess() # Replaces library names with full paths to static libraries # where possible. libm.a is excluded because it caused problems # on Sabayon Linux where libm.a is probably not compiled with # -fPIC if buildcfg.static_extension: buildcfg.replace_static_libraries(exclusions=["m"]) # Prints basic build information buildcfg.print_build_info() ext = first(extension for extension in self.extensions if extension.name == "igraph._igraph") buildcfg.configure(ext) # Run any pre-build hooks for hook in buildcfg.pre_build_hooks: hook(self) # Run the original build_ext command build_ext.run(self) # Run any post-build hooks for hook in buildcfg.post_build_hooks: hook(self) return custom_build_ext def configure(self, ext): """Configures the given Extension object using this build configuration.""" ext.include_dirs = exclude_from_list(self.include_dirs, self.excluded_include_dirs) ext.library_dirs = exclude_from_list(self.library_dirs, self.excluded_library_dirs) ext.runtime_library_dirs = self.runtime_library_dirs ext.libraries = self.libraries ext.extra_compile_args = self.extra_compile_args ext.extra_link_args = self.extra_link_args ext.extra_objects = self.extra_objects def detect_from_pkgconfig(self): """Detects the igraph include directory, library directory and the list of libraries to link to using ``pkg-config``.""" if not buildcfg.has_pkgconfig: print("Cannot find the C core of igraph on this system using pkg-config.") return False cmd = ["pkg-config", "igraph", "--cflags", "--libs"] if self.static_extension: cmd += ["--static"] line, exit_code = get_output_single_line(cmd) if exit_code > 0 or len(line) == 0: return False opts = line.strip().split() self.libraries = [opt[2:] for opt in opts if opt.startswith("-l")] self.library_dirs = [opt[2:] for opt in opts if opt.startswith("-L")] self.include_dirs = [opt[2:] for opt in opts if opt.startswith("-I")] return True def download_and_compile_igraph(self): """Downloads and compiles the C core of igraph.""" print("We will now try to download and compile the C core from scratch.") print("Version number of the C core: %s" % self.c_core_versions[0]) if len(self.c_core_versions) > 1: print("We will also try: %s" % ", ".join(self.c_core_versions[1:])) print("") igraph_builder = IgraphCCoreBuilder(self.c_core_versions, self.c_core_url, show_progress_bar=self.show_progress_bar) if not igraph_builder.run(): print("Could not download and compile the C core of igraph.") print("") return False else: return True def print_build_info(self): """Prints the include and library path being used for debugging purposes.""" if self.static_extension: build_type = "static extension" else: build_type = "dynamic extension" print("Build type: %s" % build_type) print("Include path: %s" % " ".join(self.include_dirs)) if self.excluded_include_dirs: print(" - excluding: %s" % " ".join(self.excluded_include_dirs)) print("Library path: %s" % " ".join(self.library_dirs)) if self.excluded_library_dirs: print(" - excluding: %s" % " ".join(self.excluded_library_dirs)) print("Runtime library path: %s" % " ".join(self.runtime_library_dirs)) print("Linked dynamic libraries: %s" % " ".join(self.libraries)) print("Linked static libraries: %s" % " ".join(self.extra_objects)) print("Extra compiler options: %s" % " ".join(self.extra_compile_args)) print("Extra linker options: %s" % " ".join(self.extra_link_args)) def process_args_from_command_line(self): """Preprocesses the command line options before they are passed to setup.py and sets up the build configuration.""" # Yes, this is ugly, but we don't want to interfere with setup.py's own # option handling opts_to_remove = [] for idx, option in enumerate(sys.argv): if not option.startswith("--"): continue if option == "--static": opts_to_remove.append(idx) self.static_extension = True elif option == "--no-download": opts_to_remove.append(idx) self.download_igraph_if_needed = False elif option == "--no-pkg-config": opts_to_remove.append(idx) self.use_pkgconfig = False elif option == "--no-progress-bar": opts_to_remove.append(idx) self.show_progress_bar = False elif option == "--no-wait": opts_to_remove.append(idx) self.wait = False elif option.startswith("--c-core-version"): opts_to_remove.append(idx) if option == "--c-core-version": value = sys.argv[idx+1] opts_to_remove.append(idx+1) else: value = option.split("=", 1)[1] self.c_core_versions = [value] elif option.startswith("--c-core-url"): opts_to_remove.append(idx) if option == "--c-core-url": value = sys.argv[idx+1] opts_to_remove.append(idx+1) else: value = option.split("=", 1)[1] self.c_core_url = value for idx in reversed(opts_to_remove): sys.argv[idx:(idx+1)] = [] def replace_static_libraries(self, exclusions=None): """Replaces references to libraries with full paths to their static versions if the static version is to be found on the library path.""" if "stdc++" not in self.libraries: self.libraries.append("stdc++") if exclusions is None: exclusions = [] for library_name in set(self.libraries) - set(exclusions): static_lib = find_static_library(library_name, self.library_dirs) if static_lib: self.libraries.remove(library_name) self.extra_objects.append(static_lib) def use_built_igraph(self): """Assumes that igraph is built already in ``igraphcore`` and sets up the include and library paths and the library names accordingly.""" buildcfg.include_dirs = [os.path.join("igraphcore", "include")] buildcfg.library_dirs = [os.path.join("igraphcore", "lib")] buildcfg.static_extension = True buildcfg_file = os.path.join("igraphcore", "build.cfg") if os.path.exists(buildcfg_file): buildcfg.libraries = eval(open(buildcfg_file).read()) def use_educated_guess(self): """Tries to guess the proper library names, include and library paths if everything else failed.""" preprocess_fallback_config() global LIBIGRAPH_FALLBACK_LIBRARIES global LIBIGRAPH_FALLBACK_INCLUDE_DIRS global LIBIGRAPH_FALLBACK_LIBRARY_DIRS print("WARNING: we were not able to detect where igraph is installed on") print("your machine (if it is installed at all). We will use the fallback") print("library and include pathss hardcoded in setup.py and hope that the") print("C core of igraph is installed there.") print("") print("If the compilation fails and you are sure that igraph is installed") print("on your machine, adjust the following two variables in setup.py") print("accordingly and try again:") print("") print("- LIBIGRAPH_FALLBACK_INCLUDE_DIRS") print("- LIBIGRAPH_FALLBACK_LIBRARY_DIRS") print("") seconds_remaining = 10 if self.wait else 0 while seconds_remaining > 0: if seconds_remaining > 1: plural = "s" else: plural = "" sys.stdout.write("\rContinuing in %2d second%s; press Enter to continue " "immediately. " % (seconds_remaining, plural)) sys.stdout.flush() rlist, _, _ = select([sys.stdin], [], [], 1) if rlist: sys.stdin.readline() break seconds_remaining -= 1 sys.stdout.write("\r" + " "*65 + "\r") self.libraries = LIBIGRAPH_FALLBACK_LIBRARIES[:] if self.static_extension: self.libraries.extend(["xml2", "z", "m", "stdc++"]) self.include_dirs = LIBIGRAPH_FALLBACK_INCLUDE_DIRS[:] self.library_dirs = LIBIGRAPH_FALLBACK_LIBRARY_DIRS[:] ########################################################################### # Process command line options buildcfg = BuildConfiguration() buildcfg.process_args_from_command_line() for workaround in workarounds.executed: workaround.update_buildcfg(buildcfg) # Define the extension sources=glob.glob(os.path.join('src', '*.c')) igraph_extension = Extension('igraph._igraph', sources) # library_dirs=library_dirs, # libraries=libraries, # include_dirs=include_dirs, # extra_objects=extra_objects, # extra_link_args=extra_link_args description = """Python interface to the igraph high performance graph library, primarily aimed at complex network research and analysis. Graph plotting functionality is provided by the Cairo library, so make sure you install the Python bindings of Cairo if you want to generate publication-quality graph plots. See the `Cairo homepage `_ for details. From release 0.5, the C core of the igraph library is **not** included in the Python distribution - you must compile and install the C core separately. Windows installers already contain a compiled igraph DLL, so they should work out of the box. Linux users should refer to the `igraph homepage `_ for compilation instructions (but check your distribution first, maybe there are pre-compiled packages available). OS X users may benefit from the disk images in the Python Package Index. Unofficial installers for 64-bit Windows machines and/or different Python versions can also be found `here `_. Many thanks to the maintainers of this page! """ options = dict( name = 'python-igraph', version = VERSION, url = 'http://pypi.python.org/pypi/python-igraph', description = 'High performance graph data structures and algorithms', long_description = description, license = 'GNU General Public License (GPL)', author = 'Tamas Nepusz', author_email = 'tamas@cs.rhul.ac.uk', ext_modules = [igraph_extension], package_dir = {'igraph': 'igraph'}, packages = ['igraph', 'igraph.test', 'igraph.app', 'igraph.drawing', 'igraph.remote', 'igraph.vendor'], scripts = ['scripts/igraph'], test_suite = "igraph.test.suite", headers = ['src/igraphmodule_api.h'], platforms = 'ALL', keywords = ['graph', 'network', 'mathematics', 'math', 'graph theory', 'discrete mathematics'], classifiers = [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'Operating System :: OS Independent', 'Programming Language :: C', 'Programming Language :: Python', 'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering :: Information Analysis', 'Topic :: Scientific/Engineering :: Mathematics', 'Topic :: Scientific/Engineering :: Physics', 'Topic :: Scientific/Engineering :: Bio-Informatics', 'Topic :: Software Development :: Libraries :: Python Modules' ], cmdclass = { "build_ext": buildcfg.build_ext } ) if "macosx" in get_platform() and "bdist_mpkg" in sys.argv: # OS X specific stuff to build the .mpkg installer options["data_files"] = [ \ ('/usr/local/lib', [os.path.join('..', 'igraph', 'fatbuild', 'libigraph.0.dylib')]) ] if sys.version_info > (3, 0): if build_py is None: options["use_2to3"] = True else: options["cmdclass"]["build_py"] = build_py setup(**options) python-igraph-0.7.1.post6/src/0000755000076500000240000000000012534343010016552 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/src/arpackobject.c0000644000076500000240000002575312453614202021366 0ustar ntamasstaff00000000000000/* vim:set ts=4 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2007-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "arpackobject.h" #include "graphobject.h" #include "error.h" #include "py2compat.h" PyObject* igraphmodule_arpack_options_default; /** * \ingroup python_interface_arpack * \brief Checks if the object is an ARPACK parameter object */ int igraphmodule_ARPACKOptions_Check(PyObject *ob) { if (ob) return PyType_IsSubtype(ob->ob_type, &igraphmodule_ARPACKOptionsType); return 0; } /** * \ingroup python_interface_arpack * \brief Allocates a new ARPACK parameters object */ PyObject* igraphmodule_ARPACKOptions_new() { igraphmodule_ARPACKOptionsObject* self; self=PyObject_New(igraphmodule_ARPACKOptionsObject, &igraphmodule_ARPACKOptionsType); if (self) { igraph_arpack_options_init(&self->params); igraph_arpack_options_init(&self->params_out); } return (PyObject*)self; } /** * \ingroup python_interface_arpack * \brief Deallocates a Python representation of a given ARPACK parameters object */ void igraphmodule_ARPACKOptions_dealloc( igraphmodule_ARPACKOptionsObject* self) { /*igraph_arpack_options_destroy(&self->params);*/ PyObject_Del((PyObject*)self); } /** \ingroup python_interface_arpack * \brief Returns one of the attributes of a given ARPACK parameters object */ PyObject* igraphmodule_ARPACKOptions_getattr( igraphmodule_ARPACKOptionsObject* self, char* attrname) { PyObject *result = NULL; if (strcmp(attrname, "bmat") == 0) { char buf[2] = { self->params_out.bmat[0], 0 }; result=PyString_FromString(buf); } else if (strcmp(attrname, "n") == 0) { result=PyInt_FromLong(self->params_out.n); } else if (strcmp(attrname, "which") == 0) { char buf[3] = { self->params.which[0], self->params.which[1], 0 }; result=PyString_FromString(buf); } else if (strcmp(attrname, "nev") == 0) { result=PyInt_FromLong(self->params.nev); } else if (strcmp(attrname, "tol") == 0) { result=PyFloat_FromDouble((double)self->params.tol); } else if (strcmp(attrname, "ncv") == 0) { result=PyInt_FromLong(self->params.ncv); } else if (strcmp(attrname, "ldv") == 0) { result=PyInt_FromLong(self->params.ldv); } else if (strcmp(attrname, "ishift") == 0) { result=PyInt_FromLong(self->params.ishift); } else if (strcmp(attrname, "maxiter") == 0 || strcmp(attrname, "mxiter") == 0) { result=PyInt_FromLong(self->params.mxiter); } else if (strcmp(attrname, "nb") == 0) { result=PyInt_FromLong(self->params.nb); } else if (strcmp(attrname, "mode") == 0) { result=PyInt_FromLong(self->params.mode); } else if (strcmp(attrname, "start") == 0) { result=PyInt_FromLong(self->params.start); } else if (strcmp(attrname, "sigma") == 0) { result=PyFloat_FromDouble((double)self->params.sigma); } else if (strcmp(attrname, "info") == 0) { result=PyInt_FromLong(self->params_out.info); } else if (strcmp(attrname, "iter") == 0) { result=PyInt_FromLong(self->params_out.iparam[2]); } else if (strcmp(attrname, "nconv") == 0) { result=PyInt_FromLong(self->params_out.iparam[4]); } else if (strcmp(attrname, "numop") == 0) { result=PyInt_FromLong(self->params_out.iparam[8]); } else if (strcmp(attrname, "numopb") == 0) { result=PyInt_FromLong(self->params_out.iparam[9]); } else if (strcmp(attrname, "numreo") == 0) { result=PyInt_FromLong(self->params_out.iparam[10]); } else { PyErr_SetString(PyExc_AttributeError, attrname); } return result; } /** \ingroup python_interface_arpack * \brief Sets one of the attributes of a given ARPACK parameters object */ int igraphmodule_ARPACKOptions_setattr( igraphmodule_ARPACKOptionsObject* self, char* attrname, PyObject* value) { if (value == 0) { PyErr_SetString(PyExc_TypeError, "attribute can not be deleted"); return -1; } if (strcmp(attrname, "maxiter") == 0 || strcmp(attrname, "mxiter") == 0) { if (PyInt_Check(value)) { long int n=PyInt_AsLong(value); if (n>0) self->params.mxiter=(igraph_integer_t)n; else { PyErr_SetString(PyExc_ValueError, "maxiter must be positive"); return -1; } } else { PyErr_SetString(PyExc_ValueError, "integer expected"); return -1; } } else if (strcmp(attrname, "tol") == 0) { if (PyInt_Check(value)) { self->params.tol = (igraph_real_t) PyInt_AsLong(value); } else if (PyFloat_Check(value)) { self->params.tol = (igraph_real_t) PyFloat_AsDouble(value); } else { PyErr_SetString(PyExc_ValueError, "integer or float expected"); return -1; } } else { PyErr_SetString(PyExc_AttributeError, attrname); return -1; } return 0; } /** \ingroup python_interface_arpack */ igraph_arpack_options_t *igraphmodule_ARPACKOptions_get( igraphmodule_ARPACKOptionsObject *self) { self->params_out = self->params; self->params_out.iparam[0] = self->params.ishift; self->params_out.iparam[2] = self->params.mxiter; self->params_out.iparam[3] = self->params.nb; self->params_out.iparam[6] = self->params.mode; self->params_out.lworkl = 0; self->params_out.info = self->params.start; return &self->params_out; } /** \ingroup python_interface_arpack * \brief Formats an \c igraph.ARPACKOptions object in a * human-consumable format. * * \return the formatted textual representation as a \c PyObject */ PyObject* igraphmodule_ARPACKOptions_str( igraphmodule_ARPACKOptionsObject *self) { PyObject *s; s=PyString_FromFormat("ARPACK parameters"); return s; } /** * \ingroup python_interface_arpack * Method table for the \c igraph.ARPACKOptions object */ PyMethodDef igraphmodule_ARPACKOptions_methods[] = { /*{"attributes", (PyCFunction)igraphmodule_Edge_attributes, METH_NOARGS, "attributes() -> list\n\n" "Returns the attribute list of the graph's edges\n" },*/ {NULL} }; /** * \ingroup python_interface_edge * Getter/setter table for the \c igraph.ARPACKOptions object */ PyGetSetDef igraphmodule_ARPACKOptions_getseters[] = { /*{"tuple", (getter)igraphmodule_Edge_get_tuple, NULL, "Source and target node index of this edge as a tuple", NULL },*/ {NULL} }; /** \ingroup python_interface_edge * Python type object referencing the methods Python calls when it performs * various operations on an ARPACK parameters object */ PyTypeObject igraphmodule_ARPACKOptionsType = { PyVarObject_HEAD_INIT(0, 0) "igraph.ARPACKOptions", /* tp_name */ sizeof(igraphmodule_ARPACKOptionsObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)igraphmodule_ARPACKOptions_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc)igraphmodule_ARPACKOptions_getattr, /* tp_getattr */ (setattrfunc)igraphmodule_ARPACKOptions_setattr, /* tp_setattr */ 0, /* tp_compare (2.x) / tp_reserved (3.x) */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ (reprfunc)igraphmodule_ARPACKOptions_str, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Class representing the parameters of the ARPACK module.\n\n" "ARPACK is a Fortran implementation of the implicitly restarted\n" "Arnoldi method, an algorithm for calculating some of the\n" "eigenvalues and eigenvectors of a given matrix. igraph uses this\n" "package occasionally, and this class can be used to fine-tune the\n" "behaviour of ARPACK in such cases.\n\n" "The class has several attributes which are not documented here,\n" "since they are usually of marginal use to the ordinary user.\n" "See the source code of the original ARPACK Fortran package\n" "(especially the file C{dsaupd.f}) for a detailed explanation of the\n" "parameters. Only the most basic attributes are explained here. Most\n" "of them are read only unless stated otherwise.\n\n" " - C{bmat}: type of the eigenproblem solved. C{'I'} means standard\n" " eigenproblem (A*x = lambda*x), C{'G'} means generalized\n" " eigenproblem (A*x = lambda*B*x).\n\n" " - C{n}: dimension of the eigenproblem\n\n" " - C{tol}: precision. If less than or equal to zero, the standard\n" " machine precision is used as computed by the LAPACK utility\n" " called C{dlamch}. This can be modified.\n\n" " - C{mxiter}: maximum number of update iterations to take. This\n" " can be modified. You can also use C{maxiter}.\n\n" " - C{iter}: actual number of update iterations taken\n\n" " - C{numop}: total number of OP*x operations\n\n" " - C{numopb}: total number of B*x operations if C{bmat} is C{'G'}\n\n" " - C{numreo}: total number of steps of re-orthogonalization\n\n" "", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ igraphmodule_ARPACKOptions_methods, /* tp_methods */ 0, /* tp_members */ igraphmodule_ARPACKOptions_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ (newfunc)igraphmodule_ARPACKOptions_new, /* tp_new */ 0, /* tp_free */ }; python-igraph-0.7.1.post6/src/arpackobject.h0000644000076500000240000000353712453614202021367 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_ARPACKOBJECT_H #define PYTHON_ARPACKOBJECT_H #include #include #include "graphobject.h" /** * \ingroup python_interface * \defgroup python_interface_arpack ARPACK parameters object */ extern PyTypeObject igraphmodule_ARPACKOptionsType; /** * \ingroup python_interface_arpack * \brief A structure representing ARPACK parameters */ typedef struct { PyObject_HEAD igraph_arpack_options_t params; igraph_arpack_options_t params_out; } igraphmodule_ARPACKOptionsObject; extern PyObject* igraphmodule_arpack_options_default; void igraphmodule_ARPACKOptions_dealloc(igraphmodule_ARPACKOptionsObject* self); PyObject* igraphmodule_ARPACKOptions_new(void); PyObject* igraphmodule_ARPACKOptions_str(igraphmodule_ARPACKOptionsObject *self); #define igraphmodule_ARPACKOptions_CheckExact(ob) ((ob)->ob_type == &igraphmodule_ARPACKOptionsType) igraph_arpack_options_t *igraphmodule_ARPACKOptions_get(igraphmodule_ARPACKOptionsObject *self); int igraphmodule_ARPACKOptions_Check(PyObject *ob); #endif python-igraph-0.7.1.post6/src/attributes.c0000644000076500000240000016353712460534506021135 0ustar ntamasstaff00000000000000/* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "attributes.h" #include "common.h" #include "convert.h" #include "py2compat.h" #include "pyhelpers.h" int igraphmodule_i_attribute_struct_init(igraphmodule_i_attribute_struct *attrs) { int i; for (i=0; i<3; i++) { attrs->attrs[i] = PyDict_New(); if (PyErr_Occurred()) return 1; RC_ALLOC("dict", attrs->attrs[i]); } attrs->vertex_name_index = 0; return 0; } void igraphmodule_i_attribute_struct_destroy(igraphmodule_i_attribute_struct *attrs) { int i; for (i=0; i<3; i++) { if (attrs->attrs[i]) { RC_DEALLOC("dict", attrs->attrs[i]); Py_DECREF(attrs->attrs[i]); } } if (attrs->vertex_name_index) { RC_DEALLOC("dict", attrs->vertex_name_index); Py_DECREF(attrs->vertex_name_index); } } int igraphmodule_i_attribute_struct_index_vertex_names( igraphmodule_i_attribute_struct *attrs, igraph_bool_t force) { Py_ssize_t n = 0; PyObject *name_list, *key, *value; if (attrs->vertex_name_index && !force) return 0; if (attrs->vertex_name_index == 0) { attrs->vertex_name_index = PyDict_New(); if (attrs->vertex_name_index == 0) { return 1; } } else PyDict_Clear(attrs->vertex_name_index); name_list = PyDict_GetItemString(attrs->attrs[1], "name"); if (name_list == 0) return 0; /* no name attribute */ n = PyList_Size(name_list) - 1; while (n >= 0) { key = PyList_GET_ITEM(name_list, n); /* we don't own a reference to key */ value = PyInt_FromLong(n); /* we do own a reference to value */ if (value == 0) return 1; PyDict_SetItem(attrs->vertex_name_index, key, value); /* PyDict_SetItem did an INCREF for both the key and a value, therefore we * have to drop our reference on value */ Py_DECREF(value); n--; } return 0; } void igraphmodule_i_attribute_struct_invalidate_vertex_name_index( igraphmodule_i_attribute_struct *attrs) { if (attrs->vertex_name_index == 0) return; Py_DECREF(attrs->vertex_name_index); attrs->vertex_name_index = 0; } void igraphmodule_invalidate_vertex_name_index(igraph_t *graph) { igraphmodule_i_attribute_struct_invalidate_vertex_name_index(ATTR_STRUCT(graph)); } void igraphmodule_index_vertex_names(igraph_t *graph, igraph_bool_t force) { igraphmodule_i_attribute_struct_index_vertex_names(ATTR_STRUCT(graph), force); } int igraphmodule_get_vertex_id_by_name(igraph_t *graph, PyObject* o, igraph_integer_t* vid) { igraphmodule_i_attribute_struct* attrs = ATTR_STRUCT(graph); PyObject* o_vid = NULL; int tmp; if (graph) { attrs = ATTR_STRUCT(graph); if (igraphmodule_i_attribute_struct_index_vertex_names(attrs, 0)) return 1; o_vid = PyDict_GetItem(attrs->vertex_name_index, o); } if (o_vid == NULL) { #ifdef IGRAPH_PYTHON3 PyErr_Format(PyExc_ValueError, "no such vertex: %R", o); #else PyObject* s = PyObject_Repr(o); if (s) { PyErr_Format(PyExc_ValueError, "no such vertex: %s", PyString_AS_STRING(s)); Py_DECREF(s); } else { PyErr_Format(PyExc_ValueError, "no such vertex: %p", o); } #endif return 1; } if (!PyInt_Check(o_vid)) { PyErr_SetString(PyExc_ValueError, "non-numeric vertex ID assigned to vertex name. This is most likely a bug."); return 1; } if (PyInt_AsInt(o_vid, &tmp)) return 1; *vid = tmp; return 0; } /** * \brief Checks whether the given graph has the given graph attribute. * * \param graph the graph * \param name the name of the attribute being searched for */ igraph_bool_t igraphmodule_has_graph_attribute(const igraph_t *graph, const char* name) { PyObject *dict = ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_GRAPH]; return name != 0 && dict != 0 && PyDict_GetItemString(dict, name) != 0; } /** * \brief Checks whether the given graph has the given vertex attribute. * * \param graph the graph * \param name the name of the attribute being searched for */ igraph_bool_t igraphmodule_has_vertex_attribute(const igraph_t *graph, const char* name) { PyObject *dict = ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_VERTEX]; return name != 0 && dict != 0 && PyDict_GetItemString(dict, name) != 0; } /** * \brief Checks whether the given graph has the given edge attribute. * * \param graph the graph * \param name the name of the attribute being searched for */ igraph_bool_t igraphmodule_has_edge_attribute(const igraph_t *graph, const char* name) { PyObject *dict = ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_EDGE]; return name != 0 && dict != 0 && PyDict_GetItemString(dict, name) != 0; } /** * \brief Creates a new edge attribute and sets the values to None. * * This returns the actual list that we use to store the edge attributes, so * be careful when modifying it - any modification will propagate back to the * graph itself. You have been warned. * * \param graph the graph * \param name the name of the attribute being created * \returns a Python list of the values or \c NULL if the given * attribute exists already (no exception set). The returned * reference is borrowed. */ PyObject* igraphmodule_create_edge_attribute(const igraph_t* graph, const char* name) { PyObject *dict = ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_EDGE]; PyObject *values; Py_ssize_t i, n; if (dict == 0) { dict = ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_EDGE] = PyDict_New(); } if (PyDict_GetItemString(dict, name)) return 0; n = igraph_ecount(graph); values = PyList_New(n); if (values == 0) return 0; for (i = 0; i < n; i++) { Py_INCREF(Py_None); PyList_SET_ITEM(values, i, Py_None); /* reference stolen */ } if (PyDict_SetItemString(dict, name, values)) { Py_DECREF(values); return 0; } Py_DECREF(values); return values; } /** * \brief Returns the values of the given edge attribute for all edges in the * given graph. * * This returns the actual list that we use to store the edge attributes, so * be careful when modifying it - any modification will propagate back to the * graph itself. You have been warned. * * \param graph the graph * \param name the name of the attribute being searched for * \returns a Python list or \c NULL if there is no such attribute * (no exception set). The returned reference is borrowed. */ PyObject* igraphmodule_get_edge_attribute_values(const igraph_t* graph, const char* name) { PyObject *dict = ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_EDGE]; if (dict == 0) return 0; return PyDict_GetItemString(dict, name); } /** * \brief Returns the values of the given edge attribute for all edges in the * given graph, optionally creating it if it does not exist. * * This returns the actual list that we use to store the edge attributes, so * be careful when modifying it - any modification will propagate back to the * graph itself. You have been warned. * * \param graph the graph * \param name the name of the attribute being searched for * \returns a Python list (borrowed reference) */ PyObject* igraphmodule_create_or_get_edge_attribute_values(const igraph_t* graph, const char* name) { PyObject *dict = ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_EDGE], *result; if (dict == 0) return 0; result = PyDict_GetItemString(dict, name); if (result != 0) return result; return igraphmodule_create_edge_attribute(graph, name); } /* Attribute handlers for the Python interface */ /* Initialization */ static int igraphmodule_i_attribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) { igraphmodule_i_attribute_struct* attrs; long int i, n; attrs=(igraphmodule_i_attribute_struct*)calloc(1, sizeof(igraphmodule_i_attribute_struct)); if (!attrs) IGRAPH_ERROR("not enough memory to allocate attribute hashes", IGRAPH_ENOMEM); if (igraphmodule_i_attribute_struct_init(attrs)) { PyErr_Clear(); free(attrs); IGRAPH_ERROR("not enough memory to allocate attribute hashes", IGRAPH_ENOMEM); } graph->attr=(void*)attrs; /* See if we have graph attributes */ if (attr) { PyObject *dict=attrs->attrs[0], *value; char *s; n = igraph_vector_ptr_size(attr); for (i=0; itype) { case IGRAPH_ATTRIBUTE_NUMERIC: value=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[0]); break; case IGRAPH_ATTRIBUTE_STRING: igraph_strvector_get((igraph_strvector_t*)attr_rec->value, 0, &s); if (s == 0) value=PyString_FromString(""); else value=PyString_FromString(s); break; case IGRAPH_ATTRIBUTE_BOOLEAN: value=VECTOR(*(igraph_vector_bool_t*)attr_rec->value)[0] ? Py_True : Py_False; Py_INCREF(value); break; default: IGRAPH_WARNING("unsupported attribute type (not string, numeric or Boolean)"); value=0; break; } if (value) { if (PyDict_SetItemString(dict, attr_rec->name, value)) { Py_DECREF(value); igraphmodule_i_attribute_struct_destroy(attrs); free(graph->attr); graph->attr = 0; IGRAPH_ERROR("failed to add attributes to graph attribute hash", IGRAPH_FAILURE); } Py_DECREF(value); value=0; } } } return IGRAPH_SUCCESS; } /* Destruction */ static void igraphmodule_i_attribute_destroy(igraph_t *graph) { igraphmodule_i_attribute_struct* attrs; /* printf("Destroying attribute table\n"); */ if (graph->attr) { attrs=(igraphmodule_i_attribute_struct*)graph->attr; igraphmodule_i_attribute_struct_destroy(attrs); free(attrs); } } /* Copying */ static int igraphmodule_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) { igraphmodule_i_attribute_struct *fromattrs, *toattrs; PyObject *key, *value, *newval, *o=NULL; igraph_bool_t copy_attrs[3] = { ga, va, ea }; int i, j; Py_ssize_t pos = 0; if (from->attr) { fromattrs=ATTR_STRUCT(from); /* what to do with the original value of toattrs? */ toattrs=(igraphmodule_i_attribute_struct*)calloc(1, sizeof(igraphmodule_i_attribute_struct)); if (!toattrs) IGRAPH_ERROR("not enough memory to allocate attribute hashes", IGRAPH_ENOMEM); if (igraphmodule_i_attribute_struct_init(toattrs)) { PyErr_Clear(); free(toattrs); IGRAPH_ERROR("not enough memory to allocate attribute hashes", IGRAPH_ENOMEM); } to->attr=toattrs; for (i=0; i<3; i++) { if (!copy_attrs[i]) continue; if (!PyDict_Check(fromattrs->attrs[i])) { toattrs->attrs[i]=fromattrs->attrs[i]; Py_XINCREF(fromattrs->attrs[i]); continue; } pos = 0; while (PyDict_Next(fromattrs->attrs[i], &pos, &key, &value)) { /* value is only borrowed, so copy it */ if (i>0) { newval=PyList_New(PyList_GET_SIZE(value)); for (j=0; jattrs[i], key, newval); Py_DECREF(newval); /* compensate for PyDict_SetItem */ } } } return IGRAPH_SUCCESS; } /* Adding vertices */ static int igraphmodule_i_attribute_add_vertices(igraph_t *graph, long int nv, igraph_vector_ptr_t *attr) { /* Extend the end of every value in the vertex hash with nv pieces of None */ PyObject *key, *value, *dict; long int i, j, k, l; igraph_attribute_record_t *attr_rec; igraph_bool_t *added_attrs=0; Py_ssize_t pos = 0; if (!graph->attr) return IGRAPH_SUCCESS; if (nv<0) return IGRAPH_SUCCESS; if (attr) { added_attrs = (igraph_bool_t*)calloc((size_t)igraph_vector_ptr_size(attr), sizeof(igraph_bool_t)); if (!added_attrs) IGRAPH_ERROR("can't add vertex attributes", IGRAPH_ENOMEM); IGRAPH_FINALLY(free, added_attrs); } dict=ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_VERTEX]; if (!PyDict_Check(dict)) IGRAPH_ERROR("vertex attribute hash type mismatch", IGRAPH_EINVAL); while (PyDict_Next(dict, &pos, &key, &value)) { if (!PyString_Check(key)) IGRAPH_ERROR("vertex attribute hash key is not a string", IGRAPH_EINVAL); if (!PyList_Check(value)) IGRAPH_ERROR("vertex attribute hash member is not a list", IGRAPH_EINVAL); /* Check if we have specific values for the given attribute */ attr_rec=0; if (attr) { j=igraph_vector_ptr_size(attr); for (i=0; iname)) { added_attrs[i]=1; break; } attr_rec=0; } } /* If we have specific values for the given attribute, attr_rec contains * the appropriate vector. If not, it is null. */ if (attr_rec) { for (i=0; itype) { case IGRAPH_ATTRIBUTE_NUMERIC: o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]); break; case IGRAPH_ATTRIBUTE_STRING: igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s); o=PyString_FromString(s); break; case IGRAPH_ATTRIBUTE_BOOLEAN: o=VECTOR(*(igraph_vector_bool_t*)attr_rec->value)[i] ? Py_True : Py_False; Py_INCREF(o); break; default: IGRAPH_WARNING("unsupported attribute type (not string, numeric or Boolean)"); o=0; break; } if (o) { if (PyList_Append(value, o) == -1) IGRAPH_ERROR("can't extend a vertex attribute hash member", IGRAPH_FAILURE); else Py_DECREF(o); } } /* Invalidate the vertex name index if needed */ if (!strcmp(attr_rec->name, "name")) igraphmodule_i_attribute_struct_invalidate_vertex_name_index(ATTR_STRUCT(graph)); } else { for (i=0; itype) { case IGRAPH_ATTRIBUTE_NUMERIC: o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]); break; case IGRAPH_ATTRIBUTE_STRING: igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s); o=PyString_FromString(s); break; case IGRAPH_ATTRIBUTE_BOOLEAN: o=VECTOR(*(igraph_vector_bool_t*)attr_rec->value)[i] ? Py_True : Py_False; Py_INCREF(o); break; default: IGRAPH_WARNING("unsupported attribute type (not string, numeric or Boolean)"); o=0; break; } if (o) PyList_SET_ITEM(value, i+j, o); } /* Invalidate the vertex name index if needed */ if (!strcmp(attr_rec->name, "name")) igraphmodule_i_attribute_struct_invalidate_vertex_name_index(ATTR_STRUCT(graph)); PyDict_SetItemString(dict, attr_rec->name, value); Py_DECREF(value); /* compensate for PyDict_SetItemString */ } free(added_attrs); IGRAPH_FINALLY_CLEAN(1); } return IGRAPH_SUCCESS; } /* Permuting vertices */ static int igraphmodule_i_attribute_permute_vertices(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_t *idx) { long int n, i; PyObject *key, *value, *dict, *newdict, *newlist, *o; Py_ssize_t pos=0; dict=ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_VERTEX]; if (!PyDict_Check(dict)) return 1; newdict=PyDict_New(); if (!newdict) return 1; n=igraph_vector_size(idx); pos=0; while (PyDict_Next(dict, &pos, &key, &value)) { newlist=PyList_New(n); for (i=0; iattr) return IGRAPH_SUCCESS; if (ne<0) return IGRAPH_SUCCESS; if (attr) { added_attrs = (igraph_bool_t*)calloc((size_t)igraph_vector_ptr_size(attr), sizeof(igraph_bool_t)); if (!added_attrs) IGRAPH_ERROR("can't add vertex attributes", IGRAPH_ENOMEM); IGRAPH_FINALLY(free, added_attrs); } dict=ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_EDGE]; if (!PyDict_Check(dict)) IGRAPH_ERROR("edge attribute hash type mismatch", IGRAPH_EINVAL); while (PyDict_Next(dict, &pos, &key, &value)) { if (!PyString_Check(key)) IGRAPH_ERROR("edge attribute hash key is not a string", IGRAPH_EINVAL); if (!PyList_Check(value)) IGRAPH_ERROR("edge attribute hash member is not a list", IGRAPH_EINVAL); /* Check if we have specific values for the given attribute */ attr_rec=0; if (attr) { j=igraph_vector_ptr_size(attr); for (i=0; iname)) { added_attrs[i]=1; break; } attr_rec=0; } } /* If we have specific values for the given attribute, attr_rec contains * the appropriate vector. If not, it is null. */ if (attr_rec) { for (i=0; itype) { case IGRAPH_ATTRIBUTE_NUMERIC: o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]); break; case IGRAPH_ATTRIBUTE_STRING: igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s); o=PyString_FromString(s); break; case IGRAPH_ATTRIBUTE_BOOLEAN: o=VECTOR(*(igraph_vector_bool_t*)attr_rec->value)[i] ? Py_True : Py_False; Py_INCREF(o); break; default: IGRAPH_WARNING("unsupported attribute type (not string, numeric or Boolean)"); o=0; break; } if (o) { if (PyList_Append(value, o) == -1) IGRAPH_ERROR("can't extend an edge attribute hash member", IGRAPH_FAILURE); else Py_DECREF(o); } } } else { for (i=0; itype) { case IGRAPH_ATTRIBUTE_NUMERIC: o=PyFloat_FromDouble((double)VECTOR(*(igraph_vector_t*)attr_rec->value)[i]); break; case IGRAPH_ATTRIBUTE_STRING: igraph_strvector_get((igraph_strvector_t*)attr_rec->value, i, &s); o=PyString_FromString(s); break; case IGRAPH_ATTRIBUTE_BOOLEAN: o=VECTOR(*(igraph_vector_bool_t*)attr_rec->value)[i] ? Py_True : Py_False; Py_INCREF(o); break; default: IGRAPH_WARNING("unsupported attribute type (not string, numeric or Boolean)"); o=0; break; } if (o) PyList_SET_ITEM(value, i+j, o); } PyDict_SetItemString(dict, attr_rec->name, value); Py_DECREF(value); /* compensate for PyDict_SetItemString */ } free(added_attrs); IGRAPH_FINALLY_CLEAN(1); } return IGRAPH_SUCCESS; } /* Deleting edges, currently unused */ /* static void igraphmodule_i_attribute_delete_edges(igraph_t *graph, const igraph_vector_t *idx) { long int n, i, ndeleted=0; PyObject *key, *value, *dict, *o; Py_ssize_t pos=0; dict=ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_EDGE]; if (!PyDict_Check(dict)) return; n=igraph_vector_size(idx); for (i=0; iname != 0) { free((char*)ptr->name); ptr++; } free(records); } /* Auxiliary function for the common parts of * igraphmodule_i_attribute_combine_vertices and * igraphmodule_i_attribute_combine_edges */ static int igraphmodule_i_attribute_combine_dicts(PyObject *dict, PyObject *newdict, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb) { PyObject *key, *value; Py_ssize_t pos; igraph_attribute_combination_record_t* todo; Py_ssize_t i, n; if (!PyDict_Check(dict) || !PyDict_Check(newdict)) return 1; /* Allocate memory for the attribute_combination_records */ n = PyDict_Size(dict); todo = (igraph_attribute_combination_record_t*)calloc( n+1, sizeof(igraph_attribute_combination_record_t) ); if (todo == 0) { IGRAPH_ERROR("cannot allocate memory for attribute combination", IGRAPH_ENOMEM); } for (i = 0; i < n+1; i++) todo[i].name = 0; /* sentinel elements */ IGRAPH_FINALLY(igraphmodule_i_free_attribute_combination_records, todo); /* Collect what to do for each attribute in the source dict */ pos = 0; i = 0; while (PyDict_Next(dict, &pos, &key, &value)) { todo[i].name = PyString_CopyAsString(key); if (todo[i].name == 0) IGRAPH_ERROR("PyString_CopyAsString failed", IGRAPH_FAILURE); igraph_attribute_combination_query(comb, todo[i].name, &todo[i].type, &todo[i].func); i++; } /* Combine the attributes. Here we make use of the fact that PyDict_Next * will iterate over the dict in the same order */ pos = 0; i = 0; while (PyDict_Next(dict, &pos, &key, &value)) { PyObject *empty_str; PyObject *func; PyObject *newvalue; /* Safety check */ if (!PyString_IsEqualToASCIIString(key, todo[i].name)) { IGRAPH_ERROR("PyDict_Next iteration order not consistent. " "This should never happen. Please report the bug to the igraph " "developers!", IGRAPH_FAILURE); } newvalue = 0; switch (todo[i].type) { case IGRAPH_ATTRIBUTE_COMBINE_DEFAULT: case IGRAPH_ATTRIBUTE_COMBINE_IGNORE: break; case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: func = (PyObject*)todo[i].func; newvalue = igraphmodule_i_ac_func(value, merges, func); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: newvalue = igraphmodule_i_ac_sum(value, merges); break; case IGRAPH_ATTRIBUTE_COMBINE_PROD: newvalue = igraphmodule_i_ac_prod(value, merges); break; case IGRAPH_ATTRIBUTE_COMBINE_MIN: newvalue = igraphmodule_i_ac_builtin_func(value, merges, "min"); break; case IGRAPH_ATTRIBUTE_COMBINE_MAX: newvalue = igraphmodule_i_ac_builtin_func(value, merges, "max"); break; case IGRAPH_ATTRIBUTE_COMBINE_RANDOM: newvalue = igraphmodule_i_ac_random(value, merges); break; case IGRAPH_ATTRIBUTE_COMBINE_FIRST: newvalue = igraphmodule_i_ac_first(value, merges); break; case IGRAPH_ATTRIBUTE_COMBINE_LAST: newvalue = igraphmodule_i_ac_last(value, merges); break; case IGRAPH_ATTRIBUTE_COMBINE_MEAN: newvalue = igraphmodule_i_ac_mean(value, merges); break; case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN: newvalue = igraphmodule_i_ac_median(value, merges); break; case IGRAPH_ATTRIBUTE_COMBINE_CONCAT: empty_str = PyString_FromString(""); func = PyObject_GetAttrString(empty_str, "join"); newvalue = igraphmodule_i_ac_func(value, merges, func); Py_DECREF(func); Py_DECREF(empty_str); break; default: IGRAPH_ERROR("Unsupported combination type. " "This should never happen. Please report the bug to the igraph " "developers!", IGRAPH_FAILURE); } if (newvalue) { if (PyDict_SetItem(newdict, key, newvalue)) { Py_DECREF(newvalue); /* PyDict_SetItem does not steal reference */ IGRAPH_ERROR("PyDict_SetItem failed when combining attributes.", IGRAPH_FAILURE); } Py_DECREF(newvalue); /* PyDict_SetItem does not steal reference */ } else { /* We can arrive here for two reasons: first, if the attribute is to * be ignored explicitly; second, if there was an error. */ if (PyErr_Occurred()) { IGRAPH_ERROR("Unexpected failure when combining attributes", IGRAPH_FAILURE); } } i++; } igraphmodule_i_free_attribute_combination_records(todo); IGRAPH_FINALLY_CLEAN(1); return 0; } /* Combining vertices */ static int igraphmodule_i_attribute_combine_vertices(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb) { PyObject *dict, *newdict; int result; /* Get the attribute dicts */ dict=ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_VERTEX]; newdict=ATTR_STRUCT_DICT(newgraph)[ATTRHASH_IDX_VERTEX]; /* Combine the attribute dicts */ result = igraphmodule_i_attribute_combine_dicts(dict, newdict, merges, comb); /* Invalidate vertex name index */ igraphmodule_i_attribute_struct_invalidate_vertex_name_index(ATTR_STRUCT(graph)); return result; } /* Combining edges */ static int igraphmodule_i_attribute_combine_edges(const igraph_t *graph, igraph_t *newgraph, const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb) { PyObject *dict, *newdict; /* Get the attribute dicts */ dict=ATTR_STRUCT_DICT(graph)[ATTRHASH_IDX_EDGE]; newdict=ATTR_STRUCT_DICT(newgraph)[ATTRHASH_IDX_EDGE]; return igraphmodule_i_attribute_combine_dicts(dict, newdict, merges, comb); } /* Getting attribute names and types */ static int igraphmodule_i_attribute_get_info(const igraph_t *graph, igraph_strvector_t *gnames, igraph_vector_t *gtypes, igraph_strvector_t *vnames, igraph_vector_t *vtypes, igraph_strvector_t *enames, igraph_vector_t *etypes) { igraph_strvector_t *names[3] = { gnames, vnames, enames }; igraph_vector_t *types[3] = { gtypes, vtypes, etypes }; int retval; long int i, j, k, l, m; for (i=0; i<3; i++) { igraph_strvector_t *n = names[i]; igraph_vector_t *t = types[i]; PyObject *dict = ATTR_STRUCT_DICT(graph)[i]; PyObject *keys; PyObject *values; PyObject *o=0; keys=PyDict_Keys(dict); if (!keys) IGRAPH_ERROR("Internal error in PyDict_Keys", IGRAPH_FAILURE); if (n) { retval = igraphmodule_PyList_to_strvector_t(keys, n); if (retval) return retval; } if (t) { k=PyList_Size(keys); igraph_vector_resize(t, k); for (j=0; j0) { for (i=0; iob_type) : 0; if (type_str != 0) { PyErr_Format(PyExc_TypeError, "igraph supports string attribute names only, got %s", PyString_AS_STRING(type_str)); Py_DECREF(type_str); } else { PyErr_Format(PyExc_TypeError, "igraph supports string attribute names only"); } return 0; } python-igraph-0.7.1.post6/src/attributes.h0000644000076500000240000000743512460534506021134 0ustar ntamasstaff00000000000000/* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PY_IGRAPH_ATTRIBUTES_H #define PY_IGRAPH_ATTRIBUTES_H #include #include #include #include #include #include #define ATTRHASH_IDX_GRAPH 0 #define ATTRHASH_IDX_VERTEX 1 #define ATTRHASH_IDX_EDGE 2 typedef struct { PyObject* attrs[3]; PyObject* vertex_name_index; } igraphmodule_i_attribute_struct; #define ATTR_STRUCT(graph) ((igraphmodule_i_attribute_struct*)((graph)->attr)) #define ATTR_STRUCT_DICT(graph) ((igraphmodule_i_attribute_struct*)((graph)->attr))->attrs #define ATTR_NAME_INDEX(graph) ((igraphmodule_i_attribute_struct*)((graph)->attr))->vertex_name_index int igraphmodule_i_attribute_get_type(const igraph_t *graph, igraph_attribute_type_t *type, igraph_attribute_elemtype_t elemtype, const char *name); int igraphmodule_i_get_numeric_graph_attr(const igraph_t *graph, const char *name, igraph_vector_t *value); int igraphmodule_i_get_numeric_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_t *value); int igraphmodule_i_get_numeric_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_t *value); int igraphmodule_i_get_string_graph_attr(const igraph_t *graph, const char *name, igraph_strvector_t *value); int igraphmodule_i_get_string_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_strvector_t *value); int igraphmodule_i_get_string_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_strvector_t *value); int igraphmodule_i_get_boolean_graph_attr(const igraph_t *graph, const char *name, igraph_vector_bool_t *value); int igraphmodule_i_get_boolean_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_bool_t *value); int igraphmodule_i_get_boolean_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_bool_t *value); int igraphmodule_attribute_name_check(PyObject* obj); void igraphmodule_initialize_attribute_handler(void); void igraphmodule_index_vertex_names(igraph_t *graph, igraph_bool_t force); void igraphmodule_invalidate_vertex_name_index(igraph_t *graph); int igraphmodule_get_vertex_id_by_name(igraph_t *graph, PyObject* o, igraph_integer_t* id); PyObject* igraphmodule_create_edge_attribute(const igraph_t* graph, const char* name); PyObject* igraphmodule_create_or_get_edge_attribute_values(const igraph_t* graph, const char* name); PyObject* igraphmodule_get_edge_attribute_values(const igraph_t* graph, const char* name); igraph_bool_t igraphmodule_has_graph_attribute(const igraph_t *graph, const char* name); igraph_bool_t igraphmodule_has_vertex_attribute(const igraph_t *graph, const char* name); igraph_bool_t igraphmodule_has_edge_attribute(const igraph_t *graph, const char* name); #endif python-igraph-0.7.1.post6/src/bfsiter.c0000644000076500000240000002057512453614202020371 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "bfsiter.h" #include "common.h" #include "error.h" #include "py2compat.h" #include "vertexobject.h" /** * \ingroup python_interface * \defgroup python_interface_bfsiter BFS iterator object */ PyTypeObject igraphmodule_BFSIterType; /** * \ingroup python_interface_bfsiter * \brief Allocate a new BFS iterator object for a given graph and a given root * \param g the graph object being referenced * \param vid the root vertex index * \param advanced whether the iterator should be advanced (returning distance and parent as well) * \return the allocated PyObject */ PyObject* igraphmodule_BFSIter_new(igraphmodule_GraphObject *g, PyObject *root, igraph_neimode_t mode, igraph_bool_t advanced) { igraphmodule_BFSIterObject* o; long int no_of_nodes, r; o=PyObject_GC_New(igraphmodule_BFSIterObject, &igraphmodule_BFSIterType); Py_INCREF(g); o->gref=g; o->graph=&g->g; if (!PyInt_Check(root) && !PyObject_IsInstance(root, (PyObject*)&igraphmodule_VertexType)) { PyErr_SetString(PyExc_TypeError, "root must be integer or igraph.Vertex"); return NULL; } no_of_nodes=igraph_vcount(&g->g); o->visited=(char*)calloc(no_of_nodes, sizeof(char)); if (o->visited == 0) { PyErr_SetString(PyExc_MemoryError, "out of memory"); return NULL; } if (igraph_dqueue_init(&o->queue, 100)) { PyErr_SetString(PyExc_MemoryError, "out of memory"); return NULL; } if (igraph_vector_init(&o->neis, 0)) { PyErr_SetString(PyExc_MemoryError, "out of memory"); igraph_dqueue_destroy(&o->queue); return NULL; } if (PyInt_Check(root)) { r=PyInt_AsLong(root); } else { r=((igraphmodule_VertexObject*)root)->idx; } if (igraph_dqueue_push(&o->queue, r) || igraph_dqueue_push(&o->queue, 0) || igraph_dqueue_push(&o->queue, -1)) { igraph_dqueue_destroy(&o->queue); igraph_vector_destroy(&o->neis); PyErr_SetString(PyExc_MemoryError, "out of memory"); return NULL; } o->visited[r]=1; if (!igraph_is_directed(&g->g)) mode=IGRAPH_ALL; o->mode=mode; o->advanced=advanced; PyObject_GC_Track(o); RC_ALLOC("BFSIter", o); return (PyObject*)o; } /** * \ingroup python_interface_bfsiter * \brief Support for cyclic garbage collection in Python * * This is necessary because the \c igraph.BFSIter object contains several * other \c PyObject pointers and they might point back to itself. */ int igraphmodule_BFSIter_traverse(igraphmodule_BFSIterObject *self, visitproc visit, void *arg) { int vret; RC_TRAVERSE("BFSIter", self); if (self->gref) { vret=visit((PyObject*)self->gref, arg); if (vret != 0) return vret; } return 0; } /** * \ingroup python_interface_bfsiter * \brief Clears the iterator's subobject (before deallocation) */ int igraphmodule_BFSIter_clear(igraphmodule_BFSIterObject *self) { PyObject *tmp; PyObject_GC_UnTrack(self); tmp=(PyObject*)self->gref; self->gref=NULL; Py_XDECREF(tmp); igraph_dqueue_destroy(&self->queue); igraph_vector_destroy(&self->neis); free(self->visited); self->visited=0; return 0; } /** * \ingroup python_interface_bfsiter * \brief Deallocates a Python representation of a given BFS iterator object */ void igraphmodule_BFSIter_dealloc(igraphmodule_BFSIterObject* self) { igraphmodule_BFSIter_clear(self); RC_DEALLOC("BFSIter", self); PyObject_GC_Del(self); } PyObject* igraphmodule_BFSIter_iter(igraphmodule_BFSIterObject* self) { Py_INCREF(self); return (PyObject*)self; } PyObject* igraphmodule_BFSIter_iternext(igraphmodule_BFSIterObject* self) { if (!igraph_dqueue_empty(&self->queue)) { igraph_integer_t vid = (igraph_integer_t)igraph_dqueue_pop(&self->queue); igraph_integer_t dist = (igraph_integer_t)igraph_dqueue_pop(&self->queue); igraph_integer_t parent = (igraph_integer_t)igraph_dqueue_pop(&self->queue); long int i; if (igraph_neighbors(self->graph, &self->neis, vid, self->mode)) { igraphmodule_handle_igraph_error(); return NULL; } for (i=0; ineis); i++) { igraph_integer_t neighbor = (igraph_integer_t)VECTOR(self->neis)[i]; if (self->visited[neighbor]==0) { self->visited[neighbor]=1; if (igraph_dqueue_push(&self->queue, neighbor) || igraph_dqueue_push(&self->queue, dist+1) || igraph_dqueue_push(&self->queue, vid)) { igraphmodule_handle_igraph_error(); return NULL; } } } if (self->advanced) { PyObject *vertexobj, *parentobj; vertexobj = igraphmodule_Vertex_New(self->gref, vid); if (!vertexobj) return NULL; if (parent >= 0) { parentobj = igraphmodule_Vertex_New(self->gref, parent); if (!parentobj) return NULL; } else { Py_INCREF(Py_None); parentobj=Py_None; } return Py_BuildValue("NlN", vertexobj, (long int)dist, parentobj); } else { return igraphmodule_Vertex_New(self->gref, vid); } } else { return NULL; } } /** * \ingroup python_interface_bfsiter * Method table for the \c igraph.BFSIter object */ PyMethodDef igraphmodule_BFSIter_methods[] = { {NULL} }; /** \ingroup python_interface_bfsiter * Python type object referencing the methods Python calls when it performs various operations on * a BFS iterator of a graph */ PyTypeObject igraphmodule_BFSIterType = { PyVarObject_HEAD_INIT(0, 0) "igraph.BFSIter", // tp_name sizeof(igraphmodule_BFSIterObject), // tp_basicsize 0, // tp_itemsize (destructor)igraphmodule_BFSIter_dealloc, // tp_dealloc 0, // tp_print 0, // tp_getattr 0, // tp_setattr 0, /* tp_compare (2.x) / tp_reserved (3.x) */ 0, // tp_repr 0, // tp_as_number 0, // tp_as_sequence 0, // tp_as_mapping 0, // tp_hash 0, // tp_call 0, // tp_str 0, // tp_getattro 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, // tp_flags "igraph BFS iterator object", // tp_doc (traverseproc) igraphmodule_BFSIter_traverse, /* tp_traverse */ (inquiry) igraphmodule_BFSIter_clear, /* tp_clear */ 0, // tp_richcompare 0, // tp_weaklistoffset (getiterfunc)igraphmodule_BFSIter_iter, /* tp_iter */ (iternextfunc)igraphmodule_BFSIter_iternext, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ }; python-igraph-0.7.1.post6/src/bfsiter.h0000644000076500000240000000323512453614202020370 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_BFSITER_H #define PYTHON_BFSITER_H #include #include "graphobject.h" /** * \ingroup python_interface_bfsiter * \brief A structure representing a BFS iterator of a graph */ typedef struct { PyObject_HEAD igraphmodule_GraphObject* gref; igraph_dqueue_t queue; igraph_vector_t neis; igraph_t *graph; char *visited; igraph_neimode_t mode; igraph_bool_t advanced; } igraphmodule_BFSIterObject; PyObject* igraphmodule_BFSIter_new(igraphmodule_GraphObject *g, PyObject *o, igraph_neimode_t mode, igraph_bool_t advanced); int igraphmodule_BFSIter_traverse(igraphmodule_BFSIterObject *self, visitproc visit, void *arg); int igraphmodule_BFSIter_clear(igraphmodule_BFSIterObject *self); void igraphmodule_BFSIter_dealloc(igraphmodule_BFSIterObject* self); extern PyTypeObject igraphmodule_BFSIterType; #endif python-igraph-0.7.1.post6/src/common.c0000644000076500000240000000452612453614202020221 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "structmember.h" /** * \ingroup python_interface * \brief Handler function for all unimplemented \c igraph.Graph methods * * This function is called whenever an unimplemented \c igraph.Graph method * is called ("unimplemented" meaning that there is a method name in the * method table of \c igraph.Graph , but there isn't any working implementation * either because the underlying \c igraph API might be subject to change * or because the calling format from Python is not decided yet (or maybe * because of laziness or lack of time ;)) * * All of the parameters are ignored, they are here just to make the * function satisfy the requirements of \c PyCFunction, thus allowing it * to be included in a method table. * * \return NULL */ PyObject* igraphmodule_unimplemented(PyObject* self, PyObject* args, PyObject* kwds) { PyErr_SetString(PyExc_NotImplementedError, "This method is unimplemented."); return NULL; } /** * \ingroup python_interface * \brief Resolves a weak reference to an \c igraph.Graph * \return the \c igraph.Graph object or NULL if the weak reference is dead. * Sets an exception in the latter case. */ PyObject* igraphmodule_resolve_graph_weakref(PyObject* ref) { PyObject *o; if (!PyWeakref_Check(ref)) { PyErr_SetString(PyExc_TypeError, "weak reference expected"); return NULL; } o=PyWeakref_GetObject(ref); if (o == Py_None) { PyErr_SetString(PyExc_TypeError, "underlying graph has already been destroyed"); return NULL; } return o; } python-igraph-0.7.1.post6/src/common.h0000644000076500000240000000504412453614202020222 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_COMMON_H #define PYTHON_COMMON_H #include #ifdef RC_DEBUG # define RC_ALLOC(T, P) fprintf(stderr, "[ alloc ] " T " @ %p\n", P) # define RC_DECREF(T, P) fprintf(stderr, "[ ref - ] " T " @ %p (was: %d)\n", P, (int)P->ob_refcnt); # define RC_INCREF(T, P) fprintf(stderr, "[ ref + ] " T " @ %p (was: %d)\n", P, (int)P->ob_refcnt); # define RC_PRINT(P) fprintf(stderr, "[refcntr] %s @ %p = %d\n", ((PyTypeObject*)P->ob_type)->tp_name, P, (int)P->ob_refcnt); # define RC_DEALLOC(T, P) fprintf(stderr, "[dealloc] " T " @ %p\n", P); # define RC_TRAVERSE(T, P) #else # define RC_ALLOC(T, P) # define RC_DECREF(T, P) # define RC_INCREF(T, P) # define RC_PRINT(P) # define RC_DEALLOC(T, P) # define RC_TRAVERSE(T, P) #endif /* Compatibility stuff for Python 2.3 */ #ifndef Py_RETURN_TRUE #define Py_RETURN_TRUE { Py_INCREF(Py_True); return Py_True; } #endif #ifndef Py_RETURN_FALSE #define Py_RETURN_FALSE { Py_INCREF(Py_False); return Py_False; } #endif #ifndef Py_RETURN_NONE #define Py_RETURN_NONE { Py_INCREF(Py_None); return Py_None; } #endif #ifndef Py_RETURN_NOTIMPLEMENTED #define Py_RETURN_NOTIMPLEMENTED { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } #endif #ifndef Py_RETURN #define Py_RETURN(x) { if (x) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } #endif /* Compatibility stuff for Python 2.4 */ #if (PY_MAJOR_VERSION <= 2) & (PY_MINOR_VERSION <= 4) #define lenfunc inquiry #define ssizeargfunc intargfunc #define ssizessizeargfunc intintargfunc #define Py_ssize_t int #endif #define ATTRIBUTE_TYPE_VERTEX 1 #define ATTRIBUTE_TYPE_EDGE 2 PyObject* igraphmodule_unimplemented(PyObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_resolve_graph_weakref(PyObject* ref); #endif python-igraph-0.7.1.post6/src/convert.c0000644000076500000240000025576512534342712020432 0ustar ntamasstaff00000000000000/* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /************************ Miscellaneous functions *************************/ #include #include #include "attributes.h" #include "graphobject.h" #include "vertexseqobject.h" #include "vertexobject.h" #include "edgeseqobject.h" #include "edgeobject.h" #include "convert.h" #include "error.h" #include "memory.h" #include "py2compat.h" #if defined(_MSC_VER) #define strcasecmp _stricmp #endif /** * \brief Converts a Python integer to a C int * * This is similar to PyInt_AsLong, but it checks for overflow first and throws * an exception if necessary. * * Returns -1 if there was an error, 0 otherwise. */ int PyInt_AsInt(PyObject* obj, int* result) { long dummy = PyInt_AsLong(obj); if (dummy < INT_MIN) { PyErr_SetString(PyExc_OverflowError, "integer too small for conversion to C int"); return -1; } if (dummy > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "integer too large for conversion to C int"); return -1; } *result = (int)dummy; return 0; } /** * \brief Converts a Python long to a C int * * This is similar to PyLong_AsLong, but it checks for overflow first and * throws an exception if necessary. * * Returns -1 if there was an error, 0 otherwise. */ int PyLong_AsInt(PyObject* obj, int* result) { long dummy = PyLong_AsLong(obj); if (dummy < INT_MIN) { PyErr_SetString(PyExc_OverflowError, "long integer too small for conversion to C int"); return -1; } if (dummy > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "long integer too large for conversion to C int"); return -1; } *result = (int)dummy; return 0; } /** * \ingroup python_interface_conversion * \brief Converts a Python object to a corresponding igraph enum. * * The numeric value is returned as an integer that must be converted * explicitly to the corresponding igraph enum type. This is to allow one * to use the same common conversion routine for multiple enum types. * * \param o a Python object to be converted * \param translation the translation table between strings and the * enum values. Strings are treated as case-insensitive, but it is * assumed that the translation table keys are lowercase. The last * entry of the table must contain NULL values. * \param result the result is returned here. The default value must be * passed in before calling this function, since this value is * returned untouched if the given Python object is Py_None. * \return 0 if everything is OK, 1 otherwise. An appropriate exception * is raised in this case. */ int igraphmodule_PyObject_to_enum(PyObject *o, igraphmodule_enum_translation_table_entry_t* table, int *result) { char *s, *s2; int i, best, best_result, best_unique; if (o == 0 || o == Py_None) return 0; if (PyInt_Check(o)) return PyInt_AsInt(o, result); if (PyLong_Check(o)) return PyLong_AsInt(o, result); s = PyString_CopyAsString(o); if (s == 0) { PyErr_SetString(PyExc_TypeError, "int, long or string expected"); return -1; } /* Convert string to lowercase */ for (s2=s; *s2; s2++) *s2 = tolower(*s2); best = 0; best_unique = 0; best_result = -1; /* Search for matches */ while (table->name != 0) { if (strcmp(s, table->name) == 0) { *result = table->value; free(s); return 0; } for (i=0; s[i] == table->name[i]; i++); if (i > best) { best = i; best_unique = 1; best_result = table->value; } else if (i == best) best_unique = 0; table++; } free(s); if (best_unique) { *result = best_result; return 0; } PyErr_SetObject(PyExc_ValueError, o); return -1; } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_neimode_t */ int igraphmodule_PyObject_to_neimode_t(PyObject *o, igraph_neimode_t *result) { static igraphmodule_enum_translation_table_entry_t neimode_tt[] = { {"in", IGRAPH_IN}, {"out", IGRAPH_OUT}, {"all", IGRAPH_ALL}, {0,0} }; return igraphmodule_PyObject_to_enum(o, neimode_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_add_weights_t */ int igraphmodule_PyObject_to_add_weights_t(PyObject *o, igraph_add_weights_t *result) { static igraphmodule_enum_translation_table_entry_t add_weights_tt[] = { {"true", IGRAPH_ADD_WEIGHTS_YES}, {"yes", IGRAPH_ADD_WEIGHTS_YES}, {"false", IGRAPH_ADD_WEIGHTS_NO}, {"no", IGRAPH_ADD_WEIGHTS_NO}, {"auto", IGRAPH_ADD_WEIGHTS_IF_PRESENT}, {"if_present", IGRAPH_ADD_WEIGHTS_IF_PRESENT}, {0,0} }; if (o == Py_True) { *result = IGRAPH_ADD_WEIGHTS_YES; return 0; } if (o == Py_False) { *result = IGRAPH_ADD_WEIGHTS_NO; return 0; } return igraphmodule_PyObject_to_enum(o, add_weights_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_adjacency_t */ int igraphmodule_PyObject_to_adjacency_t(PyObject *o, igraph_adjacency_t *result) { static igraphmodule_enum_translation_table_entry_t adjacency_tt[] = { {"directed", IGRAPH_ADJ_DIRECTED}, {"undirected", IGRAPH_ADJ_UNDIRECTED}, {"upper", IGRAPH_ADJ_UPPER}, {"lower", IGRAPH_ADJ_LOWER}, {"minimum", IGRAPH_ADJ_MIN}, {"maximum", IGRAPH_ADJ_MAX}, {"plus", IGRAPH_ADJ_PLUS}, {0,0} }; return igraphmodule_PyObject_to_enum(o, adjacency_tt, (int*)result); } int igraphmodule_PyObject_to_attribute_combination_type_t(PyObject* o, igraph_attribute_combination_type_t *result) { static igraphmodule_enum_translation_table_entry_t attribute_combination_type_tt[] = { {"ignore", IGRAPH_ATTRIBUTE_COMBINE_IGNORE}, {"sum", IGRAPH_ATTRIBUTE_COMBINE_SUM}, {"product", IGRAPH_ATTRIBUTE_COMBINE_PROD}, {"min", IGRAPH_ATTRIBUTE_COMBINE_MIN}, {"max", IGRAPH_ATTRIBUTE_COMBINE_MAX}, {"random", IGRAPH_ATTRIBUTE_COMBINE_RANDOM}, {"first", IGRAPH_ATTRIBUTE_COMBINE_FIRST}, {"last", IGRAPH_ATTRIBUTE_COMBINE_LAST}, {"mean", IGRAPH_ATTRIBUTE_COMBINE_MEAN}, {"median", IGRAPH_ATTRIBUTE_COMBINE_MEDIAN}, {"concatenate", IGRAPH_ATTRIBUTE_COMBINE_CONCAT}, {0, 0} }; if (o == Py_None) { *result = IGRAPH_ATTRIBUTE_COMBINE_IGNORE; return 0; } if (PyCallable_Check(o)) { *result = IGRAPH_ATTRIBUTE_COMBINE_FUNCTION; return 0; } return igraphmodule_PyObject_to_enum(o, attribute_combination_type_tt, (int*)result); } int igraphmodule_PyObject_to_eigen_algorithm_t(PyObject *object, igraph_eigen_algorithm_t *a) { static igraphmodule_enum_translation_table_entry_t eigen_algorithm_tt[] = { {"auto", IGRAPH_EIGEN_AUTO}, {"lapack", IGRAPH_EIGEN_LAPACK}, {"arpack", IGRAPH_EIGEN_ARPACK}, {"comp_auto", IGRAPH_EIGEN_COMP_AUTO}, {"comp_lapack", IGRAPH_EIGEN_COMP_LAPACK}, {"comp_arpack", IGRAPH_EIGEN_COMP_ARPACK}, {0,0} }; if (object == Py_None) { *a = IGRAPH_EIGEN_ARPACK; return 0; } else { return igraphmodule_PyObject_to_enum(object, eigen_algorithm_tt, (int*)a); } } int igraphmodule_PyObject_to_eigen_which_t(PyObject *object, igraph_eigen_which_t *w) { PyObject *key, *value; Py_ssize_t pos = 0; static igraphmodule_enum_translation_table_entry_t eigen_which_position_tt[] = { { "LM", IGRAPH_EIGEN_LM}, { "SM", IGRAPH_EIGEN_SM}, { "LA", IGRAPH_EIGEN_LA}, { "SA", IGRAPH_EIGEN_SA}, { "BE", IGRAPH_EIGEN_BE}, { "LR", IGRAPH_EIGEN_LR}, { "SR", IGRAPH_EIGEN_SR}, { "LI", IGRAPH_EIGEN_LI}, { "SI", IGRAPH_EIGEN_SI}, { "ALL", IGRAPH_EIGEN_ALL}, { "INTERVAL", IGRAPH_EIGEN_INTERVAL}, { "SELECT", IGRAPH_EIGEN_SELECT} }; static igraphmodule_enum_translation_table_entry_t lapack_dgeevc_balance_tt[] = { { "none", IGRAPH_LAPACK_DGEEVX_BALANCE_NONE }, { "perm", IGRAPH_LAPACK_DGEEVX_BALANCE_PERM }, { "scale", IGRAPH_LAPACK_DGEEVX_BALANCE_SCALE }, { "both", IGRAPH_LAPACK_DGEEVX_BALANCE_BOTH } }; w->pos = IGRAPH_EIGEN_LM; w->howmany = 1; w->il = w->iu = -1; w->vl = IGRAPH_NEGINFINITY; w->vu = IGRAPH_INFINITY; w->vestimate = 0; w->balance = IGRAPH_LAPACK_DGEEVX_BALANCE_NONE; if (object != Py_None && !PyDict_Check(object)) { PyErr_SetString(PyExc_TypeError, "Python dictionary expected"); return -1; } if (object != Py_None) { while (PyDict_Next(object, &pos, &key, &value)) { char *kv; #ifdef IGRAPH_PYTHON3 PyObject *temp_bytes; if (!PyUnicode_Check(key)) { PyErr_SetString(PyExc_TypeError, "Dict key must be string"); return -1; } temp_bytes = PyUnicode_AsEncodedString(key, "ascii", "ignore"); if (temp_bytes == 0) { /* Exception set already by PyUnicode_AsEncodedString */ return -1; } kv = strdup(PyBytes_AS_STRING(temp_bytes)); Py_DECREF(temp_bytes); #else if (!PyString_Check(key)) { PyErr_SetString(PyExc_TypeError, "Dict key must be string"); return -1; } kv=PyString_AsString(key); #endif if (!strcasecmp(kv, "pos")) { igraphmodule_PyObject_to_enum(value, eigen_which_position_tt, (int*) &w->pos); } else if (!strcasecmp(kv, "howmany")) { w->howmany = (int) PyInt_AsLong(value); } else if (!strcasecmp(kv, "il")) { w->il = (int) PyInt_AsLong(value); } else if (!strcasecmp(kv, "iu")) { w->iu = (int) PyInt_AsLong(value); } else if (!strcasecmp(kv, "vl")) { w->vl = PyFloat_AsDouble(value); } else if (!strcasecmp(kv, "vu")) { w->vu = PyFloat_AsDouble(value); } else if (!strcasecmp(kv, "vestimate")) { w->vestimate = (int) PyInt_AsLong(value); } else if (!strcasecmp(kv, "balance")) { igraphmodule_PyObject_to_enum(value, lapack_dgeevc_balance_tt, (int*) &w->balance); } else { PyErr_SetString(PyExc_TypeError, "Unknown eigen parameter"); #ifdef IGRAPH_PYTHON3 if (kv != 0) { free(kv); } #endif return -1; } #ifdef IGRAPH_PYTHON3 if (kv != 0) { free(kv); } #endif } } return 0; } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_barabasi_algorithm_t */ int igraphmodule_PyObject_to_barabasi_algorithm_t(PyObject *o, igraph_barabasi_algorithm_t *result) { static igraphmodule_enum_translation_table_entry_t barabasi_algorithm_tt[] = { {"bag", IGRAPH_BARABASI_BAG}, {"psumtree", IGRAPH_BARABASI_PSUMTREE}, {"psumtree_multiple", IGRAPH_BARABASI_PSUMTREE_MULTIPLE}, {0,0} }; return igraphmodule_PyObject_to_enum(o, barabasi_algorithm_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_connectedness_t */ int igraphmodule_PyObject_to_connectedness_t(PyObject *o, igraph_connectedness_t *result) { static igraphmodule_enum_translation_table_entry_t connectedness_tt[] = { {"weak", IGRAPH_WEAK}, {"strong", IGRAPH_STRONG}, {0,0} }; return igraphmodule_PyObject_to_enum(o, connectedness_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_vconn_nei_t */ int igraphmodule_PyObject_to_vconn_nei_t(PyObject *o, igraph_vconn_nei_t *result) { static igraphmodule_enum_translation_table_entry_t vconn_nei_tt[] = { {"error", IGRAPH_VCONN_NEI_ERROR}, {"negative", IGRAPH_VCONN_NEI_NEGATIVE}, {"number_of_nodes", IGRAPH_VCONN_NEI_NUMBER_OF_NODES}, {"nodes", IGRAPH_VCONN_NEI_NUMBER_OF_NODES}, {"ignore", IGRAPH_VCONN_NEI_IGNORE}, {0,0} }; return igraphmodule_PyObject_to_enum(o, vconn_nei_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_bliss_sh_t */ int igraphmodule_PyObject_to_bliss_sh_t(PyObject *o, igraph_bliss_sh_t *result) { static igraphmodule_enum_translation_table_entry_t bliss_sh_tt[] = { {"f", IGRAPH_BLISS_F}, {"fl", IGRAPH_BLISS_FL}, {"fs", IGRAPH_BLISS_FS}, {"fm", IGRAPH_BLISS_FM}, {"flm", IGRAPH_BLISS_FLM}, {"fsm", IGRAPH_BLISS_FSM}, {0,0} }; return igraphmodule_PyObject_to_enum(o, bliss_sh_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_community_comparison_t */ int igraphmodule_PyObject_to_community_comparison_t(PyObject *o, igraph_community_comparison_t *result) { static igraphmodule_enum_translation_table_entry_t commcmp_tt[] = { {"vi", IGRAPH_COMMCMP_VI}, {"meila", IGRAPH_COMMCMP_VI}, {"nmi", IGRAPH_COMMCMP_NMI}, {"danon", IGRAPH_COMMCMP_NMI}, {"split-join", IGRAPH_COMMCMP_SPLIT_JOIN}, {"rand", IGRAPH_COMMCMP_RAND}, {"adjusted_rand", IGRAPH_COMMCMP_ADJUSTED_RAND}, {0,0} }; return igraphmodule_PyObject_to_enum(o, commcmp_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_degseq_t */ int igraphmodule_PyObject_to_degseq_t(PyObject *o, igraph_degseq_t *result) { static igraphmodule_enum_translation_table_entry_t degseq_tt[] = { {"simple", IGRAPH_DEGSEQ_SIMPLE}, {"no_multiple", IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE}, {"vl", IGRAPH_DEGSEQ_VL}, {"viger-latapy", IGRAPH_DEGSEQ_VL}, {0,0} }; return igraphmodule_PyObject_to_enum(o, degseq_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_fas_algorithm_t */ int igraphmodule_PyObject_to_fas_algorithm_t(PyObject *o, igraph_fas_algorithm_t *result) { static igraphmodule_enum_translation_table_entry_t fas_algorithm_tt[] = { {"approx_eades", IGRAPH_FAS_APPROX_EADES}, {"eades", IGRAPH_FAS_APPROX_EADES}, {"exact", IGRAPH_FAS_EXACT_IP}, {"exact_ip", IGRAPH_FAS_EXACT_IP}, {"ip", IGRAPH_FAS_EXACT_IP}, {0,0} }; return igraphmodule_PyObject_to_enum(o, fas_algorithm_tt, (int*)result); } /** * \brief Converts a Python object to an igraph \c igraph_layout_grid_t */ /* int igraphmodule_PyObject_to_layout_grid_t(PyObject *o, igraph_layout_grid_t *result) { static igraphmodule_enum_translation_table_entry_t layout_grid_tt[] = { {"auto", IGRAPH_LAYOUT_AUTOGRID}, {"grid", IGRAPH_LAYOUT_GRID}, {"nogrid", IGRAPH_LAYOUT_NOGRID}, {0,0} }; if (o == Py_True) { *result = IGRAPH_LAYOUT_GRID; return 0; } else if (o == Py_False) { *result = IGRAPH_LAYOUT_NOGRID; return 0; } else { return igraphmodule_PyObject_to_enum(o, layout_grid_tt, (int*)result); } } */ /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_random_walk_stuck_t */ /* int igraphmodule_PyObject_to_random_walk_stuck_t(PyObject *o, igraph_random_walk_stuck_t *result) { static igraphmodule_enum_translation_table_entry_t random_walk_stuck_tt[] = { {"return", IGRAPH_RANDOM_WALK_STUCK_RETURN}, {"error", IGRAPH_RANDOM_WALK_STUCK_ERROR}, {0,0} }; return igraphmodule_PyObject_to_enum(o, random_walk_stuck_tt, (int*)result); } */ /** * \brief Converts a Python object to an igraph \c igraph_reciprocity_t */ int igraphmodule_PyObject_to_reciprocity_t(PyObject *o, igraph_reciprocity_t *result) { static igraphmodule_enum_translation_table_entry_t reciprocity_tt[] = { {"default", IGRAPH_RECIPROCITY_DEFAULT}, {"ratio", IGRAPH_RECIPROCITY_RATIO}, {0,0} }; return igraphmodule_PyObject_to_enum(o, reciprocity_tt, (int*)result); } /** * \brief Converts a Python object to an igraph \c igraph_rewiring_t */ int igraphmodule_PyObject_to_rewiring_t(PyObject *o, igraph_rewiring_t *result) { static igraphmodule_enum_translation_table_entry_t rewiring_tt[] = { {"simple", IGRAPH_REWIRING_SIMPLE}, {"simple_loops", IGRAPH_REWIRING_SIMPLE_LOOPS}, {"loops", IGRAPH_REWIRING_SIMPLE_LOOPS}, {0,0} }; return igraphmodule_PyObject_to_enum(o, rewiring_tt, (int*)result); } /** * \brief Converts a Python object to an igraph \c igraph_spinglass_implementation_t */ int igraphmodule_PyObject_to_spinglass_implementation_t(PyObject *o, igraph_spinglass_implementation_t *result) { static igraphmodule_enum_translation_table_entry_t spinglass_implementation_tt[] = { {"original", IGRAPH_SPINCOMM_IMP_ORIG}, {"negative", IGRAPH_SPINCOMM_IMP_NEG}, {0,0} }; return igraphmodule_PyObject_to_enum(o, spinglass_implementation_tt, (int*)result); } /** * \brief Converts a Python object to an igraph \c igraph_spincomm_update_t */ int igraphmodule_PyObject_to_spincomm_update_t(PyObject *o, igraph_spincomm_update_t *result) { static igraphmodule_enum_translation_table_entry_t spincomm_update_tt[] = { {"simple", IGRAPH_SPINCOMM_UPDATE_SIMPLE}, {"config", IGRAPH_SPINCOMM_UPDATE_CONFIG}, {0,0} }; return igraphmodule_PyObject_to_enum(o, spincomm_update_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_star_mode_t */ int igraphmodule_PyObject_to_star_mode_t(PyObject *o, igraph_star_mode_t *result) { static igraphmodule_enum_translation_table_entry_t star_mode_tt[] = { {"in", IGRAPH_STAR_IN}, {"out", IGRAPH_STAR_OUT}, {"mutual", IGRAPH_STAR_MUTUAL}, {"undirected", IGRAPH_STAR_UNDIRECTED}, {0,0} }; return igraphmodule_PyObject_to_enum(o, star_mode_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_subgraph_implementation_t */ int igraphmodule_PyObject_to_subgraph_implementation_t(PyObject *o, igraph_subgraph_implementation_t *result) { static igraphmodule_enum_translation_table_entry_t subgraph_impl_tt[] = { {"auto", IGRAPH_SUBGRAPH_AUTO}, {"copy_and_delete", IGRAPH_SUBGRAPH_COPY_AND_DELETE}, {"old", IGRAPH_SUBGRAPH_COPY_AND_DELETE}, {"create_from_scratch", IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH}, {"new", IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH}, {0,0} }; return igraphmodule_PyObject_to_enum(o, subgraph_impl_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_to_undirected_t */ int igraphmodule_PyObject_to_to_undirected_t(PyObject *o, igraph_to_undirected_t *result) { static igraphmodule_enum_translation_table_entry_t to_undirected_tt[] = { {"each", IGRAPH_TO_UNDIRECTED_EACH}, {"collapse", IGRAPH_TO_UNDIRECTED_COLLAPSE}, {"mutual", IGRAPH_TO_UNDIRECTED_MUTUAL}, {0,0} }; if (o == Py_True) { *result = IGRAPH_TO_UNDIRECTED_COLLAPSE; return 0; } else if (o == Py_False) { *result = IGRAPH_TO_UNDIRECTED_EACH; return 0; } return igraphmodule_PyObject_to_enum(o, to_undirected_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an \c igraph_transitivity_mode_t */ int igraphmodule_PyObject_to_transitivity_mode_t(PyObject *o, igraph_transitivity_mode_t *result) { static igraphmodule_enum_translation_table_entry_t transitivity_mode_tt[] = { {"zero", IGRAPH_TRANSITIVITY_ZERO}, {"0", IGRAPH_TRANSITIVITY_ZERO}, {"nan", IGRAPH_TRANSITIVITY_NAN}, {0,0} }; return igraphmodule_PyObject_to_enum(o, transitivity_mode_tt, (int*)result); } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_tree_mode_t */ int igraphmodule_PyObject_to_tree_mode_t(PyObject *o, igraph_tree_mode_t *result) { static igraphmodule_enum_translation_table_entry_t tree_mode_tt[] = { {"in", IGRAPH_TREE_IN}, {"out", IGRAPH_TREE_OUT}, {"all", IGRAPH_TREE_UNDIRECTED}, {"undirected", IGRAPH_TREE_UNDIRECTED}, {"tree_in", IGRAPH_TREE_IN}, {"tree_out", IGRAPH_TREE_OUT}, {"tree_all", IGRAPH_TREE_UNDIRECTED}, {0,0} }; return igraphmodule_PyObject_to_enum(o, tree_mode_tt, (int*)result); } /** * \brief Extracts a pointer to the internal \c igraph_t from a graph object * * Raises suitable Python exceptions when needed. * * \param object the Python object to be converted. If it is Py_None, the * result pointer is untouched (so it should be null by default). * \param result the pointer is stored here * * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_to_igraph_t(PyObject *o, igraph_t **result) { if (o == Py_None) return 0; if (!PyObject_TypeCheck(o, &igraphmodule_GraphType)) { PyErr_Format(PyExc_TypeError, "expected graph object, got %s", o->ob_type->tp_name); return 1; } *result = &((igraphmodule_GraphObject*)o)->g; return 0; } /** * \brief Converts a Python object to an igraph \c igraph_integer_t * * Raises suitable Python exceptions when needed. * * \param object the Python object to be converted * \param v the result is returned here * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_to_integer_t(PyObject *object, igraph_integer_t *v) { int retval, num; if (object == NULL) { } else if (PyLong_Check(object)) { retval = PyLong_AsInt(object, &num); if (retval) return retval; *v = num; return 0; #ifdef IGRAPH_PYTHON3 } else if (PyNumber_Check(object)) { PyObject *i = PyNumber_Int(object); if (i == NULL) return 1; retval = PyInt_AsInt(i, &num); Py_DECREF(i); if (retval) return retval; *v = num; return 0; } #else } else if (PyInt_Check(object)) { retval = PyInt_AsInt(object, &num); if (retval) return retval; *v = num; return 0; } else if (PyNumber_Check(object)) { PyObject *i = PyNumber_Int(object); if (i == NULL) return 1; retval = PyInt_AsInt(i, &num); Py_DECREF(i); if (retval) return retval; *v = num; return 0; } #endif PyErr_BadArgument(); return 1; } /** * \brief Converts a Python object to an igraph \c igraph_real_t * * Raises suitable Python exceptions when needed. * * \param object the Python object to be converted * \param v the result is returned here * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_to_real_t(PyObject *object, igraph_real_t *v) { if (object == NULL) { } else if (PyLong_Check(object)) { double d = PyLong_AsDouble(object); *v=(igraph_real_t)d; return 0; #ifndef IGRAPH_PYTHON3 } else if (PyInt_Check(object)) { long l = PyInt_AS_LONG((PyIntObject*)object); *v=(igraph_real_t)l; return 0; #endif } else if (PyFloat_Check(object)) { double d = PyFloat_AS_DOUBLE((PyFloatObject*)object); *v=(igraph_real_t)d; return 0; } else if (PyNumber_Check(object)) { PyObject *i = PyNumber_Float(object); double d; if (i == NULL) return 1; d = PyFloat_AS_DOUBLE((PyFloatObject*)i); Py_DECREF(i); *v = (igraph_real_t)d; return 0; } PyErr_BadArgument(); return 1; } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_vector_t * The incoming \c igraph_vector_t should be uninitialized. Raises suitable * Python exceptions when needed. * * \param list the Python list to be converted * \param v the \c igraph_vector_t containing the result * \param need_non_negative if true, checks whether all elements are non-negative * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_to_vector_t(PyObject *list, igraph_vector_t *v, igraph_bool_t need_non_negative) { PyObject *item, *it; Py_ssize_t size_hint; int ok; igraph_integer_t number; if (PyBaseString_Check(list)) { /* It is highly unlikely that a string (although it is a sequence) will * provide us with integers */ PyErr_SetString(PyExc_TypeError, "expected a sequence or an iterable containing integers"); return 1; } /* if the list is a sequence, we can pre-allocate the vector to its length */ if (PySequence_Check(list)) { size_hint = PySequence_Size(list); if (size_hint < 0) { /* should not happen but let's try to recover */ size_hint = 0; } } else { size_hint = 0; } /* initialize the result vector */ if (igraph_vector_init(v, 0)) { igraphmodule_handle_igraph_error(); return 1; } /* if we have a size hint, allocate the required space */ if (size_hint > 0) { if (igraph_vector_reserve(v, size_hint)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(v); return 1; } } /* try to use an iterator first */ it = PyObject_GetIter(list); if (it) { while ((item = PyIter_Next(it)) != 0) { ok = 1; if (igraphmodule_PyObject_to_integer_t(item, &number)) { PyErr_SetString(PyExc_ValueError, "iterable must yield integers"); ok=0; } else { if (need_non_negative && number < 0) { PyErr_SetString(PyExc_ValueError, "iterable must yield non-negative integers"); ok=0; } } Py_DECREF(item); if (!ok) { igraph_vector_destroy(v); Py_DECREF(it); return 1; } if (igraph_vector_push_back(v, number)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(v); Py_DECREF(it); return 1; } } Py_DECREF(it); } else { /* list is not iterable; maybe it's a single number? */ if (igraphmodule_PyObject_to_integer_t(list, &number)) { PyErr_SetString(PyExc_TypeError, "sequence or iterable expected"); igraph_vector_destroy(v); return 1; } else { if (need_non_negative && number < 0) { PyErr_SetString(PyExc_ValueError, "non-negative integers expected"); igraph_vector_destroy(v); return 1; } igraph_vector_push_back(v, number); } } return 0; } /** * \ingroup python_interface_conversion * \brief Converts a Python list of floats to an igraph \c igraph_vector_t * The incoming \c igraph_vector_t should be uninitialized. Raises suitable * Python exceptions when needed. * * \param list the Python list to be converted * \param v the \c igraph_vector_t containing the result * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_float_to_vector_t(PyObject *list, igraph_vector_t *v) { PyObject *item, *it; Py_ssize_t size_hint; int ok; igraph_real_t number; if (PyBaseString_Check(list)) { /* It is highly unlikely that a string (although it is a sequence) will * provide us with numbers */ PyErr_SetString(PyExc_TypeError, "expected a sequence or an iterable containing numbers"); return 1; } /* if the list is a sequence, we can pre-allocate the vector to its length */ if (PySequence_Check(list)) { size_hint = PySequence_Size(list); if (size_hint < 0) { /* should not happen but let's try to recover */ size_hint = 0; } } else { size_hint = 0; } /* initialize the result vector */ if (igraph_vector_init(v, 0)) { igraphmodule_handle_igraph_error(); return 1; } /* if we have a size hint, allocate the required space */ if (size_hint > 0) { if (igraph_vector_reserve(v, size_hint)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(v); return 1; } } /* try to use an iterator first */ it = PyObject_GetIter(list); if (it) { while ((item = PyIter_Next(it)) != 0) { ok = 1; if (igraphmodule_PyObject_to_real_t(item, &number)) { PyErr_SetString(PyExc_ValueError, "iterable must yield numbers"); ok=0; } Py_DECREF(item); if (!ok) { igraph_vector_destroy(v); Py_DECREF(it); return 1; } if (igraph_vector_push_back(v, number)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(v); Py_DECREF(it); return 1; } } Py_DECREF(it); } else { /* list is not iterable; maybe it's a single number? */ if (igraphmodule_PyObject_to_real_t(list, &number)) { PyErr_SetString(PyExc_TypeError, "sequence or iterable expected"); igraph_vector_destroy(v); return 1; } else { igraph_vector_push_back(v, number); } } return 0; } /** * \ingroup python_interface_conversion * \brief Converts a Python list of ints to an igraph \c igraph_vector_int_t * The incoming \c igraph_vector_int_t should be uninitialized. * Raises suitable Python exceptions when needed. * * This function is almost identical to * \ref igraphmodule_PyObject_to_vector_t . Make sure you fix bugs * in both cases (if any). * * \param list the Python list to be converted * \param v the \c igraph_vector_int_t containing the result * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_to_vector_int_t(PyObject *list, igraph_vector_int_t *v) { PyObject *item; int value=0; Py_ssize_t i, j, k; int ok, retval; if (PyBaseString_Check(list)) { /* It is highly unlikely that a string (although it is a sequence) will * provide us with integers or integer pairs */ PyErr_SetString(PyExc_TypeError, "expected a sequence or an iterable containing integers"); return 1; } if (!PySequence_Check(list)) { /* try to use an iterator */ PyObject *it = PyObject_GetIter(list); if (it) { PyObject *item; igraph_vector_int_init(v, 0); while ((item = PyIter_Next(it)) != 0) { ok = 1; if (!PyNumber_Check(item)) { PyErr_SetString(PyExc_TypeError, "iterable must return numbers"); ok=0; } else { PyObject *item2 = PyNumber_Int(item); if (item2 == 0) { PyErr_SetString(PyExc_TypeError, "can't convert a list item to integer"); ok = 0; } else { ok = (PyInt_AsInt(item, &value) == 0); Py_DECREF(item2); } } if (ok == 0) { igraph_vector_int_destroy(v); Py_DECREF(item); Py_DECREF(it); return 1; } if (igraph_vector_int_push_back(v, value)) { igraphmodule_handle_igraph_error(); igraph_vector_int_destroy(v); Py_DECREF(item); Py_DECREF(it); return 1; } Py_DECREF(item); } Py_DECREF(it); return 0; } else { PyErr_SetString(PyExc_TypeError, "sequence or iterable expected"); return 1; } return 0; } j=PySequence_Size(list); igraph_vector_int_init(v, j); for (i=0, k=0; i>=1; list=PyList_New(n); /* populate the list with data */ for (i=0, j=0; ig); } else { et = IGRAPH_ATTRIBUTE_EDGE; n = igraph_ecount(&self->g); } if (igraphmodule_i_attribute_get_type(&self->g, &at, et, name)) { /* exception was set by igraphmodule_i_attribute_get_type */ free(name); return 1; } if (at != IGRAPH_ATTRIBUTE_NUMERIC) { PyErr_SetString(PyExc_ValueError, "attribute values must be numeric"); free(name); return 1; } /* Now that the attribute type has been checked, allocate the target * vector */ result = (igraph_vector_t*)calloc(1, sizeof(igraph_vector_t)); if (result==0) { PyErr_NoMemory(); free(name); return 1; } igraph_vector_init(result, n); if (attr_type == ATTRIBUTE_TYPE_VERTEX) { if (igraphmodule_i_get_numeric_vertex_attr(&self->g, name, igraph_vss_all(), result)) { /* exception has already been set, so return */ igraph_vector_destroy(result); free(name); free(result); return 1; } } else { if (igraphmodule_i_get_numeric_edge_attr(&self->g, name, igraph_ess_all(IGRAPH_EDGEORDER_ID), result)) { /* exception has already been set, so return */ igraph_vector_destroy(result); free(name); free(result); return 1; } } free(name); *vptr = result; } else if (PySequence_Check(o)) { result = (igraph_vector_t*)calloc(1, sizeof(igraph_vector_t)); if (result==0) { PyErr_NoMemory(); return 1; } if (igraphmodule_PyObject_float_to_vector_t(o, result)) { igraph_vector_destroy(result); free(result); return 1; } *vptr = result; } else { PyErr_SetString(PyExc_TypeError, "unhandled type"); return 1; } return 0; } /** * \ingroup python_interface_conversion * \brief Converts an attribute name or a sequence to a vector_int_t * * Similar to igraphmodule_attrib_to_vector_t and * igraphmodule_attrib_to_vector_long_t. Make sure you fix bugs * in all three places (if any). * * Note that if the function returned a pointer to an \c igraph_vector_int_t, * it is the caller's responsibility to destroy the object and free its * pointer after having finished using it. * * \param o the Python object being converted. * \param self a Python Graph object being used when attributes are queried * \param vptr the pointer to the allocated vector is returned here. * \param attr_type the type of the attribute being handled * \return 0 if everything was OK, nonzero otherwise. */ int igraphmodule_attrib_to_vector_int_t(PyObject *o, igraphmodule_GraphObject *self, igraph_vector_int_t **vptr, int attr_type) { igraph_vector_int_t *result; *vptr = 0; if (attr_type != ATTRIBUTE_TYPE_EDGE && attr_type != ATTRIBUTE_TYPE_VERTEX) return 1; if (o == Py_None) return 0; if (PyString_Check(o)) { igraph_vector_t* dummy = 0; long int i, n; if (igraphmodule_attrib_to_vector_t(o, self, &dummy, attr_type)) return 1; if (dummy == 0) return 0; n = igraph_vector_size(dummy); result = (igraph_vector_int_t*)calloc(1, sizeof(igraph_vector_int_t)); igraph_vector_int_init(result, n); if (result==0) { igraph_vector_destroy(dummy); free(dummy); PyErr_NoMemory(); return 1; } for (i=0; ig); } else { et = IGRAPH_ATTRIBUTE_EDGE; n = igraph_ecount(&self->g); } if (igraphmodule_i_attribute_get_type(&self->g, &at, et, name)) { /* exception was set by igraphmodule_i_attribute_get_type */ free(name); return 1; } if (at == IGRAPH_ATTRIBUTE_BOOLEAN) { /* The attribute is a real Boolean attribute. Allocate the target * vector */ result = (igraph_vector_bool_t*)calloc(1, sizeof(igraph_vector_bool_t)); if (result==0) { PyErr_NoMemory(); free(name); return 1; } igraph_vector_bool_init(result, n); if (attr_type == ATTRIBUTE_TYPE_VERTEX) { if (igraphmodule_i_get_boolean_vertex_attr(&self->g, name, igraph_vss_all(), result)) { /* exception has already been set, so return */ igraph_vector_bool_destroy(result); free(name); free(result); return 1; } } else { if (igraphmodule_i_get_boolean_edge_attr(&self->g, name, igraph_ess_all(IGRAPH_EDGEORDER_ID), result)) { /* exception has already been set, so return */ igraph_vector_bool_destroy(result); free(name); free(result); return 1; } } free(name); *vptr = result; } else if (at == IGRAPH_ATTRIBUTE_NUMERIC) { /* The attribute is a numeric attribute, so we fall back to * attrib_to_vector_t and then convert the result */ igraph_vector_t *dummy = 0; free(name); if (igraphmodule_attrib_to_vector_t(o, self, &dummy, attr_type)) { return 1; } if (dummy == 0) { return 0; } n = igraph_vector_size(dummy); result = (igraph_vector_bool_t*)calloc(1, sizeof(igraph_vector_bool_t)); igraph_vector_bool_init(result, n); if (result==0) { igraph_vector_destroy(dummy); free(dummy); PyErr_NoMemory(); return 1; } for (i=0; inc) nc=n; } igraph_matrix_init(m, nr, nc); for (i=0; ig); Py_DECREF(t); } return 0; } /** * \ingroup python_interface_conversion * \brief Tries to interpret a Python object as a single vertex ID * * \param o the Python object * \param vid the vertex ID will be stored here * \param graph the graph that will be used to interpret vertex names * if a string was given in o. It may also be a null pointer * if we don't need name lookups. * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_to_vid(PyObject *o, igraph_integer_t *vid, igraph_t *graph) { int retval, tmp; if (o == Py_None || o == 0) { *vid = 0; } else if (PyInt_Check(o)) { /* Single vertex ID */ if (PyInt_AsInt(o, &tmp)) return 1; *vid = tmp; } else if (PyLong_Check(o)) { /* Single vertex ID */ if (PyLong_AsInt(o, &tmp)) return 1; *vid = tmp; } else if (graph != 0 && PyBaseString_Check(o)) { /* Single vertex ID from vertex name */ if (igraphmodule_get_vertex_id_by_name(graph, o, vid)) return 1; } else if (PyObject_IsInstance(o, (PyObject*)&igraphmodule_VertexType)) { /* Single vertex ID from Vertex object */ igraphmodule_VertexObject *vo = (igraphmodule_VertexObject*)o; *vid = igraphmodule_Vertex_get_index_igraph_integer(vo); } else if (PyIndex_Check(o)) { /* Other numeric type that can be converted to an index */ PyObject* num = PyNumber_Index(o); if (num) { if (PyInt_Check(num)) { retval = PyInt_AsInt(num, &tmp); if (retval) { Py_DECREF(num); return 1; } *vid = tmp; } else if (PyLong_Check(num)) { retval = PyLong_AsInt(num, &tmp); if (retval) { Py_DECREF(num); return 1; } *vid = tmp; } else { PyErr_SetString(PyExc_TypeError, "PyNumber_Index returned invalid type"); Py_DECREF(num); return 1; } Py_DECREF(num); } else return 1; } else { PyErr_SetString(PyExc_TypeError, "only numbers, vertex names or igraph.Vertex objects can be converted to vertex IDs"); return 1; } if (*vid < 0) { PyErr_Format(PyExc_ValueError, "vertex IDs must be positive, got: %ld", (long)(*vid)); return 1; } return 0; } /** * \ingroup python_interface_conversion * \brief Tries to interpret a Python object as a vertex selector * * \param o the Python object * \param vs the \c igraph_vs_t which will contain the result * \param graph an \c igraph_t object which will be used to interpret vertex * names (if the supplied Python object contains strings) * \param return_single will be 1 if the selector selected only a single vertex, * 0 otherwise * \param single_vid if the selector selected only a single vertex, the ID * of the selected vertex will also be returned here. * * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_to_vs_t(PyObject *o, igraph_vs_t *vs, igraph_t *graph, igraph_bool_t *return_single, igraph_integer_t *single_vid) { igraph_integer_t vid; igraph_vector_t vector; if (o == 0 || o == Py_None) { /* Returns a vertex sequence for all vertices */ if (return_single) *return_single = 0; igraph_vs_all(vs); return 0; } if (PyObject_IsInstance(o, (PyObject*)&igraphmodule_VertexSeqType)) { /* Returns a vertex sequence from a VertexSeq object */ igraphmodule_VertexSeqObject *vso = (igraphmodule_VertexSeqObject*)o; if (igraph_vs_copy(vs, &vso->vs)) { igraphmodule_handle_igraph_error(); return 1; } if (return_single) *return_single = 0; return 0; } if (PySlice_Check(o) && graph != 0) { /* Returns a vertex sequence from a slice */ Py_ssize_t no_of_vertices = igraph_vcount(graph); Py_ssize_t start, stop, step, slicelength, i; /* Casting to void* because Python 2.x expects PySliceObject* * but Python 3.x expects PyObject* */ if (PySlice_GetIndicesEx((void*)o, no_of_vertices, &start, &stop, &step, &slicelength)) return 1; if (start == 0 && slicelength == no_of_vertices) { igraph_vs_all(vs); } else { IGRAPH_CHECK(igraph_vector_init(&vector, slicelength)); IGRAPH_FINALLY(igraph_vector_destroy, &vector); for (i = 0; i < slicelength; i++, start += step) { VECTOR(vector)[i] = start; } IGRAPH_CHECK(igraph_vs_vector_copy(vs, &vector)); igraph_vector_destroy(&vector); IGRAPH_FINALLY_CLEAN(1); } if (return_single) *return_single = 0; return 0; } if (igraphmodule_PyObject_to_vid(o, &vid, graph)) { /* Object cannot be converted to a single vertex ID, * assume it is a sequence or iterable */ PyObject *iterator; PyObject *item; if (PyBaseString_Check(o)) { /* Special case: strings and unicode objects are sequences, but they * will not yield valid vertex IDs */ return 1; } /* Clear the exception set by igraphmodule_PyObject_to_vid */ PyErr_Clear(); iterator = PyObject_GetIter(o); if (iterator == NULL) { PyErr_SetString(PyExc_TypeError, "conversion to vertex sequence failed"); return 1; } IGRAPH_CHECK(igraph_vector_init(&vector, 0)); IGRAPH_FINALLY(igraph_vector_destroy, &vector); IGRAPH_CHECK(igraph_vector_reserve(&vector, 20)); while ((item = PyIter_Next(iterator))) { vid = -1; if (igraphmodule_PyObject_to_vid(item, &vid, graph)) break; Py_DECREF(item); igraph_vector_push_back(&vector, vid); } Py_DECREF(iterator); if (PyErr_Occurred()) { igraph_vector_destroy(&vector); IGRAPH_FINALLY_CLEAN(1); return 1; } IGRAPH_CHECK(igraph_vs_vector_copy(vs, &vector)); igraph_vector_destroy(&vector); IGRAPH_FINALLY_CLEAN(1); if (return_single) *return_single = 0; return 0; } /* The object can be converted into a single vertex ID */ if (return_single) *return_single = 1; if (single_vid) *single_vid = vid; igraph_vs_1(vs, vid); return 0; } /** * \ingroup python_interface_conversion * \brief Tries to interpret a Python object as a single edge ID * * \param o the Python object * \param eid the edge ID will be stored here * \param graph the graph that will be used to interpret vertex names and * indices if o is a tuple. It may also be a null pointer * if we don't want to handle tuples. * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_to_eid(PyObject *o, igraph_integer_t *eid, igraph_t *graph) { int retval, tmp; igraph_integer_t vid1, vid2; if (o == Py_None || o == 0) { *eid = 0; } else if (PyInt_Check(o)) { /* Single edge ID */ if (PyInt_AsInt(o, &tmp)) return 1; *eid = tmp; } else if (PyLong_Check(o)) { /* Single edge ID */ if (PyLong_AsInt(o, &tmp)) return 1; *eid = tmp; } else if (PyObject_IsInstance(o, (PyObject*)&igraphmodule_EdgeType)) { /* Single edge ID from Edge object */ igraphmodule_EdgeObject *eo = (igraphmodule_EdgeObject*)o; *eid = igraphmodule_Edge_get_index_igraph_integer(eo); } else if (PyIndex_Check(o)) { /* Other numeric type that can be converted to an index */ PyObject* num = PyNumber_Index(o); if (num) { if (PyInt_Check(num)) { retval = PyInt_AsInt(num, &tmp); if (retval) { Py_DECREF(num); return 1; } *eid = tmp; } else if (PyLong_Check(num)) { retval = PyLong_AsInt(num, &tmp); if (retval) { Py_DECREF(num); return 1; } *eid = tmp; } else { PyErr_SetString(PyExc_TypeError, "PyNumber_Index returned invalid type"); Py_DECREF(num); return 1; } Py_DECREF(num); } else return 1; } else if (graph != 0 && PyTuple_Check(o)) { PyObject *o1, *o2; o1 = PyTuple_GetItem(o, 0); if (!o1) return 1; o2 = PyTuple_GetItem(o, 1); if (!o2) return 1; if (igraphmodule_PyObject_to_vid(o1, &vid1, graph)) return 1; if (igraphmodule_PyObject_to_vid(o2, &vid2, graph)) return 1; igraph_get_eid(graph, eid, vid1, vid2, 1, 0); if (*eid < 0) { PyErr_Format(PyExc_ValueError, "no edge from vertex #%ld to #%ld", (long int)vid1, (long int)vid2); return 1; } } else { PyErr_SetString(PyExc_TypeError, "only numbers, igraph.Edge objects or tuples of vertex IDs can be " "converted to edge IDs"); return 1; } if (*eid < 0) { PyErr_Format(PyExc_ValueError, "edge IDs must be positive, got: %ld", (long)(*eid)); return 1; } return 0; } /** * \ingroup python_interface_conversion * \brief Tries to interpret a Python object as an edge selector * * \param o the Python object * \param vs the \c igraph_es_t which will contain the result * \param graph an \c igraph_t object which will be used to interpret vertex * names and tuples (if the supplied Python object contains them) * \param return_single will be 1 if the selector selected only a single edge, * 0 otherwise * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_to_es_t(PyObject *o, igraph_es_t *es, igraph_t *graph, igraph_bool_t *return_single) { igraph_integer_t eid; igraph_vector_t vector; if (o == 0 || o == Py_None) { /* Returns an edge sequence for all edges */ if (return_single) *return_single = 0; igraph_es_all(es, IGRAPH_EDGEORDER_ID); return 0; } if (PyObject_IsInstance(o, (PyObject*)&igraphmodule_EdgeSeqType)) { /* Returns an edge sequence from an EdgeSeq object */ igraphmodule_EdgeSeqObject *eso = (igraphmodule_EdgeSeqObject*)o; if (igraph_es_copy(es, &eso->es)) { igraphmodule_handle_igraph_error(); return 1; } if (return_single) *return_single = 0; return 0; } if (igraphmodule_PyObject_to_eid(o, &eid, graph)) { /* Object cannot be converted to a single edge ID, * assume it is a sequence or iterable */ PyObject *iterator; PyObject *item; /* Clear the exception set by igraphmodule_PyObject_to_eid */ PyErr_Clear(); iterator = PyObject_GetIter(o); if (iterator == NULL) { PyErr_SetString(PyExc_TypeError, "conversion to edge sequene failed"); return 1; } IGRAPH_CHECK(igraph_vector_init(&vector, 0)); IGRAPH_FINALLY(igraph_vector_destroy, &vector); IGRAPH_CHECK(igraph_vector_reserve(&vector, 20)); while ((item = PyIter_Next(iterator))) { eid = -1; if (igraphmodule_PyObject_to_eid(item, &eid, graph)) break; Py_DECREF(item); igraph_vector_push_back(&vector, eid); } Py_DECREF(iterator); if (PyErr_Occurred()) { igraph_vector_destroy(&vector); IGRAPH_FINALLY_CLEAN(1); return 1; } if (igraph_vector_size(&vector) > 0) { igraph_es_vector_copy(es, &vector); } else { igraph_es_none(es); } igraph_vector_destroy(&vector); IGRAPH_FINALLY_CLEAN(1); if (return_single) *return_single = 0; return 0; } /* The object can be converted into a single edge ID */ if (return_single) *return_single = 1; /* if (single_eid) *single_eid = eid; */ igraph_es_1(es, eid); return 0; } /** * \ingroup python_interface_conversion * \brief Tries to interpret a Python object as a numeric attribute value list * * \param o the Python object * \param v the \c igraph_vector_t which will contain the result * \param g a \c igraphmodule_GraphObject object or \c NULL - used when the * provided Python object is not a list and we're trying to interpret it as * an attribute name. * \param type the attribute type (graph = 0, vertex = 1, edge = 2) to be used * \param def default value if the attribute name supplied is \c None * if \c o is not a list. * \return 0 if everything was OK, 1 otherwise * * If the Python object is not a list, tries to interpret it as an attribute * name. */ int igraphmodule_PyObject_to_attribute_values(PyObject *o, igraph_vector_t *v, igraphmodule_GraphObject* g, int type, igraph_real_t def) { PyObject* list = o; long i, n; if (o==NULL) return 1; if (o == Py_None) { if (type == ATTRHASH_IDX_VERTEX) n=igraph_vcount(&g->g); else if (type == ATTRHASH_IDX_EDGE) n=igraph_ecount(&g->g); else n=1; if (igraph_vector_init(v, n)) return 1; for (i=0; ig.attr)[type], o); if (!list) { if (!PyErr_Occurred()) PyErr_SetString(PyExc_KeyError, "Attribute does not exist"); return 1; } } n=PyList_Size(list); if (igraph_vector_init(v, n)) return 1; for (i=0; iOPTION); \ Py_XDECREF(o1); \ } \ o1 = PyObject_GetAttrString(obj, #OPTION); \ igraphmodule_PyObject_to_##TYPE##_t(o1, &options->OPTION); \ Py_XDECREF(o1); \ } while (0) #define CONVERT_DRL_OPTION_BLOCK(NAME) do { \ CONVERT_DRL_OPTION(NAME##_iterations, integer); \ CONVERT_DRL_OPTION(NAME##_temperature, real); \ CONVERT_DRL_OPTION(NAME##_attraction, real); \ CONVERT_DRL_OPTION(NAME##_damping_mult, real); \ } while (0) CONVERT_DRL_OPTION(edge_cut, real); CONVERT_DRL_OPTION_BLOCK(init); CONVERT_DRL_OPTION_BLOCK(liquid); CONVERT_DRL_OPTION_BLOCK(expansion); CONVERT_DRL_OPTION_BLOCK(cooldown); CONVERT_DRL_OPTION_BLOCK(crunch); CONVERT_DRL_OPTION_BLOCK(simmer); #undef CONVERT_DRL_OPTION #undef CONVERT_DRL_OPTION_BLOCK PyErr_Clear(); return 0; } return 0; } int igraphmodule_i_PyObject_pair_to_attribute_combination_record_t( PyObject* name, PyObject* value, igraph_attribute_combination_record_t *result) { if (igraphmodule_PyObject_to_attribute_combination_type_t(value, &result->type)) return 1; if (result->type == IGRAPH_ATTRIBUTE_COMBINE_FUNCTION) { result->func = value; } else { result->func = 0; } if (name == Py_None) result->name = 0; else if (!PyString_Check(name)) { PyErr_SetString(PyExc_TypeError, "keys must be strings or None in attribute combination specification dicts"); return 1; } else { #ifdef IGRAPH_PYTHON3 result->name = PyString_CopyAsString(name); #else result->name = PyString_AS_STRING(name); #endif } return 0; } /** * \brief Converts a Python object to an \c igraph_attribute_combination_t * * Raises suitable Python exceptions when needed. * * An \c igraph_attribute_combination_t specifies how the attributes of multiple * vertices/edges should be combined when they are collapsed into a single vertex * or edge (e.g., when simplifying a graph). For each attribute, one can specify * a Python callable object to call or one of a list of recognised strings which * map to simple functions. The recognised strings are as follows: * * - \c "ignore" - the attribute will be ignored * - \c "sum" - the attribute values will be added * - \c "prod" - the product of the attribute values will be taken * - \c "min" - the minimum attribute value will be used * - \c "max" - the maximum attribute value will be used * - \c "random" - a random value will be selected * - \c "first" - the first value encountered will be selected * - \c "last" - the last value encountered will be selected * - \c "mean" - the mean of the attributes will be selected * - \c "median" - the median of the attributes will be selected * - \c "concat" - the attribute values will be concatenated * * The Python object being converted must either be a string, a callable or a dict. * If a string is given, it is considered as an \c igraph_attribute_combination_t * object that combines all attributes according to the function given by that * string. If a callable is given, it is considered as an * \c igraph_attribute_combination_t that combines all attributes by calling the * callable and taking its return value. If a dict is given, its key-value pairs * are iterated, the keys specify the attribute names (a key of None means all * explicitly not specified attributes), the values specify the functions to * call for those attributes. * * \param object the Python object to be converted * \param result the result is returned here. It must be an uninitialized * \c igraph_attribute_combination_t object, it will be initialized accordingly. * It is the responsibility of the caller to * \return 0 if everything was OK, 1 otherwise */ int igraphmodule_PyObject_to_attribute_combination_t(PyObject* object, igraph_attribute_combination_t *result) { igraph_attribute_combination_record_t rec; if (igraph_attribute_combination_init(result)) { igraphmodule_handle_igraph_error(); return 1; } if (object == Py_None) { return 0; } if (PyDict_Check(object)) { /* a full-fledged dict was passed */ PyObject *key, *value; Py_ssize_t pos = 0; while (PyDict_Next(object, &pos, &key, &value)) { if (igraphmodule_i_PyObject_pair_to_attribute_combination_record_t(key, value, &rec)) { igraph_attribute_combination_destroy(result); return 1; } igraph_attribute_combination_add(result, rec.name, rec.type, rec.func); #ifdef IGRAPH_PYTHON3 free((char*)rec.name); /* was allocated in pair_to_attribute_combination_record_t above */ #endif } } else { /* assume it is a string or callable */ if (igraphmodule_i_PyObject_pair_to_attribute_combination_record_t(Py_None, object, &rec)) { igraph_attribute_combination_destroy(result); return 1; } igraph_attribute_combination_add(result, 0, rec.type, rec.func); #ifdef IGRAPH_PYTHON3 free((char*)rec.name); /* was allocated in pair_to_attribute_combination_record_t above */ #endif } return 0; } /** * \ingroup python_interface_conversion * \brief Converts a Python object to an igraph \c igraph_pagerank_algo_t */ int igraphmodule_PyObject_to_pagerank_algo_t(PyObject *o, igraph_pagerank_algo_t *result) { static igraphmodule_enum_translation_table_entry_t pagerank_algo_tt[] = { {"prpack", IGRAPH_PAGERANK_ALGO_PRPACK}, {"arpack", IGRAPH_PAGERANK_ALGO_ARPACK}, {"power", IGRAPH_PAGERANK_ALGO_POWER}, {0,0} }; return igraphmodule_PyObject_to_enum(o, pagerank_algo_tt, (int*)result); } python-igraph-0.7.1.post6/src/convert.h0000644000076500000240000001661312534342712020422 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /************************ Miscellaneous functions *************************/ /** \defgroup python_interface_conversion Converting between Python and igraph data types * \ingroup python_interface */ #ifndef PYTHON_CONVERT_H #define PYTHON_CONVERT_H #include #include #include #include "graphobject.h" typedef enum { IGRAPHMODULE_TYPE_INT=0, IGRAPHMODULE_TYPE_FLOAT } igraphmodule_conv_t; typedef struct { const char* name; int value; } igraphmodule_enum_translation_table_entry_t; int PyInt_AsInt(PyObject* obj, int* result); int PyLong_AsInt(PyObject* obj, int* result); /* Conversion from PyObject to enum types */ int igraphmodule_PyObject_to_enum(PyObject *o, igraphmodule_enum_translation_table_entry_t *table, int *result); int igraphmodule_PyObject_to_add_weights_t(PyObject *o, igraph_add_weights_t *result); int igraphmodule_PyObject_to_adjacency_t(PyObject *o, igraph_adjacency_t *result); int igraphmodule_PyObject_to_attribute_combination_type_t(PyObject* o, igraph_attribute_combination_type_t *type); int igraphmodule_PyObject_to_barabasi_algorithm_t(PyObject *o, igraph_barabasi_algorithm_t *result); int igraphmodule_PyObject_to_bliss_sh_t(PyObject *o, igraph_bliss_sh_t *result); int igraphmodule_PyObject_to_community_comparison_t(PyObject *obj, igraph_community_comparison_t *result); int igraphmodule_PyObject_to_connectedness_t(PyObject *o, igraph_connectedness_t *result); int igraphmodule_PyObject_to_degseq_t(PyObject *o, igraph_degseq_t *result); int igraphmodule_PyObject_to_fas_algorithm_t(PyObject *o, igraph_fas_algorithm_t *result); /* int igraphmodule_PyObject_to_layout_grid_t(PyObject *o, igraph_layout_grid_t *result); */ int igraphmodule_PyObject_to_neimode_t(PyObject *o, igraph_neimode_t *result); int igraphmodule_PyObject_to_pagerank_algo_t(PyObject *o, igraph_pagerank_algo_t *result); /* int igraphmodule_PyObject_to_random_walk_stuck_t(PyObject *o, igraph_random_walk_stuck_t *result); */ int igraphmodule_PyObject_to_reciprocity_t(PyObject *o, igraph_reciprocity_t *result); int igraphmodule_PyObject_to_rewiring_t(PyObject *o, igraph_rewiring_t *result); int igraphmodule_PyObject_to_spinglass_implementation_t(PyObject *o, igraph_spinglass_implementation_t *result); int igraphmodule_PyObject_to_spincomm_update_t(PyObject *o, igraph_spincomm_update_t *result); int igraphmodule_PyObject_to_star_mode_t(PyObject *o, igraph_star_mode_t *result); int igraphmodule_PyObject_to_subgraph_implementation_t(PyObject *o, igraph_subgraph_implementation_t *result); int igraphmodule_PyObject_to_to_undirected_t(PyObject *o, igraph_to_undirected_t *result); int igraphmodule_PyObject_to_transitivity_mode_t(PyObject *o, igraph_transitivity_mode_t *result); int igraphmodule_PyObject_to_tree_mode_t(PyObject *o, igraph_tree_mode_t *result); int igraphmodule_PyObject_to_vconn_nei_t(PyObject *o, igraph_vconn_nei_t *result); /* Conversion from PyObject to igraph types */ int igraphmodule_PyObject_to_integer_t(PyObject *object, igraph_integer_t *v); int igraphmodule_PyObject_to_real_t(PyObject *object, igraph_real_t *v); int igraphmodule_PyObject_to_igraph_t(PyObject *o, igraph_t **result); int igraphmodule_PyObject_to_vector_t(PyObject *list, igraph_vector_t *v, igraph_bool_t need_non_negative); int igraphmodule_PyObject_float_to_vector_t(PyObject *list, igraph_vector_t *v); int igraphmodule_PyObject_to_vector_int_t(PyObject *list, igraph_vector_int_t *v); int igraphmodule_PyObject_to_vector_long_t(PyObject *list, igraph_vector_long_t *v); int igraphmodule_PyObject_to_vector_bool_t(PyObject *list, igraph_vector_bool_t *v); int igraphmodule_PyObject_to_vector_ptr_t(PyObject *list, igraph_vector_ptr_t *v, igraph_bool_t need_non_negative); int igraphmodule_PyObject_to_edgelist(PyObject *list, igraph_vector_t *v, igraph_t *graph); int igraphmodule_PyList_to_matrix_t(PyObject *o, igraph_matrix_t *m); PyObject* igraphmodule_strvector_t_to_PyList(igraph_strvector_t *v); int igraphmodule_PyList_to_strvector_t(PyObject* v, igraph_strvector_t *result); int igraphmodule_append_PyIter_of_graphs_to_vector_ptr_t(PyObject *it, igraph_vector_ptr_t *v); int igraphmodule_PyObject_to_vid(PyObject *o, igraph_integer_t *vid, igraph_t *graph); int igraphmodule_PyObject_to_vs_t(PyObject *o, igraph_vs_t *vs, igraph_t *graph, igraph_bool_t *return_single, igraph_integer_t *single_vid); int igraphmodule_PyObject_to_eid(PyObject *o, igraph_integer_t *eid, igraph_t *graph); int igraphmodule_PyObject_to_es_t(PyObject *o, igraph_es_t *es, igraph_t *graph, igraph_bool_t *return_single); int igraphmodule_PyObject_to_attribute_values(PyObject *o, igraph_vector_t *v, igraphmodule_GraphObject* g, int type, igraph_real_t def); int igraphmodule_PyObject_to_drl_options_t(PyObject *obj, igraph_layout_drl_options_t *options); int igraphmodule_PyObject_to_attribute_combination_t(PyObject* object, igraph_attribute_combination_t *type); int igraphmodule_PyObject_to_eigen_algorithm_t(PyObject *object, igraph_eigen_algorithm_t *a); int igraphmodule_PyObject_to_eigen_which_t(PyObject *object, igraph_eigen_which_t *w); /* Conversion from attributes to igraph types */ int igraphmodule_attrib_to_vector_t(PyObject *o, igraphmodule_GraphObject *self, igraph_vector_t **vptr, int attr_type); int igraphmodule_attrib_to_vector_int_t(PyObject *o, igraphmodule_GraphObject *self, igraph_vector_int_t **vptr, int attr_type); int igraphmodule_attrib_to_vector_long_t(PyObject *o, igraphmodule_GraphObject *self, igraph_vector_long_t **vptr, int attr_type); int igraphmodule_attrib_to_vector_bool_t(PyObject *o, igraphmodule_GraphObject *self, igraph_vector_bool_t **vptr, int attr_type); /* Conversion from igraph types to PyObjects */ PyObject* igraphmodule_vector_bool_t_to_PyList(const igraph_vector_bool_t *v); PyObject* igraphmodule_vector_t_to_PyList(const igraph_vector_t *v, igraphmodule_conv_t type); PyObject* igraphmodule_vector_t_to_PyTuple(const igraph_vector_t *v); PyObject* igraphmodule_vector_t_pair_to_PyList(const igraph_vector_t *v1, const igraph_vector_t *v2); PyObject* igraphmodule_vector_t_to_PyList_pairs(const igraph_vector_t *v); PyObject* igraphmodule_vector_ptr_t_to_PyList(const igraph_vector_ptr_t *v, igraphmodule_conv_t type); PyObject* igraphmodule_vector_int_t_to_PyList(const igraph_vector_int_t *v); PyObject* igraphmodule_vector_long_t_to_PyList(const igraph_vector_long_t *v); PyObject* igraphmodule_matrix_t_to_PyList(const igraph_matrix_t *m, igraphmodule_conv_t type); #endif python-igraph-0.7.1.post6/src/edgeobject.c0000644000076500000240000005122512460535071021026 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* vim: set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "attributes.h" #include "edgeobject.h" #include "graphobject.h" #include "vertexobject.h" #include "error.h" #include "py2compat.h" /** * \ingroup python_interface * \defgroup python_interface_edge Edge object */ PyTypeObject igraphmodule_EdgeType; /** * \ingroup python_interface_edge * \brief Checks whether the given Python object is an edge */ int igraphmodule_Edge_Check(PyObject* obj) { if (!obj) return 0; return PyObject_IsInstance(obj, (PyObject*)(&igraphmodule_EdgeType)); } /** * \ingroup python_interface_edge * \brief Checks whether the index in the given edge object is a valid one. * \return nonzero if the edge object is valid. Raises an appropriate Python * exception and returns zero if the edge object is invalid. */ int igraphmodule_Edge_Validate(PyObject* obj) { igraph_integer_t n; igraphmodule_EdgeObject *self; igraphmodule_GraphObject *graph; if (!igraphmodule_Edge_Check(obj)) { PyErr_SetString(PyExc_TypeError, "object is not an Edge"); return 0; } self = (igraphmodule_EdgeObject*)obj; graph = self->gref; if (graph == 0) { PyErr_SetString(PyExc_ValueError, "Edge object refers to a null graph"); return 0; } if (self->idx < 0) { PyErr_SetString(PyExc_ValueError, "Edge object refers to a negative edge index"); return 0; } n = igraph_ecount(&graph->g); if (self->idx >= n) { PyErr_SetString(PyExc_ValueError, "Edge object refers to a nonexistent edge"); return 0; } return 1; } /** * \ingroup python_interface_edge * \brief Allocates a new Python edge object * \param gref weak reference of the \c igraph.Graph being referenced by the edge * \param idx the index of the edge * * \warning \c igraph references its edges by indices, so if * you delete some edges from the graph, the edge indices will * change. Since the \c igraph.Edge objects do not follow these * changes, your existing edge objects will point to elsewhere * (or they might even get invalidated). */ PyObject* igraphmodule_Edge_New(igraphmodule_GraphObject *gref, igraph_integer_t idx) { igraphmodule_EdgeObject* self; self=PyObject_New(igraphmodule_EdgeObject, &igraphmodule_EdgeType); if (self) { RC_ALLOC("Edge", self); Py_INCREF(gref); self->gref=gref; self->idx=idx; self->hash=-1; } return (PyObject*)self; } /** * \ingroup python_interface_edge * \brief Clears the edge's subobject (before deallocation) */ int igraphmodule_Edge_clear(igraphmodule_EdgeObject *self) { PyObject *tmp; tmp=(PyObject*)self->gref; self->gref=NULL; Py_XDECREF(tmp); return 0; } /** * \ingroup python_interface_edge * \brief Deallocates a Python representation of a given edge object */ void igraphmodule_Edge_dealloc(igraphmodule_EdgeObject* self) { igraphmodule_Edge_clear(self); RC_DEALLOC("Edge", self); PyObject_Del((PyObject*)self); } /** \ingroup python_interface_edge * \brief Formats an \c igraph.Edge object as a string * * \return the formatted textual representation as a \c PyObject */ PyObject* igraphmodule_Edge_repr(igraphmodule_EdgeObject *self) { PyObject *s; PyObject *attrs; #ifndef IGRAPH_PYTHON3 PyObject *grepr, *drepr; #endif attrs = igraphmodule_Edge_attributes(self); if (attrs == 0) return NULL; #ifdef IGRAPH_PYTHON3 s = PyUnicode_FromFormat("igraph.Edge(%R, %ld, %R)", (PyObject*)self->gref, (long int)self->idx, attrs); Py_DECREF(attrs); #else grepr=PyObject_Repr((PyObject*)self->gref); drepr=PyObject_Repr(attrs); Py_DECREF(attrs); if (!grepr || !drepr) { Py_XDECREF(grepr); Py_XDECREF(drepr); return NULL; } s=PyString_FromFormat("igraph.Edge(%s, %ld, %s)", PyString_AsString(grepr), (long int)self->idx, PyString_AsString(drepr)); Py_DECREF(grepr); Py_DECREF(drepr); #endif return s; } /** \ingroup python_interface_edge * \brief Returns the hash code of the edge */ Py_hash_t igraphmodule_Edge_hash(igraphmodule_EdgeObject* self) { Py_hash_t hash_graph; Py_hash_t hash_index; Py_hash_t result; PyObject* index_o; if (self->hash != -1) return self->hash; index_o = PyInt_FromLong((long int)self->idx); if (index_o == 0) return -1; hash_index = PyObject_Hash(index_o); Py_DECREF(index_o); if (hash_index == -1) return -1; hash_graph = PyObject_Hash((PyObject*)self->gref); if (hash_graph == -1) return -1; result = hash_graph ^ hash_index; if (result == -1) result = 590923713U; self->hash = result; return result; } /** \ingroup python_interface_edge * \brief Rich comparison of an edge with another */ PyObject* igraphmodule_Edge_richcompare(igraphmodule_EdgeObject *a, PyObject *b, int op) { igraphmodule_EdgeObject* self = a; igraphmodule_EdgeObject* other; if (!igraphmodule_Edge_Check(b)) Py_RETURN_NOTIMPLEMENTED; other = (igraphmodule_EdgeObject*)b; if (self->gref != other->gref) Py_RETURN_FALSE; switch (op) { case Py_EQ: Py_RETURN(self->idx == other->idx); case Py_NE: Py_RETURN(self->idx != other->idx); case Py_LE: Py_RETURN(self->idx <= other->idx); case Py_LT: Py_RETURN(self->idx < other->idx); case Py_GE: Py_RETURN(self->idx >= other->idx); case Py_GT: Py_RETURN(self->idx > other->idx); default: Py_RETURN_NOTIMPLEMENTED; } } /** \ingroup python_interface_edge * \brief Returns the number of edge attributes */ Py_ssize_t igraphmodule_Edge_attribute_count(igraphmodule_EdgeObject* self) { igraphmodule_GraphObject *o = self->gref; if (!o) return 0; if (!((PyObject**)o->g.attr)[1]) return 0; return PyDict_Size(((PyObject**)o->g.attr)[1]); } /** \ingroup python_interface_edge * \brief Returns the list of attribute names */ PyObject* igraphmodule_Edge_attribute_names(igraphmodule_EdgeObject* self) { if (!self->gref) return NULL; return igraphmodule_Graph_edge_attributes(self->gref); } /** \ingroup python_interface_edge * \brief Returns a dict with attribute names and values */ PyObject* igraphmodule_Edge_attributes(igraphmodule_EdgeObject* self) { igraphmodule_GraphObject *o = self->gref; PyObject *names, *dict; long int i, n; if (!igraphmodule_Edge_Validate((PyObject*)self)) return 0; dict=PyDict_New(); if (!dict) return NULL; names=igraphmodule_Graph_edge_attributes(o); if (!names) { Py_DECREF(dict); return NULL; } n = PyList_Size(names); for (i=0; ig.attr)[ATTRHASH_IDX_EDGE], name); if (dictit) { PyObject *value = PyList_GetItem(dictit, self->idx); if (value) { /* no need to Py_INCREF, PyDict_SetItem will do that */ PyDict_SetItem(dict, name, value); } } } } Py_DECREF(names); return dict; } /** * \ingroup python_interface_edge * \brief Updates some attributes of an edge * * \param self the edge object * \param args positional arguments * \param kwds keyword arguments */ PyObject* igraphmodule_Edge_update_attributes(PyObject* self, PyObject* args, PyObject* kwds) { return igraphmodule_Vertex_update_attributes(self, args, kwds); } /** \ingroup python_interface_edge * \brief Returns the corresponding value to a given attribute of the edge * \param self the edge object * \param s the attribute name to be queried */ PyObject* igraphmodule_Edge_get_attribute(igraphmodule_EdgeObject* self, PyObject* s) { igraphmodule_GraphObject *o = self->gref; PyObject* result; if (!igraphmodule_Edge_Validate((PyObject*)self)) return 0; if (!igraphmodule_attribute_name_check(s)) return 0; result=PyDict_GetItem(((PyObject**)o->g.attr)[2], s); if (result) { /* result is a list, so get the element with index self->idx */ if (!PyList_Check(result)) { PyErr_SetString(igraphmodule_InternalError, "Edge attribute dict member is not a list"); return NULL; } result=PyList_GetItem(result, self->idx); Py_INCREF(result); return result; } /* result is NULL, check whether there was an error */ if (!PyErr_Occurred()) PyErr_SetString(PyExc_KeyError, "Attribute does not exist"); return NULL; } /** \ingroup python_interface_edge * \brief Sets the corresponding value of a given attribute of the edge * \param self the edge object * \param k the attribute name to be set * \param v the value to be set * \return 0 if everything's ok, -1 in case of error */ int igraphmodule_Edge_set_attribute(igraphmodule_EdgeObject* self, PyObject* k, PyObject* v) { igraphmodule_GraphObject *o=self->gref; PyObject* result; int r; if (!igraphmodule_Edge_Validate((PyObject*)self)) return -1; if (!igraphmodule_attribute_name_check(k)) return -1; if (v==NULL) // we are deleting attribute return PyDict_DelItem(((PyObject**)o->g.attr)[2], k); result=PyDict_GetItem(((PyObject**)o->g.attr)[2], k); if (result) { /* result is a list, so set the element with index self->idx */ if (!PyList_Check(result)) { PyErr_SetString(igraphmodule_InternalError, "Vertex attribute dict member is not a list"); return -1; } /* we actually don't own a reference here to v, so we must increase * its reference count, because PyList_SetItem will "steal" a reference! * It took me 1.5 hours between London and Manchester to figure it out */ Py_INCREF(v); r=PyList_SetItem(result, self->idx, v); if (r == -1) { Py_DECREF(v); } return r; } /* result is NULL, check whether there was an error */ if (!PyErr_Occurred()) { /* no, there wasn't, so we must simply add the attribute */ int n=(int)igraph_ecount(&o->g), i; result=PyList_New(n); for (i=0; iidx) { Py_INCREF(Py_None); if (PyList_SetItem(result, i, Py_None) == -1) { Py_DECREF(Py_None); Py_DECREF(result); return -1; } } else { /* Same game with the reference count here */ Py_INCREF(v); if (PyList_SetItem(result, i, v) == -1) { Py_DECREF(v); Py_DECREF(result); return -1; } } } if (PyDict_SetItem(((PyObject**)o->g.attr)[2], k, result) == -1) { Py_DECREF(result); /* TODO: is it needed here? maybe not! */ return -1; } Py_DECREF(result); /* compensating for PyDict_SetItem */ return 0; } return -1; } /** * \ingroup python_interface_edge * Returns the source node index of an edge */ PyObject* igraphmodule_Edge_get_from(igraphmodule_EdgeObject* self, void* closure) { igraphmodule_GraphObject *o = self->gref; igraph_integer_t from, to; if (!igraphmodule_Edge_Validate((PyObject*)self)) return NULL; if (igraph_edge(&o->g, self->idx, &from, &to)) { igraphmodule_handle_igraph_error(); return NULL; } return PyInt_FromLong((long int)from); } /** * \ingroup python_interface_edge * Returns the target node index of an edge */ PyObject* igraphmodule_Edge_get_to(igraphmodule_EdgeObject* self, void* closure) { igraphmodule_GraphObject *o = self->gref; igraph_integer_t from, to; if (!igraphmodule_Edge_Validate((PyObject*)self)) return NULL; if (igraph_edge(&o->g, self->idx, &from, &to)) { igraphmodule_handle_igraph_error(); return NULL; } return PyInt_FromLong((long)to); } /** * \ingroup python_interface_edge * Returns the edge index */ PyObject* igraphmodule_Edge_get_index(igraphmodule_EdgeObject* self, void* closure) { return PyInt_FromLong((long int)self->idx); } /** * \ingroup python_interface_edge * Returns the edge index as an igraph_integer_t */ igraph_integer_t igraphmodule_Edge_get_index_igraph_integer(igraphmodule_EdgeObject* self) { return self->idx; } /** * \ingroup python_interface_edge * Returns the edge index as an ordinary C long */ long igraphmodule_Edge_get_index_long(igraphmodule_EdgeObject* self) { return (long)self->idx; } /** * \ingroup python_interface_edge * Returns the target node index of an edge */ PyObject* igraphmodule_Edge_get_tuple(igraphmodule_EdgeObject* self, void* closure) { igraphmodule_GraphObject *o = self->gref; igraph_integer_t from, to; if (!igraphmodule_Edge_Validate((PyObject*)self)) return NULL; if (igraph_edge(&o->g, self->idx, &from, &to)) { igraphmodule_handle_igraph_error(); return NULL; } return Py_BuildValue("(ii)", (long)from, (long)to); } /** \ingroup python_interface_edge * Returns the graph where the edge belongs */ PyObject* igraphmodule_Edge_get_graph(igraphmodule_EdgeObject* self, void* closure) { Py_INCREF(self->gref); return (PyObject*)self->gref; } #define GRAPH_PROXY_METHOD(FUNC, METHODNAME) \ PyObject* igraphmodule_Edge_##FUNC(igraphmodule_EdgeObject* self, PyObject* args, PyObject* kwds) { \ PyObject *new_args, *item, *result; \ long int i, num_args = args ? PyTuple_Size(args)+1 : 1; \ \ /* Prepend ourselves to args */ \ new_args = PyTuple_New(num_args); \ Py_INCREF(self); PyTuple_SET_ITEM(new_args, 0, (PyObject*)self); \ for (i = 1; i < num_args; i++) { \ item = PyTuple_GET_ITEM(args, i-1); \ Py_INCREF(item); PyTuple_SET_ITEM(new_args, i, item); \ } \ \ /* Get the method instance */ \ item = PyObject_GetAttrString((PyObject*)(self->gref), METHODNAME); \ result = PyObject_Call(item, new_args, kwds); \ Py_DECREF(item); \ Py_DECREF(new_args); \ return result; \ } GRAPH_PROXY_METHOD(count_multiple, "count_multiple"); GRAPH_PROXY_METHOD(delete, "delete_edges"); GRAPH_PROXY_METHOD(is_loop, "is_loop"); GRAPH_PROXY_METHOD(is_multiple, "is_multiple"); GRAPH_PROXY_METHOD(is_mutual, "is_mutual"); #undef GRAPH_PROXY_METHOD #define GRAPH_PROXY_METHOD_SPEC(FUNC, METHODNAME) \ {METHODNAME, (PyCFunction)igraphmodule_Edge_##FUNC, METH_VARARGS | METH_KEYWORDS, \ "Proxy method to L{Graph." METHODNAME "()}\n\n" \ "This method calls the " METHODNAME " method of the L{Graph} class " \ "with this edge as the first argument, and returns the result.\n\n"\ "@see: Graph." METHODNAME "() for details."} #define GRAPH_PROXY_METHOD_SPEC_2(FUNC, METHODNAME, METHODNAME_IN_GRAPH) \ {METHODNAME, (PyCFunction)igraphmodule_Edge_##FUNC, METH_VARARGS | METH_KEYWORDS, \ "Proxy method to L{Graph." METHODNAME_IN_GRAPH "()}\n\n" \ "This method calls the " METHODNAME_IN_GRAPH " method of the L{Graph} class " \ "with this edge as the first argument, and returns the result.\n\n"\ "@see: Graph." METHODNAME_IN_GRAPH "() for details."} /** * \ingroup python_interface_edge * Method table for the \c igraph.Edge object */ PyMethodDef igraphmodule_Edge_methods[] = { {"attributes", (PyCFunction)igraphmodule_Edge_attributes, METH_NOARGS, "attributes() -> dict\n\n" "Returns a dict of attribute names and values for the edge\n" }, {"attribute_names", (PyCFunction)igraphmodule_Edge_attribute_names, METH_NOARGS, "attribute_names() -> list\n\n" "Returns the list of edge attribute names\n" }, {"update_attributes", (PyCFunction)igraphmodule_Edge_update_attributes, METH_VARARGS | METH_KEYWORDS, "update_attributes(E, **F) -> None\n\n" "Updates the attributes of the edge from dict/iterable E and F.\n\n" "If E has a C{keys()} method, it does: C{for k in E: self[k] = E[k]}.\n" "If E lacks a C{keys()} method, it does: C{for (k, v) in E: self[k] = v}.\n" "In either case, this is followed by: C{for k in F: self[k] = F[k]}.\n\n" "This method thus behaves similarly to the C{update()} method of Python\n" "dictionaries." }, GRAPH_PROXY_METHOD_SPEC(count_multiple, "count_multiple"), GRAPH_PROXY_METHOD_SPEC_2(delete, "delete", "delete_edges"), GRAPH_PROXY_METHOD_SPEC(is_loop, "is_loop"), GRAPH_PROXY_METHOD_SPEC(is_multiple, "is_multiple"), GRAPH_PROXY_METHOD_SPEC(is_mutual, "is_mutual"), {NULL} }; #undef GRAPH_PROXY_METHOD_SPEC #undef GRAPH_PROXY_METHOD_SPEC_2 /** * \ingroup python_interface_edge * Getter/setter table for the \c igraph.Edge object */ PyGetSetDef igraphmodule_Edge_getseters[] = { {"source", (getter)igraphmodule_Edge_get_from, NULL, "Source node index of this edge", NULL }, {"target", (getter)igraphmodule_Edge_get_to, NULL, "Target node index of this edge", NULL }, {"tuple", (getter)igraphmodule_Edge_get_tuple, NULL, "Source and target node index of this edge as a tuple", NULL }, {"index", (getter)igraphmodule_Edge_get_index, NULL, "Index of this edge", NULL, }, {"graph", (getter)igraphmodule_Edge_get_graph, NULL, "The graph the edge belongs to", NULL, }, {NULL} }; /** \ingroup python_interface_edge * This structure is the collection of functions necessary to implement * the edge as a mapping (i.e. to allow the retrieval and setting of * igraph attributes in Python as if it were of a Python mapping type) */ PyMappingMethods igraphmodule_Edge_as_mapping = { // returns the number of edge attributes (lenfunc)igraphmodule_Edge_attribute_count, // returns an attribute by name (binaryfunc)igraphmodule_Edge_get_attribute, // sets an attribute by name (objobjargproc)igraphmodule_Edge_set_attribute }; /** \ingroup python_interface_edge * Python type object referencing the methods Python calls when it performs various operations on * an edge of a graph */ PyTypeObject igraphmodule_EdgeType = { PyVarObject_HEAD_INIT(0, 0) "igraph.Edge", // tp_name sizeof(igraphmodule_EdgeObject), // tp_basicsize 0, // tp_itemsize (destructor)igraphmodule_Edge_dealloc, // tp_dealloc 0, // tp_print 0, // tp_getattr 0, // tp_setattr 0, /* tp_compare (2.x) / tp_reserved (3.x) */ (reprfunc)igraphmodule_Edge_repr, // tp_repr 0, // tp_as_number 0, // tp_as_sequence &igraphmodule_Edge_as_mapping, // tp_as_mapping (hashfunc)igraphmodule_Edge_hash, /* tp_hash */ 0, // tp_call 0, // tp_str 0, // tp_getattro 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags "Class representing a single edge in a graph.\n\n" "The edge is referenced by its index, so if the underlying graph\n" "changes, the semantics of the edge object might change as well\n" "(if the edge indices are altered in the original graph).\n\n" "The attributes of the edge can be accessed by using the edge\n" "as a hash:\n\n" " >>> e[\"weight\"] = 2 #doctest: +SKIP\n" " >>> print e[\"weight\"] #doctest: +SKIP\n" " 2\n", // tp_doc 0, // tp_traverse 0, // tp_clear (richcmpfunc)igraphmodule_Edge_richcompare, /* tp_richcompare */ 0, // tp_weaklistoffset 0, // tp_iter 0, // tp_iternext igraphmodule_Edge_methods, // tp_methods 0, // tp_members igraphmodule_Edge_getseters, // tp_getset }; python-igraph-0.7.1.post6/src/edgeobject.h0000644000076500000240000000375112460534506021036 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_EDGEOBJECT_H #define PYTHON_EDGEOBJECT_H #include #include "graphobject.h" #include "py2compat.h" /** * \ingroup python_interface_edge * \brief A structure representing an edge of a graph */ typedef struct { PyObject_HEAD igraphmodule_GraphObject* gref; igraph_integer_t idx; Py_hash_t hash; } igraphmodule_EdgeObject; int igraphmodule_Edge_clear(igraphmodule_EdgeObject *self); void igraphmodule_Edge_dealloc(igraphmodule_EdgeObject* self); int igraphmodule_Edge_Check(PyObject *obj); int igraphmodule_Edge_Validate(PyObject *obj); PyObject* igraphmodule_Edge_New(igraphmodule_GraphObject *gref, igraph_integer_t idx); PyObject* igraphmodule_Edge_repr(igraphmodule_EdgeObject *self); PyObject* igraphmodule_Edge_attributes(igraphmodule_EdgeObject* self); PyObject* igraphmodule_Edge_attribute_names(igraphmodule_EdgeObject* self); igraph_integer_t igraphmodule_Edge_get_index_igraph_integer(igraphmodule_EdgeObject* self); long igraphmodule_Edge_get_index_long(igraphmodule_EdgeObject* self); PyObject* igraphmodule_Edge_update_attributes(PyObject* self, PyObject* args, PyObject* kwds); extern PyTypeObject igraphmodule_EdgeType; #endif python-igraph-0.7.1.post6/src/edgeseqobject.c0000644000076500000240000007557012460534506021552 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* vim: set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "attributes.h" #include "common.h" #include "convert.h" #include "edgeseqobject.h" #include "edgeobject.h" #include "error.h" #include "py2compat.h" #define GET_GRAPH(obj) (((igraphmodule_GraphObject*)obj->gref)->g) /** * \ingroup python_interface * \defgroup python_interface_edgeseq Edge sequence object */ PyTypeObject igraphmodule_EdgeSeqType; /** * \ingroup python_interface_edgeseq * \brief Allocate a new edge sequence object for a given graph * \param g the graph object being referenced * \return the allocated PyObject */ PyObject* igraphmodule_EdgeSeq_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { igraphmodule_EdgeSeqObject* o; o=(igraphmodule_EdgeSeqObject*)PyType_GenericNew(subtype, args, kwds); if (o == NULL) return NULL; igraph_es_all(&o->es, IGRAPH_EDGEORDER_ID); o->gref=0; o->weakreflist=0; RC_ALLOC("EdgeSeq", o); return (PyObject*)o; } /** * \ingroup python_interface_edgeseq * \brief Copies an edge sequence object * \return the copied PyObject */ igraphmodule_EdgeSeqObject* igraphmodule_EdgeSeq_copy(igraphmodule_EdgeSeqObject* o) { igraphmodule_EdgeSeqObject *copy; copy=(igraphmodule_EdgeSeqObject*)PyType_GenericNew(Py_TYPE(o), 0, 0); if (copy == NULL) return NULL; if (igraph_es_type(&o->es) == IGRAPH_ES_VECTOR) { igraph_vector_t v; if (igraph_vector_copy(&v, o->es.data.vecptr)) { igraphmodule_handle_igraph_error(); return 0; } if (igraph_es_vector_copy(©->es, &v)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); return 0; } igraph_vector_destroy(&v); } else { copy->es = o->es; } copy->gref = o->gref; if (o->gref) Py_INCREF(o->gref); RC_ALLOC("EdgeSeq(copy)", copy); return copy; } /** * \ingroup python_interface_edgeseq * \brief Initialize a new edge sequence object for a given graph * \return the initialized PyObject */ int igraphmodule_EdgeSeq_init(igraphmodule_EdgeSeqObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "graph", "edges", NULL }; PyObject *g, *esobj=Py_None; igraph_es_t es; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O", kwlist, &igraphmodule_GraphType, &g, &esobj)) return -1; if (esobj == Py_None) { /* If es is None, we are selecting all the edges */ igraph_es_all(&es, IGRAPH_EDGEORDER_ID); } else if (PyInt_Check(esobj)) { /* We selected a single edge */ long int idx = PyInt_AsLong(esobj); if (idx < 0 || idx >= igraph_ecount(&((igraphmodule_GraphObject*)g)->g)) { PyErr_SetString(PyExc_ValueError, "edge index out of range"); return -1; } igraph_es_1(&es, (igraph_integer_t)idx); } else { /* We selected multiple edges */ igraph_vector_t v; igraph_integer_t n = igraph_ecount(&((igraphmodule_GraphObject*)g)->g); if (igraphmodule_PyObject_to_vector_t(esobj, &v, 1)) return -1; if (!igraph_vector_isininterval(&v, 0, n-1)) { igraph_vector_destroy(&v); PyErr_SetString(PyExc_ValueError, "edge index out of range"); return -1; } if (igraph_es_vector_copy(&es, &v)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); return -1; } igraph_vector_destroy(&v); } self->es = es; Py_INCREF(g); self->gref = (igraphmodule_GraphObject*)g; return 0; } /** * \ingroup python_interface_edgeseq * \brief Deallocates a Python representation of a given edge sequence object */ void igraphmodule_EdgeSeq_dealloc(igraphmodule_EdgeSeqObject* self) { if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)self); if (self->gref) { igraph_es_destroy(&self->es); Py_DECREF(self->gref); self->gref=0; } Py_TYPE(self)->tp_free((PyObject*)self); RC_DEALLOC("EdgeSeq", self); } /** * \ingroup python_interface_edgeseq * \brief Returns the length of the sequence (i.e. the number of edges in the graph) */ int igraphmodule_EdgeSeq_sq_length(igraphmodule_EdgeSeqObject* self) { igraph_t *g; igraph_integer_t result; g=&GET_GRAPH(self); if (igraph_es_size(g, &self->es, &result)) { igraphmodule_handle_igraph_error(); return -1; } return (int)result; } /** * \ingroup python_interface_edgeseq * \brief Returns the item at the given index in the sequence */ PyObject* igraphmodule_EdgeSeq_sq_item(igraphmodule_EdgeSeqObject* self, Py_ssize_t i) { igraph_t *g; igraph_integer_t idx = -1; if (!self->gref) return NULL; g=&GET_GRAPH(self); switch (igraph_es_type(&self->es)) { case IGRAPH_ES_ALL: if (i >= 0 && i < igraph_ecount(g)) idx = (igraph_integer_t)i; break; case IGRAPH_ES_VECTOR: case IGRAPH_ES_VECTORPTR: if (i >= 0 && i < igraph_vector_size(self->es.data.vecptr)) idx = (igraph_integer_t)VECTOR(*self->es.data.vecptr)[i]; break; case IGRAPH_ES_1: if (i == 0) idx = self->es.data.eid; break; case IGRAPH_ES_SEQ: if (i >= 0 && i < self->es.data.seq.to - self->es.data.seq.from) idx = self->es.data.seq.from + (igraph_integer_t)i; break; /* TODO: IGRAPH_ES_PAIRS, IGRAPH_ES_ADJ, IGRAPH_ES_PATH, IGRAPH_ES_MULTIPATH - someday :) They are unused yet in the Python interface */ } if (idx < 0) { PyErr_SetString(PyExc_IndexError, "edge index out of range"); return NULL; } return igraphmodule_Edge_New(self->gref, idx); } /** \ingroup python_interface_edgeseq * \brief Returns the list of attribute names */ PyObject* igraphmodule_EdgeSeq_attribute_names(igraphmodule_EdgeSeqObject* self) { return igraphmodule_Graph_edge_attributes(self->gref); } /** \ingroup python_interface_edgeseq * \brief Returns the list of values for a given attribute */ PyObject* igraphmodule_EdgeSeq_get_attribute_values(igraphmodule_EdgeSeqObject* self, PyObject* o) { igraphmodule_GraphObject *gr = self->gref; PyObject *result=0, *values, *item; long int i, n; if (!igraphmodule_attribute_name_check(o)) return 0; PyErr_Clear(); values=PyDict_GetItem(ATTR_STRUCT_DICT(&gr->g)[ATTRHASH_IDX_EDGE], o); if (!values) { PyErr_SetString(PyExc_KeyError, "Attribute does not exist"); return NULL; } else if (PyErr_Occurred()) return NULL; switch (igraph_es_type(&self->es)) { case IGRAPH_ES_NONE: n = 0; result = PyList_New(0); break; case IGRAPH_ES_ALL: n = PyList_Size(values); result = PyList_New(n); if (!result) return 0; for (i=0; ies.data.vecptr); result = PyList_New(n); if (!result) return 0; for (i=0; ies.data.vecptr)[i]); Py_INCREF(item); PyList_SET_ITEM(result, i, item); } break; case IGRAPH_ES_SEQ: n = self->es.data.seq.to - self->es.data.seq.from; result = PyList_New(n); if (!result) return 0; for (i=0; ies.data.seq.from+i); Py_INCREF(item); PyList_SET_ITEM(result, i, item); } break; default: PyErr_SetString(PyExc_RuntimeError, "invalid edge selector"); } return result; } PyObject* igraphmodule_EdgeSeq_is_all(igraphmodule_EdgeSeqObject* self) { if (igraph_es_is_all(&self->es)) Py_RETURN_TRUE; Py_RETURN_FALSE; } PyObject* igraphmodule_EdgeSeq_get_attribute_values_mapping(igraphmodule_EdgeSeqObject *self, PyObject *o) { Py_ssize_t index; /* Handle integer indices according to the sequence protocol */ if (PyIndex_Check(o)) { index = PyNumber_AsSsize_t(o, 0); return igraphmodule_EdgeSeq_sq_item(self, index); } /* Handle strings according to the mapping protocol */ if (PyBaseString_Check(o)) return igraphmodule_EdgeSeq_get_attribute_values(self, o); /* Handle iterables and slices by calling the select() method */ if (PySlice_Check(o) || PyObject_HasAttrString(o, "__iter__")) { PyObject *result, *args; args = Py_BuildValue("(O)", o); if (!args) return NULL; result = igraphmodule_EdgeSeq_select(self, args); Py_DECREF(args); return result; } /* Handle everything else according to the mapping protocol */ return igraphmodule_EdgeSeq_get_attribute_values(self, o); } /** \ingroup python_interface_edgeseq * \brief Sets the list of values for a given attribute */ int igraphmodule_EdgeSeq_set_attribute_values_mapping(igraphmodule_EdgeSeqObject* self, PyObject* attrname, PyObject* values) { PyObject *dict, *list, *item; igraphmodule_GraphObject *gr; igraph_vector_t es; long i, j, n, no_of_edges; gr = self->gref; dict = ATTR_STRUCT_DICT(&gr->g)[ATTRHASH_IDX_EDGE]; if (!igraphmodule_attribute_name_check(attrname)) return -1; if (values == 0) { if (igraph_es_type(&self->es) == IGRAPH_ES_ALL) return PyDict_DelItem(dict, attrname); PyErr_SetString(PyExc_TypeError, "can't delete attribute from an edge sequence not representing the whole graph"); return -1; } if (PyString_Check(values) || !PySequence_Check(values)) { /* If values is a string or not a sequence, we construct a list with a * single element (the value itself) and then call ourselves again */ int result; PyObject *newList = PyList_New(1); if (newList == 0) return -1; Py_INCREF(values); PyList_SET_ITEM(newList, 0, values); /* reference stolen here */ result = igraphmodule_EdgeSeq_set_attribute_values_mapping(self, attrname, newList); Py_DECREF(newList); return result; } n=PySequence_Size(values); if (n<0) return -1; if (igraph_es_type(&self->es) == IGRAPH_ES_ALL) { no_of_edges = (long)igraph_ecount(&gr->g); if (n == 0 && no_of_edges > 0) { PyErr_SetString(PyExc_ValueError, "sequence must not be empty"); return -1; } /* Check if we already have attributes with the given name */ list = PyDict_GetItem(dict, attrname); if (list != 0) { /* Yes, we have. Modify its items to the items found in values */ for (i=0, j=0; ig, self->es, &es)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&es); return -1; } no_of_edges = (long)igraph_vector_size(&es); if (n == 0 && no_of_edges > 0) { PyErr_SetString(PyExc_ValueError, "sequence must not be empty"); igraph_vector_destroy(&es); return -1; } /* Check if we already have attributes with the given name */ list = PyDict_GetItem(dict, attrname); if (list != 0) { /* Yes, we have. Modify its items to the items found in values */ for (i=0, j=0; ig); list = PyList_New(n2); if (list == 0) { igraph_vector_destroy(&es); return -1; } for (i=0; igref; result=igraphmodule_EdgeSeq_copy(self); if (result == 0) return NULL; /* First, filter by positional arguments */ n = PyTuple_Size(args); for (i=0; ies); igraph_es_none(&result->es); /* We can simply bail out here */ return (PyObject*)result; } else if (PyCallable_Check(item)) { /* Call the callable for every edge in the current sequence to * determine what's up */ igraph_bool_t was_excluded = 0; igraph_vector_t v; if (igraph_vector_init(&v, 0)) { igraphmodule_handle_igraph_error(); return 0; } m = PySequence_Size((PyObject*)result); for (j=0; jes); if (igraph_es_vector_copy(&result->es, &v)) { Py_DECREF(result); igraph_vector_destroy(&v); igraphmodule_handle_igraph_error(); return NULL; } } igraph_vector_destroy(&v); } else if (PyInt_Check(item)) { /* Integers are treated specially: from now on, all remaining items * in the argument list must be integers and they will be used together * to restrict the edge set. Integers are interpreted as indices on the * edge set and NOT on the original, untouched edge sequence of the * graph */ igraph_vector_t v, v2; if (igraph_vector_init(&v, 0)) { igraphmodule_handle_igraph_error(); return 0; } if (igraph_vector_init(&v2, 0)) { igraph_vector_destroy(&v); igraphmodule_handle_igraph_error(); return 0; } if (igraph_es_as_vector(&gr->g, self->es, &v2)) { igraph_vector_destroy(&v); igraph_vector_destroy(&v2); igraphmodule_handle_igraph_error(); return 0; } m = igraph_vector_size(&v2); for (; i= m || idx < 0) { PyErr_SetString(PyExc_ValueError, "edge index out of range"); igraph_vector_destroy(&v); igraph_vector_destroy(&v2); return NULL; } if (igraph_vector_push_back(&v, VECTOR(v2)[idx])) { Py_DECREF(result); igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); igraph_vector_destroy(&v2); return NULL; } } igraph_vector_destroy(&v2); igraph_es_destroy(&result->es); if (igraph_es_vector_copy(&result->es, &v)) { Py_DECREF(result); igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); return NULL; } igraph_vector_destroy(&v); } else { /* Iterators and everything that was not handled directly */ PyObject *iter, *item2; igraph_vector_t v, v2; /* Allocate stuff */ if (igraph_vector_init(&v, 0)) { igraphmodule_handle_igraph_error(); return 0; } if (igraph_vector_init(&v2, 0)) { igraph_vector_destroy(&v); igraphmodule_handle_igraph_error(); return 0; } if (igraph_es_as_vector(&gr->g, self->es, &v2)) { igraph_vector_destroy(&v); igraph_vector_destroy(&v2); igraphmodule_handle_igraph_error(); return 0; } m = igraph_vector_size(&v2); /* Create an appropriate iterator */ if (PySlice_Check(item)) { /* Create an iterator from the slice (which is not iterable by default )*/ Py_ssize_t start, stop, step, sl; PyObject* range; igraph_bool_t ok; /* Casting to void* because Python 2.x expects PySliceObject* * but Python 3.x expects PyObject* */ ok = (PySlice_GetIndicesEx((void*)item, igraph_vector_size(&v2), &start, &stop, &step, &sl) == 0); if (ok) { range = PyObject_CallFunction((PyObject*)&PyRange_Type, "lll", start, stop, step); ok = (range != 0); } if (ok) { iter = PyObject_GetIter(range); Py_DECREF(range); ok = (iter != 0); } if (!ok) { igraph_vector_destroy(&v); igraph_vector_destroy(&v2); PyErr_SetString(PyExc_TypeError, "error while converting slice to iterator"); Py_DECREF(result); return 0; } } else { /* Simply create the iterator corresponding to the object */ iter = PyObject_GetIter(item); } /* Did we manage to get an iterator? */ if (iter == 0) { igraph_vector_destroy(&v); igraph_vector_destroy(&v2); PyErr_SetString(PyExc_TypeError, "invalid edge filter among positional arguments"); Py_DECREF(result); return 0; } /* Do the iteration */ while ((item2=PyIter_Next(iter)) != 0) { if (PyInt_Check(item2)) { long idx = PyInt_AsLong(item2); Py_DECREF(item2); if (idx >= m || idx < 0) { PyErr_SetString(PyExc_ValueError, "edge index out of range"); Py_DECREF(result); Py_DECREF(iter); igraph_vector_destroy(&v); igraph_vector_destroy(&v2); return NULL; } if (igraph_vector_push_back(&v, VECTOR(v2)[idx])) { Py_DECREF(result); Py_DECREF(iter); igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); igraph_vector_destroy(&v2); return NULL; } } else { /* We simply ignore elements that we don't know */ Py_DECREF(item2); } } /* Deallocate stuff */ igraph_vector_destroy(&v2); Py_DECREF(iter); if (PyErr_Occurred()) { igraph_vector_destroy(&v); Py_DECREF(result); return 0; } igraph_es_destroy(&result->es); if (igraph_es_vector_copy(&result->es, &v)) { Py_DECREF(result); igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); return NULL; } igraph_vector_destroy(&v); } } return (PyObject*)result; } /** * \ingroup python_interface_edgeseq * Method table for the \c igraph.EdgeSeq object */ PyMethodDef igraphmodule_EdgeSeq_methods[] = { {"attribute_names", (PyCFunction)igraphmodule_EdgeSeq_attribute_names, METH_NOARGS, "attribute_names() -> list\n\n" "Returns the attribute name list of the graph's edges\n" }, {"find", (PyCFunction)igraphmodule_EdgeSeq_find, METH_VARARGS, "find(condition) -> Edge\n\n" "For internal use only.\n" }, {"get_attribute_values", (PyCFunction)igraphmodule_EdgeSeq_get_attribute_values, METH_O, "get_attribute_values(attrname) -> list\n\n" "Returns the value of a given edge attribute for all edges.\n\n" "@param attrname: the name of the attribute\n" }, {"is_all", (PyCFunction)igraphmodule_EdgeSeq_is_all, METH_NOARGS, "is_all() -> bool\n\n" "Returns whether the edge sequence contains all the edges exactly once, in\n" "the order of their edge IDs.\n\n" "This is used for optimizations in some of the edge selector routines.\n" }, {"set_attribute_values", (PyCFunction)igraphmodule_EdgeSeq_set_attribute_values, METH_VARARGS | METH_KEYWORDS, "set_attribute_values(attrname, values) -> list\n" "Sets the value of a given edge attribute for all vertices\n" "@param attrname: the name of the attribute\n" "@param values: the new attribute values in a list\n" }, {"select", (PyCFunction)igraphmodule_EdgeSeq_select, METH_VARARGS, "select(...) -> VertexSeq\n\n" "For internal use only.\n" }, {NULL} }; /** * \ingroup python_interface_edgeseq * This is the collection of functions necessary to implement the * edge sequence as a real sequence (e.g. allowing to reference * edges by indices) */ static PySequenceMethods igraphmodule_EdgeSeq_as_sequence = { (lenfunc)igraphmodule_EdgeSeq_sq_length, 0, /* sq_concat */ 0, /* sq_repeat */ (ssizeargfunc)igraphmodule_EdgeSeq_sq_item, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ 0, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; /** * \ingroup python_interface_edgeseq * This is the collection of functions necessary to implement the * edge sequence as a mapping (which maps attribute names to values) */ static PyMappingMethods igraphmodule_EdgeSeq_as_mapping = { /* returns the number of edge attributes */ (lenfunc) 0, /* returns the values of an attribute by name */ (binaryfunc) igraphmodule_EdgeSeq_get_attribute_values_mapping, /* sets the values of an attribute by name */ (objobjargproc) igraphmodule_EdgeSeq_set_attribute_values_mapping, }; /** * \ingroup python_interface_edgeseq * Returns the graph where the edge sequence belongs */ PyObject* igraphmodule_EdgeSeq_get_graph(igraphmodule_EdgeSeqObject* self, void* closure) { Py_INCREF(self->gref); return (PyObject*)self->gref; } /** * \ingroup python_interface_edgeseq * Returns the indices of the edges in this edge sequence */ PyObject* igraphmodule_EdgeSeq_get_indices(igraphmodule_EdgeSeqObject* self, void* closure) { igraphmodule_GraphObject *gr = self->gref; igraph_vector_t es; PyObject *result; if (igraph_vector_init(&es, 0)) { igraphmodule_handle_igraph_error(); return 0; } if (igraph_es_as_vector(&gr->g, self->es, &es)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&es); return 0; } result = igraphmodule_vector_t_to_PyList(&es, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&es); return result; } /** * \ingroup python_interface_edgeseq * Getter/setter table for the \c igraph.EdgeSeq object */ PyGetSetDef igraphmodule_EdgeSeq_getseters[] = { {"graph", (getter)igraphmodule_EdgeSeq_get_graph, NULL, "The graph the edge sequence belongs to", NULL}, {"indices", (getter)igraphmodule_EdgeSeq_get_indices, NULL, "The edge indices in this edge sequence", NULL, }, {NULL} }; /** \ingroup python_interface_edgeseq * Python type object referencing the methods Python calls when it performs various operations on * an edge sequence of a graph */ PyTypeObject igraphmodule_EdgeSeqType = { PyVarObject_HEAD_INIT(0, 0) "igraph.core.EdgeSeq", /* tp_name */ sizeof(igraphmodule_EdgeSeqObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)igraphmodule_EdgeSeq_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare (2.x) / tp_reserved (3.x) */ 0, /* tp_repr */ 0, /* tp_as_number */ &igraphmodule_EdgeSeq_as_sequence, /* tp_as_sequence */ &igraphmodule_EdgeSeq_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Low-level representation of an edge sequence.\n\n" /* tp_doc */ "Don't use it directly, use L{igraph.EdgeSeq} instead.\n\n" "@deffield ref: Reference", 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ offsetof(igraphmodule_EdgeSeqObject, weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ igraphmodule_EdgeSeq_methods, /* tp_methods */ 0, /* tp_members */ igraphmodule_EdgeSeq_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) igraphmodule_EdgeSeq_init, /* tp_init */ 0, /* tp_alloc */ (newfunc) igraphmodule_EdgeSeq_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weakreflist */ }; python-igraph-0.7.1.post6/src/edgeseqobject.h0000644000076500000240000000363412453614202021541 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_EDGESEQOBJECT_H #define PYTHON_EDGESEQOBJECT_H #include #include "graphobject.h" /** * \ingroup python_interface_edgeseq * \brief A structure representing the edge sequence of a graph */ typedef struct { PyObject_HEAD igraphmodule_GraphObject* gref; igraph_es_t es; PyObject* weakreflist; } igraphmodule_EdgeSeqObject; PyObject* igraphmodule_EdgeSeq_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds); igraphmodule_EdgeSeqObject* igraphmodule_EdgeSeq_copy( igraphmodule_EdgeSeqObject *o); int igraphmodule_EdgeSeq_init(igraphmodule_EdgeSeqObject *self, PyObject *args, PyObject *kwds); void igraphmodule_EdgeSeq_dealloc(igraphmodule_EdgeSeqObject* self); int igraphmodule_EdgeSeq_sq_length(igraphmodule_EdgeSeqObject *self); PyObject* igraphmodule_EdgeSeq_find(igraphmodule_EdgeSeqObject *self, PyObject *args); PyObject* igraphmodule_EdgeSeq_select(igraphmodule_EdgeSeqObject *self, PyObject *args); PyObject* igraphmodule_EdgeSeq_get_graph(igraphmodule_EdgeSeqObject *self, void* closure); extern PyTypeObject igraphmodule_EdgeSeqType; #endif python-igraph-0.7.1.post6/src/error.c0000644000076500000240000000530512453614202020056 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "error.h" #include /** \ingroup python_interface_errors * \brief Exception type to be returned when an internal \c igraph error occurs. */ PyObject* igraphmodule_InternalError; /** * \ingroup python_interface_errors * \brief Generic error handler for internal \c igraph errors. * * Since now \c igraph supports error handler functions, a special * function called \c igraphmodule_igraph_error_hook is responsible * for providing a meaningful error message. If it fails (or it isn't * even called), this function will provide a default error message. * * \return Always returns \c NULL, and all callers are advised to pass this * \c NULL value to their callers until it is propagated to the Python * interpreter. */ PyObject* igraphmodule_handle_igraph_error() { if (!PyErr_Occurred()) { PyErr_SetString(igraphmodule_InternalError, "Internal igraph error. Please contact the author!"); } return NULL; } /** * \ingroup python_interface_errors * \brief Warning hook for \c igraph */ void igraphmodule_igraph_warning_hook(const char *reason, const char *file, int line, int igraph_errno) { char buf[4096]; sprintf(buf, "%s at %s:%i", reason, file, line); PyErr_Warn(PyExc_RuntimeWarning, buf); } /** * \ingroup python_interface_errors * \brief Error hook for \c igraph */ void igraphmodule_igraph_error_hook(const char *reason, const char *file, int line, int igraph_errno) { char buf[4096]; PyObject *exc = igraphmodule_InternalError; if (igraph_errno == IGRAPH_UNIMPLEMENTED) exc = PyExc_NotImplementedError; if (igraph_errno == IGRAPH_ENOMEM) exc = PyExc_MemoryError; sprintf(buf, "Error at %s:%i: %s, %s", file, line, reason, igraph_strerror(igraph_errno)); IGRAPH_FINALLY_FREE(); /* make sure we are not masking already thrown exceptions */ if (!PyErr_Occurred()) PyErr_SetString(exc, buf); } python-igraph-0.7.1.post6/src/error.h0000644000076500000240000000306712453614202020066 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_ERROR_H #define PYTHON_ERROR_H #include #include /** \defgroup python_interface_errors Error handling * \ingroup python_interface */ PyObject* igraphmodule_handle_igraph_error(void); void igraphmodule_igraph_warning_hook(const char *reason, const char *file, int line, int igraph_errno); void igraphmodule_igraph_error_hook(const char *reason, const char *file, int line, int igraph_errno); extern PyObject* igraphmodule_InternalError; #define IGRAPH_PYCHECK(a) do { \ int igraph_i_pyret=(a); \ if (IGRAPH_UNLIKELY(igraph_i_pyret != 0)) {\ igraphmodule_handle_igraph_error(); \ IGRAPH_FINALLY_FREE(); \ return 0; \ } } while (0) #endif python-igraph-0.7.1.post6/src/filehandle.c0000644000076500000240000001071612476153532021033 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "filehandle.h" #include "py2compat.h" /** * \ingroup python_interface_filehandle * \brief Constructs a new file handle object from a Python object. * * \return 0 if everything was OK, 1 otherwise. An appropriate Python * exception is raised in this case. */ int igraphmodule_filehandle_init(igraphmodule_filehandle_t* handle, PyObject* object, char* mode) { #ifdef IGRAPH_PYTHON3 int fp; if (object == 0 || PyLong_Check(object)) { PyErr_SetString(PyExc_TypeError, "string or file-like object expected"); return 1; } #else if (object == 0 || (!PyBaseString_Check(object) && !PyFile_Check(object))) { PyErr_SetString(PyExc_TypeError, "string or file handle expected"); return 1; } #endif handle->need_close = 0; if (PyBaseString_Check(object)) { /* We have received a string; we need to open the file denoted by this * string now and mark that we opened the file ourselves (so we need * to close it when igraphmodule_filehandle_destroy is invoked). */ #ifdef IGRAPH_PYTHON3 handle->object = PyFile_FromObject(object, mode); #else handle->object = PyFile_FromString(PyString_AsString(object), mode); #endif if (handle->object == 0) { /* Could not open the file; just return an error code because an * exception was raised already */ return 1; } /* Remember that we need to close the file ourselves */ handle->need_close = 1; } else { /* This is probably a file-like object; store a reference for it and * we will handle it later */ handle->object = object; Py_INCREF(handle->object); } /* At this stage, handle->object is something we can handle. * In Python 2, we get here only if object is a file object so we * can safely call PyFile_AsFile to get a FILE* object. * In Python 3, we have to call PyObject_AsFileDescriptor instead * and then fdopen() it to get the corresponding FILE* object. */ #ifdef IGRAPH_PYTHON3 fp = PyObject_AsFileDescriptor(handle->object); if (fp == -1) { igraphmodule_filehandle_destroy(handle); /* This already called Py_DECREF(handle->object), no need to call it */ return 1; } handle->fp = fdopen(fp, mode); if (handle->fp == 0) { igraphmodule_filehandle_destroy(handle); /* This already called Py_DECREF(handle->object), no need to call it */ PyErr_SetString(PyExc_RuntimeError, "fdopen() failed unexpectedly"); return 1; } #else handle->fp = PyFile_AsFile(handle->object); if (handle->fp == 0) { igraphmodule_filehandle_destroy(handle); /* This already called Py_DECREF(handle->object), no need to call it */ PyErr_SetString(PyExc_RuntimeError, "PyFile_AsFile() failed unexpectedly"); return 1; } #endif return 0; } /** * \ingroup python_interface_filehandle * \brief Destroys the file handle object. */ void igraphmodule_filehandle_destroy(igraphmodule_filehandle_t* handle) { if (handle->fp != 0) { fflush(handle->fp); } handle->fp = 0; if (handle->object != 0) { if (handle->need_close) { if (PyFile_Close(handle->object)) { PyErr_WriteUnraisable(0); } } Py_DECREF(handle->object); handle->object = 0; } handle->need_close = 0; } /** * \ingroup python_interface_filehandle * \brief Returns the file encapsulated by the given \c igraphmodule_filehandle_t. */ FILE* igraphmodule_filehandle_get(const igraphmodule_filehandle_t* handle) { return handle->fp; } python-igraph-0.7.1.post6/src/filehandle.h0000644000076500000240000000277412453614202021034 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2010-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_FILEHANDLE_H #define PYTHON_FILEHANDLE_H #include #include /** * \defgroup python_interface_filehandle File handle object */ /** * \ingroup python_interface_filehandle * \brief A structure encapsulating a Python object and a \c FILE* pointer * created out of it. */ typedef struct { PyObject* object; FILE* fp; unsigned short int need_close; } igraphmodule_filehandle_t; int igraphmodule_filehandle_init(igraphmodule_filehandle_t* handle, PyObject* object, char* mode); FILE* igraphmodule_filehandle_get(const igraphmodule_filehandle_t* handle); void igraphmodule_filehandle_destroy(igraphmodule_filehandle_t* handle); #endif python-igraph-0.7.1.post6/src/graphobject.c0000644000076500000240000215220612534342712021226 0ustar ntamasstaff00000000000000/* vim:set ts=4 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "attributes.h" #include "arpackobject.h" #include "bfsiter.h" #include "common.h" #include "convert.h" #include "edgeseqobject.h" #include "error.h" #include "filehandle.h" #include "graphobject.h" #include "indexing.h" #include "memory.h" #include "py2compat.h" #include "pyhelpers.h" #include "vertexseqobject.h" #include PyTypeObject igraphmodule_GraphType; #define CREATE_GRAPH(py_graph, c_graph) { \ py_graph = (igraphmodule_GraphObject *) Py_TYPE(self)->tp_alloc(Py_TYPE(self), 0); \ if (py_graph != NULL) { \ igraphmodule_Graph_init_internal(py_graph); \ py_graph->g = (c_graph); \ } \ RC_ALLOC("Graph", py_graph); \ } #define CREATE_GRAPH_FROM_TYPE(py_graph, c_graph, py_type) { \ py_graph = (igraphmodule_GraphObject *) py_type->tp_alloc(py_type, 0); \ if (py_graph != NULL) { \ igraphmodule_Graph_init_internal(py_graph); \ py_graph->g = (c_graph); \ } \ RC_ALLOC("Graph", py_graph); \ } /********************************************************************** * Basic implementation of igraph.Graph * **********************************************************************/ /** \defgroup python_interface_graph Graph object * \ingroup python_interface */ /** * \ingroup python_interface_internal * \brief Initializes the internal structures in an \c igraph.Graph object's * C representation. * * This function must be called whenever we create a new Graph object with * \c tp_alloc */ void igraphmodule_Graph_init_internal(igraphmodule_GraphObject * self) { if (!self) return; self->destructor = NULL; self->weakreflist = NULL; } /** * \ingroup python_interface_graph * \brief Creates a new igraph object in Python * * This function is called whenever a new \c igraph.Graph object is created in * Python. An optional \c n parameter can be passed from Python, * representing the number of vertices in the graph. If it is omitted, * the default value is 0. * * Example call from Python: \verbatim g = igraph.Graph(5); \endverbatim * * In fact, the parameters are processed by \c igraphmodule_Graph_init * * \return the new \c igraph.Graph object or NULL if an error occurred. * * \sa igraphmodule_Graph_init * \sa igraph_empty */ PyObject *igraphmodule_Graph_new(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; self = (igraphmodule_GraphObject *) type->tp_alloc(type, 0); RC_ALLOC("Graph", self); /* don't need it, the constructor will do it */ /*if (self != NULL) { igraph_empty(&self->g, 1, 0); } */ igraphmodule_Graph_init_internal(self); return (PyObject *) self; } /** * \ingroup python_interface_graph * \brief Clears the graph object's subobject (before deallocation) */ int igraphmodule_Graph_clear(igraphmodule_GraphObject * self) { PyObject *tmp; PyObject_GC_UnTrack(self); tmp = self->destructor; self->destructor = NULL; Py_XDECREF(tmp); return 0; } /** * \ingroup python_interface_graph * \brief Support for cyclic garbage collection in Python * * This is necessary because the \c igraph.Graph object contains several * other \c PyObject pointers and they might point back to itself. */ int igraphmodule_Graph_traverse(igraphmodule_GraphObject * self, visitproc visit, void *arg) { int vret, i; RC_TRAVERSE("Graph", self); if (self->destructor) { vret = visit(self->destructor, arg); if (vret != 0) return vret; } if (self->g.attr) { for (i = 0; i < 3; i++) { vret = visit(((PyObject **) (self->g.attr))[i], arg); if (vret != 0) return vret; } } return 0; } /** * \ingroup python_interface_graph * \brief Deallocates a Python representation of a given igraph object */ void igraphmodule_Graph_dealloc(igraphmodule_GraphObject * self) { PyObject *r; /* Clear weak references */ if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); igraph_destroy(&self->g); if (PyCallable_Check(self->destructor)) { r = PyObject_CallObject(self->destructor, NULL); if (r) { Py_DECREF(r); } } igraphmodule_Graph_clear(self); RC_DEALLOC("Graph", self); Py_TYPE(self)->tp_free((PyObject*)self); } /** * \ingroup python_interface_graph * \brief Initializes a new \c igraph object in Python * * This function is called whenever a new \c igraph.Graph object is initialized in * Python (note that initializing is not equal to creating: an object might * be created but not initialized when it is being recovered from a serialized * state). * * Throws \c AssertionError in Python if \c vcount is less than or equal to zero. * \return the new \c igraph.Graph object or NULL if an error occurred. * * \sa igraphmodule_Graph_new * \sa igraph_empty * \sa igraph_create */ int igraphmodule_Graph_init(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "n", "edges", "directed", NULL }; long int n = 0; PyObject *edges = NULL, *dir = Py_False; igraph_vector_t edges_vector; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|lO!O", kwlist, &n, &PyList_Type, &edges, &dir)) return -1; if (edges && PyList_Check(edges)) { /* Caller specified an edge list, so we use igraph_create */ /* We have to convert the Python list to a igraph_vector_t */ if (igraphmodule_PyObject_to_edgelist(edges, &edges_vector, 0)) { igraphmodule_handle_igraph_error(); return -1; } if (igraph_create (&self->g, &edges_vector, (igraph_integer_t) n, PyObject_IsTrue(dir))) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&edges_vector); return -1; } igraph_vector_destroy(&edges_vector); } else { /* No edge list was specified, let's use igraph_empty */ if (igraph_empty(&self->g, (igraph_integer_t) n, PyObject_IsTrue(dir))) { igraphmodule_handle_igraph_error(); return -1; } } return 0; } /** \ingroup python_interface_graph * \brief Creates an \c igraph.Graph object from an existing \c igraph_t * * The newly created \c igraph.Graph object will take ownership of the * given \c igraph_t. This function is not accessible from Python the * normal way, but it is exposed via the C API of the Python module. * See \c api.h for more details. */ PyObject* igraphmodule_Graph_from_igraph_t(igraph_t *graph) { igraphmodule_GraphObject* result; PyTypeObject* type = &igraphmodule_GraphType; CREATE_GRAPH_FROM_TYPE(result, *graph, type); return (PyObject*)result; } /** \ingroup python_interface_graph * \brief Formats an \c igraph.Graph object in a human-readable format. * * This function is rather simple now, it returns the number of vertices * and edges in a string. * * \return the formatted textual representation as a \c PyObject */ PyObject *igraphmodule_Graph_str(igraphmodule_GraphObject * self) { if (igraph_is_directed(&self->g)) return PyString_FromFormat("Directed graph (|V| = %ld, |E| = %ld)", (long)igraph_vcount(&self->g), (long)igraph_ecount(&self->g)); else return PyString_FromFormat("Undirected graph (|V| = %ld, |E| = %ld)", (long)igraph_vcount(&self->g), (long)igraph_ecount(&self->g)); } /** \ingroup python_interface_copy * \brief Creates an exact deep copy of the graph * \return the copy of the graph */ PyObject *igraphmodule_Graph_copy(igraphmodule_GraphObject * self) { igraphmodule_GraphObject *result; igraph_t g; if (igraph_copy(&g, &self->g)) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH(result, g); return (PyObject *) result; } /********************************************************************** * The most basic igraph interface * **********************************************************************/ /** \ingroup python_interface_graph * \brief Returns the number of vertices in an \c igraph.Graph object. * \return the number of vertices as a \c PyObject * \sa igraph_vcount */ PyObject *igraphmodule_Graph_vcount(igraphmodule_GraphObject * self) { PyObject *result; result = Py_BuildValue("l", (long)igraph_vcount(&self->g)); return result; } /** \ingroup python_interface_graph * \brief Returns the number of edges in an \c igraph.Graph object. * \return the number of edges as a \c PyObject * \sa igraph_ecount */ PyObject *igraphmodule_Graph_ecount(igraphmodule_GraphObject * self) { PyObject *result; result = Py_BuildValue("l", (long)igraph_ecount(&self->g)); return result; } /** \ingroup python_interface_graph * \brief Checks whether an \c igraph.Graph object is a DAG. * \return \c True if the graph is directed, \c False otherwise. * \sa igraph_is_dag */ PyObject *igraphmodule_Graph_is_dag(igraphmodule_GraphObject * self) { igraph_bool_t res; if (igraph_is_dag(&self->g, &res)) { igraphmodule_handle_igraph_error(); return NULL; } if (res) Py_RETURN_TRUE; Py_RETURN_FALSE; } /** \ingroup python_interface_graph * \brief Checks whether an \c igraph.Graph object is directed. * \return \c True if the graph is directed, \c False otherwise. * \sa igraph_is_directed */ PyObject *igraphmodule_Graph_is_directed(igraphmodule_GraphObject * self) { if (igraph_is_directed(&self->g)) Py_RETURN_TRUE; Py_RETURN_FALSE; } /** * \ingroup python_interface_graph * \brief Checks whether a matching is valid in the context of an \c igraph.Graph * object. * \sa igraph_is_matching */ PyObject *igraphmodule_Graph_is_matching(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds) { static char* kwlist[] = { "matching", "types", NULL }; PyObject *matching_o, *types_o = Py_None; igraph_vector_long_t* matching = 0; igraph_vector_bool_t* types = 0; igraph_bool_t result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &matching_o, &types_o)) return NULL; if (igraphmodule_attrib_to_vector_long_t(matching_o, self, &matching, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraphmodule_attrib_to_vector_bool_t(types_o, self, &types, ATTRIBUTE_TYPE_VERTEX)) { if (matching != 0) { igraph_vector_long_destroy(matching); free(matching); } return NULL; } if (igraph_is_matching(&self->g, types, matching, &result)) { if (matching != 0) { igraph_vector_long_destroy(matching); free(matching); } if (types != 0) { igraph_vector_bool_destroy(types); free(types); } igraphmodule_handle_igraph_error(); return NULL; } if (matching != 0) { igraph_vector_long_destroy(matching); free(matching); } if (types != 0) { igraph_vector_bool_destroy(types); free(types); } if (result) Py_RETURN_TRUE; Py_RETURN_FALSE; } /** * \ingroup python_interface_graph * \brief Checks whether a matching is valid and maximal in the context of an * \c igraph.Graph object. * \sa igraph_is_maximal_matching */ PyObject *igraphmodule_Graph_is_maximal_matching(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds) { static char* kwlist[] = { "matching", "types", NULL }; PyObject *matching_o, *types_o = Py_None; igraph_vector_long_t* matching = 0; igraph_vector_bool_t* types = 0; igraph_bool_t result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &matching_o, &types_o)) return NULL; if (igraphmodule_attrib_to_vector_long_t(matching_o, self, &matching, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraphmodule_attrib_to_vector_bool_t(types_o, self, &types, ATTRIBUTE_TYPE_VERTEX)) { if (matching != 0) { igraph_vector_long_destroy(matching); free(matching); } return NULL; } if (igraph_is_maximal_matching(&self->g, types, matching, &result)) { if (matching != 0) { igraph_vector_long_destroy(matching); free(matching); } if (types != 0) { igraph_vector_bool_destroy(types); free(types); } igraphmodule_handle_igraph_error(); return NULL; } if (matching != 0) { igraph_vector_long_destroy(matching); free(matching); } if (types != 0) { igraph_vector_bool_destroy(types); free(types); } if (result) Py_RETURN_TRUE; Py_RETURN_FALSE; } /** \ingroup python_interface_graph * \brief Checks whether an \c igraph.Graph object is simple. * \return \c True if the graph is simple, \c False otherwise. * \sa igraph_is_simple */ PyObject *igraphmodule_Graph_is_simple(igraphmodule_GraphObject *self) { igraph_bool_t res; if (igraph_is_simple(&self->g, &res)) { igraphmodule_handle_igraph_error(); return NULL; } if (res) Py_RETURN_TRUE; Py_RETURN_FALSE; } /** \ingroup python_interface_graph * \brief Adds vertices to an \c igraph.Graph * \return the extended \c igraph.Graph object * \sa igraph_add_vertices */ PyObject *igraphmodule_Graph_add_vertices(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { long n; if (!PyArg_ParseTuple(args, "l", &n)) return NULL; if (igraph_add_vertices(&self->g, (igraph_integer_t) n, 0)) { igraphmodule_handle_igraph_error(); return NULL; } Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Removes vertices from an \c igraph.Graph * \return the modified \c igraph.Graph object * * \todo Need more error checking on vertex IDs. (igraph fails when an * invalid vertex ID is given) * \sa igraph_delete_vertices */ PyObject *igraphmodule_Graph_delete_vertices(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list; igraph_vs_t vs; if (!PyArg_ParseTuple(args, "O", &list)) return NULL; if (igraphmodule_PyObject_to_vs_t(list, &vs, &self->g, 0, 0)) return NULL; if (igraph_delete_vertices(&self->g, vs)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); return NULL; } igraph_vs_destroy(&vs); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Adds edges to an \c igraph.Graph * \return the extended \c igraph.Graph object * * \todo Need more error checking on vertex IDs. (igraph fails when an * invalid vertex ID is given) * \sa igraph_add_edges */ PyObject *igraphmodule_Graph_add_edges(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list; igraph_vector_t v; if (!PyArg_ParseTuple(args, "O", &list)) return NULL; if (igraphmodule_PyObject_to_edgelist(list, &v, &self->g)) return NULL; /* do the hard work :) */ if (igraph_add_edges(&self->g, &v, 0)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); return NULL; } igraph_vector_destroy(&v); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Deletes edges from an \c igraph.Graph * \return the extended \c igraph.Graph object * * \todo Need more error checking on vertex IDs. (igraph fails when an * invalid vertex ID is given) * \sa igraph_delete_edges */ PyObject *igraphmodule_Graph_delete_edges(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list; igraph_es_t es; static char *kwlist[] = { "edges", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &list)) return NULL; if (igraphmodule_PyObject_to_es_t(list, &es, &self->g, 0)) { /* something bad happened during conversion, return immediately */ return NULL; } if (igraph_delete_edges(&self->g, es)) { igraphmodule_handle_igraph_error(); igraph_es_destroy(&es); return NULL; } igraph_es_destroy(&es); Py_RETURN_NONE; } /********************************************************************** * tructural properties * **********************************************************************/ /** \ingroup python_interface_graph * \brief The degree of some vertices in an \c igraph.Graph * \return the degree list as a Python object * \sa igraph_degree */ PyObject *igraphmodule_Graph_degree(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list = Py_None; PyObject *loops = Py_True; PyObject *dtype_o = Py_None; PyObject *dmode_o = Py_None; igraph_neimode_t dmode = IGRAPH_ALL; igraph_vector_t result; igraph_vs_t vs; igraph_bool_t return_single = 0; static char *kwlist[] = { "vertices", "mode", "loops", "type", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &list, &dmode_o, &loops, &dtype_o)) return NULL; if (dmode_o == Py_None && dtype_o != Py_None) { dmode_o = dtype_o; PY_IGRAPH_DEPRECATED("type=... keyword argument is deprecated since igraph 0.6, use mode=... instead"); } if (igraphmodule_PyObject_to_neimode_t(dmode_o, &dmode)) return NULL; if (igraphmodule_PyObject_to_vs_t(list, &vs, &self->g, &return_single, 0)) { return NULL; } if (igraph_vector_init(&result, 0)) { igraph_vs_destroy(&vs); return NULL; } if (igraph_degree(&self->g, &result, vs, dmode, PyObject_IsTrue(loops))) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); igraph_vector_destroy(&result); return NULL; } if (!return_single) list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); else list = PyInt_FromLong((long int)VECTOR(result)[0]); igraph_vector_destroy(&result); igraph_vs_destroy(&vs); return list; } /** * \ingroup python_interface_graph * \brief Structural diversity index of some vertices in an \c igraph.Graph * \sa igraph_diversity */ PyObject *igraphmodule_Graph_diversity(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list = Py_None; PyObject *weights_o = Py_None; igraph_vector_t result, *weights = 0; igraph_vs_t vs; igraph_bool_t return_single = 0; igraph_integer_t no_of_nodes; static char *kwlist[] = { "vertices", "weights", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &list, &weights_o)) return NULL; if (igraphmodule_PyObject_to_vs_t(list, &vs, &self->g, &return_single, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&result, 0)) { igraph_vs_destroy(&vs); return NULL; } if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_vs_destroy(&vs); igraph_vector_destroy(&result); return NULL; } if (weights == 0) { /* Handle this case here because igraph_diversity bails out when no weights * are given. */ if (igraph_vs_size(&self->g, &vs, &no_of_nodes)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); igraph_vector_destroy(&result); return NULL; } if (igraph_vector_resize(&result, no_of_nodes)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); igraph_vector_destroy(&result); return NULL; } igraph_vector_fill(&result, 1.0); } else { if (igraph_diversity(&self->g, weights, &result, vs)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); igraph_vector_destroy(&result); igraph_vector_destroy(weights); free(weights); return NULL; } igraph_vector_destroy(weights); free(weights); } if (!return_single) list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_FLOAT); else list = PyFloat_FromDouble(VECTOR(result)[0]); igraph_vector_destroy(&result); igraph_vs_destroy(&vs); return list; } /** \ingroup python_interface_graph * \brief The strength (weighted degree) of some vertices in an \c igraph.Graph * \return the strength list as a Python object * \sa igraph_strength */ PyObject *igraphmodule_Graph_strength(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list = Py_None; PyObject *loops = Py_True; PyObject *dtype_o = Py_None; PyObject *dmode_o = Py_None; PyObject *weights_o = Py_None; igraph_neimode_t dmode = IGRAPH_ALL; igraph_vector_t result, *weights = 0; igraph_vs_t vs; igraph_bool_t return_single = 0; static char *kwlist[] = { "vertices", "mode", "loops", "weights", "type", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOO", kwlist, &list, &dmode_o, &loops, &weights_o, &dtype_o)) return NULL; if (dmode_o == Py_None && dtype_o != Py_None) { dmode_o = dtype_o; PY_IGRAPH_DEPRECATED("type=... keyword argument is deprecated since igraph 0.6, use mode=... instead"); } if (igraphmodule_PyObject_to_neimode_t(dmode_o, &dmode)) return NULL; if (igraphmodule_PyObject_to_vs_t(list, &vs, &self->g, &return_single, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&result, 0)) { igraph_vs_destroy(&vs); return NULL; } if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_vs_destroy(&vs); igraph_vector_destroy(&result); return NULL; } if (igraph_strength(&self->g, &result, vs, dmode, PyObject_IsTrue(loops), weights)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); igraph_vector_destroy(&result); if (weights) { igraph_vector_destroy(weights); free(weights); } return NULL; } if (weights) { igraph_vector_destroy(weights); free(weights); } if (!return_single) list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_FLOAT); else list = PyFloat_FromDouble(VECTOR(result)[0]); igraph_vector_destroy(&result); igraph_vs_destroy(&vs); return list; } /** \ingroup python_interface_graph * \brief Calculates the graph density * \return the density * \sa igraph_density */ PyObject *igraphmodule_Graph_density(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { char *kwlist[] = { "loops", NULL }; igraph_real_t result; PyObject *loops = Py_False; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &loops)) return NULL; if (igraph_density(&self->g, &result, PyObject_IsTrue(loops))) { igraphmodule_handle_igraph_error(); return NULL; } return Py_BuildValue("d", (double)result); } /** \ingroup python_interface_graph * \brief The maximum degree of some vertices in an \c igraph.Graph * \return the maxium degree as a Python object * \sa igraph_maxdegree */ PyObject *igraphmodule_Graph_maxdegree(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list = Py_None; igraph_neimode_t dmode = IGRAPH_ALL; PyObject *dtype_o = Py_None; PyObject *dmode_o = Py_None; PyObject *loops = Py_False; igraph_integer_t result; igraph_vs_t vs; igraph_bool_t return_single = 0; static char *kwlist[] = { "vertices", "mode", "loops", "type", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &list, &dmode_o, &loops, &dtype_o)) return NULL; if (dmode_o == Py_None && dtype_o != Py_None) { dmode_o = dtype_o; PY_IGRAPH_DEPRECATED("type=... keyword argument is deprecated since igraph 0.6, use mode=... instead"); } if (igraphmodule_PyObject_to_neimode_t(dmode_o, &dmode)) return NULL; if (igraphmodule_PyObject_to_vs_t(list, &vs, &self->g, &return_single, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_maxdegree(&self->g, &result, vs, dmode, PyObject_IsTrue(loops))) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); return NULL; } igraph_vs_destroy(&vs); return PyInt_FromLong((long)result); } /** \ingroup python_interface_graph * \brief Checks whether an edge is a loop edge * \return a boolean or a list of booleans * \sa igraph_is_loop */ PyObject *igraphmodule_Graph_is_loop(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { PyObject *list = Py_None; igraph_vector_bool_t result; igraph_es_t es; igraph_bool_t return_single = 0; static char *kwlist[] = { "edges", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &list)) return NULL; if (igraphmodule_PyObject_to_es_t(list, &es, &self->g, &return_single)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_bool_init(&result, 0)) { igraphmodule_handle_igraph_error(); igraph_es_destroy(&es); return NULL; } if (igraph_is_loop(&self->g, &result, es)) { igraphmodule_handle_igraph_error(); igraph_es_destroy(&es); igraph_vector_bool_destroy(&result); return NULL; } if (!return_single) list = igraphmodule_vector_bool_t_to_PyList(&result); else { list = (VECTOR(result)[0]) ? Py_True : Py_False; Py_INCREF(list); } igraph_vector_bool_destroy(&result); igraph_es_destroy(&es); return list; } /** \ingroup python_interface_graph * \brief Checks whether an edge is a multiple edge * \return a boolean or a list of booleans * \sa igraph_is_multiple */ PyObject *igraphmodule_Graph_is_multiple(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { PyObject *list = Py_None; igraph_vector_bool_t result; igraph_es_t es; igraph_bool_t return_single = 0; static char *kwlist[] = { "edges", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &list)) return NULL; if (igraphmodule_PyObject_to_es_t(list, &es, &self->g, &return_single)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_bool_init(&result, 0)) { igraphmodule_handle_igraph_error(); igraph_es_destroy(&es); return NULL; } if (igraph_is_multiple(&self->g, &result, es)) { igraphmodule_handle_igraph_error(); igraph_es_destroy(&es); igraph_vector_bool_destroy(&result); return NULL; } if (!return_single) list = igraphmodule_vector_bool_t_to_PyList(&result); else { list = (VECTOR(result)[0]) ? Py_True : Py_False; Py_INCREF(list); } igraph_vector_bool_destroy(&result); igraph_es_destroy(&es); return list; } /** \ingroup python_interface_graph * \brief Checks whether an edge is mutual * \return a boolean or a list of booleans * \sa igraph_is_mutual */ PyObject *igraphmodule_Graph_is_mutual(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { PyObject *list = Py_None; igraph_vector_bool_t result; igraph_es_t es; igraph_bool_t return_single = 0; static char *kwlist[] = { "edges", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &list)) return NULL; if (igraphmodule_PyObject_to_es_t(list, &es, &self->g, &return_single)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_bool_init(&result, 0)) { igraphmodule_handle_igraph_error(); igraph_es_destroy(&es); return NULL; } if (igraph_is_mutual(&self->g, &result, es)) { igraphmodule_handle_igraph_error(); igraph_es_destroy(&es); igraph_vector_bool_destroy(&result); return NULL; } if (!return_single) list = igraphmodule_vector_bool_t_to_PyList(&result); else { list = (VECTOR(result)[0]) ? Py_True : Py_False; Py_INCREF(list); } igraph_vector_bool_destroy(&result); igraph_es_destroy(&es); return list; } /** \ingroup python_interface_graph * \brief Checks whether an \c igraph.Graph object has multiple edges. * \return \c True if the graph has multiple edges, \c False otherwise. * \sa igraph_has_multiple */ PyObject *igraphmodule_Graph_has_multiple(igraphmodule_GraphObject *self) { igraph_bool_t res; if (igraph_has_multiple(&self->g, &res)) { igraphmodule_handle_igraph_error(); return NULL; } if (res) Py_RETURN_TRUE; Py_RETURN_FALSE; } /** \ingroup python_interface_graph * \brief Checks the multiplicity of the edges * \return the edge multiplicities as a Python list * \sa igraph_count_multiple */ PyObject *igraphmodule_Graph_count_multiple(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { PyObject *list = Py_None; igraph_vector_t result; igraph_es_t es; igraph_bool_t return_single = 0; static char *kwlist[] = { "edges", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &list)) return NULL; if (igraphmodule_PyObject_to_es_t(list, &es, &self->g, &return_single)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&result, 0)) { igraph_es_destroy(&es); return NULL; } if (igraph_count_multiple(&self->g, &result, es)) { igraphmodule_handle_igraph_error(); igraph_es_destroy(&es); igraph_vector_destroy(&result); return NULL; } if (!return_single) list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); else list = PyInt_FromLong((long int)VECTOR(result)[0]); igraph_vector_destroy(&result); igraph_es_destroy(&es); return list; } /** \ingroup python_interface_graph * \brief The neighbors of a given vertex in an \c igraph.Graph * This method accepts a single vertex ID as a parameter, and returns the * neighbors of the given vertex in the form of an integer list. A * second argument may be passed as well, meaning the type of neighbors to * be returned (\c OUT for successors, \c IN for predecessors or \c ALL * for both of them). This argument is ignored for undirected graphs. * * \return the neighbor list as a Python list object * \sa igraph_neighbors */ PyObject *igraphmodule_Graph_neighbors(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list, *dtype_o=Py_None, *dmode_o=Py_None, *index_o; igraph_neimode_t dmode = IGRAPH_ALL; igraph_integer_t idx; igraph_vector_t result; static char *kwlist[] = { "vertex", "mode", "type", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist, &index_o, &dmode_o, &dtype_o)) return NULL; if (dmode_o == Py_None && dtype_o != Py_None) { dmode_o = dtype_o; PY_IGRAPH_DEPRECATED("type=... keyword argument is deprecated since igraph 0.6, use mode=... instead"); } if (igraphmodule_PyObject_to_neimode_t(dmode_o, &dmode)) return NULL; if (igraphmodule_PyObject_to_vid(index_o, &idx, &self->g)) return NULL; if (igraph_vector_init(&result, 1)) return igraphmodule_handle_igraph_error(); if (igraph_neighbors(&self->g, &result, idx, dmode)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); return NULL; } list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&result); return list; } /** \ingroup python_interface_graph * \brief The incident edges of a given vertex in an \c igraph.Graph * This method accepts a single vertex ID as a parameter, and returns the * IDs of the incident edges of the given vertex in the form of an integer list. * A second argument may be passed as well, meaning the type of neighbors to * be returned (\c OUT for successors, \c IN for predecessors or \c ALL * for both of them). This argument is ignored for undirected graphs. * * \return the adjacency list as a Python list object * \sa igraph_incident */ PyObject *igraphmodule_Graph_incident(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list, *dmode_o = Py_None, *dtype_o = Py_None, *index_o; igraph_neimode_t dmode = IGRAPH_OUT; igraph_integer_t idx; igraph_vector_t result; static char *kwlist[] = { "vertex", "mode", "type", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist, &index_o, &dmode_o, &dtype_o)) return NULL; if (dmode_o == Py_None && dtype_o != Py_None) { dmode_o = dtype_o; PY_IGRAPH_DEPRECATED("type=... keyword argument is deprecated since igraph 0.6, use mode=... instead"); } if (igraphmodule_PyObject_to_neimode_t(dmode_o, &dmode)) return NULL; if (igraphmodule_PyObject_to_vid(index_o, &idx, &self->g)) return NULL; igraph_vector_init(&result, 1); if (igraph_incident(&self->g, &result, idx, dmode)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); return NULL; } list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&result); return list; } /** \ingroup python_interface_graph * \brief Calculates the graph reciprocity * \return the reciprocity * \sa igraph_reciprocity */ PyObject *igraphmodule_Graph_reciprocity(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { char *kwlist[] = { "ignore_loops", "mode", NULL }; igraph_real_t result; igraph_reciprocity_t mode = IGRAPH_RECIPROCITY_DEFAULT; PyObject *ignore_loops = Py_True, *mode_o = Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &ignore_loops, &mode_o)) return NULL; if (igraphmodule_PyObject_to_reciprocity_t(mode_o, &mode)) return NULL; if (igraph_reciprocity(&self->g, &result, PyObject_IsTrue(ignore_loops), mode)) { igraphmodule_handle_igraph_error(); return NULL; } return Py_BuildValue("d", (double)result); } /** \ingroup python_interface_graph * \brief The successors of a given vertex in an \c igraph.Graph * This method accepts a single vertex ID as a parameter, and returns the * successors of the given vertex in the form of an integer list. It * is equivalent to calling \c igraph.Graph.neighbors with \c type=OUT * * \return the successor list as a Python list object * \sa igraph_neighbors */ PyObject *igraphmodule_Graph_successors(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list, *index_o; igraph_integer_t idx; igraph_vector_t result; static char *kwlist[] = { "vertex", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &index_o)) return NULL; if (igraphmodule_PyObject_to_vid(index_o, &idx, &self->g)) return NULL; igraph_vector_init(&result, 1); if (igraph_neighbors(&self->g, &result, idx, IGRAPH_OUT)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); return NULL; } list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&result); return list; } /** \ingroup python_interface_graph * \brief The predecessors of a given vertex in an \c igraph.Graph * This method accepts a single vertex ID as a parameter, and returns the * predecessors of the given vertex in the form of an integer list. It * is equivalent to calling \c igraph.Graph.neighbors with \c type=IN * * \return the predecessor list as a Python list object * \sa igraph_neighbors */ PyObject *igraphmodule_Graph_predecessors(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *list, *index_o; igraph_integer_t idx; igraph_vector_t result; static char *kwlist[] = { "vertex", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &index_o)) return NULL; if (igraphmodule_PyObject_to_vid(index_o, &idx, &self->g)) return NULL; igraph_vector_init(&result, 1); if (igraph_neighbors(&self->g, &result, idx, IGRAPH_IN)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); return NULL; } list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&result); return list; } /** \ingroup python_interface_graph * \brief Decides whether a graph is connected. * \return Py_True if the graph is connected, Py_False otherwise * \sa igraph_is_connected */ PyObject *igraphmodule_Graph_is_connected(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { char *kwlist[] = { "mode", NULL }; PyObject *mode_o = Py_None; igraph_connectedness_t mode = IGRAPH_STRONG; igraph_bool_t res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &mode_o)) return NULL; if (igraphmodule_PyObject_to_connectedness_t(mode_o, &mode)) return NULL; if (igraph_is_connected(&self->g, &res, mode)) { igraphmodule_handle_igraph_error(); return NULL; } if (res) Py_RETURN_TRUE; Py_RETURN_FALSE; } /** \ingroup python_interface_graph * \brief Decides whether there is an edge from a given vertex to an other one. * \return Py_True if the vertices are directly connected, Py_False otherwise * \sa igraph_are_connected */ PyObject *igraphmodule_Graph_are_connected(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "v1", "v2", NULL }; PyObject *v1, *v2; igraph_integer_t idx1, idx2; igraph_bool_t res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &v1, &v2)) return NULL; if (igraphmodule_PyObject_to_vid(v1, &idx1, &self->g)) return NULL; if (igraphmodule_PyObject_to_vid(v2, &idx2, &self->g)) return NULL; if (igraph_are_connected(&self->g, idx1, idx2, &res)) return igraphmodule_handle_igraph_error(); if (res) Py_RETURN_TRUE; Py_RETURN_FALSE; } /** \ingroup python_interface_graph * \brief Returns the ID of an arbitrary edge between the given two vertices * \sa igraph_get_eid */ PyObject *igraphmodule_Graph_get_eid(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "v1", "v2", "directed", "error", NULL }; PyObject *v1, *v2; PyObject *directed = Py_True; PyObject *error = Py_True; igraph_integer_t idx1, idx2; igraph_integer_t result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, &v1, &v2, &directed, &error)) return NULL; if (igraphmodule_PyObject_to_vid(v1, &idx1, &self->g)) return NULL; if (igraphmodule_PyObject_to_vid(v2, &idx2, &self->g)) return NULL; if (igraph_get_eid(&self->g, &result, idx1, idx2, PyObject_IsTrue(directed), PyObject_IsTrue(error))) return igraphmodule_handle_igraph_error(); return Py_BuildValue("l", (long)result); } /** \ingroup python_interface_graph * \brief Returns the IDs of some edges between some vertices * \sa igraph_get_eids */ PyObject *igraphmodule_Graph_get_eids(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "pairs", "path", "directed", "error", NULL }; PyObject *pairs_o = Py_None, *path_o = Py_None; PyObject *directed = Py_True; PyObject *error = Py_True; PyObject *result = NULL; igraph_vector_t pairs, path, res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &pairs_o, &path_o, &directed, &error)) return NULL; if (igraph_vector_init(&res, 0)) return igraphmodule_handle_igraph_error(); if (pairs_o != Py_None) { if (igraphmodule_PyObject_to_edgelist(pairs_o, &pairs, &self->g)) { igraph_vector_destroy(&res); return NULL; } } if (path_o != Py_None) { if (igraphmodule_PyObject_to_vector_t(path_o, &path, 1)) { igraph_vector_destroy(&res); if (pairs_o != Py_None) igraph_vector_destroy(&pairs); return NULL; } } if (igraph_get_eids(&self->g, &res, pairs_o == Py_None ? 0 : &pairs, path_o == Py_None ? 0 : &path, PyObject_IsTrue(directed), PyObject_IsTrue(error))) { if (pairs_o != Py_None) igraph_vector_destroy(&pairs); if (path_o != Py_None) igraph_vector_destroy(&path); igraph_vector_destroy(&res); return igraphmodule_handle_igraph_error(); } if (pairs_o != Py_None) igraph_vector_destroy(&pairs); if (path_o != Py_None) igraph_vector_destroy(&path); result = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&res); return result; } /** \ingroup python_interface_graph * \brief Calculates the diameter of an \c igraph.Graph * This method accepts two optional parameters: the first one is * a boolean meaning whether to consider directed paths (and is * ignored for undirected graphs). The second one is only meaningful * in unconnected graphs: it is \c True if the longest geodesic * within a component should be returned and \c False if the number of * vertices should be returned. They both have a default value of \c False. * * \return the diameter as a Python integer * \sa igraph_diameter */ PyObject *igraphmodule_Graph_diameter(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *dir = Py_True, *vcount_if_unconnected = Py_True; PyObject *weights_o = Py_None; igraph_vector_t *weights = 0; static char *kwlist[] = { "directed", "unconn", "weights", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &dir, &vcount_if_unconnected, &weights_o)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; if (weights) { igraph_real_t i; if (igraph_diameter_dijkstra(&self->g, weights, &i, 0, 0, 0, PyObject_IsTrue(dir), PyObject_IsTrue(vcount_if_unconnected))) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(weights); free(weights); return NULL; } igraph_vector_destroy(weights); free(weights); return PyFloat_FromDouble((double)i); } else { igraph_integer_t i; if (igraph_diameter(&self->g, &i, 0, 0, 0, PyObject_IsTrue(dir), PyObject_IsTrue(vcount_if_unconnected))) { igraphmodule_handle_igraph_error(); return NULL; } return PyInt_FromLong((long)i); } } /** \ingroup python_interface_graph * \brief Returns a path of the actual diameter of the graph * \sa igraph_diameter */ PyObject *igraphmodule_Graph_get_diameter(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *dir = Py_True, *vcount_if_unconnected = Py_True, *result; PyObject *weights_o = Py_None; igraph_vector_t *weights = 0; igraph_vector_t res; static char *kwlist[] = { "directed", "unconn", "weights", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &dir, &vcount_if_unconnected, &weights_o)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; igraph_vector_init(&res, 0); if (weights) { if (igraph_diameter_dijkstra(&self->g, weights, 0, 0, 0, &res, PyObject_IsTrue(dir), PyObject_IsTrue(vcount_if_unconnected))) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(weights); free(weights); igraph_vector_destroy(&res); return NULL; } igraph_vector_destroy(weights); free(weights); } else { if (igraph_diameter(&self->g, 0, 0, 0, &res, PyObject_IsTrue(dir), PyObject_IsTrue(vcount_if_unconnected))) { igraphmodule_handle_igraph_error(); return NULL; } } result = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&res); return result; } /** \ingroup python_interface_graph * \brief Returns the farthest points and their distances in the graph * \sa igraph_distance */ PyObject *igraphmodule_Graph_farthest_points(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *dir = Py_True, *vcount_if_unconnected = Py_True; PyObject *weights_o = Py_None; igraph_vector_t *weights = 0; igraph_integer_t from, to, len; igraph_real_t len_real; static char *kwlist[] = { "directed", "unconn", "weights", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &dir, &vcount_if_unconnected, &weights_o)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; if (weights) { if (igraph_diameter_dijkstra(&self->g, weights, &len_real, &from, &to, 0, PyObject_IsTrue(dir), PyObject_IsTrue(vcount_if_unconnected))) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(weights); free(weights); return NULL; } igraph_vector_destroy(weights); free(weights); if (from >= 0) return Py_BuildValue("lld", (long)from, (long)to, (double)len_real); return Py_BuildValue("OOd", Py_None, Py_None, (double)len_real); } else { if (igraph_diameter(&self->g, &len, &from, &to, 0, PyObject_IsTrue(dir), PyObject_IsTrue(vcount_if_unconnected))) { igraphmodule_handle_igraph_error(); return NULL; } if (from >= 0) return Py_BuildValue("lll", (long)from, (long)to, (long)len); return Py_BuildValue("OOl", Py_None, Py_None, (long)len); } } /** * \ingroup python_interface_graph * \brief Calculates the girth of an \c igraph.Graph */ PyObject *igraphmodule_Graph_girth(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { PyObject *sc = Py_False; static char *kwlist[] = { "return_shortest_circle", NULL }; igraph_integer_t girth; igraph_vector_t vids; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &sc)) return NULL; igraph_vector_init(&vids, 0); if (igraph_girth(&self->g, &girth, &vids)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&vids); return NULL; } if (PyObject_IsTrue(sc)) { PyObject* o; o=igraphmodule_vector_t_to_PyList(&vids, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&vids); return o; } return PyInt_FromLong((long)girth); } /** * \ingroup python_interface_graph * \brief Calculates the convergence degree of the edges in a graph */ PyObject *igraphmodule_Graph_convergence_degree(igraphmodule_GraphObject *self) { igraph_vector_t result; PyObject *o; igraph_vector_init(&result, 0); if (igraph_convergence_degree(&self->g, &result, 0, 0)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); return NULL; } o=igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&result); return o; } /** * \ingroup python_interface_graph * \brief Calculates the sizes of the convergence fields in a graph */ PyObject *igraphmodule_Graph_convergence_field_size(igraphmodule_GraphObject *self) { igraph_vector_t ins, outs; PyObject *o1, *o2; igraph_vector_init(&ins, 0); igraph_vector_init(&outs, 0); if (igraph_convergence_degree(&self->g, 0, &ins, &outs)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&ins); igraph_vector_destroy(&outs); return NULL; } o1=igraphmodule_vector_t_to_PyList(&ins, IGRAPHMODULE_TYPE_INT); o2=igraphmodule_vector_t_to_PyList(&outs, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&ins); igraph_vector_destroy(&outs); return Py_BuildValue("NN", o1, o2); } /** * \ingroup python_interface_graph * \brief Calculates the average nearest neighbor degree of the vertices * of a \c igraph.Graph */ PyObject *igraphmodule_Graph_knn(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "vids", "weights", NULL }; PyObject *vids_obj = Py_None, *weights_obj = Py_None; PyObject *knn_obj, *knnk_obj; igraph_vector_t *weights = 0; igraph_vector_t knn, knnk; igraph_vs_t vids; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &vids_obj, &weights_obj)) { return NULL; } if (igraph_vector_init(&knn, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&knnk, 0)) { igraph_vector_destroy(&knn); igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_PyObject_to_vs_t(vids_obj, &vids, &self->g, 0, 0)) { igraph_vector_destroy(&knn); igraph_vector_destroy(&knnk); igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(weights_obj, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_vs_destroy(&vids); igraph_vector_destroy(&knn); igraph_vector_destroy(&knnk); return NULL; } if (igraph_avg_nearest_neighbor_degree(&self->g, vids, &knn, &knnk, weights)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vids); igraph_vector_destroy(&knn); igraph_vector_destroy(&knnk); if (weights) { igraph_vector_destroy(weights); free(weights); } return NULL; } igraph_vs_destroy(&vids); if (weights) { igraph_vector_destroy(weights); free(weights); } knn_obj = igraphmodule_vector_t_to_PyList(&knn, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&knn); if (!knn_obj) { igraph_vector_destroy(&knnk); return NULL; } knnk_obj = igraphmodule_vector_t_to_PyList(&knnk, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&knnk); if (!knnk_obj) { Py_DECREF(knn_obj); return NULL; } return Py_BuildValue("NN", knn_obj, knnk_obj); } /** \ingroup python_interface_graph * \brief Calculates the radius of an \c igraph.Graph * * \return the radius as a Python integer * \sa igraph_radius */ PyObject *igraphmodule_Graph_radius(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *mode_o = Py_None; igraph_neimode_t mode = IGRAPH_OUT; igraph_real_t radius; static char *kwlist[] = { "mode", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraph_radius(&self->g, &radius, mode)) { igraphmodule_handle_igraph_error(); return NULL; } return PyFloat_FromDouble((double)radius); } /********************************************************************** * Deterministic and non-deterministic graph generators * **********************************************************************/ /** \ingroup python_interface_graph * \brief Generates a graph from its adjacency matrix * \return a reference to the newly generated Python igraph object * \sa igraph_adjacency */ PyObject *igraphmodule_Graph_Adjacency(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; igraph_matrix_t m; PyObject *matrix, *mode_o = Py_None; igraph_adjacency_t mode = IGRAPH_ADJ_DIRECTED; static char *kwlist[] = { "matrix", "mode", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O", kwlist, &PyList_Type, &matrix, &mode_o)) return NULL; if (igraphmodule_PyObject_to_adjacency_t(mode_o, &mode)) return NULL; if (igraphmodule_PyList_to_matrix_t(matrix, &m)) { PyErr_SetString(PyExc_TypeError, "Error while converting adjacency matrix"); return NULL; } if (igraph_adjacency(&g, &m, mode)) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&m); return NULL; } igraph_matrix_destroy(&m); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph from the Graph Atlas * \return a reference to the newly generated Python igraph object * \sa igraph_atlas */ PyObject *igraphmodule_Graph_Atlas(PyTypeObject * type, PyObject * args) { long n; igraphmodule_GraphObject *self; igraph_t g; if (!PyArg_ParseTuple(args, "l", &n)) return NULL; if (igraph_atlas(&g, (igraph_integer_t) n)) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph based on the Barabasi-Albert model * This is intended to be a class method in Python, so the first argument * is the type object and not the Python igraph object (because we have * to allocate that in this method). * * \return a reference to the newly generated Python igraph object * \sa igraph_barabasi_game */ PyObject *igraphmodule_Graph_Barabasi(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long n, m = 1; float power = 1.0f, zero_appeal = 1.0f; igraph_vector_t outseq; igraph_t *start_from = 0; igraph_barabasi_algorithm_t algo = IGRAPH_BARABASI_PSUMTREE; PyObject *m_obj = 0, *outpref = Py_False, *directed = Py_False; PyObject *implementation_o = Py_None; PyObject *start_from_o = Py_None; static char *kwlist[] = { "n", "m", "outpref", "directed", "power", "zero_appeal", "implementation", "start_from", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|OOOffOO", kwlist, &n, &m_obj, &outpref, &directed, &power, &zero_appeal, &implementation_o, &start_from_o)) return NULL; if (igraphmodule_PyObject_to_barabasi_algorithm_t(implementation_o, &algo)) return NULL; if (igraphmodule_PyObject_to_igraph_t(start_from_o, &start_from)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "Number of vertices must be positive."); return NULL; } if (m_obj == 0) { igraph_vector_init(&outseq, 0); m = 1; } else if (m_obj != 0) { /* let's check whether we have a constant out-degree or a list */ if (PyInt_Check(m_obj)) { m = PyInt_AsLong(m_obj); igraph_vector_init(&outseq, 0); } else if (PyList_Check(m_obj)) { if (igraphmodule_PyObject_to_vector_t(m_obj, &outseq, 1)) { /* something bad happened during conversion */ return NULL; } } else { PyErr_SetString(PyExc_TypeError, "m must be an integer or a list of integers"); return NULL; } } if (igraph_barabasi_game(&g, (igraph_integer_t) n, (igraph_real_t) power, (igraph_integer_t) m, &outseq, PyObject_IsTrue(outpref), (igraph_real_t) zero_appeal, PyObject_IsTrue(directed), algo, start_from)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&outseq); return NULL; } igraph_vector_destroy(&outseq); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a bipartite graph * \return a reference to the newly generated Python igraph object * \sa igraph_barabasi_game */ PyObject *igraphmodule_Graph_Bipartite(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; igraph_vector_bool_t types; igraph_vector_t edges; PyObject *types_o, *edges_o, *directed = Py_False; static char *kwlist[] = { "types", "edges", "directed", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, &types_o, &edges_o, &directed)) return NULL; if (igraphmodule_PyObject_to_vector_bool_t(types_o, &types)) return NULL; if (igraphmodule_PyObject_to_edgelist(edges_o, &edges, 0)) { igraph_vector_bool_destroy(&types); return NULL; } if (igraph_create_bipartite(&g, &types, &edges, PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&edges); igraph_vector_bool_destroy(&types); return NULL; } igraph_vector_destroy(&edges); igraph_vector_bool_destroy(&types); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a De Bruijn graph * \sa igraph_kautz */ PyObject *igraphmodule_Graph_De_Bruijn(PyTypeObject *type, PyObject *args, PyObject *kwds) { long int m, n; igraphmodule_GraphObject *self; igraph_t g; static char *kwlist[] = {"m", "n", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll", kwlist, &m, &n)) return NULL; if (igraph_de_bruijn(&g, (igraph_integer_t) m, (igraph_integer_t) n)) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject*)self; } /** \ingroup python_interface_graph * \brief Generates a random graph with a given degree sequence * This is intended to be a class method in Python, so the first argument * is the type object and not the Python igraph object (because we have * to allocate that in this method). * * \return a reference to the newly generated Python igraph object * \sa igraph_degree_sequence_game */ PyObject *igraphmodule_Graph_Degree_Sequence(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; igraph_vector_t outseq, inseq; igraph_degseq_t meth = IGRAPH_DEGSEQ_SIMPLE; igraph_bool_t has_inseq = 0; PyObject *outdeg = NULL, *indeg = NULL, *method = NULL; static char *kwlist[] = { "out", "in", "method", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!O", kwlist, &PyList_Type, &outdeg, &PyList_Type, &indeg, &method)) return NULL; if (igraphmodule_PyObject_to_degseq_t(method, &meth)) return NULL; if (igraphmodule_PyObject_to_vector_t(outdeg, &outseq, 1)) return NULL; if (indeg) { if (igraphmodule_PyObject_to_vector_t(indeg, &inseq, 1)) { igraph_vector_destroy(&outseq); return NULL; } has_inseq=1; } if (igraph_degree_sequence_game(&g, &outseq, has_inseq ? &inseq : 0, meth)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&outseq); if (has_inseq) igraph_vector_destroy(&inseq); return NULL; } igraph_vector_destroy(&outseq); if (has_inseq) igraph_vector_destroy(&inseq); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph based on the Erdos-Renyi model * \return a reference to the newly generated Python igraph object * \sa igraph_erdos_renyi_game */ PyObject *igraphmodule_Graph_Erdos_Renyi(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long n, m = -1; double p = -1.0; igraph_erdos_renyi_t t; PyObject *loops = Py_False, *directed = Py_False; static char *kwlist[] = { "n", "p", "m", "directed", "loops", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|dlOO", kwlist, &n, &p, &m, &directed, &loops)) return NULL; if (m == -1 && p == -1.0) { /* no density parameters were given, throw exception */ PyErr_SetString(PyExc_TypeError, "Either m or p must be given."); return NULL; } if (m != -1 && p != -1.0) { /* both density parameters were given, throw exception */ PyErr_SetString(PyExc_TypeError, "Only one must be given from m and p."); return NULL; } t = (m == -1) ? IGRAPH_ERDOS_RENYI_GNP : IGRAPH_ERDOS_RENYI_GNM; if (igraph_erdos_renyi_game(&g, t, (igraph_integer_t) n, (igraph_real_t) (m == -1 ? p : m), PyObject_IsTrue(directed), PyObject_IsTrue(loops))) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph based on a simple growing model with vertex types * \return a reference to the newly generated Python igraph object * \sa igraph_establishment_game */ PyObject *igraphmodule_Graph_Establishment(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long n, types, k; PyObject *type_dist, *pref_matrix; PyObject *directed = Py_False; igraph_matrix_t pm; igraph_vector_t td; char *kwlist[] = { "n", "k", "type_dist", "pref_matrix", "directed", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "llO!O!|O", kwlist, &n, &k, &PyList_Type, &type_dist, &PyList_Type, &pref_matrix, &directed)) return NULL; if (n <= 0 || k <= 0) { PyErr_SetString(PyExc_ValueError, "Number of vertices and the amount of connection trials per step must be positive."); return NULL; } types = PyList_Size(type_dist); if (igraphmodule_PyList_to_matrix_t(pref_matrix, &pm)) { PyErr_SetString(PyExc_TypeError, "Error while converting preference matrix"); return NULL; } if (igraph_matrix_nrow(&pm) != igraph_matrix_ncol(&pm) || igraph_matrix_nrow(&pm) != types) { PyErr_SetString(PyExc_ValueError, "Preference matrix must have exactly the same rows and columns as the number of types"); igraph_matrix_destroy(&pm); return NULL; } if (igraphmodule_PyObject_to_vector_t(type_dist, &td, 1)) { PyErr_SetString(PyExc_ValueError, "Error while converting type distribution vector"); igraph_matrix_destroy(&pm); return NULL; } if (igraph_establishment_game(&g, (igraph_integer_t) n, (igraph_integer_t) types, (igraph_integer_t) k, &td, &pm, PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&pm); igraph_vector_destroy(&td); return NULL; } igraph_matrix_destroy(&pm); igraph_vector_destroy(&td); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a famous graph by name * \return a reference to the newly generated Python igraph object * \sa igraph_famous */ PyObject *igraphmodule_Graph_Famous(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; const char* name; static char *kwlist[] = { "name", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &name)) return NULL; if (igraph_famous(&g, name)) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph based on the forest fire model * \return a reference to the newly generated Python igraph object * \sa igraph_forest_fire_game */ PyObject *igraphmodule_Graph_Forest_Fire(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long n, ambs=1; double fw_prob, bw_factor=0.0; PyObject *directed = Py_False; static char *kwlist[] = {"n", "fw_prob", "bw_factor", "ambs", "directed", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ld|dlO", kwlist, &n, &fw_prob, &bw_factor, &ambs, &directed)) return NULL; if (igraph_forest_fire_game(&g, (igraph_integer_t)n, (igraph_real_t)fw_prob, (igraph_real_t)bw_factor, (igraph_integer_t)ambs, (igraph_bool_t)(PyObject_IsTrue(directed)))) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a full graph * \return a reference to the newly generated Python igraph object * \sa igraph_full */ PyObject *igraphmodule_Graph_Full(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long n; PyObject *loops = Py_False, *directed = Py_False; char *kwlist[] = { "n", "directed", "loops", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|OO", kwlist, &n, &directed, &loops)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "Number of vertices must be positive."); return NULL; } if (igraph_full(&g, (igraph_integer_t) n, PyObject_IsTrue(directed), PyObject_IsTrue(loops))) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a full bipartite graph * \sa igraph_full_bipartite */ PyObject *igraphmodule_Graph_Full_Bipartite(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; igraph_vector_bool_t vertex_types; igraph_neimode_t mode = IGRAPH_ALL; long int n1, n2; PyObject *mode_o = Py_None, *directed = Py_False, *vertex_types_o = 0; static char *kwlist[] = { "n1", "n2", "directed", "mode", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll|OO", kwlist, &n1, &n2, &directed, &mode_o)) return NULL; if (n1 < 0 || n2 < 0) { PyErr_SetString(PyExc_ValueError, "Number of vertices must be positive."); return NULL; } if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraph_vector_bool_init(&vertex_types, n1+n2)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_full_bipartite(&g, &vertex_types, (igraph_integer_t) n1, (igraph_integer_t) n2, PyObject_IsTrue(directed), mode)) { igraph_vector_bool_destroy(&vertex_types); igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); vertex_types_o = igraphmodule_vector_bool_t_to_PyList(&vertex_types); igraph_vector_bool_destroy(&vertex_types); if (vertex_types_o == 0) return NULL; return Py_BuildValue("NN", (PyObject *) self, vertex_types_o); } /** \ingroup python_interface_graph * \brief Generates a full citation graph * \return a reference to the newly generated Python igraph object * \sa igraph_full */ PyObject *igraphmodule_Graph_Full_Citation(PyTypeObject *type, PyObject *args, PyObject *kwds) { igraphmodule_GraphObject *self; igraph_t g; long n; PyObject *directed = Py_False; char *kwlist[] = { "n", "directed", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|O", kwlist, &n, &directed)) return NULL; if (igraph_full_citation(&g, (igraph_integer_t) n, (igraph_bool_t) PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph based on the geometric random model * \return a reference to the newly generated Python igraph object * \sa igraph_grg_game */ PyObject *igraphmodule_Graph_GRG(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long n; double r; PyObject *torus = Py_False; PyObject *o_xs, *o_ys; igraph_vector_t xs, ys; static char *kwlist[] = { "n", "radius", "torus", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ld|O", kwlist, &n, &r, &torus)) return NULL; if (igraph_vector_init(&xs, 0)) { igraphmodule_handle_igraph_error(); return NULL; } else if (igraph_vector_init(&ys, 0)) { igraph_vector_destroy(&xs); igraphmodule_handle_igraph_error(); return NULL; } if (igraph_grg_game(&g, (igraph_integer_t) n, (igraph_real_t) r, PyObject_IsTrue(torus), &xs, &ys)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&xs); igraph_vector_destroy(&ys); return NULL; } o_xs = igraphmodule_vector_t_to_PyList(&xs, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&xs); if (!o_xs) { igraph_destroy(&g); igraph_vector_destroy(&ys); return NULL; } o_ys = igraphmodule_vector_t_to_PyList(&ys, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&ys); if (!o_ys) { igraph_destroy(&g); Py_DECREF(o_xs); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return Py_BuildValue("NNN", (PyObject*)self, o_xs, o_ys); } /** \ingroup python_interface_graph * \brief Generates a growing random graph * \return a reference to the newly generated Python igraph object * \sa igraph_growing_random_game */ PyObject *igraphmodule_Graph_Growing_Random(PyTypeObject * type, PyObject * args, PyObject * kwds) { long n, m; PyObject *directed = NULL, *citation = NULL; igraphmodule_GraphObject *self; igraph_t g; static char *kwlist[] = { "n", "m", "directed", "citation", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll|O!O!", kwlist, &n, &m, &PyBool_Type, &directed, &PyBool_Type, &citation)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "Number of vertices must be positive."); return NULL; } if (m < 0) { PyErr_SetString(PyExc_ValueError, "Number of new edges per iteration must be positive."); return NULL; } if (igraph_growing_random_game(&g, (igraph_integer_t) n, (igraph_integer_t) m, (directed == Py_True), (citation == Py_True))) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a bipartite graph from an incidence matrix * \return a reference to the newly generated Python igraph object * \sa igraph_incidence */ PyObject *igraphmodule_Graph_Incidence(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_matrix_t matrix; igraph_vector_bool_t vertex_types; igraph_t g; PyObject *matrix_o, *vertex_types_o; PyObject *mode_o = Py_None, *directed = Py_False, *multiple = Py_False; igraph_neimode_t mode = IGRAPH_OUT; static char *kwlist[] = { "matrix", "directed", "mode", "multiple", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OOO", kwlist, &PyList_Type, &matrix_o, &directed, &mode_o, &multiple)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraph_vector_bool_init(&vertex_types, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_PyList_to_matrix_t(matrix_o, &matrix)) { igraph_vector_bool_destroy(&vertex_types); PyErr_SetString(PyExc_TypeError, "Error while converting incidence matrix"); return NULL; } if (igraph_incidence(&g, &vertex_types, &matrix, PyObject_IsTrue(directed), mode, PyObject_IsTrue(multiple))) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&matrix); igraph_vector_bool_destroy(&vertex_types); return NULL; } igraph_matrix_destroy(&matrix); CREATE_GRAPH_FROM_TYPE(self, g, type); vertex_types_o = igraphmodule_vector_bool_t_to_PyList(&vertex_types); igraph_vector_bool_destroy(&vertex_types); if (vertex_types_o == 0) return NULL; return Py_BuildValue("NN", (PyObject *) self, vertex_types_o); } /** \ingroup python_interface_graph * \brief Generates a graph with a given isomorphy class * This is intended to be a class method in Python, so the first argument * is the type object and not the Python igraph object (because we have * to allocate that in this method). * * \return a reference to the newly generated Python igraph object * \sa igraph_isoclass_create */ PyObject *igraphmodule_Graph_Isoclass(PyTypeObject * type, PyObject * args, PyObject * kwds) { long int n, isoclass; PyObject *directed = Py_False; igraphmodule_GraphObject *self; igraph_t g; static char *kwlist[] = { "n", "class", "directed", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll|O", kwlist, &n, &isoclass, &directed)) return NULL; if (n < 3 || n > 4) { PyErr_SetString(PyExc_ValueError, "Only graphs with 3 or 4 vertices are supported"); return NULL; } if (igraph_isoclass_create(&g, (igraph_integer_t) n, (igraph_integer_t) isoclass, PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a Kautz graph * \sa igraph_kautz */ PyObject *igraphmodule_Graph_Kautz(PyTypeObject *type, PyObject *args, PyObject *kwds) { long int m, n; igraphmodule_GraphObject *self; igraph_t g; static char *kwlist[] = {"m", "n", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll", kwlist, &m, &n)) return NULL; if (igraph_kautz(&g, (igraph_integer_t) m, (igraph_integer_t) n)) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject*)self; } /** \ingroup python_interface_graph * \brief Generates a k-regular random graph * \return a reference to the newly generated Python igraph object * \sa igraph_k_regular_game */ PyObject *igraphmodule_Graph_K_Regular(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long int n, k; PyObject *directed_o = Py_False, *multiple_o = Py_False; static char *kwlist[] = { "n", "k", "directed", "multiple", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll|OO", kwlist, &n, &k, &directed_o, &multiple_o)) return NULL; if (igraph_k_regular_game(&g, (igraph_integer_t) n, (igraph_integer_t) k, PyObject_IsTrue(directed_o), PyObject_IsTrue(multiple_o))) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject*)self; } /** \ingroup python_interface_graph * \brief Generates a regular lattice * \return a reference to the newly generated Python igraph object * \sa igraph_lattice */ PyObject *igraphmodule_Graph_Lattice(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraph_vector_t dimvector; long int nei = 1; igraph_bool_t directed; igraph_bool_t mutual; igraph_bool_t circular; PyObject *o_directed = Py_False, *o_mutual = Py_True, *o_circular = Py_True; PyObject *o_dimvector = Py_None; igraphmodule_GraphObject *self; igraph_t g; static char *kwlist[] = { "dim", "nei", "directed", "mutual", "circular", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|lOOO", kwlist, &PyList_Type, &o_dimvector, &nei, &o_directed, &o_mutual, &o_circular)) return NULL; directed = PyObject_IsTrue(o_directed); mutual = PyObject_IsTrue(o_mutual); circular = PyObject_IsTrue(o_circular); if (igraphmodule_PyObject_to_vector_t(o_dimvector, &dimvector, 1)) return NULL; if (igraph_lattice(&g, &dimvector, (igraph_integer_t) nei, directed, mutual, circular)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&dimvector); return NULL; } igraph_vector_destroy(&dimvector); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a 3-regular Hamiltonian graph from LCF notation * \return a reference to the newly generated Python igraph object * \sa igraph_lattice */ PyObject *igraphmodule_Graph_LCF(PyTypeObject *type, PyObject *args, PyObject *kwds) { igraph_vector_t shifts; long int repeats, n; PyObject *o_shifts; igraphmodule_GraphObject *self; igraph_t g; static char *kwlist[] = { "n", "shifts", "repeats", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "lOl", kwlist, &n, &o_shifts, &repeats)) return NULL; if (igraphmodule_PyObject_to_vector_t(o_shifts, &shifts, 0)) return NULL; if (igraph_lcf_vector(&g, (igraph_integer_t) n, &shifts, (igraph_integer_t) repeats)) { igraph_vector_destroy(&shifts); igraphmodule_handle_igraph_error(); return NULL; } igraph_vector_destroy(&shifts); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph based on vertex types and connection preferences * \return a reference to the newly generated Python igraph object * \sa igraph_preference_game */ PyObject *igraphmodule_Graph_Preference(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long n, types; PyObject *type_dist, *pref_matrix; PyObject *directed = Py_False; PyObject *loops = Py_False; igraph_matrix_t pm; igraph_vector_t td; igraph_vector_t type_vec; PyObject *type_vec_o; PyObject *attribute_key = Py_None; igraph_bool_t store_attribs; char *kwlist[] = { "n", "type_dist", "pref_matrix", "attribute", "directed", "loops", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "lO!O!|OOO", kwlist, &n, &PyList_Type, &type_dist, &PyList_Type, &pref_matrix, &attribute_key, &directed, &loops)) return NULL; types = PyList_Size(type_dist); if (igraphmodule_PyList_to_matrix_t(pref_matrix, &pm)) return NULL; if (igraphmodule_PyObject_float_to_vector_t(type_dist, &td)) { igraph_matrix_destroy(&pm); return NULL; } store_attribs = (attribute_key && attribute_key != Py_None); if (store_attribs && igraph_vector_init(&type_vec, (igraph_integer_t) n)) { igraph_matrix_destroy(&pm); igraph_vector_destroy(&td); igraphmodule_handle_igraph_error(); return NULL; } if (igraph_preference_game(&g, (igraph_integer_t) n, (igraph_integer_t) types, &td, 0, &pm, store_attribs ? &type_vec : 0, PyObject_IsTrue(directed), PyObject_IsTrue(loops))) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&pm); igraph_vector_destroy(&td); if (store_attribs) igraph_vector_destroy(&type_vec); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); if (store_attribs) { type_vec_o = igraphmodule_vector_t_to_PyList(&type_vec, IGRAPHMODULE_TYPE_INT); if (type_vec_o == 0) { igraph_matrix_destroy(&pm); igraph_vector_destroy(&td); igraph_vector_destroy(&type_vec); Py_DECREF(self); return NULL; } if (attribute_key != Py_None && attribute_key != 0) { if (PyDict_SetItem(ATTR_STRUCT_DICT(&self->g)[ATTRHASH_IDX_VERTEX], attribute_key, type_vec_o) == -1) { Py_DECREF(type_vec_o); igraph_matrix_destroy(&pm); igraph_vector_destroy(&td); igraph_vector_destroy(&type_vec); Py_DECREF(self); return NULL; } } Py_DECREF(type_vec_o); igraph_vector_destroy(&type_vec); } igraph_matrix_destroy(&pm); igraph_vector_destroy(&td); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph based on asymmetric vertex types and connection preferences * \return a reference to the newly generated Python igraph object * \sa igraph_asymmetric_preference_game */ PyObject *igraphmodule_Graph_Asymmetric_Preference(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long n, types; PyObject *type_dist_matrix, *pref_matrix; PyObject *loops = Py_False; igraph_matrix_t pm; igraph_matrix_t td; igraph_vector_t in_type_vec, out_type_vec; PyObject *type_vec_o; PyObject *attribute_key = Py_None; igraph_bool_t store_attribs; char *kwlist[] = { "n", "type_dist_matrix", "pref_matrix", "attribute", "loops", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "lO!O!|OO", kwlist, &n, &PyList_Type, &type_dist_matrix, &PyList_Type, &pref_matrix, &attribute_key, &loops)) return NULL; types = PyList_Size(type_dist_matrix); if (igraphmodule_PyList_to_matrix_t(pref_matrix, &pm)) return NULL; if (igraphmodule_PyList_to_matrix_t(type_dist_matrix, &td)) { igraph_matrix_destroy(&pm); return NULL; } store_attribs = (attribute_key && attribute_key != Py_None); if (store_attribs) { if (igraph_vector_init(&in_type_vec, (igraph_integer_t) n)) { igraph_matrix_destroy(&pm); igraph_matrix_destroy(&td); igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&out_type_vec, (igraph_integer_t) n)) { igraph_matrix_destroy(&pm); igraph_matrix_destroy(&td); igraph_vector_destroy(&in_type_vec); igraphmodule_handle_igraph_error(); return NULL; } } if (igraph_asymmetric_preference_game(&g, (igraph_integer_t) n, (igraph_integer_t) types, &td, &pm, store_attribs ? &in_type_vec : 0, store_attribs ? &out_type_vec : 0, PyObject_IsTrue(loops))) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&in_type_vec); igraph_vector_destroy(&out_type_vec); igraph_matrix_destroy(&pm); igraph_matrix_destroy(&td); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); if (store_attribs) { type_vec_o = igraphmodule_vector_t_pair_to_PyList(&in_type_vec, &out_type_vec); if (type_vec_o == NULL) { igraph_matrix_destroy(&pm); igraph_matrix_destroy(&td); igraph_vector_destroy(&in_type_vec); igraph_vector_destroy(&out_type_vec); Py_DECREF(self); return NULL; } if (attribute_key != Py_None && attribute_key != 0) { if (PyDict_SetItem(ATTR_STRUCT_DICT(&self->g)[ATTRHASH_IDX_VERTEX], attribute_key, type_vec_o) == -1) { Py_DECREF(type_vec_o); igraph_matrix_destroy(&pm); igraph_matrix_destroy(&td); igraph_vector_destroy(&in_type_vec); igraph_vector_destroy(&out_type_vec); Py_DECREF(self); return NULL; } } Py_DECREF(type_vec_o); igraph_vector_destroy(&in_type_vec); igraph_vector_destroy(&out_type_vec); } igraph_matrix_destroy(&pm); igraph_matrix_destroy(&td); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a bipartite graph based on the Erdos-Renyi model * \return a reference to the newly generated Python igraph object * \sa igraph_bipartite_game */ PyObject *igraphmodule_Graph_Random_Bipartite(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long int n1, n2, m = -1; double p = -1.0; igraph_erdos_renyi_t t; igraph_neimode_t neimode = IGRAPH_ALL; PyObject *directed_o = Py_False, *neimode_o = NULL; igraph_vector_bool_t vertex_types; PyObject *vertex_types_o; static char *kwlist[] = { "n1", "n2", "p", "m", "directed", "neimode", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll|dlOO", kwlist, &n1, &n2, &p, &m, &directed_o, &neimode_o)) return NULL; if (m == -1 && p == -1.0) { /* no density parameters were given, throw exception */ PyErr_SetString(PyExc_TypeError, "Either m or p must be given."); return NULL; } if (m != -1 && p != -1.0) { /* both density parameters were given, throw exception */ PyErr_SetString(PyExc_TypeError, "Only one must be given from m and p."); return NULL; } t = (m == -1) ? IGRAPH_ERDOS_RENYI_GNP : IGRAPH_ERDOS_RENYI_GNM; if (igraphmodule_PyObject_to_neimode_t(neimode_o, &neimode)) return NULL; if (igraph_vector_bool_init(&vertex_types, n1+n2)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_bipartite_game(&g, &vertex_types, t, (igraph_integer_t) n1, (igraph_integer_t) n2, (igraph_real_t) p, (igraph_integer_t) m, PyObject_IsTrue(directed_o), neimode)) { igraph_vector_bool_destroy(&vertex_types); igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); vertex_types_o = igraphmodule_vector_bool_t_to_PyList(&vertex_types); igraph_vector_bool_destroy(&vertex_types); if (vertex_types_o == 0) return NULL; return Py_BuildValue("NN", (PyObject *) self, vertex_types_o); } /** \ingroup python_interface_graph * \brief Generates a graph based on sort of a "windowed" Barabasi-Albert model * \return a reference to the newly generated Python igraph object * \sa igraph_recent_degree_game */ PyObject *igraphmodule_Graph_Recent_Degree(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long n, m = 0, window = 0; float power = 0.0f, zero_appeal = 0.0f; igraph_vector_t outseq; PyObject *m_obj, *outpref = Py_False, *directed = Py_False; char *kwlist[] = { "n", "m", "window", "outpref", "directed", "power", "zero_appeal", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "lOl|OOff", kwlist, &n, &m_obj, &window, &outpref, &directed, &power, &zero_appeal)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "Number of vertices must be positive."); return NULL; } // let's check whether we have a constant out-degree or a list if (PyInt_Check(m_obj)) { m = PyInt_AsLong(m_obj); igraph_vector_init(&outseq, 0); } else if (PyList_Check(m_obj)) { if (igraphmodule_PyObject_to_vector_t(m_obj, &outseq, 1)) { // something bad happened during conversion return NULL; } } if (igraph_recent_degree_game(&g, (igraph_integer_t) n, (igraph_real_t) power, (igraph_integer_t) window, (igraph_integer_t) m, &outseq, PyObject_IsTrue(outpref), (igraph_real_t) zero_appeal, PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&outseq); return NULL; } igraph_vector_destroy(&outseq); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a ring-shaped graph * \return a reference to the newly generated Python igraph object * \sa igraph_ring */ PyObject *igraphmodule_Graph_Ring(PyTypeObject * type, PyObject * args, PyObject * kwds) { long n; PyObject *directed = Py_False, *mutual = Py_False, *circular = Py_True; igraphmodule_GraphObject *self; igraph_t g; static char *kwlist[] = { "n", "directed", "mutual", "circular", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|O!O!O!", kwlist, &n, &PyBool_Type, &directed, &PyBool_Type, &mutual, &PyBool_Type, &circular)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "Number of vertices must be positive."); return NULL; } if (igraph_ring(&g, (igraph_integer_t) n, (directed == Py_True), (mutual == Py_True), (circular == Py_True))) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph based on a stochastic blockmodel * \return a reference to the newly generated Python igraph object * \sa igraph_sbm_game */ PyObject *igraphmodule_Graph_SBM(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; long int n; PyObject *block_sizes_o, *pref_matrix_o; PyObject *directed_o = Py_False; PyObject *loops_o = Py_False; igraph_matrix_t pref_matrix; igraph_vector_int_t block_sizes; static char *kwlist[] = { "n", "pref_matrix", "block_sizes", "directed", "loops", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "lO!O!|OO", kwlist, &n, &PyList_Type, &pref_matrix_o, &PyList_Type, &block_sizes_o, &directed_o, &loops_o)) return NULL; if (igraphmodule_PyList_to_matrix_t(pref_matrix_o, &pref_matrix)) return NULL; if (igraphmodule_PyObject_to_vector_int_t(block_sizes_o, &block_sizes)) { igraph_matrix_destroy(&pref_matrix); return NULL; } if (igraph_sbm_game(&g, (igraph_integer_t) n, &pref_matrix, &block_sizes, PyObject_IsTrue(directed_o), PyObject_IsTrue(loops_o))) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&pref_matrix); igraph_vector_int_destroy(&block_sizes); return NULL; } igraph_matrix_destroy(&pref_matrix); igraph_vector_int_destroy(&block_sizes); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a star graph * \return a reference to the newly generated Python igraph object * \sa igraph_star */ PyObject *igraphmodule_Graph_Star(PyTypeObject * type, PyObject * args, PyObject * kwds) { long n, center = 0; igraph_star_mode_t mode = IGRAPH_STAR_UNDIRECTED; PyObject* mode_o = Py_None; igraphmodule_GraphObject *self; igraph_t g; static char *kwlist[] = { "n", "mode", "center", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|Ol", kwlist, &n, &mode_o, ¢er)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "Number of vertices must be positive."); return NULL; } if (center >= n || center < 0) { PyErr_SetString(PyExc_ValueError, "Central vertex ID should be between 0 and n-1"); return NULL; } if (igraphmodule_PyObject_to_star_mode_t(mode_o, &mode)) { PyErr_SetString(PyExc_ValueError, "Mode should be either \"in\", \"out\", \"mutual\" or \"undirected\""); return NULL; } if (igraph_star(&g, (igraph_integer_t) n, mode, (igraph_integer_t) center)) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a non-growing random graph with edge probabilities * proportional to node fitnesses. * \return a reference to the newly generated Python igraph object * \sa igraph_static_fitness_game */ PyObject *igraphmodule_Graph_Static_Fitness(PyTypeObject *type, PyObject* args, PyObject* kwds) { igraphmodule_GraphObject *self; igraph_t g; long int m; PyObject *fitness_out_o = Py_None, *fitness_in_o = Py_None; PyObject *fitness_o = Py_None; PyObject *multiple = Py_False, *loops = Py_False; igraph_vector_t fitness_out, fitness_in; static char *kwlist[] = { "m", "fitness_out", "fitness_in", "loops", "multiple", "fitness", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|OOOOO", kwlist, &m, &fitness_out_o, &fitness_in_o, &loops, &multiple, &fitness_o)) return NULL; /* This trickery allows us to use "fitness" or "fitness_out" as * keyword argument, with "fitness_out" taking precedence over * "fitness" */ if (fitness_out_o == Py_None) fitness_out_o = fitness_o; if (fitness_out_o == Py_None) { PyErr_SetString(PyExc_TypeError, "Required argument 'fitness_out' (pos 2) not found"); return NULL; } if (igraphmodule_PyObject_float_to_vector_t(fitness_out_o, &fitness_out)) return NULL; if (fitness_in_o != Py_None) { if (igraphmodule_PyObject_float_to_vector_t(fitness_in_o, &fitness_in)) { igraph_vector_destroy(&fitness_out); return NULL; } } if (igraph_static_fitness_game(&g, (igraph_integer_t) m, &fitness_out, fitness_in_o == Py_None ? 0 : &fitness_in, PyObject_IsTrue(loops), PyObject_IsTrue(multiple))) { igraph_vector_destroy(&fitness_out); if (fitness_in_o != Py_None) igraph_vector_destroy(&fitness_in); igraphmodule_handle_igraph_error(); return NULL; } igraph_vector_destroy(&fitness_out); if (fitness_in_o != Py_None) igraph_vector_destroy(&fitness_in); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a non-growing random graph with prescribed power-law * degree distributions. * \return a reference to the newly generated Python igraph object * \sa igraph_static_power_law_game */ PyObject *igraphmodule_Graph_Static_Power_Law(PyTypeObject *type, PyObject* args, PyObject* kwds) { igraphmodule_GraphObject *self; igraph_t g; long int n, m; float exponent_out = -1.0f, exponent_in = -1.0f, exponent = -1.0f; PyObject *multiple = Py_False, *loops = Py_False; PyObject *finite_size_correction = Py_True; static char *kwlist[] = { "n", "m", "exponent_out", "exponent_in", "loops", "multiple", "finite_size_correction", "exponent", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll|ffOOOf", kwlist, &n, &m, &exponent_out, &exponent_in, &loops, &multiple, &finite_size_correction, &exponent)) return NULL; /* This trickery allows us to use "exponent" or "exponent_out" as * keyword argument, with "exponent_out" taking precedence over * "exponent" */ if (exponent_out == -1.0) exponent_out = exponent; if (exponent_out == -1.0) { PyErr_SetString(PyExc_TypeError, "Required argument 'exponent_out' (pos 3) not found"); return NULL; } if (igraph_static_power_law_game(&g, (igraph_integer_t) n, (igraph_integer_t) m, exponent_out, exponent_in, PyObject_IsTrue(loops), PyObject_IsTrue(multiple), PyObject_IsTrue(finite_size_correction))) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a tree graph where almost all vertices have an equal number of children * \return a reference to the newly generated Python igraph object * \sa igraph_tree */ PyObject *igraphmodule_Graph_Tree(PyTypeObject * type, PyObject * args, PyObject * kwds) { long int n, children; PyObject *tree_mode_o = Py_None, *tree_type_o = Py_None; igraph_tree_mode_t mode = IGRAPH_TREE_UNDIRECTED; igraphmodule_GraphObject *self; igraph_t g; static char *kwlist[] = { "n", "children", "mode", "type", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll|OO", kwlist, &n, &children, &tree_mode_o, &tree_type_o)) return NULL; if (n < 0) { PyErr_SetString(PyExc_ValueError, "Number of vertices must be positive."); return NULL; } if (tree_mode_o == Py_None && tree_type_o != Py_None) { tree_mode_o = tree_type_o; PY_IGRAPH_DEPRECATED("type=... keyword argument is deprecated since igraph 0.6, use mode=... instead"); } if (igraphmodule_PyObject_to_tree_mode_t(tree_mode_o, &mode)) { return NULL; } if (igraph_tree(&g, (igraph_integer_t) n, (igraph_integer_t) children, mode)) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph based on the Watts-Strogatz model * \return a reference to the newly generated Python igraph object * \sa igraph_watts_strogatz_game */ PyObject *igraphmodule_Graph_Watts_Strogatz(PyTypeObject * type, PyObject * args, PyObject * kwds) { long int nei = 1, dim, size; double p; PyObject* loops = Py_False; PyObject* multiple = Py_False; igraphmodule_GraphObject *self; igraph_t g; static char *kwlist[] = { "dim", "size", "nei", "p", "loops", "multiple", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "llld|OO", kwlist, &dim, &size, &nei, &p, &loops, &multiple)) return NULL; if (igraph_watts_strogatz_game(&g, (igraph_integer_t) dim, (igraph_integer_t) size, (igraph_integer_t) nei, p, PyObject_IsTrue(loops), PyObject_IsTrue(multiple))) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Generates a graph from its weighted adjacency matrix * \return a reference to the newly generated Python igraph object * \sa igraph_weighted_adjacency */ PyObject *igraphmodule_Graph_Weighted_Adjacency(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; igraph_matrix_t m; PyObject *matrix, *mode_o = Py_None, *attr_o = Py_None, *s = 0; PyObject *loops = Py_True; char* attr = 0; igraph_adjacency_t mode = IGRAPH_ADJ_DIRECTED; static char *kwlist[] = { "matrix", "mode", "attr", "loops", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OOO", kwlist, &PyList_Type, &matrix, &mode_o, &attr_o, &loops)) return NULL; if (igraphmodule_PyObject_to_adjacency_t(mode_o, &mode)) return NULL; if (attr_o != Py_None) { s = PyObject_Str(attr_o); if (s) { attr = PyString_CopyAsString(s); if (attr == 0) return NULL; } else return NULL; } if (igraphmodule_PyList_to_matrix_t(matrix, &m)) { if (attr != 0) free(attr); PyErr_SetString(PyExc_TypeError, "Error while converting adjacency matrix"); return NULL; } if (igraph_weighted_adjacency(&g, &m, mode, attr ? attr : "weight", PyObject_IsTrue(loops))) { igraphmodule_handle_igraph_error(); if (attr != 0) free(attr); igraph_matrix_destroy(&m); return NULL; } if (attr != 0) free(attr); igraph_matrix_destroy(&m); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /********************************************************************** * Advanced structural properties of graphs * **********************************************************************/ /** \ingroup python_interface_graph * \brief Calculates the articulation points of a graph. * \return the list of articulation points in a PyObject * \sa igraph_articulation_points */ PyObject *igraphmodule_Graph_articulation_points(igraphmodule_GraphObject *self) { igraph_vector_t res; PyObject *o; if (igraph_vector_init(&res, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_articulation_points(&self->g, &res)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&res); return NULL; } igraph_vector_sort(&res); o = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&res); return o; } /** \ingroup python_interface_graph * \brief Calculates the nominal assortativity coefficient * \sa igraph_assortativity_nominal */ PyObject *igraphmodule_Graph_assortativity_nominal(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "types", "directed", NULL }; PyObject *types_o = Py_None, *directed = Py_True; igraph_real_t res; int ret; igraph_vector_t *types = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &types_o, &directed)) return NULL; if (igraphmodule_attrib_to_vector_t(types_o, self, &types, ATTRIBUTE_TYPE_VERTEX)) return NULL; ret = igraph_assortativity_nominal(&self->g, types, &res, PyObject_IsTrue(directed)); if (types) { igraph_vector_destroy(types); free(types); } if (ret) { igraphmodule_handle_igraph_error(); return NULL; } return Py_BuildValue("d", (double)(res)); } /** \ingroup python_interface_graph * \brief Calculates the assortativity coefficient * \sa igraph_assortativity */ PyObject *igraphmodule_Graph_assortativity(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "types1", "types2", "directed", NULL }; PyObject *types1_o = Py_None, *types2_o = Py_None, *directed = Py_True; igraph_real_t res; int ret; igraph_vector_t *types1 = 0, *types2 = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist, &types1_o, &types2_o, &directed)) return NULL; if (igraphmodule_attrib_to_vector_t(types1_o, self, &types1, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraphmodule_attrib_to_vector_t(types2_o, self, &types2, ATTRIBUTE_TYPE_VERTEX)) { if (types1) { igraph_vector_destroy(types1); free(types1); } return NULL; } ret = igraph_assortativity(&self->g, types1, types2, &res, PyObject_IsTrue(directed)); if (types1) { igraph_vector_destroy(types1); free(types1); } if (types2) { igraph_vector_destroy(types2); free(types2); } if (ret) { igraphmodule_handle_igraph_error(); return NULL; } return Py_BuildValue("d", (double)(res)); } /** \ingroup python_interface_graph * \brief Calculates the assortativity coefficient for degrees * \sa igraph_assortativity_degree */ PyObject *igraphmodule_Graph_assortativity_degree(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "directed", NULL }; PyObject *directed = Py_True; igraph_real_t res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &directed)) return NULL; if (igraph_assortativity_degree(&self->g, &res, PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); return NULL; } return Py_BuildValue("d", (double)(res)); } /** \ingroup python_interface_graph * \brief Calculates Kleinberg's authority scores of the vertices in the graph * \sa igraph_authority_score */ PyObject *igraphmodule_Graph_authority_score( igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "weights", "scale", "arpack_options", "return_eigenvalue", NULL }; PyObject *scale_o = Py_True, *weights_o = Py_None; PyObject *arpack_options_o = igraphmodule_arpack_options_default; igraphmodule_ARPACKOptionsObject *arpack_options; PyObject *return_eigenvalue = Py_False; PyObject *res_o; igraph_real_t value; igraph_vector_t res, *weights = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO!O", kwlist, &weights_o, &scale_o, &igraphmodule_ARPACKOptionsType, &arpack_options_o, &return_eigenvalue)) return NULL; if (igraph_vector_init(&res, 0)) return igraphmodule_handle_igraph_error(); if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; arpack_options = (igraphmodule_ARPACKOptionsObject*)arpack_options_o; if (igraph_authority_score(&self->g, &res, &value, PyObject_IsTrue(scale_o), weights, igraphmodule_ARPACKOptions_get(arpack_options))) { igraphmodule_handle_igraph_error(); if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vector_destroy(&res); return NULL; } if (weights) { igraph_vector_destroy(weights); free(weights); } res_o = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&res); if (res_o == NULL) return igraphmodule_handle_igraph_error(); if (PyObject_IsTrue(return_eigenvalue)) { PyObject *ev_o = PyFloat_FromDouble((double)value); if (ev_o == NULL) { Py_DECREF(res_o); return igraphmodule_handle_igraph_error(); } return Py_BuildValue("NN", res_o, ev_o); } return res_o; } /** \ingroup python_interface_graph * \brief Calculates the average path length in a graph. * \return the average path length as a PyObject * \sa igraph_average_path_length */ PyObject *igraphmodule_Graph_average_path_length(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { char *kwlist[] = { "directed", "unconn", NULL }; PyObject *directed = Py_True, *unconn = Py_True; igraph_real_t res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!O!", kwlist, &PyBool_Type, &directed, &PyBool_Type, &unconn)) return NULL; if (igraph_average_path_length(&self->g, &res, (directed == Py_True), (unconn == Py_True))) { igraphmodule_handle_igraph_error(); return NULL; } return PyFloat_FromDouble(res); } /** \ingroup python_interface_graph * \brief Calculates the betweennesses of some vertices in a graph. * \return the betweennesses as a list (or a single float) * \sa igraph_betweenness */ PyObject *igraphmodule_Graph_betweenness(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "vertices", "directed", "cutoff", "weights", "nobigint", NULL }; PyObject *directed = Py_True; PyObject *vobj = Py_None, *list; PyObject *cutoff = Py_None; PyObject *weights_o = Py_None; PyObject *nobigint = Py_True; igraph_vector_t res, *weights = 0; igraph_bool_t return_single = 0; igraph_vs_t vs; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOO", kwlist, &vobj, &directed, &cutoff, &weights_o, &nobigint)) { return NULL; } if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) { if (weights) { igraph_vector_destroy(weights); free(weights); } igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&res, 0)) { igraph_vs_destroy(&vs); if (weights) { igraph_vector_destroy(weights); free(weights); } return igraphmodule_handle_igraph_error(); } if (cutoff == Py_None) { if (igraph_betweenness(&self->g, &res, vs, PyObject_IsTrue(directed), weights, PyObject_IsTrue(nobigint))) { igraph_vs_destroy(&vs); igraph_vector_destroy(&res); if (weights) { igraph_vector_destroy(weights); free(weights); } igraphmodule_handle_igraph_error(); return NULL; } } else if (PyNumber_Check(cutoff)) { PyObject *cutoff_num = PyNumber_Int(cutoff); if (cutoff_num == NULL) { igraph_vs_destroy(&vs); igraph_vector_destroy(&res); if (weights) { igraph_vector_destroy(weights); free(weights); } return NULL; } if (igraph_betweenness_estimate(&self->g, &res, vs, PyObject_IsTrue(directed), (igraph_integer_t)PyInt_AsLong(cutoff_num), weights, PyObject_IsTrue(nobigint))) { igraph_vs_destroy(&vs); igraph_vector_destroy(&res); if (weights) { igraph_vector_destroy(weights); free(weights); } Py_DECREF(cutoff_num); igraphmodule_handle_igraph_error(); return NULL; } Py_DECREF(cutoff_num); } else { PyErr_SetString(PyExc_TypeError, "cutoff value must be None or integer"); igraph_vs_destroy(&vs); igraph_vector_destroy(&res); if (weights) { igraph_vector_destroy(weights); free(weights); } return NULL; } if (!return_single) list = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); else list = PyFloat_FromDouble(VECTOR(res)[0]); igraph_vector_destroy(&res); igraph_vs_destroy(&vs); if (weights) { igraph_vector_destroy(weights); free(weights); } return list; } /** \ingroup python_interface_graph * \brief Calculates the bibliographic coupling of some vertices in a graph. * \return the bibliographic coupling values in a matrix * \sa igraph_bibcoupling */ PyObject *igraphmodule_Graph_bibcoupling(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { char *kwlist[] = { "vertices", NULL }; PyObject *vobj = NULL, *list; igraph_matrix_t res; igraph_vs_t vs; igraph_bool_t return_single = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &vobj)) return NULL; if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_matrix_init(&res, 1, igraph_vcount(&self->g))) { igraph_vs_destroy(&vs); igraphmodule_handle_igraph_error(); return NULL; } if (igraph_bibcoupling(&self->g, &res, vs)) { igraph_vs_destroy(&vs); igraphmodule_handle_igraph_error(); return NULL; } /* TODO: Return a single list instead of a matrix if only one vertex was given */ list = igraphmodule_matrix_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); igraph_matrix_destroy(&res); igraph_vs_destroy(&vs); return list; } /** \ingroup python_interface_graph * \brief Calculates the biconnected components of a graph. * \return the list of spanning trees of biconnected components in a PyObject * \sa igraph_biconnected_components */ PyObject *igraphmodule_Graph_biconnected_components(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { igraph_vector_ptr_t components; igraph_vector_t points; igraph_bool_t return_articulation_points; igraph_integer_t no; PyObject *result, *aps=Py_False; static char* kwlist[] = {"return_articulation_points", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &aps)) return NULL; return_articulation_points = PyObject_IsTrue(aps); if (igraph_vector_ptr_init(&components, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (return_articulation_points) { if (igraph_vector_init(&points, 0)) { igraphmodule_handle_igraph_error(); igraph_vector_ptr_destroy(&components); return NULL; } } if (igraph_biconnected_components(&self->g, &no, &components, 0, 0, return_articulation_points ? &points : 0)) { igraphmodule_handle_igraph_error(); igraph_vector_ptr_destroy(&components); if (return_articulation_points) igraph_vector_destroy(&points); return NULL; } result = igraphmodule_vector_ptr_t_to_PyList(&components, IGRAPHMODULE_TYPE_INT); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&components, igraph_vector_destroy); igraph_vector_ptr_destroy_all(&components); if (return_articulation_points) { PyObject *result2; igraph_vector_sort(&points); result2 = igraphmodule_vector_t_to_PyList(&points, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&points); return Py_BuildValue("NN", result, result2); /* references stolen */ } return result; } /** \ingroup python_interface_graph * \brief Returns the one-mode projections of a bipartite graph * \return the two projections as new igraph objects * \sa igraph_bipartite_projection */ PyObject *igraphmodule_Graph_bipartite_projection(igraphmodule_GraphObject * self, PyObject* args, PyObject* kwds) { PyObject *types_o = Py_None, *multiplicity_o = Py_True, *mul1 = 0, *mul2 = 0; igraphmodule_GraphObject *result1 = 0, *result2 = 0; igraph_vector_bool_t* types = 0; igraph_vector_t multiplicities[2]; igraph_t g1, g2; igraph_t *p_g1 = &g1, *p_g2 = &g2; long int probe1 = -1; long int which = -1; static char* kwlist[] = {"types", "multiplicity", "probe1", "which", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oll", kwlist, &types_o, &multiplicity_o, &probe1, &which)) return NULL; if (igraphmodule_attrib_to_vector_bool_t(types_o, self, &types, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (which == 0) { p_g2 = 0; } else if (which == 1) { p_g1 = 0; } if (PyObject_IsTrue(multiplicity_o)) { if (igraph_vector_init(&multiplicities[0], 0)) { if (types) { igraph_vector_bool_destroy(types); free(types); } igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&multiplicities[1], 0)) { igraph_vector_destroy(&multiplicities[0]); if (types) { igraph_vector_bool_destroy(types); free(types); } igraphmodule_handle_igraph_error(); return NULL; } if (igraph_bipartite_projection(&self->g, types, p_g1, p_g2, p_g1 ? &multiplicities[0] : 0, p_g2 ? &multiplicities[1] : 0, (igraph_integer_t) probe1)) { igraph_vector_destroy(&multiplicities[0]); igraph_vector_destroy(&multiplicities[1]); if (types) { igraph_vector_bool_destroy(types); free(types); } igraphmodule_handle_igraph_error(); return NULL; } } else { if (igraph_bipartite_projection(&self->g, types, p_g1, p_g2, 0, 0, (igraph_integer_t) probe1)) { if (types) { igraph_vector_bool_destroy(types); free(types); } igraphmodule_handle_igraph_error(); return NULL; } } if (types) { igraph_vector_bool_destroy(types); free(types); } if (p_g1) { CREATE_GRAPH(result1, g1); } if (p_g2) { CREATE_GRAPH(result2, g2); } if (PyObject_IsTrue(multiplicity_o)) { if (p_g1) { mul1 = igraphmodule_vector_t_to_PyList(&multiplicities[0], IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&multiplicities[0]); if (mul1 == NULL) { igraph_vector_destroy(&multiplicities[1]); return NULL; } } else { igraph_vector_destroy(&multiplicities[0]); } if (p_g2) { mul2 = igraphmodule_vector_t_to_PyList(&multiplicities[1], IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&multiplicities[1]); if (mul2 == NULL) return NULL; } else { igraph_vector_destroy(&multiplicities[1]); } if (p_g1 && p_g2) { return Py_BuildValue("NNNN", result1, result2, mul1, mul2); } else if (p_g1) { return Py_BuildValue("NN", result1, mul1); } else { return Py_BuildValue("NN", result2, mul2); } } else { if (p_g1 && p_g2) { return Py_BuildValue("NN", result1, result2); } else if (p_g1) { return (PyObject*)result1; } else { return (PyObject*)result2; } } } /** \ingroup python_interface_graph * \brief Returns the sizes of the two one-mode projections of a bipartite graph * \return the two one-mode projections as new igraph objects * \sa igraph_bipartite_projection_size */ PyObject *igraphmodule_Graph_bipartite_projection_size(igraphmodule_GraphObject * self, PyObject* args, PyObject* kwds) { PyObject *types_o = Py_None; igraph_vector_bool_t* types = 0; igraph_integer_t vcount1, vcount2, ecount1, ecount2; static char* kwlist[] = {"types", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &types_o)) return NULL; if (igraphmodule_attrib_to_vector_bool_t(types_o, self, &types, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraph_bipartite_projection_size(&self->g, types, &vcount1, &ecount1, &vcount2, &ecount2)) { if (types) { igraph_vector_bool_destroy(types); free(types); } igraphmodule_handle_igraph_error(); return NULL; } if (types) { igraph_vector_bool_destroy(types); free(types); } return Py_BuildValue("llll", (long)vcount1, (long)ecount1, (long)vcount2, (long)ecount2); } /** \ingroup python_interface_graph * \brief Calculates the closeness centrality of some vertices in a graph. * \return the closeness centralities as a list (or a single float) * \sa igraph_betweenness */ PyObject *igraphmodule_Graph_closeness(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "vertices", "mode", "cutoff", "weights", "normalized", NULL }; PyObject *vobj = Py_None, *list = NULL, *cutoff = Py_None, *mode_o = Py_None, *weights_o = Py_None, *normalized_o = Py_True; igraph_vector_t res, *weights = 0; igraph_neimode_t mode = IGRAPH_ALL; int return_single = 0; igraph_vs_t vs; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOO", kwlist, &vobj, &mode_o, &cutoff, &weights_o, &normalized_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&res, 0)) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_vs_destroy(&vs); igraph_vector_destroy(&res); return NULL; } if (cutoff == Py_None) { if (igraph_closeness(&self->g, &res, vs, mode, weights, PyObject_IsTrue(normalized_o))) { igraph_vs_destroy(&vs); igraph_vector_destroy(&res); if (weights) { igraph_vector_destroy(weights); free(weights); } igraphmodule_handle_igraph_error(); return NULL; } } else if (PyNumber_Check(cutoff)) { PyObject *cutoff_num = PyNumber_Int(cutoff); if (cutoff_num == NULL) { igraph_vs_destroy(&vs); igraph_vector_destroy(&res); return NULL; } if (igraph_closeness_estimate(&self->g, &res, vs, mode, (igraph_integer_t)PyInt_AsLong(cutoff_num), weights, PyObject_IsTrue(normalized_o))) { igraph_vs_destroy(&vs); igraph_vector_destroy(&res); if (weights) { igraph_vector_destroy(weights); free(weights); } igraphmodule_handle_igraph_error(); Py_DECREF(cutoff_num); return NULL; } Py_DECREF(cutoff_num); } if (weights) { igraph_vector_destroy(weights); free(weights); } if (!return_single) list = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); else list = PyFloat_FromDouble(VECTOR(res)[0]); igraph_vector_destroy(&res); igraph_vs_destroy(&vs); return list; } /** \ingroup python_interface_graph * \brief Calculates the (weakly or strongly) connected components in a graph. * \return a list containing the cluster ID for every vertex in the graph * \sa igraph_clusters */ PyObject *igraphmodule_Graph_clusters(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "mode", NULL }; igraph_connectedness_t mode = IGRAPH_STRONG; igraph_vector_t res1, res2; igraph_integer_t no; PyObject *list, *mode_o = Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &mode_o)) return NULL; if (igraphmodule_PyObject_to_connectedness_t(mode_o, &mode)) return NULL; igraph_vector_init(&res1, igraph_vcount(&self->g)); igraph_vector_init(&res2, 10); if (igraph_clusters(&self->g, &res1, &res2, &no, mode)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&res1); igraph_vector_destroy(&res2); return NULL; } list = igraphmodule_vector_t_to_PyList(&res1, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&res1); igraph_vector_destroy(&res2); return list; } /** \ingroup python_interface_graph * \brief Calculates Burt's constraint scores for a given graph * \sa igraph_constraint */ PyObject *igraphmodule_Graph_constraint(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "vertices", "weights", NULL }; PyObject *vids_obj = Py_None, *weight_obj = Py_None, *list; igraph_vector_t result, weights; igraph_vs_t vids; igraph_bool_t return_single = 0; if (!PyArg_ParseTupleAndKeywords (args, kwds, "|OO", kwlist, &vids_obj, &weight_obj)) return NULL; if (igraph_vector_init(&result, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_PyObject_to_attribute_values(weight_obj, &weights, self, ATTRHASH_IDX_EDGE, 1.0)) { igraph_vector_destroy(&result); return NULL; } if (igraphmodule_PyObject_to_vs_t(vids_obj, &vids, &self->g, &return_single, 0)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); igraph_vector_destroy(&weights); return NULL; } if (igraph_constraint(&self->g, &result, vids, &weights)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vids); igraph_vector_destroy(&result); igraph_vector_destroy(&weights); return NULL; } if (!return_single) list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_FLOAT); else list = PyFloat_FromDouble((double)VECTOR(result)[0]); igraph_vs_destroy(&vids); igraph_vector_destroy(&result); igraph_vector_destroy(&weights); return list; } /** \ingroup python_interface_graph * \brief Calculates the cocitation scores of some vertices in a graph. * \return the cocitation scores in a matrix * \sa igraph_cocitation */ PyObject *igraphmodule_Graph_cocitation(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { char *kwlist[] = { "vertices", NULL }; PyObject *vobj = NULL, *list = NULL; igraph_matrix_t res; int return_single = 0; igraph_vs_t vs; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &vobj)) return NULL; if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_matrix_init(&res, 1, igraph_vcount(&self->g))) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } if (igraph_cocitation(&self->g, &res, vs)) { igraph_matrix_destroy(&res); igraph_vs_destroy(&vs); igraphmodule_handle_igraph_error(); return NULL; } /* TODO: Return a single list instead of a matrix if only one vertex was given */ list = igraphmodule_matrix_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); igraph_matrix_destroy(&res); igraph_vs_destroy(&vs); return list; } /** \ingroup python_interface_graph * \brief Replaces multiple vertices with a single one. * \return None. * \sa igraph_contract_vertices */ PyObject *igraphmodule_Graph_contract_vertices(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char* kwlist[] = {"mapping", "combine_attrs", NULL }; PyObject *mapping_o, *combination_o = Py_None; igraph_vector_t mapping; igraph_attribute_combination_t combination; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &mapping_o, &combination_o)) return NULL; if (igraphmodule_PyObject_to_attribute_combination_t( combination_o, &combination)) return NULL; if (igraphmodule_PyObject_to_vector_t(mapping_o, &mapping, 1)) { igraph_attribute_combination_destroy(&combination); return NULL; } if (igraph_contract_vertices(&self->g, &mapping, &combination)) { igraph_attribute_combination_destroy(&combination); igraph_vector_destroy(&mapping); return NULL; } igraph_attribute_combination_destroy(&combination); igraph_vector_destroy(&mapping); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Decomposes a graph into components. * \return a list of graph objects, each containing a copy of a component in the original graph. * \sa igraph_components */ PyObject *igraphmodule_Graph_decompose(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { char *kwlist[] = { "mode", "maxcompno", "minelements", NULL }; igraph_connectedness_t mode = IGRAPH_STRONG; PyObject *list, *mode_o = Py_None; igraphmodule_GraphObject *o; long maxcompno = -1, minelements = -1, n, i; igraph_vector_ptr_t components; igraph_t *g; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oll", kwlist, &mode, &maxcompno, &minelements)) return NULL; if (igraphmodule_PyObject_to_connectedness_t(mode_o, &mode)) return NULL; igraph_vector_ptr_init(&components, 3); if (igraph_decompose(&self->g, &components, mode, maxcompno, minelements)) { igraph_vector_ptr_destroy(&components); igraphmodule_handle_igraph_error(); return NULL; } /* We have to create a Python igraph object for every graph returned */ n = igraph_vector_ptr_size(&components); list = PyList_New(n); for (i = 0; i < n; i++) { g = (igraph_t *) VECTOR(components)[i]; CREATE_GRAPH(o, *g); PyList_SET_ITEM(list, i, (PyObject *) o); /* reference has been transferred by PyList_SET_ITEM, no need to DECREF * * we mustn't call igraph_destroy here, because it would free the vertices * and the edges as well, but we need them in o->g. So just call free */ free(g); } igraph_vector_ptr_destroy(&components); return list; } /** \ingroup python_interface_graph * \brief Calculates the eccentricities of some vertices in a graph. * \return the eccentricities as a list (or a single float) * \sa igraph_eccentricity */ PyObject *igraphmodule_Graph_eccentricity(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds) { static char *kwlist[] = { "vertices", "mode", NULL }; PyObject *vobj = Py_None, *list = NULL, *mode_o = Py_None; igraph_vector_t res; igraph_neimode_t mode = IGRAPH_OUT; int return_single = 0; igraph_vs_t vs; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &vobj, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&res, 0)) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } if (igraph_eccentricity(&self->g, &res, vs, mode)) { igraph_vs_destroy(&vs); igraph_vector_destroy(&res); igraphmodule_handle_igraph_error(); return NULL; } if (!return_single) list = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); else list = PyFloat_FromDouble(VECTOR(res)[0]); igraph_vector_destroy(&res); igraph_vs_destroy(&vs); return list; } PyObject* igraphmodule_Graph_eigen_adjacency(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "algorithm", "which", "arpack_options", NULL }; PyObject *algorithm_o = Py_None, *which_o = Py_None; PyObject *arpack_options_o = igraphmodule_arpack_options_default; igraph_eigen_algorithm_t algorithm; igraph_eigen_which_t which; igraphmodule_ARPACKOptionsObject *arpack_options; igraph_vector_t values; igraph_matrix_t vectors; PyObject *values_o, *vectors_o; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO!", kwlist, &algorithm_o, &which_o, &igraphmodule_ARPACKOptionsType, &arpack_options)) { return NULL; } if (igraphmodule_PyObject_to_eigen_algorithm_t(algorithm_o, &algorithm)) { return NULL; } if (igraphmodule_PyObject_to_eigen_which_t(which_o, &which)) { return NULL; } if (igraph_vector_init(&values, 0)) { return NULL; } if (igraph_matrix_init(&vectors, 0, 0)) { igraph_vector_destroy(&values); return igraphmodule_handle_igraph_error(); } arpack_options = (igraphmodule_ARPACKOptionsObject*)arpack_options_o; if (igraph_eigen_adjacency(&self->g, algorithm, &which, igraphmodule_ARPACKOptions_get(arpack_options), /*storage=*/ 0, &values, &vectors, /*cmplxvalues=*/ 0, /*cmplxvectors=*/ 0)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&values); igraph_matrix_destroy(&vectors); return NULL; } values_o = igraphmodule_vector_t_to_PyList(&values, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&values); vectors_o = igraphmodule_matrix_t_to_PyList(&vectors, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&vectors); return Py_BuildValue("NN", values_o, vectors_o); } /** \ingroup python_interface_graph * \brief Calculates the edge betweennesses in the graph * \return a list containing the edge betweenness for every edge * \sa igraph_edge_betweenness */ PyObject *igraphmodule_Graph_edge_betweenness(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "directed", "cutoff", "weights", NULL }; igraph_vector_t res, *weights = 0; PyObject *list, *directed = Py_True, *cutoff = Py_None; PyObject *weights_o = Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &directed, &cutoff, &weights_o)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; igraph_vector_init(&res, igraph_ecount(&self->g)); if (cutoff == Py_None) { if (igraph_edge_betweenness(&self->g, &res, PyObject_IsTrue(directed), weights)) { igraphmodule_handle_igraph_error(); if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vector_destroy(&res); return NULL; } } else if (PyNumber_Check(cutoff)) { PyObject *cutoff_num = PyNumber_Int(cutoff); if (!cutoff_num) { if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vector_destroy(&res); return NULL; } if (igraph_edge_betweenness_estimate(&self->g, &res, PyObject_IsTrue(directed), (igraph_integer_t)PyInt_AsLong(cutoff_num), weights)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&res); if (weights) { igraph_vector_destroy(weights); free(weights); } Py_DECREF(cutoff_num); return NULL; } Py_DECREF(cutoff_num); } else { PyErr_SetString(PyExc_TypeError, "cutoff value must be None or integer"); igraph_vector_destroy(&res); return NULL; } if (weights) { igraph_vector_destroy(weights); free(weights); } list = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&res); return list; } /** \ingroup python_interface_graph * \brief Calculates the edge connectivity of the graph * \return the edge connectivity * \sa igraph_edge_connectivity, igraph_st_edge_connectivity */ PyObject *igraphmodule_Graph_edge_connectivity(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "source", "target", "checks", NULL }; PyObject *checks = Py_True; long int source = -1, target = -1, result; igraph_integer_t res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|llO", kwlist, &source, &target, &checks)) return NULL; if (source < 0 && target < 0) { if (igraph_edge_connectivity(&self->g, &res, PyObject_IsTrue(checks))) { igraphmodule_handle_igraph_error(); return NULL; } } else if (source >= 0 && target >= 0) { if (igraph_st_edge_connectivity(&self->g, &res, (igraph_integer_t) source, (igraph_integer_t) target)) { igraphmodule_handle_igraph_error(); return NULL; } } else { PyErr_SetString(PyExc_ValueError, "if source or target is given, the other one must also be specified"); return NULL; } result = res; return Py_BuildValue("l", result); } /** \ingroup python_interface_graph * \brief Calculates the eigenvector centralities of the vertices in the graph * \sa igraph_eigenvector_centrality */ PyObject *igraphmodule_Graph_eigenvector_centrality( igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "directed", "scale", "weights", "arpack_options", "return_eigenvalue", NULL }; PyObject *directed_o = Py_True; PyObject *scale_o = Py_True; PyObject *weights_o = Py_None; PyObject *arpack_options_o = igraphmodule_arpack_options_default; igraphmodule_ARPACKOptionsObject *arpack_options; PyObject *return_eigenvalue = Py_False; PyObject *res_o; igraph_real_t value; igraph_vector_t *weights=0, res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO!O", kwlist, &directed_o, &scale_o, &weights_o, &igraphmodule_ARPACKOptionsType, &arpack_options, &return_eigenvalue)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; if (igraph_vector_init(&res, 0)) { if (weights) { igraph_vector_destroy(weights); free(weights); } return igraphmodule_handle_igraph_error(); } arpack_options = (igraphmodule_ARPACKOptionsObject*)arpack_options_o; if (igraph_eigenvector_centrality(&self->g, &res, &value, PyObject_IsTrue(directed_o), PyObject_IsTrue(scale_o), weights, igraphmodule_ARPACKOptions_get(arpack_options))) { igraphmodule_handle_igraph_error(); if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vector_destroy(&res); return NULL; } if (weights) { igraph_vector_destroy(weights); free(weights); } res_o = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&res); if (res_o == NULL) return igraphmodule_handle_igraph_error(); if (PyObject_IsTrue(return_eigenvalue)) { PyObject *ev_o = PyFloat_FromDouble((double)value); if (ev_o == NULL) { Py_DECREF(res_o); return igraphmodule_handle_igraph_error(); } return Py_BuildValue("NN", res_o, ev_o); } return res_o; } /** \ingroup python_interface_graph * \brief Calculates a feedback arc set for a graph * \return a list containing the indices in the chosen feedback arc set * \sa igraph_feedback_arc_set */ PyObject *igraphmodule_Graph_feedback_arc_set( igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "weights", "method", NULL }; igraph_vector_t* weights = 0; igraph_vector_t result; igraph_fas_algorithm_t algo = IGRAPH_FAS_APPROX_EADES; PyObject *weights_o = Py_None, *result_o = NULL, *algo_o = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &weights_o, &algo_o)) return NULL; if (igraphmodule_PyObject_to_fas_algorithm_t(algo_o, &algo)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; if (igraph_vector_init(&result, 0)) { if (weights) { igraph_vector_destroy(weights); free(weights); } } if (igraph_feedback_arc_set(&self->g, &result, weights, algo)) { if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vector_destroy(&result); return NULL; } if (weights) { igraph_vector_destroy(weights); free(weights); } result_o = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&result); return result_o; } /** \ingroup python_interface_graph * \brief Calculates the shortest paths from/to a given node in the graph * \return a list containing shortest paths from/to the given node * \sa igraph_get_shortest_paths */ PyObject *igraphmodule_Graph_get_shortest_paths(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "v", "to", "weights", "mode", "output", NULL }; igraph_vector_t *res, *weights=0; igraph_neimode_t mode = IGRAPH_OUT; long int i, j; igraph_integer_t from, no_of_target_nodes; igraph_vs_t to; PyObject *list, *item, *mode_o=Py_None, *weights_o=Py_None, *output_o=Py_None, *from_o = Py_None, *to_o=Py_None; igraph_vector_ptr_t *ptrvec=0; igraph_bool_t use_edges = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOO!", kwlist, &from_o, &to_o, &weights_o, &mode_o, &PyString_Type, &output_o)) return NULL; if (output_o == 0 || output_o == Py_None || PyString_IsEqualToASCIIString(output_o, "vpath")) { use_edges = 0; } else if (PyString_IsEqualToASCIIString(output_o, "epath")) { use_edges = 1; } else { PyErr_SetString(PyExc_ValueError, "output argument must be \"vpath\" or \"epath\""); return NULL; } if (igraphmodule_PyObject_to_vid(from_o, &from, &self->g)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; if (igraphmodule_PyObject_to_vs_t(to_o, &to, &self->g, 0, 0)) { if (weights) { igraph_vector_destroy(weights); free(weights); } return NULL; } if (igraph_vs_size(&self->g, &to, &no_of_target_nodes)) { if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vs_destroy(&to); igraphmodule_handle_igraph_error(); return NULL; } ptrvec = (igraph_vector_ptr_t *) calloc(1, sizeof(igraph_vector_ptr_t)); if (!ptrvec) { PyErr_SetString(PyExc_MemoryError, ""); if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vs_destroy(&to); return NULL; } if (igraph_vector_ptr_init(ptrvec, no_of_target_nodes)) { PyErr_SetString(PyExc_MemoryError, ""); free(ptrvec); if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vs_destroy(&to); return NULL; } res = (igraph_vector_t *) calloc(no_of_target_nodes, sizeof(igraph_vector_t)); if (!res) { PyErr_SetString(PyExc_MemoryError, ""); igraph_vector_ptr_destroy(ptrvec); free(ptrvec); if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vs_destroy(&to); return NULL; } for (i = 0; i < no_of_target_nodes; i++) { VECTOR(*ptrvec)[i] = &res[i]; igraph_vector_init(&res[i], 0); } if (igraph_get_shortest_paths_dijkstra(&self->g, use_edges ? 0 : ptrvec, use_edges ? ptrvec : 0, from, to, weights, mode, 0, 0)) { igraphmodule_handle_igraph_error(); for (j = 0; j < no_of_target_nodes; j++) igraph_vector_destroy(&res[j]); free(res); igraph_vector_ptr_destroy(ptrvec); free(ptrvec); if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vs_destroy(&to); return NULL; } igraph_vector_ptr_destroy(ptrvec); free(ptrvec); if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vs_destroy(&to); list = PyList_New(no_of_target_nodes); if (!list) { for (j = 0; j < no_of_target_nodes; j++) igraph_vector_destroy(&res[j]); free(res); return NULL; } for (i = 0; i < no_of_target_nodes; i++) { item = igraphmodule_vector_t_to_PyList(&res[i], IGRAPHMODULE_TYPE_INT); if (!item || PyList_SetItem(list, i, item)) { if (item) { Py_DECREF(item); } Py_DECREF(list); for (j = 0; j < no_of_target_nodes; j++) igraph_vector_destroy(&res[j]); free(res); return NULL; } } for (j = 0; j < no_of_target_nodes; j++) igraph_vector_destroy(&res[j]); free(res); return list; } /** \ingroup python_interface_graph * \brief Calculates all of the shortest paths from/to a given node in the graph * \return a list containing shortest paths from/to the given node * \sa igraph_get_shortest_paths */ PyObject *igraphmodule_Graph_get_all_shortest_paths(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "v", "to", "weights", "mode", NULL }; igraph_vector_ptr_t res; igraph_vector_t *weights = 0; igraph_neimode_t mode = IGRAPH_OUT; long int i, j; igraph_integer_t from; igraph_vs_t to; PyObject *list, *item, *from_o, *mode_o=Py_None, *to_o=Py_None, *weights_o=Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", kwlist, &from_o, &to_o, &weights_o, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_vid(from_o, &from, &self->g)) return NULL; if (igraphmodule_PyObject_to_vs_t(to_o, &to, &self->g, 0, 0)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_vs_destroy(&to); return NULL; } if (igraph_vector_ptr_init(&res, 1)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&to); if (weights) { igraph_vector_destroy(weights); free(weights); } return NULL; } if (igraph_get_all_shortest_paths_dijkstra(&self->g, &res, NULL, from, to, weights, mode)) { igraphmodule_handle_igraph_error(); igraph_vector_ptr_destroy(&res); igraph_vs_destroy(&to); if (weights) { igraph_vector_destroy(weights); free(weights); } return NULL; } igraph_vs_destroy(&to); if (weights) { igraph_vector_destroy(weights); free(weights); } IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&res, igraph_vector_destroy); j = igraph_vector_ptr_size(&res); list = PyList_New(j); if (!list) { igraph_vector_ptr_destroy_all(&res); return NULL; } for (i = 0; i < j; i++) { item = igraphmodule_vector_t_to_PyList((igraph_vector_t *) igraph_vector_ptr_e(&res, i), IGRAPHMODULE_TYPE_INT); if (!item) { Py_DECREF(list); igraph_vector_ptr_destroy_all(&res); return NULL; } if (PyList_SetItem(list, i, item)) { Py_DECREF(list); Py_DECREF(item); igraph_vector_ptr_destroy_all(&res); return NULL; } } igraph_vector_ptr_destroy_all(&res); return list; } /** \ingroup python_interface_graph * \brief Calculates all the simple paths from a single source to other nodes * in the graph. * * \return a list containing all simple paths from the given node to the given * nodes * \sa igraph_get_all_simple_paths */ /* PyObject *igraphmodule_Graph_get_all_simple_paths(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "v", "to", "mode", NULL }; igraph_vector_int_t res; igraph_neimode_t mode = IGRAPH_OUT; igraph_integer_t from; igraph_vs_t to; PyObject *list, *from_o, *mode_o=Py_None, *to_o=Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist, &from_o, &to_o, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_vid(from_o, &from, &self->g)) return NULL; if (igraphmodule_PyObject_to_vs_t(to_o, &to, &self->g, 0, 0)) return NULL; if (igraph_vector_int_init(&res, 0)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&to); return NULL; } if (igraph_get_all_simple_paths(&self->g, &res, from, to, mode)) { igraphmodule_handle_igraph_error(); igraph_vector_int_destroy(&res); igraph_vs_destroy(&to); return NULL; } igraph_vs_destroy(&to); list = igraphmodule_vector_int_t_to_PyList(&res); return list; } */ /** \ingroup python_interface_graph * \brief Calculates Kleinberg's hub scores of the vertices in the graph * \sa igraph_hub_score */ PyObject *igraphmodule_Graph_hub_score( igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "weights", "scale", "arpack_options", "return_eigenvalue", NULL }; PyObject *scale_o = Py_True, *weights_o = Py_None; PyObject *arpack_options_o = igraphmodule_arpack_options_default; igraphmodule_ARPACKOptionsObject *arpack_options; PyObject *return_eigenvalue = Py_False; PyObject *res_o; igraph_real_t value; igraph_vector_t res, *weights = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO!O", kwlist, &weights_o, &scale_o, &igraphmodule_ARPACKOptionsType, &arpack_options, &return_eigenvalue)) return NULL; if (igraph_vector_init(&res, 0)) return igraphmodule_handle_igraph_error(); if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; arpack_options = (igraphmodule_ARPACKOptionsObject*)arpack_options_o; if (igraph_hub_score(&self->g, &res, &value, PyObject_IsTrue(scale_o), weights, igraphmodule_ARPACKOptions_get(arpack_options))) { igraphmodule_handle_igraph_error(); if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vector_destroy(&res); return NULL; } if (weights) { igraph_vector_destroy(weights); free(weights); } res_o = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&res); if (res_o == NULL) return igraphmodule_handle_igraph_error(); if (PyObject_IsTrue(return_eigenvalue)) { PyObject *ev_o = PyFloat_FromDouble((double)value); if (ev_o == NULL) { Py_DECREF(res_o); return igraphmodule_handle_igraph_error(); } return Py_BuildValue("NN", res_o, ev_o); } return res_o; } /** \ingroup python_interface_graph * \brief Returns the line graph of the graph * \return the line graph as a new igraph object * \sa igraph_linegraph */ PyObject *igraphmodule_Graph_linegraph(igraphmodule_GraphObject * self) { igraph_t lg; igraphmodule_GraphObject *result; if (igraph_linegraph(&self->g, &lg)) { igraphmodule_handle_igraph_error(); return NULL; } CREATE_GRAPH(result, lg); return (PyObject *) result; } /** * \ingroup python_interface_graph * \brief Returns the k-neighborhood of some vertices in the * graph. * \sa igraph_neighborhood */ PyObject *igraphmodule_Graph_neighborhood(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "vertices", "order", "mode", NULL }; PyObject *vobj = Py_None; PyObject *mode_o = 0; PyObject *result; long int order = 1; igraph_neimode_t mode = IGRAPH_ALL; igraph_bool_t return_single = 0; igraph_vs_t vs; igraph_vector_ptr_t res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OlO", kwlist, &vobj, &order, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) { return igraphmodule_handle_igraph_error(); } if (igraph_vector_ptr_init(&res, 0)) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } if (igraph_neighborhood(&self->g, &res, vs, (igraph_integer_t) order, mode)) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } igraph_vs_destroy(&vs); if (!return_single) result = igraphmodule_vector_ptr_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); else result = igraphmodule_vector_t_to_PyList((igraph_vector_t*)VECTOR(res)[0], IGRAPHMODULE_TYPE_INT); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&res, igraph_vector_destroy); igraph_vector_ptr_destroy_all(&res); return result; } /** * \ingroup python_interface_graph * \brief Returns the size of the k-neighborhood of some vertices in the * graph. * \sa igraph_neighborhood_size */ PyObject *igraphmodule_Graph_neighborhood_size(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "vertices", "order", "mode", NULL }; PyObject *vobj = Py_None; PyObject *mode_o = 0; PyObject *result; long int order = 1; igraph_neimode_t mode = IGRAPH_ALL; igraph_bool_t return_single = 0; igraph_vs_t vs; igraph_vector_t res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OlO", kwlist, &vobj, &order, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) { return igraphmodule_handle_igraph_error(); } if (igraph_vector_init(&res, 0)) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } if (igraph_neighborhood_size(&self->g, &res, vs, (igraph_integer_t) order, mode)) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } igraph_vs_destroy(&vs); if (!return_single) result = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); else result = PyInt_FromLong((long)VECTOR(res)[0]); igraph_vector_destroy(&res); return result; } /** \ingroup python_interface_graph * \brief Calculates the Google personalized PageRank value of some vertices in the graph. * \return the personalized PageRank values * \sa igraph_personalized_pagerank */ PyObject *igraphmodule_Graph_personalized_pagerank(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "vertices", "directed", "damping", "reset", "reset_vertices", "weights", "arpack_options", "implementation", "niter", "eps", NULL }; PyObject *directed = Py_True; PyObject *vobj = Py_None, *wobj = Py_None, *robj = Py_None, *rvsobj = Py_None; PyObject *list; PyObject *arpack_options_o = igraphmodule_arpack_options_default; igraphmodule_ARPACKOptionsObject *arpack_options; double damping = 0.85; igraph_vector_t res; igraph_vector_t *reset = 0; igraph_vector_t weights; igraph_bool_t return_single = 0; igraph_vs_t vs, reset_vs; igraph_pagerank_algo_t algo=IGRAPH_PAGERANK_ALGO_PRPACK; PyObject *algo_o = Py_None; long niter=1000; float eps=0.001f; igraph_pagerank_power_options_t popts; void *opts; int retval; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOdOOOO!Olf", kwlist, &vobj, &directed, &damping, &robj, &rvsobj, &wobj, &igraphmodule_ARPACKOptionsType, &arpack_options_o, &algo_o, &niter, &eps)) return NULL; if (robj != Py_None && rvsobj != Py_None) { PyErr_SetString(PyExc_ValueError, "only reset or reset_vs can be defined, not both"); return NULL; } if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) { igraphmodule_handle_igraph_error(); return NULL; } arpack_options = (igraphmodule_ARPACKOptionsObject*)arpack_options_o; if (robj != Py_None) { if (igraphmodule_attrib_to_vector_t(robj, self, &reset, ATTRIBUTE_TYPE_VERTEX)) { igraph_vs_destroy(&vs); igraphmodule_handle_igraph_error(); return NULL; } } else if (rvsobj != Py_None) { if (igraphmodule_PyObject_to_vs_t(rvsobj, &reset_vs, &self->g, 0, 0)) { igraph_vs_destroy(&vs); igraphmodule_handle_igraph_error(); return NULL; } } if (igraphmodule_PyObject_to_attribute_values(wobj, &weights, self, ATTRHASH_IDX_EDGE, 1.0)) { igraph_vs_destroy(&vs); if (rvsobj != Py_None) igraph_vs_destroy(&reset_vs); if (reset) { igraph_vector_destroy(reset); free(reset); } return NULL; } if (igraph_vector_init(&res, 0)) { igraph_vs_destroy(&vs); if (rvsobj != Py_None) igraph_vs_destroy(&reset_vs); if (reset) { igraph_vector_destroy(reset); free(reset); } igraph_vector_destroy(&weights); return igraphmodule_handle_igraph_error(); } if (igraphmodule_PyObject_to_pagerank_algo_t(algo_o, &algo)) return NULL; popts.niter = (igraph_integer_t) niter; popts.eps = eps; if (algo == IGRAPH_PAGERANK_ALGO_POWER) { opts = &popts; } else if (algo == IGRAPH_PAGERANK_ALGO_ARPACK) { opts = igraphmodule_ARPACKOptions_get(arpack_options); } else { opts = 0; } if (rvsobj != Py_None) retval = igraph_personalized_pagerank_vs(&self->g, algo, &res, 0, vs, PyObject_IsTrue(directed), damping, reset_vs, &weights, opts); else retval = igraph_personalized_pagerank(&self->g, algo, &res, 0, vs, PyObject_IsTrue(directed), damping, reset, &weights, opts); if (retval) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); if (rvsobj != Py_None) igraph_vs_destroy(&reset_vs); if (reset) { igraph_vector_destroy(reset); free(reset); } igraph_vector_destroy(&weights); igraph_vector_destroy(&res); return NULL; } if (!return_single) list = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); else list = PyFloat_FromDouble(VECTOR(res)[0]); igraph_vector_destroy(&res); igraph_vs_destroy(&vs); if (rvsobj != Py_None) igraph_vs_destroy(&reset_vs); igraph_vector_destroy(&weights); if (reset) { igraph_vector_destroy(reset); free(reset); } return list; } /** \ingroup python_interface_graph * \brief Calculates the path length histogram of the graph * \sa igraph_path_length_hist */ PyObject *igraphmodule_Graph_path_length_hist(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "directed", NULL }; PyObject *directed = Py_True, *result; igraph_real_t unconn; igraph_vector_t res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &directed)) return NULL; if (igraph_vector_init(&res, 0)) return igraphmodule_handle_igraph_error(); if (igraph_path_length_hist(&self->g, &res, &unconn, PyObject_IsTrue(directed))) { igraph_vector_destroy(&res); return igraphmodule_handle_igraph_error(); } result=igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&res); return Py_BuildValue("Nd", result, (double)unconn); } /** \ingroup python_interface_graph * \brief Permutes the vertices of the graph * \return the new graph as a new igraph object * \sa igraph_permute_vertices */ PyObject *igraphmodule_Graph_permute_vertices(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "permutation", NULL }; igraph_t pg; igraph_vector_t perm; igraphmodule_GraphObject *result; PyObject *list; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &PyList_Type, &list)) return NULL; if (igraphmodule_PyObject_to_vector_t(list, &perm, 1)) return NULL; if (igraph_permute_vertices(&self->g, &pg, &perm)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&perm); return NULL; } igraph_vector_destroy(&perm); CREATE_GRAPH(result, pg); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Rewires a graph while preserving degree distribution * \return the rewired graph * \sa igraph_rewire */ PyObject *igraphmodule_Graph_rewire(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "n", "mode", NULL }; long int n = 1000; PyObject *mode_o = Py_None; igraph_rewiring_t mode = IGRAPH_REWIRING_SIMPLE; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|lO", kwlist, &n, &mode_o)) return NULL; if (igraphmodule_PyObject_to_rewiring_t(mode_o, &mode)) return NULL; if (igraph_rewire(&self->g, (igraph_integer_t) n, mode)) { igraphmodule_handle_igraph_error(); return NULL; } Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Rewires the edges of a graph wth constant probability * \return the rewired graph * \sa igraph_rewire_edges */ PyObject *igraphmodule_Graph_rewire_edges(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "prob", "loops", "multiple", NULL }; double prob; PyObject *loops_o = Py_False, *multiple_o = Py_False; if (!PyArg_ParseTupleAndKeywords(args, kwds, "d|OO", kwlist, &prob, &loops_o, &multiple_o)) return NULL; if (igraph_rewire_edges(&self->g, prob, PyObject_IsTrue(loops_o), PyObject_IsTrue(multiple_o))) { igraphmodule_handle_igraph_error(); return NULL; } Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Calculates shortest paths in a graph. * \return the shortest path lengths for the given vertices * \sa igraph_shortest_paths, igraph_shortest_paths_dijkstra, * igraph_shortest_paths_bellman_ford, igraph_shortest_paths_johnson */ PyObject *igraphmodule_Graph_shortest_paths(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "source", "target", "weights", "mode", NULL }; PyObject *from_o = NULL, *to_o = NULL, *mode_o = NULL, *weights_o = Py_None; PyObject *list = NULL; igraph_matrix_t res; igraph_vector_t *weights=0; igraph_neimode_t mode = IGRAPH_OUT; int return_single_from = 0, return_single_to = 0, e = 0; igraph_vs_t from_vs, to_vs; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &from_o, &to_o, &weights_o, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return 0; if (igraphmodule_PyObject_to_vs_t(from_o, &from_vs, &self->g, &return_single_from, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_PyObject_to_vs_t(to_o, &to_vs, &self->g, &return_single_to, 0)) { igraph_vs_destroy(&from_vs); igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_vs_destroy(&from_vs); igraph_vs_destroy(&to_vs); return NULL; } if (igraph_matrix_init(&res, 1, igraph_vcount(&self->g))) { if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_vs_destroy(&from_vs); igraph_vs_destroy(&to_vs); return igraphmodule_handle_igraph_error(); } /* Select the most suitable algorithm */ if (weights) { if (igraph_vector_min(weights) > 0) { /* Only positive weights, use Dijkstra's algorithm */ e = igraph_shortest_paths_dijkstra(&self->g, &res, from_vs, to_vs, weights, mode); } else { /* There are negative weights. For a small number of sources, use Bellman-Ford. * Otherwise, use Johnson's algorithm */ igraph_integer_t vs_size; e = igraph_vs_size(&self->g, &from_vs, &vs_size); if (!e) { if (vs_size <= 100 || mode != IGRAPH_OUT) { e = igraph_shortest_paths_bellman_ford(&self->g, &res, from_vs, to_vs, weights, mode); } else { e = igraph_shortest_paths_johnson(&self->g, &res, from_vs, to_vs, weights); } } } } else { /* No weights, use a simple BFS */ e = igraph_shortest_paths(&self->g, &res, from_vs, to_vs, mode); } if (e) { if (weights) igraph_vector_destroy(weights); igraph_matrix_destroy(&res); igraph_vs_destroy(&from_vs); igraph_vs_destroy(&to_vs); igraphmodule_handle_igraph_error(); return NULL; } if (weights) { igraph_vector_destroy(weights); list = igraphmodule_matrix_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); } else { list = igraphmodule_matrix_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); } if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_matrix_destroy(&res); igraph_vs_destroy(&from_vs); igraph_vs_destroy(&to_vs); return list; } /** \ingroup python_interface_graph * \brief Calculates the Jaccard similarities of some vertices in a graph. * \return the similarity scores in a matrix * \sa igraph_similarity_jaccard */ PyObject *igraphmodule_Graph_similarity_jaccard(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "vertices", "pairs", "mode", "loops", NULL }; PyObject *vertices_o = Py_None, *pairs_o = Py_None; PyObject *list = NULL, *loops = Py_True, *mode_o = Py_None; igraph_neimode_t mode = IGRAPH_ALL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &vertices_o, &pairs_o, &mode_o, &loops)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (vertices_o != Py_None && pairs_o != Py_None) { PyErr_SetString(PyExc_ValueError, "at most one of `vertices` and `pairs` " "must be given"); return NULL; } if (pairs_o == Py_None) { /* Case #1: vertices, returning matrix */ igraph_matrix_t res; igraph_vs_t vs; int return_single = 0; if (igraphmodule_PyObject_to_vs_t(vertices_o, &vs, &self->g, &return_single, 0)) return NULL; if (igraph_matrix_init(&res, 0, 0)) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } if (igraph_similarity_jaccard(&self->g, &res, vs, mode, PyObject_IsTrue(loops))) { igraph_matrix_destroy(&res); igraph_vs_destroy(&vs); igraphmodule_handle_igraph_error(); return NULL; } igraph_vs_destroy(&vs); list = igraphmodule_matrix_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&res); } else { /* Case #2: vertex pairs or edges, returning list */ igraph_vector_t edges; igraph_vector_t res; if (igraphmodule_PyObject_to_edgelist(pairs_o, &edges, 0)) return NULL; if (igraph_vector_init(&res, igraph_vector_size(&edges) / 2)) { igraph_vector_destroy(&edges); igraphmodule_handle_igraph_error(); return NULL; } if (igraph_similarity_jaccard_pairs(&self->g, &res, &edges, mode, PyObject_IsTrue(loops))) { igraph_vector_destroy(&res); igraph_vector_destroy(&edges); igraphmodule_handle_igraph_error(); return NULL; } igraph_vector_destroy(&edges); list = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&res); } return list; } /** \ingroup python_interface_graph * \brief Calculates the Dice similarities of some vertices in a graph. * \return the similarity scores in a matrix * \sa igraph_similarity_dice */ PyObject *igraphmodule_Graph_similarity_dice(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "vertices", "pairs", "mode", "loops", NULL }; PyObject *vertices_o = Py_None, *pairs_o = Py_None; PyObject *list = NULL, *loops = Py_True, *mode_o = Py_None; igraph_neimode_t mode = IGRAPH_ALL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &vertices_o, &pairs_o, &mode_o, &loops)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (vertices_o != Py_None && pairs_o != Py_None) { PyErr_SetString(PyExc_ValueError, "at most one of `vertices` and `pairs` " "must be given"); return NULL; } if (pairs_o == Py_None) { /* Case #1: vertices, returning matrix */ igraph_matrix_t res; igraph_vs_t vs; int return_single = 0; if (igraphmodule_PyObject_to_vs_t(vertices_o, &vs, &self->g, &return_single, 0)) return NULL; if (igraph_matrix_init(&res, 0, 0)) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } if (igraph_similarity_dice(&self->g, &res, vs, mode, PyObject_IsTrue(loops))) { igraph_matrix_destroy(&res); igraph_vs_destroy(&vs); igraphmodule_handle_igraph_error(); return NULL; } igraph_vs_destroy(&vs); list = igraphmodule_matrix_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&res); } else { /* Case #2: vertex pairs or edges, returning list */ igraph_vector_t edges; igraph_vector_t res; if (igraphmodule_PyObject_to_edgelist(pairs_o, &edges, 0)) return NULL; if (igraph_vector_init(&res, igraph_vector_size(&edges) / 2)) { igraph_vector_destroy(&edges); igraphmodule_handle_igraph_error(); return NULL; } if (igraph_similarity_dice_pairs(&self->g, &res, &edges, mode, PyObject_IsTrue(loops))) { igraph_vector_destroy(&res); igraph_vector_destroy(&edges); igraphmodule_handle_igraph_error(); return NULL; } igraph_vector_destroy(&edges); list = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&res); } return list; } /** \ingroup python_interface_graph * \brief Calculates the inverse log-weighted similarities of some vertices in * a graph. * \return the similarity scores in a matrix * \sa igraph_similarity_inverse_log_weighted */ PyObject *igraphmodule_Graph_similarity_inverse_log_weighted( igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "vertices", "mode", NULL }; PyObject *vobj = NULL, *list = NULL, *mode_o = Py_None; igraph_matrix_t res; igraph_neimode_t mode = IGRAPH_ALL; int return_single = 0; igraph_vs_t vs; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &vobj, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) return NULL; if (igraph_matrix_init(&res, 0, 0)) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } if (igraph_similarity_inverse_log_weighted(&self->g,&res,vs,mode)) { igraph_matrix_destroy(&res); igraph_vs_destroy(&vs); igraphmodule_handle_igraph_error(); return NULL; } list = igraphmodule_matrix_t_to_PyList(&res, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&res); igraph_vs_destroy(&vs); return list; } /** \ingroup python_interface_graph * \brief Calculates a spanning tree for a graph * \return the spanning tree or a list of edges participating in the spanning tree * \sa igraph_minimum_spanning_tree_unweighted * \sa igraph_minimum_spanning_tree_unweighted * \sa igraph_minimum_spanning_tree_prim */ PyObject *igraphmodule_Graph_spanning_tree(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "weights", NULL }; igraph_vector_t* ws = 0; igraph_vector_t res; PyObject *weights_o = Py_None, *result = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &weights_o)) return NULL; if (igraph_vector_init(&res, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(weights_o, self, &ws, ATTRIBUTE_TYPE_EDGE)) { igraph_vector_destroy(&res); return NULL; } if (igraph_minimum_spanning_tree(&self->g, &res, ws)) { if (ws != 0) { igraph_vector_destroy(ws); free(ws); } igraph_vector_destroy(&res); igraphmodule_handle_igraph_error(); return NULL; } if (ws != 0) { igraph_vector_destroy(ws); free(ws); } result = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&res); return result; } /** \ingroup python_interface_graph * \brief Simplifies a graph by removing loops and/or multiple edges * \return the simplified graph. * \sa igraph_simplify */ PyObject *igraphmodule_Graph_simplify(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "multiple", "loops", "combine_edges", NULL }; PyObject *multiple = Py_True, *loops = Py_True, *comb_o = Py_None; igraph_attribute_combination_t comb; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &multiple, &loops, &comb_o)) return NULL; if (igraphmodule_PyObject_to_attribute_combination_t(comb_o, &comb)) return NULL; if (igraph_simplify(&self->g, PyObject_IsTrue(multiple), PyObject_IsTrue(loops), &comb)) { igraph_attribute_combination_destroy(&comb); igraphmodule_handle_igraph_error(); return NULL; } igraph_attribute_combination_destroy(&comb); Py_INCREF(self); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Calculates the vertex indices within the same component as a given vertex * \return the vertex indices in a list * \sa igraph_subcomponent */ PyObject *igraphmodule_Graph_subcomponent(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "v", "mode", NULL }; igraph_vector_t res; igraph_neimode_t mode = IGRAPH_ALL; igraph_integer_t from; PyObject *list = NULL, *mode_o = Py_None, *from_o = Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &from_o, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_vid(from_o, &from, &self->g)) return NULL; igraph_vector_init(&res, 0); if (igraph_subcomponent(&self->g, &res, from, mode)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&res); return NULL; } list = igraphmodule_vector_t_to_PyList(&res, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&res); return list; } /** \ingroup python_interface_graph * \brief Returns an induced subgraph of the graph based on the given vertices * \return the subgraph as a new igraph object * \sa igraph_induced_subgraph */ PyObject *igraphmodule_Graph_induced_subgraph(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "vertices", "implementation", NULL }; igraph_vs_t vs; igraph_t sg; igraphmodule_GraphObject *result; PyObject *list, *impl_o = Py_None; igraph_subgraph_implementation_t impl = IGRAPH_SUBGRAPH_AUTO; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &list, &impl_o)) return NULL; if (igraphmodule_PyObject_to_subgraph_implementation_t(impl_o, &impl)) return NULL; if (igraphmodule_PyObject_to_vs_t(list, &vs, &self->g, 0, 0)) return NULL; if (igraph_induced_subgraph(&self->g, &sg, vs, impl)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); return NULL; } igraph_vs_destroy(&vs); CREATE_GRAPH(result, sg); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Returns a subgraph of the graph based on the given edges * \return the subgraph as a new igraph object * \sa igraph_subgraph_edges */ PyObject *igraphmodule_Graph_subgraph_edges(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "edges", "delete_vertices", NULL }; igraph_es_t es; igraph_t sg; igraphmodule_GraphObject *result; PyObject *list, *delete_vertices = Py_True; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &list, &delete_vertices)) return NULL; if (igraphmodule_PyObject_to_es_t(list, &es, &self->g, 0)) return NULL; if (igraph_subgraph_edges(&self->g, &sg, es, PyObject_IsTrue(delete_vertices))) { igraphmodule_handle_igraph_error(); igraph_es_destroy(&es); return NULL; } CREATE_GRAPH(result, sg); igraph_es_destroy(&es); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Calculates the graph transitivity (a.k.a. clustering coefficient) * \return the clustering coefficient * \sa igraph_transitivity_undirected */ PyObject *igraphmodule_Graph_transitivity_undirected(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "mode", NULL }; igraph_real_t res; PyObject *r, *mode_o = Py_None; igraph_transitivity_mode_t mode = IGRAPH_TRANSITIVITY_NAN; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &mode_o)) return NULL; if (igraphmodule_PyObject_to_transitivity_mode_t(mode_o, &mode)) return NULL; if (igraph_transitivity_undirected(&self->g, &res, mode)) { igraphmodule_handle_igraph_error(); return NULL; } r = Py_BuildValue("d", (double)(res)); return r; } /** \ingroup python_interface_graph * \brief Calculates the average of vertex transitivities over the graph * \sa igraph_transitivity_avglocal_undirected */ PyObject *igraphmodule_Graph_transitivity_avglocal_undirected(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "mode", NULL }; igraph_real_t res; PyObject *r, *mode_o = Py_None; igraph_transitivity_mode_t mode = IGRAPH_TRANSITIVITY_NAN; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &mode_o)) return NULL; if (igraphmodule_PyObject_to_transitivity_mode_t(mode_o, &mode)) return NULL; if (igraph_transitivity_avglocal_undirected(&self->g, &res, mode)) { igraphmodule_handle_igraph_error(); return NULL; } r = Py_BuildValue("d", (double)(res)); return r; } /** \ingroup python_interface_graph * \brief Calculates the local transitivity of given vertices * \return the transitivities in a list * \sa igraph_transitivity_local_undirected */ PyObject *igraphmodule_Graph_transitivity_local_undirected(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "vertices", "mode", "weights", NULL }; PyObject *vobj = NULL, *mode_o = Py_None, *list = NULL; PyObject *weights_o = Py_None; igraph_vector_t result; igraph_vector_t *weights = 0; igraph_bool_t return_single = 0; igraph_vs_t vs; igraph_transitivity_mode_t mode = IGRAPH_TRANSITIVITY_NAN; int retval; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &vobj, &mode_o, &weights_o)) return NULL; if (igraphmodule_PyObject_to_transitivity_mode_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_vs_t(vobj, &vs, &self->g, &return_single, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&result, 0)) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_vs_destroy(&vs); igraph_vector_destroy(&result); return NULL; } if (weights == 0) { retval = igraph_transitivity_local_undirected(&self->g, &result, vs, mode); } else { retval = igraph_transitivity_barrat(&self->g, &result, vs, weights, mode); } igraph_vs_destroy(&vs); if (weights) { igraph_vector_destroy(weights); free(weights); } if (retval) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); return NULL; } if (!return_single) list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_FLOAT); else list = PyFloat_FromDouble(VECTOR(result)[0]); igraph_vector_destroy(&result); return list; } /** \ingroup python_interface_graph * \brief Calculates a possible topological sorting * \return a possible topological sorting as a list * \sa igraph_topological_sorting */ PyObject *igraphmodule_Graph_topological_sorting(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "mode", "warnings", NULL }; PyObject *list, *mode_o=Py_None; PyObject *warnings_o=Py_True; igraph_neimode_t mode = IGRAPH_OUT; igraph_vector_t result; igraph_warning_handler_t* old_handler = 0; int retval; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &mode_o, &warnings_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraph_vector_init(&result, 0)) return igraphmodule_handle_igraph_error(); if (!PyObject_IsTrue(warnings_o)) { /* Turn off the warnings temporarily */ old_handler = igraph_set_warning_handler(igraph_warning_handler_ignore); } retval = igraph_topological_sorting(&self->g, &result, mode); if (!PyObject_IsTrue(warnings_o)) { /* Restore the warning handler */ igraph_set_warning_handler(old_handler); } if (retval) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); return NULL; } list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&result); return list; } /** \ingroup python_interface_graph * \brief Calculates the vertex connectivity of the graph * \return the vertex connectivity * \sa igraph_vertex_connectivity, igraph_st_vertex_connectivity */ PyObject *igraphmodule_Graph_vertex_connectivity(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "source", "target", "checks", "neighbors", NULL }; PyObject *checks = Py_True, *neis = Py_None; long int source = -1, target = -1, result; igraph_integer_t res; igraph_vconn_nei_t neighbors = IGRAPH_VCONN_NEI_ERROR; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|llOO", kwlist, &source, &target, &checks, &neis)) return NULL; if (source < 0 && target < 0) { if (igraph_vertex_connectivity(&self->g, &res, PyObject_IsTrue(checks))) { igraphmodule_handle_igraph_error(); return NULL; } } else if (source >= 0 && target >= 0) { if (igraphmodule_PyObject_to_vconn_nei_t(neis, &neighbors)) return NULL; if (igraph_st_vertex_connectivity(&self->g, &res, (igraph_integer_t) source, (igraph_integer_t) target, neighbors)) { igraphmodule_handle_igraph_error(); return NULL; } } else { PyErr_SetString(PyExc_ValueError, "if source or target is given, the other one must also be specified"); return NULL; } if (!IGRAPH_FINITE(res)) return Py_BuildValue("d", (double)res); result = (long)res; return Py_BuildValue("l", result); } /********************************************************************** * Bipartite graphs * **********************************************************************/ /** \ingroup python_interface_graph * \brief Checks whether a graph is bipartite * \return a boolean or a (boolean, list of booleans) pair * \sa igraph_is_bipartite */ PyObject *igraphmodule_Graph_is_bipartite(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { PyObject *types_o, *return_types_o = Py_False; igraph_vector_bool_t types; igraph_bool_t return_types = 0, result; static char *kwlist[] = { "return_types", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &return_types_o)) return NULL; return_types = PyObject_IsTrue(return_types_o); if (return_types) { if (igraph_vector_bool_init(&types, igraph_vcount(&self->g))) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_is_bipartite(&self->g, &result, &types)) { igraph_vector_bool_destroy(&types); igraphmodule_handle_igraph_error(); return NULL; } if (result) { types_o = igraphmodule_vector_bool_t_to_PyList(&types); if (!types_o) { igraph_vector_bool_destroy(&types); return NULL; } igraph_vector_bool_destroy(&types); // reference to types_o will be stolen by Py_BuildValue return Py_BuildValue("ON", Py_True, types_o); } else { igraph_vector_bool_destroy(&types); return Py_BuildValue("OO", Py_False, Py_None); } } else { if (igraph_is_bipartite(&self->g, &result, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (result) Py_RETURN_TRUE; else Py_RETURN_FALSE; } } /********************************************************************** * Motifs, dyad and triad census * **********************************************************************/ /** \ingroup python_interface_graph * \brief Calculates the dyad census of the graph * \return the dyad census as a 3-tuple * \sa igraph_dyad_census */ PyObject *igraphmodule_Graph_dyad_census(igraphmodule_GraphObject *self) { igraph_integer_t mut, asym, nul; PyObject *list; if (igraph_dyad_census(&self->g, &mut, &asym, &nul)) { return igraphmodule_handle_igraph_error(); } list = Py_BuildValue("lll", (long)mut, (long)asym, (long)nul); return list; } typedef struct { PyObject* func; PyObject* graph; } igraphmodule_i_Graph_motifs_randesu_callback_data_t; igraph_bool_t igraphmodule_i_Graph_motifs_randesu_callback(const igraph_t *graph, igraph_vector_t *vids, int isoclass, void* extra) { igraphmodule_i_Graph_motifs_randesu_callback_data_t* data = (igraphmodule_i_Graph_motifs_randesu_callback_data_t*)extra; PyObject* vector; PyObject* result; igraph_bool_t retval; vector = igraphmodule_vector_t_to_PyList(vids, IGRAPHMODULE_TYPE_INT); if (vector == NULL) { /* Error in conversion, return 1 */ return 1; } result = PyObject_CallFunction(data->func, "OOi", data->graph, vector, isoclass); Py_DECREF(vector); if (result == NULL) { /* Error in callback, return 1 */ return 1; } retval = PyObject_IsTrue(result); Py_DECREF(result); return retval; } /** \ingroup python_interface_graph * \brief Counts the motifs of the graph sorted by isomorphism classes * \return the number of motifs found for each isomorphism class * \sa igraph_motifs_randesu */ PyObject *igraphmodule_Graph_motifs_randesu(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { igraph_vector_t result, cut_prob; long int size=3; PyObject* cut_prob_list=Py_None; PyObject* callback=Py_None; PyObject *list; static char* kwlist[] = {"size", "cut_prob", "callback", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|lOO", kwlist, &size, &cut_prob_list, &callback)) return NULL; if (cut_prob_list == Py_None) { if (igraph_vector_init(&cut_prob, size)) { return igraphmodule_handle_igraph_error(); } igraph_vector_fill(&cut_prob, 0); } else { if (igraphmodule_PyObject_float_to_vector_t(cut_prob_list, &cut_prob)) return NULL; } if (callback == Py_None) { if (igraph_vector_init(&result, 1)) { igraph_vector_destroy(&cut_prob); return igraphmodule_handle_igraph_error(); } if (igraph_motifs_randesu(&self->g, &result, (igraph_integer_t) size, &cut_prob)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); igraph_vector_destroy(&cut_prob); return NULL; } igraph_vector_destroy(&cut_prob); list = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&result); return list; } else if (PyCallable_Check(callback)) { igraphmodule_i_Graph_motifs_randesu_callback_data_t data; data.graph = (PyObject*)self; data.func = callback; if (igraph_motifs_randesu_callback(&self->g, (igraph_integer_t) size, &cut_prob, igraphmodule_i_Graph_motifs_randesu_callback, &data)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&cut_prob); return NULL; } igraph_vector_destroy(&cut_prob); /* Don't let exceptions from the callback function go unnoticed */ if (PyErr_Occurred()) return NULL; Py_RETURN_NONE; } else { PyErr_SetString(PyExc_TypeError, "callback must be callable or None"); return NULL; } } /** \ingroup python_interface_graph * \brief Counts the total number of motifs of the graph * \return the total number of motifs * \sa igraph_motifs_randesu */ PyObject *igraphmodule_Graph_motifs_randesu_no(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { igraph_vector_t cut_prob; igraph_integer_t result; long int size=3; PyObject* cut_prob_list=Py_None; static char* kwlist[] = {"size", "cut_prob", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|lO", kwlist, &size, &cut_prob_list)) return NULL; if (cut_prob_list == Py_None) { if (igraph_vector_init(&cut_prob, size)) { return igraphmodule_handle_igraph_error(); } igraph_vector_fill(&cut_prob, 0); } else { if (igraphmodule_PyObject_float_to_vector_t(cut_prob_list, &cut_prob)) { return NULL; } } if (igraph_motifs_randesu_no(&self->g, &result, (igraph_integer_t) size, &cut_prob)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&cut_prob); return NULL; } igraph_vector_destroy(&cut_prob); return PyInt_FromLong((long)result); } /** \ingroup python_interface_graph * \brief Estimates the total number of motifs of the graph * \return the estimated total number of motifs * \sa igraph_motifs_randesu_estimate */ PyObject *igraphmodule_Graph_motifs_randesu_estimate(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { igraph_vector_t cut_prob; igraph_integer_t result; long size=3; PyObject* cut_prob_list=Py_None; PyObject *sample=Py_None; static char* kwlist[] = {"size", "cut_prob", "sample", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|lOO", kwlist, &size, &cut_prob_list, &sample)) return NULL; if (sample == Py_None) { PyErr_SetString(PyExc_TypeError, "sample size must be given"); return NULL; } if (cut_prob_list == Py_None) { if (igraph_vector_init(&cut_prob, size)) { return igraphmodule_handle_igraph_error(); } igraph_vector_fill(&cut_prob, 0); } else { if (igraphmodule_PyObject_float_to_vector_t(cut_prob_list, &cut_prob)) { return NULL; } } if (PyInt_Check(sample)) { /* samples chosen randomly */ long int ns = PyInt_AsLong(sample); if (igraph_motifs_randesu_estimate(&self->g, &result, (igraph_integer_t) size, &cut_prob, (igraph_integer_t) ns, 0)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&cut_prob); return NULL; } } else { /* samples given in advance */ igraph_vector_t samp; if (igraphmodule_PyObject_to_vector_t(sample, &samp, 1)) { igraph_vector_destroy(&cut_prob); return NULL; } if (igraph_motifs_randesu_estimate(&self->g, &result, (igraph_integer_t) size, &cut_prob, 0, &samp)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&cut_prob); return NULL; } } igraph_vector_destroy(&cut_prob); return PyInt_FromLong((long)result); } /** \ingroup python_interface_graph * \brief Calculates the triad census of the graph * \return the triad census as a list * \sa igraph_triad_census */ PyObject *igraphmodule_Graph_triad_census(igraphmodule_GraphObject *self) { igraph_vector_t result; PyObject *list; if (igraph_vector_init(&result, 16)) { return igraphmodule_handle_igraph_error(); } if (igraph_triad_census(&self->g, &result)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); return NULL; } list = igraphmodule_vector_t_to_PyTuple(&result); igraph_vector_destroy(&result); return list; } /********************************************************************** * Graph layout algorithms * **********************************************************************/ /** \ingroup python_interface_graph * \brief Places the vertices of a graph uniformly on a circle. * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_circle */ PyObject *igraphmodule_Graph_layout_circle(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { igraph_matrix_t m; int ret; long dim = 2; PyObject *result; static char *kwlist[] = { "dim", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|l", kwlist, &dim)) return NULL; if (dim != 2 && dim != 3) { PyErr_SetString(PyExc_ValueError, "number of dimensions must be either 2 or 3"); return NULL; } if (igraph_matrix_init(&m, 1, 1)) { igraphmodule_handle_igraph_error(); return NULL; } if (dim == 2) ret = igraph_layout_circle(&self->g, &m); else ret = igraph_layout_sphere(&self->g, &m); if (ret) { igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices of a graph randomly. * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_random */ PyObject *igraphmodule_Graph_layout_random(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { igraph_matrix_t m; int ret; long dim = 2; PyObject *result; static char *kwlist[] = { "dim", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|l", kwlist, &dim)) return NULL; if (dim != 2 && dim != 3) { PyErr_SetString(PyExc_ValueError, "number of dimensions must be either 2 or 3"); return NULL; } if (igraph_matrix_init(&m, 1, 1)) { igraphmodule_handle_igraph_error(); return NULL; } if (dim == 2) ret = igraph_layout_random(&self->g, &m); else ret = igraph_layout_random_3d(&self->g, &m); if (ret) { igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices on a grid * \sa igraph_layout_grid, igraph_layout_grid_3d */ PyObject *igraphmodule_Graph_layout_grid(igraphmodule_GraphObject* self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "width", "height", "dim", NULL }; igraph_matrix_t m; PyObject *result; long int width = 0, height = 0, dim = 2; int ret; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|lll", kwlist, &width, &height, &dim)) return NULL; if (dim == 2 && height > 0) { PyErr_SetString(PyExc_ValueError, "height must not be given if dim=2"); return NULL; } if (dim != 2 && dim != 3) { PyErr_SetString(PyExc_ValueError, "number of dimensions must be either 2 or 3"); return NULL; } if (igraph_matrix_init(&m, 1, 1)) { igraphmodule_handle_igraph_error(); return NULL; } if (dim == 2) ret = igraph_layout_grid(&self->g, &m, width); else ret = igraph_layout_grid_3d(&self->g, &m, width, height); if (ret != IGRAPH_SUCCESS) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&m); return NULL; } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices in a star-like layout * \sa igraph_layout_star */ PyObject *igraphmodule_Graph_layout_star(igraphmodule_GraphObject* self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "center", "order", NULL }; igraph_matrix_t m; PyObject *result, *order_o = Py_None, *center_o = Py_None; igraph_integer_t center = 0; igraph_vector_t* order = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, ¢er_o, &order_o)) return NULL; if (igraph_matrix_init(&m, 1, 1)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_PyObject_to_vid(center_o, ¢er, &self->g)) return NULL; if (order_o != Py_None) { order = (igraph_vector_t*)calloc(1, sizeof(igraph_vector_t)); if (!order) { igraph_matrix_destroy(&m); PyErr_NoMemory(); return NULL; } if (igraphmodule_PyObject_to_vector_t(order_o, order, 1)) { igraph_matrix_destroy(&m); free(order); igraphmodule_handle_igraph_error(); return NULL; } } if (igraph_layout_star(&self->g, &m, center, order)) { if (order) { igraph_vector_destroy(order); free(order); } igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices on a plane according to the Kamada-Kawai algorithm. * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_kamada_kawai */ PyObject *igraphmodule_Graph_layout_kamada_kawai(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "maxiter", "sigma", "initemp", "coolexp", "kkconst", "seed", "minx", "maxx", "miny", "maxy", "minz", "maxz", "dim", NULL }; igraph_matrix_t m; igraph_bool_t use_seed=0; int ret; long int niter = 1000, dim = 2; double sigma, initemp, coolexp, kkconst; PyObject *result, *seed_o=Py_None; PyObject *minx_o=Py_None, *maxx_o=Py_None; PyObject *miny_o=Py_None, *maxy_o=Py_None; PyObject *minz_o=Py_None, *maxz_o=Py_None; igraph_vector_t *minx=0, *maxx=0; igraph_vector_t *miny=0, *maxy=0; igraph_vector_t *minz=0, *maxz=0; #define DESTROY_VECTORS { \ if (minx) { igraph_vector_destroy(minx); free(minx); } \ if (maxx) { igraph_vector_destroy(maxx); free(maxx); } \ if (miny) { igraph_vector_destroy(miny); free(miny); } \ if (maxy) { igraph_vector_destroy(maxy); free(maxy); } \ if (minz) { igraph_vector_destroy(minz); free(minz); } \ if (maxz) { igraph_vector_destroy(maxz); free(maxz); } \ } sigma = igraph_vcount(&self->g); kkconst = sigma * sigma; sigma = sigma / 4.0; initemp = 10.0; coolexp = 0.99; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|lddddOOOOOOOl", kwlist, &niter, &sigma, &initemp, &coolexp, &kkconst, &seed_o, &minx_o, &maxx_o, &miny_o, &maxy_o, &minz_o, &maxz_o, &dim)) return NULL; if (dim != 2 && dim != 3) { PyErr_SetString(PyExc_ValueError, "number of dimensions must be either 2 or 3"); return NULL; } if (seed_o == 0 || seed_o == Py_None) { if (igraph_matrix_init(&m, 1, 1)) { igraphmodule_handle_igraph_error(); return NULL; } } else { use_seed=1; if (igraphmodule_PyList_to_matrix_t(seed_o, &m)) return NULL; } /* Convert minimum and maximum x-y-z values */ if (igraphmodule_attrib_to_vector_t(minx_o, self, &minx, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(maxx_o, self, &maxx, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(miny_o, self, &miny, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(maxy_o, self, &maxy, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } if (dim > 2) { if (igraphmodule_attrib_to_vector_t(minz_o, self, &minz, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(maxz_o, self, &maxz, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } } if (dim == 2) ret = igraph_layout_kamada_kawai (&self->g, &m, (igraph_integer_t) niter, sigma, initemp, coolexp, kkconst, use_seed, /*bounds*/ minx, maxx, miny, maxy); else ret = igraph_layout_kamada_kawai_3d (&self->g, &m, (igraph_integer_t) niter, sigma, initemp, coolexp, kkconst, use_seed, 0, /*bounds*/ minx, maxx, miny, maxy, minz, maxz); DESTROY_VECTORS; #undef DESTROY_VECTORS if (ret) { igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices on a plane according to the DrL algorithm. * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_drl */ PyObject* igraphmodule_Graph_layout_drl(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "weights", "seed", "fixed", "options", "dim", NULL }; igraph_matrix_t m; igraph_bool_t use_seed=0; igraph_vector_t *weights=0; igraph_vector_bool_t *fixed=0; igraph_layout_drl_options_t options; PyObject *result; PyObject *wobj=Py_None, *fixed_o=Py_None, *seed_o=Py_None, *options_o=Py_None; long dim = 2; int retval; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOl", kwlist, &wobj, &seed_o, &fixed_o, &options_o, &dim)) return NULL; if (dim != 2 && dim != 3) { PyErr_SetString(PyExc_ValueError, "number of dimensions must be either 2 or 3"); return NULL; } if (igraphmodule_PyObject_to_drl_options_t(options_o, &options)) return NULL; if (igraph_layout_drl_options_init(&options, IGRAPH_LAYOUT_DRL_DEFAULT)) { igraphmodule_handle_igraph_error(); return NULL; } if (fixed_o != 0 && fixed_o != Py_None) { fixed = (igraph_vector_bool_t*)malloc(sizeof(igraph_vector_bool_t)); if (!fixed) { PyErr_NoMemory(); return NULL; } if (igraphmodule_PyObject_to_vector_bool_t(fixed_o, fixed)) { free(fixed); return NULL; } } if (seed_o == 0 || seed_o == Py_None) { if (igraph_matrix_init(&m, 1, 1)) { igraphmodule_handle_igraph_error(); if (fixed) { igraph_vector_bool_destroy(fixed); free(fixed); } return NULL; } } else { if (igraphmodule_PyList_to_matrix_t(seed_o, &m)) { if (fixed) { igraph_vector_bool_destroy(fixed); free(fixed); } return NULL; } use_seed=1; } /* Convert the weight parameter to a vector */ if (igraphmodule_attrib_to_vector_t(wobj, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); if (fixed) { igraph_vector_bool_destroy(fixed); free(fixed); } igraphmodule_handle_igraph_error(); return NULL; } if (dim == 2) { retval = igraph_layout_drl(&self->g, &m, use_seed, &options, weights, fixed); } else { retval = igraph_layout_drl_3d(&self->g, &m, use_seed, &options, weights, fixed); } if (retval) { igraph_matrix_destroy(&m); if (weights) { igraph_vector_destroy(weights); free(weights); } if (fixed) { igraph_vector_bool_destroy(fixed); free(fixed); } igraphmodule_handle_igraph_error(); return NULL; } if (weights) { igraph_vector_destroy(weights); free(weights); } if (fixed) { igraph_vector_bool_destroy(fixed); free(fixed); } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices on a plane according to the Fruchterman-Reingold algorithm. * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_fruchterman_reingold */ PyObject *igraphmodule_Graph_layout_fruchterman_reingold(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "weights", "maxiter", "maxdelta", "area", "coolexp", "repulserad", "seed", "minx", "maxx", "miny", "maxy", "minz", "maxz", "dim", NULL }; igraph_matrix_t m; igraph_bool_t use_seed=0; igraph_vector_t *weights=0; igraph_vector_t *minx=0, *maxx=0; igraph_vector_t *miny=0, *maxy=0; igraph_vector_t *minz=0, *maxz=0; int ret; long int niter = 500, dim = 2; double maxdelta, area, coolexp, repulserad; PyObject *result; PyObject *wobj=Py_None, *seed_o=Py_None; PyObject *minx_o=Py_None, *maxx_o=Py_None; PyObject *miny_o=Py_None, *maxy_o=Py_None; PyObject *minz_o=Py_None, *maxz_o=Py_None; #define DESTROY_VECTORS { \ if (weights) { igraph_vector_destroy(weights); free(weights); } \ if (minx) { igraph_vector_destroy(minx); free(minx); } \ if (maxx) { igraph_vector_destroy(maxx); free(maxx); } \ if (miny) { igraph_vector_destroy(miny); free(miny); } \ if (maxy) { igraph_vector_destroy(maxy); free(maxy); } \ if (minz) { igraph_vector_destroy(minz); free(minz); } \ if (maxz) { igraph_vector_destroy(maxz); free(maxz); } \ } maxdelta = igraph_vcount(&self->g); coolexp = 1.5; repulserad = -1; area = -1; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OlddddOOOOOOOl", kwlist, &wobj, &niter, &maxdelta, &area, &coolexp, &repulserad, &seed_o, &minx_o, &maxx_o, &miny_o, &maxy_o, &minz_o, &maxz_o, &dim)) return NULL; if (area <= 0) { area = maxdelta * maxdelta; if (dim == 3) area *= maxdelta; } if (repulserad <= 0) repulserad = area * maxdelta; if (dim != 2 && dim != 3) { PyErr_SetString(PyExc_ValueError, "number of dimensions must be either 2 or 3"); return NULL; } if (seed_o == 0 || seed_o == Py_None) { if (igraph_matrix_init(&m, 1, 1)) { igraphmodule_handle_igraph_error(); return NULL; } } else { if (igraphmodule_PyList_to_matrix_t(seed_o, &m)) return NULL; use_seed=1; } /* Convert the weight parameter to a vector */ if (igraphmodule_attrib_to_vector_t(wobj, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } /* Convert minimum and maximum x-y-z values */ if (igraphmodule_attrib_to_vector_t(minx_o, self, &minx, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(maxx_o, self, &maxx, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(miny_o, self, &miny, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(maxy_o, self, &maxy, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } if (dim > 2) { if (igraphmodule_attrib_to_vector_t(minz_o, self, &minz, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(maxz_o, self, &maxz, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); DESTROY_VECTORS; igraphmodule_handle_igraph_error(); return NULL; } } if (dim == 2) ret = igraph_layout_fruchterman_reingold (&self->g, &m, (igraph_integer_t) niter, maxdelta, area, coolexp, repulserad, use_seed, weights, minx, maxx, miny, maxy); else ret = igraph_layout_fruchterman_reingold_3d (&self->g, &m, (igraph_integer_t) niter, maxdelta, area, coolexp, repulserad, use_seed, weights, minx, maxx, miny, maxy, minz, maxz); DESTROY_VECTORS; if (ret) { igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } #undef DESTROY_VECTORS result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices on a plane according to the layout algorithm in * graphopt 0.4.1 * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_graphopt */ PyObject *igraphmodule_Graph_layout_graphopt(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "niter", "node_charge", "node_mass", "spring_length", "spring_constant", "max_sa_movement", "seed", NULL }; igraph_matrix_t m; long int niter = 500; double node_charge = 0.001, node_mass = 30; long spring_length = 0; double spring_constant = 1, max_sa_movement = 5; PyObject *result, *seed_o = Py_None; igraph_bool_t use_seed=0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|lddlddO", kwlist, &niter, &node_charge, &node_mass, &spring_length, &spring_constant, &max_sa_movement, &seed_o)) return NULL; if (seed_o == 0 || seed_o == Py_None) { if (igraph_matrix_init(&m, 1, 1)) { igraphmodule_handle_igraph_error(); return NULL; } } else { use_seed=1; if (igraphmodule_PyList_to_matrix_t(seed_o, &m)) return NULL; } if (igraph_layout_graphopt(&self->g, &m, (igraph_integer_t) niter, node_charge, node_mass, spring_length, spring_constant, max_sa_movement, use_seed)) { igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices of a graph according to the Large Graph Layout * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_lgl */ PyObject *igraphmodule_Graph_layout_lgl(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "maxiter", "maxdelta", "area", "coolexp", "repulserad", "cellsize", "root", NULL }; igraph_matrix_t m; PyObject *result, *root_o = Py_None; long int maxiter = 150; igraph_integer_t proot = -1; double maxdelta, area, coolexp, repulserad, cellsize; maxdelta = igraph_vcount(&self->g); area = -1; coolexp = 1.5; repulserad = -1; cellsize = -1; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ldddddO", kwlist, &maxiter, &maxdelta, &area, &coolexp, &repulserad, &cellsize, &root_o)) return NULL; if (area <= 0) area = igraph_vcount(&self->g)*igraph_vcount(&self->g); if (repulserad <= 0) repulserad = area*igraph_vcount(&self->g); if (cellsize <= 0) cellsize = sqrt(sqrt(area)); if (igraphmodule_PyObject_to_vid(root_o, &proot, &self->g)) return NULL; if (igraph_matrix_init(&m, 1, 1)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_layout_lgl(&self->g, &m, (igraph_integer_t) maxiter, maxdelta, area, coolexp, repulserad, cellsize, proot)) { igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices of a graph using multidimensional scaling * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_mds */ PyObject *igraphmodule_Graph_layout_mds(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "dist", "dim", "arpack_options", NULL }; igraph_matrix_t m; igraph_matrix_t *dist = 0; long int dim = 2; igraphmodule_ARPACKOptionsObject *arpack_options; PyObject *dist_o = Py_None; PyObject *arpack_options_o = igraphmodule_arpack_options_default; PyObject *result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OlO!", kwlist, &dist_o, &dim, &igraphmodule_ARPACKOptionsType, &arpack_options_o)) return NULL; if (dist_o != Py_None) { dist = (igraph_matrix_t*)malloc(sizeof(igraph_matrix_t)); if (!dist) { PyErr_NoMemory(); return NULL; } if (igraphmodule_PyList_to_matrix_t(dist_o, dist)) { free(dist); return NULL; } } if (igraph_matrix_init(&m, 1, 1)) { if (dist) { igraph_matrix_destroy(dist); free(dist); } igraphmodule_handle_igraph_error(); return NULL; } arpack_options = (igraphmodule_ARPACKOptionsObject*)arpack_options_o; if (igraph_layout_mds(&self->g, &m, dist, dim, igraphmodule_ARPACKOptions_get(arpack_options))) { if (dist) { igraph_matrix_destroy(dist); free(dist); } igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } if (dist) { igraph_matrix_destroy(dist); free(dist); } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices of a graph according to the Reingold-Tilford * tree layout algorithm * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_reingold_tilford */ PyObject *igraphmodule_Graph_layout_reingold_tilford(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "mode", "root", "rootlevel", NULL }; igraph_matrix_t m; igraph_vector_t roots, *roots_p = 0; igraph_vector_t rootlevels, *rootlevels_p = 0; PyObject *roots_o=Py_None, *rootlevels_o=Py_None, *mode_o=Py_None; igraph_neimode_t mode = IGRAPH_OUT; PyObject *result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &mode_o, &roots_o, &rootlevels_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (roots_o != Py_None) { roots_p = &roots; if (igraphmodule_PyObject_to_vector_t(roots_o, roots_p, 1)) return 0; } if (rootlevels_o != Py_None) { rootlevels_p = &rootlevels; if (igraphmodule_PyObject_to_vector_t(rootlevels_o, rootlevels_p, 1)) { if (roots_p) igraph_vector_destroy(roots_p); return 0; } } if (igraph_matrix_init(&m, 1, 1)) { if (roots_p) igraph_vector_destroy(roots_p); if (rootlevels_p) igraph_vector_destroy(rootlevels_p); igraphmodule_handle_igraph_error(); return NULL; } if (igraph_layout_reingold_tilford(&self->g, &m, mode, roots_p, rootlevels_p)) { igraph_matrix_destroy(&m); if (roots_p) igraph_vector_destroy(roots_p); if (rootlevels_p) igraph_vector_destroy(rootlevels_p); igraphmodule_handle_igraph_error(); return NULL; } if (roots_p) igraph_vector_destroy(roots_p); if (rootlevels_p) igraph_vector_destroy(rootlevels_p); result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices of a graph according to the Reingold-Tilford * tree layout algorithm in a polar coordinate system * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_reingold_tilford */ PyObject *igraphmodule_Graph_layout_reingold_tilford_circular( igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "mode", "root", "rootlevel", NULL }; igraph_matrix_t m; igraph_vector_t roots, *roots_p = 0; igraph_vector_t rootlevels, *rootlevels_p = 0; PyObject *roots_o=Py_None, *rootlevels_o=Py_None, *mode_o=Py_None; igraph_neimode_t mode = IGRAPH_OUT; PyObject *result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &mode_o, &roots_o, &rootlevels_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (roots_o != Py_None) { roots_p = &roots; if (igraphmodule_PyObject_to_vector_t(roots_o, roots_p, 1)) return 0; } if (rootlevels_o != Py_None) { rootlevels_p = &rootlevels; if (igraphmodule_PyObject_to_vector_t(rootlevels_o, rootlevels_p, 1)) { if (roots_p) igraph_vector_destroy(roots_p); return 0; } } if (igraph_matrix_init(&m, 1, 1)) { if (roots_p) igraph_vector_destroy(roots_p); if (rootlevels_p) igraph_vector_destroy(rootlevels_p); igraphmodule_handle_igraph_error(); return NULL; } if (igraph_layout_reingold_tilford_circular(&self->g, &m, mode, roots_p, rootlevels_p)) { igraph_matrix_destroy(&m); if (roots_p) igraph_vector_destroy(roots_p); if (rootlevels_p) igraph_vector_destroy(rootlevels_p); igraphmodule_handle_igraph_error(); return NULL; } if (roots_p) igraph_vector_destroy(roots_p); if (rootlevels_p) igraph_vector_destroy(rootlevels_p); result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices of a graph according to the Sugiyama layout. * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_sugiyama */ PyObject *igraphmodule_Graph_layout_sugiyama( igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "layers", "weights", "hgap", "vgap", "maxiter", "return_extended_graph", NULL }; igraph_matrix_t m; igraph_t extd_graph; igraph_vector_t extd_to_orig_eids; igraph_vector_t *weights = 0, *layers = 0; double hgap = 1, vgap = 1; long int maxiter = 100; PyObject *layers_o = Py_None, *weights_o = Py_None, *extd_to_orig_eids_o = Py_None; PyObject *return_extended_graph = Py_False; PyObject *result; igraphmodule_GraphObject *graph_o; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOddlO", kwlist, &layers_o, &weights_o, &hgap, &vgap, &maxiter, &return_extended_graph)) return NULL; if (igraph_vector_init(&extd_to_orig_eids, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_matrix_init(&m, 1, 1)) { igraph_vector_destroy(&extd_to_orig_eids); igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(layers_o, self, &layers, ATTRIBUTE_TYPE_VERTEX)) { igraph_vector_destroy(&extd_to_orig_eids); igraph_matrix_destroy(&m); return NULL; } if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) { if (layers != 0) { igraph_vector_destroy(layers); free(layers); } igraph_vector_destroy(&extd_to_orig_eids); igraph_matrix_destroy(&m); return NULL; } if (igraph_layout_sugiyama(&self->g, &m, (PyObject_IsTrue(return_extended_graph) ? &extd_graph : 0), (PyObject_IsTrue(return_extended_graph) ? &extd_to_orig_eids : 0), layers, hgap, vgap, maxiter, weights)) { if (layers != 0) { igraph_vector_destroy(layers); free(layers); } if (weights != 0) { igraph_vector_destroy(weights); free(weights); } igraph_vector_destroy(&extd_to_orig_eids); igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } if (layers != 0) { igraph_vector_destroy(layers); free(layers); } if (weights != 0) { igraph_vector_destroy(weights); free(weights); } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); if (PyObject_IsTrue(return_extended_graph)) { CREATE_GRAPH(graph_o, extd_graph); extd_to_orig_eids_o = igraphmodule_vector_t_to_PyList(&extd_to_orig_eids, IGRAPHMODULE_TYPE_INT); result = Py_BuildValue("NNN", result, graph_o, extd_to_orig_eids_o); } igraph_vector_destroy(&extd_to_orig_eids); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Places the vertices of a bipartite graph according to a simple two-layer * Sugiyama layout. * \return the calculated coordinates as a Python list of lists * \sa igraph_layout_bipartite */ PyObject *igraphmodule_Graph_layout_bipartite( igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "types", "hgap", "vgap", "maxiter", NULL }; igraph_matrix_t m; igraph_vector_bool_t *types = 0; double hgap = 1, vgap = 1; long int maxiter = 100; PyObject *types_o = Py_None; PyObject *result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oddl", kwlist, &types_o, &hgap, &vgap, &maxiter)) return NULL; if (igraph_matrix_init(&m, 1, 1)) { igraphmodule_handle_igraph_error(); return NULL; } if (types_o == Py_None) { types_o = PyString_FromString("type"); } else { Py_INCREF(types_o); } if (igraphmodule_attrib_to_vector_bool_t(types_o, self, &types, ATTRIBUTE_TYPE_VERTEX)) { igraph_matrix_destroy(&m); Py_DECREF(types_o); return NULL; } Py_DECREF(types_o); if (igraph_layout_bipartite(&self->g, types, &m, hgap, vgap, maxiter)) { if (types != 0) { igraph_vector_bool_destroy(types); free(types); } igraph_matrix_destroy(&m); igraphmodule_handle_igraph_error(); return NULL; } if (types != 0) { igraph_vector_bool_destroy(types); free(types); } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&m); return (PyObject *) result; } /********************************************************************** * Conversion between various graph representations * **********************************************************************/ /** \ingroup python_interface_graph * \brief Returns the adjacency matrix of a graph. * \return the adjacency matrix as a Python list of lists * \sa igraph_get_adjacency */ PyObject *igraphmodule_Graph_get_adjacency(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "type", "eids", NULL }; igraph_get_adjacency_t t = IGRAPH_GET_ADJACENCY_BOTH; igraph_matrix_t m; PyObject *result, *eids = Py_False; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist, &t, &eids)) return NULL; if (t != IGRAPH_GET_ADJACENCY_UPPER && t != IGRAPH_GET_ADJACENCY_LOWER && t != IGRAPH_GET_ADJACENCY_BOTH) { PyErr_SetString(PyExc_ValueError, "type must be either GET_ADJACENCY_LOWER or GET_ADJACENCY_UPPER or GET_ADJACENCY_BOTH"); return NULL; } if (igraph_matrix_init (&m, igraph_vcount(&self->g), igraph_vcount(&self->g))) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_get_adjacency(&self->g, &m, t, PyObject_IsTrue(eids))) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&m); return NULL; } result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_INT); igraph_matrix_destroy(&m); return result; } /** \ingroup python_interface_graph * \brief Returns the incidence matrix of a bipartite graph. * \return the incidence matrix as a Python list of lists * \sa igraph_get_incidence */ PyObject *igraphmodule_Graph_get_incidence(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "types", NULL }; igraph_matrix_t matrix; igraph_vector_t row_ids, col_ids; igraph_vector_bool_t *types; PyObject *matrix_o, *row_ids_o, *col_ids_o, *types_o; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &types_o)) return NULL; if (igraph_vector_init(&row_ids, 0)) return NULL; if (igraph_vector_init(&col_ids, 0)) { igraph_vector_destroy(&row_ids); return NULL; } if (igraphmodule_attrib_to_vector_bool_t(types_o, self, &types, ATTRIBUTE_TYPE_VERTEX)) { igraph_vector_destroy(&row_ids); igraph_vector_destroy(&col_ids); return NULL; } if (igraph_matrix_init(&matrix, 1, 1)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&row_ids); igraph_vector_destroy(&col_ids); if (types) { igraph_vector_bool_destroy(types); free(types); } return NULL; } if (igraph_get_incidence(&self->g, types, &matrix, &row_ids, &col_ids)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&row_ids); igraph_vector_destroy(&col_ids); if (types) { igraph_vector_bool_destroy(types); free(types); } igraph_matrix_destroy(&matrix); return NULL; } if (types) { igraph_vector_bool_destroy(types); free(types); } matrix_o = igraphmodule_matrix_t_to_PyList(&matrix, IGRAPHMODULE_TYPE_INT); igraph_matrix_destroy(&matrix); row_ids_o = igraphmodule_vector_t_to_PyList(&row_ids, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&row_ids); col_ids_o = igraphmodule_vector_t_to_PyList(&col_ids, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&col_ids); return Py_BuildValue("NNN", matrix_o, row_ids_o, col_ids_o); } /** \ingroup python_interface_graph * \brief Returns the Laplacian matrix of a graph. * \return the Laplacian matrix as a Python list of lists * \sa igraph_laplacian */ PyObject *igraphmodule_Graph_laplacian(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "weights", "normalized", NULL }; igraph_matrix_t m; PyObject *result; PyObject *weights_o = Py_None; PyObject *normalized = Py_False; igraph_vector_t *weights = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &weights_o, &normalized)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; if (igraph_matrix_init (&m, igraph_vcount(&self->g), igraph_vcount(&self->g))) { igraphmodule_handle_igraph_error(); if (weights) { igraph_vector_destroy(weights); free(weights); } return NULL; } if (igraph_laplacian(&self->g, &m, /*sparseres=*/ 0, PyObject_IsTrue(normalized), weights)) { igraphmodule_handle_igraph_error(); if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_matrix_destroy(&m); return NULL; } if (PyObject_IsTrue(normalized) || weights) { result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_FLOAT); } else { result = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_INT); } if (weights) { igraph_vector_destroy(weights); free(weights); } igraph_matrix_destroy(&m); return result; } /** \ingroup python_interface_graph * \brief Returns the list of edges in a graph. * \return the list of edges, every edge is represented by a pair * \sa igraph_get_edgelist */ PyObject *igraphmodule_Graph_get_edgelist(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { igraph_vector_t edgelist; PyObject *result; igraph_vector_init(&edgelist, igraph_ecount(&self->g)); if (igraph_get_edgelist(&self->g, &edgelist, 0)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&edgelist); return NULL; } result = igraphmodule_vector_t_to_PyList_pairs(&edgelist); igraph_vector_destroy(&edgelist); return (PyObject *) result; } /** \ingroup python_interface_graph * \function igraphmodule_Graph_to_undirected * \brief Converts a directed graph to an undirected one. * \return The undirected graph. * \sa igraph_to_undirected */ PyObject *igraphmodule_Graph_to_undirected(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *mode_o = Py_None, *comb_o = Py_None; igraph_to_undirected_t mode = IGRAPH_TO_UNDIRECTED_COLLAPSE; igraph_attribute_combination_t comb; static char *kwlist[] = { "mode", "combine_edges", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &mode_o, &comb_o)) return NULL; if (igraphmodule_PyObject_to_to_undirected_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_attribute_combination_t(comb_o, &comb)) return NULL; if (igraph_to_undirected(&self->g, mode, &comb)) { igraph_attribute_combination_destroy(&comb); igraphmodule_handle_igraph_error(); return NULL; } igraph_attribute_combination_destroy(&comb); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \function igraphmodule_Graph_to_directed * \brief Converts an undirected graph to a directed one. * \return The directed graph. * \sa igraph_to_directed */ PyObject *igraphmodule_Graph_to_directed(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *mutual = Py_True; igraph_to_directed_t mode = IGRAPH_TO_DIRECTED_MUTUAL; static char *kwlist[] = { "mutual", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &mutual)) return NULL; mode = (PyObject_IsTrue(mutual) ? IGRAPH_TO_DIRECTED_MUTUAL : IGRAPH_TO_DIRECTED_ARBITRARY); if (igraph_to_directed(&self->g, mode)) { igraphmodule_handle_igraph_error(); return NULL; } Py_RETURN_NONE; } /********************************************************************** * Reading/writing foreing graph formats * **********************************************************************/ /** \ingroup python_interface_graph * \brief Reads a DIMACS file and creates a graph from it. * \return the graph * \sa igraph_read_graph_dimacs */ PyObject *igraphmodule_Graph_Read_DIMACS(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraphmodule_filehandle_t fobj; igraph_integer_t source = 0, target = 0; igraph_vector_t capacity; igraph_t g; PyObject *fname = NULL, *directed = Py_False, *capacity_obj; static char *kwlist[] = { "f", "directed", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|O", kwlist, &fname, &directed)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "r")) return NULL; if (igraph_vector_init(&capacity, 0)) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } if (igraph_read_graph_dimacs(&g, igraphmodule_filehandle_get(&fobj), 0, 0, &source, &target, &capacity, PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&capacity); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); capacity_obj = igraphmodule_vector_t_to_PyList(&capacity, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&capacity); if (!capacity_obj) return NULL; CREATE_GRAPH_FROM_TYPE(self, g, type); return Py_BuildValue("NiiN", (PyObject *) self, (long)source, (long)target, capacity_obj); } /** \ingroup python_interface_graph * \brief Reads an UCINET DL file and creates a graph from it. * \return the graph * \sa igraph_read_graph_dl */ PyObject *igraphmodule_Graph_Read_DL(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; igraph_t g; igraphmodule_filehandle_t fobj; PyObject *fname = NULL, *directed = Py_True; static char *kwlist[] = { "f", "directed", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|O", kwlist, &fname, &directed)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "r")) return NULL; if (igraph_read_graph_dl(&g, igraphmodule_filehandle_get(&fobj), PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject*)self; } /** \ingroup python_interface_graph * \brief Reads an edge list from a file and creates a graph from it. * \return the graph * \sa igraph_read_graph_edgelist */ PyObject *igraphmodule_Graph_Read_Edgelist(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; PyObject *directed = Py_True, *fname = NULL; igraphmodule_filehandle_t fobj; igraph_t g; static char *kwlist[] = { "f", "directed", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &fname, &directed)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "r")) return NULL; if (igraph_read_graph_edgelist(&g, igraphmodule_filehandle_get(&fobj), 0, PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Reads an edge list from an NCOL file and creates a graph from it. * \return the graph * \sa igraph_read_graph_ncol */ PyObject *igraphmodule_Graph_Read_Ncol(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; PyObject *names = Py_True, *weights = Py_None, *directed = Py_True; PyObject *fname = NULL; igraphmodule_filehandle_t fobj; igraph_add_weights_t add_weights = IGRAPH_ADD_WEIGHTS_IF_PRESENT; igraph_t g; static char *kwlist[] = { "f", "names", "weights", "directed", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", kwlist, &fname, &names, &weights, &directed)) return NULL; if (igraphmodule_PyObject_to_add_weights_t(weights, &add_weights)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "r")) return NULL; if (igraph_read_graph_ncol(&g, igraphmodule_filehandle_get(&fobj), 0, PyObject_IsTrue(names), add_weights, PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Reads an edge list from an LGL file and creates a graph from it. * \return the graph * \sa igraph_read_graph_lgl */ PyObject *igraphmodule_Graph_Read_Lgl(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; PyObject *names = Py_True, *weights = Py_None, *directed = Py_True; PyObject *fname = NULL; igraphmodule_filehandle_t fobj; igraph_add_weights_t add_weights = IGRAPH_ADD_WEIGHTS_IF_PRESENT; igraph_t g; static char *kwlist[] = { "f", "names", "weights", "directed", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", kwlist, &fname, &names, &weights, &directed)) return NULL; if (igraphmodule_PyObject_to_add_weights_t(weights, &add_weights)) return NULL; if (kwds && PyDict_Check(kwds) && \ PyDict_GetItemString(kwds, "directed") == NULL) { if (PyErr_Occurred()) return NULL; PY_IGRAPH_WARN("Graph.Read_Lgl creates directed networks by default from igraph 0.6. To get rid of this warning, specify directed=... explicitly. This warning will be removed from igraph 0.7."); } if (igraphmodule_filehandle_init(&fobj, fname, "r")) return NULL; if (igraph_read_graph_lgl(&g, igraphmodule_filehandle_get(&fobj), PyObject_IsTrue(names), add_weights, PyObject_IsTrue(directed))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Reads an edge list from a Pajek file and creates a graph from it. * \return the graph * \sa igraph_read_graph_pajek */ PyObject *igraphmodule_Graph_Read_Pajek(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; PyObject *fname = NULL; igraphmodule_filehandle_t fobj; igraph_t g; static char *kwlist[] = { "f", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &fname)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "r")) return NULL; if (igraph_read_graph_pajek(&g, igraphmodule_filehandle_get(&fobj))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Reads a GML file and creates a graph from it. * \return the graph * \sa igraph_read_graph_gml */ PyObject *igraphmodule_Graph_Read_GML(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; PyObject *fname = NULL; igraphmodule_filehandle_t fobj; igraph_t g; static char *kwlist[] = { "f", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &fname)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "r")) return NULL; if (igraph_read_graph_gml(&g, igraphmodule_filehandle_get(&fobj))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Reads a GraphDB file and creates a graph from it. * \return the graph * \sa igraph_read_graph_graphdb */ PyObject *igraphmodule_Graph_Read_GraphDB(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; PyObject *fname = NULL, *directed_o = Py_False; igraph_t g; igraphmodule_filehandle_t fobj; static char *kwlist[] = { "f", "directed", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &fname, &directed_o)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "r")) return NULL; if (igraph_read_graph_graphdb(&g, igraphmodule_filehandle_get(&fobj), PyObject_IsTrue(directed_o))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Reads a GraphML file and creates a graph from it. * \return the graph * \sa igraph_read_graph_graphml */ PyObject *igraphmodule_Graph_Read_GraphML(PyTypeObject * type, PyObject * args, PyObject * kwds) { igraphmodule_GraphObject *self; PyObject *fname = NULL; long int index = 0; igraph_t g; igraphmodule_filehandle_t fobj; static char *kwlist[] = { "f", "index", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|l", kwlist, &fname, &index)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "r")) return NULL; if (igraph_read_graph_graphml(&g, igraphmodule_filehandle_get(&fobj), (igraph_integer_t) index)) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); CREATE_GRAPH_FROM_TYPE(self, g, type); return (PyObject *) self; } /** \ingroup python_interface_graph * \brief Writes the graph as a DIMACS file * \return none * \sa igraph_write_graph_dimacs */ PyObject *igraphmodule_Graph_write_dimacs(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { long source = 0, target = 0; PyObject *capacity_obj = Py_None, *fname = NULL; igraphmodule_filehandle_t fobj; igraph_vector_t* capacity = 0; static char *kwlist[] = { "f", "source", "target", "capacity", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oll|O", kwlist, &fname, &source, &target, &capacity_obj)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "w")) return NULL; if (capacity_obj == Py_None) { capacity_obj = PyString_FromString("capacity"); } else { Py_INCREF(capacity_obj); } if (igraphmodule_attrib_to_vector_t(capacity_obj, self, &capacity, ATTRIBUTE_TYPE_EDGE)) { igraphmodule_filehandle_destroy(&fobj); Py_DECREF(capacity_obj); return NULL; } Py_DECREF(capacity_obj); if (igraph_write_graph_dimacs(&self->g, igraphmodule_filehandle_get(&fobj), source, target, capacity)) { igraphmodule_handle_igraph_error(); if (capacity) { igraph_vector_destroy(capacity); free(capacity); } igraphmodule_filehandle_destroy(&fobj); return NULL; } if (capacity) { igraph_vector_destroy(capacity); free(capacity); } igraphmodule_filehandle_destroy(&fobj); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Writes the graph as a DOT (GraphViz) file * \return none * \sa igraph_write_graph_dot */ PyObject *igraphmodule_Graph_write_dot(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *fname = NULL; igraphmodule_filehandle_t fobj; static char *kwlist[] = { "f", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &fname)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "w")) return NULL; if (igraph_write_graph_dot(&self->g, igraphmodule_filehandle_get(&fobj))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Writes the edge list to a file * \return none * \sa igraph_write_graph_edgelist */ PyObject *igraphmodule_Graph_write_edgelist(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *fname = NULL; igraphmodule_filehandle_t fobj; static char *kwlist[] = { "f", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &fname)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "w")) return NULL; if (igraph_write_graph_edgelist(&self->g, igraphmodule_filehandle_get(&fobj))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Writes the graph as a GML file * \return none * \sa igraph_write_graph_gml */ PyObject *igraphmodule_Graph_write_gml(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *ids = Py_None, *fname = NULL; PyObject *creator = Py_None; igraph_vector_t idvec, *idvecptr=0; char *creator_str=0; igraphmodule_filehandle_t fobj; static char *kwlist[] = { "f", "creator", "ids", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist, &fname, &creator, &ids)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "w")) return NULL; if (PyList_Check(ids)) { idvecptr = &idvec; if (igraphmodule_PyObject_to_vector_t(ids, idvecptr, 0)) { igraphmodule_filehandle_destroy(&fobj); return NULL; } } if (creator != Py_None) { PyObject* o = PyObject_Str(creator); if (o == 0) { if (idvecptr) igraph_vector_destroy(idvecptr); igraphmodule_filehandle_destroy(&fobj); } creator_str = PyString_CopyAsString(o); Py_DECREF(o); if (creator_str == 0) { if (idvecptr) igraph_vector_destroy(idvecptr); igraphmodule_filehandle_destroy(&fobj); } } if (igraph_write_graph_gml(&self->g, igraphmodule_filehandle_get(&fobj), idvecptr, creator_str)) { if (idvecptr) { igraph_vector_destroy(idvecptr); } if (creator_str) free(creator_str); igraphmodule_filehandle_destroy(&fobj); igraphmodule_handle_igraph_error(); return NULL; } if (idvecptr) { igraph_vector_destroy(idvecptr); } if (creator_str) free(creator_str); igraphmodule_filehandle_destroy(&fobj); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Writes the edge list to a file in .ncol format * \return none * \sa igraph_write_graph_ncol */ PyObject *igraphmodule_Graph_write_ncol(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *fname = NULL; char *names = "name"; char *weights = "weight"; igraphmodule_filehandle_t fobj; static char *kwlist[] = { "f", "names", "weights", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|zz", kwlist, &fname, &names, &weights)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "w")) return NULL; if (igraph_write_graph_ncol(&self->g, igraphmodule_filehandle_get(&fobj), names, weights)) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Writes the edge list to a file in .lgl format * \return none * \sa igraph_write_graph_lgl */ PyObject *igraphmodule_Graph_write_lgl(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *fname = NULL; char *names = "name"; char *weights = "weight"; PyObject *isolates = Py_True; igraphmodule_filehandle_t fobj; static char *kwlist[] = { "f", "names", "weights", "isolates", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|zzO", kwlist, &fname, &names, &weights, &isolates)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "w")) return NULL; if (igraph_write_graph_lgl(&self->g, igraphmodule_filehandle_get(&fobj), names, weights, PyObject_IsTrue(isolates))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Writes the graph as a Pajek .net file * \return none * \sa igraph_write_graph_pajek */ PyObject *igraphmodule_Graph_write_pajek(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *fname = NULL; static char *kwlist[] = { "f", NULL }; igraphmodule_filehandle_t fobj; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &fname)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "w")) return NULL; if (igraph_write_graph_pajek(&self->g, igraphmodule_filehandle_get(&fobj))) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Writes the graph to a GraphML file * \return none * \sa igraph_write_graph_graphml */ PyObject *igraphmodule_Graph_write_graphml(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *fname = NULL; static char *kwlist[] = { "f", NULL }; igraphmodule_filehandle_t fobj; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &fname)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "w")) return NULL; if (igraph_write_graph_graphml(&self->g, igraphmodule_filehandle_get(&fobj), /*prefixattr=*/ 1)) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); Py_RETURN_NONE; } /** \ingroup python_interface_graph * \brief Writes the edge list to a file in LEDA native format * \return none * \sa igraph_write_graph_leda */ PyObject *igraphmodule_Graph_write_leda(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *fname = NULL; char *vertex_attr_name = "name"; char *edge_attr_name = "weight"; igraphmodule_filehandle_t fobj; static char *kwlist[] = { "f", "names", "weights", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|zz", kwlist, &fname, &vertex_attr_name, &edge_attr_name)) return NULL; if (igraphmodule_filehandle_init(&fobj, fname, "w")) return NULL; if (igraph_write_graph_leda(&self->g, igraphmodule_filehandle_get(&fobj), vertex_attr_name, edge_attr_name)) { igraphmodule_handle_igraph_error(); igraphmodule_filehandle_destroy(&fobj); return NULL; } igraphmodule_filehandle_destroy(&fobj); Py_RETURN_NONE; } /********************************************************************** * Routines related to graph isomorphism * **********************************************************************/ /** * \ingroup python_interface_graph * \brief Calculates the canonical permutation of a graph using BLISS * \sa igraph_canonical_permutation */ PyObject *igraphmodule_Graph_canonical_permutation( igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "sh", NULL }; PyObject *sh_o = Py_None; PyObject *list; igraph_bliss_sh_t sh = IGRAPH_BLISS_FM; igraph_vector_t labeling; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &sh_o)) return NULL; if (igraphmodule_PyObject_to_bliss_sh_t(sh_o, &sh)) return NULL; if (igraph_vector_init(&labeling, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_canonical_permutation(&self->g, &labeling, sh, 0)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&labeling); return NULL; } list = igraphmodule_vector_t_to_PyList(&labeling, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&labeling); return list; } /** \ingroup python_interface_graph * \brief Calculates the isomorphy class of a graph or its subgraph * \sa igraph_isoclass, igraph_isoclass_subgraph */ PyObject *igraphmodule_Graph_isoclass(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { Py_ssize_t n; igraph_integer_t isoclass = 0; PyObject *vids = 0; char *kwlist[] = { "vertices", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwds, "|O!", kwlist, &PyList_Type, &vids)) return NULL; n = vids ? PyList_Size(vids) : igraph_vcount(&self->g); if (n < 3 || n > 4) { PyErr_SetString(PyExc_ValueError, "Graph or subgraph must have 3 or 4 vertices."); return NULL; } if (vids) { igraph_vector_t vidsvec; if (igraphmodule_PyObject_to_vector_t(vids, &vidsvec, 1)) { PyErr_SetString(PyExc_ValueError, "Error while converting PyList to igraph_vector_t"); return NULL; } if (igraph_isoclass_subgraph(&self->g, &vidsvec, &isoclass)) { igraphmodule_handle_igraph_error(); return NULL; } } else { if (igraph_isoclass(&self->g, &isoclass)) { igraphmodule_handle_igraph_error(); return NULL; } } return PyInt_FromLong((long)isoclass); } /** \ingroup python_interface_graph * \brief Determines whether the graph is isomorphic to another graph. * * \sa igraph_isomorphic */ PyObject *igraphmodule_Graph_isomorphic(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { igraph_bool_t result = 0; PyObject *o = Py_None; igraphmodule_GraphObject *other; static char *kwlist[] = { "other", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!", kwlist, &igraphmodule_GraphType, &o)) return NULL; if (o == Py_None) other = self; else other = (igraphmodule_GraphObject *) o; if (igraph_isomorphic(&self->g, &other->g, &result)) { igraphmodule_handle_igraph_error(); return NULL; } if (result) Py_RETURN_TRUE; Py_RETURN_FALSE; } /** \ingroup python_interface_graph * \brief Determines whether the graph is isomorphic to another graph, * using the BLISS isomorphism algorithm * * The actual code is almost the same as igraphmodule_Graph_isomorphic_vf2. * Be sure to correct bugs in both interfaces if applicable! * * \sa igraph_isomorphic_bliss */ PyObject *igraphmodule_Graph_isomorphic_bliss(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { igraph_bool_t result = 0; PyObject *o=Py_None, *return1=Py_False, *return2=Py_False; PyObject *sho1=Py_None, *sho2=Py_None; igraphmodule_GraphObject *other; igraph_vector_t mapping_12, mapping_21, *map12=0, *map21=0; igraph_bliss_sh_t sh1=IGRAPH_BLISS_FM, sh2=IGRAPH_BLISS_FM; static char *kwlist[] = { "other", "return_mapping_12", "return_mapping_21", "sh1", "sh2", NULL }; /* TODO: convert igraph_bliss_info_t when needed */ if (!PyArg_ParseTupleAndKeywords (args, kwds, "|O!OOOO", kwlist, &igraphmodule_GraphType, &o, &return1, &return2, &sho1, &sho2)) return NULL; if (igraphmodule_PyObject_to_bliss_sh_t(sho1, &sh1)) return NULL; if (igraphmodule_PyObject_to_bliss_sh_t(sho2, &sh2)) return NULL; if (o == Py_None) other = self; else other = (igraphmodule_GraphObject *) o; if (PyObject_IsTrue(return1)) { igraph_vector_init(&mapping_12, 0); map12 = &mapping_12; } if (PyObject_IsTrue(return2)) { igraph_vector_init(&mapping_21, 0); map21 = &mapping_21; } if (igraph_isomorphic_bliss(&self->g, &other->g, &result, map12, map21, sh1, sh2, 0, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (!map12 && !map21) { if (result) Py_RETURN_TRUE; Py_RETURN_FALSE; } else { PyObject *iso, *m1, *m2; iso = result ? Py_True : Py_False; Py_INCREF(iso); if (map12) { m1 = igraphmodule_vector_t_to_PyList(map12, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(map12); if (!m1) { Py_DECREF(iso); if (map21) igraph_vector_destroy(map21); return NULL; } } else { m1 = Py_None; Py_INCREF(m1); } if (map21) { m2 = igraphmodule_vector_t_to_PyList(map21, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(map21); if (!m2) { Py_DECREF(iso); Py_DECREF(m1); return NULL; } } else { m2 = Py_None; Py_INCREF(m2); } return Py_BuildValue("NNN", iso, m1, m2); } } typedef struct { PyObject* node_compat_fn; PyObject* edge_compat_fn; PyObject* callback_fn; PyObject* graph1; PyObject* graph2; } igraphmodule_i_Graph_isomorphic_vf2_callback_data_t; igraph_bool_t igraphmodule_i_Graph_isomorphic_vf2_callback_fn( const igraph_vector_t *map12, const igraph_vector_t *map21, void* extra) { igraphmodule_i_Graph_isomorphic_vf2_callback_data_t* data = (igraphmodule_i_Graph_isomorphic_vf2_callback_data_t*)extra; igraph_bool_t retval; PyObject *map12_o, *map21_o; PyObject *result; map12_o = igraphmodule_vector_t_to_PyList(map12, IGRAPHMODULE_TYPE_INT); if (map12_o == NULL) { /* Error in conversion, return 0 to stop the search */ PyErr_WriteUnraisable(data->callback_fn); return 0; } map21_o = igraphmodule_vector_t_to_PyList(map21, IGRAPHMODULE_TYPE_INT); if (map21_o == NULL) { /* Error in conversion, return 0 to stop the search */ PyErr_WriteUnraisable(data->callback_fn); Py_DECREF(map21_o); return 0; } result = PyObject_CallFunction(data->callback_fn, "OOOO", data->graph1, data->graph2, map12_o, map21_o); Py_DECREF(map12_o); Py_DECREF(map21_o); if (result == NULL) { /* Error in callback, return 0 */ PyErr_WriteUnraisable(data->callback_fn); return 0; } retval = PyObject_IsTrue(result); Py_DECREF(result); return retval; } igraph_bool_t igraphmodule_i_Graph_isomorphic_vf2_node_compat_fn( const igraph_t *graph1, const igraph_t *graph2, const igraph_integer_t cand1, const igraph_integer_t cand2, void* extra) { igraphmodule_i_Graph_isomorphic_vf2_callback_data_t* data = (igraphmodule_i_Graph_isomorphic_vf2_callback_data_t*)extra; igraph_bool_t retval; PyObject *result; result = PyObject_CallFunction(data->node_compat_fn, "OOll", data->graph1, data->graph2, (long)cand1, (long)cand2); if (result == NULL) { /* Error in callback, return 0 */ PyErr_WriteUnraisable(data->node_compat_fn); return 0; } retval = PyObject_IsTrue(result); Py_DECREF(result); return retval; } igraph_bool_t igraphmodule_i_Graph_isomorphic_vf2_edge_compat_fn( const igraph_t *graph1, const igraph_t *graph2, const igraph_integer_t cand1, const igraph_integer_t cand2, void* extra) { igraphmodule_i_Graph_isomorphic_vf2_callback_data_t* data = (igraphmodule_i_Graph_isomorphic_vf2_callback_data_t*)extra; igraph_bool_t retval; PyObject *result; result = PyObject_CallFunction(data->edge_compat_fn, "OOll", data->graph1, data->graph2, (long)cand1, (long)cand2); if (result == NULL) { /* Error in callback, return 0 */ PyErr_WriteUnraisable(data->edge_compat_fn); return 0; } retval = PyObject_IsTrue(result); Py_DECREF(result); return retval; } /** \ingroup python_interface_graph * \brief Determines whether the graph is isomorphic to another graph, * using the VF2 isomorphism algorithm * * The actual code is almost the same as igraphmodule_Graph_subisomorphic. * Be sure to correct bugs in both interfaces if applicable! * * \sa igraph_isomorphic_vf2 */ PyObject *igraphmodule_Graph_isomorphic_vf2(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { igraph_bool_t result = 0; PyObject *o=Py_None, *return1=Py_False, *return2=Py_False; PyObject *color1_o=Py_None, *color2_o=Py_None; PyObject *edge_color1_o=Py_None, *edge_color2_o=Py_None; PyObject *callback_fn=Py_None; PyObject *node_compat_fn=Py_None, *edge_compat_fn=Py_None; igraphmodule_GraphObject *other; igraph_vector_t mapping_12, mapping_21; igraph_vector_t *map12=0, *map21=0; igraph_vector_int_t *color1=0, *color2=0; igraph_vector_int_t *edge_color1=0, *edge_color2=0; igraphmodule_i_Graph_isomorphic_vf2_callback_data_t callback_data; int retval; static char *kwlist[] = { "other", "color1", "color2", "edge_color1", "edge_color2", "return_mapping_12", "return_mapping_21", "callback", "node_compat_fn", "edge_compat_fn", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwds, "|O!OOOOOOOOO", kwlist, &igraphmodule_GraphType, &o, &color1_o, &color2_o, &edge_color1_o, &edge_color2_o, &return1, &return2, &callback_fn, &node_compat_fn, &edge_compat_fn)) return NULL; if (o == Py_None) other=self; else other=(igraphmodule_GraphObject*)o; if (callback_fn != Py_None && !PyCallable_Check(callback_fn)) { PyErr_SetString(PyExc_TypeError, "callback must be None or callable"); return NULL; } if (node_compat_fn != Py_None && !PyCallable_Check(node_compat_fn)) { PyErr_SetString(PyExc_TypeError, "node_compat_fn must be None or callable"); return NULL; } if (edge_compat_fn != Py_None && !PyCallable_Check(edge_compat_fn)) { PyErr_SetString(PyExc_TypeError, "edge_compat_fn must be None or callable"); return NULL; } if (igraphmodule_attrib_to_vector_int_t(color1_o, self, &color1, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraphmodule_attrib_to_vector_int_t(color2_o, other, &color2, ATTRIBUTE_TYPE_VERTEX)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color1_o, self, &edge_color1, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color2_o, other, &edge_color2, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } return NULL; } if (PyObject_IsTrue(return1)) { igraph_vector_init(&mapping_12, 0); map12 = &mapping_12; } if (PyObject_IsTrue(return2)) { igraph_vector_init(&mapping_21, 0); map21 = &mapping_21; } callback_data.graph1 = (PyObject*)self; callback_data.graph2 = (PyObject*)other; callback_data.callback_fn = callback_fn == Py_None ? 0 : callback_fn; callback_data.node_compat_fn = node_compat_fn == Py_None ? 0 : node_compat_fn; callback_data.edge_compat_fn = edge_compat_fn == Py_None ? 0 : edge_compat_fn; if (callback_data.callback_fn == 0) { retval = igraph_isomorphic_vf2(&self->g, &other->g, color1, color2, edge_color1, edge_color2, &result, map12, map21, node_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_node_compat_fn, edge_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_edge_compat_fn, &callback_data); } else { retval = igraph_isomorphic_function_vf2(&self->g, &other->g, color1, color2, edge_color1, edge_color2, map12, map21, igraphmodule_i_Graph_isomorphic_vf2_callback_fn, node_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_node_compat_fn, edge_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_edge_compat_fn, &callback_data); } if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } if (retval) { igraphmodule_handle_igraph_error(); return NULL; } if (!map12 && !map21) { if (result) Py_RETURN_TRUE; Py_RETURN_FALSE; } else { PyObject *m1, *m2; if (map12) { m1 = igraphmodule_vector_t_to_PyList(map12, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(map12); if (!m1) { if (map21) igraph_vector_destroy(map21); return NULL; } } else { m1 = Py_None; Py_INCREF(m1); } if (map21) { m2 = igraphmodule_vector_t_to_PyList(map21, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(map21); if (!m2) { Py_DECREF(m1); return NULL; } } else { m2 = Py_None; Py_INCREF(m2); } return Py_BuildValue("ONN", result ? Py_True : Py_False, m1, m2); } } /** \ingroup python_interface_graph * \brief Counts the number of isomorphisms of two given graphs * * The actual code is almost the same as igraphmodule_Graph_count_subisomorphisms. * Make sure you correct bugs in both interfaces if applicable! * * \sa igraph_count_isomorphisms_vf2 */ PyObject *igraphmodule_Graph_count_isomorphisms_vf2(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { igraph_integer_t result = 0; PyObject *o = Py_None; PyObject *color1_o=Py_None, *color2_o=Py_None; PyObject *edge_color1_o=Py_None, *edge_color2_o=Py_None; PyObject *node_compat_fn=Py_None, *edge_compat_fn=Py_None; igraphmodule_GraphObject *other; igraph_vector_int_t *color1=0, *color2=0; igraph_vector_int_t *edge_color1=0, *edge_color2=0; igraphmodule_i_Graph_isomorphic_vf2_callback_data_t callback_data; static char *kwlist[] = { "other", "color1", "color2", "edge_color1", "edge_color2", "node_compat_fn", "edge_compat_fn", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwds, "|O!OOOOOO", kwlist, &igraphmodule_GraphType, &o, &color1_o, &color2_o, &edge_color1_o, &edge_color2_o, &node_compat_fn, &edge_compat_fn)) return NULL; if (o == Py_None) other=self; else other=(igraphmodule_GraphObject*)o; if (node_compat_fn != Py_None && !PyCallable_Check(node_compat_fn)) { PyErr_SetString(PyExc_TypeError, "node_compat_fn must be None or callable"); return NULL; } if (edge_compat_fn != Py_None && !PyCallable_Check(edge_compat_fn)) { PyErr_SetString(PyExc_TypeError, "edge_compat_fn must be None or callable"); return NULL; } if (igraphmodule_attrib_to_vector_int_t(color1_o, self, &color1, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraphmodule_attrib_to_vector_int_t(color2_o, other, &color2, ATTRIBUTE_TYPE_VERTEX)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color1_o, self, &edge_color1, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color2_o, other, &edge_color2, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } return NULL; } callback_data.graph1 = (PyObject*)self; callback_data.graph2 = (PyObject*)other; callback_data.callback_fn = 0; callback_data.node_compat_fn = node_compat_fn == Py_None ? 0 : node_compat_fn; callback_data.edge_compat_fn = edge_compat_fn == Py_None ? 0 : edge_compat_fn; if (igraph_count_isomorphisms_vf2(&self->g, &other->g, color1, color2, edge_color1, edge_color2, &result, node_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_node_compat_fn, edge_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_edge_compat_fn, &callback_data)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } igraphmodule_handle_igraph_error(); return NULL; } if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } return Py_BuildValue("l", (long)result); } /** \ingroup python_interface_graph * \brief Returns all isomorphisms of two given graphs * * The actual code is almost the same as igraphmodule_Graph_get_subisomorphisms. * Make sure you correct bugs in both interfaces if applicable! * * \sa igraph_get_isomorphisms_vf2 */ PyObject *igraphmodule_Graph_get_isomorphisms_vf2(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { igraph_vector_ptr_t result; PyObject *o = Py_None; PyObject *color1_o = Py_None, *color2_o = Py_None; PyObject *edge_color1_o=Py_None, *edge_color2_o=Py_None; PyObject *node_compat_fn=Py_None, *edge_compat_fn=Py_None; PyObject *res; igraphmodule_GraphObject *other; igraph_vector_int_t *color1=0, *color2=0; igraph_vector_int_t *edge_color1=0, *edge_color2=0; igraphmodule_i_Graph_isomorphic_vf2_callback_data_t callback_data; static char *kwlist[] = { "other", "color1", "color2", "edge_color1", "edge_color2", "node_compat_fn", "edge_compat_fn", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwds, "|O!OOOOOO", kwlist, &igraphmodule_GraphType, &o, &color1_o, &color2_o, &edge_color1_o, &edge_color2_o, &node_compat_fn, &edge_compat_fn)) return NULL; if (o == Py_None) other=self; else other=(igraphmodule_GraphObject*)o; if (node_compat_fn != Py_None && !PyCallable_Check(node_compat_fn)) { PyErr_SetString(PyExc_TypeError, "node_compat_fn must be None or callable"); return NULL; } if (edge_compat_fn != Py_None && !PyCallable_Check(edge_compat_fn)) { PyErr_SetString(PyExc_TypeError, "edge_compat_fn must be None or callable"); return NULL; } if (igraphmodule_attrib_to_vector_int_t(color1_o, self, &color1, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraphmodule_attrib_to_vector_int_t(color2_o, other, &color2, ATTRIBUTE_TYPE_VERTEX)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color1_o, self, &edge_color1, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color2_o, other, &edge_color2, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } return NULL; } if (igraph_vector_ptr_init(&result, 0)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } return igraphmodule_handle_igraph_error(); } callback_data.graph1 = (PyObject*)self; callback_data.graph2 = (PyObject*)other; callback_data.callback_fn = 0; callback_data.node_compat_fn = node_compat_fn == Py_None ? 0 : node_compat_fn; callback_data.edge_compat_fn = edge_compat_fn == Py_None ? 0 : edge_compat_fn; if (igraph_get_isomorphisms_vf2(&self->g, &other->g, color1, color2, edge_color1, edge_color2, &result, node_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_node_compat_fn, edge_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_edge_compat_fn, &callback_data)) { igraphmodule_handle_igraph_error(); if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } igraph_vector_ptr_destroy(&result); return NULL; } if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } res = igraphmodule_vector_ptr_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&result, igraph_vector_destroy); igraph_vector_ptr_destroy_all(&result); return res; } /** \ingroup python_interface_graph * \brief Determines whether a subgraph of the graph is isomorphic to another graph * using the VF2 algorithm. * * \sa igraph_subisomorphic_vf2 */ PyObject *igraphmodule_Graph_subisomorphic_vf2(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { igraph_bool_t result = 0; PyObject *o, *return1=Py_False, *return2=Py_False; PyObject *color1_o=Py_None, *color2_o=Py_None; PyObject *edge_color1_o=Py_None, *edge_color2_o=Py_None; PyObject *callback_fn=Py_None; PyObject *node_compat_fn=Py_None, *edge_compat_fn=Py_None; igraphmodule_GraphObject *other; igraph_vector_t mapping_12, mapping_21, *map12=0, *map21=0; igraph_vector_int_t *color1=0, *color2=0; igraph_vector_int_t *edge_color1=0, *edge_color2=0; igraphmodule_i_Graph_isomorphic_vf2_callback_data_t callback_data; int retval; static char *kwlist[] = { "other", "color1", "color2", "edge_color1", "edge_color2", "return_mapping_12", "return_mapping_21", "callback", "node_compat_fn", "edge_compat_fn", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwds, "O!|OOOOOOOOO", kwlist, &igraphmodule_GraphType, &o, &color1_o, &color2_o, &edge_color1_o, &edge_color2_o, &return1, &return2, &callback_fn, &node_compat_fn, &edge_compat_fn)) return NULL; other=(igraphmodule_GraphObject*)o; if (callback_fn != Py_None && !PyCallable_Check(callback_fn)) { PyErr_SetString(PyExc_TypeError, "callback must be None or callable"); return NULL; } if (node_compat_fn != Py_None && !PyCallable_Check(node_compat_fn)) { PyErr_SetString(PyExc_TypeError, "node_compat_fn must be None or callable"); return NULL; } if (edge_compat_fn != Py_None && !PyCallable_Check(edge_compat_fn)) { PyErr_SetString(PyExc_TypeError, "edge_compat_fn must be None or callable"); return NULL; } if (igraphmodule_attrib_to_vector_int_t(color1_o, self, &color1, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraphmodule_attrib_to_vector_int_t(color2_o, other, &color2, ATTRIBUTE_TYPE_VERTEX)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color1_o, self, &edge_color1, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color2_o, other, &edge_color2, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } return NULL; } if (PyObject_IsTrue(return1)) { igraph_vector_init(&mapping_12, 0); map12 = &mapping_12; } if (PyObject_IsTrue(return2)) { igraph_vector_init(&mapping_21, 0); map21 = &mapping_21; } callback_data.graph1 = (PyObject*)self; callback_data.graph2 = (PyObject*)other; callback_data.callback_fn = callback_fn == Py_None ? 0 : callback_fn; callback_data.node_compat_fn = node_compat_fn == Py_None ? 0 : node_compat_fn; callback_data.edge_compat_fn = edge_compat_fn == Py_None ? 0 : edge_compat_fn; if (callback_data.callback_fn == 0) { retval = igraph_subisomorphic_vf2(&self->g, &other->g, color1, color2, edge_color1, edge_color2, &result, map12, map21, node_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_node_compat_fn, edge_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_edge_compat_fn, &callback_data); } else { retval = igraph_subisomorphic_function_vf2(&self->g, &other->g, color1, color2, edge_color1, edge_color2, map12, map21, igraphmodule_i_Graph_isomorphic_vf2_callback_fn, node_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_node_compat_fn, edge_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_edge_compat_fn, &callback_data); } if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } if (retval) { igraphmodule_handle_igraph_error(); return NULL; } if (!map12 && !map21) { if (result) Py_RETURN_TRUE; Py_RETURN_FALSE; } else { PyObject *m1, *m2; if (map12) { m1 = igraphmodule_vector_t_to_PyList(map12, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(map12); if (!m1) { if (map21) igraph_vector_destroy(map21); return NULL; } } else { m1 = Py_None; Py_INCREF(m1); } if (map21) { m2 = igraphmodule_vector_t_to_PyList(map21, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(map21); if (!m2) { Py_DECREF(m1); return NULL; } } else { m2 = Py_None; Py_INCREF(m2); } return Py_BuildValue("ONN", result ? Py_True : Py_False, m1, m2); } } /** \ingroup python_interface_graph * \brief Counts the number of subisomorphisms of two given graphs * * The actual code is almost the same as igraphmodule_Graph_count_isomorphisms. * Make sure you correct bugs in both interfaces if applicable! * * \sa igraph_count_subisomorphisms_vf2 */ PyObject *igraphmodule_Graph_count_subisomorphisms_vf2(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { igraph_integer_t result = 0; PyObject *o = Py_None; PyObject *color1_o = Py_None, *color2_o = Py_None; PyObject *edge_color1_o=Py_None, *edge_color2_o=Py_None; PyObject *node_compat_fn=Py_None, *edge_compat_fn=Py_None; igraph_vector_int_t *color1=0, *color2=0; igraph_vector_int_t *edge_color1=0, *edge_color2=0; igraphmodule_GraphObject *other; igraphmodule_i_Graph_isomorphic_vf2_callback_data_t callback_data; static char *kwlist[] = { "other", "color1", "color2", "edge_color1", "edge_color2", "node_compat_fn", "edge_compat_fn", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwds, "O!|OOOOOO", kwlist, &igraphmodule_GraphType, &o, &color1_o, &color2_o, &edge_color1_o, &edge_color2_o, &node_compat_fn, &edge_compat_fn)) return NULL; other=(igraphmodule_GraphObject*)o; if (node_compat_fn != Py_None && !PyCallable_Check(node_compat_fn)) { PyErr_SetString(PyExc_TypeError, "node_compat_fn must be None or callable"); return NULL; } if (edge_compat_fn != Py_None && !PyCallable_Check(edge_compat_fn)) { PyErr_SetString(PyExc_TypeError, "edge_compat_fn must be None or callable"); return NULL; } if (igraphmodule_attrib_to_vector_int_t(color1_o, self, &color1, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraphmodule_attrib_to_vector_int_t(color2_o, other, &color2, ATTRIBUTE_TYPE_VERTEX)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color1_o, self, &edge_color1, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color2_o, other, &edge_color2, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } return NULL; } callback_data.graph1 = (PyObject*)self; callback_data.graph2 = (PyObject*)other; callback_data.callback_fn = 0; callback_data.node_compat_fn = node_compat_fn == Py_None ? 0 : node_compat_fn; callback_data.edge_compat_fn = edge_compat_fn == Py_None ? 0 : edge_compat_fn; if (igraph_count_subisomorphisms_vf2(&self->g, &other->g, color1, color2, edge_color1, edge_color2, &result, node_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_node_compat_fn, edge_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_edge_compat_fn, &callback_data)) { igraphmodule_handle_igraph_error(); if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } return NULL; } if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } return Py_BuildValue("l", (long)result); } /** \ingroup python_interface_graph * \brief Returns all subisomorphisms of two given graphs * * The actual code is almost the same as igraphmodule_Graph_get_isomorphisms. * Make sure you correct bugs in both interfaces if applicable! * * \sa igraph_get_isomorphisms_vf2 */ PyObject *igraphmodule_Graph_get_subisomorphisms_vf2(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { igraph_vector_ptr_t result; PyObject *o; PyObject *color1_o=Py_None, *color2_o=Py_None; PyObject *edge_color1_o=Py_None, *edge_color2_o=Py_None; PyObject *node_compat_fn=Py_None, *edge_compat_fn=Py_None; PyObject *res; igraphmodule_GraphObject *other; igraph_vector_int_t *color1=0, *color2=0; igraph_vector_int_t *edge_color1=0, *edge_color2=0; igraphmodule_i_Graph_isomorphic_vf2_callback_data_t callback_data; static char *kwlist[] = { "other", "color1", "color2", "edge_color1", "edge_color2", "node_compat_fn", "edge_compat_fn", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwds, "O!|OOOOOO", kwlist, &igraphmodule_GraphType, &o, &color1_o, &color2_o, &edge_color1_o, &edge_color2_o, &node_compat_fn, &edge_compat_fn)) return NULL; if (igraph_vector_ptr_init(&result, 0)) { return igraphmodule_handle_igraph_error(); } other=(igraphmodule_GraphObject*)o; if (node_compat_fn != Py_None && !PyCallable_Check(node_compat_fn)) { PyErr_SetString(PyExc_TypeError, "node_compat_fn must be None or callable"); return NULL; } if (edge_compat_fn != Py_None && !PyCallable_Check(edge_compat_fn)) { PyErr_SetString(PyExc_TypeError, "edge_compat_fn must be None or callable"); return NULL; } if (igraphmodule_attrib_to_vector_int_t(color1_o, self, &color1, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraphmodule_attrib_to_vector_int_t(color2_o, other, &color2, ATTRIBUTE_TYPE_VERTEX)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color1_o, self, &edge_color1, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } return NULL; } if (igraphmodule_attrib_to_vector_int_t(edge_color2_o, other, &edge_color2, ATTRIBUTE_TYPE_EDGE)) { if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } return NULL; } callback_data.graph1 = (PyObject*)self; callback_data.graph2 = (PyObject*)other; callback_data.callback_fn = 0; callback_data.node_compat_fn = node_compat_fn == Py_None ? 0 : node_compat_fn; callback_data.edge_compat_fn = edge_compat_fn == Py_None ? 0 : edge_compat_fn; if (igraph_get_subisomorphisms_vf2(&self->g, &other->g, color1, color2, edge_color1, edge_color2, &result, node_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_node_compat_fn, edge_compat_fn == Py_None ? 0 : igraphmodule_i_Graph_isomorphic_vf2_edge_compat_fn, &callback_data)) { igraphmodule_handle_igraph_error(); if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } igraph_vector_ptr_destroy(&result); return NULL; } if (color1) { igraph_vector_int_destroy(color1); free(color1); } if (color2) { igraph_vector_int_destroy(color2); free(color2); } if (edge_color1) { igraph_vector_int_destroy(edge_color1); free(edge_color1); } if (edge_color2) { igraph_vector_int_destroy(edge_color2); free(edge_color2); } res = igraphmodule_vector_ptr_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&result, igraph_vector_destroy); igraph_vector_ptr_destroy_all(&result); return res; } /** \ingroup python_interface_graph * \brief Determines whether a subgraph of the graph is isomorphic to another graph * using the LAD algorithm. * * \sa igraph_subisomorphic_lad */ PyObject *igraphmodule_Graph_subisomorphic_lad(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { igraph_bool_t result = 0; PyObject *o, *return_mapping=Py_False, *domains_o=Py_None, *induced=Py_False; float time_limit = 0; igraphmodule_GraphObject *other; igraph_vector_ptr_t domains; igraph_vector_ptr_t* p_domains = 0; igraph_vector_t mapping, *map=0; static char *kwlist[] = { "pattern", "domains", "induced", "time_limit", "return_mapping", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OOfO", kwlist, &igraphmodule_GraphType, &o, &domains_o, &induced, &time_limit, &return_mapping)) return NULL; other=(igraphmodule_GraphObject*)o; if (domains_o != Py_None) { if (igraphmodule_PyObject_to_vector_ptr_t(domains_o, &domains, 1)) return NULL; p_domains = &domains; } if (PyObject_IsTrue(return_mapping)) { if (igraph_vector_init(&mapping, 0)) { if (p_domains) igraph_vector_ptr_destroy_all(p_domains); igraphmodule_handle_igraph_error(); return NULL; } map = &mapping; } if (igraph_subisomorphic_lad(&other->g, &self->g, p_domains, &result, map, 0, PyObject_IsTrue(induced), (int)time_limit)) { if (p_domains) igraph_vector_ptr_destroy_all(p_domains); igraphmodule_handle_igraph_error(); return NULL; } if (p_domains) igraph_vector_ptr_destroy_all(p_domains); if (!map) { if (result) Py_RETURN_TRUE; Py_RETURN_FALSE; } else { PyObject *m = igraphmodule_vector_t_to_PyList(map, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(map); if (!m) return NULL; return Py_BuildValue("ON", result ? Py_True : Py_False, m); } } /** \ingroup python_interface_graph * \brief Finds all the subisomorphisms of a graph to another graph using the LAD * algorithm * * \sa igraph_subisomorphic_lad */ PyObject *igraphmodule_Graph_get_subisomorphisms_lad( igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject *o, *domains_o=Py_None, *induced=Py_False, *result; float time_limit = 0; igraphmodule_GraphObject *other; igraph_vector_ptr_t domains; igraph_vector_ptr_t* p_domains = 0; igraph_vector_ptr_t mappings; static char *kwlist[] = { "pattern", "domains", "induced", "time_limit", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OOf", kwlist, &igraphmodule_GraphType, &o, &domains_o, &induced, &time_limit)) return NULL; other=(igraphmodule_GraphObject*)o; if (domains_o != Py_None) { if (igraphmodule_PyObject_to_vector_ptr_t(domains_o, &domains, 1)) return NULL; p_domains = &domains; } if (igraph_vector_ptr_init(&mappings, 0)) { igraphmodule_handle_igraph_error(); if (p_domains) igraph_vector_ptr_destroy_all(p_domains); return NULL; } if (igraph_subisomorphic_lad(&other->g, &self->g, p_domains, 0, 0, &mappings, PyObject_IsTrue(induced), (int)time_limit)) { igraphmodule_handle_igraph_error(); igraph_vector_ptr_destroy_all(&mappings); if (p_domains) igraph_vector_ptr_destroy_all(p_domains); return NULL; } if (p_domains) igraph_vector_ptr_destroy_all(p_domains); result = igraphmodule_vector_ptr_t_to_PyList(&mappings, IGRAPHMODULE_TYPE_INT); igraph_vector_ptr_destroy_all(&mappings); return result; } /********************************************************************** * Graph attribute handling * **********************************************************************/ /** \ingroup python_interface_graph * \brief Returns the number of graph attributes */ Py_ssize_t igraphmodule_Graph_attribute_count(igraphmodule_GraphObject * self) { return PyDict_Size(((PyObject **) self->g.attr)[ATTRHASH_IDX_GRAPH]); } /** \ingroup python_interface_graph * \brief Handles the subscript operator on the graph. * * When the subscript is a string, returns the corresponding value of the * given attribute in the graph. When the subscript is a tuple of length * 2, retrieves the adjacency matrix representation of the graph between * some vertices. */ PyObject *igraphmodule_Graph_mp_subscript(igraphmodule_GraphObject * self, PyObject * s) { PyObject *result = 0; if (PyTuple_Check(s) && PyTuple_Size(s) >= 2) { /* Adjacency matrix representation */ PyObject *ri = PyTuple_GET_ITEM(s, 0); PyObject *ci = PyTuple_GET_ITEM(s, 1); PyObject *attr; if (PyTuple_Size(s) == 2) { attr = 0; } else if (PyTuple_Size(s) == 3) { attr = PyTuple_GET_ITEM(s, 2); } else { PyErr_SetString(PyExc_TypeError, "adjacency matrix indexing must use at most three arguments"); return 0; } return igraphmodule_Graph_adjmatrix_get_index(&self->g, ri, ci, attr); } /* Ordinary attribute retrieval */ result = PyDict_GetItem(ATTR_STRUCT_DICT(&self->g)[ATTRHASH_IDX_GRAPH], s); if (result) { Py_INCREF(result); return result; } /* result is NULL, check whether there was an error */ if (!PyErr_Occurred()) PyErr_SetString(PyExc_KeyError, "Attribute does not exist"); return NULL; } /** \ingroup python_interface_graph * \brief Handles the subscript assignment operator on the graph. * * If k is a string, sets the value of the corresponding attribute of the graph. * If k is a tuple of length 2, sets part of the adjacency matrix. * * \return 0 if everything's ok, -1 in case of error */ int igraphmodule_Graph_mp_assign_subscript(igraphmodule_GraphObject * self, PyObject * k, PyObject * v) { PyObject* dict = ATTR_STRUCT_DICT(&self->g)[ATTRHASH_IDX_GRAPH]; if (PyTuple_Check(k) && PyTuple_Size(k) >= 2) { /* Adjacency matrix representation */ PyObject *ri, *ci, *attr; if (v == NULL) { PyErr_SetString(PyExc_NotImplementedError, "cannot delete parts " "of the adjacency matrix of a graph"); return -1; } ri = PyTuple_GET_ITEM(k, 0); ci = PyTuple_GET_ITEM(k, 1); if (PyTuple_Size(k) == 2) { attr = 0; } else if (PyTuple_Size(k) == 3) { attr = PyTuple_GET_ITEM(k, 2); } else { PyErr_SetString(PyExc_TypeError, "adjacency matrix indexing must use at most three arguments"); return 0; } return igraphmodule_Graph_adjmatrix_set_index(&self->g, ri, ci, attr, v); } /* Ordinary attribute setting/deletion */ if (v == NULL) return PyDict_DelItem(dict, k); if (PyDict_SetItem(dict, k, v) == -1) return -1; return 0; } /** \ingroup python_interface_graph * \brief Returns the attribute list of the graph */ PyObject *igraphmodule_Graph_attributes(igraphmodule_GraphObject * self) { return PyDict_Keys(ATTR_STRUCT_DICT(&self->g)[ATTRHASH_IDX_GRAPH]); } /** \ingroup python_interface_graph * \brief Returns the attribute list of the graph's vertices */ PyObject *igraphmodule_Graph_vertex_attributes(igraphmodule_GraphObject * self) { return PyDict_Keys(ATTR_STRUCT_DICT(&self->g)[ATTRHASH_IDX_VERTEX]); } /** \ingroup python_interface_graph * \brief Returns the attribute list of the graph's edges */ PyObject *igraphmodule_Graph_edge_attributes(igraphmodule_GraphObject * self) { return PyDict_Keys(ATTR_STRUCT_DICT(&self->g)[ATTRHASH_IDX_EDGE]); } /********************************************************************** * Graph operations (union, intersection etc) * **********************************************************************/ /** \ingroup python_interface_graph * \brief Creates the disjoint union of two graphs (operator version) */ PyObject *igraphmodule_Graph_disjoint_union(igraphmodule_GraphObject * self, PyObject * other) { PyObject *it; igraphmodule_GraphObject *o, *result; igraph_t g; /* Did we receive an iterable? */ it = PyObject_GetIter(other); if (it) { /* Get all elements, store the graphs in an igraph_vector_ptr */ igraph_vector_ptr_t gs; if (igraph_vector_ptr_init(&gs, 0)) { Py_DECREF(it); return igraphmodule_handle_igraph_error(); } if (igraph_vector_ptr_push_back(&gs, &self->g)) { Py_DECREF(it); igraph_vector_ptr_destroy(&gs); return igraphmodule_handle_igraph_error(); } if (igraphmodule_append_PyIter_of_graphs_to_vector_ptr_t(it, &gs)) { igraph_vector_ptr_destroy(&gs); Py_DECREF(it); return NULL; } Py_DECREF(it); /* Create disjoint union */ if (igraph_disjoint_union_many(&g, &gs)) { igraph_vector_ptr_destroy(&gs); return igraphmodule_handle_igraph_error(); } igraph_vector_ptr_destroy(&gs); } else { PyErr_Clear(); if (!PyObject_TypeCheck(other, &igraphmodule_GraphType)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } o = (igraphmodule_GraphObject *) other; if (igraph_disjoint_union(&g, &self->g, &o->g)) { igraphmodule_handle_igraph_error(); return NULL; } } /* this is correct as long as attributes are not copied by the * operator. if they are copied, the initialization should not empty * the attribute hashes */ CREATE_GRAPH(result, g); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Creates the union of two graphs (operator version) */ PyObject *igraphmodule_Graph_union(igraphmodule_GraphObject * self, PyObject * other) { PyObject *it; igraphmodule_GraphObject *o, *result; igraph_t g; /* Did we receive an iterable? */ it = PyObject_GetIter(other); if (it) { /* Get all elements, store the graphs in an igraph_vector_ptr */ igraph_vector_ptr_t gs; if (igraph_vector_ptr_init(&gs, 0)) { Py_DECREF(it); return igraphmodule_handle_igraph_error(); } if (igraph_vector_ptr_push_back(&gs, &self->g)) { Py_DECREF(it); igraph_vector_ptr_destroy(&gs); return igraphmodule_handle_igraph_error(); } if (igraphmodule_append_PyIter_of_graphs_to_vector_ptr_t(it, &gs)) { Py_DECREF(it); igraph_vector_ptr_destroy(&gs); return NULL; } Py_DECREF(it); /* Create union */ if (igraph_union_many(&g, &gs, /*edgemaps=*/ 0)) { igraph_vector_ptr_destroy(&gs); igraphmodule_handle_igraph_error(); return NULL; } igraph_vector_ptr_destroy(&gs); } else { PyErr_Clear(); if (!PyObject_TypeCheck(other, &igraphmodule_GraphType)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } o = (igraphmodule_GraphObject *) other; if (igraph_union(&g, &self->g, &o->g, /*edge_map1=*/ 0, /*edge_map2=*/ 0)) { igraphmodule_handle_igraph_error(); return NULL; } } /* this is correct as long as attributes are not copied by the * operator. if they are copied, the initialization should not empty * the attribute hashes */ CREATE_GRAPH(result, g); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Creates the intersection of two graphs (operator version) */ PyObject *igraphmodule_Graph_intersection(igraphmodule_GraphObject * self, PyObject * other) { PyObject *it; igraphmodule_GraphObject *o, *result; igraph_t g; /* Did we receive an iterable? */ it = PyObject_GetIter(other); if (it) { /* Get all elements, store the graphs in an igraph_vector_ptr */ igraph_vector_ptr_t gs; if (igraph_vector_ptr_init(&gs, 0)) { Py_DECREF(it); return igraphmodule_handle_igraph_error(); } if (igraph_vector_ptr_push_back(&gs, &self->g)) { Py_DECREF(it); igraph_vector_ptr_destroy(&gs); return igraphmodule_handle_igraph_error(); } if (igraphmodule_append_PyIter_of_graphs_to_vector_ptr_t(it, &gs)) { Py_DECREF(it); igraph_vector_ptr_destroy(&gs); return NULL; } Py_DECREF(it); /* Create union */ if (igraph_intersection_many(&g, &gs, /*edgemaps=*/ 0)) { igraph_vector_ptr_destroy(&gs); igraphmodule_handle_igraph_error(); return NULL; } igraph_vector_ptr_destroy(&gs); } else { PyErr_Clear(); if (!PyObject_TypeCheck(other, &igraphmodule_GraphType)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } o = (igraphmodule_GraphObject *) other; if (igraph_intersection(&g, &self->g, &o->g, /*edge_map1=*/ 0, /*edge_map2=*/ 0)) { igraphmodule_handle_igraph_error(); return NULL; } } /* this is correct as long as attributes are not copied by the * operator. if they are copied, the initialization should not empty * the attribute hashes */ CREATE_GRAPH(result, g); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Creates the difference of two graphs (operator version) */ PyObject *igraphmodule_Graph_difference(igraphmodule_GraphObject * self, PyObject * other) { igraphmodule_GraphObject *o, *result; igraph_t g; if (!PyObject_TypeCheck(other, &igraphmodule_GraphType)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } o = (igraphmodule_GraphObject *) other; if (igraph_difference(&g, &self->g, &o->g)) { igraphmodule_handle_igraph_error(); return NULL; } /* this is correct as long as attributes are not copied by the * operator. if they are copied, the initialization should not empty * the attribute hashes */ CREATE_GRAPH(result, g); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Creates the complementer of a graph */ PyObject *igraphmodule_Graph_complementer(igraphmodule_GraphObject * self, PyObject * args) { igraphmodule_GraphObject *result; PyObject *o = Py_True; igraph_t g; if (!PyArg_ParseTuple(args, "|O", &o)) return NULL; if (igraph_complementer(&g, &self->g, PyObject_IsTrue(o))) { igraphmodule_handle_igraph_error(); return NULL; } /* this is correct as long as attributes are not copied by the * operator. if they are copied, the initialization should not empty * the attribute hashes */ CREATE_GRAPH(result, g); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Creates the complementer of a graph (operator version) */ PyObject *igraphmodule_Graph_complementer_op(igraphmodule_GraphObject * self) { igraphmodule_GraphObject *result; igraph_t g; if (igraph_complementer(&g, &self->g, 0)) { igraphmodule_handle_igraph_error(); return NULL; } /* this is correct as long as attributes are not copied by the * operator. if they are copied, the initialization should not empty * the attribute hashes */ CREATE_GRAPH(result, g); return (PyObject *) result; } /** \ingroup python_interface_graph * \brief Creates the composition of two graphs */ PyObject *igraphmodule_Graph_compose(igraphmodule_GraphObject * self, PyObject * other) { igraphmodule_GraphObject *o, *result; igraph_t g; if (!PyObject_TypeCheck(other, &igraphmodule_GraphType)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } o = (igraphmodule_GraphObject *) other; if (igraph_compose(&g, &self->g, &o->g, /*edge_map1=*/ 0, /*edge_map2=*/ 0)) { igraphmodule_handle_igraph_error(); return NULL; } /* this is correct as long as attributes are not copied by the * operator. if they are copied, the initialization should not empty * the attribute hashes */ CREATE_GRAPH(result, g); return (PyObject *) result; } /********************************************************************** * Graph traversal algorithms * **********************************************************************/ /** \ingroup python_interface_graph * \brief Conducts a breadth first search (BFS) on the graph */ PyObject *igraphmodule_Graph_bfs(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "vid", "mode", NULL }; long vid; PyObject *l1, *l2, *l3, *result, *mode_o=Py_None; igraph_neimode_t mode = IGRAPH_OUT; igraph_vector_t vids; igraph_vector_t layers; igraph_vector_t parents; if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|O", kwlist, &vid, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraph_vector_init(&vids, igraph_vcount(&self->g))) return igraphmodule_handle_igraph_error(); if (igraph_vector_init(&layers, igraph_vcount(&self->g))) { igraph_vector_destroy(&vids); return igraphmodule_handle_igraph_error(); } if (igraph_vector_init(&parents, igraph_vcount(&self->g))) { igraph_vector_destroy(&vids); igraph_vector_destroy(&parents); return igraphmodule_handle_igraph_error(); } if (igraph_i_bfs (&self->g, (igraph_integer_t) vid, mode, &vids, &layers, &parents)) { igraphmodule_handle_igraph_error(); return NULL; } l1 = igraphmodule_vector_t_to_PyList(&vids, IGRAPHMODULE_TYPE_INT); l2 = igraphmodule_vector_t_to_PyList(&layers, IGRAPHMODULE_TYPE_INT); l3 = igraphmodule_vector_t_to_PyList(&parents, IGRAPHMODULE_TYPE_INT); if (l1 && l2 && l3) { result = Py_BuildValue("NNN", l1, l2, l3); /* references stolen */ } else { if (l1) { Py_DECREF(l1); } if (l2) { Py_DECREF(l2); } if (l3) { Py_DECREF(l3); } result = NULL; } igraph_vector_destroy(&vids); igraph_vector_destroy(&layers); igraph_vector_destroy(&parents); return result; } /** \ingroup python_interface_graph * \brief Constructs a breadth first search (BFS) iterator of the graph */ PyObject *igraphmodule_Graph_bfsiter(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { char *kwlist[] = { "vid", "mode", "advanced", NULL }; PyObject *root, *adv = Py_False, *mode_o = Py_None; igraph_neimode_t mode = IGRAPH_OUT; if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|OO", kwlist, &root, &mode_o, &adv)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; return igraphmodule_BFSIter_new(self, root, mode, PyObject_IsTrue(adv)); } /** \ingroup python_interface_graph * \brief Unfolds a graph into a tree using BFS */ PyObject *igraphmodule_Graph_unfold_tree(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "roots", "mode", NULL }; igraphmodule_GraphObject *result_o; PyObject *mapping_o, *mode_o=Py_None, *roots_o=Py_None; igraph_neimode_t mode = IGRAPH_OUT; igraph_vs_t vs; igraph_vector_t mapping, vids; igraph_t result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &roots_o, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraphmodule_PyObject_to_vs_t(roots_o, &vs, &self->g, 0, 0)) return NULL; if (igraph_vector_init(&mapping, igraph_vcount(&self->g))) { igraph_vs_destroy(&vs); return igraphmodule_handle_igraph_error(); } if (igraph_vector_init(&vids, 0)) { igraph_vs_destroy(&vs); igraph_vector_destroy(&mapping); return igraphmodule_handle_igraph_error(); } if (igraph_vs_as_vector(&self->g, vs, &vids)) { igraph_vs_destroy(&vs); igraph_vector_destroy(&vids); igraph_vector_destroy(&mapping); return igraphmodule_handle_igraph_error(); } igraph_vs_destroy(&vs); if (igraph_unfold_tree(&self->g, &result, mode, &vids, &mapping)) { igraph_vector_destroy(&vids); igraph_vector_destroy(&mapping); igraphmodule_handle_igraph_error(); return NULL; } igraph_vector_destroy(&vids); mapping_o = igraphmodule_vector_t_to_PyList(&mapping, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&mapping); if (!mapping_o) { igraph_destroy(&result); return NULL; } CREATE_GRAPH(result_o, result); return Py_BuildValue("NN", result_o, mapping_o); } /********************************************************************** * Maximum flows * **********************************************************************/ /** \ingroup python_interface_graph * \brief Calculates the value of the maximum flow in the graph */ PyObject *igraphmodule_Graph_maxflow_value(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "source", "target", "capacity", NULL }; PyObject *capacity_object = Py_None; igraph_vector_t capacity_vector; igraph_real_t result; long int vid1 = -1, vid2 = -1; igraph_integer_t v1, v2; igraph_maxflow_stats_t stats; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll|O", kwlist, &vid1, &vid2, &capacity_object)) return NULL; v1 = (igraph_integer_t) vid1; v2 = (igraph_integer_t) vid2; if (igraphmodule_PyObject_to_attribute_values(capacity_object, &capacity_vector, self, ATTRHASH_IDX_EDGE, 1.0)) return igraphmodule_handle_igraph_error(); if (igraph_maxflow_value(&self->g, &result, v1, v2, &capacity_vector, &stats)) { igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } igraph_vector_destroy(&capacity_vector); return Py_BuildValue("d", (double)result); } /** \ingroup python_interface_graph * \brief Calculates the maximum flow of the graph */ PyObject *igraphmodule_Graph_maxflow(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "source", "target", "capacity", NULL }; PyObject *capacity_object = Py_None, *flow_o, *cut_o, *partition_o; igraph_vector_t capacity_vector; igraph_real_t result; long int vid1 = -1, vid2 = -1; igraph_integer_t v1, v2; igraph_vector_t flow, cut, partition; igraph_maxflow_stats_t stats; if (!PyArg_ParseTupleAndKeywords(args, kwds, "ll|O", kwlist, &vid1, &vid2, &capacity_object)) return NULL; v1 = (igraph_integer_t) vid1; v2 = (igraph_integer_t) vid2; if (igraphmodule_PyObject_to_attribute_values(capacity_object, &capacity_vector, self, ATTRHASH_IDX_EDGE, 1.0)) return igraphmodule_handle_igraph_error(); if (igraph_vector_init(&flow, 0)) { igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } if (igraph_vector_init(&cut, 0)) { igraph_vector_destroy(&capacity_vector); igraph_vector_destroy(&flow); return igraphmodule_handle_igraph_error(); } if (igraph_vector_init(&partition, 0)) { igraph_vector_destroy(&capacity_vector); igraph_vector_destroy(&flow); igraph_vector_destroy(&cut); return igraphmodule_handle_igraph_error(); } if (igraph_maxflow(&self->g, &result, &flow, &cut, &partition, 0, v1, v2, &capacity_vector, &stats)) { igraph_vector_destroy(&capacity_vector); igraph_vector_destroy(&flow); igraph_vector_destroy(&cut); igraph_vector_destroy(&partition); return igraphmodule_handle_igraph_error(); } igraph_vector_destroy(&capacity_vector); flow_o = igraphmodule_vector_t_to_PyList(&flow, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&flow); if (flow_o == NULL) { igraph_vector_destroy(&cut); igraph_vector_destroy(&partition); return NULL; } cut_o = igraphmodule_vector_t_to_PyList(&cut, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&cut); if (cut_o == NULL) { igraph_vector_destroy(&partition); return NULL; } partition_o = igraphmodule_vector_t_to_PyList(&partition, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&partition); if (partition_o == NULL) return NULL; return Py_BuildValue("dOOO", (double)result, flow_o, cut_o, partition_o); } /********************************************************************** * Minimum cuts (edge separators) * **********************************************************************/ /** \ingroup python_interface_graph * \brief Calculates all s-t cuts in a graph */ PyObject *igraphmodule_Graph_all_st_cuts(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "source", "target", NULL }; igraph_integer_t source, target; igraph_vector_ptr_t cuts, partition1s; PyObject *source_o, *target_o; PyObject *cuts_o, *partition1s_o; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &source_o, &target_o)) return NULL; if (igraphmodule_PyObject_to_vid(source_o, &source, &self->g)) return NULL; if (igraphmodule_PyObject_to_vid(target_o, &target, &self->g)) return NULL; if (igraph_vector_ptr_init(&partition1s, 0)) { return igraphmodule_handle_igraph_error(); } if (igraph_vector_ptr_init(&cuts, 0)) { igraph_vector_ptr_destroy(&partition1s); return igraphmodule_handle_igraph_error(); } if (igraph_all_st_cuts(&self->g, &cuts, &partition1s, source, target)) { igraph_vector_ptr_destroy(&cuts); igraph_vector_ptr_destroy(&partition1s); return igraphmodule_handle_igraph_error(); } IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&cuts, igraph_vector_destroy); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&partition1s, igraph_vector_destroy); cuts_o = igraphmodule_vector_ptr_t_to_PyList(&cuts, IGRAPHMODULE_TYPE_INT); igraph_vector_ptr_destroy_all(&cuts); if (cuts_o == NULL) { igraph_vector_ptr_destroy_all(&partition1s); return NULL; } partition1s_o = igraphmodule_vector_ptr_t_to_PyList(&partition1s, IGRAPHMODULE_TYPE_INT); igraph_vector_ptr_destroy_all(&partition1s); if (partition1s_o == NULL) return NULL; return Py_BuildValue("NN", cuts_o, partition1s_o); } /** \ingroup python_interface_graph * \brief Calculates all minimum s-t cuts in a graph */ PyObject *igraphmodule_Graph_all_st_mincuts(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "source", "target", "capacity", NULL }; igraph_integer_t source, target; igraph_real_t value; igraph_vector_ptr_t cuts, partition1s; igraph_vector_t capacity_vector; PyObject *source_o, *target_o, *capacity_o = Py_None; PyObject *cuts_o, *partition1s_o; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO", kwlist, &source_o, &target_o, &capacity_o)) return NULL; if (igraphmodule_PyObject_to_vid(source_o, &source, &self->g)) return NULL; if (igraphmodule_PyObject_to_vid(target_o, &target, &self->g)) return NULL; if (igraph_vector_ptr_init(&partition1s, 0)) { return igraphmodule_handle_igraph_error(); } if (igraph_vector_ptr_init(&cuts, 0)) { igraph_vector_ptr_destroy(&partition1s); return igraphmodule_handle_igraph_error(); } if (igraphmodule_PyObject_to_attribute_values(capacity_o, &capacity_vector, self, ATTRHASH_IDX_EDGE, 1.0)) { igraph_vector_ptr_destroy(&cuts); igraph_vector_ptr_destroy(&partition1s); return igraphmodule_handle_igraph_error(); } if (igraph_all_st_mincuts(&self->g, &value, &cuts, &partition1s, source, target, &capacity_vector)) { igraph_vector_ptr_destroy(&cuts); igraph_vector_ptr_destroy(&partition1s); igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } igraph_vector_destroy(&capacity_vector); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&cuts, igraph_vector_destroy); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&partition1s, igraph_vector_destroy); cuts_o = igraphmodule_vector_ptr_t_to_PyList(&cuts, IGRAPHMODULE_TYPE_INT); igraph_vector_ptr_destroy_all(&cuts); if (cuts_o == NULL) { igraph_vector_ptr_destroy_all(&partition1s); return NULL; } partition1s_o = igraphmodule_vector_ptr_t_to_PyList(&partition1s, IGRAPHMODULE_TYPE_INT); igraph_vector_ptr_destroy_all(&partition1s); if (partition1s_o == NULL) return NULL; return Py_BuildValue("dNN", (double)value, cuts_o, partition1s_o); } /** \ingroup python_interface_graph * \brief Calculates the value of the minimum cut in the graph */ PyObject *igraphmodule_Graph_mincut_value(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "source", "target", "capacity", NULL }; PyObject *capacity_object = Py_None; igraph_vector_t capacity_vector; igraph_real_t result, mincut; igraph_integer_t v1, v2; long vid1 = -1, vid2 = -1; long n; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|llO", kwlist, &vid1, &vid2, &capacity_object)) return NULL; if (igraphmodule_PyObject_to_attribute_values(capacity_object, &capacity_vector, self, ATTRHASH_IDX_EDGE, 1.0)) return igraphmodule_handle_igraph_error(); v1 = (igraph_integer_t) vid1; v2 = (igraph_integer_t) vid2; if (v1 == -1 && v2 == -1) { if (igraph_mincut_value(&self->g, &result, &capacity_vector)) { igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } } else if (v1 == -1) { n = igraph_vcount(&self->g); result = -1; for (v1 = 0; v1 < n; v1++) { if (v2 == v1) continue; if (igraph_st_mincut_value(&self->g, &mincut, v1, v2, &capacity_vector)) { igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } if (result < 0 || result > mincut) result = mincut; } if (result < 0) result = 0.0; } else if (v2 == -1) { n = igraph_vcount(&self->g); result = -1; for (v2 = 0; v2 < n; v2++) { if (v2 == v1) continue; if (igraph_st_mincut_value(&self->g, &mincut, v1, v2, &capacity_vector)) { igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } if (result < 0.0 || result > mincut) result = mincut; } if (result < 0) result = 0.0; } else { if (igraph_st_mincut_value(&self->g, &result, v1, v2, &capacity_vector)) { igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } } igraph_vector_destroy(&capacity_vector); return Py_BuildValue("d", (double)result); } /** \ingroup python_interface_graph * \brief Calculates a minimum cut in a graph */ PyObject *igraphmodule_Graph_mincut(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "source", "target", "capacity", NULL }; PyObject *capacity_object = Py_None, *cut_o, *part_o, *part2_o, *result; PyObject *source_o = Py_None, *target_o = Py_None; int retval; igraph_vector_t capacity_vector; igraph_real_t value; igraph_vector_t partition, partition2, cut; igraph_integer_t source = -1, target = -1; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &source_o, &target_o, &capacity_object)) return NULL; if (source_o != Py_None && igraphmodule_PyObject_to_vid(source_o, &source, &self->g)) return NULL; if (target_o != Py_None && igraphmodule_PyObject_to_vid(target_o, &target, &self->g)) return NULL; if (igraphmodule_PyObject_to_attribute_values(capacity_object, &capacity_vector, self, ATTRHASH_IDX_EDGE, 1.0)) return igraphmodule_handle_igraph_error(); if (igraph_vector_init(&partition, 0)) { igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } if (igraph_vector_init(&partition2, 0)) { igraph_vector_destroy(&partition); igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } if (igraph_vector_init(&cut, 0)) { igraph_vector_destroy(&partition); igraph_vector_destroy(&partition2); igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } if (source == -1 && target == -1) { retval = igraph_mincut(&self->g, &value, &partition, &partition2, &cut, &capacity_vector); } else if (source == -1 || target == -1) { retval = IGRAPH_UNIMPLEMENTED; PyErr_SetString(PyExc_ValueError, "if you specify one of 'source' and 'target', " "you must specify the other one as well"); } else { retval = igraph_st_mincut(&self->g, &value, &cut, &partition, &partition2, source, target, &capacity_vector); } if (retval) { igraph_vector_destroy(&cut); igraph_vector_destroy(&partition); igraph_vector_destroy(&partition2); igraph_vector_destroy(&capacity_vector); if (!PyErr_Occurred()) igraphmodule_handle_igraph_error(); return NULL; } igraph_vector_destroy(&capacity_vector); cut_o=igraphmodule_vector_t_to_PyList(&cut, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&cut); if (!cut_o) { igraph_vector_destroy(&partition); igraph_vector_destroy(&partition2); return 0; } part_o=igraphmodule_vector_t_to_PyList(&partition, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&partition); if (!part_o) { Py_DECREF(cut_o); igraph_vector_destroy(&partition2); return 0; } part2_o=igraphmodule_vector_t_to_PyList(&partition2, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&partition2); if (!part2_o) { Py_DECREF(part_o); Py_DECREF(cut_o); return 0; } result = Py_BuildValue("dNNN", (double)value, cut_o, part_o, part2_o); return result; } /** \ingroup python_interface_graph * \brief Calculates the Gomory-Hu tree of an undirected graph */ PyObject *igraphmodule_Graph_gomory_hu_tree(igraphmodule_GraphObject * self, PyObject *args, PyObject *kwds) { static char* kwlist[] = { "capacity", NULL }; igraph_vector_t capacity_vector; igraph_vector_t flow_vector; igraph_t tree; PyObject *capacity_o = Py_None; PyObject *flow_o; igraphmodule_GraphObject *tree_o; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &capacity_o)) return NULL; if (igraphmodule_PyObject_to_attribute_values(capacity_o, &capacity_vector, self, ATTRHASH_IDX_EDGE, 1.0)) return igraphmodule_handle_igraph_error(); if (igraph_vector_init(&flow_vector, 0)) { igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } if (igraph_gomory_hu_tree(&self->g, &tree, &flow_vector, &capacity_vector)) { igraph_vector_destroy(&flow_vector); igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } igraph_vector_destroy(&capacity_vector); flow_o = igraphmodule_vector_t_to_PyList(&flow_vector, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&flow_vector); if (!flow_o) { igraph_destroy(&tree); return 0; } CREATE_GRAPH(tree_o, tree); if (!tree_o) { igraph_destroy(&tree); return 0; } return Py_BuildValue("NN", tree_o, flow_o); } /** \ingroup python_interface_graph * \brief Calculates a minimum s-t cut in a graph */ PyObject *igraphmodule_Graph_st_mincut(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "source", "target", "capacity", NULL }; igraph_integer_t source, target; PyObject *cut_o, *part_o, *part2_o, *result; PyObject *source_o, *target_o, *capacity_o = Py_None; igraph_vector_t capacity_vector; igraph_real_t value; igraph_vector_t partition, partition2, cut; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO", kwlist, &source_o, &target_o, &capacity_o)) return NULL; if (igraphmodule_PyObject_to_vid(source_o, &source, &self->g)) return NULL; if (igraphmodule_PyObject_to_vid(target_o, &target, &self->g)) return NULL; if (igraphmodule_PyObject_to_attribute_values(capacity_o, &capacity_vector, self, ATTRHASH_IDX_EDGE, 1.0)) return igraphmodule_handle_igraph_error(); if (igraph_vector_init(&partition, 0)) { igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } if (igraph_vector_init(&partition2, 0)) { igraph_vector_destroy(&partition); igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } if (igraph_vector_init(&cut, 0)) { igraph_vector_destroy(&partition); igraph_vector_destroy(&partition2); igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } if (igraph_st_mincut(&self->g, &value, &cut, &partition, &partition2, source, target, &capacity_vector)) { igraph_vector_destroy(&cut); igraph_vector_destroy(&partition); igraph_vector_destroy(&partition2); igraph_vector_destroy(&capacity_vector); return igraphmodule_handle_igraph_error(); } igraph_vector_destroy(&capacity_vector); cut_o=igraphmodule_vector_t_to_PyList(&cut, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&cut); if (!cut_o) { igraph_vector_destroy(&partition); igraph_vector_destroy(&partition2); return NULL; } part_o=igraphmodule_vector_t_to_PyList(&partition, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&partition); if (!part_o) { Py_DECREF(cut_o); igraph_vector_destroy(&partition2); return NULL; } part2_o=igraphmodule_vector_t_to_PyList(&partition2, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&partition2); if (!part2_o) { Py_DECREF(part_o); Py_DECREF(cut_o); return NULL; } result = Py_BuildValue("dNNN", (double)value, cut_o, part_o, part2_o); return result; } /********************************************************************** * Vertex separators * **********************************************************************/ /** \ingroup python_interface_graph * \brief Returns all minimal s-t separators of a graph */ PyObject *igraphmodule_Graph_all_minimal_st_separators( igraphmodule_GraphObject * self) { PyObject* result_o; igraph_vector_ptr_t result; if (igraph_vector_ptr_init(&result, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_all_minimal_st_separators(&self->g, &result)) { igraphmodule_handle_igraph_error(); igraph_vector_ptr_destroy(&result); return NULL; } result_o = igraphmodule_vector_ptr_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&result, igraph_vector_destroy); igraph_vector_ptr_destroy_all(&result); return result_o; } /** \ingroup python_interface_graph * \brief Checks whether a given vertex set is a vertex separator */ PyObject *igraphmodule_Graph_is_separator(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject* list = Py_None; igraph_bool_t result; igraph_vs_t vs; static char *kwlist[] = { "vertices", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &list)) return NULL; if (igraphmodule_PyObject_to_vs_t(list, &vs, &self->g, 0, 0)) { return NULL; } if (igraph_is_separator(&self->g, vs, &result)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); return NULL; } igraph_vs_destroy(&vs); if (result) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /** \ingroup python_interface_graph * \brief Checks whether a given vertex set is a minimal vertex separator */ PyObject *igraphmodule_Graph_is_minimal_separator(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { PyObject* list = Py_None; igraph_bool_t result; igraph_vs_t vs; static char *kwlist[] = { "vertices", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &list)) return NULL; if (igraphmodule_PyObject_to_vs_t(list, &vs, &self->g, 0, 0)) { return NULL; } if (igraph_is_minimal_separator(&self->g, vs, &result)) { igraphmodule_handle_igraph_error(); igraph_vs_destroy(&vs); return NULL; } igraph_vs_destroy(&vs); if (result) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /** \ingroup python_interface_graph * \brief Returns the minimum size separators of the graph */ PyObject *igraphmodule_Graph_minimum_size_separators( igraphmodule_GraphObject * self) { PyObject* result_o; igraph_vector_ptr_t result; if (igraph_vector_ptr_init(&result, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_minimum_size_separators(&self->g, &result)) { igraphmodule_handle_igraph_error(); igraph_vector_ptr_destroy(&result); return NULL; } result_o = igraphmodule_vector_ptr_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&result, igraph_vector_destroy); igraph_vector_ptr_destroy_all(&result); return result_o; } /********************************************************************** * Cohesive blocks * **********************************************************************/ /** \ingroup python_interface_graph * \brief Calculates the cohesive block structure of a graph */ PyObject *igraphmodule_Graph_cohesive_blocks(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { PyObject *blocks_o, *cohesion_o, *parents_o, *result_o; igraph_vector_ptr_t blocks; igraph_vector_t cohesion, parents; if (igraph_vector_ptr_init(&blocks, 0)) { igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&cohesion, 0)) { igraph_vector_ptr_destroy(&blocks); igraphmodule_handle_igraph_error(); return NULL; } if (igraph_vector_init(&parents, 0)) { igraph_vector_ptr_destroy(&blocks); igraph_vector_destroy(&cohesion); igraphmodule_handle_igraph_error(); return NULL; } if (igraph_cohesive_blocks(&self->g, &blocks, &cohesion, &parents, 0)) { igraph_vector_ptr_destroy(&blocks); igraph_vector_destroy(&cohesion); igraph_vector_destroy(&parents); igraphmodule_handle_igraph_error(); return NULL; } blocks_o = igraphmodule_vector_ptr_t_to_PyList(&blocks, IGRAPHMODULE_TYPE_INT); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&blocks, igraph_vector_destroy); igraph_vector_ptr_destroy_all(&blocks); if (blocks_o == NULL) { igraph_vector_destroy(&parents); igraph_vector_destroy(&cohesion); return NULL; } cohesion_o = igraphmodule_vector_t_to_PyList(&cohesion, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&cohesion); if (cohesion_o == NULL) { Py_DECREF(blocks_o); igraph_vector_destroy(&parents); return NULL; } parents_o = igraphmodule_vector_t_to_PyList(&parents, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&parents); if (parents_o == NULL) { Py_DECREF(blocks_o); Py_DECREF(cohesion_o); return NULL; } result_o = Py_BuildValue("NNN", blocks_o, cohesion_o, parents_o); if (result_o == NULL) { Py_DECREF(parents_o); Py_DECREF(blocks_o); Py_DECREF(cohesion_o); return NULL; } return result_o; } /********************************************************************** * Cliques and independent sets * **********************************************************************/ /** \ingroup python_interface_graph * \brief Find all or some cliques in a graph */ PyObject *igraphmodule_Graph_cliques(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "min", "max", NULL }; PyObject *list, *item; long int min_size = 0, max_size = 0; long int i, j, n; igraph_vector_ptr_t result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ll", kwlist, &min_size, &max_size)) return NULL; if (igraph_vector_ptr_init(&result, 0)) { PyErr_SetString(PyExc_MemoryError, ""); return NULL; } if (igraph_cliques(&self->g, &result, (igraph_integer_t) min_size, (igraph_integer_t) max_size)) { igraph_vector_ptr_destroy(&result); return igraphmodule_handle_igraph_error(); } n = (long)igraph_vector_ptr_size(&result); list = PyList_New(n); if (!list) return NULL; for (i = 0; i < n; i++) { igraph_vector_t *vec = (igraph_vector_t *) VECTOR(result)[i]; item = igraphmodule_vector_t_to_PyTuple(vec); if (!item) { for (j = i; j < n; j++) igraph_vector_destroy((igraph_vector_t *) VECTOR(result)[j]); igraph_vector_ptr_destroy_all(&result); Py_DECREF(list); return NULL; } else { PyList_SET_ITEM(list, i, item); } igraph_vector_destroy(vec); } igraph_vector_ptr_destroy_all(&result); return list; } /** \ingroup python_interface_graph * \brief Find all largest cliques in a graph */ PyObject *igraphmodule_Graph_largest_cliques(igraphmodule_GraphObject * self) { PyObject *list, *item; long int i, j, n; igraph_vector_ptr_t result; if (igraph_vector_ptr_init(&result, 0)) { PyErr_SetString(PyExc_MemoryError, ""); return NULL; } if (igraph_largest_cliques(&self->g, &result)) { igraph_vector_ptr_destroy(&result); return igraphmodule_handle_igraph_error(); } n = (long)igraph_vector_ptr_size(&result); list = PyList_New(n); if (!list) return NULL; for (i = 0; i < n; i++) { igraph_vector_t *vec = (igraph_vector_t *) VECTOR(result)[i]; item = igraphmodule_vector_t_to_PyTuple(vec); if (!item) { for (j = i; j < n; j++) igraph_vector_destroy((igraph_vector_t *) VECTOR(result)[j]); igraph_vector_ptr_destroy_all(&result); Py_DECREF(list); return NULL; } else { PyList_SET_ITEM(list, i, item); } igraph_vector_destroy(vec); } igraph_vector_ptr_destroy_all(&result); return list; } /** \ingroup python_interface_graph * \brief Finds a maximum matching in a bipartite graph */ PyObject *igraphmodule_Graph_maximum_bipartite_matching(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds) { static char* kwlist[] = { "types", "weights", "eps", NULL }; PyObject *types_o = Py_None, *weights_o = Py_None, *result_o; igraph_vector_bool_t* types = 0; igraph_vector_t* weights = 0; igraph_vector_long_t result; double eps = -1; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Od", kwlist, &types_o, &weights_o, &eps)) return NULL; if (eps < 0) eps = DBL_EPSILON * 1000; if (igraphmodule_attrib_to_vector_bool_t(types_o, self, &types, ATTRIBUTE_TYPE_VERTEX)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) { if (types != 0) { igraph_vector_bool_destroy(types); free(types); } return NULL; } if (igraph_vector_long_init(&result, 0)) { if (types != 0) { igraph_vector_bool_destroy(types); free(types); } if (weights != 0) { igraph_vector_destroy(weights); free(weights); } igraphmodule_handle_igraph_error(); return NULL; } if (igraph_maximum_bipartite_matching(&self->g, types, 0, 0, &result, weights, eps)) { if (types != 0) { igraph_vector_bool_destroy(types); free(types); } if (weights != 0) { igraph_vector_destroy(weights); free(weights); } igraph_vector_long_destroy(&result); igraphmodule_handle_igraph_error(); return NULL; } if (types != 0) { igraph_vector_bool_destroy(types); free(types); } if (weights != 0) { igraph_vector_destroy(weights); free(weights); } result_o = igraphmodule_vector_long_t_to_PyList(&result); igraph_vector_long_destroy(&result); return result_o; } /** \ingroup python_interface_graph * \brief Find all maximal cliques in a graph */ PyObject *igraphmodule_Graph_maximal_cliques(igraphmodule_GraphObject * self, PyObject* args, PyObject* kwds) { static char* kwlist[] = { "min", "max", "file", NULL }; PyObject *list, *item, *file = Py_None; long int i = 0, j = 0; igraph_integer_t min, max; Py_ssize_t n; igraph_vector_ptr_t result; igraphmodule_filehandle_t filehandle; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|llO", kwlist, &i, &j, &file)) return NULL; min = (igraph_integer_t) i; max = (igraph_integer_t) j; if (file == Py_None) { if (igraph_vector_ptr_init(&result, 0)) { PyErr_SetString(PyExc_MemoryError, ""); return NULL; } if (igraph_maximal_cliques(&self->g, &result, min, max)) { igraph_vector_ptr_destroy(&result); return igraphmodule_handle_igraph_error(); } n = (Py_ssize_t)igraph_vector_ptr_size(&result); list = PyList_New(n); if (!list) return NULL; for (i = 0; i < n; i++) { igraph_vector_t *vec = (igraph_vector_t *) VECTOR(result)[i]; item = igraphmodule_vector_t_to_PyTuple(vec); if (!item) { for (j = i; j < n; j++) igraph_vector_destroy((igraph_vector_t *) VECTOR(result)[j]); igraph_vector_ptr_destroy_all(&result); Py_DECREF(list); return NULL; } else { PyList_SET_ITEM(list, i, item); } igraph_vector_destroy(vec); } igraph_vector_ptr_destroy_all(&result); return list; } else { if (igraphmodule_filehandle_init(&filehandle, file, "w")) { return igraphmodule_handle_igraph_error(); } if (igraph_maximal_cliques_file(&self->g, igraphmodule_filehandle_get(&filehandle), min, max)) { igraphmodule_filehandle_destroy(&filehandle); return igraphmodule_handle_igraph_error(); } igraphmodule_filehandle_destroy(&filehandle); Py_RETURN_NONE; } } /** \ingroup python_interface_graph * \brief Returns the clique number of the graph */ PyObject *igraphmodule_Graph_clique_number(igraphmodule_GraphObject * self) { PyObject *result; igraph_integer_t i; if (igraph_clique_number(&self->g, &i)) return igraphmodule_handle_igraph_error(); result = PyInt_FromLong((long)i); return result; } /** \ingroup python_interface_graph * \brief Find all or some independent vertex sets in a graph */ PyObject *igraphmodule_Graph_independent_vertex_sets(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "min", "max", NULL }; PyObject *list, *item; long int min_size = 0, max_size = 0; long int i, j, n; igraph_vector_ptr_t result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ll", kwlist, &min_size, &max_size)) return NULL; if (igraph_vector_ptr_init(&result, 0)) { PyErr_SetString(PyExc_MemoryError, ""); return NULL; } if (igraph_independent_vertex_sets(&self->g, &result, (igraph_integer_t) min_size, (igraph_integer_t) max_size)) { igraph_vector_ptr_destroy(&result); return igraphmodule_handle_igraph_error(); } n = (long)igraph_vector_ptr_size(&result); list = PyList_New(n); if (!list) return NULL; for (i = 0; i < n; i++) { igraph_vector_t *vec = (igraph_vector_t *) VECTOR(result)[i]; item = igraphmodule_vector_t_to_PyTuple(vec); if (!item) { for (j = i; j < n; j++) igraph_vector_destroy((igraph_vector_t *) VECTOR(result)[j]); igraph_vector_ptr_destroy_all(&result); Py_DECREF(list); return NULL; } else { PyList_SET_ITEM(list, i, item); } igraph_vector_destroy(vec); } igraph_vector_ptr_destroy_all(&result); return list; } /** \ingroup python_interface_graph * \brief Find all largest independent_vertex_sets in a graph */ PyObject *igraphmodule_Graph_largest_independent_vertex_sets(igraphmodule_GraphObject * self) { PyObject *list, *item; long int i, j, n; igraph_vector_ptr_t result; if (igraph_vector_ptr_init(&result, 0)) { PyErr_SetString(PyExc_MemoryError, ""); return NULL; } if (igraph_largest_independent_vertex_sets(&self->g, &result)) { igraph_vector_ptr_destroy(&result); return igraphmodule_handle_igraph_error(); } n = (long)igraph_vector_ptr_size(&result); list = PyList_New(n); if (!list) return NULL; for (i = 0; i < n; i++) { igraph_vector_t *vec = (igraph_vector_t *) VECTOR(result)[i]; item = igraphmodule_vector_t_to_PyTuple(vec); if (!item) { for (j = i; j < n; j++) igraph_vector_destroy((igraph_vector_t *) VECTOR(result)[j]); igraph_vector_ptr_destroy_all(&result); Py_DECREF(list); return NULL; } else { PyList_SET_ITEM(list, i, item); } igraph_vector_destroy(vec); } igraph_vector_ptr_destroy_all(&result); return list; } /** \ingroup python_interface_graph * \brief Find all maximal independent vertex sets in a graph */ PyObject *igraphmodule_Graph_maximal_independent_vertex_sets(igraphmodule_GraphObject * self) { PyObject *list, *item; long int i, j, n; igraph_vector_ptr_t result; if (igraph_vector_ptr_init(&result, 0)) { PyErr_SetString(PyExc_MemoryError, ""); return NULL; } if (igraph_maximal_independent_vertex_sets(&self->g, &result)) { igraph_vector_ptr_destroy(&result); return igraphmodule_handle_igraph_error(); } n = (long)igraph_vector_ptr_size(&result); list = PyList_New(n); if (!list) return NULL; for (i = 0; i < n; i++) { igraph_vector_t *vec = (igraph_vector_t *) VECTOR(result)[i]; item = igraphmodule_vector_t_to_PyTuple(vec); if (!item) { for (j = i; j < n; j++) igraph_vector_destroy((igraph_vector_t *) VECTOR(result)[j]); igraph_vector_ptr_destroy_all(&result); Py_DECREF(list); return NULL; } else { PyList_SET_ITEM(list, i, item); } igraph_vector_destroy(vec); } igraph_vector_ptr_destroy_all(&result); return list; } /** \ingroup python_interface_graph * \brief Returns the independence number of the graph */ PyObject *igraphmodule_Graph_independence_number(igraphmodule_GraphObject * self) { PyObject *result; igraph_integer_t i; if (igraph_independence_number(&self->g, &i)) return igraphmodule_handle_igraph_error(); result = PyInt_FromLong((long)i); return result; } /********************************************************************** * K-core decomposition * **********************************************************************/ /** \ingroup python_interface_graph * \brief Returns the corenesses of the graph vertices * \return a new PyCObject */ PyObject *igraphmodule_Graph_coreness(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "mode", NULL }; igraph_neimode_t mode = IGRAPH_ALL; igraph_vector_t result; PyObject *o, *mode_o = Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &mode_o)) return NULL; if (igraphmodule_PyObject_to_neimode_t(mode_o, &mode)) return NULL; if (igraph_vector_init(&result, igraph_vcount(&self->g))) return igraphmodule_handle_igraph_error(); if (igraph_coreness(&self->g, &result, mode)) { igraph_vector_destroy(&result); return igraphmodule_handle_igraph_error(); } o = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&result); return o; } /********************************************************************** * Community structure detection and related routines * **********************************************************************/ /** * Modularity calculation */ PyObject *igraphmodule_Graph_modularity(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"membership", "weights", 0}; igraph_vector_t membership; igraph_vector_t *weights=0; igraph_real_t modularity; PyObject *mvec, *wvec=Py_None; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &mvec, &wvec)) return NULL; if (igraphmodule_PyObject_to_vector_t(mvec, &membership, 1)) return NULL; if (igraphmodule_attrib_to_vector_t(wvec, self, &weights, ATTRIBUTE_TYPE_EDGE)){ igraph_vector_destroy(&membership); return NULL; } if (igraph_modularity(&self->g, &membership, &modularity, weights)) { igraph_vector_destroy(&membership); if (weights) { igraph_vector_destroy(weights); free(weights); } return NULL; } igraph_vector_destroy(&membership); if (weights) { igraph_vector_destroy(weights); free(weights); } return Py_BuildValue("d", (double)modularity); } /** * Newman's edge betweenness method */ PyObject *igraphmodule_Graph_community_edge_betweenness(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "directed", "weights", NULL }; PyObject *directed = Py_True; PyObject *weights_o = Py_None; PyObject *res, *qs, *ms; igraph_matrix_t merges; igraph_vector_t q; igraph_vector_t *weights = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &directed, &weights_o)) return NULL; if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) return NULL; if (igraph_matrix_init(&merges, 0, 0)) { if (weights != 0) { igraph_vector_destroy(weights); free(weights); } return igraphmodule_handle_igraph_error(); } if (igraph_vector_init(&q, 0)) { igraph_matrix_destroy(&merges); if (weights != 0) { igraph_vector_destroy(weights); free(weights); } return igraphmodule_handle_igraph_error(); } if (igraph_community_edge_betweenness(&self->g, /* removed_edges = */ 0, /* edge_betweenness = */ 0, /* merges = */ &merges, /* bridges = */ 0, /* modularity = */ &q, /* membership = */ 0, PyObject_IsTrue(directed), weights)) { igraphmodule_handle_igraph_error(); if (weights != 0) { igraph_vector_destroy(weights); free(weights); } igraph_matrix_destroy(&merges); igraph_vector_destroy(&q); return NULL; } if (weights != 0) { igraph_vector_destroy(weights); free(weights); } qs=igraphmodule_vector_t_to_PyList(&q, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&q); if (!qs) { igraph_matrix_destroy(&merges); return NULL; } ms=igraphmodule_matrix_t_to_PyList(&merges, IGRAPHMODULE_TYPE_INT); igraph_matrix_destroy(&merges); if (ms == NULL) { Py_DECREF(qs); return NULL; } res=Py_BuildValue("NN", ms, qs); /* steals references */ return res; } /** * Newman's leading eigenvector method, precise implementation */ PyObject *igraphmodule_Graph_community_leading_eigenvector(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "n", "weights", "arpack_options", NULL }; long int n=-1; PyObject *cl, *res, *merges, *weights_obj = Py_None; igraph_vector_t members; igraph_vector_t *weights = 0; igraph_matrix_t m; igraph_real_t q; igraphmodule_ARPACKOptionsObject *arpack_options; PyObject *arpack_options_o = igraphmodule_arpack_options_default; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|lOO!", kwlist, &n, &weights_obj, &igraphmodule_ARPACKOptionsType, &arpack_options_o)) { return NULL; } if (igraph_vector_init(&members, 0)) return igraphmodule_handle_igraph_error(); if (igraph_matrix_init(&m, 0, 0)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&members); return 0; } if (n<0) n = igraph_vcount(&self->g); else n -= 1; if (igraphmodule_attrib_to_vector_t(weights_obj, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_matrix_destroy(&m); igraph_vector_destroy(&members); return NULL; } arpack_options = (igraphmodule_ARPACKOptionsObject*)arpack_options_o; if (igraph_community_leading_eigenvector(&self->g, weights, &m, &members, (igraph_integer_t) n, igraphmodule_ARPACKOptions_get(arpack_options), &q, 0, 0, 0, 0, 0, 0)){ igraph_matrix_destroy(&m); igraph_vector_destroy(&members); if (weights != 0) { igraph_vector_destroy(weights); free(weights); } return igraphmodule_handle_igraph_error(); } if (weights != 0) { igraph_vector_destroy(weights); free(weights); } cl = igraphmodule_vector_t_to_PyList(&members, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&members); if (cl == 0) { igraph_matrix_destroy(&m); return 0; } merges = igraphmodule_matrix_t_to_PyList(&m, IGRAPHMODULE_TYPE_INT); igraph_matrix_destroy(&m); if (merges == 0) return 0; res=Py_BuildValue("NNd", cl, merges, (double)q); return res; } /** * Clauset et al's greedy modularity optimization algorithm */ PyObject *igraphmodule_Graph_community_fastgreedy(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "weights", NULL }; PyObject *ms, *qs, *res, *weights = Py_None; igraph_matrix_t merges; igraph_vector_t q, *ws=0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &weights)) { return NULL; } if (igraphmodule_attrib_to_vector_t(weights, self, &ws, ATTRIBUTE_TYPE_EDGE)) return NULL; igraph_matrix_init(&merges, 0, 0); igraph_vector_init(&q, 0); if (igraph_community_fastgreedy(&self->g, ws, &merges, &q, 0)) { if (ws) { igraph_vector_destroy(ws); free(ws); } igraph_vector_destroy(&q); igraph_matrix_destroy(&merges); return igraphmodule_handle_igraph_error(); } if (ws) { igraph_vector_destroy(ws); free(ws); } qs=igraphmodule_vector_t_to_PyList(&q, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&q); if (!qs) { igraph_matrix_destroy(&merges); return NULL; } ms=igraphmodule_matrix_t_to_PyList(&merges, IGRAPHMODULE_TYPE_INT); igraph_matrix_destroy(&merges); if (ms == NULL) { Py_DECREF(qs); return NULL; } res=Py_BuildValue("NN", ms, qs); /* steals references */ return res; } /** * Infomap community detection algorithm of Martin Rosvall and Carl T. Bergstrom, */ PyObject *igraphmodule_Graph_community_infomap(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "edge_weights", "vertex_weights", "trials", NULL }; PyObject *e_weights = Py_None, *v_weights = Py_None; unsigned int nb_trials = 10; igraph_vector_t *e_ws = 0, *v_ws = 0; igraph_vector_t membership; PyObject *res = Py_False; igraph_real_t codelength; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOI", kwlist, &e_weights, &v_weights, &nb_trials)) { return NULL; } if (igraph_vector_init(&membership, igraph_vcount(&self->g))) { igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(e_weights, self, &e_ws, ATTRIBUTE_TYPE_EDGE)) { igraph_vector_destroy(&membership); return NULL; } if (igraphmodule_attrib_to_vector_t(v_weights, self, &v_ws, ATTRIBUTE_TYPE_VERTEX)){ igraph_vector_destroy(&membership); if (e_ws) { igraph_vector_destroy(e_ws); free(e_ws); } return NULL; } if (igraph_community_infomap(/*in */ &self->g, /*e_weight=*/ e_ws, /*v_weight=*/ v_ws, /*nb_trials=*/nb_trials, /*out*/ &membership, &codelength)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&membership); if (e_ws) { igraph_vector_destroy(e_ws); free(e_ws); } if (v_ws) { igraph_vector_destroy(v_ws); free(v_ws); } return NULL; } if (e_ws) { igraph_vector_destroy(e_ws); free(e_ws); } if (v_ws) { igraph_vector_destroy(v_ws); free(v_ws); } res = igraphmodule_vector_t_to_PyList(&membership, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&membership); if (!res) return NULL; return Py_BuildValue("Nd", res, (double)codelength); } /** * The label propagation algorithm of Raghavan et al */ PyObject *igraphmodule_Graph_community_label_propagation( igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "weights", "initial", "fixed", NULL }; PyObject *weights_o = Py_None, *initial_o = Py_None, *fixed_o = Py_None; PyObject *result; igraph_vector_t membership, *ws = 0, *initial = 0; igraph_vector_bool_t fixed; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &weights_o, &initial_o, &fixed_o)) { return NULL; } if (fixed_o != Py_None) { if (igraphmodule_PyObject_to_vector_bool_t(fixed_o, &fixed)) return NULL; } if (igraphmodule_attrib_to_vector_t(weights_o, self, &ws, ATTRIBUTE_TYPE_EDGE)) { if (fixed_o != Py_None) igraph_vector_bool_destroy(&fixed); return NULL; } if (igraphmodule_attrib_to_vector_t(initial_o, self, &initial, ATTRIBUTE_TYPE_VERTEX)){ if (fixed_o != Py_None) igraph_vector_bool_destroy(&fixed); if (ws) { igraph_vector_destroy(ws); free(ws); } return NULL; } igraph_vector_init(&membership, igraph_vcount(&self->g)); if (igraph_community_label_propagation(&self->g, &membership, ws, initial, (fixed_o != Py_None ? &fixed : 0), 0)) { if (fixed_o != Py_None) igraph_vector_bool_destroy(&fixed); if (ws) { igraph_vector_destroy(ws); free(ws); } if (initial) { igraph_vector_destroy(initial); free(initial); } igraph_vector_destroy(&membership); return igraphmodule_handle_igraph_error(); } if (fixed_o != Py_None) igraph_vector_bool_destroy(&fixed); if (ws) { igraph_vector_destroy(ws); free(ws); } if (initial) { igraph_vector_destroy(initial); free(initial); } result=igraphmodule_vector_t_to_PyList(&membership, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&membership); return result; } /** * Multilevel algorithm of Blondel et al */ PyObject *igraphmodule_Graph_community_multilevel(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "weights", "return_levels", NULL }; PyObject *return_levels = Py_False; PyObject *mss, *qs, *res, *weights = Py_None; igraph_matrix_t memberships; igraph_vector_t membership, modularity; igraph_vector_t *ws; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, &weights, &return_levels)) { return NULL; } if (igraphmodule_attrib_to_vector_t(weights, self, &ws, ATTRIBUTE_TYPE_EDGE)) return NULL; igraph_matrix_init(&memberships, 0, 0); igraph_vector_init(&membership, 0); igraph_vector_init(&modularity, 0); if (igraph_community_multilevel(&self->g, ws, &membership, &memberships, &modularity)) { if (ws) { igraph_vector_destroy(ws); free(ws); } igraph_vector_destroy(&membership); igraph_vector_destroy(&modularity); igraph_matrix_destroy(&memberships); return igraphmodule_handle_igraph_error(); } if (ws) { igraph_vector_destroy(ws); free(ws); } qs=igraphmodule_vector_t_to_PyList(&modularity, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&modularity); if (!qs) { igraph_vector_destroy(&membership); igraph_matrix_destroy(&memberships); return NULL; } if (PyObject_IsTrue(return_levels)) { mss=igraphmodule_matrix_t_to_PyList(&memberships, IGRAPHMODULE_TYPE_INT); if (!mss) { res = mss; } else { res=Py_BuildValue("NN", mss, qs); /* steals references */ } } else { res=igraphmodule_vector_t_to_PyList(&membership, IGRAPHMODULE_TYPE_INT); } igraph_vector_destroy(&membership); igraph_matrix_destroy(&memberships); return res; } /** * Optimal modularity by integer programming */ PyObject *igraphmodule_Graph_community_optimal_modularity( igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"weights", NULL}; PyObject *weights_o = Py_None; igraph_real_t modularity; igraph_vector_t membership; igraph_vector_t* weights = 0; PyObject *res; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &weights_o)) return NULL; if (igraph_vector_init(&membership, igraph_vcount(&self->g))) { igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_vector_destroy(&membership); return NULL; } if (igraph_community_optimal_modularity(&self->g, &modularity, &membership, weights)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&membership); if (weights != 0) { igraph_vector_destroy(weights); free(weights); } return NULL; } if (weights != 0) { igraph_vector_destroy(weights); free(weights); } res = igraphmodule_vector_t_to_PyList(&membership, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&membership); if (!res) return NULL; return Py_BuildValue("Nd", res, (double)modularity); } /** * Spinglass community detection method of Reichardt & Bornholdt */ PyObject *igraphmodule_Graph_community_spinglass(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"weights", "spins", "parupdate", "start_temp", "stop_temp", "cool_fact", "update_rule", "gamma", "implementation", "lambda_", NULL}; PyObject *weights_o = Py_None; PyObject *parupdate_o = Py_False; PyObject *update_rule_o = Py_None; PyObject *impl_o = Py_None; PyObject *res; long int spins = 25; double start_temp = 1.0; double stop_temp = 0.01; double cool_fact = 0.99; igraph_spinglass_implementation_t impl = IGRAPH_SPINCOMM_IMP_ORIG; igraph_spincomm_update_t update_rule = IGRAPH_SPINCOMM_UPDATE_CONFIG; double gamma = 1; double lambda = 1; igraph_vector_t *weights = 0, membership; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OlOdddOdOd", kwlist, &weights_o, &spins, &parupdate_o, &start_temp, &stop_temp, &cool_fact, &update_rule_o, &gamma, &impl_o, &lambda)) return NULL; if (igraphmodule_PyObject_to_spincomm_update_t(update_rule_o, &update_rule)) { return NULL; } if (igraphmodule_PyObject_to_spinglass_implementation_t(impl_o, &impl)) { return NULL; } if (igraph_vector_init(&membership, igraph_vcount(&self->g))) { igraphmodule_handle_igraph_error(); return NULL; } if (igraphmodule_attrib_to_vector_t(weights_o, self, &weights, ATTRIBUTE_TYPE_EDGE)) { igraph_vector_destroy(&membership); return NULL; } if (igraph_community_spinglass(&self->g, weights, 0, 0, &membership, 0, (igraph_integer_t) spins, PyObject_IsTrue(parupdate_o), start_temp, stop_temp, cool_fact, update_rule, gamma, impl, lambda)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&membership); if (weights != 0) { igraph_vector_destroy(weights); free(weights); } return NULL; } if (weights != 0) { igraph_vector_destroy(weights); free(weights); } res = igraphmodule_vector_t_to_PyList(&membership, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&membership); return res; } /** * Walktrap community detection of Latapy & Pons */ PyObject *igraphmodule_Graph_community_walktrap(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { static char *kwlist[] = { "weights", "steps", NULL }; PyObject *ms, *qs, *res, *weights = Py_None; igraph_matrix_t merges; int steps=4; igraph_vector_t q, *ws=0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi", kwlist, &weights, &steps)) return NULL; if (igraphmodule_attrib_to_vector_t(weights, self, &ws, ATTRIBUTE_TYPE_EDGE)) return NULL; igraph_matrix_init(&merges, 0, 0); igraph_vector_init(&q, 0); if (igraph_community_walktrap(&self->g, ws, steps, &merges, &q, 0)) { if (ws) { igraph_vector_destroy(ws); free(ws); } igraph_vector_destroy(&q); igraph_matrix_destroy(&merges); return igraphmodule_handle_igraph_error(); } if (ws) { igraph_vector_destroy(ws); free(ws); } qs = igraphmodule_vector_t_to_PyList(&q, IGRAPHMODULE_TYPE_FLOAT); igraph_vector_destroy(&q); if (!qs) { igraph_matrix_destroy(&merges); return NULL; } ms = igraphmodule_matrix_t_to_PyList(&merges, IGRAPHMODULE_TYPE_INT); igraph_matrix_destroy(&merges); if (ms == NULL) { Py_DECREF(qs); return NULL; } res=Py_BuildValue("NN", ms, qs); /* steals references */ return res; } /********************************************************************** * Special internal methods that you won't need to mess around with * **********************************************************************/ /** \defgroup python_interface_internal Internal functions * \ingroup python_interface */ #ifdef IGRAPH_PYTHON3 PyObject *igraphmodule_Graph___graph_as_capsule__(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { return PyCapsule_New((void *)&self->g, 0, 0); } #else /** \ingroup python_interface_internal * \brief Returns the encapsulated igraph graph as a PyCObject * \return a new PyCObject */ PyObject *igraphmodule_Graph___graph_as_cobject__(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { return PyCObject_FromVoidPtr((void *)&self->g, 0); } #endif /** \ingroup python_interface_internal * \brief Returns the pointer of the encapsulated igraph graph as an ordinary * Python integer. This allows us to use igraph graphs with the Python ctypes * module without any additional conversions. */ PyObject *igraphmodule_Graph__raw_pointer(igraphmodule_GraphObject *self) { return PyInt_FromLong((long int)&self->g); } /** \ingroup python_interface_internal * \brief Registers a destructor to be called when the object is destroyed * \return the previous destructor (if any) * Unimplemented. */ PyObject *igraphmodule_Graph___register_destructor__(igraphmodule_GraphObject * self, PyObject * args, PyObject * kwds) { char *kwlist[] = { "destructor", NULL }; PyObject *destructor = NULL, *result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &destructor)) return NULL; if (!PyCallable_Check(destructor)) { PyErr_SetString(PyExc_TypeError, "The destructor must be callable!"); return NULL; } result = self->destructor; self->destructor = destructor; Py_INCREF(self->destructor); if (!result) Py_RETURN_NONE; return result; } /** \ingroup python_interface * \brief Member list of the \c igraph.Graph object type */ #define OFF(x) offsetof(igraphmodule_GraphObject, x) /** \ingroup python_interface * \brief Method list of the \c igraph.Graph object type */ struct PyMethodDef igraphmodule_Graph_methods[] = { //////////////////////////// // BASIC IGRAPH INTERFACE // //////////////////////////// // interface to igraph_vcount {"vcount", (PyCFunction) igraphmodule_Graph_vcount, METH_NOARGS, "vcount()\n\n" "Counts the number of vertices.\n" "@return: the number of vertices in the graph.\n" "@rtype: integer"}, // interface to igraph_ecount {"ecount", (PyCFunction) igraphmodule_Graph_ecount, METH_NOARGS, "ecount()\n\n" "Counts the number of edges.\n" "@return: the number of edges in the graph.\n" "@rtype: integer"}, // interface to igraph_is_dag {"is_dag", (PyCFunction) igraphmodule_Graph_is_dag, METH_NOARGS, "is_dag()\n\n" "Checks whether the graph is a DAG (directed acyclic graph).\n\n" "A DAG is a directed graph with no directed cycles.\n\n" "@return: C{True} if it is a DAG, C{False} otherwise.\n" "@rtype: boolean"}, // interface to igraph_is_directed {"is_directed", (PyCFunction) igraphmodule_Graph_is_directed, METH_NOARGS, "is_directed()\n\n" "Checks whether the graph is directed.\n" "@return: C{True} if it is directed, C{False} otherwise.\n" "@rtype: boolean"}, // interface to igraph_is_simple {"is_simple", (PyCFunction) igraphmodule_Graph_is_simple, METH_NOARGS, "is_simple()\n\n" "Checks whether the graph is simple (no loop or multiple edges).\n\n" "@return: C{True} if it is simple, C{False} otherwise.\n" "@rtype: boolean"}, /* interface to igraph_add_vertices */ {"add_vertices", (PyCFunction) igraphmodule_Graph_add_vertices, METH_VARARGS, "add_vertices(n)\n\n" "Adds vertices to the graph.\n\n" "@param n: the number of vertices to be added\n"}, /* interface to igraph_delete_vertices */ {"delete_vertices", (PyCFunction) igraphmodule_Graph_delete_vertices, METH_VARARGS, "delete_vertices(vs)\n\n" "Deletes vertices and all its edges from the graph.\n\n" "@param vs: a single vertex ID or the list of vertex IDs\n" " to be deleted.\n"}, /* interface to igraph_add_edges */ {"add_edges", (PyCFunction) igraphmodule_Graph_add_edges, METH_VARARGS, "add_edges(es)\n\n" "Adds edges to the graph.\n\n" "@param es: the list of edges to be added. Every edge is\n" " represented with a tuple, containing the vertex IDs of the\n" " two endpoints. Vertices are enumerated from zero.\n"}, /* interface to igraph_delete_edges */ {"delete_edges", (PyCFunction) igraphmodule_Graph_delete_edges, METH_VARARGS | METH_KEYWORDS, "delete_edges(es)\n\n" "Removes edges from the graph.\n\n" "All vertices will be kept, even if they lose all their edges.\n" "Nonexistent edges will be silently ignored.\n\n" "@param es: the list of edges to be removed. Edges are identifed by\n" " edge IDs. L{EdgeSeq} objects are also accepted here.\n"}, /* interface to igraph_degree */ {"degree", (PyCFunction) igraphmodule_Graph_degree, METH_VARARGS | METH_KEYWORDS, "degree(vertices, mode=ALL, loops=True)\n\n" "Returns some vertex degrees from the graph.\n\n" "This method accepts a single vertex ID or a list of vertex IDs as a\n" "parameter, and returns the degree of the given vertices (in the\n" "form of a single integer or a list, depending on the input\n" "parameter).\n" "\n" "@param vertices: a single vertex ID or a list of vertex IDs\n" "@param mode: the type of degree to be returned (L{OUT} for\n" " out-degrees, L{IN} IN for in-degrees or L{ALL} for the sum of\n" " them).\n" "@param loops: whether self-loops should be counted.\n"}, /* interface to igraph_strength */ {"strength", (PyCFunction) igraphmodule_Graph_strength, METH_VARARGS | METH_KEYWORDS, "strength(vertices, mode=ALL, loops=True, weights=None)\n\n" "Returns the strength (weighted degree) of some vertices from the graph\n\n" "This method accepts a single vertex ID or a list of vertex IDs as a\n" "parameter, and returns the strength (that is, the sum of the weights\n" "of all incident edges) of the given vertices (in the\n" "form of a single integer or a list, depending on the input\n" "parameter).\n" "\n" "@param vertices: a single vertex ID or a list of vertex IDs\n" "@param mode: the type of degree to be returned (L{OUT} for\n" " out-degrees, L{IN} IN for in-degrees or L{ALL} for the sum of\n" " them).\n" "@param loops: whether self-loops should be counted.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" }, /* interface to igraph_is_loop */ {"is_loop", (PyCFunction) igraphmodule_Graph_is_loop, METH_VARARGS | METH_KEYWORDS, "is_loop(edges=None)\n\n" "Checks whether a specific set of edges contain loop edges\n\n" "@param edges: edge indices which we want to check. If C{None}, all\n" " edges are checked.\n" "@return: a list of booleans, one for every edge given\n"}, /* interface to igraph_is_multiple */ {"is_multiple", (PyCFunction) igraphmodule_Graph_is_multiple, METH_VARARGS | METH_KEYWORDS, "is_multiple(edges=None)\n\n" "Checks whether an edge is a multiple edge.\n\n" "Also works for a set of edges -- in this case, every edge is checked\n" "one by one. Note that if there are multiple edges going between a\n" "pair of vertices, there is always one of them that is I{not}\n" "reported as multiple (only the others). This allows one to easily\n" "detect the edges that have to be deleted in order to make the graph\n" "free of multiple edges.\n\n" "@param edges: edge indices which we want to check. If C{None}, all\n" " edges are checked.\n" "@return: a list of booleans, one for every edge given\n"}, /* interface to igraph_has_multiple */ {"has_multiple", (PyCFunction) igraphmodule_Graph_has_multiple, METH_NOARGS, "has_multiple()\n\n" "Checks whether the graph has multiple edges.\n\n" "@return: C{True} if the graph has at least one multiple edge,\n" " C{False} otherwise.\n" "@rtype: boolean"}, /* interface to igraph_is_mutual */ {"is_mutual", (PyCFunction) igraphmodule_Graph_is_mutual, METH_VARARGS | METH_KEYWORDS, "is_mutual(edges=None)\n\n" "Checks whether an edge has an opposite pair.\n\n" "Also works for a set of edges -- in this case, every edge is checked\n" "one by one. The result will be a list of booleans (or a single boolean\n" "if only an edge index is supplied), every boolean corresponding to an\n" "edge in the edge set supplied. C{True} is returned for a given edge\n" "M{a} --> M{b} if there exists another edge M{b} --> M{a} in the\n" "original graph (not the given edge set!). All edges in an undirected\n" "graph are mutual. In case there are multiple edges between M{a}\n" "and M{b}, it is enough to have at least one edge in either direction\n" "to report all edges between them as mutual, so the multiplicity\n" "of edges do not matter.\n\n" "@param edges: edge indices which we want to check. If C{None}, all\n" " edges are checked.\n" "@return: a list of booleans, one for every edge given\n"}, /* interface to igraph_count_multiple */ {"count_multiple", (PyCFunction) igraphmodule_Graph_count_multiple, METH_VARARGS | METH_KEYWORDS, "count_multiple(edges=None)\n\n" "Counts the multiplicities of the given edges.\n\n" "@param edges: edge indices for which we want to count their\n" " multiplicity. If C{None}, all edges are counted.\n" "@return: the multiplicities of the given edges as a list.\n"}, /* interface to igraph_neighbors */ {"neighbors", (PyCFunction) igraphmodule_Graph_neighbors, METH_VARARGS | METH_KEYWORDS, "neighbors(vertex, mode=ALL)\n\n" "Returns adjacent vertices to a given vertex.\n\n" "@param vertex: a vertex ID\n" "@param mode: whether to return only successors (L{OUT}),\n" " predecessors (L{IN}) or both (L{ALL}). Ignored for undirected\n" " graphs."}, {"successors", (PyCFunction) igraphmodule_Graph_successors, METH_VARARGS | METH_KEYWORDS, "successors(vertex)\n\n" "Returns the successors of a given vertex.\n\n" "Equivalent to calling the L{Graph.neighbors} method with type=L{OUT}."}, {"predecessors", (PyCFunction) igraphmodule_Graph_predecessors, METH_VARARGS | METH_KEYWORDS, "predecessors(vertex)\n\n" "Returns the predecessors of a given vertex.\n\n" "Equivalent to calling the L{Graph.neighbors} method with type=L{IN}."}, /* interface to igraph_get_eid */ {"get_eid", (PyCFunction) igraphmodule_Graph_get_eid, METH_VARARGS | METH_KEYWORDS, "get_eid(v1, v2, directed=True, error=True)\n\n" "Returns the edge ID of an arbitrary edge between vertices v1 and v2\n\n" "@param v1: the ID or name of the first vertex\n" "@param v2: the ID or name of the second vertex\n" "@param directed: whether edge directions should be considered in\n" " directed graphs. The default is C{True}. Ignored for undirected\n" " graphs.\n" "@param error: if C{True}, an exception will be raised when the\n" " given edge does not exist. If C{False}, -1 will be returned in\n" " that case.\n" "@return: the edge ID of an arbitrary edge between vertices v1 and v2\n"}, /* interface to igraph_get_eids */ {"get_eids", (PyCFunction) igraphmodule_Graph_get_eids, METH_VARARGS | METH_KEYWORDS, "get_eids(pairs=None, path=None, directed=True, error=True)\n\n" "Returns the edge IDs of some edges between some vertices.\n\n" "This method can operate in two different modes, depending on which\n" "of the keyword arguments C{pairs} and C{path} are given.\n\n" "The method does not consider multiple edges; if there are multiple\n" "edges between a pair of vertices, only the ID of one of the edges\n" "is returned.\n\n" "@param pairs: a list of integer pairs. Each integer pair is considered\n" " as a source-target vertex pair; the corresponding edge is looked up\n" " in the graph and the edge ID is returned for each pair.\n" "@param path: a list of vertex IDs. The list is considered as a\n" " continuous path from the first vertex to the last, passing\n" " through the intermediate vertices. The corresponding edge IDs\n" " between the first and the second, the second and the third and\n" " so on are looked up in the graph and the edge IDs are returned.\n" " If both C{path} and C{pairs} are given, the two lists are\n" " concatenated.\n" "@param directed: whether edge directions should be considered in\n" " directed graphs. The default is C{True}. Ignored for undirected\n" " graphs.\n" "@param error: if C{True}, an exception will be raised if a given\n" " edge does not exist. If C{False}, -1 will be returned in\n" " that case.\n" "@return: the edge IDs in a list\n"}, /* interface to igraph_incident */ {"incident", (PyCFunction) igraphmodule_Graph_incident, METH_VARARGS | METH_KEYWORDS, "incident(vertex, mode=OUT)\n\n" "Returns the edges a given vertex is incident on.\n\n" "@param vertex: a vertex ID\n" "@param mode: whether to return only successors (L{OUT}),\n" " predecessors (L{IN}) or both (L{ALL}). Ignored for undirected\n" " graphs."}, ////////////////////// // GRAPH GENERATORS // ////////////////////// /* interface to igraph_adjacency */ {"Adjacency", (PyCFunction) igraphmodule_Graph_Adjacency, METH_CLASS | METH_VARARGS | METH_KEYWORDS, "Adjacency(matrix, mode=ADJ_DIRECTED)\n\n" "Generates a graph from its adjacency matrix.\n\n" "@param matrix: the adjacency matrix\n" "@param mode: the mode to be used. Possible values are:\n" "\n" " - C{ADJ_DIRECTED} - the graph will be directed and a matrix\n" " element gives the number of edges between two vertex.\n" " - C{ADJ_UNDIRECTED} - alias to C{ADJ_MAX} for convenience.\n" " - C{ADJ_MAX} - undirected graph will be created and the number of\n" " edges between vertex M{i} and M{j} is M{max(A(i,j), A(j,i))}\n" " - C{ADJ_MIN} - like C{ADJ_MAX}, but with M{min(A(i,j), A(j,i))}\n" " - C{ADJ_PLUS} - like C{ADJ_MAX}, but with M{A(i,j) + A(j,i)}\n" " - C{ADJ_UPPER} - undirected graph with the upper right triangle of\n" " the matrix (including the diagonal)\n" " - C{ADJ_LOWER} - undirected graph with the lower left triangle of\n" " the matrix (including the diagonal)\n" "\n" " These values can also be given as strings without the C{ADJ} prefix.\n" }, /* interface to igraph_asymmetric_preference_game */ {"Asymmetric_Preference", (PyCFunction) igraphmodule_Graph_Asymmetric_Preference, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Asymmetric_Preference(n, type_dist_matrix, pref_matrix, attribute=None, loops=False)\n\n" "Generates a graph based on asymmetric vertex types and connection probabilities.\n\n" "This is the asymmetric variant of L{Graph.Preference}.\n" "A given number of vertices are generated. Every vertex is assigned to an\n" "\"incoming\" and an \"outgoing\" vertex type according to the given joint\n" "type probabilities. Finally, every vertex pair is evaluated and a\n" "directed edge is created between them with a probability depending on\n" "the \"outgoing\" type of the source vertex and the \"incoming\" type of\n" "the target vertex.\n\n" "@param n: the number of vertices in the graph\n" "@param type_dist_matrix: matrix giving the joint distribution of vertex\n" " types\n" "@param pref_matrix: matrix giving the connection probabilities for\n" " different vertex types.\n" "@param attribute: the vertex attribute name used to store the vertex\n" " types. If C{None}, vertex types are not stored.\n" "@param loops: whether loop edges are allowed.\n"}, // interface to igraph_atlas {"Atlas", (PyCFunction) igraphmodule_Graph_Atlas, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Atlas(idx)\n\n" "Generates a graph from the Graph Atlas.\n\n" "@param idx: The index of the graph to be generated.\n" " Indices start from zero, graphs are listed:\n\n" " 1. in increasing order of number of vertices;\n" " 2. for a fixed number of vertices, in increasing order of the\n" " number of edges;\n" " 3. for fixed numbers of vertices and edges, in increasing order\n" " of the degree sequence, for example 111223 < 112222;\n" " 4. for fixed degree sequence, in increasing number of automorphisms.\n\n" "@newfield ref: Reference\n" "@ref: I{An Atlas of Graphs} by Ronald C. Read and Robin J. Wilson,\n" " Oxford University Press, 1998."}, // interface to igraph_barabasi_game {"Barabasi", (PyCFunction) igraphmodule_Graph_Barabasi, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Barabasi(n, m, outpref=False, directed=False, power=1,\n" " zero_appeal=1, implementation=\"psumtree\", start_from=None)\n\n" "Generates a graph based on the Barabasi-Albert model.\n\n" "@param n: the number of vertices\n" "@param m: either the number of outgoing edges generated for\n" " each vertex or a list containing the number of outgoing\n" " edges for each vertex explicitly.\n" "@param outpref: C{True} if the out-degree of a given vertex\n" " should also increase its citation probability (as well as\n" " its in-degree), but it defaults to C{False}.\n" "@param directed: C{True} if the generated graph should be\n" " directed (default: C{False}).\n" "@param power: the power constant of the nonlinear model.\n" " It can be omitted, and in this case the usual linear model\n" " will be used.\n" "@param zero_appeal: the attractivity of vertices with degree\n" " zero.\n\n" "@param implementation: the algorithm to use to generate the\n" " network. Possible values are:\n\n" " - C{\"bag\"}: the algorithm that was the default in\n" " igraph before 0.6. It works by putting the ids of the\n" " vertices into a bag (multiset) exactly as many times\n" " as their in-degree, plus once more. The required number\n" " of cited vertices are then drawn from the bag with\n" " replacement. It works only for I{power}=1 and\n" " I{zero_appeal}=1.\n\n" " - C{\"psumtree\"}: this algorithm uses a partial prefix-sum\n" " tree to generate the graph. It does not generate multiple\n" " edges and it works for any values of I{power} and\n" " I{zero_appeal}.\n\n" " - C{\"psumtree_multiple\"}: similar to C{\"psumtree\"}, but\n" " it will generate multiple edges as well. igraph before\n" " 0.6 used this algorithm for I{power}s other than 1.\n\n" "@param start_from: if given and not C{None}, this must be another\n" " L{Graph} object. igraph will use this graph as a starting\n" " point for the preferential attachment model.\n\n" "@newfield ref: Reference\n" "@ref: Barabasi, A-L and Albert, R. 1999. Emergence of scaling\n" " in random networks. Science, 286 509-512."}, /* interface to igraph_create_bipartite */ {"_Bipartite", (PyCFunction) igraphmodule_Graph_Bipartite, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "_Bipartite(types, edges, directed=False)\n\n" "Internal function, undocumented.\n\n" "@see: Graph.Bipartite()\n\n"}, /* interface to igraph_de_bruijn */ {"De_Bruijn", (PyCFunction) igraphmodule_Graph_De_Bruijn, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "De_Bruijn(m, n)\n\n" "Generates a de Bruijn graph with parameters (m, n)\n\n" "A de Bruijn graph represents relationships between strings. An alphabet\n" "of M{m} letters are used and strings of length M{n} are considered.\n" "A vertex corresponds to every possible string and there is a directed edge\n" "from vertex M{v} to vertex M{w} if the string of M{v} can be transformed into\n" "the string of M{w} by removing its first letter and appending a letter to it.\n" "\n" "Please note that the graph will have M{m^n} vertices and even more edges,\n" "so probably you don't want to supply too big numbers for M{m} and M{n}.\n\n" "@param m: the size of the alphabet\n" "@param n: the length of the strings\n" }, // interface to igraph_establishment_game {"Establishment", (PyCFunction) igraphmodule_Graph_Establishment, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Establishment(n, k, type_dist, pref_matrix, directed=False)\n\n" "Generates a graph based on a simple growing model with vertex types.\n\n" "A single vertex is added at each time step. This new vertex tries to\n" "connect to k vertices in the graph. The probability that such a\n" "connection is realized depends on the types of the vertices involved.\n" "\n" "@param n: the number of vertices in the graph\n" "@param k: the number of connections tried in each step\n" "@param type_dist: list giving the distribution of vertex types\n" "@param pref_matrix: matrix (list of lists) giving the connection\n" " probabilities for different vertex types\n" "@param directed: whether to generate a directed graph.\n"}, // interface to igraph_erdos_renyi_game {"Erdos_Renyi", (PyCFunction) igraphmodule_Graph_Erdos_Renyi, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Erdos_Renyi(n, p, m, directed=False, loops=False)\n\n" "Generates a graph based on the Erdos-Renyi model.\n\n" "@param n: the number of vertices.\n" "@param p: the probability of edges. If given, C{m} must be missing.\n" "@param m: the number of edges. If given, C{p} must be missing.\n" "@param directed: whether to generate a directed graph.\n" "@param loops: whether self-loops are allowed.\n"}, /* interface to igraph_famous */ {"Famous", (PyCFunction) igraphmodule_Graph_Famous, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Famous(name)\n\n" "Generates a famous graph based on its name.\n\n" "Several famous graphs are known to C{igraph} including (but not limited to)\n" "the Chvatal graph, the Petersen graph or the Tutte graph. This method\n" "generates one of them based on its name (case insensitive). See the\n" "documentation of the C interface of C{igraph} for the names available:\n" "U{http://igraph.org/doc/c}.\n\n" "@param name: the name of the graph to be generated.\n" }, /* interface to igraph_forest_fire_game */ {"Forest_Fire", (PyCFunction) igraphmodule_Graph_Forest_Fire, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Forest_Fire(n, fw_prob, bw_factor=0.0, ambs=1, directed=False)\n\n" "Generates a graph based on the forest fire model\n\n" "The forest fire model is a growin graph model. In every time step, a new\n" "vertex is added to the graph. The new vertex chooses an ambassador (or\n" "more than one if M{ambs>1}) and starts a simulated forest fire at its\n" "ambassador(s). The fire spreads through the edges. The spreading probability\n" "along an edge is given by M{fw_prob}. The fire may also spread backwards\n" "on an edge by probability M{fw_prob * bw_factor}. When the fire ended, the\n" "newly added vertex connects to the vertices ``burned'' in the previous\n" "fire.\n\n" "@param n: the number of vertices in the graph\n" "@param fw_prob: forward burning probability\n" "@param bw_factor: ratio of backward and forward burning probability\n" "@param ambs: number of ambassadors chosen in each step\n" "@param directed: whether the graph will be directed\n" }, /* interface to igraph_full_citation */ {"Full_Citation", (PyCFunction) igraphmodule_Graph_Full_Citation, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Full_Citation(n, directed=False)\n\n" "Generates a full citation graph\n\n" "A full citation graph is a graph where the vertices are indexed from 0 to\n" "M{n-1} and vertex M{i} has a directed edge towards all vertices with an\n" "index less than M{i}.\n\n" "@param n: the number of vertices.\n" "@param directed: whether to generate a directed graph.\n"}, /* interface to igraph_full */ {"Full", (PyCFunction) igraphmodule_Graph_Full, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Full(n, directed=False, loops=False)\n\n" "Generates a full graph (directed or undirected, with or without loops).\n\n" "@param n: the number of vertices.\n" "@param directed: whether to generate a directed graph.\n" "@param loops: whether self-loops are allowed.\n"}, /* interface to igraph_full_bipartite */ {"_Full_Bipartite", (PyCFunction) igraphmodule_Graph_Full_Bipartite, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "_Full_Bipartite(n1, n2, directed=False, loops=False)\n\n" "Internal function, undocumented.\n\n" "@see: Graph.Full_Bipartite()\n\n"}, /* interface to igraph_grg_game */ {"_GRG", (PyCFunction) igraphmodule_Graph_GRG, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "_GRG(n, radius, torus=False)\n\n" "Internal function, undocumented.\n\n" "@see: Graph.GRG()\n\n"}, /* interface to igraph_growing_random_game */ {"Growing_Random", (PyCFunction) igraphmodule_Graph_Growing_Random, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Growing_Random(n, m, directed=False, citation=False)\n\n" "Generates a growing random graph.\n\n" "@param n: The number of vertices in the graph\n" "@param m: The number of edges to add in each step (after adding a new vertex)\n" "@param directed: whether the graph should be directed.\n" "@param citation: whether the new edges should originate from the most\n" " recently added vertex.\n"}, /* interface to igraph_incidence */ {"_Incidence", (PyCFunction) igraphmodule_Graph_Incidence, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "_Incidence(matrix, directed=False, mode=ALL, multiple=False)\n\n" "Internal function, undocumented.\n\n" "@see: Graph.Incidence()\n\n"}, /* interface to igraph_kautz */ {"Kautz", (PyCFunction) igraphmodule_Graph_Kautz, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Kautz(m, n)\n\n" "Generates a Kautz graph with parameters (m, n)\n\n" "A Kautz graph is a labeled graph, vertices are labeled by strings\n" "of length M{n+1} above an alphabet with M{m+1} letters, with\n" "the restriction that every two consecutive letters in the string\n" "must be different. There is a directed edge from a vertex M{v} to\n" "another vertex M{w} if it is possible to transform the string of\n" "M{v} into the string of M{w} by removing the first letter and\n" "appending a letter to it.\n\n" "@param m: the size of the alphabet minus one\n" "@param n: the length of the strings minus one\n" }, /* interface to igraph_k_regular */ {"K_Regular", (PyCFunction) igraphmodule_Graph_K_Regular, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "K_Regular(n, k, directed=False, multiple=False)\n\n" "Generates a k-regular random graph\n\n" "A k-regular random graph is a random graph where each vertex has degree k.\n" "If the graph is directed, both the in-degree and the out-degree of each vertex\n" "will be k.\n\n" "@param n: The number of vertices in the graph\n\n" "@param k: The degree of each vertex if the graph is undirected, or the in-degree\n" " and out-degree of each vertex if the graph is directed\n" "@param directed: whether the graph should be directed.\n" "@param multiple: whether it is allowed to create multiple edges.\n" }, /* interface to igraph_preference_game */ {"Preference", (PyCFunction) igraphmodule_Graph_Preference, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Preference(n, type_dist, pref_matrix, attribute=None, directed=False, loops=False)\n\n" "Generates a graph based on vertex types and connection probabilities.\n\n" "This is practically the nongrowing variant of L{Graph.Establishment}.\n" "A given number of vertices are generated. Every vertex is assigned to a\n" "vertex type according to the given type probabilities. Finally, every\n" "vertex pair is evaluated and an edge is created between them with a\n" "probability depending on the types of the vertices involved.\n\n" "@param n: the number of vertices in the graph\n" "@param type_dist: list giving the distribution of vertex types\n" "@param pref_matrix: matrix giving the connection probabilities for\n" " different vertex types.\n" "@param attribute: the vertex attribute name used to store the vertex\n" " types. If C{None}, vertex types are not stored.\n" "@param directed: whether to generate a directed graph.\n" "@param loops: whether loop edges are allowed.\n"}, /* interface to igraph_bipartite_game */ {"_Random_Bipartite", (PyCFunction) igraphmodule_Graph_Random_Bipartite, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "_Random_Bipartite(n1, n2, p=None, m=None, directed=False, neimode=\"all\")\n\n" "Internal function, undocumented.\n\n" "@see: Graph.Random_Bipartite()\n\n"}, /* interface to igraph_recent_degree_game */ {"Recent_Degree", (PyCFunction) igraphmodule_Graph_Recent_Degree, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Recent_Degree(n, m, window, outpref=False, directed=False, power=1)\n\n" "Generates a graph based on a stochastic model where the probability\n" "of an edge gaining a new node is proportional to the edges gained in\n" "a given time window.\n\n" "@param n: the number of vertices\n" "@param m: either the number of outgoing edges generated for\n" " each vertex or a list containing the number of outgoing\n" " edges for each vertex explicitly.\n" "@param window: size of the window in time steps\n" "@param outpref: C{True} if the out-degree of a given vertex\n" " should also increase its citation probability (as well as\n" " its in-degree), but it defaults to C{False}.\n" "@param directed: C{True} if the generated graph should be\n" " directed (default: C{False}).\n" "@param power: the power constant of the nonlinear model.\n" " It can be omitted, and in this case the usual linear model\n" " will be used.\n"}, /* interface to igraph_sbm_game */ {"SBM", (PyCFunction) igraphmodule_Graph_SBM, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "SBM(n, pref_matrix, block_sizes, directed=False, loops=False)\n\n" "Generates a graph based on a stochastic blockmodel.\n\n" "A given number of vertices are generated. Every vertex is assigned to a\n" "vertex type according to the given block sizes. Vertices of the same\n" "type will be assigned consecutive vertex IDs. Finally, every\n" "vertex pair is evaluated and an edge is created between them with a\n" "probability depending on the types of the vertices involved. The\n" "probabilities are taken from the preference matrix.\n\n" "@param n: the number of vertices in the graph\n" "@param pref_matrix: matrix giving the connection probabilities for\n" " different vertex types.\n" "@param block_sizes: list giving the number of vertices in each block; must\n" " sum up to I{n}.\n" "@param directed: whether to generate a directed graph.\n" "@param loops: whether loop edges are allowed.\n"}, // interface to igraph_star {"Star", (PyCFunction) igraphmodule_Graph_Star, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Star(n, mode=\"undirected\", center=0)\n\n" "Generates a star graph.\n\n" "@param n: the number of vertices in the graph\n" "@param mode: Gives the type of the star graph to create. Should be\n" " either \"in\", \"out\", \"mutual\" or \"undirected\"\n" "@param center: Vertex ID for the central vertex in the star.\n"}, // interface to igraph_lattice {"Lattice", (PyCFunction) igraphmodule_Graph_Lattice, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Lattice(dim, nei=1, directed=False, mutual=True, circular=True)\n\n" "Generates a regular lattice.\n\n" "@param dim: list with the dimensions of the lattice\n" "@param nei: value giving the distance (number of steps) within which\n" " two vertices will be connected.\n" "@param directed: whether to create a directed graph.\n" "@param mutual: whether to create all connections as mutual\n" " in case of a directed graph.\n" "@param circular: whether the generated lattice is periodic.\n"}, /* interface to igraph_lcf */ {"LCF", (PyCFunction) igraphmodule_Graph_LCF, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "LCF(n, shifts, repeats)\n\n" "Generates a graph from LCF notation.\n\n" "LCF is short for Lederberg-Coxeter-Frucht, it is a concise notation\n" "for 3-regular Hamiltonian graphs. It consists of three parameters,\n" "the number of vertices in the graph, a list of shifts giving\n" "additional edges to a cycle backbone and another integer giving how\n" "many times the shifts should be performed. See\n" "U{http://mathworld.wolfram.com/LCFNotation.html} for details.\n\n" "@param n: the number of vertices\n" "@param shifts: the shifts in a list or tuple\n" "@param repeats: the number of repeats\n" }, // interface to igraph_ring {"Ring", (PyCFunction) igraphmodule_Graph_Ring, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Ring(n, directed=False, mutual=False, circular=True)\n\n" "Generates a ring graph.\n\n" "@param n: the number of vertices in the ring\n" "@param directed: whether to create a directed ring.\n" "@param mutual: whether to create mutual edges in a directed ring.\n" "@param circular: whether to create a closed ring.\n"}, /* interface to igraph_static_fitness_game */ {"Static_Fitness", (PyCFunction) igraphmodule_Graph_Static_Fitness, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Static_Fitness(m, fitness_out, fitness_in=None, loops=False, multiple=False)\n\n" "Generates a non-growing graph with edge probabilities proportional to node\n" "fitnesses.\n\n" "The algorithm randomly selects vertex pairs and connects them until the given\n" "number of edges are created. Each vertex is selected with a probability\n" "proportional to its fitness; for directed graphs, a vertex is selected as a\n" "source proportional to its out-fitness and as a target proportional to its\n" "in-fitness.\n\n" "@param m: the number of edges in the graph\n" "@param fitness_out: a numeric vector with non-negative entries, one for each\n" " vertex. These values represent the fitness scores (out-fitness scores for\n" " directed graphs). I{fitness} is an alias of this keyword argument.\n" "@param fitness_in: a numeric vector with non-negative entries, one for each\n" " vertex. These values represent the in-fitness scores for directed graphs.\n" " For undirected graphs, this argument must be C{None}.\n" "@param loops: whether loop edges are allowed.\n" "@param multiple: whether multiple edges are allowed.\n" "@return: a directed or undirected graph with the prescribed power-law\n" " degree distributions.\n" }, /* interface to igraph_static_power_law_game */ {"Static_Power_Law", (PyCFunction) igraphmodule_Graph_Static_Power_Law, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Static_Power_Law(n, m, exponent_out, exponent_in=-1, loops=False,\n" " multiple=False, finite_size_correction=True)\n\n" "Generates a non-growing graph with prescribed power-law degree distributions.\n\n" "@param n: the number of vertices in the graph\n" "@param m: the number of edges in the graph\n" "@param exponent_out: the exponent of the out-degree distribution, which\n" " must be between 2 and infinity (inclusive). When I{exponent_in} is\n" " not given or negative, the graph will be undirected and this parameter\n" " specifies the degree distribution. I{exponent} is an alias to this\n" " keyword argument.\n" "@param exponent_in: the exponent of the in-degree distribution, which\n" " must be between 2 and infinity (inclusive) It can also be negative, in\n" " which case an undirected graph will be generated.\n" "@param loops: whether loop edges are allowed.\n" "@param multiple: whether multiple edges are allowed.\n" "@param finite_size_correction: whether to apply a finite-size correction\n" " to the generated fitness values for exponents less than 3. See the\n" " paper of Cho et al for more details.\n" "@return: a directed or undirected graph with the prescribed power-law\n" " degree distributions.\n" "\n" "@newfield ref: Reference\n" "@ref: Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution\n" " in scale-free networks. Phys Rev Lett 87(27):278701, 2001.\n" "@ref: Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in\n" " scale-free networks under the Achlioptas process. Phys Rev Lett\n" " 103:135702, 2009.\n" }, // interface to igraph_tree {"Tree", (PyCFunction) igraphmodule_Graph_Tree, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Tree(n, children, type=TREE_UNDIRECTED)\n\n" "Generates a tree in which almost all vertices have the same number of children.\n\n" "@param n: the number of vertices in the graph\n" "@param children: the number of children of a vertex in the graph\n" "@param type: determines whether the tree should be directed, and if\n" " this is the case, also its orientation. Must be one of\n" " C{TREE_IN}, C{TREE_OUT} and C{TREE_UNDIRECTED}.\n"}, /* interface to igraph_degree_sequence_game */ {"Degree_Sequence", (PyCFunction) igraphmodule_Graph_Degree_Sequence, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Degree_Sequence(out, in=None, method=\"simple\")\n\n" "Generates a graph with a given degree sequence.\n\n" "@param out: the out-degree sequence for a directed graph. If the\n" " in-degree sequence is omitted, the generated graph\n" " will be undirected, so this will be the in-degree\n" " sequence as well\n" "@param in: the in-degree sequence for a directed graph.\n" " If omitted, the generated graph will be undirected.\n" "@param method: the generation method to be used. One of the following:\n" " \n" " - C{\"simple\"} -- simple generator that sometimes generates\n" " loop edges and multiple edges. The generated graph is not\n" " guaranteed to be connected.\n" " - C{\"no_multiple\"} -- similar to C{\"simple\"} but avoids the\n" " generation of multiple and loop edges at the expense of increased\n" " time complexity. The method will re-start the generation every time\n" " it gets stuck in a configuration where it is not possible to insert\n" " any more edges without creating loops or multiple edges, and there\n" " is no upper bound on the number of iterations, but it will succeed\n" " eventually if the input degree sequence is graphical and throw an\n" " exception if the input degree sequence is not graphical.\n" " - C{\"vl\"} -- a more sophisticated generator that can sample\n" " undirected, connected simple graphs uniformly. It uses\n" " Monte-Carlo methods to randomize the graphs.\n" " This generator should be favoured if undirected and connected\n" " graphs are to be generated and execution time is not a concern.\n" " igraph uses the original implementation of Fabien Viger; see the\n" " following URL and the paper cited on it for the details of the\n" " algorithm: U{http://www-rp.lip6.fr/~latapy/FV/generation.html}.\n" }, /* interface to igraph_isoclass_create */ {"Isoclass", (PyCFunction) igraphmodule_Graph_Isoclass, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Isoclass(n, class, directed=False)\n\n" "Generates a graph with a given isomorphy class.\n\n" "@param n: the number of vertices in the graph (3 or 4)\n" "@param class: the isomorphy class\n" "@param directed: whether the graph should be directed.\n"}, /* interface to igraph_watts_strogatz_game */ {"Watts_Strogatz", (PyCFunction) igraphmodule_Graph_Watts_Strogatz, METH_VARARGS | METH_CLASS | METH_KEYWORDS, "Watts_Strogatz(dim, size, nei, p, loops=False, multiple=False)\n\n" "@param dim: the dimension of the lattice\n" "@param size: the size of the lattice along all dimensions\n" "@param nei: value giving the distance (number of steps) within which\n" " two vertices will be connected.\n" "@param p: rewiring probability\n\n" "@param loops: specifies whether loop edges are allowed\n" "@param multiple: specifies whether multiple edges are allowed\n" "@see: L{Lattice()}, L{rewire()}, L{rewire_edges()} if more flexibility is\n" " needed\n" "@newfield ref: Reference\n" "@ref: Duncan J Watts and Steven H Strogatz: I{Collective dynamics of\n" " small world networks}, Nature 393, 440-442, 1998\n"}, /* interface to igraph_weighted_adjacency */ {"Weighted_Adjacency", (PyCFunction) igraphmodule_Graph_Weighted_Adjacency, METH_CLASS | METH_VARARGS | METH_KEYWORDS, "Weighted_Adjacency(matrix, mode=ADJ_DIRECTED, attr=\"weight\", loops=True)\n\n" "Generates a graph from its adjacency matrix.\n\n" "@param matrix: the adjacency matrix\n" "@param mode: the mode to be used. Possible values are:\n" "\n" " - C{ADJ_DIRECTED} - the graph will be directed and a matrix\n" " element gives the number of edges between two vertex.\n" " - C{ADJ_UNDIRECTED} - alias to C{ADJ_MAX} for convenience.\n" " - C{ADJ_MAX} - undirected graph will be created and the number of\n" " edges between vertex M{i} and M{j} is M{max(A(i,j), A(j,i))}\n" " - C{ADJ_MIN} - like C{ADJ_MAX}, but with M{min(A(i,j), A(j,i))}\n" " - C{ADJ_PLUS} - like C{ADJ_MAX}, but with M{A(i,j) + A(j,i)}\n" " - C{ADJ_UPPER} - undirected graph with the upper right triangle of\n" " the matrix (including the diagonal)\n" " - C{ADJ_LOWER} - undirected graph with the lower left triangle of\n" " the matrix (including the diagonal)\n" "\n" " These values can also be given as strings without the C{ADJ} prefix.\n" "@param attr: the name of the edge attribute that stores the edge\n" " weights.\n" "@param loops: whether to include loop edges. When C{False}, the diagonal\n" " of the adjacency matrix will be ignored.\n" }, ///////////////////////////////////// // STRUCTURAL PROPERTIES OF GRAPHS // ///////////////////////////////////// // interface to igraph_are_connected {"are_connected", (PyCFunction) igraphmodule_Graph_are_connected, METH_VARARGS | METH_KEYWORDS, "are_connected(v1, v2)\n\n" "Decides whether two given vertices are directly connected.\n\n" "@param v1: the ID or name of the first vertex\n" "@param v2: the ID or name of the second vertex\n" "@return: C{True} if there exists an edge from v1 to v2, C{False}\n" " otherwise.\n"}, /* interface to igraph_articulation_points */ {"articulation_points", (PyCFunction)igraphmodule_Graph_articulation_points, METH_NOARGS, "articulation_points()\n\n" "Returns the list of articulation points in the graph.\n\n" "A vertex is an articulation point if its removal increases the number of\n" "connected components in the graph.\n" }, /* interface to igraph_assortativity */ {"assortativity", (PyCFunction)igraphmodule_Graph_assortativity, METH_VARARGS | METH_KEYWORDS, "assortativity(types1, types2=None, directed=True)\n\n" "Returns the assortativity of the graph based on numeric properties\n" "of the vertices.\n\n" "This coefficient is basically the correlation between the actual\n" "connectivity patterns of the vertices and the pattern expected from the\n" "disribution of the vertex types.\n\n" "See equation (21) in Newman MEJ: Mixing patterns in networks, Phys Rev E\n" "67:026126 (2003) for the proper definition. The actual calculation is\n" "performed using equation (26) in the same paper for directed graphs, and\n" "equation (4) in Newman MEJ: Assortative mixing in networks, Phys Rev Lett\n" "89:208701 (2002) for undirected graphs.\n\n" "@param types1: vertex types in a list or the name of a vertex attribute\n" " holding vertex types. Types are ideally denoted by numeric values.\n" "@param types2: in directed assortativity calculations, each vertex can\n" " have an out-type and an in-type. In this case, I{types1} contains the\n" " out-types and this parameter contains the in-types in a list or the\n" " name of a vertex attribute. If C{None}, it is assumed to be equal\n" " to I{types1}.\n\n" "@param directed: whether to consider edge directions or not.\n" "@return: the assortativity coefficient\n\n" "@newfield ref: Reference\n" "@ref: Newman MEJ: Mixing patterns in networks, Phys Rev E 67:026126, 2003.\n" "@ref: Newman MEJ: Assortative mixing in networks, Phys Rev Lett 89:208701,\n" " 2002.\n" "@see: L{assortativity_degree()} when the types are the vertex degrees\n" }, /* interface to igraph_assortativity_degree */ {"assortativity_degree", (PyCFunction)igraphmodule_Graph_assortativity_degree, METH_VARARGS | METH_KEYWORDS, "assortativity_degree(directed=True)\n\n" "Returns the assortativity of a graph based on vertex degrees.\n\n" "See L{assortativity()} for the details. L{assortativity_degree()} simply\n" "calls L{assortativity()} with the vertex degrees as types.\n\n" "@param directed: whether to consider edge directions for directed graphs\n" " or not. This argument is ignored for undirected graphs.\n" "@return: the assortativity coefficient\n\n" "@see: L{assortativity()}\n" }, /* interface to igraph_assortativity_nominal */ {"assortativity_nominal", (PyCFunction)igraphmodule_Graph_assortativity_nominal, METH_VARARGS | METH_KEYWORDS, "assortativity_nominal(types, directed=True)\n\n" "Returns the assortativity of the graph based on vertex categories.\n\n" "Assuming that the vertices belong to different categories, this\n" "function calculates the assortativity coefficient, which specifies\n" "the extent to which the connections stay within categories. The\n" "assortativity coefficient is one if all the connections stay within\n" "categories and minus one if all the connections join vertices of\n" "different categories. For a randomly connected network, it is\n" "asymptotically zero.\n\n" "See equation (2) in Newman MEJ: Mixing patterns in networks, Phys Rev E\n" "67:026126 (2003) for the proper definition.\n\n" "@param types: vertex types in a list or the name of a vertex attribute\n" " holding vertex types. Types should be denoted by numeric values.\n" "@param directed: whether to consider edge directions or not.\n" "@return: the assortativity coefficient\n\n" "@newfield ref: Reference\n" "@ref: Newman MEJ: Mixing patterns in networks, Phys Rev E 67:026126, 2003.\n" }, /* interface to igraph_average_path_length */ {"average_path_length", (PyCFunction) igraphmodule_Graph_average_path_length, METH_VARARGS | METH_KEYWORDS, "average_path_length(directed=True, unconn=True)\n\n" "Calculates the average path length in a graph.\n\n" "@param directed: whether to consider directed paths in case of a\n" " directed graph. Ignored for undirected graphs.\n" "@param unconn: what to do when the graph is unconnected. If C{True},\n" " the average of the geodesic lengths in the components is\n" " calculated. Otherwise for all unconnected vertex pairs,\n" " a path length equal to the number of vertices is used.\n" "@return: the average path length in the graph\n"}, /* interface to igraph_authority_score */ {"authority_score", (PyCFunction)igraphmodule_Graph_authority_score, METH_VARARGS | METH_KEYWORDS, "authority_score(weights=None, scale=True, arpack_options=None, return_eigenvalue=False)\n\n" "Calculates Kleinberg's authority score for the vertices of the graph\n\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@param scale: whether to normalize the scores so that the largest one\n" " is 1.\n" "@param arpack_options: an L{ARPACKOptions} object used to fine-tune\n" " the ARPACK eigenvector calculation. If omitted, the module-level\n" " variable called C{arpack_options} is used.\n" "@param return_eigenvalue: whether to return the largest eigenvalue\n" "@return: the authority scores in a list and optionally the largest eigenvalue\n" " as a second member of a tuple\n\n" "@see: hub_score()\n" }, /* interface to igraph_betweenness[_estimate] */ {"betweenness", (PyCFunction) igraphmodule_Graph_betweenness, METH_VARARGS | METH_KEYWORDS, "betweenness(vertices=None, directed=True, cutoff=None, weights=None, nobigint=True)\n\n" "Calculates or estimates the betweenness of vertices in a graph.\n\n" "Keyword arguments:\n" "@param vertices: the vertices for which the betweennesses must be returned.\n" " If C{None}, assumes all of the vertices in the graph.\n" "@param directed: whether to consider directed paths.\n" "@param cutoff: if it is an integer, only paths less than or equal to this\n" " length are considered, effectively resulting in an estimation of the\n" " betweenness for the given vertices. If C{None}, the exact betweenness is\n" " returned.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@param nobigint: if C{True}, igraph uses the longest available integer\n" " type on the current platform to count shortest paths. For some large\n" " networks that have a specific structure, the counters may overflow.\n" " To prevent this, use C{nobigint=False}, which forces igraph to use\n" " arbitrary precision integers at the expense of increased computation\n" " time.\n" "@return: the (possibly estimated) betweenness of the given vertices in a list\n"}, /* interface to biconnected_components */ {"biconnected_components", (PyCFunction) igraphmodule_Graph_biconnected_components, METH_VARARGS | METH_KEYWORDS, "biconnected_components(return_articulation_points=True)\n\n" "Calculates the biconnected components of the graph.\n\n" "@param return_articulation_points: whether to return the articulation\n" " points as well\n" "@return: a list of lists containing edge indices making up spanning trees\n" " of the biconnected components (one spanning tree for each component)\n" " and optionally the list of articulation points" }, /* interface to igraph_bipartite_projection */ {"bipartite_projection", (PyCFunction) igraphmodule_Graph_bipartite_projection, METH_VARARGS | METH_KEYWORDS, "bipartite_projection(types, multiplicity=True, probe1=-1, which=-1)\n\n" "Internal function, undocumented.\n\n" "@see: Graph.bipartite_projection()\n"}, /* interface to igraph_bipartite_projection_size */ {"bipartite_projection_size", (PyCFunction) igraphmodule_Graph_bipartite_projection_size, METH_VARARGS | METH_KEYWORDS, "bipartite_projection_size(types)\n\n" "Internal function, undocumented.\n\n" "@see: Graph.bipartite_projection_size()\n"}, /* interface to igraph_closeness */ {"closeness", (PyCFunction) igraphmodule_Graph_closeness, METH_VARARGS | METH_KEYWORDS, "closeness(vertices=None, mode=ALL, cutoff=None, weights=None,\n" " normalized=True)\n\n" "Calculates the closeness centralities of given vertices in a graph.\n\n" "The closeness centerality of a vertex measures how easily other\n" "vertices can be reached from it (or the other way: how easily it\n" "can be reached from the other vertices). It is defined as the\n" "number of the number of vertices minus one divided by the sum of\n" "the lengths of all geodesics from/to the given vertex.\n\n" "If the graph is not connected, and there is no path between two\n" "vertices, the number of vertices is used instead the length of\n" "the geodesic. This is always longer than the longest possible\n" "geodesic.\n\n" "@param vertices: the vertices for which the closenesses must\n" " be returned. If C{None}, uses all of the vertices in the graph.\n" "@param mode: must be one of L{IN}, L{OUT} and L{ALL}. L{IN} means\n" " that the length of the incoming paths, L{OUT} means that the\n" " length of the outgoing paths must be calculated. L{ALL} means\n" " that both of them must be calculated.\n" "@param cutoff: if it is an integer, only paths less than or equal to this\n" " length are considered, effectively resulting in an estimation of the\n" " closeness for the given vertices (which is always an underestimation of the\n" " real closeness, since some vertex pairs will appear as disconnected even\n" " though they are connected).. If C{None}, the exact closeness is\n" " returned.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@param normalized: Whether to normalize the raw closeness scores by\n" " multiplying by the number of vertices minus one.\n" "@return: the calculated closenesses in a list\n"}, /* interface to igraph_clusters */ {"clusters", (PyCFunction) igraphmodule_Graph_clusters, METH_VARARGS | METH_KEYWORDS, "clusters(mode=STRONG)\n\n" "Calculates the (strong or weak) clusters for a given graph.\n\n" "@attention: this function has a more convenient interface in class\n" " L{Graph} which wraps the result in a L{VertexClustering} object.\n" " It is advised to use that.\n" "@param mode: must be either C{STRONG} or C{WEAK}, depending on\n" " the clusters being sought. Optional, defaults to C{STRONG}.\n" "@return: the component index for every node in the graph.\n"}, {"copy", (PyCFunction) igraphmodule_Graph_copy, METH_NOARGS, "copy()\n\n" "Creates an exact deep copy of the graph."}, {"decompose", (PyCFunction) igraphmodule_Graph_decompose, METH_VARARGS | METH_KEYWORDS, "decompose(mode=STRONG, maxcompno=None, minelements=1)\n\n" "Decomposes the graph into subgraphs.\n\n" "@param mode: must be either STRONG or WEAK, depending on the\n" " clusters being sought.\n" "@param maxcompno: maximum number of components to return.\n" " C{None} means all possible components.\n" "@param minelements: minimum number of vertices in a component.\n" " By setting this to 2, isolated vertices are not returned\n" " as separate components.\n" "@return: a list of the subgraphs. Every returned subgraph is a\n" " copy of the original.\n"}, /* interface to igraph_contract_vertices */ {"contract_vertices", (PyCFunction) igraphmodule_Graph_contract_vertices, METH_VARARGS | METH_KEYWORDS, "contract_vertices(mapping, combine_attrs=None)\n\n" "Contracts some vertices in the graph, i.e. replaces groups of vertices\n" "with single vertices. Edges are not affected.\n\n" "@param mapping: numeric vector which gives the mapping between old and\n" " new vertex IDs. Vertices having the same new vertex ID in this vector\n" " will be remapped into a single new vertex. It is safe to pass the\n" " membership vector of a L{VertexClustering} object here.\n" "@param combine_attrs: specifies how to combine the attributes of\n" " the vertices being collapsed into a single one. If it is C{None},\n" " all the attributes will be lost. If it is a function, the\n" " attributes of the vertices will be collected and passed on to\n" " that function which will return the new attribute value that has to\n" " be assigned to the single collapsed vertex. It can also be one of\n" " the following string constants which define built-in collapsing\n" " functions: C{sum}, C{prod}, C{mean}, C{median}, C{max}, C{min},\n" " C{first}, C{last}, C{random}. You can also specify different\n" " combination functions for different attributes by passing a dict\n" " here which maps attribute names to functions. See\n" " L{Graph.simplify()} for more details.\n" "@return: C{None}.\n" "@see: L{Graph.simplify()}\n" }, /* interface to igraph_constraint */ {"constraint", (PyCFunction) igraphmodule_Graph_constraint, METH_VARARGS | METH_KEYWORDS, "constraint(vertices=None, weights=None)\n\n" "Calculates Burt's constraint scores for given vertices in a graph.\n\n" "Burt's constraint is higher if ego has less, or mutually stronger\n" "related (i.e. more redundant) contacts. Burt's measure of\n" "constraint, C[i], of vertex i's ego network V[i], is defined for\n" "directed and valued graphs as follows:\n\n" "C[i] = sum( sum( (p[i,q] p[q,j])^2, q in V[i], q != i,j ), j in V[], j != i)\n\n" "for a graph of order (ie. number od vertices) N, where proportional\n" "tie strengths are defined as follows:\n\n" "p[i,j]=(a[i,j]+a[j,i]) / sum(a[i,k]+a[k,i], k in V[i], k != i),\n" "a[i,j] are elements of A and the latter being the graph adjacency matrix.\n\n" "For isolated vertices, constraint is undefined.\n\n" "@param vertices: the vertices to be analysed or C{None} for all vertices.\n" "@param weights: weights associated to the edges. Can be an attribute name\n" " as well. If C{None}, every edge will have the same weight.\n" "@return: constraint scores for all given vertices in a matrix."}, /* interface to igraph_density */ {"density", (PyCFunction) igraphmodule_Graph_density, METH_VARARGS | METH_KEYWORDS, "density(loops=False)\n\n" "Calculates the density of the graph.\n\n" "@param loops: whether to take loops into consideration. If C{True},\n" " the algorithm assumes that there might be some loops in the graph\n" " and calculates the density accordingly. If C{False}, the algorithm\n" " assumes that there can't be any loops.\n" "@return: the reciprocity of the graph."}, /* interfaces to igraph_diameter */ {"diameter", (PyCFunction) igraphmodule_Graph_diameter, METH_VARARGS | METH_KEYWORDS, "diameter(directed=True, unconn=True, weights=None)\n\n" "Calculates the diameter of the graph.\n\n" "@param directed: whether to consider directed paths.\n" "@param unconn: if C{True} and the graph is unconnected, the\n" " longest geodesic within a component will be returned. If\n" " C{False} and the graph is unconnected, the result is the\n" " number of vertices if there are no weights or infinity\n" " if there are weights.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@return: the diameter"}, {"get_diameter", (PyCFunction) igraphmodule_Graph_get_diameter, METH_VARARGS | METH_KEYWORDS, "get_diameter(directed=True, unconn=True, weights=None)\n\n" "Returns a path with the actual diameter of the graph.\n\n" "If there are many shortest paths with the length of the diameter,\n" "it returns the first one it founds.\n\n" "@param directed: whether to consider directed paths.\n" "@param unconn: if C{True} and the graph is unconnected, the\n" " longest geodesic within a component will be returned. If\n" " C{False} and the graph is unconnected, the result is the\n" " number of vertices if there are no weights or infinity\n" " if there are weights.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@return: the vertices in the path in order."}, {"farthest_points", (PyCFunction) igraphmodule_Graph_farthest_points, METH_VARARGS | METH_KEYWORDS, "farthest_points(directed=True, unconn=True, weights=None)\n\n" "Returns two vertex IDs whose distance equals the actual diameter\n" "of the graph.\n\n" "If there are many shortest paths with the length of the diameter,\n" "it returns the first one it found.\n\n" "@param directed: whether to consider directed paths.\n" "@param unconn: if C{True} and the graph is unconnected, the\n" " longest geodesic within a component will be returned. If\n" " C{False} and the graph is unconnected, the result contains the\n" " number of vertices if there are no weights or infinity\n" " if there are weights.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@return: a triplet containing the two vertex IDs and their distance.\n" " The IDs are C{None} if the graph is unconnected and C{unconn}\n" " is C{False}."}, /* interface to igraph_diversity */ {"diversity", (PyCFunction) igraphmodule_Graph_diversity, METH_VARARGS | METH_KEYWORDS, "diversity(vertices=None, weights=None)\n\n" "Calculates the structural diversity index of the vertices.\n\n" "The structural diversity index of a vertex is simply the (normalized)\n" "Shannon entropy of the weights of the edges incident on the vertex.\n\n" "The measure is defined for undirected graphs only; edge directions are\n" "ignored.\n\n" "@param vertices: the vertices for which the diversity indices must\n" " be returned. If C{None}, uses all of the vertices in the graph.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@return: the calculated diversity indices in a list, or a single number if\n" " a single vertex was supplied.\n" "@newfield ref: Reference\n" "@ref: Eagle N, Macy M and Claxton R: Network diversity and economic\n" " development, Science 328, 1029--1031, 2010." }, /* interface to igraph_eccentricity */ {"eccentricity", (PyCFunction) igraphmodule_Graph_eccentricity, METH_VARARGS | METH_KEYWORDS, "eccentricity(vertices=None, mode=ALL)\n\n" "Calculates the eccentricities of given vertices in a graph.\n\n" "The eccentricity of a vertex is calculated by measuring the\n" "shortest distance from (or to) the vertex, to (or from) all other\n" "vertices in the graph, and taking the maximum.\n\n" "@param vertices: the vertices for which the eccentricity scores must\n" " be returned. If C{None}, uses all of the vertices in the graph.\n" "@param mode: must be one of L{IN}, L{OUT} and L{ALL}. L{IN} means\n" " that edge directions are followed; C{OUT} means that edge directions\n" " are followed the opposite direction; C{ALL} means that directions are\n" " ignored. The argument has no effect for undirected graphs.\n" "@return: the calculated eccentricities in a list, or a single number if\n" " a single vertex was supplied.\n"}, /* interface to igraph_edge_betweenness[_estimate] */ {"edge_betweenness", (PyCFunction) igraphmodule_Graph_edge_betweenness, METH_VARARGS | METH_KEYWORDS, "edge_betweenness(directed=True, cutoff=None, weights=None)\n\n" "Calculates or estimates the edge betweennesses in a graph.\n\n" "@param directed: whether to consider directed paths.\n" "@param cutoff: if it is an integer, only paths less than or equal to this\n" " length are considered, effectively resulting in an estimation of the\n" " betweenness values. If C{None}, the exact betweennesses are\n" " returned.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@return: a list with the (exact or estimated) edge betweennesses of all\n" " edges.\n"}, {"eigen_adjacency", (PyCFunction) igraphmodule_Graph_eigen_adjacency, METH_VARARGS | METH_KEYWORDS, "" }, /* interface to igraph_[st_]edge_connectivity */ {"edge_connectivity", (PyCFunction) igraphmodule_Graph_edge_connectivity, METH_VARARGS | METH_KEYWORDS, "edge_connectivity(source=-1, target=-1, checks=True)\n\n" "Calculates the edge connectivity of the graph or between some vertices.\n\n" "The edge connectivity between two given vertices is the number of edges\n" "that have to be removed in order to disconnect the two vertices into two\n" "separate components. This is also the number of edge disjoint directed\n" "paths between the vertices. The edge connectivity of the graph is the minimal\n" "edge connectivity over all vertex pairs.\n\n" "This method calculates the edge connectivity of a given vertex pair if both\n" "the source and target vertices are given. If none of them is given (or they\n" "are both negative), the overall edge connectivity is returned.\n\n" "@param source: the source vertex involved in the calculation.\n" "@param target: the target vertex involved in the calculation.\n" "@param checks: if the whole graph connectivity is calculated and this is\n" " C{True}, igraph performs some basic checks before calculation. If the\n" " graph is not strongly connected, then the connectivity is obviously\n" " zero. If the minimum degree is one, then the connectivity is\n" " also one. These simple checks are much faster than checking the entire\n" " graph, therefore it is advised to set this to C{True}. The parameter\n" " is ignored if the connectivity between two given vertices is computed.\n" "@return: the edge connectivity\n" }, /* interface to igraph_eigenvector_centrality */ {"eigenvector_centrality", (PyCFunction) igraphmodule_Graph_eigenvector_centrality, METH_VARARGS | METH_KEYWORDS, "eigenvector_centrality(directed=True, scale=True, weights=None, return_eigenvalue=False, arpack_options=None)\n\n" "Calculates the eigenvector centralities of the vertices in a graph.\n\n" "@param directed: whether to consider edge directions in a directed\n" " graph. Ignored for undirected graphs.\n" "@param scale: whether to normalize the centralities so the largest\n" " one will always be 1.\n" "@param weights: edge weights given as a list or an edge attribute. If\n" " C{None}, all edges have equal weight.\n" "@param return_eigenvalue: whether to return the actual largest\n" " eigenvalue along with the centralities\n" "@param arpack_options: an L{ARPACKOptions} object that can be used\n" " to fine-tune the calculation. If it is omitted, the module-level\n" " variable called C{arpack_options} is used.\n" "@return: the eigenvector centralities in a list and optionally the\n" " largest eigenvalue (as a second member of a tuple)" }, /* interface to igraph_feedback_arc_set */ {"feedback_arc_set", (PyCFunction) igraphmodule_Graph_feedback_arc_set, METH_VARARGS | METH_KEYWORDS, "feedback_arc_set(weights=None, method=\"eades\")\n\n" "Calculates an approximately or exactly minimal feedback arc set.\n\n" "A feedback arc set is a set of edges whose removal makes the graph acyclic.\n" "Since this is always possible by removing all the edges, we are in general\n" "interested in removing the smallest possible number of edges, or an edge set\n" "with as small total weight as possible. This method calculates one such edge\n" "set. Note that the task is trivial for an undirected graph as it is enough\n" "to find a spanning tree and then remove all the edges not in the spanning\n" "tree. Of course it is more complicated for directed graphs.\n\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name. When given, the algorithm will strive to\n" " remove lightweight edges in order to minimize the total weight of the\n" " feedback arc set.\n" "@param method: the algorithm to use. C{\"eades\"} uses the greedy cycle\n" " breaking heuristic of Eades, Lin and Smyth, which is linear in the number\n" " of edges but not necessarily optimal; however, it guarantees that the\n" " number of edges to be removed is smaller than |E|/2 - |V|/6. C{\"ip\"} uses\n" " an integer programming formulation which is guaranteed to yield an optimal\n" " result, but is too slow for large graphs.\n" "@return: the IDs of the edges to be removed, in a list.\n\n" "@newfield ref: Reference\n" "@ref: Eades P, Lin X and Smyth WF: A fast and effective heuristic for the\n" " feedback arc set problem. In: Proc Inf Process Lett 319-323, 1993.\n" }, // interface to igraph_get_shortest_paths {"get_shortest_paths", (PyCFunction) igraphmodule_Graph_get_shortest_paths, METH_VARARGS | METH_KEYWORDS, "get_shortest_paths(v, to=None, weights=None, mode=OUT, output=\"vpath\")\n\n" "Calculates the shortest paths from/to a given node in a graph.\n\n" "@param v: the source/destination for the calculated paths\n" "@param to: a vertex selector describing the destination/source for\n" " the calculated paths. This can be a single vertex ID, a list of\n" " vertex IDs, a single vertex name, a list of vertex names or a\n" " L{VertexSeq} object. C{None} means all the vertices.\n" "@param weights: edge weights in a list or the name of an edge attribute\n" " holding edge weights. If C{None}, all edges are assumed to have\n" " equal weight.\n" "@param mode: the directionality of the paths. L{IN} means to\n" " calculate incoming paths, L{OUT} means to calculate outgoing\n" " paths, L{ALL} means to calculate both ones.\n" "@param output: determines what should be returned. If this is\n" " C{\"vpath\"}, a list of vertex IDs will be returned, one path\n" " for each target vertex. For unconnected graphs, some of the list\n" " elements may be empty. Note that in case of mode=L{IN}, the vertices\n" " in a path are returned in reversed order. If C{output=\"epath\"},\n" " edge IDs are returned instead of vertex IDs.\n" "@return: see the documentation of the C{output} parameter.\n"}, /* interface to igraph_get_all_shortest_paths */ {"get_all_shortest_paths", (PyCFunction) igraphmodule_Graph_get_all_shortest_paths, METH_VARARGS | METH_KEYWORDS, "get_all_shortest_paths(v, to=None, weights=None, mode=OUT)\n\n" "Calculates all of the shortest paths from/to a given node in a graph.\n\n" "@param v: the source for the calculated paths\n" "@param to: a vertex selector describing the destination for\n" " the calculated paths. This can be a single vertex ID, a list of\n" " vertex IDs, a single vertex name, a list of vertex names or a\n" " L{VertexSeq} object. C{None} means all the vertices.\n" "@param weights: edge weights in a list or the name of an edge attribute\n" " holding edge weights. If C{None}, all edges are assumed to have\n" " equal weight.\n" "@param mode: the directionality of the paths. L{IN} means to\n" " calculate incoming paths, L{OUT} means to calculate outgoing\n" " paths, L{ALL} means to calculate both ones.\n" "@return: all of the shortest path from the given node to every other\n" " reachable node in the graph in a list. Note that in case of mode=L{IN},\n" " the vertices in a path are returned in reversed order!"}, /* interface to igraph_get_all_simple_paths */ /* {"_get_all_simple_paths", (PyCFunction) igraphmodule_Graph_get_all_simple_paths, METH_VARARGS | METH_KEYWORDS, "_get_all_simple_paths(v, to=None, mode=OUT)\n\n" "Internal function, undocumented.\n\n" "@see: Graph.get_all_simple_paths()\n\n" }, */ /* interface to igraph_girth */ {"girth", (PyCFunction)igraphmodule_Graph_girth, METH_VARARGS | METH_KEYWORDS, "girth(return_shortest_circle=False)\n\n" "Returns the girth of the graph.\n\n" "The girth of a graph is the length of the shortest circle in it.\n\n" "@param return_shortest_circle: whether to return one of the shortest\n" " circles found in the graph.\n" "@return: the length of the shortest circle or (if C{return_shortest_circle})\n" " is true, the shortest circle itself as a list\n" }, /* interface to igraph_convergence_degree */ {"convergence_degree", (PyCFunction)igraphmodule_Graph_convergence_degree, METH_NOARGS, "convergence_degree()\n\n" "Undocumented (yet)." }, /* interface to igraph_convergence_field_size */ {"convergence_field_size", (PyCFunction)igraphmodule_Graph_convergence_field_size, METH_NOARGS, "convergence_field_size()\n\n" "Undocumented (yet)." }, /* interface to igraph_hub_score */ {"hub_score", (PyCFunction)igraphmodule_Graph_hub_score, METH_VARARGS | METH_KEYWORDS, "hub_score(weights=None, scale=True, arpack_options=None, return_eigenvalue=False)\n\n" "Calculates Kleinberg's hub score for the vertices of the graph\n\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@param scale: whether to normalize the scores so that the largest one\n" " is 1.\n" "@param arpack_options: an L{ARPACKOptions} object used to fine-tune\n" " the ARPACK eigenvector calculation. If omitted, the module-level\n" " variable called C{arpack_options} is used.\n" "@param return_eigenvalue: whether to return the largest eigenvalue\n" "@return: the hub scores in a list and optionally the largest eigenvalue\n" " as a second member of a tuple\n\n" "@see: authority_score()\n" }, /* interface to igraph_induced_subgraph */ {"induced_subgraph", (PyCFunction) igraphmodule_Graph_induced_subgraph, METH_VARARGS | METH_KEYWORDS, "induced_subgraph(vertices, implementation=\"auto\")\n\n" "Returns a subgraph spanned by the given vertices.\n\n" "@param vertices: a list containing the vertex IDs which\n" " should be included in the result.\n" "@param implementation: the implementation to use when constructing\n" " the new subgraph. igraph includes two implementations at the\n" " moment. C{\"copy_and_delete\"} copies the original graph and\n" " removes those vertices that are not in the given set. This is more\n" " efficient if the size of the subgraph is comparable to the original\n" " graph. The other implementation (C{\"create_from_scratch\"})\n" " constructs the result graph from scratch and then copies the\n" " attributes accordingly. This is a better solution if the subgraph\n" " is relatively small, compared to the original graph. C{\"auto\"}\n" " selects between the two implementations automatically, based on\n" " the ratio of the size of the subgraph and the size of the original\n" " graph.\n" "@return: the subgraph\n"}, /* interface to igraph_is_bipartite */ {"is_bipartite", (PyCFunction) igraphmodule_Graph_is_bipartite, METH_VARARGS | METH_KEYWORDS, "is_bipartite(return_types=False)\n\n" "Decides whether the graph is bipartite or not.\n\n" "Vertices of a bipartite graph can be partitioned into two groups A\n" "and B in a way that all edges go between the two groups.\n\n" "@param return_types: if C{False}, the method will simply\n" " return C{True} or C{False} depending on whether the graph is\n" " bipartite or not. If C{True}, the actual group assignments\n" " are also returned as a list of boolean values. (Note that\n" " the group assignment is not unique, especially if the graph\n" " consists of multiple components, since the assignments of\n" " components are independent from each other).\n" "@return: C{True} if the graph is bipartite, C{False} if not.\n" " If C{return_types} is C{True}, the group assignment is also\n" " returned.\n" }, /* interface to igraph_avg_nearest_neighbor_degree */ {"knn", (PyCFunction) igraphmodule_Graph_knn, METH_VARARGS | METH_KEYWORDS, "knn(vids=None, weights=None)\n\n" "Calculates the average degree of the neighbors for each vertex, and\n" "the same quantity as the function of vertex degree.\n\n" "@param vids: the vertices for which the calculation is performed.\n" " C{None} means all vertices.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name. If this is given, the vertex strength\n" " will be used instead of the vertex degree in the calculations, but\n" " the \"ordinary\" vertex degree will be used for the second (degree-\n" " dependent) list in the result.\n" "@return: two lists in a tuple. The first list contains the average\n" " degree of neighbors for each vertex, the second contains the average\n" " degree of neighbors as a function of vertex degree. The zeroth element\n" " of this list corresponds to vertices of degree 1.\n" }, /* interface to igraph_is_connected */ {"is_connected", (PyCFunction) igraphmodule_Graph_is_connected, METH_VARARGS | METH_KEYWORDS, "is_connected(mode=STRONG)\n\n" "Decides whether the graph is connected.\n\n" "@param mode: whether we should calculate strong or weak connectivity.\n" "@return: C{True} if the graph is connected, C{False} otherwise.\n"}, /* interface to igraph_linegraph */ {"linegraph", (PyCFunction) igraphmodule_Graph_linegraph, METH_VARARGS | METH_KEYWORDS, "linegraph()\n\n" "Returns the line graph of the graph.\n\n" "The line graph M{L(G)} of an undirected graph is defined as follows:\n" "M{L(G)} has one vertex for each edge in G and two vertices in M{L(G)}\n" "are connected iff their corresponding edges in the original graph\n" "share an end point.\n\n" "The line graph of a directed graph is slightly different: two vertices\n" "are connected by a directed edge iff the target of the first vertex's\n" "corresponding edge is the same as the source of the second vertex's\n" "corresponding edge.\n" }, /* interface to igraph_maxdegree */ {"maxdegree", (PyCFunction) igraphmodule_Graph_maxdegree, METH_VARARGS | METH_KEYWORDS, "maxdegree(vertices=None, mode=ALL, loops=False)\n\n" "Returns the maximum degree of a vertex set in the graph.\n\n" "This method accepts a single vertex ID or a list of vertex IDs as a\n" "parameter, and returns the degree of the given vertices (in the\n" "form of a single integer or a list, depending on the input\n" "parameter).\n" "\n" "@param vertices: a single vertex ID or a list of vertex IDs, or\n" " C{None} meaning all the vertices in the graph.\n" "@param mode: the type of degree to be returned (L{OUT} for\n" " out-degrees, L{IN} IN for in-degrees or L{ALL} for the sum of\n" " them).\n" "@param loops: whether self-loops should be counted.\n"}, /* interface to igraph_neighborhood */ {"neighborhood", (PyCFunction) igraphmodule_Graph_neighborhood, METH_VARARGS | METH_KEYWORDS, "neighborhood(vertices=None, order=1, mode=ALL)\n\n" "For each vertex specified by I{vertices}, returns the\n" "vertices reachable from that vertex in at most I{order} steps.\n\n" "@param vertices: a single vertex ID or a list of vertex IDs, or\n" " C{None} meaning all the vertices in the graph.\n" "@param order: the order of the neighborhood, i.e. the maximum number of\n" " steps to take from the seed vertex.\n" "@param mode: specifies how to take into account the direction of\n" " the edges if a directed graph is analyzed. C{\"out\"} means that\n" " only the outgoing edges are followed, so all vertices reachable\n" " from the source vertex in at most I{order} steps are counted.\n" " C{\"in\"} means that only the incoming edges are followed (in\n" " reverse direction of course), so all vertices from which the source\n" " vertex is reachable in at most I{order} steps are counted. C{\"all\"}\n" " treats directed edges as undirected.\n" "@return: a single list specifying the neighborhood if I{vertices}\n" " was an integer specifying a single vertex index, or a list of lists\n" " if I{vertices} was a list or C{None}.\n" }, /* interface to igraph_neighborhood_size */ {"neighborhood_size", (PyCFunction) igraphmodule_Graph_neighborhood_size, METH_VARARGS | METH_KEYWORDS, "neighborhood_size(vertices=None, order=1, mode=ALL)\n\n" "For each vertex specified by I{vertices}, returns the number of\n" "vertices reachable from that vertex in at most I{order} steps.\n\n" "@param vertices: a single vertex ID or a list of vertex IDs, or\n" " C{None} meaning all the vertices in the graph.\n" "@param order: the order of the neighborhood, i.e. the maximum number of\n" " steps to take from the seed vertex.\n" "@param mode: specifies how to take into account the direction of\n" " the edges if a directed graph is analyzed. C{\"out\"} means that\n" " only the outgoing edges are followed, so all vertices reachable\n" " from the source vertex in at most I{order} steps are counted.\n" " C{\"in\"} means that only the incoming edges are followed (in\n" " reverse direction of course), so all vertices from which the source\n" " vertex is reachable in at most I{order} steps are counted. C{\"all\"}\n" " treats directed edges as undirected.\n" "@return: a single number specifying the neighborhood size if I{vertices}\n" " was an integer specifying a single vertex index, or a list of sizes\n" " if I{vertices} was a list or C{None}.\n" }, /* interface to igraph_personalized_pagerank */ {"personalized_pagerank", (PyCFunction) igraphmodule_Graph_personalized_pagerank, METH_VARARGS | METH_KEYWORDS, "personalized_pagerank(vertices=None, directed=True, damping=0.85,\n" " reset=None, reset_vertices=None, weights=None, \n" " arpack_options=None, implementation=\"prpack\", niter=1000,\n" " eps=0.001)\n\n" "Calculates the personalized PageRank values of a graph.\n\n" "The personalized PageRank calculation is similar to the PageRank\n" "calculation, but the random walk is reset to a non-uniform distribution\n" "over the vertices in every step with probability M{1-damping} instead of a\n" "uniform distribution.\n\n" "@param vertices: the indices of the vertices being queried.\n" " C{None} means all of the vertices.\n" "@param directed: whether to consider directed paths.\n" "@param damping: the damping factor.\n" " M{1-damping} is the PageRank value for vertices with no\n" " incoming links.\n" "@param reset: the distribution over the vertices to be used when resetting\n" " the random walk. Can be a sequence, an iterable or a vertex attribute\n" " name as long as they return a list of floats whose length is equal to\n" " the number of vertices. If C{None}, a uniform distribution is assumed,\n" " which makes the method equivalent to the original PageRank algorithm.\n" "@param reset_vertices: an alternative way to specify the distribution\n" " over the vertices to be used when resetting the random walk. Simply\n" " supply a list of vertex IDs here, or a L{VertexSeq} or a L{Vertex}.\n" " Resetting will take place using a uniform distribution over the specified\n" " vertices.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@param arpack_options: an L{ARPACKOptions} object used to fine-tune\n" " the ARPACK eigenvector calculation. If omitted, the module-level\n" " variable called C{arpack_options} is used. This argument is\n" " ignored if not the ARPACK implementation is used, see the \n" " I{implementation} argument.\n" "@param implementation: which implementation to use to solve the \n" " PageRank eigenproblem. Possible values are:\n\n" " - C{\"prpack\"}: use the PRPACK library. This is a new \n" " implementation in igraph 0.7\n\n" " - C{\"arpack\"}: use the ARPACK library. This implementation\n" " was used from version 0.5, until version 0.7.\n\n" " - C{\"power\"}: use a simple power method. This is the\n" " implementation that was used before igraph version 0.5.\n\n" "@param niter: The number of iterations to use in the power method\n" " implementation. It is ignored in the other implementations.\n" "@param eps: The power method implementation will consider the\n" " calculation as complete if the difference of PageRank values between\n" " iterations change less than this value for every node. It is \n" " ignored by the other implementations.\n" "@return: a list with the personalized PageRank values of the specified\n" " vertices.\n"}, /* interface to igraph_path_length_hist */ {"path_length_hist", (PyCFunction) igraphmodule_Graph_path_length_hist, METH_VARARGS | METH_KEYWORDS, "path_length_hist(directed=True)\n\n" "Calculates the path length histogram of the graph\n" "@attention: this function is wrapped in a more convenient syntax in the\n" " derived class L{Graph}. It is advised to use that instead of this version.\n\n" "@param directed: whether to consider directed paths\n" "@return: a tuple. The first item of the tuple is a list of path lengths,\n" " the M{i}th element of the list contains the number of paths with length\n" " M{i+1}. The second item contains the number of unconnected vertex pairs\n" " as a float (since it might not fit into an integer)\n" }, /* interface to igraph_permute_vertices */ {"permute_vertices", (PyCFunction) igraphmodule_Graph_permute_vertices, METH_VARARGS | METH_KEYWORDS, "permute_vertices(permutation)\n\n" "Permutes the vertices of the graph according to the given permutation\n" "and returns the new graph.\n\n" "Vertex M{k} of the original graph will become vertex M{permutation[k]}\n" "in the new graph. No validity checks are performed on the permutation\n" "vector.\n\n" "@return: the new graph\n" }, /* interfaces to igraph_radius */ {"radius", (PyCFunction) igraphmodule_Graph_radius, METH_VARARGS | METH_KEYWORDS, "radius(mode=OUT)\n\n" "Calculates the radius of the graph.\n\n" "The radius of a graph is defined as the minimum eccentricity of\n" "its vertices (see L{eccentricity()}).\n" "@param mode: what kind of paths to consider for the calculation\n" " in case of directed graphs. C{OUT} considers paths that follow\n" " edge directions, C{IN} considers paths that follow the opposite\n" " edge directions, C{ALL} ignores edge directions. The argument is\n" " ignored for undirected graphs.\n" "@return: the radius\n" "@see: L{Graph.eccentricity()}" }, /* interface to igraph_reciprocity */ {"reciprocity", (PyCFunction) igraphmodule_Graph_reciprocity, METH_VARARGS | METH_KEYWORDS, "reciprocity(ignore_loops=True, mode=\"default\")\n\n" "Reciprocity defines the proportion of mutual connections in a\n" "directed graph. It is most commonly defined as the probability\n" "that the opposite counterpart of a directed edge is also included\n" "in the graph. This measure is calculated if C{mode} is C{\"default\"}.\n" "\n" "Prior to igraph 0.6, another measure was implemented, defined as\n" "the probability of mutual connection between a vertex pair if we\n" "know that there is a (possibly non-mutual) connection between them.\n" "In other words, (unordered) vertex pairs are classified into three\n" "groups: (1) disconnected, (2) non-reciprocally connected and (3)\n" "reciprocally connected. The result is the size of group (3), divided\n" "by the sum of sizes of groups (2) and (3). This measure is calculated\n" "if C{mode} is C{\"ratio\"}.\n" "\n" "@param ignore_loops: whether loop edges should be ignored.\n" "@param mode: the algorithm to use to calculate the reciprocity; see\n" " above for more details.\n" "@return: the reciprocity of the graph\n" }, /* interface to igraph_rewire */ {"rewire", (PyCFunction) igraphmodule_Graph_rewire, METH_VARARGS | METH_KEYWORDS, "rewire(n=1000, mode=\"simple\")\n\n" "Randomly rewires the graph while preserving the degree distribution.\n\n" "Please note that the rewiring is done \"in-place\", so the original\n" "graph will be modified. If you want to preserve the original graph,\n" "use the L{copy} method before.\n\n" "@param n: the number of rewiring trials.\n" "@param mode: the rewiring algorithm to use. It can either be C{\"simple\"} or\n" " C{\"loops\"}; the former does not create or destroy loop edges while the\n" " latter does.\n"}, /* interface to igraph_rewire_edges */ {"rewire_edges", (PyCFunction) igraphmodule_Graph_rewire_edges, METH_VARARGS | METH_KEYWORDS, "rewire_edges(prob, loops=False, multiple=False)\n\n" "Rewires the edges of a graph with constant probability.\n\n" "Each endpoint of each edge of the graph will be rewired with a constant\n" "probability, given in the first argument.\n\n" "Please note that the rewiring is done \"in-place\", so the original\n" "graph will be modified. If you want to preserve the original graph,\n" "use the L{copy} method before.\n\n" "@param prob: rewiring probability\n" "@param loops: whether the algorithm is allowed to create loop edges\n" "@param multiple: whether the algorithm is allowed to create multiple\n" " edges.\n"}, /* interface to igraph_shortest_paths */ {"shortest_paths", (PyCFunction) igraphmodule_Graph_shortest_paths, METH_VARARGS | METH_KEYWORDS, "shortest_paths(source=None, target=None, weights=None, mode=OUT)\n\n" "Calculates shortest path lengths for given vertices in a graph.\n\n" "The algorithm used for the calculations is selected automatically:\n" "a simple BFS is used for unweighted graphs, Dijkstra's algorithm is\n" "used when all the weights are positive. Otherwise, the Bellman-Ford\n" "algorithm is used if the number of requested source vertices is larger\n" "than 100 and Johnson's algorithm is used otherwise.\n\n" "@param source: a list containing the source vertex IDs which should be\n" " included in the result. If C{None}, all vertices will be considered.\n" "@param target: a list containing the target vertex IDs which should be\n" " included in the result. If C{None}, all vertices will be considered.\n" "@param weights: a list containing the edge weights. It can also be\n" " an attribute name (edge weights are retrieved from the given\n" " attribute) or C{None} (all edges have equal weight).\n" "@param mode: the type of shortest paths to be used for the\n" " calculation in directed graphs. L{OUT} means only outgoing,\n" " L{IN} means only incoming paths. L{ALL} means to consider\n" " the directed graph as an undirected one.\n" "@return: the shortest path lengths for given vertices in a matrix\n"}, /* interface to igraph_simplify */ {"simplify", (PyCFunction) igraphmodule_Graph_simplify, METH_VARARGS | METH_KEYWORDS, "simplify(multiple=True, loops=True, combine_edges=None)\n\n" "Simplifies a graph by removing self-loops and/or multiple edges.\n\n" "\n" "For example, suppose you have a graph with an edge attribute named\n" "C{weight}. C{graph.simplify(combine_edges=max)} will take the\n" "maximum of the weights of multiple edges and assign that weight to\n" "the collapsed edge. C{graph.simplify(combine_edges=sum)} will\n" "take the sum of the weights. You can also write\n" "C{graph.simplify(combine_edges=dict(weight=\"sum\"))} or\n" "C{graph.simplify(combine_edges=dict(weight=sum))}, since\n" "C{sum} is recognised both as a Python built-in function and as\n" "a string constant.\n\n" "@param multiple: whether to remove multiple edges.\n" "@param loops: whether to remove loops.\n" "@param combine_edges: specifies how to combine the attributes of\n" " multiple edges between the same pair of vertices into a single\n" " attribute. If it is C{None}, only one of the edges will be kept\n" " and all the attributes will be lost. If it is a function, the\n" " attributes of multiple edges will be collected and passed on to\n" " that function which will return the new attribute value that has to\n" " be assigned to the single collapsed edge. It can also be one of\n" " the following string constants:\n\n" " - C{\"ignore\"}: all the edge attributes will be ignored.\n\n" " - C{\"sum\"}: the sum of the edge attribute values will be used for\n" " the new edge.\n\n" " - C{\"product\"}: the product of the edge attribute values will be used for\n" " the new edge.\n" " - C{\"mean\"}: the mean of the edge attribute values will be used for\n" " the new edge.\n\n" " - C{\"median\"}: the median of the edge attribute values will be used for\n" " the new edge.\n\n" " - C{\"min\"}: the minimum of the edge attribute values will be used for\n" " the new edge.\n\n" " - C{\"max\"}: the maximum of the edge attribute values will be used for\n" " the new edge.\n\n" " - C{\"first\"}: the attribute value of the first edge in the collapsed set\n" " will be used for the new edge.\n\n" " - C{\"last\"}: the attribute value of the last edge in the collapsed set\n" " will be used for the new edge.\n\n" " - C{\"random\"}: a randomly selected value will be used for the new edge\n\n" " - C{\"concat\"}: the attribute values will be concatenated for the new\n" " edge.\n\n" " You can also use a dict mapping edge attribute names to functions or\n" " the above string constants if you want to make the behaviour of the\n" " simplification process depend on the name of the attribute.\n" " C{None} is a special key in this dict, its value will be used for all\n" " the attributes not specified explicitly in the dictionary.\n" }, /* interface to igraph_minimum_spanning_tree */ {"_spanning_tree", (PyCFunction) igraphmodule_Graph_spanning_tree, METH_VARARGS | METH_KEYWORDS, "_spanning_tree(weights=None)\n\n" "Internal function, undocumented.\n\n" "@see: Graph.spanning_tree()"}, // interface to igraph_subcomponent {"subcomponent", (PyCFunction) igraphmodule_Graph_subcomponent, METH_VARARGS | METH_KEYWORDS, "subcomponent(v, mode=ALL)\n\n" "Determines the indices of vertices which are in the same component as a given vertex.\n\n" "@param v: the index of the vertex used as the source/destination\n" "@param mode: if equals to L{IN}, returns the vertex IDs from\n" " where the given vertex can be reached. If equals to L{OUT},\n" " returns the vertex IDs which are reachable from the given\n" " vertex. If equals to L{ALL}, returns all vertices within the\n" " same component as the given vertex, ignoring edge directions.\n" " Note that this is not equal to calculating the union of the \n" " results of L{IN} and L{OUT}.\n" "@return: the indices of vertices which are in the same component as a given vertex.\n"}, /* interface to igraph_subgraph_edges */ {"subgraph_edges", (PyCFunction) igraphmodule_Graph_subgraph_edges, METH_VARARGS | METH_KEYWORDS, "subgraph_edges(edges, delete_vertices=True)\n\n" "Returns a subgraph spanned by the given edges.\n\n" "@param edges: a list containing the edge IDs which should\n" " be included in the result.\n" "@param delete_vertices: if C{True}, vertices not incident on\n" " any of the specified edges will be deleted from the result.\n" " If C{False}, all vertices will be kept.\n" "@return: the subgraph\n"}, /* interface to igraph_topological_sorting */ {"topological_sorting", (PyCFunction) igraphmodule_Graph_topological_sorting, METH_VARARGS | METH_KEYWORDS, "topological_sorting(mode=OUT)\n\n" "Calculates a possible topological sorting of the graph.\n\n" "Returns a partial sorting and issues a warning if the graph is not\n" "a directed acyclic graph.\n\n" "@param mode: if L{OUT}, vertices are returned according to the\n" " forward topological order -- all vertices come before their\n" " successors. If L{IN}, all vertices come before their ancestors.\n" "@return: a possible topological ordering as a list"}, // interface to igraph_transitivity_undirected {"transitivity_undirected", (PyCFunction) igraphmodule_Graph_transitivity_undirected, METH_VARARGS | METH_KEYWORDS, "transitivity_undirected(mode=\"nan\")\n\n" "Calculates the global transitivity (clustering coefficient) of the\n" "graph.\n\n" "The transitivity measures the probability that two neighbors of a\n" "vertex are connected. More precisely, this is the ratio of the\n" "triangles and connected triplets in the graph. The result is a\n" "single real number. Directed graphs are considered as undirected\n" "ones.\n\n" "Note that this measure is different from the local transitivity\n" "measure (see L{transitivity_local_undirected()}) as it calculates\n" "a single value for the whole graph.\n\n" "@param mode: if C{TRANSITIVITY_ZERO} or C{\"zero\"}, the result will\n" " be zero if the graph does not have any triplets. If C{\"nan\"} or\n" " C{TRANSITIVITY_NAN}, the result will be C{NaN} (not a number).\n" "@return: the transitivity\n" "@see: L{transitivity_local_undirected()}, L{transitivity_avglocal_undirected()}\n" "@newfield ref: Reference\n" "@ref: S. Wasserman and K. Faust: I{Social Network Analysis: Methods and\n" " Applications}. Cambridge: Cambridge University Press, 1994." }, /* interface to igraph_transitivity_local_undirected and * igraph_transitivity_barrat */ {"transitivity_local_undirected", (PyCFunction) igraphmodule_Graph_transitivity_local_undirected, METH_VARARGS | METH_KEYWORDS, "transitivity_local_undirected(vertices=None, mode=\"nan\", weights=None)\n\n" "Calculates the local transitivity (clustering coefficient) of the\n" "given vertices in the graph.\n\n" "The transitivity measures the probability that two neighbors of a\n" "vertex are connected. In case of the local transitivity, this\n" "probability is calculated separately for each vertex.\n\n" "Note that this measure is different from the global transitivity\n" "measure (see L{transitivity_undirected()}) as it calculates\n" "a transitivity value for each vertex individually.\n\n" "The traditional local transitivity measure applies for unweighted graphs\n" "only. When the C{weights} argument is given, this function calculates\n" "the weighted local transitivity proposed by Barrat et al (see references).\n\n" "@param vertices: a list containing the vertex IDs which should be\n" " included in the result. C{None} means all of the vertices.\n" "@param mode: defines how to treat vertices with degree less than two.\n" " If C{TRANSITIVITT_ZERO} or C{\"zero\"}, these vertices will have\n" " zero transitivity. If C{TRANSITIVITY_NAN} or C{\"nan\"}, these\n" " vertices will have C{NaN} (not a number) as their transitivity.\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@return: the transitivities for the given vertices in a list\n" "@see: L{transitivity_undirected()}, L{transitivity_avglocal_undirected()}\n" "@newfield ref: Reference\n" "@ref: Watts DJ and Strogatz S: I{Collective dynamics of small-world\n" " networks}. Nature 393(6884):440-442, 1998.\n" "@ref: Barrat A, Barthelemy M, Pastor-Satorras R and Vespignani A:\n" " I{The architecture of complex weighted networks}. PNAS 101, 3747 (2004).\n" " U{http://arxiv.org/abs/cond-mat/0311416}." }, /* interface to igraph_transitivity_avglocal_undirected */ {"transitivity_avglocal_undirected", (PyCFunction) igraphmodule_Graph_transitivity_avglocal_undirected, METH_VARARGS | METH_KEYWORDS, "transitivity_avglocal_undirected(mode=\"nan\")\n\n" "Calculates the average of the vertex transitivities of the graph.\n\n" "The transitivity measures the probability that two neighbors of a\n" "vertex are connected. In case of the average local transitivity,\n" "this probability is calculated for each vertex and then the average\n" "is taken. Vertices with less than two neighbors require special\n" "treatment, they will either be left out from the calculation or\n" "they will be considered as having zero transitivity, depending on\n" "the I{mode} parameter.\n\n" "Note that this measure is different from the global transitivity measure\n" "(see L{transitivity_undirected()}) as it simply takes the average local\n" "transitivity across the whole network.\n\n" "@param mode: defines how to treat vertices with degree less than two.\n" " If C{TRANSITIVITT_ZERO} or C{\"zero\"}, these vertices will have\n" " zero transitivity. If C{TRANSITIVITY_NAN} or C{\"nan\"}, these\n" " vertices will be excluded from the average.\n" "@see: L{transitivity_undirected()}, L{transitivity_local_undirected()}\n" "@newfield ref: Reference\n" "@ref: D. J. Watts and S. Strogatz: I{Collective dynamics of small-world\n" " networks}. Nature 393(6884):440-442, 1998." }, /* interface to igraph_unfold_tree */ {"unfold_tree", (PyCFunction) igraphmodule_Graph_unfold_tree, METH_VARARGS | METH_KEYWORDS, "unfold_tree(sources=None, mode=OUT)\n\n" "Unfolds the graph using a BFS to a tree by duplicating vertices as necessary.\n\n" "@param sources: the source vertices to start the unfolding from. It should be a\n" " list of vertex indices, preferably one vertex from each connected component.\n" " You can use L{Graph.topological_sorting()} to determine a suitable set. A single\n" " vertex index is also accepted.\n" "@param mode: which edges to follow during the BFS. C{OUT} follows outgoing edges,\n" " C{IN} follows incoming edges, C{ALL} follows both. Ignored for undirected\n" " graphs.\n" "@return: the unfolded tree graph and a mapping from the new vertex indices to the\n" " old ones.\n" }, /* interface to igraph_[st_]vertex_connectivity */ {"vertex_connectivity", (PyCFunction) igraphmodule_Graph_vertex_connectivity, METH_VARARGS | METH_KEYWORDS, "vertex_connectivity(source=-1, target=-1, checks=True, neighbors=\"error\")\n\n" "Calculates the vertex connectivity of the graph or between some vertices.\n\n" "The vertex connectivity between two given vertices is the number of vertices\n" "that have to be removed in order to disconnect the two vertices into two\n" "separate components. This is also the number of vertex disjoint directed\n" "paths between the vertices (apart from the source and target vertices of\n" "course). The vertex connectivity of the graph is the minimal vertex\n" "connectivity over all vertex pairs.\n\n" "This method calculates the vertex connectivity of a given vertex pair if both\n" "the source and target vertices are given. If none of them is given (or they\n" "are both negative), the overall vertex connectivity is returned.\n\n" "@param source: the source vertex involved in the calculation.\n" "@param target: the target vertex involved in the calculation.\n" "@param checks: if the whole graph connectivity is calculated and this is\n" " C{True}, igraph performs some basic checks before calculation. If the\n" " graph is not strongly connected, then the connectivity is obviously\n" " zero. If the minimum degree is one, then the connectivity is\n" " also one. These simple checks are much faster than checking the entire\n" " graph, therefore it is advised to set this to C{True}. The parameter\n" " is ignored if the connectivity between two given vertices is computed.\n" "@param neighbors: tells igraph what to do when the two vertices are\n" " connected. C{\"error\"} raises an exception, C{\"infinity\"} returns\n" " infinity, C{\"ignore\"} ignores the edge.\n" "@return: the vertex connectivity\n" }, /***********************/ /* SIMILARITY MEASURES */ /***********************/ /* interface to igraph_bibcoupling */ {"bibcoupling", (PyCFunction) igraphmodule_Graph_bibcoupling, METH_VARARGS | METH_KEYWORDS, "bibcoupling(vertices=None)\n\n" "Calculates bibliographic coupling scores for given vertices in a graph.\n\n" "@param vertices: the vertices to be analysed. If C{None}, all vertices\n" " will be considered.\n" "@return: bibliographic coupling scores for all given vertices in a matrix."}, /* interface to igraph_cocitation */ {"cocitation", (PyCFunction) igraphmodule_Graph_cocitation, METH_VARARGS | METH_KEYWORDS, "cocitation(vertices=None)\n\n" "Calculates cocitation scores for given vertices in a graph.\n\n" "@param vertices: the vertices to be analysed. If C{None}, all vertices\n" " will be considered.\n" "@return: cocitation scores for all given vertices in a matrix."}, /* interface to igraph_similarity_dice */ {"similarity_dice", (PyCFunction) igraphmodule_Graph_similarity_dice, METH_VARARGS | METH_KEYWORDS, "similarity_dice(vertices=None, pairs=None, mode=IGRAPH_ALL, loops=True)\n\n" "Dice similarity coefficient of vertices.\n\n" "The Dice similarity coefficient of two vertices is twice the number of\n" "their common neighbors divided by the sum of their degrees. This\n" "coefficient is very similar to the Jaccard coefficient, but usually\n" "gives higher similarities than its counterpart.\n\n" "@param vertices: the vertices to be analysed. If C{None} and I{pairs} is also\n" " C{None}, all vertices will be considered.\n" "@param pairs: the vertex pairs to be analysed. If this is given, I{vertices}\n" " must be C{None}, and the similarity values will be calculated only for the\n" " given pairs. Vertex pairs must be specified as tuples of vertex IDs.\n" "@param mode: which neighbors should be considered for directed graphs.\n" " Can be L{ALL}, L{IN} or L{OUT}, ignored for undirected graphs.\n" "@param loops: whether vertices should be considered adjacent to\n" " themselves. Setting this to C{True} assumes a loop edge for all vertices\n" " even if none is present in the graph. Setting this to C{False} may\n" " result in strange results: nonadjacent vertices may have larger\n" " similarities compared to the case when an edge is added between them --\n" " however, this might be exactly the result you want to get.\n" "@return: the pairwise similarity coefficients for the vertices specified,\n" " in the form of a matrix if C{pairs} is C{None} or in the form of a list\n" " if C{pairs} is not C{None}.\n" }, /* interface to igraph_similarity_inverse_log_weighted */ {"similarity_inverse_log_weighted", (PyCFunction) igraphmodule_Graph_similarity_inverse_log_weighted, METH_VARARGS | METH_KEYWORDS, "similarity_inverse_log_weighted(vertices=None, mode=IGRAPH_ALL)\n\n" "Inverse log-weighted similarity coefficient of vertices.\n\n" "Each vertex is assigned a weight which is 1 / log(degree). The\n" "log-weighted similarity of two vertices is the sum of the weights\n" "of their common neighbors.\n\n" "@param vertices: the vertices to be analysed. If C{None}, all vertices\n" " will be considered.\n" "@param mode: which neighbors should be considered for directed graphs.\n" " Can be L{ALL}, L{IN} or L{OUT}, ignored for undirected graphs.\n" " L{IN} means that the weights are determined by the out-degrees, L{OUT}\n" " means that the weights are determined by the in-degrees.\n" "@return: the pairwise similarity coefficients for the vertices specified,\n" " in the form of a matrix (list of lists).\n" }, /* interface to igraph_similarity_jaccard */ {"similarity_jaccard", (PyCFunction) igraphmodule_Graph_similarity_jaccard, METH_VARARGS | METH_KEYWORDS, "similarity_jaccard(vertices=None, pairs=None, mode=IGRAPH_ALL, loops=True)\n\n" "Jaccard similarity coefficient of vertices.\n\n" "The Jaccard similarity coefficient of two vertices is the number of their\n" "common neighbors divided by the number of vertices that are adjacent to\n" "at least one of them.\n\n" "@param vertices: the vertices to be analysed. If C{None} and I{pairs} is also\n" " C{None}, all vertices will be considered.\n" "@param pairs: the vertex pairs to be analysed. If this is given, I{vertices}\n" " must be C{None}, and the similarity values will be calculated only for the\n" " given pairs. Vertex pairs must be specified as tuples of vertex IDs.\n" "@param mode: which neighbors should be considered for directed graphs.\n" " Can be L{ALL}, L{IN} or L{OUT}, ignored for undirected graphs.\n" "@param loops: whether vertices should be considered adjacent to\n" " themselves. Setting this to C{True} assumes a loop edge for all vertices\n" " even if none is present in the graph. Setting this to C{False} may\n" " result in strange results: nonadjacent vertices may have larger\n" " similarities compared to the case when an edge is added between them --\n" " however, this might be exactly the result you want to get.\n" "@return: the pairwise similarity coefficients for the vertices specified,\n" " in the form of a matrix if C{pairs} is C{None} or in the form of a list\n" " if C{pairs} is not C{None}.\n" }, /******************/ /* MOTIF COUNTING */ /******************/ {"motifs_randesu", (PyCFunction) igraphmodule_Graph_motifs_randesu, METH_VARARGS | METH_KEYWORDS, "motifs_randesu(size=3, cut_prob=None, callback=None)\n\n" "Counts the number of motifs in the graph\n\n" "Motifs are small subgraphs of a given structure in a graph. It is\n" "argued that the motif profile (ie. the number of different motifs in\n" "the graph) is characteristic for different types of networks and\n" "network function is related to the motifs in the graph.\n\n" "This function is able to find the different motifs of size three\n" "and four (ie. the number of different subgraphs with three and four\n" "vertices) in the network.\n\n" "In a big network the total number of motifs can be very large, so\n" "it takes a lot of time to find all of them. In such cases, a sampling\n" "method can be used. This function is capable of doing sampling via\n" "the I{cut_prob} argument. This argument gives the probability that\n" "a branch of the motif search tree will not be explored.\n\n" "@newfield ref: Reference\n" "@ref: S. Wernicke and F. Rasche: FANMOD: a tool for fast network\n" " motif detection, Bioinformatics 22(9), 1152--1153, 2006.\n\n" "@param size: the size of the motifs (3 or 4).\n" "@param cut_prob: the cut probabilities for different levels of the search\n" " tree. This must be a list of length I{size} or C{None} to find all\n" " motifs.\n" "@param callback: C{None} or a callable that will be called for every motif\n" " found in the graph. The callable must accept three parameters: the graph\n" " itself, the list of vertices in the motif and the isomorphy class of the\n" " motif (see L{Graph.isoclass()}). The search will stop when the callback\n" " returns an object with a non-zero truth value or raises an exception.\n" "@return: the list of motifs if I{callback} is C{None}, or C{None} otherwise\n" "@see: Graph.motifs_randesu_no()\n" }, {"motifs_randesu_no", (PyCFunction) igraphmodule_Graph_motifs_randesu_no, METH_VARARGS | METH_KEYWORDS, "motifs_randesu_no(size=3, cut_prob=None)\n\n" "Counts the total number of motifs in the graph\n\n" "Motifs are small subgraphs of a given structure in a graph.\n" "This function counts the total number of motifs in a graph without\n" "assigning isomorphism classes to them.\n\n" "@newfield ref: Reference\n" "@ref: S. Wernicke and F. Rasche: FANMOD: a tool for fast network\n" " motif detection, Bioinformatics 22(9), 1152--1153, 2006.\n\n" "@param size: the size of the motifs (3 or 4).\n" "@param cut_prob: the cut probabilities for different levels of the search\n" " tree. This must be a list of length I{size} or C{None} to find all\n" " motifs.\n" "@see: Graph.motifs_randesu()\n" }, {"motifs_randesu_estimate", (PyCFunction) igraphmodule_Graph_motifs_randesu_estimate, METH_VARARGS | METH_KEYWORDS, "motifs_randesu_estimate(size=3, cut_prob=None, sample)\n\n" "Counts the total number of motifs in the graph\n\n" "Motifs are small subgraphs of a given structure in a graph.\n" "This function estimates the total number of motifs in a graph without\n" "assigning isomorphism classes to them by extrapolating from a random\n" "sample of vertices.\n\n" "@newfield ref: Reference\n" "@ref: S. Wernicke and F. Rasche: FANMOD: a tool for fast network\n" " motif detection, Bioinformatics 22(9), 1152--1153, 2006.\n\n" "@param size: the size of the motifs (3 or 4).\n" "@param cut_prob: the cut probabilities for different levels of the search\n" " tree. This must be a list of length I{size} or C{None} to find all\n" " motifs.\n" "@param sample: the size of the sample or the vertex IDs of the vertices\n" " to be used for sampling.\n" "@see: Graph.motifs_randesu()\n" }, {"dyad_census", (PyCFunction) igraphmodule_Graph_dyad_census, METH_NOARGS, "dyad_census()\n\n" "Dyad census, as defined by Holland and Leinhardt\n\n" "Dyad census means classifying each pair of vertices of a directed\n" "graph into three categories: mutual, there is an edge from I{a} to\n" "I{b} and also from I{b} to I{a}; asymmetric, there is an edge\n" "either from I{a} to I{b} or from I{b} to I{a} but not the other way\n" "and null, no edges between I{a} and I{b}.\n\n" "@attention: this function has a more convenient interface in class\n" " L{Graph} which wraps the result in a L{DyadCensus} object.\n" " It is advised to use that.\n\n" "@return: the number of mutual, asymmetric and null connections in a\n" " 3-tuple." }, {"triad_census", (PyCFunction) igraphmodule_Graph_triad_census, METH_NOARGS, "triad_census()\n\n" "Triad census, as defined by Davis and Leinhardt\n\n" "Calculating the triad census means classifying every triplets of\n" "vertices in a directed graph. A triplet can be in one of 16 states,\n" "these are listed in the documentation of the C interface of igraph.\n" "\n" "@attention: this function has a more convenient interface in class\n" " L{Graph} which wraps the result in a L{TriadCensus} object.\n" " It is advised to use that. The name of the triplet classes are\n" " also documented there.\n\n" }, /********************/ /* LAYOUT FUNCTIONS */ /********************/ /* interface to igraph_layout_bipartite */ {"layout_bipartite", (PyCFunction) igraphmodule_Graph_layout_bipartite, METH_VARARGS | METH_KEYWORDS, "layout_bipartite(types=\"type\", hgap=1, vgap=1, maxiter=100)\n\n" "Place the vertices of a bipartite graph in two layers.\n\n" "The layout is created by placing the vertices in two rows, according\n" "to their types. The positions of the vertices within the rows are\n" "then optimized to minimize the number of edge crossings using the\n" "heuristic used by the Sugiyama layout algorithm.\n\n" "@param types: an igraph vector containing the vertex types, or an\n" " attribute name. Anything that evalulates to C{False} corresponds to\n" " vertices of the first kind, everything else to the second kind.\n" "@param hgap: minimum horizontal gap between vertices in the same layer.\n" "@param vgap: vertical gap between the two layers.\n" "@param maxiter: maximum number of iterations to take in the crossing\n" " reduction step. Increase this if you feel that you are getting too many\n" " edge crossings.\n" "@return: the calculated layout."}, /* interface to igraph_layout_circle */ {"layout_circle", (PyCFunction) igraphmodule_Graph_layout_circle, METH_VARARGS | METH_KEYWORDS, "layout_circle(dim=2)\n\n" "Places the vertices of the graph uniformly on a circle or a sphere.\n\n" "@param dim: the desired number of dimensions for the layout. dim=2\n" " means a 2D layout, dim=3 means a 3D layout.\n" "@return: the calculated layout."}, /* interface to igraph_layout_grid */ {"layout_grid", (PyCFunction) igraphmodule_Graph_layout_grid, METH_VARARGS | METH_KEYWORDS, "layout_grid(width=0, height=0, dim=2)\n\n" "Places the vertices of a graph in a 2D or 3D grid.\n\n" "@param width: the number of vertices in a single row of the layout.\n" " Zero or negative numbers mean that the width should be determined\n" " automatically.\n" "@param height: the number of vertices in a single column of the layout.\n" " Zero or negative numbers mean that the height should be determined\n" " automatically. It must not be given if the number of dimensions is 2.\n" "@param dim: the desired number of dimensions for the layout. dim=2\n" " means a 2D layout, dim=3 means a 3D layout.\n" "@return: the calculated layout."}, /* interface to igraph_layout_star */ {"layout_star", (PyCFunction) igraphmodule_Graph_layout_star, METH_VARARGS | METH_KEYWORDS, "layout_star(center=0, order=None)\n\n" "Calculates a star-like layout for the graph.\n\n" "@param center: the ID of the vertex to put in the center\n" "@param order: a numeric vector giving the order of the vertices\n" " (including the center vertex!). If it is C{None}, the vertices\n" " will be placed in increasing vertex ID order.\n" "@return: the calculated layout." }, /* interface to igraph_layout_kamada_kawai */ {"layout_kamada_kawai", (PyCFunction) igraphmodule_Graph_layout_kamada_kawai, METH_VARARGS | METH_KEYWORDS, "layout_kamada_kawai(maxiter=1000, sigma=None, initemp=10, coolexp=0.99,\n" " kkconst=None, seed=None, minx=None, maxx=None, miny=None, maxy=None, \n" " minz=None, maxz=None, dim=2)\n\n" "Places the vertices on a plane according to the Kamada-Kawai algorithm.\n\n" "This is a force directed layout, see Kamada, T. and Kawai, S.:\n" "An Algorithm for Drawing General Undirected Graphs.\n" "Information Processing Letters, 31/1, 7--15, 1989.\n\n" "@param maxiter: the number of iterations to perform.\n" "@param sigma: the standard base deviation of the position\n" " change proposals. C{None} means the number of vertices / 4\n" "@param initemp: initial temperature of the simulated annealing.\n" "@param coolexp: cooling exponent of the simulated annealing.\n" "@param kkconst: the Kamada-Kawai vertex attraction constant.\n" " C{None} means the square of the number of vertices.\n" "@param minx: if not C{None}, it must be a vector with exactly as many\n" " elements as there are vertices in the graph. Each element is a\n" " minimum constraint on the X value of the vertex in the layout.\n" "@param maxx: similar to I{minx}, but with maximum constraints\n" "@param miny: similar to I{minx}, but with the Y coordinates\n" "@param maxy: similar to I{maxx}, but with the Y coordinates\n" "@param minz: similar to I{minx}, but with the Z coordinates. Use only\n" " for 3D layouts (C{dim}=3).\n" "@param maxz: similar to I{maxx}, but with the Z coordinates. Use only\n" " for 3D layouts (C{dim}=3).\n" "@param seed: if C{None}, uses a random starting layout for the\n" " algorithm. If a matrix (list of lists), uses the given matrix\n" " as the starting position.\n" "@param dim: the desired number of dimensions for the layout. dim=2\n" " means a 2D layout, dim=3 means a 3D layout.\n" "@return: the calculated layout." }, /* interface to igraph_layout_drl */ {"layout_drl", (PyCFunction) igraphmodule_Graph_layout_drl, METH_VARARGS | METH_KEYWORDS, "layout_drl(weights=None, fixed=None, seed=None, options=None, dim=2)\n\n" "Places the vertices on a 2D plane or in the 3D space ccording to the DrL\n" "layout algorithm.\n\n" "This is an algorithm suitable for quite large graphs, but it can be\n" "surprisingly slow for small ones (where the simpler force-based layouts\n" "like C{layout_kamada_kawai()} or C{layout_fruchterman_reingold()} are\n" "more useful.\n\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@param seed: if C{None}, uses a random starting layout for the\n" " algorithm. If a matrix (list of lists), uses the given matrix\n" " as the starting position.\n" "@param fixed: if a seed is given, you can specify some vertices to be\n" " kept fixed at their original position in the seed by passing an\n" " appropriate list here. The list must have exactly as many items as\n" " the number of vertices in the graph. Items of the list that evaluate\n" " to C{True} denote vertices that will not be moved.\n" "@param options: if you give a string argument here, you can select from\n" " five default preset parameterisations: C{default}, C{coarsen} for a\n" " coarser layout, C{coarsest} for an even coarser layout, C{refine} for\n" " refining an existing layout and C{final} for finalizing a layout. If\n" " you supply an object that is not a string, the DrL layout parameters\n" " are retrieved from the respective keys of the object (so it should\n" " be a dict or something else that supports the mapping protocol).\n" " The following keys can be used:\n" " \n" " - C{edge_cut}: edge cutting is done in the late stages of the\n" " algorithm in order to achieve less dense layouts. Edges are\n" " cut if there is a lot of stress on them (a large value in the\n" " objective function sum). The edge cutting parameter is a value\n" " between 0 and 1 with 0 representing no edge cutting and 1\n" " representing maximal edge cutting.\n\n" " - C{init_iterations}: number of iterations in the initialization\n" " phase\n\n" " - C{init_temperature}: start temperature during initialization\n\n" " - C{init_attraction}: attraction during initialization\n\n" " - C{init_damping_mult}: damping multiplier during initialization\n\n" " - C{liquid_iterations}, C{liquid_temperature}, C{liquid_attraction},\n" " C{liquid_damping_mult}: same parameters for the liquid phase\n\n" " - C{expansion_iterations}, C{expansion_temperature},\n" " C{expansion_attraction}, C{expansion_damping_mult}:\n" " parameters for the expansion phase\n\n" " - C{cooldown_...}: parameters for the cooldown phase\n\n" " - C{crunch_...}: parameters for the crunch phase\n\n" " - C{simmer_...}: parameters for the simmer phase\n\n" " \n" " Instead of a mapping, you can also use an arbitrary Python object\n" " here: if the object does not support the mapping protocol, an\n" " attribute of the object with the same name is looked up instead. If\n" " a parameter cannot be found either as a key or an attribute, the\n" " default from the C{default} preset will be used.\n\n" "@param dim: the desired number of dimensions for the layout. dim=2\n" " means a 2D layout, dim=3 means a 3D layout.\n" "@return: the calculated layout." }, /* interface to igraph_layout_fruchterman_reingold */ {"layout_fruchterman_reingold", (PyCFunction) igraphmodule_Graph_layout_fruchterman_reingold, METH_VARARGS | METH_KEYWORDS, "layout_fruchterman_reingold(weights=None, maxiter=500, maxdelta=None, area=None,\n" " coolexp=1.5, repulserad=None, seed=None, minx=None, maxx=None, miny=None, \n" " maxy=None, minz=None, maxz=None, dim=2)\n\n" "Places the vertices on a 2D plane or in the 3D space according to the\n" "Fruchterman-Reingold algorithm.\n\n" "This is a force directed layout, see Fruchterman, T. M. J. and Reingold, E. M.:\n" "Graph Drawing by Force-directed Placement.\n" "Software -- Practice and Experience, 21/11, 1129--1164, 1991\n\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@param maxiter: the number of iterations to perform. The default\n" " is 500.\n" "@param maxdelta: the maximum distance to move a vertex in\n" " an iteration. The default is the number of vertices.\n" "@param area: the area of the square on which the vertices\n" " will be placed. The default is the square of the number of\n" " vertices.\n" "@param coolexp: the cooling exponent of the simulated annealing.\n" " The default is 1.5.\n" "@param repulserad: determines the radius at which vertex-vertex\n" " repulsion cancels out attraction of adjacent vertices.\n" " The default is the number of vertices^3.\n" "@param minx: if not C{None}, it must be a vector with exactly as many\n" " elements as there are vertices in the graph. Each element is a\n" " minimum constraint on the X value of the vertex in the layout.\n" "@param maxx: similar to I{minx}, but with maximum constraints\n" "@param miny: similar to I{minx}, but with the Y coordinates\n" "@param maxy: similar to I{maxx}, but with the Y coordinates\n" "@param minz: similar to I{minx}, but with the Z coordinates. Use only\n" " for 3D layouts (C{dim}=3).\n" "@param maxz: similar to I{maxx}, but with the Z coordinates. Use only\n" " for 3D layouts (C{dim}=3).\n" "@param seed: if C{None}, uses a random starting layout for the\n" " algorithm. If a matrix (list of lists), uses the given matrix\n" " as the starting position.\n" "@param dim: the desired number of dimensions for the layout. dim=2\n" " means a 2D layout, dim=3 means a 3D layout.\n" "@return: the calculated layout." }, /* interface to igraph_layout_graphopt */ {"layout_graphopt", (PyCFunction) igraphmodule_Graph_layout_graphopt, METH_VARARGS | METH_KEYWORDS, "layout_graphopt(niter=500, node_charge=0.001, node_mass=30, spring_length=0, spring_constant=1, max_sa_movement=5, seed=None)\n\n" "This is a port of the graphopt layout algorithm by Michael Schmuhl.\n" "graphopt version 0.4.1 was rewritten in C and the support for layers\n" "was removed.\n\n" "graphopt uses physical analogies for defining attracting and repelling\n" "forces among the vertices and then the physical system is simulated\n" "until it reaches an equilibrium or the maximal number of iterations is\n" "reached.\n\n" "See U{http://www.schmuhl.org/graphopt/} for the original graphopt.\n\n" "@param niter: the number of iterations to perform. Should be a couple\n" " of hundred in general.\n\n" "@param node_charge: the charge of the vertices, used to calculate electric\n" " repulsion.\n" "@param node_mass: the mass of the vertices, used for the spring forces\n" "@param spring_length: the length of the springs\n" "@param spring_constant: the spring constant\n" "@param max_sa_movement: the maximum amount of movement allowed in a single\n" " step along a single axis.\n" "@param seed: a matrix containing a seed layout from which the algorithm\n" " will be started. If C{None}, a random layout will be used.\n" "@return: the calculated layout." }, /* interface to igraph_layout_lgl */ {"layout_lgl", (PyCFunction) igraphmodule_Graph_layout_lgl, METH_VARARGS | METH_KEYWORDS, "layout_lgl(maxiter=150, maxdelta=-1, area=-1, coolexp=1.5, repulserad=-1, cellsize=-1, root=None)\n\n" "Places the vertices on a 2D plane according to the Large Graph Layout.\n\n" "@param maxiter: the number of iterations to perform.\n" "@param maxdelta: the maximum distance to move a vertex in\n" " an iteration. If negative, defaults to the number of vertices.\n" "@param area: the area of the square on which the vertices\n" " will be placed. If negative, defaults to the number of vertices\n" " squared.\n" "@param coolexp: the cooling exponent of the simulated annealing.\n" "@param repulserad: determines the radius at which vertex-vertex\n" " repulsion cancels out attraction of adjacent vertices.\n" " If negative, defaults to M{area} times the number of vertices.\n" "@param cellsize: the size of the grid cells. When calculating the\n" " repulsion forces, only vertices in the same or neighboring\n" " grid cells are taken into account. Defaults to the fourth\n" " root of M{area}.\n" "@param root: the root vertex, this is placed first, its neighbors\n" " in the first iteration, second neighbors in the second,\n" " etc. C{None} means that a random vertex will be chosen.\n" "@return: the calculated layout." }, /* interface to igraph_layout_mds */ {"layout_mds", (PyCFunction) igraphmodule_Graph_layout_mds, METH_VARARGS | METH_KEYWORDS, "layout_mds(dist=None, dim=2, arpack_options=None)\n" "Places the vertices in an Euclidean space with the given number of\n" "dimensions using multidimensional scaling.\n\n" "This layout requires a distance matrix, where the intersection of\n" "row M{i} and column M{j} specifies the desired distance between\n" "vertex M{i} and vertex M{j}. The algorithm will try to place the\n" "vertices in a way that approximates the distance relations\n" "prescribed in the distance matrix. igraph uses the classical\n" "multidimensional scaling by Torgerson (see reference below).\n\n" "For unconnected graphs, the method will decompose the graph into\n" "weakly connected components and then lay out the components\n" "individually using the appropriate parts of the distance matrix.\n\n" "@param dist: the distance matrix. It must be symmetric and the\n" " symmetry is not checked -- results are unspecified when a\n" " non-symmetric distance matrix is used. If this parameter is\n" " C{None}, the shortest path lengths will be used as distances.\n" " Directed graphs are treated as undirected when calculating\n" " the shortest path lengths to ensure symmetry.\n" "@param dim: the number of dimensions. For 2D layouts, supply\n" " 2 here; for 3D layouts, supply 3.\n" "@param arpack_options: an L{ARPACKOptions} object used to fine-tune\n" " the ARPACK eigenvector calculation. If omitted, the module-level\n" " variable called C{arpack_options} is used.\n" "@return: the calculated layout.\n\n" "@newfield ref: Reference\n" "@ref: Cox & Cox: Multidimensional Scaling (1994), Chapman and\n" " Hall, London.\n" }, /* interface to igraph_layout_reingold_tilford */ {"layout_reingold_tilford", (PyCFunction) igraphmodule_Graph_layout_reingold_tilford, METH_VARARGS | METH_KEYWORDS, "layout_reingold_tilford(mode=\"out\", root=None, rootlevel=None)\n" "Places the vertices on a 2D plane according to the Reingold-Tilford\n" "layout algorithm.\n\n" "This is a tree layout. If the given graph is not a tree, a breadth-first\n" "search is executed first to obtain a possible spanning tree.\n\n" "@param mode: specifies which edges to consider when builing the tree.\n" " If it is C{OUT} then only the outgoing, if it is C{IN} then only the\n" " incoming edges of a parent are considered. If it is C{ALL} then all\n" " edges are used (this was the behaviour in igraph 0.5 and before).\n" " This parameter also influences how the root vertices are calculated\n" " if they are not given. See the I{root} parameter.\n" "@param root: the index of the root vertex or root vertices.\n" " if this is a non-empty vector then the supplied vertex IDs are\n" " used as the roots of the trees (or a single tree if the graph is\n" " connected. If this is C{None} or an empty list, the root vertices\n" " are automatically calculated based on topological sorting,\n" " performed with the opposite of the I{mode} argument.\n" "@param rootlevel: this argument is useful when drawing forests which are\n" " not trees. It specifies the level of the root vertices for every tree\n" " in the forest.\n" "@return: the calculated layout.\n\n" "@see: layout_reingold_tilford_circular\n" "@newfield ref: Reference\n" "@ref: EM Reingold, JS Tilford: I{Tidier Drawings of Trees.}\n" "IEEE Transactions on Software Engineering 7:22, 223-228, 1981."}, /* interface to igraph_layout_reingold_tilford_circular */ {"layout_reingold_tilford_circular", (PyCFunction) igraphmodule_Graph_layout_reingold_tilford_circular, METH_VARARGS | METH_KEYWORDS, "layout_reingold_tilford_circular(mode=\"out\", root=None, rootlevel=None)\n" "Circular Reingold-Tilford layout for trees.\n\n" "This layout is similar to the Reingold-Tilford layout, but the vertices\n" "are placed in a circular way, with the root vertex in the center.\n\n" "See L{layout_reingold_tilford} for the explanation of the parameters.\n\n" "@return: the calculated layout.\n\n" "@see: layout_reingold_tilford\n" "@newfield ref: Reference\n" "@ref: EM Reingold, JS Tilford: I{Tidier Drawings of Trees.}\n" "IEEE Transactions on Software Engineering 7:22, 223-228, 1981."}, /* interface to igraph_layout_random */ {"layout_random", (PyCFunction) igraphmodule_Graph_layout_random, METH_VARARGS | METH_KEYWORDS, "layout_random(dim=2)\n" "Places the vertices of the graph randomly.\n\n" "@param dim: the desired number of dimensions for the layout. dim=2\n" " means a 2D layout, dim=3 means a 3D layout.\n" "@return: the coordinate pairs in a list."}, /* interface to igraph_layout_sugiyama */ {"_layout_sugiyama", (PyCFunction) igraphmodule_Graph_layout_sugiyama, METH_VARARGS | METH_KEYWORDS, "Internal function, undocumented.\n\n" "@see: Graph.layout_sugiyama()\n\n"}, //////////////////////////// // VISITOR-LIKE FUNCTIONS // //////////////////////////// {"bfs", (PyCFunction) igraphmodule_Graph_bfs, METH_VARARGS | METH_KEYWORDS, "bfs(vid, mode=OUT)\n\n" "Conducts a breadth first search (BFS) on the graph.\n\n" "@param vid: the root vertex ID\n" "@param mode: either L{IN} or L{OUT} or L{ALL}, ignored\n" " for undirected graphs.\n" "@return: a tuple with the following items:\n" " - The vertex IDs visited (in order)\n" " - The start indices of the layers in the vertex list\n" " - The parent of every vertex in the BFS\n"}, {"bfsiter", (PyCFunction) igraphmodule_Graph_bfsiter, METH_VARARGS | METH_KEYWORDS, "bfsiter(vid, mode=OUT, advanced=False)\n\n" "Constructs a breadth first search (BFS) iterator of the graph.\n\n" "@param vid: the root vertex ID\n" "@param mode: either L{IN} or L{OUT} or L{ALL}.\n" "@param advanced: if C{False}, the iterator returns the next\n" " vertex in BFS order in every step. If C{True}, the iterator\n" " returns the distance of the vertex from the root and the\n" " parent of the vertex in the BFS tree as well.\n" "@return: the BFS iterator as an L{igraph.BFSIter} object.\n"}, ///////////////// // CONVERSIONS // ///////////////// // interface to igraph_get_adjacency {"get_adjacency", (PyCFunction) igraphmodule_Graph_get_adjacency, METH_VARARGS | METH_KEYWORDS, "get_adjacency(type=GET_ADJACENCY_BOTH, eids=False)\n\n" "Returns the adjacency matrix of a graph.\n\n" "@param type: either C{GET_ADJACENCY_LOWER} (uses the\n" " lower triangle of the matrix) or C{GET_ADJACENCY_UPPER}\n" " (uses the upper triangle) or C{GET_ADJACENCY_BOTH}\n" " (uses both parts). Ignored for directed graphs.\n" "@param eids: if C{True}, the result matrix will contain\n" " zeros for non-edges and the ID of the edge plus one\n" " for edges in the appropriate cell. If C{False}, the\n" " result matrix will contain the number of edges for\n" " each vertex pair.\n" "@return: the adjacency matrix.\n"}, // interface to igraph_get_edgelist {"get_edgelist", (PyCFunction) igraphmodule_Graph_get_edgelist, METH_NOARGS, "get_edgelist()\n\n" "Returns the edge list of a graph."}, /* interface to igraph_get_incidence */ {"get_incidence", (PyCFunction) igraphmodule_Graph_get_incidence, METH_VARARGS | METH_KEYWORDS, "get_incidence(types)\n\n" "Internal function, undocumented.\n\n" "@see: Graph.get_incidence()\n\n"}, // interface to igraph_to_directed {"to_directed", (PyCFunction) igraphmodule_Graph_to_directed, METH_VARARGS | METH_KEYWORDS, "to_directed(mutual=True)\n\n" "Converts an undirected graph to directed.\n\n" "@param mutual: C{True} if mutual directed edges should be\n" " created for every undirected edge. If C{False}, a directed\n" " edge with arbitrary direction is created.\n"}, // interface to igraph_to_undirected {"to_undirected", (PyCFunction) igraphmodule_Graph_to_undirected, METH_VARARGS | METH_KEYWORDS, "to_undirected(mode=\"collapse\", combine_edges=None)\n\n" "Converts a directed graph to undirected.\n\n" "@param mode: specifies what to do with multiple directed edges\n" " going between the same vertex pair. C{True} or C{\"collapse\"}\n" " means that only a single edge should be created from multiple\n" " directed edges. C{False} or C{\"each\"} means that every edge\n" " will be kept (with the arrowheads removed). C{\"mutual\"}\n" " creates one undirected edge for each mutual directed edge pair.\n" "@param combine_edges: specifies how to combine the attributes of\n" " multiple edges between the same pair of vertices into a single\n" " attribute. See L{Graph.simplify()} for more details.\n" }, /* interface to igraph_laplacian */ {"laplacian", (PyCFunction) igraphmodule_Graph_laplacian, METH_VARARGS | METH_KEYWORDS, "laplacian(weights=None, normalized=False)\n\n" "Returns the Laplacian matrix of a graph.\n\n" "The Laplacian matrix is similar to the adjacency matrix, but the edges\n" "are denoted with -1 and the diagonal contains the node degrees.\n\n" "Normalized Laplacian matrices have 1 or 0 in their diagonals (0 for vertices\n" "with no edges), edges are denoted by 1 / sqrt(d_i * d_j) where d_i is the\n" "degree of node i.\n\n" "Multiple edges and self-loops are silently ignored. Although it is\n" "possible to calculate the Laplacian matrix of a directed graph, it does\n" "not make much sense.\n\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name. When edge weights are used, the degree\n" " of a node is considered to be the weight of its incident edges.\n" "@param normalized: whether to return the normalized Laplacian matrix.\n" "@return: the Laplacian matrix.\n"}, /////////////////////////////// // LOADING AND SAVING GRAPHS // /////////////////////////////// // interface to igraph_read_graph_dimacs {"Read_DIMACS", (PyCFunction) igraphmodule_Graph_Read_DIMACS, METH_VARARGS | METH_KEYWORDS | METH_CLASS, "Read_DIMACS(f, directed=False)\n\n" "Reads a graph from a file conforming to the DIMACS minimum-cost flow file format.\n\n" "For the exact description of the format, see\n" "U{http://lpsolve.sourceforge.net/5.5/DIMACS.htm}\n\n" "Restrictions compared to the official description of the format:\n\n" " - igraph's DIMACS reader requires only three fields in an arc definition,\n" " describing the edge's source and target node and its capacity.\n" " - Source vertices are identified by 's' in the FLOW field, target vertices are\n" " identified by 't'.\n" " - Node indices start from 1. Only a single source and target node is allowed.\n\n" "@param f: the name of the file or a Python file handle\n" "@param directed: whether the generated graph should be directed.\n" "@return: the generated graph, the source and the target of the flow and the edge\n" " capacities in a tuple\n"}, /* interface to igraph_read_graph_dl */ {"Read_DL", (PyCFunction) igraphmodule_Graph_Read_DL, METH_VARARGS | METH_KEYWORDS | METH_CLASS, "Read_DL(f, directed=True)\n\n" "Reads an UCINET DL file and creates a graph based on it.\n\n" "@param f: the name of the file or a Python file handle\n" "@param directed: whether the generated graph should be directed.\n"}, /* interface to igraph_read_graph_edgelist */ {"Read_Edgelist", (PyCFunction) igraphmodule_Graph_Read_Edgelist, METH_VARARGS | METH_KEYWORDS | METH_CLASS, "Read_Edgelist(f, directed=True)\n\n" "Reads an edge list from a file and creates a graph based on it.\n\n" "Please note that the vertex indices are zero-based.\n\n" "@param f: the name of the file or a Python file handle\n" "@param directed: whether the generated graph should be directed.\n"}, /* interface to igraph_read_graph_graphdb */ {"Read_GraphDB", (PyCFunction) igraphmodule_Graph_Read_GraphDB, METH_VARARGS | METH_KEYWORDS | METH_CLASS, "Read_GraphDB(f, directed=False)\n\n" "Reads a GraphDB format file and creates a graph based on it.\n\n" "GraphDB is a binary format, used in the graph database for\n" "isomorphism testing (see U{http://amalfi.dis.unina.it/graph/}).\n\n" "@param f: the name of the file or a Python file handle\n" "@param directed: whether the generated graph should be directed.\n"}, /* interface to igraph_read_graph_graphml */ {"Read_GraphML", (PyCFunction) igraphmodule_Graph_Read_GraphML, METH_VARARGS | METH_KEYWORDS | METH_CLASS, "Read_GraphML(f, directed=True, index=0)\n\n" "Reads a GraphML format file and creates a graph based on it.\n\n" "@param f: the name of the file or a Python file handle\n" "@param index: if the GraphML file contains multiple graphs,\n" " specifies the one that should be loaded. Graph indices\n" " start from zero, so if you want to load the first graph,\n" " specify 0 here.\n"}, /* interface to igraph_read_graph_gml */ {"Read_GML", (PyCFunction) igraphmodule_Graph_Read_GML, METH_VARARGS | METH_KEYWORDS | METH_CLASS, "Read_GML(f)\n\n" "Reads a GML file and creates a graph based on it.\n\n" "@param f: the name of the file or a Python file handle\n" }, /* interface to igraph_read_graph_ncol */ {"Read_Ncol", (PyCFunction) igraphmodule_Graph_Read_Ncol, METH_VARARGS | METH_KEYWORDS | METH_CLASS, "Read_Ncol(f, names=True, weights=\"if_present\", directed=True)\n\n" "Reads an .ncol file used by LGL.\n\n" "It is also useful for creating graphs from \"named\" (and\n" "optionally weighted) edge lists.\n\n" "This format is used by the Large Graph Layout program. See the\n" "U{documentation of LGL }\n" "regarding the exact format description.\n\n" "LGL originally cannot deal with graphs containing multiple or loop\n" "edges, but this condition is not checked here, as igraph is happy\n" "with these.\n\n" "@param f: the name of the file or a Python file handle\n" "@param names: If C{True}, the vertex names are added as a\n" " vertex attribute called 'name'.\n" "@param weights: If True, the edge weights are added as an\n" " edge attribute called 'weight', even if there are no\n" " weights in the file. If False, the edge weights are never\n" " added, even if they are present. C{\"auto\"} or C{\"if_present\"}\n" " means that weights are added if there is at least one weighted\n" " edge in the input file, but they are not added otherwise.\n" "@param directed: whether the graph being created should be\n" " directed\n" }, /* interface to igraph_read_graph_lgl */ {"Read_Lgl", (PyCFunction) igraphmodule_Graph_Read_Lgl, METH_VARARGS | METH_KEYWORDS | METH_CLASS, "Read_Lgl(f, names=True, weights=\"if_present\", directed=True)\n\n" "Reads an .lgl file used by LGL.\n\n" "It is also useful for creating graphs from \"named\" (and\n" "optionally weighted) edge lists.\n\n" "This format is used by the Large Graph Layout program. See the\n" "U{documentation of LGL }\n" "regarding the exact format description.\n\n" "LGL originally cannot deal with graphs containing multiple or loop\n" "edges, but this condition is not checked here, as igraph is happy\n" "with these.\n\n" "@param f: the name of the file or a Python file handle\n" "@param names: If C{True}, the vertex names are added as a\n" " vertex attribute called 'name'.\n" "@param weights: If True, the edge weights are added as an\n" " edge attribute called 'weight', even if there are no\n" " weights in the file. If False, the edge weights are never\n" " added, even if they are present. C{\"auto\"} or C{\"if_present\"}\n" " means that weights are added if there is at least one weighted\n" " edge in the input file, but they are not added otherwise.\n" "@param directed: whether the graph being created should be\n" " directed\n" }, /* interface to igraph_read_graph_pajek */ {"Read_Pajek", (PyCFunction) igraphmodule_Graph_Read_Pajek, METH_VARARGS | METH_KEYWORDS | METH_CLASS, "Read_Pajek(f)\n\n" "Reads a Pajek format file and creates a graph based on it.\n\n" "@param f: the name of the file or a Python file handle\n"}, /* interface to igraph_write_graph_dimacs */ {"write_dimacs", (PyCFunction) igraphmodule_Graph_write_dimacs, METH_VARARGS | METH_KEYWORDS, "write_dimacs(f, source, target, capacity=None)\n\n" "Writes the graph in DIMACS format to the given file.\n\n" "@param f: the name of the file to be written or a Python file handle\n" "@param source: the source vertex ID\n" "@param target: the target vertex ID\n" "@param capacity: the capacities of the edges in a list. If it is not a\n" " list, the corresponding edge attribute will be used to retrieve\n" " capacities."}, /* interface to igraph_write_graph_dot */ {"write_dot", (PyCFunction) igraphmodule_Graph_write_dot, METH_VARARGS | METH_KEYWORDS, "write_dot(f)\n\n" "Writes the graph in DOT format to the given file.\n\n" "DOT is the format used by the U{GraphViz }\n" "software package.\n\n" "@param f: the name of the file to be written or a Python file handle\n" }, /* interface to igraph_write_graph_edgelist */ {"write_edgelist", (PyCFunction) igraphmodule_Graph_write_edgelist, METH_VARARGS | METH_KEYWORDS, "write_edgelist(f)\n\n" "Writes the edge list of a graph to a file.\n\n" "Directed edges are written in (from, to) order.\n\n" "@param f: the name of the file to be written or a Python file handle\n"}, /* interface to igraph_write_graph_gml */ {"write_gml", (PyCFunction) igraphmodule_Graph_write_gml, METH_VARARGS | METH_KEYWORDS, "write_gml(f, creator=None, ids=None)\n\n" "Writes the graph in GML format to the given file.\n\n" "@param f: the name of the file to be written or a Python file handle\n" "@param creator: optional creator information to be written to the file.\n" " If C{None}, the current date and time is added.\n" "@param ids: optional numeric vertex IDs to use in the file. This must\n" " be a list of integers or C{None}. If C{None}, the C{id} attribute of\n" " the vertices are used, or if they don't exist, numeric vertex IDs\n" " will be generated automatically."}, /* interface to igraph_write_graph_ncol */ {"write_ncol", (PyCFunction) igraphmodule_Graph_write_ncol, METH_VARARGS | METH_KEYWORDS, "write_ncol(f, names=\"name\", weights=\"weights\")\n\n" "Writes the edge list of a graph to a file in .ncol format.\n\n" "Note that multiple edges and/or loops break the LGL software,\n" "but igraph does not check for this condition. Unless you know\n" "that the graph does not have multiple edges and/or loops, it\n" "is wise to call L{simplify()} before saving.\n\n" "@param f: the name of the file to be written or a Python file handle\n" "@param names: the name of the vertex attribute containing the name\n" " of the vertices. If you don't want to store vertex names,\n" " supply C{None} here.\n" "@param weights: the name of the edge attribute containing the weight\n" " of the vertices. If you don't want to store weights,\n" " supply C{None} here.\n"}, /* interface to igraph_write_graph_lgl */ {"write_lgl", (PyCFunction) igraphmodule_Graph_write_lgl, METH_VARARGS | METH_KEYWORDS, "write_lgl(f, names=\"name\", weights=\"weights\", isolates=True)\n\n" "Writes the edge list of a graph to a file in .lgl format.\n\n" "Note that multiple edges and/or loops break the LGL software,\n" "but igraph does not check for this condition. Unless you know\n" "that the graph does not have multiple edges and/or loops, it\n" "is wise to call L{simplify()} before saving.\n\n" "@param f: the name of the file to be written or a Python file handle\n" "@param names: the name of the vertex attribute containing the name\n" " of the vertices. If you don't want to store vertex names,\n" " supply C{None} here.\n" "@param weights: the name of the edge attribute containing the weight\n" " of the vertices. If you don't want to store weights,\n" " supply C{None} here.\n" "@param isolates: whether to include isolated vertices in the output.\n"}, /* interface to igraph_write_graph_pajek */ {"write_pajek", (PyCFunction) igraphmodule_Graph_write_pajek, METH_VARARGS | METH_KEYWORDS, "write_pajek(f)\n\n" "Writes the graph in Pajek format to the given file.\n\n" "@param f: the name of the file to be written or a Python file handle\n" }, /* interface to igraph_write_graph_edgelist */ {"write_graphml", (PyCFunction) igraphmodule_Graph_write_graphml, METH_VARARGS | METH_KEYWORDS, "write_graphml(f)\n\n" "Writes the graph to a GraphML file.\n\n" "@param f: the name of the file to be written or a Python file handle\n" }, /* interface to igraph_write_graph_leda */ {"write_leda", (PyCFunction) igraphmodule_Graph_write_leda, METH_VARARGS | METH_KEYWORDS, "write_leda(f, names=\"name\", weights=\"weights\")\n\n" "Writes the graph to a file in LEDA native format.\n\n" "The LEDA format supports at most one attribute per vertex and edge. You can\n" "specify which vertex and edge attribute you want to use. Note that the\n" "name of the attribute is not saved in the LEDA file.\n\n" "@param f: the name of the file to be written or a Python file handle\n" "@param names: the name of the vertex attribute to be stored along with\n" " the vertices. It is usually used to store the vertex names (hence the\n" " name of the keyword argument), but you may also use a numeric attribute.\n" " If you don't want to store any vertex attributes, supply C{None} here.\n" "@param weights: the name of the edge attribute to be stored along with\n" " the edges. It is usually used to store the edge weights (hence the\n" " name of the keyword argument), but you may also use a string attribute.\n" " If you don't want to store any edge attributes, supply C{None} here.\n"}, /***************/ /* ISOMORPHISM */ /***************/ {"canonical_permutation", (PyCFunction) igraphmodule_Graph_canonical_permutation, METH_VARARGS | METH_KEYWORDS, "canonical_permutation(sh=\"fm\")\n\n" "Calculates the canonical permutation of a graph using the BLISS isomorphism\n" "algorithm.\n\n" "Passing the permutation returned here to L{Graph.permute_vertices()} will\n" "transform the graph into its canonical form.\n\n" "See U{http://www.tcs.hut.fi/Software/bliss/index.html} for more information\n" "about the BLISS algorithm and canonical permutations.\n\n" "@param sh: splitting heuristics for graph as a case-insensitive string,\n" " with the following possible values:\n\n" " - C{\"f\"}: first non-singleton cell\n\n" " - C{\"fl\"}: first largest non-singleton cell\n\n" " - C{\"fs\"}: first smallest non-singleton cell\n\n" " - C{\"fm\"}: first maximally non-trivially connected non-singleton\n" " cell\n\n" " - C{\"flm\"}: largest maximally non-trivially connected\n" " non-singleton cell\n\n" " - C{\"fsm\"}: smallest maximally non-trivially connected\n" " non-singleton cell\n\n" "@return: a permutation vector containing vertex IDs. Vertex 0 in the original\n" " graph will be mapped to an ID contained in the first element of this\n" " vector; vertex 1 will be mapped to the second and so on.\n" }, {"isoclass", (PyCFunction) igraphmodule_Graph_isoclass, METH_VARARGS | METH_KEYWORDS, "isoclass(vertices)\n\n" "Returns the isomorphy class of the graph or its subgraph.\n\n" "Isomorphy class calculations are implemented only for graphs with\n" "3 or 4 vertices.\n\n" "@param vertices: a list of vertices if we want to calculate the\n" " isomorphy class for only a subset of vertices. C{None} means to\n" " use the full graph.\n" "@return: the isomorphy class of the (sub)graph\n\n"}, {"isomorphic", (PyCFunction) igraphmodule_Graph_isomorphic, METH_VARARGS | METH_KEYWORDS, "isomorphic(other)\n\n" "Checks whether the graph is isomorphic to another graph.\n\n" "The algorithm being used is selected using a simple heuristic:\n\n" " - If one graph is directed and the other undirected, an exception\n" " is thrown.\n\n" " - If the two graphs does not have the same number of vertices and\n" " edges, it returns with C{False}\n\n" " - If the graphs have three or four vertices, then an O(1) algorithm\n" " is used with precomputed data.\n\n" " - Otherwise if the graphs are directed, then the VF2 isomorphism\n" " algorithm is used (see L{Graph.isomorphic_vf2}).\n\n" " - Otherwise the BLISS isomorphism algorithm is used, see\n" " L{Graph.isomorphic_bliss}.\n\n" "@return: C{True} if the graphs are isomorphic, C{False} otherwise.\n" }, {"isomorphic_bliss", (PyCFunction) igraphmodule_Graph_isomorphic_bliss, METH_VARARGS | METH_KEYWORDS, "isomorphic_bliss(other, return_mapping_12=False, return_mapping_21=False,\n" " sh1=\"fm\", sh2=\"fm\")\n\n" "Checks whether the graph is isomorphic to another graph, using the\n" "BLISS isomorphism algorithm.\n\n" "See U{http://www.tcs.hut.fi/Software/bliss/index.html} for more information\n" "about the BLISS algorithm.\n\n" "@param other: the other graph with which we want to compare the graph.\n" "@param return_mapping_12: if C{True}, calculates the mapping which maps\n" " the vertices of the first graph to the second.\n" "@param return_mapping_21: if C{True}, calculates the mapping which maps\n" " the vertices of the second graph to the first.\n" "@param sh1: splitting heuristics for the first graph as a\n" " case-insensitive string, with the following possible values:\n\n" " - C{\"f\"}: first non-singleton cell\n\n" " - C{\"fl\"}: first largest non-singleton cell\n\n" " - C{\"fs\"}: first smallest non-singleton cell\n\n" " - C{\"fm\"}: first maximally non-trivially connected non-singleton\n" " cell\n\n" " - C{\"flm\"}: largest maximally non-trivially connected\n" " non-singleton cell\n\n" " - C{\"fsm\"}: smallest maximally non-trivially connected\n" " non-singleton cell\n\n" "@param sh2: splitting heuristics to be used for the second graph.\n" " Accepted values are as above.\n" "@return: if no mapping is calculated, the result is C{True} if the graphs\n" " are isomorphic, C{False} otherwise. If any or both mappings are\n" " calculated, the result is a 3-tuple, the first element being the\n" " above mentioned boolean, the second element being the 1 -> 2 mapping\n" " and the third element being the 2 -> 1 mapping. If the corresponding\n" " mapping was not calculated, C{None} is returned in the appropriate\n" " element of the 3-tuple.\n"}, {"isomorphic_vf2", (PyCFunction) igraphmodule_Graph_isomorphic_vf2, METH_VARARGS | METH_KEYWORDS, "isomorphic_vf2(other=None, color1=None, color2=None, edge_color1=None,\n" " edge_color2=None, return_mapping_12=False, return_mapping_21=False,\n" " node_compat_fn=None, edge_compat_fn=None, callback=None)\n\n" "Checks whether the graph is isomorphic to another graph, using the\n" "VF2 isomorphism algorithm.\n\n" "Vertex and edge colors may be used to restrict the isomorphisms, as only\n" "vertices and edges with the same color will be allowed to match each other.\n\n" "@param other: the other graph with which we want to compare the graph.\n" " If C{None}, the automorphjisms of the graph will be tested.\n" "@param color1: optional vector storing the coloring of the vertices of\n" " the first graph. If C{None}, all vertices have the same color.\n" "@param color2: optional vector storing the coloring of the vertices of\n" " the second graph. If C{None}, all vertices have the same color.\n" "@param edge_color1: optional vector storing the coloring of the edges of\n" " the first graph. If C{None}, all edges have the same color.\n" "@param edge_color2: optional vector storing the coloring of the edges of\n" " the second graph. If C{None}, all edges have the same color.\n" "@param return_mapping_12: if C{True}, calculates the mapping which maps\n" " the vertices of the first graph to the second.\n" "@param return_mapping_21: if C{True}, calculates the mapping which maps\n" " the vertices of the second graph to the first.\n" "@param callback: if not C{None}, the isomorphism search will not stop at\n" " the first match; it will call this callback function instead for every\n" " isomorphism found. The callback function must accept four arguments:\n" " the first graph, the second graph, a mapping from the nodes of the\n" " first graph to the second, and a mapping from the nodes of the second\n" " graph to the first. The function must return C{True} if the search\n" " should continue or C{False} otherwise.\n" "@param node_compat_fn: a function that receives the two graphs and two\n" " node indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the nodes given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on node-specific\n" " criteria that are too complicated to be represented by node color\n" " vectors (i.e. the C{color1} and C{color2} parameters). C{None} means\n" " that every node is compatible with every other node.\n" "@param edge_compat_fn: a function that receives the two graphs and two\n" " edge indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the edges given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on edge-specific\n" " criteria that are too complicated to be represented by edge color\n" " vectors (i.e. the C{edge_color1} and C{edge_color2} parameters). C{None}\n" " means that every edge is compatible with every other node.\n" "@return: if no mapping is calculated, the result is C{True} if the graphs\n" " are isomorphic, C{False} otherwise. If any or both mappings are\n" " calculated, the result is a 3-tuple, the first element being the\n" " above mentioned boolean, the second element being the 1 -> 2 mapping\n" " and the third element being the 2 -> 1 mapping. If the corresponding\n" " mapping was not calculated, C{None} is returned in the appropriate\n" " element of the 3-tuple.\n"}, {"count_isomorphisms_vf2", (PyCFunction) igraphmodule_Graph_count_isomorphisms_vf2, METH_VARARGS | METH_KEYWORDS, "count_isomorphisms_vf2(other=None, color1=None, color2=None, edge_color1=None,\n" " edge_color2=None, node_compat_fn=None, edge_compat_fn=None)\n\n" "Determines the number of isomorphisms between the graph and another one\n\n" "Vertex and edge colors may be used to restrict the isomorphisms, as only\n" "vertices and edges with the same color will be allowed to match each other.\n\n" "@param other: the other graph. If C{None}, the number of automorphisms\n" " will be returned.\n" "@param color1: optional vector storing the coloring of the vertices of\n" " the first graph. If C{None}, all vertices have the same color.\n" "@param color2: optional vector storing the coloring of the vertices of\n" " the second graph. If C{None}, all vertices have the same color.\n" "@param edge_color1: optional vector storing the coloring of the edges of\n" " the first graph. If C{None}, all edges have the same color.\n" "@param edge_color2: optional vector storing the coloring of the edges of\n" " the second graph. If C{None}, all edges have the same color.\n" "@param node_compat_fn: a function that receives the two graphs and two\n" " node indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the nodes given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on node-specific\n" " criteria that are too complicated to be represented by node color\n" " vectors (i.e. the C{color1} and C{color2} parameters). C{None} means\n" " that every node is compatible with every other node.\n" "@param edge_compat_fn: a function that receives the two graphs and two\n" " edge indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the edges given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on edge-specific\n" " criteria that are too complicated to be represented by edge color\n" " vectors (i.e. the C{edge_color1} and C{edge_color2} parameters). C{None}\n" " means that every edge is compatible with every other node.\n" "@return: the number of isomorphisms between the two given graphs (or the\n" " number of automorphisms if C{other} is C{None}.\n"}, {"get_isomorphisms_vf2", (PyCFunction) igraphmodule_Graph_get_isomorphisms_vf2, METH_VARARGS | METH_KEYWORDS, "get_isomorphisms_vf2(other=None, color1=None, color2=None, edge_color1=None,\n" " edge_color2=None, node_compat_fn=None, edge_compat_fn=None)\n\n" "Returns all isomorphisms between the graph and another one\n\n" "Vertex and edge colors may be used to restrict the isomorphisms, as only\n" "vertices and edges with the same color will be allowed to match each other.\n\n" "@param other: the other graph. If C{None}, the automorphisms\n" " will be returned.\n" "@param color1: optional vector storing the coloring of the vertices of\n" " the first graph. If C{None}, all vertices have the same color.\n" "@param color2: optional vector storing the coloring of the vertices of\n" " the second graph. If C{None}, all vertices have the same color.\n" "@param edge_color1: optional vector storing the coloring of the edges of\n" " the first graph. If C{None}, all edges have the same color.\n" "@param edge_color2: optional vector storing the coloring of the edges of\n" " the second graph. If C{None}, all edges have the same color.\n" "@param node_compat_fn: a function that receives the two graphs and two\n" " node indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the nodes given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on node-specific\n" " criteria that are too complicated to be represented by node color\n" " vectors (i.e. the C{color1} and C{color2} parameters). C{None} means\n" " that every node is compatible with every other node.\n" "@param edge_compat_fn: a function that receives the two graphs and two\n" " edge indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the edges given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on edge-specific\n" " criteria that are too complicated to be represented by edge color\n" " vectors (i.e. the C{edge_color1} and C{edge_color2} parameters). C{None}\n" " means that every edge is compatible with every other node.\n" "@return: a list of lists, each item of the list containing the mapping\n" " from vertices of the second graph to the vertices of the first one\n"}, {"subisomorphic_vf2", (PyCFunction) igraphmodule_Graph_subisomorphic_vf2, METH_VARARGS | METH_KEYWORDS, "subisomorphic_vf2(other, color1=None, color2=None, edge_color1=None,\n" " edge_color2=None, return_mapping_12=False, return_mapping_21=False,\n" " callback=None, node_compat_fn=None, edge_compat_fn=None)\n\n" "Checks whether a subgraph of the graph is isomorphic to another graph.\n\n" "Vertex and edge colors may be used to restrict the isomorphisms, as only\n" "vertices and edges with the same color will be allowed to match each other.\n\n" "@param other: the other graph with which we want to compare the graph.\n" "@param color1: optional vector storing the coloring of the vertices of\n" " the first graph. If C{None}, all vertices have the same color.\n" "@param color2: optional vector storing the coloring of the vertices of\n" " the second graph. If C{None}, all vertices have the same color.\n" "@param edge_color1: optional vector storing the coloring of the edges of\n" " the first graph. If C{None}, all edges have the same color.\n" "@param edge_color2: optional vector storing the coloring of the edges of\n" " the second graph. If C{None}, all edges have the same color.\n" "@param return_mapping_12: if C{True}, calculates the mapping which maps\n" " the vertices of the first graph to the second. The mapping can contain\n" " -1 if a given node is not mapped.\n" "@param return_mapping_21: if C{True}, calculates the mapping which maps\n" " the vertices of the second graph to the first. The mapping can contain\n" " -1 if a given node is not mapped.\n" "@param callback: if not C{None}, the subisomorphism search will not stop at\n" " the first match; it will call this callback function instead for every\n" " subisomorphism found. The callback function must accept four arguments:\n" " the first graph, the second graph, a mapping from the nodes of the\n" " first graph to the second, and a mapping from the nodes of the second\n" " graph to the first. The function must return C{True} if the search\n" " should continue or C{False} otherwise.\n" "@param node_compat_fn: a function that receives the two graphs and two\n" " node indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the nodes given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on node-specific\n" " criteria that are too complicated to be represented by node color\n" " vectors (i.e. the C{color1} and C{color2} parameters). C{None} means\n" " that every node is compatible with every other node.\n" "@param edge_compat_fn: a function that receives the two graphs and two\n" " edge indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the edges given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on edge-specific\n" " criteria that are too complicated to be represented by edge color\n" " vectors (i.e. the C{edge_color1} and C{edge_color2} parameters). C{None}\n" " means that every edge is compatible with every other node.\n" "@return: if no mapping is calculated, the result is C{True} if the graph\n" " contains a subgraph that's isomorphic to the given one, C{False}\n" " otherwise. If any or both mappings are calculated, the result is a\n" " 3-tuple, the first element being the above mentioned boolean, the\n" " second element being the 1 -> 2 mapping and the third element being\n" " the 2 -> 1 mapping. If the corresponding mapping was not calculated,\n" " C{None} is returned in the appropriate element of the 3-tuple.\n"}, {"count_subisomorphisms_vf2", (PyCFunction) igraphmodule_Graph_count_subisomorphisms_vf2, METH_VARARGS | METH_KEYWORDS, "count_subisomorphisms_vf2(other, color1=None, color2=None,\n" " edge_color1=None, edge_color2=None, node_compat_fn=None,\n" " edge_compat_fn=None)\n\n" "Determines the number of subisomorphisms between the graph and another one\n\n" "Vertex and edge colors may be used to restrict the isomorphisms, as only\n" "vertices and edges with the same color will be allowed to match each other.\n\n" "@param other: the other graph.\n" "@param color1: optional vector storing the coloring of the vertices of\n" " the first graph. If C{None}, all vertices have the same color.\n" "@param color2: optional vector storing the coloring of the vertices of\n" " the second graph. If C{None}, all vertices have the same color.\n" "@param edge_color1: optional vector storing the coloring of the edges of\n" " the first graph. If C{None}, all edges have the same color.\n" "@param edge_color2: optional vector storing the coloring of the edges of\n" " the second graph. If C{None}, all edges have the same color.\n" "@param node_compat_fn: a function that receives the two graphs and two\n" " node indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the nodes given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on node-specific\n" " criteria that are too complicated to be represented by node color\n" " vectors (i.e. the C{color1} and C{color2} parameters). C{None} means\n" " that every node is compatible with every other node.\n" "@param edge_compat_fn: a function that receives the two graphs and two\n" " edge indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the edges given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on edge-specific\n" " criteria that are too complicated to be represented by edge color\n" " vectors (i.e. the C{edge_color1} and C{edge_color2} parameters). C{None}\n" " means that every edge is compatible with every other node.\n" "@return: the number of subisomorphisms between the two given graphs\n"}, {"get_subisomorphisms_vf2", (PyCFunction) igraphmodule_Graph_get_subisomorphisms_vf2, METH_VARARGS | METH_KEYWORDS, "get_subisomorphisms_vf2(other, color1=None, color2=None,\n" " edge_color1=None, edge_color2=None, node_compat_fn=None,\n" " edge_compat_fn=None)\n\n" "Returns all subisomorphisms between the graph and another one\n\n" "Vertex and edge colors may be used to restrict the isomorphisms, as only\n" "vertices and edges with the same color will be allowed to match each other.\n\n" "@param other: the other graph.\n" "@param color1: optional vector storing the coloring of the vertices of\n" " the first graph. If C{None}, all vertices have the same color.\n" "@param color2: optional vector storing the coloring of the vertices of\n" " the second graph. If C{None}, all vertices have the same color.\n" "@param edge_color1: optional vector storing the coloring of the edges of\n" " the first graph. If C{None}, all edges have the same color.\n" "@param edge_color2: optional vector storing the coloring of the edges of\n" " the second graph. If C{None}, all edges have the same color.\n" "@param node_compat_fn: a function that receives the two graphs and two\n" " node indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the nodes given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on node-specific\n" " criteria that are too complicated to be represented by node color\n" " vectors (i.e. the C{color1} and C{color2} parameters). C{None} means\n" " that every node is compatible with every other node.\n" "@param edge_compat_fn: a function that receives the two graphs and two\n" " edge indices (one from the first graph, one from the second graph) and\n" " returns C{True} if the edges given by the two indices are compatible\n" " (i.e. they could be matched to each other) or C{False} otherwise. This\n" " can be used to restrict the set of isomorphisms based on edge-specific\n" " criteria that are too complicated to be represented by edge color\n" " vectors (i.e. the C{edge_color1} and C{edge_color2} parameters). C{None}\n" " means that every edge is compatible with every other node.\n" "@return: a list of lists, each item of the list containing the mapping\n" " from vertices of the second graph to the vertices of the first one\n"}, {"subisomorphic_lad", (PyCFunction) igraphmodule_Graph_subisomorphic_lad, METH_VARARGS | METH_KEYWORDS, "subisomorphic_lad(other, domains=None, induced=False, time_limit=0, \n" " return_mapping=False)\n\n" "Checks whether a subgraph of the graph is isomorphic to another graph.\n\n" "The optional C{domains} argument may be used to restrict vertices that\n" "may match each other. You can also specify whether you are interested\n" "in induced subgraphs only or not.\n\n" "@param other: the pattern graph we are looking for in the graph.\n" "@param domains: a list of lists, one sublist belonging to each vertex in\n" " the template graph. Sublist M{i} contains the indices of the vertices in\n" " the original graph that may match vertex M{i} in the template graph.\n" " C{None} means that every vertex may match every other vertex.\n" "@param induced: whether to consider induced subgraphs only.\n" "@param time_limit: an optimal time limit in seconds. Only the integral\n" " part of this number is taken into account. If the time limit is\n" " exceeded, the method will throw an exception.\n" "@param return_mapping: when C{True}, the function will return a tuple,\n" " where the first element is a boolean denoting whether a subisomorphism\n" " has been found or not, and the second element describes the mapping\n" " of the vertices from the template graph to the original graph. When\n" " C{False}, only the boolean is returned.\n" "@return: if no mapping is calculated, the result is C{True} if the graph\n" " contains a subgraph that is isomorphic to the given template, C{False}\n" " otherwise. If the mapping is calculated, the result is a tuple, the first\n" " element being the above mentioned boolean, and the second element being\n" " the mapping from the target to the original graph.\n"}, {"get_subisomorphisms_lad", (PyCFunction) igraphmodule_Graph_get_subisomorphisms_lad, METH_VARARGS | METH_KEYWORDS, "get_subisomorphisms_lad(other, domains=None, induced=False, time_limit=0)\n\n" "Returns all subisomorphisms between the graph and another one using the LAD\n" "algorithm.\n\n" "The optional C{domains} argument may be used to restrict vertices that\n" "may match each other. You can also specify whether you are interested\n" "in induced subgraphs only or not.\n\n" "@param other: the pattern graph we are looking for in the graph.\n" "@param domains: a list of lists, one sublist belonging to each vertex in\n" " the template graph. Sublist M{i} contains the indices of the vertices in\n" " the original graph that may match vertex M{i} in the template graph.\n" " C{None} means that every vertex may match every other vertex.\n" "@param induced: whether to consider induced subgraphs only.\n" "@param time_limit: an optimal time limit in seconds. Only the integral\n" " part of this number is taken into account. If the time limit is\n" " exceeded, the method will throw an exception.\n" "@return: a list of lists, each item of the list containing the mapping\n" " from vertices of the second graph to the vertices of the first one\n"}, //////////////////////// // ATTRIBUTE HANDLING // //////////////////////// {"attributes", (PyCFunction) igraphmodule_Graph_attributes, METH_NOARGS, "attributes()\n\n" "@return: the attribute name list of the graph\n"}, {"vertex_attributes", (PyCFunction) igraphmodule_Graph_vertex_attributes, METH_NOARGS, "vertex_attributes()\n\n" "@return: the attribute name list of the graph's vertices\n"}, {"edge_attributes", (PyCFunction) igraphmodule_Graph_edge_attributes, METH_NOARGS, "edge_attributes()\n\n" "@return: the attribute name list of the graph's edges\n"}, /////////////// // OPERATORS // /////////////// {"complementer", (PyCFunction) igraphmodule_Graph_complementer, METH_VARARGS, "complementer(loops=False)\n\n" "Returns the complementer of the graph\n\n" "@param loops: whether to include loop edges in the complementer.\n" "@return: the complementer of the graph\n"}, {"compose", (PyCFunction) igraphmodule_Graph_compose, METH_O, "compose(other)\n\nReturns the composition of two graphs."}, {"difference", (PyCFunction) igraphmodule_Graph_difference, METH_O, "difference(other)\n\nSubtracts the given graph from the original"}, {"disjoint_union", (PyCFunction) igraphmodule_Graph_disjoint_union, METH_O, "disjoint_union(graphs)\n\n" "Creates the disjoint union of two (or more) graphs.\n\n" "@param graphs: the list of graphs to be united with the current one.\n"}, {"intersection", (PyCFunction) igraphmodule_Graph_intersection, METH_O, "intersection(graphs)\n\n" "Creates the intersection of two (or more) graphs.\n\n" "@param graphs: the list of graphs to be intersected with\n" " the current one.\n"}, {"union", (PyCFunction) igraphmodule_Graph_union, METH_O, "union(graphs)\n\n" "Creates the union of two (or more) graphs.\n\n" "@param graphs: the list of graphs to be united with\n" " the current one.\n"}, /*****************/ /* MAXIMUM FLOWS */ /*****************/ {"maxflow_value", (PyCFunction) igraphmodule_Graph_maxflow_value, METH_VARARGS | METH_KEYWORDS, "maxflow_value(source, target, capacity=None)\n\n" "Returns the value of the maximum flow between the source and target vertices.\n\n" "@param source: the source vertex ID\n" "@param target: the target vertex ID\n" "@param capacity: the capacity of the edges. It must be a list or a valid\n" " attribute name or C{None}. In the latter case, every edge will have the\n" " same capacity.\n" "@return: the value of the maximum flow between the given vertices\n"}, {"maxflow", (PyCFunction) igraphmodule_Graph_maxflow, METH_VARARGS | METH_KEYWORDS, "maxflow(source, target, capacity=None)\n\n" "Returns the maximum flow between the source and target vertices.\n\n" "@attention: this function has a more convenient interface in class\n" " L{Graph} which wraps the result in a L{Flow} object. It is advised\n" " to use that.\n" "@param source: the source vertex ID\n" "@param target: the target vertex ID\n" "@param capacity: the capacity of the edges. It must be a list or a valid\n" " attribute name or C{None}. In the latter case, every edge will have the\n" " same capacity.\n" "@return: a tuple containing the following: the value of the maximum flow\n" " between the given vertices, the flow value on all the edges, the edge\n" " IDs that are part of the corresponding minimum cut, and the vertex IDs\n" " on one side of the cut. For directed graphs, the flow value vector gives\n" " the flow value on each edge. For undirected graphs, the flow value is\n" " positive if the flow goes from the smaller vertex ID to the bigger one\n" " and negative if the flow goes from the bigger vertex ID to the smaller." }, /**********************/ /* CUTS, MINIMUM CUTS */ /**********************/ {"all_st_cuts", (PyCFunction) igraphmodule_Graph_all_st_cuts, METH_VARARGS | METH_KEYWORDS, "all_st_cuts(source, target)\n\n" "Returns all the cuts between the source and target vertices in a\n" "directed graph.\n\n" "This function lists all edge-cuts between a source and a target vertex.\n" "Every cut is listed exactly once.\n\n" "@param source: the source vertex ID\n" "@param target: the target vertex ID\n" "@attention: this function has a more convenient interface in class\n" " L{Graph} which wraps the result in a list of L{Cut} objects. It is\n" " advised to use that.\n" "@return: a tuple where the first element is a list of lists of edge IDs\n" " representing a cut and the second element is a list of lists of vertex\n" " IDs representing the sets of vertices that were separated by the cuts.\n" }, {"all_st_mincuts", (PyCFunction) igraphmodule_Graph_all_st_mincuts, METH_VARARGS | METH_KEYWORDS, "all_st_mincuts(source, target)\n\n" "Returns all minimum cuts between the source and target vertices in a\n" "directed graph.\n\n" "@param source: the source vertex ID\n" "@param target: the target vertex ID\n" "@attention: this function has a more convenient interface in class\n" " L{Graph} which wraps the result in a list of L{Cut} objects. It is\n" " advised to use that.\n" }, {"mincut_value", (PyCFunction) igraphmodule_Graph_mincut_value, METH_VARARGS | METH_KEYWORDS, "mincut_value(source=-1, target=-1, capacity=None)\n\n" "Returns the minimum cut between the source and target vertices or within\n" "the whole graph.\n\n" "@param source: the source vertex ID. If negative, the calculation is\n" " done for every vertex except the target and the minimum is returned.\n" "@param target: the target vertex ID. If negative, the calculation is\n" " done for every vertex except the source and the minimum is returned.\n" "@param capacity: the capacity of the edges. It must be a list or a valid\n" " attribute name or C{None}. In the latter case, every edge will have the\n" " same capacity.\n" "@return: the value of the minimum cut between the given vertices\n"}, {"mincut", (PyCFunction) igraphmodule_Graph_mincut, METH_VARARGS | METH_KEYWORDS, "mincut(source=None, target=None, capacity=None)\n\n" "Calculates the minimum cut between the source and target vertices or\n" "within the whole graph.\n\n" "The minimum cut is the minimum set of edges that needs to be removed\n" "to separate the source and the target (if they are given) or to disconnect\n" "the graph (if the source and target are not given). The minimum is\n" "calculated using the weights (capacities) of the edges, so the cut with\n" "the minimum total capacity is calculated.\n" "For undirected graphs and no source and target, the method uses the Stoer-Wagner\n" "algorithm. For a given source and target, the method uses the push-relabel\n" "algorithm; see the references below.\n\n" "@attention: this function has a more convenient interface in class\n" " L{Graph} which wraps the result in a L{Cut} object. It is advised\n" " to use that.\n" "@param source: the source vertex ID. If C{None}, target must also be\n" " {None} and the calculation will be done for the entire graph (i.e. all\n" " possible vertex pairs).\n" "@param target: the target vertex ID. If C{None}, source must also be\n" " {None} and the calculation will be done for the entire graph (i.e. all\n" " possible vertex pairs).\n" "@param capacity: the capacity of the edges. It must be a list or a valid\n" " attribute name or C{None}. In the latter case, every edge will have the\n" " same capacity.\n" "@return: the value of the minimum cut, the IDs of vertices in the\n" " first and second partition, and the IDs of edges in the cut,\n" " packed in a 4-tuple\n\n" "@newfield ref: Reference\n" "@ref: M. Stoer, F. Wagner: A simple min-cut algorithm. Journal of\n" " the ACM 44(4):585-591, 1997.\n" "@ref: A. V. Goldberg, R. E. Tarjan: A new approach to the maximum-flow problem.\n" " Journal of the ACM 35(4):921-940, 1988.\n" }, {"st_mincut", (PyCFunction) igraphmodule_Graph_st_mincut, METH_VARARGS | METH_KEYWORDS, "st_mincut(source, target, capacity=None)\n\n" "Calculates the minimum cut between the source and target vertices in a\n" "graph.\n\n" "@param source: the source vertex ID\n" "@param target: the target vertex ID\n" "@param capacity: the capacity of the edges. It must be a list or a valid\n" " attribute name or C{None}. In the latter case, every edge will have the\n" " same capacity.\n" "@return: the value of the minimum cut, the IDs of vertices in the\n" " first and second partition, and the IDs of edges in the cut,\n" " packed in a 4-tuple\n\n" "@attention: this function has a more convenient interface in class\n" " L{Graph} which wraps the result in a list of L{Cut} objects. It is\n" " advised to use that.\n" }, {"gomory_hu_tree", (PyCFunction) igraphmodule_Graph_gomory_hu_tree, METH_VARARGS | METH_KEYWORDS, "gomory_hu_tree(capacity=None)\n\n" "Internal function, undocumented.\n\n" "@see: Graph.gomory_hu_tree()\n\n" }, /*********************/ /* VERTEX SEPARATORS */ /*********************/ {"all_minimal_st_separators", (PyCFunction) igraphmodule_Graph_all_minimal_st_separators, METH_NOARGS, "all_minimal_st_separators()\n\n" "Returns a list containing all the minimal s-t separators of a graph.\n\n" "A minimal separator is a set of vertices whose removal disconnects the graph,\n" "while the removal of any subset of the set keeps the graph connected.\n\n" "@return: a list where each item lists the vertex indices of a given\n" " minimal s-t separator.\n" "@newfield ref: Reference\n" "@ref: Anne Berry, Jean-Paul Bordat and Olivier Cogis: Generating all the\n" " minimal separators of a graph. In: Peter Widmayer, Gabriele Neyer and\n" " Stephan Eidenbenz (eds.): Graph-theoretic concepts in computer science,\n" " 1665, 167--172, 1999. Springer.\n"}, {"is_minimal_separator", (PyCFunction) igraphmodule_Graph_is_minimal_separator, METH_VARARGS | METH_KEYWORDS, "is_minimal_separator(vertices)\n\n" "Decides whether the given vertex set is a minimal separator.\n\n" "A minimal separator is a set of vertices whose removal disconnects the graph,\n" "while the removal of any subset of the set keeps the graph connected.\n\n" "@param vertices: a single vertex ID or a list of vertex IDs\n" "@return: C{True} is the given vertex set is a minimal separator, C{False}\n" " otherwise.\n"}, {"is_separator", (PyCFunction) igraphmodule_Graph_is_separator, METH_VARARGS | METH_KEYWORDS, "is_separator(vertices)\n\n" "Decides whether the removal of the given vertices disconnects the graph.\n\n" "@param vertices: a single vertex ID or a list of vertex IDs\n" "@return: C{True} is the given vertex set is a separator, C{False} if not.\n"}, {"minimum_size_separators", (PyCFunction) igraphmodule_Graph_minimum_size_separators, METH_NOARGS, "minimum_size_separators()\n\n" "Returns a list containing all separator vertex sets of minimum size.\n\n" "A vertex set is a separator if its removal disconnects the graph. This method\n" "lists all the separators for which no smaller separator set exists in the\n" "given graph.\n\n" "@return: a list where each item lists the vertex indices of a given\n" " separator of minimum size.\n" "@newfield ref: Reference\n" "@ref: Arkady Kanevsky: Finding all minimum-size separating vertex sets\n" " in a graph. Networks 23:533--541, 1993.\n"}, /*******************/ /* COHESIVE BLOCKS */ /*******************/ {"cohesive_blocks", (PyCFunction) igraphmodule_Graph_cohesive_blocks, METH_NOARGS, "cohesive_blocks()\n\n" "Calculates the cohesive block structure of the graph.\n\n" "@attention: this function has a more convenient interface in class\n" " L{Graph} which wraps the result in a L{CohesiveBlocks} object.\n" " It is advised to use that.\n" }, /********************************/ /* CLIQUES AND INDEPENDENT SETS */ /********************************/ {"cliques", (PyCFunction) igraphmodule_Graph_cliques, METH_VARARGS | METH_KEYWORDS, "cliques(min=0, max=0)\n\n" "Returns some or all cliques of the graph as a list of tuples.\n\n" "A clique is a complete subgraph -- a set of vertices where an edge\n" "is present between any two of them (excluding loops)\n\n" "@param min: the minimum size of cliques to be returned. If zero or\n" " negative, no lower bound will be used.\n" "@param max: the maximum size of cliques to be returned. If zero or\n" " negative, no upper bound will be used."}, {"largest_cliques", (PyCFunction) igraphmodule_Graph_largest_cliques, METH_NOARGS, "largest_cliques()\n\n" "Returns the largest cliques of the graph as a list of tuples.\n\n" "Quite intuitively a clique is considered largest if there is no clique\n" "with more vertices in the whole graph. All largest cliques are maximal\n" "(i.e. nonextendable) but not all maximal cliques are largest.\n\n" "@see: L{clique_number()} for the size of the largest cliques or\n" " L{maximal_cliques()} for the maximal cliques"}, {"maximal_cliques", (PyCFunction) igraphmodule_Graph_maximal_cliques, METH_VARARGS | METH_KEYWORDS, "maximal_cliques(min=0, max=0, file=None)\n\n" "Returns the maximal cliques of the graph as a list of tuples.\n\n" "A maximal clique is a clique which can't be extended by adding any other\n" "vertex to it. A maximal clique is not necessarily one of the largest\n" "cliques in the graph.\n\n" "@param min: the minimum size of maximal cliques to be returned. If zero\n" " or negative, no lower bound will be used.\n\n" "@param max: the maximum size of maximal cliques to be returned. If zero\n" " or negative, no upper bound will be used. If nonzero, the size of every\n" " maximal clique found will be compared to this value and a clique will\n" " be returned only if its size is smaller than this limit.\n\n" "@param file: a file object or the name of the file to write the results\n" " to. When this argument is C{None}, the maximal cliques will be returned\n" " as a list of lists.\n" "@return: the maximal cliques of the graph as a list of lists, or C{None}\n" " if the C{file} argument was given." "@see: L{largest_cliques()} for the largest cliques."}, {"clique_number", (PyCFunction) igraphmodule_Graph_clique_number, METH_NOARGS, "clique_number()\n\n" "Returns the clique number of the graph.\n\n" "The clique number of the graph is the size of the largest clique.\n\n" "@see: L{largest_cliques()} for the largest cliques."}, {"independent_vertex_sets", (PyCFunction) igraphmodule_Graph_independent_vertex_sets, METH_VARARGS | METH_KEYWORDS, "independent_vertex_sets(min=0, max=0)\n\n" "Returns some or all independent vertex sets of the graph as a list of tuples.\n\n" "Two vertices are independent if there is no edge between them. Members\n" "of an independent vertex set are mutually independent.\n\n" "@param min: the minimum size of sets to be returned. If zero or\n" " negative, no lower bound will be used.\n" "@param max: the maximum size of sets to be returned. If zero or\n" " negative, no upper bound will be used."}, {"largest_independent_vertex_sets", (PyCFunction) igraphmodule_Graph_largest_independent_vertex_sets, METH_NOARGS, "largest_independent_vertex_sets()\n\n" "Returns the largest independent vertex sets of the graph as a list of tuples.\n\n" "Quite intuitively an independent vertex set is considered largest if\n" "there is no other set with more vertices in the whole graph. All largest\n" "sets are maximal (i.e. nonextendable) but not all maximal sets\n" "are largest.\n\n" "@see: L{independence_number()} for the size of the largest independent\n" " vertex sets or L{maximal_independent_vertex_sets()} for the maximal\n" " (nonextendable) independent vertex sets"}, {"maximal_independent_vertex_sets", (PyCFunction) igraphmodule_Graph_maximal_independent_vertex_sets, METH_NOARGS, "maximal_independent_vertex_sets()\n\n" "Returns the maximal independent vertex sets of the graph as a list of tuples.\n\n" "A maximal independent vertex set is an independent vertex set\n" "which can't be extended by adding any other vertex to it. A maximal\n" "independent vertex set is not necessarily one of the largest\n" "independent vertex sets in the graph.\n\n" "@see: L{largest_independent_vertex_sets()} for the largest independent\n" " vertex sets\n\n" "@newfield ref: Reference\n" "@ref: S. Tsukiyama, M. Ide, H. Ariyoshi and I. Shirawaka: I{A new\n" " algorithm for generating all the maximal independent sets}.\n" " SIAM J Computing, 6:505--517, 1977."}, {"independence_number", (PyCFunction) igraphmodule_Graph_independence_number, METH_NOARGS, "independence_number()\n\n" "Returns the independence number of the graph.\n\n" "The independence number of the graph is the size of the largest\n" "independent vertex set.\n\n" "@see: L{largest_independent_vertex_sets()} for the largest independent\n" " vertex sets"}, /*********************************/ /* COMMUNITIES AND DECOMPOSITION */ /*********************************/ {"modularity", (PyCFunction) igraphmodule_Graph_modularity, METH_VARARGS | METH_KEYWORDS, "modularity(membership, weights=None)\n\n" "Calculates the modularity of the graph with respect to some vertex types.\n\n" "The modularity of a graph w.r.t. some division measures how good the\n" "division is, or how separated are the different vertex types from each\n" "other. It is defined as M{Q=1/(2m) * sum(Aij-ki*kj/(2m)delta(ci,cj),i,j)}.\n" "M{m} is the number of edges, M{Aij} is the element of the M{A} adjacency\n" "matrix in row M{i} and column M{j}, M{ki} is the degree of node M{i},\n" "M{kj} is the degree of node M{j}, and M{Ci} and C{cj} are the types of\n" "the two vertices (M{i} and M{j}). M{delta(x,y)} is one iff M{x=y}, 0\n" "otherwise.\n\n" "If edge weights are given, the definition of modularity is modified as\n" "follows: M{Aij} becomes the weight of the corresponding edge, M{ki}\n" "is the total weight of edges incident on vertex M{i}, M{kj} is the\n" "total weight of edges incident on vertex M{j} and M{m} is the total\n" "edge weight in the graph.\n\n" "@attention: method overridden in L{Graph} to allow L{VertexClustering}\n" " objects as a parameter. This method is not strictly necessary, since\n" " the L{VertexClustering} class provides a variable called C{modularity}.\n" "@param membership: the membership vector, e.g. the vertex type index for\n" " each vertex.\n" "@param weights: optional edge weights or C{None} if all edges are weighed\n" " equally.\n" "@return: the modularity score. Score larger than 0.3 usually indicates\n" " strong community structure.\n" "@newfield ref: Reference\n" "@ref: MEJ Newman and M Girvan: Finding and evaluating community structure\n" " in networks. Phys Rev E 69 026113, 2004.\n" }, {"coreness", (PyCFunction) igraphmodule_Graph_coreness, METH_VARARGS | METH_KEYWORDS, "coreness(mode=ALL)\n\n" "Finds the coreness (shell index) of the vertices of the network.\n\n" "The M{k}-core of a graph is a maximal subgraph in which each vertex\n" "has at least degree k. (Degree here means the degree in the\n" "subgraph of course). The coreness of a vertex is M{k} if it\n" "is a member of the M{k}-core but not a member of the M{k+1}-core.\n\n" "@param mode: whether to compute the in-corenesses (L{IN}), the\n" " out-corenesses (L{OUT}) or the undirected corenesses (L{ALL}).\n" " Ignored and assumed to be L{ALL} for undirected graphs.\n" "@return: the corenesses for each vertex.\n\n" "@newfield ref: Reference\n" "@ref: Vladimir Batagelj, Matjaz Zaversnik: I{An M{O(m)} Algorithm\n" " for Core Decomposition of Networks.}"}, {"community_fastgreedy", (PyCFunction) igraphmodule_Graph_community_fastgreedy, METH_VARARGS | METH_KEYWORDS, "community_fastgreedy(weights=None)\n\n" "Finds the community structure of the graph according to the algorithm of\n" "Clauset et al based on the greedy optimization of modularity.\n\n" "This is a bottom-up algorithm: initially every vertex belongs to a separate\n" "community, and communities are merged one by one. In every step, the two\n" "communities being merged are the ones which result in the maximal increase\n" "in modularity.\n\n" "@attention: this function is wrapped in a more convenient syntax in the\n" " derived class L{Graph}. It is advised to use that instead of this version.\n\n" "@param weights: name of an edge attribute or a list containing\n" " edge weights\n" "@return: a tuple with the following elements:\n" " 1. The list of merges\n" " 2. The modularity scores before each merge\n" "\n" "@newfield ref: Reference\n" "@ref: A. Clauset, M. E. J. Newman and C. Moore: I{Finding community\n" " structure in very large networks.} Phys Rev E 70, 066111 (2004).\n" "@see: modularity()\n" }, {"community_infomap", (PyCFunction) igraphmodule_Graph_community_infomap, METH_VARARGS | METH_KEYWORDS, "community_infomap(edge_weights=None, vertex_weights=None, trials=10)\n\n" "Finds the community structure of the network according to the Infomap\n" "method of Martin Rosvall and Carl T. Bergstrom.\n\n" "See U{http://www.mapequation.org} for a visualization of the algorithm\n" "or one of the references provided below.\n\n" "@param edge_weights: name of an edge attribute or a list containing\n" " edge weights.\n" "@param vertex_weights: name of an vertex attribute or a list containing\n" " vertex weights.\n" "@param trials: the number of attempts to partition the network.\n" "@return: the calculated membership vector and the corresponding\n" " codelength in a tuple.\n" "\n" "@newfield ref: Reference\n" "@ref: M. Rosvall and C. T. Bergstrom: I{Maps of information flow reveal\n" " community structure in complex networks}. PNAS 105, 1118 (2008).\n" " U{http://arxiv.org/abs/0707.0609}\n" "@ref: M. Rosvall, D. Axelsson and C. T. Bergstrom: I{The map equation}.\n" " Eur Phys J Special Topics 178, 13 (2009). U{http://arxiv.org/abs/0906.1405}\n" }, {"community_label_propagation", (PyCFunction) igraphmodule_Graph_community_label_propagation, METH_VARARGS | METH_KEYWORDS, "community_label_propagation(weights=None, initial=None, fixed=None)\n\n" "Finds the community structure of the graph according to the label\n" "propagation method of Raghavan et al.\n\n" "Initially, each vertex is assigned a different label. After that,\n" "each vertex chooses the dominant label in its neighbourhood in each\n" "iteration. Ties are broken randomly and the order in which the\n" "vertices are updated is randomized before every iteration. The algorithm\n" "ends when vertices reach a consensus.\n\n" "Note that since ties are broken randomly, there is no guarantee that\n" "the algorithm returns the same community structure after each run.\n" "In fact, they frequently differ. See the paper of Raghavan et al\n" "on how to come up with an aggregated community structure.\n\n" "@param weights: name of an edge attribute or a list containing\n" " edge weights\n" "@param initial: name of a vertex attribute or a list containing\n" " the initial vertex labels. Labels are identified by integers from\n" " zero to M{n-1} where M{n} is the number of vertices. Negative\n" " numbers may also be present in this vector, they represent unlabeled\n" " vertices.\n" "@param fixed: a list of booleans for each vertex. C{True} corresponds\n" " to vertices whose labeling should not change during the algorithm.\n" " It only makes sense if initial labels are also given. Unlabeled\n" " vertices cannot be fixed. Note that vertex attribute names are not\n" " accepted here.\n" "@return: the resulting membership vector\n" "\n" "@newfield ref: Reference\n" "@ref: Raghavan, U.N. and Albert, R. and Kumara, S. Near linear\n" " time algorithm to detect community structures in large-scale\n" " networks. Phys Rev E 76:036106, 2007. U{http://arxiv.org/abs/0709.2938}.\n" }, {"community_leading_eigenvector", (PyCFunction) igraphmodule_Graph_community_leading_eigenvector, METH_VARARGS | METH_KEYWORDS, "community_leading_eigenvector(n=-1, arpack_options=None, weights=None)\n\n" "A proper implementation of Newman's eigenvector community structure\n" "detection. Each split is done by maximizing the modularity regarding\n" "the original network. See the reference for details.\n\n" "@attention: this function is wrapped in a more convenient syntax in the\n" " derived class L{Graph}. It is advised to use that instead of this version.\n\n" "@param n: the desired number of communities. If negative, the algorithm\n" " tries to do as many splits as possible. Note that the algorithm\n" " won't split a community further if the signs of the leading eigenvector\n" " are all the same.\n" "@param arpack_options: an L{ARPACKOptions} object used to fine-tune\n" " the ARPACK eigenvector calculation. If omitted, the module-level\n" " variable called C{arpack_options} is used.\n" "@param weights: name of an edge attribute or a list containing\n" " edge weights\n" "@return: a tuple where the first element is the membership vector of the\n" " clustering and the second element is the merge matrix.\n\n" "@newfield ref: Reference\n" "@ref: MEJ Newman: Finding community structure in networks using the\n" " eigenvectors of matrices, arXiv:physics/0605087\n" }, {"community_multilevel", (PyCFunction) igraphmodule_Graph_community_multilevel, METH_VARARGS | METH_KEYWORDS, "community_multilevel(weights=None, return_levels=True)\n\n" "Finds the community structure of the graph according to the multilevel\n" "algorithm of Blondel et al. This is a bottom-up algorithm: initially\n" "every vertex belongs to a separate community, and vertices are moved\n" "between communities iteratively in a way that maximizes the vertices'\n" "local contribution to the overall modularity score. When a consensus is\n" "reached (i.e. no single move would increase the modularity score), every\n" "community in the original graph is shrank to a single vertex (while\n" "keeping the total weight of the incident edges) and the process continues\n" "on the next level. The algorithm stops when it is not possible to increase\n" "the modularity any more after shrinking the communities to vertices.\n\n" "@attention: this function is wrapped in a more convenient syntax in the\n" " derived class L{Graph}. It is advised to use that instead of this version.\n\n" "@param weights: name of an edge attribute or a list containing\n" " edge weights\n" "@param return_levels: if C{True}, returns the multilevel result. If\n" " C{False}, only the best level (corresponding to the best modularity)\n" " is returned.\n" "@return: either a single list describing the community membership of each\n" " vertex (if C{return_levels} is C{False}), or a list of community membership\n" " vectors, one corresponding to each level and a list of corresponding\n" " modularities (if C{return_levels} is C{True}).\n" "\n" "@newfield ref: Reference\n" "@ref: VD Blondel, J-L Guillaume, R Lambiotte and E Lefebvre: Fast\n" " unfolding of community hierarchies in large networks. J Stat Mech\n" " P10008 (2008), http://arxiv.org/abs/0803.0476\n" "@see: modularity()\n" }, {"community_edge_betweenness", (PyCFunction)igraphmodule_Graph_community_edge_betweenness, METH_VARARGS | METH_KEYWORDS, "community_edge_betweenness(directed=True, weights=None)\n\n" "Community structure detection based on the betweenness of the edges in\n" "the network. This algorithm was invented by M Girvan and MEJ Newman,\n" "see: M Girvan and MEJ Newman: Community structure in social and biological\n" "networks, Proc. Nat. Acad. Sci. USA 99, 7821-7826 (2002).\n\n" "The idea is that the betweenness of the edges connecting two communities\n" "is typically high. So we gradually remove the edge with the highest\n" "betweenness from the network and recalculate edge betweenness after every\n" "removal, as long as all edges are removed.\n\n" "@attention: this function is wrapped in a more convenient syntax in the\n" " derived class L{Graph}. It is advised to use that instead of this version.\n\n" "@param directed: whether to take into account the directedness of the edges\n" " when we calculate the betweenness values.\n" "@param weights: name of an edge attribute or a list containing\n" " edge weights.\n\n" "@return: a tuple with the merge matrix that describes the dendrogram\n" " and the modularity scores before each merge. The modularity scores\n" " use the weights if the original graph was weighted.\n" }, {"community_optimal_modularity", (PyCFunction) igraphmodule_Graph_community_optimal_modularity, METH_VARARGS | METH_KEYWORDS, "community_optimal_modularity(weights=None)\n\n" "Calculates the optimal modularity score of the graph and the\n" "corresponding community structure.\n\n" "This function uses the GNU Linear Programming Kit to solve a large\n" "integer optimization problem in order to find the optimal modularity\n" "score and the corresponding community structure, therefore it is\n" "unlikely to work for graphs larger than a few (less than a hundred)\n" "vertices. Consider using one of the heuristic approaches instead if\n" "you have such a large graph.\n\n" "@param weights: name of an edge attribute or a list containing\n" " edge weights.\n\n" "@return: the calculated membership vector and the corresponding\n" " modularity in a tuple.\n" }, {"community_spinglass", (PyCFunction) igraphmodule_Graph_community_spinglass, METH_VARARGS | METH_KEYWORDS, "community_spinglass(weights=None, spins=25, parupdate=False, " "start_temp=1, stop_temp=0.01, cool_fact=0.99, update_rule=\"config\", " "gamma=1, implementation=\"orig\", lambda=1)\n\n" "Finds the community structure of the graph according to the spinglass\n" "community detection method of Reichardt & Bornholdt.\n\n" "@param weights: edge weights to be used. Can be a sequence or iterable or\n" " even an edge attribute name.\n" "@param spins: integer, the number of spins to use. This is the upper limit\n" " for the number of communities. It is not a problem to supply a\n" " (reasonably) big number here, in which case some spin states will be\n" " unpopulated.\n" "@param parupdate: whether to update the spins of the vertices in parallel\n" " (synchronously) or not\n" "@param start_temp: the starting temperature\n" "@param stop_temp: the stop temperature\n" "@param cool_fact: cooling factor for the simulated annealing\n" "@param update_rule: specifies the null model of the simulation. Possible\n" " values are C{\"config\"} (a random graph with the same vertex degrees\n" " as the input graph) or C{\"simple\"} (a random graph with the same number\n" " of edges)\n" "@param gamma: the gamma argument of the algorithm, specifying the balance\n" " between the importance of present and missing edges within a community.\n" " The default value of 1.0 assigns equal importance to both of them.\n" "@param implementation: currently igraph contains two implementations for\n" " the spinglass community detection algorithm. The faster original\n" " implementation is the default. The other implementation is able to take\n" " into account negative weights, this can be chosen by setting\n" " C{implementation} to C{\"neg\"}.\n" "@param lambda: the lambda argument of the algorithm, which specifies the\n" " balance between the importance of present and missing negatively\n" " weighted edges within a community. Smaller values of lambda lead\n" " to communities with less negative intra-connectivity. If the argument\n" " is zero, the algorithm reduces to a graph coloring algorithm, using\n" " the number of spins as colors. This argument is ignored if the\n" " original implementation is used.\n" "@return: the community membership vector.\n" }, {"community_walktrap", (PyCFunction) igraphmodule_Graph_community_walktrap, METH_VARARGS | METH_KEYWORDS, "community_walktrap(weights=None, steps=None)\n\n" "Finds the community structure of the graph according to the random walk\n" "method of Latapy & Pons.\n\n" "The basic idea of the algorithm is that short random walks tend to stay\n" "in the same community. The method provides a dendrogram.\n\n" "@attention: this function is wrapped in a more convenient syntax in the\n" " derived class L{Graph}. It is advised to use that instead of this version.\n\n" "@param weights: name of an edge attribute or a list containing\n" " edge weights\n" "@return: a tuple with the list of merges and the modularity scores corresponding\n" " to each merge\n" "\n" "@newfield ref: Reference\n" "@ref: Pascal Pons, Matthieu Latapy: Computing communities in large networks\n" " using random walks, U{http://arxiv.org/abs/physics/0512106}.\n" "@see: modularity()\n" }, /*************/ /* MATCHINGS */ /*************/ {"_is_matching", (PyCFunction)igraphmodule_Graph_is_matching, METH_VARARGS | METH_KEYWORDS, "_is_matching(matching, types=None)\n\n" "Internal function, undocumented.\n\n" }, {"_is_maximal_matching", (PyCFunction)igraphmodule_Graph_is_maximal_matching, METH_VARARGS | METH_KEYWORDS, "_is_maximal_matching(matching, types=None)\n\n" "Internal function, undocumented.\n\n" "Use L{Matching.is_maximal} instead.\n" }, {"_maximum_bipartite_matching", (PyCFunction)igraphmodule_Graph_maximum_bipartite_matching, METH_VARARGS | METH_KEYWORDS, "_maximum_bipartite_matching(types, weights=None)\n\n" "Internal function, undocumented.\n\n" "@see: L{Graph.maximum_bipartite_matching}\n" }, /**********************/ /* INTERNAL FUNCTIONS */ /**********************/ #ifdef IGRAPH_PYTHON3 {"__graph_as_capsule", (PyCFunction) igraphmodule_Graph___graph_as_capsule__, METH_VARARGS | METH_KEYWORDS, "__graph_as_capsule()\n\n" "Returns the igraph graph encapsulated by the Python object as\n" "a PyCapsule\n\n." "A PyCapsule is practically a regular C pointer, wrapped in a\n" "Python object. This function should not be used directly by igraph\n" "users, it is useful only in the case when the underlying igraph object\n" "must be passed to other C code through Python.\n\n"}, #else {"__graph_as_cobject", (PyCFunction) igraphmodule_Graph___graph_as_cobject__, METH_VARARGS | METH_KEYWORDS, "__graph_as_cobject()\n\n" "Returns the igraph graph encapsulated by the Python object as\n" "a PyCObject\n\n." "A PyCObject is practically a regular C pointer, wrapped in a\n" "Python object. This function should not be used directly by igraph\n" "users, it is useful only in the case when the underlying igraph object\n" "must be passed to other C code through Python.\n\n"}, #endif {"_raw_pointer", (PyCFunction) igraphmodule_Graph__raw_pointer, METH_NOARGS, "_raw_pointer()\n\n" "Returns the memory address of the igraph graph encapsulated by the Python\n" "object as an ordinary Python integer.\n\n" "This function should not be used directly by igraph users, it is useful\n" "only if you want to access some unwrapped function in the C core of igraph\n" "using the ctypes module.\n\n"}, {"__register_destructor", (PyCFunction) igraphmodule_Graph___register_destructor__, METH_VARARGS | METH_KEYWORDS, "__register_destructor(destructor)\n\n" "Registers a destructor to be called when the object is freed by\n" "Python. This function should not be used directly by igraph users."}, {NULL} }; /** \ingroup python_interface_graph * This structure is the collection of functions necessary to implement * the graph as a mapping (i.e. to allow the retrieval and setting of * igraph attributes in Python as if it were of a Python mapping type) */ PyMappingMethods igraphmodule_Graph_as_mapping = { /* __len__ function intentionally left unimplemented */ 0, /* returns an attribute by name or returns part of the adjacency matrix */ (binaryfunc) igraphmodule_Graph_mp_subscript, /* sets an attribute by name or sets part of the adjacency matrix */ (objobjargproc) igraphmodule_Graph_mp_assign_subscript }; /** \ingroup python_interface * \brief Collection of methods to allow numeric operators to be used on the graph */ PyNumberMethods igraphmodule_Graph_as_number = { 0, /* nb_add */ 0, /*nb_subtract */ 0, /*nb_multiply */ #ifndef IGRAPH_PYTHON3 0, /*nb_divide */ #endif 0, /*nb_remainder */ 0, /*nb_divmod */ 0, /*nb_power */ 0, /*nb_negative */ 0, /*nb_positive */ 0, /*nb_absolute */ 0, /*nb_nonzero (2.x) / nb_bool (3.x) */ (unaryfunc) igraphmodule_Graph_complementer_op, /*nb_invert */ 0, /*nb_lshift */ 0, /*nb_rshift */ (binaryfunc) igraphmodule_Graph_intersection, /*nb_and */ 0, /*nb_xor */ (binaryfunc) igraphmodule_Graph_union, /*nb_or */ #ifndef IGRAPH_PYTHON3 0, /*nb_coerce */ #endif 0, /*nb_int */ 0, /*nb_long (2.x) / nb_reserved (3.x)*/ 0, /*nb_float */ #ifndef IGRAPH_PYTHON3 0, /*nb_oct */ 0, /*nb_hex */ #endif 0, /*nb_inplace_add */ 0, /*nb_inplace_subtract */ 0, /*nb_inplace_multiply */ #ifndef IGRAPH_PYTHON3 0, /*nb_inplace_divide */ #endif 0, /*nb_inplace_remainder */ 0, /*nb_inplace_power */ 0, /*nb_inplace_lshift */ 0, /*nb_inplace_rshift */ 0, /*nb_inplace_and */ 0, /*nb_inplace_xor */ 0, /*nb_inplace_or */ #ifdef IGRAPH_PYTHON3 0, /*nb_floor_divide */ 0, /*nb_true_divide */ 0, /*nb_inplace_floor_divide */ 0, /*nb_inplace_true_divide */ 0, /*nb_index */ #endif }; /** \ingroup python_interface_graph * Python type object referencing the methods Python calls when it performs various operations on an igraph (creating, printing and so on) */ PyTypeObject igraphmodule_GraphType = { PyVarObject_HEAD_INIT(0, 0) "igraph.Graph", /* tp_name */ sizeof(igraphmodule_GraphObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) igraphmodule_Graph_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare (2.x) / tp_reserved (3.x) */ 0, /* tp_repr */ &igraphmodule_Graph_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ &igraphmodule_Graph_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ (reprfunc) igraphmodule_Graph_str, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ "Low-level representation of a graph.\n\n" "Don't use it directly, use L{igraph.Graph} instead.\n\n" "@undocumented: _Bipartite, _Full_Bipartite, _GRG, _Incidence, _is_matching,\n" " _is_maximal_matching, _layout_sugiyama, _maximum_bipartite_matching,\n" " _spanning_tree\n" "@deffield ref: Reference", /* tp_doc */ (traverseproc) igraphmodule_Graph_traverse, /* tp_traverse */ (inquiry) igraphmodule_Graph_clear, /* tp_clear */ 0, /* tp_richcompare */ offsetof(igraphmodule_GraphObject, weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ igraphmodule_Graph_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) igraphmodule_Graph_init, /* tp_init */ 0, /* tp_alloc */ igraphmodule_Graph_new, /* tp_new */ 0, /* tp_free */ }; #undef CREATE_GRAPH python-igraph-0.7.1.post6/src/graphobject.h0000644000076500000240000004213412460534506021231 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_GRAPHOBJECT_H #define PYTHON_GRAPHOBJECT_H #include #include #include "structmember.h" #include "common.h" extern PyTypeObject igraphmodule_GraphType; /** * \ingroup python_interface * \brief A structure containing all the fields required to access an igraph from Python */ typedef struct { PyObject_HEAD // The graph object igraph_t g; // Python object to be called upon destruction PyObject* destructor; // Python object representing the sequence of vertices PyObject* vseq; // Python object representing the sequence of edges PyObject* eseq; // Python object of the weak reference list PyObject* weakreflist; } igraphmodule_GraphObject; void igraphmodule_Graph_init_internal(igraphmodule_GraphObject *self); PyObject* igraphmodule_Graph_new(PyTypeObject *type, PyObject *args, PyObject *kwds); int igraphmodule_Graph_clear(igraphmodule_GraphObject *self); int igraphmodule_Graph_traverse(igraphmodule_GraphObject *self, visitproc visit, void *arg); void igraphmodule_Graph_dealloc(igraphmodule_GraphObject* self); int igraphmodule_Graph_init(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_from_igraph_t(igraph_t *graph); PyObject* igraphmodule_Graph_str(igraphmodule_GraphObject *self); PyObject* igraphmodule_Graph_vcount(igraphmodule_GraphObject *self); PyObject* igraphmodule_Graph_ecount(igraphmodule_GraphObject *self); PyObject* igraphmodule_Graph_is_dag(igraphmodule_GraphObject *self); PyObject* igraphmodule_Graph_is_directed(igraphmodule_GraphObject *self); PyObject* igraphmodule_Graph_is_simple(igraphmodule_GraphObject *self); PyObject* igraphmodule_Graph_add_vertices(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_delete_vertices(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_add_edges(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_delete_edges(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_degree(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_is_loop(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_count_multiple(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_neighbors(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_successors(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_predecessors(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_get_eid(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Adjacency(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Asymmetric_Preference(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Atlas(PyTypeObject *type, PyObject *args); PyObject* igraphmodule_Graph_Barabasi(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Degree_Sequence(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Establishment(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Erdos_Renyi(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Famous(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Forest_Fire(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Full_Citation(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Full(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_GRG(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Growing_Random(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Isoclass(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Lattice(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_LCF(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Preference(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Recent_Degree(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Ring(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_SBM(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Star(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Tree(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Watts_Strogatz(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_is_connected(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_are_connected(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_adjacency_spectral_embedding(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_articulation_points(igraphmodule_GraphObject *self); PyObject* igraphmodule_Graph_average_path_length(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_betweenness(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_bibcoupling(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_closeness(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_clusters(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_cocitation(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_constraint(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_copy(igraphmodule_GraphObject *self); PyObject* igraphmodule_Graph_decompose(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_density(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_diameter(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_edge_betweenness(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_eigen_adjacency(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_get_shortest_paths(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_get_all_shortest_paths(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_maxdegree(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_pagerank(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_path_length_hist(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_reciprocity(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_rewire(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_shortest_paths(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_spanning_tree(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_simplify(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_subcomponent(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_subgraph(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_transitivity_undirected(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_transitivity_local_undirected(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_scan1(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_circle(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_sphere(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_random(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_random_3d(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_kamada_kawai(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_kamada_kawai_3d(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_drl(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_fruchterman_reingold(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_fruchterman_reingold_3d(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_grid_fruchterman_reingold(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_lgl(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_layout_reingold_tilford(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_get_adjacency(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_get_edgelist(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_to_undirected(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_to_directed(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_laplacian(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Read_DIMACS(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Read_Edgelist(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Read_GML(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Read_Ncol(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Read_Lgl(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Read_Pajek(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_Read_GraphML(PyTypeObject *type, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_write_dimacs(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_write_dot(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_write_edgelist(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_write_ncol(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_write_lgl(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_write_gml(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_write_graphml(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_isoclass(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_isomorphic(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_count_isomorphisms(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_get_isomorphisms(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_subisomorphic(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_count_subisomorphisms(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_get_subisomorphisms(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); Py_ssize_t igraphmodule_Graph_attribute_count(igraphmodule_GraphObject* self); PyObject* igraphmodule_Graph_get_attribute(igraphmodule_GraphObject* self, PyObject* s); int igraphmodule_Graph_set_attribute(igraphmodule_GraphObject* self, PyObject* k, PyObject* v); PyObject* igraphmodule_Graph_attributes(igraphmodule_GraphObject* self); PyObject* igraphmodule_Graph_vertex_attributes(igraphmodule_GraphObject* self); PyObject* igraphmodule_Graph_edge_attributes(igraphmodule_GraphObject* self); PyObject* igraphmodule_Graph_get_vertices(igraphmodule_GraphObject* self, void* closure); PyObject* igraphmodule_Graph_get_edges(igraphmodule_GraphObject* self, void* closure); PyObject* igraphmodule_Graph_complementer(igraphmodule_GraphObject* self, PyObject* args); PyObject* igraphmodule_Graph_complementer_op(igraphmodule_GraphObject* self); PyObject* igraphmodule_Graph_compose(igraphmodule_GraphObject* self, PyObject* other); PyObject* igraphmodule_Graph_difference(igraphmodule_GraphObject* self, PyObject* other); PyObject* igraphmodule_Graph_disjoint_union(igraphmodule_GraphObject* self, PyObject* other); PyObject* igraphmodule_Graph_intersection(igraphmodule_GraphObject* self, PyObject* other); PyObject* igraphmodule_Graph_union(igraphmodule_GraphObject* self, PyObject* other); PyObject* igraphmodule_Graph_bfs(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_bfsiter(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_maxflow(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_maxflow_value(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_mincut(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_mincut_value(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_cliques(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_maximal_cliques(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_largest_cliques(igraphmodule_GraphObject* self); PyObject* igraphmodule_Graph_clique_number(igraphmodule_GraphObject* self); PyObject* igraphmodule_Graph_independent_sets(igraphmodule_GraphObject* self, PyObject* args, PyObject* kwds); PyObject* igraphmodule_Graph_maximal_independent_sets(igraphmodule_GraphObject* self); PyObject* igraphmodule_Graph_largest_independent_sets(igraphmodule_GraphObject* self); PyObject* igraphmodule_Graph_independence_number(igraphmodule_GraphObject* self); PyObject* igraphmodule_Graph_community_edge_betweenness(igraphmodule_GraphObject* self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_community_fastgreedy(igraphmodule_GraphObject* self, PyObject *args, PyObject *kwds); PyObject *igraphmodule_Graph_community_infomap(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_community_label_propagation(igraphmodule_GraphObject* self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_community_leading_eigenvector(igraphmodule_GraphObject* self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_community_leading_eigenvector_naive(igraphmodule_GraphObject* self, PyObject *args, PyObject *kwds); PyObject *igraphmodule_Graph_community_multilevel(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject *igraphmodule_Graph_community_optimal_modularity(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_community_spinglass(igraphmodule_GraphObject* self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_community_walktrap(igraphmodule_GraphObject* self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph_modularity(igraphmodule_GraphObject* self, PyObject *args, PyObject *kwds); PyObject *igraphmodule_Graph_is_bipartite(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph___graph_as_cobject__(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); PyObject* igraphmodule_Graph___register_destructor__(igraphmodule_GraphObject *self, PyObject *args, PyObject *kwds); #endif python-igraph-0.7.1.post6/src/igraphmodule.c0000644000076500000240000007513612460534624021425 0ustar ntamasstaff00000000000000/* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "arpackobject.h" #include "attributes.h" #include "bfsiter.h" #include "common.h" #include "convert.h" #include "edgeobject.h" #include "edgeseqobject.h" #include "error.h" #include "graphobject.h" #include "py2compat.h" #include "random.h" #include "vertexobject.h" #include "vertexseqobject.h" #define IGRAPH_MODULE #include "igraphmodule_api.h" extern double igraph_i_fdiv(double, double); /** * \defgroup python_interface Python module implementation * \brief Functions implementing a Python interface to \a igraph * * These functions provide a way to access \a igraph functions from Python. * It should be of interest of \a igraph developers only. Classes, functions * and methods exposed to Python are still to be documented. Until it is done, * just type the following to get help about \a igraph functions in Python * (assuming you have \c igraph.so somewhere in your Python library path): * * \verbatim import igraph help(igraph) help(igraph.Graph) \endverbatim * * Most of the functions provided here share the same calling conventions * (which are determined by the Python/C API). Since the role of the * arguments are the same across many functions, I won't explain them * everywhere, just give a quick overview of the common argument names here. * * \param self the Python igraph.Graph object the method is working on * \param args pointer to the Python tuple containing the arguments * \param kwds pointer to the Python hash containing the keyword parameters * \param type the type object of a Python igraph.Graph object. Used usually * in constructors and class methods. * * Any arguments not documented here should be mentioned at the documentation * of the appropriate method. * * The functions which implement a Python method always return a pointer to * a \c PyObject. According to Python conventions, this is \c NULL if and * only if an exception was thrown by the method (or any of the functions * it has called). When I explain the return value of a function which * provides interface to an \a igraph function, I won't cover the case of * returning a \c NULL value, because this is the same for every such method. * The conclusion is that a method can return \c NULL even if I don't state * it explicitly. * * Also please take into consideration that I'm documenting the C calls * with the abovementioned parameters here, and \em not the Python methods * which are presented to the user using the Python interface of \a igraph. * If you are looking for the documentation of the classes, methods and * functions exposed to Python, please use the \c help calls from Python * or use \c pydoc to generate a formatted version. * * \section weakrefs The usage of weak references in the Python interface * * Many classes implemented in the Python interface (e.g. VertexSeq, Vertex...) * use weak references to keep track of the graph they are referencing to. * The use of weak references is twofold: * * -# If we assign a VertexSeq or a Vertex of a given graph to a local * variable and then destroy the graph, real references keep the graph * alive and do not return the memory back to Python. * -# If we use real references, a Graph object will hold a reference * to its VertexSeq (because we don't want to allocate a new VertexSeq * object for the same graph every time it is requested), and the * VertexSeq will also hold a reference to the Graph. This is a circular * reference. Python does not reclaim the memory occupied by the Graph * back when the Graph is destroyed, because the VertexSeq is holding a * reference to it. Similarly, VertexSeq doesn't get freed because the * Graph is holding a reference to it. These situations can only be * resolved by the Python garbage collector which is invoked at regular * intervals. Unfortunately, the garbage collector refuses to break * circular references and free the objects participating in the circle * when any of the objects has a \c __del__ method. In this case, * \c igraph.Graph has one (which frees the underlying \c igraph_t * graph), therefore our graphs never get freed when we use real * references. */ /** * Whether the module was initialized already */ static igraph_bool_t igraphmodule_initialized = 0; /** * Module-specific global variables */ struct module_state { PyObject* progress_handler; PyObject* status_handler; }; static struct module_state _state = { 0, 0 }; #define GETSTATE(m) (&_state) #ifdef IGRAPH_PYTHON3 static int igraphmodule_traverse(PyObject *m, visitproc visit, void* arg) { Py_VISIT(GETSTATE(m)->progress_handler); Py_VISIT(GETSTATE(m)->status_handler); return 0; } static int igraphmodule_clear(PyObject *m) { Py_CLEAR(GETSTATE(m)->progress_handler); Py_CLEAR(GETSTATE(m)->status_handler); return 0; } #endif static int igraphmodule_igraph_interrupt_hook(void* data) { if (PyErr_CheckSignals()) { IGRAPH_FINALLY_FREE(); return IGRAPH_INTERRUPTED; } return IGRAPH_SUCCESS; } int igraphmodule_igraph_progress_hook(const char* message, igraph_real_t percent, void* data) { PyObject* progress_handler = GETSTATE(0)->progress_handler; if (progress_handler) { PyObject *result; if (PyCallable_Check(progress_handler)) { result=PyObject_CallFunction(progress_handler, "sd", message, (double)percent); if (result) Py_DECREF(result); else return IGRAPH_INTERRUPTED; } } return IGRAPH_SUCCESS; } int igraphmodule_igraph_status_hook(const char* message, void*data) { PyObject* status_handler = GETSTATE(0)->status_handler; if (status_handler) { PyObject *result; if (PyCallable_Check(status_handler)) { result = PyObject_CallFunction(status_handler, "s", message); if (result) Py_DECREF(result); else return IGRAPH_INTERRUPTED; } } return IGRAPH_SUCCESS; } PyObject* igraphmodule_set_progress_handler(PyObject* self, PyObject* o) { PyObject* progress_handler; if (!PyCallable_Check(o) && o != Py_None) { PyErr_SetString(PyExc_TypeError, "Progress handler must be callable."); return NULL; } progress_handler = GETSTATE(self)->progress_handler; if (o == progress_handler) Py_RETURN_NONE; Py_XDECREF(progress_handler); if (o == Py_None) o = 0; Py_XINCREF(o); GETSTATE(self)->progress_handler=o; Py_RETURN_NONE; } PyObject* igraphmodule_set_status_handler(PyObject* self, PyObject* o) { PyObject* status_handler; if (!PyCallable_Check(o) && o != Py_None) { PyErr_SetString(PyExc_TypeError, "Status handler must be callable."); return NULL; } status_handler = GETSTATE(self)->status_handler; if (o == status_handler) Py_RETURN_NONE; Py_XDECREF(status_handler); if (o == Py_None) o = 0; Py_INCREF(o); GETSTATE(self)->status_handler = o; Py_RETURN_NONE; } PyObject* igraphmodule_convex_hull(PyObject* self, PyObject* args, PyObject* kwds) { static char* kwlist[] = {"vs", "coords", NULL}; PyObject *vs, *o, *o1=0, *o2=0, *coords = Py_False; igraph_matrix_t mtrx; igraph_vector_t result; igraph_matrix_t resmat; long no_of_nodes, i; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O", kwlist, &PyList_Type, &vs, &coords)) return NULL; no_of_nodes=PyList_Size(vs); if (igraph_matrix_init(&mtrx, no_of_nodes, 2)) { igraphmodule_handle_igraph_error(); return NULL; } for (i=0; i= 2) { o1=PyList_GetItem(o, 0); o2=PyList_GetItem(o, 1); if (PyList_Size(o) > 2) PyErr_Warn(PyExc_Warning, "vertex with more than 2 coordinates found, considering only the first 2"); } else { PyErr_SetString(PyExc_TypeError, "vertex with less than 2 coordinates found"); igraph_matrix_destroy(&mtrx); return NULL; } } else if (PyTuple_Check(o)) { if (PyTuple_Size(o) >= 2) { o1=PyTuple_GetItem(o, 0); o2=PyTuple_GetItem(o, 1); if (PyTuple_Size(o) > 2) PyErr_Warn(PyExc_Warning, "vertex with more than 2 coordinates found, considering only the first 2"); } else { PyErr_SetString(PyExc_TypeError, "vertex with less than 2 coordinates found"); igraph_matrix_destroy(&mtrx); return NULL; } } if (!PyNumber_Check(o1) || !PyNumber_Check(o2)) { PyErr_SetString(PyExc_TypeError, "vertex coordinates must be numeric"); igraph_matrix_destroy(&mtrx); return NULL; } /* o, o1 and o2 were borrowed, but now o1 and o2 are actual references! */ o1=PyNumber_Float(o1); o2=PyNumber_Float(o2); if (!o1 || !o2) { PyErr_SetString(PyExc_TypeError, "vertex coordinate conversion to float failed"); Py_XDECREF(o1); Py_XDECREF(o2); igraph_matrix_destroy(&mtrx); return NULL; } MATRIX(mtrx, i, 0)=(igraph_real_t)PyFloat_AsDouble(o1); MATRIX(mtrx, i, 1)=(igraph_real_t)PyFloat_AsDouble(o2); Py_DECREF(o1); Py_DECREF(o2); } if (!PyObject_IsTrue(coords)) { if (igraph_vector_init(&result, 0)) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&mtrx); return NULL; } if (igraph_convex_hull(&mtrx, &result, 0)) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&mtrx); igraph_vector_destroy(&result); return NULL; } o=igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&result); } else { if (igraph_matrix_init(&resmat, 0, 0)) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&mtrx); return NULL; } if (igraph_convex_hull(&mtrx, 0, &resmat)) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&mtrx); igraph_matrix_destroy(&resmat); return NULL; } o=igraphmodule_matrix_t_to_PyList(&resmat, IGRAPHMODULE_TYPE_FLOAT); igraph_matrix_destroy(&resmat); } igraph_matrix_destroy(&mtrx); return o; } PyObject* igraphmodule_community_to_membership(PyObject *self, PyObject *args, PyObject *kwds) { static char* kwlist[] = { "merges", "nodes", "steps", "return_csize", NULL }; PyObject *merges_o, *return_csize = Py_False, *result_o; igraph_matrix_t merges; igraph_vector_t result, csize, *csize_p = 0; long int nodes, steps; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!ll|O", kwlist, &PyList_Type, &merges_o, &nodes, &steps, &return_csize)) return NULL; if (igraphmodule_PyList_to_matrix_t(merges_o, &merges)) return NULL; if (igraph_vector_init(&result, nodes)) { igraphmodule_handle_igraph_error(); igraph_matrix_destroy(&merges); return NULL; } if (PyObject_IsTrue(return_csize)) { igraph_vector_init(&csize, 0); csize_p = &csize; } if (igraph_community_to_membership(&merges, (igraph_integer_t)nodes, (igraph_integer_t)steps, &result, csize_p)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&result); if (csize_p) igraph_vector_destroy(csize_p); igraph_matrix_destroy(&merges); return NULL; } igraph_matrix_destroy(&merges); result_o = igraphmodule_vector_t_to_PyList(&result, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&result); if (csize_p) { PyObject* csize_o = igraphmodule_vector_t_to_PyList(csize_p, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(csize_p); if (csize_o) return Py_BuildValue("NN", result_o, csize_o); Py_DECREF(result_o); return NULL; } return result_o; } PyObject* igraphmodule_compare_communities(PyObject *self, PyObject *args, PyObject *kwds) { static char* kwlist[] = { "comm1", "comm2", "method", NULL }; PyObject *comm1_o, *comm2_o, *method_o = Py_None; igraph_vector_t comm1, comm2; igraph_community_comparison_t method = IGRAPH_COMMCMP_VI; igraph_real_t result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, &comm1_o, &comm2_o, &method_o)) return NULL; if (igraphmodule_PyObject_to_community_comparison_t(method_o, &method)) return NULL; if (igraphmodule_PyObject_to_vector_t(comm1_o, &comm1, 0)) return NULL; if (igraphmodule_PyObject_to_vector_t(comm2_o, &comm2, 0)) { igraph_vector_destroy(&comm1); return NULL; } if (igraph_compare_communities(&comm1, &comm2, &result, method)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&comm1); igraph_vector_destroy(&comm2); return NULL; } igraph_vector_destroy(&comm1); igraph_vector_destroy(&comm2); return PyFloat_FromDouble((double)result); } PyObject* igraphmodule_is_degree_sequence(PyObject *self, PyObject *args, PyObject *kwds) { static char* kwlist[] = { "out_deg", "in_deg", NULL }; PyObject *out_deg_o = 0, *in_deg_o = 0; igraph_vector_t out_deg, in_deg; igraph_bool_t is_directed, result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &out_deg_o, &in_deg_o)) return NULL; is_directed = (in_deg_o != 0 && in_deg_o != Py_None); if (igraphmodule_PyObject_to_vector_t(out_deg_o, &out_deg, 0)) return NULL; if (is_directed && igraphmodule_PyObject_to_vector_t(in_deg_o, &in_deg, 0)) { igraph_vector_destroy(&out_deg); return NULL; } if (igraph_is_degree_sequence(&out_deg, is_directed ? &in_deg : 0, &result)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&out_deg); if (is_directed) igraph_vector_destroy(&in_deg); return NULL; } igraph_vector_destroy(&out_deg); if (is_directed) igraph_vector_destroy(&in_deg); if (result) Py_RETURN_TRUE; else Py_RETURN_FALSE; } PyObject* igraphmodule_is_graphical_degree_sequence(PyObject *self, PyObject *args, PyObject *kwds) { static char* kwlist[] = { "out_deg", "in_deg", NULL }; PyObject *out_deg_o = 0, *in_deg_o = 0; igraph_vector_t out_deg, in_deg; igraph_bool_t is_directed, result; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &out_deg_o, &in_deg_o)) return NULL; is_directed = (in_deg_o != 0 && in_deg_o != Py_None); if (igraphmodule_PyObject_to_vector_t(out_deg_o, &out_deg, 0)) return NULL; if (is_directed && igraphmodule_PyObject_to_vector_t(in_deg_o, &in_deg, 0)) { igraph_vector_destroy(&out_deg); return NULL; } if (igraph_is_graphical_degree_sequence(&out_deg, is_directed ? &in_deg : 0, &result)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&out_deg); if (is_directed) igraph_vector_destroy(&in_deg); return NULL; } igraph_vector_destroy(&out_deg); if (is_directed) igraph_vector_destroy(&in_deg); if (result) Py_RETURN_TRUE; else Py_RETURN_FALSE; } PyObject* igraphmodule_power_law_fit(PyObject *self, PyObject *args, PyObject *kwds) { static char* kwlist[] = { "data", "xmin", "force_continuous", NULL }; PyObject *data_o, *force_continuous_o = Py_False; igraph_vector_t data; igraph_plfit_result_t result; double xmin = -1; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|dO", kwlist, &data_o, &xmin, &force_continuous_o)) return NULL; if (igraphmodule_PyObject_float_to_vector_t(data_o, &data)) return NULL; if (igraph_power_law_fit(&data, &result, xmin, PyObject_IsTrue(force_continuous_o))) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&data); return NULL; } igraph_vector_destroy(&data); return Py_BuildValue("Oddddd", result.continuous ? Py_True : Py_False, result.alpha, result.xmin, result.L, result.D, result.p); } PyObject* igraphmodule_split_join_distance(PyObject *self, PyObject *args, PyObject *kwds) { static char* kwlist[] = { "comm1", "comm2", NULL }; PyObject *comm1_o, *comm2_o; igraph_vector_t comm1, comm2; igraph_integer_t distance12, distance21; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &comm1_o, &comm2_o)) return NULL; if (igraphmodule_PyObject_to_vector_t(comm1_o, &comm1, 0)) return NULL; if (igraphmodule_PyObject_to_vector_t(comm2_o, &comm2, 0)) { igraph_vector_destroy(&comm1); return NULL; } if (igraph_split_join_distance(&comm1, &comm2, &distance12, &distance21)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&comm1); igraph_vector_destroy(&comm2); return NULL; } igraph_vector_destroy(&comm1); igraph_vector_destroy(&comm2); return Py_BuildValue("ll", (long)distance12, (long)distance21); } /** \ingroup python_interface * \brief Method table for the igraph Python module */ static PyMethodDef igraphmodule_methods[] = { {"community_to_membership", (PyCFunction)igraphmodule_community_to_membership, METH_VARARGS | METH_KEYWORDS, "community_to_membership(merges, nodes, steps, return_csize=False)" }, {"_compare_communities", (PyCFunction)igraphmodule_compare_communities, METH_VARARGS | METH_KEYWORDS, "_compare_communities(comm1, comm2, method=\"vi\")" }, {"_power_law_fit", (PyCFunction)igraphmodule_power_law_fit, METH_VARARGS | METH_KEYWORDS, "_power_law_fit(data, xmin=-1, force_continuous=False)" }, {"convex_hull", (PyCFunction)igraphmodule_convex_hull, METH_VARARGS | METH_KEYWORDS, "convex_hull(vs, coords=False)\n\n" "Calculates the convex hull of a given point set.\n\n" "@param vs: the point set as a list of lists\n" "@param coords: if C{True}, the function returns the\n" " coordinates of the corners of the convex hull polygon,\n" " otherwise returns the corner indices.\n" "@return: either the hull's corner coordinates or the point\n" " indices corresponding to them, depending on the C{coords}\n" " parameter." }, {"is_degree_sequence", (PyCFunction)igraphmodule_is_degree_sequence, METH_VARARGS | METH_KEYWORDS, "is_degree_sequence(out_deg, in_deg=None)\n\n" "Returns whether a list of degrees can be a degree sequence of some graph.\n\n" "Note that it is not required for the graph to be simple; in other words,\n" "this function may return C{True} for degree sequences that can be realized\n" "using one or more multiple or loop edges only.\n\n" "In particular, this function checks whether\n\n" " - all the degrees are non-negative\n" " - for undirected graphs, the sum of degrees are even\n" " - for directed graphs, the two degree sequences are of the same length and\n" " equal sums\n\n" "@param out_deg: the list of degrees. For directed graphs, this list must\n" " contain the out-degrees of the vertices.\n" "@param in_deg: the list of in-degrees for directed graphs. This parameter\n" " must be C{None} for undirected graphs.\n" "@return: C{True} if there exists some graph that can realize the given degree\n" " sequence, C{False} otherwise." "@see: L{is_graphical_degree_sequence()} if you do not want to allow multiple\n" " or loop edges.\n" }, {"is_graphical_degree_sequence", (PyCFunction)igraphmodule_is_graphical_degree_sequence, METH_VARARGS | METH_KEYWORDS, "is_graphical_degree_sequence(out_deg, in_deg=None)\n\n" "Returns whether a list of degrees can be a degree sequence of some simple graph.\n\n" "Note that it is required for the graph to be simple; in other words,\n" "this function will return C{False} for degree sequences that cannot be realized\n" "without using one or more multiple or loop edges.\n\n" "@param out_deg: the list of degrees. For directed graphs, this list must\n" " contain the out-degrees of the vertices.\n" "@param in_deg: the list of in-degrees for directed graphs. This parameter\n" " must be C{None} for undirected graphs.\n" "@return: C{True} if there exists some simple graph that can realize the given\n" " degree sequence, C{False} otherwise.\n" "@see: L{is_degree_sequence()} if you want to allow multiple or loop edges.\n" }, {"set_progress_handler", igraphmodule_set_progress_handler, METH_O, "set_progress_handler(handler)\n\n" "Sets the handler to be called when igraph is performing a long operation.\n" "@param handler: the progress handler function. It must accept two\n" " arguments, the first is the message informing the user about\n" " what igraph is doing right now, the second is the actual\n" " progress information (a percentage).\n" }, {"set_random_number_generator", igraph_rng_Python_set_generator, METH_O, "set_random_number_generator(generator)\n\n" "Sets the random number generator used by igraph.\n" "@param generator: the generator to be used. It must be a Python object\n" " with at least three attributes: C{random}, C{randint} and C{gauss}.\n" " Each of them must be callable and their signature and behaviour\n" " must be identical to C{random.random}, C{random.randint} and\n" " C{random.gauss}. By default, igraph uses the C{random} module for\n" " random number generation, but you can supply your alternative\n" " implementation here. If the given generator is C{None}, igraph\n" " reverts to the default Mersenne twister generator implemented in the\n" " C layer, which might be slightly faster than calling back to Python\n" " for random numbers, but you cannot set its seed or save its state.\n" }, {"set_status_handler", igraphmodule_set_status_handler, METH_O, "set_status_handler(handler)\n\n" "Sets the handler to be called when igraph tries to display a status\n" "message.\n\n" "This is used to communicate the progress of some calculations where\n" "no reasonable progress percentage can be given (so it is not possible\n" "to use the progress handler).\n\n" "@param handler: the status handler function. It must accept a single\n" " argument, the message that informs the user about what igraph is\n" " doing right now.\n" }, {"_split_join_distance", (PyCFunction)igraphmodule_split_join_distance, METH_VARARGS | METH_KEYWORDS, "_split_join_distance(comm1, comm2)" }, {NULL, NULL, 0, NULL} }; #define MODULE_DOCS \ "Low-level Python interface for the igraph library. " \ "Should not be used directly.\n\n" \ "@undocumented: community_to_membership, _compare_communities, _power_law_fit, " \ "_split_join_distance" /** * Module definition table (only for Python 3.x) */ #ifdef IGRAPH_PYTHON3 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "igraph._igraph", /* m_name */ MODULE_DOCS, /* m_doc */ sizeof(struct module_state), /* m_size */ igraphmodule_methods, /* m_methods */ 0, /* m_reload */ igraphmodule_traverse, /* m_traverse */ igraphmodule_clear, /* m_clear */ 0 /* m_free */ }; #endif /****************** Exported API functions *******************/ /** * \brief Constructs a new Python Graph object from an existing igraph_t * * The newly created Graph object will take ownership of igraph_t and * it will destroy it when the Python object is destructed. * * Returns a null pointer in case of an error and sets the appropriate * Python exception. */ PyObject* PyIGraph_FromCGraph(igraph_t* g) { return igraphmodule_Graph_from_igraph_t(g); } /** * \brief Extracts the pointer to the \c igraph_t held by a Graph instance * * The ownership of the \c igraph_t object remains with the Graph instance, * so make sure you don't call \c igraph_destroy() on the extracted pointer. * * Returns a null pointer in case of an error and sets the appropriate * Python exception. */ igraph_t* PyIGraph_ToCGraph(PyObject* graph) { igraph_t *result = 0; if (graph == Py_None) { PyErr_SetString(PyExc_TypeError, "expected Graph, got None"); return 0; } if (igraphmodule_PyObject_to_igraph_t(graph, &result)) return 0; if (result == 0) PyErr_SetString(PyExc_ValueError, "null pointer stored inside a Graph " "object. Probably a bug."); return result; } extern PyObject* igraphmodule_InternalError; extern PyObject* igraphmodule_arpack_options_default; #ifdef IGRAPH_PYTHON3 # define INITERROR return NULL PyObject* PyInit__igraph(void) #else # define INITERROR return # ifndef PyMODINIT_FUNC # define PyMODINIT_FUNC void # endif PyMODINIT_FUNC init_igraph(void) #endif { PyObject* m; static void *PyIGraph_API[PyIGraph_API_pointers]; PyObject *c_api_object; /* Check if the module is already initialized (possibly in another Python * interpreter. If so, bail out as we don't support this. */ if (igraphmodule_initialized) { PyErr_SetString(PyExc_RuntimeError, "igraph module is already initialized " "in a different Python interpreter"); INITERROR; } /* Initialize VertexSeq, EdgeSeq */ if (PyType_Ready(&igraphmodule_VertexSeqType) < 0) INITERROR; if (PyType_Ready(&igraphmodule_EdgeSeqType) < 0) INITERROR; /* Initialize Vertex, Edge */ igraphmodule_VertexType.tp_clear = (inquiry)igraphmodule_Vertex_clear; if (PyType_Ready(&igraphmodule_VertexType) < 0) INITERROR; igraphmodule_EdgeType.tp_clear = (inquiry)igraphmodule_Edge_clear; if (PyType_Ready(&igraphmodule_EdgeType) < 0) INITERROR; /* Initialize Graph, BFSIter, ARPACKOptions etc */ if (PyType_Ready(&igraphmodule_GraphType) < 0) INITERROR; if (PyType_Ready(&igraphmodule_BFSIterType) < 0) INITERROR; if (PyType_Ready(&igraphmodule_ARPACKOptionsType) < 0) INITERROR; /* Initialize the core module */ #ifdef IGRAPH_PYTHON3 m = PyModule_Create(&moduledef); #else m = Py_InitModule3("igraph._igraph", igraphmodule_methods, MODULE_DOCS); #endif if (m == NULL) INITERROR; /* Initialize random number generator */ igraphmodule_init_rng(m); /* Add the types to the core module */ PyModule_AddObject(m, "GraphBase", (PyObject*)&igraphmodule_GraphType); PyModule_AddObject(m, "BFSIter", (PyObject*)&igraphmodule_BFSIterType); PyModule_AddObject(m, "ARPACKOptions", (PyObject*)&igraphmodule_ARPACKOptionsType); PyModule_AddObject(m, "Edge", (PyObject*)&igraphmodule_EdgeType); PyModule_AddObject(m, "EdgeSeq", (PyObject*)&igraphmodule_EdgeSeqType); PyModule_AddObject(m, "Vertex", (PyObject*)&igraphmodule_VertexType); PyModule_AddObject(m, "VertexSeq", (PyObject*)&igraphmodule_VertexSeqType); /* Internal error exception type */ igraphmodule_InternalError = PyErr_NewException("igraph._igraph.InternalError", PyExc_Exception, NULL); PyModule_AddObject(m, "InternalError", igraphmodule_InternalError); /* ARPACK default options variable */ igraphmodule_arpack_options_default = igraphmodule_ARPACKOptions_new(); PyModule_AddObject(m, "arpack_options", igraphmodule_arpack_options_default); /* Useful constants */ PyModule_AddIntConstant(m, "OUT", IGRAPH_OUT); PyModule_AddIntConstant(m, "IN", IGRAPH_IN); PyModule_AddIntConstant(m, "ALL", IGRAPH_ALL); PyModule_AddIntConstant(m, "STAR_OUT", IGRAPH_STAR_OUT); PyModule_AddIntConstant(m, "STAR_IN", IGRAPH_STAR_IN); PyModule_AddIntConstant(m, "STAR_MUTUAL", IGRAPH_STAR_MUTUAL); PyModule_AddIntConstant(m, "STAR_UNDIRECTED", IGRAPH_STAR_UNDIRECTED); PyModule_AddIntConstant(m, "TREE_OUT", IGRAPH_TREE_OUT); PyModule_AddIntConstant(m, "TREE_IN", IGRAPH_TREE_IN); PyModule_AddIntConstant(m, "TREE_UNDIRECTED", IGRAPH_TREE_UNDIRECTED); PyModule_AddIntConstant(m, "STRONG", IGRAPH_STRONG); PyModule_AddIntConstant(m, "WEAK", IGRAPH_WEAK); PyModule_AddIntConstant(m, "GET_ADJACENCY_UPPER", IGRAPH_GET_ADJACENCY_UPPER); PyModule_AddIntConstant(m, "GET_ADJACENCY_LOWER", IGRAPH_GET_ADJACENCY_LOWER); PyModule_AddIntConstant(m, "GET_ADJACENCY_BOTH", IGRAPH_GET_ADJACENCY_BOTH); PyModule_AddIntConstant(m, "REWIRING_SIMPLE", IGRAPH_REWIRING_SIMPLE); PyModule_AddIntConstant(m, "REWIRING_SIMPLE_LOOPS", IGRAPH_REWIRING_SIMPLE_LOOPS); PyModule_AddIntConstant(m, "ADJ_DIRECTED", IGRAPH_ADJ_DIRECTED); PyModule_AddIntConstant(m, "ADJ_UNDIRECTED", IGRAPH_ADJ_UNDIRECTED); PyModule_AddIntConstant(m, "ADJ_MAX", IGRAPH_ADJ_MAX); PyModule_AddIntConstant(m, "ADJ_MIN", IGRAPH_ADJ_MIN); PyModule_AddIntConstant(m, "ADJ_PLUS", IGRAPH_ADJ_PLUS); PyModule_AddIntConstant(m, "ADJ_UPPER", IGRAPH_ADJ_UPPER); PyModule_AddIntConstant(m, "ADJ_LOWER", IGRAPH_ADJ_LOWER); PyModule_AddIntConstant(m, "BLISS_F", IGRAPH_BLISS_F); PyModule_AddIntConstant(m, "BLISS_FL", IGRAPH_BLISS_FL); PyModule_AddIntConstant(m, "BLISS_FS", IGRAPH_BLISS_FS); PyModule_AddIntConstant(m, "BLISS_FM", IGRAPH_BLISS_FM); PyModule_AddIntConstant(m, "BLISS_FLM", IGRAPH_BLISS_FLM); PyModule_AddIntConstant(m, "BLISS_FSM", IGRAPH_BLISS_FSM); PyModule_AddIntConstant(m, "TRANSITIVITY_NAN", IGRAPH_TRANSITIVITY_NAN); PyModule_AddIntConstant(m, "TRANSITIVITY_ZERO", IGRAPH_TRANSITIVITY_ZERO); /* More useful constants */ { const char* version; igraph_version(&version, 0, 0, 0); PyModule_AddStringConstant(m, "__version__", version); } PyModule_AddStringConstant(m, "__build_date__", __DATE__); /* initialize error, progress, warning and interruption handler */ igraph_set_error_handler(igraphmodule_igraph_error_hook); igraph_set_progress_handler(igraphmodule_igraph_progress_hook); igraph_set_status_handler(igraphmodule_igraph_status_hook); igraph_set_warning_handler(igraphmodule_igraph_warning_hook); igraph_set_interruption_handler(igraphmodule_igraph_interrupt_hook); /* initialize attribute handlers */ igraphmodule_initialize_attribute_handler(); /* Initialize the C API pointer array */ PyIGraph_API[PyIGraph_FromCGraph_NUM] = (void *)PyIGraph_FromCGraph; PyIGraph_API[PyIGraph_ToCGraph_NUM] = (void *)PyIGraph_ToCGraph; /* Create a CObject containing the API pointer array's address */ #ifdef IGRAPH_PYTHON3 c_api_object = PyCapsule_New((void*)PyIGraph_API, "igraph._igraph._C_API", 0); #else c_api_object = PyCObject_FromVoidPtr((void*)PyIGraph_API, 0); #endif if (c_api_object != 0) { PyModule_AddObject(m, "_C_API", c_api_object); } igraphmodule_initialized = 1; #ifdef IGRAPH_PYTHON3 return m; #endif } python-igraph-0.7.1.post6/src/igraphmodule_api.h0000644000076500000240000000500612453614202022241 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef Py_IGRAPHMODULE_H #define Py_IGRAPHMODULE_H #ifdef __cplusplus extern "C" { #endif /* C API functions */ #define PyIGraph_FromCGraph_NUM 0 #define PyIGraph_FromCGraph_RETURN PyObject* #define PyIGraph_FromCGraph_PROTO (igraph_t *graph) #define PyIGraph_ToCGraph_NUM 1 #define PyIGraph_ToCGraph_RETURN igraph_t* #define PyIGraph_ToCGraph_PROTO (PyObject *graph) /* Total number of C API pointers */ #define PyIGraph_API_pointers 2 #ifdef IGRAPH_MODULE /* This section is used when compiling igraphmodule.c */ static PyIGraph_FromCGraph_RETURN PyIGraph_FromCGraph PyIGraph_FromCGraph_PROTO; static PyIGraph_ToCGraph_RETURN PyIGraph_ToCGraph PyIGraph_ToCGraph_PROTO; #else /* This section is used in modules that use igraph's API */ static void** PyIGraph_API; # define PyIGraph_FromCGraph \ (*(PyIGraph_FromCGraph_RETURN (*)PyIGraph_FromCGraph_PROTO) \ PyIGraph_API[PyIGraph_FromCGraph_NUM]) # define PyIGraph_ToCGraph \ (*(PyIGraph_ToCGraph_RETURN (*)PyIGraph_ToCGraph_PROTO) \ PyIGraph_API[PyIGraph_ToCGraph_NUM]) /* Return -1 and set exception on error, 0 on success */ static int import_igraph(void) { PyObject *c_api_object; PyObject *module; module = PyImport_ImportModule("igraph._igraph"); if (module == 0) return -1; c_api_object = PyObject_GetAttrString(module, "_C_API"); if (c_api_object == 0) { Py_DECREF(module); return -1; } if (PyCObject_Check(c_api_object)) PyIGraph_API = (void**)PyCObject_AsVoidPtr(c_api_object); Py_DECREF(c_api_object); Py_DECREF(module); return 0; } #endif #ifdef __cplusplus } #endif #endif /* !defined(Py_IGRAPHMODULE_H) */ python-igraph-0.7.1.post6/src/indexing.c0000644000076500000240000004110412453614202020527 0ustar ntamasstaff00000000000000/* vim:set ts=4 sw=2 sts=2 et: */ /* IGraph library - Python interface. Copyright (C) 2006-2011 Tamas Nepusz 5 Avenue Road, Staines, Middlesex, TW18 3AW, United Kingdom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "attributes.h" #include "convert.h" #include "error.h" #include "indexing.h" #include "platform.h" #include "py2compat.h" #include "pyhelpers.h" /***************************************************************************/ static PyObject* igraphmodule_i_Graph_adjmatrix_indexing_get_value_for_vertex_pair( igraph_t* graph, igraph_integer_t from, igraph_integer_t to, PyObject* values) { igraph_integer_t eid; PyObject* result; /* Retrieving a single edge */ igraph_get_eid(graph, &eid, from, to, /* directed = */1, /* error = */0); if (eid >= 0) { /* Edge found, get the value of the attribute */ if (values == 0) { return PyInt_FromLong(1L); } else { result = PyList_GetItem(values, eid); Py_XINCREF(result); return result; } } else { /* No such edge, return zero */ return PyInt_FromLong(0L); } } static PyObject* igraphmodule_i_Graph_adjmatrix_get_index_row(igraph_t* graph, igraph_integer_t from, igraph_vs_t* to, igraph_neimode_t neimode, PyObject* values); PyObject* igraphmodule_Graph_adjmatrix_get_index(igraph_t* graph, PyObject* row_index, PyObject* column_index, PyObject* attr_name) { PyObject *result = 0, *values; igraph_vs_t vs1, vs2; igraph_integer_t vid1 = -1, vid2 = -1; char* attr; if (igraphmodule_PyObject_to_vs_t(row_index, &vs1, graph, 0, &vid1)) return NULL; if (igraphmodule_PyObject_to_vs_t(column_index, &vs2, graph, 0, &vid2)) return NULL; if (attr_name == 0) { /* Using the "weight" attribute by default */ values = igraphmodule_get_edge_attribute_values(graph, "weight"); } else { /* Specifying the name of the attribute */ attr = PyObject_ConvertToCString(attr_name); values = igraphmodule_get_edge_attribute_values(graph, attr); free(attr); } if (vid1 >= 0 && vid2 >= 0) { /* Retrieving an edge between vid1 and vid2 */ result = igraphmodule_i_Graph_adjmatrix_indexing_get_value_for_vertex_pair( graph, vid1, vid2, values); } else if (vid1 >= 0) { /* Retrieving the successors of vid1 */ result = igraphmodule_i_Graph_adjmatrix_get_index_row( graph, vid1, &vs2, IGRAPH_OUT, values); } else if (vid2 >= 0) { /* Retrieving the predecessors of vid2 */ result = igraphmodule_i_Graph_adjmatrix_get_index_row( graph, vid2, &vs1, IGRAPH_IN, values); } else { /* Retrieving a submatrix */ igraph_vit_t vit; PyObject *item; if (igraph_vit_create(graph, vs1, &vit)) { igraphmodule_handle_igraph_error(); result = 0; } else { result = PyList_New(0); if (result != 0) { while (!IGRAPH_VIT_END(vit)) { vid1 = IGRAPH_VIT_GET(vit); item = igraphmodule_i_Graph_adjmatrix_get_index_row(graph, vid1, &vs2, IGRAPH_OUT, values); if (item == 0) { Py_DECREF(result); result = 0; break; } if (PyList_Append(result, item)) { /* error while appending */ Py_DECREF(item); Py_DECREF(result); result = 0; break; } Py_DECREF(item); IGRAPH_VIT_NEXT(vit); } } igraph_vit_destroy(&vit); } } igraph_vs_destroy(&vs1); igraph_vs_destroy(&vs2); return result; } static PyObject* igraphmodule_i_Graph_adjmatrix_get_index_row(igraph_t* graph, igraph_integer_t from, igraph_vs_t* to, igraph_neimode_t neimode, PyObject* values) { igraph_vector_t eids; igraph_integer_t eid; igraph_vit_t vit; PyObject *result = 0, *item; long int i, n; igraph_integer_t v; if (igraph_vs_is_all(to)) { /* Simple case: all edges */ IGRAPH_PYCHECK(igraph_vector_init(&eids, 0)); IGRAPH_FINALLY(igraph_vector_destroy, &eids); IGRAPH_PYCHECK(igraph_incident(graph, &eids, from, neimode)); n = igraph_vector_size(&eids); result = PyList_Zeroes(igraph_vcount(graph)); if (result == 0) { IGRAPH_FINALLY_FREE(); return 0; } for (i = 0; i < n; i++) { eid = (igraph_integer_t)VECTOR(eids)[i]; v = IGRAPH_OTHER(graph, eid, from); if (values) item = PyList_GetItem(values, eid); else item = PyInt_FromLong(1); Py_INCREF(item); PyList_SetItem(result, v, item); /* reference stolen here */ } IGRAPH_FINALLY_CLEAN(1); igraph_vector_destroy(&eids); return result; } /* More complicated case: only some vertices */ IGRAPH_PYCHECK(igraph_vit_create(graph, *to, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); result = PyList_New(0); if (result == 0) { IGRAPH_FINALLY_FREE(); return 0; } while (!IGRAPH_VIT_END(vit)) { v = IGRAPH_VIT_GET(vit); if (neimode == IGRAPH_OUT) { item = igraphmodule_i_Graph_adjmatrix_indexing_get_value_for_vertex_pair( graph, from, v, values); } else { item = igraphmodule_i_Graph_adjmatrix_indexing_get_value_for_vertex_pair( graph, v, from, values); } if (item == 0) { IGRAPH_FINALLY_FREE(); Py_DECREF(result); return 0; } if (PyList_Append(result, item)) { /* error while appending */ Py_DECREF(item); Py_DECREF(result); result = 0; break; } Py_DECREF(item); IGRAPH_VIT_NEXT(vit); } igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); return result; } /***************************************************************************/ /** * Determines whether the given Python value means that the user would like * to delete the edge the value is being assigned to in the adjacency matrix * assignment syntax. */ static INLINE igraph_bool_t deleting_edge(PyObject* value) { return value == Py_None || value == Py_False || (PyInt_Check(value) && PyInt_AsLong(value) == 0); } /** * Structure to hold data related to newly added/removed edges during an * adjacency matrix assignment. */ typedef struct { igraph_vector_t to_add; PyObject* to_add_values; igraph_vector_t to_delete; } igraphmodule_i_Graph_adjmatrix_set_index_data_t; int igraphmodule_i_Graph_adjmatrix_set_index_data_init( igraphmodule_i_Graph_adjmatrix_set_index_data_t* data) { if (igraph_vector_init(&data->to_add, 0)) { igraphmodule_handle_igraph_error(); return -1; } if (igraph_vector_init(&data->to_delete, 0)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&data->to_delete); return -1; } data->to_add_values = PyList_New(0); if (data->to_add_values == 0) { igraph_vector_destroy(&data->to_add); igraph_vector_destroy(&data->to_delete); return -1; } return 0; } void igraphmodule_i_Graph_adjmatrix_set_index_data_destroy( igraphmodule_i_Graph_adjmatrix_set_index_data_t* data) { igraph_vector_destroy(&data->to_add); igraph_vector_destroy(&data->to_delete); Py_DECREF(data->to_add_values); } static int igraphmodule_i_Graph_adjmatrix_set_index_row(igraph_t* graph, igraph_integer_t from, igraph_vs_t* to, igraph_neimode_t neimode, PyObject* values, PyObject* new_value, igraphmodule_i_Graph_adjmatrix_set_index_data_t* data) { PyObject *iter = 0, *item; igraph_vit_t vit; igraph_integer_t v, v1, v2, eid; igraph_bool_t deleting, ok = 1; /* Check whether new_value is an iterable (and not a string). If not, * every assignment will use the same value (that is, new_value) */ if (!PyBaseString_Check(new_value)) { iter = PyObject_GetIter(new_value); if (PyErr_Occurred()) { /* Object is not an iterable. Clear the exception */ iter = 0; PyErr_Clear(); } } if (igraph_vit_create(graph, *to, &vit)) { Py_XDECREF(iter); igraphmodule_handle_igraph_error(); return -1; } v1 = from; v2 = from; /* The two branches of the next `if' are almost the same; make sure * you make changes to both branches if appropriate! */ if (iter != 0) { /* The new value is an iterable, so it must have exactly as many elements * as the number of vertices in the graph. If it has less, we simply * skip the rest (with a warning) */ while (!IGRAPH_VIT_END(vit) && (item = PyIter_Next(iter)) != 0) { v = IGRAPH_VIT_GET(vit); /* Get the ID of the edge between from and v */ if (neimode == IGRAPH_OUT) { v2 = v; } else { v1 = v; } igraph_get_eid(graph, &eid, v1, v2, /* directed = */1, /* error = */0); if (deleting_edge(item)) { /* Deleting edges if eid != -1 */ if (eid != -1) { if (igraph_vector_push_back(&data->to_delete, eid)) { igraphmodule_handle_igraph_error(); igraph_vector_clear(&data->to_delete); ok = 0; break; } } } else { if (eid == -1) { /* Adding edges */ if (igraph_vector_push_back(&data->to_add, v1) || igraph_vector_push_back(&data->to_add, v2)) { igraphmodule_handle_igraph_error(); igraph_vector_clear(&data->to_add); ok = 0; break; } if (values != 0) { Py_INCREF(new_value); if (PyList_Append(data->to_add_values, new_value)) { Py_DECREF(new_value); igraph_vector_clear(&data->to_add); ok = 0; break; } } } else if (values != 0) { /* Setting attribute */ Py_INCREF(item); if (PyList_SetItem(values, eid, item)) { Py_DECREF(item); igraph_vector_clear(&data->to_add); } } } Py_DECREF(item); IGRAPH_VIT_NEXT(vit); } if (!IGRAPH_VIT_END(vit)) { PyErr_WarnEx(PyExc_RuntimeWarning, "iterable was shorter than the number of vertices in the vertex " "sequence", 1); } } else { /* The new value is not an iterable; setting the same value for * more than one edge */ deleting = deleting_edge(new_value); while (!IGRAPH_VIT_END(vit)) { v = IGRAPH_VIT_GET(vit); /* Get the ID of the edge between from and v */ if (neimode == IGRAPH_OUT) { v2 = v; } else { v1 = v; } igraph_get_eid(graph, &eid, v1, v2, /* directed = */1, /* error = */0); if (deleting) { /* Deleting edges if eid != -1 */ if (eid != -1) { if (igraph_vector_push_back(&data->to_delete, eid)) { igraphmodule_handle_igraph_error(); igraph_vector_clear(&data->to_delete); ok = 0; break; } } } else { if (eid == -1) { /* Adding edges */ if (igraph_vector_push_back(&data->to_add, v1) || igraph_vector_push_back(&data->to_add, v2)) { igraphmodule_handle_igraph_error(); igraph_vector_clear(&data->to_add); ok = 0; break; } if (values != 0) { Py_INCREF(new_value); if (PyList_Append(data->to_add_values, new_value)) { Py_DECREF(new_value); igraph_vector_clear(&data->to_add); ok = 0; break; } } } else if (values != 0) { /* Setting attribute */ Py_INCREF(new_value); if (PyList_SetItem(values, eid, new_value)) { Py_DECREF(new_value); igraph_vector_clear(&data->to_add); } } } IGRAPH_VIT_NEXT(vit); } } Py_XDECREF(iter); igraph_vit_destroy(&vit); return ok ? 0 : -1; } int igraphmodule_Graph_adjmatrix_set_index(igraph_t* graph, PyObject* row_index, PyObject* column_index, PyObject* attr_name, PyObject* new_value) { PyObject *values; igraph_vs_t vs1, vs2; igraph_vit_t vit; igraph_integer_t vid1 = -1, vid2 = -1, eid = -1; igraph_bool_t ok = 1; igraphmodule_i_Graph_adjmatrix_set_index_data_t data; char* attr; if (igraphmodule_PyObject_to_vs_t(row_index, &vs1, graph, 0, &vid1)) return -1; if (igraphmodule_PyObject_to_vs_t(column_index, &vs2, graph, 0, &vid2)) return -1; if (attr_name == 0) { /* Using the "weight" attribute by default */ values = igraphmodule_get_edge_attribute_values(graph, "weight"); } else { /* Specifying the name of the attribute */ attr = PyObject_ConvertToCString(attr_name); values = igraphmodule_create_or_get_edge_attribute_values(graph, attr); free(attr); } if (vid1 >= 0 && vid2 >= 0) { /* Setting an edge between vid1 and vid2 */ igraph_get_eid(graph, &eid, vid1, vid2, /* directed = */1, /* error = */0); if (deleting_edge(new_value)) { if (eid != -1) { /* Deleting the edge between vid1 and vid2 if it is there */ if (igraph_delete_edges(graph, igraph_ess_1(eid))) { igraphmodule_handle_igraph_error(); ok = 0; } } } else { /* Adding the edge between vid1 and vid2 if it is not there */ if (eid == -1) { eid = igraph_ecount(graph); if (igraph_add_edge(graph, vid1, vid2)) { igraphmodule_handle_igraph_error(); ok = 0; } } if (ok && values != 0) { /* Set the attribute value */ Py_INCREF(new_value); PyList_SetItem(values, eid, new_value); /* reference stolen here */ } } } else { /* In all the non-trivial cases, we do the modifications in three phases; * in the first phase, we modify the attribute values of edges that are to * stay (but possibly with a different attribute value) and collect the * list of edges to be added (and their attribute values) and the list of * edge to be deleted. In the second phase, we do the deletions in one * batch. Finally, we add the edges to be added. */ igraphmodule_i_Graph_adjmatrix_set_index_data_init(&data); /* First phase */ if (vid1 >= 0) { /* vs1 is a single vertex, vs2 is not */ ok = (igraphmodule_i_Graph_adjmatrix_set_index_row( graph, vid1, &vs2, IGRAPH_OUT, values, new_value, &data) == 0); } else if (vid2 >= 0) { /* vs2 is a single vertex, vs1 is not */ ok = (igraphmodule_i_Graph_adjmatrix_set_index_row( graph, vid2, &vs1, IGRAPH_IN, values, new_value, &data) == 0); } else { /* Complete submatrix */ if (igraph_vit_create(graph, vs1, &vit)) { igraphmodule_handle_igraph_error(); ok = 0; } else { while (!IGRAPH_VIT_END(vit)) { vid1 = IGRAPH_VIT_GET(vit); if (igraphmodule_i_Graph_adjmatrix_set_index_row( graph, vid1, &vs2, IGRAPH_OUT, values, new_value, &data) == 0) { ok = 0; break; } IGRAPH_VIT_NEXT(vit); } igraph_vit_destroy(&vit); } } if (ok) { /* Second phase: do the deletions in one batch */ if (igraph_delete_edges(graph, igraph_ess_vector(&data.to_delete))) { igraphmodule_handle_igraph_error(); ok = 0; } } if (ok) { /* Third phase: add the new edges in one batch */ if (!igraph_vector_empty(&data.to_add)) { eid = igraph_ecount(graph); igraph_add_edges(graph, &data.to_add, 0); if (values != 0) { PyList_SetSlice(values, eid, eid+PyList_Size(data.to_add_values), data.to_add_values); if (PyList_Size(values) != igraph_ecount(graph)) { PyErr_SetString(PyExc_ValueError, "hmmm, attribute value list " "length mismatch, this is most likely a bug."); ok = 0; } } } } igraphmodule_i_Graph_adjmatrix_set_index_data_destroy(&data); } igraph_vs_destroy(&vs1); igraph_vs_destroy(&vs2); return ok ? 0 : -1; } python-igraph-0.7.1.post6/src/indexing.h0000644000076500000240000000250012453614202020531 0ustar ntamasstaff00000000000000/* vim:set ts=4 sw=2 sts=2 et: */ /* IGraph library - Python interface. Copyright (C) 2006-2011 Tamas Nepusz 5 Avenue Road, Staines, Middlesex, TW18 3AW, United Kingdom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_INDEXING_H #define PYTHON_INDEXING_H #include #include PyObject* igraphmodule_Graph_adjmatrix_get_index(igraph_t* graph, PyObject* row_index, PyObject* column_index, PyObject* attr_name); int igraphmodule_Graph_adjmatrix_set_index(igraph_t* graph, PyObject* row_index, PyObject* column_index, PyObject* attr_name, PyObject* value); #endif python-igraph-0.7.1.post6/src/platform.h0000644000076500000240000000177412453614202020564 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* vim: set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_PLATFORM_H #define PYTHON_PLATFORM_H #ifdef _MSC_VER # define INLINE __forceinline #else # define INLINE inline #endif #endif python-igraph-0.7.1.post6/src/py2compat.c0000644000076500000240000000617212453614202020646 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* vim: set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "py2compat.h" /* Common utility functions that are useful both in Python 2.x and 3.x */ int PyFile_Close(PyObject* fileObj) { PyObject *result; result = PyObject_CallMethod(fileObj, "close", 0); if (result) { Py_DECREF(result); return 0; } else { /* Exception raised already */ return 1; } } #ifdef IGRAPH_PYTHON3 /* Python 3.x functions */ PyObject* PyFile_FromObject(PyObject* filename, const char* mode) { PyObject *ioModule, *fileObj; ioModule = PyImport_ImportModule("io"); if (ioModule == 0) return 0; fileObj = PyObject_CallMethod(ioModule, "open", "Os", filename, mode); Py_DECREF(ioModule); return fileObj; } char* PyString_CopyAsString(PyObject* string) { PyObject* bytes; char* result; if (PyBytes_Check(string)) { bytes = string; Py_INCREF(bytes); } else { bytes = PyUnicode_AsUTF8String(string); } if (bytes == 0) return 0; result = strdup(PyBytes_AS_STRING(bytes)); Py_DECREF(bytes); if (result == 0) PyErr_NoMemory(); return result; } int PyString_IsEqualToUTF8String(PyObject* py_string, const char* c_string) { PyObject* c_string_conv; int result; if (!PyUnicode_Check(py_string)) return 0; c_string_conv = PyUnicode_FromString(c_string); if (c_string_conv == 0) return 0; result = (PyUnicode_Compare(py_string, c_string_conv) == 0); Py_DECREF(c_string_conv); return result; } #else /* Python 2.x functions */ char* PyString_CopyAsString(PyObject* string) { char* result; if (!PyBaseString_Check(string)) { PyErr_SetString(PyExc_TypeError, "string or unicode object expected"); return 0; } result = PyString_AsString(string); if (result == 0) return 0; result = strdup(result); if (result == 0) PyErr_NoMemory(); return result; } int PyString_IsEqualToASCIIString(PyObject* py_string, const char* c_string) { PyObject* c_string_conv; int result; if (PyString_Check(py_string)) { return strcmp(PyString_AS_STRING(py_string), c_string) == 0; } if (!PyUnicode_Check(py_string)) return 0; c_string_conv = PyUnicode_DecodeASCII(c_string, strlen(c_string), "strict"); if (c_string_conv == 0) return 0; result = (PyUnicode_Compare(py_string, c_string_conv) == 0); Py_DECREF(c_string_conv); return result; } #endif python-igraph-0.7.1.post6/src/py2compat.h0000644000076500000240000000471612453614202020655 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* vim: set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PY_IGRAPH_PY2COMPAT_H #define PY_IGRAPH_PY2COMPAT_H #include /* Common utility functions */ int PyFile_Close(PyObject* fileObj); /* Compatibility hacks */ #ifndef Py_hash_t # define Py_hash_t long #endif #if PY_MAJOR_VERSION >= 3 /* Python 3.x-specific part follows here */ #define IGRAPH_PYTHON3 #define PyBaseString_Check(o) PyUnicode_Check(o) PyObject* PyFile_FromObject(PyObject* filename, const char* mode); #define PyIntObject PyLongObject #define PyInt_AsLong PyLong_AsLong #define PyInt_Check PyLong_Check #define PyInt_FromLong PyLong_FromLong #define PyNumber_Int PyNumber_Long #define PyString_AS_STRING PyUnicode_AS_UNICODE #define PyString_Check PyUnicode_Check #define PyString_FromFormat PyUnicode_FromFormat #define PyString_FromString PyUnicode_FromString #define PyString_Type PyUnicode_Type #define PyString_IsEqualToASCIIString(uni, string) \ (PyUnicode_CompareWithASCIIString(uni, string) == 0) #ifndef PyVarObject_HEAD_INIT #define PyVarObject_HEAD_INIT(type, size) \ PyObject_HEAD_INIT(type) size, #endif int PyString_IsEqualToUTF8String(PyObject* py_string, const char* c_string); #else /* Python 2.x-specific part follows here */ #define PyBaseString_Check(o) (PyString_Check(o) || PyUnicode_Check(o)) int PyString_IsEqualToASCIIString(PyObject* py_string, const char* c_string); #ifndef Py_TYPE # define Py_TYPE(o) ((o)->ob_type) #endif #ifndef PyVarObject_HEAD_INIT # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, #endif #endif char* PyString_CopyAsString(PyObject* string); #endif python-igraph-0.7.1.post6/src/pyhelpers.c0000644000076500000240000000434212453614202020740 0ustar ntamasstaff00000000000000/* vim:set ts=4 sw=2 sts=2 et: */ /* IGraph library - Python interface. Copyright (C) 2006-2011 Tamas Nepusz 5 Avenue Road, Staines, Middlesex, TW18 3AW, United Kingdom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "py2compat.h" #include "pyhelpers.h" /** * Creates a Python list and fills it with a pre-defined item. * * \param len the length of the list to be created * \param item the item with which the list will be filled */ PyObject* PyList_NewFill(Py_ssize_t len, PyObject* item) { Py_ssize_t i; PyObject* result = PyList_New(len); if (result == 0) return 0; for (i = 0; i < len; i++) { Py_INCREF(item); PyList_SET_ITEM(result, i, item); /* reference to item stolen */ } return result; } /** * Creates a Python list and fills it with zeroes. * * \param len the length of the list to be created */ PyObject* PyList_Zeroes(Py_ssize_t len) { PyObject* zero = PyInt_FromLong(0); PyObject* result; if (zero == 0) return 0; result = PyList_NewFill(len, zero); Py_DECREF(zero); return result; } /** * Converts a Python object to its string representation and returns it as * a C string. * * It is the responsibility of the caller to release the C string. */ char* PyObject_ConvertToCString(PyObject* string) { char* result; if (string == 0) return 0; if (!PyBaseString_Check(string)) { string = PyObject_Str(string); if (string == 0) return 0; } else { Py_INCREF(string); } result = PyString_CopyAsString(string); Py_DECREF(string); return result; } python-igraph-0.7.1.post6/src/pyhelpers.h0000644000076500000240000000246012453614202020744 0ustar ntamasstaff00000000000000/* vim:set ts=4 sw=2 sts=2 et: */ /* IGraph library - Python interface. Copyright (C) 2006-2011 Tamas Nepusz 5 Avenue Road, Staines, Middlesex, TW18 3AW, United Kingdom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_HELPERS_H #define PYTHON_HELPERS_H #include PyObject* PyList_NewFill(Py_ssize_t len, PyObject* item); PyObject* PyList_Zeroes(Py_ssize_t len); char* PyObject_ConvertToCString(PyObject* string); #define PY_IGRAPH_DEPRECATED(msg) \ PyErr_WarnEx(PyExc_DeprecationWarning, (msg), 1) #define PY_IGRAPH_WARN(msg) \ PyErr_WarnEx(PyExc_RuntimeWarning, (msg), 1) #endif python-igraph-0.7.1.post6/src/random.c0000644000076500000240000001345412453614202020211 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* vim:set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "py2compat.h" #include "random.h" #include #include /** * \ingroup python_interface_rng * \brief Internal data structure for storing references to the * functions used from Python's random number generator. */ typedef struct { PyObject* randint_func; PyObject* random_func; PyObject* gauss_func; } igraph_i_rng_Python_state_t; static igraph_i_rng_Python_state_t igraph_rng_Python_state = {0, 0, 0}; static igraph_rng_t igraph_rng_Python = {0, 0, 0}; int igraph_rng_Python_init(void **state) { IGRAPH_ERROR("Python RNG error, unsupported function called", IGRAPH_EINTERNAL); return 0; } void igraph_rng_Python_destroy(void *state) { igraph_error("Python RNG error, unsupported function called", __FILE__, __LINE__, IGRAPH_EINTERNAL); } /** * \ingroup python_interface_rng * \brief Sets the random number generator used by igraph. */ PyObject* igraph_rng_Python_set_generator(PyObject* self, PyObject* object) { igraph_i_rng_Python_state_t new_state, old_state; PyObject* func; if (object == Py_None) { /* Reverting to the default igraph random number generator instead * of the Python-based one */ igraph_rng_set_default(igraph_rng_default()); Py_RETURN_NONE; } #define GET_FUNC(name) {\ func = PyObject_GetAttrString(object, name); \ if (func == 0) \ return NULL; \ if (!PyCallable_Check(func)) {\ PyErr_SetString(PyExc_TypeError, name "attribute must be callable"); \ return NULL; \ } \ } GET_FUNC("randint"); new_state.randint_func = func; GET_FUNC("random"); new_state.random_func = func; GET_FUNC("gauss"); new_state.gauss_func = func; old_state = igraph_rng_Python_state; igraph_rng_Python_state = new_state; Py_XDECREF(old_state.randint_func); Py_XDECREF(old_state.random_func); Py_XDECREF(old_state.gauss_func); igraph_rng_set_default(&igraph_rng_Python); Py_RETURN_NONE; } /** * \ingroup python_interface_rng * \brief Sets the seed of the random generator. */ int igraph_rng_Python_seed(void *state, unsigned long int seed) { IGRAPH_ERROR("Python RNG error, unsupported function called", IGRAPH_EINTERNAL); return 0; } /** * \ingroup python_interface_rng * \brief Generates an unsigned long integer using the Python random number generator. */ unsigned long int igraph_rng_Python_get(void *state) { PyObject* result = PyObject_CallFunction(igraph_rng_Python_state.randint_func, "kk", 0, LONG_MAX); unsigned long int retval; if (result == 0) { PyErr_WriteUnraisable(PyErr_Occurred()); PyErr_Clear(); /* Fallback to the C random generator */ return rand() * LONG_MAX; } retval = PyInt_AsLong(result); Py_DECREF(result); return retval; } /** * \ingroup python_interface_rng * \brief Generates a real number between 0 and 1 using the Python random number generator. */ igraph_real_t igraph_rng_Python_get_real(void *state) { PyObject* result = PyObject_CallFunction(igraph_rng_Python_state.random_func, NULL); double retval; if (result == 0) { PyErr_WriteUnraisable(PyErr_Occurred()); PyErr_Clear(); /* Fallback to the C random generator */ return rand(); } retval = PyFloat_AsDouble(result); Py_DECREF(result); return retval; } /** * \ingroup python_interface_rng * \brief Generates a real number distributed according to the normal distribution * around zero with unit variance. */ igraph_real_t igraph_rng_Python_get_norm(void *state) { PyObject* result = PyObject_CallFunction(igraph_rng_Python_state.gauss_func, "dd", 0.0, 1.0); double retval; if (result == 0) { PyErr_WriteUnraisable(PyErr_Occurred()); PyErr_Clear(); /* Fallback to the C random generator */ return 0; } retval = PyFloat_AsDouble(result); Py_DECREF(result); return retval; } /** * \ingroup python_interface_rng * \brief Specification table for Python's random number generator. * This tells igraph which functions to call to obtain random numbers. */ igraph_rng_type_t igraph_rngtype_Python = { /* name= */ "Python random generator", /* min= */ 0, /* max= */ LONG_MAX, /* init= */ igraph_rng_Python_init, /* destroy= */ igraph_rng_Python_destroy, /* seed= */ igraph_rng_Python_seed, /* get= */ igraph_rng_Python_get, /* get_real */ igraph_rng_Python_get_real, /* get_norm= */ igraph_rng_Python_get_norm, /* get_geom= */ 0, /* get_binom= */ 0 }; void igraphmodule_init_rng(PyObject* igraph_module) { PyObject* random_module; if (igraph_rng_Python.state != 0) return; random_module = PyImport_ImportModule("random"); if (random_module == 0) { PyErr_WriteUnraisable(PyErr_Occurred()); PyErr_Clear(); return; } igraph_rng_Python.type = &igraph_rngtype_Python; igraph_rng_Python.state = &igraph_rng_Python_state; if (igraph_rng_Python_set_generator(igraph_module, random_module) == 0) { PyErr_WriteUnraisable(PyErr_Occurred()); PyErr_Clear(); return; } Py_DECREF(random_module); } python-igraph-0.7.1.post6/src/random.h0000644000076500000240000000201112453614202020201 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_RANDOM_H #define PYTHON_RANDOM_H #include void igraphmodule_init_rng(PyObject*); PyObject* igraph_rng_Python_set_generator(PyObject* self, PyObject* object); #endif python-igraph-0.7.1.post6/src/vertexobject.c0000644000076500000240000006246012460534543021445 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* vim: set ts=2 sw=2 sts=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "attributes.h" #include "convert.h" #include "error.h" #include "graphobject.h" #include "vertexobject.h" /** * \ingroup python_interface * \defgroup python_interface_vertex Vertex object */ PyTypeObject igraphmodule_VertexType; /** * \ingroup python_interface_vertex * \brief Checks whether the given Python object is a vertex */ int igraphmodule_Vertex_Check(PyObject* obj) { if (!obj) return 0; return PyObject_IsInstance(obj, (PyObject*)(&igraphmodule_VertexType)); } /** * \ingroup python_interface_vertex * \brief Checks whether the index in the given vertex object is a valid one. * \return nonzero if the vertex object is valid. Raises an appropriate Python * exception and returns zero if the vertex object is invalid. */ int igraphmodule_Vertex_Validate(PyObject* obj) { igraph_integer_t n; igraphmodule_VertexObject *self; igraphmodule_GraphObject *graph; if (!igraphmodule_Vertex_Check(obj)) { PyErr_SetString(PyExc_TypeError, "object is not a Vertex"); return 0; } self = (igraphmodule_VertexObject*)obj; graph = self->gref; if (graph == 0) { PyErr_SetString(PyExc_ValueError, "Vertex object refers to a null graph"); return 0; } if (self->idx < 0) { PyErr_SetString(PyExc_ValueError, "Vertex object refers to a negative vertex index"); return 0; } n = igraph_vcount(&graph->g); if (self->idx >= n) { PyErr_SetString(PyExc_ValueError, "Vertex object refers to a nonexistent vertex"); return 0; } return 1; } /** * \ingroup python_interface_vertex * \brief Allocates a new Python vertex object * \param gref the \c igraph.Graph being referenced by the vertex * \param idx the index of the vertex * * \warning \c igraph references its vertices by indices, so if * you delete some vertices from the graph, the vertex indices will * change. Since the \c igraph.Vertex objects do not follow these * changes, your existing vertex objects will point to elsewhere * (or they might even get invalidated). */ PyObject* igraphmodule_Vertex_New(igraphmodule_GraphObject *gref, igraph_integer_t idx) { igraphmodule_VertexObject* self; self=PyObject_New(igraphmodule_VertexObject, &igraphmodule_VertexType); if (self) { RC_ALLOC("Vertex", self); Py_INCREF(gref); self->gref=gref; self->idx=idx; self->hash=-1; } return (PyObject*)self; } /** * \ingroup python_interface_vertex * \brief Clears the vertex's subobject (before deallocation) */ int igraphmodule_Vertex_clear(igraphmodule_VertexObject *self) { PyObject *tmp; tmp=(PyObject*)self->gref; self->gref=NULL; Py_XDECREF(tmp); return 0; } /** * \ingroup python_interface_vertex * \brief Deallocates a Python representation of a given vertex object */ void igraphmodule_Vertex_dealloc(igraphmodule_VertexObject* self) { igraphmodule_Vertex_clear(self); RC_DEALLOC("Vertex", self); PyObject_Del((PyObject*)self); } /** \ingroup python_interface_vertex * \brief Formats an \c igraph.Vertex object to a string * * \return the formatted textual representation as a \c PyObject */ PyObject* igraphmodule_Vertex_repr(igraphmodule_VertexObject *self) { PyObject *s; PyObject *attrs; #ifndef IGRAPH_PYTHON3 PyObject *grepr, *drepr; #endif attrs = igraphmodule_Vertex_attributes(self); if (attrs == 0) return NULL; #ifdef IGRAPH_PYTHON3 s = PyUnicode_FromFormat("igraph.Vertex(%R, %ld, %R)", (PyObject*)self->gref, (long int)self->idx, attrs); Py_DECREF(attrs); #else grepr=PyObject_Repr((PyObject*)self->gref); drepr=PyObject_Repr(igraphmodule_Vertex_attributes(self)); Py_DECREF(attrs); if (!grepr || !drepr) { Py_XDECREF(grepr); Py_XDECREF(drepr); return NULL; } s=PyString_FromFormat("igraph.Vertex(%s,%ld,%s)", PyString_AsString(grepr), (long int)self->idx, PyString_AsString(drepr)); Py_DECREF(grepr); Py_DECREF(drepr); #endif return s; } /** \ingroup python_interface_vertex * \brief Returns the hash code of the vertex */ Py_hash_t igraphmodule_Vertex_hash(igraphmodule_VertexObject* self) { Py_hash_t hash_graph; Py_hash_t hash_index; Py_hash_t result; PyObject* index_o; if (self->hash != -1) return self->hash; index_o = PyInt_FromLong((long int)self->idx); if (index_o == 0) return -1; hash_index = PyObject_Hash(index_o); Py_DECREF(index_o); if (hash_index == -1) return -1; hash_graph = PyObject_Hash((PyObject*)self->gref); if (hash_graph == -1) return -1; result = hash_graph ^ hash_index; if (result == -1) result = 590923713U; self->hash = result; return result; } /** \ingroup python_interface_vertex * \brief Rich comparison of a vertex with another */ PyObject* igraphmodule_Vertex_richcompare(igraphmodule_VertexObject *a, PyObject *b, int op) { igraphmodule_VertexObject* self = a; igraphmodule_VertexObject* other; if (!igraphmodule_Vertex_Check(b)) Py_RETURN_NOTIMPLEMENTED; other = (igraphmodule_VertexObject*)b; if (self->gref != other->gref) Py_RETURN_FALSE; switch (op) { case Py_EQ: Py_RETURN(self->idx == other->idx); case Py_NE: Py_RETURN(self->idx != other->idx); case Py_LE: Py_RETURN(self->idx <= other->idx); case Py_LT: Py_RETURN(self->idx < other->idx); case Py_GE: Py_RETURN(self->idx >= other->idx); case Py_GT: Py_RETURN(self->idx > other->idx); default: Py_RETURN_NOTIMPLEMENTED; } } /** \ingroup python_interface_vertex * \brief Returns the number of vertex attributes */ Py_ssize_t igraphmodule_Vertex_attribute_count(igraphmodule_VertexObject* self) { igraphmodule_GraphObject *o = self->gref; if (!o) return 0; if (!((PyObject**)o->g.attr)[1]) return 0; return PyDict_Size(((PyObject**)o->g.attr)[1]); } /** \ingroup python_interface_vertex * \brief Returns the list of attribute names */ PyObject* igraphmodule_Vertex_attribute_names(igraphmodule_VertexObject* self) { if (!self->gref) return NULL; return igraphmodule_Graph_vertex_attributes(self->gref); } /** \ingroup python_interface_vertex * \brief Returns a dict with attribue names and values */ PyObject* igraphmodule_Vertex_attributes(igraphmodule_VertexObject* self) { igraphmodule_GraphObject *o = self->gref; PyObject *names, *dict; long i, n; if (!igraphmodule_Vertex_Validate((PyObject*)self)) return 0; dict=PyDict_New(); if (!dict) return NULL; names=igraphmodule_Graph_vertex_attributes(o); if (!names) { Py_DECREF(dict); return NULL; } n=PyList_Size(names); for (i=0; ig.attr)[ATTRHASH_IDX_VERTEX], name); if (dictit) { PyObject *value = PyList_GetItem(dictit, self->idx); if (value) { /* No need to Py_INCREF, PyDict_SetItem will do that */ PyDict_SetItem(dict, name, value); } } } } Py_DECREF(names); return dict; } /** * \ingroup python_interface_vertex * \brief Updates some attributes of a vertex * * Incidentally, this method is re-used intact in edgeobject.c for edges. * * \param self the vertex object * \param args positional arguments * \param kwds keyword arguments */ PyObject* igraphmodule_Vertex_update_attributes(PyObject* self, PyObject* args, PyObject* kwds) { PyObject* items[] = { Py_None, kwds, 0 }; PyObject** pObj; PyObject *key, *value, *it, *item, *keys; igraph_bool_t ok = 1; if (!PyArg_ParseTuple(args, "|O", &items[0])) return NULL; pObj = items; for (pObj = items; ok && *pObj != 0; pObj++) { PyObject* obj = *pObj; PyObject* keys_func; if (obj == Py_None) continue; keys_func = PyObject_GetAttrString(obj, "keys"); if (keys_func == 0) PyErr_Clear(); if (keys_func != 0 && PyCallable_Check(keys_func)) { /* Object has a "keys" method, so we iterate over the keys */ keys = PyObject_CallObject(keys_func, 0); if (keys == 0) { ok = 0; } else { /* Iterate over the keys */ it = PyObject_GetIter(keys); if (it == 0) { ok = 0; } else { while (ok && ((key = PyIter_Next(it)) != 0)) { value = PyObject_GetItem(obj, key); if (value == 0) { ok = 0; } else { PyObject_SetItem((PyObject*)self, key, value); Py_DECREF(value); } Py_DECREF(key); } Py_DECREF(it); if (PyErr_Occurred()) ok = 0; } Py_DECREF(keys); } } else { /* Object does not have a "keys" method; assume that it * yields tuples when treated as an iterator */ it = PyObject_GetIter(obj); if (!it) { ok = 0; } else { while (ok && ((item = PyIter_Next(it)) != 0)) { if (!PySequence_Check(item) || PyBaseString_Check(item)) { PyErr_SetString(PyExc_TypeError, "cannot convert update sequence element to a sequence"); ok = 0; } else { key = PySequence_GetItem(item, 0); if (key == 0) { ok = 0; } else { value = PySequence_GetItem(item, 1); if (value == 0) { ok = 0; } else { PyObject_SetItem((PyObject*)self, key, value); Py_DECREF(value); } Py_DECREF(key); } } Py_DECREF(item); } Py_DECREF(it); if (PyErr_Occurred()) ok = 0; } } if (keys_func != 0) { Py_DECREF(keys_func); } } if (ok) Py_RETURN_NONE; return 0; } /** \ingroup python_interface_vertex * \brief Returns the corresponding value to a given attribute of the vertex * \param self the vertex object * \param s the attribute name to be queried */ PyObject* igraphmodule_Vertex_get_attribute(igraphmodule_VertexObject* self, PyObject* s) { igraphmodule_GraphObject *o = self->gref; PyObject* result; if (!igraphmodule_Vertex_Validate((PyObject*)self)) return 0; if (!igraphmodule_attribute_name_check(s)) return 0; result=PyDict_GetItem(((PyObject**)o->g.attr)[ATTRHASH_IDX_VERTEX], s); if (result) { /* result is a list, so get the element with index self->idx */ if (!PyList_Check(result)) { PyErr_SetString(igraphmodule_InternalError, "Vertex attribute dict member is not a list"); return NULL; } result=PyList_GetItem(result, self->idx); Py_INCREF(result); return result; } /* result is NULL, check whether there was an error */ if (!PyErr_Occurred()) PyErr_SetString(PyExc_KeyError, "Attribute does not exist"); return NULL; } /** \ingroup python_interface_vertex * \brief Sets the corresponding value of a given attribute of the vertex * \param self the vertex object * \param k the attribute name to be set * \param v the value to be set * \return 0 if everything's ok, -1 in case of error */ int igraphmodule_Vertex_set_attribute(igraphmodule_VertexObject* self, PyObject* k, PyObject* v) { igraphmodule_GraphObject *o=self->gref; PyObject* result; int r; if (!igraphmodule_Vertex_Validate((PyObject*)self)) return -1; if (!igraphmodule_attribute_name_check(k)) return -1; if (PyString_IsEqualToASCIIString(k, "name")) igraphmodule_invalidate_vertex_name_index(&o->g); if (v==NULL) // we are deleting attribute return PyDict_DelItem(((PyObject**)o->g.attr)[ATTRHASH_IDX_VERTEX], k); result=PyDict_GetItem(((PyObject**)o->g.attr)[ATTRHASH_IDX_VERTEX], k); if (result) { /* result is a list, so set the element with index self->idx */ if (!PyList_Check(result)) { PyErr_SetString(igraphmodule_InternalError, "Vertex attribute dict member is not a list"); return -1; } /* we actually don't own a reference here to v, so we must increase * its reference count, because PyList_SetItem will "steal" a reference! * It took me 1.5 hours between London and Manchester to figure it out */ Py_INCREF(v); r=PyList_SetItem(result, self->idx, v); if (r == -1) { Py_DECREF(v); } return r; } /* result is NULL, check whether there was an error */ if (!PyErr_Occurred()) { /* no, there wasn't, so we must simply add the attribute */ int n=(int)igraph_vcount(&o->g), i; result=PyList_New(n); for (i=0; iidx) { Py_INCREF(Py_None); if (PyList_SetItem(result, i, Py_None) == -1) { Py_DECREF(Py_None); Py_DECREF(result); return -1; } } else { /* Same game with the reference count here */ Py_INCREF(v); if (PyList_SetItem(result, i, v) == -1) { Py_DECREF(v); Py_DECREF(result); return -1; } } } if (PyDict_SetItem(((PyObject**)o->g.attr)[1], k, result) == -1) { Py_DECREF(result); return -1; } Py_DECREF(result); /* compensating for PyDict_SetItem */ return 0; } return -1; } /** * \ingroup python_interface_vertex * Returns the vertex index */ PyObject* igraphmodule_Vertex_get_index(igraphmodule_VertexObject* self, void* closure) { return PyInt_FromLong((long int)self->idx); } /** * \ingroup python_interface_vertex * Returns the vertex index as an igraph_integer_t */ igraph_integer_t igraphmodule_Vertex_get_index_igraph_integer(igraphmodule_VertexObject* self) { return self->idx; } /** * \ingroup python_interface_vertex * Returns the vertex index as an ordinary C long */ long igraphmodule_Vertex_get_index_long(igraphmodule_VertexObject* self) { return (long)self->idx; } /** * \ingroup python_interface_vertexseq * Returns the graph where the vertex belongs */ PyObject* igraphmodule_Vertex_get_graph(igraphmodule_VertexObject* self, void* closure) { Py_INCREF(self->gref); return (PyObject*)self->gref; } /**************************************************************************/ /* Implementing proxy method in Vertex that just forward the call to the * appropriate Graph method. * * These methods may also execute a postprocessing function on the result * of the Graph method; for instance, this mechanism is used to turn the * result of Graph.neighbors() (which is a list of vertex indices) into a * list of Vertex objects. */ /* Dummy postprocessing function that does nothing. */ static PyObject* _identity(igraphmodule_VertexObject* vertex, PyObject* obj) { Py_INCREF(obj); return obj; } /* Postprocessing function that converts a Python list of integers into a * list of vertices in-place. */ static PyObject* _convert_to_vertex_list(igraphmodule_VertexObject* vertex, PyObject* obj) { Py_ssize_t i, n; if (!PyList_Check(obj)) { PyErr_SetString(PyExc_TypeError, "_convert_to_vertex_list expected list of integers"); return NULL; } n = PyList_Size(obj); for (i = 0; i < n; i++) { PyObject* idx = PyList_GET_ITEM(obj, i); PyObject* v; int idx_int; if (!PyInt_Check(idx)) { PyErr_SetString(PyExc_TypeError, "_convert_to_vertex_list expected list of integers"); return NULL; } if (PyInt_AsInt(idx, &idx_int)) return NULL; v = igraphmodule_Vertex_New(vertex->gref, idx_int); PyList_SetItem(obj, i, v); /* reference to v stolen, reference to idx discarded */ } Py_INCREF(obj); return obj; } #define GRAPH_PROXY_METHOD_PP(FUNC, METHODNAME, POSTPROCESS) \ PyObject* igraphmodule_Vertex_##FUNC(igraphmodule_VertexObject* self, PyObject* args, PyObject* kwds) { \ PyObject *new_args, *item, *result; \ long int i, num_args = args ? PyTuple_Size(args)+1 : 1; \ \ /* Prepend ourselves to args */ \ new_args = PyTuple_New(num_args); \ Py_INCREF(self); PyTuple_SET_ITEM(new_args, 0, (PyObject*)self); \ for (i = 1; i < num_args; i++) { \ item = PyTuple_GET_ITEM(args, i-1); \ Py_INCREF(item); PyTuple_SET_ITEM(new_args, i, item); \ } \ \ /* Get the method instance */ \ item = PyObject_GetAttrString((PyObject*)(self->gref), METHODNAME); \ result = PyObject_Call(item, new_args, kwds); \ Py_DECREF(item); \ Py_DECREF(new_args); \ \ /* Optional postprocessing */ \ if (result) { \ PyObject* pp_result = POSTPROCESS(self, result); \ Py_DECREF(result); \ return pp_result; \ } \ return NULL; \ } #define GRAPH_PROXY_METHOD(FUNC, METHODNAME) \ GRAPH_PROXY_METHOD_PP(FUNC, METHODNAME, _identity) GRAPH_PROXY_METHOD(betweenness, "betweenness"); GRAPH_PROXY_METHOD(closeness, "closeness"); GRAPH_PROXY_METHOD(constraint, "constraint"); GRAPH_PROXY_METHOD(degree, "degree"); GRAPH_PROXY_METHOD(delete, "delete_vertices"); GRAPH_PROXY_METHOD(diversity, "diversity"); GRAPH_PROXY_METHOD(eccentricity, "eccentricity"); GRAPH_PROXY_METHOD(get_shortest_paths, "get_shortest_paths"); GRAPH_PROXY_METHOD(indegree, "indegree"); GRAPH_PROXY_METHOD(is_minimal_separator, "is_minimal_separator"); GRAPH_PROXY_METHOD(is_separator, "is_separator"); GRAPH_PROXY_METHOD_PP(neighbors, "neighbors", _convert_to_vertex_list); GRAPH_PROXY_METHOD(outdegree, "outdegree"); GRAPH_PROXY_METHOD(pagerank, "pagerank"); GRAPH_PROXY_METHOD_PP(predecessors, "predecessors", _convert_to_vertex_list); GRAPH_PROXY_METHOD(personalized_pagerank, "personalized_pagerank"); GRAPH_PROXY_METHOD(shortest_paths, "shortest_paths"); GRAPH_PROXY_METHOD(strength, "strength"); GRAPH_PROXY_METHOD_PP(successors, "successors", _convert_to_vertex_list); #undef GRAPH_PROXY_METHOD #define GRAPH_PROXY_METHOD_SPEC(FUNC, METHODNAME) \ {METHODNAME, (PyCFunction)igraphmodule_Vertex_##FUNC, METH_VARARGS | METH_KEYWORDS, \ "Proxy method to L{Graph." METHODNAME "()}\n\n" \ "This method calls the " METHODNAME " method of the L{Graph} class " \ "with this vertex as the first argument, and returns the result.\n\n"\ "@see: Graph." METHODNAME "() for details."} #define GRAPH_PROXY_METHOD_SPEC_2(FUNC, METHODNAME, METHODNAME_IN_GRAPH) \ {METHODNAME, (PyCFunction)igraphmodule_Vertex_##FUNC, METH_VARARGS | METH_KEYWORDS, \ "Proxy method to L{Graph." METHODNAME_IN_GRAPH "()}\n\n" \ "This method calls the " METHODNAME_IN_GRAPH " method of the L{Graph} class " \ "with this vertex as the first argument, and returns the result.\n\n"\ "@see: Graph." METHODNAME_IN_GRAPH "() for details."} /** * \ingroup python_interface_vertex * Method table for the \c igraph.Vertex object */ PyMethodDef igraphmodule_Vertex_methods[] = { {"attributes", (PyCFunction)igraphmodule_Vertex_attributes, METH_NOARGS, "attributes() -> dict\n\n" "Returns a dict of attribute names and values for the vertex\n" }, {"attribute_names", (PyCFunction)igraphmodule_Vertex_attribute_names, METH_NOARGS, "attribute_names() -> list\n\n" "Returns the list of vertex attribute names\n" }, {"update_attributes", (PyCFunction)igraphmodule_Vertex_update_attributes, METH_VARARGS | METH_KEYWORDS, "update_attributes(E, **F) -> None\n\n" "Updates the attributes of the vertex from dict/iterable E and F.\n\n" "If E has a C{keys()} method, it does: C{for k in E: self[k] = E[k]}.\n" "If E lacks a C{keys()} method, it does: C{for (k, v) in E: self[k] = v}.\n" "In either case, this is followed by: C{for k in F: self[k] = F[k]}.\n\n" "This method thus behaves similarly to the C{update()} method of Python\n" "dictionaries." }, GRAPH_PROXY_METHOD_SPEC(betweenness, "betweenness"), GRAPH_PROXY_METHOD_SPEC(closeness, "closeness"), GRAPH_PROXY_METHOD_SPEC(constraint, "constraint"), GRAPH_PROXY_METHOD_SPEC(degree, "degree"), GRAPH_PROXY_METHOD_SPEC_2(delete, "delete", "delete_vertices"), GRAPH_PROXY_METHOD_SPEC(diversity, "diversity"), GRAPH_PROXY_METHOD_SPEC(eccentricity, "eccentricity"), GRAPH_PROXY_METHOD_SPEC(get_shortest_paths, "get_shortest_paths"), GRAPH_PROXY_METHOD_SPEC(indegree, "indegree"), GRAPH_PROXY_METHOD_SPEC(is_minimal_separator, "is_minimal_separator"), GRAPH_PROXY_METHOD_SPEC(is_separator, "is_separator"), GRAPH_PROXY_METHOD_SPEC(neighbors, "neighbors"), GRAPH_PROXY_METHOD_SPEC(outdegree, "outdegree"), GRAPH_PROXY_METHOD_SPEC(pagerank, "pagerank"), GRAPH_PROXY_METHOD_SPEC(predecessors, "predecessors"), GRAPH_PROXY_METHOD_SPEC(personalized_pagerank, "personalized_pagerank"), GRAPH_PROXY_METHOD_SPEC(shortest_paths, "shortest_paths"), GRAPH_PROXY_METHOD_SPEC(strength, "strength"), GRAPH_PROXY_METHOD_SPEC(successors, "successors"), {NULL} }; #undef GRAPH_PROXY_METHOD_SPEC #undef GRAPH_PROXY_METHOD_SPEC_2 /** \ingroup python_interface_vertex * This structure is the collection of functions necessary to implement * the vertex as a mapping (i.e. to allow the retrieval and setting of * igraph attributes in Python as if it were of a Python mapping type) */ PyMappingMethods igraphmodule_Vertex_as_mapping = { // returns the number of vertex attributes (lenfunc)igraphmodule_Vertex_attribute_count, // returns an attribute by name (binaryfunc)igraphmodule_Vertex_get_attribute, // sets an attribute by name (objobjargproc)igraphmodule_Vertex_set_attribute }; /** * \ingroup python_interface_vertex * Getter/setter table for the \c igraph.Vertex object */ PyGetSetDef igraphmodule_Vertex_getseters[] = { {"index", (getter)igraphmodule_Vertex_get_index, NULL, "Index of the vertex", NULL }, {"graph", (getter)igraphmodule_Vertex_get_graph, NULL, "The graph the vertex belongs to", NULL }, {NULL} }; /** \ingroup python_interface_vertex * Python type object referencing the methods Python calls when it performs various operations on * a vertex of a graph */ PyTypeObject igraphmodule_VertexType = { PyVarObject_HEAD_INIT(0, 0) "igraph.Vertex", /* tp_name */ sizeof(igraphmodule_VertexObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)igraphmodule_Vertex_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare (2.x) / tp_reserved (3.x) */ (reprfunc)igraphmodule_Vertex_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ &igraphmodule_Vertex_as_mapping, /* tp_as_mapping */ (hashfunc)igraphmodule_Vertex_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Class representing a single vertex in a graph.\n\n" "The vertex is referenced by its index, so if the underlying graph\n" "changes, the semantics of the vertex object might change as well\n" "(if the vertex indices are altered in the original graph).\n\n" "The attributes of the vertex can be accessed by using the vertex\n" "as a hash:\n\n" " >>> v[\"color\"] = \"red\" #doctest: +SKIP\n" " >>> print v[\"color\"] #doctest: +SKIP\n" " red\n", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ (richcmpfunc)igraphmodule_Vertex_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ igraphmodule_Vertex_methods, /* tp_methods */ 0, /* tp_members */ igraphmodule_Vertex_getseters, /* tp_getset */ }; python-igraph-0.7.1.post6/src/vertexobject.h0000644000076500000240000000403012460534506021436 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_VERTEXOBJECT_H #define PYTHON_VERTEXOBJECT_H #include #include "graphobject.h" #include "py2compat.h" /** * \ingroup python_interface_vertex * \brief A structure representing a vertex of a graph */ typedef struct { PyObject_HEAD igraphmodule_GraphObject* gref; igraph_integer_t idx; Py_hash_t hash; } igraphmodule_VertexObject; int igraphmodule_Vertex_clear(igraphmodule_VertexObject *self); void igraphmodule_Vertex_dealloc(igraphmodule_VertexObject* self); int igraphmodule_Vertex_Check(PyObject *obj); int igraphmodule_Vertex_Validate(PyObject *obj); PyObject* igraphmodule_Vertex_New(igraphmodule_GraphObject *gref, igraph_integer_t idx); PyObject* igraphmodule_Vertex_repr(igraphmodule_VertexObject *self); PyObject* igraphmodule_Vertex_attributes(igraphmodule_VertexObject* self); PyObject* igraphmodule_Vertex_attribute_names(igraphmodule_VertexObject* self); igraph_integer_t igraphmodule_Vertex_get_index_igraph_integer(igraphmodule_VertexObject* self); long igraphmodule_Vertex_get_index_long(igraphmodule_VertexObject* self); PyObject* igraphmodule_Vertex_update_attributes(PyObject* self, PyObject* args, PyObject* kwds); extern PyTypeObject igraphmodule_VertexType; #endif python-igraph-0.7.1.post6/src/vertexseqobject.c0000644000076500000240000010320712460534506022150 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* vim: set ts=2 sts=2 sw=2 et: */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "attributes.h" #include "common.h" #include "convert.h" #include "error.h" #include "py2compat.h" #include "vertexseqobject.h" #include "vertexobject.h" #define GET_GRAPH(obj) (((igraphmodule_GraphObject*)obj->gref)->g) /** * \ingroup python_interface * \defgroup python_interface_vertexseq Vertex sequence object */ PyTypeObject igraphmodule_VertexSeqType; /** * \ingroup python_interface_vertexseq * \brief Allocate a new vertex sequence object for a given graph * \return the allocated PyObject */ PyObject* igraphmodule_VertexSeq_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { igraphmodule_VertexSeqObject *o; o=(igraphmodule_VertexSeqObject*)PyType_GenericNew(subtype, args, kwds); if (o == NULL) return NULL; igraph_vs_all(&o->vs); o->gref=0; o->weakreflist=0; RC_ALLOC("VertexSeq", o); return (PyObject*)o; } /** * \ingroup python_interface_vertexseq * \brief Copies a vertex sequence object * \return the copied PyObject */ igraphmodule_VertexSeqObject* igraphmodule_VertexSeq_copy(igraphmodule_VertexSeqObject* o) { igraphmodule_VertexSeqObject *copy; copy=(igraphmodule_VertexSeqObject*)PyType_GenericNew(Py_TYPE(o), 0, 0); if (copy == NULL) return NULL; if (igraph_vs_type(&o->vs) == IGRAPH_VS_VECTOR) { igraph_vector_t v; if (igraph_vector_copy(&v, o->vs.data.vecptr)) { igraphmodule_handle_igraph_error(); return 0; } if (igraph_vs_vector_copy(©->vs, &v)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); return 0; } igraph_vector_destroy(&v); } else { copy->vs = o->vs; } copy->gref = o->gref; if (o->gref) Py_INCREF(o->gref); RC_ALLOC("VertexSeq(copy)", copy); return copy; } /** * \ingroup python_interface_vertexseq * \brief Initialize a new vertex sequence object for a given graph * \return the initialized PyObject */ int igraphmodule_VertexSeq_init(igraphmodule_VertexSeqObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "graph", "vertices", NULL }; PyObject *g, *vsobj=Py_None; igraph_vs_t vs; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O", kwlist, &igraphmodule_GraphType, &g, &vsobj)) return -1; if (vsobj == Py_None) { /* If vs is None, we are selecting all the vertices */ igraph_vs_all(&vs); } else if (PyInt_Check(vsobj)) { /* We selected a single vertex */ long int idx = PyInt_AsLong(vsobj); if (idx < 0 || idx >= igraph_vcount(&((igraphmodule_GraphObject*)g)->g)) { PyErr_SetString(PyExc_ValueError, "vertex index out of range"); return -1; } igraph_vs_1(&vs, (igraph_integer_t)idx); } else { igraph_vector_t v; igraph_integer_t n = igraph_vcount(&((igraphmodule_GraphObject*)g)->g); if (igraphmodule_PyObject_to_vector_t(vsobj, &v, 1)) return -1; if (!igraph_vector_isininterval(&v, 0, n-1)) { igraph_vector_destroy(&v); PyErr_SetString(PyExc_ValueError, "vertex index out of range"); return -1; } if (igraph_vs_vector_copy(&vs, &v)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); return -1; } igraph_vector_destroy(&v); } self->vs = vs; Py_INCREF(g); self->gref = (igraphmodule_GraphObject*)g; return 0; } /** * \ingroup python_interface_vertexseq * \brief Deallocates a Python representation of a given vertex sequence object */ void igraphmodule_VertexSeq_dealloc(igraphmodule_VertexSeqObject* self) { if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); if (self->gref) { igraph_vs_destroy(&self->vs); Py_DECREF(self->gref); self->gref=0; } Py_TYPE(self)->tp_free((PyObject*)self); RC_DEALLOC("VertexSeq", self); } /** * \ingroup python_interface_vertexseq * \brief Returns the length of the sequence */ int igraphmodule_VertexSeq_sq_length(igraphmodule_VertexSeqObject* self) { igraph_t *g; igraph_integer_t result; if (!self->gref) return -1; g=&GET_GRAPH(self); if (igraph_vs_size(g, &self->vs, &result)) { igraphmodule_handle_igraph_error(); return -1; } return (int)result; } /** * \ingroup python_interface_vertexseq * \brief Returns the item at the given index in the sequence */ PyObject* igraphmodule_VertexSeq_sq_item(igraphmodule_VertexSeqObject* self, Py_ssize_t i) { igraph_t *g; igraph_integer_t idx = -1; if (!self->gref) return NULL; g=&GET_GRAPH(self); switch (igraph_vs_type(&self->vs)) { case IGRAPH_VS_ALL: if (i >= 0 && i < igraph_vcount(g)) idx = (igraph_integer_t)i; break; case IGRAPH_VS_VECTOR: case IGRAPH_VS_VECTORPTR: if (i >= 0 && i < igraph_vector_size(self->vs.data.vecptr)) idx = (igraph_integer_t)VECTOR(*self->vs.data.vecptr)[i]; break; case IGRAPH_VS_1: if (i == 0) idx = self->vs.data.vid; break; case IGRAPH_VS_SEQ: if (i >= 0 && i < self->vs.data.seq.to - self->vs.data.seq.from) idx = self->vs.data.seq.from + (igraph_integer_t)i; break; /* TODO: IGRAPH_VS_ADJ, IGRAPH_VS_NONADJ - someday :) They are unused yet in the Python interface */ } if (idx < 0) { PyErr_SetString(PyExc_IndexError, "vertex index out of range"); return NULL; } return igraphmodule_Vertex_New(self->gref, idx); } /** \ingroup python_interface_vertexseq * \brief Returns the list of attribute names */ PyObject* igraphmodule_VertexSeq_attribute_names(igraphmodule_VertexSeqObject* self) { return igraphmodule_Graph_vertex_attributes(self->gref); } /** \ingroup python_interface_vertexseq * \brief Returns the list of values for a given attribute */ PyObject* igraphmodule_VertexSeq_get_attribute_values(igraphmodule_VertexSeqObject* self, PyObject* o) { igraphmodule_GraphObject *gr = self->gref; PyObject *result=0, *values, *item; long int i, n; if (!igraphmodule_attribute_name_check(o)) return 0; PyErr_Clear(); values=PyDict_GetItem(ATTR_STRUCT_DICT(&gr->g)[ATTRHASH_IDX_VERTEX], o); if (!values) { PyErr_SetString(PyExc_KeyError, "Attribute does not exist"); return NULL; } else if (PyErr_Occurred()) return NULL; switch (igraph_vs_type(&self->vs)) { case IGRAPH_VS_NONE: n = 0; result = PyList_New(0); break; case IGRAPH_VS_ALL: n = PyList_Size(values); result = PyList_New(n); if (!result) return 0; for (i=0; ivs.data.vecptr); result = PyList_New(n); if (!result) return 0; for (i=0; ivs.data.vecptr)[i]); Py_INCREF(item); PyList_SET_ITEM(result, i, item); } break; case IGRAPH_VS_SEQ: n = self->vs.data.seq.to - self->vs.data.seq.from; result = PyList_New(n); if (!result) return 0; for (i=0; ivs.data.seq.from+i); Py_INCREF(item); PyList_SET_ITEM(result, i, item); } break; default: PyErr_SetString(PyExc_RuntimeError, "invalid vertex selector"); } return result; } PyObject* igraphmodule_VertexSeq_get_attribute_values_mapping(igraphmodule_VertexSeqObject *self, PyObject *o) { long int index; /* Handle integer indices according to the sequence protocol */ if (PyIndex_Check(o)) { index = PyNumber_AsSsize_t(o, 0); return igraphmodule_VertexSeq_sq_item(self, index); } /* Handle strings according to the mapping protocol */ if (PyBaseString_Check(o)) return igraphmodule_VertexSeq_get_attribute_values(self, o); /* Handle iterables and slices by calling the select() method */ if (PySlice_Check(o) || PyObject_HasAttrString(o, "__iter__")) { PyObject *result, *args; args = Py_BuildValue("(O)", o); if (!args) return NULL; result = igraphmodule_VertexSeq_select(self, args); Py_DECREF(args); return result; } /* Handle everything else according to the mapping protocol */ return igraphmodule_VertexSeq_get_attribute_values(self, o); } /** \ingroup python_interface_vertexseq * \brief Sets the list of values for a given attribute */ int igraphmodule_VertexSeq_set_attribute_values_mapping(igraphmodule_VertexSeqObject* self, PyObject* attrname, PyObject* values) { PyObject *dict, *list, *item; igraphmodule_GraphObject *gr; igraph_vector_t vs; long i, j, n, no_of_nodes; gr = self->gref; dict = ATTR_STRUCT_DICT(&gr->g)[ATTRHASH_IDX_VERTEX]; if (!igraphmodule_attribute_name_check(attrname)) return -1; if (PyString_IsEqualToASCIIString(attrname, "name")) igraphmodule_invalidate_vertex_name_index(&gr->g); if (values == 0) { if (igraph_vs_type(&self->vs) == IGRAPH_VS_ALL) return PyDict_DelItem(dict, attrname); PyErr_SetString(PyExc_TypeError, "can't delete attribute from a vertex sequence not representing the whole graph"); return -1; } if (PyString_Check(values) || !PySequence_Check(values)) { /* If values is a string or not a sequence, we construct a list with a * single element (the value itself) and then call ourselves again */ int result; PyObject *newList = PyList_New(1); if (newList == 0) return -1; Py_INCREF(values); PyList_SET_ITEM(newList, 0, values); /* reference stolen here */ result = igraphmodule_VertexSeq_set_attribute_values_mapping(self, attrname, newList); Py_DECREF(newList); return result; } n=PySequence_Size(values); if (n<0) return -1; if (igraph_vs_type(&self->vs) == IGRAPH_VS_ALL) { no_of_nodes = (long)igraph_vcount(&gr->g); if (n == 0 && no_of_nodes > 0) { PyErr_SetString(PyExc_ValueError, "sequence must not be empty"); return -1; } /* Check if we already have attributes with the given name */ list = PyDict_GetItem(dict, attrname); if (list != 0) { /* Yes, we have. Modify its items to the items found in values */ for (i=0, j=0; ig, self->vs, &vs)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&vs); return -1; } no_of_nodes = (long)igraph_vector_size(&vs); if (n == 0 && no_of_nodes > 0) { PyErr_SetString(PyExc_ValueError, "sequence must not be empty"); igraph_vector_destroy(&vs); return -1; } /* Check if we already have attributes with the given name */ list = PyDict_GetItem(dict, attrname); if (list != 0) { /* Yes, we have. Modify its items to the items found in values */ for (i=0, j=0; ig); list = PyList_New(n2); if (list == 0) { igraph_vector_destroy(&vs); return -1; } for (i=0; igref->g, item, &i)) return NULL; /* We now have the ID of the vertex in the graph. If the vertex sequence * itself represents the full vertex sequence of the graph, we can return * here. If not, we have to check whether the vertex sequence contains this * ID or not. */ if (igraph_vs_is_all(&self->vs)) return PySequence_GetItem((PyObject*)self, i); if (igraph_vit_create(&self->gref->g, self->vs, &vit)) { igraphmodule_handle_igraph_error(); return NULL; } for (n = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), n++) { if (IGRAPH_VIT_GET(vit) == i) { igraph_vit_destroy(&vit); return PySequence_GetItem((PyObject*)self, n); } } igraph_vit_destroy(&vit); PyErr_SetString(PyExc_ValueError, "vertex with the given name exists but not in the current sequence"); return NULL; } PyErr_SetString(PyExc_IndexError, "no such vertex"); return NULL; } /** * \ingroup python_interface_vertexseq * \brief Selects a subset of the vertex sequence based on some criteria */ PyObject* igraphmodule_VertexSeq_select(igraphmodule_VertexSeqObject *self, PyObject *args) { igraphmodule_VertexSeqObject *result; igraphmodule_GraphObject *gr; long i, j, n, m; gr=self->gref; result=igraphmodule_VertexSeq_copy(self); if (result==0) return NULL; /* First, filter by positional arguments */ n = PyTuple_Size(args); for (i=0; ivs); igraph_vs_none(&result->vs); /* We can simply bail out here */ return (PyObject*)result; } else if (PyCallable_Check(item)) { /* Call the callable for every vertex in the current sequence to * determine what's up */ igraph_bool_t was_excluded = 0; igraph_vector_t v; if (igraph_vector_init(&v, 0)) { igraphmodule_handle_igraph_error(); return 0; } m = PySequence_Size((PyObject*)result); for (j=0; jvs); if (igraph_vs_vector_copy(&result->vs, &v)) { Py_DECREF(result); igraph_vector_destroy(&v); igraphmodule_handle_igraph_error(); return NULL; } } igraph_vector_destroy(&v); } else if (PyInt_Check(item)) { /* Integers are treated specially: from now on, all remaining items * in the argument list must be integers and they will be used together * to restrict the vertex set. Integers are interpreted as indices on the * vertex set and NOT on the original, untouched vertex sequence of the * graph */ igraph_vector_t v, v2; if (igraph_vector_init(&v, 0)) { igraphmodule_handle_igraph_error(); return 0; } if (igraph_vector_init(&v2, 0)) { igraph_vector_destroy(&v); igraphmodule_handle_igraph_error(); return 0; } if (igraph_vs_as_vector(&gr->g, self->vs, &v2)) { igraph_vector_destroy(&v); igraph_vector_destroy(&v2); igraphmodule_handle_igraph_error(); return 0; } m = igraph_vector_size(&v2); for (; i= m || idx < 0) { PyErr_SetString(PyExc_ValueError, "vertex index out of range"); igraph_vector_destroy(&v); igraph_vector_destroy(&v2); return NULL; } if (igraph_vector_push_back(&v, VECTOR(v2)[idx])) { Py_DECREF(result); igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); igraph_vector_destroy(&v2); return NULL; } } igraph_vector_destroy(&v2); igraph_vs_destroy(&result->vs); if (igraph_vs_vector_copy(&result->vs, &v)) { Py_DECREF(result); igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); return NULL; } igraph_vector_destroy(&v); } else { /* Iterators, slices and everything that was not handled directly */ PyObject *iter=0, *item2; igraph_vector_t v, v2; /* Allocate stuff */ if (igraph_vector_init(&v, 0)) { igraphmodule_handle_igraph_error(); Py_DECREF(result); return 0; } if (igraph_vector_init(&v2, 0)) { igraph_vector_destroy(&v); Py_DECREF(result); igraphmodule_handle_igraph_error(); return 0; } if (igraph_vs_as_vector(&gr->g, self->vs, &v2)) { igraph_vector_destroy(&v); igraph_vector_destroy(&v2); Py_DECREF(result); igraphmodule_handle_igraph_error(); return 0; } m = igraph_vector_size(&v2); /* Create an appropriate iterator */ if (PySlice_Check(item)) { /* Create an iterator from the slice (which is not iterable by default) */ Py_ssize_t start, stop, step, sl; PyObject* range; igraph_bool_t ok; /* Casting to void* because Python 2.x expects PySliceObject* * but Python 3.x expects PyObject* */ ok = (PySlice_GetIndicesEx((void*)item, igraph_vector_size(&v2), &start, &stop, &step, &sl) == 0); if (ok) { range = PyObject_CallFunction((PyObject*)&PyRange_Type, "lll", start, stop, step); ok = (range != 0); } if (ok) { iter = PyObject_GetIter(range); Py_DECREF(range); ok = (iter != 0); } if (!ok) { igraph_vector_destroy(&v); igraph_vector_destroy(&v2); PyErr_SetString(PyExc_TypeError, "error while converting slice to iterator"); Py_DECREF(result); return 0; } } else { /* Simply create the iterator corresponding to the object */ iter = PyObject_GetIter(item); } /* Did we manage to get an iterator? */ if (iter == 0) { igraph_vector_destroy(&v); igraph_vector_destroy(&v2); PyErr_SetString(PyExc_TypeError, "invalid vertex filter among positional arguments"); Py_DECREF(result); return 0; } /* Do the iteration */ while ((item2=PyIter_Next(iter)) != 0) { if (PyInt_Check(item2)) { long idx = PyInt_AsLong(item2); Py_DECREF(item2); if (idx >= m || idx < 0) { PyErr_SetString(PyExc_ValueError, "vertex index out of range"); Py_DECREF(result); Py_DECREF(iter); igraph_vector_destroy(&v); igraph_vector_destroy(&v2); return NULL; } if (igraph_vector_push_back(&v, VECTOR(v2)[idx])) { Py_DECREF(result); Py_DECREF(iter); igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); igraph_vector_destroy(&v2); return NULL; } } else { /* We simply ignore elements that we don't know */ Py_DECREF(item2); } } /* Deallocate stuff */ igraph_vector_destroy(&v2); Py_DECREF(iter); if (PyErr_Occurred()) { igraph_vector_destroy(&v); Py_DECREF(result); return 0; } igraph_vs_destroy(&result->vs); if (igraph_vs_vector_copy(&result->vs, &v)) { Py_DECREF(result); igraphmodule_handle_igraph_error(); igraph_vector_destroy(&v); return NULL; } igraph_vector_destroy(&v); } } return (PyObject*)result; } /** * \ingroup python_interface_vertexseq * Converts a vertex sequence to an igraph vector containing the corresponding * vertex indices. The vector MUST be initialized and will be resized if needed. * \return 0 if everything was ok, 1 otherwise */ int igraphmodule_VertexSeq_to_vector_t(igraphmodule_VertexSeqObject *self, igraph_vector_t *v) { return igraph_vs_as_vector(&self->gref->g, self->vs, v); } /** * \ingroup python_interface_vertexseq * Returns the graph where the vertex sequence belongs */ PyObject* igraphmodule_VertexSeq_get_graph(igraphmodule_VertexSeqObject* self, void* closure) { Py_INCREF(self->gref); return (PyObject*)self->gref; } /** * \ingroup python_interface_vertexseq * Returns the indices of the vertices in this vertex sequence */ PyObject* igraphmodule_VertexSeq_get_indices(igraphmodule_VertexSeqObject* self, void* closure) { igraphmodule_GraphObject *gr = self->gref; igraph_vector_t vs; PyObject *result; if (igraph_vector_init(&vs, 0)) { igraphmodule_handle_igraph_error(); return 0; } if (igraph_vs_as_vector(&gr->g, self->vs, &vs)) { igraphmodule_handle_igraph_error(); igraph_vector_destroy(&vs); return 0; } result = igraphmodule_vector_t_to_PyList(&vs, IGRAPHMODULE_TYPE_INT); igraph_vector_destroy(&vs); return result; } /** * \ingroup python_interface_vertexseq * Returns the internal dictionary mapping vertex names to vertex IDs. */ PyObject* igraphmodule_VertexSeq__name_index(igraphmodule_VertexSeqObject* self, void* closure) { igraphmodule_GraphObject *gr = self->gref; PyObject* result = ATTR_NAME_INDEX(&gr->g); if (result == 0) Py_RETURN_NONE; Py_INCREF(result); return result; } /** * \ingroup python_interface_vertexseq * Re-creates the dictionary that maps vertex names to vertex IDs. */ PyObject* igraphmodule_VertexSeq__reindex_names(igraphmodule_VertexSeqObject* self) { igraphmodule_index_vertex_names(&self->gref->g, 1); Py_RETURN_NONE; } /** * \ingroup python_interface_vertexseq * Method table for the \c igraph.VertexSeq object */ PyMethodDef igraphmodule_VertexSeq_methods[] = { {"attribute_names", (PyCFunction)igraphmodule_VertexSeq_attribute_names, METH_NOARGS, "attribute_names() -> list\n\n" "Returns the attribute name list of the graph's vertices\n" }, {"find", (PyCFunction)igraphmodule_VertexSeq_find, METH_VARARGS, "find(condition) -> Vertex\n\n" "For internal use only.\n" }, {"get_attribute_values", (PyCFunction)igraphmodule_VertexSeq_get_attribute_values, METH_O, "get_attribute_values(attrname) -> list\n" "Returns the value of a given vertex attribute for all vertices in a list.\n\n" "The values stored in the list are exactly the same objects that are stored\n" "in the vertex attribute, meaning that in the case of mutable objects,\n" "the modification of the list element does affect the attribute stored in\n" "the vertex. In the case of immutable objects, modification of the list\n" "does not affect the attribute values.\n\n" "@param attrname: the name of the attribute\n" }, {"set_attribute_values", (PyCFunction)igraphmodule_VertexSeq_set_attribute_values, METH_VARARGS | METH_KEYWORDS, "set_attribute_values(attrname, values) -> list\n" "Sets the value of a given vertex attribute for all vertices\n\n" "@param attrname: the name of the attribute\n" "@param values: the new attribute values in a list\n" }, {"select", (PyCFunction)igraphmodule_VertexSeq_select, METH_VARARGS, "select(...) -> VertexSeq\n\n" "For internal use only.\n" }, {"_reindex_names", (PyCFunction)igraphmodule_VertexSeq__reindex_names, METH_NOARGS, "Re-creates the dictionary that maps vertex names to IDs.\n\n" "For internal use only.\n" }, {NULL} }; /** * \ingroup python_interface_vertexseq * This is the collection of functions necessary to implement the * vertex sequence as a real sequence (e.g. allowing to reference * vertices by indices) */ static PySequenceMethods igraphmodule_VertexSeq_as_sequence = { (lenfunc)igraphmodule_VertexSeq_sq_length, 0, /* sq_concat */ 0, /* sq_repeat */ (ssizeargfunc)igraphmodule_VertexSeq_sq_item, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ 0, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; /** * \ingroup python_interface_vertexseq * This is the collection of functions necessary to implement the * vertex sequence as a mapping (which maps attribute names to values) */ static PyMappingMethods igraphmodule_VertexSeq_as_mapping = { /* this must be null, otherwise it f.cks up sq_length when inherited */ (lenfunc) 0, /* returns the values of an attribute by name */ (binaryfunc) igraphmodule_VertexSeq_get_attribute_values_mapping, /* sets the values of an attribute by name */ (objobjargproc) igraphmodule_VertexSeq_set_attribute_values_mapping, }; /** * \ingroup python_interface_vertexseq * Getter/setter table for the \c igraph.VertexSeq object */ PyGetSetDef igraphmodule_VertexSeq_getseters[] = { {"graph", (getter)igraphmodule_VertexSeq_get_graph, NULL, "The graph the vertex sequence belongs to", NULL, }, {"indices", (getter)igraphmodule_VertexSeq_get_indices, NULL, "The vertex indices in this vertex sequence", NULL, }, {"_name_index", (getter)igraphmodule_VertexSeq__name_index, NULL, "The internal index mapping vertex names to IDs", NULL }, {NULL} }; /** \ingroup python_interface_vertexseq * Python type object referencing the methods Python calls when it performs various operations on * a vertex sequence of a graph */ PyTypeObject igraphmodule_VertexSeqType = { PyVarObject_HEAD_INIT(0, 0) "igraph.core.VertexSeq", /* tp_name */ sizeof(igraphmodule_VertexSeqObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)igraphmodule_VertexSeq_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare (2.x) / tp_reserved (3.x) */ 0, /* tp_repr */ 0, /* tp_as_number */ &igraphmodule_VertexSeq_as_sequence, /* tp_as_sequence */ &igraphmodule_VertexSeq_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Low-level representation of a vertex sequence.\n\n" /* tp_doc */ "Don't use it directly, use L{igraph.VertexSeq} instead.\n\n" "@deffield ref: Reference", 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ offsetof(igraphmodule_VertexSeqObject, weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ igraphmodule_VertexSeq_methods, /* tp_methods */ 0, /* tp_members */ igraphmodule_VertexSeq_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) igraphmodule_VertexSeq_init, /* tp_init */ 0, /* tp_alloc */ (newfunc) igraphmodule_VertexSeq_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weakreflist */ }; python-igraph-0.7.1.post6/src/vertexseqobject.h0000644000076500000240000000371312453614202022150 0ustar ntamasstaff00000000000000/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2006-2012 Tamas Nepusz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PYTHON_VERTEXSEQOBJECT_H #define PYTHON_VERTEXSEQOBJECT_H #include #include "graphobject.h" /** * \ingroup python_interface_vertexseq * \brief A structure representing the vertex sequence of a graph */ typedef struct { PyObject_HEAD igraphmodule_GraphObject* gref; igraph_vs_t vs; PyObject* weakreflist; } igraphmodule_VertexSeqObject; PyObject* igraphmodule_VertexSeq_new(PyTypeObject *subtype, PyObject* args, PyObject* kwds); int igraphmodule_VertexSeq_init(igraphmodule_VertexSeqObject* self, PyObject* args, PyObject* kwds); void igraphmodule_VertexSeq_dealloc(igraphmodule_VertexSeqObject* self); int igraphmodule_VertexSeq_sq_length(igraphmodule_VertexSeqObject *self); PyObject* igraphmodule_VertexSeq_find(igraphmodule_VertexSeqObject *self, PyObject *args); PyObject* igraphmodule_VertexSeq_select(igraphmodule_VertexSeqObject *self, PyObject *args); int igraphmodule_VertexSeq_to_vector_t(igraphmodule_VertexSeqObject *self, igraph_vector_t *v); PyObject* igraphmodule_VertexSeq_get_graph(igraphmodule_VertexSeqObject *self, void* closure); extern PyTypeObject igraphmodule_VertexSeqType; #endif python-igraph-0.7.1.post6/test/0000755000076500000240000000000012534343010016742 5ustar ntamasstaff00000000000000python-igraph-0.7.1.post6/test/cytoscape_test.py0000755000076500000240000000510312453614202022353 0ustar ntamasstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Simple script that tests CytoscapeGraphDrawer. This script is kept separate from the unit tests as it is very hard to test for the correctness of CytoscapeGraphDrawer without a working instance of Cytoscape. Prerequisites for running this test: 1. Start Cytoscape 2. Activate the Cytoscape RPC plugin, listening at port 9000 """ from igraph import Graph from igraph.drawing.graph import CytoscapeGraphDrawer def test(): g = Graph.GRG(100, 0.2) ### Adding network attributes g["name"] = "Network name" g["version"] = 5 g["obsolete"] = False g["density"] = g.density() ### Adding vertex attributes # String attribute g.vs["name"] = ["Node %d" % (i+1) for i in xrange(g.vcount())] # Integer attribute g.vs["degree"] = g.degree() # Float attribute g.vs["pagerank"] = g.pagerank() # Boolean attribute g.vs["even"] = [i % 2 for i in xrange(g.vcount())] # Mixed attribute g.vs["mixed"] = ["abc", 123, None, 1.0] * ((g.vcount()+3) / 4) # Special attribute with Hungarian accents g.vs[0]["name"] = u"árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP" ### Adding edge attributes # String attribute g.es["name"] = ["Edge %d -- %d" % edge.tuple for edge in g.es] # Integer attribute g.es["multiplicity"] = g.count_multiple() # Float attribute g.es["betweenness"] = g.edge_betweenness() # Boolean attribute g.es["even"] = [i % 2 for i in xrange(g.ecount())] # Mixed attribute g.es["mixed"] = [u"yay", 123, None, 0.7] * ((g.ecount()+3) / 4) # Sending graph drawer = CytoscapeGraphDrawer() drawer.draw(g, layout="fr") # Fetching graph g2 = drawer.fetch() del g2.vs["hiddenLabel"] del g2.es["interaction"] # Check isomorphism result = g2.isomorphic(g) if not result: raise ValueError("g2 not isomorphic to g") # Check the graph attributes if set(g.attributes()) != set(g2.attributes()): raise ValueError("Graph attribute name set mismatch") for attr_name in g.attributes(): if g[attr_name] != g2[attr_name]: raise ValueError("Graph attribute mismatch for %r" % attr_name) # Check the vertex attribute names if set(g.vertex_attributes()) != set(g2.vertex_attributes()): raise ValueError("Vertex attribute name set mismatch") # Check the edge attribute names if set(g.edge_attributes()) != set(g2.edge_attributes()): raise ValueError("Edge attribute name set mismatch") if __name__ == "__main__": test() python-igraph-0.7.1.post6/test/doctests.py0000755000076500000240000000071512453614202021156 0ustar ntamasstaff00000000000000#!/usr/bin/env python """ Runs all the doctests in the igraph module """ import doctest import igraph if __name__ == "__main__": doctest.testmod(igraph) doctest.testmod(igraph.clustering) doctest.testmod(igraph.cut) doctest.testmod(igraph.datatypes) doctest.testmod(igraph.drawing.utils) doctest.testmod(igraph.formula) doctest.testmod(igraph.remote.nexus) doctest.testmod(igraph.statistics) doctest.testmod(igraph.utils) python-igraph-0.7.1.post6/test/unittests.py0000755000076500000240000000041412453614202021364 0ustar ntamasstaff00000000000000#!/usr/bin/env python """ Simple script that runs the unit tests of igraph. """ import sys from igraph.test import run_tests if __name__ == "__main__": if "-v" in sys.argv: verbosity = 2 else: verbosity = 1 run_tests(verbosity=verbosity)