Path: blob/master/src/sage/combinat/integer_matrices.py
8815 views
r"""1Counting, generating, and manipulating non-negative integer matrices23Counting, generating, and manipulating non-negative integer matrices with4prescribed row sums and column sums.56AUTHORS:78- Franco Saliola9"""10#*****************************************************************************11# Copyright (C) 2012 Franco Saliola <[email protected]>12#13# Distributed under the terms of the GNU General Public License (GPL)14# http://www.gnu.org/licenses/15#*****************************************************************************1617from sage.structure.unique_representation import UniqueRepresentation18from sage.structure.parent import Parent19from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets20from sage.combinat.integer_list import IntegerListsLex21from sage.matrix.constructor import matrix22from sage.rings.integer_ring import ZZ2324class IntegerMatrices(UniqueRepresentation, Parent):25r"""26The class of non-negative integer matrices with27prescribed row sums and column sums.2829An *integer matrix* `m` with column sums `c := (c_1,...,c_k)` and row30sums `l := (l_1,...,l_n)` where `c_1+...+c_k` is equal to `l_1+...+l_n`,31is a `n \times k` matrix `m = (m_{i,j})` such that32`m_{1,j}+\dots+m_{n,j} = c_j`, for all `j` and33`m_{i,1}+\dots+m_{i,k} = l_i`, for all `i`.3435EXAMPLES:3637There are `6` integer matrices with row sums `[3,2,2]` and column sums38`[2,5]`::3940sage: from sage.combinat.integer_matrices import IntegerMatrices41sage: IM = IntegerMatrices([3,2,2], [2,5]); IM42Non-negative integer matrices with row sums [3, 2, 2] and column sums [2, 5]43sage: IM.list()44[45[2 1] [1 2] [1 2] [0 3] [0 3] [0 3]46[0 2] [1 1] [0 2] [2 0] [1 1] [0 2]47[0 2], [0 2], [1 1], [0 2], [1 1], [2 0]48]49sage: IM.cardinality()5065152"""53@staticmethod54def __classcall__(cls, row_sums, column_sums):55r"""56Normalize the inputs so that they are hashable.5758INPUT:5960- ``row_sums`` -- list, tuple, or anything defining a Composition61- ``column_sums`` -- list, tuple, or anything defining a Composition6263EXAMPLES::6465sage: from sage.combinat.integer_matrices import IntegerMatrices66sage: IM = IntegerMatrices([4,4,5], [3,7,1,2]); IM67Non-negative integer matrices with row sums [4, 4, 5] and column sums [3, 7, 1, 2]68sage: IM = IntegerMatrices((4,4,5), (3,7,1,2)); IM69Non-negative integer matrices with row sums [4, 4, 5] and column sums [3, 7, 1, 2]70sage: IM = IntegerMatrices(Composition([4,4,5]), Composition([3,7,1,2])); IM71Non-negative integer matrices with row sums [4, 4, 5] and column sums [3, 7, 1, 2]7273"""74from sage.combinat.composition import Composition75row_sums = Composition(row_sums)76column_sums = Composition(column_sums)77return super(IntegerMatrices, cls).__classcall__(cls, row_sums, column_sums)7879def __init__(self, row_sums, column_sums):80r"""81Constructor of this class; for documentation, see82:class:`IntegerMatrices`.8384INPUT:8586- ``row_sums`` -- Composition87- ``column_sums`` -- Composition8889TESTS::9091sage: from sage.combinat.integer_matrices import IntegerMatrices92sage: IM = IntegerMatrices([3,2,2], [2,5]); IM93Non-negative integer matrices with row sums [3, 2, 2] and column sums [2, 5]94sage: TestSuite(IM).run()95"""96self._row_sums = row_sums97self._col_sums = column_sums98Parent.__init__(self, category=FiniteEnumeratedSets())99100def _repr_(self):101r"""102TESTS::103104sage: from sage.combinat.integer_matrices import IntegerMatrices105sage: IntegerMatrices([3,2,2], [2,5])._repr_()106'Non-negative integer matrices with row sums [3, 2, 2] and column sums [2, 5]'107"""108return "Non-negative integer matrices with row sums %s and column sums %s" % \109(self._row_sums, self._col_sums)110111def __iter__(self):112r"""113An iterator for the integer matrices with the prescribed row sums and114columns sums.115116EXAMPLES::117118sage: from sage.combinat.integer_matrices import IntegerMatrices119sage: IntegerMatrices([2,2], [1,2,1]).list()120[121[1 1 0] [1 0 1] [0 2 0] [0 1 1]122[0 1 1], [0 2 0], [1 0 1], [1 1 0]123]124sage: IntegerMatrices([0,0],[0,0,0]).list()125[126[0 0 0]127[0 0 0]128]129sage: IntegerMatrices([1,1],[1,1]).list()130[131[1 0] [0 1]132[0 1], [1 0]133]134135"""136for x in integer_matrices_generator(self._row_sums, self._col_sums):137yield matrix(ZZ, x)138139def __contains__(self, x):140r"""141Tests if ``x`` is an element of ``self``.142143INPUT:144145- ``x`` -- matrix146147EXAMPLES::148149sage: from sage.combinat.integer_matrices import IntegerMatrices150sage: IM = IntegerMatrices([4], [1,2,1])151sage: matrix([[1, 2, 1]]) in IM152True153sage: matrix(QQ, [[1, 2, 1]]) in IM154True155sage: matrix([[2, 1, 1]]) in IM156False157158TESTS::159160sage: from sage.combinat.integer_matrices import IntegerMatrices161sage: IM = IntegerMatrices([4], [1,2,1])162sage: [1, 2, 1] in IM163False164sage: matrix([[-1, 3, 1]]) in IM165False166167"""168from sage.matrix.matrix import Matrix169if not isinstance(x, Matrix):170return False171row_sums = [ZZ.zero()] * x.nrows()172col_sums = [ZZ.zero()] * x.ncols()173for i in range(x.nrows()):174for j in range(x.ncols()):175x_ij = x[i, j]176if x_ij not in ZZ or x_ij < 0:177return False178row_sums[i] += x_ij179col_sums[j] += x_ij180if row_sums[i] != self._row_sums[i]:181return False182if col_sums != self._col_sums:183return False184return True185186def cardinality(self):187r"""188The number of integer matrices with the prescribed row sums and columns189sums.190191EXAMPLES::192193sage: from sage.combinat.integer_matrices import IntegerMatrices194sage: IntegerMatrices([2,5], [3,2,2]).cardinality()1956196sage: IntegerMatrices([1,1,1,1,1], [1,1,1,1,1]).cardinality()197120198sage: IntegerMatrices([2,2,2,2], [2,2,2,2]).cardinality()199282200sage: IntegerMatrices([4], [3]).cardinality()2010202sage: len(IntegerMatrices([0,0], [0]).list())2031204205This method computes the cardinality using symmetric functions. Below206are the same examples, but computed by generating the actual matrices::207208sage: from sage.combinat.integer_matrices import IntegerMatrices209sage: len(IntegerMatrices([2,5], [3,2,2]).list())2106211sage: len(IntegerMatrices([1,1,1,1,1], [1,1,1,1,1]).list())212120213sage: len(IntegerMatrices([2,2,2,2], [2,2,2,2]).list())214282215sage: len(IntegerMatrices([4], [3]).list())2160217sage: len(IntegerMatrices([0], [0]).list())2181219220"""221from sage.combinat.sf.sf import SymmetricFunctions222from sage.combinat.partition import Partition223h = SymmetricFunctions(ZZ).homogeneous()224row_partition = Partition(sorted(self._row_sums, reverse=True))225col_partition = Partition(sorted(self._col_sums, reverse=True))226return h[row_partition].scalar(h[col_partition])227228def row_sums(self):229r"""230The row sums of the integer matrices in ``self``.231232OUTPUT:233234- Composition235236EXAMPLES::237238sage: from sage.combinat.integer_matrices import IntegerMatrices239sage: IM = IntegerMatrices([3,2,2], [2,5])240sage: IM.row_sums()241[3, 2, 2]242"""243return self._row_sums244245def column_sums(self):246r"""247The column sums of the integer matrices in ``self``.248249OUTPUT:250251- Composition252253EXAMPLES::254255sage: from sage.combinat.integer_matrices import IntegerMatrices256sage: IM = IntegerMatrices([3,2,2], [2,5])257sage: IM.column_sums()258[2, 5]259"""260return self._col_sums261262def to_composition(self, x):263r"""264The composition corresponding to the integer matrix.265266This is the composition obtained by reading the entries of the matrix267from left to right along each row, and reading the rows from top to268bottom, ignore zeros.269270INPUT:271272- ``x`` -- matrix273274EXAMPLES::275276sage: from sage.combinat.integer_matrices import IntegerMatrices277sage: IM = IntegerMatrices([3,2,2], [2,5]); IM278Non-negative integer matrices with row sums [3, 2, 2] and column sums [2, 5]279sage: IM.list()280[281[2 1] [1 2] [1 2] [0 3] [0 3] [0 3]282[0 2] [1 1] [0 2] [2 0] [1 1] [0 2]283[0 2], [0 2], [1 1], [0 2], [1 1], [2 0]284]285sage: for m in IM: print IM.to_composition(m)286[2, 1, 2, 2]287[1, 2, 1, 1, 2]288[1, 2, 2, 1, 1]289[3, 2, 2]290[3, 1, 1, 1, 1]291[3, 2, 2]292"""293from sage.combinat.composition import Composition294return Composition([entry for row in x for entry in row if entry != 0])295296def integer_matrices_generator(row_sums, column_sums):297r"""298Recursively generate the integer matrices with the prescribed row sums and299column sums.300301INPUT:302303- ``row_sums`` -- list or tuple304- ``column_sums`` -- list or tuple305306OUTPUT:307308- an iterator producing a list of lists309310EXAMPLES::311312sage: from sage.combinat.integer_matrices import integer_matrices_generator313sage: iter = integer_matrices_generator([3,2,2], [2,5]); iter314<generator object integer_matrices_generator at ...>315sage: for m in iter: print m316[[2, 1], [0, 2], [0, 2]]317[[1, 2], [1, 1], [0, 2]]318[[1, 2], [0, 2], [1, 1]]319[[0, 3], [2, 0], [0, 2]]320[[0, 3], [1, 1], [1, 1]]321[[0, 3], [0, 2], [2, 0]]322323"""324row_sums = list(row_sums)325column_sums = list(column_sums)326if sum(row_sums) != sum(column_sums):327raise StopIteration328if len(row_sums) == 0:329yield []330elif len(row_sums) == 1:331yield [column_sums]332else:333for comp in IntegerListsLex(row_sums[0], len(column_sums), ceiling=column_sums):334t = [column_sums[i]-ci for (i, ci) in enumerate(comp)]335for mat in integer_matrices_generator(row_sums[1:], t):336yield [list(comp)] + mat337338339