예제 #1
0
    def __init__(self, R, element_class=None, names=None):
        """
        TESTS::

            sage: from sage.combinat.species.series import LazyPowerSeriesRing
            sage: L = LazyPowerSeriesRing(QQ)

        Equality testing is undecidable in general, and not much
        efforts are done at this stage to implement equality when
        possible. Hence the failing tests below::

            sage: TestSuite(L).run()
            Failure in ...
            The following tests failed: _test_additive_associativity, _test_associativity, _test_distributivity, _test_elements, _test_one, _test_prod, _test_zero

        """
        #Make sure R is a ring with unit element
        if R not in Rings():
            raise TypeError("Argument R must be a ring.")

        #Take care of the names
        if names is None:
            names = 'x'
        else:
            names = names[0]

        self._element_class = element_class if element_class is not None else LazyPowerSeries
        self._order = None
        self._name = names
        sage.structure.parent_base.ParentWithBase.__init__(self, R, category=Rings())
예제 #2
0
    def _element_constructor_(self, x, check=True):
        """
        Construct an element of ``self`` from ``x``.

        EXAMPLES::

            sage: H = Hom(QuadraticField(-1, 'a'), QuadraticField(-1, 'b'))
            sage: phi = H([H.domain().gen()]); phi
            Ring morphism:
              From: Number Field in a with defining polynomial x^2 + 1 with a = 1*I
              To:   Number Field in b with defining polynomial x^2 + 1 with b = 1*I
              Defn: a |--> b
            sage: H1 = End(QuadraticField(-1, 'a'))
            sage: H1.coerce(loads(dumps(H1[1])))
            Ring endomorphism of Number Field in a with defining polynomial x^2 + 1 with a = 1*I
              Defn: a |--> -a

        TESTS:

        We can move morphisms between categories::

            sage: f = H1.an_element()
            sage: g = End(H1.domain(), category=Rings())(f)
            sage: f == End(H1.domain(), category=NumberFields())(g)
            True

        Check that :trac:`28869` is fixed::

            sage: K.<a> = CyclotomicField(8)
            sage: L.<b> = K.absolute_field()
            sage: H = L.Hom(K)
            sage: phi = L.structure()[0]
            sage: phi.parent() is H
            True
            sage: H(phi)
            Isomorphism given by variable name change map:
              From: Number Field in b with defining polynomial x^4 + 1
              To:   Cyclotomic Field of order 8 and degree 4
            sage: R.<x> = L[]
            sage: (x^2 + b).change_ring(phi)
            x^2 + a
        """
        if not isinstance(x, NumberFieldHomomorphism_im_gens):
            return self.element_class(self, x, check=check)
        from sage.categories.all import NumberFields, Rings
        if (x.parent() == self or
            (x.domain() == self.domain() and x.codomain() == self.codomain()
             and
             # This would be the better check, however it returns False currently:
             # self.homset_category().is_full_subcategory(x.category_for())
             # So we check instead that this is a morphism anywhere between
             # Rings and NumberFields where the hom spaces do not change.
             NumberFields().is_subcategory(self.homset_category())
             and self.homset_category().is_subcategory(Rings())
             and NumberFields().is_subcategory(x.category_for())
             and x.category_for().is_subcategory(Rings()))):
            return self.element_class(self, x.im_gens(), check=False)
예제 #3
0
    def __init__(self, group) :
        r"""
        See :class:`GroupAlgebraFunctor` for full documentation.

        EXAMPLES::

            sage: from sage.algebras.group_algebra_new import GroupAlgebraFunctor
            sage: GroupAlgebra(SU(2, GF(4, 'a')), IntegerModRing(12)).category()
            Category of hopf algebras with basis over Ring of integers modulo 12
        """
        self.__group = group

        ConstructionFunctor.__init__(self, Rings(), Rings())
예제 #4
0
    def __init__(self, R, names=None, element_class=None):
        """
        TESTS::

            sage: from sage.combinat.species.series import LazyPowerSeriesRing
            sage: L = LazyPowerSeriesRing(QQ)

        Equality testing is undecidable in general, and not much
        efforts are done at this stage to implement equality when
        possible. Hence the failing tests below::

            sage: TestSuite(L).run()
            Failure in ...
            The following tests failed: _test_additive_associativity, _test_associativity, _test_distributivity, _test_elements, _test_one, _test_prod, _test_zero

        ::

            sage: LazyPowerSeriesRing(QQ, 'z').gen()
            z
            sage: LazyPowerSeriesRing(QQ, ['z']).gen()
            z
            sage: LazyPowerSeriesRing(QQ, ['x', 'z'])
            Traceback (most recent call last):
            ...
            NotImplementedError: only univariate lazy power series rings are supported
        """
        #Make sure R is a ring with unit element
        if R not in Rings():
            raise TypeError("Argument R must be a ring.")

        #Take care of the names
        if names is None:
            names = 'x'
        elif isinstance(names, (list, tuple)):
            if len(names) != 1:
                raise NotImplementedError(
                    'only univariate lazy power series rings are supported')
            names = names[0]
        else:
            names = str(names)

        self._element_class = element_class if element_class is not None else LazyPowerSeries
        self._order = None
        self._name = names
        self._zero_base_ring = R.zero()
        sage.structure.parent_base.ParentWithBase.__init__(self,
                                                           R,
                                                           category=Rings())
예제 #5
0
    def _apply_functor_to_morphism(self, f):
        r"""
        Lift a homomorphism of rings to the corresponding homomorphism of the group algebras of ``self.group()``.

        INPUT:

        - ``f`` - a morphism of rings.

        OUTPUT:

        A morphism of group algebras.

        EXAMPLES::

            sage: G = SymmetricGroup(3)
            sage: A = GroupAlgebra(G, ZZ)
            sage: h = sage.categories.morphism.SetMorphism(Hom(ZZ, GF(5), Rings()), lambda x: GF(5)(x))
            sage: hh = A.construction()[0](h)
            sage: hh(A.0 + 5 * A.1)
            (1,2,3)
        """
        codomain = self(f.codomain())
        return SetMorphism(
            Hom(self(f.domain()), codomain, Rings()), lambda x: sum(
                codomain(g) * f(c) for (g, c) in six.iteritems(dict(x))))
예제 #6
0
    def __init__(self, R, element_class = None, names=None):
        """
        TESTS::

            sage: from sage.combinat.species.series import LazyPowerSeriesRing
            sage: L = LazyPowerSeriesRing(QQ)
            sage: loads(dumps(L))
            Lazy Power Series Ring over Rational Field
        """
        #Make sure R is a ring with unit element
        if not R in Rings():
            raise TypeError, "Argument R must be a ring."
        try:
            z = R(Integer(1))
        except Exception:
            raise ValueError, "R must have a unit element"

        #Take care of the names
        if names is None:
            names = 'x'
        else:
            names = names[0]

        self._element_class = element_class if element_class is not None else LazyPowerSeries
        self._order = None
        self._name = names
        sage.structure.parent_base.ParentWithBase.__init__(self, R)
예제 #7
0
    def hom(self, x, Y=None):
        r"""
        Return the scheme morphism from ``self`` to ``Y`` defined by ``x``.

        INPUT:

        - ``x`` -- anything that determines a scheme morphism; if
          ``x`` is a scheme, try to determine a natural map to ``x``

        - ``Y`` -- the codomain scheme (optional); if ``Y`` is not
          given, try to determine ``Y`` from context

        - ``check`` -- boolean (optional, default: ``True``); whether
          to check the defining data for consistency

        OUTPUT:

        The scheme morphism from ``self`` to ``Y`` defined by ``x``.

        EXAMPLES:

        We construct the inclusion from `\mathrm{Spec}(\QQ)` into
        `\mathrm{Spec}(\ZZ)` induced by the inclusion from `\ZZ` into
        `\QQ`::

            sage: X = Spec(QQ)
            sage: X.hom(ZZ.hom(QQ))
            Affine Scheme morphism:
              From: Spectrum of Rational Field
              To:   Spectrum of Integer Ring
              Defn: Natural morphism:
                      From: Integer Ring
                      To:   Rational Field

        TESTS:

        We can construct a morphism to an affine curve (:trac:`7956`)::

            sage: S.<p,q> = QQ[]
            sage: A1.<r> = AffineSpace(QQ,1)
            sage: A1_emb = Curve(p-2)
            sage: A1.hom([2,r],A1_emb)
            Scheme morphism:
              From: Affine Space of dimension 1 over Rational Field
              To:   Affine Plane Curve over Rational Field defined by p - 2
              Defn: Defined on coordinates by sending (r) to
                    (2, r)
        """
        from sage.categories.map import Map
        from sage.categories.all import Rings

        if is_Scheme(x):
            return self.Hom(x).natural_map()
        if Y is None and isinstance(
                x, Map) and x.category_for().is_subcategory(Rings()):
            # x is a morphism of Rings
            Y = AffineScheme(x.domain())
        return Scheme.hom(self, x, Y)
예제 #8
0
파일: morphism.py 프로젝트: shalec/sage
    def _coerce_impl(self, x):
        r"""
        Canonical coercion of ``x`` into this homset. The only things that
        coerce canonically into self are elements of self and of homsets equal
        to self.

        EXAMPLES::

            sage: H1 = End(QuadraticField(-1, 'a'))
            sage: H1.coerce(loads(dumps(H1[1]))) # indirect doctest
            Ring endomorphism of Number Field in a with defining polynomial x^2 + 1
              Defn: a |--> -a

        TESTS:

        We can move morphisms between categories::

            sage: f = H1.an_element()
            sage: g = End(H1.domain(), category=Rings())(f)
            sage: f == End(H1.domain(), category=NumberFields())(g)
            True

        """
        if not isinstance(x, NumberFieldHomomorphism_im_gens):
            raise TypeError
        if x.parent() is self:
            return x
        from sage.categories.all import NumberFields, Rings
        if (x.parent() == self or
            (x.domain() == self.domain() and x.codomain() == self.codomain()
             and
             # This would be the better check, however it returns False currently:
             # self.homset_category().is_full_subcategory(x.category_for())
             # So we check instead that this is a morphism anywhere between
             # Rings and NumberFields where the hom spaces do not change.
             NumberFields().is_subcategory(self.homset_category())
             and self.homset_category().is_subcategory(Rings())
             and NumberFields().is_subcategory(x.category_for())
             and x.category_for().is_subcategory(Rings()))):
            return NumberFieldHomomorphism_im_gens(self,
                                                   x.im_gens(),
                                                   check=False)
        raise TypeError
    def __init__(self, poly_ring):
        from sage.modules.free_module import FreeModule
        self._polynomial_ring = poly_ring
        dim = ZZ(poly_ring.ngens())
        self._free_module = FreeModule(ZZ, dim)

        # univariate extension of the polynomial ring
        # (needed in several algorithms)
        self._polynomial_ring_extra_var = self._polynomial_ring['EXTRA_VAR']

        Parent.__init__(self, category=Rings(), base=poly_ring.base_ring())
예제 #10
0
파일: callable.py 프로젝트: epettigrew/sage
    def __init__(self, arguments):
        """
        A functor which produces a CallableSymbolicExpressionRing from
        the SymbolicRing.

        EXAMPLES::

            sage: from sage.symbolic.callable import CallableSymbolicExpressionFunctor
            sage: x,y = var('x,y')
            sage: f = CallableSymbolicExpressionFunctor((x,y)); f
            CallableSymbolicExpressionFunctor(x, y)
            sage: f(SR)
            Callable function ring with arguments (x, y)

            sage: loads(dumps(f))
            CallableSymbolicExpressionFunctor(x, y)
        """
        self._arguments = arguments
        from sage.categories.all import Rings
        self.rank = 3
        ConstructionFunctor.__init__(self, Rings(), Rings())
예제 #11
0
    def is_locally_solvable(self, p) -> bool:
        r"""
        Return ``True`` if and only if ``self`` has a solution over the
        `p`-adic numbers.

        Here `p` is a prime number or equals
        `-1`, infinity, or `\RR` to denote the infinite place.

        EXAMPLES::

            sage: C = Conic(QQ, [1,2,3])
            sage: C.is_locally_solvable(-1)
            False
            sage: C.is_locally_solvable(2)
            False
            sage: C.is_locally_solvable(3)
            True
            sage: C.is_locally_solvable(QQ.hom(RR))
            False
            sage: D = Conic(QQ, [1, 2, -3])
            sage: D.is_locally_solvable(infinity)
            True
            sage: D.is_locally_solvable(RR)
            True
        """
        from sage.categories.map import Map
        from sage.categories.all import Rings

        D, T = self.diagonal_matrix()
        abc = [D[j, j] for j in range(3)]
        if abc[2] == 0:
            return True
        a = -abc[0] / abc[2]
        b = -abc[1] / abc[2]
        if isinstance(p, sage.rings.abc.RealField) or isinstance(
                p, InfinityElement):
            p = -1
        elif isinstance(p, Map) and p.category_for().is_subcategory(Rings()):
            # p is a morphism of Rings
            if p.domain() is QQ and isinstance(p.codomain(),
                                               sage.rings.abc.RealField):
                p = -1
            else:
                raise TypeError("p (=%s) needs to be a prime of base field "
                                "B ( =`QQ`) in is_locally_solvable" % p)
        if hilbert_symbol(a, b, p) == -1:
            if self._local_obstruction is None:
                self._local_obstruction = p
            return False
        return True
예제 #12
0
    def is_locally_solvable(self, p):
        r"""
        Returns ``True`` if and only if ``self`` has a solution over the
        completion of the base field `B` of ``self`` at ``p``. Here ``p``
        is a finite prime or infinite place of `B`.

        EXAMPLES::

            sage: P.<x> = QQ[]
            sage: K.<a> = NumberField(x^3 + 5)
            sage: C = Conic(K, [1, 2, 3 - a])
            sage: [p1, p2] = K.places()
            sage: C.is_locally_solvable(p1)
            False

            sage: C.is_locally_solvable(p2)
            True

            sage: O = K.maximal_order()
            sage: f = (2*O).factor()
            sage: C.is_locally_solvable(f[0][0])
            True

            sage: C.is_locally_solvable(f[1][0])
            False
        """
        D, T = self.diagonal_matrix()
        abc = [D[j, j] for j in range(3)]
        for a in abc:
            if a == 0:
                return True
        a = -abc[0] / abc[2]
        b = -abc[1] / abc[2]

        ret = self.base_ring().hilbert_symbol(a, b, p)

        if ret == -1:
            if self._local_obstruction is None:
                from sage.categories.map import Map
                from sage.categories.all import Rings
                from sage.rings.qqbar import AA
                from sage.rings.real_lazy import RLF

                if not (isinstance(p, Map) and p.category_for().is_subcategory(
                        Rings())) or p.codomain() is AA or p.codomain() is RLF:
                    self._local_obstruction = p
            return False

        return True
예제 #13
0
    def __init__(self, R, S):
        r"""
        EXAMPLES::

            sage: from sage.categories.examples.with_realizations import SubsetAlgebra
            sage: A = SubsetAlgebra(QQ, Set((1,2,3))); A
            The subset algebra of {1, 2, 3} over Rational Field
            sage: Sets().WithRealizations().example() is A
            True
            sage: TestSuite(A).run()
        """
        assert (R in Rings())
        self._base = R  # Won't be needed when CategoryObject won't override anymore base_ring
        self._S = S
        Parent.__init__(self, category=Algebras(R).WithRealizations())
예제 #14
0
    def __init__(self, R):
        """
        EXAMPLES::

            sage: Sym = SymmetricFunctions(QQ)

        TESTS:

        There are a lot of missing features for this abstract parent. But some tests do pass::

            sage: TestSuite(Sym).run()
            Failure ...
            The following tests failed: _test_additive_associativity, _test_an_element, _test_associativity, _test_distributivity, _test_elements, _test_elements_eq, _test_not_implemented_methods, _test_one, _test_prod, _test_some_elements, _test_zero
        """
        assert(R in Rings())
        self._base = R # Won't be needed when CategoryObject won't override anymore base_ring
        Parent.__init__(self, category = GradedHopfAlgebrasWithBasis(R).abstract_category())
예제 #15
0
    def __init__(self, X=None, category=None):
        """
        Construct a scheme.

        TESTS:

        The full test suite works since :trac:`7946`::

            sage: R.<x, y> = QQ[]
            sage: I = (x^2 - y^2)*R
            sage: RmodI = R.quotient(I)
            sage: X = Spec(RmodI)
            sage: TestSuite(X).run()

        """
        from sage.schemes.generic.morphism import is_SchemeMorphism
        from sage.categories.map import Map
        from sage.categories.all import Rings

        if X is None:
            self._base_ring = ZZ
        elif is_Scheme(X):
            self._base_scheme = X
        elif is_SchemeMorphism(X):
            self._base_morphism = X
        elif isinstance(X, CommutativeRing):
            self._base_ring = X
        elif isinstance(X, Map) and X.category_for().is_subcategory(Rings()):
            # X is a morphism of Rings
            self._base_ring = X.codomain()
        else:
            raise ValueError('The base must be define by a scheme, '
                             'scheme morphism, or commutative ring.')

        from sage.categories.schemes import Schemes
        if X is None:
            default_category = Schemes()
        else:
            default_category = Schemes(self.base_scheme())
        if category is None:
            category = default_category
        else:
            assert category.is_subcategory(default_category), \
                "%s is not a subcategory of %s"%(category, default_category)

        Parent.__init__(self, self.base_ring(), category=category)
예제 #16
0
    def __init__(self, R):
        r"""
        Initialization of ``self``.

        INPUT:

        - ``R`` -- a ring

        EXAMPLES::

            sage: Sym = SymmetricFunctions(QQ)

        TESTS::

            sage: TestSuite(Sym).run()

        """
        assert (R in Rings())
        self._base = R  # Won't be needed when CategoryObject won't override anymore base_ring
        Parent.__init__(self,
                        category=GradedHopfAlgebras(R).WithRealizations())
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#                  https://www.gnu.org/licenses/
# ****************************************************************************

from sage.all import cached_method, ZZ, latex, diff, prod, parent

from sage.categories.all import Morphism, Rings
from sage.categories.pushout import ConstructionFunctor, pushout
from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing_dense, InfinitePolynomialRing_sparse
from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_dense
from sage.structure.factory import UniqueFactory  #pylint: disable=no-name-in-module

from .differential_polynomial_element import DiffPolynomial, DiffPolynomialGen

_Rings = Rings.__classcall__(Rings)


class DiffPolynomialRingFactory(UniqueFactory):
    r'''
        Factory to create rings of differential polynomials.ss

        This allows to cache the same rings of differential polynomials created from different
        objects. 

        EXAMPLES::

                sage: from dalgebra.differential_polynomial.differential_polynomial_ring import *
                sage: R.<y> = DiffPolynomialRing(QQ['x']); R
                Ring of differential polynomials in (y) over [Univariate Polynomial Ring in x over Rational Field]
                sage: S = DiffPolynomialRing(QQ['x'], 'y')
예제 #18
0
    def _call_(self, x):
        """
        Construct a scheme from the data in ``x``

        EXAMPLES:

        Let us first construct the category of schemes::

            sage: S = Schemes(); S
            Category of schemes

        We create a scheme from a ring::

            sage: X = S(ZZ); X                  # indirect doctest
            Spectrum of Integer Ring

        We create a scheme from a scheme (do nothing)::

            sage: S(X)
            Spectrum of Integer Ring

        We create a scheme morphism from a ring homomorphism.x::

            sage: phi = ZZ.hom(QQ); phi
            Natural morphism:
              From: Integer Ring
              To:   Rational Field
            sage: f = S(phi); f                 # indirect doctest
            Affine Scheme morphism:
              From: Spectrum of Rational Field
              To:   Spectrum of Integer Ring
              Defn: Natural morphism:
                      From: Integer Ring
                      To:   Rational Field

            sage: f.domain()
            Spectrum of Rational Field
            sage: f.codomain()
            Spectrum of Integer Ring
            sage: S(f)                          # indirect doctest
            Affine Scheme morphism:
              From: Spectrum of Rational Field
              To:   Spectrum of Integer Ring
              Defn: Natural morphism:
                      From: Integer Ring
                      To:   Rational Field

        """
        from sage.schemes.generic.scheme import is_Scheme
        if is_Scheme(x):
            return x
        from sage.schemes.generic.morphism import is_SchemeMorphism
        if is_SchemeMorphism(x):
            return x
        from sage.rings.ring import CommutativeRing
        from sage.schemes.generic.spec import Spec
        from sage.categories.map import Map
        from sage.categories.all import Rings
        if isinstance(x, CommutativeRing):
            return Spec(x)
        elif isinstance(x, Map) and x.category_for().is_subcategory(Rings()):
            # x is a morphism of Rings
            A = Spec(x.codomain())
            return A.hom(x)
        else:
            raise TypeError("No way to create an object or morphism in %s from %s"%(self, x))
예제 #19
0
    def __init__(self, R, S):
        r"""
        EXAMPLES::

            sage: from sage.categories.examples.with_realizations import SubsetAlgebra
            sage: A = SubsetAlgebra(QQ, Set((1,2,3))); A
            The subset algebra of {1, 2, 3} over Rational Field
            sage: Sets().WithRealizations().example() is A
            True
            sage: TestSuite(A).run()

        TESTS::

            sage: A = Sets().WithRealizations().example(); A
            The subset algebra of {1, 2, 3} over Rational Field
            sage: F, In, Out = A.realizations()
            sage: type(F.coerce_map_from(In))
            <class 'sage.categories.modules_with_basis.TriangularModuleMorphism'>
            sage: type(In.coerce_map_from(F))
            <class 'sage.categories.modules_with_basis.TriangularModuleMorphism'>
            sage: type(F.coerce_map_from(Out))
            <class 'sage.categories.modules_with_basis.TriangularModuleMorphism'>
            sage: type(Out.coerce_map_from(F))
            <class 'sage.categories.modules_with_basis.TriangularModuleMorphism'>
            sage: In.coerce_map_from(Out)
            Composite map:
              From: The subset algebra of {1, 2, 3} over Rational Field in the Out basis
              To:   The subset algebra of {1, 2, 3} over Rational Field in the In basis
              Defn:   Generic morphism:
                      From: The subset algebra of {1, 2, 3} over Rational Field in the Out basis
                      To:   The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis
                    then
                      Generic morphism:
                      From: The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis
                      To:   The subset algebra of {1, 2, 3} over Rational Field in the In basis
            sage: Out.coerce_map_from(In)
            Composite map:
              From: The subset algebra of {1, 2, 3} over Rational Field in the In basis
              To:   The subset algebra of {1, 2, 3} over Rational Field in the Out basis
              Defn:   Generic morphism:
                      From: The subset algebra of {1, 2, 3} over Rational Field in the In basis
                      To:   The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis
                    then
                      Generic morphism:
                      From: The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis
                      To:   The subset algebra of {1, 2, 3} over Rational Field in the Out basis
        """
        assert (R in Rings())
        self._base = R  # Won't be needed when CategoryObject won't override anymore base_ring
        self._S = S
        Parent.__init__(self, category=Algebras(R).WithRealizations())

        # Initializes the bases and change of bases of ``self``

        category = self.Bases()
        F = self.F()
        In = self.In()
        Out = self.Out()

        In_to_F = In.module_morphism(F.sum_of_monomials * Subsets,
                                     codomain=F,
                                     category=category,
                                     triangular='upper',
                                     unitriangular=True,
                                     cmp=self.indices_cmp)
        In_to_F.register_as_coercion()
        (~In_to_F).register_as_coercion()

        F_to_Out = F.module_morphism(Out.sum_of_monomials * self.supsets,
                                     codomain=Out,
                                     category=category,
                                     triangular='lower',
                                     unitriangular=True,
                                     cmp=self.indices_cmp)
        F_to_Out.register_as_coercion()
        (~F_to_Out).register_as_coercion()
예제 #20
0
    def _element_constructor_(self, x, check=True):
        """
        Construct a scheme morphism.

        INPUT:

        - `x` -- a ring morphism, or a list or a tuple that define a
          ring morphism.

        - ``check`` -- boolean (default: ``True``) passed onto
          functions called by this one to be more careful about input
          argument type checking.

        EXAMPLES::

            sage: f = ZZ.hom(QQ); f
            Natural morphism:
              From: Integer Ring
              To:   Rational Field

            sage: H = Hom(Spec(QQ, ZZ), Spec(ZZ)); H
            Set of morphisms
              From: Spectrum of Rational Field
              To:   Spectrum of Integer Ring

            sage: phi = H(f); phi
            Affine Scheme morphism:
              From: Spectrum of Rational Field
              To:   Spectrum of Integer Ring
              Defn: Natural morphism:
                      From: Integer Ring
                      To:   Rational Field

        TESTS::

            sage: H._element_constructor_(f)
            Affine Scheme morphism:
              From: Spectrum of Rational Field
              To:   Spectrum of Integer Ring
              Defn: Natural morphism:
                      From: Integer Ring
                      To:   Rational Field

        We illustrate input type checking::

            sage: R.<x,y> = QQ[]
            sage: A.<x,y> = AffineSpace(R)
            sage: C = A.subscheme(x*y-1)
            sage: H = C.Hom(C); H
            Set of morphisms
              From: Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
              x*y - 1
              To:   Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
              x*y - 1
            sage: H(1)
            Traceback (most recent call last):
            ...
            TypeError: x must be a ring homomorphism, list or tuple
        """
        if isinstance(x, (list, tuple)):
            return self.domain()._morphism(self, x, check=check)

        from sage.categories.map import Map
        from sage.categories.all import Rings
        if isinstance(x, Map) and x.category_for().is_subcategory(Rings()):
            # x is a morphism of Rings
            return SchemeMorphism_spec(self, x, check=check)

        raise TypeError("x must be a ring homomorphism, list or tuple")
예제 #21
0
def Matroid(groundset=None, data=None, **kwds):
    r"""
    Construct a matroid.

    Matroids are combinatorial structures that capture the abstract properties
    of (linear/algebraic/...) dependence. Formally, a matroid is a pair
    `M = (E, I)` of a finite set `E`, the *groundset*, and a collection of
    subsets `I`, the independent sets, subject to the following axioms:

    * `I` contains the empty set
    * If `X` is a set in `I`, then each subset of `X` is in `I`
    * If two subsets `X`, `Y` are in `I`, and `|X| > |Y|`, then there exists
      `x \in X - Y` such that `Y + \{x\}` is in `I`.

    See the :wikipedia:`Wikipedia article on matroids <Matroid>` for more
    theory and examples. Matroids can be obtained from many types of
    mathematical structures, and Sage supports a number of them.

    There are two main entry points to Sage's matroid functionality. For
    built-in matroids, do the following:

    * Within a Sage session, type "matroids." (Do not press "Enter", and do
      not forget the final period ".")
    * Hit "tab".

    You will see a list of methods which will construct matroids. For
    example::

        sage: F7 = matroids.named_matroids.Fano()
        sage: len(F7.nonspanning_circuits())
        7

    or::

        sage: U36 = matroids.Uniform(3, 6)
        sage: U36.equals(U36.dual())
        True

    To define your own matroid, use the function ``Matroid()``.
    This function attempts to interpret its arguments to create an appropriate
    matroid. The following named arguments are supported:

    INPUT:

    - ``groundset`` -- (optional) If provided, the groundset of the
      matroid. Otherwise, the function attempts to determine a groundset
      from the data.

    Exactly one of the following inputs must be given (where ``data``
    must be a positional argument and anything else must be a keyword
    argument):

    - ``data`` -- a graph or a matrix or a RevLex-Index string or a list
      of independent sets containing all bases or a matroid.
    - ``bases`` -- The list of bases (maximal independent sets) of the
      matroid.
    - ``independent_sets`` -- The list of independent sets of the matroid.
    - ``circuits`` -- The list of circuits of the matroid.
    - ``graph`` -- A graph, whose edges form the elements of the matroid.
    - ``matrix`` -- A matrix representation of the matroid.
    - ``reduced_matrix`` -- A reduced representation of the matroid: if
      ``reduced_matrix = A``
      then the matroid is represented by `[I\ \ A]` where `I` is an
      appropriately sized identity matrix.
    - ``rank_function`` -- A function that computes the rank of each subset.
      Can only be provided together with a groundset.
    - ``circuit_closures`` -- Either a list of tuples ``(k, C)`` with ``C``
      the closure of a circuit, and ``k`` the rank of ``C``, or a dictionary
      ``D`` with ``D[k]`` the set of closures of rank-``k`` circuits.
    - ``revlex`` -- the encoding as a string of ``0`` and ``*`` symbols.
      Used by [MatroidDatabase]_ and explained in [MMIB2012]_.
    - ``matroid`` -- An object that is already a matroid. Useful only with the
      ``regular`` option.

    Further options:

    - ``regular`` -- (default: ``False``) boolean. If ``True``,
      output a
      :class:`RegularMatroid <sage.matroids.linear_matroid.RegularMatroid>`
      instance such that, *if* the input defines a valid regular matroid, then
      the output represents this matroid. Note that this option can be
      combined with any type of input.
    - ``ring`` -- any ring. If provided, and the input is a ``matrix`` or
      ``reduced_matrix``, output will be a linear matroid over the ring or
      field ``ring``.
    - ``field`` -- any field. Same as ``ring``, but only fields are allowed.
    - ``check`` -- (default: ``True``) boolean. If ``True`` and
      ``regular`` is true, the output is checked to make sure it is a valid
      regular matroid.

    .. WARNING::

        Except for regular matroids, the input is not checked for validity. If
        your data does not correspond to an actual matroid, the behavior of
        the methods is undefined and may cause strange errors. To ensure you
        have a matroid, run
        :meth:`M.is_valid() <sage.matroids.matroid.Matroid.is_valid>`.

    .. NOTE::

        The ``Matroid()`` method will return instances of type
        :class:`BasisMatroid <sage.matroids.basis_matroid.BasisMatroid>`,
        :class:`CircuitClosuresMatroid <sage.matroids.circuit_closures_matroid.CircuitClosuresMatroid>`,
        :class:`LinearMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`BinaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`TernaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`QuaternaryMatroid <sage.matroids.linear_matroid.LinearMatroid>`,
        :class:`RegularMatroid <sage.matroids.linear_matroid.LinearMatroid>`, or
        :class:`RankMatroid <sage.matroids.rank_matroid.RankMatroid>`. To
        import these classes (and other useful functions) directly into Sage's
        main namespace, type::

            sage: from sage.matroids.advanced import *

        See :mod:`sage.matroids.advanced <sage.matroids.advanced>`.

    EXAMPLES:

    Note that in these examples we will often use the fact that strings are
    iterable in these examples. So we type ``'abcd'`` to denote the list
    ``['a', 'b', 'c', 'd']``.

    #.  List of bases:

        All of the following inputs are allowed, and equivalent::

            sage: M1 = Matroid(groundset='abcd', bases=['ab', 'ac', 'ad',
            ....:                                       'bc', 'bd', 'cd'])
            sage: M2 = Matroid(bases=['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
            sage: M3 = Matroid(['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
            sage: M4 = Matroid('abcd', ['ab', 'ac', 'ad', 'bc', 'bd', 'cd'])
            sage: M5 = Matroid('abcd', bases=[['a', 'b'], ['a', 'c'],
            ....:                             ['a', 'd'], ['b', 'c'],
            ....:                             ['b', 'd'], ['c', 'd']])
            sage: M1 == M2
            True
            sage: M1 == M3
            True
            sage: M1 == M4
            True
            sage: M1 == M5
            True

        We do not check if the provided input forms an actual matroid::

            sage: M1 = Matroid(groundset='abcd', bases=['ab', 'cd'])
            sage: M1.full_rank()
            2
            sage: M1.is_valid()
            False

        Bases may be repeated::

            sage: M1 = Matroid(['ab', 'ac'])
            sage: M2 = Matroid(['ab', 'ac', 'ab'])
            sage: M1 == M2
            True

    #.  List of independent sets:

        ::

            sage: M1 = Matroid(groundset='abcd',
            ....:              independent_sets=['', 'a', 'b', 'c', 'd', 'ab',
            ....:                               'ac', 'ad', 'bc', 'bd', 'cd'])

        We only require that the list of independent sets contains each basis
        of the matroid; omissions of smaller independent sets and
        repetitions are allowed::

            sage: M1 = Matroid(bases=['ab', 'ac'])
            sage: M2 = Matroid(independent_sets=['a', 'ab', 'b', 'ab', 'a',
            ....:                                'b', 'ac'])
            sage: M1 == M2
            True

    #.  List of circuits:

        ::

            sage: M1 = Matroid(groundset='abc', circuits=['bc'])
            sage: M2 = Matroid(bases=['ab', 'ac'])
            sage: M1 == M2
            True

        A matroid specified by a list of circuits gets converted to a
        :class:`BasisMatroid <sage.matroids.basis_matroid.BasisMatroid>`
        internally::

            sage: M = Matroid(groundset='abcd', circuits=['abc', 'abd', 'acd',
            ....:                                         'bcd'])
            sage: type(M)
            <... 'sage.matroids.basis_matroid.BasisMatroid'>

        Strange things can happen if the input does not satisfy the circuit
        axioms, and these are not always caught by the
        :meth:`is_valid() <sage.matroids.matroid.Matroid.is_valid>` method. So
        always check whether your input makes sense!

        ::

            sage: M = Matroid('abcd', circuits=['ab', 'acd'])
            sage: M.is_valid()
            True
            sage: [sorted(C) for C in M.circuits()] # random
            [['a']]



    #.  Graph:

        Sage has great support for graphs, see :mod:`sage.graphs.graph`.

        ::

            sage: G = graphs.PetersenGraph()
            sage: Matroid(G)
            Graphic matroid of rank 9 on 15 elements

        If each edge has a unique label, then those are used as the ground set
        labels::

            sage: G = Graph([(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'c')])
            sage: M = Matroid(G)
            sage: sorted(M.groundset())
            ['a', 'b', 'c']

        If there are parallel edges, then integers are used for the ground set.
        If there are no edges in parallel, and is not a complete list of labels,
        or the labels are not unique, then vertex tuples are used::

            sage: G = Graph([(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'b')])
            sage: M = Matroid(G)
            sage: sorted(M.groundset())
            [(0, 1), (0, 2), (1, 2)]
            sage: H = Graph([(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'b'), (1, 2, 'c')], multiedges=True)
            sage: N = Matroid(H)
            sage: sorted(N.groundset())
            [0, 1, 2, 3]

        The GraphicMatroid object forces its graph to be connected. If a
        disconnected graph is used as input, it will connect the components.

            sage: G1 = graphs.CycleGraph(3); G2 = graphs.DiamondGraph()
            sage: G = G1.disjoint_union(G2)
            sage: M = Matroid(G)
            sage: M
            Graphic matroid of rank 5 on 8 elements
            sage: M.graph()
            Looped multi-graph on 6 vertices
            sage: M.graph().is_connected()
            True
            sage: M.is_connected()
            False


        If the keyword ``regular`` is set to ``True``, the output will instead
        be an instance of ``RegularMatroid``.

        ::

            sage: G = Graph([(0, 1), (0, 2), (1, 2)])
            sage: M = Matroid(G, regular=True); M
            Regular matroid of rank 2 on 3 elements with 3 bases

        Note: if a groundset is specified, we assume it is in the same order
        as
        :meth:`G.edge_iterator() <sage.graphs.generic_graph.GenericGraph.edge_iterator>`
        provides::

            sage: G = Graph([(0, 1), (0, 2), (0, 2), (1, 2)], multiedges=True)
            sage: M = Matroid('abcd', G)
            sage: M.rank(['b', 'c'])
            1

        As before,
        if no edge labels are present and the graph is simple, we use the
        tuples ``(i, j)`` of endpoints. If that fails, we simply use a list
        ``[0..m-1]`` ::

            sage: G = Graph([(0, 1), (0, 2), (1, 2)])
            sage: M = Matroid(G, regular=True)
            sage: sorted(M.groundset())
            [(0, 1), (0, 2), (1, 2)]

            sage: G = Graph([(0, 1), (0, 2), (0, 2), (1, 2)], multiedges=True)
            sage: M = Matroid(G, regular=True)
            sage: sorted(M.groundset())
            [0, 1, 2, 3]

        When the ``graph`` keyword is used, a variety of inputs can be
        converted to a graph automatically. The following uses a graph6 string
        (see the :class:`Graph <sage.graphs.graph.Graph>` method's
        documentation)::

            sage: Matroid(graph=':I`AKGsaOs`cI]Gb~')
            Graphic matroid of rank 9 on 17 elements

        However, this method is no more clever than ``Graph()``::

            sage: Matroid(graph=41/2)
            Traceback (most recent call last):
            ...
            ValueError: This input cannot be turned into a graph

    #.  Matrix:

        The basic input is a
        :mod:`Sage matrix <sage.matrix.constructor>`::

            sage: A = Matrix(GF(2), [[1, 0, 0, 1, 1, 0],
            ....:                    [0, 1, 0, 1, 0, 1],
            ....:                    [0, 0, 1, 0, 1, 1]])
            sage: M = Matroid(matrix=A)
            sage: M.is_isomorphic(matroids.CompleteGraphic(4))
            True

        Various shortcuts are possible::

            sage: M1 = Matroid(matrix=[[1, 0, 0, 1, 1, 0],
            ....:                      [0, 1, 0, 1, 0, 1],
            ....:                      [0, 0, 1, 0, 1, 1]], ring=GF(2))
            sage: M2 = Matroid(reduced_matrix=[[1, 1, 0],
            ....:                              [1, 0, 1],
            ....:                              [0, 1, 1]], ring=GF(2))
            sage: M3 = Matroid(groundset=[0, 1, 2, 3, 4, 5],
            ....:              matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:              ring=GF(2))
            sage: A = Matrix(GF(2), [[1, 1, 0], [1, 0, 1], [0, 1, 1]])
            sage: M4 = Matroid([0, 1, 2, 3, 4, 5], A)
            sage: M1 == M2
            True
            sage: M1 == M3
            True
            sage: M1 == M4
            True

        However, with unnamed arguments the input has to be a ``Matrix``
        instance, or the function will try to interpret it as a set of bases::

            sage: Matroid([0, 1, 2], [[1, 0, 1], [0, 1, 1]])
            Traceback (most recent call last):
            ...
            ValueError: basis has wrong cardinality.

        If the groundset size equals number of rows plus number of columns, an
        identity matrix is prepended. Otherwise the groundset size must equal
        the number of columns::

            sage: A = Matrix(GF(2), [[1, 1, 0], [1, 0, 1], [0, 1, 1]])
            sage: M = Matroid([0, 1, 2], A)
            sage: N = Matroid([0, 1, 2, 3, 4, 5], A)
            sage: M.rank()
            2
            sage: N.rank()
            3

        We automatically create an optimized subclass, if available::

            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(2))
            Binary matroid of rank 3 on 6 elements, type (2, 7)
            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(3))
            Ternary matroid of rank 3 on 6 elements, type 0-
            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(4, 'x'))
            Quaternary matroid of rank 3 on 6 elements
            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(2), regular=True)
            Regular matroid of rank 3 on 6 elements with 16 bases

        Otherwise the generic LinearMatroid class is used::

            sage: Matroid([0, 1, 2, 3, 4, 5],
            ....:         matrix=[[1, 1, 0], [1, 0, 1], [0, 1, 1]],
            ....:         field=GF(83))
            Linear matroid of rank 3 on 6 elements represented over the Finite
            Field of size 83

        An integer matrix is automatically converted to a matrix over `\QQ`.
        If you really want integers, you can specify the ring explicitly::

            sage: A = Matrix([[1, 1, 0], [1, 0, 1], [0, 1, -1]])
            sage: A.base_ring()
            Integer Ring
            sage: M = Matroid([0, 1, 2, 3, 4, 5], A)
            sage: M.base_ring()
            Rational Field
            sage: M = Matroid([0, 1, 2, 3, 4, 5], A, ring=ZZ)
            sage: M.base_ring()
            Integer Ring

    #.  Rank function:

        Any function mapping subsets to integers can be used as input::

            sage: def f(X):
            ....:     return min(len(X), 2)
            sage: M = Matroid('abcd', rank_function=f)
            sage: M
            Matroid of rank 2 on 4 elements
            sage: M.is_isomorphic(matroids.Uniform(2, 4))
            True

    #.  Circuit closures:

        This is often a really concise way to specify a matroid. The usual way
        is a dictionary of lists::

            sage: M = Matroid(circuit_closures={3: ['edfg', 'acdg', 'bcfg',
            ....:     'cefh', 'afgh', 'abce', 'abdf', 'begh', 'bcdh', 'adeh'],
            ....:     4: ['abcdefgh']})
            sage: M.equals(matroids.named_matroids.P8())
            True

        You can also input tuples `(k, X)` where `X` is the closure of a
        circuit, and `k` the rank of `X`::

            sage: M = Matroid(circuit_closures=[(2, 'abd'), (3, 'abcdef'),
            ....:                               (2, 'bce')])
            sage: M.equals(matroids.named_matroids.Q6())
            True

    #.  RevLex-Index:

        This requires the ``groundset`` to be given and also needs a
        additional keyword argument ``rank`` to specify the rank of the
        matroid::

            sage: M = Matroid("abcdef", "000000******0**", rank=4); M
            Matroid of rank 4 on 6 elements with 8 bases
            sage: list(M.bases())
            [frozenset({'a', 'b', 'd', 'f'}),
             frozenset({'a', 'c', 'd', 'f'}),
             frozenset({'b', 'c', 'd', 'f'}),
             frozenset({'a', 'b', 'e', 'f'}),
             frozenset({'a', 'c', 'e', 'f'}),
             frozenset({'b', 'c', 'e', 'f'}),
             frozenset({'b', 'd', 'e', 'f'}),
             frozenset({'c', 'd', 'e', 'f'})]

        Only the ``0`` symbols really matter, any symbol can be used
        instead of ``*``:

            sage: Matroid("abcdefg", revlex="0++++++++0++++0+++++0+--++----+--++", rank=4)
            Matroid of rank 4 on 7 elements with 31 bases

        It is checked that the input makes sense (but not that it
        defines a matroid)::

            sage: Matroid("abcdef", "000000******0**")
            Traceback (most recent call last):
            ...
            TypeError: for RevLex-Index, the rank needs to be specified
            sage: Matroid("abcdef", "000000******0**", rank=3)
            Traceback (most recent call last):
            ...
            ValueError: expected string of length 20 (6 choose 3), got 15
            sage: M = Matroid("abcdef", "*0000000000000*", rank=4); M
            Matroid of rank 4 on 6 elements with 2 bases
            sage: M.is_valid()
            False

    #.  Matroid:

        Most of the time, the matroid itself is returned::

            sage: M = matroids.named_matroids.Fano()
            sage: N = Matroid(M)
            sage: N is M
            True

        But it can be useful with the ``regular`` option::

            sage: M = Matroid(circuit_closures={2:['adb', 'bec', 'cfa',
            ....:                                  'def'], 3:['abcdef']})
            sage: N = Matroid(M, regular=True)
            sage: N
            Regular matroid of rank 3 on 6 elements with 16 bases
            sage: M == N
            False
            sage: M.is_isomorphic(N)
            True
            sage: Matrix(N) # random
            [1 0 0 1 1 0]
            [0 1 0 1 1 1]
            [0 0 1 0 1 1]

    The ``regular`` option::

        sage: M = Matroid(reduced_matrix=[[1, 1, 0],
        ....:                             [1, 0, 1],
        ....:                             [0, 1, 1]], regular=True)
        sage: M
        Regular matroid of rank 3 on 6 elements with 16 bases

        sage: M.is_isomorphic(matroids.CompleteGraphic(4))
        True

    By default we check if the resulting matroid is actually regular. To
    increase speed, this check can be skipped::

        sage: M = matroids.named_matroids.Fano()
        sage: N = Matroid(M, regular=True)
        Traceback (most recent call last):
        ...
        ValueError: input is not a valid regular matroid
        sage: N = Matroid(M, regular=True, check=False)
        sage: N
        Regular matroid of rank 3 on 7 elements with 32 bases

        sage: N.is_valid()
        False

    Sometimes the output is regular, but represents a different matroid
    from the one you intended::

        sage: M = Matroid(Matrix(GF(3), [[1, 0, 1, 1], [0, 1, 1, 2]]))
        sage: N = Matroid(Matrix(GF(3), [[1, 0, 1, 1], [0, 1, 1, 2]]),
        ....:             regular=True)
        sage: N.is_valid()
        True
        sage: N.is_isomorphic(M)
        False

    TESTS::

        sage: Matroid()
        Traceback (most recent call last):
        ...
        TypeError: no input data given for Matroid()
        sage: Matroid("abc", bases=["abc"], foo="bar")
        Traceback (most recent call last):
        ...
        TypeError: ...Matroid() got an unexpected keyword argument 'foo'
        sage: Matroid(data=["x"], matrix=Matrix(1,1))
        Traceback (most recent call last):
        ...
        TypeError: ...Matroid() got an unexpected keyword argument 'matrix'
        sage: Matroid(bases=["x"], matrix=Matrix(1,1))
        Traceback (most recent call last):
        ...
        TypeError: ...Matroid() got an unexpected keyword argument 'matrix'
        sage: Matroid(Matrix(1,1), ring=ZZ, field=QQ)
        Traceback (most recent call last):
        ...
        TypeError: ...Matroid() got an unexpected keyword argument 'ring'
        sage: Matroid(rank_function=lambda X: len(X))
        Traceback (most recent call last):
        ...
        TypeError: for rank functions, the groundset needs to be specified
        sage: Matroid(matroid="rubbish")
        Traceback (most recent call last):
        ...
        TypeError: input 'rubbish' is not a matroid
    """
    # process options
    want_regular = kwds.pop('regular', False)
    check = kwds.pop('check', True)

    base_ring = None
    if 'field' in kwds:
        base_ring = kwds.pop('field')
        if check and base_ring not in Fields():
            raise TypeError("{} is not a field".format(base_ring))
    elif 'ring' in kwds:
        base_ring = kwds.pop('ring')
        if check and base_ring not in Rings():
            raise TypeError("{} is not a ring".format(base_ring))

    # "key" is the kind of data we got
    key = None
    if data is None:
        for k in [
                'bases', 'independent_sets', 'circuits', 'graph', 'matrix',
                'reduced_matrix', 'rank_function', 'revlex',
                'circuit_closures', 'matroid'
        ]:
            if k in kwds:
                data = kwds.pop(k)
                key = k
                break
        else:
            # Assume that the single positional argument was actually
            # the data (instead of the groundset)
            data = groundset
            groundset = None

    if key is None:
        if isinstance(data, sage.graphs.graph.Graph):
            key = 'graph'
        elif is_Matrix(data):
            key = 'matrix'
        elif isinstance(data, sage.matroids.matroid.Matroid):
            key = 'matroid'
        elif isinstance(data, str):
            key = 'revlex'
        elif data is None:
            raise TypeError("no input data given for Matroid()")
        else:
            key = 'independent_sets'

    # Bases:
    if key == 'bases':
        if groundset is None:
            groundset = set()
            for B in data:
                groundset.update(B)
        M = BasisMatroid(groundset=groundset, bases=data)

    # Independent sets:
    elif key == 'independent_sets':
        # Convert to list of bases first
        rk = -1
        bases = []
        for I in data:
            if len(I) == rk:
                bases.append(I)
            elif len(I) > rk:
                bases = [I]
                rk = len(I)
        if groundset is None:
            groundset = set()
            for B in bases:
                groundset.update(B)
        M = BasisMatroid(groundset=groundset, bases=bases)

    # Circuits:
    elif key == 'circuits':
        # Convert to list of bases first
        # Determine groundset (note that this cannot detect coloops)
        if groundset is None:
            groundset = set()
            for C in data:
                groundset.update(C)
        # determine the rank by computing a basis element
        b = set(groundset)
        for C in data:
            I = b.intersection(C)
            if len(I) >= len(C):
                b.discard(I.pop())
        rk = len(b)
        # Construct the basis matroid of appropriate rank. Note: slow!
        BB = [
            frozenset(B) for B in combinations(groundset, rk)
            if not any(frozenset(C).issubset(B) for C in data)
        ]
        M = BasisMatroid(groundset=groundset, bases=BB)

    # Graphs:

    elif key == 'graph':
        if isinstance(data, sage.graphs.generic_graph.GenericGraph):
            G = data
        else:
            G = Graph(data)
        # Decide on the groundset
        m = G.num_edges()
        if groundset is None:
            # 1. Attempt to use edge labels.
            sl = G.edge_labels()
            if len(sl) == len(set(sl)):
                groundset = sl
                # 2. If simple, use vertex tuples
            elif not G.has_multiple_edges():
                groundset = [(i, j) for i, j, k in G.edge_iterator()]
            else:
                # 3. Use numbers
                groundset = list(range(m))
        if want_regular:
            # Construct the incidence matrix
            # NOTE: we are not using Sage's built-in method because
            # 1) we would need to fix the loops anyway
            # 2) Sage will sort the columns, making it impossible to keep labels!
            V = G.vertices()
            n = G.num_verts()
            A = Matrix(ZZ, n, m, 0)
            mm = 0
            for i, j, k in G.edge_iterator():
                A[V.index(i), mm] = -1
                A[V.index(j), mm] += 1  # So loops get 0
                mm += 1
            M = RegularMatroid(matrix=A, groundset=groundset)
            want_regular = False  # Save some time, since result is already regular
        else:
            M = GraphicMatroid(G, groundset=groundset)

    # Matrices:
    elif key in ['matrix', 'reduced_matrix']:
        A = data
        is_reduced = (key == 'reduced_matrix')

        # Fix the representation
        if not is_Matrix(A):
            if base_ring is not None:
                A = Matrix(base_ring, A)
            else:
                A = Matrix(A)

        # Fix the ring
        if base_ring is not None:
            if A.base_ring() is not base_ring:
                A = A.change_ring(base_ring)
        elif A.base_ring(
        ) is ZZ and not want_regular:  # Usually a rational matrix is intended, we presume.
            A = A.change_ring(QQ)
            base_ring = QQ
        else:
            base_ring = A.base_ring()

        # Check groundset
        if groundset is not None:
            if not is_reduced:
                if len(groundset) == A.ncols():
                    pass
                elif len(groundset) == A.nrows() + A.ncols():
                    is_reduced = True
                else:
                    raise ValueError(
                        "groundset size does not correspond to matrix size")
            elif is_reduced:
                if len(groundset) == A.nrows() + A.ncols():
                    pass
                else:
                    raise ValueError(
                        "groundset size does not correspond to matrix size")

        if is_reduced:
            kw = dict(groundset=groundset, reduced_matrix=A)
        else:
            kw = dict(groundset=groundset, matrix=A)

        if isinstance(base_ring, FiniteField):
            q = base_ring.order()
        else:
            q = 0

        if q == 2:
            M = BinaryMatroid(**kw)
        elif q == 3:
            M = TernaryMatroid(**kw)
        elif q == 4:
            M = QuaternaryMatroid(**kw)
        else:
            M = LinearMatroid(ring=base_ring, **kw)

    # Rank functions:
    elif key == 'rank_function':
        if groundset is None:
            raise TypeError(
                'for rank functions, the groundset needs to be specified')
        M = RankMatroid(groundset=groundset, rank_function=data)

    # RevLex-Index:
    elif key == "revlex":
        if groundset is None:
            raise TypeError(
                'for RevLex-Index, the groundset needs to be specified')
        try:
            rk = kwds.pop("rank")
        except KeyError:
            raise TypeError('for RevLex-Index, the rank needs to be specified')

        groundset = tuple(groundset)
        data = tuple(data)
        rk = int(rk)
        N = len(groundset)

        def revlex_sort_key(s):
            return tuple(reversed(s))

        subsets = sorted(combinations(range(N), rk), key=revlex_sort_key)
        if len(data) != len(subsets):
            raise ValueError(
                "expected string of length %s (%s choose %s), got %s" %
                (len(subsets), N, rk, len(data)))
        bases = []
        for i, x in enumerate(data):
            if x != '0':
                bases.append([groundset[c] for c in subsets[i]])
        M = BasisMatroid(groundset=groundset, bases=bases)

    # Circuit closures:
    elif key == 'circuit_closures':
        if isinstance(data, dict):
            CC = data
        else:
            # Convert to dictionary
            CC = {}
            for X in data:
                if X[0] not in CC:
                    CC[X[0]] = []
                CC[X[0]].append(X[1])

        if groundset is None:
            groundset = set()
            for X in CC.values():
                for Y in X:
                    groundset.update(Y)

        M = CircuitClosuresMatroid(groundset=groundset, circuit_closures=CC)

    # Matroids:
    elif key == 'matroid':
        if not isinstance(data, sage.matroids.matroid.Matroid):
            raise TypeError("input {!r} is not a matroid".format(data))
        M = data

    else:
        raise AssertionError("unknown key %r" % key)

    # All keywords should be used
    for k in kwds:
        raise TypeError(
            "Matroid() got an unexpected keyword argument '{}'".format(k))

    if want_regular:
        M = sage.matroids.utilities.make_regular_matroid_from_matroid(M)
        if check and not M.is_valid():
            raise ValueError('input is not a valid regular matroid')

    return M
예제 #22
0
def Polyhedron(vertices=None,
               rays=None,
               lines=None,
               ieqs=None,
               eqns=None,
               ambient_dim=None,
               base_ring=None,
               minimize=True,
               verbose=False,
               backend=None):
    """
    Construct a polyhedron object.

    You may either define it with vertex/ray/line or
    inequalities/equations data, but not both. Redundant data will
    automatically be removed (unless ``minimize=False``), and the
    complementary representation will be computed.

    INPUT:

    - ``vertices`` -- list of point. Each point can be specified as
      any iterable container of ``base_ring`` elements. If ``rays`` or
      ``lines`` are specified but no ``vertices``, the origin is
      taken to be the single vertex.

    - ``rays`` -- list of rays. Each ray can be specified as any
      iterable container of ``base_ring`` elements.

    - ``lines`` -- list of lines. Each line can be specified as any
      iterable container of ``base_ring`` elements.

    - ``ieqs`` -- list of inequalities. Each line can be specified as any
      iterable container of ``base_ring`` elements. An entry equal to
      ``[-1,7,3,4]`` represents the inequality `7x_1+3x_2+4x_3\geq 1`.

    - ``eqns`` -- list of equalities. Each line can be specified as
      any iterable container of ``base_ring`` elements. An entry equal to
      ``[-1,7,3,4]`` represents the equality `7x_1+3x_2+4x_3= 1`.

    - ``base_ring`` -- a sub-field of the reals implemented in
      Sage. The field over which the polyhedron will be defined. For
      ``QQ`` and algebraic extensions, exact arithmetic will be
      used. For ``RDF``, floating point numbers will be used. Floating
      point arithmetic is faster but might give the wrong result for
      degenerate input.

    - ``ambient_dim`` -- integer. The ambient space dimension. Usually
      can be figured out automatically from the H/Vrepresentation
      dimensions.

    - ``backend`` -- string or ``None`` (default). The backend to use. Valid choices are

      * ``'cdd'``: use cdd
        (:mod:`~sage.geometry.polyhedron.backend_cdd`) with `\QQ` or
        `\RDF` coefficients depending on ``base_ring``.

      * ``'normaliz'``: use normaliz
        (:mod:`~sage.geometry.polyhedron.backend_normaliz`) with `\ZZ` or
        `\QQ` coefficients depending on ``base_ring``.

      * ``'polymake'``: use polymake
        (:mod:`~sage.geometry.polyhedron.backend_polymake`) with `\QQ`, `\RDF` or
        ``QuadraticField`` coefficients depending on ``base_ring``.

      * ``'ppl'``: use ppl
        (:mod:`~sage.geometry.polyhedron.backend_ppl`) with `\ZZ` or
        `\QQ` coefficients depending on ``base_ring``.

      * ``'field'``: use python implementation
        (:mod:`~sage.geometry.polyhedron.backend_field`) for any field

    Some backends support further optional arguments:

    - ``minimize`` -- boolean (default: ``True``). Whether to
      immediately remove redundant H/V-representation data. Currently
      not used.

    - ``verbose`` -- boolean (default: ``False``). Whether to print
      verbose output for debugging purposes. Only supported by the cdd
      backends.

    OUTPUT:

    The polyhedron defined by the input data.

    EXAMPLES:

    Construct some polyhedra::

        sage: square_from_vertices = Polyhedron(vertices = [[1, 1], [1, -1], [-1, 1], [-1, -1]])
        sage: square_from_ieqs = Polyhedron(ieqs = [[1, 0, 1], [1, 1, 0], [1, 0, -1], [1, -1, 0]])
        sage: list(square_from_ieqs.vertex_generator())
        [A vertex at (1, -1),
         A vertex at (1, 1),
         A vertex at (-1, 1),
         A vertex at (-1, -1)]
        sage: list(square_from_vertices.inequality_generator())
        [An inequality (1, 0) x + 1 >= 0,
         An inequality (0, 1) x + 1 >= 0,
         An inequality (-1, 0) x + 1 >= 0,
         An inequality (0, -1) x + 1 >= 0]
        sage: p = Polyhedron(vertices = [[1.1, 2.2], [3.3, 4.4]], base_ring=RDF)
        sage: p.n_inequalities()
        2

    The same polyhedron given in two ways::

        sage: p = Polyhedron(ieqs = [[0,1,0,0],[0,0,1,0]])
        sage: p.Vrepresentation()
        (A line in the direction (0, 0, 1),
         A ray in the direction (1, 0, 0),
         A ray in the direction (0, 1, 0),
         A vertex at (0, 0, 0))
        sage: q = Polyhedron(vertices=[[0,0,0]], rays=[[1,0,0],[0,1,0]], lines=[[0,0,1]])
        sage: q.Hrepresentation()
        (An inequality (1, 0, 0) x + 0 >= 0,
         An inequality (0, 1, 0) x + 0 >= 0)

    Finally, a more complicated example. Take `\mathbb{R}_{\geq 0}^6` with
    coordinates `a, b, \dots, f` and

      * The inequality `e+b \geq c+d`
      * The inequality `e+c \geq b+d`
      * The equation `a+b+c+d+e+f = 31`

    ::

        sage: positive_coords = Polyhedron(ieqs=[
        ....:     [0, 1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0],
        ....:     [0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 1]])
        sage: P = Polyhedron(ieqs=positive_coords.inequalities() + (
        ....:     [0,0,1,-1,-1,1,0], [0,0,-1,1,-1,1,0]), eqns=[[-31,1,1,1,1,1,1]])
        sage: P
        A 5-dimensional polyhedron in QQ^6 defined as the convex hull of 7 vertices
        sage: P.dim()
        5
        sage: P.Vrepresentation()
        (A vertex at (31, 0, 0, 0, 0, 0), A vertex at (0, 0, 0, 0, 0, 31),
         A vertex at (0, 0, 0, 0, 31, 0), A vertex at (0, 0, 31/2, 0, 31/2, 0),
         A vertex at (0, 31/2, 31/2, 0, 0, 0), A vertex at (0, 31/2, 0, 0, 31/2, 0),
         A vertex at (0, 0, 0, 31/2, 31/2, 0))

    When the input contains elements of a Number Field, they require an
    embedding::

        sage: K = NumberField(x^2-2,'s')
        sage: s = K.0
        sage: L = NumberField(x^3-2,'t')
        sage: t = L.0
        sage: P = Polyhedron(vertices = [[0,s],[t,0]])
        Traceback (most recent call last):
        ...
        ValueError: invalid base ring

    .. NOTE::

      * Once constructed, a ``Polyhedron`` object is immutable.

      * Although the option ``base_ring=RDF`` allows numerical data to
        be used, it might not give the right answer for degenerate
        input data - the results can depend upon the tolerance
        setting of cdd.


    TESTS:

    Check that giving ``float`` input gets converted to ``RDF`` (see :trac:`22605`)::

        sage: f = float(1.1)
        sage: Polyhedron(vertices=[[f]])
        A 0-dimensional polyhedron in RDF^1 defined as the convex hull of 1 vertex

    Check that giving ``int`` input gets converted to ``ZZ`` (see :trac:`22605`)::

        sage: Polyhedron(vertices=[[int(42)]])
        A 0-dimensional polyhedron in ZZ^1 defined as the convex hull of 1 vertex

    Check that giving ``Fraction`` input gets converted to ``QQ`` (see :trac:`22605`)::

        sage: from fractions import Fraction
        sage: f = Fraction(int(6), int(8))
        sage: Polyhedron(vertices=[[f]])
        A 0-dimensional polyhedron in QQ^1 defined as the convex hull of 1 vertex

    Check that input with too many bits of precision returns an error (see
    :trac:`22552`)::

        sage: Polyhedron(vertices=[(8.3319544851638732, 7.0567045956967727), (6.4876921900819049, 4.8435898415984129)])
        Traceback (most recent call last):
        ...
        ValueError: for polyhedra with floating point numbers, the only allowed ring is RDF with backend 'cdd'
    
    Check that setting ``base_ring`` to a ``RealField`` returns an error (see :trac:`22552`)::
    
        sage: Polyhedron(vertices =[(8.3, 7.0), (6.4, 4.8)], base_ring=RealField(40))
        Traceback (most recent call last):
        ...
        ValueError: no appropriate backend for computations with Real Field with 40 bits of precision
        sage: Polyhedron(vertices =[(8.3, 7.0), (6.4, 4.8)], base_ring=RealField(53))
        Traceback (most recent call last):
        ...
        ValueError: no appropriate backend for computations with Real Field with 53 bits of precision
    """
    # Clean up the arguments
    vertices = _make_listlist(vertices)
    rays = _make_listlist(rays)
    lines = _make_listlist(lines)
    ieqs = _make_listlist(ieqs)
    eqns = _make_listlist(eqns)

    got_Vrep = (len(vertices + rays + lines) > 0)
    got_Hrep = (len(ieqs + eqns) > 0)

    if got_Vrep and got_Hrep:
        raise ValueError('cannot specify both H- and V-representation.')
    elif got_Vrep:
        deduced_ambient_dim = _common_length_of(vertices, rays, lines)[1]
    elif got_Hrep:
        deduced_ambient_dim = _common_length_of(ieqs, eqns)[1] - 1
    else:
        if ambient_dim is None:
            deduced_ambient_dim = 0
        else:
            deduced_ambient_dim = ambient_dim
        if base_ring is None:
            base_ring = ZZ

    # set ambient_dim
    if ambient_dim is not None and deduced_ambient_dim != ambient_dim:
        raise ValueError(
            'ambient space dimension mismatch. Try removing the "ambient_dim" parameter.'
        )
    ambient_dim = deduced_ambient_dim

    # figure out base_ring
    from sage.misc.flatten import flatten
    from sage.structure.element import parent
    from sage.categories.all import Rings, Fields

    values = flatten(vertices + rays + lines + ieqs + eqns)
    if base_ring is not None:
        convert = any(parent(x) is not base_ring for x in values)
    elif not values:
        base_ring = ZZ
        convert = False
    else:
        P = parent(values[0])
        if any(parent(x) is not P for x in values):
            from sage.structure.sequence import Sequence
            P = Sequence(values).universe()
            convert = True
        else:
            convert = False

        from sage.structure.coerce import py_scalar_parent
        if isinstance(P, type):
            base_ring = py_scalar_parent(P)
            convert = convert or P is not base_ring
        else:
            base_ring = P

        if not got_Vrep and base_ring not in Fields():
            base_ring = base_ring.fraction_field()
            convert = True

        if base_ring not in Rings():
            raise ValueError('invalid base ring')

        if not base_ring.is_exact():
            # TODO: remove this hack?
            if base_ring is RR:
                base_ring = RDF
                convert = True
            elif base_ring is not RDF:
                raise ValueError(
                    "for polyhedra with floating point numbers, the only allowed ring is RDF with backend 'cdd'"
                )

    # Add the origin if necessary
    if got_Vrep and len(vertices) == 0:
        vertices = [[0] * ambient_dim]

    # Specific backends can override the base_ring
    from sage.geometry.polyhedron.parent import Polyhedra
    parent = Polyhedra(base_ring, ambient_dim, backend=backend)
    base_ring = parent.base_ring()

    # finally, construct the Polyhedron
    Hrep = Vrep = None
    if got_Hrep:
        Hrep = [ieqs, eqns]
    if got_Vrep:
        Vrep = [vertices, rays, lines]
    return parent(Vrep, Hrep, convert=convert, verbose=verbose)
예제 #23
0
def GroupAlgebra(G, R=IntegerRing()):
    """
    Return the group algebra of `G` over `R`.

    INPUT:

    - `G` -- a group
    - `R` -- (default: `\ZZ`) a ring

    EXAMPLES:

    The *group algebra* `A=RG` is the space of formal linear
    combinations of elements of `G` with coefficients in `R`::

        sage: G = DihedralGroup(3)
        sage: R = QQ
        sage: A = GroupAlgebra(G, R); A
        Algebra of Dihedral group of order 6 as a permutation group over Rational Field
        sage: a = A.an_element(); a
        () + 4*(1,2,3) + 2*(1,3)

    This space is endowed with an algebra structure, obtained by extending
    by bilinearity the multiplication of `G` to a multiplication on `RG`::

        sage: A in Algebras
        True
        sage: a * a
        5*() + 8*(2,3) + 8*(1,2) + 8*(1,2,3) + 16*(1,3,2) + 4*(1,3)

    :func:`GroupAlgebra` is just a short hand for a more general
    construction that covers, e.g., monoid algebras, additive group
    algebras and so on::

        sage: G.algebra(QQ)
        Algebra of Dihedral group of order 6 as a permutation group over Rational Field

        sage: GroupAlgebra(G,QQ) is G.algebra(QQ)
        True

        sage: M = Monoids().example(); M
        An example of a monoid:
        the free monoid generated by ('a', 'b', 'c', 'd')
        sage: M.algebra(QQ)
        Algebra of An example of a monoid: the free monoid generated by ('a', 'b', 'c', 'd')
                over Rational Field

    See the documentation of :mod:`sage.categories.algebra_functor`
    for details.

    TESTS::

        sage: GroupAlgebra(1)
        Traceback (most recent call last):
        ...
        ValueError: 1 is not a magma or additive magma

        sage: GroupAlgebra(GL(3, GF(7)))
        Algebra of General Linear Group of degree 3 over Finite Field of size 7
         over Integer Ring
        sage: GroupAlgebra(GL(3, GF(7)), QQ)
        Algebra of General Linear Group of degree 3 over Finite Field of size 7
         over Rational Field
    """
    if not (G in Magmas() or G in AdditiveMagmas()):
        raise ValueError("%s is not a magma or additive magma" % G)
    if not R in Rings():
        raise ValueError("%s is not a ring" % R)
    return G.algebra(R)
예제 #24
0
    def _element_constructor_(self, x, check=True):
        """
        Construct a scheme morphism.

        INPUT:

        - `x` -- anything that defines a morphism of toric
          varieties. A matrix, fan morphism, or a list or tuple of
          homogeneous polynomials that define a morphism.

        - ``check`` -- boolean (default: ``True``) passed onto
          functions called by this to be more careful about input
          argument type checking

        OUTPUT:

        The morphism of toric varieties determined by ``x``.

        EXAMPLES:

        First, construct from fan morphism::

            sage: dP8.<t,x0,x1,x2> = toric_varieties.dP8()
            sage: P2.<y0,y1,y2> = toric_varieties.P2()
            sage: hom_set = dP8.Hom(P2)

            sage: fm = FanMorphism(identity_matrix(2), dP8.fan(), P2.fan())
            sage: hom_set(fm)     # calls hom_set._element_constructor_()
            Scheme morphism:
              From: 2-d CPR-Fano toric variety covered by 4 affine patches
              To:   2-d CPR-Fano toric variety covered by 3 affine patches
              Defn: Defined by sending Rational polyhedral fan in 2-d lattice N
                    to Rational polyhedral fan in 2-d lattice N.

        A matrix will automatically be converted to a fan morphism::

            sage: hom_set(identity_matrix(2))
            Scheme morphism:
              From: 2-d CPR-Fano toric variety covered by 4 affine patches
              To:   2-d CPR-Fano toric variety covered by 3 affine patches
              Defn: Defined by sending Rational polyhedral fan in 2-d lattice N
                    to Rational polyhedral fan in 2-d lattice N.

        Alternatively, one can use homogeneous polynomials to define morphisms::

            sage: P2.inject_variables()
            Defining y0, y1, y2
            sage: dP8.inject_variables()
            Defining t, x0, x1, x2
            sage: hom_set([x0,x1,x2])
            Scheme morphism:
              From: 2-d CPR-Fano toric variety covered by 4 affine patches
              To:   2-d CPR-Fano toric variety covered by 3 affine patches
              Defn: Defined on coordinates by sending [t : x0 : x1 : x2] to
                    [x0 : x1 : x2]

        A morphism of the coordinate ring will also work::

            sage: ring_hom = P2.coordinate_ring().hom([x0,x1,x2], dP8.coordinate_ring())
            sage: ring_hom
            Ring morphism:
              From: Multivariate Polynomial Ring in y0, y1, y2 over Rational Field
              To:   Multivariate Polynomial Ring in t, x0, x1, x2 over Rational Field
              Defn: y0 |--> x0
                    y1 |--> x1
                    y2 |--> x2
            sage: hom_set(ring_hom)
            Scheme morphism:
              From: 2-d CPR-Fano toric variety covered by 4 affine patches
              To:   2-d CPR-Fano toric variety covered by 3 affine patches
              Defn: Defined on coordinates by sending [t : x0 : x1 : x2] to
                    [x0 : x1 : x2]
        """
        from sage.schemes.toric.morphism import SchemeMorphism_polynomial_toric_variety
        if isinstance(x, (list, tuple)):
            return SchemeMorphism_polynomial_toric_variety(self,
                                                           x,
                                                           check=check)

        from sage.categories.map import Map
        from sage.categories.all import Rings
        if isinstance(x, Map) and x.category_for().is_subcategory(Rings()):
            # x is a morphism of Rings
            assert x.domain() is self.codomain().coordinate_ring()
            assert x.codomain() is self.domain().coordinate_ring()
            return SchemeMorphism_polynomial_toric_variety(self,
                                                           x.im_gens(),
                                                           check=check)

        if is_Matrix(x):
            x = FanMorphism(x, self.domain().fan(), self.codomain().fan())
        if isinstance(x, FanMorphism):
            if x.is_dominant():
                from sage.schemes.toric.morphism import SchemeMorphism_fan_toric_variety_dominant
                return SchemeMorphism_fan_toric_variety_dominant(self,
                                                                 x,
                                                                 check=check)
            else:
                from sage.schemes.toric.morphism import SchemeMorphism_fan_toric_variety
                return SchemeMorphism_fan_toric_variety(self, x, check=check)

        raise TypeError(
            "x must be a fan morphism or a list/tuple of polynomials")
예제 #25
0
    def has_rational_point(self,
                           point=False,
                           obstruction=False,
                           algorithm='default',
                           read_cache=True):
        r"""
        Returns True if and only if ``self`` has a point defined over `\QQ`.

        If ``point`` and ``obstruction`` are both False (default), then
        the output is a boolean ``out`` saying whether ``self`` has a
        rational point.

        If ``point`` or ``obstruction`` is True, then the output is
        a pair ``(out, S)``, where ``out`` is as above and the following
        holds:

         - if ``point`` is True and ``self`` has a rational point,
           then ``S`` is a rational point,

         - if ``obstruction`` is True and ``self`` has no rational point,
           then ``S`` is a prime such that no rational point exists
           over the completion at ``S`` or `-1` if no point exists over `\RR`.

        Points and obstructions are cached, whenever they are found.
        Cached information is used if and only if ``read_cache`` is True.

        ALGORITHM:

        The parameter ``algorithm``
        specifies the algorithm to be used:

         - ``'qfsolve'`` -- Use PARI/GP function ``qfsolve``

         - ``'rnfisnorm'`` -- Use PARI's function rnfisnorm
           (cannot be combined with ``obstruction = True``)

         - ``'local'`` -- Check if a local solution exists for all primes
           and infinite places of `\QQ` and apply the Hasse principle
           (cannot be combined with ``point = True``)

         - ``'default'`` -- Use ``'qfsolve'``

         - ``'magma'`` (requires Magma to be installed) --
           delegates the task to the Magma computer algebra
           system.

        EXAMPLES::

            sage: C = Conic(QQ, [1, 2, -3])
            sage: C.has_rational_point(point = True)
            (True, (1 : 1 : 1))
            sage: D = Conic(QQ, [1, 3, -5])
            sage: D.has_rational_point(point = True)
            (False, 3)
            sage: P.<X,Y,Z> = QQ[]
            sage: E = Curve(X^2 + Y^2 + Z^2); E
            Projective Conic Curve over Rational Field defined by X^2 + Y^2 + Z^2
            sage: E.has_rational_point(obstruction = True)
            (False, -1)

        The following would not terminate quickly with
        ``algorithm = 'rnfisnorm'`` ::

            sage: C = Conic(QQ, [1, 113922743, -310146482690273725409])
            sage: C.has_rational_point(point = True)
            (True, (-76842858034579/5424 : -5316144401/5424 : 1))
            sage: C.has_rational_point(algorithm = 'local', read_cache = False)
            True
            sage: C.has_rational_point(point=True, algorithm='magma', read_cache=False) # optional - magma
            (True, (30106379962113/7913 : 12747947692/7913 : 1))

        TESTS:

        Create a bunch of conics over `\QQ`, check if ``has_rational_point`` runs without errors
        and returns consistent answers for all algorithms. Check if all points returned are valid. ::

            sage: l = Sequence(cartesian_product_iterator([[-1, 0, 1] for i in range(6)]))
            sage: c = [Conic(QQ, a) for a in l if a != [0,0,0] and a != (0,0,0,0,0,0)]
            sage: d = []
            sage: d = [[C]+[C.has_rational_point(algorithm = algorithm, read_cache = False, obstruction = (algorithm != 'rnfisnorm'), point = (algorithm != 'local')) for algorithm in ['local', 'qfsolve', 'rnfisnorm']] for C in c[::10]] # long time: 7 seconds
            sage: assert all([e[1][0] == e[2][0] and e[1][0] == e[3][0] for e in d])
            sage: assert all([e[0].defining_polynomial()(Sequence(e[i][1])) == 0 for e in d for i in [2,3] if e[1][0]])
        """
        if read_cache:
            if self._rational_point is not None:
                if point or obstruction:
                    return True, self._rational_point
                else:
                    return True
            if self._local_obstruction is not None:
                if point or obstruction:
                    return False, self._local_obstruction
                else:
                    return False
            if (not point) and self._finite_obstructions == [] and \
               self._infinite_obstructions == []:
                if obstruction:
                    return True, None
                return True
        if self.has_singular_point():
            if point:
                return self.has_singular_point(point=True)
            if obstruction:
                return True, None
            return True
        if algorithm == 'default' or algorithm == 'qfsolve':
            M = self.symmetric_matrix()
            M *= lcm([t.denominator() for t in M.list()])
            pt = qfsolve(M)
            if pt in ZZ:
                if self._local_obstruction is None:
                    self._local_obstruction = pt
                if point or obstruction:
                    return False, pt
                return False
            pt = self.point([pt[0], pt[1], pt[2]])
            if point or obstruction:
                return True, pt
            return True
        ret = ProjectiveConic_number_field.has_rational_point( \
                                           self, point = point, \
                                           obstruction = obstruction, \
                                           algorithm = algorithm, \
                                           read_cache = read_cache)
        if point or obstruction:
            from sage.categories.map import Map
            from sage.categories.all import Rings
            if isinstance(ret[1],
                          Map) and ret[1].category_for().is_subcategory(
                              Rings()):
                # ret[1] is a morphism of Rings
                ret[1] = -1
        return ret