Path: blob/master/src/sage/numerical/backends/glpk_graph_backend.pyx
8817 views
"""1GLPK Backend for access to GLPK graph functions23AUTHORS:45- Christian Kuper (2012-11): Initial implementation67Methods index8-------------910**Graph creation and modification operations:**1112.. csv-table::13:class: contentstable14:widths: 30, 7015:delim: |1617:meth:`~GLPKGraphBackend.add_vertex` | Adds an isolated vertex to the graph.18:meth:`~GLPKGraphBackend.add_vertices` | Adds vertices from an iterable container of vertices.19:meth:`~GLPKGraphBackend.set_vertex_demand` | Sets the vertex parameters.20:meth:`~GLPKGraphBackend.set_vertices_demand` | Sets the parameters of selected vertices.21:meth:`~GLPKGraphBackend.get_vertex` | Returns a specific vertex as a ``dict`` Object.22:meth:`~GLPKGraphBackend.get_vertices` | Returns a dictionary of the dictonaries associated to each vertex.23:meth:`~GLPKGraphBackend.vertices` | Returns a ``list`` of all vertices.24:meth:`~GLPKGraphBackend.delete_vertex` | Removes a vertex from the graph.25:meth:`~GLPKGraphBackend.delete_vertices` | Removes vertices from the graph.26:meth:`~GLPKGraphBackend.add_edge` | Adds an edge between vertices ``u`` and ``v``.27:meth:`~GLPKGraphBackend.add_edges` | Adds edges to the graph.28:meth:`~GLPKGraphBackend.get_edge` | Returns an edge connecting two vertices.29:meth:`~GLPKGraphBackend.edges` | Returns a ``list`` of all edges in the graph.30:meth:`~GLPKGraphBackend.delete_edge` | Deletes an edge from the graph.31:meth:`~GLPKGraphBackend.delete_edges` | Deletes edges from the graph.3233**Graph writing operations:**3435.. csv-table::36:class: contentstable37:widths: 30, 7038:delim: |3940:meth:`~GLPKGraphBackend.write_graph` | Writes the graph to a plain text file.41:meth:`~GLPKGraphBackend.write_ccdata` | Writes the graph to a text file in DIMACS format.42:meth:`~GLPKGraphBackend.write_mincost` | Writes the mincost flow problem data to a text file in DIMACS format.43:meth:`~GLPKGraphBackend.write_maxflow` | Writes the maximum flow problem data to a text file in DIMACS format.4445**Network optimization operations:**4647.. csv-table::48:class: contentstable49:widths: 30, 7050:delim: |5152:meth:`~GLPKGraphBackend.mincost_okalg` | Finds solution to the mincost problem with the out-of-kilter algorithm.53:meth:`~GLPKGraphBackend.maxflow_ffalg` | Finds solution to the maxflow problem with Ford-Fulkerson algorithm.54:meth:`~GLPKGraphBackend.cpp` | Solves the critical path problem of a project network.5556Classes and methods57-------------------58"""5960##############################################################################61# Copyright (C) 2012 Christian Kuper <[email protected]>62# Distributed under the terms of the GNU General Public License (GPL)63# The full text of the GPL is available at:64# http://www.gnu.org/licenses/65##############################################################################6667from sage.numerical.mip import MIPSolverException6869include "sage/ext/interrupt.pxi"7071cdef class GLPKGraphBackend(object):72"""73GLPK Backend for access to GLPK graph functions7475The constructor can either be called without arguments (which results in an76empty graph) or with arguments to read graph data from a file.7778INPUT:7980- ``data`` -- a filename or a :class:`Graph` object.8182- ``format`` -- when ``data`` is a filename, specifies the format of the83data read from a file. The ``format`` parameter is a string and can take84values as described in the table below.8586**Format parameters:**8788.. list-table::89:widths: 10 709091* - ``plain``9293- Read data from a plain text file containing the following information:9495| nv na96| i[1] j[1]97| i[2] j[2]98| . . .99| i[na] j[na]100101where:102103* nv is the number of vertices (nodes);104105* na is the number of arcs;106107* i[k], k = 1, . . . , na, is the index of tail vertex of arc k;108109* j[k], k = 1, . . . , na, is the index of head vertex of arc k.110111112* - ``dimacs``113114- Read data from a plain ASCII text file in DIMACS format.115A discription of the DIMACS format can be found at116http://dimacs.rutgers.edu/Challenges/.117118* - ``mincost``119120- Reads the mincost flow problem data from a text file in DIMACS format121122* - ``maxflow``123124- Reads the maximum flow problem data from a text file in DIMACS format125126.. NOTE::127128When ``data`` is a :class:`Graph`, the following restrictions are129applied.130131* vertices -- the value of the demand of each vertex (see132:meth:`set_vertex_demand`) is obtained from the numerical133value associated with the key "rhs" if it is a dictionary.134135* edges -- The edge values used in the algorithms are read from the136edges labels (and left undefined if the edge labels are equal to137``None``). To be defined, the labels must be ``dict`` objects with138keys "low", "cap" and "cost". See :meth:`get_edge` for details.139140EXAMPLES:141142The following example creates an empty graph::143144sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend145sage: gbe = GLPKGraphBackend()146147The following example creates an empty graph, adds some data, saves the data148to a file and loads it::149150sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend151sage: gbe = GLPKGraphBackend()152sage: gbe.add_vertices([None, None])153['0', '1']154sage: a = gbe.add_edge('0', '1')155sage: gbe.write_graph(SAGE_TMP+"/graph.txt")156Writing graph to ...1572 lines were written1580159sage: gbe1 = GLPKGraphBackend(SAGE_TMP+"/graph.txt", "plain")160Reading graph from ...161Graph has 2 vertices and 1 arc1622 lines were read163164The following example imports a Sage ``Graph`` and then uses it to solve a165maxflow problem::166167sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend168sage: g = graphs.PappusGraph()169sage: for ed in g.edges():170... g.set_edge_label(ed[0], ed[1], {"cap":1})171sage: gbe = GLPKGraphBackend(g)172sage: gbe.maxflow_ffalg('1', '2')1733.0174"""175176def __cinit__(self, data = None, format = "plain"):177"""178Constructor179180The constructor can either be called without arguments creating an empty181graph or with arguments to read graph data from a file or a Sage182:class:`Graph`. See documentation of :class:`GLPKGraphBackend` for183details.184185EXAMPLE::186187sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend188sage: gbe = GLPKGraphBackend()189"""190191from sage.graphs.graph import Graph192193self.graph = <_glp_graph*> glp_create_graph(sizeof(c_v_data),194sizeof(c_a_data))195196if self.graph is NULL:197raise MemoryError("Error allocating memory.")198199self.s = 1200self.t = 1201202if isinstance(data,str):203fname = data204res = 0205if format == "plain":206res = glp_read_graph(self.graph, fname)207elif format == "dimacs":208res = glp_read_ccdata(self.graph, 0, fname)209elif format == "mincost":210res = glp_read_mincost(self.graph, 0, 0, sizeof(double),211sizeof(double) + sizeof(double), fname)212elif format == "maxflow":213res = glp_read_maxflow(self.graph, &self.s, &self.t,214sizeof(double), fname)215if res != 0:216raise IOError("Could not read graph from file %s" % (fname))217218elif isinstance(data, Graph):219self.__add_vertices_sage(data)220self.__add_edges_sage(data)221else:222ValueError("Input data is not supported")223224cpdef add_vertex(self, char * name = NULL):225"""226Adds an isolated vertex to the graph.227228If the vertex already exists, nothing is done.229230INPUT:231232- ``name`` -- ``String`` of max 255 chars length. If no name is233specified, then the vertex will be represented by the string234representation of the ID of the vertex or - if this already exists -235a string representation of the least integer not already representing236a vertex.237238OUTPUT:239240If no ``name`` is passed as an argument, the new vertex name is241returned. ``None`` otherwise.242243EXAMPLE::244245sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend246sage: gbe = GLPKGraphBackend()247sage: gbe.add_vertex()248'0'249sage: gbe.add_vertex("2")250sage: gbe.add_vertex()251'1'252"""253cdef int n254cdef vn_t = 0255cdef char* c_name256257if name is not NULL and self._find_vertex(name) >= 0:258return None259260cdef int vn = glp_add_vertices(self.graph, 1)261262if name is not NULL:263glp_set_vertex_name(self.graph, vn, name)264return None265266else:267s = str(vn-1)268c_name = s269n = self._find_vertex(c_name)270271# This is costly, but hopefully will not happen often.272while n >= 0:273vn_t += 1274s = str(vn_t-1)275c_name = s276n = self._find_vertex(c_name)277278glp_set_vertex_name(self.graph, vn, c_name)279return c_name280281cpdef __add_vertices_sage(self, g):282"""283Adds vertices to the GLPK Graph.284285This function is only used when importing a286:class:`~sage.graphs.generic_graph.GenericGraph` object.287288EXAMPLE::289290sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend291sage: g = graphs.PappusGraph()292sage: for ed in g.edges():293... g.set_edge_label(ed[0], ed[1], {"cap":1})294sage: gbe = GLPKGraphBackend(g)295sage: gbe.maxflow_ffalg('1', '2')2963.0297"""298cdef int n299cdef int i300cdef double rhs301cdef _glp_vertex* vert302cdef char* name303304verts = g.vertices()305n = len(verts)306if n < 1:307raise ValueError("Graph must contain vertices")308309glp_add_vertices(self.graph, n)310311for i in range(n):312vert = self.graph.v[i+1]313s = str(verts[i])314name = s315glp_set_vertex_name(self.graph, i+1, name)316317if g.get_vertex(verts[i]) is not None:318try:319(<c_v_data *>vert.data).rhs = g.get_vertex(verts[i])["rhs"]320except AttributeError:321pass322323glp_create_v_index(self.graph)324325cpdef list add_vertices(self, vertices):326"""327Adds vertices from an iterable container of vertices.328329Vertices that already exist in the graph will not be added again.330331INPUT:332333- ``vertices`` -- iterator of vertex labels (``str``). A label can be334``None``.335336OUTPUT:337338Generated names of new vertices if there is at least one ``None`` value339present in ``vertices``. ``None`` otherwise.340341EXAMPLE::342343sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend344sage: gbe = GLPKGraphBackend()345sage: vertices = [None for i in range(3)]346sage: gbe.add_vertices(vertices)347['0', '1', '2']348sage: gbe.add_vertices(['A', 'B', None])349['5']350sage: gbe.add_vertices(['A', 'B', 'C'])351sage: gbe.vertices()352['0', '1', '2', 'A', 'B', '5', 'C']353354TESTS::355356sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend357sage: gbe = GLPKGraphBackend()358sage: gbe.add_vertices([None, None, None, '1'])359['0', '2', '3']360"""361362# We do not want to have [None,None,None,1] as input as a vertex named363# "1" would be created twice (a first time when adding a 'None' vertex,364# and and a second time when reading the last item of the list).365nonecount = 0366for v in vertices:367if v is None:368nonecount += 1369else:370self.add_vertex(v)371372if nonecount:373return [self.add_vertex() for i in range(nonecount)]374else:375return None376377cpdef set_vertex_demand(self, char* vertex, demand):378"""379Sets the demand of the vertex in a mincost flow algorithm.380381INPUT:382383- ``vertex`` -- Name of the vertex384385- ``demand`` -- the numerical value representing demand of the vertex in386a mincost flow alorithm (it could be for instance `-1` to represent a387sink, or `1` to represent a source and `0` for a neutral vertex). This388can either be an ``int`` or ``float`` value.389390EXAMPLE::391392sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend393sage: gbe = GLPKGraphBackend()394sage: vertices = [None for i in range(3)]395sage: gbe.add_vertices(vertices)396['0', '1', '2']397sage: gbe.set_vertex_demand('0', 2)398sage: gbe.get_vertex('0')['rhs']3992.0400sage: gbe.set_vertex_demand('3', 2)401Traceback (most recent call last):402...403KeyError: 'Vertex 3 does not exist.'404"""405cdef int n = self._find_vertex(vertex)406407if n < 0:408raise KeyError("Vertex " + vertex + " does not exist.")409410cdef _glp_vertex* vert = self.graph.v[n+1]411cdef double val = demand412(<c_v_data *>vert.data).rhs = val413414cpdef set_vertices_demand(self, list pairs):415"""416Sets the parameters of selected vertices.417418INPUT:419420- ``pairs`` -- A list of pairs ``(vertex, demand)`` associating a demand421to each vertex. For more information, see the documentation of422:meth:`set_vertex_demand`.423424EXAMPLE::425426sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend427sage: gbe = GLPKGraphBackend()428sage: vertices = [None for i in range(3)]429sage: gbe.add_vertices(vertices)430['0', '1', '2']431sage: gbe.set_vertices_demand([('0', 2), ('1', 3), ('3', 4)])432sage: sorted(gbe.get_vertex('1').items())433[('cut', 0), ('es', 0.0), ('ls', 0.0), ('pi', 0.0), ('rhs', 3.0)]434"""435436for v, param in pairs:437try:438self.set_vertex_demand(v, param)439except KeyError:440pass441442cpdef dict get_vertex(self, char* vertex):443"""444Returns a specific vertex as a ``dict`` Object.445446INPUT:447448- ``vertex`` -- The vertex label as ``str``.449450OUTPUT:451452The vertex as a ``dict`` object or ``None`` if the vertex does not453exist. The ``dict`` contains the values used or created by the different454algorithms. The values associated with the keys following keys contain:455456* "rhs" -- The supply / demand value the vertex (mincost alg)457* "pi" -- The node potential (mincost alg)458* "cut" -- The cut flag of the vertex (maxflow alg)459* "es" -- The earliest start of task (cpp alg)460* "ls" -- The latest start of task (cpp alg)461462EXAMPLE::463464sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend465sage: gbe = GLPKGraphBackend()466sage: verts = ["A", "B", "C", "D"]467sage: gbe.add_vertices(verts)468sage: sorted(gbe.get_vertex("A").items())469[('cut', 0), ('es', 0.0), ('ls', 0.0), ('pi', 0.0), ('rhs', 0.0)]470sage: gbe.get_vertex("F") is None471True472"""473474cdef int i = self._find_vertex(vertex)475if i < 0:476return None477478cdef _glp_vertex* vert = self.graph.v[i+1]479cdef c_v_data * vdata = <c_v_data *> vert.data480481return {482"rhs" : vdata.rhs,483"pi" : vdata.pi,484"cut" : vdata.cut,485"es" : vdata.es,486"ls" : vdata.ls487}488489cpdef dict get_vertices(self, verts):490"""491Returns a dictionary of the dictonaries associated to each vertex.492493INPUT:494495- ``verts`` -- iterable container of vertices496497OUTPUT:498499A list of pairs ``(vertex, properties)`` where ``properties`` is a500dictionary containing the numerical values associated with a vertex. For501more information, see the documentation of502:meth:`GLPKGraphBackend.get_vertex`.503504EXAMPLE::505506sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend507sage: gbe = GLPKGraphBackend()508sage: verts = ['A', 'B']509sage: gbe.add_vertices(verts)510sage: sorted(gbe.get_vertices(verts)['B'].items())511[('cut', 0), ('es', 0.0), ('ls', 0.0), ('pi', 0.0), ('rhs', 0.0)]512sage: gbe.get_vertices(["C", "D"])513{}514"""515vl = [(v, self.get_vertex(v)) for v in verts]516return dict([(v, p) for v, p in vl if p is not None])517518cpdef list vertices(self):519"""520Returns the list of all vertices521522.. NOTE::523524Changing elements of the ``list`` will not change anything in the525the graph.526527.. NOTE::528529If a vertex in the graph does not have a name / label it will appear530as ``None`` in the resulting ``list``.531532EXAMPLE::533534sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend535sage: gbe = GLPKGraphBackend()536sage: verts = ["A", "B", "C"]537sage: gbe.add_vertices(verts)538sage: a = gbe.vertices(); a539['A', 'B', 'C']540sage: a.pop(0)541'A'542sage: gbe.vertices()543['A', 'B', 'C']544"""545546return [self.graph.v[i+1].name if self.graph.v[i+1].name is not NULL547else None for i in range(self.graph.nv)]548549cpdef add_edge(self, char* u, char* v, dict params=None):550"""551Adds an edge between vertices ``u`` and ``v``.552553Allows adding an edge and optionally providing parameters used by the554algorithms. If a vertex does not exist it is created.555556INPUT:557558- ``u`` -- The name (as ``str``) of the tail vertex559560- ``v`` -- The name (as ``str``) of the head vertex561562- ``params`` -- An optional ``dict`` containing the edge parameters used563for the algorithms. The following keys are used:564565* ``low`` -- The minimum flow through the edge566567* ``cap`` -- The maximum capacity of the edge568569* ``cost`` -- The cost of transporting one unit through the edge570571EXAMPLE::572573sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend574sage: gbe = GLPKGraphBackend()575sage: gbe.add_edge("A", "B", {"low":0.0, "cap":10.0, "cost":5})576sage: gbe.vertices()577['A', 'B']578sage: for ed in gbe.edges():579... print ed[0], ed[1], ed[2]['cap'], ed[2]['cost'], ed[2]['low']580A B 10.0 5.0 0.0581sage: gbe.add_edge("B", "C", {"low":0.0, "cap":10.0, "cost":'5'})582Traceback (most recent call last):583...584TypeError: Invalid edge parameter.585"""586cdef int i = self._find_vertex(u)587cdef int j = self._find_vertex(v)588589if i < 0:590self.add_vertex(u)591i = self._find_vertex(u)592593if j < 0:594self.add_vertex(v)595j = self._find_vertex(v)596597cdef _glp_arc *a598599a = glp_add_arc(self.graph, i+1, j+1)600601if params is not None:602try:603if params.has_key("low"):604(<c_a_data *>a.data).low = params["low"]605if params.has_key("cap"):606(<c_a_data *>a.data).cap = params["cap"]607if params.has_key("cost"):608(<c_a_data *>a.data).cost = params["cost"]609except TypeError:610glp_del_arc(self.graph, a)611raise TypeError("Invalid edge parameter.")612613cpdef list add_edges(self, edges):614"""615Adds edges to the graph.616617INPUT:618619- ``edges`` -- An iterable container of pairs of the form ``(u, v)``,620where ``u`` is name (as ``str``) of the tail vertex and ``v`` is the621name (as ``str``) of the head vertex or an interable container of622triples of the form ``(u, v, params)`` where params is a ``dict`` as623described in ``add_edge``.624625EXAMPLE::626627sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend628sage: gbe = GLPKGraphBackend()629sage: edges = [("A", "B", {"low":0.0, "cap":10.0, "cost":5})]630sage: edges.append(("B", "C"))631sage: gbe.add_edges(edges)632sage: for ed in gbe.edges():633... print ed[0], ed[1], ed[2]['cap'], ed[2]['cost'], ed[2]['low']634A B 10.0 5.0 0.0635B C 0.0 0.0 0.0636sage: edges = [("C", "D", {"low":0.0, "cap":10.0, "cost":5})]637sage: edges.append(("C", "E", 5))638sage: gbe.add_edges(edges)639Traceback (most recent call last):640...641TypeError: Argument 'params' has incorrect type ...642sage: for ed in gbe.edges():643... print ed[0], ed[1], ed[2]['cap'], ed[2]['cost'], ed[2]['low']644A B 10.0 5.0 0.0645B C 0.0 0.0 0.0646C D 10.0 5.0 0.0647"""648for ed in edges:649self.add_edge(*ed)650651cpdef __add_edges_sage(self, g):652"""653Adds edges to the Graph.654655This function is only used when importing a ``GenericGraph``.656657EXAMPLE::658659sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend660sage: g = graphs.PappusGraph()661sage: for ed in g.edges():662... g.set_edge_label(ed[0], ed[1], {"cap":1})663sage: gbe = GLPKGraphBackend(g)664sage: gbe.maxflow_ffalg('1', '2')6653.0666"""667cdef _glp_arc* a668cdef int u669cdef int v670cdef char* u_name671cdef char* v_name672cdef double cost673cdef double cap674cdef double low675cdef int isdirected = g.is_directed()676677for (eu,ev,label) in g.edges():678s = str(eu)679u_name = s680s = str(ev)681v_name = s682u = glp_find_vertex(self.graph, u_name)683v = glp_find_vertex(self.graph, v_name)684if u < 1 or v < 1:685raise IndexError(u_name + " or " + v_name + " not found")686687a = glp_add_arc(self.graph, u, v)688689if isinstance(label, dict):690if label.has_key("cost"):691cost = label["cost"]692(<c_a_data *>a.data).cost = cost693if label.has_key("cap"):694cap = label["cap"]695(<c_a_data *>a.data).cap = cap696if label.has_key("low"):697low = label["low"]698(<c_a_data *>a.data).low = low699700if not isdirected:701a = glp_add_arc(self.graph, v, u)702if isinstance(label, dict):703if label.has_key("cost"):704(<c_a_data *>a.data).cost = cost705if label.has_key("cap"):706(<c_a_data *>a.data).cap = cap707if label.has_key("low"):708(<c_a_data *>a.data).low = low709710cpdef tuple get_edge(self, char* u, char* v):711"""712Returns an edge connecting two vertices.713714.. NOTE::715716If multiple edges connect the two vertices only the first edge717found is returned.718719INPUT:720721- ``u`` -- Name (as ``str``) of the tail vertex722- ``v`` -- Name (as ``str``) of the head vertex723724OUTPUT:725726A ``triple`` describing if edge was found or ``None`` if not. The third727value of the triple is a ``dict`` containing the following edge728parameters:729730* ``low`` -- The minimum flow through the edge731* ``cap`` -- The maximum capacity of the edge732* ``cost`` -- The cost of transporting one unit through the edge733* ``x`` -- The actual flow through the edge after solving734735EXAMPLE::736737sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend738sage: gbe = GLPKGraphBackend()739sage: edges = [("A", "B"), ("A", "C"), ("B", "C")]740sage: gbe.add_edges(edges)741sage: ed = gbe.get_edge("A", "B")742sage: print ed[0], ed[1], ed[2]['x']743A B 0.0744sage: gbe.get_edge("A", "F") is None745True746"""747cdef int i = self._find_vertex(u)748cdef int j = self._find_vertex(v)749750if i < 0 or j < 0:751return None752753cdef _glp_vertex* vert_u = self.graph.v[i+1]754cdef _glp_vertex* vert_v = self.graph.v[j+1]755cdef _glp_arc* a = vert_u.out756while a is not NULL:757if a.head == vert_v:758return (u, v, {"low":(<c_a_data *>a.data).low,759"cap":(<c_a_data *>a.data).cap,760"cost":(<c_a_data *>a.data).cost,761"x":(<c_a_data *>a.data).x})762a = a.t_next763764return None765766cpdef list edges(self):767"""768Returns a ``list`` of all edges in the graph769770OUTPUT:771772A ``list`` of ``triples`` representing the edges of the graph.773774EXAMPLE::775776sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend777sage: gbe = GLPKGraphBackend()778sage: edges = [("A", "B", {"low":0.0, "cap":10.0, "cost":5})]779sage: edges.append(("B", "C"))780sage: gbe.add_edges(edges)781sage: for ed in gbe.edges():782... print ed[0], ed[1], ed[2]['cost']783A B 5.0784B C 0.0785"""786787cdef int i = 1788cdef _glp_vertex* vert_u789cdef _glp_vertex* vert_v790cdef _glp_arc* a791edge_list = []792793while i <= self.graph.nv:794vert_u = self.graph.v[i]795a = vert_u.out796while a is not NULL:797vert_v = a.head798if vert_u.name is NULL:799u_name = None800else:801u_name = vert_u.name802if vert_v.name is NULL:803v_name = None804else:805v_name = vert_v.name806edge_list.append((u_name, v_name,807{"low":(<c_a_data *>a.data).low,808"cap":(<c_a_data *>a.data).cap,809"cost":(<c_a_data *>a.data).cost,810"x":(<c_a_data *>a.data).x}))811a = a.t_next812i += 1813return edge_list814815cpdef delete_vertex(self, char* vert):816r"""817Removes a vertex from the graph.818819Trying to delete a non existing vertex will raise an exception.820821INPUT:822823- ``vert`` -- The name (as ``str``) of the vertex to delete.824825EXAMPLE::826827sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend828sage: gbe = GLPKGraphBackend()829sage: verts = ["A", "D"]830sage: gbe.add_vertices(verts)831sage: gbe.delete_vertex("A")832sage: gbe.vertices()833['D']834sage: gbe.delete_vertex("A")835Traceback (most recent call last):836...837RuntimeError: Vertex A does not exist.838"""839840cdef int i = self._find_vertex(vert)841842if i < 0:843raise RuntimeError("Vertex %s does not exist."%(vert))844845cdef int * num = <int *> sage_malloc(2 * sizeof(int))846num[1] = i + 1847cdef int ndel = 1848849if not num:850raise MemoryError("Error allocating memory.")851852glp_del_vertices(self.graph, ndel, num)853sage_free(num)854855cpdef delete_vertices(self, list verts):856r"""857Removes vertices from the graph.858859Trying to delete a non existing vertex will raise an exception.860861INPUT:862863- ``verts`` -- iterable container containing names (as ``str``) of the864vertices to delete865866EXAMPLE::867868sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend869sage: gbe = GLPKGraphBackend()870sage: verts = ["A", "B", "C", "D"]871sage: gbe.add_vertices(verts)872sage: v_d = ["A", "B"]873sage: gbe.delete_vertices(v_d)874sage: gbe.vertices()875['C', 'D']876sage: gbe.delete_vertices(["C", "A"])877Traceback (most recent call last):878...879RuntimeError: Vertex A does not exist.880sage: gbe.vertices()881['C', 'D']882"""883884verts_val = [self._find_vertex(v) for v in verts]885if -1 in verts_val:886i = verts_val.index(-1)887raise RuntimeError("Vertex %s does not exist."%(verts[i]))888889cdef int * num = <int *> sage_malloc((len(verts_val)+1) * sizeof(int))890if not num:891raise MemoryError("Error allocating memory.")892cdef int ndel = len(verts_val)893894for i,(v) in enumerate(verts_val):895num[i+1] = v+1896897glp_del_vertices(self.graph, ndel, num)898899sage_free(num)900901cpdef delete_edge(self, char* u, char* v, dict params=None):902"""903Deletes an edge from the graph.904905If an edge does not exist it is ignored.906907INPUT:908909- ``u`` -- The name (as ``str``) of the tail vertex of the edge910- ``v`` -- The name (as ``str``) of the tail vertex of the edge911- ``params`` -- ``params`` -- An optional ``dict`` containing the edge912parameters (see meth:``add_edge``). If this parameter913is not provided, all edges connecting ``u`` and ``v`` are deleted.914Otherwise only edges with matching parameters are deleted.915916.. SEEALSO::917918:meth:`delete_edges`919920EXAMPLE::921922sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend923sage: gbe = GLPKGraphBackend()924sage: edges = [("A", "B", {"low":0.0, "cap":10.0, "cost":5})]925sage: edges.append(("A", "B", {"low":0.0, "cap":15.0, "cost":10}))926sage: edges.append(("B", "C", {"low":0.0, "cap":20.0, "cost":1}))927sage: edges.append(("B", "C", {"low":0.0, "cap":10.0, "cost":20}))928sage: gbe.add_edges(edges)929sage: gbe.delete_edge("A", "B")930sage: gbe.delete_edge("B", "C", {"low":0.0, "cap":10.0, "cost":20})931sage: print gbe.edges()[0][0], gbe.edges()[0][1], gbe.edges()[0][2]['cost']932B C 1.0933"""934935cdef int i = self._find_vertex(u)936cdef int j = self._find_vertex(v)937if i < 0 or j < 0:938return939940cdef _glp_vertex* vert_u = self.graph.v[i+1]941cdef _glp_vertex* vert_v = self.graph.v[j+1]942cdef _glp_arc* a = vert_u.out943cdef _glp_arc* a2 = a944945cdef double low, cap, cost, x946947if params is not None:948if params.has_key("low"):949low = params["low"]950if params.has_key("cap"):951cap = params["cap"]952if params.has_key("cost"):953cost = params["cost"]954if params.has_key("x"):955x = params["x"]956957while a is not NULL:958a2 = a.t_next959if a.head == vert_v and params is None:960glp_del_arc(self.graph, a)961elif a.head == vert_v:962del_it = True963if params.has_key("low"):964if (<c_a_data *>a.data).low != low:965del_it = False966if params.has_key("cap"):967if (<c_a_data *>a.data).cap != cap:968del_it = False969if params.has_key("cost"):970if (<c_a_data *>a.data).cost != cost:971del_it = False972if params.has_key("x"):973if (<c_a_data *>a.data).x != x:974del_it = False975if del_it:976glp_del_arc(self.graph, a)977978a = a2979980def delete_edges(self, edges):981"""982Deletes edges from the graph.983984Non existing edges are ignored.985986INPUT:987988- ``edges`` -- An iterable container of edges.989990.. SEEALSO::991992:meth:`delete_edge`993994EXAMPLE::995996sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend997sage: gbe = GLPKGraphBackend()998sage: edges = [("A", "B", {"low":0.0, "cap":10.0, "cost":5})]999sage: edges.append(("A", "B", {"low":0.0, "cap":15.0, "cost":10}))1000sage: edges.append(("B", "C", {"low":0.0, "cap":20.0, "cost":1}))1001sage: edges.append(("B", "C", {"low":0.0, "cap":10.0, "cost":20}))1002sage: gbe.add_edges(edges)1003sage: gbe.delete_edges(edges[1:])1004sage: len(gbe.edges())100511006sage: print gbe.edges()[0][0], gbe.edges()[0][1], gbe.edges()[0][2]['cap']1007A B 10.01008"""10091010for edge in edges:1011self.delete_edge(*edge)10121013cpdef int _find_vertex(self, char *name):1014"""1015Returns the index of a vertex specified by a name10161017INPUT:10181019- ``name`` -- Name of the vertex10201021OUTPUT:10221023The index of the vertex or ``-1`` if the vertex is not found10241025EXAMPLE::10261027sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend1028sage: gbe = GLPKGraphBackend()1029sage: verts = ["A", "B", "C", "D"]1030sage: gbe.add_vertices(verts)1031sage: gbe._find_vertex("A")103201033sage: gbe._find_vertex("F")1034-11035"""10361037glp_create_v_index(self.graph)1038return glp_find_vertex(self.graph, name) - 110391040cpdef int write_graph(self, char * fname):1041r"""1042Writes the graph to a plain text file10431044INPUT:10451046- ``fname`` -- full name of the file10471048OUTPUT:10491050Zero if the operations was successful otherwise nonzero10511052EXAMPLE::10531054sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend1055sage: gbe = GLPKGraphBackend()1056sage: a = gbe.add_edge("0", "1")1057sage: gbe.write_graph(SAGE_TMP+"/graph.txt")1058Writing graph to ...10592 lines were written106001061"""10621063return glp_write_graph(self.graph, fname)10641065cpdef int write_ccdata(self, char * fname):1066r"""1067Writes the graph to a text file in DIMACS format.10681069Writes the data to plain ASCII text file in DIMACS format.1070A discription of the DIMACS format can be found at1071http://dimacs.rutgers.edu/Challenges/.10721073INPUT:10741075- ``fname`` -- full name of the file10761077OUTPUT:10781079Zero if the operations was successful otherwise nonzero10801081EXAMPLE::10821083sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend1084sage: gbe = GLPKGraphBackend()1085sage: a = gbe.add_edge("0", "1")1086sage: gbe.write_ccdata(SAGE_TMP+"/graph.dat")1087Writing graph to ...10886 lines were written108901090"""10911092return glp_write_ccdata(self.graph, 0, fname)10931094cpdef int write_mincost(self, char * fname):1095"""1096Writes the mincost flow problem data to a text file in DIMACS format10971098INPUT:10991100- ``fname`` -- Full name of file11011102OUTPUT:11031104Zero if successful, otherwise nonzero11051106EXAMPLE::11071108sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend1109sage: gbe = GLPKGraphBackend()1110sage: a = gbe.add_edge("0", "1")1111sage: gbe.write_mincost(SAGE_TMP+"/graph.min")1112Writing min-cost flow problem data to ...11134 lines were written111401115"""11161117return glp_write_mincost(self.graph, 0, 0, sizeof(double),1118sizeof(double) + sizeof(double), fname)11191120cpdef double mincost_okalg(self) except -1:1121r"""1122Finds solution to the mincost problem with the out-of-kilter algorithm.11231124The out-of-kilter algorithm requires all problem data to be integer1125valued.11261127OUTPUT:11281129The solution to the mincost problem, i.e. the total cost, if operation1130was successful.11311132.. NOTE::11331134This method raises ``MIPSolverException`` exceptions when1135the solution can not be computed for any reason (none1136exists, or the LP solver was not able to find it, etc...)11371138EXAMPLE::11391140sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend1141sage: gbe = GLPKGraphBackend()1142sage: vertices = (35, 50, 40, -45, -20, -30, -30)1143sage: vs = gbe.add_vertices([None for i in range(len(vertices))])1144sage: v_dict = {}1145sage: for i, v in enumerate(vs):1146... v_dict[v] = vertices[i]1147sage: gbe.set_vertices_demand(v_dict.items())1148sage: cost = ((8, 6, 10, 9), (9, 12, 13, 7), (14, 9, 16, 5))1149sage: lcost = range(len(cost))1150sage: lcost_0 = range(len(cost[0]))1151sage: for i in lcost:1152... for j in lcost_0:1153... gbe.add_edge(str(i), str(j + len(cost)), {"cost":cost[i][j], "cap":100})1154sage: gbe.mincost_okalg()11551020.01156sage: for ed in gbe.edges():1157... print ed[0], "->", ed[1], ed[2]["x"]11580 -> 6 0.011590 -> 5 25.011600 -> 4 10.011610 -> 3 0.011621 -> 6 0.011631 -> 5 5.011641 -> 4 0.011651 -> 3 45.011662 -> 6 30.011672 -> 5 0.011682 -> 4 10.011692 -> 3 0.01170"""1171cdef double graph_sol1172cdef int status = glp_mincost_okalg(self.graph, 0, 0, sizeof(double),11732 * sizeof(double), &graph_sol,11743 * sizeof(double), sizeof(double))1175if status == 0:1176pass1177elif status == GLP_ENOPFS:1178raise MIPSolverException("No (primal) feasible solution exists")1179elif status == GLP_EDATA:1180raise MIPSolverException("Unable to start search due to " +1181"problem data")1182elif status == GLP_ERANGE:1183raise MIPSolverException("The search was prematurely terminated " +1184"because of integer overflow")1185elif status == GLP_EFAIL:1186raise MIPSolverException("An error has been detected" +1187"in the program logic")11881189return graph_sol11901191cpdef int write_maxflow(self, char * fname) except -1:1192"""1193Writes the maximum flow problem data to a text file in DIMACS format.11941195INPUT:11961197- ``fname`` -- Full name of file11981199OUTPUT:12001201``Zero`` if successful, otherwise ``non-zero``12021203EXAMPLE::12041205sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend1206sage: gbe = GLPKGraphBackend()1207sage: gbe.add_vertices([None for i in range(2)])1208['0', '1']1209sage: a = gbe.add_edge('0', '1')1210sage: gbe.maxflow_ffalg('0', '1')12110.01212sage: gbe.write_maxflow(SAGE_TMP+"/graph.max")1213Writing maximum flow problem data to ...12146 lines were written121501216sage: gbe = GLPKGraphBackend()1217sage: gbe.write_maxflow(SAGE_TMP+"/graph.max")1218Traceback (most recent call last):1219...1220IOError: Cannot write empty graph1221"""12221223if self.graph.nv <= 0:1224raise IOError("Cannot write empty graph")12251226return glp_write_maxflow(self.graph, self.s+1, self.t+1,1227sizeof(double), fname)12281229cpdef double maxflow_ffalg(self, u=None, v=None) except -1:1230r"""1231Finds solution to the maxflow problem with Ford-Fulkerson algorithm.12321233INPUT:12341235- ``u`` -- Name (as ``str``) of the tail vertex. Default is ``None``.1236- ``v`` -- Name (as ``str``) of the head vertex. Default is ``None``.12371238If ``u`` or ``v`` are ``None``, the currently stored values for the1239head or tail vertex are used. This behavior is useful when reading1240maxflow data from a file. When calling this function with values for1241``u`` and ``v``, the head and tail vertex are stored for1242later use.12431244OUTPUT:12451246The solution to the maxflow problem, i.e. the maximum flow.12471248.. NOTE::12491250* If the source or sink vertex does not exist, an ``IndexError`` is1251raised.12521253* If the source and sink are identical, a ``ValueError`` is raised.12541255* This method raises ``MIPSolverException`` exceptions when the1256solution can not be computed for any reason (none exists, or the1257LP solver was not able to find it, etc...)12581259EXAMPLE::12601261sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend1262sage: gbe = GLPKGraphBackend()1263sage: v = gbe.add_vertices([None for i in range(5)])1264sage: edges = ((0, 1, 2), (0, 2, 3), (1, 2, 3), (1, 3, 4),1265... (3, 4, 1), (2, 4, 2))1266sage: for a in edges:1267... edge = gbe.add_edge(str(a[0]), str(a[1]), {"cap":a[2]})1268sage: gbe.maxflow_ffalg('0', '4')12693.01270sage: gbe.maxflow_ffalg()12713.01272sage: gbe.maxflow_ffalg('0', '8')1273Traceback (most recent call last):1274...1275IndexError: Source or sink vertex does not exist1276"""1277cdef int s, t12781279if u is not None and v is not None:1280s = self._find_vertex(u)1281t = self._find_vertex(v)1282else:1283s = self.s1284t = self.t12851286if s < 0 or t < 0:1287raise IndexError("Source or sink vertex does not exist")1288if s == t:1289raise ValueError ("Source and sink are identical")12901291self.s = s1292self.t = t12931294s += 11295t += 112961297cdef double graph_sol1298cdef int status = glp_maxflow_ffalg(self.graph, s, t, sizeof(double),1299&graph_sol, 3 * sizeof(double),13004 * sizeof(double))1301if status == 0:1302pass1303elif status == GLP_ENOPFS:1304raise MIPSolverException("No (primal) feasible solution exists")1305elif status == GLP_EDATA:1306raise MIPSolverException("Unable to start search due " +1307"to problem data")1308elif status == GLP_ERANGE:1309raise MIPSolverException("The search was prematurely terminated " +1310"because of integer overflow")1311elif status == GLP_EFAIL:1312raise MIPSolverException("An error has been detected in the " +1313"program logic")13141315return graph_sol13161317cpdef double cpp(self):1318r"""1319Solves the critical path problem of a project network.13201321OUTPUT:13221323The length of the critical path of the network13241325EXAMPLE::13261327sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend1328sage: gbe = GLPKGraphBackend()1329sage: gbe.add_vertices([None for i in range(3)])1330['0', '1', '2']1331sage: gbe.set_vertex_demand('0', 3)1332sage: gbe.set_vertex_demand('1', 1)1333sage: gbe.set_vertex_demand('2', 4)1334sage: a = gbe.add_edge('0', '2')1335sage: a = gbe.add_edge('1', '2')1336sage: gbe.cpp()13377.01338sage: v = gbe.get_vertex('1')1339sage: print 1, v["rhs"], v["es"], v["ls"] # abs tol 1e-613401 1.0 0.0 2.01341"""13421343return glp_cpp(self.graph, 0, 2 * sizeof(double),13443 * sizeof(double))13451346def __dealloc__(self):1347"""1348Destructor1349"""1350if self.graph is not NULL:1351glp_delete_graph(self.graph)1352self.graph = NULL135313541355