Path: blob/master/src/sage/matroids/basis_exchange_matroid.pyx
8817 views
"""1Basis exchange matroids23:class:`BasisExchangeMatroid <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid>`4is an abstract class implementing default matroid functionality in terms of5basis exchange. Several concrete matroid classes are subclasses of this. They6have the following methods in addition to the ones provided by the parent7class :mod:`Matroid <sage.matroids.matroid>`.89- :func:`bases_count() <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid.bases_count>`10- :func:`groundset_list() <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid.groundset_list>`1112AUTHORS:1314- Rudi Pendavingh, Stefan van Zwam (2013-04-01): initial version1516TESTS::1718sage: from sage.matroids.advanced import *19sage: import sage.matroids.basis_exchange_matroid20sage: M = sage.matroids.basis_exchange_matroid.BasisExchangeMatroid(21....: groundset=[1, 2, 3], rank=2)22sage: TestSuite(M).run(skip="_test_pickling")2324Note that this is an abstract base class, without data structure, so no25pickling mechanism was implemented.2627Methods28=======29"""30#*****************************************************************************31# Copyright (C) 2013 Rudi Pendavingh <[email protected]>32# Copyright (C) 2013 Stefan van Zwam <[email protected]>33#34# Distributed under the terms of the GNU General Public License (GPL)35# as published by the Free Software Foundation; either version 2 of36# the License, or (at your option) any later version.37# http://www.gnu.org/licenses/38#*****************************************************************************39include 'sage/misc/bitset.pxi'4041DEF BINT_EXCEPT = -2 ** 31 - 14243from matroid cimport Matroid44from set_system cimport SetSystem45from copy import copy46from itertools import combinations, permutations4748cdef class BasisExchangeMatroid(Matroid):49r"""50Class BasisExchangeMatroid is a virtual class that derives from Matroid.51It implements each of the elementary matroid methods52(:meth:`rank() <sage.matroids.matroid.Matroid.rank>`,53:meth:`max_independent() <sage.matroids.matroid.Matroid.max_independent>`,54:meth:`circuit() <sage.matroids.matroid.Matroid.circuit>`,55:meth:`closure() <sage.matroids.matroid.Matroid.closure>` etc.),56essentially by crawling the base exchange graph of the matroid. This is57the graph whose vertices are the bases of the matroid, two bases being58adjacent in the graph if their symmetric difference has 2 members.5960This base exchange graph is not stored as such, but should be provided61implicity by the child class in the form of two methods62``__is_exchange_pair(x, y)`` and ``__exchange(x, y)``, as well as an63initial basis. At any moment, BasisExchangeMatroid keeps a current basis64`B`. The method ``__is_exchange_pair(x, y)`` should return a boolean65indicating whether `B - x + y` is a basis. The method ``__exchange(x, y)``66is called when the current basis `B` is replaced by said `B-x + y`. It is67up to the child class to update its internal data structure to make68information relative to the new basis more accessible. For instance, a69linear matroid would perform a row reduction to make the column labeled by70`y` a standard basis vector (and therefore the columns indexed by `B-x+y`71would form an identity matrix).7273Each of the elementary matroid methods has a straightforward greedy-type74implementation in terms of these two methods. For example, given a subset75`F` of the groundset, one can step to a basis `B` over the edges of the76base exchange graph which has maximal intersection with `F`, in each step77increasing the intersection of the current `B` with `F`. Then one computes78the rank of `F` as the cardinality of the intersection of `F` and `B`.7980The following matroid classes can/will implement their oracle efficiently81by deriving from ``BasisExchangeMatroid``:8283- :class:`BasisMatroid <sage.matroids.basis_matroid.BasisMatroid>`: keeps84a list of all bases.8586- ``__is_exchange_pair(x, y)`` reduces to a query whether `B - x + y`87is a basis.88- ``__exchange(x, y)`` has no work to do.8990- :class:`LinearMatroid <sage.matroids.linear_matroid.LinearMatroid>`:91keeps a matrix representation `A` of the matroid so that `A[B] = I`.9293- ``__is_exchange_pair(x, y)`` reduces to testing whether `A[r, y]`94is nonzero, where `A[r, x]=1`.95- ``__exchange(x, y)`` should modify the matrix so that `A[B - x + y]`96becomes `I`, which means pivoting on `A[r, y]`.9798- ``TransversalMatroid`` (not yet implemented): If `A` is a set of subsets99of `E`, then `I` is independent if it is a system of distinct100representatives of `A`, i.e. if `I` is covered by a matching of an101appropriate bipartite graph `G`, with color classes `A` and `E` and an102edge `(A_i,e)` if `e` is in the subset `A_i`. At any time you keep a103maximum matching `M` of `G` covering the current basis `B`.104105- ``__is_exchange_pair(x, y)`` checks for the existence of an106`M`-alternating path `P` from `y` to `x`.107- ``__exchange(x, y)`` replaces `M` by the symmetric difference of108`M` and `E(P)`.109110- ``AlgebraicMatroid`` (not yet implemented): keeps a list of polynomials111in variables `E - B + e` for each variable `e` in `B`.112113- ``__is_exchange_pair(x, y)`` checks whether the polynomial that114relates `y` to `E-B` uses `x`.115- ``__exchange(x, y)`` make new list of polynomials by computing116resultants.117118All but the first of the above matroids are algebraic, and all119implementations specializations of the algebraic one.120121BasisExchangeMatroid internally renders subsets of the ground set as122bitsets. It provides optimized methods for enumerating bases, nonbases,123flats, circuits, etc.124"""125126def __init__(self, groundset, basis=None, rank=None):127"""128Construct a BasisExchangeMatroid.129130A BasisExchangeMatroid is a virtual class. It is unlikely that you131want to create a BasisExchangeMatroid from the command line. See the132class docstring for133:class:`BasisExchangeMatroid <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid>`.134135INPUT:136137- ``groundset`` -- a set.138- ``basis`` -- (default: ``None``) a subset of groundset.139- ``rank`` -- (default: ``None``) an integer.140141This initializer sets up a correspondance between elements of142``groundset`` and ``range(len(groundset))``. ``BasisExchangeMatroid``143uses this correspondence for encoding of subsets of the groundset as144bitpacked sets of integers --- see ``__pack()`` and ``__unpack()``. In145general, methods of ``BasisExchangeMatroid`` having a name starting146with two underscores deal with such encoded subsets.147148A second task of this initializer is to store the rank and intialize149the 'current' basis.150151EXAMPLES::152153sage: from sage.matroids.basis_exchange_matroid import BasisExchangeMatroid154sage: M = BasisExchangeMatroid(groundset='abcdef', basis='abc')155sage: sorted(M.groundset())156['a', 'b', 'c', 'd', 'e', 'f']157sage: sorted(M.basis())158['a', 'b', 'c']159"""160self._groundset_size = len(groundset)161self._bitset_size = max(self._groundset_size, 1)162if basis is None:163self._matroid_rank = rank164else:165self._matroid_rank = len(basis)166bitset_init(self._current_basis, self._bitset_size)167bitset_init(self._inside, self._bitset_size)168bitset_init(self._outside, self._bitset_size)169bitset_init(self._input, self._bitset_size)170bitset_init(self._input2, self._bitset_size)171bitset_init(self._output, self._bitset_size)172bitset_init(self._temp, self._bitset_size)173174self._groundset = frozenset(groundset)175if not isinstance(groundset, tuple):176self._E = tuple(groundset)177else:178self._E = groundset179self._idx = {}180cdef long i181for i in xrange(self._groundset_size):182self._idx[self._E[i]] = i183184if basis is not None:185self.__pack(self._current_basis, frozenset(basis))186187def __dealloc__(self):188bitset_free(self._current_basis)189bitset_free(self._inside)190bitset_free(self._outside)191bitset_free(self._input)192bitset_free(self._input2)193bitset_free(self._output)194bitset_free(self._temp)195196cdef __relabel(self, l):197"""198Relabel each element `e` as `l[e]`, where `l` is a given injective map.199200INPUT:201202- `l`, a python object such that `l[e]` is the new label of e.203204OUTPUT:205206``None``.207208NOTE:209For internal use. Matroids are immutable but this method does modify the matroid. The use this method will only210be safe in very limited circumstances, such as perhaps on a fresh copy of a matroid.211212"""213cdef long i214E = []215for i in range(self._groundset_size):216if self._E[i] in l:217E.append(l[self._E[i]])218else:219E.append(self._E[i])220self._E = tuple(E)221self._groundset = frozenset(E)222223self._idx = {}224for i in xrange(self._groundset_size):225self._idx[self._E[i]] = i226227if self._weak_partition_var:228self._weak_partition_var._relabel(l)229230if self._strong_partition_var:231self._strong_partition_var._relabel(l)232if self._heuristic_partition_var:233self._heuristic_partition_var._relabel(l)234235# the engine236cdef __pack(self, bitset_t I, F):237"""238Encode a subset F of the groundset into a bitpacked set of integers239"""240bitset_clear(I)241for f in F:242bitset_add(I, self._idx[f])243244cdef __unpack(self, bitset_t I):245"""246Unencode a bitpacked set of integers to a subset of the groundset.247"""248cdef long i249F = set()250i = bitset_first(I)251while i >= 0:252F.add(self._E[i])253i = bitset_next(I, i + 1)254return frozenset(F)255256# this method needs to be overridden by child class257cdef bint __is_exchange_pair(self, long x, long y) except BINT_EXCEPT:258"""259Test if current_basis-x + y is a basis260"""261raise NotImplementedError262263# if this method is overridden by a child class, the child class needs to call this method264cdef bint __exchange(self, long x, long y) except BINT_EXCEPT:265"""266put current_basis <-- current_basis-x + y267"""268bitset_discard(self._current_basis, x)269bitset_add(self._current_basis, y)270271cdef __move(self, bitset_t X, bitset_t Y):272"""273Change current_basis to minimize intersection with ``X``, maximize intersection with ``Y``.274"""275cdef long x, y276x = bitset_first(X)277while x >= 0:278y = bitset_first(Y)279while y >= 0:280if self.__is_exchange_pair(x, y):281self.__exchange(x, y)282bitset_discard(Y, y)283bitset_discard(X, x)284if bitset_isempty(Y):285return286break287else:288y = bitset_next(Y, y + 1)289x = bitset_next(X, x + 1)290291cdef __fundamental_cocircuit(self, bitset_t C, long x):292"""293Return the unique cocircuit that meets ``self._current_basis`` in exactly element ``x``.294"""295cdef long y296bitset_clear(C)297bitset_complement(self._temp, self._current_basis)298y = bitset_first(self._temp)299while y >= 0:300if self.__is_exchange_pair(x, y):301bitset_add(C, y)302y = bitset_next(self._temp, y + 1)303bitset_add(C, x)304305cdef __fundamental_circuit(self, bitset_t C, long y):306"""307Return the unique circuit contained in ``self._current_basis`` union ``y``.308"""309cdef long x310bitset_clear(C)311x = bitset_first(self._current_basis)312while x >= 0:313if self.__is_exchange_pair(x, y):314bitset_add(C, x)315x = bitset_next(self._current_basis, x + 1)316bitset_add(C, y)317318cdef __max_independent(self, bitset_t R, bitset_t F):319"""320Bitpacked version of ``max_independent``.321"""322bitset_difference(self._inside, self._current_basis, F)323bitset_difference(self._outside, F, self._current_basis)324self.__move(self._inside, self._outside)325bitset_intersection(R, self._current_basis, F)326327cdef __circuit(self, bitset_t R, bitset_t F):328"""329Bitpacked version of ``circuit``.330"""331bitset_difference(self._inside, self._current_basis, F)332bitset_difference(self._outside, F, self._current_basis)333cdef long x, y334y = bitset_first(self._outside)335if y < 0:336raise ValueError('no circuit in independent set.')337while y >= 0:338x = bitset_first(self._inside)339while x >= 0:340if self.__is_exchange_pair(x, y):341self.__exchange(x, y)342bitset_discard(self._outside, y)343bitset_discard(self._inside, x)344if bitset_isempty(self._outside):345raise ValueError('no circuit in independent set.')346break347else:348x = bitset_next(self._inside, x + 1)349if x == -1:350self.__fundamental_circuit(R, y)351return352y = bitset_next(self._outside, y + 1)353354cdef __closure(self, bitset_t R, bitset_t F):355"""356Bitpacked version of ``closure``.357"""358bitset_difference(self._inside, self._current_basis, F)359bitset_difference(self._outside, F, self._current_basis)360self.__move(self._inside, self._outside)361bitset_set_first_n(R, self._groundset_size)362cdef long x = bitset_first(self._inside)363while x >= 0:364self.__fundamental_cocircuit(F, x)365bitset_difference(R, R, F)366x = bitset_next(self._inside, x + 1)367368cdef __max_coindependent(self, bitset_t R, bitset_t F):369"""370Bitpacked version of ``max_coindependent``.371"""372bitset_complement(R, F)373bitset_difference(self._inside, self._current_basis, R)374bitset_difference(self._outside, R, self._current_basis)375self.__move(self._inside, self._outside)376bitset_difference(R, F, self._current_basis)377378cdef __cocircuit(self, bitset_t R, bitset_t F):379"""380Bitpacked version of ``cocircuit``.381"""382bitset_complement(R, F)383bitset_difference(self._inside, self._current_basis, R)384bitset_difference(self._outside, R, self._current_basis)385cdef long x, y386x = bitset_first(self._inside)387if x < 0:388raise ValueError('no cocircuit in coindependent set.')389while x >= 0:390y = bitset_first(self._outside)391while y >= 0:392if self.__is_exchange_pair(x, y):393self.__exchange(x, y)394bitset_discard(self._outside, y)395bitset_discard(self._inside, x)396if bitset_isempty(self._inside):397raise ValueError('no cocircuit in coindependent set.')398break399else:400y = bitset_next(self._outside, y + 1)401if y == -1:402self.__fundamental_cocircuit(R, x)403return404x = bitset_next(self._inside, x + 1)405406cdef __coclosure(self, bitset_t R, bitset_t F):407"""408Bitpacked version of ``closure``.409"""410bitset_complement(R, F)411bitset_difference(self._inside, self._current_basis, R)412bitset_difference(self._outside, R, self._current_basis)413self.__move(self._inside, self._outside)414bitset_set_first_n(R, self._groundset_size)415cdef long y = bitset_first(self._outside)416while y >= 0:417self.__fundamental_circuit(F, y)418bitset_difference(R, R, F)419y = bitset_next(self._outside, y + 1)420421cdef __augment(self, bitset_t R, bitset_t X, bitset_t Y):422"""423Bitpacked version of ``augment``.424"""425bitset_difference(self._inside, self._current_basis, X)426bitset_difference(self._outside, X, self._current_basis)427self.__move(self._inside, self._outside)428bitset_difference(self._inside, self._inside, Y)429bitset_difference(self._outside, Y, self._current_basis)430self.__move(self._inside, self._outside)431bitset_intersection(R, self._current_basis, Y)432433cdef bint __is_independent(self, bitset_t F):434"""435Bitpacked version of ``is_independent``.436"""437bitset_difference(self._inside, self._current_basis, F)438bitset_difference(self._outside, F, self._current_basis)439self.__move(self._inside, self._outside)440return bitset_isempty(self._outside)441442cdef __move_current_basis(self, bitset_t X, bitset_t Y):443"""444Bitpacked version of ``_move_current_basis``.445"""446bitset_difference(self._inside, self._current_basis, X)447bitset_difference(self._outside, X, self._current_basis)448self.__move(self._inside, self._outside)449bitset_intersection(self._inside, self._current_basis, Y)450bitset_complement(self._outside, self._current_basis)451bitset_difference(self._outside, self._outside, Y)452self.__move(self._inside, self._outside)453454# functions for derived classes and for parent class455cdef bint _set_current_basis(self, F):456"""457Set _current_basis to subset of the groundset ``F``.458"""459self.__pack(self._input, F)460bitset_difference(self._inside, self._current_basis, self._input)461bitset_difference(self._outside, self._input, self._current_basis)462self.__move(self._inside, self._outside)463return bitset_isempty(self._outside) and bitset_isempty(self._inside)464465# groundset and full_rank466cpdef groundset(self):467"""468Return the groundset of the matroid.469470The groundset is the set of elements that comprise the matroid.471472OUTPUT:473474A set.475476EXAMPLES::477478sage: M = matroids.named_matroids.Fano()479sage: sorted(M.groundset())480['a', 'b', 'c', 'd', 'e', 'f', 'g']481"""482return self._groundset483484cpdef groundset_list(self):485"""486Return a list of elements of the groundset of the matroid.487488The order of the list does not change between calls.489490OUTPUT:491492A list.493494.. SEEALSO::495496:meth:`M.groundset() <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid.groundset>`497498EXAMPLES::499500sage: M = matroids.named_matroids.Fano()501sage: type(M.groundset())502<type 'frozenset'>503sage: type(M.groundset_list())504<type 'list'>505sage: sorted(M.groundset_list())506['a', 'b', 'c', 'd', 'e', 'f', 'g']507508sage: E = M.groundset_list()509sage: E.remove('a')510sage: sorted(M.groundset_list())511['a', 'b', 'c', 'd', 'e', 'f', 'g']512"""513return list(self._E)514515def __len__(self):516"""517Return the size of the groundset.518519EXAMPLES::520521sage: M = matroids.named_matroids.Fano()522sage: len(M)5237524sage: len(M.groundset())5257526527"""528return self._groundset_size529530cpdef full_rank(self):531r"""532Return the rank of the matroid.533534The *rank* of the matroid is the size of the largest independent535subset of the groundset.536537OUTPUT:538539Integer.540541EXAMPLES::542543sage: M = matroids.named_matroids.Fano()544sage: M.full_rank()5453546sage: M.dual().full_rank()5474548"""549return self._matroid_rank550551cpdef full_corank(self):552r"""553Return the corank of the matroid.554555The *corank* of the matroid equals the rank of the dual matroid. It is556given by ``M.size() - M.full_rank()``.557558OUTPUT:559560Integer.561562.. SEEALSO::563564:meth:`M.dual() <sage.matroids.matroid.Matroid.dual>`,565:meth:`M.full_rank() <sage.matroids.basis_exchange_matroid.BasisExchangeMatroid.full_rank>`566567EXAMPLES::568569sage: M = matroids.named_matroids.Fano()570sage: M.full_corank()5714572sage: M.dual().full_corank()5733574"""575return self._groundset_size - self._matroid_rank576577# matroid oracles578579cpdef basis(self):580r"""581Return an arbitrary basis of the matroid.582583A *basis* is an inclusionwise maximal independent set.584585.. NOTE::586587The output of this method can change in between calls. It reflects588the internal state of the matroid. This state is updated by lots589of methods, including the method ``M._move_current_basis()``.590591OUTPUT:592593Set of elements.594595EXAMPLES::596597sage: M = matroids.named_matroids.Fano()598sage: sorted(M.basis())599['a', 'b', 'c']600sage: M.rank('cd')6012602sage: sorted(M.basis())603['a', 'c', 'd']604605"""606return self.__unpack(self._current_basis)607608cpdef _move_current_basis(self, X, Y):609"""610Change current basis so that intersection with X is maximized,611intersection with Y is minimized.612613INPUT:614615- ``X`` -- an object with Python's ``frozenset`` interface containing616a subset of ``self.groundset()``.617- ``Y`` -- an object with Python's ``frozenset`` interface containing618a subset of ``self.groundset()``.619620OUTPUT:621622Nothing.623624EXAMPLES::625626sage: from sage.matroids.advanced import *627sage: M = BasisMatroid(matroids.named_matroids.Vamos())628sage: sorted(M.basis())629['a', 'b', 'c', 'e']630sage: M._move_current_basis('ef', 'a')631sage: sorted(M.basis())632['b', 'c', 'e', 'f']633634"""635self.__pack(self._input, X)636self.__pack(self._input2, Y)637self.__move_current_basis(self._input, self._input2)638639cpdef _max_independent(self, F):640"""641Compute a maximal independent subset.642643INPUT:644645- ``F`` -- An object with Python's ``frozenset`` interface containing646a subset of ``self.groundset()``.647648OUTPUT:649650A subset of ``F``.651652EXAMPLES::653654sage: from sage.matroids.advanced import *655sage: M = BasisMatroid(matroids.named_matroids.Vamos())656sage: sorted(M._max_independent(set(['a', 'c', 'd', 'e', 'f'])))657['a', 'c', 'd', 'e']658659.. NOTE::660661This is an unguarded method. For the version that verifies if662the input is indeed a subset of the ground set,663see :meth:`<sage.matroids.matroid.Matroid.max_independent>`.664665"""666self.__pack(self._input, F)667self.__max_independent(self._output, self._input)668return self.__unpack(self._output)669670cpdef _rank(self, F):671"""672Compute the rank of a subset of the ground set.673674INPUT:675676- ``F`` -- An object with Python's ``frozenset`` interface containing677a subset of ``self.groundset()``.678679OUTPUT:680681Integer.682683EXAMPLES::684685sage: from sage.matroids.advanced import *686sage: M = BasisMatroid(matroids.named_matroids.Vamos())687sage: M._rank(set(['a', 'c', 'd', 'e', 'f']))6884689690.. NOTE::691692This is an unguarded method. For the version that verifies if693the input is indeed a subset of the ground set,694see :meth:`<sage.matroids.matroid.Matroid.rank>`.695696"""697self.__pack(self._input, F)698self.__max_independent(self._output, self._input)699return bitset_len(self._output)700701cpdef _circuit(self, F):702"""703Return a minimal dependent subset.704705INPUT:706707- ``F`` -- An object with Python's ``frozenset`` interface containing708a subset of ``self.groundset()``.709710OUTPUT:711712A circuit contained in ``F``, if it exists. Otherwise an error is713raised.714715EXAMPLES::716717sage: from sage.matroids.advanced import *718sage: M = BasisMatroid(matroids.named_matroids.Vamos())719sage: sorted(sage.matroids.matroid.Matroid._circuit(M,720....: set(['a', 'c', 'd', 'e', 'f'])))721['c', 'd', 'e', 'f']722sage: sorted(sage.matroids.matroid.Matroid._circuit(M,723....: set(['a', 'c', 'd'])))724Traceback (most recent call last):725...726ValueError: no circuit in independent set.727728.. NOTE::729730This is an unguarded method. For the version that verifies if731the input is indeed a subset of the ground set,732see :meth:`<sage.matroids.matroid.Matroid.circuit>`.733"""734self.__pack(self._input, F)735self.__circuit(self._output, self._input)736return self.__unpack(self._output)737738cpdef _fundamental_circuit(self, B, e):739r"""740Return the `B`-fundamental circuit using `e`.741742Internal version that does no input checking.743744INPUT:745746- ``B`` -- a basis of the matroid.747- ``e`` -- an element not in ``B``.748749OUTPUT:750751A set of elements.752753EXAMPLES::754755sage: M = matroids.named_matroids.P8()756sage: sorted(M._fundamental_circuit('abcd', 'e'))757['a', 'b', 'c', 'e']758"""759self.__pack(self._input, B)760bitset_clear(self._input2)761self.__move_current_basis(self._input, self._input2)762self.__fundamental_circuit(self._output, self._idx[e])763return self.__unpack(self._output)764765cpdef _closure(self, F):766"""767Return the closure of a set.768769INPUT:770771- ``F`` -- An object with Python's ``frozenset`` interface containing772a subset of ``self.groundset()``.773774OUTPUT:775776The smallest closed set containing ``F``.777778EXAMPLES::779780sage: from sage.matroids.advanced import *781sage: M = BasisMatroid(matroids.named_matroids.Vamos())782sage: sorted(M._closure(set(['a', 'b', 'c'])))783['a', 'b', 'c', 'd']784785.. NOTE::786787This is an unguarded method. For the version that verifies if the788input is indeed a subset of the ground set, see789:meth:`<sage.matroids.matroid.Matroid.closure>`.790791"""792self.__pack(self._input, F)793self.__closure(self._output, self._input)794return self.__unpack(self._output)795796cpdef _max_coindependent(self, F):797"""798Compute a maximal coindependent subset.799800INPUT:801802- ``F`` -- An object with Python's ``frozenset`` interface containing803a subset of ``self.groundset()``.804805OUTPUT:806807A maximal coindependent subset of ``F``.808809EXAMPLES::810811sage: from sage.matroids.advanced import *812sage: M = BasisMatroid(matroids.named_matroids.Vamos())813sage: sorted(M._max_coindependent(set(['a', 'c', 'd', 'e', 'f'])))814['a', 'c', 'd', 'f']815816.. NOTE::817818This is an unguarded method. For the version that verifies if the819input is indeed a subset of the ground set,820see :meth:`<sage.matroids.matroid.Matroid.max_coindependent>`.821822"""823self.__pack(self._input, F)824self.__max_coindependent(self._output, self._input)825return self.__unpack(self._output)826827cpdef _corank(self, F):828"""829Return the corank of a set.830831INPUT:832833- ``F`` -- An object with Python's ``frozenset`` interface containing834a subset of ``self.groundset()``.835836OUTPUT:837838Integer, the corank of ``F``.839840EXAMPLES::841842sage: from sage.matroids.advanced import *843sage: M = BasisMatroid(matroids.named_matroids.Vamos())844sage: M._corank(set(['a', 'e', 'g', 'd', 'h']))8454846847.. NOTE::848849This is an unguarded method. For the version that verifies if the850input is indeed a subset of the ground set,851see :meth:`<sage.matroids.matroid.Matroid.corank>`.852"""853self.__pack(self._input, F)854self.__max_coindependent(self._output, self._input)855return bitset_len(self._output)856857cpdef _cocircuit(self, F):858"""859Return a minimal codependent subset.860861INPUT:862863- ``F`` -- An object with Python's ``frozenset`` interface containing864a subset of ``self.groundset()``.865866OUTPUT:867868A cocircuit contained in ``F``, if it exists. Otherwise an error is869raised.870871EXAMPLES::872873sage: from sage.matroids.advanced import *874sage: M = BasisMatroid(matroids.named_matroids.Vamos())875sage: sorted(sage.matroids.matroid.Matroid._cocircuit(M,876....: set(['a', 'c', 'd', 'e', 'f'])))877['c', 'd', 'e', 'f']878sage: sorted(sage.matroids.matroid.Matroid._cocircuit(M,879....: set(['a', 'c', 'd'])))880Traceback (most recent call last):881...882ValueError: no cocircuit in coindependent set.883884..NOTE::885886This is an unguarded method. For the version that verifies if the887input is indeed a subset of the ground set,888see :meth:`<sage.matroids.matroid.Matroid.cocircuit>`.889"""890self.__pack(self._input, F)891self.__cocircuit(self._output, self._input)892return self.__unpack(self._output)893894cpdef _fundamental_cocircuit(self, B, e):895r"""896Return the `B`-fundamental circuit using `e`.897898Internal version that does no input checking.899900INPUT:901902- ``B`` -- a basis of the matroid.903- ``e`` -- an element of ``B``.904905OUTPUT:906907A set of elements.908909EXAMPLES::910911sage: M = matroids.named_matroids.P8()912sage: sorted(M._fundamental_cocircuit('efgh', 'e'))913['b', 'c', 'd', 'e']914"""915self.__pack(self._input, B)916bitset_clear(self._input2)917self.__move_current_basis(self._input, self._input2)918self.__fundamental_cocircuit(self._output, self._idx[e])919return self.__unpack(self._output)920921cpdef _coclosure(self, F):922"""923Return the coclosure of a set.924925INPUT:926927- ``X`` -- An object with Python's ``frozenset`` interface containing928a subset of ``self.groundset()``.929930OUTPUT:931932The smallest coclosed set containing ``X``.933934EXAMPLES::935936sage: from sage.matroids.advanced import *937sage: M = BasisMatroid(matroids.named_matroids.Vamos())938sage: sorted(M._coclosure(set(['a', 'b', 'c'])))939['a', 'b', 'c', 'd']940941..NOTE::942943This is an unguarded method. For the version that verifies if the944input is indeed a subset of the ground set,945see :meth:`<sage.matroids.matroid.Matroid.coclosure>`.946947"""948self.__pack(self._input, F)949self.__coclosure(self._output, self._input)950return self.__unpack(self._output)951952cpdef _augment(self, X, Y):953r"""954Return a maximal subset `I` of `Y` such that `r(X + I)=r(X) + r(I)`.955956This version of ``augment`` does no type checking. In particular,957``Y`` is assumed to be disjoint from ``X``.958959INPUT:960961- ``X`` -- An object with Python's ``frozenset`` interface containing962a subset of ``self.groundset()``.963- ``Y`` -- An object with Python's ``frozenset`` interface containing964a subset of ``self.groundset()``, and disjoint from ``X``.965966OUTPUT:967968A subset `I` of ``Y`` such that `r(X + I)=r(X) + r(I)`.969970EXAMPLES::971972sage: from sage.matroids.advanced import *973sage: M = BasisMatroid(matroids.named_matroids.Vamos())974sage: sorted(M._augment(set(['a']), set(['e', 'f', 'g', 'h'])))975['e', 'f', 'g']976977"""978self.__pack(self._input, X)979self.__pack(self._input2, Y)980self.__augment(self._output, self._input, self._input2)981return self.__unpack(self._output)982983cpdef _is_independent(self, F):984"""985Test if input is independent.986987INPUT:988989- ``F`` -- An object with Python's ``frozenset`` interface containing990a subset of ``self.groundset()``.991992OUTPUT:993994Boolean.995996EXAMPLES::997998sage: from sage.matroids.advanced import *999sage: M = BasisMatroid(matroids.named_matroids.Vamos())1000sage: M._is_independent(set(['a', 'b', 'c']))1001True1002sage: M._is_independent(set(['a', 'b', 'c', 'd']))1003False10041005..NOTE::10061007This is an unguarded method. For the version that verifies if1008the input is indeed a subset of the ground set,1009see :meth:`<sage.matroids.matroid.Matroid.is_independent>`.1010"""1011self.__pack(self._input, F)1012return self.__is_independent(self._input)10131014# enumeration10151016cpdef f_vector(self):1017r"""1018Return the `f`-vector of the matroid.10191020The `f`-*vector* is a vector `(f_0, ..., f_r)`, where `f_i` is the1021number of flats of rank `i`, and `r` is the rank of the matroid.10221023OUTPUT:10241025List of integers.10261027EXAMPLES::10281029sage: M = matroids.named_matroids.S8()1030sage: M.f_vector()1031[1, 8, 22, 14, 1]10321033"""1034cdef bitset_t *flats, *todo1035if self._matroid_rank == 0:1036return [0]1037flats = <bitset_t*>sage_malloc((self.full_rank() + 1) * sizeof(bitset_t))1038todo = <bitset_t*>sage_malloc((self.full_rank() + 1) * sizeof(bitset_t))10391040for i in xrange(self.full_rank() + 1):1041bitset_init(flats[i], self._bitset_size)1042bitset_init(todo[i], self._bitset_size)1043f_vec = [0 for i in xrange(self.full_rank() + 1)]1044i = 01045bitset_clear(todo[0])1046self.__closure(flats[0], todo[0])1047bitset_complement(todo[0], flats[0])1048self._f_vector_rec(f_vec, flats, todo, 0, 0)1049for i in xrange(self.full_rank() + 1):1050bitset_free(flats[i])1051bitset_free(todo[i])1052sage_free(flats)1053sage_free(todo)1054return f_vec10551056cdef _f_vector_rec(self, object f_vec, bitset_t* flats, bitset_t* todo, long elt, long i):1057"""1058Recursion for the f_vector method.1059"""1060cdef long e1061f_vec[i] += 11062e = bitset_next(todo[i], elt)1063while e >= 0:1064bitset_copy(self._input, flats[i])1065bitset_add(self._input, e)1066self.__closure(flats[i + 1], self._input)1067bitset_difference(todo[i], todo[i], flats[i + 1])1068bitset_difference(todo[i + 1], flats[i + 1], flats[i])1069if bitset_first(todo[i + 1]) == e:1070bitset_copy(todo[i + 1], todo[i])1071self._f_vector_rec(f_vec, flats, todo, e + 1, i + 1)1072e = bitset_next(todo[i], e)10731074cpdef flats(self, r):1075"""1076Return the collection of flats of the matroid of specified rank.10771078A *flat* is a closed set.10791080INPUT:10811082- ``r`` -- A natural number.10831084OUTPUT:10851086An iterable containing all flats of rank ``r``.10871088.. SEEALSO::10891090:meth:`Matroid.closure() <sage.matroids.matroid.Matroid.closure>`10911092EXAMPLES::10931094sage: M = matroids.named_matroids.S8()1095sage: M.f_vector()1096[1, 8, 22, 14, 1]1097sage: len(M.flats(2))1098221099sage: len(M.flats(8))110001101sage: len(M.flats(4))110211103"""1104cdef bitset_t *flats, *todo1105if r < 0 or r > self.full_rank():1106return SetSystem(self._E)1107if r == self.full_rank():1108return SetSystem(self._E, subsets=[self.groundset()])1109flats = <bitset_t*>sage_malloc((r + 1) * sizeof(bitset_t))1110todo = <bitset_t*>sage_malloc((r + 1) * sizeof(bitset_t))11111112for i in xrange(r + 1):1113bitset_init(flats[i], self._bitset_size)1114bitset_init(todo[i], self._bitset_size)1115Rflats = SetSystem(self._E)1116i = 01117bitset_clear(todo[0])1118self.__closure(flats[0], todo[0])1119bitset_complement(todo[0], flats[0])1120self._flats_rec(Rflats, r, flats, todo, 0, 0)1121for i in xrange(r + 1):1122bitset_free(flats[i])1123bitset_free(todo[i])1124sage_free(flats)1125sage_free(todo)1126return Rflats11271128cdef _flats_rec(self, SetSystem Rflats, long R, bitset_t* flats, bitset_t* todo, long elt, long i):1129"""1130Recursion for the ``flats`` method.1131"""1132cdef long e1133if i == R:1134Rflats._append(flats[i])1135return1136e = bitset_next(todo[i], elt)1137while e >= 0:1138bitset_copy(self._input, flats[i]) # I fear that self._input is dangerous in a parallel computing environment. --SvZ1139bitset_add(self._input, e) # It absolutely is! --RP1140self.__closure(flats[i + 1], self._input)1141bitset_difference(todo[i], todo[i], flats[i + 1])1142bitset_difference(todo[i + 1], flats[i + 1], flats[i])1143if bitset_first(todo[i + 1]) == e:1144bitset_copy(todo[i + 1], todo[i])1145self._flats_rec(Rflats, R, flats, todo, e + 1, i + 1)1146e = bitset_next(todo[i], e)11471148cpdef coflats(self, r):1149"""1150Return the collection of coflats of the matroid of specified corank.11511152A *coflat* is a coclosed set.11531154INPUT:11551156- ``r`` -- A natural number.11571158OUTPUT:11591160An iterable containing all coflats of corank ``r``.11611162.. SEEALSO::11631164:meth:`Matroid.coclosure() <sage.matroids.matroid.Matroid.coclosure>`11651166EXAMPLES::11671168sage: M = matroids.named_matroids.S8().dual()1169sage: M.f_vector()1170[1, 8, 22, 14, 1]1171sage: len(M.coflats(2))1172221173sage: len(M.coflats(8))117401175sage: len(M.coflats(4))117611177"""1178cdef bitset_t *coflats, *todo1179if r < 0 or r > self.full_corank():1180return SetSystem(self._E)1181if r == self.full_corank():1182return SetSystem(self._E, subsets=[self.groundset()])1183coflats = <bitset_t*>sage_malloc((r + 1) * sizeof(bitset_t))1184todo = <bitset_t*>sage_malloc((r + 1) * sizeof(bitset_t))11851186for i in xrange(r + 1):1187bitset_init(coflats[i], self._bitset_size)1188bitset_init(todo[i], self._bitset_size)1189Rcoflats = SetSystem(self._E)1190i = 01191bitset_clear(todo[0])1192self.__coclosure(coflats[0], todo[0])1193bitset_complement(todo[0], coflats[0])1194self._coflats_rec(Rcoflats, r, coflats, todo, 0, 0)1195for i in xrange(r + 1):1196bitset_free(coflats[i])1197bitset_free(todo[i])1198sage_free(coflats)1199sage_free(todo)1200return Rcoflats12011202cdef _coflats_rec(self, SetSystem Rcoflats, long R, bitset_t* coflats, bitset_t* todo, long elt, long i):1203"""1204Recursion for the ``coflats`` method.1205"""1206cdef long e1207if i == R:1208Rcoflats._append(coflats[i])1209return1210e = bitset_next(todo[i], elt)1211while e >= 0:1212bitset_copy(self._input, coflats[i])1213bitset_add(self._input, e)1214self.__coclosure(coflats[i + 1], self._input)1215bitset_difference(todo[i], todo[i], coflats[i + 1])1216bitset_difference(todo[i + 1], coflats[i + 1], coflats[i])1217if bitset_first(todo[i + 1]) == e:1218bitset_copy(todo[i + 1], todo[i])1219self._coflats_rec(Rcoflats, R, coflats, todo, e + 1, i + 1)1220e = bitset_next(todo[i], e)12211222cdef _flat_element_inv(self, long k):1223"""1224Compute a flat-element invariant of the matroid.1225"""1226cdef bitset_t *flats, *todo1227flats = <bitset_t*>sage_malloc((k + 1) * sizeof(bitset_t))1228todo = <bitset_t*>sage_malloc((k + 1) * sizeof(bitset_t))12291230for i in xrange(k + 1):1231bitset_init(flats[i], self._bitset_size)1232bitset_init(todo[i], self._bitset_size)1233f_inc = [[0 for e in range(self._groundset_size + 1)] for i in xrange(k + 1)]1234i = 01235bitset_clear(todo[0])1236self.__closure(flats[0], todo[0])1237bitset_complement(todo[0], flats[0])1238self._flat_element_inv_rec(f_inc, k, flats, todo, 0, 0)1239for i in xrange(k + 1):1240bitset_free(flats[i])1241bitset_free(todo[i])1242sage_free(flats)1243sage_free(todo)1244fie = {}1245for e in range(self._groundset_size):1246t = tuple([f_inc[i][e] for i in xrange(k + 1)])1247if t in fie:1248fie[t].add(self._E[e])1249else:1250fie[t] = set([self._E[e]])1251f_vec = tuple([f_inc[i][self._groundset_size] for i in xrange(k + 1)])1252return fie, f_vec12531254cdef _flat_element_inv_rec(self, object f_inc, long R, bitset_t* flats, bitset_t* todo, long elt, long i):1255"""1256Recursion for ``_flat_element_inv``.1257"""1258cdef long e, k1259k = bitset_len(flats[i])1260k = k * k1261e = bitset_first(flats[i])1262inc = f_inc[i]1263while e >= 0:1264inc[e] += k1265e = bitset_next(flats[i], e + 1)1266inc[self._groundset_size] += k1267if i == R:1268return1269e = bitset_next(todo[i], elt)1270while e >= 0:1271bitset_copy(self._input, flats[i])1272bitset_add(self._input, e)1273self.__closure(flats[i + 1], self._input)1274bitset_difference(todo[i], todo[i], flats[i + 1])1275bitset_difference(todo[i + 1], flats[i + 1], flats[i])1276if bitset_first(todo[i + 1]) == e:1277bitset_copy(todo[i + 1], todo[i])1278self._flat_element_inv_rec(f_inc, R, flats, todo, e + 1, i + 1)1279e = bitset_next(todo[i], e)12801281cpdef bases_count(self):1282"""1283Return the number of bases of the matroid.12841285A *basis* is an inclusionwise maximal independent set.12861287.. SEEALSO::12881289:meth:`M.basis() <sage.matroids.matroid.Matroid.basis>`.12901291OUTPUT:12921293Integer.12941295EXAMPLES::12961297sage: M = matroids.named_matroids.N1()1298sage: M.bases_count()12991841300"""1301if self._bcount is not None:1302return self._bcount1303cdef long res = 01304bitset_clear(self._input)1305bitset_set_first_n(self._input, self._matroid_rank)1306repeat = True1307while repeat:1308if self.__is_independent(self._input):1309res += 11310repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)1311self._bcount = res1312return self._bcount13131314cpdef independent_r_sets(self, long r):1315"""1316Return the list of size-``r`` independent subsets of the matroid.13171318INPUT:13191320- ``r`` -- a nonnegative integer.13211322OUTPUT:13231324An iterable containing all independent subsets of the matroid of1325cardinality ``r``.13261327EXAMPLES::13281329sage: M = matroids.named_matroids.N1()1330sage: M.bases_count()13311841332sage: [len(M.independent_r_sets(r)) for r in range(M.full_rank() + 1)]1333[1, 10, 45, 120, 201, 184]13341335"""1336cdef SetSystem BB1337BB = SetSystem(self._E)1338if r < 0:1339return BB1340bitset_clear(self._input)1341bitset_set_first_n(self._input, r)1342repeat = True1343while repeat:1344if self.__is_independent(self._input):1345BB._append(self._input)1346repeat = nxksrd(self._input, self._groundset_size, r, True)1347return BB13481349cpdef bases(self):1350"""1351Return the list of bases of the matroid.13521353A *basis* is a maximal independent set.13541355OUTPUT:13561357An iterable containing all bases of the matroid.13581359EXAMPLES::13601361sage: M = matroids.named_matroids.N1()1362sage: M.bases_count()13631841364sage: len([B for B in M.bases()])13651841366"""1367return self.independent_r_sets(self.full_rank())13681369cpdef dependent_r_sets(self, long r):1370"""1371Return the list of dependent subsets of fixed size.13721373INPUT:13741375- ``r`` -- a nonnegative integer.13761377OUTPUT:13781379An iterable containing all dependent subsets of size ``r``.13801381EXAMPLES::13821383sage: M = matroids.named_matroids.N1()1384sage: len(M.nonbases())1385681386sage: [len(M.dependent_r_sets(r)) for r in range(M.full_rank() + 1)]1387[0, 0, 0, 0, 9, 68]13881389"""1390cdef SetSystem NB1391NB = SetSystem(self._E)1392if r < 0:1393return NB1394bitset_clear(self._input)1395bitset_set_first_n(self._input, r)1396repeat = True1397while repeat:1398if not self.__is_independent(self._input):1399NB._append(self._input)1400repeat = nxksrd(self._input, self._groundset_size, r, True)1401NB.resize()1402return NB14031404cpdef nonbases(self):1405"""1406Return the list of nonbases of the matroid.14071408A *nonbasis* is a set with cardinality ``self.full_rank()`` that is1409not a basis.14101411OUTPUT:14121413An iterable containing the nonbases of the matroid.14141415.. SEEALSO::14161417:meth:`Matroid.basis() <sage.matroids.matroid.Matroid.basis>`14181419EXAMPLES::14201421sage: M = matroids.named_matroids.N1()1422sage: binomial(M.size(), M.full_rank())-M.bases_count()1423681424sage: len([B for B in M.nonbases()])1425681426"""1427return self.dependent_r_sets(self.full_rank())14281429cpdef nonspanning_circuits(self):1430"""1431Return the list of nonspanning circuits of the matroid.14321433A *nonspanning circuit* is a circuit whose rank is strictly smaller1434than the rank of the matroid.14351436OUTPUT:14371438An iterable containing all nonspanning circuits.14391440.. SEEALSO::14411442:meth:`Matroid.circuit() <sage.matroids.matroid.Matroid.circuit>`,1443:meth:`Matroid.rank() <sage.matroids.matroid.Matroid.rank>`14441445EXAMPLES::14461447sage: M = matroids.named_matroids.N1()1448sage: len(M.nonspanning_circuits())1449231450"""1451cdef SetSystem NSC1452NSC = SetSystem(self._E)1453bitset_clear(self._input)1454bitset_set_first_n(self._input, self._matroid_rank)1455cdef long e, f1456repeat = True1457while repeat:1458if self.__is_independent(self._input):1459bitset_complement(self._input2, self._current_basis)1460e = bitset_first(self._current_basis)1461while e >= 0:1462self.__fundamental_cocircuit(self._output, e)1463if e > bitset_first(self._output):1464bitset_intersection(self._input2, self._input2, self._output)1465e = bitset_next(self._current_basis, e + 1)1466f = bitset_first(self._input2)1467while f >= 0:1468self.__fundamental_circuit(self._output, f)1469if f == bitset_first(self._output) and bitset_len(self._output) <= self._matroid_rank:1470NSC._append(self._output)1471f = bitset_next(self._input2, f + 1)1472repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)1473NSC.resize()1474return NSC14751476cpdef noncospanning_cocircuits(self):1477"""1478Return the list of noncospanning cocircuits of the matroid.14791480A *noncospanning cocircuit* is a cocircuit whose corank is strictly1481smaller than the corank of the matroid.14821483OUTPUT:14841485An iterable containing all nonspanning circuits.14861487.. SEEALSO::14881489:meth:`Matroid.cocircuit() <sage.matroids.matroid.Matroid.cocircuit>`,1490:meth:`Matroid.corank() <sage.matroids.matroid.Matroid.corank>`14911492EXAMPLES::14931494sage: M = matroids.named_matroids.N1()1495sage: len(M.noncospanning_cocircuits())1496231497"""1498cdef SetSystem NSC1499NSC = SetSystem(self._E)1500bitset_clear(self._input)1501bitset_set_first_n(self._input, self._matroid_rank)1502cdef long e, f, corank1503corank = self._groundset_size - self._matroid_rank1504repeat = True1505while repeat:1506if self.__is_independent(self._input):1507bitset_copy(self._input2, self._current_basis)1508e = bitset_first(self._current_basis)1509for e in xrange(self._groundset_size):1510if not bitset_in(self._current_basis, e):1511self.__fundamental_circuit(self._output, e)1512if e > bitset_first(self._output):1513bitset_intersection(self._input2, self._input2, self._output)1514f = bitset_first(self._input2)1515while f >= 0:1516self.__fundamental_cocircuit(self._output, f)1517if f == bitset_first(self._output) and bitset_len(self._output) <= corank:1518NSC._append(self._output)1519f = bitset_next(self._input2, f + 1)1520repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)1521NSC.resize()1522return NSC15231524cpdef cocircuits(self):1525"""1526Return the list of cocircuits of the matroid.15271528OUTPUT:15291530An iterable containing all cocircuits.15311532.. SEEALSO::15331534:meth:`Matroid.cocircuit() <sage.matroids.matroid.Matroid.cocircuit>`15351536EXAMPLES::15371538sage: M = Matroid(bases=matroids.named_matroids.NonFano().bases())1539sage: sorted([sorted(C) for C in M.cocircuits()])1540[['a', 'b', 'c', 'd', 'g'], ['a', 'b', 'c', 'e', 'g'],1541['a', 'b', 'c', 'f', 'g'], ['a', 'b', 'd', 'e'],1542['a', 'c', 'd', 'f'], ['a', 'e', 'f', 'g'], ['b', 'c', 'e', 'f'],1543['b', 'd', 'f', 'g'], ['c', 'd', 'e', 'g']]1544"""15451546cdef SetSystem NSC1547NSC = SetSystem(self._E)1548bitset_clear(self._input)1549bitset_set_first_n(self._input, self._matroid_rank)1550cdef long e, f, corank1551corank = self._groundset_size - self._matroid_rank1552repeat = True1553while repeat:1554if self.__is_independent(self._input):1555bitset_copy(self._input2, self._current_basis)1556e = bitset_first(self._current_basis)1557for e in xrange(self._groundset_size):1558if not bitset_in(self._current_basis, e):1559self.__fundamental_circuit(self._output, e)1560if e > bitset_first(self._output):1561bitset_intersection(self._input2, self._input2, self._output)1562f = bitset_first(self._input2)1563while f >= 0:1564self.__fundamental_cocircuit(self._output, f)1565if f == bitset_first(self._output):1566NSC._append(self._output)1567f = bitset_next(self._input2, f + 1)1568repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)1569NSC.resize()1570return NSC15711572cpdef circuits(self):1573"""1574Return the list of circuits of the matroid.15751576OUTPUT:15771578An iterable containing all circuits.15791580.. SEEALSO::15811582:meth:`M.circuit() <sage.matroids.matroid.Matroid.circuit>`15831584EXAMPLES::15851586sage: M = Matroid(matroids.named_matroids.NonFano().bases())1587sage: sorted([sorted(C) for C in M.circuits()])1588[['a', 'b', 'c', 'g'], ['a', 'b', 'd', 'e'], ['a', 'b', 'f'],1589['a', 'c', 'd', 'f'], ['a', 'c', 'e'], ['a', 'd', 'e', 'f'],1590['a', 'd', 'g'], ['a', 'e', 'f', 'g'], ['b', 'c', 'd'],1591['b', 'c', 'e', 'f'], ['b', 'd', 'e', 'f'], ['b', 'd', 'f', 'g'],1592['b', 'e', 'g'], ['c', 'd', 'e', 'f'], ['c', 'd', 'e', 'g'],1593['c', 'f', 'g'], ['d', 'e', 'f', 'g']]1594"""1595cdef SetSystem NSC1596NSC = SetSystem(self._E)1597bitset_clear(self._input)1598bitset_set_first_n(self._input, self._matroid_rank)1599cdef long e, f1600repeat = True1601while repeat:1602if self.__is_independent(self._input):1603bitset_complement(self._input2, self._current_basis)1604e = bitset_first(self._current_basis)1605while e >= 0:1606self.__fundamental_cocircuit(self._output, e)1607if e > bitset_first(self._output):1608bitset_intersection(self._input2, self._input2, self._output)1609e = bitset_next(self._current_basis, e + 1)1610f = bitset_first(self._input2)1611while f >= 0:1612self.__fundamental_circuit(self._output, f)1613if f == bitset_first(self._output):1614NSC._append(self._output)1615f = bitset_next(self._input2, f + 1)1616repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)1617NSC.resize()1618return NSC16191620# isomorphism16211622cpdef _characteristic_setsystem(self):1623r"""1624Return a characteristic set-system for this matroid, on the same1625ground set.16261627OUTPUT:16281629A :class:`<sage.matroids.set_system.SetSystem>` instance.16301631EXAMPLES::16321633sage: M = matroids.named_matroids.N1()1634sage: M._characteristic_setsystem()1635Iterator over a system of subsets1636sage: len(M._characteristic_setsystem())1637231638"""1639if 2 * self._matroid_rank > self._groundset_size:1640return self.nonspanning_circuits()1641else:1642return self.noncospanning_cocircuits()16431644cpdef _weak_invariant(self):1645"""1646Return an isomophism invariant of the matroid.16471648Compared to BasisExchangeMatroid._strong_invariant() this invariant1649distinguishes less frequently between nonisomorphic matroids but takes1650less time to compute. See also1651:meth:`<BasisExchangeMatroid._weak_partition>`.16521653OUTPUT:16541655An integer isomorphism invariant.16561657EXAMPLES::16581659sage: M = Matroid(bases=matroids.named_matroids.Fano().bases())1660sage: N = Matroid(matroids.named_matroids.NonFano().bases())1661sage: M._weak_invariant() == N._weak_invariant()1662False1663"""1664if self._weak_invariant_var is None:1665if self.full_rank() == 0 or self.full_corank() == 0:1666self._weak_invariant_var = 01667self._weak_partition_var = SetSystem(self._E, [self.groundset()])1668else:1669k = min(self.full_rank() - 1, 2)1670fie, f_vec = self._flat_element_inv(k)1671self._weak_invariant_var = hash(tuple([tuple([(f, len(fie[f])) for f in sorted(fie)]), f_vec]))1672self._weak_partition_var = SetSystem(self._E, [fie[f] for f in sorted(fie)])1673return self._weak_invariant_var16741675cpdef _weak_partition(self):1676"""1677Return an ordered partition based on the incidences of elements with1678low-dimensional flats.16791680EXAMPLES::16811682sage: M = Matroid(matroids.named_matroids.Vamos().bases())1683sage: [sorted(p) for p in M._weak_partition()]1684[['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']]1685"""1686self._weak_invariant()1687return self._weak_partition_var16881689cpdef _strong_invariant(self):1690"""1691Return an isomophism invariant of the matroid.16921693Compared to BasisExchangeMatroid._weak_invariant() this invariant1694distinguishes more frequently between nonisomorphic matroids but takes1695more time to compute. See also1696:meth:`<BasisExchangeMatroid._strong_partition>`.16971698OUTPUT:16991700An integer isomorphism invariant.17011702EXAMPLES::17031704sage: M = Matroid(matroids.named_matroids.Fano().bases())1705sage: N = Matroid(matroids.named_matroids.NonFano().bases())1706sage: M._strong_invariant() == N._strong_invariant()1707False1708"""1709if self._strong_invariant_var is None:1710CP = self._characteristic_setsystem()._equitable_partition(self._weak_partition())1711self._strong_partition_var = CP[0]1712self._strong_invariant_var = CP[2]1713return self._strong_invariant_var17141715cpdef _strong_partition(self):1716"""1717Return an equitable partition which refines _weak_partition().17181719EXAMPLES::17201721sage: from sage.matroids.advanced import *1722sage: M = BasisMatroid(matroids.named_matroids.Vamos())1723sage: [sorted(p) for p in M._strong_partition()]1724[['a', 'b', 'e', 'f'], ['c', 'd', 'g', 'h']]1725"""1726self._strong_invariant()1727return self._strong_partition_var17281729cpdef _heuristic_invariant(self):1730"""1731Return a number characteristic for the construction of1732_heuristic_partition().17331734EXAMPLES::17351736sage: from sage.matroids.advanced import *1737sage: M = BasisMatroid(matroids.named_matroids.Vamos())1738sage: N = BasisMatroid(matroids.named_matroids.Vamos())1739sage: M._heuristic_invariant() == N._heuristic_invariant()1740True1741"""1742if self._heuristic_invariant_var is None:1743CP = self._characteristic_setsystem()._heuristic_partition(self._strong_partition())1744self._heuristic_partition_var = CP[0]1745self._heuristic_invariant_var = CP[2]1746return self._heuristic_invariant_var17471748cpdef _heuristic_partition(self):1749"""1750Return an ordered partition into singletons which refines an equitable1751partition of the matroid.17521753The purpose of this partition is to heuristically find an isomorphism1754between two matroids, by lining up their respective1755heuristic_partitions.17561757EXAMPLES::17581759sage: from sage.matroids.advanced import *1760sage: M = BasisMatroid(matroids.named_matroids.Vamos())1761sage: N = BasisMatroid(matroids.named_matroids.Vamos())1762sage: PM = M._heuristic_partition()1763sage: PN = N._heuristic_partition()1764sage: morphism = {}1765sage: for i in xrange(len(M)): morphism[min(PM[i])]=min(PN[i])1766sage: M._is_isomorphism(N, morphism)1767True1768"""1769self._heuristic_invariant()1770return self._heuristic_partition_var17711772cdef _flush(self):1773"""1774Delete all invariants.1775"""1776self._weak_invariant_var = None1777self._strong_invariant_var = None1778self._heuristic_invariant_var = None17791780cpdef _equitable_partition(self, P=None):1781"""1782Return the equitable refinement of a given ordered partition.17831784The coarsest equitable partition of the ground set of this matroid1785that refines P.17861787INPUT:17881789- ``P`` -- (default: ``None``) an ordered partition of the groundset.1790If ``None``, the trivial partition is used.17911792OUTPUT:17931794A SetSystem.17951796EXAMPLES::17971798sage: from sage.matroids.advanced import *1799sage: M = BasisMatroid(matroids.named_matroids.Vamos())1800sage: [sorted(p) for p in M._equitable_partition()]1801[['a', 'b', 'e', 'f'], ['c', 'd', 'g', 'h']]1802sage: [sorted(p) for p in M._equitable_partition(['a', 'bcdefgh'])]1803[['a'], ['b'], ['e', 'f'], ['c', 'd', 'g', 'h']]1804"""1805if P is not None:1806EQ = self._characteristic_setsystem()._equitable_partition(SetSystem(self._E, P))1807else:1808EQ = self._characteristic_setsystem()._equitable_partition()1809return EQ[0]18101811cpdef _is_isomorphism(self, other, morphism):1812"""1813Version of is_isomorphism() that does no type checking.18141815INPUT:18161817- ``other`` -- A matroid instance.1818- ``morphism`` -- a dictionary mapping the groundset of ``self`` to1819the groundset of ``other``18201821OUTPUT:18221823Boolean.18241825EXAMPLES::18261827sage: from sage.matroids.advanced import *1828sage: M = matroids.named_matroids.Pappus()1829sage: N = BasisMatroid(matroids.named_matroids.NonPappus())1830sage: N._is_isomorphism(M, {e:e for e in M.groundset()})1831False18321833sage: M = matroids.named_matroids.Fano() \ ['g']1834sage: N = matroids.Wheel(3)1835sage: morphism = {'a':0, 'b':1, 'c': 2, 'd':4, 'e':5, 'f':3}1836sage: M._is_isomorphism(N, morphism)1837True1838"""1839if not isinstance(other, BasisExchangeMatroid):1840import basis_matroid1841ot = basis_matroid.BasisMatroid(other)1842else:1843ot = other1844return self.__is_isomorphism(ot, morphism)18451846cdef bint __is_isomorphism(self, BasisExchangeMatroid other, morphism):1847"""1848Bitpacked version of ``is_isomorphism``.1849"""1850cdef long i1851morph = [other._idx[morphism[self._E[i]]] for i in xrange(len(self))]1852bitset_clear(self._input)1853bitset_set_first_n(self._input, self._matroid_rank)1854repeat = True1855while repeat:1856bitset_clear(other._input)1857i = bitset_first(self._input)1858while i != -1:1859bitset_add(other._input, morph[i])1860i = bitset_next(self._input, i + 1)1861if self.__is_independent(self._input) != other.__is_independent(other._input):1862return False1863repeat = nxksrd(self._input, self._groundset_size, self._matroid_rank, True)1864return True18651866cpdef _is_isomorphic(self, other):1867"""1868Test if ``self`` is isomorphic to ``other``.18691870Internal version that performs no checks on input.18711872INPUT:18731874- ``other`` -- A matroid.18751876OUTPUT:18771878Boolean.18791880EXAMPLES::18811882sage: from sage.matroids.advanced import *1883sage: M1 = BasisMatroid(matroids.Wheel(3))1884sage: M2 = BasisMatroid(matroids.CompleteGraphic(4))1885sage: M1._is_isomorphic(M2)1886True1887sage: M1 = BasisMatroid(matroids.named_matroids.Fano())1888sage: M2 = BasisMatroid(matroids.named_matroids.NonFano())1889sage: M1._is_isomorphic(M2)1890False18911892"""1893if not isinstance(other, BasisExchangeMatroid):1894return other._is_isomorphic(self)1895# Either generic test, which converts other to BasisMatroid,1896# or overridden method.1897if self is other:1898return True1899if len(self) != len(other):1900return False1901if self.full_rank() != other.full_rank():1902return False1903if self.full_rank() == 0 or self.full_corank() == 0:1904return True1905if self.full_rank() == 1:1906return len(self.loops()) == len(other.loops())1907if self.full_corank() == 1:1908return len(self.coloops()) == len(other.coloops())19091910if self._weak_invariant() != other._weak_invariant():1911return False1912PS = self._weak_partition()1913PO = other._weak_partition()1914if len(PS) == len(self) and len(PO) == len(other):1915morphism = {}1916for i in xrange(len(self)):1917morphism[min(PS[i])] = min(PO[i])1918return self.__is_isomorphism(other, morphism)19191920if self._strong_invariant() != other._strong_invariant():1921return False1922PS = self._strong_partition()1923PO = other._strong_partition()1924if len(PS) == len(self) and len(PO) == len(other):1925morphism = {}1926for i in xrange(len(self)):1927morphism[min(PS[i])] = min(PO[i])1928return self.__is_isomorphism(other, morphism)19291930if self._heuristic_invariant() == other._heuristic_invariant():1931PHS = self._heuristic_partition()1932PHO = other._heuristic_partition()1933morphism = {}1934for i in xrange(len(self)):1935morphism[min(PHS[i])] = min(PHO[i])1936if self._is_isomorphism(other, morphism):1937return True19381939return self._characteristic_setsystem()._isomorphism(other._characteristic_setsystem(), PS, PO) is not None19401941cpdef is_valid(self):1942r"""1943Test if the data obey the matroid axioms.19441945This method checks the basis axioms for the class. If `B` is the set1946of bases of a matroid `M`, then19471948* `B` is nonempty1949* if `X` and `Y` are in `B`, and `x` is in `X - Y`, then there is a1950`y` in `Y - X` such that `(X - x) + y` is again a member of `B`.19511952OUTPUT:19531954Boolean.19551956EXAMPLES::19571958sage: from sage.matroids.advanced import *1959sage: M = BasisMatroid(matroids.named_matroids.Fano())1960sage: M.is_valid()1961True1962sage: M = Matroid(groundset='abcd', bases=['ab', 'cd'])1963sage: M.is_valid()1964False1965"""1966cdef long pointerX, pointerY, x, y, ln1967cdef bint foundpair1968cdef SetSystem BB1969BB = self.bases()1970pointerX = 01971ln = len(BB)1972while pointerX < ln: # for X in BB1973pointerY = 01974while pointerY < ln: # for Y in BB1975# Set current basis to Y1976bitset_difference(self._inside, self._current_basis, BB._subsets[pointerY])1977bitset_difference(self._outside, BB._subsets[pointerY], self._current_basis)1978self.__move(self._inside, self._outside)1979bitset_difference(self._input, BB._subsets[pointerX], BB._subsets[pointerY])1980bitset_difference(self._input2, BB._subsets[pointerY], BB._subsets[pointerX])1981x = bitset_first(self._input)1982while x >= 0: # for x in X-Y1983foundpair = False1984y = bitset_first(self._input2)1985while y >= 0: # for y in Y-X1986if self.__is_exchange_pair(y, x):1987foundpair = True1988y = -11989else:1990y = bitset_next(self._input2, y + 1)1991if not foundpair:1992return False1993x = bitset_next(self._input, x + 1)1994pointerY += 11995pointerX += 11996return True199719981999cdef bint nxksrd(bitset_s* b, long n, long k, bint succ):2000"""2001Next size-k subset of a size-n set in a revolving-door sequence. It will2002cycle through all such sets, returning each set exactly once. Each2003successive set differs from the last in exactly one element.20042005Returns ``True`` if there is a next set, ``False`` otherwise.2006"""2007# next k-subset of n-set in a revolving-door sequence2008if n == k or k == 0:2009return False2010if bitset_in(b, n - 1):2011if nxksrd(b, n - 1, k - 1, not succ):2012return True2013else:2014if succ:2015return False2016else:2017if k == 1:2018bitset_add(b, n - 2)2019else:2020bitset_add(b, k - 2)2021bitset_discard(b, n - 1)2022return True2023else:2024if nxksrd(b, n - 1, k, succ):2025return True2026else:2027if succ:2028if k == 1:2029bitset_discard(b, n - 2)2030else:2031bitset_discard(b, k - 2)2032bitset_add(b, n - 1)2033return True2034else:2035return False203620372038