Skip to content

Commit

Permalink
Trac #31864: Improved input output for backend polymake
Browse files Browse the repository at this point in the history
From #26368.

The interface to polymake seems to have a significant delay. For input,
we can avoid it, as polymake accepts nested lists of integers,
rationals, floats.

For output we can avoid it, as at least for matrices and vectors, we can
just parse the representation string.

Before:

{{{
sage: %time P = polytopes.hypercube(8, backend='polymake')
CPU times: user 4.54 s, sys: 260 ms, total: 4.8 s
Wall time: 4.81 s
sage: %time P = polytopes.hypercube(8, backend='polymake')
CPU times: user 3.08 s, sys: 236 ms, total: 3.31 s
Wall time: 3.31 s
sage: %time P1 = loads(dumps(P))
CPU times: user 805 ms, sys: 48 ms, total: 853 ms
Wall time: 853 ms
sage: %time P = polytopes.dodecahedron(backend='polymake')
CPU times: user 763 ms, sys: 48.2 ms, total: 811 ms
Wall time: 811 ms
sage: %time P = polytopes.dodecahedron(backend='polymake')
CPU times: user 661 ms, sys: 31.7 ms, total: 692 ms
Wall time: 692 ms
sage: %time P1 = loads(dumps(P))
CPU times: user 62.9 ms, sys: 0 ns, total: 62.9 ms
Wall time: 62.6 ms
sage: %time P = polytopes.dodecahedron(backend='polymake', exact=False)
CPU times: user 408 ms, sys: 19.6 ms, total: 428 ms
Wall time: 427 ms
}}}

After:

{{{
sage: %time P = polytopes.hypercube(8, backend='polymake')
CPU times: user 1.56 s, sys: 47.4 ms, total: 1.61 s
Wall time: 1.62 s
sage: %time P = polytopes.hypercube(8, backend='polymake')
CPU times: user 69.1 ms, sys: 4.07 ms, total: 73.2 ms
Wall time: 72.8 ms
sage: %time P1 = loads(dumps(P))
CPU times: user 36.5 ms, sys: 30 µs, total: 36.5 ms
Wall time: 36 ms
sage: %time P = polytopes.dodecahedron(backend='polymake')
CPU times: user 206 ms, sys: 4.1 ms, total: 210 ms
Wall time: 209 ms
sage: %time P = polytopes.dodecahedron(backend='polymake')
CPU times: user 67.9 ms, sys: 0 ns, total: 67.9 ms
Wall time: 67.2 ms
sage: %time P1 = loads(dumps(P))
CPU times: user 39.2 ms, sys: 3.7 ms, total: 42.9 ms
Wall time: 41.8 ms
sage: %time P = polytopes.dodecahedron(backend='polymake', exact=False)
CPU times: user 78.3 ms, sys: 4.01 ms, total: 82.3 ms
Wall time: 81.3 ms
}}}

URL: https://trac.sagemath.org/31864
Reported by: gh-kliem
Ticket author(s): Jonathan Kliem
Reviewer(s): Matthias Koeppe
  • Loading branch information
Release Manager committed Jul 20, 2021
2 parents bff5830 + e6fef55 commit fce0227
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 11 deletions.
16 changes: 7 additions & 9 deletions src/sage/geometry/polyhedron/backend_polymake.py
Expand Up @@ -418,17 +418,15 @@ def _init_Vrepresentation_from_polymake(self):
self._Vrepresentation = []
parent = self.parent()
p = self._polymake_polytope
for g in p.VERTICES:
g = g.sage()
for g in p.VERTICES.sage():
d = g[0]
if d == 0:
parent._make_Ray(self, g[1:])
elif d == 1:
parent._make_Vertex(self, g[1:])
else:
raise NotImplementedError("Non-normalized vertex encountered: {}".format(g))
for g in p.LINEALITY_SPACE:
g = g.sage()
for g in p.LINEALITY_SPACE.sage():
d = g[0]
if d == 0:
parent._make_Line(self, g[1:])
Expand Down Expand Up @@ -458,14 +456,14 @@ def _init_Hrepresentation_from_polymake(self):
else:
self._Hrepresentation = []
parent = self.parent()
for g in p.FACETS:
for g in p.FACETS.sage():
if all(x==0 for x in g[1:]):
# Ignore vertical inequality
pass
else:
parent._make_Inequality(self, g.sage())
for g in p.AFFINE_HULL:
parent._make_Equation(self, g.sage())
parent._make_Inequality(self, g)
for g in p.AFFINE_HULL.sage():
parent._make_Equation(self, g)
self._Hrepresentation = tuple(self._Hrepresentation)

@classmethod
Expand Down Expand Up @@ -499,7 +497,7 @@ def _from_polymake_polytope(cls, parent, polymake_polytope):
base_ring = coercion_model.common_parent(*data).base_ring()
else:
base_ring = QQ
ambient_dim = polymake_polytope.AMBIENT_DIM()
ambient_dim = polymake_polytope.AMBIENT_DIM().sage()
parent = Polyhedra(base_ring=base_ring, ambient_dim=ambient_dim, backend='polymake')
return cls(parent, None, None, polymake_polytope=polymake_polytope)

Expand Down
88 changes: 86 additions & 2 deletions src/sage/interfaces/polymake.py
Expand Up @@ -331,8 +331,50 @@ def convert(y):
r = self.new("{" + ",".join(A) + "}")
r.__sage_dict = z # do this to avoid having the entries of the list be garbage collected
return r
else:
return super(PolymakeAbstract, self)._coerce_impl(x, use_special=use_special)

from sage.rings.all import Integer, Rational, RDF
from sage.rings.number_field.number_field import is_QuadraticField

def to_str(x):
if isinstance(x, list):
s = '['
for y in x:
s += to_str(y) + ', '
s += ']'
return s
if isinstance(x, (Integer, Rational, int)):
return '{}'.format(x)
parent = None
try:
parent = x.parent()
except AttributeError:
pass

if is_QuadraticField(parent):
return x._polymake_init_()
try:
if x.parent().is_exact():
# No other exact rings are supported.
raise NotImplementedError
except AttributeError:
pass

try:
x = RDF(x)
return '{}'.format(x)
except:
pass

raise NotImplementedError

# Iteratively calling polymake for conversion takes a long time.
# However, it takes iterated arrays of integers, rationals and floats directly.
try:
return self.new(to_str(x))
except NotImplementedError:
pass

return super(PolymakeAbstract, self)._coerce_impl(x, use_special=use_special)

def console(self):
"""
Expand Down Expand Up @@ -1512,6 +1554,48 @@ def _sage_(self):
"""
T1, T2 = self.typeof()
self._check_valid()
try:
# Try to just read things from the string representation.
if 'Sparse' in T1:
raise NotImplementedError

r = self._repr_()
if 'Float' in T1:
from sage.rings.all import RDF
base_ring = RDF
str_to_base_ring = lambda s: RDF(s)
elif 'QuadraticExtension' in T1 and 'r' in r:
i = r.find('r')
i1 = min((r[i:]+' ').find(' '), (r[i:]+'\n').find('\n'))
d = int(r[i+1:i+i1])
from sage.rings.number_field.number_field import QuadraticField
base_ring = QuadraticField(d)

def str_to_base_ring(s):
m = re.match(r'(-?[0-9/]+)[+]?((-?[0-9/]+)r([0-9/]+))?', s)
a, b = m.group(1), m.group(3)
return base_ring(a) + base_ring(b)*base_ring.gen()

elif 'Rational' in T1:
from sage.rings.all import QQ
base_ring = QQ
str_to_base_ring = lambda s: QQ(s)
else:
raise NotImplementedError

if 'Vector' in T1:
from sage.modules.free_module_element import vector
if r == '':
return vector(base_ring)
return vector(base_ring, [str_to_base_ring(s) for s in r.split(' ')])
elif 'Matrix' in T1:
from sage.matrix.constructor import matrix
if r == '':
return matrix(base_ring)
return matrix(base_ring, [[str_to_base_ring(s) for s in t.split(' ')] for t in r.split('\n')])
except:
pass

if T1:
Temp = self.typename()
if Temp:
Expand Down

0 comments on commit fce0227

Please sign in to comment.