From d23726c2fbfa5144ac3d0223907c54f28ed37bbe Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 25 Sep 2014 12:26:38 -0500 Subject: [PATCH 1/8] Added support for C. for CombinatorialFreeModule. --- src/sage/combinat/free_module.py | 54 +++++++++-- src/sage/monoids/free_monoid.py | 10 +- src/sage/monoids/indexed_free_monoid.py | 26 +++-- src/sage/structure/indexed_generators.py | 117 ++++++++++++++++++++++- 4 files changed, 177 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 6e4d9830711..5a613a20e6c 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -13,7 +13,7 @@ from sage.structure.element import Element from sage.structure.parent import Parent from sage.structure.sage_object import have_same_parent -from sage.structure.indexed_generators import IndexedGenerators +from sage.structure.indexed_generators import IndexedGenerators, parse_indices_names from sage.modules.free_module_element import vector from sage.misc.misc import repr_lincomb from sage.modules.module import Module @@ -1165,10 +1165,22 @@ class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): sage: g = 2*G.monomial(3) + G.monomial(4) sage: tensor([f, g]) 2*x[1] # y[3] + x[1] # y[4] + 4*x[2] # y[3] + 2*x[2] # y[4] + + We check that we can use the shorthand ``C. = ...``:: + + sage: C. = CombinatorialFreeModule(QQ) + sage: C + Free module generated by {'x', 'y', 'z'} over Rational Field + sage: a = x - y + 4*z; a + x - y + 4*z + sage: a.parent() is C + True + """ @staticmethod - def __classcall_private__(cls, base_ring, basis_keys, category = None, prefix="B", **keywords): + def __classcall_private__(cls, base_ring, basis_keys=None, category=None, + prefix=None, names=None, **keywords): """ TESTS:: @@ -1210,11 +1222,17 @@ def __classcall_private__(cls, base_ring, basis_keys, category = None, prefix="B latex_bracket = keywords.get('latex_bracket', None) if isinstance(latex_bracket, list): keywords['latex_bracket'] = tuple(latex_bracket) - return super(CombinatorialFreeModule, cls).__classcall__(cls, base_ring, basis_keys, category = category, prefix=prefix, **keywords) + + basis_keys, names, prefix = parse_indices_names(basis_keys, names, prefix, keywords) + if prefix is None: + prefix = "B" + + return super(CombinatorialFreeModule, cls).__classcall__(cls, base_ring, basis_keys, category=category, prefix=prefix, names=names, **keywords) Element = CombinatorialFreeModuleElement - def __init__(self, R, basis_keys, element_class = None, category = None, prefix="B", **kwds): + def __init__(self, R, basis_keys=None, element_class=None, category=None, + prefix=None, names=None, **kwds): r""" TESTS:: @@ -1279,17 +1297,23 @@ def __init__(self, R, basis_keys, element_class = None, category = None, prefix= if isinstance(basis_keys, (list, tuple)): basis_keys = FiniteEnumeratedSet(basis_keys) + # This is needed for the Cartesian product + # TODO: Remove this duplication from __classcall_private__ + basis_keys, names, prefix = parse_indices_names(basis_keys, names, prefix, kwds) + if prefix is None: + prefix = "B" + # ignore the optional 'key' since it only affects CachedRepresentation kwds.pop('key', None) # This needs to be first as per #10127 if 'monomial_cmp' in kwds: - kwds['generator_cmp'] = kwds['monomial_cmp'] - del kwds['monomial_cmp'] + kwds['generator_cmp'] = kwds.pop('monomial_cmp') + IndexedGenerators.__init__(self, basis_keys, prefix, **kwds) - Parent.__init__(self, base = R, category = category, + Parent.__init__(self, base=R, category=category, names=names, # Could we get rid of this? - element_constructor = self._element_constructor_) + element_constructor=self._element_constructor_) self._order = None @@ -1543,6 +1567,20 @@ def _an_element_impl(self): """ return self.element_class(self, {}) + def _first_ngens(self, n): + """ + Used by the preparser for ``F. = ...``. + + EXAMPLES:: + + sage: C = CombinatorialFreeModule(QQ, ZZ) + sage: C._first_ngens(3) + (B[0], B[1], B[-1]) + """ + B = self.basis() + it = iter(self._indices) + return tuple(B[it.next()] for i in range(n)) + def dimension(self): """ Returns the dimension of the combinatorial algebra (which is given diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index 8257cba1f95..fc642ebf4ee 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -111,10 +111,14 @@ def FreeMonoid(index_set=None, names=None, commutative=False, **kwds): Free abelian monoid on 3 generators (x, y, z) sage: FreeMonoid(index_set=ZZ, commutative=True) Free abelian monoid indexed by Integer Ring + + TESTS:: + + sage: FreeMonoid(index_set=ZZ, names='x,y,z') + Free monoid indexed by Integer Ring """ if 'abelian' in kwds: - commutative = kwds['abelian'] - del kwds['abelian'] + commutative = kwds.pop('abelian') if commutative: from sage.monoids.free_abelian_monoid import FreeAbelianMonoid @@ -131,6 +135,8 @@ def FreeMonoid(index_set=None, names=None, commutative=False, **kwds): if index_set not in ZZ: if names is not None: + if isinstance(names, str): + names = names.split(',') names = normalize_names(len(names), names) from sage.monoids.indexed_free_monoid import IndexedFreeMonoid return IndexedFreeMonoid(index_set, names=names, **kwds) diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index 323a8fc3d66..c3cda288728 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -19,7 +19,7 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element import MonoidElement -from sage.structure.indexed_generators import IndexedGenerators +from sage.structure.indexed_generators import IndexedGenerators, parse_indices_names from sage.combinat.dict_addition import dict_addition from sage.categories.monoids import Monoids @@ -656,7 +656,7 @@ class IndexedMonoid(Parent, IndexedGenerators, UniqueRepresentation): :class:`~sage.structure.indexed_generators.IndexedGenerators`. """ @staticmethod - def __classcall__(cls, indices, prefix="F", **kwds): + def __classcall__(cls, indices, prefix=None, names=None, **kwds): """ TESTS:: @@ -674,16 +674,11 @@ def __classcall__(cls, indices, prefix="F", **kwds): sage: Groups.Commutative.free() Traceback (most recent call last): ... - ValueError: no index set specified + ValueError: either the indices or names must be given """ - if isinstance(indices, str): - indices = FiniteEnumeratedSet(list(indices)) - elif isinstance(indices, (list, tuple)): - indices = FiniteEnumeratedSet(indices) - elif indices is None: - if kwds.get('names', None) is None: - raise ValueError("no index set specified") - indices = FiniteEnumeratedSet(kwds['names']) + indices, names, prefix = parse_indices_names(indices, names, prefix, kwds) + if prefix is None: + prefix = "F" # bracket or latex_bracket might be lists, so convert # them to tuples so that they're hashable. @@ -693,7 +688,8 @@ def __classcall__(cls, indices, prefix="F", **kwds): latex_bracket = kwds.get('latex_bracket', None) if isinstance(latex_bracket, list): kwds['latex_bracket'] = tuple(latex_bracket) - return super(IndexedMonoid, cls).__classcall__(cls, indices, prefix, **kwds) + + return super(IndexedMonoid, cls).__classcall__(cls, indices, prefix, names=names, **kwds) def __init__(self, indices, prefix, category=None, names=None, **kwds): """ @@ -729,9 +725,9 @@ def _first_ngens(self, n): EXAMPLES:: - sage: F. = FreeMonoid(index_set=ZZ) - sage: [x, y, z] - [F[0], F[1], F[-1]] + sage: F = FreeMonoid(index_set=ZZ) + sage: F._first_ngens(3) + (F[0], F[1], F[-1]) """ it = iter(self._indices) return tuple(self.gen(it.next()) for i in range(n)) diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py index 28953eb7bdf..f853a88190e 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -9,6 +9,7 @@ #***************************************************************************** from sage.rings.all import Integer +from sage.sets.finite_enumerated_set import FiniteEnumeratedSet class IndexedGenerators(object): r""" @@ -223,11 +224,7 @@ def print_options(self, **kwds): # being there altogether. if kwds: for option in kwds: - # TODO: make this into a set and put it in a global variable? - if option in ['prefix', 'latex_prefix', 'bracket', 'latex_bracket', - 'scalar_mult', 'latex_scalar_mult', 'tensor_symbol', - 'generator_cmp', 'string_quotes' - ]: + if option in self._print_options: self._print_options[option] = kwds[option] else: raise ValueError('{} is not a valid print option.'.format(option)) @@ -446,3 +443,113 @@ def _latex_generator(self, m): return left + s + right return "%s_{%s}" % (prefix, s) +def split_index_keywords(kwds): + """ + Split the dictionary ``kwds`` into two dictionaries, one containing + keywords for :class:`IndexedGenerators`, and the other is everything else. + + OUTPUT: + + The dictionary containing only they keywords + for :class:`IndexedGenerators`. This modifies the dictionary ``kwds``. + + .. WARNING:: + + This modifies the input dictionary ``kwds``. + + EXAMPLES:: + + sage: from sage.structure.indexed_generators import split_index_keywords + sage: d = {'string_quotes': False, 'bracket': None, 'base': QQ} + sage: split_index_keywords(d) + {'string_quotes': False, 'bracket': None} + sage: d + {'base': Rational Field} + """ + ret = {} + for option in ['prefix', 'latex_prefix', 'bracket', 'latex_bracket', + 'scalar_mult', 'latex_scalar_mult', 'tensor_symbol', + 'generator_cmp', 'string_quotes']: + try: + ret[option] = kwds.pop(option) + except KeyError: + pass + return ret + +def parse_indices_names(indices, names, prefix, kwds={}): + """ + Parse the indices, names, and prefix input, along with setting + default values for keyword arguments ``kwds``. + + OUTPUT: + + The triple ``(I, N, p)`` where ``I`` is the indexing set, ``N`` is the + tuple of variable names, and ``p`` is the prefix. This modifies + the dictionary ``kwds``. + + .. NOTE:: + + When the indices, names, or prefix have not been given, it + should be passed to this function as ``None``. + + .. NOTE:: + + For handling default prefixes, if the result will be ``None`` if + it is not processed in this function. + + EXAMPLES:: + + sage: from sage.structure.indexed_generators import parse_indices_names + sage: d = {} + sage: parse_indices_names(ZZ, 'x,y,z', None, d) + (Integer Ring, 'x,y,z', None) + sage: d + {} + sage: d = {} + sage: parse_indices_names(None, 'x,y,z', None, d) + ({'x', 'y', 'z'}, ('x', 'y', 'z'), '') + sage: d + {'string_quotes': False, 'bracket': False} + sage: d = {} + sage: parse_indices_names(ZZ, None, None, d) + (Integer Ring, None, None) + sage: d + {} + + :: + + sage: d = {'string_quotes':True, 'bracket':'['} + sage: parse_indices_names(ZZ, ['x','y','z'], 'x', d) + (Integer Ring, ['x', 'y', 'z'], 'x') + sage: d + {'string_quotes': True, 'bracket': '['} + sage: parse_indices_names(None, 'x,y,z', 'A', d) + ({'x', 'y', 'z'}, ('x', 'y', 'z'), 'A') + sage: d + {'string_quotes': True, 'bracket': '['} + """ + if indices is None: + if names is None: + raise ValueError("either the indices or names must be given") + if isinstance(names, str): + names = names.split(',') + names = tuple(names) + indices = names + + if prefix is None: + prefix ='' + if 'string_quotes' not in kwds: + kwds['string_quotes'] = False + if 'bracket' not in kwds: + kwds['bracket'] = False + + if isinstance(indices, dict): # dict of {name: index} -- not likely to be used + names = indices.keys() + indices = FiniteEnumeratedSet([indices[n] for n in names]) + elif isinstance(indices, str): + indices = FiniteEnumeratedSet(list(indices)) + elif isinstance(indices, (list, tuple, set, frozenset)): + indices = FiniteEnumeratedSet(indices) + + return (indices, names, prefix) + From e3175db7d92ff2e03d7a2dba5baab4ab456c2f59 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 9 Mar 2016 05:57:08 -0600 Subject: [PATCH 2/8] Fixing doctest ordering. --- src/sage/structure/indexed_generators.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py index 4496f2e958a..3f5326dfda1 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -462,7 +462,7 @@ def split_index_keywords(kwds): sage: from sage.structure.indexed_generators import split_index_keywords sage: d = {'string_quotes': False, 'bracket': None, 'base': QQ} sage: split_index_keywords(d) - {'string_quotes': False, 'bracket': None} + {'bracket': None, 'string_quotes': False} sage: d {'base': Rational Field} """ @@ -509,7 +509,7 @@ def parse_indices_names(indices, names, prefix, kwds={}): sage: parse_indices_names(None, 'x,y,z', None, d) ({'x', 'y', 'z'}, ('x', 'y', 'z'), '') sage: d - {'string_quotes': False, 'bracket': False} + {'bracket': False, 'string_quotes': False} sage: d = {} sage: parse_indices_names(ZZ, None, None, d) (Integer Ring, None, None) @@ -522,11 +522,11 @@ def parse_indices_names(indices, names, prefix, kwds={}): sage: parse_indices_names(ZZ, ['x','y','z'], 'x', d) (Integer Ring, ['x', 'y', 'z'], 'x') sage: d - {'string_quotes': True, 'bracket': '['} + {'bracket': '[', 'string_quotes': True} sage: parse_indices_names(None, 'x,y,z', 'A', d) ({'x', 'y', 'z'}, ('x', 'y', 'z'), 'A') sage: d - {'string_quotes': True, 'bracket': '['} + {'bracket': '[', 'string_quotes': True} """ if indices is None: if names is None: From fc9a4f867d726d0d3ba97f2e320d8711d45a84e7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 9 Apr 2016 15:22:11 -0500 Subject: [PATCH 3/8] Being more python3 compatible. --- src/sage/combinat/free_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 8bdfeb25e15..cc36ae2a3f8 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1374,7 +1374,7 @@ def _first_ngens(self, n): """ B = self.basis() it = iter(self._indices) - return tuple(B[it.next()] for i in range(n)) + return tuple(B[next(it)] for i in range(n)) def dimension(self): """ From 0c3452d7ec39ce63500d2018d9e2803846b0683b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 10 Apr 2016 03:22:46 -0500 Subject: [PATCH 4/8] Changing behavior of _first_ngens to support using gens(). --- src/sage/combinat/free_module.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index cc36ae2a3f8..88ea6fd4e95 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1371,7 +1371,17 @@ def _first_ngens(self, n): sage: C = CombinatorialFreeModule(QQ, ZZ) sage: C._first_ngens(3) (B[0], B[1], B[-1]) + + sage: R. = FreeAlgebra(QQ, 2) + sage: x,y + (x, y) """ + try: + # Try gens first for compatibility with classes that + # rely on this (e.g., FreeAlgebra) + return tuple(self.gens())[:n] + except (AttributeError, ValueError, TypeError): + pass B = self.basis() it = iter(self._indices) return tuple(B[next(it)] for i in range(n)) From 02df281bd3b0f283d0355ae4a4e203d3741c66e5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 10 Apr 2016 07:44:40 -0500 Subject: [PATCH 5/8] Fixing doctests. --- src/sage/algebras/associated_graded.py | 8 ++++---- src/sage/algebras/jordan_algebra.py | 2 +- .../examples/filtered_algebras_with_basis.py | 2 +- .../categories/filtered_algebras_with_basis.py | 10 +++++----- .../categories/filtered_modules_with_basis.py | 16 ++++++++-------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index d6f6a88b014..0f7c76c4663 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -139,10 +139,10 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): ``grA`` are isomorphic:: sage: grA(A.an_element()) - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(U['z']) + 3*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 2*bar(U['x']) + 3*bar(U['y']) + bar(1) sage: elt = A.an_element() + A.algebra_generators()['x'] + 2 sage: grelt = grA(elt); grelt - bar(U['x']^2*U['y']^2*U['z']^3) + 2*bar(U['x']) + 2*bar(U['z']) + 5*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 3*bar(U['x']) + 3*bar(U['y']) + 3*bar(1) sage: A(grelt) == elt True @@ -241,10 +241,10 @@ def _element_constructor_(self, x): sage: grA = A.graded_algebra() sage: grA(A.an_element()) bar(U['x']^2*U['y']^2*U['z']^3) - + bar(U['x']) + 2*bar(U['z']) + 3*bar(1) + + 2*bar(U['x']) + 3*bar(U['y']) + bar(1) sage: grA(A.an_element() + A.algebra_generators()['x'] + 2) bar(U['x']^2*U['y']^2*U['z']^3) - + 2*bar(U['x']) + 2*bar(U['z']) + 5*bar(1) + + 3*bar(U['x']) + 3*bar(U['y']) + 3*bar(1) """ if isinstance(x, CombinatorialFreeModule.Element): if x.parent() is self._A: diff --git a/src/sage/algebras/jordan_algebra.py b/src/sage/algebras/jordan_algebra.py index faa4c25210f..c2fe2128fb7 100644 --- a/src/sage/algebras/jordan_algebra.py +++ b/src/sage/algebras/jordan_algebra.py @@ -287,7 +287,7 @@ def _an_element_(self): sage: F. = FreeAlgebra(QQ) sage: J = JordanAlgebra(F) sage: J.an_element() - 2*y + 2*y^2 + 3*y^2*z + 2 + 2*x + 3*y """ return self.element_class(self, self._A.an_element()) diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py index 2738c29cfa4..3de3456a2b6 100644 --- a/src/sage/categories/examples/filtered_algebras_with_basis.py +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -114,7 +114,7 @@ def degree_on_basis(self, m): sage: A.degree_on_basis((x^4).leading_support()) 4 sage: a = A.an_element(); a - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2*U['z'] + 3 + U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 3*U['y'] + 1 sage: A.degree_on_basis(a.leading_support()) 1 sage: s = sorted(a.support(), key=str)[2]; s diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index c503cdb4316..f8cdfbadfe2 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -131,10 +131,10 @@ def to_graded_conversion(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 2*U['z'] + 5 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.to_graded_conversion()(p); q - bar(U['x']^2*U['y']^2*U['z']^3) - + 2*bar(U['x']) + 2*bar(U['z']) + 5*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 3*bar(U['x']) + + 3*bar(U['y']) + 3*bar(1) sage: q.parent() is A.graded_algebra() True """ @@ -160,7 +160,7 @@ def from_graded_conversion(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 2*U['z'] + 5 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.to_graded_conversion()(p) sage: A.from_graded_conversion()(q) == p True @@ -191,7 +191,7 @@ def projection(self, i): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 2*U['z'] + 5 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.projection(7)(p); q bar(U['x']^2*U['y']^2*U['z']^3) sage: q.parent() is A.graded_algebra() diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 9e870db932d..29b372f81be 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -835,11 +835,11 @@ def homogeneous_component(self, n): sage: G = A.algebra_generators() sage: g = A.an_element() - 2 * G['x'] * G['y']; g U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] - + U['x'] + 2*U['z'] + 3 + + 2*U['x'] + 3*U['y'] + 1 sage: g.homogeneous_component(-1) 0 sage: g.homogeneous_component(0) - 3 + 1 sage: g.homogeneous_component(2) -2*U['x']*U['y'] sage: g.homogeneous_component(5) @@ -901,22 +901,22 @@ def truncate(self, n): sage: G = A.algebra_generators() sage: g = A.an_element() - 2 * G['x'] * G['y']; g U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] - + U['x'] + 2*U['z'] + 3 + + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(-1) 0 sage: g.truncate(0) 0 sage: g.truncate(2) - U['x'] + 2*U['z'] + 3 + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(3) - -2*U['x']*U['y'] + U['x'] + 2*U['z'] + 3 + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(5) - -2*U['x']*U['y'] + U['x'] + 2*U['z'] + 3 + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(7) - -2*U['x']*U['y'] + U['x'] + 2*U['z'] + 3 + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(8) U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] - + U['x'] + 2*U['z'] + 3 + + 2*U['x'] + 3*U['y'] + 1 TESTS: From 1c1cad16af1adbc61642433db06efb750fcb77f0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 27 May 2017 11:32:35 -0500 Subject: [PATCH 6/8] Use normalize_names. --- src/sage/combinat/free_module.py | 4 ++- src/sage/monoids/free_monoid.py | 4 +-- src/sage/structure/indexed_generators.py | 38 +++++++++++------------- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 29e38e840f5..d9ecaf2d4b3 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -300,7 +300,9 @@ def __classcall_private__(cls, base_ring, basis_keys=None, category=None, if prefix is None: prefix = "B" - return super(CombinatorialFreeModule, cls).__classcall__(cls, base_ring, basis_keys, category=category, prefix=prefix, names=names, **keywords) + return super(CombinatorialFreeModule, cls).__classcall__(cls, + base_ring, basis_keys, category=category, prefix=prefix, names=names, + **keywords) # We make this explicitly a Python class so that the methods, # specifically _mul_, from category framework still works. -- TCS diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index f9bb0a84536..30ff3d0902b 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -131,9 +131,7 @@ def FreeMonoid(index_set=None, names=None, commutative=False, **kwds): if index_set not in ZZ: if names is not None: - if isinstance(names, str): - names = names.split(',') - names = normalize_names(len(names), names) + names = normalize_names(-1, names) from sage.monoids.indexed_free_monoid import IndexedFreeMonoid return IndexedFreeMonoid(index_set, names=names, **kwds) diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py index 119b4d1fb41..e43b651efad 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -11,6 +11,7 @@ from sage.rings.all import Integer from sage.sets.finite_enumerated_set import FiniteEnumeratedSet +from sage.structure.category_object import normalize_names class IndexedGenerators(object): r"""nodetex @@ -581,20 +582,18 @@ def parse_indices_names(indices, names, prefix, kwds={}): if indices is None: if names is None: raise ValueError("either the indices or names must be given") - if isinstance(names, str): - names = names.split(',') - names = tuple(names) + names = normalize_names(-1, names) indices = names if prefix is None: - prefix ='' + prefix = '' if 'string_quotes' not in kwds: kwds['string_quotes'] = False if 'bracket' not in kwds: kwds['bracket'] = False if isinstance(indices, dict): # dict of {name: index} -- not likely to be used - names = indices.keys() + names = normalize_names(-1, tuple(indices.keys())) indices = FiniteEnumeratedSet([indices[n] for n in names]) elif isinstance(indices, str): indices = FiniteEnumeratedSet(list(indices)) @@ -605,7 +604,7 @@ def parse_indices_names(indices, names, prefix, kwds={}): def standardize_names_index_set(names=None, index_set=None, ngens=None): """ - Standardize the ``names`` and ``index_set`` for a Lie algebra. + Standardize the ``names`` and ``index_set`` inputs. OUTPUT: @@ -642,28 +641,25 @@ def standardize_names_index_set(names=None, index_set=None, ngens=None): sage: standardize_names_index_set(['x'], ['a', 'b']) Traceback (most recent call last): ... - ValueError: the number of names must equal the size of the indexing set + IndexError: the number of names must equal the size of the indexing set sage: standardize_names_index_set('x,y', ['a']) Traceback (most recent call last): ... - ValueError: the number of names must equal the size of the indexing set + IndexError: the number of names must equal the size of the indexing set sage: standardize_names_index_set('x,y,z', ngens=2) Traceback (most recent call last): ... - ValueError: the number of names must equal the number of generators + IndexError: the number of names must equal the number of generators sage: standardize_names_index_set(index_set=['a'], ngens=2) Traceback (most recent call last): ... - ValueError: the size of the indexing set must equal the number of generators + IndexError: the size of the indexing set must equal the number of generators """ - if isinstance(names, str): - names = tuple(names.split(',')) - elif names is not None: - names = tuple(names) - - if names is not None and len(names) == 1 and ngens > 1: - letter = names[0] - names = tuple([letter + str(i) for i in range(ngens)]) + if names is not None: + if ngens is None or ngens < 0: + names = normalize_names(-1, names) + else: + names = normalize_names(ngens, names) if index_set is None: if names is None: @@ -683,12 +679,12 @@ def standardize_names_index_set(names=None, index_set=None, ngens=None): if names is not None: if len(names) != index_set.cardinality(): - raise ValueError("the number of names must equal" + raise IndexError("the number of names must equal" " the size of the indexing set") if ngens is not None and len(names) != ngens: - raise ValueError("the number of names must equal the number of generators") + raise IndexError("the number of names must equal the number of generators") elif ngens is not None and index_set.cardinality() != ngens: - raise ValueError("the size of the indexing set must equal" + raise IndexError("the size of the indexing set must equal" " the number of generators") return (names, index_set) From 76b3be2dd4123baaff90dc38c23d11d3e568df1c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 27 May 2017 12:10:46 -0500 Subject: [PATCH 7/8] Removing code duplication. --- src/sage/combinat/free_module.py | 4 +- src/sage/monoids/indexed_free_monoid.py | 5 +- src/sage/structure/indexed_generators.py | 96 ++++++++++++++---------- 3 files changed, 61 insertions(+), 44 deletions(-) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index d9ecaf2d4b3..1928a1e680e 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -296,7 +296,7 @@ def __classcall_private__(cls, base_ring, basis_keys=None, category=None, if isinstance(latex_bracket, list): keywords['latex_bracket'] = tuple(latex_bracket) - basis_keys, names, prefix = parse_indices_names(basis_keys, names, prefix, keywords) + names, basis_keys, prefix = parse_indices_names(names, basis_keys, prefix, keywords) if prefix is None: prefix = "B" @@ -418,7 +418,7 @@ def __init__(self, R, basis_keys=None, element_class=None, category=None, # This is needed for the Cartesian product # TODO: Remove this duplication from __classcall_private__ - basis_keys, names, prefix = parse_indices_names(basis_keys, names, prefix, kwds) + names, basis_keys, prefix = parse_indices_names(names, basis_keys, prefix, kwds) if prefix is None: prefix = "B" diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index f99f9a507d3..6e12b2931d0 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -653,7 +653,7 @@ def __classcall__(cls, indices, prefix=None, names=None, **kwds): ... ValueError: either the indices or names must be given """ - indices, names, prefix = parse_indices_names(indices, names, prefix, kwds) + names, indices, prefix = parse_indices_names(names, indices, prefix, kwds) if prefix is None: prefix = "F" @@ -666,7 +666,8 @@ def __classcall__(cls, indices, prefix=None, names=None, **kwds): if isinstance(latex_bracket, list): kwds['latex_bracket'] = tuple(latex_bracket) - return super(IndexedMonoid, cls).__classcall__(cls, indices, prefix, names=names, **kwds) + return super(IndexedMonoid, cls).__classcall__(cls, indices, prefix, + names=names, **kwds) def __init__(self, indices, prefix, category=None, names=None, **kwds): """ diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py index e43b651efad..a3f8ba4932b 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -3,14 +3,16 @@ Indexed Generators """ #***************************************************************************** -# Copyright (C) 2013 Travis Scrimshaw , +# Copyright (C) 2013 Travis Scrimshaw # -# Distributed under the terms of the GNU General Public License (GPL) +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** from sage.rings.all import Integer -from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from sage.structure.category_object import normalize_names class IndexedGenerators(object): @@ -527,16 +529,20 @@ def split_index_keywords(kwds): pass return ret -def parse_indices_names(indices, names, prefix, kwds={}): +def parse_indices_names(names, index_set, prefix, kwds={}): """ - Parse the indices, names, and prefix input, along with setting + Parse the names, index set, and prefix input, along with setting default values for keyword arguments ``kwds``. OUTPUT: - The triple ``(I, N, p)`` where ``I`` is the indexing set, ``N`` is the - tuple of variable names, and ``p`` is the prefix. This modifies - the dictionary ``kwds``. + The triple ``(N, I, p)``: + + - ``N`` is the tuple of variable names, + - ``I`` is the index set, and + - ``p`` is the prefix. + + This modifies the dictionary ``kwds``. .. NOTE:: @@ -552,38 +558,36 @@ def parse_indices_names(indices, names, prefix, kwds={}): sage: from sage.structure.indexed_generators import parse_indices_names sage: d = {} - sage: parse_indices_names(ZZ, 'x,y,z', None, d) - (Integer Ring, 'x,y,z', None) + sage: parse_indices_names('x,y,z', ZZ, None, d) + (('x', 'y', 'z'), Integer Ring, None) sage: d {} sage: d = {} - sage: parse_indices_names(None, 'x,y,z', None, d) - ({'x', 'y', 'z'}, ('x', 'y', 'z'), '') + sage: parse_indices_names('x,y,z', None, None, d) + (('x', 'y', 'z'), {'x', 'y', 'z'}, '') sage: d {'bracket': False, 'string_quotes': False} sage: d = {} - sage: parse_indices_names(ZZ, None, None, d) - (Integer Ring, None, None) + sage: parse_indices_names(None, ZZ, None, d) + (None, Integer Ring, None) sage: d {} :: sage: d = {'string_quotes':True, 'bracket':'['} - sage: parse_indices_names(ZZ, ['x','y','z'], 'x', d) - (Integer Ring, ['x', 'y', 'z'], 'x') + sage: parse_indices_names(['a','b','c'], ZZ, 'x', d) + (('a', 'b', 'c'), Integer Ring, 'x') sage: d {'bracket': '[', 'string_quotes': True} - sage: parse_indices_names(None, 'x,y,z', 'A', d) - ({'x', 'y', 'z'}, ('x', 'y', 'z'), 'A') + sage: parse_indices_names('x,y,z', None, 'A', d) + (('x', 'y', 'z'), {'x', 'y', 'z'}, 'A') sage: d {'bracket': '[', 'string_quotes': True} """ - if indices is None: + if index_set is None: if names is None: raise ValueError("either the indices or names must be given") - names = normalize_names(-1, names) - indices = names if prefix is None: prefix = '' @@ -592,20 +596,23 @@ def parse_indices_names(indices, names, prefix, kwds={}): if 'bracket' not in kwds: kwds['bracket'] = False - if isinstance(indices, dict): # dict of {name: index} -- not likely to be used - names = normalize_names(-1, tuple(indices.keys())) - indices = FiniteEnumeratedSet([indices[n] for n in names]) - elif isinstance(indices, str): - indices = FiniteEnumeratedSet(list(indices)) - elif isinstance(indices, (list, tuple, set, frozenset)): - indices = FiniteEnumeratedSet(indices) + names, index_set = standardize_names_index_set(names, index_set, -1) - return (indices, names, prefix) + return (names, index_set, prefix) def standardize_names_index_set(names=None, index_set=None, ngens=None): """ Standardize the ``names`` and ``index_set`` inputs. + INPUT: + + - ``names`` -- (optional) the variable names + - ``index_set`` -- (optional) the index set + - ``ngens`` -- (optional) the number of generators + + If ``ngens`` is a negative number, then this does not check that + the number of variable names matches the size of the index set. + OUTPUT: A pair ``(names_std, index_set_std)``, where ``names_std`` is either @@ -673,19 +680,28 @@ def standardize_names_index_set(names=None, index_set=None, ngens=None): # be the names index_set = tuple(names) - if isinstance(index_set, (tuple, list)): - from sage.sets.finite_enumerated_set import FiniteEnumeratedSet + from sage.sets.finite_enumerated_set import FiniteEnumeratedSet + if isinstance(index_set, dict): # dict of {name: index} -- not likely to be used + if names is not None: + raise ValueError("cannot give index_set as a dict and names") + names = normalize_names(-1, tuple(index_set.keys())) + index_set = FiniteEnumeratedSet([index_set[n] for n in names]) + elif isinstance(index_set, str): + index_set = FiniteEnumeratedSet(list(index_set)) + elif isinstance(index_set, (tuple, list)): index_set = FiniteEnumeratedSet(index_set) - if names is not None: - if len(names) != index_set.cardinality(): - raise IndexError("the number of names must equal" - " the size of the indexing set") - if ngens is not None and len(names) != ngens: - raise IndexError("the number of names must equal the number of generators") - elif ngens is not None and index_set.cardinality() != ngens: - raise IndexError("the size of the indexing set must equal" - " the number of generators") + if ngens is None or ngens >= 0: + if names is not None: + if len(names) != index_set.cardinality(): + raise IndexError("the number of names must equal" + " the size of the indexing set") + if ngens is not None and len(names) != ngens: + raise IndexError("the number of names must equal the" + " number of generators") + elif ngens is not None and index_set.cardinality() != ngens: + raise IndexError("the size of the indexing set must equal" + " the number of generators") return (names, index_set) From 8f7d3e635d568228ea76c130feb486ec1317c1f9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 29 May 2017 15:59:38 -0500 Subject: [PATCH 8/8] Use setdefault instead of manually checking. --- src/sage/structure/indexed_generators.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py index a3f8ba4932b..28e5f6599cc 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -591,10 +591,8 @@ def parse_indices_names(names, index_set, prefix, kwds={}): if prefix is None: prefix = '' - if 'string_quotes' not in kwds: - kwds['string_quotes'] = False - if 'bracket' not in kwds: - kwds['bracket'] = False + kwds.setdefault('string_quotes', False) + kwds.setdefault('bracket', False) names, index_set = standardize_names_index_set(names, index_set, -1)