Path: blob/master/src/sage/matroids/linear_matroid.pyx
8817 views
r"""1Linear matroids23When `A` is an `r` times `E` matrix, the linear matroid `M[A]` has ground set4`E` and, for independent sets, all `F` subset of `E` such that the columns of5`M[A]` indexed by `F` are linearly independent.67Construction8============910The recommended way to create a linear matroid is by using the11:func:`Matroid() <sage.matroids.constructor.Matroid>` function, with a12representation matrix `A` as input. This function will intelligently choose13one of the dedicated classes :class:`BinaryMatroid`, :class:`TernaryMatroid`,14:class:`QuaternaryMatroid`, :class:`RegularMatroid` when appropriate. However,15invoking the classes directly is possible too. To get access to them, type::1617sage: from sage.matroids.advanced import *1819See also :mod:`sage.matroids.advanced`. In both cases, it is possible to20provide a reduced matrix `B`, to create the matroid induced by `A = [ I B ]`::2122sage: from sage.matroids.advanced import *23sage: A = Matrix(GF(2), [[1, 0, 0, 1, 1, 0, 1], [0, 1, 0, 1, 0, 1, 1],24....: [0, 0, 1, 0, 1, 1, 1]])25sage: B = Matrix(GF(2), [[1, 1, 0, 1], [1, 0, 1, 1], [0, 1, 1, 1]])26sage: M1 = Matroid(A)27sage: M2 = LinearMatroid(A)28sage: M3 = BinaryMatroid(A)29sage: M4 = Matroid(reduced_matrix=B)30sage: M5 = LinearMatroid(reduced_matrix=B)31sage: isinstance(M1, BinaryMatroid)32True33sage: M1.equals(M2)34True35sage: M1.equals(M3)36True37sage: M1 == M438True39sage: M1.is_field_isomorphic(M5)40True41sage: M2 == M3 # comparing LinearMatroid and BinaryMatroid always yields False42False4344Class methods45=============4647The ``LinearMatroid`` class and its derivatives inherit all methods from the48:mod:`Matroid <sage.matroids.matroid>` and49:mod:`BasisExchangeMatroid <sage.matroids.basis_exchange_matroid>` classes.50See the documentation for these classes for an overview. In addition, the51following methods are available:5253- :class:`LinearMatroid`5455- :func:`base_ring() <sage.matroids.linear_matroid.LinearMatroid.base_ring>`56- :func:`characteristic() <sage.matroids.linear_matroid.LinearMatroid.characteristic>`57- :func:`representation() <sage.matroids.linear_matroid.LinearMatroid.representation>`58- :func:`representation_vectors() <sage.matroids.linear_matroid.LinearMatroid.representation_vectors>`59- :func:`is_field_equivalent() <sage.matroids.linear_matroid.LinearMatroid.is_field_equivalent>`60- :func:`is_field_isomorphism() <sage.matroids.linear_matroid.LinearMatroid.is_field_isomorphism>`61- :func:`has_field_minor() <sage.matroids.linear_matroid.LinearMatroid.has_field_minor>`62- :func:`fundamental_cycle() <sage.matroids.linear_matroid.LinearMatroid.fundamental_cycle>`63- :func:`fundamental_cocycle() <sage.matroids.linear_matroid.LinearMatroid.fundamental_cocycle>`64- :func:`cross_ratios() <sage.matroids.linear_matroid.LinearMatroid.cross_ratios>`65- :func:`cross_ratio() <sage.matroids.linear_matroid.LinearMatroid.cross_ratio>`6667- :func:`linear_extension() <sage.matroids.linear_matroid.LinearMatroid.linear_extension>`68- :func:`linear_coextension() <sage.matroids.linear_matroid.LinearMatroid.linear_coextension>`69- :func:`linear_extension_chains() <sage.matroids.linear_matroid.LinearMatroid.linear_extension_chains>`70- :func:`linear_coextension_cochains() <sage.matroids.linear_matroid.LinearMatroid.linear_coextension_cochains>`71- :func:`linear_extensions() <sage.matroids.linear_matroid.LinearMatroid.linear_extensions>`72- :func:`linear_coextensions() <sage.matroids.linear_matroid.LinearMatroid.linear_coextensions>`7374- :class:`BinaryMatroid` has all of the :class:`LinearMatroid` ones, and7576- :func:`bicycle_dimension() <sage.matroids.linear_matroid.BinaryMatroid.bicycle_dimension>`77- :func:`brown_invariant() <sage.matroids.linear_matroid.BinaryMatroid.brown_invariant>`78- :func:`is_graphic() <sage.matroids.linear_matroid.BinaryMatroid.is_graphic>`7980- :class:`TernaryMatroid` has all of the :class:`LinearMatroid` ones, and8182- :func:`bicycle_dimension() <sage.matroids.linear_matroid.TernaryMatroid.bicycle_dimension>`83- :func:`character() <sage.matroids.linear_matroid.TernaryMatroid.character>`8485- :class:`QuaternaryMatroid` has all of the :class:`LinearMatroid` ones, and8687- :func:`bicycle_dimension() <sage.matroids.linear_matroid.QuaternaryMatroid.bicycle_dimension>`8889- :class:`RegularMatroid` has all of the :class:`LinearMatroid` ones, and9091- :func:`is_graphic() <sage.matroids.linear_matroid.RegularMatroid.is_graphic>`9293AUTHORS:9495- Rudi Pendavingh, Stefan van Zwam (2013-04-01): initial version9697Methods98=======99"""100#*****************************************************************************101# Copyright (C) 2013 Rudi Pendavingh <[email protected]>102# Copyright (C) 2013 Stefan van Zwam <[email protected]>103#104#105# Distributed under the terms of the GNU General Public License (GPL)106# as published by the Free Software Foundation; either version 2 of107# the License, or (at your option) any later version.108# http://www.gnu.org/licenses/109#*****************************************************************************110include 'sage/misc/bitset.pxi'111112from sage.matroids.matroid cimport Matroid113from basis_exchange_matroid cimport BasisExchangeMatroid114from lean_matrix cimport LeanMatrix, GenericMatrix, BinaryMatrix, TernaryMatrix, QuaternaryMatrix, IntegerMatrix, generic_identity115from set_system cimport SetSystem116from utilities import newlabel117118from sage.matrix.matrix2 cimport Matrix119import sage.matrix.constructor120from copy import copy, deepcopy121from sage.rings.all import ZZ, QQ, FiniteField, GF122import itertools123from itertools import combinations124125cdef bint GF2_not_defined = True126cdef GF2, GF2_one, GF2_zero127128cdef bint GF3_not_defined = True129cdef GF3, GF3_one, GF3_zero, GF3_minus_one130131# Implementation note: internally we use data structures from lean_matrix132# instead of Sage's standard Matrix datatypes. This was done so we can use133# highly optimized methods in critical places. Our hope is to do away with134# this in the future. To do so, the following needs to be done:135# 1. Modify the ``__init__`` methods (the lean_matrix constructors are136# incompatible with Sage's matrix constructors)137# 2. Look for all lines saying ``# Not a Sage matrix operation`` and provide138# alternative implementations139# 3. Look for all lines saying ``# Deprecated Sage matrix operation`` and140# provide alternative implementations141# Below is some code, commented out currently, to get you going.142143cdef inline gauss_jordan_reduce(LeanMatrix A, columns):144return A.gauss_jordan_reduce(columns) # Not a Sage matrix operation145146cdef inline characteristic(LeanMatrix A):147return A.characteristic() # Not a Sage matrix operation148149# Implementation using default Sage matrices150151# cdef gauss_jordan_reduce(Matrix A, columns):152# """153# Row-reduce so the lexicographically first basis indexes an identity submatrix.154# """155# cdef long r = 0156# cdef list P = []157# cdef long c, p, row158# for c in columns:159# is_pivot = False160# for row in xrange(r, A.nrows()):161# if A.get_unsafe(row, c) != 0:162# is_pivot = True163# p = row164# break165# if is_pivot:166# A.swap_rows_c(p, r)167# A.rescale_row_c(r, A.get_unsafe(r, c) ** (-1), 0)168# for row in xrange(A.nrows()):169# if row != r and A.get_unsafe(row, c) != 0:170# A.add_multiple_of_row_c(row, r, -A.get_unsafe(row, c), 0)171# P.append(c)172# r += 1173# if r == A.nrows():174# break175# return P176#177# cdef inline characteristic(LeanMatrix A):178# # TODO: use caching for increased speed179# return A.base_ring().characteristic()180181cdef class LinearMatroid(BasisExchangeMatroid):182r"""183Linear matroids.184185When `A` is an `r` times `E` matrix, the linear matroid `M[A]` has ground186set `E` and set of independent sets187188`I(A) =\{F \subseteq E :` the columns of `A` indexed by `F` are linearly independent `\}`189190The simplest way to create a LinearMatroid is by giving only a matrix `A`.191Then, the groundset defaults to ``range(A.ncols())``. Any iterable object192``E`` can be given as a groundset. If ``E`` is a list, then ``E[i]`` will193label the `i`-th column of `A`. Another possibility is to specify a194*reduced* matrix `B`, to create the matroid induced by `A = [ I B ]`.195196INPUT:197198- ``matrix`` -- (default: ``None``) a matrix whose column vectors199represent the matroid.200- ``reduced_matrix`` -- (default: ``None``) a matrix `B` such that201`[I\ \ B]` represents the matroid, where `I` is an identity matrix with202the same number of rows as `B`. Only one of ``matrix`` and203``reduced_matrix`` should be provided.204- ``groundset`` -- (default: ``None``) an iterable containing the element205labels. When provided, must have the correct number of elements: the206number of columns of ``matrix`` or the number of rows plus the number207of columns of ``reduced_matrix``.208- ``ring`` -- (default: ``None``) the desired base ring of the matrix. If209the base ring is different, an attempt will be made to create a new210matrix with the correct base ring.211- ``keep_initial_representation`` -- (default: ``True``) decides whether212or not an internal copy of the input matrix should be preserved. This213can help to see the structure of the matroid (e.g. in the case of214graphic matroids), and makes it easier to look at extensions. However,215the input matrix may have redundant rows, and sometimes it is desirable216to store only a row-reduced copy.217218OUTPUT:219220A ``LinearMatroid`` instance based on the data above.221222.. NOTE::223224The recommended way to generate a linear matroid is through the225:func:`Matroid() <sage.matroids.constructor.Matroid>` function. It226will automatically choose more optimized classes when present227(currently :class:`BinaryMatroid`, :class:`TernaryMatroid`,228:class:`QuaternaryMatroid`, :class:`RegularMatroid`). For direct229access to the ``LinearMatroid`` constructor, run::230231sage: from sage.matroids.advanced import *232233EXAMPLES::234235sage: from sage.matroids.advanced import *236sage: A = Matrix(GF(3), 2, 4, [[1, 0, 1, 1], [0, 1, 1, 2]])237sage: M = LinearMatroid(A)238sage: M239Linear matroid of rank 2 on 4 elements represented over the Finite240Field of size 3241sage: sorted(M.groundset())242[0, 1, 2, 3]243sage: Matrix(M)244[1 0 1 1]245[0 1 1 2]246sage: M = LinearMatroid(A, 'abcd')247sage: sorted(M.groundset())248['a', 'b', 'c', 'd']249sage: B = Matrix(GF(3), 2, 2, [[1, 1], [1, 2]])250sage: N = LinearMatroid(reduced_matrix=B, groundset='abcd')251sage: M == N252True253"""254def __init__(self, matrix=None, groundset=None, reduced_matrix=None, ring=None, keep_initial_representation=True):255"""256See class definition for full documentation.257258EXAMPLES::259260sage: from sage.matroids.advanced import *261sage: LinearMatroid(matrix=Matrix(GF(5), [[1, 0, 1, 1, 1],262....: [0, 1, 1, 2, 3]])) # indirect doctest263Linear matroid of rank 2 on 5 elements represented over the Finite264Field of size 5265"""266basis = self._setup_internal_representation(matrix, reduced_matrix, ring, keep_initial_representation)267if groundset is None:268groundset = range(self._A.nrows() + self._A.ncols())269else:270if len(groundset) != self._A.nrows() + self._A.ncols():271raise ValueError("size of groundset does not match size of matrix")272BasisExchangeMatroid.__init__(self, groundset, [groundset[i] for i in basis])273self._zero = self._A.base_ring()(0)274self._one = self._A.base_ring()(1)275276def __dealloc__(self):277"""278Deallocate the memory.279280EXAMPLES::281282sage: from sage.matroids.advanced import *283sage: M = LinearMatroid(matrix=Matrix(GF(5), [[1, 0, 1, 1, 1],284....: [0, 1, 1, 2, 3]])) # indirect doctest285sage: M = None286"""287if self._prow is not NULL:288sage_free(self._prow)289self._prow = NULL290291cdef list _setup_internal_representation(self, matrix, reduced_matrix, ring, keep_initial_representation):292"""293Setup the internal representation matrix ``self._A`` and the array of row- and column indices ``self._prow``.294295Return the displayed basis.296"""297cdef LeanMatrix A298cdef long r, c299cdef list P300if matrix is not None:301reduced = False302if not isinstance(matrix, LeanMatrix):303A = GenericMatrix(matrix.nrows(), matrix.ncols(), M=matrix, ring=ring)304else:305A = (<LeanMatrix>matrix).copy() # Deprecated Sage matrix operation306if keep_initial_representation:307self._representation = A.copy() # Deprecated Sage matrix operation308P = gauss_jordan_reduce(A, xrange(A.ncols()))309self._A = A.matrix_from_rows_and_columns(range(len(P)), [c for c in xrange(matrix.ncols()) if not c in P])310else:311reduced = True312if not isinstance(reduced_matrix, LeanMatrix):313self._A = GenericMatrix(reduced_matrix.nrows(), reduced_matrix.ncols(), M=reduced_matrix, ring=ring)314else:315self._A = (<LeanMatrix>reduced_matrix).copy() # Deprecated Sage matrix operation316P = range(self._A.nrows())317self._prow = <long* > sage_malloc((self._A.nrows() + self._A.ncols()) * sizeof(long))318if matrix is not None:319for r in xrange(len(P)):320self._prow[P[r]] = r321r = 0322for c in xrange(A.ncols()):323if c not in P:324self._prow[c] = r325r += 1326else:327for r from 0 <= r < self._A.nrows():328self._prow[r] = r329for r from 0 <= r < self._A.ncols():330self._prow[self._A.nrows() + r] = r331return P332333cpdef _forget(self):334"""335Remove the internal representation matrix.336337When calling ``Matrix(M)`` after this, the lexicographically first338basis will be used for the identity matrix.339340EXAMPLES::341342sage: from sage.matroids.advanced import *343sage: M = LinearMatroid(matrix=Matrix(GF(5), [[1, 1, 0, 1, 1],344....: [0, 1, 1, 2, 3]]))345sage: A = Matrix(M)346sage: M._forget()347sage: A == Matrix(M)348False349"""350self._representation = None351352cpdef base_ring(self):353"""354Return the base ring of the matrix representing the matroid.355356EXAMPLES::357358sage: M = Matroid(matrix=Matrix(GF(5), [[1, 0, 1, 1, 1],359....: [0, 1, 1, 2, 3]]))360sage: M.base_ring()361Finite Field of size 5362"""363return self._A.base_ring()364365cpdef characteristic(self):366"""367Return the characteristic of the base ring of the matrix representing368the matroid.369370EXAMPLES::371372sage: M = Matroid(matrix=Matrix(GF(5), [[1, 0, 1, 1, 1],373....: [0, 1, 1, 2, 3]]))374sage: M.characteristic()3755376"""377return characteristic(self._A)378379cdef bint __is_exchange_pair(self, long x, long y):380r"""381Check if ``self.basis() - x + y`` is again a basis. Internal method.382"""383return self._A.is_nonzero(self._prow[x], self._prow[y]) # Not a Sage matrix operation384385cdef bint __exchange(self, long x, long y):386"""387Put element indexed by ``x`` into basis, taking out element ``y``.388Assumptions are that this is a valid basis exchange.389390.. NOTE::391392Safe for noncommutative rings.393"""394cdef long px, py, r395px = self._prow[x]396py = self._prow[y]397piv = self._A.get_unsafe(px, py)398pivi = piv ** (-1)399self._A.rescale_row_c(px, pivi, 0)400self._A.set_unsafe(px, py, pivi + self._one) # pivoting without column scaling. Add extra so column does not need adjusting401for r in xrange(self._A.nrows()): # if A and A' are the matrices before and after pivoting, then402a = self._A.get_unsafe(r, py) # ker[I A] equals ker[I A'] except for the labelling of the columns403if a and r != px:404self._A.add_multiple_of_row_c(r, px, -a, 0)405self._A.set_unsafe(px, py, pivi)406self._prow[y] = px407self._prow[x] = py408BasisExchangeMatroid.__exchange(self, x, y)409410cdef __exchange_value(self, long x, long y):411r"""412Return the (x, y) entry of the current representation.413"""414return self._A.get_unsafe(self._prow[x], self._prow[y])415416# Sage functions417418def _matrix_(self):419"""420Return a matrix representation of ``self``.421422OUTPUT:423424A matrix. Either this matrix is equal to the one originally supplied425by the user, or its displayed basis is the lexicographically least426basis of the matroid.427428EXAMPLES::429430sage: M = Matroid(matrix=Matrix(GF(5), [[1, 1, 0, 1, 1],431....: [0, 1, 1, 2, 3]]))432sage: M._matrix_()433[1 1 0 1 1]434[0 1 1 2 3]435sage: M._forget()436sage: M._matrix_()437[1 0 4 4 3]438[0 1 1 2 3]439"""440return self.representation()441442def _repr_(self):443"""444Return a string representation of ``self``.445446EXAMPLES::447448sage: M = Matroid(matrix=Matrix(GF(5), [[1, 1, 0, 1, 1],449....: [0, 1, 1, 2, 3]]))450sage: repr(M) # indirect doctest451'Linear matroid of rank 2 on 5 elements represented over the452Finite Field of size 5'453"""454S = "Linear matroid of rank " + str(self.rank()) + " on " + str(self.size()) + " elements represented over the " + repr(self.base_ring())455return S456457# representations458459cpdef representation(self, B=None, reduced=False, labels=None, order=None):460"""461Return a matrix representing the matroid.462463Let `M` be a matroid on `n` elements with rank `r`. Let `E` be an464ordering of the groundset, as output by465:func:`M.groundset_list() <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid.groundset_list>`.466A *representation* of the matroid is an `r \times n` matrix with the467following property. Consider column ``i`` to be labeled by ``E[i]``,468and denote by `A[F]` the submatrix formed by the columns labeled by469the subset `F \subseteq E`. Then for all `F \subseteq E`, the columns470of `A[F]` are linearly independent if and only if `F` is an471independent set in the matroid.472473A *reduced representation* is a matrix `D` such that `[I\ \ D]` is a474representation of the matroid, where `I` is an `r \times r` identity475matrix. In this case, the rows of `D` are considered to be labeled by476the first `r` elements of the list ``E``, and the columns by the477remaining `n - r` elements.478479INPUT:480481- ``B`` -- (default: ``None``) a subset of elements. When provided,482the representation is such that a basis `B'` that maximally483intersects `B` is an identity matrix.484- ``reduced`` -- (default: ``False``) when ``True``, return a reduced485matrix `D` (so `[I\ \ D]` is a representation of the matroid).486Otherwise return a full representation matrix.487- ``labels`` -- (default: ``None``) when ``True``, return additionally488a list of column labels (if ``reduced=False``) or a list of row489labels and a list of column labels (if ``reduced=True``).490The default setting, ``None``, will not return the labels for a full491matrix, but will return the labels for a reduced matrix.492- ``order`` -- (default: ``None``) an ordering of the groundset493elements. If provided, the columns (and, in case of a reduced494representation, rows) will be presented in the given order.495496OUTPUT:497498- ``A`` -- a full or reduced representation matrix of ``self``; or499- ``(A, E)`` -- a full representation matrix ``A`` and a list ``E``500of column labels; or501- ``(A, R, C)`` -- a reduced representation matrix and a list ``R`` of502row labels and a list ``C`` of column labels.503504If ``B == None`` and ``reduced == False`` and ``order == None`` then505this method will always output the same matrix (except when506``M._forget()`` is called): either the matrix used as input to create507the matroid, or a matrix in which the lexicographically least basis508corresponds to an identity. If only ``order`` is not ``None``, the509columns of this matrix will be permuted accordingly.510511.. NOTE::512513A shortcut for ``M.representation()`` is ``Matrix(M)``.514515EXAMPLES::516517sage: M = matroids.named_matroids.Fano()518sage: M.representation()519[1 0 0 0 1 1 1]520[0 1 0 1 0 1 1]521[0 0 1 1 1 0 1]522sage: Matrix(M) == M.representation()523True524sage: M.representation(labels=True)525(526[1 0 0 0 1 1 1]527[0 1 0 1 0 1 1]528[0 0 1 1 1 0 1], ['a', 'b', 'c', 'd', 'e', 'f', 'g']529)530sage: M.representation(B='efg')531[1 1 0 1 1 0 0]532[1 0 1 1 0 1 0]533[1 1 1 0 0 0 1]534sage: M.representation(B='efg', order='efgabcd')535[1 0 0 1 1 0 1]536[0 1 0 1 0 1 1]537[0 0 1 1 1 1 0]538sage: M.representation(B='abc', reduced=True)539(540[0 1 1 1]541[1 0 1 1]542[1 1 0 1], ['a', 'b', 'c'], ['d', 'e', 'f', 'g']543)544sage: M.representation(B='efg', reduced=True, labels=False,545....: order='gfeabcd')546[1 1 1 0]547[1 0 1 1]548[1 1 0 1]549"""550cdef LeanMatrix A551if order is None:552order = self.groundset_list()553else:554if not frozenset(order) == self.groundset():555raise ValueError("elements in argument ``order`` do not correspond to groundset of matroid.")556order_idx = [self._idx[e] for e in order]557if not reduced:558if B is None:559if self._representation is None:560B = set()561E = self.groundset_list()562i = 0563C = self.closure(B)564while i < len(E):565e = E[i]566if e in C:567i += 1568else:569B.add(e)570C = self.closure(B)571i += 1572self._representation = self._basic_representation(B)573A = self._representation574else:575if not self.groundset().issuperset(B):576raise ValueError("input is not a subset of the groundset.")577B = set(B)578A = self._basic_representation(B)579A = A.matrix_from_rows_and_columns(range(A.nrows()), order_idx)580if labels:581return (A._matrix_(), order)582else:583return A._matrix_()584else:585if B is None:586B = self.basis()587else:588if not self.groundset().issuperset(B):589raise ValueError("input is not a subset of the groundset.")590B = set(B)591A = self._reduced_representation(B)592R, C = self._current_rows_cols()593Ri = []594Ci = []595Rl = []596Cl = []597for e in order:598try:599i = R.index(e)600Ri.append(i)601Rl.append(e)602except ValueError:603Ci.append(C.index(e))604Cl.append(e)605A = A.matrix_from_rows_and_columns(Ri, Ci)606if labels or labels is None:607return (A._matrix_(), Rl, Cl)608else:609return A._matrix_()610611cpdef _current_rows_cols(self, B=None):612"""613Return the current row and column labels of a reduced matrix.614615INPUT:616617- ``B`` -- (default: ``None``) If provided, first find a basis having618maximal intersection with ``B``.619620OUTPUT:621622- ``R`` -- A list of row indices; corresponds to the currently used623internal basis624- ``C`` -- A list of column indices; corresponds to the complement of625the current internal basis626627EXAMPLES::628629sage: M = matroids.named_matroids.Fano()630sage: A = M._reduced_representation('efg')631sage: R, C = M._current_rows_cols()632sage: (sorted(R), sorted(C))633(['e', 'f', 'g'], ['a', 'b', 'c', 'd'])634sage: R, C = M._current_rows_cols(B='abg')635sage: (sorted(R), sorted(C))636(['a', 'b', 'g'], ['c', 'd', 'e', 'f'])637638"""639if B is not None:640self._move_current_basis(B, set())641basis = self.basis()642rows = [0] * self.full_rank()643for e in basis:644rows[self._prow[self._idx[e]]] = e645cols = [0] * self.full_corank()646for e in self.groundset() - basis:647cols[self._prow[self._idx[e]]] = e648return rows, cols649650cpdef LeanMatrix _basic_representation(self, B=None):651"""652Return a basic matrix representation of the matroid.653654INPUT:655656- ``B`` -- (default: ``None``) a set of elements of the groundset.657658OUTPUT:659660A matrix `M` representing the matroid, where `M[B'] = I` for a basis661`B'` that maximally intersects the given set `B`.662If not provided, the current basis used internally is chosen for663`B'`. For a stable representation, use ``self.representation()``.664665.. NOTE::666667The method self.groundset_list() gives the labelling of the668columns by the elements of the matroid. The matrix returned669is a LeanMatrix subclass, which is intended for internal use only.670Use the ``representation()`` method to get a Sage matrix.671672EXAMPLES::673674sage: M = Matroid(reduced_matrix=Matrix(GF(7), [[1, 1, 1],675....: [1, 2, 3]]))676sage: M._basic_representation()677LeanMatrix instance with 2 rows and 5 columns over Finite Field of678size 7679sage: matrix(M._basic_representation([3, 4]))680[3 6 2 1 0]681[5 1 6 0 1]682683"""684cdef LeanMatrix A685cdef long i686if B is not None:687self._move_current_basis(B, set())688basis = self.basis()689A = type(self._A)(self.full_rank(), self.size(), ring=self._A.base_ring())690i = 0691for e in self._E:692if e in basis:693C = self.fundamental_cocycle(basis, e)694for f in C:695A.set_unsafe(i, self._idx[f], C[f])696i += 1697return A698699cpdef representation_vectors(self):700"""701Return a dictionary that associates a column vector with each element702of the matroid.703704.. SEEALSO::705706:meth:`M.representation() <LinearMatroid.representation>`707708EXAMPLES::709710sage: M = matroids.named_matroids.Fano()711sage: E = M.groundset_list()712sage: [M.representation_vectors()[e] for e in E]713[(1, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 0),714(1, 1, 1)]715"""716R = self._matrix_().columns()717return {e: R[self._idx[e]] for e in self.groundset()}718719cpdef LeanMatrix _reduced_representation(self, B=None):720"""721Return a reduced representation of the matroid, i.e. a matrix `R` such722that `[I\ \ R]` represents the matroid.723724INPUT:725726- ``B`` -- (default: ``None``) a set of elements of the groundset.727728OUTPUT:729730A matrix `R` forming a reduced representation of the matroid, with731rows labeled by a basis `B'` that maximally intersects the given set732`B`. If not provided, the current basis used internally labels the733rows.734735.. NOTE::736737The matrix returned is a LeanMatrix subclass, which is intended738for internal use only. Use the ``representation()`` method to get739a Sage matrix.740741EXAMPLES::742743sage: M = Matroid(reduced_matrix=Matrix(GF(7), [[1, 1, 1],744....: [1, 2, 3]]))745sage: M._reduced_representation()746LeanMatrix instance with 2 rows and 3 columns over Finite Field of747size 7748sage: matrix(M._reduced_representation([3, 4]))749[2 3 6]750[6 5 1]751"""752if B is not None:753self._move_current_basis(B, set())754return self._A.copy() # Deprecated Sage matrix operation755756# (field) isomorphism757758cpdef bint _is_field_isomorphism(self, LinearMatroid other, morphism): # not safe if self == other759"""760Version of :meth:`<LinearMatroid.is_field_isomorphism>` that does no761type checking.762763INPUT:764765- ``other`` -- A matroid instance, assumed to have the same base766ring as ``self``.767- ``morphism`` -- a dictionary mapping the groundset of ``self`` to768the groundset of ``other``.769770OUTPUT:771772Boolean.773774.. WARNING::775776This method is not safe if ``self == other``.777778EXAMPLES::779780sage: from sage.matroids.advanced import *781sage: M = matroids.named_matroids.Fano() \ ['g']782sage: N = BinaryMatroid(Matrix(matroids.Wheel(3)))783sage: morphism = {'a':0, 'b':1, 'c': 2, 'd':4, 'e':5, 'f':3}784sage: M._is_field_isomorphism(N, morphism)785True786"""787# TODO: ensure this is safe for noncommutative rings788global GF2, GF2_zero, GF2_one, GF2_not_defined789if GF2_not_defined:790GF2 = GF(2)791GF2_zero = GF2(0)792GF2_one = GF2(1)793GF2_not_defined = False794B = self.basis()795N = self.groundset() - B796Bo = frozenset([morphism[e] for e in B])797No = other.groundset() - Bo798if not other._is_independent(Bo):799return False800801C = {}802for e in B:803C[e] = self._cocircuit(N | set([e]))804if other._cocircuit(No | set([morphism[e]])) != frozenset([morphism[f] for f in C[e]]):805return False806807if self.base_ring() == GF2:808return True809810self._set_current_basis(B)811other._set_current_basis(Bo)812normalization = {}813B = set([b for b in B if len(C[b]) > 1]) # coloops are boring814N = set(N)815while len(B) > 0:816found = False817for e in B:818Ce = set(C[e])819Ce.discard(e)820N2 = set(Ce - N)821if len(N2) > 0:822found = True823f = N2.pop()824normalization[e] = self._exchange_value(e, f) * normalization[f] / other._exchange_value(morphism[e], morphism[f])825B.discard(e)826for f in N2:827if self._exchange_value(e, f) * normalization[f] != normalization[e] * other._exchange_value(morphism[e], morphism[f]):828return False829for f in Ce & N:830normalization[f] = (self._one / self._exchange_value(e, f)) * normalization[e] * other._exchange_value(morphism[e], morphism[f])831N.discard(f)832break833if not found and len(N) > 0:834normalization[N.pop()] = self._one835return True836837cpdef is_field_equivalent(self, other):838"""839Test for matroid representation equality.840841Two linear matroids `M` and `N` with representation matrices `A` and842`B` are *field equivalent* if they have the same groundset, and the843identity map between the groundsets is an isomorphism between the844representations `A` and `B`. That is, one can be turned into the other845using only row operations and column scaling.846847INPUT:848849- ``other`` -- A matroid.850851OUTPUT:852853Boolean.854855.. SEEALSO::856857:meth:`M.equals() <sage.matroids.matroid.Matroid.equals>`,858:meth:`M.is_field_isomorphism() <LinearMatroid.is_field_isomorphism>`,859:meth:`M.is_field_isomorphic() <LinearMatroid.is_field_isomorphic>`860861EXAMPLES:862863A :class:`BinaryMatroid` and864:class:`LinearMatroid` use different865representations of the matroid internally, so `` == ``866yields ``False``, even if the matroids are equal::867868sage: from sage.matroids.advanced import *869sage: M = matroids.named_matroids.Fano()870sage: M1 = LinearMatroid(Matrix(M), groundset=M.groundset_list())871sage: M2 = Matroid(groundset='abcdefg',872....: reduced_matrix=[[0, 1, 1, 1],873....: [1, 0, 1, 1],874....: [1, 1, 0, 1]], field=GF(2))875sage: M.equals(M1)876True877sage: M.equals(M2)878True879sage: M.is_field_equivalent(M1)880True881sage: M.is_field_equivalent(M2)882True883sage: M == M1884False885sage: M == M2886True887888``LinearMatroid`` instances ``M`` and ``N`` satisfy ``M == N`` if the889representations are equivalent up to row operations and column890scaling::891892sage: M1 = Matroid(groundset='abcd',893....: matrix=Matrix(GF(7), [[1, 0, 1, 1], [0, 1, 1, 2]]))894sage: M2 = Matroid(groundset='abcd',895....: matrix=Matrix(GF(7), [[1, 0, 1, 1], [0, 1, 1, 3]]))896sage: M3 = Matroid(groundset='abcd',897....: matrix=Matrix(GF(7), [[2, 6, 1, 0], [6, 1, 0, 1]]))898sage: M1.equals(M2)899True900sage: M1.equals(M3)901True902sage: M1 == M2903False904sage: M1 == M3905True906sage: M1.is_field_equivalent(M2)907False908sage: M1.is_field_equivalent(M3)909True910sage: M1.is_field_equivalent(M1)911True912"""913if self is other:914return True915if self.base_ring() != other.base_ring():916return False917if self.groundset() != other.groundset():918return False919if self.full_rank() != other.full_rank():920return False921morphism = {e: e for e in self.groundset()}922return self._is_field_isomorphism(other, morphism)923924cpdef is_field_isomorphism(self, other, morphism):925"""926Test if a provided morphism induces a bijection between represented927matroids.928929Two represented matroids are *field isomorphic* if the bijection930``morphism`` between them induces a field equivalence between their931representation matrices: the matrices are equal up to row operations932and column scaling. This implies that the matroids are isomorphic, but933the converse is false: two isomorphic matroids can be represented by934matrices that are not field equivalent.935936INPUT:937938- ``other`` -- A matroid.939- ``morphism`` -- A map from the groundset of ``self`` to the940groundset of ``other``. See documentation of the941:meth:`M.is_isomorphism() <sage.matroids.matroid.Matroid.is_isomorphism>`942method for more on what is accepted as input.943944OUTPUT:945946Boolean.947948.. SEEALSO::949950:meth:`M.is_isomorphism() <sage.matroids.matroid.Matroid.is_isomorphism>`,951:meth:`M.is_field_equivalent() <LinearMatroid.is_field_equivalent>`,952:meth:`M.is_field_isomorphic() <LinearMatroid.is_field_isomorphic>`953954EXAMPLES::955956sage: M = matroids.named_matroids.Fano()957sage: N = matroids.named_matroids.NonFano()958sage: N.is_field_isomorphism(M, {e:e for e in M.groundset()})959False960961sage: from sage.matroids.advanced import *962sage: M = matroids.named_matroids.Fano() \ ['g']963sage: N = LinearMatroid(reduced_matrix=Matrix(GF(2),964....: [[-1, 0, 1], [1, -1, 0], [0, 1, -1]]))965sage: morphism = {'a':0, 'b':1, 'c': 2, 'd':4, 'e':5, 'f':3}966sage: M.is_field_isomorphism(N, morphism)967True968969sage: M1 = Matroid(groundset=[0, 1, 2, 3], matrix=Matrix(GF(7),970....: [[1, 0, 1, 1], [0, 1, 1, 2]]))971sage: M2 = Matroid(groundset=[0, 1, 2, 3], matrix=Matrix(GF(7),972....: [[1, 0, 1, 1], [0, 1, 2, 1]]))973sage: mf1 = {0:0, 1:1, 2:2, 3:3}974sage: mf2 = {0:0, 1:1, 2:3, 3:2}975sage: M1.is_field_isomorphism(M2, mf1)976False977sage: M1.is_field_isomorphism(M2, mf2)978True979"""980from copy import copy981if self.base_ring() != other.base_ring():982return False983if self.full_rank() != other.full_rank():984return False985if self.full_corank() != other.full_corank():986return False987if not isinstance(morphism, dict):988mf = {}989try:990for e in self.groundset():991mf[e] = morphism[e]992except (IndexError, TypeError, ValueError):993try:994for e in self.groundset():995mf[e] = morphism(e)996except (TypeError, ValueError):997raise TypeError("the morphism argument does not seem to be an isomorphism.")998else:999mf = morphism1000if len(self.groundset().difference(mf.keys())) > 0:1001raise ValueError("domain of morphism does not contain groundset of this matroid.")1002if len(other.groundset().difference([mf[e] for e in self.groundset()])) > 0:1003raise ValueError("range of morphism does not contain groundset of other matroid.")10041005if self != other:1006return self._is_field_isomorphism(other, mf)1007else:1008return self._is_field_isomorphism(copy(other), mf)10091010cpdef _fast_isom_test(self, other):1011"""1012Fast (field) isomorphism test for some subclasses.10131014INPUT:10151016- ``other`` -- A ``LinearMatroid`` instance, of the same subclass as1017``self``.10181019OUTPUT:10201021- ``None`` -- if the test is inconclusive;1022- ``True`` -- if the matroids were found to be field-isomorphic1023- ``False`` -- if the matroids were found to be non-field-isomorphic.10241025.. NOTE::10261027Intended for internal usage, in particular by the1028``is_field_isomorphic`` method. Matroids are assumed to be in the1029same subclass.10301031EXAMPLES::10321033sage: from sage.matroids.advanced import *1034sage: M1 = BinaryMatroid(reduced_matrix=Matrix(GF(2),1035....: [[1, 1, 0, 1], [1, 0, 1, 1], [0, 1, 1, 1]]))1036sage: M2 = LinearMatroid(reduced_matrix=Matrix(GF(2),1037....: [[1, 1, 0, 1], [1, 0, 1, 1], [1, 1, 0, 1]]))1038sage: M3 = BinaryMatroid(reduced_matrix=Matrix(GF(2),1039....: [[1, 1, 0, 1], [1, 0, 1, 1], [1, 1, 1, 0]]))1040sage: M2._fast_isom_test(M1) is None1041True1042sage: M1._fast_isom_test(M2)1043Traceback (most recent call last):1044...1045AttributeError: 'sage.matroids.linear_matroid.LinearMatroid'1046object has no attribute '_invariant'1047sage: M1._fast_isom_test(M3) is None1048True1049sage: Matroid(graphs.WheelGraph(6))._fast_isom_test(1050....: matroids.Wheel(5))1051True1052"""1053pass10541055def is_field_isomorphic(self, other):1056"""1057Test isomorphism between matroid representations.10581059Two represented matroids are *field isomorphic* if there is a1060bijection between their groundsets that induces a field equivalence1061between their representation matrices: the matrices are equal up to1062row operations and column scaling. This implies that the matroids are1063isomorphic, but the converse is false: two isomorphic matroids can be1064represented by matrices that are not field equivalent.10651066INPUT:10671068- ``other`` -- A matroid.10691070OUTPUT:10711072Boolean.10731074.. SEEALSO::10751076:meth:`M.is_isomorphic() <sage.matroids.matroid.Matroid.is_isomorphic>`,1077:meth:`M.is_field_isomorphism() <LinearMatroid.is_field_isomorphism>`,1078:meth:`M.is_field_equivalent() <LinearMatroid.is_field_equivalent>`107910801081EXAMPLES::10821083sage: M1 = matroids.Wheel(3)1084sage: M2 = matroids.CompleteGraphic(4)1085sage: M1.is_field_isomorphic(M2)1086True1087sage: M3 = Matroid(bases=M1.bases())1088sage: M1.is_field_isomorphic(M3)1089Traceback (most recent call last):1090...1091AttributeError: 'sage.matroids.basis_matroid.BasisMatroid' object1092has no attribute 'base_ring'1093sage: from sage.matroids.advanced import *1094sage: M4 = BinaryMatroid(Matrix(M1))1095sage: M5 = LinearMatroid(reduced_matrix=Matrix(GF(2), [[-1, 0, 1],1096....: [1, -1, 0], [0, 1, -1]]))1097sage: M4.is_field_isomorphic(M5)1098True10991100sage: M1 = Matroid(groundset=[0, 1, 2, 3], matrix=Matrix(GF(7),1101....: [[1, 0, 1, 1], [0, 1, 1, 2]]))1102sage: M2 = Matroid(groundset=[0, 1, 2, 3], matrix=Matrix(GF(7),1103....: [[1, 0, 1, 1], [0, 1, 2, 1]]))1104sage: M1.is_field_isomorphic(M2)1105True1106sage: M1.is_field_equivalent(M2)1107False11081109"""1110if self is other:1111return True1112if self.base_ring() != other.base_ring():1113return False1114if len(self) != len(other):1115return False1116if self.full_rank() != other.full_rank():1117return False1118if self.full_rank() == 0 or self.full_corank() == 0:1119return True1120if self.full_rank() == 1:1121return len(self.loops()) == len(other.loops())1122if self.full_corank() == 1:1123return len(self.coloops()) == len(other.coloops())1124if type(self) is type(other):1125T = self._fast_isom_test(other)1126if T is not None:1127return T11281129if self._weak_invariant() != other._weak_invariant():1130return False1131PS = self._weak_partition()1132PO = other._weak_partition()1133if len(PS) != len(PO):1134return False1135if len(PS) == len(self):1136morphism = {}1137for i in xrange(len(self)):1138morphism[min(PS[i])] = min(PO[i])1139return self._is_field_isomorphism(other, morphism)11401141if self._strong_invariant() != other._strong_invariant():1142return False1143PS = self._strong_partition()1144PO = other._strong_partition()1145if len(PS) != len(PO):1146return False1147if len(PS) == len(self):1148morphism = {}1149for i in xrange(len(self)):1150morphism[min(PS[i])] = min(PO[i])1151return self._is_field_isomorphism(other, morphism)11521153return self.nonbases()._equivalence(lambda sf, ot, morph: self._is_field_isomorphism(other, morph), other.nonbases(), PS, PO) is not None11541155def __richcmp__(left, right, op):1156r"""1157Compare two matroids.11581159We take a very restricted view on equality: the objects need to be of1160the exact same type (so no subclassing) and the internal data need to1161be the same. For linear matroids, in particular, this means field1162equivalence.11631164.. TODO::11651166In a user guide, write about "pitfalls": testing something like1167``M in S`` could yield ``False``, even if ``N.equals(M)`` is ``True`` for some1168`N` in `S`.11691170.. WARNING::11711172This method is linked to __hash__. If you override one, you MUST override the other!11731174.. SEEALSO::11751176:meth:`<LinearMatroid.is_field_equivalent>`11771178EXAMPLES:11791180See docstring for :meth:`LinearMatroid.equals>` for more::11811182sage: M1 = Matroid(groundset='abcd', matrix=Matrix(GF(7),1183....: [[1, 0, 1, 1], [0, 1, 1, 2]]))1184sage: M2 = Matroid(groundset='abcd', matrix=Matrix(GF(7),1185....: [[1, 0, 1, 1], [0, 1, 1, 3]]))1186sage: M3 = Matroid(groundset='abcd', matrix=Matrix(GF(7),1187....: [[2, 6, 1, 0], [6, 1, 0, 1]]))1188sage: M1.equals(M2)1189True1190sage: M1.equals(M3)1191True1192sage: M1 != M2 # indirect doctest1193True1194sage: M1 == M3 # indirect doctest1195True1196"""1197if op in [0, 1, 4, 5]: # <, <=, >, >=1198return NotImplemented1199if not isinstance(left, LinearMatroid) or not isinstance(right, LinearMatroid):1200return NotImplemented1201if left.__class__ != right.__class__: # since we have some subclasses, an extra test1202return NotImplemented1203if op == 2: # ==1204res = True1205if op == 3: # !=1206res = False1207# res gets inverted if matroids are deemed different.1208if left.is_field_equivalent(right):1209return res1210else:1211return not res12121213def __hash__(self):1214r"""1215Return an invariant of the matroid.12161217This function is called when matroids are added to a set. It is very1218desirable to override it so it can distinguish matroids on the same1219groundset, which is a very typical use case!12201221.. WARNING::12221223This method is linked to __richcmp__ (in Cython) and __cmp__ or1224__eq__/__ne__ (in Python). If you override one, you should (and in1225Cython: MUST) override the other!12261227EXAMPLES::12281229sage: M1 = Matroid(groundset='abcde', matrix=Matrix(GF(7),1230....: [[1, 0, 1, 1, 1], [0, 1, 1, 2, 3]]))1231sage: M2 = Matroid(groundset='abcde', matrix=Matrix(GF(7),1232....: [[0, 1, 1, 2, 3], [1, 0, 1, 1, 1]]))1233sage: hash(M1) == hash(M2)1234True1235sage: M2 = M1.dual()1236sage: hash(M1) == hash(M2)1237False1238"""1239return hash((self.groundset(), self.full_rank(), self._weak_invariant()))12401241# minors, dual12421243cpdef _minor(self, contractions, deletions):1244r"""1245Return a minor.12461247INPUT:12481249- ``contractions`` -- An object with Python's ``frozenset`` interface1250containing a subset of ``self.groundset()``.1251- ``deletions`` -- An object with Python's ``frozenset`` interface1252containing a subset of ``self.groundset()``.12531254.. NOTE::12551256This method does NOT do any checks. Besides the assumptions above,1257we assume the following:12581259- ``contractions`` is independent1260- ``deletions`` is coindependent1261- ``contractions`` and ``deletions`` are disjoint.12621263OUTPUT:12641265A matroid.12661267EXAMPLES::12681269sage: M = Matroid(groundset='abcdefgh', ring=GF(5),1270....: reduced_matrix=[[2, 1, 1, 0],1271....: [1, 1, 0, 1], [1, 0, 1, 1], [0, 1, 1, 2]])1272sage: N = M._minor(contractions=set(['a']), deletions=set([]))1273sage: N._minor(contractions=set([]), deletions=set(['b', 'c']))1274Linear matroid of rank 3 on 5 elements represented over the Finite1275Field of size 51276"""1277cdef LeanMatrix M1278self._move_current_basis(contractions, deletions)1279rows = list(self.basis() - contractions)1280cols = list(self.cobasis() - deletions)1281M = type(self._A)(len(rows), len(cols), ring=self.base_ring())1282for i in range(len(rows)):1283for j in range(len(cols)):1284M.set_unsafe(i, j, self._exchange_value(rows[i], cols[j]))1285return type(self)(reduced_matrix=M, groundset=rows + cols)12861287cpdef dual(self):1288"""1289Return the dual of the matroid.12901291Let `M` be a matroid with ground set `E`. If `B` is the set of bases1292of `M`, then the set `\{E - b : b \in B\}` is the set of bases of1293another matroid, the *dual* of `M`.12941295If the matroid is represented by `[I_1\ \ A]`, then the dual is1296represented by `[-A^T\ \ I_2]` for appropriately sized identity1297matrices `I_1, I_2`.12981299OUTPUT:13001301The dual matroid.13021303EXAMPLES::13041305sage: A = Matrix(GF(7), [[1, 1, 0, 1],1306....: [1, 0, 1, 1],1307....: [0, 1, 1, 1]])1308sage: B = - A.transpose()1309sage: Matroid(reduced_matrix=A).dual() == Matroid(1310....: reduced_matrix=B,1311....: groundset=[3, 4, 5, 6, 0, 1, 2])1312True13131314"""1315cdef LeanMatrix R = -self._reduced_representation().transpose()1316rows, cols = self._current_rows_cols()1317return type(self)(reduced_matrix=R, groundset=cols + rows)13181319cpdef has_line_minor(self, k, hyperlines=None):1320"""1321Test if the matroid has a `U_{2, k}`-minor.13221323The matroid `U_{2, k}` is a matroid on `k` elements in which every1324subset of at most 2 elements is independent, and every subset of more1325than two elements is dependent.13261327The optional argument ``hyperlines`` restricts the search space: this1328method returns ``False`` if `si(M/F)` is isomorphic to `U_{2, l}` with1329`l \geq k` for some `F` in ``hyperlines``, and ``True`` otherwise.13301331INPUT:13321333- ``k`` -- the length of the line minor1334- ``hyperlines`` -- (default: ``None``) a set of flats of codimension13352. Defaults to the set of all flats of codimension 2.13361337OUTPUT:13381339Boolean.13401341EXAMPLES::13421343sage: M = matroids.named_matroids.N1()1344sage: M.has_line_minor(4)1345True1346sage: M.has_line_minor(5)1347False1348sage: M.has_line_minor(k=4, hyperlines=[['a', 'b', 'c']])1349False1350sage: M.has_line_minor(k=4, hyperlines=[['a', 'b', 'c'],1351....: ['a', 'b', 'd' ]])1352True13531354"""1355try:1356if k > len(self.base_ring()) + 1:1357return False1358except TypeError:1359pass1360return Matroid.has_line_minor(self, k, hyperlines)13611362cpdef has_field_minor(self, N):1363"""1364Check if ``self`` has a minor field isomorphic to ``N``.13651366INPUT:13671368- ``N`` -- A matroid.13691370OUTPUT:13711372Boolean.13731374.. SEEALSO::13751376:meth:`M.minor() <sage.matroids.matroid.Matroid.minor>`,1377:meth:`M.is_field_isomorphic() <LinearMatroid.is_field_isomorphic>`13781379.. TODO::13801381This important method can (and should) be optimized considerably.1382See [Hlineny]_ p.1219 for hints to that end.13831384EXAMPLES::13851386sage: M = matroids.Whirl(3)1387sage: matroids.named_matroids.Fano().has_field_minor(M)1388False1389sage: matroids.named_matroids.NonFano().has_field_minor(M)1390True1391"""1392if self is N:1393return True1394if not isinstance(N, Matroid):1395raise ValueError("N must be a matroid.")1396if self.base_ring() != N.base_ring():1397return False1398rd = self.full_rank() - N.full_rank()1399cd = self.full_corank() - N.full_corank()1400if rd < 0 or cd < 0:1401return False14021403R = self._reduced_representation()1404M = type(self)(reduced_matrix=R)14051406F = M.flats(rd)1407G = M.dual().flats(cd)1408a = len(M.loops())1409b = len(M.coloops())1410for X in F:1411XB = M.max_independent(X)1412for Y in G:1413YB = M.max_coindependent(Y - XB)1414if len(YB) == cd and len((X - XB) - YB) <= a and len((Y - YB) - XB) <= b and N.is_field_isomorphic(M._minor(contractions=XB, deletions=YB)):1415return True1416return False14171418# cycles, cocycles and cross ratios14191420cpdef _exchange_value(self, e, f):1421"""1422Return the matrix entry indexed by row `e` and column `f`.14231424INPUT:14251426- ``e`` -- an element of the groundset.1427- ``f`` -- an element of the groundset.14281429``e`` should be in the currently active basis, and ``f`` in the1430currently active cobasis.14311432OUTPUT:14331434The (internal) matrix entry indexed by row `e` and column `f`.14351436.. WARNING::14371438Intended for internal use. This method does no checks of any kind.14391440EXAMPLES::14411442sage: M = Matroid(Matrix(GF(7), [[1, 0, 1, 1], [0, 1, 1, 4]]))1443sage: M._exchange_value(1, 3)144441445"""1446return self.__exchange_value(self._idx[e], self._idx[f])14471448cpdef fundamental_cycle(self, B, e):1449"""1450Return the fundamental cycle, relative to ``B``, containing element1451``e``.14521453This is the1454:meth:`fundamental circuit <sage.matroids.matroid.Matroid.fundamental_circuit>`1455together with an appropriate signing from the field, such that `Av=0`,1456where `A` is the representation matrix, and `v` the vector1457corresponding to the output.14581459INPUT:14601461- ``B`` -- a basis of the matroid1462- ``e`` -- an element outside the basis14631464OUTPUT:14651466A dictionary mapping elements of ``M.fundamental_circuit(B, e)`` to1467elements of the ring.14681469.. SEEALSO::14701471:meth:`M.fundamental_circuit() <sage.matroids.matroid.Matroid.fundamental_circuit>`14721473EXAMPLES::14741475sage: M = Matroid(Matrix(GF(7), [[1, 0, 1, 1], [0, 1, 1, 4]]))1476sage: v = M.fundamental_cycle([0, 1], 3)1477sage: [v[0], v[1], v[3]]1478[6, 3, 1]1479sage: frozenset(v.keys()) == M.fundamental_circuit([0, 1], 3)1480True1481"""1482if e in B or not self._is_basis(B):1483return None1484self._move_current_basis(B, set())1485self._move_current_basis(B, set())1486chain = {}1487chain[e] = self._one1488for f in B:1489x = self._exchange_value(f, e)1490if x != 0:1491chain[f] = -x1492return chain14931494cpdef fundamental_cocycle(self, B, e):1495"""1496Return the fundamental cycle, relative to ``B``, containing element1497``e``.14981499This is the1500:meth:`fundamental cocircuit <sage.matroids.matroid.Matroid.fundamental_cocircuit>`1501together with an appropriate signing from the field, such that `Av=0`,1502where `A` is a representation matrix of the dual, and `v` the vector1503corresponding to the output.15041505INPUT:15061507- ``B`` -- a basis of the matroid1508- ``e`` -- an element of the basis15091510OUTPUT:15111512A dictionary mapping elements of ``M.fundamental_cocircuit(B, e)`` to1513elements of the ring.15141515.. SEEALSO::15161517:meth:`M.fundamental_cocircuit() <sage.matroids.matroid.Matroid.fundamental_cocircuit>`15181519EXAMPLES::15201521sage: M = Matroid(Matrix(GF(7), [[1, 0, 1, 1], [0, 1, 1, 4]]))1522sage: v = M.fundamental_cocycle([0, 1], 0)1523sage: [v[0], v[2], v[3]]1524[1, 1, 1]1525sage: frozenset(v.keys()) == M.fundamental_cocircuit([0, 1], 0)1526True1527"""1528if e not in B or not self._is_basis(B):1529return None1530self._move_current_basis(B, set())1531cochain = {}1532cochain[e] = self._one1533for f in self.groundset() - set(B):1534x = self._exchange_value(e, f)1535if x != 0:1536cochain[f] = x1537return cochain15381539cpdef _line_ratios(self, F):1540"""1541Return the set of nonzero ratios of column entries after contracting1542a rank-`r-2` flat ``F``.15431544.. WARNING::15451546Intended for internal use. Does no checking.15471548EXAMPLES::15491550sage: M = Matroid(Matrix(GF(7), [[1, 0, 0, 1, 1, 1],1551....: [0, 1, 0, 1, 2, 4], [0, 0, 1, 3, 2, 5]]))1552sage: sorted(M._line_ratios(set([2])))1553[1, 2, 4]1554sage: sorted(M._line_ratios([0]))1555[1, 5]1556"""1557self._move_current_basis(F, set())1558X = self.basis().difference(F)1559a = min(X)1560b = max(X)1561rat = set()1562for c in self.cobasis():1563s = self._exchange_value(a, c)1564if s != 0:1565t = self._exchange_value(b, c)1566if t != 0:1567rat.add(s * (t ** (-1)))1568return rat15691570cpdef _line_length(self, F):1571"""1572Return ``len(M.contract(F).simplify())``, where ``F`` is assumed to be1573a flat of rank 2 less than the matroid.15741575.. WARNING::15761577Intended for internal use. Does no checking.15781579EXAMPLES::15801581sage: M = Matroid(Matrix(GF(7), [[1, 0, 0, 1, 1, 1],1582....: [0, 1, 0, 1, 2, 4], [0, 0, 1, 3, 2, 5]]))1583sage: M._line_length([2])158451585sage: M._line_length([0])158641587"""1588return 2 + len(self._line_ratios(F))15891590cpdef _line_cross_ratios(self, F):1591"""1592Return the set of cross ratios of column entries after contracting a1593rank-`r-2` flat ``F``.15941595Note that these are only the ordered cross ratios!15961597.. WARNING::15981599Intended for internal use. Does no checking.16001601EXAMPLES::16021603sage: M = Matroid(Matrix(GF(7), [[1, 0, 0, 1, 1, 1],1604....: [0, 1, 0, 1, 2, 4], [0, 0, 1, 3, 2, 5]]))1605sage: sorted(M._line_cross_ratios(set([2])))1606[2, 4]1607sage: sorted(M._line_cross_ratios([0]))1608[5]1609"""1610cr = set()1611rat = self._line_ratios(F)1612while len(rat) != 0:1613r1 = rat.pop()1614for r2 in rat:1615cr.add(r2 / r1)1616return cr16171618cpdef cross_ratios(self, hyperlines=None):1619r"""1620Return the set of cross ratios that occur in this linear matroid.16211622Consider the following matrix with columns labeled by1623`\{a, b, c, d\}`.16241625.. MATH::16261627\begin{matrix}16281 & 0 & 1 & 1\\16290 & 1 & x & 11630\end{matrix}16311632The cross ratio of the ordered tuple `(a, b, c, d)` equals `x`. The1633set of all cross ratios of a matroid is the set of cross ratios of all1634such minors.16351636INPUT:16371638- ``hyperlines`` -- (optional) a set of flats of the matroid, of rank1639`r - 2`, where `r` is the rank of the matroid. If not given, then1640``hyperlines`` defaults to all such flats.16411642OUTPUT:16431644A list of all cross ratios of this linearly represented matroid that1645occur in rank-2 minors that arise by contracting a flat ``F`` in1646``hyperlines`` (so by default, those are all cross ratios).16471648.. SEEALSO::16491650:meth:`M.cross_ratio() <LinearMatroid.cross_ratio>`16511652EXAMPLES::16531654sage: M = Matroid(Matrix(GF(7), [[1, 0, 0, 1, 1, 1],1655....: [0, 1, 0, 1, 2, 4],1656....: [0, 0, 1, 3, 2, 5]]))1657sage: sorted(M.cross_ratios())1658[2, 3, 4, 5, 6]1659sage: M = matroids.CompleteGraphic(5)1660sage: M.cross_ratios()1661set([])1662"""1663if hyperlines is None:1664hyperlines = self.flats(self.full_rank() - 2)1665CR = set()1666for F in hyperlines:1667CR |= self._line_cross_ratios(F)1668CR2 = set()1669while len(CR) != 0:1670cr = CR.pop()1671asc = set([cr, cr ** (-1), -cr + 1, (-cr + 1) ** (-1), cr / (cr - 1), (cr - 1) / cr])1672CR2.update(asc)1673CR.difference_update(asc)1674return CR216751676cpdef cross_ratio(self, F, a, b, c, d):1677r"""1678Return the cross ratio of the four ordered points ``a, b, c, d``1679after contracting a flat ``F`` of codimension 2.16801681Consider the following matrix with columns labeled by1682`\{a, b, c, d\}`.16831684.. MATH::16851686\begin{bmatrix}16871 & 0 & 1 & 1\\16880 & 1 & x & 11689\end{bmatrix}16901691The cross ratio of the ordered tuple `(a, b, c, d)` equals `x`. This1692method looks at such minors where ``F`` is a flat to be contracted,1693and all remaining elements other than ``a, b, c, d`` are deleted.16941695INPUT:16961697- ``F`` -- A flat of codimension 21698- ``a, b, c, d`` -- elements of the groundset16991700OUTPUT:17011702The cross ratio of the four points on the line obtained by1703contracting ``F``.17041705EXAMPLES::17061707sage: M = Matroid(Matrix(GF(7), [[1, 0, 0, 1, 1, 1],1708....: [0, 1, 0, 1, 2, 4],1709....: [0, 0, 1, 3, 2, 6]]))1710sage: M.cross_ratio([0], 1, 2, 3, 5)1711417121713sage: M = Matroid(ring=GF(7), matrix=[[1, 0, 1, 1], [0, 1, 1, 1]])1714sage: M.cross_ratio(set(), 0, 1, 2, 3)1715Traceback (most recent call last):1716...1717ValueError: points a, b, c, d do not form a 4-point line in M/F17181719"""1720F = frozenset(F)1721if not F.issubset(self.groundset()):1722raise ValueError("set F must be subset of the groundset")1723if not self.groundset().issuperset([a, b, c, d]):1724raise ValueError("variables a, b, c, d need to be elements of the matroid")1725if self._closure(F | set([a, b])) != self.groundset():1726raise ValueError("set F must be a flat; F with a, b must span the matroid")1727self._move_current_basis(set([a, b]), set([c, d]))1728cr1 = self._exchange_value(a, c) * self._exchange_value(b, d)1729cr2 = self._exchange_value(a, d) * self._exchange_value(b, c)1730if cr1 == 0 or cr2 == 0 or cr1 == cr2:1731raise ValueError("points a, b, c, d do not form a 4-point line in M/F")1732return cr1 / cr217331734cpdef _line_cross_ratio_test(self, F, x, fundamentals):1735r"""1736Check whether the cross ratios involving a fixed element in a fixed1737rank-2 minor are in a specified subset.17381739INPUT:17401741- ``F`` -- a flat of codimension 21742- ``x`` -- an element outside ``F``1743- ``fundamentals`` -- a set of fundamental elements.17441745OUTPUT:17461747``True`` if all cross ratios involving ``x`` are in1748``fundamentals``; ``False`` otherwise.17491750.. NOTE::17511752This method is intended for checking extensions of a matroid, so1753it is assumed that the cross ratios of `(M/F)-x` are all in the1754desired subset. Moreover, the set of cross ratios is closed under1755reordering of the elements, i.e. if `x` is in ``fundamentals``1756then also `1/x, 1-x, 1/(1-x), x/(x-1), (x-1)/x` are in it.17571758.. WARNING::17591760Intended for internal use. No checks whatsoever on validity of1761input.17621763EXAMPLES::17641765sage: M = Matroid(ring=QQ, reduced_matrix=[[1, 1, 1, 0],1766....: [1, 1, 0, 1], [1, 0, 1, 1]])1767sage: M._line_cross_ratio_test(set([0]), 6, set([1]))1768True1769sage: M._line_cross_ratio_test(set([4]), 6, set([1]))1770False1771sage: M._line_cross_ratio_test(set([4]), 6, set([1, 2, 1/2, -1]))1772True1773"""1774self._move_current_basis(F, set([x]))1775X = self.basis() - F1776a = min(X)1777b = max(X)1778s = self._exchange_value(a, x)1779t = self._exchange_value(b, x)1780if s == 0 or t == 0:1781return True1782try:1783r = s / t1784for c in self.cobasis(): # Only need to check 2x2 submatrices relative to a fixed basis, because of our assumptions1785s = self._exchange_value(a, c)1786t = self._exchange_value(b, c)1787if s != 0 and t != 0:1788if not s / t / r in fundamentals:1789return False1790except ZeroDivisionError:1791return False1792return True17931794cpdef _cross_ratio_test(self, x, fundamentals, hyperlines=None):1795r"""1796Test if the cross ratios using a given element of this linear matroid1797are contained in a given set of fundamentals.17981799INPUT:18001801- ``x`` -- an element of the ground set1802- ``fundamentals`` -- a subset of the base ring1803- ``hyperlines`` (optional) -- a set of flats of rank=full_rank-218041805OUTPUT:18061807Boolean. ``True`` if each cross ratio using ``x`` is an element of1808``fundamentals``. If ``hyperlines`` is specified, then the method1809tests if each cross ratio in a minor that arises by contracting a flat1810``F`` in ``hyperlines`` and uses ``x`` is in ``fundamentals``. If1811``hyperlines`` is not specified, all flats of codimension 2 are1812tested.18131814.. NOTE::18151816This method is intended for checking extensions of a matroid, so1817it is assumed that the cross ratios of `M/F\setminus x` are all in1818the desired subset. Moreover, the set of fundamentals is closed1819under reordering of the elements, i.e. if `x \in`1820``fundamentals`` then also `1/x, 1-x, 1/(1-x), x/(x-1), (x-1)/x`1821are in it.18221823EXAMPLES::18241825sage: M = Matroid(ring=QQ, reduced_matrix=[[1, 1, 1, 0],1826....: [1, 1, 0, 1], [1, 0, 1, 1]])1827sage: M._cross_ratio_test(6, set([1]))1828False1829sage: M._cross_ratio_test(6, set([1, 2, 1/2, -1]))1830True18311832"""1833if hyperlines is None:1834hyperlines = [F for F in self.flats(self.full_rank() - 2) if self._line_length(F) > 2]1835if self.rank(self.groundset() - set([x])) < self.full_rank():1836return True1837for F in hyperlines:1838if not self._line_cross_ratio_test(F, x, fundamentals):1839return False1840return True18411842# linear extension1843cpdef linear_extension(self, element, chain=None, col=None):1844r"""1845Return a linear extension of this matroid.18461847A *linear extension* of the represented matroid `M` by element `e` is1848a matroid represented by `[A\ \ b]`, where `A` is a representation1849matrix of `M` and `b` is a new column labeled by `e`.18501851INPUT:18521853- ``element`` -- the name of the new element.1854- ``col`` -- (default: ``None``) a column to be appended to1855``self.representation()``. Can be any iterable.1856- ``chain`` -- (default: ``None``) a dictionary that maps elements of1857the ground set to elements of the base ring.18581859OUTPUT:18601861A linear matroid `N = M([A\ \ b])`, where `A` is a matrix such that1862the current matroid is `M[A]`, and `b` is either given by ``col`` or1863is a weighted combination of columns of `A`, the weigths being given1864by ``chain``.18651866.. SEEALSO::18671868:meth:`M.extension() <sage.matroids.matroid.Matroid.extension>`.18691870EXAMPLES::18711872sage: M = Matroid(ring=GF(2), matrix=[[1, 1, 0, 1, 0, 0],1873....: [1, 0, 1, 0, 1, 0],1874....: [0, 1, 1, 0, 0, 1],1875....: [0, 0, 0, 1, 1, 1]])1876sage: M.linear_extension(6, {0:1, 5: 1}).representation()1877[1 1 0 1 0 0 1]1878[1 0 1 0 1 0 1]1879[0 1 1 0 0 1 1]1880[0 0 0 1 1 1 1]1881sage: M.linear_extension(6, col=[0, 1, 1, 1]).representation()1882[1 1 0 1 0 0 0]1883[1 0 1 0 1 0 1]1884[0 1 1 0 0 1 1]1885[0 0 0 1 1 1 1]18861887"""1888cdef LeanMatrix cl1889cdef long i1890if element in self.groundset():1891raise ValueError("extension element is already in groundset")1892if self._representation is not None and col is not None:1893R = self.base_ring()1894cl = type(self._representation)(self._representation.nrows(), 1, ring=R)1895i = 01896for x in col:1897if i == self._representation.nrows():1898raise ValueError("provided column has too many entries")1899cl.set_unsafe(i, 0, R(x))1900i += 11901if i < self._representation.nrows():1902raise ValueError("provided column has too few entries")1903E = self._E + (element,)1904return type(self)(matrix=self._representation.augment(cl), groundset=E)1905elif col is not None:1906raise ValueError("can only specify column relative to fixed representation. Run self._matrix_() first.")1907else:1908if not isinstance(chain, dict):1909raise TypeError("chain argument needs to be a dictionary")1910return self._linear_extensions(element, [chain])[0]19111912cpdef linear_coextension(self, element, cochain=None, row=None):1913r"""1914Return a linear coextension of this matroid.19151916A *linear coextension* of the represented matroid `M` by element `e`1917is a matroid represented by19181919.. MATH::19201921\begin{bmatrix}1922A & 0\\1923-c & 11924\end{bmatrix},19251926where `A` is a representation matrix of `M`, `c` is a new row, and the1927last column is labeled by `e`.19281929This is the dual method of1930:meth:`M.linear_extension() <LinearMatroid.linear_extension>`.19311932INPUT:19331934- ``element`` -- the name of the new element.1935- ``row`` -- (default: ``None``) a row to be appended to1936``self.representation()``. Can be any iterable.1937- ``cochain`` -- (default: ``None``) a dictionary that maps elements1938of the ground set to elements of the base ring.19391940OUTPUT:19411942A linear matroid `N = M([A 0; -c 1])`, where `A` is a matrix such that1943the current matroid is `M[A]`, and `c` is either given by ``row``1944(relative to ``self.representation()``) or has nonzero entries given1945by ``cochain``.19461947.. NOTE::19481949The minus sign is to ensure this method commutes with dualizing.1950See the last example.19511952.. SEEALSO::19531954:meth:`M.coextension() <sage.matroids.matroid.Matroid.coextension>`,1955:meth:`M.linear_extension() <LinearMatroid.linear_extension>`,1956:meth:`M.dual() <LinearMatroid.dual>`19571958EXAMPLES::19591960sage: M = Matroid(ring=GF(2), matrix=[[1, 1, 0, 1, 0, 0],1961....: [1, 0, 1, 0, 1, 0],1962....: [0, 1, 1, 0, 0, 1],1963....: [0, 0, 0, 1, 1, 1]])1964sage: M.linear_coextension(6, {0:1, 5: 1}).representation()1965[1 1 0 1 0 0 0]1966[1 0 1 0 1 0 0]1967[0 1 1 0 0 1 0]1968[0 0 0 1 1 1 0]1969[1 0 0 0 0 1 1]1970sage: M.linear_coextension(6, row=[0,1,1,1,0,1]).representation()1971[1 1 0 1 0 0 0]1972[1 0 1 0 1 0 0]1973[0 1 1 0 0 1 0]1974[0 0 0 1 1 1 0]1975[0 1 1 1 0 1 1]19761977Coextending commutes with dualizing::19781979sage: M = matroids.named_matroids.NonFano()1980sage: chain = {'a': 1, 'b': -1, 'f': 1}1981sage: M1 = M.linear_coextension('x', chain)1982sage: M2 = M.dual().linear_extension('x', chain)1983sage: M1 == M2.dual()1984True1985"""1986cdef LeanMatrix col1987cdef LeanMatrix rw1988cdef long i1989if element in self.groundset():1990raise ValueError("extension element is already in groundset")1991if self._representation is not None and row is not None:1992R = self.base_ring()1993rw = type(self._representation)(1, self._representation.ncols(), ring=R)1994i = 01995for x in row:1996if i == self._representation.ncols():1997raise ValueError("provided row has too many entries")1998rw.set_unsafe(0, i, -R(x))1999i += 12000if i < self._representation.ncols():2001raise ValueError("provided row has too few entries")2002E = self._E + (element,)2003col = type(self._representation)(self._representation.nrows() + 1, 1, ring=self.base_ring())2004col.set_unsafe(self._representation.nrows(), 0, self._one)2005return type(self)(matrix=self._representation.stack(rw).augment(col), groundset=E)2006elif row is not None:2007raise ValueError("can only specify row relative to fixed representation. Run self.representation() first.")2008else:2009if not isinstance(cochain, dict):2010raise TypeError("cochain argument needs to be a dictionary")2011return self._linear_coextensions(element, [cochain])[0]20122013cpdef _linear_extensions(self, element, chains):2014r"""2015Return the linear extensions of this matroid representation specified2016by the given chains.20172018This is an internal method that does no checking on the input.20192020INPUT:20212022- ``element`` -- the name of the new element.2023- ``chains`` -- a list of dictionaries, each of which maps elements of2024the ground set to elements of the base ring.20252026OUTPUT:20272028A list of linear matroids `N = M([A b])`, where `A` is a matrix such2029that the current matroid is `M[A]`, and `b` is a weighted combination2030of columns of `A`, the weigths being given by the elements of2031``chains``.20322033EXAMPLES::20342035sage: M = Matroid(ring=GF(2), matrix=[[1, 1, 0, 1, 0, 0],2036....: [1, 0, 1, 0, 1, 0], [0, 1, 1, 0, 0, 1], [0, 0, 0, 1, 1, 1]])2037sage: M._linear_extensions(6, [{0:1, 5: 1}])[0].representation()2038[1 1 0 1 0 0 1]2039[1 0 1 0 1 0 1]2040[0 1 1 0 0 1 1]2041[0 0 0 1 1 1 1]2042"""2043cdef long i, j2044cdef LeanMatrix M2045ext = []2046if self._representation is None:2047M = type(self._A)(self.full_rank(), self.size() + 1, self._basic_representation())2048else:2049M = type(self._A)(self._representation.nrows(), self.size() + 1, self._representation)2050E = self._E + (element,)2051D = {}2052for i from 0 <= i < self.size():2053D[E[i]] = i2054for chain in chains:2055for i from 0 <= i < M.nrows():2056a = self._zero2057for e in chain:2058a += M.get_unsafe(i, D[e]) * chain[e]2059M.set_unsafe(i, self.size(), a)2060ext.append(type(self)(matrix=M, groundset=E))2061return ext20622063cpdef _linear_coextensions(self, element, cochains):2064r"""2065Return the linear coextensions of this matroid representation2066specified by the given cochains.20672068Internal method that does no typechecking.20692070INPUT:20712072- ``element`` -- the name of the new element.2073- ``cochains`` -- a list of dictionaries, each of which maps elements2074of the ground set to elements of the base ring.20752076OUTPUT:20772078A list of linear matroids `N = M([A 0; -c 1])`, where `A` is a matrix2079such that the current matroid is `M[A]`, and `c` has nonzero entries2080given by ``cochain``.20812082EXAMPLES::20832084sage: M = Matroid(ring=GF(2), matrix=[[1, 1, 0, 1, 0, 0],2085....: [1, 0, 1, 0, 1, 0], [0, 1, 1, 0, 0, 1], [0, 0, 0, 1, 1, 1]])2086sage: M._linear_coextensions(6, [{0:1, 5: 1}])[0].representation()2087[1 1 0 1 0 0 0]2088[1 0 1 0 1 0 0]2089[0 1 1 0 0 1 0]2090[0 0 0 1 1 1 0]2091[1 0 0 0 0 1 1]2092"""2093cdef long i, j2094cdef LeanMatrix M2095coext = []2096if self._representation is None:2097M = type(self._A)(self.full_rank() + 1, self.size() + 1, self._basic_representation())2098else:2099M = type(self._A)(self._representation.nrows() + 1, self.size() + 1, self._representation)2100M.set_unsafe(M.nrows() - 1, M.ncols() - 1, self._one)2101E = self._E + (element,)2102D = {}2103for i from 0 <= i < self.size():2104D[E[i]] = i2105for cochain in cochains:2106for i from 0 <= i < M.ncols() - 1:2107M.set_unsafe(M.nrows() - 1, i, 0)2108for e in cochain:2109M.set_unsafe(M.nrows() - 1, D[e], -cochain[e])2110coext.append(type(self)(matrix=M, groundset=E))2111return coext21122113cdef _extend_chains(self, C, f, fundamentals=None):2114r"""2115Extend a list of chains for ``self / f`` to a list of chains for2116``self``.21172118See :meth:`linear_extension_chains` for full documentation.2119"""2120# assumes connected self, non-loop f, chains with basic support2121R = self.base_ring()2122res = []2123for c in C:2124if len(set(c.keys())) == 0:2125values = [self._one]2126else:2127if fundamentals is None:2128values = R2129else: # generate only chains that satisfy shallow test for 'crossratios in fundamentals'2130T = set(c.keys())2131if not self._is_independent(T | set([f])):2132raise ValueError("_extend_chains can only extend chains with basic support")2133self._move_current_basis(T | set([f]), set())2134B = self.basis()2135mult = {f: self._one}2136mult2 = {}2137todo = set([f])2138todo2 = set()2139while len(todo) > 0 or len(todo2) > 0:2140while len(todo) > 0:2141r = todo.pop()2142cocirc = self.fundamental_cocycle(B, r)2143for s, v in cocirc.iteritems():2144if s != r and s not in mult2:2145mult2[s] = mult[r] * v2146todo2.add(s)2147while len(todo2) > 0:2148s = todo2.pop()2149circ = self.fundamental_cycle(B, s)2150for t, w in circ.iteritems():2151if t != s and t not in mult:2152mult[t] = mult2[s] / w2153if t not in T:2154todo.add(t)2155T2 = set(mult.keys()) & T2156t = T2.pop()2157m = -mult[t] * c[t]2158values = set([fund * m for fund in fundamentals])2159while len(T2) > 0:2160t = T2.pop()2161m = -mult[t] * c[t]2162values &= set([fund * m for fund in fundamentals])2163for x in values:2164if x != 0:2165cp = c.copy()2166cp[f] = x2167res.append(cp)2168res.append(c)2169ne = newlabel(self._E)2170if fundamentals is not None and self.full_rank() > 1:2171hyperlines = [F for F in self.flats(self.full_rank() - 2) if self._line_length(F) > 2]2172res = [chain for chain in res if len(chain) < 2 or self._linear_extensions(ne, [chain])[0]._cross_ratio_test(ne, fundamentals, hyperlines)]2173return res21742175cpdef _linear_extension_chains(self, F, fundamentals=None): # assumes independent F2176r"""2177Create a list of chains that determine single-element extensions of2178this linear matroid representation.21792180.. WARNING::21812182Intended for internal use; does no input checking.21832184INPUT:21852186- ``F`` -- an independent set of elements.2187- ``fundamentals`` -- (default: ``None``) a set elements of the base2188ring.21892190OUTPUT:21912192A list of chains, so each single-element extension of this linear2193matroid, with support contained in ``F``, is given by one of these2194chains.21952196EXAMPLES::21972198sage: M = Matroid(reduced_matrix=Matrix(GF(2), [[1, 1, 0],2199....: [1, 0, 1], [0, 1, 1]]))2200sage: len(M._linear_extension_chains(F=set([0, 1, 2])))220182202sage: M._linear_extension_chains(F=set())2203[{}]2204sage: M._linear_extension_chains(F=set([1]))2205[{}, {1: 1}]2206sage: len(M._linear_extension_chains(F=set([0, 1])))220742208sage: N = Matroid(ring=QQ, reduced_matrix=[[1, 1, 0],2209....: [1, 0, 1], [0, 1, 1]])2210sage: N._linear_extension_chains(F=set([0, 1]),2211....: fundamentals=set([1, -1, 1/2, 2]))2212[{0: 1}, {}, {0: 1, 1: 1}, {0: -1, 1: 1}, {1: 1}]2213"""22142215if len(F) == 0:2216return [{}]2217if len(F) == 1:2218return [{}, {min(F): self._one}]2219C = self.components()2220if len(C) == 1:2221for f in F:2222sf = set([f])2223ff = self._closure(sf)2224M = self._minor(contractions=sf, deletions=ff - sf)2225if M.is_connected():2226break2227FM = F & M.groundset()2228chains = M._linear_extension_chains(FM, fundamentals)2229chains = self._extend_chains(chains, f, fundamentals)2230else:2231comp_chains = {} # make chains for each component2232for comp in C:2233FM = F & comp2234A = self._max_independent(self.groundset() - comp)2235B = self.groundset() - (comp | A)2236M = self._minor(deletions=B, contractions=A)2237M._forget()2238comp_chains[comp] = M._linear_extension_chains(FM, fundamentals)22392240chains = [{}] # make cartesian product of component chains2241for comp in comp_chains:2242new_chains = []2243for c in chains:2244for d in comp_chains[comp]:2245c_new = copy(c)2246c_new.update(d)2247new_chains.append(c_new)2248chains = new_chains2249return chains22502251cpdef linear_extension_chains(self, F=None, simple=False, fundamentals=None):2252r"""2253Create a list of chains that determine the single-element extensions2254of this linear matroid representation.22552256A *chain* is a dictionary, mapping elements from the groundset to2257elements of the base ring, indicating a linear combination of columns2258to form the new column. Think of chains as vectors, only independent2259of representation.22602261INPUT:22622263- ``F`` -- (default: ``self.groundset()``) a subset of the groundset.2264- ``simple`` -- (default: ``False``) a boolean variable.2265- ``fundamentals`` -- (default: ``None``) a set elements of the base2266ring.22672268OUTPUT:22692270A list of chains, so each single-element extension of this linear2271matroid representation is given by one of these chains.22722273If one or more of the above inputs is given, the list is restricted to2274chains22752276- so that the support of each chain lies in ``F``, if given2277- so that the chain does not generate a parallel extension or loop, if2278``simple = True``2279- so that in the extension generated by this chain, the cross ratios2280are restricted to ``fundamentals``, if given.22812282.. SEEALSO::22832284:meth:`M.linear_extension() <LinearMatroid.linear_extension>`,2285:meth:`M.linear_extensions() <LinearMatroid.linear_extensions>`,2286:meth:`M.cross_ratios() <LinearMatroid.cross_ratios>`22872288EXAMPLES::22892290sage: M = Matroid(reduced_matrix=Matrix(GF(2),2291....: [[1, 1, 0], [1, 0, 1], [0, 1, 1]]))2292sage: len(M.linear_extension_chains())229382294sage: len(M.linear_extension_chains(F=[0, 1]))229542296sage: len(M.linear_extension_chains(F=[0, 1], simple=True))229702298sage: M.linear_extension_chains(F=[0, 1, 2], simple=True)2299[{0: 1, 1: 1, 2: 1}]2300sage: N = Matroid(ring=QQ,2301....: reduced_matrix=[[-1, -1, 0], [1, 0, -1], [0, 1, 1]])2302sage: N.linear_extension_chains(F=[0, 1], simple=True,2303....: fundamentals=set([1, -1, 1/2, 2]))2304[{0: 1, 1: 1}, {0: -1/2, 1: 1}, {0: -2, 1: 1}]2305"""2306if F is None:2307FI = self.basis()2308else:2309FI = self.max_independent(F)2310M = self._minor(contractions=set(), deletions=self.loops())2311M._forget() # this is necessary for testing the crossratios of the extension2312# --> skips gauss-jordan reduction when taking minors of M2313# TODO: maybe make this an optional argument for _minor?2314# TODO: make sure the _representation isn't accidentally recreated anywhere (currently this only happens when self.representation() is called)2315chains = M._linear_extension_chains(FI, fundamentals)23162317if simple: # filter out chains that produce a parallel element,2318par = [] # test uses that each supp(chain) is independent2319self._move_current_basis(FI, set())2320B = self.basis()2321for e in self.groundset() - B:2322C = self.fundamental_cycle(B, e)2323C.pop(e)2324par.append(C)2325simple_chains = []2326for c in chains:2327if len(c) < 2:2328continue2329parallel = False2330for p in par:2331if set(p.keys()) == set(c.keys()):2332parallel = True2333e = min(p)2334ratio = c[e] / p[e]2335for f, w in p.iteritems():2336if c[f] / w != ratio:2337parallel = False2338break2339if parallel:2340break2341if not parallel:2342simple_chains.append(c)2343chains = simple_chains2344return chains23452346cpdef linear_coextension_cochains(self, F=None, cosimple=False, fundamentals=None):2347r"""2348Create a list of cochains that determine the single-element2349coextensions of this linear matroid representation.23502351A cochain is a dictionary, mapping elements from the groundset to2352elements of the base ring. If `A` represents the current matroid, then2353the coextension is given by `N = M([A 0; -c 1])`, with the entries of2354`c` given by the cochain. Note that the matroid does not change when2355row operations are carried out on `A`.23562357INPUT:23582359- ``F`` -- (default: ``self.groundset()``) a subset of the groundset.2360- ``cosimple`` -- (default: ``False``) a boolean variable.2361- ``fundamentals`` -- (default: ``None``) a set elements of the base2362ring.23632364OUTPUT:23652366A list of cochains, so each single-element coextension of this linear2367matroid representation is given by one of these cochains.23682369If one or more of the above inputs is given, the list is restricted to2370chains23712372- so that the support of each cochain lies in ``F``, if given2373- so that the cochain does not generate a series extension or coloop,2374if ``cosimple = True``2375- so that in the coextension generated by this cochain, the cross2376ratios are restricted to ``fundamentals``, if given.23772378.. SEEALSO::23792380:meth:`M.linear_coextension() <LinearMatroid.linear_coextension>`,2381:meth:`M.linear_coextensions() <LinearMatroid.linear_coextensions>`,2382:meth:`M.cross_ratios() <LinearMatroid.cross_ratios>`23832384EXAMPLES::23852386sage: M = Matroid(reduced_matrix=Matrix(GF(2),2387....: [[1, 1, 0], [1, 0, 1], [0, 1, 1]]))2388sage: len(M.linear_coextension_cochains())238982390sage: len(M.linear_coextension_cochains(F=[0, 1]))239142392sage: len(M.linear_coextension_cochains(F=[0, 1], cosimple=True))239302394sage: M.linear_coextension_cochains(F=[3, 4, 5], cosimple=True)2395[{3: 1, 4: 1, 5: 1}]2396sage: N = Matroid(ring=QQ,2397....: reduced_matrix=[[-1, -1, 0], [1, 0, -1], [0, 1, 1]])2398sage: N.linear_coextension_cochains(F=[0, 1], cosimple=True,2399....: fundamentals=set([1, -1, 1/2, 2]))2400[{0: 2, 1: 1}, {0: 1/2, 1: 1}, {0: -1, 1: 1}]2401"""2402return self.dual().linear_extension_chains(F=F, simple=cosimple, fundamentals=fundamentals)24032404cpdef linear_extensions(self, element=None, F=None, simple=False, fundamentals=None):2405r"""2406Create a list of linear matroids represented by single-element2407extensions of this linear matroid representation.24082409INPUT:24102411- ``element`` -- (default: ``None``) the name of the new element of2412the groundset.2413- ``F`` -- (default: ``None``) a subset of the ground set.2414- ``simple`` -- (default: ``False``) a boolean variable.2415- ``fundamentals`` -- (default: ``None``) a set elements of the base2416ring.24172418OUTPUT:24192420A list of linear matroids represented by single-element extensions of2421this linear matroid representation.24222423If one or more of the above inputs is given, the list is restricted to2424matroids24252426- so that the new element is spanned by ``F``, if given2427- so that the new element is not a loop or in a parallel pair, if2428``simple=True``2429- so that in the representation of the extension, the cross ratios are2430restricted to ``fundamentals``, if given. Note that it is assumed2431that the cross ratios of the input matroid already satisfy this2432condition.24332434.. SEEALSO::24352436:meth:`M.linear_extension() <LinearMatroid.linear_extension>`,2437:meth:`M.linear_extension_chains() <LinearMatroid.linear_extension_chains>`,2438:meth:`M.cross_ratios() <LinearMatroid.cross_ratios>`24392440EXAMPLES::24412442sage: M = Matroid(ring=GF(2),2443....: reduced_matrix=[[-1, 0, 1], [1, -1, 0], [0, 1, -1]])2444sage: len(M.linear_extensions())244582446sage: S = M.linear_extensions(simple=True)2447sage: S2448[Binary matroid of rank 3 on 7 elements, type (3, 0)]2449sage: S[0].is_field_isomorphic(matroids.named_matroids.Fano())2450True2451sage: M = Matroid(ring=QQ,2452....: reduced_matrix=[[1, 0, 1], [1, 1, 0], [0, 1, 1]])2453sage: S = M.linear_extensions(simple=True,2454....: fundamentals=[1, -1, 1/2, 2])2455sage: len(S)245672457sage: any(N.is_isomorphic(matroids.named_matroids.NonFano())2458....: for N in S)2459True2460sage: len(M.linear_extensions(simple=True,2461....: fundamentals=[1, -1, 1/2, 2], F=[0, 1]))246212463"""2464if element is None:2465element = newlabel(self.groundset())2466else:2467if element in self.groundset():2468raise ValueError("cannot extend by element already in groundset")2469chains = self.linear_extension_chains(F, simple=simple, fundamentals=fundamentals)2470return self._linear_extensions(element, chains)24712472cpdef linear_coextensions(self, element=None, F=None, cosimple=False, fundamentals=None):2473r"""2474Create a list of linear matroids represented by single-element2475coextensions of this linear matroid representation.24762477INPUT:24782479- ``element`` -- (default: ``None``) the name of the new element of2480the groundset.2481- ``F`` -- (default: ``None``) a subset of the ground set.2482- ``cosimple`` -- (default: ``False``) a boolean variable.2483- ``fundamentals`` -- (default: ``None``) a set elements of the base2484ring.24852486OUTPUT:24872488A list of linear matroids represented by single-element coextensions2489of this linear matroid representation.24902491If one or more of the above inputs is given, the list is restricted to2492coextensions24932494- so that the new element lies in the cospan of ``F``, if given.2495- so that the new element is no coloop and is not in series with2496another element, if ``cosimple = True``.2497- so that in the representation of the coextension, the cross ratios2498are restricted to ``fundamentals``, if given. Note that it is2499assumed that the cross ratios of the input matroid already satisfy2500this condition.25012502.. SEEALSO::25032504:meth:`M.linear_coextension() <LinearMatroid.linear_coextension>`,2505:meth:`M.linear_coextension_cochains() <LinearMatroid.linear_coextension_cochains>`,2506:meth:`M.cross_ratios() <LinearMatroid.cross_ratios>`25072508EXAMPLES::25092510sage: M = Matroid(ring=GF(2),2511....: reduced_matrix=[[-1, 0, 1], [1, -1, 0], [0, 1, -1]])2512sage: len(M.linear_coextensions())251382514sage: S = M.linear_coextensions(cosimple=True)2515sage: S2516[Binary matroid of rank 4 on 7 elements, type (3, 7)]2517sage: F7 = matroids.named_matroids.Fano()2518sage: S[0].is_field_isomorphic(F7.dual())2519True2520sage: M = Matroid(ring=QQ,2521....: reduced_matrix=[[1, 0, 1], [1, 1, 0], [0, 1, 1]])2522sage: S = M.linear_coextensions(cosimple=True,2523....: fundamentals=[1, -1, 1/2, 2])2524sage: len(S)252572526sage: NF7 = matroids.named_matroids.NonFano()2527sage: any(N.is_isomorphic(NF7.dual()) for N in S)2528True2529sage: len(M.linear_coextensions(cosimple=True,2530....: fundamentals=[1, -1, 1/2, 2],2531....: F=[3, 4]))253212533"""2534if element is None:2535element = newlabel(self.groundset())2536else:2537if element in self.groundset():2538raise ValueError("cannot extend by element already in groundset")2539cochains = self.linear_coextension_cochains(F, cosimple=cosimple, fundamentals=fundamentals)2540return self._linear_coextensions(element, cochains)25412542cpdef is_valid(self):2543r"""2544Test if the data represent an actual matroid.25452546Since this matroid is linear, we test the representation matrix.25472548OUTPUT:25492550- ``True`` if the matrix is over a field.2551- ``True`` if the matrix is over a ring and all cross ratios are2552invertible.2553- ``False`` otherwise.25542555.. NOTE::25562557This function does NOT test if the cross ratios are contained in2558the appropriate set of fundamentals. To that end, use25592560``M.cross_ratios().issubset(F)``25612562where ``F`` is the set of fundamentals.25632564.. SEEALSO::25652566:meth:`M.cross_ratios() <LinearMatroid.cross_ratios>`25672568EXAMPLES::25692570sage: M = Matroid(ring=QQ, reduced_matrix=Matrix(ZZ,2571....: [[1, 0, 1], [1, 1, 0], [0, 1, 1]]))2572sage: M.is_valid()2573True2574sage: from sage.matroids.advanced import * # LinearMatroid2575sage: M = LinearMatroid(ring=ZZ, reduced_matrix=Matrix(ZZ,2576....: [[1, 0, 1], [1, 1, 0], [0, 1, 1]]))2577sage: M.is_valid()2578False2579"""2580if self.base_ring().is_field():2581return True2582try:2583CR = self.cross_ratios()2584except (ArithmeticError, TypeError, ValueError):2585return False2586for x in CR:2587if not x ** (-1) in self.base_ring():2588return False2589return True25902591# Copying, loading, saving25922593def __copy__(self):2594"""2595Create a shallow copy.25962597EXAMPLES::25982599sage: M = Matroid(Matrix(GF(7), [[1, 0, 0, 1, 1], [0, 1, 0, 1, 2],2600....: [0, 0, 1, 1, 3]]))2601sage: N = copy(M) # indirect doctest2602sage: M == N2603True2604"""2605cdef LinearMatroid N2606if self._representation is not None:2607N = LinearMatroid(groundset=self._E, matrix=self._representation, keep_initial_representation=True)2608else:2609rows, cols = self._current_rows_cols()2610N = LinearMatroid(groundset=rows + cols, reduced_matrix=self._A)2611N.rename(getattr(self, '__custom_name'))2612return N26132614def __deepcopy__(self, memo):2615"""2616Create a deep copy.26172618EXAMPLES::26192620sage: M = Matroid(Matrix(GF(7), [[1, 0, 0, 1, 1], [0, 1, 0, 1, 2],2621....: [0, 0, 1, 1, 3]]))2622sage: N = deepcopy(M) # indirect doctest2623sage: M == N2624True2625"""2626cdef LinearMatroid N2627if self._representation is not None:2628N = LinearMatroid(groundset=deepcopy(self._E, memo), matrix=deepcopy(self._representation, memo), keep_initial_representation=True)2629else:2630rows, cols = self._current_rows_cols()2631N = LinearMatroid(groundset=deepcopy(rows + cols, memo), reduced_matrix=deepcopy(self._A, memo))2632N.rename(deepcopy(getattr(self, '__custom_name'), memo))2633return N26342635def __reduce__(self):2636"""2637Save the matroid for later reloading.26382639OUTPUT:26402641A tuple ``(unpickle_lean_linear_matroid, (version, data))``, where2642``unpickle_lean_linear_matroid`` is the name of a function that, when2643called with ``(version, data)``, produces a matroid isomorphic to2644``self``. ``version`` is an integer (currently 0) and ``data`` is a2645tuple ``(A, E, reduced, name)`` where ``A`` is the representation2646matrix, ``E`` is the groundset of the matroid, ``reduced`` is a2647boolean indicating whether ``A`` is a reduced matrix, and ``name`` is2648a custom name.26492650.. WARNING::26512652Users should never call this function directly.26532654EXAMPLES::26552656sage: M = Matroid(Matrix(GF(7), [[1, 0, 0, 1, 1], [0, 1, 0, 1, 2],2657....: [0, 0, 1, 1, 3]]))2658sage: M == loads(dumps(M)) # indirect doctest2659True2660sage: M.rename("U35")2661sage: loads(dumps(M))2662U352663sage: M = Matroid(Matrix(GF(7), [[1, 0, 1], [1, 0, 1]]))2664sage: N = loads(dumps(M))2665sage: N.representation()2666[1 0 1]2667[1 0 1]2668"""2669import sage.matroids.unpickling2670cdef LeanMatrix A2671version = 02672if self._representation is not None:2673A = self._representation2674gs = self._E2675reduced = False2676else:2677A = self._reduced_representation()2678rows, cols = self._current_rows_cols()2679gs = rows + cols2680reduced = True2681data = (A, gs, reduced, getattr(self, '__custom_name'))2682return sage.matroids.unpickling.unpickle_linear_matroid, (version, data)26832684# Binary matroid26852686cdef class BinaryMatroid(LinearMatroid):2687r"""2688Binary matroids.26892690A binary matroid is a linear matroid represented over the finite field2691with two elements. See :class:`LinearMatroid` for a definition.26922693The simplest way to create a ``BinaryMatroid`` is by giving only a matrix2694`A`. Then, the groundset defaults to ``range(A.ncols())``. Any iterable2695object `E` can be given as a groundset. If `E` is a list, then ``E[i]``2696will label the `i`-th column of `A`. Another possibility is to specify a2697*reduced* matrix `B`, to create the matroid induced by `A = [ I B ]`.26982699INPUT:27002701- ``matrix`` -- (default: ``None``) a matrix whose column vectors2702represent the matroid.2703- ``reduced_matrix`` -- (default: ``None``) a matrix `B` such that2704`[I\ \ B]` represents the matroid, where `I` is an identity matrix with2705the same number of rows as `B`. Only one of ``matrix`` and2706``reduced_matrix`` should be provided.2707- ``groundset`` -- (default: ``None``) an iterable containing the element2708labels. When provided, must have the correct number of elements: the2709number of columns of ``matrix`` or the number of rows plus the number2710of columns of ``reduced_matrix``.2711- ``ring`` -- (default: ``None``) ignored.2712- ``keep_initial_representation`` -- (default: ``True``) decides whether2713or not an internal copy of the input matrix should be preserved. This2714can help to see the structure of the matroid (e.g. in the case of2715graphic matroids), and makes it easier to look at extensions. However,2716the input matrix may have redundant rows, and sometimes it is desirable2717to store only a row-reduced copy.2718- ``basis`` -- (default: ``None``) When provided, this is an ordered2719subset of ``groundset``, such that the submatrix of ``matrix`` indexed2720by ``basis`` is an identity matrix. In this case, no row reduction takes2721place in the initialization phase.27222723OUTPUT:27242725A :class:`BinaryMatroid` instance based on the data above.27262727.. NOTE::27282729An indirect way to generate a binary matroid is through the2730:func:`Matroid() <sage.matroids.constructor.Matroid>` function. This2731is usually the preferred way, since it automatically chooses between2732:class:`BinaryMatroid` and other classes. For direct access to the2733``BinaryMatroid`` constructor, run::27342735sage: from sage.matroids.advanced import *27362737EXAMPLES::27382739sage: A = Matrix(GF(2), 2, 4, [[1, 0, 1, 1], [0, 1, 1, 1]])2740sage: M = Matroid(A)2741sage: M2742Binary matroid of rank 2 on 4 elements, type (0, 6)2743sage: sorted(M.groundset())2744[0, 1, 2, 3]2745sage: Matrix(M)2746[1 0 1 1]2747[0 1 1 1]2748sage: M = Matroid(matrix=A, groundset='abcd')2749sage: sorted(M.groundset())2750['a', 'b', 'c', 'd']2751sage: B = Matrix(GF(2), 2, 2, [[1, 1], [1, 1]])2752sage: N = Matroid(reduced_matrix=B, groundset='abcd')2753sage: M == N2754True2755"""2756def __init__(self, matrix=None, groundset=None, reduced_matrix=None, ring=None, keep_initial_representation=True, basis=None):2757"""2758See class definition for full documentation.27592760.. NOTE::27612762The extra argument ``basis``, when provided, is an ordered list of2763elements of the groundset, ordered such that they index a standard2764identity matrix within ``matrix``.27652766EXAMPLES::27672768sage: from sage.matroids.advanced import *2769sage: BinaryMatroid(matrix=Matrix(GF(5), [[1, 0, 1, 1, 1],2770....: [0, 1, 1, 2, 3]])) # indirect doctest2771Binary matroid of rank 2 on 5 elements, type (1, 7)2772"""2773cdef BinaryMatrix A2774cdef long r, c2775cdef list P2776global GF2, GF2_zero, GF2_one, GF2_not_defined2777if GF2_not_defined:2778GF2 = GF(2)2779GF2_zero = GF2(0)2780GF2_one = GF2(1)2781GF2_not_defined = False27822783# Setup representation; construct displayed basis2784if matrix is not None:2785A = BinaryMatrix(matrix.nrows(), matrix.ncols(), M=matrix)2786if keep_initial_representation:2787self._representation = A.copy() # Deprecated Sage matrix operation2788if basis is None:2789P = gauss_jordan_reduce(A, xrange(A.ncols()))2790A.resize(len(P)) # Not a Sage matrix operation2791self._A = A2792else:2793A = BinaryMatrix(reduced_matrix.nrows(), reduced_matrix.ncols(), M=reduced_matrix)2794P = range(A.nrows())2795self._A = A.prepend_identity() # Not a Sage matrix operation27962797# Setup groundset, BasisExchangeMatroid data2798if groundset is None:2799groundset = range(self._A.ncols())2800else:2801if len(groundset) != self._A.ncols():2802raise ValueError("size of groundset does not match size of matrix")2803if basis is None:2804bas = [groundset[i] for i in P]2805else:2806bas = basis2807BasisExchangeMatroid.__init__(self, groundset, bas)28082809# Setup index of displayed basis2810self._prow = <long* > sage_malloc((self._A.ncols()) * sizeof(long))2811for c in xrange(self._A.ncols()):2812self._prow[c] = -12813if matrix is not None:2814if basis is None:2815for r in xrange(len(P)):2816self._prow[P[r]] = r2817else:2818for r in xrange(self._A.nrows()):2819self._prow[self._idx[basis[r]]] = r2820else:2821for r from 0 <= r < self._A.nrows():2822self._prow[r] = r28232824L = []2825for i from 0 <= i < self._A.ncols():2826L.append(self._prow[i])2827self._zero = GF2_zero2828self._one = GF2_one28292830cpdef base_ring(self):2831"""2832Return the base ring of the matrix representing the matroid,2833in this case `\GF{2}`.28342835EXAMPLES::28362837sage: M = matroids.named_matroids.Fano()2838sage: M.base_ring()2839Finite Field of size 22840"""2841global GF22842return GF228432844cpdef characteristic(self):2845"""2846Return the characteristic of the base ring of the matrix representing2847the matroid, in this case `2`.28482849EXAMPLES::28502851sage: M = matroids.named_matroids.Fano()2852sage: M.characteristic()285322854"""2855return 228562857cdef bint __is_exchange_pair(self, long x, long y):2858r"""2859Check if ``self.basis() - x + y`` is again a basis. Internal method.2860"""2861return (<BinaryMatrix>self._A).get(self._prow[x], y) # Not a Sage matrix operation28622863cdef bint __exchange(self, long x, long y):2864r"""2865Replace ``self.basis() with ``self.basis() - x + y``. Internal method, does no checks.2866"""2867cdef long p = self._prow[x]2868self._A.pivot(p, y) # Not a Sage matrix operation2869self._prow[y] = p2870BasisExchangeMatroid.__exchange(self, x, y)28712872cdef __fundamental_cocircuit(self, bitset_t C, long x):2873r"""2874Fill bitset `C` with the incidence vector of the `B`-fundamental cocircuit using ``x``. Internal method using packed elements.2875"""2876bitset_copy(C, (<BinaryMatrix>self._A)._M[self._prow[x]])28772878cdef __exchange_value(self, long x, long y):2879r"""2880Return the (x, y) entry of the current representation.2881"""2882if (<BinaryMatrix>self._A).get(self._prow[x], y): # Not a Sage matrix operation2883return self._one2884else:2885return self._zero28862887def _repr_(self):2888"""2889Return a string representation of ``self``.28902891The type consists of :meth:`BinaryMatroid.bicycle_dimension` and2892:meth:`BinaryMatroid.brown_invariant`.28932894EXAMPLES::28952896sage: M = matroids.named_matroids.Fano()2897sage: M.rename()2898sage: repr(M) # indirect doctest2899'Binary matroid of rank 3 on 7 elements, type (3, 0)'2900"""2901S = "Binary matroid of rank " + str(self.rank()) + " on " + str(self.size()) + " elements, type (" + str(self.bicycle_dimension()) + ', ' + str(self.brown_invariant()) + ')'2902return S29032904cpdef _current_rows_cols(self, B=None):2905"""2906Return the current row and column labels of a reduced matrix.29072908INPUT:29092910- ``B`` -- (default: ``None``) If provided, first find a basis having2911maximal intersection with ``B``.29122913OUTPUT:29142915- ``R`` -- A list of row indices; corresponds to the currently used2916internal basis2917- ``C`` -- A list of column indices; corresponds to the complement of2918the current internal basis29192920EXAMPLES::29212922sage: M = matroids.named_matroids.Fano()2923sage: A = M._reduced_representation('efg')2924sage: R, C = M._current_rows_cols()2925sage: (sorted(R), sorted(C))2926(['e', 'f', 'g'], ['a', 'b', 'c', 'd'])2927sage: R, C = M._current_rows_cols(B='abg')2928sage: (sorted(R), sorted(C))2929(['a', 'b', 'g'], ['c', 'd', 'e', 'f'])29302931"""2932if B is not None:2933self._move_current_basis(B, set())2934basis = self.basis()2935rows = [0] * self.full_rank()2936cols = [0] * self.full_corank()2937c = 02938for e in self._E:2939if e in basis:2940rows[self._prow[self._idx[e]]] = e2941else:2942cols[c] = e2943c += 12944return rows, cols29452946cpdef LeanMatrix _basic_representation(self, B=None):2947"""2948Return a basic matrix representation of the matroid.29492950INPUT:29512952- ``B`` -- (default: ``None``) a set of elements of the groundset.29532954OUTPUT:29552956A matrix `M` representing the matroid, where `M[B'] = I` for a basis2957`B'` that maximally intersects the given set `B`. If not provided, the2958current basis used internally is chosen for `B'`. For a stable2959representation, use ``self.representation()``.29602961.. NOTE::29622963The method self.groundset_list() gives the labelling of the2964columns by the elements of the matroid. The matrix returned2965is a LeanMatrix subclass, which is intended for internal use2966only. Use the ``representation()`` method to get a Sage matrix.29672968EXAMPLES::29692970sage: M = matroids.named_matroids.Fano()2971sage: M._basic_representation()29723 x 7 BinaryMatrix2973[1000111]2974[0101011]2975[0011101]2976sage: matrix(M._basic_representation('efg'))2977[1 1 0 1 1 0 0]2978[1 0 1 1 0 1 0]2979[1 1 1 0 0 0 1]2980"""2981if B is not None:2982self._move_current_basis(B, set())2983return self._A.copy() # Deprecated Sage matrix operation29842985cpdef LeanMatrix _reduced_representation(self, B=None):2986"""2987Return a reduced representation of the matroid, i.e. a matrix `R` such2988that `[I\ \ R]` represents the matroid.29892990INPUT:29912992- ``B`` -- (default: ``None``) a set of elements of the groundset.29932994OUTPUT:29952996A matrix `R` forming a reduced representation of the matroid, with2997rows labeled by a basis `B'` that maximally intersects the given set2998`B`. If not provided, the current basis used internally labels the2999rows.30003001.. NOTE::30023003The matrix returned is a LeanMatrix subclass, which is intended3004for internal use only. Use the ``representation()`` method to get3005a Sage matrix.30063007EXAMPLES::30083009sage: M = matroids.named_matroids.Fano()3010sage: M._reduced_representation()30113 x 4 BinaryMatrix3012[0111]3013[1011]3014[1101]3015sage: matrix(M._reduced_representation('efg'))3016[1 1 0 1]3017[1 0 1 1]3018[1 1 1 0]3019"""3020if B is not None:3021self._move_current_basis(B, set())3022rows, cols = self._current_rows_cols()3023return self._A.matrix_from_rows_and_columns(range(self.full_rank()), [self._idx[e] for e in cols])30243025# isomorphism30263027cpdef _is_isomorphic(self, other):3028"""3029Test if ``self`` is isomorphic to ``other``.30303031Internal version that performs no checks on input.30323033INPUT:30343035- ``other`` -- A matroid.30363037OUTPUT:30383039Boolean.30403041EXAMPLES::30423043sage: M1 = matroids.named_matroids.Fano()3044sage: M2 = Matroid(ring=GF(2),3045....: reduced_matrix=[[1, 0, 1, 1], [0, 1, 1, 1], [1, 1, 0, 1]])3046sage: M1._is_isomorphic(M2)3047True30483049sage: M1 = matroids.named_matroids.Fano().delete('a')3050sage: M2 = matroids.Whirl(3)3051sage: M1._is_isomorphic(M2)3052False3053sage: M1._is_isomorphic(matroids.Wheel(3))3054True3055"""3056if type(other) == BinaryMatroid:3057return self.is_field_isomorphic(other)3058else:3059return LinearMatroid._is_isomorphic(self, other)30603061# invariants3062cpdef _make_invariant(self):3063"""3064Create an invariant.30653066Internal method; see ``_invariant`` for full documentation.30673068EXAMPLES::30693070sage: M = matroids.named_matroids.Fano()3071sage: M._invariant() # indirect doctest3072(3, 0, 7, 0, 0, 0, 0, 0)3073"""3074cdef BinaryMatrix B3075cdef long r, d, i, j3076if self._b_invariant is not None:3077return3078B = (<BinaryMatrix>self._A).copy() # Deprecated Sage matrix operation3079r = B.nrows()3080b = 03081d = 03082i = 03083U = set()3084while i + d < r:3085for j in xrange(i, r - d):3086if B.row_len(j) % 2 == 1: # Not a Sage matrix operation3087B.swap_rows_c(i, j)3088break3089if B.row_len(i) % 2 == 1: # Not a Sage matrix operation3090for j in xrange(i + 1, r - d):3091if B.row_inner_product(i, j): # Not a Sage matrix operation3092B.add_multiple_of_row_c(j, i, 1, 0)3093if B.row_len(i) % 4 == 1: # Not a Sage matrix operation3094b += 13095else:3096b -= 13097U.add(i)3098i += 13099else:3100for j in xrange(i + 1, r - d):3101if B.row_inner_product(i, j): # Not a Sage matrix operation3102B.swap_rows_c(i + 1, j)3103break3104if i + 1 < r - d:3105if B.row_inner_product(i, i + 1): # Not a Sage matrix operation3106for j in xrange(i + 2, r):3107if B.row_inner_product(i, j): # Not a Sage matrix operation3108B.add_multiple_of_row_c(j, i + 1, 1, 0)3109if B.row_inner_product(i + 1, j): # Not a Sage matrix operation3110B.add_multiple_of_row_c(j, i, 1, 0)3111if B.row_len(i) % 4 == 2 and B.row_len(i + 1) % 4 == 2: # Not a Sage matrix operation3112b += 43113i += 23114else:3115d += 13116B.swap_rows_c(i, r - d)3117else:3118d += 131193120doubly_even = True3121for i in xrange(r - d, r):3122if B.row_len(i) % 4 == 2: # Not a Sage matrix operation3123doubly_even = False3124break3125if doubly_even:3126b2 = b % 83127else:3128b2 = None31293130Fm = B.row_union(range(r - d, r)) # Not a Sage matrix operation3131Fp = [i for i in B.row_sum(U) if i not in Fm] # Not a Sage matrix operation3132F0 = [i for i in range(len(self)) if i not in (Fm + Fp)]31333134BT = B.transpose()3135self._b_projection = BT._matrix_times_matrix_((B._matrix_times_matrix_(BT))._matrix_times_matrix_(B))3136P = [F0, Fp]3137p = []3138for a in xrange(2):3139for b in xrange(a + 1):3140x = 03141for i in P[a]:3142for j in P[b]:3143if self._b_projection.get(i, j) != 0: # Not a Sage matrix operation3144x += 13145p.append(x)3146if d > 0:3147F = F0 + Fp3148self._b_projection = self._b_projection.matrix_from_rows_and_columns(F, F)3149self._b_invariant = tuple([d, b2, len(Fm), len(F0), len(Fp), p[0], p[1], p[2]])3150self._b_partition = tuple([Fm, F0, Fp])31513152cpdef _invariant(self):3153r"""3154Return a matroid invariant.31553156See [Pen12]_ for more information.31573158OUTPUT:31593160A tuple ``(d, b, Lm, L0, Lp, p0, p1, p2)``, with the following3161interpretation:31623163- ``d`` is the :meth:`bicycle dimension <BinaryMatroid.bicycle_dimension>`.3164- ``b`` is the :meth:`Brown invariant <BinaryMatroid.brown_invariant>`.3165- ``(Lm, L0, Lp)`` is the triple of lengths of the principal tripartition.3166- ``(p0, p1, p2)`` are the counts of edges in a characteristic graph3167of the matroid, whose vertices are the union of ``F_-`` and ``F_0``3168from the principal tripartition.31693170EXAMPLES::31713172sage: from sage.matroids.advanced import *3173sage: M = BinaryMatroid(matroids.AG(2, 5).representation())3174sage: M._invariant()3175(2, 1, 24, 0, 1, 0, 0, 1)3176"""3177if self._b_invariant is None:3178self._make_invariant()3179return self._b_invariant31803181cpdef bicycle_dimension(self):3182r"""3183Return the bicycle dimension of the binary matroid.31843185The *bicycle dimension* of a linear subspace `V` is3186`\dim(V\cap V^\perp)`. The bicycle dimension of a matroid equals the3187bicycle dimension of its cocycle-space, and is an invariant for binary3188matroids. See [Pen12]_, [GR01]_ for more information.31893190OUTPUT:31913192Integer.31933194EXAMPLES::31953196sage: M = matroids.named_matroids.Fano()3197sage: M.bicycle_dimension()319833199"""3200if self._b_invariant is None:3201self._make_invariant()3202return self._b_invariant[0]32033204cpdef brown_invariant(self):3205r"""3206Return the value of Brown's invariant for the binary matroid.32073208For a binary space `V`, consider the sum3209`B(V):=\sum_{v\in V} i^{|v|}`, where `|v|` denotes the number of3210nonzero entries of a binary vector `v`. The value of the Tutte3211Polynomial in the point `(-i, i)` can be expressed in terms of3212`B(V)`, see [Pen12]_. If `|v|` equals `2` modulo 4 for some3213`v\in V\cap V^\perp`, then `B(V)=0`. In this case, Browns invariant is3214not defined. Otherwise, `B(V)=\sqrt{2}^k \exp(\sigma \pi i/4)` for3215some integers `k, \sigma`. In that case, `k` equals the bycycle3216dimension of `V`, and Browns invariant for `V` is defined as `\sigma`3217modulo `8`.32183219The Brown invariant of a binary matroid equals the Brown invariant of3220its cocycle-space.32213222OUTPUT:32233224Integer.32253226EXAMPLES::32273228sage: M = matroids.named_matroids.Fano()3229sage: M.brown_invariant()323003231sage: M = Matroid(Matrix(GF(2), 3, 8, [[1, 0, 0, 1, 1, 1, 1, 1],3232....: [0, 1, 0, 1, 1, 0, 0, 0],3233....: [0, 0, 1, 0, 0, 1, 1, 0]]))3234sage: M.brown_invariant() is None3235True3236"""3237if self._b_invariant is None:3238self._make_invariant()3239return self._b_invariant[1]32403241cpdef _principal_tripartition(self):3242r"""3243Return the principal tripartition of the binary matroid.32443245The principal tripartition is a partition `(F_{-1}, F_0, F_{1})` of3246the ground set. A defining property is the following. It is3247straightforward that if the bicycle dimension of a matroid `M` is `k`,3248then the bicycle dimension of `M\setminus e' is one of `k-1, k, k + 1`3249for each element `e` of `M`. Then if `F_i` denotes the set of elements3250such that the bicycle dimension of `M\setminus e` is `k + i`, we3251obtain the principal tripartition `(F_{-1}, F_0, F_{1})` of `M`.3252See [Pen12]_ and [GR01]_.32533254OUTPUT:32553256``(F_{-1}, F_0, F_{1})``, the principal tripartition of the matroid.32573258EXAMPLES::32593260sage: M = matroids.named_matroids.S8()3261sage: for F in M._principal_tripartition(): print sorted(F)3262['a', 'b', 'c', 'e', 'f', 'g']3263['d']3264['h']3265sage: M.bicycle_dimension()326623267sage: for i in [-1, 0, 1]: print sorted([e for e in M.groundset() if (M\e).bicycle_dimension() == 2 + i])3268['a', 'b', 'c', 'e', 'f', 'g']3269['d']3270['h']3271"""3272if self._b_invariant is None:3273self._make_invariant()3274P = self._b_partition3275return frozenset([self._E[e] for e in P[0]]), frozenset([self._E[e] for e in P[1]]), frozenset([self._E[e] for e in P[2]])32763277cpdef BinaryMatrix _projection(self):3278"""3279Return the projection matrix onto the row space.32803281This projection is determined modulo the bicycle space. See [Pen12]_.32823283INPUT:32843285- Nothing32863287OUTPUT:32883289A binary matrix `P`, so that the `e`-th column of `P` is the3290incidence vector of a cocycle `C` such that `C-e` is a cycle. Such a3291`C` is determined up to taking the symmetric difference with bicycles.3292We output the restriction of `P` to rows and columns that are not in3293any bicycle.32943295EXAMPLES::32963297sage: from sage.matroids.advanced import *3298sage: M = BinaryMatroid(matrix(matroids.named_matroids.R12()))3299sage: M._projection()330012 x 12 BinaryMatrix3301[001110111000]3302[001101110100]3303[111011100010]3304[110111010001]3305[101100001011]3306[011100000111]3307[111000101110]3308[110100011101]3309[100010110011]3310[010001110011]3311[001011101110]3312[000111011101]33133314"""3315if self._b_invariant is None:3316self._make_invariant()3317return self._b_projection33183319cpdef BinaryMatrix _projection_partition(self):3320"""3321Return the equitable partition of the graph whose incidence matrix is3322the projection matrix of this matroid.33233324See method ``._projection()``.33253326INPUT:33273328- Nothing33293330OUTPUT:33313332An ordered partition.33333334sage: from sage.matroids.advanced import *3335sage: M = matroids.named_matroids.R12()3336sage: N = BinaryMatroid(reduced_matrix=M.representation(reduced=True,3337....: labels=False), groundset='abcdefghijkl')3338sage: N._projection_partition()33392 x 12 BinaryMatrix3340[110011001100]3341[001100110011]3342"""3343if self._eq_part is None:3344if self._b_invariant is None:3345self._make_invariant()3346self._eq_part = self._b_projection.equitable_partition() # Not a Sage matrix operation3347return self._eq_part33483349cpdef _fast_isom_test(self, other):3350r"""3351Run a quick test to see if two binary matroids are isomorphic.33523353The test is based on comparing strong invariants. See [Pen12]_ for a3354full account of these invariants.33553356INPUT:33573358- ``other`` -- a binary matroid.33593360OUTPUT:33613362- ``True``, if ``self`` is isomorphic to ``other``;3363- ``False``, if ``self`` is not isomorphic to ``other``;3364- ``None``, if this test is inconclusive33653366EXAMPLES::33673368sage: M = matroids.named_matroids.S8()3369sage: N = matroids.named_matroids.S8()3370sage: M._fast_isom_test(N) is None3371True3372"""3373if self._invariant() != other._invariant():3374return False3375q = self._projection().is_isomorphic(other._projection(), self._projection_partition(), other._projection_partition()) # Not a Sage matrix operation3376if self.bicycle_dimension() == 0:3377return q3378if not q:3379return False33803381# minors, dual33823383cpdef _minor(self, contractions, deletions):3384r"""3385Return a minor.33863387INPUT:33883389- ``contractions`` -- An object with Python's ``frozenset`` interface3390containing a subset of ``self.groundset()``.3391- ``deletions`` -- An object with Python's ``frozenset`` interface3392containing a subset of ``self.groundset()``.33933394.. NOTE::33953396This method does NOT do any checks. Besides the assumptions above,3397we assume the following:33983399- ``contractions`` is independent3400- ``deletions`` is coindependent3401- ``contractions`` and ``deletions`` are disjoint.34023403OUTPUT:34043405A matroid.34063407EXAMPLES::34083409sage: M = matroids.named_matroids.Fano()3410sage: N = M._minor(contractions=set(['a']), deletions=set([]))3411sage: N._minor(contractions=set([]), deletions=set(['b', 'c']))3412Binary matroid of rank 2 on 4 elements, type (0, 6)3413"""3414self._move_current_basis(contractions, deletions)3415bas = list(self.basis() - contractions)3416R = [self._prow[self._idx[b]] for b in bas]3417C = [c for c in range(len(self._E)) if self._E[c] not in deletions | contractions]3418return BinaryMatroid(matrix=(<BinaryMatrix>self._A).matrix_from_rows_and_columns(R, C), groundset=[self._E[c] for c in C], basis=bas)34193420# graphicness test3421cpdef is_graphic(self):3422"""3423Test if the binary matroid is graphic.34243425A matroid is *graphic* if there exists a graph whose edge set equals3426the groundset of the matroid, such that a subset of elements of the3427matroid is independent if and only if the corresponding subgraph is3428acyclic.34293430OUTPUT:34313432Boolean.34333434EXAMPLES::34353436sage: R10 = matroids.named_matroids.R10()3437sage: M = Matroid(ring=GF(2), reduced_matrix=R10.representation(3438....: reduced=True, labels=False))3439sage: M.is_graphic()3440False3441sage: K5 = matroids.CompleteGraphic(5)3442sage: M = Matroid(ring=GF(2), reduced_matrix=K5.representation(3443....: reduced=True, labels=False))3444sage: M.is_graphic()3445True3446sage: M.dual().is_graphic()3447False34483449.. ALGORITHM:34503451In a recent paper, Geelen and Gerards [GG12]_ reduced the problem to3452testing if a system of linear equations has a solution. While not the3453fastest method, and not necessarily constructive (in the presence of34542-separations especially), it is easy to implement.3455"""3456global GF23457B= self.basis()3458Bl = [e for e in B]3459C = [self._cocircuit((self.groundset() - B) | set([e])) for e in Bl]34603461c = 13462col = {}3463for e in xrange(len(B)):3464for f in range(len(B)):3465if e is not f:3466col[e, f] = c3467c += 13468M = {}3469r = 03470for e in xrange(len(B)):3471for f in xrange(e):3472for g in xrange(f):3473if not C[e].issuperset(C[f] & C[g]):3474M[(r, col[e, f])] = 13475M[(r, col[e, g])] = 13476M[(r, 0)] = 03477r += 13478if not C[f].issuperset(C[e] & C[g]):3479M[(r, col[f, e])] = 13480M[(r, col[f, g])] = 13481M[(r, 0)] = 03482r += 13483if not C[g].issuperset(C[e] & C[f]):3484M[(r, col[g, e])] = 13485M[(r, col[g, f])] = 13486M[(r, 0)] = 03487r += 13488if len(C[e] & C[f] & C[g]) > 0:3489M[(r, col[e, f])] = 13490M[(r, col[e, g])] = 13491M[(r, col[f, e])] = 13492M[(r, col[f, g])] = 13493M[(r, col[g, e])] = 13494M[(r, col[g, f])] = 13495M[(r, 0)] = 13496r += 13497m = sage.matrix.constructor.Matrix(GF2, r, c, M)3498# now self is graphic iff there is a binary vector x so that M*x = 0 and x_0 = 1, so:3499return BinaryMatroid(m).corank(frozenset([0])) > 035003501cpdef is_valid(self):3502r"""3503Test if the data obey the matroid axioms.35043505Since this is a linear matroid over the field `\GF{2}`, this is always3506the case.35073508OUTPUT:35093510``True``.35113512EXAMPLES::35133514sage: M = Matroid(Matrix(GF(2), [[]]))3515sage: M.is_valid()3516True3517"""3518return True35193520def __copy__(self):3521"""3522Create a shallow copy.35233524EXAMPLES::35253526sage: M = Matroid(Matrix(GF(2), [[1, 0, 0, 1, 1], [0, 1, 0, 1, 2],3527....: [0, 0, 1, 1, 3]]))3528sage: N = copy(M) # indirect doctest3529sage: M == N3530True3531"""3532cdef BinaryMatroid N3533cdef list basis3534if self._representation is not None:3535N = BinaryMatroid(groundset=self._E, matrix=self._representation, keep_initial_representation=True)3536else:3537basis = [0] * self.full_rank()3538for e in self.basis():3539basis[self._prow[self._idx[e]]] = e3540N = BinaryMatroid(groundset=self._E, matrix=self._A, basis=basis)3541N.rename(getattr(self, '__custom_name'))3542return N35433544def __deepcopy__(self, memo):3545"""3546Create a deep copy.35473548EXAMPLES::35493550sage: M = Matroid(Matrix(GF(2), [[1, 0, 0, 1, 1], [0, 1, 0, 1, 2],3551....: [0, 0, 1, 1, 3]]))3552sage: N = deepcopy(M) # indirect doctest3553sage: M == N3554True3555"""3556from copy import deepcopy3557cdef BinaryMatroid N3558cdef list basis3559if self._representation is not None:3560N = BinaryMatroid(groundset=deepcopy(self._E, memo), matrix=deepcopy(self._representation, memo), keep_initial_representation=True)3561else:3562basis = [0] * self.full_rank()3563for e in self.basis():3564basis[self._prow[self._idx[e]]] = e3565N = BinaryMatroid(groundset=deepcopy(self._E, memo), matrix=deepcopy(self._A, memo), basis=deepcopy(basis, memo))3566N.rename(deepcopy(getattr(self, '__custom_name'), memo))3567return N35683569def __reduce__(self):3570"""3571Save the matroid for later reloading.35723573OUTPUT:35743575A tuple ``(unpickle_binary_matroid, (version, data))``, where3576``unpickle_binary_matroid`` is the name of a function that, when3577called with ``(version, data)``, produces a matroid isomorphic to3578``self``. ``version`` is an integer (currently 0) and ``data`` is a3579tuple ``(A, E, B, name)`` where ``A`` is the representation3580matrix, ``E`` is the groundset of the matroid, ``B`` is the currently3581displayed basis, and ``name`` is a custom name.35823583.. WARNING::35843585Users should never call this function directly.35863587EXAMPLES::35883589sage: M = Matroid(Matrix(GF(2), [[1, 0, 0, 1], [0, 1, 0, 1],3590....: [0, 0, 1, 1]]))3591sage: M == loads(dumps(M)) # indirect doctest3592True3593sage: M.rename("U34")3594sage: loads(dumps(M))3595U343596sage: M = Matroid(Matrix(GF(2), [[1, 0, 1], [1, 0, 1]]))3597sage: loads(dumps(M)).representation()3598[1 0 1]3599[1 0 1]3600"""3601import sage.matroids.unpickling3602version = 03603cdef list basis = [0] * self.full_rank()3604if self._representation is not None:3605A = self._representation3606gs = self._E3607basis = None3608else:3609A = self._A3610for e in self.basis():3611basis[self._prow[self._idx[e]]] = e3612rows, cols = self._current_rows_cols()3613gs = rows + cols3614data = (A, gs, basis, getattr(self, '__custom_name'))3615return sage.matroids.unpickling.unpickle_binary_matroid, (version, data)36163617cdef class TernaryMatroid(LinearMatroid):3618r"""3619Ternary matroids.36203621A ternary matroid is a linear matroid represented over the finite field3622with three elements. See :class:`LinearMatroid` for a definition.36233624The simplest way to create a ``TernaryMatroid`` is by giving only a3625matrix `A`. Then, the groundset defaults to ``range(A.ncols())``. Any3626iterable object `E` can be given as a groundset. If `E` is a list, then3627``E[i]`` will label the `i`-th column of `A`. Another possibility is to3628specify a 'reduced' matrix `B`, to create the matroid induced by3629`A = [I\ \ B]`.36303631INPUT:36323633- ``matrix`` -- (default: ``None``) a matrix whose column vectors3634represent the matroid.3635- ``reduced_matrix`` -- (default: ``None``) a matrix `B` such that3636`[I\ \ B]` represents the matroid, where `I` is an identity matrix with3637the same number of rows as `B`. Only one of ``matrix`` and3638``reduced_matrix`` should be provided.3639- ``groundset`` -- (default: ``None``) an iterable containing the element3640labels. When provided, must have the correct number of elements: the3641number of columns of ``matrix`` or the number of rows plus the number3642of columns of ``reduced_matrix``.3643- ``ring`` -- (default: ``None``) ignored.3644- ``keep_initial_representation`` -- (default: ``True``) boolean. Decides3645whether or not an internal copy of the input matrix should be preserved.3646This can help to see the structure of the matroid (e.g. in the case of3647graphic matroids), and makes it easier to look at extensions. However,3648the input matrix may have redundant rows, and sometimes it is desirable3649to store only a row-reduced copy.3650- ``basis`` -- (default: ``None``) when provided, this is an ordered3651subset of ``groundset``, such that the submatrix of ``matrix`` indexed3652by ``basis`` is an identity matrix. In this case, no row reduction takes3653place in the initialization phase.36543655OUTPUT:36563657A ``TernaryMatroid`` instance based on the data above.36583659.. NOTE::36603661The recommended way to generate a ternary matroid is through the3662:func:`Matroid() <sage.matroids.constructor.Matroid>` function. This3663is usually the preferred way, since it automatically chooses between3664``TernaryMatroid`` and other classes. For direct access to the3665``TernaryMatroid`` constructor, run::36663667sage: from sage.matroids.advanced import *36683669EXAMPLES::36703671sage: A = Matrix(GF(3), 2, 4, [[1, 0, 1, 1], [0, 1, 1, 1]])3672sage: M = Matroid(A)3673sage: M3674Ternary matroid of rank 2 on 4 elements, type 0-3675sage: sorted(M.groundset())3676[0, 1, 2, 3]3677sage: Matrix(M)3678[1 0 1 1]3679[0 1 1 1]3680sage: M = Matroid(matrix=A, groundset='abcd')3681sage: sorted(M.groundset())3682['a', 'b', 'c', 'd']3683sage: B = Matrix(GF(2), 2, 2, [[1, 1], [1, 1]])3684sage: N = Matroid(ring=GF(3), reduced_matrix=B, groundset='abcd')3685sage: M == N3686True3687"""3688def __init__(self, matrix=None, groundset=None, reduced_matrix=None, ring=None, keep_initial_representation=True, basis=None):3689"""3690See class definition for full documentation.36913692.. NOTE::36933694The extra argument ``basis``, when provided, is an ordered list of3695elements of the groundset, ordered such that they index a standard3696identity matrix within ``matrix``.36973698EXAMPLES::36993700sage: from sage.matroids.advanced import *3701sage: TernaryMatroid(matrix=Matrix(GF(5), [[1, 0, 1, 1, 1],3702....: [0, 1, 1, 2, 3]])) # indirect doctest3703Ternary matroid of rank 2 on 5 elements, type 1+3704"""3705cdef TernaryMatrix A3706cdef long r, c3707cdef list P3708global GF3, GF3_zero, GF3_one, GF3_minus_one, GF3_not_defined3709if GF3_not_defined:3710GF3 = GF(3)3711GF3_zero = GF3(0)3712GF3_one = GF3(1)3713GF3_minus_one = GF3(2)3714GF3_not_defined = False37153716# Setup representation; construct displayed basis3717if matrix is not None:3718A = TernaryMatrix(matrix.nrows(), matrix.ncols(), M=matrix)3719if keep_initial_representation:3720self._representation = A.copy() # Deprecated Sage matrix operation3721if basis is None:3722P = gauss_jordan_reduce(A, xrange(A.ncols()))3723A.resize(len(P)) # Not a Sage matrix operation3724self._A = A3725else:3726A = TernaryMatrix(reduced_matrix.nrows(), reduced_matrix.ncols(), M=reduced_matrix)3727P = range(A.nrows())3728self._A = A.prepend_identity() # Not a Sage matrix operation37293730# Setup groundset, BasisExchangeMatroid data3731if groundset is None:3732groundset = range(self._A.ncols())3733else:3734if len(groundset) != self._A.ncols():3735raise ValueError("size of groundset does not match size of matrix")3736if basis is None:3737bas = [groundset[i] for i in P]3738else:3739bas = basis3740BasisExchangeMatroid.__init__(self, groundset, bas)37413742# Setup index of displayed basis3743self._prow = <long* > sage_malloc((self._A.ncols()) * sizeof(long))3744for c in xrange(self._A.ncols()):3745self._prow[c] = -13746if matrix is not None:3747if basis is None:3748for r in xrange(len(P)):3749self._prow[P[r]] = r3750else:3751for r in xrange(self._A.nrows()):3752self._prow[self._idx[basis[r]]] = r3753else:3754for r from 0 <= r < self._A.nrows():3755self._prow[r] = r37563757L = []3758for i from 0 <= i < self._A.ncols():3759L.append(self._prow[i])3760self._zero = GF3_zero3761self._one = GF3_one3762self._two = GF3_minus_one37633764cpdef base_ring(self):3765"""3766Return the base ring of the matrix representing the matroid, in this3767case `\GF{3}`.37683769EXAMPLES::37703771sage: M = matroids.named_matroids.NonFano()3772sage: M.base_ring()3773Finite Field of size 33774"""3775global GF33776return GF337773778cpdef characteristic(self):3779"""3780Return the characteristic of the base ring of the matrix representing3781the matroid, in this case `3`.37823783EXAMPLES::37843785sage: M = matroids.named_matroids.NonFano()3786sage: M.characteristic()378733788"""3789return 337903791cdef bint __is_exchange_pair(self, long x, long y):3792r"""3793Check if ``self.basis() - x + y`` is again a basis. Internal method.3794"""3795return (<TernaryMatrix>self._A).get(self._prow[x], y) # Not a Sage matrix operation37963797cdef bint __exchange(self, long x, long y):3798r"""3799Replace ``self.basis() with ``self.basis() - x + y``. Internal method, does no checks.3800"""3801cdef long p = self._prow[x]3802self._A.pivot(p, y) # Not a Sage matrix operation3803self._prow[y] = p3804BasisExchangeMatroid.__exchange(self, x, y)38053806cdef __fundamental_cocircuit(self, bitset_t C, long x):3807r"""3808Fill bitset `C` with the incidence vector of the `B`-fundamental cocircuit using ``x``. Internal method using packed elements.3809"""3810bitset_copy(C, (<TernaryMatrix>self._A)._M0[self._prow[x]])38113812cdef __exchange_value(self, long x, long y):3813r"""3814Return the (x, y) entry of the current representation.3815"""3816cdef long t = (<TernaryMatrix>self._A).get(self._prow[x], y) # Not a Sage matrix operation3817if t == 0:3818return self._zero3819if t == 1:3820return self._one3821if t == -1:3822return self._two38233824def _repr_(self):3825"""3826Return a string representation of ``self``.38273828The type consists of the ``bicycle_dimension`` and the ``character``.38293830EXAMPLES::38313832sage: M = matroids.named_matroids.NonFano()3833sage: M.rename()3834sage: repr(M) # indirect doctest3835'Ternary matroid of rank 3 on 7 elements, type 0-'3836"""3837S = "Ternary matroid of rank " + str(self.rank()) + " on " + str(self.size()) + " elements, type " + str(self.bicycle_dimension())3838if self.character() == 1:3839S = S + '+'3840else:3841S = S + '-'3842return S38433844cpdef _current_rows_cols(self, B=None):3845"""3846Return the current row and column labels of a reduced matrix.38473848INPUT:38493850- ``B`` -- (default: ``None``) If provided, first find a basis having3851maximal intersection with ``B``.38523853OUTPUT:38543855- ``R`` -- A list of row indices; corresponds to the currently used3856internal basis3857- ``C`` -- A list of column indices; corresponds to the complement of3858the current internal basis38593860EXAMPLES::38613862sage: M = matroids.named_matroids.NonFano()3863sage: A = M._reduced_representation('efg')3864sage: R, C = M._current_rows_cols()3865sage: (sorted(R), sorted(C))3866(['e', 'f', 'g'], ['a', 'b', 'c', 'd'])3867sage: R, C = M._current_rows_cols(B='abg')3868sage: (sorted(R), sorted(C))3869(['a', 'b', 'g'], ['c', 'd', 'e', 'f'])38703871"""3872if B is not None:3873self._move_current_basis(B, set())3874basis = self.basis()3875rows = [0] * self.full_rank()3876cols = [0] * self.full_corank()3877c = 03878for e in self._E:3879if e in basis:3880rows[self._prow[self._idx[e]]] = e3881else:3882cols[c] = e3883c += 13884return rows, cols38853886cpdef LeanMatrix _basic_representation(self, B=None):3887"""3888Return a basic matrix representation of the matroid.38893890INPUT:38913892- ``B`` -- (default: ``None``) a set of elements of the groundset.38933894OUTPUT:38953896A matrix `M` representing the matroid, where `M[B'] = I` for a basis3897`B'` that maximally intersects the given set `B`. If not provided, the3898current basis used internally is chosen for `B'`. For a stable3899representation, use ``self.representation()``.39003901.. NOTE::39023903The method self.groundset_list() gives the labelling of the3904columns by the elements of the matroid. The matrix returned is a3905LeanMatrix subclass, which is intended for internal use only. Use3906the ``representation()`` method to get a Sage matrix.39073908EXAMPLES::39093910sage: M = matroids.named_matroids.NonFano()3911sage: M._basic_representation()39123 x 7 TernaryMatrix3913[+000+++]3914[0+0+0++]3915[00+++0+]3916sage: matrix(M._basic_representation('efg'))3917[1 2 0 2 1 0 0]3918[1 0 2 2 0 1 0]3919[2 1 1 2 0 0 1]3920"""3921if B is not None:3922self._move_current_basis(B, set())3923return self._A.copy() # Deprecated Sage matrix operation39243925cpdef LeanMatrix _reduced_representation(self, B=None):3926"""3927Return a reduced representation of the matroid, i.e. a matrix `R`3928such that `[I\ \ R]` represents the matroid.39293930INPUT:39313932- ``B`` -- (default: ``None``) a set of elements of the groundset.39333934OUTPUT:39353936A matrix `R` forming a reduced representation of the matroid, with3937rows labeled by a basis `B'` that maximally intersects the given set3938`B`. If not provided, the current basis used internally labels the3939rows.39403941.. NOTE::39423943The matrix returned is a LeanMatrix subclass, which is intended3944for internal use only. Use the ``representation()`` method to get3945a Sage matrix.39463947EXAMPLES::39483949sage: M = matroids.named_matroids.NonFano()3950sage: M._reduced_representation()39513 x 4 TernaryMatrix3952[0+++]3953[+0++]3954[++0+]3955sage: matrix(M._reduced_representation('efg'))3956[1 2 0 2]3957[1 0 2 2]3958[2 1 1 2]3959"""3960if B is not None:3961self._move_current_basis(B, set())3962rows, cols = self._current_rows_cols()3963return self._A.matrix_from_rows_and_columns(range(self.full_rank()), [self._idx[e] for e in cols])39643965# isomorphism39663967cpdef _is_isomorphic(self, other):3968"""3969Test if ``self`` is isomorphic to ``other``. Internal version that3970performs no checks on input.39713972INPUT:39733974- ``other`` -- A matroid.39753976OUTPUT:39773978Boolean.39793980EXAMPLES::39813982sage: M1 = matroids.named_matroids.NonFano().delete('a')3983sage: M2 = matroids.Whirl(3)3984sage: M1._is_isomorphic(M2)3985True39863987sage: M2 = matroids.Wheel(3)3988sage: M1._is_isomorphic(M2)3989False3990"""3991if type(other) == TernaryMatroid:3992return self.is_field_isomorphic(other)3993else:3994return LinearMatroid._is_isomorphic(self, other)39953996# invariants39973998cpdef _make_invariant(self):3999"""4000Create an invariant.40014002Internal method; see ``_invariant`` for full documentation.40034004EXAMPLES::40054006sage: M = matroids.named_matroids.NonFano()4007sage: M._invariant() # indirect doctest4008(0, 2, 0, 4, 3, 0, 12, 12, 3, 0, 0, 0)4009"""4010cdef TernaryMatrix T4011cdef long i, j, d, r, x, y4012global GF340134014if self._t_invariant is not None:4015return4016T = (<TernaryMatrix>self._A).copy() # Deprecated Sage matrix operation4017r = T.nrows()4018d = 04019c = self._one4020i = 04021while i < r - d:4022for j in xrange(i, r - d):4023if T.row_inner_product(j, j) != 0: # Not a Sage matrix operation4024if j > i:4025T.swap_rows_c(i, j)4026break4027if T.row_inner_product(i, j) != 0: # Not a Sage matrix operation4028if j > i:4029T.add_multiple_of_row_c(i, j, 1, 0)4030break4031x = T.row_inner_product(i, i) # Not a Sage matrix operation4032if x == 0:4033d += 14034T.swap_rows_c(i, r - d)4035else:4036c = c * GF3(x)4037for j in xrange(i + 1, r - d):4038y = T.row_inner_product(i, j) # Not a Sage matrix operation4039if y == 0:4040continue4041if x == y:4042T.row_subs(j, i) # Not a Sage matrix operation4043else:4044T.add_multiple_of_row_c(j, i, 1, 0)4045i += 140464047TT = T.transpose()4048self._t_projection = TT._matrix_times_matrix_((T._matrix_times_matrix_(TT))._matrix_times_matrix_(T))4049F = frozenset()4050for i in xrange(r - d, r):4051F = F | frozenset(T.nonzero_positions_in_row(i))4052Fa = frozenset([j for j in xrange(len(self)) if self._t_projection.get(j, j) == 0]) - F # Not a Sage matrix operation4053Fb = frozenset([j for j in xrange(len(self)) if self._t_projection.get(j, j) == 1]) - F # Not a Sage matrix operation4054Fc = frozenset([j for j in xrange(len(self)) if self._t_projection.get(j, j) == -1]) - F # Not a Sage matrix operation40554056P = [Fa, Fb, Fc]4057p = []4058for a in xrange(3):4059for b in xrange(a + 1):4060x = 04061for i in P[a]:4062for j in P[b]:4063if self._t_projection.get(i, j) != 0: # Not a Sage matrix operation4064x += 14065p.append(x)40664067self._t_partition = tuple([F, Fa, Fb, Fc])4068self._t_invariant = tuple([d, c, len(F), len(Fa), len(Fb), len(Fc), p[0], p[1], p[2], p[3], p[4], p[5]])40694070cpdef _invariant(self):4071r"""4072Return a matroid invariant.40734074See [Pen12]_ for more information.40754076OUTPUT:40774078A tuple ``(d, c, L, La, Lb, Lc, p0, p1, p2, p3, p4, p5)``, with the4079following interpretation:40804081- ``d`` is the bicycle dimension.4082- ``c`` is the character.4083- ``(L, La, Lb, Lc)`` is the triple of lengths of the principal4084quadripartition.4085- ``(p0, ..., p5)`` counts of edges in a characteristic graph of the4086matroid whose vertex set is the ground set of the matroid,4087restricted to the sets in the principal quadripartition.40884089EXAMPLES::40904091sage: M = matroids.named_matroids.NonFano()4092sage: M._invariant()4093(0, 2, 0, 4, 3, 0, 12, 12, 3, 0, 0, 0)4094"""4095if self._t_invariant is None:4096self._make_invariant()4097return self._t_invariant40984099cpdef bicycle_dimension(self):4100"""4101Return the bicycle dimension of the ternary matroid.41024103The bicycle dimension of a linear subspace `V` is4104`\dim(V\cap V^\perp)`. The bicycle dimension of a matroid equals the4105bicycle dimension of its rowspace, and is a matroid invariant.4106See [Pen12]_.41074108OUTPUT:41094110Integer.41114112EXAMPLES::41134114sage: M = matroids.named_matroids.NonFano()4115sage: M.bicycle_dimension()411604117"""4118if self._t_invariant is None:4119self._make_invariant()4120return self._t_invariant[0]41214122cpdef character(self):4123r"""4124Return the character of the ternary matroid.41254126For a linear subspace `V` over `GF(3)` with orthogonal basis4127`q_1, \ldots, q_k` the character equals the product of `|q_i|`4128modulo 3, where the product ranges over the `i` such that `|q_i|`4129is not divisible by 3. The character does not depend on the choice of4130the orthogonal basis. The character of a ternary matroid equals the4131character of its cocycle-space, and is an invariant for ternary4132matroids. See [Pen12]_.41334134OUTPUT:41354136Integer.41374138EXAMPLES::41394140sage: M = matroids.named_matroids.NonFano()4141sage: M.character()414224143"""4144if self._t_invariant is None:4145self._make_invariant()4146return self._t_invariant[1]41474148cpdef _principal_quadripartition(self):4149r"""4150Return an ordered partition of the ground set.41514152The partition groups each element `e` of the ground set4153according to the bicycle dimension and the character of `M/e`, where4154`M` is the present matroid.41554156EXAMPLES::41574158sage: M = matroids.named_matroids.N1()4159sage: print M4160N1: Ternary matroid of rank 5 on 10 elements, type 0+4161sage: P = M._principal_quadripartition()4162sage: for e in sorted(P[0]): print e, M/e4163sage: for e in sorted(P[1]): print e, M/e4164a Ternary matroid of rank 4 on 9 elements, type 1-4165b Ternary matroid of rank 4 on 9 elements, type 1-4166e Ternary matroid of rank 4 on 9 elements, type 1-4167f Ternary matroid of rank 4 on 9 elements, type 1-4168sage: for e in sorted(P[2]): print e, M/e4169d Ternary matroid of rank 4 on 9 elements, type 0-4170i Ternary matroid of rank 4 on 9 elements, type 0-4171sage: for e in sorted(P[3]): print e, M/e4172c Ternary matroid of rank 4 on 9 elements, type 0+4173g Ternary matroid of rank 4 on 9 elements, type 0+4174h Ternary matroid of rank 4 on 9 elements, type 0+4175j Ternary matroid of rank 4 on 9 elements, type 0+417641774178"""4179if self._t_invariant is None:4180self._make_invariant()4181return tuple([[self._E[j] for j in self._t_partition[0]], [self._E[j] for j in self._t_partition[1]], [self._E[j] for j in self._t_partition[2]], [self._E[j] for j in self._t_partition[3]]])41824183cpdef TernaryMatrix _projection(self):4184"""4185Return the projection matrix onto the row space.41864187This projection is determined modulo the bicycle space. See [Pen12]_.41884189INPUT:41904191- Nothing41924193OUTPUT:41944195A ternary matrix `P`, so that the `e`-th column of `P` is the signed4196incidence vector of a cocycle `C` such that `C-e` is a cycle.4197The bicycles `e`-th column is thus determined up to the bicycle4198space. We output the restriction of `P` to rows and columns that are4199not in any bicycle.42004201EXAMPLES::42024203sage: from sage.matroids.advanced import *4204sage: M = TernaryMatroid(matrix(matroids.named_matroids.R12()))4205sage: M._projection()420612 x 12 TernaryMatrix4207[++00-0--0+++]4208[+-+000+0+-+0]4209[0+-0-00+-+0+]4210[000000000000]4211[-0-0-0+-+00+]4212[000000000000]4213[-+00+0000+--]4214[-0+0-00-+0-+]4215[0+-0+00++++-]4216[+-+000+0+-+0]4217[++0000--++00]4218[+0+0+0-+-00-]42194220"""4221if self._t_invariant is None:4222self._make_invariant()4223return self._t_projection42244225cpdef _fast_isom_test(self, other):4226r"""4227Run a quick test to see if two ternary matroids are isomorphic.42284229The test is based on comparing strong invariants, including bicycle4230dimension, character, and the principal quadripartition.4231See also [Pen12]_ .42324233INPUT:42344235- ``other`` -- a ternary matroid.42364237OUTPUT:42384239- ``True``, if ``self`` is isomorphic to ``other``;4240- ``False``, if ``self`` is not isomorphic to ``other``;4241- ``None``, if the test is inconclusive42424243EXAMPLES::42444245sage: M = matroids.named_matroids.T8()4246sage: N = matroids.named_matroids.P8()4247sage: M._fast_isom_test(N)4248False4249"""4250if self._invariant() != other._invariant():4251return False4252return None42534254# minors, dual42554256cpdef _minor(self, contractions, deletions):4257r"""4258Return a minor.42594260INPUT:42614262- ``contractions`` -- An object with Python's ``frozenset`` interface4263containing a subset of ``self.groundset()``.4264- ``deletions`` -- An object with Python's ``frozenset`` interface4265containing a subset of ``self.groundset()``.42664267.. NOTE::42684269This method does NOT do any checks. Besides the assumptions above,4270we assume the following:42714272- ``contractions`` is independent4273- ``deletions`` is coindependent4274- ``contractions`` and ``deletions`` are disjoint.42754276OUTPUT:42774278A matroid.42794280EXAMPLES::42814282sage: M = matroids.named_matroids.P8()4283sage: N = M._minor(contractions=set(['a']), deletions=set([]))4284sage: N._minor(contractions=set([]), deletions=set(['b', 'c']))4285Ternary matroid of rank 3 on 5 elements, type 0-4286"""4287self._move_current_basis(contractions, deletions)4288bas = list(self.basis() - contractions)4289R = [self._prow[self._idx[b]] for b in bas]4290C = [c for c in range(len(self._E)) if self._E[c] not in deletions | contractions]4291return TernaryMatroid(matrix=(<TernaryMatrix>self._A).matrix_from_rows_and_columns(R, C), groundset=[self._E[c] for c in C], basis=bas)42924293cpdef is_valid(self):4294r"""4295Test if the data obey the matroid axioms.42964297Since this is a linear matroid over the field `\GF{3}`, this is always4298the case.42994300OUTPUT:43014302``True``.43034304EXAMPLES::43054306sage: M = Matroid(Matrix(GF(3), [[]]))4307sage: M.is_valid()4308True4309"""4310return True43114312def __copy__(self):4313"""4314Create a shallow copy.43154316EXAMPLES::43174318sage: M = Matroid(Matrix(GF(3), [[1, 0, 0, 1, 1], [0, 1, 0, 1, 2],4319....: [0, 0, 1, 1, 3]]))4320sage: N = copy(M) # indirect doctest4321sage: M == N4322True4323"""4324cdef TernaryMatroid N4325cdef list basis4326if self._representation is not None:4327N = TernaryMatroid(groundset=self._E, matrix=self._representation, keep_initial_representation=True)4328else:4329basis = [0] * self.full_rank()4330for e in self.basis():4331basis[self._prow[self._idx[e]]] = e4332N = TernaryMatroid(groundset=self._E, matrix=self._A, basis=basis)4333N.rename(getattr(self, '__custom_name'))4334return N43354336def __deepcopy__(self, memo):4337"""4338Create a deep copy.43394340EXAMPLES::43414342sage: M = Matroid(Matrix(GF(3), [[1, 0, 0, 1, 1], [0, 1, 0, 1, 2],4343....: [0, 0, 1, 1, -1]]))4344sage: N = deepcopy(M) # indirect doctest4345sage: M == N4346True4347"""4348from copy import deepcopy4349cdef TernaryMatroid N4350cdef list basis4351if self._representation is not None:4352N = TernaryMatroid(groundset=deepcopy(self._E, memo), matrix=deepcopy(self._representation, memo), keep_initial_representation=True)4353else:4354basis = [0] * self.full_rank()4355for e in self.basis():4356basis[self._prow[self._idx[e]]] = e4357N = TernaryMatroid(groundset=deepcopy(self._E, memo), matrix=deepcopy(self._A, memo), basis=deepcopy(basis, memo))4358N.rename(deepcopy(getattr(self, '__custom_name'), memo))4359return N43604361def __reduce__(self):4362"""4363Save the matroid for later reloading.43644365OUTPUT:43664367A tuple ``(unpickle_ternary_matroid, (version, data))``, where4368``unpickle_ternary_matroid`` is the name of a function that, when4369called with ``(version, data)``, produces a matroid isomorphic to4370``self``. ``version`` is an integer (currently 0) and ``data`` is a4371tuple ``(A, E, B, name)`` where ``A`` is the representation4372matrix, ``E`` is the groundset of the matroid, ``B`` is the currently4373displayed basis, and ``name`` is a custom name.43744375.. WARNING::43764377Users should never call this function directly.43784379EXAMPLES::43804381sage: from sage.matroids.advanced import *4382sage: M = TernaryMatroid(Matrix(GF(3), [[1, 0, 0, 1],4383....: [0, 1, 0, 1], [0, 0, 1, 1]]))4384sage: M == loads(dumps(M)) # indirect doctest4385True4386sage: M.rename("U34")4387sage: loads(dumps(M))4388U344389sage: M = TernaryMatroid(Matrix(GF(3), [[1, 0, 1], [1, 0, 1]]))4390sage: loads(dumps(M)).representation()4391[1 0 1]4392[1 0 1]4393"""4394import sage.matroids.unpickling4395version = 04396cdef list basis = [0] * self.full_rank()4397if self._representation is not None:4398A = self._representation4399gs = self._E4400basis = None4401else:4402A = self._A4403for e in self.basis():4404basis[self._prow[self._idx[e]]] = e4405rows, cols = self._current_rows_cols()4406gs = rows + cols4407data = (A, gs, basis, getattr(self, '__custom_name'))4408return sage.matroids.unpickling.unpickle_ternary_matroid, (version, data)44094410# Quaternary Matroids44114412cdef class QuaternaryMatroid(LinearMatroid):4413r"""4414Quaternary matroids.44154416A quaternary matroid is a linear matroid represented over the finite field4417with four elements. See :class:`LinearMatroid` for a definition.44184419The simplest way to create a ``QuaternaryMatroid`` is by giving only a4420matrix `A`. Then, the groundset defaults to ``range(A.ncols())``. Any4421iterable object `E` can be given as a groundset. If `E` is a list, then4422``E[i]`` will label the `i`-th column of `A`. Another possibility is to4423specify a 'reduced' matrix `B`, to create the matroid induced by4424`A = [I\ \ B]`.44254426INPUT:44274428- ``matrix`` -- (default: ``None``) a matrix whose column vectors4429represent the matroid.4430- ``reduced_matrix`` -- (default: ``None``) a matrix `B` such that4431`[I\ \ B]` represents the matroid, where `I` is an identity matrix with4432the same number of rows as `B`. Only one of ``matrix`` and4433``reduced_matrix`` should be provided.4434- ``groundset`` -- (default: ``None``) an iterable containing the element4435labels. When provided, must have the correct number of elements:4436the number of columns of ``matrix`` or the number of rows plus the4437number of columns of ``reduced_matrix``.4438- ``ring`` -- (default: ``None``) must be a copy of `\GF{4}`.4439- ``keep_initial_representation`` -- (default: ``True``) boolean. Decides4440whether or not an internal copy of the input matrix should be preserved.4441This can help to see the structure of the matroid (e.g. in the case of4442graphic matroids), and makes it easier to look at extensions. However,4443the input matrix may have redundant rows, and sometimes it is desirable4444to store only a row-reduced copy.4445- ``basis`` -- (default: ``None``) When provided, this is an ordered4446subset of ``groundset``, such that the submatrix of ``matrix`` indexed4447by ``basis`` is an identity matrix. In this case, no row reduction takes4448place in the initialization phase.44494450OUTPUT:44514452A ``QuaternaryMatroid`` instance based on the data above.44534454.. NOTE::44554456The recommended way to generate a quaternary matroid is through the4457:func:`Matroid() <sage.matroids.constructor.Matroid>` function. This4458is usually the preferred way, since it automatically chooses between4459``QuaternaryMatroid`` and other classes. For direct access to the4460``QuaternaryMatroid`` constructor, run::44614462sage: from sage.matroids.advanced import *44634464EXAMPLES::44654466sage: GF4 = GF(4, 'x')4467sage: x = GF4.gens()[0]4468sage: A = Matrix(GF4, 2, 4, [[1, 0, 1, 1], [0, 1, 1, x]])4469sage: M = Matroid(A)4470sage: M4471Quaternary matroid of rank 2 on 4 elements4472sage: sorted(M.groundset())4473[0, 1, 2, 3]4474sage: Matrix(M)4475[1 0 1 1]4476[0 1 1 x]4477sage: M = Matroid(matrix=A, groundset='abcd')4478sage: sorted(M.groundset())4479['a', 'b', 'c', 'd']4480sage: GF4p = GF(4, 'y')4481sage: y = GF4p.gens()[0]4482sage: B = Matrix(GF4p, 2, 2, [[1, 1], [1, y]])4483sage: N = Matroid(reduced_matrix=B, groundset='abcd')4484sage: M == N4485False4486"""4487def __init__(self, matrix=None, groundset=None, reduced_matrix=None, ring=None, keep_initial_representation=True, basis=None):4488"""4489See class definition for full documentation.44904491.. NOTE::44924493The extra argument ``basis``, when provided, is an ordered list of4494elements of the groundset, ordered such that they index a standard4495identity matrix within ``matrix``.44964497EXAMPLES::44984499sage: from sage.matroids.advanced import *4500sage: QuaternaryMatroid(matrix=Matrix(GF(4, 'x'),4501....: [[1, 0, 1, 1, 1], [0, 1, 1, 1, 1]])) # indirect doctest4502Quaternary matroid of rank 2 on 5 elements4503"""4504cdef QuaternaryMatrix A4505cdef long r, c4506cdef list P45074508# Setup representation; construct displayed basis4509if matrix is not None:4510A = QuaternaryMatrix(matrix.nrows(), matrix.ncols(), M=matrix, ring=ring)4511if keep_initial_representation:4512self._representation = A.copy() # Deprecated Sage matrix operation4513if basis is None:4514P = gauss_jordan_reduce(A, xrange(A.ncols()))4515A.resize(len(P)) # Not a Sage matrix operation4516self._A = A4517else:4518A = QuaternaryMatrix(reduced_matrix.nrows(), reduced_matrix.ncols(), M=reduced_matrix, ring=ring)4519P = range(A.nrows())4520self._A = A.prepend_identity() # Not a Sage matrix operation45214522# Setup groundset, BasisExchangeMatroid data4523if groundset is None:4524groundset = range(self._A.ncols())4525else:4526if len(groundset) != self._A.ncols():4527raise ValueError("size of groundset does not match size of matrix")4528if basis is None:4529bas = [groundset[i] for i in P]4530else:4531bas = basis4532BasisExchangeMatroid.__init__(self, groundset, bas)45334534# Setup index of displayed basis4535self._prow = <long* > sage_malloc((self._A.ncols()) * sizeof(long))4536for c in xrange(self._A.ncols()):4537self._prow[c] = -14538if matrix is not None:4539if basis is None:4540for r in xrange(len(P)):4541self._prow[P[r]] = r4542else:4543for r in xrange(self._A.nrows()):4544self._prow[self._idx[basis[r]]] = r4545else:4546for r from 0 <= r < self._A.nrows():4547self._prow[r] = r45484549L = []4550for i from 0 <= i < self._A.ncols():4551L.append(self._prow[i])4552self._zero = (<QuaternaryMatrix>self._A)._zero4553self._one = (<QuaternaryMatrix>self._A)._one4554self._x_zero = (<QuaternaryMatrix>self._A)._x_zero4555self._x_one = (<QuaternaryMatrix>self._A)._x_one45564557cpdef base_ring(self):4558"""4559Return the base ring of the matrix representing the matroid, in this4560case `\GF{4}`.45614562EXAMPLES::45634564sage: M = Matroid(ring=GF(4, 'y'), reduced_matrix=[[1, 0, 1],4565....: [0, 1, 1]])4566sage: M.base_ring()4567Finite Field in y of size 2^24568"""4569return (<QuaternaryMatrix>self._A).base_ring()45704571cpdef characteristic(self):4572"""4573Return the characteristic of the base ring of the matrix representing4574the matroid, in this case `2`.45754576EXAMPLES::45774578sage: M = Matroid(ring=GF(4, 'y'), reduced_matrix=[[1, 0, 1],4579....: [0, 1, 1]])4580sage: M.characteristic()458124582"""4583return 245844585cdef bint __is_exchange_pair(self, long x, long y):4586r"""4587Check if ``self.basis() - x + y`` is again a basis. Internal method.4588"""4589return (<QuaternaryMatrix>self._A).get(self._prow[x], y) # Not a Sage matrix operation45904591cdef bint __exchange(self, long x, long y):4592r"""4593Replace ``self.basis() with ``self.basis() - x + y``. Internal method, does no checks.4594"""4595cdef long p = self._prow[x]4596self._A.pivot(p, y) # Not a Sage matrix operation4597self._prow[y] = p4598BasisExchangeMatroid.__exchange(self, x, y)45994600cdef __fundamental_cocircuit(self, bitset_t C, long x):4601r"""4602Fill bitset `C` with the incidence vector of the `B`-fundamental cocircuit using ``x``. Internal method using packed elements.4603"""4604bitset_union(C, (<QuaternaryMatrix>self._A)._M0[self._prow[x]], (<QuaternaryMatrix>self._A)._M1[self._prow[x]])46054606cdef __exchange_value(self, long x, long y):4607r"""4608Return the (x, y) entry of the current representation.4609"""4610return (<QuaternaryMatrix>self._A).get(self._prow[x], y) # Not a Sage matrix operation46114612def _repr_(self):4613"""4614Return a string representation of ``self``.46154616EXAMPLES::46174618sage: M = Matroid(ring=GF(4, 'x'), matrix=[[1, 0, 1], [0, 1, 1]])4619sage: M.rename()4620sage: repr(M) # indirect doctest4621'Quaternary matroid of rank 2 on 3 elements'4622"""4623S = "Quaternary matroid of rank " + str(self.rank()) + " on " + str(self.size()) + " elements"4624return S46254626cpdef _current_rows_cols(self, B=None):4627"""4628Return the current row and column labels of a reduced matrix.46294630INPUT:46314632- ``B`` -- (default: ``None``) If provided, first find a basis having4633maximal intersection with ``B``.46344635OUTPUT:46364637- ``R`` -- A list of row indices; corresponds to the currently used4638internal basis4639- ``C`` -- A list of column indices; corresponds to the complement of4640the current internal basis46414642EXAMPLES::46434644sage: M = matroids.named_matroids.Q10()4645sage: A = M._reduced_representation('efghi')4646sage: R, C = M._current_rows_cols()4647sage: (sorted(R), sorted(C))4648(['e', 'f', 'g', 'h', 'i'], ['a', 'b', 'c', 'd', 'j'])4649sage: R, C = M._current_rows_cols(B='abcde')4650sage: (sorted(R), sorted(C))4651(['a', 'b', 'c', 'd', 'e'], ['f', 'g', 'h', 'i', 'j'])46524653"""4654if B is not None:4655self._move_current_basis(B, set())4656basis = self.basis()4657rows = [0] * self.full_rank()4658cols = [0] * self.full_corank()4659c = 04660for e in self._E:4661if e in basis:4662rows[self._prow[self._idx[e]]] = e4663else:4664cols[c] = e4665c += 14666return rows, cols46674668cpdef LeanMatrix _basic_representation(self, B=None):4669"""4670Return a basic matrix representation of the matroid.46714672INPUT:46734674- ``B`` -- (default: ``None``) a set of elements of the groundset.46754676OUTPUT:46774678A matrix `M` representing the matroid, where `M[B'] = I` for a basis4679`B'` that maximally intersects the given set `B`. If not provided, the4680current basis used internally is chosen for `B'`. For a stable4681representation, use ``self.representation()``.46824683.. NOTE::46844685The method self.groundset_list() gives the labelling of the4686columns by the elements of the matroid. The matrix returned4687is a LeanMatrix subclass, which is intended for internal use only.4688Use the ``representation()`` method to get a Sage matrix.46894690EXAMPLES::46914692sage: M = matroids.named_matroids.Q10()4693sage: M._basic_representation()46945 x 10 QuaternaryMatrix4695[100001x00y]4696[01000y1x00]4697[001000y1x0]4698[0001000y1x]4699[00001x00y1]4700sage: matrix(M._basic_representation('efghi'))4701[ 1 0 x + 1 1 0 1 0 0 0 1]4702[ x x + 1 0 0 0 0 0 1 0 1]4703[ 0 0 x x + 1 0 0 1 0 0 1]4704[ 1 x 0 1 0 0 0 0 1 1]4705[ 1 1 1 1 1 0 0 0 0 0]4706"""4707if B is not None:4708self._move_current_basis(B, set())4709return self._A.copy() # Deprecated Sage matrix operation47104711cpdef LeanMatrix _reduced_representation(self, B=None):4712"""4713Return a reduced representation of the matroid, i.e. a matrix `R` such4714that `[I\ \ R]` represents the matroid.47154716INPUT:47174718- ``B`` -- (default: ``None``) a set of elements of the groundset.47194720OUTPUT:47214722A matrix `R` forming a reduced representation of the matroid, with4723rows labeled by a basis `B'` that maximally intersects the given set4724`B`. If not provided, the current basis used internally labels the4725rows.47264727.. NOTE::47284729The matrix returned is a LeanMatrix subclass, which is intended4730for internal use only. Use the ``representation()`` method to get4731a Sage matrix.47324733EXAMPLES::47344735sage: M = matroids.named_matroids.Q10()4736sage: M._reduced_representation()47375 x 5 QuaternaryMatrix4738[1x00y]4739[y1x00]4740[0y1x0]4741[00y1x]4742[x00y1]4743sage: matrix(M._reduced_representation('efghi'))4744[ 1 0 x + 1 1 1]4745[ x x + 1 0 0 1]4746[ 0 0 x x + 1 1]4747[ 1 x 0 1 1]4748[ 1 1 1 1 0]4749"""4750if B is not None:4751self._move_current_basis(B, set())4752rows, cols = self._current_rows_cols()4753return self._A.matrix_from_rows_and_columns(range(self.full_rank()), [self._idx[e] for e in cols])47544755cpdef _make_invariant(self):4756"""4757Create an invariant.47584759Internal method; see ``_invariant`` for full documentation.47604761EXAMPLES::47624763sage: M = matroids.named_matroids.Q10()4764sage: M._invariant() # indirect doctest4765(0, 0, 5, 5, 20, 10, 25)4766"""4767cdef QuaternaryMatrix Q, QT4768cdef long i, j, d, r47694770if self._q_invariant is not None:4771return4772Q = (<QuaternaryMatrix>self._A).copy() # Deprecated Sage matrix operation4773r = Q.nrows()4774d = 04775i = 04776while i < r - d:4777for j in xrange(i, r - d):4778if Q.row_inner_product(j, j) != 0: # Not a Sage matrix operation4779if j > i:4780Q.swap_rows_c(i, j)4781break4782y = Q.row_inner_product(i, j) # Not a Sage matrix operation4783if y != 0:4784if j > i:4785Q.add_multiple_of_row_c(i, j, Q._x_zero * y, 0)4786break4787x = Q.row_inner_product(i, i) # Not a Sage matrix operation4788if x == 0:4789d += 14790Q.swap_rows_c(i, r - d)4791else:4792for j in xrange(i + 1, r - d):4793y = Q.row_inner_product(j, i) # Not a Sage matrix operation4794if y == 0:4795continue4796Q.add_multiple_of_row_c(j, i, y, 0)4797i += 147984799QT = Q.transpose()4800QT.conjugate() # Not a Sage matrix operation4801self._q_projection = QT._matrix_times_matrix_((Q._matrix_times_matrix_(QT))._matrix_times_matrix_(Q))4802F = frozenset()4803for i in xrange(r - d, r):4804F = F | frozenset(Q.nonzero_positions_in_row(i))4805Fa = frozenset([j for j in xrange(len(self)) if self._q_projection.get(j, j) == 0]) - F # Not a Sage matrix operation4806Fb = frozenset([j for j in xrange(len(self)) if self._q_projection.get(j, j) == 1]) - F # Not a Sage matrix operation48074808P = [Fa, Fb]4809p = []4810for a in xrange(2):4811for b in xrange(a + 1):4812x = 04813for i in P[a]:4814for j in P[b]:4815if self._q_projection.get(i, j) != 0: # Not a Sage matrix operation4816x += 14817p.append(x)48184819self._q_partition = tuple([F, Fa, Fb])4820self._q_invariant = tuple([d, len(F), len(Fa), len(Fb), p[0], p[1], p[2]])48214822cpdef _invariant(self):4823r"""4824Return a matroid invariant.48254826See [Pen12]_ for more information.48274828OUTPUT:48294830A tuple ``(d, Lm, L0, Lp, p0, p1, p2)``, with the following4831interpretation:48324833- ``d`` is the bicycle dimension.4834- ``(Lm, L0, Lp)`` is the triple of lengths of the principal4835tripartition.4836- ``(p0, p1, p2)`` counts of edges in a characteristic graph of the4837matroid, whose vertices are the union of ``F_-`` and ``F_0`` from4838the principal tripartition.48394840EXAMPLES::48414842sage: M = matroids.named_matroids.Q10()4843sage: M._invariant()4844(0, 0, 5, 5, 20, 10, 25)48454846"""4847if self._q_invariant is None:4848self._make_invariant()4849return self._q_invariant48504851cpdef bicycle_dimension(self):4852"""4853Return the bicycle dimension of the quaternary matroid.48544855The bicycle dimension of a linear subspace `V` is4856`\dim(V\cap V^\perp)`. We use the inner product4857`< v, w >=v_1 w_1^* + ... + v_n w_n^*`, where `w_i^*` is obtained from4858`w_i` by applying the unique nontrivial field automorphism of4859`\GF{4}`.48604861The bicycle dimension of a matroid equals the bicycle dimension of its4862rowspace, and is a matroid invariant. See [Pen12]_.48634864OUTPUT:48654866Integer.48674868EXAMPLES::48694870sage: M = matroids.named_matroids.Q10()4871sage: M.bicycle_dimension()487204873"""4874if self._q_invariant is None:4875self._make_invariant()4876return self._q_invariant[0]48774878cpdef _principal_tripartition(self):4879r"""4880Return the principal tripartition of the quaternary matroid.48814882The principal tripartition is a partition `(F_{-1}, F_0, F_{1})` of4883the ground set. A defining property is the following. It is4884straightforward that if the bicycle dimension of a matroid `M` is `k`,4885then the bicycle dimension of `M\setminus e' is one of `k-1, k, k + 1`4886for each element `e` of `M`. Then if `F_i` denotes the set of elements4887such that the bicycle dimension of `M\setminus e` is `k + i`, we4888obtain the principal tripartition `(F_{-1}, F_0, F_{1})` of `M`.4889See [Pen12]_, [GR01]_.48904891OUTPUT:48924893``(F_{-1}, F_0, F_{1})``, the principal tripartition of the matroid.48944895EXAMPLES::48964897sage: M = matroids.named_matroids.Q10()\'a'4898sage: for F in M._principal_tripartition(): print sorted(F)4899['b', 'c', 'd', 'e', 'h', 'i']4900['f', 'g', 'j']4901[]4902sage: M.bicycle_dimension()490314904sage: for i in [-1, 0, 1]: print sorted([e for e in M.groundset() if (M\e).bicycle_dimension() == 1 + i])4905['b', 'c', 'd', 'e', 'h', 'i']4906['f', 'g', 'j']4907[]4908"""4909if self._q_invariant is None:4910self._make_invariant()4911P = self._q_partition4912return frozenset([self._E[e] for e in P[0]]), frozenset([self._E[e] for e in P[1]]), frozenset([self._E[e] for e in P[2]])49134914cpdef _fast_isom_test(self, other):4915r"""4916Run a quick test to see if two quaternary matroids are isomorphic.49174918The test is based on comparing the invariants returned by4919self._invariant().49204921INPUT:49224923- ``other`` -- a quaternary matroid.49244925OUTPUT:49264927- ``True``, if ``self`` is isomorphic to ``other``;4928- ``False``, if ``self`` is not isomorphic to ``other``;4929- ``None``, if this test is inconclusive49304931EXAMPLES::49324933sage: M = matroids.named_matroids.Q10()\'a'4934sage: N = matroids.named_matroids.Q10()\'b'4935sage: M._fast_isom_test(N) is None4936True4937"""4938if self._invariant() != other._invariant():4939return False49404941# minors, dual49424943cpdef _minor(self, contractions, deletions):4944r"""4945Return a minor.49464947INPUT:49484949- ``contractions`` -- An object with Python's ``frozenset`` interface4950containing a subset of ``self.groundset()``.4951- ``deletions`` -- An object with Python's ``frozenset`` interface4952containing a subset of ``self.groundset()``.49534954.. NOTE::49554956This method does NOT do any checks. Besides the assumptions above,4957we assume the following:49584959- ``contractions`` is independent4960- ``deletions`` is coindependent4961- ``contractions`` and ``deletions`` are disjoint.49624963OUTPUT:49644965A matroid.49664967EXAMPLES::49684969sage: M = matroids.named_matroids.Q10()4970sage: N = M._minor(contractions=set(['a']), deletions=set([]))4971sage: N._minor(contractions=set([]), deletions=set(['b', 'c']))4972Quaternary matroid of rank 4 on 7 elements4973"""4974self._move_current_basis(contractions, deletions)4975bas = list(self.basis() - contractions)4976R = [self._prow[self._idx[b]] for b in bas]4977C = [c for c in range(len(self._E)) if self._E[c] not in deletions | contractions]4978return QuaternaryMatroid(matrix=(<QuaternaryMatrix>self._A).matrix_from_rows_and_columns(R, C), groundset=[self._E[c] for c in C], basis=bas)49794980cpdef is_valid(self):4981r"""4982Test if the data obey the matroid axioms.49834984Since this is a linear matroid over the field `\GF{4}`, this is always4985the case.49864987OUTPUT:49884989``True``.49904991EXAMPLES::49924993sage: M = Matroid(Matrix(GF(4, 'x'), [[]]))4994sage: M.is_valid()4995True4996"""4997return True49984999def __copy__(self):5000"""5001Create a shallow copy.50025003EXAMPLES::50045005sage: M = Matroid(Matrix(GF(4, 'x'), [[1, 0, 0, 1, 1],5006....: [0, 1, 0, 1, 2], [0, 0, 1, 1, 3]]))5007sage: N = copy(M) # indirect doctest5008sage: M == N5009True5010"""5011cdef QuaternaryMatroid N5012cdef list basis5013if self._representation is not None:5014N = QuaternaryMatroid(groundset=self._E, matrix=self._representation, keep_initial_representation=True)5015else:5016basis = [0] * self.full_rank()5017for e in self.basis():5018basis[self._prow[self._idx[e]]] = e5019N = QuaternaryMatroid(groundset=self._E, matrix=self._A, basis=basis)5020N.rename(getattr(self, '__custom_name'))5021return N50225023def __deepcopy__(self, memo):5024"""5025Create a deep copy.50265027EXAMPLES::50285029sage: M = Matroid(Matrix(GF(4, 'x'), [[1, 0, 0, 1, 1],5030....: [0, 1, 0, 1, 2], [0, 0, 1, 1, -1]]))5031sage: N = deepcopy(M) # indirect doctest5032sage: M == N5033True5034"""5035from copy import deepcopy5036cdef QuaternaryMatroid N5037cdef list basis5038if self._representation is not None:5039N = QuaternaryMatroid(groundset=deepcopy(self._E, memo), matrix=deepcopy(self._representation, memo), keep_initial_representation=True)5040else:5041basis = [0] * self.full_rank()5042for e in self.basis():5043basis[self._prow[self._idx[e]]] = e5044N = QuaternaryMatroid(groundset=deepcopy(self._E, memo), matrix=deepcopy(self._A, memo), basis=deepcopy(basis, memo))5045N.rename(deepcopy(getattr(self, '__custom_name'), memo))5046return N50475048def __reduce__(self):5049"""5050Save the matroid for later reloading.50515052OUTPUT:50535054A tuple ``(unpickle_quaternary_matroid, (version, data))``, where5055``unpickle_quaternary_matroid`` is the name of a function that,5056when called with ``(version, data)``, produces a matroid isomorphic to5057``self``. ``version`` is an integer (currently 0) and ``data`` is a5058tuple ``(A, E, B, name)`` where ``A`` is the representation5059matrix, ``E`` is the groundset of the matroid, ``B`` is the currently5060displayed basis, and ``name`` is a custom name.50615062.. WARNING::50635064Users should never call this function directly.50655066EXAMPLES::50675068sage: M = Matroid(Matrix(GF(4, 'x'), [[1, 0, 0, 1], [0, 1, 0, 1],5069....: [0, 0, 1, 1]]))5070sage: M == loads(dumps(M)) # indirect doctest5071True5072sage: M.rename("U34")5073sage: loads(dumps(M))5074U345075"""5076import sage.matroids.unpickling5077version = 05078cdef list basis = [0] * self.full_rank()5079if self._representation is not None:5080A = self._representation5081gs = self._E5082basis = None5083else:5084A = self._A5085for e in self.basis():5086basis[self._prow[self._idx[e]]] = e5087rows, cols = self._current_rows_cols()5088gs = rows + cols5089data = (A, gs, basis, getattr(self, '__custom_name'))5090return sage.matroids.unpickling.unpickle_quaternary_matroid, (version, data)50915092# Regular Matroids50935094cdef class RegularMatroid(LinearMatroid):5095r"""5096Regular matroids.50975098A regular matroid is a linear matroid represented over the integers by a5099totally unimodular matrix.51005101The simplest way to create a ``RegularMatroid`` is by giving only a matrix5102`A`. Then, the groundset defaults to ``range(A.ncols())``.5103Any iterable object `E` can be given as a groundset. If `E` is a list, then ``E[i]`` will label the `i`-th column of `A`.5104Another possibility is to specify a 'reduced' matrix `B`, to create the matroid induced by `A = [ I B ]`.51055106INPUT:51075108- ``matrix`` -- (default: ``None``) a matrix whose column vectors5109represent the matroid.5110- ``reduced_matrix`` -- (default: ``None``) a matrix `B` such that5111`[I\ \ B]` represents the matroid, where `I` is an identity matrix with5112the same number of rows as `B`. Only one of ``matrix`` and5113``reduced_matrix`` should be provided.5114- ``groundset`` -- (default: ``None``) an iterable containing the element5115labels. When provided, must have the correct number of elements: the5116number of columns of ``matrix`` or the number of rows plus the number of5117columns of ``reduced_matrix``.5118- ``ring`` -- (default: ``None``) ignored.5119- ``keep_initial_representation`` -- (default: ``True``) boolean. Decides5120whether or not an internal copy of the input matrix should be preserved.5121This can help to see the structure of the matroid (e.g. in the case of5122graphic matroids), and makes it easier to look at extensions. However,5123the input matrix may have redundant rows, and sometimes it is desirable5124to store only a row-reduced copy.5125- ``basis`` -- (default: ``None``) when provided, this is an ordered5126subset of ``groundset``, such that the submatrix of ``matrix`` indexed5127by ``basis`` is an identity matrix. In this case, no row reduction takes5128place in the initialization phase.51295130OUTPUT:51315132A ``RegularMatroid`` instance based on the data above.51335134.. NOTE::51355136The recommended way to generate a regular matroid is through the5137:func:`Matroid() <sage.matroids.constructor.Matroid>` function. This5138is usually the preferred way, since it automatically chooses between5139``RegularMatroid`` and other classes. Moreover, it will test whether5140the input actually yields a regular matroid, unlike this class.5141For direct access to the ``RegularMatroid`` constructor, run::51425143sage: from sage.matroids.advanced import *51445145.. WARNING::51465147No checks are performed to ensure the input data form an actual regular5148matroid! If not, the behavior is unpredictable, and the internal5149representation can get corrupted. If in doubt, run5150:meth:`self.is_valid() <RegularMatroid.is_valid>` to ensure the data5151are as desired.51525153EXAMPLES::51545155sage: A = Matrix(ZZ, 2, 4, [[1, 0, 1, 1], [0, 1, 1, 1]])5156sage: M = Matroid(A, regular=True)5157sage: M5158Regular matroid of rank 2 on 4 elements with 5 bases5159sage: sorted(M.groundset())5160[0, 1, 2, 3]5161sage: Matrix(M)5162[1 0 1 1]5163[0 1 1 1]5164sage: M = Matroid(matrix=A, groundset='abcd', regular=True)5165sage: sorted(M.groundset())5166['a', 'b', 'c', 'd']5167"""5168def __init__(self, matrix=None, groundset=None, reduced_matrix=None, ring=None, keep_initial_representation=True):5169"""5170See class definition for full documentation.51715172.. NOTE::51735174The extra argument ``basis``, when provided, is an ordered list of5175elements of the groundset, ordered such that they index a standard5176identity matrix within ``matrix``.51775178EXAMPLES::51795180sage: from sage.matroids.advanced import *5181sage: RegularMatroid(matrix=Matrix(ZZ, [[1, 0, 1, 1, 1],5182....: [0, 1, 1, 1, 1]])) # indirect doctest5183Regular matroid of rank 2 on 5 elements with 7 bases5184"""5185LinearMatroid.__init__(self, matrix, groundset, reduced_matrix, ring=ZZ, keep_initial_representation=keep_initial_representation)51865187cdef list _setup_internal_representation(self, matrix, reduced_matrix, ring, keep_initial_representation):5188"""5189Setup the internal representation matrix ``self._A`` and the array of5190row- and column indices ``self._prow``.51915192Return the displayed basis.5193"""5194cdef IntegerMatrix A5195cdef long r, c5196cdef list P5197if matrix is not None:5198reduced = False5199if not isinstance(matrix, IntegerMatrix):5200A = IntegerMatrix(matrix.nrows(), matrix.ncols(), M=matrix)5201else:5202A = (<IntegerMatrix>matrix).copy() # Deprecated Sage matrix operation5203if keep_initial_representation:5204self._representation = A.copy() # Deprecated Sage matrix operation5205P = gauss_jordan_reduce(A, xrange(A.ncols()))5206self._A = A.matrix_from_rows_and_columns(range(len(P)), [c for c in xrange(matrix.ncols()) if not c in P])5207else:5208reduced = True5209if not isinstance(reduced_matrix, IntegerMatrix):5210self._A = IntegerMatrix(reduced_matrix.nrows(), reduced_matrix.ncols(), M=reduced_matrix)5211else:5212self._A = (<IntegerMatrix>reduced_matrix).copy() # Deprecated Sage matrix operation5213P = range(self._A.nrows())5214self._prow = <long* > sage_malloc((self._A.nrows() + self._A.ncols()) * sizeof(long))5215if matrix is not None:5216for r in xrange(len(P)):5217self._prow[P[r]] = r5218r = 05219for c in xrange(A.ncols()):5220if c not in P:5221self._prow[c] = r5222r += 15223else:5224for r from 0 <= r < self._A.nrows():5225self._prow[r] = r5226for r from 0 <= r < self._A.ncols():5227self._prow[self._A.nrows() + r] = r5228return P52295230cpdef base_ring(self):5231"""5232Return the base ring of the matrix representing the matroid, in this5233case `\ZZ`.52345235EXAMPLES::52365237sage: M = matroids.named_matroids.R10()5238sage: M.base_ring()5239Integer Ring5240"""5241return ZZ52425243cpdef characteristic(self):5244"""5245Return the characteristic of the base ring of the matrix representing5246the matroid, in this case `0`.52475248EXAMPLES::52495250sage: M = matroids.named_matroids.R10()5251sage: M.characteristic()525205253"""5254return 052555256cdef bint __is_exchange_pair(self, long x, long y):5257r"""5258Check if ``self.basis() - x + y`` is again a basis. Internal method.5259"""5260return (<IntegerMatrix>self._A).get(self._prow[x], self._prow[y]) # Not a Sage matrix operation52615262cdef bint __exchange(self, long x, long y):5263"""5264Put element indexed by ``x`` into basis, taking out element ``y``. Assumptions are that this is a valid basis exchange.52655266.. NOTE::52675268Safe for noncommutative rings.5269"""5270cdef long px, py, r5271cdef int a, piv, pivi5272px = self._prow[x]5273py = self._prow[y]5274piv = (<IntegerMatrix>self._A).get(px, py) # Not a Sage matrix operation5275pivi = piv # NOTE: 1 and -1 are their own inverses.5276(<IntegerMatrix>self._A).rescale_row_c(px, pivi, 0)5277(<IntegerMatrix>self._A).set(px, py, pivi + 1) # pivoting without column scaling. Add extra so column does not need adjusting # Not a Sage matrix operation5278for r in xrange(self._A.nrows()): # if A and A' are the matrices before and after pivoting, then5279a = (<IntegerMatrix>self._A).get(r, py) # ker[I A] equals ker[I A'] except for the labelling of the columns # Not a Sage matrix operation5280if a and r != px:5281(<IntegerMatrix>self._A).add_multiple_of_row_c(r, px, -a, 0)5282(<IntegerMatrix>self._A).set(px, py, pivi) # Not a Sage matrix operation5283self._prow[y] = px5284self._prow[x] = py5285BasisExchangeMatroid.__exchange(self, x, y)52865287cdef __exchange_value(self, long x, long y):5288r"""5289Return the (x, y) entry of the current representation.52905291.. NOTE::52925293This uses get_unsafe(), which returns a Sage ``Integer`` instance,5294rather than the ``int`` returned by ``get``. The5295advantage is that cross ratio tests will return rational numbers5296rather than unwarranted zeroes.5297"""5298return (<IntegerMatrix>self._A).get_unsafe(self._prow[x], self._prow[y])52995300def _repr_(self):5301"""5302Return a string representation of ``self``.53035304EXAMPLES::53055306sage: M = matroids.named_matroids.R10()5307sage: M.rename()5308sage: repr(M) # indirect doctest5309'Regular matroid of rank 5 on 10 elements with 162 bases'5310"""5311S = "Regular matroid of rank " + str(self.rank()) + " on " + str(self.size()) + " elements with " + str(self.bases_count()) + " bases"5312return S53135314cpdef bases_count(self):5315"""5316Count the number of bases.53175318EXAMPLES::53195320sage: M = matroids.CompleteGraphic(5)5321sage: M.bases_count()532212553235324ALGORITHM:53255326Since the matroid is regular, we use Kirchhoff's Matrix-Tree Theorem.5327See also :wikipedia:`Kirchhoff's_theorem`.5328"""5329if self._bases_count is None:5330R = self._basic_representation()._matrix_()5331self._bases_count = (R * R.transpose()).det()5332return self._bases_count53335334cpdef _projection(self):5335"""5336Return the projection matrix onto the row space.53375338INPUT:53395340- Nothing53415342OUTPUT:53435344A matrix `P`, defined as follows. If `A` is a representation matrix5345of the matroid, then `Q = A^T (A A^T)^{-1} A`. Finally, `P` is equal5346to `Q` multiplied by the number of bases of the matroid.53475348The matrix `P` is independent of the choice of `A`, except for column5349scaling. It has the property that `xP` is the orthogonal projection of5350the row vector `x` onto the row space of `A`. For regular matroids,5351there is an extended Matrix Tree theorem that derives the fraction of5352bases containing a subset by computing the determinant of the5353principal submatrix corresponding to that subset. See [Lyons]_ .5354In particular, the entries of `P` are integers.53555356EXAMPLES::53575358sage: from sage.matroids.advanced import *5359sage: M = RegularMatroid(reduced_matrix=Matrix([[-1, 0, 1],5360....: [-1, 1, 0], [0, 1, -1]]))5361sage: M._projection()5362[ 8 -4 4 -4 0 4]5363[-4 8 -4 -4 4 0]5364[ 4 -4 8 0 4 -4]5365[-4 -4 0 8 -4 -4]5366[ 0 4 4 -4 8 -4]5367[ 4 0 -4 -4 -4 8]5368"""5369if self._r_projection is None:5370R = self._basic_representation()._matrix_()5371X = (R * R.transpose())5372self._bases_count = X.det()5373self._r_projection = self._bases_count * R.transpose() * X.inverse() * R5374return self._r_projection53755376cpdef _invariant(self):5377"""5378Compute a regular matroid invariant.53795380OUTPUT:53815382The hash of a list of pairs `(w, A[w])` and `(w, B[w])`, where `A[w]`5383counts the number of `i` such that `|P[i, i]|=w` (where `P` is the5384projection matrix from ``self._projection()``), and `B[w]` counts the5385number of pairs `(i, j)` such that `|P[i, j]|=w`.53865387EXAMPLES::53885389sage: M = matroids.named_matroids.R10()5390sage: N = matroids.named_matroids.R10().dual()5391sage: O = matroids.named_matroids.R12()5392sage: M._invariant() == N._invariant()5393True5394sage: M._invariant() == O._invariant()5395False5396"""5397# TODO: this currently uses Sage matrices internally. Perhaps dependence on those can be eliminated for further speed gains.5398if self._r_invariant is not None:5399return self._r_invariant5400cdef Matrix P5401P = self._projection()5402A = {}5403B = {}5404for i in xrange(P.nrows()):5405w = P.get_unsafe(i, i)5406if w != 0:5407if w in A:5408A[w] += 15409else:5410A[w] = 15411for j in xrange(i):5412w = abs(P.get_unsafe(i, j))5413if w != 0:5414if w in B:5415B[w] += 15416else:5417B[w] = 15418self._r_invariant = hash(tuple([tuple([(w, A[w]) for w in sorted(A)]), tuple([(w, B[w]) for w in sorted(B)])]))5419return self._r_invariant54205421cpdef _hypergraph(self):5422"""5423Create a bipartite digraph and a vertex partition.54245425INPUT:54265427- Nothing.54285429OUTPUT:54305431- ``PV`` -- A partition of the vertices of ``G``.5432- ``tups`` -- A list of pairs ``(x, y)``, where ``x`` denotes the5433color class of a part and ``y`` the number of elements in that part.5434- ``G`` -- a graph.54355436All are derived from the entries of the projection matrix `P`. The5437partition ``PV`` groups vertices of the form `i` by the value of5438`P[i, i]`. Whenever `P[i, j]` is nonzero, there are edges `i - (i, j)`5439and `j - (i, j)`. Finally, the vertices `(i, j)` are grouped by value5440of `P[i, j]`.54415442EXAMPLES::54435444sage: M = matroids.named_matroids.R10()5445sage: PV, tups, G = M._hypergraph()5446sage: G5447Digraph on 55 vertices5448"""5449# NEW VERSION, USES SAGE'S GRAPH ISOMORPHISM5450from sage.graphs.all import Graph, DiGraph5451if self._r_hypergraph is not None:5452return (self._hypergraph_vertex_partition, self._hypergraph_tuples, self._r_hypergraph)5453cdef Matrix P = self._projection()5454A = {}5455B = {}5456V = []5457E = []5458for i in xrange(P.nrows()):5459e = self._E[i]5460w = P.get_unsafe(i, i)5461if w != 0:5462if w in A:5463A[w].append(e)5464else:5465A[w] = [e]5466V.append(e)5467for j in xrange(i):5468f = self._E[j]5469w = abs(P.get_unsafe(i, j))5470if w != 0:5471if w in B:5472B[w].append(frozenset([e, f]))5473else:5474B[w] = [frozenset([e, f])]5475E.append(frozenset([e, f]))5476self._hypergraph_vertex_partition = [[str(x) for x in A[w]] for w in sorted(A)] + [['**' + str(x) for x in B[w]] for w in sorted(B)]5477self._hypergraph_tuples = [(w, len(A[w])) for w in sorted(A)] + [(w, len(B[w])) for w in sorted(B)]5478G = DiGraph()5479G.add_vertices([str(x) for x in V] + ['**' + str(x) for x in E])5480# Note that Sage's Graph object attempts a sort on calling G.vertices(), which means vertices have to be well-behaved.5481for X in E:5482Y = list(X)5483G.add_edge(str(Y[0]), '**' + str(X))5484G.add_edge(str(Y[1]), '**' + str(X))54855486self._r_hypergraph = G5487return self._hypergraph_vertex_partition, self._hypergraph_tuples, self._r_hypergraph5488# REMNANT OF THE OLD CODE THAT WAS NOT YET TRANSLATED TO SAGE'S GRAPH ISOMORPHISM. POTENTIAL SPEEDUP?5489# C = []5490# if len(A) < 5:5491# for i in xrange(P.nrows()):5492# for j in xrange(i):5493# if P.get_unsafe(i, j) == 0:5494# continue5495# for k in xrange(j):5496# w = P.get_unsafe(i, j)*P.get_unsafe(j, k)*P.get_unsafe(k, i)5497# if w < 0:5498# C.append(frozenset([self._E[i], self._E[j], self._E[k]]))5499# self._r_hypergraph.add_color_class(C)5500# self._r_hypergraph = self._r_hypergraph.max_refined()5501# return self._r_hypergraph55025503cpdef _is_isomorphic(self, other):5504"""5505Test if ``self`` is isomorphic to ``other``.55065507Internal version that performs no checks on input.55085509INPUT:55105511- ``other`` -- A matroid.55125513OUTPUT:55145515Boolean.55165517EXAMPLES::55185519sage: M1 = matroids.Wheel(3)5520sage: M2 = matroids.CompleteGraphic(4)5521sage: M1._is_isomorphic(M2)5522True55235524sage: M1 = matroids.Wheel(3)5525sage: M2 = matroids.named_matroids.Fano()5526sage: M1._is_isomorphic(M2)5527False5528sage: M1._is_isomorphic(M2.delete('a'))5529True5530"""5531if type(other) == RegularMatroid:5532return self.is_field_isomorphic(other)5533else:5534return LinearMatroid._is_isomorphic(self, other)55355536cpdef _fast_isom_test(self, other):5537r"""5538Run a quick test to see if two regular matroids are isomorphic.55395540The test is based on:55415542* A comparison of the number of bases, which may be computed5543efficiently through the matrix-tree lemma (see self.bases_count()).5544* A comparison of the orthogonal projection matrices of both matroids5545(see self._invariant()).5546* A isomorphism test which makes use of a hypergraph derived from the5547orthogonal projection matrix (see self._hypertest()).55485549INPUT:55505551- ``other`` -- a regular matroid.55525553OUTPUT:55545555- ``True``, if ``self`` is isomorphic to ``other``;5556- ``False``, if ``self`` is not isomorphic to ``other``;55575558EXAMPLES::55595560sage: M = matroids.named_matroids.R10()\'a'5561sage: N = matroids.named_matroids.R10()\'b'5562sage: M._fast_isom_test(N)5563True5564"""5565if self.bases_count() != other.bases_count():5566return False5567if self._invariant() != other._invariant():5568return False5569if self.size() > 8: # TODO: Optimize the cutoff. _hypertest() is slow for small matroids, and can be fast for larger ones.5570return self._hypertest(other)55715572cdef _hypertest(self, other):5573"""5574Test if the hypergraphs associated with ``self`` and ``other`` are5575isomorphic.55765577INPUT:55785579- ``other`` -- A ``RegularMatroid`` instance.55805581OUTPUT:55825583- ``True`` if the hypergraphs are isomorphic; ``False`` otherwise.5584"""5585from sage.groups.perm_gps.partn_ref.refinement_graphs import isomorphic5586HS = self._hypergraph()5587HO = other._hypergraph()5588VO = []5589for X in HO[0]:5590VO.extend(X)5591return isomorphic(HS[2], HO[2], HS[0], VO, 1, 1) is not None55925593cpdef has_line_minor(self, k, hyperlines=None):5594"""5595Test if the matroid has a `U_{2, k}`-minor.55965597The matroid `U_{2, k}` is a matroid on `k` elements in which every5598subset of at most 2 elements is independent, and every subset of more5599than two elements is dependent.56005601The optional argument ``hyperlines`` restricts the search space: this5602method returns ``False`` if `si(M/F)` is isomorphic to `U_{2, l}` with5603`l \geq k` for some `F` in ``hyperlines``, and ``True`` otherwise.56045605INPUT:56065607- ``k`` -- the length of the line minor5608- ``hyperlines`` -- (default: ``None``) a set of flats of codimension56092. Defaults to the set of all flats of codimension 2.56105611OUTPUT:56125613Boolean.56145615.. SEEALSO::56165617:meth:`Matroid.has_minor() <sage.matroids.matroid.Matroid.has_minor>`56185619EXAMPLES::56205621sage: M = matroids.named_matroids.R10()5622sage: M.has_line_minor(4)5623False5624sage: M.has_line_minor(3)5625True5626sage: M.has_line_minor(k=3, hyperlines=[['a', 'b', 'c'],5627....: ['a', 'b', 'd' ]])5628True56295630"""5631if k > 3:5632return False5633return Matroid.has_line_minor(self, k, hyperlines)56345635cpdef _linear_extension_chains(self, F, fundamentals=None):5636r"""5637Create a list of chains that determine single-element extensions of5638this linear matroid representation.56395640.. WARNING::56415642Intended for internal use; does no input checking.56435644INPUT:56455646- ``F`` -- an independent set of elements.5647- ``fundamentals`` -- (default: ``None``) a set elements of the base5648ring.56495650OUTPUT:56515652A list of chains, so each single-element regular extension of this5653linear matroid, with support contained in ``F``, is5654given by one of these chains.56555656EXAMPLES::56575658sage: M = matroids.Wheel(3)5659sage: len(M._linear_extension_chains(F=set([0, 1, 2])))566075661sage: M._linear_extension_chains(F=set())5662[{}]5663sage: M._linear_extension_chains(F=set([1]))5664[{}, {1: 1}]5665sage: len(M._linear_extension_chains(F=set([0, 1])))566645667"""5668if fundamentals is None:5669fundamentals = set([1])5670return LinearMatroid._linear_extension_chains(self, F, fundamentals)56715672cpdef is_graphic(self):5673"""5674Test if the regular matroid is graphic.56755676A matroid is *graphic* if there exists a graph whose edge set equals5677the groundset of the matroid, such that a subset of elements of the5678matroid is independent if and only if the corresponding subgraph is5679acyclic.56805681OUTPUT:56825683Boolean.56845685EXAMPLES::56865687sage: M = matroids.named_matroids.R10()5688sage: M.is_graphic()5689False5690sage: M = matroids.CompleteGraphic(5)5691sage: M.is_graphic()5692True5693sage: M.dual().is_graphic()5694False56955696ALGORITHM:56975698In a recent paper, Geelen and Gerards [GG12]_ reduced the problem to5699testing if a system of linear equations has a solution. While not the5700fastest method, and not necessarily constructive (in the presence of57012-separations especially), it is easy to implement.5702"""5703return BinaryMatroid(reduced_matrix=self._reduced_representation()).is_graphic()57045705cpdef is_valid(self):5706r"""5707Test if the data obey the matroid axioms.57085709Since this is a regular matroid, this function tests if the5710representation matrix is *totally unimodular*, i.e. if all square5711submatrices have determinant in `\{-1, 0, 1\}`.57125713OUTPUT:57145715Boolean.57165717EXAMPLES::57185719sage: M = Matroid(Matrix(ZZ, [[1, 0, 0, 1, 1, 0, 1],5720....: [0, 1, 0, 1, 0, 1, 1],5721....: [0, 0, 1, 0, 1, 1, 1]]),5722....: regular=True, check=False)5723sage: M.is_valid()5724False5725sage: M = Matroid(graphs.PetersenGraph())5726sage: M.is_valid()5727True5728"""5729M = LinearMatroid(ring=QQ, reduced_matrix=self.representation(self.basis(), True, False))5730CR = M.cross_ratios()5731return CR.issubset(set([1]))57325733# Copying, loading, saving57345735def __copy__(self):5736"""5737Create a shallow copy.57385739EXAMPLES::57405741sage: M = matroids.named_matroids.R10()5742sage: N = copy(M) # indirect doctest5743sage: M == N5744True5745"""5746cdef RegularMatroid N5747if self._representation is not None:5748N = RegularMatroid(groundset=self._E, matrix=self._representation, keep_initial_representation=True)5749else:5750rows, cols = self._current_rows_cols()5751N = RegularMatroid(groundset=rows + cols, reduced_matrix=self._A)5752N.rename(getattr(self, '__custom_name'))5753return N57545755def __deepcopy__(self, memo):5756"""5757Create a deep copy.57585759EXAMPLES::57605761sage: M = matroids.named_matroids.R10()5762sage: N = deepcopy(M) # indirect doctest5763sage: M == N5764True5765"""5766cdef RegularMatroid N5767if self._representation is not None:5768N = RegularMatroid(groundset=deepcopy(self._E, memo), matrix=deepcopy(self._representation, memo), keep_initial_representation=True)5769else:5770rows, cols = self._current_rows_cols()5771N = RegularMatroid(groundset=deepcopy(rows + cols, memo), reduced_matrix=deepcopy(self._A, memo))5772N.rename(deepcopy(getattr(self, '__custom_name'), memo))5773return N57745775def __reduce__(self):5776"""5777Save the matroid for later reloading.57785779OUTPUT:57805781A tuple ``(unpickle_regular_matroid, (version, data))``, where5782``unpickle_regular_matroid`` is the name of a function that, when5783called with ``(version, data)``, produces a matroid isomorphic to5784``self``. ``version`` is an integer (currently 0) and ``data`` is a5785tuple ``(A, E, reduced, name)`` where ``A`` is the representation5786matrix, ``E`` is the groundset of the matroid, ``reduced`` is a5787boolean indicating whether ``A`` is a reduced matrix, and ``name`` is5788a custom name.57895790.. WARNING::57915792Users should never call this function directly.57935794EXAMPLES::57955796sage: from sage.matroids.advanced import *5797sage: M = matroids.named_matroids.R12()5798sage: M == loads(dumps(M)) # indirect doctest5799True5800sage: M.rename("R_{12}")5801sage: loads(dumps(M))5802R_{12}5803sage: M = RegularMatroid(Matrix(QQ, [[1, 0, 1], [1, 0, 1]]))5804sage: N = loads(dumps(M))5805sage: N.representation()5806[1 0 1]5807[1 0 1]5808"""5809import sage.matroids.unpickling5810cdef LeanMatrix A5811version = 05812if self._representation is not None:5813A = self._representation5814gs = self._E5815reduced = False5816else:5817A = self._reduced_representation()5818rows, cols = self._current_rows_cols()5819gs = rows + cols5820reduced = True5821data = (A, gs, reduced, getattr(self, '__custom_name'))5822return sage.matroids.unpickling.unpickle_regular_matroid, (version, data)582358245825