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())
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)
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())
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())
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))))
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)
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)
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())
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())
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
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
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())
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())
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)
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')
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))
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()
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")
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
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)
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)
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")
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