Skip to content

Commit 9490e5e

Browse files
Release Managervbraun
Release Manager
authored andcommitted
Trac #15919: unrank via R[i] conflicts with notation for constructing polynomial rings in CartesianProduct
{{{ FF = IntegerModRing(29) tester = FF._tester() list(tester.some_elements(CartesianProduct(FF, FF, FF))) }}} This should give a list of 3-arrays of elements of Z/29. Instead I get: {{{ sage: FF = IntegerModRing(29) sage: tester = FF._tester() sage: list(tester.some_elements(CartesianProduct(FF, FF, FF))) ------------------------------------------------------------------------ --- ValueError Traceback (most recent call last) <ipython-input-3-5d7e71a13340> in <module>() ----> 1 list(tester.some_elements(CartesianProduct(FF, FF, FF))) /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/misc/sage_unittest.pyc in some_elements(self, S) 498 if n > self._max_runs: 499 from random import sample --> 500 S = sample(S, self._max_runs) 501 except (TypeError, AttributeError): 502 # We already can't tell what the length of n is, so /home/darij/gitsage/sage-5.13.beta1/local/lib/python/random.pyc in sample(self, population, k) 342 j = _int(random() * n) 343 selected_add(j) --> 344 result[i] = population[j] 345 except (TypeError, KeyError): # handle (at least) sets 346 if isinstance(population, list): /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/combinat/combinat.pyc in __getitem__(self, i) 1220 ValueError: the value must be between 0 and 2 inclusive 1221 """ -> 1222 return self.unrank(i) 1223 1224 def __str__(self): /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/combinat/cartesian_product.pyc in unrank(self, x) 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): /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/structure/parent.so in sage.structure.parent.Parent.__getitem__ (sage/structure/parent.c:11060)() /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/categories/rings.pyc in __getitem__(self, arg) 859 860 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing --> 861 return PolynomialRing(self, elts) 862 863 class ElementMethods: /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/rings/polynomial/polynomial_ring_constructor.pyc in PolynomialRing(base_ring, arg1, arg2, sparse, order, names, name, var_array, implementation) 467 raise TypeError("if second arguments is a string with no commas, then there must be no other non-optional arguments") 468 name = arg1 --> 469 R = _single_variate(base_ring, name, sparse, implementation) 470 else: 471 # 2-4. PolynomialRing(base_ring, names, order='degrevlex'): /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/rings/polynomial/polynomial_ring_constructor.pyc in _single_variate(base_ring, name, sparse, implementation) 518 def _single_variate(base_ring, name, sparse, implementation): 519 import sage.rings.polynomial.polynomial_ring as m --> 520 name = normalize_names(1, name) 521 key = (base_ring, name, sparse, implementation if not sparse else None) 522 R = _get_from_cache(key) /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/structure/parent_gens.so in sage.structure.parent_gens.normalize_names (sage/structure/parent_gens.c:2759)() /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/structure/parent_gens.so in sage.structure.parent_gens._certify_names (sage/structure/parent_gens.c:2322)() ValueError: first letter of variable name must be a letter }}} And 29 is the smallest number where this seems to throw this error; also, it doesn't fail if I only take the cartesian product of two copies of the field. I get a similar error if I do `CartesianProduct(FF, FF).unrank(3)`, and this has been around for a while (not just since beta4): {{{ sage: FF = IntegerModRing(3) sage: CartesianProduct(FF, FF).unrank(3) ------------------------------------------------------------------------ --- ValueError Traceback (most recent call last) <ipython-input-15-98d0a571cc61> in <module>() ----> 1 CartesianProduct(FF, FF).unrank(Integer(3)) /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/combinat/cartesian_product.pyc in unrank(self, x) 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): /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/structure/parent.so in sage.structure.parent.Parent.__getitem__ (sage/structure/parent.c:11060)() /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/categories/rings.pyc in __getitem__(self, arg) 859 860 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing --> 861 return PolynomialRing(self, elts) 862 863 class ElementMethods: /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/rings/polynomial/polynomial_ring_constructor.pyc in PolynomialRing(base_ring, arg1, arg2, sparse, order, names, name, var_array, implementation) 467 raise TypeError("if second arguments is a string with no commas, then there must be no other non-optional arguments") 468 name = arg1 --> 469 R = _single_variate(base_ring, name, sparse, implementation) 470 else: 471 # 2-4. PolynomialRing(base_ring, names, order='degrevlex'): /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/rings/polynomial/polynomial_ring_constructor.pyc in _single_variate(base_ring, name, sparse, implementation) 518 def _single_variate(base_ring, name, sparse, implementation): 519 import sage.rings.polynomial.polynomial_ring as m --> 520 name = normalize_names(1, name) 521 key = (base_ring, name, sparse, implementation if not sparse else None) 522 R = _get_from_cache(key) /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/structure/parent_gens.so in sage.structure.parent_gens.normalize_names (sage/structure/parent_gens.c:2759)() /home/darij/gitsage/sage-5.13.beta1/local/lib/python2.7/site- packages/sage/structure/parent_gens.so in sage.structure.parent_gens._certify_names (sage/structure/parent_gens.c:2322)() ValueError: first letter of variable name must be a letter }}} So it seems that both bugs are due to the fancy `R[x]` syntax for polynomial rings conflicting with the `R[i]` syntax for the i-th element of an enumerated set `R`, and #8389 didn't cause the error, but at most exposed it better. This reworks the `unrank` function in `sage.combinat.ranker` and uses it for the Cartesian product (this doesn't work for `ZZ`, see #16239). In followup tickets, we also want to be more systematic about using unrank in `TestSuite` and do better construction of `some_elements()` #16244. URL: http://trac.sagemath.org/15919 Reported by: darij Ticket author(s): Nicolas M. Thiéry Reviewer(s): Travis Scrimshaw
2 parents 022e875 + 79d4698 commit 9490e5e

File tree

2 files changed

+124
-5
lines changed

2 files changed

+124
-5
lines changed

src/sage/combinat/cartesian_product.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@
1818

1919
from inspect import isgenerator
2020
import sage.misc.prandom as rnd
21-
import __builtin__
21+
from sage.misc.mrange import xmrange_iter, _is_finite, _len
2222
from combinat import CombinatorialClass
23-
from sage.rings.integer import Integer
23+
from ranker import unrank
2424
from sage.rings.infinity import infinity
25-
from sage.misc.mrange import xmrange_iter, _is_finite, _len
2625

2726
def CartesianProduct(*iters):
2827
"""
@@ -241,6 +240,13 @@ def unrank(self, x):
241240
sage: C = CartesianProduct(xrange(1000), xrange(1000), xrange(1000))
242241
sage: C[238792368]
243242
[238, 792, 368]
243+
244+
Check for :trac:`15919`::
245+
246+
sage: FF = IntegerModRing(29)
247+
sage: C = CartesianProduct(FF, FF, FF)
248+
sage: C.unrank(0)
249+
[0, 0, 0]
244250
"""
245251
try:
246252
lens = [_len(it) for it in self.iters]
@@ -257,7 +263,7 @@ def unrank(self, x):
257263
if x != 0:
258264
raise IndexError("x larger than the size of the Cartesian Product")
259265
positions.reverse()
260-
return [L[i] for L,i in zip(self.iters, positions)]
266+
return [unrank(L, i) for L,i in zip(self.iters, positions)]
261267

262268
def random_element(self):
263269
"""

src/sage/combinat/ranker.py

+114-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
# http://www.gnu.org/licenses/
1919
#*****************************************************************************
2020

21-
from sage.misc.all import cached_function
21+
from collections import Iterable, Sequence
22+
from sage.misc.cachefunc import cached_function
23+
from sage.structure.parent import Parent
24+
from sage.categories.enumerated_sets import EnumeratedSets
2225

2326
def from_list(l):
2427
"""
@@ -134,3 +137,113 @@ def unrank(i):
134137
return None
135138

136139
return [rank, unrank]
140+
141+
def unrank(L, i):
142+
r"""
143+
Return the `i`-th element of `L`.
144+
145+
INPUT:
146+
147+
- ``L`` -- a list, tuple, finite enumerated set, ...
148+
- ``i`` -- an int or :class:`Integer`
149+
150+
The purpose of this utility is to give a uniform idiom to recover
151+
the `i`-th element of an object ``L``, whether ``L`` is a list,
152+
tuple (or more generally a :class:`collections.Sequence`), an
153+
enumerated set, some old parent of Sage still implementing
154+
unranking in the method ``__getitem__``, or an iterable (see
155+
:class:`collections.Iterable`). See :trac:`15919`.
156+
157+
EXAMPLES:
158+
159+
Lists, tuples, and other :class:`sequences <collections.Sequence>`::
160+
161+
sage: from sage.combinat.ranker import unrank
162+
sage: unrank(['a','b','c'], 2)
163+
'c'
164+
sage: unrank(('a','b','c'), 1)
165+
'b'
166+
sage: unrank(xrange(3,13,2), 1)
167+
5
168+
169+
Enumerated sets::
170+
171+
sage: unrank(GF(7), 2)
172+
2
173+
sage: unrank(IntegerModRing(29), 10)
174+
10
175+
176+
An old parent with unranking implemented in ``__getitem__``::
177+
178+
sage: M = MatrixSpace(GF(3), 2, 2)
179+
sage: hasattr(M, "unrank")
180+
False
181+
sage: M[42]
182+
[1 0]
183+
[2 1]
184+
sage: unrank(M, 42)
185+
[1 0]
186+
[2 1]
187+
188+
An iterable::
189+
190+
sage: unrank(NN,4)
191+
4
192+
193+
An iterator::
194+
195+
sage: unrank(('a{}'.format(i) for i in range(20)), 0)
196+
'a0'
197+
sage: unrank(('a{}'.format(i) for i in range(20)), 2)
198+
'a2'
199+
200+
.. WARNING::
201+
202+
When unranking an iterator, it returns the``i``-th element
203+
beyond where it is currently at::
204+
205+
sage: from sage.combinat.ranker import unrank
206+
sage: it = iter(range(20))
207+
sage: unrank(it, 2)
208+
2
209+
sage: unrank(it, 2)
210+
5
211+
212+
TESTS::
213+
214+
sage: from sage.combinat.ranker import unrank
215+
sage: unrank(range(3), 10)
216+
Traceback (most recent call last):
217+
...
218+
IndexError: list index out of range
219+
220+
sage: unrank(('a{}'.format(i) for i in range(20)), 22)
221+
Traceback (most recent call last):
222+
...
223+
IndexError: index out of range
224+
225+
sage: M[100]
226+
Traceback (most recent call last):
227+
...
228+
IndexError: list index out of range
229+
"""
230+
if L in EnumeratedSets:
231+
return L.unrank(i)
232+
if isinstance(L, Sequence):
233+
return L[i]
234+
if isinstance(L, Parent):
235+
# handle parents still implementing unranking in __getitem__
236+
try:
237+
return L[i]
238+
except (AttributeError, TypeError, ValueError):
239+
pass
240+
if isinstance(L, Iterable):
241+
try:
242+
it = iter(L)
243+
for _ in range(i):
244+
it.next()
245+
return it.next()
246+
except StopIteration as e:
247+
raise IndexError("index out of range")
248+
raise ValueError("Don't know how to unrank on {}".format(L))
249+

0 commit comments

Comments
 (0)