Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/geometry/polyhedron/backend_cdd.py
8817 views
1
"""
2
The cdd backend for polyhedral computations
3
"""
4
5
from subprocess import Popen, PIPE
6
from sage.rings.all import ZZ, QQ, RDF
7
from sage.misc.all import SAGE_TMP, tmp_filename, union, cached_method, prod
8
from sage.matrix.constructor import matrix
9
10
from base import Polyhedron_base
11
from base_QQ import Polyhedron_QQ
12
from base_RDF import Polyhedron_RDF
13
14
15
16
#########################################################################
17
class Polyhedron_cdd(Polyhedron_base):
18
"""
19
Base class for the cdd backend.
20
"""
21
22
def _init_from_Vrepresentation(self, vertices, rays, lines, verbose=False):
23
"""
24
Construct polyhedron from V-representation data.
25
26
INPUT:
27
28
- ``vertices`` -- list of point. Each point can be specified
29
as any iterable container of
30
:meth:`~sage.geometry.polyhedron.base.base_ring` elements.
31
32
- ``rays`` -- list of rays. Each ray can be specified as any
33
iterable container of
34
:meth:`~sage.geometry.polyhedron.base.base_ring` elements.
35
36
- ``lines`` -- list of lines. Each line can be specified as
37
any iterable container of
38
:meth:`~sage.geometry.polyhedron.base.base_ring` elements.
39
40
- ``verbose`` -- boolean (default: ``False``). Whether to print
41
verbose output for debugging purposes.
42
43
EXAMPLES::
44
45
sage: Polyhedron(vertices=[(0,0)], rays=[(1,1)],
46
... lines=[(1,-1)], backend='cdd', base_ring=QQ) # indirect doctest
47
A 2-dimensional polyhedron in QQ^2 defined as the
48
convex hull of 1 vertex, 1 ray, 1 line
49
"""
50
from cdd_file_format import cdd_Vrepresentation
51
s = cdd_Vrepresentation(self._cdd_type, vertices, rays, lines)
52
self._init_from_cdd_input(s, '--reps', verbose)
53
54
55
def _init_from_Hrepresentation(self, ieqs, eqns, verbose=False):
56
"""
57
Construct polyhedron from H-representation data.
58
59
INPUT:
60
61
- ``ieqs`` -- list of inequalities. Each line can be specified
62
as any iterable container of
63
:meth:`~sage.geometry.polyhedron.base.base_ring` elements.
64
65
- ``eqns`` -- list of equalities. Each line can be specified
66
as any iterable container of
67
:meth:`~sage.geometry.polyhedron.base.base_ring` elements.
68
69
- ``verbose`` -- boolean (default: ``False``). Whether to print
70
verbose output for debugging purposes.
71
72
EXAMPLES::
73
74
sage: Polyhedron(ieqs=[(0,1,1)], eqns=[(0,1,-1)],
75
... backend='cdd', base_ring=QQ) # indirect doctest
76
A 1-dimensional polyhedron in QQ^2 defined as the
77
convex hull of 1 vertex and 1 ray
78
"""
79
from cdd_file_format import cdd_Hrepresentation
80
s = cdd_Hrepresentation(self._cdd_type, ieqs, eqns)
81
self._init_from_cdd_input(s, '--reps', verbose)
82
83
84
def _init_facet_adjacency_matrix(self, verbose=False):
85
"""
86
Compute the facet adjacency matrix in case it has not been
87
computed during initialization.
88
89
INPUT:
90
91
- ``verbose`` -- boolean (default: ``False``). Whether to print
92
verbose output for debugging purposes.
93
94
EXAMPLES::
95
96
sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)], backend='cdd', base_ring=QQ)
97
sage: '_H_adjacency_matrix' in p.__dict__
98
False
99
sage: p._init_facet_adjacency_matrix()
100
sage: p._H_adjacency_matrix
101
[0 1 1]
102
[1 0 1]
103
[1 1 0]
104
"""
105
self._init_from_cdd_input(self.cdd_Hrepresentation(),
106
'--adjacency', verbose)
107
108
109
def _init_vertex_adjacency_matrix(self, verbose=False):
110
"""
111
Compute the vertex adjacency matrix in case it has not been
112
computed during initialization.
113
114
INPUT:
115
116
- ``verbose`` -- boolean (default: ``False``). Whether to print
117
verbose output for debugging purposes.
118
119
EXAMPLES::
120
121
sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)], backend='cdd', base_ring=QQ)
122
sage: '_V_adjacency_matrix' in p.__dict__
123
False
124
sage: p._init_vertex_adjacency_matrix()
125
sage: p._V_adjacency_matrix
126
[0 1 1]
127
[1 0 1]
128
[1 1 0]
129
"""
130
self._init_from_cdd_input(self.cdd_Vrepresentation(),
131
'--adjacency', verbose)
132
133
134
def _init_from_cdd_input(self, cdd_input_string, cmdline_arg='--all', verbose=False):
135
"""
136
Internal method: run cdd on a cdd H- or V-representation
137
and initialize ourselves with the output.
138
139
TESTS::
140
141
sage: p = Polyhedron(vertices=[[0,0,0],[1,0,0],[0,1,0],[0,0,1]],
142
... backend='cdd', base_ring=QQ)
143
sage: from sage.geometry.polyhedron.cdd_file_format import cdd_Vrepresentation
144
sage: s = cdd_Vrepresentation('rational', [[0,0,1],[0,1,0],[1,0,0]], [], [])
145
sage: p._init_from_cdd_input(s)
146
sage: p.dim()
147
2
148
149
sage: point_list = [[0.132, -1.028, 0.028],[0.5, 0.5, -1.5],
150
... [-0.5, 1.5, -0.5],[0.5, 0.5, 0.5],[1.5, -0.5, -0.5],
151
... [-0.332, -0.332, -0.668],[-1.332, 0.668, 0.332],
152
... [-0.932, 0.068, 0.932],[-0.38, -0.38, 1.38],
153
... [-0.744, -0.12, 1.12],[-0.7781818182, -0.12, 0.9490909091],
154
... [0.62, -1.38, 0.38],[0.144, -1.04, 0.04],
155
... [0.1309090909, -1.0290909091, 0.04]]
156
sage: Polyhedron(point_list)
157
Traceback (most recent call last):
158
...
159
ValueError: *Error: Numerical inconsistency is found. Use the GMP exact arithmetic.
160
sage: Polyhedron(point_list, base_ring=QQ)
161
A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 14 vertices
162
"""
163
if verbose:
164
print '---- CDD input -----'
165
print cdd_input_string
166
167
cdd_proc = Popen([self._cdd_executable, cmdline_arg],
168
stdin=PIPE, stdout=PIPE, stderr=PIPE)
169
ans, err = cdd_proc.communicate(input=cdd_input_string)
170
171
if verbose:
172
print '---- CDD output -----'
173
print ans
174
print err
175
if 'Error:' in ans + err:
176
# cdd reports errors on stdout and misc information on stderr
177
raise ValueError(ans.strip())
178
self._init_from_cdd_output(ans)
179
180
181
def _init_from_cdd_output(self, cdd_output_string):
182
"""
183
Initialize ourselves with the output from cdd.
184
185
TESTS::
186
187
sage: from sage.geometry.polyhedron.cdd_file_format import cdd_Vrepresentation
188
sage: s = cdd_Vrepresentation('rational',[[0,0],[1,0],[0,1],[1,1]], [], [])
189
sage: from subprocess import Popen, PIPE
190
sage: cdd_proc = Popen(['cdd_both_reps_gmp', '--all'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
191
sage: ans, err = cdd_proc.communicate(input=s)
192
sage: p = Polyhedron(vertices = [[0,0],[1,0],[0,1],[1,1]], backend='cdd', base_ring=QQ)
193
sage: p._init_from_cdd_output(ans)
194
sage: p.vertices()
195
(A vertex at (0, 0), A vertex at (1, 0), A vertex at (0, 1), A vertex at (1, 1))
196
"""
197
cddout=cdd_output_string.splitlines()
198
suppressed_vertex = False # whether cdd suppressed the vertex in output
199
parent = self.parent()
200
201
# nested function
202
def expect_in_cddout(expected_string):
203
l = cddout.pop(0).strip()
204
if l!=expected_string:
205
raise ValueError, ('Error while parsing cdd output: expected "'
206
+expected_string+'" but got "'+l+'".\n' )
207
# nested function
208
def cdd_linearities():
209
l = cddout[0].split()
210
if l[0] != "linearity":
211
return []
212
cddout.pop(0)
213
assert len(l) == int(l[1])+2, "Not enough linearities given"
214
return [int(i)-1 for i in l[2:]] # make indices pythonic
215
216
# nested function
217
def cdd_convert(string, field=self.field()):
218
"""
219
Converts the cdd output string to a numerical value.
220
"""
221
return [field(x) for x in string.split()]
222
223
# nested function
224
def find_in_cddout(expected_string):
225
"""
226
Find the expected string in a list of strings, and
227
truncates ``cddout`` to start at that point. Returns
228
``False`` if search fails.
229
"""
230
for pos in range(0,len(cddout)):
231
l = cddout[pos].strip();
232
if l==expected_string:
233
# must not assign to cddout in nested function
234
for i in range(0,pos+1):
235
cddout.pop(0)
236
return True
237
return False
238
239
if find_in_cddout('V-representation'):
240
self._Vrepresentation = []
241
lines = cdd_linearities()
242
expect_in_cddout('begin')
243
l = cddout.pop(0).split()
244
assert self.ambient_dim() == int(l[1])-1, "Different ambient dimension?"
245
suppressed_vertex = True
246
for i in range(int(l[0])):
247
l = cddout.pop(0).strip()
248
l_type = l[0]
249
l = l[1:]
250
if i in lines:
251
parent._make_Line(self, cdd_convert(l));
252
elif l_type == '0':
253
parent._make_Ray(self, cdd_convert(l));
254
else:
255
parent._make_Vertex(self, cdd_convert(l));
256
suppressed_vertex = False
257
if suppressed_vertex and self.n_Vrepresentation()>0:
258
# cdd does not output the vertex if it is only the origin
259
parent._make_Vertex(self, [0] * self.ambient_dim())
260
self._Vrepresentation = tuple(self._Vrepresentation)
261
expect_in_cddout('end')
262
263
if find_in_cddout('H-representation'):
264
self._Hrepresentation = []
265
equations = cdd_linearities()
266
expect_in_cddout('begin')
267
l = cddout.pop(0).split()
268
assert self.ambient_dim() == int(l[1])-1, "Different ambient dimension?"
269
for i in range(int(l[0])):
270
l = cddout.pop(0)
271
if i in equations:
272
parent._make_Equation(self, cdd_convert(l));
273
else:
274
parent._make_Inequality(self, cdd_convert(l));
275
self._Hrepresentation = tuple(self._Hrepresentation)
276
expect_in_cddout('end')
277
278
# nested function
279
def cdd_adjacencies():
280
l = cddout.pop(0).split()
281
assert l[2] == ':', "Not a line of the adjacency data?"
282
return [int(i)-1 for i in l[3:]]
283
284
if find_in_cddout('Vertex graph'):
285
n = len(self._Vrepresentation);
286
if suppressed_vertex:
287
n_cdd=n-1;
288
else:
289
n_cdd=n;
290
self._V_adjacency_matrix = matrix(ZZ, n, n, 0)
291
expect_in_cddout('begin')
292
l = cddout.pop(0).split()
293
assert int(l[0]) == n_cdd, "Not enough V-adjacencies in cdd output?"
294
for i in range(n_cdd):
295
for a in cdd_adjacencies():
296
self._V_adjacency_matrix[i,a] = 1
297
# cdd reports that lines are never adjacent to anything.
298
# I disagree, they are adjacent to everything!
299
if self._Vrepresentation[i].is_line():
300
for j in range(n):
301
self._V_adjacency_matrix[i,j] = 1
302
self._V_adjacency_matrix[j,i] = 1
303
self._V_adjacency_matrix[i,i] = 0
304
if suppressed_vertex: # cdd implied that there is only one vertex
305
for i in range(n-1):
306
self._V_adjacency_matrix[i,n-1] = 1
307
self._V_adjacency_matrix[n-1,i] = 1
308
self._V_adjacency_matrix.set_immutable()
309
expect_in_cddout('end')
310
311
if find_in_cddout('Facet graph'):
312
n = len(self._Hrepresentation);
313
self._H_adjacency_matrix = matrix(ZZ, n, n, 0)
314
expect_in_cddout('begin')
315
l = cddout.pop(0).split()
316
assert int(l[0]) == n, "Not enough H-adjacencies in cdd output?"
317
for i in range(n):
318
for a in cdd_adjacencies():
319
self._H_adjacency_matrix[i,a] = 1
320
self._H_adjacency_matrix.set_immutable()
321
expect_in_cddout('end')
322
323
324
#########################################################################
325
class Polyhedron_QQ_cdd(Polyhedron_cdd, Polyhedron_QQ):
326
"""
327
Polyhedra over QQ with cdd
328
329
INPUT:
330
331
- ``parent`` -- the parent, an instance of
332
:class:`~sage.geometry.polyhedron.parent.Polyhedra`.
333
334
- ``Vrep`` -- a list ``[vertices, rays, lines]`` or ``None``.
335
336
- ``Hrep`` -- a list ``[ieqs, eqns]`` or ``None``.
337
338
EXAMPLES::
339
340
sage: from sage.geometry.polyhedron.parent import Polyhedra
341
sage: parent = Polyhedra(QQ, 2, backend='cdd')
342
sage: from sage.geometry.polyhedron.backend_cdd import Polyhedron_QQ_cdd
343
sage: Polyhedron_QQ_cdd(parent, [ [(1,0),(0,1),(0,0)], [], []], None, verbose=False)
344
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices
345
"""
346
347
_cdd_type = 'rational'
348
349
_cdd_executable = 'cdd_both_reps_gmp'
350
351
def __init__(self, parent, Vrep, Hrep, **kwds):
352
"""
353
The Python constructor.
354
355
See :class:`Polyhedron_base` for a description of the input
356
data.
357
358
TESTS::
359
360
sage: p = Polyhedron(backend='cdd', base_ring=QQ)
361
sage: type(p)
362
<class 'sage.geometry.polyhedron.backend_cdd.Polyhedra_QQ_cdd_with_category.element_class'>
363
sage: TestSuite(p).run()
364
"""
365
Polyhedron_cdd.__init__(self, parent, Vrep, Hrep, **kwds)
366
367
368
#########################################################################
369
class Polyhedron_RDF_cdd(Polyhedron_cdd, Polyhedron_RDF):
370
"""
371
Polyhedra over RDF with cdd
372
373
INPUT:
374
375
- ``ambient_dim`` -- integer. The dimension of the ambient space.
376
377
- ``Vrep`` -- a list ``[vertices, rays, lines]`` or ``None``.
378
379
- ``Hrep`` -- a list ``[ieqs, eqns]`` or ``None``.
380
381
EXAMPLES::
382
383
sage: from sage.geometry.polyhedron.parent import Polyhedra
384
sage: parent = Polyhedra(RDF, 2, backend='cdd')
385
sage: from sage.geometry.polyhedron.backend_cdd import Polyhedron_RDF_cdd
386
sage: Polyhedron_RDF_cdd(parent, [ [(1,0),(0,1),(0,0)], [], []], None, verbose=False)
387
A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 3 vertices
388
"""
389
_cdd_type = 'real'
390
391
_cdd_executable = 'cdd_both_reps'
392
393
def __init__(self, parent, Vrep, Hrep, **kwds):
394
"""
395
The Python constructor.
396
397
See :class:`Polyhedron_base` for a description of the input
398
data.
399
400
TESTS::
401
402
sage: p = Polyhedron(backend='cdd', base_ring=RDF)
403
sage: type(p)
404
<class 'sage.geometry.polyhedron.backend_cdd.Polyhedra_RDF_cdd_with_category.element_class'>
405
sage: TestSuite(p).run()
406
"""
407
Polyhedron_cdd.__init__(self, parent, Vrep, Hrep, **kwds)
408
409
410