def _fraction_field(ring): r""" Return a fraction field of ``ring``. EXAMPLES: This works around some annoyances with ``ring.fraction_field()``:: sage: R.<x> = ZZ[] sage: S = R.quo(x^2 + 1) sage: S.fraction_field() Fraction Field of Univariate Quotient Polynomial Ring in xbar over Integer Ring with modulus x^2 + 1 sage: from mac_lane.padic_valuation import _fraction_field sage: _fraction_field(S) Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 + 1 """ from sage.categories.all import Fields if ring in Fields(): return ring from sage.rings.polynomial.polynomial_quotient_ring import is_PolynomialQuotientRing if is_PolynomialQuotientRing(ring): from sage.categories.all import IntegralDomains if ring in IntegralDomains(): return ring.base().change_ring(ring.base_ring().fraction_field()).quo(ring.modulus()) return ring.fraction_field()
def super_categories(self): """ EXAMPLES:: sage: FiniteFields().super_categories() [Category of fields, Category of finite enumerated sets] """ from sage.categories.all import Fields, FiniteEnumeratedSets return [Fields(), FiniteEnumeratedSets()]
def __init__(self, R, S, category=None): """ TESTS: Check that :trac:`23647` is fixed:: sage: K.<a, b> = NumberField([x^2 - 2, x^2 - 3]) sage: e, u, v, w = End(K) sage: e.abs_hom().parent().category() Category of homsets of number fields sage: (v*v).abs_hom().parent().category() Category of homsets of number fields """ if category is None: from sage.categories.all import Fields, NumberFields if S in NumberFields(): category = NumberFields() elif S in Fields(): category = Fields() RingHomset_generic.__init__(self, R, S, category)
def super_categories(self): """ EXAMPLES:: sage: ChainComplexes(Integers(9)).super_categories() [Category of modules over Ring of integers modulo 9] """ from sage.categories.all import Fields, Modules, VectorSpaces base_ring = self.base_ring() if base_ring in Fields(): return [VectorSpaces(base_ring)] return [Modules(base_ring)]
def value_semigroup(self): r""" Return the value semigroup of this valuation. EXAMPLES:: sage: v = GaussianIntegers().valuation(2) sage: v.value_semigroup() Additive Abelian Semigroup generated by 1/2 """ from sage.categories.all import Fields v = self(self.uniformizer()) if self.domain() in Fields(): return DiscreteValueSemigroup([-v, v]) else: return DiscreteValueSemigroup([v])
def value_semigroup(self): r""" Return the value semigroup of this valuation. EXAMPLES:: sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone sage: v = pAdicValuation(GaussianIntegers(), 2) sage: v.value_semigroup() Additive Abelian Semigroup generated by 1/2 """ from sage.categories.all import Fields v = self(self.uniformizer()) if self.domain() in Fields(): return DiscreteValueSemigroup([-v,v]) else: return DiscreteValueSemigroup([v])
def create_object(self, version, key): ring, polynomial, names = key R = ring.base_ring() from sage.categories.all import IntegralDomains if R in IntegralDomains(): try: is_irreducible = polynomial.is_irreducible() except NotImplementedError: # is_irreducible sometimes not implemented pass else: if is_irreducible: from sage.categories.all import Fields if R in Fields(): from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_field return PolynomialQuotientRing_field(ring, polynomial, names) else: from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_domain return PolynomialQuotientRing_domain(ring, polynomial, names) from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_generic return PolynomialQuotientRing_generic(ring, polynomial, names)
def __init__(self, p, V): r""" Construct an :class:`AffineSubspace`. TESTS:: sage: from sage.geometry.hyperplane_arrangement.affine_subspace import AffineSubspace sage: a = AffineSubspace([1,0,0,0], VectorSpace(QQ,4)) sage: TestSuite(a).run() sage: AffineSubspace(0, VectorSpace(QQ,4)).point() (0, 0, 0, 0) """ R = V.base_ring() from sage.categories.all import Fields if R not in Fields(): R = R.fraction_field() V = V.change_ring(R) self._base_ring = R self._linear_part = V p = V.ambient_vector_space()(p) p.set_immutable() self._point = p
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 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 simplify(self, x, error=None, force=False, size_heuristic_bound=32): r""" Return a simplified version of ``x``. Produce an element which differs from ``x`` by an element of valuation strictly greater than the valuation of ``x`` (or strictly greater than ``error`` if set.) INPUT: - ``x`` -- an element in the domain of this valuation - ``error`` -- a rational, infinity, or ``None`` (default: ``None``), the error allowed to introduce through the simplification - ``force`` -- ignored - ``size_heuristic_bound`` -- when ``force`` is not set, the expected factor by which the ``x`` need to shrink to perform an actual simplification (default: 32) EXAMPLES:: sage: v = ZZ.valuation(2) sage: v.simplify(6, force=True) 2 sage: v.simplify(6, error=0, force=True) 0 In this example, the usual rational reconstruction misses a good answer for some moduli (because the absolute value of the numerator is not bounded by the square root of the modulus):: sage: v = QQ.valuation(2) sage: v.simplify(110406, error=16, force=True) 562/19 sage: Qp(2, 16)(110406).rational_reconstruction() Traceback (most recent call last): ... ArithmeticError: rational reconstruction of 55203 (mod 65536) does not exist """ if not force and self._relative_size(x) <= size_heuristic_bound: return x x = self.domain().coerce(x) v = self(x) if error is None: error = v from sage.rings.all import infinity if error is infinity: return x if error < v: return self.domain().zero() from sage.rings.all import QQ from sage.rings.all import Qp precision_ring = Qp(self.p(), QQ(error).floor() + 1 - v) reduced = precision_ring(x) lift = (reduced >> v).lift() best = self.domain()(lift) * self.p()**v if self._relative_size(x) < self._relative_size(best): best = x # We implement a modified version of the usual rational reconstruction # algorithm (based on the extended Euclidean algorithm) here. We do not # get the uniqueness properties but we do not need them actually. # This is certainly slower than the implementation in Cython. from sage.categories.all import Fields m = self.p()**(QQ(error).floor() + 1 - v) if self.domain() in Fields(): r = (m, lift) s = (0, 1) while r[1]: qq, rr = r[0].quo_rem(r[1]) r = r[1], rr s = s[1], s[0] - qq * s[1] from sage.arith.all import gcd if s[1] != 0 and gcd(s[1], r[1]) == 1: rational = self.domain()(r[1]) / self.domain()( s[1]) * self.p()**v if self._relative_size(rational) < self._relative_size( best): best = rational assert (self(x - best) > error) return best