Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/combinat/cartesian_product.py
8817 views
1
r"""
2
Cartesian Products
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2007 Mike Hansen <[email protected]>,
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
#
9
# This code is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
# General Public License for more details.
13
#
14
# The full text of the GPL is available at:
15
#
16
# http://www.gnu.org/licenses/
17
#*****************************************************************************
18
19
from inspect import isgenerator
20
import sage.misc.prandom as rnd
21
import __builtin__
22
from combinat import CombinatorialClass
23
from sage.rings.integer import Integer
24
from sage.rings.infinity import infinity
25
from sage.misc.mrange import xmrange_iter, _is_finite, _len
26
27
def CartesianProduct(*iters):
28
"""
29
Returns the combinatorial class of the Cartesian product of
30
\*iters.
31
32
EXAMPLES::
33
34
sage: cp = CartesianProduct([1,2], [3,4]); cp
35
Cartesian product of [1, 2], [3, 4]
36
sage: cp.list()
37
[[1, 3], [1, 4], [2, 3], [2, 4]]
38
39
Note that you must not use a generator-type object that is
40
returned by a function (using "yield"). They cannot be copied or
41
rewound (you cannot jump back to the beginning), but this is
42
necessary to construct the cartesian product::
43
44
sage: def a(n): yield 1*n; yield 2*n
45
sage: def b(): yield 'a'; yield 'b'
46
sage: CartesianProduct(a(3), b()).list()
47
Traceback (most recent call last):
48
...
49
ValueError: generators are not allowed, see the
50
documentation (type "CartesianProduct?") for a workaround
51
52
You either create a list of all values or you use
53
:class:`sage.combinat.misc.IterableFunctionCall` to make a
54
(copy-able) iterator::
55
56
sage: from sage.combinat.misc import IterableFunctionCall
57
sage: CartesianProduct(IterableFunctionCall(a, 3), IterableFunctionCall(b)).list()
58
[[3, 'a'], [3, 'b'], [6, 'a'], [6, 'b']]
59
60
See the documentation for
61
:class:`~sage.combinat.misc.IterableFunctionCall` for more
62
information.
63
"""
64
if any(isgenerator(i) for i in iters):
65
raise ValueError('generators are not allowed, see the documentation '+
66
'(type "CartesianProduct?") for a workaround')
67
return CartesianProduct_iters(*iters)
68
69
class CartesianProduct_iters(CombinatorialClass):
70
def __init__(self, *iters):
71
"""
72
TESTS::
73
74
sage: import sage.combinat.cartesian_product as cartesian_product
75
sage: cp = cartesian_product.CartesianProduct_iters([1,2],[3,4]); cp
76
Cartesian product of [1, 2], [3, 4]
77
sage: loads(dumps(cp)) == cp
78
True
79
"""
80
self.iters = iters
81
self._mrange = xmrange_iter(iters)
82
CombinatorialClass.__init__(self)
83
84
def __contains__(self, x):
85
"""
86
EXAMPLES::
87
88
sage: cp = CartesianProduct([1,2],[3,4])
89
sage: [1,3] in cp
90
True
91
sage: [1,2] in cp
92
False
93
sage: [1, 3, 1] in cp
94
False
95
"""
96
try:
97
return len(x) == len(self.iters) and all(x[i] in self.iters[i] for i in range(len(self.iters)))
98
except (TypeError, IndexError):
99
return False
100
101
def __repr__(self):
102
"""
103
EXAMPLES::
104
105
sage: CartesianProduct(range(2), range(3))
106
Cartesian product of [0, 1], [0, 1, 2]
107
"""
108
return "Cartesian product of " + ", ".join(map(str, self.iters))
109
110
def cardinality(self):
111
"""
112
Returns the number of elements in the cartesian product of
113
everything in \*iters.
114
115
EXAMPLES::
116
117
sage: CartesianProduct(range(2), range(3)).cardinality()
118
6
119
sage: CartesianProduct(range(2), xrange(3)).cardinality()
120
6
121
sage: CartesianProduct(range(2), xrange(3), xrange(4)).cardinality()
122
24
123
124
This works correctly for infinite objects::
125
126
sage: CartesianProduct(ZZ, QQ).cardinality()
127
+Infinity
128
sage: CartesianProduct(ZZ, []).cardinality()
129
0
130
"""
131
return self._mrange.cardinality()
132
133
def __len__(self):
134
"""
135
Return the number of elements of the cartesian product.
136
137
OUTPUT:
138
139
An ``int``, the number of elements in the cartesian product. If the
140
number of elements is infinite or does not fit into a python ``int``, a
141
``TypeError`` is raised.
142
143
.. SEEALSO::
144
145
:meth:`cardinality`
146
147
EXAMPLES::
148
149
sage: C = CartesianProduct(xrange(3), xrange(4))
150
sage: len(C)
151
12
152
sage: C = CartesianProduct(ZZ, QQ)
153
sage: len(C)
154
Traceback (most recent call last):
155
...
156
TypeError: cardinality does not fit into a Python int.
157
sage: C = CartesianProduct(ZZ, [])
158
sage: len(C)
159
0
160
"""
161
return self._mrange.__len__()
162
163
def list(self):
164
"""
165
Returns
166
167
EXAMPLES::
168
169
sage: CartesianProduct(range(3), range(3)).list()
170
[[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]
171
sage: CartesianProduct('dog', 'cat').list()
172
[['d', 'c'],
173
['d', 'a'],
174
['d', 't'],
175
['o', 'c'],
176
['o', 'a'],
177
['o', 't'],
178
['g', 'c'],
179
['g', 'a'],
180
['g', 't']]
181
"""
182
return [e for e in self]
183
184
185
def __iter__(self):
186
"""
187
An iterator for the elements in the cartesian product of the
188
iterables \*iters.
189
190
From Recipe 19.9 in the Python Cookbook by Alex Martelli and David
191
Ascher.
192
193
EXAMPLES::
194
195
sage: [e for e in CartesianProduct(range(3), range(3))]
196
[[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]
197
sage: [e for e in CartesianProduct('dog', 'cat')]
198
[['d', 'c'],
199
['d', 'a'],
200
['d', 't'],
201
['o', 'c'],
202
['o', 'a'],
203
['o', 't'],
204
['g', 'c'],
205
['g', 'a'],
206
['g', 't']]
207
"""
208
return self._mrange.__iter__()
209
210
def is_finite(self):
211
"""
212
The cartesian product is finite if all of its inputs are
213
finite, or if any input is empty.
214
215
EXAMPLES::
216
217
sage: CartesianProduct(ZZ, []).is_finite()
218
True
219
sage: CartesianProduct(4,4).is_finite()
220
Traceback (most recent call last):
221
...
222
ValueError: Unable to determine whether this product is finite
223
"""
224
finites = [_is_finite(L, fallback=None) for L in self.iters]
225
if any(f is None for f in finites):
226
raise ValueError("Unable to determine whether this product is finite")
227
if all(f is True for f in finites):
228
return True
229
lens = [_len(L) for L in self.iters]
230
if any(l == 0 for l in lens):
231
return True
232
return False
233
234
def unrank(self, x):
235
"""
236
For finite cartesian products, we can reduce unrank to the
237
constituent iterators.
238
239
EXAMPLES::
240
241
sage: C = CartesianProduct(xrange(1000), xrange(1000), xrange(1000))
242
sage: C[238792368]
243
[238, 792, 368]
244
"""
245
try:
246
lens = [_len(it) for it in self.iters]
247
except (TypeError, AttributeError):
248
return CartesianProduct_iters.unrank(self, x)
249
positions = []
250
for n in lens:
251
if n is infinity:
252
return CartesianProduct_iters.unrank(self, x)
253
if n == 0:
254
raise IndexError("Cartesian Product is empty")
255
positions.append(x % n)
256
x = x // n
257
if x != 0:
258
raise IndexError("x larger than the size of the Cartesian Product")
259
positions.reverse()
260
return [L[i] for L,i in zip(self.iters, positions)]
261
262
def random_element(self):
263
"""
264
Returns a random element from the cartesian product of \*iters.
265
266
EXAMPLES::
267
268
sage: CartesianProduct('dog', 'cat').random_element()
269
['d', 'a']
270
"""
271
return list(map(rnd.choice, self.iters))
272
273