passagemath-glpk 10.6.42__cp313-cp313-macosx_13_0_arm64.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.
- passagemath_glpk/.dylibs/libglpk.40.dylib +0 -0
- passagemath_glpk/.dylibs/libgmp.10.dylib +0 -0
- passagemath_glpk/.dylibs/libz.1.3.1.dylib +0 -0
- passagemath_glpk/__init__.py +3 -0
- passagemath_glpk-10.6.42.dist-info/METADATA +103 -0
- passagemath_glpk-10.6.42.dist-info/RECORD +29 -0
- passagemath_glpk-10.6.42.dist-info/WHEEL +6 -0
- passagemath_glpk-10.6.42.dist-info/top_level.txt +3 -0
- sage/all__sagemath_glpk.py +2 -0
- sage/libs/all__sagemath_glpk.py +1 -0
- sage/libs/glpk/__init__.py +1 -0
- sage/libs/glpk/constants.pxd +94 -0
- sage/libs/glpk/env.pxd +14 -0
- sage/libs/glpk/graph.pxd +43 -0
- sage/libs/glpk/lp.pxd +95 -0
- sage/libs/glpk/types.pxd +87 -0
- sage/numerical/all__sagemath_glpk.py +1 -0
- sage/numerical/backends/all__sagemath_glpk.py +1 -0
- sage/numerical/backends/glpk_backend.cpython-313-darwin.so +0 -0
- sage/numerical/backends/glpk_backend.pxd +41 -0
- sage/numerical/backends/glpk_backend.pyx +3359 -0
- sage/numerical/backends/glpk_backend_test.py +13 -0
- sage/numerical/backends/glpk_exact_backend.cpython-313-darwin.so +0 -0
- sage/numerical/backends/glpk_exact_backend.pxd +17 -0
- sage/numerical/backends/glpk_exact_backend.pyx +190 -0
- sage/numerical/backends/glpk_exact_backend_test.py +12 -0
- sage/numerical/backends/glpk_graph_backend.cpython-313-darwin.so +0 -0
- sage/numerical/backends/glpk_graph_backend.pxd +56 -0
- 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
|