Ticket #13238: trac_13238-integer_matrices-fs.patch

File trac_13238-integer_matrices-fs.patch, 12.1 KB (added by saliola, 11 years ago)
  • doc/en/reference/combinat/index.rst

    # HG changeset patch
    # User Franco Saliola <saliola@gmail.com>
    # Date 1342117977 14400
    # Node ID b5c702765315285ee0645fd2bef8ce4820445ac0
    # Parent  59e1cdb4125ac165f75c4507fc1d2b2ac59bcb1b
    #13238: counting, generating, and manipulating non-negative integer matrices
    
    diff --git a/doc/en/reference/combinat/index.rst b/doc/en/reference/combinat/index.rst
    a b Combinatorics 
    2222   ../sage/combinat/e_one_star
    2323   ../sage/combinat/finite_class
    2424   ../sage/combinat/integer_list
     25   ../sage/combinat/integer_matrices
    2526   ../sage/combinat/integer_vector
    2627   ../sage/combinat/integer_vector_weighted
    2728   ../sage/combinat/integer_vectors_mod_permgroup
  • new file sage/combinat/integer_matrices.py

    diff --git a/sage/combinat/integer_matrices.py b/sage/combinat/integer_matrices.py
    new file mode 100644
    - +  
     1r"""
     2Counting, generating, and manipulating non-negative integer matrices
     3
     4Counting, generating, and manipulating non-negative integer matrices with
     5prescribed row sums and column sums.
     6
     7AUTHORS:
     8
     9- Franco Saliola
     10"""
     11#*****************************************************************************
     12#       Copyright (C) 2012 Franco Saliola <saliola@gmail.com>
     13#
     14#  Distributed under the terms of the GNU General Public License (GPL)
     15#                  http://www.gnu.org/licenses/
     16#*****************************************************************************
     17
     18from sage.structure.unique_representation import UniqueRepresentation
     19from sage.structure.parent import Parent
     20from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     21from sage.combinat.integer_list import IntegerListsLex
     22from sage.matrix.constructor import matrix
     23from sage.rings.integer_ring import ZZ
     24
     25class IntegerMatrices(UniqueRepresentation, Parent):
     26    r"""
     27    The class of non-negative integer matrices with
     28    prescribed row sums and column sums.
     29
     30    An *integer matrix* `m` with column sums `c := (c_1,...,c_k)` and row
     31    sums `l := (l_1,...,l_n)` where `c_1+...+c_k` is equal to `l_1+...+l_n`,
     32    is a `n \times k` matrix `m = (m_{i,j})` such that
     33    `m_{1,j}+\dots+m_{n,j} = c_j`, for all `j` and
     34    `m_{i,1}+\dots+m_{i,k} = l_i`, for all `i`.
     35
     36    EXAMPLES:
     37
     38    There are `6` integer matrices with row sums `[3,2,2]` and column sums
     39    `[2,5]`::
     40
     41        sage: from sage.combinat.integer_matrices import IntegerMatrices
     42        sage: IM = IntegerMatrices([3,2,2], [2,5]); IM
     43        Non-negative integer matrices with row sums [3, 2, 2] and column sums [2, 5]
     44        sage: IM.list()
     45        [
     46        [2 1]  [1 2]  [1 2]  [0 3]  [0 3]  [0 3]
     47        [0 2]  [1 1]  [0 2]  [2 0]  [1 1]  [0 2]
     48        [0 2], [0 2], [1 1], [0 2], [1 1], [2 0]
     49        ]
     50        sage: IM.cardinality()
     51        6
     52
     53    """
     54    @staticmethod
     55    def __classcall__(cls, row_sums, column_sums):
     56        r"""
     57        Normalize the inputs so that they are hashable.
     58
     59        INPUT:
     60
     61        - ``row_sums`` -- list, tuple, or anything defining a Composition
     62        - ``column_sums`` -- list, tuple, or anything defining a Composition
     63
     64        EXAMPLES::
     65
     66            sage: from sage.combinat.integer_matrices import IntegerMatrices
     67            sage: IM = IntegerMatrices([4,4,5], [3,7,1,2]); IM
     68            Non-negative integer matrices with row sums [4, 4, 5] and column sums [3, 7, 1, 2]
     69            sage: IM = IntegerMatrices((4,4,5), (3,7,1,2)); IM
     70            Non-negative integer matrices with row sums [4, 4, 5] and column sums [3, 7, 1, 2]
     71            sage: IM = IntegerMatrices(Composition([4,4,5]), Composition([3,7,1,2])); IM
     72            Non-negative integer matrices with row sums [4, 4, 5] and column sums [3, 7, 1, 2]
     73
     74        """
     75        from sage.combinat.composition import Composition
     76        row_sums = Composition(row_sums)
     77        column_sums = Composition(column_sums)
     78        return super(IntegerMatrices, cls).__classcall__(cls, row_sums, column_sums)
     79
     80    def __init__(self, row_sums, column_sums):
     81        r"""
     82        Constructor of this class; for documentation, see
     83        :class:`IntegerMatrices`.
     84
     85        INPUT:
     86
     87        - ``row_sums`` -- Composition
     88        - ``column_sums`` -- Composition
     89
     90        TESTS::
     91
     92            sage: from sage.combinat.integer_matrices import IntegerMatrices
     93            sage: IM = IntegerMatrices([3,2,2], [2,5]); IM
     94            Non-negative integer matrices with row sums [3, 2, 2] and column sums [2, 5]
     95            sage: TestSuite(IM).run()
     96        """
     97        self._row_sums = row_sums
     98        self._col_sums = column_sums
     99        Parent.__init__(self, category=FiniteEnumeratedSets())
     100
     101    def _repr_(self):
     102        r"""
     103        TESTS::
     104
     105            sage: from sage.combinat.integer_matrices import IntegerMatrices
     106            sage: IntegerMatrices([3,2,2], [2,5])._repr_()
     107            'Non-negative integer matrices with row sums [3, 2, 2] and column sums [2, 5]'
     108        """
     109        return "Non-negative integer matrices with row sums %s and column sums %s" % \
     110                        (self._row_sums, self._col_sums)
     111
     112    def __iter__(self):
     113        r"""
     114        An iterator for the integer matrices with the prescribed row sums and
     115        columns sums.
     116
     117        EXAMPLES::
     118
     119            sage: from sage.combinat.integer_matrices import IntegerMatrices
     120            sage: IntegerMatrices([2,2], [1,2,1]).list()
     121            [
     122            [1 1 0]  [1 0 1]  [0 2 0]  [0 1 1]
     123            [0 1 1], [0 2 0], [1 0 1], [1 1 0]
     124            ]
     125            sage: IntegerMatrices([0,0],[0,0,0]).list()
     126            [
     127            [0 0 0]
     128            [0 0 0]
     129            ]
     130            sage: IntegerMatrices([1,1],[1,1]).list()
     131            [
     132            [1 0]  [0 1]
     133            [0 1], [1 0]
     134            ]
     135
     136        """
     137        for x in integer_matrices_generator(self._row_sums, self._col_sums):
     138            yield matrix(ZZ, x)
     139
     140    def __contains__(self, x):
     141        r"""
     142        Tests if ``x`` is an element of ``self``.
     143
     144        INPUT:
     145
     146        - ``x`` -- matrix
     147
     148        EXAMPLES::
     149
     150            sage: from sage.combinat.integer_matrices import IntegerMatrices
     151            sage: IM = IntegerMatrices([4], [1,2,1])
     152            sage: matrix([[1, 2, 1]]) in IM
     153            True
     154            sage: matrix(QQ, [[1, 2, 1]]) in IM
     155            True
     156            sage: matrix([[2, 1, 1]]) in IM
     157            False
     158
     159        TESTS::
     160
     161            sage: from sage.combinat.integer_matrices import IntegerMatrices
     162            sage: IM = IntegerMatrices([4], [1,2,1])
     163            sage: [1, 2, 1] in IM
     164            False
     165            sage: matrix([[-1, 3, 1]]) in IM
     166            False
     167
     168        """
     169        from sage.matrix.matrix import Matrix
     170        if not isinstance(x, Matrix):
     171            return False
     172        row_sums = [ZZ.zero()] * x.nrows()
     173        col_sums = [ZZ.zero()] * x.ncols()
     174        for i in range(x.nrows()):
     175            for j in range(x.ncols()):
     176                x_ij = x[i, j]
     177                if x_ij not in ZZ or x_ij < 0:
     178                    return False
     179                row_sums[i] += x_ij
     180                col_sums[j] += x_ij
     181            if row_sums[i] != self._row_sums[i]:
     182                return False
     183        if col_sums != self._col_sums:
     184            return False
     185        return True
     186
     187    def cardinality(self):
     188        r"""
     189        The number of integer matrices with the prescribed row sums and columns
     190        sums.
     191
     192        EXAMPLES::
     193
     194            sage: from sage.combinat.integer_matrices import IntegerMatrices
     195            sage: IntegerMatrices([2,5], [3,2,2]).cardinality()
     196            6
     197            sage: IntegerMatrices([1,1,1,1,1], [1,1,1,1,1]).cardinality()
     198            120
     199            sage: IntegerMatrices([2,2,2,2], [2,2,2,2]).cardinality()
     200            282
     201            sage: IntegerMatrices([4], [3]).cardinality()
     202            0
     203            sage: len(IntegerMatrices([0,0], [0]).list())
     204            1
     205
     206        This method computes the cardinality using symmetric functions. Below
     207        are the same examples, but computed by generating the actual matrices::
     208
     209            sage: from sage.combinat.integer_matrices import IntegerMatrices
     210            sage: len(IntegerMatrices([2,5], [3,2,2]).list())
     211            6
     212            sage: len(IntegerMatrices([1,1,1,1,1], [1,1,1,1,1]).list())
     213            120
     214            sage: len(IntegerMatrices([2,2,2,2], [2,2,2,2]).list())
     215            282
     216            sage: len(IntegerMatrices([4], [3]).list())
     217            0
     218            sage: len(IntegerMatrices([0], [0]).list())
     219            1
     220
     221        """
     222        from sage.combinat.sf.sf import SymmetricFunctions
     223        from sage.combinat.partition import Partition
     224        h = SymmetricFunctions(ZZ).homogeneous()
     225        row_partition = Partition(sorted(self._row_sums, reverse=True))
     226        col_partition = Partition(sorted(self._col_sums, reverse=True))
     227        return h[row_partition].scalar(h[col_partition])
     228
     229    def row_sums(self):
     230        r"""
     231        The row sums of the integer matrices in ``self``.
     232
     233        OUTPUT:
     234
     235        - Composition
     236
     237        EXAMPLES::
     238
     239            sage: from sage.combinat.integer_matrices import IntegerMatrices
     240            sage: IM = IntegerMatrices([3,2,2], [2,5])
     241            sage: IM.row_sums()
     242            [3, 2, 2]
     243        """
     244        return self._row_sums
     245
     246    def column_sums(self):
     247        r"""
     248        The column sums of the integer matrices in ``self``.
     249
     250        OUTPUT:
     251
     252        - Composition
     253
     254        EXAMPLES::
     255
     256            sage: from sage.combinat.integer_matrices import IntegerMatrices
     257            sage: IM = IntegerMatrices([3,2,2], [2,5])
     258            sage: IM.column_sums()
     259            [2, 5]
     260        """
     261        return self._col_sums
     262
     263    def to_composition(self, x):
     264        r"""
     265        The composition corresponding to the integer matrix.
     266
     267        This is the composition obtained by reading the entries of the matrix
     268        from left to right along each row, and reading the rows from top to
     269        bottom, ignore zeros.
     270
     271        INPUT:
     272
     273        - ``x`` -- matrix
     274
     275        EXAMPLES::
     276
     277            sage: from sage.combinat.integer_matrices import IntegerMatrices
     278            sage: IM = IntegerMatrices([3,2,2], [2,5]); IM
     279            Non-negative integer matrices with row sums [3, 2, 2] and column sums [2, 5]
     280            sage: IM.list()
     281            [
     282            [2 1]  [1 2]  [1 2]  [0 3]  [0 3]  [0 3]
     283            [0 2]  [1 1]  [0 2]  [2 0]  [1 1]  [0 2]
     284            [0 2], [0 2], [1 1], [0 2], [1 1], [2 0]
     285            ]
     286            sage: for m in IM: print IM.to_composition(m)
     287            [2, 1, 2, 2]
     288            [1, 2, 1, 1, 2]
     289            [1, 2, 2, 1, 1]
     290            [3, 2, 2]
     291            [3, 1, 1, 1, 1]
     292            [3, 2, 2]
     293        """
     294        from sage.combinat.composition import Composition
     295        return Composition([entry for row in x for entry in row if entry != 0])
     296
     297def integer_matrices_generator(row_sums, column_sums):
     298    r"""
     299    Recursively generate the integer matrices with the prescribed row sums and
     300    column sums.
     301
     302    INPUT:
     303
     304    - ``row_sums`` -- list or tuple
     305    - ``column_sums`` -- list or tuple
     306
     307    OUTPUT:
     308
     309    - an iterator producing a list of lists
     310
     311    EXAMPLES::
     312
     313        sage: from sage.combinat.integer_matrices import integer_matrices_generator
     314        sage: iter = integer_matrices_generator([3,2,2], [2,5]); iter
     315        <generator object integer_matrices_generator at ...>
     316        sage: for m in iter: print m
     317        [[2, 1], [0, 2], [0, 2]]
     318        [[1, 2], [1, 1], [0, 2]]
     319        [[1, 2], [0, 2], [1, 1]]
     320        [[0, 3], [2, 0], [0, 2]]
     321        [[0, 3], [1, 1], [1, 1]]
     322        [[0, 3], [0, 2], [2, 0]]
     323
     324    """
     325    row_sums = list(row_sums)
     326    column_sums = list(column_sums)
     327    if sum(row_sums) != sum(column_sums):
     328        raise StopIteration
     329    if len(row_sums) == 0:
     330        yield []
     331    elif len(row_sums) == 1:
     332        yield [column_sums]
     333    else:
     334        for comp in IntegerListsLex(row_sums[0], len(column_sums), ceiling=column_sums):
     335            t = [column_sums[i]-ci for (i, ci) in enumerate(comp)]
     336            for mat in integer_matrices_generator(row_sums[1:], t):
     337                yield [comp] + mat