From 7982d3ad9815b6313ac93880467aaf71ddb096ca Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 15 Dec 2013 22:46:17 -0800 Subject: [PATCH 001/421] Iniital fix and added regular partitions. --- src/sage/combinat/partition.py | 288 ++++++++++++++++++++++++++++++++- 1 file changed, 283 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 138b66f68ea..d49f3f05a26 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -4216,8 +4216,8 @@ class Partitions(UniqueRepresentation, Parent): Valid keywords are: ``starting``, ``ending``, ``min_part``, ``max_part``, ``max_length``, ``min_length``, ``length``, - ``max_slope``, ``min_slope``, ``inner``, ``outer``, and - ``parts_in``. They have the following meanings: + ``max_slope``, ``min_slope``, ``inner``, ``outer``, ``parts_in`` + and ``regular``. They have the following meanings: - ``starting=p`` specifies that the partitions should all be less than or equal to `p` in lex order. @@ -4247,6 +4247,10 @@ class Partitions(UniqueRepresentation, Parent): `S`, which can be any sequence of pairwise distinct positive integers. + - ``regular=ell`` specifies that the partitions are `\ell`-regular, + and can only be combined with the ``max_part`` keyword if `n` is + not specified + The ``max_*`` versions, along with ``inner`` and ``ending``, work analogously. @@ -4454,6 +4458,10 @@ def __classcall_private__(cls, n=None, **kwargs): if len(kwargs) == 1: if 'max_part' in kwargs: return Partitions_all_bounded(kwargs['max_part']) + if 'regular' in kwargs: + return RegularPartitions_all(kwargs['regular']) + elif len(kwargs) == 2 and 'max_part' in kwargs and 'regular' in kwargs: + return RegularPartitions_all_bounded(kwargs['regular'], kwargs['max_part']) raise ValueError("the size must be specified with any keyword argument") return Partitions_all() else: @@ -4473,6 +4481,8 @@ def __classcall_private__(cls, n=None, **kwargs): return Partitions_starting(n, kwargs['starting']) elif 'ending' in kwargs: return Partitions_ending(n, kwargs['ending']) + elif 'regular' in kwargs: + return RegularPartitions_n(n, kwargs['regular']) if 'min_part' not in kwargs: kwargs['min_part'] = 1 @@ -4500,9 +4510,7 @@ def __classcall_private__(cls, n=None, **kwargs): else: kwargs['min_length'] = len(inner) del kwargs['inner'] - kwargs['element_class'] = Partition - kwargs['global_options'] = Partitions.global_options - return IntegerListsLex(n, **kwargs) + return Partitions_with_constraints(n, **kwargs) def __init__(self, is_infinite=False): """ @@ -5908,6 +5916,276 @@ def __setstate__(self, data): constraints.update(data['constraints']) self.__init__(n, **constraints) +class Partitions_with_constraints(IntegerListsLex): + """ + Partitions which satisfy a set of constraints. + + EXAMPLES:: + + sage: P = Partitions(6, inner=[1,1], max_slope=-1) + sage: list(P) + [[5, 1], [4, 2], [3, 2, 1]] + """ + def __init__(self, n, **kwargs): + """ + Initialize ``self``. + + TESTS:: + + sage: P = Partitions(6, min_part=2, max_slope=-1) + sage: TestSuite(P).run() + + Test that :trac:`-1` is fixed:: + + sage: loads(dumps(P)) == P + True + """ + IntegerListsLex.__init__(self, n, **kwargs) + + Element = Partition + global_options = PartitionOptions + +###################### +# Regular Partitions # +###################### + +class RegularPartitions(Partitions): + r""" + Base class for `\ell`-regular partitions. + + A partition is `\ell`-regular if `m_i < \ell` for all `i`. + """ + def __init__(self, ell, is_infinte=False): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: P = Partitions(regular=2) + sage: TestSuite(P).run() + """ + self.ell = ell + Partitions.__init__(self, is_infinte) + + def __contains__(self, x): + """ + TESTS:: + + sage: P = Partitions(regular=3) + sage: [5] in P + True + sage: [] in P + True + sage: [3, 3, 2, 2] in P + True + sage: [3, 3, 3, 1] in P + False + sage: [4, 0, 0, 0, 0, 0] in P + True + sage: Partition([4,2,2,1]) in P + True + sage: Partition([4,2,2,2]) in P + False + """ + if not Partitions.__contains__(self, x): + return False + if isinstance(x, Partition): + return max(x.to_exp(1)) < self.ell + return all(x.count(i) < self.ell for i in set(x) if i > 0) + + def _fast_iterator(self, n, max_part): + """ + A fast (recursive) iterator which returns a list. + + EXAMPLES:: + + sage: P = Partitions(regular=3) + sage: list(P._fast_iterator(5, 5)) + [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1]] + sage: list(P._fast_iterator(5, 3)) + [[3, 2], [3, 1, 1], [2, 2, 1]] + sage: list(P._fast_iterator(5, 6)) + [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1]] + """ + if n == 0: + yield [] + return + + if n < max_part: + max_part = n + bdry = self.ell - 1 + + for i in reversed(range(1, max_part+1)): + for p in self._fast_iterator(n-i, i): + if p.count(i) < bdry: + yield [i] + p + +class RegularPartitions_all(RegularPartitions): + r""" + The class of all `\ell`-regular partitions. + """ + def __init__(self, ell): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: P = Partitions(regular=4) + sage: TestSuite(P).run() + """ + RegularPartitions.__init__(self, ell, True) + + def _repr_(self): + """ + TESTS:: + + sage: from sage.combinat.partition import RegularPartitions_all + sage: RegularPartitions_all(3) + 3-Regular Partitions + """ + return "{}-Regular Partitions".format(self.ell) + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: P = Partitions(regular=3) + sage: it = P.__iter__() + sage: [it.next() for x in range(10)] + [[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [2, 1, 1]] + """ + n = 0 + while True: + for p in self._fast_iterator(n, n): + yield self.element_class(self, p) + n += 1 + +class RegularPartitions_all_bounded(RegularPartitions): + r""" + The class of `\ell`-regular partitions bounded by `k`. + """ + def __init__(self, ell, k): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: P = Partitions(regular=4, max_part=3) + sage: TestSuite(P).run() + """ + self.k = k + RegularPartitions.__init__(self, ell, False) + + def __contains__(self, x): + """ + TESTS:: + + sage: P = Partitions(regular=4, max_part=3) + sage: [3, 3, 3] in P + True + sage: [] in P + True + sage: [4, 2, 1] in P + False + """ + return len(x) == 0 or (x[0] <= self.k and RegularPartitions.__contains__(self, x)) + + def _repr_(self): + """ + TESTS:: + + sage: from sage.combinat.partition import RegularPartitions_all_bounded + sage: RegularPartitions_all_bounded(4, 3) + 3-Bounded 4-Regular Partitions + """ + return "{}-Bounded {}-Regular Partitions".format(self.k, self.ell) + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: P = Partitions(regular=2, max_part=3) + sage: list(P) + [[3, 2, 1], [3, 2], [3, 1], [3], [2, 1], [2], [1], []] + """ + k = self.k + for n in reversed(range(k*(k+1)/2 * self.ell)): + for p in self._fast_iterator(n, k): + yield self.element_class(self, p) + +class RegularPartitions_n(RegularPartitions, Partitions_n): + r""" + The class of `\ell`-regular partitions of `n`. + """ + def __init__(self, n, ell): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: P = Partitions(5, regular=3) + sage: TestSuite(P).run() + """ + RegularPartitions.__init__(self, ell) + Partitions_n.__init__(self, n) + + def _repr_(self): + """ + TESTS:: + + sage: from sage.combinat.partition import RegularPartitions_n + sage: RegularPartitions_n(3, 5) + 5-Regular Partitions of the integer 3 + """ + return "{}-Regular Partitions of the integer {}".format(self.ell, self.n) + + def __contains__(self, x): + """ + TESTS:: + + sage: P = Partitions(5, regular=3) + sage: [3, 1, 1] in P + True + sage: [3, 2, 1] in P + False + """ + return RegularPartitions.__contains__(self, x) and sum(x) == self.n + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: P = Partitions(5, regular=3) + sage: list(P) + [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1]] + """ + for p in self._fast_iterator(self.n, self.n): + yield self.element_class(self, p) + + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: P = Partitions(5, regular=3) + sage: P.cardinality() + 5 + sage: P = Partitions(5, regular=6) + sage: P.cardinality() + 7 + sage: P.cardinality() == Partitions(5).cardinality() + True + """ + if self.ell > self.n: + return Partitions_n.cardinality(self) + return ZZ.sum(1 for x in self) ###################### # Ordered Partitions # From 1adb368c00b4b8e1eb49186af47ee1e86dafd94f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 26 Dec 2013 19:02:15 -0800 Subject: [PATCH 002/421] Added deprecation to IntegerListLex global_options arg. Fixed doctests. --- src/sage/combinat/integer_list.py | 9 ++ src/sage/combinat/partition.py | 234 ++++++++++++++++++++++++------ 2 files changed, 198 insertions(+), 45 deletions(-) diff --git a/src/sage/combinat/integer_list.py b/src/sage/combinat/integer_list.py index 2b2297990e1..fa8ad089abe 100644 --- a/src/sage/combinat/integer_list.py +++ b/src/sage/combinat/integer_list.py @@ -931,6 +931,10 @@ def __init__(self, sage: C.cardinality().parent() is ZZ True sage: TestSuite(C).run() + sage: C = IntegerListsLex(2, global_options=Partitions.global_options) + doctest:...: DeprecationWarning: the global_options argument is deprecated + since, in general, pickling is broken; create your own class instead + See http://trac.sagemath.org/15525 for details. """ # Convert to float infinity from sage.rings.infinity import infinity @@ -943,6 +947,11 @@ def __init__(self, if max_part == infinity: max_part = float('+inf') + if global_options is not None: + from sage.misc.superseded import deprecation + deprecation(15525, 'the global_options argument is deprecated since, in general,' + ' pickling is broken; create your own class instead') + if floor is None: self.floor_list = [] elif isinstance(floor, __builtin__.list): diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 5390be626e7..c1ae1536d0c 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -3747,7 +3747,8 @@ def remove_horizontal_border_strip(self, k): sage: Partition([5,3,1]).remove_horizontal_border_strip(6).list() [] - The result is returned as an instance of :class:`IntegerListsLex`:: + The result is returned as an instance of + :class:`Partitions_with_constraints`:: sage: Partition([5,3,1]).remove_horizontal_border_strip(5) The subpartitions of [5, 3, 1] obtained by removing an horizontal border strip of length 5 @@ -3763,15 +3764,13 @@ def remove_horizontal_border_strip(self, k): sage: Partition([]).remove_horizontal_border_strip(6).list() [] """ - return IntegerListsLex(n = self.size()-k, - min_length = len(self)-1, - max_length = len(self), - floor = self[1:]+[0], - ceiling = self[:], - max_slope = 0, - element_class = Partition, - global_options = Partitions.global_options, - name = "The subpartitions of %s obtained by removing an horizontal border strip of length %s"%(self,k)) + return Partitions_with_constraints(n = self.size()-k, + min_length = len(self)-1, + max_length = len(self), + floor = self[1:]+[0], + ceiling = self[:], + max_slope = 0, + name = "The subpartitions of {} obtained by removing an horizontal border strip of length {}".format(self,k)) def k_conjugate(self, k): r""" @@ -4251,17 +4250,17 @@ class Partitions(UniqueRepresentation, Parent): (see :trac:`15467`). - ``regular=ell`` specifies that the partitions are `\ell`-regular, - and can only be combined with the ``max_part`` keyword if `n` is - not specified + and can only be combined with the ``max_length`` or ``max_part``, but + not both, keywords if `n` is not specified The ``max_*`` versions, along with ``inner`` and ``ending``, work analogously. - Right now, the ``parts_in``, ``starting``, and ``ending`` keyword - arguments are mutually exclusive, both of each other and of other + Right now, the ``parts_in``, ``starting``, ``ending``, and ``regular`` + keyword arguments are mutually exclusive, both of each other and of other keyword arguments. If you specify, say, ``parts_in``, all other - keyword arguments will be ignored; ``starting`` and ``ending`` work - the same way. + keyword arguments will be ignored; ``starting``, ``ending``, and + ``regular`` work the same way. EXAMPLES: @@ -4339,6 +4338,16 @@ class Partitions(UniqueRepresentation, Parent): sage: Partitions(10, min_part=2, length=3).list() [[6, 2, 2], [5, 3, 2], [4, 4, 2], [4, 3, 3]] + Some examples using the ``regular`` keyword:: + + sage: Partitions(regular=4) + 4-Regular Partitions + sage: Partitions(regular=4, max_length=3) + 4-Regular Partitions with max length 3 + sage: Partitions(regular=4, max_part=3) + 4-Regular 3-Bounded Partitions + sage: Partitions(3, regular=4) + 4-Regular Partitions of the integer 3 Here are some further examples using various constraints:: @@ -4396,7 +4405,7 @@ class Partitions(UniqueRepresentation, Parent): sage: TestSuite(Partitions(0)).run() sage: TestSuite(Partitions(5)).run() - sage: TestSuite(Partitions(5, min_part=2)).run() # Not tested: todo - IntegerListsLex needs to pickle properly + sage: TestSuite(Partitions(5, min_part=2)).run() sage: repr( Partitions(5, min_part=2) ) 'Partitions of the integer 5 satisfying constraints min_part=2' @@ -4472,10 +4481,6 @@ def __classcall_private__(cls, n=None, **kwargs): sage: P2 = Partitions(int(4)) sage: P is P2 True - sage: P = Partitions(4, length=2, parts_in=[3,1,1]) - sage: P2 = Partitions(4, length=2, parts_in=(3,1,1)) - sage: P is P2 - True """ if n == infinity: raise ValueError("n cannot be infinite") @@ -4486,8 +4491,14 @@ def __classcall_private__(cls, n=None, **kwargs): return Partitions_all_bounded(kwargs['max_part']) if 'regular' in kwargs: return RegularPartitions_all(kwargs['regular']) - elif len(kwargs) == 2 and 'max_part' in kwargs and 'regular' in kwargs: - return RegularPartitions_all_bounded(kwargs['regular'], kwargs['max_part']) + elif len(kwargs) == 2: + if 'regular' in kwargs: + if kwargs['regular'] < 2: + raise ValueError("the regularity must be at least 2") + if 'max_part' in kwargs: + return RegularPartitions_bounded(kwargs['regular'], kwargs['max_part']) + if 'max_length' in kwargs: + return RegularPartitions_truncated(kwargs['regular'], kwargs['max_length']) raise ValueError("the size must be specified with any keyword argument") return Partitions_all() elif isinstance(n, (int,Integer)): @@ -4541,6 +4552,9 @@ def __classcall_private__(cls, n=None, **kwargs): del kwargs['inner'] return Partitions_with_constraints(n, **kwargs) + raise ValueError("n must be an integer or be equal to one of " + "None, NN, NonNegativeIntegers()") + def __init__(self, is_infinite=False): """ Initialize ``self``. @@ -5937,11 +5951,9 @@ def __setstate__(self, data): [[2, 1], [1, 1, 1]] """ n = data['n'] - self.__class__ = IntegerListsLex + self.__class__ = Partitions_with_constraints constraints = {'max_slope' : 0, - 'min_part' : 1, - 'element_class' : Partition, - 'global_options' : Partitions.global_options} + 'min_part' : 1} constraints.update(data['constraints']) self.__init__(n, **constraints) @@ -5954,22 +5966,22 @@ class Partitions_with_constraints(IntegerListsLex): sage: P = Partitions(6, inner=[1,1], max_slope=-1) sage: list(P) [[5, 1], [4, 2], [3, 2, 1]] - """ - def __init__(self, n, **kwargs): - """ - Initialize ``self``. - TESTS:: + TESTS:: - sage: P = Partitions(6, min_part=2, max_slope=-1) - sage: TestSuite(P).run() + sage: P = Partitions(6, min_part=2, max_slope=-1) + sage: TestSuite(P).run() - Test that :trac:`-1` is fixed:: + Test that :trac:`15525` is fixed:: - sage: loads(dumps(P)) == P - True - """ - IntegerListsLex.__init__(self, n, **kwargs) + sage: loads(dumps(P)) == P + True + """ +# def __init__(self, n, **kwargs): +# """ +# Initialize ``self``. +# """ +# IntegerListsLex.__init__(self, n, **kwargs) Element = Partition global_options = PartitionOptions @@ -5983,6 +5995,11 @@ class RegularPartitions(Partitions): Base class for `\ell`-regular partitions. A partition is `\ell`-regular if `m_i < \ell` for all `i`. + + INPUT: + + - ``ell`` -- the value `\ell` + - ``is_infinite`` -- if the subset of `\ell`-regular partitions is infinite """ def __init__(self, ell, is_infinte=False): """ @@ -6015,6 +6032,8 @@ def __contains__(self, x): True sage: Partition([4,2,2,2]) in P False + sage: Partition([10,1]) in P + True """ if not Partitions.__contains__(self, x): return False @@ -6052,6 +6071,14 @@ def _fast_iterator(self, n, max_part): class RegularPartitions_all(RegularPartitions): r""" The class of all `\ell`-regular partitions. + + INPUT: + + - ``ell`` -- the value `\ell` + + .. SEEALSO:: + + :class:`~sage.combinat.partition.RegularPartitions` """ def __init__(self, ell): """ @@ -6091,9 +6118,117 @@ def __iter__(self): yield self.element_class(self, p) n += 1 -class RegularPartitions_all_bounded(RegularPartitions): +class RegularPartitions_truncated(RegularPartitions): r""" - The class of `\ell`-regular partitions bounded by `k`. + The class of `\ell`-regular partitions with max length `k`. + + INPUT: + + - ``ell`` -- the value `\ell` + - ``max_len`` -- the maximum length + + .. SEEALSO:: + + :class:`~sage.combinat.partition.RegularPartitions` + """ + def __init__(self, ell, max_len): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: P = Partitions(regular=4, max_length=3) + sage: TestSuite(P).run() + """ + self.max_len = max_len + RegularPartitions.__init__(self, ell) + + def __contains__(self, x): + """ + TESTS:: + + sage: P = Partitions(regular=4, max_length=3) + sage: [3, 3, 3] in P + True + sage: [] in P + True + sage: [4, 2, 1, 1] in P + False + """ + return len(x) <= self.max_len and RegularPartitions.__contains__(self, x) + + def _repr_(self): + """ + TESTS:: + + sage: from sage.combinat.partition import RegularPartitions_truncated + sage: RegularPartitions_truncated(4, 3) + 4-Regular Partitions with max length 3 + """ + return "{}-Regular Partitions with max length {}".format(self.ell, self.max_len) + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: P = Partitions(regular=3, max_length=2) + sage: it = P.__iter__() + sage: [it.next() for x in range(10)] + [[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [5]] + """ + n = 0 + while True: + for p in self._fast_iterator(n, n): + yield self.element_class(self, p) + n += 1 + + def _fast_iterator(self, n, max_part, depth=0): + """ + A fast (recursive) iterator which returns a list. + + EXAMPLES:: + + sage: P = Partitions(regular=2, max_length=2) + sage: list(P._fast_iterator(5, 5)) + [[5], [4, 1], [3, 2]] + sage: list(P._fast_iterator(5, 3)) + [[3, 2]] + sage: list(P._fast_iterator(5, 6)) + [[5], [4, 1], [3, 2]] + """ + if n == 0 or depth >= self.max_len: + yield [] + return + + # Special case + if depth + 1 == self.max_len: + if max_part >= n: + yield [n] + return + + if n < max_part: + max_part = n + bdry = self.ell - 1 + + for i in reversed(range(1, max_part+1)): + for p in self._fast_iterator(n-i, i, depth+1): + if p.count(i) < bdry: + yield [i] + p + +class RegularPartitions_bounded(RegularPartitions): + r""" + The class of `\ell`-regular `k`-bounded partitions. + + INPUT: + + - ``ell`` -- the value `\ell` + - ``k`` -- the value `k` + + .. SEEALSO:: + + :class:`~sage.combinat.partition.RegularPartitions` """ def __init__(self, ell, k): """ @@ -6125,11 +6260,11 @@ def _repr_(self): """ TESTS:: - sage: from sage.combinat.partition import RegularPartitions_all_bounded - sage: RegularPartitions_all_bounded(4, 3) - 3-Bounded 4-Regular Partitions + sage: from sage.combinat.partition import RegularPartitions_bounded + sage: RegularPartitions_bounded(4, 3) + 4-Regular 3-Bounded Partitions """ - return "{}-Bounded {}-Regular Partitions".format(self.k, self.ell) + return "{}-Regular {}-Bounded Partitions".format(self.ell, self.k) def __iter__(self): """ @@ -6149,6 +6284,15 @@ def __iter__(self): class RegularPartitions_n(RegularPartitions, Partitions_n): r""" The class of `\ell`-regular partitions of `n`. + + INPUT: + + - ``n`` -- the integer `n` to partition + - ``ell`` -- the value `\ell` + + .. SEEALSO:: + + :class:`~sage.combinat.partition.RegularPartitions` """ def __init__(self, n, ell): """ From fa0d60186cf7f9bf73b8eb1ad70ab6bc5a862330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 3 Jun 2014 16:54:38 +0200 Subject: [PATCH 003/421] trac #16209 first sketch of a working version (rough) --- .../cluster_algebra_quiver/cluster_seed.py | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index eeecc7a37f2..e56ac86b7a4 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -29,7 +29,8 @@ from sage.rings.all import FractionField, PolynomialRing from sage.rings.fraction_field_element import FractionFieldElement from sage.sets.all import Set -from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType_Irreducible, QuiverMutationType_Reducible +from sage.graphs.digraph import DiGraph +from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType_Irreducible, QuiverMutationType_Reducible from sage.combinat.cluster_algebra_quiver.mutation_type import is_mutation_finite class ClusterSeed(SageObject): @@ -2037,6 +2038,79 @@ def greedy(self, a1, a2, method='by_recursion'): raise ValueError("Greedy elements are only currently " "defined for cluster seeds of rank two.") + def oriented_exchange_graph(self): + """ + Return the oriented exchange graph of ``self`` as a directed + graph + + The seed must be a cluster seed for a cluster algebra of + finite type with principal coefficients (the corresponding + quiver must have mutable vertices 0,1,...,n-1). + + EXAMPLES:: + + sage: S = ClusterSeed(['A', 2]).principal_extension() + sage: G = S.oriented_exchange_graph(); G + Digraph on 5 vertices + sage: G.out_degree_sequence() + [2, 1, 1, 1, 0] + + sage: S = ClusterSeed(['B', 2]).principal_extension() + sage: G = S.oriented_exchange_graph(); G + Digraph on 6 vertices + sage: G.out_degree_sequence() + [2, 1, 1, 1, 1, 0] + + TESTS:: + + sage: S = ClusterSeed(['A',[2,2],1]) + sage: S.oriented_exchange_graph() + Traceback (most recent call last): + ... + TypeError: only works for finite mutation type + + sage: S = ClusterSeed(['A', 2]) + sage: S.oriented_exchange_graph() + Traceback (most recent call last): + ... + TypeError: only works for principal coefficients + """ + if not self._mutation_type.is_finite(): + raise TypeError('only works for finite mutation type') + + if not self._is_principal: + raise TypeError('only works for principal coefficients') + + mut_class = self.mutation_class() + covers = [] + + n = self.n() + pairs = [(i, j) for i in mut_class for j in mut_class if i != j] + for (i, j) in pairs: + for k in range(n): + B = i.b_matrix() + count = 0 + green = False + for i2 in range(n, 2 * n): + if B[i2][k] <= 0: + count += 1 + green = (count == n) + S1 = i + NewS1 = S1.mutate(k, inplace=False) + Var1 = [NewS1.cluster_variable(k) for k in range(n)] + Var1.sort() + Var2 = [j.cluster_variable(k) for k in range(n)] + Var2.sort() + P_Res_NewS1 = NewS1.exchangeable_part() + P_Res_S2 = j.exchangeable_part() + New_P_Res_NewS1 = P_Res_NewS1.quiver().digraph() + New_P_Res_s2 = P_Res_S2.quiver().digraph() + if Var1 == Var2 and green and (i, j) not in covers: + covers.append((i, j)) + + return DiGraph(covers) + + def _bino(n, k): """ Binomial coefficient which we define as zero for negative n. From 3f66020116363b17de6d5a68c6ec21554c567f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 3 Jun 2014 17:07:16 +0200 Subject: [PATCH 004/421] trac #16209 a little bit smoother --- .../cluster_algebra_quiver/cluster_seed.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index e56ac86b7a4..164c16e2935 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -2087,24 +2087,15 @@ def oriented_exchange_graph(self): n = self.n() pairs = [(i, j) for i in mut_class for j in mut_class if i != j] for (i, j) in pairs: + B = i.b_matrix() for k in range(n): - B = i.b_matrix() - count = 0 - green = False - for i2 in range(n, 2 * n): - if B[i2][k] <= 0: - count += 1 + count = len([i2 for i2 in range(n, 2 * n) if B[i2][k] <= 0]) green = (count == n) - S1 = i - NewS1 = S1.mutate(k, inplace=False) + NewS1 = i.mutate(k, inplace=False) Var1 = [NewS1.cluster_variable(k) for k in range(n)] Var1.sort() Var2 = [j.cluster_variable(k) for k in range(n)] Var2.sort() - P_Res_NewS1 = NewS1.exchangeable_part() - P_Res_S2 = j.exchangeable_part() - New_P_Res_NewS1 = P_Res_NewS1.quiver().digraph() - New_P_Res_s2 = P_Res_S2.quiver().digraph() if Var1 == Var2 and green and (i, j) not in covers: covers.append((i, j)) From ad184efacc9ceabfb1179ccd4d677786f4713b01 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 19 Oct 2014 14:56:30 -0700 Subject: [PATCH 005/421] Implemented filtered modules/algebras and associated graded algebras. --- src/sage/algebras/associated_graded.py | 178 +++++++++++++++ src/sage/categories/algebras.py | 1 + src/sage/categories/algebras_with_basis.py | 1 + src/sage/categories/category_with_axiom.py | 7 +- .../examples/filtered_algebras_with_basis.py | 169 ++++++++++++++ .../examples/filtered_modules_with_basis.py | 151 +++++++++++++ .../examples/graded_modules_with_basis.py | 5 +- src/sage/categories/filtered_algebras.py | 47 ++++ .../filtered_algebras_with_basis.py | 143 ++++++++++++ src/sage/categories/filtered_modules.py | 208 ++++++++++++++++++ .../categories/filtered_modules_with_basis.py | 193 ++++++++++++++++ src/sage/categories/graded_algebras.py | 27 ++- .../categories/graded_algebras_with_basis.py | 117 ++-------- src/sage/categories/graded_modules.py | 88 ++------ .../categories/graded_modules_with_basis.py | 173 ++------------- src/sage/categories/modules.py | 38 ++++ src/sage/categories/modules_with_basis.py | 1 + 17 files changed, 1223 insertions(+), 324 deletions(-) create mode 100644 src/sage/algebras/associated_graded.py create mode 100644 src/sage/categories/examples/filtered_algebras_with_basis.py create mode 100644 src/sage/categories/examples/filtered_modules_with_basis.py create mode 100644 src/sage/categories/filtered_algebras.py create mode 100644 src/sage/categories/filtered_algebras_with_basis.py create mode 100644 src/sage/categories/filtered_modules.py create mode 100644 src/sage/categories/filtered_modules_with_basis.py diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py new file mode 100644 index 00000000000..4f9e6e925d2 --- /dev/null +++ b/src/sage/algebras/associated_graded.py @@ -0,0 +1,178 @@ +r""" +Associated Graded Algebras + +AUTHORS: + +- Travis Scrimshaw (2014-10-08): Initial version +""" + +#***************************************************************************** +# Copyright (C) 2014 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.misc.misc_c import prod + +from sage.categories.algebras_with_basis import AlgebrasWithBasis +from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis +from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis +from sage.categories.graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis +from sage.rings.all import ZZ +from sage.rings.infinity import infinity +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.sets.family import Family +from sage.sets.positive_integers import PositiveIntegers +from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid +from sage.combinat.cartesian_product import CartesianProduct +from sage.combinat.free_module import CombinatorialFreeModule + +class AssociatedGradedAlgebra(CombinatorialFreeModule): + """ + The associated graded algebra `\mathrm{gr} A` corresponding to + a filtered algebra `A`. + + INPUT: + + - ``A`` -- a filtered algebra + """ + def __init__(self, A, category=None): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: TestSuite(grA).run(elements=[prod(grA.algebra_generators())]) + """ + if A not in AlgebrasWithBasis(A.base_ring()).Filtered(): + raise ValueError("the base algebra must be filtered") + self._A = A + if category is None: + category = A.category().Graded() + from copy import copy + opts = copy(A.print_options()) + if not opts['prefix'] and not opts['bracket']: + opts['bracket'] = '(' + opts['prefix'] = opts['prefix'] + 'bar' + CombinatorialFreeModule.__init__(self, A.base_ring(), A.indices(), + category=category, **opts) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: A.graded_algebra() + Graded Algebra of An example of a filtered module with basis: + the universal enveloping algebra of Lie algebra of RR^3 + with cross product over Rational Field + """ + return "Graded Algebra of {}".format(self._A) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: latex(A.graded_algebra()) + \mathrm{gr}\; ... + """ + from sage.misc.latex import latex + return "\\mathrm{gr}\; " + latex(self._A) + + def _element_constructor_(self, x): + """ + Construct an element of ``self`` from ``x``. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: grA(A.an_element()) + bar(U['x']^2*U['y']^2*U['z']^3) + sage: grA(A.an_element() + A.algebra_generators()['x'] + 2) + bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + """ + if isinstance(x, CombinatorialFreeModule.Element): + if x.parent() is self._A: + return self._from_dict(dict(x)) + return super(AssociatedGradedAlgebra, self)._element_constructor_(x) + + def gen(self, *args, **kwds): + """ + Return a generator of ``self``. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: grA.gen('x') + bar(U['x']) + """ + try: + x = self._A.gen(*args, **kwds) + except AttributeError: + x = self._A.algebra_generators()[args[0]] + return self(x) + + @cached_method + def algebra_generators(self): + """ + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: grA.algebra_generators() + Finite family {'y': bar(U['y']), 'x': bar(U['x']), 'z': bar(U['z'])} + """ + G = self._A.algebra_generators() + return Family(G.keys(), lambda x: self(G[x]), name="generator") + + @cached_method + def one_basis(self): + """ + Return the basis index of the element `1`. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: grA.one_basis() + 1 + """ + return self._A.one_basis() + + def product_on_basis(self, x, y): + """ + Return the product on basis elements given by the + indices ``x`` and ``y``. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: G = grA.algebra_generators() + sage: x,y,z = G['x'], G['y'], G['z'] + sage: x * y # indirect doctest + bar(U['x']*U['y']) + sage: y * x + bar(U['x']*U['y']) + sage: z * y * x + bar(U['x']*U['y']*U['z']) + """ + ret = self._A.product_on_basis(x, y) + deg = self._A.degree_on_basis(x) + self._A.degree_on_basis(y) + return self.sum_of_terms([(i,c) for i,c in ret + if self._A.degree_on_basis(i) == deg], + distinct=True) + diff --git a/src/sage/categories/algebras.py b/src/sage/categories/algebras.py index 4d921dffe59..e24f16e2e4b 100644 --- a/src/sage/categories/algebras.py +++ b/src/sage/categories/algebras.py @@ -87,6 +87,7 @@ def __contains__(self, x): # return [Rings()] # TODO: won't be needed when Rings() will be Rngs().Unital() Commutative = LazyImport('sage.categories.commutative_algebras', 'CommutativeAlgebras', at_startup=True) + Filtered = LazyImport('sage.categories.filtered_algebras', 'FilteredAlgebras') Graded = LazyImport('sage.categories.graded_algebras', 'GradedAlgebras') WithBasis = LazyImport('sage.categories.algebras_with_basis', 'AlgebrasWithBasis') diff --git a/src/sage/categories/algebras_with_basis.py b/src/sage/categories/algebras_with_basis.py index 8a4668edf1e..339ca9de605 100644 --- a/src/sage/categories/algebras_with_basis.py +++ b/src/sage/categories/algebras_with_basis.py @@ -116,6 +116,7 @@ def example(self, alphabet = ('a','b','c')): from sage.categories.examples.algebras_with_basis import Example return Example(self.base_ring(), alphabet) + Filtered = LazyImport('sage.categories.filtered_algebras_with_basis', 'FilteredAlgebrasWithBasis') FiniteDimensional = LazyImport('sage.categories.finite_dimensional_algebras_with_basis', 'FiniteDimensionalAlgebrasWithBasis') Graded = LazyImport('sage.categories.graded_algebras_with_basis', 'GradedAlgebrasWithBasis') diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py index caf3a32f391..f62598efa6d 100644 --- a/src/sage/categories/category_with_axiom.py +++ b/src/sage/categories/category_with_axiom.py @@ -2208,8 +2208,11 @@ def _repr_object_names_static(category, axioms): continue if axiom == "WithBasis": result = result.replace(" over ", " with basis over ", 1) - elif axiom == "Connected" and "graded " in result: - result = result.replace("graded ", "graded connected ", 1) + elif axiom == "Connected": + if "graded " in result: + result = result.replace("graded ", "graded connected ", 1) + elif "filtered " in result: + result = result.replace("filtered ", "filtered connected ", 1) else: result = uncamelcase(axiom) + " " + result return result diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py new file mode 100644 index 00000000000..0b864fe3aa4 --- /dev/null +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -0,0 +1,169 @@ +r""" +Examples of filtered algebra with basis +""" +#***************************************************************************** +# Copyright (C) 2014 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.categories.filtered_algebras_with_basis import FilteredAlgebrasWithBasis +from sage.combinat.free_module import CombinatorialFreeModule +from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid +from sage.sets.family import Family +from sage.misc.misc import powerset + +class IntersectionFilteredAlgebra(CombinatorialFreeModule): + r""" + This class illustrates an implementation of a filtered algebra + with basis: the universal enveloping algebra of the Lie algebra + of `\RR^3` under the cross product. + + The Lie algebra is generated by `x,y,z` with brackets defined by + `[x, y] = z`, `[y, z] = x`, and `[x, z] = -y`. The universal enveloping + algebra has a (PBW) basis consisting of monomials `x^i y^j z^k`. + + INPUT: + + - ``R`` -- base ring + + The implementation involves the following: + + - A set of algebra generators -- the set of generators `x,y,z`. + + - A product -- this is given on basis elements by using + :meth:`product_on_basis`. + + - A degree function -- this is determined on the basis elements + by using :meth:`degree_on_basis` which returns the sum of exponents + of the monomial. + """ + def __init__(self, base_ring): + """ + EXAMPLES:: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: x,y,z = A.algebra_generators() + sage: TestSuite(A).run(elements=[x*y+z]) + """ + I = IndexedFreeAbelianMonoid(['x', 'y', 'z'], prefix='U') + gen_cmp = lambda x,y: cmp((-len(x), x.to_word_list()), (-len(y), y.to_word_list())) + CombinatorialFreeModule.__init__(self, base_ring, I, bracket=False, prefix='', + generator_cmp=gen_cmp, + category=FilteredAlgebrasWithBasis(base_ring)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: AlgebrasWithBasis(QQ).Filtered().example() + An example of a filtered module with basis: + the universal enveloping algebra of + Lie algebra of RR^3 with cross product over Rational Field + """ + return "An example of a filtered module with basis: the universal enveloping algebra of Lie algebra of RR^3 with cross product over {}".format(self.base_ring()) + + def algebra_generators(self): + """ + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: key = lambda x: x.leading_support().to_word_list() + sage: sorted(A.algebra_generators(), key=key) + [U['x'], U['y'], U['z']] + """ + G = self._indices.monoid_generators() + return Family({x: self.monomial(G[x]) for x in G.keys()}) + + def one_basis(self): + """ + Return the index of the unit of ``self``. + + EXAMPLES:: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: A.one_basis() + 1 + """ + return self._indices.one() + + def degree_on_basis(self, m): + """ + The degree of the element determined by ``m`` in ``self``. + + INPUT: + + - ``m`` -- an element of the free monoid + + OUTPUT: an integer, the degree of the corresponding basis element + + EXAMPLES:: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: x = A.algebra_generators()['x'] + 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 + sage: A.degree_on_basis(a.leading_support()) + 7 + """ + return len(m) + + def product_on_basis(self, s, t): + """ + Return the product of two basis elements indexed by ``s`` and ``t``. + + EXAMPLES:: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: G = A.algebra_generators() + sage: x,y,z = G['x'], G['y'], G['z'] + sage: A.product_on_basis(x.leading_support(), y.leading_support()) + U['x']*U['y'] + sage: y*x + U['x']*U['y'] - U['z'] + sage: x*y*x + U['x']^2*U['y'] - U['x']*U['z'] + sage: z*y*x + U['x']*U['y']*U['z'] - U['x']^2 + U['y']^2 - U['z']^2 + """ + if len(s) == 0: + return self.monomial(t) + if len(t) == 0: + return self.monomial(s) + if s.trailing_support() <= t.leading_support(): + return self.monomial(s*t) + + if len(t) == 1: + if len(s) == 1: + a = s.leading_support() + b = t.leading_support() + cur = self.monomial(s*t) + if a <= b: + return cur + if a == 'z': + if b == 'y': + return cur - self.monomial(self._indices.gen('x')) + # b == 'x' + return cur + self.monomial(self._indices.gen('y')) + # a == 'y' and b == 'x' + return cur - self.monomial(self._indices.gen('z')) + + cur = self.monomial(t) + for a in reversed(s.to_word_list()): + cur = self.monomial(self._indices.gen(a)) * cur + return cur + + cur = self.monomial(s) + for a in t.to_word_list(): + cur = cur * self.monomial(self._indices.gen(a)) + return cur + +Example = IntersectionFilteredAlgebra + diff --git a/src/sage/categories/examples/filtered_modules_with_basis.py b/src/sage/categories/examples/filtered_modules_with_basis.py new file mode 100644 index 00000000000..4af2f427e45 --- /dev/null +++ b/src/sage/categories/examples/filtered_modules_with_basis.py @@ -0,0 +1,151 @@ +r""" +Examples of filtered modules with basis +""" +#***************************************************************************** +# Copyright (C) 2013 Frederic Chapoton +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.categories.filtered_modules_with_basis import FilteredModulesWithBasis +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.partition import Partitions + + +class FilteredPartitionModule(CombinatorialFreeModule): + r""" + This class illustrates an implementation of a filtered module + with basis: the free module over partitions. + + INPUT: + + - ``R`` -- base ring + + The implementation involves the following: + + - A choice of how to represent elements. In this case, the basis + elements are partitions. The algebra is constructed as a + :class:`CombinatorialFreeModule + ` on the + set of partitions, so it inherits all of the methods for such + objects, and has operations like addition already defined. + + :: + + sage: A = ModulesWithBasis(QQ).Filtered().example() + + - If the algebra is called ``A``, then its basis function is + stored as ``A.basis``. Thus the function can be used to + find a basis for the degree `d` piece: essentially, just call + ``A.basis(d)``. More precisely, call ``x`` for + each ``x`` in ``A.basis(d)``. + + :: + + sage: [m for m in A.basis(4)] + [P[4], P[3, 1], P[2, 2], P[2, 1, 1], P[1, 1, 1, 1]] + + - For dealing with basis elements: :meth:`degree_on_basis`, and + :meth:`_repr_term`. The first of these defines the degree of any + monomial, and then the :meth:`degree + ` method for elements -- + see the next item -- uses it to compute the degree for a linear + combination of monomials. The last of these determines the + print representation for monomials, which automatically produces + the print representation for general elements. + + :: + + sage: A.degree_on_basis(Partition([4,3])) + 7 + sage: A._repr_term(Partition([4,3])) + 'P[4, 3]' + + - There is a class for elements, which inherits from + :class:`CombinatorialFreeModuleElement + `. An + element is determined by a dictionary whose keys are partitions and whose + corresponding values are the coefficients. The class implements + two things: an :meth:`is_homogeneous + ` method and a + :meth:`degree ` method. + + :: + + sage: p = A.monomial(Partition([3,2,1])); p + P[3, 2, 1] + sage: p.is_homogeneous() + True + sage: p.degree() + 6 + """ + def __init__(self, base_ring): + """ + EXAMPLES:: + + sage: A = ModulesWithBasis(QQ).Filtered().example(); A + An example of a filtered module with basis: the free module on partitions over Rational Field + sage: TestSuite(A).run() + """ + CombinatorialFreeModule.__init__(self, base_ring, Partitions(), + category=FilteredModulesWithBasis(base_ring)) + + # FIXME: this is currently required, because the implementation of ``basis`` + # in CombinatorialFreeModule overrides that of GradedModulesWithBasis + basis = FilteredModulesWithBasis.ParentMethods.__dict__['basis'] + + # This could be a default implementation + def degree_on_basis(self, t): + """ + The degree of the element determined by the partition ``t`` in + this filtered module. + + INPUT: + + - ``t`` -- the index of an element of the basis of this module, + i.e. a partition + + OUTPUT: an integer, the degree of the corresponding basis element + + EXAMPLES:: + + sage: A = ModulesWithBasis(QQ).Filtered().example() + sage: A.degree_on_basis(Partition((2,1))) + 3 + sage: A.degree_on_basis(Partition((4,2,1,1,1,1))) + 10 + sage: type(A.degree_on_basis(Partition((1,1)))) + + """ + return t.size() + + def _repr_(self): + """ + Print representation + + EXAMPLES:: + + sage: ModulesWithBasis(QQ).Filtered().example() # indirect doctest + An example of a filtered module with basis: the free module on partitions over Rational Field + """ + return "An example of a filtered module with basis: the free module on partitions over %s" % self.base_ring() + + def _repr_term(self, t): + """ + Print representation for the basis element represented by the + partition ``t``. + + This governs the behavior of the print representation of all elements + of the algebra. + + EXAMPLES:: + + sage: A = ModulesWithBasis(QQ).Filtered().example() + sage: A._repr_term(Partition((4,2,1))) + 'P[4, 2, 1]' + """ + return 'P' + t._repr_() + +Example = FilteredPartitionModule + diff --git a/src/sage/categories/examples/graded_modules_with_basis.py b/src/sage/categories/examples/graded_modules_with_basis.py index ebd05587f55..e0f95880199 100644 --- a/src/sage/categories/examples/graded_modules_with_basis.py +++ b/src/sage/categories/examples/graded_modules_with_basis.py @@ -9,6 +9,7 @@ #***************************************************************************** from sage.categories.graded_modules_with_basis import GradedModulesWithBasis +from sage.categories.filtered_modules_with_basis import FilteredModulesWithBasis from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.partition import Partitions @@ -20,7 +21,7 @@ class GradedPartitionModule(CombinatorialFreeModule): INPUT: - - ``R`` - base ring + - ``R`` -- base ring The implementation involves the following: @@ -106,7 +107,7 @@ def __init__(self, base_ring): # FIXME: this is currently required, because the implementation of ``basis`` # in CombinatorialFreeModule overrides that of GradedModulesWithBasis - basis = GradedModulesWithBasis.ParentMethods.__dict__['basis'] + basis = FilteredModulesWithBasis.ParentMethods.__dict__['basis'] # This could be a default implementation def degree_on_basis(self, t): diff --git a/src/sage/categories/filtered_algebras.py b/src/sage/categories/filtered_algebras.py new file mode 100644 index 00000000000..f03b2fb5877 --- /dev/null +++ b/src/sage/categories/filtered_algebras.py @@ -0,0 +1,47 @@ +r""" +Filtered Algebras +""" +#***************************************************************************** +# Copyright (C) 2014 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.misc.abstract_method import abstract_method +from sage.categories.filtered_modules import FilteredModulesCategory + +class FilteredAlgebras(FilteredModulesCategory): + """ + The category of filtered algebras. + + EXAMPLES:: + + sage: Algebras(ZZ).Filtered() + Category of filtered algebras over Integer Ring + sage: Algebras(ZZ).Filtered().super_categories() + [Category of algebras over Integer Ring, + Category of filtered modules over Integer Ring] + + TESTS:: + + sage: TestSuite(Algebras(ZZ).Filtered()).run() + """ + class ParentMethods: + @abstract_method(optional=True) + def graded_algebra(self): + """ + Return the associated graded algebra to ``self``. + + EXAMPLES:: + + sage: A = AlgebrasWithBasis(ZZ).Filtered().example() + sage: A.graded_algebra() + Graded Algebra of An example of a filtered module with basis: + the universal enveloping algebra of + Lie algebra of RR^3 with cross product over Integer Ring + """ + + class ElementMethods: + pass + diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py new file mode 100644 index 00000000000..d9d3fe61a96 --- /dev/null +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -0,0 +1,143 @@ +r""" +Filtered algebras with basis +""" +#***************************************************************************** +# Copyright (C) 2014 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.categories.filtered_modules import FilteredModulesCategory + +class FilteredAlgebrasWithBasis(FilteredModulesCategory): + """ + The category of filtered algebras with a distinguished basis. + + EXAMPLES:: + + sage: C = AlgebrasWithBasis(ZZ).Filtered(); C + Category of filtered algebras with basis over Integer Ring + sage: sorted(C.super_categories(), key=str) + [Category of algebras with basis over Integer Ring, + Category of filtered algebras over Integer Ring, + Category of filtered modules with basis over Integer Ring] + + TESTS:: + + sage: TestSuite(C).run() + """ + class ParentMethods: + def graded_algebra(self): + """ + Return the associated graded algebra to ``self``. + + EXAMPLES:: + + sage: A = AlgebrasWithBasis(ZZ).Filtered().example() + sage: A.graded_algebra() + Graded Algebra of An example of a filtered module with basis: + the universal enveloping algebra of + Lie algebra of RR^3 with cross product over Integer Ring + """ + from sage.algebras.associated_graded import AssociatedGradedAlgebra + return AssociatedGradedAlgebra(self) + + class ElementMethods: + def is_homogeneous(self): + """ + Return whether this element is homogeneous. + + EXAMPLES:: + + sage: S = NonCommutativeSymmetricFunctions(QQ).S() + sage: (x, y) = (S[2], S[3]) + sage: (3*x).is_homogeneous() + True + sage: (x^3 - y^2).is_homogeneous() + True + sage: ((x + y)^2).is_homogeneous() + False + """ + degree_on_basis = self.parent().degree_on_basis + degree = None + for m in self.support(): + if degree is None: + degree = degree_on_basis(m) + else: + if degree != degree_on_basis(m): + return False + return True + + def homogeneous_degree(self): + """ + The degree of this element. + + .. NOTE:: + + This raises an error if the element is not homogeneous. + To obtain the maximum of the degrees of the homogeneous + summands, use :meth:`maximal_degree` + + .. SEEALSO:: :meth:`maximal_degree` + + EXAMPLES:: + + sage: S = NonCommutativeSymmetricFunctions(QQ).S() + sage: (x, y) = (S[2], S[3]) + sage: x.homogeneous_degree() + 2 + sage: (x^3 + 4*y^2).homogeneous_degree() + 6 + sage: ((1 + x)^3).homogeneous_degree() + Traceback (most recent call last): + ... + ValueError: element is not homogeneous + + TESTS:: + + sage: S = NonCommutativeSymmetricFunctions(QQ).S() + sage: S.zero().degree() + Traceback (most recent call last): + ... + ValueError: the zero element does not have a well-defined degree + """ + if self.is_zero(): + raise ValueError("the zero element does not have a well-defined degree") + if not self.is_homogeneous(): + raise ValueError("element is not homogeneous") + return self.parent().degree_on_basis(self.leading_support()) + + # default choice for degree; will be overridden as necessary + degree = homogeneous_degree + + def maximal_degree(self): + """ + The maximum of the degrees of the homogeneous summands. + + .. SEEALSO:: :meth:`homogeneous_degree` + + EXAMPLES:: + + sage: S = NonCommutativeSymmetricFunctions(QQ).S() + sage: (x, y) = (S[2], S[3]) + sage: x.maximal_degree() + 2 + sage: (x^3 + 4*y^2).maximal_degree() + 6 + sage: ((1 + x)^3).maximal_degree() + 6 + + TESTS:: + + sage: S = NonCommutativeSymmetricFunctions(QQ).S() + sage: S.zero().degree() + Traceback (most recent call last): + ... + ValueError: the zero element does not have a well-defined degree + """ + if self.is_zero(): + raise ValueError("the zero element does not have a well-defined degree") + degree_on_basis = self.parent().degree_on_basis + return max(degree_on_basis(m) for m in self.support()) + diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py new file mode 100644 index 00000000000..bb7e4998e20 --- /dev/null +++ b/src/sage/categories/filtered_modules.py @@ -0,0 +1,208 @@ +r""" +Filtered modules +""" +#***************************************************************************** +# Copyright (C) 2014 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_class_attribute +from sage.categories.category_types import Category_over_base_ring +from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.covariant_functorial_construction import RegressiveCovariantConstructionCategory + +class FilteredModulesCategory(RegressiveCovariantConstructionCategory, Category_over_base_ring): + def __init__(self, base_category): + """ + EXAMPLES:: + + sage: C = Algebras(QQ).Filtered() + sage: C + Category of filtered algebras over Rational Field + sage: C.base_category() + Category of algebras over Rational Field + sage: sorted(C.super_categories(), key=str) + [Category of algebras over Rational Field, + Category of filtered modules over Rational Field] + + sage: AlgebrasWithBasis(QQ).Filtered().base_ring() + Rational Field + sage: HopfAlgebrasWithBasis(QQ).Filtered().base_ring() + Rational Field + """ + super(FilteredModulesCategory, self).__init__(base_category, base_category.base_ring()) + + _functor_category = "Filtered" + + @lazy_class_attribute + def _base_category_class(cls): + """ + Recover the class of the base category. + + OUTPUT: + + A *tuple* whose first entry is the base category class. + + .. WARNING:: + + This is only used for filtered categories that are not + implemented as nested classes, and won't work otherwise. + + .. SEEALSO:: :meth:`__classcall__` + + EXAMPLES:: + + sage: from sage.categories.filtered_modules import FilteredModules + sage: FilteredModules._base_category_class + (,) + sage: from sage.categories.filtered_algebras_with_basis import FilteredAlgebrasWithBasis + sage: FilteredAlgebrasWithBasis._base_category_class + (,) + + The reason for wrapping the base category class in a tuple is + that, often, the base category class implements a + :meth:`__classget__` method which would get in the way upon + attribute access:: + + sage: F = FilteredAlgebrasWithBasis + sage: F._foo = F._base_category_class[0] + sage: F._foo + Traceback (most recent call last): + ... + AssertionError: base category class for <...AlgebrasWithBasis'> mismatch; + expected <...Algebras'>, got <...FilteredAlgebrasWithBasis'> + """ + module_name = cls.__module__.replace("filtered_","") + import sys + name = cls.__name__.replace("Filtered","") + __import__(module_name) + module = sys.modules[module_name] + return (module.__dict__[name],) + + @staticmethod + def __classcall__(cls, category, *args): + """ + Magic support for putting Filtered categories in their own file. + + EXAMPLES:: + + sage: from sage.categories.filtered_modules import FilteredModules + sage: FilteredModules(ZZ) # indirect doctest + Category of filtered modules over Integer Ring + sage: Modules(ZZ).Filtered() + Category of filtered modules over Integer Ring + sage: FilteredModules(ZZ) is Modules(ZZ).Filtered() + True + + .. TODO:: + + Generalize this support for all other functorial + constructions if at some point we have a category ``Blah`` for + which we want to implement the construction ``Blah.Foo`` in a + separate file like we do for e.g. :class:`FilteredModules`, + :class:`FilteredAlgebras`, ... + + .. SEEALSO:: :meth:`_base_category_class` + """ + base_category_class = cls._base_category_class[0] + if isinstance(category, base_category_class): + return super(FilteredModulesCategory, cls).__classcall__(cls, category, *args) + else: + return base_category_class(category, *args).Filtered() + + def _repr_object_names(self): + """ + EXAMPLES:: + + sage: AlgebrasWithBasis(QQ).Filtered() # indirect doctest + Category of filtered algebras with basis over Rational Field + """ + return "filtered {}".format(self.base_category()._repr_object_names()) + +class FilteredModules(FilteredModulesCategory): + """ + The category of filtered modules. + + EXAMPLES:: + + sage: Modules(ZZ).Filtered() + Category of filtered modules over Integer Ring + sage: Modules(ZZ).Filtered().super_categories() + [Category of modules over Integer Ring] + + TESTS:: + + sage: TestSuite(Modules(ZZ).Filtered()).run() + """ + + def extra_super_categories(self): + r""" + Adds :class:`VectorSpaces` to the super categories of ``self`` if + the base ring is a field. + + EXAMPLES:: + + sage: Modules(QQ).Filtered().extra_super_categories() + [Category of vector spaces over Rational Field] + sage: Modules(ZZ).Filtered().extra_super_categories() + [] + + This makes sure that ``Modules(QQ).Filtered()`` returns an + instance of :class:`FilteredModules` and not a join category of + an instance of this class and of ``VectorSpaces(QQ)``:: + + sage: type(Modules(QQ).Filtered()) + + + .. TODO:: + + Get rid of this workaround once there is a more systematic + approach for the alias ``Modules(QQ)`` -> ``VectorSpaces(QQ)``. + Probably the later should be a category with axiom, and + covariant constructions should play well with axioms. + """ + from sage.categories.modules import Modules + from sage.categories.fields import Fields + base_ring = self.base_ring() + if base_ring in Fields: + return [Modules(base_ring)] + else: + return [] + + class SubcategoryMethods: + + @cached_method + def Connected(self): + r""" + Return the full subcategory of the connected objects of ``self``. + + EXAMPLES:: + + sage: Modules(ZZ).Filtered().Connected() + Category of filtered connected modules over Integer Ring + sage: Coalgebras(QQ).Filtered().Connected() + Join of Category of filtered connected modules over Rational Field + and Category of coalgebras over Rational Field + sage: AlgebrasWithBasis(QQ).Filtered().Connected() + Category of filtered connected algebras with basis over Rational Field + + TESTS:: + + sage: TestSuite(Modules(ZZ).Filtered().Connected()).run() + sage: Coalgebras(QQ).Filtered().Connected.__module__ + 'sage.categories.filtered_modules' + """ + return self._with_axiom("Connected") + + class Connected(CategoryWithAxiom_over_base_ring): + pass + + class ParentMethods: + pass + + class ElementMethods: + pass + diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py new file mode 100644 index 00000000000..8db42471566 --- /dev/null +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -0,0 +1,193 @@ +r""" +Filtered modules with basis +""" +#***************************************************************************** +# Copyright (C) 2014 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.categories.filtered_modules import FilteredModulesCategory + +class FilteredModulesWithBasis(FilteredModulesCategory): + """ + The category of filtered modules with a distinguished basis. + + EXAMPLES:: + + sage: C = ModulesWithBasis(ZZ).Filtered(); C + Category of filtered modules with basis over Integer Ring + sage: sorted(C.super_categories(), key=str) + [Category of filtered modules over Integer Ring, + Category of modules with basis over Integer Ring] + sage: C is ModulesWithBasis(ZZ).Filtered() + True + + TESTS:: + + sage: TestSuite(C).run() + """ + class ParentMethods: + + # TODO: which syntax do we prefer? + # A.basis(degree = 3) + # A.basis().subset(degree=3) + + # This is related to the following design question: + # If F = (f_i)_{i\in I} is a family, should ``F.subset(degree = 3)`` + # be the elements of F of degree 3 or those whose index is of degree 3? + + def basis(self, d=None): + """ + Return the basis for (an homogeneous component of) ``self``. + + INPUT: + + - `d` -- non negative integer or ``None``, optional (default: ``None``) + + If `d` is None, returns a basis of the module. + Otherwise, returns the basis of the homogeneous component of degree `d`. + + EXAMPLES:: + + sage: A = ModulesWithBasis(ZZ).Filtered().example() + sage: A.basis(4) + Lazy family (Term map from Partitions to An example of a filtered module with basis: the free module on partitions over Integer Ring(i))_{i in Partitions of the integer 4} + + Without arguments, the full basis is returned:: + + sage: A.basis() + Lazy family (Term map from Partitions to An example of a filtered module with basis: the free module on partitions over Integer Ring(i))_{i in Partitions} + sage: A.basis() + Lazy family (Term map from Partitions to An example of a filtered module with basis: the free module on partitions over Integer Ring(i))_{i in Partitions} + """ + from sage.sets.family import Family + if d is None: + return Family(self._indices, self.monomial) + else: + return Family(self._indices.subset(size=d), self.monomial) + + class ElementMethods: + + def is_homogeneous(self): + """ + Return whether ``self`` is homogeneous. + + EXAMPLES:: + + sage: A = ModulesWithBasis(ZZ).Filtered().example() + sage: x=A(Partition((3,2,1))) + sage: y=A(Partition((4,4,1))) + sage: z=A(Partition((2,2,2))) + sage: (3*x).is_homogeneous() + True + sage: (x - y).is_homogeneous() + False + sage: (x+2*z).is_homogeneous() + True + """ + degree_on_basis = self.parent().degree_on_basis + degree = None + for m in self.support(): + if degree is None: + degree = degree_on_basis(m) + else: + if degree != degree_on_basis(m): + return False + return True + + def degree(self): + """ + The degree of this element in the filtered module. + + .. NOTE:: + + This raises an error if the element is not homogeneous. + Another implementation option would be to return the + maximum of the degrees of the homogeneous summands. + + EXAMPLES:: + + sage: A = ModulesWithBasis(ZZ).Filtered().example() + sage: x = A(Partition((3,2,1))) + sage: y = A(Partition((4,4,1))) + sage: z = A(Partition((2,2,2))) + sage: x.degree() + 6 + sage: (x + 2*z).degree() + 6 + sage: (y - x).degree() + Traceback (most recent call last): + ... + ValueError: element is not homogeneous + """ + if not self.support(): + raise ValueError("the zero element does not have a well-defined degree") + if not self.is_homogeneous(): + raise ValueError("element is not homogeneous") + return self.parent().degree_on_basis(self.leading_support()) + + def homogeneous_component(self, n): + """ + Return the homogeneous component of degree ``n`` of this + element. + + EXAMPLES:: + + sage: A = ModulesWithBasis(ZZ).Filtered().example() + sage: x = A.an_element(); x + 2*P[] + 2*P[1] + 3*P[2] + sage: x.homogeneous_component(-1) + 0 + sage: x.homogeneous_component(0) + 2*P[] + sage: x.homogeneous_component(1) + 2*P[1] + sage: x.homogeneous_component(2) + 3*P[2] + sage: x.homogeneous_component(3) + 0 + + TESTS: + + Check that this really return ``A.zero()`` and not a plain ``0``:: + + sage: x.homogeneous_component(3).parent() is A + True + """ + degree_on_basis = self.parent().degree_on_basis + return self.parent().sum_of_terms((i, c) + for (i, c) in self + if degree_on_basis(i) == n) + + def truncate(self, n): + """ + Return the sum of the homogeneous components of degree + strictly less than ``n`` of this element. + + EXAMPLES:: + + sage: A = ModulesWithBasis(ZZ).Filtered().example() + sage: x = A.an_element(); x + 2*P[] + 2*P[1] + 3*P[2] + sage: x.truncate(0) + 0 + sage: x.truncate(1) + 2*P[] + sage: x.truncate(2) + 2*P[] + 2*P[1] + sage: x.truncate(3) + 2*P[] + 2*P[1] + 3*P[2] + + TESTS: + + Check that this really return ``A.zero()`` and not a plain ``0``:: + + sage: x.truncate(0).parent() is A + True + """ + degree_on_basis = self.parent().degree_on_basis + return self.parent().sum_of_terms((i, c) for (i, c) in self + if degree_on_basis(i) < n) + diff --git a/src/sage/categories/graded_algebras.py b/src/sage/categories/graded_algebras.py index 98e89f81993..f9e59a137e1 100644 --- a/src/sage/categories/graded_algebras.py +++ b/src/sage/categories/graded_algebras.py @@ -20,16 +20,39 @@ class GradedAlgebras(GradedModulesCategory): sage: GradedAlgebras(ZZ) Category of graded algebras over Integer Ring sage: GradedAlgebras(ZZ).super_categories() - [Category of algebras over Integer Ring, + [Category of filtered algebras over Integer Ring, Category of graded modules over Integer Ring] TESTS:: sage: TestSuite(GradedAlgebras(ZZ)).run() """ + def extra_super_categories(self): + r""" + Adds :class:`FilteredAlgebras` to the super categories of ``self`` + since every graded algebra admits a filtraion. + + EXAMPLES:: + + sage: GradedAlgebras(ZZ).extra_super_categories() + [Category of filtered algebras over Integer Ring] + """ + from sage.categories.filtered_algebras import FilteredAlgebras + return [FilteredAlgebras(self.base_ring())] class ParentMethods: - pass + def graded_algebra(self): + """ + Return the associated graded algebra to ``self``. + + EXAMPLES:: + + sage: m = SymmetricFunctions(QQ).m() + sage: m.graded_algebra() is m + True + """ + return self class ElementMethods: pass + diff --git a/src/sage/categories/graded_algebras_with_basis.py b/src/sage/categories/graded_algebras_with_basis.py index 66a608fad2e..e45ece1de8e 100644 --- a/src/sage/categories/graded_algebras_with_basis.py +++ b/src/sage/categories/graded_algebras_with_basis.py @@ -20,7 +20,7 @@ class GradedAlgebrasWithBasis(GradedModulesCategory): sage: C = GradedAlgebrasWithBasis(ZZ); C Category of graded algebras with basis over Integer Ring sage: sorted(C.super_categories(), key=str) - [Category of algebras with basis over Integer Ring, + [Category of filtered algebras with basis over Integer Ring, Category of graded algebras over Integer Ring, Category of graded modules with basis over Integer Ring] @@ -28,107 +28,34 @@ class GradedAlgebrasWithBasis(GradedModulesCategory): sage: TestSuite(C).run() """ + def extra_super_categories(self): + r""" + Adds :class:`FilteredAlgebras` to the super categories of ``self`` + since every graded algebra admits a filtraion. - class ParentMethods: - pass - - class ElementMethods: - def is_homogeneous(self): - """ - Return whether this element is homogeneous. - - EXAMPLES:: + EXAMPLES:: - sage: S = NonCommutativeSymmetricFunctions(QQ).S() - sage: (x, y) = (S[2], S[3]) - sage: (3*x).is_homogeneous() - True - sage: (x^3 - y^2).is_homogeneous() - True - sage: ((x + y)^2).is_homogeneous() - False - """ - degree_on_basis = self.parent().degree_on_basis - degree = None - for m in self.support(): - if degree is None: - degree = degree_on_basis(m) - else: - if degree != degree_on_basis(m): - return False - return True + sage: GradedAlgebras(ZZ).extra_super_categories() + [Category of filtered algebras over Integer Ring] + """ + from sage.categories.filtered_algebras_with_basis import FilteredAlgebrasWithBasis + return [FilteredAlgebrasWithBasis(self.base_ring())] - def homogeneous_degree(self): + class ParentMethods: + # This needs to be copied in GradedAlgebras because we need to have + # FilteredAlgebrasWithBasis as an extra super category + def graded_algebra(self): """ - The degree of this element. - - .. note:: - - This raises an error if the element is not homogeneous. - To obtain the maximum of the degrees of the homogeneous - summands, use :meth:`maximal_degree` - - .. seealso: :meth:`maximal_degree` + Return the associated graded algebra to ``self``. EXAMPLES:: - sage: S = NonCommutativeSymmetricFunctions(QQ).S() - sage: (x, y) = (S[2], S[3]) - sage: x.homogeneous_degree() - 2 - sage: (x^3 + 4*y^2).homogeneous_degree() - 6 - sage: ((1 + x)^3).homogeneous_degree() - Traceback (most recent call last): - ... - ValueError: Element is not homogeneous. - - TESTS:: - - sage: S = NonCommutativeSymmetricFunctions(QQ).S() - sage: S.zero().degree() - Traceback (most recent call last): - ... - ValueError: The zero element does not have a well-defined degree. - """ - if self.is_zero(): - raise ValueError("The zero element does not have a well-defined degree.") - try: - assert self.is_homogeneous() - return self.parent().degree_on_basis(self.leading_support()) - except AssertionError: - raise ValueError("Element is not homogeneous.") - - # default choice for degree; will be overridden as necessary - degree = homogeneous_degree - - def maximal_degree(self): + sage: m = SymmetricFunctions(QQ).m() + sage: m.graded_algebra() is m + True """ - The maximum of the degrees of the homogeneous summands. - - .. seealso: :meth:`homogeneous_degree` + return self - EXAMPLES:: - - sage: S = NonCommutativeSymmetricFunctions(QQ).S() - sage: (x, y) = (S[2], S[3]) - sage: x.maximal_degree() - 2 - sage: (x^3 + 4*y^2).maximal_degree() - 6 - sage: ((1 + x)^3).maximal_degree() - 6 - - TESTS:: + class ElementMethods: + pass - sage: S = NonCommutativeSymmetricFunctions(QQ).S() - sage: S.zero().degree() - Traceback (most recent call last): - ... - ValueError: The zero element does not have a well-defined degree. - """ - if self.is_zero(): - raise ValueError("The zero element does not have a well-defined degree.") - else: - degree_on_basis = self.parent().degree_on_basis - return max(degree_on_basis(m) for m in self.support()) diff --git a/src/sage/categories/graded_modules.py b/src/sage/categories/graded_modules.py index 99c005c8052..73d9136783c 100644 --- a/src/sage/categories/graded_modules.py +++ b/src/sage/categories/graded_modules.py @@ -26,7 +26,7 @@ def __init__(self, base_category): sage: C.base_category() Category of algebras over Rational Field sage: sorted(C.super_categories(), key=str) - [Category of algebras over Rational Field, + [Category of filtered algebras over Rational Field, Category of graded modules over Rational Field] sage: AlgebrasWithBasis(QQ).Graded().base_ring() @@ -120,86 +120,44 @@ def _repr_object_names(self): """ return "graded {}".format(self.base_category()._repr_object_names()) -class GradedModules(GradedModulesCategory): - """ - The category of graded modules. - - EXAMPLES:: - - sage: GradedModules(ZZ) - Category of graded modules over Integer Ring - sage: GradedModules(ZZ).super_categories() - [Category of modules over Integer Ring] - - TESTS:: - - sage: TestSuite(GradedModules(ZZ)).run() - """ - def extra_super_categories(self): r""" - Adds :class:`VectorSpaces` to the super categories of ``self`` if - the base ring is a field. + Adds :class:`FilteredModules` to the super categories of ``self`` + since every graded module admits a filtraion. EXAMPLES:: - sage: Modules(QQ).Graded().extra_super_categories() - [Category of vector spaces over Rational Field] - sage: Modules(ZZ).Graded().extra_super_categories() - [] - - This makes sure that ``Modules(QQ).Graded()`` returns an - instance of :class:`GradedModules` and not a join category of - an instance of this class and of ``VectorSpaces(QQ)``:: - - sage: type(Modules(QQ).Graded()) - - - .. TODO:: - - Get rid of this workaround once there is a more systematic - approach for the alias ``Modules(QQ)`` -> ``VectorSpaces(QQ)``. - Probably the later should be a category with axiom, and - covariant constructions should play well with axioms. + sage: GradedModules(ZZ).extra_super_categories() + [Category of filtered modules over Integer Ring] """ - from sage.categories.modules import Modules - from sage.categories.fields import Fields - base_ring = self.base_ring() - if base_ring in Fields: - return [Modules(base_ring)] - else: - return [] + from sage.categories.filtered_modules import FilteredModules + return [FilteredModules(self.base_ring())] - class SubcategoryMethods: +class GradedModules(GradedModulesCategory): + r""" + The category of graded modules. - @cached_method - def Connected(self): - r""" - Return the full subcategory of the connected objects of ``self``. + We consider every graded module `M = \bigoplus_i M_i` as a + filtered module under the (natural) filtration of - EXAMPLES:: + .. MATH:: - sage: Modules(ZZ).Graded().Connected() - Category of graded connected modules over Integer Ring - sage: Coalgebras(QQ).Graded().Connected() - Join of Category of graded connected modules over Rational Field - and Category of coalgebras over Rational Field - sage: GradedAlgebrasWithBasis(QQ).Connected() - Category of graded connected algebras with basis over Rational Field + F_i = \bigoplus_{j < i} M_j. - TESTS:: + EXAMPLES:: - sage: TestSuite(Modules(ZZ).Graded().Connected()).run() - sage: Coalgebras(QQ).Graded().Connected.__module__ - 'sage.categories.graded_modules' - """ - return self._with_axiom("Connected") + sage: GradedModules(ZZ) + Category of graded modules over Integer Ring + sage: GradedModules(ZZ).super_categories() + [Category of filtered modules over Integer Ring] - class Connected(CategoryWithAxiom_over_base_ring): - pass + TESTS:: + sage: TestSuite(GradedModules(ZZ)).run() + """ class ParentMethods: pass class ElementMethods: pass + diff --git a/src/sage/categories/graded_modules_with_basis.py b/src/sage/categories/graded_modules_with_basis.py index 9bb99fc5a95..10671e3b307 100644 --- a/src/sage/categories/graded_modules_with_basis.py +++ b/src/sage/categories/graded_modules_with_basis.py @@ -20,8 +20,8 @@ class GradedModulesWithBasis(GradedModulesCategory): sage: C = GradedModulesWithBasis(ZZ); C Category of graded modules with basis over Integer Ring sage: sorted(C.super_categories(), key=str) - [Category of graded modules over Integer Ring, - Category of modules with basis over Integer Ring] + [Category of filtered modules with basis over Integer Ring, + Category of graded modules over Integer Ring] sage: C is ModulesWithBasis(ZZ).Graded() True @@ -29,165 +29,22 @@ class GradedModulesWithBasis(GradedModulesCategory): sage: TestSuite(C).run() """ - class ParentMethods: - - # TODO: which syntax do we prefer? - # A.basis(degree = 3) - # A.basis().subset(degree=3) - - # This is related to the following design question: - # If F = (f_i)_{i\in I} is a family, should ``F.subset(degree = 3)`` - # be the elements of F of degree 3 or those whose index is of degree 3? - - def basis(self, d=None): - """ - Returns the basis for (an homogeneous component of) this graded module - - INPUT: - - - `d` -- non negative integer or ``None``, optional (default: ``None``) - - If `d` is None, returns a basis of the module. - Otherwise, returns the basis of the homogeneous component of degree `d`. + def extra_super_categories(self): + r""" + Adds :class:`FilteredModulesWithBasis` to the super categories + of ``self`` since every graded module admits a filtraion. - EXAMPLES:: + EXAMPLES:: - sage: A = GradedModulesWithBasis(ZZ).example() - sage: A.basis(4) - Lazy family (Term map from Partitions to An example of a graded module with basis: the free module on partitions over Integer Ring(i))_{i in Partitions of the integer 4} + sage: GradedModulesWithBasis(ZZ).extra_super_categories() + [Category of filtered modules with basis over Integer Ring] + """ + from sage.categories.filtered_modules_with_basis import FilteredModulesWithBasis + return [FilteredModulesWithBasis(self.base_ring())] - Without arguments, the full basis is returned:: - - sage: A.basis() - Lazy family (Term map from Partitions to An example of a graded module with basis: the free module on partitions over Integer Ring(i))_{i in Partitions} - sage: A.basis() - Lazy family (Term map from Partitions to An example of a graded module with basis: the free module on partitions over Integer Ring(i))_{i in Partitions} - """ - from sage.sets.family import Family - if d is None: - return Family(self._indices, self.monomial) - else: - return Family(self._indices.subset(size=d), self.monomial) + class ParentMethods: + pass class ElementMethods: + pass - def is_homogeneous(self): - """ - Return whether this element is homogeneous. - - EXAMPLES:: - - sage: A = GradedModulesWithBasis(ZZ).example() - sage: x=A(Partition((3,2,1))) - sage: y=A(Partition((4,4,1))) - sage: z=A(Partition((2,2,2))) - sage: (3*x).is_homogeneous() - True - sage: (x - y).is_homogeneous() - False - sage: (x+2*z).is_homogeneous() - True - """ - degree_on_basis = self.parent().degree_on_basis - degree = None - for m in self.support(): - if degree is None: - degree = degree_on_basis(m) - else: - if degree != degree_on_basis(m): - return False - return True - - def degree(self): - """ - The degree of this element in the graded module. - - .. note:: - - This raises an error if the element is not homogeneous. - Another implementation option would be to return the - maximum of the degrees of the homogeneous summands. - - EXAMPLES:: - - sage: A = GradedModulesWithBasis(ZZ).example() - sage: x = A(Partition((3,2,1))) - sage: y = A(Partition((4,4,1))) - sage: z = A(Partition((2,2,2))) - sage: x.degree() - 6 - sage: (x + 2*z).degree() - 6 - sage: (y - x).degree() - Traceback (most recent call last): - ... - ValueError: Element is not homogeneous. - """ - if not self.support(): - raise ValueError("The zero element does not have a well-defined degree.") - if self.is_homogeneous(): - return self.parent().degree_on_basis(self.leading_support()) - else: - raise ValueError("Element is not homogeneous.") - - def homogeneous_component(self, n): - """ - Return the homogeneous component of degree ``n`` of this - element. - - EXAMPLES:: - - sage: A = GradedModulesWithBasis(ZZ).example() - sage: x = A.an_element(); x - 2*P[] + 2*P[1] + 3*P[2] - sage: x.homogeneous_component(-1) - 0 - sage: x.homogeneous_component(0) - 2*P[] - sage: x.homogeneous_component(1) - 2*P[1] - sage: x.homogeneous_component(2) - 3*P[2] - sage: x.homogeneous_component(3) - 0 - - TESTS: - - Check that this really return ``A.zero()`` and not a plain ``0``:: - - sage: x.homogeneous_component(3).parent() is A - True - """ - degree_on_basis = self.parent().degree_on_basis - return self.parent().sum_of_terms((i, c) - for (i, c) in self - if degree_on_basis(i) == n) - - def truncate(self, n): - """ - Return the sum of the homogeneous components of degree ``< n`` of this element - - EXAMPLES:: - - sage: A = GradedModulesWithBasis(ZZ).example() - sage: x = A.an_element(); x - 2*P[] + 2*P[1] + 3*P[2] - sage: x.truncate(0) - 0 - sage: x.truncate(1) - 2*P[] - sage: x.truncate(2) - 2*P[] + 2*P[1] - sage: x.truncate(3) - 2*P[] + 2*P[1] + 3*P[2] - - TESTS: - - Check that this really return ``A.zero()`` and not a plain ``0``:: - - sage: x.truncate(0).parent() is A - True - """ - degree_on_basis = self.parent().degree_on_basis - return self.parent().sum_of_terms((i, c) for (i, c) in self - if degree_on_basis(i) < n) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 8bb11ba4129..e10f241e364 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -325,6 +325,43 @@ def FiniteDimensional(self): """ return self._with_axiom("FiniteDimensional") + @cached_method + def Filtered(self, base_ring=None): + r""" + Return the subcategory of the filtered objects of ``self``. + + INPUT:: + + - ``base_ring`` -- this is ignored + + EXAMPLES:: + + sage: Modules(ZZ).Filtered() + Category of filtered modules over Integer Ring + + sage: Coalgebras(QQ).Filtered() + Join of Category of filtered modules over Rational Field + and Category of coalgebras over Rational Field + + sage: AlgebrasWithBasis(QQ).Filtered() + Category of filtered algebras with basis over Rational Field + + .. TODO:: + + - Explain why this does not commute with :meth:`WithBasis` + - Improve the support for covariant functorial + constructions categories over a base ring so as to + get rid of the ``base_ring`` argument. + + TESTS:: + + sage: Coalgebras(QQ).Graded.__module__ + 'sage.categories.modules' + """ + assert base_ring is None or base_ring is self.base_ring() + from sage.categories.filtered_modules import FilteredModulesCategory + return FilteredModulesCategory.category_of(self) + @cached_method def Graded(self, base_ring=None): r""" @@ -407,6 +444,7 @@ def extra_super_categories(self): else: return [] + Filtered = LazyImport('sage.categories.filtered_modules', 'FilteredModules') Graded = LazyImport('sage.categories.graded_modules', 'GradedModules') WithBasis = LazyImport('sage.categories.modules_with_basis', 'ModulesWithBasis') diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index aac486b3931..93a1ced2c8b 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -179,6 +179,7 @@ def is_abelian(self): return self.base_ring().is_field() FiniteDimensional = LazyImport('sage.categories.finite_dimensional_modules_with_basis', 'FiniteDimensionalModulesWithBasis') + Filtered = LazyImport('sage.categories.filtered_modules_with_basis', 'FilteredModulesWithBasis') Graded = LazyImport('sage.categories.graded_modules_with_basis', 'GradedModulesWithBasis') class ParentMethods: From bb234ec71d1276c6cc14320074b3de6fdc606192 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 20 Oct 2014 08:09:20 -0700 Subject: [PATCH 006/421] Use default_super_categories instead of extra_super_categories. --- src/sage/categories/graded_algebras.py | 13 ------ .../categories/graded_algebras_with_basis.py | 13 ------ src/sage/categories/graded_modules.py | 45 +++++++++++++++---- .../categories/graded_modules_with_basis.py | 13 ------ 4 files changed, 37 insertions(+), 47 deletions(-) diff --git a/src/sage/categories/graded_algebras.py b/src/sage/categories/graded_algebras.py index f9e59a137e1..48f47f4068b 100644 --- a/src/sage/categories/graded_algebras.py +++ b/src/sage/categories/graded_algebras.py @@ -27,19 +27,6 @@ class GradedAlgebras(GradedModulesCategory): sage: TestSuite(GradedAlgebras(ZZ)).run() """ - def extra_super_categories(self): - r""" - Adds :class:`FilteredAlgebras` to the super categories of ``self`` - since every graded algebra admits a filtraion. - - EXAMPLES:: - - sage: GradedAlgebras(ZZ).extra_super_categories() - [Category of filtered algebras over Integer Ring] - """ - from sage.categories.filtered_algebras import FilteredAlgebras - return [FilteredAlgebras(self.base_ring())] - class ParentMethods: def graded_algebra(self): """ diff --git a/src/sage/categories/graded_algebras_with_basis.py b/src/sage/categories/graded_algebras_with_basis.py index e45ece1de8e..d0ff3160643 100644 --- a/src/sage/categories/graded_algebras_with_basis.py +++ b/src/sage/categories/graded_algebras_with_basis.py @@ -28,19 +28,6 @@ class GradedAlgebrasWithBasis(GradedModulesCategory): sage: TestSuite(C).run() """ - def extra_super_categories(self): - r""" - Adds :class:`FilteredAlgebras` to the super categories of ``self`` - since every graded algebra admits a filtraion. - - EXAMPLES:: - - sage: GradedAlgebras(ZZ).extra_super_categories() - [Category of filtered algebras over Integer Ring] - """ - from sage.categories.filtered_algebras_with_basis import FilteredAlgebrasWithBasis - return [FilteredAlgebrasWithBasis(self.base_ring())] - class ParentMethods: # This needs to be copied in GradedAlgebras because we need to have # FilteredAlgebrasWithBasis as an extra super category diff --git a/src/sage/categories/graded_modules.py b/src/sage/categories/graded_modules.py index 73d9136783c..02caab1ddcd 100644 --- a/src/sage/categories/graded_modules.py +++ b/src/sage/categories/graded_modules.py @@ -11,6 +11,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_class_attribute +from sage.categories.category import Category from sage.categories.category_types import Category_over_base_ring from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring from sage.categories.covariant_functorial_construction import RegressiveCovariantConstructionCategory @@ -120,18 +121,46 @@ def _repr_object_names(self): """ return "graded {}".format(self.base_category()._repr_object_names()) - def extra_super_categories(self): + @classmethod + def default_super_categories(cls, category, *args): r""" - Adds :class:`FilteredModules` to the super categories of ``self`` - since every graded module admits a filtraion. + Return the default super categories of ``category.Graded()`` - EXAMPLES:: + Mathematical meaning: every graded category is a filtered category + with the (implicit) filtration of `F_i = \bigoplus_{j \leq i} G_j`. + + INPUT: + + - ``cls`` -- the class ``QuotientsCategory`` + - ``category`` -- a category `Cat` + + OUTPUT: a (join) category + + In practice, this returns ``category.Filtered()``, joined + together with the result of the method + :meth:`RegressiveCovariantConstructionCategory.default_super_categories() ` + (that is the join of ``category`` and ``cat.Filtered()`` for + each ``cat`` in the super categories of ``category``). + + EXAMPLES: + + Consider ``category=Algebras()``, which has ``cat=Modules()`` + as super category. Then, a grading of an algebra `G` + is also a filtration of `G`:: + + sage: Algebras(QQ).Graded().super_categories() + [Category of filtered algebras over Rational Field, + Category of graded modules over Rational Field] + + This resulted from the following call:: - sage: GradedModules(ZZ).extra_super_categories() - [Category of filtered modules over Integer Ring] + sage: sage.categories.graded_modules.GradedModulesCategory.default_super_categories(Algebras(QQ)) + Join of Category of filtered algebras over Rational Field + and Category of graded modules over Rational Field """ - from sage.categories.filtered_modules import FilteredModules - return [FilteredModules(self.base_ring())] + cat = super(GradedModulesCategory, cls).default_super_categories(category, *args) + return Category.join([category.Filtered(), + cat]) class GradedModules(GradedModulesCategory): r""" diff --git a/src/sage/categories/graded_modules_with_basis.py b/src/sage/categories/graded_modules_with_basis.py index 10671e3b307..a1c4a5d5c2e 100644 --- a/src/sage/categories/graded_modules_with_basis.py +++ b/src/sage/categories/graded_modules_with_basis.py @@ -29,19 +29,6 @@ class GradedModulesWithBasis(GradedModulesCategory): sage: TestSuite(C).run() """ - def extra_super_categories(self): - r""" - Adds :class:`FilteredModulesWithBasis` to the super categories - of ``self`` since every graded module admits a filtraion. - - EXAMPLES:: - - sage: GradedModulesWithBasis(ZZ).extra_super_categories() - [Category of filtered modules with basis over Integer Ring] - """ - from sage.categories.filtered_modules_with_basis import FilteredModulesWithBasis - return [FilteredModulesWithBasis(self.base_ring())] - class ParentMethods: pass From 5f6337908a43200ef3283e6dd24f4f6c6da66ee9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 29 Oct 2014 15:46:50 -0700 Subject: [PATCH 007/421] Addressing Darij's comments. --- src/sage/algebras/associated_graded.py | 42 ++++++++++++++++++- .../examples/filtered_algebras_with_basis.py | 11 ++++- src/sage/categories/filtered_algebras.py | 6 ++- .../filtered_algebras_with_basis.py | 7 +++- src/sage/categories/filtered_modules.py | 9 +++- .../categories/filtered_modules_with_basis.py | 18 +++++--- 6 files changed, 81 insertions(+), 12 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index 4f9e6e925d2..8df63023460 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -15,6 +15,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod +from copy import copy from sage.categories.algebras_with_basis import AlgebrasWithBasis from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis @@ -37,6 +38,35 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): INPUT: - ``A`` -- a filtered algebra + + EXAMPLES: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: x,y,z = map(lambda s: grA.algebra_generators()[s], ['x','y','z']) + sage: x + bar(U['x']) + sage: y * x + z + bar(U['x']*U['y']) + bar(U['z']) + sage: A(y) * A(x) + A(z) + U['x']*U['y'] + + We note that the conversion between ``A`` and ``grA`` is the canonical + ``QQ``-module isomorphism:: + + sage: grA(A.an_element()) + bar(U['x']^2*U['y']^2*U['z']^3) + 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) + bar(U['x']) + 2*bar(1) + sage: A(grelt) == elt + True + + .. TODO:: + + The algebra ``A`` must currently be an instance of (a subclass of) + :class:`CombinatorialFreeModule`. This should work with any algebra + with a basis. """ def __init__(self, A, category=None): """ @@ -51,16 +81,21 @@ def __init__(self, A, category=None): if A not in AlgebrasWithBasis(A.base_ring()).Filtered(): raise ValueError("the base algebra must be filtered") self._A = A + if category is None: category = A.category().Graded() - from copy import copy opts = copy(A.print_options()) if not opts['prefix'] and not opts['bracket']: opts['bracket'] = '(' opts['prefix'] = opts['prefix'] + 'bar' + CombinatorialFreeModule.__init__(self, A.base_ring(), A.indices(), category=category, **opts) + # Setup the conversion back + phi = self.module_morphism(lambda x: A.monomial(x), codomain=A) + self._A.register_conversion(phi) + def _repr_(self): """ Return a string representation of ``self``. @@ -92,6 +127,11 @@ def _element_constructor_(self, x): """ Construct an element of ``self`` from ``x``. + .. NOTE:: + + This constructs an element from the filtered algebra ``A`` + by the canonical module isomorphism. + EXAMPLES:: sage: A = Algebras(QQ).WithBasis().Filtered().example() diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py index 0b864fe3aa4..df4e7bcfa95 100644 --- a/src/sage/categories/examples/filtered_algebras_with_basis.py +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -14,7 +14,7 @@ from sage.sets.family import Family from sage.misc.misc import powerset -class IntersectionFilteredAlgebra(CombinatorialFreeModule): +class PBWBasisCrossProduct(CombinatorialFreeModule): r""" This class illustrates an implementation of a filtered algebra with basis: the universal enveloping algebra of the Lie algebra @@ -32,6 +32,9 @@ class IntersectionFilteredAlgebra(CombinatorialFreeModule): - A set of algebra generators -- the set of generators `x,y,z`. + - The index of the unit element -- the unit element in the monoid + of monomials. + - A product -- this is given on basis elements by using :meth:`product_on_basis`. @@ -115,6 +118,9 @@ def degree_on_basis(self, m): """ return len(m) + # TODO: This is a general procedure of expanding multiplication defined + # on generators to arbitrary monomials and could likely be factored out + # and be useful elsewhere. def product_on_basis(self, s, t): """ Return the product of two basis elements indexed by ``s`` and ``t``. @@ -142,6 +148,7 @@ def product_on_basis(self, s, t): if len(t) == 1: if len(s) == 1: + # Do the product of the generators a = s.leading_support() b = t.leading_support() cur = self.monomial(s*t) @@ -165,5 +172,5 @@ def product_on_basis(self, s, t): cur = cur * self.monomial(self._indices.gen(a)) return cur -Example = IntersectionFilteredAlgebra +Example = PBWBasisCrossProduct diff --git a/src/sage/categories/filtered_algebras.py b/src/sage/categories/filtered_algebras.py index f03b2fb5877..ccb050a7ae9 100644 --- a/src/sage/categories/filtered_algebras.py +++ b/src/sage/categories/filtered_algebras.py @@ -12,9 +12,13 @@ from sage.categories.filtered_modules import FilteredModulesCategory class FilteredAlgebras(FilteredModulesCategory): - """ + r""" The category of filtered algebras. + An algebra `A` over `R` is *filtered* if `A` is a filtered `R`-module + such that `F_i \cdot F_j \subseteq F_{i+j}` for all `i, j` in the + filteration group. + EXAMPLES:: sage: Algebras(ZZ).Filtered() diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index d9d3fe61a96..6a1ea5ef1e7 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -46,7 +46,10 @@ def graded_algebra(self): class ElementMethods: def is_homogeneous(self): """ - Return whether this element is homogeneous. + Return whether ``self`` is homogeneous. + + An element `x` is homogeneous if `x \in F_i \setminus F_{i-1}` + for some `i`. EXAMPLES:: @@ -71,7 +74,7 @@ def is_homogeneous(self): def homogeneous_degree(self): """ - The degree of this element. + The degree of ``self``. .. NOTE:: diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index bb7e4998e20..655a1b1dc53 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -1,5 +1,7 @@ r""" Filtered modules + +We require all `F_i \setminus F_{i-1}` to be modules for all `i`. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw @@ -123,9 +125,14 @@ def _repr_object_names(self): return "filtered {}".format(self.base_category()._repr_object_names()) class FilteredModules(FilteredModulesCategory): - """ + r""" The category of filtered modules. + A `R`-module `M` is *filtered* if there exists a `R`-module + isomorphism `A = \bigoplus_{i \in I} F_i`, where `I` is a + totally ordered additive abelian group, such that + `F_{i-1} \subseteq F_i` for all `i \in I`. + EXAMPLES:: sage: Modules(ZZ).Filtered() diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 8db42471566..0be47d2c722 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -44,10 +44,12 @@ def basis(self, d=None): INPUT: - - `d` -- non negative integer or ``None``, optional (default: ``None``) + - ``d`` -- (optional, default ``None``) non negative integer + or ``None`` - If `d` is None, returns a basis of the module. - Otherwise, returns the basis of the homogeneous component of degree `d`. + If ``d`` is ``None``, returns a basis of the module. + Otherwise, returns the basis of the homogeneous component + of degree ``d`` (i.e., of ``F_d \setminus F_{d-1}``). EXAMPLES:: @@ -74,6 +76,9 @@ def is_homogeneous(self): """ Return whether ``self`` is homogeneous. + An element `x` is homogeneous if `x \in F_i \setminus F_{i-1}` + for some `i`. + EXAMPLES:: sage: A = ModulesWithBasis(ZZ).Filtered().example() @@ -99,7 +104,10 @@ def is_homogeneous(self): def degree(self): """ - The degree of this element in the filtered module. + The degree of ``self`` in the filtered module. + + The degree of an element `x` is the value `i` such that + `x \in F_i \setminus F_{i-1}`. .. NOTE:: @@ -164,7 +172,7 @@ def homogeneous_component(self, n): def truncate(self, n): """ Return the sum of the homogeneous components of degree - strictly less than ``n`` of this element. + strictly less than ``n`` of ``self``. EXAMPLES:: From 2aec8bdaeba3c34478807342b5659b8e8ceca2f4 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 29 Oct 2014 15:50:49 -0700 Subject: [PATCH 008/421] Fixing some typos. --- src/sage/categories/filtered_algebras.py | 2 +- src/sage/categories/filtered_modules.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/filtered_algebras.py b/src/sage/categories/filtered_algebras.py index ccb050a7ae9..7a91de609ba 100644 --- a/src/sage/categories/filtered_algebras.py +++ b/src/sage/categories/filtered_algebras.py @@ -17,7 +17,7 @@ class FilteredAlgebras(FilteredModulesCategory): An algebra `A` over `R` is *filtered* if `A` is a filtered `R`-module such that `F_i \cdot F_j \subseteq F_{i+j}` for all `i, j` in the - filteration group. + filtration group. EXAMPLES:: diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index 655a1b1dc53..caf248febea 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -129,7 +129,7 @@ class FilteredModules(FilteredModulesCategory): The category of filtered modules. A `R`-module `M` is *filtered* if there exists a `R`-module - isomorphism `A = \bigoplus_{i \in I} F_i`, where `I` is a + isomorphism `A = \bigcup_{i \in I} F_i`, where `I` is a totally ordered additive abelian group, such that `F_{i-1} \subseteq F_i` for all `i \in I`. From 592ba1176ae7a337c40792c0da3d439831ea3dc0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 31 Oct 2014 14:57:59 -0700 Subject: [PATCH 009/421] Some minor doc tweaks. --- src/sage/categories/filtered_algebras_with_basis.py | 2 +- src/sage/categories/filtered_modules.py | 4 ++-- src/sage/categories/filtered_modules_with_basis.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 6a1ea5ef1e7..f5dbdef0406 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -45,7 +45,7 @@ def graded_algebra(self): class ElementMethods: def is_homogeneous(self): - """ + r""" Return whether ``self`` is homogeneous. An element `x` is homogeneous if `x \in F_i \setminus F_{i-1}` diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index caf248febea..02762f2d3c8 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -1,7 +1,7 @@ r""" Filtered modules -We require all `F_i \setminus F_{i-1}` to be modules for all `i`. +We require all `F_i / F_{i-1}` to be modules for all `i`. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw @@ -168,7 +168,7 @@ def extra_super_categories(self): Get rid of this workaround once there is a more systematic approach for the alias ``Modules(QQ)`` -> ``VectorSpaces(QQ)``. - Probably the later should be a category with axiom, and + Probably the latter should be a category with axiom, and covariant constructions should play well with axioms. """ from sage.categories.modules import Modules diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 0be47d2c722..21f02012141 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -39,7 +39,7 @@ class ParentMethods: # be the elements of F of degree 3 or those whose index is of degree 3? def basis(self, d=None): - """ + r""" Return the basis for (an homogeneous component of) ``self``. INPUT: @@ -49,7 +49,7 @@ def basis(self, d=None): If ``d`` is ``None``, returns a basis of the module. Otherwise, returns the basis of the homogeneous component - of degree ``d`` (i.e., of ``F_d \setminus F_{d-1}``). + of degree ``d`` (i.e., of `F_d \setminus F_{d-1}`). EXAMPLES:: @@ -73,7 +73,7 @@ def basis(self, d=None): class ElementMethods: def is_homogeneous(self): - """ + r""" Return whether ``self`` is homogeneous. An element `x` is homogeneous if `x \in F_i \setminus F_{i-1}` @@ -103,7 +103,7 @@ def is_homogeneous(self): return True def degree(self): - """ + r""" The degree of ``self`` in the filtered module. The degree of an element `x` is the value `i` such that From 60beb17df13f5399d9ce13f21984e903f77d3ea9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 31 Oct 2014 15:00:09 -0700 Subject: [PATCH 010/421] More explicit documentation. --- src/sage/categories/filtered_modules.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index 02762f2d3c8..caa2b271ea5 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -1,7 +1,8 @@ r""" Filtered modules -We require all `F_i / F_{i-1}` to be modules for all `i`. +Consider a filtered module `M = \cup_{i \in I} F_i`. We require +all `F_i / F_{i-1}` to be modules for all `i`. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw From 9d567ea2aaa030f51dbf2e966c8584bd2d8489ee Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Sun, 9 Nov 2014 11:26:12 -0800 Subject: [PATCH 011/421] clarifying doc, or maybe obscuring it -- needs to be proofread --- src/sage/algebras/associated_graded.py | 76 +++++++++++++++--- .../examples/filtered_algebras_with_basis.py | 5 +- .../examples/filtered_modules_with_basis.py | 2 +- src/sage/categories/filtered_algebras.py | 19 ++++- .../filtered_algebras_with_basis.py | 15 ++-- src/sage/categories/filtered_modules.py | 44 +++++++---- .../categories/filtered_modules_with_basis.py | 77 ++++++++++++++++--- 7 files changed, 188 insertions(+), 50 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index 8df63023460..66f5692db52 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -31,9 +31,57 @@ from sage.combinat.free_module import CombinatorialFreeModule class AssociatedGradedAlgebra(CombinatorialFreeModule): - """ - The associated graded algebra `\mathrm{gr} A` corresponding to - a filtered algebra `A`. + r""" + The associated graded algebra `\operatorname{gr} A` + of a filtered-algebra-with-basis `A`. + + Let `A` be a filtered algebra with basis over a + commutative ring `R`. Let `(F_0, F_1, F_2, \ldots)` be + its filtration, let `(b_i)_{i \in I}` be its basis, + and consider the partition of the set `I` into subsets + `I_0, I_1, I_2, \ldots` which is part of the data of + a filtered algebra with basis. The *associated graded + algebra* (or, for short, just *graded algebra*) of + `A` is a graded algebra with basis defined as follows: + + We know (see :class:`FilteredModulesWithBasis`) that + `A` (being a filtered `R`-module with basis) canonically + becomes a graded `R`-module with basis. (Its `n`-th + graded component, for every `n \in \NN`, is the + `R`-submodule of `A` spanned by `(b_i)_{i \in I_n}`.) + We define a multiplication `*` on this graded `R`-module + `A` (not to be mistaken for the multiplication of the + original algebra `A`) by requiring that + + .. MATH:: + + b_i * b_j = \left( \text{the } + (n+m)\text{-th homogeneous component of } + b_i b_j \right) + \qquad \text{for all } n, m \in \NN \text{ and } + i \in I_n \text{ and } j \in I_m + + (or, equivalently, + + .. MATH:: + + u * v = \left( \text{the } + (n+m)\text{-th homogeneous component of } + uv \right) + \qquad \text{for all } n, m \in \NN + \text{ and any homogeneous elements } u + \text{ and } v \text{ of respective degrees } + n \text{ and } m + + ). + + Thus, `(A, *)` is a graded `R`-algebra with basis. + This is called the associated graded algebra of `A`, + and denoted by `\operatorname{gr} A`. + + Notice that the multiplication `*` of this associated + graded algebra depends not only on the filtered algebra + `A`, but also on its basis. INPUT: @@ -43,6 +91,8 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: grA = A.graded_algebra() + sage: grA.category() + Category of graded algebras with basis over Rational Field sage: x,y,z = map(lambda s: grA.algebra_generators()[s], ['x','y','z']) sage: x bar(U['x']) @@ -51,8 +101,10 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): sage: A(y) * A(x) + A(z) U['x']*U['y'] - We note that the conversion between ``A`` and ``grA`` is the canonical - ``QQ``-module isomorphism:: + We note that the conversion between ``A`` and ``grA`` is + the canonical ``QQ``-module isomorphism stemming from the + fact that the underlying ``QQ``-modules of ``A`` and + ``grA`` are the same:: sage: grA(A.an_element()) bar(U['x']^2*U['y']^2*U['z']^3) @@ -104,7 +156,7 @@ def _repr_(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: A.graded_algebra() - Graded Algebra of An example of a filtered module with basis: + Graded Algebra of An example of a filtered algebra with basis: the universal enveloping algebra of Lie algebra of RR^3 with cross product over Rational Field """ @@ -118,19 +170,19 @@ def _latex_(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: latex(A.graded_algebra()) - \mathrm{gr}\; ... + \operatorname{gr} ... """ from sage.misc.latex import latex - return "\\mathrm{gr}\; " + latex(self._A) + return "\\operatorname{gr} " + latex(self._A) def _element_constructor_(self, x): """ Construct an element of ``self`` from ``x``. - .. NOTE:: - - This constructs an element from the filtered algebra ``A`` - by the canonical module isomorphism. + This constructs an element from the filtered algebra `A` + by the canonical module isomorphism (stemming from the + fact that `A` and the associated graded algebra `A` + have the same underlying `R`-module). EXAMPLES:: diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py index df4e7bcfa95..1f9bd7bd9bb 100644 --- a/src/sage/categories/examples/filtered_algebras_with_basis.py +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -12,7 +12,6 @@ from sage.combinat.free_module import CombinatorialFreeModule from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid from sage.sets.family import Family -from sage.misc.misc import powerset class PBWBasisCrossProduct(CombinatorialFreeModule): r""" @@ -63,11 +62,11 @@ def _repr_(self): EXAMPLES:: sage: AlgebrasWithBasis(QQ).Filtered().example() - An example of a filtered module with basis: + An example of a filtered algebra with basis: the universal enveloping algebra of Lie algebra of RR^3 with cross product over Rational Field """ - return "An example of a filtered module with basis: the universal enveloping algebra of Lie algebra of RR^3 with cross product over {}".format(self.base_ring()) + return "An example of a filtered algebra with basis: the universal enveloping algebra of Lie algebra of RR^3 with cross product over {}".format(self.base_ring()) def algebra_generators(self): """ diff --git a/src/sage/categories/examples/filtered_modules_with_basis.py b/src/sage/categories/examples/filtered_modules_with_basis.py index 4af2f427e45..d2c8b5d4ef5 100644 --- a/src/sage/categories/examples/filtered_modules_with_basis.py +++ b/src/sage/categories/examples/filtered_modules_with_basis.py @@ -16,7 +16,7 @@ class FilteredPartitionModule(CombinatorialFreeModule): r""" This class illustrates an implementation of a filtered module - with basis: the free module over partitions. + with basis: the free module on the set of all partitions. INPUT: diff --git a/src/sage/categories/filtered_algebras.py b/src/sage/categories/filtered_algebras.py index 7a91de609ba..3252c215b69 100644 --- a/src/sage/categories/filtered_algebras.py +++ b/src/sage/categories/filtered_algebras.py @@ -15,9 +15,11 @@ class FilteredAlgebras(FilteredModulesCategory): r""" The category of filtered algebras. - An algebra `A` over `R` is *filtered* if `A` is a filtered `R`-module - such that `F_i \cdot F_j \subseteq F_{i+j}` for all `i, j` in the - filtration group. + An algebra `A` over a commutative ring `R` is *filtered* if + `A` is endowed with a structure of a filtered `R`-module + (whose underlying `R`-module structure is identical with + that of the `R`-algebra `A`) such that + `F_i \cdot F_j \subseteq F_{i+j}` for all `i, j \in \NN`. EXAMPLES:: @@ -37,11 +39,20 @@ def graded_algebra(self): """ Return the associated graded algebra to ``self``. + .. TODO:: + + Should this really be here and not in the + ``_with_basis`` class? The notion of an associated + graded algebra of a filtered algebra (without + basis) exists, but are we ever going to get it into + Sage, and, more importantly: will it cooperate with + the with-basis version? + EXAMPLES:: sage: A = AlgebrasWithBasis(ZZ).Filtered().example() sage: A.graded_algebra() - Graded Algebra of An example of a filtered module with basis: + Graded Algebra of An example of a filtered algebra with basis: the universal enveloping algebra of Lie algebra of RR^3 with cross product over Integer Ring """ diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index f5dbdef0406..481c6245818 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -1,5 +1,12 @@ r""" Filtered algebras with basis + +A filtered algebra with basis over a commutative ring `R` +is a filtered algebra over `R` endowed with the structure +of a filtered module with basis (with the same underlying +filtered-module structure). See +:class:`FilteredAlgebras` and +:class:`FilteredModulesWithBasis` for these two notions. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw @@ -36,7 +43,7 @@ def graded_algebra(self): sage: A = AlgebrasWithBasis(ZZ).Filtered().example() sage: A.graded_algebra() - Graded Algebra of An example of a filtered module with basis: + Graded Algebra of An example of a filtered algebra with basis: the universal enveloping algebra of Lie algebra of RR^3 with cross product over Integer Ring """ @@ -48,9 +55,6 @@ def is_homogeneous(self): r""" Return whether ``self`` is homogeneous. - An element `x` is homogeneous if `x \in F_i \setminus F_{i-1}` - for some `i`. - EXAMPLES:: sage: S = NonCommutativeSymmetricFunctions(QQ).S() @@ -74,7 +78,8 @@ def is_homogeneous(self): def homogeneous_degree(self): """ - The degree of ``self``. + The degree of a nonzero homogeneous element ``self`` in the + filtered module. .. NOTE:: diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index caa2b271ea5..2a1c2d126e4 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -1,8 +1,15 @@ r""" Filtered modules -Consider a filtered module `M = \cup_{i \in I} F_i`. We require -all `F_i / F_{i-1}` to be modules for all `i`. +A *filtered module* over a commutative ring `R` is an +`R`-module `M` equipped with a sequence `(F_0, F_1, F_2, \ldots)` +of `R`-submodules satisfying +`F_0 \subseteq F_1 \subseteq F_2 \subseteq \cdots` and +`M = \cup_{i \geq 0} F_i`. This sequence is called the +*filtration* of the given module `M`. + +(More general notions of filtered modules exist, but are not +currently implemented in Sage.) """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw @@ -70,13 +77,13 @@ def _base_category_class(cls): :meth:`__classget__` method which would get in the way upon attribute access:: - sage: F = FilteredAlgebrasWithBasis - sage: F._foo = F._base_category_class[0] - sage: F._foo - Traceback (most recent call last): - ... - AssertionError: base category class for <...AlgebrasWithBasis'> mismatch; - expected <...Algebras'>, got <...FilteredAlgebrasWithBasis'> + sage: F = FilteredAlgebrasWithBasis + sage: F._foo = F._base_category_class[0] + sage: F._foo + Traceback (most recent call last): + ... + AssertionError: base category class for <...AlgebrasWithBasis'> mismatch; + expected <...Algebras'>, got <...FilteredAlgebrasWithBasis'> """ module_name = cls.__module__.replace("filtered_","") import sys @@ -129,10 +136,15 @@ class FilteredModules(FilteredModulesCategory): r""" The category of filtered modules. - A `R`-module `M` is *filtered* if there exists a `R`-module - isomorphism `A = \bigcup_{i \in I} F_i`, where `I` is a - totally ordered additive abelian group, such that - `F_{i-1} \subseteq F_i` for all `i \in I`. + A *filtered module* over a commutative ring `R` is an + `R`-module `M` equipped with a sequence `(F_0, F_1, F_2, \ldots)` + of `R`-submodules satisfying + `F_0 \subseteq F_1 \subseteq F_2 \subseteq \cdots` and + `M = \cup_{i \geq 0} F_i`. This sequence is called the + *filtration* of the given module `M`. + + (More general notions of filtered modules exist, but are not + currently implemented in Sage.) EXAMPLES:: @@ -148,7 +160,7 @@ class FilteredModules(FilteredModulesCategory): def extra_super_categories(self): r""" - Adds :class:`VectorSpaces` to the super categories of ``self`` if + Add :class:`VectorSpaces` to the super categories of ``self`` if the base ring is a field. EXAMPLES:: @@ -187,6 +199,10 @@ def Connected(self): r""" Return the full subcategory of the connected objects of ``self``. + A filtered `R`-module `M` with filtration + `(F_0, F_1, F_2, \ldots)` is said to be + *connected* if `F_0` is isomorphic to `R`. + EXAMPLES:: sage: Modules(ZZ).Filtered().Connected() diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 21f02012141..ce331e13355 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -1,5 +1,37 @@ r""" Filtered modules with basis + +A *filtered module with basis* over a commutative ring `R` +means (for the purpose of this code) a filtered `R`-module +`M` with filtration `(F_0, F_1, F_2, \ldots)` endowed with a +basis `(b_i)_{i \in I}` of `M` and a partition of the set +`I` into subsets `I_0, I_1, I_2, \ldots` (which can be +empty) such that for every `n \in \NN`, the subfamily +`(b_i)_{i \in I_0 \cup I_1 \cup \cdots \cup I_n}` is a basis +of the `R`-submodule `F_n`. + +For every `n \in \NN`, the `R`-submodule of `M` spanned by +`(b_i)_{i \in I_n}` is called the `*n*-th graded component* +of the filtered-module-with-basis `M`; the elements of +this submodule are referred to as *homogeneous elements of +degree `n`*. The `R`-module `M` is the direct sum of its +`n`-th graded components over all `n \in \NN`, and thus +becomes a graded `R`-module with basis. Conversely, any +graded `R`-module with basis canonically becomes a filtered +`R`-module with basis (by defining `F_n` as the direct sum +of the `0`-th, `1`-st, ..., `n`-th graded components, and +`I_n` as the indexing set of the basis of the `n`-th graded +component). Hence, the notion of a filtered `R`-module with +basis is equivalent to the notion of a graded `R`-module +with basis. However, the *category* of filtered `R`-modules +with basis is not the category of graded `R`-modules with +basis. Indeed, the *morphisms* of filtered `R`-modules with +basis are defined to be morphisms of `R`-modules which send +each `F_n` of the domain to the corresponding `F_n` of the +target; in contrast, the morphisms of graded `R`-modules +with basis must preserve each homogeneous component. Also, +the notion of a filtered algebra with basis differs from +that of a graded algebra with basis. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw @@ -14,6 +46,21 @@ class FilteredModulesWithBasis(FilteredModulesCategory): """ The category of filtered modules with a distinguished basis. + A *filtered module with basis* over a commutative ring `R` + means (for the purpose of this code) a filtered `R`-module + `M` with filtration `(F_0, F_1, F_2, \ldots)` endowed with a + basis `(b_i)_{i \in I}` of `M` and a partition of the set + `I` into subsets `I_0, I_1, I_2, \ldots` (which can be + empty) such that for every `n \in \NN`, the subfamily + `(b_i)_{i \in I_0 \cup I_1 \cup \cdots \cup I_n}` is a basis + of the `R`-submodule `F_n`. + + For every `n \in \NN`, the `R`-submodule of `M` spanned by + `(b_i)_{i \in I_n}` is called the `*n*-th graded component* + of the filtered-module-with-basis `M`; the elements of + this submodule are referred to as *homogeneous elements of + degree `n`*. + EXAMPLES:: sage: C = ModulesWithBasis(ZZ).Filtered(); C @@ -40,16 +87,19 @@ class ParentMethods: def basis(self, d=None): r""" - Return the basis for (an homogeneous component of) ``self``. + Return the basis for (the ``d``-th homogeneous component + of) ``self``. INPUT: - - ``d`` -- (optional, default ``None``) non negative integer + - ``d`` -- (optional, default ``None``) nonnegative integer or ``None`` If ``d`` is ``None``, returns a basis of the module. Otherwise, returns the basis of the homogeneous component - of degree ``d`` (i.e., of `F_d \setminus F_{d-1}`). + of degree ``d`` (i.e., the subfamily of the basis of the + whole module which consists only of the basis vectors + lying in `F_d \setminus F_{d-1}`). EXAMPLES:: @@ -76,9 +126,6 @@ def is_homogeneous(self): r""" Return whether ``self`` is homogeneous. - An element `x` is homogeneous if `x \in F_i \setminus F_{i-1}` - for some `i`. - EXAMPLES:: sage: A = ModulesWithBasis(ZZ).Filtered().example() @@ -104,10 +151,8 @@ def is_homogeneous(self): def degree(self): r""" - The degree of ``self`` in the filtered module. - - The degree of an element `x` is the value `i` such that - `x \in F_i \setminus F_{i-1}`. + The degree of a nonzero homogeneous element ``self`` in the + filtered module. .. NOTE:: @@ -141,6 +186,13 @@ def homogeneous_component(self, n): Return the homogeneous component of degree ``n`` of this element. + Let `m` be an element of a filtered `R`-module `M` with + basis. Then, `m` can be uniquely written in the form + `m = m_0 + m_1 + m_2 + \ldots`, where each `m_i` is a + homogeneous element of degree `i`. For `n \in \NN`, we + define the homogeneous component of degree `n` of the + element `m` to be `m_n`. + EXAMPLES:: sage: A = ModulesWithBasis(ZZ).Filtered().example() @@ -159,7 +211,7 @@ def homogeneous_component(self, n): TESTS: - Check that this really return ``A.zero()`` and not a plain ``0``:: + Check that this really returns ``A.zero()`` and not a plain ``0``:: sage: x.homogeneous_component(3).parent() is A True @@ -174,6 +226,9 @@ def truncate(self, n): Return the sum of the homogeneous components of degree strictly less than ``n`` of ``self``. + See :meth:`homogeneous_component` for the notion of a + homogeneous component. + EXAMPLES:: sage: A = ModulesWithBasis(ZZ).Filtered().example() From f1aa77ee1f5c71e03b490d760f22b6810e94501e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 9 Nov 2014 11:58:42 -0800 Subject: [PATCH 012/421] Initial implementation of colored permutations. --- src/sage/combinat/all.py | 1 + src/sage/combinat/colored_permutations.py | 384 ++++++++++++++++++++++ 2 files changed, 385 insertions(+) create mode 100644 src/sage/combinat/colored_permutations.py diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 1d0aa066dda..13eb4f7747d 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -33,6 +33,7 @@ #Permutations from permutation import Permutation, Permutations, Arrangements, PermutationOptions, CyclicPermutations, CyclicPermutationsOfPartition from affine_permutation import AffinePermutationGroup +from colored_permutations import ColoredPermutations, SignedPermutations from derangements import Derangements #RSK diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py new file mode 100644 index 00000000000..0a767ebe7b6 --- /dev/null +++ b/src/sage/combinat/colored_permutations.py @@ -0,0 +1,384 @@ +r""" +Colored Permutations +""" +from sage.categories.groups import Groups +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.structure.element import Element +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.misc.cachefunc import cached_method + +from sage.combinat.permutation import Permutations +from sage.combinat.cartesian_product import CartesianProduct +from sage.matrix.constructor import diagonal_matrix +from sage.rings.finite_rings.integer_mod_ring import IntegerModRing +from sage.rings.number_field.number_field import CyclotomicField + +class ColoredPermutation(Element): + """ + A colored permutation. + """ + def __init__(self, parent, colors, perm): + """ + Initialize ``self``. + """ + self._colors = tuple(colors) + self._perm = perm + Element.__init__(self, parent=parent) + + def _repr_(self): + """ + Return a string representation of ``self``. + """ + return repr([list(self._colors), self._perm]) + + def _mul_(self, other): + """ + Multiply ``self`` and ``other``. + """ + colors = tuple(self._colors[i] + other._colors[val-1] + for i,val in enumerate(~self._perm)) + return self.__class__(self.parent(), colors, self._perm * other._perm) + + def __eq__(self, other): + """ + Check equality. + """ + if not isinstance(other, ColoredPermutation): + return False + return (self.parent() is other.parent() + and self._colors == other._colors + and self._perm == other._perm) + + def __ne__(self, other): + """ + Check inequality. + """ + return not self.__eq__(other) + + def __iter__(self): + """ + Iterate over ``self``. + """ + for i,p in enumerate(self._perm): + yield (self._colors[i], p) + + def one_line_form(self): + """ + Return the one line form of ``self``. + """ + return list(self) + + def colors(self): + """ + Return the colors of ``self``. + """ + return list(self._colors) + + def permutation(self): + """ + Return the permutation of ``self``. + """ + return self._perm + + def to_matrix(self): + """ + Return a matrix of ``self``. + """ + Cp = CyclotomicField(self.parent()._m) + g = Cp.gen() + return diagonal_matrix(Cp, [g**i for i in self._colors]) * self._perm.to_matrix() + +class ColoredPermutations(Parent, UniqueRepresentation): + r""" + The group of `m`-colored permutations on `\{1, 2, \ldots, n\}`. + + Let `S_n` be the symmetric group on `n` letters and `C_m` be the cyclic + group of order `m`. The `m`-colored permutation group on `n` letters + is given by `P_n^m = C_m \wr S_n`. This is also the complex reflection + group `G(m, 1, n)`. + + We define our multiplication by + + .. MATH:: + + ((s_1, \ldots s_n), \sigma) \cdot ((t_1, \ldots, t_n), \tau) + = ((s_1 t_{\sigma^{-1}(1)}, \ldots, s_n t_{\sigma^{-1}(n)}), + \sigma \tau). + + REFERENCES: + + - :wikipedia:`Generalized_symmetric_group` + - :wikipedia:`Complex_reflection_group` + """ + def __init__(self, m, n): + """ + Initialize ``self``. + + EXAMPLES:: + """ + self._m = m + self._n = n + self._C = IntegerModRing(self._m) + self._P = Permutations(self._n) + Parent.__init__(self, category=(Groups(), FiniteEnumeratedSets())) + + def _repr_(self): + """ + Return a string representation of ``self``. + """ + return "{}-colored permutations of size {}".format(self._m, self._n) + + def one(self): + """ + Return the identity element of ``self``. + """ + return self.element_class(self, [self._C.zero()]*self._n, self._P.identity()) + + @cached_method + def gens(self): + """ + Return the generators of ``self``. + """ + zero = [self._C.zero()]*self._n + g = [] + for i in range(self._n-1): + p = range(1, self._n+1) + p[i] = i+2 + p[i+1] = i+1 + g.append( self.element_class(self, zero, self._P(p)) ) + zero[-1] = self._C.one() + g.append( self.element_class(self, zero, self._P.identity()) ) + return tuple(g) + + def matrix_group(self): + """ + Return the matrix group corresponding to ``self``. + """ + from sage.groups.matrix_gps.finitely_generated import MatrixGroup + return MatrixGroup([g.to_matrix() for g in self.gens()]) + + def _element_constructor_(self, x): + """ + Construct an element of ``self`` from ``x``. + """ + if isinstance(x, list): + if isinstance(x[0], tuple): + c = [] + p = [] + for k in x: + if len(k) != 2: + raise ValueError("input must be pairs (color, element)") + c.append( self._C(k[0]) ) + p.append(k[1]) + return self.element_class(self, c, self._P(p)) + + if len(x) != 2: + raise ValueError("input must be a pair of a list of colors and a permutation") + return self.element_class(self, map(self._C, x[0]), self._P(x[1])) + + def __iter__(self): + """ + Iterate over ``self``. + """ + C = CartesianProduct(*[self._C]*self._n) + for p in self._P: + for c in C: + yield self.element_class(self, c, p) + + def cardinality(self): + """ + Return the cardinality of ``self``. + """ + return self._m**self._n * self._P.cardinality() + + def rank(self): + """ + Return the rank of ``self``. + + The rank of a complex reflection group is equal to the dimension + of the complex vector space the group acts on. + """ + if self._m == 1: + return self._n - 1 + return self._n + + def degrees(self): + """ + Return the degrees of ``self``. + + The degrees of a complex reflection group are the degrees of + the fundamental invariants of the ring of polynomial invariants. + + If `m = 1`, then we are in the special case of the symmetric group + and the degrees are `(2, 3, \ldots, n, n+1)`. Otherwise the degrees + are `(m, 2m, \ldots, nm)`. + + EXAMPLES:: + + sage: CP = ColoredPermutations(4, 3) + sage: CP.degrees() + [4, 8, 12] + sage: S = ColoredPermutations(1, 3) + sage: S.degrees() + [2, 3] + + We now check that the product of the degrees is equal to the + cardinality of ``self``:: + + sage: prod(CP.degrees()) == CP.cardinality() + True + sage: prod(S.degrees()) == S.cardinality() + True + """ + if self._m == 1: # Special case for the usual symmetric group + return range(2, self._n+1) + return [self._m * i for i in range(1, self._n+1)] + + def codegrees(self): + """ + Return the codegrees of ``self``. + + Let `G` be a complex reflection group. The codegrees + `d_1^* \leq d_2^* \leq \cdots \leq d_{\ell}^*` of `G` can be + defined in terms of the fixed point polynomial: + + f_G(q) = \prod_{i=1}^{\ell} (q - d_i^* - 1). + + If `m = 1`, then we are in the special case of the symmetric group + and the codegrees are `(n-2, n-3, \ldots 1, 0)`. Otherwise the degrees + are `((n-1)m, (n-2)m, \ldots, m, 0)`. + """ + if self._m == 1: # Special case for the usual symmetric group + return list(reversed(range(self._n-1))) + return [self._m * i for i in reversed(range(self._n))] + + def number_reflection_hyperplanes(self): + """ + Return the number of reflection hyperplanes of ``self``. + + The number of reflection hyperplanes of a complex reflection + group is equal to the sume of the codegrees plus the rank. + """ + return sum(self.codegrees()) + self.rank() + + def fixed_point_polynomial(self, q): + r""" + The fixed point polynomial of ``self``. + + The fixed point polynomial `f_G` of a complex reflection group `G` is + counting the dimesions fixed points subspaces: + + .. MATH:: + + f_G(q) = \sum_{w \in W} q^{\dim V^w}. + + Furthermore, let `d_1, d_2, \ldots, d_{\ell}` be the degrees of `G`, + then the fixed point polynomial is given by + + .. MATH:: + + f_G(q) = \prod_{i=1}^{\ell} (q + d_i - 1). + """ + return prod(q + d - 1 for d in self.degrees()) + + def is_well_generated(self): + """ + Return if ``self`` is a well-generated complex reflection group. + + A complex reflection group `G` is well-generated if it is + generated by `\ell` reflections. Equivalently, `G` is well-generated + if `d_i + d_i^* = d_{\ell}` for all `1 \leq i \leq \ell` + """ + deg = self.degrees() + dstar = self.codegrees() + return all(deg[-1] == d + dstar[i] for i,d in enumerate(deg)) + + Element = ColoredPermutation + +##################################################################### +## Signed permutations + +class SignedPermutation(ColoredPermutation): + """ + A signed permutation. + """ + def _mul_(self, other): + """ + Multiply ``self`` and ``other``. + """ + colors = tuple(self._colors[i] * other._colors[val-1] + for i,val in enumerate(~self._perm)) + return self.__class__(self.parent(), colors, self._perm * other._perm) + + def __iter__(self): + """ + Iterate over ``self``. + """ + for i,p in enumerate(self._perm): + yield self._colors[i] * p + + def to_matrix(self): + """ + Return a matrix of ``self``. + """ + return identity_matrix(self._colors) * self._perm.to_matrix() + +class SignedPermutations(ColoredPermutations): + r""" + Group of signed permutations. + + The group of signed permutations is also known as the hyperoctahedral + group and the 2-colored permutation group. Thus it can be constructed + as the wreath product `S_2 \wr S_n`. + + REFERENCES: + + - :wikipedia:`Hyperoctahedral_group` + """ + def __init__(self, n): + """ + Initialize ``self``. + """ + ColoredPermutations.__init__(self, 2, n) + + def _repr_(self): + """ + Return a string representation of ``self``. + """ + return "Signed permutations of {}".format(self._n) + + def __iter__(self): + """ + Iterate over ``self``. + """ + C = CartesianProduct(*[[-1,1]]*self._n) + for p in self._P: + for c in C: + yield self.element_class(self, c, p) + + Element = SignedPermutation + +class EvenSignedPermutations(SignedPermutations): + """ + Group of even signed permutations. + """ + def _repr_(self): + """ + Return a string representation of ``self``. + """ + return "Even signed permtuations of {}".format(self._n) + + def __iter__(self): + """ + Iterate over ``self``. + """ + for s in SignedPermutations.__iter__(self): + total = 0 + for pm in s._colors: + if pm == -1: + total += 1 + + if total % 2 == 0: + yield s + From a4bc65cc8e1915f9e80c55d07509cf8be569f2ae Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 15 Nov 2014 00:44:22 -0800 Subject: [PATCH 013/421] Fix failing doctests. --- src/sage/categories/category.py | 2 +- src/sage/categories/category_with_axiom.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index 8fd57986f9e..8dbf0f045c3 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -2625,7 +2625,7 @@ def category_graph(categories = None): Graphics object consisting of 20 graphics primitives sage: sage.categories.category.category_graph().plot() - Graphics object consisting of 312 graphics primitives + Graphics object consisting of 324 graphics primitives """ from sage import graphs if categories is None: diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py index 70c091a25ba..000f96bd8bd 100644 --- a/src/sage/categories/category_with_axiom.py +++ b/src/sage/categories/category_with_axiom.py @@ -2249,10 +2249,9 @@ def _repr_object_names_static(category, axioms): base_category = base_category._with_axiom(axiom) if axiom == "WithBasis": result = result.replace(" over ", " with basis over ", 1) - elif axiom == "Connected": - if "graded " in result: - result = result.replace("graded ", "graded connected ", 1) - elif "filtered " in result: + elif axiom == "Connected" and "graded " in result: + result = result.replace("graded ", "graded connected ", 1) + elif axiom == "Connected" and "filtered " in result: result = result.replace("filtered ", "filtered connected ", 1) elif axiom == "Endset" and "homsets" in result: # Without the space at the end to handle Homsets().Endset() From a15a170e64ec10b4c57215029c9239ee9805bc07 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 15 Nov 2014 18:08:39 -0800 Subject: [PATCH 014/421] Made CliffordAlgebra into a filtered algebra. --- src/sage/algebras/clifford_algebra.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 53403eb8425..af6ce5a280f 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -18,7 +18,6 @@ from copy import copy from sage.categories.algebras_with_basis import AlgebrasWithBasis -from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis from sage.categories.graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis from sage.categories.modules_with_basis import ModuleMorphismByLinearity from sage.categories.poor_man_map import PoorManMap @@ -536,7 +535,7 @@ def __init__(self, Q, names, category=None): self._quadratic_form = Q R = Q.base_ring() if category is None: - category = GradedAlgebrasWithBasis(R) + category = AlgebrasWithBasis(R).Filtered() indices = SubsetsSorted(range(Q.dim())) CombinatorialFreeModule.__init__(self, R, indices, category=category) self._assign_names(names) @@ -1049,8 +1048,8 @@ def lift_module_morphism(self, m, names=None): f = lambda x: self.prod(self._from_dict( {(j,): m[j,i] for j in range(n)}, remove_zeros=True ) for i in x) - return Cl.module_morphism(on_basis=f, codomain=self, - category=GradedAlgebrasWithBasis(self.base_ring())) + cat = AlgebrasWithBasis(self.base_ring()).Filtered() + return Cl.module_morphism(on_basis=f, codomain=self, category=cat) def lift_isometry(self, m, names=None): r""" @@ -1114,8 +1113,9 @@ def lift_isometry(self, m, names=None): f = lambda x: Cl.prod(Cl._from_dict( {(j,): m[j,i] for j in range(n)}, remove_zeros=True ) for i in x) - return self.module_morphism(on_basis=f, codomain=Cl, - category=GradedAlgebrasWithBasis(self.base_ring())) + + cat = AlgebrasWithBasis(self.base_ring()).Filtered() + return self.module_morphism(on_basis=f, codomain=Cl, category=cat) # This is a general method for finite dimensional algebras with bases # and should be moved to the corresponding category once there is @@ -1562,7 +1562,7 @@ def lift_morphism(self, phi, names=None): f = lambda x: E.prod(E._from_dict( {(j,): phi[j,i] for j in range(n)}, remove_zeros=True ) for i in x) - return self.module_morphism(on_basis=f, codomain=E, category=GradedAlgebrasWithBasis(R)) + return self.module_morphism(on_basis=f, codomain=E, category=GradedHopfAlgebrasWithBasis(R)) def volume_form(self): """ From 41fbe5c6b1f8be6619f68529fa847ef9bf6c9c33 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 16 Nov 2014 11:08:18 -0800 Subject: [PATCH 015/421] Made Weyl algebra into a filtered algebra. --- src/sage/algebras/weyl_algebra.py | 40 ++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index a85c0c91629..a2a1050a00e 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -436,6 +436,23 @@ def list(self): return sorted(self.__monomials.items(), key=lambda x: (-sum(x[0][1]), x[0][1], -sum(x[0][0]), x[0][0]) ) + def support(self): + """ + Return the support of ``self``. + + EXAMPLES:: + + sage: W. = DifferentialWeylAlgebra(QQ) + sage: dx,dy,dz = W.differentials() + sage: elt = dy - (3*x - z)*dx + 1 + sage: elt.support() + [((0, 0, 0), (0, 1, 0)), + ((1, 0, 0), (1, 0, 0)), + ((0, 0, 0), (0, 0, 0)), + ((0, 0, 1), (1, 0, 0))] + """ + return self.__monomials.keys() + # This is essentially copied from # sage.combinat.free_module.CombinatorialFreeModuleElement def __div__(self, x, self_on_left=False): @@ -567,9 +584,8 @@ def __init__(self, R, names=None): names = names + tuple('d' + n for n in names) if len(names) != self._n * 2: raise ValueError("variable names cannot differ by a leading 'd'") - # TODO: Make this into a filtered algebra under the natural grading of - # x_i and dx_i have degree 1 - Algebra.__init__(self, R, names, category=AlgebrasWithBasis(R).NoZeroDivisors()) + cat = AlgebrasWithBasis(R).NoZeroDivisors().Filtered() + Algebra.__init__(self, R, names, category=cat) def _repr_(self): r""" @@ -663,6 +679,24 @@ def _coerce_map_from_(self, R): and self.base_ring().has_coerce_map_from(R.base_ring()) ) return super(DifferentialWeylAlgebra, self)._coerce_map_from_(R) + def degree_on_basis(self, i): + """ + Return the degree of the basis element indexed by ``i``. + + EXAMPLES:: + + sage: W. = DifferentialWeylAlgebra(QQ) + sage: W.degree_on_basis( ((1, 3, 2), (0, 1, 3)) ) + 10 + + sage: W. = DifferentialWeylAlgebra(QQ) + sage: dx,dy,dz = W.differentials() + sage: elt = y*dy - (3*x - z)*dx + sage: elt.degree() + 2 + """ + return sum(i[0]) + sum(i[1]) + def polynomial_ring(self): """ Return the associated polynomial ring of ``self``. From a11c501518c6785db1a99c78c04a1e38c2daff67 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 16 Nov 2014 11:09:23 -0800 Subject: [PATCH 016/421] Documentation changes and added to docbuild. --- src/doc/en/reference/algebras/index.rst | 2 + src/doc/en/reference/categories/index.rst | 4 + src/sage/algebras/associated_graded.py | 73 +++++++--------- src/sage/categories/filtered_algebras.py | 18 ++-- .../filtered_algebras_with_basis.py | 10 ++- src/sage/categories/filtered_modules.py | 42 +++++---- .../categories/filtered_modules_with_basis.py | 85 +++++++++---------- 7 files changed, 118 insertions(+), 116 deletions(-) diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 36ed741b169..16be0dd29a3 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -4,6 +4,8 @@ Algebras .. toctree:: :maxdepth: 2 + sage/algebras/associated_graded + sage/algebras/clifford_algebra sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index ef69b30335e..421e0f30aaf 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -75,6 +75,10 @@ Categories sage/categories/enumerated_sets sage/categories/euclidean_domains sage/categories/fields + sage/categories/filtered_algebras + sage/categories/filtered_algebras_with_basis + sage/categories/filtered_modules + sage/categories/filtered_modules_with_basis sage/categories/finite_coxeter_groups sage/categories/finite_crystals sage/categories/finite_dimensional_algebras_with_basis diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index 66f5692db52..21cb4108144 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -1,5 +1,5 @@ r""" -Associated Graded Algebras +Associated Graded Algebras To Filtered Algebras AUTHORS: @@ -33,61 +33,41 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): r""" The associated graded algebra `\operatorname{gr} A` - of a filtered-algebra-with-basis `A`. + of a filtered algebra with basis `A`. Let `A` be a filtered algebra with basis over a - commutative ring `R`. Let `(F_0, F_1, F_2, \ldots)` be - its filtration, let `(b_i)_{i \in I}` be its basis, - and consider the partition of the set `I` into subsets - `I_0, I_1, I_2, \ldots` which is part of the data of - a filtered algebra with basis. The *associated graded - algebra* (or, for short, just *graded algebra*) of - `A` is a graded algebra with basis defined as follows: - - We know (see :class:`FilteredModulesWithBasis`) that - `A` (being a filtered `R`-module with basis) canonically - becomes a graded `R`-module with basis. (Its `n`-th - graded component, for every `n \in \NN`, is the - `R`-submodule of `A` spanned by `(b_i)_{i \in I_n}`.) - We define a multiplication `*` on this graded `R`-module - `A` (not to be mistaken for the multiplication of the - original algebra `A`) by requiring that + commutative ring `R`. Let `(F_i)_{i \in I}` be + its filtration, let `(b_x)_{x \in X}` be its basis, + and consider the partition of the set `X = \bigsqcup_{i \in I} X_i`, + which is part of the data of a filtered algebra with basis. The + *associated graded algebra* (or, for short, just *graded algebra*) + of `A` is a graded algebra with basis defined as follows. + + We know (see + :class:`~sage.categories.filtered_modules_with_basis.FilteredModulesWithBasis`) + that `A` (being a filtered `R`-module with basis) canonically + becomes a graded `R`-module with basis. (Its `i`-th + graded component, for every `i \in I`, is the + `R`-submodule of `A` spanned by `(b_x)_{x \in X_i}`.) + Let `u \in F_i` and `v \in F_j` and suppose + `u v = \sum_{k \leq i + j} m_k` where `m_k` is in the `k`-th + homogeneous component. We define a multiplication `*` on this + graded `R`-module `A` (not to be mistaken for the multiplication + of the original algebra `A`) by requiring that .. MATH:: - b_i * b_j = \left( \text{the } - (n+m)\text{-th homogeneous component of } - b_i b_j \right) - \qquad \text{for all } n, m \in \NN \text{ and } - i \in I_n \text{ and } j \in I_m - - (or, equivalently, - - .. MATH:: - - u * v = \left( \text{the } - (n+m)\text{-th homogeneous component of } - uv \right) - \qquad \text{for all } n, m \in \NN - \text{ and any homogeneous elements } u - \text{ and } v \text{ of respective degrees } - n \text{ and } m - - ). + u * v = m_{i+j}. Thus, `(A, *)` is a graded `R`-algebra with basis. This is called the associated graded algebra of `A`, and denoted by `\operatorname{gr} A`. - Notice that the multiplication `*` of this associated - graded algebra depends not only on the filtered algebra - `A`, but also on its basis. - INPUT: - ``A`` -- a filtered algebra - EXAMPLES: + EXAMPLES:: sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: grA = A.graded_algebra() @@ -119,6 +99,15 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): The algebra ``A`` must currently be an instance of (a subclass of) :class:`CombinatorialFreeModule`. This should work with any algebra with a basis. + + .. TODO:: + + Implement a version of for filtered algebras without a + distinguished basis. + + REFERENCES: + + - :wikipedia:`Filtered_algebra#Associated_graded_algebra` """ def __init__(self, A, category=None): """ diff --git a/src/sage/categories/filtered_algebras.py b/src/sage/categories/filtered_algebras.py index 3252c215b69..536ef505a1a 100644 --- a/src/sage/categories/filtered_algebras.py +++ b/src/sage/categories/filtered_algebras.py @@ -18,8 +18,10 @@ class FilteredAlgebras(FilteredModulesCategory): An algebra `A` over a commutative ring `R` is *filtered* if `A` is endowed with a structure of a filtered `R`-module (whose underlying `R`-module structure is identical with - that of the `R`-algebra `A`) such that - `F_i \cdot F_j \subseteq F_{i+j}` for all `i, j \in \NN`. + that of the `R`-algebra `A`) such that the indexing set `I` + (typically `I = \NN`) is also an additive abelian monoid, + `F_0 = \{ 0 \}`, and `F_i \cdot F_j \subseteq F_{i+j}` + for all `i, j \in I`. EXAMPLES:: @@ -32,6 +34,10 @@ class FilteredAlgebras(FilteredModulesCategory): TESTS:: sage: TestSuite(Algebras(ZZ).Filtered()).run() + + REFERENCES: + + - :wikipedia:`Filtered_algebra` """ class ParentMethods: @abstract_method(optional=True) @@ -41,12 +47,8 @@ def graded_algebra(self): .. TODO:: - Should this really be here and not in the - ``_with_basis`` class? The notion of an associated - graded algebra of a filtered algebra (without - basis) exists, but are we ever going to get it into - Sage, and, more importantly: will it cooperate with - the with-basis version? + Implement a version of the associated graded algebra + without a distinguished basis. EXAMPLES:: diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 481c6245818..c56fb3878e7 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -1,12 +1,13 @@ r""" -Filtered algebras with basis +Filtered Algebras With Basis A filtered algebra with basis over a commutative ring `R` is a filtered algebra over `R` endowed with the structure of a filtered module with basis (with the same underlying filtered-module structure). See -:class:`FilteredAlgebras` and -:class:`FilteredModulesWithBasis` for these two notions. +:class:`~sage.categories.filtered_algebras.FilteredAlgebras` and +:class:`~sage.categories.filtered_modules_with_basis.FilteredModulesWithBasis` +for these two notions. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw @@ -19,7 +20,8 @@ class FilteredAlgebrasWithBasis(FilteredModulesCategory): """ - The category of filtered algebras with a distinguished basis. + The category of filtered algebras with a distinguished + homogeneous basis. EXAMPLES:: diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index 2a1c2d126e4..8b4bba1bf2e 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -1,15 +1,20 @@ r""" -Filtered modules +Filtered Modules -A *filtered module* over a commutative ring `R` is an -`R`-module `M` equipped with a sequence `(F_0, F_1, F_2, \ldots)` -of `R`-submodules satisfying -`F_0 \subseteq F_1 \subseteq F_2 \subseteq \cdots` and -`M = \cup_{i \geq 0} F_i`. This sequence is called the -*filtration* of the given module `M`. +A *filtered module* over a commutative ring `R` with a totally ordered +indexing set `I` (typically `I = \NN`) is an `R`-module `M` equipped +with a sequence `(F_i)_{i \in I}` of `R`-submodules satisfying +`F_i \subseteq F_j` if `i \leq j` for all `i,j \in I` and +`M = \bigcup_{i \in I} F_i`. This sequence is called a *filtration* +of the given module `M`. -(More general notions of filtered modules exist, but are not -currently implemented in Sage.) +.. TODO:: + + Implement a notion for decreasing filtrations: where `F_j \subseteq F_i`. + +.. TODO:: + + Implement filtrations for all concrete categories. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw @@ -136,15 +141,12 @@ class FilteredModules(FilteredModulesCategory): r""" The category of filtered modules. - A *filtered module* over a commutative ring `R` is an - `R`-module `M` equipped with a sequence `(F_0, F_1, F_2, \ldots)` - of `R`-submodules satisfying - `F_0 \subseteq F_1 \subseteq F_2 \subseteq \cdots` and - `M = \cup_{i \geq 0} F_i`. This sequence is called the - *filtration* of the given module `M`. - - (More general notions of filtered modules exist, but are not - currently implemented in Sage.) + A *filtered module* over a commutative ring `R` with a totally ordered + indexing set `I` (typically `I = \NN`) is an `R`-module `M` equipped + with a sequence `(F_i)_{i \in I}` of `R`-submodules satisfying + `F_i \subseteq F_j` if `i \leq j` for all `i, j \in I` and + `M = \bigcup_{i \in I} F_i`. This sequence is called a *filtration* + of the given module `M`. EXAMPLES:: @@ -156,6 +158,10 @@ class FilteredModules(FilteredModulesCategory): TESTS:: sage: TestSuite(Modules(ZZ).Filtered()).run() + + REFERENCES: + + - :wikipedia:`Filtration_(mathematics)` """ def extra_super_categories(self): diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index ce331e13355..5dcf2f34e42 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -1,37 +1,19 @@ r""" -Filtered modules with basis +Filtered Modules With Basis A *filtered module with basis* over a commutative ring `R` means (for the purpose of this code) a filtered `R`-module -`M` with filtration `(F_0, F_1, F_2, \ldots)` endowed with a -basis `(b_i)_{i \in I}` of `M` and a partition of the set -`I` into subsets `I_0, I_1, I_2, \ldots` (which can be -empty) such that for every `n \in \NN`, the subfamily -`(b_i)_{i \in I_0 \cup I_1 \cup \cdots \cup I_n}` is a basis -of the `R`-submodule `F_n`. - -For every `n \in \NN`, the `R`-submodule of `M` spanned by -`(b_i)_{i \in I_n}` is called the `*n*-th graded component* -of the filtered-module-with-basis `M`; the elements of +`M` with filtration `(F_i)_{i \in I}` (typically `I = \NN`) +endowed with a basis `(b_j)_{j \in J}` of `M` and a partition of +the set `J = \bigsqcup_{i \in I} J_i` (which can be empty) such +that for every `n \in I`, the subfamily `(b_j)_{j \in U_n}`, where +`U_n = \bigcup_{i \leq n} J_i`, is a basis of the `R`-submodule `F_n`. + +For every `i \in I`, the `R`-submodule of `M` spanned by +`(b_j)_{j \in J_i}` is called the `i`-*th graded component* +of the filtered module with basis `M`; the elements of this submodule are referred to as *homogeneous elements of -degree `n`*. The `R`-module `M` is the direct sum of its -`n`-th graded components over all `n \in \NN`, and thus -becomes a graded `R`-module with basis. Conversely, any -graded `R`-module with basis canonically becomes a filtered -`R`-module with basis (by defining `F_n` as the direct sum -of the `0`-th, `1`-st, ..., `n`-th graded components, and -`I_n` as the indexing set of the basis of the `n`-th graded -component). Hence, the notion of a filtered `R`-module with -basis is equivalent to the notion of a graded `R`-module -with basis. However, the *category* of filtered `R`-modules -with basis is not the category of graded `R`-modules with -basis. Indeed, the *morphisms* of filtered `R`-modules with -basis are defined to be morphisms of `R`-modules which send -each `F_n` of the domain to the corresponding `F_n` of the -target; in contrast, the morphisms of graded `R`-modules -with basis must preserve each homogeneous component. Also, -the notion of a filtered algebra with basis differs from -that of a graded algebra with basis. +degree* `i`. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw @@ -43,23 +25,38 @@ from sage.categories.filtered_modules import FilteredModulesCategory class FilteredModulesWithBasis(FilteredModulesCategory): - """ + r""" The category of filtered modules with a distinguished basis. A *filtered module with basis* over a commutative ring `R` means (for the purpose of this code) a filtered `R`-module - `M` with filtration `(F_0, F_1, F_2, \ldots)` endowed with a - basis `(b_i)_{i \in I}` of `M` and a partition of the set - `I` into subsets `I_0, I_1, I_2, \ldots` (which can be - empty) such that for every `n \in \NN`, the subfamily - `(b_i)_{i \in I_0 \cup I_1 \cup \cdots \cup I_n}` is a basis - of the `R`-submodule `F_n`. - - For every `n \in \NN`, the `R`-submodule of `M` spanned by - `(b_i)_{i \in I_n}` is called the `*n*-th graded component* - of the filtered-module-with-basis `M`; the elements of - this submodule are referred to as *homogeneous elements of - degree `n`*. + `M` with filtration `(F_i)_{i \in I}` (typically `I = \NN`) + endowed with a basis `(b_j)_{j \in J}` of `M` and a partition of + the set `J = \bigsqcup_{i \in I} J_i` (which can be empty) such + that for every `n \in I`, the subfamily `(b_j)_{j \in U_n}`, where + `U_n = \bigcup_{i \leq n} J_i`, is a basis of the `R`-submodule `F_n`. + + For every `i \in I`, the `R`-submodule of `M` spanned by + `(b_j)_{j \in J_i}` is called the `i`-*th graded component* of the + filtered module with basis `M`; the elements of this submodule are + referred to as *homogeneous elements of degree* `i`. The `R`-module + `M` is the direct sum of its `i`-th graded components over + all `i \in I`, and thus becomes a graded `R`-module with basis. + Conversely, any graded `R`-module with basis canonically becomes a filtered + `R`-module with basis (by defining `F_n = \bigoplus_{i \leq n} G_i` + where `G_i` is the `i`-th graded component and `J_i` as the indexing + set of the basis of the `i`-th graded component). Hence, the notion + of a filtered `R`-module with basis is equivalent to the notion of + a graded `R`-module with basis. + + However, the *category* of filtered `R`-modules with basis is not + the category of graded `R`-modules with basis. Indeed, the *morphisms* + of filtered `R`-modules with basis are defined to be morphisms of + `R`-modules which send each `F_n` of the domain to the corresponding + `F_n` of the target; in contrast, the morphisms of graded `R`-modules + with basis must preserve each homogeneous component. Also, + the notion of a filtered algebra with basis differs from + that of a graded algebra with basis. EXAMPLES:: @@ -188,8 +185,8 @@ def homogeneous_component(self, n): Let `m` be an element of a filtered `R`-module `M` with basis. Then, `m` can be uniquely written in the form - `m = m_0 + m_1 + m_2 + \ldots`, where each `m_i` is a - homogeneous element of degree `i`. For `n \in \NN`, we + `m = \sum_{i \in I} m_i`, where each `m_i` is a + homogeneous element of degree `i`. For `n \in I`, we define the homogeneous component of degree `n` of the element `m` to be `m_n`. From 45aca1bb8e9492c4b076532a89c9334eec4dc1d2 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 16 Nov 2014 15:57:52 -0800 Subject: [PATCH 017/421] Changed some doc from conversation with Darij. --- src/sage/algebras/associated_graded.py | 62 ++++++++++++------- .../categories/filtered_modules_with_basis.py | 2 +- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index 21cb4108144..305c695abc1 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -35,33 +35,51 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): The associated graded algebra `\operatorname{gr} A` of a filtered algebra with basis `A`. - Let `A` be a filtered algebra with basis over a - commutative ring `R`. Let `(F_i)_{i \in I}` be - its filtration, let `(b_x)_{x \in X}` be its basis, - and consider the partition of the set `X = \bigsqcup_{i \in I} X_i`, - which is part of the data of a filtered algebra with basis. The - *associated graded algebra* (or, for short, just *graded algebra*) - of `A` is a graded algebra with basis defined as follows. + Let `A` be a filtered algebra with basis over a commutative + ring `R`. Let `(F_i)_{i \in I}` be the filtration of `A`, and + define - We know (see - :class:`~sage.categories.filtered_modules_with_basis.FilteredModulesWithBasis`) - that `A` (being a filtered `R`-module with basis) canonically - becomes a graded `R`-module with basis. (Its `i`-th - graded component, for every `i \in I`, is the - `R`-submodule of `A` spanned by `(b_x)_{x \in X_i}`.) - Let `u \in F_i` and `v \in F_j` and suppose - `u v = \sum_{k \leq i + j} m_k` where `m_k` is in the `k`-th - homogeneous component. We define a multiplication `*` on this - graded `R`-module `A` (not to be mistaken for the multiplication - of the original algebra `A`) by requiring that + .. MATH:: + + G_i = F_i / \sum_{j < i} F_j + + and then .. MATH:: - u * v = m_{i+j}. + \operatorname{gr} A = \bigoplus_{i \in I} G_i. + + There are canonical projections `p_i : F_i \to G_i` for + every `i \in I`. Moreover `\operatorname{gr} A` is naturally a + graded module with `G_i` being the `i`-th graded component. + + Let `u \in G_i` and `v \in G_j` with lifts `u' \in F_i` + and `v' \in F_j` respectively. Therefore we define + multiplication `*` on `\operatorname{gr} A` (not to be mistaken + for the multiplication of the original algebra `A`) by - Thus, `(A, *)` is a graded `R`-algebra with basis. - This is called the associated graded algebra of `A`, - and denoted by `\operatorname{gr} A`. + .. MATH:: + + u * v = p_{i+j}(u' v'). + + The *associated graded algebra* (or, for short, just *graded algebra*) + of `A` is the graded algebra `\operatorname{gr} A`. + + In particular, let `(b_x)_{x \in X}` be the basis of `A`, + and consider the partition of the set `X = \bigsqcup_{i \in I} X_i`, + which is part of the data of a filtered algebra with basis. + We know (see + :class:`~sage.categories.filtered_modules_with_basis.FilteredModulesWithBasis`) + that `A` (being a filtered `R`-module with basis) is canonically + isomorphic to `\operatorname{gr} A` as an `R`-module. Therefore + the `k`-th graded component `G_k` can be identified with + the span of `(b_x)_{x \in X_k}`, or equivalently the + `k`-th homogeneous component of `A`. Suppose + that `u' v' = \sum_{k \leq i+j} m_k` where `m_k \in G_k` (which + has been identified with the `k`-th homogeneous component of `A`). + Then `u * v = m_{i+j}`. We also note that the choice of + identification of `G_k` with the `k`-th homogeneous component + depends on the given basis. INPUT: diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 5dcf2f34e42..efe84a1416b 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -96,7 +96,7 @@ def basis(self, d=None): Otherwise, returns the basis of the homogeneous component of degree ``d`` (i.e., the subfamily of the basis of the whole module which consists only of the basis vectors - lying in `F_d \setminus F_{d-1}`). + lying in `F_d \setminus \bigcup_{i Date: Sun, 16 Nov 2014 20:52:48 -0800 Subject: [PATCH 018/421] fixes to associated_graded.py --- src/doc/en/reference/algebras/index.rst | 2 + src/sage/algebras/associated_graded.py | 156 ++++++++++++++++++++---- 2 files changed, 133 insertions(+), 25 deletions(-) diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 192c6e39eb8..af0254920c4 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -33,6 +33,8 @@ Algebras sage/algebras/hall_algebra + sage/algebras/quatalg/quaternion_algebra + sage/algebras/shuffle_algebra sage/algebras/steenrod/steenrod_algebra diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index 305c695abc1..36f62a1081c 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -36,14 +36,14 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): of a filtered algebra with basis `A`. Let `A` be a filtered algebra with basis over a commutative - ring `R`. Let `(F_i)_{i \in I}` be the filtration of `A`, and - define + ring `R`. Let `(F_i)_{i \in I}` be the filtration of `A`, with + `I` being a totally ordered set. Define .. MATH:: G_i = F_i / \sum_{j < i} F_j - and then + for every `i \in I`, and then .. MATH:: @@ -51,26 +51,29 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): There are canonical projections `p_i : F_i \to G_i` for every `i \in I`. Moreover `\operatorname{gr} A` is naturally a - graded module with `G_i` being the `i`-th graded component. + graded `R`-module with `G_i` being the `i`-th graded component. - Let `u \in G_i` and `v \in G_j` with lifts `u' \in F_i` - and `v' \in F_j` respectively. Therefore we define + Let `u \in G_i` and `v \in G_j`, and let `u' \in F_i` and + `v' \in F_j` be lifts of `u` and `v`, respectively (so that + `u = p_i(u')` and `v = p_j(v')`). Then, we define a multiplication `*` on `\operatorname{gr} A` (not to be mistaken for the multiplication of the original algebra `A`) by .. MATH:: - u * v = p_{i+j}(u' v'). + u * v = p_{i+j} (u' v'). - The *associated graded algebra* (or, for short, just *graded algebra*) - of `A` is the graded algebra `\operatorname{gr} A`. + The *associated graded algebra* (or, for short, just + *graded algebra*) of `A` is the graded algebra + `\operatorname{gr} A` (endowed with this multiplication). In particular, let `(b_x)_{x \in X}` be the basis of `A`, - and consider the partition of the set `X = \bigsqcup_{i \in I} X_i`, - which is part of the data of a filtered algebra with basis. - We know (see + and consider the partition `X = \bigsqcup_{i \in I} X_i` of + the set `X`, which is part of the data of a filtered + algebra with basis. We know (see :class:`~sage.categories.filtered_modules_with_basis.FilteredModulesWithBasis`) that `A` (being a filtered `R`-module with basis) is canonically + (when the basis is considered to be part of the data) isomorphic to `\operatorname{gr} A` as an `R`-module. Therefore the `k`-th graded component `G_k` can be identified with the span of `(b_x)_{x \in X_k}`, or equivalently the @@ -79,11 +82,20 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): has been identified with the `k`-th homogeneous component of `A`). Then `u * v = m_{i+j}`. We also note that the choice of identification of `G_k` with the `k`-th homogeneous component - depends on the given basis. + of `A` depends on the given basis. + + In this class, the `R`-module isomorphism from `A` to + `\operatorname{gr} A` is implemented as + :meth:`to_graded_conversion` and also as the default + conversion from `A` to `\operatorname{gr} A`. Its + inverse map is implemented as + :meth:`from_graded_conversion`. The projection + `p_i : F_i \to G_i` is implemented as + :meth:`projection` ``(i)``. INPUT: - - ``A`` -- a filtered algebra + - ``A`` -- a filtered algebra with basis EXAMPLES:: @@ -102,7 +114,7 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): We note that the conversion between ``A`` and ``grA`` is the canonical ``QQ``-module isomorphism stemming from the fact that the underlying ``QQ``-modules of ``A`` and - ``grA`` are the same:: + ``grA`` are isomorphic:: sage: grA(A.an_element()) bar(U['x']^2*U['y']^2*U['z']^3) @@ -120,8 +132,8 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): .. TODO:: - Implement a version of for filtered algebras without a - distinguished basis. + Implement a version of associated graded algebra for + filtered algebras without a distinguished basis. REFERENCES: @@ -138,9 +150,12 @@ def __init__(self, A, category=None): sage: TestSuite(grA).run(elements=[prod(grA.algebra_generators())]) """ if A not in AlgebrasWithBasis(A.base_ring()).Filtered(): - raise ValueError("the base algebra must be filtered") + raise ValueError("the base algebra must be filtered and with basis") self._A = A + base_ring = A.base_ring() + self.base_one = base_ring.one() + if category is None: category = A.category().Graded() opts = copy(A.print_options()) @@ -148,11 +163,11 @@ def __init__(self, A, category=None): opts['bracket'] = '(' opts['prefix'] = opts['prefix'] + 'bar' - CombinatorialFreeModule.__init__(self, A.base_ring(), A.indices(), + CombinatorialFreeModule.__init__(self, base_ring, A.indices(), category=category, **opts) # Setup the conversion back - phi = self.module_morphism(lambda x: A.monomial(x), codomain=A) + phi = self.module_morphism(diagonal=lambda x: self.base_one, codomain=A) self._A.register_conversion(phi) def _repr_(self): @@ -183,13 +198,15 @@ def _latex_(self): return "\\operatorname{gr} " + latex(self._A) def _element_constructor_(self, x): - """ + r""" Construct an element of ``self`` from ``x``. - This constructs an element from the filtered algebra `A` - by the canonical module isomorphism (stemming from the - fact that `A` and the associated graded algebra `A` - have the same underlying `R`-module). + If ``self`` `= \operatorname{gr} A` for a filtered algebra + `A`, and if ``x`` is an element of `A`, then this returns + the image of `x` under the canonical `R`-module + isomorphism `A \to \operatorname{gr} A`. (In this case, + this is equivalent to calling + ``self.to_graded_conversion()(x)``.) EXAMPLES:: @@ -205,6 +222,90 @@ def _element_constructor_(self, x): return self._from_dict(dict(x)) return super(AssociatedGradedAlgebra, self)._element_constructor_(x) + # Maps + + def to_graded_conversion(self): + r""" + Return the canonical `R`-module isomorphism + `A \to \operatorname{gr} A` induced by the basis of `A`. + + This is an isomorphism of `R`-modules, not of algebras. See + the class-wide documentation :class:`AssociatedGradedAlgebra`. + + .. SEEALSO:: + + :meth:`from_graded_conversion`. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p + U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + sage: grA.to_graded_conversion()(p) + bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + """ + return self._A.module_morphism(diagonal=lambda x: self.base_one, + codomain=self) + + def from_graded_conversion(self): + r""" + Return the inverse of the canonical `R`-module isomorphism + `A \to \operatorname{gr} A` induced by the basis of `A`. + + This is an isomorphism of `R`-modules, not of algebras. See + the class-wide documentation :class:`AssociatedGradedAlgebra`. + + .. SEEALSO:: + + :meth:`to_graded_conversion`. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p + U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + sage: q = grA.to_graded_conversion()(p) + sage: grA.from_graded_conversion()(q) == p + True + """ + return self.module_morphism(diagonal=lambda x: self.base_one, + codomain=self._A) + + def projection(self, i): + r""" + Return the `i`-th projection `p_i : F_i \to G_i` (in the + notations of the class-wide documentation + :class:`AssociatedGradedAlgebra`). + + This method actually does not return the map `p_i` itself, + but an extension of `p_i` to the whole `R`-module `A`. + This extension is the composition of the `R`-module + isomorphism `A \to \operatorname{gr} A` with the canonical + projection of the graded `R`-module `\operatorname{gr} A` + onto its `i`-th graded component `G_i`. The codomain of + this map is `\operatorname{gr} A`, although its actual + image is `G_i`. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p + U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + sage: grA.projection(7)(p) + bar(U['x']^2*U['y']^2*U['z']^3) + sage: grA.projection(8)(p) + 0 + """ + base_zero = self.base_ring().zero() + return self._A.module_morphism(diagonal=lambda x: + (self.base_one if + self._A.degree_on_basis(x) == i + else base_zero), + codomain=self) + def gen(self, *args, **kwds): """ Return a generator of ``self``. @@ -227,6 +328,9 @@ def algebra_generators(self): """ Return the algebra generators of ``self``. + This assumes that the algebra generators of `A` provided by + its ``algebra_generators`` method are homogeneous. + EXAMPLES:: sage: A = Algebras(QQ).WithBasis().Filtered().example() @@ -242,6 +346,8 @@ def one_basis(self): """ Return the basis index of the element `1`. + This assumes that the unity `1` of `A` belongs to `F_0`. + EXAMPLES:: sage: A = Algebras(QQ).WithBasis().Filtered().example() From b2bc6f59a7daf29f2e55fe0db40a241c06066b6e Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Sun, 16 Nov 2014 20:57:07 -0800 Subject: [PATCH 019/421] self.base_one removed --- src/sage/algebras/associated_graded.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index 36f62a1081c..d372e301048 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -154,7 +154,7 @@ def __init__(self, A, category=None): self._A = A base_ring = A.base_ring() - self.base_one = base_ring.one() + base_one = base_ring.one() if category is None: category = A.category().Graded() @@ -167,7 +167,7 @@ def __init__(self, A, category=None): category=category, **opts) # Setup the conversion back - phi = self.module_morphism(diagonal=lambda x: self.base_one, codomain=A) + phi = self.module_morphism(diagonal=lambda x: base_one, codomain=A) self._A.register_conversion(phi) def _repr_(self): @@ -245,7 +245,8 @@ def to_graded_conversion(self): sage: grA.to_graded_conversion()(p) bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) """ - return self._A.module_morphism(diagonal=lambda x: self.base_one, + base_one = self.base_ring().one() + return self._A.module_morphism(diagonal=lambda x: base_one, codomain=self) def from_graded_conversion(self): @@ -270,7 +271,8 @@ def from_graded_conversion(self): sage: grA.from_graded_conversion()(q) == p True """ - return self.module_morphism(diagonal=lambda x: self.base_one, + base_one = self.base_ring().one() + return self.module_morphism(diagonal=lambda x: base_one, codomain=self._A) def projection(self, i): @@ -300,8 +302,9 @@ def projection(self, i): 0 """ base_zero = self.base_ring().zero() + base_one = self.base_ring().one() return self._A.module_morphism(diagonal=lambda x: - (self.base_one if + (base_one if self._A.degree_on_basis(x) == i else base_zero), codomain=self) From b32aaf289e6a152a8938a87a6865d510d818bfb6 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Sun, 16 Nov 2014 22:31:31 -0800 Subject: [PATCH 020/421] further review changes --- src/sage/algebras/associated_graded.py | 46 +++++++++---------- src/sage/categories/category_with_axiom.py | 2 +- .../examples/filtered_algebras_with_basis.py | 9 ++-- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index d372e301048..1fddb176f6b 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -14,20 +14,10 @@ #***************************************************************************** from sage.misc.cachefunc import cached_method -from sage.misc.misc_c import prod from copy import copy from sage.categories.algebras_with_basis import AlgebrasWithBasis -from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis -from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis -from sage.categories.graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis -from sage.rings.all import ZZ -from sage.rings.infinity import infinity -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.sets.family import Family -from sage.sets.positive_integers import PositiveIntegers -from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid -from sage.combinat.cartesian_product import CartesianProduct from sage.combinat.free_module import CombinatorialFreeModule class AssociatedGradedAlgebra(CombinatorialFreeModule): @@ -35,9 +25,9 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): The associated graded algebra `\operatorname{gr} A` of a filtered algebra with basis `A`. - Let `A` be a filtered algebra with basis over a commutative - ring `R`. Let `(F_i)_{i \in I}` be the filtration of `A`, with - `I` being a totally ordered set. Define + Let `A` be a filtered algebra over a commutative ring `R`. + Let `(F_i)_{i \in I}` be the filtration of `A`, with `I` being + a totally ordered set. Define .. MATH:: @@ -67,7 +57,8 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): *graded algebra*) of `A` is the graded algebra `\operatorname{gr} A` (endowed with this multiplication). - In particular, let `(b_x)_{x \in X}` be the basis of `A`, + Now, assume that `A` is a filtered `R`-algebra with basis. + Let `(b_x)_{x \in X}` be the basis of `A`, and consider the partition `X = \bigsqcup_{i \in I} X_i` of the set `X`, which is part of the data of a filtered algebra with basis. We know (see @@ -84,13 +75,20 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): identification of `G_k` with the `k`-th homogeneous component of `A` depends on the given basis. + The basis `(b_x)_{x \in X}` of `A` gives rise to a basis + of `\operatorname{gr} A`. This latter basis is still indexed + by the elements of `X`, and consists of the images of the + `b_x` under the `R`-module isomorphism from `A` to + `\operatorname{gr} A`. It makes `\operatorname{gr} A` into + a graded `R`-algebra with basis. + In this class, the `R`-module isomorphism from `A` to `\operatorname{gr} A` is implemented as :meth:`to_graded_conversion` and also as the default conversion from `A` to `\operatorname{gr} A`. Its inverse map is implemented as - :meth:`from_graded_conversion`. The projection - `p_i : F_i \to G_i` is implemented as + :meth:`from_graded_conversion`. + The projection `p_i : F_i \to G_i` is implemented as :meth:`projection` ``(i)``. INPUT: @@ -127,8 +125,8 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): .. TODO:: The algebra ``A`` must currently be an instance of (a subclass of) - :class:`CombinatorialFreeModule`. This should work with any algebra - with a basis. + :class:`CombinatorialFreeModule`. This should work with any + filtered algebra with a basis. .. TODO:: @@ -202,8 +200,8 @@ def _element_constructor_(self, x): Construct an element of ``self`` from ``x``. If ``self`` `= \operatorname{gr} A` for a filtered algebra - `A`, and if ``x`` is an element of `A`, then this returns - the image of `x` under the canonical `R`-module + `A` with basis, and if ``x`` is an element of `A`, then + this returns the image of `x` under the canonical `R`-module isomorphism `A \to \operatorname{gr} A`. (In this case, this is equivalent to calling ``self.to_graded_conversion()(x)``.) @@ -301,8 +299,9 @@ def projection(self, i): sage: grA.projection(8)(p) 0 """ - base_zero = self.base_ring().zero() - base_one = self.base_ring().one() + base_ring = self.base_ring() + base_zero = base_ring.zero() + base_one = base_ring.one() return self._A.module_morphism(diagonal=lambda x: (base_one if self._A.degree_on_basis(x) == i @@ -347,7 +346,8 @@ def algebra_generators(self): @cached_method def one_basis(self): """ - Return the basis index of the element `1`. + Return the basis index of the element `1` of + `\operatorname{gr} A`. This assumes that the unity `1` of `A` belongs to `F_0`. diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py index 000f96bd8bd..c262783a7ab 100644 --- a/src/sage/categories/category_with_axiom.py +++ b/src/sage/categories/category_with_axiom.py @@ -2252,7 +2252,7 @@ def _repr_object_names_static(category, axioms): elif axiom == "Connected" and "graded " in result: result = result.replace("graded ", "graded connected ", 1) elif axiom == "Connected" and "filtered " in result: - result = result.replace("filtered ", "filtered connected ", 1) + result = result.replace("filtered ", "filtered connected ", 1) elif axiom == "Endset" and "homsets" in result: # Without the space at the end to handle Homsets().Endset() result = result.replace("homsets", "endsets", 1) diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py index 1f9bd7bd9bb..9cf0e8d07db 100644 --- a/src/sage/categories/examples/filtered_algebras_with_basis.py +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -22,6 +22,9 @@ class PBWBasisCrossProduct(CombinatorialFreeModule): The Lie algebra is generated by `x,y,z` with brackets defined by `[x, y] = z`, `[y, z] = x`, and `[x, z] = -y`. The universal enveloping algebra has a (PBW) basis consisting of monomials `x^i y^j z^k`. + Despite these monomials not commuting with each other, we + nevertheless label them by the elements of the free abelian monoid + on three generators. INPUT: @@ -46,7 +49,7 @@ def __init__(self, base_ring): EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).Filtered().example() - sage: x,y,z = A.algebra_generators() + sage: x,y,z = [A.algebra_generators()[i] for i in ['x','y','z']] sage: TestSuite(A).run(elements=[x*y+z]) """ I = IndexedFreeAbelianMonoid(['x', 'y', 'z'], prefix='U') @@ -96,11 +99,11 @@ def one_basis(self): def degree_on_basis(self, m): """ - The degree of the element determined by ``m`` in ``self``. + The degree of the basis element of ``self`` labelled by ``m``. INPUT: - - ``m`` -- an element of the free monoid + - ``m`` -- an element of the free abelian monoid OUTPUT: an integer, the degree of the corresponding basis element From 68b47a88d9a650d3633f1b4ba3492b8a7bc3dabe Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 17 Nov 2014 00:05:55 -0800 Subject: [PATCH 021/421] Better return of algebra_generators for filtered alg w/ basis example. --- .../categories/examples/filtered_algebras_with_basis.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py index 9cf0e8d07db..9e7765aefa8 100644 --- a/src/sage/categories/examples/filtered_algebras_with_basis.py +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -49,7 +49,7 @@ def __init__(self, base_ring): EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).Filtered().example() - sage: x,y,z = [A.algebra_generators()[i] for i in ['x','y','z']] + sage: x,y,z = A.algebra_generators() sage: TestSuite(A).run(elements=[x*y+z]) """ I = IndexedFreeAbelianMonoid(['x', 'y', 'z'], prefix='U') @@ -78,12 +78,12 @@ def algebra_generators(self): EXAMPLES:: sage: A = AlgebrasWithBasis(QQ).Filtered().example() - sage: key = lambda x: x.leading_support().to_word_list() - sage: sorted(A.algebra_generators(), key=key) + sage: list(A.algebra_generators()) [U['x'], U['y'], U['z']] """ G = self._indices.monoid_generators() - return Family({x: self.monomial(G[x]) for x in G.keys()}) + I = sorted(G.keys()) + return Family(I, lambda x: self.monomial(G[x])) def one_basis(self): """ From c3b29504eb8ec28ec0fe47b82ef59079f8f494c4 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Mon, 17 Nov 2014 00:25:44 -0800 Subject: [PATCH 022/421] enough for today --- .../examples/filtered_modules_with_basis.py | 6 +- src/sage/categories/filtered_algebras.py | 7 +- .../filtered_algebras_with_basis.py | 74 ++++++++++++++++-- src/sage/categories/filtered_modules.py | 19 ++--- .../categories/filtered_modules_with_basis.py | 77 +++++++++++++------ 5 files changed, 140 insertions(+), 43 deletions(-) diff --git a/src/sage/categories/examples/filtered_modules_with_basis.py b/src/sage/categories/examples/filtered_modules_with_basis.py index d2c8b5d4ef5..9954d6a2ff5 100644 --- a/src/sage/categories/examples/filtered_modules_with_basis.py +++ b/src/sage/categories/examples/filtered_modules_with_basis.py @@ -98,8 +98,8 @@ def __init__(self, base_ring): # This could be a default implementation def degree_on_basis(self, t): """ - The degree of the element determined by the partition ``t`` in - this filtered module. + The degree of the basis element indexed by the partition ``t`` + in this filtered module. INPUT: @@ -122,7 +122,7 @@ def degree_on_basis(self, t): def _repr_(self): """ - Print representation + Print representation of ``self``. EXAMPLES:: diff --git a/src/sage/categories/filtered_algebras.py b/src/sage/categories/filtered_algebras.py index 536ef505a1a..c9444680f0a 100644 --- a/src/sage/categories/filtered_algebras.py +++ b/src/sage/categories/filtered_algebras.py @@ -20,8 +20,8 @@ class FilteredAlgebras(FilteredModulesCategory): (whose underlying `R`-module structure is identical with that of the `R`-algebra `A`) such that the indexing set `I` (typically `I = \NN`) is also an additive abelian monoid, - `F_0 = \{ 0 \}`, and `F_i \cdot F_j \subseteq F_{i+j}` - for all `i, j \in I`. + the unity `1` of `A` belongs to `F_0`, and we have + `F_i \cdot F_j \subseteq F_{i+j}` for all `i, j \in I`. EXAMPLES:: @@ -48,7 +48,8 @@ def graded_algebra(self): .. TODO:: Implement a version of the associated graded algebra - without a distinguished basis. + which does not require ``self`` to have a + distinguished basis. EXAMPLES:: diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index c56fb3878e7..68badc0e2fc 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -41,6 +41,9 @@ def graded_algebra(self): """ Return the associated graded algebra to ``self``. + See :class:`~sage.algebras.associated_graded.AssociatedGradedAlgebra` + for the definition and the properties of this. + EXAMPLES:: sage: A = AlgebrasWithBasis(ZZ).Filtered().example() @@ -53,11 +56,14 @@ def graded_algebra(self): return AssociatedGradedAlgebra(self) class ElementMethods: + def is_homogeneous(self): r""" Return whether ``self`` is homogeneous. - EXAMPLES:: + EXAMPLES: + + Here is a case where the algebra is graded:: sage: S = NonCommutativeSymmetricFunctions(QQ).S() sage: (x, y) = (S[2], S[3]) @@ -67,6 +73,23 @@ def is_homogeneous(self): True sage: ((x + y)^2).is_homogeneous() False + + Let us now test a filtered algebra (but remember that the + notion of homogeneity now depends on the choice of a + basis):: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: x,y,z = A.algebra_generators() + sage: (x*y).is_homogeneous() + True + sage: (y*x).is_homogeneous() + False + sage: A.one().is_homogeneous() + True + sage: A.zero().is_homogeneous() + True + sage: (A.one()+x).is_homogeneous() + False """ degree_on_basis = self.parent().degree_on_basis degree = None @@ -87,11 +110,13 @@ def homogeneous_degree(self): This raises an error if the element is not homogeneous. To obtain the maximum of the degrees of the homogeneous - summands, use :meth:`maximal_degree` + summands, use :meth:`maximal_degree`. .. SEEALSO:: :meth:`maximal_degree` - EXAMPLES:: + EXAMPLES: + + First, an example where the algebra is graded:: sage: S = NonCommutativeSymmetricFunctions(QQ).S() sage: (x, y) = (S[2], S[3]) @@ -104,6 +129,21 @@ def homogeneous_degree(self): ... ValueError: element is not homogeneous + Let us now test a filtered algebra (but remember that the + notion of homogeneity now depends on the choice of a + basis):: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: x,y,z = A.algebra_generators() + sage: (x*y).homogeneous_degree() + 2 + sage: (y*x).homogeneous_degree() + Traceback (most recent call last): + ... + ValueError: element is not homogeneous + sage: A.one().homogeneous_degree() + 0 + TESTS:: sage: S = NonCommutativeSymmetricFunctions(QQ).S() @@ -123,11 +163,18 @@ def homogeneous_degree(self): def maximal_degree(self): """ - The maximum of the degrees of the homogeneous summands. + The maximum of the degrees of the homogeneous components + of ``self``. + + This is also the smallest `i` such that ``self`` belongs + to `F_i`. Hence, it does not depend on the basis of the + parent of ``self``. .. SEEALSO:: :meth:`homogeneous_degree` - EXAMPLES:: + EXAMPLES: + + First, we test this on a graded algebra:: sage: S = NonCommutativeSymmetricFunctions(QQ).S() sage: (x, y) = (S[2], S[3]) @@ -138,6 +185,23 @@ def maximal_degree(self): sage: ((1 + x)^3).maximal_degree() 6 + Let us now test a filtered algebra:: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: x,y,z = A.algebra_generators() + sage: (x*y).maximal_degree() + 2 + sage: (y*x).maximal_degree() + 2 + sage: A.one().maximal_degree() + 0 + sage: A.zero().maximal_degree() + Traceback (most recent call last): + ... + ValueError: the zero element does not have a well-defined degree + sage: (A.one()+x).maximal_degree() + 1 + TESTS:: sage: S = NonCommutativeSymmetricFunctions(QQ).S() diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index 8b4bba1bf2e..af5f0f83fc4 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -3,9 +3,9 @@ A *filtered module* over a commutative ring `R` with a totally ordered indexing set `I` (typically `I = \NN`) is an `R`-module `M` equipped -with a sequence `(F_i)_{i \in I}` of `R`-submodules satisfying -`F_i \subseteq F_j` if `i \leq j` for all `i,j \in I` and -`M = \bigcup_{i \in I} F_i`. This sequence is called a *filtration* +with a family `(F_i)_{i \in I}` of `R`-submodules satisfying +`F_i \subseteq F_j` for all `i,j \in I` having `i \leq j`, and +`M = \bigcup_{i \in I} F_i`. This family is called a *filtration* of the given module `M`. .. TODO:: @@ -139,13 +139,13 @@ def _repr_object_names(self): class FilteredModules(FilteredModulesCategory): r""" - The category of filtered modules. + The category of filtered modules over a given commutative ring `R`. A *filtered module* over a commutative ring `R` with a totally ordered indexing set `I` (typically `I = \NN`) is an `R`-module `M` equipped - with a sequence `(F_i)_{i \in I}` of `R`-submodules satisfying - `F_i \subseteq F_j` if `i \leq j` for all `i, j \in I` and - `M = \bigcup_{i \in I} F_i`. This sequence is called a *filtration* + with a family `(F_i)_{i \in I}` of `R`-submodules satisfying + `F_i \subseteq F_j` for all `i,j \in I` having `i \leq j`, and + `M = \bigcup_{i \in I} F_i`. This family is called a *filtration* of the given module `M`. EXAMPLES:: @@ -206,8 +206,9 @@ def Connected(self): Return the full subcategory of the connected objects of ``self``. A filtered `R`-module `M` with filtration - `(F_0, F_1, F_2, \ldots)` is said to be - *connected* if `F_0` is isomorphic to `R`. + `(F_0, F_1, F_2, \ldots)` (indexed by `\NN`) + is said to be *connected* if `F_0` is isomorphic + to `R`. EXAMPLES:: diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index efe84a1416b..cee97aabcef 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -4,16 +4,18 @@ A *filtered module with basis* over a commutative ring `R` means (for the purpose of this code) a filtered `R`-module `M` with filtration `(F_i)_{i \in I}` (typically `I = \NN`) -endowed with a basis `(b_j)_{j \in J}` of `M` and a partition of -the set `J = \bigsqcup_{i \in I} J_i` (which can be empty) such -that for every `n \in I`, the subfamily `(b_j)_{j \in U_n}`, where -`U_n = \bigcup_{i \leq n} J_i`, is a basis of the `R`-submodule `F_n`. +endowed with a basis `(b_j)_{j \in J}` of `M` and a partition +`J = \bigsqcup_{i \in I} J_i` of the set `J` (it is allowed +that some `J_i` are empty) such that for every `n \in I`, +the subfamily `(b_j)_{j \in U_n}`, where +`U_n = \bigcup_{i \leq n} J_i`, is a basis of the +`R`-submodule `F_n`. For every `i \in I`, the `R`-submodule of `M` spanned by `(b_j)_{j \in J_i}` is called the `i`-*th graded component* -of the filtered module with basis `M`; the elements of -this submodule are referred to as *homogeneous elements of -degree* `i`. +(aka the `i`-*th homogeneous component*) of the filtered +module with basis `M`; the elements of this submodule are +referred to as *homogeneous elements of degree* `i`. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw @@ -31,23 +33,28 @@ class FilteredModulesWithBasis(FilteredModulesCategory): A *filtered module with basis* over a commutative ring `R` means (for the purpose of this code) a filtered `R`-module `M` with filtration `(F_i)_{i \in I}` (typically `I = \NN`) - endowed with a basis `(b_j)_{j \in J}` of `M` and a partition of - the set `J = \bigsqcup_{i \in I} J_i` (which can be empty) such - that for every `n \in I`, the subfamily `(b_j)_{j \in U_n}`, where - `U_n = \bigcup_{i \leq n} J_i`, is a basis of the `R`-submodule `F_n`. + endowed with a basis `(b_j)_{j \in J}` of `M` and a partition + `J = \bigsqcup_{i \in I} J_i` of the set `J` (it is allowed + that some `J_i` are empty) such that for every `n \in I`, + the subfamily `(b_j)_{j \in U_n}`, where + `U_n = \bigcup_{i \leq n} J_i`, is a basis of the + `R`-submodule `F_n`. For every `i \in I`, the `R`-submodule of `M` spanned by - `(b_j)_{j \in J_i}` is called the `i`-*th graded component* of the - filtered module with basis `M`; the elements of this submodule are - referred to as *homogeneous elements of degree* `i`. The `R`-module - `M` is the direct sum of its `i`-th graded components over - all `i \in I`, and thus becomes a graded `R`-module with basis. - Conversely, any graded `R`-module with basis canonically becomes a filtered - `R`-module with basis (by defining `F_n = \bigoplus_{i \leq n} G_i` - where `G_i` is the `i`-th graded component and `J_i` as the indexing - set of the basis of the `i`-th graded component). Hence, the notion - of a filtered `R`-module with basis is equivalent to the notion of - a graded `R`-module with basis. + `(b_j)_{j \in J_i}` is called the `i`-*th graded component* + (aka the `i`-*th homogeneous component*) of the filtered + module with basis `M`; the elements of this submodule are + referred to as *homogeneous elements of degree* `i`. + The `R`-module `M` is the direct sum of its `i`-th graded + components over all `i \in I`, and thus becomes a graded + `R`-module with basis. + Conversely, any graded `R`-module with basis canonically + becomes a filtered `R`-module with basis (by defining + `F_n = \bigoplus_{i \leq n} G_i` where `G_i` is the `i`-th + graded component, and defining `J_i` as the indexing set + of the basis of the `i`-th graded component). Hence, the + notion of a filtered `R`-module with basis is equivalent + to the notion of a graded `R`-module with basis. However, the *category* of filtered `R`-modules with basis is not the category of graded `R`-modules with basis. Indeed, the *morphisms* @@ -58,6 +65,22 @@ class FilteredModulesWithBasis(FilteredModulesCategory): the notion of a filtered algebra with basis differs from that of a graded algebra with basis. + .. NOTE:: + + Currently, to make use of the functionality of this class, + an instance of ``FilteredModulesWithBasis`` should fulfill + the contract of a :class:`CombinatorialFreeModule` (most + likely by inheriting from it). It should also have the + indexing set `J` encoded as its ``_indices`` attribute, + and ``_indices.subset(basis=i)`` should yield the subset + `J_i` (as an iterable). If the latter conditions are not + satisfied, then :meth:`basis` must be overridden. + + .. TODO:: + + This deserves to be handled better, and the contracts + involved might also profit from some explicit writing-up. + EXAMPLES:: sage: C = ModulesWithBasis(ZZ).Filtered(); C @@ -92,7 +115,9 @@ def basis(self, d=None): - ``d`` -- (optional, default ``None``) nonnegative integer or ``None`` - If ``d`` is ``None``, returns a basis of the module. + OUTPUT: + + If ``d`` is ``None``, returns the basis of the module. Otherwise, returns the basis of the homogeneous component of degree ``d`` (i.e., the subfamily of the basis of the whole module which consists only of the basis vectors @@ -178,6 +203,12 @@ def degree(self): raise ValueError("element is not homogeneous") return self.parent().degree_on_basis(self.leading_support()) +# .. TODO:: +# +# maximal_degree. This actually does not depend on the basis +# and can probably be copied, up to doctests, from +# filtered_algebras_with_basis.py. + def homogeneous_component(self, n): """ Return the homogeneous component of degree ``n`` of this From 2d2929600e9e94b54cd4f9af0c19f6cd465fea2d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 17 Nov 2014 08:11:03 -0800 Subject: [PATCH 023/421] Added option to consider the clifford algebra as filtered or graded. --- src/sage/algebras/clifford_algebra.py | 93 +++++++++++++++++++++------ 1 file changed, 74 insertions(+), 19 deletions(-) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index af6ce5a280f..c22b1581ad2 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -454,6 +454,8 @@ class CliffordAlgebra(CombinatorialFreeModule): - ``Q`` -- a quadratic form - ``names`` -- (default: ``'e'``) the generator names + - ``graded`` -- (default: ``True``) if ``True``, then use the `\ZZ / 2\ZZ` + grading, otherwise use the `\ZZ` filtration EXAMPLES: @@ -463,7 +465,8 @@ class CliffordAlgebra(CombinatorialFreeModule): sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) sage: Cl = CliffordAlgebra(Q) sage: Cl - The Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: + The graded Clifford algebra of the Quadratic form in 3 variables + over Integer Ring with coefficients: [ 1 2 3 ] [ * 4 5 ] [ * * 6 ] @@ -485,7 +488,7 @@ class CliffordAlgebra(CombinatorialFreeModule): will be changed once :trac:`17096` is finished. """ @staticmethod - def __classcall_private__(cls, Q, names=None): + def __classcall_private__(cls, Q, names=None, graded=True): """ Normalize arguments to ensure a unique representation. @@ -508,9 +511,9 @@ def __classcall_private__(cls, Q, names=None): names = tuple( '{}{}'.format(names[0], i) for i in range(Q.dim()) ) else: raise ValueError("the number of variables does not match the number of generators") - return super(CliffordAlgebra, cls).__classcall__(cls, Q, names) + return super(CliffordAlgebra, cls).__classcall__(cls, Q, names, graded=bool(graded)) - def __init__(self, Q, names, category=None): + def __init__(self, Q, names, category=None, graded=True): r""" Initialize ``self``. @@ -519,6 +522,8 @@ def __init__(self, Q, names, category=None): sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) sage: Cl = CliffordAlgebra(Q) sage: TestSuite(Cl).run() + sage: Cl = CliffordAlgebra(Q, graded=False) + sage: TestSuite(Cl).run() TESTS: @@ -533,9 +538,13 @@ def __init__(self, Q, names, category=None): True """ self._quadratic_form = Q + self._graded = graded R = Q.base_ring() if category is None: - category = AlgebrasWithBasis(R).Filtered() + if graded: + category = AlgebrasWithBasis(R).Graded() + else: + category = AlgebrasWithBasis(R).Filtered() indices = SubsetsSorted(range(Q.dim())) CombinatorialFreeModule.__init__(self, R, indices, category=category) self._assign_names(names) @@ -548,12 +557,20 @@ def _repr_(self): sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) sage: CliffordAlgebra(Q) - The Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: + The graded Clifford algebra of the Quadratic form in 3 variables + over Integer Ring with coefficients: + [ 1 2 3 ] + [ * 4 5 ] + [ * * 6 ] + sage: CliffordAlgebra(Q, graded=False) + The filtered Clifford algebra of the Quadratic form in 3 variables + over Integer Ring with coefficients: [ 1 2 3 ] [ * 4 5 ] [ * * 6 ] """ - return "The Clifford algebra of the {}".format(self._quadratic_form) + gr = "graded" if self._graded else "filtered" + return "The {} Clifford algebra of the {}".format(gr, self._quadratic_form) def _repr_term(self, m): """ @@ -667,12 +684,24 @@ def _coerce_map_from_(self, V): sage: b = Cl.basis()[(0,2)] sage: Clp(3*a-4*b) 2*e0*e2 + + The filtered Clifford algebra coerces into the graded Clifford + algebra, but not the other way around:: + + sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) + sage: Cl = CliffordAlgebra(Q) + sage: ClF = CliffordAlgebra(Q, graded=False) + sage: Cl.has_coerce_map_from(ClF) + True + sage: ClF.has_coerce_map_from(Cl) + False """ if isinstance(V, CliffordAlgebra): Q = self._quadratic_form try: return (V.variable_names() == self.variable_names() and - V._quadratic_form.base_change_to(self.base_ring()) == Q) + V._quadratic_form.base_change_to(self.base_ring()) == Q + and (self._graded or not V._graded)) except Exception: return False @@ -835,19 +864,22 @@ def degree_on_basis(self, m): r""" Return the degree of the monomial indexed by ``m``. - This degree is a nonnegative integer, and should be interpreted - as a residue class modulo `2`, since we consider ``self`` to be - `\ZZ_2`-graded (not `\ZZ`-graded, although there is a natural - *filtration* by the length of ``m``). The degree of the monomial - ``m`` in this `\ZZ_2`-grading is defined to be the length of ``m`` - taken mod `2`. + If we consider the Clifford algebra to be `\ZZ_2`-graded, this + degree is a nonnegative integer, and should be interpreted as a + residue class modulo `2`. The degree of the monomial ``m`` in this + `\ZZ_2`-grading is defined to be the length of ``m`` taken mod `2`. + + Otherwise we are considering the Clifford algebra to be + `\NN`-filtered, and the degree of the monomial ``m`` is the + length of ``m``. .. WARNING: On the :class:`ExteriorAlgebra` class (which inherits from :class:`CliffordAlgebra`), the :meth:`degree_on_basis` - method is overridden to return an actual `\NN`-degree. So - don't count on this method always returning `0` or `1` !! + method is overridden to always return an actual `\NN`-degree + since it is `\NN`-graded. So don't count on this method + always returning `0` or `1` !! EXAMPLES:: @@ -857,8 +889,31 @@ def degree_on_basis(self, m): 1 sage: Cl.degree_on_basis((0,1)) 0 + sage: Cl. = CliffordAlgebra(Q, graded=False) + sage: Cl.degree_on_basis((0,1)) + 2 + """ + if self._graded: + return len(m) % ZZ(2) + return ZZ(len(m)) + + def graded_algebra(self): + """ + Return the associated graded algebra of ``self``. + + EXAMPLES:: + + sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) + sage: Cl. = CliffordAlgebra(Q) + sage: Cl.graded_algebra() is Cl + True + sage: Cl. = CliffordAlgebra(Q, graded=False) + sage: Cl.graded_algebra() + The exterior algebra of rank 3 over Integer Ring """ - return len(m) % ZZ(2) + if self._graded: + return self + return ExteriorAlgebra(self.base_ring(), self.variable_names()) @cached_method def free_module(self): @@ -956,11 +1011,11 @@ def lift_module_morphism(self, m, names=None): sage: phi = Cl.lift_module_morphism(m, 'abc') sage: phi Generic morphism: - From: The Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: + From: The graded Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: [ 10 17 3 ] [ * 11 0 ] [ * * 5 ] - To: The Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: + To: The graded Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: [ 1 2 3 ] [ * 4 5 ] [ * * 6 ] From 096538121130679a55b971d3d3a5626e94b157a5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 19 Nov 2014 15:54:34 -0800 Subject: [PATCH 024/421] Changes from discussion on trac. --- src/sage/algebras/associated_graded.py | 104 ++------- src/sage/algebras/clifford_algebra.py | 7 +- src/sage/algebras/weyl_algebra.py | 7 +- .../filtered_algebras_with_basis.py | 211 ++++++------------ src/sage/categories/filtered_modules.py | 9 +- .../categories/filtered_modules_with_basis.py | 135 ++++++++++- 6 files changed, 225 insertions(+), 248 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index 1fddb176f6b..bc75474a798 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -147,7 +147,7 @@ def __init__(self, A, category=None): sage: grA = A.graded_algebra() sage: TestSuite(grA).run(elements=[prod(grA.algebra_generators())]) """ - if A not in AlgebrasWithBasis(A.base_ring()).Filtered(): + if A not in AlgebrasWithBasis(A.base_ring().category()).Filtered(): raise ValueError("the base algebra must be filtered and with basis") self._A = A @@ -220,94 +220,6 @@ def _element_constructor_(self, x): return self._from_dict(dict(x)) return super(AssociatedGradedAlgebra, self)._element_constructor_(x) - # Maps - - def to_graded_conversion(self): - r""" - Return the canonical `R`-module isomorphism - `A \to \operatorname{gr} A` induced by the basis of `A`. - - This is an isomorphism of `R`-modules, not of algebras. See - the class-wide documentation :class:`AssociatedGradedAlgebra`. - - .. SEEALSO:: - - :meth:`from_graded_conversion`. - - EXAMPLES:: - - sage: A = Algebras(QQ).WithBasis().Filtered().example() - sage: grA = A.graded_algebra() - sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 - sage: grA.to_graded_conversion()(p) - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) - """ - base_one = self.base_ring().one() - return self._A.module_morphism(diagonal=lambda x: base_one, - codomain=self) - - def from_graded_conversion(self): - r""" - Return the inverse of the canonical `R`-module isomorphism - `A \to \operatorname{gr} A` induced by the basis of `A`. - - This is an isomorphism of `R`-modules, not of algebras. See - the class-wide documentation :class:`AssociatedGradedAlgebra`. - - .. SEEALSO:: - - :meth:`to_graded_conversion`. - - EXAMPLES:: - - sage: A = Algebras(QQ).WithBasis().Filtered().example() - sage: grA = A.graded_algebra() - sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 - sage: q = grA.to_graded_conversion()(p) - sage: grA.from_graded_conversion()(q) == p - True - """ - base_one = self.base_ring().one() - return self.module_morphism(diagonal=lambda x: base_one, - codomain=self._A) - - def projection(self, i): - r""" - Return the `i`-th projection `p_i : F_i \to G_i` (in the - notations of the class-wide documentation - :class:`AssociatedGradedAlgebra`). - - This method actually does not return the map `p_i` itself, - but an extension of `p_i` to the whole `R`-module `A`. - This extension is the composition of the `R`-module - isomorphism `A \to \operatorname{gr} A` with the canonical - projection of the graded `R`-module `\operatorname{gr} A` - onto its `i`-th graded component `G_i`. The codomain of - this map is `\operatorname{gr} A`, although its actual - image is `G_i`. - - EXAMPLES:: - - sage: A = Algebras(QQ).WithBasis().Filtered().example() - sage: grA = A.graded_algebra() - sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 - sage: grA.projection(7)(p) - bar(U['x']^2*U['y']^2*U['z']^3) - sage: grA.projection(8)(p) - 0 - """ - base_ring = self.base_ring() - base_zero = base_ring.zero() - base_one = base_ring.one() - return self._A.module_morphism(diagonal=lambda x: - (base_one if - self._A.degree_on_basis(x) == i - else base_zero), - codomain=self) - def gen(self, *args, **kwds): """ Return a generator of ``self``. @@ -343,6 +255,20 @@ def algebra_generators(self): G = self._A.algebra_generators() return Family(G.keys(), lambda x: self(G[x]), name="generator") + def degree_on_basis(self, x): + """ + Return the degree on the basis element indexed by ``x``. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: all(A.degree_on_basis(x) == grA.degree_on_basis(x) + ....: for g in grA.algebra_generators() for x in g.support()) + True + """ + return self._A.degree_on_basis(x) + @cached_method def one_basis(self): """ diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index c22b1581ad2..40170457e55 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -542,9 +542,9 @@ def __init__(self, Q, names, category=None, graded=True): R = Q.base_ring() if category is None: if graded: - category = AlgebrasWithBasis(R).Graded() + category = AlgebrasWithBasis(R.category()).Graded() else: - category = AlgebrasWithBasis(R).Filtered() + category = AlgebrasWithBasis(R.category()).Filtered() indices = SubsetsSorted(range(Q.dim())) CombinatorialFreeModule.__init__(self, R, indices, category=category) self._assign_names(names) @@ -1473,7 +1473,8 @@ def __init__(self, R, names): sage: E. = ExteriorAlgebra(QQ) sage: TestSuite(E).run() """ - CliffordAlgebra.__init__(self, QuadraticForm(R, len(names)), names, GradedHopfAlgebrasWithBasis(R)) + cat = GradedHopfAlgebrasWithBasis(R.category()) + CliffordAlgebra.__init__(self, QuadraticForm(R, len(names)), names, cat) # TestSuite will fail if the HopfAlgebra classes will ever have tests for # the coproduct being an algebra morphism -- since this is really a # Hopf superalgebra, not a Hopf algebra. diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index a2a1050a00e..1727f8e8377 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -546,6 +546,11 @@ class DifferentialWeylAlgebra(Algebra, UniqueRepresentation): sage: W. = DifferentialWeylAlgebra(QQ); W Differential Weyl algebra of polynomials in a, b over Rational Field + + .. TODO:: + + Implement the :meth:`graded_algebra` as a polynomial ring once + they are considered to be graded rings (algebras). """ @staticmethod def __classcall__(cls, R, names=None): @@ -584,7 +589,7 @@ def __init__(self, R, names=None): names = names + tuple('d' + n for n in names) if len(names) != self._n * 2: raise ValueError("variable names cannot differ by a leading 'd'") - cat = AlgebrasWithBasis(R).NoZeroDivisors().Filtered() + cat = AlgebrasWithBasis(R.category()).NoZeroDivisors().Filtered() Algebra.__init__(self, R, names, category=cat) def _repr_(self): diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 68badc0e2fc..7756d5a3a52 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -55,163 +55,96 @@ def graded_algebra(self): from sage.algebras.associated_graded import AssociatedGradedAlgebra return AssociatedGradedAlgebra(self) - class ElementMethods: + # Maps - def is_homogeneous(self): + def to_graded_conversion(self): r""" - Return whether ``self`` is homogeneous. + Return the canonical `R`-module isomorphism + `A \to \operatorname{gr} A` induced by the basis of `A`. - EXAMPLES: + This is an isomorphism of `R`-modules, not of algebras. See + the class documentation :class:`AssociatedGradedAlgebra`. - Here is a case where the algebra is graded:: + .. SEEALSO:: - sage: S = NonCommutativeSymmetricFunctions(QQ).S() - sage: (x, y) = (S[2], S[3]) - sage: (3*x).is_homogeneous() - True - sage: (x^3 - y^2).is_homogeneous() - True - sage: ((x + y)^2).is_homogeneous() - False + :meth:`from_graded_conversion` - Let us now test a filtered algebra (but remember that the - notion of homogeneity now depends on the choice of a - basis):: + EXAMPLES:: - sage: A = AlgebrasWithBasis(QQ).Filtered().example() - sage: x,y,z = A.algebra_generators() - sage: (x*y).is_homogeneous() - True - sage: (y*x).is_homogeneous() - False - sage: A.one().is_homogeneous() - True - sage: A.zero().is_homogeneous() + 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 + U['x'] + 2 + sage: q = A.to_graded_conversion()(p); q + bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + sage: q.parent() is A.graded_algebra() True - sage: (A.one()+x).is_homogeneous() - False - """ - degree_on_basis = self.parent().degree_on_basis - degree = None - for m in self.support(): - if degree is None: - degree = degree_on_basis(m) - else: - if degree != degree_on_basis(m): - return False - return True - - def homogeneous_degree(self): - """ - The degree of a nonzero homogeneous element ``self`` in the - filtered module. - - .. NOTE:: - - This raises an error if the element is not homogeneous. - To obtain the maximum of the degrees of the homogeneous - summands, use :meth:`maximal_degree`. - - .. SEEALSO:: :meth:`maximal_degree` - - EXAMPLES: - - First, an example where the algebra is graded:: - - sage: S = NonCommutativeSymmetricFunctions(QQ).S() - sage: (x, y) = (S[2], S[3]) - sage: x.homogeneous_degree() - 2 - sage: (x^3 + 4*y^2).homogeneous_degree() - 6 - sage: ((1 + x)^3).homogeneous_degree() - Traceback (most recent call last): - ... - ValueError: element is not homogeneous - - Let us now test a filtered algebra (but remember that the - notion of homogeneity now depends on the choice of a - basis):: - - sage: A = AlgebrasWithBasis(QQ).Filtered().example() - sage: x,y,z = A.algebra_generators() - sage: (x*y).homogeneous_degree() - 2 - sage: (y*x).homogeneous_degree() - Traceback (most recent call last): - ... - ValueError: element is not homogeneous - sage: A.one().homogeneous_degree() - 0 - - TESTS:: - - sage: S = NonCommutativeSymmetricFunctions(QQ).S() - sage: S.zero().degree() - Traceback (most recent call last): - ... - ValueError: the zero element does not have a well-defined degree """ - if self.is_zero(): - raise ValueError("the zero element does not have a well-defined degree") - if not self.is_homogeneous(): - raise ValueError("element is not homogeneous") - return self.parent().degree_on_basis(self.leading_support()) + base_one = self.base_ring().one() + return self.module_morphism(diagonal=lambda x: base_one, + codomain=self.graded_algebra()) - # default choice for degree; will be overridden as necessary - degree = homogeneous_degree + def from_graded_conversion(self): + r""" + Return the inverse of the canonical `R`-module isomorphism + `A \to \operatorname{gr} A` induced by the basis of `A` + (that is a map `\operatorname{gr} A \to A`). - def maximal_degree(self): - """ - The maximum of the degrees of the homogeneous components - of ``self``. + This is an isomorphism of `R`-modules, not of algebras. See + the class documentation :class:`AssociatedGradedAlgebra`. - This is also the smallest `i` such that ``self`` belongs - to `F_i`. Hence, it does not depend on the basis of the - parent of ``self``. + .. SEEALSO:: - .. SEEALSO:: :meth:`homogeneous_degree` + :meth:`to_graded_conversion` - EXAMPLES: + EXAMPLES:: - First, we test this on a graded algebra:: + 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 + U['x'] + 2 + sage: q = A.to_graded_conversion()(p) + sage: A.from_graded_conversion()(q) == p + True + sage: q.parent() is A.graded_algebra() + True + """ + base_one = self.base_ring().one() + return self.graded_algebra().module_morphism(diagonal=lambda x: base_one, + codomain=self) - sage: S = NonCommutativeSymmetricFunctions(QQ).S() - sage: (x, y) = (S[2], S[3]) - sage: x.maximal_degree() - 2 - sage: (x^3 + 4*y^2).maximal_degree() - 6 - sage: ((1 + x)^3).maximal_degree() - 6 + def projection(self, i): + r""" + Return the `i`-th projection `p_i : F_i \to G_i` (in the + notations of the class documentation + :class:`AssociatedGradedAlgebra`). + + This method actually does not return the map `p_i` itself, + but an extension of `p_i` to the whole `R`-module `A`. + This extension is the composition of the `R`-module + isomorphism `A \to \operatorname{gr} A` with the canonical + projection of the graded `R`-module `\operatorname{gr} A` + onto its `i`-th graded component `G_i`. The codomain of + this map is `\operatorname{gr} A`, although its actual + image is `G_i`. - Let us now test a filtered algebra:: + EXAMPLES:: - sage: A = AlgebrasWithBasis(QQ).Filtered().example() - sage: x,y,z = A.algebra_generators() - sage: (x*y).maximal_degree() - 2 - sage: (y*x).maximal_degree() - 2 - sage: A.one().maximal_degree() + 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 + U['x'] + 2 + sage: q = A.projection(7)(p); q + bar(U['x']^2*U['y']^2*U['z']^3) + sage: q.parent() is A.graded_algebra() + True + sage: A.projection(8)(p) 0 - sage: A.zero().maximal_degree() - Traceback (most recent call last): - ... - ValueError: the zero element does not have a well-defined degree - sage: (A.one()+x).maximal_degree() - 1 - - TESTS:: - - sage: S = NonCommutativeSymmetricFunctions(QQ).S() - sage: S.zero().degree() - Traceback (most recent call last): - ... - ValueError: the zero element does not have a well-defined degree """ - if self.is_zero(): - raise ValueError("the zero element does not have a well-defined degree") - degree_on_basis = self.parent().degree_on_basis - return max(degree_on_basis(m) for m in self.support()) + base_zero = self.base_ring().zero() + base_one = self.base_ring().one() + grA = self.graded_algebra() + proj = lambda x: (base_one if grA.degree_on_basis(x) == i + else base_zero) + return self.module_morphism(diagonal=proj, codomain=grA) + + class ElementMethods: + pass diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index af5f0f83fc4..784b0faaf59 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -1,7 +1,7 @@ r""" Filtered Modules -A *filtered module* over a commutative ring `R` with a totally ordered +A *filtered module* over a ring `R` with a totally ordered indexing set `I` (typically `I = \NN`) is an `R`-module `M` equipped with a family `(F_i)_{i \in I}` of `R`-submodules satisfying `F_i \subseteq F_j` for all `i,j \in I` having `i \leq j`, and @@ -10,7 +10,8 @@ .. TODO:: - Implement a notion for decreasing filtrations: where `F_j \subseteq F_i`. + Implement a notion for decreasing filtrations: where `F_j \subseteq F_i` + when `i \leq j`. .. TODO:: @@ -139,9 +140,9 @@ def _repr_object_names(self): class FilteredModules(FilteredModulesCategory): r""" - The category of filtered modules over a given commutative ring `R`. + The category of filtered modules over a given ring `R`. - A *filtered module* over a commutative ring `R` with a totally ordered + A *filtered module* over a ring `R` with a totally ordered indexing set `I` (typically `I = \NN`) is an `R`-module `M` equipped with a family `(F_i)_{i \in I}` of `R`-submodules satisfying `F_i \subseteq F_j` for all `i,j \in I` having `i \leq j`, and diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index cee97aabcef..6feee58383e 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -1,9 +1,9 @@ r""" Filtered Modules With Basis -A *filtered module with basis* over a commutative ring `R` -means (for the purpose of this code) a filtered `R`-module -`M` with filtration `(F_i)_{i \in I}` (typically `I = \NN`) +A *filtered module with basis* over a ring `R` means +(for the purpose of this code) a filtered `R`-module `M` +with filtration `(F_i)_{i \in I}` (typically `I = \NN`) endowed with a basis `(b_j)_{j \in J}` of `M` and a partition `J = \bigsqcup_{i \in I} J_i` of the set `J` (it is allowed that some `J_i` are empty) such that for every `n \in I`, @@ -30,9 +30,9 @@ class FilteredModulesWithBasis(FilteredModulesCategory): r""" The category of filtered modules with a distinguished basis. - A *filtered module with basis* over a commutative ring `R` - means (for the purpose of this code) a filtered `R`-module - `M` with filtration `(F_i)_{i \in I}` (typically `I = \NN`) + A *filtered module with basis* over a ring `R` means + (for the purpose of this code) a filtered `R`-module `M` + with filtration `(F_i)_{i \in I}` (typically `I = \NN`) endowed with a basis `(b_j)_{j \in J}` of `M` and a partition `J = \bigsqcup_{i \in I} J_i` of the set `J` (it is allowed that some `J_i` are empty) such that for every `n \in I`, @@ -160,6 +160,32 @@ def is_homogeneous(self): False sage: (x+2*z).is_homogeneous() True + + Here is a case where the algebra is graded:: + + sage: S = NonCommutativeSymmetricFunctions(QQ).S() + sage: (x, y) = (S[2], S[3]) + sage: (3*x).is_homogeneous() + True + sage: (x^3 - y^2).is_homogeneous() + True + sage: ((x + y)^2).is_homogeneous() + False + + Let us now test a filtered algebra:: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: x,y,z = A.algebra_generators() + sage: (x*y).is_homogeneous() + True + sage: (y*x).is_homogeneous() + False + sage: A.one().is_homogeneous() + True + sage: A.zero().is_homogeneous() + True + sage: (A.one()+x).is_homogeneous() + False """ degree_on_basis = self.parent().degree_on_basis degree = None @@ -171,7 +197,7 @@ def is_homogeneous(self): return False return True - def degree(self): + def homogeneous_degree(self): r""" The degree of a nonzero homogeneous element ``self`` in the filtered module. @@ -196,6 +222,40 @@ def degree(self): Traceback (most recent call last): ... ValueError: element is not homogeneous + + An example where the algebra is graded:: + + sage: S = NonCommutativeSymmetricFunctions(QQ).S() + sage: (x, y) = (S[2], S[3]) + sage: x.homogeneous_degree() + 2 + sage: (x^3 + 4*y^2).homogeneous_degree() + 6 + sage: ((1 + x)^3).homogeneous_degree() + Traceback (most recent call last): + ... + ValueError: element is not homogeneous + + Let us now test a filtered algebra:: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: x,y,z = A.algebra_generators() + sage: (x*y).homogeneous_degree() + 2 + sage: (y*x).homogeneous_degree() + Traceback (most recent call last): + ... + ValueError: element is not homogeneous + sage: A.one().homogeneous_degree() + 0 + + TESTS:: + + sage: S = NonCommutativeSymmetricFunctions(QQ).S() + sage: S.zero().degree() + Traceback (most recent call last): + ... + ValueError: the zero element does not have a well-defined degree """ if not self.support(): raise ValueError("the zero element does not have a well-defined degree") @@ -203,11 +263,62 @@ def degree(self): raise ValueError("element is not homogeneous") return self.parent().degree_on_basis(self.leading_support()) -# .. TODO:: -# -# maximal_degree. This actually does not depend on the basis -# and can probably be copied, up to doctests, from -# filtered_algebras_with_basis.py. + # default choice for degree; will be overridden as necessary + degree = homogeneous_degree + + def maximal_degree(self): + """ + The maximum of the degrees of the homogeneous components + of ``self``. + + This is also the smallest `i` such that ``self`` belongs + to `F_i`. Hence, it does not depend on the basis of the + parent of ``self``. + + .. SEEALSO:: :meth:`homogeneous_degree` + + EXAMPLES: + + First, we test this on a graded algebra:: + + sage: S = NonCommutativeSymmetricFunctions(QQ).S() + sage: (x, y) = (S[2], S[3]) + sage: x.maximal_degree() + 2 + sage: (x^3 + 4*y^2).maximal_degree() + 6 + sage: ((1 + x)^3).maximal_degree() + 6 + + Let us now test a filtered algebra:: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example() + sage: x,y,z = A.algebra_generators() + sage: (x*y).maximal_degree() + 2 + sage: (y*x).maximal_degree() + 2 + sage: A.one().maximal_degree() + 0 + sage: A.zero().maximal_degree() + Traceback (most recent call last): + ... + ValueError: the zero element does not have a well-defined degree + sage: (A.one()+x).maximal_degree() + 1 + + TESTS:: + + sage: S = NonCommutativeSymmetricFunctions(QQ).S() + sage: S.zero().degree() + Traceback (most recent call last): + ... + ValueError: the zero element does not have a well-defined degree + """ + if self.is_zero(): + raise ValueError("the zero element does not have a well-defined degree") + degree_on_basis = self.parent().degree_on_basis + return max(degree_on_basis(m) for m in self.support()) def homogeneous_component(self, n): """ From 79894b4c5b248281fbba1b932171b18a59e5b137 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Sun, 23 Nov 2014 17:55:25 -0800 Subject: [PATCH 025/421] filtered_modules(_with_basis).py reviewed -- see TODOs --- src/sage/algebras/associated_graded.py | 2 +- .../categories/filtered_modules_with_basis.py | 159 ++++++++++++++++-- 2 files changed, 146 insertions(+), 15 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index bc75474a798..f286964aa95 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -257,7 +257,7 @@ def algebra_generators(self): def degree_on_basis(self, x): """ - Return the degree on the basis element indexed by ``x``. + Return the degree of the basis element indexed by ``x``. EXAMPLES:: diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 6feee58383e..326a099fcfb 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -16,6 +16,10 @@ (aka the `i`-*th homogeneous component*) of the filtered module with basis `M`; the elements of this submodule are referred to as *homogeneous elements of degree* `i`. + +See the class documentation +:class:`~sage.categories.filtered_modules_with_basis.FilteredModulesWithBasis` +for further details. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw @@ -72,7 +76,7 @@ class FilteredModulesWithBasis(FilteredModulesCategory): the contract of a :class:`CombinatorialFreeModule` (most likely by inheriting from it). It should also have the indexing set `J` encoded as its ``_indices`` attribute, - and ``_indices.subset(basis=i)`` should yield the subset + and ``_indices.subset(size=i)`` should yield the subset `J_i` (as an iterable). If the latter conditions are not satisfied, then :meth:`basis` must be overridden. @@ -81,6 +85,11 @@ class FilteredModulesWithBasis(FilteredModulesCategory): This deserves to be handled better, and the contracts involved might also profit from some explicit writing-up. + What else should be part of the requirements for + inheriting from :class:`FilteredModulesWithBasis`? + At least having a ``degree_on_basis`` method? Is that + enough? + EXAMPLES:: sage: C = ModulesWithBasis(ZZ).Filtered(); C @@ -123,18 +132,57 @@ def basis(self, d=None): whole module which consists only of the basis vectors lying in `F_d \setminus \bigcup_{i Date: Sun, 23 Nov 2014 19:55:05 -0800 Subject: [PATCH 026/421] looked through clifford_algebra and filtered_algebras*; many TODOs left --- src/sage/algebras/clifford_algebra.py | 60 +++++++- .../filtered_algebras_with_basis.py | 128 +++++++++++++++++- .../categories/graded_algebras_with_basis.py | 12 ++ 3 files changed, 189 insertions(+), 11 deletions(-) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 40170457e55..8eebd21df6e 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -421,6 +421,17 @@ class CliffordAlgebra(CombinatorialFreeModule): canonical isomorphism. The inclusion `i` is commonly used to identify `V` with a vector subspace of `Cl(V)`. + The Clifford algebra `Cl(V, Q)` is a `\ZZ_2`-graded algebra + (where `\ZZ_2 = \ZZ / 2 \ZZ`); this grading is determined by + placing all elements of `V` in degree `1`. It is also an + `\NN`-filtered algebra, with the filtration too being defined + by placing all elements of `V` in degree `1`. Due to current + limitations of the category framework, Sage can consider + either the grading or the filtration but not both at the same + time (though one can introduce two equal Clifford algebras, + one filtered and the other graded); the ``graded`` parameter + determines which of them is to be used. + The Clifford algebra also can be considered as a covariant functor from the category of vector spaces equipped with quadratic forms to the category of algebras. In fact, if `(V, Q)` and `(W, R)` @@ -481,11 +492,6 @@ class CliffordAlgebra(CombinatorialFreeModule): a*d sage: d*c*b*a + a + 4*b*c a*b*c*d + 4*b*c + a - - .. WARNING:: - - The Clifford algebra is not graded, but instead filtered. This - will be changed once :trac:`17096` is finished. """ @staticmethod def __classcall_private__(cls, Q, names=None, graded=True): @@ -740,6 +746,38 @@ def _element_constructor_(self, x): sage: Cl3 = CliffordAlgebra(Q3, names='xyz') # different syntax for a change sage: Cl3( M((1,-3,2)) ) x + 2*z + + Conversions work between the filtered and the graded Clifford + algebra for the same form: + + sage: Q = QuadraticForm(ZZ, 3, [1,2,4,3,5,6]) + sage: Cl = CliffordAlgebra(Q); Cl + The graded Clifford algebra of the Quadratic form in 3 variables + over Integer Ring with coefficients: + [ 1 2 4 ] + [ * 3 5 ] + [ * * 6 ] + sage: Cl2 = CliffordAlgebra(Q, graded=False); Cl2 + The filtered Clifford algebra of the Quadratic form in 3 variables + over Integer Ring with coefficients: + [ 1 2 4 ] + [ * 3 5 ] + [ * * 6 ] + sage: Cl == Cl2 + False + sage: x,y,z = Cl.gens() + sage: a = (x+y)*(x+z) - 2*x + 3; a + -e0*e1 + e0*e2 + e1*e2 - 2*e0 + 6 + sage: Cl2(a) + -e0*e1 + e0*e2 + e1*e2 - 2*e0 + 6 + sage: Cl(Cl2(a)) == a + True + + .. TODO:: + + These conversions don't work. You might not want a coercion + from the graded Cl into the filtered Cl (although I don't + see why not), but a conversion should exist! """ # This is the natural lift morphism of the underlying free module if x in self.free_module(): @@ -910,6 +948,10 @@ def graded_algebra(self): sage: Cl. = CliffordAlgebra(Q, graded=False) sage: Cl.graded_algebra() The exterior algebra of rank 3 over Integer Ring + + .. TODO:: + + Doctest that the three methods do what they should. """ if self._graded: return self @@ -1104,6 +1146,9 @@ def lift_module_morphism(self, m, names=None): remove_zeros=True ) for i in x) cat = AlgebrasWithBasis(self.base_ring()).Filtered() + # .. TODO:: + # And if the Clifford is graded, we don't use .Graded()? + # Also, why self.base_ring() and not self.base_ring().category()? return Cl.module_morphism(on_basis=f, codomain=self, category=cat) def lift_isometry(self, m, names=None): @@ -1170,6 +1215,9 @@ def lift_isometry(self, m, names=None): for i in x) cat = AlgebrasWithBasis(self.base_ring()).Filtered() + # .. TODO:: + # And if the Clifford is graded, we don't use .Graded()? + # Also, why self.base_ring() and not self.base_ring().category()? return self.module_morphism(on_basis=f, codomain=Cl, category=cat) # This is a general method for finite dimensional algebras with bases @@ -1619,6 +1667,8 @@ def lift_morphism(self, phi, names=None): remove_zeros=True ) for i in x) return self.module_morphism(on_basis=f, codomain=E, category=GradedHopfAlgebrasWithBasis(R)) + # .. TODO:: + # not R.category()? def volume_form(self): """ diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 7756d5a3a52..7acfb205a1c 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -23,6 +23,14 @@ class FilteredAlgebrasWithBasis(FilteredModulesCategory): The category of filtered algebras with a distinguished homogeneous basis. + A filtered algebra with basis over a commutative ring `R` + is a filtered algebra over `R` endowed with the structure + of a filtered module with basis (with the same underlying + filtered-module structure). See + :class:`~sage.categories.filtered_algebras.FilteredAlgebras` and + :class:`~sage.categories.filtered_modules_with_basis.FilteredModulesWithBasis` + for these two notions. + EXAMPLES:: sage: C = AlgebrasWithBasis(ZZ).Filtered(); C @@ -38,12 +46,48 @@ class FilteredAlgebrasWithBasis(FilteredModulesCategory): """ class ParentMethods: def graded_algebra(self): - """ + r""" Return the associated graded algebra to ``self``. See :class:`~sage.algebras.associated_graded.AssociatedGradedAlgebra` for the definition and the properties of this. + If the filtered algebra ``self`` with basis is called `A`, + then this method returns `\operatorname{gr} A`. The method + :meth:`to_graded_conversion` returns the canonical + `R`-module isomorphism `A \to \operatorname{gr} A` induced + by the basis of `A`, and the method + :meth:`from_graded_conversion` returns the inverse of this + isomorphism. The method :meth:`projection` projects + elements of `A` onto `\operatorname{gr} A` according to + their place in the filtration on `A`. + + .. WARNING:: + + When not overridden, this method returns the default + implementation of an associated graded algebra -- + namely, ``AssociatedGradedAlgebra(self)``, where + ``AssociatedGradedAlgebra`` is + :class:`~sage.algebras.associated_graded.AssociatedGradedAlgebra`. + But many instances of :class:`FilteredAlgebrasWithBasis` + override this method, as the associated graded algebra + often is (isomorphic) to a simpler object (for instance, + the associated graded algebra of a graded algebra can be + identified with the graded algebra itself). Generic code + that uses associated graded algebras (such as the code + of the :meth:`induced_graded_map` method below) should + make sure to only communicate with them via the + :meth:`to_graded_conversion`, + :meth:`from_graded_conversion` and + :meth:`from_graded_conversion` methods. Similarly, when + overriding :meth:`graded_algebra`, make sure to + accordingly redefine these three methods, unless their + definitions below still apply to your case (this will + happen whenever the basis of your :meth:`graded_algebra` + has the same indexing set as ``self``, and the partition + of this indexing set according to degree is the same as + for ``self``). + EXAMPLES:: sage: A = AlgebrasWithBasis(ZZ).Filtered().example() @@ -60,7 +104,8 @@ def graded_algebra(self): def to_graded_conversion(self): r""" Return the canonical `R`-module isomorphism - `A \to \operatorname{gr} A` induced by the basis of `A`. + `A \to \operatorname{gr} A` induced by the basis of `A` + (where `A = ` ``self``). This is an isomorphism of `R`-modules, not of algebras. See the class documentation :class:`AssociatedGradedAlgebra`. @@ -87,7 +132,8 @@ def from_graded_conversion(self): r""" Return the inverse of the canonical `R`-module isomorphism `A \to \operatorname{gr} A` induced by the basis of `A` - (that is a map `\operatorname{gr} A \to A`). + (where `A = ` ``self``). This inverse is an isomorphism + `\operatorname{gr} A \to A`. This is an isomorphism of `R`-modules, not of algebras. See the class documentation :class:`AssociatedGradedAlgebra`. @@ -115,7 +161,7 @@ def projection(self, i): r""" Return the `i`-th projection `p_i : F_i \to G_i` (in the notations of the class documentation - :class:`AssociatedGradedAlgebra`). + :class:`AssociatedGradedAlgebra`, where `A = ` ``self``). This method actually does not return the map `p_i` itself, but an extension of `p_i` to the whole `R`-module `A`. @@ -124,7 +170,8 @@ def projection(self, i): projection of the graded `R`-module `\operatorname{gr} A` onto its `i`-th graded component `G_i`. The codomain of this map is `\operatorname{gr} A`, although its actual - image is `G_i`. + image is `G_i`. The map `p_i` is obtained from this map + by restricting its domain to `F_i` and its image to `G_i`. EXAMPLES:: @@ -141,10 +188,79 @@ def projection(self, i): base_zero = self.base_ring().zero() base_one = self.base_ring().one() grA = self.graded_algebra() - proj = lambda x: (base_one if grA.degree_on_basis(x) == i + proj = lambda x: (base_one if self.degree_on_basis(x) == i else base_zero) return self.module_morphism(diagonal=proj, codomain=grA) + def induced_graded_map(self, other, f): + r""" + Return the graded linear map between the associated graded + algebras of ``self`` and ``other`` canonically induced by + the filtration-preserving map ``f : self -> other``. + + Let `A` and `B` be two filtered algebras with basis, and let + `(F_i)_{i \in I}` and `(G_i)_{i \in I}` be their + filtrations. Let `f : A \to B` be a linear map which + preserves the filtration (i.e., satisfies `f(F_i) \subseteq + G_i` for all `i \in I`). Then, there is a canonically + defined graded linear map + `\operatorname{gr} f : \operatorname{gr} A \to + \operatorname{gr} B` which satisfies + + .. MATH:: + + (\operatorname{gr} f) (p_i(a)) = p_i(f(a)) + \qquad \text{for all } i \in I \text{ and } a \in F_i , + + where the `p_i` on the left hand side is the canonical + projection from `F_i` onto the `i`-th graded component + of `\operatorname{gr} A`, while the `p_i` on the right + hand side is the canonical projection from `G_i` onto + the `i`-th graded component of `\operatorname{gr} B`. + + INPUT: + + - ``other`` -- a filtered algebra with basis + + - ``f`` -- a filtration-preserving linear map from ``self`` + to ``other`` (can be given as a morphism or as a function) + + OUTPUT: + + The graded linear map `\operatorname{gr} f`. + + EXAMPLES: + + .. TODO:: + + doctests. Currently, Clifford algebras seem the most + appropriate. But need also trivial test with graded + algebra. + """ + grA = self.graded_algebra() + grB = other.graded_algebra() + from sage.categories.graded_modules_with_basis import GradedModulesWithBasis + cat = GradedModulesWithBasis(self.base_ring()) + from_gr = self.from_graded_conversion() + def on_basis(m): + i = grA.degree_on_basis(m) + return grB.projection(i)(f(from_gr(grA.monomial(m)))) + return grA.module_morphism(on_basis=on_basis, + codomain=grB, category=cat) + # If we could assume that the projection of the basis + # element of ``self`` indexed by an index ``m`` is the + # basis element of ``grA`` indexed by ``m``, then this + # could go faster: + # + # def on_basis(m): + # i = grA.degree_on_basis(m) + # return grB.projection(i)(f(self.monomial(m))) + # return grA.module_morphism(on_basis=on_basis, + # codomain=grB, category=cat) + # + # But this assumption might come back to bite us in the + # ass one day. What do you think? + class ElementMethods: pass diff --git a/src/sage/categories/graded_algebras_with_basis.py b/src/sage/categories/graded_algebras_with_basis.py index d0ff3160643..b43ba1416c4 100644 --- a/src/sage/categories/graded_algebras_with_basis.py +++ b/src/sage/categories/graded_algebras_with_basis.py @@ -35,11 +35,23 @@ def graded_algebra(self): """ Return the associated graded algebra to ``self``. + This is ``self``, because ``self`` is already graded. + See :meth:`~sage.categories.filtered_algebras_with_basis.FilteredAlgebrasWithBasis.graded_algebra` + for the general behavior of this method, and see + :class:`~sage.algebras.associated_graded.AssociatedGradedAlgebra` + for the definition and properties of associated graded + algebras. + EXAMPLES:: sage: m = SymmetricFunctions(QQ).m() sage: m.graded_algebra() is m True + + .. TODO:: + + Add examples showing that the three methods are + overridden correctly. """ return self From 2a62c3b093a2a991422f8c4f261d1a9d5ac6e059 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Sun, 23 Nov 2014 20:10:20 -0800 Subject: [PATCH 027/421] lift_* methods in algebras/clifford_algebra.py should remember the graded-filtered choice --- src/sage/algebras/clifford_algebra.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 8eebd21df6e..e96f98ec7a0 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -1139,7 +1139,7 @@ def lift_module_morphism(self, m, names=None): if Q == self._quadratic_form and names is None: Cl = self else: - Cl = CliffordAlgebra(Q, names) + Cl = CliffordAlgebra(Q, names, graded=self._graded) n = self._quadratic_form.dim() f = lambda x: self.prod(self._from_dict( {(j,): m[j,i] for j in range(n)}, @@ -1207,7 +1207,7 @@ def lift_isometry(self, m, names=None): else: if names is None: names = 'e' - Cl = CliffordAlgebra(Q, names) + Cl = CliffordAlgebra(Q, names, graded=self._graded) n = Q.dim() f = lambda x: Cl.prod(Cl._from_dict( {(j,): m[j,i] for j in range(n)}, From 15cf0dce22ece06760fdd270257f98757625f0a9 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Sun, 23 Nov 2014 20:25:26 -0800 Subject: [PATCH 028/421] fix and a first doctest for induced_graded_map --- .../filtered_algebras_with_basis.py | 63 ++++++++++++++++++- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 7acfb205a1c..68455fd1054 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -231,11 +231,67 @@ def induced_graded_map(self, other, f): EXAMPLES: + Let us compute `\operatorname{gr} f` for a map `f` between + two Clifford algebras:: + + sage: Q = QuadraticForm(ZZ, 2, [1,2,3]) + sage: B = CliffordAlgebra(Q, names=['u','v'], graded=False); B + The filtered Clifford algebra of the Quadratic form in 2 + variables over Integer Ring with coefficients: + [ 1 2 ] + [ * 3 ] + sage: m = Matrix(ZZ, [[1, 2], [1, -1]]) + sage: f = B.lift_module_morphism(m, names=['x','y']) + sage: A = f.domain(); A + The filtered Clifford algebra of the Quadratic form in 2 + variables over Integer Ring with coefficients: + [ 6 0 ] + [ * 3 ] + sage: x, y = A.gens() + sage: f(x) + u + v + sage: f(y) + 2*u - v + sage: f(x**2) + 6 + sage: f(x*y) + -3*u*v + 3 + sage: grA = A.graded_algebra(); grA + The exterior algebra of rank 2 over Integer Ring + sage: A.to_graded_conversion()(x) + x + sage: A.to_graded_conversion()(y) + y + sage: A.to_graded_conversion()(x*y) + x^y + sage: u = A.to_graded_conversion()(x*y+1); u + x^y + 1 + sage: A.from_graded_conversion()(u) + x*y + 1 + sage: A.projection(2)(x*y+1) + x^y + sage: A.projection(1)(x+2*y-2) + x + 2*y + sage: grf = A.induced_graded_map(B, f); grf + Generic morphism: + From: The exterior algebra of rank 2 over Integer Ring + To: The exterior algebra of rank 2 over Integer Ring + sage: grf(A.to_graded_conversion()(x)) + u + v + sage: grf(A.to_graded_conversion()(y)) + 2*u - v + sage: grf(A.to_graded_conversion()(x**2)) + 6 + sage: grf(A.to_graded_conversion()(x*y)) + -3*u^v + sage: grf(grA.one()) + 1 + .. TODO:: - doctests. Currently, Clifford algebras seem the most + more doctests. Currently, Clifford algebras seem the most appropriate. But need also trivial test with graded - algebra. + algebra. (Maybe not the Clifford one, though.) """ grA = self.graded_algebra() grB = other.graded_algebra() @@ -244,7 +300,8 @@ def induced_graded_map(self, other, f): from_gr = self.from_graded_conversion() def on_basis(m): i = grA.degree_on_basis(m) - return grB.projection(i)(f(from_gr(grA.monomial(m)))) + lifted_img_of_m = f(from_gr(grA.monomial(m))) + return other.projection(i)(lifted_img_of_m) return grA.module_morphism(on_basis=on_basis, codomain=grB, category=cat) # If we could assume that the projection of the basis From 50299560bf500b649aa5bdc3e7c7690fa09e2fb1 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Sun, 23 Nov 2014 20:28:47 -0800 Subject: [PATCH 029/421] another pitfall documented --- src/sage/categories/filtered_algebras_with_basis.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 68455fd1054..0759a7e8fd0 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -79,7 +79,10 @@ def graded_algebra(self): make sure to only communicate with them via the :meth:`to_graded_conversion`, :meth:`from_graded_conversion` and - :meth:`from_graded_conversion` methods. Similarly, when + :meth:`from_graded_conversion` methods (in particular, + do not expect there to be a conversion from ``self`` + to ``self.graded_algebra()``; this currently does not + work for Clifford algebras). Similarly, when overriding :meth:`graded_algebra`, make sure to accordingly redefine these three methods, unless their definitions below still apply to your case (this will @@ -88,6 +91,13 @@ def graded_algebra(self): of this indexing set according to degree is the same as for ``self``). + .. TODO:: + + Maybe the thing about the conversion from ``self`` + to ``self.graded_algebra()`` at least could be made to + work? (I would still warn the user against ASSUMING + that it must work.) + EXAMPLES:: sage: A = AlgebrasWithBasis(ZZ).Filtered().example() From 292ef5f2a26bff5302a5d97ebf22d34240c78f72 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Sun, 23 Nov 2014 21:50:18 -0800 Subject: [PATCH 030/421] another doctest --- .../filtered_algebras_with_basis.py | 80 +++++++++++++++++-- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 0759a7e8fd0..a4c753ff99e 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -94,9 +94,12 @@ def graded_algebra(self): .. TODO:: Maybe the thing about the conversion from ``self`` - to ``self.graded_algebra()`` at least could be made to - work? (I would still warn the user against ASSUMING - that it must work.) + to ``self.graded_algebra()`` on the Clifford at least + could be made to work? (I would still warn the user + against ASSUMING that it must work -- as there is + probably no way to guarantee it in all cases, and + we shouldn't require users to mess with + element constructors.) EXAMPLES:: @@ -241,8 +244,75 @@ def induced_graded_map(self, other, f): EXAMPLES: - Let us compute `\operatorname{gr} f` for a map `f` between - two Clifford algebras:: + We start with the universal enveloping algebra of the + Lie algebra `\RR^3` (with the cross product serving as + Lie bracket):: + + sage: A = AlgebrasWithBasis(QQ).Filtered().example(); A + An example of a filtered algebra with basis: the + universal enveloping algebra of Lie algebra of RR^3 + with cross product over Rational Field + sage: M = A.indices(); M + Free abelian monoid indexed by {'x', 'y', 'z'} + sage: x,y,z = [A.basis()[M.gens()[i]] for i in "xyz"] + + Let us define a stupid filtered map from ``A`` to + itself:: + + sage: def map_on_basis(m): + ....: d = m.dict() + ....: i = d.get('x', 0); j = d.get('y', 0); k = d.get('z', 0) + ....: g = (y ** (i+j)) * (z ** k) + ....: if i > 0: + ....: g += i * (x ** (i-1)) * (y ** j) * (z ** k) + ....: return g + sage: f = A.module_morphism(on_basis=map_on_basis, + ....: codomain=A) + sage: f(x) + U['y'] + 1 + sage: f(x*y*z) + U['y']^2*U['z'] + U['y']*U['z'] + sage: f(x*x*y*z) + U['y']^3*U['z'] + 2*U['x']*U['y']*U['z'] + sage: f(A.one()) + 1 + sage: f(y*z) + U['y']*U['z'] + + (There is nothing here that is peculiar to this + universal enveloping algebra; we are only using its + module structure, and we could just as well be using + a polynomial algebra in its stead.) + + We now compute `\operatorname{gr} f` :: + + sage: grA = A.graded_algebra(); grA + Graded Algebra of An example of a filtered algebra with + basis: the universal enveloping algebra of Lie algebra + of RR^3 with cross product over Rational Field + sage: xx, yy, zz = [A.to_graded_conversion()(i) for i in [x, y, z]] + sage: xx+yy*zz + bar(U['y']*U['z']) + bar(U['x']) + sage: grf = A.induced_graded_map(A, f); grf + Generic endomorphism of Graded Algebra of An example + of a filtered algebra with basis: the universal + enveloping algebra of Lie algebra of RR^3 with cross + product over Rational Field + sage: grf(xx) + bar(U['y']) + sage: grf(xx*yy*zz) + bar(U['y']^2*U['z']) + sage: grf(xx*xx*yy*zz) + bar(U['y']^3*U['z']) + sage: grf(grA.one()) + bar(1) + sage: grf(yy*zz) + bar(U['y']*U['z']) + sage: grf(yy*zz-2*yy) + bar(U['y']*U['z']) - 2*bar(U['y']) + + For another example, let us compute `\operatorname{gr} f` for a + map `f` between two Clifford algebras:: sage: Q = QuadraticForm(ZZ, 2, [1,2,3]) sage: B = CliffordAlgebra(Q, names=['u','v'], graded=False); B From c2e84e1367cbfc47eb7104de654e2ee55caea95d Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Mon, 24 Nov 2014 08:04:09 -0800 Subject: [PATCH 031/421] remaining doctests for induced_graded_map --- .../filtered_algebras_with_basis.py | 150 +++++++++++++++++- 1 file changed, 144 insertions(+), 6 deletions(-) diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index a4c753ff99e..a33d941172a 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -244,6 +244,8 @@ def induced_graded_map(self, other, f): EXAMPLES: + **Example 1.** + We start with the universal enveloping algebra of the Lie algebra `\RR^3` (with the cross product serving as Lie bracket):: @@ -311,6 +313,148 @@ def induced_graded_map(self, other, f): sage: grf(yy*zz-2*yy) bar(U['y']*U['z']) - 2*bar(U['y']) + **Example 2.** + + We shall now construct `\operatorname{gr} f` for a + different map `f` out of the same ``A``; the new map + `f` will lead into a graded algebra already, namely into + the algebra of symmetric functions:: + + sage: h = SymmetricFunctions(QQ).h() + sage: def map_on_basis(m): # redefining map_on_basis + ....: d = m.dict() + ....: i = d.get('x', 0); j = d.get('y', 0); k = d.get('z', 0) + ....: g = (h[1] ** i) * (h[2] ** (floor(j/2))) * (h[3] ** (floor(k/3))) + ....: g += i * (h[1] ** (i+j+k)) + ....: return g + sage: f = A.module_morphism(on_basis=map_on_basis, + ....: codomain=h) # redefining f + sage: f(x) + 2*h[1] + sage: f(y) + h[] + sage: f(z) + h[] + sage: f(y**2) + h[2] + sage: f(x**2) + 3*h[1, 1] + sage: f(x*y*z) + h[1] + h[1, 1, 1] + sage: f(x*x*y*y*z) + 2*h[1, 1, 1, 1, 1] + h[2, 1, 1] + sage: f(A.one()) + h[] + + The algebra ``h`` of symmetric functions in the `h`-basis + is already graded, so its associated graded algebra is + implemented as itself:: + + sage: grh = h.graded_algebra(); grh is h + True + sage: grf = A.induced_graded_map(h, f); grf + Generic morphism: + From: Graded Algebra of An example of a filtered + algebra with basis: the universal enveloping + algebra of Lie algebra of RR^3 with cross + product over Rational Field + To: Symmetric Functions over Rational Field + in the homogeneous basis + sage: grf(xx) + 2*h[1] + sage: grf(yy) + 0 + sage: grf(zz) + 0 + sage: grf(yy**2) + h[2] + sage: grf(xx**2) + 3*h[1, 1] + sage: grf(xx*yy*zz) + h[1, 1, 1] + sage: grf(xx*xx*yy*yy*zz) + 2*h[1, 1, 1, 1, 1] + sage: grf(grA.one()) + h[] + + **Example 3.** + + After having had a graded algebra as the codomain, let us try to + have one as the domain instead. Our new ``f`` will go from ``h`` + to ``A``:: + + sage: def map_on_basis(lam): # redefining map_on_basis + ....: return x ** (sum(lam)) + y ** (len(lam)) + sage: f = h.module_morphism(on_basis=map_on_basis, + ....: codomain=A) # redefining f + sage: f(h[1]) + U['x'] + U['y'] + sage: f(h[2]) + U['x']^2 + U['y'] + sage: f(h[1, 1]) + U['x']^2 + U['y']^2 + sage: f(h[2, 2]) + U['x']^4 + U['y']^2 + sage: f(h[3, 2, 1]) + U['x']^6 + U['y']^3 + sage: f(h.one()) + 2 + sage: grf = h.induced_graded_map(A, f); grf + Generic morphism: + From: Symmetric Functions over Rational Field + in the homogeneous basis + To: Graded Algebra of An example of a filtered + algebra with basis: the universal enveloping + algebra of Lie algebra of RR^3 with cross + product over Rational Field + sage: grf(h[1]) + bar(U['x']) + bar(U['y']) + sage: grf(h[2]) + bar(U['x']^2) + sage: grf(h[1, 1]) + bar(U['x']^2) + bar(U['y']^2) + sage: grf(h[2, 2]) + bar(U['x']^4) + sage: grf(h[3, 2, 1]) + bar(U['x']^6) + sage: grf(h.one()) + 2*bar(1) + + **Example 4.** + + The construct `\operatorname{gr} f` also makes sense when `f` + is a filtration-preserving map between graded algebras. + + sage: def map_on_basis(lam): # redefining map_on_basis + ....: return h[lam] + h[len(lam)] + sage: f = h.module_morphism(on_basis=map_on_basis, + ....: codomain=h) # redefining f + sage: f(h[1]) + 2*h[1] + sage: f(h[2]) + h[1] + h[2] + sage: f(h[1, 1]) + h[1, 1] + h[2] + sage: f(h[2, 1]) + h[2] + h[2, 1] + sage: f(h.one()) + 2*h[] + sage: grf = h.induced_graded_map(h, f); grf + Generic endomorphism of Symmetric Functions over Rational + Field in the homogeneous basis + sage: grf(h[1]) + 2*h[1] + sage: grf(h[2]) + h[2] + sage: grf(h[1, 1]) + h[1, 1] + h[2] + sage: grf(h[2, 1]) + h[2, 1] + sage: grf(h.one()) + 2*h[] + + **Example 5.** + For another example, let us compute `\operatorname{gr} f` for a map `f` between two Clifford algebras:: @@ -366,12 +510,6 @@ def induced_graded_map(self, other, f): -3*u^v sage: grf(grA.one()) 1 - - .. TODO:: - - more doctests. Currently, Clifford algebras seem the most - appropriate. But need also trivial test with graded - algebra. (Maybe not the Clifford one, though.) """ grA = self.graded_algebra() grB = other.graded_algebra() From 03bd4cfb53e24b24c65aada21f08850b40fb5c5c Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Mon, 24 Nov 2014 08:13:53 -0800 Subject: [PATCH 032/421] possibly controversial: graded_algebra and the three methods forming its interface are now cached_methods --- src/sage/categories/filtered_algebras_with_basis.py | 5 +++++ src/sage/categories/graded_algebras_with_basis.py | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index a33d941172a..4930de1a6d6 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -17,6 +17,7 @@ #****************************************************************************** from sage.categories.filtered_modules import FilteredModulesCategory +from sage.misc.cachefunc import cached_method class FilteredAlgebrasWithBasis(FilteredModulesCategory): """ @@ -45,6 +46,7 @@ class FilteredAlgebrasWithBasis(FilteredModulesCategory): sage: TestSuite(C).run() """ class ParentMethods: + @cached_method def graded_algebra(self): r""" Return the associated graded algebra to ``self``. @@ -114,6 +116,7 @@ def graded_algebra(self): # Maps + @cached_method def to_graded_conversion(self): r""" Return the canonical `R`-module isomorphism @@ -141,6 +144,7 @@ def to_graded_conversion(self): return self.module_morphism(diagonal=lambda x: base_one, codomain=self.graded_algebra()) + @cached_method def from_graded_conversion(self): r""" Return the inverse of the canonical `R`-module isomorphism @@ -170,6 +174,7 @@ def from_graded_conversion(self): return self.graded_algebra().module_morphism(diagonal=lambda x: base_one, codomain=self) + @cached_method def projection(self, i): r""" Return the `i`-th projection `p_i : F_i \to G_i` (in the diff --git a/src/sage/categories/graded_algebras_with_basis.py b/src/sage/categories/graded_algebras_with_basis.py index b43ba1416c4..c55c3671261 100644 --- a/src/sage/categories/graded_algebras_with_basis.py +++ b/src/sage/categories/graded_algebras_with_basis.py @@ -10,6 +10,7 @@ #****************************************************************************** from sage.categories.graded_modules import GradedModulesCategory +from sage.misc.cachefunc import cached_method class GradedAlgebrasWithBasis(GradedModulesCategory): """ @@ -31,6 +32,7 @@ class GradedAlgebrasWithBasis(GradedModulesCategory): class ParentMethods: # This needs to be copied in GradedAlgebras because we need to have # FilteredAlgebrasWithBasis as an extra super category + @cached_method def graded_algebra(self): """ Return the associated graded algebra to ``self``. @@ -55,6 +57,14 @@ def graded_algebra(self): """ return self + # .. TODO:: + # Possibly override ``to_graded_conversion`` and + # ``from_graded_conversion`` with identity morphisms? + # I have to admit I don't know the right way to construct + # identity morphisms other than using the identity matrix. + # Also, ``projection`` could be overridden by, well, a + # projection. + class ElementMethods: pass From 8a747c073e706950d1b14e77458539e126ff0550 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Mon, 24 Nov 2014 08:37:21 -0800 Subject: [PATCH 033/421] further edits --- .../filtered_algebras_with_basis.py | 4 +-- src/sage/categories/graded_algebras.py | 3 +++ .../categories/graded_algebras_with_basis.py | 25 ++++++++++++++++--- src/sage/categories/graded_modules.py | 15 +++++------ 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 4930de1a6d6..22832812521 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -81,7 +81,7 @@ def graded_algebra(self): make sure to only communicate with them via the :meth:`to_graded_conversion`, :meth:`from_graded_conversion` and - :meth:`from_graded_conversion` methods (in particular, + :meth:`projection` methods (in particular, do not expect there to be a conversion from ``self`` to ``self.graded_algebra()``; this currently does not work for Clifford algebras). Similarly, when @@ -428,7 +428,7 @@ def induced_graded_map(self, other, f): **Example 4.** The construct `\operatorname{gr} f` also makes sense when `f` - is a filtration-preserving map between graded algebras. + is a filtration-preserving map between graded algebras. :: sage: def map_on_basis(lam): # redefining map_on_basis ....: return h[lam] + h[len(lam)] diff --git a/src/sage/categories/graded_algebras.py b/src/sage/categories/graded_algebras.py index 48f47f4068b..d03f648d499 100644 --- a/src/sage/categories/graded_algebras.py +++ b/src/sage/categories/graded_algebras.py @@ -32,6 +32,9 @@ def graded_algebra(self): """ Return the associated graded algebra to ``self``. + Since ``self`` is already graded, this just returns + ``self``. + EXAMPLES:: sage: m = SymmetricFunctions(QQ).m() diff --git a/src/sage/categories/graded_algebras_with_basis.py b/src/sage/categories/graded_algebras_with_basis.py index c55c3671261..f6b8dce4513 100644 --- a/src/sage/categories/graded_algebras_with_basis.py +++ b/src/sage/categories/graded_algebras_with_basis.py @@ -50,10 +50,29 @@ def graded_algebra(self): sage: m.graded_algebra() is m True - .. TODO:: + TESTS: - Add examples showing that the three methods are - overridden correctly. + Let us check that the three methods + :meth:`to_graded_conversion`, :meth:`from_graded_conversion` + and :meth:`projection` (which form the interface of the + associated graded algebra) work correctly here:: + + sage: to_gr = m.to_graded_conversion() + sage: from_gr = m.from_graded_conversion() + sage: m[2] == to_gr(m[2]) == from_gr(m[2]) + True + sage: u = 3*m[1] - (1/2)*m[3] + sage: u == to_gr(u) == from_gr(u) + True + sage: m.zero() == to_gr(m.zero()) == from_gr(m.zero()) + True + sage: p2 = m.projection(2) + sage: p2(m[2] - 4*m[1,1] + 3*m[1] - 2*m[[]]) + -4*m[1, 1] + m[2] + sage: p2(4*m[1]) + 0 + sage: p2(m.zero()) == m.zero() + True """ return self diff --git a/src/sage/categories/graded_modules.py b/src/sage/categories/graded_modules.py index 5c34438260b..7a843eb3dc5 100644 --- a/src/sage/categories/graded_modules.py +++ b/src/sage/categories/graded_modules.py @@ -124,22 +124,23 @@ def _repr_object_names(self): @classmethod def default_super_categories(cls, category, *args): r""" - Return the default super categories of ``category.Graded()`` + Return the default super categories of ``category.Graded()``. - Mathematical meaning: every graded category is a filtered category - with the (implicit) filtration of `F_i = \bigoplus_{j \leq i} G_j`. + Mathematical meaning: every graded object (module, algebra, + etc.) is a filtered object with the (implicit) filtration + defined by `F_i = \bigoplus_{j \leq i} G_j`. INPUT: - - ``cls`` -- the class ``QuotientsCategory`` - - ``category`` -- a category `Cat` + - ``cls`` -- the class ``GradedModulesCategory`` + - ``category`` -- a category OUTPUT: a (join) category In practice, this returns ``category.Filtered()``, joined together with the result of the method :meth:`RegressiveCovariantConstructionCategory.default_super_categories() ` - (that is the join of ``category`` and ``cat.Filtered()`` for + (that is the join of ``category.Filtered()`` and ``cat`` for each ``cat`` in the super categories of ``category``). EXAMPLES: @@ -167,7 +168,7 @@ class GradedModules(GradedModulesCategory): The category of graded modules. We consider every graded module `M = \bigoplus_i M_i` as a - filtered module under the (natural) filtration of + filtered module under the (natural) filtration given by .. MATH:: From 7525680af6c574932b8a1323e3fb544fa8ab3bb3 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 27 Nov 2014 12:36:40 -0800 Subject: [PATCH 034/421] Fixing things and added full coverage. --- src/sage/combinat/colored_permutations.py | 635 ++++++++++++++++++++-- 1 file changed, 591 insertions(+), 44 deletions(-) diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index 0a767ebe7b6..c2b9d6c8e84 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -3,46 +3,116 @@ """ from sage.categories.groups import Groups from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from sage.structure.element import Element +from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups +from sage.structure.element import MultiplicativeGroupElement from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.misc.cachefunc import cached_method +from sage.misc.misc_c import prod from sage.combinat.permutation import Permutations from sage.combinat.cartesian_product import CartesianProduct from sage.matrix.constructor import diagonal_matrix from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.number_field.number_field import CyclotomicField +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.all import ZZ -class ColoredPermutation(Element): +class ColoredPermutation(MultiplicativeGroupElement): """ A colored permutation. """ def __init__(self, parent, colors, perm): """ Initialize ``self``. + + TESTS:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: TestSuite(s1*s2*t).run() """ self._colors = tuple(colors) self._perm = perm - Element.__init__(self, parent=parent) + MultiplicativeGroupElement.__init__(self, parent=parent) def _repr_(self): """ Return a string representation of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: s1*s2*t + [[1, 0, 0], [3, 1, 2]] """ return repr([list(self._colors), self._perm]) + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: latex(s1*s2*t) + [3_{1}, 1_{0}, 2_{0}] + """ + ret = "[" + ret += ", ".join("{}_{{{}}}".format(x, self._colors[i]) + for i,x in enumerate(self._perm)) + return ret + "]" + def _mul_(self, other): """ Multiply ``self`` and ``other``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: s1*s2*s1 == s2*s1*s2 + True + """ + colors = tuple(self._colors[i] + other._colors[val-1] #-1 for indexing + for i,val in enumerate(self._perm)) + p = self._perm._left_to_right_multiply_on_right(other._perm) + return self.__class__(self.parent(), colors, p) + + def __invert__(self): + """ + Return the inverse of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: ~t + [[0, 0, 3], [1, 2, 3]] + sage: all(x * ~x == C.one() for x in C.gens()) + True """ - colors = tuple(self._colors[i] + other._colors[val-1] - for i,val in enumerate(~self._perm)) - return self.__class__(self.parent(), colors, self._perm * other._perm) + ip = ~self._perm + return self.__class__(self.parent(), + tuple([-self._colors[i-1] for i in ip]), #-1 for indexing + ip) def __eq__(self, other): """ Check equality. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: s1*s2*s1 == s2*s1*s2 + True + sage: t^4 == C.one() + True + sage: s1*s2 == s2*s1 + False """ if not isinstance(other, ColoredPermutation): return False @@ -53,12 +123,29 @@ def __eq__(self, other): def __ne__(self, other): """ Check inequality. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: s1*s2*s1 != s2*s1*s2 + False + sage: s1*s2 != s2*s1 + True """ return not self.__eq__(other) def __iter__(self): """ Iterate over ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: x = s1*s2*t + sage: list(x) + [(1, 3), (0, 1), (0, 2)] """ for i,p in enumerate(self._perm): yield (self._colors[i], p) @@ -66,28 +153,71 @@ def __iter__(self): def one_line_form(self): """ Return the one line form of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: x = s1*s2*t + sage: x + [[1, 0, 0], [3, 1, 2]] + sage: x.one_line_form() + [(1, 3), (0, 1), (0, 2)] """ return list(self) def colors(self): """ Return the colors of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: x = s1*s2*t + sage: x.colors() + [1, 0, 0] """ return list(self._colors) def permutation(self): """ Return the permutation of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: x = s1*s2*t + sage: x.permutation() + [3, 1, 2] """ return self._perm def to_matrix(self): """ Return a matrix of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: s1,s2,t = C.gens() + sage: x = s1*s2*t*s2; x.one_line_form() + [(1, 2), (0, 1), (0, 3)] + sage: M = x.to_matrix(); M + [ 0 1 0] + [zeta4 0 0] + [ 0 0 1] + + The matrix multiplication is in the *opposite* order:: + + sage: M == s2.to_matrix()*t.to_matrix()*s2.to_matrix()*s1.to_matrix() + True """ Cp = CyclotomicField(self.parent()._m) g = Cp.gen() - return diagonal_matrix(Cp, [g**i for i in self._colors]) * self._perm.to_matrix() + D = diagonal_matrix(Cp, [g**i for i in self._colors]) + return self._perm.to_matrix() * D class ColoredPermutations(Parent, UniqueRepresentation): r""" @@ -103,35 +233,73 @@ class ColoredPermutations(Parent, UniqueRepresentation): .. MATH:: ((s_1, \ldots s_n), \sigma) \cdot ((t_1, \ldots, t_n), \tau) - = ((s_1 t_{\sigma^{-1}(1)}, \ldots, s_n t_{\sigma^{-1}(n)}), - \sigma \tau). + = ((s_1 t_{\sigma(1)}, \ldots, s_n t_{\sigma(n)}), \tau \sigma). + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3); C + 4-colored permutations of size 3 + sage: s1,s2,t = C.gens() + sage: (s1, s2, t) + ([[0, 0, 0], [2, 1, 3]], [[0, 0, 0], [1, 3, 2]], [[0, 0, 1], [1, 2, 3]]) + sage: s1*s2 + [[0, 0, 0], [3, 1, 2]] + sage: s1*s2*s1 == s2*s1*s2 + True + sage: t^4 == C.one() + True + sage: s2*t*s2 + [[0, 1, 0], [1, 2, 3]] REFERENCES: - :wikipedia:`Generalized_symmetric_group` - :wikipedia:`Complex_reflection_group` """ - def __init__(self, m, n): + def __init__(self, m, n, category=None): """ Initialize ``self``. EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: TestSuite(C).run() + sage: C = ColoredPermutations(2, 3) + sage: TestSuite(C).run() + sage: C = ColoredPermutations(1, 3) + sage: TestSuite(C).run() """ + if m <= 0: + raise ValueError("m must be a positive integer") self._m = m self._n = n self._C = IntegerModRing(self._m) self._P = Permutations(self._n) - Parent.__init__(self, category=(Groups(), FiniteEnumeratedSets())) + if category is None: + category = (Groups(), FiniteEnumeratedSets()) + Parent.__init__(self, category=category) def _repr_(self): """ Return a string representation of ``self``. + + EXAMPLES:: + + sage: ColoredPermutations(4, 3) + 4-colored permutations of size 3 """ return "{}-colored permutations of size {}".format(self._m, self._n) + @cached_method def one(self): """ Return the identity element of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: C.one() + [[0, 0, 0], [1, 2, 3]] """ return self.element_class(self, [self._C.zero()]*self._n, self._P.identity()) @@ -139,6 +307,14 @@ def one(self): def gens(self): """ Return the generators of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: C.gens() + ([[0, 0, 0], [2, 1, 3]], + [[0, 0, 0], [1, 3, 2]], + [[0, 0, 1], [1, 2, 3]]) """ zero = [self._C.zero()]*self._n g = [] @@ -154,6 +330,16 @@ def gens(self): def matrix_group(self): """ Return the matrix group corresponding to ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: C.matrix_group() + Matrix group over Cyclotomic Field of order 4 and degree 2 with 3 generators ( + [0 1 0] [1 0 0] [ 1 0 0] + [1 0 0] [0 0 1] [ 0 1 0] + [0 0 1], [0 1 0], [ 0 0 zeta4] + ) """ from sage.groups.matrix_gps.finitely_generated import MatrixGroup return MatrixGroup([g.to_matrix() for g in self.gens()]) @@ -161,6 +347,14 @@ def matrix_group(self): def _element_constructor_(self, x): """ Construct an element of ``self`` from ``x``. + + TESTS:: + + sage: C = ColoredPermutations(4, 3) + sage: x = C([(2,1), (3,3), (3,2)]); x + [[2, 3, 3], [1, 3, 2]] + sage: x == C([[2,3,3], [1,3,2]]) + True """ if isinstance(x, list): if isinstance(x[0], tuple): @@ -180,6 +374,19 @@ def _element_constructor_(self, x): def __iter__(self): """ Iterate over ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(2, 2) + sage: [x for x in C] + [[[0, 0], [1, 2]], + [[0, 1], [1, 2]], + [[1, 0], [1, 2]], + [[1, 1], [1, 2]], + [[0, 0], [2, 1]], + [[0, 1], [2, 1]], + [[1, 0], [2, 1]], + [[1, 1], [2, 1]]] """ C = CartesianProduct(*[self._C]*self._n) for p in self._P: @@ -189,6 +396,14 @@ def __iter__(self): def cardinality(self): """ Return the cardinality of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: C.cardinality() + 384 + sage: C.cardinality() == 4**3 * factorial(3) + True """ return self._m**self._n * self._P.cardinality() @@ -198,6 +413,18 @@ def rank(self): The rank of a complex reflection group is equal to the dimension of the complex vector space the group acts on. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 12) + sage: C.rank() + 12 + sage: C = ColoredPermutations(7, 4) + sage: C.rank() + 4 + sage: C = ColoredPermutations(1, 4) + sage: C.rank() + 3 """ if self._m == 1: return self._n - 1 @@ -216,8 +443,8 @@ def degrees(self): EXAMPLES:: - sage: CP = ColoredPermutations(4, 3) - sage: CP.degrees() + sage: C = ColoredPermutations(4, 3) + sage: C.degrees() [4, 8, 12] sage: S = ColoredPermutations(1, 3) sage: S.degrees() @@ -226,7 +453,7 @@ def degrees(self): We now check that the product of the degrees is equal to the cardinality of ``self``:: - sage: prod(CP.degrees()) == CP.cardinality() + sage: prod(C.degrees()) == C.cardinality() True sage: prod(S.degrees()) == S.cardinality() True @@ -241,13 +468,37 @@ def codegrees(self): Let `G` be a complex reflection group. The codegrees `d_1^* \leq d_2^* \leq \cdots \leq d_{\ell}^*` of `G` can be - defined in terms of the fixed point polynomial: + defined by: + + \prod_{i=1}^{\ell} (q - d_i^* - 1) + = \sum_{g \in G} \det(g) q^{\dim(V^g)}, - f_G(q) = \prod_{i=1}^{\ell} (q - d_i^* - 1). + where `V` is the natural complex vector space that `G` acts on. If `m = 1`, then we are in the special case of the symmetric group and the codegrees are `(n-2, n-3, \ldots 1, 0)`. Otherwise the degrees are `((n-1)m, (n-2)m, \ldots, m, 0)`. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: C.codegrees() + [8, 4, 0] + sage: S = ColoredPermutations(1, 3) + sage: S.codegrees() + [1, 0] + + TESTS: + + We check the polynomial identity:: + + sage: R. = ZZ[] + sage: C = ColoredPermutations(3, 2) + sage: f = prod(q - ds - 1 for ds in C.codegrees()) + sage: d = lambda x: sum(1 for e in x.to_matrix().eigenvalues() if e == 1) + sage: g = sum(det(x.to_matrix()) * q**d(x) for x in C) + sage: f == g + True """ if self._m == 1: # Special case for the usual symmetric group return list(reversed(range(self._n-1))) @@ -259,10 +510,22 @@ def number_reflection_hyperplanes(self): The number of reflection hyperplanes of a complex reflection group is equal to the sume of the codegrees plus the rank. + + EXAMPLES:: + + sage: C = ColoredPermutations(1, 2) + sage: C.number_reflection_hyperplanes() + 1 + sage: C = ColoredPermutations(1, 3) + sage: C.number_reflection_hyperplanes() + 3 + sage: C = ColoredPermutations(4, 12) + sage: C.number_reflection_hyperplanes() + 276 """ return sum(self.codegrees()) + self.rank() - def fixed_point_polynomial(self, q): + def fixed_point_polynomial(self, q=None): r""" The fixed point polynomial of ``self``. @@ -279,7 +542,32 @@ def fixed_point_polynomial(self, q): .. MATH:: f_G(q) = \prod_{i=1}^{\ell} (q + d_i - 1). + + INPUT: + + - ``q`` -- (default: the generator of ``ZZ['q']``) the parameter `q` + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: C.fixed_point_polynomial() + q^3 + 21*q^2 + 131*q + 231 + + sage: S = ColoredPermutations(1, 3) + sage: S.fixed_point_polynomial() + q^2 + 3*q + 2 + + TESTS: + + We check the against the degrees and codegrees:: + + sage: R. = ZZ[] + sage: C = ColoredPermutations(4, 3) + sage: C.fixed_point_polynomial(q) == prod(q + d - 1 for d in C.degrees()) + True """ + if q is None: + q = PolynomialRing(ZZ, 'q').gen(0) return prod(q + d - 1 for d in self.degrees()) def is_well_generated(self): @@ -288,7 +576,19 @@ def is_well_generated(self): A complex reflection group `G` is well-generated if it is generated by `\ell` reflections. Equivalently, `G` is well-generated - if `d_i + d_i^* = d_{\ell}` for all `1 \leq i \leq \ell` + if `d_i + d_i^* = d_{\ell}` for all `1 \leq i \leq \ell`. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: C.is_well_generated() + True + sage: C = ColoredPermutations(2, 8) + sage: C.is_well_generated() + True + sage: C = ColoredPermutations(1, 4) + sage: C.is_well_generated() + True """ deg = self.degrees() dstar = self.codegrees() @@ -303,17 +603,78 @@ class SignedPermutation(ColoredPermutation): """ A signed permutation. """ + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: s1,s2,s3,s4 = S.gens() + sage: s4*s1*s2*s3*s4 + [-4, 1, 2, -3] + """ + return repr(list(self)) + + _latex_ = _repr_ + def _mul_(self, other): """ Multiply ``self`` and ``other``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: s1,s2,s3,s4 = S.gens() + sage: x = s4*s1*s2*s3*s4; x + [-4, 1, 2, -3] + sage: x * x + [3, -4, 1, -2] + + :: + + sage: s1*s2*s1 == s1*s2*s1 + True + sage: s3*s4*s3*s4 == s4*s3*s4*s3 + True + """ + colors = tuple(self._colors[i] * other._colors[val-1] #-1 for indexing + for i,val in enumerate(self._perm)) + p = self._perm._left_to_right_multiply_on_right(other._perm) + return self.__class__(self.parent(), colors, p) + + def inverse(self): + """ + Return the inverse of ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: s1,s2,s3,s4 = S.gens() + sage: x = s4*s1*s2*s3*s4 + sage: ~x + [2, 3, -4, -1] + sage: x * ~x == S.one() + True """ - colors = tuple(self._colors[i] * other._colors[val-1] - for i,val in enumerate(~self._perm)) - return self.__class__(self.parent(), colors, self._perm * other._perm) + ip = ~self._perm + return self.__class__(self.parent(), + tuple([self._colors[i-1] for i in ip]), #-1 for indexing + ip) + + __invert__ = inverse def __iter__(self): """ Iterate over ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: s1,s2,s3,s4 = S.gens() + sage: x = s4*s1*s2*s3*s4 + sage: [a for a in x] + [-4, 1, 2, -3] """ for i,p in enumerate(self._perm): yield self._colors[i] * p @@ -321,16 +682,52 @@ def __iter__(self): def to_matrix(self): """ Return a matrix of ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: s1,s2,s3,s4 = S.gens() + sage: x = s4*s1*s2*s3*s4 + sage: M = x.to_matrix(); M + [ 0 1 0 0] + [ 0 0 1 0] + [ 0 0 0 -1] + [-1 0 0 0] + + The matrix multiplication is in the *opposite* order:: + + sage: m1,m2,m3,m4 = [g.to_matrix() for g in S.gens()] + sage: M == m4 * m3 * m2 * m1 * m4 + True + """ + return self._perm.to_matrix() * diagonal_matrix(self._colors) + + def has_left_descent(self, i): """ - return identity_matrix(self._colors) * self._perm.to_matrix() + Return ``True`` if ``i`` is a left descent of ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: s1,s2,s3,s4 = S.gens() + sage: x = s4*s1*s2*s3*s4 + sage: [x.has_left_descent(i) for i in S.index_set()] + [True, False, False, True] + """ + n = self.parent()._n + if i == n: + return self._colors[-1] == -1 + if self._colors[i-1] == -1: + return self._colors[i] == 1 or self._perm[i-1] < self._perm[i] + return self._colors[i] == 1 and self._perm[i-1] > self._perm[i] class SignedPermutations(ColoredPermutations): r""" Group of signed permutations. The group of signed permutations is also known as the hyperoctahedral - group and the 2-colored permutation group. Thus it can be constructed - as the wreath product `S_2 \wr S_n`. + group, the Coxeter group of type `B_n`, and the 2-colored permutation + group. Thus it can be constructed as the wreath product `S_2 \wr S_n`. REFERENCES: @@ -339,46 +736,196 @@ class SignedPermutations(ColoredPermutations): def __init__(self, n): """ Initialize ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: TestSuite(S).run() """ - ColoredPermutations.__init__(self, 2, n) + ColoredPermutations.__init__(self, 2, n, FiniteCoxeterGroups()) def _repr_(self): """ Return a string representation of ``self``. + + EXAMPLES:: + + sage: SignedPermutations(4) + Signed permutations of 4 """ return "Signed permutations of {}".format(self._n) + @cached_method + def one(self): + """ + Return the identity element of ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: S.one() + [1, 2, 3, 4] + """ + return self.element_class(self, [ZZ.one()]*self._n, self._P.identity()) + + def simple_reflection(self, i): + r""" + Return the ``i``-th simple refection of ``self``. + + Return the generators of ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: S.simple_reflection(1) + [2, 1, 3, 4] + sage: S.simple_reflection(4) + [1, 2, 3, -4] + """ + if i not in self.index_set(): + raise ValueError("i must be in the index set") + if i < self._n: + p = range(1, self._n+1) + p[i-1] = i+1 + p[i] = i + return self.element_class(self, [ZZ.one()]*self._n, self._P(p)) + temp = [ZZ.one()]*self._n + temp[-1] = -ZZ.one() + return self.element_class(self, temp, self._P.identity()) + + @cached_method + def gens(self): + """ + Return the generators of ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: S.gens() + ([2, 1, 3, 4], [1, 3, 2, 4], [1, 2, 4, 3], [1, 2, 3, -4]) + """ + return tuple(self.simple_reflection(i) for i in self.index_set()) + + def _element_constructor_(self, x): + """ + Construct an element of ``self`` from ``x``. + + TESTS:: + + sage: S = SignedPermutations(3) + sage: x = S([(+1,1), (-1,3), (-1,2)]); x + [1, -3, -2] + sage: x == S([[+1,-1,-1], [1,3,2]]) + True + sage: x == S([1, -3, -2]) + True + """ + if isinstance(x, list): + if isinstance(x[0], tuple): + c = [] + p = [] + for k in x: + if len(k) != 2: + raise ValueError("input must be pairs (sign, element)") + if k[0] != 1 and k[0] != -1: + raise ValueError("the sign must be +1 or -1") + c.append( ZZ(k[0]) ) + p.append(k[1]) + return self.element_class(self, c, self._P(p)) + + if len(x) == self._n: + c = [] + p = [] + one = ZZ.one() + for v in x: + if v > 0: + c.append(one) + p.append(v) + else: + c.append(-one) + p.append(-v) + return self.element_class(self, c, self._P(p)) + + if len(x) != 2: + raise ValueError("input must be a pair of a list of signs and a permutation") + if any(s != 1 and s != -1 for s in x[0]): + raise ValueError("the sign must be +1 or -1") + return self.element_class(self, map(ZZ, x[0]), self._P(x[1])) + def __iter__(self): """ Iterate over ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(2) + sage: [x for x in S] + [[1, 2], [1, -2], [-1, 2], [-1, -2], + [2, 1], [2, -1], [-2, 1], [-2, -1]] """ - C = CartesianProduct(*[[-1,1]]*self._n) + one = ZZ.one() + C = CartesianProduct(*[[one,-one]]*self._n) for p in self._P: for c in C: yield self.element_class(self, c, p) - Element = SignedPermutation - -class EvenSignedPermutations(SignedPermutations): - """ - Group of even signed permutations. - """ - def _repr_(self): + @cached_method + def index_set(self): """ - Return a string representation of ``self``. + Return the index set of ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: S.index_set() + (1, 2, 3, 4) """ - return "Even signed permtuations of {}".format(self._n) + return tuple(range(1, self._n+1)) - def __iter__(self): + def long_element(self, index_set=None): """ - Iterate over ``self``. + Return the longest element of ``self``, or of the + parabolic subgroup corresponding to the given ``index_set``. + + INPUT: + + - ``index_set`` -- (optioal) a subset (as a list or iterable) + of the nodes of the indexing set + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: S.long_element() + [-4, -3, -2, -1] """ - for s in SignedPermutations.__iter__(self): - total = 0 - for pm in s._colors: - if pm == -1: - total += 1 + if index_set is not None: + return super(SignedPermutations, self).long_element() + p = range(self._n, 0, -1) + return self.element_class(self, [-ZZ.one()]*self._n, self._P(p)) + + Element = SignedPermutation - if total % 2 == 0: - yield s +# TODO: Make this a subgroup +#class EvenSignedPermutations(SignedPermutations): +# """ +# Group of even signed permutations. +# """ +# def _repr_(self): +# """ +# Return a string representation of ``self``. +# """ +# return "Even signed permtuations of {}".format(self._n) +# +# def __iter__(self): +# """ +# Iterate over ``self``. +# """ +# for s in SignedPermutations.__iter__(self): +# total = 0 +# for pm in s._colors: +# if pm == -1: +# total += 1 +# +# if total % 2 == 0: +# yield s From 184b4f9403b613e357db74d9074c3e95e8370ce3 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 27 Nov 2014 12:53:26 -0800 Subject: [PATCH 035/421] Added some more methods and some comments. --- src/sage/combinat/colored_permutations.py | 41 +++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index c2b9d6c8e84..0d3699b947d 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -18,6 +18,8 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.all import ZZ +# TODO: Much of the colored permutations (and element) class can be +# generalized to `G \wr S_n` class ColoredPermutation(MultiplicativeGroupElement): """ A colored permutation. @@ -219,6 +221,7 @@ def to_matrix(self): D = diagonal_matrix(Cp, [g**i for i in self._colors]) return self._perm.to_matrix() * D +# TODO: Parts of this should be put in the category of complex reflection groups class ColoredPermutations(Parent, UniqueRepresentation): r""" The group of `m`-colored permutations on `\{1, 2, \ldots, n\}`. @@ -729,6 +732,28 @@ class SignedPermutations(ColoredPermutations): group, the Coxeter group of type `B_n`, and the 2-colored permutation group. Thus it can be constructed as the wreath product `S_2 \wr S_n`. + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: s1,s2,s3,s4 = S.group_generators() + sage: x = s4*s1*s2*s3*s4; x + [-4, 1, 2, -3] + sage: x^4 == S.one() + True + + This is a finite Coxeter group of type `B_n`:: + + sage: S.canonical_representation() + Coxeter group over Universal Cyclotomic Field with Coxeter matrix: + [1 3 2 2] + [3 1 3 2] + [2 3 1 4] + [2 2 4 1] + sage: S.long_element() + [-4, -3, -2, -1] + sage: S.long_element().reduced_word() + [4, 3, 4, 2, 3, 4, 1, 2, 3, 4] + REFERENCES: - :wikipedia:`Hyperoctahedral_group` @@ -882,6 +907,22 @@ def index_set(self): """ return tuple(range(1, self._n+1)) + def coxeter_matrix(self): + """ + Return the Coxeter matrix of ``self``. + + EXAMPLES:: + + sage: S = SignedPermutations(4) + sage: S.coxeter_matrix() + [1 3 2 2] + [3 1 3 2] + [2 3 1 4] + [2 2 4 1] + """ + from sage.combinat.root_system.cartan_type import CartanType + return CartanType(['B', self._n]).coxeter_matrix() + def long_element(self, index_set=None): """ Return the longest element of ``self``, or of the From 924d341abe2f85b95031ee9c6169cea69cad39e4 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 13 Dec 2014 17:49:14 -0800 Subject: [PATCH 036/421] Added check for formality, k-generated, and matroid methods. --- .../hyperplane_arrangement/arrangement.py | 112 +++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index adddb797bb9..cd642dfb5f3 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -23,7 +23,9 @@ sage: -2*h Hyperplane -6*x - 4*y + 10*z + 14 sage: x, y, z - (Hyperplane x + 0*y + 0*z + 0, Hyperplane 0*x + y + 0*z + 0, Hyperplane 0*x + 0*y + z + 0) + (Hyperplane x + 0*y + 0*z + 0, + Hyperplane 0*x + y + 0*z + 0, + Hyperplane 0*x + 0*y + z + 0) See :mod:`sage.geometry.hyperplane_arrangement.hyperplane` for more functionality of the individual hyperplanes. @@ -340,9 +342,12 @@ from sage.misc.cachefunc import cached_method from sage.misc.misc import uniq from sage.matrix.constructor import matrix, vector +from sage.modules.free_module import VectorSpace +from sage.matroids.constructor import Matroid from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane +from copy import copy class HyperplaneArrangementElement(Element): @@ -1481,7 +1486,7 @@ def regions(self): return tuple(regions) def region_containing_point(self, p): - r""" + r"""def region The region in the hyperplane arrangement containing a given point. The base field must have characteristic zero. @@ -1928,7 +1933,110 @@ def varchenko_matrix(self, names='h'): v.set_immutable() return v + @cached_method + def matroid(self): + r""" + Return the matroid assoicated to ``self``. + + Let `A` denote a central hyperplane arrangement and `n_H` the + normal vector of some hyperplane `H \in A`. We define a matroid + `M_A` as the linear matoid spanned by `\{ n_H | H \in A \}`. + The matroid `M_A` is such that the lattice of flats of `M` is + isomorphic to the intersection lattice of `A` + (Proposition 3.6 in [RS]_). + + EXAMPLES:: + + sage: P. = HyperplaneArrangements(QQ) + sage: A = P(x, y, z, x+y+z, 2*x+y+z, 2*x+3*y+z, 2*x+3*y+4*z) + sage: M = A.matroid(); M + Linear matroid of rank 3 on 7 elements represented over the Rational Field + + We check the lattice of flats is isomorphic to the + intersection lattice:: + + sage: f = sum([list(M.flats(i)) for i in range(M.rank()+1)], []) + sage: PF = Poset([f, lambda x,y: x < y]) + sage: PF.is_isomorphic(A.intersection_poset()) + True + """ + if not self.is_central(): + raise ValueError("the hyperplane arrangement must be central") + norms = [p.normal() for p in self] + return Matroid(matrix=matrix(norms).transpose()) + + @cached_method + def minimal_generated_number(self): + r""" + Return the minimum `k` such that ``self`` is `k`-generated. + Let `A` be a central hyperplane arrangement. Let `W_k` denote + the solution space of the linear system corresponding to the + linear dependencies among the hyperplanes of `A` of length at + most `k`. We say `A` is `k`-*generated* if `\dim W_k = \rank A`. + Equivalently this says all dependencies forming the Orlik-Terao + ideal are generated by at most `k` hyperplanes. + + EXAMPLES: + + We construct Example 2.2 from [Vuz93]_:: + + sage: P. = HyperplaneArrangements(QQ) + sage: A = P(x, y, z, x+y+z, 2*x+y+z, 2*x+3*y+z, 2*x+3*y+4*z, 3*x+5*z, 3*x+4*y+5*z) + sage: B = P(x, y, z, x+y+z, 2*x+y+z, 2*x+3*y+z, 2*x+3*y+4*z, x+3*z, x+2*y+3*z) + sage: A.minimal_generated_number() + 3 + sage: B.minimal_generated_number() + 4 + + REFERENCES: + + .. [Vuz93] Sergey Yuzvinksy, + *The first two obstructions to the freeness of arrangements*, + Transactions of the American Mathematical Society, + Vol. 335, **1** (1993) pp. 231--244. + """ + V = VectorSpace(self.base_ring(), self.dimension()) + W = VectorSpace(self.base_ring(), self.n_hyperplanes()) + r = self.rank() + D = [] + M = self.matroid() + norms = M.representation().columns() + circuits = M.circuits() + for i in range(2, self.n_hyperplanes()): + sol = [] + for d in circuits: + if len(d) > i: + continue + d = list(d) + dep = V.linear_dependence([norms[j] for j in d]) + w = copy(W.zero()) + for j,k in enumerate(d): + w[k] = dep[0][j] + sol.append(w) + mat = matrix(sol) + if mat.right_kernel().dimension() == r: + return i + return self.n_hyperplanes() + + def is_formal(self): + """ + Return if ``self`` is formal. + + A hyperplane arrangement is *formal* if it is 3-generated as + defined in :meth:`minimal_generated_number`. + + EXAMPLES:: + + sage: P. = HyperplaneArrangements(QQ) + sage: A = P(x, y, z, x+y+z, 2*x+y+z, 2*x+3*y+z, 2*x+3*y+4*z, 3*x+5*z, 3*x+4*y+5*z) + sage: B = P(x, y, z, x+y+z, 2*x+y+z, 2*x+3*y+z, 2*x+3*y+4*z, x+3*z, x+2*y+3*z) + sage: A.is_formal() + True + sage: B.is_formal() + False + """ + return self.minimal_generated_number() <= 3 class HyperplaneArrangements(Parent, UniqueRepresentation): """ From 56d27553989dc42a9563628e545f08eb874ac15f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 13 Dec 2014 17:51:16 -0800 Subject: [PATCH 037/421] Some cleanup. --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index cd642dfb5f3..ccd728066c6 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -343,7 +343,6 @@ from sage.misc.misc import uniq from sage.matrix.constructor import matrix, vector from sage.modules.free_module import VectorSpace -from sage.matroids.constructor import Matroid from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane @@ -700,7 +699,6 @@ def intersection_poset(self): """ K = self.base_ring() from sage.geometry.hyperplane_arrangement.affine_subspace import AffineSubspace - from sage.modules.all import VectorSpace whole_space = AffineSubspace(0, VectorSpace(K, self.dimension())) L = [[whole_space]] active = True @@ -1963,6 +1961,7 @@ def matroid(self): if not self.is_central(): raise ValueError("the hyperplane arrangement must be central") norms = [p.normal() for p in self] + from sage.matroids.constructor import Matroid return Matroid(matrix=matrix(norms).transpose()) @cached_method @@ -1999,7 +1998,6 @@ def minimal_generated_number(self): V = VectorSpace(self.base_ring(), self.dimension()) W = VectorSpace(self.base_ring(), self.n_hyperplanes()) r = self.rank() - D = [] M = self.matroid() norms = M.representation().columns() circuits = M.circuits() From cd37cc6e312d7b028024c6fff4778f73c557636b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 2 Mar 2015 15:05:07 -0800 Subject: [PATCH 038/421] Fix a docstring. --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 4e628b6172f..5efda2a41c5 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -1484,7 +1484,7 @@ def regions(self): return tuple(regions) def region_containing_point(self, p): - r"""def region + r""" The region in the hyperplane arrangement containing a given point. The base field must have characteristic zero. From 67bdd8ce642998e6c9194065e91ff45b9b14e090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 7 Mar 2015 23:15:01 +0100 Subject: [PATCH 039/421] trac #16209 another algo, without precomputing the mutation class --- .../cluster_algebra_quiver/cluster_seed.py | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index ae908388440..ed542ace6d4 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -2040,7 +2040,7 @@ def greedy(self, a1, a2, method='by_recursion'): def oriented_exchange_graph(self): """ Return the oriented exchange graph of ``self`` as a directed - graph + graph. The seed must be a cluster seed for a cluster algebra of finite type with principal coefficients (the corresponding @@ -2080,23 +2080,24 @@ def oriented_exchange_graph(self): if not self._is_principal: raise TypeError('only works for principal coefficients') - mut_class = self.mutation_class() covers = [] - n = self.n() - pairs = [(i, j) for i in mut_class for j in mut_class if i != j] - for (i, j) in pairs: + stack = [self] + known_clusters = {} + while stack: + i = stack.pop() B = i.b_matrix() for k in range(n): - count = len([i2 for i2 in range(n, 2 * n) if B[i2][k] <= 0]) - green = (count == n) - NewS1 = i.mutate(k, inplace=False) - Var1 = [NewS1.cluster_variable(k) for k in range(n)] - Var1.sort() - Var2 = [j.cluster_variable(k) for k in range(n)] - Var2.sort() - if Var1 == Var2 and green and (i, j) not in covers: - covers.append((i, j)) + # check if green + if all(B[i2][k] >= 0 for i2 in range(n, 2 * n)): + j = i.mutate(k, inplace=False) + Varj = tuple(sorted(j.cluster())) + if Varj in known_clusters: + covers.append((i, known_clusters[Varj])) + else: + covers.append((i, j)) + known_clusters[Varj] = j + stack.append(j) return DiGraph(covers) From ba6d80f844a11b373c09198192272fed6a72de9d Mon Sep 17 00:00:00 2001 From: MariaMonks Date: Fri, 20 Mar 2015 22:06:01 -0400 Subject: [PATCH 040/421] skew rectification optimization! --- src/sage/combinat/skew_tableau.py | 36 +++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index e0cd3671649..9fc43cdccff 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -42,7 +42,7 @@ from sage.combinat.combinat import CombinatorialObject from sage.combinat.partition import Partition -from sage.combinat.tableau import TableauOptions +from sage.combinat.tableau import TableauOptions, Tableaux, SemistandardTableau, StandardTableau, Tableau from sage.combinat.skew_partition import SkewPartition, SkewPartitions from sage.combinat.integer_vector import IntegerVectors from sage.combinat.words.words import Words @@ -761,7 +761,7 @@ def slide(self, corner=None): return SkewTableau(new_st) - def rectify(self): + def rectify(self, force=None): """ Return a :class:`Tableau` formed by applying the jeu de taquin process to ``self``. See page 15 of [FW]_. @@ -772,6 +772,11 @@ def rectify(self): *Young Tableaux*, Cambridge University Press 1997. + INPUT: + - ``force`` -- optional: if set to ``'jdt'``, rectifies by jeu de taquin; + if set to ``'schensted'``, rectifies by Schensted insertion of the + reading word; otherwise, guesses which will be faster. + EXAMPLES:: sage: s = SkewTableau([[None,1],[2,3]]) @@ -779,20 +784,33 @@ def rectify(self): [[1, 3], [2]] sage: SkewTableau([[None, None, None, 4],[None,None,1,6],[None,None,5],[2,3]]).rectify() [[1, 3, 4, 6], [2, 5]] + sage: SkewTableau([[None, None, None, 4],[None,None,1,6],[None,None,5],[2,3]]).rectify('jdt') + [[1, 3, 4, 6], [2, 5]] + sage: SkewTableau([[None, None, None, 4],[None,None,1,6],[None,None,5],[2,3]]).rectify('schensted') + [[1, 3, 4, 6], [2, 5]] TESTS:: sage: s [[None, 1], [2, 3]] """ - rect = copy.deepcopy(self) - inner_corners = rect.inner_shape().corners() - - while len(inner_corners) > 0: - rect = rect.slide() + labda = self.outer_shape() + musize = self.inner_shape().size() + labdasize = labda.size() + if force == 'jdt' or (force != 'schensted' and musize < len(labda) * (labdasize - musize)**(1/2)): + rect = self inner_corners = rect.inner_shape().corners() - - return rect.to_tableau() + while len(inner_corners) > 0: + rect = rect.slide() + inner_corners = rect.inner_shape().corners() + else: + w = self.to_word() + rect = Tableau([]).insert_word(w) + if self in SemistandardSkewTableaux(): + return SemistandardTableau(rect[:]) + if self in StandardSkewTableaux(): + return StandardTableau(rect[:]) + return Tableau(rect) def standardization(self, check=True): r""" From 752df7798b2ae6b9afc60528227b4e88c745fae9 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Sat, 21 Mar 2015 18:16:36 +0100 Subject: [PATCH 041/421] Revert the cached_method decorators from commit "possibly controversial: graded_algebra and the three methods forming its interface are now cached_methods" This reverts most of commit 03bd4cfb53e24b24c65aada21f08850b40fb5c5c. --- .../categories/filtered_algebras_with_basis.py | 5 ----- src/sage/categories/graded_algebras_with_basis.py | 14 ++++++-------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 22832812521..62f24559a0e 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -17,7 +17,6 @@ #****************************************************************************** from sage.categories.filtered_modules import FilteredModulesCategory -from sage.misc.cachefunc import cached_method class FilteredAlgebrasWithBasis(FilteredModulesCategory): """ @@ -46,7 +45,6 @@ class FilteredAlgebrasWithBasis(FilteredModulesCategory): sage: TestSuite(C).run() """ class ParentMethods: - @cached_method def graded_algebra(self): r""" Return the associated graded algebra to ``self``. @@ -116,7 +114,6 @@ def graded_algebra(self): # Maps - @cached_method def to_graded_conversion(self): r""" Return the canonical `R`-module isomorphism @@ -144,7 +141,6 @@ def to_graded_conversion(self): return self.module_morphism(diagonal=lambda x: base_one, codomain=self.graded_algebra()) - @cached_method def from_graded_conversion(self): r""" Return the inverse of the canonical `R`-module isomorphism @@ -174,7 +170,6 @@ def from_graded_conversion(self): return self.graded_algebra().module_morphism(diagonal=lambda x: base_one, codomain=self) - @cached_method def projection(self, i): r""" Return the `i`-th projection `p_i : F_i \to G_i` (in the diff --git a/src/sage/categories/graded_algebras_with_basis.py b/src/sage/categories/graded_algebras_with_basis.py index f6b8dce4513..949fceddfb3 100644 --- a/src/sage/categories/graded_algebras_with_basis.py +++ b/src/sage/categories/graded_algebras_with_basis.py @@ -10,7 +10,6 @@ #****************************************************************************** from sage.categories.graded_modules import GradedModulesCategory -from sage.misc.cachefunc import cached_method class GradedAlgebrasWithBasis(GradedModulesCategory): """ @@ -32,7 +31,6 @@ class GradedAlgebrasWithBasis(GradedModulesCategory): class ParentMethods: # This needs to be copied in GradedAlgebras because we need to have # FilteredAlgebrasWithBasis as an extra super category - @cached_method def graded_algebra(self): """ Return the associated graded algebra to ``self``. @@ -77,12 +75,12 @@ def graded_algebra(self): return self # .. TODO:: - # Possibly override ``to_graded_conversion`` and - # ``from_graded_conversion`` with identity morphisms? - # I have to admit I don't know the right way to construct - # identity morphisms other than using the identity matrix. - # Also, ``projection`` could be overridden by, well, a - # projection. + # Possibly override ``to_graded_conversion`` and + # ``from_graded_conversion`` with identity morphisms? + # I have to admit I don't know the right way to construct + # identity morphisms other than using the identity matrix. + # Also, ``projection`` could be overridden by, well, a + # projection. class ElementMethods: pass From dfaaaeb6d9652e071ea89c9854da984bc8ada319 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Sat, 21 Mar 2015 19:41:08 +0100 Subject: [PATCH 042/421] associated-graded now also defined for filtered *modules*, although still named 'graded algebra' in this case --- src/sage/algebras/associated_graded.py | 43 +- .../categories/filtered_modules_with_basis.py | 401 ++++++++++++++++++ 2 files changed, 436 insertions(+), 8 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index f286964aa95..a86df92acbf 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -16,16 +16,16 @@ from sage.misc.cachefunc import cached_method from copy import copy -from sage.categories.algebras_with_basis import AlgebrasWithBasis +from sage.categories.modules_with_basis import ModulesWithBasis from sage.sets.family import Family from sage.combinat.free_module import CombinatorialFreeModule class AssociatedGradedAlgebra(CombinatorialFreeModule): r""" - The associated graded algebra `\operatorname{gr} A` - of a filtered algebra with basis `A`. + The associated graded algebra/module `\operatorname{gr} A` + of a filtered algebra/module with basis `A`. - Let `A` be a filtered algebra over a commutative ring `R`. + Let `A` be a filtered module over a commutative ring `R`. Let `(F_i)_{i \in I}` be the filtration of `A`, with `I` being a totally ordered set. Define @@ -42,7 +42,12 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): There are canonical projections `p_i : F_i \to G_i` for every `i \in I`. Moreover `\operatorname{gr} A` is naturally a graded `R`-module with `G_i` being the `i`-th graded component. + This graded `R`-module is known as the *associated graded module* + (or, for short, just *graded module*) of `A`. + Now, assume that `A` (endowed with the filtration + `(F_i)_{i \in I}`) is not just a filtered `R`-module, but also + a filtered `R`-algebra. Let `u \in G_i` and `v \in G_j`, and let `u' \in F_i` and `v' \in F_j` be lifts of `u` and `v`, respectively (so that `u = p_i(u')` and `v = p_j(v')`). Then, we define a @@ -93,9 +98,28 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): INPUT: - - ``A`` -- a filtered algebra with basis + - ``A`` -- a filtered module (or algebra) with basis - EXAMPLES:: + OUTPUT: + + The associated graded module of `A`, if `A` is just a filtered + `R`-module. + The associated graded algebra of `A`, if `A` is a filtered + `R`-algebra. + + EXAMPLES: + + Associated graded module of a filtered module:: + + sage: A = Modules(QQ).WithBasis().Filtered().example() + sage: grA = A.graded_algebra() + sage: grA.category() + Category of graded modules with basis over Rational Field + sage: x = A.basis()[Partition([3,2,1])] + sage: grA(x) + Bbar[[3, 2, 1]] + + Associated graded algebra of a filtered algebra:: sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: grA = A.graded_algebra() @@ -147,7 +171,7 @@ def __init__(self, A, category=None): sage: grA = A.graded_algebra() sage: TestSuite(grA).run(elements=[prod(grA.algebra_generators())]) """ - if A not in AlgebrasWithBasis(A.base_ring().category()).Filtered(): + if A not in ModulesWithBasis(A.base_ring().category()).Filtered(): raise ValueError("the base algebra must be filtered and with basis") self._A = A @@ -180,7 +204,10 @@ def _repr_(self): the universal enveloping algebra of Lie algebra of RR^3 with cross product over Rational Field """ - return "Graded Algebra of {}".format(self._A) + from sage.categories.algebras_with_basis import AlgebrasWithBasis + if self in AlgebrasWithBasis: + return "Graded Algebra of {}".format(self._A) + return "Graded Module of {}".format(self._A) def _latex_(self): r""" diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 326a099fcfb..0fefaa12e19 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -190,6 +190,407 @@ def basis(self, d=None): else: return Family(self._indices.subset(size=d), self.monomial) + def graded_algebra(self): + r""" + Return the associated graded module to ``self``. + + See :class:`~sage.algebras.associated_graded.AssociatedGradedAlgebra` + for the definition and the properties of this. + + If the filtered module ``self`` with basis is called `A`, + then this method returns `\operatorname{gr} A`. The method + :meth:`to_graded_conversion` returns the canonical + `R`-module isomorphism `A \to \operatorname{gr} A` induced + by the basis of `A`, and the method + :meth:`from_graded_conversion` returns the inverse of this + isomorphism. The method :meth:`projection` projects + elements of `A` onto `\operatorname{gr} A` according to + their place in the filtration on `A`. + + .. WARNING:: + + When not overridden, this method returns the default + implementation of an associated graded module -- + namely, ``AssociatedGradedAlgebra(self)``, where + ``AssociatedGradedAlgebra`` is + :class:`~sage.algebras.associated_graded.AssociatedGradedAlgebra`. + But some instances of :class:`FilteredModulesWithBasis` + override this method, as the associated graded module + often is (isomorphic) to a simpler object (for instance, + the associated graded module of a graded module can be + identified with the graded module itself). Generic code + that uses associated graded modules (such as the code + of the :meth:`induced_graded_map` method below) should + make sure to only communicate with them via the + :meth:`to_graded_conversion`, + :meth:`from_graded_conversion` and + :meth:`projection` methods (in particular, + do not expect there to be a conversion from ``self`` + to ``self.graded_algebra()``; this currently does not + work for Clifford algebras). Similarly, when + overriding :meth:`graded_algebra`, make sure to + accordingly redefine these three methods, unless their + definitions below still apply to your case (this will + happen whenever the basis of your :meth:`graded_algebra` + has the same indexing set as ``self``, and the partition + of this indexing set according to degree is the same as + for ``self``). + + .. TODO:: + + Maybe the thing about the conversion from ``self`` + to ``self.graded_algebra()`` on the Clifford at least + could be made to work? (I would still warn the user + against ASSUMING that it must work -- as there is + probably no way to guarantee it in all cases, and + we shouldn't require users to mess with + element constructors.) + + EXAMPLES:: + + sage: A = ModulesWithBasis(ZZ).Filtered().example() + sage: A.graded_algebra() + Graded Module of An example of a filtered module with basis: + the free module on partitions over Integer Ring + """ + from sage.algebras.associated_graded import AssociatedGradedAlgebra + return AssociatedGradedAlgebra(self) + + # Maps + + def to_graded_conversion(self): + r""" + Return the canonical `R`-module isomorphism + `A \to \operatorname{gr} A` induced by the basis of `A` + (where `A = ` ``self``). + + This is an isomorphism of `R`-modules. See + the class documentation :class:`AssociatedGradedAlgebra`. + + .. SEEALSO:: + + :meth:`from_graded_conversion` + + EXAMPLES:: + + sage: A = Modules(QQ).WithBasis().Filtered().example() + sage: p = -2 * A.an_element(); p + -4*P[] - 4*P[1] - 6*P[2] + sage: q = A.to_graded_conversion()(p); q + -4*Bbar[[]] - 4*Bbar[[1]] - 6*Bbar[[2]] + sage: q.parent() is A.graded_algebra() + True + """ + base_one = self.base_ring().one() + return self.module_morphism(diagonal=lambda x: base_one, + codomain=self.graded_algebra()) + + def from_graded_conversion(self): + r""" + Return the inverse of the canonical `R`-module isomorphism + `A \to \operatorname{gr} A` induced by the basis of `A` + (where `A = ` ``self``). This inverse is an isomorphism + `\operatorname{gr} A \to A`. + + This is an isomorphism of `R`-modules. See + the class documentation :class:`AssociatedGradedAlgebra`. + + .. SEEALSO:: + + :meth:`to_graded_conversion` + + EXAMPLES:: + + sage: A = Modules(QQ).WithBasis().Filtered().example() + sage: p = -2 * A.an_element(); p + -4*P[] - 4*P[1] - 6*P[2] + sage: q = A.to_graded_conversion()(p); q + -4*Bbar[[]] - 4*Bbar[[1]] - 6*Bbar[[2]] + sage: A.from_graded_conversion()(q) == p + True + sage: q.parent() is A.graded_algebra() + True + """ + base_one = self.base_ring().one() + return self.graded_algebra().module_morphism(diagonal=lambda x: base_one, + codomain=self) + + def projection(self, i): + r""" + Return the `i`-th projection `p_i : F_i \to G_i` (in the + notations of the class documentation + :class:`AssociatedGradedAlgebra`, where `A = ` ``self``). + + This method actually does not return the map `p_i` itself, + but an extension of `p_i` to the whole `R`-module `A`. + This extension is the composition of the `R`-module + isomorphism `A \to \operatorname{gr} A` with the canonical + projection of the graded `R`-module `\operatorname{gr} A` + onto its `i`-th graded component `G_i`. The codomain of + this map is `\operatorname{gr} A`, although its actual + image is `G_i`. The map `p_i` is obtained from this map + by restricting its domain to `F_i` and its image to `G_i`. + + EXAMPLES:: + + sage: A = Modules(QQ).WithBasis().Filtered().example() + sage: p = -2 * A.an_element(); p + -4*P[] - 4*P[1] - 6*P[2] + sage: q = A.projection(2)(p); q + -6*Bbar[[2]] + sage: q.parent() is A.graded_algebra() + True + sage: A.projection(3)(p) + 0 + """ + base_zero = self.base_ring().zero() + base_one = self.base_ring().one() + grA = self.graded_algebra() + proj = lambda x: (base_one if self.degree_on_basis(x) == i + else base_zero) + return self.module_morphism(diagonal=proj, codomain=grA) + + def induced_graded_map(self, other, f): + r""" + Return the graded linear map between the associated graded + modules of ``self`` and ``other`` canonically induced by + the filtration-preserving map ``f : self -> other``. + + Let `A` and `B` be two filtered modules with basis, and let + `(F_i)_{i \in I}` and `(G_i)_{i \in I}` be their + filtrations. Let `f : A \to B` be a linear map which + preserves the filtration (i.e., satisfies `f(F_i) \subseteq + G_i` for all `i \in I`). Then, there is a canonically + defined graded linear map + `\operatorname{gr} f : \operatorname{gr} A \to + \operatorname{gr} B` which satisfies + + .. MATH:: + + (\operatorname{gr} f) (p_i(a)) = p_i(f(a)) + \qquad \text{for all } i \in I \text{ and } a \in F_i , + + where the `p_i` on the left hand side is the canonical + projection from `F_i` onto the `i`-th graded component + of `\operatorname{gr} A`, while the `p_i` on the right + hand side is the canonical projection from `G_i` onto + the `i`-th graded component of `\operatorname{gr} B`. + + INPUT: + + - ``other`` -- a filtered algebra with basis + + - ``f`` -- a filtration-preserving linear map from ``self`` + to ``other`` (can be given as a morphism or as a function) + + OUTPUT: + + The graded linear map `\operatorname{gr} f`. + + EXAMPLES: + + **Example 1.** + + We start with the free `\QQ`-module with basis the set of all + partitions:: + + sage: A = Modules(QQ).WithBasis().Filtered().example(); A + An example of a filtered module with basis: the free module + on partitions over Rational Field + sage: M = A.indices(); M + Partitions + sage: p1, p2, p21, p321 = [A.basis()[Partition(i)] for i in [[1], [2], [2,1], [3,2,1]]] + + Let us define a map from ``A`` to itself which acts on the + basis by sending every partition `\lambda` to the sum of + the conjugates of all partitions `\mu` for which + `\lambda / \mu` is a horizontal strip:: + + sage: def map_on_basis(lam): + ....: return A.sum_of_monomials([Partition(mu).conjugate() for k in range(sum(lam) + 1) + ....: for mu in lam.remove_horizontal_border_strip(k)]) + sage: f = A.module_morphism(on_basis=map_on_basis, + ....: codomain=A) + sage: f(p1) + P[] + P[1] + sage: f(p2) + P[] + P[1] + P[1, 1] + sage: f(p21) + P[1] + P[1, 1] + P[2] + P[2, 1] + sage: f(p21 - p1) + -P[] + P[1, 1] + P[2] + P[2, 1] + sage: f(p321) + P[2, 1] + P[2, 1, 1] + P[2, 2] + P[2, 2, 1] + + P[3, 1] + P[3, 1, 1] + P[3, 2] + P[3, 2, 1] + + We now compute `\operatorname{gr} f` :: + + sage: grA = A.graded_algebra(); grA + Graded Module of An example of a filtered module with basis: + the free module on partitions over Rational Field + sage: pp1, pp2, pp21, pp321 = [A.to_graded_conversion()(i) for i in [p1, p2, p21, p321]] + sage: pp2 + 4 * pp21 + Bbar[[2]] + 4*Bbar[[2, 1]] + sage: grf = A.induced_graded_map(A, f); grf + Generic endomorphism of Graded Module of An example of a + filtered module with basis: + the free module on partitions over Rational Field + sage: grf(pp1) + Bbar[[1]] + sage: grf(pp2 + 4 * pp21) + Bbar[[1, 1]] + 4*Bbar[[2, 1]] + + **Example 2.** + + We shall now construct `\operatorname{gr} f` for a + different map `f` out of the same ``A``; the new map + `f` will lead into a graded algebra already, namely into + the algebra of symmetric functions:: + + sage: h = SymmetricFunctions(QQ).h() + sage: def map_on_basis(lam): # redefining map_on_basis + ....: return h.sum_of_monomials([Partition(mu).conjugate() for k in range(sum(lam) + 1) + ....: for mu in lam.remove_horizontal_border_strip(k)]) + sage: f = A.module_morphism(on_basis=map_on_basis, + ....: codomain=h) # redefining f + sage: f(p1) + h[] + h[1] + sage: f(p2) + h[] + h[1] + h[1, 1] + sage: f(A.zero()) + 0 + sage: f(p2 - 3*p1) + -2*h[] - 2*h[1] + h[1, 1] + + The algebra ``h`` of symmetric functions in the `h`-basis + is already graded, so its associated graded algebra is + implemented as itself:: + + sage: grh = h.graded_algebra(); grh is h + True + sage: grf = A.induced_graded_map(h, f); grf + Generic morphism: + From: Graded Module of An example of a filtered + module with basis: the free module on partitions + over Rational Field + To: Symmetric Functions over Rational Field + in the homogeneous basis + sage: grf(pp1) + h[1] + sage: grf(pp2) + h[1, 1] + sage: grf(pp321) + h[3, 2, 1] + sage: grf(pp2 - 3*pp1) + -3*h[1] + h[1, 1] + sage: grf(pp21) + h[2, 1] + sage: grf(grA.zero()) + 0 + + **Example 3.** + + After having had a graded module as the codomain, let us try to + have one as the domain instead. Our new ``f`` will go from ``h`` + to ``A``:: + + sage: def map_on_basis(lam): # redefining map_on_basis + ....: return A.sum_of_monomials([Partition(mu).conjugate() for k in range(sum(lam) + 1) + ....: for mu in lam.remove_horizontal_border_strip(k)]) + sage: f = h.module_morphism(on_basis=map_on_basis, + ....: codomain=A) # redefining f + sage: f(h[1]) + P[] + P[1] + sage: f(h[2]) + P[] + P[1] + P[1, 1] + sage: f(h[1, 1]) + P[1] + P[2] + sage: f(h[2, 2]) + P[1, 1] + P[2, 1] + P[2, 2] + sage: f(h[3, 2, 1]) + P[2, 1] + P[2, 1, 1] + P[2, 2] + P[2, 2, 1] + + P[3, 1] + P[3, 1, 1] + P[3, 2] + P[3, 2, 1] + sage: f(h.one()) + P[] + sage: grf = h.induced_graded_map(A, f); grf + Generic morphism: + From: Symmetric Functions over Rational Field + in the homogeneous basis + To: Graded Module of An example of a filtered + module with basis: the free module on partitions + over Rational Field + sage: grf(h[1]) + Bbar[[1]] + sage: grf(h[2]) + Bbar[[1, 1]] + sage: grf(h[1, 1]) + Bbar[[2]] + sage: grf(h[2, 2]) + Bbar[[2, 2]] + sage: grf(h[3, 2, 1]) + Bbar[[3, 2, 1]] + sage: grf(h.one()) + Bbar[[]] + + **Example 4.** + + The construct `\operatorname{gr} f` also makes sense when `f` + is a filtration-preserving map between graded modules. :: + + sage: def map_on_basis(lam): # redefining map_on_basis + ....: return h.sum_of_monomials([Partition(mu).conjugate() for k in range(sum(lam) + 1) + ....: for mu in lam.remove_horizontal_border_strip(k)]) + sage: f = h.module_morphism(on_basis=map_on_basis, + ....: codomain=h) # redefining f + sage: f(h[1]) + h[] + h[1] + sage: f(h[2]) + h[] + h[1] + h[1, 1] + sage: f(h[1, 1]) + h[1] + h[2] + sage: f(h[2, 1]) + h[1] + h[1, 1] + h[2] + h[2, 1] + sage: f(h.one()) + h[] + sage: grf = h.induced_graded_map(h, f); grf + Generic endomorphism of Symmetric Functions over Rational + Field in the homogeneous basis + sage: grf(h[1]) + h[1] + sage: grf(h[2]) + h[1, 1] + sage: grf(h[1, 1]) + h[2] + sage: grf(h[2, 1]) + h[2, 1] + sage: grf(h.one()) + h[] + """ + grA = self.graded_algebra() + grB = other.graded_algebra() + from sage.categories.graded_modules_with_basis import GradedModulesWithBasis + cat = GradedModulesWithBasis(self.base_ring()) + from_gr = self.from_graded_conversion() + def on_basis(m): + i = grA.degree_on_basis(m) + lifted_img_of_m = f(from_gr(grA.monomial(m))) + return other.projection(i)(lifted_img_of_m) + return grA.module_morphism(on_basis=on_basis, + codomain=grB, category=cat) + # If we could assume that the projection of the basis + # element of ``self`` indexed by an index ``m`` is the + # basis element of ``grA`` indexed by ``m``, then this + # could go faster: + # + # def on_basis(m): + # i = grA.degree_on_basis(m) + # return grB.projection(i)(f(self.monomial(m))) + # return grA.module_morphism(on_basis=on_basis, + # codomain=grB, category=cat) + # + # But this assumption might come back to bite us in the + # ass one day. What do you think? + class ElementMethods: def is_homogeneous(self): From cd6d21aa19660d5b60074d5e5299310e6e27d626 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 23 Mar 2015 19:54:34 -0700 Subject: [PATCH 043/421] Created super categories for algebras and modules. --- src/sage/algebras/clifford_algebra.py | 18 +- src/sage/categories/algebras.py | 1 + src/sage/categories/algebras_with_basis.py | 1 + src/sage/categories/modules.py | 34 +++ src/sage/categories/modules_with_basis.py | 1 + src/sage/categories/super_algebras.py | 34 +++ .../categories/super_algebras_with_basis.py | 35 +++ src/sage/categories/super_modules.py | 259 ++++++++++++++++++ .../categories/super_modules_with_basis.py | 160 +++++++++++ 9 files changed, 531 insertions(+), 12 deletions(-) create mode 100644 src/sage/categories/super_algebras.py create mode 100644 src/sage/categories/super_algebras_with_basis.py create mode 100644 src/sage/categories/super_modules.py create mode 100644 src/sage/categories/super_modules_with_basis.py diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 53403eb8425..8225292888f 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -18,8 +18,7 @@ from copy import copy from sage.categories.algebras_with_basis import AlgebrasWithBasis -from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis -from sage.categories.graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis +from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis from sage.categories.modules_with_basis import ModuleMorphismByLinearity from sage.categories.poor_man_map import PoorManMap from sage.rings.all import ZZ @@ -479,11 +478,6 @@ class CliffordAlgebra(CombinatorialFreeModule): a*d sage: d*c*b*a + a + 4*b*c a*b*c*d + 4*b*c + a - - .. WARNING:: - - The Clifford algebra is not graded, but instead filtered. This - will be changed once :trac:`17096` is finished. """ @staticmethod def __classcall_private__(cls, Q, names=None): @@ -536,7 +530,7 @@ def __init__(self, Q, names, category=None): self._quadratic_form = Q R = Q.base_ring() if category is None: - category = GradedAlgebrasWithBasis(R) + category = AlgebrasWithBasis(R).Super() indices = SubsetsSorted(range(Q.dim())) CombinatorialFreeModule.__init__(self, R, indices, category=category) self._assign_names(names) @@ -1050,7 +1044,7 @@ def lift_module_morphism(self, m, names=None): remove_zeros=True ) for i in x) return Cl.module_morphism(on_basis=f, codomain=self, - category=GradedAlgebrasWithBasis(self.base_ring())) + category=AlgebrasWithBasis(self.base_ring()).Graded()) def lift_isometry(self, m, names=None): r""" @@ -1115,7 +1109,7 @@ def lift_isometry(self, m, names=None): remove_zeros=True ) for i in x) return self.module_morphism(on_basis=f, codomain=Cl, - category=GradedAlgebrasWithBasis(self.base_ring())) + category=AlgebrasWithBasis(self.base_ring()).Super()) # This is a general method for finite dimensional algebras with bases # and should be moved to the corresponding category once there is @@ -1418,7 +1412,7 @@ def __init__(self, R, names): sage: E. = ExteriorAlgebra(QQ) sage: TestSuite(E).run() """ - CliffordAlgebra.__init__(self, QuadraticForm(R, len(names)), names, GradedHopfAlgebrasWithBasis(R)) + CliffordAlgebra.__init__(self, QuadraticForm(R, len(names)), names, HopfAlgebrasWithBasis(R).Super()) # TestSuite will fail if the HopfAlgebra classes will ever have tests for # the coproduct being an algebra morphism -- since this is really a # Hopf superalgebra, not a Hopf algebra. @@ -1562,7 +1556,7 @@ def lift_morphism(self, phi, names=None): f = lambda x: E.prod(E._from_dict( {(j,): phi[j,i] for j in range(n)}, remove_zeros=True ) for i in x) - return self.module_morphism(on_basis=f, codomain=E, category=GradedAlgebrasWithBasis(R)) + return self.module_morphism(on_basis=f, codomain=E, category=AlgebrasWithBasis(R).Super()) def volume_form(self): """ diff --git a/src/sage/categories/algebras.py b/src/sage/categories/algebras.py index 3440acaab7c..6fbf51d83c5 100644 --- a/src/sage/categories/algebras.py +++ b/src/sage/categories/algebras.py @@ -88,6 +88,7 @@ def __contains__(self, x): Commutative = LazyImport('sage.categories.commutative_algebras', 'CommutativeAlgebras', at_startup=True) Graded = LazyImport('sage.categories.graded_algebras', 'GradedAlgebras') + Super = LazyImport('sage.categories.super_algebras', 'SuperAlgebras') WithBasis = LazyImport('sage.categories.algebras_with_basis', 'AlgebrasWithBasis') class ElementMethods: diff --git a/src/sage/categories/algebras_with_basis.py b/src/sage/categories/algebras_with_basis.py index 8a4668edf1e..e3ae7546129 100644 --- a/src/sage/categories/algebras_with_basis.py +++ b/src/sage/categories/algebras_with_basis.py @@ -118,6 +118,7 @@ def example(self, alphabet = ('a','b','c')): FiniteDimensional = LazyImport('sage.categories.finite_dimensional_algebras_with_basis', 'FiniteDimensionalAlgebrasWithBasis') Graded = LazyImport('sage.categories.graded_algebras_with_basis', 'GradedAlgebrasWithBasis') + Super = LazyImport('sage.categories.super_algebras_with_basis', 'SuperAlgebrasWithBasis') class ParentMethods: diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 4eaa56726de..5b7325e6e18 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -378,6 +378,39 @@ def Graded(self, base_ring=None): from sage.categories.graded_modules import GradedModulesCategory return GradedModulesCategory.category_of(self) + @cached_method + def Super(self, base_ring=None): + r""" + Return the subcategory of the super objects of ``self``. + + INPUT:: + + - ``base_ring`` -- this is ignored + + EXAMPLES:: + + sage: Modules(ZZ).Super() + Category of graded modules over Integer Ring + + sage: Coalgebras(QQ).Super() + Join of Category of graded modules over Rational Field and Category of coalgebras over Rational Field + + sage: AlgebrasWithBasis(QQ).Super() + Category of graded algebras with basis over Rational Field + + .. TODO:: + + Same as :meth:`Graded`. + + TESTS:: + + sage: Coalgebras(QQ).Super.__module__ + 'sage.categories.modules' + """ + assert base_ring is None or base_ring is self.base_ring() + from sage.categories.super_modules import SuperModulesCategory + return SuperModulesCategory.category_of(self) + @cached_method def WithBasis(self): r""" @@ -425,6 +458,7 @@ def extra_super_categories(self): return [] Graded = LazyImport('sage.categories.graded_modules', 'GradedModules') + Super = LazyImport('sage.categories.super_modules', 'SuperModules') WithBasis = LazyImport('sage.categories.modules_with_basis', 'ModulesWithBasis') class ParentMethods: diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 0f899ca04f4..0c6d9edea69 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -180,6 +180,7 @@ def is_abelian(self): FiniteDimensional = LazyImport('sage.categories.finite_dimensional_modules_with_basis', 'FiniteDimensionalModulesWithBasis') Graded = LazyImport('sage.categories.graded_modules_with_basis', 'GradedModulesWithBasis') + Super = LazyImport('sage.categories.super_modules_with_basis', 'SuperModulesWithBasis') class ParentMethods: @cached_method diff --git a/src/sage/categories/super_algebras.py b/src/sage/categories/super_algebras.py new file mode 100644 index 00000000000..355bf857758 --- /dev/null +++ b/src/sage/categories/super_algebras.py @@ -0,0 +1,34 @@ +r""" +Super Algebras +""" +#***************************************************************************** +# Copyright (C) 2015 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.categories.super_modules import SuperModulesCategory + +class SuperAlgebras(SuperModulesCategory): + """ + The category of super algebras. + + EXAMPLES:: + + sage: Algebras(ZZ).Super() + Category of super algebras over Integer Ring + sage: Algebras(ZZ).Super().super_categories() + [Category of graded algebras over Integer Ring, + Category of super modules over Integer Ring] + + TESTS:: + + sage: TestSuite(Algebras(ZZ).Super()).run() + """ + class ParentMethods: + pass + + class ElementMethods: + pass + diff --git a/src/sage/categories/super_algebras_with_basis.py b/src/sage/categories/super_algebras_with_basis.py new file mode 100644 index 00000000000..9ba242c829f --- /dev/null +++ b/src/sage/categories/super_algebras_with_basis.py @@ -0,0 +1,35 @@ +r""" +Super algebras with basis +""" +#***************************************************************************** +# Copyright (C) 2015 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.categories.super_modules import SuperModulesCategory + +class SuperAlgebrasWithBasis(SuperModulesCategory): + """ + The category of super algebras with a distinguished basis + + EXAMPLES:: + + sage: C = Algebras(ZZ).WithBasis().Super(); C + Category of super algebras with basis over Integer Ring + sage: sorted(C.super_categories(), key=str) + [Category of graded algebras with basis over Integer Ring, + Category of super algebras over Integer Ring, + Category of super modules with basis over Integer Ring] + + TESTS:: + + sage: TestSuite(C).run() + """ + class ParentMethods: + pass + + class ElementMethods: + pass + diff --git a/src/sage/categories/super_modules.py b/src/sage/categories/super_modules.py new file mode 100644 index 00000000000..154ca9f6168 --- /dev/null +++ b/src/sage/categories/super_modules.py @@ -0,0 +1,259 @@ +r""" +Super modules +""" +#***************************************************************************** +# Copyright (C) 2015 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_class_attribute +from sage.categories.category import Category +from sage.categories.category_types import Category_over_base_ring +from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.covariant_functorial_construction import RegressiveCovariantConstructionCategory +from sage.categories.modules import Modules + +class SuperModulesCategory(RegressiveCovariantConstructionCategory, Category_over_base_ring): + def __init__(self, base_category): + """ + EXAMPLES:: + + sage: C = Algebras(QQ).Super() + sage: C + Category of super algebras over Rational Field + sage: C.base_category() + Category of algebras over Rational Field + sage: sorted(C.super_categories(), key=str) + [Category of graded algebras over Rational Field, + Category of super modules over Rational Field] + + sage: AlgebrasWithBasis(QQ).Super().base_ring() + Rational Field + sage: HopfAlgebrasWithBasis(QQ).Super().base_ring() + Rational Field + """ + super(SuperModulesCategory, self).__init__(base_category, base_category.base_ring()) + + _functor_category = "Super" + + @lazy_class_attribute + def _base_category_class(cls): + """ + Recover the class of the base category. + + OUTPUT: + + A *tuple* whose first entry is the base category class. + + .. WARNING:: + + This is only used for super categories that are not + implemented as nested classes, and won't work otherwise. + + .. SEEALSO:: :meth:`__classcall__` + + EXAMPLES:: + + sage: from sage.categories.super_modules import SuperModules + sage: from sage.categories.super_algebras_with_basis import SuperAlgebrasWithBasis + sage: SuperModules._base_category_class + (,) + sage: SuperAlgebrasWithBasis._base_category_class + (,) + + The reason for wrapping the base category class in a tuple is + that, often, the base category class implements a + :meth:`__classget__` method which would get in the way upon + attribute access:: + + sage: F = SuperAlgebrasWithBasis + sage: F._foo = F._base_category_class[0] + sage: F._foo + Traceback (most recent call last): + ... + AssertionError: base category class for <...AlgebrasWithBasis'> mismatch; + expected <...Algebras'>, got <...SuperAlgebrasWithBasis'> + """ + module_name = cls.__module__.replace("super_","") + import sys + name = cls.__name__.replace("Super","") + __import__(module_name) + module = sys.modules[module_name] + return (module.__dict__[name],) + + @staticmethod + def __classcall__(cls, category, *args): + """ + Magic support for putting Super categories in their own file. + + EXAMPLES:: + + sage: from sage.categories.super_modules import SuperModules + sage: SuperModules(ZZ) # indirect doctest + Category of super modules over Integer Ring + sage: Modules(ZZ).Super() + Category of super modules over Integer Ring + sage: SuperModules(ZZ) is Modules(ZZ).Super() + True + + .. TODO:: + + Generalize this support for all other functorial + constructions if at some point we have a category ``Blah`` for + which we want to implement the construction ``Blah.Foo`` in a + separate file like we do for e.g. :class:`SuperModules`, + :class:`SuperAlgebras`, ... + + .. SEEALSO:: :meth:`_base_category_class` + """ + base_category_class = cls._base_category_class[0] + if isinstance(category, base_category_class): + return super(SuperModulesCategory, cls).__classcall__(cls, category, *args) + else: + return base_category_class(category, *args).Super() + + def _repr_object_names(self): + """ + EXAMPLES:: + + sage: AlgebrasWithBasis(QQ).Super() # indirect doctest + Category of super algebras with basis over Rational Field + """ + return "super {}".format(self.base_category()._repr_object_names()) + + @classmethod + def default_super_categories(cls, category, *args): + """ + Returns the default super categories of ``category.Subobjects()`` + + Mathematical meaning: if `A` is a super version of `B`, + then `A` is also a graded version of `B`. + + INPUT: + + - ``cls`` -- the class ``SubobjectsCategory`` + - ``category`` -- a category `Cat` + + OUTPUT: a (join) category + + In practice, this returns ``category.Subquotients()``, joined + together with the result of the method + :meth:`RegressiveCovariantConstructionCategory.default_super_categories() ` + (that is the join of ``category`` and ``cat.Subobjects()`` for + each ``cat`` in the super categories of ``category``). + + EXAMPLES:: + """ + return Category.join([category.Graded(), super(SuperModulesCategory, cls).default_super_categories(category, *args)]) + +class SuperModules(SuperModulesCategory): + """ + The category of super modules. + + EXAMPLES:: + + sage: Modules(ZZ).Super() + Category of super modules over Integer Ring + sage: Modules(ZZ).Super().super_categories() + [Category of graded modules over Integer Ring] + + The category of super modules defines the super structure which + shall be preserved by morphisms:: + + sage: Modules(ZZ).Super().additional_structure() + Category of super modules over Integer Ring + + TESTS:: + + sage: TestSuite(Modules(ZZ).Super()).run() + """ + def extra_super_categories(self): + r""" + Adds :class:`VectorSpaces` to the super categories of ``self`` if + the base ring is a field. + + EXAMPLES:: + + sage: Modules(QQ).Super().extra_super_categories() + [Category of vector spaces over Rational Field] + sage: Modules(ZZ).Super().extra_super_categories() + [] + + This makes sure that ``Modules(QQ).Super()`` returns an + instance of :class:`GradedModules` and not a join category of + an instance of this class and of ``VectorSpaces(QQ)``:: + + sage: type(Modules(QQ).Super()) + + + .. TODO:: + + Get rid of this workaround once there is a more systematic + approach for the alias ``Modules(QQ)`` -> ``VectorSpaces(QQ)``. + Probably the later should be a category with axiom, and + covariant constructions should play well with axioms. + """ + from sage.categories.modules import Modules + from sage.categories.fields import Fields + base_ring = self.base_ring() + if base_ring in Fields: + return [Modules(base_ring)] + else: + return [] + + class ParentMethods: + pass + + class ElementMethods: + def is_even_odd(self): + """ + Return ``0`` if ``self`` is an even element or ``1`` + if an odd element. + + EXAMPLES:: + + sage: cat = Algebras(QQ).WithBasis().Super() + sage: C = CombinatorialFreeModule(QQ, Partitions(), category=cat) + sage: C.degree_on_basis = sum + sage: C.basis()[2,2,1].is_even_odd() + 1 + sage: C.basis()[2,2].is_even_odd() + 0 + """ + return self.degree() % 2 + + def is_even(self): + """ + Return if ``self`` is an even element. + + EXAMPLES:: + + sage: cat = Algebras(QQ).WithBasis().Super() + sage: C = CombinatorialFreeModule(QQ, Partitions(), category=cat) + sage: C.degree_on_basis = sum + sage: C.basis()[2,2,1].is_even() + False + sage: C.basis()[2,2].is_even() + True + """ + return self.is_even_odd() == 0 + + def is_odd(self): + """ + Return if ``self`` is an odd element. + + EXAMPLES:: + + sage: cat = Algebras(QQ).WithBasis().Super() + sage: C = CombinatorialFreeModule(QQ, Partitions(), category=cat) + sage: C.degree_on_basis = sum + sage: C.basis()[2,2,1].is_odd() + True + sage: C.basis()[2,2].is_odd() + False + """ + return self.is_even_odd() == 1 + diff --git a/src/sage/categories/super_modules_with_basis.py b/src/sage/categories/super_modules_with_basis.py new file mode 100644 index 00000000000..64cfcc3da7b --- /dev/null +++ b/src/sage/categories/super_modules_with_basis.py @@ -0,0 +1,160 @@ +r""" +Super modules with basis +""" +#***************************************************************************** +# Copyright (C) 2015 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.categories.super_modules import SuperModulesCategory + +class SuperModulesWithBasis(SuperModulesCategory): + """ + The category of super modules with a distinguished basis. + + EXAMPLES:: + + sage: C = GradedModulesWithBasis(ZZ); C + Category of graded modules with basis over Integer Ring + sage: sorted(C.super_categories(), key=str) + [Category of graded modules over Integer Ring, + Category of modules with basis over Integer Ring] + sage: C is ModulesWithBasis(ZZ).Graded() + True + + TESTS:: + + sage: TestSuite(C).run() + """ + class ParentMethods: + def _even_odd_on_basis(self, m): + """ + Return if ``m`` is an index of an even or odd basis element. + + OUTPUT: + + ``0`` if ``m`` is for an even element or ``1`` if ``m`` + is for an odd element. + """ + return self.degree_on_basis(m) % 2 + + class ElementMethods: + def is_super_homogeneous(self): + r""" + Return whether this element is homogeneous, in the sense + of a super module. + + EXAMPLES:: + + sage: Q = QuadraticForm(QQ, 2, [1,2,3]) + sage: C. = CliffordAlgebra(Q) + sage: a = x + y + sage: a.is_super_homogeneous() + True + sage: a = x*y + 4 + sage: a.is_super_homogeneous() + True + sage: a = x*y + x - 3*y + 4 + sage: a.is_super_homogeneous() + False + + The exterior algebra has a `\ZZ` grading, which induces the + `\ZZ / 2\ZZ` grading, however the definition of homogeneous + elements differ because of the different gradings:: + + sage: E. = ExteriorAlgebra(QQ) + sage: a = x*y + 4 + sage: a.is_super_homogeneous() + True + sage: a.is_homogeneous() + False + """ + even_odd = self.parent()._even_odd_on_basis + degree = None + for m in self.support(): + if degree is None: + degree = even_odd(m) + else: + if degree != even_odd(m): + return False + return True + + def is_even_odd(self): + """ + Return ``0`` if ``self`` is an even element and ``1`` if + ``self`` is an odd element. + + EXAMPLES:: + + sage: Q = QuadraticForm(QQ, 2, [1,2,3]) + sage: C. = CliffordAlgebra(Q) + sage: a = x + y + sage: a.is_even_odd() + 1 + sage: a = x*y + 4 + sage: a.is_even_odd() + 0 + sage: a = x + 4 + sage: a.is_even_odd() + Traceback (most recent call last): + ... + ValueError: element is not homogeneous + """ + if not self.support(): + raise ValueError("the zero element does not have a well-defined degree") + if not self.is_super_homogeneous(): + raise ValueError("element is not homogeneous") + return self.parent()._even_odd_on_basis(self.leading_support()) + + def even_component(self): + """ + Return the even component of ``self``. + + EXAMPLES:: + + sage: Q = QuadraticForm(QQ, 2, [1,2,3]) + sage: C. = CliffordAlgebra(Q) + sage: a = x*y + x - 3*y + 4 + sage: a.even_component() + x*y + 4 + + TESTS: + + Check that this really return ``A.zero()`` and not a plain ``0``:: + + sage: a = x + y + sage: a.even_component().parent() is C + True + """ + even_odd = self.parent()._even_odd_on_basis + return self.parent().sum_of_terms((i, c) + for (i, c) in self + if even_odd(i) == 0) + + def odd_component(self): + """ + Return the odd component of ``self``. + + EXAMPLES:: + + sage: Q = QuadraticForm(QQ, 2, [1,2,3]) + sage: C. = CliffordAlgebra(Q) + sage: a = x*y + x - 3*y + 4 + sage: a.odd_component() + x - 3*y + + TESTS: + + Check that this really return ``A.zero()`` and not a plain ``0``:: + + sage: a = x*y + sage: a.odd_component().parent() is C + True + """ + even_odd = self.parent()._even_odd_on_basis + return self.parent().sum_of_terms((i, c) + for (i, c) in self + if even_odd(i) == 1) + From d1d135f03b4bed898ae018b8fec03786c8ca4c1a Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 25 Mar 2015 11:58:50 -0700 Subject: [PATCH 044/421] Give Weyl/Clifford/Exterior super powers. --- src/sage/algebras/clifford_algebra.py | 139 ++++---------------------- src/sage/algebras/weyl_algebra.py | 2 +- 2 files changed, 20 insertions(+), 121 deletions(-) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index ac915b5a01b..73552f8e651 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -476,7 +476,7 @@ class CliffordAlgebra(CombinatorialFreeModule): sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) sage: Cl = CliffordAlgebra(Q) sage: Cl - The graded Clifford algebra of the Quadratic form in 3 variables + The Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: [ 1 2 3 ] [ * 4 5 ] @@ -494,7 +494,7 @@ class CliffordAlgebra(CombinatorialFreeModule): a*b*c*d + 4*b*c + a """ @staticmethod - def __classcall_private__(cls, Q, names=None, graded=True): + def __classcall_private__(cls, Q, names=None): """ Normalize arguments to ensure a unique representation. @@ -517,9 +517,9 @@ def __classcall_private__(cls, Q, names=None, graded=True): names = tuple( '{}{}'.format(names[0], i) for i in range(Q.dim()) ) else: raise ValueError("the number of variables does not match the number of generators") - return super(CliffordAlgebra, cls).__classcall__(cls, Q, names, graded=bool(graded)) + return super(CliffordAlgebra, cls).__classcall__(cls, Q, names) - def __init__(self, Q, names, category=None, graded=True): + def __init__(self, Q, names, category=None): r""" Initialize ``self``. @@ -528,8 +528,6 @@ def __init__(self, Q, names, category=None, graded=True): sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) sage: Cl = CliffordAlgebra(Q) sage: TestSuite(Cl).run() - sage: Cl = CliffordAlgebra(Q, graded=False) - sage: TestSuite(Cl).run() TESTS: @@ -544,13 +542,8 @@ def __init__(self, Q, names, category=None, graded=True): True """ self._quadratic_form = Q - self._graded = graded R = Q.base_ring() - if category is None: - if graded: - category = AlgebrasWithBasis(R.category()).Graded() - else: - category = AlgebrasWithBasis(R.category()).Filtered() + category = AlgebrasWithBasis(R.category()).Super().Filtered().or_subcategory(category) indices = SubsetsSorted(range(Q.dim())) CombinatorialFreeModule.__init__(self, R, indices, category=category) self._assign_names(names) @@ -563,20 +556,13 @@ def _repr_(self): sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) sage: CliffordAlgebra(Q) - The graded Clifford algebra of the Quadratic form in 3 variables - over Integer Ring with coefficients: - [ 1 2 3 ] - [ * 4 5 ] - [ * * 6 ] - sage: CliffordAlgebra(Q, graded=False) - The filtered Clifford algebra of the Quadratic form in 3 variables + The Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: [ 1 2 3 ] [ * 4 5 ] [ * * 6 ] """ - gr = "graded" if self._graded else "filtered" - return "The {} Clifford algebra of the {}".format(gr, self._quadratic_form) + return "The Clifford algebra of the {}".format(self._quadratic_form) def _repr_term(self, m): """ @@ -690,24 +676,12 @@ def _coerce_map_from_(self, V): sage: b = Cl.basis()[(0,2)] sage: Clp(3*a-4*b) 2*e0*e2 - - The filtered Clifford algebra coerces into the graded Clifford - algebra, but not the other way around:: - - sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) - sage: Cl = CliffordAlgebra(Q) - sage: ClF = CliffordAlgebra(Q, graded=False) - sage: Cl.has_coerce_map_from(ClF) - True - sage: ClF.has_coerce_map_from(Cl) - False """ if isinstance(V, CliffordAlgebra): Q = self._quadratic_form try: return (V.variable_names() == self.variable_names() and - V._quadratic_form.base_change_to(self.base_ring()) == Q - and (self._graded or not V._graded)) + V._quadratic_form.base_change_to(self.base_ring()) == Q) except Exception: return False @@ -746,38 +720,6 @@ def _element_constructor_(self, x): sage: Cl3 = CliffordAlgebra(Q3, names='xyz') # different syntax for a change sage: Cl3( M((1,-3,2)) ) x + 2*z - - Conversions work between the filtered and the graded Clifford - algebra for the same form: - - sage: Q = QuadraticForm(ZZ, 3, [1,2,4,3,5,6]) - sage: Cl = CliffordAlgebra(Q); Cl - The graded Clifford algebra of the Quadratic form in 3 variables - over Integer Ring with coefficients: - [ 1 2 4 ] - [ * 3 5 ] - [ * * 6 ] - sage: Cl2 = CliffordAlgebra(Q, graded=False); Cl2 - The filtered Clifford algebra of the Quadratic form in 3 variables - over Integer Ring with coefficients: - [ 1 2 4 ] - [ * 3 5 ] - [ * * 6 ] - sage: Cl == Cl2 - False - sage: x,y,z = Cl.gens() - sage: a = (x+y)*(x+z) - 2*x + 3; a - -e0*e1 + e0*e2 + e1*e2 - 2*e0 + 6 - sage: Cl2(a) - -e0*e1 + e0*e2 + e1*e2 - 2*e0 + 6 - sage: Cl(Cl2(a)) == a - True - - .. TODO:: - - These conversions don't work. You might not want a coercion - from the graded Cl into the filtered Cl (although I don't - see why not), but a conversion should exist! """ # This is the natural lift morphism of the underlying free module if x in self.free_module(): @@ -902,22 +844,8 @@ def degree_on_basis(self, m): r""" Return the degree of the monomial indexed by ``m``. - If we consider the Clifford algebra to be `\ZZ_2`-graded, this - degree is a nonnegative integer, and should be interpreted as a - residue class modulo `2`. The degree of the monomial ``m`` in this - `\ZZ_2`-grading is defined to be the length of ``m`` taken mod `2`. - - Otherwise we are considering the Clifford algebra to be - `\NN`-filtered, and the degree of the monomial ``m`` is the - length of ``m``. - - .. WARNING: - - On the :class:`ExteriorAlgebra` class (which inherits from - :class:`CliffordAlgebra`), the :meth:`degree_on_basis` - method is overridden to always return an actual `\NN`-degree - since it is `\NN`-graded. So don't count on this method - always returning `0` or `1` !! + We are considering the Clifford algebra to be `\NN`-filtered, + and the degree of the monomial ``m`` is the length of ``m``. EXAMPLES:: @@ -926,13 +854,8 @@ def degree_on_basis(self, m): sage: Cl.degree_on_basis((0,)) 1 sage: Cl.degree_on_basis((0,1)) - 0 - sage: Cl. = CliffordAlgebra(Q, graded=False) - sage: Cl.degree_on_basis((0,1)) 2 """ - if self._graded: - return len(m) % ZZ(2) return ZZ(len(m)) def graded_algebra(self): @@ -943,18 +866,9 @@ def graded_algebra(self): sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) sage: Cl. = CliffordAlgebra(Q) - sage: Cl.graded_algebra() is Cl - True - sage: Cl. = CliffordAlgebra(Q, graded=False) sage: Cl.graded_algebra() The exterior algebra of rank 3 over Integer Ring - - .. TODO:: - - Doctest that the three methods do what they should. """ - if self._graded: - return self return ExteriorAlgebra(self.base_ring(), self.variable_names()) @cached_method @@ -1053,11 +967,11 @@ def lift_module_morphism(self, m, names=None): sage: phi = Cl.lift_module_morphism(m, 'abc') sage: phi Generic morphism: - From: The graded Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: + From: The Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: [ 10 17 3 ] [ * 11 0 ] [ * * 5 ] - To: The graded Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: + To: The Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients: [ 1 2 3 ] [ * 4 5 ] [ * * 6 ] @@ -1139,17 +1053,13 @@ def lift_module_morphism(self, m, names=None): if Q == self._quadratic_form and names is None: Cl = self else: - Cl = CliffordAlgebra(Q, names, graded=self._graded) + Cl = CliffordAlgebra(Q, names) n = self._quadratic_form.dim() f = lambda x: self.prod(self._from_dict( {(j,): m[j,i] for j in range(n)}, remove_zeros=True ) for i in x) - cat = AlgebrasWithBasis(self.base_ring()).Filtered() - # .. TODO:: - # And if the Clifford is graded, we don't use .Graded()? - # Also, why self.base_ring() and not self.base_ring().category()? - return Cl.module_morphism(on_basis=f, codomain=self, category=cat) + return Cl.module_morphism(on_basis=f, codomain=self) def lift_isometry(self, m, names=None): r""" @@ -1207,18 +1117,13 @@ def lift_isometry(self, m, names=None): else: if names is None: names = 'e' - Cl = CliffordAlgebra(Q, names, graded=self._graded) + Cl = CliffordAlgebra(Q, names) n = Q.dim() f = lambda x: Cl.prod(Cl._from_dict( {(j,): m[j,i] for j in range(n)}, remove_zeros=True ) for i in x) - - cat = AlgebrasWithBasis(self.base_ring()).Filtered() - # .. TODO:: - # And if the Clifford is graded, we don't use .Graded()? - # Also, why self.base_ring() and not self.base_ring().category()? - return self.module_morphism(on_basis=f, codomain=Cl, category=cat) + return self.module_morphism(on_basis=f, codomain=Cl) # This is a general method for finite dimensional algebras with bases # and should be moved to the corresponding category once there is @@ -1441,7 +1346,7 @@ class ExteriorAlgebra(CliffordAlgebra): `Q(v) = 0` for all vectors `v \in V`. See :class:`CliffordAlgebra` for the notion of a Clifford algebra. - The exterior algebra of an `R`-module `V` is a `\ZZ`-graded connected + The exterior algebra of an `R`-module `V` is a super connected Hopf superalgebra. It is commutative in the super sense (i.e., the odd elements anticommute and square to `0`). @@ -1455,10 +1360,6 @@ class ExteriorAlgebra(CliffordAlgebra): Hopf superalgebra with the odd-degree components forming the odd part. So use Hopf-algebraic methods with care! - .. TODO:: - - Add a category for Hopf superalgebras (perhaps part of :trac:`16513`). - INPUT: - ``R`` -- the base ring, *or* the free module whose exterior algebra @@ -1521,7 +1422,7 @@ def __init__(self, R, names): sage: E. = ExteriorAlgebra(QQ) sage: TestSuite(E).run() """ - cat = GradedHopfAlgebrasWithBasis(R.category()) + cat = HopfAlgebrasWithBasis(R.category()).Super().Graded() CliffordAlgebra.__init__(self, QuadraticForm(R, len(names)), names, cat) # TestSuite will fail if the HopfAlgebra classes will ever have tests for # the coproduct being an algebra morphism -- since this is really a @@ -1666,9 +1567,7 @@ def lift_morphism(self, phi, names=None): f = lambda x: E.prod(E._from_dict( {(j,): phi[j,i] for j in range(n)}, remove_zeros=True ) for i in x) - return self.module_morphism(on_basis=f, codomain=E, category=GradedHopfAlgebrasWithBasis(R)) - # .. TODO:: - # not R.category()? + return self.module_morphism(on_basis=f, codomain=E) def volume_form(self): """ diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index 445b6ba61ba..d074d1dedfb 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -589,7 +589,7 @@ def __init__(self, R, names=None): names = names + tuple('d' + n for n in names) if len(names) != self._n * 2: raise ValueError("variable names cannot differ by a leading 'd'") - cat = AlgebrasWithBasis(R.category()).NoZeroDivisors().Filtered() + cat = AlgebrasWithBasis(R.category()).NoZeroDivisors().Super().Filtered() Algebra.__init__(self, R, names, category=cat) def _repr_(self): From 14e19ba97f1bb096c7614241c9b09c1d0f699a19 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 26 Mar 2015 13:32:00 -0700 Subject: [PATCH 045/421] Fixing doctests and updating documentation. --- .../filtered_algebras_with_basis.py | 8 +-- src/sage/categories/filtered_modules.py | 4 ++ .../categories/filtered_modules_with_basis.py | 65 ++++++++++--------- src/sage/categories/modules_with_basis.py | 14 ++++ .../categories/super_modules_with_basis.py | 4 +- 5 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 62f24559a0e..1179b068b7b 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -78,7 +78,7 @@ def graded_algebra(self): of the :meth:`induced_graded_map` method below) should make sure to only communicate with them via the :meth:`to_graded_conversion`, - :meth:`from_graded_conversion` and + :meth:`from_graded_conversion`, and :meth:`projection` methods (in particular, do not expect there to be a conversion from ``self`` to ``self.graded_algebra()``; this currently does not @@ -459,15 +459,15 @@ def induced_graded_map(self, other, f): map `f` between two Clifford algebras:: sage: Q = QuadraticForm(ZZ, 2, [1,2,3]) - sage: B = CliffordAlgebra(Q, names=['u','v'], graded=False); B - The filtered Clifford algebra of the Quadratic form in 2 + sage: B = CliffordAlgebra(Q, names=['u','v']); B + The Clifford algebra of the Quadratic form in 2 variables over Integer Ring with coefficients: [ 1 2 ] [ * 3 ] sage: m = Matrix(ZZ, [[1, 2], [1, -1]]) sage: f = B.lift_module_morphism(m, names=['x','y']) sage: A = f.domain(); A - The filtered Clifford algebra of the Quadratic form in 2 + The Clifford algebra of the Quadratic form in 2 variables over Integer Ring with coefficients: [ 6 0 ] [ * 3 ] diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index 784b0faaf59..7ba8bd51a7c 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -16,6 +16,10 @@ .. TODO:: Implement filtrations for all concrete categories. + +.. TODO:: + + Implement `\operatorname{gr}` as a functor. """ #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 0fefaa12e19..bf7642ae79e 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -29,6 +29,7 @@ #****************************************************************************** from sage.categories.filtered_modules import FilteredModulesCategory +from sage.misc.abstract_method import abstract_method class FilteredModulesWithBasis(FilteredModulesCategory): r""" @@ -80,15 +81,11 @@ class FilteredModulesWithBasis(FilteredModulesCategory): `J_i` (as an iterable). If the latter conditions are not satisfied, then :meth:`basis` must be overridden. - .. TODO:: - - This deserves to be handled better, and the contracts - involved might also profit from some explicit writing-up. + .. NOTE:: - What else should be part of the requirements for - inheriting from :class:`FilteredModulesWithBasis`? - At least having a ``degree_on_basis`` method? Is that - enough? + One should implement a ``degree_on_basis`` method in the parent + class in order to fully utilize the methods of this category. + This might become a required abstract method in the future. EXAMPLES:: @@ -102,6 +99,9 @@ class FilteredModulesWithBasis(FilteredModulesCategory): TESTS:: + sage: C = ModulesWithBasis(ZZ).Filtered() + sage: TestSuite(C).run() + sage: C = ModulesWithBasis(QQ).Filtered() sage: TestSuite(C).run() """ class ParentMethods: @@ -153,7 +153,9 @@ def basis(self, d=None): filtered module with basis: the free module on partitions over Integer Ring(i))_{i in Partitions} - Checking this method on a filtered algebra:: + Checking this method on a filtered algebra. Note that this + will typically raise an ``AttributeError`` when this feature + is not implemented. :: sage: A = AlgebrasWithBasis(ZZ).Filtered().example() sage: A.basis(4) @@ -161,15 +163,6 @@ def basis(self, d=None): ... AttributeError: 'IndexedFreeAbelianMonoid_with_category' object has no attribute 'subset' - .. TODO:: - - Oops! This doesn't work. This ``size=d`` thing seems very - frail to me. For how many families does it work, and - (more importantly) how many does it result in wrong - return values? What about leaving it to the instances to - define? (The ``basis`` method without extra parameters - should stay general, of course.) - Without arguments, the full basis is returned:: sage: A.basis() @@ -179,10 +172,13 @@ def basis(self, d=None): of RR^3 with cross product over Integer Ring(i))_{i in Free abelian monoid indexed by {'x', 'y', 'z'}} - .. TODO:: + An example with a graded algebra:: - Add doctests for some graded modules and algebras (which - seem to inherit this method). + sage: E. = ExteriorAlgebra(QQ) + sage: E.basis() + Lazy family (Term map from Subsets of {0, 1} to + The exterior algebra of rank 2 over Rational Field(i))_{i in + Subsets of {0, 1}} """ from sage.sets.family import Family if d is None: @@ -236,16 +232,6 @@ def graded_algebra(self): of this indexing set according to degree is the same as for ``self``). - .. TODO:: - - Maybe the thing about the conversion from ``self`` - to ``self.graded_algebra()`` on the Clifford at least - could be made to work? (I would still warn the user - against ASSUMING that it must work -- as there is - probably no way to guarantee it in all cases, and - we shouldn't require users to mess with - element constructors.) - EXAMPLES:: sage: A = ModulesWithBasis(ZZ).Filtered().example() @@ -333,7 +319,7 @@ def projection(self, i): EXAMPLES:: - sage: A = Modules(QQ).WithBasis().Filtered().example() + sage: A = Modules(ZZ).WithBasis().Filtered().example() sage: p = -2 * A.an_element(); p -4*P[] - 4*P[1] - 6*P[2] sage: q = A.projection(2)(p); q @@ -649,6 +635,21 @@ def is_homogeneous(self): return False return True + @abstract_method(optional=True) + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m`` + in ``self``. + + EXAMPLES:: + + sage: A = GradedModulesWithBasis(QQ).example() + sage: A.degree_on_basis(Partition((2,1))) + 3 + sage: A.degree_on_basis(Partition((4,2,1,1,1,1))) + 10 + """ + def homogeneous_degree(self): r""" The degree of a nonzero homogeneous element ``self`` in the diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 09c9333cefe..76744f1aabc 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -19,6 +19,7 @@ from sage.misc.lazy_import import LazyImport from sage.misc.cachefunc import cached_method from sage.misc.misc import attrcall +from sage.misc.abstract_method import abstract_method from sage.misc.sage_itertools import max_cmp, min_cmp from sage.categories.homsets import HomsetsCategory from sage.categories.cartesian_product import CartesianProductsCategory @@ -463,6 +464,19 @@ class ElementMethods: # """ # return self._lmul_(-self.parent().base_ring().one(), self) + @abstract_method + def support(self): + """ + Return the support of ``self``. + + EXAMPLES:: + + sage: C = CombinatorialFreeModule(QQ, ZZ) + sage: x = C.an_element(); x + 3*B[-1] + B[0] + 3*B[1] + sage: x.support() + [-1, 0, 1] + """ def support_of_term(self): """ diff --git a/src/sage/categories/super_modules_with_basis.py b/src/sage/categories/super_modules_with_basis.py index 64cfcc3da7b..fda2ab4abe5 100644 --- a/src/sage/categories/super_modules_with_basis.py +++ b/src/sage/categories/super_modules_with_basis.py @@ -19,8 +19,8 @@ class SuperModulesWithBasis(SuperModulesCategory): sage: C = GradedModulesWithBasis(ZZ); C Category of graded modules with basis over Integer Ring sage: sorted(C.super_categories(), key=str) - [Category of graded modules over Integer Ring, - Category of modules with basis over Integer Ring] + [Category of filtered modules with basis over Integer Ring, + Category of graded modules over Integer Ring] sage: C is ModulesWithBasis(ZZ).Graded() True From 9bc28fd5fdbcb5463703739a9077886a113596a5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 26 Mar 2015 23:33:19 -0700 Subject: [PATCH 046/421] Changing abstract methods and doc based off talking with Nicolas. --- src/sage/categories/modules_with_basis.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 76744f1aabc..e23bdb6b0f5 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -122,6 +122,12 @@ class ModulesWithBasis(CategoryWithAxiom_over_base_ring): .. TODO:: ``End(X)`` is an algebra. + .. NOTE:: + + This category currently requires an implementation of an + element method ``support``. Once :trac:`18066`, an implementation + of an ``items`` method will be required. + TESTS:: sage: TestSuite(ModulesWithBasis(ZZ)).run() @@ -464,20 +470,6 @@ class ElementMethods: # """ # return self._lmul_(-self.parent().base_ring().one(), self) - @abstract_method - def support(self): - """ - Return the support of ``self``. - - EXAMPLES:: - - sage: C = CombinatorialFreeModule(QQ, ZZ) - sage: x = C.an_element(); x - 3*B[-1] + B[0] + 3*B[1] - sage: x.support() - [-1, 0, 1] - """ - def support_of_term(self): """ Return the support of ``self``, where ``self`` is a monomial From 0ce1cad1332ec34909c4baa2761307a3bd205ef8 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 28 Mar 2015 16:46:44 -0700 Subject: [PATCH 047/421] Changing the wording of the Super method. --- src/sage/categories/modules.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 5b7325e6e18..13ad379c7a2 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -381,7 +381,7 @@ def Graded(self, base_ring=None): @cached_method def Super(self, base_ring=None): r""" - Return the subcategory of the super objects of ``self``. + Return the super-analogue category of ``self``. INPUT:: @@ -393,7 +393,8 @@ def Super(self, base_ring=None): Category of graded modules over Integer Ring sage: Coalgebras(QQ).Super() - Join of Category of graded modules over Rational Field and Category of coalgebras over Rational Field + Join of Category of graded modules over Rational Field + and Category of coalgebras over Rational Field sage: AlgebrasWithBasis(QQ).Super() Category of graded algebras with basis over Rational Field From b29650b87b103a465d66de189182a0e7dcf4fde9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 1 Apr 2015 23:23:47 -0700 Subject: [PATCH 048/421] Made it so you must explicitly designate graded as a super category --- src/sage/algebras/clifford_algebra.py | 3 +- .../categories/hopf_algebras_with_basis.py | 1 + src/sage/categories/super_algebras.py | 17 ++++++-- .../categories/super_algebras_with_basis.py | 15 ++++++- .../super_hopf_algebras_with_basis.py | 35 ++++++++++++++++ src/sage/categories/super_modules.py | 41 ++++++++----------- 6 files changed, 82 insertions(+), 30 deletions(-) create mode 100644 src/sage/categories/super_hopf_algebras_with_basis.py diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 8225292888f..9e02718b022 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -1412,7 +1412,8 @@ def __init__(self, R, names): sage: E. = ExteriorAlgebra(QQ) sage: TestSuite(E).run() """ - CliffordAlgebra.__init__(self, QuadraticForm(R, len(names)), names, HopfAlgebrasWithBasis(R).Super()) + cat = HopfAlgebrasWithBasis(R).Super().Graded() + CliffordAlgebra.__init__(self, QuadraticForm(R, len(names)), names, cat) # TestSuite will fail if the HopfAlgebra classes will ever have tests for # the coproduct being an algebra morphism -- since this is really a # Hopf superalgebra, not a Hopf algebra. diff --git a/src/sage/categories/hopf_algebras_with_basis.py b/src/sage/categories/hopf_algebras_with_basis.py index 005cb59dde6..e14cfe92722 100644 --- a/src/sage/categories/hopf_algebras_with_basis.py +++ b/src/sage/categories/hopf_algebras_with_basis.py @@ -146,6 +146,7 @@ def example(self, G = None): FiniteDimensional = LazyImport('sage.categories.finite_dimensional_hopf_algebras_with_basis', 'FiniteDimensionalHopfAlgebrasWithBasis') Graded = LazyImport('sage.categories.graded_hopf_algebras_with_basis', 'GradedHopfAlgebrasWithBasis') + Super = LazyImport('sage.categories.super_hopf_algebras_with_basis', 'SuperHopfAlgebrasWithBasis') class ParentMethods: diff --git a/src/sage/categories/super_algebras.py b/src/sage/categories/super_algebras.py index 355bf857758..ff6cc9a147e 100644 --- a/src/sage/categories/super_algebras.py +++ b/src/sage/categories/super_algebras.py @@ -9,6 +9,9 @@ #****************************************************************************** from sage.categories.super_modules import SuperModulesCategory +from sage.categories.algebras import Algebras +from sage.categories.modules import Modules +from sage.misc.lazy_import import LazyImport class SuperAlgebras(SuperModulesCategory): """ @@ -18,14 +21,22 @@ class SuperAlgebras(SuperModulesCategory): sage: Algebras(ZZ).Super() Category of super algebras over Integer Ring - sage: Algebras(ZZ).Super().super_categories() - [Category of graded algebras over Integer Ring, - Category of super modules over Integer Ring] TESTS:: sage: TestSuite(Algebras(ZZ).Super()).run() """ + def super_categories(self): + """ + EXAMPLES:: + + sage: Algebras(ZZ).Super().super_categories() + [Category of graded algebras over Integer Ring, + Category of super modules over Integer Ring] + """ + R = self.base_ring() + return [Algebras(R).Graded(), Modules(R).Super()] + class ParentMethods: pass diff --git a/src/sage/categories/super_algebras_with_basis.py b/src/sage/categories/super_algebras_with_basis.py index 9ba242c829f..1996e9aab73 100644 --- a/src/sage/categories/super_algebras_with_basis.py +++ b/src/sage/categories/super_algebras_with_basis.py @@ -9,6 +9,8 @@ #****************************************************************************** from sage.categories.super_modules import SuperModulesCategory +from sage.categories.algebras import Algebras +from sage.categories.modules import Modules class SuperAlgebrasWithBasis(SuperModulesCategory): """ @@ -20,13 +22,24 @@ class SuperAlgebrasWithBasis(SuperModulesCategory): Category of super algebras with basis over Integer Ring sage: sorted(C.super_categories(), key=str) [Category of graded algebras with basis over Integer Ring, - Category of super algebras over Integer Ring, Category of super modules with basis over Integer Ring] TESTS:: sage: TestSuite(C).run() """ + def super_categories(self): + """ + EXAMPLES:: + + sage: Algebras(ZZ).WithBasis().Super().super_categories() + [Category of graded algebras with basis over Integer Ring, + Category of super modules with basis over Integer Ring] + """ + R = self.base_ring() + return [Algebras(R).WithBasis().Graded(), + Modules(R).WithBasis().Super()] + class ParentMethods: pass diff --git a/src/sage/categories/super_hopf_algebras_with_basis.py b/src/sage/categories/super_hopf_algebras_with_basis.py new file mode 100644 index 00000000000..f1f2f1d8773 --- /dev/null +++ b/src/sage/categories/super_hopf_algebras_with_basis.py @@ -0,0 +1,35 @@ +r""" +Super algebras with basis +""" +#***************************************************************************** +# Copyright (C) 2015 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.categories.super_modules import SuperModulesCategory + +class SuperHopfAlgebrasWithBasis(SuperModulesCategory): + """ + The category of super algebras with a distinguished basis + + EXAMPLES:: + + sage: C = HopfAlgebras(ZZ).WithBasis().Super(); C + Category of super hopf algebras with basis over Integer Ring + sage: sorted(C.super_categories(), key=str) + [Category of hopf algebras with basis over Integer Ring, + Category of super algebras over Integer Ring, + Category of super algebras with basis over Integer Ring] + + TESTS:: + + sage: TestSuite(C).run() + """ + class ParentMethods: + pass + + class ElementMethods: + pass + diff --git a/src/sage/categories/super_modules.py b/src/sage/categories/super_modules.py index 154ca9f6168..46c170080cb 100644 --- a/src/sage/categories/super_modules.py +++ b/src/sage/categories/super_modules.py @@ -124,31 +124,6 @@ def _repr_object_names(self): """ return "super {}".format(self.base_category()._repr_object_names()) - @classmethod - def default_super_categories(cls, category, *args): - """ - Returns the default super categories of ``category.Subobjects()`` - - Mathematical meaning: if `A` is a super version of `B`, - then `A` is also a graded version of `B`. - - INPUT: - - - ``cls`` -- the class ``SubobjectsCategory`` - - ``category`` -- a category `Cat` - - OUTPUT: a (join) category - - In practice, this returns ``category.Subquotients()``, joined - together with the result of the method - :meth:`RegressiveCovariantConstructionCategory.default_super_categories() ` - (that is the join of ``category`` and ``cat.Subobjects()`` for - each ``cat`` in the super categories of ``category``). - - EXAMPLES:: - """ - return Category.join([category.Graded(), super(SuperModulesCategory, cls).default_super_categories(category, *args)]) - class SuperModules(SuperModulesCategory): """ The category of super modules. @@ -170,6 +145,22 @@ class SuperModules(SuperModulesCategory): sage: TestSuite(Modules(ZZ).Super()).run() """ + def super_categories(self): + """ + EXAMPLES:: + + sage: Modules(ZZ).Super().super_categories() + [Category of graded modules over Integer Ring] + + Nota bene:: + + sage: Modules(QQ).Super() + Category of super modules over Rational Field + sage: Modules(QQ).Super().super_categories() + [Category of graded modules over Rational Field] + """ + return [Modules(self.base_ring()).Graded()] + def extra_super_categories(self): r""" Adds :class:`VectorSpaces` to the super categories of ``self`` if From 23e2b43f9f7112d3c4cfe4f22712db883f56b3d2 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 3 Apr 2015 22:32:41 +0000 Subject: [PATCH 049/421] Fixed bug in guessing code and slightly modified guessing algorithm to favor Schensted --- src/sage/combinat/skew_tableau.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 23de6091f15..849dc2829bb 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -774,8 +774,8 @@ def rectify(self, force=None): INPUT: - ``force`` -- optional: if set to ``'jdt'``, rectifies by jeu de taquin; - if set to ``'schensted'``, rectifies by Schensted insertion of the - reading word; otherwise, guesses which will be faster. + if set to ``'schensted'``, rectifies by Schensted insertion of the + reading word; otherwise, guesses which will be faster. EXAMPLES:: @@ -797,7 +797,7 @@ def rectify(self, force=None): labda = self.outer_shape() musize = self.inner_shape().size() labdasize = labda.size() - if force == 'jdt' or (force != 'schensted' and musize < len(labda) * (labdasize - musize)**(1/2)): + if force == 'jdt' or (force != 'schensted' and musize**2 < len(labda) * (labdasize - musize)): rect = self inner_corners = rect.inner_shape().corners() while len(inner_corners) > 0: From ab4a84d739f7616a89a324c4723b7233cf5beb3c Mon Sep 17 00:00:00 2001 From: Josh Swanson Date: Wed, 8 Apr 2015 21:39:38 -0700 Subject: [PATCH 050/421] Renamed 'force' to 'algorithm'; cleaned up code --- src/sage/combinat/skew_tableau.py | 56 ++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 849dc2829bb..81f583281aa 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -685,8 +685,8 @@ def to_chain(self, max_entry=None): def slide(self, corner=None): """ - Apply a jeu-de-taquin slide to ``self`` on the specified corner and - returns the new tableau. If no corner is given an arbitrary corner + Apply a jeu-de-taquin slide to ``self`` on the specified inner corner and + returns the new tableau. If no corner is given an arbitrary inner corner is chosen. See [FW]_ p12-13. @@ -761,9 +761,10 @@ def slide(self, corner=None): return SkewTableau(new_st) - def rectify(self, force=None): + def rectify(self, algorithm=None): """ - Return a :class:`Tableau` formed by applying the jeu de taquin + Return a :class:`StandardTableau`, :class:`SemistandardTableau`, + or just :class:`Tableau` formed by applying the jeu de taquin process to ``self``. See page 15 of [FW]_. REFERENCES: @@ -773,39 +774,54 @@ def rectify(self, force=None): Cambridge University Press 1997. INPUT: - - ``force`` -- optional: if set to ``'jdt'``, rectifies by jeu de taquin; + - ``algorithm`` -- optional: if set to ``'jdt'``, rectifies by jeu de taquin; if set to ``'schensted'``, rectifies by Schensted insertion of the reading word; otherwise, guesses which will be faster. EXAMPLES:: - sage: s = SkewTableau([[None,1],[2,3]]) - sage: s.rectify() + sage: S = SkewTableau([[None,1],[2,3]]) + sage: S.rectify() [[1, 3], [2]] - sage: SkewTableau([[None, None, None, 4],[None,None,1,6],[None,None,5],[2,3]]).rectify() + sage: T = SkewTableau([[None, None, None, 4],[None,None,1,6],[None,None,5],[2,3]]) + sage: T.rectify() [[1, 3, 4, 6], [2, 5]] - sage: SkewTableau([[None, None, None, 4],[None,None,1,6],[None,None,5],[2,3]]).rectify('jdt') + sage: T.rectify(algorithm='jdt') [[1, 3, 4, 6], [2, 5]] - sage: SkewTableau([[None, None, None, 4],[None,None,1,6],[None,None,5],[2,3]]).rectify('schensted') + sage: T.rectify(algorithm='schensted') [[1, 3, 4, 6], [2, 5]] + sage: T.rectify(algorithm='spaghetti') + Traceback (most recent call last): + ... + ValueError: algorithm must be 'jdt', 'schensted', or None TESTS:: - sage: s + sage: S [[None, 1], [2, 3]] + sage: T + [[None, None, None, 4], [None, None, 1, 6], [None, None, 5], [2, 3]] """ - labda = self.outer_shape() - musize = self.inner_shape().size() - labdasize = labda.size() - if force == 'jdt' or (force != 'schensted' and musize**2 < len(labda) * (labdasize - musize)): + la = self.outer_shape() + mu_size = self.inner_shape().size() + la_size = la.size() + + # Roughly, use jdt with a small inner shape, Schensted with a large one + if algorithm is None: + if mu_size^2 < len(la) * (la_size - mu_size): + algorithm = 'jdt' + else: + algorithm = 'schensted' + + if algorithm == 'jdt': rect = self - inner_corners = rect.inner_shape().corners() - while len(inner_corners) > 0: + for i in range(mu_size): rect = rect.slide() - inner_corners = rect.inner_shape().corners() + elif algorithm == 'schensted': + rect = Tableau([]).insert_word(self.to_word()) else: - w = self.to_word() - rect = Tableau([]).insert_word(w) + raise ValueError("algorithm must be 'jdt', 'schensted', or None") + if self in SemistandardSkewTableaux(): return SemistandardTableau(rect[:]) if self in StandardSkewTableaux(): From 9785351a4006977fb21a65f25c6245e77f7acb4a Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 10 Apr 2015 21:09:40 +0000 Subject: [PATCH 051/421] moved definitions to where used; reordered type checking --- src/sage/combinat/skew_tableau.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 81f583281aa..05d306980bf 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -802,12 +802,12 @@ def rectify(self, algorithm=None): sage: T [[None, None, None, 4], [None, None, 1, 6], [None, None, 5], [2, 3]] """ - la = self.outer_shape() mu_size = self.inner_shape().size() - la_size = la.size() # Roughly, use jdt with a small inner shape, Schensted with a large one if algorithm is None: + la = self.outer_shape() + la_size = la.size() if mu_size^2 < len(la) * (la_size - mu_size): algorithm = 'jdt' else: @@ -821,11 +821,10 @@ def rectify(self, algorithm=None): rect = Tableau([]).insert_word(self.to_word()) else: raise ValueError("algorithm must be 'jdt', 'schensted', or None") - - if self in SemistandardSkewTableaux(): - return SemistandardTableau(rect[:]) if self in StandardSkewTableaux(): return StandardTableau(rect[:]) + if self in SemistandardSkewTableaux(): + return SemistandardTableau(rect[:]) return Tableau(rect) def standardization(self, check=True): From 710d09e3f75ce2fbc95d2f67ba8dbc12bf666c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Sun, 12 Apr 2015 17:48:06 -0400 Subject: [PATCH 052/421] 18174: more doc improvements + uniformization with CategoryWithAxiom --- .../covariant_functorial_construction.py | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/covariant_functorial_construction.py b/src/sage/categories/covariant_functorial_construction.py index 284fd5ca064..cc2ea36216e 100644 --- a/src/sage/categories/covariant_functorial_construction.py +++ b/src/sage/categories/covariant_functorial_construction.py @@ -43,6 +43,7 @@ #****************************************************************************** from sage.misc.cachefunc import cached_function, cached_method from sage.misc.lazy_attribute import lazy_class_attribute +from sage.misc.lazy_import import LazyImport from sage.categories.category import Category from sage.structure.sage_object import SageObject from sage.structure.unique_representation import UniqueRepresentation @@ -317,22 +318,37 @@ def __classget__(cls, base_category, base_category_class): ``Category``, even if it has been overriden by a ``Subquotients`` class. - TESTS:: + EXAMPLES:: sage: Sets.Subquotients sage: Sets().Subquotients Cached version of + This method also initializes the attribute + ``_base_category_class`` if not already set:: + + sage: Sets.Subquotients._base_category_class + (,) + + It also forces the resolution of lazy imports (see :trac:`15648`):: + + sage: type(Algebras.__dict__["Graded"]) + + sage: Algebras.Graded + + sage: type(Algebras.__dict__["Graded"]) + + .. TODO:: The logic is very similar to that implemented in :class:`CategoryWithAxiom.__classget__`. Find a way to refactor this to avoid the duplication. """ - if base_category is None: - return cls - + if base_category is not None: + assert base_category.__class__ is base_category_class + assert isinstance(base_category_class, DynamicMetaclass) if isinstance(base_category_class, DynamicMetaclass): base_category_class = base_category_class.__base__ if "_base_category_class" not in cls.__dict__: @@ -342,6 +358,13 @@ def __classget__(cls, base_category, base_category_class): "base category class for {} mismatch; expected {}, got {}".format( cls, cls._base_category_class[0], base_category_class) + # Workaround #15648: if Sets.Subquotients is a LazyImport object, + # this forces the substitution of the object back into Sets + # to avoid resolving the lazy import over and over + if isinstance(base_category_class.__dict__[cls._functor_category], LazyImport): + setattr(base_category_class, cls._functor_category, cls) + if base_category is None: + return cls return getattr(super(base_category.__class__.__base__, base_category), cls._functor_category) From 19c3471ffe0277979904e465d6867d31d284d969 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 13 Apr 2015 10:28:41 -0400 Subject: [PATCH 053/421] Cleanup from adding #18044. --- src/sage/categories/super_modules.py | 76 ---------------------------- 1 file changed, 76 deletions(-) diff --git a/src/sage/categories/super_modules.py b/src/sage/categories/super_modules.py index 46c170080cb..60f94965d86 100644 --- a/src/sage/categories/super_modules.py +++ b/src/sage/categories/super_modules.py @@ -39,82 +39,6 @@ def __init__(self, base_category): _functor_category = "Super" - @lazy_class_attribute - def _base_category_class(cls): - """ - Recover the class of the base category. - - OUTPUT: - - A *tuple* whose first entry is the base category class. - - .. WARNING:: - - This is only used for super categories that are not - implemented as nested classes, and won't work otherwise. - - .. SEEALSO:: :meth:`__classcall__` - - EXAMPLES:: - - sage: from sage.categories.super_modules import SuperModules - sage: from sage.categories.super_algebras_with_basis import SuperAlgebrasWithBasis - sage: SuperModules._base_category_class - (,) - sage: SuperAlgebrasWithBasis._base_category_class - (,) - - The reason for wrapping the base category class in a tuple is - that, often, the base category class implements a - :meth:`__classget__` method which would get in the way upon - attribute access:: - - sage: F = SuperAlgebrasWithBasis - sage: F._foo = F._base_category_class[0] - sage: F._foo - Traceback (most recent call last): - ... - AssertionError: base category class for <...AlgebrasWithBasis'> mismatch; - expected <...Algebras'>, got <...SuperAlgebrasWithBasis'> - """ - module_name = cls.__module__.replace("super_","") - import sys - name = cls.__name__.replace("Super","") - __import__(module_name) - module = sys.modules[module_name] - return (module.__dict__[name],) - - @staticmethod - def __classcall__(cls, category, *args): - """ - Magic support for putting Super categories in their own file. - - EXAMPLES:: - - sage: from sage.categories.super_modules import SuperModules - sage: SuperModules(ZZ) # indirect doctest - Category of super modules over Integer Ring - sage: Modules(ZZ).Super() - Category of super modules over Integer Ring - sage: SuperModules(ZZ) is Modules(ZZ).Super() - True - - .. TODO:: - - Generalize this support for all other functorial - constructions if at some point we have a category ``Blah`` for - which we want to implement the construction ``Blah.Foo`` in a - separate file like we do for e.g. :class:`SuperModules`, - :class:`SuperAlgebras`, ... - - .. SEEALSO:: :meth:`_base_category_class` - """ - base_category_class = cls._base_category_class[0] - if isinstance(category, base_category_class): - return super(SuperModulesCategory, cls).__classcall__(cls, category, *args) - else: - return base_category_class(category, *args).Super() - def _repr_object_names(self): """ EXAMPLES:: From 7554dbda3721d66fb582ab3bf22c1d3c577b7934 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 14 Apr 2015 17:19:15 -0400 Subject: [PATCH 054/421] 1 step forward, 2 steps back. --- src/sage/algebras/clifford_algebra.py | 9 +++- src/sage/algebras/weyl_algebra.py | 3 +- src/sage/categories/super_algebras.py | 45 ------------------- .../categories/super_algebras_with_basis.py | 12 +---- .../super_hopf_algebras_with_basis.py | 7 +-- src/sage/categories/super_modules.py | 37 +++++++++++++-- 6 files changed, 46 insertions(+), 67 deletions(-) delete mode 100644 src/sage/categories/super_algebras.py diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 9e02718b022..a9dc9318131 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -478,6 +478,11 @@ class CliffordAlgebra(CombinatorialFreeModule): a*d sage: d*c*b*a + a + 4*b*c a*b*c*d + 4*b*c + a + + .. WARNING:: + + The Clifford algebra is not graded, but instead filtered. This + will be changed once :trac:`17096` is finished. """ @staticmethod def __classcall_private__(cls, Q, names=None): @@ -1044,7 +1049,7 @@ def lift_module_morphism(self, m, names=None): remove_zeros=True ) for i in x) return Cl.module_morphism(on_basis=f, codomain=self, - category=AlgebrasWithBasis(self.base_ring()).Graded()) + category=AlgebrasWithBasis(self.base_ring()).Super()) def lift_isometry(self, m, names=None): r""" @@ -1412,7 +1417,7 @@ def __init__(self, R, names): sage: E. = ExteriorAlgebra(QQ) sage: TestSuite(E).run() """ - cat = HopfAlgebrasWithBasis(R).Super().Graded() + cat = HopfAlgebrasWithBasis(R).Super() CliffordAlgebra.__init__(self, QuadraticForm(R, len(names)), names, cat) # TestSuite will fail if the HopfAlgebra classes will ever have tests for # the coproduct being an algebra morphism -- since this is really a diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index 1c8705f77d9..99fd7bf7f46 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -569,7 +569,8 @@ def __init__(self, R, names=None): raise ValueError("variable names cannot differ by a leading 'd'") # TODO: Make this into a filtered algebra under the natural grading of # x_i and dx_i have degree 1 - Algebra.__init__(self, R, names, category=AlgebrasWithBasis(R).NoZeroDivisors()) + cat = AlgebrasWithBasis(R).NoZeroDivisors().Super() + Algebra.__init__(self, R, names, category=cat) def _repr_(self): r""" diff --git a/src/sage/categories/super_algebras.py b/src/sage/categories/super_algebras.py deleted file mode 100644 index ff6cc9a147e..00000000000 --- a/src/sage/categories/super_algebras.py +++ /dev/null @@ -1,45 +0,0 @@ -r""" -Super Algebras -""" -#***************************************************************************** -# Copyright (C) 2015 Travis Scrimshaw -# -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** - -from sage.categories.super_modules import SuperModulesCategory -from sage.categories.algebras import Algebras -from sage.categories.modules import Modules -from sage.misc.lazy_import import LazyImport - -class SuperAlgebras(SuperModulesCategory): - """ - The category of super algebras. - - EXAMPLES:: - - sage: Algebras(ZZ).Super() - Category of super algebras over Integer Ring - - TESTS:: - - sage: TestSuite(Algebras(ZZ).Super()).run() - """ - def super_categories(self): - """ - EXAMPLES:: - - sage: Algebras(ZZ).Super().super_categories() - [Category of graded algebras over Integer Ring, - Category of super modules over Integer Ring] - """ - R = self.base_ring() - return [Algebras(R).Graded(), Modules(R).Super()] - - class ParentMethods: - pass - - class ElementMethods: - pass - diff --git a/src/sage/categories/super_algebras_with_basis.py b/src/sage/categories/super_algebras_with_basis.py index 1996e9aab73..ce9a8d266ff 100644 --- a/src/sage/categories/super_algebras_with_basis.py +++ b/src/sage/categories/super_algebras_with_basis.py @@ -28,7 +28,7 @@ class SuperAlgebrasWithBasis(SuperModulesCategory): sage: TestSuite(C).run() """ - def super_categories(self): + def extra_super_categories(self): """ EXAMPLES:: @@ -36,13 +36,5 @@ def super_categories(self): [Category of graded algebras with basis over Integer Ring, Category of super modules with basis over Integer Ring] """ - R = self.base_ring() - return [Algebras(R).WithBasis().Graded(), - Modules(R).WithBasis().Super()] - - class ParentMethods: - pass - - class ElementMethods: - pass + return [self.base_category().Graded()] diff --git a/src/sage/categories/super_hopf_algebras_with_basis.py b/src/sage/categories/super_hopf_algebras_with_basis.py index f1f2f1d8773..2de32bb0890 100644 --- a/src/sage/categories/super_hopf_algebras_with_basis.py +++ b/src/sage/categories/super_hopf_algebras_with_basis.py @@ -1,5 +1,5 @@ r""" -Super algebras with basis +Super Hopf algebras with basis """ #***************************************************************************** # Copyright (C) 2015 Travis Scrimshaw @@ -27,9 +27,4 @@ class SuperHopfAlgebrasWithBasis(SuperModulesCategory): sage: TestSuite(C).run() """ - class ParentMethods: - pass - - class ElementMethods: - pass diff --git a/src/sage/categories/super_modules.py b/src/sage/categories/super_modules.py index 60f94965d86..0165a18adf8 100644 --- a/src/sage/categories/super_modules.py +++ b/src/sage/categories/super_modules.py @@ -13,10 +13,41 @@ from sage.categories.category import Category from sage.categories.category_types import Category_over_base_ring from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring -from sage.categories.covariant_functorial_construction import RegressiveCovariantConstructionCategory +from sage.categories.covariant_functorial_construction import CovariantConstructionCategory from sage.categories.modules import Modules -class SuperModulesCategory(RegressiveCovariantConstructionCategory, Category_over_base_ring): +axiom_whitelist = frozenset(["Facade", "Finite", "Infinite", + "FiniteDimensional", "Connected", "WithBasis", + # "Commutative", + "Associative", "Inverse", "Unital", "Division", + "AdditiveCommutative", "AdditiveAssociative", + "AdditiveInverse", "AdditiveUnital", + "NoZeroDivisors", "Distributive"]) + +class SuperModulesCategory(CovariantConstructionCategory, Category_over_base_ring): + @classmethod + def default_super_categories(cls, category, *args): + """ + Return the default super categories of `F_{Cat}(A,B,...)` for + `A,B,...` parents in `Cat`. + + INPUT: + + - ``cls`` -- the category class for the functor `F` + - ``category`` -- a category `Cat` + - ``*args`` -- further arguments for the functor + + OUTPUT: + + A join category. + + This implements the property that subcategories constructed by + the set of whitelisted axioms is a subcategory. + """ + axioms = axiom_whitelist.intersection(category.axioms()) + C = super(SuperModulesCategory, cls).default_super_categories(category, *args) + return C._with_axioms(axioms) + def __init__(self, base_category): """ EXAMPLES:: @@ -83,7 +114,7 @@ def super_categories(self): sage: Modules(QQ).Super().super_categories() [Category of graded modules over Rational Field] """ - return [Modules(self.base_ring()).Graded()] + return [self.base_category().Graded()] def extra_super_categories(self): r""" From caada136601a6af26a7c66ec417a0180b00b1812 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 14 Apr 2015 17:59:58 -0400 Subject: [PATCH 055/421] Going all the way to the finish line like a superstar. --- src/sage/categories/bialgebras.py | 5 +-- src/sage/categories/coalgebras.py | 28 +++++++------- src/sage/categories/coalgebras_with_basis.py | 3 ++ src/sage/categories/hopf_algebras.py | 3 ++ src/sage/categories/modules.py | 22 ++++++++--- src/sage/categories/super_algebras.py | 38 +++++++++++++++++++ .../categories/super_algebras_with_basis.py | 7 ++-- .../super_hopf_algebras_with_basis.py | 6 +-- src/sage/categories/super_modules.py | 5 +++ .../categories/super_modules_with_basis.py | 9 +++++ 10 files changed, 97 insertions(+), 29 deletions(-) create mode 100644 src/sage/categories/super_algebras.py diff --git a/src/sage/categories/bialgebras.py b/src/sage/categories/bialgebras.py index e78f080ef18..e779d8acb08 100644 --- a/src/sage/categories/bialgebras.py +++ b/src/sage/categories/bialgebras.py @@ -11,6 +11,7 @@ from sage.categories.category_types import Category_over_base_ring from sage.categories.all import Algebras, Coalgebras +from sage.categories.super_modules import SuperModulesCategory class Bialgebras(Category_over_base_ring): """ @@ -56,8 +57,6 @@ def additional_structure(self): """ return None - class ParentMethods: + class Super(SuperModulesCategory): pass - class ElementMethods: - pass diff --git a/src/sage/categories/coalgebras.py b/src/sage/categories/coalgebras.py index 2bd98353394..2b2cc74043a 100644 --- a/src/sage/categories/coalgebras.py +++ b/src/sage/categories/coalgebras.py @@ -13,6 +13,7 @@ from sage.categories.all import Modules from sage.categories.tensor import TensorProductsCategory, tensor from sage.categories.dual import DualObjectsCategory +from sage.categories.super_modules import SuperModulesCategory from sage.categories.realizations import RealizationsCategory from sage.categories.with_realizations import WithRealizationsCategory from sage.misc.abstract_method import abstract_method @@ -50,19 +51,6 @@ class ParentMethods: # # Will declare the coproduct of self to the coercion mechanism when it exists # pass - @cached_method - def tensor_square(self): - """ - Returns the tensor square of ``self`` - - EXAMPLES:: - - sage: A = HopfAlgebrasWithBasis(QQ).example() - sage: A.tensor_square() - An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field # An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field - """ - return tensor([self, self]) - @abstract_method def counit(self, x): """ @@ -192,6 +180,20 @@ def extra_super_categories(self): from sage.categories.algebras import Algebras return [Algebras(self.base_category().base_ring())] + class Super(SuperModulesCategory): + def extra_super_categories(self): + """ + EXAMPLES:: + + sage: Coalgebras(ZZ).Super().extra_super_categories() + [Join of Category of graded modules over Integer Ring + and Category of coalgebras over Integer Ring] + sage: Coalgebras(ZZ).Super().super_categories() + [Category of super modules over Integer Ring, + Category of coalgebras over Integer Ring] + """ + return [self.base_category().Graded()] + class WithRealizations(WithRealizationsCategory): class ParentMethods: diff --git a/src/sage/categories/coalgebras_with_basis.py b/src/sage/categories/coalgebras_with_basis.py index 0b9ff24f0ec..ddcaad16d26 100644 --- a/src/sage/categories/coalgebras_with_basis.py +++ b/src/sage/categories/coalgebras_with_basis.py @@ -13,6 +13,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring from sage.categories.all import ModulesWithBasis, tensor, Hom +from sage.categories.super_modules import SuperModulesCategory class CoalgebrasWithBasis(CategoryWithAxiom_over_base_ring): """ @@ -129,3 +130,5 @@ def counit(self): class ElementMethods: pass + class Super(SuperModulesCategory): + pass diff --git a/src/sage/categories/hopf_algebras.py b/src/sage/categories/hopf_algebras.py index eba5add5d9a..a28911b4448 100644 --- a/src/sage/categories/hopf_algebras.py +++ b/src/sage/categories/hopf_algebras.py @@ -15,6 +15,7 @@ from sage.categories.bialgebras import Bialgebras from sage.categories.tensor import TensorProductsCategory # tensor from sage.categories.realizations import RealizationsCategory +from sage.categories.super_modules import SuperModulesCategory from sage.misc.cachefunc import cached_method #from sage.misc.lazy_attribute import lazy_attribute @@ -104,6 +105,8 @@ class Morphism(Category): """ pass + class Super(SuperModulesCategory): + pass class TensorProducts(TensorProductsCategory): """ diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 13ad379c7a2..4147ea6b622 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -17,7 +17,7 @@ from sage.categories.homsets import HomsetsCategory from category import Category, JoinCategory from category_types import Category_module, Category_over_base_ring -from tensor import TensorProductsCategory +from sage.categories.tensor import TensorProductsCategory, tensor from dual import DualObjectsCategory from sage.categories.sets_cat import Sets from sage.categories.bimodules import Bimodules @@ -390,14 +390,13 @@ def Super(self, base_ring=None): EXAMPLES:: sage: Modules(ZZ).Super() - Category of graded modules over Integer Ring + Category of super modules over Integer Ring sage: Coalgebras(QQ).Super() - Join of Category of graded modules over Rational Field - and Category of coalgebras over Rational Field + Category of super coalgebras over Rational Field sage: AlgebrasWithBasis(QQ).Super() - Category of graded algebras with basis over Rational Field + Category of super algebras with basis over Rational Field .. TODO:: @@ -463,7 +462,18 @@ def extra_super_categories(self): WithBasis = LazyImport('sage.categories.modules_with_basis', 'ModulesWithBasis') class ParentMethods: - pass + @cached_method + def tensor_square(self): + """ + Returns the tensor square of ``self`` + + EXAMPLES:: + + sage: A = HopfAlgebrasWithBasis(QQ).example() + sage: A.tensor_square() + An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field # An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field + """ + return tensor([self, self]) class ElementMethods: diff --git a/src/sage/categories/super_algebras.py b/src/sage/categories/super_algebras.py new file mode 100644 index 00000000000..4fd9583c206 --- /dev/null +++ b/src/sage/categories/super_algebras.py @@ -0,0 +1,38 @@ +r""" +Super Algebras +""" +#***************************************************************************** +# Copyright (C) 2015 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.categories.super_modules import SuperModulesCategory +from sage.categories.algebras import Algebras +from sage.categories.modules import Modules +from sage.misc.lazy_import import LazyImport + +class SuperAlgebras(SuperModulesCategory): + """ + The category of super algebras. + + EXAMPLES:: + + sage: Algebras(ZZ).Super() + Category of super algebras over Integer Ring + + TESTS:: + + sage: TestSuite(Algebras(ZZ).Super()).run() + """ + def extra_super_categories(self): + """ + EXAMPLES:: + + sage: Algebras(ZZ).Super().super_categories() + [Category of graded algebras over Integer Ring, + Category of super modules over Integer Ring] + """ + return [self.base_category().Graded()] + diff --git a/src/sage/categories/super_algebras_with_basis.py b/src/sage/categories/super_algebras_with_basis.py index ce9a8d266ff..51b2b72afe8 100644 --- a/src/sage/categories/super_algebras_with_basis.py +++ b/src/sage/categories/super_algebras_with_basis.py @@ -20,9 +20,6 @@ class SuperAlgebrasWithBasis(SuperModulesCategory): sage: C = Algebras(ZZ).WithBasis().Super(); C Category of super algebras with basis over Integer Ring - sage: sorted(C.super_categories(), key=str) - [Category of graded algebras with basis over Integer Ring, - Category of super modules with basis over Integer Ring] TESTS:: @@ -32,8 +29,10 @@ def extra_super_categories(self): """ EXAMPLES:: - sage: Algebras(ZZ).WithBasis().Super().super_categories() + sage: C = Algebras(ZZ).WithBasis().Super() + sage: sorted(C.super_categories(), key=str) # indirect doctest [Category of graded algebras with basis over Integer Ring, + Category of super algebras over Integer Ring, Category of super modules with basis over Integer Ring] """ return [self.base_category().Graded()] diff --git a/src/sage/categories/super_hopf_algebras_with_basis.py b/src/sage/categories/super_hopf_algebras_with_basis.py index 2de32bb0890..58e133915b6 100644 --- a/src/sage/categories/super_hopf_algebras_with_basis.py +++ b/src/sage/categories/super_hopf_algebras_with_basis.py @@ -19,9 +19,9 @@ class SuperHopfAlgebrasWithBasis(SuperModulesCategory): sage: C = HopfAlgebras(ZZ).WithBasis().Super(); C Category of super hopf algebras with basis over Integer Ring sage: sorted(C.super_categories(), key=str) - [Category of hopf algebras with basis over Integer Ring, - Category of super algebras over Integer Ring, - Category of super algebras with basis over Integer Ring] + [Category of super algebras with basis over Integer Ring, + Category of super coalgebras with basis over Integer Ring, + Category of super hopf algebras over Integer Ring] TESTS:: diff --git a/src/sage/categories/super_modules.py b/src/sage/categories/super_modules.py index 0165a18adf8..b93883e4139 100644 --- a/src/sage/categories/super_modules.py +++ b/src/sage/categories/super_modules.py @@ -43,6 +43,11 @@ def default_super_categories(cls, category, *args): This implements the property that subcategories constructed by the set of whitelisted axioms is a subcategory. + + EXAMPLES:: + + sage: HopfAlgebras(ZZ).WithBasis().FiniteDimensional().Super() # indirect doctest + Category of finite dimensional super hopf algebras with basis over Integer Ring """ axioms = axiom_whitelist.intersection(category.axioms()) C = super(SuperModulesCategory, cls).default_super_categories(category, *args) diff --git a/src/sage/categories/super_modules_with_basis.py b/src/sage/categories/super_modules_with_basis.py index 64cfcc3da7b..e6930e620e9 100644 --- a/src/sage/categories/super_modules_with_basis.py +++ b/src/sage/categories/super_modules_with_basis.py @@ -37,6 +37,15 @@ def _even_odd_on_basis(self, m): ``0`` if ``m`` is for an even element or ``1`` if ``m`` is for an odd element. + + EXAMPLES:: + + sage: Q = QuadraticForm(QQ, 2, [1,2,3]) + sage: C. = CliffordAlgebra(Q) + sage: C._even_odd_on_basis((0,)) + 1 + sage: C._even_odd_on_basis((0,1)) + 0 """ return self.degree_on_basis(m) % 2 From 1404ad0e2336eee05877e3e1a434809d437a998d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 14 Apr 2015 18:10:00 -0400 Subject: [PATCH 056/421] Cleanup from merged code. --- src/sage/algebras/weyl_algebra.py | 2 +- src/sage/categories/filtered_algebras.py | 3 - src/sage/categories/filtered_modules.py | 83 ------------------------ src/sage/categories/graded_modules.py | 3 +- 4 files changed, 2 insertions(+), 89 deletions(-) diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index d074d1dedfb..0400ba7fc37 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -589,7 +589,7 @@ def __init__(self, R, names=None): names = names + tuple('d' + n for n in names) if len(names) != self._n * 2: raise ValueError("variable names cannot differ by a leading 'd'") - cat = AlgebrasWithBasis(R.category()).NoZeroDivisors().Super().Filtered() + cat = AlgebrasWithBasis(R.category()).NoZeroDivisors().Super() Algebra.__init__(self, R, names, category=cat) def _repr_(self): diff --git a/src/sage/categories/filtered_algebras.py b/src/sage/categories/filtered_algebras.py index c9444680f0a..321dd604ed3 100644 --- a/src/sage/categories/filtered_algebras.py +++ b/src/sage/categories/filtered_algebras.py @@ -60,6 +60,3 @@ def graded_algebra(self): Lie algebra of RR^3 with cross product over Integer Ring """ - class ElementMethods: - pass - diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index 7ba8bd51a7c..8a9a79d6c08 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -57,82 +57,6 @@ def __init__(self, base_category): _functor_category = "Filtered" - @lazy_class_attribute - def _base_category_class(cls): - """ - Recover the class of the base category. - - OUTPUT: - - A *tuple* whose first entry is the base category class. - - .. WARNING:: - - This is only used for filtered categories that are not - implemented as nested classes, and won't work otherwise. - - .. SEEALSO:: :meth:`__classcall__` - - EXAMPLES:: - - sage: from sage.categories.filtered_modules import FilteredModules - sage: FilteredModules._base_category_class - (,) - sage: from sage.categories.filtered_algebras_with_basis import FilteredAlgebrasWithBasis - sage: FilteredAlgebrasWithBasis._base_category_class - (,) - - The reason for wrapping the base category class in a tuple is - that, often, the base category class implements a - :meth:`__classget__` method which would get in the way upon - attribute access:: - - sage: F = FilteredAlgebrasWithBasis - sage: F._foo = F._base_category_class[0] - sage: F._foo - Traceback (most recent call last): - ... - AssertionError: base category class for <...AlgebrasWithBasis'> mismatch; - expected <...Algebras'>, got <...FilteredAlgebrasWithBasis'> - """ - module_name = cls.__module__.replace("filtered_","") - import sys - name = cls.__name__.replace("Filtered","") - __import__(module_name) - module = sys.modules[module_name] - return (module.__dict__[name],) - - @staticmethod - def __classcall__(cls, category, *args): - """ - Magic support for putting Filtered categories in their own file. - - EXAMPLES:: - - sage: from sage.categories.filtered_modules import FilteredModules - sage: FilteredModules(ZZ) # indirect doctest - Category of filtered modules over Integer Ring - sage: Modules(ZZ).Filtered() - Category of filtered modules over Integer Ring - sage: FilteredModules(ZZ) is Modules(ZZ).Filtered() - True - - .. TODO:: - - Generalize this support for all other functorial - constructions if at some point we have a category ``Blah`` for - which we want to implement the construction ``Blah.Foo`` in a - separate file like we do for e.g. :class:`FilteredModules`, - :class:`FilteredAlgebras`, ... - - .. SEEALSO:: :meth:`_base_category_class` - """ - base_category_class = cls._base_category_class[0] - if isinstance(category, base_category_class): - return super(FilteredModulesCategory, cls).__classcall__(cls, category, *args) - else: - return base_category_class(category, *args).Filtered() - def _repr_object_names(self): """ EXAMPLES:: @@ -168,7 +92,6 @@ class FilteredModules(FilteredModulesCategory): - :wikipedia:`Filtration_(mathematics)` """ - def extra_super_categories(self): r""" Add :class:`VectorSpaces` to the super categories of ``self`` if @@ -236,9 +159,3 @@ def Connected(self): class Connected(CategoryWithAxiom_over_base_ring): pass - class ParentMethods: - pass - - class ElementMethods: - pass - diff --git a/src/sage/categories/graded_modules.py b/src/sage/categories/graded_modules.py index 311c986215d..ca31efe005b 100644 --- a/src/sage/categories/graded_modules.py +++ b/src/sage/categories/graded_modules.py @@ -96,8 +96,7 @@ def default_super_categories(cls, category, *args): and Category of graded modules over Rational Field """ cat = super(GradedModulesCategory, cls).default_super_categories(category, *args) - return Category.join([category.Filtered(), - cat]) + return Category.join([category.Filtered(), cat]) class GradedModules(GradedModulesCategory): r""" From 60518658bf6575db0a12a7fce0b863dffea8d9cb Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Thu, 16 Apr 2015 07:38:51 +0200 Subject: [PATCH 057/421] add a doctest which hopefully works (hard to check while compiling) --- src/sage/algebras/weyl_algebra.py | 5 ++++- src/sage/categories/coalgebras.py | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index 1f4ba9dac8e..6983a2bac6a 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -567,7 +567,10 @@ def __init__(self, R, names=None): raise ValueError("variable names cannot differ by a leading 'd'") # TODO: Make this into a filtered algebra under the natural grading of # x_i and dx_i have degree 1 - cat = AlgebrasWithBasis(R).NoZeroDivisors().Super() + if R.is_field(): + cat = AlgebrasWithBasis(R).NoZeroDivisors().Super() + else: + cat = AlgebrasWithBasis(R).Super() Algebra.__init__(self, R, names, category=cat) def _repr_(self): diff --git a/src/sage/categories/coalgebras.py b/src/sage/categories/coalgebras.py index 2b2cc74043a..c4ce8148121 100644 --- a/src/sage/categories/coalgebras.py +++ b/src/sage/categories/coalgebras.py @@ -191,6 +191,17 @@ def extra_super_categories(self): sage: Coalgebras(ZZ).Super().super_categories() [Category of super modules over Integer Ring, Category of coalgebras over Integer Ring] + + Compare this with the situation for bialgebras:: + + sage: Bialgebras(ZZ).Super().extra_super_categories() + [] + sage: Bialgebras(ZZ).Super().super_categories() + [Category of super algebras over Integer Ring, + Category of super coalgebras over Integer Ring] + + The category of bialgebras does not occur in these results, + since super bialgebras are not bialgebras. """ return [self.base_category().Graded()] From 94af4b0cf84d244d96b5bd3dcd65a572687cbb67 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Fri, 17 Apr 2015 08:19:55 +0200 Subject: [PATCH 058/421] tweak to_word methods to return non-Word results if so demanded; fix incorrect use of a^2 for a**2 (this works in the interactive session only due to the preparser); a few minor fixes here and there --- src/sage/combinat/skew_tableau.py | 44 ++++++++++++++++++++++--------- src/sage/combinat/tableau.py | 38 +++++++++++++++++++++----- 2 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 36ace110037..78708074a4b 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -24,7 +24,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -import copy from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.structure.parent import Parent @@ -41,7 +40,7 @@ from sage.structure.list_clone import ClonableList from sage.combinat.partition import Partition -from sage.combinat.tableau import TableauOptions, Tableaux, SemistandardTableau, StandardTableau, Tableau +from sage.combinat.tableau import TableauOptions, SemistandardTableau, StandardTableau, Tableau from sage.combinat.skew_partition import SkewPartition, SkewPartitions from sage.combinat.integer_vector import IntegerVectors from sage.combinat.words.words import Words @@ -405,13 +404,20 @@ def conjugate(self): return SkewTableau(conj) - def to_word_by_row(self): + def to_word_by_row(self, as_word=True): """ Return a word obtained from a row reading of ``self``. Specifically, this is the word obtained by concatenating the rows from the bottommost one (in English notation) to the topmost one. + INPUT: + + - ``as_word`` -- boolean (default: ``True``); if ``True``, + the result is returned as a word (i.e., an element of + :class:`Words`), while otherwise it is returned as a + list + EXAMPLES:: sage: s = SkewTableau([[None,1],[2,3]]) @@ -427,6 +433,8 @@ def to_word_by_row(self): 1 sage: s.to_word_by_row() word: 1324 + sage: s.to_word_by_row(as_word=False) + [1, 3, 2, 4] TESTS:: @@ -439,9 +447,11 @@ def to_word_by_row(self): for row in self: word = list(row) + word + if not as_word: + return filter(lambda x: x is not None, word) return Words("positive integers")([i for i in word if i is not None]) - def to_word_by_column(self): + def to_word_by_column(self, as_word=True): """ Return the word obtained from a column reading of the skew tableau. @@ -449,6 +459,13 @@ def to_word_by_column(self): columns from the rightmost one (in English notation) to the leftmost one. + INPUT: + + - ``as_word`` -- boolean (default: ``True``); if ``True``, + the result is returned as a word (i.e., an element of + :class:`Words`), while otherwise it is returned as a + list + EXAMPLES:: sage: s = SkewTableau([[None,1],[2,3]]) @@ -467,8 +484,10 @@ def to_word_by_column(self): 1 sage: s.to_word_by_column() word: 4231 + sage: s.to_word_by_column(as_word=False) + [4, 2, 3, 1] """ - return self.conjugate().to_word_by_row() + return self.conjugate().to_word_by_row(as_word=as_word) to_word = to_word_by_row @@ -780,8 +799,8 @@ def to_chain(self, max_entry=None): def slide(self, corner=None): """ Apply a jeu-de-taquin slide to ``self`` on the specified inner corner and - returns the new tableau. If no corner is given an arbitrary inner corner - is chosen. + return the resulting tableau. If no corner is given, an arbitrary inner + corner is chosen. See [FW]_ p12-13. @@ -902,7 +921,7 @@ def rectify(self, algorithm=None): if algorithm is None: la = self.outer_shape() la_size = la.size() - if mu_size^2 < len(la) * (la_size - mu_size): + if mu_size ** 2 < len(la) * (la_size - mu_size): algorithm = 'jdt' else: algorithm = 'schensted' @@ -912,7 +931,7 @@ def rectify(self, algorithm=None): for i in range(mu_size): rect = rect.slide() elif algorithm == 'schensted': - rect = Tableau([]).insert_word(self.to_word()) + rect = Tableau([]).insert_word(self.to_word(as_word=False)) else: raise ValueError("algorithm must be 'jdt', 'schensted', or None") if self in StandardSkewTableaux(): @@ -1801,9 +1820,10 @@ def cardinality(self): def __iter__(self): """ - An iterator for all the standard skew tableaux with shape of the - skew partition ``skp``. The standard skew tableaux are ordered - lexicographically by the word obtained from their row reading. + An iterator for all the standard skew tableaux whose shape is + the skew partition ``skp``. The standard skew tableaux are + ordered lexicographically by the word obtained from their row + reading. EXAMPLES:: diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 0cc8b8fe065..2d044843caa 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -881,45 +881,67 @@ def pp(self): """ print self._repr_diagram() - def to_word_by_row(self): + def to_word_by_row(self, as_word=True): """ Return the word obtained from a row reading of the tableau ``self`` (starting with the lowermost row, reading every row from left to right). + INPUT: + + - ``as_word`` -- boolean (default: ``True``); if ``True``, + the result is returned as a word (i.e., an element of + :class:`Words`), while otherwise it is returned as a + list + EXAMPLES:: sage: Tableau([[1,2],[3,4]]).to_word_by_row() word: 3412 sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word_by_row() word: 325146 + sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word_by_row(as_word=False) + [3, 2, 5, 1, 4, 6] """ - from sage.combinat.words.word import Word w = [] for row in reversed(self): w += row + if not as_word: + return w + from sage.combinat.words.word import Word return Word(w) - def to_word_by_column(self): + def to_word_by_column(self, as_word=True): """ Return the word obtained from a column reading of the tableau ``self`` (starting with the leftmost column, reading every column from bottom to top). + INPUT: + + - ``as_word`` -- boolean (default: ``True``); if ``True``, + the result is returned as a word (i.e., an element of + :class:`Words`), while otherwise it is returned as a + list + EXAMPLES:: sage: Tableau([[1,2],[3,4]]).to_word_by_column() word: 3142 sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word_by_column() word: 321546 + sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word_by_column(as_word=False) + [3, 2, 1, 5, 4, 6] """ - from sage.combinat.words.word import Word w = [] for row in self.conjugate(): w += row[::-1] + if not as_word: + return w + from sage.combinat.words.word import Word return Word(w) - def to_word(self): + def to_word(self, as_word=True): """ An alias for :meth:`to_word_by_row`. @@ -929,8 +951,10 @@ def to_word(self): word: 3412 sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word() word: 325146 + sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word(as_word=False) + [3, 2, 5, 1, 4, 6] """ - return self.to_word_by_row() + return self.to_word_by_row(as_word=as_word) def attacking_pairs(self): """ @@ -2546,7 +2570,7 @@ def row_stabilizer(self): k = self.size() gens = [range(1, k+1)] for row in self: - for j in range(0, len(row)-1): + for j in range(len(row)-1): gens.append( (row[j], row[j+1]) ) return PermutationGroup( gens ) From f219ce3a60512bac0da8c8b05042c4ee02eacf07 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Wed, 22 Apr 2015 05:04:48 +0200 Subject: [PATCH 059/421] Fixed whitespace errors and corrected second-hand lazy import (probably a merge error) --- src/sage/algebras/clifford_algebra.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index ed22c0e6796..324660b0797 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -19,7 +19,7 @@ from sage.categories.algebras_with_basis import AlgebrasWithBasis from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis -from sage.categories.modules_with_basis import ModuleMorphismByLinearity +from sage.modules.with_basis.morphism import ModuleMorphismByLinearity from sage.categories.poor_man_map import PoorManMap from sage.rings.all import ZZ from sage.modules.free_module import FreeModule, FreeModule_generic @@ -477,7 +477,7 @@ class CliffordAlgebra(CombinatorialFreeModule): sage: Cl = CliffordAlgebra(Q) sage: Cl The Clifford algebra of the Quadratic form in 3 variables - over Integer Ring with coefficients: + over Integer Ring with coefficients: [ 1 2 3 ] [ * 4 5 ] [ * * 6 ] @@ -557,7 +557,7 @@ def _repr_(self): sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) sage: CliffordAlgebra(Q) The Clifford algebra of the Quadratic form in 3 variables - over Integer Ring with coefficients: + over Integer Ring with coefficients: [ 1 2 3 ] [ * 4 5 ] [ * * 6 ] @@ -833,7 +833,7 @@ def quadratic_form(self): sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) sage: Cl. = CliffordAlgebra(Q) sage: Cl.quadratic_form() - Quadratic form in 3 variables over Integer Ring with coefficients: + Quadratic form in 3 variables over Integer Ring with coefficients: [ 1 2 3 ] [ * 4 5 ] [ * * 6 ] From c609f87a6fbe26ca8f549904c25f491808065c0d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 22 Apr 2015 14:15:39 -0700 Subject: [PATCH 060/421] Implement left_top and right_bottom methods for RC's. --- .../rigged_configuration_element.py | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index 47b558bf4b4..6bb8e2c6961 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -1782,6 +1782,125 @@ def left_box(self, return_b=False): delta = left_box + def left_top(self): + r""" + Return the image of ``self`` under the left column top splitting + map `\gamma`. + + Consider the map `\gamma : RC(B^{r,1} \otimes B) \to RC(B^{1,1} + B^{r-1,1} \otimes B)` for `r > 1`, which is a natural strict + classical crystal injection. On rigged configurations, the map + `\gamma` adds a singular string of length `1` to `\nu^{(a)}`. + + We can extend `\gamma` when the left-most factor is not a single + column by precomposing with a :meth:`left_split()`. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['C',3,1], [[3,1], [2,1]]) + sage: mg = RC.module_generators[-1] + sage: ascii_art(mg) + 0[ ]0 0[ ][ ]0 0[ ]0 + 0[ ]0 0[ ]0 + sage: ascii_art(mg.left_top()) + 0[ ]0 0[ ][ ]0 0[ ]0 + 0[ ]0 0[ ]0 0[ ]0 + 0[ ]0 + + sage: RC = RiggedConfigurations(['C',3,1], [[2,1], [1,1], [3,1]]) + sage: mg = RC.module_generators[7] + sage: ascii_art(mg) + 1[ ]0 0[ ][ ]0 0[ ]0 + 0[ ]0 0[ ]0 + sage: ascii_art(mg.left_top()) + 1[ ]1 0[ ][ ]0 0[ ]0 + 1[ ]0 0[ ]0 0[ ]0 + """ + P = self.parent() + r = P.dims[0][0] + if r == 1: + raise ValueError("cannot split a single box") + ct = P.cartan_type() + if ct.type() == 'D': + if P.dims[0][0] >= ct.rank() - 2: + raise ValueError("only for non-spinor cases") + elif ct.type() == 'B' or ct.dual().type() == 'B': + if P.dims[0][0] == ct.rank() - 1: + raise ValueError("only for non-spinor cases") + + if P.dims[0][1] > 1: + return self.left_split().left_top() + + B = [[1,1], [r-1,1]] + B.extend(P.dims[1:]) + from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations + RC = RiggedConfigurations(P._cartan_type, B) + parts = [x._clone() for x in self] # Make a deep copy + for nu in parts[:r-1]: + nu._list.append(1) + for a, nu in enumerate(parts[:r-1]): + vac_num = RC._calc_vacancy_number(parts, a, -1) + i = nu._list.index(1) + nu.vacancy_numbers.insert(i, vac_num) + nu.rigging.insert(i, vac_num) + return RC(*parts) + + def right_bottom(self): + r""" + Return the image of ``self`` under the right column bottom splitting + map `\gamma^*`. + + Consider the map `\gamma^* : RC(B \otimes B^{r,1}) \to RC(B \otimes + B^{r-1,1} \otimes B^{1,1})` for `r > 1`, which is a natural strict + classical crystal injection. On rigged configurations, the map + `\gamma` adds a string of length `1` with rigging 0 to `\nu^{(a)}` + for all `a < r` to a classically highest weight element and extended + as a classical crystal morphism. + + We can extend `\gamma^*` when the right-most factor is not a single + column by precomposing with a :meth:`right_split()`. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['C',3,1], [[2,1], [1,1], [3,1]]) + sage: mg = RC.module_generators[7] + sage: ascii_art(mg) + 1[ ]0 0[ ][ ]0 0[ ]0 + 0[ ]0 0[ ]0 + sage: ascii_art(mg.right_bottom()) + 1[ ]0 0[ ][ ]0 0[ ]0 + 1[ ]0 0[ ]0 0[ ]0 + 0[ ]0 + """ + P = self.parent() + r = P.dims[-1][0] + if r == 1: + raise ValueError("cannot split a single box") + ct = P.cartan_type() + if ct.type() == 'D': + if P.dims[-1][0] >= ct.rank() - 2: + raise ValueError("only for non-spinor cases") + elif ct.type() == 'B' or ct.dual().type() == 'B': + if P.dims[-1][0] == ct.rank() - 1: + raise ValueError("only for non-spinor cases") + + if P.dims[-1][1] > 1: + return self.right_split().right_bottom() + + rc, e_string = self.to_highest_weight(P.cartan_type().classical().index_set()) + + B = P.dims[:-1] + ([r-1,1], [1,1]) + from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations + RC = RiggedConfigurations(P._cartan_type, B) + parts = [x._clone() for x in rc] # Make a deep copy + for nu in parts[:r-1]: + nu._list.append(1) + for a, nu in enumerate(parts[:r-1]): + vac_num = RC._calc_vacancy_number(parts, a, -1) + nu.vacancy_numbers.append(vac_num) + nu.rigging.append(0) + return RC(*parts).f_string(reversed(e_string)) + def complement_rigging(self, reverse_factors=False): r""" Apply the complement rigging morphism `\theta` to ``self``. From e90356f13323c36e2a8f8ed8e790577f48688c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 24 Apr 2015 11:49:30 +0200 Subject: [PATCH 061/421] trac #18284 making doc build --- .../rigged_configurations/rigged_configuration_element.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index 6bb8e2c6961..742522a1ab5 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -1788,7 +1788,7 @@ def left_top(self): map `\gamma`. Consider the map `\gamma : RC(B^{r,1} \otimes B) \to RC(B^{1,1} - B^{r-1,1} \otimes B)` for `r > 1`, which is a natural strict + \otimes B^{r-1,1} \otimes B)` for `r > 1`, which is a natural strict classical crystal injection. On rigged configurations, the map `\gamma` adds a singular string of length `1` to `\nu^{(a)}`. From 0b6e3f732224e9784afad1cec93afb89d1a7810a Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Fri, 24 Apr 2015 14:14:21 -0400 Subject: [PATCH 062/421] use list comprehension as Travis suggested --- src/sage/combinat/skew_tableau.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 78708074a4b..7f5dc2fa067 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -443,13 +443,10 @@ def to_word_by_row(self, as_word=True): sage: SkewTableau([]).to_word_by_row() word: """ - word = [] - for row in self: - word = list(row) + word - + word = [x for row in reversed(self) for x in row if x is not None] if not as_word: - return filter(lambda x: x is not None, word) - return Words("positive integers")([i for i in word if i is not None]) + return word + return Words("positive integers")(word) def to_word_by_column(self, as_word=True): """ From db1c8969aa371946ddbd092c555fc010a3d69a98 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Mon, 27 Apr 2015 14:15:37 +0200 Subject: [PATCH 063/421] New encoder class --- src/sage/coding/encoder.py | 255 +++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 src/sage/coding/encoder.py diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py new file mode 100644 index 00000000000..47469d0f915 --- /dev/null +++ b/src/sage/coding/encoder.py @@ -0,0 +1,255 @@ +r""" +Encoder + +Given a code, an encoder embeds some specific methods to link this code's +message space to this code's ambient space. +""" + +#***************************************************************************** +# Copyright (C) 2015 David Lucas +# +# 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.modules.free_module_element import vector +from sage.misc.abstract_method import abstract_method +from sage.misc.cachefunc import cached_method +from sage.structure.sage_object import SageObject + +class Encoder(SageObject): + r""" + Abstract top-class for Encoder objects. + + + This class contains all methods that can be used by encoders. + So, every encoder class should inherit from this abstract class. + + This class provides: + + - ``code``, the associated code of the encoder + + - methods that will work for any encoder + + To implement an encoder, you need to: + + - inherit from Encoder + + - call Encoder ``__init__`` method in the subclass constructor. Example: + ``super(SubclassName, self).__init__(code)``. + By doing that, your subclass will have its ``code`` parameter initialized. + You need of course to complete the constructor by adding any additional parameter + needed to describe properly the code defined in the subclass. + + Then, if the message space is a vectorial space, default implementation of ``encode`` and + ``unencode_nocheck`` methods are provided. These implementations rely on ``generator_matrix`` + which you need to override to use the default implementations. + + If the message space is not a vectorial space, you cannot have a generator matrix. + In that case, you need to override ``encode`` and ``unencode_nocheck``. + + As Encoder is not designed to be implemented, it does not have any representation + methods. You should implement ``_repr_`` and ``_latex_`` methods in the sublclass. + """ + + def __init__(self, code): + r""" + Initializes mandatory parameters for an Encoder object. + + This method only exists for inheritance purposes as it initializes + parameters that need to be known by every linear code. An abstract + encoder object should never be created. + + INPUT: + + - ``code`` -- the associated code of ``self`` + + EXAMPLES: + + We first create a new Encoder subclass:: + + sage: class EncoderExample(sage.coding.encoder.Encoder): + ....: def __init__(self, code): + ....: super(EncoderExample, self).__init__(code) + + We now create a member of our newly made class:: + + sage: G = Matrix(GF(2), [[1, 0, 0, 1], [0, 1, 1, 1]]) + sage: C = codes.LinearCode(G) + sage: E = EncoderExample(C) + + We can check its parameters:: + + sage: E.code() + Linear code of length 4, dimension 2 over Finite Field of size 2 + """ + self._code = code + + def encode(self, word): + r""" + Encodes ``word`` as a codeword of ``self``. + + This is a default implementation which assumes that the message + space of the encoder is a Vector Space. If this is not the case, + this method should be overwritten by the subclass. + + INPUT: + + - ``word`` -- a vector of the same length as dimension of ``self`` + + OUTPUT: + + - a vector of ``self`` + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: word = vector((0, 1, 1, 0)) + sage: C.encode(word) + (1, 1, 0, 0, 1, 1, 0) + """ + return vector(word) * self.generator_matrix() + + def unencode(self, c, nocheck=False, **kwargs): + r""" + Returns ``c`` decoded to the message space of ``self``. + + INPUT: + + - ``c`` -- a vector of the same length as ``self`` over the + base field of ``self`` + + - ``nocheck`` -- (default: ``False``) checks if ``c`` is in self. If this is set + to True, the return value of this method is not guaranteed to be correct. + + OUTPUT: + + - a vector of the message space of ``self`` + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) + sage: C.unencode(c) + (0, 1, 1, 0) + """ + if nocheck == False: + if c not in self.code(): + raise EncodingFailure("Given word is not in the code") + else: + return self.unencode_nocheck(c, **kwargs) + else: + return self.unencode_nocheck(c, **kwargs) + + @cached_method + def _unencoder_matrix(self): + r""" + Finds an information set for G, and return the inverse of those + columns of G. + + AUTHORS: + + This function is taken from codinglib (https://bitbucket.org/jsrn/codinglib/) + and was written by Johan Nielsen. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: E = C.encoder() + sage: E._unencoder_matrix() + [1 0 1 9] + [0 3 3 6] + [0 4 2 5] + [0 4 5 2] + """ + Gt = self.generator_matrix().matrix_from_columns(self.code().information_set()) + return Gt.inverse() + + def unencode_nocheck(self, c, **kwargs): + r""" + Returns the message corresponding to a codeword. + + When c is not a codeword, the output is unspecified. + + AUTHORS: + + This function is taken from codinglib (https://bitbucket.org/jsrn/codinglib/) + and was written by Johan Nielsen. + + INPUT: + + - ``c`` -- a vector of the same length as ``self`` over the + base field of ``self`` + + OUTPUT: + + - a vector + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) + sage: c in C + True + sage: C.unencode_nocheck(c) + (0, 1, 1, 0) + #TODO: another exemple with a word that does not belong to C + """ + U = self._unencoder_matrix() + info_set = self.code().information_set() + cc = vector( c[i] for i in info_set ) + return cc * U + + def code(self): + r""" + Returns the code in which ``self.encode()`` has its output. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: E = C.encoder() + sage: E.code() + Linear code of length 7, dimension 4 over Finite Field of size 2 + """ + return self._code + + def message_space(self): + r""" + Returns the ambient space of allowed input to ``self.encode()``. + Note that the ``self.encode()`` is possibly a partial function over + the ambient space. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: E = C.encoder() + sage: E.message_space() + Vector space of dimension 4 over Finite Field of size 2 + """ + return self.code().base_field()**(self.code().dimension()) + + @abstract_method(optional = True) + def generator_matrix(self): + r""" + Returns a generator matrix of the associated code of self. + + This is an abstract method and it should be implemented separately. + Reimplementing this for each subclass of Encoder is not mandatory + (as encoders with a polynomial message space, for instance, do not + need a generator matrix). + """ + +class EncodingFailure(Exception): + r""" + Special exception class to indicate a failure during encoding or unencoding. + """ + pass From 7048dbb7d2be41d9b4983b92b6944e1d30ea0576 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Mon, 27 Apr 2015 14:26:02 +0200 Subject: [PATCH 064/421] New encoder-related methods in linear_code.py --- src/sage/coding/linear_code.py | 122 +++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index f4fd1c0c00f..72b9578fd50 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -1631,6 +1631,98 @@ def __eq__(self, right): return False return True + def encode(self, word, name=None, **kwargs): + r""" + Encodes ``word`` as a codeword of ``self``. + + INPUT: + + - ``word`` -- a vector of the same length as dimension of ``self`` + + - ``name`` -- (default: ``None``) Name of the encoder which will be used + to encode ``word``. The default encoder of ``self`` will be used if + default value is kept + + OUTPUT: + + - a vector of ``self`` + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: word = vector((0, 1, 1, 0)) + sage: C.encode(word) + (1, 1, 0, 0, 1, 1, 0) + + It is possible to manually choose the encoder amongst the list of the available ones:: + + sage: C.encoders_available() + ['GeneratorMatrix'] + sage: word = vector((0, 1, 1, 0)) + sage: C.encode(word, 'GeneratorMatrix') + (3, 5, 0, 4, 0, 4, 10, 1, 4, 2) + """ + E = self.encoder(name, **kwargs) + return E.encode(word) + + @cached_method + def encoder(self, name=None, **kwargs): + r""" + Returns an encoder of ``self``. + + INPUT: + + - ``name`` -- (default: ``None``) name of the encoder which will be + returned. The default encoder of ``self`` will be used if + default value is kept. + + OUTPUT: + + - an Encoder object + + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: C.encoder() + Generator-matrix based encoder for the Linear code of length 7, dimension 4 over + Finite Field of size 2 + + If the name of an encoder which is not known by ``self`` is passed, + an exception will be raised:: + + sage: C.encoders_available() + ['GeneratorMatrix'] + sage: C.encoder('NonExistingEncoder') + Traceback (most recent call last): + ... + ValueError: Passed Encoder name not known + """ + if name is None: + name = self._encoder_default_name + return self.encoder(name, **kwargs) + if name in self._registered_encoders: + encClass = self._registered_encoders[name] + E = encClass(self, **kwargs) + return E + else: + raise ValueError("Passed Encoder name not known") + + def encoders_available(self): + r""" + Returns a list of the possible encoders for ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: C.encoders_available() + ['GeneratorMatrix'] + """ + return self._registered_encoders.keys() + def extended_code(self): r""" If ``self`` is a linear code of length `n` defined over `F` then this @@ -3011,6 +3103,36 @@ def syndrome(self, r): """ return self.parity_check_matrix()*r + def unencode(self, c, name=None, nocheck=False, **kwargs): + r""" + Returns ``c`` decoded to the message space of ``self``. + + INPUT: + + - ``c`` -- a vector of the same length as ``self`` over the + base field of ``self`` + + - ``name`` -- (default: ``None``) name of the decoder which will be used + to decode ``word``. The default decoder of ``self`` will be used if + default value is kept. + + - ``nocheck`` -- (default: ``False``) checks if ``c`` is in self. If this is set + to True, the return value of this method is not guaranteed to be correct. + + OUTPUT: + + - a vector + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) + sage: C.unencode(c) + (0, 1, 1, 0) + """ + E = self.encoder(name, **kwargs) + return E.unencode(c, nocheck) def weight_enumerator(self, names="xy", name2=None): """ From 15cd2cd1eb157c554ed04b78f67def2135e6e5d0 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Mon, 27 Apr 2015 14:36:35 +0200 Subject: [PATCH 065/421] New encoder for linear codes --- src/sage/coding/linear_code_encoders.py | 90 +++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/sage/coding/linear_code_encoders.py diff --git a/src/sage/coding/linear_code_encoders.py b/src/sage/coding/linear_code_encoders.py new file mode 100644 index 00000000000..245a64eefc1 --- /dev/null +++ b/src/sage/coding/linear_code_encoders.py @@ -0,0 +1,90 @@ +r""" +Linear Code Encoders +""" + +#***************************************************************************** +# Copyright (C) 2015 David Lucas +# +# 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 encoder import Encoder +from sage.misc.cachefunc import cached_method + +class EncoderLinearCodeGeneratorMatrix(Encoder): + r""" + Encoder based on generator_matrix for Linear codes. + + The only purpose of this encoder is to include the existing code + into the new Encoder and Decoder structure. + + INPUT: + + - ``code`` -- The associated code of this encoder. + """ + + def __init__(self, code): + r""" + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: C.encoder() + Generator-matrix based encoder for the Linear code of length 7, dimension 4 over + Finite Field of size 2 + """ + super(EncoderLinearCodeGeneratorMatrix, self).__init__(code) + + def _repr_(self): + r""" + Returns a string representation of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: C.encoder() + Generator-matrix based encoder for the Linear code of length 7, dimension 4 over + Finite Field of size 2 + """ + return "Generator matrix-based encoder for the %s" % self.code() + + def _latex_(self): + r""" + Returns a latex representation of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: E = C.encoder() + sage: latex(E) + \textnormal{Generator-matrix based encoder for the Linear code of length 7, dimension 4 over + Finite Field of size 2} + """ + return "\\textnormal{Generator matrix-based encoder for the %s}" % self.code()._latex_() + + @cached_method + def generator_matrix(self): + r""" + Returns a generator matrix of the associated code of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = codes.LinearCode(G) + sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E.generator_matrix() + [1 1 1 0 0 0 0] + [1 0 0 1 1 0 0] + [0 1 0 1 0 1 0] + [1 1 0 1 0 0 1] + """ + if hasattr(self.code(), "_generator_matrix"): + return self.code()._generator_matrix + else: + return self.code().generator_matrix() From b54bac5c21435b47bb1cc4884b4c106640abf45c Mon Sep 17 00:00:00 2001 From: David Lucas Date: Mon, 27 Apr 2015 15:05:16 +0200 Subject: [PATCH 066/421] Links with encoder structure --- src/sage/coding/__init__.py | 1 + src/sage/coding/all.py | 2 ++ src/sage/coding/encoder.py | 39 ++++++++++++++++++++++------------ src/sage/coding/linear_code.py | 3 +++ 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/sage/coding/__init__.py b/src/sage/coding/__init__.py index c9fecacd721..24bb6b11ac1 100644 --- a/src/sage/coding/__init__.py +++ b/src/sage/coding/__init__.py @@ -1 +1,2 @@ import all +linear_code.LinearCode._registered_encoders["GeneratorMatrix"] = linear_code_encoders.EncoderLinearCodeGeneratorMatrix diff --git a/src/sage/coding/all.py b/src/sage/coding/all.py index 7512aac7a6d..4e1faf0d4db 100644 --- a/src/sage/coding/all.py +++ b/src/sage/coding/all.py @@ -68,6 +68,8 @@ bounds_minimum_distance, self_orthogonal_binary_codes) +from linear_code_encoders import EncoderLinearCodeGeneratorMatrix + from sd_codes import self_dual_codes_binary lazy_import("sage.coding.delsarte_bounds", diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 47469d0f915..61f75953acc 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -78,7 +78,7 @@ def __init__(self, code): We now create a member of our newly made class:: sage: G = Matrix(GF(2), [[1, 0, 0, 1], [0, 1, 1, 1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: E = EncoderExample(C) We can check its parameters:: @@ -107,7 +107,7 @@ def encode(self, word): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: word = vector((0, 1, 1, 0)) sage: C.encode(word) (1, 1, 0, 0, 1, 1, 0) @@ -133,7 +133,7 @@ def unencode(self, c, nocheck=False, **kwargs): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) sage: C.unencode(c) (0, 1, 1, 0) @@ -160,13 +160,13 @@ def _unencoder_matrix(self): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: E = C.encoder() sage: E._unencoder_matrix() - [1 0 1 9] - [0 3 3 6] - [0 4 2 5] - [0 4 5 2] + [0 0 1 1] + [0 1 0 1] + [1 1 1 0] + [0 1 1 1] """ Gt = self.generator_matrix().matrix_from_columns(self.code().information_set()) return Gt.inverse() @@ -194,13 +194,26 @@ def unencode_nocheck(self, c, **kwargs): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) sage: c in C True - sage: C.unencode_nocheck(c) + sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E.unencode_nocheck(c) (0, 1, 1, 0) - #TODO: another exemple with a word that does not belong to C + + We take a vector that does not belong to C:: + + sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 1)) + sage: c in C + False + sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E.unencode_nocheck(c) + (0, 1, 1, 0) + sage: m = vector(GF(2), (0, 1, 1, 0)) + sage: c1 = E.encode(m) + sage: c == c1 + False """ U = self._unencoder_matrix() info_set = self.code().information_set() @@ -214,7 +227,7 @@ def code(self): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: E = C.encoder() sage: E.code() Linear code of length 7, dimension 4 over Finite Field of size 2 @@ -230,7 +243,7 @@ def message_space(self): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: E = C.encoder() sage: E.message_space() Vector space of dimension 4 over Finite Field of size 2 diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 72b9578fd50..94ae90eb0e2 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -3408,6 +3408,8 @@ class LinearCode(AbstractLinearCode): # sage: C.minimum_distance_why() # optional (net connection) # Ub(7,4) = 3 follows by the Griesmer bound. + _registered_encoders = {} + def __init__(self, generator_matrix, d=None): r""" See the docstring for :meth:`LinearCode`. @@ -3473,6 +3475,7 @@ def __init__(self, generator_matrix, d=None): self._generator_matrix = generator_matrix self._dimension = generator_matrix.rank() self._minimum_distance = d + self._encoder_default_name = "GeneratorMatrix" def _repr_(self): r""" From 5f4ca778bdd1dffa809710f14982ddb462dc1e39 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Mon, 27 Apr 2015 15:46:38 +0200 Subject: [PATCH 067/421] Default implementation of generator_matrix in AbstractLinearCode. Changed documentation --- src/sage/coding/__init__.py | 2 +- src/sage/coding/encoder.py | 6 +- src/sage/coding/linear_code.py | 104 +++++++++++++++--------- src/sage/coding/linear_code_encoders.py | 27 +++--- 4 files changed, 84 insertions(+), 55 deletions(-) diff --git a/src/sage/coding/__init__.py b/src/sage/coding/__init__.py index 24bb6b11ac1..661b4bbcef4 100644 --- a/src/sage/coding/__init__.py +++ b/src/sage/coding/__init__.py @@ -1,2 +1,2 @@ import all -linear_code.LinearCode._registered_encoders["GeneratorMatrix"] = linear_code_encoders.EncoderLinearCodeGeneratorMatrix +linear_code.AbstractLinearCode._registered_encoders["GeneratorMatrix"] = linear_code_encoders.EncoderLinearCodeGeneratorMatrix diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 61f75953acc..65e902b773e 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -109,7 +109,8 @@ def encode(self, word): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: word = vector((0, 1, 1, 0)) - sage: C.encode(word) + sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E.encode(word) (1, 1, 0, 0, 1, 1, 0) """ return vector(word) * self.generator_matrix() @@ -135,7 +136,8 @@ def unencode(self, c, nocheck=False, **kwargs): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) - sage: C.unencode(c) + sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E.unencode(c) (0, 1, 1, 0) """ if nocheck == False: diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 94ae90eb0e2..068b023c006 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -706,6 +706,11 @@ class AbstractLinearCode(module.Module): - ``length``, the length of the code + - ``encoder_default_name``, the name of the encoder that will be used if no encoder name is passed + to an encoder-related method (``generator_matrix``, ``encode``, ``unencode``) + + - ``_registered_encoders``, a set of all encoders available for this class + - numerous methods that will work for any linear code (including families) To implement a linear code, you need to: @@ -719,19 +724,19 @@ class AbstractLinearCode(module.Module): You need of course to complete the constructor by adding any additional parameter needed to describe properly the code defined in the subclass. - - reimplement ``generator_matrix()`` method - As AbstractLinearCode is not designed to be implemented, it does not have any representation methods. You should implement ``_repr_`` and ``_latex_`` methods in the sublclass. - .. NOTE:: - - AbstractLinearCode embeds some generic implementations of helper methods like ``__cmp__`` or ``__eq__``. - As they are designed to fit for every linear code, they mostly use the generator matrix - and thus can be long for certain families of code. In that case, overriding these methods is encouraged. + ..NOTE:: + A lot of methods of the abstract class rely on the knowledge of a generator_matrix. + It is thus strongly recommended to set an encoder with a generator matrix implemented + as a default encoder. """ - def __init__(self, base_field, length): + + _registered_encoders = {} + + def __init__(self, base_field, length, encoder_default_name): """ Initializes mandatory parameters for a Linear Code object. @@ -745,13 +750,15 @@ def __init__(self, base_field, length): - ``length`` -- the length of ``self`` + - ``encoder_default_name`` -- the name of the default encoder of ``self`` + EXAMPLES: We first create a new LinearCode subclass:: sage: class CodeExample(sage.coding.linear_code.AbstractLinearCode): ....: def __init__(self, field, length, dimension, generator_matrix): - ....: sage.coding.linear_code.AbstractLinearCode.__init__(self,field, length) + ....: sage.coding.linear_code.AbstractLinearCode.__init__(self,field, length, "GeneratorMatrix") ....: self._dimension = dimension ....: self._generator_matrix = generator_matrix ....: def generator_matrix(self): @@ -795,10 +802,31 @@ def __init__(self, base_field, length): Traceback (most recent call last): ... ValueError: length must be a Python int or a Sage Integer + + If the name of the default encoder is not known by the class, it will raise + an exception:: + + sage: class CodeExample(sage.coding.linear_code.AbstractLinearCode): + ....: def __init__(self, field, length, dimension, generator_matrix): + ....: sage.coding.linear_code.AbstractLinearCode.__init__(self,field, length, "Fail") + ....: self._dimension = dimension + ....: self._generator_matrix = generator_matrix + ....: def generator_matrix(self): + ....: return self._generator_matrix + ....: def _repr_(self): + ....: return "Dummy code of length %d, dimension %d over %s" % (self.length(), self.dimension(), self.base_field()) + + sage: C = CodeExample(GF(17), 10, 5, generator_matrix) + Traceback (most recent call last): + ... + ValueError: You must set a valid encoder as default encoder for this code """ if not isinstance(length, (int, Integer)): raise ValueError("length must be a Python int or a Sage Integer") self._length = Integer(length) + if not encoder_default_name in self._registered_encoders: + raise ValueError("You must set a valid encoder as default encoder for this code") + self._encoder_default_name = encoder_default_name cat = Modules(base_field).FiniteDimensional().WithBasis().Finite() facade_for = VectorSpace(base_field, self._length) self.Element = type(facade_for.an_element()) #for when we made this a non-facade parent @@ -1650,7 +1678,7 @@ def encode(self, word, name=None, **kwargs): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: word = vector((0, 1, 1, 0)) sage: C.encode(word) (1, 1, 0, 0, 1, 1, 0) @@ -1661,7 +1689,7 @@ def encode(self, word, name=None, **kwargs): ['GeneratorMatrix'] sage: word = vector((0, 1, 1, 0)) sage: C.encode(word, 'GeneratorMatrix') - (3, 5, 0, 4, 0, 4, 10, 1, 4, 2) + (1, 1, 0, 0, 1, 1, 0) """ E = self.encoder(name, **kwargs) return E.encode(word) @@ -1685,10 +1713,9 @@ def encoder(self, name=None, **kwargs): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: C.encoder() - Generator-matrix based encoder for the Linear code of length 7, dimension 4 over - Finite Field of size 2 + Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 If the name of an encoder which is not known by ``self`` is passed, an exception will be raised:: @@ -1717,7 +1744,7 @@ def encoders_available(self): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: C.encoders_available() ['GeneratorMatrix'] """ @@ -1894,8 +1921,28 @@ def __getitem__(self, i): codeword.set_immutable() return codeword - def generator_matrix(self): - return NotImplementedError("This method must be set in subclasses") + def generator_matrix(self, name=None, **kwargs): + r""" + Returns a generator matrix of ``self``. + + INPUT: + + - ``name`` -- (default: ``None``) name of the encoder which will be + used to compute the generator matrix. The default encoder of ``self`` + will be used if default value is kept. + + EXAMPLES:: + + sage: G = matrix(GF(3),2,[1,-1,1,-1,1,1]) + sage: code = LinearCode(G) + sage: code.generator_matrix() + [1 2 1] + [2 1 1] + """ + E = self.encoder(name, **kwargs) + return E.generator_matrix() + + gen_mat = deprecated_function_alias(17973, generator_matrix) def generator_matrix_systematic(self): """ @@ -3126,7 +3173,7 @@ def unencode(self, c, name=None, nocheck=False, **kwargs): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) sage: C.unencode(c) (0, 1, 1, 0) @@ -3408,8 +3455,6 @@ class LinearCode(AbstractLinearCode): # sage: C.minimum_distance_why() # optional (net connection) # Ub(7,4) = 3 follows by the Griesmer bound. - _registered_encoders = {} - def __init__(self, generator_matrix, d=None): r""" See the docstring for :meth:`LinearCode`. @@ -3471,11 +3516,10 @@ def __init__(self, generator_matrix, d=None): if generator_matrix.nrows() == 0: raise ValueError("this linear code contains no non-zero vector") - super(LinearCode, self).__init__(base_ring, generator_matrix.ncols()) + super(LinearCode, self).__init__(base_ring, generator_matrix.ncols(), "GeneratorMatrix") self._generator_matrix = generator_matrix self._dimension = generator_matrix.rank() self._minimum_distance = d - self._encoder_default_name = "GeneratorMatrix" def _repr_(self): r""" @@ -3490,19 +3534,3 @@ def _repr_(self): Linear code of length 7, dimension 4 over Finite Field of size 2 """ return "Linear code of length %s, dimension %s over %s"%(self.length(), self.dimension(), self.base_ring()) - - def generator_matrix(self): - r""" - Return a generator matrix of this code. - - EXAMPLES:: - - sage: G = matrix(GF(3),2,[1,-1,1,-1,1,1]) - sage: code = LinearCode(G) - sage: code.generator_matrix() - [1 2 1] - [2 1 1] - """ - return self._generator_matrix - - gen_mat = deprecated_function_alias(17973, generator_matrix) diff --git a/src/sage/coding/linear_code_encoders.py b/src/sage/coding/linear_code_encoders.py index 245a64eefc1..720923c0307 100644 --- a/src/sage/coding/linear_code_encoders.py +++ b/src/sage/coding/linear_code_encoders.py @@ -32,10 +32,10 @@ def __init__(self, code): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) - sage: C.encoder() - Generator-matrix based encoder for the Linear code of length 7, dimension 4 over - Finite Field of size 2 + sage: C = LinearCode(G) + sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E + Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 """ super(EncoderLinearCodeGeneratorMatrix, self).__init__(code) @@ -46,10 +46,10 @@ def _repr_(self): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) - sage: C.encoder() - Generator-matrix based encoder for the Linear code of length 7, dimension 4 over - Finite Field of size 2 + sage: C = LinearCode(G) + sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E + Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 """ return "Generator matrix-based encoder for the %s" % self.code() @@ -60,13 +60,12 @@ def _latex_(self): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) - sage: E = C.encoder() + sage: C = LinearCode(G) + sage: E = EncoderLinearCodeGeneratorMatrix(C) sage: latex(E) - \textnormal{Generator-matrix based encoder for the Linear code of length 7, dimension 4 over - Finite Field of size 2} + \textnormal{Generator matrix-based encoder for the }[7, 4]\textnormal{ Linear code over }\Bold{F}_{2} """ - return "\\textnormal{Generator matrix-based encoder for the %s}" % self.code()._latex_() + return "\\textnormal{Generator matrix-based encoder for the }%s" % self.code()._latex_() @cached_method def generator_matrix(self): @@ -76,7 +75,7 @@ def generator_matrix(self): EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = codes.LinearCode(G) + sage: C = LinearCode(G) sage: E = EncoderLinearCodeGeneratorMatrix(C) sage: E.generator_matrix() [1 1 1 0 0 0 0] From c6418c98de1a931c1736e31594330e38135780d1 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Tue, 28 Apr 2015 13:47:05 +0200 Subject: [PATCH 068/421] Changes to the documentation --- src/sage/coding/encoder.py | 9 ++++----- src/sage/coding/linear_code.py | 11 +++++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 65e902b773e..ee5b24f23b2 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -1,8 +1,7 @@ r""" Encoder -Given a code, an encoder embeds some specific methods to link this code's -message space to this code's ambient space. +Representation of a bijection between a message space and a code. """ #***************************************************************************** @@ -24,7 +23,6 @@ class Encoder(SageObject): r""" Abstract top-class for Encoder objects. - This class contains all methods that can be used by encoders. So, every encoder class should inherit from this abstract class. @@ -32,7 +30,7 @@ class Encoder(SageObject): - ``code``, the associated code of the encoder - - methods that will work for any encoder + - some methods for encoder objects To implement an encoder, you need to: @@ -48,7 +46,8 @@ class Encoder(SageObject): ``unencode_nocheck`` methods are provided. These implementations rely on ``generator_matrix`` which you need to override to use the default implementations. - If the message space is not a vectorial space, you cannot have a generator matrix. + If the message space is not of the form `F ** k`, where `F` is a finite field, + you cannot have a generator matrix. In that case, you need to override ``encode`` and ``unencode_nocheck``. As Encoder is not designed to be implemented, it does not have any representation diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 068b023c006..0873d9c50e2 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -709,7 +709,7 @@ class AbstractLinearCode(module.Module): - ``encoder_default_name``, the name of the encoder that will be used if no encoder name is passed to an encoder-related method (``generator_matrix``, ``encode``, ``unencode``) - - ``_registered_encoders``, a set of all encoders available for this class + - ``_registered_encoders``, a dictionary of all encoders available for this class - numerous methods that will work for any linear code (including families) @@ -724,8 +724,15 @@ class AbstractLinearCode(module.Module): You need of course to complete the constructor by adding any additional parameter needed to describe properly the code defined in the subclass. + - fill the dictionary of its encoders in ``sage.coding.__init__`` file. Example: + I want to link the encoder ``MyEncoderClass`` to ``MyNewCodeClass`` + under the name ``MyEncoderName``. + All I need to do is to write this line in the ``__init__.py`` file: + ``MyNewCodeClass._registered_encoders["NameOfMyEncoder"] = MyEncoderClass`` and all instances of + ``MyNewCodeClass`` will be able to use instances of ``MyEncoderClass``. + As AbstractLinearCode is not designed to be implemented, it does not have any representation - methods. You should implement ``_repr_`` and ``_latex_`` methods in the sublclass. + methods. You should implement ``_repr_`` and ``_latex_`` methods in the subclass. ..NOTE:: From 1a0a5a48f682ddc2867ad2f1ccbd9bb3b9f505bf Mon Sep 17 00:00:00 2001 From: David Lucas Date: Wed, 29 Apr 2015 12:52:22 +0200 Subject: [PATCH 069/421] Add new method for adding encoders to the list of available encoders on the fly. Changed encoders_available method --- src/sage/coding/linear_code.py | 92 +++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 0873d9c50e2..c1597271fc1 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -216,6 +216,7 @@ import sage.modules.free_module as fm import sage.modules.module as module from sage.categories.modules import Modules +from copy import copy from sage.interfaces.all import gap from sage.rings.finite_rings.constructor import FiniteField as GF from sage.groups.perm_gps.permgroup import PermutationGroup @@ -707,7 +708,7 @@ class AbstractLinearCode(module.Module): - ``length``, the length of the code - ``encoder_default_name``, the name of the encoder that will be used if no encoder name is passed - to an encoder-related method (``generator_matrix``, ``encode``, ``unencode``) + to an encoder-related method (``generator_matrix``, ``encode``, ``unencode``) - ``_registered_encoders``, a dictionary of all encoders available for this class @@ -734,9 +735,9 @@ class AbstractLinearCode(module.Module): As AbstractLinearCode is not designed to be implemented, it does not have any representation methods. You should implement ``_repr_`` and ``_latex_`` methods in the subclass. - ..NOTE:: + .. NOTE:: - A lot of methods of the abstract class rely on the knowledge of a generator_matrix. + A lot of methods of the abstract class rely on the knowledge of a generator matrix. It is thus strongly recommended to set an encoder with a generator matrix implemented as a default encoder. """ @@ -828,6 +829,7 @@ def __init__(self, base_field, length, encoder_default_name): ... ValueError: You must set a valid encoder as default encoder for this code """ + self._registered_encoders = copy(self._registered_encoders) if not isinstance(length, (int, Integer)): raise ValueError("length must be a Python int or a Sage Integer") self._length = Integer(length) @@ -871,6 +873,72 @@ def _an_element_(self): """ return self.gens()[0] + def add_encoder(self, name, encoder, check=True): + r""" + Adds an encoder to the list of registered encoders of ``self``. + + INPUT: + + - ``name`` -- the string name for the encoder + + - ``encoder`` -- the class name of the encoder + + - ``check`` -- (default: ``True``) if true, checks if ``name`` or ``encoder`` + are already in the list of registered encoders, and raises an error if yes + + EXAMPLES: + + First of all, we create a (very basic) new encoder:: + + sage: class MyEncoder(sage.coding.encoder.Encoder): + ....: def __init__(self, code): + ....: super(MyEncoder, self).__init__(code) + ....: def _repr_(self): + ....: return "MyEncoder encoder with associated code %s" % self.code() + + We now create a new code:: + + sage: C = codes.HammingCode(3, GF(2)) + + We can add our new encoder to the list of available encoders of C:: + + sage: C.add_encoder("MyEncoder", MyEncoder) + sage: C.encoders_available() + ['MyEncoder', 'GeneratorMatrix'] + + We can verify that any new code will not know MyEncoder:: + + sage: C2 = codes.HammingCode(3, GF(3)) + sage: C2.encoders_available() + ['GeneratorMatrix'] + + TESTS: + + If ``check`` is True, it is impossible to use a name which is in + the dictionnary of available encoders:: + + sage: C.add_encoder("GeneratorMatrix", MyEncoder) + Traceback (most recent call last): + ... + ValueError: There is already a registered encoder with this name + + But if ``check`` is set to false, overriding names is authorized:: + + sage: C.encoders_available(True) + [('MyEncoder', ), + ('GeneratorMatrix', + )] + sage: C.add_encoder("GeneratorMatrix", MyEncoder, False) + sage: C.encoders_available(True) + [('MyEncoder', ), + ('GeneratorMatrix', )] + """ + reg_enc = self._registered_encoders + if check==True: + if(name in reg_enc.keys()): + raise ValueError("There is already a registered encoder with this name") + reg_enc[name] = encoder + def automorphism_group_gens(self, equivalence="semilinear"): r""" Return generators of the automorphism group of ``self``. @@ -1744,9 +1812,14 @@ def encoder(self, name=None, **kwargs): else: raise ValueError("Passed Encoder name not known") - def encoders_available(self): + def encoders_available(self, values=False): r""" - Returns a list of the possible encoders for ``self``. + Returns a list of the available encoders' names for ``self``. + + INPUT: + + - ``values`` -- (default: ``False``) if values is set to ``True``, it also + returns the encoders' classes associated with the encoders' names EXAMPLES:: @@ -1754,8 +1827,15 @@ def encoders_available(self): sage: C = LinearCode(G) sage: C.encoders_available() ['GeneratorMatrix'] + + sage: C.encoders_available(True) + [('GeneratorMatrix', + )] """ - return self._registered_encoders.keys() + reg_enc = self._registered_encoders + if values == True: + return reg_enc.items() + return reg_enc.keys() def extended_code(self): r""" From d3cc5586e9039da92f1e87bf6a56a5dfae50386a Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Wed, 29 Apr 2015 22:14:18 +0200 Subject: [PATCH 070/421] #18338 bell_polynomial(n,k) should always return a polynomial. --- src/sage/combinat/combinat.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index f118cfe68fb..0c843d7511d 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -2702,6 +2702,22 @@ def bell_polynomial(n, k): sage: bell_polynomial(6,3) 15*x_2^3 + 60*x_1*x_2*x_3 + 15*x_1^2*x_4 + TESTS: + + Check that :trac:`18338` is fixed:: + + sage: bell_polynomial(0,0).parent() + Univariate Polynomial Ring in x_1 over Rational Field + + sage: for n in (0..4): + ....: print [bell_polynomial(n,k).coefficients() for k in (0..n)] + [[1]] + [[], [1]] + [[], [1], [1]] + [[], [1], [3], [1]] + [[], [1], [3, 4], [6], [1]] + + REFERENCES: - E.T. Bell, "Partition Polynomials" @@ -2709,24 +2725,23 @@ def bell_polynomial(n, k): AUTHORS: - Blair Sutton (2009-01-26) + - Thierry Monteil (2015-09-29): the result must always be a polynomial. """ from sage.combinat.partition import Partitions from sage.rings.arith import factorial - vars = ZZ[tuple(['x_'+str(i) for i in range(1, n-k+2)])].gens() - result = 0 + R = QQ[tuple(['x_'+str(i) for i in range(1, n-k+2)])] + vars = R.gens() + result = R.zero() for p in Partitions(n, length=k): - factorial_product = 1 + factorial_product = 1 power_factorial_product = 1 for part, count in p.to_exp_dict().iteritems(): factorial_product *= factorial(count) power_factorial_product *= factorial(part)**count - coefficient = factorial(n) / (factorial_product * power_factorial_product) result += coefficient * prod([vars[i - 1] for i in p]) - return result - def fibonacci_sequence(start, stop=None, algorithm=None): r""" Return an iterator over the Fibonacci sequence, for all fibonacci From 145a97c362ca5faee034430f2a67ce915283fc7e Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Mon, 4 May 2015 14:13:22 +0000 Subject: [PATCH 071/421] #18338 variables start at x0, not x_1 --- src/sage/combinat/combinat.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 0c843d7511d..0dd9bc3df5f 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -2698,16 +2698,16 @@ def bell_polynomial(n, k): EXAMPLES:: sage: bell_polynomial(6,2) - 10*x_3^2 + 15*x_2*x_4 + 6*x_1*x_5 + 10*x2^2 + 15*x1*x3 + 6*x0*x4 sage: bell_polynomial(6,3) - 15*x_2^3 + 60*x_1*x_2*x_3 + 15*x_1^2*x_4 + 15*x1^3 + 60*x0*x1*x2 + 15*x0^2*x3 TESTS: Check that :trac:`18338` is fixed:: sage: bell_polynomial(0,0).parent() - Univariate Polynomial Ring in x_1 over Rational Field + Multivariate Polynomial Ring in x over Rational Field sage: for n in (0..4): ....: print [bell_polynomial(n,k).coefficients() for k in (0..n)] @@ -2727,9 +2727,10 @@ def bell_polynomial(n, k): - Blair Sutton (2009-01-26) - Thierry Monteil (2015-09-29): the result must always be a polynomial. """ + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.combinat.partition import Partitions from sage.rings.arith import factorial - R = QQ[tuple(['x_'+str(i) for i in range(1, n-k+2)])] + R = PolynomialRing(QQ, 'x', n-k+1) vars = R.gens() result = R.zero() for p in Partitions(n, length=k): From c82673a12d3209ab2351aff25f7d4ac6d5eee761 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Tue, 5 May 2015 09:58:36 +0200 Subject: [PATCH 072/421] Changes to the documentation --- src/doc/en/reference/coding/index.rst | 1 + src/sage/coding/encoder.py | 14 +++++++------- src/sage/coding/linear_code.py | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/doc/en/reference/coding/index.rst b/src/doc/en/reference/coding/index.rst index 293158d8682..97e862db7ff 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -6,6 +6,7 @@ Coding Theory .. toctree:: :maxdepth: 1 + sage/coding/encoder sage/coding/codes_catalog sage/coding/linear_code sage/coding/code_constructions diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index ee5b24f23b2..4ac1f0de82e 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -37,16 +37,16 @@ class Encoder(SageObject): - inherit from Encoder - call Encoder ``__init__`` method in the subclass constructor. Example: - ``super(SubclassName, self).__init__(code)``. - By doing that, your subclass will have its ``code`` parameter initialized. - You need of course to complete the constructor by adding any additional parameter - needed to describe properly the code defined in the subclass. + ``super(SubclassName, self).__init__(code)``. + By doing that, your subclass will have its ``code`` parameter initialized. + You need of course to complete the constructor by adding any additional parameter + needed to describe properly the code defined in the subclass. Then, if the message space is a vectorial space, default implementation of ``encode`` and ``unencode_nocheck`` methods are provided. These implementations rely on ``generator_matrix`` which you need to override to use the default implementations. - If the message space is not of the form `F ** k`, where `F` is a finite field, + If the message space is not of the form `F^k`, where `F` is a finite field, you cannot have a generator matrix. In that case, you need to override ``encode`` and ``unencode_nocheck``. @@ -89,7 +89,7 @@ def __init__(self, code): def encode(self, word): r""" - Encodes ``word`` as a codeword of ``self``. + Transforms an element of the message space into an element of the code. This is a default implementation which assumes that the message space of the encoder is a Vector Space. If this is not the case, @@ -97,7 +97,7 @@ def encode(self, word): INPUT: - - ``word`` -- a vector of the same length as dimension of ``self`` + - ``word`` -- a vector of the message space of the code OUTPUT: diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index c1597271fc1..a60292d7f46 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -1736,11 +1736,11 @@ def __eq__(self, right): def encode(self, word, name=None, **kwargs): r""" - Encodes ``word`` as a codeword of ``self``. + Transforms an element of the message space into an element of the code. INPUT: - - ``word`` -- a vector of the same length as dimension of ``self`` + - ``word`` -- a vector of the message space of the code - ``name`` -- (default: ``None``) Name of the encoder which will be used to encode ``word``. The default encoder of ``self`` will be used if From 9b7ac0bcada31f0b09800dd799c0e88a59b8fe85 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Thu, 7 May 2015 13:28:19 +0200 Subject: [PATCH 073/421] Default equality check method in Encoder. Changed naming convention for encoders. Removed linear_code_encoders file. Updated documentation --- src/sage/coding/__init__.py | 2 +- src/sage/coding/all.py | 2 +- src/sage/coding/encoder.py | 71 +++++++++++++++--- src/sage/coding/linear_code.py | 95 ++++++++++++++++++++++++- src/sage/coding/linear_code_encoders.py | 89 ----------------------- 5 files changed, 157 insertions(+), 102 deletions(-) delete mode 100644 src/sage/coding/linear_code_encoders.py diff --git a/src/sage/coding/__init__.py b/src/sage/coding/__init__.py index 661b4bbcef4..b2cee969e25 100644 --- a/src/sage/coding/__init__.py +++ b/src/sage/coding/__init__.py @@ -1,2 +1,2 @@ import all -linear_code.AbstractLinearCode._registered_encoders["GeneratorMatrix"] = linear_code_encoders.EncoderLinearCodeGeneratorMatrix +linear_code.AbstractLinearCode._registered_encoders["GeneratorMatrix"] = linear_code.LinearCodeGeneratorMatrixEncoder diff --git a/src/sage/coding/all.py b/src/sage/coding/all.py index 02f9891ecb4..cada61837a0 100644 --- a/src/sage/coding/all.py +++ b/src/sage/coding/all.py @@ -68,7 +68,7 @@ bounds_minimum_distance, self_orthogonal_binary_codes) -from linear_code_encoders import EncoderLinearCodeGeneratorMatrix +from linear_code import LinearCodeGeneratorMatrixEncoder from sd_codes import self_dual_codes_binary diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 4ac1f0de82e..a1b6419f55b 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -50,6 +50,17 @@ class Encoder(SageObject): you cannot have a generator matrix. In that case, you need to override ``encode`` and ``unencode_nocheck``. + Equality methods (``__eq__`` and ``__ne__``) might be useful for encoding in advanced + codes constructions (like concatenated codes). If provided default implementation of + these methods is not enough for your subclass, you are strongly encouraged to override + them. + + .. NOTE:: + + For consistency on encoders, please follow this convention on names for subclasses: + for a new encoder named ``EncName``, for code family ``CodeFam``, call it + ``CodeFamEncNameEncoder``. + As Encoder is not designed to be implemented, it does not have any representation methods. You should implement ``_repr_`` and ``_latex_`` methods in the sublclass. """ @@ -87,6 +98,46 @@ def __init__(self, code): """ self._code = code + def __eq__(self, other): + r""" + Checks equality between ``self`` and ``other``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E1 = LinearCodeGeneratorMatrixEncoder(C) + sage: E2 = LinearCodeGeneratorMatrixEncoder(C) + sage: E1 == E2 + True + sage: G = Matrix(GF(3), [[2,1,1,0,0,0,1],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,1,1,0,0,1]]) + sage: C1 = LinearCode(G) + sage: E2 = LinearCodeGeneratorMatrixEncoder(C1) + sage: E1 == E2 + False + """ + return self.code() == other.code() + + def __ne__(self, other): + r""" + Checks difference between ``self`` and ``other``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E1 = LinearCodeGeneratorMatrixEncoder(C) + sage: E2 = LinearCodeGeneratorMatrixEncoder(C) + sage: E1 != E2 + False + sage: G = Matrix(GF(3), [[2,1,1,0,0,0,1],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,1,1,0,0,1]]) + sage: C1 = LinearCode(G) + sage: E2 = LinearCodeGeneratorMatrixEncoder(C1) + sage: E1 != E2 + True + """ + return not self.__eq__(other) + def encode(self, word): r""" Transforms an element of the message space into an element of the code. @@ -108,7 +159,7 @@ def encode(self, word): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: word = vector((0, 1, 1, 0)) - sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E = LinearCodeGeneratorMatrixEncoder(C) sage: E.encode(word) (1, 1, 0, 0, 1, 1, 0) """ @@ -116,7 +167,7 @@ def encode(self, word): def unencode(self, c, nocheck=False, **kwargs): r""" - Returns ``c`` decoded to the message space of ``self``. + Returns the message corresponding to ``c``. INPUT: @@ -135,7 +186,7 @@ def unencode(self, c, nocheck=False, **kwargs): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) - sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E = LinearCodeGeneratorMatrixEncoder(C) sage: E.unencode(c) (0, 1, 1, 0) """ @@ -148,11 +199,15 @@ def unencode(self, c, nocheck=False, **kwargs): return self.unencode_nocheck(c, **kwargs) @cached_method - def _unencoder_matrix(self): + def unencoder_matrix(self): r""" - Finds an information set for G, and return the inverse of those + Finds an information set for G, and returns the inverse of those columns of G. + .. NOTE:: + + This is a helper function, for internal use only. + AUTHORS: This function is taken from codinglib (https://bitbucket.org/jsrn/codinglib/) @@ -174,7 +229,7 @@ def _unencoder_matrix(self): def unencode_nocheck(self, c, **kwargs): r""" - Returns the message corresponding to a codeword. + Returns the message corresponding to ``c``. When c is not a codeword, the output is unspecified. @@ -199,7 +254,7 @@ def unencode_nocheck(self, c, **kwargs): sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) sage: c in C True - sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E = LinearCodeGeneratorMatrixEncoder(C) sage: E.unencode_nocheck(c) (0, 1, 1, 0) @@ -208,7 +263,7 @@ def unencode_nocheck(self, c, **kwargs): sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 1)) sage: c in C False - sage: E = EncoderLinearCodeGeneratorMatrix(C) + sage: E = LinearCodeGeneratorMatrixEncoder(C) sage: E.unencode_nocheck(c) (0, 1, 1, 0) sage: m = vector(GF(2), (0, 1, 1, 0)) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index a60292d7f46..b26bdb9abe3 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -927,7 +927,7 @@ def add_encoder(self, name, encoder, check=True): sage: C.encoders_available(True) [('MyEncoder', ), ('GeneratorMatrix', - )] + )] sage: C.add_encoder("GeneratorMatrix", MyEncoder, False) sage: C.encoders_available(True) [('MyEncoder', ), @@ -1774,6 +1774,10 @@ def encoder(self, name=None, **kwargs): r""" Returns an encoder of ``self``. + This methods creates a new instance of the encoder subclass designated by ``name``. + While it is also possible to do the same by directly calling the subclass' constructor, + it is strongly advised to use this method to take advantage of the caching mechanism. + INPUT: - ``name`` -- (default: ``None``) name of the encoder which will be @@ -1830,7 +1834,7 @@ def encoders_available(self, values=False): sage: C.encoders_available(True) [('GeneratorMatrix', - )] + )] """ reg_enc = self._registered_encoders if values == True: @@ -3239,7 +3243,7 @@ def syndrome(self, r): def unencode(self, c, name=None, nocheck=False, **kwargs): r""" - Returns ``c`` decoded to the message space of ``self``. + Returns the message corresponding to ``c``. INPUT: @@ -3621,3 +3625,88 @@ def _repr_(self): Linear code of length 7, dimension 4 over Finite Field of size 2 """ return "Linear code of length %s, dimension %s over %s"%(self.length(), self.dimension(), self.base_ring()) + +####################### encoders ############################### +from encoder import Encoder + +class LinearCodeGeneratorMatrixEncoder(Encoder): + r""" + Encoder based on generator_matrix for Linear codes. + + The only purpose of this encoder is to set generic linear codes + into the new Encoder structure by providing a valid ``generator_matrix`` + method. + + This encoder uses default implementations of ``encode`` and ``unencode``. + Its ``generator_matrix`` method returns private field ``_generator_matrix`` + of its associated code if any, else it calls the ``generator_matrix`` method + of the default encoder of the associated code. + + According to this behaviour, this encoder should never be used for other codes than + :class:`LinearCode`. + + INPUT: + + - ``code`` -- The associated code of this encoder. + """ + + def __init__(self, code): + r""" + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E + Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 + """ + super(LinearCodeGeneratorMatrixEncoder, self).__init__(code) + + def _repr_(self): + r""" + Returns a string representation of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E + Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 + """ + return "Generator matrix-based encoder for the %s" % self.code() + + def _latex_(self): + r""" + Returns a latex representation of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: latex(E) + \textnormal{Generator matrix-based encoder for the }[7, 4]\textnormal{ Linear code over }\Bold{F}_{2} + """ + return "\\textnormal{Generator matrix-based encoder for the }%s" % self.code()._latex_() + + @cached_method + def generator_matrix(self): + r""" + Returns a generator matrix of the associated code of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E.generator_matrix() + [1 1 1 0 0 0 0] + [1 0 0 1 1 0 0] + [0 1 0 1 0 1 0] + [1 1 0 1 0 0 1] + """ + if hasattr(self.code(), "_generator_matrix"): + return self.code()._generator_matrix + else: + return self.code().generator_matrix() diff --git a/src/sage/coding/linear_code_encoders.py b/src/sage/coding/linear_code_encoders.py deleted file mode 100644 index 720923c0307..00000000000 --- a/src/sage/coding/linear_code_encoders.py +++ /dev/null @@ -1,89 +0,0 @@ -r""" -Linear Code Encoders -""" - -#***************************************************************************** -# Copyright (C) 2015 David Lucas -# -# 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 encoder import Encoder -from sage.misc.cachefunc import cached_method - -class EncoderLinearCodeGeneratorMatrix(Encoder): - r""" - Encoder based on generator_matrix for Linear codes. - - The only purpose of this encoder is to include the existing code - into the new Encoder and Decoder structure. - - INPUT: - - - ``code`` -- The associated code of this encoder. - """ - - def __init__(self, code): - r""" - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E = EncoderLinearCodeGeneratorMatrix(C) - sage: E - Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 - """ - super(EncoderLinearCodeGeneratorMatrix, self).__init__(code) - - def _repr_(self): - r""" - Returns a string representation of ``self``. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E = EncoderLinearCodeGeneratorMatrix(C) - sage: E - Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 - """ - return "Generator matrix-based encoder for the %s" % self.code() - - def _latex_(self): - r""" - Returns a latex representation of ``self``. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E = EncoderLinearCodeGeneratorMatrix(C) - sage: latex(E) - \textnormal{Generator matrix-based encoder for the }[7, 4]\textnormal{ Linear code over }\Bold{F}_{2} - """ - return "\\textnormal{Generator matrix-based encoder for the }%s" % self.code()._latex_() - - @cached_method - def generator_matrix(self): - r""" - Returns a generator matrix of the associated code of ``self``. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E = EncoderLinearCodeGeneratorMatrix(C) - sage: E.generator_matrix() - [1 1 1 0 0 0 0] - [1 0 0 1 1 0 0] - [0 1 0 1 0 1 0] - [1 1 0 1 0 0 1] - """ - if hasattr(self.code(), "_generator_matrix"): - return self.code()._generator_matrix - else: - return self.code().generator_matrix() From bad97158f1e6b46821bb311422c2e0f73b06319d Mon Sep 17 00:00:00 2001 From: David Lucas Date: Thu, 7 May 2015 14:47:23 +0200 Subject: [PATCH 074/421] Hid encoders under codes.encoders. --- src/doc/en/reference/coding/index.rst | 3 ++- src/sage/coding/codes_catalog.py | 2 ++ src/sage/coding/encoder.py | 24 ++++++++++++------------ src/sage/coding/linear_code.py | 8 ++++---- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/doc/en/reference/coding/index.rst b/src/doc/en/reference/coding/index.rst index 31b91f7dd89..6311e17f696 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -7,8 +7,9 @@ Coding Theory :maxdepth: 1 sage/coding/encoder - sage/coding/channels_catalog + sage/coding/encoders_catalog sage/coding/channel_constructions + sage/coding/channels_catalog sage/coding/codes_catalog sage/coding/linear_code sage/coding/code_constructions diff --git a/src/sage/coding/codes_catalog.py b/src/sage/coding/codes_catalog.py index edbd32e5dac..d90ebe36f04 100644 --- a/src/sage/coding/codes_catalog.py +++ b/src/sage/coding/codes_catalog.py @@ -52,3 +52,5 @@ ToricCode, TrivialCode, WalshCode) from guava import BinaryReedMullerCode, QuasiQuadraticResidueCode, RandomLinearCodeGuava + +import encoders_catalog as encoders diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index a1b6419f55b..1b2be944dbf 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -106,13 +106,13 @@ def __eq__(self, other): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) - sage: E1 = LinearCodeGeneratorMatrixEncoder(C) - sage: E2 = LinearCodeGeneratorMatrixEncoder(C) + sage: E1 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) + sage: E2 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E1 == E2 True sage: G = Matrix(GF(3), [[2,1,1,0,0,0,1],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,1,1,0,0,1]]) sage: C1 = LinearCode(G) - sage: E2 = LinearCodeGeneratorMatrixEncoder(C1) + sage: E2 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C1) sage: E1 == E2 False """ @@ -126,13 +126,13 @@ def __ne__(self, other): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) - sage: E1 = LinearCodeGeneratorMatrixEncoder(C) - sage: E2 = LinearCodeGeneratorMatrixEncoder(C) + sage: E1 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) + sage: E2 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E1 != E2 False sage: G = Matrix(GF(3), [[2,1,1,0,0,0,1],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,1,1,0,0,1]]) sage: C1 = LinearCode(G) - sage: E2 = LinearCodeGeneratorMatrixEncoder(C1) + sage: E2 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C1) sage: E1 != E2 True """ @@ -159,7 +159,7 @@ def encode(self, word): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: word = vector((0, 1, 1, 0)) - sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.encode(word) (1, 1, 0, 0, 1, 1, 0) """ @@ -186,7 +186,7 @@ def unencode(self, c, nocheck=False, **kwargs): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) - sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.unencode(c) (0, 1, 1, 0) """ @@ -218,7 +218,7 @@ def unencoder_matrix(self): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: E = C.encoder() - sage: E._unencoder_matrix() + sage: E.unencoder_matrix() [0 0 1 1] [0 1 0 1] [1 1 1 0] @@ -254,7 +254,7 @@ def unencode_nocheck(self, c, **kwargs): sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) sage: c in C True - sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.unencode_nocheck(c) (0, 1, 1, 0) @@ -263,7 +263,7 @@ def unencode_nocheck(self, c, **kwargs): sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 1)) sage: c in C False - sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.unencode_nocheck(c) (0, 1, 1, 0) sage: m = vector(GF(2), (0, 1, 1, 0)) @@ -271,7 +271,7 @@ def unencode_nocheck(self, c, **kwargs): sage: c == c1 False """ - U = self._unencoder_matrix() + U = self.unencoder_matrix() info_set = self.code().information_set() cc = vector( c[i] for i in info_set ) return cc * U diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index b26bdb9abe3..00e35d784d2 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -3656,7 +3656,7 @@ def __init__(self, code): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) - sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 """ @@ -3670,7 +3670,7 @@ def _repr_(self): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) - sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 """ @@ -3684,7 +3684,7 @@ def _latex_(self): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) - sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: latex(E) \textnormal{Generator matrix-based encoder for the }[7, 4]\textnormal{ Linear code over }\Bold{F}_{2} """ @@ -3699,7 +3699,7 @@ def generator_matrix(self): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) - sage: E = LinearCodeGeneratorMatrixEncoder(C) + sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.generator_matrix() [1 1 1 0 0 0 0] [1 0 0 1 1 0 0] From d063050fa1e0abe4506eaa30fa911ba7791a3a97 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 7 May 2015 09:30:35 -0700 Subject: [PATCH 075/421] Fix doctest from update to Coxeter groups. --- src/sage/combinat/colored_permutations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index 0d3699b947d..9b89fda5806 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -744,7 +744,7 @@ class SignedPermutations(ColoredPermutations): This is a finite Coxeter group of type `B_n`:: sage: S.canonical_representation() - Coxeter group over Universal Cyclotomic Field with Coxeter matrix: + Finite Coxeter group over Universal Cyclotomic Field with Coxeter matrix: [1 3 2 2] [3 1 3 2] [2 3 1 4] From 5f32618962e05ffec650d973c10328ea8cc6bd85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 12 May 2015 12:32:53 +0200 Subject: [PATCH 076/421] trac #17411 a few doc typo corrections and also some pep8 code changes --- src/sage/combinat/colored_permutations.py | 52 ++++++++++++----------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index 9b89fda5806..a698eedbbd4 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -149,7 +149,7 @@ def __iter__(self): sage: list(x) [(1, 3), (0, 1), (0, 2)] """ - for i,p in enumerate(self._perm): + for i, p in enumerate(self._perm): yield (self._colors[i], p) def one_line_form(self): @@ -218,10 +218,12 @@ def to_matrix(self): """ Cp = CyclotomicField(self.parent()._m) g = Cp.gen() - D = diagonal_matrix(Cp, [g**i for i in self._colors]) + D = diagonal_matrix(Cp, [g ** i for i in self._colors]) return self._perm.to_matrix() * D -# TODO: Parts of this should be put in the category of complex reflection groups + +# TODO: Parts of this should be put in the category of complex +# reflection groups class ColoredPermutations(Parent, UniqueRepresentation): r""" The group of `m`-colored permutations on `\{1, 2, \ldots, n\}`. @@ -319,7 +321,7 @@ def gens(self): [[0, 0, 0], [1, 3, 2]], [[0, 0, 1], [1, 2, 3]]) """ - zero = [self._C.zero()]*self._n + zero = [self._C.zero()] * self._n g = [] for i in range(self._n-1): p = range(1, self._n+1) @@ -408,7 +410,7 @@ def cardinality(self): sage: C.cardinality() == 4**3 * factorial(3) True """ - return self._m**self._n * self._P.cardinality() + return self._m ** self._n * self._P.cardinality() def rank(self): """ @@ -461,9 +463,9 @@ def degrees(self): sage: prod(S.degrees()) == S.cardinality() True """ - if self._m == 1: # Special case for the usual symmetric group - return range(2, self._n+1) - return [self._m * i for i in range(1, self._n+1)] + if self._m == 1: # Special case for the usual symmetric group + return range(2, self._n + 1) + return [self._m * i for i in range(1, self._n + 1)] def codegrees(self): """ @@ -503,8 +505,8 @@ def codegrees(self): sage: f == g True """ - if self._m == 1: # Special case for the usual symmetric group - return list(reversed(range(self._n-1))) + if self._m == 1: # Special case for the usual symmetric group + return list(reversed(range(self._n - 1))) return [self._m * i for i in reversed(range(self._n))] def number_reflection_hyperplanes(self): @@ -512,7 +514,7 @@ def number_reflection_hyperplanes(self): Return the number of reflection hyperplanes of ``self``. The number of reflection hyperplanes of a complex reflection - group is equal to the sume of the codegrees plus the rank. + group is equal to the sum of the codegrees plus the rank. EXAMPLES:: @@ -533,7 +535,7 @@ def fixed_point_polynomial(self, q=None): The fixed point polynomial of ``self``. The fixed point polynomial `f_G` of a complex reflection group `G` is - counting the dimesions fixed points subspaces: + counting the dimensions of fixed points subspaces: .. MATH:: @@ -595,13 +597,14 @@ def is_well_generated(self): """ deg = self.degrees() dstar = self.codegrees() - return all(deg[-1] == d + dstar[i] for i,d in enumerate(deg)) + return all(deg[-1] == d + dstar[i] for i, d in enumerate(deg)) Element = ColoredPermutation ##################################################################### ## Signed permutations + class SignedPermutation(ColoredPermutation): """ A signed permutation. @@ -642,7 +645,7 @@ def _mul_(self, other): True """ colors = tuple(self._colors[i] * other._colors[val-1] #-1 for indexing - for i,val in enumerate(self._perm)) + for i, val in enumerate(self._perm)) p = self._perm._left_to_right_multiply_on_right(other._perm) return self.__class__(self.parent(), colors, p) @@ -679,7 +682,7 @@ def __iter__(self): sage: [a for a in x] [-4, 1, 2, -3] """ - for i,p in enumerate(self._perm): + for i, p in enumerate(self._perm): yield self._colors[i] * p def to_matrix(self): @@ -720,10 +723,11 @@ def has_left_descent(self, i): n = self.parent()._n if i == n: return self._colors[-1] == -1 - if self._colors[i-1] == -1: + if self._colors[i - 1] == -1: return self._colors[i] == 1 or self._perm[i-1] < self._perm[i] return self._colors[i] == 1 and self._perm[i-1] > self._perm[i] + class SignedPermutations(ColoredPermutations): r""" Group of signed permutations. @@ -791,7 +795,8 @@ def one(self): sage: S.one() [1, 2, 3, 4] """ - return self.element_class(self, [ZZ.one()]*self._n, self._P.identity()) + return self.element_class(self, [ZZ.one()] * self._n, + self._P.identity()) def simple_reflection(self, i): r""" @@ -810,11 +815,11 @@ def simple_reflection(self, i): if i not in self.index_set(): raise ValueError("i must be in the index set") if i < self._n: - p = range(1, self._n+1) - p[i-1] = i+1 + p = range(1, self._n + 1) + p[i - 1] = i + 1 p[i] = i return self.element_class(self, [ZZ.one()]*self._n, self._P(p)) - temp = [ZZ.one()]*self._n + temp = [ZZ.one()] * self._n temp[-1] = -ZZ.one() return self.element_class(self, temp, self._P.identity()) @@ -889,7 +894,7 @@ def __iter__(self): [2, 1], [2, -1], [-2, 1], [-2, -1]] """ one = ZZ.one() - C = CartesianProduct(*[[one,-one]]*self._n) + C = CartesianProduct(*[[one,-one]] * self._n) for p in self._P: for c in C: yield self.element_class(self, c, p) @@ -930,7 +935,7 @@ def long_element(self, index_set=None): INPUT: - - ``index_set`` -- (optioal) a subset (as a list or iterable) + - ``index_set`` -- (optional) a subset (as a list or iterable) of the nodes of the indexing set EXAMPLES:: @@ -942,7 +947,7 @@ def long_element(self, index_set=None): if index_set is not None: return super(SignedPermutations, self).long_element() p = range(self._n, 0, -1) - return self.element_class(self, [-ZZ.one()]*self._n, self._P(p)) + return self.element_class(self, [-ZZ.one()] * self._n, self._P(p)) Element = SignedPermutation @@ -969,4 +974,3 @@ def long_element(self, index_set=None): # # if total % 2 == 0: # yield s - From 66a2290ff1013646e7aa6de18a5d4a846fe53f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 12 May 2015 14:58:08 +0200 Subject: [PATCH 077/421] trac #17411 final pep8 cleanup --- src/sage/combinat/colored_permutations.py | 60 ++++++++++++++--------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index a698eedbbd4..912e0717643 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -1,5 +1,10 @@ r""" Colored Permutations + +.. TODO:: + + Much of the colored permutations (and element) class can be + generalized to `G \wr S_n` """ from sage.categories.groups import Groups from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -18,8 +23,7 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.all import ZZ -# TODO: Much of the colored permutations (and element) class can be -# generalized to `G \wr S_n` + class ColoredPermutation(MultiplicativeGroupElement): """ A colored permutation. @@ -64,7 +68,7 @@ def _latex_(self): """ ret = "[" ret += ", ".join("{}_{{{}}}".format(x, self._colors[i]) - for i,x in enumerate(self._perm)) + for i, x in enumerate(self._perm)) return ret + "]" def _mul_(self, other): @@ -78,8 +82,8 @@ def _mul_(self, other): sage: s1*s2*s1 == s2*s1*s2 True """ - colors = tuple(self._colors[i] + other._colors[val-1] #-1 for indexing - for i,val in enumerate(self._perm)) + colors = tuple(self._colors[i] + other._colors[val - 1] # -1 for indexing + for i, val in enumerate(self._perm)) p = self._perm._left_to_right_multiply_on_right(other._perm) return self.__class__(self.parent(), colors, p) @@ -98,7 +102,7 @@ def __invert__(self): """ ip = ~self._perm return self.__class__(self.parent(), - tuple([-self._colors[i-1] for i in ip]), #-1 for indexing + tuple([-self._colors[i - 1] for i in ip]), # -1 for indexing ip) def __eq__(self, other): @@ -186,6 +190,8 @@ def permutation(self): """ Return the permutation of ``self``. + This is obtained by forgetting the colors. + EXAMPLES:: sage: C = ColoredPermutations(4, 3) @@ -200,6 +206,8 @@ def to_matrix(self): """ Return a matrix of ``self``. + The colors are mapped to roots of unity. + EXAMPLES:: sage: C = ColoredPermutations(4, 3) @@ -306,7 +314,8 @@ def one(self): sage: C.one() [[0, 0, 0], [1, 2, 3]] """ - return self.element_class(self, [self._C.zero()]*self._n, self._P.identity()) + return self.element_class(self, [self._C.zero()] * self._n, + self._P.identity()) @cached_method def gens(self): @@ -323,13 +332,13 @@ def gens(self): """ zero = [self._C.zero()] * self._n g = [] - for i in range(self._n-1): - p = range(1, self._n+1) - p[i] = i+2 - p[i+1] = i+1 - g.append( self.element_class(self, zero, self._P(p)) ) + for i in range(self._n - 1): + p = range(1, self._n + 1) + p[i] = i + 2 + p[i + 1] = i + 1 + g.append(self.element_class(self, zero, self._P(p))) zero[-1] = self._C.one() - g.append( self.element_class(self, zero, self._P.identity()) ) + g.append(self.element_class(self, zero, self._P.identity())) return tuple(g) def matrix_group(self): @@ -353,6 +362,11 @@ def _element_constructor_(self, x): """ Construct an element of ``self`` from ``x``. + INPUT: + + either a list of pairs (color, element) + or a pair of lists (colors, elements) + TESTS:: sage: C = ColoredPermutations(4, 3) @@ -368,7 +382,7 @@ def _element_constructor_(self, x): for k in x: if len(k) != 2: raise ValueError("input must be pairs (color, element)") - c.append( self._C(k[0]) ) + c.append(self._C(k[0])) p.append(k[1]) return self.element_class(self, c, self._P(p)) @@ -393,7 +407,7 @@ def __iter__(self): [[1, 0], [2, 1]], [[1, 1], [2, 1]]] """ - C = CartesianProduct(*[self._C]*self._n) + C = CartesianProduct(*[self._C] * self._n) for p in self._P: for c in C: yield self.element_class(self, c, p) @@ -644,7 +658,7 @@ def _mul_(self, other): sage: s3*s4*s3*s4 == s4*s3*s4*s3 True """ - colors = tuple(self._colors[i] * other._colors[val-1] #-1 for indexing + colors = tuple(self._colors[i] * other._colors[val - 1] # -1 for indexing for i, val in enumerate(self._perm)) p = self._perm._left_to_right_multiply_on_right(other._perm) return self.__class__(self.parent(), colors, p) @@ -665,7 +679,7 @@ def inverse(self): """ ip = ~self._perm return self.__class__(self.parent(), - tuple([self._colors[i-1] for i in ip]), #-1 for indexing + tuple([self._colors[i - 1] for i in ip]), # -1 for indexing ip) __invert__ = inverse @@ -724,8 +738,8 @@ def has_left_descent(self, i): if i == n: return self._colors[-1] == -1 if self._colors[i - 1] == -1: - return self._colors[i] == 1 or self._perm[i-1] < self._perm[i] - return self._colors[i] == 1 and self._perm[i-1] > self._perm[i] + return self._colors[i] == 1 or self._perm[i - 1] < self._perm[i] + return self._colors[i] == 1 and self._perm[i - 1] > self._perm[i] class SignedPermutations(ColoredPermutations): @@ -818,7 +832,7 @@ def simple_reflection(self, i): p = range(1, self._n + 1) p[i - 1] = i + 1 p[i] = i - return self.element_class(self, [ZZ.one()]*self._n, self._P(p)) + return self.element_class(self, [ZZ.one()] * self._n, self._P(p)) temp = [ZZ.one()] * self._n temp[-1] = -ZZ.one() return self.element_class(self, temp, self._P.identity()) @@ -859,7 +873,7 @@ def _element_constructor_(self, x): raise ValueError("input must be pairs (sign, element)") if k[0] != 1 and k[0] != -1: raise ValueError("the sign must be +1 or -1") - c.append( ZZ(k[0]) ) + c.append(ZZ(k[0])) p.append(k[1]) return self.element_class(self, c, self._P(p)) @@ -894,7 +908,7 @@ def __iter__(self): [2, 1], [2, -1], [-2, 1], [-2, -1]] """ one = ZZ.one() - C = CartesianProduct(*[[one,-one]] * self._n) + C = CartesianProduct(*[[one, -one]] * self._n) for p in self._P: for c in C: yield self.element_class(self, c, p) @@ -910,7 +924,7 @@ def index_set(self): sage: S.index_set() (1, 2, 3, 4) """ - return tuple(range(1, self._n+1)) + return tuple(range(1, self._n + 1)) def coxeter_matrix(self): """ From 0350849cd7f02b23156de843e2dadcead7d4baa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 12 May 2015 15:04:05 +0200 Subject: [PATCH 078/421] trac #17411 insertion in module_list --- src/doc/en/reference/combinat/module_list.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 2b6bb9989b7..7bdb55c6626 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -35,6 +35,7 @@ Comprehensive Module list sage/combinat/cluster_algebra_quiver/mutation_type sage/combinat/cluster_algebra_quiver/quiver sage/combinat/cluster_algebra_quiver/quiver_mutation_type + sage/combinat/colored_permutations sage/combinat/combinat sage/combinat/combinat_cython sage/combinat/combination From 0b56cd3df944345e4578570cba8df571f1a158b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 12 May 2015 17:05:29 +0200 Subject: [PATCH 079/421] trac #17411 one typo found --- src/sage/combinat/colored_permutations.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index 912e0717643..3a501b65ebf 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -814,9 +814,7 @@ def one(self): def simple_reflection(self, i): r""" - Return the ``i``-th simple refection of ``self``. - - Return the generators of ``self``. + Return the ``i``-th simple reflection of ``self``. EXAMPLES:: From 24b4b4fe83f7453c155714439ff255e576e5f652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 12 May 2015 17:08:40 +0200 Subject: [PATCH 080/421] trac #17411 lazy import --- src/sage/combinat/all.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index a390a0896f9..b27e2a75902 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -36,7 +36,8 @@ #Permutations from permutation import Permutation, Permutations, Arrangements, PermutationOptions, CyclicPermutations, CyclicPermutationsOfPartition from affine_permutation import AffinePermutationGroup -from colored_permutations import ColoredPermutations, SignedPermutations +lazy_import('sage.combinat.colored_permutations', ['ColoredPermutations', + 'SignedPermutations']) from derangements import Derangements lazy_import('sage.combinat.baxter_permutations', ['BaxterPermutations']) From ebc2a65c5d377cc1e7872201709fabcec3e9e4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 17 May 2015 20:20:50 +0200 Subject: [PATCH 081/421] trac #17096 fixing two doctests --- src/sage/combinat/ncsf_qsym/generic_basis_code.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/ncsf_qsym/generic_basis_code.py b/src/sage/combinat/ncsf_qsym/generic_basis_code.py index 571dd69036e..2e062819317 100644 --- a/src/sage/combinat/ncsf_qsym/generic_basis_code.py +++ b/src/sage/combinat/ncsf_qsym/generic_basis_code.py @@ -940,15 +940,16 @@ def degree(self): sage: S.zero().degree() Traceback (most recent call last): ... - ValueError: The zero element does not have a well-defined degree. + ValueError: the zero element does not have a well-defined degree sage: F = QuasiSymmetricFunctions(QQ).F() sage: F.zero().degree() Traceback (most recent call last): ... - ValueError: The zero element does not have a well-defined degree. + ValueError: the zero element does not have a well-defined degree """ return self.maximal_degree() + class AlgebraMorphism(ModuleMorphismByLinearity): # Find a better name """ A class for algebra morphism defined on a free algebra from the image of the generators From c5281c075bf4e196aea84cafa2c54d119ee6cd7e Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Thu, 21 May 2015 17:45:32 +0200 Subject: [PATCH 082/421] #18338 : return a polynomial over ZZ. --- src/sage/combinat/combinat.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 0dd9bc3df5f..ef788e7ccc0 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -2693,7 +2693,7 @@ def bell_polynomial(n, k): OUTPUT: - - a polynomial in `n-k+1` variables over `\QQ` + - a polynomial in `n-k+1` variables over `\ZZ` EXAMPLES:: @@ -2707,7 +2707,7 @@ def bell_polynomial(n, k): Check that :trac:`18338` is fixed:: sage: bell_polynomial(0,0).parent() - Multivariate Polynomial Ring in x over Rational Field + Multivariate Polynomial Ring in x over Integer Ring sage: for n in (0..4): ....: print [bell_polynomial(n,k).coefficients() for k in (0..n)] @@ -2730,7 +2730,7 @@ def bell_polynomial(n, k): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.combinat.partition import Partitions from sage.rings.arith import factorial - R = PolynomialRing(QQ, 'x', n-k+1) + R = PolynomialRing(ZZ, 'x', n-k+1) vars = R.gens() result = R.zero() for p in Partitions(n, length=k): @@ -2741,7 +2741,7 @@ def bell_polynomial(n, k): power_factorial_product *= factorial(part)**count coefficient = factorial(n) / (factorial_product * power_factorial_product) result += coefficient * prod([vars[i - 1] for i in p]) - return result + return R(result) def fibonacci_sequence(start, stop=None, algorithm=None): r""" From 3715880126fce2176ca4f12ab700d3e1cf5ea804 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Fri, 29 May 2015 11:25:24 +0200 Subject: [PATCH 083/421] Fix related to encoders_catalog file --- src/sage/coding/encoders_catalog.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/sage/coding/encoders_catalog.py diff --git a/src/sage/coding/encoders_catalog.py b/src/sage/coding/encoders_catalog.py new file mode 100644 index 00000000000..743e021b3a7 --- /dev/null +++ b/src/sage/coding/encoders_catalog.py @@ -0,0 +1,16 @@ +r""" +Index of encoders + +The ``codes.encoders`` object may be used to access the codes that Sage can build. + +- :func:`linear_code.LinearCodeGeneratorMatrixEncoder + + +.. NOTE:: + + To import these names into the global namespace, use: + + sage: from sage.coding.encoders_catalog import * +""" + +from linear_code import (LinearCodeGeneratorMatrixEncoder) From f447b9aaa45eb900812c8e21f7a6e601d1d422d2 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Fri, 29 May 2015 12:47:58 +0200 Subject: [PATCH 084/421] Updated documentation in encoder --- src/sage/coding/encoder.py | 42 ++++++++++++++--------------- src/sage/coding/encoders_catalog.py | 5 ++-- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 1b2be944dbf..1eea44019fd 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -21,10 +21,9 @@ class Encoder(SageObject): r""" - Abstract top-class for Encoder objects. + Abstract top-class for :class:`Encoder` objects. - This class contains all methods that can be used by encoders. - So, every encoder class should inherit from this abstract class. + Every encoder class should inherit from this abstract class. This class provides: @@ -34,21 +33,21 @@ class Encoder(SageObject): To implement an encoder, you need to: - - inherit from Encoder + - inherit from :class:`Encoder` - - call Encoder ``__init__`` method in the subclass constructor. Example: - ``super(SubclassName, self).__init__(code)``. + - call :class:`Encoder`'s :meth:`__init__` in the subclass constructor. + Example: ``super(SubclassName, self).__init__(code)``. By doing that, your subclass will have its ``code`` parameter initialized. You need of course to complete the constructor by adding any additional parameter needed to describe properly the code defined in the subclass. - Then, if the message space is a vectorial space, default implementation of ``encode`` and - ``unencode_nocheck`` methods are provided. These implementations rely on ``generator_matrix`` + Then, if the message space is a vectorial space, default implementation of :meth:`encode` and + :meth:`unencode_nocheck` methods are provided. These implementations rely on :meth:`generator_matrix` which you need to override to use the default implementations. If the message space is not of the form `F^k`, where `F` is a finite field, you cannot have a generator matrix. - In that case, you need to override ``encode`` and ``unencode_nocheck``. + In that case, you need to override :meth:`encode` and :meth:`unencode_nocheck`. Equality methods (``__eq__`` and ``__ne__``) might be useful for encoding in advanced codes constructions (like concatenated codes). If provided default implementation of @@ -61,13 +60,13 @@ class Encoder(SageObject): for a new encoder named ``EncName``, for code family ``CodeFam``, call it ``CodeFamEncNameEncoder``. - As Encoder is not designed to be implemented, it does not have any representation + As :class:`Encoder` is not designed to be implemented, it does not have any representation methods. You should implement ``_repr_`` and ``_latex_`` methods in the sublclass. """ def __init__(self, code): r""" - Initializes mandatory parameters for an Encoder object. + Initializes mandatory parameters for an :class:`Encoder` object. This method only exists for inheritance purposes as it initializes parameters that need to be known by every linear code. An abstract @@ -79,7 +78,7 @@ def __init__(self, code): EXAMPLES: - We first create a new Encoder subclass:: + We first create a new :class:`Encoder` subclass:: sage: class EncoderExample(sage.coding.encoder.Encoder): ....: def __init__(self, code): @@ -143,7 +142,8 @@ def encode(self, word): Transforms an element of the message space into an element of the code. This is a default implementation which assumes that the message - space of the encoder is a Vector Space. If this is not the case, + space of the encoder is `F^k`, where `F` is ``self.code().base_field()`` + and ``k`` is ``self.code().dimension()``. If this is not the case, this method should be overwritten by the subclass. INPUT: @@ -174,8 +174,8 @@ def unencode(self, c, nocheck=False, **kwargs): - ``c`` -- a vector of the same length as ``self`` over the base field of ``self`` - - ``nocheck`` -- (default: ``False``) checks if ``c`` is in self. If this is set - to True, the return value of this method is not guaranteed to be correct. + - ``nocheck`` -- (default: ``False``) checks if ``c`` is in ``self``. If this is set + to ``True``, the return value of this method is not guaranteed to be correct. OUTPUT: @@ -201,8 +201,8 @@ def unencode(self, c, nocheck=False, **kwargs): @cached_method def unencoder_matrix(self): r""" - Finds an information set for G, and returns the inverse of those - columns of G. + Finds an information set for the matrix ``G`` returned by :meth:`generator_matrix`, + and returns the inverse of that submatrix of ``G``. .. NOTE:: @@ -231,7 +231,7 @@ def unencode_nocheck(self, c, **kwargs): r""" Returns the message corresponding to ``c``. - When c is not a codeword, the output is unspecified. + When ``c`` is not a codeword, the output is unspecified. AUTHORS: @@ -293,7 +293,7 @@ def code(self): def message_space(self): r""" Returns the ambient space of allowed input to ``self.encode()``. - Note that the ``self.encode()`` is possibly a partial function over + Note that `self.encode()` is possibly a partial function over the ambient space. EXAMPLES:: @@ -309,10 +309,10 @@ def message_space(self): @abstract_method(optional = True) def generator_matrix(self): r""" - Returns a generator matrix of the associated code of self. + Returns a generator matrix of the associated code of ``self``. This is an abstract method and it should be implemented separately. - Reimplementing this for each subclass of Encoder is not mandatory + Reimplementing this for each subclass of :class:`Encoder` is not mandatory (as encoders with a polynomial message space, for instance, do not need a generator matrix). """ diff --git a/src/sage/coding/encoders_catalog.py b/src/sage/coding/encoders_catalog.py index 743e021b3a7..23c0ecb7998 100644 --- a/src/sage/coding/encoders_catalog.py +++ b/src/sage/coding/encoders_catalog.py @@ -1,10 +1,9 @@ r""" Index of encoders -The ``codes.encoders`` object may be used to access the codes that Sage can build. - -- :func:`linear_code.LinearCodeGeneratorMatrixEncoder +The ``codes.encoders`` object may be used to access the encoders that Sage can build. +- :func:`linear_code.LinearCodeGeneratorMatrixEncoder ` .. NOTE:: From 55d9a5ef6747d4cac7df581af41c126a676fa280 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Sun, 31 May 2015 22:23:34 +0200 Subject: [PATCH 085/421] #18338 coefficients are integers. --- src/sage/combinat/combinat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index ef788e7ccc0..300d6ddf0f0 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -2739,9 +2739,9 @@ def bell_polynomial(n, k): for part, count in p.to_exp_dict().iteritems(): factorial_product *= factorial(count) power_factorial_product *= factorial(part)**count - coefficient = factorial(n) / (factorial_product * power_factorial_product) + coefficient = factorial(n) // (factorial_product * power_factorial_product) result += coefficient * prod([vars[i - 1] for i in p]) - return R(result) + return result def fibonacci_sequence(start, stop=None, algorithm=None): r""" From 188b56f3367da3bcd1e5298dc012c663f5f08025 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Tue, 9 Jun 2015 13:52:22 +0200 Subject: [PATCH 086/421] Minor changes --- src/sage/coding/encoder.py | 65 ++++++++++------------------------ src/sage/coding/linear_code.py | 32 ++++++----------- 2 files changed, 29 insertions(+), 68 deletions(-) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 1eea44019fd..6c8cb90305e 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -97,46 +97,6 @@ def __init__(self, code): """ self._code = code - def __eq__(self, other): - r""" - Checks equality between ``self`` and ``other``. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E1 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) - sage: E2 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) - sage: E1 == E2 - True - sage: G = Matrix(GF(3), [[2,1,1,0,0,0,1],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,1,1,0,0,1]]) - sage: C1 = LinearCode(G) - sage: E2 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C1) - sage: E1 == E2 - False - """ - return self.code() == other.code() - - def __ne__(self, other): - r""" - Checks difference between ``self`` and ``other``. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E1 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) - sage: E2 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) - sage: E1 != E2 - False - sage: G = Matrix(GF(3), [[2,1,1,0,0,0,1],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,1,1,0,0,1]]) - sage: C1 = LinearCode(G) - sage: E2 = codes.encoders.LinearCodeGeneratorMatrixEncoder(C1) - sage: E1 != E2 - True - """ - return not self.__eq__(other) - def encode(self, word): r""" Transforms an element of the message space into an element of the code. @@ -158,14 +118,25 @@ def encode(self, word): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) - sage: word = vector((0, 1, 1, 0)) + sage: word = vector(GF(2), (0, 1, 1, 0)) sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.encode(word) (1, 1, 0, 0, 1, 1, 0) + + If ``word`` is not in the message space of ``self``, it will return an exception:: + + sage: word = random_vector(GF(7), 4) + sage: E.encode(word) + Traceback (most recent call last): + ... + ValueError: Vector to encode must be in a Vector space of dimension 4 over Finite Field of size 2 """ + M = self.message_space() + if word not in M: + raise ValueError("Vector to encode must be in a %s" % M) return vector(word) * self.generator_matrix() - def unencode(self, c, nocheck=False, **kwargs): + def unencode(self, c, nocheck=False): r""" Returns the message corresponding to ``c``. @@ -194,9 +165,9 @@ def unencode(self, c, nocheck=False, **kwargs): if c not in self.code(): raise EncodingFailure("Given word is not in the code") else: - return self.unencode_nocheck(c, **kwargs) + return self.unencode_nocheck(c) else: - return self.unencode_nocheck(c, **kwargs) + return self.unencode_nocheck(c) @cached_method def unencoder_matrix(self): @@ -227,7 +198,7 @@ def unencoder_matrix(self): Gt = self.generator_matrix().matrix_from_columns(self.code().information_set()) return Gt.inverse() - def unencode_nocheck(self, c, **kwargs): + def unencode_nocheck(self, c): r""" Returns the message corresponding to ``c``. @@ -313,8 +284,8 @@ def generator_matrix(self): This is an abstract method and it should be implemented separately. Reimplementing this for each subclass of :class:`Encoder` is not mandatory - (as encoders with a polynomial message space, for instance, do not - need a generator matrix). + (as a generator matrix only makes sense when the message space is of the `F^k`, + where `F` is the base field of :meth:`code`.) """ class EncodingFailure(Exception): diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 2f3657c6078..5d3ad8455f9 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -744,6 +744,12 @@ class AbstractLinearCode(module.Module): .. NOTE:: + AbstractLinearCode embeds some generic implementations of helper methods like ``__cmp__`` + or ``__eq__``. + As they are designed to fit for every linear code, they mostly use the generator matrix + and thus can be long for certain families of code. + In that case, overriding these methods is encouraged. + A lot of methods of the abstract class rely on the knowledge of a generator matrix. It is thus strongly recommended to set an encoder with a generator matrix implemented as a default encoder. @@ -880,7 +886,7 @@ def _an_element_(self): """ return self.gens()[0] - def add_encoder(self, name, encoder, check=True): + def add_encoder(self, name, encoder): r""" Adds an encoder to the list of registered encoders of ``self``. @@ -890,9 +896,6 @@ def add_encoder(self, name, encoder, check=True): - ``encoder`` -- the class name of the encoder - - ``check`` -- (default: ``True``) if true, checks if ``name`` or ``encoder`` - are already in the list of registered encoders, and raises an error if yes - EXAMPLES: First of all, we create a (very basic) new encoder:: @@ -921,29 +924,16 @@ def add_encoder(self, name, encoder, check=True): TESTS: - If ``check`` is True, it is impossible to use a name which is in - the dictionnary of available encoders:: + It is impossible to use a name which is in the dictionnary of available encoders:: sage: C.add_encoder("GeneratorMatrix", MyEncoder) Traceback (most recent call last): ... ValueError: There is already a registered encoder with this name - - But if ``check`` is set to false, overriding names is authorized:: - - sage: C.encoders_available(True) - [('MyEncoder', ), - ('GeneratorMatrix', - )] - sage: C.add_encoder("GeneratorMatrix", MyEncoder, False) - sage: C.encoders_available(True) - [('MyEncoder', ), - ('GeneratorMatrix', )] """ reg_enc = self._registered_encoders - if check==True: - if(name in reg_enc.keys()): - raise ValueError("There is already a registered encoder with this name") + if(name in reg_enc.keys()): + raise ValueError("There is already a registered encoder with this name") reg_enc[name] = encoder def automorphism_group_gens(self, equivalence="semilinear"): @@ -2126,7 +2116,7 @@ def __iter__(self): FiniteFieldsubspace_iterator return FiniteFieldsubspace_iterator(self.generator_matrix(), immutable=True) - + @cached_method def information_set(self): """ Return an information set of the code. From 12dd9fe9073fef76e81f841466f860c899a5e027 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Tue, 9 Jun 2015 19:46:23 +0200 Subject: [PATCH 087/421] #18338 : update docstring formula. --- src/sage/combinat/combinat.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 300d6ddf0f0..dbbd38019ef 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -2681,9 +2681,9 @@ def bell_polynomial(n, k): .. MATH:: - B_{n,k}(x_1, x_2, \ldots, x_{n-k+1}) = \sum_{\sum{j_i}=k, \sum{i j_i} - =n} \frac{n!}{j_1!j_2!\cdots} \frac{x_1}{1!}^j_1 \frac{x_2}{2!}^j_2 - \cdots. + B_{n,k}(x_0, x_1, \ldots, x_{n-k}) = \sum_{\sum{j_i}=k, \sum{(i+1) j_i} + =n} \frac{n!}{j_0!j_1!\cdots j_{n-k}!} \left(\frac{x_0}{(0+1)!}\right)^{j_0} \left(\frac{x_1}{(1+1)!}\right)^{j_1} + \cdots \left(\frac{x_{n-k}}{(n-k+1)!}\right)^{j_{n-k}}. INPUT: From 6de51b865d814879c98e0b3a7c4dbeed963dfb25 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Wed, 10 Jun 2015 08:39:20 +0200 Subject: [PATCH 088/421] #18338 reformat formula. --- src/sage/combinat/combinat.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index dbbd38019ef..38a08ef898a 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -2681,9 +2681,12 @@ def bell_polynomial(n, k): .. MATH:: - B_{n,k}(x_0, x_1, \ldots, x_{n-k}) = \sum_{\sum{j_i}=k, \sum{(i+1) j_i} - =n} \frac{n!}{j_0!j_1!\cdots j_{n-k}!} \left(\frac{x_0}{(0+1)!}\right)^{j_0} \left(\frac{x_1}{(1+1)!}\right)^{j_1} - \cdots \left(\frac{x_{n-k}}{(n-k+1)!}\right)^{j_{n-k}}. + B_{n,k}(x_0, x_1, \ldots, x_{n-k}) = + \sum_{\sum{j_i}=k, \sum{(i+1) j_i}=n} + \frac{n!}{j_0!j_1!\cdots j_{n-k}!} + \left(\frac{x_0}{(0+1)!}\right)^{j_0} + \left(\frac{x_1}{(1+1)!}\right)^{j_1} \cdots + \left(\frac{x_{n-k}}{(n-k+1)!}\right)^{j_{n-k}}. INPUT: From 20bd15dd22b857e41bbe50244112f6128c8082bb Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 19 Jun 2015 15:54:00 -0700 Subject: [PATCH 089/421] Added support for _repr_option('ascii_art'). --- src/sage/repl/display/fancy_repr.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/sage/repl/display/fancy_repr.py b/src/sage/repl/display/fancy_repr.py index 5c1aa6977fa..d8ec2165fd1 100644 --- a/src/sage/repl/display/fancy_repr.py +++ b/src/sage/repl/display/fancy_repr.py @@ -317,11 +317,36 @@ def __call__(self, obj, p, cycle): sage: format_list = TallListRepr().format_string sage: format_list([1, 2, identity_matrix(2)]) '[\n [1 0]\n1, 2, [0 1]\n]' + + Check that :trac:`18743` is fixed:: + + sage: class Foo(object): + ....: def __repr__(self): + ....: return '''BBB AA RRR + ....: B B A A R R + ....: BBB AAAA RRR + ....: B B A A R R + ....: BBB A A R R''' + ....: def _repr_option(self, key): + ....: return key == 'ascii_art' + sage: F = Foo() + sage: [F, F] + [ + BBB AA RRR BBB AA RRR + B B A A R R B B A A R R + BBB AAAA RRR BBB AAAA RRR + B B A A R R B B A A R R + BBB A A R R, BBB A A R R + ] """ if not (isinstance(obj, (tuple, list)) and len(obj) > 0): return False ascii_art_repr = False for o in obj: + try: + ascii_art_repr = ascii_art_repr or o._repr_option('ascii_art') + except (AttributeError, TypeError): + pass try: ascii_art_repr = ascii_art_repr or o.parent()._repr_option('element_ascii_art') except (AttributeError, TypeError): From 52d11e500036f996dd4e2440eca89cbbb7e214f2 Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Mon, 22 Jun 2015 14:39:55 -0500 Subject: [PATCH 090/421] Added morphism to SymmetricGroupAlgebra to DiagramAlgebra and registered it as coercion --- src/sage/combinat/diagram_algebras.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 93d55bcde01..17f2c81fa68 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -801,6 +801,9 @@ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): self._base_diagrams = diagrams if category is None: category = FiniteDimensionalAlgebrasWithBasis(base_ring) + KSS = SymmetricGroupAlgebra(base_ring, k) + to_DA = KSS.module_morphism(lambda i : self(self._perm_to_Blst(i)),codomain=self) + to_DA.register_as_coercion() CombinatorialFreeModule.__init__(self, base_ring, diagrams, category=category, prefix=prefix, bracket=False) @@ -850,6 +853,12 @@ def __getitem__(self, i): return self.basis()[i] raise ValueError("{0} is not an index of a basis element".format(i)) + def _perm_to_Blst(self, w): + ## 'perm' is a permutation in one-line notation + ## turns w into an expression suitable for the element constructor. + u = sorted(w) + return [[u[i],-w[i]] for i in range(len(w))] + def order(self): r""" Return the order of ``self``. From 169e666878335aebe8fec04618851d7f76d71124 Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Mon, 22 Jun 2015 18:36:15 -0500 Subject: [PATCH 091/421] added missing import statement to diagram algebras file --- src/sage/combinat/diagram_algebras.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 17f2c81fa68..8c3e0f1ccc5 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -25,10 +25,11 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.structure.list_clone import ClonableArray -from sage.combinat.set_partition import SetPartitions, SetPartition +from sage.combinat.combinat import (bell_number, catalan_number) from sage.combinat.partition import Partitions from sage.combinat.permutation import Permutation -from sage.combinat.combinat import (bell_number, catalan_number) +from sage.combinat.set_partition import SetPartitions, SetPartition +from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra from sage.sets.set import Set from sage.graphs.graph import Graph from sage.misc.cachefunc import cached_method From 28ee71f199a7162aa57408864e451f8a3477fcbf Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Mon, 22 Jun 2015 19:23:08 -0500 Subject: [PATCH 092/421] fixed the order of commands and import statements --- src/sage/combinat/diagram_algebras.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 8c3e0f1ccc5..72ad8f2c17d 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -35,6 +35,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.flatten import flatten from sage.rings.all import ZZ +from sage.rings.rational_field import RationalField import math import operator @@ -802,12 +803,12 @@ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): self._base_diagrams = diagrams if category is None: category = FiniteDimensionalAlgebrasWithBasis(base_ring) - KSS = SymmetricGroupAlgebra(base_ring, k) - to_DA = KSS.module_morphism(lambda i : self(self._perm_to_Blst(i)),codomain=self) - to_DA.register_as_coercion() CombinatorialFreeModule.__init__(self, base_ring, diagrams, category=category, prefix=prefix, bracket=False) + KSS = SymmetricGroupAlgebra(RationalField(), k) # QQ probably should not be hardcoded here. + KSS.module_morphism(lambda i : self(self._perm_to_Blst(i)), codomain=self).register_as_coercion() + def _element_constructor_(self, set_partition): r""" Construct an element of ``self``. From 69bd5dbdd4cb11875dc5ab79cd3a8735633f3226 Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Mon, 22 Jun 2015 19:43:56 -0500 Subject: [PATCH 093/421] changed the ring of the symmetric group algebra created for the morphism from sga to diagram algebra to be the base ring of the diagram algebra. However, this causes a type error with the MRO --- src/sage/combinat/diagram_algebras.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 72ad8f2c17d..6c87efcb348 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -806,7 +806,8 @@ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): CombinatorialFreeModule.__init__(self, base_ring, diagrams, category=category, prefix=prefix, bracket=False) - KSS = SymmetricGroupAlgebra(RationalField(), k) # QQ probably should not be hardcoded here. + + KSS = SymmetricGroupAlgebra(self.base_ring(), k) # QQ probably should not be hardcoded here. KSS.module_morphism(lambda i : self(self._perm_to_Blst(i)), codomain=self).register_as_coercion() def _element_constructor_(self, set_partition): From d16a7f5087d26374c80b26d55dd608024ddd6bdb Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Fri, 26 Jun 2015 12:35:43 -0500 Subject: [PATCH 094/421] tried moving SymmetricGroupAlgebra declaration in DiagramAlgebras init to no avail --- src/sage/combinat/diagram_algebras.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 6c87efcb348..6dd95ad2bee 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -803,11 +803,10 @@ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): self._base_diagrams = diagrams if category is None: category = FiniteDimensionalAlgebrasWithBasis(base_ring) + KSS = SymmetricGroupAlgebra(base_ring, k, category = category) # QQ probably should not be hardcoded here. CombinatorialFreeModule.__init__(self, base_ring, diagrams, category=category, prefix=prefix, bracket=False) - - KSS = SymmetricGroupAlgebra(self.base_ring(), k) # QQ probably should not be hardcoded here. KSS.module_morphism(lambda i : self(self._perm_to_Blst(i)), codomain=self).register_as_coercion() def _element_constructor_(self, set_partition): From 22c7116a9e7355b0b351a9d7dd733e8423fcdcf2 Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Fri, 26 Jun 2015 12:52:54 -0500 Subject: [PATCH 095/421] temporary workaround for getting SymmetricGroupAlgebra initialized by initializing twice. --- src/sage/combinat/diagram_algebras.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 6dd95ad2bee..a9ac3dd92b8 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -797,6 +797,7 @@ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): sage: D = da.DiagramAlgebra(2, x, R, 'P', da.PartitionDiagrams(2)) sage: TestSuite(D).run() """ + SymmetricGroupAlgebra(base_ring,k) self._prefix = prefix self._q = base_ring(q) self._k = k From 7bc49f55af4b8758616f75855037387b89410b92 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 27 Jun 2015 08:06:51 -0700 Subject: [PATCH 096/421] Adding `_repr_option` to the interface element to avoid __getattr__. --- src/sage/interfaces/sage0.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/interfaces/sage0.py b/src/sage/interfaces/sage0.py index 0795ca7eec7..1462030975e 100644 --- a/src/sage/interfaces/sage0.py +++ b/src/sage/interfaces/sage0.py @@ -402,6 +402,22 @@ def _rich_repr_(self, display_manager, **kwds): """ return None + def _repr_option(self, option): + """ + Disable repr option. + + This is necessary because otherwise our :meth:`__getattr__` + would be called. + + EXAMPLES:: + + sage: from sage.repl.rich_output import get_display_manager + sage: m = sage0(4) + sage: m._repr_option('ascii_art') + False + """ + return False + def __getattr__(self, attrname): """ EXAMPLES:: From cb070380d79d62bdd560ba7b0f14c3c86074e5bf Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Wed, 1 Jul 2015 21:23:28 -0500 Subject: [PATCH 097/421] removed category declaration and unnecessary comment --- src/sage/combinat/diagram_algebras.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 23fdf4e687f..ee241162941 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -797,14 +797,14 @@ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): sage: D = da.DiagramAlgebra(2, x, R, 'P', da.PartitionDiagrams(2)) sage: TestSuite(D).run() """ - SymmetricGroupAlgebra(base_ring,k) + SymmetricGroupAlgebra(base_ring,k) # Necessary for some odd reason self._prefix = prefix self._q = base_ring(q) self._k = k self._base_diagrams = diagrams if category is None: category = FiniteDimensionalAlgebrasWithBasis(base_ring) - KSS = SymmetricGroupAlgebra(base_ring, k, category = category) # QQ probably should not be hardcoded here. + KSS = SymmetricGroupAlgebra(base_ring, k) CombinatorialFreeModule.__init__(self, base_ring, diagrams, category=category, prefix=prefix, bracket=False) From e138144a3975a51a005f06df98c6ef1b0fad1753 Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Fri, 3 Jul 2015 21:09:29 -0500 Subject: [PATCH 098/421] Added documentation and a doctest for coercion --- src/sage/combinat/diagram_algebras.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 00685d98348..4749faefc1f 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -875,6 +875,19 @@ class DiagramAlgebra(CombinatorialFreeModule): P{{-2, 1, 2}, {-1}}, P{{-2, 2}, {-1}, {1}}, P{{-2, 2}, {-1, 1}}] + + Due to the nature of diagrams, there is also a built-in coercion to turn + SymmetricGroupAlgebra elements into DiagramAlgebra elements. However, + this coercion can cause errors if the SymmetricGroupAlgebra element + is not actually valid in the algebra. For instance, not all + SymmetricGroupAlgebra elements are valid in the Temperely--Lieb algebra, + but the planar ones are. + + :: + + sage: S = SymmetricGroupAlgebra(R, 2) + sage: S([2,1])*D([[1,-1],[2,-2]]) + P{{-2, 1}, {-1, 2}} """ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): r""" From 84d2af35beef9637136ae6e437d8b069baeb261e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 13 Jul 2015 00:47:07 -0700 Subject: [PATCH 099/421] Changed name left_top -> left_column_box. --- .../rigged_configuration_element.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index 26d41166be2..26c840b3ece 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -1818,9 +1818,9 @@ def left_box(self, return_b=False): delta = left_box - def left_top(self): + def left_column_box(self): r""" - Return the image of ``self`` under the left column top splitting + Return the image of ``self`` under the left column box splitting map `\gamma`. Consider the map `\gamma : RC(B^{r,1} \otimes B) \to RC(B^{1,1} @@ -1838,7 +1838,7 @@ def left_top(self): sage: ascii_art(mg) 0[ ]0 0[ ][ ]0 0[ ]0 0[ ]0 0[ ]0 - sage: ascii_art(mg.left_top()) + sage: ascii_art(mg.left_column_box()) 0[ ]0 0[ ][ ]0 0[ ]0 0[ ]0 0[ ]0 0[ ]0 0[ ]0 @@ -1848,7 +1848,7 @@ def left_top(self): sage: ascii_art(mg) 1[ ]0 0[ ][ ]0 0[ ]0 0[ ]0 0[ ]0 - sage: ascii_art(mg.left_top()) + sage: ascii_art(mg.left_column_box()) 1[ ]1 0[ ][ ]0 0[ ]0 1[ ]0 0[ ]0 0[ ]0 """ @@ -1865,7 +1865,7 @@ def left_top(self): raise ValueError("only for non-spinor cases") if P.dims[0][1] > 1: - return self.left_split().left_top() + return self.left_split().left_column_box() B = [[1,1], [r-1,1]] B.extend(P.dims[1:]) @@ -1881,9 +1881,9 @@ def left_top(self): nu.rigging.insert(i, vac_num) return RC(*parts) - def right_bottom(self): + def right_column_box(self): r""" - Return the image of ``self`` under the right column bottom splitting + Return the image of ``self`` under the right column box splitting map `\gamma^*`. Consider the map `\gamma^* : RC(B \otimes B^{r,1}) \to RC(B \otimes @@ -1903,7 +1903,7 @@ def right_bottom(self): sage: ascii_art(mg) 1[ ]0 0[ ][ ]0 0[ ]0 0[ ]0 0[ ]0 - sage: ascii_art(mg.right_bottom()) + sage: ascii_art(mg.right_column_box()) 1[ ]0 0[ ][ ]0 0[ ]0 1[ ]0 0[ ]0 0[ ]0 0[ ]0 @@ -1921,7 +1921,7 @@ def right_bottom(self): raise ValueError("only for non-spinor cases") if P.dims[-1][1] > 1: - return self.right_split().right_bottom() + return self.right_split().right_column_box() rc, e_string = self.to_highest_weight(P.cartan_type().classical().index_set()) From 1eb53c38e523f7850b27f8d0e6d07c0e013b7e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 22 Jul 2015 17:17:16 +0200 Subject: [PATCH 100/421] adding conversion from Arc class to Bezier paths. --- src/sage/plot/arc.py | 57 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/src/sage/plot/arc.py b/src/sage/plot/arc.py index 04fac945f42..efbf72b739a 100644 --- a/src/sage/plot/arc.py +++ b/src/sage/plot/arc.py @@ -235,6 +235,48 @@ def _allowed_options(self): "'dashed', 'dotted', 'solid', 'dashdot', or '--', ':', '-', '-.', " "respectively."} + def _matplotlib_arc(self): + """ + Return ``self`` as a matplotlib arc object. + + EXAMPLES:: + + sage: from sage.plot.arc import Arc + sage: Arc(2,3,2.2,2.2,0,2,3,{})._matplotlib_arc() + + """ + import matplotlib.patches as patches + p = patches.Arc( + (self.x, self.y), + 2. * self.r1, + 2. * self.r2, + fmod(self.angle, 2 * pi) * (180. / pi), + self.s1 * (180. / pi), + self.s2 * (180. / pi)) + return p + + def bezier_path(self): + """ + Return ``self`` as a Bezier path. + + This is useful to concatenate arcs, in particular to + create hyperbolic polygons. + + EXAMPLES:: + + sage: from sage.plot.arc import Arc + sage: Arc(2,3,2.2,2.2,0,2,3,{}).bezier_path() + Graphics object consisting of 1 graphics primitive + """ + from sage.plot.bezier_path import bezier_path + points = [list(u) for u in self._matplotlib_arc()._path.vertices] + cutlist = [points[0: 4]] + N = 4 + while N < len(points): + cutlist += [points[N: N + 3]] + N += 3 + return bezier_path(cutlist) + def _repr_(self): """ String representation of ``Arc`` primitive. @@ -254,26 +296,19 @@ def _render_on_subplot(self, subplot): sage: A = arc((1,1),3,4,pi/4,(pi,4*pi/3)); A Graphics object consisting of 1 graphics primitive """ - import matplotlib.patches as patches from sage.plot.misc import get_matplotlib_linestyle - options = self.options() - p = patches.Arc( - (self.x,self.y), - 2.*self.r1, - 2.*self.r2, - fmod(self.angle,2*pi)*(180./pi), - self.s1*(180./pi), - self.s2*(180./pi)) + p = self._matplotlib_arc() p.set_linewidth(float(options['thickness'])) a = float(options['alpha']) p.set_alpha(a) - z = int(options.pop('zorder',1)) + z = int(options.pop('zorder', 1)) p.set_zorder(z) c = to_mpl_color(options['rgbcolor']) - p.set_linestyle(get_matplotlib_linestyle(options['linestyle'],return_type='long')) + p.set_linestyle(get_matplotlib_linestyle(options['linestyle'], + return_type='long')) p.set_edgecolor(c) subplot.add_patch(p) From b262d972aa792e70bb0f3c1afded2aaeb102dd77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 23 Jul 2015 14:03:47 +0200 Subject: [PATCH 101/421] trac #18939 now with correct options and proportions + fully pep8/pyflakes ok --- src/sage/plot/arc.py | 222 +++++++++++++++++++++++++------------------ 1 file changed, 132 insertions(+), 90 deletions(-) diff --git a/src/sage/plot/arc.py b/src/sage/plot/arc.py index efbf72b739a..789e68e07f5 100644 --- a/src/sage/plot/arc.py +++ b/src/sage/plot/arc.py @@ -22,6 +22,7 @@ from math import fmod, sin, cos, pi, atan + class Arc(GraphicPrimitive): """ Primitive class for the Arc graphics type. See ``arc?`` for information @@ -87,7 +88,7 @@ def __init__(self, x, y, r1, r2, angle, s1, s2, options): self.s1 = float(s1) self.s2 = float(s2) if self.s2 < self.s1: - self.s1,self.s2=self.s2,self.s1 + self.s1, self.s2 = self.s2, self.s1 GraphicPrimitive.__init__(self, options) def get_minmax_data(self): @@ -126,91 +127,113 @@ def get_minmax_data(self): """ from sage.plot.plot import minmax_data - twopi = 2*pi + twopi = 2 * pi s1 = self.s1 s2 = self.s2 - s = s2-s1 - s1 = fmod(s1,twopi) - if s1 < 0: s1 += twopi - s2 = fmod(s1 + s,twopi) - if s2 < 0: s2 += twopi + s = s2 - s1 + s1 = fmod(s1, twopi) + if s1 < 0: + s1 += twopi + s2 = fmod(s1 + s, twopi) + if s2 < 0: + s2 += twopi r1 = self.r1 r2 = self.r2 - angle = fmod(self.angle,twopi) - if angle < 0: angle += twopi + angle = fmod(self.angle, twopi) + if angle < 0: + angle += twopi epsilon = float(0.0000001) cos_angle = cos(angle) sin_angle = sin(angle) - if cos_angle > 1-epsilon: - xmin=-r1; ymin=-r2 - xmax=r1; ymax=r2 - axmin = pi; axmax = 0 - aymin = 3*pi/2; aymax = pi/2 - - elif cos_angle < -1+epsilon: - xmin=-r1; ymin=-r2 - xmax=r1; ymax=r2 - axmin=0; axmax=pi - aymin=pi/2; aymax=3*pi/2 - - elif sin_angle > 1-epsilon: - xmin=-r2; ymin=-r1 - xmax=r2; ymax=r1 - axmin = pi/2; axmax = 3*pi/2 - aymin = pi; aymax = 0 - - elif sin_angle < -1+epsilon: - xmin=-r2; ymin=-r1 - xmax=r2; ymax=r1 - axmin = 3*pi/2; axmax = pi/2 - aymin = 0; aymax = pi + if cos_angle > 1 - epsilon: + xmin = -r1 + ymin = -r2 + xmax = r1 + ymax = r2 + axmin = pi + axmax = 0 + aymin = 3 * pi / 2 + aymax = pi / 2 + + elif cos_angle < -1 + epsilon: + xmin = -r1 + ymin = -r2 + xmax = r1 + ymax = r2 + axmin = 0 + axmax = pi + aymin = pi / 2 + aymax = 3 * pi / 2 + + elif sin_angle > 1 - epsilon: + xmin = -r2 + ymin = -r1 + xmax = r2 + ymax = r1 + axmin = pi / 2 + axmax = 3 * pi / 2 + aymin = pi + aymax = 0 + + elif sin_angle < -1 + epsilon: + xmin = -r2 + ymin = -r1 + xmax = r2 + ymax = r1 + axmin = 3 * pi / 2 + axmax = pi / 2 + aymin = 0 + aymax = pi else: tan_angle = sin_angle / cos_angle - axmax = atan(-r2/r1*tan_angle) - if axmax < 0: axmax += twopi - xmax = ( - r1 * cos_angle * cos(axmax) - - r2 * sin_angle * sin(axmax)) + axmax = atan(-r2 / r1 * tan_angle) + if axmax < 0: + axmax += twopi + xmax = (r1 * cos_angle * cos(axmax) - + r2 * sin_angle * sin(axmax)) if xmax < 0: xmax = -xmax - axmax = fmod(axmax+pi,twopi) + axmax = fmod(axmax + pi, twopi) xmin = -xmax - axmin = fmod(axmax + pi,twopi) + axmin = fmod(axmax + pi, twopi) - aymax = atan(r2/(r1*tan_angle)) - if aymax < 0: aymax += twopi - ymax = ( - r1 * sin_angle * cos(aymax) + - r2 * cos_angle * sin(aymax)) + aymax = atan(r2 / (r1 * tan_angle)) + if aymax < 0: + aymax += twopi + ymax = (r1 * sin_angle * cos(aymax) + + r2 * cos_angle * sin(aymax)) if ymax < 0: ymax = -ymax - aymax = fmod(aymax+pi,twopi) + aymax = fmod(aymax + pi, twopi) ymin = -ymax aymin = fmod(aymax + pi, twopi) - if s < twopi-epsilon: # bb determined by the sector - def is_cyclic_ordered(x1,x2,x3): - return ( - (x1 < x2 and x2 < x3) or - (x2 < x3 and x3 < x1) or - (x3 < x1 and x1 < x2)) - - x1 = cos_angle*r1*cos(s1) - sin_angle*r2*sin(s1) - x2 = cos_angle*r1*cos(s2) - sin_angle*r2*sin(s2) - y1 = sin_angle*r1*cos(s1) + cos_angle*r2*sin(s1) - y2 = sin_angle*r1*cos(s2) + cos_angle*r2*sin(s2) - - if is_cyclic_ordered(s1,s2,axmin): xmin = min(x1,x2) - if is_cyclic_ordered(s1,s2,aymin): ymin = min(y1,y2) - if is_cyclic_ordered(s1,s2,axmax): xmax = max(x1,x2) - if is_cyclic_ordered(s1,s2,aymax): ymax = max(y1,y2) + if s < twopi - epsilon: # bb determined by the sector + def is_cyclic_ordered(x1, x2, x3): + return ((x1 < x2 and x2 < x3) or + (x2 < x3 and x3 < x1) or + (x3 < x1 and x1 < x2)) + + x1 = cos_angle * r1 * cos(s1) - sin_angle * r2 * sin(s1) + x2 = cos_angle * r1 * cos(s2) - sin_angle * r2 * sin(s2) + y1 = sin_angle * r1 * cos(s1) + cos_angle * r2 * sin(s1) + y2 = sin_angle * r1 * cos(s2) + cos_angle * r2 * sin(s2) + + if is_cyclic_ordered(s1, s2, axmin): + xmin = min(x1, x2) + if is_cyclic_ordered(s1, s2, aymin): + ymin = min(y1, y2) + if is_cyclic_ordered(s1, s2, axmax): + xmax = max(x1, x2) + if is_cyclic_ordered(s1, s2, aymax): + ymax = max(y1, y2) return minmax_data([self.x + xmin, self.x + xmax], [self.y + ymin, self.y + ymax], @@ -226,12 +249,12 @@ def _allowed_options(self): sage: p[0]._allowed_options()['alpha'] 'How transparent the figure is.' """ - return {'alpha':'How transparent the figure is.', - 'thickness':'How thick the border of the arc is.', - 'hue':'The color given as a hue.', - 'rgbcolor':'The color', - 'zorder':'2D only: The layer level in which to draw', - 'linestyle':"2D only: The style of the line, which is one of " + return {'alpha': 'How transparent the figure is.', + 'thickness': 'How thick the border of the arc is.', + 'hue': 'The color given as a hue.', + 'rgbcolor': 'The color', + 'zorder': '2D only: The layer level in which to draw', + 'linestyle': "2D only: The style of the line, which is one of " "'dashed', 'dotted', 'solid', 'dashdot', or '--', ':', '-', '-.', " "respectively."} @@ -246,36 +269,54 @@ def _matplotlib_arc(self): """ import matplotlib.patches as patches - p = patches.Arc( - (self.x, self.y), - 2. * self.r1, - 2. * self.r2, - fmod(self.angle, 2 * pi) * (180. / pi), - self.s1 * (180. / pi), - self.s2 * (180. / pi)) + p = patches.Arc((self.x, self.y), + 2. * self.r1, + 2. * self.r2, + fmod(self.angle, 2 * pi) * (180. / pi), + self.s1 * (180. / pi), + self.s2 * (180. / pi)) return p def bezier_path(self): """ Return ``self`` as a Bezier path. - This is useful to concatenate arcs, in particular to + This is needed to concatenate arcs, in order to create hyperbolic polygons. - + EXAMPLES:: sage: from sage.plot.arc import Arc - sage: Arc(2,3,2.2,2.2,0,2,3,{}).bezier_path() + sage: op = {'alpha':0,'thickness':1,'rgbcolor':'blue','zorder':0, + ....: 'linestyle':'--'} + sage: Arc(2,3,2.2,2.2,0,2,3,op).bezier_path() Graphics object consisting of 1 graphics primitive + + sage: a = arc((0,0),2,1,0,(pi/5,pi/2+pi/12), linestyle="--", color="red") + sage: b = a[0].bezier_path() + sage: b[0] + Bezier path from (1.618..., 0.5877...) to (-0.5176..., 0.9659...) """ - from sage.plot.bezier_path import bezier_path - points = [list(u) for u in self._matplotlib_arc()._path.vertices] + from sage.plot.bezier_path import BezierPath + from sage.plot.graphics import Graphics + ma = self._matplotlib_arc() + transform = ma.get_transform().get_matrix() + cA, cC, cE = transform[0] + cB, cD, cF = transform[1] + points = [] + for u in ma._path.vertices: + x, y = list(u) + points += [(cA * x + cC * y + cE, cB * x + cD * y + cF)] cutlist = [points[0: 4]] N = 4 while N < len(points): cutlist += [points[N: N + 3]] N += 3 - return bezier_path(cutlist) + g = Graphics() + opt = self.options() + opt['fill'] = False + g.add_primitive(BezierPath(cutlist, opt)) + return g def _repr_(self): """ @@ -287,7 +328,7 @@ def _repr_(self): sage: print Arc(2,3,2.2,2.2,0,2,3,{}) Arc with center (2.0,3.0) radii (2.2,2.2) angle 0.0 inside the sector (2.0,3.0) """ - return "Arc with center (%s,%s) radii (%s,%s) angle %s inside the sector (%s,%s)" %(self.x,self.y,self.r1,self.r2,self.angle,self.s1,self.s2) + return "Arc with center (%s,%s) radii (%s,%s) angle %s inside the sector (%s,%s)" % (self.x, self.y, self.r1, self.r2, self.angle, self.s1, self.s2) def _render_on_subplot(self, subplot): """ @@ -324,10 +365,11 @@ def plot3d(self): """ raise NotImplementedError + @rename_keyword(color='rgbcolor') -@options(alpha=1, thickness=1, linestyle='solid', zorder=5,rgbcolor='blue', +@options(alpha=1, thickness=1, linestyle='solid', zorder=5, rgbcolor='blue', aspect_ratio=1.0) -def arc(center, r1, r2=None, angle=0.0, sector=(0.0,2*pi), **options): +def arc(center, r1, r2=None, angle=0.0, sector=(0.0, 2 * pi), **options): r""" An arc (that is a portion of a circle or an ellipse) @@ -407,19 +449,19 @@ def arc(center, r1, r2=None, angle=0.0, sector=(0.0,2*pi), **options): if scale == 'semilogy' or scale == 'semilogx': options['aspect_ratio'] = 'automatic' - if len(center)==2: - if r2 is None: r2 = r1 + if len(center) == 2: + if r2 is None: + r2 = r1 g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) if len(sector) != 2: raise ValueError("the sector must consist of two angles") g.add_primitive(Arc( - center[0],center[1], - r1,r2, + center[0], center[1], + r1, r2, angle, - sector[0],sector[1], + sector[0], sector[1], options)) return g - elif len(center)==3: + elif len(center) == 3: raise NotImplementedError - From 2e47dc2df348f80446c4724f6c8c066c2aa29d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 23 Jul 2015 16:58:14 +0300 Subject: [PATCH 102/421] Shorted doc for is_chain_of_poset(). --- src/sage/combinat/posets/posets.py | 65 +++++++++--------------------- 1 file changed, 18 insertions(+), 47 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 047af78e04a..086d52e611e 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -114,10 +114,10 @@ :delim: | :meth:`~FinitePoset.is_chain_of_poset` | Return ``True`` if given iterable is a chain of the poset. - :meth:`~FinitePoset.chains` | Return all the chains of ``self``. + :meth:`~FinitePoset.chains` | Return the chains of the poset. :meth:`~FinitePoset.antichains` | Return the antichains of the poset. - :meth:`~FinitePoset.maximal_chains` | Return all maximal chains of the poset. - :meth:`~FinitePoset.maximal_antichains` | Return all maximal antichains of the poset. + :meth:`~FinitePoset.maximal_chains` | Return the maximal chains of the poset. + :meth:`~FinitePoset.maximal_antichains` | Return the maximal antichains of the poset. :meth:`~FinitePoset.antichains_iterator` | Return an iterator over the antichains of the poset. **Drawing** @@ -2392,77 +2392,48 @@ def is_chain(self): """ return self._hasse_diagram.is_chain() - def is_chain_of_poset(self, o, ordered=False): + def is_chain_of_poset(self, elms, ordered=False): """ - Return whether an iterable ``o`` is a chain of ``self``, - including a check for ``o`` being ordered from smallest - to largest element if the keyword ``ordered`` is set to - ``True``. + Return ``True`` if `elms` is a chain of the poset, and ``False`` otherwise. INPUT: - - ``o`` -- an iterable (e. g., list, set, or tuple) - containing some elements of ``self`` + - ``elms`` -- an iterable (e. g., list, set, or tuple) + containing some elements of the poset - - ``ordered`` -- a Boolean (default: ``False``) which - decides whether the notion of a chain includes being - ordered - - OUTPUT: - - If ``ordered`` is set to ``False``, the truth value of - the following assertion is returned: The subset of ``self`` - formed by the elements of ``o`` is a chain in ``self``. - - If ``ordered`` is set to ``True``, the truth value of - the following assertion is returned: Every element of the - list ``o`` is (strictly!) smaller than its successor in - ``self``. (This makes no sense if ``ordered`` is a set.) + - ``ordered`` -- a Boolean. If ``True``, then return ``True`` + only if elements in `elms` are strictly increasing in the poset. If + ``False`` (the default), then elements can be repeated and be in any + order. EXAMPLES:: sage: P = Poset((divisors(12), attrcall("divides"))) sage: sorted(P.list()) [1, 2, 3, 4, 6, 12] - sage: P.is_chain_of_poset([2, 4]) + sage: P.is_chain_of_poset([12, 3]) True - sage: P.is_chain_of_poset([12, 6]) - True - sage: P.is_chain_of_poset([12, 6], ordered=True) + sage: P.is_chain_of_poset({3, 4, 12}) False - sage: P.is_chain_of_poset([6, 12], ordered=True) - True - sage: P.is_chain_of_poset(()) - True - sage: P.is_chain_of_poset((), ordered=True) - True - sage: P.is_chain_of_poset((3, 4, 12)) - False - sage: P.is_chain_of_poset((3, 6, 12, 1)) - True - sage: P.is_chain_of_poset((3, 6, 12, 1), ordered=True) + sage: P.is_chain_of_poset([12, 3], ordered=True) False - sage: P.is_chain_of_poset((3, 6, 12), ordered=True) - True sage: P.is_chain_of_poset((1, 1, 3)) True sage: P.is_chain_of_poset((1, 1, 3), ordered=True) False sage: P.is_chain_of_poset((1, 3), ordered=True) True - sage: P.is_chain_of_poset((6, 1, 1, 3)) - True - sage: P.is_chain_of_poset((2, 1, 1, 3)) - False """ if ordered: - sorted_o = o + if not hasattr(elms, '__getitem__'): + raise TypeError("ordered=True not combatible with type %s for elms" % type(elms)) + sorted_o = elms return all(self.lt(a, b) for a, b in zip(sorted_o, sorted_o[1:])) else: # _element_to_vertex can be assumed to be a linear extension # of the poset according to the documentation of class # HasseDiagram. - sorted_o = sorted(o, key=self._element_to_vertex) + sorted_o = sorted(elms, key=self._element_to_vertex) return all(self.le(a, b) for a, b in zip(sorted_o, sorted_o[1:])) def is_connected(self): From 041cf315cbec72a405b59e004e6a16d24474c9db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Fri, 24 Jul 2015 14:17:58 +0300 Subject: [PATCH 103/421] Minor changes in doc for chains(). --- src/sage/combinat/posets/posets.py | 40 ++++++++++++++---------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 086d52e611e..1c9771be862 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -3435,51 +3435,47 @@ def dilworth_decomposition(self): def chains(self, element_constructor=__builtin__.list, exclude=None): """ - Return all the chains of ``self``. + Return the chains of the poset. + + A *chain* of a poset is a set of elements of the poset + that are pairwise comparable. INPUT: - ``element_constructor`` -- a function taking an iterable as - argument (default: ``list``) + argument (default: ``list``) - ``exclude`` -- elements of the poset to be excluded (default: ``None``) OUTPUT: - The enumerated set of all chains of ``self``, each of which - is given as an ``element_constructor``. - - A *chain* of a poset is a set of elements of the poset - that are pairwise comparable. + The enumerated set (of type + :class:`~sage.combinat.subsets_pairwise.PairwiseCompatibleSubsets`) + of all chains of the poset, each of which is given as an + ``element_constructor``. EXAMPLES:: - sage: A = Posets.PentagonPoset().chains(); A + sage: C = Posets.PentagonPoset().chains(); C Set of chains of Finite lattice containing 5 elements - sage: list(A) + sage: list(C) [[], [0], [0, 1], [0, 1, 4], [0, 2], [0, 2, 3], [0, 2, 3, 4], [0, 2, 4], [0, 3], [0, 3, 4], [0, 4], [1], [1, 4], [2], [2, 3], [2, 3, 4], [2, 4], [3], [3, 4], [4]] - To get the chains of a given size one can currently use:: - - sage: list(A.elements_of_depth_iterator(2)) - [[0, 1], [0, 2], [0, 3], [0, 4], [1, 4], [2, 3], [2, 4], [3, 4]] - - For bounded posets, one can exclude the bounds as follows:: - - sage: P = Posets.DiamondPoset(5) - sage: list(P.chains(exclude=[0, 4])) - [[], [1], [2], [3]] - - Another example of exclusion of vertices:: + Exclusion of vertices, tuple (instead of list) as constructor:: sage: P = Poset({1: [2, 3], 2: [4], 3: [4, 5]}) sage: list(P.chains(element_constructor=tuple, exclude=[3])) [(), (1,), (1, 2), (1, 2, 4), (1, 4), (1, 5), (2,), (2, 4), (4,), (5,)] + To get the chains of a given size one can currently use:: + + sage: list(C.elements_of_depth_iterator(2)) + [[0, 1], [0, 2], [0, 3], [0, 4], [1, 4], [2, 3], [2, 4], [3, 4]] + Eventually the following syntax will be accepted:: - sage: A.subset(size = 2) # todo: not implemented + sage: C.subset(size = 2) # todo: not implemented .. SEEALSO:: :meth:`maximal_chains`, :meth:`antichains` """ From d185533c18017b13368fd37886fbfe98af909cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Fri, 24 Jul 2015 14:33:53 +0300 Subject: [PATCH 104/421] Minor changes in doc for antichains(). --- src/sage/combinat/posets/posets.py | 33 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 1c9771be862..9203c99dbfe 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -3255,17 +3255,22 @@ def isomorphic_subposets(self, other): def antichains(self, element_constructor = __builtin__.list): """ - Returns the antichains of the poset. + Return the antichains of the poset. + + An *antichain* of a poset is a set of elements of the + poset that are pairwise incomparable. INPUT: - ``element_constructor`` -- a function taking an iterable as - argument (default: list) + argument (default: ``list``) - OUTPUT: an enumerated set + OUTPUT: - An *antichain* of a poset is a collection of elements of the - poset that are pairwise incomparable. + The enumerated set (of type + :class:`~sage.combinat.subsets_pairwise.PairwiseCompatibleSubsets`) + of all antichains of the poset, each of which is given as an + ``element_constructor.`` EXAMPLES:: @@ -3277,10 +3282,12 @@ def antichains(self, element_constructor = __builtin__.list): 8 sage: A[3] [1, 2] - sage: list(Posets.AntichainPoset(3).antichains()) - [[], [2], [2, 1], [2, 1, 0], [2, 0], [1], [1, 0], [0]] - sage: list(Posets.ChainPoset(3).antichains()) - [[], [0], [1], [2]] + + To get the antichains as, say, sets, one may use the + ``element_constructor`` option:: + + sage: list(Posets.ChainPoset(3).antichains(element_constructor=set)) + [set(), {0}, {1}, {2}] To get the antichains of a given size one can currently use:: @@ -3291,12 +3298,6 @@ def antichains(self, element_constructor = __builtin__.list): sage: A.subset(size = 2) # todo: not implemented - To get the antichains as, say, sets, one may use the - ``element_constructor`` option:: - - sage: list(Posets.ChainPoset(3).antichains(element_constructor = set)) - [set(), {0}, {1}, {2}] - .. NOTE:: Internally, this uses @@ -3312,7 +3313,7 @@ def antichains(self, element_constructor = __builtin__.list): On the other hand, this returns a full featured enumerated set, with containment testing, etc. - .. seealso:: :meth:`maximal_antichains` + .. seealso:: :meth:`maximal_antichains`, :meth:`chains` """ vertex_to_element = self._vertex_to_element From 9642c432505970dff49d647900ecd218596670ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Fri, 24 Jul 2015 14:45:33 +0300 Subject: [PATCH 105/421] Minor addition to maximal_antichains(). --- src/sage/combinat/posets/posets.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 9203c99dbfe..b28f68bf6ea 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -4395,7 +4395,11 @@ def incomparability_graph(self): def maximal_antichains(self): """ - Return all maximal antichains of the poset. + Return the maximal antichains of the poset. + + An antichain `a` of poset `P` is *maximal* if there is + no element `e \in P \setminus a` such that `a \cup \{e\}` + is an antichain. EXAMPLES:: @@ -4406,7 +4410,7 @@ def maximal_antichains(self): sage: Posets.PentagonPoset().maximal_antichains() [[0], [1, 2], [1, 3], [4]] - .. seealso:: :meth:`maximal_chains`, :meth:`antichains` + .. seealso:: :meth:`antichains`, :meth:`maximal_chains` """ # Maximal antichains are maximum cliques on incomparability graph. return self.incomparability_graph().cliques_maximal() From df6d947928eda82b989a1fd104c7ed57e911cbd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Fri, 24 Jul 2015 15:40:49 +0300 Subject: [PATCH 106/421] An example to antichains_iterator(). --- src/sage/combinat/posets/posets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index b28f68bf6ea..9548e27ea38 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -3325,12 +3325,14 @@ def f(antichain): def antichains_iterator(self): """ - Returns an iterator over the antichains of the poset. + Return an iterator over the antichains of the poset. EXAMPLES:: - sage: Posets.PentagonPoset().antichains_iterator() + sage: it = Posets.PentagonPoset().antichains_iterator(); it + sage: it.next(), it.next() + ([], [4]) .. SEEALSO:: :meth:`antichains` """ From aa42238c6754cff63c5cc42e8d77e78b36074fbc Mon Sep 17 00:00:00 2001 From: David Lucas Date: Wed, 29 Jul 2015 16:34:40 +0200 Subject: [PATCH 107/421] Integrated reviewer's comments --- src/sage/coding/all.py | 2 -- src/sage/coding/encoder.py | 57 ++++++++++++------------------- src/sage/coding/linear_code.py | 62 +++++++++++++++++----------------- 3 files changed, 53 insertions(+), 68 deletions(-) diff --git a/src/sage/coding/all.py b/src/sage/coding/all.py index cada61837a0..84e1b6e81f5 100644 --- a/src/sage/coding/all.py +++ b/src/sage/coding/all.py @@ -68,8 +68,6 @@ bounds_minimum_distance, self_orthogonal_binary_codes) -from linear_code import LinearCodeGeneratorMatrixEncoder - from sd_codes import self_dual_codes_binary lazy_import("sage.coding.delsarte_bounds", diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 6c8cb90305e..fd840ea3534 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -25,23 +25,17 @@ class Encoder(SageObject): Every encoder class should inherit from this abstract class. - This class provides: - - - ``code``, the associated code of the encoder - - - some methods for encoder objects - To implement an encoder, you need to: - inherit from :class:`Encoder` - - call :class:`Encoder`'s :meth:`__init__` in the subclass constructor. + - call ``Encoder.__init__`` in the subclass constructor. Example: ``super(SubclassName, self).__init__(code)``. By doing that, your subclass will have its ``code`` parameter initialized. You need of course to complete the constructor by adding any additional parameter needed to describe properly the code defined in the subclass. - Then, if the message space is a vectorial space, default implementation of :meth:`encode` and + Then, if the message space is a vector space, default implementations of :meth:`encode` and :meth:`unencode_nocheck` methods are provided. These implementations rely on :meth:`generator_matrix` which you need to override to use the default implementations. @@ -54,14 +48,12 @@ class Encoder(SageObject): these methods is not enough for your subclass, you are strongly encouraged to override them. - .. NOTE:: + As :class:`Encoder` is not designed to be instanciated, it does not have any representation + methods. You should implement ``_repr_`` and ``_latex_`` methods in the sublclass. - For consistency on encoders, please follow this convention on names for subclasses: - for a new encoder named ``EncName``, for code family ``CodeFam``, call it - ``CodeFamEncNameEncoder``. + REFERENCES: - As :class:`Encoder` is not designed to be implemented, it does not have any representation - methods. You should implement ``_repr_`` and ``_latex_`` methods in the sublclass. + .. [Nielsen] Johan S. R. Nielsen, (https://bitbucket.org/jsrn/codinglib/) """ def __init__(self, code): @@ -99,12 +91,13 @@ def __init__(self, code): def encode(self, word): r""" - Transforms an element of the message space into an element of the code. + Transforms an element of the message space into a codeword. This is a default implementation which assumes that the message - space of the encoder is `F^k`, where `F` is ``self.code().base_field()`` - and ``k`` is ``self.code().dimension()``. If this is not the case, - this method should be overwritten by the subclass. + space of the encoder is `F^k`, where `F` is + :meth:`sage.coding.linear_code.AbstractLinearCode.base_field` + and ``k`` is :meth:`sage.coding.linear_code.AbstractLinearCode.dimension`. + If this is not the case, this method should be overwritten by the subclass. INPUT: @@ -163,33 +156,28 @@ def unencode(self, c, nocheck=False): """ if nocheck == False: if c not in self.code(): - raise EncodingFailure("Given word is not in the code") + raise EncodingError("Given word is not in the code") else: return self.unencode_nocheck(c) else: return self.unencode_nocheck(c) @cached_method - def unencoder_matrix(self): + def _unencoder_matrix(self): r""" Finds an information set for the matrix ``G`` returned by :meth:`generator_matrix`, and returns the inverse of that submatrix of ``G``. - .. NOTE:: - - This is a helper function, for internal use only. - AUTHORS: - This function is taken from codinglib (https://bitbucket.org/jsrn/codinglib/) - and was written by Johan Nielsen. + This function is taken from codinglib [Nielsen]_ EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: E = C.encoder() - sage: E.unencoder_matrix() + sage: E._unencoder_matrix() [0 0 1 1] [0 1 0 1] [1 1 1 0] @@ -206,8 +194,7 @@ def unencode_nocheck(self, c): AUTHORS: - This function is taken from codinglib (https://bitbucket.org/jsrn/codinglib/) - and was written by Johan Nielsen. + This function is taken from codinglib [Nielsen]_ INPUT: @@ -242,14 +229,14 @@ def unencode_nocheck(self, c): sage: c == c1 False """ - U = self.unencoder_matrix() + U = self._unencoder_matrix() info_set = self.code().information_set() cc = vector( c[i] for i in info_set ) return cc * U def code(self): r""" - Returns the code in which ``self.encode()`` has its output. + Returns the code in which :meth:`encode` has its output. EXAMPLES:: @@ -263,8 +250,8 @@ def code(self): def message_space(self): r""" - Returns the ambient space of allowed input to ``self.encode()``. - Note that `self.encode()` is possibly a partial function over + Returns the ambient space of allowed input to :meth:`encode`. + Note that :meth:`encode` is possibly a partial function over the ambient space. EXAMPLES:: @@ -288,8 +275,8 @@ def generator_matrix(self): where `F` is the base field of :meth:`code`.) """ -class EncodingFailure(Exception): +class EncodingError(Exception): r""" - Special exception class to indicate a failure during encoding or unencoding. + Special exception class to indicate an error during encoding or unencoding. """ pass diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index dab90a9dd8e..4f608cefa4c 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -933,7 +933,7 @@ def add_encoder(self, name, encoder): ValueError: There is already a registered encoder with this name """ reg_enc = self._registered_encoders - if(name in reg_enc.keys()): + if name in reg_enc: raise ValueError("There is already a registered encoder with this name") reg_enc[name] = encoder @@ -1732,18 +1732,20 @@ def __eq__(self, right): return False return True - def encode(self, word, name=None, **kwargs): + def encode(self, word, encoder_name=None, **kwargs): r""" - Transforms an element of the message space into an element of the code. + Transforms an element of the message space into a codeword. INPUT: - ``word`` -- a vector of the message space of the code - - ``name`` -- (default: ``None``) Name of the encoder which will be used + - ``encoder_name`` -- (default: ``None``) Name of the encoder which will be used to encode ``word``. The default encoder of ``self`` will be used if default value is kept + - ``kwargs`` -- all additional arguments are forwarded to :meth:`encoder` + OUTPUT: - a vector of ``self`` @@ -1764,11 +1766,11 @@ def encode(self, word, name=None, **kwargs): sage: C.encode(word, 'GeneratorMatrix') (1, 1, 0, 0, 1, 1, 0) """ - E = self.encoder(name, **kwargs) + E = self.encoder(encoder_name, **kwargs) return E.encode(word) @cached_method - def encoder(self, name=None, **kwargs): + def encoder(self, encoder_name=None, **kwargs): r""" Returns an encoder of ``self``. @@ -1778,10 +1780,13 @@ def encoder(self, name=None, **kwargs): INPUT: - - ``name`` -- (default: ``None``) name of the encoder which will be + - ``encoder_name`` -- (default: ``None``) name of the encoder which will be returned. The default encoder of ``self`` will be used if default value is kept. + - ``kwargs`` -- all additional arguments are forwarded to the constructor of the encoder + this method will return + OUTPUT: - an Encoder object @@ -1804,11 +1809,11 @@ def encoder(self, name=None, **kwargs): ... ValueError: Passed Encoder name not known """ - if name is None: - name = self._encoder_default_name - return self.encoder(name, **kwargs) - if name in self._registered_encoders: - encClass = self._registered_encoders[name] + if encoder_name is None: + encoder_name = self._encoder_default_name + return self.encoder(encoder_name, **kwargs) + if encoder_name in self._registered_encoders: + encClass = self._registered_encoders[encoder_name] E = encClass(self, **kwargs) return E else: @@ -2010,16 +2015,18 @@ def __getitem__(self, i): codeword.set_immutable() return codeword - def generator_matrix(self, name=None, **kwargs): + def generator_matrix(self, encoder_name=None, **kwargs): r""" Returns a generator matrix of ``self``. INPUT: - - ``name`` -- (default: ``None``) name of the encoder which will be + - ``encoder_name`` -- (default: ``None``) name of the encoder which will be used to compute the generator matrix. The default encoder of ``self`` will be used if default value is kept. + - ``kwargs`` -- all additional arguments are forwarded to :meth:`encoder` + EXAMPLES:: sage: G = matrix(GF(3),2,[1,-1,1,-1,1,1]) @@ -2028,7 +2035,7 @@ def generator_matrix(self, name=None, **kwargs): [1 2 1] [2 1 1] """ - E = self.encoder(name, **kwargs) + E = self.encoder(encoder_name, **kwargs) return E.generator_matrix() gen_mat = deprecated_function_alias(17973, generator_matrix) @@ -2616,7 +2623,7 @@ def permutation_automorphism_group(self, algorithm="partition"): print "\n Using the %s codewords of weight %s \n Supergroup size: \n %s\n "%(wts[wt],wt,size) gap.eval("Cwt:=Filtered(eltsC,c->WeightCodeword(c)=%s)"%wt) # bottleneck 2 (repeated gap.eval("matCwt:=List(Cwt,c->VectorCodeword(c))") # for each i until stop = 1) - if gap("Length(matCwt)") > 0: + if gap("Length(matCwt)") > 0: A = gap("MatrixAutomorphisms(matCwt)") G2 = gap("Intersection2(%s,%s)"%(str(A).replace("\n",""),str(Gp).replace("\n",""))) # bottleneck 3 Gp = G2 @@ -3098,7 +3105,7 @@ def spectrum(self, algorithm=None): input = code2leon(self) + "::code" import os, subprocess lines = subprocess.check_output([os.path.join(guava_bin_dir, 'wtdist'), input]) - import StringIO # to use the already present output parser + import StringIO # to use the already present output parser wts = [0]*(n+1) s = 0 for L in StringIO.StringIO(lines).readlines(): @@ -3235,7 +3242,7 @@ def syndrome(self, r): """ return self.parity_check_matrix()*r - def unencode(self, c, name=None, nocheck=False, **kwargs): + def unencode(self, c, encoder_name=None, nocheck=False, **kwargs): r""" Returns the message corresponding to ``c``. @@ -3244,13 +3251,15 @@ def unencode(self, c, name=None, nocheck=False, **kwargs): - ``c`` -- a vector of the same length as ``self`` over the base field of ``self`` - - ``name`` -- (default: ``None``) name of the decoder which will be used + - ``encoder_name`` -- (default: ``None``) name of the decoder which will be used to decode ``word``. The default decoder of ``self`` will be used if default value is kept. - ``nocheck`` -- (default: ``False``) checks if ``c`` is in self. If this is set to True, the return value of this method is not guaranteed to be correct. + - ``kwargs`` -- all additional arguments are forwarded to :meth:`encoder` + OUTPUT: - a vector @@ -3263,7 +3272,7 @@ def unencode(self, c, name=None, nocheck=False, **kwargs): sage: C.unencode(c) (0, 1, 1, 0) """ - E = self.encoder(name, **kwargs) + E = self.encoder(encoder_name, **kwargs) return E.unencode(c, nocheck) def weight_enumerator(self, names="xy", name2=None): @@ -3627,21 +3636,12 @@ class LinearCodeGeneratorMatrixEncoder(Encoder): r""" Encoder based on generator_matrix for Linear codes. - The only purpose of this encoder is to set generic linear codes - into the new Encoder structure by providing a valid ``generator_matrix`` - method. - - This encoder uses default implementations of ``encode`` and ``unencode``. - Its ``generator_matrix`` method returns private field ``_generator_matrix`` - of its associated code if any, else it calls the ``generator_matrix`` method - of the default encoder of the associated code. - - According to this behaviour, this encoder should never be used for other codes than + This is the default encoder of a generic linear code, and should never be used for other codes than :class:`LinearCode`. INPUT: - - ``code`` -- The associated code of this encoder. + - ``code`` -- The associated :class:`LinearCode` of this encoder. """ def __init__(self, code): From b1471480a14c682c2d7fa30b49931810d95784f6 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Thu, 30 Jul 2015 15:41:46 +0200 Subject: [PATCH 108/421] Integrated reviewer's comments --- src/sage/coding/linear_code.py | 50 +++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 4f608cefa4c..6410e86c428 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -715,7 +715,7 @@ class AbstractLinearCode(module.Module): - ``length``, the length of the code - - ``encoder_default_name``, the name of the encoder that will be used if no encoder name is passed + - ``default_encoder_name``, the name of the encoder that will be used if no encoder name is passed to an encoder-related method (``generator_matrix``, ``encode``, ``unencode``) - ``_registered_encoders``, a dictionary of all encoders available for this class @@ -758,7 +758,7 @@ class AbstractLinearCode(module.Module): _registered_encoders = {} - def __init__(self, base_field, length, encoder_default_name): + def __init__(self, base_field, length, default_encoder_name): """ Initializes mandatory parameters for a Linear Code object. @@ -772,7 +772,7 @@ def __init__(self, base_field, length, encoder_default_name): - ``length`` -- the length of ``self`` - - ``encoder_default_name`` -- the name of the default encoder of ``self`` + - ``default_encoder_name`` -- the name of the default encoder of ``self`` EXAMPLES: @@ -847,9 +847,9 @@ def __init__(self, base_field, length, encoder_default_name): if not isinstance(length, (int, Integer)): raise ValueError("length must be a Python int or a Sage Integer") self._length = Integer(length) - if not encoder_default_name in self._registered_encoders: + if not default_encoder_name in self._registered_encoders: raise ValueError("You must set a valid encoder as default encoder for this code") - self._encoder_default_name = encoder_default_name + self._default_encoder_name = default_encoder_name cat = Modules(base_field).FiniteDimensional().WithBasis().Finite() facade_for = VectorSpace(base_field, self._length) self.Element = type(facade_for.an_element()) #for when we made this a non-facade parent @@ -1810,7 +1810,7 @@ def encoder(self, encoder_name=None, **kwargs): ValueError: Passed Encoder name not known """ if encoder_name is None: - encoder_name = self._encoder_default_name + encoder_name = self._default_encoder_name return self.encoder(encoder_name, **kwargs) if encoder_name in self._registered_encoders: encClass = self._registered_encoders[encoder_name] @@ -1836,12 +1836,12 @@ def encoders_available(self, values=False): ['GeneratorMatrix'] sage: C.encoders_available(True) - [('GeneratorMatrix', - )] + {'GeneratorMatrix': + } """ reg_enc = self._registered_encoders if values == True: - return reg_enc.items() + return copy(self._registered_encoders) return reg_enc.keys() def extended_code(self): @@ -3629,6 +3629,33 @@ def _repr_(self): """ return "Linear code of length %s, dimension %s over %s"%(self.length(), self.dimension(), self.base_ring()) + def generator_matrix(self, encoder_name=None, **kwargs): + r""" + Returns a generator matrix of ``self``. + + INPUT: + + - ``encoder_name`` -- (default: ``None``) name of the encoder which will be + used to compute the generator matrix. ``self._generator_matrix`` + will be returned if default value is kept. + + - ``kwargs`` -- all additional arguments are forwarded to :meth:`encoder` + + EXAMPLES:: + + sage: G = matrix(GF(3),2,[1,-1,1,-1,1,1]) + sage: code = LinearCode(G) + sage: code.generator_matrix() + [1 2 1] + [2 1 1] + """ + if hasattr(self, "_generator_matrix"): + return self._generator_matrix + E = self.encoder(encoder_name, **kwargs) + return E.generator_matrix() + + + ####################### encoders ############################### from encoder import Encoder @@ -3700,7 +3727,4 @@ def generator_matrix(self): [0 1 0 1 0 1 0] [1 1 0 1 0 0 1] """ - if hasattr(self.code(), "_generator_matrix"): - return self.code()._generator_matrix - else: - return self.code().generator_matrix() + return self.code().generator_matrix() From 00c7ca58ceaa0febc0ff72c393cf265178cda03a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 4 Aug 2015 11:35:45 +0200 Subject: [PATCH 109/421] towards new style spkg for patchbot --- build/pkgs/patchbot/SPKG.txt | 21 +++++++++++++++++++++ build/pkgs/patchbot/checksums.ini | 4 ++++ build/pkgs/patchbot/package-version.txt | 1 + build/pkgs/patchbot/spkg-install | 21 +++++++++++++++++++++ build/pkgs/patchbot/type | 1 + 5 files changed, 48 insertions(+) create mode 100644 build/pkgs/patchbot/SPKG.txt create mode 100644 build/pkgs/patchbot/checksums.ini create mode 100644 build/pkgs/patchbot/package-version.txt create mode 100755 build/pkgs/patchbot/spkg-install create mode 100644 build/pkgs/patchbot/type diff --git a/build/pkgs/patchbot/SPKG.txt b/build/pkgs/patchbot/SPKG.txt new file mode 100644 index 00000000000..93c9d3da752 --- /dev/null +++ b/build/pkgs/patchbot/SPKG.txt @@ -0,0 +1,21 @@ += SageMath patchbot = + +== Description == + +Apply branches and run tests on open Sage tickets. + +The patchbot is used to automate the testing of git branches. It has two different aspects: a server side and a client side. + +== License == + +GPLv2+ + +== Upstream Contact == + +Robert Bradshaw +Frédéric Chapoton +https://github.com/robertwb/sage-patchbot/ + +== Dependencies == + +python, sage-scripts diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini new file mode 100644 index 00000000000..73aec48e51f --- /dev/null +++ b/build/pkgs/patchbot/checksums.ini @@ -0,0 +1,4 @@ +tarball=patchbot-VERSION.tar.bz2 +sha1=338e924734df1228427a03fbf82ea65ec345b420 +md5=aaf9ee33455656a47f687e553d401cac +cksum=1478772629 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt new file mode 100644 index 00000000000..5aa7c523257 --- /dev/null +++ b/build/pkgs/patchbot/package-version.txt @@ -0,0 +1 @@ +2.3.9 diff --git a/build/pkgs/patchbot/spkg-install b/build/pkgs/patchbot/spkg-install new file mode 100755 index 00000000000..657ce96695c --- /dev/null +++ b/build/pkgs/patchbot/spkg-install @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +if [ "$SAGE_LOCAL" = "" ]; then + echo "SAGE_LOCAL undefined ... exiting"; + echo "Maybe run 'sage -sh'?" + exit 1 +fi + +# Move any currently exising patchbot out of the way. +if [ -e "$SAGE_LOCAL/bin/patchbot" ]; then + i=0 + while [ -e "$SAGE_LOCAL/bin/patchbot-old-$i" ]; do + i=$(( $i + 1 )) + done + echo "Renaming existing patchbot directory to patchbot-old-$i" + mv "$SAGE_LOCAL/bin/patchbot" "$SAGE_LOCAL/bin/patchbot-old-$i" +fi + +# Copy into final location. +# The sage-sage script knows how to call this... +cp -Rv src "$SAGE_LOCAL/bin/patchbot" diff --git a/build/pkgs/patchbot/type b/build/pkgs/patchbot/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/patchbot/type @@ -0,0 +1 @@ +optional From b27296098cc6a7e86dca7ea1bac66e4ef2dc429b Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Wed, 25 Jun 2014 23:41:54 +0200 Subject: [PATCH 110/421] Add pseudo-division and gcd for Polynomials (as generic as possible) --- .../rings/polynomial/polynomial_element.pyx | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 85bd0e705c9..90d91b8a581 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3879,6 +3879,154 @@ cdef class Polynomial(CommutativeAlgebraElement): else: raise NotImplementedError("%s does not provide a gcd implementation for univariate polynomials"%self.base_ring()) + def pseudo_quo_rem(self,other): + """ + Compute the pseudo-division of two polynomials. + + INPUT: + + - ``self`` -- A polynomial + + - ``other`` -- A nonzero polynomial, otherwise an exception ValueError is raised + + OUTPUT: + + If ``other`` is nonzero, this algorithm finds Q and R such that + l^(m-n+1) self = Q * other + R where m = deg(self), n = deg(other), + l is the leading coefficient of other, and such that deg(R) < deg(other). + + + EXAMPLES:: + + sage: R. = PolynomialRing(ZZ,Sparse=True) + sage: p = x^4 + 6*x^3 + x^2 - x + 2 + sage: q = 2*x^2 - 3*x - 1 + sage: (quo,rem)=p.pseudo_quo_rem(q); quo,rem + (4*x^2 + 30*x + 51, 175*x + 67) + sage: 2^(4-2+1)*p == quo*q + rem + True + + sage: S. = R[] + sage: p = (-3*x^2 - x)*T^3 - 3*x*T^2 + (x^2 - x)*T + 2*x^2 + 3*x - 2 + sage: q = (-x^2 - 4*x - 5)*T^2 + (6*x^2 + x + 1)*T + 2*x^2 - x + sage: quo,rem=p.pseudo_quo_rem(q); quo,rem + ((3*x^4 + 13*x^3 + 19*x^2 + 5*x)*T + 18*x^4 + 12*x^3 + 16*x^2 + 16*x, + (-113*x^6 - 106*x^5 - 133*x^4 - 101*x^3 - 42*x^2 - 41*x)*T - 34*x^6 + 13*x^5 + 54*x^4 + 126*x^3 + 134*x^2 - 5*x - 50) + sage: (-x^2 - 4*x - 5)^(3-2+1) * p == quo*q + rem + True + + AUTHORS: + + - Bruno Grenet (2014-06-25) + """ + # This is Algorithm 3.1.2 in Cohen [GTM 138] + if other.degree() < 0: + raise ValueError("Pseudo-division by zero is not possible") + + R = self + B = other + Q = self.parent().zero_element() + e = self.degree() - other.degree() + 1 + d = B.leading_coefficient() + X = self.parent().gen() + + while not R.degree() < B.degree(): + S = R.leading_coefficient()*X**(R.degree()-B.degree()) + Q = d*Q+S + R = d*R-S*B + e -= 1 + + q = d**e + return (q*Q,q*R) + + @coerce_binop + def gcd(self, other): + """ + Return the gcd of self and other + + INPUT: + + - ``self`` -- A polynomial + + - ``other`` -- A polynomial + + EXAMPLES:: + + sage: R. = PolynomialRing(ZZ,Sparse=True) + sage: p = x^4 + 6*x^3 + x^2 - x + 2 + sage: q = 2*x^2 - 3*x - 1 + sage: gcd(p,q) + 1 + sage: r = x^2 + x + 1 + sage: gcd(p*r,q*r) + x^2 + x + 1 + + sage: S. = R[] + sage: p = (-3*x^2 - x)*T^3 - 3*x*T^2 + (x^2 - x)*T + 2*x^2 + 3*x - 2 + sage: q = (-x^2 - 4*x - 5)*T^2 + (6*x^2 + x + 1)*T + 2*x^2 - x + sage: gcd(p,q) + 1 + sage: r = (1 + x)*T^2 + (x - 1)*T + 2*x + 3 + sage: gcd(p*r,q*r) + (x + 1)*T^2 + (x - 1)*T + 2*x + 3 + + AUTHORS: + + - Bruno Grenet (2014-06-25) + """ + # This is Algorithm 3.3.1 in Cohen [GTM 138] + if not self.parent().base_ring().is_unique_factorization_domain(): + raise ValueError("The base ring must be a unique factorization domain") + + if self.degree() < other.degree(): + A,B = other, self + else: + A,B = self, other + + if B.is_zero(): + return A + + #from sage.rings.arith import gcd + + a = b = self.base_ring().zero_element() + for c in A.coefficients(): + a = a.gcd(c) + if a.is_one(): + break + for c in B.coefficients(): + b = b.gcd(c) + if b.is_one(): + break + + d = a.gcd(b) + A = self.parent()(A/a) + B = self.parent()(B/b) + g = h = 1 + + delta = A.degree()-B.degree() + _,R = A.pseudo_quo_rem(B) + + while R.degree() > 0: + A = B + B = self.parent()(R/(g*h**delta)) + g = A.leading_coefficient() + h = self.parent().base_ring()(h*g**delta/h**delta) + delta = A.degree() - B.degree() + _, R = A.pseudo_quo_rem(B) + + if R.is_zero(): + b = self.base_ring().zero_element() + for c in B.coefficients(): + b = b.gcd(c) + if b.is_one(): + break + return self.parent()(d*B/b) + + return d + + + + @coerce_binop def lcm(self, other): """ From c88b600cc02a37759e5a33346a52115842c85050 Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Thu, 26 Jun 2014 00:44:50 +0200 Subject: [PATCH 111/421] Add gcd method to class Polynomial_generic_sparse --- .../rings/polynomial/polynomial_element.pyx | 6 +- .../polynomial/polynomial_element_generic.py | 66 ++++++++++++++++++- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 90d91b8a581..91f563a5033 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3885,8 +3885,6 @@ cdef class Polynomial(CommutativeAlgebraElement): INPUT: - - ``self`` -- A polynomial - - ``other`` -- A nonzero polynomial, otherwise an exception ValueError is raised OUTPUT: @@ -3946,9 +3944,7 @@ cdef class Polynomial(CommutativeAlgebraElement): INPUT: - - ``self`` -- A polynomial - - - ``other`` -- A polynomial + - ``other`` -- a polynomial defined over the same ring as ``self`` EXAMPLES:: diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 7c261dc649a..7a95c7a9281 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -644,6 +644,71 @@ def shift(self, n): output[index + n] = coeff return self.parent()(output, check=False) + def gcd(self,other,algorithm=None): + """ + Return the gcd of ``self`` and ``other`` + + INPUT: + + - ``other`` -- a polynomial defined over the same ring as ``self`` + + Three algorithms are available: + + - "dense": The polynomials are converted to the dense representation, + their gcd are computed and is converted back to the sparse + representation. + - "fraction_field": The polynomials are coerced to the ring of + polynomials over the fraction field of their base ring. It won't + work with non integral domain as base rings. The gcd method is + called with the "dense" algorithm, in case there is no specific + sparse gcd method for the fraction field. + - "pseudo-division": Uses the gcd method of the class Polynomial. + + Default is "dense" for polynomials over ZZ and "pseudo-division" in the + other cases. + + EXAMPLES:: + + sage: R. = PolynomialRing(ZZ,sparse=True) + sage: p = x^6 + 7*x^5 + 8*x^4 + 6*x^3 + 2*x^2 + x + 2 + sage: q = 2*x^4 - x^3 - 2*x^2 - 4*x - 1 + sage: gcd(p,q) + x^2 + x + 1 + sage: gcd(p, q, algorithm = "dense") + x^2 + x + 1 + sage: gcd(p, q, algorithm = "fraction_field") + x^2 + x + 1 + sage: gcd(p, q, algorithm = "pseudo-division") + x^2 + x + 1 + + AUTHORS: + + - Bruno Grenet (2014-06-25) + """ + + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.rings.arith import lcm + + if algorithm is None: + if self.base_ring() == ZZ: + algorithm = "dense" + else: + algorithm = "pseudo-division" + if algorithm=="dense": + S = self.parent() + D = PolynomialRing(S.base_ring(),'x',sparse=False) + g = D(self).gcd(D(other)) + return S(g) + if algorithm=="fraction_field": + R = self.parent().base_ring() + F = R.fraction_field() + S = PolynomialRing(F,'x',sparse=True) + g = S(self).gcd(S(other),algorithm="dense") + d = lcm([gg.denominator() for gg in g.coefficients()]) + return self.parent()(d*g) + if algorithm=="pseudo-division": + return Polynomial.gcd(self,other) + @coerce_binop def quo_rem(self, other): """ @@ -726,7 +791,6 @@ def quo_rem(self, other): rem = rem[:rem.degree()] - c*other[:d].shift(e) return (quo,rem) - class Polynomial_generic_domain(Polynomial, IntegralDomainElement): def __init__(self, parent, is_gen=False, construct=False): Polynomial.__init__(self, parent, is_gen=is_gen) From 6f6a167671cffbdcc0c1d91a226a77a26f3c1cc5 Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Thu, 10 Jul 2014 15:20:24 +0200 Subject: [PATCH 112/421] Add special case for p.pseudo_quo_rem(q) when q is a constant --- src/sage/rings/polynomial/polynomial_element.pyx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 91f563a5033..cb01692c7ba 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3921,17 +3921,21 @@ cdef class Polynomial(CommutativeAlgebraElement): if other.degree() < 0: raise ValueError("Pseudo-division by zero is not possible") + # if other is a constant, then R = 0 and Q = self * other^(deg(self)) + if other in self.parent().base_ring(): + return (self * other**(self.degree()), self.parent().zero_element()) + R = self B = other Q = self.parent().zero_element() e = self.degree() - other.degree() + 1 d = B.leading_coefficient() - X = self.parent().gen() while not R.degree() < B.degree(): - S = R.leading_coefficient()*X**(R.degree()-B.degree()) - Q = d*Q+S - R = d*R-S*B + c = R.leading_coefficient() + diffdeg = R.degree() - B.degree() + Q = d*Q + self.parent()(c).shift(diffdeg) + R = d*R - c*B.shift(diffdeg) e -= 1 q = d**e @@ -4016,6 +4020,7 @@ cdef class Polynomial(CommutativeAlgebraElement): b = b.gcd(c) if b.is_one(): break + return self.parent()(d*B/b) return d From 9f797ef3ab0b1714454c7a6f1080e46f4f654411 Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Sat, 13 Dec 2014 14:22:12 +0100 Subject: [PATCH 113/421] Correct formatting of doctests + raise error for unknown algorithm --- .../polynomial/polynomial_element_generic.py | 132 +++++++++--------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 7a95c7a9281..5fb03fdc7fd 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -644,71 +644,6 @@ def shift(self, n): output[index + n] = coeff return self.parent()(output, check=False) - def gcd(self,other,algorithm=None): - """ - Return the gcd of ``self`` and ``other`` - - INPUT: - - - ``other`` -- a polynomial defined over the same ring as ``self`` - - Three algorithms are available: - - - "dense": The polynomials are converted to the dense representation, - their gcd are computed and is converted back to the sparse - representation. - - "fraction_field": The polynomials are coerced to the ring of - polynomials over the fraction field of their base ring. It won't - work with non integral domain as base rings. The gcd method is - called with the "dense" algorithm, in case there is no specific - sparse gcd method for the fraction field. - - "pseudo-division": Uses the gcd method of the class Polynomial. - - Default is "dense" for polynomials over ZZ and "pseudo-division" in the - other cases. - - EXAMPLES:: - - sage: R. = PolynomialRing(ZZ,sparse=True) - sage: p = x^6 + 7*x^5 + 8*x^4 + 6*x^3 + 2*x^2 + x + 2 - sage: q = 2*x^4 - x^3 - 2*x^2 - 4*x - 1 - sage: gcd(p,q) - x^2 + x + 1 - sage: gcd(p, q, algorithm = "dense") - x^2 + x + 1 - sage: gcd(p, q, algorithm = "fraction_field") - x^2 + x + 1 - sage: gcd(p, q, algorithm = "pseudo-division") - x^2 + x + 1 - - AUTHORS: - - - Bruno Grenet (2014-06-25) - """ - - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - from sage.rings.arith import lcm - - if algorithm is None: - if self.base_ring() == ZZ: - algorithm = "dense" - else: - algorithm = "pseudo-division" - if algorithm=="dense": - S = self.parent() - D = PolynomialRing(S.base_ring(),'x',sparse=False) - g = D(self).gcd(D(other)) - return S(g) - if algorithm=="fraction_field": - R = self.parent().base_ring() - F = R.fraction_field() - S = PolynomialRing(F,'x',sparse=True) - g = S(self).gcd(S(other),algorithm="dense") - d = lcm([gg.denominator() for gg in g.coefficients()]) - return self.parent()(d*g) - if algorithm=="pseudo-division": - return Polynomial.gcd(self,other) - @coerce_binop def quo_rem(self, other): """ @@ -791,6 +726,73 @@ def quo_rem(self, other): rem = rem[:rem.degree()] - c*other[:d].shift(e) return (quo,rem) + def gcd(self,other,algorithm=None): + """ + Return the gcd of ``self`` and ``other`` + + INPUT: + + - ``other`` -- a polynomial defined over the same ring as ``self`` + + Three algorithms are available: + + - "dense": The polynomials are converted to the dense representation, + their gcd are computed and is converted back to the sparse + representation. + - "fraction_field": The polynomials are coerced to the ring of + polynomials over the fraction field of their base ring. It won't + work with non integral domain as base rings. The gcd method is + called with the "dense" algorithm, in case there is no specific + sparse gcd method for the fraction field. + - "pseudo-division": Uses the gcd method of the class Polynomial. + + Default is "dense" for polynomials over ZZ and "pseudo-division" in the + other cases. + + EXAMPLES:: + + sage: R. = PolynomialRing(ZZ,sparse=True) + sage: p = x^6 + 7*x^5 + 8*x^4 + 6*x^3 + 2*x^2 + x + 2 + sage: q = 2*x^4 - x^3 - 2*x^2 - 4*x - 1 + sage: gcd(p,q) + x^2 + x + 1 + sage: gcd(p, q, algorithm = "dense") + x^2 + x + 1 + sage: gcd(p, q, algorithm = "fraction_field") + x^2 + x + 1 + sage: gcd(p, q, algorithm = "pseudo-division") + x^2 + x + 1 + + AUTHORS: + + - Bruno Grenet (2014-06-25) + """ + + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.rings.arith import lcm + + if algorithm is None: + if self.base_ring() == ZZ: + algorithm = "dense" + else: + algorithm = "pseudo-division" + if algorithm=="dense": + S = self.parent() + D = PolynomialRing(S.base_ring(),'x',sparse=False) + g = D(self).gcd(D(other)) + return S(g) + if algorithm=="fraction_field": + R = self.parent().base_ring() + F = R.fraction_field() + S = PolynomialRing(F,'x',sparse=True) + g = S(self).gcd(S(other),algorithm="dense") + d = lcm([gg.denominator() for gg in g.coefficients()]) + return self.parent()(d*g) + if algorithm=="pseudo-division": + return Polynomial.gcd(self,other) + else: + raise ValueError("Unknown algorithm '%s'" % algorithm) + class Polynomial_generic_domain(Polynomial, IntegralDomainElement): def __init__(self, parent, is_gen=False, construct=False): Polynomial.__init__(self, parent, is_gen=is_gen) From 6c31b67522c46efa26f5b7b60db6ee2b4869e89a Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Sat, 13 Dec 2014 14:51:01 +0100 Subject: [PATCH 114/421] Change Error for pseudo-division by zero --- src/sage/rings/polynomial/polynomial_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index cb01692c7ba..8608df269a9 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3918,8 +3918,8 @@ cdef class Polynomial(CommutativeAlgebraElement): - Bruno Grenet (2014-06-25) """ # This is Algorithm 3.1.2 in Cohen [GTM 138] - if other.degree() < 0: - raise ValueError("Pseudo-division by zero is not possible") + if other.is_zero(): + raise ZeroDivisionError("Pseudo-division by zero is not possible") # if other is a constant, then R = 0 and Q = self * other^(deg(self)) if other in self.parent().base_ring(): From f623b8893f9b08689be7e3df95e99a51d8c744ce Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Mon, 15 Dec 2014 18:35:12 +0100 Subject: [PATCH 115/421] Choice between FLINT and NTL --- .../rings/polynomial/polynomial_element_generic.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 5fb03fdc7fd..26487f33b05 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -778,7 +778,17 @@ def gcd(self,other,algorithm=None): algorithm = "pseudo-division" if algorithm=="dense": S = self.parent() - D = PolynomialRing(S.base_ring(),'x',sparse=False) + # FLINT is faster but a bug makes the conversion extremely slow, + # so NTL is used in those cases where the conversion is too slow. Cf + # + sd = self.degree() + od = other.degree() + if max(sd,od)<100 or \ + min(len(self.__coeffs)/sd, len(other.__coeffs)/od)>.06: + implementation="FLINT" + else: + implementation="NTL" + D = PolynomialRing(S.base_ring(),'x',implementation=implementation) g = D(self).gcd(D(other)) return S(g) if algorithm=="fraction_field": From 3015456844cb54f37cf4bbae8423521cdd33624a Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Fri, 7 Aug 2015 15:53:33 +0200 Subject: [PATCH 116/421] Adapted to 6.9beta1 => Merged two gcd methods --- .../rings/polynomial/polynomial_element.pyx | 162 ++++++++---------- 1 file changed, 67 insertions(+), 95 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 8608df269a9..a8a4f321be3 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3824,61 +3824,6 @@ cdef class Polynomial(CommutativeAlgebraElement): raise NotImplementedError("splitting_field() is only implemented over number fields and finite fields") - @coerce_binop - def gcd(self, other): - """ - Return a greatest common divisor of this polynomial and ``other``. - - INPUT: - - - ``other`` -- a polynomial in the same ring as this polynomial - - OUTPUT: - - A greatest common divisor as a polynomial in the same ring as - this polynomial. If the base ring is a field, the return value - is a monic polynomial. - - .. NOTE:: - - The actual algorithm for computing greatest common divisors depends - on the base ring underlying the polynomial ring. If the base ring - defines a method ``_gcd_univariate_polynomial``, then this method - will be called (see examples below). - - EXAMPLES:: - - sage: R. = QQ[] - sage: (2*x^2).gcd(2*x) - x - sage: R.zero().gcd(0) - 0 - sage: (2*x).gcd(0) - x - - One can easily add gcd functionality to new rings by providing a - method ``_gcd_univariate_polynomial``:: - - sage: R. = QQ[] - sage: S. = R[] - sage: h1 = y*x - sage: h2 = y^2*x^2 - sage: h1.gcd(h2) - Traceback (most recent call last): - ... - NotImplementedError: Univariate Polynomial Ring in x over Rational Field does not provide a gcd implementation for univariate polynomials - sage: T. = QQ[] - sage: R._gcd_univariate_polynomial = lambda f,g: S(T(f).gcd(g)) - sage: h1.gcd(h2) - x*y - sage: del R._gcd_univariate_polynomial - - """ - if hasattr(self.base_ring(), '_gcd_univariate_polynomial'): - return self.base_ring()._gcd_univariate_polynomial(self, other) - else: - raise NotImplementedError("%s does not provide a gcd implementation for univariate polynomials"%self.base_ring()) - def pseudo_quo_rem(self,other): """ Compute the pseudo-division of two polynomials. @@ -3893,10 +3838,13 @@ cdef class Polynomial(CommutativeAlgebraElement): l^(m-n+1) self = Q * other + R where m = deg(self), n = deg(other), l is the leading coefficient of other, and such that deg(R) < deg(other). + ALGORITHM: + + Algorithm 3.1.2 in [GTM138]_. EXAMPLES:: - sage: R. = PolynomialRing(ZZ,Sparse=True) + sage: R. = PolynomialRing(ZZ, sparse=True) sage: p = x^4 + 6*x^3 + x^2 - x + 2 sage: q = 2*x^2 - 3*x - 1 sage: (quo,rem)=p.pseudo_quo_rem(q); quo,rem @@ -3913,21 +3861,21 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: (-x^2 - 4*x - 5)^(3-2+1) * p == quo*q + rem True - AUTHORS: + REFERENCES: - - Bruno Grenet (2014-06-25) + .. [GTM138] Henri Cohen. A Course in Computational Number Theory. + Graduate Texts in Mathematics, vol. 138. Springer, 1993. """ - # This is Algorithm 3.1.2 in Cohen [GTM 138] if other.is_zero(): raise ZeroDivisionError("Pseudo-division by zero is not possible") # if other is a constant, then R = 0 and Q = self * other^(deg(self)) if other in self.parent().base_ring(): - return (self * other**(self.degree()), self.parent().zero_element()) + return (self * other**(self.degree()), self.parent().zero()) R = self B = other - Q = self.parent().zero_element() + Q = self.parent().zero() e = self.degree() - other.degree() + 1 d = B.leading_coefficient() @@ -3944,37 +3892,71 @@ cdef class Polynomial(CommutativeAlgebraElement): @coerce_binop def gcd(self, other): """ - Return the gcd of self and other + Return a greatest common divisor of this polynomial and ``other``. INPUT: - - ``other`` -- a polynomial defined over the same ring as ``self`` + - ``other`` -- a polynomial in the same ring as this polynomial + + OUTPUT: + + A greatest common divisor as a polynomial in the same ring as + this polynomial. If the base ring is a field, the return value + is a monic polynomial. + + .. NOTE:: + + The actual algorithm for computing greatest common divisors depends + on the base ring underlying the polynomial ring. If the base ring + defines a method ``_gcd_univariate_polynomial``, then this method + will be called (see examples below). If no such method exists, a + fallback algorithm is used. + + ALGORITHM: + + The fallback algorithm is Algorithm 3.3.1 in [GTM138]_. EXAMPLES:: - sage: R. = PolynomialRing(ZZ,Sparse=True) - sage: p = x^4 + 6*x^3 + x^2 - x + 2 - sage: q = 2*x^2 - 3*x - 1 - sage: gcd(p,q) - 1 - sage: r = x^2 + x + 1 - sage: gcd(p*r,q*r) - x^2 + x + 1 + sage: R. = QQ[] + sage: (2*x^2).gcd(2*x) + x + sage: R.zero().gcd(0) + 0 + sage: (2*x).gcd(0) + x - sage: S. = R[] - sage: p = (-3*x^2 - x)*T^3 - 3*x*T^2 + (x^2 - x)*T + 2*x^2 + 3*x - 2 - sage: q = (-x^2 - 4*x - 5)*T^2 + (6*x^2 + x + 1)*T + 2*x^2 - x - sage: gcd(p,q) + A fallback algorithm is implemented for rings that have no method + ``_gcd_univariate_polynomial``:: + + sage: R. = ZZ[] + sage: S. = PolynomialRing(R, sparse=True) + sage: p = (-3*x^2 - x)*y^3 - 3*x*y^2 + (x^2 - x)*y + 2*x^2 + 3*x - 2 + sage: q = (-x^2 - 4*x - 5)*y^2 + (6*x^2 + x + 1)*y + 2*x^2 - x + sage: p.gcd(q) 1 - sage: r = (1 + x)*T^2 + (x - 1)*T + 2*x + 3 - sage: gcd(p*r,q*r) - (x + 1)*T^2 + (x - 1)*T + 2*x + 3 + sage: r = (1 + x)*y^2 + (x - 1)*y + 2*x + 3 + sage: (p*r).gcd(q*r) + (x + 1)*y^2 + (x - 1)*y + 2*x + 3 - AUTHORS: + One can easily provide a specialized gcd function for rings by providing + a method ``_gcd_univariate_polynomial``:: + + sage: T. = QQ[] + sage: R._gcd_univariate_polynomial = lambda f,g: S(T(f).gcd(g)) + sage: (p*r).gcd(q*r) + (x + 1)*y^2 + (x - 1)*y + 2*x + 3 + sage: del R._gcd_univariate_polynomial + + REFERENCES: - - Bruno Grenet (2014-06-25) + .. [GTM138] Henri Cohen. A Course in Computational Number Theory. + Graduate Texts in Mathematics, vol. 138. Springer, 1993. """ - # This is Algorithm 3.3.1 in Cohen [GTM 138] + if hasattr(self.base_ring(), '_gcd_univariate_polynomial'): + return self.base_ring()._gcd_univariate_polynomial(self, other) + + # Fallback algorithm: Algorithm 3.3.1 in Cohen [GTM 138] if not self.parent().base_ring().is_unique_factorization_domain(): raise ValueError("The base ring must be a unique factorization domain") @@ -3986,9 +3968,7 @@ cdef class Polynomial(CommutativeAlgebraElement): if B.is_zero(): return A - #from sage.rings.arith import gcd - - a = b = self.base_ring().zero_element() + a = b = self.base_ring().zero() for c in A.coefficients(): a = a.gcd(c) if a.is_one(): @@ -4015,7 +3995,7 @@ cdef class Polynomial(CommutativeAlgebraElement): _, R = A.pseudo_quo_rem(B) if R.is_zero(): - b = self.base_ring().zero_element() + b = self.base_ring().zero() for c in B.coefficients(): b = b.gcd(c) if b.is_one(): @@ -4025,9 +4005,6 @@ cdef class Polynomial(CommutativeAlgebraElement): return d - - - @coerce_binop def lcm(self, other): """ @@ -6787,15 +6764,10 @@ cdef class Polynomial(CommutativeAlgebraElement): False sage: R(0).is_squarefree() False - - This can obviously fail if the ring does not implement ``gcd()``:: - sage: S. = QQ[] sage: R. = S[] - sage: (2*x*y).is_squarefree() # R does not provide a gcd implementation - Traceback (most recent call last): - ... - NotImplementedError: Univariate Polynomial Ring in y over Rational Field does not provide a gcd implementation for univariate polynomials + sage: (2*x*y).is_squarefree() + True sage: (2*x*y^2).is_squarefree() False From b91cd828945a4668938298ef741769ce64f909a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 9 Aug 2015 14:19:32 +0200 Subject: [PATCH 117/421] trac #18044 correct one typo in the doc --- src/sage/categories/modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 9d578f178ea..56bbe1b0b61 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -388,7 +388,7 @@ def Super(self, base_ring=None): r""" Return the super-analogue category of ``self``. - INPUT:: + INPUT: - ``base_ring`` -- this is ignored From fc7d6a56de2a3dc24003adfa5fddfbb711ca143b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Sun, 9 Aug 2015 17:58:47 +0300 Subject: [PATCH 118/421] Typo correction. --- src/sage/combinat/posets/posets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 9548e27ea38..9b7cef63f84 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -2426,7 +2426,7 @@ def is_chain_of_poset(self, elms, ordered=False): """ if ordered: if not hasattr(elms, '__getitem__'): - raise TypeError("ordered=True not combatible with type %s for elms" % type(elms)) + raise TypeError("ordered=True not compatible with type %s for elms" % type(elms)) sorted_o = elms return all(self.lt(a, b) for a, b in zip(sorted_o, sorted_o[1:])) else: From 3a548d9882d963d9740d57d7095ad3ac4df24e03 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 16 Aug 2015 20:31:42 +0200 Subject: [PATCH 119/421] trac #19042: Default SAT solver in Sage --- src/doc/en/reference/sat/index.rst | 38 ++++---- src/module_list.py | 3 + src/sage/all.py | 1 + src/sage/sat/all.py | 1 + src/sage/sat/solvers/sat_lp.pyx | 150 +++++++++++++++++++++++++++++ src/sage/sat/solvers/satsolver.pyx | 54 ++++++++++- 6 files changed, 225 insertions(+), 22 deletions(-) create mode 100644 src/sage/sat/all.py create mode 100644 src/sage/sat/solvers/sat_lp.pyx diff --git a/src/doc/en/reference/sat/index.rst b/src/doc/en/reference/sat/index.rst index 37adb48dd03..adfbddcc179 100644 --- a/src/doc/en/reference/sat/index.rst +++ b/src/doc/en/reference/sat/index.rst @@ -18,34 +18,37 @@ should be true, we write:: Solvers ------- -Any SAT solver supporting the DIMACS input format is easily interfaced using the -:class:`sage.sat.solvers.dimacs.DIMACS` blueprint. Sage ships with pre-written interfaces for *RSat* -[RS]_ and *Glucose* [GL]_. Furthermore, Sage provides a C++ interface to the *CryptoMiniSat* [CMS]_ SAT -solver which can be used interchangably with DIMACS-based solvers, but also provides advanced -features. For this, the optional CryptoMiniSat package must be installed, this can be accomplished -by typing:: +By default, Sage solves SAT instances as an Integer Linear Program (see +:mod:`sage.numerical.mip`), but any SAT solver supporting the DIMACS input +format is easily interfaced using the :class:`sage.sat.solvers.dimacs.DIMACS` +blueprint. Sage ships with pre-written interfaces for *RSat* [RS]_ and *Glucose* +[GL]_. Furthermore, Sage provides a C++ interface to the *CryptoMiniSat* [CMS]_ +SAT solver which can be used interchangably with DIMACS-based solvers, but also +provides advanced features. For this last solver, the optional CryptoMiniSat +package must be installed, this can be accomplished by typing:: sage: install_package('cryptominisat') # not tested and by running ``sage -b`` from the shell afterwards to build Sage's CryptoMiniSat extension module. -Since by default Sage does not include any SAT solver, we demonstrate key features by instantiating -a fake DIMACS-based solver. We start with a trivial example:: +We now show how to solve a simple SAT problem. :: (x1 OR x2 OR x3) AND (x1 OR x2 OR (NOT x3)) In Sage's notation:: - sage: from sage.sat.solvers.dimacs import DIMACS - sage: solver = DIMACS(command="sat-solver") + sage: solver = SAT() # random sage: solver.add_clause( ( 1, 2, 3) ) sage: solver.add_clause( ( 1, 2, -3) ) + sage: solver() # random + (None, True, True, False) .. NOTE:: - :meth:`sage.sat.solvers.dimacs.DIMACS.add_clause` creates new variables when necessary. In - particular, it creates *all* variables up to the given index. Hence, adding a literal involving - the variable 1000 creates up to 1000 internal variables. + :meth:`~sage.sat.solvers.dimacs.DIMACS.add_clause` creates new variables + when necessary. When using CryptoMiniSat, it creates *all* variables up to + the given index. Hence, adding a literal involving the variable 1000 creates + up to 1000 internal variables. DIMACS-base solvers can also be used to write DIMACS files:: @@ -77,14 +80,6 @@ Alternatively, there is :meth:`sage.sat.solvers.dimacs.DIMACS.clauses`:: These files can then be passed external SAT solvers. -We demonstrate solving using CryptoMiniSat:: - - sage: from sage.sat.solvers import CryptoMiniSat # optional - cryptominisat - sage: cms = CryptoMiniSat() # optional - cryptominisat - sage: cms.add_clause((1,2,-3)) # optional - cryptominisat - sage: cms() # optional - cryptominisat - (None, True, True, False) - Details on Specific Solvers ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -93,6 +88,7 @@ Details on Specific Solvers sage/sat/solvers/satsolver sage/sat/solvers/dimacs + sage/sat/solvers/sat_lp .. optional - cryptominisat .. sage/sat/solvers/cryptominisat/cryptominisat .. sage/sat/solvers/cryptominisat/solverconf diff --git a/src/module_list.py b/src/module_list.py index 8958bbf3919..87a6e4bf196 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1622,6 +1622,9 @@ def uname_specific(name, value, alternative): Extension('sage.sat.solvers.satsolver', sources = ['sage/sat/solvers/satsolver.pyx']), + Extension('sage.sat.solvers.sat_lp', + sources = ['sage/sat/solvers/sat_lp.pyx']), + ################################ ## ## sage.schemes diff --git a/src/sage/all.py b/src/sage/all.py index 2158832213a..9cd34294b20 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -106,6 +106,7 @@ from sage.monoids.all import * from sage.algebras.all import * from sage.modular.all import * +from sage.sat.all import * from sage.schemes.all import * from sage.graphs.all import * from sage.groups.all import * diff --git a/src/sage/sat/all.py b/src/sage/sat/all.py new file mode 100644 index 00000000000..63993752958 --- /dev/null +++ b/src/sage/sat/all.py @@ -0,0 +1 @@ +from solvers.satsolver import SAT diff --git a/src/sage/sat/solvers/sat_lp.pyx b/src/sage/sat/solvers/sat_lp.pyx new file mode 100644 index 00000000000..024ddad5a2e --- /dev/null +++ b/src/sage/sat/solvers/sat_lp.pyx @@ -0,0 +1,150 @@ +r""" +Solve SAT problems Integer Linear Programming + +The class defined here is a :class:`~sage.sat.solvers.satsolver.SatSolver` that +solves its instance using :class:`MixedIntegerLinearProgram`. Its performances +can be expected to be slower than when using +:class:`~sage.sat.solvers.cryptominisat.cryptominisat.CryptoMiniSat`. +""" +from satsolver cimport SatSolver +from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException + +class SatLP(SatSolver): + def __init__(self, solver=None): + r""" + Initializes the instance + + INPUT: + + - ``solver`` -- (default: ``None``) Specify a Linear Program (LP) + solver to be used. If set to ``None``, the default one is used. For + more information on LP solvers and which default solver is used, see + the method + :meth:`solve ` + of the class + :class:`MixedIntegerLinearProgram `. + + EXAMPLE:: + + sage: S=SAT(solver="LP"); S + an ILP-based SAT Solver + """ + SatSolver.__init__(self) + self._LP = MixedIntegerLinearProgram() + self._vars = self._LP.new_variable(binary=True) + + def var(self): + """ + Return a *new* variable. + + EXAMPLE:: + + sage: S=SAT(solver="LP"); S + an ILP-based SAT Solver + sage: S.var() + 1 + """ + nvars = n = self._LP.number_of_variables() + while nvars==self._LP.number_of_variables(): + n += 1 + self._vars[n] # creates the variable if needed + return n + + def nvars(self): + """ + Return the number of variables. + + EXAMPLE:: + + sage: S=SAT(solver="LP"); S + an ILP-based SAT Solver + sage: S.var() + 1 + sage: S.var() + 2 + sage: S.nvars() + 2 + """ + return self._LP.number_of_variables() + + def add_clause(self, lits): + """ + Add a new clause to set of clauses. + + INPUT: + + - ``lits`` - a tuple of integers != 0 + + .. note:: + + If any element ``e`` in ``lits`` has ``abs(e)`` greater + than the number of variables generated so far, then new + variables are created automatically. + + EXAMPLE:: + + sage: S=SAT(solver="LP"); S + an ILP-based SAT Solver + sage: for u,v in graphs.CycleGraph(6).edges(labels=False): + ....: u,v = u+1,v+1 + ....: S.add_clause((u,v)) + ....: S.add_clause((-u,-v)) + """ + if 0 in lits: + raise ValueError("0 should not appear in the clause: {}".format(lits)) + p = self._LP + p.add_constraint(p.sum(self._vars[x] if x>0 else 1-self._vars[-x] for x in lits) + >=1) + + def __call__(self): + """ + Solve this instance. + + OUTPUT: + + - If this instance is SAT: A tuple of length ``nvars()+1`` + where the ``i``-th entry holds an assignment for the + ``i``-th variables (the ``0``-th entry is always ``None``). + + - If this instance is UNSAT: ``False`` + + - If the solver was interrupted before deciding satisfiability + ``None``. + + EXAMPLE:: + + sage: def is_bipartite_SAT(G): + ....: S=SAT(solver="LP"); S + ....: for u,v in G.edges(labels=False): + ....: u,v = u+1,v+1 + ....: S.add_clause((u,v)) + ....: S.add_clause((-u,-v)) + ....: return S + sage: S = is_bipartite_SAT(graphs.CycleGraph(6)) + sage: S() # random + [None, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0] + sage: True in S() + True + sage: S = is_bipartite_SAT(graphs.CycleGraph(7)) + sage: S() + False + """ + try: + self._LP.solve() + except MIPSolverException: + return False + except KeyboardInterrupt: + return None + + b = self._LP.get_values(self._vars) + n = max(b) + return [None]+[b.get(i,False) for i in range(1,n+1)] + + def __repr__(self): + """ + TESTS:: + + sage: S=SAT(solver="LP"); S + an ILP-based SAT Solver + """ + return "an ILP-based SAT Solver" diff --git a/src/sage/sat/solvers/satsolver.pyx b/src/sage/sat/solvers/satsolver.pyx index 102ce003d78..621e742b541 100644 --- a/src/sage/sat/solvers/satsolver.pyx +++ b/src/sage/sat/solvers/satsolver.pyx @@ -33,7 +33,7 @@ cdef class SatSolver: INPUT: - - ``decision`` - is this variable a deicison variable? + - ``decision`` - is this variable a decision variable? EXAMPLE:: @@ -226,3 +226,55 @@ cdef class SatSolver: """ return ["gens"] +def SAT(solver=None): + r""" + Return a :class:`SatSolver` instance. + + Through this class, one can define and solve `SAT + `__ problems. + + INPUT: + + - ``solver`` (string) -- select a solver. Admissible values are: + + - ``"cryptominisat"`` -- note that the cryptominisat package must be + installed. + + - ``"LP"`` -- use :class:`~sage.sat.solvers.sat_lp.SatLP` to solve the + SAT instance. + + - ``None`` (default) -- use CryptoMiniSat if available, and a LP solver + otherwise. + + EXAMPLE:: + + sage: SAT(solver="LP") + an ILP-based SAT Solver + + TESTS:: + + sage: SAT(solver="Wouhouuuuuu") + Traceback (most recent call last): + ... + ValueError: Solver 'Wouhouuuuuu' is not available + + Forcing CryptoMiniSat:: + + sage: SAT(solver="cryptominisat") # optional - cryptominisat + CryptoMiniSat + #vars: 0, #lits: 0, #clauses: 0, #learnt: 0, #assigns: 0 + + """ + from sage.misc.package import is_package_installed + if (solver == 'cryptominisat' or + (solver is None and is_package_installed('cryptominisat'))): + try: + from sage.sat.solvers.cryptominisat.cryptominisat import CryptoMiniSat + except ImportError: + raise ImportError("To enable this feature, run 'sage -i cryptominisat'.") + return CryptoMiniSat() + elif (solver == "LP" or solver is None): + from sat_lp import SatLP + return SatLP() + else: + raise ValueError("Solver '{}' is not available".format(solver)) From d4d3a091c747cf5ed31f02eb10d6f3298445aecd Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Sun, 16 Aug 2015 18:34:03 -0500 Subject: [PATCH 120/421] accidentally deleted crucial statement for coercion. I have reinserted it. --- src/sage/combinat/diagram_algebras.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index e8bc530ef13..330d51578d6 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -1213,6 +1213,7 @@ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): self._k = k self._base_diagrams = diagrams category = Algebras(base_ring).FiniteDimensional().WithBasis().or_subcategory(category) + KSS = SymmetricGroupAlgebra(base_ring, k) CombinatorialFreeModule.__init__(self, base_ring, diagrams, category=category, prefix=prefix, bracket=False) From c6db8534c85b16c336ea78085975f8b8edc98cec Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 20 Aug 2015 16:02:06 +0200 Subject: [PATCH 121/421] trac #19061: Auto-generated thematic index of functions --- src/sage/graphs/graph.py | 250 ++++++++++--------------- src/sage/misc/rest_index_of_methods.py | 169 +++++++++++++++-- 2 files changed, 252 insertions(+), 167 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 526ff02b6ec..33d6b405230 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -4,141 +4,7 @@ This module implements functions and operations involving undirected graphs. -**Graph basic operations:** - -.. csv-table:: - :class: contentstable - :widths: 30, 70 - :delim: | - - :meth:`~Graph.write_to_eps` | Writes a plot of the graph to ``filename`` in ``eps`` format. - :meth:`~Graph.to_undirected` | Since the graph is already undirected, simply returns a copy of itself. - :meth:`~Graph.to_directed` | Returns a directed version of the graph. - :meth:`~Graph.sparse6_string` | Returns the sparse6 representation of the graph as an ASCII string. - :meth:`~Graph.graph6_string` | Returns the graph6 representation of the graph as an ASCII string. - :meth:`~Graph.bipartite_sets` | Returns `(X,Y)` where X and Y are the nodes in each bipartite set of graph. - :meth:`~Graph.bipartite_color` | Returns a dictionary with vertices as the keys and the color class as the values. - :meth:`~Graph.is_directed` | Since graph is undirected, returns False. - :meth:`~Graph.join` | Returns the join of self and other. - - -**Distances:** - -.. csv-table:: - :class: contentstable - :widths: 30, 70 - :delim: | - - :meth:`~Graph.centrality_closeness` | Returns the closeness centrality (1/average distance to all vertices) - :meth:`~Graph.centrality_degree` | Returns the degree centrality - - -**Graph properties:** - -.. csv-table:: - :class: contentstable - :widths: 30, 70 - :delim: | - - :meth:`~Graph.is_asteroidal_triple_free` | Tests whether the current graph is asteroidal triple free. - :meth:`~Graph.is_prime` | Tests whether the current graph is prime. - :meth:`~Graph.is_split` | Returns ``True`` if the graph is a Split graph, ``False`` otherwise. - :meth:`~Graph.is_triangle_free` | Returns whether ``self`` is triangle-free. - :meth:`~Graph.is_bipartite` | Returns True if graph G is bipartite, False if not. - :meth:`~Graph.is_line_graph` | Tests wether the graph is a line graph. - :meth:`~Graph.is_odd_hole_free` | Tests whether ``self`` contains an induced odd hole. - :meth:`~Graph.is_even_hole_free` | Tests whether ``self`` contains an induced even hole. - :meth:`~Graph.is_cartesian_product` | Tests whether ``self`` is a cartesian product of graphs. - :meth:`~Graph.is_long_hole_free` | Tests whether ``self`` contains an induced cycle of length at least 5. - :meth:`~Graph.is_long_antihole_free` | Tests whether ``self`` contains an induced anticycle of length at least 5. - :meth:`~Graph.is_weakly_chordal` | Tests whether ``self`` is weakly chordal. - :meth:`~Graph.is_strongly_regular` | Tests whether ``self`` is strongly regular. - :meth:`~Graph.is_distance_regular` | Tests whether ``self`` is distance-regular. - :meth:`~Graph.is_tree` | Return True if the graph is a tree. - :meth:`~Graph.is_forest` | Return True if the graph is a forest, i.e. a disjoint union of trees. - :meth:`~Graph.is_overfull` | Tests whether the current graph is overfull. - :meth:`~Graph.odd_girth` | Returns the odd girth of ``self``. - :meth:`~Graph.is_edge_transitive` | Returns true if self is edge-transitive. - :meth:`~Graph.is_arc_transitive` | Returns true if self is arc-transitive. - :meth:`~Graph.is_half_transitive` | Returns true if self is a half-transitive graph. - :meth:`~Graph.is_semi_symmetric` | Returns true if self is a semi-symmetric graph. - -**Connectivity, orientations, trees:** - -.. csv-table:: - :class: contentstable - :widths: 30, 70 - :delim: | - - :meth:`~Graph.gomory_hu_tree` | Returns a Gomory-Hu tree of self. - :meth:`~Graph.minimum_outdegree_orientation` | Returns an orientation of ``self`` with the smallest possible maximum outdegree - :meth:`~Graph.bounded_outdegree_orientation` | Computes an orientation of ``self`` such that every vertex `v` has out-degree less than `b(v)` - :meth:`~Graph.strong_orientation` | Returns a strongly connected orientation of the current graph. - :meth:`~Graph.degree_constrained_subgraph` | Returns a degree-constrained subgraph. - :meth:`~Graph.bridges` | Returns the list of all bridges. - :meth:`~Graph.spanning_trees` | Returns the list of all spanning trees. - :meth:`~Graph.random_spanning_tree` | Returns a random spanning tree. - -**Clique-related methods:** - -.. csv-table:: - :class: contentstable - :widths: 30, 70 - :delim: | - - :meth:`~Graph.clique_complex` | Returns the clique complex of self - :meth:`~Graph.cliques_containing_vertex` | Returns the cliques containing each vertex - :meth:`~Graph.cliques_vertex_clique_number` | Returns a dictionary of sizes of the largest maximal cliques containing each vertex - :meth:`~Graph.cliques_get_clique_bipartite` | Returns a bipartite graph constructed such that maximal cliques are the right vertices and the left vertices are retained from the given graph - :meth:`~Graph.cliques_get_max_clique_graph` | Returns a graph constructed with maximal cliques as vertices, and edges between maximal cliques sharing vertices. - :meth:`~Graph.cliques_number_of` | Returns a dictionary of the number of maximal cliques containing each vertex, keyed by vertex. - :meth:`~Graph.clique_number` | Returns the order of the largest clique of the graph. - :meth:`~Graph.clique_maximum` | Returns the vertex set of a maximal order complete subgraph. - :meth:`~Graph.cliques_maximum` | Returns the list of all maximum cliques - :meth:`~Graph.cliques_maximal` | Returns the list of all maximal cliques - :meth:`~Graph.clique_polynomial` | Returns the clique polynomial - -**Algorithmically hard stuff:** - -.. csv-table:: - :class: contentstable - :widths: 30, 70 - :delim: | - - :meth:`~Graph.vertex_cover` | Returns a minimum vertex cover. - :meth:`~Graph.independent_set` | Returns a maximum independent set. - :meth:`~Graph.topological_minor` | Returns a topological `H`-minor of ``self`` if one exists. - :meth:`~Graph.convexity_properties` | Returns a ``ConvexityProperties`` object corresponding to ``self``. - :meth:`~Graph.matching_polynomial` | Computes the matching polynomial. - :meth:`~Graph.rank_decomposition` | Returns an rank-decomposition of ``self`` achieving optiml rank-width. - :meth:`~Graph.minor` | Returns the vertices of a minor isomorphic to `H`. - :meth:`~Graph.independent_set_of_representatives` | Returns an independent set of representatives. - :meth:`~Graph.coloring` | Returns the first (optimal) proper vertex-coloring found. - :meth:`~Graph.has_homomorphism_to` | Checks whether there is a morphism between two graphs. - :meth:`~Graph.chromatic_number` | Returns the minimal number of colors needed to color the vertices. - :meth:`~Graph.chromatic_polynomial` | Returns the chromatic polynomial. - :meth:`~Graph.chromatic_symmetric_function` | Return the chromatic symmetric function. - :meth:`~Graph.tutte_polynomial` | Returns the Tutte polynomial. - :meth:`~Graph.is_perfect` | Tests whether the graph is perfect. - :meth:`~Graph.treewidth` | Computes the tree-width and provides a decomposition. - - -**Leftovers:** - -.. csv-table:: - :class: contentstable - :widths: 30, 70 - :delim: | - - :meth:`~Graph.cores` | Returns the core number for each vertex in an ordered list. - :meth:`~Graph.matching` | Returns a maximum weighted matching of the graph - :meth:`~Graph.fractional_chromatic_index` | Computes the fractional chromatic index. - :meth:`~Graph.lovasz_theta` | Returns the Lovasz number (a.k.a theta). - :meth:`~Graph.kirchhoff_symanzik_polynomial` | Returns the Kirchhoff-Symanzik polynomial. - :meth:`~Graph.modular_decomposition` | Returns the modular decomposition. - :meth:`~Graph.maximum_average_degree` | Returns the Maximum Average Degree (MAD). - :meth:`~Graph.two_factor_petersen` | Returns a decomposition of the graph into 2-factors. - :meth:`~Graph.ihara_zeta_function_inverse` | Returns the inverse of the zeta function. +{INDEX_OF_METHODS} AUTHORS: @@ -550,7 +416,7 @@ from sage.graphs.digraph import DiGraph from sage.graphs.independent_sets import IndependentSets from sage.combinat.combinatorial_map import combinatorial_map - +from sage.misc.rest_index_of_methods import doc_index, gen_thematic_rest_table_index class Graph(GenericGraph): r""" @@ -1603,6 +1469,8 @@ def __init__(self, data=None, pos=None, loops=None, format=None, self._immutable = True ### Formats + + @doc_index("Basic methods") def graph6_string(self): """ Returns the graph6 representation of the graph as an ASCII string. @@ -1630,6 +1498,7 @@ def graph6_string(self): else: return generic_graph_pyx.small_integer_to_graph6(n) + generic_graph_pyx.binary_string_to_graph6(self._bit_vector()) + @doc_index("Basic methods") def sparse6_string(self): r""" Returns the sparse6 representation of the graph as an ASCII string. @@ -1718,6 +1587,7 @@ def sparse6_string(self): ### Attributes @combinatorial_map(name="partition of connected components") + @doc_index("Deprecated") def to_partition(self): """ Return the partition of connected components of ``self``. @@ -1738,6 +1608,7 @@ def to_partition(self): from sage.combinat.partition import Partition return Partition(sorted([len(y) for y in self.connected_components()], reverse=True)) + @doc_index("Basic methods") def is_directed(self): """ Since graph is undirected, returns False. @@ -1749,6 +1620,7 @@ def is_directed(self): """ return False + @doc_index("Connectivity, orientations, trees") def bridges(self): r""" Returns a list of the bridges (or cut edges). @@ -1774,6 +1646,7 @@ def bridges(self): bridges.extend(gs.edge_boundary(scc)) return bridges + @doc_index("Connectivity, orientations, trees") def spanning_trees(self): """ Returns a list of all spanning trees. @@ -1859,6 +1732,7 @@ def _recursive_spanning_trees(G,forest): return [] ### Properties + @doc_index("Graph properties") def is_tree(self, certificate=False, output='vertex'): """ Tests if the graph is a tree @@ -1989,6 +1863,7 @@ def vertices_to_edges(x): else: return self.num_verts() == self.num_edges() + 1 + @doc_index("Graph properties") def is_forest(self, certificate=False, output='vertex'): """ Tests if the graph is a forest, i.e. a disjoint union of trees. @@ -2040,6 +1915,7 @@ def is_forest(self, certificate=False, output='vertex'): if not isit: return (False, cycle) + @doc_index("Graph properties") def is_overfull(self): r""" Tests whether the current graph is overfull. @@ -2139,6 +2015,7 @@ def is_overfull(self): return (self.order() % 2 == 1) and ( 2 * self.size() > max(self.degree()) * (self.order() - 1)) + @doc_index("Graph properties") def is_even_hole_free(self, certificate = False): r""" Tests whether ``self`` contains an induced even hole. @@ -2247,6 +2124,7 @@ def is_even_hole_free(self, certificate = False): return True + @doc_index("Graph properties") def is_odd_hole_free(self, certificate = False): r""" Tests whether ``self`` contains an induced odd hole. @@ -2326,6 +2204,7 @@ def is_odd_hole_free(self, certificate = False): return True + @doc_index("Graph properties") def is_bipartite(self, certificate = False): """ Returns ``True`` if graph `G` is bipartite, ``False`` if not. @@ -2406,6 +2285,7 @@ def is_bipartite(self, certificate = False): else: return True + @doc_index("Graph properties") def is_triangle_free(self, algorithm='bitset'): r""" Returns whether ``self`` is triangle-free @@ -2497,6 +2377,7 @@ def is_triangle_free(self, algorithm='bitset'): else: raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm)) + @doc_index("Graph properties") def is_split(self): r""" Returns ``True`` if the graph is a Split graph, ``False`` otherwise. @@ -2570,6 +2451,7 @@ def is_split(self): return left == right + @doc_index("Algorithmically hard stuff") def treewidth(self,k=None,certificate=False): r""" Computes the tree-width of `G` (and provides a decomposition) @@ -2784,6 +2666,7 @@ def rec(cut,cc): return G + @doc_index("Algorithmically hard stuff") def is_perfect(self, certificate = False): r""" Tests whether the graph is perfect. @@ -2912,6 +2795,7 @@ def is_perfect(self, certificate = False): return self_complement.is_odd_hole_free(certificate = certificate) + @doc_index("Graph properties") def odd_girth(self): r""" Returns the odd girth of self. @@ -2979,6 +2863,7 @@ def odd_girth(self): return Infinity + @doc_index("Graph properties") def is_edge_transitive(self): """ Returns true if self is an edge transitive graph. @@ -3025,6 +2910,7 @@ def is_edge_transitive(self): return gap("OrbitLength("+str(A._gap_())+",Set(" + str(e) + "),OnSets);") == self.size() + @doc_index("Graph properties") def is_arc_transitive(self): """ Returns true if self is an arc-transitive graph @@ -3067,6 +2953,7 @@ def is_arc_transitive(self): return gap("OrbitLength("+str(A._gap_())+",Set(" + str(e) + "),OnTuples);") == 2*self.size() + @doc_index("Graph properties") def is_half_transitive(self): """ Returns true if self is a half-transitive graph. @@ -3106,6 +2993,7 @@ def is_half_transitive(self): self.is_vertex_transitive() and not self.is_arc_transitive()) + @doc_index("Graph properties") def is_semi_symmetric(self): """ Returns true if self is semi-symmetric. @@ -3150,6 +3038,7 @@ def is_semi_symmetric(self): self.is_edge_transitive() and not self.is_vertex_transitive()) + @doc_index("Connectivity, orientations, trees") def degree_constrained_subgraph(self, bounds=None, solver=None, verbose=0): r""" Returns a degree-constrained subgraph. @@ -3248,6 +3137,7 @@ def degree_constrained_subgraph(self, bounds=None, solver=None, verbose=0): ### Orientations + @doc_index("Connectivity, orientations, trees") def strong_orientation(self): r""" Returns a strongly connected orientation of the current graph. See @@ -3354,6 +3244,7 @@ def strong_orientation(self): return d + @doc_index("Connectivity, orientations, trees") def minimum_outdegree_orientation(self, use_edge_labels=False, solver=None, verbose=0): r""" Returns an orientation of ``self`` with the smallest possible maximum @@ -3462,6 +3353,7 @@ def minimum_outdegree_orientation(self, use_edge_labels=False, solver=None, verb return O + @doc_index("Connectivity, orientations, trees") def bounded_outdegree_orientation(self, bound): r""" Computes an orientation of ``self`` such that every vertex `v` @@ -3619,6 +3511,7 @@ def bounded_outdegree_orientation(self, bound): ### Coloring + @doc_index("Basic methods") def bipartite_color(self): """ Returns a dictionary with vertices as the keys and the color class @@ -3640,6 +3533,7 @@ def bipartite_color(self): else: raise RuntimeError("Graph is not bipartite.") + @doc_index("Basic methods") def bipartite_sets(self): """ Returns `(X,Y)` where `X` and `Y` are the nodes in each bipartite set of @@ -3666,6 +3560,7 @@ def bipartite_sets(self): return left, right + @doc_index("Algorithmically hard stuff") def chromatic_number(self, algorithm="DLX", verbose = 0): r""" Returns the minimal number of colors needed to color the vertices @@ -3765,6 +3660,7 @@ def chromatic_number(self, algorithm="DLX", verbose = 0): else: raise ValueError("The 'algorithm' keyword must be set to either 'DLX', 'MILP' or 'CP'.") + @doc_index("Algorithmically hard stuff") def coloring(self, algorithm="DLX", hex_colors=False, verbose = 0): r""" Returns the first (optimal) proper vertex-coloring found. @@ -3839,6 +3735,7 @@ def coloring(self, algorithm="DLX", hex_colors=False, verbose = 0): else: raise ValueError("The 'algorithm' keyword must be set to either 'DLX' or 'MILP'.") + @doc_index("Algorithmically hard stuff") def chromatic_symmetric_function(self, R=None): r""" Return the chromatic symmetric function of ``self``. @@ -3912,6 +3809,7 @@ def chromatic_symmetric_function(self, R=None): ret += (-1)**len(F) * p[la] return ret + @doc_index("Leftovers") def matching(self, value_only=False, algorithm="Edmonds", use_edge_labels=True, solver=None, verbose=0): r""" Returns a maximum weighted matching of the graph @@ -4052,6 +3950,7 @@ def matching(self, value_only=False, algorithm="Edmonds", use_edge_labels=True, else: raise ValueError('algorithm must be set to either "Edmonds" or "LP"') + @doc_index("Algorithmically hard stuff") def has_homomorphism_to(self, H, core = False, solver = None, verbose = 0): r""" Checks whether there is a homomorphism between two graphs. @@ -4159,6 +4058,7 @@ def has_homomorphism_to(self, H, core = False, solver = None, verbose = 0): except MIPSolverException: return False + @doc_index("Leftovers") def fractional_chromatic_index(self, solver = None, verbose_constraints = 0, verbose = 0): r""" Computes the fractional chromatic index of ``self`` @@ -4279,6 +4179,7 @@ def fractional_chromatic_index(self, solver = None, verbose_constraints = 0, ver # Accomplished ! return obj + @doc_index("Leftovers") def maximum_average_degree(self, value_only=True, solver = None, verbose = 0): r""" Returns the Maximum Average Degree (MAD) of the current graph. @@ -4384,6 +4285,7 @@ def maximum_average_degree(self, value_only=True, solver = None, verbose = 0): else: return g_mad + @doc_index("Algorithmically hard stuff") def independent_set_of_representatives(self, family, solver=None, verbose=0): r""" Returns an independent set of representatives. @@ -4512,6 +4414,7 @@ def independent_set_of_representatives(self, family, solver=None, verbose=0): return repr + @doc_index("Algorithmically hard stuff") def minor(self, H, solver=None, verbose=0): r""" Returns the vertices of a minor isomorphic to `H` in the current graph. @@ -4677,6 +4580,7 @@ def minor(self, H, solver=None, verbose=0): ### Convexity + @doc_index("Algorithmically hard stuff") def convexity_properties(self): r""" Returns a ``ConvexityProperties`` object corresponding to ``self``. @@ -4718,6 +4622,7 @@ def convexity_properties(self): return ConvexityProperties(self) # Centrality + @doc_index("Distances") def centrality_degree(self, v=None): r""" Returns the degree centrality of a vertex. @@ -4763,6 +4668,7 @@ def centrality_degree(self, v=None): else: return self.degree(v)/n_minus_one + @doc_index("Distances") def centrality_closeness(self, v=None): r""" Returns the closeness centrality of a vertex. @@ -4814,6 +4720,7 @@ def centrality_closeness(self, v=None): ### Constructors + @doc_index("Basic methods") def to_directed(self, implementation='c_graph', data_structure=None, sparse=None): """ @@ -4884,6 +4791,7 @@ def to_directed(self, implementation='c_graph', data_structure=None, return D + @doc_index("Basic methods") def to_undirected(self): """ Since the graph is already undirected, simply returns a copy of @@ -4896,6 +4804,7 @@ def to_undirected(self): """ return self.copy() + @doc_index("Basic methods") def join(self, other, verbose_relabel=None, labels="pairs"): """ Returns the join of self and other. @@ -4966,6 +4875,7 @@ def join(self, other, verbose_relabel=None, labels="pairs"): ### Visualization + @doc_index("Basic methods") def write_to_eps(self, filename, **options): r""" Writes a plot of the graph to ``filename`` in ``eps`` format. @@ -4998,6 +4908,7 @@ def write_to_eps(self, filename, **options): f.write( print_graph_eps(self.vertices(), self.edge_iterator(), pos) ) f.close() + @doc_index("Algorithmically hard stuff") def topological_minor(self, H, vertices = False, paths = False, solver=None, verbose=0): r""" Returns a topological `H`-minor from ``self`` if one exists. @@ -5229,6 +5140,7 @@ def topological_minor(self, H, vertices = False, paths = False, solver=None, ver ### Cliques + @doc_index("Clique-related methods") def cliques_maximal(self, algorithm = "native"): """ Returns the list of all maximal cliques, with each clique represented @@ -5304,6 +5216,7 @@ def cliques_maximal(self, algorithm = "native"): else: raise ValueError("Algorithm must be equal to 'native' or to 'NetworkX'.") + @doc_index("Clique-related methods") def clique_maximum(self, algorithm="Cliquer"): """ Returns the vertex set of a maximal order complete subgraph. @@ -5374,6 +5287,7 @@ def clique_maximum(self, algorithm="Cliquer"): else: raise NotImplementedError("Only 'MILP', 'Cliquer' and 'mcqd' are supported.") + @doc_index("Clique-related methods") def clique_number(self, algorithm="Cliquer", cliques=None): r""" Returns the order of the largest clique of the graph (the clique @@ -5464,6 +5378,7 @@ def clique_number(self, algorithm="Cliquer", cliques=None): else: raise NotImplementedError("Only 'networkx' 'MILP' 'Cliquer' and 'mcqd' are supported.") + @doc_index("Clique-related methods") def cliques_number_of(self, vertices=None, cliques=None): """ Returns a dictionary of the number of maximal cliques containing each @@ -5514,6 +5429,7 @@ def cliques_number_of(self, vertices=None, cliques=None): import networkx return networkx.number_of_cliques(self.networkx_graph(copy=False), vertices, cliques) + @doc_index("Clique-related methods") def cliques_get_max_clique_graph(self, name=''): """ Returns a graph constructed with maximal cliques as vertices, and @@ -5545,6 +5461,7 @@ def cliques_get_max_clique_graph(self, name=''): import networkx return Graph(networkx.make_max_clique_graph(self.networkx_graph(copy=False), name=name, create_using=networkx.MultiGraph())) + @doc_index("Clique-related methods") def cliques_get_clique_bipartite(self, **kwds): """ Returns a bipartite graph constructed such that maximal cliques are the @@ -5572,6 +5489,7 @@ def cliques_get_clique_bipartite(self, **kwds): import networkx return BipartiteGraph(networkx.make_clique_bipartite(self.networkx_graph(copy=False), **kwds)) + @doc_index("Algorithmically hard stuff") def independent_set(self, algorithm = "Cliquer", value_only = False, reduction_rules = True, solver = None, verbosity = 0): r""" Returns a maximum independent set. @@ -5661,6 +5579,7 @@ def independent_set(self, algorithm = "Cliquer", value_only = False, reduction_r return [u for u in self.vertices() if not u in my_cover] + @doc_index("Algorithmically hard stuff") def vertex_cover(self, algorithm = "Cliquer", value_only = False, reduction_rules = True, solver = None, verbosity = 0): r""" @@ -5945,6 +5864,7 @@ def vertex_cover(self, algorithm = "Cliquer", value_only = False, cover_g.sort() return cover_g + @doc_index("Clique-related methods") def cliques_vertex_clique_number(self, algorithm="cliquer", vertices=None, cliques=None): """ @@ -6016,6 +5936,7 @@ def cliques_vertex_clique_number(self, algorithm="cliquer", vertices=None, else: raise NotImplementedError("Only 'networkx' and 'cliquer' are supported.") + @doc_index("Clique-related methods") def cliques_containing_vertex(self, vertices=None, cliques=None): """ Returns the cliques containing each vertex, represented as a dictionary @@ -6066,6 +5987,7 @@ def cliques_containing_vertex(self, vertices=None, cliques=None): import networkx return networkx.cliques_containing_node(self.networkx_graph(copy=False),vertices, cliques) + @doc_index("Clique-related methods") def clique_complex(self): """ Returns the clique complex of self. This is the largest simplicial complex on @@ -6097,6 +6019,7 @@ def clique_complex(self): C._graph = self return C + @doc_index("Clique-related methods") def clique_polynomial(self, t = None): """ Returns the clique polynomial of self. @@ -6128,6 +6051,7 @@ def clique_polynomial(self, t = None): ### Miscellaneous + @doc_index("Leftovers") def cores(self, k = None, with_labels=False): """ Returns the core number for each vertex in an ordered list. @@ -6285,6 +6209,7 @@ def cores(self, k = None, with_labels=False): else: return core.values() + @doc_index("Leftovers") def modular_decomposition(self): r""" Returns the modular decomposition of the current graph. @@ -6427,6 +6352,7 @@ def modular_decomposition(self): return relabel(D) + @doc_index("Graph properties") def is_prime(self): r""" Tests whether the current graph is prime. @@ -6554,6 +6480,7 @@ def _gomory_hu_tree(self, vertices, method="FF"): return g + @doc_index("Connectivity, orientations, trees") def gomory_hu_tree(self, method="FF"): r""" Returns a Gomory-Hu tree of self. @@ -6648,6 +6575,7 @@ def gomory_hu_tree(self, method="FF"): g.set_pos(dict(self.get_pos())) return g + @doc_index("Leftovers") def two_factor_petersen(self): r""" Returns a decomposition of the graph into 2-factors. @@ -6717,6 +6645,7 @@ def two_factor_petersen(self): return classes_b + @doc_index("Leftovers") def kirchhoff_symanzik_polynomial(self, name='t'): """ Return the Kirchhoff-Symanzik polynomial of a graph. @@ -6816,6 +6745,7 @@ def kirchhoff_symanzik_polynomial(self, name='t'): D = matrix.diagonal(PolynomialRing(ZZ, name, self.size()).gens()) return (circuit_mtrx.transpose() * D * circuit_mtrx).determinant() + @doc_index("Leftovers") def ihara_zeta_function_inverse(self): """ Compute the inverse of the Ihara zeta function of the graph @@ -6890,43 +6820,63 @@ def ihara_zeta_function_inverse(self): import types import sage.graphs.weakly_chordal -Graph.is_long_hole_free = types.MethodType(sage.graphs.weakly_chordal.is_long_hole_free, None, Graph) -Graph.is_long_antihole_free = types.MethodType(sage.graphs.weakly_chordal.is_long_antihole_free, None, Graph) -Graph.is_weakly_chordal = types.MethodType(sage.graphs.weakly_chordal.is_weakly_chordal, None, Graph) +Graph.is_long_hole_free = types.MethodType(sage.graphs.weakly_chordal.is_long_hole_free, None, Graph) +Graph.is_long_antihole_free = types.MethodType(sage.graphs.weakly_chordal.is_long_antihole_free, None, Graph) +Graph.is_weakly_chordal = types.MethodType(sage.graphs.weakly_chordal.is_weakly_chordal, None, Graph) import sage.graphs.asteroidal_triples Graph.is_asteroidal_triple_free = types.MethodType(sage.graphs.asteroidal_triples.is_asteroidal_triple_free, None, Graph) import sage.graphs.chrompoly -Graph.chromatic_polynomial = types.MethodType(sage.graphs.chrompoly.chromatic_polynomial, None, Graph) +Graph.chromatic_polynomial = types.MethodType(sage.graphs.chrompoly.chromatic_polynomial, None, Graph) import sage.graphs.graph_decompositions.rankwidth -Graph.rank_decomposition = types.MethodType(sage.graphs.graph_decompositions.rankwidth.rank_decomposition, None, Graph) +Graph.rank_decomposition = types.MethodType(sage.graphs.graph_decompositions.rankwidth.rank_decomposition, None, Graph) import sage.graphs.matchpoly -Graph.matching_polynomial = types.MethodType(sage.graphs.matchpoly.matching_polynomial, None, Graph) +Graph.matching_polynomial = types.MethodType(sage.graphs.matchpoly.matching_polynomial, None, Graph) import sage.graphs.cliquer -Graph.cliques_maximum = types.MethodType(sage.graphs.cliquer.all_max_clique, None, Graph) +Graph.cliques_maximum = types.MethodType(sage.graphs.cliquer.all_max_clique, None, Graph) import sage.graphs.spanning_tree -Graph.random_spanning_tree = types.MethodType(sage.graphs.spanning_tree.random_spanning_tree, None, Graph) +Graph.random_spanning_tree = types.MethodType(sage.graphs.spanning_tree.random_spanning_tree, None, Graph) import sage.graphs.graph_decompositions.graph_products -Graph.is_cartesian_product = types.MethodType(sage.graphs.graph_decompositions.graph_products.is_cartesian_product, None, Graph) +Graph.is_cartesian_product = types.MethodType(sage.graphs.graph_decompositions.graph_products.is_cartesian_product, None, Graph) import sage.graphs.distances_all_pairs -Graph.is_distance_regular = types.MethodType(sage.graphs.distances_all_pairs.is_distance_regular, None, Graph) +Graph.is_distance_regular = types.MethodType(sage.graphs.distances_all_pairs.is_distance_regular, None, Graph) import sage.graphs.base.static_dense_graph -Graph.is_strongly_regular = types.MethodType(sage.graphs.base.static_dense_graph.is_strongly_regular, None, Graph) +Graph.is_strongly_regular = types.MethodType(sage.graphs.base.static_dense_graph.is_strongly_regular, None, Graph) # From Python modules import sage.graphs.line_graph -Graph.is_line_graph = sage.graphs.line_graph.is_line_graph +Graph.is_line_graph = sage.graphs.line_graph.is_line_graph from sage.graphs.tutte_polynomial import tutte_polynomial -Graph.tutte_polynomial = tutte_polynomial +Graph.tutte_polynomial = tutte_polynomial from sage.graphs.lovasz_theta import lovasz_theta -Graph.lovasz_theta = lovasz_theta +Graph.lovasz_theta = lovasz_theta + +_additional_categories = { + Graph.is_long_hole_free : "Graph properties", + Graph.is_long_antihole_free : "Graph properties", + Graph.is_weakly_chordal : "Graph properties", + Graph.is_asteroidal_triple_free : "Graph properties", + Graph.chromatic_polynomial : "Algorithmically hard stuff", + Graph.rank_decomposition : "Algorithmically hard stuff", + Graph.matching_polynomial : "Algorithmically hard stuff", + Graph.cliques_maximum : "Clique-related methods", + Graph.random_spanning_tree : "Connectivity, orientations, trees", + Graph.is_cartesian_product : "Graph properties", + Graph.is_distance_regular : "Graph properties", + Graph.is_strongly_regular : "Graph properties", + Graph.is_line_graph : "Graph properties", + Graph.tutte_polynomial : "Algorithmically hard stuff", + Graph.lovasz_theta : "Leftovers", + } + +__doc__ = __doc__.replace("{INDEX_OF_METHODS}",gen_thematic_rest_table_index(Graph,_additional_categories)) diff --git a/src/sage/misc/rest_index_of_methods.py b/src/sage/misc/rest_index_of_methods.py index 6c74764b983..7a4f19f69d1 100644 --- a/src/sage/misc/rest_index_of_methods.py +++ b/src/sage/misc/rest_index_of_methods.py @@ -7,7 +7,7 @@ {INDEX_OF_FUNCTIONS} """ -def gen_rest_table_index(list_of_entries, sort=True, only_local_functions=True): +def gen_rest_table_index(list_of_entries, names=None, sort=True, only_local_functions=True): r""" Return a ReST table describing a list of functions. @@ -24,6 +24,10 @@ def gen_rest_table_index(list_of_entries, sort=True, only_local_functions=True): deprecated or those starting with an underscore. In the case of a class, note that inherited methods are not displayed. + - ``names`` -- a dictionary associating a name to a function. Takes + precedence over the automatically computed name for the functions. Only + used when ``list_of_entries`` is a list. + - ``sort`` (boolean; ``True``) -- whether to sort the list of methods lexicographically. @@ -51,7 +55,12 @@ def gen_rest_table_index(list_of_entries, sort=True, only_local_functions=True): :widths: 30, 70 :delim: | - :func:`~sage.misc.rest_index_of_methods.gen_rest_table_index` | Return a ReST table describing a list of functions... + :func:`~sage.misc.rest_index_of_methods.doc_index` | Attribute an index name to a function. + :func:`~sage.misc.rest_index_of_methods.gen_rest_table_index` | Return a ReST table describing a list of functions. + :func:`~sage.misc.rest_index_of_methods.gen_thematic_rest_table_index` | Return a ReST string of thematically sorted function (or methods) of a module (or class). + :func:`~sage.misc.rest_index_of_methods.list_of_subfunctions` | Returns the functions (resp. methods) of a given module (resp. class) with their names. + + The table of a class:: @@ -85,33 +94,43 @@ def gen_rest_table_index(list_of_entries, sort=True, only_local_functions=True): :widths: 30, 70 :delim: | - :func:`~sage.misc.rest_index_of_methods.gen_rest_table_index` | Return a ReST table describing a list of functions... + :func:`~sage.misc.rest_index_of_methods.doc_index` | Attribute an index name to a function. + :func:`~sage.misc.rest_index_of_methods.gen_rest_table_index` | Return a ReST table describing a list of functions. + :func:`~sage.misc.rest_index_of_methods.gen_thematic_rest_table_index` | Return a ReST string of thematically sorted function (or methods) of a module (or class). + :func:`~sage.misc.rest_index_of_methods.list_of_subfunctions` | Returns the functions (resp. methods) of a given module (resp. class) with their names. + + sage: print gen_rest_table_index(sage.misc.rest_index_of_methods, only_local_functions=False) .. csv-table:: :class: contentstable :widths: 30, 70 :delim: | + + :func:`~sage.misc.rest_index_of_methods.doc_index` | Attribute an index name to a function. + :func:`~sage.misc.rest_index_of_methods.gen_thematic_rest_table_index` | Return a ReST string of thematically sorted function (or methods) of a module (or class). + :func:`~sage.misc.rest_index_of_methods.list_of_subfunctions` | Returns the functions (resp. methods) of a given module (resp. class) with their names. + A function that is imported into a class under a different name is listed + under its 'new' name:: + + sage: 'cliques_maximum' in gen_rest_table_index(Graph) + True + sage: 'all_max_cliques`' in gen_rest_table_index(Graph) + False """ import inspect + if names is None: + names = {} # If input is a class/module, we list all its non-private and methods/functions if (inspect.isclass(list_of_entries) or inspect.ismodule(list_of_entries)): root = list_of_entries - def local_filter(f,name): - if only_local_functions: - return inspect.getmodule(root) == inspect.getmodule(f) - else: - return inspect.isclass(list_of_entries) or not (f is gen_rest_table_index) - list_of_entries = [getattr(root,name) for name,f in root.__dict__.items() if - (not name.startswith('_') and # private functions - not hasattr(f,'trac_number') and # deprecated functions - not inspect.isclass(f) and # classes - local_filter(f,name) # possibly filter imported functions - )] + list_of_entries,names = list_of_subfunctions(root, only_local_functions=only_local_functions) + + fname = lambda x:names.get(x,getattr(x,"__name__","")) assert isinstance(list_of_entries,list) @@ -121,14 +140,14 @@ def local_filter(f,name): " :delim: |\n\n") if sort: - list_of_entries.sort(key=lambda x:getattr(x,'__name__','')) + list_of_entries.sort(key=fname) for e in list_of_entries: if inspect.ismethod(e): - link = ":meth:`~"+str(e.im_class.__module__)+"."+str(e.im_class.__name__)+"."+e.__name__+"`" + link = ":meth:`~"+str(e.im_class.__module__)+"."+str(e.im_class.__name__)+"."+fname(e)+"`" elif inspect.isfunction(e): - link = ":func:`~"+str(e.__module__)+"."+str(e.__name__)+"`" + link = ":func:`~"+str(e.__module__)+"."+fname(e)+"`" else: continue @@ -143,4 +162,120 @@ def local_filter(f,name): return s+'\n' +def list_of_subfunctions(root, only_local_functions=True): + r""" + Returns the functions (resp. methods) of a given module (resp. class) with their names. + + INPUT: + + - ``root`` -- the module, or class, whose elements are to be listed. + + - ``only_local_functions`` (boolean; ``True``) -- if ``root`` is a module, + ``only_local_functions = True`` means that imported functions will be + filtered out. This can be useful to disable for making indexes of + e.g. catalog modules such as :mod:`sage.coding.codes_catalog`. + + OUTPUT: + + A pair ``(list,dict)`` where ``list`` is a list of function/methods and + ``dict`` associates to every function/method the name under which it appears + in ``root``. + + EXAMPLE:: + + sage: from sage.misc.rest_index_of_methods import list_of_subfunctions + sage: l = list_of_subfunctions(Graph)[0] + sage: Graph.bipartite_color in l + True + """ + import inspect + if inspect.ismodule(root): + ismodule = True + elif inspect.isclass(root): + ismodule = False + superclasses = inspect.getmro(root)[1:] + else: + raise ValueError("'root' must be a module or a class.") + + def local_filter(f,name): + if only_local_functions: + if ismodule: + return inspect.getmodule(root) == inspect.getmodule(f) + else: + return not any(hasattr(s,name) for s in superclasses) + else: + return inspect.isclass(root) or not (f is gen_rest_table_index) + + functions = {getattr(root,name):name for name,f in root.__dict__.items() if + (not name.startswith('_') and # private functions + not hasattr(f,'trac_number') and # deprecated functions + not inspect.isclass(f) and # classes + callable(f) and # e.g. GenericGraph.graphics_array_defaults + local_filter(f,name)) # possibly filter imported functions + } + return functions.keys(),functions + +def gen_thematic_rest_table_index(root,additional_categories=None,only_local_functions=True): + r""" + Return a ReST string of thematically sorted function (or methods) of a module (or class). + + INPUT: + + - ``root`` -- the module, or class, whose elements are to be listed. + + - ``additional_categories`` -- a dictionary associating a category (given as + a string) to a function's name. Can be used when the decorator + :func:`doc_index` does not work on a function. + + - ``only_local_functions`` (boolean; ``True``) -- if ``root`` is a module, + ``only_local_functions = True`` means that imported functions will be + filtered out. This can be useful to disable for making indexes of + e.g. catalog modules such as :mod:`sage.coding.codes_catalog`. + + EXAMPLE:: + + sage: from sage.misc.rest_index_of_methods import gen_thematic_rest_table_index, list_of_subfunctions + sage: l = list_of_subfunctions(Graph)[0] + sage: Graph.bipartite_color in l + True + """ + from collections import defaultdict + if additional_categories is None: + additional_categories = {} + + functions,names = list_of_subfunctions(root,only_local_functions=only_local_functions) + theme_to_function = defaultdict(list) + for f in functions: + theme_to_function[getattr(f,"doc_index",additional_categories.get(f,"Unsorted"))].append(f) + s = ["**"+theme+"**\n\n"+gen_rest_table_index(list_of_functions,names=names) + for theme, list_of_functions in sorted(theme_to_function.items())] + return "\n\n".join(s) + +def doc_index(name): + r""" + Attribute an index name to a function. + + This decorator can be applied to a function/method in order to specify in + which index it must appear, in the index generated by + :func:`gen_thematic_rest_table_index`. + + INPUT: + + - ``name`` -- a string, which will become the title of the index in which + this function/method will appear. + + EXAMPLE:: + + sage: from sage.misc.rest_index_of_methods import doc_index + sage: @doc_index("Wouhouuuuu") + ....: def a(): + ....: print "Hey" + sage: a.doc_index + 'Wouhouuuuu' + """ + def hey(f): + setattr(f,"doc_index",name) + return f + return hey + __doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index([gen_rest_table_index])) From 6122b083c484cb580e61040fade56fded6da9a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 20 Aug 2015 22:26:11 +0200 Subject: [PATCH 122/421] trac #18937 now 2.4.0, still trying to switch to new style spkg --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- build/pkgs/patchbot/spkg-install | 11 +++-------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index 73aec48e51f..62ffca027aa 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=338e924734df1228427a03fbf82ea65ec345b420 -md5=aaf9ee33455656a47f687e553d401cac -cksum=1478772629 +sha1=25e1f2a8bbe84a01e2b5157cfeffc02218a6e7ab +md5=d2b2fde69496db246d2fe3b086ad20d5 +cksum=532828853 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 5aa7c523257..197c4d5c2d7 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.3.9 +2.4.0 diff --git a/build/pkgs/patchbot/spkg-install b/build/pkgs/patchbot/spkg-install index 657ce96695c..787152c6d9c 100755 --- a/build/pkgs/patchbot/spkg-install +++ b/build/pkgs/patchbot/spkg-install @@ -6,16 +6,11 @@ if [ "$SAGE_LOCAL" = "" ]; then exit 1 fi -# Move any currently exising patchbot out of the way. +# Delete any currently existing patchbot if [ -e "$SAGE_LOCAL/bin/patchbot" ]; then - i=0 - while [ -e "$SAGE_LOCAL/bin/patchbot-old-$i" ]; do - i=$(( $i + 1 )) - done - echo "Renaming existing patchbot directory to patchbot-old-$i" - mv "$SAGE_LOCAL/bin/patchbot" "$SAGE_LOCAL/bin/patchbot-old-$i" + rm -rf "$SAGE_LOCAL/bin/patchbot" fi # Copy into final location. -# The sage-sage script knows how to call this... +# The sage-patchbot script knows how to call this... cp -Rv src "$SAGE_LOCAL/bin/patchbot" From 62756e97c33ed9b8cbc04f5e50da0a9f71771f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 21 Aug 2015 09:40:52 +0200 Subject: [PATCH 123/421] trac #18937 simplify spkg-install --- build/pkgs/patchbot/spkg-install | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build/pkgs/patchbot/spkg-install b/build/pkgs/patchbot/spkg-install index 787152c6d9c..5dcc96f4629 100755 --- a/build/pkgs/patchbot/spkg-install +++ b/build/pkgs/patchbot/spkg-install @@ -7,9 +7,7 @@ if [ "$SAGE_LOCAL" = "" ]; then fi # Delete any currently existing patchbot -if [ -e "$SAGE_LOCAL/bin/patchbot" ]; then - rm -rf "$SAGE_LOCAL/bin/patchbot" -fi +rm -rf "$SAGE_LOCAL/bin/patchbot" # Copy into final location. # The sage-patchbot script knows how to call this... From 2adcfeffa5d87f137ee9010fb69087a2b76e98a7 Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Mon, 24 Aug 2015 21:19:44 -0500 Subject: [PATCH 124/421] oops! Didn't completely merge --- src/sage/combinat/diagram_algebras.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index aa9c2e380ca..d8a013347c3 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -34,7 +34,6 @@ from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra from sage.combinat.permutation import Permutations from sage.combinat.combinat import (bell_number, catalan_number) ->>>>>>> t/18720/change_diagram_algebra_basis_set_partitions_from_list_to_generator from sage.sets.set import Set from sage.graphs.graph import Graph from sage.misc.cachefunc import cached_method From 47badc1b7322077ffbf2f89ab6ad2acc2dce18b4 Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Tue, 25 Aug 2015 16:10:54 +0200 Subject: [PATCH 125/421] Move fallback algorithm to category UFD --- .../unique_factorization_domains.py | 81 ++++++++++++++++ .../rings/polynomial/polynomial_element.pyx | 93 ++++--------------- 2 files changed, 97 insertions(+), 77 deletions(-) diff --git a/src/sage/categories/unique_factorization_domains.py b/src/sage/categories/unique_factorization_domains.py index 50c6fd107a4..ddedb674d3b 100644 --- a/src/sage/categories/unique_factorization_domains.py +++ b/src/sage/categories/unique_factorization_domains.py @@ -124,6 +124,87 @@ def is_unique_factorization_domain(self, proof=True): """ return True + def _gcd_univariate_polynomial(self, f, g): + """ + Return the greatest common divisor of ``f`` and ``g``. + + INPUT: + + - ``f``, ``g`` -- two polynomials defined over this UFD. + + .. NOTE:: + + This is a helper method for + :meth:`sage.rings.polynomial.polynomial_element.Polynomial.gcd`. + + ALGORITHM: + + Algorithm 3.3.1 in [GTM138]_, based on pseudo-division. + + EXAMPLES:: + + sage: R. = PolynomialRing(ZZ, sparse=True) + sage: S. = R[] + sage: p = (-3*x^2 - x)*T^3 - 3*x*T^2 + (x^2 - x)*T + 2*x^2 + 3*x - 2 + sage: q = (-x^2 - 4*x - 5)*T^2 + (6*x^2 + x + 1)*T + 2*x^2 - x + sage: quo,rem=p.pseudo_quo_rem(q); quo,rem + ((3*x^4 + 13*x^3 + 19*x^2 + 5*x)*T + 18*x^4 + 12*x^3 + 16*x^2 + 16*x, + (-113*x^6 - 106*x^5 - 133*x^4 - 101*x^3 - 42*x^2 - 41*x)*T - 34*x^6 + 13*x^5 + 54*x^4 + 126*x^3 + 134*x^2 - 5*x - 50) + sage: (-x^2 - 4*x - 5)^(3-2+1) * p == quo*q + rem + True + + REFERENCES: + + .. [GTM138] Henri Cohen. A Course in Computational Number Theory. + Graduate Texts in Mathematics, vol. 138. Springer, 1993. + """ + if f.degree() < g.degree(): + A,B = g, f + else: + A,B = f, g + + if B.is_zero(): + return A + + a = b = self.zero() + for c in A.coefficients(): + a = a.gcd(c) + if a.is_one(): + break + for c in B.coefficients(): + b = b.gcd(c) + if b.is_one(): + break + + parent = f.parent() + + d = a.gcd(b) + A = parent(A/a) + B = parent(B/b) + g = h = 1 + + delta = A.degree()-B.degree() + _,R = A.pseudo_quo_rem(B) + + while R.degree() > 0: + A = B + B = parent(R/(g*h**delta)) + g = A.leading_coefficient() + h = self(h*g**delta/h**delta) + delta = A.degree() - B.degree() + _, R = A.pseudo_quo_rem(B) + + if R.is_zero(): + b = self.zero() + for c in B.coefficients(): + b = b.gcd(c) + if b.is_one(): + break + + return parent(d*B/b) + + return d + class ElementMethods: # prime? # squareFree diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index a8a4f321be3..8d41a70e6f9 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3909,12 +3909,7 @@ cdef class Polynomial(CommutativeAlgebraElement): The actual algorithm for computing greatest common divisors depends on the base ring underlying the polynomial ring. If the base ring defines a method ``_gcd_univariate_polynomial``, then this method - will be called (see examples below). If no such method exists, a - fallback algorithm is used. - - ALGORITHM: - - The fallback algorithm is Algorithm 3.3.1 in [GTM138]_. + will be called (see examples below). EXAMPLES:: @@ -3926,84 +3921,28 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: (2*x).gcd(0) x - A fallback algorithm is implemented for rings that have no method + One can easily add gcd functionality to new rings by providing a method ``_gcd_univariate_polynomial``:: - sage: R. = ZZ[] - sage: S. = PolynomialRing(R, sparse=True) - sage: p = (-3*x^2 - x)*y^3 - 3*x*y^2 + (x^2 - x)*y + 2*x^2 + 3*x - 2 - sage: q = (-x^2 - 4*x - 5)*y^2 + (6*x^2 + x + 1)*y + 2*x^2 - x + sage: O = ZZ[-sqrt(5)] + sage: R. = O[] + sage: a = O.1 + sage: p = x + a + sage: q = x^2 - 5 sage: p.gcd(q) - 1 - sage: r = (1 + x)*y^2 + (x - 1)*y + 2*x + 3 - sage: (p*r).gcd(q*r) - (x + 1)*y^2 + (x - 1)*y + 2*x + 3 - - One can easily provide a specialized gcd function for rings by providing - a method ``_gcd_univariate_polynomial``:: - - sage: T. = QQ[] - sage: R._gcd_univariate_polynomial = lambda f,g: S(T(f).gcd(g)) - sage: (p*r).gcd(q*r) - (x + 1)*y^2 + (x - 1)*y + 2*x + 3 - sage: del R._gcd_univariate_polynomial - - REFERENCES: - - .. [GTM138] Henri Cohen. A Course in Computational Number Theory. - Graduate Texts in Mathematics, vol. 138. Springer, 1993. + Traceback (most recent call last): + ... + NotImplementedError: Order in Number Field in a with defining polynomial x^2 - 5 does not provide a gcd implementation for univariate polynomials + sage: S. = O.number_field()[] + sage: O._gcd_univariate_polynomial = lambda f,g : R(S(f).gcd(S(g))) + sage: p.gcd(q) + x + a + sage: del O._gcd_univariate_polynomial """ if hasattr(self.base_ring(), '_gcd_univariate_polynomial'): return self.base_ring()._gcd_univariate_polynomial(self, other) - - # Fallback algorithm: Algorithm 3.3.1 in Cohen [GTM 138] - if not self.parent().base_ring().is_unique_factorization_domain(): - raise ValueError("The base ring must be a unique factorization domain") - - if self.degree() < other.degree(): - A,B = other, self else: - A,B = self, other - - if B.is_zero(): - return A - - a = b = self.base_ring().zero() - for c in A.coefficients(): - a = a.gcd(c) - if a.is_one(): - break - for c in B.coefficients(): - b = b.gcd(c) - if b.is_one(): - break - - d = a.gcd(b) - A = self.parent()(A/a) - B = self.parent()(B/b) - g = h = 1 - - delta = A.degree()-B.degree() - _,R = A.pseudo_quo_rem(B) - - while R.degree() > 0: - A = B - B = self.parent()(R/(g*h**delta)) - g = A.leading_coefficient() - h = self.parent().base_ring()(h*g**delta/h**delta) - delta = A.degree() - B.degree() - _, R = A.pseudo_quo_rem(B) - - if R.is_zero(): - b = self.base_ring().zero() - for c in B.coefficients(): - b = b.gcd(c) - if b.is_one(): - break - - return self.parent()(d*B/b) - - return d + raise NotImplementedError("%s does not provide a gcd implementation for univariate polynomials"%self.base_ring()) @coerce_binop def lcm(self, other): From f9089369482cc589372b0f3a7461d633d9540964 Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Tue, 25 Aug 2015 16:51:04 +0200 Subject: [PATCH 126/421] Simplify GCD for sparse polynomials --- .../polynomial/polynomial_element_generic.py | 48 ++++++++----------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 26487f33b05..4fdbc4663ab 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -728,25 +728,24 @@ def quo_rem(self, other): def gcd(self,other,algorithm=None): """ - Return the gcd of ``self`` and ``other`` + Return the gcd of this polynomial and ``other`` INPUT: - - ``other`` -- a polynomial defined over the same ring as ``self`` + - ``other`` -- a polynomial defined over the same ring as this + polynomial. - Three algorithms are available: + ALGORITHM: - - "dense": The polynomials are converted to the dense representation, - their gcd are computed and is converted back to the sparse - representation. - - "fraction_field": The polynomials are coerced to the ring of - polynomials over the fraction field of their base ring. It won't - work with non integral domain as base rings. The gcd method is - called with the "dense" algorithm, in case there is no specific - sparse gcd method for the fraction field. - - "pseudo-division": Uses the gcd method of the class Polynomial. + Two algorithms are provided: - Default is "dense" for polynomials over ZZ and "pseudo-division" in the + - ``generic``: Uses the generic implementation, which depends on the + base ring being a UFD or a field. + - ``dense``: The polynomials are converted to the dense representation, + their gcd is computed and is converted back to the sparse + representation. + + Default is ``dense`` for polynomials over ZZ and ``generic`` in the other cases. EXAMPLES:: @@ -758,14 +757,12 @@ def gcd(self,other,algorithm=None): x^2 + x + 1 sage: gcd(p, q, algorithm = "dense") x^2 + x + 1 - sage: gcd(p, q, algorithm = "fraction_field") - x^2 + x + 1 - sage: gcd(p, q, algorithm = "pseudo-division") + sage: gcd(p, q, algorithm = "generic") x^2 + x + 1 - - AUTHORS: - - - Bruno Grenet (2014-06-25) + sage: gcd(p, q, algorithm = "foobar") + Traceback (most recent call last): + ... + ValueError: Unknown algorithm 'foobar' """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -775,7 +772,7 @@ def gcd(self,other,algorithm=None): if self.base_ring() == ZZ: algorithm = "dense" else: - algorithm = "pseudo-division" + algorithm = "generic" if algorithm=="dense": S = self.parent() # FLINT is faster but a bug makes the conversion extremely slow, @@ -791,14 +788,7 @@ def gcd(self,other,algorithm=None): D = PolynomialRing(S.base_ring(),'x',implementation=implementation) g = D(self).gcd(D(other)) return S(g) - if algorithm=="fraction_field": - R = self.parent().base_ring() - F = R.fraction_field() - S = PolynomialRing(F,'x',sparse=True) - g = S(self).gcd(S(other),algorithm="dense") - d = lcm([gg.denominator() for gg in g.coefficients()]) - return self.parent()(d*g) - if algorithm=="pseudo-division": + elif algorithm=="generic": return Polynomial.gcd(self,other) else: raise ValueError("Unknown algorithm '%s'" % algorithm) From 8aa97d33ebf996d3943db2cb470d67dbb2bdbbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 30 Aug 2015 17:42:04 +0200 Subject: [PATCH 127/421] trac #18937 patchbot version without pytz --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index 62ffca027aa..8fe090e4573 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=25e1f2a8bbe84a01e2b5157cfeffc02218a6e7ab -md5=d2b2fde69496db246d2fe3b086ad20d5 -cksum=532828853 +sha1=f8e41048da15add40d24c0d01a846bef2ff22f95 +md5=b063258d4dfbb6b888f38b53e257432b +cksum=3707204329 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 197c4d5c2d7..005119baaa0 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.4.0 +2.4.1 From 5ef48b17f3e76760d567e75f6420382709f1d482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 1 Sep 2015 15:56:05 +0200 Subject: [PATCH 128/421] trac #18937 adding the dependencies file --- build/pkgs/patchbot/dependencies | 1 + 1 file changed, 1 insertion(+) create mode 100644 build/pkgs/patchbot/dependencies diff --git a/build/pkgs/patchbot/dependencies b/build/pkgs/patchbot/dependencies new file mode 100644 index 00000000000..b50cc22b772 --- /dev/null +++ b/build/pkgs/patchbot/dependencies @@ -0,0 +1 @@ +# No dependencies \ No newline at end of file From 6d2c1f465c683641910916afcaf2323da42880f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 1 Sep 2015 15:59:32 +0200 Subject: [PATCH 129/421] adding a missing newline --- build/pkgs/patchbot/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/patchbot/dependencies b/build/pkgs/patchbot/dependencies index b50cc22b772..4e1e0144211 100644 --- a/build/pkgs/patchbot/dependencies +++ b/build/pkgs/patchbot/dependencies @@ -1 +1 @@ -# No dependencies \ No newline at end of file +# No dependencies From d2ff0627171251668b807e811bbcc3797ea56dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 1 Sep 2015 16:03:58 +0200 Subject: [PATCH 130/421] trac #18937 turning to 2.4.2 --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index 8fe090e4573..e801e6e5156 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=f8e41048da15add40d24c0d01a846bef2ff22f95 -md5=b063258d4dfbb6b888f38b53e257432b -cksum=3707204329 +sha1=9f7d1d3df8dbb8821d8d077f3ffb6ba94d464372 +md5=54155d5531d615928011387185f48b34 +cksum=3140942259 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 005119baaa0..8e8299dcc06 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.4.1 +2.4.2 From 7f6cffe7e38835212a508f6fcf4f617e43c4103b Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Tue, 1 Sep 2015 11:35:09 -0700 Subject: [PATCH 131/421] trac 19125: fix deprecated method binding --- src/sage/misc/superseded.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index 6c13f92a80a..1e4dcf4743c 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -274,12 +274,13 @@ class DeprecatedFunctionAlias(object): - Florent Hivert (2009-11-23), with the help of Mike Hansen. - Luca De Feo (2011-07-11), printing the full module path when different from old path """ - def __init__(self, trac_number, func, module): + def __init__(self, trac_number, func, module, instance = None, unbound = None): r""" TESTS:: sage: from sage.misc.superseded import deprecated_function_alias sage: g = deprecated_function_alias(13109, number_of_partitions) + sage: from sage.misc.superseded import deprecated_function_alias sage: g.__doc__ 'Deprecated: Use :func:`number_of_partitions` instead.\nSee :trac:`13109` for details.\n\n' """ @@ -290,7 +291,8 @@ def __init__(self, trac_number, func, module): pass # Cython classes don't have __dict__ self.func = func self.trac_number = trac_number - self.instance = None # for use with methods + self.instance = instance # for use with methods + self.unbound = unbound self.__module__ = module if isinstance(func, type(deprecation)): sphinxrole = "func" @@ -350,6 +352,12 @@ def is_class(gc_ref): for key, val in ref_copy.iteritems(): if val is self: return key + for ref in gc.get_referrers(self.unbound): + if is_class(ref): + ref_copy = copy.copy(ref) + for key, val in ref_copy.iteritems(): + if val is self.unbound: + return key raise AttributeError("The name of this deprecated function can not be determined") def __call__(self, *args, **kwds): @@ -388,8 +396,7 @@ def __get__(self, inst, cls = None): sage: obj.old_meth.instance is obj True """ - self.instance = inst - return self + return DeprecatedFunctionAlias(self.trac_number, self.func, self.__module__, instance = inst, unbound = self) def deprecated_function_alias(trac_number, func): From c148ac0677c14d23b5b9bb22f8fbc245c630a731 Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Tue, 1 Sep 2015 22:57:46 -0700 Subject: [PATCH 132/421] doctest and slightly less fragile method name lookup --- src/sage/misc/superseded.py | 39 +++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index 1e4dcf4743c..15c863adcd8 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -346,17 +346,15 @@ def is_class(gc_ref): is_python_class = '__module__' in gc_ref or '__package__' in gc_ref is_cython_class = '__new__' in gc_ref return is_python_class or is_cython_class - for ref in gc.get_referrers(self): - if is_class(ref): - ref_copy = copy.copy(ref) - for key, val in ref_copy.iteritems(): - if val is self: - return key - for ref in gc.get_referrers(self.unbound): - if is_class(ref): + if self.unbound is None: + search_for=self + else: + search_for=self.unbound + for ref in gc.get_referrers(search_for): + if is_class(ref) and ref is not self.__dict__: ref_copy = copy.copy(ref) for key, val in ref_copy.iteritems(): - if val is self.unbound: + if val is search_for: return key raise AttributeError("The name of this deprecated function can not be determined") @@ -371,6 +369,8 @@ def __call__(self, *args, **kwds): doctest:...: DeprecationWarning: blo is deprecated. Please use bla instead. See http://trac.sagemath.org/13109 for details. 42 + + """ if self.instance is None and self.__module__ != self.func.__module__: other = self.func.__module__ + "." + self.func.__name__ @@ -395,8 +395,27 @@ def __get__(self, inst, cls = None): sage: obj = cls() sage: obj.old_meth.instance is obj True + + :trac:`19125`:: + + sage: from sage.misc.superseded import deprecated_function_alias + sage: class A: + ....: def __init__(self, x): + ....: self.x = x + ....: def f(self, y): + ....: return self.x+y + ....: g = deprecated_function_alias(42, f) + sage: a1 = A(1) + sage: a2 = A(2) + sage: a1.g(a2.g(0)) + doctest:...: DeprecationWarning: g is deprecated. Please use f instead. + See http://trac.sagemath.org/42 for details. + 3 + sage: a1.f(a2.f(0)) + 3 + """ - return DeprecatedFunctionAlias(self.trac_number, self.func, self.__module__, instance = inst, unbound = self) + return self if (inst is None) else DeprecatedFunctionAlias(self.trac_number, self.func, self.__module__, instance = inst, unbound = self) def deprecated_function_alias(trac_number, func): From ecb16e648cdff2cb22bc88c7fd01f114af8006a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 3 Sep 2015 09:51:28 +0300 Subject: [PATCH 133/421] Added tests to is_chain_of_poset(). --- src/sage/combinat/posets/posets.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 9b7cef63f84..3d30d681191 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -113,7 +113,7 @@ :widths: 30, 70 :delim: | - :meth:`~FinitePoset.is_chain_of_poset` | Return ``True`` if given iterable is a chain of the poset. + :meth:`~FinitePoset.is_chain_of_poset` | Return ``True`` if given list is a chain of the poset. :meth:`~FinitePoset.chains` | Return the chains of the poset. :meth:`~FinitePoset.antichains` | Return the antichains of the poset. :meth:`~FinitePoset.maximal_chains` | Return the maximal chains of the poset. @@ -2398,8 +2398,8 @@ def is_chain_of_poset(self, elms, ordered=False): INPUT: - - ``elms`` -- an iterable (e. g., list, set, or tuple) - containing some elements of the poset + - ``elms`` -- a list or other iterable containing some elements + of the poset - ``ordered`` -- a Boolean. If ``True``, then return ``True`` only if elements in `elms` are strictly increasing in the poset. If @@ -2423,6 +2423,24 @@ def is_chain_of_poset(self, elms, ordered=False): False sage: P.is_chain_of_poset((1, 3), ordered=True) True + + TESTS:: + + sage: P = Posets.BooleanLattice(4) + sage: P.is_chain_of_poset([]) + True + sage: P.is_chain_of_poset((1,3,7,15,14)) + False + sage: P.is_chain_of_poset({10}) + True + sage: P.is_chain_of_poset({10}, ordered=True) + Traceback (most recent call last): + ... + TypeError: ordered=True not compatible with type for elms + sage: P.is_chain_of_poset([32]) + Traceback (most recent call last): + ... + ValueError: element (=32) not in poset """ if ordered: if not hasattr(elms, '__getitem__'): @@ -3344,8 +3362,9 @@ def width(self): r""" Return the width of the poset (the size of its longest antichain). - It is computed through a matching in a bipartite graph. See - :wikipedia:`Dilworth's_theorem` for more information. + It is computed through a matching in a bipartite graph; see + :wikipedia:`Dilworth's_theorem` for more information. The width is + also called Dilworth number. .. SEEALSO:: From 93f35e5dd1f134f072a84f8d08971207d64d8023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 3 Sep 2015 14:05:33 +0300 Subject: [PATCH 134/421] Removed a (failing) test of 'elms' being ordered. --- src/sage/combinat/posets/posets.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 3d30d681191..bb31700320e 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -113,7 +113,7 @@ :widths: 30, 70 :delim: | - :meth:`~FinitePoset.is_chain_of_poset` | Return ``True`` if given list is a chain of the poset. + :meth:`~FinitePoset.is_chain_of_poset` | Return ``True`` if the given list is a chain of the poset. :meth:`~FinitePoset.chains` | Return the chains of the poset. :meth:`~FinitePoset.antichains` | Return the antichains of the poset. :meth:`~FinitePoset.maximal_chains` | Return the maximal chains of the poset. @@ -2402,8 +2402,9 @@ def is_chain_of_poset(self, elms, ordered=False): of the poset - ``ordered`` -- a Boolean. If ``True``, then return ``True`` - only if elements in `elms` are strictly increasing in the poset. If - ``False`` (the default), then elements can be repeated and be in any + only if elements in `elms` are strictly increasing in the + poset; this makes no sense if `elms` is a set. If ``False`` + (the default), then elements can be repeated and be in any order. EXAMPLES:: @@ -2433,18 +2434,12 @@ def is_chain_of_poset(self, elms, ordered=False): False sage: P.is_chain_of_poset({10}) True - sage: P.is_chain_of_poset({10}, ordered=True) - Traceback (most recent call last): - ... - TypeError: ordered=True not compatible with type for elms sage: P.is_chain_of_poset([32]) Traceback (most recent call last): ... ValueError: element (=32) not in poset """ if ordered: - if not hasattr(elms, '__getitem__'): - raise TypeError("ordered=True not compatible with type %s for elms" % type(elms)) sorted_o = elms return all(self.lt(a, b) for a, b in zip(sorted_o, sorted_o[1:])) else: From 9791f5bf370666d65b8e5e9237460b49dc64d089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 11 Sep 2015 21:49:07 +0200 Subject: [PATCH 135/421] trac #18937 new patchbot 2.4.3 with enhanced trust control --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index e801e6e5156..b4af6341a82 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=9f7d1d3df8dbb8821d8d077f3ffb6ba94d464372 -md5=54155d5531d615928011387185f48b34 -cksum=3140942259 +sha1=15118651d1d2d32327d425528b4d4fc4b1cac6aa +md5=a01538fa40d938392b4bd6d5a7c60bb6 +cksum=1454859190 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 8e8299dcc06..35cee72dcbf 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.4.2 +2.4.3 From 914341e07afd70770cb8299989924bbf84e77552 Mon Sep 17 00:00:00 2001 From: Valentin Buciumas Date: Fri, 11 Sep 2015 16:24:14 -0700 Subject: [PATCH 136/421] last patch --- .../root_system/integrable_representations.py | 135 ++++++++++++++++-- 1 file changed, 123 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/root_system/integrable_representations.py b/src/sage/combinat/root_system/integrable_representations.py index ab8842f370f..38a0fcc6023 100644 --- a/src/sage/combinat/root_system/integrable_representations.py +++ b/src/sage/combinat/root_system/integrable_representations.py @@ -5,6 +5,7 @@ #***************************************************************************** # Copyright (C) 2014, 2105 Daniel Bump # Travis Scrimshaw +# Twisted Affine case: Valentin Buciumas # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ @@ -20,7 +21,7 @@ from sage.combinat.root_system.weyl_characters import WeylCharacterRing # TODO: Make this a proper parent and implement actions -class IntegrableRepresentation(UniqueRepresentation, CategoryObject): +class IntegrableRepresentation(CategoryObject, UniqueRepresentation): r""" An irreducible integrable highest weight representation of an affine Lie algebra. @@ -42,6 +43,9 @@ class IntegrableRepresentation(UniqueRepresentation, CategoryObject): .. [KacPeterson] Kac and Peterson. *Infinite-dimensional Lie algebras, theta functions and modular forms*. Adv. in Math. 53 (1984), no. 2, 125-264. + + .. [Carter] Carter, *Lie algebras of finite and affine type*. Cambridge + University Press, 2005 If `\Lambda` is a dominant integral weight for an affine root system, there exists a unique integrable representation `V=V_\Lambda` of highest @@ -147,6 +151,28 @@ class IntegrableRepresentation(UniqueRepresentation, CategoryObject): Lambda[0] + Lambda[2] - delta: 1 5 18 55 149 372 872 1941 4141 8523 17005 33019 2*Lambda[1] - delta: 1 4 15 44 122 304 721 1612 3469 7176 14414 28124 2*Lambda[2] - 2*delta: 2 7 26 72 194 467 1084 2367 5010 10191 20198 38907 + + Two examples for twisted affine types:: + + sage: Lambda = RootSystem(['G',2,1]).dual.weight_lattice(extended=true).fundamental_weights() + sage: V = IntegrableRepresentation(Lambda[0]+Lambda[1]+Lambda[2]) + sage: V.print_strings() # long time + 6*Lambdacheck[0]: 4 28 100 320 944 2460 6064 14300 31968 69020 144676 293916 + 4*Lambdacheck[0] + Lambdacheck[2]: 4 22 84 276 800 2124 5288 12470 28116 61056 128304 261972 + 3*Lambdacheck[0] + Lambdacheck[1]: 2 16 58 192 588 1568 3952 9520 21644 47456 100906 207536 + Lambdacheck[0] + Lambdacheck[1] + Lambdacheck[2]: 1 6 26 94 294 832 2184 5388 12634 28390 61488 128976 + 2*Lambdacheck[1] - deltacheck: 2 8 32 120 354 980 2576 6244 14498 32480 69776 145528 + 2*Lambdacheck[0] + 2*Lambdacheck[2]: 2 12 48 164 492 1344 3428 8256 18960 41844 89208 184512 + 3*Lambdacheck[2] - deltacheck: 4 16 60 208 592 1584 4032 9552 21728 47776 101068 207888 + sage: Lambda = RootSystem(['A',6,2]).weight_lattice(extended=true).fundamental_weights() + sage: V = IntegrableRepresentation(Lambda[0]+2*Lambda[1]) + sage: V.print_strings() # long time + 5*Lambda[0]: 3 42 378 2508 13707 64650 272211 1045470 3721815 12425064 39254163 118191378 + 3*Lambda[0] + Lambda[2]: 1 23 234 1690 9689 47313 204247 800029 2893198 9786257 31262198 95035357 + Lambda[0] + 2*Lambda[1]: 1 14 154 1160 6920 34756 153523 612354 2248318 7702198 24875351 76341630 + Lambda[0] + Lambda[1] + Lambda[3] - 2*delta: 6 87 751 4779 25060 113971 464842 1736620 6034717 19723537 61152367 181068152 + Lambda[0] + 2*Lambda[2] - 2*delta: 3 54 499 3349 18166 84836 353092 1341250 4725259 15625727 48938396 146190544 + Lambda[0] + 2*Lambda[3] - 4*delta: 15 195 1539 9186 45804 200073 789201 2866560 9723582 31120281 94724550 275919741 """ def __init__(self, Lam): """ @@ -160,16 +186,13 @@ def __init__(self, Lam): """ CategoryObject.__init__(self, base=ZZ, category=Modules(ZZ)) - if not Lam.parent().cartan_type().is_affine() or not Lam.parent()._extended: - raise ValueError("the parent of %s must be an extended affine root lattice"%Lam) self._Lam = Lam self._P = Lam.parent() self._Q = self._P.root_system.root_lattice() self._cartan_matrix = self._P.root_system.cartan_matrix() self._cartan_type = self._P.root_system.cartan_type() - if not self._cartan_type.is_untwisted_affine(): - raise NotImplementedError("integrable representations are only implemented for untwisted affine types") + self._classical_rank = self._cartan_type.classical().rank() self._index_set = self._P.index_set() self._index_set_classical = self._cartan_type.classical().index_set() @@ -594,7 +617,9 @@ def _freudenthal_roots_real(self, nu): r""" Return the set of real positive roots `\alpha \in \Delta^+` in ``self`` such that `\nu - \alpha \in Q^+`. - + + See [Kac] Proposition 6.3 for the way to compute the set of real roots for twisted affine case. + INPUT: - ``nu`` -- an element in `Q` @@ -616,9 +641,52 @@ def _freudenthal_roots_real(self, nu): for al in self._classical_positive_roots: if all(x >= 0 for x in self._from_weight_helper(nu-al)): ret.append(al) - for al in self._classical_roots: - for ir in self._freudenthal_roots_imaginary(nu-al): - ret.append(al+ir) + """ + changed the way you compute the set of real roots for twisted affine case, see [Kac] page 83 + """ + from sage.combinat.root_system.cartan_type import CartanType + if self._cartan_type == CartanType(['B',self._classical_rank,1]).dual() or self._cartan_type == CartanType(['C',self._classical_rank,1]).dual() or self._cartan_type == CartanType(['F',4,1]).dual(): #case A^2_{2l-1} or case D^2_{l+1} or case E^2_6: + for al in self._classical_roots: + if self._inner_qq(al,al) == 2: #if al is a short root: + for ir in self._freudenthal_roots_imaginary(nu-al): + ret.append(al+ir) + else: + for ir in self._freudenthal_roots_imaginary(nu-al): + if 2*ir in self._freudenthal_roots_imaginary(nu-al): + ret.append(al+2*ir) + + elif self._cartan_type == CartanType(['BC',self._classical_rank,2]): #case A^2_{2l} + for al in self._classical_roots: + if self._inner_qq(al,al) == 2: #if al is a short root: + for ir in self._freudenthal_roots_imaginary(nu-al): + ret.append(al+ir) + else: + for ir in self._freudenthal_roots_imaginary(nu-al): + if 2*ir in self._freudenthal_roots_imaginary(nu-al): + ret.append(al+2*ir) + for ir in self._freudenthal_roots_imaginary(2*nu-al)[::2]: + n = [x//2 for x in self._from_weight_helper(al+ir)] + alpha = self._Q.simple_roots() + if sum([val*alpha[i] for i,val in enumerate(n)]) not in ret: + ret.append(sum([val*alpha[i] for i,val in enumerate(n)])) + + elif self._cartan_type == CartanType(['D',4,3]) or self._cartan_type == CartanType(['G',2,1]).dual(): # case D^3_4 in the Kac/Stembridge notation + for al in self._classical_roots: + if self._inner_qq(al,al) == 2: #if al is a short root: + for ir in self._freudenthal_roots_imaginary(nu-al): + ret.append(al+ir) + else: + for ir in self._freudenthal_roots_imaginary(nu-al): + if 3*ir in self._freudenthal_roots_imaginary(nu-al): + ret.append(al+3*ir) + + + else: # untwisted case + for al in self._classical_roots: + for ir in self._freudenthal_roots_imaginary(nu-al): + ret.append(al+ir) + + return ret def _freudenthal_accum(self, nu, al): @@ -654,7 +722,11 @@ def _m_freudenthal(self, n): """ Compute the weight multiplicity using the Freudenthal multiplicity formula in ``self``. - + The multiplicities of the imaginary roots for the twisted + affine case are different than those for the untwisted case, + see [Carter]_ Corollary 18.10 for general type and Corollary + 18.15 for `A^2_{2l}` + EXAMPLES:: sage: Lambda = RootSystem(['B',3,1]).weight_lattice(extended=true).fundamental_weights() @@ -672,10 +744,49 @@ def _m_freudenthal(self, n): remove_zeros=False) den = 2*self._inner_pq(self._Lam+self._P.rho(), al) - self._inner_qq(al, al) num = 0 + for al in self._freudenthal_roots_real(self._Lam - mu): num += self._freudenthal_accum(mu, al) - for al in self._freudenthal_roots_imaginary(self._Lam - mu): - num += self._classical_rank * self._freudenthal_accum(mu, al) + + from sage.combinat.root_system.cartan_type import CartanType + + if self._cartan_type == CartanType(['B',self._classical_rank,1]).dual(): + list = self._freudenthal_roots_imaginary(self._Lam - mu) + dict = {key:list[key-1] for key in range(1,len(list)+1)} + for k in dict: # k is a positive number, dict[k] is k*delta + num += (self._classical_rank-k%2) * self._freudenthal_accum(mu, dict[k]) + + + elif self._cartan_type == CartanType(['BC',self._classical_rank,2]): + for al in self._freudenthal_roots_imaginary(self._Lam - mu): + num += self._classical_rank * self._freudenthal_accum(mu, al) + + elif self._cartan_type == CartanType(['C',self._classical_rank,1]).dual(): + list = self._freudenthal_roots_imaginary(self._Lam - mu) + dict = {key:list[key-1] for key in range(1,len(list)+1)} + for k in dict: # k is a positive number, dict[k] is k*delta + num += (self._classical_rank-(self._classical_rank -1)*(k%2)) * self._freudenthal_accum(mu, dict[k]) + + elif self._cartan_type == CartanType(['F',4,1]).dual(): + list = self._freudenthal_roots_imaginary(self._Lam - mu) + dict = {key:list[key-1] for key in range(1,len(list)+1)} + for k in dict: # k is a positive number, dict[k] is k*delta + num += (4 - 2*(k%2)) * self._freudenthal_accum(mu, dict[k]) + + elif self._cartan_type == CartanType(['D',4,3]) or self._cartan_type == CartanType(['G',2,1]).dual(): + list = self._freudenthal_roots_imaginary(self._Lam - mu) + dict = {key:list[key-1] for key in range(1,len(list)+1)} + for k in dict: # k is a positive number, dict[k] is k*delta + if k%3 == 0: + num += 2 * self._freudenthal_accum(mu, dict[k]) + else: + num += self._freudenthal_accum(mu, dict[k]) + + else: + for al in self._freudenthal_roots_imaginary(self._Lam - mu): + num += self._classical_rank * self._freudenthal_accum(mu, al) + + try: return ZZ(num / den) except TypeError: From 4ed09ecce4b1bd1028b6046cfa9cb0f74e690c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 12 Sep 2015 09:47:03 +0200 Subject: [PATCH 137/421] trac #18937 patchbot 2.4.4 bugfix for broken 2.4.3 --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index b4af6341a82..cea65720c5d 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=15118651d1d2d32327d425528b4d4fc4b1cac6aa -md5=a01538fa40d938392b4bd6d5a7c60bb6 -cksum=1454859190 +sha1=b9fd210e0fc8da23b9968777f6014d23970d1dc1 +md5=2ff4eb8cf4dac6ed24bb4c0962e782bc +cksum=672259990 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 35cee72dcbf..79a614418f7 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.4.3 +2.4.4 From 61aa576861e6a227bdfdfe4dfa15bb78b6d28729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 13 Sep 2015 17:55:14 +0200 Subject: [PATCH 138/421] trac #18937 version 2.4.5 --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index cea65720c5d..a22531aa99c 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=b9fd210e0fc8da23b9968777f6014d23970d1dc1 -md5=2ff4eb8cf4dac6ed24bb4c0962e782bc -cksum=672259990 +sha1=7c0c2bd9966c15733b97ee5a5e0870c22e44661c +md5=4923739c0bf31fcfbe13ad6c9b421290 +cksum=2215322151 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 79a614418f7..59aa62c1fa4 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.4.4 +2.4.5 From f71f7fac22538903327fa1ed78aa0c36055892f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 15 Sep 2015 15:18:20 +0200 Subject: [PATCH 139/421] version 2.4.6 --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index a22531aa99c..8071e26bf7e 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=7c0c2bd9966c15733b97ee5a5e0870c22e44661c -md5=4923739c0bf31fcfbe13ad6c9b421290 -cksum=2215322151 +sha1=5e23aabb8ed9851ef6d04fc3209a1a4f16e4de6c +md5=2717b61c5de5b76312370f1099c90ef8 +cksum=183944074 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 59aa62c1fa4..7bf4b6a8aef 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.4.5 +2.4.6 From 855f5078cd89e78bedc1edd18bc700f1c837ce07 Mon Sep 17 00:00:00 2001 From: Stefan Kraemer Date: Tue, 15 Sep 2015 16:33:07 +0200 Subject: [PATCH 140/421] Bugfix for hyperbolic_arc and hyperbolic_polygon --- src/sage/plot/hyperbolic_arc.py | 3 ++- src/sage/plot/hyperbolic_polygon.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/plot/hyperbolic_arc.py b/src/sage/plot/hyperbolic_arc.py index 1e658fc84d5..11ab9e85277 100644 --- a/src/sage/plot/hyperbolic_arc.py +++ b/src/sage/plot/hyperbolic_arc.py @@ -7,6 +7,7 @@ """ #***************************************************************************** # Copyright (C) 2011 Hartmut Monien , +# 2015 Stefan Kraemer # # Distributed under the terms of the GNU General Public License (GPL) # @@ -70,7 +71,7 @@ def _hyperbolic_arc(self, z0, z3, first=False): the hyperbolic arc between the complex numbers z0 and z3 in the hyperbolic plane. """ - if (z0-z3).real() == 0: + if abs((z0-z3).real()) < 10.**(-3): self.path.append([(z0.real(),z0.imag()), (z3.real(),z3.imag())]) return z0, z3 = (CC(z0), CC(z3)) diff --git a/src/sage/plot/hyperbolic_polygon.py b/src/sage/plot/hyperbolic_polygon.py index e6465183e04..7bc97f336ca 100644 --- a/src/sage/plot/hyperbolic_polygon.py +++ b/src/sage/plot/hyperbolic_polygon.py @@ -8,7 +8,8 @@ """ #***************************************************************************** # Copyright (C) 2011 Hartmut Monien , -# 2014 Vincent Delecroix <20100.delecroix@gmail.com> +# 2014 Vincent Delecroix <20100.delecroix@gmail.com>, +# 2015 Stefan Kraemer # # Distributed under the terms of the GNU General Public License (GPL) # @@ -85,7 +86,7 @@ def _hyperbolic_arc(self, z0, z3, first=False): the hyperbolic arc between the complex numbers z0 and z3 in the hyperbolic plane. """ - if (z0-z3).real() == 0: + if abs((z0-z3).real()) < 10.**(-3): self.path.append([(z0.real(), z0.imag()), (z3.real(), z3.imag())]) return z0, z3 = (CC(z0), CC(z3)) From b06cc7f2214b94c24a071e7fe06eb39772ce3a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 16 Sep 2015 11:25:38 +0200 Subject: [PATCH 141/421] trac #18937 changes to patchbot SPKG.txt --- build/pkgs/patchbot/SPKG.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/build/pkgs/patchbot/SPKG.txt b/build/pkgs/patchbot/SPKG.txt index 93c9d3da752..c34994b3818 100644 --- a/build/pkgs/patchbot/SPKG.txt +++ b/build/pkgs/patchbot/SPKG.txt @@ -4,7 +4,12 @@ Apply branches and run tests on open Sage tickets. -The patchbot is used to automate the testing of git branches. It has two different aspects: a server side and a client side. +The patchbot is used to automate the testing of git branches. It has two +different aspects: a server side and a client side. + +Instructions for using the client side can be found at + +http://wiki.sagemath.org/buildbot/details == License == @@ -18,4 +23,4 @@ https://github.com/robertwb/sage-patchbot/ == Dependencies == -python, sage-scripts +python, python-dateutil, sage-scripts From 92a2a6fb4bce16667412a5d206133d6128b25c00 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 16 Sep 2015 15:21:47 +0200 Subject: [PATCH 142/421] trac #19224: swtich OA(k,n)+* strongly regular graphs --- src/sage/graphs/strongly_regular_db.pyx | 69 ++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index f4bde1c2a36..5db46f95372 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -668,6 +668,72 @@ def is_taylor_twograph_srg(int v,int k,int l,int mu): return (TaylorTwographSRG, q) return +def is_switch_OA_srg(int v,int k,int l,int mu): + r""" + Test whether some *switch* `OA(k,n)+*` is `(v,k,\lambda,\mu)`-strongly regular. + + The "switch* `OA(k,n)+*` graphs appear on `Andries Brouwer's database + `__ and are built by + adding an isolated vertex to a + :meth:`~sage.graphs.graph_generators.GraphGenerators.OrthogonalArrayBlockGraph`, + and a :meth:`Seidel switching ` a set of disjoint + `n`-cocliques. + + INPUT: + + - ``v,k,l,mu`` (integers) + + OUTPUT: + + A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if the + parameters match, and ``None`` otherwise. + + EXAMPLES:: + + sage: from sage.graphs.strongly_regular_db import is_switch_OA_srg + sage: t = is_switch_OA_srg(170, 78, 35, 36); t + (.switch_OA_srg ...>, 6, 13) + sage: g = t[0](*t[1:]); g + Graph on 170 vertices + sage: g.is_strongly_regular(parameters=True) + (170, 78, 35, 36) + sage: t = is_switch_OA_srg(5,5,5,5); t + + TESTS:: + + sage: is_switch_OA_srg(290, 136, 63, 64) + (.switch_OA_srg at ..., 8, 17) + sage: is_switch_OA_srg(626, 300, 143, 144) + (.switch_OA_srg at ..., 12, 25) + sage: is_switch_OA_srg(842, 406, 195, 196) + (.switch_OA_srg at ..., 14, 29) + + """ + n_2_p_1 = v + if not is_square(n_2_p_1-1): + return None + + from sage.combinat.designs.orthogonal_arrays import orthogonal_array + from math import sqrt + cdef int n = int(sqrt(n_2_p_1-1)) + + cdef int c = k/n + if (k % n or + l != c*c-1 or + k != 1+(c-1)*(c+1)+(n-c)*(n-c-1) or + not orthogonal_array(c+1,n,existence=True)): + return None + + def switch_OA_srg(c,n): + from itertools import izip + OA = map(tuple,orthogonal_array(c+1,n,resolvable=True)) + g = Graph([OA,lambda x,y: any(xx==yy for xx,yy in izip(x,y))],loops=False) + g.add_vertex(0) + g.seidel_switching(OA[:c*n]) + return g + + return (switch_OA_srg,c,n) + cdef eigenvalues(int v,int k,int l,int mu): r""" Return the eigenvalues of a (v,k,l,mu)-strongly regular graph. @@ -1741,7 +1807,8 @@ def strongly_regular_graph(int v,int k,int l,int mu=-1,bint existence=False,bint is_unitary_dual_polar, is_RSHCD, is_twograph_descendant_of_srg, - is_taylor_twograph_srg] + is_taylor_twograph_srg, + is_switch_OA_srg] # Going through all test functions, for the set of parameters and its # complement. From 80786754e17a3a8866f48386b341374363b7c047 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 16 Sep 2015 15:23:39 -0700 Subject: [PATCH 143/421] coordinates enabled for PG designs --- src/sage/combinat/designs/block_design.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 9dee5f2ec5d..d516efce18e 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -165,7 +165,7 @@ def are_hyperplanes_in_projective_geometry_parameters(v, k, lmbda, return_parame return (True, (q,d)) if return_parameters else True -def ProjectiveGeometryDesign(n, d, F, algorithm=None, check=True): +def ProjectiveGeometryDesign(n, d, F, algorithm=None, coordinates=None, check=True): """ Return a projective geometry design. @@ -188,6 +188,10 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, check=True): GAP's "design" package must be available in this case, and that it can be installed with the ``gap_packages`` spkg. + - ``coordinates`` -- ``None`` by default. In this case and also if + ``algorithm="gap"``, the ground set is indexed by integers, + Otherwise it is indexed by coordinates in `F^{n+1}`. + EXAMPLES: The set of `d`-dimensional subspaces in a `n`-dimensional projective space @@ -203,6 +207,12 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, check=True): sage: PG.is_t_design(return_parameters=True) (True, (2, 85, 5, 1)) + Use coordinates:: + + sage: PG = designs.ProjectiveGeometryDesign(2,1,GF(3),coordinates=1) + sage: PG.blocks()[0] + [(1, 0, 0), (1, 0, 1), (1, 0, 2), (0, 0, 1)] + Check that the constructor using gap also works:: sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package) @@ -224,7 +234,10 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, check=True): m.set_immutable() b.append(points[m]) blocks.append(b) - return BlockDesign(len(points), blocks, name="ProjectiveGeometryDesign", check=check) + B = BlockDesign(len(points), blocks, name="ProjectiveGeometryDesign", check=check) + if not coordinates is None: + B.relabel({i:p[0] for p,i in points.iteritems()}) + return B if algorithm == "gap": # Requires GAP's Design from sage.interfaces.gap import gap gap.load_package("design") From 8d7562fa257f1a8e199352b812fd27718c563967 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 16 Sep 2015 23:45:06 -0700 Subject: [PATCH 144/421] GQ(q+1,q-1) and GQ(q-1,q+1) implemented and recognised in DB TODO: non-classical hyperovals, for completeness... --- .../graphs/generators/classical_geometries.py | 146 +++++++++++++++++- src/sage/graphs/graph_generators.py | 4 + src/sage/graphs/strongly_regular_db.pyx | 78 +++++++++- 3 files changed, 218 insertions(+), 10 deletions(-) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index cae8a1e9e30..d78aabdc0ed 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -21,6 +21,8 @@ from math import sin, cos, pi from sage.graphs.graph import Graph from sage.graphs import graph +from sage.rings.arith import is_prime_power +from sage.rings.finite_rings.constructor import FiniteField def SymplecticPolarGraph(d, q, algorithm=None): r""" @@ -87,7 +89,6 @@ def SymplecticPolarGraph(d, q, algorithm=None): G = _polar_graph(d, q, libgap.SymplecticGroup(d, q)) elif algorithm == None: # faster for small (q<4) fields - from sage.rings.finite_rings.constructor import FiniteField from sage.modules.free_module import VectorSpace from sage.schemes.projective.projective_space import ProjectiveSpace from sage.matrix.constructor import identity_matrix, block_matrix, zero_matrix @@ -187,7 +188,6 @@ def AffineOrthogonalPolarGraph(d,q,sign="+"): s = 0 from sage.interfaces.gap import gap - from sage.rings.finite_rings.constructor import FiniteField from sage.modules.free_module import VectorSpace from sage.matrix.constructor import Matrix from sage.libs.gap.libgap import libgap @@ -287,7 +287,6 @@ def _orthogonal_polar_graph(m, q, sign="+", point_type=[0]): """ from sage.schemes.projective.projective_space import ProjectiveSpace - from sage.rings.finite_rings.constructor import FiniteField from sage.modules.free_module_element import free_module_element as vector from sage.matrix.constructor import Matrix from sage.libs.gap.libgap import libgap @@ -491,7 +490,6 @@ def NonisotropicOrthogonalPolarGraph(m, q, sign="+", perp=None): """ from sage.graphs.generators.classical_geometries import _orthogonal_polar_graph - from sage.rings.arith import is_prime_power p, k = is_prime_power(q,get_data=True) if k==0: raise ValueError('q must be a prime power') @@ -635,7 +633,6 @@ def UnitaryPolarGraph(m, q, algorithm="gap"): elif algorithm == None: # slow on large examples from sage.schemes.projective.projective_space import ProjectiveSpace - from sage.rings.finite_rings.constructor import FiniteField from sage.modules.free_module_element import free_module_element as vector from __builtin__ import sum as psum Fq = FiniteField(q**2, 'a') @@ -697,7 +694,6 @@ def NonisotropicUnitaryPolarGraph(m, q): Disc. Math. 13(1975), pp 357--381. http://dx.doi.org/10.1016/0012-365X(75)90057-6 """ - from sage.rings.arith import is_prime_power p, k = is_prime_power(q,get_data=True) if k==0: raise ValueError('q must be a prime power') @@ -881,12 +877,10 @@ def TaylorTwographDescendantSRG(q, clique_partition=None): ... ValueError: q must be an odd prime power """ - from sage.rings.arith import is_prime_power p, k = is_prime_power(q,get_data=True) if k==0 or p==2: raise ValueError('q must be an odd prime power') from sage.schemes.projective.projective_space import ProjectiveSpace - from sage.rings.finite_rings.constructor import FiniteField from sage.modules.free_module_element import free_module_element as vector from sage.rings.finite_rings.integer_mod import mod from __builtin__ import sum @@ -941,3 +935,139 @@ def TaylorTwographSRG(q): G.seidel_switching(sum(l[:(q**2+1)/2],[])) G.name("Taylor two-graph SRG") return G + +def AhrensSzekeresGQ(q, dual=False): + r""" + Return the collinearity graph of GQ AS(q) or its dual + + INPUT: + + - ``q`` -- a power of an odd prime number + + - ``dual`` -- if ``False`` (default), return the graph of `GQ(q-1,q+1)`. + Otherwise return the graph of `GQ(q+1,q-1)`. + + `AS(q)` is a generalised quadrangle (GQ) of order `(q-1,q+1)`, see 3.1.5 in [PT09]_. + Let `q` be an odd prime power. Then the points are elements of `F_q^3`, + and lines are of the form + + * `(\sigma, a, b), \sigma\in F_q` + * `(a, \sigma, b), \sigma\in F_q` + * `(c \sigma^2 - b \sigma + a, -2 c \sigma + b, \sigma), \sigma\in F_q` + + where `a`, `b`, `c` are arbitrary elements of `F_q`. + + EXAMPLES:: + + sage: g=graphs.AhrensSzekeresGQ(5); g + AS(5); GQ(4, 6): Graph on 125 vertices + sage: g.is_strongly_regular(parameters=True) + (125, 28, 3, 7) + sage: g=graphs.AhrensSzekeresGQ(5,dual=True); g + AS(5)*; GQ(6, 4): Graph on 175 vertices + sage: g.is_strongly_regular(parameters=True) + (175, 30, 5, 5) + + REFERENCE: + + .. [PT09] S. Payne, J. A. Thas. + Finite generalized quadrangles. + European Mathematical Society, + 2nd edition, 2009. + """ + from sage.combinat.designs.incidence_structures import IncidenceStructure + p, k = is_prime_power(q,get_data=True) + if k==0 or p==2: + raise ValueError('q must be an odd prime power') + F = FiniteField(q, 'a') + L = [] + for a in F: + for b in F: + L.append(tuple(map(lambda s: (s, a, b), F))) + L.append(tuple(map(lambda s: (a, s, b), F))) + for c in F: + L.append(tuple(map(lambda s: (c*s**2 - b*s + a, -2*c*s + b, s), F))) + if dual: + G = IncidenceStructure(L).intersection_graph() + G.name('AS('+str(q)+')*; GQ'+str((q+1,q-1))) + else: + G = IncidenceStructure(L).dual().intersection_graph() + G.name('AS('+str(q)+'); GQ'+str((q-1,q+1))) + return G + +def T2starGQ(q, dual=False, hyperoval=None, field=None, checkhyperoval=False): + r""" + Return the collinearity graph of GQ T_2*(q) or its dual + + INPUT: + + - ``q`` -- a power of two + + - ``dual`` -- if ``False`` (default), return the graph of `GQ(q-1,q+1)`. + Otherwise return the graph of `GQ(q+1,q-1)`. + + - ``hyperoval`` -- a hyperoval (i.e. a complete 2-arc; a set of points in the plane + meeting every line in 0 or 2 points) in the plane of points with 0th coordinate + 0 in `PG(3,q)` over the field ``field``. By default, ``hyperoval`` and + ``field`` are not specified, and constructed on the fly. + + - ``field`` -- an instance of a finite field of order `q`, must be provided + if ``hyperoval`` is provided. + + - ``checkhyperoval`` -- (default: ``False``) if ``True``, + check ``hyperoval`` for correctness. + + `T_2^*(q)` is a generalised quadrangle (GQ) of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. + Let `q=2^k` and `\Theta=PG(3,q)`. Fix a plane `\Pi \subset \Theta` and + a hyperoval `O \subset \Pi`. The points of the GQ are the points of `\Theta` + outside `\Pi`, and the lines are the lines of `\Theta` outside `\Pi` + that meet `\Pi` in a point of `O`. + + + EXAMPLES:: + + sage: g=graphs.T2starGQ(4); g + T2*(O,4); GQ(3, 5): Graph on 64 vertices + sage: g.is_strongly_regular(parameters=True) + (64, 18, 2, 6) + sage: g=graphs.T2starGQ(4,dual=True); g + T2*(O,4)*; GQ(5, 3): Graph on 96 vertices + sage: g.is_strongly_regular(parameters=True) + (96, 20, 4, 4) + """ + from sage.combinat.designs.incidence_structures import IncidenceStructure + from sage.combinat.designs.block_design import ProjectiveGeometryDesign as PG + from sage.modules.free_module_element import free_module_element as vector + + p, k = is_prime_power(q,get_data=True) + if k==0 or p!=2: + raise ValueError('q must be a power of 2') + if field is None: + F = FiniteField(q, 'a') + else: + F = field + + Theta = PG(3,1,F,coordinates=1) + Pi = set(filter(lambda x: x[0]==F.zero(), Theta.ground_set())) + if hyperoval is None: + O = filter(lambda x: x[1]+x[2]*x[3]==0 or (x[1]==1 and x[2]==0 and x[3]==0), Pi) + O = set(O) + else: + O = set(hyperoval) + + if checkhyperoval: + if len(O) != q+2: + raise RunTimeError("incorrect hyperoval size") + for L in Theta.blocks(): + if Pi.issubset(L): + if not len(O.intersection(L)) in [0,2]: + raise RunTimeError("incorrect hyperoval") + L = map(lambda z: filter(lambda y: not y in O, z), + filter(lambda x: len(O.intersection(x)) == 1, Theta.blocks())) + if dual: + G = IncidenceStructure(L).intersection_graph() + G.name('T2*(O,'+str(q)+')*; GQ'+str((q+1,q-1))) + else: + G = IncidenceStructure(L).dual().intersection_graph() + G.name('T2*(O,'+str(q)+'); GQ'+str((q-1,q+1))) + return G diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 74f0cb3bbd3..a192f992fdb 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -234,6 +234,7 @@ def __append_to_doc(methods): __append_to_doc( ["AffineOrthogonalPolarGraph", + "AhrensSzekeresGQ", "NonisotropicOrthogonalPolarGraph", "NonisotropicUnitaryPolarGraph", "OrthogonalPolarGraph", @@ -241,6 +242,7 @@ def __append_to_doc(methods): "SymplecticPolarGraph", "TaylorTwographDescendantSRG", "TaylorTwographSRG", + "T2starGQ", "UnitaryDualPolarGraph", "UnitaryPolarGraph"]) @@ -1995,6 +1997,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None ########################################################################### import sage.graphs.generators.classical_geometries AffineOrthogonalPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.AffineOrthogonalPolarGraph) + AhrensSzekeresGQ = staticmethod(sage.graphs.generators.classical_geometries.AhrensSzekeresGQ) NonisotropicOrthogonalPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.NonisotropicOrthogonalPolarGraph) NonisotropicUnitaryPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.NonisotropicUnitaryPolarGraph) OrthogonalPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.OrthogonalPolarGraph) @@ -2004,6 +2007,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None TaylorTwographDescendantSRG = \ staticmethod(sage.graphs.generators.classical_geometries.TaylorTwographDescendantSRG) TaylorTwographSRG = staticmethod(sage.graphs.generators.classical_geometries.TaylorTwographSRG) + T2starGQ = staticmethod(sage.graphs.generators.classical_geometries.T2starGQ) UnitaryDualPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.UnitaryDualPolarGraph) UnitaryPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.UnitaryPolarGraph) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 6aa69bdfc8d..7ff9dc2b13d 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -857,6 +857,81 @@ def is_unitary_dual_polar(int v,int k,int l,int mu): from sage.graphs.generators.classical_geometries import UnitaryDualPolarGraph return (UnitaryDualPolarGraph, 5, p**t) +@cached_function +def is_GQqmqp(int v,int k,int l,int mu): + r""" + Test whether some `GQ(q-1,q+1)` or `GQ(q+1,q-1)`-graph is `(v,k,\lambda,\mu)`-srg. + + INPUT: + + - ``v,k,l,mu`` (integers) + + OUTPUT: + + A tuple ``t`` such that ``t[0](*t[1:])`` builds the requested graph if one + exists, and ``None`` otherwise. + + EXAMPLES:: + + sage: from sage.graphs.strongly_regular_db import is_GQqmqp + sage: t = is_GQqmqp(27,10,1,5); t + (, 3, False) + sage: g = t[0](*t[1:]); g + AS(3); GQ(2, 4): Graph on 27 vertices + sage: t = is_GQqmqp(45,12,3,3); t + (, 3, True) + sage: g = t[0](*t[1:]); g + AS(3)*; GQ(4, 2): Graph on 45 vertices + sage: g.is_strongly_regular(parameters=True) + (45, 12, 3, 3) + sage: t = is_GQqmqp(16,6,2,2); t + (, 2, True) + sage: g = t[0](*t[1:]); g + T2*(O,2)*; GQ(3, 1): Graph on 16 vertices + sage: g.is_strongly_regular(parameters=True) + (16, 6, 2, 2) + sage: t = is_GQqmqp(64,18,2,6); t + (, 4, False) + sage: g = t[0](*t[1:]); g + T2*(O,4); GQ(3, 5): Graph on 64 vertices + sage: g.is_strongly_regular(parameters=True) + (64, 18, 2, 6) + + TESTS:: + + sage: (S,T)=(127,129) + sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t + (, 128, False) + sage: (S,T)=(129,127) + sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t + (, 128, True) + sage: (S,T)=(124,126) + sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t + (, 125, False) + sage: (S,T)=(126,124) + sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t + (, 125, True) + sage: t = is_GQqmqp(5,5,5,5); t + """ + # do we have GQ(s,t)? we must have mu=t+1, s=l+1, + # v=(s+1)(st+1), k=s(t+1) + S=l+1 + T=mu-1 + if (v == (S+1)*(S*T+1) and + k == S*(T+1) and + (S+T) % 2 == 0): + q = (S+T)/2 + p, k = is_prime_power(q, get_data=True) + if p % 2 == 0: + from sage.graphs.generators.classical_geometries import T2starGQ as F + else: + from sage.graphs.generators.classical_geometries import AhrensSzekeresGQ as F + if k != 0: + if (S,T) == (q-1, q+1): + return (F, q, False) + elif (S,T) == (q+1, q-1): + return (F, q, True) + @cached_function def is_twograph_descendant_of_srg(int v, int k0, int l, int mu): r""" @@ -2058,8 +2133,7 @@ def strongly_regular_graph(int v,int k,int l,int mu=-1,bint existence=False,bint is_steiner, is_affine_polar, is_orthogonal_polar, is_NOodd, is_NOperp_F5, is_NO_F2, is_NO_F3, is_NU, - is_unitary_polar, - is_unitary_dual_polar, + is_unitary_polar, is_unitary_dual_polar, is_GQqmqp, is_RSHCD, is_twograph_descendant_of_srg, is_taylor_twograph_srg] From 1de29de8ab7afe7805a4a3d8bb62c77dc1e3b76f Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 16 Sep 2015 23:48:57 -0700 Subject: [PATCH 145/421] removed explicit graphs that we can now build (thanks to this ticket and NONU) --- src/sage/graphs/strongly_regular_db.pyx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 7ff9dc2b13d..245ddf0d5f0 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -2073,20 +2073,8 @@ def strongly_regular_graph(int v,int k,int l,int mu=-1,bint existence=False,bint ( 27, 16, 10, 8): [SchlaefliGraph], ( 36, 14, 4, 6): [Graph,('c~rLDEOcKTPO`U`HOIj@MWFLQFAaRIT`HIWqPsQQJ'+ 'DXGLqYM@gRLAWLdkEW@RQYQIErcgesClhKefC_ygSGkZ`OyHETdK[?lWStCapVgKK')], - ( 40, 12, 2, 4): [Graph,('g}iS[A@_S@OA_BWQIGaPCQE@CcQGcQECXAgaOdS@a'+ - 'CWEEAOIBH_HW?scb?f@GMBGGhIPGaQoh?q_bD_pGPq_WI`T_DBU?R_dECsSARGgogBO'+ - '{_IPBKZ?DI@Wgt_E?MPo{_?')], - ( 45, 12, 3, 3): [Graph,('l~}CKMF_C?oB_FPCGaICQOaH@DQAHQ@Ch?aJHAQ@G'+ - 'P_CQAIGcAJGO`IcGOY`@IGaGHGaKSCDI?gGDgGcE_@OQAg@PCSO_hOa`GIDADAD@XCI'+ - 'ASDKB?oKOo@_SHCc?SGcGd@A`B?bOOHGQH?ROQOW`?XOPa@C_hcGo`CGJK')], ( 50, 7, 0, 1): [HoffmanSingletonGraph], ( 56, 10, 0, 2): [SimsGewirtzGraph], - ( 64, 18, 2, 6): [Graph,('~?@?~aK[A@_[?O@_B_?O?K?B_?A??K??YQQPHGcQQ'+ - 'CaPIOHAX?POhAPIC`GcgSAHDE?PCiC@BCcDADIG_QCocS@AST?OOceGG@QGcKcdCbCB'+ - 'gIEHAScIDDOy?DAWaEg@IQO?maHPOhAW_dBCX?s@HOpKD@@GpOpHO?bCbHGOaGgpWQQ'+ - '?PDDDw@A_CSRIS_P?GeGpg`@?EOcaJGccbDC_dLAc_pHOe@`ocEGgo@sRo?WRAbAcPc'+ - '?iCiHEKBO_hOiOWpOSGSTBQCUAW_DDIWOqHBO?gghw_?`kOAXH?\\Ds@@@CpIDKOpc@'+ - 'OCoeIS_YOgGATGaqAhKGA?cqDOwQKGc?')], ( 77, 16, 0, 4): [M22Graph], ( 81, 50, 31, 30): [SRG_81_50_31_30], (100, 22, 0, 6): [HigmanSimsGraph], From 3318570ec262afdaaf26041c3dfc34e89840f927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 17 Sep 2015 10:39:47 +0200 Subject: [PATCH 146/421] trac #18937 patchbot 2.4.7, no more needing dateutil, better log --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index 8071e26bf7e..7cebb66b8e2 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=5e23aabb8ed9851ef6d04fc3209a1a4f16e4de6c -md5=2717b61c5de5b76312370f1099c90ef8 -cksum=183944074 +sha1=be2fcb7039c02f37669f5ea078fb2868730df67c +md5=3ebebd93ce6194dee6263270a6419970 +cksum=2163763617 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 7bf4b6a8aef..e30309f735e 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.4.6 +2.4.7 From 4076a47f7b7ec9c4a6d7e161429e25b0bc5231a1 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 17 Sep 2015 17:10:05 -0700 Subject: [PATCH 147/421] added examples and tests for user-supplied hyperoval in T_2*(O), and small bug fixed --- .../graphs/generators/classical_geometries.py | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index d78aabdc0ed..ad34b7be04f 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -1008,7 +1008,8 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, checkhyperoval=False): - ``hyperoval`` -- a hyperoval (i.e. a complete 2-arc; a set of points in the plane meeting every line in 0 or 2 points) in the plane of points with 0th coordinate - 0 in `PG(3,q)` over the field ``field``. By default, ``hyperoval`` and + 0 in `PG(3,q)` over the field ``field``. Each point of ``hyperoval`` must be a length 4 + vector over ``field`` with 1st non-0 coordinate equal to 1. By default, ``hyperoval`` and ``field`` are not specified, and constructed on the fly. - ``field`` -- an instance of a finite field of order `q`, must be provided @@ -1018,13 +1019,16 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, checkhyperoval=False): check ``hyperoval`` for correctness. `T_2^*(q)` is a generalised quadrangle (GQ) of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. - Let `q=2^k` and `\Theta=PG(3,q)`. Fix a plane `\Pi \subset \Theta` and - a hyperoval `O \subset \Pi`. The points of the GQ are the points of `\Theta` + Let `q=2^k` and `\Theta=PG(3,q)`. Fix a plane `\Pi \subset \Theta` and a + `hyperoval `__ + `O \subset \Pi`. The points of the GQ are the points of `\Theta` outside `\Pi`, and the lines are the lines of `\Theta` outside `\Pi` that meet `\Pi` in a point of `O`. - EXAMPLES:: + EXAMPLES: + + using the built-in construction:: sage: g=graphs.T2starGQ(4); g T2*(O,4); GQ(3, 5): Graph on 64 vertices @@ -1034,6 +1038,29 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, checkhyperoval=False): T2*(O,4)*; GQ(5, 3): Graph on 96 vertices sage: g.is_strongly_regular(parameters=True) (96, 20, 4, 4) + + supplying your own hyperoval:: + + sage: F=GF(4,'b') + sage: O=[vector(F,(0,0,0,1)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) + sage: g=graphs.T2starGQ(4, hyperoval=O, field=F); g + T2*(O,4); GQ(3, 5): Graph on 64 vertices + sage: g.is_strongly_regular(parameters=True) + (64, 18, 2, 6) + + TESTS:: + + sage: F=GF(4,'b') # repeating a point... + sage: O=[vector(F,(0,1,0,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) + sage: graphs.T2starGQ(4, hyperoval=O, field=F, checkhyperoval=True) + Traceback (most recent call last): + ... + RuntimeError: incorrect hyperoval size + sage: O=[vector(F,(0,1,1,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) + sage: graphs.T2starGQ(4, hyperoval=O, field=F, checkhyperoval=True) + Traceback (most recent call last): + ... + RuntimeError: incorrect hyperoval """ from sage.combinat.designs.incidence_structures import IncidenceStructure from sage.combinat.designs.block_design import ProjectiveGeometryDesign as PG @@ -1053,15 +1080,16 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, checkhyperoval=False): O = filter(lambda x: x[1]+x[2]*x[3]==0 or (x[1]==1 and x[2]==0 and x[3]==0), Pi) O = set(O) else: + map(lambda x: x.set_immutable(), hyperoval) O = set(hyperoval) if checkhyperoval: if len(O) != q+2: - raise RunTimeError("incorrect hyperoval size") + raise RuntimeError("incorrect hyperoval size") for L in Theta.blocks(): - if Pi.issubset(L): + if set(L).issubset(Pi): if not len(O.intersection(L)) in [0,2]: - raise RunTimeError("incorrect hyperoval") + raise RuntimeError("incorrect hyperoval") L = map(lambda z: filter(lambda y: not y in O, z), filter(lambda x: len(O.intersection(x)) == 1, Theta.blocks())) if dual: From 2115bf45aadf6dcf104031cdcbd86a0787fb9def Mon Sep 17 00:00:00 2001 From: David Lucas Date: Fri, 18 Sep 2015 14:57:39 +0200 Subject: [PATCH 148/421] Changes accordingly to reviewer's comments --- src/sage/coding/codes_catalog.py | 8 +- src/sage/coding/encoder.py | 101 ++++++++++++++--------- src/sage/coding/encoders_catalog.py | 5 +- src/sage/coding/linear_code.py | 122 +++++++++++++++++----------- 4 files changed, 144 insertions(+), 92 deletions(-) diff --git a/src/sage/coding/codes_catalog.py b/src/sage/coding/codes_catalog.py index 99f42b179a1..9af7b3a908c 100644 --- a/src/sage/coding/codes_catalog.py +++ b/src/sage/coding/codes_catalog.py @@ -22,7 +22,7 @@ CyclicCode, CyclicCodeFromCheckPolynomial, DuadicCodeEvenPair, DuadicCodeOddPair, ExtendedBinaryGolayCode, ExtendedQuadraticResidueCode, ExtendedTernaryGolayCode, - HammingCode, LinearCodeFromCheckMatrix, + HammingCode, LinearCode, LinearCodeFromCheckMatrix, QuadraticResidueCode, QuadraticResidueCodeEvenPair, QuadraticResidueCodeOddPair, RandomLinearCode, ReedSolomonCode, TernaryGolayCode, @@ -31,6 +31,6 @@ from guava import BinaryReedMullerCode, QuasiQuadraticResidueCode, RandomLinearCodeGuava import encoders_catalog as encoders -from sage.misc.rest_index_of_methods import gen_rest_table_index -import sys -__doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__], only_local_functions=False)) +from sage.misc.rest_index_of_methods import gen_rest_table_index as _gen_rest_table_index +import sys as _sys +__doc__ = __doc__.format(INDEX_OF_FUNCTIONS=_gen_rest_table_index(_sys.modules[__name__], only_local_functions=False)) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index fd840ea3534..8c937f217f3 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -27,29 +27,27 @@ class Encoder(SageObject): To implement an encoder, you need to: - - inherit from :class:`Encoder` + - inherit from :class:`Encoder`, - call ``Encoder.__init__`` in the subclass constructor. Example: ``super(SubclassName, self).__init__(code)``. By doing that, your subclass will have its ``code`` parameter initialized. - You need of course to complete the constructor by adding any additional parameter - needed to describe properly the code defined in the subclass. - Then, if the message space is a vector space, default implementations of :meth:`encode` and - :meth:`unencode_nocheck` methods are provided. These implementations rely on :meth:`generator_matrix` - which you need to override to use the default implementations. + - Then, if the message space is a vector space, default implementations of :meth:`encode` and + :meth:`unencode_nocheck` methods are provided. These implementations rely on :meth:`generator_matrix` + which you need to override to use the default implementations. - If the message space is not of the form `F^k`, where `F` is a finite field, - you cannot have a generator matrix. - In that case, you need to override :meth:`encode` and :meth:`unencode_nocheck`. + - If the message space is not of the form `F^k`, where `F` is a finite field, + you cannot have a generator matrix. + In that case, you need to override :meth:`encode`, :meth:`unencode_nocheck` and + :meth:`message_space`. - Equality methods (``__eq__`` and ``__ne__``) might be useful for encoding in advanced - codes constructions (like concatenated codes). If provided default implementation of - these methods is not enough for your subclass, you are strongly encouraged to override - them. + - By default, comparison of :class:`Encoder` (using methods ``__eq__`` and ``__ne__`` ) are + by memory reference: if you build the same encoder twice, they will be different. If you + need something more clever, override ``__eq__`` and ``__ne__`` in your subclass. - As :class:`Encoder` is not designed to be instanciated, it does not have any representation - methods. You should implement ``_repr_`` and ``_latex_`` methods in the sublclass. + - As :class:`Encoder` is not designed to be instanciated, it does not have any representation + methods. You should implement ``_repr_`` and ``_latex_`` methods in the sublclass. REFERENCES: @@ -94,18 +92,18 @@ def encode(self, word): Transforms an element of the message space into a codeword. This is a default implementation which assumes that the message - space of the encoder is `F^k`, where `F` is + space of the encoder is `F^{k}`, where `F` is :meth:`sage.coding.linear_code.AbstractLinearCode.base_field` - and ``k`` is :meth:`sage.coding.linear_code.AbstractLinearCode.dimension`. + and `k` is :meth:`sage.coding.linear_code.AbstractLinearCode.dimension`. If this is not the case, this method should be overwritten by the subclass. INPUT: - - ``word`` -- a vector of the message space of the code + - ``word`` -- a vector of the message space of the ``self``. OUTPUT: - - a vector of ``self`` + - a vector of :meth:`code`. EXAMPLES:: @@ -122,45 +120,60 @@ def encode(self, word): sage: E.encode(word) Traceback (most recent call last): ... - ValueError: Vector to encode must be in a Vector space of dimension 4 over Finite Field of size 2 + ValueError: The value to encode must be in Vector space of dimension 4 over Finite Field of size 2 """ M = self.message_space() if word not in M: - raise ValueError("Vector to encode must be in a %s" % M) + raise ValueError("The value to encode must be in %s" % M) return vector(word) * self.generator_matrix() def unencode(self, c, nocheck=False): r""" - Returns the message corresponding to ``c``. + Returns the message corresponding to the codeword ``c``. + + This is the inverse of :meth:`encode`. INPUT: - - ``c`` -- a vector of the same length as ``self`` over the - base field of ``self`` + - ``c`` -- a vector of the same length as :meth:`code` over the + base field of :meth:`code`. - - ``nocheck`` -- (default: ``False``) checks if ``c`` is in ``self``. If this is set - to ``True``, the return value of this method is not guaranteed to be correct. + - ``nocheck`` -- (default: ``False``) checks if ``c`` is in ``self``. You might set + this to ``True`` to disable the check for saving computation. Note that if ``c`` is + not in ``self`` and ``nocheck = True``, then the output of :meth:`unencode` is + not defined (except that it will be in the message space of ``self``). OUTPUT: - - a vector of the message space of ``self`` + - an element of the message space of ``self`` EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) + sage: c in C + True sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.unencode(c) (0, 1, 1, 0) + + TESTS: + + If ``nocheck`` is set to ``False``, and one provides a word which is not in + :meth:`code`, :meth:`unencode` will return an error:: + + sage: c = vector(GF(2), (0, 1, 0, 0, 1, 1, 0)) + sage: c in C + False + sage: E.unencode(c, False) + Traceback (most recent call last): + ... + EncodingError: Given word is not in the code """ - if nocheck == False: - if c not in self.code(): - raise EncodingError("Given word is not in the code") - else: - return self.unencode_nocheck(c) - else: - return self.unencode_nocheck(c) + if nocheck == False and c not in self.code(): + raise EncodingError("Given word is not in the code") + return self.unencode_nocheck(c) @cached_method def _unencoder_matrix(self): @@ -216,7 +229,8 @@ def unencode_nocheck(self, c): sage: E.unencode_nocheck(c) (0, 1, 1, 0) - We take a vector that does not belong to C:: + Taking a vector that does not belong to ``C`` will not raise an error but + probably just give a non-sensical result:: sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 1)) sage: c in C @@ -236,15 +250,15 @@ def unencode_nocheck(self, c): def code(self): r""" - Returns the code in which :meth:`encode` has its output. + Returns the code for this :class:`Encoder`. EXAMPLES:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: E = C.encoder() - sage: E.code() - Linear code of length 7, dimension 4 over Finite Field of size 2 + sage: E.code() == C + True """ return self._code @@ -273,6 +287,17 @@ def generator_matrix(self): Reimplementing this for each subclass of :class:`Encoder` is not mandatory (as a generator matrix only makes sense when the message space is of the `F^k`, where `F` is the base field of :meth:`code`.) + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E = C.encoder() + sage: E.generator_matrix() + [1 1 1 0 0 0 0] + [1 0 0 1 1 0 0] + [0 1 0 1 0 1 0] + [1 1 0 1 0 0 1] """ class EncodingError(Exception): diff --git a/src/sage/coding/encoders_catalog.py b/src/sage/coding/encoders_catalog.py index 23c0ecb7998..ec48db90837 100644 --- a/src/sage/coding/encoders_catalog.py +++ b/src/sage/coding/encoders_catalog.py @@ -3,7 +3,7 @@ The ``codes.encoders`` object may be used to access the encoders that Sage can build. -- :func:`linear_code.LinearCodeGeneratorMatrixEncoder ` +:class:`linear_code.LinearCodeGeneratorMatrixEncoder ` .. NOTE:: @@ -12,4 +12,5 @@ sage: from sage.coding.encoders_catalog import * """ -from linear_code import (LinearCodeGeneratorMatrixEncoder) +from sage.misc.lazy_import import lazy_import +lazy_import('sage.coding.linear_code', 'LinearCodeGeneratorMatrixEncoder') diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 6410e86c428..b0a57d24c70 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -711,17 +711,6 @@ class AbstractLinearCode(module.Module): So, every Linear Code-related class should inherit from this abstract class. - This class provides: - - - ``length``, the length of the code - - - ``default_encoder_name``, the name of the encoder that will be used if no encoder name is passed - to an encoder-related method (``generator_matrix``, ``encode``, ``unencode``) - - - ``_registered_encoders``, a dictionary of all encoders available for this class - - - numerous methods that will work for any linear code (including families) - To implement a linear code, you need to: - inherit from AbstractLinearCode @@ -733,7 +722,7 @@ class AbstractLinearCode(module.Module): You need of course to complete the constructor by adding any additional parameter needed to describe properly the code defined in the subclass. - - fill the dictionary of its encoders in ``sage.coding.__init__`` file. Example: + - fill the dictionary of its encoders in ``sage.coding.__init__.py`` file. Example: I want to link the encoder ``MyEncoderClass`` to ``MyNewCodeClass`` under the name ``MyEncoderName``. All I need to do is to write this line in the ``__init__.py`` file: @@ -745,11 +734,14 @@ class AbstractLinearCode(module.Module): .. NOTE:: - AbstractLinearCode embeds some generic implementations of helper methods like ``__cmp__`` - or ``__eq__``. - As they are designed to fit for every linear code, they mostly use the generator matrix - and thus can be long for certain families of code. - In that case, overriding these methods is encouraged. + :class:`AbstractLinearCode` has generic implementations of the comparison methods ``__cmp`` + and ``__eq__`` which use the generator matrix and are quite slow. In subclasses you are + encouraged to override these functions. + + .. WARNING:: + + The default encoder should always have `F^{k}` as message space, with `k` the dimension + of the code and `F` its base ring. A lot of methods of the abstract class rely on the knowledge of a generator matrix. It is thus strongly recommended to set an encoder with a generator matrix implemented @@ -841,14 +833,13 @@ def __init__(self, base_field, length, default_encoder_name): sage: C = CodeExample(GF(17), 10, 5, generator_matrix) Traceback (most recent call last): ... - ValueError: You must set a valid encoder as default encoder for this code + ValueError: You must set a valid encoder as default encoder for this code, by completing __init__.py """ - self._registered_encoders = copy(self._registered_encoders) if not isinstance(length, (int, Integer)): raise ValueError("length must be a Python int or a Sage Integer") self._length = Integer(length) if not default_encoder_name in self._registered_encoders: - raise ValueError("You must set a valid encoder as default encoder for this code") + raise ValueError("You must set a valid encoder as default encoder for this code, by completing __init__.py") self._default_encoder_name = default_encoder_name cat = Modules(base_field).FiniteDimensional().WithBasis().Finite() facade_for = VectorSpace(base_field, self._length) @@ -891,6 +882,12 @@ def add_encoder(self, name, encoder): r""" Adds an encoder to the list of registered encoders of ``self``. + .. NOTE:: + + This method only adds ``encoder`` to ``self``, and not to any member of the class + of ``self``. To know how to add an :class:`sage.coding.encoder.Encoder`, please refer + to the documentation of :class:`AbstractLinearCode`. + INPUT: - ``name`` -- the string name for the encoder @@ -932,10 +929,16 @@ def add_encoder(self, name, encoder): ... ValueError: There is already a registered encoder with this name """ - reg_enc = self._registered_encoders - if name in reg_enc: - raise ValueError("There is already a registered encoder with this name") - reg_enc[name] = encoder + if self._registered_encoders == self.__class__._registered_encoders: + self._registered_encoders = copy(self._registered_encoders) + reg_enc = self._registered_encoders + if name in reg_enc: + raise ValueError("There is already a registered encoder with this name") + reg_enc[name] = encoder + else: + if name in self._registered_encoders: + raise ValueError("There is already a registered encoder with this name") + reg_enc[name] = encoder def automorphism_group_gens(self, equivalence="semilinear"): r""" @@ -1734,21 +1737,27 @@ def __eq__(self, right): def encode(self, word, encoder_name=None, **kwargs): r""" - Transforms an element of the message space into a codeword. + Transforms an element of a message space into a codeword. INPUT: - - ``word`` -- a vector of the message space of the code + - ``word`` -- a vector of a message space of the code. - ``encoder_name`` -- (default: ``None``) Name of the encoder which will be used to encode ``word``. The default encoder of ``self`` will be used if - default value is kept + default value is kept. - - ``kwargs`` -- all additional arguments are forwarded to :meth:`encoder` + - ``kwargs`` -- all additional arguments are forwarded to the construction of the + encoder that is used. + + .. NOTE:: + + The default encoder always has `F^{k}` as message space, with `k` the dimension + of ``self`` and `F` the base ring of ``self``. OUTPUT: - - a vector of ``self`` + - a vector of ``self``. EXAMPLES:: @@ -1774,7 +1783,9 @@ def encoder(self, encoder_name=None, **kwargs): r""" Returns an encoder of ``self``. - This methods creates a new instance of the encoder subclass designated by ``name``. + The returned encoder provided by this method is cached. + + This methods creates a new instance of the encoder subclass designated by ``encoder_name``. While it is also possible to do the same by directly calling the subclass' constructor, it is strongly advised to use this method to take advantage of the caching mechanism. @@ -1785,12 +1796,16 @@ def encoder(self, encoder_name=None, **kwargs): default value is kept. - ``kwargs`` -- all additional arguments are forwarded to the constructor of the encoder - this method will return + this method will return. OUTPUT: - - an Encoder object + - an Encoder object. + .. NOTE:: + + The default encoder always has `F^{k}` as message space, with `k` the dimension + of ``self`` and `F` the base ring of ``self``. EXAMPLES:: @@ -1799,6 +1814,11 @@ def encoder(self, encoder_name=None, **kwargs): sage: C.encoder() Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 + We check that the returned encoder is cached:: + + sage: C.encoder.is_in_cache() + True + If the name of an encoder which is not known by ``self`` is passed, an exception will be raised:: @@ -1811,7 +1831,6 @@ def encoder(self, encoder_name=None, **kwargs): """ if encoder_name is None: encoder_name = self._default_encoder_name - return self.encoder(encoder_name, **kwargs) if encoder_name in self._registered_encoders: encClass = self._registered_encoders[encoder_name] E = encClass(self, **kwargs) @@ -1819,14 +1838,14 @@ def encoder(self, encoder_name=None, **kwargs): else: raise ValueError("Passed Encoder name not known") - def encoders_available(self, values=False): + def encoders_available(self, classes=False): r""" Returns a list of the available encoders' names for ``self``. INPUT: - - ``values`` -- (default: ``False``) if values is set to ``True``, it also - returns the encoders' classes associated with the encoders' names + - ``classes`` -- (default: ``False``) if ``classes`` is set to ``True``, it also + returns the encoders' classes associated with the encoders' names. EXAMPLES:: @@ -1839,10 +1858,9 @@ def encoders_available(self, values=False): {'GeneratorMatrix': } """ - reg_enc = self._registered_encoders - if values == True: + if classes == True: return copy(self._registered_encoders) - return reg_enc.keys() + return self._registered_encoders.keys() def extended_code(self): r""" @@ -2025,7 +2043,8 @@ def generator_matrix(self, encoder_name=None, **kwargs): used to compute the generator matrix. The default encoder of ``self`` will be used if default value is kept. - - ``kwargs`` -- all additional arguments are forwarded to :meth:`encoder` + - ``kwargs`` -- all additional arguments are forwarded to the construction of the + encoder that is used. EXAMPLES:: @@ -2129,6 +2148,8 @@ def information_set(self): """ Return an information set of the code. + Return value of this method is cached. + A set of column positions of a generator matrix of a code is called an information set if the corresponding columns form a square matrix of full rank. @@ -3246,6 +3267,8 @@ def unencode(self, c, encoder_name=None, nocheck=False, **kwargs): r""" Returns the message corresponding to ``c``. + This is the inverse of :meth:`encode`. + INPUT: - ``c`` -- a vector of the same length as ``self`` over the @@ -3255,14 +3278,17 @@ def unencode(self, c, encoder_name=None, nocheck=False, **kwargs): to decode ``word``. The default decoder of ``self`` will be used if default value is kept. - - ``nocheck`` -- (default: ``False``) checks if ``c`` is in self. If this is set - to True, the return value of this method is not guaranteed to be correct. + - ``nocheck`` -- (default: ``False``) checks if ``c`` is in ``self``. You might set + this to ``True`` to disable the check for saving computation. Note that if ``c`` is + not in ``self`` and ``nocheck = True``, then the output of :meth:`unencode` is + not defined (except that it will be in the message space of ``self``). - - ``kwargs`` -- all additional arguments are forwarded to :meth:`encoder` + - ``kwargs`` -- all additional arguments are forwarded to the construction of the + encoder that is used. OUTPUT: - - a vector + - an element of the message space of ``encoder_name`` of ``self``. EXAMPLES:: @@ -3639,7 +3665,8 @@ def generator_matrix(self, encoder_name=None, **kwargs): used to compute the generator matrix. ``self._generator_matrix`` will be returned if default value is kept. - - ``kwargs`` -- all additional arguments are forwarded to :meth:`encoder` + - ``kwargs`` -- all additional arguments are forwarded to the construction of the + encoder that is used. EXAMPLES:: @@ -3649,10 +3676,9 @@ def generator_matrix(self, encoder_name=None, **kwargs): [1 2 1] [2 1 1] """ - if hasattr(self, "_generator_matrix"): + if encoder_name is None or encoder_name is 'GeneratorMatrix': return self._generator_matrix - E = self.encoder(encoder_name, **kwargs) - return E.generator_matrix() + return super(LinearCode, self).generator_matrix(encoder_name, **kwargs) From 9fc78a492823ede303f45a4a6238d06a0c20f87e Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 19 Sep 2015 08:54:59 +0200 Subject: [PATCH 149/421] trac #19224: Review --- src/sage/graphs/strongly_regular_db.pyx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 79d9ed613ad..6f6d9967d5b 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -46,7 +46,7 @@ from sage.graphs.generators.smallgraphs import HigmanSimsGraph from sage.graphs.generators.smallgraphs import LocalMcLaughlinGraph from sage.graphs.generators.smallgraphs import SuzukiGraph from sage.graphs.graph import Graph -from libc.math cimport sqrt +from libc.math cimport sqrt, floor from sage.matrix.constructor import Matrix from sage.rings.finite_rings.constructor import FiniteField as GF from sage.coding.linear_code import LinearCode @@ -966,7 +966,7 @@ def is_taylor_twograph_srg(int v,int k,int l,int mu): return (TaylorTwographSRG, q) return -def is_switch_OA_srg(int v,int k,int l,int mu): +def is_switch_OA_srg(int v, int k, int l, int mu): r""" Test whether some *switch* `OA(k,n)+*` is `(v,k,\lambda,\mu)`-strongly regular. @@ -1007,19 +1007,19 @@ def is_switch_OA_srg(int v,int k,int l,int mu): (.switch_OA_srg at ..., 14, 29) """ - n_2_p_1 = v - if not is_square(n_2_p_1-1): - return None - from sage.combinat.designs.orthogonal_arrays import orthogonal_array - from math import sqrt - cdef int n = int(sqrt(n_2_p_1-1)) + + cdef int n_2_p_1 = v + cdef int n = floor(sqrt(n_2_p_1-1)) + + if n*n != n_2_p_1-1: # is it a square? + return None cdef int c = k/n if (k % n or l != c*c-1 or k != 1+(c-1)*(c+1)+(n-c)*(n-c-1) or - not orthogonal_array(c+1,n,existence=True)): + not orthogonal_array(c+1,n,existence=True,resolvable=True)): return None def switch_OA_srg(c,n): From 23d3c15e8ccc401d14135d69c62c071a43afe2b3 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 19 Sep 2015 22:58:21 -0500 Subject: [PATCH 150/421] Implementation of free Zinbiel algebras. --- src/doc/en/reference/algebras/index.rst | 2 + src/sage/algebras/catalog.py | 2 + src/sage/algebras/free_zinbiel_algebra.py | 258 ++++++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 src/sage/algebras/free_zinbiel_algebra.py diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index f80229432e5..d11483e5646 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -25,6 +25,8 @@ Algebras sage/algebras/free_algebra_quotient sage/algebras/free_algebra_quotient_element + sage/algebras/free_zinbiel_algebra + sage/algebras/group_algebra sage/algebras/iwahori_hecke_algebra diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index da062022b5d..262e8113f44 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -16,6 +16,7 @@ - :class:`algebras.FiniteDimensional ` - :class:`algebras.Free ` +- :class:`algebras.FreeZinbiel ` - :class:`algebras.PreLieAlgebra ` - :func:`algebras.GradedCommutative ` @@ -48,6 +49,7 @@ from sage.misc.lazy_import import lazy_import lazy_import('sage.algebras.nil_coxeter_algebra', 'NilCoxeterAlgebra', 'NilCoxeter') +lazy_import('sage.algebras.free_zinbiel_algebra', 'FreeZinbielAlgebra', 'FreeZinbiel') lazy_import('sage.algebras.hall_algebra', 'HallAlgebra', 'Hall') lazy_import('sage.algebras.jordan_algebra', 'JordanAlgebra', 'Jordan') lazy_import('sage.algebras.shuffle_algebra', 'ShuffleAlgebra', 'Shuffle') diff --git a/src/sage/algebras/free_zinbiel_algebra.py b/src/sage/algebras/free_zinbiel_algebra.py new file mode 100644 index 00000000000..55e0d266702 --- /dev/null +++ b/src/sage/algebras/free_zinbiel_algebra.py @@ -0,0 +1,258 @@ +""" +Free Zinbiel Algebras + +AUTHORS: + +- Travis Scrimshaw (2015-09): initial version +""" + +#***************************************************************************** +# Copyright (C) 2015 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.monoids.free_monoid import FreeMonoid +from sage.monoids.free_monoid_element import FreeMonoidElement + +from sage.misc.cachefunc import cached_method +from sage.categories.magmatic_algebras import MagmaticAlgebras +from sage.categories.rings import Rings +from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement +from sage.combinat.words.words import Words +from sage.combinat.words.alphabet import Alphabet +from sage.structure.element import generic_power +from sage.sets.family import Family + +class FreeZinbielAlgebra(CombinatorialFreeModule): + r""" + The free Zinbiel algebra on `n` generators. + + Let `R` be a ring. A *Zinbiel algebra* is a non-associative + algebra with multiplication `\circ` that satisfies + + .. MATH:: + + a \circ (b \circ c) = a \circ (b \circ c) + a \circ (c \circ b). + + Zinbiel algebras were first introduced by Loday as the Koszul + dual to Leibniz algebras (hence the name coined by Lemaire). + + Zinbiel algebras are divided power algebras, in that for + + .. MATH:: + + x^{\circ n} = \bigl(x \circ (x \circ \cdots \circ( x \circ x) \cdots + ) \bigr) + + we have + + .. MATH:: + + x^{\circ m} \circ x^{\circ n} = \binom{n+m-1}{m} x^{n+m} + + and + + .. MATH:: + + \underbrace{\bigl( ( x \circ \cdots \circ x \circ (x \circ x) \cdots + ) \bigr)}_{n+1 \text{ times}} = n! x^n. + + .. NOTE:: + + This implies that Zinbiel algebras are not power associative. + + To every Zinbiel algebra, we can construct a corresponding commutative + associative algebra by using the symmetrized product: + + .. MATH:: + + a * b = a \circ b + b \circ a. + + The free Zinbiel algebra on `n` generators is isomorphic as `R`-modules + to the reduced tensor algebra `\bar{T}(R^n)` with the product + + .. MATH:: + + (x_0 x_1 \cdots x_p) \circ (x_{p+1} x_{p+2} \cdots x_{p+q}) + = \sum_{\simga \in S_{p,q}} x_0 (x_{\sigma(1)} x_{\sigma(2)} + \cdots x_{\sigma(p+q)}, + + where `S_{p,q}` is the set of `(p,q)`-shuffles. + + The free Zinbiel algebra is free as a divided power algebra. Moreover, + the corresponding commutative algebra is isomorphic to the (non-unital) + shuffle algebra. + + INPUT: + + - ``R`` -- a ring + - ``n`` -- (optional) the number of generators + - ``names`` -- the generator names + + .. WARNING:: + + Currently the basis is indexed all words over the variables, + incuding the empty word. This is a slight abuse as it is suppose + to be the indexed by all non-empty words. + + EXAMPLES: + + We create the free Zinbiel algebra and check the defining relation:: + + sage: Z. = algebras.FreeZinbiel(QQ) + sage: (x*y)*z + Z[xyz] + Z[xzy] + sage: x*(y*z) + x*(z*y) + Z[xyz] + Z[xzy] + + We see that the Zinbiel algebra is not associative, nor even + power associative:: + + sage: x*(y*z) + Z[xyz] + sage: x*(x*x) + Z[xxx] + sage: (x*x)*x + 2*Z[xxx] + + We verify that it is a divided powers algebra:: + + sage: (x*(x*x)) * (x*(x*(x*x))) + 15*Z[xxxxxxx] + sage: binomial(3+4-1,4) + 15 + sage: (x*(x*(x*x))) * (x*(x*x)) + 20*Z[xxxxxxx] + sage: binomial(3+4-1,3) + 20 + sage: ((x*x)*x)*x + 6*Z[xxxx] + sage: (((x*x)*x)*x)*x + 24*Z[xxxx] + + REFERENCES: + + - :wikipedia:`Zinbiel_algebra` + .. [Loday95] Jean-Louis Loday. + *Cup-product for Leibniz cohomology and dual Leibniz algebras*. + Math. Scand., pp. 189--196 (1995). + http://www.math.uiuc.edu/K-theory/0015/cup_product.pdf + .. [LV12] Jean-Louis Loday and Bruno Vallette. *Algebraic Operads*. + Springer-Verlag Berlin Heidelberg (2012). + :doi:`10.1007/978-3-642-30362-3`. + """ + @staticmethod + def __classcall_private__(cls, R, n=None, names=None): + """ + Standardize input to ensure a unqiue representation. + + TESTS:: + + sage: Z1. = algebras.FreeZinbiel(QQ) + sage: Z2. = algebras.FreeZinbiel(QQ, 3) + sage: Z3 = algebras.FreeZinbiel(QQ, 3, 'x,y,z') + sage: Z4. = algebras.FreeZinbiel(QQ, 'x,y,z') + sage: Z1 is Z2 and Z1 is Z3 and Z1 is Z4 + True + """ + if isinstance(n, (list,tuple)): + names = n + n = len(names) + elif isinstance(n, str): + names = n.split(',') + n = len(names) + elif isinstance(names, str): + names = names.split(',') + elif n is None: + n = len(names) + return super(FreeZinbielAlgebra, cls).__classcall__(cls, R, n, tuple(names)) + + def __init__(self, R, n, names): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: Z. = algebras.FreeZinbiel(QQ) + sage: TestSuite(Z).run() + """ + if R not in Rings: + raise TypeError("argument R must be a ring") + indices = Words(Alphabet(n, names=names)) + cat = MagmaticAlgebras(R).WithBasis() + self._n = n + CombinatorialFreeModule.__init__(self, R, indices, prefix='Z', + category=cat) + self._assign_names(names) + + def _repr_term(self, t): + """ + Return a string representation of the basis element indexed by ``t``. + + EXAMPLES:: + + sage: Z. = algebras.FreeZinbiel(QQ) + sage: Z._repr_term(Z._indices('xyzxxy')) + 'Z[xyzxxy]' + """ + return "{!s}[{!s}]".format(self._print_options['prefix'], repr(t)[6:]) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: Z. = algebras.FreeZinbiel(QQ) + sage: Z + Free Zinbiel algebra on generators (Z[x], Z[y], Z[z]) over Rational Field + """ + return "Free Zinbiel algebra on generators {} over {}".format( + self.gens(), self.base_ring()) + + @cached_method + def algebra_generators(self): + """ + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: Z. = algebras.FreeZinbiel(QQ) + sage: list(Z.algebra_generators()) + [Z[x], Z[y], Z[z]] + """ + A = self.variable_names() + return Family( A, lambda g: self.monomial(self._indices(g)) ) + + @cached_method + def gens(self): + """ + Return the generators of ``self``. + + EXAMPLES:: + + sage: Z. = algebras.FreeZinbiel(QQ) + sage: Z.gens() + (Z[x], Z[y], Z[z]) + """ + return tuple(self.algebra_generators()) + + def product_on_basis(self, x, y): + """ + Return the product of the basis elements indexed by ``x`` and ``y``. + + EXAMPLES:: + + sage: Z. = algebras.FreeZinbiel(QQ) + sage: (x*y)*z # indirect doctest + Z[xyz] + Z[xzy] + sage: x^4 + 3*Z[xxxx] + """ + if not x: + return self.monomial(y) + x0 = self._indices(x[0]) + return self.sum_of_monomials(x0 + sh for sh in x[1:].shuffle(y)) + From 5a0384c60fb35730a87d96db3a983d0cf27610d6 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 20 Sep 2015 22:51:38 +0200 Subject: [PATCH 151/421] trac #19224: Rework the doctests --- src/sage/graphs/strongly_regular_db.pyx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 6f6d9967d5b..77b8983ab66 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -988,17 +988,13 @@ def is_switch_OA_srg(int v, int k, int l, int mu): EXAMPLES:: - sage: from sage.graphs.strongly_regular_db import is_switch_OA_srg - sage: t = is_switch_OA_srg(170, 78, 35, 36); t - (.switch_OA_srg ...>, 6, 13) - sage: g = t[0](*t[1:]); g + sage: graphs.strongly_regular_graph(170, 78, 35, 36) # indirect doctest Graph on 170 vertices - sage: g.is_strongly_regular(parameters=True) - (170, 78, 35, 36) - sage: t = is_switch_OA_srg(5,5,5,5); t TESTS:: + sage: from sage.graphs.strongly_regular_db import is_switch_OA_srg + sage: t = is_switch_OA_srg(5,5,5,5); t sage: is_switch_OA_srg(290, 136, 63, 64) (.switch_OA_srg at ..., 8, 17) sage: is_switch_OA_srg(626, 300, 143, 144) From 154cc6f676ebd0f531bba1d6b282f55f246ba180 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Mon, 21 Sep 2015 09:58:44 +0200 Subject: [PATCH 152/421] Integrated reviewer's comments --- src/sage/coding/encoder.py | 18 +++++++++++++----- src/sage/coding/linear_code.py | 15 +++++++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 8c937f217f3..6569ba13c11 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -97,6 +97,12 @@ def encode(self, word): and `k` is :meth:`sage.coding.linear_code.AbstractLinearCode.dimension`. If this is not the case, this method should be overwritten by the subclass. + .. NOTE:: + + :meth:`encode` is a partial function over ``self``'s :meth:`message_space`. + One should use the exception :class:`EncodingError` to catch attempts + to encode words that are outside of the message space. + INPUT: - ``word`` -- a vector of the message space of the ``self``. @@ -191,13 +197,16 @@ def _unencoder_matrix(self): sage: C = LinearCode(G) sage: E = C.encoder() sage: E._unencoder_matrix() + ( [0 0 1 1] [0 1 0 1] [1 1 1 0] - [0 1 1 1] + [0 1 1 1], (0, 1, 2, 3) + ) """ - Gt = self.generator_matrix().matrix_from_columns(self.code().information_set()) - return Gt.inverse() + info_set = self.code().information_set() + Gt = self.generator_matrix().matrix_from_columns(info_set) + return (Gt.inverse(), info_set) def unencode_nocheck(self, c): r""" @@ -243,8 +252,7 @@ def unencode_nocheck(self, c): sage: c == c1 False """ - U = self._unencoder_matrix() - info_set = self.code().information_set() + U, info_set = self._unencoder_matrix() cc = vector( c[i] for i in info_set ) return cc * U diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index a6d2140abe0..39e25a56853 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -1811,7 +1811,7 @@ def encoder(self, encoder_name=None, **kwargs): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: C.encoder() - Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 + Generator matrix-based encoder for Linear code of length 7, dimension 4 over Finite Field of size 2 We check that the returned encoder is cached:: @@ -3266,8 +3266,7 @@ def unencode(self, c, encoder_name=None, nocheck=False, **kwargs): INPUT: - - ``c`` -- a vector of the same length as ``self`` over the - base field of ``self`` + - ``c`` -- a codeword of ``self`` - ``encoder_name`` -- (default: ``None``) name of the decoder which will be used to decode ``word``. The default decoder of ``self`` will be used if @@ -3695,7 +3694,7 @@ def __init__(self, code): sage: C = LinearCode(G) sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E - Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 + Generator matrix-based encoder for Linear code of length 7, dimension 4 over Finite Field of size 2 """ super(LinearCodeGeneratorMatrixEncoder, self).__init__(code) @@ -3709,9 +3708,9 @@ def _repr_(self): sage: C = LinearCode(G) sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E - Generator matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 + Generator matrix-based encoder for Linear code of length 7, dimension 4 over Finite Field of size 2 """ - return "Generator matrix-based encoder for the %s" % self.code() + return "Generator matrix-based encoder for %s" % self.code() def _latex_(self): r""" @@ -3723,9 +3722,9 @@ def _latex_(self): sage: C = LinearCode(G) sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: latex(E) - \textnormal{Generator matrix-based encoder for the }[7, 4]\textnormal{ Linear code over }\Bold{F}_{2} + \textnormal{Generator matrix-based encoder for }[7, 4]\textnormal{ Linear code over }\Bold{F}_{2} """ - return "\\textnormal{Generator matrix-based encoder for the }%s" % self.code()._latex_() + return "\\textnormal{Generator matrix-based encoder for }%s" % self.code()._latex_() @cached_method def generator_matrix(self): From cc2d23212d2eded850f29a6344767e06306cfb1b Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 21 Sep 2015 19:57:20 +0200 Subject: [PATCH 153/421] Add some "no dependencies" --- build/pkgs/autotools/dependencies | 5 +++++ build/pkgs/boehm_gc/dependencies | 5 +++++ build/pkgs/boost_cropped/dependencies | 5 +++++ build/pkgs/buckygen/dependencies | 5 +++++ build/pkgs/cephes/dependencies | 5 +++++ build/pkgs/cliquer/dependencies | 5 +++++ build/pkgs/combinatorial_designs/dependencies | 5 +++++ build/pkgs/compilerwrapper/dependencies | 5 +++++ build/pkgs/cryptominisat/dependencies | 5 +++++ build/pkgs/d3js/dependencies | 5 +++++ build/pkgs/database_cremona_ellcurve/dependencies | 5 +++++ build/pkgs/database_jones_numfield/dependencies | 5 +++++ build/pkgs/database_stein_watkins/dependencies | 5 +++++ build/pkgs/database_stein_watkins_mini/dependencies | 5 +++++ build/pkgs/database_symbolic_data/dependencies | 5 +++++ build/pkgs/graphs/dependencies | 5 +++++ build/pkgs/iconv/dependencies | 5 +++++ build/pkgs/libogg/dependencies | 5 +++++ build/pkgs/lrcalc/dependencies | 5 +++++ build/pkgs/nauty/dependencies | 5 +++++ build/pkgs/ncurses/dependencies | 5 +++++ build/pkgs/openssl/dependencies | 5 +++++ build/pkgs/pari_galdata/dependencies | 5 +++++ build/pkgs/pari_seadata_small/dependencies | 5 +++++ build/pkgs/patch/dependencies | 5 +++++ build/pkgs/planarity/dependencies | 5 +++++ build/pkgs/plantri/dependencies | 5 +++++ build/pkgs/polytopes_db/dependencies | 5 +++++ build/pkgs/rubiks/dependencies | 5 +++++ build/pkgs/rw/dependencies | 5 +++++ build/pkgs/saclib/dependencies | 5 +++++ build/pkgs/symmetrica/dependencies | 5 +++++ build/pkgs/sympow/dependencies | 5 +++++ build/pkgs/termcap/dependencies | 5 +++++ build/pkgs/valgrind/dependencies | 5 +++++ build/pkgs/zeromq/dependencies | 5 +++++ build/pkgs/zlib/dependencies | 5 +++++ 37 files changed, 185 insertions(+) create mode 100644 build/pkgs/autotools/dependencies create mode 100644 build/pkgs/boehm_gc/dependencies create mode 100644 build/pkgs/boost_cropped/dependencies create mode 100644 build/pkgs/buckygen/dependencies create mode 100644 build/pkgs/cephes/dependencies create mode 100644 build/pkgs/cliquer/dependencies create mode 100644 build/pkgs/combinatorial_designs/dependencies create mode 100644 build/pkgs/compilerwrapper/dependencies create mode 100644 build/pkgs/cryptominisat/dependencies create mode 100644 build/pkgs/d3js/dependencies create mode 100644 build/pkgs/database_cremona_ellcurve/dependencies create mode 100644 build/pkgs/database_jones_numfield/dependencies create mode 100644 build/pkgs/database_stein_watkins/dependencies create mode 100644 build/pkgs/database_stein_watkins_mini/dependencies create mode 100644 build/pkgs/database_symbolic_data/dependencies create mode 100644 build/pkgs/graphs/dependencies create mode 100644 build/pkgs/iconv/dependencies create mode 100644 build/pkgs/libogg/dependencies create mode 100644 build/pkgs/lrcalc/dependencies create mode 100644 build/pkgs/nauty/dependencies create mode 100644 build/pkgs/ncurses/dependencies create mode 100644 build/pkgs/openssl/dependencies create mode 100644 build/pkgs/pari_galdata/dependencies create mode 100644 build/pkgs/pari_seadata_small/dependencies create mode 100644 build/pkgs/patch/dependencies create mode 100644 build/pkgs/planarity/dependencies create mode 100644 build/pkgs/plantri/dependencies create mode 100644 build/pkgs/polytopes_db/dependencies create mode 100644 build/pkgs/rubiks/dependencies create mode 100644 build/pkgs/rw/dependencies create mode 100644 build/pkgs/saclib/dependencies create mode 100644 build/pkgs/symmetrica/dependencies create mode 100644 build/pkgs/sympow/dependencies create mode 100644 build/pkgs/termcap/dependencies create mode 100644 build/pkgs/valgrind/dependencies create mode 100644 build/pkgs/zeromq/dependencies create mode 100644 build/pkgs/zlib/dependencies diff --git a/build/pkgs/autotools/dependencies b/build/pkgs/autotools/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/autotools/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/boehm_gc/dependencies b/build/pkgs/boehm_gc/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/boehm_gc/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/boost_cropped/dependencies b/build/pkgs/boost_cropped/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/boost_cropped/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/buckygen/dependencies b/build/pkgs/buckygen/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/buckygen/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/cephes/dependencies b/build/pkgs/cephes/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/cephes/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/cliquer/dependencies b/build/pkgs/cliquer/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/cliquer/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/combinatorial_designs/dependencies b/build/pkgs/combinatorial_designs/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/combinatorial_designs/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/compilerwrapper/dependencies b/build/pkgs/compilerwrapper/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/compilerwrapper/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/cryptominisat/dependencies b/build/pkgs/cryptominisat/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/cryptominisat/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/d3js/dependencies b/build/pkgs/d3js/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/d3js/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/database_cremona_ellcurve/dependencies b/build/pkgs/database_cremona_ellcurve/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/database_cremona_ellcurve/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/database_jones_numfield/dependencies b/build/pkgs/database_jones_numfield/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/database_jones_numfield/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/database_stein_watkins/dependencies b/build/pkgs/database_stein_watkins/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/database_stein_watkins/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/database_stein_watkins_mini/dependencies b/build/pkgs/database_stein_watkins_mini/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/database_stein_watkins_mini/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/database_symbolic_data/dependencies b/build/pkgs/database_symbolic_data/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/database_symbolic_data/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/graphs/dependencies b/build/pkgs/graphs/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/graphs/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/iconv/dependencies b/build/pkgs/iconv/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/iconv/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/libogg/dependencies b/build/pkgs/libogg/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/libogg/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/lrcalc/dependencies b/build/pkgs/lrcalc/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/lrcalc/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/nauty/dependencies b/build/pkgs/nauty/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/nauty/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/ncurses/dependencies b/build/pkgs/ncurses/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/ncurses/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/openssl/dependencies b/build/pkgs/openssl/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/openssl/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/pari_galdata/dependencies b/build/pkgs/pari_galdata/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/pari_galdata/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/pari_seadata_small/dependencies b/build/pkgs/pari_seadata_small/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/pari_seadata_small/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/patch/dependencies b/build/pkgs/patch/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/patch/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/planarity/dependencies b/build/pkgs/planarity/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/planarity/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/plantri/dependencies b/build/pkgs/plantri/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/plantri/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/polytopes_db/dependencies b/build/pkgs/polytopes_db/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/polytopes_db/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/rubiks/dependencies b/build/pkgs/rubiks/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/rubiks/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/rw/dependencies b/build/pkgs/rw/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/rw/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/saclib/dependencies b/build/pkgs/saclib/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/saclib/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/symmetrica/dependencies b/build/pkgs/symmetrica/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/symmetrica/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/sympow/dependencies b/build/pkgs/sympow/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/sympow/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/termcap/dependencies b/build/pkgs/termcap/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/termcap/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/valgrind/dependencies b/build/pkgs/valgrind/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/valgrind/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/zeromq/dependencies b/build/pkgs/zeromq/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/zeromq/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/zlib/dependencies b/build/pkgs/zlib/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/zlib/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. From d4cc95be16dcf1b402bea40ae73f83833487b8ce Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 21 Sep 2015 14:17:54 -0700 Subject: [PATCH 154/421] addressing referee's remarks --- src/sage/combinat/designs/block_design.py | 8 +-- .../graphs/generators/classical_geometries.py | 49 ++++++++++--------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index d516efce18e..a34356d25eb 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -165,7 +165,7 @@ def are_hyperplanes_in_projective_geometry_parameters(v, k, lmbda, return_parame return (True, (q,d)) if return_parameters else True -def ProjectiveGeometryDesign(n, d, F, algorithm=None, coordinates=None, check=True): +def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=False, check=True): """ Return a projective geometry design. @@ -188,7 +188,7 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, coordinates=None, check=Tr GAP's "design" package must be available in this case, and that it can be installed with the ``gap_packages`` spkg. - - ``coordinates`` -- ``None`` by default. In this case and also if + - ``point_coordinates`` -- ``False`` by default. In this case and also if ``algorithm="gap"``, the ground set is indexed by integers, Otherwise it is indexed by coordinates in `F^{n+1}`. @@ -209,7 +209,7 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, coordinates=None, check=Tr Use coordinates:: - sage: PG = designs.ProjectiveGeometryDesign(2,1,GF(3),coordinates=1) + sage: PG = designs.ProjectiveGeometryDesign(2,1,GF(3),point_coordinates=1) sage: PG.blocks()[0] [(1, 0, 0), (1, 0, 1), (1, 0, 2), (0, 0, 1)] @@ -235,7 +235,7 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, coordinates=None, check=Tr b.append(points[m]) blocks.append(b) B = BlockDesign(len(points), blocks, name="ProjectiveGeometryDesign", check=check) - if not coordinates is None: + if point_coordinates: B.relabel({i:p[0] for p,i in points.iteritems()}) return B if algorithm == "gap": # Requires GAP's Design diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index ad34b7be04f..defb5e6ae37 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -739,7 +739,7 @@ def UnitaryDualPolarGraph(m, q): EXAMPLES: - The point graph of a generalized quadrangle of order (8,4):: + The point graph of a generalized quadrangle (see [GQwiki]_, [PT09]_) of order (8,4):: sage: G = graphs.UnitaryDualPolarGraph(5,2); G # long time Unitary Dual Polar Graph DU(5, 2); GQ(8, 4): Graph on 297 vertices @@ -940,14 +940,8 @@ def AhrensSzekeresGQ(q, dual=False): r""" Return the collinearity graph of GQ AS(q) or its dual - INPUT: - - - ``q`` -- a power of an odd prime number - - - ``dual`` -- if ``False`` (default), return the graph of `GQ(q-1,q+1)`. - Otherwise return the graph of `GQ(q+1,q-1)`. - - `AS(q)` is a generalised quadrangle (GQ) of order `(q-1,q+1)`, see 3.1.5 in [PT09]_. + `AS(q)` is a generalised quadrangle (GQ, see [GQwiki]_) of order `(q-1,q+1)`, + see 3.1.5 in [PT09]_. Let `q` be an odd prime power. Then the points are elements of `F_q^3`, and lines are of the form @@ -957,6 +951,13 @@ def AhrensSzekeresGQ(q, dual=False): where `a`, `b`, `c` are arbitrary elements of `F_q`. + INPUT: + + - ``q`` -- a power of an odd prime number + + - ``dual`` -- if ``False`` (default), return the graph of `GQ(q-1,q+1)`. + Otherwise return the graph of `GQ(q+1,q-1)`. + EXAMPLES:: sage: g=graphs.AhrensSzekeresGQ(5); g @@ -970,6 +971,9 @@ def AhrensSzekeresGQ(q, dual=False): REFERENCE: + .. [GQwiki] `Generalized quadrangle + `__ + .. [PT09] S. Payne, J. A. Thas. Finite generalized quadrangles. European Mathematical Society, @@ -995,10 +999,18 @@ def AhrensSzekeresGQ(q, dual=False): G.name('AS('+str(q)+'); GQ'+str((q-1,q+1))) return G -def T2starGQ(q, dual=False, hyperoval=None, field=None, checkhyperoval=False): +def T2starGQ(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): r""" Return the collinearity graph of GQ T_2*(q) or its dual + `T_2^*(q)` is a generalised quadrangle (GQ, see [GQwiki]_) + of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. + Let `q=2^k` and `\Theta=PG(3,q)`. Fix a plane `\Pi \subset \Theta` and a + `hyperoval `__ + `O \subset \Pi`. The points of the GQ are the points of `\Theta` + outside `\Pi`, and the lines are the lines of `\Theta` outside `\Pi` + that meet `\Pi` in a point of `O`. + INPUT: - ``q`` -- a power of two @@ -1015,16 +1027,9 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, checkhyperoval=False): - ``field`` -- an instance of a finite field of order `q`, must be provided if ``hyperoval`` is provided. - - ``checkhyperoval`` -- (default: ``False``) if ``True``, + - ``check_hyperoval`` -- (default: ``True``) if ``True``, check ``hyperoval`` for correctness. - `T_2^*(q)` is a generalised quadrangle (GQ) of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. - Let `q=2^k` and `\Theta=PG(3,q)`. Fix a plane `\Pi \subset \Theta` and a - `hyperoval `__ - `O \subset \Pi`. The points of the GQ are the points of `\Theta` - outside `\Pi`, and the lines are the lines of `\Theta` outside `\Pi` - that meet `\Pi` in a point of `O`. - EXAMPLES: @@ -1052,12 +1057,12 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, checkhyperoval=False): sage: F=GF(4,'b') # repeating a point... sage: O=[vector(F,(0,1,0,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) - sage: graphs.T2starGQ(4, hyperoval=O, field=F, checkhyperoval=True) + sage: graphs.T2starGQ(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval size sage: O=[vector(F,(0,1,1,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) - sage: graphs.T2starGQ(4, hyperoval=O, field=F, checkhyperoval=True) + sage: graphs.T2starGQ(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval @@ -1074,7 +1079,7 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, checkhyperoval=False): else: F = field - Theta = PG(3,1,F,coordinates=1) + Theta = PG(3, 1, F, point_coordinates=1) Pi = set(filter(lambda x: x[0]==F.zero(), Theta.ground_set())) if hyperoval is None: O = filter(lambda x: x[1]+x[2]*x[3]==0 or (x[1]==1 and x[2]==0 and x[3]==0), Pi) @@ -1083,7 +1088,7 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, checkhyperoval=False): map(lambda x: x.set_immutable(), hyperoval) O = set(hyperoval) - if checkhyperoval: + if check_hyperoval and (not hyperoval is None): if len(O) != q+2: raise RuntimeError("incorrect hyperoval size") for L in Theta.blocks(): From 2a62ef16861fb421358f3d5f636cc9fc97e59011 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 21 Sep 2015 23:11:09 -0700 Subject: [PATCH 155/421] further doc fixes --- .../graphs/generators/classical_geometries.py | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index defb5e6ae37..28fd7c71831 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -938,16 +938,15 @@ def TaylorTwographSRG(q): def AhrensSzekeresGQ(q, dual=False): r""" - Return the collinearity graph of GQ AS(q) or its dual + Return the collinearity graph of the generalized quadrangle `AS(q)`, or of its dual - `AS(q)` is a generalised quadrangle (GQ, see [GQwiki]_) of order `(q-1,q+1)`, - see 3.1.5 in [PT09]_. - Let `q` be an odd prime power. Then the points are elements of `F_q^3`, - and lines are of the form + Let `q` be an odd prime power. `AS(q)` is a generalized quadrangle [GQwiki]_ of + order `(q-1,q+1)`, see 3.1.5 in [PT09]_. Its points are elements + of `F_q^3`, and lines are sets of size `q` of the form - * `(\sigma, a, b), \sigma\in F_q` - * `(a, \sigma, b), \sigma\in F_q` - * `(c \sigma^2 - b \sigma + a, -2 c \sigma + b, \sigma), \sigma\in F_q` + * `\{ (\sigma, a, b) \mid \sigma\in F_q \}` + * `\{ (a, \sigma, b) \mid \sigma\in F_q \}` + * `\{ (c \sigma^2 - b \sigma + a, -2 c \sigma + b, \sigma) \mid \sigma\in F_q \}`, where `a`, `b`, `c` are arbitrary elements of `F_q`. @@ -955,8 +954,8 @@ def AhrensSzekeresGQ(q, dual=False): - ``q`` -- a power of an odd prime number - - ``dual`` -- if ``False`` (default), return the graph of `GQ(q-1,q+1)`. - Otherwise return the graph of `GQ(q+1,q-1)`. + - ``dual`` -- if ``False`` (default), return the collinearity graph of `GQ(q-1,q+1)`. + Otherwise return the collinearity graph of `GQ(q+1,q-1)`. EXAMPLES:: @@ -1001,13 +1000,12 @@ def AhrensSzekeresGQ(q, dual=False): def T2starGQ(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): r""" - Return the collinearity graph of GQ T_2*(q) or its dual + Return the collinearity graph of the generalized quadrangle `T_2*(q)`, or of its dual - `T_2^*(q)` is a generalised quadrangle (GQ, see [GQwiki]_) - of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. - Let `q=2^k` and `\Theta=PG(3,q)`. Fix a plane `\Pi \subset \Theta` and a + Let `q=2^k` and `\Theta=PG(3,q)`. `T_2^*(q)` is a generalised quadrangle [GQwiki]_ + of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. Fix a plane `\Pi \subset \Theta` and a `hyperoval `__ - `O \subset \Pi`. The points of the GQ are the points of `\Theta` + `O \subset \Pi`. The points of `T_2^*(q):=T_2^*(O)` are the points of `\Theta` outside `\Pi`, and the lines are the lines of `\Theta` outside `\Pi` that meet `\Pi` in a point of `O`. From 6bb228fab7fbd1ccb7b5b9d061dc48bdcdc9409c Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 22 Sep 2015 09:30:13 +0200 Subject: [PATCH 156/421] use SageObject instead of object --- src/sage/data_structures/mutable_poset.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index bae921007e1..ed21211482d 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -155,8 +155,10 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.structure.sage_object import SageObject -class MutablePosetShell(object): + +class MutablePosetShell(SageObject): r""" A shell for an element of a :class:`mutable poset `. @@ -198,6 +200,7 @@ def __init__(self, poset, element): self._element_ = element self._predecessors_ = set() self._successors_ = set() + super(MutablePosetShell, self).__init__() @property @@ -1100,7 +1103,7 @@ def is_MutablePoset(P): return isinstance(P, MutablePoset) -class MutablePoset(object): +class MutablePoset(SageObject): r""" A data structure that models a mutable poset (partially ordered set). @@ -1227,6 +1230,7 @@ def __init__(self, data=None, key=None, merge=None, can_merge=None): raise TypeError('%s is not iterable; do not know what to ' 'do with it.' % (data,)) self.union_update(it) + super(MutablePoset, self).__init__() def clear(self): From 07591351097bc4c28294ca358fee5c4e0a27b28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Tue, 22 Sep 2015 11:20:20 +0300 Subject: [PATCH 157/421] Change default of facade to true in order_ideals_lattice(). --- src/sage/categories/finite_posets.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index 06ff1fa25f6..ffddd4bbbb0 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -1678,7 +1678,7 @@ def toggling_orbit_iter(self, vs, oideal, element_constructor=set, stop=True, ch next = self.order_ideal_toggles(next, vs) yield element_constructor(next) - def order_ideals_lattice(self, as_ideals=True, facade=False): + def order_ideals_lattice(self, as_ideals=True, facade=True): r""" Return the lattice of order ideals of a poset ``self``, ordered by inclusion. @@ -1703,7 +1703,7 @@ def order_ideals_lattice(self, as_ideals=True, facade=False): EXAMPLES:: - sage: P = Posets.PentagonPoset(facade = True) + sage: P = Posets.PentagonPoset() sage: P.cover_relations() [[0, 1], [0, 2], [1, 4], [2, 3], [3, 4]] sage: J = P.order_ideals_lattice(); J @@ -1727,8 +1727,11 @@ def order_ideals_lattice(self, as_ideals=True, facade=False): sage: J.cover_relations() [[{}, {0}], [{0}, {0, 2}], [{0}, {0, 1}], [{0, 2}, {0, 1, 2}], [{0, 1}, {0, 1, 2}], [{0, 1, 2}, {0, 1, 2, 3}]] - .. NOTE:: we use facade posets in the examples above just - to ensure a nicer ordering in the output. + sage: P = Poset({1:[2]}) + sage: J_facade = P.order_ideals_lattice() + sage: J_nonfacade = P.order_ideals_lattice(facade=False) + sage: type(J_facade[0]) == type(J_nonfacade[0]) + False """ from sage.combinat.posets.lattices import LatticePoset if as_ideals: From 097ac4e9452facc25f8a11dd1996d101683af825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Tue, 22 Sep 2015 11:43:05 +0300 Subject: [PATCH 158/421] New default value for facade: None. (=take it from the poset) --- src/sage/categories/finite_posets.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index ffddd4bbbb0..06e741dd0fa 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -1678,7 +1678,7 @@ def toggling_orbit_iter(self, vs, oideal, element_constructor=set, stop=True, ch next = self.order_ideal_toggles(next, vs) yield element_constructor(next) - def order_ideals_lattice(self, as_ideals=True, facade=True): + def order_ideals_lattice(self, as_ideals=True, facade=None): r""" Return the lattice of order ideals of a poset ``self``, ordered by inclusion. @@ -1700,6 +1700,9 @@ def order_ideals_lattice(self, as_ideals=True, facade=True): - ``as_ideals`` -- Boolean, if ``True`` (default) returns a poset on the set of order ideals, otherwise on the set of antichains + - ``facade`` -- Boolean or ``None`` (default). Whether to + return a facade lattice or not. By default return facade + lattice if the poset is a facade poset. EXAMPLES:: @@ -1734,6 +1737,8 @@ def order_ideals_lattice(self, as_ideals=True, facade=True): False """ from sage.combinat.posets.lattices import LatticePoset + if facade is None: + facade = self._is_facade if as_ideals: from sage.misc.misc import attrcall from sage.sets.set import Set From bfe6b60683f8e6a3f7024cdee6f48e4607db389a Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 22 Sep 2015 11:00:51 -0500 Subject: [PATCH 159/421] Fixing documtation. --- src/sage/algebras/free_zinbiel_algebra.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/algebras/free_zinbiel_algebra.py b/src/sage/algebras/free_zinbiel_algebra.py index 55e0d266702..6d0b6497c2c 100644 --- a/src/sage/algebras/free_zinbiel_algebra.py +++ b/src/sage/algebras/free_zinbiel_algebra.py @@ -76,7 +76,7 @@ class FreeZinbielAlgebra(CombinatorialFreeModule): .. MATH:: (x_0 x_1 \cdots x_p) \circ (x_{p+1} x_{p+2} \cdots x_{p+q}) - = \sum_{\simga \in S_{p,q}} x_0 (x_{\sigma(1)} x_{\sigma(2)} + = \sum_{\sigma \in S_{p,q}} x_0 (x_{\sigma(1)} x_{\sigma(2)} \cdots x_{\sigma(p+q)}, where `S_{p,q}` is the set of `(p,q)`-shuffles. @@ -135,6 +135,7 @@ class FreeZinbielAlgebra(CombinatorialFreeModule): REFERENCES: - :wikipedia:`Zinbiel_algebra` + .. [Loday95] Jean-Louis Loday. *Cup-product for Leibniz cohomology and dual Leibniz algebras*. Math. Scand., pp. 189--196 (1995). From 2b7736294074261e90e184ddd454b928833e160e Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 22 Sep 2015 10:43:57 -0700 Subject: [PATCH 160/421] changed the default of point_coordinates, added a doctest --- src/sage/combinat/designs/block_design.py | 16 +++++++++++----- .../combinat/designs/incidence_structures.py | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index a34356d25eb..ce1fc319b8c 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -165,7 +165,7 @@ def are_hyperplanes_in_projective_geometry_parameters(v, k, lmbda, return_parame return (True, (q,d)) if return_parameters else True -def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=False, check=True): +def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=True, check=True): """ Return a projective geometry design. @@ -188,9 +188,9 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=False, c GAP's "design" package must be available in this case, and that it can be installed with the ``gap_packages`` spkg. - - ``point_coordinates`` -- ``False`` by default. In this case and also if - ``algorithm="gap"``, the ground set is indexed by integers, - Otherwise it is indexed by coordinates in `F^{n+1}`. + - ``point_coordinates`` -- ``True`` by default. Ignored and assumed to be ``False`` if + ``algorithm="gap"``. If ``True``, the ground set is indexed by coordinates in `F^{n+1}`. + Otherwise the ground set is indexed by integers, EXAMPLES: @@ -209,10 +209,16 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=False, c Use coordinates:: - sage: PG = designs.ProjectiveGeometryDesign(2,1,GF(3),point_coordinates=1) + sage: PG = designs.ProjectiveGeometryDesign(2,1,GF(3)) sage: PG.blocks()[0] [(1, 0, 0), (1, 0, 1), (1, 0, 2), (0, 0, 1)] + Use indexing by integers:: + + sage: PG = designs.ProjectiveGeometryDesign(2,1,GF(3),point_coordinates=0) + sage: PG.blocks()[0] + [0, 1, 2, 12] + Check that the constructor using gap also works:: sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index b34aae434a3..95af7ec2635 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -398,7 +398,7 @@ def __contains__(self, block): True sage: ["Am", "I", "finally", "done ?"] in IS False - sage: IS = designs.ProjectiveGeometryDesign(3, 1, GF(2)) + sage: IS = designs.ProjectiveGeometryDesign(3, 1, GF(2), point_coordinates=False) sage: [3,8,7] in IS True sage: [3,8,9] in IS From 5e0a6d77628c9637daf180a2e9b0dbdf0a869b4e Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 22 Sep 2015 10:58:57 -0700 Subject: [PATCH 161/421] updated is_GQqmqp to make reviewer happier --- src/sage/graphs/strongly_regular_db.pyx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 2c80a283bd9..b5b0e05ab1a 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -918,20 +918,20 @@ def is_GQqmqp(int v,int k,int l,int mu): # v=(s+1)(st+1), k=s(t+1) S=l+1 T=mu-1 + q = (S+T)//2 + p, w = is_prime_power(q, get_data=True) if (v == (S+1)*(S*T+1) and k == S*(T+1) and - (S+T) % 2 == 0): - q = (S+T)/2 - p, k = is_prime_power(q, get_data=True) + q == p**w and + (S+T)/2 == q): if p % 2 == 0: from sage.graphs.generators.classical_geometries import T2starGQ as F else: from sage.graphs.generators.classical_geometries import AhrensSzekeresGQ as F - if k != 0: - if (S,T) == (q-1, q+1): - return (F, q, False) - elif (S,T) == (q+1, q-1): - return (F, q, True) + if (S,T) == (q-1, q+1): + return (F, q, False) + elif (S,T) == (q+1, q-1): + return (F, q, True) @cached_function def is_twograph_descendant_of_srg(int v, int k0, int l, int mu): From 5f5706434f184eafc41ed4c0dad9b76abe1293b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 22 Sep 2015 21:08:16 +0200 Subject: [PATCH 162/421] trac #18937 patchbot version 2.4.8 (bugfix) --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index 7cebb66b8e2..0138cd1c8e6 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=be2fcb7039c02f37669f5ea078fb2868730df67c -md5=3ebebd93ce6194dee6263270a6419970 -cksum=2163763617 +sha1=32b2209e7fd8050ee8555cbd9fe695c770850b16 +md5=cffdb67332a325ab801c9f7f15c1b4ee +cksum=2059267430 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index e30309f735e..f041bc6dba2 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.4.7 +2.4.8 From b5a2fcd90050e59021aef77f1a803aaeb65f4c55 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 22 Sep 2015 22:32:46 -0700 Subject: [PATCH 163/421] Graph Graph Graph... --- .../graphs/generators/classical_geometries.py | 18 +++++++-------- src/sage/graphs/graph_generators.py | 8 +++---- src/sage/graphs/strongly_regular_db.pyx | 22 ++++++++++--------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index 28fd7c71831..93b26c39755 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -936,7 +936,7 @@ def TaylorTwographSRG(q): G.name("Taylor two-graph SRG") return G -def AhrensSzekeresGQ(q, dual=False): +def AhrensSzekeresGQGraph(q, dual=False): r""" Return the collinearity graph of the generalized quadrangle `AS(q)`, or of its dual @@ -959,11 +959,11 @@ def AhrensSzekeresGQ(q, dual=False): EXAMPLES:: - sage: g=graphs.AhrensSzekeresGQ(5); g + sage: g=graphs.AhrensSzekeresGQGraph(5); g AS(5); GQ(4, 6): Graph on 125 vertices sage: g.is_strongly_regular(parameters=True) (125, 28, 3, 7) - sage: g=graphs.AhrensSzekeresGQ(5,dual=True); g + sage: g=graphs.AhrensSzekeresGQGraph(5,dual=True); g AS(5)*; GQ(6, 4): Graph on 175 vertices sage: g.is_strongly_regular(parameters=True) (175, 30, 5, 5) @@ -998,7 +998,7 @@ def AhrensSzekeresGQ(q, dual=False): G.name('AS('+str(q)+'); GQ'+str((q-1,q+1))) return G -def T2starGQ(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): +def T2starGQGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): r""" Return the collinearity graph of the generalized quadrangle `T_2*(q)`, or of its dual @@ -1033,11 +1033,11 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): using the built-in construction:: - sage: g=graphs.T2starGQ(4); g + sage: g=graphs.T2starGQGraph(4); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 18, 2, 6) - sage: g=graphs.T2starGQ(4,dual=True); g + sage: g=graphs.T2starGQGraph(4,dual=True); g T2*(O,4)*; GQ(5, 3): Graph on 96 vertices sage: g.is_strongly_regular(parameters=True) (96, 20, 4, 4) @@ -1046,7 +1046,7 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): sage: F=GF(4,'b') sage: O=[vector(F,(0,0,0,1)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) - sage: g=graphs.T2starGQ(4, hyperoval=O, field=F); g + sage: g=graphs.T2starGQGraph(4, hyperoval=O, field=F); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 18, 2, 6) @@ -1055,12 +1055,12 @@ def T2starGQ(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): sage: F=GF(4,'b') # repeating a point... sage: O=[vector(F,(0,1,0,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) - sage: graphs.T2starGQ(4, hyperoval=O, field=F) + sage: graphs.T2starGQGraph(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval size sage: O=[vector(F,(0,1,1,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) - sage: graphs.T2starGQ(4, hyperoval=O, field=F) + sage: graphs.T2starGQGraph(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index d6ce9e5c69f..25c012b0bd2 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -235,7 +235,7 @@ def __append_to_doc(methods): __append_to_doc( ["AffineOrthogonalPolarGraph", - "AhrensSzekeresGQ", + "AhrensSzekeresGQGraph", "NonisotropicOrthogonalPolarGraph", "NonisotropicUnitaryPolarGraph", "OrthogonalPolarGraph", @@ -243,7 +243,7 @@ def __append_to_doc(methods): "SymplecticPolarGraph", "TaylorTwographDescendantSRG", "TaylorTwographSRG", - "T2starGQ", + "T2starGQGraph", "UnitaryDualPolarGraph", "UnitaryPolarGraph"]) @@ -1999,7 +1999,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None ########################################################################### import sage.graphs.generators.classical_geometries AffineOrthogonalPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.AffineOrthogonalPolarGraph) - AhrensSzekeresGQ = staticmethod(sage.graphs.generators.classical_geometries.AhrensSzekeresGQ) + AhrensSzekeresGQGraph = staticmethod(sage.graphs.generators.classical_geometries.AhrensSzekeresGQGraph) NonisotropicOrthogonalPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.NonisotropicOrthogonalPolarGraph) NonisotropicUnitaryPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.NonisotropicUnitaryPolarGraph) OrthogonalPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.OrthogonalPolarGraph) @@ -2009,7 +2009,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None TaylorTwographDescendantSRG = \ staticmethod(sage.graphs.generators.classical_geometries.TaylorTwographDescendantSRG) TaylorTwographSRG = staticmethod(sage.graphs.generators.classical_geometries.TaylorTwographSRG) - T2starGQ = staticmethod(sage.graphs.generators.classical_geometries.T2starGQ) + T2starGQGraph = staticmethod(sage.graphs.generators.classical_geometries.T2starGQGraph) UnitaryDualPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.UnitaryDualPolarGraph) UnitaryPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.UnitaryPolarGraph) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index b5b0e05ab1a..3b78d8a7963 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -876,23 +876,23 @@ def is_GQqmqp(int v,int k,int l,int mu): sage: from sage.graphs.strongly_regular_db import is_GQqmqp sage: t = is_GQqmqp(27,10,1,5); t - (, 3, False) + (, 3, False) sage: g = t[0](*t[1:]); g AS(3); GQ(2, 4): Graph on 27 vertices sage: t = is_GQqmqp(45,12,3,3); t - (, 3, True) + (, 3, True) sage: g = t[0](*t[1:]); g AS(3)*; GQ(4, 2): Graph on 45 vertices sage: g.is_strongly_regular(parameters=True) (45, 12, 3, 3) sage: t = is_GQqmqp(16,6,2,2); t - (, 2, True) + (, 2, True) sage: g = t[0](*t[1:]); g T2*(O,2)*; GQ(3, 1): Graph on 16 vertices sage: g.is_strongly_regular(parameters=True) (16, 6, 2, 2) sage: t = is_GQqmqp(64,18,2,6); t - (, 4, False) + (, 4, False) sage: g = t[0](*t[1:]); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) @@ -902,16 +902,16 @@ def is_GQqmqp(int v,int k,int l,int mu): sage: (S,T)=(127,129) sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t - (, 128, False) + (, 128, False) sage: (S,T)=(129,127) sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t - (, 128, True) + (, 128, True) sage: (S,T)=(124,126) sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t - (, 125, False) + (, 125, False) sage: (S,T)=(126,124) sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t - (, 125, True) + (, 125, True) sage: t = is_GQqmqp(5,5,5,5); t """ # do we have GQ(s,t)? we must have mu=t+1, s=l+1, @@ -925,9 +925,11 @@ def is_GQqmqp(int v,int k,int l,int mu): q == p**w and (S+T)/2 == q): if p % 2 == 0: - from sage.graphs.generators.classical_geometries import T2starGQ as F + from sage.graphs.generators.classical_geometries\ + import T2starGQGraph as F else: - from sage.graphs.generators.classical_geometries import AhrensSzekeresGQ as F + from sage.graphs.generators.classical_geometries\ + import AhrensSzekeresGQGraph as F if (S,T) == (q-1, q+1): return (F, q, False) elif (S,T) == (q+1, q-1): From 8de4188efff6b534e881dbe8e7071f026f47a317 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 23 Sep 2015 10:16:16 +0200 Subject: [PATCH 164/421] Trac #17693: minor language adjustments --- src/sage/data_structures/mutable_poset.py | 29 ++++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index daf731cd188..be2ae5ccc25 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -63,8 +63,9 @@ ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) -It is equipped with a `\leq`-operation which makes `a \leq b` if all -entries of `a` are at most `b`. For example, we have +It is equipped with a `\leq`-operation such that `a \leq b` if all +entries of `a` are at most the corresponding entry of `b`. For +example, we have :: @@ -75,7 +76,7 @@ (True, True, False) The last comparison gives ``False``, since the comparison of the -first component yield `2 \leq 1`. +first component checks whether `2 \leq 1`. Now, let us add such elements to a poset:: @@ -90,7 +91,7 @@ poset((1, 1), (2, 2), (2, 3), (3, 2), (3, 3), (4, 1)) In the representation above, the elements are sorted topologically, -smallest first. This does not show (directly) more structural +smallest first. This does not (directly) show more structural information. We can overcome this and display a "wiring layout" by typing:: @@ -778,7 +779,7 @@ def _iter_depth_first_visit_(self, marked, shell to ``True`` (include in iteration) or ``False`` (do not include). ``None`` is equivalent to a function returning always ``True``. Note that the iteration does not go beyond a - not shell included shell. + not included shell. OUTPUT: @@ -828,7 +829,7 @@ def iter_depth_first(self, reverse=False, key=None, condition=None): shell to ``True`` (include in iteration) or ``False`` (do not include). ``None`` is equivalent to a function returning always ``True``. Note that the iteration does not go beyond a - not shell included shell. + not included shell. OUTPUT: @@ -887,7 +888,7 @@ def _iter_topological_visit_(self, marked, shell to ``True`` (include in iteration) or ``False`` (do not include). ``None`` is equivalent to a function returning always ``True``. Note that the iteration does not go beyond a - not shell included shell. + not included shell. OUTPUT: @@ -937,7 +938,7 @@ def iter_topological(self, reverse=False, key=None, condition=None): shell to ``True`` (include in iteration) or ``False`` (do not include). ``None`` is equivalent to a function returning always ``True``. Note that the iteration does not go beyond a - not shell included shell. + not included shell. OUTPUT: @@ -1043,8 +1044,8 @@ def merge(self, element, check=True, delete=True): ``can_merge``-function of :class:`MutablePoset` determines if the merge is possible. - - ``delete`` -- (default: ``True``) if set, then the passed - element is removed from the poset after the merge. + - ``delete`` -- (default: ``True``) if set, then `element` + is removed from the poset after the merge. OUTPUT: @@ -1125,14 +1126,14 @@ class MutablePoset(SageObject): - ``merge`` -- a function which merges its second argument (an element) to its first (again an element) and returns the result (as an element). If the return value is ``None``, the element is - removed out of the poset. + removed from the poset. This hook is called by :meth:`merge`. Moreover it is used during :meth:`add` when an element (more precisely its key) is already in this poset. ``merge`` is ``None`` (default) is equivalent to ``merge`` - returns its first argument. Note that it is not allowed that the + returning its first argument. Note that it is not allowed that the key of the returning element differs from the key of the first input parameter. @@ -1144,7 +1145,7 @@ class MutablePoset(SageObject): in this poset. ``can_merge`` is ``None`` (default) is equivalent to ``can_merge`` - returns ``True`` in all cases. + returning ``True`` in all cases. OUTPUT: @@ -1857,7 +1858,7 @@ def add(self, element): INPUT: - ``element`` -- an object (hashable and supporting comparison - with the operator ``<=``. + with the operator ``<=``). OUTPUT: From 740852e2897cfb1e4903150f110c8c242ba153f3 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 23 Sep 2015 10:17:53 +0200 Subject: [PATCH 165/421] Trac #17693: command instead of description for short descriptions --- src/sage/data_structures/mutable_poset.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index be2ae5ccc25..3b3fada85ed 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -266,7 +266,7 @@ def predecessors(self, reverse=False): INPUT: - - ``reverse`` -- (default: ``False``) if set, then returns + - ``reverse`` -- (default: ``False``) if set, then return successors instead. OUTPUT: @@ -293,7 +293,7 @@ def successors(self, reverse=False): INPUT: - - ``reverse`` -- (default: ``False``) if set, then returns + - ``reverse`` -- (default: ``False``) if set, then return predecessors instead. OUTPUT: @@ -813,7 +813,7 @@ def _iter_depth_first_visit_(self, marked, def iter_depth_first(self, reverse=False, key=None, condition=None): r""" - Iterates over all shells in depth first order. + Iterate over all shells in depth first order. INPUT: @@ -922,7 +922,7 @@ def _iter_topological_visit_(self, marked, def iter_topological(self, reverse=False, key=None, condition=None): r""" - Iterates over all shells in topological order. + Iterate over all shells in topological order. INPUT: @@ -1090,7 +1090,7 @@ def merge(self, element, check=True, delete=True): def is_MutablePoset(P): r""" - Tests if ``P`` inherits from :class:`MutablePoset`. + Test whether ``P`` inherits from :class:`MutablePoset`. TESTS:: @@ -1463,7 +1463,7 @@ def _copy_shells_(self, other, mapping): def copy(self, mapping=None): r""" - Creates a shallow copy. + Create a shallow copy. INPUT: @@ -1822,7 +1822,7 @@ def repr_full(self, reverse=False): def contains(self, key): r""" - Tests if ``key`` is encapsulated by one of the poset's elements. + Test whether ``key`` is encapsulated by one of the poset's elements. INPUT: From 347656c7dec2fef3ed8f43c5f24e5ade977b35f3 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 23 Sep 2015 10:18:53 +0200 Subject: [PATCH 166/421] Trac #17693: language adjustment: whether instead of if --- src/sage/data_structures/mutable_poset.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 3b3fada85ed..7fe73a0c7e2 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -316,7 +316,7 @@ def successors(self, reverse=False): def is_special(self): r""" - Return if this shell contains either the null-element, i.e., the + Return whether this shell contains either the null-element, i.e., the element smaller than any possible other element or the infinity-element, i.e., the element larger than any possible other element. @@ -343,7 +343,7 @@ def is_special(self): def is_null(self): r""" - Return if this shell contains the null-element, i.e., the element + Return whether this shell contains the null-element, i.e., the element smaller than any possible other element. OUTPUT: @@ -364,7 +364,7 @@ def is_null(self): def is_oo(self): r""" - Return if this shell contains the infinity-element, i.e., the element + Return whether this shell contains the infinity-element, i.e., the element larger than any possible other element. OUTPUT: @@ -570,7 +570,7 @@ def eq(self, other): This method compares the keys of the elements contained in the shells, if the elements are not both ``None``. - Otherwise, this method checks if both shells describe the + Otherwise, this method checks whether both shells describe the same special element. TESTS:: @@ -1042,7 +1042,7 @@ def merge(self, element, check=True, delete=True): - ``check`` -- (default: ``True``) if set, then the ``can_merge``-function of :class:`MutablePoset` determines - if the merge is possible. + whether the merge is possible. - ``delete`` -- (default: ``True``) if set, then `element` is removed from the poset after the merge. @@ -1137,7 +1137,7 @@ class MutablePoset(SageObject): key of the returning element differs from the key of the first input parameter. - - ``can_merge`` -- a function which checks if its second argument + - ``can_merge`` -- a function which checks whether its second argument can be merged to its first. This hook is called by :meth:`merge`. Moreover it is used during @@ -2623,7 +2623,7 @@ def symmetric_difference_update(self, other): def is_disjoint(self, other): r""" - Return if another poset is disjoint to this poset. + Return whether another poset is disjoint to this poset. INPUT: @@ -2665,7 +2665,7 @@ def is_disjoint(self, other): def is_subset(self, other): r""" - Return if another poset contains this poset, i.e., if this poset + Return whether another poset contains this poset, i.e., whether this poset is a subset of the other poset. INPUT: @@ -2712,7 +2712,7 @@ def is_subset(self, other): def is_superset(self, other): r""" - Return if this poset contains another poset, i.e., if this poset + Return whether this poset contains another poset, i.e., whether this poset is a superset of the other poset. INPUT: @@ -2780,7 +2780,7 @@ def merge(self, key=None, reverse=False): Nothing. This method tests all (not necessarily direct) successors and - predecessors of the given element if they can be merged with + predecessors of the given element whether they can be merged with the element itself. This is done by the ``can_merge``-function of :class:`MutablePoset`. If this merge is possible, then it is performed by calling :class:`MutablePoset`'s From 4774baac95a8490280253fc3de41a12c896fc6b4 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 23 Sep 2015 10:20:05 +0200 Subject: [PATCH 167/421] Trac #17693: corrections in documentation --- src/sage/data_structures/mutable_poset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 7fe73a0c7e2..bc8c1566bdd 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -560,7 +560,7 @@ def eq(self, other): INPUT: - - ``right`` -- a shell. + - ``other`` -- a shell. OUTPUT: @@ -881,7 +881,7 @@ def _iter_topological_visit_(self, marked, ``True`` searches towards ``'null'``. - ``key`` -- (default: ``None``) a function used for sorting - the direct successors of a shell (used in case of a + the direct predecessors of a shell (used in case of a tie). If this is ``None``, no sorting occurs. - ``condition`` -- (default: ``None``) a function mapping a @@ -931,7 +931,7 @@ def iter_topological(self, reverse=False, key=None, condition=None): ``True`` searches towards ``'null'``. - ``key`` -- (default: ``None``) a function used for sorting - the direct successors of a shell (used in case of a + the direct predeccessors of a shell (used in case of a tie). If this is ``None``, no sorting occurs. - ``condition`` -- (default: ``None``) a function mapping a From fe5d2f1dec3f35eeaf2a4e525e4fe3e9807d17a7 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 23 Sep 2015 10:20:23 +0200 Subject: [PATCH 168/421] Trac #17693: additional doctest --- src/sage/data_structures/mutable_poset.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index bc8c1566bdd..df363c4d911 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -579,6 +579,7 @@ def eq(self, other): sage: P = MP() sage: from sage.data_structures.mutable_poset import MutablePosetShell sage: e = MutablePosetShell(P, (1, 2)) + sage: f = MutablePosetShell(P, (2, 1)) sage: z = P.null sage: oo = P.oo sage: z == z @@ -587,6 +588,8 @@ def eq(self, other): True sage: e == e True + sage: e == f + False sage: z == e False sage: e == oo From e4f1fea624a794d32d2e3380a8eb42ee6888af4f Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 23 Sep 2015 10:21:01 +0200 Subject: [PATCH 169/421] Trac #17693: fix indentations and whitespace --- src/sage/data_structures/mutable_poset.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index df363c4d911..c8228185c12 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -676,7 +676,7 @@ def _search_covers_(self, covers, shell, reverse=False): Note that ``False`` is returned if we do not have ``self <= shell``. - TESTS:: + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP sage: class T(tuple): @@ -979,7 +979,7 @@ def iter_topological(self, reverse=False, key=None, condition=None): :: sage: for e in P.shells_topological(include_special=True, - ....: reverse=True): + ....: reverse=True): ....: print e ....: print list(e.iter_topological(reverse=True, key=repr)) oo @@ -1002,7 +1002,7 @@ def iter_topological(self, reverse=False, key=None, condition=None): :: sage: for e in P.shells_topological(include_special=True, - ....: reverse=True): + ....: reverse=True): ....: print e ....: print list(e.iter_topological(reverse=False, key=repr)) oo @@ -1548,7 +1548,7 @@ def shells_topological(self, include_special=False, - ``include_special`` -- (default: ``False``) if set, then including shells containing a smallest element (`\emptyset`) and a largest element (`\infty`). - + - ``reverse`` -- (default: ``False``) -- if set, reverses the order, i.e., ``False`` gives smallest elements first, ``True`` gives largest first. From 254951dad86ad94fa55c91f3f811497614490493 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 23 Sep 2015 10:21:40 +0200 Subject: [PATCH 170/421] Trac #17693: add keyword in doctest for clarity --- src/sage/data_structures/mutable_poset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index c8228185c12..8b52bbc4494 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -795,7 +795,7 @@ def _iter_depth_first_visit_(self, marked, sage: P.add(42) sage: P.add(5) sage: marked = set() - sage: list(P.oo._iter_depth_first_visit_(marked, True)) + sage: list(P.oo._iter_depth_first_visit_(marked, reverse=True)) [oo, 42, 5, null] """ if (condition is not None and @@ -904,7 +904,7 @@ def _iter_topological_visit_(self, marked, sage: P.add(42) sage: P.add(5) sage: marked = set() - sage: list(P.null._iter_topological_visit_(marked, True)) + sage: list(P.null._iter_topological_visit_(marked, reverse=True)) [oo, 42, 5, null] """ if (condition is not None and From f7bce7e8aff4e25a23b57d1052ff13df302120e5 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 23 Sep 2015 10:22:06 +0200 Subject: [PATCH 171/421] Trac #17693: remove superfluous doctest --- src/sage/data_structures/mutable_poset.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 8b52bbc4494..41192935b46 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1405,7 +1405,6 @@ def get_key(self, element): TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: from sage.data_structures.mutable_poset import MutablePosetShell sage: P = MP() sage: P.get_key(None) is None True From ed96cbcf9d58ab262082d1c8dbac00c8b0af6b5f Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 23 Sep 2015 10:29:43 +0200 Subject: [PATCH 172/421] Trac #17693: alternative implementation of covers --- src/sage/data_structures/mutable_poset.py | 63 ++++------------------- 1 file changed, 9 insertions(+), 54 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 41192935b46..01a83adcbc2 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -654,57 +654,6 @@ def _copy_all_linked_(self, memo, poset, mapping): return new - def _search_covers_(self, covers, shell, reverse=False): - r""" - Search for cover shells of this shell. - - This is a helper function for :meth:`covers`. - - INPUT: - - - ``covers`` -- a set which finally contains all covers. - - - ``shell`` -- the shell for which to find the covering shells. - - - ``reverse`` -- (default: ``False``) if not set, then find - the lower covers, otherwise find the upper covers. - - OUTPUT: - - ``True`` or ``False``. - - Note that ``False`` is returned if we do not have - ``self <= shell``. - - TESTS:: - - sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: class T(tuple): - ....: def __le__(left, right): - ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1, 1))) - sage: P.add(T((1, 3, 1))) - sage: P.add(T((2, 1, 2))) - sage: P.add(T((4, 4, 2))) - sage: P.add(T((1, 2, 2))) - sage: P.add(T((2, 2, 2))) - sage: e = P.shell(T((2, 2, 2))); e - (2, 2, 2) - sage: covers = set() - sage: P.null._search_covers_(covers, e) - True - sage: sorted(covers, key=lambda c: repr(c.element)) - [(1, 2, 2), (2, 1, 2)] - """ - if not self.le(shell, reverse) or self == shell: - return False - if not any([e._search_covers_(covers, shell, reverse) - for e in self.successors(reverse)]): - covers.add(self) - return True - - def covers(self, shell, reverse=False): r""" Return the covers of the given shell (considering only @@ -753,9 +702,15 @@ def covers(self, shell, reverse=False): ....: key=lambda c: repr(c.element)) [(4, 4)] """ - covers = set() - self._search_covers_(covers, shell, reverse) - return covers + if self == shell: + return set() + covers = set().union(*(e.covers(shell, reverse) + for e in self.successors(reverse) + if e.le(shell, reverse))) + if covers: + return covers + else: + return set([self]) def _iter_depth_first_visit_(self, marked, From ec15597efaf476e4d1cc822877e77a3ec740d78d Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Wed, 23 Sep 2015 10:31:40 +0200 Subject: [PATCH 173/421] Trac #17693: alternative implementation add --- src/sage/data_structures/mutable_poset.py | 24 ++++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 41192935b46..61d334f77d8 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2008,20 +2008,16 @@ def add(self, element): return new = MutablePosetShell(self, element) - smaller = self.null.covers(new, reverse=False) - larger = self.oo.covers(new, reverse=True) - - # In the following we first search towards oo (reverse=False) and - # then towards null (reverse=True; everything is "inverted"). - for reverse in (False, True): - sm = smaller if not reverse else larger - la = larger if not reverse else smaller - for shell in sm: - for e in shell.successors(reverse).intersection(la): - e.predecessors(reverse).remove(shell) - shell.successors(reverse).remove(e) - new.predecessors(reverse).add(shell) - shell.successors(reverse).add(new) + new._predecessors_ = self.null.covers(new, reverse=False) + new._successors_ = self.oo.covers(new, reverse=True) + + for s in new.predecessors(): + for l in s.successors().intersection(new.successors()): + l.predecessors().remove(s) + s.successors().remove(l) + s.successors().add(new) + for l in new.successors(): + l.predecessors().add(new) self._shells_[key] = new From d0435eb70960ec1755e15b9ac4ac705e009e3c7c Mon Sep 17 00:00:00 2001 From: David Lucas Date: Wed, 23 Sep 2015 10:53:19 +0200 Subject: [PATCH 174/421] Replaced import of linear_code module by a lazy_import --- src/sage/coding/all.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/coding/all.py b/src/sage/coding/all.py index 84e1b6e81f5..e753a13d621 100644 --- a/src/sage/coding/all.py +++ b/src/sage/coding/all.py @@ -62,11 +62,12 @@ elias_bound_asymp, mrrw1_bound_asymp) -from linear_code import (LinearCode, LinearCodeFromVectorSpace, - best_known_linear_code, - best_known_linear_code_www, - bounds_minimum_distance, - self_orthogonal_binary_codes) +lazy_import("sage.coding.linear_code", ["LinearCode",\ + "LinearCodeFromVectorSpace",\ + "best_known_linear_code",\ + "best_known_linear_code_www",\ + "bounds_minimum_distance", + "self_orthogonal_binary_codes"]) from sd_codes import self_dual_codes_binary From 42cf082c47cee522e61b4a4067b7c431d0c2268a Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Wed, 23 Sep 2015 05:55:53 -0500 Subject: [PATCH 175/421] handle empty matroids --- src/sage/matroids/matroid.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index bd705e8f433..4a879f7e638 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -6741,7 +6741,8 @@ cdef class Matroid(SageObject): from sage.matroids.union_matroid import MatroidSum, PartitionMatroid if self.loops(): raise ValueError("Cannot partition matroids with loops.") - + if self.size()==0: + return [set()] # doubling search for minimum independent sets that partitions the groundset n = self.size() r = self.rank() From b8c0cc769e88462e6da06bec09351fd5c30cb69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Wed, 23 Sep 2015 23:07:30 +1200 Subject: [PATCH 176/421] Upgrade networkx to 1.10 --- build/pkgs/networkx/checksums.ini | 6 +++--- build/pkgs/networkx/package-version.txt | 2 +- src/sage/graphs/generic_graph.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/pkgs/networkx/checksums.ini b/build/pkgs/networkx/checksums.ini index 50122f00a66..9aaf119a3b8 100644 --- a/build/pkgs/networkx/checksums.ini +++ b/build/pkgs/networkx/checksums.ini @@ -1,4 +1,4 @@ tarball=networkx-VERSION.tar.gz -sha1=d6c1524724d3e47f7621bb2072863463924bfb99 -md5=b4a9e68ecd1b0164446ee432d2e20bd0 -cksum=3256827710 +sha1=99292e464c25be5e96de295752880bf5e5f1848a +md5=eb7a065e37250a4cc009919dacfe7a9d +cksum=2520536431 diff --git a/build/pkgs/networkx/package-version.txt b/build/pkgs/networkx/package-version.txt index a8fdfda1c78..c044b1a3269 100644 --- a/build/pkgs/networkx/package-version.txt +++ b/build/pkgs/networkx/package-version.txt @@ -1 +1 @@ -1.8.1 +1.10 diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index b005ae8e4be..4c6305adc27 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -14560,9 +14560,9 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, sage: D.shortest_path(4, 9, algorithm='BFS') [4, 3, 2, 1, 8, 9] sage: D.shortest_path(4, 9, algorithm='Dijkstra_NetworkX') - [4, 3, 2, 1, 8, 9] + [4, 17, 16, 12, 13, 9] sage: D.shortest_path(4, 9, algorithm='Dijkstra_Bid_NetworkX') - [4, 3, 2, 1, 8, 9] + [4, 17, 16, 12, 13, 9] sage: D.shortest_path(4, 9, algorithm='Dijkstra_Bid') [4, 3, 19, 0, 10, 9] sage: D.shortest_path(5, 5) From 3062471726c26fa0727f594f75ad73f26037683b Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Wed, 23 Sep 2015 14:02:57 +0200 Subject: [PATCH 177/421] cross-review changes: use `` instead of ` once --- src/sage/data_structures/mutable_poset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 7d45fe15260..6731748956d 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1048,7 +1048,7 @@ def merge(self, element, check=True, delete=True): ``can_merge``-function of :class:`MutablePoset` determines whether the merge is possible. - - ``delete`` -- (default: ``True``) if set, then `element` + - ``delete`` -- (default: ``True``) if set, then ``element`` is removed from the poset after the merge. OUTPUT: From 9945e9f3d6d7e594ad42bd224f61de3c69df490a Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Wed, 23 Sep 2015 14:40:02 +0200 Subject: [PATCH 178/421] Trac #17693, comment 36, 4: lists instead of .add --- src/sage/data_structures/mutable_poset.py | 267 ++++++---------------- 1 file changed, 76 insertions(+), 191 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 6731748956d..a114273951a 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -49,7 +49,13 @@ We see that they elements are sorted using `\leq` which exists on the integers `\ZZ`. Since this is even a total order, we could have used a -more efficient data structure. +more efficient data structure. Alternativly, we can write +:: + + sage: MP([42, 7, 13, 3]) + poset(3, 7, 13, 42) + +to add several elements at once on construction. A less boring Example @@ -80,14 +86,8 @@ Now, let us add such elements to a poset:: - sage: Q = MP() - sage: Q.add(T((1, 1))) - sage: Q.add(T((3, 3))) - sage: Q.add(T((4, 1))) - sage: Q.add(T((3, 2))) - sage: Q.add(T((2, 3))) - sage: Q.add(T((2, 2))) - sage: Q + sage: Q = MP([T((1, 1)), T((3, 3)), T((4, 1)), + ....: T((3, 2)), T((2, 3)), T((2, 2))]); Q poset((1, 1), (2, 2), (2, 3), (3, 2), (3, 3), (4, 1)) In the representation above, the elements are sorted topologically, @@ -683,13 +683,8 @@ def _search_covers_(self, covers, shell, reverse=False): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1, 1))) - sage: P.add(T((1, 3, 1))) - sage: P.add(T((2, 1, 2))) - sage: P.add(T((4, 4, 2))) - sage: P.add(T((1, 2, 2))) - sage: P.add(T((2, 2, 2))) + sage: P = MP([T((1, 1, 1)), T((1, 3, 1)), T((2, 1, 2)), + ....: T((4, 4, 2)), T((1, 2, 2)), T((2, 2, 2))]) sage: e = P.shell(T((2, 2, 2))); e (2, 2, 2) sage: covers = set() @@ -738,13 +733,8 @@ def covers(self, shell, reverse=False): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: e = P.shell(T((2, 2))); e (2, 2) sage: sorted(P.null.covers(e), @@ -849,13 +839,8 @@ def iter_depth_first(self, reverse=False, key=None, condition=None): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: list(P.null.iter_depth_first(reverse=False, key=repr)) [null, (1, 1), (1, 2), (1, 3), (4, 4), oo, (2, 2), (2, 1)] sage: list(P.oo.iter_depth_first(reverse=True, key=repr)) @@ -969,13 +954,8 @@ def iter_topological(self, reverse=False, key=None, condition=None): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) :: @@ -1062,11 +1042,8 @@ def merge(self, element, check=True, delete=True): ....: return (left[0], ''.join(sorted(left[1] + right[1]))) sage: def can_add(left, right): ....: return left[0] <= right[0] - sage: P = MP(key=lambda c: c[0], merge=add, can_merge=can_add) - sage: P.add((1, 'a')) - sage: P.add((3, 'b')) - sage: P.add((2, 'c')) - sage: P.add((4, 'd')) + sage: P = MP([(1, 'a'), (3, 'b'), (2, 'c'), (4, 'd')], + ....: key=lambda c: c[0], merge=add, can_merge=can_add) sage: P poset((1, 'a'), (2, 'c'), (3, 'b'), (4, 'd')) sage: P.shell(2).merge((3, 'b')) @@ -1443,11 +1420,8 @@ def _copy_shells_(self, other, mapping): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2))]) sage: Q = MP() sage: Q._copy_shells_(P, lambda e: e) sage: P.repr_full() == Q.repr_full() @@ -1483,12 +1457,8 @@ def copy(self, mapping=None): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2))]) sage: Q = copy(P) # indirect doctest sage: P.repr_full() == Q.repr_full() True @@ -1574,13 +1544,8 @@ def shells_topological(self, include_special=False, sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: list(P.shells_topological()) [(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (4, 4)] sage: list(P.shells_topological(reverse=True)) @@ -1613,10 +1578,7 @@ def elements(self, **kwargs): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3) - sage: P.add(42) - sage: P.add(7) + sage: P = MP([3, 42, 7]) sage: [(v, type(v)) for v in sorted(P.elements())] [(3, ), (7, ), @@ -1657,13 +1619,8 @@ def elements_topological(self, **kwargs): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: [(v, type(v)) for v in P.elements_topological()] [((1, 1), ), ((1, 2), ), @@ -1691,10 +1648,7 @@ def keys(self, **kwargs): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP(key=lambda c: -c) - sage: P.add(3) - sage: P.add(42) - sage: P.add(7) + sage: P = MP([3, 42, 7], key=lambda c: -c) sage: [(v, type(v)) for v in sorted(P.keys())] [(-42, ), (-7, ), @@ -1731,13 +1685,8 @@ def keys_topological(self, **kwargs): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP(key=lambda c: c[0]) - sage: P.add((1, 1)) - sage: P.add((1, 3)) - sage: P.add((2, 1)) - sage: P.add((4, 4)) - sage: P.add((1, 2)) - sage: P.add((2, 2)) + sage: P = MP([(1, 1), (1, 3), (2, 1), (4, 4), (1, 2), (2, 2)], + ....: key=lambda c: c[0]) sage: [(v, type(v)) for v in P.keys_topological()] [(1, ), (2, ), @@ -1874,12 +1823,8 @@ def add(self, element): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2))]) sage: print P.repr_full(reverse=True) poset((4, 4), (1, 3), (1, 2), (2, 1), (1, 1)) +-- oo @@ -1970,13 +1915,9 @@ def add(self, element): TESTS:: - sage: R = MP(key=lambda k: T(k[2:3])) - sage: R.add((1, 1, 42)) - sage: R.add((1, 3, 42)) - sage: R.add((2, 1, 7)) - sage: R.add((4, 4, 42)) - sage: R.add((1, 2, 7)) - sage: R.add((2, 2, 7)) + sage: R = MP([(1, 1, 42), (1, 3, 42), (2, 1, 7), + ....: (4, 4, 42), (1, 2, 7), (2, 2, 7)], + ....: key=lambda k: T(k[2:3])) sage: print R.repr_full(reverse=True) poset((1, 1, 42), (2, 1, 7)) +-- oo @@ -2052,13 +1993,8 @@ def remove(self, key, raise_key_error=True): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: print P.repr_full(reverse=True) poset((4, 4), (1, 3), (2, 2), (1, 2), (2, 1), (1, 1)) +-- oo @@ -2112,13 +2048,9 @@ def remove(self, key, raise_key_error=True): TESTS:: - sage: Q = MP(key=lambda k: T(k[0:2])) - sage: Q.add((1, 1, 42)) - sage: Q.add((1, 3, 42)) - sage: Q.add((2, 1, 7)) - sage: Q.add((4, 4, 42)) - sage: Q.add((1, 2, 7)) - sage: Q.add((2, 2, 7)) + sage: Q = MP([(1, 1, 42), (1, 3, 42), (2, 1, 7), + ....: (4, 4, 42), (1, 2, 7), (2, 2, 7)], + ....: key=lambda k: T(k[0:2])) sage: print Q.repr_full(reverse=True) poset((4, 4, 42), (1, 3, 42), (2, 2, 7), (1, 2, 7), (2, 1, 7), (1, 1, 42)) @@ -2224,13 +2156,8 @@ def discard(self, key, raise_key_error=False): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: P.discard(T((1, 2))) sage: P.remove(T((1, 2))) Traceback (most recent call last): @@ -2305,11 +2232,9 @@ def union(self, *other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.union(Q) poset(3, 4, 7, 8, 42) @@ -2353,11 +2278,9 @@ def union_update(self, other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.union_update(Q) sage: P @@ -2406,11 +2329,9 @@ def difference(self, *other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.difference(Q) poset(3, 7) @@ -2452,11 +2373,9 @@ def difference_update(self, other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.difference_update(Q) sage: P @@ -2492,11 +2411,9 @@ def intersection(self, *other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.intersection(Q) poset(42) @@ -2534,11 +2451,9 @@ def intersection_update(self, other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.intersection_update(Q) sage: P @@ -2574,11 +2489,9 @@ def symmetric_difference(self, other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.symmetric_difference(Q) poset(3, 4, 7, 8) @@ -2610,11 +2523,9 @@ def symmetric_difference_update(self, other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.symmetric_difference_update(Q) sage: P @@ -2647,11 +2558,9 @@ def is_disjoint(self, other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.is_disjoint(Q) False @@ -2690,11 +2599,9 @@ def is_subset(self, other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.is_subset(Q) False @@ -2737,11 +2644,9 @@ def is_superset(self, other): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP() - sage: P.add(3); P.add(42); P.add(7); P + sage: P = MP([3, 42, 7]); P poset(3, 7, 42) - sage: Q = MP() - sage: Q.add(4); Q.add(8); Q.add(42); Q + sage: Q = MP([4, 8, 42]); Q poset(4, 8, 42) sage: P.is_superset(Q) False @@ -2803,13 +2708,9 @@ def merge(self, key=None, reverse=False): ....: ''.join(sorted(left[2] + right[2]))) sage: def can_add(left, right): ....: return key(left) >= key(right) - sage: P = MP(key=key, merge=add, can_merge=can_add) - sage: P.add((1, 1, 'a')) - sage: P.add((1, 3, 'b')) - sage: P.add((2, 1, 'c')) - sage: P.add((4, 4, 'd')) - sage: P.add((1, 2, 'e')) - sage: P.add((2, 2, 'f')) + sage: P = MP([(1, 1, 'a'), (1, 3, 'b'), (2, 1, 'c'), + ....: (4, 4, 'd'), (1, 2, 'e'), (2, 2, 'f')], + ....: key=key, merge=add, can_merge=can_add) sage: Q = copy(P) sage: Q.merge(T((1, 3))) sage: print Q.repr_full(reverse=True) @@ -2908,12 +2809,8 @@ def maximal_elements(self): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 1))) - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((1, 2)), T((2, 2))]) sage: list(P.maximal_elements()) [(1, 3), (2, 2)] """ @@ -2940,12 +2837,8 @@ def minimal_elements(self): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: list(P.minimal_elements()) [(1, 2), (2, 1)] """ @@ -2983,12 +2876,8 @@ def map(self, function, topological=False, reverse=False): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: P.map(lambda e: str(e)) sage: P poset('(1, 2)', '(1, 3)', '(2, 1)', '(2, 2)', '(4, 4)') @@ -3024,12 +2913,8 @@ def mapped(self, function): sage: class T(tuple): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) - sage: P = MP() - sage: P.add(T((1, 3))) - sage: P.add(T((2, 1))) - sage: P.add(T((4, 4))) - sage: P.add(T((1, 2))) - sage: P.add(T((2, 2))) + sage: P = MP([T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: P.mapped(lambda e: str(e)) poset('(1, 2)', '(1, 3)', '(2, 1)', '(2, 2)', '(4, 4)') """ From 57457eb26536a776cfd024f3f881f731d14021a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 23 Sep 2015 15:15:24 +0200 Subject: [PATCH 179/421] trac #18937 version 2.4.9, forcing the doc to build so that tests can pass --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index 0138cd1c8e6..a5f3f2d4a37 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=32b2209e7fd8050ee8555cbd9fe695c770850b16 -md5=cffdb67332a325ab801c9f7f15c1b4ee -cksum=2059267430 +sha1=f4e1cb57aca1f8225e1c2baca228dccf232c424d +md5=6d67b4ea3c3559135a5edc2c01f776cd +cksum=1760773179 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index f041bc6dba2..3f5987a5cb2 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.4.8 +2.4.9 From b73b175cbcc8aa8a28a84cfb3466d058406e48c4 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 23 Sep 2015 08:03:25 -0700 Subject: [PATCH 180/421] replace GQ by more appropriate things, and remove extra condition in if --- .../graphs/generators/classical_geometries.py | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index 93b26c39755..f7e92ae13d5 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -954,8 +954,8 @@ def AhrensSzekeresGQGraph(q, dual=False): - ``q`` -- a power of an odd prime number - - ``dual`` -- if ``False`` (default), return the collinearity graph of `GQ(q-1,q+1)`. - Otherwise return the collinearity graph of `GQ(q+1,q-1)`. + - ``dual`` -- if ``False`` (default), return the collinearity graph of `AS(q)`. + Otherwise return the collinearity graph of the dual `AS(q)`. EXAMPLES:: @@ -1000,7 +1000,7 @@ def AhrensSzekeresGQGraph(q, dual=False): def T2starGQGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): r""" - Return the collinearity graph of the generalized quadrangle `T_2*(q)`, or of its dual + Return the collinearity graph of the generalized quadrangle `T_2^*(q)`, or of its dual Let `q=2^k` and `\Theta=PG(3,q)`. `T_2^*(q)` is a generalised quadrangle [GQwiki]_ of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. Fix a plane `\Pi \subset \Theta` and a @@ -1013,14 +1013,16 @@ def T2starGQGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=Tru - ``q`` -- a power of two - - ``dual`` -- if ``False`` (default), return the graph of `GQ(q-1,q+1)`. - Otherwise return the graph of `GQ(q+1,q-1)`. + - ``dual`` -- if ``False`` (default), return the graph of `T_2^*(O)`. + Otherwise return the graph of the dual `T_2^*(O)`. - ``hyperoval`` -- a hyperoval (i.e. a complete 2-arc; a set of points in the plane meeting every line in 0 or 2 points) in the plane of points with 0th coordinate 0 in `PG(3,q)` over the field ``field``. Each point of ``hyperoval`` must be a length 4 vector over ``field`` with 1st non-0 coordinate equal to 1. By default, ``hyperoval`` and - ``field`` are not specified, and constructed on the fly. + ``field`` are not specified, and constructed on the fly. In particular, ``hyperoval`` + we build is the classical one, i.e. a conic with the point of intersection of its + tangent lines. - ``field`` -- an instance of a finite field of order `q`, must be provided if ``hyperoval`` is provided. @@ -1085,14 +1087,13 @@ def T2starGQGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=Tru else: map(lambda x: x.set_immutable(), hyperoval) O = set(hyperoval) - - if check_hyperoval and (not hyperoval is None): - if len(O) != q+2: - raise RuntimeError("incorrect hyperoval size") - for L in Theta.blocks(): - if set(L).issubset(Pi): - if not len(O.intersection(L)) in [0,2]: - raise RuntimeError("incorrect hyperoval") + if check_hyperoval: + if len(O) != q+2: + raise RuntimeError("incorrect hyperoval size") + for L in Theta.blocks(): + if set(L).issubset(Pi): + if not len(O.intersection(L)) in [0,2]: + raise RuntimeError("incorrect hyperoval") L = map(lambda z: filter(lambda y: not y in O, z), filter(lambda x: len(O.intersection(x)) == 1, Theta.blocks())) if dual: From 605b2dfc685a8eb0e3caf1163a0adad5c47b9f9a Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 23 Sep 2015 10:48:42 -0700 Subject: [PATCH 181/421] typo in a graph docstring --- src/sage/graphs/generators/families.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index 05efc1dc10b..303383ef39f 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -46,7 +46,7 @@ def JohnsonGraph(n, k): sage: g.is_vertex_transitive() True - The complement of the Johnson graph `J(n,2)` is isomorphic to the Knesser + The complement of the Johnson graph `J(n,2)` is isomorphic to the Kneser Graph `K(n,2)`. In paritcular the complement of `J(5,2)` is isomorphic to the Petersen graph. :: From fac62d77f4a1bc26cdb92140d61c306ab5028636 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Wed, 23 Sep 2015 13:14:48 -0500 Subject: [PATCH 182/421] add test --- src/sage/matroids/matroid.pyx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 4a879f7e638..e7bd6db34eb 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -6731,6 +6731,9 @@ cdef class Matroid(SageObject): True sage: sum(map(len,P))==len(M.groundset()) True + sage: from sage.matroids.advanced import * + sage: BasisMatroid().partition() + [set()] ALGORITHM: From 79f01f9100ea8e9d8275ba72e712bcb478343102 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Wed, 23 Sep 2015 13:20:13 -0500 Subject: [PATCH 183/421] should return empty set --- src/sage/matroids/matroid.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index e7bd6db34eb..2a6d7f3d92f 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -6733,7 +6733,7 @@ cdef class Matroid(SageObject): True sage: from sage.matroids.advanced import * sage: BasisMatroid().partition() - [set()] + [] ALGORITHM: @@ -6745,7 +6745,7 @@ cdef class Matroid(SageObject): if self.loops(): raise ValueError("Cannot partition matroids with loops.") if self.size()==0: - return [set()] + return [] # doubling search for minimum independent sets that partitions the groundset n = self.size() r = self.rank() From dfa11ac492437cedd686081681fec297404ecf59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 24 Sep 2015 13:28:48 +1200 Subject: [PATCH 184/421] Removing work around for older version of networkx. It gets in the way in newer install. --- build/pkgs/networkx/spkg-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/networkx/spkg-install b/build/pkgs/networkx/spkg-install index 6769f79adac..5cd75ee5791 100755 --- a/build/pkgs/networkx/spkg-install +++ b/build/pkgs/networkx/spkg-install @@ -15,4 +15,4 @@ rm -rf "$SAGE_LOCAL"/spkg/network* cd src -python setup.py install --home="$SAGE_LOCAL" --force +python setup.py install From 2d9dfcdf97ce3b741d181b6f0c6f1f1cc68b880c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 24 Sep 2015 14:03:36 +1200 Subject: [PATCH 185/421] Networkx has a runtime dependency on decorator and may try to install it if not present --- build/pkgs/networkx/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/networkx/dependencies b/build/pkgs/networkx/dependencies index edf27112135..d4db0ff5eae 100644 --- a/build/pkgs/networkx/dependencies +++ b/build/pkgs/networkx/dependencies @@ -1,4 +1,4 @@ -$(INST)/$(PYTHON) +$(INST)/$(PYTHON) $(INST)/$(DECORATOR) ---------- All lines of this file are ignored except the first. From 4f8a84b8f65b917883e46e28b8942ae52dd09b3d Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 24 Sep 2015 13:17:06 +0200 Subject: [PATCH 186/421] trac #19279: IncidenceStructure.is_generalized_quadrangle --- .../combinat/designs/incidence_structures.py | 89 +++++++++++++++++++ src/sage/graphs/hypergraph_generators.py | 19 ++++ 2 files changed, 108 insertions(+) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index b34aae434a3..35ed5b0f068 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -1640,6 +1640,95 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): ll = b return (True, (tt,v,k,ll)) if return_parameters else True + def is_generalized_quadrangle(self, verbose=False): + r""" + Test if the incidence structure is a generalized quadrangle. + + An incidence structure is a generalized quadrangle iif: + + - it is `s+1`-:meth:`uniform ` for some positive integer `s`. + + - it is `t+1`-:meth:`regular ` for some positive integer `t`. + + - two blocks intersect on at most one point. + + - For every point `p` not in a block `B`, there is a unique block `B'` + intersecting both `\{p\}` and `B` + + For more information, see the :wikipedia:`Generalized_quadrangle`. + + INPUT: + + - ``verbose`` (boolean) -- whether to print an explanation when the + instance is not a generalized quadrangle. + + EXAMPLE:: + + sage: h = designs.CremonaRichmondConfiguration() + sage: h.is_generalized_quadrangle() + True + + TESTS:: + + sage: H = IncidenceStructure([[1,2],[3,4,5]]) + sage: H.is_generalized_quadrangle(verbose=True) + The incidence structure is not (s+1)-uniform for some s>0. + False + + sage: H = IncidenceStructure([[1,2,3],[3,4,5]]) + sage: H.is_generalized_quadrangle(verbose=True) + The incidence structure is not (t+1)-regular for some t>0. + False + + sage: H = IncidenceStructure((2*graphs.CompleteGraph(3)).edges(labels=False)) + sage: H.is_generalized_quadrangle(verbose=True) + Some point is at distance >3 from some block. + False + + sage: G = graphs.CycleGraph(5) + sage: B = list(G.subgraph_search_iterator(graphs.PathGraph(3))) + sage: H = IncidenceStructure(B) + sage: H.is_generalized_quadrangle(verbose=True) + Two blocks intersect on >1 points. + False + + sage: hypergraphs.CompleteUniform(4,2).is_generalized_quadrangle(verbose=1) + Some point has two projections on some line. + False + """ + s = self.is_uniform()-1 + if not s or s <= 0: + if verbose: + print "The incidence structure is not (s+1)-uniform for some s>0." + return False + + t = self.is_regular()-1 + if not t or t <= 0: + if verbose: + print "The incidence structure is not (t+1)-regular for some t>0." + return False + + # The distance between a point and a line in the incidence graph is odd + # and must be <= 3. Thus, the diameter is at most 4 + g = self.incidence_graph() + if g.diameter() > 4: + if verbose: + print "Some point is at distance >3 from some block." + return False + + # There is a unique projection of a point on a line. Thus, the girth of + # g is at least 7 + girth = g.girth() + if girth == 4: + if verbose: + print "Two blocks intersect on >1 points." + return False + elif girth == 6: + if verbose: + print "Some point has two projections on some line." + return False + return True + def dual(self, algorithm=None): """ Return the dual of the incidence structure. diff --git a/src/sage/graphs/hypergraph_generators.py b/src/sage/graphs/hypergraph_generators.py index 0deb4cbafdd..7dfb9473d99 100644 --- a/src/sage/graphs/hypergraph_generators.py +++ b/src/sage/graphs/hypergraph_generators.py @@ -159,4 +159,23 @@ def nauty(self, number_of_sets, number_of_vertices, yield tuple( tuple( x for x in G.neighbors(v)) for v in range(number_of_vertices, total)) + def CompleteUniform(self, n, k): + r""" + Return the complete `k`-uniform hypergraph on `n` points. + + INPUT: + + - ``k,n`` -- nonnegative integers with `k\leq n` + + EXAMPLE:: + + sage: h = hypergraphs.CompleteUniform(5,2); h + Incidence structure with 5 points and 10 blocks + sage: len(h.packing()) + 2 + """ + from sage.combinat.designs.incidence_structures import IncidenceStructure + from itertools import combinations + return IncidenceStructure(list(combinations(range(n),k))) + hypergraphs = HypergraphGenerators() From 7ab4a0bfb0f42bab0aad673daa9077aadb2ae364 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:00:27 +0200 Subject: [PATCH 187/421] Trac #17693, comment 36, 6: rewrite doc of method key --- src/sage/data_structures/mutable_poset.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index a114273951a..c9e3d6601ce 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -243,7 +243,9 @@ def key(self): r""" The key of the element contained in this shell. - The element is converted by the poset to the key. + The key of an element is determined by the mutable poset (the + parent) via the ``key``-function (see construction of a + :class:`MutablePoset`). TESTS:: From 5f7a691c78f34fa815fd904adadbde09041dc6c6 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:02:06 +0200 Subject: [PATCH 188/421] Trac #17693, comment 36, 7: cache keys --- src/sage/data_structures/mutable_poset.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index c9e3d6601ce..399039d0fab 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -259,8 +259,24 @@ def key(self): sage: f = MutablePosetShell(Q, (1, 2)) sage: f.key 1 + + Test the caching of the key:: + + sage: def k(k): + ....: print 'key %s' % (k,) + ....: return k + sage: R = MP(key=k) + sage: h = MutablePosetShell(R, (1, 2)) + sage: h.key; h.key + key (1, 2) + (1, 2) + (1, 2) """ - return self.poset.get_key(self._element_) + # workaround for #19281 + # (Use @property @cached_method once #19281 is fixed.) + if not hasattr(self, '_cached_key_'): + self._cached_key_ = self.poset.get_key(self._element_) + return self._cached_key_ def predecessors(self, reverse=False): From 60699e1f0906057fdd99b2575f91fbfd0403aef8 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:02:34 +0200 Subject: [PATCH 189/421] Trac #17693, comment 36, 8: Add a note on special elements in class description --- src/sage/data_structures/mutable_poset.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 399039d0fab..3e18d497fbe 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -174,6 +174,17 @@ class MutablePosetShell(SageObject): A shell for the given element. + .. NOTE:: + + If the :meth:`element` of a shell is ``None``, then this + element is considered as "special" (see :meth:`is_special`). + There are two special elements, namely + + - a ``'null'`` (an element smaller than each other element; + it has no predecessors) and + - a ``'oo'`` (an element larger than each other element; + it has no successors). + EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP From 402290906f87257b996d8e4e57a0df72af9cf1ff Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:03:47 +0200 Subject: [PATCH 190/421] use _repr_ instead of __repr__ (since we have a SageObject now) --- src/sage/data_structures/mutable_poset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 3e18d497fbe..3fca74ae3a1 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -413,7 +413,7 @@ def is_oo(self): return self.element is None and not self.successors() - def __repr__(self): + def _repr_(self): r""" Return the representation of this shell. @@ -1799,7 +1799,7 @@ def repr_full(self, reverse=False): return '\n'.join(strings) - __repr__ = repr + _repr_ = repr def contains(self, key): From fd292c295f5d6e63737ad0c3657ce3adc4024e4e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:08:43 +0200 Subject: [PATCH 191/421] Trac #17693, comment 36, 9: rewrite repr to use is_null and is_oo --- src/sage/data_structures/mutable_poset.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 3fca74ae3a1..377d7dabef9 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -445,12 +445,12 @@ def _repr_(self): sage: repr(P.oo) # indirect doctest 'oo' """ - if self.element is None: - if not self.predecessors(): - return 'null' - if not self.successors(): - return 'oo' - return repr(self.element) + if self.is_null(): + return 'null' + elif self.is_oo(): + return 'oo' + else: + return repr(self.element) def __hash__(self): From bf7dfa682c5110912caac4abf6252525b74c1e96 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:10:31 +0200 Subject: [PATCH 192/421] Trac #17693, comment 36, 10: simplify note-box --- src/sage/data_structures/mutable_poset.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 377d7dabef9..750a3465da6 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -498,10 +498,7 @@ def le(self, other, reverse=False): The comparison of the shells is based on the comparison of the keys of the elements contained in the shells, - except for the shells containing ``None``. These special - shells are interpreted as smaller or larger than all - other elements, depending on whether they have no - predecessors or no successors, respectively. + except for special shells (see :class:`MutablePosetShell`). TESTS:: From 665692abdab38ce82b2b7786d2d4a96e0ab229e1 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:13:14 +0200 Subject: [PATCH 193/421] Trac #17693, comment 36, 11: rewrite description of ``reverse`` in le --- src/sage/data_structures/mutable_poset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 750a3465da6..21278dc252e 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -488,7 +488,7 @@ def le(self, other, reverse=False): - ``other`` -- a shell. - ``reverse`` -- (default: ``False``) if set, then return - ``right <= left`` instead. + whether this shell is greater than or equal to ``other``. OUTPUT: From 104300bbecc327e8cb2ba86c2e7735330c86e975 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:17:43 +0200 Subject: [PATCH 194/421] Trac #17693, comment 36, 12: simplify code of le --- src/sage/data_structures/mutable_poset.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 21278dc252e..43137c6ffc8 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -563,18 +563,10 @@ def le(self, other, reverse=False): else: # not null, not oo on the right return False - if other.element is None: - if not other.successors(): - # oo on the right - return True - else: - # null on the right - if self.element is None: - # null or oo on the left - return not self.predecessors() - else: - # not null, not oo on the right - return False + elif other.element is None: + # null/oo on the right + return not other.successors() + return self.key <= other.key From bb088bd8b8dc07d7141c33134025f7cbf4a2e295 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:19:02 +0200 Subject: [PATCH 195/421] Trac #17693, comment 36, 13: use _predecessors_, _successors_ in le for performance --- src/sage/data_structures/mutable_poset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 43137c6ffc8..342bee822c8 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -552,20 +552,20 @@ def le(self, other, reverse=False): return other.le(self, reverse=False) if self.element is None: - if not self.predecessors(): + if not self._predecessors_: # null on the left return True else: # oo on the left if other.element is None: # null or oo on the right - return not other.successors() + return not other._successors_ else: # not null, not oo on the right return False elif other.element is None: # null/oo on the right - return not other.successors() + return not other._successors_ return self.key <= other.key From 0369feef049e67d04149f354158e7b99b5319b89 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:21:51 +0200 Subject: [PATCH 196/421] Trac #17693, comment 36, 14+15: simplify/rewrite note-box in eq --- src/sage/data_structures/mutable_poset.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 342bee822c8..8d43f3521d7 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -588,9 +588,8 @@ def eq(self, other): .. NOTE:: This method compares the keys of the elements contained - in the shells, if the elements are not both ``None``. - Otherwise, this method checks whether both shells describe the - same special element. + in the (non-special) shells. In particlar, + elements/shells with the same key are considered as equal. TESTS:: From 899324763f34bf04901a8ff8065add230d1d8238 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:35:04 +0200 Subject: [PATCH 197/421] Trac #17693, comment 36, 17: explain role of poset better in _copy_all_linked_ --- src/sage/data_structures/mutable_poset.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 8d43f3521d7..6c2446bd5b3 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -635,7 +635,10 @@ def _copy_all_linked_(self, memo, poset, mapping): - ``memo`` -- a dictionary which assigns to the id of the calling shell to a copy of it. - - ``poset`` -- the poset to which the newly created shells belongs. + - ``poset`` -- the poset to which the newly created shells + belongs. Note that the elements are not inserted into + ``poset``; this is done in the calling method + :meth:`_copy_shells_`. - ``mapping`` -- a function which is applied on each of the elements. From b8a0a6a400a6a9421e2ccf48ad40bebe32e327db Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 19:37:29 +0200 Subject: [PATCH 198/421] Trac #17693, comment 36, 16: rewrite short-descr of _copy_all_linked_ --- src/sage/data_structures/mutable_poset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 6c2446bd5b3..205cef34c1b 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -625,8 +625,8 @@ def eq(self, other): def _copy_all_linked_(self, memo, poset, mapping): r""" - Return a copy of all shells linked to this shell - (including a copy of this shell). + Return a copy of this shell. All shells linked to this shell + are copied as well. This is a helper function for :meth:`MutablePoset.copy`. From 311c752e4555cd38ca8c8a6cc1c6c8b95167da86 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 20:18:29 +0200 Subject: [PATCH 199/421] Trac #17693, comment 36, 32: Delete not needed line in doctest of iter_topological --- src/sage/data_structures/mutable_poset.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 205cef34c1b..9d2b8146fca 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1025,8 +1025,6 @@ def iter_topological(self, reverse=False, key=None, condition=None): :: - sage: def C(shell): - ....: return shell.element[0] == 1 sage: list(P.null.iter_topological( ....: reverse=True, condition=lambda s: s.element[0] == 1)) [(1, 3), (1, 2), (1, 1), null] From d0af5fa4e3b44d21f1a0cd4b5459d5d217c14bc5 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 20:24:48 +0200 Subject: [PATCH 200/421] Trac #17693, comment 36, 36: document merge-function None in merge --- src/sage/data_structures/mutable_poset.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 9d2b8146fca..26585da387d 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1053,6 +1053,15 @@ def merge(self, element, check=True, delete=True): Nothing. + .. NOTE:: + + This method uses the parameters ``merge`` and + ``can_merge`` of the :class:`MutablePoset` which contains + this shell. + + If the ``merge`` function returns ``None``, then this shell + is removed from the poset. + EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP From 9433564213a8a699814106c375b4cc0a5ef22f71 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 24 Sep 2015 20:24:52 +0200 Subject: [PATCH 201/421] trac #19224: Additional doctests --- src/sage/graphs/strongly_regular_db.pyx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 77b8983ab66..49ae296e540 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -995,8 +995,12 @@ def is_switch_OA_srg(int v, int k, int l, int mu): sage: from sage.graphs.strongly_regular_db import is_switch_OA_srg sage: t = is_switch_OA_srg(5,5,5,5); t - sage: is_switch_OA_srg(290, 136, 63, 64) - (.switch_OA_srg at ..., 8, 17) + sage: t = is_switch_OA_srg(170, 78, 35, 36); + sage: t[0](*t[1:]).is_strongly_regular(parameters=True) + (170, 78, 35, 36) + sage: t = is_switch_OA_srg(290, 136, 63, 64); + sage: t[0](*t[1:]).is_strongly_regular(parameters=True) + (290, 136, 63, 64) sage: is_switch_OA_srg(626, 300, 143, 144) (.switch_OA_srg at ..., 12, 25) sage: is_switch_OA_srg(842, 406, 195, 196) From 0855f60975543f2243bd8b0aff73dd6fbe2b9f40 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 20:27:29 +0200 Subject: [PATCH 202/421] Trac #17693, comment 36, 39: document that null and oo are not counted in __len__ --- src/sage/data_structures/mutable_poset.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 26585da387d..8b16a749a2f 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1288,6 +1288,10 @@ def __len__(self): An integer. + .. NOTE:: + + The special elements ``'null'`` and ``'oo'`` are not counted. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP From a959efd2bfcea9c3a76f02b44120153258212788 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 20:30:13 +0200 Subject: [PATCH 203/421] Trac #17693, comment 36, 40: rewrite key-parameter description in shells_topological --- src/sage/data_structures/mutable_poset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 8b16a749a2f..6b58831fa14 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1556,9 +1556,9 @@ def shells_topological(self, include_special=False, ``True`` gives largest first. - ``key`` -- (default: ``None``) a function used for sorting - the direct successors of a shell (used in case of a - tie). If this is ``None``, no sorting according to the - representation string occurs. + the direct successors of a shell (used in case of a tie). If + this is ``None``, then the successors are sorted according + to their representation strings. OUTPUT: From e1784af0405e43a8c9a3cb776a4536a024335e81 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 20:32:48 +0200 Subject: [PATCH 204/421] Trac #17693, comment 36, 41: remove some poset elements in doctest since not needed --- src/sage/data_structures/mutable_poset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 6b58831fa14..452af533600 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1716,7 +1716,7 @@ def keys_topological(self, **kwargs): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP([(1, 1), (1, 3), (2, 1), (4, 4), (1, 2), (2, 2)], + sage: P = MP([(1, 1), (2, 1), (4, 4)], ....: key=lambda c: c[0]) sage: [(v, type(v)) for v in P.keys_topological()] [(1, ), From ccb337830ac6004d551e660f582466ffd8afb823 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 24 Sep 2015 20:36:19 +0200 Subject: [PATCH 205/421] Trac #17693, comment 36, 42+43: complete INPUT blocks of repr* --- src/sage/data_structures/mutable_poset.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 452af533600..727ebf2c532 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1741,7 +1741,12 @@ def repr(self, include_special=False, reverse=False): INPUT: - Nothing. + - ``include_special`` -- (default: ``False``) a boolean + indicating whether to include the special elements + ``'null'`` and ``'oo'`` or not. + + - ``reverse`` -- (default: ``False``) a boolean. If set, then + largest elements are displayed first. OUTPUT: @@ -1766,7 +1771,8 @@ def repr_full(self, reverse=False): INPUT: - Nothing. + - ``reverse`` -- (default: ``False``) a boolean. If set, then + largest elements are displayed first. OUTPUT: From c26038e543830f388cdceafb139d33e5f28359cd Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 24 Sep 2015 20:39:03 +0200 Subject: [PATCH 206/421] trac #19279: Different definition --- .../combinat/designs/incidence_structures.py | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 35ed5b0f068..3373ddb06b9 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -1640,21 +1640,23 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): ll = b return (True, (tt,v,k,ll)) if return_parameters else True - def is_generalized_quadrangle(self, verbose=False): + def is_generalized_quadrangle(self, verbose=False, parameters=False): r""" Test if the incidence structure is a generalized quadrangle. An incidence structure is a generalized quadrangle iif: - - it is `s+1`-:meth:`uniform ` for some positive integer `s`. - - - it is `t+1`-:meth:`regular ` for some positive integer `t`. - - two blocks intersect on at most one point. - For every point `p` not in a block `B`, there is a unique block `B'` intersecting both `\{p\}` and `B` + It is a *regular* generalized quadrangle if furthermore: + + - it is `s+1`-:meth:`uniform ` for some positive integer `s`. + + - it is `t+1`-:meth:`regular ` for some positive integer `t`. + For more information, see the :wikipedia:`Generalized_quadrangle`. INPUT: @@ -1662,23 +1664,23 @@ def is_generalized_quadrangle(self, verbose=False): - ``verbose`` (boolean) -- whether to print an explanation when the instance is not a generalized quadrangle. + - ``parameters`` (boolean; ``False``) -- if set to ``True``, the + function returns a pair ``(s,t)`` instead of ``True`` answers. In this + case, `s` and `t` are the integers defined above if they exist (each + can be set to ``False`` otherwise). + EXAMPLE:: sage: h = designs.CremonaRichmondConfiguration() sage: h.is_generalized_quadrangle() True - TESTS:: + This is actually a *regular* generalized quadrangle:: - sage: H = IncidenceStructure([[1,2],[3,4,5]]) - sage: H.is_generalized_quadrangle(verbose=True) - The incidence structure is not (s+1)-uniform for some s>0. - False + sage: h.is_generalized_quadrangle(parameters=True) + (2, 2) - sage: H = IncidenceStructure([[1,2,3],[3,4,5]]) - sage: H.is_generalized_quadrangle(verbose=True) - The incidence structure is not (t+1)-regular for some t>0. - False + TESTS:: sage: H = IncidenceStructure((2*graphs.CompleteGraph(3)).edges(labels=False)) sage: H.is_generalized_quadrangle(verbose=True) @@ -1696,18 +1698,6 @@ def is_generalized_quadrangle(self, verbose=False): Some point has two projections on some line. False """ - s = self.is_uniform()-1 - if not s or s <= 0: - if verbose: - print "The incidence structure is not (s+1)-uniform for some s>0." - return False - - t = self.is_regular()-1 - if not t or t <= 0: - if verbose: - print "The incidence structure is not (t+1)-regular for some t>0." - return False - # The distance between a point and a line in the incidence graph is odd # and must be <= 3. Thus, the diameter is at most 4 g = self.incidence_graph() @@ -1727,7 +1717,17 @@ def is_generalized_quadrangle(self, verbose=False): if verbose: print "Some point has two projections on some line." return False - return True + + if parameters: + s = self.is_uniform() + t = self.is_regular() + s = s-1 if (s is not False and s>=2) else False + t = t-1 if (t is not False and t>=2) else False + return (s,t) + else: + return True + + def dual(self, algorithm=None): """ From 58cd46270c1ac5dc4413a9e6efe3c7aabe83f5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 24 Sep 2015 21:59:33 +0300 Subject: [PATCH 207/421] Added function linear_extensions_graph. --- src/sage/combinat/posets/posets.py | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 58245aa7aca..fa799e57250 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -4456,6 +4456,50 @@ def incomparability_graph(self): G.rename('Incomparability graph on %s vertices' % self.cardinality()) return G + def linear_extensions_graph(self): + r""" + Return the linear extensions graph of the poset. + + Vertices of the graph are linear extensions of the poset. + Two vertices are connected by an edge if the linear extensions + differ by only one adjacent transposition. + + EXAMPLES:: + + sage: P = Poset({1:[3,4],2:[4]}) + sage: G = P.linear_extensions_graph(); G + Graph on 5 vertices + sage: G.degree_sequence() + [3, 2, 2, 2, 1] + + sage: chevron = Poset({1:[2,6], 2:[3], 4:[3,5], 6:[5]}) + sage: G = chevron.linear_extensions_graph(); G + Graph on 22 vertices + sage: G.size() + 36 + + TESTS:: + + sage: Poset().linear_extensions_graph() + Graph on 1 vertex + + sage: A4 = Posets.AntichainPoset(4) + sage: G = A4.linear_extensions_graph() + sage: G.is_regular() + True + """ + from sage.graphs.graph import Graph + # Direct implementation, no optimizations + L = self.linear_extensions() + G = Graph() + G.add_vertices(L) + for i in range(len(L)): + for j in range(i): + tmp = map(lambda x,y: x != y, L[i], L[j]) + if tmp.count(True) == 2 and tmp[tmp.index(True)+1]: + G.add_edge(L[i], L[j]) + return G + def maximal_antichains(self): """ Return all maximal antichains of the poset. From 93346eaa75889e5bb189a5b29ccbc727474ae708 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Thu, 24 Sep 2015 14:03:45 -0500 Subject: [PATCH 208/421] better test case --- src/sage/matroids/matroid.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 2a6d7f3d92f..8405b7fe937 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -6731,8 +6731,7 @@ cdef class Matroid(SageObject): True sage: sum(map(len,P))==len(M.groundset()) True - sage: from sage.matroids.advanced import * - sage: BasisMatroid().partition() + sage: Matroid(matrix([])).partition() [] ALGORITHM: From c3e75dc8c995b8a1454a1f109b081f8c45df0eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 24 Sep 2015 22:22:07 +0200 Subject: [PATCH 209/421] octave vector parser --- src/sage/interfaces/octave.py | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/sage/interfaces/octave.py b/src/sage/interfaces/octave.py index df08d363fe9..fc9bd0741d3 100644 --- a/src/sage/interfaces/octave.py +++ b/src/sage/interfaces/octave.py @@ -524,15 +524,30 @@ def _matrix_(self, R): [3.00000000000000 4.50000000000000] """ from sage.matrix.all import MatrixSpace - s = str(self).strip() - v = s.split('\n ') - nrows = len(v) - if nrows == 0: - return MatrixSpace(R,0,0)(0) - ncols = len(v[0].split()) - M = MatrixSpace(R, nrows, ncols) - v = sum([[x for x in w.split()] for w in v], []) - return M(v) + s = str(self).strip('\n ') + w = [u.strip().split(' ') for u in s.split('\n')] + nrows = len(w) + ncols = len(w[0]) + return MatrixSpace(R, nrows, ncols)(w) + + def _vector_(self, R): + r""" + Return Sage vector from this octave element. + + EXAMPLES:: + + sage: A = octave('[1,2,3,4]') # optional - octave + sage: vector(ZZ, A) # optional - octave + [1 2 3 4] + sage: A = octave('[1,2.3,4.5]') # optional - octave + sage: vector(RR, A) # optional - octave + [1.00000000000000 2.30000000000000 4.50000000000000] + """ + from sage.modules.free_module import FreeModule + s = str(self).strip('\n ') + w = s.strip().split(' ') + nrows = len(w) + return FreeModule(R, nrows)(w) # An instance From 2cd19ba5eee863d7224c28fceb7db998548b904a Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 24 Sep 2015 17:05:17 -0700 Subject: [PATCH 210/421] added a test, revised comments --- .../root_system/integrable_representations.py | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/root_system/integrable_representations.py b/src/sage/combinat/root_system/integrable_representations.py index 38a0fcc6023..2942f936480 100644 --- a/src/sage/combinat/root_system/integrable_representations.py +++ b/src/sage/combinat/root_system/integrable_representations.py @@ -5,7 +5,7 @@ #***************************************************************************** # Copyright (C) 2014, 2105 Daniel Bump # Travis Scrimshaw -# Twisted Affine case: Valentin Buciumas +# Valentin Buciumas # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ @@ -152,8 +152,11 @@ class IntegrableRepresentation(CategoryObject, UniqueRepresentation): 2*Lambda[1] - delta: 1 4 15 44 122 304 721 1612 3469 7176 14414 28124 2*Lambda[2] - 2*delta: 2 7 26 72 194 467 1084 2367 5010 10191 20198 38907 - Two examples for twisted affine types:: + Examples for twisted affine types:: + sage: Lambda = RootSystem(["A",2,2]).weight_lattice(extended=True).fundamental_weights() + sage: IntegrableRepresentation(Lambda[0]).strings() + {Lambda[0]: [1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56]} sage: Lambda = RootSystem(['G',2,1]).dual.weight_lattice(extended=true).fundamental_weights() sage: V = IntegrableRepresentation(Lambda[0]+Lambda[1]+Lambda[2]) sage: V.print_strings() # long time @@ -495,8 +498,7 @@ def _from_weight_helper(self, mu, check=False): """ mu = self._P(mu) zero = ZZ.zero() - n0 = mu.monomial_coefficients().get('delta', zero) - mu0 = mu - n0 * self._P.simple_root(self._cartan_type.special_node()) + n0 = mu.monomial_coefficients().get('delta', zero) mu0 = mu - n0 * self._P.simple_root(self._cartan_type.special_node()) ret = [n0] # This should be in ZZ because it is in the weight lattice mc_mu0 = mu0.monomial_coefficients() for ii, i in enumerate(self._index_set_classical): @@ -618,7 +620,7 @@ def _freudenthal_roots_real(self, nu): Return the set of real positive roots `\alpha \in \Delta^+` in ``self`` such that `\nu - \alpha \in Q^+`. - See [Kac] Proposition 6.3 for the way to compute the set of real roots for twisted affine case. + See [Kac]_ Proposition 6.3 for the way to compute the set of real roots for twisted affine case. INPUT: @@ -641,9 +643,6 @@ def _freudenthal_roots_real(self, nu): for al in self._classical_positive_roots: if all(x >= 0 for x in self._from_weight_helper(nu-al)): ret.append(al) - """ - changed the way you compute the set of real roots for twisted affine case, see [Kac] page 83 - """ from sage.combinat.root_system.cartan_type import CartanType if self._cartan_type == CartanType(['B',self._classical_rank,1]).dual() or self._cartan_type == CartanType(['C',self._classical_rank,1]).dual() or self._cartan_type == CartanType(['F',4,1]).dual(): #case A^2_{2l-1} or case D^2_{l+1} or case E^2_6: for al in self._classical_roots: @@ -670,7 +669,7 @@ def _freudenthal_roots_real(self, nu): if sum([val*alpha[i] for i,val in enumerate(n)]) not in ret: ret.append(sum([val*alpha[i] for i,val in enumerate(n)])) - elif self._cartan_type == CartanType(['D',4,3]) or self._cartan_type == CartanType(['G',2,1]).dual(): # case D^3_4 in the Kac/Stembridge notation + elif self._cartan_type == CartanType(['D',4,3]) or self._cartan_type == CartanType(['G',2,1]).dual(): # case D^3_4 in the Kac notation for al in self._classical_roots: if self._inner_qq(al,al) == 2: #if al is a short root: for ir in self._freudenthal_roots_imaginary(nu-al): @@ -723,8 +722,8 @@ def _m_freudenthal(self, n): Compute the weight multiplicity using the Freudenthal multiplicity formula in ``self``. The multiplicities of the imaginary roots for the twisted - affine case are different than those for the untwisted case, - see [Carter]_ Corollary 18.10 for general type and Corollary + affine case are different than those for the untwisted case. + See [Carter]_ Corollary 18.10 for general type and Corollary 18.15 for `A^2_{2l}` EXAMPLES:: @@ -756,7 +755,6 @@ def _m_freudenthal(self, n): for k in dict: # k is a positive number, dict[k] is k*delta num += (self._classical_rank-k%2) * self._freudenthal_accum(mu, dict[k]) - elif self._cartan_type == CartanType(['BC',self._classical_rank,2]): for al in self._freudenthal_roots_imaginary(self._Lam - mu): num += self._classical_rank * self._freudenthal_accum(mu, al) @@ -786,7 +784,6 @@ def _m_freudenthal(self, n): for al in self._freudenthal_roots_imaginary(self._Lam - mu): num += self._classical_rank * self._freudenthal_accum(mu, al) - try: return ZZ(num / den) except TypeError: From 513ea7bfa133b5677d75396fcd41c6a5efd133a1 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 24 Sep 2015 17:18:21 -0700 Subject: [PATCH 211/421] undid accidental reversion of #18246 --- src/sage/combinat/root_system/integrable_representations.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/root_system/integrable_representations.py b/src/sage/combinat/root_system/integrable_representations.py index 2942f936480..48e7cc24d0a 100644 --- a/src/sage/combinat/root_system/integrable_representations.py +++ b/src/sage/combinat/root_system/integrable_representations.py @@ -21,7 +21,7 @@ from sage.combinat.root_system.weyl_characters import WeylCharacterRing # TODO: Make this a proper parent and implement actions -class IntegrableRepresentation(CategoryObject, UniqueRepresentation): +class IntegrableRepresentation(UniqueRepresentation, CategoryObject): r""" An irreducible integrable highest weight representation of an affine Lie algebra. @@ -498,7 +498,8 @@ def _from_weight_helper(self, mu, check=False): """ mu = self._P(mu) zero = ZZ.zero() - n0 = mu.monomial_coefficients().get('delta', zero) mu0 = mu - n0 * self._P.simple_root(self._cartan_type.special_node()) + n0 = mu.monomial_coefficients().get('delta', zero) + mu0 = mu - n0 * self._P.simple_root(self._cartan_type.special_node()) ret = [n0] # This should be in ZZ because it is in the weight lattice mc_mu0 = mu0.monomial_coefficients() for ii, i in enumerate(self._index_set_classical): From 02a7a7653653a8c4c44ec936aa962c514b5ecce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Fri, 25 Sep 2015 12:27:15 +1200 Subject: [PATCH 212/421] More robust test for networkx --- src/sage/graphs/generic_graph.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 4c6305adc27..98e6597a6cf 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -14559,10 +14559,10 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, [4, 17, 16, 12, 13, 9] sage: D.shortest_path(4, 9, algorithm='BFS') [4, 3, 2, 1, 8, 9] - sage: D.shortest_path(4, 9, algorithm='Dijkstra_NetworkX') - [4, 17, 16, 12, 13, 9] - sage: D.shortest_path(4, 9, algorithm='Dijkstra_Bid_NetworkX') - [4, 17, 16, 12, 13, 9] + sage: D.shortest_path(4, 8, algorithm='Dijkstra_NetworkX') + [4, 3, 2, 1, 8] + sage: D.shortest_path(4, 8, algorithm='Dijkstra_Bid_NetworkX') + [4, 3, 2, 1, 8] sage: D.shortest_path(4, 9, algorithm='Dijkstra_Bid') [4, 3, 19, 0, 10, 9] sage: D.shortest_path(5, 5) From 6a86e239c68f0d66cc182a98e74f72b34df4f961 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 25 Sep 2015 11:58:15 +0200 Subject: [PATCH 213/421] trac #19291: Graph.spanning_trees does not like loops --- src/sage/graphs/graph.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 5d11da8a6a1..6f5ca77eaa3 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1868,6 +1868,19 @@ def spanning_trees(self): - :meth:`~sage.graphs.graph.Graph.random_spanning_tree` -- returns a random spanning tree. + TESTS: + + Works with looped graphs:: + + sage: g = Graph({i:[i,(i+1)%6] for i in range(6)}) + sage: g.spanning_trees() + [Graph on 6 vertices, + Graph on 6 vertices, + Graph on 6 vertices, + Graph on 6 vertices, + Graph on 6 vertices, + Graph on 6 vertices] + REFERENCES: .. [RT75] Read, R. C. and Tarjan, R. E. @@ -1886,7 +1899,7 @@ def _recursive_spanning_trees(G,forest): return [forest.copy()] else: # Pick an edge e from G-forest - for e in G.edges(): + for e in G.edge_iterator(labels=False): if not forest.has_edge(e): break @@ -1919,7 +1932,7 @@ def _recursive_spanning_trees(G,forest): forest = Graph([]) forest.add_vertices(self.vertices()) forest.add_edges(self.bridges()) - return _recursive_spanning_trees(self, forest) + return _recursive_spanning_trees(Graph(self,immutable=False,loops=False), forest) else: return [] From b597ceb52fed43f6e62a246b977a1f0322b09892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 25 Sep 2015 13:28:14 +0200 Subject: [PATCH 214/421] trac #19284 adding equality symbol to octave interface, plus doc details --- src/sage/interfaces/octave.py | 50 ++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/sage/interfaces/octave.py b/src/sage/interfaces/octave.py index fc9bd0741d3..b26c4fe617b 100644 --- a/src/sage/interfaces/octave.py +++ b/src/sage/interfaces/octave.py @@ -94,9 +94,8 @@ sage: a = octave.eval(t + ';') # optional - octave, < 1/100th of a second sage: a = octave(t) # optional - octave -Note that actually reading a back out takes forever. This *must* -be fixed ASAP - see -http://trac.sagemath.org/sage_trac/ticket/940/. +Note that actually reading ``a`` back out takes forever. This *must* +be fixed as soon as possible, see :trac:`940`. Tutorial -------- @@ -286,7 +285,7 @@ def quit(self, verbose=False): # to signals. if not self._expect is None: if verbose: - print "Exiting spawned %s process."%self + print "Exiting spawned %s process." % self return def _start(self): @@ -308,9 +307,38 @@ def _start(self): # set random seed self.set_seed(self._seed) + def _equality_symbol(self): + """ + EXAMPLES:: + + sage: octave('0 == 1') + 0 + sage: octave('1 == 1') + 1 + """ + return '==' + + def _true_symbol(self): + """ + EXAMPLES:: + + sage: octave('1 == 1') + 1 + """ + return '1' + + def _false_symbol(self): + """ + EXAMPLES:: + + sage: octave('0 == 1') + 0 + """ + return '0' + def set(self, var, value): """ - Set the variable var to the given value. + Set the variable ``var`` to the given ``value``. EXAMPLES:: @@ -325,7 +353,7 @@ def set(self, var, value): def get(self, var): """ - Get the value of the variable var. + Get the value of the variable ``var``. EXAMPLES:: @@ -387,18 +415,16 @@ def version(self): return octave_version() def solve_linear_system(self, A, b): - """ + r""" Use octave to compute a solution x to A\*x = b, as a list. INPUT: + - ``A`` -- mxn matrix A with entries in `\QQ` or `\RR` - - ``A`` - mxn matrix A with entries in QQ or RR - - - ``b`` - m-vector b entries in QQ or RR (resp) - + - ``b`` -- m-vector b entries in `\QQ` or `\RR` (resp) - OUTPUT: An list x (if it exists) which solves M\*x = b + OUTPUT: A list x (if it exists) which solves M\*x = b EXAMPLES:: From fe6f95c764bbc69663ac6a68cc92723426ec40d2 Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Fri, 25 Sep 2015 15:55:49 +0200 Subject: [PATCH 215/421] #17190: Authorize empty dictionary in initialization of polynomial over RR --- .../polynomial/polynomial_real_mpfr_dense.pyx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx index 9e10335ee2c..a0986cbedb5 100644 --- a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +++ b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx @@ -68,7 +68,7 @@ cdef class PolynomialRealDense(Polynomial): TESTS: - Check that errors and interrupts are handled properly (see #10100):: + Check that errors and interrupts are handled properly (see :trac:`10100`):: sage: a = var('a') sage: PolynomialRealDense(RR['x'], [1,a]) @@ -83,7 +83,7 @@ cdef class PolynomialRealDense(Polynomial): sage: sig_on_count() 0 - Test that we don't clean up uninitialized coefficients (#9826):: + Test that we don't clean up uninitialized coefficients (:trac:`9826`):: sage: k. = GF(7^3) sage: P. = PolynomialRing(k) @@ -91,6 +91,10 @@ cdef class PolynomialRealDense(Polynomial): Traceback (most recent call last): ... TypeError: Unable to convert x (='a') to real number. + + Check that :trac:`17190` is fixed:: + sage: RR['x']({}) + 0 """ Polynomial.__init__(self, parent, is_gen=is_gen) self._base_ring = parent._base @@ -108,11 +112,7 @@ cdef class PolynomialRealDense(Polynomial): elif isinstance(x, (int, float, Integer, Rational, RealNumber)): x = [x] elif isinstance(x, dict): - degree = max(x.keys()) - c = [0] * (degree+1) - for i, a in x.items(): - c[i] = a - x = c + x = self._dict_to_list(x,self._base_ring.zero()) elif isinstance(x, pari_gen): x = [self._base_ring(w) for w in x.list()] elif not isinstance(x, list): @@ -665,9 +665,9 @@ cdef class PolynomialRealDense(Polynomial): sage: f = PolynomialRealDense(RR['x']) sage: f(12) 0.000000000000000 - + TESTS:: - + sage: R. = RR[] # :trac:`17311` sage: (x^2+1)(x=5) 26.0000000000000 @@ -676,13 +676,13 @@ cdef class PolynomialRealDense(Polynomial): xx = args[0] else: return Polynomial.__call__(self, *args, **kwds) - + if not isinstance(xx, RealNumber): if self._base_ring.has_coerce_map_from(parent(xx)): xx = self._base_ring(xx) else: return Polynomial.__call__(self, xx) - + cdef Py_ssize_t i cdef mp_rnd_t rnd = self._base_ring.rnd cdef RealNumber x = xx @@ -713,7 +713,7 @@ cdef class PolynomialRealDense(Polynomial): mpfr_mul(res.value, res.value, x.value, rnd) mpfr_add(res.value, res.value, self._coeffs[i], rnd) return res - + def change_ring(self, R): """ EXAMPLES:: From 6315d74ebf51bc82c26d6dafd389f52d895c8d78 Mon Sep 17 00:00:00 2001 From: Bruno Grenet Date: Fri, 25 Sep 2015 16:29:53 +0200 Subject: [PATCH 216/421] #17190: Add missing blank line --- src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx index a0986cbedb5..46c8e1dcbfc 100644 --- a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +++ b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx @@ -93,6 +93,7 @@ cdef class PolynomialRealDense(Polynomial): TypeError: Unable to convert x (='a') to real number. Check that :trac:`17190` is fixed:: + sage: RR['x']({}) 0 """ From 7a3c068eef3d401baba5913a4842def688a411b4 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 25 Sep 2015 11:27:57 -0500 Subject: [PATCH 217/421] Fixing doctest for coxeter3. --- src/sage/combinat/kazhdan_lusztig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/kazhdan_lusztig.py b/src/sage/combinat/kazhdan_lusztig.py index 5a7dc5e62df..915adcde9f9 100644 --- a/src/sage/combinat/kazhdan_lusztig.py +++ b/src/sage/combinat/kazhdan_lusztig.py @@ -66,7 +66,7 @@ class KazhdanLusztigPolynomial(UniqueRepresentation, SageObject): sage: W = CoxeterGroup(['B', 3], implementation='coxeter3') # optional - coxeter3 sage: W.kazhdan_lusztig_polynomial([2], [3,2,3,1,2]) # optional - coxeter3 - 1 + q + q + 1 """ def __init__(self, W, q, trace=False): """ From 0780530a7a7457e13f0cdd1f6ff006782466f0cf Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 25 Sep 2015 11:59:25 -0700 Subject: [PATCH 218/421] TL;DR: abbrvs now dead --- .../graphs/generators/classical_geometries.py | 20 +++++++++---------- src/sage/graphs/graph_generators.py | 8 ++++---- src/sage/graphs/strongly_regular_db.pyx | 20 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index f7e92ae13d5..3081a3eadc3 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -936,7 +936,7 @@ def TaylorTwographSRG(q): G.name("Taylor two-graph SRG") return G -def AhrensSzekeresGQGraph(q, dual=False): +def AhrensSzekeresGeneralizedQuadrangleGraph(q, dual=False): r""" Return the collinearity graph of the generalized quadrangle `AS(q)`, or of its dual @@ -959,11 +959,11 @@ def AhrensSzekeresGQGraph(q, dual=False): EXAMPLES:: - sage: g=graphs.AhrensSzekeresGQGraph(5); g + sage: g=graphs.AhrensSzekeresGeneralizedQuadrangleGraph(5); g AS(5); GQ(4, 6): Graph on 125 vertices sage: g.is_strongly_regular(parameters=True) (125, 28, 3, 7) - sage: g=graphs.AhrensSzekeresGQGraph(5,dual=True); g + sage: g=graphs.AhrensSzekeresGeneralizedQuadrangleGraph(5,dual=True); g AS(5)*; GQ(6, 4): Graph on 175 vertices sage: g.is_strongly_regular(parameters=True) (175, 30, 5, 5) @@ -998,11 +998,11 @@ def AhrensSzekeresGQGraph(q, dual=False): G.name('AS('+str(q)+'); GQ'+str((q-1,q+1))) return G -def T2starGQGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): +def T2starGeneralizedQuadrangleGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): r""" Return the collinearity graph of the generalized quadrangle `T_2^*(q)`, or of its dual - Let `q=2^k` and `\Theta=PG(3,q)`. `T_2^*(q)` is a generalised quadrangle [GQwiki]_ + Let `q=2^k` and `\Theta=PG(3,q)`. `T_2^*(q)` is a generalized quadrangle [GQwiki]_ of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. Fix a plane `\Pi \subset \Theta` and a `hyperoval `__ `O \subset \Pi`. The points of `T_2^*(q):=T_2^*(O)` are the points of `\Theta` @@ -1035,11 +1035,11 @@ def T2starGQGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=Tru using the built-in construction:: - sage: g=graphs.T2starGQGraph(4); g + sage: g=graphs.T2starGeneralizedQuadrangleGraph(4); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 18, 2, 6) - sage: g=graphs.T2starGQGraph(4,dual=True); g + sage: g=graphs.T2starGeneralizedQuadrangleGraph(4,dual=True); g T2*(O,4)*; GQ(5, 3): Graph on 96 vertices sage: g.is_strongly_regular(parameters=True) (96, 20, 4, 4) @@ -1048,7 +1048,7 @@ def T2starGQGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=Tru sage: F=GF(4,'b') sage: O=[vector(F,(0,0,0,1)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) - sage: g=graphs.T2starGQGraph(4, hyperoval=O, field=F); g + sage: g=graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 18, 2, 6) @@ -1057,12 +1057,12 @@ def T2starGQGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=Tru sage: F=GF(4,'b') # repeating a point... sage: O=[vector(F,(0,1,0,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) - sage: graphs.T2starGQGraph(4, hyperoval=O, field=F) + sage: graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval size sage: O=[vector(F,(0,1,1,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) - sage: graphs.T2starGQGraph(4, hyperoval=O, field=F) + sage: graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 25c012b0bd2..3db5bb3f4da 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -235,7 +235,7 @@ def __append_to_doc(methods): __append_to_doc( ["AffineOrthogonalPolarGraph", - "AhrensSzekeresGQGraph", + "AhrensSzekeresGeneralizedQuadrangleGraph", "NonisotropicOrthogonalPolarGraph", "NonisotropicUnitaryPolarGraph", "OrthogonalPolarGraph", @@ -243,7 +243,7 @@ def __append_to_doc(methods): "SymplecticPolarGraph", "TaylorTwographDescendantSRG", "TaylorTwographSRG", - "T2starGQGraph", + "T2starGeneralizedQuadrangleGraph", "UnitaryDualPolarGraph", "UnitaryPolarGraph"]) @@ -1999,7 +1999,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None ########################################################################### import sage.graphs.generators.classical_geometries AffineOrthogonalPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.AffineOrthogonalPolarGraph) - AhrensSzekeresGQGraph = staticmethod(sage.graphs.generators.classical_geometries.AhrensSzekeresGQGraph) + AhrensSzekeresGeneralizedQuadrangleGraph = staticmethod(sage.graphs.generators.classical_geometries.AhrensSzekeresGeneralizedQuadrangleGraph) NonisotropicOrthogonalPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.NonisotropicOrthogonalPolarGraph) NonisotropicUnitaryPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.NonisotropicUnitaryPolarGraph) OrthogonalPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.OrthogonalPolarGraph) @@ -2009,7 +2009,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None TaylorTwographDescendantSRG = \ staticmethod(sage.graphs.generators.classical_geometries.TaylorTwographDescendantSRG) TaylorTwographSRG = staticmethod(sage.graphs.generators.classical_geometries.TaylorTwographSRG) - T2starGQGraph = staticmethod(sage.graphs.generators.classical_geometries.T2starGQGraph) + T2starGeneralizedQuadrangleGraph = staticmethod(sage.graphs.generators.classical_geometries.T2starGeneralizedQuadrangleGraph) UnitaryDualPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.UnitaryDualPolarGraph) UnitaryPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.UnitaryPolarGraph) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 3006c7f827e..b1019c1d265 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -876,23 +876,23 @@ def is_GQqmqp(int v,int k,int l,int mu): sage: from sage.graphs.strongly_regular_db import is_GQqmqp sage: t = is_GQqmqp(27,10,1,5); t - (, 3, False) + (, 3, False) sage: g = t[0](*t[1:]); g AS(3); GQ(2, 4): Graph on 27 vertices sage: t = is_GQqmqp(45,12,3,3); t - (, 3, True) + (, 3, True) sage: g = t[0](*t[1:]); g AS(3)*; GQ(4, 2): Graph on 45 vertices sage: g.is_strongly_regular(parameters=True) (45, 12, 3, 3) sage: t = is_GQqmqp(16,6,2,2); t - (, 2, True) + (, 2, True) sage: g = t[0](*t[1:]); g T2*(O,2)*; GQ(3, 1): Graph on 16 vertices sage: g.is_strongly_regular(parameters=True) (16, 6, 2, 2) sage: t = is_GQqmqp(64,18,2,6); t - (, 4, False) + (, 4, False) sage: g = t[0](*t[1:]); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) @@ -902,16 +902,16 @@ def is_GQqmqp(int v,int k,int l,int mu): sage: (S,T)=(127,129) sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t - (, 128, False) + (, 128, False) sage: (S,T)=(129,127) sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t - (, 128, True) + (, 128, True) sage: (S,T)=(124,126) sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t - (, 125, False) + (, 125, False) sage: (S,T)=(126,124) sage: t = is_GQqmqp((S+1)*(S*T+1), S*(T+1), S-1, T+1); t - (, 125, True) + (, 125, True) sage: t = is_GQqmqp(5,5,5,5); t """ # do we have GQ(s,t)? we must have mu=t+1, s=l+1, @@ -926,10 +926,10 @@ def is_GQqmqp(int v,int k,int l,int mu): (S+T)/2 == q): if p % 2 == 0: from sage.graphs.generators.classical_geometries\ - import T2starGQGraph as F + import T2starGeneralizedQuadrangleGraph as F else: from sage.graphs.generators.classical_geometries\ - import AhrensSzekeresGQGraph as F + import AhrensSzekeresGeneralizedQuadrangleGraph as F if (S,T) == (q-1, q+1): return (F, q, False) elif (S,T) == (q+1, q-1): From 371febf3f0365ae956325f4e651e81ac6041d85e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 25 Sep 2015 21:05:18 +0200 Subject: [PATCH 219/421] trac #19284, check vector and matrix type, also trying to make a basic parser --- src/sage/interfaces/octave.py | 97 +++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/src/sage/interfaces/octave.py b/src/sage/interfaces/octave.py index b26c4fe617b..47da9f0ce15 100644 --- a/src/sage/interfaces/octave.py +++ b/src/sage/interfaces/octave.py @@ -311,9 +311,9 @@ def _equality_symbol(self): """ EXAMPLES:: - sage: octave('0 == 1') + sage: octave('0 == 1') # optional - octave 0 - sage: octave('1 == 1') + sage: octave('1 == 1') # optional - octave 1 """ return '==' @@ -322,7 +322,7 @@ def _true_symbol(self): """ EXAMPLES:: - sage: octave('1 == 1') + sage: octave('1 == 1') # optional - octave 1 """ return '1' @@ -331,7 +331,7 @@ def _false_symbol(self): """ EXAMPLES:: - sage: octave('0 == 1') + sage: octave('0 == 1') # optional - octave 0 """ return '0' @@ -550,6 +550,9 @@ def _matrix_(self, R): [3.00000000000000 4.50000000000000] """ from sage.matrix.all import MatrixSpace + oc = self.parent() + if not oc('ismatrix(%s)' % self): + raise TypeError('not an octave matrix') s = str(self).strip('\n ') w = [u.strip().split(' ') for u in s.split('\n')] nrows = len(w) @@ -570,11 +573,97 @@ def _vector_(self, R): [1.00000000000000 2.30000000000000 4.50000000000000] """ from sage.modules.free_module import FreeModule + oc = self.parent() + if not oc('isvector(%s)' % self): + raise TypeError('not an octave vector') s = str(self).strip('\n ') w = s.strip().split(' ') nrows = len(w) return FreeModule(R, nrows)(w) + def _scalar_(self, find_parent=False): + """ + Return Sage scalar from this octave element. + + INPUT: + + - find_parent -- boolean (default ``False``). If ``True`` also return + the ring to which the scalar belongs. + + EXAMPLES:: + + sage: A = octave('2833') # optional - octave + sage: As = A.sage(); As # optional - octave + 2833 + sage: As.parent() # optional - octave + Integer Ring + sage: B = sqrt(A) # optional - octave + sage: Bs = B.sage(); Bs # optional - octave + 53.2259 + sage: Bs.parent() # optional - octave + Real Field with 53 bits of precision + sage: C = sqrt(-A) # optional - octave + sage: Cs = C.sage(); Cs # optional - octave + 53.2259*I + sage: Cs.parent() # optional - octave + Complex Field with 53 bits of precision + """ + from sage.rings.complex_double import CDF + from sage.rings.integer_ring import ZZ + from sage.rings.real_double import RDF + oc = self.parent() + if oc('isinteger(%s)' % self): + if not find_parent: + return ZZ(str(self)) + else: + return ZZ(str(self)), ZZ + elif oc('isreal(%s)' % self): + if not find_parent: + return RDF(str(self)) + else: + return RDF(str(self)), RDF + elif oc('iscomplex(%s)' % self): + real, imag = str(self).strip('() ').split(',') + if not find_parent: + return CDF(RDF(real), RDF(imag)) + else: + return CDF(RDF(real), RDF(imag)), CDF + + def _sage_(self): + """ + Try to parse the octave object and return a sage object. + + EXAMPLES:: + + sage: A = octave('2833') # optional - octave + sage: A.sage() # optional - octave + 2833 + sage: B = sqrt(A) # optional - octave + sage: B.sage() # optional - octave + 53.2259 + sage: C = sqrt(-A) # optional - octave + sage: C.sage() # optional - octave + 53.2259*I + sage: A = octave('[1,2,3,4]') # optional - octave + sage: A.sage() # optional - octave + [1 2 3 4] + sage: A = octave('[1,2.3,4.5]') # optional - octave + sage: A.sage() # optional - octave + [1.00000000000000 2.30000000000000 4.50000000000000] + sage: A = octave('[1,2.3+I,4.5]') # optional - octave + sage: A.sage() # optional - octave + [1.00000000000000 2.30000000000000+1.0*I 4.50000000000000] + """ + oc = self.parent() + if oc('isscalar(%s)' % self): + return self._scalar_() + elif oc('isvector(%s)' % self): + return self._vector_() + elif oc('ismatrix(%s)' % self): + return self._matrix_() + else: + raise NotImplementedError('octave type is not recognized') + # An instance octave = Octave() From 3848dc75d2811fbae85c33b3a1e599f569daf7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 25 Sep 2015 21:13:38 +0200 Subject: [PATCH 220/421] trac #18937 version 2.5.0, (grep removed in doc plugin and adapted to sage 6.9 by using ./configure) --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index a5f3f2d4a37..59c2a0e4222 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=f4e1cb57aca1f8225e1c2baca228dccf232c424d -md5=6d67b4ea3c3559135a5edc2c01f776cd -cksum=1760773179 +sha1=80a4c95fd2a476efb6ea3bafa8da0edfeba6f9eb +md5=eab98d58cd9822821f81e69516396f79 +cksum=783383131 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 3f5987a5cb2..437459cd94c 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.4.9 +2.5.0 From 2c19eb9c5981350fcf3436d028e78ba77846bd97 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 26 Sep 2015 11:18:28 -0300 Subject: [PATCH 221/421] Trac 19125: pep 8 issue --- src/sage/misc/superseded.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index 15c863adcd8..716461ba8c0 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -347,9 +347,9 @@ def is_class(gc_ref): is_cython_class = '__new__' in gc_ref return is_python_class or is_cython_class if self.unbound is None: - search_for=self + search_for = self else: - search_for=self.unbound + search_for = self.unbound for ref in gc.get_referrers(search_for): if is_class(ref) and ref is not self.__dict__: ref_copy = copy.copy(ref) From b37033ac0dbe763f7b7e9b3ab337d6adff42cb5b Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 26 Sep 2015 11:36:57 -0300 Subject: [PATCH 222/421] Trac 19125: a simplification and a doctest --- src/sage/misc/superseded.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index 716461ba8c0..9a1362bc1e7 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -317,7 +317,8 @@ def __name__(self): sage: class cls(object): ....: def new_meth(self): return 42 ....: old_meth = deprecated_function_alias(13109, new_meth) - ....: + sage: cls.old_meth.__name__ + 'old_meth' sage: cls().old_meth.__name__ 'old_meth' @@ -346,10 +347,7 @@ def is_class(gc_ref): is_python_class = '__module__' in gc_ref or '__package__' in gc_ref is_cython_class = '__new__' in gc_ref return is_python_class or is_cython_class - if self.unbound is None: - search_for = self - else: - search_for = self.unbound + search_for = self.unbound or self for ref in gc.get_referrers(search_for): if is_class(ref) and ref is not self.__dict__: ref_copy = copy.copy(ref) @@ -369,8 +367,6 @@ def __call__(self, *args, **kwds): doctest:...: DeprecationWarning: blo is deprecated. Please use bla instead. See http://trac.sagemath.org/13109 for details. 42 - - """ if self.instance is None and self.__module__ != self.func.__module__: other = self.func.__module__ + "." + self.func.__name__ From 2a5609463a71f7baab5a9f028112bab3de20fd36 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 26 Sep 2015 18:19:32 +0200 Subject: [PATCH 223/421] trac #19279: Settling the definition issue --- .../combinat/designs/incidence_structures.py | 19 ++++++++++++++++--- .../graphs/generators/classical_geometries.py | 3 +-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 3373ddb06b9..bab1c8df92c 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -1644,7 +1644,8 @@ def is_generalized_quadrangle(self, verbose=False, parameters=False): r""" Test if the incidence structure is a generalized quadrangle. - An incidence structure is a generalized quadrangle iif: + An incidence structure is a generalized quadrangle iff (see [BH12]_, + section 9.6): - two blocks intersect on at most one point. @@ -1659,6 +1660,13 @@ def is_generalized_quadrangle(self, verbose=False, parameters=False): For more information, see the :wikipedia:`Generalized_quadrangle`. + .. NOTE:: + + Observe that some references (e.g. [PT09]_ or Wikipedia) only allow + *regular* generalized quadrangles. To use such a definition, see the + ``parameters`` optional argument described below, or the methods + :meth:`is_regular` and :meth:`is_uniform`. + INPUT: - ``verbose`` (boolean) -- whether to print an explanation when the @@ -1697,6 +1705,13 @@ def is_generalized_quadrangle(self, verbose=False, parameters=False): sage: hypergraphs.CompleteUniform(4,2).is_generalized_quadrangle(verbose=1) Some point has two projections on some line. False + + REFERENCE: + + .. [PT09] S. Payne, J. Thas, + Finite generalized quadrangles, + European Mathematical Society, 2009. + http://cage.ugent.be/~bamberg/FGQ.pdf """ # The distance between a point and a line in the incidence graph is odd # and must be <= 3. Thus, the diameter is at most 4 @@ -1727,8 +1742,6 @@ def is_generalized_quadrangle(self, verbose=False, parameters=False): else: return True - - def dual(self, algorithm=None): """ Return the dual of the incidence structure. diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index cae8a1e9e30..7e86a0352e6 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -637,12 +637,11 @@ def UnitaryPolarGraph(m, q, algorithm="gap"): from sage.schemes.projective.projective_space import ProjectiveSpace from sage.rings.finite_rings.constructor import FiniteField from sage.modules.free_module_element import free_module_element as vector - from __builtin__ import sum as psum Fq = FiniteField(q**2, 'a') PG = map(vector, ProjectiveSpace(m - 1, Fq)) map(lambda x: x.set_immutable(), PG) def P(x,y): - return psum(map(lambda j: x[j]*y[m-1-j]**q, xrange(m)))==0 + return sum(map(lambda j: x[j]*y[m-1-j]**q, xrange(m)))==0 V = filter(lambda x: P(x,x), PG) G = Graph([V,lambda x,y: # bottleneck is here, of course: From a7e4db33f44888e91bd6ea2aa7274f84510aefbe Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 26 Sep 2015 19:08:36 -0300 Subject: [PATCH 224/421] Trac 19125: reintroduce a more careful test --- src/sage/misc/superseded.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index 9a1362bc1e7..c676ee410c2 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -347,7 +347,7 @@ def is_class(gc_ref): is_python_class = '__module__' in gc_ref or '__package__' in gc_ref is_cython_class = '__new__' in gc_ref return is_python_class or is_cython_class - search_for = self.unbound or self + search_for = self if (self.unbound is None) else self.unbound for ref in gc.get_referrers(search_for): if is_class(ref) and ref is not self.__dict__: ref_copy = copy.copy(ref) From 25a60cb27ea91e4a001a8e370e2d08a615a43c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 27 Sep 2015 09:14:54 +0200 Subject: [PATCH 225/421] trac #18937 version 2.5.1, back to simple "make" in doc plugins --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index 59c2a0e4222..f67bd8165cd 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=80a4c95fd2a476efb6ea3bafa8da0edfeba6f9eb -md5=eab98d58cd9822821f81e69516396f79 -cksum=783383131 +sha1=cbf2002db03d3bf45be16202005b9c75afa99ed2 +md5=862de73795502ae064c172e7c196c1fc +cksum=973755707 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 437459cd94c..73462a5a134 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.5.0 +2.5.1 From f61803a88c49fb7c5c18a59dae3a4b6f693525ce Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:05:52 +0200 Subject: [PATCH 226/421] Trac #17693, comment 36, 46: explain difference remove/discard --- src/sage/data_structures/mutable_poset.py | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 727ebf2c532..d87ecba4683 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2024,6 +2024,17 @@ def remove(self, key, raise_key_error=True): If the element is not a member and ``raise_key_error`` is set (default), raise a ``KeyError``. + .. NOTE:: + + As with Python's ``set``, the methods :meth:`remove` + and meth:`discard` only differ in their behavior when an + element is not contained in the poset: :meth:`remove` + raises a ``KeyError`` whereas :meth:`discard` does not + raise any exception. + + This default behavior can be overridden with the + ``raise_key_error`` parameter. + EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -2083,6 +2094,11 @@ def remove(self, key, raise_key_error=True): | +-- successors: (1, 1) | +-- no predecessors + .. SEEALSO:: + + :meth:`discard`, + :meth:`pop`. + TESTS:: sage: Q = MP([(1, 1, 42), (1, 3, 42), (2, 1, 7), @@ -2187,6 +2203,17 @@ def discard(self, key, raise_key_error=False): If the element is not a member and ``raise_key_error`` is set (not default), raise a ``KeyError``. + .. NOTE:: + + As with Python's ``set``, the methods :meth:`remove` + and meth:`discard` only differ in their behavior when an + element is not contained in the poset: :meth:`remove` + raises a ``KeyError`` whereas :meth:`discard` does not + raise any exception. + + This default behavior can be overridden with the + ``raise_key_error`` parameter. + EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -2201,6 +2228,11 @@ def discard(self, key, raise_key_error=False): ... KeyError: 'Key (1, 2) is not contained in this poset.' sage: P.discard(T((1, 2))) + + .. SEEALSO:: + + :meth:`remove`, + :meth:`pop`. """ return self.remove(key, raise_key_error) From 2e687daa1b42b20b028371f667cf180dc5545f93 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:08:02 +0200 Subject: [PATCH 227/421] Trac #17693, comment 36, 47: remove key from doctest of pop --- src/sage/data_structures/mutable_poset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index d87ecba4683..246013ed0c2 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2252,7 +2252,7 @@ def pop(self, **kwargs): EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP - sage: P = MP(key=lambda c: -c) + sage: P = MP() sage: P.add(3) sage: P poset(3) From cf5aa19d2d9dd9a0dc6c74634937701cd597565b Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:09:45 +0200 Subject: [PATCH 228/421] Trac #17693, comment 36, 48: add note that special elements cannot be popped --- src/sage/data_structures/mutable_poset.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 246013ed0c2..044efd0e35c 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2249,6 +2249,10 @@ def pop(self, **kwargs): An object. + .. NOTE:: + + The special elements ``'null'`` and ``'oo'`` cannot be popped. + EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP From 852b71ae01b0f0961068c03d7c86dc165a5f8af3 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:11:54 +0200 Subject: [PATCH 229/421] Trac #17693, comment 36, 49: simplify code of pop --- src/sage/data_structures/mutable_poset.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 044efd0e35c..e470887ec53 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2269,10 +2269,6 @@ def pop(self, **kwargs): ... KeyError: 'pop from an empty poset' """ - try: - del kwargs['include_special'] - except KeyError: - pass kwargs['include_special'] = False try: From d718694c9311b46b4c6e34084de672e9010d11e4 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:41:07 +0200 Subject: [PATCH 230/421] Trac #17693, comment 36, 50: extend description of INPUT-block --- src/sage/data_structures/mutable_poset.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index e470887ec53..40e88c28586 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2287,6 +2287,8 @@ def union(self, *other): - ``other`` -- a poset or an iterable. In the latter case the iterated objects are seen as elements of a poset. + It is possible to specify more than one ``other`` as + variadic arguments (arbitrary argument lists). OUTPUT: @@ -2327,6 +2329,8 @@ def union_update(self, other): - ``other`` -- a poset or an iterable. In the latter case the iterated objects are seen as elements of a poset. + It is possible to specify more than one ``other`` as + variadic arguments (arbitrary argument lists). OUTPUT: From ac7030514567374889feab7abc9a8e370d17dd57 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:42:08 +0200 Subject: [PATCH 231/421] Trac #17693, comment 36, 51: note extended/changed --- src/sage/data_structures/mutable_poset.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 40e88c28586..ec0c02f61d8 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2296,9 +2296,11 @@ def union(self, *other): .. NOTE:: - If this poset uses a ``key``-function, then all - comparisons are performed on the keys of the elements (and - not on the elements themselves). + The key of an element is used for comparison. Thus elements with + the same key are considered as equal. + + Due to keys and a ``merge`` function (see :class:`MutablePoset`) + this operation might not be commutative. EXAMPLES:: @@ -2338,9 +2340,11 @@ def union_update(self, other): .. NOTE:: - If this poset uses a ``key``-function, then all - comparisons are performed on the keys of the elements (and - not on the elements themselves). + The key of an element is used for comparison. Thus elements with + the same key are considered as equal. + + Due to keys and a ``merge`` function (see :class:`MutablePoset`) + this operation might not be commutative. .. TODO:: From bbd93167dd19c0d537e030323af59af4524e2687 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:42:31 +0200 Subject: [PATCH 232/421] Trac #17693, comment 36, 52: add TODO box --- src/sage/data_structures/mutable_poset.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index ec0c02f61d8..12c9b9df164 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2302,6 +2302,12 @@ def union(self, *other): Due to keys and a ``merge`` function (see :class:`MutablePoset`) this operation might not be commutative. + .. TODO:: + + Use the already existing information in the other poset to speed + up this function. (At the moment each element of the other poset + is inserted one by one and without using this information.) + EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP From aa00e5604327ee3f7ee33b69d99f73d02b514869 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:43:15 +0200 Subject: [PATCH 233/421] Trac #17693, comment 36, 53: rewrite code to unify union/union_update --- src/sage/data_structures/mutable_poset.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 12c9b9df164..d7e0aa70b25 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2324,12 +2324,11 @@ def union(self, *other): poset(3, 4, 7, 8, 42) """ new = self.copy() - for o in other: - new.update(o) + new.update(*other) return new - def union_update(self, other): + def union_update(self, *other): r""" Update this poset with the union of itself and another poset. @@ -2375,12 +2374,13 @@ def union_update(self, other): sage: Q poset(3, 4, 7, 8, 42) """ - try: - it = other.elements() - except AttributeError: - it = iter(other) - for element in it: - self.add(element) + for o in other: + try: + it = o.elements() + except AttributeError: + it = iter(o) + for element in it: + self.add(element) update = union_update # as in a Python set From 141ef15293c10b2516fce41c485e4a39e8326bd0 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:48:57 +0200 Subject: [PATCH 234/421] Trac #17693, comment 36, 54: changes to difference --- src/sage/data_structures/mutable_poset.py | 38 ++++++++++++++--------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index d7e0aa70b25..8e619182821 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2398,6 +2398,8 @@ def difference(self, *other): - ``other`` -- a poset or an iterable. In the latter case the iterated objects are seen as elements of a poset. + It is possible to specify more than one ``other`` as + variadic arguments (arbitrary argument lists). OUTPUT: @@ -2405,9 +2407,11 @@ def difference(self, *other): .. NOTE:: - If this poset uses a ``key``-function, then all - comparisons are performed on the keys of the elements (and - not on the elements themselves). + The key of an element is used for comparison. Thus elements with + the same key are considered as equal. + + Due to keys and a ``merge`` function (see :class:`MutablePoset`) + this operation might not be commutative. EXAMPLES:: @@ -2429,12 +2433,11 @@ def difference(self, *other): poset() """ new = self.copy() - for o in other: - new.difference_update(o) + new.difference_update(*other) return new - def difference_update(self, other): + def difference_update(self, *other): r""" Remove all elements of another poset from this poset. @@ -2442,6 +2445,8 @@ def difference_update(self, other): - ``other`` -- a poset or an iterable. In the latter case the iterated objects are seen as elements of a poset. + It is possible to specify more than one ``other`` as + variadic arguments (arbitrary argument lists). OUTPUT: @@ -2449,9 +2454,11 @@ def difference_update(self, other): .. NOTE:: - If this poset uses a ``key``-function, then all - comparisons are performed on the keys of the elements (and - not on the elements themselves). + The key of an element is used for comparison. Thus elements with + the same key are considered as equal. + + Due to keys and a ``merge`` function (see :class:`MutablePoset`) + this operation might not be commutative. EXAMPLES:: @@ -2464,12 +2471,13 @@ def difference_update(self, other): sage: P poset(3, 7) """ - try: - it = other.keys() - except AttributeError: - it = iter(other) - for key in it: - self.discard(key) + for o in other: + try: + it = o.keys() + except AttributeError: + it = iter(o) + for key in it: + self.discard(key) def intersection(self, *other): From 673a1a0c02070e0efd4c00b87e37a74771c85d85 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:55:03 +0200 Subject: [PATCH 235/421] Trac #17693, comment 36, 54+55: changes to intersection --- src/sage/data_structures/mutable_poset.py | 32 +++++++++++++---------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 8e619182821..de892ad8a78 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2488,6 +2488,8 @@ def intersection(self, *other): - ``other`` -- a poset or an iterable. In the latter case the iterated objects are seen as elements of a poset. + It is possible to specify more than one ``other`` as + variadic arguments (arbitrary argument lists). OUTPUT: @@ -2495,9 +2497,11 @@ def intersection(self, *other): .. NOTE:: - If this poset uses a ``key``-function, then all - comparisons are performed on the keys of the elements (and - not on the elements themselves). + The key of an element is used for comparison. Thus elements with + the same key are considered as equal. + + Due to keys and a ``merge`` function (see :class:`MutablePoset`) + this operation might not be commutative. EXAMPLES:: @@ -2515,12 +2519,11 @@ def intersection(self, *other): poset(42) """ new = self.copy() - for o in other: - new.intersection_update(o) + new.intersection_update(*other) return new - def intersection_update(self, other): + def intersection_update(self, *other): r""" Update this poset with the intersection of itself and another poset. @@ -2528,6 +2531,8 @@ def intersection_update(self, other): - ``other`` -- a poset or an iterable. In the latter case the iterated objects are seen as elements of a poset. + It is possible to specify more than one ``other`` as + variadic arguments (arbitrary argument lists). OUTPUT: @@ -2535,9 +2540,11 @@ def intersection_update(self, other): .. NOTE:: - If this poset uses a ``key``-function, then all - comparisons are performed on the keys of the elements (and - not on the elements themselves). + The key of an element is used for comparison. Thus elements with + the same key are considered as equal. + + Due to keys and a ``merge`` function (see :class:`MutablePoset`) + this operation might not be commutative. EXAMPLES:: @@ -2550,12 +2557,9 @@ def intersection_update(self, other): sage: P poset(42) """ - try: - keys = tuple(self.keys()) - except AttributeError: - keys = tuple(iter(self)) + keys = tuple(self.keys()) for key in keys: - if key not in other: + if any(key not in o for o in other): self.discard(key) From d27425671e3b8fc3b562758cd4c7b04622b42979 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 09:58:39 +0200 Subject: [PATCH 236/421] Trac #17693, comment 36, 57: document parameter reverse better in merge --- src/sage/data_structures/mutable_poset.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index de892ad8a78..cf1d66e4689 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2776,7 +2776,10 @@ def merge(self, key=None, reverse=False): element in this poset. - ``reverse`` -- (default: ``False``) specifies which - direction to go first. When ``key=None``, then this also + direction to go first: + ``False`` searches towards ``'oo'`` and + ``True`` searches towards ``'null'``. + When ``key=None``, then this also specifies which elements are merged first. OUTPUT: From 77b93ed753912f259313ee033168222e7460e183 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 10:00:42 +0200 Subject: [PATCH 237/421] Trac #17693, comment 36, 58: add doctest for non-reflexive can_merge --- src/sage/data_structures/mutable_poset.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index cf1d66e4689..caf8d756e11 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2865,6 +2865,16 @@ def merge(self, key=None, reverse=False): sage: R = P.mapped(lambda x: x+1) sage: R.merge(reverse=True); R poset(4) + + :: + + sage: P = MP(srange(4), + ....: merge=lambda l, r: r, can_merge=lambda l, r: l < r) + sage: P.merge() + Traceback (most recent call last): + ... + RuntimeError: Stopping merge before started; + the can_merge-function is not reflexive. """ if key is None: for shell in tuple(self.shells_topological(reverse=reverse)): From 55803ca3f24bb33ae00fab0119f7286e156b44f3 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 10:04:23 +0200 Subject: [PATCH 238/421] Trac #17693, comment 36, 59: add note on can_merge --- src/sage/data_structures/mutable_poset.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index caf8d756e11..f28f3aaa6d3 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2794,6 +2794,12 @@ def merge(self, key=None, reverse=False): ``merge``-function and the corresponding successor/predecessor is removed from the poset. + .. NOTE:: + + ``can_merge` is applied in the sense of the condition of + depth first iteration, i.e., once ``can_merge`` fails, + the successors/predecessors are no longer tested. + EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP From ac76b87ec4e4b190b327a865b62b5c7b52c34b97 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 10:11:02 +0200 Subject: [PATCH 239/421] Trac #17693, comment 36, 61: change example in map --- src/sage/data_structures/mutable_poset.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index f28f3aaa6d3..783f81d2ebf 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2991,10 +2991,11 @@ def map(self, function, topological=False, reverse=False): ....: def __le__(left, right): ....: return all(l <= r for l, r in zip(left, right)) sage: P = MP([T((1, 3)), T((2, 1)), - ....: T((4, 4)), T((1, 2)), T((2, 2))]) - sage: P.map(lambda e: str(e)) + ....: T((4, 4)), T((1, 2)), T((2, 2))], + ....: key=lambda e: e[:2]) + sage: P.map(lambda e: e + (sum(e),)) sage: P - poset('(1, 2)', '(1, 3)', '(2, 1)', '(2, 2)', '(4, 4)') + poset((1, 2, 3), (1, 3, 4), (2, 1, 3), (2, 2, 4), (4, 4, 8)) """ shells = self.shells_topological(reverse=reverse) \ if topological else self.shells() From 0353cb659e21c64078828944dcd5bdd4acbeb726 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 10:16:51 +0200 Subject: [PATCH 240/421] Trac #17693, comment 36, 62: add note on key-order preservation in mapped --- src/sage/data_structures/mutable_poset.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 783f81d2ebf..038f1cb1c16 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -3022,6 +3022,12 @@ def mapped(self, function): A :class:`MutablePoset`. + .. NOTE:: + + ``function`` is not allowed to change the order of the keys, + but changing the keys themselves is allowed (in contrast + to :meth:`map`). + EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP From 328e8ac6261c223e5013bbd875ce2a42b2308df9 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 10:24:36 +0200 Subject: [PATCH 241/421] Trac #17693, comment 36, 2: remove empty "Introduction" --- src/sage/data_structures/mutable_poset.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 038f1cb1c16..bceb80e3548 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -10,12 +10,6 @@ :mod:`Posets ` in the reference manual. -.. _mutable_poset_intro: - -Introduction -============ - - .. _mutable_poset_examples: Examples From 9da9c95bd16f03149bcc64b24ebea37036bab6a7 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 11:05:22 +0200 Subject: [PATCH 242/421] Trac #17693, comment 36, 1: add a lot of SEEALSO-blocks --- src/sage/data_structures/mutable_poset.py | 372 ++++++++++++++++++++++ 1 file changed, 372 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index bceb80e3548..8372b989d0c 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -189,6 +189,10 @@ class MutablePosetShell(SageObject): sage: s = P.shell(66) sage: type(s) + + .. SEEALSO:: + + :class:`MutablePoset` """ def __init__(self, poset, element): r""" @@ -214,6 +218,10 @@ def poset(self): r""" The poset to which this shell belongs. + .. SEEALSO:: + + :class:`MutablePoset` + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -231,6 +239,11 @@ def element(self): r""" The element contained in this shell. + .. SEEALSO:: + + :meth:`key`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -252,6 +265,11 @@ def key(self): parent) via the ``key``-function (see construction of a :class:`MutablePoset`). + .. SEEALSO:: + + :meth:`element`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -297,6 +315,11 @@ def predecessors(self, reverse=False): A set. + .. SEEALSO:: + + :meth:`successors`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -324,6 +347,11 @@ def successors(self, reverse=False): A set. + .. SEEALSO:: + + :meth:`predecessors`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -353,6 +381,12 @@ def is_special(self): ``True`` or ``False``. + .. SEEALSO:: + + :meth:`is_null`, + :meth:`is_oo`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -374,6 +408,13 @@ def is_null(self): ``True`` or ``False``. + .. SEEALSO:: + + :meth:`is_special`, + :meth:`is_oo`, + :meth:`MutablePoset.null`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -395,6 +436,13 @@ def is_oo(self): ``True`` or ``False``. + .. SEEALSO:: + + :meth:`is_null`, + :meth:`is_special`, + :meth:`MutablePoset.oo`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -494,6 +542,11 @@ def le(self, other, reverse=False): of the keys of the elements contained in the shells, except for special shells (see :class:`MutablePosetShell`). + .. SEEALSO:: + + :meth:`eq`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -585,6 +638,11 @@ def eq(self, other): in the (non-special) shells. In particlar, elements/shells with the same key are considered as equal. + .. SEEALSO:: + + :meth:`le`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -640,6 +698,11 @@ def _copy_all_linked_(self, memo, poset, mapping): A new shell. + .. SEEALSO:: + + :meth:`MutablePoset.copy`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -691,6 +754,11 @@ def _search_covers_(self, covers, shell, reverse=False): Note that ``False`` is returned if we do not have ``self <= shell``. + .. SEEALSO:: + + :meth:`covers`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -757,6 +825,10 @@ def covers(self, shell, reverse=False): sage: sorted(P.oo.covers(e, reverse=True), ....: key=lambda c: repr(c.element)) [(4, 4)] + + .. SEEALSO:: + + :class:`MutablePoset` """ covers = set() self._search_covers_(covers, shell, reverse) @@ -793,6 +865,12 @@ def _iter_depth_first_visit_(self, marked, An iterator. + .. SEEALSO:: + + :meth:`iter_depth_first`, + :meth:`iter_topological`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -862,6 +940,11 @@ def iter_depth_first(self, reverse=False, key=None, condition=None): sage: list(P.null.iter_depth_first( ....: condition=lambda s: s.element[0] == 1)) [null, (1, 1), (1, 2), (1, 3)] + + .. SEEALSO:: + + :meth:`iter_topological`, + :class:`MutablePoset`. """ marked = set() return self._iter_depth_first_visit_(marked, reverse, key, condition) @@ -897,6 +980,12 @@ def _iter_topological_visit_(self, marked, An iterator. + .. SEEALSO:: + + :meth:`iter_depth_first`, + :meth:`iter_topological`, + :class:`MutablePoset`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -1022,6 +1111,14 @@ def iter_topological(self, reverse=False, key=None, condition=None): sage: list(P.null.iter_topological( ....: reverse=True, condition=lambda s: s.element[0] == 1)) [(1, 3), (1, 2), (1, 1), null] + + .. SEEALSO:: + + :meth:`iter_depth_first`, + :meth:`MutablePoset.shells_topological`, + :meth:`MutablePoset.elements_topological`, + :meth:`MutablePoset.keys_topological`, + :class:`MutablePoset`. """ marked = set() return self._iter_topological_visit_(marked, reverse, key, condition) @@ -1070,6 +1167,11 @@ def merge(self, element, check=True, delete=True): sage: P.shell(2).merge((3, 'b')) sage: P poset((1, 'a'), (2, 'bc'), (4, 'd')) + + .. SEEALSO:: + + :meth:`MutablePoset.merge`, + :class:`MutablePoset`. """ poset = self.poset if poset._merge_ is None: @@ -1094,6 +1196,10 @@ def is_MutablePoset(P): r""" Test whether ``P`` inherits from :class:`MutablePoset`. + .. SEEALSO:: + + :class:`MutablePoset` + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -1179,6 +1285,10 @@ class MutablePoset(SageObject): sage: C = MP([5, 3, 11]); C poset(3, 5, 11) + + .. SEEALSO:: + + :class:`MutablePosetShell`. """ def __init__(self, data=None, key=None, merge=None, can_merge=None): r""" @@ -1247,6 +1357,12 @@ def clear(self): Nothing. + .. SEEALSO:: + + :meth:`discard`, + :meth:`pop`, + :meth:`remove`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -1317,6 +1433,12 @@ def null(self): null sage: z.is_null() True + + .. SEEALSO:: + + :meth:`oo`, + :meth:`MutablePosetShell.is_null`, + :meth:`MutablePosetShell.is_special`. """ return self._null_ @@ -1335,6 +1457,12 @@ def oo(self): oo sage: oo.is_oo() True + + .. SEEALSO:: + + :meth:`null`, + :meth:`MutablePosetShell.is_oo`, + :meth:`MutablePosetShell.is_special`. """ return self._oo_ @@ -1365,6 +1493,11 @@ def shell(self, key): 42 sage: type(e) + + .. SEEALSO:: + + :meth:`element`, + :meth:`get_key`. """ return self._shells_[key] @@ -1390,6 +1523,11 @@ def element(self, key): 42 sage: type(e) + + .. SEEALSO:: + + :meth:`shell`, + :meth:`get_key`. """ return self.shell(key).element @@ -1406,6 +1544,11 @@ def get_key(self, element): An object (the key of ``element``). + .. SEEALSO:: + + :meth:`element`, + :meth:`shell`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -1438,6 +1581,10 @@ def _copy_shells_(self, other, mapping): Nothing. + .. SEEALSO:: + + :meth:`copy` + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -1476,6 +1623,11 @@ def copy(self, mapping=None): A poset with the same content as ``self``. + .. SEEALSO:: + + :meth:`map`, + :meth:`mapped`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -1525,6 +1677,16 @@ def shells(self, include_special=False): () sage: tuple(P.shells(include_special=True)) (null, oo) + + .. SEEALSO:: + + :meth:`shells_topological`, + :meth:`elements`, + :meth:`elements_topological`, + :meth:`keys`, + :meth:`keys_topological`, + :meth:`MutablePosetShell.iter_depth_first`, + :meth:`MutablePosetShell.iter_topological`. """ if include_special: yield self.null @@ -1580,6 +1742,16 @@ def shells_topological(self, include_special=False, sage: list(P.shells_topological( ....: include_special=True, reverse=True)) [oo, (4, 4), (1, 3), (2, 2), (1, 2), (2, 1), (1, 1), null] + + .. SEEALSO:: + + :meth:`shells`, + :meth:`elements`, + :meth:`elements_topological`, + :meth:`keys`, + :meth:`keys_topological`, + :meth:`MutablePosetShell.iter_depth_first`, + :meth:`MutablePosetShell.iter_topological`. """ if key is None: key = repr @@ -1618,6 +1790,16 @@ def elements(self, **kwargs): [3, 7, 42] returns all elements as well. + + .. SEEALSO:: + + :meth:`shells`, + :meth:`shells_topological`, + :meth:`elements_topological`, + :meth:`keys`, + :meth:`keys_topological`, + :meth:`MutablePosetShell.iter_depth_first`, + :meth:`MutablePosetShell.iter_topological`. """ for shell in self.shells(**kwargs): yield shell.element @@ -1653,6 +1835,16 @@ def elements_topological(self, **kwargs): ((2, 1), ), ((2, 2), ), ((4, 4), )] + + .. SEEALSO:: + + :meth:`shells`, + :meth:`shells_topological`, + :meth:`elements`, + :meth:`keys`, + :meth:`keys_topological`, + :meth:`MutablePosetShell.iter_depth_first`, + :meth:`MutablePosetShell.iter_topological`. """ for shell in self.shells_topological(**kwargs): yield shell.element @@ -1689,6 +1881,16 @@ def keys(self, **kwargs): [(3, ), (7, ), (42, )] + + .. SEEALSO:: + + :meth:`shells`, + :meth:`shells_topological`, + :meth:`elements`, + :meth:`elements_topological`, + :meth:`keys_topological`, + :meth:`MutablePosetShell.iter_depth_first`, + :meth:`MutablePosetShell.iter_topological`. """ for shell in self.shells(**kwargs): yield shell.key @@ -1724,6 +1926,16 @@ def keys_topological(self, **kwargs): [((1, 1), ), ((2, 1), ), ((4, 4), )] + + .. SEEALSO:: + + :meth:`shells`, + :meth:`shells_topological`, + :meth:`elements`, + :meth:`elements_topological`, + :meth:`keys`, + :meth:`MutablePosetShell.iter_depth_first`, + :meth:`MutablePosetShell.iter_topological`. """ for shell in self.shells_topological(**kwargs): yield shell.key @@ -1746,6 +1958,10 @@ def repr(self, include_special=False, reverse=False): A string. + .. SEEALSO:: + + :meth:`repr_full` + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -1772,6 +1988,10 @@ def repr_full(self, reverse=False): A string. + .. SEEALSO:: + + :meth:`repr` + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -1816,6 +2036,12 @@ def contains(self, key): ``True`` or ``False``. + .. SEEALSO:: + + :meth:`shells`, + :meth:`elements`, + :meth:`keys`. + TESTS:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -1944,6 +2170,12 @@ def add(self, element): sage: B poset() + .. SEEALSO:: + + :meth:`discard`, + :meth:`pop`, + :meth:`remove`. + TESTS:: sage: R = MP([(1, 1, 42), (1, 3, 42), (2, 1, 7), @@ -2090,6 +2322,8 @@ def remove(self, key, raise_key_error=True): .. SEEALSO:: + :meth:`add`, + :meth:`clear`, :meth:`discard`, :meth:`pop`. @@ -2225,6 +2459,8 @@ def discard(self, key, raise_key_error=False): .. SEEALSO:: + :meth:`add`, + :meth:`clear`, :meth:`remove`, :meth:`pop`. """ @@ -2262,6 +2498,13 @@ def pop(self, **kwargs): Traceback (most recent call last): ... KeyError: 'pop from an empty poset' + + .. SEEALSO:: + + :meth:`add`, + :meth:`clear`, + :meth:`discard`, + :meth:`remove`. """ kwargs['include_special'] = False @@ -2312,6 +2555,16 @@ def union(self, *other): sage: P.union(Q) poset(3, 4, 7, 8, 42) + .. SEEALSO:: + + :meth:`union_update`, + :meth:`difference`, :meth:`difference_update`, + :meth:`intersection`, :meth:`intersection_update`, + :meth:`symmetric_difference`, :meth:`symmetric_difference_update`, + :meth:`is_disjoint`, + :meth:`is_subset`, + :meth:`is_superset`. + TESTS:: sage: P.union(P, Q, Q, P) @@ -2362,6 +2615,16 @@ def union_update(self, *other): sage: P poset(3, 4, 7, 8, 42) + .. SEEALSO:: + + :meth:`union`, + :meth:`difference`, :meth:`difference_update`, + :meth:`intersection`, :meth:`intersection_update`, + :meth:`symmetric_difference`, :meth:`symmetric_difference_update`, + :meth:`is_disjoint`, + :meth:`is_subset`, + :meth:`is_superset`. + TESTS:: sage: Q.update(P) @@ -2417,6 +2680,16 @@ def difference(self, *other): sage: P.difference(Q) poset(3, 7) + .. SEEALSO:: + + :meth:`union`, :meth:`union_update`, + :meth:`difference_update`, + :meth:`intersection`, :meth:`intersection_update`, + :meth:`symmetric_difference`, :meth:`symmetric_difference_update`, + :meth:`is_disjoint`, + :meth:`is_subset`, + :meth:`is_superset`. + TESTS:: sage: P.difference(Q, Q) @@ -2464,6 +2737,16 @@ def difference_update(self, *other): sage: P.difference_update(Q) sage: P poset(3, 7) + + .. SEEALSO:: + + :meth:`union`, :meth:`union_update`, + :meth:`difference`, + :meth:`intersection`, :meth:`intersection_update`, + :meth:`symmetric_difference`, :meth:`symmetric_difference_update`, + :meth:`is_disjoint`, + :meth:`is_subset`, + :meth:`is_superset`. """ for o in other: try: @@ -2507,6 +2790,16 @@ def intersection(self, *other): sage: P.intersection(Q) poset(42) + .. SEEALSO:: + + :meth:`union`, :meth:`union_update`, + :meth:`difference`, :meth:`difference_update`, + :meth:`intersection_update`, + :meth:`symmetric_difference`, :meth:`symmetric_difference_update`, + :meth:`is_disjoint`, + :meth:`is_subset`, + :meth:`is_superset`. + TESTS:: sage: P.intersection(P, Q, Q, P) @@ -2550,6 +2843,16 @@ def intersection_update(self, *other): sage: P.intersection_update(Q) sage: P poset(42) + + .. SEEALSO:: + + :meth:`union`, :meth:`union_update`, + :meth:`difference`, :meth:`difference_update`, + :meth:`intersection`, + :meth:`symmetric_difference`, :meth:`symmetric_difference_update`, + :meth:`is_disjoint`, + :meth:`is_subset`, + :meth:`is_superset`. """ keys = tuple(self.keys()) for key in keys: @@ -2584,6 +2887,16 @@ def symmetric_difference(self, other): poset(4, 8, 42) sage: P.symmetric_difference(Q) poset(3, 4, 7, 8) + + .. SEEALSO:: + + :meth:`union`, :meth:`union_update`, + :meth:`difference`, :meth:`difference_update`, + :meth:`intersection`, :meth:`intersection_update`, + :meth:`symmetric_difference_update`, + :meth:`is_disjoint`, + :meth:`is_subset`, + :meth:`is_superset`. """ new = self.copy() new.symmetric_difference_update(other) @@ -2619,6 +2932,16 @@ def symmetric_difference_update(self, other): sage: P.symmetric_difference_update(Q) sage: P poset(3, 4, 7, 8) + + .. SEEALSO:: + + :meth:`union`, :meth:`union_update`, + :meth:`difference`, :meth:`difference_update`, + :meth:`intersection`, :meth:`intersection_update`, + :meth:`symmetric_difference`, + :meth:`is_disjoint`, + :meth:`is_subset`, + :meth:`is_superset`. """ T = other.difference(self) self.difference_update(other) @@ -2655,6 +2978,15 @@ def is_disjoint(self, other): False sage: P.is_disjoint(Q.difference(P)) True + + .. SEEALSO:: + + :meth:`is_subset`, + :meth:`is_superset`, + :meth:`union`, :meth:`union_update`, + :meth:`difference`, :meth:`difference_update`, + :meth:`intersection`, :meth:`intersection_update`, + :meth:`symmetric_difference`, :meth:`symmetric_difference_update`. """ return all(key not in other for key in self.keys()) @@ -2700,6 +3032,15 @@ def is_subset(self, other): True sage: P.is_subset(P.union(Q)) True + + .. SEEALSO:: + + :meth:`is_disjoint`, + :meth:`is_superset`, + :meth:`union`, :meth:`union_update`, + :meth:`difference`, :meth:`difference_update`, + :meth:`intersection`, :meth:`intersection_update`, + :meth:`symmetric_difference`, :meth:`symmetric_difference_update`. """ return all(key in other for key in self.keys()) @@ -2745,6 +3086,15 @@ def is_superset(self, other): True sage: P.union(Q).is_superset(P) True + + .. SEEALSO:: + + :meth:`is_disjoint`, + :meth:`is_subset`, + :meth:`union`, :meth:`union_update`, + :meth:`difference`, :meth:`difference_update`, + :meth:`intersection`, :meth:`intersection_update`, + :meth:`symmetric_difference`, :meth:`symmetric_difference_update`. """ try: it = other.keys() @@ -2849,6 +3199,10 @@ def merge(self, key=None, reverse=False): sage: Q.merge(); Q poset((4, 4, 'abcdef')) + .. SEEALSO:: + + :meth:`MutablePosetShell.merge` + TESTS:: sage: copy(P).merge(reverse=False) == copy(P).merge(reverse=True) @@ -2921,6 +3275,10 @@ def maximal_elements(self): ....: T((1, 2)), T((2, 2))]) sage: list(P.maximal_elements()) [(1, 3), (2, 2)] + + .. SEEALSO:: + + :meth:`minimal_elements` """ return iter(shell.element for shell in self.oo.predecessors() @@ -2949,6 +3307,10 @@ def minimal_elements(self): ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: list(P.minimal_elements()) [(1, 2), (2, 1)] + + .. SEEALSO:: + + :meth:`maximal_elements` """ return iter(shell.element for shell in self.null.successors() @@ -2990,6 +3352,11 @@ def map(self, function, topological=False, reverse=False): sage: P.map(lambda e: e + (sum(e),)) sage: P poset((1, 2, 3), (1, 3, 4), (2, 1, 3), (2, 2, 4), (4, 4, 8)) + + .. SEEALSO:: + + :meth:`copy`, + :meth:`mapped`. """ shells = self.shells_topological(reverse=reverse) \ if topological else self.shells() @@ -3032,6 +3399,11 @@ def mapped(self, function): ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: P.mapped(lambda e: str(e)) poset('(1, 2)', '(1, 3)', '(2, 1)', '(2, 2)', '(4, 4)') + + .. SEEALSO:: + + :meth:`copy`, + :meth:`map`. """ return self.copy(mapping=function) From 10aedb7e47ce3466bd80ca6ed7724df263e706ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 28 Sep 2015 11:31:03 +0200 Subject: [PATCH 243/421] trac #18937, bot version 2.5.2, respecting $MAKE if it exists --- build/pkgs/patchbot/checksums.ini | 6 +++--- build/pkgs/patchbot/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index f67bd8165cd..4a35077fa89 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=cbf2002db03d3bf45be16202005b9c75afa99ed2 -md5=862de73795502ae064c172e7c196c1fc -cksum=973755707 +sha1=1850ce7004fe49b669be0b53102d32e9095cc307 +md5=a84f244c2f6e6c715676a09028750b36 +cksum=1356602931 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index 73462a5a134..f225a78adf0 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.5.1 +2.5.2 From f904968af9b3dd51313876e95820f9dd9bb2f6ce Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 12:26:13 +0200 Subject: [PATCH 244/421] Trac #17693, comment 36, 24-26: rewrite docstring of covers --- src/sage/data_structures/mutable_poset.py | 29 ++++++++++++++--------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 8372b989d0c..f2b26f35d84 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -785,29 +785,36 @@ def _search_covers_(self, covers, shell, reverse=False): def covers(self, shell, reverse=False): r""" - Return the covers of the given shell (considering only - shells which originate from this shell). + Return the lower or upper covers of the specified ``shell``; + the search is started at this (``self``) shell. + + A lower cover of `x` is an element `y` of the poset + such that `y < x` and there is no element `z` of the poset + so that `x < z < y`. Likewise, + an upper cover of `x` is an element `y` such that `x < y` and + there is no element `z` so that `x < z < y`. INPUT: - ``shell`` -- the shell for which to find the covering shells. + There is no restrition of ``shell`` being contained in the poset. - ``reverse`` -- (default: ``False``) if not set, then find the lower covers, otherwise find the upper covers. OUTPUT: - A set of the covers. + A set of :class:`shells `. + + .. NOTE:: - Suppose ``reverse`` is ``False``. This method returns all the - lower covers of the given ``shell``, i.e., shells in the - poset, which are at most the given shell and maximal with - this property. Only shells which are (not necessarily - direct) successors of the calling shell are considered. + Suppose ``reverse`` is ``False``. This method starts at + the calling shell (``self``) and searches towards ``'oo'``. + Thus, only shells which are (not necessarily + direct) successors of this shell are considered. - If ``reverse`` is ``True``, then the reverse direction is - taken, i.e., in the text above replace lower covers by upper - covers, maximal by minimal, and successors by predecessors. + If ``reverse`` is ``True``, then the reverse direction is + taken. EXAMPLES:: From c546102768f5ace3dd178d55ac4ca25140356a61 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 12:44:34 +0200 Subject: [PATCH 245/421] minor simplification of covers code --- src/sage/data_structures/mutable_poset.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index ce35de4a0c5..fb7e0263f9b 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -791,10 +791,7 @@ def covers(self, shell, reverse=False): covers = set().union(*(e.covers(shell, reverse) for e in self.successors(reverse) if e.le(shell, reverse))) - if covers: - return covers - else: - return set([self]) + return covers or set([self]) def _iter_depth_first_visit_(self, marked, From 34e34dca5609d9b5ff4bb90247fa412d014dbd49 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 12:45:41 +0200 Subject: [PATCH 246/421] Trac #17693, comment 36, 19--23: improve docstring of covers (former _search_covers_) --- src/sage/data_structures/mutable_poset.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index fb7e0263f9b..95222430d73 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -747,6 +747,8 @@ def covers(self, shell, reverse=False): - ``shell`` -- the shell for which to find the covering shells. There is no restrition of ``shell`` being contained in the poset. + If ``shell`` is contained in the poset, then use the more efficient + methods :meth:`predecessors` and :meth:`successors`. - ``reverse`` -- (default: ``False``) if not set, then find the lower covers, otherwise find the upper covers. @@ -778,13 +780,30 @@ def covers(self, shell, reverse=False): sage: sorted(P.null.covers(e), ....: key=lambda c: repr(c.element)) [(1, 2), (2, 1)] + sage: set(_) == e.predecessors() + True sage: sorted(P.oo.covers(e, reverse=True), ....: key=lambda c: repr(c.element)) [(4, 4)] + sage: set(_) == e.successors() + True + + :: + + sage: Q = MP([T((3, 2))]) + sage: f = next(Q.shells()) + sage: sorted(P.null.covers(f), + ....: key=lambda c: repr(c.element)) + [(2, 2)] + sage: sorted(P.oo.covers(f, reverse=True), + ....: key=lambda c: repr(c.element)) + [(4, 4)] .. SEEALSO:: - :class:`MutablePoset` + :meth:`predecessors`, + :meth:`successors`, + :class:`MutablePoset`. """ if self == shell: return set() From 490c2236b3ba38a125986c0cd5c4669f92911917 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 12:51:17 +0200 Subject: [PATCH 247/421] Trac #17693, comment 36, 28--31: document role of self --- src/sage/data_structures/mutable_poset.py | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 95222430d73..2e01c395343 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -843,6 +843,12 @@ def _iter_depth_first_visit_(self, marked, An iterator. + .. NOTE:: + + The depth first search starts at this (``self``) shell. Thus + only this shell and shells greater than (in case of + ``reverse=False``) this shell are visited. + .. SEEALSO:: :meth:`iter_depth_first`, @@ -899,6 +905,12 @@ def iter_depth_first(self, reverse=False, key=None, condition=None): An iterator. + .. NOTE:: + + The depth first search starts at this (``self``) shell. Thus + only this shell and shells greater than (in case of + ``reverse=False``) this shell are visited. + ALGORITHM: See :wikipedia:`Depth-first_search`. @@ -958,6 +970,13 @@ def _iter_topological_visit_(self, marked, An iterator. + .. NOTE:: + + The topological search will only find shells smaller than + (in case of ``reverse=False``) + or equal to this (``self``) shell. This is in contrast to + :meth:`iter_depth_first`. + .. SEEALSO:: :meth:`iter_depth_first`, @@ -1014,6 +1033,13 @@ def iter_topological(self, reverse=False, key=None, condition=None): An iterator. + .. NOTE:: + + The topological search will only find shells smaller than + (in case of ``reverse=False``) + or equal to this (``self``) shell. This is in contrast to + :meth:`iter_depth_first`. + ALGORITHM: Here a simplified version of the algorithm found in [T1976]_ From 3de0a1d250a07c902295ff6199b9995e3fd19849 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 13:10:31 +0200 Subject: [PATCH 248/421] Trac #17693, comment 36, 33: explicitly mention merge and can_merge at top of docstring --- src/sage/data_structures/mutable_poset.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 2e01c395343..b61ce1cd53d 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1133,6 +1133,7 @@ def merge(self, element, check=True, delete=True): Merge the given element with the element contained in this shell. + INPUT: - ``element`` -- an element (of the poset). @@ -1150,9 +1151,12 @@ def merge(self, element, check=True, delete=True): .. NOTE:: - This method uses the parameters ``merge`` and - ``can_merge`` of the :class:`MutablePoset` which contains - this shell. + This operation depends on the parameters ``merge`` and + ``can_merge`` of the :class:`MutablePoset` this shell is + contained in. These parameters are defined when the poset + is constructed. + + .. NOTE:: If the ``merge`` function returns ``None``, then this shell is removed from the poset. From 485b46f4f2203885ee22469c3b237e67a605bc21 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 13:38:22 +0200 Subject: [PATCH 249/421] Trac #17693, comment 36, 34: raise exceptions when merge is not possible --- src/sage/data_structures/mutable_poset.py | 26 +++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index b61ce1cd53d..233a5638f0b 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1133,14 +1133,14 @@ def merge(self, element, check=True, delete=True): Merge the given element with the element contained in this shell. - INPUT: - ``element`` -- an element (of the poset). - ``check`` -- (default: ``True``) if set, then the ``can_merge``-function of :class:`MutablePoset` determines - whether the merge is possible. + whether the merge is possible. ``can_merge`` is ``None`` means + that this check is always passed. - ``delete`` -- (default: ``True``) if set, then ``element`` is removed from the poset after the merge. @@ -1180,14 +1180,25 @@ def merge(self, element, check=True, delete=True): :meth:`MutablePoset.merge`, :class:`MutablePoset`. + + TESTS:: + + sage: MP([2], merge=operator.add, + ....: can_merge=lambda _, __: False).shell(2).merge(1) + Traceback (most recent call last): + ... + RuntimeError: Cannot merge 2 with 1. """ poset = self.poset if poset._merge_ is None: + # poset._merge_ is None means no merge (poset._merge_ simply + # returns its first input argument). return self_element = self.element - if check and poset._can_merge_ is not None and \ - not poset._can_merge_(self_element, element): - return + if check: + if not poset._can_merge_(self_element, element): + raise RuntimeError('Cannot merge %s with %s.' % + (self_element, element)) new = poset._merge_(self_element, element) if new is None: poset.discard(poset.get_key(self.element)) @@ -1341,7 +1352,10 @@ def __init__(self, data=None, key=None, merge=None, can_merge=None): self._key_ = key self._merge_ = merge - self._can_merge_ = can_merge + if can_merge is None: + self._can_merge_ = lambda _, __: True + else: + self._can_merge_ = can_merge if data is not None: try: From d5d6bec7548d7c2ab961a31d70e96211cb572d2e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 13:38:54 +0200 Subject: [PATCH 250/421] check=False in MutablePoset.merge since checked already before --- src/sage/data_structures/mutable_poset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 233a5638f0b..dcf3deaf784 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -3272,7 +3272,7 @@ def can_merge(other): for m in tuple(to_merge): if m.is_special(): continue - shell.merge(m.element, delete=True) + shell.merge(m.element, check=False, delete=True) def maximal_elements(self): From 6da3db12035b5b4904dd7ba25082140c17023fa6 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 13:52:28 +0200 Subject: [PATCH 251/421] Trac #17693, comment 36, 37: extend doc to mention more explicitly that merge is not allowed to change keys --- src/sage/data_structures/mutable_poset.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index dcf3deaf784..67f4600d1f9 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1262,7 +1262,8 @@ class MutablePoset(SageObject): ``merge`` is ``None`` (default) is equivalent to ``merge`` returning its first argument. Note that it is not allowed that the key of the returning element differs from the key of the first - input parameter. + input parameter. This means ``merge`` must not change the + position of the element in the poset. - ``can_merge`` -- a function which checks whether its second argument can be merged to its first. From f1e9f9e8224c4e78944c57f3ca9c4b73a2f96d83 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 13:57:55 +0200 Subject: [PATCH 252/421] Trac #17693, comment 36, 38: clarify parameter key --- src/sage/data_structures/mutable_poset.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 67f4600d1f9..b99414785f6 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1250,6 +1250,12 @@ class MutablePoset(SageObject): (default), this is the identity, i.e., keys are equal to their elements. + Two elements with the same keys are considered as equal; so only + one of these two elements can be in the poset. + + This ``key`` is not used for sorting (in contrast to + sorting-functions, e.g. ``sorted``). + - ``merge`` -- a function which merges its second argument (an element) to its first (again an element) and returns the result (as an element). If the return value is ``None``, the element is From ae0cad989dd4e5dddc9bf2218280fab131db2c85 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 28 Sep 2015 14:03:14 +0200 Subject: [PATCH 253/421] Trac #17693, comment 36, 60: mention motivation asymptoic expansions in merge method --- src/sage/data_structures/mutable_poset.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index b99414785f6..117d8138fc0 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -3173,6 +3173,13 @@ def merge(self, key=None, reverse=False): depth first iteration, i.e., once ``can_merge`` fails, the successors/predecessors are no longer tested. + .. NOTE:: + + The motivation for such a merge behavior comes from + asymptotic expansions: `O(n^3)` is merges with, for + example, `3n^2` or `O(n)` to `O(n^3)` (as `n` tends to + `\infty`; see :wikipedia:`Big_O_notation`) + EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP From 617df137291c1fd10fcc7de21435867d316870ff Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 28 Sep 2015 10:22:17 -0300 Subject: [PATCH 254/421] Trac 19299: implement cartesian_factors --- src/sage/sets/cartesian_product.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/sets/cartesian_product.py b/src/sage/sets/cartesian_product.py index a208f257250..bf44ea3ba50 100644 --- a/src/sage/sets/cartesian_product.py +++ b/src/sage/sets/cartesian_product.py @@ -276,3 +276,18 @@ def __iter__(self): 1 """ return iter(self.value) + + def cartesian_factors(self): + r""" + Return the tuple of elements that compose this element. + + EXAMPLES:: + + sage: A = cartesian_product([ZZ, RR]) + sage: A((1, 1.23)).cartesian_factors() + sage: A((1, 1.23)).cartesian_factors() + (1, 1.23000000000000) + sage: type(_) + + """ + return self.value From 1b271285ac389d31d9603e227e40da6fe3f45d3e Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 28 Sep 2015 10:34:22 -0300 Subject: [PATCH 255/421] Trac 19299: improve speed of OperationTable --- src/sage/matrix/operation_table.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/sage/matrix/operation_table.py b/src/sage/matrix/operation_table.py index eca698fd93d..4f1aff5de81 100644 --- a/src/sage/matrix/operation_table.py +++ b/src/sage/matrix/operation_table.py @@ -388,6 +388,12 @@ def __init__(self, S, operation, names='letters', elements=None): except Exception: raise TypeError('unable to coerce %s into %s' % (e, S)) self._elts = elems + try: + self._index_elts = {e: i for i,e in enumerate(self._elts)} + except TypeError: + # the elements might not be hashable. But if they are it is much + # faster to lookup in a hash table rather than in a list! + pass self._n = len(self._elts) self._name_dict = {} @@ -419,16 +425,24 @@ def __init__(self, S, operation, names='letters', elements=None): # If not, we'll discover that next in actual use. self._table = [] + if hasattr(self, '_index_elts'): + get_row = lambda x: self._index_elts[x] + else: + get_row = lambda x: self._elts.index(x) for g in self._elts: row = [] for h in self._elts: try: result = self._operation(g, h) - row.append(self._elts.index(result)) - except ValueError: # list/index condition - raise ValueError('%s%s%s=%s, and so the set is not closed' % (g, self._ascii_symbol, h, result)) except Exception: raise TypeError('elements %s and %s of %s are incompatible with operation: %s' % (g,h,S,self._operation)) + + try: + r = get_row(result) + except (KeyError,ValueError): + raise ValueError('%s%s%s=%s, and so the set is not closed' % (g, self._ascii_symbol, h, result)) + + row.append(r) self._table.append(row) def _name_maker(self, names): @@ -559,8 +573,8 @@ def __getitem__(self, pair): TESTS:: sage: from sage.matrix.operation_table import OperationTable - sage: G=DiCyclicGroup(3) - sage: T=OperationTable(G, operator.mul) + sage: G = DiCyclicGroup(3) + sage: T = OperationTable(G, operator.mul) sage: T[G('(1,2)(3,4)(5,6,7)')] Traceback (most recent call last): ... From a0b8757523c91c8d8c7cea296930743a3b263585 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 28 Sep 2015 16:17:36 +0200 Subject: [PATCH 256/421] trac #19299: Code rephrase --- src/sage/matrix/operation_table.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/sage/matrix/operation_table.py b/src/sage/matrix/operation_table.py index 4f1aff5de81..e76a66c0ab8 100644 --- a/src/sage/matrix/operation_table.py +++ b/src/sage/matrix/operation_table.py @@ -388,12 +388,6 @@ def __init__(self, S, operation, names='letters', elements=None): except Exception: raise TypeError('unable to coerce %s into %s' % (e, S)) self._elts = elems - try: - self._index_elts = {e: i for i,e in enumerate(self._elts)} - except TypeError: - # the elements might not be hashable. But if they are it is much - # faster to lookup in a hash table rather than in a list! - pass self._n = len(self._elts) self._name_dict = {} @@ -425,10 +419,14 @@ def __init__(self, S, operation, names='letters', elements=None): # If not, we'll discover that next in actual use. self._table = [] - if hasattr(self, '_index_elts'): - get_row = lambda x: self._index_elts[x] - else: - get_row = lambda x: self._elts.index(x) + + # the elements might not be hashable. But if they are it is much + # faster to lookup in a hash table rather than in a list! + try: + get_row = {e: i for i,e in enumerate(self._elts)}.__getitem__ + except TypeError: + get_row = self._elts.index + for g in self._elts: row = [] for h in self._elts: From b3b9821d5f8566848b0ba107c3ef0bd28e761f78 Mon Sep 17 00:00:00 2001 From: Stefan Kraemer Date: Mon, 28 Sep 2015 16:47:11 +0200 Subject: [PATCH 257/421] Bugfix hyperbolic_arc and hyperbolic_polygon --- src/sage/plot/hyperbolic_arc.py | 10 +++++++--- src/sage/plot/hyperbolic_polygon.py | 14 ++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/sage/plot/hyperbolic_arc.py b/src/sage/plot/hyperbolic_arc.py index 1e658fc84d5..e4e7c6bbfff 100644 --- a/src/sage/plot/hyperbolic_arc.py +++ b/src/sage/plot/hyperbolic_arc.py @@ -7,6 +7,7 @@ """ #***************************************************************************** # Copyright (C) 2011 Hartmut Monien , +# 2015 Stefan Kraemer # # Distributed under the terms of the GNU General Public License (GPL) # @@ -70,13 +71,16 @@ def _hyperbolic_arc(self, z0, z3, first=False): the hyperbolic arc between the complex numbers z0 and z3 in the hyperbolic plane. """ - if (z0-z3).real() == 0: + z0, z3 = (CC(z0), CC(z3)) + p = (abs(z0)*abs(z0)-abs(z3)*abs(z3))/(z0-z3).real()/2 + r = abs(z0-p) + + if abs(z3-z0)/r < 0.1: self.path.append([(z0.real(),z0.imag()), (z3.real(),z3.imag())]) return - z0, z3 = (CC(z0), CC(z3)) + if z0.imag() == 0 and z3.imag() == 0: p = (z0.real()+z3.real())/2 - r = abs(z0-p) zm = CC(p, r) self._hyperbolic_arc(z0, zm, first) self._hyperbolic_arc(zm, z3) diff --git a/src/sage/plot/hyperbolic_polygon.py b/src/sage/plot/hyperbolic_polygon.py index e6465183e04..3bfbdcb3404 100644 --- a/src/sage/plot/hyperbolic_polygon.py +++ b/src/sage/plot/hyperbolic_polygon.py @@ -8,7 +8,8 @@ """ #***************************************************************************** # Copyright (C) 2011 Hartmut Monien , -# 2014 Vincent Delecroix <20100.delecroix@gmail.com> +# 2014 Vincent Delecroix <20100.delecroix@gmail.com>, +# 2015 Stefan Kraemer # # Distributed under the terms of the GNU General Public License (GPL) # @@ -85,20 +86,21 @@ def _hyperbolic_arc(self, z0, z3, first=False): the hyperbolic arc between the complex numbers z0 and z3 in the hyperbolic plane. """ - if (z0-z3).real() == 0: + z0, z3 = (CC(z0), CC(z3)) + p = (abs(z0)*abs(z0)-abs(z3)*abs(z3))/(z0-z3).real()/2 + r = abs(z0-p) + + if abs(z3-z0)/r < 0.1: self.path.append([(z0.real(), z0.imag()), (z3.real(), z3.imag())]) return - z0, z3 = (CC(z0), CC(z3)) + if z0.imag() == 0 and z3.imag() == 0: p = (z0.real()+z3.real())/2 - r = abs(z0-p) zm = CC(p, r) self._hyperbolic_arc(z0, zm, first) self._hyperbolic_arc(zm, z3) return else: - p = (abs(z0)*abs(z0)-abs(z3)*abs(z3))/(z0-z3).real()/2 - r = abs(z0-p) zm = ((z0+z3)/2-p)/abs((z0+z3)/2-p)*r+p t = (8*zm-4*(z0+z3)).imag()/3/(z3-z0).real() z1 = z0 + t*CC(z0.imag(), (p-z0.real())) From db042e0ecc3b30a1e41f39bca38e4ac59bb9e4fc Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 28 Sep 2015 18:00:49 +0200 Subject: [PATCH 258/421] Trac #17693: fix ReSt and hyperlink issues --- src/sage/data_structures/mutable_poset.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 63a18d44611..be5e2607f35 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -690,7 +690,7 @@ def _copy_all_linked_(self, memo, poset, mapping): - ``poset`` -- the poset to which the newly created shells belongs. Note that the elements are not inserted into ``poset``; this is done in the calling method - :meth:`_copy_shells_`. + :meth:`MutablePoset._copy_shells_`. - ``mapping`` -- a function which is applied on each of the elements. @@ -2278,7 +2278,7 @@ def remove(self, key, raise_key_error=True): .. NOTE:: As with Python's ``set``, the methods :meth:`remove` - and meth:`discard` only differ in their behavior when an + and :meth:`discard` only differ in their behavior when an element is not contained in the poset: :meth:`remove` raises a ``KeyError`` whereas :meth:`discard` does not raise any exception. @@ -2459,7 +2459,7 @@ def discard(self, key, raise_key_error=False): .. NOTE:: As with Python's ``set``, the methods :meth:`remove` - and meth:`discard` only differ in their behavior when an + and :meth:`discard` only differ in their behavior when an element is not contained in the poset: :meth:`remove` raises a ``KeyError`` whereas :meth:`discard` does not raise any exception. @@ -3165,7 +3165,7 @@ def merge(self, key=None, reverse=False): .. NOTE:: - ``can_merge` is applied in the sense of the condition of + ``can_merge`` is applied in the sense of the condition of depth first iteration, i.e., once ``can_merge`` fails, the successors/predecessors are no longer tested. From bbe02ef463cdd3536bd907892c68e73792790525 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 28 Sep 2015 18:01:14 +0200 Subject: [PATCH 259/421] Trac #17693: minor language issues and typos --- src/sage/data_structures/mutable_poset.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index be5e2607f35..363699c4cc1 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -176,7 +176,7 @@ class MutablePosetShell(SageObject): - a ``'null'`` (an element smaller than each other element; it has no predecessors) and - - a ``'oo'`` (an element larger than each other element; + - an ``'oo'`` (an element larger than each other element; it has no successors). EXAMPLES:: @@ -635,7 +635,7 @@ def eq(self, other): .. NOTE:: This method compares the keys of the elements contained - in the (non-special) shells. In particlar, + in the (non-special) shells. In particular, elements/shells with the same key are considered as equal. .. SEEALSO:: @@ -3172,9 +3172,9 @@ def merge(self, key=None, reverse=False): .. NOTE:: The motivation for such a merge behavior comes from - asymptotic expansions: `O(n^3)` is merges with, for + asymptotic expansions: `O(n^3)` merges with, for example, `3n^2` or `O(n)` to `O(n^3)` (as `n` tends to - `\infty`; see :wikipedia:`Big_O_notation`) + `\infty`; see :wikipedia:`Big_O_notation`). EXAMPLES:: From 4deddbd1a09e0e60273797cb89becdc651325b9c Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 28 Sep 2015 18:02:23 +0200 Subject: [PATCH 260/421] Trac #17693: remove comment on non-commutativity for difference and update_difference --- src/sage/data_structures/mutable_poset.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 363699c4cc1..878459e6f3a 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2692,9 +2692,6 @@ def difference(self, *other): The key of an element is used for comparison. Thus elements with the same key are considered as equal. - Due to keys and a ``merge`` function (see :class:`MutablePoset`) - this operation might not be commutative. - EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -2749,9 +2746,6 @@ def difference_update(self, *other): The key of an element is used for comparison. Thus elements with the same key are considered as equal. - Due to keys and a ``merge`` function (see :class:`MutablePoset`) - this operation might not be commutative. - EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP From 62cb899564ef038d3ca230de0bf00e19a7fe8b47 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 25 Sep 2015 22:48:16 +0200 Subject: [PATCH 261/421] Revert autotools dependencies --- build/pkgs/autotools/dependencies | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 build/pkgs/autotools/dependencies diff --git a/build/pkgs/autotools/dependencies b/build/pkgs/autotools/dependencies deleted file mode 100644 index 3546cda4614..00000000000 --- a/build/pkgs/autotools/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -# no dependencies - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. From 954c0d03b85680cb45534ddb7a920aa4ab451357 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 28 Sep 2015 15:13:47 -0300 Subject: [PATCH 262/421] Trac 19299: remove a duplicated line --- src/sage/sets/cartesian_product.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/sets/cartesian_product.py b/src/sage/sets/cartesian_product.py index bf44ea3ba50..0ecfd6917e4 100644 --- a/src/sage/sets/cartesian_product.py +++ b/src/sage/sets/cartesian_product.py @@ -285,7 +285,6 @@ def cartesian_factors(self): sage: A = cartesian_product([ZZ, RR]) sage: A((1, 1.23)).cartesian_factors() - sage: A((1, 1.23)).cartesian_factors() (1, 1.23000000000000) sage: type(_) From f6b88ed88f106904bd548e1d2cafbaca75b75d1e Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 28 Sep 2015 18:19:14 +0200 Subject: [PATCH 263/421] trac #19301: Cleanup in dense_graph.pyx --- src/sage/graphs/base/dense_graph.pyx | 111 +++++++++++---------------- 1 file changed, 44 insertions(+), 67 deletions(-) diff --git a/src/sage/graphs/base/dense_graph.pyx b/src/sage/graphs/base/dense_graph.pyx index f1abc3d4128..e9da1a2a239 100644 --- a/src/sage/graphs/base/dense_graph.pyx +++ b/src/sage/graphs/base/dense_graph.pyx @@ -102,18 +102,16 @@ from ``CGraph`` (for explanation, refer to the documentation there):: It also contains the following variables:: - cdef int radix_div_shift - cdef int radix_mod_mask cdef int num_longs cdef unsigned long *edges The array ``edges`` is a series of bits which are turned on or off, and due to this, dense graphs only support graphs without edge labels and with no multiple -edges. The ints ``radix_div_shift`` and ``radix_mod_mask`` are simply for doing -efficient division by powers of two, and ``num_longs`` stores the length of the -``edges`` array. Recall that this length reflects the number of available -vertices, not the number of "actual" vertices. For more details about this, -refer to the documentation for ``CGraph``. +edges. ``num_longs`` stores the length of the ``edges`` array. Recall that this +length reflects the number of available vertices, not the number of "actual" +vertices. For more details about this, refer to the documentation for +``CGraph``. + """ #******************************************************************************* @@ -125,6 +123,11 @@ refer to the documentation for ``CGraph``. include 'sage/data_structures/bitset.pxi' +from libc.string cimport memcpy + +cdef int radix = sizeof(unsigned long) * 8 # number of bits per 'unsigned long' +cdef int radix_mod_mask = radix - 1 # (assumes that radis is a power of 2) + cdef class DenseGraph(CGraph): """ Compiled dense graphs. @@ -151,7 +154,6 @@ cdef class DenseGraph(CGraph): for use in pickling. """ - def __cinit__(self, int nverts, int extra_vertices = 10, verts = None, arcs = None): """ Allocation and initialization happen in one place. @@ -167,35 +169,24 @@ cdef class DenseGraph(CGraph): """ if nverts == 0 and extra_vertices == 0: raise RuntimeError('Dense graphs must allocate space for vertices!') - cdef int radix = sizeof(unsigned long) << 3 - self.radix_mod_mask = radix - 1 - cdef int i = 0 - while ((1)<> self.radix_div_shift - if total_verts & self.radix_mod_mask: - i += 1 - self.num_longs = i + # self.num_longs = "ceil(total_verts/radix)" + self.num_longs = total_verts / radix + (0 != (total_verts & radix_mod_mask)) - self.edges = sage_malloc(total_verts * self.num_longs * sizeof(unsigned long)) - self.in_degrees = sage_malloc(total_verts * sizeof(int)) - self.out_degrees = sage_malloc(total_verts * sizeof(int)) + self.edges = sage_calloc(total_verts * self.num_longs, sizeof(unsigned long)) + self.in_degrees = sage_calloc(total_verts, sizeof(int)) + self.out_degrees = sage_calloc(total_verts, sizeof(int)) if not self.edges or not self.in_degrees or not self.out_degrees: - if self.edges: sage_free(self.edges) - if self.in_degrees: sage_free(self.in_degrees) - if self.out_degrees: sage_free(self.out_degrees) + sage_free(self.edges) + sage_free(self.in_degrees) + sage_free(self.out_degrees) raise MemoryError - for i from 0 <= i < self.num_longs * total_verts: - self.edges[i] = 0 - for i from 0 <= i < total_verts: - self.in_degrees[i] = 0 - self.out_degrees[i] = 0 + bitset_init(self.active_vertices, total_verts) bitset_set_first_n(self.active_vertices, self.num_verts) @@ -261,6 +252,7 @@ cdef class DenseGraph(CGraph): cdef int i, j if total_verts == 0: raise RuntimeError('Dense graphs must allocate space for vertices!') + cdef bitset_t bits cdef int min_verts, min_longs, old_longs = self.num_longs if total_verts < self.active_vertices.size: @@ -276,42 +268,27 @@ cdef class DenseGraph(CGraph): min_verts = self.active_vertices.size min_longs = self.num_longs - i = total_verts >> self.radix_div_shift - if total_verts & self.radix_mod_mask: - i += 1 - self.num_longs = i - if min_longs == -1: min_longs = self.num_longs + # self.num_longs = "ceil(total_verts/radix)" + self.num_longs = total_verts / radix + (0 != (total_verts & radix_mod_mask)) - cdef unsigned long *new_edges = sage_malloc(total_verts * self.num_longs * sizeof(unsigned long)) + if min_longs == -1: + min_longs = self.num_longs + # Resize of self.edges + cdef unsigned long *new_edges = sage_calloc(total_verts * self.num_longs, sizeof(unsigned long)) for i from 0 <= i < min_verts: - for j from 0 <= j < min_longs: - new_edges[i*self.num_longs + j] = self.edges[i*old_longs + j] - for j from min_longs <= j < self.num_longs: - new_edges[i*self.num_longs + j] = 0 - for i from min_verts <= i < total_verts: - for j from 0 <= j < self.num_longs: - new_edges[i*self.num_longs + j] = 0 + memcpy(new_edges+i*self.num_longs, self.edges+i*old_longs, min_longs*sizeof(unsigned long)) + sage_free(self.edges) self.edges = new_edges - self.in_degrees = sage_realloc(self.in_degrees, total_verts * sizeof(int)) + self.in_degrees = sage_realloc(self.in_degrees , total_verts * sizeof(int)) self.out_degrees = sage_realloc(self.out_degrees, total_verts * sizeof(int)) - cdef int first_limb - cdef unsigned long zero_gate - if total_verts > self.active_vertices.size: - first_limb = (self.active_vertices.size >> self.radix_div_shift) - zero_gate = (1) << (self.active_vertices.size & self.radix_mod_mask) - zero_gate -= 1 - for i from 0 <= i < total_verts: - self.edges[first_limb] &= zero_gate - for j from first_limb < j < self.num_longs: - self.edges[j] = 0 - - for i from self.active_vertices.size <= i < total_verts: - self.in_degrees[i] = 0 - self.out_degrees[i] = 0 + for i in range(self.active_vertices.size, total_verts): + self.in_degrees[i] = 0 + self.out_degrees[i] = 0 + bitset_realloc(self.active_vertices, total_verts) ################################### @@ -326,8 +303,8 @@ cdef class DenseGraph(CGraph): u, v -- non-negative integers """ - cdef int place = (u * self.num_longs) + (v >> self.radix_div_shift) - cdef unsigned long word = (1) << (v & self.radix_mod_mask) + cdef int place = (u * self.num_longs) + (v / radix) + cdef unsigned long word = (1) << (v & radix_mod_mask) if not self.edges[place] & word: self.in_degrees[v] += 1 self.out_degrees[u] += 1 @@ -373,9 +350,9 @@ cdef class DenseGraph(CGraph): 1 -- True """ - cdef int place = (u * self.num_longs) + (v >> self.radix_div_shift) - cdef unsigned long word = (1) << (v & self.radix_mod_mask) - return (self.edges[place] & word) >> (v & self.radix_mod_mask) + cdef int place = (u * self.num_longs) + (v / radix) + cdef unsigned long word = (1) << (v & radix_mod_mask) + return (self.edges[place] & word) >> (v & radix_mod_mask) cpdef bint has_arc(self, int u, int v) except -1: """ @@ -409,8 +386,8 @@ cdef class DenseGraph(CGraph): u, v -- non-negative integers, must be in self """ - cdef int place = (u * self.num_longs) + (v >> self.radix_div_shift) - cdef unsigned long word = (1) << (v & self.radix_mod_mask) + cdef int place = (u * self.num_longs) + (v / radix) + cdef unsigned long word = (1) << (v & radix_mod_mask) if self.edges[place] & word: self.in_degrees[v] -= 1 self.out_degrees[u] -= 1 @@ -527,8 +504,8 @@ cdef class DenseGraph(CGraph): there were more """ - cdef int place = v >> self.radix_div_shift - cdef unsigned long word = (1) << (v & self.radix_mod_mask) + cdef int place = v / radix + cdef unsigned long word = (1) << (v & radix_mod_mask) cdef int i, num_nbrs = 0 for i from 0 <= i < self.active_vertices.size: if self.edges[place + i*self.num_longs] & word: From 548b8f012647c1fae0ab8d080f2f27a382c39d51 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 28 Sep 2015 20:24:58 +0200 Subject: [PATCH 264/421] trac #19301: complement() --- src/sage/graphs/base/dense_graph.pyx | 37 ++++++++++++++++++++++++++++ src/sage/graphs/generic_graph.py | 13 ++++------ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/base/dense_graph.pyx b/src/sage/graphs/base/dense_graph.pyx index e9da1a2a239..29bf399164d 100644 --- a/src/sage/graphs/base/dense_graph.pyx +++ b/src/sage/graphs/base/dense_graph.pyx @@ -421,6 +421,43 @@ cdef class DenseGraph(CGraph): self.check_vertex(v) self.del_arc_unsafe(u,v) + def complement(self): + r""" + Replaces the graph with its complement + + .. NOTE:: + + Assumes that the graph has no loop. + + EXAMPLE:: + + sage: from sage.graphs.base.dense_graph import DenseGraph + sage: G = DenseGraph(5) + sage: G.add_arc(0,1) + sage: G.has_arc(0,1) + True + sage: G.complement() + sage: G.has_arc(0,1) + False + """ + cdef int num_arcs_old = self.num_arcs + + # The following cast assumes that mp_limb_t is an unsigned long. + # (this assumption is already made in bitset.pxi) + cdef unsigned long * active_vertices_bitset + active_vertices_bitset = self.active_vertices.bits + + cdef int i,j + for i in range(self.active_vertices.size): + if bitset_in(self.active_vertices,i): + self.add_arc_unsafe(i,i) + for j in range(self.num_longs): # the actual job + self.edges[i*self.num_longs+j] ^= active_vertices_bitset[j] + self.in_degrees[i] = self.num_verts-self.in_degrees[i] + self.out_degrees[i] = self.num_verts-self.out_degrees[i] + + self.num_arcs = self.num_verts*(self.num_verts-1) - num_arcs_old + ################################### # Neighbor functions ################################### diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 84938f5c7eb..fb80ead83ea 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -16338,17 +16338,14 @@ def complement(self): Graph on 10 vertices """ - if self.has_multiple_edges(): - raise TypeError('complement not well defined for (di)graphs with multiple edges') self._scream_if_not_simple() - G = copy(self) - G.delete_edges(G.edges()) + + G = self.copy(data_structure='dense') + G._backend.c_graph()[0].complement() + if self.name(): G.name("complement({})".format(self.name())) - for u in self: - for v in self: - if not self.has_edge(u,v): - G.add_edge(u,v) + if getattr(self, '_immutable', False): return G.copy(immutable=True) return G From 173cc9beb820fdcb97b5323297df28ec1acc89f6 Mon Sep 17 00:00:00 2001 From: Andrew Gainer-Dewar Date: Mon, 28 Sep 2015 15:53:12 -0400 Subject: [PATCH 265/421] Fix bogus doc ref to polyhedron.interior_point() --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 0d3fe99bad4..4892e002b6b 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -1909,7 +1909,7 @@ def center(self): """ Return the average of the vertices. - See also :meth:`interior_point`. + See also :meth:`representative_point`. OUTPUT: From 0d806488874034fc5d47ffd6603880256b4e79f2 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 28 Sep 2015 22:18:56 +0200 Subject: [PATCH 266/421] trac #19301: Doctest --- src/sage/graphs/generic_graph.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index fb80ead83ea..e4feaeb6694 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -16291,8 +16291,7 @@ def add_path(self, vertices): vert1 = v def complement(self): - """ - Returns the complement of the (di)graph. + """Returns the complement of the (di)graph. The complement of a graph has the same vertices, but exactly those edges that are not in the original graph. This is not well defined @@ -16320,7 +16319,10 @@ def complement(self): sage: G.complement() Traceback (most recent call last): ... - TypeError: complement not well defined for (di)graphs with multiple edges + ValueError: This method is not known to work on graphs with + multiedges. Perhaps this method can be updated to handle them, but + in the meantime if you want to use it please disallow multiedges + using allow_multiple_edges(). TESTS: From c590ad18c93f7b0376145142c4da1448cda05b8f Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 29 Sep 2015 05:48:25 +0200 Subject: [PATCH 267/421] trac #19279: Remove duplicate references --- src/sage/combinat/designs/incidence_structures.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index bab1c8df92c..4597204b8a8 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -1662,8 +1662,8 @@ def is_generalized_quadrangle(self, verbose=False, parameters=False): .. NOTE:: - Observe that some references (e.g. [PT09]_ or Wikipedia) only allow - *regular* generalized quadrangles. To use such a definition, see the + Some references (e.g. [PT09]_ or [GQwiki]_) only allow *regular* + generalized quadrangles. To use such a definition, see the ``parameters`` optional argument described below, or the methods :meth:`is_regular` and :meth:`is_uniform`. @@ -1705,13 +1705,6 @@ def is_generalized_quadrangle(self, verbose=False, parameters=False): sage: hypergraphs.CompleteUniform(4,2).is_generalized_quadrangle(verbose=1) Some point has two projections on some line. False - - REFERENCE: - - .. [PT09] S. Payne, J. Thas, - Finite generalized quadrangles, - European Mathematical Society, 2009. - http://cage.ugent.be/~bamberg/FGQ.pdf """ # The distance between a point and a line in the incidence graph is odd # and must be <= 3. Thus, the diameter is at most 4 From f7bd83a0f256e186fe5f8ecc9c4501d0643f7811 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 29 Sep 2015 08:50:11 +0200 Subject: [PATCH 268/421] Trac #17693, comment 43, 7: do caching of key in __init__ --- src/sage/data_structures/mutable_poset.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 878459e6f3a..41e5cd3a32c 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -208,6 +208,7 @@ def __init__(self, poset, element): """ self._poset_ = poset self._element_ = element + self._key_ = self.poset.get_key(element) self._predecessors_ = set() self._successors_ = set() super(MutablePosetShell, self).__init__() @@ -290,16 +291,12 @@ def key(self): ....: return k sage: R = MP(key=k) sage: h = MutablePosetShell(R, (1, 2)) - sage: h.key; h.key key (1, 2) + sage: h.key; h.key (1, 2) (1, 2) """ - # workaround for #19281 - # (Use @property @cached_method once #19281 is fixed.) - if not hasattr(self, '_cached_key_'): - self._cached_key_ = self.poset.get_key(self._element_) - return self._cached_key_ + return self._key_ def predecessors(self, reverse=False): From a554d697526397fca280239fa11b210cbec94c80 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 29 Sep 2015 08:56:40 +0200 Subject: [PATCH 269/421] rac #17693, comment 43, 18: use .is_oo to test for oo in doctest --- src/sage/data_structures/mutable_poset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 41e5cd3a32c..6465689ac7a 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -710,7 +710,7 @@ def _copy_all_linked_(self, memo, poset, mapping): sage: z.poset is Q True sage: oo = z.successors().pop() - sage: oo == P.oo + sage: oo.is_oo() True """ try: From 3ac7ed1b8f318620cbeee52f570d6e8ef7bb23f9 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 29 Sep 2015 08:57:05 +0200 Subject: [PATCH 270/421] rac #17693, comment 43, 18: add a doctest "oo is Q.oo" --- src/sage/data_structures/mutable_poset.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 6465689ac7a..3905ac1510f 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -712,6 +712,14 @@ def _copy_all_linked_(self, memo, poset, mapping): sage: oo = z.successors().pop() sage: oo.is_oo() True + + Note that :meth:`_copy_all_linked_` does not change the mutable + poset `Q` (this is done in the calling method + :meth:`MutablePoset._copy_shells_`). Thus we have + :: + + sage: oo is Q.oo + False """ try: return memo[id(self)] From 2f675b3a6fff5e50770e85aedc23f38afbe275f0 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 29 Sep 2015 09:03:06 +0200 Subject: [PATCH 271/421] Trac #17693, comment 43, 18: add a doctest in eq (comparing elements in different posets) --- src/sage/data_structures/mutable_poset.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 3905ac1510f..6498e069796 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -663,6 +663,16 @@ def eq(self, other): False sage: oo == z False + + Comparing elements in different mutable posets is possible; their + shells are equal if their elements are:: + + sage: S = MP([42]); s = S.shell(42) + sage: T = MP([42]); t = T.shell(42) + sage: s == t + True + sage: S.oo == T.oo + True """ if self.element is None and other.element is None: return self.is_null() == other.is_null() From f064a3b6434551b97a0bbb36d918b8b2d1ffe3d6 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 29 Sep 2015 09:14:02 +0200 Subject: [PATCH 272/421] Trac #17693, comment 43, 22: covers --> lower_covers, upper_covers --- src/sage/data_structures/mutable_poset.py | 103 +++++++++++++++++++--- 1 file changed, 89 insertions(+), 14 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 6498e069796..e7412ab6357 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -747,16 +747,14 @@ def _copy_all_linked_(self, memo, poset, mapping): return new - def covers(self, shell, reverse=False): + def lower_covers(self, shell, reverse=False): r""" - Return the lower or upper covers of the specified ``shell``; + Return the lower covers of the specified ``shell``; the search is started at this (``self``) shell. A lower cover of `x` is an element `y` of the poset such that `y < x` and there is no element `z` of the poset - so that `x < z < y`. Likewise, - an upper cover of `x` is an element `y` such that `x < y` and - there is no element `z` so that `x < z < y`. + so that `y < z < x`. INPUT: @@ -765,8 +763,9 @@ def covers(self, shell, reverse=False): If ``shell`` is contained in the poset, then use the more efficient methods :meth:`predecessors` and :meth:`successors`. - - ``reverse`` -- (default: ``False``) if not set, then find - the lower covers, otherwise find the upper covers. + - ``reverse`` -- (default: ``False``) if set, then find + the upper covers (see also :meth:`upper_covers`) + instead of the lower covers. OUTPUT: @@ -792,12 +791,12 @@ def covers(self, shell, reverse=False): ....: T((4, 4)), T((1, 2)), T((2, 2))]) sage: e = P.shell(T((2, 2))); e (2, 2) - sage: sorted(P.null.covers(e), + sage: sorted(P.null.lower_covers(e), ....: key=lambda c: repr(c.element)) [(1, 2), (2, 1)] sage: set(_) == e.predecessors() True - sage: sorted(P.oo.covers(e, reverse=True), + sage: sorted(P.oo.upper_covers(e), ....: key=lambda c: repr(c.element)) [(4, 4)] sage: set(_) == e.successors() @@ -807,27 +806,103 @@ def covers(self, shell, reverse=False): sage: Q = MP([T((3, 2))]) sage: f = next(Q.shells()) - sage: sorted(P.null.covers(f), + sage: sorted(P.null.lower_covers(f), ....: key=lambda c: repr(c.element)) [(2, 2)] - sage: sorted(P.oo.covers(f, reverse=True), + sage: sorted(P.oo.upper_covers(f), ....: key=lambda c: repr(c.element)) [(4, 4)] .. SEEALSO:: + :meth:`upper_covers`, :meth:`predecessors`, :meth:`successors`, :class:`MutablePoset`. """ if self == shell: return set() - covers = set().union(*(e.covers(shell, reverse) + covers = set().union(*(e.lower_covers(shell, reverse) for e in self.successors(reverse) if e.le(shell, reverse))) return covers or set([self]) + def upper_covers(self, shell, reverse=False): + r""" + Return the upper covers of the specified ``shell``; + the search is started at this (``self``) shell. + + An upper cover of `x` is an element `y` of the poset + such that `x < y` and there is no element `z` of the poset + so that `x < z < y`. + + INPUT: + + - ``shell`` -- the shell for which to find the covering shells. + There is no restrition of ``shell`` being contained in the poset. + If ``shell`` is contained in the poset, then use the more efficient + methods :meth:`predecessors` and :meth:`successors`. + + - ``reverse`` -- (default: ``False``) if set, then find + the lower covers (see also :meth:`lower_covers`) + instead of the upper covers. + + OUTPUT: + + A set of :class:`shells `. + + .. NOTE:: + + Suppose ``reverse`` is ``False``. This method starts at + the calling shell (``self``) and searches towards ``'null'``. + Thus, only shells which are (not necessarily + direct) predecessors of this shell are considered. + + If ``reverse`` is ``True``, then the reverse direction is + taken. + + EXAMPLES:: + + sage: from sage.data_structures.mutable_poset import MutablePoset as MP + sage: class T(tuple): + ....: def __le__(left, right): + ....: return all(l <= r for l, r in zip(left, right)) + sage: P = MP([T((1, 1)), T((1, 3)), T((2, 1)), + ....: T((4, 4)), T((1, 2)), T((2, 2))]) + sage: e = P.shell(T((2, 2))); e + (2, 2) + sage: sorted(P.null.lower_covers(e), + ....: key=lambda c: repr(c.element)) + [(1, 2), (2, 1)] + sage: set(_) == e.predecessors() + True + sage: sorted(P.oo.upper_covers(e), + ....: key=lambda c: repr(c.element)) + [(4, 4)] + sage: set(_) == e.successors() + True + + :: + + sage: Q = MP([T((3, 2))]) + sage: f = next(Q.shells()) + sage: sorted(P.null.lower_covers(f), + ....: key=lambda c: repr(c.element)) + [(2, 2)] + sage: sorted(P.oo.upper_covers(f), + ....: key=lambda c: repr(c.element)) + [(4, 4)] + + .. SEEALSO:: + + :meth:`predecessors`, + :meth:`successors`, + :class:`MutablePoset`. + """ + return self.lower_covers(shell, not reverse) + + def _iter_depth_first_visit_(self, marked, reverse=False, key=None, condition=None): @@ -2258,8 +2333,8 @@ def add(self, element): return new = MutablePosetShell(self, element) - new._predecessors_ = self.null.covers(new, reverse=False) - new._successors_ = self.oo.covers(new, reverse=True) + new._predecessors_ = self.null.lower_covers(new) + new._successors_ = self.oo.upper_covers(new) for s in new.predecessors(): for l in s.successors().intersection(new.successors()): From bea925cec11160275c4b3357c25b0f3d157d482e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 29 Sep 2015 09:16:06 +0200 Subject: [PATCH 273/421] Trac #17693, comment 43, 55: remove comment on non-commutativity --- src/sage/data_structures/mutable_poset.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index e7412ab6357..c7d36b4279f 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2886,9 +2886,6 @@ def intersection(self, *other): The key of an element is used for comparison. Thus elements with the same key are considered as equal. - Due to keys and a ``merge`` function (see :class:`MutablePoset`) - this operation might not be commutative. - EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP @@ -2939,9 +2936,6 @@ def intersection_update(self, *other): The key of an element is used for comparison. Thus elements with the same key are considered as equal. - Due to keys and a ``merge`` function (see :class:`MutablePoset`) - this operation might not be commutative. - EXAMPLES:: sage: from sage.data_structures.mutable_poset import MutablePoset as MP From 4e73b4576e1e949b7257d858fbeb958152df9f66 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 29 Sep 2015 09:22:06 +0200 Subject: [PATCH 274/421] Trac #17693, comment 43, 63: extend/rewrite non-commutativity note --- src/sage/data_structures/mutable_poset.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index c7d36b4279f..d46d930d814 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2708,10 +2708,9 @@ def union_update(self, *other): .. NOTE:: The key of an element is used for comparison. Thus elements with - the same key are considered as equal. - - Due to keys and a ``merge`` function (see :class:`MutablePoset`) - this operation might not be commutative. + the same key are considered as equal; + ``A.union_update(B)`` and ``B.union_update(A)`` might + result in different posets. .. TODO:: @@ -2934,7 +2933,9 @@ def intersection_update(self, *other): .. NOTE:: The key of an element is used for comparison. Thus elements with - the same key are considered as equal. + the same key are considered as equal; + ``A.union_update(B)`` and ``B.union_update(A)`` might + result in different posets. EXAMPLES:: @@ -2977,9 +2978,8 @@ def symmetric_difference(self, other): .. NOTE:: - If this poset uses a ``key``-function, then all - comparisons are performed on the keys of the elements (and - not on the elements themselves). + The key of an element is used for comparison. Thus elements with + the same key are considered as equal. EXAMPLES:: @@ -3021,9 +3021,10 @@ def symmetric_difference_update(self, other): .. NOTE:: - If this poset uses a ``key``-function, then all - comparisons are performed on the keys of the elements (and - not on the elements themselves). + The key of an element is used for comparison. Thus elements with + the same key are considered as equal; + ``A.union_update(B)`` and ``B.union_update(A)`` might + result in different posets. EXAMPLES:: From e8d8d2c4b4975054c2470d2ee88e937ae1044167 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Tue, 29 Sep 2015 10:05:07 +0200 Subject: [PATCH 275/421] Trac #17693: typos --- src/sage/data_structures/mutable_poset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index d46d930d814..d95798f363b 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -759,7 +759,7 @@ def lower_covers(self, shell, reverse=False): INPUT: - ``shell`` -- the shell for which to find the covering shells. - There is no restrition of ``shell`` being contained in the poset. + There is no restriction of ``shell`` being contained in the poset. If ``shell`` is contained in the poset, then use the more efficient methods :meth:`predecessors` and :meth:`successors`. @@ -840,7 +840,7 @@ def upper_covers(self, shell, reverse=False): INPUT: - ``shell`` -- the shell for which to find the covering shells. - There is no restrition of ``shell`` being contained in the poset. + There is no restriction of ``shell`` being contained in the poset. If ``shell`` is contained in the poset, then use the more efficient methods :meth:`predecessors` and :meth:`successors`. From 4b7f929e34a497e1180cb51efc74af4fdf7f79df Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Tue, 29 Sep 2015 10:05:25 +0200 Subject: [PATCH 276/421] Trac #17693: fix two copy&paste errors --- src/sage/data_structures/mutable_poset.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index d95798f363b..882a1135648 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -2934,7 +2934,7 @@ def intersection_update(self, *other): The key of an element is used for comparison. Thus elements with the same key are considered as equal; - ``A.union_update(B)`` and ``B.union_update(A)`` might + ``A.intersection_update(B)`` and ``B.intersection_update(A)`` might result in different posets. EXAMPLES:: @@ -3023,7 +3023,8 @@ def symmetric_difference_update(self, other): The key of an element is used for comparison. Thus elements with the same key are considered as equal; - ``A.union_update(B)`` and ``B.union_update(A)`` might + ``A.symmetric_difference_update(B)`` and + ``B.symmetric_difference_update(A)`` might result in different posets. EXAMPLES:: From a0b3d7bce3325e271e53b0a83047a6f80ac28a22 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Tue, 29 Sep 2015 10:05:47 +0200 Subject: [PATCH 277/421] Trac #17693: ReSt improvement --- src/sage/data_structures/mutable_poset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 882a1135648..3275be203ff 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -724,7 +724,7 @@ def _copy_all_linked_(self, memo, poset, mapping): True Note that :meth:`_copy_all_linked_` does not change the mutable - poset `Q` (this is done in the calling method + poset ``Q`` (this is done in the calling method :meth:`MutablePoset._copy_shells_`). Thus we have :: From ed50a2c763c6e89059a87600e76fc82e7a378f07 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 29 Sep 2015 10:54:36 +0200 Subject: [PATCH 278/421] Remove comparison boilerplate - part 4 --- src/sage/rings/complex_ball_acb.pyx | 143 ++++---- src/sage/rings/complex_double.pyx | 20 +- src/sage/rings/complex_interval.pyx | 28 +- src/sage/rings/complex_number.pyx | 8 +- .../rings/finite_rings/element_givaro.pyx | 13 - .../rings/finite_rings/element_ntl_gf2e.pyx | 22 +- .../rings/finite_rings/element_pari_ffelt.pyx | 56 ++- .../rings/finite_rings/hom_finite_field.pyx | 11 - src/sage/rings/finite_rings/integer_mod.pyx | 12 - .../rings/laurent_series_ring_element.pyx | 3 - src/sage/rings/morphism.pyx | 51 +-- .../number_field/number_field_element.pyx | 5 +- .../number_field_element_quadratic.pyx | 21 +- src/sage/rings/power_series_poly.pyx | 18 - src/sage/rings/power_series_ring_element.pyx | 28 +- src/sage/rings/rational.pyx | 7 +- src/sage/rings/real_arb.pyx | 341 +++++++++--------- src/sage/rings/real_lazy.pyx | 91 +---- src/sage/rings/real_mpfi.pyx | 22 +- 19 files changed, 340 insertions(+), 560 deletions(-) diff --git a/src/sage/rings/complex_ball_acb.pyx b/src/sage/rings/complex_ball_acb.pyx index 6823960c101..0c5b9e6ec29 100644 --- a/src/sage/rings/complex_ball_acb.pyx +++ b/src/sage/rings/complex_ball_acb.pyx @@ -583,7 +583,7 @@ cdef class ComplexBall(Element): """ return acb_is_exact(self.value) - def __richcmp__(left, right, int op): + cpdef _richcmp_(left, Element right, int op): """ Compare ``left`` and ``right``. @@ -607,19 +607,6 @@ cdef class ComplexBall(Element): False sage: a == b # optional - arb False - """ - return (left)._richcmp(right, op) - - cpdef _richcmp_(left, Element right, int op): - """ - Compare ``left`` and ``right``. - - For more information, see :mod:`sage.rings.complex_ball_acb`. - - EXAMPLES:: - - sage: from sage.rings.complex_ball_acb import ComplexBallField # optional - arb - sage: CBF = ComplexBallField() # optional - arb sage: a = CBF(1, 2) # optional - arb sage: b = CBF(1, 2) # optional - arb sage: a is b # optional - arb @@ -629,70 +616,70 @@ cdef class ComplexBall(Element): TESTS: - Balls whose intersection consists of one point:: - - sage: a = CBF(RIF(1, 2), RIF(1, 2)) # optional - arb - sage: b = CBF(RIF(2, 4), RIF(2, 4)) # optional - arb - sage: a < b # optional - arb - Traceback (most recent call last): - ... - TypeError: No order is defined for ComplexBalls. - sage: a > b # optional - arb - Traceback (most recent call last): - ... - TypeError: No order is defined for ComplexBalls. - sage: a <= b # optional - arb - Traceback (most recent call last): - ... - TypeError: No order is defined for ComplexBalls. - sage: a >= b # optional - arb - Traceback (most recent call last): - ... - TypeError: No order is defined for ComplexBalls. - sage: a == b # optional - arb - False - sage: a != b # optional - arb - False - - Balls with non-trivial intersection:: - - sage: a = CBF(RIF(1, 4), RIF(1, 4)) # optional - arb - sage: a = CBF(RIF(2, 5), RIF(2, 5)) # optional - arb - sage: a == b # optional - arb - False - sage: a != b # optional - arb - False - - One ball contained in another:: - - sage: a = CBF(RIF(1, 4), RIF(1, 4)) # optional - arb - sage: b = CBF(RIF(2, 3), RIF(2, 3)) # optional - arb - sage: a == b # optional - arb - False - sage: a != b # optional - arb - False - - Disjoint balls:: - - sage: a = CBF(1/3, 1/3) # optional - arb - sage: b = CBF(1/5, 1/5) # optional - arb - sage: a == b # optional - arb - False - sage: a != b # optional - arb - True - - Exact elements:: - - sage: a = CBF(2, 2) # optional - arb - sage: b = CBF(2, 2) # optional - arb - sage: a.is_exact() # optional - arb - True - sage: b.is_exact() # optional - arb - True - sage: a == b # optional - arb - True - sage: a != b # optional - arb - False + Balls whose intersection consists of one point:: + + sage: a = CBF(RIF(1, 2), RIF(1, 2)) # optional - arb + sage: b = CBF(RIF(2, 4), RIF(2, 4)) # optional - arb + sage: a < b # optional - arb + Traceback (most recent call last): + ... + TypeError: No order is defined for ComplexBalls. + sage: a > b # optional - arb + Traceback (most recent call last): + ... + TypeError: No order is defined for ComplexBalls. + sage: a <= b # optional - arb + Traceback (most recent call last): + ... + TypeError: No order is defined for ComplexBalls. + sage: a >= b # optional - arb + Traceback (most recent call last): + ... + TypeError: No order is defined for ComplexBalls. + sage: a == b # optional - arb + False + sage: a != b # optional - arb + False + + Balls with non-trivial intersection:: + + sage: a = CBF(RIF(1, 4), RIF(1, 4)) # optional - arb + sage: a = CBF(RIF(2, 5), RIF(2, 5)) # optional - arb + sage: a == b # optional - arb + False + sage: a != b # optional - arb + False + + One ball contained in another:: + + sage: a = CBF(RIF(1, 4), RIF(1, 4)) # optional - arb + sage: b = CBF(RIF(2, 3), RIF(2, 3)) # optional - arb + sage: a == b # optional - arb + False + sage: a != b # optional - arb + False + + Disjoint balls:: + + sage: a = CBF(1/3, 1/3) # optional - arb + sage: b = CBF(1/5, 1/5) # optional - arb + sage: a == b # optional - arb + False + sage: a != b # optional - arb + True + + Exact elements:: + + sage: a = CBF(2, 2) # optional - arb + sage: b = CBF(2, 2) # optional - arb + sage: a.is_exact() # optional - arb + True + sage: b.is_exact() # optional - arb + True + sage: a == b # optional - arb + True + sage: a != b # optional - arb + False """ cdef ComplexBall lt, rt cdef acb_t difference diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 2dbd9b83612..e181c7d8237 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -795,9 +795,13 @@ cdef class ComplexDoubleElement(FieldElement): """ return hash(complex(self)) - def __richcmp__(left, right, int op): + cpdef int _cmp_(left, Element right) except -2: """ - Rich comparison between ``left`` and ``right``. + We order the complex numbers in dictionary order by real parts then + imaginary parts. + + This order, of course, does not respect the field structure, though + it agrees with the usual order on the real numbers. EXAMPLES:: @@ -807,18 +811,8 @@ cdef class ComplexDoubleElement(FieldElement): -1 sage: cmp(CDF(1 + i), CDF(-1 - i)) 1 - """ - return (left)._richcmp(right, op) - - cpdef int _cmp_(left, Element right) except -2: - """ - We order the complex numbers in dictionary order by real parts then - imaginary parts. - This order, of course, does not respect the field structure, though - it agrees with the usual order on the real numbers. - - EXAMPLES:: + :: sage: CDF(2,3) < CDF(3,1) True diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index 2fc79377f4a..eb358f81872 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -1000,7 +1000,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): """ return self.real().__nonzero__() or self.imag().__nonzero__() - def __richcmp__(left, right, int op): + cpdef _richcmp_(left, Element right, int op): r""" As with the real interval fields this never returns false positives. Thus, `a == b` is ``True`` iff both `a` and `b` represent the same @@ -1037,9 +1037,6 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CDF(1) >= CDF(1) >= CDF.gen() >= CDF.gen() >= 0 >= -CDF.gen() >= CDF(-1) True """ - return (left)._richcmp(right, op) - - cpdef _richcmp_(left, Element right, int op): cdef ComplexIntervalFieldElement lt, rt lt = left rt = right @@ -1072,7 +1069,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): elif op == 5: #>= return real_diff > 0 or (real_diff == 0 and imag_diff >= 0) - def __cmp__(left, right): + cpdef int _cmp_(left, sage.structure.element.Element right) except -2: """ Intervals are compared lexicographically on the 4-tuple: ``(x.real().lower(), x.real().upper(), @@ -1093,27 +1090,18 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): 0 sage: cmp(b, a) 1 - """ - return (left)._cmp(right) - - - cpdef int _cmp_(left, sage.structure.element.Element right) except -2: - """ - Intervals are compared lexicographically on the 4-tuple: - ``(x.real().lower(), x.real().upper(), - x.imag().lower(), x.imag().upper())`` TESTS:: sage: tests = [] sage: for rl in (0, 1): - ... for ru in (rl, rl + 1): - ... for il in (0, 1): - ... for iu in (il, il + 1): - ... tests.append((CIF(RIF(rl, ru), RIF(il, iu)), (rl, ru, il, iu))) + ....: for ru in (rl, rl + 1): + ....: for il in (0, 1): + ....: for iu in (il, il + 1): + ....: tests.append((CIF(RIF(rl, ru), RIF(il, iu)), (rl, ru, il, iu))) sage: for (i1, t1) in tests: - ... for (i2, t2) in tests: - ... assert(cmp(i1, i2) == cmp(t1, t2)) + ....: for (i2, t2) in tests: + ....: assert(cmp(i1, i2) == cmp(t1, t2)) """ cdef int a, b a = mpfi_nan_p(left.__re) diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index 34e941fe73a..8e4de8876a7 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -1123,11 +1123,10 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): """ return complex(mpfr_get_d(self.__re, rnd), mpfr_get_d(self.__im, rnd)) - # return complex(float(self.__re), float(self.__im)) - def __richcmp__(left, right, int op): + cpdef int _cmp_(left, sage.structure.element.Element right) except -2: """ - Rich comparision between ``left`` and ``right``. + Compare ``left`` and ``right``. EXAMPLES:: @@ -1136,9 +1135,6 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: cmp(CC(2, 1), CC(2, 1)) 0 """ - return (left)._richcmp(right, op) - - cpdef int _cmp_(left, sage.structure.element.Element right) except -2: cdef int a, b a = mpfr_nan_p(left.__re) b = mpfr_nan_p((right).__re) diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index c94ee5a675f..403b99c8fd0 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -1297,19 +1297,6 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): return make_FiniteField_givaroElement(cache, cache.objectptr.one) return make_FiniteField_givaroElement(cache, r) - def __richcmp__(left, right, int op): - """ - EXAMPLES:: - - sage: k. = GF(9); k - Finite Field in a of size 3^2 - sage: a == k('a') # indirect doctest - True - sage: a == a + 1 - False - """ - return (left)._richcmp(right, op) - cpdef int _cmp_(left, Element right) except -2: """ Comparison of finite field elements is correct or equality diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index ba7b6991436..7c890f3ac2f 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -803,10 +803,16 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): from sage.groups.generic import power return power(self,exp) - def __richcmp__(left, right, int op): + cpdef int _cmp_(left, Element right) except -2: """ Comparison of finite field elements. + .. NOTE:: + + Finite fields are unordered. However, we adopt the convention that + an element ``e`` is bigger than element ``f`` if its polynomial + representation is bigger. + EXAMPLES:: sage: k. = GF(2^20) @@ -819,13 +825,7 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): sage: e != (e + 1) True - .. NOTE:: - - Finite fields are unordered. However, we adopt the convention that - an element ``e`` is bigger than element ``f`` if its polynomial - representation is bigger. - - EXAMPLES:: + :: sage: K. = GF(2^100) sage: a < a^2 @@ -843,12 +843,6 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): sage: a == a True """ - return (left)._richcmp(right, op) - - cpdef int _cmp_(left, Element right) except -2: - """ - Comparison of finite field elements. - """ (left._parent._cache).F.restore() c = GF2E_equal((left).x, (right).x) if c == 1: diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index 2ab45e245c5..2401a21b95a 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -388,31 +388,13 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): """ Comparison of finite field elements. - TESTS:: - - sage: k. = FiniteField(3^3, impl='pari_ffelt') - sage: a == 1 - False - sage: a^0 == 1 - True - sage: a == a - True - sage: a < a^2 - True - sage: a > a^2 - False - """ - cdef int r - pari_catch_sig_on() - r = cmp_universal(self.val, (other).val) - pari_catch_sig_off() - return r + .. NOTE:: - def __richcmp__(FiniteFieldElement_pari_ffelt left, object right, int op): - """ - Rich comparison of finite field elements. + Finite fields are unordered. However, for the purpose of + this function, we adopt the lexicographic ordering on the + representing polynomials. - EXAMPLE:: + EXAMPLES:: sage: k. = GF(2^20, impl='pari_ffelt') sage: e = k.random_element() @@ -424,13 +406,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): sage: e != (e + 1) True - .. NOTE:: - - Finite fields are unordered. However, for the purpose of - this function, we adopt the lexicographic ordering on the - representing polynomials. - - EXAMPLE:: + :: sage: K. = GF(2^100, impl='pari_ffelt') sage: a < a^2 @@ -447,8 +423,26 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): False sage: a == a True + + TESTS:: + + sage: k. = FiniteField(3^3, impl='pari_ffelt') + sage: a == 1 + False + sage: a^0 == 1 + True + sage: a == a + True + sage: a < a^2 + True + sage: a > a^2 + False """ - return (left)._richcmp(right, op) + cdef int r + pari_catch_sig_on() + r = cmp_universal(self.val, (other).val) + pari_catch_sig_off() + return r cpdef ModuleElement _add_(FiniteFieldElement_pari_ffelt self, ModuleElement right): """ diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 04a45c10142..1ca9fd576c1 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -341,16 +341,10 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): """ return self._section_class(self) - - def __richcmp__(left, right, int op): - return (left)._richcmp(right, op) - - def __hash__(self): return Morphism.__hash__(self) - cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ A class implementing Frobenius endomorphisms on finite fields. @@ -670,11 +664,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ return self.power() == 0 - - def __richcmp__(left, right, int op): - return (left)._richcmp(right, op) - - def __hash__(self): return Morphism.__hash__(self) diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index efc5d11d33b..4b041bba56b 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -1838,10 +1838,6 @@ cdef class IntegerMod_gmp(IntegerMod_abstract): else: return 1 - def __richcmp__(left, right, int op): - return (left)._richcmp(right, op) - - cpdef bint is_one(IntegerMod_gmp self): """ Returns ``True`` if this is `1`, otherwise @@ -2252,10 +2248,6 @@ cdef class IntegerMod_int(IntegerMod_abstract): else: return 1 - def __richcmp__(left, right, int op): - return (left)._richcmp(right, op) - - cpdef bint is_one(IntegerMod_int self): """ Returns ``True`` if this is `1`, otherwise @@ -3080,10 +3072,6 @@ cdef class IntegerMod_int64(IntegerMod_abstract): elif self.ivalue < (right).ivalue: return -1 else: return 1 - def __richcmp__(left, right, int op): - return (left)._richcmp(right, op) - - cpdef bint is_one(IntegerMod_int64 self): """ Returns ``True`` if this is `1`, otherwise diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 391991b4329..f54ada8f8d5 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -869,9 +869,6 @@ cdef class LaurentSeries(AlgebraElement): return self.prec() return min(self.prec(), f.prec()) - def __richcmp__(left, right, int op): - return (left)._richcmp(right, op) - cpdef int _cmp_(self, Element right_r) except -2: r""" Comparison of self and right. diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index 502c1ceb976..594cf9ff34e 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -1137,20 +1137,6 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): _slots['__im_gens'] = self.__im_gens return RingHomomorphism._extra_slots(self, _slots) - def __richcmp__(left, right, int op): - """ - Used internally by the cmp method. - - TESTS:: - - sage: R. = QQ[]; f = R.hom([x,x+y]); g = R.hom([y,x]) - sage: cmp(f,g) # indirect doctest - 1 - sage: cmp(g,f) - -1 - """ - return (left)._richcmp(right, op) - cpdef int _cmp_(self, Element other) except -2: r""" EXAMPLES: @@ -1175,6 +1161,14 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): sage: loads(dumps(f2)) == f2 True + :: + + sage: R. = QQ[]; f = R.hom([x,x+y]); g = R.hom([y,x]) + sage: cmp(f,g) # indirect doctest + 1 + sage: cmp(g,f) + -1 + EXAMPLES: A multivariate quotient over a finite field:: @@ -1441,22 +1435,6 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): _slots['__underlying'] = self.__underlying return RingHomomorphism._extra_slots(self, _slots) - def __richcmp__(left, right, int op): - """ - Used internally by the cmp method. - - TESTS:: - - sage: R. = QQ[]; f = R.hom([x,x+y]); g = R.hom([y,x]) - sage: S. = R[] - sage: fS = S.hom(f,S); gS = S.hom(g,S) - sage: cmp(fS,gS) # indirect doctest - 1 - sage: cmp(gS,fS) # indirect doctest - -1 - """ - return (left)._richcmp(right, op) - cpdef int _cmp_(self, Element other) except -2: r""" EXAMPLES: @@ -1480,6 +1458,16 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): sage: f1P == loads(dumps(f1P)) True + TESTS:: + + sage: R. = QQ[]; f = R.hom([x,x+y]); g = R.hom([y,x]) + sage: S. = R[] + sage: fS = S.hom(f,S); gS = S.hom(g,S) + sage: cmp(fS,gS) # indirect doctest + 1 + sage: cmp(gS,fS) # indirect doctest + -1 + EXAMPLES: A matrix ring over a multivariate quotient over a finite field:: @@ -2109,9 +2097,6 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): codomain = self.codomain() return hash((domain, codomain, ('Frob', self._power))) - def __richcmp__(left, right, int op): - return (left)._richcmp(right, op) - cpdef int _cmp_(left, Element right) except -2: if left is right: return 0 domain = left.domain() diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 1e3d3306f14..030c8d4dda5 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -709,7 +709,7 @@ cdef class NumberFieldElement(FieldElement): raise IndexError, "index must be between 0 and degree minus 1." return self.polynomial()[n] - def __richcmp__(left, right, int op): + cpdef int _cmp_(left, sage.structure.element.Element right) except -2: r""" EXAMPLE:: @@ -719,9 +719,6 @@ cdef class NumberFieldElement(FieldElement): sage: a + 1 < a # indirect doctest False """ - return (left)._richcmp(right, op) - - cpdef int _cmp_(left, sage.structure.element.Element right) except -2: cdef NumberFieldElement _right = right return not (ZZX_equal(left.__numerator, _right.__numerator) and ZZ_equal(left.__denominator, _right.__denominator)) diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index 223ed701c1b..1d8ebcf6c2e 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -658,22 +658,15 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): return test return -test - def __richcmp__(left, right, int op): + cpdef _richcmp_(left, Element _right, int op): r""" - Note: we may implement a more direct way of comparison for integer, - float and quadratic numbers input (ie avoiding coercion). + Rich comparison of elements. TESTS:: sage: K. = QuadraticField(-1) sage: sorted([5*i+1, 2, 3*i+1, 2-i]) [3*i + 1, 5*i + 1, -i + 2, 2] - """ - return (left)._richcmp(right, op) - - cpdef _richcmp_(left, Element _right, int op): - r""" - C implementation of comparison. TESTS: @@ -796,8 +789,8 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): mpz_clear(j) return rich_to_bool_sgn(op, test) - def __cmp__(left, right): - r""" + cpdef int _cmp_(left, Element _right) except -2: + """ Comparisons of elements. When there is a real embedding defined, the comparisons uses comparison @@ -869,12 +862,6 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: map(CDF, l) == sorted(map(CDF, l)) True """ - return (left)._cmp(right) - - cpdef int _cmp_(left, Element _right) except -2: - """ - C implementation of comparison. - """ cdef NumberFieldElement_quadratic right = _right cdef int test diff --git a/src/sage/rings/power_series_poly.pyx b/src/sage/rings/power_series_poly.pyx index e2b3c29cd68..9881351ef45 100644 --- a/src/sage/rings/power_series_poly.pyx +++ b/src/sage/rings/power_series_poly.pyx @@ -89,24 +89,6 @@ cdef class PowerSeries_poly(PowerSeries): """ return self.__class__, (self._parent, self.__f, self._prec, self.__is_gen) - def __richcmp__(left, right, int op): - """ - Used for comparing power series. - - EXAMPLES:: - - sage: R. = ZZ[[]] - sage: f = 1 + t + t^7 - 5*t^10 - sage: g = 1 + t + t^7 - 5*t^10 + O(t^15) - sage: f == f - True - sage: f < g - False - sage: f == g - True - """ - return (left)._richcmp(right, op) - def polynomial(self): """ Return the underlying polynomial of self. diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index ab3f67a20cf..ae25cd36fea 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -315,18 +315,6 @@ cdef class PowerSeries(AlgebraElement): S = self._parent.change_ring(R) return S(self) - def __cmp__(left, right): - """ - Called by comparison operations. - - EXAMPLES:: - - sage: R. = PowerSeriesRing(ZZ) - sage: 1+x^2 < 2-x - True - """ - return (left)._cmp(right) - cpdef int _cmp_(self, Element right) except -2: r""" Comparison of self and ``right``. @@ -358,9 +346,23 @@ cdef class PowerSeries(AlgebraElement): sage: 1 - 2*q + q^2 +O(q^3) == 1 - 2*q^2 + q^2 + O(q^4) False + :: + + sage: R. = ZZ[[]] + sage: 1 + t^2 < 2 - t + True + sage: f = 1 + t + t^7 - 5*t^10 + sage: g = 1 + t + t^7 - 5*t^10 + O(t^15) + sage: f == f + True + sage: f < g + False + sage: f == g + True + TESTS: - Ticket :trac:`9457` is fixed:: + :trac:`9457` is fixed:: sage: A. = PowerSeriesRing(ZZ) sage: g = t + t^3 + t^5 + O(t^6); g diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 63fa7fa0225..d1680334abe 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -709,9 +709,9 @@ cdef class Rational(sage.structure.element.FieldElement): l = self.continued_fraction_list() return ContinuedFraction_periodic(l) - def __richcmp__(left, right, int op): + cpdef int _cmp_(left, sage.structure.element.Element right) except -2: """ - Rich comparison between two rational numbers. + Compare two rational numbers. INPUT: @@ -730,9 +730,6 @@ cdef class Rational(sage.structure.element.FieldElement): sage: 4/5 < 0.8 False """ - return (left)._richcmp(right, op) - - cpdef int _cmp_(left, sage.structure.element.Element right) except -2: cdef int i i = mpq_cmp((left).value, (right).value) if i < 0: return -1 diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index f7ad8c92f67..7c7722dae9e 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -1392,7 +1392,7 @@ cdef class RealBall(RingElement): """ return arb_is_exact(self.value) - def __richcmp__(left, right, int op): + cpdef _richcmp_(left, Element right, int op): """ Compare ``left`` and ``right``. @@ -1400,191 +1400,172 @@ cdef class RealBall(RingElement): EXAMPLES:: - sage: from sage.rings.real_arb import RealBallField # optional - arb - sage: RBF = RealBallField() # optional - arb - sage: a = RBF(1) # optional - arb - sage: b = RBF(1) # optional - arb - sage: a is b # optional - arb - False - sage: a == b # optional - arb - True - sage: a = RBF(1/3) # optional - arb - sage: a.is_exact() # optional - arb - False - sage: b = RBF(1/3) # optional - arb - sage: b.is_exact() # optional - arb - False - sage: a == b # optional - arb - False - """ - return (left)._richcmp(right, op) + sage: from sage.rings.real_arb import RealBallField # optional - arb + sage: RBF = RealBallField() # optional - arb + sage: a = RBF(1) # optional - arb + sage: b = RBF(1) # optional - arb + sage: a is b # optional - arb + False + sage: a == b # optional - arb + True + sage: a = RBF(1/3) # optional - arb + sage: a.is_exact() # optional - arb + False + sage: b = RBF(1/3) # optional - arb + sage: b.is_exact() # optional - arb + False + sage: a == b # optional - arb + False - cpdef _richcmp_(left, Element right, int op): - """ - Compare ``left`` and ``right``. + TESTS: - For more information, see :mod:`sage.rings.real_arb`. + Balls whose intersection consists of one point:: - EXAMPLES:: + sage: a = RBF(RIF(1, 2)) # optional - arb + sage: b = RBF(RIF(2, 4)) # optional - arb + sage: a < b # optional - arb + False + sage: a > b # optional - arb + False + sage: a <= b # optional - arb + False + sage: a >= b # optional - arb + False + sage: a == b # optional - arb + False + sage: a != b # optional - arb + False - sage: from sage.rings.real_arb import RealBallField # optional - arb - sage: RBF = RealBallField() # optional - arb - sage: a = RBF(1) # optional - arb - sage: b = RBF(1) # optional - arb - sage: a is b # optional - arb - False - sage: a == b # optional - arb - True + Balls with non-trivial intersection:: - TESTS: + sage: a = RBF(RIF(1, 4)) # optional - arb + sage: a = RBF(RIF(2, 5)) # optional - arb + sage: a < b # optional - arb + False + sage: a <= b # optional - arb + False + sage: a > b # optional - arb + False + sage: a >= b # optional - arb + False + sage: a == b # optional - arb + False + sage: a != b # optional - arb + False - Balls whose intersection consists of one point:: - - sage: a = RBF(RIF(1, 2)) # optional - arb - sage: b = RBF(RIF(2, 4)) # optional - arb - sage: a < b # optional - arb - False - sage: a > b # optional - arb - False - sage: a <= b # optional - arb - False - sage: a >= b # optional - arb - False - sage: a == b # optional - arb - False - sage: a != b # optional - arb - False - - Balls with non-trivial intersection:: - - sage: a = RBF(RIF(1, 4)) # optional - arb - sage: a = RBF(RIF(2, 5)) # optional - arb - sage: a < b # optional - arb - False - sage: a <= b # optional - arb - False - sage: a > b # optional - arb - False - sage: a >= b # optional - arb - False - sage: a == b # optional - arb - False - sage: a != b # optional - arb - False - - One ball contained in another:: - - sage: a = RBF(RIF(1, 4)) # optional - arb - sage: b = RBF(RIF(2, 3)) # optional - arb - sage: a < b # optional - arb - False - sage: a <= b # optional - arb - False - sage: a > b # optional - arb - False - sage: a >= b # optional - arb - False - sage: a == b # optional - arb - False - sage: a != b # optional - arb - False - - Disjoint balls:: - - sage: a = RBF(1/3) # optional - arb - sage: b = RBF(1/2) # optional - arb - sage: a < b # optional - arb - True - sage: a <= b # optional - arb - True - sage: a > b # optional - arb - False - sage: a >= b # optional - arb - False - sage: a == b # optional - arb - False - sage: a != b # optional - arb - True - - Exact elements:: - - sage: a = RBF(2) # optional - arb - sage: b = RBF(2) # optional - arb - sage: a.is_exact() # optional - arb - True - sage: b.is_exact() # optional - arb - True - sage: a < b # optional - arb - False - sage: a <= b # optional - arb - True - sage: a > b # optional - arb - False - sage: a >= b # optional - arb - True - sage: a == b # optional - arb - True - sage: a != b # optional - arb - False - - Special values:: - - sage: inf = RBF(+infinity) # optional - arb - sage: other_inf = RBF(+infinity, 42.r) # optional - arb - sage: neg_inf = RBF(-infinity) # optional - arb - sage: extended_line = 1/RBF(0) # optional - arb - sage: exact_nan = inf - inf # optional - arb - sage: exact_nan.mid(), exact_nan.rad() # optional - arb - (NaN, 0.00000000) - sage: other_exact_nan = inf - inf # optional - arb - - :: - - sage: exact_nan == exact_nan, exact_nan <= exact_nan, exact_nan >= exact_nan # optional - arb - (False, False, False) - sage: exact_nan != exact_nan, exact_nan < exact_nan, exact_nan > exact_nan # optional - arb - (False, False, False) - sage: from operator import eq, ne, le, lt, ge, gt # optional - arb - sage: ops = [eq, ne, le, lt, ge, gt] # optional - arb - sage: any(op(exact_nan, other_exact_nan) for op in ops) # optional - arb - False - sage: any(op(exact_nan, b) for op in ops for b in [RBF(1), extended_line, inf, neg_inf]) # optional - arb - False - - :: - - sage: neg_inf < a < inf and inf > a > neg_inf # optional - arb - True - sage: neg_inf <= b <= inf and inf >= b >= neg_inf # optional - arb - True - sage: neg_inf <= extended_line <= inf and inf >= extended_line >= neg_inf # optional - arb - True - sage: neg_inf < extended_line or extended_line < inf # optional - arb - False - sage: inf > extended_line or extended_line > neg_inf # optional - arb - False - - :: - - sage: all(b <= b == b >= b and not (b < b or b != b or b > b) # optional - arb - ....: for b in [inf, neg_inf, other_inf]) - True - sage: any(b1 == b2 for b1 in [inf, neg_inf, a, extended_line] # optional - arb - ....: for b2 in [inf, neg_inf, a, extended_line] - ....: if not b1 is b2) - False - sage: all(b1 != b2 and not b1 == b2 # optional - arb - ....: for b1 in [inf, neg_inf, a] - ....: for b2 in [inf, neg_inf, a] - ....: if not b1 is b2) - True - sage: neg_inf <= -other_inf == neg_inf == -other_inf < other_inf == inf <= other_inf # optional - arb - True - sage: any(inf < b or b > inf # optional - arb - ....: for b in [inf, other_inf, a, extended_line]) - False - sage: any(inf <= b or b >= inf for b in [a, extended_line]) # optional - arb - False + One ball contained in another:: + + sage: a = RBF(RIF(1, 4)) # optional - arb + sage: b = RBF(RIF(2, 3)) # optional - arb + sage: a < b # optional - arb + False + sage: a <= b # optional - arb + False + sage: a > b # optional - arb + False + sage: a >= b # optional - arb + False + sage: a == b # optional - arb + False + sage: a != b # optional - arb + False + + Disjoint balls:: + + sage: a = RBF(1/3) # optional - arb + sage: b = RBF(1/2) # optional - arb + sage: a < b # optional - arb + True + sage: a <= b # optional - arb + True + sage: a > b # optional - arb + False + sage: a >= b # optional - arb + False + sage: a == b # optional - arb + False + sage: a != b # optional - arb + True + + Exact elements:: + + sage: a = RBF(2) # optional - arb + sage: b = RBF(2) # optional - arb + sage: a.is_exact() # optional - arb + True + sage: b.is_exact() # optional - arb + True + sage: a < b # optional - arb + False + sage: a <= b # optional - arb + True + sage: a > b # optional - arb + False + sage: a >= b # optional - arb + True + sage: a == b # optional - arb + True + sage: a != b # optional - arb + False + + Special values:: + + sage: inf = RBF(+infinity) # optional - arb + sage: other_inf = RBF(+infinity, 42.r) # optional - arb + sage: neg_inf = RBF(-infinity) # optional - arb + sage: extended_line = 1/RBF(0) # optional - arb + sage: exact_nan = inf - inf # optional - arb + sage: exact_nan.mid(), exact_nan.rad() # optional - arb + (NaN, 0.00000000) + sage: other_exact_nan = inf - inf # optional - arb + + :: + + sage: exact_nan == exact_nan, exact_nan <= exact_nan, exact_nan >= exact_nan # optional - arb + (False, False, False) + sage: exact_nan != exact_nan, exact_nan < exact_nan, exact_nan > exact_nan # optional - arb + (False, False, False) + sage: from operator import eq, ne, le, lt, ge, gt # optional - arb + sage: ops = [eq, ne, le, lt, ge, gt] # optional - arb + sage: any(op(exact_nan, other_exact_nan) for op in ops) # optional - arb + False + sage: any(op(exact_nan, b) for op in ops for b in [RBF(1), extended_line, inf, neg_inf]) # optional - arb + False + + :: + + sage: neg_inf < a < inf and inf > a > neg_inf # optional - arb + True + sage: neg_inf <= b <= inf and inf >= b >= neg_inf # optional - arb + True + sage: neg_inf <= extended_line <= inf and inf >= extended_line >= neg_inf # optional - arb + True + sage: neg_inf < extended_line or extended_line < inf # optional - arb + False + sage: inf > extended_line or extended_line > neg_inf # optional - arb + False + + :: + + sage: all(b <= b == b >= b and not (b < b or b != b or b > b) # optional - arb + ....: for b in [inf, neg_inf, other_inf]) + True + sage: any(b1 == b2 for b1 in [inf, neg_inf, a, extended_line] # optional - arb + ....: for b2 in [inf, neg_inf, a, extended_line] + ....: if not b1 is b2) + False + sage: all(b1 != b2 and not b1 == b2 # optional - arb + ....: for b1 in [inf, neg_inf, a] + ....: for b2 in [inf, neg_inf, a] + ....: if not b1 is b2) + True + sage: neg_inf <= -other_inf == neg_inf == -other_inf < other_inf == inf <= other_inf # optional - arb + True + sage: any(inf < b or b > inf # optional - arb + ....: for b in [inf, other_inf, a, extended_line]) + False + sage: any(inf <= b or b >= inf for b in [a, extended_line]) # optional - arb + False """ cdef RealBall lt, rt cdef arb_t difference diff --git a/src/sage/rings/real_lazy.pyx b/src/sage/rings/real_lazy.pyx index bfe6fe659f0..fce4acfed0d 100644 --- a/src/sage/rings/real_lazy.pyx +++ b/src/sage/rings/real_lazy.pyx @@ -668,6 +668,26 @@ cdef class LazyFieldElement(FieldElement): True sage: RLF(3) == RLF(4) False + sage: RLF(3) < RLF(5/3) + False + + TESTS:: + + sage: from sage.rings.real_lazy import LazyBinop + sage: RLF(3) < LazyBinop(RLF, 5, 3, operator.div) + False + sage: from sage.rings.real_lazy import LazyWrapper + sage: LazyWrapper(RLF, 3) < LazyWrapper(RLF, 5/3) + False + sage: from sage.rings.real_lazy import LazyUnop + sage: RLF(3) < LazyUnop(RLF, 2, sqrt) + False + sage: from sage.rings.real_lazy import LazyNamedUnop + sage: RLF(3) < LazyNamedUnop(RLF, 0, 'sin') + False + sage: from sage.rings.real_lazy import LazyConstant + sage: RLF(3) < LazyConstant(RLF, 'e') + False """ left = self try: @@ -679,17 +699,6 @@ cdef class LazyFieldElement(FieldElement): left, right = self.approx(), other.approx() return cmp(left, right) - def __richcmp__(left, right, int op): - """ - Perform a rich comparison between ``left`` and ``right``. - - EXAMPLES:: - - sage: RLF(3) < RLF(5/3) - False - """ - return (left)._richcmp(right, op) - def __hash__(self): """ Return the hash value of ``self``. @@ -993,18 +1002,6 @@ cdef class LazyWrapper(LazyFieldElement): """ return not not self._value - def __richcmp__(left, right, int op): - """ - Perform a rich comparison between ``left`` and ``right``. - - EXAMPLES:: - - sage: from sage.rings.real_lazy import LazyWrapper - sage: LazyWrapper(RLF, 3) < LazyWrapper(RLF, 5/3) - False - """ - return (left)._richcmp(right, op) - def __hash__(self): """ Return the hash value of ``self``. @@ -1176,18 +1173,6 @@ cdef class LazyBinop(LazyFieldElement): # We only do a call here because it is a python call. return self._op(left, right) - def __richcmp__(left, right, int op): - """ - Perform a rich comparison between ``left`` and ``right``. - - EXAMPLES:: - - sage: from sage.rings.real_lazy import LazyBinop - sage: RLF(3) < LazyBinop(RLF, 5, 3, operator.div) - False - """ - return (left)._richcmp(right, op) - def __hash__(self): """ Return the hash value of ``self``. @@ -1280,18 +1265,6 @@ cdef class LazyUnop(LazyFieldElement): return ~arg return self._op(self._arg.eval(R)) - def __richcmp__(left, right, int op): - """ - Perform a rich comparison between ``left`` and ``right``. - - EXAMPLES:: - - sage: from sage.rings.real_lazy import LazyUnop - sage: RLF(3) < LazyUnop(RLF, 2, sqrt) - False - """ - return (left)._richcmp(right, op) - def __hash__(self): """ Return the hash value of ``self``. @@ -1411,18 +1384,6 @@ cdef class LazyNamedUnop(LazyUnop): interval_field = self._parent.interval_field() return self.eval(interval_field._middle_field()) - def __richcmp__(left, right, int op): - """ - Perform a rich comparison between ``left`` and ``right``. - - EXAMPLES:: - - sage: from sage.rings.real_lazy import LazyNamedUnop - sage: RLF(3) < LazyNamedUnop(RLF, 0, 'sin') - False - """ - return (left)._richcmp(right, op) - def __hash__(self): """ Return the hash value of ``self``. @@ -1549,18 +1510,6 @@ cdef class LazyConstant(LazyFieldElement): self._extra_args = args return self - def __richcmp__(left, right, int op): - """ - Perform a rich comparison between ``left`` and ``right``. - - EXAMPLES:: - - sage: from sage.rings.real_lazy import LazyConstant - sage: RLF(3) < LazyConstant(RLF, 'e') - False - """ - return (left)._richcmp(right, op) - def __hash__(self): """ Return the hash value of ``self``. diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 85d3b750787..4da87d4c4bd 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -3445,11 +3445,10 @@ cdef class RealIntervalFieldElement(sage.structure.element.RingElement): """ return mpfi_nan_p(self.value) - def __richcmp__(left, right, int op): + cpdef _richcmp_(left, Element right, int op): """ - Rich comparison between ``left`` and ``right``. - - For more information, see :mod:`sage.rings.real_mpfi`. + Implements comparisons between intervals. (See the file header + comment for more information on interval comparison.) EXAMPLES:: @@ -3469,13 +3468,6 @@ cdef class RealIntervalFieldElement(sage.structure.element.RingElement): False sage: RIF(0, 2) > RIF(2, 3) False - """ - return (left)._richcmp(right, op) - - cpdef _richcmp_(left, Element right, int op): - """ - Implements comparisons between intervals. (See the file header - comment for more information on interval comparison.) EXAMPLES:: @@ -3666,7 +3658,7 @@ cdef class RealIntervalFieldElement(sage.structure.element.RingElement): """ return not (mpfr_zero_p(&self.value.left) and mpfr_zero_p(&self.value.right)) - def __cmp__(left, right): + cpdef int _cmp_(left, Element right) except -2: """ Compare two intervals lexicographically. @@ -3694,12 +3686,6 @@ cdef class RealIntervalFieldElement(sage.structure.element.RingElement): sage: cmp(RIF(0, 1), RIF(0, 1/2)) 1 """ - return (left)._cmp(right) - - cpdef int _cmp_(left, Element right) except -2: - """ - Implements the lexicographic total order on intervals. - """ cdef RealIntervalFieldElement lt, rt lt = left From 83d60c416226061858eb17f1792345639078ff87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 29 Sep 2015 17:04:07 +0200 Subject: [PATCH 279/421] trac #17946 fixing two typoes --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 9b658fb1d46..0b0f2ed3f47 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -1934,11 +1934,11 @@ def varchenko_matrix(self, names='h'): @cached_method def matroid(self): r""" - Return the matroid assoicated to ``self``. + Return the matroid associated to ``self``. Let `A` denote a central hyperplane arrangement and `n_H` the normal vector of some hyperplane `H \in A`. We define a matroid - `M_A` as the linear matoid spanned by `\{ n_H | H \in A \}`. + `M_A` as the linear matroid spanned by `\{ n_H | H \in A \}`. The matroid `M_A` is such that the lattice of flats of `M` is isomorphic to the intersection lattice of `A` (Proposition 3.6 in [RS]_). From 5f8ee7921401ca77960ac7afc7cf9af62e8b8a6c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 29 Sep 2015 21:09:52 -0500 Subject: [PATCH 280/421] Added reference and minor tweaks. --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 0b0f2ed3f47..a1306b07250 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -2008,7 +2008,7 @@ def minimal_generated_number(self): continue d = list(d) dep = V.linear_dependence([norms[j] for j in d]) - w = copy(W.zero()) + w = W.zero().list() for j,k in enumerate(d): w[k] = dep[0][j] sol.append(w) @@ -2021,8 +2021,8 @@ def is_formal(self): """ Return if ``self`` is formal. - A hyperplane arrangement is *formal* if it is 3-generated as - defined in :meth:`minimal_generated_number`. + A hyperplane arrangement is *formal* if it is 3-generated [Vuz93]_, + where `k`-generated is defined in :meth:`minimal_generated_number`. EXAMPLES:: From 596515000c225cb924b553d20cec33e77c978e78 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Mon, 28 Sep 2015 14:53:30 +0200 Subject: [PATCH 281/421] Moved line related to dictionary of encoders --- src/sage/coding/__init__.py | 1 - src/sage/coding/all.py | 4 +--- src/sage/coding/linear_code.py | 5 ++--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sage/coding/__init__.py b/src/sage/coding/__init__.py index b2cee969e25..c9fecacd721 100644 --- a/src/sage/coding/__init__.py +++ b/src/sage/coding/__init__.py @@ -1,2 +1 @@ import all -linear_code.AbstractLinearCode._registered_encoders["GeneratorMatrix"] = linear_code.LinearCodeGeneratorMatrixEncoder diff --git a/src/sage/coding/all.py b/src/sage/coding/all.py index e753a13d621..5895ac96efc 100644 --- a/src/sage/coding/all.py +++ b/src/sage/coding/all.py @@ -1,7 +1,6 @@ from sage.misc.lazy_import import lazy_import -from code_constructions import (permutation_action, - walsh_matrix,cyclotomic_cosets) +from code_constructions import (permutation_action, walsh_matrix,cyclotomic_cosets) from sage.misc.superseded import deprecated_callable_import deprecated_callable_import(15445, @@ -70,7 +69,6 @@ "self_orthogonal_binary_codes"]) from sd_codes import self_dual_codes_binary - lazy_import("sage.coding.delsarte_bounds", ["Krawtchouk", "delsarte_bound_hamming_space", "delsarte_bound_additive_hamming_space"]) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 39e25a56853..92c3f486fd6 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -238,7 +238,7 @@ from sage.misc.decorators import rename_keyword from sage.misc.cachefunc import cached_method from sage.misc.superseded import deprecated_function_alias - +from encoder import Encoder ZZ = IntegerRing() VectorSpace = fm.VectorSpace @@ -3672,8 +3672,6 @@ def generator_matrix(self, encoder_name=None, **kwargs): ####################### encoders ############################### -from encoder import Encoder - class LinearCodeGeneratorMatrixEncoder(Encoder): r""" Encoder based on generator_matrix for Linear codes. @@ -3743,3 +3741,4 @@ def generator_matrix(self): [1 1 0 1 0 0 1] """ return self.code().generator_matrix() +AbstractLinearCode._registered_encoders["GeneratorMatrix"] = LinearCodeGeneratorMatrixEncoder From dc390fc22a0c22b75ebd26ef0161bf0770142c48 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 30 Sep 2015 12:42:32 +0200 Subject: [PATCH 282/421] forgot an else --- src/sage/databases/findstat.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index c831644c4ca..61dfb90dab1 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -458,6 +458,15 @@ def __call__(self, query, function=None, depth=2, max_values=FINDSTAT_MAX_VALUES Traceback (most recent call last): ... ValueError: The given arguments, Permutations and 1, cannot be used for a FindStat search. + + sage: from sage.databases.findstat import FindStatCollection + sage: findstat(FindStatCollection("Permutations"), lambda pi: pi.length()) # optional -- internet,random + 0: (St000018: The [[/Permutations/Inversions|number of inversions]] of a permutation., [], 200) + 1: (St000004: The [[/Permutations/Descents-Major|major index]] of a permutation., [Mp00062: inversion-number to major-index bijection], 200) + 2: (St000067: The inversion number of the alternating sign matrix., [Mp00063: to alternating sign matrix], 152) + 3: (St000246: The [[/Permutations/Inversions|number of non-inversions]] of a permutation., [Mp00064: reverse], 200) + 4: (St000008: The major index of the composition., [Mp00062: inversion-number to major-index bijection, Mp00071: descent composition], 200) + """ try: depth = int(depth) @@ -526,7 +535,9 @@ def __call__(self, query, function=None, depth=2, max_values=FINDSTAT_MAX_VALUES else: if callable(function): - if not isinstance(query, FindStatCollection): + if isinstance(query, FindStatCollection): + collection = query + else: collection = FindStatCollection(query) first_terms = collection.first_terms(function, max_values=max_values) data = [([key], [value]) for (key, value) in first_terms] From d9ba9b2a3b3fc59d4b98e85f87cc4575036f139f Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 30 Sep 2015 15:25:28 +0200 Subject: [PATCH 283/421] trac #19042: Faster package check --- src/sage/sat/solvers/satsolver.pyx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sage/sat/solvers/satsolver.pyx b/src/sage/sat/solvers/satsolver.pyx index 801c9c76b2a..c3baf8cf5d4 100644 --- a/src/sage/sat/solvers/satsolver.pyx +++ b/src/sage/sat/solvers/satsolver.pyx @@ -14,6 +14,7 @@ AUTHORS: - Martin Albrecht (2012): first version """ +from sage.misc.package import PackageNotFoundError cdef class SatSolver: def __cinit__(self, *args, **kwds): @@ -314,13 +315,19 @@ def SAT(solver=None): #vars: 0, #lits: 0, #clauses: 0, #learnt: 0, #assigns: 0 """ - from sage.misc.package import is_package_installed + if solver is None: + try: + from sage.sat.solvers.cryptominisat.cryptominisat import CryptoMiniSat + cryptominisat_available = True + except ImportError: + cryptominisat_available = False + if (solver == 'cryptominisat' or - (solver is None and is_package_installed('cryptominisat'))): + (solver is None and cryptominisat_available)): try: from sage.sat.solvers.cryptominisat.cryptominisat import CryptoMiniSat except ImportError: - raise ImportError("To enable this feature, run 'sage -i cryptominisat'.") + raise PackageNotFoundError("To enable this feature, run 'sage -i cryptominisat'.") return CryptoMiniSat() elif (solver == "LP" or solver is None): from sat_lp import SatLP From d35f98f88314101d141e185f7ed0f385aa32860a Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 30 Sep 2015 15:26:00 +0200 Subject: [PATCH 284/421] trac #19042: Remove KeyboardInterrupt check --- src/sage/sat/solvers/sat_lp.pyx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sage/sat/solvers/sat_lp.pyx b/src/sage/sat/solvers/sat_lp.pyx index 024ddad5a2e..bffe684a047 100644 --- a/src/sage/sat/solvers/sat_lp.pyx +++ b/src/sage/sat/solvers/sat_lp.pyx @@ -108,9 +108,6 @@ class SatLP(SatSolver): - If this instance is UNSAT: ``False`` - - If the solver was interrupted before deciding satisfiability - ``None``. - EXAMPLE:: sage: def is_bipartite_SAT(G): @@ -133,8 +130,6 @@ class SatLP(SatSolver): self._LP.solve() except MIPSolverException: return False - except KeyboardInterrupt: - return None b = self._LP.get_values(self._vars) n = max(b) From 470500f86e188164d23cb5c417ebecbab10f43cc Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 30 Sep 2015 15:27:38 +0200 Subject: [PATCH 285/421] trac #19042: pyx->py --- src/module_list.py | 3 --- src/sage/sat/solvers/{sat_lp.pyx => sat_lp.py} | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) rename src/sage/sat/solvers/{sat_lp.pyx => sat_lp.py} (99%) diff --git a/src/module_list.py b/src/module_list.py index feca321996a..7108872b586 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1605,9 +1605,6 @@ def uname_specific(name, value, alternative): Extension('sage.sat.solvers.satsolver', sources = ['sage/sat/solvers/satsolver.pyx']), - Extension('sage.sat.solvers.sat_lp', - sources = ['sage/sat/solvers/sat_lp.pyx']), - ################################ ## ## sage.schemes diff --git a/src/sage/sat/solvers/sat_lp.pyx b/src/sage/sat/solvers/sat_lp.py similarity index 99% rename from src/sage/sat/solvers/sat_lp.pyx rename to src/sage/sat/solvers/sat_lp.py index bffe684a047..7c8b2fe2e8d 100644 --- a/src/sage/sat/solvers/sat_lp.pyx +++ b/src/sage/sat/solvers/sat_lp.py @@ -6,7 +6,7 @@ can be expected to be slower than when using :class:`~sage.sat.solvers.cryptominisat.cryptominisat.CryptoMiniSat`. """ -from satsolver cimport SatSolver +from satsolver import SatSolver from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException class SatLP(SatSolver): From e9309e7c826982f251825f1c8f959723daf0ab35 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 30 Sep 2015 15:32:17 +0200 Subject: [PATCH 286/421] trac #19042: Wrong text --- src/sage/sat/solvers/satsolver.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/sat/solvers/satsolver.pyx b/src/sage/sat/solvers/satsolver.pyx index c3baf8cf5d4..9666c7cae22 100644 --- a/src/sage/sat/solvers/satsolver.pyx +++ b/src/sage/sat/solvers/satsolver.pyx @@ -327,7 +327,7 @@ def SAT(solver=None): try: from sage.sat.solvers.cryptominisat.cryptominisat import CryptoMiniSat except ImportError: - raise PackageNotFoundError("To enable this feature, run 'sage -i cryptominisat'.") + raise PackageNotFoundError("cryptominisat") return CryptoMiniSat() elif (solver == "LP" or solver is None): from sat_lp import SatLP From b52a8bce2a3cabc089ae6a4b91e38a38af94e9d8 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 30 Sep 2015 16:47:11 +0200 Subject: [PATCH 287/421] trac #19061: utf8 for graph.py --- src/sage/graphs/graph.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 77bf03c1e42..ef59285c82d 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Undirected graphs From 8fa7a280222ee3bccdc91ecce9f58ed88cd1c6f1 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 30 Sep 2015 17:18:13 +0200 Subject: [PATCH 288/421] trac #19061: Workaround Cython injection --- src/sage/misc/rest_index_of_methods.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sage/misc/rest_index_of_methods.py b/src/sage/misc/rest_index_of_methods.py index 9e120a1b2ce..c928f036c62 100644 --- a/src/sage/misc/rest_index_of_methods.py +++ b/src/sage/misc/rest_index_of_methods.py @@ -7,6 +7,8 @@ {INDEX_OF_FUNCTIONS} """ +from sage.misc.sageinspect import _extract_embedded_position + def gen_rest_table_index(list_of_entries, names=None, sort=True, only_local_functions=True): r""" Return a ReST table describing a list of functions. @@ -170,9 +172,15 @@ def gen_rest_table_index(list_of_entries, names=None, sort=True, only_local_func else: continue + # Extract lines injected by cython + doc = e.__doc__ + doc_tmp = _extract_embedded_position(doc) + if doc_tmp: + doc = doc_tmp[0] + # Descriptions of the method/function - if e.__doc__: - desc = e.__doc__.split('\n\n')[0] # first paragraph + if doc: + desc = doc.split('\n\n')[0] # first paragraph desc = " ".join([x.strip() for x in desc.splitlines()]) # concatenate lines desc = desc.strip() # remove leading spaces else: From a4ac4458b8f5aa633047299f9db2269fdf4e74ca Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 30 Sep 2015 22:20:22 +0200 Subject: [PATCH 289/421] trac #19042: Review --- src/doc/en/reference/sat/index.rst | 2 +- src/sage/sat/all.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/sat/index.rst b/src/doc/en/reference/sat/index.rst index d4cf6adfbe0..7f6314ac5d8 100644 --- a/src/doc/en/reference/sat/index.rst +++ b/src/doc/en/reference/sat/index.rst @@ -36,7 +36,7 @@ We now show how to solve a simple SAT problem. :: In Sage's notation:: - sage: solver = SAT() # random + sage: solver = SAT() sage: solver.add_clause( ( 1, 2, 3) ) sage: solver.add_clause( ( 1, 2, -3) ) sage: solver() # random diff --git a/src/sage/sat/all.py b/src/sage/sat/all.py index 63993752958..d4beeb2a9a1 100644 --- a/src/sage/sat/all.py +++ b/src/sage/sat/all.py @@ -1 +1,2 @@ -from solvers.satsolver import SAT +from sage.misc.lazy_import import lazy_import +lazy_import('sage.sat.solvers.satsolver', 'SAT') From d330146452be5ce3cdc491868f1320f05c882445 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 23 Sep 2015 15:11:41 +0200 Subject: [PATCH 290/421] Upgrade PARI/GP to latest master --- build/pkgs/pari/checksums.ini | 6 +- build/pkgs/pari/package-version.txt | 2 +- build/pkgs/pari/patches/KERNELCFLAGS.patch | 16 -- build/pkgs/pari/patches/README.txt | 10 -- build/pkgs/pari/patches/det_garbage.patch | 55 ------ build/pkgs/pari/patches/perl_regex.patch | 200 --------------------- build/pkgs/pari/spkg-install | 9 +- src/sage/libs/pari/paridecl.pxd | 101 ++++++++++- src/sage_setup/autogen/pari/parser.py | 2 +- 9 files changed, 111 insertions(+), 290 deletions(-) delete mode 100644 build/pkgs/pari/patches/KERNELCFLAGS.patch delete mode 100644 build/pkgs/pari/patches/det_garbage.patch delete mode 100644 build/pkgs/pari/patches/perl_regex.patch diff --git a/build/pkgs/pari/checksums.ini b/build/pkgs/pari/checksums.ini index 8870cbb86ee..c62c530a888 100644 --- a/build/pkgs/pari/checksums.ini +++ b/build/pkgs/pari/checksums.ini @@ -1,4 +1,4 @@ tarball=pari-VERSION.tar.gz -sha1=307409c3917f6df71d2e10640c119e7d31c1f2e6 -md5=41936ce2dce6bd00a662bf43a772685f -cksum=855809013 +sha1=fa23e0c8b6e38a356048d19224dc9b9658d77724 +md5=c753faaa4780de5ad8d461f0ffd70ecf +cksum=1220765312 diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index 8db184f072c..2b25bd18c62 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.8-1637-g489005a.p1 +2.8-1813-g6157df4.p0 diff --git a/build/pkgs/pari/patches/KERNELCFLAGS.patch b/build/pkgs/pari/patches/KERNELCFLAGS.patch deleted file mode 100644 index 537dbb52d1e..00000000000 --- a/build/pkgs/pari/patches/KERNELCFLAGS.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff -ru src/config/get_cc b/config/get_cc ---- src/config/get_cc 2014-02-01 21:41:54.534348273 +0100 -+++ b/config/get_cc 2014-02-01 21:42:50.850930971 +0100 -@@ -94,7 +94,11 @@ - OPTFLAGS="$OPTFLAGS -fno-strict-aliasing" - fi - rm -f $exe $exe$exe_suff -- KERNELCFLAGS=-funroll-loops -+ if [ "$SAGE_DEBUG" = yes ]; then -+ KERNELCFLAGS=-O1 -+ else -+ KERNELCFLAGS=-funroll-loops -+ fi - - DBGFLAGS=${DBGFLAGS:-"-g $warn"} - # Specific optimisations for some architectures diff --git a/build/pkgs/pari/patches/README.txt b/build/pkgs/pari/patches/README.txt index 93ac577bd55..d9aef73659a 100644 --- a/build/pkgs/pari/patches/README.txt +++ b/build/pkgs/pari/patches/README.txt @@ -11,17 +11,7 @@ Patches to configuration files: Darwin. Submitted upstream, but upstream only applied it for PowerPC. Since this doesn't break anything and only improves performance, add the flag unconditionally. -* KERNELCFLAGS.patch (Jeroen Demeyer): when SAGE_DEBUG=yes, compile - kernel files with -O1 instead of -funroll-loops; -O0 gives a - segmentation fault on some OS X systems when doing - factor(10356613*10694706299664611221) - See #13921, also reported upstream: - - http://pari.math.u-bordeaux.fr/archives/pari-dev-1301/msg00000.html C files: -* det_garbage.patch (Jeroen Demeyer, #15654): When computing a - determinant(), only collect garbage once per outer loop iteration. - Better increase PARI stack size instead of collecting garbage too - often. * public_memory_functions.patch (Jeroen Demeyer, #16997): Make some of PARI's private memory functions public to improve interface with Sage. diff --git a/build/pkgs/pari/patches/det_garbage.patch b/build/pkgs/pari/patches/det_garbage.patch deleted file mode 100644 index ab7af6643af..00000000000 --- a/build/pkgs/pari/patches/det_garbage.patch +++ /dev/null @@ -1,55 +0,0 @@ -diff --git a/src/basemath/alglin1.c b/src/basemath/alglin1.c -index cada9eb..515eba9 100644 ---- a/src/basemath/alglin1.c -+++ b/src/basemath/alglin1.c -@@ -248,6 +248,7 @@ gen_det(GEN a, void *E, const struct bb_field *ff) - a = RgM_shallowcopy(a); - for (i=1; ired(E,gcoeff(a,k,i)); -@@ -272,7 +273,7 @@ gen_det(GEN a, void *E, const struct bb_field *ff) - for (j=i+1; j<=nbco; j++) - { - gcoeff(a,j,k) = ff->add(E, gcoeff(a,j,k), ff->mul(E,m,gcoeff(a,j,i))); -- if (gc_needed(av,1)) -+ if (gc_needed(av,1) && (garbage++ == 0)) - { - if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i); - gerepileall(av,4, &a,&x,&q,&m); -@@ -3722,6 +3723,7 @@ det_simple_gauss(GEN a, GEN data, pivot_fun pivot) - a = RgM_shallowcopy(a); - for (i=1; i nbco) return gerepilecopy(av, gcoeff(a,i,i)); - if (k != i) -@@ -3741,7 +3743,7 @@ det_simple_gauss(GEN a, GEN data, pivot_fun pivot) - for (j=i+1; j<=nbco; j++) - { - gcoeff(a,j,k) = gsub(gcoeff(a,j,k), gmul(m,gcoeff(a,j,i))); -- if (gc_needed(av,3)) -+ if (gc_needed(av,3) && (garbage++ == 0)) - { - if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i); - gerepileall(av,2, &a,&x); -@@ -3792,6 +3794,7 @@ det_bareiss(GEN a) - { - GEN ci, ck, m; - int diveuc = (gequal1(pprec)==0); -+ int garbage = 0; /* Only gerepile() once per loop iteration */ - - p = gcoeff(a,i,i); - if (gequal0(p)) -@@ -3828,7 +3831,7 @@ det_bareiss(GEN a) - GEN p1 = gsub(gmul(p,gel(ck,j)), gmul(m,gel(ci,j))); - if (diveuc) p1 = mydiv(p1,pprec); - gel(ck,j) = gerepileupto(av2, p1); -- if (gc_needed(av,2)) -+ if (gc_needed(av,2) && (garbage++ == 0)) - { - if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i); - gerepileall(av,2, &a,&pprec); diff --git a/build/pkgs/pari/patches/perl_regex.patch b/build/pkgs/pari/patches/perl_regex.patch deleted file mode 100644 index 038f4d604e8..00000000000 --- a/build/pkgs/pari/patches/perl_regex.patch +++ /dev/null @@ -1,200 +0,0 @@ -commit 257750686ae1fe928a2b4b489844c1b57a108bd3 -Author: Karim Belabas -Date: Tue Jul 14 15:23:42 2015 +0200 - - doc_make: escape all {} in regexps [ perl-5.22 warns on these => fatal - -diff --git a/src/desc/doc_make b/src/desc/doc_make -index bb41bc9..8521a9d 100755 ---- a/src/desc/doc_make -+++ b/src/desc/doc_make -@@ -38,13 +38,13 @@ while () - $v =~ s/\[\]/[\\,]/g; - $v =~ s/(\w\w+)/\\var{$1}/g; - $v =~ s/\^([a-z])/\\hbox{\\kbd{\\pow}}$1/g; -- $v =~ s/\\var{flag}/\\fl/g; -- $v =~ s/\\var{(\d+)}/{$1}/g; -+ $v =~ s/\\var\{flag\}/\\fl/g; -+ $v =~ s/\\var\{(\d+)\}/{$1}/g; - $v =~ s/_/\\_/g; # don't merge with first subst: \var{} rule kills it - - $v = "\$($v)\$"; - } -- if ($doc !~ /\\syn\w*{/ && $sec !~ /programming\/control/) { -+ if ($doc !~ /\\syn\w*\{/ && $sec !~ /programming\/control/) { - $doc .= library_syntax($fun, $args); - } - s/_def_//; -commit 742c70e505a7e75128720f619d63e882c03e9346 -Author: Karim Belabas -Date: Tue Jul 14 13:20:07 2015 +0200 - - gphelp: escape all {} in regexps [ perl-5.22 warns on these => fatal ] - -diff --git a/doc/gphelp.in b/doc/gphelp.in -index 00ff6bd..89f2768 100755 ---- a/doc/gphelp.in -+++ b/doc/gphelp.in -@@ -298,7 +298,7 @@ sub treat { - if ($pat =~ /[a-zA-Z0-9]$/) { $pat .= '\b'; } else { $pat .= '}'; } - while () - { -- if (/\\(subsubsec[a-z]*|subsec[a-z]*|section|chapter|label){$pat/) -+ if (/\\(subsubsec[a-z]*|subsec[a-z]*|section|chapter|label)\{$pat/) - { $first = $_; last; } - } - if (eof(DOC)) -@@ -380,7 +380,7 @@ sub apropos_check { - return if ($line !~ /$help/i); - - local($_) = $current; -- s/\\b{(.)}/\\$1/; -+ s/\\b\{(.)\}/\\$1/; - s/\{\}//g; - s/\\pow/^/; - s/\\%/%/; -@@ -400,7 +400,7 @@ sub apropos { - @sentence_list = @list = ""; - while () - { -- if (/^\\(subsubsec[a-z]*|subsec[a-z]*|section|chapter){/) -+ if (/^\\(subsubsec[a-z]*|subsec[a-z]*|section|chapter)\{/) - { - $new = &get_match($_,'{','}'); - &apropos_check($line, $current); -@@ -748,7 +748,7 @@ sub basic_subst { - s/\\fun\s*\{([^{}]*)\}\s*\{((?:[^{}]|\{[^{}]*\})*)\}\s*\{((?:[^{}]|\{[^{}]*\})*)\}/\\kbd{$1 \\key{$2}($3)}\\sidx{$2}/g; - - s/\\\\(?=[a-zA-Z])/\\bs /g; -- s/\\b{}\\b{}/\\bs\\bs /g; -+ s/\\b\{\}\\b\{\}/\\bs\\bs /g; - s/\\\\/\\bs/g; - s/(\'\'|\`\`)/"/g unless $to_pod; # (english) double quotes - # asymptotic or isomorphic (~) [beware of ties] -@@ -760,16 +760,16 @@ sub basic_subst { - s/\\(~|tilde)/~/g; - - s/\\(equiv)(?![a-zA-Z])/ = /g; -- s/\\`a/$tr{agrave}/; s/\\`{a}/$tr{agrave}/; -- s/\\"o/$tr{ouml}/; s/\\"{o}/$tr{ouml}/; -- s/\\"u/$tr{uuml}/; s/\\"{u}/$tr{uuml}/; -- s/\\'e/$tr{eacute}/; s/\\'{e}/$tr{eacute}/; -+ s/\\`a/$tr{agrave}/; s/\\`\{a\}/$tr{agrave}/; -+ s/\\"o/$tr{ouml}/; s/\\"\{o\}/$tr{ouml}/; -+ s/\\"u/$tr{uuml}/; s/\\"\{u\}/$tr{uuml}/; -+ s/\\'e/$tr{eacute}/; s/\\'\{e\}/$tr{eacute}/; - - s/(^|[^\\])%.*/$1/g; # comments - s/\\vadjust\s*\{\s*\\penalty\s*\d+\s*\}//g; - - # We do not strip %\n, thus: -- s/\\kbd{\n\s*/\\kbd{/g; -+ s/\\kbd\{\n\s*/\\kbd{/g; - s/\$\\bf(\b|(?=[\d_]))\s*([^\$]+)\$/\$$tr{startbcode}$1$tr{endbcode}\$/g; - s/\$/$tr{dollar}/g; # math mode - s/\t/ /g; s/\\,//g; s/\\[ ;]/ /g; # various spaces -@@ -779,7 +779,7 @@ sub basic_subst { - s/\\TeX\{\}/TeX/g; - s/\\TeX(\W)/TeX$1/g; - s/ *\\circ\b */ o /g; -- s/\\d?frac{\s*((?:[^{}]|\{[^{}]*\})*)}{\s*((?:[^{}]|\{[^{}]*\})*)}/($1)\/($2)/g; -+ s/\\d?frac\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}/($1)\/($2)/g; - s(\\d?frac\s*(\d)\s*(\d))(($1/$2))g; - s[{\s*(\w)\s*\\over(?![a-zA-Z])\s*(\w)\s*}]{($1/$2)}g; - s[{\s*((?:[^{}]|\{[^{}]*\})*)\\over(?![a-zA-Z])\s*((?:[^{}]|\{[^{}]*\})*)}][($1)/($2)]g; -@@ -796,7 +796,7 @@ sub basic_subst { - - s/(\\string)?\\_/_/g; - s/\\([#\$&%|])/$1/g; -- s/\\(hat(?![a-zA-Z])|\^)({\\?\s*})?/^/g; -+ s/\\(hat(?![a-zA-Z])|\^)(\{\\?\s*\})?/^/g; - s/^(\@\[podleader\]head\d *)\\pow(?![a-zA-z])( *)/$1^$2/gm; - s/ *\\pow(?![a-zA-z]) */^/g; - -@@ -896,21 +896,21 @@ sub basic_subst { - s/\\(floor|ceil|round|binom)\{/$1\{/g; - s/\\(var|emph)\{([^\}]*)\}/$tr{startit}$2$tr{endit}/g; - s/\\fl(?![a-zA-Z])/$tr{startit}flag$tr{endit}/g; -- s/\\b{([^}]*)}/$tr{startcode}\\$1$tr{endcode}/g; -+ s/\\b\{([^}]*)\}/$tr{startcode}\\$1$tr{endcode}/g; - s/\\kbdsidx/\\sidx/g; - s/\\sidx\{[^\}]*\}//g unless $to_pod; - s/\\[a-zA-Z]*idx\{([^\}]*)\}/$1/g unless $to_pod; -- s/{\\text{(st|nd|th)}}/\\text{$1}/g; -- s/\^\\text{th}/-th/g; -- s/1\^\\text{st}/1st/g; -- s/2\^\\text{nd}/2nd/g; -+ s/\{\\text\{(st|nd|th)\}\}/\\text{$1}/g; -+ s/\^\\text\{th\}/-th/g; -+ s/1\^\\text\{st\}/1st/g; -+ s/2\^\\text\{nd\}/2nd/g; - - s/\\(text|hbox|Big)//g; - s/^([ \t]+)\{ *\\(it|sl|bf|tt)\b/S<$1>{\\$2/gm; - s/\{ *\\(it|sl) *(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/$tr{startit}$2$tr{endit}/g; - s/\{ *\\bf *(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/$tr{startbold}$1$tr{endbold}/g; - s/\{ *\\tt *(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/$tr{startpodcode}$1$tr{endpodcode}/g; -- $seek=1 if (s/\\emph{ */$tr{startit}/g); -+ $seek=1 if (s/\\emph\{ */$tr{startit}/g); - if ($seek) { $seek=0 if (s/\}/$tr{endit}/) } - s/\\(backslash|bs)\{(\w)\}/\\$2/g; - s/\\(backslash|bs)(?![a-zA-Z]) */\\/g; -@@ -1028,21 +1028,21 @@ sub TeXprint_topod { - # Try to guard \label/\sidx (removing possible '.') - # This somehow breaks index... - # s/(\\(?:section|subsec(?:ref|idx|op)?(unix)?)\s*{(?:(?:[^{}]+(?=[{}])|{[^{}]+})+)})\.?\s*\\(label|sidx)/$1\n\\$2/; -- s/(\\(?:section|subsec(?:ref|idx|op)?)\s*{(?:(?:[^{}]+(?=[{}])|{[^{}]+})+)})\.?\s*\\(label|sidx)/$1\n\\$2/; -+ s/(\\(?:section|subsec(?:ref|idx|op)?)\s*\{(?:(?:[^{}]+(?=[{}])|{[^{}]+})+)\})\.?\s*\\(label|sidx)/$1\n\\$2/; - - # last if /\\subsec[\\{}ref]*[\\\${]$help[}\\\$]/o; -- s/\\chapter\s*{((?:[^{}]|\{[^{}]*\})*)}\s*/\n\n$tr{podleader}head1 NAME\n\nlibPARI - $1\n\n/; -- s/\\appendix\s*{((?:[^{}]|\{[^{}]*\})*)}\s*/\n\n$tr{podleader}head1 NAME\n\nAppendix - $1\n\n/; -- s/\\section\s*{((?:[^{}]|\{[^{}]*\})*)}\s*/"\n\n$tr{podleader}head1 " . indexify($1) . "\n\n"/e; -+ s/\\chapter\s*\{((?:[^{}]|\{[^{}]*\})*)\}\s*/\n\n$tr{podleader}head1 NAME\n\nlibPARI - $1\n\n/; -+ s/\\appendix\s*\{((?:[^{}]|\{[^{}]*\})*)\}\s*/\n\n$tr{podleader}head1 NAME\n\nAppendix - $1\n\n/; -+ s/\\section\s*\{((?:[^{}]|\{[^{}]*\})*)\}\s*/"\n\n$tr{podleader}head1 " . indexify($1) . "\n\n"/e; - - # Try to delimit by : -- s/\\subsec(?:ref)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}([^\n]*):[\n ]/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e; -- s/\\subsubsec(?:ref)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}([^:]*):\s*/"\n\n$tr{podleader}head3 " . indexify("$1$3") . "\n\n"/e; -+ s/\\subsec(?:ref)?(?:unix)?\s*\{(([^{}]+(?=[{}])|{[^{}]+})+)\}([^\n]*):[\n ]/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e; -+ s/\\subsubsec(?:ref)?(?:unix)?\s*\{(([^{}]+(?=[{}])|{[^{}]+})+)\}([^:]*):\s*/"\n\n$tr{podleader}head3 " . indexify("$1$3") . "\n\n"/e; - s/\\subsubsec\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}(.*)$/"\n\n$tr{podleader}head3 " . indexify("$1") . "$3\n\n"/me; - s/\\subseckbd\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}([^:]*):\s*/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e; - # Try to delimit by ' ' -- s/\\subsec(?:ref)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}(\S*)\s+/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e; -- s/\\subsec(?:title)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]*})+)}:?\s*/"\n\n$tr{podleader}head2 " . indexify("$1") . "\n\n"/e; -+ s/\\subsec(?:ref)?(?:unix)?\s*\{(([^{}]+(?=[{}])|{[^{}]+})+)\}(\S*)\s+/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e; -+ s/\\subsec(?:title)?(?:unix)?\s*\{(([^{}]+(?=[{}])|{[^{}]*})+)\}:?\s*/"\n\n$tr{podleader}head2 " . indexify("$1") . "\n\n"/e; - - # This is to skip preface in refcard: - /\Q$tr{podleader}\Ehead1|\\title(?![a-zA-Z])\s*\{/o and $seen_start = 1 -@@ -1097,18 +1097,18 @@ sub TeXprint_topod { - s/\$\$(.*?)\$\$\s*/\n\nS< >$tr{startcode}$1$tr{endcode}\n\n/gs; - s/\$([^\$]+)\$/$tr{startcode}$1$tr{endcode}/g; - -- s/\\s(?:ref|idx){\s*([^{}]*)}/"X<" . for_index($1) . ">"/ge; # -- s/\\(?:ref|idx){\s*([^{}]*)}/"X<" . for_index($1) . ">$1"/ge; -+ s/\\s(?:ref|idx)\{\s*([^{}]*)\}/"X<" . for_index($1) . ">"/ge; # -+ s/\\(?:ref|idx)\{\s*([^{}]*)\}/"X<" . for_index($1) . ">$1"/ge; - - # Conflict between different versions of PARI and refcard: --# s/\\(?:key|li)\s*{(.*)}\s*{(.+)}[ \t]*\n/\n\n=item C<$2>\n\n$1\n\n/msg; --# s/\\(?:key|li)\s*{(.*)}\s*{}[ \t]*\n/\n\n=back\n\n$1\n\n=over\n\n/mgs; --# s/\\(key|var)(?![a-zA-Z])\s*{(\w+)}/C<$2>/mg; -- s/\\var\s*{X<(\w+)>(\w+)}/X<$1>$tr{startcode}$2$tr{endcode}/mg; -- s/\\var\s*{f{}lag}/$tr{startcode}flag$tr{endcode}/mg; -- -- s/\\metax(?![a-zA-Z])\s*{(.*)}\s*{\s*(\w+)(?=C\<)(.*)}[ \t]*\n/\n\n=item C$3>\n\n$1\n\n/mg; -- s/\\metax(?![a-zA-Z])\s*{(.*)}\s*{(.*)}[ \t]*\n/\n\n=item C<$2>\n\n$1\n\n/mg; -+# s/\\(?:key|li)\s*\{(.*)\}\s*\{(.+)\}[ \t]*\n/\n\n=item C<$2>\n\n$1\n\n/msg; -+# s/\\(?:key|li)\s*\{(.*)\}\s*\{\}[ \t]*\n/\n\n=back\n\n$1\n\n=over\n\n/mgs; -+# s/\\(key|var)(?![a-zA-Z])\s*\{(\w+)\}/C<$2>/mg; -+ s/\\var\s*\{X<(\w+)>(\w+)\}/X<$1>$tr{startcode}$2$tr{endcode}/mg; -+ s/\\var\s*\{f\{\}lag\}/$tr{startcode}flag$tr{endcode}/mg; -+ -+ s/\\metax(?![a-zA-Z])\s*\{(.*)\}\s*\{\s*(\w+)(?=C\<)(.*)\}[ \t]*\n/\n\n=item C$3>\n\n$1\n\n/mg; -+ s/\\metax(?![a-zA-Z])\s*\{(.*)\}\s*\{(.*)\}[ \t]*\n/\n\n=item C<$2>\n\n$1\n\n/mg; - s/C\<\{\}=/C\<=/g; - s/\\fl(?![a-zA-Z])/I/g; - s/\\file(?![a-zA-Z])/F/g; diff --git a/build/pkgs/pari/spkg-install b/build/pkgs/pari/spkg-install index 6a7b1ebc9d3..55a64aec9a8 100755 --- a/build/pkgs/pari/spkg-install +++ b/build/pkgs/pari/spkg-install @@ -206,7 +206,14 @@ set_environment # Set CFLAGS if [ "$SAGE_DEBUG" = yes ]; then # Disable optimisation, add debug symbols. - CFLAGS="$CFLAGS -O0 -g" + CFLAGS="-O0 -g $CFLAGS" + + # Compile kernel files with -O1 instead of -funroll-loops; -O0 gives + # a segmentation fault on some OS X systems when doing + # factor(10356613*10694706299664611221) + # See #13921, also reported upstream: + # - http://pari.math.u-bordeaux.fr/archives/pari-dev-1301/msg00000.html + PARI_MAKEFLAGS="KERNELCFLAGS=-O1 $PARI_MAKEFLAGS" else # Use PARI's default CFLAGS (with -g added). # PARI's Configure adds -O3 to the CFLAGS, so we don't need to add diff --git a/src/sage/libs/pari/paridecl.pxd b/src/sage/libs/pari/paridecl.pxd index fac8f363ccd..8e039a6084a 100644 --- a/src/sage/libs/pari/paridecl.pxd +++ b/src/sage/libs/pari/paridecl.pxd @@ -1435,6 +1435,7 @@ cdef extern from "sage/libs/pari/parisage.h": long fetch_var_higher() GEN fetch_var_value(long vx, GEN t) GEN gp_read_str(char *t) + GEN gp_read_str_multiline(char *t) entree* install(void *f, char *name, char *code) entree* is_entry(char *s) void kill0(char *e) @@ -2203,7 +2204,7 @@ cdef extern from "sage/libs/pari/parisage.h": # classpoly.c - GEN polclass(GEN D, long xvar) + GEN polclass(GEN D, long inv, long xvar) # compile.c @@ -2216,8 +2217,8 @@ cdef extern from "sage/libs/pari/parisage.h": # concat.c - GEN concat(GEN x, GEN y) - GEN concat1(GEN x) + GEN gconcat(GEN x, GEN y) + GEN gconcat1(GEN x) GEN matconcat(GEN v) GEN shallowconcat(GEN x, GEN y) GEN shallowconcat1(GEN x) @@ -3181,7 +3182,101 @@ cdef extern from "sage/libs/pari/parisage.h": GEN rnfkummer(GEN bnr, GEN subgroup, long all, long prec) + # lfun.c + + long is_linit(GEN data) + GEN ldata_get_an(GEN ldata) + long ldata_get_selfdual(GEN ldata) + long ldata_isreal(GEN ldata) + GEN ldata_get_gammavec(GEN ldata) + long ldata_get_degree(GEN ldata) + long ldata_get_k(GEN ldata) + GEN ldata_get_conductor(GEN ldata) + GEN ldata_get_rootno(GEN ldata) + GEN ldata_get_residue(GEN ldata) + GEN ldata_vecan(GEN ldata, long L, long prec) + long ldata_get_type(GEN ldata) + long linit_get_type(GEN linit) + GEN linit_get_ldata(GEN linit) + GEN linit_get_tech(GEN linit) + GEN lfun_get_domain(GEN tech) + GEN lfun_get_dom(GEN tech) + long lfun_get_bitprec(GEN tech) + GEN lfun_get_factgammavec(GEN tech) + GEN lfun_get_step(GEN tech) + GEN lfun_get_pol(GEN tech) + GEN lfun_get_Residue(GEN tech) + GEN lfun_get_k2(GEN tech) + GEN lfun_get_w2(GEN tech) + GEN lfun_get_expot(GEN tech) + long lfun_get_der(GEN tech) + long lfun_get_bitprec(GEN tech) + GEN lfun(GEN ldata, GEN s, long prec) + GEN lfun_bitprec(GEN ldata, GEN s, long bitprec) + GEN lfun0_bitprec(GEN ldata, GEN s, long der, long bitprec) + GEN lfun0(GEN ldata, GEN s, long der, long prec) + long lfuncheckfeq(GEN data, GEN t0, long prec) + long lfuncheckfeq_bitprec(GEN data, GEN t0, long bitprec) + GEN lfunconductor(GEN data, GEN maxcond, long flag, long prec) + GEN lfuncreate(GEN obj) + GEN lfunan(GEN ldata, long L, long prec) + GEN lfunhardy(GEN ldata, GEN t, long prec) + GEN lfunhardy_bitprec(GEN ldata, GEN t, long bitprec) + GEN lfuninit(GEN ldata, GEN dom, long der, long prec) + GEN lfuninit_bitprec(GEN ldata, GEN dom, long der, long bitprec) + GEN lfuninit_make(long t, GEN ldata, GEN molin, GEN domain) + long lfunisvgaell(GEN Vga, long flag) + GEN lfunlambda(GEN ldata, GEN s, long prec) + GEN lfunlambda_bitprec(GEN ldata, GEN s, long bitprec) + GEN lfunlambda0(GEN ldata, GEN s, long der, long prec) + GEN lfunlambda0_bitprec(GEN ldata, GEN s, long der, long bitprec) + GEN lfunmisc_to_ldata(GEN ldata) + GEN lfunmisc_to_ldata_shallow(GEN ldata) + long lfunorderzero(GEN ldata, long prec) + long lfunorderzero_bitprec(GEN ldata, long bitprec) + GEN lfunprod_get_fact(GEN tech) + GEN lfunrootno(GEN data, long prec) + GEN lfunrootno_bitprec(GEN data, long bitprec) + GEN lfunrootres(GEN data, long prec) + GEN lfunrootres_bitprec(GEN data, long bitprec) + GEN lfunrtopoles(GEN r) + GEN lfuntheta(GEN data, GEN t, long m, long prec) + GEN lfuntheta_bitprec(GEN data, GEN t, long m, long bitprec) + GEN lfunthetainit(GEN ldata, GEN tinf, long m, long prec) + GEN lfunthetainit_bitprec(GEN ldata, GEN tdom, long m, long bitprec) + GEN lfunthetacheckinit(GEN data, GEN tinf, long m, long *ptbitprec, long fl) + GEN lfunzeros(GEN ldata, GEN lim, long divz, long prec) + GEN lfunzeros_bitprec(GEN ldata, GEN lim, long divz, long bitprec) + int sdomain_isincl(GEN dom, GEN dom0) + GEN theta_get_an(GEN tdata) + GEN theta_get_K(GEN tdata) + GEN theta_get_R(GEN tdata) + long theta_get_bitprec(GEN tdata) + long theta_get_m(GEN tdata) + GEN theta_get_tdom(GEN tdata) + GEN theta_get_sqrtN(GEN tdata) + + # lfunutils.c + + GEN dirzetak(GEN nf, GEN b) + GEN ellmoddegree(GEN e, long prec) + GEN lfunabelianrelinit(GEN bnfabs, GEN bnf, GEN polrel, GEN dom, long der, long prec) + GEN lfunabelianrelinit_bitprec(GEN bnfabs, GEN bnf, GEN polrel, GEN dom, long der, long bitprec) + GEN lfunconvol(GEN a1, GEN a2) + GEN lfundiv(GEN ldata1, GEN ldata2, long prec) + GEN lfunzetakinit(GEN pol, GEN dom, long der, long flag, long prec) + GEN lfunzetakinit_bitprec(GEN pol, GEN dom, long der, long flag, long bitprec) + GEN lfunetaquo(GEN ldata) + GEN lfunmfspec(GEN ldata, long prec) + GEN lfunmfpeters(GEN ldata, long prec) + GEN lfunmfpeters_bitprec(GEN ldata, long bitprec) + GEN lfunmul(GEN ldata1, GEN ldata2, long prec) + GEN lfunqf(GEN ldata) + GEN lfunsymsq(GEN ldata, GEN known, long prec) + GEN lfunsymsqspec(GEN ldata, long prec) + # lll.c + GEN ZM_lll_norms(GEN x, double D, long flag, GEN *B) GEN kerint(GEN x) GEN lll(GEN x) diff --git a/src/sage_setup/autogen/pari/parser.py b/src/sage_setup/autogen/pari/parser.py index f4d8a04d823..639673bd024 100644 --- a/src/sage_setup/autogen/pari/parser.py +++ b/src/sage_setup/autogen/pari/parser.py @@ -47,7 +47,7 @@ def pari_share(): return os.path.join(SAGE_LOCAL, "share", "pari") paren_re = re.compile(r"[(](.*)[)]") -argname_re = re.compile(r"[ {]*([A-Za-z0-9_]+)") +argname_re = re.compile(r"[ {]*([A-Za-z_][A-Za-z0-9_]*)") def read_pari_desc(): """ From 5bf22dd5904ad797f492c0215e2040f8854b21a9 Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Tue, 18 Aug 2015 01:47:26 -0700 Subject: [PATCH 291/421] Trac 19321: hash for LaurentPolynomial_mpair --- src/sage/rings/polynomial/laurent_polynomial.pyx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 091fc1a2fb0..2dd216c3ffa 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -1335,6 +1335,21 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial_generic): """ return self._parent, (self._poly,) + def __hash__(self): + r""" + TESTS:: + + sage: L. = LaurentPolynomialRing(QQ) + sage: f = L({(-1,-1):1}) + sage: hash(f) + 1 + sage: f = L({(1,1):1}) + sage: hash(f) + -2021162459040316190 # 64-bit + -1148451614 # 32-bit + """ + return hash(self._poly) + cdef _new_c(self): """ Returns a new Laurent polynomial From b6a04d66866d79f79c3c3dae511beda92e5a9e62 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 16 Aug 2015 21:18:27 +0200 Subject: [PATCH 292/421] Trac 19321: hash values for CFiniteSequence --- src/sage/rings/cfinite_sequence.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py index a33e1c75d01..972392426db 100644 --- a/src/sage/rings/cfinite_sequence.py +++ b/src/sage/rings/cfinite_sequence.py @@ -429,6 +429,18 @@ def _repr_(self): else: return 'C-finite sequence, generated by ' + str(self.ogf()) + def __hash__(self): + r""" + Hash value for C finite sequence. + + EXAMPLES:: + + sage: C. = CFiniteSequences(QQ) + sage: hash(C((2-x)/(1-x-x^2))) # random + 42 + """ + return hash(self.parent()) ^ hash(self._ogf) + def _add_(self, other): """ Addition of C-finite sequences. From d671b88ab629a6a2a94a09d92c5a8d3b792845a6 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 16 Aug 2015 21:57:35 +0200 Subject: [PATCH 293/421] Trac 19321: hash for abelian group elements --- src/sage/groups/abelian_gps/abelian_group_element.py | 1 - src/sage/groups/abelian_gps/element_base.py | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/groups/abelian_gps/abelian_group_element.py b/src/sage/groups/abelian_gps/abelian_group_element.py index c49325d699b..d1d61c93cc0 100644 --- a/src/sage/groups/abelian_gps/abelian_group_element.py +++ b/src/sage/groups/abelian_gps/abelian_group_element.py @@ -93,7 +93,6 @@ class AbelianGroupElement(AbelianGroupElementBase): sage: a*b in F True """ - def as_permutation(self): r""" Return the element of the permutation group G (isomorphic to the diff --git a/src/sage/groups/abelian_gps/element_base.py b/src/sage/groups/abelian_gps/element_base.py index ab2cff59587..1cf9c4a5a62 100644 --- a/src/sage/groups/abelian_gps/element_base.py +++ b/src/sage/groups/abelian_gps/element_base.py @@ -73,6 +73,16 @@ def __init__(self, parent, exponents): if len(self._exponents) != n: raise IndexError('argument length (= %s) must be %s.'%(len(exponents), n)) + def __hash__(self): + r""" + TESTS:: + + sage: F = AbelianGroup(3,[7,8,9]) + sage: hash(F.an_element()) # random + 1024 + """ + return hash(self.parent()) ^ hash(self._exponents) + def exponents(self): """ The exponents of the generators defining the group element. From 503679090e7ed809761abe6f211fe3cb5f8d73fd Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 18 Aug 2015 12:43:35 +0200 Subject: [PATCH 294/421] Trac 19321: hash for matrix group element --- src/sage/groups/matrix_gps/group_element.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sage/groups/matrix_gps/group_element.py b/src/sage/groups/matrix_gps/group_element.py index 5c0ae88326b..434c7ce8027 100644 --- a/src/sage/groups/matrix_gps/group_element.py +++ b/src/sage/groups/matrix_gps/group_element.py @@ -117,12 +117,23 @@ class MatrixGroupElement_base(MultiplicativeGroupElement): EXAMPLES:: sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) sage: g = G.random_element() sage: type(g) """ + def __hash__(self): + r""" + TESTS:: + + sage: MS = MatrixSpace(GF(3), 2) + sage: G = MatrixGroup([MS([1,1,0,1]), MS([1,0,1,1])]) + sage: g = G.an_element() + sage: hash(g) + 0 + """ + return hash(self.matrix()) def _repr_(self): """ From 9a3c11d18979217cbd3bff32037c17a9769df3cf Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 18 Aug 2015 12:43:50 +0200 Subject: [PATCH 295/421] Trac 19321: hash for indexed free monoid --- src/sage/monoids/indexed_free_monoid.py | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index 734d24087bd..e4772f34910 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -371,6 +371,19 @@ def __init__(self, F, x): """ IndexedMonoidElement.__init__(self, F, tuple(map(tuple, x))) + def __hash__(self): + r""" + TESTS:: + + sage: F = FreeMonoid(index_set=tuple('abcde')) + sage: hash(F ([(1,2),(0,1)]) ) + 2401565693828035651 # 64-bit + 1164080195 # 32-bit + sage: hash(F ([(0,2),(1,1)]) ) + -3359280905493236379 # 64-bit + -1890405019 # 32-bit + """ + return hash(self._monomial) def _sorted_items(self): """ @@ -486,6 +499,20 @@ def _sorted_items(self): pass return v + def __hash__(self): + r""" + TESTS:: + + sage: F = FreeAbelianMonoid(index_set=ZZ) + sage: hash( F([(0,1), (2,2)]) ) + 8087055352805725849 # 64-bit + 250091161 # 32-bit + sage: hash( F([(2,1)]) ) + 5118585357534560720 # 64-bit + 1683816912 # 32-bit + """ + return hash(frozenset(self._monomial.items())) + def _mul_(self, other): """ Multiply ``self`` by ``other``. From 90f5186b64f7fceadbd6d053230b4cc0194da0d3 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 18 Aug 2015 12:44:04 +0200 Subject: [PATCH 296/421] Trac 19321: hash for free groups --- src/sage/groups/free_group.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index 0bdef462d65..f328c0efbdc 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -227,6 +227,9 @@ def __init__(self, parent, x): x = AbstractWordTietzeWord(l, parent._gap_gens()) ElementLibGAP.__init__(self, parent, x) + def __hash__(self): + return hash(self.Tietze()) + def _latex_(self): """ Return a LaTeX representation From 48b50028ed63682f45b3f0db6d891d35303f7119 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 18 Aug 2015 13:32:37 +0200 Subject: [PATCH 297/421] Trac 19321: hash for poset elements --- src/sage/combinat/posets/elements.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/combinat/posets/elements.py b/src/sage/combinat/posets/elements.py index b27afd59980..0cf391ef992 100644 --- a/src/sage/combinat/posets/elements.py +++ b/src/sage/combinat/posets/elements.py @@ -51,6 +51,17 @@ def __init__(self, poset, element, vertex): self.element = element self.vertex = vertex + def __hash__(self): + r""" + TESTS:: + + sage: P = Poset([[1,2],[4],[3],[4],[]], facade = False) + sage: e = P(0) + sage: hash(e) + 0 + """ + return hash(self.element) + def _repr_(self): """ TESTS:: From 07e740d630e9123c462922e9efa572ab8aff75c0 Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Tue, 18 Aug 2015 22:21:52 -0700 Subject: [PATCH 298/421] Trac 19321: some more hashes --- src/sage/groups/free_group.py | 8 +++++++ .../modform_hecketriangle/abstract_ring.py | 4 ++-- .../modular/overconvergent/weightspace.py | 14 ++++++++++++ src/sage/monoids/free_monoid_element.py | 17 ++++++++++++++ src/sage/structure/element.pyx | 22 +++++++++++++++++-- 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index f328c0efbdc..45ef7a1c10c 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -228,6 +228,14 @@ def __init__(self, parent, x): ElementLibGAP.__init__(self, parent, x) def __hash__(self): + r""" + TESTS:: + + sage: G. = FreeGroup() + sage: hash(a*b*b*~a) + -485698212495963022 # 64-bit + -1876767630 # 32-bit + """ return hash(self.Tietze()) def _latex_(self): diff --git a/src/sage/modular/modform_hecketriangle/abstract_ring.py b/src/sage/modular/modform_hecketriangle/abstract_ring.py index 6577462c05f..9f80cd3de45 100644 --- a/src/sage/modular/modform_hecketriangle/abstract_ring.py +++ b/src/sage/modular/modform_hecketriangle/abstract_ring.py @@ -747,11 +747,11 @@ def diff_alg(self): sage: from sage.modular.modform_hecketriangle.graded_ring import ModularFormsRing sage: ModularFormsRing().diff_alg() - Noncommutative Multivariate Polynomial Ring in X, Y, Z, dX, dY, dZ over Rational Field, nc-relations: {dY*Y: Y*dY + 1, dZ*Z: Z*dZ + 1, dX*X: X*dX + 1} + Noncommutative Multivariate Polynomial Ring in X, Y, Z, dX, dY, dZ over Rational Field, nc-relations: {dZ*Z: Z*dZ + 1, dY*Y: Y*dY + 1, dX*X: X*dX + 1} sage: from sage.modular.modform_hecketriangle.space import CuspForms sage: CuspForms(k=12, base_ring=AA).diff_alg() - Noncommutative Multivariate Polynomial Ring in X, Y, Z, dX, dY, dZ over Rational Field, nc-relations: {dY*Y: Y*dY + 1, dZ*Z: Z*dZ + 1, dX*X: X*dX + 1} + Noncommutative Multivariate Polynomial Ring in X, Y, Z, dX, dY, dZ over Rational Field, nc-relations: {dZ*Z: Z*dZ + 1, dY*Y: Y*dY + 1, dX*X: X*dX + 1} """ # We only use two operators for now which do not involve 'd', so for performance diff --git a/src/sage/modular/overconvergent/weightspace.py b/src/sage/modular/overconvergent/weightspace.py index 3e6e55445f4..a1f65f55d25 100644 --- a/src/sage/modular/overconvergent/weightspace.py +++ b/src/sage/modular/overconvergent/weightspace.py @@ -566,6 +566,20 @@ def chi(self): """ return self._chi + def __hash__(self): + r""" + TESTS:: + + sage: w = pAdicWeightSpace(23)(12, DirichletGroup(23, QQ).0) + sage: hash(w) + -2363716619315244394 # 64-bit + 470225558 # 32-bit + """ + if self._chi.is_trivial(): + return hash(self._k) + else: + return hash( (self._k,self._chi.modulus(),self._chi) ) + def _repr_(self): r""" String representation of self. diff --git a/src/sage/monoids/free_monoid_element.py b/src/sage/monoids/free_monoid_element.py index 7fef15de61c..9872b16c232 100644 --- a/src/sage/monoids/free_monoid_element.py +++ b/src/sage/monoids/free_monoid_element.py @@ -80,6 +80,23 @@ def __init__(self, F, x, check=True): # TODO: should have some other checks here... raise TypeError("Argument x (= %s) is of the wrong type."%x) + def __hash__(self): + r""" + TESTS:: + + sage: R. = FreeMonoid(2) + sage: hash(x) + 1914282862589934403 # 64-bit + 139098947 # 32-bit + sage: hash(y) + 2996819001369607946 # 64-bit + 13025034 # 32-bit + sage: hash(x*y) + 7114093379175463612 # 64-bit + 2092317372 # 32-bit + """ + return hash(tuple(self._element_list)) + def __iter__(self): """ Returns an iterator which yields tuples of variable and exponent. diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 5b71a088b05..b0d93cb1035 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -128,6 +128,8 @@ underscores). # by any element. Derived class must call __init__ ################################################################## +from libc.limits cimport LONG_MAX, LONG_MIN + include "sage/ext/python.pxi" from sage.ext.stdsage cimport * @@ -3279,10 +3281,26 @@ cdef class InfinityElement(RingElement): return ZZ(0) cdef class PlusInfinityElement(InfinityElement): - pass + def __hash__(self): + r""" + TESTS:: + + sage: hash(+infinity) + 9223372036854775807 # 64-bit + 2147483647 # 32-bit + """ + return LONG_MAX cdef class MinusInfinityElement(InfinityElement): - pass + def __hash__(self): + r""" + TESTS:: + + sage: hash(-infinity) + -9223372036854775808 # 64-bit + -2147483648 # 32-bit + """ + return LONG_MIN ################################################################################# From 3e920d0379ae690dd3f16dc9eafdf8498ef5f3ef Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 19 Aug 2015 12:22:42 +0200 Subject: [PATCH 299/421] Trac 19321: hash for linear expression --- src/sage/geometry/linear_expression.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/linear_expression.py b/src/sage/geometry/linear_expression.py index 93e7d05cf4f..ca4773e0449 100644 --- a/src/sage/geometry/linear_expression.py +++ b/src/sage/geometry/linear_expression.py @@ -351,6 +351,18 @@ def change_ring(self, base_ring): return self return P.change_ring(base_ring)(self) + def __hash__(self): + r""" + TESTS:: + + sage: from sage.geometry.linear_expression import LinearExpressionModule + sage: L. = LinearExpressionModule(QQ) + sage: hash(L([0,1])) + 3430019387558 # 64-bit + -1659481946 # 32-bit + """ + return hash(self._coeffs) ^ hash(self._const) + def __cmp__(self, other): """ Compare two linear expressions. @@ -379,10 +391,7 @@ def __cmp__(self, other): False """ assert type(self) is type(other) and self.parent() is other.parent() # guaranteed by framework - c = cmp(self._coeffs, other._coeffs) - if c != 0: return c - c = cmp(self._const, other._const) - return c + return cmp(self._coeffs, other._coeffs) or cmp(self._const, other._const) def evaluate(self, point): """ From 3436b380cb4cfdfeaf4277893fbe76e281551c81 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 19 Aug 2015 12:37:34 +0200 Subject: [PATCH 300/421] Trac 19321: hash for alternating sign matrices --- src/sage/combinat/alternating_sign_matrix.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 930a703e6af..8558936ef7a 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -104,6 +104,17 @@ def __init__(self, parent, asm): self._matrix = asm Element.__init__(self, parent) + def __hash__(self): + r""" + TESTS:: + + sage: A = AlternatingSignMatrices(3) + sage: elt = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + sage: hash(elt) + 12 + """ + return hash(self._matrix) + def _repr_(self): """ Return a string representation of ``self``. @@ -1101,7 +1112,9 @@ def _element_constructor_(self, asm): raise ValueError("Cannot convert between alternating sign matrices of different sizes") if asm in MonotoneTriangles(self._n): return self.from_monotone_triangle(asm) - return self.element_class(self, self._matrix_space(asm)) + m = self._matrix_space(asm) + m.set_immutable() + return self.element_class(self, m) Element = AlternatingSignMatrix @@ -1147,7 +1160,9 @@ def from_monotone_triangle(self, triangle): asm.append(row) prev = v - return self.element_class(self, self._matrix_space(asm)) + m = self._matrix_space(asm) + m.set_immutable() + return self.element_class(self, m) def from_corner_sum(self, corner): r""" From c777f223fdad903062b16b7094821fe45216756a Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 19 Aug 2015 20:10:00 +0200 Subject: [PATCH 301/421] Trac 19321: fix infinite polynomial elements --- src/sage/rings/polynomial/infinite_polynomial_element.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 59e873fe00d..081b6d4f340 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -255,16 +255,15 @@ def _repr_(self): """ return repr(self._p) - def _hash_(self): + def __hash__(self): """ TESTS:: sage: X. = InfinitePolynomialRing(QQ) sage: a = x[0] + x[1] sage: hash(a) # indirect doctest - -6172640511012239345 # 64-bit - -957478897 # 32-bit - + 971115012877883067 # 64-bit + -2103273797 # 32-bit """ return hash(self._p) From 6716eb6e156a2e04298e0ac60e8251e636ac26fc Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 20 Aug 2015 00:58:07 +0200 Subject: [PATCH 302/421] Trac 19321: fix similarity classes --- src/sage/combinat/similarity_class_type.py | 43 ++++++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/similarity_class_type.py b/src/sage/combinat/similarity_class_type.py index 9cbfc8b1774..36f5bc22194 100644 --- a/src/sage/combinat/similarity_class_type.py +++ b/src/sage/combinat/similarity_class_type.py @@ -181,7 +181,7 @@ class type, it is also possible to compute the number of classes of that type from sage.functions.all import factorial from sage.rings.arith import moebius from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.structure.element import Element +from sage.structure.element import Element, parent from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -400,6 +400,25 @@ def __repr__(self): """ return "%s"%([self._deg, self._par]) + def __hash__(self): + r""" + TESTS:: + + sage: PT1 = PrimarySimilarityClassType(2, [3, 2, 1]) + sage: PT2 = PrimarySimilarityClassType(3, [3, 2, 1]) + sage: PT3 = PrimarySimilarityClassType(2, [4, 2, 1]) + sage: hash(PT1) + 5050909583595644741 # 64-bit + 1658169157 # 32-bit + sage: hash(PT2) + 5050909583595644740 # 64-bit + 1658169156 # 32-bit + sage: hash(PT3) + 6312110366011971308 # 64-bit + 1429493484 # 32-bit + """ + return hash(self._deg) ^ hash(tuple(self._par)) + def __eq__(self, other): """ Check equality. @@ -420,9 +439,25 @@ def __eq__(self, other): sage: PT1 == PT5 False """ - if isinstance(other, PrimarySimilarityClassType): - return self.degree() == other.degree() and self.partition() == other.partition() - return False + return isinstance(other, PrimarySimilarityClassType) and \ + self.degree() == other.degree() and \ + self.partition() == other.partition() + + def __ne__(self, other): + r""" + TESTS:: + + sage: PT1 = PrimarySimilarityClassType(2, [3, 2, 1]) + sage: PT2 = PrimarySimilarityClassType(2, Partition([3, 2, 1])) + sage: PT1 != PT2 + False + sage: PT3 = PrimarySimilarityClassType(3, [3, 2, 1]) + sage: PT1 != PT3 + True + """ + return not isinstance(other, PrimarySimilarityClassType) or \ + self.degree() != other.degree() or \ + self.partition() != other.partition() def size(self): """ From ff2b1c517186e3195dd6f89b25a7e2dac901d8b1 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 20 Aug 2015 01:07:04 +0200 Subject: [PATCH 303/421] Trac 19321: fix Kleber tree hash value --- .../rigged_configurations/kleber_tree.py | 56 ++++++++++++------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index 1a059859c79..18cecea1f23 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -24,30 +24,30 @@ sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: KT = KleberTree(['A', 3, 1], [[3,2], [2,1], [1,1], [1,1]]) - sage: for x in set(KT.list()): x - Kleber tree node with weight [1, 0, 3] and upwards edge root [1, 1, 0] - Kleber tree node with weight [0, 2, 2] and upwards edge root [1, 0, 0] - Kleber tree node with weight [2, 1, 2] and upwards edge root [0, 0, 0] - Kleber tree node with weight [2, 0, 0] and upwards edge root [0, 1, 1] - Kleber tree node with weight [0, 0, 2] and upwards edge root [1, 1, 0] - Kleber tree node with weight [0, 1, 0] and upwards edge root [0, 0, 1] - Kleber tree node with weight [3, 0, 1] and upwards edge root [0, 1, 1] - Kleber tree node with weight [0, 1, 0] and upwards edge root [1, 1, 1] - Kleber tree node with weight [1, 1, 1] and upwards edge root [1, 1, 1] - Kleber tree node with weight [0, 0, 2] and upwards edge root [2, 2, 1] + sage: sorted((x.weight.to_vector(), x.up_root.to_vector()) for x in KT.list()) + [((0, 0, 2), (1, 1, 0)), + ((0, 0, 2), (2, 2, 1)), + ((0, 1, 0), (0, 0, 1)), + ((0, 1, 0), (1, 1, 1)), + ((0, 2, 2), (1, 0, 0)), + ((1, 0, 3), (1, 1, 0)), + ((1, 1, 1), (1, 1, 1)), + ((2, 0, 0), (0, 1, 1)), + ((2, 1, 2), (0, 0, 0)), + ((3, 0, 1), (0, 1, 1))] sage: KT = KleberTree(['A', 7, 1], [[3,2], [2,1], [1,1]]) sage: KT Kleber tree of Cartan type ['A', 7, 1] and B = ((3, 2), (2, 1), (1, 1)) - sage: for x in set(KT.list()): x - Kleber tree node with weight [1, 0, 1, 0, 1, 0, 0] and upwards edge root [1, 2, 2, 1, 0, 0, 0] - Kleber tree node with weight [0, 0, 1, 0, 0, 1, 0] and upwards edge root [2, 3, 3, 2, 1, 0, 0] - Kleber tree node with weight [1, 1, 2, 0, 0, 0, 0] and upwards edge root [0, 0, 0, 0, 0, 0, 0] - Kleber tree node with weight [2, 0, 1, 1, 0, 0, 0] and upwards edge root [0, 1, 1, 0, 0, 0, 0] - Kleber tree node with weight [1, 0, 0, 2, 0, 0, 0] and upwards edge root [0, 1, 1, 0, 0, 0, 0] - Kleber tree node with weight [0, 0, 3, 0, 0, 0, 0] and upwards edge root [1, 1, 0, 0, 0, 0, 0] - Kleber tree node with weight [0, 0, 0, 1, 1, 0, 0] and upwards edge root [1, 1, 1, 0, 0, 0, 0] - Kleber tree node with weight [0, 1, 1, 1, 0, 0, 0] and upwards edge root [1, 1, 1, 0, 0, 0, 0] + sage: sorted((x.weight.to_vector(), x.up_root.to_vector()) for x in KT.list()) + [((0, 0, 0, 1, 1, 0, 0), (1, 1, 1, 0, 0, 0, 0)), + ((0, 0, 1, 0, 0, 1, 0), (2, 3, 3, 2, 1, 0, 0)), + ((0, 0, 3, 0, 0, 0, 0), (1, 1, 0, 0, 0, 0, 0)), + ((0, 1, 1, 1, 0, 0, 0), (1, 1, 1, 0, 0, 0, 0)), + ((1, 0, 0, 2, 0, 0, 0), (0, 1, 1, 0, 0, 0, 0)), + ((1, 0, 1, 0, 1, 0, 0), (1, 2, 2, 1, 0, 0, 0)), + ((1, 1, 2, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0)), + ((2, 0, 1, 1, 0, 0, 0), (0, 1, 1, 0, 0, 0, 0))] """ #***************************************************************************** @@ -347,6 +347,22 @@ def multiplicity(self): return mult + def __hash__(self): + r""" + TESTS:: + + sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree + sage: RS = RootSystem(['A', 2]) + sage: WS = RS.weight_space() + sage: R = RS.root_space() + sage: KT = KleberTree(['A', 2, 1], [[1,1]]) + sage: n = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()) + sage: hash(n) + -603608031356818252 # 64-bit + -1956156236 # 32-bit + """ + return hash(self.depth) ^ hash(self.weight) + def __cmp__(self, rhs): r""" Check whether two nodes are equal. From b8b389a530cde4d7dd2c6764cbb4f1b837569995 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 20 Aug 2015 01:18:05 +0200 Subject: [PATCH 304/421] Trac 19321: fix hash for Additive abelian group elements --- .../additive_abelian_group.py | 4 ++-- src/sage/modules/fg_pid/fgp_element.py | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/sage/groups/additive_abelian/additive_abelian_group.py b/src/sage/groups/additive_abelian/additive_abelian_group.py index 51fa899443a..3bf8aef61e6 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_group.py +++ b/src/sage/groups/additive_abelian/additive_abelian_group.py @@ -401,7 +401,7 @@ def __init__(self, cover, rels, gens): Additive abelian group isomorphic to Z/3 """ AdditiveAbelianGroup_class.__init__(self, cover, rels) - self._orig_gens = [self(x) for x in gens] + self._orig_gens = tuple(self(x) for x in gens) def gens(self): r""" @@ -416,7 +416,7 @@ def gens(self): sage: G.smith_form_gens() ((1, 2),) """ - return tuple(self._orig_gens) + return self._orig_gens def identity(self): r""" diff --git a/src/sage/modules/fg_pid/fgp_element.py b/src/sage/modules/fg_pid/fgp_element.py index 2b4f43bb14f..3e47a40fbde 100644 --- a/src/sage/modules/fg_pid/fgp_element.py +++ b/src/sage/modules/fg_pid/fgp_element.py @@ -343,8 +343,28 @@ def vector(self): try: return self.__vector except AttributeError: self.__vector = self.parent().coordinate_vector(self, reduce=True) + self.__vector.set_immutable() return self.__vector + def __hash__(self): + r""" + TESTS:: + + sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ) + sage: W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) + sage: Q = V/W + sage: x = Q.0 + 3*Q.1 + sage: hash(x) + 3713081631933328131 # 64-bit + 1298787075 # 32-bit + + sage: A = AdditiveAbelianGroup([3]) + sage: hash(A.an_element()) + 3430019387558 # 64-bit + -1659481946 # 32-bit + """ + return hash(self.vector()) + def _vector_(self, base_ring=None): """ Support for conversion to vectors. From 03f05ddbc1e1acf7001e09efc30db79199872e31 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 23 Aug 2015 07:53:35 -0300 Subject: [PATCH 305/421] Trac 19321: fix fgp vector conversion --- src/sage/modules/fg_pid/fgp_element.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/fg_pid/fgp_element.py b/src/sage/modules/fg_pid/fgp_element.py index 3e47a40fbde..749f67a3991 100644 --- a/src/sage/modules/fg_pid/fgp_element.py +++ b/src/sage/modules/fg_pid/fgp_element.py @@ -387,10 +387,21 @@ def _vector_(self, base_ring=None): (1, 3) sage: vector(CDF, x) (1.0, 3.0) + + TESTS:: + + sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ) + sage: W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) + sage: Q = V/W + sage: x = Q.0 + 3*Q.1 + sage: vector(x).is_mutable() + True + sage: vector(CDF,x).is_mutable() + True """ v = self.vector() if base_ring is None or v.base_ring() is base_ring: - return v + return v.__copy__() else: return v.change_ring(base_ring) From 42c9d8555a49906123cdf6aaad2aa5c4a0126d7a Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 19 Aug 2015 16:17:11 +0200 Subject: [PATCH 306/421] Trac 19321: hash for polyhedra --- src/sage/geometry/polyhedron/base.py | 26 +++++++++++++++++++ .../polyhedron/ppl_lattice_polytope.py | 17 ++++++------ .../geometry/polyhedron/representation.py | 4 +-- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 0d3fe99bad4..f3a43681522 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -120,6 +120,32 @@ def __init__(self, parent, Vrep, Hrep, **kwds): else: self._init_empty_polyhedron() + def __hash__(self): + r""" + TESTS:: + + sage: K. = QuadraticField(2) + sage: p = Polyhedron(vertices=[(0,1,a),(3,a,5)], + ....: rays=[(a,2,3), (0,0,1)], + ....: base_ring=K) + sage: q = Polyhedron(vertices=[(3,a,5),(0,1,a)], + ....: rays=[(0,0,1), (a,2,3)], + ....: base_ring=K) + sage: hash(p) == hash(q) + True + """ + # TODO: find something better *but* fast + return hash((self.dim(), + self.ambient_dim(), + self.n_Hrepresentation(), + self.n_Vrepresentation(), + self.n_equations(), + self.n_facets(), + self.n_inequalities(), + self.n_lines(), + self.n_rays(), + self.n_vertices())) + def _sage_input_(self, sib, coerced): """ Return Sage command to reconstruct ``self``. diff --git a/src/sage/geometry/polyhedron/ppl_lattice_polytope.py b/src/sage/geometry/polyhedron/ppl_lattice_polytope.py index 8ffaddec672..6b43c095f42 100644 --- a/src/sage/geometry/polyhedron/ppl_lattice_polytope.py +++ b/src/sage/geometry/polyhedron/ppl_lattice_polytope.py @@ -900,18 +900,19 @@ def base_rays(self, fiber, points): ((1),) """ quo = self.base_projection(fiber) - vertices = [] + vertices = set() for p in points: - v = vector(ZZ,quo(p)) + v = quo(p).vector() if v.is_zero(): continue - d = GCD_list(v.list()) - if d>1: - for i in range(0,v.degree()): + d = GCD_list(v.list()) + if d > 1: + v = v.__copy__() + for i in range(v.degree()): v[i] /= d - v.set_immutable() - vertices.append(v) - return tuple(uniq(vertices)) + v.set_immutable() + vertices.add(v) + return tuple(sorted(vertices)) @cached_method def has_IP_property(self): diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index b61b0caa2d5..3249ceb5184 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -135,9 +135,7 @@ def __cmp__(self, other): """ if not isinstance(other, PolyhedronRepresentation): return -1 - type_cmp = cmp(type(self), type(other)) - if (type_cmp != 0): return type_cmp - return cmp(self._vector, other._vector) + return cmp(type(self), type(other)) or cmp(self._vector, other._vector) def vector(self, base_ring=None): """ From bb5c34159dc5f9813d6019f4ffecb25fe462ac58 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 26 Sep 2015 00:16:56 -0300 Subject: [PATCH 307/421] Trac 19321: change output order in documentation --- src/sage/categories/regular_crystals.py | 2 +- src/sage/combinat/posets/posets.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/regular_crystals.py b/src/sage/categories/regular_crystals.py index 9fa8085633b..8f46bd74ad9 100644 --- a/src/sage/categories/regular_crystals.py +++ b/src/sage/categories/regular_crystals.py @@ -560,7 +560,7 @@ def demazure_operator_simple(self, i, ring = None): sage: K = crystals.KirillovReshetikhin(['A',2,1],2,1) sage: t = K(rows=[[3],[2]]) sage: t.demazure_operator_simple(0) - B[[[2, 3]]] + B[[[1, 2]]] + B[[[1, 2]]] + B[[[2, 3]]] TESTS:: diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 58245aa7aca..b665046afcd 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -1236,9 +1236,9 @@ def hasse_diagram(self, wrapped = True): sage: P = Poset((divisors(15), attrcall("divides")), facade = False) sage: H = P.hasse_diagram() sage: H.vertices() - [1, 5, 3, 15] + [1, 3, 5, 15] sage: H.edges() - [(1, 3, None), (1, 5, None), (5, 15, None), (3, 15, None)] + [(1, 3, None), (1, 5, None), (3, 15, None), (5, 15, None)] sage: H.set_latex_options(format="dot2tex") # optional - dot2tex sage: view(H, tight_page=True) # optional - dot2tex, not tested (opens external window) """ From a64ff78d0d1a6d232e3fb6eafad43f66c6ad952b Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Fri, 25 Sep 2015 18:20:53 -0700 Subject: [PATCH 308/421] trac 19321: fix some non-deterministic doctests (and add some deterministic checks) --- .../polynomial/multi_polynomial_ideal.py | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index c0e7bce2c1e..da64b370cdb 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2692,10 +2692,14 @@ def __init__(self, ring, gens, coerce=True, side = "left"): sage: H.inject_variables() Defining x, y, z sage: I = H.ideal([y^2, x^2, z^2-H.one()],coerce=False) # indirect doctest - sage: I + sage: I #random Left Ideal (y^2, x^2, z^2 - 1) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: H.ideal([y^2, x^2, z^2-H.one()], side="twosided") + sage: sorted(I.gens(),key=str) + [x^2, y^2, z^2 - 1] + sage: H.ideal([y^2, x^2, z^2-H.one()], side="twosided") #random Twosided Ideal (y^2, x^2, z^2 - 1) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} + sage: sorted(H.ideal([y^2, x^2, z^2-H.one()], side="twosided").gens(),key=str) + [x^2, y^2, z^2 - 1] sage: H.ideal([y^2, x^2, z^2-H.one()], side="right") Traceback (most recent call last): ... @@ -2726,8 +2730,10 @@ def __call_singular(self, cmd, arg = None): sage: H.inject_variables() Defining x, y, z sage: id = H.ideal(x + y, y + z) - sage: id.std() # indirect doctest + sage: id.std() # indirect doctest # random Left Ideal (z, y, x) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} + sage: sorted(id.std().gens(),key=str) + [x, y, z] """ from sage.libs.singular.function import singular_function fun = singular_function(cmd) @@ -2748,23 +2754,34 @@ def std(self): sage: H.inject_variables() Defining x, y, z sage: I = H.ideal([y^2, x^2, z^2-H.one()],coerce=False) - sage: I.std() + sage: I.std() #random Left Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} + sage: sorted(I.std().gens(),key=str) + [2*x*y - z - 1, x*z + x, x^2, y*z - y, y^2, z^2 - 1] + If the ideal is a left ideal, then std returns a left Groebner basis. But if it is a two-sided ideal, then the output of std and :meth:`twostd` coincide:: sage: JL = H.ideal([x^3, y^3, z^3 - 4*z]) - sage: JL + sage: JL #random Left Ideal (x^3, y^3, z^3 - 4*z) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: JL.std() + sage: sorted(JL.gens(),key=str) + [x^3, y^3, z^3 - 4*z] + sage: JL.std() #random Left Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, 2*x*y*z - z^2 - 2*z, y^3, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} + sage: sorted(JL.std().gens(),key=str) + [2*x*y*z - z^2 - 2*z, x*z^2 + 2*x*z, x^3, y*z^2 - 2*y*z, y^3, z^3 - 4*z] sage: JT = H.ideal([x^3, y^3, z^3 - 4*z], side='twosided') - sage: JT + sage: JT #random Twosided Ideal (x^3, y^3, z^3 - 4*z) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: JT.std() + sage: sorted(JT.gens(),key=str) + [x^3, y^3, z^3 - 4*z] + sage: JT.std() #random Twosided Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, y^2*z - 2*y^2, 2*x*y*z - z^2 - 2*z, x^2*z + 2*x^2, y^3, x*y^2 - y*z, x^2*y - x*z - 2*x, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} + sage: sorted(JT.std().gens(),key=str) + [2*x*y*z - z^2 - 2*z, x*y^2 - y*z, x*z^2 + 2*x*z, x^2*y - x*z - 2*x, x^2*z + 2*x^2, x^3, y*z^2 - 2*y*z, y^2*z - 2*y^2, y^3, z^3 - 4*z] sage: JT.std() == JL.twostd() True @@ -2787,8 +2804,10 @@ def twostd(self): sage: H.inject_variables() Defining x, y, z sage: I = H.ideal([y^2, x^2, z^2-H.one()],coerce=False) - sage: I.twostd() + sage: I.twostd() #random Twosided Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field... + sage: sorted(I.twostd().gens(),key=str) + [2*x*y - z - 1, x*z + x, x^2, y*z - y, y^2, z^2 - 1] ALGORITHM: Uses Singular's twostd command """ @@ -2809,7 +2828,7 @@ def _groebner_strategy(self): sage: A. = FreeAlgebra(QQ, 3) sage: H. = A.g_algebra({y*x:x*y-z, z*x:x*z+2*x, z*y:y*z-2*y}) sage: I = H.ideal([y^2, x^2, z^2-H.one()],coerce=False) - sage: I._groebner_strategy() + sage: I._groebner_strategy() #random Groebner Strategy for ideal generated by 6 elements over Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} @@ -2837,7 +2856,7 @@ def reduce(self,p): sage: A. = FreeAlgebra(QQ, 3) sage: H. = A.g_algebra({y*x:x*y-z, z*x:x*z+2*x, z*y:y*z-2*y}) sage: I = H.ideal([y^2, x^2, z^2-H.one()],coerce=False, side='twosided') - sage: Q = H.quotient(I); Q + sage: Q = H.quotient(I); Q #random Quotient of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} by the ideal (y^2, x^2, z^2 - 1) @@ -2847,10 +2866,8 @@ def reduce(self,p): Here, we see that the relation that we just found in the quotient is actually a consequence of the given relations:: - sage: I.std() - Twosided Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) - of Noncommutative Multivariate Polynomial Ring in x, y, z over - Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} + sage: H.2^2-H.one() in I.std().gens() + True Here is the corresponding direct test:: @@ -2869,10 +2886,10 @@ def _contains_(self,p): sage: A. = FreeAlgebra(QQ, 3) sage: H. = A.g_algebra({y*x:x*y-z, z*x:x*z+2*x, z*y:y*z-2*y}) sage: JL = H.ideal([x^3, y^3, z^3 - 4*z]) - sage: JL.std() + sage: JL.std() #random Left Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, 2*x*y*z - z^2 - 2*z, y^3, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} sage: JT = H.ideal([x^3, y^3, z^3 - 4*z], side='twosided') - sage: JT.std() + sage: JT.std() #random Twosided Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, y^2*z - 2*y^2, 2*x*y*z - z^2 - 2*z, x^2*z + 2*x^2, y^3, x*y^2 - y*z, x^2*y - x*z - 2*x, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} Apparently, ``x*y^2-y*z`` should be in the two-sided, but not From 93b537d6d02ba40bb5253faeae2aa293756b055a Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Fri, 25 Sep 2015 17:57:07 -0700 Subject: [PATCH 309/421] trac 19321: doctest fixes and making some code deterministic --- src/sage/groups/conjugacy_classes.py | 2 +- src/sage/groups/finitely_presented_named.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/groups/conjugacy_classes.py b/src/sage/groups/conjugacy_classes.py index 3553af8e8a0..d528090a295 100644 --- a/src/sage/groups/conjugacy_classes.py +++ b/src/sage/groups/conjugacy_classes.py @@ -180,7 +180,7 @@ def __iter__(self): sage: a = G(a) sage: C = ConjugacyClass(G, a) sage: it = iter(C) - sage: [next(it) for _ in range(5)] + sage: [next(it) for _ in range(5)] # random (nothing guarantees enumeration order) [ [1 1] [ 2 1] [ 0 1] [ 3 1] [ 3 4] [0 1], [-1 0], [-1 2], [-4 -1], [-1 -1] diff --git a/src/sage/groups/finitely_presented_named.py b/src/sage/groups/finitely_presented_named.py index 31214fc9751..88fd6df7420 100644 --- a/src/sage/groups/finitely_presented_named.py +++ b/src/sage/groups/finitely_presented_named.py @@ -130,14 +130,14 @@ def FinitelyGeneratedAbelianPresentation(int_list): sage: groups.presentation.FGAbelian([0,0]) Finitely presented group < a, b | a^-1*b^-1*a*b > sage: groups.presentation.FGAbelian([0,0,0]) - Finitely presented group < a, b, c | a^-1*c^-1*a*c, a^-1*b^-1*a*b, c^-1*b^-1*c*b > + Finitely presented group < a, b, c | a^-1*b^-1*a*b, a^-1*c^-1*a*c, b^-1*c^-1*b*c > And various infinite abelian groups:: sage: groups.presentation.FGAbelian([0,2]) Finitely presented group < a, b | a^2, a^-1*b^-1*a*b > sage: groups.presentation.FGAbelian([0,2,2]) - Finitely presented group < a, b, c | a^2, b^2, a^-1*c^-1*a*c, a^-1*b^-1*a*b, c^-1*b^-1*c*b > + Finitely presented group < a, b, c | a^2, b^2, a^-1*b^-1*a*b, a^-1*c^-1*a*c, b^-1*c^-1*b*c > Outputs are reduced to minimal generators and relations:: @@ -193,7 +193,7 @@ def FinitelyGeneratedAbelianPresentation(int_list): ret_rls = [F([i+1])**invariants[i] for i in range(len(invariants)) if invariants[i]!=0] # Build commutator relations - gen_pairs = list(Set(F.gens()).subsets(2)) + gen_pairs = [[F.gen(i),F.gen(j)] for i in range(F.ngens()-1) for j in range(i+1,F.ngens())] ret_rls = ret_rls + [x[0]**(-1)*x[1]**(-1)*x[0]*x[1] for x in gen_pairs] return FinitelyPresentedGroup(F, tuple(ret_rls)) From 23b2b461a1821aba592c09e85ae9c19846bad1bc Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Fri, 25 Sep 2015 18:34:28 -0700 Subject: [PATCH 310/421] trac 19321: fix some doctests in polynomial/plural.pyx ncalgebras print a set and hence their representation is non-deterministic --- src/sage/libs/singular/groebner_strategy.pyx | 4 ++-- src/sage/rings/polynomial/plural.pyx | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/sage/libs/singular/groebner_strategy.pyx b/src/sage/libs/singular/groebner_strategy.pyx index 231cce0ed21..b4c2be95922 100644 --- a/src/sage/libs/singular/groebner_strategy.pyx +++ b/src/sage/libs/singular/groebner_strategy.pyx @@ -312,7 +312,7 @@ cdef class NCGroebnerStrategy(SageObject): sage: A. = FreeAlgebra(QQ, 3) sage: H. = A.g_algebra({y*x:x*y-z, z*x:x*z+2*x, z*y:y*z-2*y}) sage: I = H.ideal([y^2, x^2, z^2-H.one()]) - sage: NCGroebnerStrategy(I) + sage: NCGroebnerStrategy(I) #random Groebner Strategy for ideal generated by 3 elements over Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} TESTS:: @@ -412,7 +412,7 @@ cdef class NCGroebnerStrategy(SageObject): sage: H. = A.g_algebra({y*x:x*y-z, z*x:x*z+2*x, z*y:y*z-2*y}) sage: I = H.ideal([y^2, x^2, z^2-H.one()]) sage: strat = NCGroebnerStrategy(I) - sage: strat # indirect doctest + sage: strat # indirect doctest #random Groebner Strategy for ideal generated by 3 elements over Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} """ return "Groebner Strategy for ideal generated by %d elements over %s"%(self._ideal.ngens(),self._parent) diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index 6dc97afb04e..56a572dc4fb 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -161,9 +161,9 @@ class G_AlgFactory(UniqueFactory): TEST:: sage: A. = FreeAlgebra(QQ, 3) - sage: A.g_algebra({y*x:x*y-z, z*x:x*z+2*x, z*y:y*z-2*y}) # indirect doctest - Noncommutative Multivariate Polynomial Ring in x, y, z over Rational - Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} + sage: H=A.g_algebra({y*x:x*y-z, z*x:x*z+2*x, z*y:y*z-2*y}) + sage: sorted(H.relations().iteritems(),key=str) + [(y*x, x*y - z), (z*x, x*z + 2*x), (z*y, y*z - 2*y)] """ # key = (base_ring,names, c,d, order, category) @@ -1711,10 +1711,13 @@ cdef class NCPolynomial_plural(RingElement): The Groebner basis shows that the result is correct:: - sage: I.std() + sage: I.std() #random Left Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} + sage: sorted(I.std().gens(),key=str) + [2*x*y - z - 1, x*z + x, x^2, y*z - y, y^2, z^2 - 1] + """ cdef ideal *_I @@ -2954,8 +2957,12 @@ def ExteriorAlgebra(base_ring, names,order='degrevlex'): EXAMPLES:: sage: from sage.rings.polynomial.plural import ExteriorAlgebra - sage: E = ExteriorAlgebra(QQ, ['x', 'y', 'z']) ; E + sage: E = ExteriorAlgebra(QQ, ['x', 'y', 'z']) ; E #random Quotient of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: -x*z, z*y: -y*z, y*x: -x*y} by the ideal (z^2, y^2, x^2) + sage: sorted(E.cover().domain().relations().iteritems(),key=str) + [(y*x, -x*y), (z*x, -x*z), (z*y, -y*z)] + sage: sorted(E.cover().kernel().gens(),key=str) + [x^2, y^2, z^2] sage: E.inject_variables() Defining xbar, ybar, zbar sage: x,y,z = (xbar,ybar,zbar) From bebb8af5eaa7ff834a03e9c887da69b118a257af Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 30 Sep 2015 18:17:27 -0300 Subject: [PATCH 311/421] Trac 19321: fix a doctest --- src/sage/categories/regular_crystals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/regular_crystals.py b/src/sage/categories/regular_crystals.py index 8f46bd74ad9..9fa8085633b 100644 --- a/src/sage/categories/regular_crystals.py +++ b/src/sage/categories/regular_crystals.py @@ -560,7 +560,7 @@ def demazure_operator_simple(self, i, ring = None): sage: K = crystals.KirillovReshetikhin(['A',2,1],2,1) sage: t = K(rows=[[3],[2]]) sage: t.demazure_operator_simple(0) - B[[[1, 2]]] + B[[[2, 3]]] + B[[[2, 3]]] + B[[[1, 2]]] TESTS:: From fe306f971ac9edde2cec32997c3cff7b4db1ce1b Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 29 Sep 2015 21:30:44 -0300 Subject: [PATCH 312/421] Trac 19322: faster longest_common_prefix --- src/sage/combinat/words/word_char.pyx | 153 +++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/words/word_char.pyx b/src/sage/combinat/words/word_char.pyx index bef4c943c05..3940c5a41eb 100644 --- a/src/sage/combinat/words/word_char.pyx +++ b/src/sage/combinat/words/word_char.pyx @@ -550,7 +550,6 @@ cdef class WordDatatype_char(WordDatatype): return w._new_c(data, new_length, None) - @cython.boundscheck(False) def has_prefix(self, other): r""" Test whether ``other`` is a prefix of ``self``. @@ -573,6 +572,22 @@ cdef class WordDatatype_char(WordDatatype): True sage: w.has_prefix(w[1:]) False + + TESTS: + + :trac:`19322`:: + + sage: W = Words([0,1,2]) + sage: w = W([0,1,0,2]) + sage: w.has_prefix(words.FibonacciWord()) + False + + sage: w.has_prefix([0,1,0,2,0]) + False + sage: w.has_prefix([0,1,0,2]) + True + sage: w.has_prefix([0,1,0]) + True """ cdef size_t i cdef WordDatatype_char w @@ -582,15 +597,16 @@ cdef class WordDatatype_char(WordDatatype): w = other if w._length > self._length: return False - return memcmp(self._data, w._data, w._length) == 0 + return memcmp(self._data, w._data, w._length * sizeof(unsigned char)) == 0 elif PySequence_Check(other): # python level - if len(other) > self._length: + from sage.combinat.words.infinite_word import InfiniteWord_class + if isinstance(other, InfiniteWord_class) or len(other) > len(self): return False for i in range(len(other)): - if other[i] != self._data[i]: + if other[i] != self[i]: return False return True @@ -638,3 +654,132 @@ cdef class WordDatatype_char(WordDatatype): return memcmp(self._data, self._data + l, l * sizeof(unsigned char)) == 0 + + def longest_common_prefix(self, other): + r""" + Return the longest common prefix of this word and ``other``. + + EXAMPLES:: + + sage: W = Words([0,1,2]) + sage: W([0,1,0,2]).longest_common_prefix([0,1]) + word: 01 + sage: u = W([0,1,0,0,1]) + sage: v = W([0,1,0,2]) + sage: u.longest_common_prefix(v) + word: 010 + sage: v.longest_common_prefix(u) + word: 010 + + Using infinite words is also possible (and the return type is also a + of the same type as ``self``):: + + sage: W([0,1,0,0]).longest_common_prefix(words.FibonacciWord()) + word: 0100 + sage: type(_) + + + An example of an intensive usage:: + + sage: W = Words([0,1]) + sage: w = words.FibonacciWord() + sage: w = W(list(w[:5000])) + sage: L = [[len(w[n:].longest_common_prefix(w[n+fibonacci(i):])) + ....: for i in range(5,15)] for n in range(1,1000)] + sage: for n,l in enumerate(L): + ....: if l.count(0) > 4: print n+1,l + 375 [0, 13, 0, 34, 0, 89, 0, 233, 0, 233] + 376 [0, 12, 0, 33, 0, 88, 0, 232, 0, 232] + 608 [8, 0, 21, 0, 55, 0, 144, 0, 377, 0] + 609 [7, 0, 20, 0, 54, 0, 143, 0, 376, 0] + 985 [0, 13, 0, 34, 0, 89, 0, 233, 0, 610] + 986 [0, 12, 0, 33, 0, 88, 0, 232, 0, 609] + """ + cdef WordDatatype_char w + cdef size_t i + cdef size_t m + + if isinstance(other, WordDatatype_char): + # C level + # (this can be much faster if we allow to compare larger memory + # zones) + w = other + m = self._length if self._length <= w._length else w._length + for i in range(m): + if self._data[i] != w._data[i]: + break + else: + if self._length <= w._length: + return self + else: + return other + + return self._new_c(self._data, i, self) + + elif PySequence_Check(other): + # Python level + # we avoid to call len(other) since it might be an infinite word + from itertools import islice + for i,a in enumerate(islice(other, self._length)): + if self._data[i] != a: + break + else: + i += 1 + + return self._new_c(self._data, i, self) + + raise TypeError("not able to initialize a word from {}".format(other)) + + def longest_common_suffix(self, other): + r""" + Return the longest common suffix between this word and ``other``. + + EXAMPLES:: + + sage: W = Words([0,1,2]) + sage: W([0,1,0,2]).longest_common_suffix([2,0,2]) + word: 02 + sage: u = W([0,1,0,0,1]) + sage: v = W([1,2,0,0,1]) + sage: u.longest_common_suffix(v) + word: 001 + sage: v.longest_common_suffix(u) + word: 001 + """ + cdef WordDatatype_char w + cdef size_t i + cdef size_t m + + if isinstance(other, WordDatatype_char): + # C level + # (this can be much faster if we could compare larger memory + # zones) + w = other + m = self._length if self._length <= w._length else w._length + for i in range(m): + if self._data[self._length-i-1] != w._data[w._length-i-1]: + break + else: + if self._length <= w._length: + return self + else: + return other + + return self._new_c(self._data+self._length-i, i, self) + + elif PySequence_Check(other): + # Python level + m = self._length if self._length <= len(other) else len(other) + for i in range(m): + if self._data[self._length-i-1] != other[len(other)-i-1]: + break + else: + if self._length == m: + return self + else: + i += 1 + + return self._new_c(self._data+self._length-i, i, self) + + raise TypeError("not able to initialize a word from {}".format(other)) + From 0f9019534834ca1b9d38d5dcb9625de7505b56c8 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 1 Oct 2015 10:16:13 +0200 Subject: [PATCH 313/421] Force reconfiguration after GCC installation --- build/pkgs/gcc/spkg-install | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/pkgs/gcc/spkg-install b/build/pkgs/gcc/spkg-install index a93411334ad..f9c7fe690e9 100755 --- a/build/pkgs/gcc/spkg-install +++ b/build/pkgs/gcc/spkg-install @@ -137,3 +137,7 @@ $MAKE install # Force re-installation of mpir, mpfr and mpc with the GCC we just built. cd "$SAGE_SPKG_INST" rm -f mpir-* mpfr-* mpc-* + +# Force re-configuration: the next time that "make" is run, we don't +# want GCC to be built again, see Trac #19324 +touch "$SAGE_ROOT/configure" From 94166b67c1a2e0a06770b7e7fb6acffd71d480f6 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 1 Oct 2015 11:23:26 +0200 Subject: [PATCH 314/421] trac #19042: Review --- src/sage/sat/converters/polybori.py | 2 +- src/sage/sat/solvers/sat_lp.py | 6 +++--- src/sage/sat/solvers/satsolver.pyx | 9 ++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/sage/sat/converters/polybori.py b/src/sage/sat/converters/polybori.py index bbc3b64ab8c..11b5d295b14 100644 --- a/src/sage/sat/converters/polybori.py +++ b/src/sage/sat/converters/polybori.py @@ -154,7 +154,7 @@ def var(self, m=None, decision=None): INPUT: - ``m`` - something the new variables maps to, usually a monomial - - ``decision`` - is this variable a deicison variable? + - ``decision`` - is this variable a decision variable? EXAMPLE:: diff --git a/src/sage/sat/solvers/sat_lp.py b/src/sage/sat/solvers/sat_lp.py index 7c8b2fe2e8d..1a74206f162 100644 --- a/src/sage/sat/solvers/sat_lp.py +++ b/src/sage/sat/solvers/sat_lp.py @@ -2,7 +2,7 @@ Solve SAT problems Integer Linear Programming The class defined here is a :class:`~sage.sat.solvers.satsolver.SatSolver` that -solves its instance using :class:`MixedIntegerLinearProgram`. Its performances +solves its instance using :class:`MixedIntegerLinearProgram`. Its performance can be expected to be slower than when using :class:`~sage.sat.solvers.cryptominisat.cryptominisat.CryptoMiniSat`. """ @@ -119,7 +119,7 @@ def __call__(self): ....: return S sage: S = is_bipartite_SAT(graphs.CycleGraph(6)) sage: S() # random - [None, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0] + [None, True, False, True, False, True, False] sage: True in S() True sage: S = is_bipartite_SAT(graphs.CycleGraph(7)) @@ -133,7 +133,7 @@ def __call__(self): b = self._LP.get_values(self._vars) n = max(b) - return [None]+[b.get(i,False) for i in range(1,n+1)] + return [None]+[bool(b.get(i,0)) for i in range(1,n+1)] def __repr__(self): """ diff --git a/src/sage/sat/solvers/satsolver.pyx b/src/sage/sat/solvers/satsolver.pyx index 9666c7cae22..734393d4800 100644 --- a/src/sage/sat/solvers/satsolver.pyx +++ b/src/sage/sat/solvers/satsolver.pyx @@ -318,18 +318,17 @@ def SAT(solver=None): if solver is None: try: from sage.sat.solvers.cryptominisat.cryptominisat import CryptoMiniSat - cryptominisat_available = True + solver = "cryptominisat" except ImportError: - cryptominisat_available = False + solver = "LP" - if (solver == 'cryptominisat' or - (solver is None and cryptominisat_available)): + if solver == 'cryptominisat': try: from sage.sat.solvers.cryptominisat.cryptominisat import CryptoMiniSat except ImportError: raise PackageNotFoundError("cryptominisat") return CryptoMiniSat() - elif (solver == "LP" or solver is None): + elif solver == "LP": from sat_lp import SatLP return SatLP() else: From f603730e82659f72d78b15af4e4d01c28f3bf509 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Thu, 1 Oct 2015 20:38:54 +0200 Subject: [PATCH 315/421] Updated Sage version to 6.9.rc1 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- src/bin/sage-banner | 2 +- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 20905998067..9bd68a18d71 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 6.9.rc0, released 2015-09-25 +Sage version 6.9.rc1, released 2015-10-01 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index cf5c6b5db1c..478eca36b16 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=8cfd9c770b0b2b6740da8861b8e5fb55be6a1961 -md5=e24e7847a5df019a174b8a8f4d1928dd -cksum=1203608624 +sha1=8a65cdb752db6a5dec7455b4ad4a19828d3c62d3 +md5=278cd95661b3854ae016f02591ec3481 +cksum=4192095503 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index ee977b5ecd7..4699eb3cc96 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -115 +116 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 37584805324..e18dae9f900 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath Version 6.9.rc0, Release Date: 2015-09-25 │ +│ SageMath Version 6.9.rc1, Release Date: 2015-10-01 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index e5b60c9973f..14674cf646a 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='6.9.rc0' -SAGE_RELEASE_DATE='2015-09-25' +SAGE_VERSION='6.9.rc1' +SAGE_RELEASE_DATE='2015-10-01' diff --git a/src/sage/version.py b/src/sage/version.py index 3b8f0aa4ca7..ab18d3e7efa 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '6.9.rc0' -date = '2015-09-25' +version = '6.9.rc1' +date = '2015-10-01' From 048b036934efaefb3a4c38b8a5d0b90ff25dc995 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 1 Oct 2015 22:53:04 +0200 Subject: [PATCH 316/421] Implement conversion of interval fields to real/complex fields --- src/sage/rings/complex_interval.pyx | 22 +++++- src/sage/rings/complex_interval_field.py | 9 ++- src/sage/rings/qqbar.py | 4 +- src/sage/rings/real_mpfi.pxd | 6 +- src/sage/rings/real_mpfi.pyx | 93 ++++++++++++++---------- 5 files changed, 83 insertions(+), 51 deletions(-) diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index 2fc79377f4a..655600142e2 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -51,10 +51,9 @@ from complex_number cimport ComplexNumber import complex_interval_field from complex_field import ComplexField import sage.misc.misc -import integer +cimport integer import infinity -import real_mpfi -import real_mpfr +cimport real_mpfi cimport real_mpfr @@ -927,6 +926,23 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): return x + def _complex_mpfr_field_(self, field): + """ + Convert to a complex field. + + EXAMPLES:: + + sage: re = RIF("1.2") + sage: im = RIF(2, 3) + sage: a = ComplexIntervalField(30)(re, im) + sage: CC(a) + 1.20000000018626 + 2.50000000000000*I + """ + cdef ComplexNumber x = field(0) + mpfi_mid(x.__re, self.__re) + mpfi_mid(x.__im, self.__im) + return x + def __int__(self): """ Convert ``self`` to an ``int``. diff --git a/src/sage/rings/complex_interval_field.py b/src/sage/rings/complex_interval_field.py index a400f29f278..e147a410be5 100644 --- a/src/sage/rings/complex_interval_field.py +++ b/src/sage/rings/complex_interval_field.py @@ -377,10 +377,15 @@ def __call__(self, x, im=None): 2 + 3*I sage: CIF(pi, e) 3.141592653589794? + 2.718281828459046?*I + sage: ComplexIntervalField(100)(CIF(RIF(2,3))) + 3.? """ if im is None: - if isinstance(x, complex_interval.ComplexIntervalFieldElement) and x.parent() is self: - return x + if isinstance(x, complex_interval.ComplexIntervalFieldElement): + if x.parent() is self: + return x + else: + return complex_interval.ComplexIntervalFieldElement(self, x) elif isinstance(x, complex_double.ComplexDoubleElement): return complex_interval.ComplexIntervalFieldElement(self, x.real(), x.imag()) elif isinstance(x, str): diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 87143860d09..b12789b9194 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -362,8 +362,8 @@ AA(2) Just for fun, let's try ``sage_input`` on a very complicated expression. The -output of this example changed with the rewritting of polynomial multiplication -algorithms in #10255:: +output of this example changed with the rewriting of polynomial multiplication +algorithms in :trac:`10255`:: sage: rt2 = sqrt(AA(2)) sage: rt3 = sqrt(QQbar(3)) diff --git a/src/sage/rings/real_mpfi.pxd b/src/sage/rings/real_mpfi.pxd index 5996b03d2f5..d90d471627e 100644 --- a/src/sage/rings/real_mpfi.pxd +++ b/src/sage/rings/real_mpfi.pxd @@ -2,15 +2,13 @@ from sage.libs.mpfi cimport * cimport sage.rings.ring -cimport sage.structure.element from sage.structure.element cimport RingElement -from rational import Rational from rational cimport Rational cimport real_mpfr -cdef class RealIntervalFieldElement(sage.structure.element.RingElement) # forward decl +cdef class RealIntervalFieldElement(RingElement) # forward decl cdef class RealIntervalField_class(sage.rings.ring.Field): cdef int __prec @@ -35,7 +33,7 @@ cdef class RealIntervalField_class(sage.rings.ring.Field): cdef RealIntervalFieldElement _new(self) -cdef class RealIntervalFieldElement(sage.structure.element.RingElement): +cdef class RealIntervalFieldElement(RingElement): cdef mpfi_t value cdef char init cdef RealIntervalFieldElement _new(self) diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 85d3b750787..b3ba9c88ebb 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -239,6 +239,7 @@ Comparisons with numpy types are right (see :trac:`17758` and :trac:`18076`):: import math # for log import sys +import operator include 'sage/ext/interrupt.pxi' include "sage/ext/cdefs.pxi" @@ -246,32 +247,21 @@ from cpython.mem cimport * from cpython.string cimport * cimport sage.rings.ring -import sage.rings.ring - cimport sage.structure.element from sage.structure.element cimport RingElement, Element, ModuleElement -import sage.structure.element cimport real_mpfr -from real_mpfr cimport RealField_class, RealNumber -from real_mpfr import RealField -import real_mpfr - -import operator +from real_mpfr cimport RealField_class, RealNumber, RealField +from sage.libs.mpfr cimport MPFR_RNDN, MPFR_RNDZ, MPFR_RNDU, MPFR_RNDD, MPFR_RNDA -from integer import Integer from integer cimport Integer - -from real_double import RealDoubleElement from real_double cimport RealDoubleElement import sage.rings.complex_field - import sage.rings.infinity from sage.structure.parent_gens cimport ParentWithGens -cdef class RealIntervalFieldElement(sage.structure.element.RingElement) #***************************************************************************** # @@ -1115,16 +1105,13 @@ cdef class RealIntervalField_class(sage.rings.ring.Field): return self(-1) raise ValueError, "No %sth root of unity in self"%n -R = RealIntervalField() #***************************************************************************** # # RealIntervalFieldElement -- element of Real Field # -# -# #***************************************************************************** -cdef class RealIntervalFieldElement(sage.structure.element.RingElement): +cdef class RealIntervalFieldElement(RingElement): """ A real number interval. """ @@ -3073,33 +3060,59 @@ cdef class RealIntervalFieldElement(sage.structure.element.RingElement): # Conversions ########################################### -# def __float__(self): -# return mpfr_get_d(self.value, (self._parent).rnd) - -# def __int__(self): -# """ -# Returns integer truncation of this real number. -# """ -# s = self.str(32) -# i = s.find('.') -# return int(s[:i], 32) + def _mpfr_(self, RealField_class field): + """ + Convert to a real field, honoring the rounding mode of the + real field. -# def __long__(self): -# """ -# Returns long integer truncation of this real number. -# """ -# s = self.str(32) -# i = s.find('.') -# return long(s[:i], 32) + EXAMPLES:: -# def __complex__(self): -# return complex(float(self)) + sage: a = RealIntervalField(30)("1.2") + sage: RR(a) + 1.20000000018626 + sage: b = RIF(-1, 3) + sage: RR(b) + 1.00000000000000 -# def _complex_number_(self): -# return sage.rings.complex_field.ComplexField(self.prec())(self) + With different rounding modes:: -# def _pari_(self): -# return sage.libs.pari.all.pari.new_with_bits_prec(str(self), (self._parent).__prec) + sage: RealField(53, rnd="RNDU")(a) + 1.20000000111759 + sage: RealField(53, rnd="RNDD")(a) + 1.19999999925494 + sage: RealField(53, rnd="RNDZ")(a) + 1.19999999925494 + sage: RealField(53, rnd="RNDU")(b) + 3.00000000000000 + sage: RealField(53, rnd="RNDD")(b) + -1.00000000000000 + sage: RealField(53, rnd="RNDZ")(b) + 0.000000000000000 + """ + cdef RealNumber x = field._new() + if field.rnd == MPFR_RNDN: + mpfi_mid(x.value, self.value) + elif field.rnd == MPFR_RNDD: + mpfi_get_left(x.value, self.value) + elif field.rnd == MPFR_RNDU: + mpfi_get_right(x.value, self.value) + elif field.rnd == MPFR_RNDZ: + if mpfi_is_strictly_pos_default(self.value): # interval is > 0 + mpfi_get_left(x.value, self.value) + elif mpfi_is_strictly_neg_default(self.value): # interval is < 0 + mpfi_get_right(x.value, self.value) + else: + mpfr_set_zero(x.value, 1) # interval contains 0 + elif field.rnd == MPFR_RNDA: + # return the endpoint which is furthest from 0 + lo, hi = self.endpoints() + if hi.abs() >= lo.abs(): + mpfi_get_right(x.value, self.value) + else: + mpfi_get_left(x.value, self.value) + else: + raise AssertionError("%s has unknown rounding mode"%field) + return x def unique_sign(self): r""" From 752c401b852050c48186439a727040e3d81d90be Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 1 Oct 2015 23:22:36 +0200 Subject: [PATCH 317/421] Use the new conversions in qqbar --- src/sage/rings/qqbar.py | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index b12789b9194..8fc07afb441 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -4553,13 +4553,13 @@ def complex_number(self, field): EXAMPLES:: sage: a = QQbar.zeta(5) - sage: a.complex_number(CIF) + sage: a.complex_number(CC) 0.309016994374947 + 0.951056516295154*I - sage: (a + a.conjugate()).complex_number(CIF) + sage: (a + a.conjugate()).complex_number(CC) 0.618033988749895 - 5.42101086242752e-20*I """ v = self.interval(ComplexIntervalField(field.prec())) - return v.center() + return field(v) def complex_exact(self, field): r""" @@ -5204,21 +5204,7 @@ def real_number(self, field): 1.41421356237309 """ v = self.interval(RealIntervalField(field.prec())) - - mode = field.rounding_mode() - if mode == 'RNDN': - return v.center() - if mode == 'RNDD': - return v.lower() - if mode == 'RNDU': - return v.upper() - if mode == 'RNDZ': - if v > 0: - return field(v.lower()) - elif v < 0: - return field(v.upper()) - else: - return field(0) + return field(v) _mpfr_ = real_number From 88b74bba339c1ff53063ebe43819b222fd478f1b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 1 Oct 2015 21:53:50 -0400 Subject: [PATCH 318/421] Trac #19332: Add discrete_complementarity_set() method for cones. Any sort of "linear property" on a cone can usually be verified on a generating set of the cone instead of the whole thing. This involves a finite number of steps for a polyhedral cone. The discrete complementarity set is a finite subset, consisting of generators, of the usual complementarity set that appears in complementarity and mathematical programming problems. Having it available lets us check complementarity properties on the cone. This commit adds the method, examples, and tests. --- src/sage/geometry/cone.py | 94 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 936b8c434ee..e425afad19f 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -4348,6 +4348,100 @@ def lineality(self): """ return self.linear_subspace().dimension() + def discrete_complementarity_set(self): + r""" + Compute a discrete complementarity set of this cone. + + A discrete complementarity set of a cone is the set of all + orthogonal pairs `(x,s)` where `x` is in some fixed generating + set of the cone, and `s` is in some fixed generating set of its + dual. The generators chosen for this cone and its dual are + simply their :meth:`~IntegralRayCollection.rays`. + + OUTPUT: + + A list of pairs `(x,s)` such that, + + * `x` and `s` are orthogonal vectors. + * `x` is one of this cone's :meth:`~IntegralRayCollection.rays`. + * `s` is one of the :meth:`~IntegralRayCollection.rays` of this + cone's :meth:`dual`. + + REFERENCES: + + .. [Orlitzky] M. Orlitzky. The Lyapunov rank of an improper cone. + http://www.optimization-online.org/DB_HTML/2015/10/5135.html + + EXAMPLES: + + The discrete complementarity set of the nonnegative orthant + consists of pairs of standard basis vectors:: + + sage: K = Cone([(1,0),(0,1)]) + sage: K.discrete_complementarity_set() + [((1, 0), (0, 1)), ((0, 1), (1, 0))] + + If the cone consists of a single ray, the second components of + the discrete complementarity set should generate the orthogonal + complement of that ray:: + + sage: K = Cone([(1,0)]) + sage: K.discrete_complementarity_set() + [((1, 0), (0, 1)), ((1, 0), (0, -1))] + sage: K = Cone([(1,0,0)]) + sage: K.discrete_complementarity_set() + [((1, 0, 0), (0, 1, 0)), + ((1, 0, 0), (0, -1, 0)), + ((1, 0, 0), (0, 0, 1)), + ((1, 0, 0), (0, 0, -1))] + + When the cone is the entire space, its dual is the trivial cone, + so the discrete complementarity set is empty:: + + sage: K = Cone([(1,0),(-1,0),(0,1),(0,-1)]) + sage: K.discrete_complementarity_set() + [] + + Likewise when this cone is trivial (its dual is the entire space):: + + sage: L = ToricLattice(0) + sage: K = Cone([], ToricLattice(0)) + sage: K.discrete_complementarity_set() + [] + + TESTS: + + The complementarity set of the dual can be obtained by switching + components in the complementarity set of the original cone:: + + sage: set_random_seed() + sage: K = random_cone(max_ambient_dim=6) + sage: dcs_dual = K.dual().discrete_complementarity_set() + sage: expected = [ (x,s) for (s,x) in dcs_dual ] + sage: actual = K.discrete_complementarity_set() + sage: sorted(actual) == sorted(expected) + True + + The pairs in the discrete complementarity set are in fact + complementary:: + + sage: set_random_seed() + sage: K = random_cone(max_ambient_dim=6) + sage: dcs = K.discrete_complementarity_set() + sage: sum([ x.inner_product(s).abs() for (x,s) in dcs ]) + 0 + + """ + V = self.lattice().vector_space() + + # Convert rays to vectors so that we can compute inner products. + G1 = [ V(x) for x in self.rays() ] + + # We also convert the generators of the dual cone so that we + # return pairs of vectors and not (vector, ray) pairs. + G2 = [ V(s) for s in self.dual().rays() ] + + return [ (x,s) for x in G1 for s in G2 if x.inner_product(s) == 0 ] def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, From 75922c99d315a0b775da7cd41405518018d37b67 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 1 Oct 2015 10:23:07 +0200 Subject: [PATCH 319/421] trac #19325: cython() does not know how to compile c++ --- src/sage/misc/cython.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 01ee022b37d..8bacfe1ebc7 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -454,7 +454,6 @@ def cython(filename, verbose=False, compile_message=False, include_dirs = %s) """%(extra_args, name, name, extension, additional_source_files, libs, language, includes) open('%s/setup.py'%build_dir,'w').write(setup) - cython_include = ' '.join(["-I '%s'"%x for x in includes if len(x.strip()) > 0 ]) options = ['-p'] @@ -463,7 +462,12 @@ def cython(filename, verbose=False, compile_message=False, if sage_namespace: options.append('--pre-import sage.all') - cmd = "cd '%s' && cython %s %s '%s.pyx' 1>log 2>err " % (build_dir, ' '.join(options), cython_include, name) + cmd = "cd '{DIR}' && cython {OPT} {INC} {LANG} '{NAME}.pyx' 1>log 2>err ".format( + DIR=build_dir, + OPT=' '.join(options), + INC=cython_include, + LANG='--cplus' if language=='c++' else '', + NAME=name) if create_local_c_file: target_c = '%s/_%s.c'%(os.path.abspath(os.curdir), base) @@ -481,9 +485,6 @@ def cython(filename, verbose=False, compile_message=False, err = subtract_from_line_numbers(open('%s/err'%build_dir).read(), offset) raise RuntimeError("Error converting {} to C:\n{}\n{}".format(filename, log, err)) - if language=='c++': - os.system("cd '%s' && mv '%s.c' '%s.cpp'"%(build_dir,name,name)) - cmd = 'cd %s && python setup.py build 1>log 2>err'%build_dir if verbose: print(cmd) From d2a8cf6349e188f75891ceb9c5d4abc9f4f0aed6 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 1 Oct 2015 10:43:12 +0200 Subject: [PATCH 320/421] trac #19325: Doctest --- src/sage/misc/cython.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 8bacfe1ebc7..8fd9e67f6ff 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -283,7 +283,7 @@ def pyx_preparse(s): def cython(filename, verbose=False, compile_message=False, use_cache=False, create_local_c_file=False, annotate=True, sage_namespace=True, create_local_so_file=False): - """ + r""" Compile a Cython file. This converts a Cython file to a C (or C++ file), and then compiles that. The .c file and the .so file are created in a temporary directory. @@ -341,6 +341,11 @@ def cython(filename, verbose=False, compile_message=False, sage: x x^2 + Check that compiling c++ code works:: + + sage: cython("#clang C++\n"+ + ....: "from libcpp.vector cimport vector\n" + ....: "cdef vector[int] * v = new vector[int](4)\n") """ if not filename.endswith('pyx'): print("Warning: file (={}) should have extension .pyx".format(filename), file=sys.stderr) From a15ae13c5a9ff74d345525c8b028f4ebdfac0d56 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Fri, 2 Oct 2015 21:11:18 +0200 Subject: [PATCH 321/421] Update to Cython 0.23.3 --- build/pkgs/cython/checksums.ini | 6 +++--- build/pkgs/cython/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cython/checksums.ini b/build/pkgs/cython/checksums.ini index 95899805812..f3ac4410434 100644 --- a/build/pkgs/cython/checksums.ini +++ b/build/pkgs/cython/checksums.ini @@ -1,4 +1,4 @@ tarball=Cython-VERSION.tar.gz -sha1=2ff0f863d3b996d2265d0bf06e567e5dd23d004d -md5=db3c5b365e1c3f71c7cd90e96473a3ab -cksum=1672168057 +sha1=d5592dc3d529c55a5ef95346caccf11c556993bd +md5=813df20f7ce5f00e60568e0371fbd07c +cksum=365027876 diff --git a/build/pkgs/cython/package-version.txt b/build/pkgs/cython/package-version.txt index e13359b8fa3..9e40e75c5d2 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -0.23.1.p0 +0.23.3 From f1f93b9620dbb15e9615ebebe33c3601c2c88c29 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 3 Oct 2015 12:56:27 +0200 Subject: [PATCH 322/421] Disambiguate integer type rank --- src/sage/graphs/asteroidal_triples.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/asteroidal_triples.pyx b/src/sage/graphs/asteroidal_triples.pyx index 56bfe7d2ce6..fbe72d233ca 100644 --- a/src/sage/graphs/asteroidal_triples.pyx +++ b/src/sage/graphs/asteroidal_triples.pyx @@ -240,7 +240,7 @@ cdef list is_asteroidal_triple_free_C(int n, # We now search for an unseen vertex v = bitset_first_in_complement(seen) - while v!=-1: + while v != -1: # and add it to the queue waiting_list[0] = v waiting_beginning = 0 From 698579c245ca131aa14b837c18bc01bbf9d6290c Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 3 Oct 2015 13:16:25 +0200 Subject: [PATCH 323/421] Updated Sage version to 6.9.rc2 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- src/bin/sage-banner | 2 +- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 9bd68a18d71..0b9154de703 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 6.9.rc1, released 2015-10-01 +Sage version 6.9.rc2, released 2015-10-03 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 478eca36b16..d5d29ab9f1d 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=8a65cdb752db6a5dec7455b4ad4a19828d3c62d3 -md5=278cd95661b3854ae016f02591ec3481 -cksum=4192095503 +sha1=ada10da8395c80225ed3f14fb431903738e018e6 +md5=34d89f1260160198a6255a73553c5c1d +cksum=3738976361 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 4699eb3cc96..5bc6609e3d8 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -116 +117 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index e18dae9f900..ec3aa085983 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath Version 6.9.rc1, Release Date: 2015-10-01 │ +│ SageMath Version 6.9.rc2, Release Date: 2015-10-03 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 14674cf646a..1c0f8fe3ffb 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='6.9.rc1' -SAGE_RELEASE_DATE='2015-10-01' +SAGE_VERSION='6.9.rc2' +SAGE_RELEASE_DATE='2015-10-03' diff --git a/src/sage/version.py b/src/sage/version.py index ab18d3e7efa..2089950ea72 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '6.9.rc1' -date = '2015-10-01' +version = '6.9.rc2' +date = '2015-10-03' From de554dcf47578218e3db9c86f0b79d32dc4604c5 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 3 Oct 2015 15:34:38 +0200 Subject: [PATCH 324/421] Add ticket number in deprecated_function_alias() doc --- src/doc/en/developer/coding_in_python.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/developer/coding_in_python.rst b/src/doc/en/developer/coding_in_python.rst index 30c65ff6557..d8b74192727 100644 --- a/src/doc/en/developer/coding_in_python.rst +++ b/src/doc/en/developer/coding_in_python.rst @@ -503,7 +503,7 @@ documentation for more information on its behaviour and optional arguments. def my_new_function(): ... - my_old_function = deprecated_function_alias(my_new_function) + my_old_function = deprecated_function_alias(666, my_new_function) * **Moving an object to a different module:** if you rename a source file or move some function (or class) to a From 057933782566ba05423d302fe8fae82276238edc Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 3 Oct 2015 17:56:38 -0500 Subject: [PATCH 325/421] Some reviewer tweaks and doc additions. --- src/sage/categories/modules.py | 11 +++++++-- src/sage/categories/super_algebras.py | 12 ++++++++++ .../super_hopf_algebras_with_basis.py | 2 +- src/sage/categories/super_modules.py | 23 +++++++++++++++++-- .../categories/super_modules_with_basis.py | 20 ++++++++++++---- 5 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 56bbe1b0b61..d389b571c2d 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -405,7 +405,10 @@ def Super(self, base_ring=None): .. TODO:: - Same as :meth:`Graded`. + - Explain why this does not commute with :meth:`WithBasis` + - Improve the support for covariant functorial + constructions categories over a base ring so as to + get rid of the ``base_ring`` argument. TESTS:: @@ -476,7 +479,11 @@ def tensor_square(self): sage: A = HopfAlgebrasWithBasis(QQ).example() sage: A.tensor_square() - An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field # An example of Hopf algebra with basis: the group algebra of the Dihedral group of order 6 as a permutation group over Rational Field + An example of Hopf algebra with basis: + the group algebra of the Dihedral group of order 6 + as a permutation group over Rational Field # An example + of Hopf algebra with basis: the group algebra of the Dihedral + group of order 6 as a permutation group over Rational Field """ return tensor([self, self]) diff --git a/src/sage/categories/super_algebras.py b/src/sage/categories/super_algebras.py index 4fd9583c206..84c1bc1503b 100644 --- a/src/sage/categories/super_algebras.py +++ b/src/sage/categories/super_algebras.py @@ -17,6 +17,18 @@ class SuperAlgebras(SuperModulesCategory): """ The category of super algebras. + An `R`-*super algebra* is an `R`-super module `A` endowed with an + `R`-algebra structure satisfying + + .. MATH:: + + A_0 A_0 \subseteq A_0, \qquad + A_0 A_1 \subseteq A_1, \qquad + A_1 A_0 \subseteq A_1, \qquad + A_1 A_1 \subseteq A_0 + + and `1 \in A_0`. + EXAMPLES:: sage: Algebras(ZZ).Super() diff --git a/src/sage/categories/super_hopf_algebras_with_basis.py b/src/sage/categories/super_hopf_algebras_with_basis.py index 58e133915b6..28347832bc4 100644 --- a/src/sage/categories/super_hopf_algebras_with_basis.py +++ b/src/sage/categories/super_hopf_algebras_with_basis.py @@ -12,7 +12,7 @@ class SuperHopfAlgebrasWithBasis(SuperModulesCategory): """ - The category of super algebras with a distinguished basis + The category of super Hopf algebras with a distinguished basis. EXAMPLES:: diff --git a/src/sage/categories/super_modules.py b/src/sage/categories/super_modules.py index b93883e4139..16ef317ce16 100644 --- a/src/sage/categories/super_modules.py +++ b/src/sage/categories/super_modules.py @@ -16,6 +16,8 @@ from sage.categories.covariant_functorial_construction import CovariantConstructionCategory from sage.categories.modules import Modules +# Note, a commutative algebra is not a commutative super algebra, +# therefore the following whitelist. axiom_whitelist = frozenset(["Facade", "Finite", "Infinite", "FiniteDimensional", "Connected", "WithBasis", # "Commutative", @@ -88,6 +90,15 @@ class SuperModules(SuperModulesCategory): """ The category of super modules. + An `R`-*super module* (where `R` is a ring) is an `R`-module `M` equipped + with a decomposition `M = M_0 \oplus M_1` into two `R`-submodules + `M_0` and `M_1` (called the *even part* and the *odd part* of `M`, + respectively). + + Thus, an `R`-super module automatically becomes a `\ZZ / 2 \ZZ`-graded + `R`-module, with `M_0` being the degree-`0` component and `M_1` being the + degree-`1` component. + EXAMPLES:: sage: Modules(ZZ).Super() @@ -134,7 +145,7 @@ def extra_super_categories(self): [] This makes sure that ``Modules(QQ).Super()`` returns an - instance of :class:`GradedModules` and not a join category of + instance of :class:`SuperModules` and not a join category of an instance of this class and of ``VectorSpaces(QQ)``:: sage: type(Modules(QQ).Super()) @@ -144,7 +155,7 @@ def extra_super_categories(self): Get rid of this workaround once there is a more systematic approach for the alias ``Modules(QQ)`` -> ``VectorSpaces(QQ)``. - Probably the later should be a category with axiom, and + Probably the latter should be a category with axiom, and covariant constructions should play well with axioms. """ from sage.categories.modules import Modules @@ -164,6 +175,14 @@ def is_even_odd(self): Return ``0`` if ``self`` is an even element or ``1`` if an odd element. + .. NOTE:: + + The default implementation assumes that the even/odd is + determined by the parity of :meth:`degree`. + + Overwrite this method if the even/odd behavior is desired + to be independent. + EXAMPLES:: sage: cat = Algebras(QQ).WithBasis().Super() diff --git a/src/sage/categories/super_modules_with_basis.py b/src/sage/categories/super_modules_with_basis.py index e6930e620e9..c56b097e2d2 100644 --- a/src/sage/categories/super_modules_with_basis.py +++ b/src/sage/categories/super_modules_with_basis.py @@ -14,6 +14,10 @@ class SuperModulesWithBasis(SuperModulesCategory): """ The category of super modules with a distinguished basis. + An `R`-*super module with a distinguished basis* is an + `R`-super module equipped with an `R`-module basis whose elements are + homogeneous. + EXAMPLES:: sage: C = GradedModulesWithBasis(ZZ); C @@ -31,13 +35,21 @@ class SuperModulesWithBasis(SuperModulesCategory): class ParentMethods: def _even_odd_on_basis(self, m): """ - Return if ``m`` is an index of an even or odd basis element. + Return the parity of the basis element indexed by ``m``. OUTPUT: ``0`` if ``m`` is for an even element or ``1`` if ``m`` is for an odd element. + .. NOTE:: + + The default implementation assumes that the even/odd is + determined by the parity of :meth:`degree`. + + Overwrite this method if the even/odd behavior is desired + to be independent. + EXAMPLES:: sage: Q = QuadraticForm(QQ, 2, [1,2,3]) @@ -53,7 +65,7 @@ class ElementMethods: def is_super_homogeneous(self): r""" Return whether this element is homogeneous, in the sense - of a super module. + of a super module (i.e., is even or odd). EXAMPLES:: @@ -70,8 +82,8 @@ def is_super_homogeneous(self): False The exterior algebra has a `\ZZ` grading, which induces the - `\ZZ / 2\ZZ` grading, however the definition of homogeneous - elements differ because of the different gradings:: + `\ZZ / 2\ZZ` grading. However the definition of homogeneous + elements differs because of the different gradings:: sage: E. = ExteriorAlgebra(QQ) sage: a = x*y + 4 From aec22cc54dee46aceaa315a96905059927ad1ea4 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 3 Oct 2015 18:14:35 -0500 Subject: [PATCH 326/421] Added one more test. --- src/sage/categories/super_modules_with_basis.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/categories/super_modules_with_basis.py b/src/sage/categories/super_modules_with_basis.py index c56b097e2d2..4fa76c34e2d 100644 --- a/src/sage/categories/super_modules_with_basis.py +++ b/src/sage/categories/super_modules_with_basis.py @@ -122,6 +122,10 @@ def is_even_odd(self): Traceback (most recent call last): ... ValueError: element is not homogeneous + + sage: E. = ExteriorAlgebra(QQ) + sage: (x*y).is_even_odd() + 0 """ if not self.support(): raise ValueError("the zero element does not have a well-defined degree") From d77ee902e808082dcc44daf71bb0ea2756c621bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 3 Oct 2015 21:35:47 -0500 Subject: [PATCH 327/421] Improved docstring of PolynomialElement.pseudo_quo_rem() --- src/sage/rings/polynomial/polynomial_element.pyx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index aeb9ba9cd34..ebe25afc471 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -4007,13 +4007,14 @@ cdef class Polynomial(CommutativeAlgebraElement): INPUT: - - ``other`` -- A nonzero polynomial, otherwise an exception ValueError is raised + - ``other`` -- a nonzero polynomial OUTPUT: - If ``other`` is nonzero, this algorithm finds Q and R such that - l^(m-n+1) self = Q * other + R where m = deg(self), n = deg(other), - l is the leading coefficient of other, and such that deg(R) < deg(other). + `Q` and `R` such that `l^{m-n+1} \mathrm{self} = Q \cdot\mathrm{other} + R` + where `m` is the degree of this polynomial, `n` is the degree of + ``other``, `l` is the leading coefficient of ``other``. The result is + such that `\deg(R) < \deg(\mathrm{other})`. ALGORITHM: From 3f67b6b0d0e10c2f4816acc791f6dc2a016eeb8d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 4 Oct 2015 09:17:21 -0500 Subject: [PATCH 328/421] Fixing trivial doctest due to changes in category heirarchy. --- src/sage/misc/c3_controlled.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index cb1c4fe2e18..dbf8c5f19ac 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -326,9 +326,9 @@ For a typical category, few bases, if any, need to be added to force sage: x.mro == x.mro_standard False sage: x.all_bases_len() - 82 + 90 sage: x.all_bases_controlled_len() - 89 + 97 The following can be used to search through the Sage named categories for any that requires the addition of some bases:: From 9e5437364d8cb518b5d4d49dcddf947680c6e9ca Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 4 Oct 2015 12:33:19 -0500 Subject: [PATCH 329/421] Fixing code from change in behavior. --- .../rigged_configurations/rigged_configuration_element.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index 43b9e20cba8..bbfe85b94a7 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -1862,7 +1862,7 @@ def left_column_box(self): for nu in parts[:r-1]: nu._list.append(1) for a, nu in enumerate(parts[:r-1]): - vac_num = RC._calc_vacancy_number(parts, a, -1) + vac_num = RC._calc_vacancy_number(parts, a, 1) i = nu._list.index(1) nu.vacancy_numbers.insert(i, vac_num) nu.rigging.insert(i, vac_num) From fa476ddac910418cb12790af8896154ddca8fd00 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 4 Oct 2015 12:58:51 -0500 Subject: [PATCH 330/421] Fixing double-colon in INPUT block. --- src/sage/categories/modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index f38253fb33e..9ac2f77f110 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -352,7 +352,7 @@ def Filtered(self, base_ring=None): r""" Return the subcategory of the filtered objects of ``self``. - INPUT:: + INPUT: - ``base_ring`` -- this is ignored From 6cc8b8460dfe5a05380e73e667dc7294e4369774 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 4 Oct 2015 16:20:51 -0500 Subject: [PATCH 331/421] Reviewer changes with Darij. --- src/sage/algebras/associated_graded.py | 18 +++++++------- src/sage/algebras/clifford_algebra.py | 24 ++++++++++--------- src/sage/algebras/weyl_algebra.py | 1 + .../filtered_algebras_with_basis.py | 6 ++--- .../categories/filtered_modules_with_basis.py | 2 +- src/sage/categories/modules_with_basis.py | 4 ++-- src/sage/categories/super_algebras.py | 17 +++++++++++++ .../categories/super_algebras_with_basis.py | 22 +++++++++++++++++ src/sage/categories/super_modules.py | 2 +- 9 files changed, 70 insertions(+), 26 deletions(-) diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index a86df92acbf..750cbe33df1 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -178,14 +178,16 @@ def __init__(self, A, category=None): base_ring = A.base_ring() base_one = base_ring.one() - if category is None: - category = A.category().Graded() - opts = copy(A.print_options()) - if not opts['prefix'] and not opts['bracket']: - opts['bracket'] = '(' - opts['prefix'] = opts['prefix'] + 'bar' - - CombinatorialFreeModule.__init__(self, base_ring, A.indices(), + category = A.category().Graded().or_subcategory(category) + try: + opts = copy(A.print_options()) + if not opts['prefix'] and not opts['bracket']: + opts['bracket'] = '(' + opts['prefix'] = opts['prefix'] + 'bar' + except AttributeError: + opts = {'prefix': 'Abar'} + + CombinatorialFreeModule.__init__(self, base_ring, A.basis().keys(), category=category, **opts) # Setup the conversion back diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 7dc85e88289..bb88f5e3bb4 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -426,12 +426,9 @@ class CliffordAlgebra(CombinatorialFreeModule): (where `\ZZ_2 = \ZZ / 2 \ZZ`); this grading is determined by placing all elements of `V` in degree `1`. It is also an `\NN`-filtered algebra, with the filtration too being defined - by placing all elements of `V` in degree `1`. Due to current - limitations of the category framework, Sage can consider - either the grading or the filtration but not both at the same - time (though one can introduce two equal Clifford algebras, - one filtered and the other graded); the ``graded`` parameter - determines which of them is to be used. + by placing all elements of `V` in degree `1`. The :meth:`degree` gives + the `\NN`-*filtration* degree, and to get the super degree use instead + :meth:`~sage.categories.super_modules.SuperModules.ElementMethods.is_even_odd`. The Clifford algebra also can be considered as a covariant functor from the category of vector spaces equipped with quadratic forms @@ -466,8 +463,6 @@ class CliffordAlgebra(CombinatorialFreeModule): - ``Q`` -- a quadratic form - ``names`` -- (default: ``'e'``) the generator names - - ``graded`` -- (default: ``True``) if ``True``, then use the `\ZZ / 2\ZZ` - grading, otherwise use the `\ZZ` filtration EXAMPLES: @@ -528,6 +523,9 @@ def __init__(self, Q, names, category=None): sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6]) sage: Cl = CliffordAlgebra(Q) + sage: Cl.category() + Category of finite dimensional super algebras with basis + over (euclidean domains and infinite enumerated sets) sage: TestSuite(Cl).run() TESTS: @@ -1125,7 +1123,8 @@ def lift_isometry(self, m, names=None): f = lambda x: Cl.prod(Cl._from_dict( {(j,): m[j,i] for j in range(n)}, remove_zeros=True ) for i in x) - return self.module_morphism(on_basis=f, codomain=Cl) + return self.module_morphism(on_basis=f, codomain=Cl, + category=AlgebrasWithBasis(self.base_ring()).Super()) # This is a general method for finite dimensional algebras with bases # and should be moved to the corresponding category once there is @@ -1348,7 +1347,7 @@ class ExteriorAlgebra(CliffordAlgebra): `Q(v) = 0` for all vectors `v \in V`. See :class:`CliffordAlgebra` for the notion of a Clifford algebra. - The exterior algebra of an `R`-module `V` is a super connected + The exterior algebra of an `R`-module `V` is a connected `\ZZ`-graded Hopf superalgebra. It is commutative in the super sense (i.e., the odd elements anticommute and square to `0`). @@ -1422,6 +1421,9 @@ def __init__(self, R, names): EXAMPLES:: sage: E. = ExteriorAlgebra(QQ) + sage: E.category() + Category of finite dimensional super hopf algebras with basis + over Rational Field sage: TestSuite(E).run() """ cat = HopfAlgebrasWithBasis(R).Super() @@ -1569,7 +1571,7 @@ def lift_morphism(self, phi, names=None): f = lambda x: E.prod(E._from_dict( {(j,): phi[j,i] for j in range(n)}, remove_zeros=True ) for i in x) - return self.module_morphism(on_basis=f, codomain=E) + return self.module_morphism(on_basis=f, codomain=E, category=AlgebrasWithBasis(R).Super()) def volume_form(self): """ diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index 1ab18ac6715..d94fbd37f9d 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -589,6 +589,7 @@ def __init__(self, R, names=None): raise ValueError("variable names cannot differ by a leading 'd'") # TODO: Make this into a filtered algebra under the natural grading of # x_i and dx_i have degree 1 + # Filtered is not included because it is a supercategory of super if R.is_field(): cat = AlgebrasWithBasis(R).NoZeroDivisors().Super() else: diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 1179b068b7b..053d119a040 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -461,14 +461,14 @@ def induced_graded_map(self, other, f): sage: Q = QuadraticForm(ZZ, 2, [1,2,3]) sage: B = CliffordAlgebra(Q, names=['u','v']); B The Clifford algebra of the Quadratic form in 2 - variables over Integer Ring with coefficients: + variables over Integer Ring with coefficients: [ 1 2 ] [ * 3 ] sage: m = Matrix(ZZ, [[1, 2], [1, -1]]) sage: f = B.lift_module_morphism(m, names=['x','y']) sage: A = f.domain(); A The Clifford algebra of the Quadratic form in 2 - variables over Integer Ring with coefficients: + variables over Integer Ring with coefficients: [ 6 0 ] [ * 3 ] sage: x, y = A.gens() @@ -521,7 +521,7 @@ def on_basis(m): lifted_img_of_m = f(from_gr(grA.monomial(m))) return other.projection(i)(lifted_img_of_m) return grA.module_morphism(on_basis=on_basis, - codomain=grB, category=cat) + codomain=grB, category=cat) # If we could assume that the projection of the basis # element of ``self`` indexed by an index ``m`` is the # basis element of ``grA`` indexed by ``m``, then this diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index bf7642ae79e..7f2320f7f19 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -562,7 +562,7 @@ def on_basis(m): lifted_img_of_m = f(from_gr(grA.monomial(m))) return other.projection(i)(lifted_img_of_m) return grA.module_morphism(on_basis=on_basis, - codomain=grB, category=cat) + codomain=grB, category=cat) # If we could assume that the projection of the basis # element of ``self`` indexed by an index ``m`` is the # basis element of ``grA`` indexed by ``m``, then this diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 291c55f7f7a..282b6fc7fa9 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -126,8 +126,8 @@ class ModulesWithBasis(CategoryWithAxiom_over_base_ring): .. NOTE:: This category currently requires an implementation of an - element method ``support``. Once :trac:`18066`, an implementation - of an ``items`` method will be required. + element method ``support``. Once :trac:`18066` is merged, an + implementation of an ``items`` method will be required. TESTS:: diff --git a/src/sage/categories/super_algebras.py b/src/sage/categories/super_algebras.py index 84c1bc1503b..55c7562b255 100644 --- a/src/sage/categories/super_algebras.py +++ b/src/sage/categories/super_algebras.py @@ -48,3 +48,20 @@ def extra_super_categories(self): """ return [self.base_category().Graded()] + class ParentMethods: + def graded_algebra(self): + r""" + Return the associated graded algebra to ``self``. + + .. WARNING:: + + Because a super module `M` is naturally `\ZZ / 2 \ZZ`-graded, and + graded modules have a natural filtration induced by the grading, if + `M` has a different filtration, then the associated graded module + `\operatorname{gr} M \neq M`. This is most apparent with super + algebras, such as the :class:`differential Weyl algebra + `, and the + multiplication may not coincide. + """ + raise NotImplementedError + diff --git a/src/sage/categories/super_algebras_with_basis.py b/src/sage/categories/super_algebras_with_basis.py index 51b2b72afe8..9a4a1bc05a0 100644 --- a/src/sage/categories/super_algebras_with_basis.py +++ b/src/sage/categories/super_algebras_with_basis.py @@ -37,3 +37,25 @@ def extra_super_categories(self): """ return [self.base_category().Graded()] + class ParentMethods: + def graded_algebra(self): + r""" + Return the associated graded module to ``self``. + + See :class:`~sage.algebras.associated_graded.AssociatedGradedAlgebra` + for the definition and the properties of this. + + .. SEEALSO:: + + :meth:`~sage.categories.filtered_modules_with_basis.ParentMethods.graded_algebra` + + EXAMPLES:: + + sage: W. = algebras.DifferentialWeyl(QQ) + sage: W.graded_algebra() + Graded Algebra of Differential Weyl algebra of + polynomials in x, y over Rational Field + """ + from sage.algebras.associated_graded import AssociatedGradedAlgebra + return AssociatedGradedAlgebra(self) + diff --git a/src/sage/categories/super_modules.py b/src/sage/categories/super_modules.py index 16ef317ce16..a0a06dddf3d 100644 --- a/src/sage/categories/super_modules.py +++ b/src/sage/categories/super_modules.py @@ -87,7 +87,7 @@ def _repr_object_names(self): return "super {}".format(self.base_category()._repr_object_names()) class SuperModules(SuperModulesCategory): - """ + r""" The category of super modules. An `R`-*super module* (where `R` is a ring) is an `R`-module `M` equipped From ebbc28d6aa6e890312fa23f87863ec4179bf6f4b Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 4 Oct 2015 19:31:54 -0300 Subject: [PATCH 332/421] Trac 19322: reviewer comments - use min/max - avoid recomputing len(other) - move an import at the module level - better error message (+ test) --- src/sage/combinat/words/word_char.pyx | 37 +++++++++++++++++++++------ 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/words/word_char.pyx b/src/sage/combinat/words/word_char.pyx index 3940c5a41eb..0c55949c019 100644 --- a/src/sage/combinat/words/word_char.pyx +++ b/src/sage/combinat/words/word_char.pyx @@ -25,6 +25,8 @@ from cpython.number cimport PyIndex_Check, PyNumber_Check from cpython.sequence cimport PySequence_Check from cpython.slice cimport PySlice_Check, PySlice_GetIndicesEx +import itertools + # the maximum value of a size_t cdef size_t SIZE_T_MAX = -( 1) @@ -694,6 +696,15 @@ cdef class WordDatatype_char(WordDatatype): 609 [7, 0, 20, 0, 54, 0, 143, 0, 376, 0] 985 [0, 13, 0, 34, 0, 89, 0, 233, 0, 610] 986 [0, 12, 0, 33, 0, 88, 0, 232, 0, 609] + + TESTS:: + + sage: W = Words([0,1,2]) + sage: w = W([0,2,1,0,0,1]) + sage: w.longest_common_prefix(0) + Traceback (most recent call last): + ... + TypeError: unsupported input 0 """ cdef WordDatatype_char w cdef size_t i @@ -704,7 +715,7 @@ cdef class WordDatatype_char(WordDatatype): # (this can be much faster if we allow to compare larger memory # zones) w = other - m = self._length if self._length <= w._length else w._length + m = min(self._length, w._length) for i in range(m): if self._data[i] != w._data[i]: break @@ -719,8 +730,7 @@ cdef class WordDatatype_char(WordDatatype): elif PySequence_Check(other): # Python level # we avoid to call len(other) since it might be an infinite word - from itertools import islice - for i,a in enumerate(islice(other, self._length)): + for i,a in enumerate(itertools.islice(other, self._length)): if self._data[i] != a: break else: @@ -728,7 +738,7 @@ cdef class WordDatatype_char(WordDatatype): return self._new_c(self._data, i, self) - raise TypeError("not able to initialize a word from {}".format(other)) + raise TypeError("unsupported input {}".format(other)) def longest_common_suffix(self, other): r""" @@ -745,17 +755,27 @@ cdef class WordDatatype_char(WordDatatype): word: 001 sage: v.longest_common_suffix(u) word: 001 + + TESTS:: + + sage: W = Words([0,1,2]) + sage: w = W([0,2,1,0,0,1]) + sage: w.longest_common_suffix(0) + Traceback (most recent call last): + ... + TypeError: unsupported input 0 """ cdef WordDatatype_char w cdef size_t i cdef size_t m + cdef size_t lo if isinstance(other, WordDatatype_char): # C level # (this can be much faster if we could compare larger memory # zones) w = other - m = self._length if self._length <= w._length else w._length + m = min(self._length, w._length) for i in range(m): if self._data[self._length-i-1] != w._data[w._length-i-1]: break @@ -769,9 +789,10 @@ cdef class WordDatatype_char(WordDatatype): elif PySequence_Check(other): # Python level - m = self._length if self._length <= len(other) else len(other) + lo = len(other) + m = min(self._length, lo) for i in range(m): - if self._data[self._length-i-1] != other[len(other)-i-1]: + if self._data[self._length-i-1] != other[lo-i-1]: break else: if self._length == m: @@ -781,5 +802,5 @@ cdef class WordDatatype_char(WordDatatype): return self._new_c(self._data+self._length-i, i, self) - raise TypeError("not able to initialize a word from {}".format(other)) + raise TypeError("unsupported input {}".format(other)) From 5852e1ccc9a362dbb577798961deb3958458cfc5 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 4 Oct 2015 21:49:43 -0400 Subject: [PATCH 333/421] Trac #19332: Use reviewer's implementation of discrete_complementarity_set(). The initial implementation of discrete_complementarity_set() converted all rays to vectors before computing inner products and returning orthogonal pairs. Now the inner product is computed directly on the rays of the cone. As a result, toric lattice elements (not vectors) are returned. This simplifies the implementation, and the test output has been adjusted to reflect the ambient lattice of each ray. --- src/sage/geometry/cone.py | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index e425afad19f..ec0c157451c 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -4362,7 +4362,7 @@ def discrete_complementarity_set(self): A list of pairs `(x,s)` such that, - * `x` and `s` are orthogonal vectors. + * `x` and `s` are orthogonal. * `x` is one of this cone's :meth:`~IntegralRayCollection.rays`. * `s` is one of the :meth:`~IntegralRayCollection.rays` of this cone's :meth:`dual`. @@ -4375,11 +4375,11 @@ def discrete_complementarity_set(self): EXAMPLES: The discrete complementarity set of the nonnegative orthant - consists of pairs of standard basis vectors:: + consists of pairs of standard basis elements:: sage: K = Cone([(1,0),(0,1)]) sage: K.discrete_complementarity_set() - [((1, 0), (0, 1)), ((0, 1), (1, 0))] + [(N(1, 0), M(0, 1)), (N(0, 1), M(1, 0))] If the cone consists of a single ray, the second components of the discrete complementarity set should generate the orthogonal @@ -4387,13 +4387,13 @@ def discrete_complementarity_set(self): sage: K = Cone([(1,0)]) sage: K.discrete_complementarity_set() - [((1, 0), (0, 1)), ((1, 0), (0, -1))] + [(N(1, 0), M(0, 1)), (N(1, 0), M(0, -1))] sage: K = Cone([(1,0,0)]) sage: K.discrete_complementarity_set() - [((1, 0, 0), (0, 1, 0)), - ((1, 0, 0), (0, -1, 0)), - ((1, 0, 0), (0, 0, 1)), - ((1, 0, 0), (0, 0, -1))] + [(N(1, 0, 0), M(0, 1, 0)), + (N(1, 0, 0), M(0, -1, 0)), + (N(1, 0, 0), M(0, 0, 1)), + (N(1, 0, 0), M(0, 0, -1))] When the cone is the entire space, its dual is the trivial cone, so the discrete complementarity set is empty:: @@ -4430,18 +4430,10 @@ def discrete_complementarity_set(self): sage: dcs = K.discrete_complementarity_set() sage: sum([ x.inner_product(s).abs() for (x,s) in dcs ]) 0 - """ - V = self.lattice().vector_space() - - # Convert rays to vectors so that we can compute inner products. - G1 = [ V(x) for x in self.rays() ] - - # We also convert the generators of the dual cone so that we - # return pairs of vectors and not (vector, ray) pairs. - G2 = [ V(s) for s in self.dual().rays() ] - - return [ (x,s) for x in G1 for s in G2 if x.inner_product(s) == 0 ] + return [ (x,s) for x in self + for s in self.dual() + if x.inner_product(s) == 0 ] def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, From 872932952e7db998d013637636ca82124ace544e Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 4 Oct 2015 22:14:27 -0400 Subject: [PATCH 334/421] Trac #19332: Documentation updates for discrete_complementarity_set(). It's somewhat important to note that the elements of a discrete complementarity set are nonzero, so a statement to that effect has been added to the OUTPUT block. A few places referred to "the" discrete complementarity set; these have been fixed to say "a discrete complementarity set," as they are in general not unique. --- src/sage/geometry/cone.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index ec0c157451c..f9768c96406 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -4362,6 +4362,7 @@ def discrete_complementarity_set(self): A list of pairs `(x,s)` such that, + * `x` and `s` are nonzero. * `x` and `s` are orthogonal. * `x` is one of this cone's :meth:`~IntegralRayCollection.rays`. * `s` is one of the :meth:`~IntegralRayCollection.rays` of this @@ -4374,16 +4375,16 @@ def discrete_complementarity_set(self): EXAMPLES: - The discrete complementarity set of the nonnegative orthant - consists of pairs of standard basis elements:: + Pairs of standard basis elements form a discrete complementarity + set for the nonnegative orthant:: sage: K = Cone([(1,0),(0,1)]) sage: K.discrete_complementarity_set() [(N(1, 0), M(0, 1)), (N(0, 1), M(1, 0))] - If the cone consists of a single ray, the second components of - the discrete complementarity set should generate the orthogonal - complement of that ray:: + If a cone consists of a single ray, then the second components + of a discrete complementarity set for that cone should generate + the orthogonal complement of the ray:: sage: K = Cone([(1,0)]) sage: K.discrete_complementarity_set() @@ -4395,14 +4396,16 @@ def discrete_complementarity_set(self): (N(1, 0, 0), M(0, 0, 1)), (N(1, 0, 0), M(0, 0, -1))] - When the cone is the entire space, its dual is the trivial cone, - so the discrete complementarity set is empty:: + When a cone is the entire space, its dual is the trivial cone, + so the only discrete complementarity set for it is empty:: sage: K = Cone([(1,0),(-1,0),(0,1),(0,-1)]) + sage: K.is_full_space() + True sage: K.discrete_complementarity_set() [] - Likewise when this cone is trivial (its dual is the entire space):: + Likewise for trivial cones, whose duals are the entire space:: sage: L = ToricLattice(0) sage: K = Cone([], ToricLattice(0)) @@ -4411,8 +4414,9 @@ def discrete_complementarity_set(self): TESTS: - The complementarity set of the dual can be obtained by switching - components in the complementarity set of the original cone:: + A discrete complementarity set for the dual can be obtained by + switching components in a discrete complementarity set of the + original cone:: sage: set_random_seed() sage: K = random_cone(max_ambient_dim=6) @@ -4422,7 +4426,7 @@ def discrete_complementarity_set(self): sage: sorted(actual) == sorted(expected) True - The pairs in the discrete complementarity set are in fact + The pairs in a discrete complementarity set are in fact complementary:: sage: set_random_seed() From 34379a94487c494f38bea80ebe17c7e6d2c35e42 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 5 Oct 2015 11:04:41 +0200 Subject: [PATCH 335/421] Fix downloading in sage-sdist --- build/make/deps | 6 ++++++ src/bin/sage-sdist | 8 +++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/build/make/deps b/build/make/deps index c9becf07a8a..3d0fb935312 100644 --- a/build/make/deps +++ b/build/make/deps @@ -44,6 +44,12 @@ all-sage: \ $(EXTCODE) \ $(SCRIPTS) +# Download all packages which should be inside an sdist tarball (the -B +# option to make forces all targets to be built unconditionally) +download-for-sdist: base + env SAGE_INSTALL_FETCH_ONLY=yes $(MAKE) -B SAGERUNTIME= \ + $(STANDARD_PACKAGES) gcc mpir python2 + # TOOLCHAIN consists of dependencies determined by build/make/install, # including for example the GCC package. toolchain: $(TOOLCHAIN) diff --git a/src/bin/sage-sdist b/src/bin/sage-sdist index 6b08cec0dc1..69424b20148 100755 --- a/src/bin/sage-sdist +++ b/src/bin/sage-sdist @@ -43,12 +43,10 @@ echo "Sage version $SAGE_VERSION, release date $SAGE_RELEASE_DATE" TARGET="sage-$SAGE_VERSION" sage-clone-source "$SAGE_ROOT" "$TMP_DIR/$TARGET" -# Download and copy all upstream tarballs (the -B option to make forces -# all targets to be built unconditionally) +# Download and copy all upstream tarballs cd "$SAGE_ROOT/build/make" -SAGE_INSTALL_GCC=yes SAGE_SPKG_COPY_UPSTREAM="$TMP_DIR/$TARGET/upstream" \ -SAGE_INSTALL_FETCH_ONLY=yes \ -./install -B all-build +export SAGE_SPKG_COPY_UPSTREAM="$TMP_DIR/$TARGET/upstream" +$MAKE download-for-sdist # Create source .tar.gz cd "$TMP_DIR" From 5a0c4b0d9610c2eee9f8d25f929ae3047cb65df8 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 5 Oct 2015 14:39:00 +0200 Subject: [PATCH 336/421] Better error handling in sage-download-file --- build/sage_bootstrap/cmdline.py | 41 +++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index c1dfe89c8d8..76c3fc008bf 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -170,8 +170,8 @@ def run(self): destination = None for arg in sys.argv[1:]: if arg.startswith('--print-fastest-mirror'): - print(MirrorList().fastest) - sys.exit(0) + url = "fastest mirror" + continue if arg.startswith('--quiet'): progress = False continue @@ -184,13 +184,30 @@ def run(self): raise ValueError('too many arguments') if url is None: print(dedent(self.__doc__.format(SAGE_DISTFILES=SAGE_DISTFILES))) - sys.exit(1) - if url.startswith('http://') or url.startswith('https://') or url.startswith('ftp://'): - Download(url, destination, progress=progress, ignore_errors=True).run() - else: - # url is a tarball name - tarball = Tarball(url) - tarball.download() - if destination is not None: - tarball.save_as(destination) - + sys.exit(2) + + try: + if url.startswith('http://') or url.startswith('https://') or url.startswith('ftp://'): + Download(url, destination, progress=progress, ignore_errors=True).run() + elif url == "fastest mirror": + print(MirrorList().fastest) + else: + # url is a tarball name + tarball = Tarball(url) + tarball.download() + if destination is not None: + tarball.save_as(destination) + except: + try: + stars = '*' * 72 + '\n' + sys.stderr.write(stars) + try: + import traceback + traceback.print_exc(file=sys.stderr) + sys.stderr.write(stars) + except: + pass + sys.stderr.write("Error downloading %s\n"%(url,)) + sys.stderr.write(stars) + finally: + sys.exit(1) From 92582788c1f3a01479f4414b466275120aa843ec Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 5 Oct 2015 16:44:07 +0200 Subject: [PATCH 337/421] Avoid magic string --- build/sage_bootstrap/cmdline.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index 76c3fc008bf..c08d958e4c4 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -167,10 +167,12 @@ class SageDownloadFileApplication(object): def run(self): progress = True url = None + print_fastest_mirror = None destination = None for arg in sys.argv[1:]: if arg.startswith('--print-fastest-mirror'): - url = "fastest mirror" + url = "" + print_fastest_mirror = True continue if arg.startswith('--quiet'): progress = False @@ -189,7 +191,8 @@ def run(self): try: if url.startswith('http://') or url.startswith('https://') or url.startswith('ftp://'): Download(url, destination, progress=progress, ignore_errors=True).run() - elif url == "fastest mirror": + elif print_fastest_mirror: + url = "fastest mirror" # For error message print(MirrorList().fastest) else: # url is a tarball name @@ -197,7 +200,7 @@ def run(self): tarball.download() if destination is not None: tarball.save_as(destination) - except: + except BaseException: try: stars = '*' * 72 + '\n' sys.stderr.write(stars) From 53164a9a0567d6b84111a0eb8eabd1630970fda5 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 5 Oct 2015 09:37:49 -0700 Subject: [PATCH 338/421] implementing SRG_210_99_48_45 --- src/sage/graphs/strongly_regular_db.pyx | 54 +++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index cfefb513375..33f8d65d65f 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -1218,6 +1218,59 @@ def SRG_176_105_68_54(): H = IncidenceStructure([x for x in W if 22 not in x]) return H.intersection_graph(3) +def SRG_210_99_48_45(): + r""" + Return a strongly regular graph with parameters `(210, 99, 48, 45)` + + This graph is from Example 4.2 in [KPRWZ10]_. One considers the action of + the symmetric group `S_7` on the 210 digraphs isomorphic to the + disjoint union of `K_1` and the circulant 6-vertex digraph + ``digraphs.Circulant(6,[1,4])``. It has 16 orbitals; the package [COCO]_ + found a megring of them, explicitly described in [KPRWZ10]_, resulting in + this graph. + + EXAMPLE:: + + sage: from sage.graphs.strongly_regular_db import SRG_210_99_48_45 + sage: g=SRG_210_99_48_45() + sage: g.is_strongly_regular(parameters=True) + (210, 99, 48, 45) + + REFERENCES: + + .. [KPRWZ10] M. H. Klin, C. Pech, S. Reichard, A. Woldar, M. Zvi-Av, + Examples of computer experimentation in algebraic combinatorics, + ARS MATHEMATICA CONTEMPORANEA 3 (2010) 237–258 + http://amc-journal.eu/index.php/amc/article/viewFile/119/118 + + .. [COCO] I. A. Faradjev and M. H. Klin, + Computer package for computations with coherent configurations, + Proc. ISSAC-91, ACM Press, Bonn, 1991, pages 219–223; + code, by I.A.Faradjev (with contributions by A.E.Brouwer, D.V.Pasechnik) + https://github.com/dimpase/coco + + """ + from sage.libs.gap.libgap import libgap + from sage.combinat.permutation import Permutation + def ekg(g0): # return arcs of the Cayley digraph of on {g,g^4} + g = Permutation(g0) + return libgap.Set(map(lambda x: (x,g(x)), range(1,8))\ + + map(lambda x: (x,g(g(g(g(x))))), range(1,8))) + + kd=map(ekg, + [(7, 1, 2, 3, 4, 5), (7, 1, 3, 4, 5, 6), + (7, 3, 4, 5, 6, 2), (7, 1, 4, 3, 5, 6), + (7, 3, 1, 4, 5, 6), (7, 2, 4, 3, 5, 6), + (7, 3, 2, 4, 5, 1), (7, 2, 4, 3, 5, 1)]) + s=libgap.SymmetricGroup(7) + O=s.Orbit(kd[0],libgap.OnSetsTuples) + sa=s.Action(O,libgap.OnSetsTuples) + G=Graph() + for g in kd[1:]: + G.add_edges(libgap.Orbit(sa,[libgap.Position(O,kd[0]),\ + libgap.Position(O,g)],libgap.OnSets)) + return G + def SRG_243_110_37_60(): r""" Return a `(243, 110, 37, 60)`-strongly regular graph. @@ -2258,6 +2311,7 @@ def strongly_regular_graph(int v,int k,int l,int mu=-1,bint existence=False,bint (176, 49, 12, 14): [SRG_176_49_12_14], (176, 105, 68, 54): [SRG_176_105_68_54], (196, 91, 42, 42): [SRG_196_91_42_42], + (210, 99, 48, 45): [SRG_210_99_48_45], (220, 84, 38, 28): [SRG_220_84_38_28], (231, 30, 9, 3): [CameronGraph], (243, 110, 37, 60): [SRG_243_110_37_60], From e345dfab6cc04da5451c4fbee1de6a09625f0b41 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 5 Oct 2015 21:30:54 -0300 Subject: [PATCH 339/421] Trac 19284: fix doc + make it work --- src/sage/interfaces/octave.py | 222 ++++++++++++++++++++++------------ 1 file changed, 148 insertions(+), 74 deletions(-) diff --git a/src/sage/interfaces/octave.py b/src/sage/interfaces/octave.py index 47da9f0ce15..bb1fe260ab4 100644 --- a/src/sage/interfaces/octave.py +++ b/src/sage/interfaces/octave.py @@ -372,9 +372,9 @@ def clear(self, var): EXAMPLES:: sage: octave.set('x', '2') # optional - octave - sage: octave.clear('x') # optional - octave - sage: octave.get('x') # optional - octave - "error: `x' undefined near line ... column 1" + sage: octave.clear('x') # optional - octave + sage: octave.get('x') # optional - octave + "error: 'x' undefined near line ... column 1" """ self.eval('clear %s'%var) @@ -533,33 +533,114 @@ def _object_class(self): return OctaveElement +octave_functions = set() + +def to_complex(octave_string, R): + r""" + Helper function to convert octave complex number + + TESTS:: + + sage: from sage.interfaces.octave import to_complex + sage: to_complex('(0,1)', CDF) + 1.0*I + sage: to_complex('(1.3231,-0.2)', CDF) + 1.3231 - 0.2*I + """ + real, imag = octave_string.strip('() ').split(',') + return R(float(real), float(imag)) + class OctaveElement(ExpectElement): - def _matrix_(self, R): + def _get_sage_ring(self): + r""" + TESTS:: + + sage: octave('1')._get_sage_ring() # optional - octave + Real Double Field + sage: octave('I')._get_sage_ring() # optional - octave + Complex Double Field + sage: octave('[]')._get_sage_ring() # optional - octave + Real Double Field + """ + if self.isinteger(): + import sage.rings.integer_ring + return sage.rings.integer_ring.ZZ + elif self.isreal(): + import sage.rings.real_double + return sage.rings.real_double.RDF + elif self.iscomplex(): + import sage.rings.complex_double + return sage.rings.complex_double.CDF + else: + raise TypeError("no Sage ring associated to this element.") + + def __nonzero__(self): + r""" + Test whether this element is zero. + + EXAMPLES:: + + sage: bool(octave('0')) # optional - octave + False + sage: bool(octave('[]')) # optional - octave + False + sage: bool(octave('[0,0]')) # optional - octave + False + sage: bool(octave('[0,0,0;0,0,0]')) # optional - octave + False + + sage: bool(octave('0.1')) # optional - octave + True + sage: bool(octave('[0,1,0]')) # optional - octave + True + sage: bool(octave('[0,0,-0.1;0,0,0]')) # optional - octave + True + """ + return str(self) != ' [](0x0)' and any(x != '0' for x in str(self).split()) + + def _matrix_(self, R=None): r""" Return Sage matrix from this octave element. EXAMPLES:: + sage: A = octave('[1,2;3,4.5]') # optional - octave + sage: matrix(A) # optional - octave + [1.0 2.0] + [3.0 4.5] + sage: _.base_ring() # optional - octave + Real Double Field + + sage: A = octave('[I,1;-1,0]') # optional - octave + sage: matrix(A) # optional - octave + [1.0*I 1.0] + [ -1.0 0.0] + sage: _.base_ring() # optional - octave + Complex Double Field + sage: A = octave('[1,2;3,4]') # optional - octave sage: matrix(ZZ, A) # optional - octave [1 2] [3 4] - sage: A = octave('[1,2;3,4.5]') # optional - octave - sage: matrix(RR, A) # optional - octave - [1.00000000000000 2.00000000000000] - [3.00000000000000 4.50000000000000] """ - from sage.matrix.all import MatrixSpace oc = self.parent() - if not oc('ismatrix(%s)' % self): + if not self.ismatrix(): raise TypeError('not an octave matrix') + if R is None: + R = self._get_sage_ring() + s = str(self).strip('\n ') w = [u.strip().split(' ') for u in s.split('\n')] nrows = len(w) ncols = len(w[0]) + + if self.iscomplex(): + w = [[to_complex(x,R) for x in row] for row in w] + + from sage.matrix.all import MatrixSpace return MatrixSpace(R, nrows, ncols)(w) - def _vector_(self, R): + def _vector_(self, R=None): r""" Return Sage vector from this octave element. @@ -567,67 +648,62 @@ def _vector_(self, R): sage: A = octave('[1,2,3,4]') # optional - octave sage: vector(ZZ, A) # optional - octave - [1 2 3 4] + (1, 2, 3, 4) sage: A = octave('[1,2.3,4.5]') # optional - octave - sage: vector(RR, A) # optional - octave - [1.00000000000000 2.30000000000000 4.50000000000000] + sage: vector(A) # optional - octave + (1.0, 2.3, 4.5) + sage: A = octave('[1,I]') # optional - octave + sage: vector(A) # optional - octave + (1.0, 1.0*I) """ - from sage.modules.free_module import FreeModule oc = self.parent() - if not oc('isvector(%s)' % self): + if not self.isvector(): raise TypeError('not an octave vector') + if R is None: + R = self._get_sage_ring() + s = str(self).strip('\n ') w = s.strip().split(' ') nrows = len(w) + + if self.iscomplex(): + w = [to_complex(x, R) for x in w] + + from sage.modules.free_module import FreeModule return FreeModule(R, nrows)(w) - def _scalar_(self, find_parent=False): + def _scalar_(self): """ Return Sage scalar from this octave element. - INPUT: - - - find_parent -- boolean (default ``False``). If ``True`` also return - the ring to which the scalar belongs. - EXAMPLES:: sage: A = octave('2833') # optional - octave - sage: As = A.sage(); As # optional - octave - 2833 - sage: As.parent() # optional - octave - Integer Ring + sage: As = A.sage(); As # optional - octave + 2833.0 + sage: As.parent() # optional - octave + Real Double Field + sage: B = sqrt(A) # optional - octave - sage: Bs = B.sage(); Bs # optional - octave + sage: Bs = B.sage(); Bs # optional - octave 53.2259 - sage: Bs.parent() # optional - octave - Real Field with 53 bits of precision + sage: Bs.parent() # optional - octave + Real Double Field + sage: C = sqrt(-A) # optional - octave - sage: Cs = C.sage(); Cs # optional - octave + sage: Cs = C.sage(); Cs # optional - octave 53.2259*I - sage: Cs.parent() # optional - octave - Complex Field with 53 bits of precision + sage: Cs.parent() # optional - octave + Complex Double Field """ - from sage.rings.complex_double import CDF - from sage.rings.integer_ring import ZZ - from sage.rings.real_double import RDF - oc = self.parent() - if oc('isinteger(%s)' % self): - if not find_parent: - return ZZ(str(self)) - else: - return ZZ(str(self)), ZZ - elif oc('isreal(%s)' % self): - if not find_parent: - return RDF(str(self)) - else: - return RDF(str(self)), RDF - elif oc('iscomplex(%s)' % self): - real, imag = str(self).strip('() ').split(',') - if not find_parent: - return CDF(RDF(real), RDF(imag)) - else: - return CDF(RDF(real), RDF(imag)), CDF + if not self.isscalar(): + raise TypeError("not an octave scalar") + + R = self._get_sage_ring() + if self.iscomplex(): + return to_complex(str(self), R) + else: + return R(str(self)) def _sage_(self): """ @@ -635,36 +711,34 @@ def _sage_(self): EXAMPLES:: - sage: A = octave('2833') # optional - octave - sage: A.sage() # optional - octave - 2833 - sage: B = sqrt(A) # optional - octave - sage: B.sage() # optional - octave + sage: A = octave('2833') # optional - octave + sage: A.sage() # optional - octave + 2833.0 + sage: B = sqrt(A) # optional - octave + sage: B.sage() # optional - octave 53.2259 - sage: C = sqrt(-A) # optional - octave - sage: C.sage() # optional - octave + sage: C = sqrt(-A) # optional - octave + sage: C.sage() # optional - octave 53.2259*I - sage: A = octave('[1,2,3,4]') # optional - octave - sage: A.sage() # optional - octave - [1 2 3 4] - sage: A = octave('[1,2.3,4.5]') # optional - octave - sage: A.sage() # optional - octave - [1.00000000000000 2.30000000000000 4.50000000000000] - sage: A = octave('[1,2.3+I,4.5]') # optional - octave - sage: A.sage() # optional - octave - [1.00000000000000 2.30000000000000+1.0*I 4.50000000000000] - """ - oc = self.parent() - if oc('isscalar(%s)' % self): + sage: A = octave('[1,2,3,4]') # optional - octave + sage: A.sage() # optional - octave + (1.0, 2.0, 3.0, 4.0) + sage: A = octave('[1,2.3,4.5]') # optional - octave + sage: A.sage() # optional - octave + (1.0, 2.3, 4.5) + sage: A = octave('[1,2.3+I,4.5]') # optional - octave + sage: A.sage() # optional - octave + (1.0, 2.3 + 1.0*I, 4.5) + """ + if self.isscalar(): return self._scalar_() - elif oc('isvector(%s)' % self): + elif self.isvector(): return self._vector_() - elif oc('ismatrix(%s)' % self): + elif self.ismatrix(): return self._matrix_() else: raise NotImplementedError('octave type is not recognized') - # An instance octave = Octave() From 16a3791a26e665843d0b9357a0bd56f4daf88881 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 5 Oct 2015 21:12:26 -0500 Subject: [PATCH 340/421] Marked a doctest as indirect doctest. --- src/sage/categories/super_algebras.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/super_algebras.py b/src/sage/categories/super_algebras.py index 84c1bc1503b..cfd3e944203 100644 --- a/src/sage/categories/super_algebras.py +++ b/src/sage/categories/super_algebras.py @@ -42,7 +42,7 @@ def extra_super_categories(self): """ EXAMPLES:: - sage: Algebras(ZZ).Super().super_categories() + sage: Algebras(ZZ).Super().super_categories() # indirect doctest [Category of graded algebras over Integer Ring, Category of super modules over Integer Ring] """ From e79114af731f069c880a6d687953cd4586f07798 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 5 Oct 2015 22:53:33 -0500 Subject: [PATCH 341/421] First round of review changes. --- .../root_system/integrable_representations.py | 138 ++++++++++-------- 1 file changed, 78 insertions(+), 60 deletions(-) diff --git a/src/sage/combinat/root_system/integrable_representations.py b/src/sage/combinat/root_system/integrable_representations.py index 48e7cc24d0a..ebd2140573a 100644 --- a/src/sage/combinat/root_system/integrable_representations.py +++ b/src/sage/combinat/root_system/integrable_representations.py @@ -596,8 +596,8 @@ def to_dominant(self, n): def _freudenthal_roots_imaginary(self, nu): r""" - Return the set of imaginary roots `\alpha \in \Delta^+` in ``self`` - such that `\nu - \alpha \in Q^+`. + Iterate over the set of imaginary roots `\alpha \in \Delta^+` + in ``self`` such that `\nu - \alpha \in Q^+`. INPUT: @@ -607,21 +607,23 @@ def _freudenthal_roots_imaginary(self, nu): sage: Lambda = RootSystem(['B',3,1]).weight_lattice(extended=true).fundamental_weights() sage: V = IntegrableRepresentation(Lambda[0]+Lambda[1]+Lambda[3]) - sage: [V._freudenthal_roots_imaginary(V.highest_weight() - mw) + sage: [list(V._freudenthal_roots_imaginary(V.highest_weight() - mw)) ....: for mw in V.dominant_maximal_weights()] [[], [], [], [], []] """ l = self._from_weight_helper(nu) kp = min(l[i] // self._a[i] for i in self._index_set) delta = self._Q.null_root() - return [u * delta for u in range(1, kp+1)] + for u in range(1, kp+1): + yield u * delta def _freudenthal_roots_real(self, nu): r""" - Return the set of real positive roots `\alpha \in \Delta^+` in - ``self`` such that `\nu - \alpha \in Q^+`. + Iterate over the set of real positive roots `\alpha \in \Delta^+` + in ``self`` such that `\nu - \alpha \in Q^+`. - See [Kac]_ Proposition 6.3 for the way to compute the set of real roots for twisted affine case. + See [Kac]_ Proposition 6.3 for the way to compute the set of real + roots for twisted affine case. INPUT: @@ -632,7 +634,7 @@ def _freudenthal_roots_real(self, nu): sage: Lambda = RootSystem(['B',3,1]).weight_lattice(extended=true).fundamental_weights() sage: V = IntegrableRepresentation(Lambda[0]+Lambda[1]+Lambda[3]) sage: mw = V.dominant_maximal_weights()[0] - sage: V._freudenthal_roots_real(V.highest_weight() - mw) + sage: list(V._freudenthal_roots_real(V.highest_weight() - mw)) [alpha[1], alpha[2], alpha[3], @@ -640,54 +642,70 @@ def _freudenthal_roots_real(self, nu): alpha[2] + alpha[3], alpha[1] + alpha[2] + alpha[3]] """ - ret = [] for al in self._classical_positive_roots: if all(x >= 0 for x in self._from_weight_helper(nu-al)): - ret.append(al) + yield al + + if self._cartan_type.is_untwisted_affine(): + # untwisted case + for al in self._classical_roots: + for ir in self._freudenthal_roots_imaginary(nu-al): + yield al+ir + return + from sage.combinat.root_system.cartan_type import CartanType - if self._cartan_type == CartanType(['B',self._classical_rank,1]).dual() or self._cartan_type == CartanType(['C',self._classical_rank,1]).dual() or self._cartan_type == CartanType(['F',4,1]).dual(): #case A^2_{2l-1} or case D^2_{l+1} or case E^2_6: + if self._cartan_type in [CartanType(['B',self._classical_rank,1]).dual(), + CartanType(['C',self._classical_rank,1]).dual(), + CartanType(['F',4,1]).dual()]: + #case A^2_{2l-1} or case D^2_{l+1} or case E^2_6: for al in self._classical_roots: if self._inner_qq(al,al) == 2: #if al is a short root: for ir in self._freudenthal_roots_imaginary(nu-al): - ret.append(al+ir) + yield al+ir else: - for ir in self._freudenthal_roots_imaginary(nu-al): - if 2*ir in self._freudenthal_roots_imaginary(nu-al): - ret.append(al+2*ir) + fri = list(self._freudenthal_roots_imaginary(nu-al)) + friset = set(fri) + for ir in fri: + if 2*ir in friset: + yield al+2*ir - elif self._cartan_type == CartanType(['BC',self._classical_rank,2]): #case A^2_{2l} + elif self._cartan_type == CartanType(['BC',self._classical_rank,2]): + #case A^2_{2l} + # We have to keep track of the roots we have visted for this case + ret = set(self._classical_positive_roots) for al in self._classical_roots: if self._inner_qq(al,al) == 2: #if al is a short root: for ir in self._freudenthal_roots_imaginary(nu-al): - ret.append(al+ir) + ret.add(al+ir) + yield al+ir else: - for ir in self._freudenthal_roots_imaginary(nu-al): - if 2*ir in self._freudenthal_roots_imaginary(nu-al): - ret.append(al+2*ir) - for ir in self._freudenthal_roots_imaginary(2*nu-al)[::2]: + fri = list(self._freudenthal_roots_imaginary(nu-al)) + friset = set(fri) + for ir in fri: + if 2*ir in friset: + ret.add(al+2*ir) + yield al+2*ir + alpha = self._Q.simple_roots() + fri = list(self._freudenthal_roots_imaginary(2*nu-al)) + for ir in fri[::2]: n = [x//2 for x in self._from_weight_helper(al+ir)] - alpha = self._Q.simple_roots() - if sum([val*alpha[i] for i,val in enumerate(n)]) not in ret: - ret.append(sum([val*alpha[i] for i,val in enumerate(n)])) + if sum(val*alpha[i] for i,val in enumerate(n)) not in ret: + rt = sum(val*alpha[i] for i,val in enumerate(n)) + ret.add(rt) + yield rt - elif self._cartan_type == CartanType(['D',4,3]) or self._cartan_type == CartanType(['G',2,1]).dual(): # case D^3_4 in the Kac notation + elif self._cartan_type in [CartanType(['D',4,3]), CartanType(['G',2,1]).dual()]: + # case D^3_4 in the Kac notation for al in self._classical_roots: if self._inner_qq(al,al) == 2: #if al is a short root: for ir in self._freudenthal_roots_imaginary(nu-al): - ret.append(al+ir) + yield al+ir else: - for ir in self._freudenthal_roots_imaginary(nu-al): - if 3*ir in self._freudenthal_roots_imaginary(nu-al): - ret.append(al+3*ir) - - - else: # untwisted case - for al in self._classical_roots: - for ir in self._freudenthal_roots_imaginary(nu-al): - ret.append(al+ir) - - - return ret + fri = list(self._freudenthal_roots_imaginary(nu-al)) + friset = set(fri) + for ir in fri: + if 3*ir in friset: + yield al+3*ir def _freudenthal_accum(self, nu, al): """ @@ -750,10 +768,14 @@ def _m_freudenthal(self, n): from sage.combinat.root_system.cartan_type import CartanType - if self._cartan_type == CartanType(['B',self._classical_rank,1]).dual(): - list = self._freudenthal_roots_imaginary(self._Lam - mu) - dict = {key:list[key-1] for key in range(1,len(list)+1)} - for k in dict: # k is a positive number, dict[k] is k*delta + if self._cartan_type.is_untwisted_affine(): + for al in self._freudenthal_roots_imaginary(self._Lam - mu): + num += self._classical_rank * self._freudenthal_accum(mu, al) + + elif self._cartan_type == CartanType(['B',self._classical_rank,1]).dual(): + d = {key+1: rt for key, rt in + enumerate(self._freudenthal_roots_imaginary(self._Lam - mu))} + for k in d: # k is a positive number, d[k] is k*delta num += (self._classical_rank-k%2) * self._freudenthal_accum(mu, dict[k]) elif self._cartan_type == CartanType(['BC',self._classical_rank,2]): @@ -761,29 +783,25 @@ def _m_freudenthal(self, n): num += self._classical_rank * self._freudenthal_accum(mu, al) elif self._cartan_type == CartanType(['C',self._classical_rank,1]).dual(): - list = self._freudenthal_roots_imaginary(self._Lam - mu) - dict = {key:list[key-1] for key in range(1,len(list)+1)} - for k in dict: # k is a positive number, dict[k] is k*delta - num += (self._classical_rank-(self._classical_rank -1)*(k%2)) * self._freudenthal_accum(mu, dict[k]) + d = {key+1: rt for key, rt in + enumerate(self._freudenthal_roots_imaginary(self._Lam - mu))} + for k in d: # k is a positive number, d[k] is k*delta + num += (self._classical_rank-(self._classical_rank -1)*(k%2)) * self._freudenthal_accum(mu, d[k]) elif self._cartan_type == CartanType(['F',4,1]).dual(): - list = self._freudenthal_roots_imaginary(self._Lam - mu) - dict = {key:list[key-1] for key in range(1,len(list)+1)} - for k in dict: # k is a positive number, dict[k] is k*delta - num += (4 - 2*(k%2)) * self._freudenthal_accum(mu, dict[k]) + d = {key+1: rt for key, rt in + enumerate(self._freudenthal_roots_imaginary(self._Lam - mu))} + for k in d: # k is a positive number, d[k] is k*delta + num += (4 - 2*(k%2)) * self._freudenthal_accum(mu, d[k]) - elif self._cartan_type == CartanType(['D',4,3]) or self._cartan_type == CartanType(['G',2,1]).dual(): - list = self._freudenthal_roots_imaginary(self._Lam - mu) - dict = {key:list[key-1] for key in range(1,len(list)+1)} - for k in dict: # k is a positive number, dict[k] is k*delta + elif self._cartan_type in [CartanType(['D',4,3]), CartanType(['G',2,1]).dual()]: + d = {key+1: rt for key, rt in + enumerate(self._freudenthal_roots_imaginary(self._Lam - mu))} + for k in d: # k is a positive number, d[k] is k*delta if k%3 == 0: - num += 2 * self._freudenthal_accum(mu, dict[k]) + num += 2 * self._freudenthal_accum(mu, d[k]) else: - num += self._freudenthal_accum(mu, dict[k]) - - else: - for al in self._freudenthal_roots_imaginary(self._Lam - mu): - num += self._classical_rank * self._freudenthal_accum(mu, al) + num += self._freudenthal_accum(mu, d[k]) try: return ZZ(num / den) From 3966f09d89f5d0c66eb6f803f1a91efd3e0bd979 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 5 Oct 2015 23:30:04 -0500 Subject: [PATCH 342/421] Getting some more speed and minor refactoring to improve readability/memory. --- .../root_system/integrable_representations.py | 163 +++++++++--------- 1 file changed, 83 insertions(+), 80 deletions(-) diff --git a/src/sage/combinat/root_system/integrable_representations.py b/src/sage/combinat/root_system/integrable_representations.py index ebd2140573a..14e3386db3d 100644 --- a/src/sage/combinat/root_system/integrable_representations.py +++ b/src/sage/combinat/root_system/integrable_representations.py @@ -193,6 +193,9 @@ def __init__(self, Lam): self._P = Lam.parent() self._Q = self._P.root_system.root_lattice() + # Store some extra simple computations that appear in tight loops + self._Lam_rho = self._Lam + self._P.rho() + self._cartan_matrix = self._P.root_system.cartan_matrix() self._cartan_type = self._P.root_system.cartan_type() @@ -212,11 +215,14 @@ def __init__(self, Lam): self._a = self._cartan_type.a() # This is not cached self._ac = self._cartan_type.dual().a() # This is not cached self._eps = {i: self._a[i] / self._ac[i] for i in self._index_set} - self._coxeter_number = sum(self._a) - self._dual_coxeter_number = sum(self._ac) E = Matrix.diagonal([self._eps[i] for i in self._index_set_classical]) self._ip = (self._cartan_type.classical().cartan_matrix()*E).inverse() + # Extra data for the twisted cases + if not self._cartan_type.is_untwisted_affine(): + self._classical_short_roots = frozenset(al for al in self._classical_roots + if self._inner_qq(al,al) == 2) + def highest_weight(self): """ Returns the highest weight of ``self``. @@ -269,6 +275,7 @@ def level(self): """ return ZZ(self._inner_pq(self._Lam, self._Q.null_root())) + @cached_method def coxeter_number(self): """ Return the Coxeter number of the Cartan type of ``self``. @@ -283,8 +290,9 @@ def coxeter_number(self): sage: V.coxeter_number() 12 """ - return self._coxeter_number + return sum(self._a) + @cached_method def dual_coxeter_number(self): r""" Return the dual Coxeter number of the Cartan type of ``self``. @@ -299,7 +307,7 @@ def dual_coxeter_number(self): sage: V.dual_coxeter_number() 9 """ - return self._dual_coxeter_number + return sum(self._ac) def _repr_(self): """ @@ -643,69 +651,65 @@ def _freudenthal_roots_real(self, nu): alpha[1] + alpha[2] + alpha[3]] """ for al in self._classical_positive_roots: - if all(x >= 0 for x in self._from_weight_helper(nu-al)): + if min(self._from_weight_helper(nu-al)) >= 0: yield al if self._cartan_type.is_untwisted_affine(): # untwisted case for al in self._classical_roots: for ir in self._freudenthal_roots_imaginary(nu-al): - yield al+ir - return - - from sage.combinat.root_system.cartan_type import CartanType - if self._cartan_type in [CartanType(['B',self._classical_rank,1]).dual(), - CartanType(['C',self._classical_rank,1]).dual(), - CartanType(['F',4,1]).dual()]: - #case A^2_{2l-1} or case D^2_{l+1} or case E^2_6: - for al in self._classical_roots: - if self._inner_qq(al,al) == 2: #if al is a short root: - for ir in self._freudenthal_roots_imaginary(nu-al): - yield al+ir - else: - fri = list(self._freudenthal_roots_imaginary(nu-al)) - friset = set(fri) - for ir in fri: - if 2*ir in friset: - yield al+2*ir + yield al + ir - elif self._cartan_type == CartanType(['BC',self._classical_rank,2]): + elif self._cartan_type.type() == 'BC': #case A^2_{2l} # We have to keep track of the roots we have visted for this case ret = set(self._classical_positive_roots) for al in self._classical_roots: - if self._inner_qq(al,al) == 2: #if al is a short root: + if al in self._classical_short_roots: for ir in self._freudenthal_roots_imaginary(nu-al): - ret.add(al+ir) - yield al+ir + ret.add(al + ir) + yield al + ir else: fri = list(self._freudenthal_roots_imaginary(nu-al)) friset = set(fri) for ir in fri: if 2*ir in friset: - ret.add(al+2*ir) - yield al+2*ir + ret.add(al + 2*ir) + yield al + 2*ir alpha = self._Q.simple_roots() fri = list(self._freudenthal_roots_imaginary(2*nu-al)) for ir in fri[::2]: - n = [x//2 for x in self._from_weight_helper(al+ir)] - if sum(val*alpha[i] for i,val in enumerate(n)) not in ret: - rt = sum(val*alpha[i] for i,val in enumerate(n)) + rt = sum( val // 2 * alpha[i] for i,val in + enumerate(self._from_weight_helper(al+ir)) ) + if rt not in ret: ret.add(rt) yield rt - elif self._cartan_type in [CartanType(['D',4,3]), CartanType(['G',2,1]).dual()]: + elif self._cartan_type.dual().type() == 'G': # case D^3_4 in the Kac notation for al in self._classical_roots: - if self._inner_qq(al,al) == 2: #if al is a short root: + if al in self._classical_short_roots: for ir in self._freudenthal_roots_imaginary(nu-al): - yield al+ir + yield al + ir else: fri = list(self._freudenthal_roots_imaginary(nu-al)) friset = set(fri) for ir in fri: if 3*ir in friset: - yield al+3*ir + yield al + 3*ir + + elif self._cartan_type.dual().type() in ['B','C','F']: + #case A^2_{2l-1} or case D^2_{l+1} or case E^2_6: + for al in self._classical_roots: + if al in self._classical_short_roots: + for ir in self._freudenthal_roots_imaginary(nu-al): + yield al + ir + else: + fri = list(self._freudenthal_roots_imaginary(nu-al)) + friset = set(fri) + for ir in fri: + if 2*ir in friset: + yield al + 2*ir def _freudenthal_accum(self, nu, al): """ @@ -726,20 +730,20 @@ def _freudenthal_accum(self, nu, al): n_shift = self._from_weight_helper(al) ip_shift = self._inner_qq(al, al) - while all(val >= 0 for val in n): + while min(n) >= 0: # Change in data by adding ``al`` to our current weight ip += ip_shift for i,val in enumerate(n_shift): n[i] -= val # Compute the multiplicity - mk = self.m(tuple(n)) - ret += 2*mk*ip + ret += 2 * self.m(tuple(n)) * ip return ret def _m_freudenthal(self, n): - """ + r""" Compute the weight multiplicity using the Freudenthal multiplicity formula in ``self``. + The multiplicities of the imaginary roots for the twisted affine case are different than those for the untwisted case. See [Carter]_ Corollary 18.10 for general type and Corollary @@ -760,49 +764,48 @@ def _m_freudenthal(self, n): I = self._index_set al = self._Q._from_dict({I[i]: val for i,val in enumerate(n) if val}, remove_zeros=False) - den = 2*self._inner_pq(self._Lam+self._P.rho(), al) - self._inner_qq(al, al) - num = 0 - - for al in self._freudenthal_roots_real(self._Lam - mu): - num += self._freudenthal_accum(mu, al) - - from sage.combinat.root_system.cartan_type import CartanType + cr = self._classical_rank + num = sum(self._freudenthal_accum(mu, alr) + for alr in self._freudenthal_roots_real(self._Lam - mu)) if self._cartan_type.is_untwisted_affine(): - for al in self._freudenthal_roots_imaginary(self._Lam - mu): - num += self._classical_rank * self._freudenthal_accum(mu, al) - - elif self._cartan_type == CartanType(['B',self._classical_rank,1]).dual(): - d = {key+1: rt for key, rt in - enumerate(self._freudenthal_roots_imaginary(self._Lam - mu))} - for k in d: # k is a positive number, d[k] is k*delta - num += (self._classical_rank-k%2) * self._freudenthal_accum(mu, dict[k]) - - elif self._cartan_type == CartanType(['BC',self._classical_rank,2]): - for al in self._freudenthal_roots_imaginary(self._Lam - mu): - num += self._classical_rank * self._freudenthal_accum(mu, al) - - elif self._cartan_type == CartanType(['C',self._classical_rank,1]).dual(): - d = {key+1: rt for key, rt in - enumerate(self._freudenthal_roots_imaginary(self._Lam - mu))} - for k in d: # k is a positive number, d[k] is k*delta - num += (self._classical_rank-(self._classical_rank -1)*(k%2)) * self._freudenthal_accum(mu, d[k]) - - elif self._cartan_type == CartanType(['F',4,1]).dual(): - d = {key+1: rt for key, rt in - enumerate(self._freudenthal_roots_imaginary(self._Lam - mu))} - for k in d: # k is a positive number, d[k] is k*delta - num += (4 - 2*(k%2)) * self._freudenthal_accum(mu, d[k]) + num += sum(cr * self._freudenthal_accum(mu, alr) + for alr in self._freudenthal_roots_imaginary(self._Lam - mu)) + + elif self._cartan_type.dual().type() == 'B': # A_{2n-1}^{(2)} + val = 1 + for rt in self._freudenthal_roots_imaginary(self._Lam - mu): + # k-th element (starting from 1) is k*delta + num += (cr - val) * self._freudenthal_accum(mu, rt) + val = 1 - val + + elif self._cartan_type.type() == 'BC': # A_{2n}^{(2)} + num += sum(cr * self._freudenthal_accum(mu, alr) + for alr in self._freudenthal_roots_imaginary(self._Lam - mu)) + + elif self._cartan_type.dual() == 'C': # D_{n+1}^{(2)} + val = 1 + for rt in self._freudenthal_roots_imaginary(self._Lam - mu): + # k-th element (starting from 1) is k*delta + num += (cr - (cr - 1)*val) * self._freudenthal_accum(mu, rt) + val = 1 - val + + elif self._cartan_type.dual().type() == 'F': # E_6^{(2)} + val = 1 + for rt in self._freudenthal_roots_imaginary(self._Lam - mu): + # k-th element (starting from 1) is k*delta + num += (4 - 2*val) * self._freudenthal_accum(mu, rt) + val = 1 - val - elif self._cartan_type in [CartanType(['D',4,3]), CartanType(['G',2,1]).dual()]: - d = {key+1: rt for key, rt in - enumerate(self._freudenthal_roots_imaginary(self._Lam - mu))} - for k in d: # k is a positive number, d[k] is k*delta - if k%3 == 0: - num += 2 * self._freudenthal_accum(mu, d[k]) + elif self._cartan_type.dual().type() == 'G': # D_4^{(3)} (or dual of G_2^{(1)}) + for k,rt in enumerate(self._freudenthal_roots_imaginary(self._Lam - mu)): + # k-th element (starting from 1) is k*delta + if (k+1) % 3 == 0: + num += 2 * self._freudenthal_accum(mu, rt) else: - num += self._freudenthal_accum(mu, d[k]) + num += self._freudenthal_accum(mu, rt) + den = 2*self._inner_pq(self._Lam_rho, al) - self._inner_qq(al, al) try: return ZZ(num / den) except TypeError: @@ -999,9 +1002,9 @@ def modular_characteristic(self, mu=None): else: n = self.from_weight(mu) k = self.level() - hd = self._dual_coxeter_number + hd = self.dual_coxeter_number() rho = self._P.rho() - m_Lambda = self._inner_pp(self._Lam+rho, self._Lam+rho) / (2*(k+hd)) \ + m_Lambda = self._inner_pp(self._Lam_rho, self._Lam_rho) / (2*(k+hd)) \ - self._inner_pp(rho, rho) / (2*hd) if n is None: return m_Lambda From f84f27425b68b38859c24ce727397372833636de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 6 Oct 2015 10:03:34 +0200 Subject: [PATCH 343/421] trac #19284 correct typo in nonzero method --- src/sage/interfaces/octave.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/octave.py b/src/sage/interfaces/octave.py index bb1fe260ab4..c599690cfc3 100644 --- a/src/sage/interfaces/octave.py +++ b/src/sage/interfaces/octave.py @@ -576,7 +576,7 @@ def _get_sage_ring(self): def __nonzero__(self): r""" - Test whether this element is zero. + Test whether this element is nonzero. EXAMPLES:: From 2049f5a4f3143479395827f1ab82c0a0b39089bc Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 6 Oct 2015 09:54:07 +0200 Subject: [PATCH 344/421] Move refine_root() to refine_root.pyx --- .../polynomial_rings_univar.rst | 1 + src/module_list.py | 4 + src/sage/rings/polynomial/complex_roots.py | 129 ++-------------- src/sage/rings/polynomial/refine_root.pyx | 141 ++++++++++++++++++ 4 files changed, 159 insertions(+), 116 deletions(-) create mode 100644 src/sage/rings/polynomial/refine_root.pyx diff --git a/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst b/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst index d84fd747804..bd55ed8b014 100644 --- a/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst +++ b/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst @@ -35,6 +35,7 @@ whereas others have multiple bases. sage/rings/polynomial/real_roots sage/rings/polynomial/complex_roots + sage/rings/polynomial/refine_root sage/rings/polynomial/ideal sage/rings/polynomial/polynomial_quotient_ring diff --git a/src/module_list.py b/src/module_list.py index 7108872b586..d0a48218689 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1570,6 +1570,10 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/polynomial/real_roots.pyx'], libraries=['mpfr']), + Extension('sage.rings.polynomial.refine_root', + sources = ['sage/rings/polynomial/refine_root.pyx'], + libraries=['gmp', 'mpfr', 'mpfi']), + Extension('sage.rings.polynomial.symmetric_reduction', sources = ['sage/rings/polynomial/symmetric_reduction.pyx']), diff --git a/src/sage/rings/polynomial/complex_roots.py b/src/sage/rings/polynomial/complex_roots.py index 5056ea53222..42432118f68 100644 --- a/src/sage/rings/polynomial/complex_roots.py +++ b/src/sage/rings/polynomial/complex_roots.py @@ -24,128 +24,25 @@ [(1.167303978261419?, 1), (-0.764884433600585? - 0.352471546031727?*I, 1), (-0.764884433600585? + 0.352471546031727?*I, 1), (0.181232444469876? - 1.083954101317711?*I, 1), (0.181232444469876? + 1.083954101317711?*I, 1)] """ +#***************************************************************************** +# Copyright (C) 2007 Carl Witty +# +# 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 copy import copy -from sage.rings.real_mpfi import RealIntervalField from sage.rings.complex_field import ComplexField from sage.rings.complex_interval_field import ComplexIntervalField from sage.rings.qqbar import AA, QQbar from sage.rings.arith import sort_complex_numbers_for_display +from sage.rings.polynomial.refine_root import refine_root -def refine_root(ip, ipd, irt, fld): - """ - We are given a polynomial and its derivative (with complex - interval coefficients), an estimated root, and a complex interval - field to use in computations. We use interval arithmetic to - refine the root and prove that we have in fact isolated a unique - root. - - If we succeed, we return the isolated root; if we fail, we return - None. - - EXAMPLES:: - - sage: from sage.rings.polynomial.complex_roots import * - sage: x = polygen(ZZ) - sage: p = x^9 - 1 - sage: ip = CIF['x'](p); ip - x^9 - 1 - sage: ipd = CIF['x'](p.derivative()); ipd - 9*x^8 - sage: irt = CIF(CC(cos(2*pi/9), sin(2*pi/9))); irt - 0.76604444311897802? + 0.64278760968653926?*I - sage: ip(irt) - 0.?e-14 + 0.?e-14*I - sage: ipd(irt) - 6.89439998807080? - 5.78508848717885?*I - sage: refine_root(ip, ipd, irt, CIF) - 0.766044443118978? + 0.642787609686540?*I - """ - - # There has got to be a better way to do this, but I don't know - # what it is... - - # We start with a basic fact: if we do an interval Newton-Raphson - # step, and the refined interval is contained in the original interval, - # then the refined interval contains exactly one root. - - # Unfortunately, our initial estimated root almost certainly does not - # contain the actual root (our initial interval is a point, which - # is exactly equal to whatever floating-point estimate we got from - # the external solver). So we need to do multiple Newton-Raphson - # steps, and check this inclusion property on each step. - - # After a few steps of refinement, if the initial root estimate was - # close to a root, we should have an essentially perfect interval - # bound on the root (since Newton-Raphson has quadratic convergence), - # unless either the real or imaginary component of the root is zero. - # If the real or imaginary component is zero, then we could spend - # a long time computing closer and closer approximations to that - # component. (This doesn't happen for non-zero components, because - # of the imprecision of floating-point numbers combined with the - # outward interval rounding; but close to zero, MPFI provides - # extremely precise numbers.) - - # If the root is actually a real root, but we start with an imaginary - # component, we can bounce back and forth between having a positive - # and negative imaginary component, without ever hitting zero. - # To deal with this, on every other Newton-Raphson step, instead of - # replacing the old interval with the new one, we take the union. - - # If the containment check continues to fail many times in a row, - # we give up and return None; we also return None if we detect - # that the slope in our current interval is not bounded away - # from zero at any step. - - # After every refinement step, we check to see if the real or - # imaginary component of our interval includes zero. If so, we - # try setting it to exactly zero. This gives us a good chance of - # detecting real roots. However, we do this replacement at most - # once per component. - - refinement_steps = 10 - - smashed_real = False - smashed_imag = False - - for i in range(refinement_steps): - slope = ipd(irt) - if slope.contains_zero(): - return None - center = fld(irt.center()) - val = ip(center) - - nirt = center - val / slope - # print irt, nirt, (nirt in irt), nirt.diameter(), irt.diameter(), center, val, slope - if nirt in irt and (nirt.diameter() >= irt.diameter() >> 3 or i >= 8): - # If the new diameter is much less than the original diameter, - # then we have not yet converged. (Perhaps we were asked - # for a particularly high-precision result.) So we don't - # return yet. - return nirt - - if i & 1: - irt = nirt - else: - irt = irt.union(nirt) - # If we don't find a root after a while, try (approximately) - # tripling the size of the region. - if i >= 6: - rD = irt.real().absolute_diameter() - iD = irt.imag().absolute_diameter() - md = max(rD, iD) - md_intv = RealIntervalField(rD.prec())(-md, md) - md_cintv = ComplexIntervalField(rD.prec())(md_intv, md_intv) - irt = irt + md_cintv - - if not smashed_real and irt.real().contains_zero(): - irt = irt.parent()(0, irt.imag()) - smashed_real = True - if not smashed_imag and irt.imag().contains_zero(): - irt = irt.parent()(irt.real(), 0) - smashed_imag = True - - return None def interval_roots(p, rts, prec): """ @@ -335,7 +232,7 @@ def complex_roots(p, skip_squarefree=False, retval='interval', min_prec=0): TESTS: - Verify that trac 12026 is fixed:: + Verify that :trac:`12026` is fixed:: sage: f = matrix(QQ, 8, lambda i, j: 1/(i + j + 1)).charpoly() sage: from sage.rings.polynomial.complex_roots import complex_roots diff --git a/src/sage/rings/polynomial/refine_root.pyx b/src/sage/rings/polynomial/refine_root.pyx new file mode 100644 index 00000000000..aafbeb442f3 --- /dev/null +++ b/src/sage/rings/polynomial/refine_root.pyx @@ -0,0 +1,141 @@ +""" +Refine polynomial roots using Newton--Raphson + +This is an implementation of the Newton--Raphson algorithm to +approximate roots of complex polynomials. The implementation +is based on interval arithmetic + +AUTHORS: + +- Carl Witty (2007-11-18): initial version +""" + +#***************************************************************************** +# Copyright (C) 2007 Carl Witty +# +# 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.real_mpfi import RealIntervalField +from sage.rings.complex_interval_field import ComplexIntervalField + + +def refine_root(ip, ipd, irt, fld): + """ + We are given a polynomial and its derivative (with complex + interval coefficients), an estimated root, and a complex interval + field to use in computations. We use interval arithmetic to + refine the root and prove that we have in fact isolated a unique + root. + + If we succeed, we return the isolated root; if we fail, we return + None. + + EXAMPLES:: + + sage: from sage.rings.polynomial.refine_root import refine_root + sage: x = polygen(ZZ) + sage: p = x^9 - 1 + sage: ip = CIF['x'](p); ip + x^9 - 1 + sage: ipd = CIF['x'](p.derivative()); ipd + 9*x^8 + sage: irt = CIF(CC(cos(2*pi/9), sin(2*pi/9))); irt + 0.76604444311897802? + 0.64278760968653926?*I + sage: ip(irt) + 0.?e-14 + 0.?e-14*I + sage: ipd(irt) + 6.89439998807080? - 5.78508848717885?*I + sage: refine_root(ip, ipd, irt, CIF) + 0.766044443118978? + 0.642787609686540?*I + """ + + # There has got to be a better way to do this, but I don't know + # what it is... + + # We start with a basic fact: if we do an interval Newton-Raphson + # step, and the refined interval is contained in the original interval, + # then the refined interval contains exactly one root. + + # Unfortunately, our initial estimated root almost certainly does not + # contain the actual root (our initial interval is a point, which + # is exactly equal to whatever floating-point estimate we got from + # the external solver). So we need to do multiple Newton-Raphson + # steps, and check this inclusion property on each step. + + # After a few steps of refinement, if the initial root estimate was + # close to a root, we should have an essentially perfect interval + # bound on the root (since Newton-Raphson has quadratic convergence), + # unless either the real or imaginary component of the root is zero. + # If the real or imaginary component is zero, then we could spend + # a long time computing closer and closer approximations to that + # component. (This doesn't happen for non-zero components, because + # of the imprecision of floating-point numbers combined with the + # outward interval rounding; but close to zero, MPFI provides + # extremely precise numbers.) + + # If the root is actually a real root, but we start with an imaginary + # component, we can bounce back and forth between having a positive + # and negative imaginary component, without ever hitting zero. + # To deal with this, on every other Newton-Raphson step, instead of + # replacing the old interval with the new one, we take the union. + + # If the containment check continues to fail many times in a row, + # we give up and return None; we also return None if we detect + # that the slope in our current interval is not bounded away + # from zero at any step. + + # After every refinement step, we check to see if the real or + # imaginary component of our interval includes zero. If so, we + # try setting it to exactly zero. This gives us a good chance of + # detecting real roots. However, we do this replacement at most + # once per component. + + refinement_steps = 10 + + smashed_real = False + smashed_imag = False + + for i in range(refinement_steps): + slope = ipd(irt) + if slope.contains_zero(): + return None + center = fld(irt.center()) + val = ip(center) + + nirt = center - val / slope + # print irt, nirt, (nirt in irt), nirt.diameter(), irt.diameter(), center, val, slope + if nirt in irt and (nirt.diameter() >= irt.diameter() >> 3 or i >= 8): + # If the new diameter is much less than the original diameter, + # then we have not yet converged. (Perhaps we were asked + # for a particularly high-precision result.) So we don't + # return yet. + return nirt + + if i & 1: + irt = nirt + else: + irt = irt.union(nirt) + # If we don't find a root after a while, try (approximately) + # tripling the size of the region. + if i >= 6: + rD = irt.real().absolute_diameter() + iD = irt.imag().absolute_diameter() + md = max(rD, iD) + md_intv = RealIntervalField(rD.prec())(-md, md) + md_cintv = ComplexIntervalField(rD.prec())(md_intv, md_intv) + irt = irt + md_cintv + + if not smashed_real and irt.real().contains_zero(): + irt = irt.parent()(0, irt.imag()) + smashed_real = True + if not smashed_imag and irt.imag().contains_zero(): + irt = irt.parent()(irt.real(), 0) + smashed_imag = True + + return None From 75b2f438073042e310adbcaea224959ba5f2105f Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Tue, 6 Oct 2015 10:20:58 -0400 Subject: [PATCH 345/421] Trac #19332: Return a cached tuple from discrete_complementarity_set(). This method takes a while to compute, so it makes sense to cache the output. Since it will be cached, an immutable tuple makes more sense as a return value than a mutable list. --- src/sage/geometry/cone.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index f9768c96406..d16221a6962 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -4348,6 +4348,7 @@ def lineality(self): """ return self.linear_subspace().dimension() + @cached_method def discrete_complementarity_set(self): r""" Compute a discrete complementarity set of this cone. @@ -4360,7 +4361,7 @@ def discrete_complementarity_set(self): OUTPUT: - A list of pairs `(x,s)` such that, + A tuple of pairs `(x,s)` such that, * `x` and `s` are nonzero. * `x` and `s` are orthogonal. @@ -4380,7 +4381,7 @@ def discrete_complementarity_set(self): sage: K = Cone([(1,0),(0,1)]) sage: K.discrete_complementarity_set() - [(N(1, 0), M(0, 1)), (N(0, 1), M(1, 0))] + ((N(1, 0), M(0, 1)), (N(0, 1), M(1, 0))) If a cone consists of a single ray, then the second components of a discrete complementarity set for that cone should generate @@ -4388,13 +4389,13 @@ def discrete_complementarity_set(self): sage: K = Cone([(1,0)]) sage: K.discrete_complementarity_set() - [(N(1, 0), M(0, 1)), (N(1, 0), M(0, -1))] + ((N(1, 0), M(0, 1)), (N(1, 0), M(0, -1))) sage: K = Cone([(1,0,0)]) sage: K.discrete_complementarity_set() - [(N(1, 0, 0), M(0, 1, 0)), + ((N(1, 0, 0), M(0, 1, 0)), (N(1, 0, 0), M(0, -1, 0)), (N(1, 0, 0), M(0, 0, 1)), - (N(1, 0, 0), M(0, 0, -1))] + (N(1, 0, 0), M(0, 0, -1))) When a cone is the entire space, its dual is the trivial cone, so the only discrete complementarity set for it is empty:: @@ -4403,14 +4404,14 @@ def discrete_complementarity_set(self): sage: K.is_full_space() True sage: K.discrete_complementarity_set() - [] + () Likewise for trivial cones, whose duals are the entire space:: sage: L = ToricLattice(0) sage: K = Cone([], ToricLattice(0)) sage: K.discrete_complementarity_set() - [] + () TESTS: @@ -4421,7 +4422,7 @@ def discrete_complementarity_set(self): sage: set_random_seed() sage: K = random_cone(max_ambient_dim=6) sage: dcs_dual = K.dual().discrete_complementarity_set() - sage: expected = [ (x,s) for (s,x) in dcs_dual ] + sage: expected = tuple( (x,s) for (s,x) in dcs_dual ) sage: actual = K.discrete_complementarity_set() sage: sorted(actual) == sorted(expected) True @@ -4435,9 +4436,11 @@ def discrete_complementarity_set(self): sage: sum([ x.inner_product(s).abs() for (x,s) in dcs ]) 0 """ - return [ (x,s) for x in self - for s in self.dual() - if x.inner_product(s) == 0 ] + # Return an immutable tuple instead of a mutable list because + # the result will be cached. + return tuple( (x,s) for x in self + for s in self.dual() + if x.inner_product(s) == 0 ) def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, From 5cb0d4bbcbd32ed23686742fd773a28e77d72074 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 6 Oct 2015 12:13:58 -0500 Subject: [PATCH 346/421] Fixing doctests. --- src/sage/algebras/free_zinbiel_algebra.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/sage/algebras/free_zinbiel_algebra.py b/src/sage/algebras/free_zinbiel_algebra.py index 6d0b6497c2c..83c489fd124 100644 --- a/src/sage/algebras/free_zinbiel_algebra.py +++ b/src/sage/algebras/free_zinbiel_algebra.py @@ -13,16 +13,12 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.monoids.free_monoid import FreeMonoid -from sage.monoids.free_monoid_element import FreeMonoidElement - from sage.misc.cachefunc import cached_method from sage.categories.magmatic_algebras import MagmaticAlgebras from sage.categories.rings import Rings -from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.words.words import Words from sage.combinat.words.alphabet import Alphabet -from sage.structure.element import generic_power from sage.sets.family import Family class FreeZinbielAlgebra(CombinatorialFreeModule): @@ -130,7 +126,7 @@ class FreeZinbielAlgebra(CombinatorialFreeModule): sage: ((x*x)*x)*x 6*Z[xxxx] sage: (((x*x)*x)*x)*x - 24*Z[xxxx] + 24*Z[xxxxx] REFERENCES: @@ -249,8 +245,6 @@ def product_on_basis(self, x, y): sage: Z. = algebras.FreeZinbiel(QQ) sage: (x*y)*z # indirect doctest Z[xyz] + Z[xzy] - sage: x^4 - 3*Z[xxxx] """ if not x: return self.monomial(y) From 6a9b8ea4e2af1b89194237bd53ab3929684c7fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 6 Oct 2015 19:23:28 +0200 Subject: [PATCH 347/421] trac #19336 fixing custom latex of Lambert W function --- src/sage/functions/log.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index ac1f2e0fd00..d60ab561515 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -723,12 +723,12 @@ def _maxima_init_evaled_(self, n, z): NotImplementedError: Non-principal branch lambert_w[1](x) is not implemented in Maxima """ if n == 0: - if isinstance(z,str): - maxima_z=z - elif hasattr(z,'_maxima_init_'): - maxima_z=z._maxima_init_() + if isinstance(z, str): + maxima_z = z + elif hasattr(z, '_maxima_init_'): + maxima_z = z._maxima_init_() else: - maxima_z=str(z) + maxima_z = str(z) return "lambert_w(%s)" % maxima_z else: raise NotImplementedError("Non-principal branch lambert_w[%s](%s) is not implemented in Maxima" % (n, z)) @@ -759,15 +759,17 @@ def _print_latex_(self, n, z): EXAMPLES:: sage: latex(lambert_w(1)) - \operatorname{W_0}(1) + \operatorname{W}({1}) sage: latex(lambert_w(0,x)) - \operatorname{W_0}(x) + \operatorname{W}({x}) sage: latex(lambert_w(1,x)) - \operatorname{W_{1}}(x) + \operatorname{W_{1}}({x}) + sage: latex(lambert_w(1,x+exp(x))) + \operatorname{W_{1}}({x + e^x}) """ if n == 0: - return r"\operatorname{W_0}(%s)" % z + return r"\operatorname{W}({%s})" % z else: - return r"\operatorname{W_{%s}}(%s)" % (n, z) + return r"\operatorname{W_{%s}}({%s})" % (n, z) lambert_w = Function_lambert_w() From aabe6d1f51e3da7fd74787353802885815b45ea6 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Wed, 7 Oct 2015 00:47:09 +0200 Subject: [PATCH 348/421] Updated Sage version to 6.9.rc3 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- src/bin/sage-banner | 2 +- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 0b9154de703..fffa4f6775c 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 6.9.rc2, released 2015-10-03 +Sage version 6.9.rc3, released 2015-10-06 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index d5d29ab9f1d..55c47a9e1f3 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=ada10da8395c80225ed3f14fb431903738e018e6 -md5=34d89f1260160198a6255a73553c5c1d -cksum=3738976361 +sha1=03f35d65364edef5f2f8bb43a2e53e8816be4dbe +md5=903c006410269e4ca3767a6cd4db8971 +cksum=850114776 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 5bc6609e3d8..415196e47f5 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -117 +118 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index ec3aa085983..b4a7da6767f 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath Version 6.9.rc2, Release Date: 2015-10-03 │ +│ SageMath Version 6.9.rc3, Release Date: 2015-10-06 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 1c0f8fe3ffb..8b2b6b5fb05 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='6.9.rc2' -SAGE_RELEASE_DATE='2015-10-03' +SAGE_VERSION='6.9.rc3' +SAGE_RELEASE_DATE='2015-10-06' diff --git a/src/sage/version.py b/src/sage/version.py index 2089950ea72..27ca7af9cf7 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '6.9.rc2' -date = '2015-10-03' +version = '6.9.rc3' +date = '2015-10-06' From be0c14deab2fef8da6289f6a62c94d5c5ca53b95 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 29 Sep 2015 13:00:55 +0200 Subject: [PATCH 349/421] trac #19311: A (729,336,153,156)-strongly regular graph --- src/sage/graphs/strongly_regular_db.pyx | 60 +++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index cfefb513375..33913de6b5c 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -1522,6 +1522,65 @@ def SRG_560_208_72_80(): h.relabel() return h +def SRG_729_336_153_156(): + r""" + Return a `(729, 336, 153, 156)`-strongly regular graph. + + This graph is built from a 2-intersection code shared by L. Disset in his + thesis [Disset00]_ and available at + http://www.mat.uc.cl/~ldissett/newgraphs/. + + EXAMPLE:: + + sage: from sage.graphs.strongly_regular_db import SRG_729_336_153_156 + sage: G = SRG_729_336_153_156() # long time + sage: G.is_strongly_regular(parameters=True) # long time + (729, 336, 153, 156) + + REFERENCES: + + .. [Disset00] L. Dissett, + Combinatorial and computational aspects of finite geometries, + 2000, + https://tspace.library.utoronto.ca/bitstream/1807/14575/1/NQ49844.pdf + """ + from itertools import product, izip + L = [ + "101212212122202012010102120101112012121001201012120220122112001121201201201201010020012201001201201201202120121122012021201221021110200212121011211002012220000122201201", + "011100122001200111220011220020011222001200022000220012220122011220011101122012012001222010122200012011120112220112000120120012002012201122001220012122000201212001211211", + "000011111000011111112000001112000000111122222000001111112222000001111122222000111222222001111122222000001111112222000001112222000111122222000001111222000011122000011122", + "000000000111111111111000000000111111111111111222222222222222000000000000000111111111111222222222222000000000000000111111111111222222222222000000000000111111111222222222", + "000000000000000000000111111111111111111111111111111111111111000000000000000000000000000000000000000111111111111111111111111111111111111111222222222222222222222222222222", + "000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", + "0"*168 + ] + + q = 3 + k = 6 + K = GF(q) + L = list(Matrix(GF(q),map(list,L)).transpose()) + g = Graph() + + # Vertices of the graph + V = [x for x in product(K,repeat=k+1) + if x[-1]] + + # For every point in F_q^{k+1} not on the hyperplane of L + for u in V: + uh = tuple([uu/u[-1] for uu in u]) + + # For every v point of L + for v in L: + + # u is adjacent with all vertices on a uv line. + for qq in K: + v_last = u[-1]+qq*(v[-1]-u[-1]) + if v_last: + g.add_edge(uh,tuple([(uu+qq*(vv-uu))/v_last + for uu,vv in izip(u,v)])) + g.relabel() + return g + def SRG_729_532_391_380(): r""" Return a `(729, 532, 391, 380)`-strongly regular graph. @@ -2275,6 +2334,7 @@ def strongly_regular_graph(int v,int k,int l,int mu=-1,bint existence=False,bint (560, 208, 72, 80): [SRG_560_208_72_80], (625, 416, 279,272): [SRG_625_416_279_272], (625, 364, 213,210): [SRG_625_364_213_210], + (729, 336, 153,156): [SRG_729_336_153_156], (729, 616, 523,506): [SRG_729_616_523_506], (729, 420, 243,240): [SRG_729_420_243_240], (729, 560, 433,420): [SRG_729_560_433_420], From 59b3692f5f1fb83aaeb6f6d99322537e2d0b9d57 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 30 Sep 2015 21:46:09 +0200 Subject: [PATCH 350/421] trac #19311: Draft of an individual function --- src/sage/graphs/strongly_regular_db.pyx | 94 ++++++++++++++++++------- 1 file changed, 67 insertions(+), 27 deletions(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 33913de6b5c..ac1fe890a92 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -1522,6 +1522,71 @@ def SRG_560_208_72_80(): h.relabel() return h +def strongly_regular_from_two_intersection_set(M): + r""" + Return a strongly regular graph from a 2-intersection set. + + A set of points in the projective geometry `PG(k,q)` is said to be a + 2-intersection set if it intersects every hyperplane in either `h_1` or + `h_2` points, where `h_1,h_2\in \\NN`. + + From a 2-intersection set `S` can be defined a strongly-regular graph in the + following way: + + - Place the points of `S` on a hyperplane `H` in `PG(k+1,q)` + + - Define the graph `G` on all points of `PG(k+1,q)\backslash H` + + - Make two points of `V(G)=PG(k+1,q)\backslash H` adjacent if the line going + through them intersects `S` + + For more information, see e.g. [CDB13]_ where this explanation has been + taken from. + + INPUT: + + - `M` -- a `k \times |S|` matrix defined on `F_q` representing the points of + the 2-intersection set. + + **EXPLAIN HOMOGENEOUS COORDINATES AND HOW THEY ARE EXPECTED BY THIS FUNCTION** + + EXAMPLE:: + + ** Find examples** + + .. [CDB13] I. Cardinali and B. De Bruyn, + Spin-embeddings, two-intersection sets and two-weight codes, + Ars Comb. 109 (2013): 309-319. + https://biblio.ugent.be/publication/4241842/file/4241845.pdf + """ + from itertools import product, izip + K = M.base_ring() + k = M.ncols() + g = Graph() + + M = [list(p)+[0] for p in M] + + # Vertices of the graph + V = [x for x in product(K,repeat=k+1) + if x[-1]] + + # For every point in F_q^{k+1} not on the hyperplane of M + for u in V: + uh = tuple([uu/u[-1] for uu in u]) + + # For every v point of M + for v in M: + + # u is adjacent with all vertices on a uv line. + for qq in K: + v_last = u[-1]+qq*(v[-1]-u[-1]) + if v_last: + g.add_edge(uh,tuple([(uu+qq*(vv-uu))/v_last + for uu,vv in izip(u,v)])) + g.relabel() + return g + + def SRG_729_336_153_156(): r""" Return a `(729, 336, 153, 156)`-strongly regular graph. @@ -1544,7 +1609,6 @@ def SRG_729_336_153_156(): 2000, https://tspace.library.utoronto.ca/bitstream/1807/14575/1/NQ49844.pdf """ - from itertools import product, izip L = [ "101212212122202012010102120101112012121001201012120220122112001121201201201201010020012201001201201201202120121122012021201221021110200212121011211002012220000122201201", "011100122001200111220011220020011222001200022000220012220122011220011101122012012001222010122200012011120112220112000120120012002012201122001220012122000201212001211211", @@ -1552,34 +1616,10 @@ def SRG_729_336_153_156(): "000000000111111111111000000000111111111111111222222222222222000000000000000111111111111222222222222000000000000000111111111111222222222222000000000000111111111222222222", "000000000000000000000111111111111111111111111111111111111111000000000000000000000000000000000000000111111111111111111111111111111111111111222222222222222222222222222222", "000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", - "0"*168 ] - q = 3 - k = 6 - K = GF(q) - L = list(Matrix(GF(q),map(list,L)).transpose()) - g = Graph() - - # Vertices of the graph - V = [x for x in product(K,repeat=k+1) - if x[-1]] - - # For every point in F_q^{k+1} not on the hyperplane of L - for u in V: - uh = tuple([uu/u[-1] for uu in u]) - - # For every v point of L - for v in L: - - # u is adjacent with all vertices on a uv line. - for qq in K: - v_last = u[-1]+qq*(v[-1]-u[-1]) - if v_last: - g.add_edge(uh,tuple([(uu+qq*(vv-uu))/v_last - for uu,vv in izip(u,v)])) - g.relabel() - return g + L = Matrix(GF(3),map(list,L)).transpose() + return strongly_regular_from_two_intersection_set(L) def SRG_729_532_391_380(): r""" From 85e1bc1a868e3906b073e08fb1507918ee484c14 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 6 Oct 2015 16:04:35 -0700 Subject: [PATCH 351/421] docs and example and optimisation/simplification --- src/sage/graphs/strongly_regular_db.pyx | 33 +++++++++++-------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index ac1fe890a92..64e7c6f55dd 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -1545,14 +1545,21 @@ def strongly_regular_from_two_intersection_set(M): INPUT: - - `M` -- a `k \times |S|` matrix defined on `F_q` representing the points of - the 2-intersection set. + - `M` -- a `|S| \times k` matrix with entries in `F_q` representing the points of + the 2-intersection set. We assume that the first non-zero entry of each row is + equal to `1`, that is, they give points in homogeneous coordinates. - **EXPLAIN HOMOGENEOUS COORDINATES AND HOW THEY ARE EXPECTED BY THIS FUNCTION** + The implementation does not check that `S` is actually a 2-intersection set. EXAMPLE:: - ** Find examples** + sage: from sage.graphs.strongly_regular_db import strongly_regular_from_two_intersection_set + sage: S=Matrix([(0,0,1),(0,1,0)] + map(lambda x: (1,x^2,x), GF(4,'b'))) + sage: g=strongly_regular_from_two_intersection_set(S) + sage: g.is_strongly_regular(parameters=True) + (64, 18, 2, 6) + + REFERENCES: .. [CDB13] I. Cardinali and B. De Bruyn, Spin-embeddings, two-intersection sets and two-weight codes, @@ -1564,25 +1571,15 @@ def strongly_regular_from_two_intersection_set(M): k = M.ncols() g = Graph() - M = [list(p)+[0] for p in M] - - # Vertices of the graph - V = [x for x in product(K,repeat=k+1) - if x[-1]] + M = [list(p) for p in M] # For every point in F_q^{k+1} not on the hyperplane of M - for u in V: - uh = tuple([uu/u[-1] for uu in u]) - + for u in [tuple(x) for x in product(K,repeat=k)]: # For every v point of M for v in M: - # u is adjacent with all vertices on a uv line. - for qq in K: - v_last = u[-1]+qq*(v[-1]-u[-1]) - if v_last: - g.add_edge(uh,tuple([(uu+qq*(vv-uu))/v_last - for uu,vv in izip(u,v)])) + g.add_edges([[u,tuple([u[i]+qq*v[i] for i in range(k)])] \ + for qq in K if not qq==K.zero()]) g.relabel() return g From 70d9d5158e99e59566b4f4508f40c1dac0d99672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Oct 2015 18:37:21 +0200 Subject: [PATCH 352/421] trac #19336 latex of lambert_w again, plus a few local enhancements --- src/sage/functions/log.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index d60ab561515..d75c7b2e2f1 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -565,8 +565,10 @@ def __init__(self): 0.567143290409784 """ BuiltinFunction.__init__(self, "lambert_w", nargs=2, - conversions={'mathematica':'ProductLog', - 'maple':'LambertW'}) + conversions={'mathematica': 'ProductLog', + 'maple': 'LambertW', + 'matlab': 'lambertw', + 'maxima': 'generalized_lambert_w'}) def __call__(self, *args, **kwds): r""" @@ -718,21 +720,18 @@ def _maxima_init_evaled_(self, n, z): sage: lambert_w(0, x)._maxima_() lambert_w(_SAGE_VAR_x) sage: lambert_w(1, x)._maxima_() - Traceback (most recent call last): - ... - NotImplementedError: Non-principal branch lambert_w[1](x) is not implemented in Maxima + generalized_lambert_w(1,_SAGE_VAR_x) """ + if isinstance(z, str): + maxima_z = z + elif hasattr(z, '_maxima_init_'): + maxima_z = z._maxima_init_() + else: + maxima_z = str(z) if n == 0: - if isinstance(z, str): - maxima_z = z - elif hasattr(z, '_maxima_init_'): - maxima_z = z._maxima_init_() - else: - maxima_z = str(z) return "lambert_w(%s)" % maxima_z else: - raise NotImplementedError("Non-principal branch lambert_w[%s](%s) is not implemented in Maxima" % (n, z)) - + return "generalized_lambert_w(%s,%s)" % (n, maxima_z) def _print_(self, n, z): """ @@ -765,11 +764,11 @@ def _print_latex_(self, n, z): sage: latex(lambert_w(1,x)) \operatorname{W_{1}}({x}) sage: latex(lambert_w(1,x+exp(x))) - \operatorname{W_{1}}({x + e^x}) + \operatorname{W_{1}}({x + e^{x}}) """ if n == 0: - return r"\operatorname{W}({%s})" % z + return r"\operatorname{W}({%s})" % z._latex_() else: - return r"\operatorname{W_{%s}}({%s})" % (n, z) + return r"\operatorname{W_{%s}}({%s})" % (n, z._latex_()) lambert_w = Function_lambert_w() From d79d3df614f6c0dc4d1131627673dafdfe9688cd Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Oct 2015 13:44:56 -0500 Subject: [PATCH 353/421] Fixing typo. --- src/sage/algebras/free_zinbiel_algebra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/free_zinbiel_algebra.py b/src/sage/algebras/free_zinbiel_algebra.py index 83c489fd124..9c00304eea7 100644 --- a/src/sage/algebras/free_zinbiel_algebra.py +++ b/src/sage/algebras/free_zinbiel_algebra.py @@ -89,7 +89,7 @@ class FreeZinbielAlgebra(CombinatorialFreeModule): .. WARNING:: - Currently the basis is indexed all words over the variables, + Currently the basis is indexed by all words over the variables, incuding the empty word. This is a slight abuse as it is suppose to be the indexed by all non-empty words. From 2f8fe5e02cfbfaf1d1b624b29c125028c787a089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Oct 2015 21:30:15 +0200 Subject: [PATCH 354/421] fixing hidden doctests in geometry folder --- .../hyperbolic_space/hyperbolic_geodesic.py | 4 ++-- src/sage/geometry/lattice_polytope.py | 19 +++++++++---------- src/sage/geometry/polyhedron/base.py | 2 +- .../geometry/polyhedron/double_description.py | 2 +- .../double_description_inhomogeneous.py | 4 ++-- .../lattice_euclidean_group_element.py | 2 +- src/sage/geometry/polyhedron/palp_database.py | 10 +++++----- 7 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index 73fd2c6414e..3c2fe11daf6 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -1168,11 +1168,11 @@ def _crossratio_matrix(p0, p1, p2): # UHP r""" Given three points (the list `p`) in `\mathbb{CP}^{1}` in affine coordinates, return the linear fractional transformation taking - the elements of `p` to `0`,`1`, and `\infty'. + the elements of `p` to `0`, `1`, and `\infty`. INPUT: - - a list of three distinct elements of three distinct elements + - a list of three distinct elements of `\mathbb{CP}^1` in affine coordinates; that is, each element must be a complex number, `\infty`, or symbolic. diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 8f0eca94c9b..d0cc12f77b6 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -839,19 +839,19 @@ def _copy_faces(self, other, reverse=False): reflexive polytopes, faces of this polytope and its polar are in inclusion reversing bijection. - .. note:: + .. NOTE:: This function does not perform any checks that this operation makes sense. INPUT: - - ``other`` - another LatticePolytope, whose facial structure will be + - ``other`` -- another LatticePolytope, whose facial structure will be copied - - ``reverse`` - (default: False) if True, the facial structure of the - other polytope will be reversed, i.e. vertices will correspond to - facets etc. + - ``reverse`` -- (default: ``False``) if ``True``, the facial + structure of the other polytope will be reversed, + i.e. vertices will correspond to facets etc. TESTS:: @@ -1101,8 +1101,8 @@ def _pullback(self, data): INPUT: - - ``data`` - rational point or matrix of points (as columns) in the - ambient space + - ``data`` -- rational point or matrix of points (as columns) in the + ambient space OUTPUT: The same point(s) in the coordinates of the affine subspace space spanned by this polytope. @@ -5752,7 +5752,7 @@ def _palp_convert_permutation(permutation): PALP specifies a permutation group element by its domain. Furthermore, it only supports permutations of up to 62 objects and labels these by - `0 \dots 9', 'a \dots z', and 'A \dots Z'. + `0 \dots 9`, `a \dots z`, and `A \dots Z`. INPUT: @@ -5760,8 +5760,7 @@ def _palp_convert_permutation(permutation): OUTPUT: - A :class:`permutation group element - `. + A :class:`permutation group element `. EXAMPLES:: diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 0d3fe99bad4..e270b03b004 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2381,7 +2381,7 @@ def Minkowski_difference(self, other): return P.element_class(P, None, [new_ieqs, new_eqns]) def __sub__(self, other): - """ + r""" Implement minus binary operation Polyhedra are not a ring with respect to dilatation and diff --git a/src/sage/geometry/polyhedron/double_description.py b/src/sage/geometry/polyhedron/double_description.py index 3bff500b362..451e4638b90 100644 --- a/src/sage/geometry/polyhedron/double_description.py +++ b/src/sage/geometry/polyhedron/double_description.py @@ -628,7 +628,7 @@ def dim(self): return self._A.ncols() def __repr__(self): - """ + r""" Return a string representation. OUTPUT: diff --git a/src/sage/geometry/polyhedron/double_description_inhomogeneous.py b/src/sage/geometry/polyhedron/double_description_inhomogeneous.py index 9d4c6bd3d7d..2425070aca6 100644 --- a/src/sage/geometry/polyhedron/double_description_inhomogeneous.py +++ b/src/sage/geometry/polyhedron/double_description_inhomogeneous.py @@ -232,7 +232,7 @@ def _init_Vrep(self, inequalities, equations): return self._pivot_inequalities(A) def _split_linear_subspace(self): - """ + r""" Split the linear subspace in a generator with `x_0\not=0` and the remaining generators with `x_0=0`. @@ -504,7 +504,7 @@ def is_trivial(ray): self.equations = self._linear_subspace.matrix().rows() def _repr_(self): - """ + r""" Return a string representation. OUTPUT: diff --git a/src/sage/geometry/polyhedron/lattice_euclidean_group_element.py b/src/sage/geometry/polyhedron/lattice_euclidean_group_element.py index 96b2bc42eeb..f3fc6f54125 100644 --- a/src/sage/geometry/polyhedron/lattice_euclidean_group_element.py +++ b/src/sage/geometry/polyhedron/lattice_euclidean_group_element.py @@ -116,7 +116,7 @@ def __call__(self, x): return v def _repr_(self): - """ + r""" Return a string representation EXAMPLES:: diff --git a/src/sage/geometry/polyhedron/palp_database.py b/src/sage/geometry/polyhedron/palp_database.py index 98a85712530..4fb51d9312a 100644 --- a/src/sage/geometry/polyhedron/palp_database.py +++ b/src/sage/geometry/polyhedron/palp_database.py @@ -134,7 +134,7 @@ def _palp_Popen(self): return Popen(["class.x", "-b2a", "-di", self._data_basename], stdout=PIPE) def _read_vertices(self, stdout, rows, cols): - """ + r""" Read vertex data from the PALP output pipe. OUTPUT: @@ -158,7 +158,7 @@ def _read_vertices(self, stdout, rows, cols): return m def _read_vertices_transposed(self, stdout, rows, cols): - """ + r""" Read vertex data from the PALP output pipe. OUTPUT: @@ -428,9 +428,9 @@ def __init__(self, h11, h21, data_basename=None, **kwds): TESTS:: - sage: from sage.geometry.polyhedron.palp_database import Reflexive4dHodge - sage: Reflexive4dHodge(1,101) # optional - polytopes_db_4d - + sage: from sage.geometry.polyhedron.palp_database import Reflexive4dHodge + sage: Reflexive4dHodge(1,101) # optional - polytopes_db_4d + """ dim = 4 if data_basename is None: From 371714c6914b5392d5ee0b10df35fef48130809b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 4 Oct 2015 20:35:43 -0400 Subject: [PATCH 355/421] Trac #19368: Add LL() method for polyhedral closed convex cones. The Lyapunov-like transformations generalize the complementary slackness condition and were originally studied by Rudolf et al. under another name. It was later shown that the space of all Lyapunov-like transformations is the Lie algebra of the automorphism group of a proper cone. This result extends to all closed convex cones. Being a complementarity property, the Lyapunov-like property `(L*x).inner_product(s) == 0` need only be checked on pairs of vectors in the complementarity set of a cone. And in fact, it suffices to check only those pairs belonging to generating sets of the cone and its dual -- namely, the `discrete_complementarity_set()`. This commit adds a new `LL()` method for polyhedral closed convex cones. The method computes a basis for the space of all Lyapunov-like transformations on a given cone by constructing the orthogonal complement of a set based on the `discrete_complementarity_set()`. --- src/sage/geometry/cone.py | 147 +++++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index d16221a6962..10c815ab6d0 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -207,10 +207,10 @@ is_ToricLatticeQuotient from sage.geometry.toric_plotter import ToricPlotter, label_list from sage.graphs.digraph import DiGraph -from sage.matrix.all import matrix +from sage.matrix.all import matrix, MatrixSpace from sage.misc.all import cached_method, flatten, latex from sage.misc.superseded import deprecation -from sage.modules.all import span, vector +from sage.modules.all import span, vector, VectorSpace from sage.rings.all import QQ, RR, ZZ, gcd from sage.structure.all import SageObject, parent from sage.libs.ppl import C_Polyhedron, Generator_System, Constraint_System, \ @@ -4442,6 +4442,149 @@ def discrete_complementarity_set(self): for s in self.dual() if x.inner_product(s) == 0 ) + def LL(self): + r""" + Compute a basis of Lyapunov-like transformations on this cone. + + A linear transformation `L` is said to be Lyapunov-like on this + cone if `L(x)` and `s` are orthogonal for every pair `(x,s)` in + its :meth:`discrete_complementarity_set`. The set of all such + transformations forms a vector space, namely the Lie algebra of + the automorphism group of this cone. + + OUTPUT: + + A list of matrices forming a basis for the space of all + Lyapunov-like transformations on this cone. + + REFERENCES: + + M. Orlitzky. The Lyapunov rank of an improper cone. + http://www.optimization-online.org/DB_HTML/2015/10/5135.html + + .. [Rudolf] G. Rudolf, N. Noyan, D. Papp, and F. Alizadeh. + Bilinear optimality constraints for the cone of positive + polynomials. Mathematical Programming, Series B, 129 (2011) 5-31. + + EXAMPLES: + + The trivial cone has no Lyapunov-like transformations:: + + sage: L = ToricLattice(0) + sage: K = Cone([], lattice=L) + sage: K.LL() + [] + + The Lyapunov-like transformations on the nonnegative orthant are + diagonal matrices:: + + sage: K = Cone([(1,)]) + sage: K.LL() + [[1]] + + sage: K = Cone([(1,0),(0,1)]) + sage: K.LL() + [ + [1 0] [0 0] + [0 0], [0 1] + ] + + sage: K = Cone([(1,0,0),(0,1,0),(0,0,1)]) + sage: K.LL() + [ + [1 0 0] [0 0 0] [0 0 0] + [0 0 0] [0 1 0] [0 0 0] + [0 0 0], [0 0 0], [0 0 1] + ] + + Only the identity matrix is Lyapunov-like on the pyramids + defined by the one- and infinity-norms [Rudolf]_:: + + sage: l31 = Cone([(1,0,1), (0,-1,1), (-1,0,1), (0,1,1)]) + sage: l31.LL() + [ + [1 0 0] + [0 1 0] + [0 0 1] + ] + + sage: l3infty = Cone([(0,1,1), (1,0,1), (0,-1,1), (-1,0,1)]) + sage: l3infty.LL() + [ + [1 0 0] + [0 1 0] + [0 0 1] + ] + + Every transformation is Lyapunov-like on the ambient space:: + + sage: K = Cone([(1,0), (-1,0), (0,1), (0,-1)]) + sage: K.is_full_space() + True + sage: M = MatrixSpace(K.lattice().base_field(), K.lattice_dim()) + sage: M.basis() == K.LL() + True + + TESTS: + + The vectors `L(x)` and `s` are orthogonal for every pair `(x,s)` + in the :meth:`discrete_complementarity_set` of the cone:: + + sage: set_random_seed() + sage: K = random_cone(max_ambient_dim=8) + sage: dcs = K.discrete_complementarity_set() + sage: ips = [ (L*x).inner_product(s) for (x,s) in dcs + ....: for L in K.LL() ] + sage: sum(map(abs, ips)) + 0 + + The Lyapunov-like transformations on a cone and its dual are + transposes of one another. However, there's no reason to expect + that one basis will consist of transposes of the other:: + + sage: set_random_seed() + sage: K = random_cone(max_ambient_dim=8) + sage: LL2 = [ L.transpose() for L in K.dual().LL() ] + sage: V = VectorSpace(K.lattice().base_field(), K.lattice_dim()^2) + sage: LL1_vecs = [ V(m.list()) for m in K.LL() ] + sage: LL2_vecs = [ V(m.list()) for m in LL2 ] + sage: V.span(LL1_vecs) == V.span(LL2_vecs) + True + + The space of all Lyapunov-like transformations is a Lie algebra + and should therefore be closed under the lie bracket:: + + sage: set_random_seed() + sage: K = random_cone(max_ambient_dim=4) + sage: W = VectorSpace(K.lattice().base_field(), K.lattice_dim()**2) + sage: LL_W = W.span([ W(m.list()) for m in K.LL() ]) + sage: brackets = [ W((L1*L2 - L2*L1).list()) for L1 in K.LL() + ....: for L2 in K.LL() ] + sage: all([ b in LL_W for b in brackets ]) + True + """ + # Matrices are not vectors in Sage, so we have to convert them + # to vectors explicitly before we can find a basis. We need these + # two values to construct the appropriate "long vector" space. + F = self.lattice().base_field() + n = self.lattice_dim() + + # These tensor products contain a basis for the orthogonal + # complement of the Lyapunov-like transformations on this cone. + tensor_products = [ s.tensor_product(x) + for (x,s) in self.discrete_complementarity_set() ] + + # Convert those tensor products to long vectors. + W = VectorSpace(F, n**2) + perp_vectors = [ W(tp.list()) for tp in tensor_products ] + + # Now find the Lyapunov-like transformations (as long vectors). + LL_vectors = W.span(perp_vectors).complement() + + # And finally convert the long vectors back to matrices. + M = MatrixSpace(F, n, n) + return [ M(v.list()) for v in LL_vectors.basis() ] + def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, min_rays=0, max_rays=None, strictly_convex=None, solid=None): From e94cf83e5699cca28746cdf70f201f3080440b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Oct 2015 09:45:39 +0200 Subject: [PATCH 356/421] trac #18939 turn on visibility of example (alpha=1) --- src/sage/plot/arc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/plot/arc.py b/src/sage/plot/arc.py index 789e68e07f5..61502267446 100644 --- a/src/sage/plot/arc.py +++ b/src/sage/plot/arc.py @@ -287,7 +287,7 @@ def bezier_path(self): EXAMPLES:: sage: from sage.plot.arc import Arc - sage: op = {'alpha':0,'thickness':1,'rgbcolor':'blue','zorder':0, + sage: op = {'alpha':1,'thickness':1,'rgbcolor':'blue','zorder':0, ....: 'linestyle':'--'} sage: Arc(2,3,2.2,2.2,0,2,3,op).bezier_path() Graphics object consisting of 1 graphics primitive From f15bf84f28da17b9bcfc3780a804a432eac3f6f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Oct 2015 20:15:42 +0200 Subject: [PATCH 357/421] correct hidden doc in graphs, and in particular in schnyder.py --- src/sage/graphs/independent_sets.pyx | 2 +- src/sage/graphs/schnyder.py | 145 +++++++++++++++------------ 2 files changed, 83 insertions(+), 64 deletions(-) diff --git a/src/sage/graphs/independent_sets.pyx b/src/sage/graphs/independent_sets.pyx index cbb99409f55..e91e0609dba 100644 --- a/src/sage/graphs/independent_sets.pyx +++ b/src/sage/graphs/independent_sets.pyx @@ -338,7 +338,7 @@ cdef class IndependentSets: - ``S`` -- a set of vertices to be tested. - TESTS:: + TESTS: All independent sets of PetersenGraph are... independent sets:: diff --git a/src/sage/graphs/schnyder.py b/src/sage/graphs/schnyder.py index c4b4b8398fc..6af206e9581 100644 --- a/src/sage/graphs/schnyder.py +++ b/src/sage/graphs/schnyder.py @@ -6,12 +6,14 @@ Walter Schnyder's Algorithm. AUTHORS: - -- Jonathan Bober, Emily Kirkman (2008-02-09): initial version + +- Jonathan Bober, Emily Kirkman (2008-02-09) -- initial version REFERENCE: - [1] Schnyder, Walter. Embedding Planar Graphs on the Grid. - Proc. 1st Annual ACM-SIAM Symposium on Discrete Algorithms, - San Francisco (1994), pp. 138-147. + +.. [1] Schnyder, Walter. Embedding Planar Graphs on the Grid. + Proc. 1st Annual ACM-SIAM Symposium on Discrete Algorithms, + San Francisco (1994), pp. 138-147. """ #***************************************************************************** # Copyright (C) 2008 Jonathan Bober and Emily Kirkman @@ -42,11 +44,13 @@ def _triangulate(g, comb_emb): method will work on one of these attempts.) INPUT: - g -- the graph to triangulate - comb_emb -- a planar combinatorial embedding of g - RETURNS: - A list of edges that are added to the graph (in place) + - g -- the graph to triangulate + - ``comb_emb`` -- a planar combinatorial embedding of g + + OUTPUT: + + A list of edges that are added to the graph (in place) EXAMPLES:: @@ -125,27 +129,33 @@ def _triangulate(g, comb_emb): return edges_added def _normal_label(g, comb_emb, external_face): - """ - Helper function to schnyder method for computing coordinates in the plane to - plot a planar graph with no edge crossings. + r""" + Helper function to schnyder method for computing coordinates in + the plane to plot a planar graph with no edge crossings. - Constructs a normal labelling of a triangular graph g, given the planar - combinatorial embedding of g and a designated external face. Returns labels - dictionary. The normal label is constructed by first contracting the graph - down to its external face, then expanding the graph back to the original while - simultaneously adding angle labels. + Constructs a normal labelling of a triangular graph g, given the + planar combinatorial embedding of g and a designated external + face. Returns labels dictionary. The normal label is constructed + by first contracting the graph down to its external face, then + expanding the graph back to the original while simultaneously + adding angle labels. INPUT: - g -- the graph to find the normal labeling of (g must be triangulated) - comb_emb -- a planar combinatorial embedding of g - external_face -- the list of three edges in the external face of g - RETURNS: - x -- tuple with entries - x[0] = dict of dicts of normal labeling for each vertex of g and each - adjacent neighbors u,v (u < v) of vertex: - { vertex : { (u,v): angel_label } } - x[1] = (v1,v2,v3) tuple of the three vertices of the external face. + - g -- the graph to find the normal labeling of (g must be triangulated) + - ``comb_emb`` -- a planar combinatorial embedding of g + - ``external_face`` -- the list of three edges in the external face of g + + OUTPUT: + + x -- tuple with entries + + x[0] = dict of dicts of normal labeling for each vertex of g and each + adjacent neighbors u,v (u < v) of vertex: + + { vertex : { (u,v): angel_label } } + + x[1] = (v1,v2,v3) tuple of the three vertices of the external face. EXAMPLES:: @@ -160,7 +170,6 @@ def _normal_label(g, comb_emb, external_face): sage: _realizer(g, tn) ({0: []}, (0, 1, 2)) - """ contracted = [] contractible = [] @@ -329,20 +338,27 @@ def _realizer(g, x, example=False): give a path to each of the three external vertices. INPUT: - g -- the graph to compute the realizer of - x -- tuple with entries - x[0] = dict of dicts representing a normal labeling of g. For - each vertex of g and each adjacent neighbors u,v (u < v) of - vertex: { vertex : { (u,v): angle_label } } - x[1] = (v1, v2, v3) tuple of the three external vertices (also - the roots of each tree) - - RETURNS: - x -- tuple with entries - x[0] = dict of lists of TreeNodes: - { root_vertex : [ list of all TreeNodes under root_vertex ] } - x[0] = (v1,v2,v3) tuple of the three external vertices (also the - roots of each tree) + + - g -- the graph to compute the realizer of + - x -- tuple with entries + + x[0] = dict of dicts representing a normal labeling of g. For + each vertex of g and each adjacent neighbors u,v (u < v) of + vertex: { vertex : { (u,v): angle_label } } + + x[1] = (v1, v2, v3) tuple of the three external vertices (also + the roots of each tree) + + OUTPUT: + + - x -- tuple with entries + + x[0] = dict of lists of TreeNodes: + + { root_vertex : [ list of all TreeNodes under root_vertex ] } + + x[1] = (v1,v2,v3) tuple of the three external vertices (also the + roots of each tree) EXAMPLES:: @@ -409,22 +425,26 @@ def _realizer(g, x, example=False): return tree_nodes, (v1,v2,v3) def _compute_coordinates(g, x): - """ + r""" Given a triangulated graph g with a dict of trees given by the realizer and tuple of the external vertices, we compute the coordinates of a planar geometric embedding in the grid. - The coordinates will be set to the _pos attribute of g. + The coordinates will be set to the ``_pos`` attribute of g. INPUT: - g -- the graph to compute the coordinates of - x -- tuple with entries - x[0] = dict of tree nodes for the three trees with each external - vertex as root - { root_vertex : [ list of all TreeNodes under root_vertex ] } - - x[1] = (v1, v2, v3) tuple of the three external vertices (also - the roots of each tree) + + - g -- the graph to compute the coordinates of + - x -- tuple with entries + + x[0] = dict of tree nodes for the three trees with each external + vertex as root: + + { root_vertex : [ list of all TreeNodes under root_vertex ] } + + x[1] = (v1, v2, v3) tuple of the three external vertices (also + the roots of each tree) + EXAMPLES:: sage: from sage.graphs.schnyder import _triangulate, _normal_label, _realizer, _compute_coordinates @@ -505,16 +525,17 @@ def _compute_coordinates(g, x): class TreeNode(): """ - A class to represent each node in the trees used by _realizer() and - _compute_coordinates() when finding a planar geometric embedding in + A class to represent each node in the trees used by :func:`_realizer` and + :func:`_compute_coordinates` when finding a planar geometric embedding in the grid. Each tree node is doubly linked to its parent and children. INPUT: - parent -- the parent TreeNode of self - children -- a list of TreeNode children of self - label -- the associated realizer vertex label + + - ``parent`` -- the parent TreeNode of ``self`` + - ``children`` -- a list of TreeNode children of ``self`` + - ``label`` -- the associated realizer vertex label EXAMPLES:: @@ -532,14 +553,14 @@ class TreeNode(): sage: tn.compute_depth_of_self_and_children() sage: tn3.depth 2 - """ def __init__(self, parent = None, children = None, label = None): """ INPUT: - parent -- the parent TreeNode of self - children -- a list of TreeNode children of self - label -- the associated realizer vertex label + + - ``parent`` -- the parent TreeNode of ``self`` + - ``children`` -- a list of TreeNode children of ``self`` + - ``label`` -- the associated realizer vertex label EXAMPLE:: @@ -557,7 +578,6 @@ def __init__(self, parent = None, children = None, label = None): sage: tn.compute_depth_of_self_and_children() sage: tn3.depth 2 - """ if children is None: children = [] @@ -566,10 +586,10 @@ def __init__(self, parent = None, children = None, label = None): self.label = label self.number_of_descendants = 1 - def compute_number_of_descendants(self): """ Computes the number of descendants of self and all descendants. + For each TreeNode, sets result as attribute self.number_of_descendants EXAMPLES:: @@ -599,6 +619,7 @@ def compute_number_of_descendants(self): def compute_depth_of_self_and_children(self): """ Computes the depth of self and all descendants. + For each TreeNode, sets result as attribute self.depth EXAMPLES:: @@ -617,7 +638,6 @@ def compute_depth_of_self_and_children(self): sage: tn.compute_depth_of_self_and_children() sage: tn3.depth 2 - """ if self.parent is None: self.depth = 1 @@ -646,7 +666,6 @@ def append_child(self, child): sage: tn.compute_depth_of_self_and_children() sage: tn3.depth 2 - """ if child in self.children: return From e1a1963ebd7351830f6e58fa63e8db796df1a1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Oct 2015 20:17:36 +0200 Subject: [PATCH 358/421] one missing correction --- src/sage/graphs/hyperbolicity.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index bf8a8a2c8b4..07d3b4703f4 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -208,7 +208,7 @@ def _my_subgraph(G, vertices, relabel=False, return_map=False): etc.). If ``relabel`` is ``True``, the vertices of the new graph are relabeled - with integers in the range '0\cdots |vertices|-1'. The relabeling map is + with integers in the range '0\cdots \mid vertices \mid -1'. The relabeling map is returned if ``return_map`` is also ``True``. TESTS: From 30476c7ac05393358aa0eb7a3304a8eb8ac76e00 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 8 Oct 2015 15:54:10 -0400 Subject: [PATCH 359/421] Trac #19368: Rename LL() to lyapunov_like_basis(). This name is better for two reasons. First, it gives the user some idea what the method does. Second, it indicates that you get a basis (rather than a vector space object) back. --- src/sage/geometry/cone.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 10c815ab6d0..fd599013842 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -4442,7 +4442,7 @@ def discrete_complementarity_set(self): for s in self.dual() if x.inner_product(s) == 0 ) - def LL(self): + def lyapunov_like_basis(self): r""" Compute a basis of Lyapunov-like transformations on this cone. @@ -4472,25 +4472,25 @@ def LL(self): sage: L = ToricLattice(0) sage: K = Cone([], lattice=L) - sage: K.LL() + sage: K.lyapunov_like_basis() [] The Lyapunov-like transformations on the nonnegative orthant are diagonal matrices:: sage: K = Cone([(1,)]) - sage: K.LL() + sage: K.lyapunov_like_basis() [[1]] sage: K = Cone([(1,0),(0,1)]) - sage: K.LL() + sage: K.lyapunov_like_basis() [ [1 0] [0 0] [0 0], [0 1] ] sage: K = Cone([(1,0,0),(0,1,0),(0,0,1)]) - sage: K.LL() + sage: K.lyapunov_like_basis() [ [1 0 0] [0 0 0] [0 0 0] [0 0 0] [0 1 0] [0 0 0] @@ -4501,7 +4501,7 @@ def LL(self): defined by the one- and infinity-norms [Rudolf]_:: sage: l31 = Cone([(1,0,1), (0,-1,1), (-1,0,1), (0,1,1)]) - sage: l31.LL() + sage: l31.lyapunov_like_basis() [ [1 0 0] [0 1 0] @@ -4509,7 +4509,7 @@ def LL(self): ] sage: l3infty = Cone([(0,1,1), (1,0,1), (0,-1,1), (-1,0,1)]) - sage: l3infty.LL() + sage: l3infty.lyapunov_like_basis() [ [1 0 0] [0 1 0] @@ -4522,7 +4522,7 @@ def LL(self): sage: K.is_full_space() True sage: M = MatrixSpace(K.lattice().base_field(), K.lattice_dim()) - sage: M.basis() == K.LL() + sage: M.basis() == K.lyapunov_like_basis() True TESTS: @@ -4533,8 +4533,9 @@ def LL(self): sage: set_random_seed() sage: K = random_cone(max_ambient_dim=8) sage: dcs = K.discrete_complementarity_set() + sage: LL = K.lyapunov_like_basis() sage: ips = [ (L*x).inner_product(s) for (x,s) in dcs - ....: for L in K.LL() ] + ....: for L in LL ] sage: sum(map(abs, ips)) 0 @@ -4544,10 +4545,11 @@ def LL(self): sage: set_random_seed() sage: K = random_cone(max_ambient_dim=8) - sage: LL2 = [ L.transpose() for L in K.dual().LL() ] + sage: LL1 = K.lyapunov_like_basis() + sage: LL2 = [L.transpose() for L in K.dual().lyapunov_like_basis()] sage: V = VectorSpace(K.lattice().base_field(), K.lattice_dim()^2) - sage: LL1_vecs = [ V(m.list()) for m in K.LL() ] - sage: LL2_vecs = [ V(m.list()) for m in LL2 ] + sage: LL1_vecs = [ V(m.list()) for m in LL1 ] + sage: LL2_vecs = [ V(m.list()) for m in LL2 ] sage: V.span(LL1_vecs) == V.span(LL2_vecs) True @@ -4556,10 +4558,11 @@ def LL(self): sage: set_random_seed() sage: K = random_cone(max_ambient_dim=4) + sage: LL = K.lyapunov_like_basis() sage: W = VectorSpace(K.lattice().base_field(), K.lattice_dim()**2) - sage: LL_W = W.span([ W(m.list()) for m in K.LL() ]) - sage: brackets = [ W((L1*L2 - L2*L1).list()) for L1 in K.LL() - ....: for L2 in K.LL() ] + sage: LL_W = W.span([ W(m.list()) for m in LL ]) + sage: brackets = [ W((L1*L2 - L2*L1).list()) for L1 in LL + ....: for L2 in LL ] sage: all([ b in LL_W for b in brackets ]) True """ From 385d49edbeeea381052690a172445b8b9df1ec88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 9 Oct 2015 10:11:41 +0200 Subject: [PATCH 360/421] hidden doc in /homology --- src/sage/interfaces/chomp.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/interfaces/chomp.py b/src/sage/interfaces/chomp.py index 24fd9087940..d4d45b60d9b 100644 --- a/src/sage/interfaces/chomp.py +++ b/src/sage/interfaces/chomp.py @@ -126,10 +126,10 @@ def __call__(self, program, complex, subcomplex=None, **kwds): EXAMPLES:: - sage: from sage.interfaces.chomp import CHomP - sage: T = cubical_complexes.Torus() - sage: CHomP()('homcubes', T) # indirect doctest, optional - CHomP - {0: 0, 1: Z x Z, 2: Z} + sage: from sage.interfaces.chomp import CHomP + sage: T = cubical_complexes.Torus() + sage: CHomP()('homcubes', T) # indirect doctest, optional - CHomP + {0: 0, 1: Z x Z, 2: Z} """ from sage.misc.temporary_file import tmp_filename from sage.homology.all import CubicalComplex, cubical_complexes From 89c82f758936fee2ebe81e7b8b50e9da79f825d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 9 Oct 2015 10:17:03 +0200 Subject: [PATCH 361/421] correct hidden doc in /tensor --- src/sage/tensor/coordinate_patch.py | 12 ++++-------- src/sage/tensor/differential_form_element.py | 6 +----- src/sage/tensor/differential_forms.py | 4 ++-- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/sage/tensor/coordinate_patch.py b/src/sage/tensor/coordinate_patch.py index 88df32a42d7..908e1f9d972 100644 --- a/src/sage/tensor/coordinate_patch.py +++ b/src/sage/tensor/coordinate_patch.py @@ -81,20 +81,18 @@ def __init__(self, coordinates, metric = None): INPUT: - ``coordinates`` -- a set of symbolic variables that serve - as coordinates on this space. + as coordinates on this space. - - ``metric`` (default: None) -- a metric tensor on this - coordinate patch. Providing anything other than ``None`` - is currently not defined. + - ``metric`` (default: ``None``) -- a metric tensor on this + coordinate patch. Providing anything other than ``None`` + is currently not defined. EXAMPLES:: sage: x, y, z = var('x, y, z') sage: S = CoordinatePatch((x, y, z)); S Open subset of R^3 with coordinates x, y, z - """ - from sage.symbolic.ring import is_SymbolicVariable if not all(is_SymbolicVariable(c) for c in coordinates): @@ -107,8 +105,6 @@ def __init__(self, coordinates, metric = None): if metric is not None: raise NotImplementedError("Metric geometry not supported yet.") - - def __eq__(self, other): """ Return equality if and only if other has the same coordinates diff --git a/src/sage/tensor/differential_form_element.py b/src/sage/tensor/differential_form_element.py index e109d133ec4..de7e36523e3 100644 --- a/src/sage/tensor/differential_form_element.py +++ b/src/sage/tensor/differential_form_element.py @@ -396,7 +396,6 @@ def __init__(self, parent, degree, fun = None): if degree == 0 and fun is not None: self.__setitem__([], fun) - def __getitem__(self, subscript): r""" Return a given component of the differential form. @@ -404,8 +403,7 @@ def __getitem__(self, subscript): INPUT: - ``subscript``: subscript of the component. Must be an integer - or a list of integers. - + or a list of integers. EXAMPLES:: @@ -426,7 +424,6 @@ def __getitem__(self, subscript): sage: df[2] 0 """ - if isinstance(subscript, (Integer, int)): subscript = (subscript, ) else: @@ -447,7 +444,6 @@ def __getitem__(self, subscript): else: return 0 - def __setitem__(self, subscript, fun): r""" Modify a given component of the differential form. diff --git a/src/sage/tensor/differential_forms.py b/src/sage/tensor/differential_forms.py index 6f367db317b..d4d2332b19e 100644 --- a/src/sage/tensor/differential_forms.py +++ b/src/sage/tensor/differential_forms.py @@ -76,11 +76,13 @@ class DifferentialForms(Algebra): def __init__(self, coordinate_patch = None): """ Construct the algebra of differential forms on a given coordinate patch. + See ``DifferentialForms`` for details. INPUT: - ``coordinate_patch`` -- Coordinate patch where the algebra lives. + If no coordinate patch is given, a default coordinate patch with coordinates (x, y, z) is used. @@ -91,9 +93,7 @@ def __init__(self, coordinate_patch = None): Open subset of R^2 with coordinates p, q sage: F = DifferentialForms(U); F Algebra of differential forms in the variables p, q - """ - from sage.categories.graded_algebras_with_basis \ import GradedAlgebrasWithBasis from sage.structure.parent_gens import ParentWithGens From 55208bb52064f22c0f91ef07d68610f1d997eb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 9 Oct 2015 10:25:39 +0200 Subject: [PATCH 362/421] fixing hidden doc in matroids --- src/sage/matroids/lean_matrix.pyx | 6 +++--- src/sage/matroids/matroid.pyx | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sage/matroids/lean_matrix.pyx b/src/sage/matroids/lean_matrix.pyx index 08f0db2ee3a..715888bc68e 100644 --- a/src/sage/matroids/lean_matrix.pyx +++ b/src/sage/matroids/lean_matrix.pyx @@ -1039,7 +1039,7 @@ cdef class BinaryMatrix(LeanMatrix): bitset_free(self._temp) def __repr__(self): - """ + r""" Print representation string EXAMPLES:: @@ -1666,7 +1666,7 @@ cdef class TernaryMatrix(LeanMatrix): bitset_free(self._u) def __repr__(self): - """ + r""" Print representation string EXAMPLES:: @@ -2241,7 +2241,7 @@ cdef class QuaternaryMatrix(LeanMatrix): bitset_free(self._u) def __repr__(self): - """ + r""" Print representation string EXAMPLES:: diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index bd705e8f433..77aa227347b 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -5575,9 +5575,10 @@ cdef class Matroid(SageObject): ``True`` if the matroid is 3-connected, ``False`` otherwise. INPUT: + - ``basis`` -- a basis of the matroid. - ``fund_cocircuits`` -- a iterable of some fundamental cocircuits with - respect to ``basis``. It must contain all separating fundamental cocircuits. + respect to ``basis``. It must contain all separating fundamental cocircuits. OUTPUT: @@ -5598,8 +5599,8 @@ cdef class Matroid(SageObject): .. NOTE:: - The function does not check its input at all. You may want to make - sure the matroid is both simple and cosimple. + The function does not check its input at all. You may want to make + sure the matroid is both simple and cosimple. """ # Step 1: base case if self.rank() <= 2: From 0fbeeee15c26273d1edcac312bbd49ddfc3f03a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 9 Oct 2015 10:33:25 +0200 Subject: [PATCH 363/421] fixing hidden doc in dynamics --- .../dynamics/interval_exchanges/template.py | 33 +++++---- src/sage/sandpiles/sandpile.py | 70 +++++++++---------- 2 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/sage/dynamics/interval_exchanges/template.py b/src/sage/dynamics/interval_exchanges/template.py index ded494ef6d0..57cb4ada51c 100644 --- a/src/sage/dynamics/interval_exchanges/template.py +++ b/src/sage/dynamics/interval_exchanges/template.py @@ -1981,7 +1981,7 @@ class RauzyDiagram(SageObject): r""" Template for Rauzy diagrams. - .. warning: + .. warning:: Internal class! Do not use directly! @@ -2600,27 +2600,30 @@ def __init__(self, p, top_bottom_inversion=False, symmetric=False): r""" - self._succ contains successors - self._pred contains predecessors + - ``self._succ`` contains successors + + - ``self._pred`` contains predecessors - self._element_class is the class of elements of self - self._element is an instance of this class (hence contains the alphabet, - the representation mode, ...). It is used to store data about property - of permutations and also as a fast iterator. + - ``self._element_class`` is the class of elements of ``self`` - INPUT: + - ``self._element`` is an instance of this class (hence + contains the alphabet, the representation mode, ...). It is + used to store data about property of permutations and also as + a fast iterator. + + INPUT: - - ``right_induction`` - boolean or 'top' or 'bottom': consider the - right induction + - ``right_induction`` - boolean or 'top' or 'bottom': consider the + right induction - - ``left_induction`` - boolean or 'top' or 'bottom': consider the - left induction + - ``left_induction`` - boolean or 'top' or 'bottom': consider the + left induction - - ``left_right_inversion`` - consider the left right inversion + - ``left_right_inversion`` - consider the left right inversion - - ``top_bottom_inversion`` - consider the top bottom inversion + - ``top_bottom_inversion`` - consider the top bottom inversion - - ``symmetric`` - consider the symmetric + - ``symmetric`` - consider the symmetric TESTS:: diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index c7410b04368..6d2d2653696 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -490,14 +490,14 @@ def __init__(self, g, sink=None): INPUT: - - ``g`` -- dict for directed multigraph with edges weighted by - nonnegative integers (see NOTE), a Graph or DiGraph. + - ``g`` -- dict for directed multigraph with edges weighted by + nonnegative integers (see NOTE), a Graph or DiGraph. - - ``sink`` -- (optional) A sink vertex. Any outgoing edges from the - designated sink are ignored for the purposes of stabilization. It is - assumed that every vertex has a directed path into the sink. If the - ``sink`` argument is omitted, the first vertex in the list of the - Sandpile's vertices is set as the sink. + - ``sink`` -- (optional) A sink vertex. Any outgoing edges from the + designated sink are ignored for the purposes of stabilization. It is + assumed that every vertex has a directed path into the sink. If the + ``sink`` argument is omitted, the first vertex in the list of the + Sandpile's vertices is set as the sink. OUTPUT: @@ -517,41 +517,37 @@ def __init__(self, g, sink=None): sage: G = Sandpile(g,'d') Here is a square with unweighted edges. In this example, the graph is - also undirected. - - :: + also undirected. :: sage: g = {0:[1,2], 1:[0,3], 2:[0,3], 3:[1,2]} sage: G = Sandpile(g,3) In the following example, multiple edges and loops in the dictionary - become edge weights in the Sandpile. - - :: - - sage: s = Sandpile({0:[1,2,3], 1:[0,1,2,2,2], 2:[1,1,0,2,2,2,2]}) - sage: s.laplacian() - [ 3 -1 -1 -1] - [-1 4 -3 0] - [-1 -2 3 0] - [ 0 0 0 0] - sage: s.dict() - {0: {1: 1, 2: 1, 3: 1}, 1: {0: 1, 1: 1, 2: 3}, 2: {0: 1, 1: 2, 2: 4}} - - Sandpiles can be created from Graphs and DiGraphs. - - sage: g = DiGraph({0:{1:2,2:4}, 1:{1:3,2:1}, 2:{1:7}}, weighted=True) - sage: s = Sandpile(g) - sage: s.dict() - {0: {1: 2, 2: 4}, 1: {0: 0, 1: 3, 2: 1}, 2: {0: 0, 1: 7}} - sage: s.sink() - 0 - sage: s = sandpiles.Cycle(4) - sage: s.laplacian() - [ 2 -1 0 -1] - [-1 2 -1 0] - [ 0 -1 2 -1] - [-1 0 -1 2] + become edge weights in the Sandpile. :: + + sage: s = Sandpile({0:[1,2,3], 1:[0,1,2,2,2], 2:[1,1,0,2,2,2,2]}) + sage: s.laplacian() + [ 3 -1 -1 -1] + [-1 4 -3 0] + [-1 -2 3 0] + [ 0 0 0 0] + sage: s.dict() + {0: {1: 1, 2: 1, 3: 1}, 1: {0: 1, 1: 1, 2: 3}, 2: {0: 1, 1: 2, 2: 4}} + + Sandpiles can be created from Graphs and DiGraphs. :: + + sage: g = DiGraph({0:{1:2,2:4}, 1:{1:3,2:1}, 2:{1:7}}, weighted=True) + sage: s = Sandpile(g) + sage: s.dict() + {0: {1: 2, 2: 4}, 1: {0: 0, 1: 3, 2: 1}, 2: {0: 0, 1: 7}} + sage: s.sink() + 0 + sage: s = sandpiles.Cycle(4) + sage: s.laplacian() + [ 2 -1 0 -1] + [-1 2 -1 0] + [ 0 -1 2 -1] + [-1 0 -1 2] .. NOTE:: From 8bda4c2134f5e6460c6ce9bae5e0ba96679e54c3 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 9 Oct 2015 10:26:07 -0400 Subject: [PATCH 364/421] Trac #19368: Clarify trivial cone/space doctest for lyapunov_like_basis(). The "trivial cone" referred to in one of the lyapunov_like_basis() doctests was really the trivial cone in a trivial space. The trivial space is the important part, and that doctest has been made clearer. A separate test was added for the trivial cone in a non-trivial space. --- src/sage/geometry/cone.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index fd599013842..68fd8ba37cc 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -4468,7 +4468,25 @@ def lyapunov_like_basis(self): EXAMPLES: - The trivial cone has no Lyapunov-like transformations:: + Every transformation is Lyapunov-like on the trivial cone:: + + sage: K = Cone([(0,0)]) + sage: M = MatrixSpace(K.lattice().base_field(), K.lattice_dim()) + sage: M.basis() == K.lyapunov_like_basis() + True + + And by duality, every transformation is Lyapunov-like on the + ambient space:: + + sage: K = Cone([(1,0), (-1,0), (0,1), (0,-1)]) + sage: K.is_full_space() + True + sage: M = MatrixSpace(K.lattice().base_field(), K.lattice_dim()) + sage: M.basis() == K.lyapunov_like_basis() + True + + However, in a trivial space, there are no non-trivial linear maps, + so there can be no Lyapunov-like basis:: sage: L = ToricLattice(0) sage: K = Cone([], lattice=L) @@ -4516,15 +4534,6 @@ def lyapunov_like_basis(self): [0 0 1] ] - Every transformation is Lyapunov-like on the ambient space:: - - sage: K = Cone([(1,0), (-1,0), (0,1), (0,-1)]) - sage: K.is_full_space() - True - sage: M = MatrixSpace(K.lattice().base_field(), K.lattice_dim()) - sage: M.basis() == K.lyapunov_like_basis() - True - TESTS: The vectors `L(x)` and `s` are orthogonal for every pair `(x,s)` From 5d04759b9631a3cb6b66920a82b09d72187dea3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 9 Oct 2015 18:22:54 +0200 Subject: [PATCH 365/421] trac #19379 reviewer's comments, done --- src/sage/dynamics/interval_exchanges/template.py | 16 ++++++++-------- src/sage/matroids/lean_matrix.pyx | 10 +++++----- src/sage/tensor/differential_form_element.py | 7 ++++--- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/sage/dynamics/interval_exchanges/template.py b/src/sage/dynamics/interval_exchanges/template.py index 57cb4ada51c..8d9ee2fd7f9 100644 --- a/src/sage/dynamics/interval_exchanges/template.py +++ b/src/sage/dynamics/interval_exchanges/template.py @@ -251,7 +251,7 @@ class Permutation(SageObject): r""" Template for all permutations. - .. warning:: + .. WARNING:: Internal class! Do not use directly! @@ -790,7 +790,7 @@ class PermutationIET(Permutation): """ Template for permutation from Interval Exchange Transformation. - .. warning:: + .. WARNING:: Internal class! Do not use directly! @@ -1582,7 +1582,7 @@ class PermutationLI(Permutation): r""" Template for quadratic permutation. - .. warning:: + .. WARNING:: Internal class! Do not use directly! @@ -1857,7 +1857,7 @@ class FlippedPermutation(Permutation): r""" Template for flipped generalized permutations. - .. warning:: + .. WARNING:: Internal class! Do not use directly! @@ -1916,7 +1916,7 @@ class FlippedPermutationIET(FlippedPermutation, PermutationIET): r""" Template for flipped Abelian permutations. - .. warning:: + .. WARNING:: Internal class! Do not use directly! @@ -1945,7 +1945,7 @@ class FlippedPermutationLI(FlippedPermutation, PermutationLI): r""" Template for flipped quadratic permutations. - .. warning:: + .. WARNING:: Internal class! Do not use directly! @@ -1981,7 +1981,7 @@ class RauzyDiagram(SageObject): r""" Template for Rauzy diagrams. - .. warning:: + .. WARNING:: Internal class! Do not use directly! @@ -3532,7 +3532,7 @@ class FlippedRauzyDiagram(RauzyDiagram): r""" Template for flipped Rauzy diagrams. - .. warning: + .. WARNING:: Internal class! Do not use directly! diff --git a/src/sage/matroids/lean_matrix.pyx b/src/sage/matroids/lean_matrix.pyx index 715888bc68e..2c920b21519 100644 --- a/src/sage/matroids/lean_matrix.pyx +++ b/src/sage/matroids/lean_matrix.pyx @@ -741,7 +741,7 @@ cdef class GenericMatrix(LeanMatrix): def __repr__(self): """ - Print representation. + Return representation. EXAMPLES:: @@ -1040,7 +1040,7 @@ cdef class BinaryMatrix(LeanMatrix): def __repr__(self): r""" - Print representation string + Return representation string EXAMPLES:: @@ -1667,7 +1667,7 @@ cdef class TernaryMatrix(LeanMatrix): def __repr__(self): r""" - Print representation string + Return representation string EXAMPLES:: @@ -2242,7 +2242,7 @@ cdef class QuaternaryMatrix(LeanMatrix): def __repr__(self): r""" - Print representation string + Return representation string EXAMPLES:: @@ -2829,7 +2829,7 @@ cdef class IntegerMatrix(LeanMatrix): def __repr__(self): """ - Print representation. + Return representation. EXAMPLES:: diff --git a/src/sage/tensor/differential_form_element.py b/src/sage/tensor/differential_form_element.py index de7e36523e3..87abcc47173 100644 --- a/src/sage/tensor/differential_form_element.py +++ b/src/sage/tensor/differential_form_element.py @@ -44,7 +44,7 @@ def sort_subscript(subscript): INPUT: - ``subscript`` -- a subscript, i.e. a range of not necessarily - distinct integers + distinct integers OUTPUT: @@ -402,7 +402,7 @@ def __getitem__(self, subscript): INPUT: - - ``subscript``: subscript of the component. Must be an integer + - ``subscript`` -- subscript of the component. Must be an integer or a list of integers. EXAMPLES:: @@ -450,7 +450,8 @@ def __setitem__(self, subscript, fun): INPUT: - - ``subscript``: subscript of the component. Must be an integer or a list of integers. + - ``subscript`` -- subscript of the component. Must be an integer + or a list of integers. EXAMPLES:: From 0cded598ebe90e7aaa73d595069c9d7b8f0f1770 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 09:33:29 +0200 Subject: [PATCH 366/421] trac #19381: A new module --- src/doc/en/reference/graphs/index.rst | 1 + src/sage/graphs/graph_input.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/sage/graphs/graph_input.py diff --git a/src/doc/en/reference/graphs/index.rst b/src/doc/en/reference/graphs/index.rst index f71db685e66..a27c03f556a 100644 --- a/src/doc/en/reference/graphs/index.rst +++ b/src/doc/en/reference/graphs/index.rst @@ -91,6 +91,7 @@ Libraries of algorithms sage/graphs/graph_latex sage/graphs/graph_editor sage/graphs/graph_list + sage/graphs/graph_input sage/graphs/hyperbolicity sage/graphs/tutte_polynomial sage/graphs/generic_graph_pyx diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py new file mode 100644 index 00000000000..a487fcb87f4 --- /dev/null +++ b/src/sage/graphs/graph_input.py @@ -0,0 +1,17 @@ +r""" +Functions for reading/building graphs/digraphs. + +This module gathers functions needed to build a graph from any other data. + + Note that because they are called by the constructors of :class:`Graph` and +:class:`DiGraph`, most of these functions modify a graph inplace. + +Functions +--------- + +""" + + +from sage.misc.rest_index_of_methods import gen_rest_table_index +import sys +__doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__])) From 5da21ca8331c9332be9e491dc06854c24e1e1cf7 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 09:58:17 +0200 Subject: [PATCH 367/421] trac #19381: graph6 --- src/sage/graphs/graph.py | 22 ++--------------- src/sage/graphs/graph_input.py | 45 +++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 56282c0c86e..f8bf86a25e2 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1267,26 +1267,8 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if weighted is None: weighted = False self.allow_loops(loops if loops else False, check=False) self.allow_multiple_edges(multiedges if multiedges else False, check=False) - if not isinstance(data, str): - raise ValueError('If input format is graph6, then data must be a string.') - n = data.find('\n') - if n == -1: - n = len(data) - ss = data[:n] - n, s = generic_graph_pyx.length_and_string_from_graph6(ss) - m = generic_graph_pyx.binary_string_from_graph6(s, n) - expected = n*(n-1)/2 + (6 - n*(n-1)/2)%6 - if len(m) > expected: - raise RuntimeError("The string (%s) seems corrupt: for n = %d, the string is too long."%(ss,n)) - elif len(m) < expected: - raise RuntimeError("The string (%s) seems corrupt: for n = %d, the string is too short."%(ss,n)) - self.add_vertices(range(n)) - k = 0 - for i in xrange(n): - for j in xrange(i): - if m[k] == '1': - self._backend.add_edge(i, j, None, False) - k += 1 + from graph_input import from_graph6 + from_graph6(self, data) elif format == 'sparse6': if weighted is None: weighted = False diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index a487fcb87f4..a629706b966 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -3,14 +3,57 @@ This module gathers functions needed to build a graph from any other data. - Note that because they are called by the constructors of :class:`Graph` and +Note that because they are called by the constructors of :class:`Graph` and :class:`DiGraph`, most of these functions modify a graph inplace. +{INDEX_OF_FUNCTIONS} + Functions --------- """ +def from_graph6(G, g6_string): + r""" + Fill ``G`` with the data of a graph6 string. + + INPUT: + + - ``G`` -- a graph + + - ``g6_string`` -- a graph6 string + + EXAMPLE:: + + sage: from sage.graphs.graph_input import from_graph6 + sage: g = Graph() + sage: from_graph6(g, 'IheA@GUAo') + sage: g.is_isomorphic(graphs.PetersenGraph()) + True + + """ + from generic_graph_pyx import length_and_string_from_graph6, binary_string_from_graph6 + + if not isinstance(g6_string, str): + raise ValueError('If input format is graph6, then g6_string must be a string.') + n = g6_string.find('\n') + if n == -1: + n = len(g6_string) + ss = g6_string[:n] + n, s = length_and_string_from_graph6(ss) + m = binary_string_from_graph6(s, n) + expected = n*(n-1)/2 + (6 - n*(n-1)/2)%6 + if len(m) > expected: + raise RuntimeError("The string (%s) seems corrupt: for n = %d, the string is too long."%(ss,n)) + elif len(m) < expected: + raise RuntimeError("The string (%s) seems corrupt: for n = %d, the string is too short."%(ss,n)) + G.add_vertices(range(n)) + k = 0 + for i in xrange(n): + for j in xrange(i): + if m[k] == '1': + G._backend.add_edge(i, j, None, False) + k += 1 from sage.misc.rest_index_of_methods import gen_rest_table_index import sys From 7a33fad6c1d0c4d754c7c525d97a2865ba57b22f Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 10:05:42 +0200 Subject: [PATCH 368/421] trac #19381: sparse6 --- src/sage/graphs/graph.py | 35 ++--------------------- src/sage/graphs/graph_input.py | 52 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index f8bf86a25e2..3469517ab15 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1274,38 +1274,9 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if weighted is None: weighted = False self.allow_loops(False if loops is False else True, check=False) self.allow_multiple_edges(False if multiedges is False else True, check=False) - from math import ceil, floor - from sage.misc.functional import log - n = data.find('\n') - if n == -1: - n = len(data) - s = data[:n] - n, s = generic_graph_pyx.length_and_string_from_graph6(s[1:]) - if n == 0: - edges = [] - else: - k = int(ceil(log(n,2))) - ords = [ord(i) for i in s] - if any(o > 126 or o < 63 for o in ords): - raise RuntimeError("The string seems corrupt: valid characters are \n" + ''.join([chr(i) for i in xrange(63,127)])) - bits = ''.join([generic_graph_pyx.int_to_binary_string(o-63).zfill(6) for o in ords]) - b = [] - x = [] - for i in xrange(int(floor(len(bits)/(k+1)))): - b.append(int(bits[(k+1)*i:(k+1)*i+1],2)) - x.append(int(bits[(k+1)*i+1:(k+1)*i+k+1],2)) - v = 0 - edges = [] - for i in xrange(len(b)): - if b[i] == 1: - v += 1 - if x[i] > v: - v = x[i] - else: - if v < n: - edges.append((x[i],v)) - self.add_vertices(range(n)) - self.add_edges(edges) + from graph_input import from_sparse6 + from_sparse6(self, data) + elif format == 'adjacency_matrix': assert is_Matrix(data) # note: the adjacency matrix might be weighted and hence not diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index a629706b966..668e63008f0 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -55,6 +55,58 @@ def from_graph6(G, g6_string): G._backend.add_edge(i, j, None, False) k += 1 +def from_sparse6(G, g6_string): + r""" + Fill ``G`` with the data of a sparse6 string. + + INPUT: + + - ``G`` -- a graph + + - ``g6_string`` -- a sparse6 string + + EXAMPLE:: + + sage: from sage.graphs.graph_input import from_sparse6 + sage: g = Graph() + sage: from_sparse6(g, ':I`ES@obGkqegW~') + sage: g.is_isomorphic(graphs.PetersenGraph()) + True + """ + from generic_graph_pyx import length_and_string_from_graph6, int_to_binary_string + from math import ceil, floor + from sage.misc.functional import log + n = g6_string.find('\n') + if n == -1: + n = len(g6_string) + s = g6_string[:n] + n, s = length_and_string_from_graph6(s[1:]) + if n == 0: + edges = [] + else: + k = int(ceil(log(n,2))) + ords = [ord(i) for i in s] + if any(o > 126 or o < 63 for o in ords): + raise RuntimeError("The string seems corrupt: valid characters are \n" + ''.join([chr(i) for i in xrange(63,127)])) + bits = ''.join([int_to_binary_string(o-63).zfill(6) for o in ords]) + b = [] + x = [] + for i in xrange(int(floor(len(bits)/(k+1)))): + b.append(int(bits[(k+1)*i:(k+1)*i+1],2)) + x.append(int(bits[(k+1)*i+1:(k+1)*i+k+1],2)) + v = 0 + edges = [] + for i in xrange(len(b)): + if b[i] == 1: + v += 1 + if x[i] > v: + v = x[i] + else: + if v < n: + edges.append((x[i],v)) + G.add_vertices(range(n)) + G.add_edges(edges) + from sage.misc.rest_index_of_methods import gen_rest_table_index import sys __doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__])) From efb842b6b5d15780c892f45d76baaea97e684cc4 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 10:08:22 +0200 Subject: [PATCH 369/421] trac #19381: disclaimer --- src/sage/graphs/graph_input.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 668e63008f0..95691855ccd 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -3,6 +3,12 @@ This module gathers functions needed to build a graph from any other data. +.. NOTE:: + + This is a **internal** module of Sage. All features implemented here are + made available to end-users through the constructors of :class:`Graph` and + :class:`DiGraph`. + Note that because they are called by the constructors of :class:`Graph` and :class:`DiGraph`, most of these functions modify a graph inplace. From d9522bbcaca763cf89a3c81f47cc2126e38b35a1 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 10:20:40 +0200 Subject: [PATCH 370/421] trac #19381: seidel adjacency matrix --- src/sage/graphs/graph.py | 34 +++--------------------- src/sage/graphs/graph_input.py | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 3469517ab15..afe35c4eb1b 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1370,39 +1370,13 @@ def __init__(self, data=None, pos=None, loops=None, format=None, self.add_vertices(range(data.nrows())) self.add_edges(positions) elif format == 'seidel_adjacency_matrix': - assert is_Matrix(data) - if data.base_ring() != ZZ: - try: - data = data.change_ring(ZZ) - except TypeError: - raise ValueError("Graph's Seidel adjacency matrix must"+ - " have only 0,1,-1 integer entries") - - if data.is_sparse(): - entries = set(data[i,j] for i,j in data.nonzero_positions()) - else: - entries = set(data.list()) - - if any(e < -1 or e > 1 for e in entries): - raise ValueError("Graph's Seidel adjacency matrix must"+ - " have only 0,1,-1 integer entries") - if any(i==j for i,j in data.nonzero_positions()): - raise ValueError("Graph's Seidel adjacency matrix must"+ - " have 0s on the main diagonal") - if not data.is_symmetric(): - raise ValueError("Graph's Seidel adjacency matrix must"+ - " be symmetric") multiedges = False weighted = False loops = False - self.allow_loops(False) - self.allow_multiple_edges(False) - self.add_vertices(range(data.nrows())) - e = [] - for i,j in data.nonzero_positions(): - if i <= j and data[i,j] < 0: - e.append((i,j)) - self.add_edges(e) + G.allow_loops(False) + G.allow_multiple_edges(False) + from graph_input import from_seidel_adjacency_matrix + from_seidel_adjacency_matrix(self, data) elif format == 'Graph': if loops is None: loops = data.allows_loops() if multiedges is None: multiedges = data.allows_multiple_edges() diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 95691855ccd..5c283d33b2f 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -113,6 +113,54 @@ def from_sparse6(G, g6_string): G.add_vertices(range(n)) G.add_edges(edges) +def from_seidel_adjacency_matrix(G, g6_string): + r""" + Fill ``G`` with the data of a Seidel adjacency matrix. + + INPUT: + + - ``G`` -- a graph + + - ``M`` -- a Seidel adjacency matrix + + EXAMPLE:: + + sage: from sage.graphs.graph_input import from_seidel_adjacency_matrix + sage: g = Graph() + sage: from_seidel_adjacency_matrix(g, ':I`ES@obGkqegW~') + sage: g.is_isomorphic(graphs.PetersenGraph()) + True + """ + assert is_Matrix(data) + if data.base_ring() != ZZ: + try: + data = data.change_ring(ZZ) + except TypeError: + raise ValueError("Graph's Seidel adjacency matrix must"+ + " have only 0,1,-1 integer entries") + + if data.is_sparse(): + entries = set(data[i,j] for i,j in data.nonzero_positions()) + else: + entries = set(data.list()) + + if any(e < -1 or e > 1 for e in entries): + raise ValueError("Graph's Seidel adjacency matrix must"+ + " have only 0,1,-1 integer entries") + if any(i==j for i,j in data.nonzero_positions()): + raise ValueError("Graph's Seidel adjacency matrix must"+ + " have 0s on the main diagonal") + if not data.is_symmetric(): + raise ValueError("Graph's Seidel adjacency matrix must"+ + " be symmetric") + G.add_vertices(range(data.nrows())) + e = [] + for i,j in data.nonzero_positions(): + if i <= j and data[i,j] < 0: + e.append((i,j)) + G.add_edges(e) + + from sage.misc.rest_index_of_methods import gen_rest_table_index import sys __doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__])) From f0f8ee4cff832d33e43549d2f182d86e09d321dc Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 11:10:45 +0200 Subject: [PATCH 371/421] trac #19381: adjacency matrix --- src/sage/graphs/graph.py | 58 ++----------------------- src/sage/graphs/graph_input.py | 78 ++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 55 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index afe35c4eb1b..d79e4db04bc 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1278,61 +1278,9 @@ def __init__(self, data=None, pos=None, loops=None, format=None, from_sparse6(self, data) elif format == 'adjacency_matrix': - assert is_Matrix(data) - # note: the adjacency matrix might be weighted and hence not - # necessarily consists of integers - if not weighted and data.base_ring() != ZZ: - try: - data = data.change_ring(ZZ) - except TypeError: - if weighted is False: - raise ValueError("Non-weighted graph's"+ - " adjacency matrix must have only nonnegative"+ - " integer entries") - weighted = True - - if data.is_sparse(): - entries = set(data[i,j] for i,j in data.nonzero_positions()) - else: - entries = set(data.list()) - - if not weighted and any(e < 0 for e in entries): - if weighted is False: - raise ValueError("Non-weighted digraph's"+ - " adjacency matrix must have only nonnegative"+ - " integer entries") - weighted = True - if multiedges is None: multiedges = False - if weighted is None: - weighted = False - - if multiedges is None: - multiedges = ((not weighted) and any(e != 0 and e != 1 for e in entries)) + from graph_input import from_adjacency_matrix + from_adjacency_matrix(self, data, loops=loops, multiedges=multiedges, weighted=weighted) - if not loops and any(data[i,i] for i in xrange(data.nrows())): - if loops is False: - raise ValueError("Non-looped digraph's adjacency"+ - " matrix must have zeroes on the diagonal.") - loops = True - if loops is None: - loops = False - self.allow_loops(loops, check=False) - self.allow_multiple_edges(multiedges, check=False) - self.add_vertices(range(data.nrows())) - e = [] - if weighted: - for i,j in data.nonzero_positions(): - if i <= j: - e.append((i,j,data[i][j])) - elif multiedges: - for i,j in data.nonzero_positions(): - if i <= j: - e += [(i,j)]*int(data[i][j]) - else: - for i,j in data.nonzero_positions(): - if i <= j: - e.append((i,j)) - self.add_edges(e) elif format == 'incidence_matrix': assert is_Matrix(data) @@ -1579,7 +1527,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, raise ValueError("Unknown input format '{}'".format(format)) if weighted is None: weighted = False - self._weighted = weighted + self._weighted = getattr(self,'_weighted',weighted) self._pos = pos diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 5c283d33b2f..7665af3b419 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -160,6 +160,84 @@ def from_seidel_adjacency_matrix(G, g6_string): e.append((i,j)) G.add_edges(e) +def from_adjacency_matrix(G, M, loops=False, multiedges=False, weighted=False): + r""" + Fill ``G`` with the data of an adjacency matrix. + + INPUT: + + - ``G`` -- a graph + + - ``M`` -- an adjacency matrix + + - ``loops``, ``multiedges``, ``weighted`` (booleans) -- whether to consider + the graph as having loops, multiple edges, or weights. Set to ``False`` by default. + + EXAMPLE:: + + sage: from sage.graphs.graph_input import from_adjacency_matrix + sage: g = Graph() + sage: from_adjacency_matrix(g, graphs.PetersenGraph().adjacency_matrix()) + sage: g.is_isomorphic(graphs.PetersenGraph()) + True + """ + assert is_Matrix(M) + # note: the adjacency matrix might be weighted and hence not + # necessarily consists of integers + if not weighted and M.base_ring() != ZZ: + try: + M = M.change_ring(ZZ) + except TypeError: + if weighted is False: + raise ValueError("Non-weighted graph's"+ + " adjacency matrix must have only nonnegative"+ + " integer entries") + weighted = True + + if M.is_sparse(): + entries = set(M[i,j] for i,j in M.nonzero_positions()) + else: + entries = set(M.list()) + + if not weighted and any(e < 0 for e in entries): + if weighted is False: + raise ValueError("Non-weighted digraph's"+ + " adjacency matrix must have only nonnegative"+ + " integer entries") + weighted = True + if multiedges is None: multiedges = False + if weighted is None: + weighted = False + + if multiedges is None: + multiedges = ((not weighted) and any(e != 0 and e != 1 for e in entries)) + + if not loops and any(M[i,i] for i in xrange(M.nrows())): + if loops is False: + raise ValueError("Non-looped digraph's adjacency"+ + " matrix must have zeroes on the diagonal.") + loops = True + if loops is None: + loops = False + G.allow_loops(loops, check=False) + G.allow_multiple_edges(multiedges, check=False) + G.add_vertices(range(M.nrows())) + e = [] + if weighted: + for i,j in M.nonzero_positions(): + if i <= j: + e.append((i,j,M[i][j])) + elif multiedges: + for i,j in M.nonzero_positions(): + if i <= j: + e += [(i,j)]*int(M[i][j]) + else: + for i,j in M.nonzero_positions(): + if i <= j: + e.append((i,j)) + G.add_edges(e) + G._weighted = weighted + from sage.misc.rest_index_of_methods import gen_rest_table_index import sys From 7a51fde66641266ec6ac91bdb0b54d82a7f6fc19 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 11:15:00 +0200 Subject: [PATCH 372/421] trac #19381: Fix seidel adjacency --- src/sage/graphs/graph.py | 4 ++-- src/sage/graphs/graph_input.py | 31 ++++++++++++++++++------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index d79e4db04bc..214b02edc19 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1321,8 +1321,8 @@ def __init__(self, data=None, pos=None, loops=None, format=None, multiedges = False weighted = False loops = False - G.allow_loops(False) - G.allow_multiple_edges(False) + self.allow_loops(False) + self.allow_multiple_edges(False) from graph_input import from_seidel_adjacency_matrix from_seidel_adjacency_matrix(self, data) elif format == 'Graph': diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 7665af3b419..2b190335bfe 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -113,7 +113,7 @@ def from_sparse6(G, g6_string): G.add_vertices(range(n)) G.add_edges(edges) -def from_seidel_adjacency_matrix(G, g6_string): +def from_seidel_adjacency_matrix(G, M): r""" Fill ``G`` with the data of a Seidel adjacency matrix. @@ -127,36 +127,39 @@ def from_seidel_adjacency_matrix(G, g6_string): sage: from sage.graphs.graph_input import from_seidel_adjacency_matrix sage: g = Graph() - sage: from_seidel_adjacency_matrix(g, ':I`ES@obGkqegW~') + sage: from_seidel_adjacency_matrix(g, graphs.PetersenGraph().seidel_adjacency_matrix()) sage: g.is_isomorphic(graphs.PetersenGraph()) True """ - assert is_Matrix(data) - if data.base_ring() != ZZ: + from sage.matrix.matrix import is_Matrix + from sage.rings.integer_ring import ZZ + assert is_Matrix(M) + + if M.base_ring() != ZZ: try: - data = data.change_ring(ZZ) + M = M.change_ring(ZZ) except TypeError: raise ValueError("Graph's Seidel adjacency matrix must"+ " have only 0,1,-1 integer entries") - if data.is_sparse(): - entries = set(data[i,j] for i,j in data.nonzero_positions()) + if M.is_sparse(): + entries = set(M[i,j] for i,j in M.nonzero_positions()) else: - entries = set(data.list()) + entries = set(M.list()) if any(e < -1 or e > 1 for e in entries): raise ValueError("Graph's Seidel adjacency matrix must"+ " have only 0,1,-1 integer entries") - if any(i==j for i,j in data.nonzero_positions()): + if any(i==j for i,j in M.nonzero_positions()): raise ValueError("Graph's Seidel adjacency matrix must"+ " have 0s on the main diagonal") - if not data.is_symmetric(): + if not M.is_symmetric(): raise ValueError("Graph's Seidel adjacency matrix must"+ " be symmetric") - G.add_vertices(range(data.nrows())) + G.add_vertices(range(M.nrows())) e = [] - for i,j in data.nonzero_positions(): - if i <= j and data[i,j] < 0: + for i,j in M.nonzero_positions(): + if i <= j and M[i,j] < 0: e.append((i,j)) G.add_edges(e) @@ -181,6 +184,8 @@ def from_adjacency_matrix(G, M, loops=False, multiedges=False, weighted=False): sage: g.is_isomorphic(graphs.PetersenGraph()) True """ + from sage.matrix.matrix import is_Matrix + from sage.rings.integer_ring import ZZ assert is_Matrix(M) # note: the adjacency matrix might be weighted and hence not # necessarily consists of integers From 5abbd4b53f337acd89738db4a3a067c2b54618d5 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 12:07:09 +0200 Subject: [PATCH 373/421] trac #19381: Incidence matrix --- src/sage/graphs/graph.py | 51 ++++-------------------------- src/sage/graphs/graph_input.py | 57 ++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 45 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 214b02edc19..10a0730c476 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -898,9 +898,7 @@ class Graph(GenericGraph): sage: Graph(Matrix([[1],[1],[1]])) Traceback (most recent call last): ... - ValueError: Non-symmetric or non-square matrix assumed to be an - incidence matrix: There must be one or two nonzero entries per - column. Got entries [1, 1, 1] in column 0 + ValueError: There must be one or two nonzero entries per column in an incidence matrix. Got entries [1, 1, 1] in column 0 sage: Graph(Matrix([[1],[1],[0]])) Graph on 3 vertices @@ -918,9 +916,7 @@ class Graph(GenericGraph): sage: Graph(M) Traceback (most recent call last): ... - ValueError: Non-symmetric or non-square matrix assumed to be an - incidence matrix: There must be one or two nonzero entries per - column. Got entries [1, 1] in column 2 + ValueError: There must be one or two nonzero entries per column in an incidence matrix. Got entries [1, 1] in column 2 Check that :trac:`9714` is fixed:: @@ -1140,9 +1136,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, sage: Graph(matrix([[1,1],[1,1],[1,0]])) Traceback (most recent call last): ... - ValueError: Non-symmetric or non-square matrix assumed to be an - incidence matrix: There must be one or two nonzero entries per - column. Got entries [1, 1, 1] in column 0 + ValueError: There must be one or two nonzero entries per column in an incidence matrix. Got entries [1, 1, 1] in column 0 sage: Graph(matrix([[3,1,1],[0,1,1]])) Traceback (most recent call last): ... @@ -1150,7 +1144,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, to 2, but column 0 does not """ GenericGraph.__init__(self) - msg = '' + from sage.structure.element import is_Matrix if sparse is False: @@ -1201,7 +1195,6 @@ def __init__(self, data=None, pos=None, loops=None, format=None, format = 'adjacency_matrix' else: format = 'incidence_matrix' - msg += "Non-symmetric or non-square matrix assumed to be an incidence matrix: " if format is None and isinstance(data, Graph): format = 'Graph' from sage.graphs.all import DiGraph @@ -1282,41 +1275,9 @@ def __init__(self, data=None, pos=None, loops=None, format=None, from_adjacency_matrix(self, data, loops=loops, multiedges=multiedges, weighted=weighted) elif format == 'incidence_matrix': - assert is_Matrix(data) - - oriented = any(data[pos] < 0 for pos in data.nonzero_positions(copy=False)) - - positions = [] - for i in range(data.ncols()): - NZ = data.nonzero_positions_in_column(i) - if len(NZ) == 1: - if oriented: - raise ValueError("Column {} of the (oriented) incidence " - "matrix contains only one nonzero value".format(i)) - elif data[NZ[0],i] != 2: - raise ValueError("Each column of a non-oriented incidence " - "matrix must sum to 2, but column {} does not".format(i)) - if loops is None: - loops = True - positions.append((NZ[0],NZ[0])) - elif len(NZ) != 2 or \ - (oriented and not ((data[NZ[0],i] == +1 and data[NZ[1],i] == -1) or \ - (data[NZ[0],i] == -1 and data[NZ[1],i] == +1))) or \ - (not oriented and (data[NZ[0],i] != 1 or data[NZ[1],i] != 1)): - msg += "There must be one or two nonzero entries per column. " - msg += "Got entries {} in column {}".format([data[j,i] for j in NZ], i) - raise ValueError(msg) - else: - positions.append(tuple(NZ)) + from graph_input import from_incidence_matrix + from_incidence_matrix(self, data, loops=loops, multiedges=multiedges, weighted=weighted) - if weighted is None: weighted = False - if multiedges is None: - total = len(positions) - multiedges = (len(set(positions)) < total ) - self.allow_loops(False if loops is None else loops, check=False) - self.allow_multiple_edges(multiedges, check=False) - self.add_vertices(range(data.nrows())) - self.add_edges(positions) elif format == 'seidel_adjacency_matrix': multiedges = False weighted = False diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 2b190335bfe..22ed464d5f4 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -243,6 +243,63 @@ def from_adjacency_matrix(G, M, loops=False, multiedges=False, weighted=False): G.add_edges(e) G._weighted = weighted +def from_incidence_matrix(G, M, loops=False, multiedges=False, weighted=False): + r""" + Fill ``G`` with the data of an incidence matrix. + + INPUT: + + - ``G`` -- a graph + + - ``M`` -- an incidence matrix + + - ``loops``, ``multiedges``, ``weighted`` (booleans) -- whether to consider + the graph as having loops, multiple edges, or weights. Set to ``False`` by default. + + EXAMPLE:: + + sage: from sage.graphs.graph_input import from_incidence_matrix + sage: g = Graph() + sage: from_incidence_matrix(g, graphs.PetersenGraph().incidence_matrix()) + sage: g.is_isomorphic(graphs.PetersenGraph()) + True + """ + from sage.matrix.matrix import is_Matrix + assert is_Matrix(M) + + oriented = any(M[pos] < 0 for pos in M.nonzero_positions(copy=False)) + + positions = [] + for i in range(M.ncols()): + NZ = M.nonzero_positions_in_column(i) + if len(NZ) == 1: + if oriented: + raise ValueError("Column {} of the (oriented) incidence " + "matrix contains only one nonzero value".format(i)) + elif M[NZ[0],i] != 2: + raise ValueError("Each column of a non-oriented incidence " + "matrix must sum to 2, but column {} does not".format(i)) + if loops is None: + loops = True + positions.append((NZ[0],NZ[0])) + elif len(NZ) != 2 or \ + (oriented and not ((M[NZ[0],i] == +1 and M[NZ[1],i] == -1) or \ + (M[NZ[0],i] == -1 and M[NZ[1],i] == +1))) or \ + (not oriented and (M[NZ[0],i] != 1 or M[NZ[1],i] != 1)): + msg = "There must be one or two nonzero entries per column in an incidence matrix. " + msg += "Got entries {} in column {}".format([M[j,i] for j in NZ], i) + raise ValueError(msg) + else: + positions.append(tuple(NZ)) + + if weighted is None: G._weighted = False + if multiedges is None: + total = len(positions) + multiedges = (len(set(positions)) < total ) + G.allow_loops(False if loops is None else loops, check=False) + G.allow_multiple_edges(multiedges, check=False) + G.add_vertices(range(M.nrows())) + G.add_edges(positions) from sage.misc.rest_index_of_methods import gen_rest_table_index import sys From cf7f5159941c63f64d54912c8bc97f5ec56235ac Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 12:25:22 +0200 Subject: [PATCH 374/421] trac #19381: dict_of_dicts --- src/sage/graphs/graph.py | 53 ++----------------------- src/sage/graphs/graph_input.py | 72 ++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 50 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 10a0730c476..436efca493f 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1344,57 +1344,10 @@ def __init__(self, data=None, pos=None, loops=None, format=None, self.add_edges(e for e in combinations(verts,2) if f(*e)) self.add_edges((v,v) for v in verts if f(v,v)) elif format == 'dict_of_dicts': - # adjust for empty dicts instead of None in NetworkX default edge labels - if convert_empty_dict_labels_to_None is None: - convert_empty_dict_labels_to_None = (format == 'NX') + from graph_input import from_dict_of_dicts + from_dict_of_dicts(self, data, loops=loops, multiedges=multiedges, weighted=weighted, + convert_empty_dict_labels_to_None = False if convert_empty_dict_labels_to_None is None else convert_empty_dict_labels_to_None) - if not all(isinstance(data[u], dict) for u in data): - raise ValueError("Input dict must be a consistent format.") - - if not loops and any(u in neighb for u,neighb in data.iteritems()): - if loops is False: - u = next(u for u,neighb in data.iteritems() if u in neighb) - raise ValueError("The graph was built with loops=False but input data has a loop at {}.".format(u)) - loops = True - if loops is None: - loops = False - - if weighted is None: weighted = False - for u in data: - for v in data[u]: - if hash(u) > hash(v): - if v in data and u in data[v]: - if data[u][v] != data[v][u]: - raise ValueError("Dict does not agree on edge (%s,%s)"%(u,v)) - continue - if multiedges is not False and not isinstance(data[u][v], list): - if multiedges is None: multiedges = False - if multiedges: - raise ValueError("Dict of dicts for multigraph must be in the format {v : {u : list}}") - if multiedges is None and len(data) > 0: - multiedges = True - self.allow_loops(loops, check=False) - self.allow_multiple_edges(multiedges, check=False) - verts = set().union(data.keys(), *data.values()) - self.add_vertices(verts) - if convert_empty_dict_labels_to_None: - for u in data: - for v in data[u]: - if hash(u) <= hash(v) or v not in data or u not in data[v]: - if multiedges: - for l in data[u][v]: - self._backend.add_edge(u,v,l,False) - else: - self._backend.add_edge(u,v,data[u][v] if data[u][v] != {} else None,False) - else: - for u in data: - for v in data[u]: - if hash(u) <= hash(v) or v not in data or u not in data[v]: - if multiedges: - for l in data[u][v]: - self._backend.add_edge(u,v,l,False) - else: - self._backend.add_edge(u,v,data[u][v],False) elif format == 'dict_of_lists': if not all(isinstance(data[u], list) for u in data): raise ValueError("Input dict must be a consistent format.") diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 22ed464d5f4..9a35a2812fd 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -301,6 +301,78 @@ def from_incidence_matrix(G, M, loops=False, multiedges=False, weighted=False): G.add_vertices(range(M.nrows())) G.add_edges(positions) +def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, convert_empty_dict_labels_to_None=False): + r""" + Fill ``G`` with the data of a dictionary of dictionaries. + + INPUT: + + - ``G`` -- a graph + + - ``M`` -- a dictionary of dictionaries. + + - ``loops``, ``multiedges``, ``weighted`` (booleans) -- whether to consider + the graph as having loops, multiple edges, or weights. Set to ``False`` by default. + + - ``convert_empty_dict_labels_to_None`` (boolean) -- whether to adjust for + empty dicts instead of None in NetworkX default edge labels. + + EXAMPLE:: + + sage: from sage.graphs.graph_input import from_dict_of_dicts + sage: g = Graph() + sage: from_dict_of_dicts(g, graphs.PetersenGraph().to_dictionary()) + sage: g.is_isomorphic(graphs.PetersenGraph()) + True + """ + if not all(isinstance(M[u], dict) for u in M): + raise ValueError("Input dict must be a consistent format.") + + if not loops and any(u in neighb for u,neighb in M.iteritems()): + if loops is False: + u = next(u for u,neighb in M.iteritems() if u in neighb) + raise ValueError("The graph was built with loops=False but input M has a loop at {}.".format(u)) + loops = True + if loops is None: + loops = False + + if weighted is None: G._weighted = False + for u in M: + for v in M[u]: + if hash(u) > hash(v): + if v in M and u in M[v]: + if M[u][v] != M[v][u]: + raise ValueError("Dict does not agree on edge (%s,%s)"%(u,v)) + continue + if multiedges is not False and not isinstance(M[u][v], list): + if multiedges is None: multiedges = False + if multiedges: + raise ValueError("Dict of dicts for multigraph must be in the format {v : {u : list}}") + if multiedges is None and len(M) > 0: + multiedges = True + G.allow_loops(loops, check=False) + G.allow_multiple_edges(multiedges, check=False) + verts = set().union(M.keys(), *M.values()) + G.add_vertices(verts) + if convert_empty_dict_labels_to_None: + for u in M: + for v in M[u]: + if hash(u) <= hash(v) or v not in M or u not in M[v]: + if multiedges: + for l in M[u][v]: + G._backend.add_edge(u,v,l,False) + else: + G._backend.add_edge(u,v,M[u][v] if M[u][v] != {} else None,False) + else: + for u in M: + for v in M[u]: + if hash(u) <= hash(v) or v not in M or u not in M[v]: + if multiedges: + for l in M[u][v]: + G._backend.add_edge(u,v,l,False) + else: + G._backend.add_edge(u,v,M[u][v],False) + from sage.misc.rest_index_of_methods import gen_rest_table_index import sys __doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__])) From f9fc7a89220dced27599cec5b903c9feb26d0459 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 12:37:42 +0200 Subject: [PATCH 375/421] trac #19381: dict of lists --- src/sage/graphs/graph.py | 35 ++------------------- src/sage/graphs/graph_input.py | 56 +++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 436efca493f..b19a1104f47 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1349,38 +1349,9 @@ def __init__(self, data=None, pos=None, loops=None, format=None, convert_empty_dict_labels_to_None = False if convert_empty_dict_labels_to_None is None else convert_empty_dict_labels_to_None) elif format == 'dict_of_lists': - if not all(isinstance(data[u], list) for u in data): - raise ValueError("Input dict must be a consistent format.") - - verts = set().union(data.keys(),*data.values()) - if loops is None or loops is False: - for u in data: - if u in data[u]: - if loops is None: - loops = True - elif loops is False: - u = next(u for u,neighb in data.iteritems() if u in neighb) - raise ValueError("The graph was built with loops=False but input data has a loop at {}.".format(u)) - break - if loops is None: - loops = False - if weighted is None: weighted = False - for u in data: - if len(set(data[u])) != len(data[u]): - if multiedges is False: - v = next((v for v in data[u] if data[u].count(v) > 1)) - raise ValueError("Non-multigraph got several edges (%s,%s)"%(u,v)) - if multiedges is None: - multiedges = True - if multiedges is None: multiedges = False - self.allow_loops(loops, check=False) - self.allow_multiple_edges(multiedges, check=False) - self.add_vertices(verts) - for u in data: - for v in data[u]: - if (multiedges or hash(u) <= hash(v) or - v not in data or u not in data[v]): - self._backend.add_edge(u,v,None,False) + from graph_input import from_dict_of_lists + from_dict_of_lists(self, data, loops=loops, multiedges=multiedges, weighted=weighted) + elif format == 'int': self.allow_loops(loops if loops else False, check=False) self.allow_multiple_edges(multiedges if multiedges else False, check=False) diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 9a35a2812fd..94e58f73be8 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -321,7 +321,7 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv sage: from sage.graphs.graph_input import from_dict_of_dicts sage: g = Graph() - sage: from_dict_of_dicts(g, graphs.PetersenGraph().to_dictionary()) + sage: from_dict_of_dicts(g, graphs.PetersenGraph().to_dictionary(edge_labels=True)) sage: g.is_isomorphic(graphs.PetersenGraph()) True """ @@ -373,6 +373,60 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv else: G._backend.add_edge(u,v,M[u][v],False) +def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): + r""" + Fill ``G`` with the data of a dictionary of lists. + + INPUT: + + - ``G`` -- a graph + + - ``D`` -- a dictionary of lists. + + - ``loops``, ``multiedges``, ``weighted`` (booleans) -- whether to consider + the graph as having loops, multiple edges, or weights. Set to ``False`` by default. + + EXAMPLE:: + + sage: from sage.graphs.graph_input import from_dict_of_lists + sage: g = Graph() + sage: from_dict_of_lists(g, graphs.PetersenGraph().to_dictionary()) + sage: g.is_isomorphic(graphs.PetersenGraph()) + True + """ + if not all(isinstance(D[u], list) for u in D): + raise ValueError("Input dict must be a consistent format.") + + verts = set().union(D.keys(),*D.values()) + if loops is None or loops is False: + for u in D: + if u in D[u]: + if loops is None: + loops = True + elif loops is False: + u = next(u for u,neighb in D.iteritems() if u in neighb) + raise ValueError("The graph was built with loops=False but input D has a loop at {}.".format(u)) + break + if loops is None: + loops = False + if weighted is None: G._weighted = False + for u in D: + if len(set(D[u])) != len(D[u]): + if multiedges is False: + v = next((v for v in D[u] if D[u].count(v) > 1)) + raise ValueError("Non-multigraph got several edges (%s,%s)"%(u,v)) + if multiedges is None: + multiedges = True + if multiedges is None: multiedges = False + G.allow_loops(loops, check=False) + G.allow_multiple_edges(multiedges, check=False) + G.add_vertices(verts) + for u in D: + for v in D[u]: + if (multiedges or hash(u) <= hash(v) or + v not in D or u not in D[v]): + G._backend.add_edge(u,v,None,False) + from sage.misc.rest_index_of_methods import gen_rest_table_index import sys __doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__])) From dd28d937cb552e4f97e764ce0e9a02c5e6073a31 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 12:46:24 +0200 Subject: [PATCH 376/421] trac #19381: dig6 --- src/sage/graphs/digraph.py | 25 ++++----------------- src/sage/graphs/graph_input.py | 41 +++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 3abc113977f..e4c6c2e64e4 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -676,29 +676,12 @@ def __init__(self, data=None, pos=None, loops=None, format=None, # At this point, format has been set. We build the graph if format == 'dig6': - if weighted is None: weighted = False - if not isinstance(data, str): - raise ValueError('If input format is dig6, then data must be a string.') - n = data.find('\n') - if n == -1: - n = len(data) - ss = data[:n] - n, s = generic_graph_pyx.length_and_string_from_graph6(ss) - m = generic_graph_pyx.binary_string_from_dig6(s, n) - expected = n**2 - if len(m) > expected: - raise RuntimeError("The string (%s) seems corrupt: for n = %d, the string is too long."%(ss,n)) - elif len(m) < expected: - raise RuntimeError("The string (%s) seems corrupt: for n = %d, the string is too short."%(ss,n)) + if weighted is None: self._weighted = False self.allow_loops(True if loops else False,check=False) self.allow_multiple_edges(True if multiedges else False,check=False) - self.add_vertices(range(n)) - k = 0 - for i in xrange(n): - for j in xrange(n): - if m[k] == '1': - self._backend.add_edge(i, j, None, True) - k += 1 + from graph_input import from_dig6 + from_dig6(self, data) + elif format == 'adjacency_matrix': assert is_Matrix(data) # note: the adjacency matrix might be weighted and hence not diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 94e58f73be8..f2071a4b282 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -36,7 +36,6 @@ def from_graph6(G, g6_string): sage: from_graph6(g, 'IheA@GUAo') sage: g.is_isomorphic(graphs.PetersenGraph()) True - """ from generic_graph_pyx import length_and_string_from_graph6, binary_string_from_graph6 @@ -113,6 +112,46 @@ def from_sparse6(G, g6_string): G.add_vertices(range(n)) G.add_edges(edges) +def from_dig6(G, dig6_string): + r""" + Fill ``G`` with the data of a dig6 string. + + INPUT: + + - ``G`` -- a graph + + - ``dig6_string`` -- a dig6 string + + EXAMPLE:: + + sage: from sage.graphs.graph_input import from_dig6 + sage: g = DiGraph() + sage: from_dig6(g, digraphs.Circuit(10).dig6_string()) + sage: g.is_isomorphic(digraphs.Circuit(10)) + True + """ + from generic_graph_pyx import length_and_string_from_graph6, binary_string_from_dig6 + if not isinstance(dig6_string, str): + raise ValueError('If input format is dig6, then dig6_string must be a string.') + n = dig6_string.find('\n') + if n == -1: + n = len(dig6_string) + ss = dig6_string[:n] + n, s = length_and_string_from_graph6(ss) + m = binary_string_from_dig6(s, n) + expected = n**2 + if len(m) > expected: + raise RuntimeError("The string (%s) seems corrupt: for n = %d, the string is too long."%(ss,n)) + elif len(m) < expected: + raise RuntimeError("The string (%s) seems corrupt: for n = %d, the string is too short."%(ss,n)) + G.add_vertices(range(n)) + k = 0 + for i in xrange(n): + for j in xrange(n): + if m[k] == '1': + G._backend.add_edge(i, j, None, True) + k += 1 + def from_seidel_adjacency_matrix(G, M): r""" Fill ``G`` with the data of a Seidel adjacency matrix. From 9bdf527fd59509647ccefd86cbd87bb2e92071b4 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 16:29:49 +0200 Subject: [PATCH 377/421] trac #19381: Typo --- src/sage/graphs/graph_input.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index f2071a4b282..1cec738f1c7 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -5,7 +5,7 @@ .. NOTE:: - This is a **internal** module of Sage. All features implemented here are + This is an **internal** module of Sage. All features implemented here are made available to end-users through the constructors of :class:`Graph` and :class:`DiGraph`. From 14b672decf9e60cf6a00593e15d4dcd0af5c4df2 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Oct 2015 20:15:04 +0200 Subject: [PATCH 378/421] trac #19061: Split a line of doc --- src/sage/graphs/graph.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index ef59285c82d..61b2ede9b58 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -3204,13 +3204,12 @@ def degree_constrained_subgraph(self, bounds=None, solver=None, verbose=0): @doc_index("Connectivity, orientations, trees") def strong_orientation(self): r""" - Returns a strongly connected orientation of the current graph. See - also the :wikipedia:`Strongly_connected_component`. + Returns a strongly connected orientation of the current graph. - An orientation of an undirected graph is a digraph obtained by - giving an unique direction to each of its edges. An orientation - is said to be strong if there is a directed path between each - pair of vertices. + An orientation of an undirected graph is a digraph obtained by giving an + unique direction to each of its edges. An orientation is said to be + strong if there is a directed path between each pair of vertices. See + also the :wikipedia:`Strongly_connected_component`. If the graph is 2-edge-connected, a strongly connected orientation can be found in linear time. If the given graph is not 2-connected, From 5dfc679f85677227c372562cf29fdb77fa2b16d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 9 Oct 2015 20:16:29 +0200 Subject: [PATCH 379/421] trac #19357 fixing floordiv and dump in Laurent polynomials in several var --- src/sage/rings/polynomial/laurent_polynomial.pyx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 091fc1a2fb0..c96bad68969 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -1332,8 +1332,11 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial_generic): sage: R. = LaurentPolynomialRing(QQ) sage: loads(dumps(x1)) == x1 # indirect doctest True + sage: z = x1/x2 + sage: loads(dumps(z)) == z + True """ - return self._parent, (self._poly,) + return self._parent, (self._poly, self._mon) cdef _new_c(self): """ @@ -1374,7 +1377,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial_generic): else: e = e.emin(k) if len(e.nonzero_positions()) > 0: - self._poly = self._poly / self._poly.parent()({e: 1}) + self._poly = self._poly // self._poly.parent()({e: 1}) self._mon = self._mon.eadd(e) else: e = None @@ -1382,7 +1385,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial_generic): if e is None or k[i] < e: e = k[i] if e > 0: - self._poly = self._poly / self._poly.parent().gen(i) + self._poly = self._poly // self._poly.parent().gen(i) self._mon = self._mon.eadd_p(e, i) def _dict(self): @@ -2047,8 +2050,15 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial_generic): x^2 - x*y^-1 + y^-2 sage: h * (f // h) == f True + + TESTS (:trac:`19357`):: + + sage: x // y + x*y^-1 """ cdef LaurentPolynomial_mpair ans = self._new_c() + self._normalize() + right._normalize() ans._mon = self._mon.esub((right)._mon) ans._poly = self._poly.__floordiv__((right)._poly) return ans From 0cc28a0202ba183faec750fa4e02083d1500e52d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 9 Oct 2015 16:47:37 -0500 Subject: [PATCH 380/421] Fixing some things. --- src/sage/combinat/integer_list.py | 8 ++++- src/sage/combinat/partition.py | 56 ++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/sage/combinat/integer_list.py b/src/sage/combinat/integer_list.py index b32c163abfb..774ea865bad 100644 --- a/src/sage/combinat/integer_list.py +++ b/src/sage/combinat/integer_list.py @@ -126,7 +126,8 @@ class IntegerListsLex(Parent): (default: `ClonableArray`). This merely sets the attribute ``self.Element``. See the examples for details. - - ``global_options`` -- a :class:`~sage.structure.global_options.GlobalOptions` + - ``global_options`` -- (deprecated) a + :class:`~sage.structure.global_options.GlobalOptions` object that will be assigned to the attribute ``_global_options``; for internal use only (subclasses, ...). @@ -524,6 +525,10 @@ class IntegerListsLex(Parent): in the class :class:`Partition`:: sage: IntegerListsLex(3, max_slope=0, element_class=Partition, global_options=Partitions.global_options).list() + doctest:...: DeprecationWarning: the global_options argument is + deprecated since, in general, pickling is broken; + create your own class instead + See http://trac.sagemath.org/15525 for details. [[3], [2, 1], [1, 1, 1]] Note that the :class:`Partition` further assumes the existence of @@ -943,6 +948,7 @@ def __init__(self, from sage.misc.superseded import deprecation deprecation(15525, 'the global_options argument is deprecated since, in general,' ' pickling is broken; create your own class instead') + self.global_options = global_options Parent.__init__(self, element_constructor=element_constructor, category=category) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index e4dcfb71244..362c7b86956 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -6703,9 +6703,21 @@ def __init__(self, ell, is_infinte=False): sage: P = Partitions(regular=2) sage: TestSuite(P).run() """ - self.ell = ell + self._ell = ell Partitions.__init__(self, is_infinte) + def ell(self): + r""" + Return the value `\ell`. + + EXAMPLES:: + + sage: P = Partitions(regular=2) + sage: P.ell() + 2 + """ + return self._ell + def __contains__(self, x): """ TESTS:: @@ -6731,8 +6743,8 @@ def __contains__(self, x): if not Partitions.__contains__(self, x): return False if isinstance(x, Partition): - return max(x.to_exp(1)) < self.ell - return all(x.count(i) < self.ell for i in set(x) if i > 0) + return max(x.to_exp(1)) < self._ell + return all(x.count(i) < self._ell for i in set(x) if i > 0) def _fast_iterator(self, n, max_part): """ @@ -6754,7 +6766,7 @@ def _fast_iterator(self, n, max_part): if n < max_part: max_part = n - bdry = self.ell - 1 + bdry = self._ell - 1 for i in reversed(range(1, max_part+1)): for p in self._fast_iterator(n-i, i): @@ -6792,7 +6804,7 @@ def _repr_(self): sage: RegularPartitions_all(3) 3-Regular Partitions """ - return "{}-Regular Partitions".format(self.ell) + return "{}-Regular Partitions".format(self._ell) def __iter__(self): """ @@ -6833,8 +6845,20 @@ def __init__(self, ell, max_len): sage: P = Partitions(regular=4, max_length=3) sage: TestSuite(P).run() """ - self.max_len = max_len - RegularPartitions.__init__(self, ell) + self._max_len = max_len + RegularPartitions.__init__(self, ell, True) + + def max_length(self): + """ + Return the maximum length of the partitions of ``self``. + + EXAMPLES:: + + sage: P = Partitions(regular=4, max_length=3) + sage: P.max_length() + 3 + """ + return self._max_len def __contains__(self, x): """ @@ -6848,7 +6872,7 @@ def __contains__(self, x): sage: [4, 2, 1, 1] in P False """ - return len(x) <= self.max_len and RegularPartitions.__contains__(self, x) + return len(x) <= self._max_len and RegularPartitions.__contains__(self, x) def _repr_(self): """ @@ -6858,7 +6882,7 @@ def _repr_(self): sage: RegularPartitions_truncated(4, 3) 4-Regular Partitions with max length 3 """ - return "{}-Regular Partitions with max length {}".format(self.ell, self.max_len) + return "{}-Regular Partitions with max length {}".format(self._ell, self._max_len) def __iter__(self): """ @@ -6891,19 +6915,19 @@ def _fast_iterator(self, n, max_part, depth=0): sage: list(P._fast_iterator(5, 6)) [[5], [4, 1], [3, 2]] """ - if n == 0 or depth >= self.max_len: + if n == 0 or depth >= self._max_len: yield [] return # Special case - if depth + 1 == self.max_len: + if depth + 1 == self._max_len: if max_part >= n: yield [n] return if n < max_part: max_part = n - bdry = self.ell - 1 + bdry = self._ell - 1 for i in reversed(range(1, max_part+1)): for p in self._fast_iterator(n-i, i, depth+1): @@ -6957,7 +6981,7 @@ def _repr_(self): sage: RegularPartitions_bounded(4, 3) 4-Regular 3-Bounded Partitions """ - return "{}-Regular {}-Bounded Partitions".format(self.ell, self.k) + return "{}-Regular {}-Bounded Partitions".format(self._ell, self.k) def __iter__(self): """ @@ -6970,7 +6994,7 @@ def __iter__(self): [[3, 2, 1], [3, 2], [3, 1], [3], [2, 1], [2], [1], []] """ k = self.k - for n in reversed(range(k*(k+1)/2 * self.ell)): + for n in reversed(range(k*(k+1)/2 * self._ell)): for p in self._fast_iterator(n, k): yield self.element_class(self, p) @@ -7007,7 +7031,7 @@ def _repr_(self): sage: RegularPartitions_n(3, 5) 5-Regular Partitions of the integer 3 """ - return "{}-Regular Partitions of the integer {}".format(self.ell, self.n) + return "{}-Regular Partitions of the integer {}".format(self._ell, self.n) def __contains__(self, x): """ @@ -7049,7 +7073,7 @@ def cardinality(self): sage: P.cardinality() == Partitions(5).cardinality() True """ - if self.ell > self.n: + if self._ell > self.n: return Partitions_n.cardinality(self) return ZZ.sum(1 for x in self) From 765e300aeaf9e58c34db97836bcc22bcbb40521f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 10 Oct 2015 08:38:06 +0200 Subject: [PATCH 381/421] trac #19357 undo the change to pickle (__reduce__) --- src/sage/rings/polynomial/laurent_polynomial.pyx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index c96bad68969..15384f60653 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -1333,10 +1333,11 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial_generic): sage: loads(dumps(x1)) == x1 # indirect doctest True sage: z = x1/x2 - sage: loads(dumps(z)) == z + sage: loads(dumps(z)) == z # not tested (bug) True """ - return self._parent, (self._poly, self._mon) + # one should also record the monomial self._mon + return self._parent, (self._poly,) # THIS IS WRONG ! cdef _new_c(self): """ @@ -2051,7 +2052,9 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial_generic): sage: h * (f // h) == f True - TESTS (:trac:`19357`):: + TESTS: + + Check that :trac:`19357` is fixed:: sage: x // y x*y^-1 From 4fcdb6ee743d6d73f90017bf919b87a7095e8fab Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 10 Oct 2015 12:59:52 +0200 Subject: [PATCH 382/421] Use jupyter_notebook_config.py to setup mathjax url --- build/pkgs/notebook/package-version.txt | 2 +- .../pkgs/notebook/patches/jupyter_notebook_config.py | 10 ++++++++++ build/pkgs/notebook/spkg-install | 5 +++++ src/sage/repl/ipython_kernel/install.py | 11 +++++++++-- 4 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 build/pkgs/notebook/patches/jupyter_notebook_config.py diff --git a/build/pkgs/notebook/package-version.txt b/build/pkgs/notebook/package-version.txt index c5106e6d139..61d8a2af785 100644 --- a/build/pkgs/notebook/package-version.txt +++ b/build/pkgs/notebook/package-version.txt @@ -1 +1 @@ -4.0.4 +4.0.4.p1 diff --git a/build/pkgs/notebook/patches/jupyter_notebook_config.py b/build/pkgs/notebook/patches/jupyter_notebook_config.py new file mode 100644 index 00000000000..3cb41976c98 --- /dev/null +++ b/build/pkgs/notebook/patches/jupyter_notebook_config.py @@ -0,0 +1,10 @@ +# Configuration file for Sage's builtin Jupyter notebook server + +# Note for distributors: Sage uses mathjax, so the notebook server +# needs to have the mathjax_url set to wherever your distribution +# installs mathjax. + +import os +c = get_config() + +c.NotebookApp.mathjax_url = '/nbextensions/mathjax/MathJax.js' diff --git a/build/pkgs/notebook/spkg-install b/build/pkgs/notebook/spkg-install index afb3f302fd1..10759a4d973 100755 --- a/build/pkgs/notebook/spkg-install +++ b/build/pkgs/notebook/spkg-install @@ -1,3 +1,8 @@ #!/usr/bin/env bash cd src && python setup.py install + +# Install the Jupyter notebook configuration +ETC_JUPYTER="$SAGE_ETC"/jupyter +mkdir -p "$ETC_JUPYTER" +cp ../patches/jupyter_notebook_config.py "$ETC_JUPYTER"/ diff --git a/src/sage/repl/ipython_kernel/install.py b/src/sage/repl/ipython_kernel/install.py index 7bc122fd509..08bad01cb58 100644 --- a/src/sage/repl/ipython_kernel/install.py +++ b/src/sage/repl/ipython_kernel/install.py @@ -250,13 +250,18 @@ def update(cls): instance._symlink_resources() -def have_prerequisites(): +def have_prerequisites(debug=True): """ Check that we have all prerequisites to run the IPython notebook. In particular, the IPython notebook requires OpenSSL whether or not you are using https. See :trac:`17318`. + INPUT: + + ``debug`` -- boolean (default: ``True``). Whether to print debug + information in case that prerequisites are missing. + OUTPUT: Boolean. @@ -264,13 +269,15 @@ def have_prerequisites(): EXAMPLES:: sage: from sage.repl.ipython_kernel.install import have_prerequisites - sage: have_prerequisites() in [True, False] + sage: have_prerequisites(debug=False) in [True, False] True """ try: from notebook.notebookapp import NotebookApp return True except ImportError: + import traceback + traceback.print_exc() return False From fbbb44dbb56529080b8229ad857a209ac54495c8 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 10 Oct 2015 13:21:33 +0200 Subject: [PATCH 383/421] Use relative path to MathJax; minor fixes --- .../notebook/patches/jupyter_notebook_config.py | 5 +---- src/sage/repl/ipython_kernel/install.py | 13 +++++-------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/build/pkgs/notebook/patches/jupyter_notebook_config.py b/build/pkgs/notebook/patches/jupyter_notebook_config.py index 3cb41976c98..3bf63a1649a 100644 --- a/build/pkgs/notebook/patches/jupyter_notebook_config.py +++ b/build/pkgs/notebook/patches/jupyter_notebook_config.py @@ -4,7 +4,4 @@ # needs to have the mathjax_url set to wherever your distribution # installs mathjax. -import os -c = get_config() - -c.NotebookApp.mathjax_url = '/nbextensions/mathjax/MathJax.js' +c.NotebookApp.mathjax_url = '../nbextensions/mathjax/MathJax.js' diff --git a/src/sage/repl/ipython_kernel/install.py b/src/sage/repl/ipython_kernel/install.py index 08bad01cb58..55e03622122 100644 --- a/src/sage/repl/ipython_kernel/install.py +++ b/src/sage/repl/ipython_kernel/install.py @@ -252,9 +252,9 @@ def update(cls): def have_prerequisites(debug=True): """ - Check that we have all prerequisites to run the IPython notebook. + Check that we have all prerequisites to run the Jupyter notebook. - In particular, the IPython notebook requires OpenSSL whether or + In particular, the Jupyter notebook requires OpenSSL whether or not you are using https. See :trac:`17318`. INPUT: @@ -276,10 +276,7 @@ def have_prerequisites(debug=True): from notebook.notebookapp import NotebookApp return True except ImportError: - import traceback - traceback.print_exc() + if debug: + import traceback + traceback.print_exc() return False - - - - From cf53613abb473f53411b50bd2ddc049050cd6dbc Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 10 Oct 2015 16:07:56 +0200 Subject: [PATCH 384/421] trac #19385: Refactor DiGraph.__init__ --- src/sage/graphs/digraph.py | 141 +++------------------------------ src/sage/graphs/graph_input.py | 124 ++++++++++++++++++++--------- 2 files changed, 98 insertions(+), 167 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index e4c6c2e64e4..ab6e5501944 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -349,7 +349,7 @@ class DiGraph(GenericGraph): [ 0 1 -1] [ -1 0 -1/2] [ 1 1/2 0] - sage: G = DiGraph(M,sparse=True); G + sage: G = DiGraph(M,sparse=True,weighted=True); G Digraph on 3 vertices sage: G.weighted() True @@ -683,80 +683,13 @@ def __init__(self, data=None, pos=None, loops=None, format=None, from_dig6(self, data) elif format == 'adjacency_matrix': - assert is_Matrix(data) - # note: the adjacency matrix might be weighted and hence not - # necessarily consists of integers - if not weighted and data.base_ring() != ZZ: - try: - data = data.change_ring(ZZ) - except TypeError: - if weighted is False: - raise ValueError("Non-weighted graph's"+ - " adjacency matrix must have only nonnegative"+ - " integer entries") - weighted = True - - if data.is_sparse(): - entries = set(data[i,j] for i,j in data.nonzero_positions()) - else: - entries = set(data.list()) - - if not weighted and any(e < 0 for e in entries): - if weighted is False: - raise ValueError("Non-weighted digraph's"+ - " adjacency matrix must have only nonnegative"+ - " integer entries") - weighted = True - if multiedges is None: multiedges = False - if weighted is None: - weighted = False - - if multiedges is None: - multiedges = ((not weighted) and any(e != 0 and e != 1 for e in entries)) + from graph_input import from_adjacency_matrix + from_adjacency_matrix(self, data, loops=loops, multiedges=multiedges, weighted=weighted) - if not loops and any(data[i,i] for i in xrange(data.nrows())): - if loops is False: - raise ValueError("Non-looped digraph's adjacency"+ - " matrix must have zeroes on the diagonal.") - loops = True - self.allow_multiple_edges(multiedges,check=False) - self.allow_loops(True if loops else False,check=False) - self.add_vertices(range(data.nrows())) - e = [] - if weighted: - for i,j in data.nonzero_positions(): - e.append((i,j,data[i][j])) - elif multiedges: - for i,j in data.nonzero_positions(): - e += [(i,j)]*int(data[i][j]) - else: - for i,j in data.nonzero_positions(): - e.append((i,j)) - self.add_edges(e) elif format == 'incidence_matrix': - assert is_Matrix(data) - positions = [] - for c in data.columns(): - NZ = c.nonzero_positions() - if len(NZ) != 2: - msg += "There must be two nonzero entries (-1 & 1) per column." - raise ValueError(msg) - L = sorted(set(c.list())) - if L != [-1,0,1]: - msg += "Each column represents an edge: -1 goes to 1." - raise ValueError(msg) - if c[NZ[0]] == -1: - positions.append(tuple(NZ)) - else: - positions.append((NZ[1],NZ[0])) - if weighted is None: weighted = False - if multiedges is None: - total = len(positions) - multiedges = ( len(set(positions)) < total ) - self.allow_loops(True if loops else False,check=False) - self.allow_multiple_edges(multiedges,check=False) - self.add_vertices(range(data.nrows())) - self.add_edges(positions) + from graph_input import from_oriented_incidence_matrix + from_oriented_incidence_matrix(self, data, loops=loops, multiedges=multiedges, weighted=weighted) + elif format == 'DiGraph': if loops is None: loops = data.allows_loops() elif not loops and data.has_loops(): @@ -784,66 +717,14 @@ def __init__(self, data=None, pos=None, loops=None, format=None, self.add_vertices(data[0]) self.add_edges((u,v) for u in data[0] for v in data[0] if f(u,v)) elif format == 'dict_of_dicts': - if not all(isinstance(data[u], dict) for u in data): - raise ValueError("Input dict must be a consistent format.") - - verts = set(data.keys()) - if loops is None or loops is False: - for u in data: - if u in data[u]: - if loops is None: - loops = True - elif loops is False: - u = next(u for u,neighb in data.iteritems() if u in neighb) - raise ValueError("The digraph was built with loops=False but input data has a loop at {}.".format(u)) - break - if loops is None: loops = False - if weighted is None: weighted = False - for u in data: - for v in data[u]: - if v not in verts: verts.add(v) - if multiedges is not False and not isinstance(data[u][v], list): - if multiedges is None: - multiedges = False - if multiedges: - raise ValueError("Dict of dicts for multidigraph must be in the format {v : {u : list}}") - if multiedges is None and len(data) > 0: - multiedges = True - self.allow_multiple_edges(multiedges,check=False) - self.allow_loops(loops,check=False) - self.add_vertices(verts) + from graph_input import from_dict_of_dicts + from_dict_of_dicts(self, data, loops=loops, multiedges=multiedges, weighted=weighted, + convert_empty_dict_labels_to_None = False if convert_empty_dict_labels_to_None is None else convert_empty_dict_labels_to_None) - if multiedges: - self.add_edges((u,v,l) for u,Nu in data.iteritems() for v,labels in Nu.iteritems() for l in labels) - else: - self.add_edges((u,v,l) for u,Nu in data.iteritems() for v,l in Nu.iteritems()) elif format == 'dict_of_lists': - # convert to a dict of lists if not already one - if not all(isinstance(data[u], list) for u in data): - data = {u: list(v) for u,v in data.iteritems()} - - if not loops and any(u in neighb for u,neighb in data.iteritems()): - if loops is False: - u = next(u for u,neighb in data.iteritems() if u in neighb) - raise ValueError("The digraph was built with loops=False but input data has a loop at {}.".format(u)) - loops = True - if loops is None: - loops = False - - if weighted is None: weighted = False + from graph_input import from_dict_of_lists + from_dict_of_lists(self, data, loops=loops, multiedges=multiedges, weighted=weighted) - if not multiedges and any(len(set(neighb)) != len(neighb) for neighb in data.itervalues()): - if multiedges is False: - uv = next((u,v) for u,neighb in data.iteritems() for v in neighb if neighb.count(v) > 1) - raise ValueError("Non-multidigraph got several edges (%s,%s)"%(u,v)) - multiedges = True - if multiedges is None: - multiedges = False - self.allow_multiple_edges(multiedges,check=False) - self.allow_loops(loops,check=False) - verts = set().union(data.keys(),*data.values()) - self.add_vertices(verts) - self.add_edges((u,v) for u,Nu in data.iteritems() for v in Nu) elif format == 'NX': # adjust for empty dicts instead of None in NetworkX default edge labels if convert_empty_dict_labels_to_None is None: diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 1cec738f1c7..cea82597dc3 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -208,7 +208,7 @@ def from_adjacency_matrix(G, M, loops=False, multiedges=False, weighted=False): INPUT: - - ``G`` -- a graph + - ``G`` -- a :class:`Graph` or :class:`DiGraph`. - ``M`` -- an adjacency matrix @@ -267,18 +267,19 @@ def from_adjacency_matrix(G, M, loops=False, multiedges=False, weighted=False): G.allow_multiple_edges(multiedges, check=False) G.add_vertices(range(M.nrows())) e = [] + if G.is_directed(): + pairs = M.nonzero_positions() + else: + pairs = ((i,j) for i,j in M.nonzero_positions() if i<=j) if weighted: - for i,j in M.nonzero_positions(): - if i <= j: - e.append((i,j,M[i][j])) + for i,j in pairs: + e.append((i,j,M[i][j])) elif multiedges: - for i,j in M.nonzero_positions(): - if i <= j: - e += [(i,j)]*int(M[i][j]) + for i,j in pairs: + e += [(i,j)]*int(M[i][j]) else: - for i,j in M.nonzero_positions(): - if i <= j: - e.append((i,j)) + for i,j in pairs: + e.append((i,j)) G.add_edges(e) G._weighted = weighted @@ -340,6 +341,56 @@ def from_incidence_matrix(G, M, loops=False, multiedges=False, weighted=False): G.add_vertices(range(M.nrows())) G.add_edges(positions) +def from_oriented_incidence_matrix(G, M, loops=False, multiedges=False, weighted=False): + r""" + Fill ``G`` with the data of an *oriented* incidence matrix. + + An oriented incidence matrix is the incidence matrix of a directed graph, in + which each non-loop edge corresponds to a `+1` and a `-1`, indicating its + source and destination. + + INPUT: + + - ``G`` -- a :class:`DiGraph` + + - ``M`` -- an incidence matrix + + - ``loops``, ``multiedges``, ``weighted`` (booleans) -- whether to consider + the graph as having loops, multiple edges, or weights. Set to ``False`` by default. + + EXAMPLE:: + + sage: from sage.graphs.graph_input import from_oriented_incidence_matrix + sage: g = DiGraph() + sage: from_oriented_incidence_matrix(g, digraphs.Circuit(10).incidence_matrix()) + sage: g.is_isomorphic(digraphs.Circuit(10)) + True + """ + from sage.matrix.matrix import is_Matrix + assert is_Matrix(M) + + positions = [] + for c in M.columns(): + NZ = c.nonzero_positions() + if len(NZ) != 2: + raise ValueError("There must be two nonzero entries (-1 & 1) per column.") + L = sorted(set(c.list())) + if L != [-1,0,1]: + msg += "Each column represents an edge: -1 goes to 1." + raise ValueError(msg) + if c[NZ[0]] == -1: + positions.append(tuple(NZ)) + else: + positions.append((NZ[1],NZ[0])) + if weighted is None: weighted = False + if multiedges is None: + total = len(positions) + multiedges = ( len(set(positions)) < total ) + G.allow_loops(True if loops else False,check=False) + G.allow_multiple_edges(multiedges,check=False) + G.add_vertices(range(M.nrows())) + G.add_edges(positions) + def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, convert_empty_dict_labels_to_None=False): r""" Fill ``G`` with the data of a dictionary of dictionaries. @@ -347,7 +398,7 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv INPUT: - ``G`` -- a graph - +x - ``M`` -- a dictionary of dictionaries. - ``loops``, ``multiedges``, ``weighted`` (booleans) -- whether to consider @@ -378,11 +429,6 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv if weighted is None: G._weighted = False for u in M: for v in M[u]: - if hash(u) > hash(v): - if v in M and u in M[v]: - if M[u][v] != M[v][u]: - raise ValueError("Dict does not agree on edge (%s,%s)"%(u,v)) - continue if multiedges is not False and not isinstance(M[u][v], list): if multiedges is None: multiedges = False if multiedges: @@ -394,23 +440,22 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv verts = set().union(M.keys(), *M.values()) G.add_vertices(verts) if convert_empty_dict_labels_to_None: + relabel = lambda x : x if x!={} else None + else: + relabel = lambda x : x + + is_directed = G.is_directed() + if not is_directed and multiedges: + v_to_id = {v:i for i,v in enumerate(verts)} for u in M: for v in M[u]: - if hash(u) <= hash(v) or v not in M or u not in M[v]: - if multiedges: - for l in M[u][v]: - G._backend.add_edge(u,v,l,False) - else: - G._backend.add_edge(u,v,M[u][v] if M[u][v] != {} else None,False) + if v_to_id[u] <= v_to_id[v] or v not in M or u not in M[v] or u == v: + for l in M[u][v]: + G._backend.add_edge(u,v,relabel(l),False) else: for u in M: for v in M[u]: - if hash(u) <= hash(v) or v not in M or u not in M[v]: - if multiedges: - for l in M[u][v]: - G._backend.add_edge(u,v,l,False) - else: - G._backend.add_edge(u,v,M[u][v],False) + G._backend.add_edge(u,v,relabel(M[u][v]),is_directed) def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): r""" @@ -418,7 +463,7 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): INPUT: - - ``G`` -- a graph + - ``G`` -- a :class:`Graph` or :class:`DiGraph`. - ``D`` -- a dictionary of lists. @@ -433,9 +478,6 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): sage: g.is_isomorphic(graphs.PetersenGraph()) True """ - if not all(isinstance(D[u], list) for u in D): - raise ValueError("Input dict must be a consistent format.") - verts = set().union(D.keys(),*D.values()) if loops is None or loops is False: for u in D: @@ -460,11 +502,19 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): G.allow_loops(loops, check=False) G.allow_multiple_edges(multiedges, check=False) G.add_vertices(verts) - for u in D: - for v in D[u]: - if (multiedges or hash(u) <= hash(v) or - v not in D or u not in D[v]): - G._backend.add_edge(u,v,None,False) + + is_directed = G.is_directed() + if not is_directed and multiedges: + v_to_id = {v:i for i,v in enumerate(verts)} + for u in D: + for v in D[u]: + if (v_to_id[u] <= v_to_id[v] or + v not in D or u not in D[v] or u == v): + G._backend.add_edge(u,v,None,False) + else: + for u in D: + for v in D[u]: + G._backend.add_edge(u,v,None,is_directed) from sage.misc.rest_index_of_methods import gen_rest_table_index import sys From d2f690be75c4f68df5ad10837edc1d1bdf7a4073 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Oct 2015 09:25:44 -0500 Subject: [PATCH 385/421] Fix for sparse dot dense vector. --- src/sage/modules/free_module_element.pyx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 5d0b8159274..cfcb49095e9 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -4580,8 +4580,21 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): sage: w = vector(R, [], sparse=True) sage: parent(v._dot_product_coerce_(w)) Univariate Polynomial Ring in x over Real Double Field + + TESTS: + + Check that :trac:`19377` is fixed:: + + sage: w = vector(ZZ, (1,2,3), sparse=False) + sage: v = vector(ZZ, (1,2,3), sparse=True) + sage: v._dot_product_coerce_(w) + 14 """ - cdef dict e = (right)._entries + cdef dict e + if right.is_sparse_c(): + e = (right)._entries + else: + e = right.dict() z = left.base_ring().zero() if left.base_ring() is not right.base_ring(): z *= right.base_ring().zero() From ccfd5f827df03f150209d1b347de1853cff1036d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Oct 2015 09:50:11 -0500 Subject: [PATCH 386/421] Don't need to multiply if the vector is 0-dimensional. --- src/sage/matrix/matrix_mod2_dense.pyx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index be72592dd1f..6e1b3014f6f 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -625,6 +625,15 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sage: r1 = A*v1 sage: r0.column(0) == r1 True + + TESTS: + + Check that :trac:`19378` is fixed:: + + sage: m = matrix(GF(2), 11, 0) + sage: v = vector(GF(2), 0) + sage: m * v + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) """ cdef mzd_t *tmp if not isinstance(v, Vector_mod2_dense): @@ -634,6 +643,9 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse raise ArithmeticError("number of columns of matrix must equal degree of vector") VS = VectorSpace(self._base_ring, self._nrows) + # If the vector is 0-dimensional, the result will be the 0-vector + if not self.ncols(): + return VS.zero() cdef Vector_mod2_dense c = Vector_mod2_dense.__new__(Vector_mod2_dense) c._init(self._nrows, VS) c._entries = mzd_init(1, self._nrows) From 99c8601b947b746cfd0cbbca4c836b82b3ac5dde Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 10 Oct 2015 17:55:55 +0200 Subject: [PATCH 387/421] trac #19385: Dictionary of lists for a digraph with labelled multiple edges. How I wish I could forget those stupid things. --- src/sage/graphs/graph_input.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index cea82597dc3..e2c07cf78aa 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -452,6 +452,11 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv if v_to_id[u] <= v_to_id[v] or v not in M or u not in M[v] or u == v: for l in M[u][v]: G._backend.add_edge(u,v,relabel(l),False) + elif multiedges: + for u in M: + for v in M[u]: + for l in M[u][v]: + G._backend.add_edge(u,v,relabel(l),is_directed) else: for u in M: for v in M[u]: From 7395b58933960ee9eaba588347ba516da27f533d Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Sat, 10 Oct 2015 11:49:05 -0500 Subject: [PATCH 388/421] Map from a noncrossing perfect matching to a noncrossing set partition --- src/sage/combinat/perfect_matching.py | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/sage/combinat/perfect_matching.py b/src/sage/combinat/perfect_matching.py index 40852a14e1f..ac325241e49 100644 --- a/src/sage/combinat/perfect_matching.py +++ b/src/sage/combinat/perfect_matching.py @@ -808,6 +808,35 @@ def to_permutation(self): from sage.combinat.permutation import Permutation return Permutation(self.value) + def to_non_crossing_set_partition(self): + r""" + Returns the noncrossing set partition (on half as many elements) + corresponding to the perfect matching, if the perfect matching is + noncrossing. Otherwise, gives an error. + + OUTPUT: + + The realization of ``self`` as a noncrossing set partition. + + EXAMPLES:: + + sage: PerfectMatching([[1,3], [4,2]]).to_non_crossing_set_partition() + Traceback (most recent call last): + ... + ValueError: matching must be non-crossing + sage: PerfectMatching([[1,4], [3,2]]).to_non_crossing_set_partition() + {{1, 2}} + sage: PerfectMatching([]).to_non_crossing_set_partition() + {} + """ + from sage.combinat.set_partition import SetPartition + if not self.is_non_crossing(): + raise ValueError("matching must be non-crossing") + else: + perm = self.to_permutation() + perm2 = Permutation([(perm[2*i])/2 for i in range(len(perm)/2)]) + return SetPartition(perm2.cycle_tuples()) + class PerfectMatchings(UniqueRepresentation, Parent): r""" From 35a422c82b352d4aedbd18f2f0a73c9a84d78ff8 Mon Sep 17 00:00:00 2001 From: Kevin Dilks Date: Sat, 10 Oct 2015 13:21:11 -0500 Subject: [PATCH 389/421] changed first line of doc string to be one sentence --- src/sage/combinat/perfect_matching.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/perfect_matching.py b/src/sage/combinat/perfect_matching.py index ac325241e49..74f0ebe0c59 100644 --- a/src/sage/combinat/perfect_matching.py +++ b/src/sage/combinat/perfect_matching.py @@ -811,8 +811,8 @@ def to_permutation(self): def to_non_crossing_set_partition(self): r""" Returns the noncrossing set partition (on half as many elements) - corresponding to the perfect matching, if the perfect matching is - noncrossing. Otherwise, gives an error. + corresponding to the perfect matching if the perfect matching is + noncrossing, and otherwise gives an error. OUTPUT: From 2060ebfbdd68c6af2882f695377e9953dafb12d4 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Oct 2015 14:10:12 -0500 Subject: [PATCH 390/421] Removing as_word from to_word and directly constructing a list, plus doc tweaks. --- src/sage/combinat/skew_tableau.py | 57 +++++++++++-------------------- 1 file changed, 20 insertions(+), 37 deletions(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 3e7b7b42332..026302b5228 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -404,19 +404,12 @@ def conjugate(self): return SkewTableau(conj) - def to_word_by_row(self, as_word=True): + def to_word_by_row(self): """ Return a word obtained from a row reading of ``self``. - Specifically, this is the word obtained by concatenating the - rows from the bottommost one (in English notation) to the - topmost one. - INPUT: - - - ``as_word`` -- boolean (default: ``True``); if ``True``, - the result is returned as a word (i.e., an element of - :class:`Words`), while otherwise it is returned as a - list + This is the word obtained by concatenating the rows from + the bottommost one (in English notation) to the topmost one. EXAMPLES:: @@ -433,8 +426,6 @@ def to_word_by_row(self, as_word=True): 1 sage: s.to_word_by_row() word: 1324 - sage: s.to_word_by_row(as_word=False) - [1, 3, 2, 4] TESTS:: @@ -444,24 +435,15 @@ def to_word_by_row(self, as_word=True): word: """ word = [x for row in reversed(self) for x in row if x is not None] - if not as_word: - return word return Words("positive integers")(word) - def to_word_by_column(self, as_word=True): + def to_word_by_column(self): """ Return the word obtained from a column reading of the skew tableau. - Specifically, this is the word obtained by concatenating the - columns from the rightmost one (in English notation) to the - leftmost one. - - INPUT: - - ``as_word`` -- boolean (default: ``True``); if ``True``, - the result is returned as a word (i.e., an element of - :class:`Words`), while otherwise it is returned as a - list + This is the word obtained by concatenating the columns from + the rightmost one (in English notation) to the leftmost one. EXAMPLES:: @@ -481,10 +463,8 @@ def to_word_by_column(self, as_word=True): 1 sage: s.to_word_by_column() word: 4231 - sage: s.to_word_by_column(as_word=False) - [4, 2, 3, 1] """ - return self.conjugate().to_word_by_row(as_word=as_word) + return self.conjugate().to_word_by_row() to_word = to_word_by_row @@ -875,18 +855,15 @@ def rectify(self, algorithm=None): """ Return a :class:`StandardTableau`, :class:`SemistandardTableau`, or just :class:`Tableau` formed by applying the jeu de taquin - process to ``self``. See page 15 of [FW]_. - - REFERENCES: + process to ``self``. - .. [FW] William Fulton, - *Young Tableaux*, - Cambridge University Press 1997. + See page 15 of [Fulton97]_. INPUT: - - ``algorithm`` -- optional: if set to ``'jdt'``, rectifies by jeu de taquin; - if set to ``'schensted'``, rectifies by Schensted insertion of the - reading word; otherwise, guesses which will be faster. + + - ``algorithm`` -- optional: if set to ``'jdt'``, rectifies by jeu de + taquin; if set to ``'schensted'``, rectifies by Schensted insertion + of the reading word; otherwise, guesses which will be faster. EXAMPLES:: @@ -911,6 +888,11 @@ def rectify(self, algorithm=None): [[None, 1], [2, 3]] sage: T [[None, None, None, 4], [None, None, 1, 6], [None, None, 5], [2, 3]] + + REFERENCES: + + .. [Fulton97] William Fulton, *Young Tableaux*, + Cambridge University Press 1997. """ mu_size = self.inner_shape().size() @@ -928,7 +910,8 @@ def rectify(self, algorithm=None): for i in range(mu_size): rect = rect.slide() elif algorithm == 'schensted': - rect = Tableau([]).insert_word(self.to_word(as_word=False)) + w = [x for row in reversed(self) for x in row if x is not None] + rect = Tableau([]).insert_word(w) else: raise ValueError("algorithm must be 'jdt', 'schensted', or None") if self in StandardSkewTableaux(): From 9a7d973760755605760baaf5a078b3667396f75d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Oct 2015 14:23:52 -0500 Subject: [PATCH 391/421] Removing some more as_word. --- src/sage/combinat/tableau.py | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 211b6783128..2dfe0da2d39 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -971,67 +971,45 @@ def pp(self): """ print self._repr_diagram() - def to_word_by_row(self, as_word=True): + def to_word_by_row(self): """ Return the word obtained from a row reading of the tableau ``self`` (starting with the lowermost row, reading every row from left to right). - INPUT: - - - ``as_word`` -- boolean (default: ``True``); if ``True``, - the result is returned as a word (i.e., an element of - :class:`Words`), while otherwise it is returned as a - list - EXAMPLES:: sage: Tableau([[1,2],[3,4]]).to_word_by_row() word: 3412 sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word_by_row() word: 325146 - sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word_by_row(as_word=False) - [3, 2, 5, 1, 4, 6] """ + from sage.combinat.words.word import Word w = [] for row in reversed(self): w += row - if not as_word: - return w - from sage.combinat.words.word import Word return Word(w) - def to_word_by_column(self, as_word=True): + def to_word_by_column(self): """ Return the word obtained from a column reading of the tableau ``self`` (starting with the leftmost column, reading every column from bottom to top). - INPUT: - - - ``as_word`` -- boolean (default: ``True``); if ``True``, - the result is returned as a word (i.e., an element of - :class:`Words`), while otherwise it is returned as a - list - EXAMPLES:: sage: Tableau([[1,2],[3,4]]).to_word_by_column() word: 3142 sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word_by_column() word: 321546 - sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word_by_column(as_word=False) - [3, 2, 1, 5, 4, 6] """ + from sage.combinat.words.word import Word w = [] for row in self.conjugate(): w += row[::-1] - if not as_word: - return w - from sage.combinat.words.word import Word return Word(w) - def to_word(self, as_word=True): + def to_word(self): """ An alias for :meth:`to_word_by_row`. @@ -1041,10 +1019,8 @@ def to_word(self, as_word=True): word: 3412 sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word() word: 325146 - sage: Tableau([[1, 4, 6], [2, 5], [3]]).to_word(as_word=False) - [3, 2, 5, 1, 4, 6] """ - return self.to_word_by_row(as_word=as_word) + return self.to_word_by_row() def attacking_pairs(self): """ From 80a70ce93d2cf6d4a7d76bd8b381a68f662f7465 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Oct 2015 14:39:07 -0500 Subject: [PATCH 392/421] Fixing missing imports. --- src/sage/combinat/skew_tableau.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 026302b5228..b49d163884b 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -40,7 +40,8 @@ from sage.structure.list_clone import ClonableList from sage.combinat.partition import Partition -from sage.combinat.tableau import Tableau, TableauOptions +from sage.combinat.tableau import (Tableau, TableauOptions, + StandardTableau, SemistandardTableau) from sage.combinat.skew_partition import SkewPartition, SkewPartitions from sage.combinat.integer_vector import IntegerVectors from sage.combinat.words.words import Words From 4595847bc52ffb96ebf970944c7ebc8d3347cf3c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Oct 2015 15:47:36 -0500 Subject: [PATCH 393/421] Making changes based upon Kevin's comments. --- src/sage/combinat/colored_permutations.py | 137 +++++++++++++++++++--- 1 file changed, 123 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index 3a501b65ebf..40e14878ce9 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -87,7 +87,7 @@ def _mul_(self, other): p = self._perm._left_to_right_multiply_on_right(other._perm) return self.__class__(self.parent(), colors, p) - def __invert__(self): + def inverse(self): """ Return the inverse of ``self``. @@ -105,6 +105,8 @@ def __invert__(self): tuple([-self._colors[i - 1] for i in ip]), # -1 for indexing ip) + __invert__ = inverse + def __eq__(self, other): """ Check equality. @@ -264,6 +266,23 @@ class ColoredPermutations(Parent, UniqueRepresentation): sage: s2*t*s2 [[0, 1, 0], [1, 2, 3]] + We can also create a colored permutation by passing + either a list of tuples consisting of ``(color, element)``:: + + sage: x = C([(2,1), (3,3), (3,2)]); x + [[2, 3, 3], [1, 3, 2]] + + or a list of colors and a permutation:: + + sage: C([[3,3,1], [1,3,2]]) + [[3, 3, 1], [1, 3, 2]] + + There is also the natural lift from permutations:: + + sage: P = Permutations(3) + sage: C(P.an_element()) + [[0, 0, 0], [3, 1, 2]] + REFERENCES: - :wikipedia:`Generalized_symmetric_group` @@ -364,8 +383,8 @@ def _element_constructor_(self, x): INPUT: - either a list of pairs (color, element) - or a pair of lists (colors, elements) + Either a list of pairs (color, element) + or a pair of lists (colors, elements). TESTS:: @@ -388,7 +407,41 @@ def _element_constructor_(self, x): if len(x) != 2: raise ValueError("input must be a pair of a list of colors and a permutation") - return self.element_class(self, map(self._C, x[0]), self._P(x[1])) + return self.element_class(self, [self._C(v) for v in x[0]], self._P(x[1])) + + def _coerce_map_from_(self, C): + """ + Return a coerce map from ``C`` if it exists and ``None`` otherwise. + + EXAMPLES:: + + sage: C = ColoredPermutations(2, 3) + sage: S = SignedPermutations(3) + sage: C.has_coerce_map_from(S) + True + + sage: C = ColoredPermutations(4, 3) + sage: C.has_coerce_map_from(S) + False + sage: S = SignedPermutations(4) + sage: C.has_coerce_map_from(S) + False + + sage: P = Permutations(3) + sage: C.has_coerce_map_from(P) + True + sage: P = Permutations(4) + sage: C.has_coerce_map_from(P) + False + """ + if isinstance(C, Permutations) and C.n == self._n: + return lambda P, x: P.element_class(P, [P._C.zero()]*P._n, x) + if self._m == 2 and isinstance(C, SignedPermutations) and C._n == self._n: + return lambda P, x: P.element_class(P, + [P._C.zero() if v == 1 else P._C.one() + for v in x._colors], + x._perm) + return super(ColoredPermutations, self)._coerce_map_from_(C) def __iter__(self): """ @@ -482,17 +535,20 @@ def degrees(self): return [self._m * i for i in range(1, self._n + 1)] def codegrees(self): - """ + r""" Return the codegrees of ``self``. Let `G` be a complex reflection group. The codegrees `d_1^* \leq d_2^* \leq \cdots \leq d_{\ell}^*` of `G` can be defined by: + .. MATH:: + \prod_{i=1}^{\ell} (q - d_i^* - 1) = \sum_{g \in G} \det(g) q^{\dim(V^g)}, - where `V` is the natural complex vector space that `G` acts on. + where `V` is the natural complex vector space that `G` acts on + and `\ell` is the :meth:`rank`. If `m = 1`, then we are in the special case of the symmetric group and the codegrees are `(n-2, n-3, \ldots 1, 0)`. Otherwise the degrees @@ -523,7 +579,7 @@ def codegrees(self): return list(reversed(range(self._n - 1))) return [self._m * i for i in reversed(range(self._n))] - def number_reflection_hyperplanes(self): + def number_of_reflection_hyperplanes(self): """ Return the number of reflection hyperplanes of ``self``. @@ -533,13 +589,13 @@ def number_reflection_hyperplanes(self): EXAMPLES:: sage: C = ColoredPermutations(1, 2) - sage: C.number_reflection_hyperplanes() + sage: C.number_of_reflection_hyperplanes() 1 sage: C = ColoredPermutations(1, 3) - sage: C.number_reflection_hyperplanes() + sage: C.number_of_reflection_hyperplanes() 3 sage: C = ColoredPermutations(4, 12) - sage: C.number_reflection_hyperplanes() + sage: C.number_of_reflection_hyperplanes() 276 """ return sum(self.codegrees()) + self.rank() @@ -548,15 +604,16 @@ def fixed_point_polynomial(self, q=None): r""" The fixed point polynomial of ``self``. - The fixed point polynomial `f_G` of a complex reflection group `G` is - counting the dimensions of fixed points subspaces: + The fixed point polynomial `f_G` of a complex reflection group `G` + is counting the dimensions of fixed points subspaces: .. MATH:: f_G(q) = \sum_{w \in W} q^{\dim V^w}. Furthermore, let `d_1, d_2, \ldots, d_{\ell}` be the degrees of `G`, - then the fixed point polynomial is given by + where `\ell` is the :meth:`rank`. Then the fixed point polynomial + is given by .. MATH:: @@ -772,6 +829,27 @@ class SignedPermutations(ColoredPermutations): sage: S.long_element().reduced_word() [4, 3, 4, 2, 3, 4, 1, 2, 3, 4] + We can also go between the 2-colored permutation group:: + + sage: C = ColoredPermutations(2, 3) + sage: S = SignedPermutations(3) + sage: S.an_element() + [-3, 1, 2] + sage: C(S.an_element()) + [[1, 0, 0], [3, 1, 2]] + sage: S(C(S.an_element())) == S.an_element() + True + sage: S(C.an_element()) + [1, 2, 3] + + There is also the natural lift from permutations:: + + sage: P = Permutations(3) + sage: x = S(P.an_element()); x + [3, 1, 2] + sage: x.parent() + Signed permutations of 3 + REFERENCES: - :wikipedia:`Hyperoctahedral_group` @@ -892,7 +970,7 @@ def _element_constructor_(self, x): raise ValueError("input must be a pair of a list of signs and a permutation") if any(s != 1 and s != -1 for s in x[0]): raise ValueError("the sign must be +1 or -1") - return self.element_class(self, map(ZZ, x[0]), self._P(x[1])) + return self.element_class(self, [ZZ(v) for v in x[0]], self._P(x[1])) def __iter__(self): """ @@ -911,6 +989,37 @@ def __iter__(self): for c in C: yield self.element_class(self, c, p) + def _coerce_map_from_(self, C): + """ + Return a coerce map from ``C`` if it exists and ``None`` otherwise. + + EXAMPLES:: + + sage: C = ColoredPermutations(2, 3) + sage: S = SignedPermutations(3) + sage: S.has_coerce_map_from(C) + True + + sage: C = ColoredPermutations(4, 3) + sage: S.has_coerce_map_from(C) + False + + sage: P = Permutations(3) + sage: C.has_coerce_map_from(P) + True + sage: P = Permutations(4) + sage: C.has_coerce_map_from(P) + False + """ + if isinstance(C, Permutations) and C.n == self._n: + return lambda P, x: P.element_class(P, [1]*P._n, x) + if isinstance(C, ColoredPermutations) and C._n == self._n and C._m == 2: + return lambda P, x: P.element_class(P, + [1 if v == 0 else -1 + for v in x._colors], + x._perm) + return super(SignedPermutations, self)._coerce_map_from_(C) + @cached_method def index_set(self): """ From e92b704774396ffb2fa1a25e9ceb45e8a2c8f9f0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Oct 2015 15:50:29 -0500 Subject: [PATCH 394/421] Following Jeroen's suggestion. --- src/sage/modules/free_module_element.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index cfcb49095e9..24de92f1e77 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -4591,9 +4591,9 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): 14 """ cdef dict e - if right.is_sparse_c(): - e = (right)._entries - else: + try: + e = (right)._entries + except: e = right.dict() z = left.base_ring().zero() if left.base_ring() is not right.base_ring(): From 26a0b6bc624b8eee81d35dfcf0dfa85b626a6245 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Oct 2015 16:01:09 -0500 Subject: [PATCH 395/421] TypeError... --- src/sage/modules/free_module_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 24de92f1e77..587bc33f032 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -4593,7 +4593,7 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): cdef dict e try: e = (right)._entries - except: + except TypeError: e = right.dict() z = left.base_ring().zero() if left.base_ring() is not right.base_ring(): From 2cc7f6ce9b270c1efcd07b541fe94e34a245918d Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 10 Oct 2015 23:41:42 +0200 Subject: [PATCH 396/421] Updated Sage version to 6.9 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- src/bin/sage-banner | 5 +---- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index fffa4f6775c..68733eba217 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 6.9.rc3, released 2015-10-06 +Sage version 6.9, released 2015-10-10 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 55c47a9e1f3..ad47e649554 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=03f35d65364edef5f2f8bb43a2e53e8816be4dbe -md5=903c006410269e4ca3767a6cd4db8971 -cksum=850114776 +sha1=beb4de16a8e5a632516a6b5f63313e66c6770183 +md5=d6922f63e807c1d240582601e746dcb1 +cksum=2973216508 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 415196e47f5..078fa0fe576 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -118 +119 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index b4a7da6767f..955061b92e5 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,8 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath Version 6.9.rc3, Release Date: 2015-10-06 │ +│ SageMath Version 6.9, Release Date: 2015-10-10 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ -┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ -┃ Warning: this is a prerelease version, and it may be unstable. ┃ -┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 8b2b6b5fb05..7b35690365e 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='6.9.rc3' -SAGE_RELEASE_DATE='2015-10-06' +SAGE_VERSION='6.9' +SAGE_RELEASE_DATE='2015-10-10' diff --git a/src/sage/version.py b/src/sage/version.py index 27ca7af9cf7..da4209e58b1 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '6.9.rc3' -date = '2015-10-06' +version = '6.9' +date = '2015-10-10' From ff97f0b11bb9f5dd9b27a6ee738f89d3efb6e6f0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Oct 2015 17:47:42 -0500 Subject: [PATCH 397/421] Getting one last reference. --- src/sage/combinat/skew_tableau.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index b49d163884b..cb1b6a308bc 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -780,7 +780,7 @@ def slide(self, corner=None): return the resulting tableau. If no corner is given, an arbitrary inner corner is chosen. - See [FW]_ p12-13. + See [Fulton97]_ p12-13. EXAMPLES:: From 6e7cb2173baacb6b5bbb8b29ec7cafb57c4ad6c7 Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Sat, 10 Oct 2015 18:03:09 -0500 Subject: [PATCH 398/421] Created conversions of SymmetricGroupAlgebra to DiagramAlgebras as well as appropriate coercions where it makes sense --- src/sage/combinat/diagram_algebras.py | 56 ++++++++++++++++++--------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 6672e2e1a8d..90ef23e015f 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -31,7 +31,7 @@ from sage.structure.global_options import GlobalOptions from sage.combinat.set_partition import SetPartitions, SetPartition from sage.combinat.partition import Partitions -from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra +from sage.combinat.symmetric_group_algebra import (SymmetricGroupAlgebra, SymmetricGroupAlgebra_n) from sage.combinat.permutation import Permutations from sage.combinat.combinat import (bell_number, catalan_number) from sage.sets.set import Set @@ -1200,18 +1200,6 @@ class DiagramAlgebra(CombinatorialFreeModule): P{{-2, 2}, {-1}, {1}}, P{{-2, 2}, {-1, 1}}] - Due to the nature of diagrams, there is also a built-in coercion to turn - SymmetricGroupAlgebra elements into DiagramAlgebra elements. However, - this coercion can cause errors if the SymmetricGroupAlgebra element - is not actually valid in the algebra. For instance, not all - SymmetricGroupAlgebra elements are valid in the Temperely--Lieb algebra, - but the planar ones are. - - :: - - sage: S = SymmetricGroupAlgebra(R, 2) - sage: S([2,1])*D([[1,-1],[2,-2]]) - P{{-2, 1}, {-1, 2}} """ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): r""" @@ -1233,18 +1221,14 @@ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): sage: D = da.DiagramAlgebra(2, x, R, 'P', da.PartitionDiagrams(2)) sage: TestSuite(D).run() """ - SymmetricGroupAlgebra(base_ring,k) # Necessary for some odd reason self._prefix = prefix self._q = base_ring(q) self._k = k self._base_diagrams = diagrams category = Algebras(base_ring).FiniteDimensional().WithBasis().or_subcategory(category) - KSS = SymmetricGroupAlgebra(base_ring, k) CombinatorialFreeModule.__init__(self, base_ring, diagrams, category=category, prefix=prefix, bracket=False) - KSS.module_morphism(lambda i : self(self._perm_to_Blst(i)), codomain=self).register_as_coercion() - def _element_constructor_(self, set_partition): r""" Construct an element of ``self``. @@ -1263,10 +1247,14 @@ def _element_constructor_(self, set_partition): True sage: D([{1,2},{-1,-2}]) == b_elt True + sage: S = SymmetricGroupAlgebra(SR,2) + sage: D(S([2,1])) + P{{-2, 1}, {-1, 2}} """ if self.basis().keys().is_parent_of(set_partition): return self.basis()[set_partition] - + if isinstance(set_partition, SymmetricGroupAlgebra_n.Element): + return self._element_constructor(self._perm_to_Blst(set_partition.support_of_term())) sp = self._base_diagrams(set_partition) # attempt conversion if sp in self.basis().keys(): return self.basis()[sp] @@ -1297,6 +1285,9 @@ def _perm_to_Blst(self, w): u = sorted(w) return [[u[i],-w[i]] for i in range(len(w))] + def _convert_perm_to_element_of_self(self, x): + return self(self._perm_to_Blst(x)) + def order(self): r""" Return the order of ``self``. @@ -1650,6 +1641,16 @@ class PartitionAlgebra(DiagramAlgebra): sage: a*a 17*P{{-1}, {1}} + Symmetric group algebra elements can also be coerced into the partition algebra. + + TESTS: + + sage: A = PartitionAlgebra(2, x, SR) + sage: S = SymmetricGroupAlgebra(SR, 2) + sage: S([2,1])*A([[1,-1],[2,-2]]) + P{{-2, 1}, {-1, 2}} + + REFERENCES: .. [HR2005] Tom Halverson and Arun Ram, *Partition algebras*. European @@ -1688,7 +1689,10 @@ def __init__(self, k, q, base_ring, prefix): self._k = k self._prefix = prefix self._q = base_ring(q) + KSS = SymmetricGroupAlgebra(base_ring, k) DiagramAlgebra.__init__(self, k, q, base_ring, prefix, PartitionDiagrams(k)) + KSS.module_morphism(lambda i : self(self._perm_to_Blst(i)), codomain=self).register_as_coercion() + def _repr_(self): """ @@ -1835,6 +1839,16 @@ class BrauerAlgebra(SubPartitionAlgebra): x*B{{-2, -1}, {1, 2}} sage: b[2]^5 x^4*B{{-2, -1}, {1, 2}} + + Note, also that since the symmetric group algebra is contained in the Brauer algebra, + there is also a coercion between the two. :: + + sage: R. = ZZ[] + sage: B = BrauerAlgebra(2,x,R) + sage: S = SymmetricGroupAlgebra(R,2) + sage: S([2,1])*B([[1,-1],[2,-2]]) + B{{-2, 1}, {-1, 2}} + """ global_options = BrauerDiagramOptions @@ -1866,7 +1880,9 @@ def __init__(self, k, q, base_ring, prefix): sage: BA = BrauerAlgebra(2, q, R) sage: TestSuite(BA).run() """ + KSS = SymmetricGroupAlgebra(base_ring, k) SubPartitionAlgebra.__init__(self, k, q, base_ring, prefix, BrauerDiagrams(k)) + KSS.module_morphism(lambda i : self(self._perm_to_Blst(i)), codomain=self).register_as_coercion() def _repr_(self): """ @@ -1900,6 +1916,8 @@ def _element_constructor_(self, set_partition): sage: BA([{1,2},{-1,-2}]) == b_elt True """ + if isinstance(set_partition, SymmetricGroupAlgebra_n.Element): + return DiagramAlgebra._element_constructor_(self, set_partition) set_partition = to_Brauer_partition(set_partition, k = self.order()) return DiagramAlgebra._element_constructor_(self, set_partition) @@ -2013,6 +2031,8 @@ def _element_constructor_(self, set_partition): sage: TL([{1,2},{-1,-2}]) == b_elt True """ + if isinstance(set_partition, SymmetricGroupAlgebra_n.Element): + return SubPartitionAlgebra._element_constructor_(self, set_partition) set_partition = to_Brauer_partition(set_partition, k = self.order()) return SubPartitionAlgebra._element_constructor_(self, set_partition) From 1ec481d803e3fa7d8c3d0e4038f4c476a73c0fd0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Oct 2015 18:05:48 -0500 Subject: [PATCH 399/421] Addressing Kevin's comments. --- src/sage/combinat/partition.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 362c7b86956..cc4d0db3927 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -6687,12 +6687,20 @@ class RegularPartitions(Partitions): r""" Base class for `\ell`-regular partitions. - A partition is `\ell`-regular if `m_i < \ell` for all `i`. + Let `\ell` be a positive integer. A partition `\lambda` is + `\ell`-*regular* if `m_i < \ell` for all `i`, where `m_i` is the + multiplicity of `i` in `\lambda`. + + .. NOTE:: + + This is conjugate to the notion of `\ell`-*restricted* partitions, + where the difference between any two parts is at most `\ell`. INPUT: - - ``ell`` -- the value `\ell` - - ``is_infinite`` -- if the subset of `\ell`-regular partitions is infinite + - ``ell`` -- the integer `\ell` + - ``is_infinite`` -- boolean; if the subset of `\ell`-regular + partitions is infinite """ def __init__(self, ell, is_infinte=False): """ @@ -6779,7 +6787,7 @@ class RegularPartitions_all(RegularPartitions): INPUT: - - ``ell`` -- the value `\ell` + - ``ell`` -- the integer `\ell` .. SEEALSO:: @@ -6829,8 +6837,8 @@ class RegularPartitions_truncated(RegularPartitions): INPUT: - - ``ell`` -- the value `\ell` - - ``max_len`` -- the maximum length + - ``ell`` -- the integer `\ell` + - ``max_len`` -- integer; the maximum length .. SEEALSO:: @@ -6940,8 +6948,8 @@ class RegularPartitions_bounded(RegularPartitions): INPUT: - - ``ell`` -- the value `\ell` - - ``k`` -- the value `k` + - ``ell`` -- the integer `\ell` + - ``k`` -- integer; the value `k` .. SEEALSO:: @@ -7005,7 +7013,7 @@ class RegularPartitions_n(RegularPartitions, Partitions_n): INPUT: - ``n`` -- the integer `n` to partition - - ``ell`` -- the value `\ell` + - ``ell`` -- the integer `\ell` .. SEEALSO:: From ea857ff335ac439123ef7b7891bdd8909e5c3fb4 Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Sat, 10 Oct 2015 19:11:11 -0500 Subject: [PATCH 400/421] added more negative unit tests for conversion from symmetric group algebra to diagram algebra. One test does not pass --- src/sage/combinat/diagram_algebras.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 90ef23e015f..7bc486265c6 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -1247,9 +1247,16 @@ def _element_constructor_(self, set_partition): True sage: D([{1,2},{-1,-2}]) == b_elt True - sage: S = SymmetricGroupAlgebra(SR,2) + sage: S = SymmetricGroupAlgebra(R,2) sage: D(S([2,1])) P{{-2, 1}, {-1, 2}} + sage: D2 = da.DiagramAlgebra(2, x, R, 'P', da.PlanarDiagrams(2)) + sage: D2(S([1,2])) + P{{-2, 2}, {-1, 1}} + sage: D2(S([2,1])) + Traceback (most recent call last): + ... + ValueError: invalid input of [[1, -2], [2, -1]] """ if self.basis().keys().is_parent_of(set_partition): return self.basis()[set_partition] @@ -2030,6 +2037,13 @@ def _element_constructor_(self, set_partition): True sage: TL([{1,2},{-1,-2}]) == b_elt True + sage: S = SymmetricGroupAlgebra(R, 2) + sage: TL(S([1,2])) + T{{-2, 2}, {-1, 1}} + sage: TL(S([2,1])) + Traceback (most recent call last): + ... + ValueError: invalid input of [set([1, -2]), set([2, -1])] """ if isinstance(set_partition, SymmetricGroupAlgebra_n.Element): return SubPartitionAlgebra._element_constructor_(self, set_partition) From ec9cd10617f78bb1a8a0d1550d1accb6c765bc1e Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sun, 11 Oct 2015 08:55:13 +0200 Subject: [PATCH 401/421] 17624: coerce factorizations to SR --- src/sage/symbolic/ring.pyx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index bdf639a1995..3891da5f05f 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -251,6 +251,18 @@ cdef class SymbolicRing(CommutativeRing): sage: bool(si == CC.0) True + Polynomial ring element factorizations:: + + sage: R. = QQ[] + sage: SR(factor(x^2 - 1)) + (x + 1)*(x - 1) + sage: R. = QQ[] + sage: SR(factor(x^2 - y^2)) + (x + y)*(x - y) + sage: R. = QQ[] + sage: SR(factor(x^2*y^3 + x^2*y^2*z - x*y^3 - x*y^2*z - 2*x*y*z - 2*x*z^2 + 2*y*z + 2*z^2)) + -(x*y^2 - 2*z)*(x - 1)*(y + z) + """ cdef GEx exp @@ -271,6 +283,7 @@ cdef class SymbolicRing(CommutativeRing): from sage.rings.infinity import (infinity, minus_infinity, unsigned_infinity) + from sage.structure.factorization import Factorization if isinstance(x, (Integer, RealNumber, float, long, complex)): GEx_construct_pyobject(exp, x) @@ -284,6 +297,9 @@ cdef class SymbolicRing(CommutativeRing): return new_Expression_from_GEx(self, g_UnsignedInfinity) elif isinstance(x, (RingElement, Matrix)): GEx_construct_pyobject(exp, x) + elif isinstance(x, Factorization): + from sage.misc.all import prod + return prod([SR(p)**e for p,e in x], SR.one()) else: raise TypeError From 45e9685ab5d01440a3e2cbea033f9e797eda2456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 11 Oct 2015 11:50:24 +0200 Subject: [PATCH 402/421] trac #17496 fixing the pdf doc --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index a1306b07250..032d7fcaaa9 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -1972,7 +1972,9 @@ def minimal_generated_number(self): Let `A` be a central hyperplane arrangement. Let `W_k` denote the solution space of the linear system corresponding to the linear dependencies among the hyperplanes of `A` of length at - most `k`. We say `A` is `k`-*generated* if `\dim W_k = \rank A`. + most `k`. We say `A` is `k`-*generated* if + `\dim W_k = \operatorname{rank} A`. + Equivalently this says all dependencies forming the Orlik-Terao ideal are generated by at most `k` hyperplanes. From a4c199d2842865c6385e6938b67974f39ebd8876 Mon Sep 17 00:00:00 2001 From: "George H. Seelinger" Date: Sun, 11 Oct 2015 12:01:47 -0500 Subject: [PATCH 403/421] added the necessary extra colon after tests --- src/sage/combinat/diagram_algebras.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 7bc486265c6..d8618f16500 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -1650,7 +1650,7 @@ class PartitionAlgebra(DiagramAlgebra): Symmetric group algebra elements can also be coerced into the partition algebra. - TESTS: + TESTS:: sage: A = PartitionAlgebra(2, x, SR) sage: S = SymmetricGroupAlgebra(SR, 2) From 894330e1787253600523bea93f34ce793f25e05c Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 11 Oct 2015 19:02:44 +0200 Subject: [PATCH 404/421] trac #19226: Broken doctest --- src/sage/homology/simplicial_complex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py index c8c188552f6..534340fbc16 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/homology/simplicial_complex.py @@ -775,7 +775,7 @@ class SimplicialComplex(CategoryObject, GenericCellComplex): sage: l=designs.ProjectiveGeometryDesign(2,1,GF(4,name='a')) sage: f = lambda S: not any(len(set(S).intersection(x))>2 for x in l) - sage: SimplicialComplex(from_characteristic_function=(f, range(21))) + sage: SimplicialComplex(from_characteristic_function=(f, l.ground_set())) Simplicial complex with 21 vertices and 168 facets TESTS: From 14f80d3624d95079c896cb5efe8f29988834e1ac Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 11 Oct 2015 19:36:44 -0500 Subject: [PATCH 405/421] Reviewer changes and fixing coercions. --- src/sage/combinat/diagram_algebras.py | 159 ++++++++++++++++---------- 1 file changed, 99 insertions(+), 60 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index d8618f16500..7752a564c42 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -27,19 +27,18 @@ CombinatorialFreeModuleElement) from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.combinat.combinat import (bell_number, catalan_number) +from sage.combinat.combinat import bell_number, catalan_number from sage.structure.global_options import GlobalOptions from sage.combinat.set_partition import SetPartitions, SetPartition from sage.combinat.partition import Partitions -from sage.combinat.symmetric_group_algebra import (SymmetricGroupAlgebra, SymmetricGroupAlgebra_n) +from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra_n from sage.combinat.permutation import Permutations -from sage.combinat.combinat import (bell_number, catalan_number) from sage.sets.set import Set from sage.graphs.graph import Graph from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute from sage.misc.flatten import flatten from sage.rings.all import ZZ -import operator BrauerDiagramOptions = GlobalOptions(name='Brauer diagram', doc=r""" @@ -1199,7 +1198,6 @@ class DiagramAlgebra(CombinatorialFreeModule): P{{-2, 1, 2}, {-1}}, P{{-2, 2}, {-1}, {1}}, P{{-2, 2}, {-1, 1}}] - """ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): r""" @@ -1225,7 +1223,8 @@ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): self._q = base_ring(q) self._k = k self._base_diagrams = diagrams - category = Algebras(base_ring).FiniteDimensional().WithBasis().or_subcategory(category) + category = Algebras(base_ring.category()).FiniteDimensional().WithBasis() + category = category.or_subcategory(category) CombinatorialFreeModule.__init__(self, base_ring, diagrams, category=category, prefix=prefix, bracket=False) @@ -1256,12 +1255,12 @@ def _element_constructor_(self, set_partition): sage: D2(S([2,1])) Traceback (most recent call last): ... - ValueError: invalid input of [[1, -2], [2, -1]] + ValueError: {{-2, 1}, {-1, 2}} is not an index of a basis element """ if self.basis().keys().is_parent_of(set_partition): return self.basis()[set_partition] if isinstance(set_partition, SymmetricGroupAlgebra_n.Element): - return self._element_constructor(self._perm_to_Blst(set_partition.support_of_term())) + return self._apply_module_morphism(set_partition, self._perm_to_Blst, self) sp = self._base_diagrams(set_partition) # attempt conversion if sp in self.basis().keys(): return self.basis()[sp] @@ -1287,13 +1286,25 @@ def __getitem__(self, i): raise ValueError("{0} is not an index of a basis element".format(i)) def _perm_to_Blst(self, w): + """ + Convert the permutation ``w`` to an element of ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: S = SymmetricGroupAlgebra(R,2) + sage: import sage.combinat.diagram_algebras as da + sage: D2 = da.DiagramAlgebra(2, x, R, 'P', da.PlanarDiagrams(2)) + sage: D2._perm_to_Blst([2,1]) + Traceback (most recent call last): + ... + ValueError: {{-2, 1}, {-1, 2}} is not an index of a basis element + """ ## 'perm' is a permutation in one-line notation ## turns w into an expression suitable for the element constructor. u = sorted(w) - return [[u[i],-w[i]] for i in range(len(w))] - - def _convert_perm_to_element_of_self(self, x): - return self(self._perm_to_Blst(x)) + p = [[u[i],-x] for i,x in enumerate(w)] + return self[p] def order(self): r""" @@ -1648,15 +1659,13 @@ class PartitionAlgebra(DiagramAlgebra): sage: a*a 17*P{{-1}, {1}} - Symmetric group algebra elements can also be coerced into the partition algebra. + Symmetric group algebra elements can also be coerced into the + partition algebra:: - TESTS:: - - sage: A = PartitionAlgebra(2, x, SR) sage: S = SymmetricGroupAlgebra(SR, 2) + sage: A = PartitionAlgebra(2, x, SR) sage: S([2,1])*A([[1,-1],[2,-2]]) P{{-2, 1}, {-1, 2}} - REFERENCES: @@ -1696,10 +1705,7 @@ def __init__(self, k, q, base_ring, prefix): self._k = k self._prefix = prefix self._q = base_ring(q) - KSS = SymmetricGroupAlgebra(base_ring, k) DiagramAlgebra.__init__(self, k, q, base_ring, prefix, PartitionDiagrams(k)) - KSS.module_morphism(lambda i : self(self._perm_to_Blst(i)), codomain=self).register_as_coercion() - def _repr_(self): """ @@ -1715,6 +1721,31 @@ def _repr_(self): return "Partition Algebra of rank {} with parameter {} over {}".format( self._k, self._q, self.base_ring()) + def _coerce_map_from_(self, R): + """ + Return a coerce map from ``R`` if one exists and ``None`` otherwise. + + EXAMPLES:: + + sage: R. = QQ[] + sage: S = SymmetricGroupAlgebra(R, 4) + sage: A = PartitionAlgebra(4, x, R) + sage: A._coerce_map_from_(S) + Generic morphism: + From: Symmetric group algebra of order 4 over Univariate Polynomial Ring in x over Rational Field + To: Partition Algebra of rank 4 with parameter x over Univariate Polynomial Ring in x over Rational Field + sage: Sp = SymmetricGroupAlgebra(QQ, 4) + sage: A._coerce_map_from_(Sp) + Generic morphism: + From: Symmetric group algebra of order 4 over Rational Field + To: Partition Algebra of rank 4 with parameter x over Univariate Polynomial Ring in x over Rational Field + """ + if isinstance(R, SymmetricGroupAlgebra_n): + if R.n == self._k and self.base_ring().has_coerce_map_from(R.base_ring()): + return R.module_morphism(self._perm_to_Blst, codomain=self) + return None + return super(PartitionAlgebra, self)._coerce_map_from_(R) + class SubPartitionAlgebra(DiagramAlgebra): """ A subalgebra of the partition algebra indexed by a subset of the diagrams. @@ -1731,15 +1762,11 @@ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): True """ DiagramAlgebra.__init__(self, k, q, base_ring, prefix, diagrams, category) - amb = self.ambient() - self.module_morphism(self.lift, codomain=amb, - category=self.category()).register_as_coercion() - #These methods allow for a sub algebra to be correctly identified in a partition algebra + #These methods allow for a subalgebra to be correctly identified in a partition algebra def ambient(self): r""" Return the partition algebra ``self`` is a sub-algebra of. - Generally, this method is not called directly. EXAMPLES:: @@ -1748,13 +1775,12 @@ def ambient(self): sage: BA.ambient() Partition Algebra of rank 2 with parameter x over Symbolic Ring """ - return PartitionAlgebra(self._k, self._q, self.base_ring(), prefix=self._prefix) + return self.lift.codomain() - def lift(self, x): + @lazy_attribute + def lift(self): r""" - Lift a diagram subalgebra element to the corresponding element - in the ambient space. This method is not intended to be called - directly. + Return the lift map from diagram subalgebra to the ambient space. EXAMPLES:: @@ -1766,20 +1792,16 @@ def lift(self, x): sage: lifted.parent() is BA.ambient() True """ - if x not in self: - raise ValueError("{0} is not in {1}".format(x, self)) - monomial_indices = x.support() - monomial_coefficients = x.coefficients() - result = 0 - for i in xrange(len(monomial_coefficients)): - result += monomial_coefficients[i]*self.ambient().monomial(monomial_indices[i]) - return result + amb = PartitionAlgebra(self._k, self._q, self.base_ring(), prefix=self._prefix) + phi = self.module_morphism(lambda d: amb.monomial(d), + codomain=amb, category=self.category()) + phi.register_as_coercion() + return phi def retract(self, x): r""" Retract an appropriate partition algebra element to the - corresponding element in the partition subalgebra. This method - is not intended to be called directly. + corresponding element in the partition subalgebra. EXAMPLES:: @@ -1790,14 +1812,10 @@ def retract(self, x): sage: BA.retract(E) in BA True """ - if x not in self.ambient() or reduce(operator.mul, [(i in self._indices) for i in x.support()]) == 0: + if ( x not in self.ambient() + or any(i not in self._indices for i in x.support()) ): raise ValueError("{0} cannot retract to {1}".format(x, self)) - monomial_indices = x.support() - monomial_coefficients = x.coefficients() - result = self.zero() - for i in xrange(len(monomial_coefficients)): - result += monomial_coefficients[i]*self.monomial(monomial_indices[i]) - return result + return self._from_dict(x._monomial_coefficients, remove_zeros=False) class BrauerAlgebra(SubPartitionAlgebra): r""" @@ -1825,8 +1843,8 @@ class BrauerAlgebra(SubPartitionAlgebra): EXAMPLES: - We now define the Brauer algebra of rank `2` with parameter ``x`` over - `\ZZ`:: + We now define the Brauer algebra of rank `2` with parameter ``x`` + over `\ZZ`:: sage: R. = ZZ[] sage: B = BrauerAlgebra(2, x, R) @@ -1847,15 +1865,14 @@ class BrauerAlgebra(SubPartitionAlgebra): sage: b[2]^5 x^4*B{{-2, -1}, {1, 2}} - Note, also that since the symmetric group algebra is contained in the Brauer algebra, - there is also a coercion between the two. :: + Note, also that since the symmetric group algebra is contained in + the Brauer algebra, there is also a conversion between the two. :: sage: R. = ZZ[] - sage: B = BrauerAlgebra(2,x,R) - sage: S = SymmetricGroupAlgebra(R,2) + sage: B = BrauerAlgebra(2, x, R) + sage: S = SymmetricGroupAlgebra(R, 2) sage: S([2,1])*B([[1,-1],[2,-2]]) B{{-2, 1}, {-1, 2}} - """ global_options = BrauerDiagramOptions @@ -1887,9 +1904,7 @@ def __init__(self, k, q, base_ring, prefix): sage: BA = BrauerAlgebra(2, q, R) sage: TestSuite(BA).run() """ - KSS = SymmetricGroupAlgebra(base_ring, k) SubPartitionAlgebra.__init__(self, k, q, base_ring, prefix, BrauerDiagrams(k)) - KSS.module_morphism(lambda i : self(self._perm_to_Blst(i)), codomain=self).register_as_coercion() def _repr_(self): """ @@ -1905,6 +1920,32 @@ def _repr_(self): return "Brauer Algebra of rank {} with parameter {} over {}".format( self._k, self._q, self.base_ring()) + # TODO: Make a mixin class for diagram algebras that have coercions from SGA? + def _coerce_map_from_(self, R): + """ + Return a coerce map from ``R`` if one exists and ``None`` otherwise. + + EXAMPLES:: + + sage: R. = QQ[] + sage: S = SymmetricGroupAlgebra(R, 4) + sage: A = BrauerAlgebra(4, x, R) + sage: A._coerce_map_from_(S) + Generic morphism: + From: Symmetric group algebra of order 4 over Univariate Polynomial Ring in x over Rational Field + To: Brauer Algebra of rank 4 with parameter x over Univariate Polynomial Ring in x over Rational Field + sage: Sp = SymmetricGroupAlgebra(QQ, 4) + sage: A._coerce_map_from_(Sp) + Generic morphism: + From: Symmetric group algebra of order 4 over Rational Field + To: Brauer Algebra of rank 4 with parameter x over Univariate Polynomial Ring in x over Rational Field + """ + if isinstance(R, SymmetricGroupAlgebra_n): + if R.n == self._k and self.base_ring().has_coerce_map_from(R.base_ring()): + return R.module_morphism(self._perm_to_Blst, codomain=self) + return None + return super(BrauerAlgebra, self)._coerce_map_from_(R) + def _element_constructor_(self, set_partition): r""" Construct an element of ``self``. @@ -1923,8 +1964,6 @@ def _element_constructor_(self, set_partition): sage: BA([{1,2},{-1,-2}]) == b_elt True """ - if isinstance(set_partition, SymmetricGroupAlgebra_n.Element): - return DiagramAlgebra._element_constructor_(self, set_partition) set_partition = to_Brauer_partition(set_partition, k = self.order()) return DiagramAlgebra._element_constructor_(self, set_partition) @@ -2043,7 +2082,7 @@ def _element_constructor_(self, set_partition): sage: TL(S([2,1])) Traceback (most recent call last): ... - ValueError: invalid input of [set([1, -2]), set([2, -1])] + ValueError: {{-2, 1}, {-1, 2}} is not an index of a basis element """ if isinstance(set_partition, SymmetricGroupAlgebra_n.Element): return SubPartitionAlgebra._element_constructor_(self, set_partition) @@ -2205,7 +2244,7 @@ def __init__(self, k, q, base_ring, prefix): sage: TestSuite(I).run() # Not tested -- needs non-unital algebras category """ # This should be the category of non-unital fin-dim algebras with basis - category = Algebras(base_ring).FiniteDimensional().WithBasis() + category = Algebras(base_ring.category()).FiniteDimensional().WithBasis() SubPartitionAlgebra.__init__(self, k, q, base_ring, prefix, IdealDiagrams(k), category) From a87b14961e31957410948c8225febed4d66c8107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 12 Oct 2015 18:37:42 +0200 Subject: [PATCH 406/421] trac #19336 some more doctest for maxima lambert_w --- src/sage/functions/log.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index d75c7b2e2f1..a25b85f5976 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -721,6 +721,13 @@ def _maxima_init_evaled_(self, n, z): lambert_w(_SAGE_VAR_x) sage: lambert_w(1, x)._maxima_() generalized_lambert_w(1,_SAGE_VAR_x) + + TESTS:: + + sage: lambert_w(x)._maxima_()._sage_() + lambert_w(x) + sage: lambert_w(2, x)._maxima_()._sage_() + lambert_w(2, x) """ if isinstance(z, str): maxima_z = z From 6a17c58a5e6f13b8a55efd744521bc80d5828926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 12 Oct 2015 19:50:44 +0200 Subject: [PATCH 407/421] trac #16209 fixing now that seeds are not hashable --- src/sage/combinat/cluster_algebra_quiver/cluster_seed.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index 7c6ba32b392..96c45affd02 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -3976,9 +3976,10 @@ def oriented_exchange_graph(self): covers = [] n = self.n() stack = [self] - known_clusters = {} + known_clusters = [] while stack: i = stack.pop() + Vari = tuple(sorted(i.cluster())) B = i.b_matrix() for k in range(n): # check if green @@ -3986,10 +3987,10 @@ def oriented_exchange_graph(self): j = i.mutate(k, inplace=False) Varj = tuple(sorted(j.cluster())) if Varj in known_clusters: - covers.append((i, known_clusters[Varj])) + covers.append((Vari, Varj)) else: - covers.append((i, j)) - known_clusters[Varj] = j + covers.append((Vari, Varj)) + known_clusters += [Varj] stack.append(j) return DiGraph(covers) From 2f76a4e74dae5398094d7c41df80c58b9e6cb9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 12 Oct 2015 20:28:15 +0200 Subject: [PATCH 408/421] trac #16209 details --- src/sage/combinat/cluster_algebra_quiver/cluster_seed.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index 96c45affd02..dcff261ea1c 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -3986,10 +3986,8 @@ def oriented_exchange_graph(self): if all(B[i2][k] >= 0 for i2 in range(n, 2 * n)): j = i.mutate(k, inplace=False) Varj = tuple(sorted(j.cluster())) - if Varj in known_clusters: - covers.append((Vari, Varj)) - else: - covers.append((Vari, Varj)) + covers.append((Vari, Varj)) + if not(Varj in known_clusters): known_clusters += [Varj] stack.append(j) From 2485c770ee762b4bd8ca597507dd0bb909e2053f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 12 Oct 2015 13:32:39 -0500 Subject: [PATCH 409/421] Better iteration of RC's and a better doctest. --- .../rigged_configurations/kr_tableaux.py | 14 +++++------- .../rigged_configurations.py | 22 ++++--------------- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index 1637d297003..acf1b1c9c85 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -308,20 +308,16 @@ def __iter__(self): EXAMPLES:: - sage: KR = crystals.KirillovReshetikhin(['A', 3, 1], 2, 1, model='KR') - sage: g = KR.__iter__() - sage: next(g) - [[1], [2]] - sage: next(g) - [[1], [3]] - sage: next(g) - [[2], [3]] + sage: KR = crystals.KirillovReshetikhin(['A', 5, 2], 2, 1, model='KR') + sage: L = [x for x in KR] + sage: len(L) + 15 """ index_set = self._cartan_type.classical().index_set() from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet return RecursivelyEnumeratedSet(self.module_generators, lambda x: [x.f(i) for i in index_set], - structure=None).naive_search_iterator() + structure='graded').breadth_first_search_iterator() def module_generator(self, i=None, **options): r""" diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 86f5c38263d..abfba3bc94b 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -480,29 +480,15 @@ def __iter__(self): EXAMPLES:: sage: RC = RiggedConfigurations(['A', 3, 1], [[2,1], [1,1]]) - sage: g = RC.__iter__() - sage: next(g) - - (/) - - (/) - - (/) - - sage: next(g) # random - - 0[ ]0 - - 0[ ]0 - - (/) - + sage: L = [x for x in RC] + sage: len(L) + 24 """ index_set = self._cartan_type.classical().index_set() from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet return RecursivelyEnumeratedSet(self.module_generators, lambda x: [x.f(i) for i in index_set], - structure=None).naive_search_iterator() + structure='graded').breadth_first_search_iterator() @lazy_attribute def module_generators(self): From 1e97e1d5604f0119ebcbf5f599ff0047c79336a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Philippe=20Labb=C3=A9?= Date: Mon, 12 Oct 2015 21:33:46 +0200 Subject: [PATCH 410/421] Removed many blank spaces --- .../cluster_algebra_quiver/cluster_seed.py | 93 +++++++++---------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index dcff261ea1c..ad41db77a43 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -285,7 +285,7 @@ def __init__(self, data, frozen=None, is_principal=False, user_labels=None, user self._user_labels_prefix = user_labels_prefix # initialize the rest - + self._C = matrix.identity(self._n) self._use_c_vec = True @@ -300,7 +300,7 @@ def __init__(self, data, frozen=None, is_principal=False, user_labels=None, user self._mut_path = [ ] self._track_mut = True - + if user_labels: self._sanitize_init_vars(user_labels, user_labels_prefix) else: @@ -618,7 +618,7 @@ def use_fpolys(self, use=True, user_labels=None, user_labels_prefix=None): self._yhat = dict([ (self._U.gen(j),prod([self._R.gen(i)**self._M[i,j] for i in xrange(self._n+self._m)])) for j in xrange(self._n)]) elif self._cluster: raise ValueError("should not be possible to have cluster variables without f-polynomials") # added this as a sanity check. This error should never appear however. - elif self._track_mut == True: #If we can navigate from the root to where we are + elif self._track_mut == True: # If we can navigate from the root to where we are if not self._use_g_vec: self.use_g_vectors(True) catchup = ClusterSeed(self._b_initial, user_labels=user_labels, user_labels_prefix=user_labels_prefix) @@ -712,20 +712,20 @@ def track_mutations(self, use=True): def _sanitize_init_vars(self, user_labels, user_labels_prefix = 'x'): r""" Warning: This is an internal method that rewrites a user-given set of cluster variable names into a format that Sage can utilize. - + INPUT: - + - ``user_labels`` -- The labels that need sanitizing - ``user_labels_prefix`` -- (default:'x') The prefix to use for labels if integers given for labels - + EXAMPLES:: - + sage: S = ClusterSeed(['A',4]); S._init_vars {0: 'x0', 1: 'x1', 2: 'x2', 3: 'x3', 4: 'y0', 5: 'y1', 6: 'y2', 7: 'y3'} sage: S._sanitize_init_vars([1,2,3,4],'z') sage: S._init_vars {0: 'z1', 1: 'z2', 2: 'z3', 3: 'z4'} - + sage: S = ClusterSeed(['A',4]); S._init_vars {0: 'x0', 1: 'x1', 2: 'x2', 3: 'x3', 4: 'y0', 5: 'y1', 6: 'y2', 7: 'y3'} sage: S._sanitize_init_vars(['a', 'b', 'c', 'd']) @@ -828,13 +828,13 @@ def __eq__(self, other): True sage: S.mutate([0,1,0,1,0]) - sage: S.__eq__( T ) + sage: S.__eq__( T ) False sage: S.cluster() [x1, x0] sage: T.cluster() [x0, x1] - + sage: S.mutate([0,1,0,1,0]) sage: S.__eq__( T ) True @@ -1183,18 +1183,18 @@ def mutations(self): sage: S.mutate([0,1,0,2]) sage: S.mutations() [0, 1, 0, 2] - + sage: S.track_mutations(False) sage: S.mutations() Traceback (most recent call last): ... - ValueError: Not recording mutation sequence. Need to track mutations. + ValueError: Not recording mutation sequence. Need to track mutations. """ if self._track_mut: - return copy(self._mut_path) + return copy(self._mut_path) else: - raise ValueError("Not recording mutation sequence. Need to track mutations.") - + raise ValueError("Not recording mutation sequence. Need to track mutations.") + def cluster_variable(self, k): r""" Generates a cluster variable using F-polynomials @@ -1225,7 +1225,7 @@ def cluster_variable(self, k): else: raise ValueError('No cluster variable with index or label ' + str(k) + '.') elif self._track_mut: # if we can recreate the clusters - catchup = ClusterSeed(self._b_initial, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix) + catchup = ClusterSeed(self._b_initial, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix) catchup.use_c_vectors(use=self._use_c_vec, bot_is_c=self._bot_is_c) catchup.mutate(self.mutations()) return catchup.cluster_variable(k) @@ -1258,7 +1258,7 @@ def cluster(self): if not self._use_fpolys: if self._track_mut: # if we can recreate the clusters - catchup = ClusterSeed(self._b_initial, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix) + catchup = ClusterSeed(self._b_initial, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix) catchup.use_c_vectors(use=self._use_c_vec, bot_is_c=self._bot_is_c) catchup.mutate(self.mutations()) return catchup.cluster() @@ -1345,7 +1345,7 @@ def f_polynomial(self,k): return self._F[IE[k]] elif self._track_mut: - catchup = ClusterSeed(self._b_initial, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix) + catchup = ClusterSeed(self._b_initial, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix) catchup.use_c_vectors(use=self._use_c_vec, bot_is_c=self._bot_is_c) catchup.mutate(self.mutations()) @@ -1515,7 +1515,7 @@ def _g_mutate(self, k): J[j,k] += max(0, -eps*B[j,k]) J[k,k] = -1 self._G = self._G*J - + def c_vector(self,k): r""" Returns the ``k``-th *c-vector* of ``self``. It is obtained as the @@ -1631,7 +1631,7 @@ def d_vector(self, k): catchup.use_fpolys(False) catchup.use_g_vectors(False) catchup.use_c_vectors(False) - + catchup.mutate(self.mutations()) return copy(catchup._D).column(k) else: @@ -1640,7 +1640,7 @@ def d_vector(self, k): def d_matrix(self, show_warnings=True): r""" Returns the matrix of *d-vectors* of ``self``. - + EXAMPLES:: sage: S = ClusterSeed(['A',4]); S.d_matrix() [-1 0 0 0] @@ -1668,7 +1668,7 @@ def d_matrix(self, show_warnings=True): catchup.use_g_vectors(False) catchup.use_c_vectors(False) catchup.track_mutations(False) - + catchup.mutate(self.mutations()) return catchup.d_matrix() elif show_warnings: @@ -2301,7 +2301,7 @@ def mutate(self, sequence, inplace=True): # function should return either integer or sequence sequence = sequence(seed) - + if sequence is None: raise ValueError('Not mutating: No vertices given.') @@ -2312,17 +2312,17 @@ def mutate(self, sequence, inplace=True): n, m = seed.n(), seed.m() V = range(n)+IE - + if seed._use_fpolys and isinstance(sequence, str): sequence = seed.cluster_index(sequence) if sequence is None: raise ValueError("Variable provided is not in our cluster") - + if (sequence in xrange(n)) or (sequence in IE): seqq = [sequence] else: seqq = sequence - + if isinstance(seqq, tuple): @@ -2336,7 +2336,7 @@ def mutate(self, sequence, inplace=True): #raise ValueError('The quiver cannot be mutated at the vertex ' + str( v )) seq = iter(seqq) - + for k in seq: if k in xrange(n): @@ -2378,21 +2378,21 @@ def mutate(self, sequence, inplace=True): if not inplace: return seed - + def cluster_index(self, cluster_str): r""" Returns the index of a cluster if use_fpolys is on INPUT: - + - ``cluster_str`` -- The string to look for in the cluster - + OUTPUT: - + Returns an integer or None if the string is not a cluster variable - + EXAMPLES:: - + sage: S = ClusterSeed(['A',4],user_labels=['x','y','z','w']); S.mutate('x') sage: S.cluster_index('x') sage: S.cluster_index('(y+1)/x') @@ -2404,7 +2404,7 @@ def cluster_index(self, cluster_str): cluster_str = ClusterVariable( FractionField(self._R), c.numerator(), c.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable',xdim=self._n ) if cluster_str in self.cluster(): return self.cluster().index(cluster_str) - + return None def mutation_sequence(self, sequence, show_sequence=False, fig_size=1.2,return_output='seed'): @@ -2721,7 +2721,7 @@ def exchangeable_part(self): from sage.combinat.cluster_algebra_quiver.mutation_class import _principal_part eval_dict = dict( [ ( self.y(i), 1 ) for i in xrange(self._m) ] ) - seed = ClusterSeed( _principal_part( self._M ), is_principal = True, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix, frozen=None) + seed = ClusterSeed( _principal_part( self._M ), is_principal = True, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix, frozen=None) seed.use_c_vectors(self._use_c_vec) seed.use_fpolys(self._use_fpolys) seed.use_g_vectors(self._use_g_vec) @@ -2813,7 +2813,7 @@ def universal_extension(self): for alpha in almost_positive_coroots]) M = self._M.stack(C) - seed = ClusterSeed(M, is_principal = False, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix, frozen=None) + seed = ClusterSeed(M, is_principal = False, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix, frozen=None) seed.use_c_vectors(self._use_c_vec) seed.use_fpolys(self._use_fpolys) seed.use_g_vectors(self._use_g_vec) @@ -2847,7 +2847,7 @@ def principal_extension(self): [ 0 0 1 0 0] [ 0 0 0 1 0] [ 0 0 0 0 1] - + sage: S = ClusterSeed(['A',4],user_labels=['a','b','c','d']) sage: T= S.principal_extension() sage: T.cluster() @@ -2871,7 +2871,7 @@ def principal_extension(self): self._user_labels = self._user_labels + ['y%s'%i for i in xrange(self._n)] elif isinstance(self._user_labels,dict): self._user_labels.update( {(i+self._n):'y%s'%i for i in xrange(self._n)} ) - seed = ClusterSeed(M, is_principal = is_principal, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix, frozen=None) + seed = ClusterSeed(M, is_principal = is_principal, user_labels=self._user_labels, user_labels_prefix=self._user_labels_prefix, frozen=None) seed.use_c_vectors(self._use_c_vec) seed.use_fpolys(self._use_fpolys) seed.use_g_vectors(self._use_g_vec) @@ -2949,7 +2949,7 @@ def set_cluster( self, cluster, force=False ): sage: S.set_cluster(cluster2, force=True) sage: S.cluster() [x0, (x1 + 1)/x2, (x0*x2 + x1 + 1)/(x1*x2)] - + sage: S = ClusterSeed(['A',3]); S.use_fpolys(False) sage: S.set_cluster([1,1,1]) Warning: clusters not being tracked so this command is ignored. @@ -2966,7 +2966,7 @@ def set_cluster( self, cluster, force=False ): self._cluster = [ FractionField(self._R)(x) for x in cluster ][0:self._n] self._is_principal = None else: - print("Warning: clusters not being tracked so this command is ignored.") + print("Warning: clusters not being tracked so this command is ignored.") def reset_cluster( self ): r""" @@ -2993,12 +2993,12 @@ def reset_cluster( self ): sage: T.reset_cluster() sage: T.cluster() [x0, x1, x2] - + sage: S = ClusterSeed(['B',3],user_labels=[[1,2],[2,3],[3,4]],user_labels_prefix='p') sage: S.mutate([0,1]) sage: S.cluster() [(p_2_3 + 1)/p_1_2, (p_1_2*p_3_4^2 + p_2_3 + 1)/(p_1_2*p_2_3), p_3_4] - + sage: S.reset_cluster() sage: S.cluster() [p_1_2, p_2_3, p_3_4] @@ -3015,7 +3015,7 @@ def reset_cluster( self ): self._F = dict([(i,self._U(1)) for i in self._init_exch.values()]) if self._use_fpolys: self.set_cluster(self._R.gens(), force=True) - + def reset_coefficients( self ): r""" Resets the coefficients of ``self`` to the frozen variables but keeps the current cluster. @@ -3732,7 +3732,7 @@ def variable_class(self, depth=infinity, ignore_bipartite_belt=False): var_iter = self.variable_class_iter( depth=depth, ignore_bipartite_belt=ignore_bipartite_belt ) return sorted(var_iter) - def is_finite( self ): + def is_finite(self): r""" Returns True if ``self`` is of finite type. @@ -4164,9 +4164,9 @@ def get_green_vertices(C): INPUT: - ``C`` -- The C matrix to check - + EXAMPLES:: - + sage: from sage.combinat.cluster_algebra_quiver.cluster_seed import get_green_vertices sage: S = ClusterSeed(['A',4]); S.mutate([1,2,3,2,0,1,2,0,3]) sage: get_green_vertices(S.c_matrix()) @@ -4284,4 +4284,3 @@ def almost_positive_root( self ): return sum( [ root[i]*Phiplus[ i+1 ] for i in range(self._n) ] ) else: raise ValueError('The cluster algebra for %s is not of finite type.'%self._repr_()) - From d9f2c454bab76af892452cfefbf4b62790feb6c5 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 13 Oct 2015 08:27:18 +0200 Subject: [PATCH 411/421] 17624: fix previous commit --- src/sage/symbolic/ring.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 3891da5f05f..061951f8571 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -254,14 +254,14 @@ cdef class SymbolicRing(CommutativeRing): Polynomial ring element factorizations:: sage: R. = QQ[] - sage: SR(factor(x^2 - 1)) - (x + 1)*(x - 1) + sage: SR(factor(5*x^2 - 5)) + 5*(x + 1)*(x - 1) sage: R. = QQ[] sage: SR(factor(x^2 - y^2)) (x + y)*(x - y) sage: R. = QQ[] sage: SR(factor(x^2*y^3 + x^2*y^2*z - x*y^3 - x*y^2*z - 2*x*y*z - 2*x*z^2 + 2*y*z + 2*z^2)) - -(x*y^2 - 2*z)*(x - 1)*(y + z) + (x*y^2 - 2*z)*(x - 1)*(y + z) """ cdef GEx exp @@ -299,7 +299,7 @@ cdef class SymbolicRing(CommutativeRing): GEx_construct_pyobject(exp, x) elif isinstance(x, Factorization): from sage.misc.all import prod - return prod([SR(p)**e for p,e in x], SR.one()) + return prod([SR(p)**e for p,e in x], SR.one()) * x.unit() else: raise TypeError From 43ec64071da5ff5e5f5f700fe61b53f8fa11330e Mon Sep 17 00:00:00 2001 From: David Lucas Date: Tue, 13 Oct 2015 12:40:48 +0200 Subject: [PATCH 412/421] Added a check in unencode to raise an appropriate exception if a code of dimension 0 is used --- src/sage/coding/encoder.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 6569ba13c11..556810e9a7b 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -176,7 +176,21 @@ def unencode(self, c, nocheck=False): Traceback (most recent call last): ... EncodingError: Given word is not in the code + + If ones tries to unencode "a" codeword of a code of dimension 0, it + returns an error:: + + sage: G = Matrix(GF(17), []) + sage: C = LinearCode(G) + sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) + sage: c = C.random_element() + sage: E.unencode(c) + Traceback (most recent call last): + ... + EncodingError: Impossible to unencode a word of a code of dimension 0 """ + if self.code().ambient_space().dimension() == 0: + raise EncodingError("Impossible to unencode a word of a code of dimension 0") if nocheck == False and c not in self.code(): raise EncodingError("Given word is not in the code") return self.unencode_nocheck(c) From d09d3847fcf55f661e9be98cd4dfb3226e8d9a9d Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 13 Oct 2015 14:37:17 +0200 Subject: [PATCH 413/421] trac #19385: --- src/sage/graphs/digraph.py | 4 +++- src/sage/graphs/graph_input.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index ab6e5501944..d7e8f81a587 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -807,7 +807,9 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if data_structure == "static_sparse": from sage.graphs.base.static_sparse_backend import StaticSparseBackend - ib = StaticSparseBackend(self, loops = loops, multiedges = multiedges) + ib = StaticSparseBackend(self, + loops = self.allows_loops(), + multiedges = self.allows_multiple_edges()) self._backend = ib self._immutable = True diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index e2c07cf78aa..1a698b04610 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -435,6 +435,7 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv raise ValueError("Dict of dicts for multigraph must be in the format {v : {u : list}}") if multiedges is None and len(M) > 0: multiedges = True + G.allow_loops(loops, check=False) G.allow_multiple_edges(multiedges, check=False) verts = set().union(M.keys(), *M.values()) From ced36581c5258b40dc9e72a986fbaa9b7e12a8cc Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 13 Oct 2015 14:40:14 +0200 Subject: [PATCH 414/421] trac #19385: Typo T_T --- src/sage/graphs/graph_input.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 1a698b04610..3692458a517 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -398,7 +398,7 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv INPUT: - ``G`` -- a graph -x + - ``M`` -- a dictionary of dictionaries. - ``loops``, ``multiedges``, ``weighted`` (booleans) -- whether to consider From b89f01470aba338aad524fe71f173de1e3915d9f Mon Sep 17 00:00:00 2001 From: David Lucas Date: Tue, 13 Oct 2015 14:56:18 +0200 Subject: [PATCH 415/421] Unencoding from a code of dimension 0 now returns the empty vector --- src/sage/coding/encoder.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 556810e9a7b..09a21db1fa4 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -177,21 +177,20 @@ def unencode(self, c, nocheck=False): ... EncodingError: Given word is not in the code - If ones tries to unencode "a" codeword of a code of dimension 0, it - returns an error:: + If ones tries to unencode a codeword of a code of dimension 0, it + returns the empty vector:: sage: G = Matrix(GF(17), []) sage: C = LinearCode(G) sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: c = C.random_element() sage: E.unencode(c) - Traceback (most recent call last): - ... - EncodingError: Impossible to unencode a word of a code of dimension 0 + () """ - if self.code().ambient_space().dimension() == 0: - raise EncodingError("Impossible to unencode a word of a code of dimension 0") - if nocheck == False and c not in self.code(): + C = self.code() + if C.ambient_space().dimension() == 0: + return vector(C.base_ring(), 0) + if nocheck == False and c not in C: raise EncodingError("Given word is not in the code") return self.unencode_nocheck(c) From ed67ee233a40eefc5ce785596cdb83a6d0e62c63 Mon Sep 17 00:00:00 2001 From: David Lucas Date: Tue, 13 Oct 2015 17:14:53 +0200 Subject: [PATCH 416/421] Integrated reviewer's comments --- src/sage/coding/encoder.py | 4 ++-- src/sage/coding/encoders_catalog.py | 4 ++-- src/sage/coding/linear_code.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 09a21db1fa4..2eaa9241bd8 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -46,7 +46,7 @@ class Encoder(SageObject): by memory reference: if you build the same encoder twice, they will be different. If you need something more clever, override ``__eq__`` and ``__ne__`` in your subclass. - - As :class:`Encoder` is not designed to be instanciated, it does not have any representation + - As :class:`Encoder` is not designed to be instantiated, it does not have any representation methods. You should implement ``_repr_`` and ``_latex_`` methods in the sublclass. REFERENCES: @@ -99,7 +99,7 @@ def encode(self, word): .. NOTE:: - :meth:`encode` is a partial function over ``self``'s :meth:`message_space`. + :meth:`encode` might be a partial function over ``self``'s :meth:`message_space`. One should use the exception :class:`EncodingError` to catch attempts to encode words that are outside of the message space. diff --git a/src/sage/coding/encoders_catalog.py b/src/sage/coding/encoders_catalog.py index ec48db90837..36a0f84e34a 100644 --- a/src/sage/coding/encoders_catalog.py +++ b/src/sage/coding/encoders_catalog.py @@ -12,5 +12,5 @@ sage: from sage.coding.encoders_catalog import * """ -from sage.misc.lazy_import import lazy_import -lazy_import('sage.coding.linear_code', 'LinearCodeGeneratorMatrixEncoder') +from sage.misc.lazy_import import lazy_import as _lazy_import +_lazy_import('sage.coding.linear_code', 'LinearCodeGeneratorMatrixEncoder') diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 92c3f486fd6..f39e6e6d77c 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -740,7 +740,7 @@ class AbstractLinearCode(module.Module): .. WARNING:: The default encoder should always have `F^{k}` as message space, with `k` the dimension - of the code and `F` its base ring. + of the code and `F` is the base ring of the code. A lot of methods of the abstract class rely on the knowledge of a generator matrix. It is thus strongly recommended to set an encoder with a generator matrix implemented @@ -3741,4 +3741,4 @@ def generator_matrix(self): [1 1 0 1 0 0 1] """ return self.code().generator_matrix() -AbstractLinearCode._registered_encoders["GeneratorMatrix"] = LinearCodeGeneratorMatrixEncoder +LinearCode._registered_encoders["GeneratorMatrix"] = LinearCodeGeneratorMatrixEncoder From 09cff050d07c9d32120f3e81e1d8a721d04ed35b Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Tue, 13 Oct 2015 21:23:59 +0200 Subject: [PATCH 417/421] Alternative fix for the zero-dimensional unencoding bug --- src/sage/coding/encoder.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 2eaa9241bd8..d5051e7ccfe 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -187,10 +187,7 @@ def unencode(self, c, nocheck=False): sage: E.unencode(c) () """ - C = self.code() - if C.ambient_space().dimension() == 0: - return vector(C.base_ring(), 0) - if nocheck == False and c not in C: + if nocheck == False and c not in self.code(): raise EncodingError("Given word is not in the code") return self.unencode_nocheck(c) @@ -266,7 +263,7 @@ def unencode_nocheck(self, c): False """ U, info_set = self._unencoder_matrix() - cc = vector( c[i] for i in info_set ) + cc = vector(self.code().base_ring(), [c[i] for i in info_set]) return cc * U def code(self): From 23b24efe0f7355f312b5b38f9cf8d520240903a3 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 14 Oct 2015 14:24:34 +0200 Subject: [PATCH 418/421] 17624: convert factorization unit to SR --- src/sage/symbolic/ring.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 061951f8571..488373d04f3 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -299,7 +299,7 @@ cdef class SymbolicRing(CommutativeRing): GEx_construct_pyobject(exp, x) elif isinstance(x, Factorization): from sage.misc.all import prod - return prod([SR(p)**e for p,e in x], SR.one()) * x.unit() + return prod([SR(p)**e for p,e in x], SR.one()) * SR(x.unit()) else: raise TypeError From 9d1432aa35cf5005a6431c0911b2e96b0de615f7 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 14 Oct 2015 14:55:27 +0200 Subject: [PATCH 419/421] 17426: cosmetics --- src/sage/symbolic/ring.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 488373d04f3..c3adf6750b8 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -299,7 +299,7 @@ cdef class SymbolicRing(CommutativeRing): GEx_construct_pyobject(exp, x) elif isinstance(x, Factorization): from sage.misc.all import prod - return prod([SR(p)**e for p,e in x], SR.one()) * SR(x.unit()) + return prod([SR(p)**e for p,e in x], SR(x.unit()) else: raise TypeError From 9c461e3f14d5d48fffc2bb59ee17e37a8afff125 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 14 Oct 2015 14:59:05 +0200 Subject: [PATCH 420/421] 17426: fix typo --- src/sage/symbolic/ring.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index c3adf6750b8..439d02de32c 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -299,7 +299,7 @@ cdef class SymbolicRing(CommutativeRing): GEx_construct_pyobject(exp, x) elif isinstance(x, Factorization): from sage.misc.all import prod - return prod([SR(p)**e for p,e in x], SR(x.unit()) + return prod([SR(p)**e for p,e in x], SR(x.unit())) else: raise TypeError From 792aab7202f9e62d389c0dcb10c21ccfb5f24ec9 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Thu, 15 Oct 2015 20:26:53 +0200 Subject: [PATCH 421/421] Updated Sage version to 6.10.beta0 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- src/bin/sage-banner | 5 ++++- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 68733eba217..9d90d562953 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 6.9, released 2015-10-10 +Sage version 6.10.beta0, released 2015-10-15 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index ad47e649554..0c170545927 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=beb4de16a8e5a632516a6b5f63313e66c6770183 -md5=d6922f63e807c1d240582601e746dcb1 -cksum=2973216508 +sha1=3e0d10789b34d6f890e1575c2a06894a90e4807e +md5=020a9b7f31e61b57056969b6816455f1 +cksum=2662451870 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 078fa0fe576..52bd8e43afb 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -119 +120 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 955061b92e5..4a95ae46233 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,8 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath Version 6.9, Release Date: 2015-10-10 │ +│ SageMath Version 6.10.beta0, Release Date: 2015-10-15 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ +┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +┃ Warning: this is a prerelease version, and it may be unstable. ┃ +┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 7b35690365e..b8eaf7940b3 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='6.9' -SAGE_RELEASE_DATE='2015-10-10' +SAGE_VERSION='6.10.beta0' +SAGE_RELEASE_DATE='2015-10-15' diff --git a/src/sage/version.py b/src/sage/version.py index da4209e58b1..13ac1cb1b05 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '6.9' -date = '2015-10-10' +version = '6.10.beta0' +date = '2015-10-15'