Skip to content

Commit

Permalink
gh-35812: Decouple tuple
Browse files Browse the repository at this point in the history
    
Change sage/combinat/tuple.py to use itertools instead of GAP.

### 📚 Description

This fixes #35784.
Iterating Tuples delegates to itertools.product
Iterating UnorderedTuples delegates to
itertools.combinations_with_replacement

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. It should be `[x]` not `[x
]`. -->

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation accordingly.

### ⌛ Dependencies


<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
    
URL: #35812
Reported by: deinst
Reviewer(s): David Coudert, deinst, Matthias Köppe, Travis Scrimshaw
  • Loading branch information
Release Manager committed Jul 8, 2023
2 parents e97ed20 + f79c0d0 commit 9119b4d
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 51 deletions.
88 changes: 39 additions & 49 deletions src/sage/combinat/tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
# https://www.gnu.org/licenses/
# ****************************************************************************

from sage.libs.gap.libgap import libgap
from sage.arith.misc import binomial
from sage.rings.integer_ring import ZZ
from sage.structure.parent import Parent
from sage.structure.unique_representation import UniqueRepresentation
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets

from itertools import product, combinations_with_replacement

class Tuples(Parent, UniqueRepresentation):
"""
Expand All @@ -35,23 +35,23 @@ class Tuples(Parent, UniqueRepresentation):
sage: S = [1,2]
sage: Tuples(S,3).list()
[[1, 1, 1], [2, 1, 1], [1, 2, 1], [2, 2, 1], [1, 1, 2],
[2, 1, 2], [1, 2, 2], [2, 2, 2]]
[(1, 1, 1), (2, 1, 1), (1, 2, 1), (2, 2, 1), (1, 1, 2),
(2, 1, 2), (1, 2, 2), (2, 2, 2)]
sage: mset = ["s","t","e","i","n"]
sage: Tuples(mset,2).list()
[['s', 's'], ['t', 's'], ['e', 's'], ['i', 's'], ['n', 's'],
['s', 't'], ['t', 't'], ['e', 't'], ['i', 't'], ['n', 't'],
['s', 'e'], ['t', 'e'], ['e', 'e'], ['i', 'e'], ['n', 'e'],
['s', 'i'], ['t', 'i'], ['e', 'i'], ['i', 'i'], ['n', 'i'],
['s', 'n'], ['t', 'n'], ['e', 'n'], ['i', 'n'], ['n', 'n']]
[('s', 's'), ('t', 's'), ('e', 's'), ('i', 's'), ('n', 's'),
('s', 't'), ('t', 't'), ('e', 't'), ('i', 't'), ('n', 't'),
('s', 'e'), ('t', 'e'), ('e', 'e'), ('i', 'e'), ('n', 'e'),
('s', 'i'), ('t', 'i'), ('e', 'i'), ('i', 'i'), ('n', 'i'),
('s', 'n'), ('t', 'n'), ('e', 'n'), ('i', 'n'), ('n', 'n')]
::
sage: K.<a> = GF(4, 'a') # optional - sage.rings.finite_rings
sage: mset = [x for x in K if x != 0] # optional - sage.rings.finite_rings
sage: Tuples(mset,2).list() # optional - sage.rings.finite_rings
[[a, a], [a + 1, a], [1, a], [a, a + 1], [a + 1, a + 1], [1, a + 1],
[a, 1], [a + 1, 1], [1, 1]]
[(a, a), (a + 1, a), (1, a), (a, a + 1), (a + 1, a + 1), (1, a + 1),
(a, 1), (a + 1, 1), (1, 1)]
"""
@staticmethod
def __classcall_private__(cls, S, k):
Expand All @@ -75,7 +75,7 @@ def __init__(self, S, k):
"""
self.S = S
self.k = k
self._index_list = [S.index(s) for s in S]
self._index_list = list(set(S.index(s) for s in S))
category = FiniteEnumeratedSets()
Parent.__init__(self, category=category)

Expand All @@ -94,33 +94,21 @@ def __iter__(self):
sage: S = [1,2]
sage: Tuples(S,3).list()
[[1, 1, 1], [2, 1, 1], [1, 2, 1], [2, 2, 1], [1, 1, 2],
[2, 1, 2], [1, 2, 2], [2, 2, 2]]
[(1, 1, 1), (2, 1, 1), (1, 2, 1), (2, 2, 1), (1, 1, 2),
(2, 1, 2), (1, 2, 2), (2, 2, 2)]
sage: mset = ["s","t","e","i","n"]
sage: Tuples(mset,2).list()
[['s', 's'], ['t', 's'], ['e', 's'], ['i', 's'], ['n', 's'],
['s', 't'], ['t', 't'], ['e', 't'], ['i', 't'],
['n', 't'], ['s', 'e'], ['t', 'e'], ['e', 'e'], ['i', 'e'],
['n', 'e'], ['s', 'i'], ['t', 'i'], ['e', 'i'],
['i', 'i'], ['n', 'i'], ['s', 'n'], ['t', 'n'], ['e', 'n'],
['i', 'n'], ['n', 'n']]
"""
S = self.S
k = self.k
import copy
if k <= 0:
yield []
return
if k == 1:
for x in S:
yield [x]
return

for s in S:
for x in Tuples(S, k - 1):
y = copy.copy(x)
y.append(s)
yield y
[('s', 's'), ('t', 's'), ('e', 's'), ('i', 's'), ('n', 's'),
('s', 't'), ('t', 't'), ('e', 't'), ('i', 't'), ('n', 't'),
('s', 'e'), ('t', 'e'), ('e', 'e'), ('i', 'e'), ('n', 'e'),
('s', 'i'), ('t', 'i'), ('e', 'i'), ('i', 'i'), ('n', 'i'),
('s', 'n'), ('t', 'n'), ('e', 'n'), ('i', 'n'), ('n', 'n')]
sage: Tuples((1,1,2),3).list()
[(1, 1, 1), (2, 1, 1), (1, 2, 1), (2, 2, 1), (1, 1, 2),
(2, 1, 2), (1, 2, 2), (2, 2, 2)]
"""
for p in product(self._index_list, repeat=self.k):
yield tuple(self.S[i] for i in reversed(p))

def cardinality(self):
"""
Expand All @@ -133,7 +121,7 @@ def cardinality(self):
sage: Tuples(S,2).cardinality() # optional - sage.libs.gap
25
"""
return ZZ(libgap.NrTuples(self._index_list, ZZ(self.k)))
return ZZ(len(self._index_list)).__pow__(self.k)


Tuples_sk = Tuples
Expand All @@ -151,10 +139,10 @@ class UnorderedTuples(Parent, UniqueRepresentation):
sage: S = [1,2]
sage: UnorderedTuples(S,3).list()
[[1, 1, 1], [1, 1, 2], [1, 2, 2], [2, 2, 2]]
[(1, 1, 1), (1, 1, 2), (1, 2, 2), (2, 2, 2)]
sage: UnorderedTuples(["a","b","c"],2).list()
[['a', 'a'], ['a', 'b'], ['a', 'c'], ['b', 'b'], ['b', 'c'],
['c', 'c']]
[('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'b'), ('b', 'c'),
('c', 'c')]
"""
@staticmethod
def __classcall_private__(cls, S, k):
Expand All @@ -178,7 +166,7 @@ def __init__(self, S, k):
"""
self.S = S
self.k = k
self._index_list = [S.index(s) for s in S]
self._index_list = list(set(S.index(s) for s in S))
category = FiniteEnumeratedSets()
Parent.__init__(self, category=category)

Expand All @@ -191,19 +179,21 @@ def __repr__(self):
"""
return "Unordered tuples of %s of length %s" % (self.S, self.k)

def list(self):
def __iter__(self):
"""
EXAMPLES::
sage: S = [1,2]
sage: UnorderedTuples(S,3).list()
[[1, 1, 1], [1, 1, 2], [1, 2, 2], [2, 2, 2]]
[(1, 1, 1), (1, 1, 2), (1, 2, 2), (2, 2, 2)]
sage: UnorderedTuples(["a","b","c"],2).list()
[['a', 'a'], ['a', 'b'], ['a', 'c'], ['b', 'b'], ['b', 'c'],
['c', 'c']]
[('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'b'), ('b', 'c'),
('c', 'c')]
sage: UnorderedTuples([1,1,2],3).list()
[(1, 1, 1), (1, 1, 2), (1, 2, 2), (2, 2, 2)]
"""
ans = libgap.UnorderedTuples(self._index_list, ZZ(self.k))
return [[self.S[i] for i in l] for l in ans]
for ans in combinations_with_replacement(self._index_list, self.k):
yield tuple(self.S[i] for i in ans)

def cardinality(self):
"""
Expand All @@ -213,7 +203,7 @@ def cardinality(self):
sage: UnorderedTuples(S,2).cardinality() # optional - sage.libs.gap
15
"""
return ZZ(libgap.NrUnorderedTuples(self._index_list, ZZ(self.k)))
return binomial(len(self._index_list) + self.k - 1, self.k)


UnorderedTuples_sk = UnorderedTuples
4 changes: 2 additions & 2 deletions src/sage/schemes/projective/projective_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -2068,7 +2068,7 @@ def subscheme_from_Chow_form(self, Ch, dim):
L1 = []
for t in UnorderedTuples(list(range(n + 1)), dim + 1):
if all(t[i] < t[i + 1] for i in range(dim)):
L1.append(t)
L1.append(list(t))
# create the dual brackets
L2 = []
signs = []
Expand Down Expand Up @@ -2374,7 +2374,7 @@ def rational_points(self, bound=0):
for ai in R:
P[i] = ai
for tup in S[i - 1]:
if gcd([ai] + tup) == 1:
if gcd((ai,) + tup) == 1:
for j in range(i):
P[j] = tup[j]
pts.append(self(P))
Expand Down

0 comments on commit 9119b4d

Please sign in to comment.