Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/combinat/integer_matrices.py
8815 views
1
r"""
2
Counting, generating, and manipulating non-negative integer matrices
3
4
Counting, generating, and manipulating non-negative integer matrices with
5
prescribed row sums and column sums.
6
7
AUTHORS:
8
9
- Franco Saliola
10
"""
11
#*****************************************************************************
12
# Copyright (C) 2012 Franco Saliola <[email protected]>
13
#
14
# Distributed under the terms of the GNU General Public License (GPL)
15
# http://www.gnu.org/licenses/
16
#*****************************************************************************
17
18
from sage.structure.unique_representation import UniqueRepresentation
19
from sage.structure.parent import Parent
20
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
21
from sage.combinat.integer_list import IntegerListsLex
22
from sage.matrix.constructor import matrix
23
from sage.rings.integer_ring import ZZ
24
25
class 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
297
def 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 [list(comp)] + mat
338
339