Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/numerical/backends/glpk_graph_backend.pyx
8817 views
1
"""
2
GLPK Backend for access to GLPK graph functions
3
4
AUTHORS:
5
6
- Christian Kuper (2012-11): Initial implementation
7
8
Methods index
9
-------------
10
11
**Graph creation and modification operations:**
12
13
.. csv-table::
14
:class: contentstable
15
:widths: 30, 70
16
:delim: |
17
18
:meth:`~GLPKGraphBackend.add_vertex` | Adds an isolated vertex to the graph.
19
:meth:`~GLPKGraphBackend.add_vertices` | Adds vertices from an iterable container of vertices.
20
:meth:`~GLPKGraphBackend.set_vertex_demand` | Sets the vertex parameters.
21
:meth:`~GLPKGraphBackend.set_vertices_demand` | Sets the parameters of selected vertices.
22
:meth:`~GLPKGraphBackend.get_vertex` | Returns a specific vertex as a ``dict`` Object.
23
:meth:`~GLPKGraphBackend.get_vertices` | Returns a dictionary of the dictonaries associated to each vertex.
24
:meth:`~GLPKGraphBackend.vertices` | Returns a ``list`` of all vertices.
25
:meth:`~GLPKGraphBackend.delete_vertex` | Removes a vertex from the graph.
26
:meth:`~GLPKGraphBackend.delete_vertices` | Removes vertices from the graph.
27
:meth:`~GLPKGraphBackend.add_edge` | Adds an edge between vertices ``u`` and ``v``.
28
:meth:`~GLPKGraphBackend.add_edges` | Adds edges to the graph.
29
:meth:`~GLPKGraphBackend.get_edge` | Returns an edge connecting two vertices.
30
:meth:`~GLPKGraphBackend.edges` | Returns a ``list`` of all edges in the graph.
31
:meth:`~GLPKGraphBackend.delete_edge` | Deletes an edge from the graph.
32
:meth:`~GLPKGraphBackend.delete_edges` | Deletes edges from the graph.
33
34
**Graph writing operations:**
35
36
.. csv-table::
37
:class: contentstable
38
:widths: 30, 70
39
:delim: |
40
41
:meth:`~GLPKGraphBackend.write_graph` | Writes the graph to a plain text file.
42
:meth:`~GLPKGraphBackend.write_ccdata` | Writes the graph to a text file in DIMACS format.
43
:meth:`~GLPKGraphBackend.write_mincost` | Writes the mincost flow problem data to a text file in DIMACS format.
44
:meth:`~GLPKGraphBackend.write_maxflow` | Writes the maximum flow problem data to a text file in DIMACS format.
45
46
**Network optimization operations:**
47
48
.. csv-table::
49
:class: contentstable
50
:widths: 30, 70
51
:delim: |
52
53
:meth:`~GLPKGraphBackend.mincost_okalg` | Finds solution to the mincost problem with the out-of-kilter algorithm.
54
:meth:`~GLPKGraphBackend.maxflow_ffalg` | Finds solution to the maxflow problem with Ford-Fulkerson algorithm.
55
:meth:`~GLPKGraphBackend.cpp` | Solves the critical path problem of a project network.
56
57
Classes and methods
58
-------------------
59
"""
60
61
##############################################################################
62
# Copyright (C) 2012 Christian Kuper <[email protected]>
63
# Distributed under the terms of the GNU General Public License (GPL)
64
# The full text of the GPL is available at:
65
# http://www.gnu.org/licenses/
66
##############################################################################
67
68
from sage.numerical.mip import MIPSolverException
69
70
include "sage/ext/interrupt.pxi"
71
72
cdef class GLPKGraphBackend(object):
73
"""
74
GLPK Backend for access to GLPK graph functions
75
76
The constructor can either be called without arguments (which results in an
77
empty graph) or with arguments to read graph data from a file.
78
79
INPUT:
80
81
- ``data`` -- a filename or a :class:`Graph` object.
82
83
- ``format`` -- when ``data`` is a filename, specifies the format of the
84
data read from a file. The ``format`` parameter is a string and can take
85
values as described in the table below.
86
87
**Format parameters:**
88
89
.. list-table::
90
:widths: 10 70
91
92
* - ``plain``
93
94
- Read data from a plain text file containing the following information:
95
96
| nv na
97
| i[1] j[1]
98
| i[2] j[2]
99
| . . .
100
| i[na] j[na]
101
102
where:
103
104
* nv is the number of vertices (nodes);
105
106
* na is the number of arcs;
107
108
* i[k], k = 1, . . . , na, is the index of tail vertex of arc k;
109
110
* j[k], k = 1, . . . , na, is the index of head vertex of arc k.
111
112
113
* - ``dimacs``
114
115
- Read data from a plain ASCII text file in DIMACS format.
116
A discription of the DIMACS format can be found at
117
http://dimacs.rutgers.edu/Challenges/.
118
119
* - ``mincost``
120
121
- Reads the mincost flow problem data from a text file in DIMACS format
122
123
* - ``maxflow``
124
125
- Reads the maximum flow problem data from a text file in DIMACS format
126
127
.. NOTE::
128
129
When ``data`` is a :class:`Graph`, the following restrictions are
130
applied.
131
132
* vertices -- the value of the demand of each vertex (see
133
:meth:`set_vertex_demand`) is obtained from the numerical
134
value associated with the key "rhs" if it is a dictionary.
135
136
* edges -- The edge values used in the algorithms are read from the
137
edges labels (and left undefined if the edge labels are equal to
138
``None``). To be defined, the labels must be ``dict`` objects with
139
keys "low", "cap" and "cost". See :meth:`get_edge` for details.
140
141
EXAMPLES:
142
143
The following example creates an empty graph::
144
145
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
146
sage: gbe = GLPKGraphBackend()
147
148
The following example creates an empty graph, adds some data, saves the data
149
to a file and loads it::
150
151
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
152
sage: gbe = GLPKGraphBackend()
153
sage: gbe.add_vertices([None, None])
154
['0', '1']
155
sage: a = gbe.add_edge('0', '1')
156
sage: gbe.write_graph(SAGE_TMP+"/graph.txt")
157
Writing graph to ...
158
2 lines were written
159
0
160
sage: gbe1 = GLPKGraphBackend(SAGE_TMP+"/graph.txt", "plain")
161
Reading graph from ...
162
Graph has 2 vertices and 1 arc
163
2 lines were read
164
165
The following example imports a Sage ``Graph`` and then uses it to solve a
166
maxflow problem::
167
168
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
169
sage: g = graphs.PappusGraph()
170
sage: for ed in g.edges():
171
... g.set_edge_label(ed[0], ed[1], {"cap":1})
172
sage: gbe = GLPKGraphBackend(g)
173
sage: gbe.maxflow_ffalg('1', '2')
174
3.0
175
"""
176
177
def __cinit__(self, data = None, format = "plain"):
178
"""
179
Constructor
180
181
The constructor can either be called without arguments creating an empty
182
graph or with arguments to read graph data from a file or a Sage
183
:class:`Graph`. See documentation of :class:`GLPKGraphBackend` for
184
details.
185
186
EXAMPLE::
187
188
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
189
sage: gbe = GLPKGraphBackend()
190
"""
191
192
from sage.graphs.graph import Graph
193
194
self.graph = <_glp_graph*> glp_create_graph(sizeof(c_v_data),
195
sizeof(c_a_data))
196
197
if self.graph is NULL:
198
raise MemoryError("Error allocating memory.")
199
200
self.s = 1
201
self.t = 1
202
203
if isinstance(data,str):
204
fname = data
205
res = 0
206
if format == "plain":
207
res = glp_read_graph(self.graph, fname)
208
elif format == "dimacs":
209
res = glp_read_ccdata(self.graph, 0, fname)
210
elif format == "mincost":
211
res = glp_read_mincost(self.graph, 0, 0, sizeof(double),
212
sizeof(double) + sizeof(double), fname)
213
elif format == "maxflow":
214
res = glp_read_maxflow(self.graph, &self.s, &self.t,
215
sizeof(double), fname)
216
if res != 0:
217
raise IOError("Could not read graph from file %s" % (fname))
218
219
elif isinstance(data, Graph):
220
self.__add_vertices_sage(data)
221
self.__add_edges_sage(data)
222
else:
223
ValueError("Input data is not supported")
224
225
cpdef add_vertex(self, char * name = NULL):
226
"""
227
Adds an isolated vertex to the graph.
228
229
If the vertex already exists, nothing is done.
230
231
INPUT:
232
233
- ``name`` -- ``String`` of max 255 chars length. If no name is
234
specified, then the vertex will be represented by the string
235
representation of the ID of the vertex or - if this already exists -
236
a string representation of the least integer not already representing
237
a vertex.
238
239
OUTPUT:
240
241
If no ``name`` is passed as an argument, the new vertex name is
242
returned. ``None`` otherwise.
243
244
EXAMPLE::
245
246
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
247
sage: gbe = GLPKGraphBackend()
248
sage: gbe.add_vertex()
249
'0'
250
sage: gbe.add_vertex("2")
251
sage: gbe.add_vertex()
252
'1'
253
"""
254
cdef int n
255
cdef vn_t = 0
256
cdef char* c_name
257
258
if name is not NULL and self._find_vertex(name) >= 0:
259
return None
260
261
cdef int vn = glp_add_vertices(self.graph, 1)
262
263
if name is not NULL:
264
glp_set_vertex_name(self.graph, vn, name)
265
return None
266
267
else:
268
s = str(vn-1)
269
c_name = s
270
n = self._find_vertex(c_name)
271
272
# This is costly, but hopefully will not happen often.
273
while n >= 0:
274
vn_t += 1
275
s = str(vn_t-1)
276
c_name = s
277
n = self._find_vertex(c_name)
278
279
glp_set_vertex_name(self.graph, vn, c_name)
280
return c_name
281
282
cpdef __add_vertices_sage(self, g):
283
"""
284
Adds vertices to the GLPK Graph.
285
286
This function is only used when importing a
287
:class:`~sage.graphs.generic_graph.GenericGraph` object.
288
289
EXAMPLE::
290
291
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
292
sage: g = graphs.PappusGraph()
293
sage: for ed in g.edges():
294
... g.set_edge_label(ed[0], ed[1], {"cap":1})
295
sage: gbe = GLPKGraphBackend(g)
296
sage: gbe.maxflow_ffalg('1', '2')
297
3.0
298
"""
299
cdef int n
300
cdef int i
301
cdef double rhs
302
cdef _glp_vertex* vert
303
cdef char* name
304
305
verts = g.vertices()
306
n = len(verts)
307
if n < 1:
308
raise ValueError("Graph must contain vertices")
309
310
glp_add_vertices(self.graph, n)
311
312
for i in range(n):
313
vert = self.graph.v[i+1]
314
s = str(verts[i])
315
name = s
316
glp_set_vertex_name(self.graph, i+1, name)
317
318
if g.get_vertex(verts[i]) is not None:
319
try:
320
(<c_v_data *>vert.data).rhs = g.get_vertex(verts[i])["rhs"]
321
except AttributeError:
322
pass
323
324
glp_create_v_index(self.graph)
325
326
cpdef list add_vertices(self, vertices):
327
"""
328
Adds vertices from an iterable container of vertices.
329
330
Vertices that already exist in the graph will not be added again.
331
332
INPUT:
333
334
- ``vertices`` -- iterator of vertex labels (``str``). A label can be
335
``None``.
336
337
OUTPUT:
338
339
Generated names of new vertices if there is at least one ``None`` value
340
present in ``vertices``. ``None`` otherwise.
341
342
EXAMPLE::
343
344
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
345
sage: gbe = GLPKGraphBackend()
346
sage: vertices = [None for i in range(3)]
347
sage: gbe.add_vertices(vertices)
348
['0', '1', '2']
349
sage: gbe.add_vertices(['A', 'B', None])
350
['5']
351
sage: gbe.add_vertices(['A', 'B', 'C'])
352
sage: gbe.vertices()
353
['0', '1', '2', 'A', 'B', '5', 'C']
354
355
TESTS::
356
357
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
358
sage: gbe = GLPKGraphBackend()
359
sage: gbe.add_vertices([None, None, None, '1'])
360
['0', '2', '3']
361
"""
362
363
# We do not want to have [None,None,None,1] as input as a vertex named
364
# "1" would be created twice (a first time when adding a 'None' vertex,
365
# and and a second time when reading the last item of the list).
366
nonecount = 0
367
for v in vertices:
368
if v is None:
369
nonecount += 1
370
else:
371
self.add_vertex(v)
372
373
if nonecount:
374
return [self.add_vertex() for i in range(nonecount)]
375
else:
376
return None
377
378
cpdef set_vertex_demand(self, char* vertex, demand):
379
"""
380
Sets the demand of the vertex in a mincost flow algorithm.
381
382
INPUT:
383
384
- ``vertex`` -- Name of the vertex
385
386
- ``demand`` -- the numerical value representing demand of the vertex in
387
a mincost flow alorithm (it could be for instance `-1` to represent a
388
sink, or `1` to represent a source and `0` for a neutral vertex). This
389
can either be an ``int`` or ``float`` value.
390
391
EXAMPLE::
392
393
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
394
sage: gbe = GLPKGraphBackend()
395
sage: vertices = [None for i in range(3)]
396
sage: gbe.add_vertices(vertices)
397
['0', '1', '2']
398
sage: gbe.set_vertex_demand('0', 2)
399
sage: gbe.get_vertex('0')['rhs']
400
2.0
401
sage: gbe.set_vertex_demand('3', 2)
402
Traceback (most recent call last):
403
...
404
KeyError: 'Vertex 3 does not exist.'
405
"""
406
cdef int n = self._find_vertex(vertex)
407
408
if n < 0:
409
raise KeyError("Vertex " + vertex + " does not exist.")
410
411
cdef _glp_vertex* vert = self.graph.v[n+1]
412
cdef double val = demand
413
(<c_v_data *>vert.data).rhs = val
414
415
cpdef set_vertices_demand(self, list pairs):
416
"""
417
Sets the parameters of selected vertices.
418
419
INPUT:
420
421
- ``pairs`` -- A list of pairs ``(vertex, demand)`` associating a demand
422
to each vertex. For more information, see the documentation of
423
:meth:`set_vertex_demand`.
424
425
EXAMPLE::
426
427
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
428
sage: gbe = GLPKGraphBackend()
429
sage: vertices = [None for i in range(3)]
430
sage: gbe.add_vertices(vertices)
431
['0', '1', '2']
432
sage: gbe.set_vertices_demand([('0', 2), ('1', 3), ('3', 4)])
433
sage: sorted(gbe.get_vertex('1').items())
434
[('cut', 0), ('es', 0.0), ('ls', 0.0), ('pi', 0.0), ('rhs', 3.0)]
435
"""
436
437
for v, param in pairs:
438
try:
439
self.set_vertex_demand(v, param)
440
except KeyError:
441
pass
442
443
cpdef dict get_vertex(self, char* vertex):
444
"""
445
Returns a specific vertex as a ``dict`` Object.
446
447
INPUT:
448
449
- ``vertex`` -- The vertex label as ``str``.
450
451
OUTPUT:
452
453
The vertex as a ``dict`` object or ``None`` if the vertex does not
454
exist. The ``dict`` contains the values used or created by the different
455
algorithms. The values associated with the keys following keys contain:
456
457
* "rhs" -- The supply / demand value the vertex (mincost alg)
458
* "pi" -- The node potential (mincost alg)
459
* "cut" -- The cut flag of the vertex (maxflow alg)
460
* "es" -- The earliest start of task (cpp alg)
461
* "ls" -- The latest start of task (cpp alg)
462
463
EXAMPLE::
464
465
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
466
sage: gbe = GLPKGraphBackend()
467
sage: verts = ["A", "B", "C", "D"]
468
sage: gbe.add_vertices(verts)
469
sage: sorted(gbe.get_vertex("A").items())
470
[('cut', 0), ('es', 0.0), ('ls', 0.0), ('pi', 0.0), ('rhs', 0.0)]
471
sage: gbe.get_vertex("F") is None
472
True
473
"""
474
475
cdef int i = self._find_vertex(vertex)
476
if i < 0:
477
return None
478
479
cdef _glp_vertex* vert = self.graph.v[i+1]
480
cdef c_v_data * vdata = <c_v_data *> vert.data
481
482
return {
483
"rhs" : vdata.rhs,
484
"pi" : vdata.pi,
485
"cut" : vdata.cut,
486
"es" : vdata.es,
487
"ls" : vdata.ls
488
}
489
490
cpdef dict get_vertices(self, verts):
491
"""
492
Returns a dictionary of the dictonaries associated to each vertex.
493
494
INPUT:
495
496
- ``verts`` -- iterable container of vertices
497
498
OUTPUT:
499
500
A list of pairs ``(vertex, properties)`` where ``properties`` is a
501
dictionary containing the numerical values associated with a vertex. For
502
more information, see the documentation of
503
:meth:`GLPKGraphBackend.get_vertex`.
504
505
EXAMPLE::
506
507
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
508
sage: gbe = GLPKGraphBackend()
509
sage: verts = ['A', 'B']
510
sage: gbe.add_vertices(verts)
511
sage: sorted(gbe.get_vertices(verts)['B'].items())
512
[('cut', 0), ('es', 0.0), ('ls', 0.0), ('pi', 0.0), ('rhs', 0.0)]
513
sage: gbe.get_vertices(["C", "D"])
514
{}
515
"""
516
vl = [(v, self.get_vertex(v)) for v in verts]
517
return dict([(v, p) for v, p in vl if p is not None])
518
519
cpdef list vertices(self):
520
"""
521
Returns the list of all vertices
522
523
.. NOTE::
524
525
Changing elements of the ``list`` will not change anything in the
526
the graph.
527
528
.. NOTE::
529
530
If a vertex in the graph does not have a name / label it will appear
531
as ``None`` in the resulting ``list``.
532
533
EXAMPLE::
534
535
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
536
sage: gbe = GLPKGraphBackend()
537
sage: verts = ["A", "B", "C"]
538
sage: gbe.add_vertices(verts)
539
sage: a = gbe.vertices(); a
540
['A', 'B', 'C']
541
sage: a.pop(0)
542
'A'
543
sage: gbe.vertices()
544
['A', 'B', 'C']
545
"""
546
547
return [self.graph.v[i+1].name if self.graph.v[i+1].name is not NULL
548
else None for i in range(self.graph.nv)]
549
550
cpdef add_edge(self, char* u, char* v, dict params=None):
551
"""
552
Adds an edge between vertices ``u`` and ``v``.
553
554
Allows adding an edge and optionally providing parameters used by the
555
algorithms. If a vertex does not exist it is created.
556
557
INPUT:
558
559
- ``u`` -- The name (as ``str``) of the tail vertex
560
561
- ``v`` -- The name (as ``str``) of the head vertex
562
563
- ``params`` -- An optional ``dict`` containing the edge parameters used
564
for the algorithms. The following keys are used:
565
566
* ``low`` -- The minimum flow through the edge
567
568
* ``cap`` -- The maximum capacity of the edge
569
570
* ``cost`` -- The cost of transporting one unit through the edge
571
572
EXAMPLE::
573
574
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
575
sage: gbe = GLPKGraphBackend()
576
sage: gbe.add_edge("A", "B", {"low":0.0, "cap":10.0, "cost":5})
577
sage: gbe.vertices()
578
['A', 'B']
579
sage: for ed in gbe.edges():
580
... print ed[0], ed[1], ed[2]['cap'], ed[2]['cost'], ed[2]['low']
581
A B 10.0 5.0 0.0
582
sage: gbe.add_edge("B", "C", {"low":0.0, "cap":10.0, "cost":'5'})
583
Traceback (most recent call last):
584
...
585
TypeError: Invalid edge parameter.
586
"""
587
cdef int i = self._find_vertex(u)
588
cdef int j = self._find_vertex(v)
589
590
if i < 0:
591
self.add_vertex(u)
592
i = self._find_vertex(u)
593
594
if j < 0:
595
self.add_vertex(v)
596
j = self._find_vertex(v)
597
598
cdef _glp_arc *a
599
600
a = glp_add_arc(self.graph, i+1, j+1)
601
602
if params is not None:
603
try:
604
if params.has_key("low"):
605
(<c_a_data *>a.data).low = params["low"]
606
if params.has_key("cap"):
607
(<c_a_data *>a.data).cap = params["cap"]
608
if params.has_key("cost"):
609
(<c_a_data *>a.data).cost = params["cost"]
610
except TypeError:
611
glp_del_arc(self.graph, a)
612
raise TypeError("Invalid edge parameter.")
613
614
cpdef list add_edges(self, edges):
615
"""
616
Adds edges to the graph.
617
618
INPUT:
619
620
- ``edges`` -- An iterable container of pairs of the form ``(u, v)``,
621
where ``u`` is name (as ``str``) of the tail vertex and ``v`` is the
622
name (as ``str``) of the head vertex or an interable container of
623
triples of the form ``(u, v, params)`` where params is a ``dict`` as
624
described in ``add_edge``.
625
626
EXAMPLE::
627
628
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
629
sage: gbe = GLPKGraphBackend()
630
sage: edges = [("A", "B", {"low":0.0, "cap":10.0, "cost":5})]
631
sage: edges.append(("B", "C"))
632
sage: gbe.add_edges(edges)
633
sage: for ed in gbe.edges():
634
... print ed[0], ed[1], ed[2]['cap'], ed[2]['cost'], ed[2]['low']
635
A B 10.0 5.0 0.0
636
B C 0.0 0.0 0.0
637
sage: edges = [("C", "D", {"low":0.0, "cap":10.0, "cost":5})]
638
sage: edges.append(("C", "E", 5))
639
sage: gbe.add_edges(edges)
640
Traceback (most recent call last):
641
...
642
TypeError: Argument 'params' has incorrect type ...
643
sage: for ed in gbe.edges():
644
... print ed[0], ed[1], ed[2]['cap'], ed[2]['cost'], ed[2]['low']
645
A B 10.0 5.0 0.0
646
B C 0.0 0.0 0.0
647
C D 10.0 5.0 0.0
648
"""
649
for ed in edges:
650
self.add_edge(*ed)
651
652
cpdef __add_edges_sage(self, g):
653
"""
654
Adds edges to the Graph.
655
656
This function is only used when importing a ``GenericGraph``.
657
658
EXAMPLE::
659
660
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
661
sage: g = graphs.PappusGraph()
662
sage: for ed in g.edges():
663
... g.set_edge_label(ed[0], ed[1], {"cap":1})
664
sage: gbe = GLPKGraphBackend(g)
665
sage: gbe.maxflow_ffalg('1', '2')
666
3.0
667
"""
668
cdef _glp_arc* a
669
cdef int u
670
cdef int v
671
cdef char* u_name
672
cdef char* v_name
673
cdef double cost
674
cdef double cap
675
cdef double low
676
cdef int isdirected = g.is_directed()
677
678
for (eu,ev,label) in g.edges():
679
s = str(eu)
680
u_name = s
681
s = str(ev)
682
v_name = s
683
u = glp_find_vertex(self.graph, u_name)
684
v = glp_find_vertex(self.graph, v_name)
685
if u < 1 or v < 1:
686
raise IndexError(u_name + " or " + v_name + " not found")
687
688
a = glp_add_arc(self.graph, u, v)
689
690
if isinstance(label, dict):
691
if label.has_key("cost"):
692
cost = label["cost"]
693
(<c_a_data *>a.data).cost = cost
694
if label.has_key("cap"):
695
cap = label["cap"]
696
(<c_a_data *>a.data).cap = cap
697
if label.has_key("low"):
698
low = label["low"]
699
(<c_a_data *>a.data).low = low
700
701
if not isdirected:
702
a = glp_add_arc(self.graph, v, u)
703
if isinstance(label, dict):
704
if label.has_key("cost"):
705
(<c_a_data *>a.data).cost = cost
706
if label.has_key("cap"):
707
(<c_a_data *>a.data).cap = cap
708
if label.has_key("low"):
709
(<c_a_data *>a.data).low = low
710
711
cpdef tuple get_edge(self, char* u, char* v):
712
"""
713
Returns an edge connecting two vertices.
714
715
.. NOTE::
716
717
If multiple edges connect the two vertices only the first edge
718
found is returned.
719
720
INPUT:
721
722
- ``u`` -- Name (as ``str``) of the tail vertex
723
- ``v`` -- Name (as ``str``) of the head vertex
724
725
OUTPUT:
726
727
A ``triple`` describing if edge was found or ``None`` if not. The third
728
value of the triple is a ``dict`` containing the following edge
729
parameters:
730
731
* ``low`` -- The minimum flow through the edge
732
* ``cap`` -- The maximum capacity of the edge
733
* ``cost`` -- The cost of transporting one unit through the edge
734
* ``x`` -- The actual flow through the edge after solving
735
736
EXAMPLE::
737
738
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
739
sage: gbe = GLPKGraphBackend()
740
sage: edges = [("A", "B"), ("A", "C"), ("B", "C")]
741
sage: gbe.add_edges(edges)
742
sage: ed = gbe.get_edge("A", "B")
743
sage: print ed[0], ed[1], ed[2]['x']
744
A B 0.0
745
sage: gbe.get_edge("A", "F") is None
746
True
747
"""
748
cdef int i = self._find_vertex(u)
749
cdef int j = self._find_vertex(v)
750
751
if i < 0 or j < 0:
752
return None
753
754
cdef _glp_vertex* vert_u = self.graph.v[i+1]
755
cdef _glp_vertex* vert_v = self.graph.v[j+1]
756
cdef _glp_arc* a = vert_u.out
757
while a is not NULL:
758
if a.head == vert_v:
759
return (u, v, {"low":(<c_a_data *>a.data).low,
760
"cap":(<c_a_data *>a.data).cap,
761
"cost":(<c_a_data *>a.data).cost,
762
"x":(<c_a_data *>a.data).x})
763
a = a.t_next
764
765
return None
766
767
cpdef list edges(self):
768
"""
769
Returns a ``list`` of all edges in the graph
770
771
OUTPUT:
772
773
A ``list`` of ``triples`` representing the edges of the graph.
774
775
EXAMPLE::
776
777
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
778
sage: gbe = GLPKGraphBackend()
779
sage: edges = [("A", "B", {"low":0.0, "cap":10.0, "cost":5})]
780
sage: edges.append(("B", "C"))
781
sage: gbe.add_edges(edges)
782
sage: for ed in gbe.edges():
783
... print ed[0], ed[1], ed[2]['cost']
784
A B 5.0
785
B C 0.0
786
"""
787
788
cdef int i = 1
789
cdef _glp_vertex* vert_u
790
cdef _glp_vertex* vert_v
791
cdef _glp_arc* a
792
edge_list = []
793
794
while i <= self.graph.nv:
795
vert_u = self.graph.v[i]
796
a = vert_u.out
797
while a is not NULL:
798
vert_v = a.head
799
if vert_u.name is NULL:
800
u_name = None
801
else:
802
u_name = vert_u.name
803
if vert_v.name is NULL:
804
v_name = None
805
else:
806
v_name = vert_v.name
807
edge_list.append((u_name, v_name,
808
{"low":(<c_a_data *>a.data).low,
809
"cap":(<c_a_data *>a.data).cap,
810
"cost":(<c_a_data *>a.data).cost,
811
"x":(<c_a_data *>a.data).x}))
812
a = a.t_next
813
i += 1
814
return edge_list
815
816
cpdef delete_vertex(self, char* vert):
817
r"""
818
Removes a vertex from the graph.
819
820
Trying to delete a non existing vertex will raise an exception.
821
822
INPUT:
823
824
- ``vert`` -- The name (as ``str``) of the vertex to delete.
825
826
EXAMPLE::
827
828
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
829
sage: gbe = GLPKGraphBackend()
830
sage: verts = ["A", "D"]
831
sage: gbe.add_vertices(verts)
832
sage: gbe.delete_vertex("A")
833
sage: gbe.vertices()
834
['D']
835
sage: gbe.delete_vertex("A")
836
Traceback (most recent call last):
837
...
838
RuntimeError: Vertex A does not exist.
839
"""
840
841
cdef int i = self._find_vertex(vert)
842
843
if i < 0:
844
raise RuntimeError("Vertex %s does not exist."%(vert))
845
846
cdef int * num = <int *> sage_malloc(2 * sizeof(int))
847
num[1] = i + 1
848
cdef int ndel = 1
849
850
if not num:
851
raise MemoryError("Error allocating memory.")
852
853
glp_del_vertices(self.graph, ndel, num)
854
sage_free(num)
855
856
cpdef delete_vertices(self, list verts):
857
r"""
858
Removes vertices from the graph.
859
860
Trying to delete a non existing vertex will raise an exception.
861
862
INPUT:
863
864
- ``verts`` -- iterable container containing names (as ``str``) of the
865
vertices to delete
866
867
EXAMPLE::
868
869
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
870
sage: gbe = GLPKGraphBackend()
871
sage: verts = ["A", "B", "C", "D"]
872
sage: gbe.add_vertices(verts)
873
sage: v_d = ["A", "B"]
874
sage: gbe.delete_vertices(v_d)
875
sage: gbe.vertices()
876
['C', 'D']
877
sage: gbe.delete_vertices(["C", "A"])
878
Traceback (most recent call last):
879
...
880
RuntimeError: Vertex A does not exist.
881
sage: gbe.vertices()
882
['C', 'D']
883
"""
884
885
verts_val = [self._find_vertex(v) for v in verts]
886
if -1 in verts_val:
887
i = verts_val.index(-1)
888
raise RuntimeError("Vertex %s does not exist."%(verts[i]))
889
890
cdef int * num = <int *> sage_malloc((len(verts_val)+1) * sizeof(int))
891
if not num:
892
raise MemoryError("Error allocating memory.")
893
cdef int ndel = len(verts_val)
894
895
for i,(v) in enumerate(verts_val):
896
num[i+1] = v+1
897
898
glp_del_vertices(self.graph, ndel, num)
899
900
sage_free(num)
901
902
cpdef delete_edge(self, char* u, char* v, dict params=None):
903
"""
904
Deletes an edge from the graph.
905
906
If an edge does not exist it is ignored.
907
908
INPUT:
909
910
- ``u`` -- The name (as ``str``) of the tail vertex of the edge
911
- ``v`` -- The name (as ``str``) of the tail vertex of the edge
912
- ``params`` -- ``params`` -- An optional ``dict`` containing the edge
913
parameters (see meth:``add_edge``). If this parameter
914
is not provided, all edges connecting ``u`` and ``v`` are deleted.
915
Otherwise only edges with matching parameters are deleted.
916
917
.. SEEALSO::
918
919
:meth:`delete_edges`
920
921
EXAMPLE::
922
923
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
924
sage: gbe = GLPKGraphBackend()
925
sage: edges = [("A", "B", {"low":0.0, "cap":10.0, "cost":5})]
926
sage: edges.append(("A", "B", {"low":0.0, "cap":15.0, "cost":10}))
927
sage: edges.append(("B", "C", {"low":0.0, "cap":20.0, "cost":1}))
928
sage: edges.append(("B", "C", {"low":0.0, "cap":10.0, "cost":20}))
929
sage: gbe.add_edges(edges)
930
sage: gbe.delete_edge("A", "B")
931
sage: gbe.delete_edge("B", "C", {"low":0.0, "cap":10.0, "cost":20})
932
sage: print gbe.edges()[0][0], gbe.edges()[0][1], gbe.edges()[0][2]['cost']
933
B C 1.0
934
"""
935
936
cdef int i = self._find_vertex(u)
937
cdef int j = self._find_vertex(v)
938
if i < 0 or j < 0:
939
return
940
941
cdef _glp_vertex* vert_u = self.graph.v[i+1]
942
cdef _glp_vertex* vert_v = self.graph.v[j+1]
943
cdef _glp_arc* a = vert_u.out
944
cdef _glp_arc* a2 = a
945
946
cdef double low, cap, cost, x
947
948
if params is not None:
949
if params.has_key("low"):
950
low = params["low"]
951
if params.has_key("cap"):
952
cap = params["cap"]
953
if params.has_key("cost"):
954
cost = params["cost"]
955
if params.has_key("x"):
956
x = params["x"]
957
958
while a is not NULL:
959
a2 = a.t_next
960
if a.head == vert_v and params is None:
961
glp_del_arc(self.graph, a)
962
elif a.head == vert_v:
963
del_it = True
964
if params.has_key("low"):
965
if (<c_a_data *>a.data).low != low:
966
del_it = False
967
if params.has_key("cap"):
968
if (<c_a_data *>a.data).cap != cap:
969
del_it = False
970
if params.has_key("cost"):
971
if (<c_a_data *>a.data).cost != cost:
972
del_it = False
973
if params.has_key("x"):
974
if (<c_a_data *>a.data).x != x:
975
del_it = False
976
if del_it:
977
glp_del_arc(self.graph, a)
978
979
a = a2
980
981
def delete_edges(self, edges):
982
"""
983
Deletes edges from the graph.
984
985
Non existing edges are ignored.
986
987
INPUT:
988
989
- ``edges`` -- An iterable container of edges.
990
991
.. SEEALSO::
992
993
:meth:`delete_edge`
994
995
EXAMPLE::
996
997
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
998
sage: gbe = GLPKGraphBackend()
999
sage: edges = [("A", "B", {"low":0.0, "cap":10.0, "cost":5})]
1000
sage: edges.append(("A", "B", {"low":0.0, "cap":15.0, "cost":10}))
1001
sage: edges.append(("B", "C", {"low":0.0, "cap":20.0, "cost":1}))
1002
sage: edges.append(("B", "C", {"low":0.0, "cap":10.0, "cost":20}))
1003
sage: gbe.add_edges(edges)
1004
sage: gbe.delete_edges(edges[1:])
1005
sage: len(gbe.edges())
1006
1
1007
sage: print gbe.edges()[0][0], gbe.edges()[0][1], gbe.edges()[0][2]['cap']
1008
A B 10.0
1009
"""
1010
1011
for edge in edges:
1012
self.delete_edge(*edge)
1013
1014
cpdef int _find_vertex(self, char *name):
1015
"""
1016
Returns the index of a vertex specified by a name
1017
1018
INPUT:
1019
1020
- ``name`` -- Name of the vertex
1021
1022
OUTPUT:
1023
1024
The index of the vertex or ``-1`` if the vertex is not found
1025
1026
EXAMPLE::
1027
1028
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
1029
sage: gbe = GLPKGraphBackend()
1030
sage: verts = ["A", "B", "C", "D"]
1031
sage: gbe.add_vertices(verts)
1032
sage: gbe._find_vertex("A")
1033
0
1034
sage: gbe._find_vertex("F")
1035
-1
1036
"""
1037
1038
glp_create_v_index(self.graph)
1039
return glp_find_vertex(self.graph, name) - 1
1040
1041
cpdef int write_graph(self, char * fname):
1042
r"""
1043
Writes the graph to a plain text file
1044
1045
INPUT:
1046
1047
- ``fname`` -- full name of the file
1048
1049
OUTPUT:
1050
1051
Zero if the operations was successful otherwise nonzero
1052
1053
EXAMPLE::
1054
1055
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
1056
sage: gbe = GLPKGraphBackend()
1057
sage: a = gbe.add_edge("0", "1")
1058
sage: gbe.write_graph(SAGE_TMP+"/graph.txt")
1059
Writing graph to ...
1060
2 lines were written
1061
0
1062
"""
1063
1064
return glp_write_graph(self.graph, fname)
1065
1066
cpdef int write_ccdata(self, char * fname):
1067
r"""
1068
Writes the graph to a text file in DIMACS format.
1069
1070
Writes the data to plain ASCII text file in DIMACS format.
1071
A discription of the DIMACS format can be found at
1072
http://dimacs.rutgers.edu/Challenges/.
1073
1074
INPUT:
1075
1076
- ``fname`` -- full name of the file
1077
1078
OUTPUT:
1079
1080
Zero if the operations was successful otherwise nonzero
1081
1082
EXAMPLE::
1083
1084
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
1085
sage: gbe = GLPKGraphBackend()
1086
sage: a = gbe.add_edge("0", "1")
1087
sage: gbe.write_ccdata(SAGE_TMP+"/graph.dat")
1088
Writing graph to ...
1089
6 lines were written
1090
0
1091
"""
1092
1093
return glp_write_ccdata(self.graph, 0, fname)
1094
1095
cpdef int write_mincost(self, char * fname):
1096
"""
1097
Writes the mincost flow problem data to a text file in DIMACS format
1098
1099
INPUT:
1100
1101
- ``fname`` -- Full name of file
1102
1103
OUTPUT:
1104
1105
Zero if successful, otherwise nonzero
1106
1107
EXAMPLE::
1108
1109
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
1110
sage: gbe = GLPKGraphBackend()
1111
sage: a = gbe.add_edge("0", "1")
1112
sage: gbe.write_mincost(SAGE_TMP+"/graph.min")
1113
Writing min-cost flow problem data to ...
1114
4 lines were written
1115
0
1116
"""
1117
1118
return glp_write_mincost(self.graph, 0, 0, sizeof(double),
1119
sizeof(double) + sizeof(double), fname)
1120
1121
cpdef double mincost_okalg(self) except -1:
1122
r"""
1123
Finds solution to the mincost problem with the out-of-kilter algorithm.
1124
1125
The out-of-kilter algorithm requires all problem data to be integer
1126
valued.
1127
1128
OUTPUT:
1129
1130
The solution to the mincost problem, i.e. the total cost, if operation
1131
was successful.
1132
1133
.. NOTE::
1134
1135
This method raises ``MIPSolverException`` exceptions when
1136
the solution can not be computed for any reason (none
1137
exists, or the LP solver was not able to find it, etc...)
1138
1139
EXAMPLE::
1140
1141
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
1142
sage: gbe = GLPKGraphBackend()
1143
sage: vertices = (35, 50, 40, -45, -20, -30, -30)
1144
sage: vs = gbe.add_vertices([None for i in range(len(vertices))])
1145
sage: v_dict = {}
1146
sage: for i, v in enumerate(vs):
1147
... v_dict[v] = vertices[i]
1148
sage: gbe.set_vertices_demand(v_dict.items())
1149
sage: cost = ((8, 6, 10, 9), (9, 12, 13, 7), (14, 9, 16, 5))
1150
sage: lcost = range(len(cost))
1151
sage: lcost_0 = range(len(cost[0]))
1152
sage: for i in lcost:
1153
... for j in lcost_0:
1154
... gbe.add_edge(str(i), str(j + len(cost)), {"cost":cost[i][j], "cap":100})
1155
sage: gbe.mincost_okalg()
1156
1020.0
1157
sage: for ed in gbe.edges():
1158
... print ed[0], "->", ed[1], ed[2]["x"]
1159
0 -> 6 0.0
1160
0 -> 5 25.0
1161
0 -> 4 10.0
1162
0 -> 3 0.0
1163
1 -> 6 0.0
1164
1 -> 5 5.0
1165
1 -> 4 0.0
1166
1 -> 3 45.0
1167
2 -> 6 30.0
1168
2 -> 5 0.0
1169
2 -> 4 10.0
1170
2 -> 3 0.0
1171
"""
1172
cdef double graph_sol
1173
cdef int status = glp_mincost_okalg(self.graph, 0, 0, sizeof(double),
1174
2 * sizeof(double), &graph_sol,
1175
3 * sizeof(double), sizeof(double))
1176
if status == 0:
1177
pass
1178
elif status == GLP_ENOPFS:
1179
raise MIPSolverException("No (primal) feasible solution exists")
1180
elif status == GLP_EDATA:
1181
raise MIPSolverException("Unable to start search due to " +
1182
"problem data")
1183
elif status == GLP_ERANGE:
1184
raise MIPSolverException("The search was prematurely terminated " +
1185
"because of integer overflow")
1186
elif status == GLP_EFAIL:
1187
raise MIPSolverException("An error has been detected" +
1188
"in the program logic")
1189
1190
return graph_sol
1191
1192
cpdef int write_maxflow(self, char * fname) except -1:
1193
"""
1194
Writes the maximum flow problem data to a text file in DIMACS format.
1195
1196
INPUT:
1197
1198
- ``fname`` -- Full name of file
1199
1200
OUTPUT:
1201
1202
``Zero`` if successful, otherwise ``non-zero``
1203
1204
EXAMPLE::
1205
1206
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
1207
sage: gbe = GLPKGraphBackend()
1208
sage: gbe.add_vertices([None for i in range(2)])
1209
['0', '1']
1210
sage: a = gbe.add_edge('0', '1')
1211
sage: gbe.maxflow_ffalg('0', '1')
1212
0.0
1213
sage: gbe.write_maxflow(SAGE_TMP+"/graph.max")
1214
Writing maximum flow problem data to ...
1215
6 lines were written
1216
0
1217
sage: gbe = GLPKGraphBackend()
1218
sage: gbe.write_maxflow(SAGE_TMP+"/graph.max")
1219
Traceback (most recent call last):
1220
...
1221
IOError: Cannot write empty graph
1222
"""
1223
1224
if self.graph.nv <= 0:
1225
raise IOError("Cannot write empty graph")
1226
1227
return glp_write_maxflow(self.graph, self.s+1, self.t+1,
1228
sizeof(double), fname)
1229
1230
cpdef double maxflow_ffalg(self, u=None, v=None) except -1:
1231
r"""
1232
Finds solution to the maxflow problem with Ford-Fulkerson algorithm.
1233
1234
INPUT:
1235
1236
- ``u`` -- Name (as ``str``) of the tail vertex. Default is ``None``.
1237
- ``v`` -- Name (as ``str``) of the head vertex. Default is ``None``.
1238
1239
If ``u`` or ``v`` are ``None``, the currently stored values for the
1240
head or tail vertex are used. This behavior is useful when reading
1241
maxflow data from a file. When calling this function with values for
1242
``u`` and ``v``, the head and tail vertex are stored for
1243
later use.
1244
1245
OUTPUT:
1246
1247
The solution to the maxflow problem, i.e. the maximum flow.
1248
1249
.. NOTE::
1250
1251
* If the source or sink vertex does not exist, an ``IndexError`` is
1252
raised.
1253
1254
* If the source and sink are identical, a ``ValueError`` is raised.
1255
1256
* This method raises ``MIPSolverException`` exceptions when the
1257
solution can not be computed for any reason (none exists, or the
1258
LP solver was not able to find it, etc...)
1259
1260
EXAMPLE::
1261
1262
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
1263
sage: gbe = GLPKGraphBackend()
1264
sage: v = gbe.add_vertices([None for i in range(5)])
1265
sage: edges = ((0, 1, 2), (0, 2, 3), (1, 2, 3), (1, 3, 4),
1266
... (3, 4, 1), (2, 4, 2))
1267
sage: for a in edges:
1268
... edge = gbe.add_edge(str(a[0]), str(a[1]), {"cap":a[2]})
1269
sage: gbe.maxflow_ffalg('0', '4')
1270
3.0
1271
sage: gbe.maxflow_ffalg()
1272
3.0
1273
sage: gbe.maxflow_ffalg('0', '8')
1274
Traceback (most recent call last):
1275
...
1276
IndexError: Source or sink vertex does not exist
1277
"""
1278
cdef int s, t
1279
1280
if u is not None and v is not None:
1281
s = self._find_vertex(u)
1282
t = self._find_vertex(v)
1283
else:
1284
s = self.s
1285
t = self.t
1286
1287
if s < 0 or t < 0:
1288
raise IndexError("Source or sink vertex does not exist")
1289
if s == t:
1290
raise ValueError ("Source and sink are identical")
1291
1292
self.s = s
1293
self.t = t
1294
1295
s += 1
1296
t += 1
1297
1298
cdef double graph_sol
1299
cdef int status = glp_maxflow_ffalg(self.graph, s, t, sizeof(double),
1300
&graph_sol, 3 * sizeof(double),
1301
4 * sizeof(double))
1302
if status == 0:
1303
pass
1304
elif status == GLP_ENOPFS:
1305
raise MIPSolverException("No (primal) feasible solution exists")
1306
elif status == GLP_EDATA:
1307
raise MIPSolverException("Unable to start search due " +
1308
"to problem data")
1309
elif status == GLP_ERANGE:
1310
raise MIPSolverException("The search was prematurely terminated " +
1311
"because of integer overflow")
1312
elif status == GLP_EFAIL:
1313
raise MIPSolverException("An error has been detected in the " +
1314
"program logic")
1315
1316
return graph_sol
1317
1318
cpdef double cpp(self):
1319
r"""
1320
Solves the critical path problem of a project network.
1321
1322
OUTPUT:
1323
1324
The length of the critical path of the network
1325
1326
EXAMPLE::
1327
1328
sage: from sage.numerical.backends.glpk_graph_backend import GLPKGraphBackend
1329
sage: gbe = GLPKGraphBackend()
1330
sage: gbe.add_vertices([None for i in range(3)])
1331
['0', '1', '2']
1332
sage: gbe.set_vertex_demand('0', 3)
1333
sage: gbe.set_vertex_demand('1', 1)
1334
sage: gbe.set_vertex_demand('2', 4)
1335
sage: a = gbe.add_edge('0', '2')
1336
sage: a = gbe.add_edge('1', '2')
1337
sage: gbe.cpp()
1338
7.0
1339
sage: v = gbe.get_vertex('1')
1340
sage: print 1, v["rhs"], v["es"], v["ls"] # abs tol 1e-6
1341
1 1.0 0.0 2.0
1342
"""
1343
1344
return glp_cpp(self.graph, 0, 2 * sizeof(double),
1345
3 * sizeof(double))
1346
1347
def __dealloc__(self):
1348
"""
1349
Destructor
1350
"""
1351
if self.graph is not NULL:
1352
glp_delete_graph(self.graph)
1353
self.graph = NULL
1354
1355