passagemath-glpk 10.6.39__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-glpk might be problematic. Click here for more details.

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