Ejemplo n.º 1
0
def Polyhedra(ambient_space_or_base_ring=None,
              ambient_dim=None,
              backend=None,
              *,
              ambient_space=None,
              base_ring=None):
    r"""
    Construct a suitable parent class for polyhedra

    INPUT:

    - ``base_ring`` -- A ring. Currently there are backends for `\ZZ`,
      `\QQ`, and `\RDF`.

    - ``ambient_dim`` -- integer. The ambient space dimension.

    - ``ambient_space`` -- A free module.

    - ``backend`` -- string. The name of the backend for computations. There are
       several backends implemented:

         * ``backend="ppl"`` uses the Parma Polyhedra Library

         * ``backend="cdd"`` uses CDD

         * ``backend="normaliz"`` uses normaliz

         * ``backend="polymake"`` uses polymake

         * ``backend="field"`` a generic Sage implementation

    OUTPUT:

    A parent class for polyhedra over the given base ring if the
    backend supports it. If not, the parent base ring can be larger
    (for example, `\QQ` instead of `\ZZ`). If there is no
    implementation at all, a ``ValueError`` is raised.

    EXAMPLES::

        sage: from sage.geometry.polyhedron.parent import Polyhedra
        sage: Polyhedra(AA, 3)
        Polyhedra in AA^3
        sage: Polyhedra(ZZ, 3)
        Polyhedra in ZZ^3
        sage: type(_)
        <class 'sage.geometry.polyhedron.parent.Polyhedra_ZZ_ppl_with_category'>
        sage: Polyhedra(QQ, 3, backend='cdd')
        Polyhedra in QQ^3
        sage: type(_)
        <class 'sage.geometry.polyhedron.parent.Polyhedra_QQ_cdd_with_category'>

    CDD does not support integer polytopes directly::

        sage: Polyhedra(ZZ, 3, backend='cdd')
        Polyhedra in QQ^3

    Using a more general form of the constructor::

        sage: V = VectorSpace(QQ, 3)
        sage: Polyhedra(V) is Polyhedra(QQ, 3)
        True
        sage: Polyhedra(V, backend='field') is Polyhedra(QQ, 3, 'field')
        True
        sage: Polyhedra(backend='field', ambient_space=V) is Polyhedra(QQ, 3, 'field')
        True

        sage: M = FreeModule(ZZ, 2)
        sage: Polyhedra(M, backend='ppl') is Polyhedra(ZZ, 2, 'ppl')
        True

    TESTS::

        sage: Polyhedra(RR, 3, backend='field')
        Traceback (most recent call last):
        ...
        ValueError: the 'field' backend for polyhedron cannot be used with non-exact fields
        sage: Polyhedra(RR, 3)
        Traceback (most recent call last):
        ...
        ValueError: no default backend for computations with Real Field with 53 bits of precision
        sage: Polyhedra(QQ[I], 2)
        Traceback (most recent call last):
        ...
        ValueError: invalid base ring: Number Field in I with defining polynomial x^2 + 1 with I = 1*I cannot be coerced to a real field
        sage: Polyhedra(AA, 3, backend='polymake')  # optional - polymake
        Traceback (most recent call last):
        ...
        ValueError: the 'polymake' backend for polyhedron cannot be used with Algebraic Real Field

        sage: Polyhedra(QQ, 2, backend='normaliz')   # optional - pynormaliz
        Polyhedra in QQ^2
        sage: Polyhedra(SR, 2, backend='normaliz')   # optional - pynormaliz  # optional - sage.symbolic
        Polyhedra in (Symbolic Ring)^2
        sage: SCR = SR.subring(no_variables=True)                             # optional - sage.symbolic
        sage: Polyhedra(SCR, 2, backend='normaliz')  # optional - pynormaliz  # optional - sage.symbolic
        Polyhedra in (Symbolic Constants Subring)^2
    """
    if ambient_space_or_base_ring is not None:
        if ambient_space_or_base_ring in Rings():
            base_ring = ambient_space_or_base_ring
        else:
            ambient_space = ambient_space_or_base_ring
    if ambient_space is not None:
        if ambient_space not in Modules:
            # There is no category of free modules, unfortunately
            # (see https://trac.sagemath.org/ticket/30164)...
            raise ValueError('ambient_space must be a free module')
        if base_ring is None:
            base_ring = ambient_space.base_ring()
        if ambient_dim is None:
            try:
                ambient_dim = ambient_space.rank()
            except AttributeError:
                # ... so we test whether it is free using the existence of
                # a rank method
                raise ValueError('ambient_space must be a free module')
        if ambient_space is not FreeModule(base_ring, ambient_dim):
            raise NotImplementedError(
                'ambient_space must be a standard free module')
    if backend is None:
        if base_ring is ZZ or base_ring is QQ:
            backend = 'ppl'
        elif base_ring is RDF:
            backend = 'cdd'
        elif base_ring.is_exact():
            # TODO: find a more robust way of checking that the coefficients are indeed
            # real numbers
            if not RDF.has_coerce_map_from(base_ring):
                raise ValueError(
                    "invalid base ring: {} cannot be coerced to a real field".
                    format(base_ring))
            backend = 'field'
        else:
            raise ValueError(
                "no default backend for computations with {}".format(
                    base_ring))

    try:
        from sage.symbolic.ring import SR
    except ImportError:
        SR = None
    if backend == 'ppl' and base_ring is QQ:
        return Polyhedra_QQ_ppl(base_ring, ambient_dim, backend)
    elif backend == 'ppl' and base_ring is ZZ:
        return Polyhedra_ZZ_ppl(base_ring, ambient_dim, backend)
    elif backend == 'normaliz' and base_ring is QQ:
        return Polyhedra_QQ_normaliz(base_ring, ambient_dim, backend)
    elif backend == 'normaliz' and base_ring is ZZ:
        return Polyhedra_ZZ_normaliz(base_ring, ambient_dim, backend)
    elif backend == 'normaliz' and (isinstance(
            base_ring, sage.rings.abc.SymbolicRing) or base_ring.is_exact()):
        return Polyhedra_normaliz(base_ring, ambient_dim, backend)
    elif backend == 'cdd' and base_ring in (ZZ, QQ):
        return Polyhedra_QQ_cdd(QQ, ambient_dim, backend)
    elif backend == 'cdd' and base_ring is RDF:
        return Polyhedra_RDF_cdd(RDF, ambient_dim, backend)
    elif backend == 'polymake':
        base_field = base_ring.fraction_field()
        try:
            from sage.interfaces.polymake import polymake
            polymake_base_field = polymake(base_field)
        except TypeError:
            raise ValueError(
                f"the 'polymake' backend for polyhedron cannot be used with {base_field}"
            )
        return Polyhedra_polymake(base_field, ambient_dim, backend)
    elif backend == 'field':
        if not base_ring.is_exact():
            raise ValueError(
                "the 'field' backend for polyhedron cannot be used with non-exact fields"
            )
        return Polyhedra_field(base_ring.fraction_field(), ambient_dim,
                               backend)
    else:
        raise ValueError('No such backend (=' + str(backend) +
                         ') implemented for given basering (=' +
                         str(base_ring) + ').')