Path: blob/master/src/sage/combinat/cartesian_product.py
8817 views
r"""1Cartesian Products2"""3#*****************************************************************************4# Copyright (C) 2007 Mike Hansen <[email protected]>,5#6# Distributed under the terms of the GNU General Public License (GPL)7#8# This code is distributed in the hope that it will be useful,9# but WITHOUT ANY WARRANTY; without even the implied warranty of10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU11# General Public License for more details.12#13# The full text of the GPL is available at:14#15# http://www.gnu.org/licenses/16#*****************************************************************************1718from inspect import isgenerator19import sage.misc.prandom as rnd20import __builtin__21from combinat import CombinatorialClass22from sage.rings.integer import Integer23from sage.rings.infinity import infinity24from sage.misc.mrange import xmrange_iter, _is_finite, _len2526def CartesianProduct(*iters):27"""28Returns the combinatorial class of the Cartesian product of29\*iters.3031EXAMPLES::3233sage: cp = CartesianProduct([1,2], [3,4]); cp34Cartesian product of [1, 2], [3, 4]35sage: cp.list()36[[1, 3], [1, 4], [2, 3], [2, 4]]3738Note that you must not use a generator-type object that is39returned by a function (using "yield"). They cannot be copied or40rewound (you cannot jump back to the beginning), but this is41necessary to construct the cartesian product::4243sage: def a(n): yield 1*n; yield 2*n44sage: def b(): yield 'a'; yield 'b'45sage: CartesianProduct(a(3), b()).list()46Traceback (most recent call last):47...48ValueError: generators are not allowed, see the49documentation (type "CartesianProduct?") for a workaround5051You either create a list of all values or you use52:class:`sage.combinat.misc.IterableFunctionCall` to make a53(copy-able) iterator::5455sage: from sage.combinat.misc import IterableFunctionCall56sage: CartesianProduct(IterableFunctionCall(a, 3), IterableFunctionCall(b)).list()57[[3, 'a'], [3, 'b'], [6, 'a'], [6, 'b']]5859See the documentation for60:class:`~sage.combinat.misc.IterableFunctionCall` for more61information.62"""63if any(isgenerator(i) for i in iters):64raise ValueError('generators are not allowed, see the documentation '+65'(type "CartesianProduct?") for a workaround')66return CartesianProduct_iters(*iters)6768class CartesianProduct_iters(CombinatorialClass):69def __init__(self, *iters):70"""71TESTS::7273sage: import sage.combinat.cartesian_product as cartesian_product74sage: cp = cartesian_product.CartesianProduct_iters([1,2],[3,4]); cp75Cartesian product of [1, 2], [3, 4]76sage: loads(dumps(cp)) == cp77True78"""79self.iters = iters80self._mrange = xmrange_iter(iters)81CombinatorialClass.__init__(self)8283def __contains__(self, x):84"""85EXAMPLES::8687sage: cp = CartesianProduct([1,2],[3,4])88sage: [1,3] in cp89True90sage: [1,2] in cp91False92sage: [1, 3, 1] in cp93False94"""95try:96return len(x) == len(self.iters) and all(x[i] in self.iters[i] for i in range(len(self.iters)))97except (TypeError, IndexError):98return False99100def __repr__(self):101"""102EXAMPLES::103104sage: CartesianProduct(range(2), range(3))105Cartesian product of [0, 1], [0, 1, 2]106"""107return "Cartesian product of " + ", ".join(map(str, self.iters))108109def cardinality(self):110"""111Returns the number of elements in the cartesian product of112everything in \*iters.113114EXAMPLES::115116sage: CartesianProduct(range(2), range(3)).cardinality()1176118sage: CartesianProduct(range(2), xrange(3)).cardinality()1196120sage: CartesianProduct(range(2), xrange(3), xrange(4)).cardinality()12124122123This works correctly for infinite objects::124125sage: CartesianProduct(ZZ, QQ).cardinality()126+Infinity127sage: CartesianProduct(ZZ, []).cardinality()1280129"""130return self._mrange.cardinality()131132def __len__(self):133"""134Return the number of elements of the cartesian product.135136OUTPUT:137138An ``int``, the number of elements in the cartesian product. If the139number of elements is infinite or does not fit into a python ``int``, a140``TypeError`` is raised.141142.. SEEALSO::143144:meth:`cardinality`145146EXAMPLES::147148sage: C = CartesianProduct(xrange(3), xrange(4))149sage: len(C)15012151sage: C = CartesianProduct(ZZ, QQ)152sage: len(C)153Traceback (most recent call last):154...155TypeError: cardinality does not fit into a Python int.156sage: C = CartesianProduct(ZZ, [])157sage: len(C)1580159"""160return self._mrange.__len__()161162def list(self):163"""164Returns165166EXAMPLES::167168sage: CartesianProduct(range(3), range(3)).list()169[[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]170sage: CartesianProduct('dog', 'cat').list()171[['d', 'c'],172['d', 'a'],173['d', 't'],174['o', 'c'],175['o', 'a'],176['o', 't'],177['g', 'c'],178['g', 'a'],179['g', 't']]180"""181return [e for e in self]182183184def __iter__(self):185"""186An iterator for the elements in the cartesian product of the187iterables \*iters.188189From Recipe 19.9 in the Python Cookbook by Alex Martelli and David190Ascher.191192EXAMPLES::193194sage: [e for e in CartesianProduct(range(3), range(3))]195[[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]196sage: [e for e in CartesianProduct('dog', 'cat')]197[['d', 'c'],198['d', 'a'],199['d', 't'],200['o', 'c'],201['o', 'a'],202['o', 't'],203['g', 'c'],204['g', 'a'],205['g', 't']]206"""207return self._mrange.__iter__()208209def is_finite(self):210"""211The cartesian product is finite if all of its inputs are212finite, or if any input is empty.213214EXAMPLES::215216sage: CartesianProduct(ZZ, []).is_finite()217True218sage: CartesianProduct(4,4).is_finite()219Traceback (most recent call last):220...221ValueError: Unable to determine whether this product is finite222"""223finites = [_is_finite(L, fallback=None) for L in self.iters]224if any(f is None for f in finites):225raise ValueError("Unable to determine whether this product is finite")226if all(f is True for f in finites):227return True228lens = [_len(L) for L in self.iters]229if any(l == 0 for l in lens):230return True231return False232233def unrank(self, x):234"""235For finite cartesian products, we can reduce unrank to the236constituent iterators.237238EXAMPLES::239240sage: C = CartesianProduct(xrange(1000), xrange(1000), xrange(1000))241sage: C[238792368]242[238, 792, 368]243"""244try:245lens = [_len(it) for it in self.iters]246except (TypeError, AttributeError):247return CartesianProduct_iters.unrank(self, x)248positions = []249for n in lens:250if n is infinity:251return CartesianProduct_iters.unrank(self, x)252if n == 0:253raise IndexError("Cartesian Product is empty")254positions.append(x % n)255x = x // n256if x != 0:257raise IndexError("x larger than the size of the Cartesian Product")258positions.reverse()259return [L[i] for L,i in zip(self.iters, positions)]260261def random_element(self):262"""263Returns a random element from the cartesian product of \*iters.264265EXAMPLES::266267sage: CartesianProduct('dog', 'cat').random_element()268['d', 'a']269"""270return list(map(rnd.choice, self.iters))271272273