From 02a6ef9f81f0973f603fede9fae369647f49e56f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 May 2021 16:35:28 +0200 Subject: [PATCH 1/5] faster sage eval of polymake --- .../geometry/polyhedron/backend_polymake.py | 14 +++--- src/sage/interfaces/polymake.py | 48 +++++++++++++++++++ 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 53b73d28966..835dab701f7 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -418,8 +418,7 @@ 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.fast_sage(): d = g[0] if d == 0: parent._make_Ray(self, g[1:]) @@ -427,8 +426,7 @@ def _init_Vrepresentation_from_polymake(self): 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.fast_sage(): d = g[0] if d == 0: parent._make_Line(self, g[1:]) @@ -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.fast_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.fast_sage(): + parent._make_Equation(self, g) self._Hrepresentation = tuple(self._Hrepresentation) @classmethod diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 3b83652a583..beb8e74225c 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -1544,6 +1544,54 @@ def _sage_(self): else: return super(PolymakeElement, self)._sage_() + def fast_sage(self): + r""" + Convert ``self`` quickly to a sage object. + + Not as clean as :meth:`_sage_`. + """ + T1, T2 = self.typeof() + self._check_valid() + if 'Sparse' in T1: + return self._sage_() + + 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: + return self._sage_() + + 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')]) + else: + return self._sage_() + def _sage_doc_(self): """ EXAMPLES:: From 2ca121eb5c7d1127cc52007d365c21573c5e9286 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 May 2021 19:44:33 +0200 Subject: [PATCH 2/5] try faster method directly in _sage_ --- .../geometry/polyhedron/backend_polymake.py | 8 +- src/sage/interfaces/polymake.py | 90 +++++++++---------- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 835dab701f7..44c7e71a49e 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -418,7 +418,7 @@ def _init_Vrepresentation_from_polymake(self): self._Vrepresentation = [] parent = self.parent() p = self._polymake_polytope - for g in p.VERTICES.fast_sage(): + for g in p.VERTICES.sage(): d = g[0] if d == 0: parent._make_Ray(self, g[1:]) @@ -426,7 +426,7 @@ def _init_Vrepresentation_from_polymake(self): parent._make_Vertex(self, g[1:]) else: raise NotImplementedError("Non-normalized vertex encountered: {}".format(g)) - for g in p.LINEALITY_SPACE.fast_sage(): + for g in p.LINEALITY_SPACE.sage(): d = g[0] if d == 0: parent._make_Line(self, g[1:]) @@ -456,13 +456,13 @@ def _init_Hrepresentation_from_polymake(self): else: self._Hrepresentation = [] parent = self.parent() - for g in p.FACETS.fast_sage(): + 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) - for g in p.AFFINE_HULL.fast_sage(): + for g in p.AFFINE_HULL.sage(): parent._make_Equation(self, g) self._Hrepresentation = tuple(self._Hrepresentation) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index beb8e74225c..94249562c79 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -1514,6 +1514,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: @@ -1544,54 +1586,6 @@ def _sage_(self): else: return super(PolymakeElement, self)._sage_() - def fast_sage(self): - r""" - Convert ``self`` quickly to a sage object. - - Not as clean as :meth:`_sage_`. - """ - T1, T2 = self.typeof() - self._check_valid() - if 'Sparse' in T1: - return self._sage_() - - 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: - return self._sage_() - - 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')]) - else: - return self._sage_() - def _sage_doc_(self): """ EXAMPLES:: From 4a663578407c7e00ea6cad4e65a6c9ca7dbf4600 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 27 May 2021 09:16:41 +0200 Subject: [PATCH 3/5] improved polymake input --- .../geometry/polyhedron/backend_polymake.py | 2 +- src/sage/interfaces/polymake.py | 38 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_polymake.py b/src/sage/geometry/polyhedron/backend_polymake.py index 44c7e71a49e..ee22da06338 100644 --- a/src/sage/geometry/polyhedron/backend_polymake.py +++ b/src/sage/geometry/polyhedron/backend_polymake.py @@ -497,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) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 94249562c79..51e8555a039 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -334,8 +334,42 @@ 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 + + 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) + + 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: + x = to_str(x) + except NotImplementedError: + pass + + return super(PolymakeAbstract, self)._coerce_impl(x, use_special=use_special) def console(self): """ From 543d2115d88cd57dc6939567982840999f20cd0d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 27 May 2021 14:00:39 +0200 Subject: [PATCH 4/5] fix number field input --- src/sage/interfaces/polymake.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 51e8555a039..3e21b76d619 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -336,6 +336,7 @@ def convert(y): return r 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): @@ -346,7 +347,17 @@ def to_str(x): return s if isinstance(x, (Integer, Rational, int)): return '{}'.format(x) + parent = None + try: + parent = x.parent() + except AttributeError: + pass + if is_QuadraticField(parent): + c = x._coefficients() + [0,0] + gen = parent.gen() + r = 'new QuadraticExtension({}, {}, {})'.format(c[0], c[1], gen*gen) + return r try: if x.parent().is_exact(): # No other exact rings are supported. @@ -365,7 +376,7 @@ def to_str(x): # Iteratively calling polymake for conversion takes a long time. # However, it takes iterated arrays of integers, rationals and floats directly. try: - x = to_str(x) + return self.new(to_str(x)) except NotImplementedError: pass From c0eb507ad3f5f277e23173d67ed240ab455d02bb Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 27 May 2021 20:17:27 +0200 Subject: [PATCH 5/5] use `_polymake_init_` for quadratic fields --- src/sage/interfaces/polymake.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 3e21b76d619..aa237caf771 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -354,10 +354,7 @@ def to_str(x): pass if is_QuadraticField(parent): - c = x._coefficients() + [0,0] - gen = parent.gen() - r = 'new QuadraticExtension({}, {}, {})'.format(c[0], c[1], gen*gen) - return r + return x._polymake_init_() try: if x.parent().is_exact(): # No other exact rings are supported.