Skip to content

Commit

Permalink
Trac #30587: Remove import of 'ppl' at startup using lazy_import with…
Browse files Browse the repository at this point in the history
… feature

Currently, sage imports `ppl` (from `pplpy`) at startup, via
`sage.geometry.cone`, `sage.geometry.integral_points`, and
`sage.geometry.lattice_polytopes`.

This should be changed, both
 - as preparation for other backends for polyhedral cones (in particular
`PyNormaliz`)
 - for the modularization of sagelib (#29705).

In addition we add documentation to the option `feature` of lazy import
and while we are at it, use this option for backend normaliz as well.

URL: https://trac.sagemath.org/30587
Reported by: mkoeppe
Ticket author(s): Jonathan Kliem, Matthias Koeppe
Reviewer(s): Matthias Koeppe, Jonathan Kliem
  • Loading branch information
Release Manager committed Dec 13, 2020
2 parents 0f32a15 + 17c4cdb commit 7503467
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 28 deletions.
4 changes: 3 additions & 1 deletion src/sage/features/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,9 @@ def package_systems():
# to obtain system package advice.
try:
proc = run('sage-guess-package-system', shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True, check=True)
_cache_package_systems = [PackageSystem(proc.stdout.strip())]
system_name = proc.stdout.strip()
if system_name != 'unknown':
_cache_package_systems = [PackageSystem(system_name)]
except CalledProcessError:
pass
more_package_systems = [SagePackageSystem(), PipPackageSystem()]
Expand Down
11 changes: 8 additions & 3 deletions src/sage/geometry/cone.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,16 @@
from sage.rings.all import QQ, ZZ
from sage.structure.all import SageObject, parent
from sage.structure.richcmp import richcmp_method, richcmp
from ppl import (C_Polyhedron, Generator_System, Constraint_System,
Linear_Expression, ray as PPL_ray, point as PPL_point,
Poly_Con_Relation)
from sage.geometry.integral_points import parallelotope_points

from sage.misc.lazy_import import lazy_import
from sage.features import PythonModule
lazy_import('ppl', ['C_Polyhedron', 'Generator_System', 'Constraint_System',
'Linear_Expression', 'Poly_Con_Relation'],
feature=PythonModule("ppl", spkg="pplpy"))
lazy_import('ppl', ['ray', 'point'], as_=['PPL_ray', 'PPL_point'],
feature=PythonModule("ppl", spkg="pplpy"))


def is_Cone(x):
r"""
Expand Down
10 changes: 8 additions & 2 deletions src/sage/geometry/lattice_polytope.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,14 @@
from sage.geometry.toric_lattice import ToricLattice, is_ToricLattice
from sage.graphs.graph import DiGraph, Graph
from sage.groups.perm_gps.permgroup_named import SymmetricGroup
from ppl import (C_Polyhedron, Generator_System, Linear_Expression,
point as PPL_point)

from sage.misc.lazy_import import lazy_import
from sage.features import PythonModule
lazy_import('ppl', ['C_Polyhedron', 'Generator_System', 'Linear_Expression'],
feature=PythonModule("ppl", spkg="pplpy"))
lazy_import('ppl', 'point', as_='PPL_point',
feature=PythonModule("ppl", spkg="pplpy"))

from sage.matrix.constructor import matrix
from sage.structure.element import is_Matrix
from sage.misc.all import cached_method, flatten, tmp_filename
Expand Down
26 changes: 10 additions & 16 deletions src/sage/geometry/polyhedron/backend_normaliz.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
from sage.structure.element import Element
from sage.misc.all import cached_method, prod
from sage.features import PythonModule
from sage.misc.lazy_import import lazy_import
lazy_import('PyNormaliz', ['NmzResult', 'NmzCompute', 'NmzCone', 'NmzConeCopy'],
feature=PythonModule("PyNormaliz", spkg="pynormaliz"))

from sage.rings.all import ZZ, QQ
from sage.arith.functions import LCM_list
Expand Down Expand Up @@ -273,11 +276,9 @@ def nfelem_handler(coords):
# coords might be too short which is not accepted by Sage number field
v = list(coords) + [0] * (self._normaliz_field.degree() - len(coords))
return self._normaliz_field(v)

import PyNormaliz
return PyNormaliz.NmzResult(normaliz_cone, property,
RationalHandler=rational_handler,
NumberfieldElementHandler=nfelem_handler)
return NmzResult(normaliz_cone, property,
RationalHandler=rational_handler,
NumberfieldElementHandler=nfelem_handler)

def _init_from_normaliz_cone(self, normaliz_cone, normaliz_field):
"""
Expand Down Expand Up @@ -416,9 +417,7 @@ def _cone_from_normaliz_data(self, data, verbose=False):
if verbose:
print("# Calling {}".format(_format_function_call('PyNormaliz.NmzCone', **data)))

PythonModule("PyNormaliz", spkg="pynormaliz").require()
import PyNormaliz
cone = PyNormaliz.NmzCone(**data)
cone = NmzCone(**data)
assert cone, "{} did not return a cone".format(_format_function_call('PyNormaliz.NmzCone', **data))
return cone

Expand Down Expand Up @@ -1117,11 +1116,9 @@ def _make_normaliz_cone(data, verbose=False):
sage: NmzResult(nmz_cone, "ExtremeRays") # py3 # optional - pynormaliz
[[1, 2, 0], [2, 1, 0]]
"""
PythonModule("PyNormaliz", spkg="pynormaliz").require()
import PyNormaliz
if verbose:
print("# Calling PyNormaliz.NmzCone(**{})".format(data))
cone = PyNormaliz.NmzCone(**data)
cone = NmzCone(**data)
assert cone, "NmzCone(**{}) did not return a cone".format(data)
return cone

Expand Down Expand Up @@ -1253,8 +1250,7 @@ def __copy__(self):

# Make a copy of the cone.
cone = self._normaliz_cone
import PyNormaliz
conecopy = PyNormaliz.NmzConeCopy(cone)
conecopy = NmzConeCopy(cone)
other._normaliz_cone = conecopy
return other

Expand Down Expand Up @@ -1803,9 +1799,7 @@ def _ehrhart_quasipolynomial_normaliz(self, variable='t'):
"""
cone = self._normaliz_cone
# Normaliz needs to compute the EhrhartSeries first
PythonModule("PyNormaliz", spkg="pynormaliz").require()
import PyNormaliz
assert PyNormaliz.NmzCompute(cone, ["EhrhartSeries"])
assert NmzCompute(cone, ["EhrhartSeries"])
e = self._nmz_result(cone, "EhrhartQuasiPolynomial")

from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
Expand Down
11 changes: 6 additions & 5 deletions src/sage/geometry/polyhedron/backend_ppl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@
from sage.rings.integer import Integer
from sage.arith.functions import LCM_list
from sage.misc.functional import denominator
from ppl import (
C_Polyhedron, Constraint_System, Generator_System,
Linear_Expression,
line, ray, point
)
from .base import Polyhedron_base
from .base_QQ import Polyhedron_QQ
from .base_ZZ import Polyhedron_ZZ

from sage.misc.lazy_import import lazy_import
from sage.features import PythonModule
lazy_import('ppl', ['C_Polyhedron', 'Generator_System', 'Constraint_System',
'Linear_Expression', 'line', 'ray', 'point'],
feature=PythonModule("ppl", spkg="pplpy"))


#########################################################################
class Polyhedron_ppl(Polyhedron_base):
Expand Down
25 changes: 24 additions & 1 deletion src/sage/misc/lazy_import.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,11 @@ cdef class LazyImport(object):
sage: repr(lazy_ZZ)
'Integer Ring'
"""
return repr(self.get_object())
try:
obj = self.get_object()
return repr(obj)
except FeatureNotPresentError as e:
return "Failed lazy import:\n" + str(e)

def __str__(self):
"""
Expand Down Expand Up @@ -990,6 +994,9 @@ def lazy_import(module, names, as_=None, *,
``deprecation`` should be either a trac number (integer) or a
pair ``(trac_number, message)``
- ``feature`` -- a python module (optional), if it cannot be imported
an appropriate error is raised
.. SEEALSO:: :mod:`sage.misc.lazy_import`, :class:`LazyImport`
EXAMPLES::
Expand Down Expand Up @@ -1052,6 +1059,22 @@ def lazy_import(module, names, as_=None, *,
doctest:...: DeprecationWarning: This is an example.
See http://trac.sagemath.org/14275 for details.
5-adic Field with capped relative precision 20
An example of an import relying on a feature::
sage: from sage.features import PythonModule
sage: lazy_import('ppl', 'equation', feature=PythonModule('ppl', spkg='pplpy'))
sage: equation
<built-in function equation>
sage: lazy_import('PyNormaliz', 'NmzListConeProperties', feature=PythonModule('PyNormaliz', spkg='pynormaliz')) # optional - pynormaliz
sage: NmzListConeProperties # optional - pynormaliz
<built-in function NmzListConeProperties>
sage: lazy_import('foo', 'not_there', feature=PythonModule('foo', spkg='non-existing-package'))
sage: not_there
Failed lazy import:
foo is not available.
Importing not_there failed: No module named 'foo'...
No equivalent system packages for ... are known to Sage...
"""
if as_ is None:
as_ = names
Expand Down

0 comments on commit 7503467

Please sign in to comment.