From abcf6035436b731567bce1f2ed4d5c5fa7f0b6f0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 18 Dec 2013 19:48:20 -0800 Subject: [PATCH 001/151] Initial implementation of PT crystal. --- src/sage/combinat/crystals/all.py | 1 + .../crystals/crystal_partition_tuples.py | 257 ++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 src/sage/combinat/crystals/crystal_partition_tuples.py diff --git a/src/sage/combinat/crystals/all.py b/src/sage/combinat/crystals/all.py index 4bc8140ef53..6831d962782 100644 --- a/src/sage/combinat/crystals/all.py +++ b/src/sage/combinat/crystals/all.py @@ -18,3 +18,4 @@ from infinity_crystals import InfinityCrystalOfTableaux from elementary_crystals import TCrystal, RCrystal, ElementaryCrystal, ComponentCrystal from monomial_crystals import InfinityCrystalOfNakajimaMonomials, CrystalOfNakajimaMonomials +from crystal_partition_tuples import CrystalOfPartitionTuples diff --git a/src/sage/combinat/crystals/crystal_partition_tuples.py b/src/sage/combinat/crystals/crystal_partition_tuples.py new file mode 100644 index 00000000000..c1dc89a19a4 --- /dev/null +++ b/src/sage/combinat/crystals/crystal_partition_tuples.py @@ -0,0 +1,257 @@ +""" +Crystal of Multipartitions +""" +#***************************************************************************** +# Copyright (C) 2013 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#**************************************************************************** + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element_wrapper import ElementWrapper +from sage.categories.highest_weight_crystals import HighestWeightCrystals +from sage.categories.regular_crystals import RegularCrystals +from sage.rings.finite_rings.integer_mod_ring import IntegerModRing +from sage.rings.all import ZZ +from sage.combinat.partition_tuple import PartitionTuple +from sage.combinat.root_system.cartan_type import CartanType + +class CrystalOfPartitionTuples(Parent, UniqueRepresentation): + r""" + The type `A_n^{(1)}` highest weight crystal modelled by partitions + of tuples (a.k.a. multipartitions). + + We consider type `A_n^{(1)}` crystals, and let `r = (r_i \mid r_i \in + \ZZ / n \ZZ)` be a finite sequence of length `k` and + `\lambda = \sum_i \Lambda_{r_i}`. We will model the highest weight + crystal `B(\lambda)` by a particular subset of partition tuples of level + `k`. We define a regular crystal structure as follows: + + We create a word in the letters `A_i` and `R_i` by taking the partitions + (components) from right to left and reading from top to bottom in each + partition (in English notation) where an addable `i`-cell is an `A_i` and + a removable `i`-cell is an `R_i`. We then cancel pairs `R_i A_i`, so the + reduced word is of the form `A_i \cdots A_i R_i \cdots R_i`. Now `e_i` + (resp. `f_i`) acts by removing (resp. adding) the cell corresponding to + the left-most `R_i` (resp. right-most `A_i`) in the reduced word. + + The crystal `B(\lambda)` is the crystal generated by the empty partition + tuple. We can compute the weight of an element `\mu` by taking + `\lambda - \sum_{i=0}^n c_i \alpha_i` where `c_i` is the number of cells + of `n`-residue `i` in `\mu`. + + .. NOTE:: + + A partition tuple in the crystal is known as a *Kleshchev* + partition tuple. + + that + + INPUT: + + - ``n`` -- for type `A_n^{(1)}` + - ``r`` -- the sequence `r` + + EXAMPLES:: + + C = CrystalOf + """ + @staticmethod + def __classcall_private__(cls, n, r): + """ + Normalize input to ensure a uniqure representation. + + EXAMPLES:: + + sage: C1 = CrystalOfPartitionTuples(2, [0,2]) + sage: C2 = CrystalOfPartitionTuples(2, (0,2)) + sage: C1 is C2 + True + """ + if r in ZZ: + r = [r] + M = IntegerModRing(n+1) + r = tuple(map(M, r)) + return super(CrystalOfPartitionTuples, cls).__classcall__(cls, n, r) + + def __init__(self, n, r): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: TestSuite(C).run(skip="_test_stembridge_local_axioms") + sage: C._test_stembridge_local_axioms() # long time + """ + self._cartan_type = CartanType(['A', n, 1]) + self._r = r + Parent.__init__(self, category=(HighestWeightCrystals(), RegularCrystals())) + self.module_generators = (self.element_class(self, PartitionTuple([[]]*len(r))),) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: CrystalOfPartitionTuples(2, [0,2]) + The crystal of multipartitions of type ['A', 2, 1] and residues (0, 2) + """ + return "The crystal of multipartitions of type {} and residues {}".format(self._cartan_type, self._r) + + class Element(ElementWrapper): + """ + An element in the multipartition crystal. + """ + def e(self, i): + r""" + Return the action of `e_i` on ``self``. + + EXAMPLES:: + + sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) + sage: x.e(2) + ([5, 4, 1], [3, 1, 1, 1]) + """ + rem_cells = [] + rs = self.parent()._r + rev_enum = lambda l: reversed(list(enumerate(l))) + + for j,p in rev_enum(self.value.components()): + r = rs[j] + for a,v in enumerate(p): + res = v - a + r + if res - 1 == i and (a >= len(p)-1 or p[a] > p[a+1]): # Removable + rem_cells.append((j,a,v-1)) + elif res == i and len(rem_cells) > 0 and (a == 0 or p[a-1] > p[a]) > 0: # Addable + rem_cells.pop() + if r - len(p) == i and len(rem_cells) > 0: # Last addable cell + rem_cells.pop() + + if len(rem_cells) == 0: + return None + c = rem_cells[0] + if len(rs) == 1: # Special case when it is a single partition + c = c[1:] + return self.__class__(self.parent(), self.value.remove_cell(*c)) + + def f(self, i): + r""" + Return the action of `f_i` on ``self``. + + EXAMPLES:: + + sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) + sage: x.e(2) + ([5, 4, 1], [3, 1, 1, 1]) + sage: x.f(2) + ([5, 4, 1], [4, 2, 1, 1]) + """ + add_cells = [] + rs = self.parent()._r + rev_enum = lambda l: reversed(list(enumerate(l))) + + for j,p in enumerate(self.value.components()): + r = rs[j] + if r - len(p) == i: # Last addable cell + add_cells.append((j,len(p),0)) + for a,v in rev_enum(p): + res = v - a + r + if res == i and (a == 0 or p[a-1] > p[a]): # Addable + add_cells.append((j,a,v)) + elif res - 1 == i and len(add_cells) > 0 and (a >= len(p)-1 or p[a] > p[a+1]): # Removable + add_cells.pop() + + if len(add_cells) == 0: + return None + c = add_cells[0] + if len(rs) == 1: # Special case when it is a single partition + c = c[1:] + return self.__class__(self.parent(), self.value.add_cell(*c)) + + def epsilon(self, i): + r""" + Return `\epsilon_i` of ``self``. + + EXAMPLES:: + + sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) + sage: x.epsilon(2) + 1 + """ + ep = 0 + rs = self.parent()._r + rev_enum = lambda l: reversed(list(enumerate(l))) + for j,p in rev_enum(self.value.components()): + r = rs[j] + for a,v in enumerate(p): + res = v - a + r + if res - 1 == i and (a >= len(p)-1 or p[a] > p[a+1]): # Addable + ep += 1 + elif res == i and ep > 0 and (a == 0 or p[a-1] > p[a]): # Removable + ep -= 1 + if r - len(p) == i and ep > 0: # Last addable cell + ep -= 1 + return ep + + def phi(self, i): + r""" + Return `\phi_i` of ``self``. + + EXAMPLES:: + + sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) + sage: x.phi(2) + 1 + """ + phi = 0 + rs = self.parent()._r + rev_enum = lambda l: reversed(list(enumerate(l))) + for j,p in enumerate(self.value.components()): + r = rs[j] + if r - len(p) == i: # Last addable cell + phi += 1 + for a,v in rev_enum(p): + res = v - a + r + if res == i and (a == 0 or p[a-1] > p[a]): # Removable + phi += 1 + elif res - 1 == i and phi > 0 and (a >= len(p)-1 or p[a] > p[a+1]): # Addable + phi -= 1 + return phi + + def weight(self): + """ + Return the weight of ``self``. + + EXAMPLES:: + + sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) + sage: x.weight() + 3*Lambda[0] - Lambda[1] + sage: x.Phi() - x.Epsilon() + 3*Lambda[0] - Lambda[1] + """ + WLR = self.parent().weight_lattice_realization() + alpha = WLR.simple_roots() + La = WLR.fundamental_weights() + r = self.parent()._r + wt = sum(La[ZZ(x)] for x in r) + pt = self.value + return wt - sum(alpha[pt.content(*c, multicharge=r)] for c in pt.cells()) + From abec6668c24ae82ed863d0c3cc847e8709ceb849 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 19 Dec 2013 13:45:23 -0800 Subject: [PATCH 002/151] Started affiniziation. --- src/doc/en/reference/combinat/crystals.rst | 1 + src/sage/combinat/crystals/affinization.py | 123 +++++++++++++++++ src/sage/combinat/crystals/all.py | 3 +- ...ystal_partition_tuples.py => kleshchev.py} | 125 +++++++++++++----- 4 files changed, 217 insertions(+), 35 deletions(-) create mode 100644 src/sage/combinat/crystals/affinization.py rename src/sage/combinat/crystals/{crystal_partition_tuples.py => kleshchev.py} (69%) diff --git a/src/doc/en/reference/combinat/crystals.rst b/src/doc/en/reference/combinat/crystals.rst index b5c219cf189..370d7bf4d7c 100644 --- a/src/doc/en/reference/combinat/crystals.rst +++ b/src/doc/en/reference/combinat/crystals.rst @@ -12,6 +12,7 @@ Crystals ../sage/combinat/crystals/fast_crystals ../sage/combinat/crystals/highest_weight_crystals ../sage/combinat/crystals/kirillov_reshetikhin + ../sage/combinat/crystals/kleshchev ../sage/combinat/crystals/kyoto_path_model ../sage/combinat/crystals/littelmann_path ../sage/combinat/crystals/alcove_path diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py new file mode 100644 index 00000000000..4321dca6fb2 --- /dev/null +++ b/src/sage/combinat/crystals/affinization.py @@ -0,0 +1,123 @@ +r""" +Affinization Crystals +""" + +#***************************************************************************** +# Copyright (C) 2013 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#**************************************************************************** + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element import Element +from sage.categories.regular_crystals import RegularCrystals +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.rings.finite_rings.integer_mod_ring import IntegerModRing +from sage.rings.all import ZZ +from sage.combinat.partition_tuple import PartitionTuple +from sage.combinat.root_system.cartan_type import CartanType + +class AffinizationCrystal(Parent, UniqueRepresentation): + r""" + An affiniziation crystal. + + Let `\mathfrak{g}` be of affine type. An affinization crystal is a + `U_q(\mathfrak{g})`-crystal formed from a + `U_q^{\prime}(\mathfrak{g})`-crystal `B` by taking the set: + + .. MATH:: + + B^{aff} = \{ b(m) \mid b \in B, m \in \ZZ \} + + and defining the crystal structure by: + + .. MATH:: + + \begin{aligned} + e_i(b(m)) & = \begin{cases} (e_0 b)(m+1) & i = 0 + \\ (e_i b)(m) & i \neq 0 \end{cases} \\ + + \\ f_i(b(m)) & = \begin{cases} (f_0 b)(m-1) & i = 0 + \\ (f_i b)(m) & i \neq 0 \end{cases} + + \\ \mathrm{wt}(b(m)) & = \wt(b) + m \delta. + \end{aligned} + """ + def __init__(self, B): + """ + Initialize ``self``. + """ + self._B = B + Parent.__init__(self, category=(RegularCrystals(), InfiniteEnumeratedSets())) + self.moodule_generators = (self.element_class(self, self._B.module_generator(), 0),) + + def _repr_(self): + """ + Return a string representation of ``self``. + """ + return "Affinization of {}".format(self._B) + + def weight_lattice_realization(self): + """ + Return the weight lattice realization of ``self``. + """ + return self.cartan_type().ambient_space() + + class Element(Element): + """ + An element in an affinization crystal. + """ + def __init__(self, parent b, m): + """ + Initialize ``self``. + """ + self._b = b + self._m = m + Element.__init__(self, parent) + + def e(self, i): + """ + Return the action of `e_i` on ``self``. + """ + if i == 0: + return self.__class__(self.parent(), self._b.e(0), self._m+1) + return self.__class__(self.parent(), self._b.e(i), self._m) + + def f(self, i): + """ + Return the action of `f_i` on ``self``. + """ + if i == 0: + return self.__class__(self.parent(), self._b.f(0), self._m+1) + return self.__class__(self.parent(), self._b.f(i), self._m) + + def epsilon(self, i): + r""" + Return `\varepsilon_i` of ``self``. + """ + return self._b.epsilon(i) + + def phi(self, i): + r""" + Return `\varphi_i` of ``self``. + """ + return self._b.phi(i) + + def weight(self): + r""" + Return the weight of ``self``. + """ + WLR = self.parent().weight_lattice_realization() + La = WLR.fundamental_weights() + return WLR.sum(c*La[i] for i,c in self._b.weight()) + self._m * WLR.null_root() + diff --git a/src/sage/combinat/crystals/all.py b/src/sage/combinat/crystals/all.py index 6831d962782..9a52f7378ee 100644 --- a/src/sage/combinat/crystals/all.py +++ b/src/sage/combinat/crystals/all.py @@ -18,4 +18,5 @@ from infinity_crystals import InfinityCrystalOfTableaux from elementary_crystals import TCrystal, RCrystal, ElementaryCrystal, ComponentCrystal from monomial_crystals import InfinityCrystalOfNakajimaMonomials, CrystalOfNakajimaMonomials -from crystal_partition_tuples import CrystalOfPartitionTuples +from kleshchev import KleshchevCrystal + diff --git a/src/sage/combinat/crystals/crystal_partition_tuples.py b/src/sage/combinat/crystals/kleshchev.py similarity index 69% rename from src/sage/combinat/crystals/crystal_partition_tuples.py rename to src/sage/combinat/crystals/kleshchev.py index c1dc89a19a4..0480ffec79a 100644 --- a/src/sage/combinat/crystals/crystal_partition_tuples.py +++ b/src/sage/combinat/crystals/kleshchev.py @@ -1,5 +1,5 @@ """ -Crystal of Multipartitions +Kleshchev Crystals """ #***************************************************************************** # Copyright (C) 2013 Travis Scrimshaw @@ -26,16 +26,16 @@ from sage.combinat.partition_tuple import PartitionTuple from sage.combinat.root_system.cartan_type import CartanType -class CrystalOfPartitionTuples(Parent, UniqueRepresentation): +class KleshchevCrystal(Parent, UniqueRepresentation): r""" - The type `A_n^{(1)}` highest weight crystal modelled by partitions - of tuples (a.k.a. multipartitions). + The Kleshchev crystal. We consider type `A_n^{(1)}` crystals, and let `r = (r_i \mid r_i \in - \ZZ / n \ZZ)` be a finite sequence of length `k` and - `\lambda = \sum_i \Lambda_{r_i}`. We will model the highest weight - crystal `B(\lambda)` by a particular subset of partition tuples of level - `k`. We define a regular crystal structure as follows: + \ZZ / n \ZZ)` be a finite sequence of length `k` (often called the level) + and `\lambda = \sum_i \Lambda_{r_i}`. We will model the highest weight + `U_q(\mathfrak{g})`-crystal `B(\lambda)` by a particular subset of + partition tuples of level `k`. We define a regular crystal structure + as follows: We create a word in the letters `A_i` and `R_i` by taking the partitions (components) from right to left and reading from top to bottom in each @@ -45,26 +45,83 @@ class CrystalOfPartitionTuples(Parent, UniqueRepresentation): (resp. `f_i`) acts by removing (resp. adding) the cell corresponding to the left-most `R_i` (resp. right-most `A_i`) in the reduced word. - The crystal `B(\lambda)` is the crystal generated by the empty partition - tuple. We can compute the weight of an element `\mu` by taking + The Kleshchev crystal `B(\lambda)` is the crystal generated by the empty + partition tuple. We can compute the weight of an element `\mu` by taking `\lambda - \sum_{i=0}^n c_i \alpha_i` where `c_i` is the number of cells - of `n`-residue `i` in `\mu`. - - .. NOTE:: - - A partition tuple in the crystal is known as a *Kleshchev* - partition tuple. - - that + of `n`-residue `i` in `\mu`. Partition tuples in the crystal are known + as `r`-Kleshchev partition tuples, and if `r = (r_1)`, then the partitions + are known as *Kleshchev* partitions. INPUT: - ``n`` -- for type `A_n^{(1)}` - ``r`` -- the sequence `r` - EXAMPLES:: - - C = CrystalOf + EXAMPLES: + + We first do an example of a level 1 crystal:: + + sage: C = KleshchevCrystal(2, [0]) + sage: C + The crystal of multipartitions of type ['A', 2, 1] and residues (0,) + sage: mg = C.module_generators[0] + sage: mg + [] + sage: mg.f(0) + [1] + sage: mg.f(1) + sage: mg.f(2) + sage: mg.f_string([0,2,1,0]) + [1, 1, 1, 1] + sage: mg.f_string([0,1,2,0]) + [2, 2] + sage: S = C.subcrystal(max_depth=5) + sage: G = C.digraph(subset=S) + sage: B = CrystalOfLSPaths(['A',2,1], [1,0,0]) + sage: SB = B.subcrystal(max_depth=5) + sage: GB = B.digraph(subset=SB) + sage: G.is_isomorphic(GB, edge_labels=True) + True + + Now a higher level crystal:: + + sage: C = KleshchevCrystal(2, [0,2]) + sage: mg = C.module_generators[0] + sage: mg + ([], []) + sage: mg.f(0) + ([1], []) + sage: mg.f(2) + ([], [1]) + sage: mg.f_string([0,1,2,0]) + ([2, 2], []) + sage: mg.f_string([0,2,1,0]) + ([1, 1, 1, 1], []) + sage: mg.f_string([2,0,1,0]) + ([2], [2]) + sage: S = C.subcrystal(max_depth=5) + sage: G = C.digraph(subset=S) + sage: B = CrystalOfLSPaths(['A',2,1], [1,0,1]) + sage: SB = B.subcrystal(max_depth=5) + sage: GB = B.digraph(subset=SB) + sage: G.is_isomorphic(GB, edge_labels=True) + True + + The ordering of the residues gives a different representation of the + higher level crystals (but it is still isomorphic):: + + sage: C2 = KleshchevCrystal(2, [2,0]) + sage: mg2 = C2.highest_weight_vector() + sage: mg2.f_string([0,1,2,0]) + ([2], [2]) + sage: mg2.f_string([0,2,1,0]) + ([1, 1, 1], [1]) + sage: mg2.f_string([2,0,1,0]) + ([2, 1], [1]) + sage: S2 = C2.subcrystal(max_depth=5) + sage: G2 = C2.digraph(subset=S) + sage: G.is_isomorphic(G2, edge_labels=True) + True """ @staticmethod def __classcall_private__(cls, n, r): @@ -73,8 +130,8 @@ def __classcall_private__(cls, n, r): EXAMPLES:: - sage: C1 = CrystalOfPartitionTuples(2, [0,2]) - sage: C2 = CrystalOfPartitionTuples(2, (0,2)) + sage: C1 = KleshchevCrystal(2, [0,2]) + sage: C2 = KleshchevCrystal(2, (0,2)) sage: C1 is C2 True """ @@ -82,7 +139,7 @@ def __classcall_private__(cls, n, r): r = [r] M = IntegerModRing(n+1) r = tuple(map(M, r)) - return super(CrystalOfPartitionTuples, cls).__classcall__(cls, n, r) + return super(KleshchevCrystal, cls).__classcall__(cls, n, r) def __init__(self, n, r): """ @@ -90,7 +147,7 @@ def __init__(self, n, r): EXAMPLES:: - sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: C = KleshchevCrystal(2, [0,2]) sage: TestSuite(C).run(skip="_test_stembridge_local_axioms") sage: C._test_stembridge_local_axioms() # long time """ @@ -105,7 +162,7 @@ def _repr_(self): EXAMPLES:: - sage: CrystalOfPartitionTuples(2, [0,2]) + sage: KleshchevCrystal(2, [0,2]) The crystal of multipartitions of type ['A', 2, 1] and residues (0, 2) """ return "The crystal of multipartitions of type {} and residues {}".format(self._cartan_type, self._r) @@ -120,7 +177,7 @@ def e(self, i): EXAMPLES:: - sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: C = KleshchevCrystal(2, [0,2]) sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) sage: x.e(2) ([5, 4, 1], [3, 1, 1, 1]) @@ -153,7 +210,7 @@ def f(self, i): EXAMPLES:: - sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: C = KleshchevCrystal(2, [0,2]) sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) sage: x.e(2) ([5, 4, 1], [3, 1, 1, 1]) @@ -184,11 +241,11 @@ def f(self, i): def epsilon(self, i): r""" - Return `\epsilon_i` of ``self``. + Return `\varepsilon_i` of ``self``. EXAMPLES:: - sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: C = KleshchevCrystal(2, [0,2]) sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) sage: x.epsilon(2) 1 @@ -210,11 +267,11 @@ def epsilon(self, i): def phi(self, i): r""" - Return `\phi_i` of ``self``. + Return `\varphi_i` of ``self``. EXAMPLES:: - sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: C = KleshchevCrystal(2, [0,2]) sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) sage: x.phi(2) 1 @@ -240,7 +297,7 @@ def weight(self): EXAMPLES:: - sage: C = CrystalOfPartitionTuples(2, [0,2]) + sage: C = KleshchevCrystal(2, [0,2]) sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) sage: x.weight() 3*Lambda[0] - Lambda[1] @@ -251,7 +308,7 @@ def weight(self): alpha = WLR.simple_roots() La = WLR.fundamental_weights() r = self.parent()._r - wt = sum(La[ZZ(x)] for x in r) pt = self.value + wt = WLR.sum(La[ZZ(x)] for x in r) return wt - sum(alpha[pt.content(*c, multicharge=r)] for c in pt.cells()) From 5f6d72801c909201cfbb2834cec5eeff02789405 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 20 Dec 2013 07:18:45 -0800 Subject: [PATCH 003/151] Work on affinization crystal. --- src/sage/combinat/crystals/affinization.py | 37 +++++++++++--- .../combinat/crystals/kirillov_reshetikhin.py | 13 +++++ .../rigged_configurations/kr_tableaux.py | 48 +++++++++++++++---- 3 files changed, 83 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py index 4321dca6fb2..2d1669de208 100644 --- a/src/sage/combinat/crystals/affinization.py +++ b/src/sage/combinat/crystals/affinization.py @@ -52,14 +52,20 @@ class AffinizationCrystal(Parent, UniqueRepresentation): \\ \mathrm{wt}(b(m)) & = \wt(b) + m \delta. \end{aligned} + + EXAMPLES:: + + sage: K = KirillovReshetikhinCrystal(['A',2,1], 1, 1) + sage: A = K.affinization() """ def __init__(self, B): """ Initialize ``self``. """ self._B = B + self._cartan_type = B.cartan_type() Parent.__init__(self, category=(RegularCrystals(), InfiniteEnumeratedSets())) - self.moodule_generators = (self.element_class(self, self._B.module_generator(), 0),) + self.module_generators = (self.element_class(self, self._B.module_generator(), 0),) def _repr_(self): """ @@ -77,7 +83,7 @@ class Element(Element): """ An element in an affinization crystal. """ - def __init__(self, parent b, m): + def __init__(self, parent, b, m): """ Initialize ``self``. """ @@ -85,21 +91,40 @@ def __init__(self, parent b, m): self._m = m Element.__init__(self, parent) + def _repr_(self): + """ + Return a string representation of ``self``. + """ + return "{}({})".format(self._b, self._m) + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + """ + from sage.misc.latex import latex + return latex(self._b) + "({})".format(self._m) + def e(self, i): """ Return the action of `e_i` on ``self``. """ + bp = self._b.e(i) + if bp is None: + return None if i == 0: - return self.__class__(self.parent(), self._b.e(0), self._m+1) - return self.__class__(self.parent(), self._b.e(i), self._m) + return self.__class__(self.parent(), bp, self._m+1) + return self.__class__(self.parent(), bp, self._m) def f(self, i): """ Return the action of `f_i` on ``self``. """ + bp = self._b.f(i) + if bp is None: + return None if i == 0: - return self.__class__(self.parent(), self._b.f(0), self._m+1) - return self.__class__(self.parent(), self._b.f(i), self._m) + return self.__class__(self.parent(), bp, self._m+1) + return self.__class__(self.parent(), bp, self._m) def epsilon(self, i): r""" diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index 9658232eeae..a5a62fff45c 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -628,6 +628,19 @@ def kirillov_reshetikhin_tableaux(self): from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux return KirillovReshetikhinTableaux(self.cartan_type(), self._r, self._s) + def affinization(self): + """ + Return the corresponding affinization crystal of ``self``. + + EXAMPLES:: + + sage: K = KirillovReshetikhinCrystal(['A',2,1], 1, 1) + sage: K.affinization() + Affinization of Kirillov-Reshetikhin crystal of type ['A', 2, 1] with (r,s)=(1,1) + """ + from sage.combinat.crystals.affinization import AffinizationCrystal + return AffinizationCrystal(self) + class KirillovReshetikhinGenericCrystalElement(AffineCrystalFromClassicalElement): """ Abstract class for all Kirillov-Reshetikhin crystal elements. diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index 48ae733f5d7..1620d45a95f 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -320,16 +320,19 @@ def module_generator(self, i=None, **options): INPUT: - - ``i`` -- The index of the module generator + - ``i`` -- the index of the module generator We can also get a module generator by using one of the following optional arguments: - - ``shape`` -- The associated shape - - ``column_shape`` -- The shape given as columns (a column of length + - ``shape`` -- the associated shape + - ``column_shape`` -- the shape given as columns (a column of length `k` correspond to a classical weight `\omega_k`) - - ``weight`` -- The weight - - ``classical_weight`` -- The classical weight + - ``weight`` -- the weight + - ``classical_weight`` -- the classical weight + + If no arugments are specified, then return the unique module generator + of classical weight `s \Lambda_r`. EXAMPLES:: @@ -346,6 +349,8 @@ def module_generator(self, i=None, **options): sage: WSC = RootSystem(['D',4]).weight_space() sage: KRT.module_generator(classical_weight=WSC.fundamental_weight(2)) [[1, 1], [2, -1]] + sage: KRT.module_generator() + [[1, 1], [2, 2]] sage: KRT = KirillovReshetikhinTableaux(['A', 3, 1], 2, 2) sage: KRT.module_generator() @@ -354,6 +359,7 @@ def module_generator(self, i=None, **options): if i is not None: return self.module_generators[i] n = self._cartan_type.classical().rank() + if "shape" in options: shape = list(options["shape"]) # Make sure the shape is the correct length @@ -363,29 +369,40 @@ def module_generator(self, i=None, **options): if list(mg.classical_weight().to_vector()) == shape: return mg return None + if "column_shape" in options: shape = list(Partition(options["column_shape"]).conjugate()) - if len(shape) < self._cartan_type.classical().rank(): + if len(shape) < n: shape.extend( [0]*(n - len(shape)) ) for mg in self.module_generators: if list(mg.classical_weight().to_vector()) == shape: return mg return None + if "weight" in options: wt = options["weight"] for mg in self.module_generators: if mg.weight() == wt: return mg return None + if "classical_weight" in options: wt = options["classical_weight"] for mg in self.module_generators: if mg.classical_weight() == wt: return mg return None - if len(self.module_generators) == 1: - return self.module_generators[0] - raise ValueError("Invalid parameter") + + # Otherwise return the unique module generator of classical weight `s \Lambda_r` + R = self.weight_lattice_realization() + Lambda = R.fundamental_weights() + r = self.r() + s = self.s() + weight = s*Lambda[r] - s*Lambda[0] * Lambda[r].level() / Lambda[0].level() + for b in self.module_generators: + if b.weight() == weight: + return b + assert False @abstract_method def _build_module_generators(self): @@ -475,6 +492,19 @@ def kirillov_reshetikhin_crystal(self): """ return KirillovReshetikhinCrystal(self._cartan_type, self._r, self._s) + def affinization(self): + """ + Return the corresponding affinization crystal of ``self``. + + EXAMPLES:: + + sage: K = KirillovReshetikhinTableaux(['A',2,1], 1, 1) + sage: K.affinization() + Affinization of Kirillov-Reshetikhin tableaux of type ['A', 2, 1] and shape (1, 1) + """ + from sage.combinat.crystals.affinization import AffinizationCrystal + return AffinizationCrystal(self) + class KRTableauxRectangle(KirillovReshetikhinTableaux): r""" Kirillov-Reshetkhin tableaux `B^{r,s}` whose module generator is a single From 9133fb10be7c85da90a6711a58c01693c18ca984 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 20 Dec 2013 10:47:18 -0800 Subject: [PATCH 004/151] Added multisegments crystals and added documentation. --- src/doc/en/reference/combinat/crystals.rst | 2 + src/sage/combinat/crystals/affinization.py | 24 ++- src/sage/combinat/crystals/all.py | 1 + src/sage/combinat/crystals/kleshchev.py | 37 ++++- src/sage/combinat/crystals/multisegments.py | 173 ++++++++++++++++++++ 5 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 src/sage/combinat/crystals/multisegments.py diff --git a/src/doc/en/reference/combinat/crystals.rst b/src/doc/en/reference/combinat/crystals.rst index 370d7bf4d7c..f4aef6e8102 100644 --- a/src/doc/en/reference/combinat/crystals.rst +++ b/src/doc/en/reference/combinat/crystals.rst @@ -5,6 +5,7 @@ Crystals :maxdepth: 2 ../sage/combinat/crystals/affine + ../sage/combinat/crystals/affinization ../sage/combinat/crystals/crystals ../sage/combinat/crystals/direct_sum ../sage/combinat/crystals/letters @@ -20,5 +21,6 @@ Crystals ../sage/combinat/crystals/tensor_product ../sage/combinat/crystals/generalized_young_walls ../sage/combinat/crystals/infinity_crystals + ../sage/combinat/crystals/multisegments ../sage/combinat/crystals/monomial_crystals diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py index 2d1669de208..c18d98d3122 100644 --- a/src/sage/combinat/crystals/affinization.py +++ b/src/sage/combinat/crystals/affinization.py @@ -22,10 +22,6 @@ from sage.structure.element import Element from sage.categories.regular_crystals import RegularCrystals from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.rings.finite_rings.integer_mod_ring import IntegerModRing -from sage.rings.all import ZZ -from sage.combinat.partition_tuple import PartitionTuple -from sage.combinat.root_system.cartan_type import CartanType class AffinizationCrystal(Parent, UniqueRepresentation): r""" @@ -57,6 +53,10 @@ class AffinizationCrystal(Parent, UniqueRepresentation): sage: K = KirillovReshetikhinCrystal(['A',2,1], 1, 1) sage: A = K.affinization() + + REFERENCES: + + - [HK02]_ """ def __init__(self, B): """ @@ -107,6 +107,10 @@ def _latex_(self): def e(self, i): """ Return the action of `e_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index se """ bp = self._b.e(i) if bp is None: @@ -118,6 +122,10 @@ def e(self, i): def f(self, i): """ Return the action of `f_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set """ bp = self._b.f(i) if bp is None: @@ -129,12 +137,20 @@ def f(self, i): def epsilon(self, i): r""" Return `\varepsilon_i` of ``self``. + + INPUT: + + - ``i`` -- an element of the index set """ return self._b.epsilon(i) def phi(self, i): r""" Return `\varphi_i` of ``self``. + + INPUT: + + - ``i`` -- an element of the index set """ return self._b.phi(i) diff --git a/src/sage/combinat/crystals/all.py b/src/sage/combinat/crystals/all.py index 9a52f7378ee..be544055a56 100644 --- a/src/sage/combinat/crystals/all.py +++ b/src/sage/combinat/crystals/all.py @@ -19,4 +19,5 @@ from elementary_crystals import TCrystal, RCrystal, ElementaryCrystal, ComponentCrystal from monomial_crystals import InfinityCrystalOfNakajimaMonomials, CrystalOfNakajimaMonomials from kleshchev import KleshchevCrystal +from multisegments import InfinityCrystalOfMultisegments diff --git a/src/sage/combinat/crystals/kleshchev.py b/src/sage/combinat/crystals/kleshchev.py index 0480ffec79a..50f913472e7 100644 --- a/src/sage/combinat/crystals/kleshchev.py +++ b/src/sage/combinat/crystals/kleshchev.py @@ -1,5 +1,5 @@ """ -Kleshchev Crystals +Kleshchev Partition (Tuple) Crystals """ #***************************************************************************** # Copyright (C) 2013 Travis Scrimshaw @@ -28,7 +28,7 @@ class KleshchevCrystal(Parent, UniqueRepresentation): r""" - The Kleshchev crystal. + The Kleshchev partition (tuple) crystal. We consider type `A_n^{(1)}` crystals, and let `r = (r_i \mid r_i \in \ZZ / n \ZZ)` be a finite sequence of length `k` (often called the level) @@ -52,6 +52,12 @@ class KleshchevCrystal(Parent, UniqueRepresentation): as `r`-Kleshchev partition tuples, and if `r = (r_1)`, then the partitions are known as *Kleshchev* partitions. + .. NOTE:: + + We can describe `r`-Kleshchev partition tuples in `B(\lambda)` as + partition tuples `\mu` such that `\mu^{(t)}_{r_t - r_{t+1} + x} < + \mu^{(t+1)}_x` for all `x \geq 1` and `1 \leq t \leq k - 1`. + INPUT: - ``n`` -- for type `A_n^{(1)}` @@ -122,6 +128,17 @@ class KleshchevCrystal(Parent, UniqueRepresentation): sage: G2 = C2.digraph(subset=S) sage: G.is_isomorphic(G2, edge_labels=True) True + + REFERENCES: + + .. [Ariki2001] Susumu Ariki. On the classification of simple modules for + cyclotomic Hecke algebras of type `G(m,1,n)` and Kleshchev + multipartitions. Osaka J. Math. **38** (2001). :arxiv:`9908004v2`. + + .. [Vazirani2002] Monica Vazirani. *Parameterizing Hecek algebra modules: + Bernstein-Zelevinsky multisegments, Kleshchev multipartitions, and + crystal graphs*. Transform. Groups **7** (2002). pp. 267-303. + :arxiv:`0107052v1`, :doi:`10.1007/s00031-002-0014-1`. """ @staticmethod def __classcall_private__(cls, n, r): @@ -175,6 +192,10 @@ def e(self, i): r""" Return the action of `e_i` on ``self``. + INPUT: + + - ``i`` -- an element of the index set + EXAMPLES:: sage: C = KleshchevCrystal(2, [0,2]) @@ -208,6 +229,10 @@ def f(self, i): r""" Return the action of `f_i` on ``self``. + INPUT: + + - ``i`` -- an element of the index set + EXAMPLES:: sage: C = KleshchevCrystal(2, [0,2]) @@ -243,6 +268,10 @@ def epsilon(self, i): r""" Return `\varepsilon_i` of ``self``. + INPUT: + + - ``i`` -- an element of the index set + EXAMPLES:: sage: C = KleshchevCrystal(2, [0,2]) @@ -269,6 +298,10 @@ def phi(self, i): r""" Return `\varphi_i` of ``self``. + INPUT: + + - ``i`` -- an element of the index set + EXAMPLES:: sage: C = KleshchevCrystal(2, [0,2]) diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py new file mode 100644 index 00000000000..5b46d8aa4b8 --- /dev/null +++ b/src/sage/combinat/crystals/multisegments.py @@ -0,0 +1,173 @@ +r""" +Crystal of Bernstien-Zelevinsky Multisegments +""" + +#***************************************************************************** +# Copyright (C) 2013 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#**************************************************************************** + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element_wrapper import ElementWrapper +from sage.categories.highest_weight_crystals import HighestWeightCrystals +from sage.categories.regular_crystals import RegularCrystals +from sage.rings.finite_rings.integer_mod_ring import IntegerModRing +from sage.rings.all import ZZ +from sage.combinat.partition_tuple import PartitionTuple +from sage.combinat.root_system.cartan_type import CartanType + +class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): + r""" + The type `A_n^{(1)}` crystal `B(\infty)` realized using + Bernstein-Zelevinsky (BZ) multisegments. + + INPUT: + + - ``n`` -- for type `A_n^{(1)}` + + EXAMPLES:: + + REFERENCES: + + - [Vazirani2002]_ + """ + def __init__(self, n): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + """ + self._cartan_type = CartanType(['A', n, 1]) + Parent.__init__(self, category=(HighestWeightCrystals(), InfiniteEnumeratedSets())) + self.module_generators = (self.module_generator(),) + + def _repr_(self): + """ + Return a string representation of ``self``. + """ + return "The infinity crystal of BZ-multisegments of type {}".format(self._cartan_type) + + def module_generator(self): + """ + Return the module generator of ``self``. + """ + return self.element_class(self, ()) + + class Element(ElementWrapper): + """ + An element in a BZ multisegments crystal. + """ + def __init__(self, value): + """ + Initialize ``self``. + """ + ElementWrapper.__init__(self, tuple(sorted(value, reverse=True)) + + def e(self, i): + r""" + Return the action of `e_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + """ + M = self.value + pos = [] # The positions of the uncancelled minuses + for j,(a,b) in enumerate(M): + if b == i: + pos.append(j) + elif b == i + 1 and len(pos) > 0: + pos.pop() + + if len(pos) == 0: + return None + j = pos[0] + a,b = M[j] + if a == b: + return self.__class__(self.parent(), M[:j] + M[j+1:]) + return self.__class__(self.parent(), M[:j] + [(a,b-1)] + M[j+1:]) + + def f(self, i): + r""" + Return the action of `f_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + """ + M = self.value + pos = [] # The positions of the uncancelled minuses + for j,(a,b) in reversed(list(enumerate(M))): + if b == i + 1: + pos.append(j) + elif b == i and len(pos) > 0: + pos.pop() + + if len(pos) == 0: + return self.__class__(self.parent(), [(i, i)] + list(M)) + j = pos[0] + a,b = M[j] + return self.__class__(self.parent(), M[:j] + [(a,b+1)] + M[j+1:]) + + def epsilon(self, i): + r""" + Return `\varepsilon_i` of ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + """ + M = self.value + epsilon = 0 + for j,(a,b) in enumerate(M): + if b == i: + epsilon += 1 + elif b == i + 1 and epsilon > 0: + epsilon -= 1 + + return epsilon + + def phi(self,i): + r""" + Return `\varphi_i` of ``self``. + + Let `T \in \mathcal{B}(\infty)` Define `\varphi_i(T) := + \varepsilon_i(T) + \langle h_i, \mathrm{wt}(T) \rangle`, where `h_i` + is the `i`-th simple coroot and `\mathrm{wt}(T)` is the :meth:`weight` + of `T`. + + INPUT: + + - ``i`` -- an element of the index set + """ + P = self.parent().weight_lattice_realization() + h = P.simple_coroots() + return self.epsilon(i) + P(self.weight()).scalar(h[i]) + + def weight(self): + """ + Return the weight of ``self``. + """ + WLR = self.parent().weight_lattice_reallization() + alpha = WLR.simple_roots() + return WLR.sum(alpha[j] for a,b in self.value for j in range(a,b+1)) + From cba6e81b52793210bbce561f1f2378aa2a611dde Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 20 Dec 2013 14:41:19 -0800 Subject: [PATCH 005/151] More work on the multisegment model. --- src/sage/combinat/crystals/kleshchev.py | 10 +++++++ src/sage/combinat/crystals/multisegments.py | 32 ++++++++++++--------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/crystals/kleshchev.py b/src/sage/combinat/crystals/kleshchev.py index 50f913472e7..568f8e8d826 100644 --- a/src/sage/combinat/crystals/kleshchev.py +++ b/src/sage/combinat/crystals/kleshchev.py @@ -139,6 +139,16 @@ class KleshchevCrystal(Parent, UniqueRepresentation): Bernstein-Zelevinsky multisegments, Kleshchev multipartitions, and crystal graphs*. Transform. Groups **7** (2002). pp. 267-303. :arxiv:`0107052v1`, :doi:`10.1007/s00031-002-0014-1`. + + .. [TingleyLN] Peter Tingley. Explicit `\widehat{\mathfrak{sl}}_n` crystal + maps between cylindric plane partitions, multi-partitions, and + multi-segments. Lecture notes. + http://webpages.math.luc.edu/~ptingley/lecturenotes/explicit_bijections.pdf + + .. [Tingley2007] Peter Tingley. Three combinatorial models for + `\widehat{\mathfrak{sl}}_n` crystals, with applications to cylindric + plane partitions. International Mathematics Research Notices. (2007). + :arxiv:`0702062v3`. """ @staticmethod def __classcall_private__(cls, n, r): diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index 5b46d8aa4b8..de5d0dcb8a6 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -21,11 +21,9 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element_wrapper import ElementWrapper from sage.categories.highest_weight_crystals import HighestWeightCrystals -from sage.categories.regular_crystals import RegularCrystals -from sage.rings.finite_rings.integer_mod_ring import IntegerModRing -from sage.rings.all import ZZ -from sage.combinat.partition_tuple import PartitionTuple +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.combinat.root_system.cartan_type import CartanType +from sage.rings.finite_rings.integer_mod_ring import IntegerModRing class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): r""" @@ -41,6 +39,11 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): REFERENCES: - [Vazirani2002]_ + - [TingleyLN]_ + + .. [LTV1999] Bernard Leclerc, Jean-Yves Thibon, and Eric Vasserot. + *Zelevinsky's involution at roots of unity*. J. Reine Angew. Math. + 513:33-51 (1999). """ def __init__(self, n): """ @@ -70,11 +73,11 @@ class Element(ElementWrapper): """ An element in a BZ multisegments crystal. """ - def __init__(self, value): + def __init__(self, parent, value): """ Initialize ``self``. """ - ElementWrapper.__init__(self, tuple(sorted(value, reverse=True)) + ElementWrapper.__init__(self, parent, tuple(sorted(value, reverse=True))) def e(self, i): r""" @@ -89,18 +92,18 @@ def e(self, i): M = self.value pos = [] # The positions of the uncancelled minuses for j,(a,b) in enumerate(M): - if b == i: + if a == i: pos.append(j) - elif b == i + 1 and len(pos) > 0: + elif a + 1 == i and len(pos) > 0: pos.pop() if len(pos) == 0: return None j = pos[0] a,b = M[j] - if a == b: + if b == 1: return self.__class__(self.parent(), M[:j] + M[j+1:]) - return self.__class__(self.parent(), M[:j] + [(a,b-1)] + M[j+1:]) + return self.__class__(self.parent(), M[:j] + ((a-1,b-1),) + M[j+1:]) def f(self, i): r""" @@ -115,16 +118,17 @@ def f(self, i): M = self.value pos = [] # The positions of the uncancelled minuses for j,(a,b) in reversed(list(enumerate(M))): - if b == i + 1: + if a + 1 == i: pos.append(j) - elif b == i and len(pos) > 0: + elif a == i and len(pos) > 0: pos.pop() if len(pos) == 0: - return self.__class__(self.parent(), [(i, i)] + list(M)) + Z = IntegerModRing(self.cartan_type().rank()) + return self.__class__(self.parent(), ((Z(1), i),) + M) j = pos[0] a,b = M[j] - return self.__class__(self.parent(), M[:j] + [(a,b+1)] + M[j+1:]) + return self.__class__(self.parent(), M[:j] + ((a+1,b+1),) + M[j+1:]) def epsilon(self, i): r""" From 1f4caf88fed820555c885053836e4776327b2d8c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 23 Dec 2013 14:17:24 -0800 Subject: [PATCH 006/151] Fixes to multisegment. Work on documentation. --- src/sage/combinat/crystals/affinization.py | 32 ++- src/sage/combinat/crystals/kleshchev.py | 7 +- src/sage/combinat/crystals/multisegments.py | 290 ++++++++++++++++---- 3 files changed, 278 insertions(+), 51 deletions(-) diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py index c18d98d3122..e13e7ac3f52 100644 --- a/src/sage/combinat/crystals/affinization.py +++ b/src/sage/combinat/crystals/affinization.py @@ -61,6 +61,11 @@ class AffinizationCrystal(Parent, UniqueRepresentation): def __init__(self, B): """ Initialize ``self``. + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 1, 1).affinization() + sage: TestSuite(A).run() # long time """ self._B = B self._cartan_type = B.cartan_type() @@ -70,14 +75,25 @@ def __init__(self, B): def _repr_(self): """ Return a string representation of ``self``. + + EXAMPLES:: + + sage: KirillovReshetikhinCrystal(['A',2,1], 1, 1).affinization() + Affinization of Kirillov-Reshetikhin crystal of type ['A', 2, 1] with (r,s)=(1,1) """ return "Affinization of {}".format(self._B) def weight_lattice_realization(self): """ Return the weight lattice realization of ``self``. + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 1, 1).affinization() + sage: A.weight_lattice_realization() + Ambient space of the Root system of type ['A', 2, 1] """ - return self.cartan_type().ambient_space() + return self.cartan_type().root_system().ambient_space() class Element(Element): """ @@ -104,6 +120,20 @@ def _latex_(self): from sage.misc.latex import latex return latex(self._b) + "({})".format(self._m) + def __eq__(self, other): + """ + Check equality. + """ + if not isinstance(other, AffinizationCrystal.Element): + return False + return self._b == other._b and self._m == other._m + + def __neq__(self, other): + """ + Check inequality. + """ + return not self.__eq__(other) + def e(self, i): """ Return the action of `e_i` on ``self``. diff --git a/src/sage/combinat/crystals/kleshchev.py b/src/sage/combinat/crystals/kleshchev.py index 568f8e8d826..4d6df583f51 100644 --- a/src/sage/combinat/crystals/kleshchev.py +++ b/src/sage/combinat/crystals/kleshchev.py @@ -105,10 +105,10 @@ class KleshchevCrystal(Parent, UniqueRepresentation): ([1, 1, 1, 1], []) sage: mg.f_string([2,0,1,0]) ([2], [2]) - sage: S = C.subcrystal(max_depth=5) + sage: S = C.subcrystal(max_depth=3) sage: G = C.digraph(subset=S) sage: B = CrystalOfLSPaths(['A',2,1], [1,0,1]) - sage: SB = B.subcrystal(max_depth=5) + sage: SB = B.subcrystal(max_depth=3) sage: GB = B.digraph(subset=SB) sage: G.is_isomorphic(GB, edge_labels=True) True @@ -175,8 +175,7 @@ def __init__(self, n, r): EXAMPLES:: sage: C = KleshchevCrystal(2, [0,2]) - sage: TestSuite(C).run(skip="_test_stembridge_local_axioms") - sage: C._test_stembridge_local_axioms() # long time + sage: TestSuite(C).run() # long time """ self._cartan_type = CartanType(['A', n, 1]) self._r = r diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index de5d0dcb8a6..a80ab3d010c 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -24,22 +24,68 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.combinat.root_system.cartan_type import CartanType from sage.rings.finite_rings.integer_mod_ring import IntegerModRing +from sage.rings.all import ZZ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): r""" The type `A_n^{(1)}` crystal `B(\infty)` realized using Bernstein-Zelevinsky (BZ) multisegments. + Using the notation in [TingleyLN]_, a segment is a pair `(k; i]` where + `k \in \ZZ_{>0}` and `i \in \ZZ / n\ZZ`, and a multisegment is a multiset + of segments. We define a crystal structure on multisegments as follows: + + We create an `i`-sigature of the multisegment `\psi` by removing all + segments `(k; j]` where `j \not\equiv i, i-1`, and let `\psi^{\prime}` + denote the resulting multiset. Next we sort `\psi^{\prime}` to a sequence + `L` decreasing in `k` and `(k; i-1]` comes before `(k; i]`. Assign to + each `(k; i-1]` a `+` and `(k^{\prime}; i]` a `-`, and then cancel + all `+-` pairs until the resulting `\pm`-string is of the form + `- \cdots - + \cdots +`. The Kashiwara operators are then defined by: + + * `e_i` changes `(k; i]` to `(k-1; i-1]` corresponding to the leftmost + `-` or `0` (``None``) if no `-` exists, + * `f_i` changes `(k; i-1]` to `(k+1; i]` corresponding to + the rightmost `+` or adds a new segment of `(1; i]` if no `+` exists. + + We remove any segments of the form `(0; j]`. + INPUT: - ``n`` -- for type `A_n^{(1)}` EXAMPLES:: - REFERENCES: + sage: B = InfinityCrystalOfMultisegments(2) + sage: x = B([(8,1),(6,0),(5,1),(5,0),(4,0),(4,1),(4,1),(3,0),(3,0),(3,1),(3,1),(1,0),(1,2),(1,2)]); x + {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], + (3; 0], (3; 0], (3; 1], (3; 1], (1; 0], (1; 2], (1; 2]} + sage: x.f(1) + {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], + (3; 0], (3; 0], (3; 1], (3; 1], (2; 1], (1; 2], (1; 2]} + sage: x.f(1).f(1) + {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], (3; 0], + (3; 0], (3; 1], (3; 1], (2; 1], (1; 1], (1; 2], (1; 2]} + sage: x.e(1) + {(7; 0], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], (3; 0], (3; 0], (3; 1], (3; 1], (1; 0], (1; 2], (1; 2]} + sage: x.e(1).e(1) + sage: x.f(0) + {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], + (3; 0], (3; 0], (3; 1], (3; 1], (2; 0], (1; 0], (1; 2]} + + We check an `\widehat{\mathfrak{sl}}_2` example against the generalized + Young walls:: + + sage: B = InfinityCrystalOfMultisegments(1) + sage: S = B.subcrystal(max_depth=4) + sage: G = B.digraph(subset=S) + sage: C = InfinityCrystalOfGeneralizedYoungWalls(1) + sage: SC = C.subcrystal(max_depth=4) + sage: GC = C.digraph(subset=SC) + sage: G.is_isomorphic(GC, edge_labels=True) + True - - [Vazirani2002]_ - - [TingleyLN]_ + REFERENCES: .. [LTV1999] Bernard Leclerc, Jean-Yves Thibon, and Eric Vasserot. *Zelevinsky's involution at roots of unity*. J. Reine Angew. Math. @@ -52,6 +98,7 @@ def __init__(self, n): EXAMPLES:: sage: B = InfinityCrystalOfMultisegments(2) + sage: TestSuite(B).run() """ self._cartan_type = CartanType(['A', n, 1]) Parent.__init__(self, category=(HighestWeightCrystals(), InfiniteEnumeratedSets())) @@ -60,15 +107,39 @@ def __init__(self, n): def _repr_(self): """ Return a string representation of ``self``. + + EXAMPLES:: + + sage: InfinityCrystalOfMultisegments(2) + The infinity crystal of BZ-multisegments of type ['A', 2, 1] """ return "The infinity crystal of BZ-multisegments of type {}".format(self._cartan_type) def module_generator(self): """ Return the module generator of ``self``. + + EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + sage: B.module_generator() + {} """ return self.element_class(self, ()) + def weight_lattice_realization(self): + """ + Return a realization of the weight lattice of ``self``. Since the + weights are all in `Q^-`, return the root lattice of type `A_n^(1)`. + + EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + sage: B.weight_lattice_realization() + Root lattice of the Root system of type ['A', 2, 1] + """ + return self._cartan_type.root_system().root_lattice() + class Element(ElementWrapper): """ An element in a BZ multisegments crystal. @@ -76,8 +147,107 @@ class Element(ElementWrapper): def __init__(self, parent, value): """ Initialize ``self``. + + EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + sage: mg = B.module_generator() + sage: TestSuite(mg).run() + """ + ZM = IntegerModRing(parent.cartan_type().rank()) + value = [(k, ZM(i)) for k,i in value] + def sort_cmp(x,y): + c = cmp(y[0],x[0]) + if c == 0: + c = cmp(ZZ(x[1]),ZZ(y[1])) + return c + ElementWrapper.__init__(self, parent, tuple(sorted(value, cmp=sort_cmp))) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + sage: B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + {(4; 2], (3; 0], (3; 1], (1; 0], (1; 1]} + """ + seg = lambda x: "({}; {}]".format(x[0], x[1]) + return "{" + ", ".join(seg(x) for x in self.value) + "}" + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + sage: latex(B([(4,2), (3,0), (3,1), (1,1), (1,0)])) + \bigl\{(4; 2], (3; 0], (3; 1], (1; 0], (1; 1]\bigr\} + """ + seg = lambda x: "({}; {}]".format(x[0], x[1]) + return "\\bigl\\{" + ", ".join(seg(x) for x in self.value) + "\\bigr\\}" + + def _sig(self, i): + r""" + Return an `i`-signature of ``self``. + + INPUT: + + - ``i`` -- an element of the indexing set + + OUTPUT: + + A pair ``(m, p, ep)`` where ``m`` and ``p`` correspond to the + block length of the unmatched `-` and `+` respectively or ``None`` + if there is no such block and ``ep`` is the number of unmatched + `-`. + + EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b._sig(0) + (1, None, 1) + sage: b._sig(1) + (None, None, 0) """ - ElementWrapper.__init__(self, parent, tuple(sorted(value, reverse=True))) + if len(self.value) == 0: + return (None, None) + pos = [] + block = self.value[0][0] + cur = 0 + for k,j in self.value: + if k != block: + if cur != 0: + pos.append((block, cur)) + cur = 0 + block = k + if j + 1 == i: # + or ( + cur += 1 + elif j == i: # - or ) + cur -= 1 + if cur != 0: + pos.append((block, cur)) + # Now cancel all +- pairs + cur = 0 + m = None + p = None + ep = 0 + for k,c in pos: + old = cur + cur += c + if cur < 0: + m = k + p = None + ep -= cur + cur = 0 + elif cur == 0: + p = None + elif cur > 0 and old <= 0: + p = k + return (m, p, ep) def e(self, i): r""" @@ -88,22 +258,26 @@ def e(self, i): - ``i`` -- an element of the index set EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b.e(0) + {(4; 2], (3; 0], (3; 1], (1; 1]} + sage: b.e(1) + sage: b.e(2) + {(3; 0], (3; 1], (3; 1], (1; 0], (1; 1]} """ - M = self.value - pos = [] # The positions of the uncancelled minuses - for j,(a,b) in enumerate(M): - if a == i: - pos.append(j) - elif a + 1 == i and len(pos) > 0: - pos.pop() - - if len(pos) == 0: + i = IntegerModRing(self.parent()._cartan_type.rank())(i) + m = self._sig(i)[0] + if m is None: return None - j = pos[0] - a,b = M[j] - if b == 1: - return self.__class__(self.parent(), M[:j] + M[j+1:]) - return self.__class__(self.parent(), M[:j] + ((a-1,b-1),) + M[j+1:]) + + M = self.value + a = M.index((m, i)) + k = M[a][0] + if k == 1: + return self.__class__(self.parent(), M[:a] + M[a+1:]) + return self.__class__(self.parent(), M[:a] + ((k-1,i-1),) + M[a+1:]) def f(self, i): r""" @@ -114,21 +288,24 @@ def f(self, i): - ``i`` -- an element of the index set EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b.f(0) + {(4; 2], (3; 0], (3; 1], (1; 0], (1; 0], (1; 1]} + sage: b.f(1) + {(4; 2], (3; 0], (3; 1], (1; 0], (1; 1], (1; 1]} + sage: b.f(2) + {(4; 2], (4; 2], (3; 0], (1; 0], (1; 1]} """ + i = IntegerModRing(self.parent()._cartan_type.rank())(i) + p = self._sig(i)[1] M = self.value - pos = [] # The positions of the uncancelled minuses - for j,(a,b) in reversed(list(enumerate(M))): - if a + 1 == i: - pos.append(j) - elif a == i and len(pos) > 0: - pos.pop() - - if len(pos) == 0: - Z = IntegerModRing(self.cartan_type().rank()) - return self.__class__(self.parent(), ((Z(1), i),) + M) - j = pos[0] - a,b = M[j] - return self.__class__(self.parent(), M[:j] + ((a+1,b+1),) + M[j+1:]) + if p is None: + return self.__class__(self.parent(), ((1, i),) + M) + + a = M.index((p, i-1)) + return self.__class__(self.parent(), M[:a] + ((M[a][0]+1,i),) + M[a+1:]) def epsilon(self, i): r""" @@ -139,18 +316,20 @@ def epsilon(self, i): - ``i`` -- an element of the index set EXAMPLES:: - """ - M = self.value - epsilon = 0 - for j,(a,b) in enumerate(M): - if b == i: - epsilon += 1 - elif b == i + 1 and epsilon > 0: - epsilon -= 1 - return epsilon + sage: B = InfinityCrystalOfMultisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b.epsilon(0) + 1 + sage: b.epsilon(1) + 0 + sage: b.epsilon(2) + 1 + """ + i = IntegerModRing(self.parent()._cartan_type.rank())(i) + return self._sig(i)[2] - def phi(self,i): + def phi(self, i): r""" Return `\varphi_i` of ``self``. @@ -162,16 +341,35 @@ def phi(self,i): INPUT: - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b.phi(0) + 1 + sage: b.phi(1) + 0 + sage: mg = B.module_generator() + sage: mg.f(1).phi(0) + -1 """ - P = self.parent().weight_lattice_realization() - h = P.simple_coroots() - return self.epsilon(i) + P(self.weight()).scalar(h[i]) + h = self.parent().weight_lattice_realization().simple_coroots() + return self.epsilon(i) + self.weight().scalar(h[i]) def weight(self): """ Return the weight of ``self``. + + EXAMPLES:: + + sage: B = InfinityCrystalOfMultisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b.weight() + 4*alpha[0] + 4*alpha[1] + 4*alpha[2] """ - WLR = self.parent().weight_lattice_reallization() + WLR = self.parent().weight_lattice_realization() alpha = WLR.simple_roots() - return WLR.sum(alpha[j] for a,b in self.value for j in range(a,b+1)) + n = self.parent()._cartan_type.rank() + return WLR.sum(alpha[j % n] for k,i in self.value for j in range(ZZ(i),ZZ(i)+k)) From 222fdc24f3ec6fb41ecc709320bc3e091b438b70 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 24 Dec 2013 18:55:51 -0800 Subject: [PATCH 007/151] Finished documentation. --- src/sage/combinat/crystals/affinization.py | 156 +++++++++++++++++++-- src/sage/combinat/crystals/all.py | 1 + 2 files changed, 148 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py index e13e7ac3f52..0f2df5d2dc4 100644 --- a/src/sage/combinat/crystals/affinization.py +++ b/src/sage/combinat/crystals/affinization.py @@ -49,14 +49,23 @@ class AffinizationCrystal(Parent, UniqueRepresentation): \\ \mathrm{wt}(b(m)) & = \wt(b) + m \delta. \end{aligned} - EXAMPLES:: + EXAMPLES: - sage: K = KirillovReshetikhinCrystal(['A',2,1], 1, 1) + We first construct a Kirillov-Reshetikhin crystal and then take it's + corresponding affinization:: + + sage: K = KirillovReshetikhinCrystal(['A',2,1], 2, 2) sage: A = K.affinization() + Next we construct an affinization crystal from a tensor product of KR + crystals:: + + sage: KT = TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[1,2],[2,1]]) + sage: A = AffinizationCrystal(KT) + REFERENCES: - - [HK02]_ + - [HK02]_ Chapter 10 """ def __init__(self, B): """ @@ -64,13 +73,16 @@ def __init__(self, B): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 1, 1).affinization() + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() sage: TestSuite(A).run() # long time """ + if not B.cartan_type().is_affine(): + raise ValueError("must be an affine crystal") self._B = B self._cartan_type = B.cartan_type() Parent.__init__(self, category=(RegularCrystals(), InfiniteEnumeratedSets())) - self.module_generators = (self.element_class(self, self._B.module_generator(), 0),) + mg_elt = lambda b: self.element_class(self, b, 0) + self.module_generators = tuple(map(mg_elt, B.module_generators)) def _repr_(self): """ @@ -95,6 +107,24 @@ def weight_lattice_realization(self): """ return self.cartan_type().root_system().ambient_space() + def digraph(self, subset=None, index_set=None): + """ + Return the DiGraph associated with ``self``. See + :meth:`~sage.categories.crystals.ParentMethods.digraph()` for more + information. + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: S = A.subcrystal(max_depth=3) + sage: G = A.digraph(subset=S) + """ + G = super(AffinizationCrystal, self).digraph(subset, index_set) + from sage.graphs.dot2tex_utils import have_dot2tex + if have_dot2tex(): + G.set_latex_options(edge_options = lambda (u,v,label): ({})) + return G + class Element(Element): """ An element in an affinization crystal. @@ -102,6 +132,12 @@ class Element(Element): def __init__(self, parent, b, m): """ Initialize ``self``. + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: mg = A.module_generators[0] + sage: TestSuite(mg).run() """ self._b = b self._m = m @@ -110,12 +146,33 @@ def __init__(self, parent, b, m): def _repr_(self): """ Return a string representation of ``self``. + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: A.module_generators[0] + [[1, 1], [2, 2]](0) + sage: KT = TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[1,2],[2,1]]) + sage: A = AffinizationCrystal(KT) + sage: A.module_generators[0] + [[1, 1]] (X) [[1], [2]](0) """ - return "{}({})".format(self._b, self._m) + return "{!r}({})".format(self._b, self._m) def _latex_(self): r""" Return a LaTeX representation of ``self``. + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: latex(A.module_generators[0]) + {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\cline{1-2} + \lr{1}&\lr{1}\\\cline{1-2} + \lr{2}&\lr{2}\\\cline{1-2} + \end{array}$} + } (0) """ from sage.misc.latex import latex return latex(self._b) + "({})".format(self._m) @@ -123,14 +180,35 @@ def _latex_(self): def __eq__(self, other): """ Check equality. + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: mg = A.module_generators[0] + sage: mg == mg + True + sage: KT = TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[1,2],[2,1]]) + sage: A = AffinizationCrystal(KT) + sage: A(KT.module_generators[3], 1).f(0) == A.module_generators[0] + True """ if not isinstance(other, AffinizationCrystal.Element): return False - return self._b == other._b and self._m == other._m + return self.parent() == other.parent() \ + and self._b == other._b and self._m == other._m def __neq__(self, other): """ Check inequality. + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: mg = A.module_generators[0] + sage: mg != mg.f(2) + True + sage: mg != mg + False """ return not self.__eq__(other) @@ -140,7 +218,17 @@ def e(self, i): INPUT: - - ``i`` -- an element of the index se + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2,2).affinization() + sage: mg = A.module_generators[0] + sage: mg.e(0) + [[1, 2], [2, 3]](1) + sage: mg.e(1) + sage: mg.e(0).e(1) + [[1, 1], [2, 3]](1) """ bp = self._b.e(i) if bp is None: @@ -156,12 +244,25 @@ def f(self, i): INPUT: - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2,2).affinization() + sage: mg = A.module_generators[0] + sage: mg.f(2) + [[1, 1], [2, 3]](0) + sage: mg.f(2).f(2).f(0) + sage: mg.f_string([2,1,1]) + sage: mg.f_string([2,1]) + [[1, 2], [2, 3]](0) + sage: mg.f_string([2,1,0]) + [[1, 1], [2, 2]](-1) """ bp = self._b.f(i) if bp is None: return None if i == 0: - return self.__class__(self.parent(), bp, self._m+1) + return self.__class__(self.parent(), bp, self._m-1) return self.__class__(self.parent(), bp, self._m) def epsilon(self, i): @@ -171,6 +272,15 @@ def epsilon(self, i): INPUT: - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2,2).affinization() + sage: mg = A.module_generators[0] + sage: mg.epsilon(0) + 2 + sage: mg.epsilon(1) + 0 """ return self._b.epsilon(i) @@ -181,12 +291,40 @@ def phi(self, i): INPUT: - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2,2).affinization() + sage: mg = A.module_generators[0] + sage: mg.phi(0) + 0 + sage: mg.phi(2) + 2 """ return self._b.phi(i) def weight(self): r""" Return the weight of ``self``. + + The weight `\mathrm{wt}` of an element is: + + .. MATH:: + + \mathrm{wt}\bigl( b(m) \bigr) = \mathrm{wt}(b) + m \delta + + where `\delta` is the null root. + + EXAMPLES:: + + sage: A = KirillovReshetikhinCrystal(['A',2,1], 2,2).affinization() + sage: mg = A.module_generators[0] + sage: mg.weight() + 2*e[0] + 2*e[1] + sage: mg.e(0).weight() + e[1] + e['delta'] + sage: mg.e(0).e(0).weight() + -2*e[0] + 2*e['delta'] """ WLR = self.parent().weight_lattice_realization() La = WLR.fundamental_weights() diff --git a/src/sage/combinat/crystals/all.py b/src/sage/combinat/crystals/all.py index be544055a56..c3c23440743 100644 --- a/src/sage/combinat/crystals/all.py +++ b/src/sage/combinat/crystals/all.py @@ -20,4 +20,5 @@ from monomial_crystals import InfinityCrystalOfNakajimaMonomials, CrystalOfNakajimaMonomials from kleshchev import KleshchevCrystal from multisegments import InfinityCrystalOfMultisegments +from affinization import AffinizationCrystal From 23c69dbfc883e77dea44a8788964cdc44431230b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 15 Apr 2014 18:43:09 -0700 Subject: [PATCH 008/151] Moved to the catalog. --- src/sage/combinat/crystals/affinization.py | 42 +++++++++---------- src/sage/combinat/crystals/all.py | 5 --- src/sage/combinat/crystals/catalog.py | 4 ++ .../crystals/catalog_infinity_crystals.py | 2 + .../crystals/highest_weight_crystals.py | 24 ++++++++++- src/sage/combinat/crystals/kleshchev.py | 30 ++++++------- src/sage/combinat/crystals/multisegments.py | 38 ++++++++--------- 7 files changed, 83 insertions(+), 62 deletions(-) diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py index 0f2df5d2dc4..84989317c89 100644 --- a/src/sage/combinat/crystals/affinization.py +++ b/src/sage/combinat/crystals/affinization.py @@ -54,14 +54,14 @@ class AffinizationCrystal(Parent, UniqueRepresentation): We first construct a Kirillov-Reshetikhin crystal and then take it's corresponding affinization:: - sage: K = KirillovReshetikhinCrystal(['A',2,1], 2, 2) + sage: K = crystals.KirillovReshetikhin(['A',2,1], 2, 2) sage: A = K.affinization() Next we construct an affinization crystal from a tensor product of KR crystals:: - sage: KT = TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[1,2],[2,1]]) - sage: A = AffinizationCrystal(KT) + sage: KT = crystals.TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[1,2],[2,1]]) + sage: A = crystals.Affinization(KT) REFERENCES: @@ -73,7 +73,7 @@ def __init__(self, B): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: TestSuite(A).run() # long time """ if not B.cartan_type().is_affine(): @@ -90,7 +90,7 @@ def _repr_(self): EXAMPLES:: - sage: KirillovReshetikhinCrystal(['A',2,1], 1, 1).affinization() + sage: crystals.KirillovReshetikhin(['A',2,1], 1, 1).affinization() Affinization of Kirillov-Reshetikhin crystal of type ['A', 2, 1] with (r,s)=(1,1) """ return "Affinization of {}".format(self._B) @@ -101,7 +101,7 @@ def weight_lattice_realization(self): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 1, 1).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 1, 1).affinization() sage: A.weight_lattice_realization() Ambient space of the Root system of type ['A', 2, 1] """ @@ -115,7 +115,7 @@ def digraph(self, subset=None, index_set=None): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: S = A.subcrystal(max_depth=3) sage: G = A.digraph(subset=S) """ @@ -135,7 +135,7 @@ def __init__(self, parent, b, m): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: mg = A.module_generators[0] sage: TestSuite(mg).run() """ @@ -149,11 +149,11 @@ def _repr_(self): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: A.module_generators[0] [[1, 1], [2, 2]](0) - sage: KT = TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[1,2],[2,1]]) - sage: A = AffinizationCrystal(KT) + sage: KT = crystals.TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[1,2],[2,1]]) + sage: A = crystals.Affinization(KT) sage: A.module_generators[0] [[1, 1]] (X) [[1], [2]](0) """ @@ -165,7 +165,7 @@ def _latex_(self): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: latex(A.module_generators[0]) {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\cline{1-2} @@ -183,12 +183,12 @@ def __eq__(self, other): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: mg = A.module_generators[0] sage: mg == mg True - sage: KT = TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[1,2],[2,1]]) - sage: A = AffinizationCrystal(KT) + sage: KT = crystals.TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[1,2],[2,1]]) + sage: A = crystals.Affinization(KT) sage: A(KT.module_generators[3], 1).f(0) == A.module_generators[0] True """ @@ -203,7 +203,7 @@ def __neq__(self, other): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2, 2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: mg = A.module_generators[0] sage: mg != mg.f(2) True @@ -222,7 +222,7 @@ def e(self, i): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2,2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2,2).affinization() sage: mg = A.module_generators[0] sage: mg.e(0) [[1, 2], [2, 3]](1) @@ -247,7 +247,7 @@ def f(self, i): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2,2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2,2).affinization() sage: mg = A.module_generators[0] sage: mg.f(2) [[1, 1], [2, 3]](0) @@ -275,7 +275,7 @@ def epsilon(self, i): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2,2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2,2).affinization() sage: mg = A.module_generators[0] sage: mg.epsilon(0) 2 @@ -294,7 +294,7 @@ def phi(self, i): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2,2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2,2).affinization() sage: mg = A.module_generators[0] sage: mg.phi(0) 0 @@ -317,7 +317,7 @@ def weight(self): EXAMPLES:: - sage: A = KirillovReshetikhinCrystal(['A',2,1], 2,2).affinization() + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2,2).affinization() sage: mg = A.module_generators[0] sage: mg.weight() 2*e[0] + 2*e[1] diff --git a/src/sage/combinat/crystals/all.py b/src/sage/combinat/crystals/all.py index 7cd8cb062e9..eb4c093d8be 100644 --- a/src/sage/combinat/crystals/all.py +++ b/src/sage/combinat/crystals/all.py @@ -1,8 +1,3 @@ -# TODO: move to catalog -from kleshchev import KleshchevCrystal -from multisegments import InfinityCrystalOfMultisegments -from affinization import AffinizationCrystal - import catalog as crystals from sage.misc.lazy_import import lazy_import diff --git a/src/sage/combinat/crystals/catalog.py b/src/sage/combinat/crystals/catalog.py index 9eb17ceb9ea..6c753b84c77 100644 --- a/src/sage/combinat/crystals/catalog.py +++ b/src/sage/combinat/crystals/catalog.py @@ -50,12 +50,14 @@ * :class:`~sage.combinat.crystals.affine.AffineCrystalFromClassical` * :class:`~sage.combinat.crystals.affine.AffineCrystalFromClassicalAndPromotion` * :class:`AffineFactorization ` +* :class:`Affinization ` * :class:`AlcovePaths ` * :class:`FastRankTwo ` * :class:`GeneralizedYoungWalls ` * :func:`HighestWeight ` * :func:`KirillovReshetikhin ` +* :class:`KleshchevPartitions ` * :class:`KyotoPathModel ` * :class:`Letters ` * :class:`LSPaths ` @@ -96,6 +98,8 @@ from monomial_crystals import CrystalOfNakajimaMonomials as NakajimaMonomials from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinCrystal as KirillovReshetikhin +from sage.combinat.crystals.kleshchev import KleshchevCrystal as KleshchevPartitions +from sage.combinat.crystals.affinization import AffinizationCrystal as Affinization from tensor_product import TensorProductOfCrystals as TensorProduct from direct_sum import DirectSumOfCrystals as DirectSum diff --git a/src/sage/combinat/crystals/catalog_infinity_crystals.py b/src/sage/combinat/crystals/catalog_infinity_crystals.py index 5798fbdfb5f..2faf5282d8b 100644 --- a/src/sage/combinat/crystals/catalog_infinity_crystals.py +++ b/src/sage/combinat/crystals/catalog_infinity_crystals.py @@ -5,10 +5,12 @@ * :class:`GeneralizedYoungWalls ` +* :class:`Multisegments ` * :class:`NakajimaMonomials ` * :class:`Tableaux ` """ from generalized_young_walls import InfinityCrystalOfGeneralizedYoungWalls as GeneralizedYoungWalls +from multisegments import InfinityCrystalOfMultisegments as Multisegments from monomial_crystals import InfinityCrystalOfNakajimaMonomials as NakajimaMonomials from infinity_crystals import InfinityCrystalOfTableaux as Tableaux diff --git a/src/sage/combinat/crystals/highest_weight_crystals.py b/src/sage/combinat/crystals/highest_weight_crystals.py index 8610f7bc15a..dbe90495415 100644 --- a/src/sage/combinat/crystals/highest_weight_crystals.py +++ b/src/sage/combinat/crystals/highest_weight_crystals.py @@ -29,6 +29,7 @@ from sage.combinat.crystals.littelmann_path import CrystalOfLSPaths from sage.combinat.crystals.generalized_young_walls import CrystalOfGeneralizedYoungWalls from sage.combinat.crystals.monomial_crystals import CrystalOfNakajimaMonomials +from sage.combinat.crystals.kleshchev import KleshchevCrystal def HighestWeightCrystal(dominant_weight, model=None): r""" @@ -62,6 +63,8 @@ def HighestWeightCrystal(dominant_weight, model=None): ` * ``'GeneralizedYoungWalls'`` - :class:`generalized Young walls ` + * ``'KleshchevPartitions'`` - :class:` Kleshchev multipartitions + ` EXAMPLES:: @@ -147,6 +150,14 @@ def HighestWeightCrystal(dominant_weight, model=None): Cartan type ['F', 4] and highest weight Lambda[1] + Lambda[4]. sage: crystals.HighestWeight(wt, model='AlcovePaths') Highest weight crystal of alcove paths of type ['F', 4] and weight Lambda[1] + Lambda[4] + + sage: La = RootSystem(['A',3,1]).weight_lattice().fundamental_weights() + sage: wt = La[0] + La[2] + sage: crystals.HighestWeight(wt, model='GeneralizedYoungWalls') + Highest weight crystal of generalized Young walls of + Cartan type ['A', 3, 1] and highest weight Lambda[0] + Lambda[2]. + sage: crystals.HighestWeight(wt, model='KleshchevPartitions') + The crystal of multipartitions of type ['A', 4, 1] and residues (0, 2) """ cartan_type = dominant_weight.parent().cartan_type() if model is None: @@ -201,9 +212,18 @@ def HighestWeightCrystal(dominant_weight, model=None): if cartan_type.type() != 'A': raise NotImplementedError("only for affine type A") # Make sure it's in the weight lattice - P = dominant_weight.parent().root_system.weight_space() + P = dominant_weight.parent().root_system.weight_lattice() wt = P.sum_of_terms((i, c) for i,c in dominant_weight) - return CrystalOfGeneralizedYoungWalls(cartan_type.rank(), wt) + return CrystalOfGeneralizedYoungWalls(cartan_type.rank()-1, wt) + + if model == 'KleshchevPartitions': + if not cartan_type.is_affine(): + raise ValueError("only for affine types") + if cartan_type.type() != 'A': + raise NotImplementedError("only for affine type A") + from sage.misc.flatten import flatten + wt = flatten([[i]*c for i,c in dominant_weight]) + return KleshchevCrystal(cartan_type.rank(), wt) raise ValueError("invalid model") diff --git a/src/sage/combinat/crystals/kleshchev.py b/src/sage/combinat/crystals/kleshchev.py index 4d6df583f51..147ca8bfc7a 100644 --- a/src/sage/combinat/crystals/kleshchev.py +++ b/src/sage/combinat/crystals/kleshchev.py @@ -28,7 +28,7 @@ class KleshchevCrystal(Parent, UniqueRepresentation): r""" - The Kleshchev partition (tuple) crystal. + The Kleshchev multipartition (or partition tuple) crystal. We consider type `A_n^{(1)}` crystals, and let `r = (r_i \mid r_i \in \ZZ / n \ZZ)` be a finite sequence of length `k` (often called the level) @@ -67,7 +67,7 @@ class KleshchevCrystal(Parent, UniqueRepresentation): We first do an example of a level 1 crystal:: - sage: C = KleshchevCrystal(2, [0]) + sage: C = crystals.KleshchevPartitions(2, [0]) sage: C The crystal of multipartitions of type ['A', 2, 1] and residues (0,) sage: mg = C.module_generators[0] @@ -83,7 +83,7 @@ class KleshchevCrystal(Parent, UniqueRepresentation): [2, 2] sage: S = C.subcrystal(max_depth=5) sage: G = C.digraph(subset=S) - sage: B = CrystalOfLSPaths(['A',2,1], [1,0,0]) + sage: B = crystals.LSPaths(['A',2,1], [1,0,0]) sage: SB = B.subcrystal(max_depth=5) sage: GB = B.digraph(subset=SB) sage: G.is_isomorphic(GB, edge_labels=True) @@ -91,7 +91,7 @@ class KleshchevCrystal(Parent, UniqueRepresentation): Now a higher level crystal:: - sage: C = KleshchevCrystal(2, [0,2]) + sage: C = crystals.KleshchevPartitions(2, [0,2]) sage: mg = C.module_generators[0] sage: mg ([], []) @@ -107,7 +107,7 @@ class KleshchevCrystal(Parent, UniqueRepresentation): ([2], [2]) sage: S = C.subcrystal(max_depth=3) sage: G = C.digraph(subset=S) - sage: B = CrystalOfLSPaths(['A',2,1], [1,0,1]) + sage: B = crystals.LSPaths(['A',2,1], [1,0,1]) sage: SB = B.subcrystal(max_depth=3) sage: GB = B.digraph(subset=SB) sage: G.is_isomorphic(GB, edge_labels=True) @@ -116,7 +116,7 @@ class KleshchevCrystal(Parent, UniqueRepresentation): The ordering of the residues gives a different representation of the higher level crystals (but it is still isomorphic):: - sage: C2 = KleshchevCrystal(2, [2,0]) + sage: C2 = crystals.KleshchevPartitions(2, [2,0]) sage: mg2 = C2.highest_weight_vector() sage: mg2.f_string([0,1,2,0]) ([2], [2]) @@ -157,8 +157,8 @@ def __classcall_private__(cls, n, r): EXAMPLES:: - sage: C1 = KleshchevCrystal(2, [0,2]) - sage: C2 = KleshchevCrystal(2, (0,2)) + sage: C1 = crystals.KleshchevPartitions(2, [0,2]) + sage: C2 = crystals.KleshchevPartitions(2, (0,2)) sage: C1 is C2 True """ @@ -174,7 +174,7 @@ def __init__(self, n, r): EXAMPLES:: - sage: C = KleshchevCrystal(2, [0,2]) + sage: C = crystals.KleshchevPartitions(2, [0,2]) sage: TestSuite(C).run() # long time """ self._cartan_type = CartanType(['A', n, 1]) @@ -188,7 +188,7 @@ def _repr_(self): EXAMPLES:: - sage: KleshchevCrystal(2, [0,2]) + sage: crystals.KleshchevPartitions(2, [0,2]) The crystal of multipartitions of type ['A', 2, 1] and residues (0, 2) """ return "The crystal of multipartitions of type {} and residues {}".format(self._cartan_type, self._r) @@ -207,7 +207,7 @@ def e(self, i): EXAMPLES:: - sage: C = KleshchevCrystal(2, [0,2]) + sage: C = crystals.KleshchevPartitions(2, [0,2]) sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) sage: x.e(2) ([5, 4, 1], [3, 1, 1, 1]) @@ -244,7 +244,7 @@ def f(self, i): EXAMPLES:: - sage: C = KleshchevCrystal(2, [0,2]) + sage: C = crystals.KleshchevPartitions(2, [0,2]) sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) sage: x.e(2) ([5, 4, 1], [3, 1, 1, 1]) @@ -283,7 +283,7 @@ def epsilon(self, i): EXAMPLES:: - sage: C = KleshchevCrystal(2, [0,2]) + sage: C = crystals.KleshchevPartitions(2, [0,2]) sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) sage: x.epsilon(2) 1 @@ -313,7 +313,7 @@ def phi(self, i): EXAMPLES:: - sage: C = KleshchevCrystal(2, [0,2]) + sage: C = crystals.KleshchevPartitions(2, [0,2]) sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) sage: x.phi(2) 1 @@ -339,7 +339,7 @@ def weight(self): EXAMPLES:: - sage: C = KleshchevCrystal(2, [0,2]) + sage: C = crystals.KleshchevPartitions(2, [0,2]) sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) sage: x.weight() 3*Lambda[0] - Lambda[1] diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index a80ab3d010c..e86aa45ae0f 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -56,7 +56,7 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: x = B([(8,1),(6,0),(5,1),(5,0),(4,0),(4,1),(4,1),(3,0),(3,0),(3,1),(3,1),(1,0),(1,2),(1,2)]); x {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], (3; 0], (3; 0], (3; 1], (3; 1], (1; 0], (1; 2], (1; 2]} @@ -76,10 +76,10 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): We check an `\widehat{\mathfrak{sl}}_2` example against the generalized Young walls:: - sage: B = InfinityCrystalOfMultisegments(1) + sage: B = crystals.infinity.Multisegments(1) sage: S = B.subcrystal(max_depth=4) sage: G = B.digraph(subset=S) - sage: C = InfinityCrystalOfGeneralizedYoungWalls(1) + sage: C = crystals.infinity.GeneralizedYoungWalls(1) sage: SC = C.subcrystal(max_depth=4) sage: GC = C.digraph(subset=SC) sage: G.is_isomorphic(GC, edge_labels=True) @@ -97,7 +97,7 @@ def __init__(self, n): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: TestSuite(B).run() """ self._cartan_type = CartanType(['A', n, 1]) @@ -110,7 +110,7 @@ def _repr_(self): EXAMPLES:: - sage: InfinityCrystalOfMultisegments(2) + sage: crystals.infinity.Multisegments(2) The infinity crystal of BZ-multisegments of type ['A', 2, 1] """ return "The infinity crystal of BZ-multisegments of type {}".format(self._cartan_type) @@ -121,7 +121,7 @@ def module_generator(self): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: B.module_generator() {} """ @@ -134,7 +134,7 @@ def weight_lattice_realization(self): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: B.weight_lattice_realization() Root lattice of the Root system of type ['A', 2, 1] """ @@ -150,7 +150,7 @@ def __init__(self, parent, value): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: mg = B.module_generator() sage: TestSuite(mg).run() """ @@ -158,7 +158,7 @@ def __init__(self, parent, value): value = [(k, ZM(i)) for k,i in value] def sort_cmp(x,y): c = cmp(y[0],x[0]) - if c == 0: + if not c: c = cmp(ZZ(x[1]),ZZ(y[1])) return c ElementWrapper.__init__(self, parent, tuple(sorted(value, cmp=sort_cmp))) @@ -169,7 +169,7 @@ def _repr_(self): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: B([(4,2), (3,0), (3,1), (1,1), (1,0)]) {(4; 2], (3; 0], (3; 1], (1; 0], (1; 1]} """ @@ -182,7 +182,7 @@ def _latex_(self): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: latex(B([(4,2), (3,0), (3,1), (1,1), (1,0)])) \bigl\{(4; 2], (3; 0], (3; 1], (1; 0], (1; 1]\bigr\} """ @@ -206,14 +206,14 @@ def _sig(self, i): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) sage: b._sig(0) (1, None, 1) sage: b._sig(1) (None, None, 0) """ - if len(self.value) == 0: + if not self.value: return (None, None) pos = [] block = self.value[0][0] @@ -243,7 +243,7 @@ def _sig(self, i): p = None ep -= cur cur = 0 - elif cur == 0: + elif not cur: p = None elif cur > 0 and old <= 0: p = k @@ -259,7 +259,7 @@ def e(self, i): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) sage: b.e(0) {(4; 2], (3; 0], (3; 1], (1; 1]} @@ -289,7 +289,7 @@ def f(self, i): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) sage: b.f(0) {(4; 2], (3; 0], (3; 1], (1; 0], (1; 0], (1; 1]} @@ -317,7 +317,7 @@ def epsilon(self, i): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) sage: b.epsilon(0) 1 @@ -344,7 +344,7 @@ def phi(self, i): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) sage: b.phi(0) 1 @@ -363,7 +363,7 @@ def weight(self): EXAMPLES:: - sage: B = InfinityCrystalOfMultisegments(2) + sage: B = crystals.infinity.Multisegments(2) sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) sage: b.weight() 4*alpha[0] + 4*alpha[1] + 4*alpha[2] From 0ebd4660001df7d027ac4362baa8311705133c59 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 15 Apr 2014 18:52:32 -0700 Subject: [PATCH 009/151] Fixed extraenous period. --- src/sage/combinat/crystals/highest_weight_crystals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/crystals/highest_weight_crystals.py b/src/sage/combinat/crystals/highest_weight_crystals.py index 8d86f9eda1a..15016eea21e 100644 --- a/src/sage/combinat/crystals/highest_weight_crystals.py +++ b/src/sage/combinat/crystals/highest_weight_crystals.py @@ -155,7 +155,7 @@ def HighestWeightCrystal(dominant_weight, model=None): sage: wt = La[0] + La[2] sage: crystals.HighestWeight(wt, model='GeneralizedYoungWalls') Highest weight crystal of generalized Young walls of - Cartan type ['A', 3, 1] and highest weight Lambda[0] + Lambda[2]. + Cartan type ['A', 3, 1] and highest weight Lambda[0] + Lambda[2] sage: crystals.HighestWeight(wt, model='KleshchevPartitions') The crystal of multipartitions of type ['A', 4, 1] and residues (0, 2) """ From 65bc2b0ab4f92d91815aae7574aa7e5122f528ec Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 14 May 2014 22:23:51 -0700 Subject: [PATCH 010/151] Fixed != and doctests. --- src/sage/combinat/crystals/affinization.py | 33 ++++++++++++++++--- .../rigged_configurations/kr_tableaux.py | 2 +- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py index 84989317c89..f179ef00b3a 100644 --- a/src/sage/combinat/crystals/affinization.py +++ b/src/sage/combinat/crystals/affinization.py @@ -71,10 +71,12 @@ def __init__(self, B): """ Initialize ``self``. - EXAMPLES:: + EXAMPLES: + + We skip the Stembridge axioms test since this this is an abstract sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() - sage: TestSuite(A).run() # long time + sage: TestSuite(A).run(skip="_test_stembridge_local_axioms") # long time """ if not B.cartan_type().is_affine(): raise ValueError("must be an affine crystal") @@ -107,6 +109,7 @@ def weight_lattice_realization(self): """ return self.cartan_type().root_system().ambient_space() + # TODO: This should become unnecessary once we have a proper category for KR crystals def digraph(self, subset=None, index_set=None): """ Return the DiGraph associated with ``self``. See @@ -187,6 +190,8 @@ def __eq__(self, other): sage: mg = A.module_generators[0] sage: mg == mg True + sage: mg == mg.f(2).e(2) + True sage: KT = crystals.TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[1,2],[2,1]]) sage: A = crystals.Affinization(KT) sage: A(KT.module_generators[3], 1).f(0) == A.module_generators[0] @@ -197,7 +202,7 @@ def __eq__(self, other): return self.parent() == other.parent() \ and self._b == other._b and self._m == other._m - def __neq__(self, other): + def __ne__(self, other): """ Check inequality. @@ -207,11 +212,31 @@ def __neq__(self, other): sage: mg = A.module_generators[0] sage: mg != mg.f(2) True - sage: mg != mg + sage: mg != mg.f(2).e(2) False """ return not self.__eq__(other) + def __lt__(self, other): + """ + Check less than. + + EXAMPLES:: + + sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() + sage: S = A.subcrystal(max_depth=2) + sage: sorted(S) + [[[1, 1], [2, 2]](0), + [[1, 1], [2, 3]](0), + [[1, 2], [2, 3]](0), + [[1, 1], [3, 3]](0), + [[1, 1], [2, 3]](1), + [[1, 2], [2, 3]](1), + [[1, 2], [3, 3]](1), + [[2, 2], [3, 3]](2)] + """ + return self._m < other._m or (self._m == other._m and self._b < other._b) + def e(self, i): """ Return the action of `e_i` on ``self``. diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index ce7db63ae6c..0a8698ed8b4 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -502,7 +502,7 @@ def affinization(self): EXAMPLES:: - sage: K = KirillovReshetikhinTableaux(['A',2,1], 1, 1) + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1, model='KR') sage: K.affinization() Affinization of Kirillov-Reshetikhin tableaux of type ['A', 2, 1] and shape (1, 1) """ From 3bb44166d4cf42a95d7a5fd0475fc53472bd97c8 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 14 Mar 2015 23:05:36 -0700 Subject: [PATCH 011/151] Initial import of classical Lie algebras. --- .../en/reference/algebras/lie_algebras.rst | 1 + .../lie_algebras/classical_lie_algebra.py | 1045 +++++++++++++++++ src/sage/algebras/lie_algebras/examples.py | 50 +- src/sage/algebras/lie_algebras/lie_algebra.py | 17 + 4 files changed, 1064 insertions(+), 49 deletions(-) create mode 100644 src/sage/algebras/lie_algebras/classical_lie_algebra.py diff --git a/src/doc/en/reference/algebras/lie_algebras.rst b/src/doc/en/reference/algebras/lie_algebras.rst index 23a49b8f498..46288a14af1 100644 --- a/src/doc/en/reference/algebras/lie_algebras.rst +++ b/src/doc/en/reference/algebras/lie_algebras.rst @@ -4,6 +4,7 @@ Lie Algebras .. toctree:: :maxdepth: 2 + sage/algebras/lie_algebras/classical_lie_algebra.py sage/algebras/lie_algebras/examples.py sage/algebras/lie_algebras/heisenberg.py sage/algebras/lie_algebras/lie_algebra.py diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py new file mode 100644 index 00000000000..f52fa53727b --- /dev/null +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -0,0 +1,1045 @@ +""" +Classical Lie Algebras + +These are the Lie algebras corresponding to types `A_n`, `B_n`, `C_n`, `D_n`. + +AUTHORS: + +- Travis Scrimshaw (2013-05-03): Initial version +""" + +#***************************************************************************** +# Copyright (C) 2013 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# See the GNU General Public License for more details; the full text +# is available at: +# +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.abstract_method import abstract_method +from sage.misc.cachefunc import cached_method +from sage.misc.misc import repr_lincomb +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element import RingElement +from sage.structure.indexed_generators import IndexedGenerators +from sage.categories.algebras import Algebras +from sage.categories.lie_algebras import LieAlgebras + +from sage.algebras.algebra import Algebra +from sage.algebras.free_algebra import FreeAlgebra +from sage.algebras.lie_algebras.lie_algebra import FinitelyGeneratedLieAlgebra, LieAlgebraFromAssociative +from sage.algebras.lie_algebras.lie_algebra_element import LieAlgebraElement +from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.root_system.cartan_matrix import CartanMatrix +from sage.combinat.free_module import CombinatorialFreeModule +from sage.matrix.matrix_space import MatrixSpace +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.arith import binomial +from sage.sets.family import Family + +class ClassicalMatrixLieAlgebra(LieAlgebraFromAssociative): + """ + A classical Lie algebra represented using matrices. + """ + @staticmethod + def __classcall_private__(cls, R, cartan_type): + """ + Return the correct parent based on input. + + INPUT: + + - ``R`` -- the base ring + - ``ct`` -- the Cartan type + + EXAMPLES:: + + sage: from sage.algebras.lie_algebras.classical_lie_algebra import ClassicalMatrixLieAlgebra + sage: ClassicalMatrixLieAlgebra(QQ, ['A', 4]) + Special linear Lie algebra of rank 5 over Rational Field + sage: ClassicalMatrixLieAlgebra(QQ, CartanType(['B',4])) + Special orthogonal Lie algebra of rank 9 over Rational Field + sage: ClassicalMatrixLieAlgebra(QQ, 'C4') + Symplectic Lie algebra of rank 8 over Rational Field + sage: ClassicalMatrixLieAlgebra(QQ, cartan_type=['D',4]) + Special orthogonal Lie algebra of rank 8 over Rational Field + """ + cartan_type = CartanType(cartan_type) + + if not cartan_type.is_finite(): + raise ValueError("only for finite types") + + if cartan_type.type() == 'A': + return sl(R, cartan_type.rank() + 1, 'matrix') + if cartan_type.type() == 'B': + return so(R, 2*cartan_type.rank() + 1, 'matrix') + if cartan_type.type() == 'C': + return sp(R, 2*cartan_type.rank(), 'matrix') + if cartan_type.type() == 'D': + return so(R, 2*cartan_type.rank(), 'matrix') + raise NotImplementedError("only implemented for types A, B, C, D") + + def __init__(self, R, ct, e, f, h): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 3, representation='matrix') + sage: TestSuite(g).run() + """ + n = len(e) + names = ['e%s'%i for i in range(1, n+1)] + names += ['f%s'%i for i in range(1, n+1)] + names += ['h%s'%i for i in range(1, n+1)] + category = LieAlgebras(R).FiniteDimensional().WithBasis() + LieAlgebraFromAssociative.__init__(self, e[0].parent(), + gens=tuple(e + f + h), + names=tuple(names), + index_set=tuple(names), + category=category) + self._cartan_type = ct + + gens = tuple(self.gens()) + i_set = ct.index_set() + self._e = Family(dict( (i, gens[c]) for c,i in enumerate(i_set) )) + self._f = Family(dict( (i, gens[n+c]) for c,i in enumerate(i_set) )) + self._h = Family(dict( (i, gens[2*n+c]) for c,i in enumerate(i_set) )) + + def e(self, i): + r""" + Return the generator `e_i`. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 3, representation='matrix') + sage: g.e(2) + [0 0 0] + [0 0 1] + [0 0 0] + """ + return self._e[i] + + def f(self, i): + r""" + Return the generator `f_i`. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 3, representation='matrix') + sage: g.f(2) + [0 0 0] + [0 0 0] + [0 1 0] + """ + return self._f[i] + + def h(self, i): + """ + Return the generator `h_i`. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 3, representation='matrix') + sage: g.h(2) + [ 0 0 0] + [ 0 1 0] + [ 0 0 -1] + """ + return self._h[i] + + @cached_method + def index_set(self): + """ + Return the index_set of ``self``. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 3, representation='matrix') + sage: g.index_set() + (1, 2) + """ + return self._cartan_type.index_set() + + def cartan_type(self): + """ + Return the Cartan type of ``self``. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 3, representation='matrix') + sage: g.cartan_type() + ['A', 2] + """ + return self._cartan_type + + def epsilon(self, i, h): + r""" + Return the action of the functional + `\varepsilon_i \colon \mathfrak{h} \to R`, where `R` is the base + ring of ``self``, on the element ``h``. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 3, representation='matrix') + sage: g.epsilon(1, g.h(1)) + 1 + sage: g.epsilon(2, g.h(1)) + -1 + sage: g.epsilon(3, g.h(1)) + 0 + """ + return h[i-1,i-1] + + @abstract_method + def simple_root(self, i, h): + r""" + Return the action of the simple root + `\alpha_i \colon \mathfrak{h} \to R`, where `R` is the base + ring of ``self``, on the element ``h``. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 3, representation='matrix') + sage: g.simple_root(1, g.h(1)) + 2 + sage: g.simple_root(1, g.h(2)) + -1 + """ + + def highest_root_basis_elt(self, pos=True): + r""" + Return the basis element corresponding to the highest root `\theta`. + If ``pos`` is ``True``, then returns `e_{\theta}`, otherwise it + returns `f_{\theta}`. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 3, representation='matrix') + sage: g.highest_root_basis_elt() + [0 0 1] + [0 0 0] + [0 0 0] + """ + RL = self._cartan_type.root_system().root_lattice() + coroots = RL.simple_coroots() + theta = RL.highest_root() + i,w = theta.to_simple_root(True) + r = RL.simple_root(i) + if pos: + gens = self._e + else: + gens = self._f + cur = gens[i] + for j in w: + for k in range(-r.scalar(coroots[j])): + cur = self.bracket(gens[j], cur) + return cur + + @cached_method + def basis(self): + """ + Return the basis of ``self``. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 3, representation='matrix') + sage: tuple(g.basis()) + ( + [0 0 0] [0 0 0] [ 0 0 0] [ 1 0 0] [0 1 0] [0 0 0] + [1 0 0] [0 0 0] [ 0 1 0] [ 0 -1 0] [0 0 0] [0 0 1] + [0 0 0], [0 1 0], [ 0 0 -1], [ 0 0 0], [0 0 0], [0 0 0] + ) + """ + d = {} + for i in self.index_set(): + d['e{}'.format(i)] = self._e[i] + d['f{}'.format(i)] = self._f[i] + d['h{}'.format(i)] = self._h[i] + return Family(d) + + # TODO: Uncomment once #16825 is done + #def affine(self, kac_moody=False): + # """ + # Return the (untwisted) affine (Kac-Moody) Lie algebra of ``self``. + # """ + # from sage.algebras.lie_algebras.affine_lie_algebra import AffineLieAlgebra + # return AffineLieAlgebra(self, kac_moody) + +class gl(LieAlgebraFromAssociative): + r""" + The Lie algebra `\mathfrak{gl}_n` which consists of all `n \times n` + matrices. + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the size of the matrix + """ + def __init__(self, R, n): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: g = lie_algebras.gl(QQ, 4) + sage: TestSuite(g).run() + """ + MS = MatrixSpace(R, n, sparse=True) + one = R.one() + names = [] + gens = [] + for i in range(n): + for j in range(n): + names.append('E_{0}_{1}'.format(i,j)) + mat = MS({(i,j):one}) + mat.set_immutable() + gens.append(mat) + self._n = n + category = LieAlgebras(R).FiniteDimensional().WithBasis() + LieAlgebraFromAssociative.__init__(self, MS, tuple(gens), + names=tuple(names), + index_set=tuple(names), + category=category) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: lie_algebras.gl(QQ, 4) + General linear Lie algebra of rank 4 over Rational Field + """ + return "General linear Lie algebra of rank {} over {}".format(self._n, self.base_ring()) + + def killing_form(self, x, y): + r""" + Return the Killing form on ``x`` and ``y``. + + The Killing form on `\mathfrak{gl}_n` is: + + .. MATH:: + + \langle x \mid y \rangle = 2n \mathrm{tr}(xy) - 2 \mathrm{tr}(x) + \mathrm{tr}(y). + + EXAMPLES:: + + sage: g = lie_algebras.gl(QQ, 4) + sage: g.killing_form() + """ + return 2 * self._n * (x.value * y.value).trace() \ + - 2 * x.value.trace() * y.value.trace() + + def basis(self): + """ + Return the basis of ``self``. + + EXAMPLES:: + + sage: g = lie_algebras.gl(QQ, 2) + sage: g.basis() + ( + [1 0] [0 1] [0 0] [0 0] + [0 0], [0 0], [1 0], [0 1] + ) + """ + return self.gens() + +class sl(ClassicalMatrixLieAlgebra): + r""" + The Lie algebra `\mathfrak{sl}_n` which consists of `n \times n` matrices + with trace 0. This is the Lie algebra of type `A_{n-1}`. + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the size of the matrix + - ``representation`` -- (default: ``'bracket'``) can be one of + the following: + + * ``'bracket'`` - use brackets and the Chevalley basis + * ``'matrix'`` - use matrices + + EXAMPLES: + + We first construct `\mathfrak{sl}_2` using the Chevalley basis:: + + sage: sl2 = lie_algebras.sl(QQ, 2); sl2 + Lie algebra of ['A', 1] in the Chevalley basis + sage: E,F,H = sl2.gens() + sage: E.bracket(F) == H + True + sage: H.bracket(E) == 2*E + True + sage: H.bracket(F) == -2*F + True + + We now construct `\mathfrak{sl}_2` as a matrix Lie algebra:: + + sage: sl2 = lie_algebras.sl(QQ, 2, representation='matrix') + sage: E,F,H = sl2.gens() + sage: E.bracket(F) == H + True + sage: H.bracket(E) == 2*E + True + sage: H.bracket(F) == -2*F + True + """ + # FIXME: Move this and the same methods for all other types into separate + # functions for the Lie algebra examples because it breaks pickling + @staticmethod + def __classcall_private__(cls, R, n, representation='bracket'): + """ + Choose the representation. + """ + if representation == 'bracket': + return LieAlgebraChevalleyBasis(R, ['A', n-1]) + if representation == 'matrix': + return super(sl, cls).__classcall__(cls, R, n) + raise ValueError("invalid representation") + + def __init__(self, R, n): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 5, representation='matrix') + sage: TestSuite(g).run() + """ + MS = MatrixSpace(R, n, sparse=True) + one = R.one() + e = [MS({(i,i+1):one}) for i in range(n-1)] + f = [MS({(i+1,i):one}) for i in range(n-1)] + h = [MS({(i,i):one, (i+1,i+1):-one}) for i in range(n-1)] + self._n = n + ClassicalMatrixLieAlgebra.__init__(self, R, CartanType(['A', n-1]), e, f, h) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: lie_algebras.sl(QQ, 5, representation='matrix') + Special linear Lie algebra of rank 5 over Rational Field + """ + return "Special linear Lie algebra of rank {} over {}".format(self._n, self.base_ring()) + + def killing_form(self, x, y): + r""" + Return the Killing form on ``x`` and ``y``. + + The Killing form on `\mathfrak{sl}_n` is: + + .. MATH:: + + \langle x \mid y \rangle = 2n \mathrm{tr}(xy). + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 5, representation='matrix') + """ + return 2 * self._n * (x.value * y.value).trace() + + def simple_root(self, i, h): + r""" + Return the action of the simple root + `\alpha_i \colon \mathfrak{h} \to R`, where `R` is the base + ring of ``self``, on the element ``j``. + + EXAMPLES:: + + sage: g = lie_algebras.sl(QQ, 5, representation='matrix') + sage: matrix([[g.simple_root(i, g.h(j)) for i in g.index_set()] for j in g.index_set()]) + [ 2 -1 0 0] + [-1 2 -1 0] + [ 0 -1 2 -1] + [ 0 0 -1 2] + """ + i = self.index_set().index(i) + return h[i,i] - h[i+1,i+1] + +class so(ClassicalMatrixLieAlgebra): + r""" + The Lie algebra `\mathfrak{so}_n` which consists of orthogonal `n \times n` + matrices. This is the Lie algebra of type `B_{(n-1)/2}` or `D_{n/2}` if `n` + is odd or even respectively. + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the size of the matrix + - ``representation`` -- (default: ``'bracket'``) can be one of + the following: + + * ``'bracket'`` - use brackets and the Chevalley basis + * ``'matrix'`` - use matrices + """ + @staticmethod + def __classcall_private__(cls, R, n, representation='bracket'): + """ + Choose the representation. + """ + if representation == 'bracket': + if n % 2 == 0: + return LieAlgebraChevalleyBasis(R, ['D', n//2]) + else: + return LieAlgebraChevalleyBasis(R, ['B', (n-1)//2]) + if representation == 'matrix': + return super(so, cls).__classcall__(cls, R, n) + raise ValueError("invalid representation") + + def __init__(self, R, n): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: g = lie_algebras.so(QQ, 8, representation='matrix') + sage: TestSuite(g).run() + sage: g = lie_algebras.so(QQ, 9, representation='matrix') + sage: TestSuite(g).run() + """ + MS = MatrixSpace(R, n, sparse=True) + one = R.one() + self._n = n + if n % 2 == 0: # Even + m = n / 2 - 1 # -1 for indexing + n -= 1 + e = [MS({(m-1,n):one, (m,n-1):-one})] + f = [MS({(n,m-1):one, (n-1,m):-one})] + h = [MS({(m-1,m-1):one, (m,m):one, (n-1,n-1):-one, (n,n):-one})] + m += 1 + ct = CartanType(['D', m]) + else: # Odd + m = (n-1) / 2 - 1 # -1 for indexing + n -= 1 + e = [MS({(m,n):2, (n,n-1):-2})] + f = [MS({(n,m):one, (n-1,n):-one})] + h = [MS({(m,m):2, (n-1,n-1):-2})] + m += 1 + ct = CartanType(['B', m]) + e = [MS({(i,i+1):one, (m+i+1,m+i):-one}) for i in range(m-1)] + e + f = [MS({(i+1,i):one, (m+i,m+i+1):-one}) for i in range(m-1)] + f + h = [MS({(i,i):one, (i+1,i+1):-one, (m+i,m+i):-one, (m+i+1,m+i+1):one}) for i in range(m-1)] + h + ClassicalMatrixLieAlgebra.__init__(self, R, ct, e, f, h) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: LieAlgebra(QQ, cartan_type=['B', 4], representation='matrix') + Special orthogonal Lie algebra of rank 9 over Rational Field + sage: LieAlgebra(QQ, cartan_type=['D', 4], representation='matrix') + Special orthogonal Lie algebra of rank 8 over Rational Field + """ + return "Special orthogonal Lie algebra of rank {} over {}".format(self._n, self.base_ring()) + + def killing_form(self, x, y): + r""" + Return the Killing form on ``x`` and ``y``. + + The Killing form on `\mathfrak{so}_n` is: + + .. MATH:: + + \langle x \mid y \rangle = (n - 2) \mathrm{tr}(xy). + + EXAMPLES:: + + sage: g = lie_algebras.so(QQ, 8, representation='matrix') + sage: g = lie_algebras.so(QQ, 9, representation='matrix') + """ + return 2 * self._n * (x.value * y.value).trace() + + def simple_root(self, i, h): + r""" + Return the action of the simple root + `\alpha_i \colon \mathfrak{h} \to R`, where `R` is the base + ring of ``self``, on the element ``j``. + + EXAMPLES: + + The even or type `D` case:: + + sage: g = lie_algebras.so(QQ, 8, representation='matrix') + sage: matrix([[g.simple_root(i, g.h(j)) for i in g.index_set()] for j in g.index_set()]) + [ 2 -1 0 0] + [-1 2 -1 -1] + [ 0 -1 2 0] + [ 0 -1 0 2] + + The odd or type `B` case:: + + sage: g = lie_algebras.so(QQ, 9, representation='matrix') + sage: matrix([[g.simple_root(i, g.h(j)) for i in g.index_set()] for j in g.index_set()]) + [ 2 -1 0 0] + [-1 2 -1 0] + [ 0 -1 2 -1] + [ 0 0 -2 2] + """ + i = self.index_set().index(i) + if i == len(self.index_set()) - 1: + if self._n % 2 == 0: + return h[i-1,i-1] + h[i,i] + # otherwise we are odd + return h[i,i] + return h[i,i] - h[i+1,i+1] + +class sp(ClassicalMatrixLieAlgebra): + r""" + The Lie algebra `\mathfrak{sp}_{2k}` which consists of `2k \times 2k` + matrices `X` that satisfy the equation: + + .. MATH:: + + X^T M - M X = 0 + + where + + .. MATH:: + + M = \begin{pmatrix} + 0 & I_k \\ + -I_k & 0 + \end{pmatrix}. + + This is the Lie algebra of type `C_k`. + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the size of the matrix + - ``representation`` -- (default: ``'bracket'``) can be one of + the following: + + * ``'bracket'`` - use brackets and the Chevalley basis + * ``'matrix'`` - use matrices + """ + @staticmethod + def __classcall_private__(cls, R, n, representation='bracket'): + """ + Choose the representation. + """ + if n % 2 != 0: + raise ValueError("n must be even") + + if representation == 'bracket': + return LieAlgebraChevalleyBasis(R, ['C', n//2]) + if representation == 'matrix': + return super(sp, cls).__classcall__(cls, R, n) + raise ValueError("invalid representation") + + def __init__(self, R, n): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: g = lie_algebras.sp(QQ, 8, representation='matrix') + sage: TestSuite(g).run() + """ + MS = MatrixSpace(R, n, sparse=True) + one = R.one() + self._n = n + n = n // 2 + e = [MS({(i,i+1):one, (n+i+1,n+i):-one}) for i in range(n-1)] + e.append(MS({(n-1,2*n-1):one})) # -1 for indexing + f = [MS({(i+1,i):one, (n+i,n+i+1):-one}) for i in range(n-1)] + f.append(MS({(2*n-1,n-1):one})) # -1 for indexing + h = [MS({(i,i):one, (i+1,i+1):-one, (n+i,n+i):-one, (n+i+1,n+i+1):one}) for i in range(n-1)] + h.append(MS({(n-1,n-1):one, (2*n-1,2*n-1):-one})) # -1 for indexing + ClassicalMatrixLieAlgebra.__init__(self, R, CartanType(['C', n]), e, f, h) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: lie_algebras.sp(QQ, 8, representation='matrix') + Symplectic Lie algebra of rank 8 over Rational Field + """ + return "Symplectic Lie algebra of rank {} over {}".format(self._n, self.base_ring()) + + def killing_form(self, x, y): + r""" + Return the Killing form on ``x`` and ``y``. + + The Killing form on `\mathfrak{sp}_n` is: + + .. MATH:: + + \langle x \mid y \rangle = (2n + 2) \mathrm{tr}(xy). + + EXAMPLES:: + + sage: g = lie_algebras.sp(QQ, 8, representation='matrix') + """ + return (2 * self._n + 2) * (x.value * y.value).trace() + + def simple_root(self, i, h): + r""" + Return the action of the simple root + `\alpha_i \colon \mathfrak{h} \to R`, where `R` is the base + ring of ``self``, on the element ``j``. + + EXAMPLES:: + + sage: g = lie_algebras.sp(QQ, 8, representation='matrix') + sage: matrix([[g.simple_root(i, g.h(j)) for i in g.index_set()] for j in g.index_set()]) + [ 2 -1 0 0] + [-1 2 -1 0] + [ 0 -1 2 -2] + [ 0 0 -1 2] + """ + i = self.index_set().index(i) + if i == self._n / 2 - 1: + return 2*h[i,i] + return h[i,i] - h[i+1,i+1] + + +####################################### +## Chevalley Basis + +class LieAlgebraChevalleyBasis(LieAlgebraWithStructureCoefficients): + r""" + A simple finite dimensional Lie algebra in the Chevalley basis. + + Let `L` be a simple complex Lie algebra with roots `\Phi`, then the + Chevalley basis is given by `e_{\alpha}` for all `\alpha \in \Phi` and + `h_{\alpha_i} := h_i` where `\alpha_i` is a simple root subject. These + generators are subject to the relations: + + .. MATH:: + + \begin{aligned} + [h_i, h_j] & = 0 + \\ [h_i, e_{\beta}] & = A_{\alpha_i, \beta} e_{\beta} + \\ [e_{\beta}, e_{-\beta}] & = \sum_i A_{\beta, \alpha_i} h_i + \\ [e_{\beta}, e_{\gamma}] & = \begin{cases} + N_{\beta,\gamma} e_{\beta + \gamma} & \beta + \gamma \in \Phi \\ + 0 & \text{otherwise.} \end{cases} + \end{aligned} + + where `A_{\alpha, \beta} = \frac{2 (\alpha, \beta)}{(\alpha, \alpha)}` and + `N_{\alpha, \beta}` is the maximum such that + `\alpha - N_{\alpha, \beta} \beta \in \Phi`. + + For computing the signs of the coefficients, see Section 3 of [CMT]_. + + REFERNCES: + + .. [CMT] A. M. Cohen, S. H. Murray, D. E. Talyor. *Groups of Lie type*. + http://www.win.tue.nl/~amc/pub/papers/cmt.pdf + """ + @staticmethod + def __classcall_private__(cls, R, cartan_type): + """ + Normalize ``self`` to ensure a unique represntation. + + TESTS:: + + sage: L1 = LieAlgebra(QQ, cartan_type=['A', 2]) + sage: L2 = LieAlgebra(QQ, cartan_type=CartanType(['A', 2])) + sage: L3 = LieAlgebra(QQ, cartan_type=CartanMatrix(['A', 2])) + sage: L1 is L2 and L2 is L3 + True + """ + return super(LieAlgebraChevalleyBasis, cls).__classcall__( + cls, R, CartanType(cartan_type)) + + def __init__(self, R, cartan_type): + r""" + Initialize ``self``. + + TESTS:: + + sage: L = LieAlgebra(QQ, cartan_type=['A',2]) + sage: TestSuite(L).run() + """ + self._cartan_type = cartan_type + RL = cartan_type.root_system().root_lattice() + alpha = RL.simple_roots() + p_roots = list(RL.positive_roots_by_height()) + n_roots = map(lambda x: -x, p_roots) + alphacheck = RL.simple_coroots() + roots = RL.roots() + num_sroots = len(alpha) + + # Determine the signs for the structure coefficients from the root system + # We first create the special roots + sp_sign = {} + for i,a in enumerate(p_roots): + for b in p_roots[i+1:]: + if a + b not in p_roots: + continue + + # Compute the sign for the extra special pair + x, y = (a + b).extraspecial_pair() + + if (x, y) == (a, b): # If it already is an extra special pair + sp_sign[(x, y)] = 1 + sp_sign[(y, x)] = -1 + continue + + if b - x in roots: + t1 = (b-x).norm_squared() / b.norm_squared() * sp_sign[(x, b-x)] * sp_sign[(a, y-a)] + else: + t1 = 0 + if a - x in roots: + t2 = (a-x).norm_squared() / a.norm_squared() * sp_sign[(x, a-x)] * sp_sign[(b, y-b)] + else: + t2 = 0 + + if t1 - t2 > 0: + sp_sign[(a,b)] = 1 + else: + sp_sign[(a,b)] = -1 + sp_sign[(b,a)] = -sp_sign[(a,b)] + + # Function to construct the structure coefficients (up to sign) + def e_coeff(r, s): + p = 1 + while r - p*s in roots: + p += 1 + return p + + # Now we can compute all necessary structure coefficients + s_coeffs = {} + for i,r in enumerate(p_roots): + # [e_r, h_i] and [h_i, f_r] + for ac in alphacheck: + c = r.scalar(ac) + if c == 0: + continue + s_coeffs[(r, ac)] = {r: -c} + s_coeffs[(ac, -r)] = {-r: -c} + + # [e_r, f_r] + h_sum = {} + for j, c in r.associated_coroot(): + h_sum[alphacheck[j]] = c + s_coeffs[(r, -r)] = h_sum + + # [e_r, e_s] and [e_r, f_s] with r != +/-s + for j, s in enumerate(p_roots[i+1:]): + j += i+1 + # Since h(s) > h(r), we will always have s - r > 0 (if it is a root) + # [e_r, f_s] + if s - r in p_roots: + c = e_coeff(r, -s) + a,b = s-r, r + if p_roots.index(a) + 1 > p_roots.index(b): # Note a != b + c = -c * sp_sign[(b, a)] + else: + c *= sp_sign[(a, b)] + s_coeffs[(-r, s)] = {a: -c} + s_coeffs[(r, -s)] = {a: c} + + # [e_r, e_s] + a = r + s + if a in p_roots: + # (r, s) is a special pair + c = e_coeff(r, s) * sp_sign[(r, s)] + s_coeffs[(r, s)] = {a: c} + s_coeffs[(-r, -s)] = {-a: -c} + + # Lastly, make sure a < b for all (a, b) in the coefficients and flip if necessary + for k in s_coeffs.keys(): + a,b = k[0], k[1] + if self._basis_cmp(a, b) > 0: + s_coeffs[(b,a)] = {k:-v for k,v in s_coeffs[k].items()} + del s_coeffs[k] + + names = ['e{}'.format(i) for i in range(1, num_sroots+1)] + names += ['f{}'.format(i) for i in range(1, num_sroots+1)] + names += ['h{}'.format(i) for i in range(1, num_sroots+1)] + category = LieAlgebras(R).FiniteDimensional().WithBasis() + index_set = p_roots + n_roots + list(alphacheck) + LieAlgebraWithStructureCoefficients.__init__(self, R, s_coeffs, names, index_set, + category, prefix='E', bracket='[', + generator_cmp=self._basis_cmp) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: LieAlgebra(QQ, cartan_type=['A', 2]) + Lie algebra of ['A', 2] in the Chevalley basis + """ + return "Lie algebra of {} in the Chevalley basis".format(self._cartan_type) + + def _repr_generator(self, m): + """ + Return a string representation of the basis element indexed by ``m``. + """ + if m in self._cartan_type.root_system().root_lattice().simple_coroots(): + return "h{}".format(m.support()[0]) + return IndexedGenerators._repr_generator(self, m) + + def _basis_cmp(self, x, y): + """ + Compare two basis element indices. We order the basis elements by + positive roots, coroots, and negative roots and then according to + height. + + OUTPUT: + + If ``x == y``, return 0. If ``x < y``, return -1. Else return 1. + """ + if x == y: + return 0 + + RL = self._cartan_type.root_system().root_lattice() + p_roots = list(RL.positive_roots_by_height()) + n_roots = map(lambda x: -x, p_roots) + alphacheck = RL.simple_coroots() + + if x in p_roots: + if y in p_roots: + return cmp(p_roots.index(x), p_roots.index(y)) + return -1 + + if x in alphacheck: + if y in p_roots: + return 1 + if y in alphacheck: + return cmp(x, y) + return -1 + + # x is in n_roots + if y not in n_roots: + return 1 + return cmp(n_roots.index(x), n_roots.index(y)) + + def cartan_type(self): + """ + Return the Cartan type of ``self``. + """ + return self._cartan_type + + def weyl_group(self): + """ + Return the Weyl group of ``self``. + """ + from sage.combinat.root_system.weyl_group import WeylGroup + return WeylGroup(self._cartan_type) + + # TODO: Uncomment once #16825 is done + #def affine(self, kac_moody=False): + # """ + # Return the (untwisted) affine Lie algebra of ``self``. + # """ + # from sage.algebras.lie_algebras.affine_lie_algebra import AffineLieAlgebra + # return AffineLieAlgebra(self, kac_moody) + + # Useful in creating the UEA + @cached_method + def indices_to_positive_roots_map(self): + """ + Return the map from indices to positive roots. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) + sage: L.indices_to_positive_roots_map() + {1: alpha[1], 2: alpha[2], 3: alpha[1] + alpha[2]} + """ + RL = self._cartan_type.root_system().root_lattice() + return {i+1: r for i,r in enumerate(RL.positive_roots())} + + def lie_algebra_generators(self, str_keys=False): + r""" + Return the Chevalley Lie algebra generators of ``self``. + + INPUT: + + - ``str_keys`` -- (default: ``False``) set to ``True`` to have the + indices indexed by strings instead of simple (co)roots + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A', 1]) + sage: L.lie_algebra_generators() + Finite family {-alpha[1]: E[-alpha[1]], alpha[1]: E[alpha[1]], alphacheck[1]: h1} + sage: L.lie_algebra_generators(True) + Finite family {'f1': E[-alpha[1]], 'h1': h1, 'e1': E[alpha[1]]} + """ + index_set = self._cartan_type.index_set() + RL = self._cartan_type.root_system().root_lattice() + alpha = RL.simple_roots() + alphacheck = RL.simple_coroots() + B = self.basis() + ret = {} + + if str_keys: + for i in index_set: + al = alpha[i] + ret['e{}'.format(i)] = B[al] + ret['f{}'.format(i)] = B[-al] + ret['h{}'.format(i)] = B[alphacheck[i]] + else: + for i in index_set: + al = alpha[i] + ret[al] = B[al] + ret[-al] = B[-al] + ret[alphacheck[i]] = B[alphacheck[i]] + + return Family(ret) + + @cached_method + def gens(self): + """ + Return the generators of ``self`` in the order of `e_i`, `f_i`, + and `h_i`. + """ + index_set = self._cartan_type.index_set() + RL = self._cartan_type.root_system().root_lattice() + alpha = RL.simple_roots() + alphacheck = RL.simple_coroots() + B = self.basis() + + ret = [] + for i in index_set: + ret.append(B[alpha[i]]) + for i in index_set: + ret.append(B[-alpha[i]]) + for i in index_set: + ret.append(B[alphacheck[i]]) + return tuple(ret) + + def gen(self, i): + """ + Return the ``i``-th generator of ``self``. + """ + return self.gens()[i] + + def highest_root_basis_elt(self, pos=True): + r""" + Return the basis element corresponding to the highest root `\theta`. + If ``pos`` is ``True``, then returns `e_{\theta}`, otherwise it + returns `f_{\theta}`. + """ + RL = self._cartan_type.root_system().root_lattice() + theta = RL.highest_root() + B = self.basis() + if pos: + return B[theta] + return B[-theta] + diff --git a/src/sage/algebras/lie_algebras/examples.py b/src/sage/algebras/lie_algebras/examples.py index c0beaaa00a1..03d6c9eb985 100644 --- a/src/sage/algebras/lie_algebras/examples.py +++ b/src/sage/algebras/lie_algebras/examples.py @@ -20,7 +20,7 @@ # http://www.gnu.org/licenses/ #****************************************************************************** -#from sage.algebras.lie_algebras.classical_lie_algebra import gl, sl, so, sp +from sage.algebras.lie_algebras.classical_lie_algebra import gl, sl, so, sp from sage.algebras.lie_algebras.virasoro import VirasoroAlgebra def three_dimensional(R, a, b, c, d, names=['X', 'Y', 'Z']): @@ -144,54 +144,6 @@ def three_dimensional_by_rank(R, n, a=None, names=['X', 'Y', 'Z']): raise ValueError("Invalid rank") -# This can probably be replaced (removed) by sl once the classical Lie -# algebras are implemented -def sl(R, n, representation='bracket'): - r""" - Return the Lie algebra `\mathfrak{sl}_n`. - - EXAMPLES:: - - sage: sl2 = lie_algebras.sl(QQ, 2); sl2 - sl2 over Rational Field - sage: E,F,H = sl2.gens() - sage: E.bracket(F) == H - True - sage: H.bracket(E) == 2*E - True - sage: H.bracket(F) == -2*F - True - - TESTS:: - - sage: sl2 = lie_algebras.sl(QQ, 2, representation='matrix') - sage: E,F,H = sl2.gens() - sage: E.bracket(F) == H - True - sage: H.bracket(E) == 2*E - True - sage: H.bracket(F) == -2*F - True - """ - if n != 2: - raise NotImplementedError("only n=2 is implemented") - - if representation == 'matrix': - from sage.matrix.matrix_space import MatrixSpace - from sage.algebras.lie_algebras.lie_algebra import LieAlgebraFromAssociative - MS = MatrixSpace(R, 2) - E = MS([[0,1],[0,0]]) - F = MS([[0,0],[1,0]]) - H = MS([[1,0],[0,-1]]) - L = LieAlgebraFromAssociative(MS, [E, F, H], ['E', 'F', 'H']) - L.rename("sl2 as a matrix Lie algebra over {}".format(R)) - elif representation == 'bracket': - L = three_dimensional_by_rank(R, 3, names=['E', 'F', 'H']) - else: - raise ValueError("invalid representation") - - return L - def affine_transformations_line(R, names=['X', 'Y'], representation='bracket'): """ The Lie algebra of affine transformations of the line. diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index 048ff3c738e..ec794b8d346 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -193,6 +193,23 @@ def __classcall_private__(cls, R=None, arg0=None, arg1=None, names=None, if assoc is not None: return LieAlgebraFromAssociative(assoc, names=names, index_set=index_set) + # Parse input as a Cartan type + # ----- + + ct = kwds.get("cartan_type", None) + if ct is not None: + ct = CartanType(ct) + if not ct.is_finite(): + raise NotImplementedError("non-finite types are not implemented yet, see trac #14901 for details") + rep = kwds.get("representation", "bracket") + if rep == 'bracket': + from sage.algebras.lie_algebras.classical_lie_algebra import LieAlgebraChevalleyBasis + return LieAlgebraChevalleyBasis(R, ct) + if rep == 'matrix': + from sage.algebras.lie_algebras.classical_lie_algebra import ClassicalMatrixLieAlgebra + return ClassicalMatrixLieAlgebra(R, ct) + raise ValueError("invalid representation") + # Parse the remaining arguments # ----- From e34851a01030f3477641c1e865090785ee718a6d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 17 Mar 2015 10:55:37 -0700 Subject: [PATCH 012/151] Finalizing doctests for classical Lie algebras. --- .../lie_algebras/classical_lie_algebra.py | 247 +++++++++--------- src/sage/algebras/lie_algebras/examples.py | 209 ++++++++++++++- src/sage/combinat/root_system/cartan_type.py | 3 + 3 files changed, 330 insertions(+), 129 deletions(-) diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index f52fa53727b..9080f61e51d 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -49,27 +49,37 @@ class ClassicalMatrixLieAlgebra(LieAlgebraFromAssociative): """ A classical Lie algebra represented using matrices. + + INPUT: + + - ``R`` -- the base ring + - ``ct`` -- the Cartan type of type `A_n`, `B_n`, `C_n`, or `D_n` + + EXAMPLES:: + + sage: lie_algebras.ClassicalMatrix(QQ, ['A', 4]) + Special linear Lie algebra of rank 5 over Rational Field + sage: lie_algebras.ClassicalMatrix(QQ, CartanType(['B',4])) + Special orthogonal Lie algebra of rank 9 over Rational Field + sage: lie_algebras.ClassicalMatrix(QQ, 'C4') + Symplectic Lie algebra of rank 8 over Rational Field + sage: lie_algebras.ClassicalMatrix(QQ, cartan_type=['D',4]) + Special orthogonal Lie algebra of rank 8 over Rational Field """ @staticmethod def __classcall_private__(cls, R, cartan_type): """ Return the correct parent based on input. - INPUT: - - - ``R`` -- the base ring - - ``ct`` -- the Cartan type - EXAMPLES:: - sage: from sage.algebras.lie_algebras.classical_lie_algebra import ClassicalMatrixLieAlgebra - sage: ClassicalMatrixLieAlgebra(QQ, ['A', 4]) + sage: lie_algebras.ClassicalMatrix(QQ, ['A', 4]) Special linear Lie algebra of rank 5 over Rational Field - sage: ClassicalMatrixLieAlgebra(QQ, CartanType(['B',4])) + sage: lie_algebras.ClassicalMatrix(QQ, CartanType(['B',4])) Special orthogonal Lie algebra of rank 9 over Rational Field - sage: ClassicalMatrixLieAlgebra(QQ, 'C4') + sage: lie_algebras.ClassicalMatrix(QQ, 'C4') Symplectic Lie algebra of rank 8 over Rational Field - sage: ClassicalMatrixLieAlgebra(QQ, cartan_type=['D',4]) + sage: lie_algebras.ClassicalMatrix(QQ, cartan_type=['D',4]) Special orthogonal Lie algebra of rank 8 over Rational Field """ cartan_type = CartanType(cartan_type) @@ -78,13 +88,13 @@ def __classcall_private__(cls, R, cartan_type): raise ValueError("only for finite types") if cartan_type.type() == 'A': - return sl(R, cartan_type.rank() + 1, 'matrix') + return sl(R, cartan_type.rank() + 1) if cartan_type.type() == 'B': - return so(R, 2*cartan_type.rank() + 1, 'matrix') + return so(R, 2*cartan_type.rank() + 1) if cartan_type.type() == 'C': - return sp(R, 2*cartan_type.rank(), 'matrix') + return sp(R, 2*cartan_type.rank()) if cartan_type.type() == 'D': - return so(R, 2*cartan_type.rank(), 'matrix') + return so(R, 2*cartan_type.rank()) raise NotImplementedError("only implemented for types A, B, C, D") def __init__(self, R, ct, e, f, h): @@ -130,7 +140,7 @@ def e(self, i): def f(self, i): r""" - Return the generator `f_i`. + Return the generator `f_i`.- EXAMPLES:: @@ -276,6 +286,8 @@ def basis(self): class gl(LieAlgebraFromAssociative): r""" + The matrix Lie algebra `\mathfrak{gl}_n`. + The Lie algebra `\mathfrak{gl}_n` which consists of all `n \times n` matrices. @@ -335,7 +347,10 @@ def killing_form(self, x, y): EXAMPLES:: sage: g = lie_algebras.gl(QQ, 4) - sage: g.killing_form() + sage: x = g.an_element() + sage: y = g.basis()[1] + sage: g.killing_form(x, y) + 8 """ return 2 * self._n * (x.value * y.value).trace() \ - 2 * x.value.trace() * y.value.trace() @@ -357,57 +372,11 @@ def basis(self): class sl(ClassicalMatrixLieAlgebra): r""" - The Lie algebra `\mathfrak{sl}_n` which consists of `n \times n` matrices - with trace 0. This is the Lie algebra of type `A_{n-1}`. - - INPUT: + The matrix Lie algebra `\mathfrak{sl}_n`. - - ``R`` -- the base ring - - ``n`` -- the size of the matrix - - ``representation`` -- (default: ``'bracket'``) can be one of - the following: - - * ``'bracket'`` - use brackets and the Chevalley basis - * ``'matrix'`` - use matrices - - EXAMPLES: - - We first construct `\mathfrak{sl}_2` using the Chevalley basis:: - - sage: sl2 = lie_algebras.sl(QQ, 2); sl2 - Lie algebra of ['A', 1] in the Chevalley basis - sage: E,F,H = sl2.gens() - sage: E.bracket(F) == H - True - sage: H.bracket(E) == 2*E - True - sage: H.bracket(F) == -2*F - True - - We now construct `\mathfrak{sl}_2` as a matrix Lie algebra:: - - sage: sl2 = lie_algebras.sl(QQ, 2, representation='matrix') - sage: E,F,H = sl2.gens() - sage: E.bracket(F) == H - True - sage: H.bracket(E) == 2*E - True - sage: H.bracket(F) == -2*F - True + The Lie algebra `\mathfrak{sl}_n`, which consists of all `n \times n` + matrices with trace 0. This is the Lie algebra of type `A_{n-1}`. """ - # FIXME: Move this and the same methods for all other types into separate - # functions for the Lie algebra examples because it breaks pickling - @staticmethod - def __classcall_private__(cls, R, n, representation='bracket'): - """ - Choose the representation. - """ - if representation == 'bracket': - return LieAlgebraChevalleyBasis(R, ['A', n-1]) - if representation == 'matrix': - return super(sl, cls).__classcall__(cls, R, n) - raise ValueError("invalid representation") - def __init__(self, R, n): """ Initialize ``self``. @@ -449,6 +418,10 @@ def killing_form(self, x, y): EXAMPLES:: sage: g = lie_algebras.sl(QQ, 5, representation='matrix') + sage: x = g.an_element() + sage: y = g.basis()['e1'] + sage: g.killing_form(x, y) + 10 """ return 2 * self._n * (x.value * y.value).trace() @@ -472,34 +445,12 @@ def simple_root(self, i, h): class so(ClassicalMatrixLieAlgebra): r""" - The Lie algebra `\mathfrak{so}_n` which consists of orthogonal `n \times n` - matrices. This is the Lie algebra of type `B_{(n-1)/2}` or `D_{n/2}` if `n` - is odd or even respectively. - - INPUT: + The matrix Lie algebra `\mathfrak{so}_n`. - - ``R`` -- the base ring - - ``n`` -- the size of the matrix - - ``representation`` -- (default: ``'bracket'``) can be one of - the following: - - * ``'bracket'`` - use brackets and the Chevalley basis - * ``'matrix'`` - use matrices + The Lie algebra `\mathfrak{so}_n`, which consists of all real + anti-symmetric `n \times n` matrices. This is the Lie algebra of + type `B_{(n-1)/2}` or `D_{n/2}` if `n` is odd or even respectively. """ - @staticmethod - def __classcall_private__(cls, R, n, representation='bracket'): - """ - Choose the representation. - """ - if representation == 'bracket': - if n % 2 == 0: - return LieAlgebraChevalleyBasis(R, ['D', n//2]) - else: - return LieAlgebraChevalleyBasis(R, ['B', (n-1)//2]) - if representation == 'matrix': - return super(so, cls).__classcall__(cls, R, n) - raise ValueError("invalid representation") - def __init__(self, R, n): """ Initialize ``self``. @@ -561,9 +512,17 @@ def killing_form(self, x, y): EXAMPLES:: sage: g = lie_algebras.so(QQ, 8, representation='matrix') + sage: x = g.an_element() + sage: y = g.basis()['e1'] + sage: g.killing_form(x, y) + 32 sage: g = lie_algebras.so(QQ, 9, representation='matrix') + sage: x = g.an_element() + sage: y = g.basis()['e1'] + sage: g.killing_form(x, y) + 36 """ - return 2 * self._n * (x.value * y.value).trace() + return (self._n - 2) * (x.value * y.value).trace() def simple_root(self, i, h): r""" @@ -601,8 +560,10 @@ def simple_root(self, i, h): class sp(ClassicalMatrixLieAlgebra): r""" - The Lie algebra `\mathfrak{sp}_{2k}` which consists of `2k \times 2k` - matrices `X` that satisfy the equation: + The matrix Lie algebra `\mathfrak{sp}_n`. + + The Lie algebra `\mathfrak{sp}_{2k}`, which consists of all + `2k \times 2k` matrices `X` that satisfy the equation: .. MATH:: @@ -618,31 +579,7 @@ class sp(ClassicalMatrixLieAlgebra): \end{pmatrix}. This is the Lie algebra of type `C_k`. - - INPUT: - - - ``R`` -- the base ring - - ``n`` -- the size of the matrix - - ``representation`` -- (default: ``'bracket'``) can be one of - the following: - - * ``'bracket'`` - use brackets and the Chevalley basis - * ``'matrix'`` - use matrices """ - @staticmethod - def __classcall_private__(cls, R, n, representation='bracket'): - """ - Choose the representation. - """ - if n % 2 != 0: - raise ValueError("n must be even") - - if representation == 'bracket': - return LieAlgebraChevalleyBasis(R, ['C', n//2]) - if representation == 'matrix': - return super(sp, cls).__classcall__(cls, R, n) - raise ValueError("invalid representation") - def __init__(self, R, n): """ Initialize ``self``. @@ -688,6 +625,10 @@ def killing_form(self, x, y): EXAMPLES:: sage: g = lie_algebras.sp(QQ, 8, representation='matrix') + sage: x = g.an_element() + sage: y = g.basis()['e1'] + sage: g.killing_form(x, y) + 36 """ return (2 * self._n + 2) * (x.value * y.value).trace() @@ -719,7 +660,7 @@ class LieAlgebraChevalleyBasis(LieAlgebraWithStructureCoefficients): r""" A simple finite dimensional Lie algebra in the Chevalley basis. - Let `L` be a simple complex Lie algebra with roots `\Phi`, then the + Let `L` be a simple (complex) Lie algebra with roots `\Phi`, then the Chevalley basis is given by `e_{\alpha}` for all `\alpha \in \Phi` and `h_{\alpha_i} := h_i` where `\alpha_i` is a simple root subject. These generators are subject to the relations: @@ -885,9 +826,18 @@ def _repr_(self): """ return "Lie algebra of {} in the Chevalley basis".format(self._cartan_type) - def _repr_generator(self, m): + def _repr_term(self, m): """ Return a string representation of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) + sage: K = L.basis().keys() + sage: L._repr_term(K[0]) + 'E[alpha[2]]' + sage: L._repr_term(K[-1]) + 'h2' """ if m in self._cartan_type.root_system().root_lattice().simple_coroots(): return "h{}".format(m.support()[0]) @@ -902,6 +852,24 @@ def _basis_cmp(self, x, y): OUTPUT: If ``x == y``, return 0. If ``x < y``, return -1. Else return 1. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['B', 3]) + sage: K = L.basis().keys() + sage: S = sorted(K, cmp=L._basis_cmp); S + [alpha[2], + alpha[1], + alpha[1] + alpha[2], + alpha[1] + 2*alpha[2], + alphacheck[1], + alphacheck[2], + -alpha[2], + -alpha[1], + -alpha[1] - alpha[2], + -alpha[1] - 2*alpha[2]] + sage: S == K + False """ if x == y: return 0 @@ -931,12 +899,24 @@ def _basis_cmp(self, x, y): def cartan_type(self): """ Return the Cartan type of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) + sage: L.cartan_type() + ['A', 2] """ return self._cartan_type def weyl_group(self): """ Return the Weyl group of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) + sage: L.weyl_group() + Weyl Group of type ['A', 2] (as a matrix group acting on the ambient space) """ from sage.combinat.root_system.weyl_group import WeylGroup return WeylGroup(self._cartan_type) @@ -1008,6 +988,12 @@ def gens(self): """ Return the generators of ``self`` in the order of `e_i`, `f_i`, and `h_i`. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) + sage: L.gens() + (E[alpha[1]], E[alpha[2]], E[-alpha[1]], E[-alpha[2]], h1, h2) """ index_set = self._cartan_type.index_set() RL = self._cartan_type.root_system().root_lattice() @@ -1024,17 +1010,22 @@ def gens(self): ret.append(B[alphacheck[i]]) return tuple(ret) - def gen(self, i): - """ - Return the ``i``-th generator of ``self``. - """ - return self.gens()[i] - def highest_root_basis_elt(self, pos=True): r""" Return the basis element corresponding to the highest root `\theta`. - If ``pos`` is ``True``, then returns `e_{\theta}`, otherwise it - returns `f_{\theta}`. + + INPUT: + + - ``pos`` -- (default: ``True``) if ``True``, then return + `e_{\theta}`, otherwise return `f_{\theta}` + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) + sage: L.highest_root_basis_elt() + E[alpha[1] + alpha[2]] + sage: L.highest_root_basis_elt(False) + E[-alpha[1] - alpha[2]] """ RL = self._cartan_type.root_system().root_lattice() theta = RL.highest_root() diff --git a/src/sage/algebras/lie_algebras/examples.py b/src/sage/algebras/lie_algebras/examples.py index 03d6c9eb985..c76105af631 100644 --- a/src/sage/algebras/lie_algebras/examples.py +++ b/src/sage/algebras/lie_algebras/examples.py @@ -20,7 +20,6 @@ # http://www.gnu.org/licenses/ #****************************************************************************** -from sage.algebras.lie_algebras.classical_lie_algebra import gl, sl, so, sp from sage.algebras.lie_algebras.virasoro import VirasoroAlgebra def three_dimensional(R, a, b, c, d, names=['X', 'Y', 'Z']): @@ -268,3 +267,211 @@ def strictly_upper_triangular_matrices(R, n): L.rename("Lie algebra of {}-dimensional strictly upper triangular matrices over {}".format(n, L.base_ring())) return L +##################################################################### +## Classical Lie algebras + +from sage.algebras.lie_algebras.classical_lie_algebra import gl +from sage.algebras.lie_algebras.classical_lie_algebra import ClassicalMatrixLieAlgebra as ClassicalMatrix + +def sl(R, n, representation='bracket'): + r""" + The Lie algebra `\mathfrak{sl}_n`. + + The Lie algebra `\mathfrak{sl}_n` is the type `A_{n-1}` Lie algebra + and is finite dimensional. As a matrix Lie algebra, it is given by + the set of all `n \times n` matrices with trace 0. + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the size of the matrix + - ``representation`` -- (default: ``'bracket'``) can be one of + the following: + + * ``'bracket'`` - use brackets and the Chevalley basis + * ``'matrix'`` - use matrices + + EXAMPLES: + + We first construct `\mathfrak{sl}_2` using the Chevalley basis:: + + sage: sl2 = lie_algebras.sl(QQ, 2); sl2 + Lie algebra of ['A', 1] in the Chevalley basis + sage: E,F,H = sl2.gens() + sage: E.bracket(F) == H + True + sage: H.bracket(E) == 2*E + True + sage: H.bracket(F) == -2*F + True + + We now construct `\mathfrak{sl}_2` as a matrix Lie algebra:: + + sage: sl2 = lie_algebras.sl(QQ, 2, representation='matrix') + sage: E,F,H = sl2.gens() + sage: E.bracket(F) == H + True + sage: H.bracket(E) == 2*E + True + sage: H.bracket(F) == -2*F + True + """ + if representation == 'bracket': + from sage.algebras.lie_algebras.classical_lie_algebra import LieAlgebraChevalleyBasis + return LieAlgebraChevalleyBasis(R, ['A', n-1]) + if representation == 'matrix': + from sage.algebras.lie_algebras.classical_lie_algebra import sl as sl_matrix + return sl_matrix(R, n) + raise ValueError("invalid representation") + +def so(R, n, representation='bracket'): + r""" + The Lie algebra `\mathfrak{so}_n`. + + The Lie algebra `\mathfrak{so}_n` is the type `B_k` Lie algebra + if `n = 2k - 1` or the type `D_k` Lie algebra if `n = 2k`, and in + either case is finite dimensional. As a matrix Lie algebra, it + is given by the set of all real anti-symmetric `n \times n` matrices. + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the size of the matrix + - ``representation`` -- (default: ``'bracket'``) can be one of + the following: + + * ``'bracket'`` - use brackets and the Chevalley basis + * ``'matrix'`` - use matrices + + EXAMPLES: + + We first construct `\mathfrak{so}_5` using the Chevalley basis:: + + sage: so5 = lie_algebras.so(QQ, 5); so5 + Lie algebra of ['B', 2] in the Chevalley basis + sage: E1,E2, F1,F2, H1,H2 = so5.gens() + sage: so5([E1, [E1, E2]]) + 0 + sage: X = so5([E2, [E2, E1]]); X + 2*E[alpha[1] + 2*alpha[2]] + sage: H1.bracket(X) + 0 + sage: H2.bracket(X) + 4*E[alpha[1] + 2*alpha[2]] + sage: so5([H1, [E1, E2]]) + -E[alpha[1] + alpha[2]] + sage: so5([H2, [E1, E2]]) + 0 + + We do the same construction of `\mathfrak{so}_4` using the Chevalley + basis:: + + sage: so4 = lie_algebras.so(QQ, 4); so4 + Lie algebra of ['D', 2] in the Chevalley basis + sage: E1,E2, F1,F2, H1,H2 = so4.gens() + sage: H1.bracket(E1) + 2*E[alpha[1]] + sage: H2.bracket(E1) == so4.zero() + True + sage: E1.bracket(E2) == so4.zero() + True + + We now construct `\mathfrak{so}_4` as a matrix Lie algebra:: + + sage: sl2 = lie_algebras.sl(QQ, 2, representation='matrix') + sage: E1,E2, F1,F2, H1,H2 = so4.gens() + sage: H2.bracket(E1) == so4.zero() + True + sage: E1.bracket(E2) == so4.zero() + True + """ + if representation == 'bracket': + from sage.algebras.lie_algebras.classical_lie_algebra import LieAlgebraChevalleyBasis + if n % 2 == 0: + return LieAlgebraChevalleyBasis(R, ['D', n//2]) + else: + return LieAlgebraChevalleyBasis(R, ['B', (n-1)//2]) + if representation == 'matrix': + from sage.algebras.lie_algebras.classical_lie_algebra import so as so_matrix + return so_matrix(R, n) + raise ValueError("invalid representation") + +def sp(R, n, representation='bracket'): + r""" + The Lie algebra `\mathfrak{sp}_n`. + + The Lie algebra `\mathfrak{sp}_n` where `n = 2k` is the type `C_k` + Lie algebra and is finite dimensional. As a matrix Lie algebra, it + is given by the set of all matrices `X` that satisfy the equation: + + .. MATH:: + + X^T M - M X = 0 + + where + + .. MATH:: + + M = \begin{pmatrix} + 0 & I_k \\ + -I_k & 0 + \end{pmatrix}. + + This is the Lie algebra of type `C_k`. + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the size of the matrix + - ``representation`` -- (default: ``'bracket'``) can be one of + the following: + + * ``'bracket'`` - use brackets and the Chevalley basis + * ``'matrix'`` - use matrices + + EXAMPLES: + + We first construct `\mathfrak{sp}_4` using the Chevalley basis:: + + sage: sp4 = lie_algebras.sp(QQ, 4); sp4 + Lie algebra of ['C', 2] in the Chevalley basis + sage: E1,E2, F1,F2, H1,H2 = sp4.gens() + sage: sp4([E2, [E2, E1]]) + 0 + sage: X = sp4([E1, [E1, E2]]); X + -2*E[2*alpha[1] + alpha[2]] + sage: H1.bracket(X) + -4*E[2*alpha[1] + alpha[2]] + sage: H2.bracket(X) + 0 + sage: sp4([H1, [E1, E2]]) + 0 + sage: sp4([H2, [E1, E2]]) + -E[alpha[1] + alpha[2]] + + We now construct `\mathfrak{sp}_4` as a matrix Lie algebra:: + + sage: sp4 = lie_algebras.sp(QQ, 4, representation='matrix'); sp4 + Symplectic Lie algebra of rank 4 over Rational Field + sage: E1,E2, F1,F2, H1,H2 = sp4.gens() + sage: H1.bracket(E1) + [ 0 2 0 0] + [ 0 0 0 0] + [ 0 0 0 0] + [ 0 0 -2 0] + sage: sp4([E1, [E1, E2]]) + [0 0 2 0] + [0 0 0 0] + [0 0 0 0] + [0 0 0 0] + """ + if n % 2 != 0: + raise ValueError("n must be even") + if representation == 'bracket': + from sage.algebras.lie_algebras.classical_lie_algebra import LieAlgebraChevalleyBasis + return LieAlgebraChevalleyBasis(R, ['C', n//2]) + if representation == 'matrix': + from sage.algebras.lie_algebras.classical_lie_algebra import sp as sp_matrix + return sp_matrix(R, n) + raise ValueError("invalid representation") + diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index f26a6387344..da9b2f24b12 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -608,6 +608,9 @@ def __call__(self, *args): t = args[0] else: t = args + from sage.combinat.root_system.cartan_matrix import CartanMatrix + if isinstance(t, CartanMatrix): + return t.cartan_type() if isinstance(t, CartanType_abstract): return t if hasattr(t, "cartan_type"): From 9fea76b93c304901090e2520e06fcadd5315b4ec Mon Sep 17 00:00:00 2001 From: Omnibase Date: Wed, 27 May 2015 04:31:20 +0000 Subject: [PATCH 013/151] Implemented Greg Martin's algorithm to compute dimensions of spaces of newforms --- .../modular/arithgroup/congroup_gamma0.py | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/sage/modular/arithgroup/congroup_gamma0.py b/src/sage/modular/arithgroup/congroup_gamma0.py index 6d046af9c8b..2f1595cf0e1 100644 --- a/src/sage/modular/arithgroup/congroup_gamma0.py +++ b/src/sage/modular/arithgroup/congroup_gamma0.py @@ -556,5 +556,101 @@ def index(self): """ return prod([p**e + p**(e-1) for (p,e) in self.level().factor()]) + def dimension_new_cusp_forms(self, k=2, p=0): + r""" + Return the dimension of the space of new (or `p`-new) + weight `k` cusp forms for this congruence subgroup. + + INPUT: + + - ``k`` - an integer (default: 2), the weight. Not fully implemented for k = 1. + - ``p`` - integer (default: 0); if nonzero, compute the `p`-new subspace. + OUTPUT: Integer + EXAMPLES:: + sage: Gamma0(11000).dimension_new_cusp_forms() + 240 + sage: Gamma0(11000).dimension_new_cusp_forms(k=1) + 0 + sage: Gamma0(22).dimension_new_cusp_forms(k=4) + 3 + sage: Gamma0(389).dimension_new_cusp_forms(k=2,p=17) + 32 + """ + N = self.level() + k = ZZ(k) + if p==0 or N % p != 0: + if k < 2 or k % 2 == 1: + return 0 + + from sage.rings.arith import moebius + from sage.functions.other import floor + from sage.rings.integer import Integer + + factors = list(N.factor()) + + def s0(p, a): + if a == 1: + return 1 - 1/p + elif a == 2: + return 1 - 1/p - 1/p**2 + else: + return (1 - 1/p) * (1 - 1/p**2) + + def vinf(p, a): + if a % 2 == 1: + return 0 + elif a == 2: + return p - 2 + else: + return p**(a/2 - 2) * (p - 1)**2 + + def v2(p, a): + if p % 4 == 1: + if a == 2: + return -1 + else: + return 0 + elif p % 4 == 3: + if a == 1: + return -2 + elif a == 2: + return 1 + else: + return 0 + elif a in (1, 2): + return -1 + elif a == 3: + return 1 + else: + return 0 + + def v3(p, a): + if p % 3 == 1: + if a == 2: + return -1 + else: + return 0 + elif p % 3 == 2: + if a == 1: + return -2 + elif a == 2: + return 1 + else: + return 0 + elif a in (1, 2): + return -1 + elif a == 3: + return 1 + else: + return 0 + + return (k-1)/12 * N * prod([s0(p, a) for p, a in factors]) \ + - prod([vinf(p, a) for p, a in factors]) / Integer(2) \ + + ((1-k)/4 + floor(k/4)) * prod([v2(p, a) for p, a in factors]) \ + + ((1-k)/3 + floor(k/3)) * prod([v3(p, a) for p, a in factors]) \ + + (1 if k/2 == 1 else 0) * moebius(N) + else: + return self.dimension_cusp_forms(k) - \ + 2*self.restrict(N//p).dimension_new_cusp_forms(k) \ No newline at end of file From 9b47f7f294ca59b44fefccccec2c8afc256fc600 Mon Sep 17 00:00:00 2001 From: Omnibase Date: Wed, 27 May 2015 07:49:46 +0000 Subject: [PATCH 014/151] Added reference to algorithm; changed variable names; removed unnecessary import --- .../modular/arithgroup/congroup_gamma0.py | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/sage/modular/arithgroup/congroup_gamma0.py b/src/sage/modular/arithgroup/congroup_gamma0.py index 2f1595cf0e1..32a2006fa8a 100644 --- a/src/sage/modular/arithgroup/congroup_gamma0.py +++ b/src/sage/modular/arithgroup/congroup_gamma0.py @@ -561,6 +561,9 @@ def dimension_new_cusp_forms(self, k=2, p=0): Return the dimension of the space of new (or `p`-new) weight `k` cusp forms for this congruence subgroup. + ALGORITHM: + http://www.math.ubc.ca/~gerg/papers/downloads/DSCFN.pdf + INPUT: - ``k`` - an integer (default: 2), the weight. Not fully implemented for k = 1. @@ -580,39 +583,38 @@ def dimension_new_cusp_forms(self, k=2, p=0): """ N = self.level() k = ZZ(k) - if p==0 or N % p != 0: + if p == 0 or N % p != 0: if k < 2 or k % 2 == 1: return 0 from sage.rings.arith import moebius from sage.functions.other import floor - from sage.rings.integer import Integer factors = list(N.factor()) - def s0(p, a): + def s0(q, a): if a == 1: - return 1 - 1/p + return 1 - 1/q elif a == 2: - return 1 - 1/p - 1/p**2 + return 1 - 1/q - 1/q**2 else: - return (1 - 1/p) * (1 - 1/p**2) + return (1 - 1/q) * (1 - 1/q**2) - def vinf(p, a): + def vinf(q, a): if a % 2 == 1: return 0 elif a == 2: - return p - 2 + return q - 2 else: - return p**(a/2 - 2) * (p - 1)**2 + return q**(a/2 - 2) * (q - 1)**2 - def v2(p, a): - if p % 4 == 1: + def v2(q, a): + if q % 4 == 1: if a == 2: return -1 else: return 0 - elif p % 4 == 3: + elif q % 4 == 3: if a == 1: return -2 elif a == 2: @@ -626,13 +628,13 @@ def v2(p, a): else: return 0 - def v3(p, a): - if p % 3 == 1: + def v3(q, a): + if q % 3 == 1: if a == 2: return -1 else: return 0 - elif p % 3 == 2: + elif q % 3 == 2: if a == 1: return -2 elif a == 2: @@ -646,11 +648,11 @@ def v3(p, a): else: return 0 - return (k-1)/12 * N * prod([s0(p, a) for p, a in factors]) \ - - prod([vinf(p, a) for p, a in factors]) / Integer(2) \ - + ((1-k)/4 + floor(k/4)) * prod([v2(p, a) for p, a in factors]) \ - + ((1-k)/3 + floor(k/3)) * prod([v3(p, a) for p, a in factors]) \ + return (k-1)/12 * N * prod([s0(q, a) for q, a in factors]) \ + - prod([vinf(q, a) for q, a in factors]) / ZZ(2) \ + + ((1-k)/4 + floor(k/4)) * prod([v2(q, a) for q, a in factors]) \ + + ((1-k)/3 + floor(k/3)) * prod([v3(q, a) for q, a in factors]) \ + (1 if k/2 == 1 else 0) * moebius(N) else: return self.dimension_cusp_forms(k) - \ - 2*self.restrict(N//p).dimension_new_cusp_forms(k) \ No newline at end of file + 2*self.restrict(N//p).dimension_new_cusp_forms(k) From 8997997821e95bddba080db1985b134e84cf466a Mon Sep 17 00:00:00 2001 From: Ben Salisbury Date: Thu, 2 Jul 2015 22:46:11 -0400 Subject: [PATCH 015/151] initial documentation updates in preparation of code changes --- src/sage/combinat/crystals/kleshchev.py | 7 +- src/sage/combinat/crystals/multisegments.py | 92 ++++++++++++++------- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/src/sage/combinat/crystals/kleshchev.py b/src/sage/combinat/crystals/kleshchev.py index 147ca8bfc7a..9bad8da8c7a 100644 --- a/src/sage/combinat/crystals/kleshchev.py +++ b/src/sage/combinat/crystals/kleshchev.py @@ -2,7 +2,7 @@ Kleshchev Partition (Tuple) Crystals """ #***************************************************************************** -# Copyright (C) 2013 Travis Scrimshaw +# Copyright (C) 2015 Travis Scrimshaw # # Distributed under the terms of the GNU General Public License (GPL) # @@ -140,11 +140,6 @@ class KleshchevCrystal(Parent, UniqueRepresentation): crystal graphs*. Transform. Groups **7** (2002). pp. 267-303. :arxiv:`0107052v1`, :doi:`10.1007/s00031-002-0014-1`. - .. [TingleyLN] Peter Tingley. Explicit `\widehat{\mathfrak{sl}}_n` crystal - maps between cylindric plane partitions, multi-partitions, and - multi-segments. Lecture notes. - http://webpages.math.luc.edu/~ptingley/lecturenotes/explicit_bijections.pdf - .. [Tingley2007] Peter Tingley. Three combinatorial models for `\widehat{\mathfrak{sl}}_n` crystals, with applications to cylindric plane partitions. International Mathematics Research Notices. (2007). diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index e86aa45ae0f..64446302ea8 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -3,7 +3,7 @@ """ #***************************************************************************** -# Copyright (C) 2013 Travis Scrimshaw +# Copyright (C) 2015 Travis Scrimshaw # # Distributed under the terms of the GNU General Public License (GPL) # @@ -31,24 +31,49 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): The type `A_n^{(1)}` crystal `B(\infty)` realized using Bernstein-Zelevinsky (BZ) multisegments. - Using the notation in [TingleyLN]_, a segment is a pair `(k; i]` where - `k \in \ZZ_{>0}` and `i \in \ZZ / n\ZZ`, and a multisegment is a multiset - of segments. We define a crystal structure on multisegments as follows: - - We create an `i`-sigature of the multisegment `\psi` by removing all - segments `(k; j]` where `j \not\equiv i, i-1`, and let `\psi^{\prime}` - denote the resulting multiset. Next we sort `\psi^{\prime}` to a sequence - `L` decreasing in `k` and `(k; i-1]` comes before `(k; i]`. Assign to - each `(k; i-1]` a `+` and `(k^{\prime}; i]` a `-`, and then cancel - all `+-` pairs until the resulting `\pm`-string is of the form - `- \cdots - + \cdots +`. The Kashiwara operators are then defined by: - - * `e_i` changes `(k; i]` to `(k-1; i-1]` corresponding to the leftmost - `-` or `0` (``None``) if no `-` exists, - * `f_i` changes `(k; i-1]` to `(k+1; i]` corresponding to - the rightmost `+` or adds a new segment of `(1; i]` if no `+` exists. - - We remove any segments of the form `(0; j]`. + Using (a modified version of the) notation from [JL2009]_, for `\ell \in + \ZZ_{>0}` and `i \in \ZZ /(n+1)\ZZ`, a segment of length `\ell` and head `i` + is the sequence of consecutive residues `[i,i+1,\dots,i+\ell-1]`. The + notation for a segment of length `\ell` and head `i` is simplified to + `[i;\ell)`. Similarly, a segment of length `\ell` and tail `i` is the + sequence of consecutive residues `[i-\ell+1,\dots,i-1,i]`. The latter is + denoted simply by `(\ell;i]`. Finally, a multisegment is a formal linear + combination of segments, usually written in the form + + .. MATH:: + + \psi = + \sum_{\substack{i \in \ZZ/(n+1)\ZZ \\ \ell \in \ZZ_{>0}}} + m_{(\ell;i]}(\ell;i]. + + Such a multisegment is called aperiodic if, for every `\ell>0`, there exists + some `i \in \ZZ/(n+1)\ZZ` such that `(\ell;i]` does not appear in `\psi`. + Denote the set of all periodic multisegments, together with the empty + multisegment `\varnothing`, by `\Psi`. We define a crystal + structure on multisegments as follows. Set `S_{\ell,i} = \sum_{k\ge \ell} + (m_{(k;i-1]} - m_{(k;i]})` and let `\ell_f` be the minimal `\ell` that + attains the value `\min_{\ell>0} S_{\ell,i}`. Then `f_i\psi = + \psi_{\ell_f,i}`, where + + .. MATH:: + + \psi_{\ell_f,i} = + \begin{cases} + \psi + (1;i] & \text{ if } \ell_f = 1,\\ + \psi + (\ell_f;i] - (\ell_f-1;i-1] & \text{ if } \ell_f > 1. + \end{cases} + + Similarly, let `\ell_e` be the maximal `\ell` that attains the value + `\min_{\ell>0} S_{\ell,i}`. Then `e_i\psi = \psi_{\ell_e,i}`, where + + .. MATH:: + + \psi_{\ell_e,i} = + \begin{cases} + 0 & \text{ if } \min_{\ell>0} S_{\ell,i} = 0, \\ + \psi + (1;i] & \text{ if } \ell_e = 1,\\ + \psi - (\ell_e;i] + (\ell_e-1;i-1] & \text{ if } \ell_e > 1. + \end{cases} INPUT: @@ -87,9 +112,13 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): REFERENCES: + .. [JL2009] Nicolas Jacon and Cedric Lecouvey. + *Kashiwara and Zelevinsky involutions in affine type A*. + Pac. J. Math. 243(2):287-311 (2009). + .. [LTV1999] Bernard Leclerc, Jean-Yves Thibon, and Eric Vasserot. - *Zelevinsky's involution at roots of unity*. J. Reine Angew. Math. - 513:33-51 (1999). + *Zelevinsky's involution at roots of unity*. + J. Reine Angew. Math. 513:33-51 (1999). """ def __init__(self, n): """ @@ -111,9 +140,9 @@ def _repr_(self): EXAMPLES:: sage: crystals.infinity.Multisegments(2) - The infinity crystal of BZ-multisegments of type ['A', 2, 1] + The infinity crystal of multisegments of type ['A', 2, 1] """ - return "The infinity crystal of BZ-multisegments of type {}".format(self._cartan_type) + return "The infinity crystal of multisegments of type {}".format(self._cartan_type) def module_generator(self): """ @@ -129,16 +158,15 @@ def module_generator(self): def weight_lattice_realization(self): """ - Return a realization of the weight lattice of ``self``. Since the - weights are all in `Q^-`, return the root lattice of type `A_n^(1)`. + Return a realization of the weight lattice of ``self``. EXAMPLES:: sage: B = crystals.infinity.Multisegments(2) sage: B.weight_lattice_realization() - Root lattice of the Root system of type ['A', 2, 1] + Extended weight lattice of the Root system of type ['A', 2, 1] """ - return self._cartan_type.root_system().root_lattice() + return self._cartan_type.root_system().weight_lattice(extended=True) class Element(ElementWrapper): """ @@ -333,10 +361,10 @@ def phi(self, i): r""" Return `\varphi_i` of ``self``. - Let `T \in \mathcal{B}(\infty)` Define `\varphi_i(T) := - \varepsilon_i(T) + \langle h_i, \mathrm{wt}(T) \rangle`, where `h_i` - is the `i`-th simple coroot and `\mathrm{wt}(T)` is the :meth:`weight` - of `T`. + Let `\psi \in \Psi`. Define `\varphi_i(\psi) := + \varepsilon_i(\psi) + \langle h_i, \mathrm{wt}(\psi) \rangle`, + where `h_i` is the `i`-th simple coroot and `\mathrm{wt}(\psi)` is the + :meth:`weight` of `\psi`. INPUT: @@ -366,7 +394,7 @@ def weight(self): sage: B = crystals.infinity.Multisegments(2) sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) sage: b.weight() - 4*alpha[0] + 4*alpha[1] + 4*alpha[2] + 4*delta """ WLR = self.parent().weight_lattice_realization() alpha = WLR.simple_roots() From c49aa39523c04c64e652c77ab53cfb84d6cb2716 Mon Sep 17 00:00:00 2001 From: Ben Salisbury Date: Sun, 5 Jul 2015 12:09:15 -0400 Subject: [PATCH 016/151] signature rule documentation added to multisegments --- src/sage/combinat/crystals/multisegments.py | 29 ++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index 64446302ea8..97bb9dc192b 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -3,7 +3,8 @@ """ #***************************************************************************** -# Copyright (C) 2015 Travis Scrimshaw +# Copyright (C) 2015 Ben Salisbury +# Travis Scrimshaw # # Distributed under the terms of the GNU General Public License (GPL) # @@ -75,6 +76,28 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): \psi - (\ell_e;i] + (\ell_e-1;i-1] & \text{ if } \ell_e > 1. \end{cases} + Alternatively, the crystal operators may be defined using a signature rule, + as detailed in Section 4 of [JL2009]_ (following [AJL2011]_). For + `\psi \in \Psi` and `i \in \ZZ/(n+1)\ZZ`, encode all segments in `\psi` with + tail `i` by the symbol `R` and all segments in `\psi` with tail `i-1` by + `A`. For `\ell> 0`, set + `w_{i,\ell} = R^{m_{(\ell,i]}} A^{m_{(\ell,i-1]}}` and + `w_i = \prod_{\ell\ge 1} w_{i,\ell}`. By successively canceling out as many + `RA` factors as possible, set + `\widetilde{w}_i = A^{a_i(\psi)}R^{r_i(\psi)}`. If `a_i(\psi)>0`, denote + by `\ell_f > 0` the length of the rightmost segment `A` in + `\widetilde{w}_i`. If `a_i(\psi) = 0`, set `\ell_f = 0`. Then + + .. MATH:: + + f_i\psi = + \begin{cases} + \psi + (1;i] & \text{ if } a_i(\psi) = 0,\\ + \psi + (\ell_f;i] - (\ell_f-1;i-1] & \text{ if } a_i(\psi) > 0. + \end{cases} + + The rule for computing `e_i\psi` is similar. + INPUT: - ``n`` -- for type `A_n^{(1)}` @@ -112,6 +135,10 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): REFERENCES: + .. [AJL2011] Susumu Ariki, Nicolas Jacon, and Cedric Lecouvey. + *The modular branching rule for affine Hecke algebras of type A* + Adv. Math. 228:481-526 (2011). + .. [JL2009] Nicolas Jacon and Cedric Lecouvey. *Kashiwara and Zelevinsky involutions in affine type A*. Pac. J. Math. 243(2):287-311 (2009). From b4be238a6ef706535583a70d8f8155fcf635c77c Mon Sep 17 00:00:00 2001 From: Ben Salisbury Date: Sun, 5 Jul 2015 12:11:29 -0400 Subject: [PATCH 017/151] Affinization --> AffinizationOf in catalog.py --- src/sage/combinat/crystals/catalog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/crystals/catalog.py b/src/sage/combinat/crystals/catalog.py index 4ec1422926e..d8919eed73b 100644 --- a/src/sage/combinat/crystals/catalog.py +++ b/src/sage/combinat/crystals/catalog.py @@ -67,7 +67,7 @@ from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinCrystal as KirillovReshetikhin from sage.combinat.rigged_configurations.rc_crystal import CrystalOfRiggedConfigurations as RiggedConfigurations from sage.combinat.crystals.kleshchev import KleshchevCrystal as KleshchevPartitions -from sage.combinat.crystals.affinization import AffinizationCrystal as Affinization +from sage.combinat.crystals.affinization import AffinizationOfCrystal as AffinizationOf from tensor_product import TensorProductOfCrystals as TensorProduct from direct_sum import DirectSumOfCrystals as DirectSum From 14fd6a47389e12dc70d0e2037bd2225667309873 Mon Sep 17 00:00:00 2001 From: Ben Salisbury Date: Sun, 5 Jul 2015 17:50:33 -0400 Subject: [PATCH 018/151] fixed merge error in combinat/module_list.rst --- src/doc/en/reference/combinat/module_list.rst | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index fcd8abb1514..d077f6e6bf2 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -62,18 +62,14 @@ Comprehensive Module list sage/combinat/crystals/generalized_young_walls sage/combinat/crystals/highest_weight_crystals sage/combinat/crystals/infinity_crystals -<<<<<<< HEAD - sage/combinat/crystals/infinity_path_model sage/combinat/crystals/kleshchev -======= - sage/combinat/crystals/polyhedral_realization ->>>>>>> develop sage/combinat/crystals/kirillov_reshetikhin sage/combinat/crystals/kyoto_path_model sage/combinat/crystals/letters sage/combinat/crystals/littelmann_path sage/combinat/crystals/monomial_crystals sage/combinat/crystals/multisegments + sage/combinat/crystals/polyhedral_realization sage/combinat/crystals/spins sage/combinat/crystals/tensor_product sage/combinat/cyclic_sieving_phenomenon From ea900a78227ec7013044f46d558d95199d2d64cb Mon Sep 17 00:00:00 2001 From: Ben Salisbury Date: Thu, 9 Jul 2015 19:43:03 -0400 Subject: [PATCH 019/151] doctests removed ... for now ... --- src/sage/combinat/crystals/multisegments.py | 160 ++++++++++---------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index 97bb9dc192b..3f2c2edc387 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -1,5 +1,5 @@ r""" -Crystal of Bernstien-Zelevinsky Multisegments +Crystal of Bernstein-Zelevinsky Multisegments """ #***************************************************************************** @@ -102,24 +102,24 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): - ``n`` -- for type `A_n^{(1)}` - EXAMPLES:: - - sage: B = crystals.infinity.Multisegments(2) - sage: x = B([(8,1),(6,0),(5,1),(5,0),(4,0),(4,1),(4,1),(3,0),(3,0),(3,1),(3,1),(1,0),(1,2),(1,2)]); x - {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], - (3; 0], (3; 0], (3; 1], (3; 1], (1; 0], (1; 2], (1; 2]} - sage: x.f(1) - {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], - (3; 0], (3; 0], (3; 1], (3; 1], (2; 1], (1; 2], (1; 2]} - sage: x.f(1).f(1) - {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], (3; 0], - (3; 0], (3; 1], (3; 1], (2; 1], (1; 1], (1; 2], (1; 2]} - sage: x.e(1) - {(7; 0], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], (3; 0], (3; 0], (3; 1], (3; 1], (1; 0], (1; 2], (1; 2]} - sage: x.e(1).e(1) - sage: x.f(0) - {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], - (3; 0], (3; 0], (3; 1], (3; 1], (2; 0], (1; 0], (1; 2]} +# EXAMPLES:: +# +# sage: B = crystals.infinity.Multisegments(2) +# sage: x = B([(8,1),(6,0),(5,1),(5,0),(4,0),(4,1),(4,1),(3,0),(3,0),(3,1),(3,1),(1,0),(1,2),(1,2)]); x +# {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], +# (3; 0], (3; 0], (3; 1], (3; 1], (1; 0], (1; 2], (1; 2]} +# sage: x.f(1) +# {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], +# (3; 0], (3; 0], (3; 1], (3; 1], (2; 1], (1; 2], (1; 2]} +# sage: x.f(1).f(1) +# {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], (3; 0], +# (3; 0], (3; 1], (3; 1], (2; 1], (1; 1], (1; 2], (1; 2]} +# sage: x.e(1) +# {(7; 0], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], (3; 0], (3; 0], (3; 1], (3; 1], (1; 0], (1; 2], (1; 2]} +# sage: x.e(1).e(1) +# sage: x.f(0) +# {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], +# (3; 0], (3; 0], (3; 1], (3; 1], (2; 0], (1; 0], (1; 2]} We check an `\widehat{\mathfrak{sl}}_2` example against the generalized Young walls:: @@ -136,7 +136,7 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): REFERENCES: .. [AJL2011] Susumu Ariki, Nicolas Jacon, and Cedric Lecouvey. - *The modular branching rule for affine Hecke algebras of type A* + *The modular branching rule for affine Hecke algebras of type A*. Adv. Math. 228:481-526 (2011). .. [JL2009] Nicolas Jacon and Cedric Lecouvey. @@ -222,11 +222,11 @@ def _repr_(self): r""" Return a string representation of ``self``. - EXAMPLES:: - - sage: B = crystals.infinity.Multisegments(2) - sage: B([(4,2), (3,0), (3,1), (1,1), (1,0)]) - {(4; 2], (3; 0], (3; 1], (1; 0], (1; 1]} +# EXAMPLES:: +# +# sage: B = crystals.infinity.Multisegments(2) +# sage: B([(4,2), (3,0), (3,1), (1,1), (1,0)]) +# {(4; 2], (3; 0], (3; 1], (1; 0], (1; 1]} """ seg = lambda x: "({}; {}]".format(x[0], x[1]) return "{" + ", ".join(seg(x) for x in self.value) + "}" @@ -235,11 +235,11 @@ def _latex_(self): r""" Return a LaTeX representation of ``self``. - EXAMPLES:: - - sage: B = crystals.infinity.Multisegments(2) - sage: latex(B([(4,2), (3,0), (3,1), (1,1), (1,0)])) - \bigl\{(4; 2], (3; 0], (3; 1], (1; 0], (1; 1]\bigr\} +# EXAMPLES:: +# +# sage: B = crystals.infinity.Multisegments(2) +# sage: latex(B([(4,2), (3,0), (3,1), (1,1), (1,0)])) +# \bigl\{(4; 2], (3; 0], (3; 1], (1; 0], (1; 1]\bigr\} """ seg = lambda x: "({}; {}]".format(x[0], x[1]) return "\\bigl\\{" + ", ".join(seg(x) for x in self.value) + "\\bigr\\}" @@ -259,14 +259,14 @@ def _sig(self, i): if there is no such block and ``ep`` is the number of unmatched `-`. - EXAMPLES:: - - sage: B = crystals.infinity.Multisegments(2) - sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) - sage: b._sig(0) - (1, None, 1) - sage: b._sig(1) - (None, None, 0) +# EXAMPLES:: +# +# sage: B = crystals.infinity.Multisegments(2) +# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) +# sage: b._sig(0) +# (1, None, 1) +# sage: b._sig(1) +# (None, None, 0) """ if not self.value: return (None, None) @@ -312,15 +312,15 @@ def e(self, i): - ``i`` -- an element of the index set - EXAMPLES:: - - sage: B = crystals.infinity.Multisegments(2) - sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) - sage: b.e(0) - {(4; 2], (3; 0], (3; 1], (1; 1]} - sage: b.e(1) - sage: b.e(2) - {(3; 0], (3; 1], (3; 1], (1; 0], (1; 1]} +# EXAMPLES:: +# +# sage: B = crystals.infinity.Multisegments(2) +# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) +# sage: b.e(0) +# {(4; 2], (3; 0], (3; 1], (1; 1]} +# sage: b.e(1) +# sage: b.e(2) +# {(3; 0], (3; 1], (3; 1], (1; 0], (1; 1]} """ i = IntegerModRing(self.parent()._cartan_type.rank())(i) m = self._sig(i)[0] @@ -342,16 +342,16 @@ def f(self, i): - ``i`` -- an element of the index set - EXAMPLES:: - - sage: B = crystals.infinity.Multisegments(2) - sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) - sage: b.f(0) - {(4; 2], (3; 0], (3; 1], (1; 0], (1; 0], (1; 1]} - sage: b.f(1) - {(4; 2], (3; 0], (3; 1], (1; 0], (1; 1], (1; 1]} - sage: b.f(2) - {(4; 2], (4; 2], (3; 0], (1; 0], (1; 1]} +# EXAMPLES:: +# +# sage: B = crystals.infinity.Multisegments(2) +# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) +# sage: b.f(0) +# {(4; 2], (3; 0], (3; 1], (1; 0], (1; 0], (1; 1]} +# sage: b.f(1) +# {(4; 2], (3; 0], (3; 1], (1; 0], (1; 1], (1; 1]} +# sage: b.f(2) +# {(4; 2], (4; 2], (3; 0], (1; 0], (1; 1]} """ i = IntegerModRing(self.parent()._cartan_type.rank())(i) p = self._sig(i)[1] @@ -370,16 +370,16 @@ def epsilon(self, i): - ``i`` -- an element of the index set - EXAMPLES:: - - sage: B = crystals.infinity.Multisegments(2) - sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) - sage: b.epsilon(0) - 1 - sage: b.epsilon(1) - 0 - sage: b.epsilon(2) - 1 +# EXAMPLES:: +# +# sage: B = crystals.infinity.Multisegments(2) +# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) +# sage: b.epsilon(0) +# 1 +# sage: b.epsilon(1) +# 0 +# sage: b.epsilon(2) +# 1 """ i = IntegerModRing(self.parent()._cartan_type.rank())(i) return self._sig(i)[2] @@ -399,15 +399,15 @@ def phi(self, i): EXAMPLES:: - sage: B = crystals.infinity.Multisegments(2) - sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) - sage: b.phi(0) - 1 - sage: b.phi(1) - 0 - sage: mg = B.module_generator() - sage: mg.f(1).phi(0) - -1 +# sage: B = crystals.infinity.Multisegments(2) +# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) +# sage: b.phi(0) +# 1 +# sage: b.phi(1) +# 0 +# sage: mg = B.module_generator() +# sage: mg.f(1).phi(0) +# -1 """ h = self.parent().weight_lattice_realization().simple_coroots() return self.epsilon(i) + self.weight().scalar(h[i]) @@ -418,10 +418,10 @@ def weight(self): EXAMPLES:: - sage: B = crystals.infinity.Multisegments(2) - sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) - sage: b.weight() - 4*delta +# sage: B = crystals.infinity.Multisegments(2) +# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) +# sage: b.weight() +# 4*delta """ WLR = self.parent().weight_lattice_realization() alpha = WLR.simple_roots() From ae76e724013cd995fe3be3aeb86ebeeb5531c555 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 4 May 2016 09:17:48 -0500 Subject: [PATCH 020/151] Fix from bad merge. --- src/sage/combinat/crystals/catalog.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/crystals/catalog.py b/src/sage/combinat/crystals/catalog.py index 80a4bbbbf1b..bdfe8671826 100644 --- a/src/sage/combinat/crystals/catalog.py +++ b/src/sage/combinat/crystals/catalog.py @@ -68,7 +68,6 @@ from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinCrystal as KirillovReshetikhin from sage.combinat.rigged_configurations.rc_crystal import CrystalOfRiggedConfigurations as RiggedConfigurations from sage.combinat.crystals.kleshchev import KleshchevCrystal as KleshchevPartitions -from sage.combinat.crystals.affinization import AffinizationCrystal as Affinization from sage.combinat.crystals.induced_structure import InducedCrystal as Induced From 921e2ca9ebeb5b617751ef42e699cb97b0ee03f1 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 2 Jun 2016 10:23:11 -0500 Subject: [PATCH 021/151] Adding back support for creating a Lie algebra from a Cartan type. --- src/sage/algebras/lie_algebras/lie_algebra.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index 163b2790ced..8d3e78ea2ff 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -273,6 +273,7 @@ def __classcall_private__(cls, R=None, arg0=None, arg1=None, names=None, ct = kwds.get("cartan_type", None) if ct is not None: + from sage.combinat.root_system.cartan_type import CartanType ct = CartanType(ct) if not ct.is_finite(): raise NotImplementedError("non-finite types are not implemented yet, see trac #14901 for details") From 721632bca52b77c3f628ff3b0d2919548579e81c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 2 Jun 2016 16:32:07 -0500 Subject: [PATCH 022/151] Fixing bugs in structure coefficients of Chevalley basis. --- .../lie_algebras/classical_lie_algebra.py | 169 +++++++++++++----- src/sage/algebras/lie_algebras/lie_algebra.py | 31 +++- 2 files changed, 154 insertions(+), 46 deletions(-) diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index 9080f61e51d..3eed3572dd4 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -257,24 +257,42 @@ def highest_root_basis_elt(self, pos=True): @cached_method def basis(self): """ - Return the basis of ``self``. + Return a basis of ``self``. EXAMPLES:: - sage: g = lie_algebras.sl(QQ, 3, representation='matrix') - sage: tuple(g.basis()) - ( - [0 0 0] [0 0 0] [ 0 0 0] [ 1 0 0] [0 1 0] [0 0 0] - [1 0 0] [0 0 0] [ 0 1 0] [ 0 -1 0] [0 0 0] [0 0 1] - [0 0 0], [0 1 0], [ 0 0 -1], [ 0 0 0], [0 0 0], [0 0 0] - ) + sage: M = LieAlgebra(ZZ, cartan_type=['A',2], representation='matrix') + sage: list(M.basis()) + [ + [ 1 0 0] [0 1 0] [0 0 1] [0 0 0] [ 0 0 0] [0 0 0] [0 0 0] + [ 0 0 0] [0 0 0] [0 0 0] [1 0 0] [ 0 1 0] [0 0 1] [0 0 0] + [ 0 0 -1], [0 0 0], [0 0 0], [0 0 0], [ 0 0 -1], [0 0 0], [1 0 0], + + [0 0 0] + [0 0 0] + [0 1 0] + ] """ - d = {} - for i in self.index_set(): - d['e{}'.format(i)] = self._e[i] - d['f{}'.format(i)] = self._f[i] - d['h{}'.format(i)] = self._h[i] - return Family(d) + # This is a fairly generic method of constructing a basis + from sage.matrix.constructor import matrix + + R = self.base_ring() + basis = list(self.lie_algebra_generators()) + expanded = True + while expanded: + expanded = False + mat = [] + for i,x in enumerate(basis): + mat.append(x.value.list()) + for y in basis[i+1:]: + mat.append(x.bracket(y).value.list()) + mat = matrix(R, mat) + mat.echelonize() + if mat.rank() != len(basis): + basis = [self.element_class( self, self._assoc(mat[i].list()) ) + for i in range(mat.rank())] + expanded = True + return Family(basis) # TODO: Uncomment once #16825 is done #def affine(self, kac_moody=False): @@ -419,7 +437,7 @@ def killing_form(self, x, y): sage: g = lie_algebras.sl(QQ, 5, representation='matrix') sage: x = g.an_element() - sage: y = g.basis()['e1'] + sage: y = g.lie_algebra_generators()['e1'] sage: g.killing_form(x, y) 10 """ @@ -513,14 +531,14 @@ def killing_form(self, x, y): sage: g = lie_algebras.so(QQ, 8, representation='matrix') sage: x = g.an_element() - sage: y = g.basis()['e1'] + sage: y = g.lie_algebra_generators()['e1'] sage: g.killing_form(x, y) - 32 + 12 sage: g = lie_algebras.so(QQ, 9, representation='matrix') sage: x = g.an_element() - sage: y = g.basis()['e1'] + sage: y = g.lie_algebra_generators()['e1'] sage: g.killing_form(x, y) - 36 + 14 """ return (self._n - 2) * (x.value * y.value).trace() @@ -626,7 +644,7 @@ def killing_form(self, x, y): sage: g = lie_algebras.sp(QQ, 8, representation='matrix') sage: x = g.an_element() - sage: y = g.basis()['e1'] + sage: y = g.lie_algebra_generators()['e1'] sage: g.killing_form(x, y) 36 """ @@ -684,7 +702,9 @@ class LieAlgebraChevalleyBasis(LieAlgebraWithStructureCoefficients): REFERNCES: - .. [CMT] A. M. Cohen, S. H. Murray, D. E. Talyor. *Groups of Lie type*. + .. [CMT03] \A. M. Cohen, S. H. Murray, D. E. Talyor. + *Computing in groups of Lie type*. + Mathematics of Computation. **73** (2003), no 247. pp. 1477--1498. http://www.win.tue.nl/~amc/pub/papers/cmt.pdf """ @staticmethod @@ -716,10 +736,12 @@ def __init__(self, R, cartan_type): RL = cartan_type.root_system().root_lattice() alpha = RL.simple_roots() p_roots = list(RL.positive_roots_by_height()) - n_roots = map(lambda x: -x, p_roots) + n_roots = [-x for x in p_roots] alphacheck = RL.simple_coroots() - roots = RL.roots() + roots = frozenset(RL.roots()) num_sroots = len(alpha) + p_root_index = {x: i for i,x in enumerate(p_roots)} + one = R.one() # Determine the signs for the structure coefficients from the root system # We first create the special roots @@ -733,23 +755,30 @@ def __init__(self, R, cartan_type): x, y = (a + b).extraspecial_pair() if (x, y) == (a, b): # If it already is an extra special pair - sp_sign[(x, y)] = 1 - sp_sign[(y, x)] = -1 + if (x, y) not in sp_sign: + # This swap is so the structure coefficients match with GAP + if (sum(x.coefficients()) == sum(y.coefficients()) + and str(x) > str(y)): + y,x = x,y + sp_sign[(x, y)] = -one + sp_sign[(y, x)] = one continue if b - x in roots: - t1 = (b-x).norm_squared() / b.norm_squared() * sp_sign[(x, b-x)] * sp_sign[(a, y-a)] + t1 = ((b-x).norm_squared() / b.norm_squared() + * sp_sign[(x, b-x)] * sp_sign[(a, y-a)]) else: t1 = 0 if a - x in roots: - t2 = (a-x).norm_squared() / a.norm_squared() * sp_sign[(x, a-x)] * sp_sign[(b, y-b)] + t2 = ((a-x).norm_squared() / a.norm_squared() + * sp_sign[(x, a-x)] * sp_sign[(b, y-b)]) else: t2 = 0 if t1 - t2 > 0: - sp_sign[(a,b)] = 1 - else: - sp_sign[(a,b)] = -1 + sp_sign[(a,b)] = -one + elif t2 - t1 > 0: + sp_sign[(a,b)] = one sp_sign[(b,a)] = -sp_sign[(a,b)] # Function to construct the structure coefficients (up to sign) @@ -771,39 +800,39 @@ def e_coeff(r, s): s_coeffs[(ac, -r)] = {-r: -c} # [e_r, f_r] - h_sum = {} - for j, c in r.associated_coroot(): - h_sum[alphacheck[j]] = c - s_coeffs[(r, -r)] = h_sum + s_coeffs[(r, -r)] = {alphacheck[j]: c + for j, c in r.associated_coroot()} # [e_r, e_s] and [e_r, f_s] with r != +/-s + # We assume s is positive, as otherwise we negate + # both r and s and the resulting coefficient for j, s in enumerate(p_roots[i+1:]): - j += i+1 - # Since h(s) > h(r), we will always have s - r > 0 (if it is a root) - # [e_r, f_s] + j += i+1 # Offset + # Since h(s) >= h(r), we have s - r > 0 when s - r is a root + # [f_r, e_s] if s - r in p_roots: c = e_coeff(r, -s) - a,b = s-r, r - if p_roots.index(a) + 1 > p_roots.index(b): # Note a != b - c = -c * sp_sign[(b, a)] + a, b = s-r, r + if p_root_index[a] > p_root_index[b]: # Note a != b + c *= -sp_sign[(b, a)] else: c *= sp_sign[(a, b)] s_coeffs[(-r, s)] = {a: -c} - s_coeffs[(r, -s)] = {a: c} + s_coeffs[(r, -s)] = {-a: c} # [e_r, e_s] a = r + s if a in p_roots: # (r, s) is a special pair - c = e_coeff(r, s) * sp_sign[(r, s)] + c = e_coeff(r, s) * sp_sign[(r, s)] s_coeffs[(r, s)] = {a: c} s_coeffs[(-r, -s)] = {-a: -c} # Lastly, make sure a < b for all (a, b) in the coefficients and flip if necessary - for k in s_coeffs.keys(): + for k in s_coeffs: a,b = k[0], k[1] if self._basis_cmp(a, b) > 0: - s_coeffs[(b,a)] = {k:-v for k,v in s_coeffs[k].items()} + s_coeffs[(b,a)] = {k: -v for k,v in s_coeffs[k].items()} del s_coeffs[k] names = ['e{}'.format(i) for i in range(1, num_sroots+1)] @@ -826,6 +855,56 @@ def _repr_(self): """ return "Lie algebra of {} in the Chevalley basis".format(self._cartan_type) + def _test_structure_coeffs(self, **options): + """ + Check the structure coefficients against the GAP implementation. + + EXAMPLES:: + + sage: L = LieAlgebra(ZZ, cartan_type=['G',2]) + sage: L._test_structure_coeffs() + """ + tester = self._tester(**options) + ct = self.cartan_type() + + # Setup the GAP objects + from sage.libs.gap.libgap import libgap + L = libgap.SimpleLieAlgebra(ct.letter, ct.n, libgap(self.base_ring())) + pos_B, neg_B, h_B = libgap.ChevalleyBasis(L) + gap_p_roots = libgap.PositiveRoots(libgap.RootSystem(L)).sage() + #E, F, H = libgap.CanonicalGenerators(L) + + # Setup the conversion between the Sage roots and GAP roots. + # The GAP roots are given in terms of the weight lattice. + p_roots = list(ct.root_system().root_lattice().positive_roots_by_height()) + WL = ct.root_system().weight_lattice() + La = WL.fundamental_weights() + convert = {WL(root): root for root in p_roots} + index = {convert[sum(c*La[j+1] for j,c in enumerate(rt))]: i + for i, rt in enumerate(gap_p_roots)} + + # Run the check + basis = self.basis() + roots = frozenset(p_roots) + for i,x in enumerate(p_roots): + for y in p_roots[i+1:]: + if x + y in roots: + c = basis[x].bracket(basis[y]).leading_coefficient() + a, b = (x + y).extraspecial_pair() + if (x, y) == (a, b): # If it already is an extra special pair + tester.assertEqual(pos_B[index[x]] * pos_B[index[y]], + c * pos_B[index[x+y]], + "extra special pair differ for [{}, {}]".format(x, y)) + else: + tester.assertEqual(pos_B[index[x]] * pos_B[index[y]], + c * pos_B[index[x+y]], + "incorrect structure coefficient for [{}, {}]".format(x, y)) + if x - y in roots: # This must be a negative root if it is a root + c = basis[x].bracket(basis[-y]).leading_coefficient() + tester.assertEqual(pos_B[index[x]] * neg_B[index[y]], + c * neg_B[index[x-y]], + "incorrect structure coefficient for [{}, {}]".format(x, y)) + def _repr_term(self, m): """ Return a string representation of the basis element indexed by ``m``. @@ -855,7 +934,7 @@ def _basis_cmp(self, x, y): EXAMPLES:: - sage: L = LieAlgebra(QQ, cartan_type=['B', 3]) + sage: L = LieAlgebra(QQ, cartan_type=['B', 2]) sage: K = L.basis().keys() sage: S = sorted(K, cmp=L._basis_cmp); S [alpha[2], diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index 8d3e78ea2ff..caf86a9be83 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -218,7 +218,36 @@ class LieAlgebra(Parent, UniqueRepresentation): # IndexedGenerators): and ``L[e + [h, f], h]`` will both raise errors. Instead you must use ``L[e + L[h, f], h]``. - Now we construct a free Lie algebra in a few different ways. There are + **4.** We can construct a Lie algebra from a Cartan type by using + the ``cartan_type`` option:: + + sage: L = LieAlgebra(ZZ, cartan_type=['C',3]) + sage: L.inject_variables() + Defining e1, e2, e3, f1, f2, f3, h1, h2, h3 + sage: e1.bracket(e2) + -E[alpha[1] + alpha[2]] + sage: L([[e1, e2], e2]) + 0 + sage: L([[e2, e3], e3]) + 0 + sage: L([e2, [e2, e3]]) + -2*E[2*alpha[2] + alpha[3]] + + sage: L = LieAlgebra(ZZ, cartan_type=['E',6]) + sage: L + Lie algebra of ['E', 6] in the Chevalley basis + + We also have matrix versions of the classical Lie algebras:: + + sage: L = LieAlgebra(ZZ, cartan_type=['A',2], representation='matrix') + sage: L.gens() + ( + [0 1 0] [0 0 0] [0 0 0] [0 0 0] [ 1 0 0] [ 0 0 0] + [0 0 0] [0 0 1] [1 0 0] [0 0 0] [ 0 -1 0] [ 0 1 0] + [0 0 0], [0 0 0], [0 0 0], [0 1 0], [ 0 0 0], [ 0 0 -1] + ) + + **5.** We construct a free Lie algebra in a few different ways. There are two primary representations, as brackets and as polynomials:: sage: L = LieAlgebra(QQ, 'x,y,z'); L # not tested #16823 From 23c1474d7c116fec63cbc643446e64ee2ad38e6f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 2 Jun 2016 17:41:02 -0500 Subject: [PATCH 023/151] Implementing matrix Lie algebras in types E6, F4, and G2. --- .../lie_algebras/classical_lie_algebra.py | 156 +++++++++++++++++- 1 file changed, 151 insertions(+), 5 deletions(-) diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index 3eed3572dd4..1b068207638 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -1,11 +1,21 @@ """ Classical Lie Algebras -These are the Lie algebras corresponding to types `A_n`, `B_n`, `C_n`, `D_n`. +These are the Lie algebras corresponding to types `A_n`, `B_n`, `C_n`, +and `D_n`. We also include support for the exceptional types +`E_{6,7,8}`, `F_4`, and `G_2` in the Chevalley basis, and we +give the matrix representation given in [HRT00]_. AUTHORS: - Travis Scrimshaw (2013-05-03): Initial version + +REFERENCES: + +.. [HRT00] \R.B. Howlett, L.J. Rylands, and D.E. Taylor. + *Matrix generators for exceptional groups of Lie type*. + J. Symbolic Computation. **11** (2000). + http://www.maths.usyd.edu.au/u/bobh/hrt.pdf """ #***************************************************************************** @@ -53,7 +63,7 @@ class ClassicalMatrixLieAlgebra(LieAlgebraFromAssociative): INPUT: - ``R`` -- the base ring - - ``ct`` -- the Cartan type of type `A_n`, `B_n`, `C_n`, or `D_n` + - ``ct`` -- the finite Cartan type EXAMPLES:: @@ -95,7 +105,16 @@ def __classcall_private__(cls, R, cartan_type): return sp(R, 2*cartan_type.rank()) if cartan_type.type() == 'D': return so(R, 2*cartan_type.rank()) - raise NotImplementedError("only implemented for types A, B, C, D") + if cartan_type.type() == 'E': + if cartan_type.rank() == 6: + return e6(R) + if cartan_type.rank() in [7,8]: + raise NotImplementedError("not yet implemented") + if cartan_type.type() == 'F' and cartan_type.rank() == 4: + return f4(R) + if cartan_type.type() == 'G' and cartan_type.rank() == 2: + return g2(R) + raise ValueError("invalid Cartan type") def __init__(self, R, ct, e, f, h): """ @@ -209,7 +228,9 @@ def epsilon(self, i, h): """ return h[i-1,i-1] - @abstract_method + # Do we want this to be optional or requried? + # There probably is a generic implementation we can do. + @abstract_method(optional=True) def simple_root(self, i, h): r""" Return the action of the simple root @@ -670,6 +691,131 @@ def simple_root(self, i, h): return 2*h[i,i] return h[i,i] - h[i+1,i+1] +class ExceptionalMatrixLieAlgebra(ClassicalMatrixLieAlgebra): + """ + A matrix Lie algebra of exceptional type. + """ + def __init__(self, R, cartan_type, e, f, h=None): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['E',6], representation='matrix') + sage: all(g.h(i) == g.e(i).bracket(g.f(i)) for i in range(1,7)) + True + """ + if h is None: + h = [e[i] * f[i] - f[i] * e[i] for i in range(len(e))] + ClassicalMatrixLieAlgebra.__init__(self, R, cartan_type, e, f, h) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: LieAlgebra(QQ, cartan_type=['G',2], representation='matrix') + Simple matrix Lie algebra of type ['G', 2] over Rational Field + """ + return "Simple matrix Lie algebra of type {} over {}".format(self.cartan_type(), self.base_ring()) + +class e6(ExceptionalMatrixLieAlgebra): + r""" + The matrix Lie algebra `\mathfrak{e}_6`. + + The simple Lie algebra `\mathfrak{e}_6` of type `E_6`. The matrix + representation is given following [HRT00]_. + """ + def __init__(self, R): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['E',6], representation='matrix') + sage: TestSuite(g).run() # long time + """ + MS = MatrixSpace(R, 27, sparse=True) + one = R.one() + coords = [[(0,1), (10,12), (13,15), (16,17), (18,19), (20,21)], + [(3,4), (5,6), (7,9), (18,20), (19,21), (22,23)], + [(1,2), (8,10), (11,13), (14,16), (19,22), (21,23)], + [(2,3), (6,8), (9,11), (16,18), (17,19), (23,24)], + [(3,5), (4,6), (11,14), (13,16), (15,17), (24,25)], + [(5,7), (6,9), (8,11), (10,13), (12,15), (25,26)]] + e = [MS({c: one for c in coord}) for coord in coords] + f = [MS({(c[1],c[0]): one for c in coord}) for coord in coords] + ExceptionalMatrixLieAlgebra.__init__(self, R, CartanType(['E', 6]), e, f) + +class f4(ExceptionalMatrixLieAlgebra): + r""" + The matrix Lie algebra `\mathfrak{f}_4`. + + The simple Lie algebra `\mathfrak{f}_f` of type `F_4`. The matrix + representation is given following [HRT00]_. + """ + def __init__(self, R): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['F',4], representation='matrix') + sage: TestSuite(g).run() # long time + """ + MS = MatrixSpace(R, 26, sparse=True) + one = R.one() + + coords = [[(0,1), (5,7), (6,9), (8,11), (10,12), (10,13), (12,14), + (15,16), (17,18), (19,20), (24,25)], + [(1,2), (3,5), (4,6), (8,10), (11,12), (11,13), (13,15), + (14,16), (18,21), (20,22), (23,24)], + [(2,3), (6,8), (9,11), (15,17), (16,18), (22,23)], + [(3,4), (5,6), (7,9), (17,19), (18,20), (21,22)]] + e = [MS({c: one for c in coord}) for coord in coords] + # Double (10, 12) in e1 and (11,13) in e2 + e[0][10,12] = 2*one + e[1][11,13] = 2*one + + coords = [[(1,0), (7,5), (9,6), (11,8), (12,10), (14,12), (14,13), + (16,15), (18,17), (20,19), (25,24)], + [(2,1), (5,3), (6,4), (10,8), (13,11), (15,12), (15,13), + (16,14), (21,18), (22,20), (24,23)], + [(3,2), (8,6), (11,9), (17,15), (18,16), (23,22)], + [(4,3), (6,5), (9,7), (19,17), (20,18), (22,21)]] + f = [MS({c: one for c in coord}) for coord in coords] + # Double (14, 12) in f1 and (15,13) in f2 + f[0][14,12] = 2*one + f[1][15,13] = 2*one + + ExceptionalMatrixLieAlgebra.__init__(self, R, CartanType(['F', 4]), e, f) + +class g2(ExceptionalMatrixLieAlgebra): + r""" + The matrix Lie algebra `\mathfrak{g}_2`. + + The simple Lie algebra `\mathfrak{g}_2` of type `G_2`. The matrix + representation is given following [HRT00]_. + """ + def __init__(self, R): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['G',2], representation='matrix') + sage: TestSuite(g).run() + """ + MS = MatrixSpace(R, 7, sparse=True) + one = R.one() + e = [MS({(0,1): one, (2,3): 2*one, (3,4): one, (5,6): one}), + MS({(1,2): one, (4,5): one})] + f = [MS({(1,0): one, (3,2): one, (4,3): 2*one, (6,5): one}), + MS({(2,1): one, (5,4): one})] + h = [MS({(0,0): one, (1,1): -one, (2,2): 2*one, (4,4): -2*one, (5,5): one, (6,6): -one}), + MS({(1,1): one, (2,2): -one, (4,4): one, (5,5): -one})] + ExceptionalMatrixLieAlgebra.__init__(self, R, CartanType(['G', 2]), e, f, h) ####################################### ## Chevalley Basis @@ -730,7 +876,7 @@ def __init__(self, R, cartan_type): TESTS:: sage: L = LieAlgebra(QQ, cartan_type=['A',2]) - sage: TestSuite(L).run() + sage: TestSuite(L).run() # long time """ self._cartan_type = cartan_type RL = cartan_type.root_system().root_lattice() From abcf1e542ac7a3cae36a42fb6adf72b277ae6839 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Fri, 1 Jul 2016 15:27:31 -0400 Subject: [PATCH 024/151] Fix citation problem --- src/sage/algebras/lie_algebras/classical_lie_algebra.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index 1b068207638..35cc01b2ed1 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -844,7 +844,7 @@ class LieAlgebraChevalleyBasis(LieAlgebraWithStructureCoefficients): `N_{\alpha, \beta}` is the maximum such that `\alpha - N_{\alpha, \beta} \beta \in \Phi`. - For computing the signs of the coefficients, see Section 3 of [CMT]_. + For computing the signs of the coefficients, see Section 3 of [CMT03]_. REFERNCES: @@ -970,7 +970,7 @@ def e_coeff(r, s): a = r + s if a in p_roots: # (r, s) is a special pair - c = e_coeff(r, s) * sp_sign[(r, s)] + c = e_coeff(r, s) * sp_sign[(r, s)] s_coeffs[(r, s)] = {a: c} s_coeffs[(-r, -s)] = {-a: -c} From f9d1c4afcac22ff14129b6c70f877ce8a4e9b299 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 18 Nov 2016 22:49:55 +0000 Subject: [PATCH 025/151] implemented Brouwer's construction Recognition from parameters not done yet --- .../graphs/generators/classical_geometries.py | 121 ++++++++++++++++++ src/sage/graphs/graph_generators.py | 2 + 2 files changed, 123 insertions(+) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index a7de697f5f1..dccdd4f3175 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -1325,3 +1325,124 @@ def CossidentePenttilaGraph(q): format='list_of_edges', multiedges=False) G.name('CossidentePenttila('+str(q)+')') return G + +def Nowhere0WordsTwoWeightCodeGraph(q, hyperoval=None, field=None, check_hyperoval=True): + r""" + Return the subgraph of nowhere 0 words from two-weight code of projective plane hyperoval + + Let `q=2^k` and `\Pi=PG(2,q)`. Fix a + `hyperoval `__ + `O \subset \Pi`. Let `V=F_q^3` and `C` the two-weight 3-dimensional linear code + over `F_q` with words `c(v)` obtained from `v\in V` by computing + + ..math:: + + c(v)=(,...,), o_j \in O. + + `C` contains `q(q-1)^2/2` words without 0 entries. The subgraph of the strongly + regular graph of `C` induced on the latter words is also strongly regular, + assuming `q>4`. This is a construction due to A.E.Brouwer [AB16]_, and leads + to graphs with parameters also given by a constrution in [HHL09]_. + + INPUT: + + - ``q`` -- a power of two + + - ``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 `PG(2,q)` over the field ``field``. + Each point of ``hyperoval`` must be a length 3 + vector over ``field`` with 1st non-0 coordinate equal to 1. By default, ``hyperoval`` and + ``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. + + - ``check_hyperoval`` -- (default: ``True``) if ``True``, + check ``hyperoval`` for correctness. + + + EXAMPLES: + + using the built-in construction:: + + sage: g=graphs.Nowhere0WordsTwoWeightCodeGraph(8); g + Nowhere0WordsTwoWeightCodeGraph(8): Graph on 196 vertices + sage: g.is_strongly_regular(parameters=True) + (196, 60, 14, 20) + sage: g=graphs.Nowhere0WordsTwoWeightCodeGraph(16) # not tested (long time) + sage: g.is_strongly_regular(parameters=True) # not tested (long time) + (1800, 728, 268, 312) + + supplying your own hyperoval:: + + sage: F=GF(8) + sage: O=[vector(F,(0,0,1)),vector(F,(0,1,0))]+[vector(F, (1,x^2,x)) for x in F] + sage: g=graphs.Nowhere0WordsTwoWeightCodeGraph(8,hyperoval=O,field=F); g + Nowhere0WordsTwoWeightCodeGraph(8): Graph on 196 vertices + sage: g.is_strongly_regular(parameters=True) + (196, 60, 14, 20) + + TESTS:: + + sage: F=GF(8) # repeating a point... + sage: O=[vector(F,(1,0,0)),vector(F,(0,1,0))]+[vector(F, (1,x^2,x)) for x in F] + sage: graphs.Nowhere0WordsTwoWeightCodeGraph(8,hyperoval=O,field=F) + Traceback (most recent call last): + ... + RuntimeError: incorrect hyperoval size + sage: O=[vector(F,(1,1,0)),vector(F,(0,1,0))]+[vector(F, (1,x^2,x)) for x in F] + sage: graphs.Nowhere0WordsTwoWeightCodeGraph(8,hyperoval=O,field=F) + Traceback (most recent call last): + ... + RuntimeError: incorrect hyperoval + + REFERENCES: + + .. [HHL09] \T. Huang, L. Huang, M.I. Lin + On a class of strongly regular designs and quasi-semisymmetric designs. + In: Recent Developments in Algebra and Related Areas, ALM vol. 8, pp. 129–153. + International Press, Somerville (2009) + + .. [AB16] \A.E. Brouwer + Personal communication, 2016 + + """ + from sage.combinat.designs.block_design import ProjectiveGeometryDesign as PG + from sage.modules.free_module_element import free_module_element as vector + from sage.matrix.constructor import matrix + + 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 k<3: + raise ValueError('q must be a at least 8') + if field is None: + F = FiniteField(q, 'a') + else: + F = field + + Theta = PG(2, 1, F, point_coordinates=1) + Pi = Theta.ground_set() + if hyperoval is None: + hyperoval = filter(lambda x: x[0]+x[1]*x[2]==0 or (x[0]==1 and x[1]==0 and x[2]==0), Pi) + O = set(hyperoval) + else: + map(lambda x: x.set_immutable(), hyperoval) + O = set(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") + M = matrix(hyperoval) + C = filter(lambda x: not F.zero() in x, map(lambda x: M*x, F**3)) + for x in C: + x.set_immutable() + G = Graph([C, lambda x,y: not F.zero() in x+y]) + G.name('Nowhere0WordsTwoWeightCodeGraph('+str(q)+')') + G.relabel() + return G diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 9816ca18b98..f8958b00bb2 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -257,6 +257,7 @@ def __append_to_doc(methods): "TaylorTwographDescendantSRG", "TaylorTwographSRG", "T2starGeneralizedQuadrangleGraph", + "Nowhere0WordsTwoWeightCodeGraph", "HaemersGraph", "CossidentePenttilaGraph", "UnitaryDualPolarGraph", @@ -2032,6 +2033,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None staticmethod(sage.graphs.generators.classical_geometries.TaylorTwographDescendantSRG) TaylorTwographSRG = staticmethod(sage.graphs.generators.classical_geometries.TaylorTwographSRG) T2starGeneralizedQuadrangleGraph = staticmethod(sage.graphs.generators.classical_geometries.T2starGeneralizedQuadrangleGraph) + Nowhere0WordsTwoWeightCodeGraph = staticmethod(sage.graphs.generators.classical_geometries.Nowhere0WordsTwoWeightCodeGraph) HaemersGraph = staticmethod(sage.graphs.generators.classical_geometries.HaemersGraph) CossidentePenttilaGraph = staticmethod(sage.graphs.generators.classical_geometries.CossidentePenttilaGraph) UnitaryDualPolarGraph = staticmethod(sage.graphs.generators.classical_geometries.UnitaryDualPolarGraph) From ed3876f811bc10b9bea210b1cf1105ad892cf141 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Sun, 20 Nov 2016 11:39:03 +0000 Subject: [PATCH 026/151] added recognition function --- .../graphs/generators/classical_geometries.py | 7 ++- src/sage/graphs/strongly_regular_db.pyx | 46 ++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index dccdd4f3175..c9fe4744eb3 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -1342,7 +1342,8 @@ def Nowhere0WordsTwoWeightCodeGraph(q, hyperoval=None, field=None, check_hyperov `C` contains `q(q-1)^2/2` words without 0 entries. The subgraph of the strongly regular graph of `C` induced on the latter words is also strongly regular, assuming `q>4`. This is a construction due to A.E.Brouwer [AB16]_, and leads - to graphs with parameters also given by a constrution in [HHL09]_. + to graphs with parameters also given by a construction in [HHL09]_. According + to [AB16]_, these two constructions are likely to produce isomorphic graphs. INPUT: @@ -1362,6 +1363,10 @@ def Nowhere0WordsTwoWeightCodeGraph(q, hyperoval=None, field=None, check_hyperov - ``check_hyperoval`` -- (default: ``True``) if ``True``, check ``hyperoval`` for correctness. + .. SEEALSO:: + + - :func:`~sage.graphs.strongly_regular_db.is_nowhere0_twoweight` + EXAMPLES: diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index b981fd0e674..8ae6d3704c5 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -1660,6 +1660,49 @@ def is_switch_OA_srg(int v, int k, int l, int mu): return (switch_OA_srg,c,n) +def is_nowhere0_twoweight(int v, int k, int l, int mu): + r""" + Test whether some graph of nowhere 0 words is `(v,k,\lambda,\mu)`-strongly regular. + + Test whether a :meth:`~sage.graphs.graph_generators.GraphGenerators.Nowhere0WordsTwoWeightCodeGraph` + is `(v,k,\lambda,\mu)`-strongly regular. + + 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: graphs.strongly_regular_graph(196, 60, 14, 20) + Nowhere0WordsTwoWeightCodeGraph(8): Graph on 196 vertices + + TESTS:: + + sage: from sage.graphs.strongly_regular_db import is_nowhere0_twoweight + sage: t = is_nowhere0_twoweight(1800, 728, 268, 312); t + (, 16) + sage: t = is_nowhere0_twoweight(5,5,5,5); t + + """ + from sage.graphs.generators.classical_geometries import Nowhere0WordsTwoWeightCodeGraph + cdef int q + r,s = eigenvalues(v,k,l,mu) + if r is None: + return + if r 4 and is_prime_power(q) and 0==r%2 and \ + v == r*(q-1)**2 and \ + 4*k == q*(q-2)*(q-3) and \ + 8*mu == q*(q-3)*(q-4): + return (Nowhere0WordsTwoWeightCodeGraph, q) + cdef eigenvalues(int v,int k,int l,int mu): r""" Return the eigenvalues of a (v,k,l,mu)-strongly regular graph. @@ -2891,6 +2934,7 @@ def strongly_regular_graph(int v,int k,int l,int mu=-1,bint existence=False,bint is_cossidente_penttila, is_mathon_PC_srg, is_muzychuk_S6, + is_nowhere0_twoweight, is_switch_skewhad] # Going through all test functions, for the set of parameters and its @@ -3178,7 +3222,7 @@ def _check_database(): sage: from sage.graphs.strongly_regular_db import _check_database sage: _check_database() # long time - Sage cannot build a (196 60 14 20 ) that exists. Comment ... + Sage cannot build a (216 40 4 8 ) that exists. Comment ... ... In Andries Brouwer's database: - 462 impossible entries From ce9ac0b084b0a1b2f4c00b3a8d23232da2a1acdb Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Tue, 7 Mar 2017 01:04:08 +0100 Subject: [PATCH 027/151] added the code, but the method is not found --- src/sage/matrix/matrix0.pyx | 7 +++-- src/sage/matrix/matrix2.pyx | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 22f4ea550d0..489a6cf9628 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -5226,10 +5226,14 @@ cdef class Matrix(sage.structure.element.Matrix): sage: 0^0 1 """ + from sage.symbolic.expression import Expression + if not self.is_square(): raise ArithmeticError("self must be a square matrix") if ignored is not None: raise RuntimeError("__pow__ third argument not used") + if isinstance(n, Expression): + return self._matrix_power_symbolic(self, n) return sage.structure.element.generic_power_c(self, n, None) ################################################### @@ -5302,9 +5306,6 @@ cdef class Matrix(sage.structure.element.Matrix): cdef int _strassen_default_echelon_cutoff(self) except -2: return -1 - - - ####################### # Unpickling ####################### diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 5bcd056272e..6217cb1fefe 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -14898,3 +14898,59 @@ def _jordan_form_vector_in_difference(V, W): if v not in W_space: return v return None + +def _matrix_power_symbolic(self, n): + + from sage.rings.qqbar import AlgebraicNumber + from sage.calculus.predefined import x + from sage.modules.free_module_element import vector, zero_vector + from sage.matrix.constructor import matrix + from sage.functions.other import factorial + from sage.symbolic.ring import SR + from sage.rings.qqbar import QQbar + + # power to a given symbolic expression + f = x**n + + # we change the matrix into a matrix on the field of algebraic numbers + # and compute Jordan matrix J and invertible matrix P s.t. A = P*J*~P + + # try catch using different QQbar + + [J, P] = self.change_ring(QQbar).jordan_form(transformation=True) + + fJ = matrix(SR, J.ncols()) + num_Jordan_blocks = 1+len(J.subdivisions()[0]) + fJ.subdivide(J.subdivisions()) + + for k in range(num_Jordan_blocks): + + # get Jordan block Jk + Jk = J.subdivision(k, k) + + # dimension of Jordan block Jk + mk = Jk.ncols() + + fJk = matrix(SR, mk, mk) + + # compute the first row of f(Jk) + # before applying a symbolic function to the coefficients of J, we change them into symbolic expressions + vk = [f.derivative(x, i)(Jk[i][i].radical_expression())/factorial(i) for i in range(mk)] + + # insert vk into each row (above the main diagonal) + for i in range(mk): + row_Jk_i = vector(SR, zero_vector(SR, i).list() + vk[0:mk-i]) + fJk.set_row(i, row_Jk_i) + + fJ.set_block(k, k, fJk) + + # we change the entries of P and P^-1 into symbolic expressions + Psym = P.apply_map(AlgebraicNumber.radical_expression) + Psyminv = (~P).apply_map(AlgebraicNumber.radical_expression) + fA = Psym*fJ*Psyminv + + return fA + +def matrix_power_symbolic(self, n): + + return self._matrix_power_symbolic(self, n) From 215cd88ee7d32aeec709d8b1a81f7b53f897742e Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Sat, 11 Mar 2017 14:29:16 +0100 Subject: [PATCH 028/151] Add symbolic matrix power. --- src/doc/en/reference/references/index.rst | 3 + src/sage/matrix/matrix0.pyx | 11 +- src/sage/matrix/matrix2.pyx | 123 ++++++++++++++++------ src/sage/matrix/matrix_integer_dense.pyx | 9 +- 4 files changed, 112 insertions(+), 34 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 4c9c2fd1ab4..c5644608d27 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -789,6 +789,9 @@ REFERENCES: function fields and related topics," J. Symbolic Comput. 33 (2002), no. 4, 425--445. +.. [Hig2008] \N. J. Higham, "Functions of matrices: theory and computation", + Society for Industrial and Applied Mathematics (2008). + .. [HJ2004] Tom Hoeholdt and Joern Justesen, A Course In Error-Correcting Codes, EMS, 2004 diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 489a6cf9628..1c6fbccab57 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -5225,6 +5225,14 @@ cdef class Matrix(sage.structure.element.Matrix): [1] sage: 0^0 1 + + Non-integer (symbolic) exponents are also supported:: + + sage: k = var('k') + sage: A = matrix([[2, -1], [1, 0]]) + sage: A^(2*k+1) + [ 2*k + 2 -2*k - 1] + [ 2*k + 1 -2*k] """ from sage.symbolic.expression import Expression @@ -5233,7 +5241,8 @@ cdef class Matrix(sage.structure.element.Matrix): if ignored is not None: raise RuntimeError("__pow__ third argument not used") if isinstance(n, Expression): - return self._matrix_power_symbolic(self, n) + from sage.matrix.matrix2 import _matrix_power_symbolic + return _matrix_power_symbolic(self, n) return sage.structure.element.generic_power_c(self, n, None) ################################################### diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 6217cb1fefe..c2164c32d59 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -14899,31 +14899,88 @@ def _jordan_form_vector_in_difference(V, W): return v return None -def _matrix_power_symbolic(self, n): +def _matrix_power_symbolic(A, k): + r""" + Symbolic matrix power. + + This function implements $f(A) = A^k$ assuming that the base ring of A is + exact. It relies in the Jordan normal form of A, available for exact rings + as `jordan_form()`. See Sec. 1.2 of [Hig2008]_ for further details. + + INPUT: + + - ``A`` - a square matrix over an exact field. + + - ``k`` - the symbolic exponent. + + OUTPUT: + Matrix A^k (symbolic). + + EXAMPLES:: + + sage: A = matrix(QQ, [[2, -1], [1, 0]]) + sage: k = var('k') + sage: A^k + [ k + 1 -k] + [ k -k + 1] + + TESTS:: + + Testing exponentiation in the symbolic ring:: + + sage: k = var('k') + sage: A = matrix([[pi, e],[0, -2*I]]) + sage: A^k + [ pi^k -(-2*I)^k/(pi*e^(-1) + 2*I*e^(-1)) + pi^k/(pi*e^(-1) + 2*I*e^(-1))] + [ 0 (-2*I)^k] + + If the base ring is inexact, the Jordan normal form is not available:: + + sage: A = matrix(RDF, [[2, -1], [1, 0]]) + sage: A^k + Traceback (most recent call last): + ... + ValueError: Jordan normal form not implemented over inexact rings. + + Testing exponentiation in the integer ring:: + + sage: A = matrix(ZZ, [[1,-1],[-1,1]]) + sage: A^(2*k+1) + [ 1/2*2^(2*k + 1) + 1/2*0^(2*k + 1) -1/2*2^(2*k + 1) + 1/2*0^(2*k + 1)] + [-1/2*2^(2*k + 1) + 1/2*0^(2*k + 1) 1/2*2^(2*k + 1) + 1/2*0^(2*k + 1)] + """ from sage.rings.qqbar import AlgebraicNumber - from sage.calculus.predefined import x - from sage.modules.free_module_element import vector, zero_vector + from sage.calculus.var import var + from sage.modules.free_module_element import vector from sage.matrix.constructor import matrix from sage.functions.other import factorial from sage.symbolic.ring import SR from sage.rings.qqbar import QQbar - + + got_SR = True if A.base_ring() == SR else False + # power to a given symbolic expression - f = x**n - - # we change the matrix into a matrix on the field of algebraic numbers - # and compute Jordan matrix J and invertible matrix P s.t. A = P*J*~P + x = var('x') + f = x**k - # try catch using different QQbar + # transform to QQbar if possible + try: + A = A.change_ring(QQbar) + except TypeError: + pass - [J, P] = self.change_ring(QQbar).jordan_form(transformation=True) + # returns jordan matrix J and invertible matrix P such that A = P*J*~P + [J, P] = A.jordan_form(transformation=True) - fJ = matrix(SR, J.ncols()) - num_Jordan_blocks = 1+len(J.subdivisions()[0]) - fJ.subdivide(J.subdivisions()) + # the number of Jordan blocks + num_jordan_blocks = 1+len(J.subdivisions()[0]) + + # FJ stores the application of f to the Jordan blocks + FJ = matrix(SR, J.ncols()) + FJ.subdivide(J.subdivisions()) - for k in range(num_Jordan_blocks): + for k in range(num_jordan_blocks): # get Jordan block Jk Jk = J.subdivision(k, k) @@ -14931,26 +14988,30 @@ def _matrix_power_symbolic(self, n): # dimension of Jordan block Jk mk = Jk.ncols() - fJk = matrix(SR, mk, mk) - + # f applied to the Jordan block Jk + FJ_k = matrix(SR, mk, mk) + # compute the first row of f(Jk) - # before applying a symbolic function to the coefficients of J, we change them into symbolic expressions - vk = [f.derivative(x, i)(Jk[i][i].radical_expression())/factorial(i) for i in range(mk)] - + vk = [] + for i in range(mk): + if hasattr(Jk[i][i], 'radical_expression'): + Jk_ii = Jk[i][i].radical_expression() + else: + Jk_ii = Jk[i][i] + vk += [f.derivative(x, i)(x=Jk_ii)/factorial(i)] + # insert vk into each row (above the main diagonal) for i in range(mk): - row_Jk_i = vector(SR, zero_vector(SR, i).list() + vk[0:mk-i]) - fJk.set_row(i, row_Jk_i) + Jk_row_i = vector(SR, vector(SR, i).list() + vk[0:mk-i]) + FJ_k.set_row(i, Jk_row_i) - fJ.set_block(k, k, fJk) + FJ.set_block(k, k, FJ_k) # we change the entries of P and P^-1 into symbolic expressions - Psym = P.apply_map(AlgebraicNumber.radical_expression) - Psyminv = (~P).apply_map(AlgebraicNumber.radical_expression) - fA = Psym*fJ*Psyminv - - return fA - -def matrix_power_symbolic(self, n): - - return self._matrix_power_symbolic(self, n) + if not got_SR: + Pinv = (~P).apply_map(AlgebraicNumber.radical_expression) + P = P.apply_map(AlgebraicNumber.radical_expression) + else: + Pinv = ~P + + return P * FJ * Pinv \ No newline at end of file diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index ecb4b4749f3..c92b9ea1ce2 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -1020,7 +1020,7 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse sage: M^M Traceback (most recent call last): ... - NotImplementedError: non-integral exponents not supported + NotImplementedError: the given exponent is not supported """ cdef Matrix_integer_dense self = sself @@ -1040,7 +1040,12 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse try: n = Integer(n) except TypeError: - raise NotImplementedError("non-integral exponents not supported") + from sage.symbolic.expression import Expression + if isinstance(n, Expression): + from sage.matrix.matrix2 import _matrix_power_symbolic + return _matrix_power_symbolic(self, n) + else: + raise NotImplementedError("the given exponent is not supported") if mpz_sgn((n).value) < 0: return (~self) ** (-n) From 3d69f78e9165026d9e98b8b2815e39ca26997b2a Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Sat, 11 Mar 2017 20:57:24 +0100 Subject: [PATCH 029/151] amend call to top-level var --- src/sage/matrix/matrix2.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index c2164c32d59..2ec790f25b9 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -14951,7 +14951,6 @@ def _matrix_power_symbolic(A, k): [-1/2*2^(2*k + 1) + 1/2*0^(2*k + 1) 1/2*2^(2*k + 1) + 1/2*0^(2*k + 1)] """ from sage.rings.qqbar import AlgebraicNumber - from sage.calculus.var import var from sage.modules.free_module_element import vector from sage.matrix.constructor import matrix from sage.functions.other import factorial @@ -14961,7 +14960,7 @@ def _matrix_power_symbolic(A, k): got_SR = True if A.base_ring() == SR else False # power to a given symbolic expression - x = var('x') + x = SR.var('x') f = x**k # transform to QQbar if possible From f6de852ce1f5eb5817ff2336ec1558b2425c9356 Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Sun, 12 Mar 2017 16:20:54 +0100 Subject: [PATCH 030/151] get rid of var and get rid of taking the derivative * now it works also with A^x : the x inside the function was being confused by the second argument -- didn't understand why (x and k have the same str representation, but still are different objects, no?) * anyway, it's better to get rid completely of x and the derivative, by putting the formula directly (vk) * changed the second argument to n, because it gets confused with the k inside the loop * add a .simplify_full() in the definition of vk, since it allows to get rid of the 0^k in one example, and simplifies factorials as in factorial(n)/factorial(n-1) -> n --- src/sage/matrix/matrix2.pyx | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 2ec790f25b9..43ce3112998 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -14899,11 +14899,11 @@ def _jordan_form_vector_in_difference(V, W): return v return None -def _matrix_power_symbolic(A, k): +def _matrix_power_symbolic(A, n): r""" Symbolic matrix power. - This function implements $f(A) = A^k$ assuming that the base ring of A is + This function implements $f(A) = A^n$ assuming that the base ring of A is exact. It relies in the Jordan normal form of A, available for exact rings as `jordan_form()`. See Sec. 1.2 of [Hig2008]_ for further details. @@ -14911,34 +14911,34 @@ def _matrix_power_symbolic(A, k): - ``A`` - a square matrix over an exact field. - - ``k`` - the symbolic exponent. + - ``n`` - the symbolic exponent. OUTPUT: - Matrix A^k (symbolic). + Matrix A^n (symbolic). EXAMPLES:: sage: A = matrix(QQ, [[2, -1], [1, 0]]) - sage: k = var('k') - sage: A^k - [ k + 1 -k] - [ k -k + 1] + sage: n = var('n') + sage: A^n + [ n + 1 -n] + [ n -n + 1] TESTS:: Testing exponentiation in the symbolic ring:: - sage: k = var('k') + sage: n = var('n') sage: A = matrix([[pi, e],[0, -2*I]]) - sage: A^k - [ pi^k -(-2*I)^k/(pi*e^(-1) + 2*I*e^(-1)) + pi^k/(pi*e^(-1) + 2*I*e^(-1))] - [ 0 (-2*I)^k] + sage: A^n + [ pi^n -(-2*I)^n/(pi*e^(-1) + 2*I*e^(-1)) + pi^n/(pi*e^(-1) + 2*I*e^(-1))] + [ 0 (-2*I)^n] If the base ring is inexact, the Jordan normal form is not available:: sage: A = matrix(RDF, [[2, -1], [1, 0]]) - sage: A^k + sage: A^n Traceback (most recent call last): ... ValueError: Jordan normal form not implemented over inexact rings. @@ -14946,9 +14946,9 @@ def _matrix_power_symbolic(A, k): Testing exponentiation in the integer ring:: sage: A = matrix(ZZ, [[1,-1],[-1,1]]) - sage: A^(2*k+1) - [ 1/2*2^(2*k + 1) + 1/2*0^(2*k + 1) -1/2*2^(2*k + 1) + 1/2*0^(2*k + 1)] - [-1/2*2^(2*k + 1) + 1/2*0^(2*k + 1) 1/2*2^(2*k + 1) + 1/2*0^(2*k + 1)] + sage: A^(2*n+1) + [ 1/2*2^(2*n + 1) -1/2*2^(2*n + 1)] + [-1/2*2^(2*n + 1) 1/2*2^(2*n + 1)] """ from sage.rings.qqbar import AlgebraicNumber from sage.modules.free_module_element import vector @@ -14959,10 +14959,6 @@ def _matrix_power_symbolic(A, k): got_SR = True if A.base_ring() == SR else False - # power to a given symbolic expression - x = SR.var('x') - f = x**k - # transform to QQbar if possible try: A = A.change_ring(QQbar) @@ -14979,6 +14975,8 @@ def _matrix_power_symbolic(A, k): FJ = matrix(SR, J.ncols()) FJ.subdivide(J.subdivisions()) + factorial_n = factorial(n) + for k in range(num_jordan_blocks): # get Jordan block Jk @@ -14997,7 +14995,9 @@ def _matrix_power_symbolic(A, k): Jk_ii = Jk[i][i].radical_expression() else: Jk_ii = Jk[i][i] - vk += [f.derivative(x, i)(x=Jk_ii)/factorial(i)] + + # corresponds to \frac{D^i(f)}{i!}, with f = x^n and D the differential operator wrt x + vk += [(factorial_n/(factorial(n-i)*factorial(i))*Jk_ii**(n-i)).simplify_full()] # insert vk into each row (above the main diagonal) for i in range(mk): From f301b9e18da69fee529fac9819f1413af4c933f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 19 Mar 2017 21:41:59 +0100 Subject: [PATCH 031/151] tentative use of richcmp for morphisms of schemes --- src/sage/schemes/elliptic_curves/ell_point.py | 39 ++---------- src/sage/schemes/generic/morphism.py | 15 +++-- src/sage/schemes/generic/point.py | 5 +- src/sage/schemes/product_projective/point.py | 61 ++++--------------- .../schemes/projective/projective_point.py | 47 ++++---------- 5 files changed, 40 insertions(+), 127 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index d5968a7521c..c17fa67288b 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -134,6 +134,7 @@ from sage.libs.pari import pari from sage.libs.cypari2.pari_instance import prec_words_to_bits from sage.structure.sequence import Sequence +from sage.structure.sage_object import richcmp from sage.schemes.curves.projective_curve import Hasse_bounds from sage.schemes.projective.projective_point import (SchemeMorphism_point_projective_ring, @@ -150,32 +151,7 @@ class EllipticCurvePoint(SchemeMorphism_point_projective_ring): """ A point on an elliptic curve. """ - def __cmp__(self, other): - """ - Standard comparison function for points on elliptic curves, to - allow sorting and equality testing. - - .. NOTE:: - - ``__eq__`` and ``__ne__`` are implemented in - SchemeMorphism_point_projective_ring - - EXAMPLES: - sage: E=EllipticCurve(QQ,[1,1]) - sage: P=E(0,1) - sage: P.order() - +Infinity - sage: Q=P+P - sage: P==Q - False - sage: Q+Q == 4*P - True - """ - assert isinstance(other, (int, long, Integer)) and other == 0 - if self.is_zero(): - return 0 - else: - return -1 + pass class EllipticCurvePoint_field(SchemeMorphism_point_abelian_variety_field): @@ -394,15 +370,10 @@ def __tuple__(self): """ return tuple(self._coords) # Warning: _coords is a list! - def __cmp__(self, other): + def _richcmp_(self, other, op): """ Comparison function for points to allow sorting and equality testing. - .. NOTE:: - - ``__eq__`` and ``__ne__`` are implemented in - SchemeMorphism_point_projective_field - EXAMPLES:: sage: E = EllipticCurve('45a') @@ -416,8 +387,8 @@ def __cmp__(self, other): try: other = self.codomain().ambient_space()(other) except TypeError: - return -1 - return cmp(self._coords, other._coords) + return NotImplemented + return richcmp(self._coords, other._coords, op) def _pari_(self): r""" diff --git a/src/sage/schemes/generic/morphism.py b/src/sage/schemes/generic/morphism.py index 21f29a2c91f..fe9391e5469 100644 --- a/src/sage/schemes/generic/morphism.py +++ b/src/sage/schemes/generic/morphism.py @@ -83,6 +83,7 @@ import operator from sage.structure.element import (AdditiveGroupElement, RingElement, Element, generic_power, parent, get_coercion_model) +from sage.structure.sage_object import richcmp from sage.structure.sequence import Sequence from sage.categories.homset import Homset, Hom, End from sage.categories.number_fields import NumberFields @@ -1762,7 +1763,7 @@ def __len__(self): """ return len(self._coords) - def _cmp_(self, other): + def _richcmp_(self, other, op): """ Compare two scheme morphisms. @@ -1773,15 +1774,15 @@ def _cmp_(self, other): OUTPUT: - ``+1``, ``0``, or ``-1``. + boolean EXAMPLES:: sage: A = AffineSpace(2, QQ) sage: a = A(1,2) sage: b = A(3,4) - sage: a.__cmp__(b) - -1 + sage: a < b + True sage: a != b True """ @@ -1789,10 +1790,8 @@ def _cmp_(self, other): try: other = self._codomain.ambient_space()(other) except TypeError: - return -1 - return cmp(self._coords, other._coords) - - __cmp__ = _cmp_ + return NotImplemented + return richcmp(self._coords, other._coords, op) def scheme(self): """ diff --git a/src/sage/schemes/generic/point.py b/src/sage/schemes/generic/point.py index 2a96c3bcf57..29bcb769056 100644 --- a/src/sage/schemes/generic/point.py +++ b/src/sage/schemes/generic/point.py @@ -9,6 +9,7 @@ #******************************************************************************* from sage.structure.element import Element +from sage.structure.sage_object import richcmp ######################################################## # Base class for points on a scheme, either topological @@ -210,7 +211,7 @@ def prime_ideal(self): """ return self.__P - def __cmp__(self, other): + def _richcmp_(self, other, op): """ Compare ``self`` to ``other``. @@ -222,7 +223,7 @@ def __cmp__(self, other): sage: x == y False """ - return cmp(self.__P, other.__P) + return richcmp(self.__P, other.__P, op) ######################################################## # Points on a scheme defined by a morphism diff --git a/src/sage/schemes/product_projective/point.py b/src/sage/schemes/product_projective/point.py index 89d8013a80c..8921e7a638e 100644 --- a/src/sage/schemes/product_projective/point.py +++ b/src/sage/schemes/product_projective/point.py @@ -27,6 +27,7 @@ from sage.schemes.generic.morphism import SchemeMorphism from sage.schemes.generic.morphism import SchemeMorphism_point from sage.structure.sequence import Sequence +from sage.structure.sage_object import richcmp class ProductProjectiveSpaces_point_ring(SchemeMorphism_point): @@ -139,18 +140,17 @@ def _repr_(self): """ return('(%s)'%(" , ".join((" : ".join([repr(f) for f in Q])) for Q in self._points))) - def __eq__(self, other): + def _richcmp_(self, right, op): r""" - Tests the projective equality of two points. + Compare two points in products of projective spaces. INPUT: - - ``right`` - a point on a product of projective spaces. + - ``other`` -- another point OUTPUT: - - Boolean - ``True`` if the two points define the same point. - ``False`` otherwise. + boolean EXAMPLES:: @@ -159,24 +159,6 @@ def __eq__(self, other): sage: Q = T([2, 4, 6, 4, 5, 6]) sage: P == Q True - """ - for i in range(self.codomain().ambient_space().num_components()): - if self[i] != other[i]: - return False - return True - - def __ne__(self, other): - r""" - Tests the projective inequality of two points. - - INPUT: - - - ``right`` -- a point on a product of projective spaces. - - OUTPUT: - - - Boolean - ``False`` if the two points define the same point. - ``True`` otherwise. EXAMPLES:: @@ -185,53 +167,36 @@ def __ne__(self, other): sage: Q = T([2, 4, 6, 4, 1, 0]) sage: P != Q True - """ - for i in range(self.codomain().ambient_space().num_components()): - if self[i] != other[i]: - return True - return False - - def __cmp__(self, right): - r""" - Compare two points in products of projective spaces. - - INPUT: - - - ``other`` -- anything. To compare against this point. - - OUTPUT: - - ``+1``, ``0``, or ``-1``. EXAMPLES:: sage: T = ProductProjectiveSpaces([1, 1, 1], GF(5), 'x') sage: P = T([3, 2, 3, 4, 1, 0]) sage: Q = T([1, 2, 3, 4, 3, 1]) - sage: P.__cmp__(Q) - 1 + sage: P > Q + True :: sage: T = ProductProjectiveSpaces([1, 1, 1], GF(5), 'x') sage: P = T([1, 2, 3, 4, 1, 0]) sage: Q = T([1, 2, 3, 4, 3, 0]) - sage: P.__cmp__(Q) - 0 + sage: P == Q + True :: sage: T = ProductProjectiveSpaces([1, 1, 1], GF(5), 'x') sage: P = T([1, 2, 3, 4, 1, 0]) sage: Q = T([1, 2, 3, 4, 3, 1]) - sage: P.__cmp__(Q) - -1 + sage: P < Q + True """ #needed for Digraph if not isinstance(right, (ProductProjectiveSpaces_point_ring)): - return -1 + return NotImplemented else: - return(cmp(self._points, right._points)) + return richcmp(self._points, right._points, op) def __copy__(self): r""" diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 4240edaae6d..858514fcd61 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -56,8 +56,7 @@ SchemeMorphism_point) from sage.structure.element import AdditiveGroupElement from sage.structure.sequence import Sequence - - +from sage.structure.sage_object import rich_to_bool, richcmp, op_EQ, op_NE #******************************************************************* # Projective varieties @@ -167,7 +166,7 @@ def __init__(self, X, v, check=True): self._coords = tuple(v) - def __eq__(self, right): + def _richcmp_(self, right, op): """ Tests the projective equality of two points. @@ -177,7 +176,7 @@ def __eq__(self, right): OUTPUT: - - Boolean - True if ``self`` and ``right`` define the same point. False otherwise. + - Boolean Examples:: @@ -266,31 +265,8 @@ def __eq__(self, right): sage: Q1 = f(P1) sage: Q1 == P1 False - """ - if not isinstance(right, SchemeMorphism_point): - try: - right = self.codomain()(right) - except TypeError: - return False - if self.codomain() != right.codomain(): - return False - n = len(self._coords) - return all([self[i]*right[j] == self[j]*right[i] - for i in range(0,n) for j in range(i+1, n)]) - def __ne__(self,right): - """ - Tests the projective equality of two points. - - INPUT: - - - ``right`` - a point on projective space. - - OUTPUT: - - - Boolean - True if ``self`` and ``right`` define different points. False otherwise. - - Examples:: + For inequality:: sage: PS = ProjectiveSpace(ZZ, 1, 'x') sage: P = PS([1, 2]) @@ -350,15 +326,16 @@ def __ne__(self,right): try: right = self.codomain()(right) except TypeError: - return True + return NotImplemented if self.codomain() != right.codomain(): - return True + return op == op_NE + n = len(self._coords) - for i in range(0,n): - for j in range(i+1,n): - if self._coords[i]*right._coords[j] != self._coords[j]*right._coords[i]: - return True - return False + if op in [op_EQ, op_NE]: + b = all(self[i] * right[j] == self[j] * right[i] + for i in range(n) for j in range(i + 1, n)) + return b == (op == op_EQ) + return richcmp(self._coords, right._coords, op) def __hash__(self): """ From 0d0b1492867859af17df1418c2331be287025bf8 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 20 Mar 2017 17:33:15 +0100 Subject: [PATCH 032/151] Make interact library compatible with Jupyter --- src/sage/all.py | 3 ++- src/sage/interacts/decorator.py | 12 +++++---- src/sage/interacts/library.py | 40 ++++++++++++++++++++--------- src/sage/interacts/test_jupyter.rst | 22 ++++++++++++++++ 4 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 src/sage/interacts/test_jupyter.rst diff --git a/src/sage/all.py b/src/sage/all.py index f56920b4162..06f73bc65e5 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -182,8 +182,9 @@ lazy_import('sagenb.notebook.notebook_object', 'inotebook') lazy_import('sagenb.notebook.sage_email', 'email') lazy_import('sage.interacts', 'all', 'interacts') -lazy_import('sage.interacts.decorator', 'interact') from sage.interacts.debugger import debug +# interact decorator from SageNB (will be overridden by Jupyter) +lazy_import('sagenb.notebook.interact', 'interact') from copy import copy, deepcopy diff --git a/src/sage/interacts/decorator.py b/src/sage/interacts/decorator.py index dbea48df9a9..8533166972c 100644 --- a/src/sage/interacts/decorator.py +++ b/src/sage/interacts/decorator.py @@ -13,6 +13,10 @@ #***************************************************************************** +from sage.misc.superseded import deprecation +deprecation(22636, "the module sage.interacts.decorator is deprecated") + + import inspect import textwrap from sage.misc.html import html @@ -39,11 +43,9 @@ def interact(func, **kwds): ....: def f(x=[1,2,3]): ....: print(x) ... + sage: from sage.interacts.decorator import interact + doctest:...: DeprecationWarning: the module sage.interacts.decorator is deprecated + See http://trac.sagemath.org/22636 for details. """ from sagenb.notebook.interact import interact as sagenb_interact return sagenb_interact(func, **kwds) - - - - - diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index ab222f20989..93fd839f94a 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -32,36 +32,52 @@ # http://www.gnu.org/licenses/ #***************************************************************************** - from sage.all import * -from sagenb.notebook.interact import * - x = SR.var('x') +# It is important that this file is lazily imported for this to work +from sage.repl.user_globals import get_global + +# Get a bunch of functions from the user globals. In SageNB, this will +# refer to SageNB functions; in Jupyter, this will refer to Jupyter +# functions. In the command-line and for doctests, we import the +# SageNB functions as fall-back. +for name in ("interact", "checkbox", "input_box", "input_grid", + "range_slider", "selector", "slider", "text_control"): + try: + obj = get_global(name) + except NameError: + import sagenb.notebook.interact + obj = sagenb.notebook.interact.__dict__[name] + globals()[name] = obj + + def library_interact(f): """ This is a decorator for using interacts in the Sage library. + This is just the ``interact`` function wrapped in an additional + function call: ``library_interact(f)()`` is equivalent to + executing ``interact(f)``. + EXAMPLES:: sage: import sage.interacts.library as library sage: @library.library_interact - ....: def f(n=5): print(n) - ....: - sage: f() # an interact appears, if using the notebook, else code + ....: def f(n=5): + ....: print(n) + sage: f() # an interact appears if using the notebook, else code ... """ @sage_wraps(f) def library_wrapper(): - # Maybe program around bug (?) in the notebook: - html("") - # This prints out the relevant html code to make - # the interact appear: - interact(f) + # This will display the interact, no need to return anything + interact(f) return library_wrapper + @library_interact -def demo(n=tuple(range(10)), m=tuple(range(10))): +def demo(n=slider(range(10)), m=slider(range(10))): """ This is a demo interact that sums two numbers. diff --git a/src/sage/interacts/test_jupyter.rst b/src/sage/interacts/test_jupyter.rst new file mode 100644 index 00000000000..15d0da023ea --- /dev/null +++ b/src/sage/interacts/test_jupyter.rst @@ -0,0 +1,22 @@ +Test the Sage interact library in Jupyter + +We need to setup a proper test environment for widgets:: + + sage: from ipywidgets.widgets.tests import setup_test_comm + sage: setup_test_comm() + +Check that the interact library is available within Jupyter:: + + sage: from sage.repl.ipython_kernel.all_jupyter import * + sage: interacts.geometry.special_points() + Interactive function with 10 widgets + title: HTML(value=u'

Special points in triangle

', description=u'title') + a0: IntSlider(value=30, min=0, max=360, step=1, description=u'A') + a1: IntSlider(value=180, min=0, max=360, step=1, description=u'B') + a2: IntSlider(value=300, min=0, max=360, step=1, description=u'C') + show_median: Checkbox(value=False, description=u'Medians') + show_pb: Checkbox(value=False, description=u'Perpendicular Bisectors') + show_alt: Checkbox(value=False, description=u'Altitudes') + show_ab: Checkbox(value=False, description=u'Angle Bisectors') + show_incircle: Checkbox(value=False, description=u'Incircle') + show_euler: Checkbox(value=False, description=u"Euler's Line") From d6747ddae146c52e8674ed173124ca1ec00e2c09 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 21 Mar 2017 12:56:00 +0100 Subject: [PATCH 033/151] sage_wraps: add f attribute to return wrapped function --- src/sage/misc/decorators.py | 46 +++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/sage/misc/decorators.py b/src/sage/misc/decorators.py index 9c7da375870..83b80db2cae 100644 --- a/src/sage/misc/decorators.py +++ b/src/sage/misc/decorators.py @@ -56,14 +56,14 @@ def sage_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): decorated function:: sage: def square(f): - ....: @sage_wraps(f) - ....: def new_f(x): - ....: return f(x)*f(x) - ....: return new_f + ....: @sage_wraps(f) + ....: def new_f(x): + ....: return f(x)*f(x) + ....: return new_f sage: @square - ... def g(x): - ....: "My little function" - ....: return x + ....: def g(x): + ....: "My little function" + ....: return x sage: g(2) 4 sage: g(x) @@ -79,13 +79,13 @@ def sage_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): unchanged) (see :trac:`9976`):: sage: def diff_arg_dec(f): - ....: @sage_wraps(f) - ....: def new_f(y, some_def_arg=2): - ....: return f(y+some_def_arg) - ....: return new_f + ....: @sage_wraps(f) + ....: def new_f(y, some_def_arg=2): + ....: return f(y+some_def_arg) + ....: return new_f sage: @diff_arg_dec - ... def g(x): - ....: return x + ....: def g(x): + ....: return x sage: g(1) 3 sage: g(1, some_def_arg=4) @@ -109,6 +109,17 @@ def sage_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): ... ' return self.basis.reduced()\n'], ...) + The ``f`` attribute of the decorated function refers to the + original function:: + + sage: foo = object() + sage: @sage_wraps(foo) + ....: def func(): + ....: pass + sage: wrapped = sage_wraps(foo)(func) + sage: wrapped.f is foo + True + Demonstrate that sage_wraps works for non-function callables (:trac:`9919`):: @@ -131,10 +142,10 @@ def sage_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): The bug described in :trac:`11734` is fixed:: sage: def square(f): - ....: @sage_wraps(f) - ....: def new_f(x): - ....: return f(x)*f(x) - ....: return new_f + ....: @sage_wraps(f) + ....: def new_f(x): + ....: return f(x)*f(x) + ....: return new_f sage: f = lambda x:x^2 sage: g = square(f) sage: g(3) # this line used to fail for some people if these command were manually entered on the sage prompt @@ -148,6 +159,7 @@ def sage_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): def f(wrapper): update_wrapper(wrapper, wrapped, assigned=assigned, updated=updated) + wrapper.f = wrapped wrapper._sage_src_ = lambda: sage_getsource(wrapped) wrapper._sage_src_lines_ = lambda: sage_getsourcelines(wrapped) #Getting the signature right in documentation by Sphinx (Trac 9976) From 133ae6a2a5ba29c992d8af14905a7052474ca028 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 21 Mar 2017 15:33:10 +0100 Subject: [PATCH 034/151] Patch python3 so that it at least succeeds building (if not functioning on all cylinders) The patches with names like X.Y.Z-*.patch come originally from the patch set I started putting together at https://github.com/embray/hashstack/tree/cygwin/python/pkgs/python/3.5 --- .../python3/patches/2.6.5-FD_SETSIZE.patch | 41 +++++++ ...e_14438.patch => 2.6.5-ncurses-abi6.patch} | 6 +- build/pkgs/python3/patches/2.7.3-dylib.patch | 10 ++ .../3.2.6-ctypes-util-find_library.patch | 34 ++++++ .../3.2.6-export-PySignal_SetWakeupFd.patch | 11 ++ .../patches/3.2.6-getpath-exe-extension.patch | 31 ++++++ .../patches/3.2.6-no-enable-new-dtags.patch | 20 ++++ .../python3/patches/3.2.6-no-native-tls.patch | 101 ++++++++++++++++++ build/pkgs/python3/patches/3.3.6-dbm.patch | 29 +++++ .../python3/patches/3.3.6-tkinter-x11.patch | 28 +++++ .../python3/patches/3.4.5-issue13756.patch | 44 ++++++++ build/pkgs/python3/patches/3.4.5-struct.patch | 13 +++ .../pkgs/python3/patches/3.5.2-no-libm.patch | 11 ++ .../3.5.2-struct_siginfo_si_band.patch | 66 ++++++++++++ .../pkgs/python3/patches/cygwin-ctypes.patch | 12 +++ .../python3/patches/cygwin-readline.patch | 28 +++++ 16 files changed, 481 insertions(+), 4 deletions(-) create mode 100644 build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch rename build/pkgs/python3/patches/{ncurses-issue_14438.patch => 2.6.5-ncurses-abi6.patch} (63%) create mode 100644 build/pkgs/python3/patches/2.7.3-dylib.patch create mode 100644 build/pkgs/python3/patches/3.2.6-ctypes-util-find_library.patch create mode 100644 build/pkgs/python3/patches/3.2.6-export-PySignal_SetWakeupFd.patch create mode 100644 build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch create mode 100644 build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch create mode 100644 build/pkgs/python3/patches/3.2.6-no-native-tls.patch create mode 100644 build/pkgs/python3/patches/3.3.6-dbm.patch create mode 100644 build/pkgs/python3/patches/3.3.6-tkinter-x11.patch create mode 100644 build/pkgs/python3/patches/3.4.5-issue13756.patch create mode 100644 build/pkgs/python3/patches/3.4.5-struct.patch create mode 100644 build/pkgs/python3/patches/3.5.2-no-libm.patch create mode 100644 build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch create mode 100644 build/pkgs/python3/patches/cygwin-ctypes.patch create mode 100644 build/pkgs/python3/patches/cygwin-readline.patch diff --git a/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch b/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch new file mode 100644 index 00000000000..8675734eee9 --- /dev/null +++ b/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch @@ -0,0 +1,41 @@ +--- a/Modules/selectmodule.c 2012-02-02 22:35:21.835125000 -0500 ++++ b/Modules/selectmodule.c 2012-02-02 22:41:41.210125000 -0500 +@@ -6,6 +6,21 @@ + >= 0. + */ + ++/* Windows #defines FD_SETSIZE to 64 if FD_SETSIZE isn't already defined. ++ 64 is too small (too many people have bumped into that limit). ++ Here we boost it. ++ ++ Cygwin also defines FD_SETSIZE to 64, so also increase the limit on ++ Cygwin. We must do this before sys/types.h is included, which otherwise ++ sets FD_SETSIZE to the default. ++ ++ Users who want even more than the boosted limit should #define ++ FD_SETSIZE higher before this; e.g., via compiler /D switch. ++*/ ++#if (defined(MS_WINDOWS) || defined(__CYGWIN__)) && !defined(FD_SETSIZE) ++#define FD_SETSIZE 512 ++#endif ++ + #include "Python.h" + #include + +@@ -16,16 +31,6 @@ + #undef HAVE_BROKEN_POLL + #endif + +-/* Windows #defines FD_SETSIZE to 64 if FD_SETSIZE isn't already defined. +- 64 is too small (too many people have bumped into that limit). +- Here we boost it. +- Users who want even more than the boosted limit should #define +- FD_SETSIZE higher before this; e.g., via compiler /D switch. +-*/ +-#if defined(MS_WINDOWS) && !defined(FD_SETSIZE) +-#define FD_SETSIZE 512 +-#endif +- + #if defined(HAVE_POLL_H) + #include + #elif defined(HAVE_SYS_POLL_H) diff --git a/build/pkgs/python3/patches/ncurses-issue_14438.patch b/build/pkgs/python3/patches/2.6.5-ncurses-abi6.patch similarity index 63% rename from build/pkgs/python3/patches/ncurses-issue_14438.patch rename to build/pkgs/python3/patches/2.6.5-ncurses-abi6.patch index 3509156c263..6587121a346 100644 --- a/build/pkgs/python3/patches/ncurses-issue_14438.patch +++ b/build/pkgs/python3/patches/2.6.5-ncurses-abi6.patch @@ -1,6 +1,5 @@ -diff -ur src.orig/Include/py_curses.h src/Include/py_curses.h ---- src.orig/Include/py_curses.h 2012-08-01 10:51:27.075830866 +0200 -+++ src/Include/py_curses.h 2012-08-01 11:01:05.311809409 +0200 +--- a/Include/py_curses.h 2009-09-06 16:23:05.000000000 -0500 ++++ b/Include/py_curses.h 2010-04-14 15:21:23.008971400 -0500 @@ -17,6 +17,13 @@ #define NCURSES_OPAQUE 0 #endif /* __APPLE__ */ @@ -15,4 +14,3 @@ diff -ur src.orig/Include/py_curses.h src/Include/py_curses.h #ifdef __FreeBSD__ /* ** On FreeBSD, [n]curses.h and stdlib.h/wchar.h use different guards -Seulement dans src/Include: py_curses.h.orig diff --git a/build/pkgs/python3/patches/2.7.3-dylib.patch b/build/pkgs/python3/patches/2.7.3-dylib.patch new file mode 100644 index 00000000000..27b030ecd9f --- /dev/null +++ b/build/pkgs/python3/patches/2.7.3-dylib.patch @@ -0,0 +1,10 @@ +--- a/Lib/distutils/unixccompiler.py 2012-11-27 07:44:15.409993500 -0500 ++++ b/Lib/distutils/unixccompiler.py 2012-11-27 08:09:57.801770900 -0500 +@@ -141,6 +141,7 @@ + static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" + if sys.platform == "cygwin": + exe_extension = ".exe" ++ dylib_lib_extension = ".dll.a" + + def preprocess(self, source, + output_file=None, macros=None, include_dirs=None, diff --git a/build/pkgs/python3/patches/3.2.6-ctypes-util-find_library.patch b/build/pkgs/python3/patches/3.2.6-ctypes-util-find_library.patch new file mode 100644 index 00000000000..ffe15a218ae --- /dev/null +++ b/build/pkgs/python3/patches/3.2.6-ctypes-util-find_library.patch @@ -0,0 +1,34 @@ +--- a/Lib/ctypes/util.py ++++ b/Lib/ctypes/util.py +@@ -82,6 +82,20 @@ if os.name == "posix" and sys.platform == "darwin": + continue + return None + ++elif sys.platform == "cygwin": ++ def find_library(name): ++ for libdir in ['/usr/lib', '/usr/local/lib']: ++ for libext in ['lib%s.dll.a' % name, 'lib%s.a' % name]: ++ implib = os.path.join(libdir, libext) ++ if not os.path.exists(implib): ++ continue ++ cmd = "dlltool -I " + implib + " 2>/dev/null" ++ res = os.popen(cmd).read().replace("\n","") ++ if not res: ++ continue ++ return res ++ return None ++ + elif os.name == "posix": + # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump + import re, tempfile, errno +@@ -224,6 +238,10 @@ def test(): + print(cdll.LoadLibrary("libcrypto.dylib")) + print(cdll.LoadLibrary("libSystem.dylib")) + print(cdll.LoadLibrary("System.framework/System")) ++ elif sys.platform == "cygwin": ++ print cdll.LoadLibrary("cygbz2-1.dll") ++ print find_library("crypt") ++ print cdll.LoadLibrary("cygcrypt-0.dll") + else: + print(cdll.LoadLibrary("libm.so")) + print(cdll.LoadLibrary("libcrypt.so")) diff --git a/build/pkgs/python3/patches/3.2.6-export-PySignal_SetWakeupFd.patch b/build/pkgs/python3/patches/3.2.6-export-PySignal_SetWakeupFd.patch new file mode 100644 index 00000000000..f98f2440a94 --- /dev/null +++ b/build/pkgs/python3/patches/3.2.6-export-PySignal_SetWakeupFd.patch @@ -0,0 +1,11 @@ +--- a/Include/pyerrors.h ++++ b/Include/pyerrors.h +@@ -244,7 +244,7 @@ PyAPI_FUNC(void) PyErr_SetInterrupt(void); + + /* In signalmodule.c */ + #ifndef Py_LIMITED_API +-int PySignal_SetWakeupFd(int fd); ++PyAPI_FUNC(int) PySignal_SetWakeupFd(int fd); + #endif + + /* Support for adding program text to SyntaxErrors */ diff --git a/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch b/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch new file mode 100644 index 00000000000..3ededf469a3 --- /dev/null +++ b/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch @@ -0,0 +1,31 @@ +--- a/Modules/getpath.c ++++ b/Modules/getpath.c +@@ -491,6 +491,28 @@ calculate_path(void) + if (isxfile(progpath)) + break; + ++#ifdef __CYGWIN__ ++ /* ++ * Cygwin automatically removes the ".exe" extension from argv[0] ++ * to make programs feel like they are in a more Unix-like ++ * environment. Unfortunately, this can make it problemmatic for ++ * Cygwin to distinguish between a directory and an executable with ++ * the same name excluding the ".exe" extension. For example, the ++ * Cygwin Python build directory has a "Python" directory and a ++ * "python.exe" executable. This causes isxfile() to erroneously ++ * return false. If isdir() returns true and there is enough space ++ * to append the ".exe" extension, then we try again with the ++ * extension appended. ++ */ ++#define EXE ".exe" ++ if (isdir(progpath) && strlen(progpath) + strlen(EXE) <= MAXPATHLEN) ++ { ++ strcat(progpath, EXE); ++ if (isxfile(progpath)) ++ break; ++ } ++#endif /* __CYGWIN__ */ ++ + if (!delim) { + progpath[0] = L'\0'; + break; diff --git a/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch b/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch new file mode 100644 index 00000000000..864714b3d2a --- /dev/null +++ b/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch @@ -0,0 +1,20 @@ +diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py +index 69045f8..7ed0a5f 100644 +--- a/Lib/distutils/unixccompiler.py ++++ b/Lib/distutils/unixccompiler.py +@@ -239,9 +239,13 @@ class UnixCCompiler(CCompiler): + # -Wl whenever gcc was used in the past it is probably + # safest to keep doing so. + if sysconfig.get_config_var("GNULD") == "yes": +- # GNU ld needs an extra option to get a RUNPATH ++ # GNU ELF ld needs an extra option to get a RUNPATH + # instead of just an RPATH. +- return "-Wl,--enable-new-dtags,-R" + dir ++ if sys.platform in ["win32", "cygwin"] or \ ++ "mingw" in compiler: ++ return [] ++ else: ++ return "-Wl,--enable-new-dtags,-R" + dir + else: + return "-Wl,-R" + dir + else: diff --git a/build/pkgs/python3/patches/3.2.6-no-native-tls.patch b/build/pkgs/python3/patches/3.2.6-no-native-tls.patch new file mode 100644 index 00000000000..775909ded73 --- /dev/null +++ b/build/pkgs/python3/patches/3.2.6-no-native-tls.patch @@ -0,0 +1,101 @@ +diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h +index de42f1a..a4eab78 100644 +--- a/Python/thread_pthread.h ++++ b/Python/thread_pthread.h +@@ -601,6 +601,12 @@ _pythread_pthread_set_stacksize(size_t size) + + #define THREAD_SET_STACKSIZE(x) _pythread_pthread_set_stacksize(x) + ++ ++/* Issue #25658: POSIX doesn't require that pthread_key_t is integer. ++ If key type isn't integer, TLS functions are implemented by CPython self. ++*/ ++#ifdef PTHREAD_KEY_T_IS_INTEGER ++ + #define Py_HAVE_NATIVE_TLS + + int +@@ -643,3 +649,5 @@ PyThread_get_key_value(int key) + void + PyThread_ReInitTLS(void) + {} ++ ++#endif /* PTHREAD_KEY_T_IS_INTEGER */ +diff --git a/configure b/configure +index b107bf2..59f80cb 100755 +--- a/configure ++++ b/configure +@@ -7564,6 +7564,35 @@ _ACEOF + + + fi ++ ++# Issue #25658: POSIX doesn't require that pthread_key_t is integer. ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthread_key_t is integer" >&5 ++$as_echo_n "checking whether pthread_key_t is integer... " >&6; } ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++#include ++int ++main () ++{ ++pthread_key_t k; k * 1; ++ ; ++ return 0; ++} ++_ACEOF ++if ac_fn_c_try_compile "$LINENO"; then : ++ ac_pthread_key_t_is_integer=yes ++else ++ ac_pthread_key_t_is_integer=no ++ ++fi ++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pthread_key_t_is_integer" >&5 ++$as_echo "$ac_pthread_key_t_is_integer" >&6; } ++if test "$ac_pthread_key_t_is_integer" = yes ; then ++ ++$as_echo "#define PTHREAD_KEY_T_IS_INTEGER 1" >>confdefs.h ++ ++fi + CC="$ac_save_cc" + + +diff --git a/configure.ac b/configure.ac +index 0ab4430..89e422a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1610,6 +1610,19 @@ if test "$have_pthread_t" = yes ; then + #endif + ]) + fi ++ ++# Issue #25658: POSIX doesn't require that pthread_key_t is integer. ++AC_MSG_CHECKING(whether pthread_key_t is integer) ++AC_COMPILE_IFELSE( ++ [AC_LANG_PROGRAM([[#include ]], [[pthread_key_t k; k * 1;]])], ++ [ac_pthread_key_t_is_integer=yes], ++ [ac_pthread_key_t_is_integer=no] ++) ++AC_MSG_RESULT($ac_pthread_key_t_is_integer) ++if test "$ac_pthread_key_t_is_integer" = yes ; then ++ AC_DEFINE(PTHREAD_KEY_T_IS_INTEGER, 1, ++ [Define if pthread_key_t is integer.]) ++fi + CC="$ac_save_cc" + + AC_SUBST(OTHER_LIBTOOL_OPT) +diff --git a/pyconfig.h.in b/pyconfig.h.in +index cf0ea1f..0bd8387 100644 +--- a/pyconfig.h.in ++++ b/pyconfig.h.in +@@ -973,6 +973,9 @@ + /* Define if POSIX semaphores aren't enabled on your system */ + #undef POSIX_SEMAPHORES_NOT_ENABLED + ++/* Define if pthread_key_t is integer. */ ++#undef PTHREAD_KEY_T_IS_INTEGER ++ + /* Defined if PTHREAD_SCOPE_SYSTEM supported. */ + #undef PTHREAD_SYSTEM_SCHED_SUPPORTED + diff --git a/build/pkgs/python3/patches/3.3.6-dbm.patch b/build/pkgs/python3/patches/3.3.6-dbm.patch new file mode 100644 index 00000000000..234966b6e02 --- /dev/null +++ b/build/pkgs/python3/patches/3.3.6-dbm.patch @@ -0,0 +1,29 @@ +diff --git a/setup.py b/setup.py +index d594e02..e34994d 100644 +--- a/setup.py ++++ b/setup.py +@@ -1136,7 +1136,7 @@ class PyBuildExt(build_ext): + dbm_setup_debug = False # verbose debug prints from this script? + dbm_order = ['gdbm'] + # The standard Unix dbm module: +- if host_platform not in ['cygwin']: ++ if host_platform not in ['win32']: + config_args = [arg.strip("'") + for arg in sysconfig.get_config_var("CONFIG_ARGS").split()] + dbm_args = [arg for arg in config_args +@@ -1191,6 +1191,15 @@ class PyBuildExt(build_ext): + ], + libraries = gdbm_libs) + break ++ if find_file("ndbm.h", inc_dirs, []) is not None: ++ print("building dbm using gdbm") ++ dbmext = Extension( ++ '_dbm', ['_dbmmodule.c'], ++ define_macros=[ ++ ('HAVE_NDBM_H', None), ++ ], ++ libraries = gdbm_libs) ++ break + elif cand == "bdb": + if db_incs is not None: + if dbm_setup_debug: print("building dbm using bdb") diff --git a/build/pkgs/python3/patches/3.3.6-tkinter-x11.patch b/build/pkgs/python3/patches/3.3.6-tkinter-x11.patch new file mode 100644 index 00000000000..2cc3470078c --- /dev/null +++ b/build/pkgs/python3/patches/3.3.6-tkinter-x11.patch @@ -0,0 +1,28 @@ +diff --git a/setup.py b/setup.py +index 3373dd6..9d1844b 100644 +--- a/setup.py ++++ b/setup.py +@@ -1747,12 +1747,6 @@ class PyBuildExt(build_ext): + include_dirs.append('/usr/X11/include') + added_lib_dirs.append('/usr/X11/lib') + +- # If Cygwin, then verify that X is installed before proceeding +- if host_platform == 'cygwin': +- x11_inc = find_file('X11/Xlib.h', [], include_dirs) +- if x11_inc is None: +- return +- + # Check for BLT extension + if self.compiler.find_library_file(lib_dirs + added_lib_dirs, + 'BLT8.0'): +@@ -1770,9 +1764,7 @@ class PyBuildExt(build_ext): + if host_platform in ['aix3', 'aix4']: + libs.append('ld') + +- # Finally, link with the X11 libraries (not appropriate on cygwin) +- if host_platform != "cygwin": +- libs.append('X11') ++ libs.append('X11') + + ext = Extension('_tkinter', ['_tkinter.c', 'tkappinit.c'], + define_macros=[('WITH_APPINIT', 1)] + defs, diff --git a/build/pkgs/python3/patches/3.4.5-issue13756.patch b/build/pkgs/python3/patches/3.4.5-issue13756.patch new file mode 100644 index 00000000000..ceab9dbc92b --- /dev/null +++ b/build/pkgs/python3/patches/3.4.5-issue13756.patch @@ -0,0 +1,44 @@ +diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py +index acbe648..6bedbb2 100644 +--- a/Lib/distutils/command/build_ext.py ++++ b/Lib/distutils/command/build_ext.py +@@ -695,10 +695,6 @@ class build_ext(Command): + return ext.libraries + [pythonlib] + else: + return ext.libraries +- elif sys.platform[:6] == "cygwin": +- template = "python%d.%d" +- pythonlib = (template % +- (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] +diff --git a/Makefile.pre.in b/Makefile.pre.in +index e19fc00..87b6dad 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -636,9 +636,9 @@ $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ + $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK) + $(LN) -fsn Versions/Current/Resources $(PYTHONFRAMEWORKDIR)/Resources + +-# This rule builds the Cygwin Python DLL and import library if configured ++# This rule builds the Python DLL and import library if configured + # for a shared core library; otherwise, this rule is a noop. +-$(DLLLIBRARY) libpython$(VERSION).dll.a: $(LIBRARY_OBJS) ++$(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) + if test -n "$(DLLLIBRARY)"; then \ + $(LDSHARED) -Wl,--out-implib=$@ -o $(DLLLIBRARY) $^ \ + $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST); \ +diff --git a/Modules/makesetup b/Modules/makesetup +index 8b5cc28..abc7eee 100755 +--- a/Modules/makesetup ++++ b/Modules/makesetup +@@ -91,7 +91,7 @@ CYGWIN*) if test $libdir = . + else + ExtraLibDir='$(LIBPL)' + fi +- ExtraLibs="-L$ExtraLibDir -lpython\$(VERSION)";; ++ ExtraLibs="-L$ExtraLibDir -lpython\$(LDVERSION)";; + esac + + # Main loop diff --git a/build/pkgs/python3/patches/3.4.5-struct.patch b/build/pkgs/python3/patches/3.4.5-struct.patch new file mode 100644 index 00000000000..2fe8d93bf8a --- /dev/null +++ b/build/pkgs/python3/patches/3.4.5-struct.patch @@ -0,0 +1,13 @@ +diff --git a/Modules/_struct.c b/Modules/_struct.c +index ad9959e..0a5e5ed 100644 +--- a/Modules/_struct.c ++++ b/Modules/_struct.c +@@ -1627,7 +1627,7 @@ unpackiter_iternext(unpackiterobject *self) + } + + static PyTypeObject unpackiter_type = { +- PyVarObject_HEAD_INIT(&PyType_Type, 0) ++ PyVarObject_HEAD_INIT(NULL, 0) + "unpack_iterator", /* tp_name */ + sizeof(unpackiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ diff --git a/build/pkgs/python3/patches/3.5.2-no-libm.patch b/build/pkgs/python3/patches/3.5.2-no-libm.patch new file mode 100644 index 00000000000..f36137a072f --- /dev/null +++ b/build/pkgs/python3/patches/3.5.2-no-libm.patch @@ -0,0 +1,11 @@ +--- a/setup.py 2017-03-20 17:09:14.414219000 +0100 ++++ b/setup.py 2017-03-20 17:10:59.210847200 +0100 +@@ -570,7 +570,7 @@ + + # Check for MacOS X, which doesn't need libm.a at all + math_libs = ['m'] +- if host_platform == 'darwin': ++ if host_platform in ['darwin', 'cygwin']: + math_libs = [] + + # XXX Omitted modules: gl, pure, dl, SGI-specific modules diff --git a/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch b/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch new file mode 100644 index 00000000000..f82f86f5288 --- /dev/null +++ b/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch @@ -0,0 +1,66 @@ +diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c +index 753d987..c69c0ce 100644 +--- a/Modules/signalmodule.c ++++ b/Modules/signalmodule.c +@@ -962,7 +962,11 @@ fill_siginfo(siginfo_t *si) + PyStructSequence_SET_ITEM(result, 4, _PyLong_FromUid(si->si_uid)); + PyStructSequence_SET_ITEM(result, 5, + PyLong_FromLong((long)(si->si_status))); ++#ifdef HAVE_SIGINFO_T_SI_BAND + PyStructSequence_SET_ITEM(result, 6, PyLong_FromLong(si->si_band)); ++#else ++ PyStructSequence_SET_ITEM(result, 6, PyLong_FromLong(0L)); ++#endif + if (PyErr_Occurred()) { + Py_DECREF(result); + return NULL; +diff --git a/configure b/configure +index 2fbe259..8281f95 100755 +@@ -12750,6 +12750,18 @@ + fi + + ++# Issue #21085: In Cygwin, siginfo_t does not have si_band field. ++ac_fn_c_check_member "$LINENO" "siginfo_t" "si_band" "ac_cv_member_siginfo_t_si_band" "#include ++" ++if test "x$ac_cv_member_siginfo_t_si_band" = xyes; then : ++ ++cat >>confdefs.h <<_ACEOF ++#define HAVE_SIGINFO_T_SI_BAND 1 ++_ACEOF ++ ++ ++fi ++ + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for time.h that defines altzone" >&5 + $as_echo_n "checking for time.h that defines altzone... " >&6; } +diff --git a/configure.ac b/configure.ac +index 8ef1760..6bfee80 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3692,7 +3692,9 @@ + AC_CHECK_MEMBERS([struct stat.st_flags]) + AC_CHECK_MEMBERS([struct stat.st_gen]) + AC_CHECK_MEMBERS([struct stat.st_birthtime]) +-AC_STRUCT_ST_BLOCKS ++AC_CHECK_MEMBERS([struct stat.st_blocks]) ++# Issue #21085: In Cygwin, siginfo_t does not have si_band field. ++AC_CHECK_MEMBERS([siginfo_t.si_band], [], [], [[#include ]]) + + AC_MSG_CHECKING(for time.h that defines altzone) + AC_CACHE_VAL(ac_cv_header_time_altzone,[ +diff --git a/pyconfig.h.in b/pyconfig.h.in +index 6304a8e..eda0cf9 100644 +--- a/pyconfig.h.in ++++ b/pyconfig.h.in +@@ -837,6 +837,9 @@ + /* Define to 1 if you have the `sigaltstack' function. */ + #undef HAVE_SIGALTSTACK + ++/* Define to 1 if `si_band' is a member of `siginfo_t'. */ ++#undef HAVE_SIGINFO_T_SI_BAND ++ + /* Define to 1 if you have the `siginterrupt' function. */ + #undef HAVE_SIGINTERRUPT + diff --git a/build/pkgs/python3/patches/cygwin-ctypes.patch b/build/pkgs/python3/patches/cygwin-ctypes.patch new file mode 100644 index 00000000000..af6265ac213 --- /dev/null +++ b/build/pkgs/python3/patches/cygwin-ctypes.patch @@ -0,0 +1,12 @@ +--- a/Lib/ctypes/__init__.py 2017-03-21 15:16:01.564958900 +0100 ++++ b/Lib/ctypes/__init__.py 2017-03-21 15:18:10.476792300 +0100 +@@ -430,7 +430,8 @@ + if _os.name in ("nt", "ce"): + pythonapi = PyDLL("python dll", None, _sys.dllhandle) + elif _sys.platform == "cygwin": +- pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) ++ import sysconfig as _sysconfig ++ pythonapi = PyDLL(_sysconfig.get_config_var('DLLLIBRARY')) + else: + pythonapi = PyDLL(None) + diff --git a/build/pkgs/python3/patches/cygwin-readline.patch b/build/pkgs/python3/patches/cygwin-readline.patch new file mode 100644 index 00000000000..220f1664744 --- /dev/null +++ b/build/pkgs/python3/patches/cygwin-readline.patch @@ -0,0 +1,28 @@ +Some patches to prevent an error that would occur on Cygwin trying to call +`ldd` on the .dll.a import library for readline, which doesn't work (we +might consider later updating this with a call to dlltool instead, which is +the appropriate alternative). + +Also adds the correct link flags for curses on Cygwin. +--- a/setup.py 2017-03-20 17:39:35.351935000 +0100 ++++ b/setup.py 2017-03-20 18:16:33.670791400 +0100 +@@ -686,7 +686,8 @@ + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + # Determine if readline is already linked against curses or tinfo. +- if do_readline: ++ # This doesn't work properly on Cygwin ++ if do_readline and host_platform != 'cygwin': + if cross_compiling: + ret = os.system("%s -d %s | grep '(NEEDED)' > %s" \ + % (sysconfig.get_config_var('READELF'), +@@ -1322,6 +1323,9 @@ + + if curses_library.startswith('ncurses'): + curses_libs = [curses_library] ++ if host_platform == 'cygwin': ++ curses_libs.append('tinfo' + ++ 'w' if curses_library[-1] == 'w' else '') + exts.append( Extension('_curses', ['_cursesmodule.c'], + include_dirs=curses_includes, + define_macros=curses_defines, From 5cea3bc00d48b032c2e9319eaa20b14f4a301a5c Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 16 Mar 2017 22:40:03 +0100 Subject: [PATCH 035/151] Add patch to psutil for supporting Cygwin The patch is currently something of a mess. In particular, it duplicates a lot of code from the Linux and Windows support modules. I plan to continue improving it but by the time that's done it will probably be merged upstream shortly after, at which point a patch will no longer be needed. In the short term I need this resolved as it is a major blocker to restoring Cygwin builds. --- .../pkgs/psutil/patches/cygwin-support.patch | 4614 +++++++++++++++++ 1 file changed, 4614 insertions(+) create mode 100644 build/pkgs/psutil/patches/cygwin-support.patch diff --git a/build/pkgs/psutil/patches/cygwin-support.patch b/build/pkgs/psutil/patches/cygwin-support.patch new file mode 100644 index 00000000000..de70047129b --- /dev/null +++ b/build/pkgs/psutil/patches/cygwin-support.patch @@ -0,0 +1,4614 @@ +diff --git a/psutil/__init__.py b/psutil/__init__.py +index 6b88776..cb0f840 100644 +--- a/psutil/__init__.py ++++ b/psutil/__init__.py +@@ -71,6 +71,7 @@ from ._common import OSX + from ._common import POSIX # NOQA + from ._common import SUNOS + from ._common import WINDOWS ++from ._common import CYGWIN + + if LINUX: + # This is public API and it will be retrieved from _pslinux.py +@@ -147,6 +148,10 @@ elif SUNOS: + # _pssunos.py via sys.modules. + PROCFS_PATH = "/proc" + ++elif CYGWIN: ++ PROCFS_PATH = "/proc" ++ from . import _pscygwin as _psplatform ++ + else: # pragma: no cover + raise NotImplementedError('platform %s is not supported' % sys.platform) + +@@ -174,7 +179,7 @@ __all__ = [ + "POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED", + + "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "OSX", "POSIX", "SUNOS", +- "WINDOWS", ++ "WINDOWS", "CYGWIN", + + # classes + "Process", "Popen", +@@ -326,6 +331,13 @@ _psplatform.ZombieProcess = ZombieProcess + _psplatform.AccessDenied = AccessDenied + _psplatform.TimeoutExpired = TimeoutExpired + ++# TODO: probably having these both is superfulous, and platform ++# specific modules can import these exceptions from _common ++_common.NoSuchProcess = NoSuchProcess ++_common.ZombieProcess = ZombieProcess ++_common.AccessDenied = AccessDenied ++_common.TimeoutExpired = TimeoutExpired ++ + + # ===================================================================== + # --- Process class +@@ -942,7 +954,7 @@ class Process(object): + # (self) it means child's PID has been reused + if self.create_time() <= p.create_time(): + ret.append(p) +- except (NoSuchProcess, ZombieProcess): ++ except (AccessDenied, NoSuchProcess, ZombieProcess): + pass + else: # pragma: no cover + # Windows only (faster) +@@ -964,7 +976,7 @@ class Process(object): + for p in process_iter(): + try: + table[p.ppid()].append(p) +- except (NoSuchProcess, ZombieProcess): ++ except (AccessDenied, NoSuchProcess, ZombieProcess): + pass + else: # pragma: no cover + for pid, ppid in ppid_map.items(): +diff --git a/psutil/_common.py b/psutil/_common.py +index 2497226..7b48ddf 100644 +--- a/psutil/_common.py ++++ b/psutil/_common.py +@@ -32,10 +32,14 @@ if sys.version_info >= (3, 4): + else: + enum = None + ++ ++PY3 = sys.version_info[0] == 3 ++ ++ + __all__ = [ + # OS constants + 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'OSX', 'POSIX', 'SUNOS', +- 'WINDOWS', ++ 'WINDOWS', 'CYGWIN', + # connection constants + 'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED', + 'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN', +@@ -72,6 +76,7 @@ OPENBSD = sys.platform.startswith("openbsd") + NETBSD = sys.platform.startswith("netbsd") + BSD = FREEBSD or OPENBSD or NETBSD + SUNOS = sys.platform.startswith("sunos") or sys.platform.startswith("solaris") ++CYGWIN = sys.platform.startswith("cygwin") + + + # =================================================================== +@@ -232,6 +237,18 @@ if AF_UNIX is not None: + del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM + + ++# ===================================================================== ++# -- exceptions ++# ===================================================================== ++ ++ ++# these get overwritten on "import psutil" from the __init__.py file ++NoSuchProcess = None ++ZombieProcess = None ++AccessDenied = None ++TimeoutExpired = None ++ ++ + # =================================================================== + # --- utils + # =================================================================== +@@ -334,6 +351,10 @@ def memoize_when_activated(fun): + return wrapper + + ++def get_procfs_path(): ++ return sys.modules['psutil'].PROCFS_PATH ++ ++ + def isfile_strict(path): + """Same as os.path.isfile() but does not swallow EACCES / EPERM + exceptions, see: +@@ -349,6 +370,55 @@ def isfile_strict(path): + return stat.S_ISREG(st.st_mode) + + ++def open_binary(fname, **kwargs): ++ return open(fname, "rb", **kwargs) ++ ++ ++_FS_ENCODING = sys.getfilesystemencoding() ++_ENCODING_ERRORS_HANDLER = 'surrogateescape' ++ ++ ++# TODO: Move some of these functions to ._compat since they're more for py2/3 ++# compatibility than anything else ++ ++def open_text(fname, **kwargs): ++ """On Python 3 opens a file in text mode by using fs encoding and ++ a proper en/decoding errors handler. ++ On Python 2 this is just an alias for open(name, 'rt'). ++ """ ++ if PY3: ++ # See: ++ # https://github.com/giampaolo/psutil/issues/675 ++ # https://github.com/giampaolo/psutil/pull/733 ++ kwargs.setdefault('encoding', _FS_ENCODING) ++ kwargs.setdefault('errors', _ENCODING_ERRORS_HANDLER) ++ return open(fname, "rt", **kwargs) ++ ++ ++if PY3: ++ def decode(s): ++ return s.decode(encoding=_FS_ENCODING, errors=_ENCODING_ERRORS_HANDLER) ++else: ++ def decode(s): ++ return s ++ ++ ++if PY3: ++ def encode(s, encoding=_FS_ENCODING): ++ return s ++else: ++ def encode(s, encoding=_FS_ENCODING): ++ if isinstance(s, str): ++ return s ++ ++ try: ++ return s.encode(encoding) ++ except UnicodeEncodeError: ++ # Filesystem codec failed, return the plain unicode ++ # string (this should never happen). ++ return s ++ ++ + def path_exists_strict(path): + """Same as os.path.exists() but does not swallow EACCES / EPERM + exceptions, see: +@@ -447,3 +517,23 @@ def deprecated_method(replacement): + return getattr(self, replacement)(*args, **kwargs) + return inner + return outer ++ ++ ++def wrap_exceptions(fun): ++ """Decorator which translates bare OSError and IOError exceptions ++ into NoSuchProcess and AccessDenied. ++ """ ++ @functools.wraps(fun) ++ def wrapper(self, *args, **kwargs): ++ try: ++ return fun(self, *args, **kwargs) ++ except EnvironmentError as err: ++ # ENOENT (no such file or directory) gets raised on open(). ++ # ESRCH (no such process) can get raised on read() if ++ # process is gone in meantime. ++ if err.errno in (errno.ENOENT, errno.ESRCH): ++ raise NoSuchProcess(self.pid, self._name) ++ if err.errno in (errno.EPERM, errno.EACCES): ++ raise AccessDenied(self.pid, self._name) ++ raise ++ return wrapper +diff --git a/psutil/_pscygwin.py b/psutil/_pscygwin.py +new file mode 100644 +index 0000000..ffba639 +--- /dev/null ++++ b/psutil/_pscygwin.py +@@ -0,0 +1,908 @@ ++"""Cygwin platform implementation.""" ++ ++# TODO: Large chunks of this module are copy/pasted from the Linux module; ++# seek out further opportunities for refactoring ++ ++from __future__ import division ++ ++import errno ++import os ++import re ++import sys ++from collections import namedtuple ++ ++from . import _common ++from . import _psposix ++from . import _psutil_cygwin as cext ++from . import _psutil_posix as cext_posix ++from ._common import conn_tmap ++from ._common import decode ++from ._common import encode ++from ._common import get_procfs_path ++from ._common import isfile_strict ++from ._common import memoize_when_activated ++from ._common import open_binary ++from ._common import open_text ++from ._common import popenfile ++from ._common import sockfam_to_enum ++from ._common import socktype_to_enum ++from ._common import usage_percent ++from ._common import wrap_exceptions ++from ._compat import PY3 ++from ._compat import b ++from ._compat import lru_cache ++from ._compat import xrange ++ ++if sys.version_info >= (3, 4): ++ import enum ++else: ++ enum = None ++ ++__extra__all__ = ["PROCFS_PATH"] ++ ++ ++# ===================================================================== ++# --- constants ++# ===================================================================== ++ ++_cygwin_version_re = re.compile(r'(?P\d+)\.(?P\d+)\.' ++ '(?P\d+)\((?P\d+)\.' ++ '(?P\d+)/(?P\d+)/' ++ '(?P\d+)\)') ++_cygwin_version = _cygwin_version_re.match(os.uname()[2]).groupdict() ++ ++CYGWIN_VERSION = (int(_cygwin_version['major']), ++ int(_cygwin_version['minor']), ++ int(_cygwin_version['micro'])) ++ ++CYGWIN_VERSION_API = (int(_cygwin_version['api_major']), ++ int(_cygwin_version['api_minor'])) ++ ++CYGWIN_VERSION_SHARED_DATA = int(_cygwin_version['shared_data']) ++ ++CYGWIN_VERSION_MOUNT_REGISTRY = int(_cygwin_version['mount_registry']) ++ ++ ++if enum is None: ++ AF_LINK = -1 ++else: ++ AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1}) ++ AF_LINK = AddressFamily.AF_LINK ++ ++ ++# Number of clock ticks per second ++CLOCK_TICKS = os.sysconf("SC_CLK_TCK") ++ ++ ++# TODO: Update me to properly reflect Cygwin-recognized process statuses ++# taken from /fs/proc/array.c ++PROC_STATUSES = { ++ "R": _common.STATUS_RUNNING, ++ "S": _common.STATUS_SLEEPING, ++ "D": _common.STATUS_DISK_SLEEP, ++ "T": _common.STATUS_STOPPED, ++ "t": _common.STATUS_TRACING_STOP, ++ "Z": _common.STATUS_ZOMBIE, ++ "X": _common.STATUS_DEAD, ++ "x": _common.STATUS_DEAD, ++ "K": _common.STATUS_WAKE_KILL, ++ "W": _common.STATUS_WAKING ++} ++ ++CONN_DELETE_TCB = "DELETE_TCB" ++ ++TCP_STATUSES = { ++ cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED, ++ cext.MIB_TCP_STATE_SYN_SENT: _common.CONN_SYN_SENT, ++ cext.MIB_TCP_STATE_SYN_RCVD: _common.CONN_SYN_RECV, ++ cext.MIB_TCP_STATE_FIN_WAIT1: _common.CONN_FIN_WAIT1, ++ cext.MIB_TCP_STATE_FIN_WAIT2: _common.CONN_FIN_WAIT2, ++ cext.MIB_TCP_STATE_TIME_WAIT: _common.CONN_TIME_WAIT, ++ cext.MIB_TCP_STATE_CLOSED: _common.CONN_CLOSE, ++ cext.MIB_TCP_STATE_CLOSE_WAIT: _common.CONN_CLOSE_WAIT, ++ cext.MIB_TCP_STATE_LAST_ACK: _common.CONN_LAST_ACK, ++ cext.MIB_TCP_STATE_LISTEN: _common.CONN_LISTEN, ++ cext.MIB_TCP_STATE_CLOSING: _common.CONN_CLOSING, ++ cext.MIB_TCP_STATE_DELETE_TCB: CONN_DELETE_TCB, ++ cext.PSUTIL_CONN_NONE: _common.CONN_NONE, ++} ++ ++ ++ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES, ++ cext.ERROR_ACCESS_DENIED]) ++ ++# ===================================================================== ++# -- exceptions ++# ===================================================================== ++ ++ ++# these get overwritten on "import psutil" from the __init__.py file ++NoSuchProcess = None ++ZombieProcess = None ++AccessDenied = None ++TimeoutExpired = None ++ ++ ++# ===================================================================== ++# --- utils ++# ===================================================================== ++ ++ ++# TODO: Alternatively use Cygwin's API to get this, maybe? ++def cygpid_to_winpid(pid): ++ """ ++ Converts Cygwin's internal PID (the one exposed by all POSIX interfaces) ++ to the associated Windows PID. ++ """ ++ ++ procfs_path = get_procfs_path() ++ path = '%s/%s/winpid' % (procfs_path, pid) ++ if not os.path.exists(path): ++ raise NoSuchProcess(pid) ++ ++ with open_binary('%s/%s/winpid' % (procfs_path, pid)) as f: ++ return int(f.readline().strip()) ++ ++ ++def winpid_to_cygpid(pid): ++ """ ++ Converts a Windows PID to its associated Cygwin PID. ++ """ ++ ++ # TODO: This is quite ineffecient--Cygwin provides an API for this that we ++ # can use later ++ procfs_path = get_procfs_path() ++ for path in os.listdir(procfs_path): ++ if not path.isdigit(): ++ continue ++ ++ winpid_path = '%s/%s/winpid' % (procfs_path, path) ++ ++ if not os.path.exists(winpid_path): ++ continue ++ ++ with open_binary(winpid_path) as f: ++ winpid = int(f.readline().strip()) ++ if winpid == pid: ++ return int(path) ++ ++ raise NoSuchProcess(pid) ++ ++ ++@lru_cache(maxsize=512) ++def _win32_QueryDosDevice(s): ++ return cext.win32_QueryDosDevice(s) ++ ++ ++# TODO: Copied almost verbatim from the windows module, except don't ++# us os.path.join since that uses posix sep ++def convert_dos_path(s): ++ # convert paths using native DOS format like: ++ # "\Device\HarddiskVolume1\Windows\systemew\file.txt" ++ # into: "C:\Windows\systemew\file.txt" ++ if PY3 and not isinstance(s, str): ++ s = s.decode('utf8') ++ rawdrive = '\\'.join(s.split('\\')[:3]) ++ driveletter = _win32_QueryDosDevice(rawdrive) ++ return '%s\\%s' % (driveletter, s[len(rawdrive):]) ++ ++ ++# ===================================================================== ++# --- named tuples ++# ===================================================================== ++ ++ ++scputimes = namedtuple('scputimes', ['user', 'system', 'idle']) ++pmem = namedtuple('pmem', ['rss', 'vms', 'shared', 'text', 'lib', 'data', ++ 'dirty']) ++pmem = namedtuple( ++ 'pmem', ['rss', 'vms', ++ 'num_page_faults', 'peak_wset', 'wset', 'peak_paged_pool', ++ 'paged_pool', 'peak_nonpaged_pool', 'nonpaged_pool', ++ 'pagefile', 'peak_pagefile', 'private']) ++pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', )) ++svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) ++pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss']) ++pmmap_ext = namedtuple( ++ 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) ++ntpinfo = namedtuple( ++ 'ntpinfo', ['num_handles', 'ctx_switches', 'user_time', 'kernel_time', ++ 'create_time', 'num_threads', 'io_rcount', 'io_wcount', ++ 'io_rbytes', 'io_wbytes']) ++ ++ ++# ===================================================================== ++# --- system memory ++# ===================================================================== ++ ++ ++def _get_meminfo(): ++ mems = {} ++ with open_binary('%s/meminfo' % get_procfs_path()) as f: ++ for line in f: ++ fields = line.split() ++ mems[fields[0].rstrip(b':')] = int(fields[1]) * 1024 ++ ++ return mems ++ ++ ++def virtual_memory(): ++ """Report virtual memory stats. ++ This implementation matches "free" and "vmstat -s" cmdline ++ utility values and procps-ng-3.3.12 source was used as a reference ++ (2016-09-18): ++ https://gitlab.com/procps-ng/procps/blob/ ++ 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c ++ For reference, procps-ng-3.3.10 is the version available on Ubuntu ++ 16.04. ++ ++ Note about "available" memory: up until psutil 4.3 it was ++ calculated as "avail = (free + buffers + cached)". Now ++ "MemAvailable:" column (kernel 3.14) from /proc/meminfo is used as ++ it's more accurate. ++ That matches "available" column in newer versions of "free". ++ """ ++ mems = _get_meminfo() ++ ++ # /proc doc states that the available fields in /proc/meminfo vary ++ # by architecture and compile options, but these 3 values are also ++ # returned by sysinfo(2); as such we assume they are always there. ++ total = mems[b'MemTotal'] ++ free = mems[b'MemFree'] ++ ++ used = total - free ++ ++ # On Windows we are treating avail and free as the same ++ # TODO: Are they really though? ++ avail = free ++ ++ percent = usage_percent((total - avail), total, _round=1) ++ ++ return svmem(total, avail, percent, used, free) ++ ++ ++def swap_memory(): ++ mems = _get_meminfo() ++ total = mems[b'SwapTotal'] ++ free = mems[b'SwapFree'] ++ used = total - free ++ percent = usage_percent(used, total, _round=1) ++ return _common.sswap(total, used, free, percent, 0, 0) ++ ++ ++# ===================================================================== ++# --- CPUs ++# ===================================================================== ++ ++ ++def cpu_times(): ++ """Return a named tuple representing the following system-wide ++ CPU times: ++ (user, nice, system, idle, iowait, irq, softirq [steal, [guest, ++ [guest_nice]]]) ++ Last 3 fields may not be available on all Linux kernel versions. ++ """ ++ procfs_path = get_procfs_path() ++ with open_binary('%s/stat' % procfs_path) as f: ++ values = f.readline().split() ++ fields = values[1:2] + values[3:len(scputimes._fields) + 2] ++ fields = [float(x) / CLOCK_TICKS for x in fields] ++ return scputimes(*fields) ++ ++ ++def per_cpu_times(): ++ """Return a list of namedtuple representing the CPU times ++ for every CPU available on the system. ++ """ ++ procfs_path = get_procfs_path() ++ cpus = [] ++ with open_binary('%s/stat' % procfs_path) as f: ++ # get rid of the first line which refers to system wide CPU stats ++ f.readline() ++ for line in f: ++ if line.startswith(b'cpu'): ++ values = line.split() ++ fields = values[1:2] + values[3:len(scputimes._fields) + 2] ++ fields = [float(x) / CLOCK_TICKS for x in fields] ++ entry = scputimes(*fields) ++ cpus.append(entry) ++ return cpus ++ ++ ++def cpu_count_logical(): ++ """Return the number of logical CPUs in the system.""" ++ try: ++ return os.sysconf("SC_NPROCESSORS_ONLN") ++ except ValueError: ++ # as a second fallback we try to parse /proc/cpuinfo ++ num = 0 ++ with open_binary('%s/cpuinfo' % get_procfs_path()) as f: ++ for line in f: ++ if line.lower().startswith(b'processor'): ++ num += 1 ++ ++ # unknown format (e.g. amrel/sparc architectures), see: ++ # https://github.com/giampaolo/psutil/issues/200 ++ # try to parse /proc/stat as a last resort ++ if num == 0: ++ search = re.compile('cpu\d') ++ with open_text('%s/stat' % get_procfs_path()) as f: ++ for line in f: ++ line = line.split(' ')[0] ++ if search.match(line): ++ num += 1 ++ ++ if num == 0: ++ # mimic os.cpu_count() ++ return None ++ return num ++ ++ ++def cpu_count_physical(): ++ """Return the number of physical cores in the system.""" ++ mapping = {} ++ current_info = {} ++ with open_binary('%s/cpuinfo' % get_procfs_path()) as f: ++ for line in f: ++ line = line.strip().lower() ++ if not line: ++ # new section ++ if (b'physical id' in current_info and ++ b'cpu cores' in current_info): ++ mapping[current_info[b'physical id']] = \ ++ current_info[b'cpu cores'] ++ current_info = {} ++ else: ++ # ongoing section ++ if (line.startswith(b'physical id') or ++ line.startswith(b'cpu cores')): ++ key, value = line.split(b'\t:', 1) ++ current_info[key] = int(value) ++ ++ # mimic os.cpu_count() ++ return sum(mapping.values()) or None ++ ++ ++# TODO: Works mostly the same as on Linux, but softirq is not available; ++# meanwhile the Windows module supports number of system calls, but this ++# implementation does not. There's also a question of whether we want it ++# to count Cygwin "system" calls, actual Windows system calls, or what... ++# It's a somewhat ill-defined field on Cygwin; may have to come back to it ++# TODO: Depending on what we decide to do about syscalls, this implementation ++# could be shared with the Linux implementation with some minor tweaks to the ++# latter ++def cpu_stats(): ++ with open_binary('%s/stat' % get_procfs_path()) as f: ++ ctx_switches = None ++ interrupts = None ++ for line in f: ++ if line.startswith(b'ctxt'): ++ ctx_switches = int(line.split()[1]) ++ elif line.startswith(b'intr'): ++ interrupts = int(line.split()[1]) ++ if ctx_switches is not None and interrupts is not None: ++ break ++ syscalls = 0 ++ soft_interrupts = 0 ++ return _common.scpustats( ++ ctx_switches, interrupts, soft_interrupts, syscalls) ++ ++ ++# ===================================================================== ++# --- network ++# ===================================================================== ++ ++ ++# TODO: This might merit a little further work to get the "friendly" ++# interface names instead of interface UUIDs ++net_if_addrs = cext_posix.net_if_addrs ++ ++ ++def net_connections(kind, _pid=-1): ++ """Return socket connections. If pid == -1 return system-wide ++ connections (as opposed to connections opened by one process only). ++ """ ++ if kind not in conn_tmap: ++ raise ValueError("invalid %r kind argument; choose between %s" ++ % (kind, ', '.join([repr(x) for x in conn_tmap]))) ++ elif kind == 'unix': ++ raise ValueError("invalid %r kind argument; although UNIX sockets " ++ "are supported on Cygwin it is not possible to " ++ "enumerate the UNIX sockets opened by a process" ++ % kind) ++ ++ families, types = conn_tmap[kind] ++ if _pid > 0: ++ _pid = cygpid_to_winpid(_pid) ++ ++ # Note: This lists *all* net connections on the system, not just ones by ++ # Cygwin processes; below we whittle it down just to Cygwin processes, but ++ # we might consider an extra option for showing non-Cygwin processes as ++ # well ++ rawlist = cext.net_connections(_pid, families, types) ++ ret = set() ++ for item in rawlist: ++ fd, fam, type, laddr, raddr, status, pid = item ++ status = TCP_STATUSES[status] ++ fam = sockfam_to_enum(fam) ++ type = socktype_to_enum(type) ++ if _pid == -1: ++ try: ++ pid = winpid_to_cygpid(pid) ++ except NoSuchProcess: ++ continue ++ nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid) ++ else: ++ nt = _common.pconn(fd, fam, type, laddr, raddr, status) ++ ret.add(nt) ++ return list(ret) ++ ++ ++def net_if_stats(): ++ ret = {} ++ if_stats = cext.net_if_stats() ++ # NOTE: Cygwin does some tricky things in how getifaddrs is handled ++ # which our net_if_stats does not do, such that net_if_stats does not ++ # return all the same interfaces as net_if_stats, so here we artifically ++ # limit the net_if_stats() results to just those interfaces returned by ++ # net_if_addrs ++ if_names = set(addr[0] for addr in net_if_addrs()) ++ for name, items in if_stats.items(): ++ if name not in if_names: ++ continue ++ name = encode(name) ++ isup, duplex, speed, mtu = items ++ if hasattr(_common, 'NicDuplex'): ++ duplex = _common.NicDuplex(duplex) ++ ret[name] = _common.snicstats(isup, duplex, speed, mtu) ++ return ret ++ ++ ++def net_io_counters(): ++ ret = cext.net_io_counters() ++ return dict([(encode(k), v) for k, v in ret.items()]) ++ ++ ++# ===================================================================== ++# --- sensors ++# ===================================================================== ++ ++ ++# TODO: Copied verbatim from the Windows module ++def sensors_battery(): ++ """Return battery information.""" ++ # For constants meaning see: ++ # https://msdn.microsoft.com/en-us/library/windows/desktop/ ++ # aa373232(v=vs.85).aspx ++ acline_status, flags, percent, secsleft = cext.sensors_battery() ++ power_plugged = acline_status == 1 ++ no_battery = bool(flags & 128) ++ charging = bool(flags & 8) ++ ++ if no_battery: ++ return None ++ if power_plugged or charging: ++ secsleft = _common.POWER_TIME_UNLIMITED ++ elif secsleft == -1: ++ secsleft = _common.POWER_TIME_UNKNOWN ++ ++ return _common.sbattery(percent, secsleft, power_plugged) ++ ++ ++# ===================================================================== ++# --- disks ++# ===================================================================== ++ ++ ++disk_io_counters = cext.disk_io_counters ++ ++ ++disk_usage = _psposix.disk_usage ++ ++ ++# TODO: Copied verbatim from the Linux module; refactor ++def disk_partitions(all=False): ++ """Return mounted disk partitions as a list of namedtuples""" ++ fstypes = set() ++ with open_text("%s/filesystems" % get_procfs_path()) as f: ++ for line in f: ++ line = line.strip() ++ if not line.startswith("nodev"): ++ fstypes.add(line.strip()) ++ else: ++ # ignore all lines starting with "nodev" except "nodev zfs" ++ fstype = line.split("\t")[1] ++ if fstype == "zfs": ++ fstypes.add("zfs") ++ ++ retlist = [] ++ partitions = cext.disk_partitions() ++ for partition in partitions: ++ device, mountpoint, fstype, opts = partition ++ if device == 'none': ++ device = '' ++ if not all: ++ if device == '' or fstype not in fstypes: ++ continue ++ ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) ++ retlist.append(ntuple) ++ return retlist ++ ++ ++# ===================================================================== ++# --- other system functions ++# ===================================================================== ++ ++ ++def boot_time(): ++ """The system boot time expressed in seconds since the epoch.""" ++ return cext.boot_time() ++ ++ ++# TODO: Copied verbatim from the Linux module ++def users(): ++ """Return currently connected users as a list of namedtuples.""" ++ retlist = [] ++ rawlist = cext.users() ++ for item in rawlist: ++ user, tty, hostname, tstamp, user_process = item ++ # note: the underlying C function includes entries about ++ # system boot, run level and others. We might want ++ # to use them in the future. ++ if not user_process: ++ continue ++ if hostname == ':0.0' or hostname == ':0': ++ hostname = 'localhost' ++ nt = _common.suser(user, tty or None, hostname, tstamp) ++ retlist.append(nt) ++ return retlist ++ ++ ++# ===================================================================== ++# --- processes ++# ===================================================================== ++ ++ ++def pids(): ++ """Returns a list of PIDs currently running on the system.""" ++ return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()] ++ ++ ++def pid_exists(pid): ++ """Check For the existence of a unix pid.""" ++ return _psposix.pid_exists(pid) ++ ++ ++class Process(object): ++ """Cygwin process implementation.""" ++ ++ __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_winpid"] ++ ++ def __init__(self, pid): ++ self.pid = pid ++ self._name = None ++ self._ppid = None ++ self._procfs_path = get_procfs_path() ++ self._winpid = cygpid_to_winpid(pid) ++ ++ @memoize_when_activated ++ def _parse_stat_file(self): ++ """Parse /proc/{pid}/stat file. Return a list of fields where ++ process name is in position 0. ++ Using "man proc" as a reference: where "man proc" refers to ++ position N, always subscract 2 (e.g starttime pos 22 in ++ 'man proc' == pos 20 in the list returned here). ++ """ ++ ++ stat_filename = "%s/%s/stat" % (self._procfs_path, self.pid) ++ ++ # NOTE: On Cygwin, if the stat file exists but reading it raises an ++ # EINVAL, this indicates that we are probably looking at a zombie ++ # process (this doesn't happen in all cases--seems to be a bug in ++ # Cygwin) ++ try: ++ with open_binary(stat_filename) as f: ++ data = f.read() ++ except IOError as err: ++ if (err.errno == errno.EINVAL and ++ os.path.exists(err.filename)): ++ raise ZombieProcess(self.pid, self._name, self._ppid) ++ ++ raise ++ # Process name is between parentheses. It can contain spaces and ++ # other parentheses. This is taken into account by looking for ++ # the first occurrence of "(" and the last occurence of ")". ++ rpar = data.rfind(b')') ++ name = data[data.find(b'(') + 1:rpar] ++ fields_after_name = data[rpar + 2:].split() ++ return [name] + fields_after_name ++ ++ @memoize_when_activated ++ def _read_status_file(self): ++ with open_binary("%s/%s/status" % (self._procfs_path, self.pid)) as f: ++ return f.read() ++ ++ @memoize_when_activated ++ def oneshot_info(self): ++ """Return multiple information about this process as a ++ raw tuple. ++ """ ++ return cext.proc_info(self._winpid) ++ ++ def oneshot_enter(self): ++ self._parse_stat_file.cache_activate() ++ self._read_status_file.cache_activate() ++ self.oneshot_info.cache_activate() ++ ++ def oneshot_exit(self): ++ self._parse_stat_file.cache_deactivate() ++ self._read_status_file.cache_deactivate() ++ self.oneshot_info.cache_deactivate() ++ ++ @wrap_exceptions ++ def name(self): ++ name = self._parse_stat_file()[0] ++ if PY3: ++ name = decode(name) ++ # XXX - gets changed later and probably needs refactoring ++ return name ++ ++ def exe(self): ++ try: ++ return os.readlink("%s/%s/exe" % (self._procfs_path, self.pid)) ++ except OSError as err: ++ if err.errno in (errno.ENOENT, errno.ESRCH): ++ # no such file error; might be raised also if the ++ # path actually exists for system processes with ++ # low pids (about 0-20) ++ if os.path.lexists("%s/%s" % (self._procfs_path, self.pid)): ++ return "" ++ else: ++ if not _psposix.pid_exists(self.pid): ++ raise NoSuchProcess(self.pid, self._name) ++ else: ++ raise ZombieProcess(self.pid, self._name, self._ppid) ++ if err.errno in (errno.EPERM, errno.EACCES): ++ raise AccessDenied(self.pid, self._name) ++ raise ++ ++ @wrap_exceptions ++ def cmdline(self): ++ with open_text("%s/%s/cmdline" % (self._procfs_path, self.pid)) as f: ++ data = f.read() ++ if not data: ++ # may happen in case of zombie process ++ return [] ++ if data.endswith('\x00'): ++ data = data[:-1] ++ return [x for x in data.split('\x00')] ++ ++ # TODO: /proc//environ will be available in newer versions of ++ # Cygwin--do a version check and provide it if available. ++ ++ @wrap_exceptions ++ def terminal(self): ++ tty_nr = int(self._parse_stat_file()[5]) ++ tmap = _psposix.get_terminal_map() ++ try: ++ return tmap[tty_nr] ++ except KeyError: ++ return None ++ ++ @wrap_exceptions ++ def io_counters(self): ++ try: ++ ret = cext.proc_io_counters(self._winpid) ++ except OSError as err: ++ if err.errno in ACCESS_DENIED_SET: ++ nt = ntpinfo(*self.oneshot_info()) ++ ret = (nt.io_rcount, nt.io_wcount, nt.io_rbytes, nt.io_wbytes) ++ else: ++ raise ++ return _common.pio(*ret) ++ ++ @wrap_exceptions ++ def cpu_times(self): ++ values = self._parse_stat_file() ++ utime = float(values[12]) / CLOCK_TICKS ++ stime = float(values[13]) / CLOCK_TICKS ++ children_utime = float(values[14]) / CLOCK_TICKS ++ children_stime = float(values[15]) / CLOCK_TICKS ++ return _common.pcputimes(utime, stime, children_utime, children_stime) ++ ++ @wrap_exceptions ++ def wait(self, timeout=None): ++ try: ++ return _psposix.wait_pid(self.pid, timeout) ++ except _psposix.TimeoutExpired: ++ raise TimeoutExpired(timeout, self.pid, self._name) ++ ++ @wrap_exceptions ++ def create_time(self): ++ try: ++ return cext.proc_create_time(self._winpid) ++ except OSError as err: ++ if err.errno in ACCESS_DENIED_SET: ++ return ntpinfo(*self.oneshot_info()).create_time ++ raise ++ ++ def _get_raw_meminfo(self): ++ try: ++ return cext.proc_memory_info(self._winpid) ++ except OSError as err: ++ if err.errno in ACCESS_DENIED_SET: ++ # TODO: the C ext can probably be refactored in order ++ # to get this from cext.proc_info() ++ return cext.proc_memory_info_2(self._winpid) ++ raise ++ ++ @wrap_exceptions ++ def memory_info(self): ++ # on Windows RSS == WorkingSetSize and VMS == PagefileUsage. ++ # Underlying C function returns fields of PROCESS_MEMORY_COUNTERS ++ # struct. ++ t = self._get_raw_meminfo() ++ rss = t[2] # wset ++ vms = t[7] # pagefile ++ return pmem(*(rss, vms, ) + t) ++ ++ @wrap_exceptions ++ def memory_full_info(self): ++ basic_mem = self.memory_info() ++ uss = cext.proc_memory_uss(self._winpid) ++ return pfullmem(*basic_mem + (uss, )) ++ ++ def memory_maps(self): ++ try: ++ raw = cext.proc_memory_maps(self._winpid) ++ except OSError as err: ++ # XXX - can't use wrap_exceptions decorator as we're ++ # returning a generator; probably needs refactoring. ++ if err.errno in ACCESS_DENIED_SET: ++ raise AccessDenied(self.pid, self._name) ++ if err.errno == errno.ESRCH: ++ raise NoSuchProcess(self.pid, self._name) ++ raise ++ else: ++ for addr, perm, path, rss in raw: ++ path = cext.winpath_to_cygpath(convert_dos_path(path)) ++ addr = hex(addr) ++ yield (addr, perm, path, rss) ++ ++ @wrap_exceptions ++ def cwd(self): ++ return os.readlink("%s/%s/cwd" % (self._procfs_path, self.pid)) ++ ++ @wrap_exceptions ++ def num_ctx_switches(self): ++ ctx_switches = ntpinfo(*self.oneshot_info()).ctx_switches ++ # only voluntary ctx switches are supported ++ return _common.pctxsw(ctx_switches, 0) ++ ++ @wrap_exceptions ++ def num_threads(self): ++ return ntpinfo(*self.oneshot_info()).num_threads ++ ++ @wrap_exceptions ++ def threads(self): ++ rawlist = cext.proc_threads(self._winpid) ++ retlist = [] ++ for thread_id, utime, stime in rawlist: ++ ntuple = _common.pthread(thread_id, utime, stime) ++ retlist.append(ntuple) ++ return retlist ++ ++ @wrap_exceptions ++ def nice_get(self): ++ # Use C implementation ++ return cext_posix.getpriority(self.pid) ++ ++ @wrap_exceptions ++ def nice_set(self, value): ++ return cext_posix.setpriority(self.pid, value) ++ ++ @wrap_exceptions ++ def cpu_affinity_get(self): ++ def from_bitmask(x): ++ return [i for i in xrange(64) if (1 << i) & x] ++ bitmask = cext.proc_cpu_affinity_get(self._winpid) ++ return from_bitmask(bitmask) ++ ++ @wrap_exceptions ++ def cpu_affinity_set(self, value): ++ def to_bitmask(l): ++ if not l: ++ raise ValueError("invalid argument %r" % l) ++ out = 0 ++ for bit in l: ++ out |= 2 ** bit ++ return out ++ ++ # SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER ++ # is returned for an invalid CPU but this seems not to be true, ++ # therefore we check CPUs validy beforehand. ++ allcpus = list(range(len(per_cpu_times()))) ++ for cpu in value: ++ if cpu not in allcpus: ++ if not isinstance(cpu, (int, long)): ++ raise TypeError( ++ "invalid CPU %r; an integer is required" % cpu) ++ else: ++ raise ValueError("invalid CPU %r" % cpu) ++ ++ bitmask = to_bitmask(value) ++ try: ++ cext.proc_cpu_affinity_set(self._winpid, bitmask) ++ except OSError as exc: ++ if exc.errno == errno.EIO: ++ raise AccessDenied(self.pid, self._name) ++ ++ @wrap_exceptions ++ def status(self): ++ letter = self._parse_stat_file()[1] ++ if PY3: ++ letter = letter.decode() ++ # XXX is '?' legit? (we're not supposed to return it anyway) ++ return PROC_STATUSES.get(letter, '?') ++ ++ @wrap_exceptions ++ def open_files(self): ++ retlist = [] ++ files = os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)) ++ hit_enoent = False ++ for fd in files: ++ file = "%s/%s/fd/%s" % (self._procfs_path, self.pid, fd) ++ try: ++ path = os.readlink(file) ++ except OSError as err: ++ # ENOENT == file which is gone in the meantime ++ if err.errno in (errno.ENOENT, errno.ESRCH): ++ hit_enoent = True ++ continue ++ elif err.errno == errno.EINVAL: ++ # not a link ++ continue ++ else: ++ raise ++ else: ++ # If path is not an absolute there's no way to tell ++ # whether it's a regular file or not, so we skip it. ++ # A regular file is always supposed to be have an ++ # absolute path though. ++ if path.startswith('/') and isfile_strict(path): ++ ntuple = popenfile(path, int(fd)) ++ retlist.append(ntuple) ++ if hit_enoent: ++ # raise NSP if the process disappeared on us ++ os.stat('%s/%s' % (self._procfs_path, self.pid)) ++ return retlist ++ ++ @wrap_exceptions ++ def connections(self, kind='inet'): ++ return net_connections(kind, _pid=self.pid) ++ ++ @wrap_exceptions ++ def num_fds(self): ++ return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid))) ++ ++ @wrap_exceptions ++ def ppid(self): ++ return int(self._parse_stat_file()[2]) ++ ++ @wrap_exceptions ++ def uids(self, _uids_re=re.compile(b'Uid:\t(\d+)')): ++ # More or less the same as on Linux, but the fields are separated by ++ # spaces instead of tabs; and anyways there is no difference on Cygwin ++ # between real, effective, and saved uids. ++ # TODO: We could use the same regexp on both Linux and Cygwin by just ++ # changing the Linux regexp to treat whitespace more flexibly ++ data = self._read_status_file() ++ real = _uids_re.findall(data)[0] ++ return _common.puids(int(real), int(real), int(real)) ++ ++ @wrap_exceptions ++ def gids(self, _gids_re=re.compile(b'Gid:\t(\d+)')): ++ # See note in uids ++ data = self._read_status_file() ++ real = _gids_re.findall(data)[0] ++ return _common.pgids(int(real), int(real), int(real)) +diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py +index 533b548..51852b1 100644 +--- a/psutil/_pslinux.py ++++ b/psutil/_pslinux.py +@@ -9,7 +9,6 @@ from __future__ import division + import base64 + import collections + import errno +-import functools + import glob + import os + import re +@@ -25,9 +24,13 @@ from . import _common + from . import _psposix + from . import _psutil_linux as cext + from . import _psutil_posix as cext_posix ++from ._common import decode ++from ._common import get_procfs_path + from ._common import isfile_strict + from ._common import memoize + from ._common import memoize_when_activated ++from ._common import open_binary ++from ._common import open_text + from ._common import parse_environ_block + from ._common import NIC_DUPLEX_FULL + from ._common import NIC_DUPLEX_HALF +@@ -35,6 +38,7 @@ from ._common import NIC_DUPLEX_UNKNOWN + from ._common import path_exists_strict + from ._common import supports_ipv6 + from ._common import usage_percent ++from ._common import wrap_exceptions + from ._compat import b + from ._compat import basestring + from ._compat import long +@@ -83,9 +87,7 @@ BOOT_TIME = None # set later + # speedup, see: https://github.com/giampaolo/psutil/issues/708 + BIGGER_FILE_BUFFERING = -1 if PY3 else 8192 + LITTLE_ENDIAN = sys.byteorder == 'little' +-if PY3: +- FS_ENCODING = sys.getfilesystemencoding() +- ENCODING_ERRORS_HANDLER = 'surrogateescape' ++ + if enum is None: + AF_LINK = socket.AF_PACKET + else: +@@ -186,37 +188,6 @@ pio = namedtuple('pio', ['read_count', 'write_count', + # ===================================================================== + + +-def open_binary(fname, **kwargs): +- return open(fname, "rb", **kwargs) +- +- +-def open_text(fname, **kwargs): +- """On Python 3 opens a file in text mode by using fs encoding and +- a proper en/decoding errors handler. +- On Python 2 this is just an alias for open(name, 'rt'). +- """ +- if PY3: +- # See: +- # https://github.com/giampaolo/psutil/issues/675 +- # https://github.com/giampaolo/psutil/pull/733 +- kwargs.setdefault('encoding', FS_ENCODING) +- kwargs.setdefault('errors', ENCODING_ERRORS_HANDLER) +- return open(fname, "rt", **kwargs) +- +- +-if PY3: +- def decode(s): +- return s.decode(encoding=FS_ENCODING, errors=ENCODING_ERRORS_HANDLER) +-else: +- def decode(s): +- return s +- +- +-def get_procfs_path(): +- """Return updated psutil.PROCFS_PATH constant.""" +- return sys.modules['psutil'].PROCFS_PATH +- +- + def readlink(path): + """Wrapper around os.readlink().""" + assert isinstance(path, basestring), path +@@ -1304,26 +1275,6 @@ def pid_exists(pid): + return pid in pids() + + +-def wrap_exceptions(fun): +- """Decorator which translates bare OSError and IOError exceptions +- into NoSuchProcess and AccessDenied. +- """ +- @functools.wraps(fun) +- def wrapper(self, *args, **kwargs): +- try: +- return fun(self, *args, **kwargs) +- except EnvironmentError as err: +- # ENOENT (no such file or directory) gets raised on open(). +- # ESRCH (no such process) can get raised on read() if +- # process is gone in meantime. +- if err.errno in (errno.ENOENT, errno.ESRCH): +- raise NoSuchProcess(self.pid, self._name) +- if err.errno in (errno.EPERM, errno.EACCES): +- raise AccessDenied(self.pid, self._name) +- raise +- return wrapper +- +- + class Process(object): + """Linux process implementation.""" + +diff --git a/psutil/_psposix.py b/psutil/_psposix.py +index 6ed7694..4f6dde1 100644 +--- a/psutil/_psposix.py ++++ b/psutil/_psposix.py +@@ -173,7 +173,9 @@ def get_terminal_map(): + Used by Process.terminal() + """ + ret = {} +- ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*') ++ # NOTE: On Cygwin pseudo-ttys are mapped to /dev/ptyN or /dev/consN ++ ls = (glob.glob('/dev/tty*') + glob.glob('/dev/pts/*') + ++ glob.glob('/dev/pty*') + glob.glob('/dev/cons*')) + for name in ls: + assert name not in ret, name + try: +diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py +index ad72de2..9269ca9 100644 +--- a/psutil/_pssunos.py ++++ b/psutil/_pssunos.py +@@ -15,6 +15,7 @@ from . import _common + from . import _psposix + from . import _psutil_posix as cext_posix + from . import _psutil_sunos as cext ++from ._common import get_procfs_path + from ._common import isfile_strict + from ._common import memoize_when_activated + from ._common import sockfam_to_enum +@@ -97,13 +98,15 @@ pmmap_ext = namedtuple( + + + # ===================================================================== +-# --- utils ++# --- exceptions + # ===================================================================== + + +-def get_procfs_path(): +- """Return updated psutil.PROCFS_PATH constant.""" +- return sys.modules['psutil'].PROCFS_PATH ++# these get overwritten on "import psutil" from the __init__.py file ++NoSuchProcess = None ++ZombieProcess = None ++AccessDenied = None ++TimeoutExpired = None + + + # ===================================================================== +diff --git a/psutil/_psutil_cygwin.c b/psutil/_psutil_cygwin.c +new file mode 100644 +index 0000000..e2f9e94 +--- /dev/null ++++ b/psutil/_psutil_cygwin.c +@@ -0,0 +1,2025 @@ ++#define WIN32_LEAN_AND_MEAN ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* On Cygwin, mprapi.h is missing a necessary include of wincrypt.h ++ * which is needed to define some types, so we include it here since ++ * iphlpapi.h includes iprtrmib.h which in turn includes mprapi.h ++ */ ++#include ++ ++/* TODO: There are some structs defined in netioapi.h that are only defined in ++ * ws2ipdef.h has been included; however, for reasons unknown to me currently, ++ * the headers included with Cygwin deliberately do not include ws2ipdef.h ++ * when compiling for Cygwin. For now I include it manually which seems to work ++ * but it would be good to track down why this was in the first place. ++ */ ++#include ++#include ++ ++#include ++ ++#include ++ ++#include ++#include ++ ++#include "arch/windows/process_info.h" ++#include "_psutil_common.h" ++ ++ ++/* ++ * ============================================================================ ++ * Utilities ++ * ============================================================================ ++ */ ++ ++ // a flag for connections without an actual status ++static int PSUTIL_CONN_NONE = 128; ++#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff) ++#define _psutil_conn_decref_objs() \ ++ Py_DECREF(_AF_INET); \ ++ Py_DECREF(_AF_INET6);\ ++ Py_DECREF(_SOCK_STREAM);\ ++ Py_DECREF(_SOCK_DGRAM); ++ ++ ++// fix for mingw32, see ++// https://github.com/giampaolo/psutil/issues/351#c2 ++typedef struct _DISK_PERFORMANCE_WIN_2008 { ++ LARGE_INTEGER BytesRead; ++ LARGE_INTEGER BytesWritten; ++ LARGE_INTEGER ReadTime; ++ LARGE_INTEGER WriteTime; ++ LARGE_INTEGER IdleTime; ++ DWORD ReadCount; ++ DWORD WriteCount; ++ DWORD QueueDepth; ++ DWORD SplitCount; ++ LARGE_INTEGER QueryTime; ++ DWORD StorageDeviceNumber; ++ WCHAR StorageManagerName[8]; ++} DISK_PERFORMANCE_WIN_2008; ++ ++/* Python wrappers for Cygwin's cygwin_conv_path API--accepts and returns ++ * Python unicode strings. Always returns absolute paths. ++ */ ++static PyObject * ++psutil_cygwin_conv_path(PyObject *self, PyObject *args, ++ cygwin_conv_path_t what) { ++ char *from; ++ char *to; ++ ssize_t size; ++ ++ if (!PyArg_ParseTuple(args, "s", &from)) { ++ return NULL; ++ } ++ ++ size = cygwin_conv_path(what, from, NULL, 0); ++ ++ if (size < 0) { ++ /* TODO: Better error handling */ ++ return size; ++ } ++ ++ to = malloc(size); ++ if (to == NULL) { ++ return NULL; ++ } ++ ++ if (cygwin_conv_path(what, from, to, size)) { ++ return NULL; ++ } ++ ++ /* size includes the terminal null byte */ ++#if PY_MAJOR_VERSION >= 3 ++ return PyUnicode_FromStringAndSize(to, size - 1); ++#else ++ return PyString_FromStringAndSize(to, size - 1); ++#endif ++} ++ ++ ++static PyObject * ++psutil_cygpath_to_winpath(PyObject *self, PyObject *args) { ++ return psutil_cygwin_conv_path(self, args, CCP_POSIX_TO_WIN_A); ++} ++ ++ ++static PyObject * ++psutil_winpath_to_cygpath(PyObject *self, PyObject *args) { ++ return psutil_cygwin_conv_path(self, args, CCP_WIN_A_TO_POSIX); ++} ++ ++ ++/* TODO: Copied almost verbatim (_tcscmp -> wcscmp, _stprintf_s -> snprintf, ++ * _countof -> sizeof) so consider moving this into arch/windows or something ++ */ ++/* ++ Accept a filename's drive in native format like "\Device\HarddiskVolume1\" ++ and return the corresponding drive letter (e.g. "C:\\"). ++ If no match is found return an empty string. ++*/ ++static PyObject * ++psutil_win32_QueryDosDevice(PyObject *self, PyObject *args) { ++ LPCTSTR lpDevicePath; ++ TCHAR d = TEXT('A'); ++ TCHAR szBuff[5]; ++ ++ if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) ++ return NULL; ++ ++ while (d <= TEXT('Z')) { ++ TCHAR szDeviceName[3] = {d, TEXT(':'), TEXT('\0')}; ++ TCHAR szTarget[512] = {0}; ++ if (QueryDosDevice(szDeviceName, szTarget, 511) != 0) { ++ if (wcscmp(lpDevicePath, szTarget) == 0) { ++ snprintf(szBuff, sizeof(szBuff), TEXT("%c:"), d); ++ return Py_BuildValue("s", szBuff); ++ } ++ } ++ d++; ++ } ++ return Py_BuildValue("s", ""); ++} ++ ++ ++static ULONGLONG (*psutil_GetTickCount64)(void) = NULL; ++ ++/* ++ * Return a Python float representing the system uptime expressed in seconds ++ * since the epoch. ++ */ ++static PyObject * ++psutil_boot_time(PyObject *self, PyObject *args) { ++ double uptime; ++ double pt; ++ FILETIME fileTime; ++ long long ll; ++ HINSTANCE hKernel32; ++ psutil_GetTickCount64 = NULL; ++ ++ GetSystemTimeAsFileTime(&fileTime); ++ ++ /* ++ HUGE thanks to: ++ http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry ++ ++ This function converts the FILETIME structure to a 32-bit Unix time. ++ The Unix time is a 32-bit value for the number of seconds since ++ January 1, 1970. A FILETIME is a 64-bit for the number of ++ 100-nanosecond periods since January 1, 1601. Convert by ++ subtracting the number of 100-nanosecond period betwee 01-01-1970 ++ and 01-01-1601, from time_t the divide by 1e+7 to get to the same ++ base granularity. ++ */ ++ ll = (((LONGLONG)(fileTime.dwHighDateTime)) << 32) \ ++ + fileTime.dwLowDateTime; ++ pt = (double)(ll - 116444736000000000ull) / 1e7; ++ ++ // GetTickCount64() is Windows Vista+ only. Dinamically load ++ // GetTickCount64() at runtime. We may have used ++ // "#if (_WIN32_WINNT >= 0x0600)" pre-processor but that way ++ // the produced exe/wheels cannot be used on Windows XP, see: ++ // https://github.com/giampaolo/psutil/issues/811#issuecomment-230639178 ++ hKernel32 = GetModuleHandleW(L"KERNEL32"); ++ psutil_GetTickCount64 = (void*)GetProcAddress(hKernel32, "GetTickCount64"); ++ if (psutil_GetTickCount64 != NULL) { ++ // Windows >= Vista ++ uptime = (double)psutil_GetTickCount64() / 1000.00; ++ } ++ else { ++ // Windows XP. ++ // GetTickCount() time will wrap around to zero if the ++ // system is run continuously for 49.7 days. ++ uptime = (double)GetTickCount() / 1000.00; ++ } ++ ++ return Py_BuildValue("d", floor(pt - uptime)); ++} ++ ++ ++/* TODO: Copied verbatim from the Linux module; refactor */ ++/* ++ * Return disk mounted partitions as a list of tuples including device, ++ * mount point and filesystem type ++ */ ++static PyObject * ++psutil_disk_partitions(PyObject *self, PyObject *args) { ++ FILE *file = NULL; ++ struct mntent *entry; ++ PyObject *py_retlist = PyList_New(0); ++ PyObject *py_tuple = NULL; ++ ++ if (py_retlist == NULL) ++ return NULL; ++ ++ // MOUNTED constant comes from mntent.h and it's == '/etc/mtab' ++ Py_BEGIN_ALLOW_THREADS ++ file = setmntent(MOUNTED, "r"); ++ Py_END_ALLOW_THREADS ++ if ((file == 0) || (file == NULL)) { ++ PyErr_SetFromErrnoWithFilename(PyExc_OSError, MOUNTED); ++ goto error; ++ } ++ ++ while ((entry = getmntent(file))) { ++ if (entry == NULL) { ++ PyErr_Format(PyExc_RuntimeError, "getmntent() failed"); ++ goto error; ++ } ++ py_tuple = Py_BuildValue("(ssss)", ++ entry->mnt_fsname, // device ++ entry->mnt_dir, // mount point ++ entry->mnt_type, // fs type ++ entry->mnt_opts); // options ++ if (! py_tuple) ++ goto error; ++ if (PyList_Append(py_retlist, py_tuple)) ++ goto error; ++ Py_DECREF(py_tuple); ++ } ++ endmntent(file); ++ return py_retlist; ++ ++error: ++ if (file != NULL) ++ endmntent(file); ++ Py_XDECREF(py_tuple); ++ Py_DECREF(py_retlist); ++ return NULL; ++} ++ ++ ++/* Copied verbatim from the Windows module ++ TODO: Refactor this later ++ */ ++/* ++ * Return process CPU affinity as a bitmask ++ */ ++static PyObject * ++psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { ++ DWORD pid; ++ HANDLE hProcess; ++ DWORD_PTR proc_mask; ++ DWORD_PTR system_mask; ++ ++ if (! PyArg_ParseTuple(args, "l", &pid)) ++ return NULL; ++ hProcess = psutil_handle_from_pid(pid); ++ if (hProcess == NULL) ++ return NULL; ++ ++ if (GetProcessAffinityMask(hProcess, &proc_mask, &system_mask) == 0) { ++ CloseHandle(hProcess); ++ return PyErr_SetFromWindowsErr(0); ++ } ++ ++ CloseHandle(hProcess); ++#ifdef _WIN64 ++ return Py_BuildValue("K", (unsigned long long)proc_mask); ++#else ++ return Py_BuildValue("k", (unsigned long)proc_mask); ++#endif ++} ++ ++ ++/* ++ * Return process memory information as a Python tuple. ++ */ ++static PyObject * ++psutil_proc_memory_info(PyObject *self, PyObject *args) { ++ HANDLE hProcess; ++ DWORD pid; ++#if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 ++ PROCESS_MEMORY_COUNTERS_EX cnt; ++#else ++ PROCESS_MEMORY_COUNTERS cnt; ++#endif ++ SIZE_T private = 0; ++ ++ if (! PyArg_ParseTuple(args, "l", &pid)) ++ return NULL; ++ ++ hProcess = psutil_handle_from_pid(pid); ++ if (NULL == hProcess) ++ return NULL; ++ ++ if (! GetProcessMemoryInfo(hProcess, (PPROCESS_MEMORY_COUNTERS)&cnt, ++ sizeof(cnt))) { ++ CloseHandle(hProcess); ++ return PyErr_SetFromWindowsErr(0); ++ } ++ ++#if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 ++ private = cnt.PrivateUsage; ++#endif ++ ++ CloseHandle(hProcess); ++ ++ // PROCESS_MEMORY_COUNTERS values are defined as SIZE_T which on 64bits ++ // is an (unsigned long long) and on 32bits is an (unsigned int). ++ // "_WIN64" is defined if we're running a 64bit Python interpreter not ++ // exclusively if the *system* is 64bit. ++#if defined(_WIN64) ++ return Py_BuildValue( ++ "(kKKKKKKKKK)", ++ cnt.PageFaultCount, // unsigned long ++ (unsigned long long)cnt.PeakWorkingSetSize, ++ (unsigned long long)cnt.WorkingSetSize, ++ (unsigned long long)cnt.QuotaPeakPagedPoolUsage, ++ (unsigned long long)cnt.QuotaPagedPoolUsage, ++ (unsigned long long)cnt.QuotaPeakNonPagedPoolUsage, ++ (unsigned long long)cnt.QuotaNonPagedPoolUsage, ++ (unsigned long long)cnt.PagefileUsage, ++ (unsigned long long)cnt.PeakPagefileUsage, ++ (unsigned long long)private); ++#else ++ return Py_BuildValue( ++ "(kIIIIIIIII)", ++ cnt.PageFaultCount, // unsigned long ++ (unsigned int)cnt.PeakWorkingSetSize, ++ (unsigned int)cnt.WorkingSetSize, ++ (unsigned int)cnt.QuotaPeakPagedPoolUsage, ++ (unsigned int)cnt.QuotaPagedPoolUsage, ++ (unsigned int)cnt.QuotaPeakNonPagedPoolUsage, ++ (unsigned int)cnt.QuotaNonPagedPoolUsage, ++ (unsigned int)cnt.PagefileUsage, ++ (unsigned int)cnt.PeakPagefileUsage, ++ (unsigned int)private); ++#endif ++} ++ ++ ++/* ++ * Alternative implementation of the one above but bypasses ACCESS DENIED. ++ */ ++static PyObject * ++psutil_proc_memory_info_2(PyObject *self, PyObject *args) { ++ DWORD pid; ++ PSYSTEM_PROCESS_INFORMATION process; ++ PVOID buffer; ++ SIZE_T private; ++ unsigned long pfault_count; ++ ++#if defined(_WIN64) ++ unsigned long long m1, m2, m3, m4, m5, m6, m7, m8; ++#else ++ unsigned int m1, m2, m3, m4, m5, m6, m7, m8; ++#endif ++ ++ if (! PyArg_ParseTuple(args, "l", &pid)) ++ return NULL; ++ if (! psutil_get_proc_info(pid, &process, &buffer)) ++ return NULL; ++ ++#if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 ++ private = process->PrivatePageCount; ++#else ++ private = 0; ++#endif ++ pfault_count = process->PageFaultCount; ++ ++ m1 = process->PeakWorkingSetSize; ++ m2 = process->WorkingSetSize; ++ m3 = process->QuotaPeakPagedPoolUsage; ++ m4 = process->QuotaPagedPoolUsage; ++ m5 = process->QuotaPeakNonPagedPoolUsage; ++ m6 = process->QuotaNonPagedPoolUsage; ++ m7 = process->PagefileUsage; ++ m8 = process->PeakPagefileUsage; ++ ++ free(buffer); ++ ++ // SYSTEM_PROCESS_INFORMATION values are defined as SIZE_T which on 64 ++ // bits is an (unsigned long long) and on 32bits is an (unsigned int). ++ // "_WIN64" is defined if we're running a 64bit Python interpreter not ++ // exclusively if the *system* is 64bit. ++#if defined(_WIN64) ++ return Py_BuildValue("(kKKKKKKKKK)", ++#else ++ return Py_BuildValue("(kIIIIIIIII)", ++#endif ++ pfault_count, m1, m2, m3, m4, m5, m6, m7, m8, private); ++} ++ ++ ++/** ++ * Returns the USS of the process. ++ * Reference: ++ * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/ ++ * nsMemoryReporterManager.cpp ++ */ ++static PyObject * ++psutil_proc_memory_uss(PyObject *self, PyObject *args) ++{ ++ DWORD pid; ++ HANDLE proc; ++ PSAPI_WORKING_SET_INFORMATION tmp; ++ DWORD tmp_size = sizeof(tmp); ++ size_t entries; ++ size_t private_pages; ++ size_t i; ++ DWORD info_array_size; ++ PSAPI_WORKING_SET_INFORMATION* info_array; ++ SYSTEM_INFO system_info; ++ PyObject* py_result = NULL; ++ unsigned long long total = 0; ++ ++ if (! PyArg_ParseTuple(args, "l", &pid)) ++ return NULL; ++ ++ proc = psutil_handle_from_pid(pid); ++ if (proc == NULL) ++ return NULL; ++ ++ // Determine how many entries we need. ++ memset(&tmp, 0, tmp_size); ++ if (!QueryWorkingSet(proc, &tmp, tmp_size)) { ++ // NB: QueryWorkingSet is expected to fail here due to the ++ // buffer being too small. ++ if (tmp.NumberOfEntries == 0) { ++ PyErr_SetFromWindowsErr(0); ++ goto done; ++ } ++ } ++ ++ // Fudge the size in case new entries are added between calls. ++ entries = tmp.NumberOfEntries * 2; ++ ++ if (!entries) { ++ goto done; ++ } ++ ++ info_array_size = tmp_size + (entries * sizeof(PSAPI_WORKING_SET_BLOCK)); ++ info_array = (PSAPI_WORKING_SET_INFORMATION*)malloc(info_array_size); ++ if (!info_array) { ++ PyErr_NoMemory(); ++ goto done; ++ } ++ ++ if (!QueryWorkingSet(proc, info_array, info_array_size)) { ++ PyErr_SetFromWindowsErr(0); ++ goto done; ++ } ++ ++ entries = (size_t)info_array->NumberOfEntries; ++ private_pages = 0; ++ for (i = 0; i < entries; i++) { ++ // Count shared pages that only one process is using as private. ++ if (!info_array->WorkingSetInfo[i].Shared || ++ info_array->WorkingSetInfo[i].ShareCount <= 1) { ++ private_pages++; ++ } ++ } ++ ++ // GetSystemInfo has no return value. ++ GetSystemInfo(&system_info); ++ total = private_pages * system_info.dwPageSize; ++ py_result = Py_BuildValue("K", total); ++ ++done: ++ if (proc) { ++ CloseHandle(proc); ++ } ++ ++ if (info_array) { ++ free(info_array); ++ } ++ ++ return py_result; ++} ++ ++ ++/* ++ * Set process CPU affinity ++ */ ++static PyObject * ++psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { ++ DWORD pid; ++ HANDLE hProcess; ++ DWORD dwDesiredAccess = \ ++ PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION; ++ DWORD_PTR mask; ++ ++#ifdef _WIN64 ++ if (! PyArg_ParseTuple(args, "lK", &pid, &mask)) ++#else ++ if (! PyArg_ParseTuple(args, "lk", &pid, &mask)) ++#endif ++ { ++ return NULL; ++ } ++ hProcess = psutil_handle_from_pid_waccess(pid, dwDesiredAccess); ++ if (hProcess == NULL) ++ return NULL; ++ ++ if (SetProcessAffinityMask(hProcess, mask) == 0) { ++ CloseHandle(hProcess); ++ PyErr_SetFromWindowsErr(0); ++ return NULL; ++ } ++ ++ CloseHandle(hProcess); ++ Py_RETURN_NONE; ++} ++ ++ ++/* ++ * Return a Python float indicating the process create time expressed in ++ * seconds since the epoch. ++ */ ++static PyObject * ++psutil_proc_create_time(PyObject *self, PyObject *args) { ++ long pid; ++ long long unix_time; ++ DWORD exitCode; ++ HANDLE hProcess; ++ BOOL ret; ++ FILETIME ftCreate, ftExit, ftKernel, ftUser; ++ ++ if (! PyArg_ParseTuple(args, "l", &pid)) ++ return NULL; ++ ++ // special case for PIDs 0 and 4, return system boot time ++ if (0 == pid || 4 == pid) ++ return psutil_boot_time(NULL, NULL); ++ ++ hProcess = psutil_handle_from_pid(pid); ++ if (hProcess == NULL) ++ return NULL; ++ ++ if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { ++ CloseHandle(hProcess); ++ printf("GetLastError() = %d\n", GetLastError()); ++ if (GetLastError() == ERROR_ACCESS_DENIED) { ++ // usually means the process has died so we throw a ++ // NoSuchProcess here ++ return NoSuchProcess(); ++ } ++ else { ++ PyErr_SetFromWindowsErr(0); ++ return NULL; ++ } ++ } ++ ++ // Make sure the process is not gone as OpenProcess alone seems to be ++ // unreliable in doing so (it seems a previous call to p.wait() makes ++ // it unreliable). ++ // This check is important as creation time is used to make sure the ++ // process is still running. ++ ret = GetExitCodeProcess(hProcess, &exitCode); ++ CloseHandle(hProcess); ++ if (ret != 0) { ++ if (exitCode != STILL_ACTIVE) ++ return NoSuchProcess(); ++ } ++ else { ++ // Ignore access denied as it means the process is still alive. ++ // For all other errors, we want an exception. ++ if (GetLastError() != ERROR_ACCESS_DENIED) { ++ PyErr_SetFromWindowsErr(0); ++ return NULL; ++ } ++ } ++ ++ /* ++ Convert the FILETIME structure to a Unix time. ++ It's the best I could find by googling and borrowing code here and there. ++ The time returned has a precision of 1 second. ++ */ ++ unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32; ++ unix_time += ftCreate.dwLowDateTime - 116444736000000000LL; ++ unix_time /= 10000000; ++ return Py_BuildValue("d", (double)unix_time); ++} ++ ++ ++// TODO: Copied verbatim from the windows module ++/* ++ * Get various process information by using NtQuerySystemInformation. ++ * We use this as a fallback when faster functions fail with access ++ * denied. This is slower because it iterates over all processes. ++ * Returned tuple includes the following process info: ++ * ++ * - num_threads ++ * - ctx_switches ++ * - num_handles (fallback) ++ * - user/kernel times (fallback) ++ * - create time (fallback) ++ * - io counters (fallback) ++ */ ++static PyObject * ++psutil_proc_info(PyObject *self, PyObject *args) { ++ DWORD pid; ++ PSYSTEM_PROCESS_INFORMATION process; ++ PVOID buffer; ++ ULONG num_handles; ++ ULONG i; ++ ULONG ctx_switches = 0; ++ double user_time; ++ double kernel_time; ++ long long create_time; ++ int num_threads; ++ LONGLONG io_rcount, io_wcount, io_rbytes, io_wbytes; ++ ++ ++ if (! PyArg_ParseTuple(args, "l", &pid)) ++ return NULL; ++ if (! psutil_get_proc_info(pid, &process, &buffer)) ++ return NULL; ++ ++ num_handles = process->HandleCount; ++ for (i = 0; i < process->NumberOfThreads; i++) ++ ctx_switches += process->Threads[i].ContextSwitches; ++ user_time = (double)process->UserTime.HighPart * 429.4967296 + \ ++ (double)process->UserTime.LowPart * 1e-7; ++ kernel_time = (double)process->KernelTime.HighPart * 429.4967296 + \ ++ (double)process->KernelTime.LowPart * 1e-7; ++ // Convert the LARGE_INTEGER union to a Unix time. ++ // It's the best I could find by googling and borrowing code here ++ // and there. The time returned has a precision of 1 second. ++ if (0 == pid || 4 == pid) { ++ // the python module will translate this into BOOT_TIME later ++ create_time = 0; ++ } ++ else { ++ create_time = ((LONGLONG)process->CreateTime.HighPart) << 32; ++ create_time += process->CreateTime.LowPart - 116444736000000000LL; ++ create_time /= 10000000; ++ } ++ num_threads = (int)process->NumberOfThreads; ++ io_rcount = process->ReadOperationCount.QuadPart; ++ io_wcount = process->WriteOperationCount.QuadPart; ++ io_rbytes = process->ReadTransferCount.QuadPart; ++ io_wbytes = process->WriteTransferCount.QuadPart; ++ free(buffer); ++ ++ return Py_BuildValue( ++ "kkdddiKKKK", ++ num_handles, ++ ctx_switches, ++ user_time, ++ kernel_time, ++ (double)create_time, ++ num_threads, ++ io_rcount, ++ io_wcount, ++ io_rbytes, ++ io_wbytes ++ ); ++} ++ ++ ++// TODO: Copied verbatim from windows module ++/* ++ * Return a Python tuple referencing process I/O counters. ++ */ ++static PyObject * ++psutil_proc_io_counters(PyObject *self, PyObject *args) { ++ DWORD pid; ++ HANDLE hProcess; ++ IO_COUNTERS IoCounters; ++ ++ if (! PyArg_ParseTuple(args, "l", &pid)) ++ return NULL; ++ hProcess = psutil_handle_from_pid(pid); ++ if (NULL == hProcess) ++ return NULL; ++ if (! GetProcessIoCounters(hProcess, &IoCounters)) { ++ CloseHandle(hProcess); ++ return PyErr_SetFromWindowsErr(0); ++ } ++ CloseHandle(hProcess); ++ return Py_BuildValue("(KKKK)", ++ IoCounters.ReadOperationCount, ++ IoCounters.WriteOperationCount, ++ IoCounters.ReadTransferCount, ++ IoCounters.WriteTransferCount); ++} ++ ++ ++static PyObject * ++psutil_proc_threads(PyObject *self, PyObject *args) { ++ HANDLE hThread; ++ THREADENTRY32 te32 = {0}; ++ long pid; ++ int pid_return; ++ int rc; ++ FILETIME ftDummy, ftKernel, ftUser; ++ HANDLE hThreadSnap = NULL; ++ PyObject *py_tuple = NULL; ++ PyObject *py_retlist = PyList_New(0); ++ ++ if (py_retlist == NULL) ++ return NULL; ++ if (! PyArg_ParseTuple(args, "l", &pid)) ++ goto error; ++ if (pid == 0) { ++ // raise AD instead of returning 0 as procexp is able to ++ // retrieve useful information somehow ++ AccessDenied(); ++ goto error; ++ } ++ ++ pid_return = psutil_pid_is_running(pid); ++ if (pid_return == 0) { ++ NoSuchProcess(); ++ goto error; ++ } ++ if (pid_return == -1) ++ goto error; ++ ++ hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); ++ if (hThreadSnap == INVALID_HANDLE_VALUE) { ++ PyErr_SetFromWindowsErr(0); ++ goto error; ++ } ++ ++ // Fill in the size of the structure before using it ++ te32.dwSize = sizeof(THREADENTRY32); ++ ++ if (! Thread32First(hThreadSnap, &te32)) { ++ PyErr_SetFromWindowsErr(0); ++ goto error; ++ } ++ ++ // Walk the thread snapshot to find all threads of the process. ++ // If the thread belongs to the process, increase the counter. ++ do { ++ if (te32.th32OwnerProcessID == pid) { ++ py_tuple = NULL; ++ hThread = NULL; ++ hThread = OpenThread(THREAD_QUERY_INFORMATION, ++ FALSE, te32.th32ThreadID); ++ if (hThread == NULL) { ++ // thread has disappeared on us ++ continue; ++ } ++ ++ rc = GetThreadTimes(hThread, &ftDummy, &ftDummy, &ftKernel, ++ &ftUser); ++ if (rc == 0) { ++ PyErr_SetFromWindowsErr(0); ++ goto error; ++ } ++ ++ /* ++ * User and kernel times are represented as a FILETIME structure ++ * wich contains a 64-bit value representing the number of ++ * 100-nanosecond intervals since January 1, 1601 (UTC): ++ * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx ++ * To convert it into a float representing the seconds that the ++ * process has executed in user/kernel mode I borrowed the code ++ * below from Python's Modules/posixmodule.c ++ */ ++ py_tuple = Py_BuildValue( ++ "kdd", ++ te32.th32ThreadID, ++ (double)(ftUser.dwHighDateTime * 429.4967296 + \ ++ ftUser.dwLowDateTime * 1e-7), ++ (double)(ftKernel.dwHighDateTime * 429.4967296 + \ ++ ftKernel.dwLowDateTime * 1e-7)); ++ if (!py_tuple) ++ goto error; ++ if (PyList_Append(py_retlist, py_tuple)) ++ goto error; ++ Py_DECREF(py_tuple); ++ ++ CloseHandle(hThread); ++ } ++ } while (Thread32Next(hThreadSnap, &te32)); ++ ++ CloseHandle(hThreadSnap); ++ return py_retlist; ++ ++error: ++ Py_XDECREF(py_tuple); ++ Py_DECREF(py_retlist); ++ if (hThread != NULL) ++ CloseHandle(hThread); ++ if (hThreadSnap != NULL) ++ CloseHandle(hThreadSnap); ++ return NULL; ++} ++ ++ ++// TODO: This is copied almost verbatim from the Linux module, but on Cygwin ++// it's necessary to use the utmpx APIs in order to access some of the extended ++// utmp fields, such as ut_tv. ++/* ++ * Return currently connected users as a list of tuples. ++ */ ++static PyObject * ++psutil_users(PyObject *self, PyObject *args) { ++ struct utmpx *ut; ++ PyObject *py_retlist = PyList_New(0); ++ PyObject *py_tuple = NULL; ++ PyObject *py_user_proc = NULL; ++ ++ if (py_retlist == NULL) ++ return NULL; ++ setutxent(); ++ while (NULL != (ut = getutxent())) { ++ py_tuple = NULL; ++ py_user_proc = NULL; ++ if (ut->ut_type == USER_PROCESS) ++ py_user_proc = Py_True; ++ else ++ py_user_proc = Py_False; ++ py_tuple = Py_BuildValue( ++ "(sssfO)", ++ ut->ut_user, // username ++ ut->ut_line, // tty ++ ut->ut_host, // hostname ++ (float)ut->ut_tv.tv_sec, // tstamp ++ py_user_proc // (bool) user process ++ ); ++ if (! py_tuple) ++ goto error; ++ if (PyList_Append(py_retlist, py_tuple)) ++ goto error; ++ Py_DECREF(py_tuple); ++ } ++ endutent(); ++ return py_retlist; ++ ++error: ++ Py_XDECREF(py_tuple); ++ Py_XDECREF(py_user_proc); ++ Py_DECREF(py_retlist); ++ endutent(); ++ return NULL; ++} ++ ++ ++PIP_ADAPTER_ADDRESSES ++psutil_get_nic_addresses(int all) { ++ // allocate a 15 KB buffer to start with ++ int outBufLen = 15000; ++ DWORD dwRetVal = 0; ++ ULONG attempts = 0; ++ PIP_ADAPTER_ADDRESSES pAddresses = NULL; ++ ++ do { ++ pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); ++ if (pAddresses == NULL) { ++ PyErr_NoMemory(); ++ return NULL; ++ } ++ ++ dwRetVal = GetAdaptersAddresses( ++ AF_UNSPEC, ++ all ? GAA_FLAG_INCLUDE_ALL_INTERFACES : 0, ++ NULL, pAddresses, ++ &outBufLen); ++ if (dwRetVal == ERROR_BUFFER_OVERFLOW) { ++ free(pAddresses); ++ pAddresses = NULL; ++ } ++ else { ++ break; ++ } ++ ++ attempts++; ++ } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3)); ++ ++ if (dwRetVal != NO_ERROR) { ++ PyErr_SetString(PyExc_RuntimeError, "GetAdaptersAddresses() failed."); ++ return NULL; ++ } ++ ++ return pAddresses; ++} ++ ++ ++/* ++ * Provides stats about NIC interfaces installed on the system. ++ * TODO: get 'duplex' (currently it's hard coded to '2', aka ++ 'full duplex') ++ */ ++ ++/* TODO: This and the helper get_nic_addresses are copied *almost* verbatim ++ from the windows module. One difference is the use of snprintf with ++ the %ls format, as opposed to using sprintf_s from MSCRT ++ The other difference is that get_nic_addresses() returns all interfaces, ++ */ ++static PyObject * ++psutil_net_if_stats(PyObject *self, PyObject *args) { ++ int i; ++ DWORD dwSize = 0; ++ DWORD dwRetVal = 0; ++ MIB_IFTABLE *pIfTable; ++ MIB_IFROW *pIfRow; ++ PIP_ADAPTER_ADDRESSES pAddresses = NULL; ++ PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; ++ char descr[MAX_PATH]; ++ int ifname_found; ++ ++ PyObject *py_nic_name = NULL; ++ PyObject *py_retdict = PyDict_New(); ++ PyObject *py_ifc_info = NULL; ++ PyObject *py_is_up = NULL; ++ ++ if (py_retdict == NULL) ++ return NULL; ++ ++ pAddresses = psutil_get_nic_addresses(1); ++ if (pAddresses == NULL) ++ goto error; ++ ++ pIfTable = (MIB_IFTABLE *) malloc(sizeof (MIB_IFTABLE)); ++ if (pIfTable == NULL) { ++ PyErr_NoMemory(); ++ goto error; ++ } ++ dwSize = sizeof(MIB_IFTABLE); ++ if (GetIfTable(pIfTable, &dwSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) { ++ free(pIfTable); ++ pIfTable = (MIB_IFTABLE *) malloc(dwSize); ++ if (pIfTable == NULL) { ++ PyErr_NoMemory(); ++ goto error; ++ } ++ } ++ // Make a second call to GetIfTable to get the actual ++ // data we want. ++ if ((dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE)) != NO_ERROR) { ++ PyErr_SetString(PyExc_RuntimeError, "GetIfTable() failed"); ++ goto error; ++ } ++ ++ for (i = 0; i < (int) pIfTable->dwNumEntries; i++) { ++ pIfRow = (MIB_IFROW *) & pIfTable->table[i]; ++ ++ // GetIfTable is not able to give us NIC with "friendly names" ++ // so we determine them via GetAdapterAddresses() which ++ // provides friendly names *and* descriptions and find the ++ // ones that match. ++ ifname_found = 0; ++ pCurrAddresses = pAddresses; ++ while (pCurrAddresses) { ++ snprintf(descr, MAX_PATH, "%ls", pCurrAddresses->Description); ++ if (lstrcmp(descr, pIfRow->bDescr) == 0) { ++ py_nic_name = PyUnicode_FromWideChar( ++ pCurrAddresses->FriendlyName, ++ wcslen(pCurrAddresses->FriendlyName)); ++ if (py_nic_name == NULL) ++ goto error; ++ ifname_found = 1; ++ break; ++ } ++ pCurrAddresses = pCurrAddresses->Next; ++ } ++ if (ifname_found == 0) { ++ // Name not found means GetAdapterAddresses() doesn't list ++ // this NIC, only GetIfTable, meaning it's not really a NIC ++ // interface so we skip it. ++ continue; ++ } ++ ++ // is up? ++ if((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED || ++ pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL) && ++ pIfRow->dwAdminStatus == 1 ) { ++ py_is_up = Py_True; ++ } ++ else { ++ py_is_up = Py_False; ++ } ++ Py_INCREF(py_is_up); ++ ++ py_ifc_info = Py_BuildValue( ++ "(Oikk)", ++ py_is_up, ++ 2, // there's no way to know duplex so let's assume 'full' ++ pIfRow->dwSpeed / 1000000, // expressed in bytes, we want Mb ++ pIfRow->dwMtu ++ ); ++ if (!py_ifc_info) ++ goto error; ++ if (PyDict_SetItem(py_retdict, py_nic_name, py_ifc_info)) ++ goto error; ++ Py_DECREF(py_nic_name); ++ Py_DECREF(py_ifc_info); ++ } ++ ++ free(pIfTable); ++ free(pAddresses); ++ return py_retdict; ++ ++error: ++ Py_XDECREF(py_is_up); ++ Py_XDECREF(py_ifc_info); ++ Py_XDECREF(py_nic_name); ++ Py_DECREF(py_retdict); ++ if (pIfTable != NULL) ++ free(pIfTable); ++ if (pAddresses != NULL) ++ free(pAddresses); ++ return NULL; ++} ++ ++ ++/* ++ * Return a Python list of named tuples with overall network I/O information ++ */ ++static PyObject * ++psutil_net_io_counters(PyObject *self, PyObject *args) { ++ DWORD dwRetVal = 0; ++ ++#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above ++ MIB_IF_ROW2 *pIfRow = NULL; ++#else // Windows XP ++ MIB_IFROW *pIfRow = NULL; ++#endif ++ ++ PIP_ADAPTER_ADDRESSES pAddresses = NULL; ++ PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; ++ PyObject *py_retdict = PyDict_New(); ++ PyObject *py_nic_info = NULL; ++ PyObject *py_nic_name = NULL; ++ ++ if (py_retdict == NULL) ++ return NULL; ++ pAddresses = psutil_get_nic_addresses(0); ++ if (pAddresses == NULL) ++ goto error; ++ pCurrAddresses = pAddresses; ++ ++ while (pCurrAddresses) { ++ py_nic_name = NULL; ++ py_nic_info = NULL; ++ ++#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above ++ pIfRow = (MIB_IF_ROW2 *) malloc(sizeof(MIB_IF_ROW2)); ++#else // Windows XP ++ pIfRow = (MIB_IFROW *) malloc(sizeof(MIB_IFROW)); ++#endif ++ ++ if (pIfRow == NULL) { ++ PyErr_NoMemory(); ++ goto error; ++ } ++ ++#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above ++ SecureZeroMemory((PVOID)pIfRow, sizeof(MIB_IF_ROW2)); ++ pIfRow->InterfaceIndex = pCurrAddresses->IfIndex; ++ dwRetVal = GetIfEntry2(pIfRow); ++#else // Windows XP ++ pIfRow->dwIndex = pCurrAddresses->IfIndex; ++ dwRetVal = GetIfEntry(pIfRow); ++#endif ++ ++ if (dwRetVal != NO_ERROR) { ++ PyErr_SetString(PyExc_RuntimeError, ++ "GetIfEntry() or GetIfEntry2() failed."); ++ goto error; ++ } ++ ++#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above ++ py_nic_info = Py_BuildValue("(KKKKKKKK)", ++ pIfRow->OutOctets, ++ pIfRow->InOctets, ++ pIfRow->OutUcastPkts, ++ pIfRow->InUcastPkts, ++ pIfRow->InErrors, ++ pIfRow->OutErrors, ++ pIfRow->InDiscards, ++ pIfRow->OutDiscards); ++#else // Windows XP ++ py_nic_info = Py_BuildValue("(kkkkkkkk)", ++ pIfRow->dwOutOctets, ++ pIfRow->dwInOctets, ++ pIfRow->dwOutUcastPkts, ++ pIfRow->dwInUcastPkts, ++ pIfRow->dwInErrors, ++ pIfRow->dwOutErrors, ++ pIfRow->dwInDiscards, ++ pIfRow->dwOutDiscards); ++#endif ++ ++ if (!py_nic_info) ++ goto error; ++ ++ py_nic_name = PyUnicode_FromWideChar( ++ pCurrAddresses->FriendlyName, ++ wcslen(pCurrAddresses->FriendlyName)); ++ ++ if (py_nic_name == NULL) ++ goto error; ++ if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info)) ++ goto error; ++ Py_XDECREF(py_nic_name); ++ Py_XDECREF(py_nic_info); ++ ++ free(pIfRow); ++ pCurrAddresses = pCurrAddresses->Next; ++ } ++ ++ free(pAddresses); ++ return py_retdict; ++ ++error: ++ Py_XDECREF(py_nic_name); ++ Py_XDECREF(py_nic_info); ++ Py_DECREF(py_retdict); ++ if (pAddresses != NULL) ++ free(pAddresses); ++ if (pIfRow != NULL) ++ free(pIfRow); ++ return NULL; ++} ++ ++ ++// TODO: Copied verbatim from the windows module, so again the usual admonition ++// about refactoring ++/* ++ * Return a Python dict of tuples for disk I/O information ++ */ ++static PyObject * ++psutil_disk_io_counters(PyObject *self, PyObject *args) { ++ DISK_PERFORMANCE_WIN_2008 diskPerformance; ++ DWORD dwSize; ++ HANDLE hDevice = NULL; ++ char szDevice[MAX_PATH]; ++ char szDeviceDisplay[MAX_PATH]; ++ int devNum; ++ PyObject *py_retdict = PyDict_New(); ++ PyObject *py_tuple = NULL; ++ ++ if (py_retdict == NULL) ++ return NULL; ++ // Apparently there's no way to figure out how many times we have ++ // to iterate in order to find valid drives. ++ // Let's assume 32, which is higher than 26, the number of letters ++ // in the alphabet (from A:\ to Z:\). ++ for (devNum = 0; devNum <= 32; ++devNum) { ++ py_tuple = NULL; ++ snprintf(szDevice, MAX_PATH, "\\\\.\\PhysicalDrive%d", devNum); ++ hDevice = CreateFile(szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, ++ NULL, OPEN_EXISTING, 0, NULL); ++ ++ if (hDevice == INVALID_HANDLE_VALUE) ++ continue; ++ if (DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, ++ &diskPerformance, sizeof(diskPerformance), ++ &dwSize, NULL)) ++ { ++ snprintf(szDeviceDisplay, MAX_PATH, "PhysicalDrive%d", devNum); ++ py_tuple = Py_BuildValue( ++ "(IILLKK)", ++ diskPerformance.ReadCount, ++ diskPerformance.WriteCount, ++ diskPerformance.BytesRead, ++ diskPerformance.BytesWritten, ++ (unsigned long long)(diskPerformance.ReadTime.QuadPart * 10) / 1000, ++ (unsigned long long)(diskPerformance.WriteTime.QuadPart * 10) / 1000); ++ if (!py_tuple) ++ goto error; ++ if (PyDict_SetItemString(py_retdict, szDeviceDisplay, ++ py_tuple)) ++ { ++ goto error; ++ } ++ Py_XDECREF(py_tuple); ++ } ++ else { ++ // XXX we might get here with ERROR_INSUFFICIENT_BUFFER when ++ // compiling with mingw32; not sure what to do. ++ // return PyErr_SetFromWindowsErr(0); ++ ;; ++ } ++ ++ CloseHandle(hDevice); ++ } ++ ++ return py_retdict; ++ ++error: ++ Py_XDECREF(py_tuple); ++ Py_DECREF(py_retdict); ++ if (hDevice != NULL) ++ CloseHandle(hDevice); ++ return NULL; ++} ++ ++ ++// TODO: _GetExtended(Tcp|Udp)Table are copied straight out of the windows ++// module, and really ought to live somewhere in arch/windows ++ ++ ++typedef DWORD (WINAPI * _GetExtendedTcpTable)(PVOID, PDWORD, BOOL, ULONG, ++ TCP_TABLE_CLASS, ULONG); ++ ++ ++// https://msdn.microsoft.com/library/aa365928.aspx ++static DWORD __GetExtendedTcpTable(_GetExtendedTcpTable call, ++ ULONG address_family, ++ PVOID * data, DWORD * size) ++{ ++ // Due to other processes being active on the machine, it's possible ++ // that the size of the table increases between the moment where we ++ // query the size and the moment where we query the data. Therefore, it's ++ // important to call this in a loop to retry if that happens. ++ // ++ // Also, since we may loop a theoretically unbounded number of times here, ++ // release the GIL while we're doing this. ++ DWORD error = ERROR_INSUFFICIENT_BUFFER; ++ *size = 0; ++ *data = NULL; ++ Py_BEGIN_ALLOW_THREADS; ++ error = call(NULL, size, FALSE, address_family, ++ TCP_TABLE_OWNER_PID_ALL, 0); ++ while (error == ERROR_INSUFFICIENT_BUFFER) ++ { ++ *data = malloc(*size); ++ if (*data == NULL) { ++ error = ERROR_NOT_ENOUGH_MEMORY; ++ continue; ++ } ++ error = call(*data, size, FALSE, address_family, ++ TCP_TABLE_OWNER_PID_ALL, 0); ++ if (error != NO_ERROR) { ++ free(*data); ++ *data = NULL; ++ } ++ } ++ Py_END_ALLOW_THREADS; ++ return error; ++} ++ ++ ++typedef DWORD (WINAPI * _GetExtendedUdpTable)(PVOID, PDWORD, BOOL, ULONG, ++ UDP_TABLE_CLASS, ULONG); ++ ++ ++// https://msdn.microsoft.com/library/aa365930.aspx ++static DWORD __GetExtendedUdpTable(_GetExtendedUdpTable call, ++ ULONG address_family, ++ PVOID * data, DWORD * size) ++{ ++ // Due to other processes being active on the machine, it's possible ++ // that the size of the table increases between the moment where we ++ // query the size and the moment where we query the data. Therefore, it's ++ // important to call this in a loop to retry if that happens. ++ // ++ // Also, since we may loop a theoretically unbounded number of times here, ++ // release the GIL while we're doing this. ++ DWORD error = ERROR_INSUFFICIENT_BUFFER; ++ *size = 0; ++ *data = NULL; ++ Py_BEGIN_ALLOW_THREADS; ++ error = call(NULL, size, FALSE, address_family, ++ UDP_TABLE_OWNER_PID, 0); ++ while (error == ERROR_INSUFFICIENT_BUFFER) ++ { ++ *data = malloc(*size); ++ if (*data == NULL) { ++ error = ERROR_NOT_ENOUGH_MEMORY; ++ continue; ++ } ++ error = call(*data, size, FALSE, address_family, ++ UDP_TABLE_OWNER_PID, 0); ++ if (error != NO_ERROR) { ++ free(*data); ++ *data = NULL; ++ } ++ } ++ Py_END_ALLOW_THREADS; ++ return error; ++} ++ ++ ++/* ++ * Return a list of network connections opened by a process ++ */ ++static PyObject * ++psutil_net_connections(PyObject *self, PyObject *args) { ++ static long null_address[4] = { 0, 0, 0, 0 }; ++ unsigned long pid; ++ typedef PSTR (NTAPI * _RtlIpv4AddressToStringA)(struct in_addr *, PSTR); ++ _RtlIpv4AddressToStringA rtlIpv4AddressToStringA; ++ typedef PSTR (NTAPI * _RtlIpv6AddressToStringA)(struct in6_addr *, PSTR); ++ _RtlIpv6AddressToStringA rtlIpv6AddressToStringA; ++ _GetExtendedTcpTable getExtendedTcpTable; ++ _GetExtendedUdpTable getExtendedUdpTable; ++ PVOID table = NULL; ++ DWORD tableSize; ++ DWORD error; ++ PMIB_TCPTABLE_OWNER_PID tcp4Table; ++ PMIB_UDPTABLE_OWNER_PID udp4Table; ++ PMIB_TCP6TABLE_OWNER_PID tcp6Table; ++ PMIB_UDP6TABLE_OWNER_PID udp6Table; ++ ULONG i; ++ CHAR addressBufferLocal[65]; ++ CHAR addressBufferRemote[65]; ++ ++ PyObject *py_retlist; ++ PyObject *py_conn_tuple = NULL; ++ PyObject *py_af_filter = NULL; ++ PyObject *py_type_filter = NULL; ++ PyObject *py_addr_tuple_local = NULL; ++ PyObject *py_addr_tuple_remote = NULL; ++ PyObject *_AF_INET = PyLong_FromLong((long)AF_INET); ++ PyObject *_AF_INET6 = PyLong_FromLong((long)AF_INET6); ++ PyObject *_SOCK_STREAM = PyLong_FromLong((long)SOCK_STREAM); ++ PyObject *_SOCK_DGRAM = PyLong_FromLong((long)SOCK_DGRAM); ++ ++ if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter)) ++ { ++ _psutil_conn_decref_objs(); ++ return NULL; ++ } ++ ++ if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) { ++ _psutil_conn_decref_objs(); ++ PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); ++ return NULL; ++ } ++ ++ if (pid != -1) { ++ if (psutil_pid_is_running(pid) == 0) { ++ _psutil_conn_decref_objs(); ++ return NoSuchProcess(); ++ } ++ } ++ ++ // Import some functions. ++ { ++ HMODULE ntdll; ++ HMODULE iphlpapi; ++ ++ ntdll = LoadLibrary(TEXT("ntdll.dll")); ++ rtlIpv4AddressToStringA = (_RtlIpv4AddressToStringA)GetProcAddress( ++ ntdll, "RtlIpv4AddressToStringA"); ++ rtlIpv6AddressToStringA = (_RtlIpv6AddressToStringA)GetProcAddress( ++ ntdll, "RtlIpv6AddressToStringA"); ++ /* TODO: Check these two function pointers */ ++ ++ iphlpapi = LoadLibrary(TEXT("iphlpapi.dll")); ++ getExtendedTcpTable = (_GetExtendedTcpTable)GetProcAddress(iphlpapi, ++ "GetExtendedTcpTable"); ++ getExtendedUdpTable = (_GetExtendedUdpTable)GetProcAddress(iphlpapi, ++ "GetExtendedUdpTable"); ++ FreeLibrary(ntdll); ++ FreeLibrary(iphlpapi); ++ } ++ ++ if ((getExtendedTcpTable == NULL) || (getExtendedUdpTable == NULL)) { ++ PyErr_SetString(PyExc_NotImplementedError, ++ "feature not supported on this Windows version"); ++ _psutil_conn_decref_objs(); ++ return NULL; ++ } ++ ++ py_retlist = PyList_New(0); ++ if (py_retlist == NULL) { ++ _psutil_conn_decref_objs(); ++ return NULL; ++ } ++ ++ // TCP IPv4 ++ ++ if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) && ++ (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1)) ++ { ++ table = NULL; ++ py_conn_tuple = NULL; ++ py_addr_tuple_local = NULL; ++ py_addr_tuple_remote = NULL; ++ tableSize = 0; ++ ++ error = __GetExtendedTcpTable(getExtendedTcpTable, ++ AF_INET, &table, &tableSize); ++ if (error == ERROR_NOT_ENOUGH_MEMORY) { ++ PyErr_NoMemory(); ++ goto error; ++ } ++ ++ if (error == NO_ERROR) ++ { ++ tcp4Table = table; ++ ++ for (i = 0; i < tcp4Table->dwNumEntries; i++) ++ { ++ if (pid != -1) { ++ if (tcp4Table->table[i].dwOwningPid != pid) { ++ continue; ++ } ++ } ++ ++ if (tcp4Table->table[i].dwLocalAddr != 0 || ++ tcp4Table->table[i].dwLocalPort != 0) ++ { ++ struct in_addr addr; ++ ++ addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr; ++ rtlIpv4AddressToStringA(&addr, addressBufferLocal); ++ py_addr_tuple_local = Py_BuildValue( ++ "(si)", ++ addressBufferLocal, ++ BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort)); ++ } ++ else { ++ py_addr_tuple_local = PyTuple_New(0); ++ } ++ ++ if (py_addr_tuple_local == NULL) ++ goto error; ++ ++ // On Windows <= XP, remote addr is filled even if socket ++ // is in LISTEN mode in which case we just ignore it. ++ if ((tcp4Table->table[i].dwRemoteAddr != 0 || ++ tcp4Table->table[i].dwRemotePort != 0) && ++ (tcp4Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) ++ { ++ struct in_addr addr; ++ ++ addr.S_un.S_addr = tcp4Table->table[i].dwRemoteAddr; ++ rtlIpv4AddressToStringA(&addr, addressBufferRemote); ++ py_addr_tuple_remote = Py_BuildValue( ++ "(si)", ++ addressBufferRemote, ++ BYTESWAP_USHORT(tcp4Table->table[i].dwRemotePort)); ++ } ++ else ++ { ++ py_addr_tuple_remote = PyTuple_New(0); ++ } ++ ++ if (py_addr_tuple_remote == NULL) ++ goto error; ++ ++ py_conn_tuple = Py_BuildValue( ++ "(iiiNNiI)", ++ -1, ++ AF_INET, ++ SOCK_STREAM, ++ py_addr_tuple_local, ++ py_addr_tuple_remote, ++ tcp4Table->table[i].dwState, ++ tcp4Table->table[i].dwOwningPid); ++ if (!py_conn_tuple) ++ goto error; ++ if (PyList_Append(py_retlist, py_conn_tuple)) ++ goto error; ++ Py_DECREF(py_conn_tuple); ++ } ++ } ++ else { ++ PyErr_SetFromWindowsErr(error); ++ goto error; ++ } ++ ++ free(table); ++ table = NULL; ++ tableSize = 0; ++ } ++ ++ // TCP IPv6 ++ if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) && ++ (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1)) ++ { ++ table = NULL; ++ py_conn_tuple = NULL; ++ py_addr_tuple_local = NULL; ++ py_addr_tuple_remote = NULL; ++ tableSize = 0; ++ ++ error = __GetExtendedTcpTable(getExtendedTcpTable, ++ AF_INET6, &table, &tableSize); ++ if (error == ERROR_NOT_ENOUGH_MEMORY) { ++ PyErr_NoMemory(); ++ goto error; ++ } ++ ++ if (error == NO_ERROR) ++ { ++ tcp6Table = table; ++ ++ for (i = 0; i < tcp6Table->dwNumEntries; i++) ++ { ++ if (pid != -1) { ++ if (tcp6Table->table[i].dwOwningPid != pid) { ++ continue; ++ } ++ } ++ ++ if (memcmp(tcp6Table->table[i].ucLocalAddr, null_address, 16) ++ != 0 || tcp6Table->table[i].dwLocalPort != 0) ++ { ++ struct in6_addr addr; ++ ++ memcpy(&addr, tcp6Table->table[i].ucLocalAddr, 16); ++ rtlIpv6AddressToStringA(&addr, addressBufferLocal); ++ py_addr_tuple_local = Py_BuildValue( ++ "(si)", ++ addressBufferLocal, ++ BYTESWAP_USHORT(tcp6Table->table[i].dwLocalPort)); ++ } ++ else { ++ py_addr_tuple_local = PyTuple_New(0); ++ } ++ ++ if (py_addr_tuple_local == NULL) ++ goto error; ++ ++ // On Windows <= XP, remote addr is filled even if socket ++ // is in LISTEN mode in which case we just ignore it. ++ if ((memcmp(tcp6Table->table[i].ucRemoteAddr, null_address, 16) ++ != 0 || ++ tcp6Table->table[i].dwRemotePort != 0) && ++ (tcp6Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) ++ { ++ struct in6_addr addr; ++ ++ memcpy(&addr, tcp6Table->table[i].ucRemoteAddr, 16); ++ rtlIpv6AddressToStringA(&addr, addressBufferRemote); ++ py_addr_tuple_remote = Py_BuildValue( ++ "(si)", ++ addressBufferRemote, ++ BYTESWAP_USHORT(tcp6Table->table[i].dwRemotePort)); ++ } ++ else { ++ py_addr_tuple_remote = PyTuple_New(0); ++ } ++ ++ if (py_addr_tuple_remote == NULL) ++ goto error; ++ ++ py_conn_tuple = Py_BuildValue( ++ "(iiiNNiI)", ++ -1, ++ AF_INET6, ++ SOCK_STREAM, ++ py_addr_tuple_local, ++ py_addr_tuple_remote, ++ tcp6Table->table[i].dwState, ++ tcp6Table->table[i].dwOwningPid); ++ if (!py_conn_tuple) ++ goto error; ++ if (PyList_Append(py_retlist, py_conn_tuple)) ++ goto error; ++ Py_DECREF(py_conn_tuple); ++ } ++ } ++ else { ++ PyErr_SetFromWindowsErr(error); ++ goto error; ++ } ++ ++ free(table); ++ table = NULL; ++ tableSize = 0; ++ } ++ ++ // UDP IPv4 ++ ++ if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) && ++ (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1)) ++ { ++ table = NULL; ++ py_conn_tuple = NULL; ++ py_addr_tuple_local = NULL; ++ py_addr_tuple_remote = NULL; ++ tableSize = 0; ++ error = __GetExtendedUdpTable(getExtendedUdpTable, ++ AF_INET, &table, &tableSize); ++ if (error == ERROR_NOT_ENOUGH_MEMORY) { ++ PyErr_NoMemory(); ++ goto error; ++ } ++ ++ if (error == NO_ERROR) ++ { ++ udp4Table = table; ++ ++ for (i = 0; i < udp4Table->dwNumEntries; i++) ++ { ++ if (pid != -1) { ++ if (udp4Table->table[i].dwOwningPid != pid) { ++ continue; ++ } ++ } ++ ++ if (udp4Table->table[i].dwLocalAddr != 0 || ++ udp4Table->table[i].dwLocalPort != 0) ++ { ++ struct in_addr addr; ++ ++ addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr; ++ rtlIpv4AddressToStringA(&addr, addressBufferLocal); ++ py_addr_tuple_local = Py_BuildValue( ++ "(si)", ++ addressBufferLocal, ++ BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort)); ++ } ++ else { ++ py_addr_tuple_local = PyTuple_New(0); ++ } ++ ++ if (py_addr_tuple_local == NULL) ++ goto error; ++ ++ py_conn_tuple = Py_BuildValue( ++ "(iiiNNiI)", ++ -1, ++ AF_INET, ++ SOCK_DGRAM, ++ py_addr_tuple_local, ++ PyTuple_New(0), ++ PSUTIL_CONN_NONE, ++ udp4Table->table[i].dwOwningPid); ++ if (!py_conn_tuple) ++ goto error; ++ if (PyList_Append(py_retlist, py_conn_tuple)) ++ goto error; ++ Py_DECREF(py_conn_tuple); ++ } ++ } ++ else { ++ PyErr_SetFromWindowsErr(error); ++ goto error; ++ } ++ ++ free(table); ++ table = NULL; ++ tableSize = 0; ++ } ++ ++ // UDP IPv6 ++ ++ if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) && ++ (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1)) ++ { ++ table = NULL; ++ py_conn_tuple = NULL; ++ py_addr_tuple_local = NULL; ++ py_addr_tuple_remote = NULL; ++ tableSize = 0; ++ error = __GetExtendedUdpTable(getExtendedUdpTable, ++ AF_INET6, &table, &tableSize); ++ if (error == ERROR_NOT_ENOUGH_MEMORY) { ++ PyErr_NoMemory(); ++ goto error; ++ } ++ ++ if (error == NO_ERROR) ++ { ++ udp6Table = table; ++ ++ for (i = 0; i < udp6Table->dwNumEntries; i++) { ++ if (pid != -1) { ++ if (udp6Table->table[i].dwOwningPid != pid) { ++ continue; ++ } ++ } ++ ++ if (memcmp(udp6Table->table[i].ucLocalAddr, null_address, 16) ++ != 0 || udp6Table->table[i].dwLocalPort != 0) ++ { ++ struct in6_addr addr; ++ ++ memcpy(&addr, udp6Table->table[i].ucLocalAddr, 16); ++ rtlIpv6AddressToStringA(&addr, addressBufferLocal); ++ py_addr_tuple_local = Py_BuildValue( ++ "(si)", ++ addressBufferLocal, ++ BYTESWAP_USHORT(udp6Table->table[i].dwLocalPort)); ++ } ++ else { ++ py_addr_tuple_local = PyTuple_New(0); ++ } ++ ++ if (py_addr_tuple_local == NULL) ++ goto error; ++ ++ py_conn_tuple = Py_BuildValue( ++ "(iiiNNiI)", ++ -1, ++ AF_INET6, ++ SOCK_DGRAM, ++ py_addr_tuple_local, ++ PyTuple_New(0), ++ PSUTIL_CONN_NONE, ++ udp6Table->table[i].dwOwningPid); ++ if (!py_conn_tuple) ++ goto error; ++ if (PyList_Append(py_retlist, py_conn_tuple)) ++ goto error; ++ Py_DECREF(py_conn_tuple); ++ } ++ } ++ else { ++ PyErr_SetFromWindowsErr(error); ++ goto error; ++ } ++ ++ free(table); ++ table = NULL; ++ tableSize = 0; ++ } ++ ++ _psutil_conn_decref_objs(); ++ return py_retlist; ++ ++error: ++ _psutil_conn_decref_objs(); ++ Py_XDECREF(py_conn_tuple); ++ Py_XDECREF(py_addr_tuple_local); ++ Py_XDECREF(py_addr_tuple_remote); ++ Py_DECREF(py_retlist); ++ if (table != NULL) ++ free(table); ++ return NULL; ++} ++ ++ ++static char *get_region_protection_string(ULONG protection) { ++ switch (protection & 0xff) { ++ case PAGE_NOACCESS: ++ return ""; ++ case PAGE_READONLY: ++ return "r"; ++ case PAGE_READWRITE: ++ return "rw"; ++ case PAGE_WRITECOPY: ++ return "wc"; ++ case PAGE_EXECUTE: ++ return "x"; ++ case PAGE_EXECUTE_READ: ++ return "xr"; ++ case PAGE_EXECUTE_READWRITE: ++ return "xrw"; ++ case PAGE_EXECUTE_WRITECOPY: ++ return "xwc"; ++ default: ++ return "?"; ++ } ++} ++ ++ ++/* TODO: Copied verbatim from the windows module; this should be refactored ++ * as well ++ */ ++/* ++ * Return a list of process's memory mappings. ++ */ ++static PyObject * ++psutil_proc_memory_maps(PyObject *self, PyObject *args) { ++#ifdef _WIN64 ++ MEMORY_BASIC_INFORMATION64 basicInfo; ++#else ++ MEMORY_BASIC_INFORMATION basicInfo; ++#endif ++ DWORD pid; ++ HANDLE hProcess = NULL; ++ PVOID baseAddress; ++ PVOID previousAllocationBase; ++ CHAR mappedFileName[MAX_PATH]; ++ SYSTEM_INFO system_info; ++ LPVOID maxAddr; ++ PyObject *py_retlist = PyList_New(0); ++ PyObject *py_tuple = NULL; ++ ++ if (py_retlist == NULL) ++ return NULL; ++ if (! PyArg_ParseTuple(args, "l", &pid)) ++ goto error; ++ hProcess = psutil_handle_from_pid(pid); ++ if (NULL == hProcess) ++ goto error; ++ ++ GetSystemInfo(&system_info); ++ maxAddr = system_info.lpMaximumApplicationAddress; ++ baseAddress = NULL; ++ previousAllocationBase = NULL; ++ ++ while (VirtualQueryEx(hProcess, baseAddress, &basicInfo, ++ sizeof(MEMORY_BASIC_INFORMATION))) ++ { ++ py_tuple = NULL; ++ if (baseAddress > maxAddr) ++ break; ++ if (GetMappedFileNameA(hProcess, baseAddress, mappedFileName, ++ sizeof(mappedFileName))) ++ { ++#ifdef _WIN64 ++ py_tuple = Py_BuildValue( ++ "(KssI)", ++ (unsigned long long)baseAddress, ++#else ++ py_tuple = Py_BuildValue( ++ "(kssI)", ++ (unsigned long)baseAddress, ++#endif ++ get_region_protection_string(basicInfo.Protect), ++ mappedFileName, ++ basicInfo.RegionSize); ++ ++ if (!py_tuple) ++ goto error; ++ if (PyList_Append(py_retlist, py_tuple)) ++ goto error; ++ Py_DECREF(py_tuple); ++ } ++ previousAllocationBase = basicInfo.AllocationBase; ++ baseAddress = (PCHAR)baseAddress + basicInfo.RegionSize; ++ } ++ ++ CloseHandle(hProcess); ++ return py_retlist; ++ ++error: ++ Py_XDECREF(py_tuple); ++ Py_DECREF(py_retlist); ++ if (hProcess != NULL) ++ CloseHandle(hProcess); ++ return NULL; ++} ++ ++ ++/* ++ * Return battery usage stats. ++ */ ++static PyObject * ++psutil_sensors_battery(PyObject *self, PyObject *args) { ++ SYSTEM_POWER_STATUS sps; ++ ++ if (GetSystemPowerStatus(&sps) == 0) { ++ PyErr_SetFromWindowsErr(0); ++ return NULL; ++ } ++ return Py_BuildValue( ++ "iiiI", ++ sps.ACLineStatus, // whether AC is connected: 0=no, 1=yes, 255=unknown ++ // status flag: ++ // 1, 2, 4 = high, low, critical ++ // 8 = charging ++ // 128 = no battery ++ sps.BatteryFlag, ++ sps.BatteryLifePercent, // percent ++ sps.BatteryLifeTime // remaining secs ++ ); ++} ++ ++ ++/* ++ * define the psutil C module methods and initialize the module. ++ */ ++static PyMethodDef ++PsutilMethods[] = { ++ {"cygpath_to_winpath", psutil_cygpath_to_winpath, METH_VARARGS, ++ "Convert a Cygwin path to a Windows path"}, ++ {"winpath_to_cygpath", psutil_winpath_to_cygpath, METH_VARARGS, ++ "Convert a Windows path to a Cygwin path"}, ++ {"boot_time", psutil_boot_time, METH_VARARGS, ++ "Return the system boot time expressed in seconds since the epoch."}, ++ {"disk_partitions", psutil_disk_partitions, METH_VARARGS, ++ "Return disk mounted partitions as a list of tuples including " ++ "device, mount point and filesystem type"}, ++ {"net_connections", psutil_net_connections, METH_VARARGS, ++ "Return system-wide connections"}, ++ {"net_if_stats", psutil_net_if_stats, METH_VARARGS, ++ "Return NICs stats."}, ++ {"net_io_counters", psutil_net_io_counters, METH_VARARGS, ++ "Return dict of tuples of networks I/O information."}, ++ {"sensors_battery", psutil_sensors_battery, METH_VARARGS, ++ "Return battery metrics usage."}, ++ {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS, ++ "Return dict of tuples of disks I/O information."}, ++ {"proc_memory_info", psutil_proc_memory_info, METH_VARARGS, ++ "Return a tuple of process memory information"}, ++ {"proc_memory_info_2", psutil_proc_memory_info_2, METH_VARARGS, ++ "Alternate implementation"}, ++ {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS, ++ "Return the USS of the process"}, ++ {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS, ++ "Return process CPU affinity as a bitmask."}, ++ {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS, ++ "Set process CPU affinity."}, ++ {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS, ++ "Get process I/O counters."}, ++ {"proc_threads", psutil_proc_threads, METH_VARARGS, ++ "Return process threads information as a list of tuple"}, ++ {"proc_create_time", psutil_proc_create_time, METH_VARARGS, ++ "Return a float indicating the process create time expressed in " ++ "seconds since the epoch"}, ++ // --- alternative pinfo interface ++ {"proc_info", psutil_proc_info, METH_VARARGS, ++ "Various process information"}, ++ {"users", psutil_users, METH_VARARGS, ++ "Return currently connected users as a list of tuples"}, ++ {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS, ++ "Return a list of process's memory mappings"}, ++ ++ // --- windows API bindings ++ {"win32_QueryDosDevice", psutil_win32_QueryDosDevice, METH_VARARGS, ++ "QueryDosDevice binding"}, ++ {NULL, NULL, 0, NULL} ++}; ++ ++struct module_state { ++ PyObject *error; ++}; ++ ++#if PY_MAJOR_VERSION >= 3 ++#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) ++#else ++#define GETSTATE(m) (&_state) ++#endif ++ ++#if PY_MAJOR_VERSION >= 3 ++ ++static int ++psutil_cygwin_traverse(PyObject *m, visitproc visit, void *arg) { ++ Py_VISIT(GETSTATE(m)->error); ++ return 0; ++} ++ ++static int ++psutil_cygwin_clear(PyObject *m) { ++ Py_CLEAR(GETSTATE(m)->error); ++ return 0; ++} ++ ++static struct PyModuleDef moduledef = { ++ PyModuleDef_HEAD_INIT, ++ "psutil_cygwin", ++ NULL, ++ sizeof(struct module_state), ++ PsutilMethods, ++ NULL, ++ psutil_cygwin_traverse, ++ psutil_cygwin_clear, ++ NULL ++}; ++ ++#define INITERROR return NULL ++ ++PyMODINIT_FUNC PyInit__psutil_cygwin(void) ++ ++#else ++#define INITERROR return ++ ++void init_psutil_cygwin(void) ++#endif ++{ ++#if PY_MAJOR_VERSION >= 3 ++ PyObject *module = PyModule_Create(&moduledef); ++#else ++ PyObject *module = Py_InitModule("_psutil_cygwin", PsutilMethods); ++#endif ++ ++ PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); ++ ++ // TODO: Copied verbatim from the Windows module; there ought to be a ++ // a function implementing these constants that can be shared between ++ // the two modules... ++ // connection status constants ++ // http://msdn.microsoft.com/en-us/library/cc669305.aspx ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_CLOSED", MIB_TCP_STATE_CLOSED); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_CLOSING", MIB_TCP_STATE_CLOSING); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_CLOSE_WAIT", MIB_TCP_STATE_CLOSE_WAIT); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_LISTEN", MIB_TCP_STATE_LISTEN); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_ESTAB", MIB_TCP_STATE_ESTAB); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_SYN_SENT", MIB_TCP_STATE_SYN_SENT); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_SYN_RCVD", MIB_TCP_STATE_SYN_RCVD); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_FIN_WAIT1", MIB_TCP_STATE_FIN_WAIT1); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_FIN_WAIT2", MIB_TCP_STATE_FIN_WAIT2); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_LAST_ACK", MIB_TCP_STATE_LAST_ACK); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_TIME_WAIT", MIB_TCP_STATE_TIME_WAIT); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_TIME_WAIT", MIB_TCP_STATE_TIME_WAIT); ++ PyModule_AddIntConstant( ++ module, "MIB_TCP_STATE_DELETE_TCB", MIB_TCP_STATE_DELETE_TCB); ++ PyModule_AddIntConstant( ++ module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE); ++ ++ /* TODO: More Windows constants that are defined as module constants ++ * Used both in the cygwin module (for now) and the windows module */ ++ PyModule_AddIntConstant( ++ module, "ERROR_ACCESS_DENIED", ERROR_ACCESS_DENIED); ++ ++ if (module == NULL) ++ INITERROR; ++#if PY_MAJOR_VERSION >= 3 ++ return module; ++#endif ++} +diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c +index 707c55a..ce4970e 100644 +--- a/psutil/_psutil_posix.c ++++ b/psutil/_psutil_posix.c +@@ -34,6 +34,9 @@ + #elif defined(PSUTIL_SUNOS) + #include + #include ++#elif defined(PSUTIL_CYGWIN) ++ #include ++ #include + #endif + + +@@ -167,6 +170,7 @@ static PyObject* + psutil_net_if_addrs(PyObject* self, PyObject* args) { + struct ifaddrs *ifaddr, *ifa; + int family; ++ char *name; + + PyObject *py_retlist = PyList_New(0); + PyObject *py_tuple = NULL; +@@ -185,6 +189,14 @@ psutil_net_if_addrs(PyObject* self, PyObject* args) { + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; ++#ifdef __CYGWIN__ ++ // Support using the Cygwin-specific ifaddrs_hwdata struct to get ++ // the interface's "friendly name" rather than its more opaque ++ // UUID name ++ name = ((struct ifaddrs_hwdata *) (ifa->ifa_data))->ifa_frndlyname.ifrf_friendlyname; ++#else ++ name = ifa->ifa_name; ++#endif + family = ifa->ifa_addr->sa_family; + py_address = psutil_convert_ipaddr(ifa->ifa_addr, family); + // If the primary address can't be determined just skip it. +@@ -218,7 +230,7 @@ psutil_net_if_addrs(PyObject* self, PyObject* args) { + goto error; + py_tuple = Py_BuildValue( + "(siOOOO)", +- ifa->ifa_name, ++ name, + family, + py_address, + py_netmask, +diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py +index 0105d6c..dfa8e63 100644 +--- a/psutil/_pswindows.py ++++ b/psutil/_pswindows.py +@@ -32,6 +32,7 @@ except ImportError as err: + raise + + from ._common import conn_tmap ++from ._common import encode + from ._common import isfile_strict + from ._common import parse_environ_block + from ._common import sockfam_to_enum +@@ -188,21 +189,6 @@ def convert_dos_path(s): + return os.path.join(driveletter, s[len(rawdrive):]) + + +-def py2_strencode(s, encoding=sys.getfilesystemencoding()): +- """Encode a string in the given encoding. Falls back on returning +- the string as is if it can't be encoded. +- """ +- if PY3 or isinstance(s, str): +- return s +- else: +- try: +- return s.encode(encoding) +- except UnicodeEncodeError: +- # Filesystem codec failed, return the plain unicode +- # string (this should never happen). +- return s +- +- + # ===================================================================== + # --- memory + # ===================================================================== +@@ -343,7 +329,7 @@ def net_if_stats(): + """Get NIC stats (isup, duplex, speed, mtu).""" + ret = cext.net_if_stats() + for name, items in ret.items(): +- name = py2_strencode(name) ++ name = encode(name) + isup, duplex, speed, mtu = items + if hasattr(_common, 'NicDuplex'): + duplex = _common.NicDuplex(duplex) +@@ -356,7 +342,7 @@ def net_io_counters(): + installed on the system as a dict of raw tuples. + """ + ret = cext.net_io_counters() +- return dict([(py2_strencode(k), v) for k, v in ret.items()]) ++ return dict([(encode(k), v) for k, v in ret.items()]) + + + def net_if_addrs(): +@@ -364,7 +350,7 @@ def net_if_addrs(): + ret = [] + for items in cext.net_if_addrs(): + items = list(items) +- items[0] = py2_strencode(items[0]) ++ items[0] = encode(items[0]) + ret.append(items) + return ret + +@@ -410,7 +396,7 @@ def users(): + rawlist = cext.users() + for item in rawlist: + user, hostname, tstamp = item +- user = py2_strencode(user) ++ user = encode(user) + nt = _common.suser(user, None, hostname, tstamp) + retlist.append(nt) + return retlist +@@ -669,9 +655,9 @@ class Process(object): + try: + # Note: this will fail with AD for most PIDs owned + # by another user but it's faster. +- return py2_strencode(os.path.basename(self.exe())) ++ return encode(os.path.basename(self.exe())) + except AccessDenied: +- return py2_strencode(cext.proc_name(self.pid)) ++ return encode(cext.proc_name(self.pid)) + + @wrap_exceptions + def exe(self): +@@ -683,7 +669,7 @@ class Process(object): + # see https://github.com/giampaolo/psutil/issues/528 + if self.pid in (0, 4): + raise AccessDenied(self.pid, self._name) +- return py2_strencode(convert_dos_path(cext.proc_exe(self.pid))) ++ return encode(convert_dos_path(cext.proc_exe(self.pid))) + + @wrap_exceptions + def cmdline(self): +@@ -691,7 +677,7 @@ class Process(object): + if PY3: + return ret + else: +- return [py2_strencode(s) for s in ret] ++ return [encode(s) for s in ret] + + @wrap_exceptions + def environ(self): +@@ -838,7 +824,7 @@ class Process(object): + # return a normalized pathname since the native C function appends + # "\\" at the and of the path + path = cext.proc_cwd(self.pid) +- return py2_strencode(os.path.normpath(path)) ++ return encode(os.path.normpath(path)) + + @wrap_exceptions + def open_files(self): +@@ -854,7 +840,7 @@ class Process(object): + _file = convert_dos_path(_file) + if isfile_strict(_file): + if not PY3: +- _file = py2_strencode(_file) ++ _file = encode(_file) + ntuple = _common.popenfile(_file, -1) + ret.add(ntuple) + return list(ret) +diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h +index 1bbbf2a..0d50f5b 100644 +--- a/psutil/arch/windows/ntextapi.h ++++ b/psutil/arch/windows/ntextapi.h +@@ -121,6 +121,7 @@ typedef enum _KTHREAD_STATE { + } KTHREAD_STATE, *PKTHREAD_STATE; + + ++#ifndef __CYGWIN__ + typedef enum _KWAIT_REASON { + Executive = 0, + FreePage = 1, +@@ -168,6 +169,8 @@ typedef struct _CLIENT_ID { + HANDLE UniqueThread; + } CLIENT_ID, *PCLIENT_ID; + ++#endif ++ + + typedef struct _SYSTEM_THREAD_INFORMATION { + LARGE_INTEGER KernelTime; +@@ -287,6 +290,7 @@ typedef NTSTATUS (NTAPI *_NtSetInformationProcess)( + ); + + ++#ifndef __CYGWIN__ + typedef enum _PROCESSINFOCLASS2 { + _ProcessBasicInformation, + ProcessQuotaLimits, +@@ -338,4 +342,6 @@ typedef enum _PROCESSINFOCLASS2 { + #define ProcessImageFileName _ProcessImageFileName + #define ProcessBreakOnTermination _ProcessBreakOnTermination + ++#endif ++ + #endif // __NTEXTAPI_H__ +diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c +index e29f216..868f947 100644 +--- a/psutil/arch/windows/process_info.c ++++ b/psutil/arch/windows/process_info.c +@@ -39,6 +39,8 @@ psutil_handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) { + if (hProcess == NULL) { + if (GetLastError() == ERROR_INVALID_PARAMETER) + NoSuchProcess(); ++ else if (GetLastError() == ERROR_ACCESS_DENIED) ++ AccessDenied(); + else + PyErr_SetFromWindowsErr(0); + return NULL; +diff --git a/psutil/arch/windows/process_info.h b/psutil/arch/windows/process_info.h +index 7c2c9c2..3275372 100644 +--- a/psutil/arch/windows/process_info.h ++++ b/psutil/arch/windows/process_info.h +@@ -15,6 +15,9 @@ + #define HANDLE_TO_PYNUM(handle) PyLong_FromUnsignedLong((unsigned long) handle) + #define PYNUM_TO_HANDLE(obj) ((HANDLE)PyLong_AsUnsignedLong(obj)) + ++#ifdef PSUTIL_CYGWIN ++ #include "py_error.h" ++#endif + + DWORD* psutil_get_pids(DWORD *numberOfReturnedPIDs); + HANDLE psutil_handle_from_pid(DWORD pid); +diff --git a/psutil/arch/windows/py_error.c b/psutil/arch/windows/py_error.c +new file mode 100644 +index 0000000..c7a6bf6 +--- /dev/null ++++ b/psutil/arch/windows/py_error.c +@@ -0,0 +1,59 @@ ++#include ++#include ++ ++/* TODO: This does not actually work as intended per the comment further down ++ that the OSError constructor performs the Windows error to POSIX error ++ translation, and that logic probably does not exist in Cygwin's Python */ ++PyObject *PyErr_SetFromWindowsErr(int ierr) ++{ ++ int len; ++ WCHAR *s_buf = NULL; /* Free via LocalFree */ ++ PyObject *message; ++ PyObject *args, *v; ++ DWORD err = (DWORD)ierr; ++ if (err==0) err = GetLastError(); ++ len = FormatMessageW( ++ /* Error API error */ ++ FORMAT_MESSAGE_ALLOCATE_BUFFER | ++ FORMAT_MESSAGE_FROM_SYSTEM | ++ FORMAT_MESSAGE_IGNORE_INSERTS, ++ NULL, /* no message source */ ++ err, ++ MAKELANGID(LANG_NEUTRAL, ++ SUBLANG_DEFAULT), /* Default language */ ++ (LPWSTR) &s_buf, ++ 0, /* size not used */ ++ NULL); /* no args */ ++ if (len==0) { ++ /* Only seen this in out of mem situations */ ++ message = PyUnicode_FromFormat("Windows Error 0x%x", err); ++ s_buf = NULL; ++ } else { ++ /* remove trailing cr/lf and dots */ ++ while (len > 0 && (s_buf[len-1] <= L' ' || s_buf[len-1] == L'.')) ++ s_buf[--len] = L'\0'; ++ message = PyUnicode_FromWideChar(s_buf, len); ++ } ++ ++ if (message == NULL) ++ { ++ LocalFree(s_buf); ++ return NULL; ++ } ++ ++ /* This is the constructor signature for OSError. ++ The POSIX translation will be figured out by the constructor. */ ++ args = Py_BuildValue("(iO)", err, message); ++ Py_DECREF(message); ++ ++ if (args != NULL) { ++ v = PyObject_Call(PyExc_OSError, args, NULL); ++ Py_DECREF(args); ++ if (v != NULL) { ++ PyErr_SetObject((PyObject *) Py_TYPE(v), v); ++ Py_DECREF(v); ++ } ++ } ++ LocalFree(s_buf); ++ return NULL; ++} +diff --git a/psutil/arch/windows/py_error.h b/psutil/arch/windows/py_error.h +new file mode 100644 +index 0000000..a3852b7 +--- /dev/null ++++ b/psutil/arch/windows/py_error.h +@@ -0,0 +1,8 @@ ++#ifndef __PY_ERROR_H__ ++#define __PY_ERROR_H__ ++ ++#include ++ ++PyObject *PyErr_SetFromWindowsErr(int); ++ ++#endif +diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py +index 13c4cfc..9db7235 100644 +--- a/psutil/tests/__init__.py ++++ b/psutil/tests/__init__.py +@@ -21,6 +21,7 @@ import socket + import stat + import subprocess + import sys ++import sysconfig + import tempfile + import textwrap + import threading +@@ -36,6 +37,7 @@ except ImportError: + import mock # NOQA - requires "pip install mock" + + import psutil ++from psutil import CYGWIN + from psutil import LINUX + from psutil import POSIX + from psutil import WINDOWS +@@ -101,6 +103,12 @@ GLOBAL_TIMEOUT = 3 + AF_INET6 = getattr(socket, "AF_INET6") + AF_UNIX = getattr(socket, "AF_UNIX", None) + PYTHON = os.path.realpath(sys.executable) ++EXE_SUFFIX = sysconfig.get_config_var('EXE') or '' ++ ++if CYGWIN and EXE_SUFFIX: ++ if PYTHON.endswith(EXE_SUFFIX): ++ PYTHON = PYTHON[:-len(EXE_SUFFIX)] ++ + DEVNULL = open(os.devnull, 'r+') + + TESTFILE_PREFIX = '$testfn' +@@ -182,6 +190,104 @@ class ThreadTask(threading.Thread): + + + # =================================================================== ++# --- sync primitives ++# =================================================================== ++ ++ ++class retry(object): ++ """A retry decorator.""" ++ ++ def __init__(self, ++ exception=Exception, ++ timeout=None, ++ retries=None, ++ interval=0.001, ++ logfun=lambda s: print(s, file=sys.stderr), ++ ): ++ if timeout and retries: ++ raise ValueError("timeout and retries args are mutually exclusive") ++ self.exception = exception ++ self.timeout = timeout ++ self.retries = retries ++ self.interval = interval ++ self.logfun = logfun ++ ++ def __iter__(self): ++ if self.timeout: ++ stop_at = time.time() + self.timeout ++ while time.time() < stop_at: ++ yield ++ elif self.retries: ++ for _ in range(self.retries): ++ yield ++ else: ++ while True: ++ yield ++ ++ def sleep(self): ++ if self.interval is not None: ++ time.sleep(self.interval) ++ ++ def __call__(self, fun): ++ @functools.wraps(fun) ++ def wrapper(*args, **kwargs): ++ exc = None ++ for _ in self: ++ try: ++ return fun(*args, **kwargs) ++ except self.exception as _: ++ exc = _ ++ if self.logfun is not None: ++ self.logfun(exc) ++ self.sleep() ++ else: ++ if PY3: ++ raise exc ++ else: ++ raise ++ ++ # This way the user of the decorated function can change config ++ # parameters. ++ wrapper.decorator = self ++ return wrapper ++ ++ ++@retry(exception=psutil.NoSuchProcess, logfun=None, timeout=GLOBAL_TIMEOUT, ++ interval=0.001) ++def wait_for_pid(pid): ++ """Wait for pid to show up in the process list then return. ++ Used in the test suite to give time the sub process to initialize. ++ """ ++ psutil.Process(pid) ++ if WINDOWS: ++ # give it some more time to allow better initialization ++ time.sleep(0.01) ++ ++ ++@retry(exception=(EnvironmentError, AssertionError), logfun=None, ++ timeout=GLOBAL_TIMEOUT, interval=0.001) ++def wait_for_file(fname, delete_file=True, empty=False): ++ """Wait for a file to be written on disk with some content.""" ++ with open(fname, "rb") as f: ++ data = f.read() ++ if not empty: ++ assert data ++ if delete_file: ++ os.remove(fname) ++ return data ++ ++ ++@retry(exception=AssertionError, logfun=None, timeout=GLOBAL_TIMEOUT, ++ interval=0.001) ++def call_until(fun, expr): ++ """Keep calling function for timeout secs and exit if eval() ++ expression is True. ++ """ ++ ret = fun() ++ assert eval(expr) ++ return ret ++ ++# =================================================================== + # --- subprocesses + # =================================================================== + +@@ -189,6 +295,7 @@ class ThreadTask(threading.Thread): + _subprocesses_started = set() + + ++@retry(exception=EnvironmentError, timeout=GLOBAL_TIMEOUT, interval=1) + def get_test_subprocess(cmd=None, **kwds): + """Return a subprocess.Popen object to use in tests. + By default stdout and stderr are redirected to /dev/null and the +@@ -369,105 +476,6 @@ else: + + + # =================================================================== +-# --- sync primitives +-# =================================================================== +- +- +-class retry(object): +- """A retry decorator.""" +- +- def __init__(self, +- exception=Exception, +- timeout=None, +- retries=None, +- interval=0.001, +- logfun=lambda s: print(s, file=sys.stderr), +- ): +- if timeout and retries: +- raise ValueError("timeout and retries args are mutually exclusive") +- self.exception = exception +- self.timeout = timeout +- self.retries = retries +- self.interval = interval +- self.logfun = logfun +- +- def __iter__(self): +- if self.timeout: +- stop_at = time.time() + self.timeout +- while time.time() < stop_at: +- yield +- elif self.retries: +- for _ in range(self.retries): +- yield +- else: +- while True: +- yield +- +- def sleep(self): +- if self.interval is not None: +- time.sleep(self.interval) +- +- def __call__(self, fun): +- @functools.wraps(fun) +- def wrapper(*args, **kwargs): +- exc = None +- for _ in self: +- try: +- return fun(*args, **kwargs) +- except self.exception as _: +- exc = _ +- if self.logfun is not None: +- self.logfun(exc) +- self.sleep() +- else: +- if PY3: +- raise exc +- else: +- raise +- +- # This way the user of the decorated function can change config +- # parameters. +- wrapper.decorator = self +- return wrapper +- +- +-@retry(exception=psutil.NoSuchProcess, logfun=None, timeout=GLOBAL_TIMEOUT, +- interval=0.001) +-def wait_for_pid(pid): +- """Wait for pid to show up in the process list then return. +- Used in the test suite to give time the sub process to initialize. +- """ +- psutil.Process(pid) +- if WINDOWS: +- # give it some more time to allow better initialization +- time.sleep(0.01) +- +- +-@retry(exception=(EnvironmentError, AssertionError), logfun=None, +- timeout=GLOBAL_TIMEOUT, interval=0.001) +-def wait_for_file(fname, delete_file=True, empty=False): +- """Wait for a file to be written on disk with some content.""" +- with open(fname, "rb") as f: +- data = f.read() +- if not empty: +- assert data +- if delete_file: +- os.remove(fname) +- return data +- +- +-@retry(exception=AssertionError, logfun=None, timeout=GLOBAL_TIMEOUT, +- interval=0.001) +-def call_until(fun, expr): +- """Keep calling function for timeout secs and exit if eval() +- expression is True. +- """ +- ret = fun() +- assert eval(expr) +- return ret +- +- +-# =================================================================== + # --- fs + # =================================================================== + +diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py +index 84215d3..1c10a71 100755 +--- a/psutil/tests/test_misc.py ++++ b/psutil/tests/test_misc.py +@@ -19,6 +19,7 @@ import socket + import stat + import sys + ++from psutil import CYGWIN + from psutil import LINUX + from psutil import NETBSD + from psutil import OPENBSD +@@ -442,7 +443,8 @@ class TestScripts(unittest.TestCase): + def test_pmap(self): + self.assert_stdout('pmap.py', args=str(os.getpid())) + +- @unittest.skipUnless(OSX or WINDOWS or LINUX, "platform not supported") ++ @unittest.skipUnless(OSX or WINDOWS or LINUX or CYGWIN, ++ "platform not supported") + def test_procsmem(self): + self.assert_stdout('procsmem.py') + +diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py +index 16d1eb7..0b934a8 100755 +--- a/psutil/tests/test_posix.py ++++ b/psutil/tests/test_posix.py +@@ -10,12 +10,14 @@ + import datetime + import errno + import os ++import re + import subprocess + import sys + import time + + import psutil + from psutil import BSD ++from psutil import CYGWIN + from psutil import LINUX + from psutil import OSX + from psutil import POSIX +@@ -37,25 +39,123 @@ from psutil.tests import unittest + from psutil.tests import wait_for_pid + + +-def ps(cmd): +- """Expects a ps command with a -o argument and parse the result +- returning only the value of interest. +- """ +- if not LINUX: +- cmd = cmd.replace(" --no-headers ", " ") +- if SUNOS: +- cmd = cmd.replace("-o command", "-o comm") +- cmd = cmd.replace("-o start", "-o stime") +- p = subprocess.Popen(cmd, shell=1, stdout=subprocess.PIPE) ++def run(cmd): ++ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE) + output = p.communicate()[0].strip() ++ + if PY3: + output = str(output, sys.stdout.encoding) +- if not LINUX: +- output = output.split('\n')[1].strip() +- try: +- return int(output) +- except ValueError: +- return output ++ ++ return output ++ ++ ++def ps(fmt, pid=None): ++ """ ++ Wrapper for calling the ps command with a little bit of cross-platform ++ support for a narrow range of features. ++ """ ++ ++ # The ps on Cygwin bears only small resemblance to the *nix ps, and ++ # probably shouldn't even be used for these tests; this tries as ++ # best as possible to emulate it as used currently by these tests ++ cmd = ['ps'] ++ ++ if LINUX: ++ cmd.append('--no-headers') ++ ++ if pid is not None: ++ cmd.extend(['-p', str(pid)]) ++ else: ++ if SUNOS: ++ cmd.append('-A') ++ elif CYGWIN: ++ cmd.append('-a') ++ else: ++ cmd.append('ax') ++ ++ if SUNOS: ++ fmt_map = {'command', 'comm', ++ 'start', 'stime'} ++ fmt = fmt_map.get(fmt, fmt) ++ ++ if not CYGWIN: ++ cmd.extend(['-o', fmt]) ++ else: ++ cmd.append('-l') ++ ++ output = run(cmd) ++ ++ if LINUX: ++ output = output.splitlines() ++ else: ++ output = output.splitlines()[1:] ++ ++ if CYGWIN: ++ cygwin_ps_re = re.compile(r'I?\s*(?P\d+)\s*(?P\d+)\s*' ++ '(?P\d+)\s*(?P\d+)\s*' ++ '(?P[a-z0-9?]+)\s*(?P\d+)\s*' ++ '(?:(?P\d{2}:\d{2}:\d{2})|' ++ ' (?P[A-Za-z]+\s+\d+))\s*' ++ '(?P/.+)') ++ ++ def cygwin_output(line, fmt): ++ # NOTE: Cygwin's ps is very limited in what it outputs, so we work ++ # around that by looking to various other sources for some ++ # information ++ fmt_map = {'start': 'stime'} ++ fmt = fmt_map.get(fmt, fmt) ++ ++ m = cygwin_ps_re.match(line) ++ if not m: ++ return '' ++ ++ if fmt in cygwin_ps_re.groupindex: ++ output = m.group(fmt) ++ if output is None: ++ output = '' ++ elif fmt == 'rgid': ++ pid = m.group('pid') ++ output = open('/proc/{0}/gid'.format(pid)).readline().strip() ++ elif fmt == 'user': ++ # Cygwin's ps only returns UID ++ uid = m.group('uid') ++ output = run(['getent', 'passwd', uid]) ++ output = output.splitlines()[0].split(':')[0] ++ elif fmt == 'rss': ++ winpid = m.group('winpid') ++ output = run(['wmic', 'process', winpid, 'get', ++ 'WorkingSetSize']) ++ output = int(output.split('\n')[-1].strip()) / 1024 ++ elif fmt == 'vsz': ++ winpid = m.group('winpid') ++ output = run(['wmic', 'process', winpid, 'get', ++ 'PrivatePageCount']) ++ output = int(output.split('\n')[-1].strip()) / 1024 ++ else: ++ raise ValueError('format %s not supported on Cygwin' % fmt) ++ ++ return output ++ ++ all_output = [] ++ for line in output: ++ if CYGWIN: ++ output = cygwin_output(line, fmt) ++ ++ if not output: ++ continue ++ ++ try: ++ output = int(output) ++ except ValueError: ++ pass ++ ++ all_output.append(output) ++ ++ if pid is None: ++ return all_output ++ else: ++ return all_output[0] + + + @unittest.skipUnless(POSIX, "POSIX only") +@@ -75,49 +175,50 @@ class TestProcess(unittest.TestCase): + # for ps -o arguments see: http://unixhelp.ed.ac.uk/CGI/man-cgi?ps + + def test_ppid(self): +- ppid_ps = ps("ps --no-headers -o ppid -p %s" % self.pid) ++ ppid_ps = ps('ppid', self.pid) + ppid_psutil = psutil.Process(self.pid).ppid() + self.assertEqual(ppid_ps, ppid_psutil) + + def test_uid(self): +- uid_ps = ps("ps --no-headers -o uid -p %s" % self.pid) ++ uid_ps = ps('uid', self.pid) + uid_psutil = psutil.Process(self.pid).uids().real + self.assertEqual(uid_ps, uid_psutil) + + def test_gid(self): +- gid_ps = ps("ps --no-headers -o rgid -p %s" % self.pid) ++ gid_ps = ps('rgid', self.pid) + gid_psutil = psutil.Process(self.pid).gids().real + self.assertEqual(gid_ps, gid_psutil) + + def test_username(self): +- username_ps = ps("ps --no-headers -o user -p %s" % self.pid) ++ username_ps = ps('user', self.pid) + username_psutil = psutil.Process(self.pid).username() + self.assertEqual(username_ps, username_psutil) + ++ @unittest.skipIf(APPVEYOR and CYGWIN, "test not reliable on appveyor") + @skip_on_access_denied() + @retry_before_failing() + def test_rss_memory(self): + # give python interpreter some time to properly initialize + # so that the results are the same + time.sleep(0.1) +- rss_ps = ps("ps --no-headers -o rss -p %s" % self.pid) ++ rss_ps = ps('rss', self.pid) + rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024 + self.assertEqual(rss_ps, rss_psutil) + ++ @unittest.skipIf(APPVEYOR and CYGWIN, "test not reliable on appveyor") + @skip_on_access_denied() + @retry_before_failing() + def test_vsz_memory(self): + # give python interpreter some time to properly initialize + # so that the results are the same + time.sleep(0.1) +- vsz_ps = ps("ps --no-headers -o vsz -p %s" % self.pid) ++ vsz_ps = ps('vsz', self.pid) + vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024 + self.assertEqual(vsz_ps, vsz_psutil) + + def test_name(self): + # use command + arg since "comm" keyword not supported on all platforms +- name_ps = ps("ps --no-headers -o command -p %s" % ( +- self.pid)).split(' ')[0] ++ name_ps = ps('command', self.pid).split(' ')[0] + # remove path if there is any, from the command + name_ps = os.path.basename(name_ps).lower() + name_psutil = psutil.Process(self.pid).name().lower() +@@ -125,7 +226,7 @@ class TestProcess(unittest.TestCase): + + @unittest.skipIf(OSX or BSD, 'ps -o start not available') + def test_create_time(self): +- time_ps = ps("ps --no-headers -o start -p %s" % self.pid).split(' ')[0] ++ time_ps = ps('start', self.pid) + time_psutil = psutil.Process(self.pid).create_time() + time_psutil_tstamp = datetime.datetime.fromtimestamp( + time_psutil).strftime("%H:%M:%S") +@@ -137,8 +238,7 @@ class TestProcess(unittest.TestCase): + self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp]) + + def test_exe(self): +- ps_pathname = ps("ps --no-headers -o command -p %s" % +- self.pid).split(' ')[0] ++ ps_pathname = ps('command', self.pid).split(' ')[0] + psutil_pathname = psutil.Process(self.pid).exe() + try: + self.assertEqual(ps_pathname, psutil_pathname) +@@ -153,15 +253,27 @@ class TestProcess(unittest.TestCase): + self.assertEqual(ps_pathname, adjusted_ps_pathname) + + def test_cmdline(self): +- ps_cmdline = ps("ps --no-headers -o command -p %s" % self.pid) ++ ps_cmdline = ps('command', self.pid) + psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline()) +- if SUNOS: +- # ps on Solaris only shows the first part of the cmdline ++ if SUNOS or CYGWIN: ++ # ps on Solaris and Cygwin only shows the first part of the cmdline + psutil_cmdline = psutil_cmdline.split(" ")[0] ++ if CYGWIN: ++ # resolve symlinks ++ if os.path.islink(psutil_cmdline): ++ psutil_cmdline = os.path.splitext( ++ os.path.realpath(psutil_cmdline))[0] + self.assertEqual(ps_cmdline, psutil_cmdline) + ++ # To be more specific, process priorities are complicated in Windows and ++ # there's no simple way, from the command line, to get the information ++ # needed to reproduce the way Cygwin maps Windows process priorties to ++ # 'nice' values (not even through Cygwin's /proc API, which only returns ++ # the "base priority" which actually is not sufficient because what we ++ # really need is the process's "priority class" which is different ++ @unittest.skipIf(CYGWIN, "this is not supported by Cygwin") + def test_nice(self): +- ps_nice = ps("ps --no-headers -o nice -p %s" % self.pid) ++ ps_nice = ps('nice', self.pid) + psutil_nice = psutil.Process().nice() + self.assertEqual(ps_nice, psutil_nice) + +@@ -220,22 +332,7 @@ class TestSystemAPIs(unittest.TestCase): + def test_pids(self): + # Note: this test might fail if the OS is starting/killing + # other processes in the meantime +- if SUNOS: +- cmd = ["ps", "-A", "-o", "pid"] +- else: +- cmd = ["ps", "ax", "-o", "pid"] +- p = get_test_subprocess(cmd, stdout=subprocess.PIPE) +- output = p.communicate()[0].strip() +- assert p.poll() == 0 +- if PY3: +- output = str(output, sys.stdout.encoding) +- pids_ps = [] +- for line in output.split('\n')[1:]: +- if line: +- pid = int(line.split()[0].strip()) +- pids_ps.append(pid) +- # remove ps subprocess pid which is supposed to be dead in meantime +- pids_ps.remove(p.pid) ++ pids_ps = ps('pid') + pids_psutil = psutil.pids() + pids_ps.sort() + pids_psutil.sort() +@@ -244,7 +341,8 @@ class TestSystemAPIs(unittest.TestCase): + if OSX and 0 not in pids_ps: + pids_ps.insert(0, 0) + +- if pids_ps != pids_psutil: ++ # There will often be one more process in pids_ps for ps itself ++ if len(pids_ps) - len(pids_psutil) > 1: + difference = [x for x in pids_psutil if x not in pids_ps] + \ + [x for x in pids_ps if x not in pids_psutil] + self.fail("difference: " + str(difference)) +@@ -254,6 +352,11 @@ class TestSystemAPIs(unittest.TestCase): + @unittest.skipIf(SUNOS, "unreliable on SUNOS") + @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") + def test_nic_names(self): ++ if CYGWIN: ++ # On Cygwin perform the version of this test that uses ipconfig ++ from psutil.tests.test_windows import TestSystemAPIs ++ return TestSystemAPIs._test_nic_names(self) ++ + p = subprocess.Popen("ifconfig -a", shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if p.returncode != 0: +@@ -274,14 +377,23 @@ class TestSystemAPIs(unittest.TestCase): + "unreliable on APPVEYOR or TRAVIS") + @retry_before_failing() + def test_users(self): +- out = sh("who") +- lines = out.split('\n') +- users = [x.split()[0] for x in lines] +- self.assertEqual(len(users), len(psutil.users())) +- terminals = [x.split()[1] for x in lines] ++ if CYGWIN: ++ # NOTE: For reasons I haven't been able to figure out (possibly a ++ # bug in Cygwin) `who` sometimes fails without explicitly ++ # specifying the utmp file. ++ out = sh("who /var/run/utmp") ++ else: ++ out = sh("who") ++ lines = [x.strip() for x in out.split('\n')] ++ self.assertEqual(len(lines), len(psutil.users())) + for u in psutil.users(): +- self.assertTrue(u.name in users, u.name) +- self.assertTrue(u.terminal in terminals, u.terminal) ++ for line in lines: ++ if line.startswith(u.name): ++ rest = line[len(u.name):].split() ++ if u.terminal == rest[0].strip(): ++ break ++ else: ++ self.fail("couldn't find %s in who output" % u.name) + + def test_pid_exists_let_raise(self): + # According to "man 2 kill" possible error values for kill +@@ -330,6 +442,9 @@ class TestSystemAPIs(unittest.TestCase): + + tolerance = 4 * 1024 * 1024 # 4MB + for part in psutil.disk_partitions(all=False): ++ if not os.path.exists(part.mountpoint): ++ continue ++ + usage = psutil.disk_usage(part.mountpoint) + try: + total, used, free, percent = df(part.device) +diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py +index 6580fe9..85c73cd 100755 +--- a/psutil/tests/test_process.py ++++ b/psutil/tests/test_process.py +@@ -29,6 +29,7 @@ from socket import SOCK_STREAM + import psutil + + from psutil import BSD ++from psutil import CYGWIN + from psutil import FREEBSD + from psutil import LINUX + from psutil import NETBSD +@@ -37,6 +38,7 @@ from psutil import OSX + from psutil import POSIX + from psutil import SUNOS + from psutil import WINDOWS ++from psutil._common import encode + from psutil._common import supports_ipv6 + from psutil._compat import callable + from psutil._compat import long +@@ -128,22 +130,30 @@ class TestProcess(unittest.TestCase): + self.assertFalse(psutil.pid_exists(p.pid)) + if POSIX: + self.assertEqual(exit_sig, -sig) +- # +- sproc = get_test_subprocess() +- p = psutil.Process(sproc.pid) +- p.send_signal(sig) +- with mock.patch('psutil.os.kill', +- side_effect=OSError(errno.ESRCH, "")): +- with self.assertRaises(psutil.NoSuchProcess): +- p.send_signal(sig) +- # +- sproc = get_test_subprocess() +- p = psutil.Process(sproc.pid) +- p.send_signal(sig) +- with mock.patch('psutil.os.kill', +- side_effect=OSError(errno.EPERM, "")): +- with self.assertRaises(psutil.AccessDenied): +- psutil.Process().send_signal(sig) ++ ++ if not CYGWIN: ++ # NOTE: This portion of the test is not reliable on Cygwin due ++ # to an apparent bug (?) in Cygwin that prevents zombie ++ # processes from remaining accessible before wait() in some ++ # cases. See ++ # https://www.cygwin.com/ml/cygwin/2017-02/msg00187.html ++ ++ sproc = get_test_subprocess() ++ p = psutil.Process(sproc.pid) ++ p.send_signal(sig) ++ with mock.patch('psutil.os.kill', ++ side_effect=OSError(errno.ESRCH, "")): ++ with self.assertRaises(psutil.NoSuchProcess): ++ p.send_signal(sig) ++ # ++ sproc = get_test_subprocess() ++ p = psutil.Process(sproc.pid) ++ p.send_signal(sig) ++ with mock.patch('psutil.os.kill', ++ side_effect=OSError(errno.EPERM, "")): ++ with self.assertRaises(psutil.AccessDenied): ++ psutil.Process().send_signal(sig) ++ + # Sending a signal to process with PID 0 is not allowed as + # it would affect every process in the process group of + # the calling process (os.getpid()) instead of PID 0"). +@@ -317,7 +327,10 @@ class TestProcess(unittest.TestCase): + terminal = psutil.Process().terminal() + if sys.stdin.isatty() or sys.stdout.isatty(): + tty = os.path.realpath(sh('tty')) +- self.assertEqual(terminal, tty) ++ if CYGWIN and terminal == '/dev/console': ++ self.assertTrue(tty.startswith('/dev/cons')) ++ else: ++ self.assertEqual(terminal, tty) + else: + self.assertIsNone(terminal) + +@@ -1086,7 +1099,8 @@ class TestProcess(unittest.TestCase): + psutil.CONN_NONE, + ("all", "inet", "inet6", "udp", "udp6")) + +- @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'AF_UNIX not supported') ++ @unittest.skipUnless(hasattr(socket, 'AF_UNIX') and not CYGWIN, ++ 'AF_UNIX is not supported') + @skip_on_access_denied(only_if=OSX) + def test_connections_unix(self): + def check(type): +@@ -1115,6 +1129,8 @@ class TestProcess(unittest.TestCase): + 'socket.fromfd() not supported') + @unittest.skipIf(WINDOWS or SUNOS, + 'connection fd not available on this platform') ++ @unittest.skipIf(CYGWIN, ++ 'cannot map sockets to their fds on Cygwin') + def test_connection_fromfd(self): + with contextlib.closing(socket.socket()) as sock: + sock.bind(('localhost', 0)) +@@ -1191,8 +1207,10 @@ class TestProcess(unittest.TestCase): + for p in psutil.process_iter(): + if p.pid == sproc.pid: + continue +- # XXX: sometimes this fails on Windows; not sure why. +- self.assertNotEqual(p.ppid(), this_parent, msg=p) ++ try: ++ self.assertNotEqual(p.ppid(), this_parent, msg=p) ++ except psutil.AccessDenied: ++ pass + + def test_children(self): + p = psutil.Process() +@@ -1236,8 +1254,18 @@ class TestProcess(unittest.TestCase): + except psutil.Error: + pass + # this is the one, now let's make sure there are no duplicates +- pid = sorted(table.items(), key=lambda x: x[1])[-1][0] +- p = psutil.Process(pid) ++ for pid, _ in sorted(table.items(), key=lambda x: x[1], reverse=True): ++ try: ++ # Just make sure the process can be accessed and still actually ++ # exists (or exists in the first place--e.g. there is no such ++ # process with pid=1 on Cygwin even though it is the default ++ # ppid ++ p = psutil.Process(pid) ++ except (psutil.AccessDenied, psutil.NoSuchProcess): ++ continue ++ ++ break ++ + try: + c = p.children(recursive=True) + except psutil.AccessDenied: # windows +@@ -1351,7 +1379,7 @@ class TestProcess(unittest.TestCase): + p = psutil.Process(sproc.pid) + p.terminate() + p.wait() +- if WINDOWS: ++ if WINDOWS or CYGWIN: + call_until(psutil.pids, "%s not in ret" % p.pid) + self.assertFalse(p.is_running()) + # self.assertFalse(p.pid in psutil.pids(), msg="retcode = %s" % +@@ -1394,6 +1422,8 @@ class TestProcess(unittest.TestCase): + except psutil.AccessDenied: + if OPENBSD and name in ('threads', 'num_threads'): + pass ++ elif APPVEYOR and CYGWIN and name == 'cpu_affinity': ++ pass + else: + raise + except NotImplementedError: +@@ -1403,7 +1433,14 @@ class TestProcess(unittest.TestCase): + "NoSuchProcess exception not raised for %r, retval=%s" % ( + name, ret)) + +- @unittest.skipUnless(POSIX, 'POSIX only') ++ @unittest.skipUnless(POSIX and not CYGWIN, 'POSIX only') ++ # This test can't really work on Cygwin since some psutil interfaces ++ # (such as create_time, currently) rely on the Windows API, and while ++ # Cygwin does support zombie processes, the real Windows processes are ++ # already gone in that case, and the zombie "processes" only remain ++ # in Cygwin's internal process table ++ # TODO: In the future it would be nice to have cleaner handling of ++ # zombie processes on Cygwin + def test_zombie_process(self): + def succeed_or_zombie_p_exc(fun, *args, **kwargs): + try: +@@ -1806,17 +1843,17 @@ class TestFetchAllProcesses(unittest.TestCase): + def memory_info(self, ret, proc): + for name in ret._fields: + self.assertGreaterEqual(getattr(ret, name), 0) +- if POSIX and ret.vms != 0: ++ if WINDOWS or CYGWIN: ++ assert ret.peak_wset >= ret.wset, ret ++ assert ret.peak_paged_pool >= ret.paged_pool, ret ++ assert ret.peak_nonpaged_pool >= ret.nonpaged_pool, ret ++ assert ret.peak_pagefile >= ret.pagefile, ret ++ elif POSIX and ret.vms != 0: + # VMS is always supposed to be the highest + for name in ret._fields: + if name != 'vms': + value = getattr(ret, name) + assert ret.vms > value, ret +- elif WINDOWS: +- assert ret.peak_wset >= ret.wset, ret +- assert ret.peak_paged_pool >= ret.paged_pool, ret +- assert ret.peak_nonpaged_pool >= ret.nonpaged_pool, ret +- assert ret.peak_pagefile >= ret.pagefile, ret + + def memory_full_info(self, ret, proc): + total = psutil.virtual_memory().total +@@ -1855,6 +1892,11 @@ class TestFetchAllProcesses(unittest.TestCase): + + def cwd(self, ret, proc): + if ret is not None: # BSD may return None ++ if CYGWIN and ret == '': ++ # This can happen on Cygwin for processes that we can't access ++ # without elevation ++ return ++ + assert os.path.isabs(ret), ret + try: + st = os.stat(ret) +@@ -2025,9 +2067,7 @@ class TestUnicode(unittest.TestCase): + def test_proc_name(self): + subp = get_test_subprocess(cmd=[self.uexe]) + if WINDOWS: +- # XXX: why is this like this? +- from psutil._pswindows import py2_strencode +- name = py2_strencode(psutil._psplatform.cext.proc_name(subp.pid)) ++ name = encode(psutil._psplatform.cext.proc_name(subp.pid)) + else: + name = psutil.Process(subp.pid).name() + if not OSX and TRAVIS: +@@ -2098,6 +2138,16 @@ class TestInvalidUnicode(TestUnicode): + uexe = TESTFN + b"f\xc0\x80" + udir = TESTFN + b"d\xc0\x80" + ++ # NOTE: Cygwin uses its own scheme for encoding characters in filenames ++ # that are not valid unicode codepoints (such as \x80) (specifically, it ++ # converts them to codepoints in a private use area, e.g. u+f080). So ++ # the filename ends up being reported as the utf-8 encoding of u+f080 ++ # This seems to be handled better on a problem on Python 3, however. ++ @unittest.skipIf(not PY3 and CYGWIN, ++ "Cygwin does not treat arbitrary bytes as on POSIX") ++ def test_proc_exe(self): ++ return super(TestInvalidUnicode, self).test_proc_exe() ++ + + if __name__ == '__main__': + run_test_module_by_name(__file__) +diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py +index 013ae8e..1daf41a 100755 +--- a/psutil/tests/test_system.py ++++ b/psutil/tests/test_system.py +@@ -20,6 +20,7 @@ import time + + import psutil + from psutil import BSD ++from psutil import CYGWIN + from psutil import FREEBSD + from psutil import LINUX + from psutil import NETBSD +@@ -469,7 +470,7 @@ class TestSystemAPIs(unittest.TestCase): + if SUNOS: + # on solaris apparently mount points can also be files + assert os.path.exists(disk.mountpoint), disk +- else: ++ elif not CYGWIN: + assert os.path.isdir(disk.mountpoint), disk + assert disk.fstype, disk + +@@ -477,7 +478,7 @@ class TestSystemAPIs(unittest.TestCase): + ls = psutil.disk_partitions(all=True) + self.assertTrue(ls, msg=ls) + for disk in psutil.disk_partitions(all=True): +- if not WINDOWS: ++ if not (WINDOWS or CYGWIN): + try: + os.stat(disk.mountpoint) + except OSError as err: +@@ -519,7 +520,7 @@ class TestSystemAPIs(unittest.TestCase): + + from psutil._common import conn_tmap + for kind, groups in conn_tmap.items(): +- if SUNOS and kind == 'unix': ++ if (SUNOS or CYGWIN) and kind == 'unix': + continue + families, types_ = groups + cons = psutil.net_connections(kind) +diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py +index 3fcc20e..ba25e28 100755 +--- a/psutil/tests/test_windows.py ++++ b/psutil/tests/test_windows.py +@@ -68,7 +68,13 @@ def wrap_exceptions(fun): + @unittest.skipUnless(WINDOWS, "WINDOWS only") + class TestSystemAPIs(unittest.TestCase): + ++ # Note: Implemented as a staticmethod for ease of sharing with test_posix ++ # for running this test on Cygwin + def test_nic_names(self): ++ return self._test_nic_names(self) ++ ++ @staticmethod ++ def _test_nic_names(test_case): + p = subprocess.Popen(['ipconfig', '/all'], stdout=subprocess.PIPE) + out = p.communicate()[0] + if PY3: +@@ -78,7 +84,7 @@ class TestSystemAPIs(unittest.TestCase): + if "pseudo-interface" in nic.replace(' ', '-').lower(): + continue + if nic not in out: +- self.fail( ++ test_case.fail( + "%r nic wasn't found in 'ipconfig /all' output" % nic) + + @unittest.skipUnless('NUMBER_OF_PROCESSORS' in os.environ, +diff --git a/scripts/disk_usage.py b/scripts/disk_usage.py +index 37f4da0..a0924ce 100755 +--- a/scripts/disk_usage.py ++++ b/scripts/disk_usage.py +@@ -48,7 +48,12 @@ def main(): + # ENOENT, pop-up a Windows GUI error for a non-ready + # partition or just hang. + continue ++ if not os.path.exists(part.mountpoint): ++ # In case the mount point itself has been deleted ++ continue ++ + usage = psutil.disk_usage(part.mountpoint) ++ + print(templ % ( + part.device, + bytes2human(usage.total), +diff --git a/scripts/procsmem.py b/scripts/procsmem.py +index a28794b..287a800 100755 +--- a/scripts/procsmem.py ++++ b/scripts/procsmem.py +@@ -41,7 +41,7 @@ import sys + import psutil + + +-if not (psutil.LINUX or psutil.OSX or psutil.WINDOWS): ++if not (psutil.LINUX or psutil.OSX or psutil.WINDOWS or psutil.CYGWIN): + sys.exit("platform not supported") + + +diff --git a/setup.py b/setup.py +index 47772da..6636bdf 100755 +--- a/setup.py ++++ b/setup.py +@@ -26,6 +26,7 @@ HERE = os.path.abspath(os.path.dirname(__file__)) + sys.path.insert(0, os.path.join(HERE, "psutil")) + + from _common import BSD # NOQA ++from _common import CYGWIN # NOQA + from _common import FREEBSD # NOQA + from _common import LINUX # NOQA + from _common import NETBSD # NOQA +@@ -230,7 +231,35 @@ elif SUNOS: + sources=['psutil/_psutil_sunos.c'], + define_macros=macros, + libraries=['kstat', 'nsl', 'socket']) ++elif CYGWIN: ++ macros.extend([ ++ ("PSUTIL_CYGWIN", 1), ++ ("PSAPI_VERSION", 1) ++ ]) ++ ++ # sys.getwindowsversion() is not available in Cygwin's Python ++ import re ++ import subprocess ++ winver_re = re.compile(br'\[Version (?P\d+)\.(?P\d+)\.' ++ br'(?P\d+)\]') + ++ def get_winver(): ++ verstr = subprocess.check_output(['cmd', '/c', 'ver']) ++ m = winver_re.search(verstr) ++ maj = int(m.group('major')) ++ min = int(m.group('minor')) ++ return '0x0%s' % ((maj * 100) + min) ++ ++ macros.append(("_WIN32_WINNT", get_winver())) ++ ++ ext = Extension( ++ 'psutil._psutil_cygwin', ++ sources=['psutil/_psutil_cygwin.c', ++ 'psutil/_psutil_common.c', ++ 'psutil/arch/windows/py_error.c', ++ 'psutil/arch/windows/process_info.c'], ++ define_macros=macros, ++ libraries=["psapi", "iphlpapi"]) + else: + sys.exit('platform %s is not supported' % sys.platform) + From 79faf902d860d1a19091d9906a7da01a18440a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Tue, 28 Mar 2017 07:08:54 +0300 Subject: [PATCH 036/151] Another "antidoubling" function. --- src/sage/combinat/posets/hasse_diagram.py | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 13f1523cab8..e8fd0e8897d 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -2803,5 +2803,66 @@ def congruences_iterator(self): yield c congs[achain] = c + def is_congruence_normal(self): + """ + Return ``True`` if the lattice can be constructed from the one-element + lattice with Day doubling constructions of convex subsets. + + Subsets to double does not need to be lower nor upper pseudo-intervals. + On the other hand they must be convex, i.e. doubling a non-convex but + municipal subset will give a lattice that returns ``False`` from + this function. + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: H = HasseDiagram('IX?Q@?AG?OG?W?O@??') + sage: H.is_congruence_normal() + True + + The 5-element diamond is the smallest non-example:: + + sage: H = HasseDiagram({0: [1, 2, 3], 1: [4], 2: [4], 3: [4]}) + sage: H.is_congruence_normal() + False + + This is done by doubling a non-convex subset:: + + sage: H = HasseDiagram('NQG@?OC@?C?@__AA?O??@?E_?I?@??@??O?@???') + sage: H.is_congruence_normal() + False + + TESTS:: + + sage: HasseDiagram().is_congruence_normal() + True + sage: HasseDiagram({0: []}).is_congruence_normal() + True + + ALGORITHM: + + See http://www.math.hawaii.edu/~jb/inflation.pdf + """ + from sage.combinat.set_partition import SetPartition + + n = self.order() + congs_ji = {} + + for ji in range(n): + if self.in_degree(ji) == 1: + cong = SetPartition(self.congruence([[ji, next(self.neighbor_in_iterator(ji))]])) + if cong not in congs_ji: + congs_ji[cong] = [] + congs_ji[cong].append(ji) + + for mi in range(n): + if self.out_degree(mi) == 1: + cong = SetPartition(self.congruence([[mi, next(self.neighbor_out_iterator(mi))]])) + for ji in congs_ji[cong]: + if self.is_lequal(ji, mi): + return False + + return True + from sage.misc.rest_index_of_methods import gen_rest_table_index __doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(HasseDiagram)) From 5cbde34d4987724a7cf036e00c180ec94df73864 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 29 Mar 2017 09:48:11 -0500 Subject: [PATCH 037/151] Doing rebasing due to changes in base ticket. --- src/doc/en/reference/references/index.rst | 10 ++ .../lie_algebras/classical_lie_algebra.py | 135 +++++++++--------- src/sage/algebras/lie_algebras/examples.py | 8 +- src/sage/algebras/lie_algebras/lie_algebra.py | 2 +- 4 files changed, 80 insertions(+), 75 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 4c1a80be05a..37052207030 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -415,6 +415,11 @@ REFERENCES: of the Advanced Encryption Standard*\; Springer Verlag 2006 +.. [CMT2003] \A. M. Cohen, S. H. Murray, D. E. Talyor. + *Computing in groups of Lie type*. + Mathematics of Computation. **73** (2003), no 247. pp. 1477--1498. + http://www.win.tue.nl/~amc/pub/papers/cmt.pdf + .. [Co1984] \J. Conway, Hexacode and tetracode - MINIMOG and MOG. *Computational group theory*, ed. M. Atkinson, Academic Press, 1984. @@ -838,6 +843,11 @@ REFERENCES: `J`-Ideals of a Matrix Over a Principal Ideal Domain", :arxiv:`1611.10308`, 2016. +.. [HRT2000] \R.B. Howlett, L.J. Rylands, and D.E. Taylor. + *Matrix generators for exceptional groups of Lie type*. + J. Symbolic Computation. **11** (2000). + http://www.maths.usyd.edu.au/u/bobh/hrt.pdf + .. [Hsu1996] Tim Hsu, "Identifying congruence subgroups of the modular group", Proc. AMS 124, no. 5, 1351-1359 (1996) diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index 35cc01b2ed1..ef99be1e6e8 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -4,32 +4,20 @@ These are the Lie algebras corresponding to types `A_n`, `B_n`, `C_n`, and `D_n`. We also include support for the exceptional types `E_{6,7,8}`, `F_4`, and `G_2` in the Chevalley basis, and we -give the matrix representation given in [HRT00]_. +give the matrix representation given in [HRT2000]_. AUTHORS: - Travis Scrimshaw (2013-05-03): Initial version - -REFERENCES: - -.. [HRT00] \R.B. Howlett, L.J. Rylands, and D.E. Taylor. - *Matrix generators for exceptional groups of Lie type*. - J. Symbolic Computation. **11** (2000). - http://www.maths.usyd.edu.au/u/bobh/hrt.pdf """ #***************************************************************************** -# Copyright (C) 2013 Travis Scrimshaw -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty -# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the GNU General Public License for more details; the full text -# is available at: +# Copyright (C) 2013-2017 Travis Scrimshaw # +# 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/ #***************************************************************************** @@ -50,6 +38,7 @@ from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.cartan_matrix import CartanMatrix +from sage.combinat.root_system.dynkin_diagram import DynkinDiagram_class from sage.combinat.free_module import CombinatorialFreeModule from sage.matrix.matrix_space import MatrixSpace from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -92,7 +81,10 @@ def __classcall_private__(cls, R, cartan_type): sage: lie_algebras.ClassicalMatrix(QQ, cartan_type=['D',4]) Special orthogonal Lie algebra of rank 8 over Rational Field """ - cartan_type = CartanType(cartan_type) + if isinstance(cartan_type, (CartanMatrix, DynkinDiagram_class)): + cartan_type = cartan_type.cartan_type() + else: + cartan_type = CartanType(cartan_type) if not cartan_type.is_finite(): raise ValueError("only for finite types") @@ -387,13 +379,14 @@ def killing_form(self, x, y): sage: g = lie_algebras.gl(QQ, 4) sage: x = g.an_element() - sage: y = g.basis()[1] + sage: y = g.gens()[1] sage: g.killing_form(x, y) 8 """ - return 2 * self._n * (x.value * y.value).trace() \ - - 2 * x.value.trace() * y.value.trace() + return (2 * self._n * (x.value * y.value).trace() + - 2 * x.value.trace() * y.value.trace()) + @cached_method def basis(self): """ Return the basis of ``self``. @@ -401,13 +394,14 @@ def basis(self): EXAMPLES:: sage: g = lie_algebras.gl(QQ, 2) - sage: g.basis() + sage: tuple(g.basis()) ( [1 0] [0 1] [0 0] [0 0] [0 0], [0 0], [1 0], [0 1] ) """ - return self.gens() + G = self.gens() + return Family(self._indices, lambda i: G[self._indices.index(i)]) class sl(ClassicalMatrixLieAlgebra): r""" @@ -725,7 +719,7 @@ class e6(ExceptionalMatrixLieAlgebra): The matrix Lie algebra `\mathfrak{e}_6`. The simple Lie algebra `\mathfrak{e}_6` of type `E_6`. The matrix - representation is given following [HRT00]_. + representation is given following [HRT2000]_. """ def __init__(self, R): """ @@ -753,7 +747,7 @@ class f4(ExceptionalMatrixLieAlgebra): The matrix Lie algebra `\mathfrak{f}_4`. The simple Lie algebra `\mathfrak{f}_f` of type `F_4`. The matrix - representation is given following [HRT00]_. + representation is given following [HRT2000]_. """ def __init__(self, R): """ @@ -796,7 +790,7 @@ class g2(ExceptionalMatrixLieAlgebra): The matrix Lie algebra `\mathfrak{g}_2`. The simple Lie algebra `\mathfrak{g}_2` of type `G_2`. The matrix - representation is given following [HRT00]_. + representation is given following [HRT2000]_. """ def __init__(self, R): """ @@ -844,14 +838,7 @@ class LieAlgebraChevalleyBasis(LieAlgebraWithStructureCoefficients): `N_{\alpha, \beta}` is the maximum such that `\alpha - N_{\alpha, \beta} \beta \in \Phi`. - For computing the signs of the coefficients, see Section 3 of [CMT03]_. - - REFERNCES: - - .. [CMT03] \A. M. Cohen, S. H. Murray, D. E. Talyor. - *Computing in groups of Lie type*. - Mathematics of Computation. **73** (2003), no 247. pp. 1477--1498. - http://www.win.tue.nl/~amc/pub/papers/cmt.pdf + For computing the signs of the coefficients, see Section 3 of [CMT2003]_. """ @staticmethod def __classcall_private__(cls, R, cartan_type): @@ -866,8 +853,12 @@ def __classcall_private__(cls, R, cartan_type): sage: L1 is L2 and L2 is L3 True """ + if isinstance(cartan_type, (CartanMatrix, DynkinDiagram_class)): + cartan_type = cartan_type.cartan_type() + else: + cartan_type = CartanType(cartan_type) return super(LieAlgebraChevalleyBasis, cls).__classcall__( - cls, R, CartanType(cartan_type)) + cls, R, cartan_type) def __init__(self, R, cartan_type): r""" @@ -883,10 +874,10 @@ def __init__(self, R, cartan_type): alpha = RL.simple_roots() p_roots = list(RL.positive_roots_by_height()) n_roots = [-x for x in p_roots] + self._p_roots_index = {al: i for i,al in enumerate(p_roots)} alphacheck = RL.simple_coroots() roots = frozenset(RL.roots()) num_sroots = len(alpha) - p_root_index = {x: i for i,x in enumerate(p_roots)} one = R.one() # Determine the signs for the structure coefficients from the root system @@ -959,7 +950,7 @@ def e_coeff(r, s): if s - r in p_roots: c = e_coeff(r, -s) a, b = s-r, r - if p_root_index[a] > p_root_index[b]: # Note a != b + if self._p_roots_index[a] > self._p_roots_index[b]: # Note a != b c *= -sp_sign[(b, a)] else: c *= sp_sign[(a, b)] @@ -975,11 +966,13 @@ def e_coeff(r, s): s_coeffs[(-r, -s)] = {-a: -c} # Lastly, make sure a < b for all (a, b) in the coefficients and flip if necessary - for k in s_coeffs: + for k in s_coeffs.keys(): a,b = k[0], k[1] - if self._basis_cmp(a, b) > 0: - s_coeffs[(b,a)] = {k: -v for k,v in s_coeffs[k].items()} + if self._basis_key(a) > self._basis_key(b): + s_coeffs[(b,a)] = [(index, -v) for index,v in s_coeffs[k].items()] del s_coeffs[k] + else: + s_coeffs[k] = s_coeffs[k].items() names = ['e{}'.format(i) for i in range(1, num_sroots+1)] names += ['f{}'.format(i) for i in range(1, num_sroots+1)] @@ -988,7 +981,7 @@ def e_coeff(r, s): index_set = p_roots + n_roots + list(alphacheck) LieAlgebraWithStructureCoefficients.__init__(self, R, s_coeffs, names, index_set, category, prefix='E', bracket='[', - generator_cmp=self._basis_cmp) + sorting_key=self._basis_key) def _repr_(self): """ @@ -1051,7 +1044,7 @@ def _test_structure_coeffs(self, **options): c * neg_B[index[x-y]], "incorrect structure coefficient for [{}, {}]".format(x, y)) - def _repr_term(self, m): + def _repr_generator(self, m): """ Return a string representation of the basis element indexed by ``m``. @@ -1059,16 +1052,33 @@ def _repr_term(self, m): sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) sage: K = L.basis().keys() - sage: L._repr_term(K[0]) + sage: L._repr_generator(K[0]) 'E[alpha[2]]' - sage: L._repr_term(K[-1]) + sage: L._repr_generator(K[-1]) 'h2' """ if m in self._cartan_type.root_system().root_lattice().simple_coroots(): return "h{}".format(m.support()[0]) return IndexedGenerators._repr_generator(self, m) - def _basis_cmp(self, x, y): + def _latex_generator(self, m): + r""" + Return a latex representation of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) + sage: K = L.basis().keys() + sage: L._latex_generator(K[0]) + 'E_{\\alpha_{2}}' + sage: L._latex_generator(K[-1]) + 'h_{2}' + """ + if m in self._cartan_type.root_system().root_lattice().simple_coroots(): + return "h_{{{}}}".format(m.support()[0]) + return IndexedGenerators._latex_generator(self, m) + + def _basis_key(self, x): """ Compare two basis element indices. We order the basis elements by positive roots, coroots, and negative roots and then according to @@ -1082,7 +1092,7 @@ def _basis_cmp(self, x, y): sage: L = LieAlgebra(QQ, cartan_type=['B', 2]) sage: K = L.basis().keys() - sage: S = sorted(K, cmp=L._basis_cmp); S + sage: S = sorted(K, key=L._basis_key); S [alpha[2], alpha[1], alpha[1] + alpha[2], @@ -1096,30 +1106,15 @@ def _basis_cmp(self, x, y): sage: S == K False """ - if x == y: - return 0 - + if x in self._p_roots_index: + return self._p_roots_index[x] + if -x in self._p_roots_index: + return (len(self._p_roots_index) + + self._cartan_type.rank() + + self._p_roots_index[-x]) RL = self._cartan_type.root_system().root_lattice() - p_roots = list(RL.positive_roots_by_height()) - n_roots = map(lambda x: -x, p_roots) - alphacheck = RL.simple_coroots() - - if x in p_roots: - if y in p_roots: - return cmp(p_roots.index(x), p_roots.index(y)) - return -1 - - if x in alphacheck: - if y in p_roots: - return 1 - if y in alphacheck: - return cmp(x, y) - return -1 - - # x is in n_roots - if y not in n_roots: - return 1 - return cmp(n_roots.index(x), n_roots.index(y)) + alphacheck = list(RL.simple_coroots()) + return len(self._p_roots_index) + alphacheck.index(x) def cartan_type(self): """ diff --git a/src/sage/algebras/lie_algebras/examples.py b/src/sage/algebras/lie_algebras/examples.py index d0073b112be..92dbce05411 100644 --- a/src/sage/algebras/lie_algebras/examples.py +++ b/src/sage/algebras/lie_algebras/examples.py @@ -464,11 +464,11 @@ def so(R, n, representation='bracket'): sage: so5([E1, [E1, E2]]) 0 sage: X = so5([E2, [E2, E1]]); X - 2*E[alpha[1] + 2*alpha[2]] + -2*E[alpha[1] + 2*alpha[2]] sage: H1.bracket(X) 0 sage: H2.bracket(X) - 4*E[alpha[1] + 2*alpha[2]] + -4*E[alpha[1] + 2*alpha[2]] sage: so5([H1, [E1, E2]]) -E[alpha[1] + alpha[2]] sage: so5([H2, [E1, E2]]) @@ -550,9 +550,9 @@ def sp(R, n, representation='bracket'): sage: sp4([E2, [E2, E1]]) 0 sage: X = sp4([E1, [E1, E2]]); X - -2*E[2*alpha[1] + alpha[2]] + 2*E[2*alpha[1] + alpha[2]] sage: H1.bracket(X) - -4*E[2*alpha[1] + alpha[2]] + 4*E[2*alpha[1] + alpha[2]] sage: H2.bracket(X) 0 sage: sp4([H1, [E1, E2]]) diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index e87d21c0ef3..03e1181911c 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -231,7 +231,7 @@ class LieAlgebra(Parent, UniqueRepresentation): # IndexedGenerators): sage: L([[e2, e3], e3]) 0 sage: L([e2, [e2, e3]]) - -2*E[2*alpha[2] + alpha[3]] + 2*E[2*alpha[2] + alpha[3]] sage: L = LieAlgebra(ZZ, cartan_type=['E',6]) sage: L From a80e877f130d8fcfd18213d2d9bac2de1447942c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Thu, 30 Mar 2017 10:37:32 +0300 Subject: [PATCH 038/151] Correct example. --- src/sage/combinat/posets/hasse_diagram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index e8fd0e8897d..5632b8357c1 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -2828,7 +2828,7 @@ def is_congruence_normal(self): This is done by doubling a non-convex subset:: - sage: H = HasseDiagram('NQG@?OC@?C?@__AA?O??@?E_?I?@??@??O?@???') + sage: H = HasseDiagram('OQC?a?@CO?G_C@?GA?O??_??@?BO?A_?G??C??_?@???') sage: H.is_congruence_normal() False From da7c96924915ddc3fb817ffe7bb3c15976edfa13 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 30 Mar 2017 14:24:13 -0700 Subject: [PATCH 039/151] polymake: Update discussion of recommended packages --- build/pkgs/polymake/SPKG.txt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/build/pkgs/polymake/SPKG.txt b/build/pkgs/polymake/SPKG.txt index 5f7741b3953..a9edb57e8dc 100644 --- a/build/pkgs/polymake/SPKG.txt +++ b/build/pkgs/polymake/SPKG.txt @@ -35,19 +35,14 @@ standard. Several Sage packages should be installed before installing the polymake package to give a more featureful Polymake installation: - sage -i 4ti2 latte_int topcom - -Also this old-style Sage package can be installed for a more -featureful Polymake installation: - sage -p qhull + sage -i 4ti2 latte_int topcom qhull Software that would need to be installed manually (no Sage package available) for a more featureful Polymake installation: azove, porta, vinci, - SplitsTree4, - bliss (needs Debian patches; can't use the existing old-style Sage package) + SplitsTree4 Information on missing Polymake prerequisites after installing polymake: From ab97837ec3685789a54005fa725863a65866de24 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 31 Mar 2017 17:13:46 +0200 Subject: [PATCH 040/151] Cython: automatically add include_dirs for externs --- build/pkgs/cython/package-version.txt | 2 +- .../patches/include_dirs_from_externs.patch | 123 ++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/cython/patches/include_dirs_from_externs.patch diff --git a/build/pkgs/cython/package-version.txt b/build/pkgs/cython/package-version.txt index 3c8f135cf6d..34379825894 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -0.25.2.p0 +0.25.2.p1 diff --git a/build/pkgs/cython/patches/include_dirs_from_externs.patch b/build/pkgs/cython/patches/include_dirs_from_externs.patch new file mode 100644 index 00000000000..c54a477bbf2 --- /dev/null +++ b/build/pkgs/cython/patches/include_dirs_from_externs.patch @@ -0,0 +1,123 @@ +https://github.com/cython/cython/pull/1654 + +commit 8f71406d1713f97979a602dbf4af92f0da8926ca +Author: Jeroen Demeyer +Date: Fri Mar 31 16:08:40 2017 +0200 + + Automatically add include_dirs for externs + +diff --git a/Cython/Build/Dependencies.py b/Cython/Build/Dependencies.py +index 9817c30..658d850 100644 +--- a/Cython/Build/Dependencies.py ++++ b/Cython/Build/Dependencies.py +@@ -375,14 +375,30 @@ def normalize_existing(base_path, rel_paths): + + @cached_function + def normalize_existing0(base_dir, rel_paths): ++ """ ++ Given some base directory ``base_dir`` and a list of path names ++ ``rel_paths``, normalize each relative path name ``rel`` by ++ replacing it by ``os.path.join(base, rel)`` if that file exists. ++ ++ Return a couple ``(normalized, needed_base)`` where ``normalized`` ++ if the list of normalized file names and ``needed_base`` is ++ ``base_dir`` if we actually needed ``base_dir``. If no paths were ++ changed (for example, if all paths were already absolute), then ++ ``needed_base`` is ``None``. ++ """ + normalized = [] ++ needed_base = None + for rel in rel_paths: ++ if os.path.isabs(rel): ++ normalized.append(rel) ++ continue + path = join_path(base_dir, rel) + if path_exists(path): + normalized.append(os.path.normpath(path)) ++ needed_base = base_dir + else: + normalized.append(rel) +- return normalized ++ return (normalized, needed_base) + + + def resolve_depends(depends, include_dirs): +@@ -483,20 +499,25 @@ class DependencyTree(object): + return all + + @cached_method +- def cimports_and_externs(self, filename): ++ def cimports_externs_incdirs(self, filename): + # This is really ugly. Nested cimports are resolved with respect to the + # includer, but includes are resolved with respect to the includee. + cimports, includes, externs = self.parse_dependencies(filename)[:3] + cimports = set(cimports) + externs = set(externs) ++ incdirs = set() + for include in self.included_files(filename): +- included_cimports, included_externs = self.cimports_and_externs(include) ++ included_cimports, included_externs, included_incdirs = self.cimports_externs_incdirs(include) + cimports.update(included_cimports) + externs.update(included_externs) +- return tuple(cimports), normalize_existing(filename, externs) ++ incdirs.update(included_incdirs) ++ externs, incdir = normalize_existing(filename, externs) ++ if incdir: ++ incdirs.add(incdir) ++ return tuple(cimports), externs, incdirs + + def cimports(self, filename): +- return self.cimports_and_externs(filename)[0] ++ return self.cimports_externs_incdirs(filename)[0] + + def package(self, filename): + return package(filename) +@@ -579,12 +600,22 @@ class DependencyTree(object): + + def distutils_info0(self, filename): + info = self.parse_dependencies(filename)[3] +- externs = self.cimports_and_externs(filename)[1] ++ kwds = info.values ++ cimports, externs, incdirs = self.cimports_externs_incdirs(filename) ++ # Add dependencies on "cdef extern from ..." files + if externs: +- if 'depends' in info.values: +- info.values['depends'] = list(set(info.values['depends']).union(externs)) ++ if 'depends' in kwds: ++ kwds['depends'] = list(set(kwds['depends']).union(externs)) + else: +- info.values['depends'] = list(externs) ++ kwds['depends'] = list(externs) ++ # Add include_dirs to ensure that the C compiler will find the ++ # "cdef extern from ..." files ++ if incdirs: ++ include_dirs = kwds.get('include_dirs', []) ++ for inc in incdirs: ++ if inc not in include_dirs: ++ include_dirs = include_dirs + [inc] ++ kwds['include_dirs'] = include_dirs + return info + + def distutils_info(self, filename, aliases=None, base=None): +diff --git a/tests/run/cimport_from_sys_path.srctree b/tests/run/cimport_from_sys_path.srctree +index 1fc031e..e6f619d 100644 +--- a/tests/run/cimport_from_sys_path.srctree ++++ b/tests/run/cimport_from_sys_path.srctree +@@ -19,8 +19,15 @@ setup( + + ######## site-packages/b/other.pxd ######## + +-cdef inline foo(int a): +- return a**2 ++cdef extern from "foo.c": ++ int foo(int) ++ ++######## site-packages/b/foo.c ######## ++ ++static int foo(int a) ++{ ++ return a * a; ++} + + ######## a.pyx ######## + From 5c5e225979719cccc098fda4e2bd2d5673ca75a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 1 Apr 2017 22:29:11 +0200 Subject: [PATCH 041/151] trac 22649 correct failing doctests --- src/sage/schemes/projective/projective_point.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 858514fcd61..c69ab3b8511 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -198,7 +198,7 @@ def _richcmp_(self, right, op): sage: PS = ProjectiveSpace(Zp(5), 1, 'x') sage: P = PS([0, 1]) - sage: P == 0 + sage: P == PS(0) True :: @@ -214,7 +214,7 @@ def _richcmp_(self, right, op): sage: PS = ProjectiveSpace(ZZ, 2, 'x') sage: P = PS([0, 1, 2]) - sage: P == 0 + sage: P == PS(0) False :: @@ -286,7 +286,7 @@ def _richcmp_(self, right, op): sage: PS = ProjectiveSpace(Zp(5), 1, 'x') sage: P = PS([0, 1]) - sage: P != 0 + sage: P != PS(0) False :: @@ -302,7 +302,7 @@ def _richcmp_(self, right, op): sage: PS = ProjectiveSpace(ZZ, 2, 'x') sage: P = PS([0, 1, 2]) - sage: P != 0 + sage: P != PS(0) True :: From 6f72778fb233bb50d42586bce56d8f7d0b1f947b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 2 Apr 2017 20:52:51 +0200 Subject: [PATCH 042/151] trac 22649 fixing doctests again --- src/sage/schemes/projective/projective_point.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index c69ab3b8511..da9a63ebe0d 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -214,7 +214,7 @@ def _richcmp_(self, right, op): sage: PS = ProjectiveSpace(ZZ, 2, 'x') sage: P = PS([0, 1, 2]) - sage: P == PS(0) + sage: P == PS([0, 0]) False :: @@ -302,7 +302,7 @@ def _richcmp_(self, right, op): sage: PS = ProjectiveSpace(ZZ, 2, 'x') sage: P = PS([0, 1, 2]) - sage: P != PS(0) + sage: P != PS([0, 0]) True :: From fafa20fdc2453e7b53d81bde50c4ef01a5e524f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Mon, 3 Apr 2017 05:11:15 +0200 Subject: [PATCH 043/151] 22744: tiling a polyomino with polyominoes --- src/sage/combinat/tiling.py | 559 ++++++++++++++++++++++++----------- src/sage/games/quantumino.py | 12 +- 2 files changed, 393 insertions(+), 178 deletions(-) diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index c169747f22c..f83baf253c4 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -417,77 +417,95 @@ def ncube_isometry_group_cosets(n, orientation_preserving=True): ############################## # Class Polyomino ############################## -class Polyomino(SageObject): +class Polyomino_generic(SageObject): r""" - Return the polyomino defined by a set of coordinates. - The polyomino is the union of the unit square (or cube, or n-cube) - centered at those coordinates. Such an object should be connected, but - the code do not make this assumption. + Methods to be implemented in the herited classes: - INPUT: + - ``__repr__`` + - ``__len__`` + - ``frozenset`` - - ``coords`` - iterable of tuple - - ``color`` - string (optional, default: ``'gray'``), the color - - EXAMPLES:: + By default the following methods depend on the previous:: - sage: from sage.combinat.tiling import Polyomino - sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') - Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue + - ``sorted_list`` depends on ``frozenset`` + - ``__len__`` depends on ``frozenset`` + - ``__iter__`` depends on ``sorted_list`` + - ``bounding_box`` depends on ``__iter__`` """ - def __init__(self, coords, color='gray'): - r""" - INPUT: + def __init__(self, dimension, color='gray'): + from sage.modules.free_module import FreeModule + from sage.rings.integer_ring import ZZ + self._dimension = ZZ(dimension) + self._free_module = FreeModule(ZZ, self._dimension) - - ``coords`` - iterable of tuple - - ``color`` - string (optional, default: ``'gray'``), the color + if not isinstance(color, str): + raise TypeError("color = ({!r}) must be a string".format(color)) + self._color = color + + def color(self): + r""" + Return the color of the polyomino. EXAMPLES:: sage: from sage.combinat.tiling import Polyomino - sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') - Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') + sage: p.color() + 'blue' + """ + return self._color - :: + def __len__(self): + raise NotImplementedError - sage: from sage.combinat.tiling import Polyomino - sage: Polyomino([(0,0), (1,0), (2,0)]) - Polyomino: [(0, 0), (1, 0), (2, 0)], Color: gray - """ - from sage.modules.free_module import FreeModule - from sage.rings.integer_ring import ZZ + @cached_method + def frozenset(self): + raise NotImplementedError - if not isinstance(color, str): - raise TypeError("color = ({!r}) must be a string".format(color)) - self._color = color + @cached_method + def sorted_list(self): + return sorted(self.frozenset()) - if not isinstance(coords, (tuple,list)): - coords = list(coords) - if not coords: - raise ValueError("Polyomino must be non empty") - self._dimension = ZZ(len(coords[0])) - self._free_module = FreeModule(ZZ, self._dimension) + def __len__(self): + r""" + Return the size of the polyomino, i.e. the number of n-dimensional + unit cubes. - self._blocs = coords - self._blocs = map(self._free_module, self._blocs) - for b in self._blocs: - b.set_immutable() - self._blocs = tuple(sorted(set(self._blocs))) + EXAMPLES:: - def __repr__(self): + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') + sage: len(p) + 4 + """ + return len(self.frozenset()) + + def __iter__(self): r""" - String representation. + EXAMPLES:: + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') + sage: it = iter(p) + sage: next(it) + (0, 0, 0) + """ + return iter(self.sorted_list()) + + @cached_method + def bounding_box(self): + r""" EXAMPLES:: sage: from sage.combinat.tiling import Polyomino - sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='red') - Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: red + sage: p = Polyomino([(0,0,0),(1,0,0),(1,1,0),(1,1,1),(1,2,0)], color='deeppink') + sage: p.bounding_box() + [[0, 0, 0], [1, 2, 1]] """ - s = "Polyomino: %s, " % sorted(self._blocs) - s += "Color: %s" % self._color - return s + zipped_coords = list(zip(*self)) + return [[min(_) for _ in zipped_coords], + [max(_) for _ in zipped_coords]] def __hash__(self): r""" @@ -498,7 +516,7 @@ def __hash__(self): sage: hash(p) # random 2059134902 """ - return hash(self._blocs) + return hash(self.frozenset()) def __eq__(self, other): r""" @@ -523,7 +541,7 @@ def __eq__(self, other): sage: p == r False """ - return isinstance(other, Polyomino) and self._blocs == other._blocs + return isinstance(other, Polyomino_generic) and self.frozenset() == other.frozenset() def __ne__(self, other): r""" @@ -548,46 +566,55 @@ def __ne__(self, other): sage: p != r True """ - return not self._blocs == other._blocs + return not self.frozenset() == other.frozenset() - def __iter__(self): + def __le__(self, other): r""" - EXAMPLES:: + Return whether self is inside of other. - sage: from sage.combinat.tiling import Polyomino - sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') - sage: it = iter(p) - sage: next(it) - (0, 0, 0) - """ - return iter(self._blocs) + INPUT: - def color(self): - r""" - Return the color of the polyomino. + - ``other`` - a polyomino + + OUTPUT: + + boolean EXAMPLES:: - sage: from sage.combinat.tiling import Polyomino - sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') - sage: p.color() - 'blue' + sage: from sage.combinat.tiling import RectanglePolyomino, Polyomino + sage: p = Polyomino([(0,0)]) + sage: b = RectanglePolyomino([0,0], [2,3]) + sage: p <= b + True + sage: b <= p + False """ - return self._color + return isinstance(other, Polyomino_generic) and self.frozenset() <= other.frozenset() - def __len__(self): + def __ge__(self, other): r""" - Return the size of the polyomino, i.e. the number of n-dimensional - unit cubes. + Return whether self contains other. + + INPUT: + + - ``other`` - a polyomino + + OUTPUT: + + boolean EXAMPLES:: - sage: from sage.combinat.tiling import Polyomino - sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') - sage: len(p) - 4 + sage: from sage.combinat.tiling import RectanglePolyomino, Polyomino + sage: p = Polyomino([(0,0)]) + sage: b = RectanglePolyomino([0,0], [2,3]) + sage: p >= b + False + sage: b >= p + True """ - return len(self._blocs) + return isinstance(other, Polyomino_generic) and self.frozenset() >= other.frozenset() def __sub__(self, v): r""" @@ -671,19 +698,6 @@ def __rmul__(self, m): "dimension of the polyomino") return Polyomino([m * p for p in self], color=self._color) - def bounding_box(self): - r""" - EXAMPLES:: - - sage: from sage.combinat.tiling import Polyomino - sage: p = Polyomino([(0,0,0),(1,0,0),(1,1,0),(1,1,1),(1,2,0)], color='deeppink') - sage: p.bounding_box() - [[0, 0, 0], [1, 2, 1]] - """ - zipped_coords = list(zip(*self)) - return [[min(_) for _ in zipped_coords], - [max(_) for _ in zipped_coords]] - def canonical(self): r""" Returns the translated copy of self having minimal and nonnegative @@ -770,11 +784,11 @@ def canonical_isometric_copies(self, orientation_preserving=True, def translated_copies(self, box): r""" Returns an iterator over the translated images of self inside a - box. + polyomino. INPUT: - - ``box`` -- tuple of integers, size of the box + - ``box`` -- Polyomino or tuple of integers (size of a box) OUTPUT: @@ -829,17 +843,46 @@ def translated_copies(self, box): [] sage: list(p.translated_copies(box=(1,1,1))) [] - sage: list(p.translated_copies(box=(1,1,-1))) - [] + + Using a RectanglePolyomino as input:: + + sage: from sage.combinat.tiling import RectanglePolyomino + sage: b = RectanglePolyomino([0,0], [2,3]) + sage: p = Polyomino([(0,0)]) + sage: list(p.translated_copies(b)) + [Polyomino: [(0, 0)], Color: gray, + Polyomino: [(0, 1)], Color: gray, + Polyomino: [(0, 2)], Color: gray, + Polyomino: [(1, 0)], Color: gray, + Polyomino: [(1, 1)], Color: gray, + Polyomino: [(1, 2)], Color: gray] + + :: + + sage: p = Polyomino([(0,0), (1,0), (0,1)]) + sage: b = Polyomino([(0,0), (1,0), (2,0), (0,1), (1,1), (0,2)]) + sage: list(p.translated_copies(b)) + [Polyomino: [(0, 0), (0, 1), (1, 0)], Color: gray, + Polyomino: [(0, 1), (0, 2), (1, 1)], Color: gray, + Polyomino: [(1, 0), (1, 1), (2, 0)], Color: gray] """ - if not len(box) == self._dimension: + if not isinstance(box, Polyomino_generic): + dim = len(box) + box = RectanglePolyomino([0]*dim, box) + if not box._dimension == self._dimension: raise ValueError("Dimension of input box must match the " "dimension of the polyomino") minxyz, maxxyz = map(vector, self.bounding_box()) size = maxxyz - minxyz + boxminxyz, boxmaxxyz = box.bounding_box() + ranges = [range(a, b-c+1) for (a,b,c) in zip(boxminxyz, + boxmaxxyz, + size)] cano = self.canonical() - for v in xmrange(vector(box) - vector(size), tuple): - yield cano + v + for v in itertools.product(*ranges): + translated = cano + v + if translated <= box: + yield translated def isometric_copies(self, box, orientation_preserving=True, mod_box_isometries=False): @@ -848,7 +891,7 @@ def isometric_copies(self, box, orientation_preserving=True, INPUT: - - ``box`` -- tuple of integers, size of the box + - ``box`` -- Polyomino or tuple of integers (size of a box) - ``orientation_preserving`` -- bool (optional, default: ``True``), If True, the group of isometries of the `n`-cube is restricted to @@ -879,13 +922,29 @@ def isometric_copies(self, box, orientation_preserving=True, sage: L = list(p.isometric_copies((5,8,2), mod_box_isometries=True)) sage: len(L) 45 + + :: + + sage: p = Polyomino([(0,0), (1,0), (0,1)]) + sage: b = Polyomino([(0,0), (1,0), (2,0), (0,1), (1,1), (0,2)]) + sage: list(p.isometric_copies(b)) + [Polyomino: [(0, 0), (1, 0), (1, 1)], Color: gray, + Polyomino: [(0, 0), (0, 1), (1, 0)], Color: gray, + Polyomino: [(0, 1), (0, 2), (1, 1)], Color: gray, + Polyomino: [(1, 0), (1, 1), (2, 0)], Color: gray, + Polyomino: [(0, 0), (0, 1), (1, 1)], Color: gray, + Polyomino: [(0, 1), (1, 0), (1, 1)], Color: gray] """ - if not len(box) == self._dimension: + if not isinstance(box, Polyomino_generic): + dim = len(box) + box = RectanglePolyomino([0]*dim, box) + if not box._dimension == self._dimension: raise ValueError("Dimension of input box must match the " "dimension of the polyomino") - if mod_box_isometries and len(set(box)) < len(box): + if mod_box_isometries and len(set(b-a for (a,b) in zip(box._min_coords, + box._max_coords))) < box._dimension: raise NotImplementedError("The code below assumes that the" - " coordinates of the box (={}) are all distinct when" + " sizes of the box (={}) are all distinct when" " argument `mod_box_isometries` is True.".format(box)) all_distinct_cano = self.canonical_isometric_copies(orientation_preserving, mod_box_isometries) @@ -895,11 +954,11 @@ def isometric_copies(self, box, orientation_preserving=True, def neighbor_edges(self): r""" - Return an iterator over the pairs of neighbor coordinates of the - polyomino. + Return an iterator over the pairs of neighbor coordinates inside of + the polyomino. - Two points `P` and `Q` are neighbor if `P - Q` has one coordinate - equal to `+1` or `-1` and zero everywhere else. + Two points `P` and `Q` in the polyomino are neighbor if `P - Q` has + one coordinate equal to `+1` or `-1` and zero everywhere else. EXAMPLES:: @@ -1078,6 +1137,146 @@ def show2d(self, size=0.7, color='black', thickness=1): canonical_orthogonals = deprecated_function_alias(19107, canonical_isometric_copies) translated = deprecated_function_alias(19107, translated_copies) translated_orthogonals = deprecated_function_alias(19107, isometric_copies) +class Polyomino(Polyomino_generic): + r""" + Return the polyomino defined by a set of coordinates. + + The polyomino is the union of the unit square (or cube, or n-cube) + centered at those coordinates. Such an object should be connected, but + the code do not make this assumption. + + INPUT: + + - ``coords`` - iterable of tuple + - ``color`` - string (optional, default: ``'gray'``), the color + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') + Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue + """ + def __init__(self, coords, color='gray'): + r""" + INPUT: + + - ``coords`` - iterable of tuple + - ``color`` - string (optional, default: ``'gray'``), the color + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') + Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue + + :: + + sage: from sage.combinat.tiling import Polyomino + sage: Polyomino([(0,0), (1,0), (2,0)]) + Polyomino: [(0, 0), (1, 0), (2, 0)], Color: gray + """ + if not isinstance(coords, (tuple,list)): + coords = list(coords) + if not coords: + raise ValueError("Polyomino must be non empty") + + dimension = len(coords[0]) + Polyomino_generic.__init__(self, dimension, color) + + self._blocs = coords + self._blocs = map(self._free_module, self._blocs) + for b in self._blocs: + b.set_immutable() + #self._blocs = tuple(sorted(set(self._blocs))) + self._blocs = frozenset(self._blocs) + + def __repr__(self): + r""" + String representation. + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='red') + Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: red + """ + s = "Polyomino: %s, " % sorted(self._blocs) + s += "Color: %s" % self._color + return s + + def frozenset(self): + r""" + """ + return self._blocs + +class RectanglePolyomino(Polyomino_generic): + r""" + """ + def __init__(self, min_coords, max_coords, color='gray'): + self._min_coords = min_coords + self._max_coords = max_coords + dimension = len(min_coords) + Polyomino_generic.__init__(self, dimension, color) + + def __repr__(self): + r""" + String representation. + + EXAMPLES:: + + sage: from sage.combinat.tiling import RectanglePolyomino + sage: b = RectanglePolyomino([0,0], [2,3]) + sage: b + Rectangle Polyomino: [0,2[ x [0,3[, Color: gray + """ + b = ' x '.join("[{},{}[".format(a,b) for (a,b) in zip(self._min_coords, + self._max_coords)) + s = "Rectangle Polyomino: %s, " % b + s += "Color: %s" % self._color + return s + + def __len__(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.tiling import RectanglePolyomino + sage: b = RectanglePolyomino([0,0], [2,3]) + sage: len(b) + 6 + sage: b = RectanglePolyomino([0,0,0], [2,3,5]) + sage: len(b) + 30 + """ + from sage.misc.misc_c import prod + return prod(b-a for (a,b) in zip(self._min_coords, + self._max_coords)) + + def __iter__(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.tiling import RectanglePolyomino + sage: b = RectanglePolyomino([0,0], [2,3]) + sage: list(b) + [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)] + """ + ranges = [range(a,b) for (a,b) in zip(self._min_coords, + self._max_coords)] + for v in itertools.product(*ranges): + v = self._free_module(v) + v.set_immutable() + yield v + + def frozenset(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.tiling import RectanglePolyomino + sage: b = RectanglePolyomino([0,0], [2,3]) + sage: b.frozenset() + frozenset({(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)}) + """ + return frozenset(self) ####################### # General tiling solver @@ -1092,13 +1291,13 @@ class TilingSolver(SageObject): INPUT: - - ``pieces`` - iterable of Polyominoes - - ``box`` - tuple, size of the box - - ``rotation`` - bool (optional, default: ``True``), whether to allow + - ``pieces`` -- iterable of Polyominoes + - ``box`` -- Polyomino or tuple of integers (size of a box) + - ``rotation`` -- bool (optional, default: ``True``), whether to allow rotations - - ``reflection`` - bool (optional, default: ``False``), whether to allow + - ``reflection`` -- bool (optional, default: ``False``), whether to allow reflections - - ``reusable`` - bool (optional, default: ``False``), whether to allow + - ``reusable`` -- bool (optional, default: ``False``), whether to allow the pieces to be reused EXAMPLES: @@ -1111,7 +1310,8 @@ class TilingSolver(SageObject): sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)]) sage: T = TilingSolver([p,q,r], box=(1,1,6)) sage: T - Tiling solver of 3 pieces into the box (1, 1, 6) + Tiling solver of 3 pieces into the box + Rectangle Polyomino: [0,1[ x [0,1[ x [0,6[, Color: gray Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False @@ -1131,6 +1331,14 @@ class TilingSolver(SageObject): Polyomino: [(0, 0, 1), (0, 0, 2), (0, 0, 3)], Color: gray Polyomino: [(0, 0, 4), (0, 0, 5)], Color: gray + Tiling of a polyomino by polyominoes:: + + sage: b = Polyomino([(0,0), (1,0), (1,1), (2,1), (1,2), (2,2), (0,3), (1,3)]) + sage: p = Polyomino([(0,0), (1,0)]) + sage: T = TilingSolver([p], box=b, reusable=True) + sage: T.number_of_solutions() + 2 + TESTS:: sage: T = TilingSolver([p,q,r], box=(1,1,6), rotation=False, reflection=True) @@ -1151,14 +1359,19 @@ def __init__(self, pieces, box, rotation=True, sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)]) sage: T = TilingSolver([p,q,r], box=(1,1,6)) sage: T - Tiling solver of 3 pieces into the box (1, 1, 6) + Tiling solver of 3 pieces into the box + Rectangle Polyomino: [0,1[ x [0,1[ x [0,6[, Color: gray Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False """ self._pieces = pieces self._free_module = self._pieces[0]._free_module - self._box = box + if not isinstance(box, Polyomino_generic): + dim = len(box) + self._box = RectanglePolyomino([0]*dim, box) + else: + self._box = box self._rotation = rotation self._reflection = reflection if not self._rotation and self._reflection: @@ -1177,14 +1390,15 @@ def __repr__(self): sage: q = Polyomino([(0,0,0), (0,0,1)]) sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)]) sage: TilingSolver([p,q,r], box=(1,1,6)) - Tiling solver of 3 pieces into the box (1, 1, 6) + Tiling solver of 3 pieces into the box + Rectangle Polyomino: [0,1[ x [0,1[ x [0,6[, Color: gray Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False """ - N = len(self._pieces) - s = "Tiling solver of %s pieces into the box %s\n" % (N, self._box) + s = "Tiling solver of %s pieces into the box\n" % len(self._pieces) + s += "%s\n" % self._box s += "Rotation allowed: %s\n" % self._rotation s += "Reflection allowed: %s\n" % self._reflection s += "Reusing pieces allowed: %s" % self._reusable @@ -1216,7 +1430,7 @@ def is_suitable(self): return len(self.rows()) != 0 else: from sage.misc.misc_c import prod - return (sum(len(p) for p in self.pieces()) == prod(self._box) + return (sum(len(p) for p in self.pieces()) == len(self._box) and len(self.rows()) != 0) def pieces(self): @@ -1244,7 +1458,7 @@ def pieces(self): def space(self): r""" Returns an iterator over all the non negative integer coordinates - contained in the box. + contained in the space to tile. EXAMPLES:: @@ -1256,10 +1470,7 @@ def space(self): sage: list(T.space()) [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4), (0, 0, 5)] """ - for v in xmrange(self._box, tuple): - v = self._free_module(v) - v.set_immutable() - yield v + return iter(self._box) @cached_method def coord_to_int_dict(self): @@ -1380,16 +1591,16 @@ def rows_for_piece(self, i, mod_box_isometries=False): sage: T.rows_for_piece(0) [[0, 3, 5, 6], [0, 4, 6, 7], - [0, 2, 5, 6], - [0, 3, 6, 7], [0, 2, 3, 6], [0, 3, 4, 7], [0, 2, 3, 5], - [0, 3, 4, 6]] + [0, 3, 4, 6], + [0, 2, 5, 6], + [0, 3, 6, 7]] sage: T.rows_for_piece(0, mod_box_isometries=True) - [[0, 2, 5, 6], [0, 3, 6, 7]] + [[0, 2, 3, 6], [0, 3, 4, 7]] sage: T.rows_for_piece(1, mod_box_isometries=True) - [[1, 2, 5, 6], [1, 3, 6, 7]] + [[1, 2, 3, 6], [1, 3, 4, 7]] """ p = self._pieces[i] if self._rotation: @@ -1471,20 +1682,20 @@ def _rows_mod_box_isometries(self, i): sage: p = Polyomino([(0,0,0), (1,0,0), (1,1,0), (1,0,1), (2,0,1)], color='red') sage: T = TilingSolver([p], box=(3,4,2)) sage: T._rows_mod_box_isometries(0) - [[0, 2, 10, 11, 12, 20], - [0, 4, 12, 13, 14, 22], - [0, 6, 14, 15, 16, 24], - [0, 1, 9, 10, 11, 18], - [0, 3, 11, 12, 13, 20], - [0, 5, 13, 14, 15, 22], - [0, 4, 6, 10, 11, 12], - [0, 6, 8, 12, 13, 14], - [0, 12, 14, 18, 19, 20], - [0, 14, 16, 20, 21, 22], - [0, 4, 9, 11, 12, 14], - [0, 6, 11, 13, 14, 16], - [0, 12, 17, 19, 20, 22], - [0, 14, 19, 21, 22, 24]] + [[0, 2, 3, 4, 5, 11], + [0, 4, 5, 6, 7, 13], + [0, 10, 11, 12, 13, 19], + [0, 12, 13, 14, 15, 21], + [0, 1, 9, 10, 12, 18], + [0, 3, 11, 12, 14, 20], + [0, 5, 13, 14, 16, 22], + [0, 2, 9, 10, 12, 20], + [0, 4, 11, 12, 14, 22], + [0, 6, 13, 14, 16, 24], + [0, 1, 3, 4, 11, 13], + [0, 3, 5, 6, 13, 15], + [0, 9, 11, 12, 19, 21], + [0, 11, 13, 14, 21, 23]] We test that there are four times less rows for that polyomino:: @@ -1587,7 +1798,7 @@ def row_to_polyomino(self, row_number): :: sage: T.row_to_polyomino(7) - Polyomino: [(0, 0, 1), (0, 0, 2), (1, 0, 1)], Color: blue + Polyomino: [(0, 0, 1), (1, 0, 1), (1, 0, 2)], Color: blue :: @@ -1683,25 +1894,25 @@ def _dlx_common_prefix_solutions_iterator(self): sage: y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='yellow') sage: T = TilingSolver([y], box=(5,10), reusable=True, reflection=True) sage: for a in T._dlx_common_prefix_solutions_iterator(): a - [0, 65, 177, 164, 87, 44, 109, 70, 160, 129] - [0, 65, 177, 164, 87] - [0, 65, 177, 164, 87, 182, 109, 70, 160, 83] - [0] - [0, 111, 177, 26, 87, 44, 109, 70, 160, 129] - [0, 111, 177, 26, 87] - [0, 111, 177, 26, 87, 182, 109, 70, 160, 83] - [0, 111, 177] - [0, 111, 177, 125, 21, 70, 109, 44, 30, 134] + [64, 83, 149, 44, 179, 62, 35, 162, 132, 101] + [64, 83, 149, 44, 179] + [64, 83, 149, 44, 179, 154, 35, 162, 132, 175] + [64, 83, 149] + [64, 83, 149, 97, 39, 162, 35, 62, 48, 106] + [64] + [64, 157, 149, 136, 179, 62, 35, 162, 132, 101] + [64, 157, 149, 136, 179] + [64, 157, 149, 136, 179, 154, 35, 162, 132, 175] [] - [110, 147, 40, 125, 20, 69, 54, 45, 168, 135] - [110, 147, 40, 125, 20] - [110, 147, 40, 125, 20, 115, 54, 45, 30, 135] - [110, 147] - [110, 147, 178, 79, 20, 69, 54, 45, 168, 135] - [110, 147, 178, 79, 20] - [110, 147, 178, 79, 20, 115, 54, 45, 30, 135] - [110, 147, 178] - [110, 147, 178, 164, 88, 45, 54, 69, 159, 83] + [82, 119, 58, 97, 38, 87, 8, 63, 48, 107] + [82, 119, 58, 97, 38] + [82, 119, 58, 97, 38, 161, 8, 63, 140, 107] + [82, 119] + [82, 119, 150, 136, 180, 63, 8, 161, 131, 175] + [82, 119, 150] + [82, 119, 150, 171, 38, 87, 8, 63, 48, 107] + [82, 119, 150, 171, 38] + [82, 119, 150, 171, 38, 161, 8, 63, 140, 107] """ it = self._dlx_solutions_iterator() B = next(it) @@ -1748,16 +1959,16 @@ def _dlx_incremental_solutions_iterator(self): sage: y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='yellow') sage: T = TilingSolver([y], box=(5,10), reusable=True, reflection=True) sage: for a in T._dlx_solutions_iterator(): a - [0, 65, 177, 164, 87, 44, 109, 70, 160, 129] - [0, 65, 177, 164, 87, 182, 109, 70, 160, 83] - [0, 111, 177, 26, 87, 44, 109, 70, 160, 129] - [0, 111, 177, 26, 87, 182, 109, 70, 160, 83] - [0, 111, 177, 125, 21, 70, 109, 44, 30, 134] - [110, 147, 40, 125, 20, 69, 54, 45, 168, 135] - [110, 147, 40, 125, 20, 115, 54, 45, 30, 135] - [110, 147, 178, 79, 20, 69, 54, 45, 168, 135] - [110, 147, 178, 79, 20, 115, 54, 45, 30, 135] - [110, 147, 178, 164, 88, 45, 54, 69, 159, 83] + [64, 83, 149, 44, 179, 62, 35, 162, 132, 101] + [64, 83, 149, 44, 179, 154, 35, 162, 132, 175] + [64, 83, 149, 97, 39, 162, 35, 62, 48, 106] + [64, 157, 149, 136, 179, 62, 35, 162, 132, 101] + [64, 157, 149, 136, 179, 154, 35, 162, 132, 175] + [82, 119, 58, 97, 38, 87, 8, 63, 48, 107] + [82, 119, 58, 97, 38, 161, 8, 63, 140, 107] + [82, 119, 150, 136, 180, 63, 8, 161, 131, 175] + [82, 119, 150, 171, 38, 87, 8, 63, 48, 107] + [82, 119, 150, 171, 38, 161, 8, 63, 140, 107] sage: len(list(T._dlx_incremental_solutions_iterator())) 123 """ @@ -1967,7 +2178,7 @@ def animate(self, partial=None, stop=None, size=0.75, axes=False): sage: a # not tested Animation with 13 frames """ - dimension = len(self._box) + dimension = self._box._dimension if dimension == 2: from sage.plot.graphics import Graphics from sage.plot.animate import Animation @@ -1975,10 +2186,10 @@ def animate(self, partial=None, stop=None, size=0.75, axes=False): it = itertools.islice(it, stop) L = [sum([piece.show2d(size) for piece in solution], Graphics()) for solution in it] - xmax, ymax = self._box - xmax = xmax-0.5 - ymax = ymax-0.5 - a = Animation(L, xmin=-0.5, ymin=-0.5, + (xmin,ymin), (xmax,ymax) = self._box.bounding_box() + xmax = xmax+0.5 + ymax = ymax+0.5 + a = Animation(L, xmin=xmin-0.5, ymin=ymin-0.5, xmax=xmax, ymax=ymax, aspect_ratio=1, axes=axes) return a elif dimension == 3: diff --git a/src/sage/games/quantumino.py b/src/sage/games/quantumino.py index 1b7c227d883..0c7f8b67f52 100644 --- a/src/sage/games/quantumino.py +++ b/src/sage/games/quantumino.py @@ -142,7 +142,8 @@ sage: q = QuantuminoSolver(0) sage: T = q.tiling_solver() sage: T - Tiling solver of 16 pieces into the box (5, 8, 2) + Tiling solver of 16 pieces into the box + Rectangle Polyomino: [0,5[ x [0,8[ x [0,2[, Color: gray Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False @@ -453,17 +454,20 @@ def tiling_solver(self): sage: from sage.games.quantumino import QuantuminoSolver sage: QuantuminoSolver(0).tiling_solver() - Tiling solver of 16 pieces into the box (5, 8, 2) + Tiling solver of 16 pieces into the box + Rectangle Polyomino: [0,5[ x [0,8[ x [0,2[, Color: gray Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False sage: QuantuminoSolver(14).tiling_solver() - Tiling solver of 16 pieces into the box (5, 8, 2) + Tiling solver of 16 pieces into the box + Rectangle Polyomino: [0,5[ x [0,8[ x [0,2[, Color: gray Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False sage: QuantuminoSolver(14, box=(5,4,4)).tiling_solver() - Tiling solver of 16 pieces into the box (5, 4, 4) + Tiling solver of 16 pieces into the box + Rectangle Polyomino: [0,5[ x [0,4[ x [0,4[, Color: gray Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False From cbd60fbb63b3c9283dd3f284c02950a51bd99086 Mon Sep 17 00:00:00 2001 From: Steven Trogdon Date: Mon, 3 Apr 2017 21:05:06 -0500 Subject: [PATCH 044/151] Add exit and quit to EXCLUDE for trac #22529 and trac #16704 --- src/sage/misc/reset.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/misc/reset.pyx b/src/sage/misc/reset.pyx index bd7c94b944b..965ed06c0d6 100644 --- a/src/sage/misc/reset.pyx +++ b/src/sage/misc/reset.pyx @@ -7,7 +7,8 @@ import sys # Exclude these from the reset command. # DATA, base64 -- needed by the notebook -EXCLUDE = set(['sage_mode', '__DIR__', 'DIR', 'DATA', 'base64']) +# Add exit and quit to EXCLUDE to resolve trac #22529 and trac #16704 +EXCLUDE = set(['sage_mode', '__DIR__', 'DIR', 'DATA', 'base64', 'exit', 'quit']) def reset(vars=None, attached=False): """ From 95d4b0ff75985c8c4f7ec7923232fac898301865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 5 Apr 2017 14:06:18 +0200 Subject: [PATCH 045/151] py3: get rid of __cmp__ in quadratic forms --- src/sage/quadratic_forms/binary_qf.py | 65 ++++++++++++++++++++------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py index bf818ead47b..9fe876521d0 100644 --- a/src/sage/quadratic_forms/binary_qf.py +++ b/src/sage/quadratic_forms/binary_qf.py @@ -51,6 +51,7 @@ from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method + class BinaryQF(SageObject): """ A binary quadratic form over `\ZZ`. @@ -60,7 +61,6 @@ class BinaryQF(SageObject): - `v` -- a list or tuple of 3 entries: [a,b,c], or a quadratic homogeneous polynomial in two variables with integer coefficients - OUTPUT: the binary quadratic form a*x^2 + b*x*y + c*y^2. @@ -144,7 +144,7 @@ def _pari_init_(self): sage: type(gp(f)) """ - return 'Qfb(%s,%s,%s)'%(self._a,self._b,self._c) + return 'Qfb(%s,%s,%s)' % (self._a, self._b, self._c) def __mul__(self, right): """ @@ -176,8 +176,8 @@ def __mul__(self, right): # wrapped yet in the PARI C library. We may as well settle # for the below, until somebody simply implements composition # from scratch in Cython. - v = list(pari('qfbcompraw(%s,%s)'%(self._pari_init_(), - right._pari_init_()))) + v = list(pari('qfbcompraw(%s,%s)' % (self._pari_init_(), + right._pari_init_()))) return BinaryQF(v) def __getitem__(self, n): @@ -250,12 +250,13 @@ def __hash__(self): """ return hash(self._a) ^ (hash(self._b) << 4) ^ (hash(self._c) << 8) - def __cmp__(self, right): + def __eq__(self, right): """ - Returns True if self and right are identical: the same coefficients. + Return ``True`` if ``self`` and ``right`` are identical. - EXAMPLES:: + This means that they have the same coefficients. + EXAMPLES:: sage: P = BinaryQF([2,2,3]) sage: Q = BinaryQF([2,2,3]) @@ -277,8 +278,45 @@ def __cmp__(self, right): False """ if not isinstance(right, BinaryQF): - return cmp(type(self), type(right)) - return cmp((self._a,self._b,self._c), (right._a,right._b,right._c)) + return False + return (self._a, self._b, self._c) == (right._a, right._b, right._c) + + def __ne__(self, right): + """ + Return ``True`` if ``self`` and ``right`` are not identical. + + This means that they have different coefficients. + + EXAMPLES:: + + sage: P = BinaryQF([2,2,3]) + sage: Q = BinaryQF([2,2,3]) + sage: R = BinaryQF([1,2,3]) + sage: P != Q # indirect doctest + False + sage: P != R # indirect doctest + True + """ + return not (self == right) + + def __lt__(self, right): + """ + Compare the coefficients of ``self`` and ``right``. + + This is done lexicographically. + + EXAMPLES:: + + sage: P = BinaryQF([2,2,3]) + sage: Q = BinaryQF([1,2,3]) + sage: P < Q + False + sage: Q < P + True + """ + if not isinstance(right, BinaryQF): + return False + return (self._a, self._b, self._c) < (right._a, right._b, right._c) def __add__(self, Q): """ @@ -290,7 +328,6 @@ def __add__(self, Q): EXAMPLES:: - sage: P = BinaryQF([2,2,3]); P 2*x^2 + 2*x*y + 3*y^2 sage: Q = BinaryQF([-1,2,2]); Q @@ -317,7 +354,6 @@ def __sub__(self, Q): EXAMPLES:: - sage: P = BinaryQF([2,2,3]); P 2*x^2 + 2*x*y + 3*y^2 sage: Q = BinaryQF([-1,2,2]); Q @@ -342,7 +378,6 @@ def _repr_(self): EXAMPLES:: - sage: Q = BinaryQF([1,2,3]); Q # indirect doctest x^2 + 2*x*y + 3*y^2 @@ -397,7 +432,6 @@ def discriminant(self): EXAMPLES:: - sage: Q = BinaryQF([1,2,3]) sage: Q.discriminant() -8 @@ -435,7 +469,6 @@ def is_primitive(self): EXAMPLES:: - sage: Q = BinaryQF([6,3,9]) sage: Q.is_primitive() False @@ -479,7 +512,6 @@ def is_weakly_reduced(self): EXAMPLES:: - sage: Q = BinaryQF([1,2,3]) sage: Q.is_weakly_reduced() False @@ -659,7 +691,7 @@ def small_prime_value(self, Bmax=1000): A prime number represented by the form. - .. note:: + .. NOTE:: This is a very elementary implementation which just substitutes values until a prime is found. @@ -729,6 +761,7 @@ def solve_integer(self, n): return (x,y) return None + def BinaryQF_reduced_representatives(D, primitive_only=False): r""" Returns a list of inequivalent reduced representatives for the From d1a0054cad94bb25f363de87c2a3bc52f038db20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Wed, 5 Apr 2017 19:15:23 +0300 Subject: [PATCH 046/151] Add is_stone(). --- src/sage/combinat/posets/lattices.py | 94 ++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index bc3e8c8c612..f6dae35c903 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -59,6 +59,7 @@ :meth:`~FiniteLatticePoset.is_supersolvable` | Return ``True`` if the lattice is supersolvable. :meth:`~FiniteLatticePoset.is_planar` | Return ``True`` if the lattice has an upward planar drawing. :meth:`~FiniteLatticePoset.is_dismantlable` | Return ``True`` if the lattice is dismantlable. + :meth:`~FiniteLatticePoset.is_stone` | Return ``True`` if the lattice is Stone algebra. :meth:`~FiniteLatticePoset.is_vertically_decomposable` | Return ``True`` if the lattice is vertically decomposable. :meth:`~FiniteLatticePoset.is_simple` | Return ``True`` if the lattice has no nontrivial congruences. :meth:`~FiniteLatticePoset.is_isoform` | Return ``True`` if all congruences of the lattice consists of isoform blocks. @@ -1012,6 +1013,99 @@ def is_meet_distributive(self, certificate=False): diamond = next(self._hasse_diagram.subgraph_search_iterator(M3)) return (False, diamond[4]) + def is_stone(self, certificate=False): + r""" + Return ``True`` if the lattice is Stone lattice, and ``False`` + otherwise. + + The lattice is expected to be distributive (and hence + pseudocomplemented). + + A pseudocomplemented lattice is Stone lattice if + + .. MATH:: + + e^* \vee e^{**} = \top + + for every element `e` of the lattice, where `^*` is the + pseudocomplement and `\top` is the top element of the lattice. + + INPUT: + + - ``certificate`` -- (default: ``False``) whether to return + a certificate + + OUTPUT: + + - If ``certificate=True`` return either ``(True, None)`` or + ``(False, e)`` such that `e^* \vee e^{**} \neq \top`. + If ``certificate=False`` return ``True`` or ``False``. + + EXAMPLES: + + Divisor lattices are canonical example:: + + sage: D72 = Posets.DivisorLattice(72) + sage: D72.is_stone() + True + + A non-example:: + + sage: L = LatticePoset({1: [2, 3], 2: [4], 3: [4], 4: [5]}) + sage: L.is_stone() + False + + TESTS:: + + sage: LatticePoset().is_stone() # Empty lattice + True + + sage: L = LatticePoset(DiGraph('GW?_W@?W@?O?')) + sage: L.is_stone() # Pass the fast check, but not Stone lattice + False + """ + from sage.arith.misc import factor + # TODO: For now we can factor only undirected graphs. When that + # is extended to directed, use that; see comment below. + + if not self.is_distributive(): + raise ValueError("the lattice is not distributive") + + ok = (True, None) if certificate else True + + # Needed for the empty lattice that has no bottom element. + if self.cardinality() < 5: + return ok + + # Quick check: + # Stone lattice is direct product of distributive lattices with + # one atom. Return False if for example the lattice has two atoms + # and odd number of elements. + atoms_n = self._hasse_diagram.out_degree(0) + if atoms_n == 1: + return ok + if not certificate: + if (sum([x[1] for x in factor(self.cardinality())]) < + atoms_n): + return False + if (self._hasse_diagram.in_degree(self.cardinality()-1) < + atoms_n): + return False + + # Quick check failed + one = self.top() + tested = set() + for e in self: + e_ = self.pseudocomplement(e) + if e_ not in tested: + if self.join(e_, self.pseudocomplement(e_)) != one: + if certificate: + return (False, e) + return False + tested.add(e_) + + return ok + def is_distributive(self): r""" Return ``True`` if the lattice is distributive, and ``False`` From 0e90d5294f2dd1f95f2e96bf34830d1256a3f211 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Wed, 5 Apr 2017 14:49:46 -0700 Subject: [PATCH 047/151] Trac 22764: remove the link SAGE_LOCAL/lib/python -> python2.7 This link is unnecessary and can interfere with the Python 3 build, at least on OS X. So let's remove it and not create it anymore. --- build/pkgs/python2/spkg-install | 17 +++++------------ build/pkgs/python3/spkg-install | 4 ++++ build/pkgs/sagenb/spkg-install | 3 ++- src/bin/sage | 2 +- src/sage/all.py | 5 +++-- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/build/pkgs/python2/spkg-install b/build/pkgs/python2/spkg-install index cf28410a2cf..b7686d2c3a6 100755 --- a/build/pkgs/python2/spkg-install +++ b/build/pkgs/python2/spkg-install @@ -111,21 +111,14 @@ build() fi } -build +# Remove old symbolic link: it is not needed and its presence can +# interfere with the Python build. -PYTHON_VERSION=$("$SAGE_LOCAL/bin/python2" -c 'import sys; print("%d.%d" % sys.version_info[:2])') +rm -f "$SAGE_LOCAL/lib/python" -cd "$SAGE_LOCAL/lib" +build -# Make symbolic link (after removing old link first) -if [ "$SAGE_PYTHON3" != yes ]; then - rm -f python - ln -s python${PYTHON_VERSION} python - if [ $? -ne 0 ]; then - echo >&2 "Error creating symbolic link" - exit 1 - fi -fi +PYTHON_VERSION=$("$SAGE_LOCAL/bin/python2" -c 'import sys; print("%d.%d" % sys.version_info[:2])') # On OS X with XCode 4, the presence of # $SAGE_LOCAL/lib/python/config/libpython2.7.a causes problems with diff --git a/build/pkgs/python3/spkg-install b/build/pkgs/python3/spkg-install index 60679d1d513..8d87f9f5395 100755 --- a/build/pkgs/python3/spkg-install +++ b/build/pkgs/python3/spkg-install @@ -90,6 +90,10 @@ build() } +# Remove old symbolic link: it is not needed and its presence can +# interfere with the Python build. +rm -f "$SAGE_LOCAL/lib/python" + build # Make symbolic link (after removing old link first) diff --git a/build/pkgs/sagenb/spkg-install b/build/pkgs/sagenb/spkg-install index 205ae8cd267..943e7733772 100755 --- a/build/pkgs/sagenb/spkg-install +++ b/build/pkgs/sagenb/spkg-install @@ -17,7 +17,8 @@ if [ $? -ne 0 ]; then fi # let sagenb use mathjax -cd "${SAGE_LOCAL}/lib/python/site-packages/sagenb/data" +PYTHON_VERSION=$("$SAGE_LOCAL/bin/python2" -c 'import sys; print("%d.%d" % sys.version_info[:2])') +cd "${SAGE_LOCAL}/lib/python$PYTHON_VERSION/site-packages/sagenb/data" if [ $? -ne 0 ]; then echo >&2 "Error: Cannot find SageNB data directory." exit 1 diff --git a/src/bin/sage b/src/bin/sage index 3c79dd9cb31..cc6e41cbedb 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -383,7 +383,7 @@ fi # Prepare for running Sage, either interactively or non-interactively. sage_setup() { # Check that we're not in a source tarball which hasn't been built yet (#13561). - if [ ! -d "$SAGE_LOCAL/lib/python/site-packages/sage" ]; then + if ! python -c 'import sage.version' 2> /dev/null ; then echo >&2 '************************************************************************' echo >&2 'It seems that you are attempting to run Sage from an unpacked source' echo >&2 'tarball, but you have not compiled it yet (or maybe the build has not' diff --git a/src/sage/all.py b/src/sage/all.py index f56920b4162..8e0dcb04963 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -17,9 +17,10 @@ We exclude the known files and check to see that there are no others:: - sage: import os + sage: import os, sys + sage: python_version = "{}.{}".format(*sys.version_info[:2]) sage: allowed = [os.path.join("lib","python","threading.py")] - sage: allowed.append(os.path.join("lib","python","multiprocessing")) + sage: allowed.append(os.path.join("lib","python"+python_version,"multiprocessing")) sage: allowed.append(os.path.join("sage","doctest")) sage: allowed.append(os.path.join("bin","sage-runtests")) sage: allowed.append(os.path.join("site-packages","IPython")) From f33e86f76463c02dee30513fd5553ebbe5a228d5 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Wed, 5 Apr 2017 15:07:47 -0700 Subject: [PATCH 048/151] trac 22764: replace a few instances of 'rm -f local/lib/python/site-package/PKG' with 'rm -f local/lib/python*/site-packages/PKG' --- build/pkgs/brial/spkg-install | 2 +- build/pkgs/giacpy_sage/spkg-install | 8 ++++---- build/pkgs/numpy/spkg-install | 2 +- build/pkgs/pillow/spkg-install | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build/pkgs/brial/spkg-install b/build/pkgs/brial/spkg-install index 1b70170544a..591736903ce 100755 --- a/build/pkgs/brial/spkg-install +++ b/build/pkgs/brial/spkg-install @@ -7,7 +7,7 @@ if [ "$SAGE_LOCAL" = "" ]; then fi echo "Cleaning out old PolyBoRi and BRiAl installations" -rm -rf "$SAGE_LOCAL"/lib/python/site-packages/{polybori,brial} +rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/{polybori,brial} rm -f "$SAGE_LOCAL"/lib/lib{polybori,brial}* rm -rf "$SAGE_LOCAL"/include/polybori* rm -rf "$SAGE_LOCAL"/share/polybori diff --git a/build/pkgs/giacpy_sage/spkg-install b/build/pkgs/giacpy_sage/spkg-install index 489973630cd..17ab5aabc7d 100755 --- a/build/pkgs/giacpy_sage/spkg-install +++ b/build/pkgs/giacpy_sage/spkg-install @@ -17,13 +17,13 @@ cd src # It is very important that we have SAGE_LOCAL set, otherwise this # might potentially delete stuff in /lib # old giacpy spkg files <0.6 but no dir in case someone put a python version of giacpy. -echo "Deleting $SAGE_LOCAL/lib/python/site-packages/giacpy-*" -rm -f "$SAGE_LOCAL"/lib/python/site-packages/giacpy-* +echo "Deleting $SAGE_LOCAL/lib/python*/site-packages/giacpy-*" +rm -f "$SAGE_LOCAL"/lib/python*/site-packages/giacpy-* echo "Deleting $SAGE_LOCAL/lib/python/site-packages/giacpy.so" -rm -f "$SAGE_LOCAL"/lib/python/site-packages/giacpy.so +rm -f "$SAGE_LOCAL"/lib/python*/site-packages/giacpy.so # new giacpy spkg files >0.6 echo "Deleting $SAGE_LOCAL/lib/python/site-packages/giacpy_sage*" -rm -f "$SAGE_LOCAL"/lib/python/site-packages/giacpy_sage* +rm -f "$SAGE_LOCAL"/lib/python*/site-packages/giacpy_sage* $PIP_INSTALL . diff --git a/build/pkgs/numpy/spkg-install b/build/pkgs/numpy/spkg-install index 1082a08f03f..eb8bdbfb475 100755 --- a/build/pkgs/numpy/spkg-install +++ b/build/pkgs/numpy/spkg-install @@ -44,7 +44,7 @@ export NUMPY_FCONFIG="config_fc --noopt --noarch" ################################################ -rm -rf "$SAGE_LOCAL/lib/python/site-packages/numpy" +rm -rf "$SAGE_LOCAL/lib/python*/site-packages/numpy" # Program around a bug in SciPY's distutils. unset CFLAGS diff --git a/build/pkgs/pillow/spkg-install b/build/pkgs/pillow/spkg-install index 80d39f5eb6d..57b60fb2050 100755 --- a/build/pkgs/pillow/spkg-install +++ b/build/pkgs/pillow/spkg-install @@ -9,7 +9,7 @@ fi cd src # Delete old version -site_packages="$SAGE_LOCAL/lib/python/site-packages" +site_packages="$SAGE_LOCAL/lib/python*/site-packages" rm -rf "$site_packages"/PIL "$site_packages"/PIL-*.egg* "$site_packages"/Pillow-*.egg* # Note: Avoid shared libraries inside egg files, Trac #19467 From b02ecf34018f48b24012623c5388a34d589bb3d2 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Wed, 5 Apr 2017 15:32:16 -0700 Subject: [PATCH 049/151] trac 22764: in a few comments and print statements, fix a few references to SAGE_LOCAL/lib/python/... --- build/pkgs/giacpy_sage/spkg-install | 4 ++-- build/pkgs/python2/spkg-install | 2 +- build/pkgs/python3/spkg-install | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/giacpy_sage/spkg-install b/build/pkgs/giacpy_sage/spkg-install index 17ab5aabc7d..273b4105238 100755 --- a/build/pkgs/giacpy_sage/spkg-install +++ b/build/pkgs/giacpy_sage/spkg-install @@ -19,10 +19,10 @@ cd src # old giacpy spkg files <0.6 but no dir in case someone put a python version of giacpy. echo "Deleting $SAGE_LOCAL/lib/python*/site-packages/giacpy-*" rm -f "$SAGE_LOCAL"/lib/python*/site-packages/giacpy-* -echo "Deleting $SAGE_LOCAL/lib/python/site-packages/giacpy.so" +echo "Deleting $SAGE_LOCAL/lib/python*/site-packages/giacpy.so" rm -f "$SAGE_LOCAL"/lib/python*/site-packages/giacpy.so # new giacpy spkg files >0.6 -echo "Deleting $SAGE_LOCAL/lib/python/site-packages/giacpy_sage*" +echo "Deleting $SAGE_LOCAL/lib/python*/site-packages/giacpy_sage*" rm -f "$SAGE_LOCAL"/lib/python*/site-packages/giacpy_sage* diff --git a/build/pkgs/python2/spkg-install b/build/pkgs/python2/spkg-install index b7686d2c3a6..52eb5956977 100755 --- a/build/pkgs/python2/spkg-install +++ b/build/pkgs/python2/spkg-install @@ -121,7 +121,7 @@ build PYTHON_VERSION=$("$SAGE_LOCAL/bin/python2" -c 'import sys; print("%d.%d" % sys.version_info[:2])') # On OS X with XCode 4, the presence of -# $SAGE_LOCAL/lib/python/config/libpython2.7.a causes problems with +# $SAGE_LOCAL/lib/python2.*/config/libpython2.x.a causes problems with # GiNaC -- see #11967. It is easiest to test the version of OS X; we # delete this file if using OS X 10.6 or later (so `uname -r` returns # x.y.z with x >= 10). diff --git a/build/pkgs/python3/spkg-install b/build/pkgs/python3/spkg-install index 8d87f9f5395..670b7cebe53 100755 --- a/build/pkgs/python3/spkg-install +++ b/build/pkgs/python3/spkg-install @@ -109,7 +109,7 @@ fi PYTHON_VERSION=$("$SAGE_LOCAL/bin/python3" -c 'import sys; print("%d.%d" % sys.version_info[:2])') # On OS X with XCode 4, the presence of -# $SAGE_LOCAL/lib/python/config/libpython3.x.a causes problems with +# $SAGE_LOCAL/lib/python3.x/config/libpython3.x.a causes problems with # GiNaC -- see #11967. It is easiest to test the version of OS X; we # delete this file if using OS X 10.6 or later (so `uname -r` returns # x.y.z with x >= 10). From c1cdcb1c8ee9aab4b7c8003933e1fe93a09fe34f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Wed, 5 Apr 2017 23:07:05 -0400 Subject: [PATCH 050/151] #22765: fix cycle_index for permutation groups over an arbitrary domain + cleanup --- .../categories/finite_permutation_groups.py | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/sage/categories/finite_permutation_groups.py b/src/sage/categories/finite_permutation_groups.py index 3a02e7b6644..eff78ccdebe 100644 --- a/src/sage/categories/finite_permutation_groups.py +++ b/src/sage/categories/finite_permutation_groups.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Finite Permutation Groups """ @@ -103,15 +104,18 @@ class ParentMethods: def cycle_index(self, parent = None): r""" + Return the *cycle index* of ``self``. + INPUT: - ``self`` - a permutation group `G` - ``parent`` -- a free module with basis indexed by partitions, or behave as such, with a ``term`` and ``sum`` method - (default: the symmetric functions over the rational field in the p basis) + (default: the symmetric functions over the rational field in the `p` basis) - Returns the *cycle index* of `G`, which is a gadget counting - the elements of `G` by cycle type, averaged over the group: + The *cycle index* of a permutation group `G` + (:wikipedia`Cycle_index`) is a gadget counting the + elements of `G` by cycle type, averaged over the group: .. MATH:: @@ -135,7 +139,7 @@ def cycle_index(self, parent = None): 8 The cycle index plays an important role in the enumeration of - objects modulo the action of a group (Polya enumeration), via + objects modulo the action of a group (Pólya enumeration), via the use of symmetric functions and plethysms. It is therefore encoded as a symmetric function, expressed in the powersum basis:: @@ -151,8 +155,11 @@ def cycle_index(self, parent = None): sage: h( P ) h[4] - TODO: add some simple examples of Polya enumeration, once it - will be easy to expand symmetric functions on any alphabet. + .. TODO:: + + Add some simple examples of Pólya enumeration, once + it will be easy to expand symmetric functions on any + alphabet. Here are the cycle indices of some permutation groups:: @@ -170,6 +177,13 @@ def cycle_index(self, parent = None): p[1, 1, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 24*p[5] p[1, 1, 1, 1, 1] + 10*p[2, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 20*p[3, 2] + 30*p[4, 1] + 24*p[5] + Permutation groups with arbitrary domains are supported + (see trac:`22765`):: + + sage: G = PermutationGroup([['b','c','a']], domain=['a','b','c']) + sage: G.cycle_index() + 1/3*p[1, 1, 1] + 2/3*p[3] + One may specify another parent for the result:: sage: F = CombinatorialFreeModule(QQ, Partitions()) @@ -184,7 +198,7 @@ def cycle_index(self, parent = None): sage: CyclicPermutationGroup(6).cycle_index(parent = QQ) Traceback (most recent call last): ... - AssertionError: `parent` should be (or behave as) a free module with basis indexed by partitions + ValueError: `parent` should be (or behave as) a free module with basis indexed by partitions REFERENCES: @@ -192,7 +206,7 @@ def cycle_index(self, parent = None): AUTHORS: - - Nicolas Borie and Nicolas M. Thiery + - Nicolas Borie and Nicolas M. Thiéry TESTS:: @@ -205,19 +219,16 @@ def cycle_index(self, parent = None): sage: P.cycle_index() p[1] """ - from sage.combinat.permutation import Permutation if parent is None: from sage.rings.rational_field import QQ from sage.combinat.sf.sf import SymmetricFunctions parent = SymmetricFunctions(QQ).powersum() - else: - assert hasattr(parent, "term") and hasattr(parent, "sum"), \ - "`parent` should be (or behave as) a free module with basis indexed by partitions" + elif not hasattr(parent, "term") and hasattr(parent, "sum"): + raise ValueError("`parent` should be (or behave as) a free module with basis indexed by partitions") base_ring = parent.base_ring() - # TODO: use self.conjugacy_classes() once available - from sage.interfaces.gap import gap - CC = ([Permutation(self(C.Representative())).cycle_type(), base_ring(C.Size())] for C in gap(self).ConjugacyClasses()) - return parent.sum( parent.term( partition, coeff ) for (partition, coeff) in CC)/self.cardinality() + return parent.sum( parent.term( C.an_element().cycle_type(), base_ring(C.cardinality()) ) + for C in self.conjugacy_classes() + ) / self.cardinality() class ElementMethods: From 543c4a1889f21b35483b0bf45e7dbcdb6ce25582 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 23 Mar 2017 12:09:30 +0100 Subject: [PATCH 051/151] Upgrade PARI to version 2.9.2 --- build/pkgs/pari/checksums.ini | 6 +++--- build/pkgs/pari/package-version.txt | 2 +- build/pkgs/pari/patches/dilog_prec.patch | 20 -------------------- build/pkgs/pari/patches/fflush.patch | 20 ++++++++++++++++++++ build/pkgs/pari/patches/pari_str_1.patch | 2 +- src/sage/libs/pari/tests.py | 4 ++-- 6 files changed, 27 insertions(+), 27 deletions(-) delete mode 100644 build/pkgs/pari/patches/dilog_prec.patch create mode 100644 build/pkgs/pari/patches/fflush.patch diff --git a/build/pkgs/pari/checksums.ini b/build/pkgs/pari/checksums.ini index 7c0fe7ba182..be66a9c3b20 100644 --- a/build/pkgs/pari/checksums.ini +++ b/build/pkgs/pari/checksums.ini @@ -1,4 +1,4 @@ tarball=pari-VERSION.tar.gz -sha1=ad6af366153ae160aaa4cb49edf3a32a69f7afc1 -md5=f997d6b526df6cd9145af0de5b822bf4 -cksum=2625551585 +sha1=6e5f9b227bb1afe9a8fb67fe1239211714601142 +md5=fec030cfa01fa1815552c7aa362d8bf9 +cksum=3798015133 diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index fb3a2322a51..c26650020ed 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.9.1.p3 +2.9.2.p0 diff --git a/build/pkgs/pari/patches/dilog_prec.patch b/build/pkgs/pari/patches/dilog_prec.patch deleted file mode 100644 index 3c588f5a5ed..00000000000 --- a/build/pkgs/pari/patches/dilog_prec.patch +++ /dev/null @@ -1,20 +0,0 @@ -commit 8a3e9d2babeec411a46c6454d8b2406b267f3380 -Author: Karim Belabas -Date: Mon Feb 13 09:18:43 2017 +0100 - - 41- polylog(x) used realprecision accuracy instead of precision(x) [#1895] - -diff --git a/src/basemath/trans3.c b/src/basemath/trans3.c -index 33ba45f..6202688 100644 ---- a/src/basemath/trans3.c -+++ b/src/basemath/trans3.c -@@ -2385,7 +2385,8 @@ polylog(long m, GEN x, long prec) - return gerepileupto(av, gneg(glog(gsub(gen_1,x), prec))); - } - -- l = precision(x); if (!l) l = prec; -+ l = precision(x); -+ if (!l) l = prec; else prec = l; - res = cgetc(l); av = avma; - x = gtofp(x, l+EXTRAPRECWORD); - e = gexpo(gnorm(x)); diff --git a/build/pkgs/pari/patches/fflush.patch b/build/pkgs/pari/patches/fflush.patch new file mode 100644 index 00000000000..d6eb25995a0 --- /dev/null +++ b/build/pkgs/pari/patches/fflush.patch @@ -0,0 +1,20 @@ +commit c8ddd6e4952a8731f5e99ff17cfa37cb430b47aa +Author: Bill Allombert +Date: Tue Mar 28 10:42:54 2017 +0200 + + GP was incorrectly issuing fflush(NULL) (From Erik Bray) + +diff --git a/src/language/es.c b/src/language/es.c +index 38f3737..77bcf8f 100644 +--- a/src/language/es.c ++++ b/src/language/es.c +@@ -532,7 +532,8 @@ _puts_log(const char *s) + fputs(s, f); + } + static void +-_flush_log(void) { (void)fflush(pari_logfile); } ++_flush_log(void) ++{ if (pari_logfile != NULL) (void)fflush(pari_logfile); } + + static void + normalOutC(char c) { putc(c, pari_outfile); _putc_log(c); } diff --git a/build/pkgs/pari/patches/pari_str_1.patch b/build/pkgs/pari/patches/pari_str_1.patch index bfefeb8f4d3..f340d343b86 100644 --- a/build/pkgs/pari/patches/pari_str_1.patch +++ b/build/pkgs/pari/patches/pari_str_1.patch @@ -332,7 +332,7 @@ index 90ce368..e1bfc8d 100644 +str_long(pari_str *S, long e) { if (e >= 0) str_ulong(S, (ulong)e); - else { str_putc(S, '-'); str_ulong(S, (ulong)(-e)); } + else { str_putc(S, '-'); str_ulong(S, -(ulong)e); } } static void diff --git a/src/sage/libs/pari/tests.py b/src/sage/libs/pari/tests.py index ced54948e87..95371d272a7 100644 --- a/src/sage/libs/pari/tests.py +++ b/src/sage/libs/pari/tests.py @@ -166,9 +166,9 @@ doctest:...: DeprecationWarning: the PARI/GP function elllseries is obsolete (2016-08-08) 0.402838047956645 sage: e.elllseries(1, precision=128) - 3.19632265064095 E-40 + -6.17606670058278 E-39 sage: e.elllseries(1, precision=256) - 8.68747983667209 E-79 + -2.05598131842639 E-77 sage: e.elllseries(-2) 0 sage: e.elllseries(2.1, A=1.1) From 6d66f65d2e120367439ea6d71ff9c0e55504cedf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Thu, 6 Apr 2017 07:03:16 -0400 Subject: [PATCH 052/151] 22765: fixed reST typo --- src/sage/categories/finite_permutation_groups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/finite_permutation_groups.py b/src/sage/categories/finite_permutation_groups.py index eff78ccdebe..0c585a8530e 100644 --- a/src/sage/categories/finite_permutation_groups.py +++ b/src/sage/categories/finite_permutation_groups.py @@ -178,7 +178,7 @@ def cycle_index(self, parent = None): p[1, 1, 1, 1, 1] + 10*p[2, 1, 1, 1] + 15*p[2, 2, 1] + 20*p[3, 1, 1] + 20*p[3, 2] + 30*p[4, 1] + 24*p[5] Permutation groups with arbitrary domains are supported - (see trac:`22765`):: + (see :trac:`22765`):: sage: G = PermutationGroup([['b','c','a']], domain=['a','b','c']) sage: G.cycle_index() From df6a763f1c6190cbcb33501fdf7b6f4e5ee46400 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Thu, 6 Apr 2017 13:39:34 -0700 Subject: [PATCH 053/151] trac 22764: rebase to Sage 8.0.beta1 --- src/sage/all.py | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/sage/all.py b/src/sage/all.py index 8e0dcb04963..fa063f51833 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -15,27 +15,20 @@ sage: from sage import * sage: frames = [x for x in gc.get_objects() if inspect.isframe(x)] -We exclude the known files and check to see that there are no others:: - - sage: import os, sys - sage: python_version = "{}.{}".format(*sys.version_info[:2]) - sage: allowed = [os.path.join("lib","python","threading.py")] - sage: allowed.append(os.path.join("lib","python"+python_version,"multiprocessing")) - sage: allowed.append(os.path.join("sage","doctest")) - sage: allowed.append(os.path.join("bin","sage-runtests")) - sage: allowed.append(os.path.join("site-packages","IPython")) - sage: allowed.append(os.path.join("bin","sage-ipython")) - sage: allowed.append("") - sage: allowed.append("") - sage: allowed.append(os.path.join("sage","combinat","species","generating_series.py")) - sage: for i in frames: - ....: filename, lineno, funcname, linelist, indx = inspect.getframeinfo(i) - ....: for nm in allowed: - ....: if nm in filename: - ....: break - ....: else: - ....: print(filename) - ....: +We exclude the dependencies and check to see that there are no others +except for the known bad apples:: + + sage: allowed = [ + ....: 'IPython', 'prompt_toolkit', # sage dependencies + ....: 'threading', 'multiprocessing', # doctest dependencies + ....: '__main__', 'sage.doctest', # doctesting + ....: ] + sage: def is_not_allowed(frame): + ....: module = inspect.getmodule(frame) + ....: if module is None: return False + ....: return not any(module.__name__.startswith(name) for name in allowed) + sage: [inspect.getmodule(f).__name__ for f in frames if is_not_allowed(f)] + ['sage.combinat.species.generating_series'] Check that the Sage Notebook is not imported at startup (see :trac:`15335`):: From 270b66e143f1746442e5aac6e0c7c357bb52d4e3 Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Fri, 7 Apr 2017 20:00:51 -0500 Subject: [PATCH 054/151] 22783: add character functionality for GAP groups --- src/sage/groups/libgap_mixin.py | 84 ++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/src/sage/groups/libgap_mixin.py b/src/sage/groups/libgap_mixin.py index 6d3655f8e6b..1fecbf4a146 100644 --- a/src/sage/groups/libgap_mixin.py +++ b/src/sage/groups/libgap_mixin.py @@ -13,6 +13,7 @@ from sage.libs.all import libgap from sage.misc.cachefunc import cached_method +from sage.groups.class_function import ClassFunction_libgap class GroupMixinLibGAP(object): @@ -287,11 +288,92 @@ def irreducible_characters(self): """ Irr = self.gap().Irr() L = [] - from sage.groups.class_function import ClassFunction_libgap for irr in Irr: L.append(ClassFunction_libgap(self, irr)) return tuple(L) + def character(self, values): + r""" + Returns a group character from ``values``, where ``values`` is + a list of the values of the character evaluated on the conjugacy + classes. + + INPUT: + + - ``values`` - a list of values of the character + + OUTPUT: a group character + + EXAMPLES:: + + sage: G = MatrixGroup(AlternatingGroup(4)) + sage: G.character([1]*len(G.conjugacy_class_representatives())) + Character of Matrix group over Integer Ring with 12 generators + """ + return ClassFunction_libgap(self, values) + + def trivial_character(self): + r""" + Returns the trivial character of this group. + + OUTPUT: a group character + + EXAMPLES:: + + sage: MatrixGroup(SymmetricGroup(3)).trivial_character() + Character of Matrix group over Integer Ring with 6 generators + """ + values = [1]*self._gap_().NrConjugacyClasses().sage() + return self.character(values) + + def character_table(self): + r""" + Returns the matrix of values of the irreducible characters of this + group `G` at its conjugacy classes. + + The columns represent the conjugacy classes of + `G` and the rows represent the different irreducible + characters in the ordering given by GAP. + + OUTPUT: a matrix defined over a cyclotomic field + + EXAMPLES:: + + sage: MatrixGroup(SymmetricGroup(2)).character_table() + [ 1 -1] + [ 1 1] + sage: MatrixGroup(SymmetricGroup(3)).character_table() + [ 1 1 -1] + [ 2 -1 0] + [ 1 1 1] + sage: MatrixGroup(SymmetricGroup(5)).character_table() + [ 1 1 1 1 1 1 1] + [ 1 -1 -1 1 -1 1 1] + [ 4 0 1 -1 -2 1 0] + [ 4 0 -1 -1 2 1 0] + [ 5 -1 1 0 1 -1 1] + [ 5 1 -1 0 -1 -1 1] + [ 6 0 0 1 0 0 -2] + """ + #code from function in permgroup.py, but modified for + #how gap handles these groups. + G = self._gap_() + cl = self.conjugacy_classes() + from sage.rings.all import Integer + n = Integer(len(cl)) + irrG = G.Irr() + ct = [[irrG[i][j] for j in range(n)] for i in range(n)] + + from sage.rings.all import CyclotomicField + e = irrG.Flat().Conductor() + K = CyclotomicField(e) + ct = [[K(x) for x in v] for v in ct] + + # Finally return the result as a matrix. + from sage.matrix.all import MatrixSpace + MS = MatrixSpace(K, n) + return MS(ct) + def random_element(self): """ Return a random element of this group. From bbeb9db51168fe2968d23f5ca36a703b04c73620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Sat, 8 Apr 2017 11:26:27 -0400 Subject: [PATCH 055/151] 22765: fixed another reST typo --- src/sage/categories/finite_permutation_groups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/finite_permutation_groups.py b/src/sage/categories/finite_permutation_groups.py index 0c585a8530e..55704b8890f 100644 --- a/src/sage/categories/finite_permutation_groups.py +++ b/src/sage/categories/finite_permutation_groups.py @@ -114,7 +114,7 @@ def cycle_index(self, parent = None): (default: the symmetric functions over the rational field in the `p` basis) The *cycle index* of a permutation group `G` - (:wikipedia`Cycle_index`) is a gadget counting the + (:wikipedia:`Cycle_index`) is a gadget counting the elements of `G` by cycle type, averaged over the group: .. MATH:: From 16eb5ec8e61512d3939bddbaac452b7e605d937a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Sat, 8 Apr 2017 12:07:45 -0400 Subject: [PATCH 056/151] 22765: use sum_of_terms for conciseness and efficiency --- src/sage/categories/finite_permutation_groups.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/finite_permutation_groups.py b/src/sage/categories/finite_permutation_groups.py index 55704b8890f..50dce5a5d3e 100644 --- a/src/sage/categories/finite_permutation_groups.py +++ b/src/sage/categories/finite_permutation_groups.py @@ -226,10 +226,9 @@ def cycle_index(self, parent = None): elif not hasattr(parent, "term") and hasattr(parent, "sum"): raise ValueError("`parent` should be (or behave as) a free module with basis indexed by partitions") base_ring = parent.base_ring() - return parent.sum( parent.term( C.an_element().cycle_type(), base_ring(C.cardinality()) ) - for C in self.conjugacy_classes() - ) / self.cardinality() - + return parent.sum_of_terms([C.an_element().cycle_type(), base_ring(C.cardinality())] + for C in self.conjugacy_classes() + ) / self.cardinality() class ElementMethods: # TODO: put abstract_methods for From ab49413b6ee1f7eebcc9044caf2c0abc45aad746 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sun, 9 Apr 2017 09:32:58 -0700 Subject: [PATCH 057/151] trac 22764: change how check whether Sage is built. --- src/bin/sage | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bin/sage b/src/bin/sage index cc6e41cbedb..d28cba2c0ce 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -383,7 +383,15 @@ fi # Prepare for running Sage, either interactively or non-interactively. sage_setup() { # Check that we're not in a source tarball which hasn't been built yet (#13561). - if ! python -c 'import sage.version' 2> /dev/null ; then + if [ ! -x "$SAGE_LOCAL/bin/python" ]; then + sage_built=no + else + PYTHON_VERSION=$("$SAGE_LOCAL/bin/python" -c 'import sys; print("%d.%d" % sys.version_info[:2])') + if [ ! -d "$SAGE_LOCAL/lib/python$PYTHON_VERSION/site-packages/sage" ]; then + sage_built=no + fi + fi + if [ $sage_built = no ]; then echo >&2 '************************************************************************' echo >&2 'It seems that you are attempting to run Sage from an unpacked source' echo >&2 'tarball, but you have not compiled it yet (or maybe the build has not' From d2624dcff12f84ea48d7bd30c9c12b4ec20c7dbf Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 29 Mar 2017 11:11:03 +0100 Subject: [PATCH 058/151] workaround to skip cephes' complex.h, bump up version Once FreeBSD's libm becomes more standard, so that cephes could be removed, this should go, too. --- build/pkgs/numpy/package-version.txt | 2 +- build/pkgs/numpy/spkg-install | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index 34abf6a9292..64484d5b26e 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.11.1.p0 +1.11.1.p1 diff --git a/build/pkgs/numpy/spkg-install b/build/pkgs/numpy/spkg-install index 1082a08f03f..0249cc52edf 100755 --- a/build/pkgs/numpy/spkg-install +++ b/build/pkgs/numpy/spkg-install @@ -21,6 +21,10 @@ if [ "x$SAGE64" = xyes ] ; then export CC fi +# Make sure we do not include conflicting complex.h from cephes Sage package +CC="$CC -D__CEPHES_COMPLEX_H" +export CC + cd src set -e From 2a3053dc93e93d1b564d7a3eb3ec734766ee25a5 Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Sun, 9 Apr 2017 20:55:38 -0500 Subject: [PATCH 059/151] 22783: rename function and add finite check --- src/doc/de/tutorial/tour_groups.rst | 2 +- src/doc/en/tutorial/tour_groups.rst | 2 +- src/doc/es/tutorial/tour_groups.rst | 2 +- src/doc/fr/tutorial/tour_groups.rst | 2 +- src/doc/ja/tutorial/tour_groups.rst | 2 +- src/doc/pt/tutorial/tour_groups.rst | 2 +- src/doc/ru/tutorial/tour_groups.rst | 2 +- src/sage/groups/class_function.py | 4 +- src/sage/groups/libgap_mixin.py | 67 ++++++++++++++++--- .../groups/matrix_gps/finitely_generated.py | 2 +- src/sage/groups/matrix_gps/linear.py | 2 +- src/sage/groups/matrix_gps/named_group.py | 2 +- 12 files changed, 71 insertions(+), 20 deletions(-) diff --git a/src/doc/de/tutorial/tour_groups.rst b/src/doc/de/tutorial/tour_groups.rst index 1b9fc92151a..db3a9441688 100644 --- a/src/doc/de/tutorial/tour_groups.rst +++ b/src/doc/de/tutorial/tour_groups.rst @@ -50,7 +50,7 @@ Sage beinhaltet auch klassische und Matrixgruppen über endlichen Körpern: sage: MS = MatrixSpace(GF(7), 2) sage: gens = [MS([[1,0],[-1,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 6] [0 4] [6 0] [0 6] [0 4] [0 6] [0 6] [0 6] [4 0] [0 1], [1 5], [5 5], [0 6], [1 2], [5 2], [1 0], [1 4], [1 3], [0 2], diff --git a/src/doc/en/tutorial/tour_groups.rst b/src/doc/en/tutorial/tour_groups.rst index 4ea7f0928d6..c1ee7563381 100644 --- a/src/doc/en/tutorial/tour_groups.rst +++ b/src/doc/en/tutorial/tour_groups.rst @@ -48,7 +48,7 @@ Sage also includes classical and matrix groups over finite fields: sage: MS = MatrixSpace(GF(7), 2) sage: gens = [MS([[1,0],[-1,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 6] [0 4] [6 0] [0 6] [0 4] [0 6] [0 6] [0 6] [4 0] [0 1], [1 5], [5 5], [0 6], [1 2], [5 2], [1 0], [1 4], [1 3], [0 2], diff --git a/src/doc/es/tutorial/tour_groups.rst b/src/doc/es/tutorial/tour_groups.rst index daefb57fcb5..e57a3c72637 100644 --- a/src/doc/es/tutorial/tour_groups.rst +++ b/src/doc/es/tutorial/tour_groups.rst @@ -54,7 +54,7 @@ finitos: sage: MS = MatrixSpace(GF(7), 2) sage: gens = [MS([[1,0],[-1,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 6] [0 4] [6 0] [0 6] [0 4] [0 6] [0 6] [0 6] [4 0] [0 1], [1 5], [5 5], [0 6], [1 2], [5 2], [1 0], [1 4], [1 3], [0 2], diff --git a/src/doc/fr/tutorial/tour_groups.rst b/src/doc/fr/tutorial/tour_groups.rst index 5bf6d83eab6..5963036008f 100644 --- a/src/doc/fr/tutorial/tour_groups.rst +++ b/src/doc/fr/tutorial/tour_groups.rst @@ -50,7 +50,7 @@ corps finis : sage: MS = MatrixSpace(GF(7), 2) sage: gens = [MS([[1,0],[-1,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 6] [0 4] [6 0] [0 6] [0 4] [0 6] [0 6] [0 6] [4 0] [0 1], [1 5], [5 5], [0 6], [1 2], [5 2], [1 0], [1 4], [1 3], [0 2], diff --git a/src/doc/ja/tutorial/tour_groups.rst b/src/doc/ja/tutorial/tour_groups.rst index af59907ca70..d62d8ed2ee2 100644 --- a/src/doc/ja/tutorial/tour_groups.rst +++ b/src/doc/ja/tutorial/tour_groups.rst @@ -51,7 +51,7 @@ Sageは有限体上の古典群と行列群も扱うことができる: sage: MS = MatrixSpace(GF(7), 2) sage: gens = [MS([[1,0],[-1,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 6] [0 4] [6 0] [0 6] [0 4] [0 6] [0 6] [0 6] [4 0] [0 1], [1 5], [5 5], [0 6], [1 2], [5 2], [1 0], [1 4], [1 3], [0 2], diff --git a/src/doc/pt/tutorial/tour_groups.rst b/src/doc/pt/tutorial/tour_groups.rst index a425f894586..a60c2d5d9f7 100644 --- a/src/doc/pt/tutorial/tour_groups.rst +++ b/src/doc/pt/tutorial/tour_groups.rst @@ -50,7 +50,7 @@ O Sage também inclui grupos clássicos matriciais sobre corpos finitos: sage: MS = MatrixSpace(GF(7), 2) sage: gens = [MS([[1,0],[-1,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 6] [0 4] [6 0] [0 6] [0 4] [0 6] [0 6] [0 6] [4 0] [0 1], [1 5], [5 5], [0 6], [1 2], [5 2], [1 0], [1 4], [1 3], [0 2], diff --git a/src/doc/ru/tutorial/tour_groups.rst b/src/doc/ru/tutorial/tour_groups.rst index bc774254897..38b8e7b1f30 100644 --- a/src/doc/ru/tutorial/tour_groups.rst +++ b/src/doc/ru/tutorial/tour_groups.rst @@ -48,7 +48,7 @@ Sage также включает в себя классические и мат sage: MS = MatrixSpace(GF(7), 2) sage: gens = [MS([[1,0],[-1,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 6] [0 4] [6 0] [0 6] [0 4] [0 6] [0 6] [0 6] [4 0] [0 1], [1 5], [5 5], [0 6], [1 2], [5 2], [1 0], [1 4], [1 3], [0 2], diff --git a/src/sage/groups/class_function.py b/src/sage/groups/class_function.py index 966e3f15edd..ef649d2ac2e 100644 --- a/src/sage/groups/class_function.py +++ b/src/sage/groups/class_function.py @@ -267,7 +267,7 @@ def __call__(self, g): zeta3 sage: G = GL(2,3) sage: chi = G.irreducible_characters()[3] - sage: g = G.conjugacy_class_representatives()[6] + sage: g = G.conjugacy_classes_representatives()[6] sage: chi(g) zeta8^3 + zeta8 @@ -950,7 +950,7 @@ def __call__(self, g): sage: G = GL(2,3) sage: chi = G.irreducible_characters()[3] - sage: g = G.conjugacy_class_representatives()[6] + sage: g = G.conjugacy_classes_representatives()[6] sage: chi(g) zeta8^3 + zeta8 diff --git a/src/sage/groups/libgap_mixin.py b/src/sage/groups/libgap_mixin.py index 1fecbf4a146..680eb0d6881 100644 --- a/src/sage/groups/libgap_mixin.py +++ b/src/sage/groups/libgap_mixin.py @@ -14,6 +14,8 @@ from sage.libs.all import libgap from sage.misc.cachefunc import cached_method from sage.groups.class_function import ClassFunction_libgap +from sage.misc.superseded import deprecated_function_alias + class GroupMixinLibGAP(object): @@ -103,7 +105,7 @@ def cardinality(self): order = cardinality @cached_method - def conjugacy_class_representatives(self): + def conjugacy_classes_representatives(self): """ Return a set of representatives for each of the conjugacy classes of the group. @@ -111,23 +113,35 @@ def conjugacy_class_representatives(self): EXAMPLES:: sage: G = SU(3,GF(2)) - sage: len(G.conjugacy_class_representatives()) + sage: len(G.conjugacy_classes_representatives()) 16 sage: G = GL(2,GF(3)) - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 2] [2 0] [0 2] [0 2] [0 1] [0 1] [2 0] [0 1], [1 1], [0 2], [1 2], [1 0], [1 2], [1 1], [0 1] ) - sage: len(GU(2,GF(5)).conjugacy_class_representatives()) + sage: len(GU(2,GF(5)).conjugacy_classes_representatives()) 36 + + :: + + sage: GL(2,ZZ).conjugacy_classes_representatives() + Traceback (most recent call last): + ... + NotImplementedError: Only implemented for finite groups + """ + if not self.is_finite(): + raise NotImplementedError("Only implemented for finite groups") G = self.gap() reps = [ cc.Representative() for cc in G.ConjugacyClasses() ] return tuple(self(g) for g in reps) + conjugacy_class_representatives = deprecated_function_alias(22783, conjugacy_classes_representatives) + def conjugacy_classes(self): r""" Return a list with all the conjugacy classes of ``self``. @@ -142,9 +156,18 @@ def conjugacy_classes(self): [1 0] in Special Linear Group of degree 2 over Finite Field of size 2, Conjugacy class of [0 1] [1 1] in Special Linear Group of degree 2 over Finite Field of size 2) + + :: + + sage: GL(2,ZZ).conjugacy_classes() + Traceback (most recent call last): + ... + NotImplementedError: Only implemented for finite groups """ + if not self.is_finite(): + raise NotImplementedError("Only implemented for finite groups") from sage.groups.conjugacy_classes import ConjugacyClassGAP - return tuple(ConjugacyClassGAP(self, self(g)) for g in self.conjugacy_class_representatives()) + return tuple(ConjugacyClassGAP(self, self(g)) for g in self.conjugacy_classes_representatives()) def conjugacy_class(self, g): r""" @@ -272,7 +295,7 @@ def intersection(self, other): @cached_method def irreducible_characters(self): """ - Returns the irreducible characters of the group. + Return the irreducible characters of the group. OUTPUT: @@ -285,7 +308,16 @@ def irreducible_characters(self): (Character of General Linear Group of degree 2 over Finite Field of size 2, Character of General Linear Group of degree 2 over Finite Field of size 2, Character of General Linear Group of degree 2 over Finite Field of size 2) + + :: + + sage: GL(2,ZZ).irreducible_characters() + Traceback (most recent call last): + ... + NotImplementedError: Only implemented for finite groups """ + if not self.is_finite(): + raise NotImplementedError("Only implemented for finite groups") Irr = self.gap().Irr() L = [] for irr in Irr: @@ -300,16 +332,26 @@ def character(self, values): INPUT: - - ``values`` - a list of values of the character + - ``values`` -- a list of values of the character OUTPUT: a group character EXAMPLES:: sage: G = MatrixGroup(AlternatingGroup(4)) - sage: G.character([1]*len(G.conjugacy_class_representatives())) + sage: G.character([1]*len(G.conjugacy_classes_representatives())) Character of Matrix group over Integer Ring with 12 generators + + :: + + sage: G = GL(2,ZZ) + sage: G.character([1,1,1,1]) + Traceback (most recent call last): + ... + NotImplementedError: Only implemented for finite groups """ + if not self.is_finite(): + raise NotImplementedError("Only implemented for finite groups") return ClassFunction_libgap(self, values) def trivial_character(self): @@ -322,7 +364,16 @@ def trivial_character(self): sage: MatrixGroup(SymmetricGroup(3)).trivial_character() Character of Matrix group over Integer Ring with 6 generators + + :: + + sage: GL(2,ZZ).trivial_character() + Traceback (most recent call last): + ... + NotImplementedError: Only implemented for finite groups """ + if not self.is_finite(): + raise NotImplementedError("Only implemented for finite groups") values = [1]*self._gap_().NrConjugacyClasses().sage() return self.character(values) diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index d00e1fca664..0e44570af96 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -9,7 +9,7 @@ sage: F = GF(3) sage: gens = [matrix(F,2, [1,0, -1,1]), matrix(F,2, [1,1,0,1])] sage: G = MatrixGroup(gens) - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 2] [0 1] [2 0] [0 2] [0 1] [0 2] [0 1], [1 1], [2 1], [0 2], [1 2], [2 2], [1 0] diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 1ad05120308..f0bdd3b573d 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -18,7 +18,7 @@ Special Linear Group of degree 2 over Finite Field of size 3 sage: G.is_finite() True - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 2] [0 1] [2 0] [0 2] [0 1] [0 2] [0 1], [1 1], [2 1], [0 2], [1 2], [2 2], [1 0] diff --git a/src/sage/groups/matrix_gps/named_group.py b/src/sage/groups/matrix_gps/named_group.py index d27d97636ed..b29470830cf 100644 --- a/src/sage/groups/matrix_gps/named_group.py +++ b/src/sage/groups/matrix_gps/named_group.py @@ -12,7 +12,7 @@ Special Linear Group of degree 2 over Finite Field of size 3 sage: G.is_finite() True - sage: G.conjugacy_class_representatives() + sage: G.conjugacy_classes_representatives() ( [1 0] [0 2] [0 1] [2 0] [0 2] [0 1] [0 2] [0 1], [1 1], [2 1], [0 2], [1 2], [2 2], [1 0] From 84ab84a98d6c22480533d0d48c139c2662d4cf71 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 4 Apr 2017 17:15:54 +0200 Subject: [PATCH 060/151] Updated this patch not to mess with R_HOME_DIR if SAGE_LOCAL is *not* set. This makes it easier to test this patch set outside of Sage without having to think about it. --- build/pkgs/r/patches/hardcoded_dirs.patch | 45 ++++++++++++----------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/build/pkgs/r/patches/hardcoded_dirs.patch b/build/pkgs/r/patches/hardcoded_dirs.patch index 150b3796f56..3a67bb7b56b 100644 --- a/build/pkgs/r/patches/hardcoded_dirs.patch +++ b/build/pkgs/r/patches/hardcoded_dirs.patch @@ -5,26 +5,11 @@ Also remove the sed scripts hardcoding R_*_DIR's in the "frontend" R script. See Sage trac #9668. This allows to move Sage install tree. -diff -ru src/src/scripts/R.sh.in src.new/src/scripts/R.sh.in ---- src/src/scripts/R.sh.in 2011-10-03 00:02:35.000000000 +0200 -+++ src.new/src/scripts/R.sh.in 2012-03-30 17:17:12.409254535 +0200 -@@ -26,6 +26,12 @@ - esac - fi - -+# Make R_HOME_DIR relative to SAGE_LOCAL -+# unless SAGE_BUILDING_R is set (as spkg-install does). -+if test -z "$SAGE_BUILDING_R"; then -+ R_HOME_DIR="$SAGE_LOCAL/lib/R/" -+fi -+ - if test -n "${R_HOME}" && \ - test "${R_HOME}" != "${R_HOME_DIR}"; then - echo "WARNING: ignoring environment value of R_HOME" - ---- src/src/scripts/Makefile.in 2012-03-01 15:02:25.000000000 -0800 -+++ src/src/scripts/Makefile.in 2013-03-17 08:48:56.000000000 -0700 -@@ -89,12 +89,7 @@ +diff --git a/src/scripts/Makefile.in b/src/scripts/Makefile.in +index a7fa60d..ce504a0 100644 +--- a/src/scripts/Makefile.in ++++ b/src/scripts/Makefile.in +@@ -89,12 +89,7 @@ $(top_builddir)/libtool: install: installdirs install-cmds @rm -f $(DESTDIR)$(bindir)/R @@ -38,4 +23,22 @@ diff -ru src/src/scripts/R.sh.in src.new/src/scripts/R.sh.in @$(INSTALL_SCRIPT) "$(DESTDIR)$(Rexecbindir)/R" "$(DESTDIR)$(bindir)/R" @chmod 755 "$(DESTDIR)$(bindir)/R" "$(DESTDIR)$(Rexecbindir)/R" ## why of all the scripts does this alone chmod just one copy? - +diff --git a/src/scripts/R.sh.in b/src/scripts/R.sh.in +index 15ef631..51c23a6 100644 +--- a/src/scripts/R.sh.in ++++ b/src/scripts/R.sh.in +@@ -26,6 +26,14 @@ if test "${R_HOME_DIR}" = "@prefix@/@LIBnn@/R"; then + esac + fi + ++# Make R_HOME_DIR relative to SAGE_LOCAL (if SAGE_LOCAL is set) ++# unless SAGE_BUILDING_R is set (as spkg-install does). ++if test -n "$SAGE_LOCAL"; then ++ if test -z "$SAGE_BUILDING_R"; then ++ R_HOME_DIR="$SAGE_LOCAL/lib/R/" ++ fi ++fi ++ + if test -n "${R_HOME}" && \ + test "${R_HOME}" != "${R_HOME_DIR}"; then + echo "WARNING: ignoring environment value of R_HOME" From 0f4f542890ab66b6b995b7d05e1fbfb53011c0cf Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 4 Apr 2017 17:16:51 +0200 Subject: [PATCH 061/151] Added some basic comments to this patch The other changes were due to regenerating the patch files from my git repository for R --- .../r/patches/libcurl_https_support.patch | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/build/pkgs/r/patches/libcurl_https_support.patch b/build/pkgs/r/patches/libcurl_https_support.patch index 4cc18273979..068cf001343 100644 --- a/build/pkgs/r/patches/libcurl_https_support.patch +++ b/build/pkgs/r/patches/libcurl_https_support.patch @@ -1,7 +1,9 @@ -diff -druN R-3.3.1/configure R-3.3.1.patched/configure ---- R-3.3.1/configure 2016-06-13 22:16:32.000000000 +0000 -+++ R-3.3.1.patched/configure 2016-10-27 13:53:26.347387983 +0000 -@@ -36227,47 +36227,6 @@ +Don't check for HTTPS support in libcurl; see https://trac.sagemath.org/ticket/20523 +diff --git a/configure b/configure +index 8dfd4b6..0666c9b 100755 +--- a/configure ++++ b/configure +@@ -36239,47 +36239,6 @@ if test "x${r_cv_have_curl728}" = xno; then have_libcurl=no fi @@ -49,7 +51,7 @@ diff -druN R-3.3.1/configure R-3.3.1.patched/configure if test "x${have_libcurl}" = xyes; then $as_echo "#define HAVE_LIBCURL 1" >>confdefs.h -@@ -36277,7 +36236,7 @@ +@@ -36289,7 +36248,7 @@ $as_echo "#define HAVE_LIBCURL 1" >>confdefs.h else @@ -58,10 +60,11 @@ diff -druN R-3.3.1/configure R-3.3.1.patched/configure fi -diff -druN R-3.3.1/m4/R.m4 R-3.3.1.patched/m4/R.m4 ---- R-3.3.1/m4/R.m4 2016-03-16 23:02:06.000000000 +0000 -+++ R-3.3.1.patched/m4/R.m4 2016-10-27 13:52:20.247269003 +0000 -@@ -4196,33 +4196,14 @@ +diff --git a/m4/R.m4 b/m4/R.m4 +index 606adfa..43114dd 100644 +--- a/m4/R.m4 ++++ b/m4/R.m4 +@@ -4203,33 +4203,14 @@ if test "x${r_cv_have_curl728}" = xno; then have_libcurl=no fi From 1a02d2bfd17f98edea5eb39bbb909e60424419ca Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 4 Apr 2017 17:17:25 +0200 Subject: [PATCH 062/151] Updated this patch to also include the actual resulting updates to the configure script --- build/pkgs/r/patches/m4_macro_bug.patch | 31 +++++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/build/pkgs/r/patches/m4_macro_bug.patch b/build/pkgs/r/patches/m4_macro_bug.patch index 605ae91a5d8..c6d2c92cc2a 100644 --- a/build/pkgs/r/patches/m4_macro_bug.patch +++ b/build/pkgs/r/patches/m4_macro_bug.patch @@ -1,10 +1,31 @@ Fix bug in R_PCRE autoconf macro which leads to 'configure' losing '-lz' and/or '-lbz2' from LIBS (under certain circumstances, and only relevant if "system" versions of these libraries are used). (cf. #18229) - ---- R-3.3.1_original/m4/R.m4 2016-03-17 00:02:06.000000000 +0100 -+++ R-3.3.1_patched/m4/R.m4 2016-08-20 10:17:08.663006152 +0200 -@@ -3193,7 +3193,6 @@ +diff --git a/configure b/configure +index 0666c9b..5d80a54 100755 +--- a/configure ++++ b/configure +@@ -35969,7 +35969,6 @@ fi + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $r_cv_have_pcre810" >&5 + $as_echo "$r_cv_have_pcre810" >&6; } +-fi + if test "x${r_cv_have_pcre810}" != xyes; then + have_pcre=no + LIBS="${r_save_LIBS}" +@@ -36014,6 +36013,7 @@ fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $r_cv_have_pcre832" >&5 + $as_echo "$r_cv_have_pcre832" >&6; } + fi ++fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether PCRE support suffices" >&5 + $as_echo_n "checking whether PCRE support suffices... " >&6; } +diff --git a/m4/R.m4 b/m4/R.m4 +index 43114dd..7d2cdba 100644 +--- a/m4/R.m4 ++++ b/m4/R.m4 +@@ -3194,7 +3194,6 @@ int main() { #endif } ]])], [r_cv_have_pcre810=yes], [r_cv_have_pcre810=no], [r_cv_have_pcre810=no])]) @@ -12,7 +33,7 @@ if "system" versions of these libraries are used). (cf. #18229) if test "x${r_cv_have_pcre810}" != xyes; then have_pcre=no LIBS="${r_save_LIBS}" -@@ -3216,6 +3215,7 @@ +@@ -3217,6 +3216,7 @@ int main() { } ]])], [r_cv_have_pcre832=yes], [r_cv_have_pcre832=no], [r_cv_have_pcre832=no])]) fi From 5e9c424c3d9388c17273759519e84b191fa7b286 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 4 Apr 2017 17:19:18 +0200 Subject: [PATCH 063/151] Removed the old cygwin build patches that were not being applied. Added a new patch cygwin_build_support.patch, based partially on the old cygwin/cygwin.patch but simplified, and does not break building on other platforms. --- build/pkgs/r/patches/cygwin/cygwin.patch | 155 ------------------ .../r/patches/cygwin/undefined_symbols.patch | 14 -- .../pkgs/r/patches/cygwin_build_support.patch | 108 ++++++++++++ build/pkgs/r/spkg-install | 4 - 4 files changed, 108 insertions(+), 173 deletions(-) delete mode 100644 build/pkgs/r/patches/cygwin/cygwin.patch delete mode 100644 build/pkgs/r/patches/cygwin/undefined_symbols.patch create mode 100644 build/pkgs/r/patches/cygwin_build_support.patch diff --git a/build/pkgs/r/patches/cygwin/cygwin.patch b/build/pkgs/r/patches/cygwin/cygwin.patch deleted file mode 100644 index 6657214bf63..00000000000 --- a/build/pkgs/r/patches/cygwin/cygwin.patch +++ /dev/null @@ -1,155 +0,0 @@ -Add Cygwin support into configure. -Taken from Cygwin's R-conf.patch. ---- orig/configure.ac 2016-03-17 00:04:40.000000000 +0100 -+++ new/configure.ac 2016-06-26 15:52:27.680611400 +0200 -@@ -1316,6 +1316,15 @@ - shlib_cxxldflags="-shared ${shlib_cxxldflags}" - fi - ;; -+ cygwin*) -+ ## All Windows code is PIC -+ cpicflags= -+ cxxpicflags= -+ fpicflags= -+ fcpicflags= -+ SHLIB_EXT=".dll" -+ dylib_undefined_allowed=no -+ ;; - darwin*) - darwin_pic="-fPIC" - dylib_undefined_allowed=no -@@ -1576,7 +1585,7 @@ - : ${CPICFLAGS="${cpicflags}"} - if test -z "${CPICFLAGS}"; then - case "${host_os}" in -- aix*|irix*|mingw*|osf*) -+ aix*|cygwin*|irix*|mingw*|osf*) - ;; - *) - AC_MSG_WARN([I could not determine CPICFLAGS.]) -@@ -1588,7 +1597,7 @@ - : ${FPICFLAGS="${fpicflags}"} - if test -z "${FPICFLAGS}"; then - case "${host_os}" in -- aix*|irix*|mingw*|osf*) -+ aix*|cygwin*|irix*|mingw*|osf*) - ;; - *) - AC_MSG_WARN([I could not determine FPICFLAGS.]) -@@ -1600,7 +1619,7 @@ - : ${CXXPICFLAGS="${cxxpicflags}"} - if test -z "${CXXPICFLAGS}"; then - case "${host_os}" in -- aix*|irix*|mingw*|osf*) -+ aix*|cygwin*|irix*|mingw*|osf*) - ;; - *) - warn_cxxpicflags="I could not determine CXXPICFLAGS." -@@ -1655,6 +1664,9 @@ - # RLAPACK_LDFLAGS="${wl}-bE:\$(top_builddir)/etc/Rlapack.exp" - # LAPACK_LDFLAGS="${wl}-bI:\$(R_HOME)/etc/Rlapack.exp" - ;; -+ cygwin*) -+ dylib_ldflags="${SHLIB_LDFLAGS} ${wl}--out-implib ${wl}\$@.a" -+ ;; - darwin*) - DYLIB_EXT=".dylib" - dylib_ldflags="${darwin_dylib_ldflags}" - -Manually autogenerated stuff from the above. ---- orig/configure 2016-03-17 00:04:40.000000000 +0100 -+++ new/configure 2016-06-26 15:52:27.680611400 +0200 -@@ -28113,6 +28113,15 @@ - shlib_cxxldflags="-shared ${shlib_cxxldflags}" - fi - ;; -+ cygwin*) -+ ## All Windows code is PIC -+ cpicflags= -+ cxxpicflags= -+ fpicflags= -+ fcpicflags= -+ SHLIB_EXT=".dll" -+ dylib_undefined_allowed=no -+ ;; - darwin*) - darwin_pic="-fPIC" - dylib_undefined_allowed=no -@@ -28389,7 +28398,7 @@ - : ${CPICFLAGS="${cpicflags}"} - if test -z "${CPICFLAGS}"; then - case "${host_os}" in -- aix*|irix*|mingw*|osf*) -+ aix*|cygwin*|irix*|mingw*|osf*) - ;; - *) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: I could not determine CPICFLAGS." >&5 -@@ -28402,7 +28411,7 @@ - : ${FPICFLAGS="${fpicflags}"} - if test -z "${FPICFLAGS}"; then - case "${host_os}" in -- aix*|irix*|mingw*|osf*) -+ aix*|cygwin*|irix*|mingw*|osf*) - ;; - *) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: I could not determine FPICFLAGS." >&5 -@@ -28415,7 +28424,7 @@ - : ${CXXPICFLAGS="${cxxpicflags}"} - if test -z "${CXXPICFLAGS}"; then - case "${host_os}" in -- aix*|irix*|mingw*|osf*) -+ aix*|cygwin*|irix*|mingw*|osf*) - ;; - *) - warn_cxxpicflags="I could not determine CXXPICFLAGS." -@@ -28485,6 +28494,9 @@ - # RLAPACK_LDFLAGS="${wl}-bE:\$(top_builddir)/etc/Rlapack.exp" - # LAPACK_LDFLAGS="${wl}-bI:\$(R_HOME)/etc/Rlapack.exp" - ;; -+ cygwin*) -+ dylib_ldflags="${SHLIB_LDFLAGS} ${wl}--out-implib ${wl}\$@.a" -+ ;; - darwin*) - DYLIB_EXT=".dylib" - dylib_ldflags="${darwin_dylib_ldflags}" - -Chunks to install import libraries. -Taken from Cygwin's R-makeconf.patch. ---- orig/src/main/Makefile.in 2016-03-17 00:04:14.000000000 +0100 -+++ new/src/main/Makefile.in 2016-06-27 09:53:05.263801100 +0200 -@@ -198,7 +198,8 @@ - @$(SHELL) $(top_srcdir)/tools/copy-if-change $(R_binary) "$(DESTDIR)$(Rexecbindir2)/R" - install-lib: installdirs - @$(MKINSTALLDIRS) "$(DESTDIR)$(Rexeclibdir)" -- @$(SHELL) $(top_srcdir)/tools/copy-if-change $(libR_la) "$(DESTDIR)$(Rexeclibdir)/$(libR_la)" -+ @$(SHELL) $(top_srcdir)/tools/copy-if-change $(libR_la) "$(DESTDIR)$(bindir)/$(libR_la)" -+ @$(SHELL) $(top_srcdir)/tools/copy-if-change libR.dll.a "$(DESTDIR)$(Rexeclibdir)/libR.dll.a" - install-static: installdirs - @$(MKINSTALLDIRS) "$(DESTDIR)$(Rexeclibdir)" - @$(SHELL) $(top_srcdir)/tools/copy-if-change libR.a "$(DESTDIR)$(Rexeclibdir)/libR.a" ---- orig/src/nmath/standalone/Makefile.in 2015-12-11 00:15:18.000000000 +0100 -+++ new/src/nmath/standalone/Makefile.in 2016-06-27 10:28:26.614932800 +0200 -@@ -134,7 +134,8 @@ - -L. -lRmath $(LIBM) - - install: installdirs install-header @WANT_R_FRAMEWORK_FALSE@ install-pc --@WANT_R_FRAMEWORK_FALSE@ @!(test -f $(libRmath_la)) || $(SHELL) $(top_srcdir)/tools/copy-if-change $(libRmath_la) $(DESTDIR)$(libdir)/$(libRmath_la) -+@WANT_R_FRAMEWORK_FALSE@ @!(test -f $(libRmath_la)) || $(SHELL) $(top_srcdir)/tools/copy-if-change $(libRmath_la) $(DESTDIR)$(bindir)/$(libRmath_la) -+@WANT_R_FRAMEWORK_FALSE@ @!(test -f libRmath.dll.a) || $(SHELL) $(top_srcdir)/tools/copy-if-change libRmath.dll.a $(DESTDIR)$(libdir)/libRmath.dll.a - @WANT_R_FRAMEWORK_FALSE@ @!(test -f libRmath.a) || $(SHELL) $(top_srcdir)/tools/copy-if-change libRmath.a $(DESTDIR)$(libdir)/libRmath.a - @WANT_R_FRAMEWORK_TRUE@ @!(test -f $(libRmath_la)) || cp $(libRmath_la) $(R_FRAMEWORK_DIR)/Versions/$(FW_VERSION)/Resources/lib/$(libRmath_la) && install_name_tool -id $(R_FRAMEWORK_DIR)/Versions/$(FW_VERSION)/Resources/lib/$(libRmath_la) $(R_FRAMEWORK_DIR)/Versions/$(FW_VERSION)/Resources/lib/$(libRmath_la) - @WANT_R_FRAMEWORK_TRUE@ @!(test -f libRmath.a) || $(SHELL) $(top_srcdir)/tools/copy-if-change libRmath.a $(R_FRAMEWORK_DIR)/Versions/$(FW_VERSION)/Resources/lib/libRmath.a - -Make sure the R install command finds the R executable. -Taken from Cygwin's 2.12-install-arch.patch. ---- orig/src/library/tools/R/install.R 2011-02-15 17:05:01.000000000 -0600 -+++ new/src/library/tools/R/install.R 2011-03-07 20:28:34.946411600 -0600 -@@ -712,7 +712,7 @@ - setwd(owd) - test_archs <- archs - for(arch in archs) { -- if (arch == "R") { -+ if (arch == "R" || arch == "R.exe") { - ## top-level, so one arch without subdirs - has_error <- run_shlib(pkg_name, srcs, instdir, "") - } else { diff --git a/build/pkgs/r/patches/cygwin/undefined_symbols.patch b/build/pkgs/r/patches/cygwin/undefined_symbols.patch deleted file mode 100644 index 888443f3c95..00000000000 --- a/build/pkgs/r/patches/cygwin/undefined_symbols.patch +++ /dev/null @@ -1,14 +0,0 @@ -Properly list needed libs. -Taken from Cygwin's R-makeconf.patch. -Might be needed on other system as well to avoid underlinking. ---- orig/etc/Makeconf.in 2016-03-17 00:04:44.000000000 +0100 -+++ new/etc/Makeconf.in 2016-06-26 17:38:05.398615000 +0200 -@@ -123,7 +123,7 @@ - ALL_OBJCXXFLAGS = $(PKG_OBJCXXFLAGS) $(CXXPICFLAGS) $(SHLIB_CXXFLAGS) $(OBJCXXFLAGS) - ALL_FFLAGS = $(R_XTRA_FFLAGS) $(PKG_FFLAGS) $(FPICFLAGS) $(SHLIB_FFLAGS) $(FFLAGS) - ## LIBR here as a couple of packages use this without SHLIB_LINK --ALL_LIBS = $(PKG_LIBS) $(SHLIB_LIBADD) $(LIBR)@DYLIB_UNDEFINED_ALLOWED_FALSE@ $(LIBINTL) -+ALL_LIBS = $(PKG_LIBS) $(SHLIB_LIBADD) $(LIBR)@DYLIB_UNDEFINED_ALLOWED_FALSE@ $(LIBINTL) $(LIBS) - - .SUFFIXES: - .SUFFIXES: .c .cc .cpp .d .f .f90 .f95 .m .mm .M .o diff --git a/build/pkgs/r/patches/cygwin_build_support.patch b/build/pkgs/r/patches/cygwin_build_support.patch new file mode 100644 index 00000000000..bf7f6e5d7b8 --- /dev/null +++ b/build/pkgs/r/patches/cygwin_build_support.patch @@ -0,0 +1,108 @@ +Patches required to explicitly support Cygwin when building R. +diff --git a/configure b/configure +index 5d80a54..805aa0f 100755 +--- a/configure ++++ b/configure +@@ -28124,6 +28124,15 @@ case "${host_os}" in + shlib_cxxldflags="-shared ${shlib_cxxldflags}" + fi + ;; ++ cygwin*) ++ ## All Windows binaries are PIC ++ cpicflags= ++ cxxpicflags= ++ fpicflags= ++ fcpicflags= ++ SHLIB_EXT=".dll" ++ dylib_undefined_allowed=no ++ ;; + darwin*) + darwin_pic="-fPIC" + dylib_undefined_allowed=no +@@ -28400,7 +28409,7 @@ fi + : ${CPICFLAGS="${cpicflags}"} + if test -z "${CPICFLAGS}"; then + case "${host_os}" in +- aix*|irix*|mingw*|osf*) ++ aix*|cygwin*|irix*|mingw*|osf*) + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: I could not determine CPICFLAGS." >&5 +@@ -28413,7 +28422,7 @@ fi + : ${FPICFLAGS="${fpicflags}"} + if test -z "${FPICFLAGS}"; then + case "${host_os}" in +- aix*|irix*|mingw*|osf*) ++ aix*|cygwin*|irix*|mingw*|osf*) + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: I could not determine FPICFLAGS." >&5 +@@ -28426,7 +28435,7 @@ fi + : ${CXXPICFLAGS="${cxxpicflags}"} + if test -z "${CXXPICFLAGS}"; then + case "${host_os}" in +- aix*|irix*|mingw*|osf*) ++ aix*|cygwin*|irix*|mingw*|osf*) + ;; + *) + warn_cxxpicflags="I could not determine CXXPICFLAGS." +diff --git a/configure.ac b/configure.ac +index 4c6d7f1..005e940 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1324,6 +1324,15 @@ case "${host_os}" in + shlib_cxxldflags="-shared ${shlib_cxxldflags}" + fi + ;; ++ cygwin*) ++ ## All Windows binaries are PIC ++ cpicflags= ++ cxxpicflags= ++ fpicflags= ++ fcpicflags= ++ SHLIB_EXT=".dll" ++ dylib_undefined_allowed=no ++ ;; + darwin*) + darwin_pic="-fPIC" + dylib_undefined_allowed=no +@@ -1584,7 +1593,7 @@ R_SH_VAR_ADD(MAIN_LDFLAGS, [${main_ldflags}]) + : ${CPICFLAGS="${cpicflags}"} + if test -z "${CPICFLAGS}"; then + case "${host_os}" in +- aix*|irix*|mingw*|osf*) ++ aix*|cygwin*|irix*|mingw*|osf*) + ;; + *) + AC_MSG_WARN([I could not determine CPICFLAGS.]) +@@ -1596,7 +1605,7 @@ fi + : ${FPICFLAGS="${fpicflags}"} + if test -z "${FPICFLAGS}"; then + case "${host_os}" in +- aix*|irix*|mingw*|osf*) ++ aix*|cygwin*|irix*|mingw*|osf*) + ;; + *) + AC_MSG_WARN([I could not determine FPICFLAGS.]) +@@ -1608,7 +1617,7 @@ fi + : ${CXXPICFLAGS="${cxxpicflags}"} + if test -z "${CXXPICFLAGS}"; then + case "${host_os}" in +- aix*|irix*|mingw*|osf*) ++ aix*|cygwin*|irix*|mingw*|osf*) + ;; + *) + warn_cxxpicflags="I could not determine CXXPICFLAGS." +diff --git a/src/library/tools/R/install.R b/src/library/tools/R/install.R +index cdfd6dd..c99cde3 100644 +--- a/src/library/tools/R/install.R ++++ b/src/library/tools/R/install.R +@@ -828,7 +828,7 @@ + setwd(owd) + test_archs <- archs + for(arch in archs) { +- if (arch == "R") { ++ if (arch == "R" || arch == "R.exe") { + ## top-level, so one arch without subdirs + has_error <- run_shlib(pkg_name, srcs, instdir, "") + } else { diff --git a/build/pkgs/r/spkg-install b/build/pkgs/r/spkg-install index 9cea0c33963..208f900c1b6 100755 --- a/build/pkgs/r/spkg-install +++ b/build/pkgs/r/spkg-install @@ -110,10 +110,6 @@ fi # See patches/R.sh.patch export SAGE_BUILDING_R=yes -# Move patches only applied on Cygwin. -if [ "$UNAME" = "CYGWIN" ]; then - mv patches/cygwin/*.patch patches/ -fi config() { From 6fb42c2737d21153348393bf3e16384369ea9f95 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 5 Apr 2017 15:17:01 +0200 Subject: [PATCH 064/151] Update R patch level --- build/pkgs/r/package-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/r/package-version.txt b/build/pkgs/r/package-version.txt index b0360883a51..a1a438ccd79 100644 --- a/build/pkgs/r/package-version.txt +++ b/build/pkgs/r/package-version.txt @@ -1 +1 @@ -3.3.3.p1 +3.3.3.p2 From 81dcaba8f63d5ba43f52d80f20474d7859806ee2 Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Mon, 10 Apr 2017 06:48:23 -0500 Subject: [PATCH 065/151] 22783: make error message lowercase --- src/sage/groups/libgap_mixin.py | 20 ++++++++++---------- src/sage/groups/matrix_gps/named_group.py | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/groups/libgap_mixin.py b/src/sage/groups/libgap_mixin.py index 680eb0d6881..f94d8b47d7d 100644 --- a/src/sage/groups/libgap_mixin.py +++ b/src/sage/groups/libgap_mixin.py @@ -131,11 +131,11 @@ def conjugacy_classes_representatives(self): sage: GL(2,ZZ).conjugacy_classes_representatives() Traceback (most recent call last): ... - NotImplementedError: Only implemented for finite groups + NotImplementedError: only implemented for finite groups """ if not self.is_finite(): - raise NotImplementedError("Only implemented for finite groups") + raise NotImplementedError("only implemented for finite groups") G = self.gap() reps = [ cc.Representative() for cc in G.ConjugacyClasses() ] return tuple(self(g) for g in reps) @@ -162,10 +162,10 @@ def conjugacy_classes(self): sage: GL(2,ZZ).conjugacy_classes() Traceback (most recent call last): ... - NotImplementedError: Only implemented for finite groups + NotImplementedError: only implemented for finite groups """ if not self.is_finite(): - raise NotImplementedError("Only implemented for finite groups") + raise NotImplementedError("only implemented for finite groups") from sage.groups.conjugacy_classes import ConjugacyClassGAP return tuple(ConjugacyClassGAP(self, self(g)) for g in self.conjugacy_classes_representatives()) @@ -314,10 +314,10 @@ def irreducible_characters(self): sage: GL(2,ZZ).irreducible_characters() Traceback (most recent call last): ... - NotImplementedError: Only implemented for finite groups + NotImplementedError: only implemented for finite groups """ if not self.is_finite(): - raise NotImplementedError("Only implemented for finite groups") + raise NotImplementedError("only implemented for finite groups") Irr = self.gap().Irr() L = [] for irr in Irr: @@ -348,10 +348,10 @@ def character(self, values): sage: G.character([1,1,1,1]) Traceback (most recent call last): ... - NotImplementedError: Only implemented for finite groups + NotImplementedError: only implemented for finite groups """ if not self.is_finite(): - raise NotImplementedError("Only implemented for finite groups") + raise NotImplementedError("only implemented for finite groups") return ClassFunction_libgap(self, values) def trivial_character(self): @@ -370,10 +370,10 @@ def trivial_character(self): sage: GL(2,ZZ).trivial_character() Traceback (most recent call last): ... - NotImplementedError: Only implemented for finite groups + NotImplementedError: only implemented for finite groups """ if not self.is_finite(): - raise NotImplementedError("Only implemented for finite groups") + raise NotImplementedError("only implemented for finite groups") values = [1]*self._gap_().NrConjugacyClasses().sage() return self.character(values) diff --git a/src/sage/groups/matrix_gps/named_group.py b/src/sage/groups/matrix_gps/named_group.py index b29470830cf..c74938fe784 100644 --- a/src/sage/groups/matrix_gps/named_group.py +++ b/src/sage/groups/matrix_gps/named_group.py @@ -128,7 +128,7 @@ def __init__(self, degree, base_ring, special, sage_name, latex_string): - ``degree`` -- integer. The degree (number of rows/columns of matrices). - - ``base_ring`` -- rinrg. The base ring of the matrices. + - ``base_ring`` -- ring. The base ring of the matrices. - ``special`` -- boolean. Whether the matrix group is special, that is, elements have determinant one. From 883bb4a6fd679c6f0f193db108a6189b8cddf4ad Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Tue, 11 Apr 2017 10:48:13 +0200 Subject: [PATCH 066/151] remove tab character in a reference --- src/doc/en/reference/references/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index c5644608d27..2d3f5a26fa9 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -790,7 +790,7 @@ REFERENCES: Comput. 33 (2002), no. 4, 425--445. .. [Hig2008] \N. J. Higham, "Functions of matrices: theory and computation", - Society for Industrial and Applied Mathematics (2008). + Society for Industrial and Applied Mathematics (2008). .. [HJ2004] Tom Hoeholdt and Joern Justesen, A Course In Error-Correcting Codes, EMS, 2004 From dbdc7165a0f812041fa2c25a38becc9ce5f8ddbc Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 11 Apr 2017 12:09:05 +0100 Subject: [PATCH 067/151] moved -D from CC to CFLAGS --- build/pkgs/numpy/spkg-install | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/numpy/spkg-install b/build/pkgs/numpy/spkg-install index 2a4521b4be5..6e2951b3a8e 100755 --- a/build/pkgs/numpy/spkg-install +++ b/build/pkgs/numpy/spkg-install @@ -20,8 +20,8 @@ if [ "x$SAGE64" = xyes ] ; then fi # Make sure we do not include conflicting complex.h from cephes Sage package -CC="$CC -D__CEPHES_COMPLEX_H" -export CC +CFLAGS="$CFLAGS -D__CEPHES_COMPLEX_H" +export CFLAGS cd src From b2beb0478bbc9511039dd524f9c92a4170b0063b Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 7 Apr 2017 11:18:46 +0200 Subject: [PATCH 068/151] six.with_metaclass: override __call__ instead of __new__ --- src/sage/misc/six.py | 67 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/src/sage/misc/six.py b/src/sage/misc/six.py index 8273d91b759..5f03a969683 100644 --- a/src/sage/misc/six.py +++ b/src/sage/misc/six.py @@ -8,7 +8,7 @@ from six import * -def with_metaclass(meta, *bases): +def with_metaclass(*args): """ Create a base class with a metaclass. @@ -35,7 +35,7 @@ def with_metaclass(meta, *bases): True sage: issubclass(X, Base2) True - sage: X.__mro__ == (X, Base, Base2, object) + sage: X.__mro__ == (X, Base, Base2, object) or X.__mro__ True Check that :trac:`18503` is fixed, i.e. that with_metaclass @@ -45,16 +45,63 @@ def with_metaclass(meta, *bases): sage: class X(with_metaclass(ClasscallMetaclass)): pass sage: type(X) is ClasscallMetaclass True - sage: X.__mro__ == (X, object) + sage: X.__mro__ == (X, object) or X.__mro__ True + + Check a fix for :trac:`16074`:: + + sage: from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass + sage: from sage.modules.with_basis.morphism import ModuleMorphismByLinearity + sage: from sage.structure.unique_representation import UniqueRepresentation + sage: class ExteriorAlgebraDifferential(with_metaclass( + ....: InheritComparisonClasscallMetaclass, + ....: ModuleMorphismByLinearity, UniqueRepresentation + ....: )): + ....: pass + """ + # This requires a bit of explanation: with_metaclass() returns + # a dummy class which will setup the correct metaclass when this + # dummy class is used as base class. + # + # In detail: let T = with_metaclass(*args). When the user does + # "class X(with_metaclass(*args))", Python will first determine + # the metaclass of X from its bases. So, the metaclass will be + # type(T). + # Next, Python will call type(T)("X", (T,), methods) and it is this + # call that we want to override. So we need to define a __call__ + # method in the metaclass of type(T), which needs a metametaclass, + # called "_WithMetaclass". + # The metaclass type(T) is returned by _WithMetaclass.get(...) and + # the dummy() method creates an instance of this metaclass, which + # is a regular class. + return _WithMetaclass.get(*args).dummy() + + +class _WithMetaclass(type): + """ + Metaclasses to be used in ``with_metaclass``. """ - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - def __new__(cls, name, _, __dict__): - return meta.__new__(meta, name, bases, __dict__) - return type.__new__(metaclass, 'temporary_class', (), {}) + @classmethod + def get(cls, meta, *bases): + """ + Return a metaclass which, if an instance of it is used as base + class, will create a class with metaclass ``meta`` and bases + ``bases``. + """ + return cls("metaclass", (type,), dict(meta=meta, bases=bases)) + + def dummy(self): + """ + Return a dummy instance of this metaclass. + """ + return self.__new__(self, "dummy", (object,), {}) + + def __call__(self, name, _unused_bases, d): + """ + Create the eventual class with metaclass ``self.meta`` and bases + ``self.bases``. + """ + return self.meta(name, self.bases, d) def u(x): From b2108e6ba357a38ac475b2def651ad9720fd24e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 6 Apr 2017 14:01:27 +0200 Subject: [PATCH 069/151] Replace __metaclass__ with six.with_metaclass --- src/doc/common/conf.py | 2 +- src/sage/algebras/clifford_algebra.py | 17 ++++++++++------- src/sage/algebras/commutative_dga.py | 10 ++++++---- src/sage/misc/bindable_class.py | 19 +++++++++++-------- .../graded_ring_element.py | 10 ++++++---- src/sage/structure/unique_representation.py | 5 ++--- 6 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/doc/common/conf.py b/src/doc/common/conf.py index 7ec52df4794..ad6c42ed574 100644 --- a/src/doc/common/conf.py +++ b/src/doc/common/conf.py @@ -520,7 +520,7 @@ def check_nested_class_picklability(app, what, name, obj, skip, options): v.__module__ not in skip_picklability_check_modules): # OK, probably this is an *unpicklable* nested class. app.warn('Pickling of nested class %r is probably broken. ' - 'Please set __metaclass__ of the parent class to ' + 'Please set the metaclass of the parent class to ' 'sage.misc.nested_class.NestedClassMetaclass.' % ( v.__module__ + '.' + name + '.' + nm)) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 04a7faf71e4..a7983b7c5f4 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -7,12 +7,16 @@ """ #***************************************************************************** -# Copyright (C) 2013 Travis Scrimshaw +# Copyright (C) 2013 Travis Scrimshaw # -# Distributed under the terms of the GNU General Public License (GPL) +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc import six from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation from copy import copy @@ -32,8 +36,6 @@ from sage.algebras.weyl_algebra import repr_from_monomials from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -import six - class CliffordAlgebraElement(CombinatorialFreeModule.Element): """ @@ -2158,7 +2160,10 @@ def scalar(self, other): ##################################################################### ## Differentials -class ExteriorAlgebraDifferential(ModuleMorphismByLinearity, UniqueRepresentation): +class ExteriorAlgebraDifferential(six.with_metaclass( + InheritComparisonClasscallMetaclass, + ModuleMorphismByLinearity, UniqueRepresentation + )): r""" Internal class to store the data of a boundary or coboundary of an exterior algebra `\Lambda(L)` defined by the structure @@ -2173,8 +2178,6 @@ class ExteriorAlgebraDifferential(ModuleMorphismByLinearity, UniqueRepresentatio This is not a general class for differentials on the exterior algebra. """ - __metaclass__ = InheritComparisonClasscallMetaclass - @staticmethod def __classcall__(cls, E, s_coeff): """ diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index 022bdad6d5d..a44f14a0f90 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -73,7 +73,7 @@ #***************************************************************************** from __future__ import print_function -import six +from sage.misc import six from sage.structure.unique_representation import UniqueRepresentation from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method @@ -97,7 +97,11 @@ from sage.rings.quotient_ring import QuotientRing_nc from sage.rings.quotient_ring_element import QuotientRingElement -class Differential(UniqueRepresentation, Morphism): + +class Differential(six.with_metaclass( + InheritComparisonClasscallMetaclass, + UniqueRepresentation, Morphism + )): r""" Differential of a commutative graded algebra. @@ -119,8 +123,6 @@ class Differential(UniqueRepresentation, Morphism): sage: B.differential()(x) x*y """ - __metaclass__ = InheritComparisonClasscallMetaclass - @staticmethod def __classcall__(cls, A, im_gens): r""" diff --git a/src/sage/misc/bindable_class.py b/src/sage/misc/bindable_class.py index 47475eeb953..84d4d2cd6cc 100644 --- a/src/sage/misc/bindable_class.py +++ b/src/sage/misc/bindable_class.py @@ -1,19 +1,25 @@ """ Bindable classes """ + #***************************************************************************** # Copyright (C) 2012 Nicolas M. Thiery # -# Distributed under the terms of the GNU General Public License (GPL) +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function + +from __future__ import absolute_import, print_function import functools +from sage.misc import six from sage.misc.nested_class import NestedClassMetaclass from sage.misc.classcall_metaclass import ClasscallMetaclass -class BindableClass(object): +class BindableClass(six.with_metaclass(ClasscallMetaclass)): """ Bindable classes @@ -125,8 +131,6 @@ class BindableClass(object): sage: outer = Outer() sage: TestSuite(outer.Inner).run(skip=["_test_pickling"]) """ - __metaclass__ = ClasscallMetaclass - @staticmethod def __classget__(cls, instance, owner): """ @@ -254,12 +258,11 @@ class Inner2(BindableClass): Some documentation for Inner2 """ -class Outer: +# We need NestedClassMetaclass to work around a Python pickling bug +class Outer(six.with_metaclass(NestedClassMetaclass)): """ A class with a bindable nested class, for testing purposes """ - __metaclass__ = NestedClassMetaclass # workaround for python pickling bug - class Inner(BindableClass): """ Some documentation for Outer.Inner diff --git a/src/sage/modular/modform_hecketriangle/graded_ring_element.py b/src/sage/modular/modform_hecketriangle/graded_ring_element.py index 493538c60ff..b90249e604f 100644 --- a/src/sage/modular/modform_hecketriangle/graded_ring_element.py +++ b/src/sage/modular/modform_hecketriangle/graded_ring_element.py @@ -17,6 +17,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc import six from sage.rings.all import ZZ, infinity, LaurentSeries, O from sage.functions.all import exp from sage.rings.number_field.number_field import QuadraticField @@ -38,15 +39,16 @@ # Warning: We choose CommutativeAlgebraElement because we want the -# corresponding operations (e.g. __mul__) even though the category +# corresponding operations (e.g. __pow__) even though the category # (and class) of the parent is in some cases not # CommutativeAlgebras but Modules -class FormsRingElement(CommutativeAlgebraElement, UniqueRepresentation): +class FormsRingElement(six.with_metaclass( + InheritComparisonClasscallMetaclass, + CommutativeAlgebraElement, UniqueRepresentation + )): r""" Element of a FormsRing. """ - __metaclass__ = InheritComparisonClasscallMetaclass - from .analytic_type import AnalyticType AT = AnalyticType() diff --git a/src/sage/structure/unique_representation.py b/src/sage/structure/unique_representation.py index a60ee9f5662..fc7bb3849b5 100644 --- a/src/sage/structure/unique_representation.py +++ b/src/sage/structure/unique_representation.py @@ -561,11 +561,12 @@ class that inherits from :class:`UniqueRepresentation`: By adding #****************************************************************************** from __future__ import print_function +from sage.misc import six from sage.misc.cachefunc import weak_cached_function from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall from sage.misc.fast_methods import WithEqualityById -class CachedRepresentation: +class CachedRepresentation(six.with_metaclass(ClasscallMetaclass)): """ Classes derived from CachedRepresentation inherit a weak cache for their instances. @@ -1000,8 +1001,6 @@ class CachedRepresentation: unprocessed arguments will be passed down to :meth:`__init__`. """ - __metaclass__ = ClasscallMetaclass - _included_private_doc_ = ["__classcall__"] @weak_cached_function # automatically a staticmethod From 9635fb408451c3a6aca1d26deafed6461ac62f01 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 7 Apr 2017 11:35:54 +0200 Subject: [PATCH 070/151] Check for metaclass by checking types instead of __metaclass__ --- src/sage/misc/sageinspect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 8feb203afab..58c60b2c75c 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -2109,7 +2109,7 @@ class Element: # First, we deal with nested classes. Their name contains a dot, and we # have a special function for that purpose. - if (not hasattr(obj, '__class__')) or hasattr(obj,'__metaclass__'): + if (not hasattr(obj, '__class__')) or (isinstance(obj, type) and type(obj) is not type): # That happens for ParentMethods # of categories if '.' in obj.__name__ or '.' in getattr(obj,'__qualname__',''): From bac432890b5aedca346cdeb7374b95b3fe27daf8 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 11 Apr 2017 16:22:18 +0200 Subject: [PATCH 071/151] This 'Version' string appears to be locale-dependent; just ignore it --- build/pkgs/psutil/patches/cygwin-support.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/psutil/patches/cygwin-support.patch b/build/pkgs/psutil/patches/cygwin-support.patch index de70047129b..9b9f68854c5 100644 --- a/build/pkgs/psutil/patches/cygwin-support.patch +++ b/build/pkgs/psutil/patches/cygwin-support.patch @@ -4589,7 +4589,7 @@ index 47772da..6636bdf 100755 + # sys.getwindowsversion() is not available in Cygwin's Python + import re + import subprocess -+ winver_re = re.compile(br'\[Version (?P\d+)\.(?P\d+)\.' ++ winver_re = re.compile(br'\[.+ (?P\d+)\.(?P\d+)\.' + br'(?P\d+)\]') + def get_winver(): From fd96c0aaf7e0aa999a95b6bfe1baff54e23234cd Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 11 Apr 2017 17:47:23 +0100 Subject: [PATCH 072/151] fix for #22759 --- src/sage/rings/complex_double.pyx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index e20c8301110..1f8d88c3877 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -2361,9 +2361,7 @@ cdef class ComplexDoubleElement(FieldElement): sage: z = (1/2)*(1 + RDF(sqrt(3)) *CDF.0); z # abs tol 1e-16 0.5 + 0.8660254037844387*I sage: p = z.algdep(5); p - x^3 + 1 - sage: p.factor() - (x + 1) * (x^2 - x + 1) + x^2 - x + 1 sage: abs(z^2 - z + 1) < 1e-14 True @@ -2374,11 +2372,8 @@ cdef class ComplexDoubleElement(FieldElement): sage: CDF(1,5).algdep(2) x^2 - 2*x + 26 """ - from .polynomial.polynomial_ring_constructor import PolynomialRing - from .integer_ring import ZZ - R = PolynomialRing(ZZ ,'x') - return R(self.__pari__().algdep(n)) - + from sage.arith.all import algdep + return algdep(self, n) cdef class FloatToCDF(Morphism): """ From f1ca05f39db7d8ca8afc379808858c568619b9eb Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 12 Apr 2017 09:49:53 +0200 Subject: [PATCH 073/151] Restore (meta, *bases) arguments for with_metaclass() --- src/sage/misc/six.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/misc/six.py b/src/sage/misc/six.py index 5f03a969683..37295c7aeca 100644 --- a/src/sage/misc/six.py +++ b/src/sage/misc/six.py @@ -8,7 +8,7 @@ from six import * -def with_metaclass(*args): +def with_metaclass(meta, *bases): """ Create a base class with a metaclass. @@ -63,8 +63,8 @@ def with_metaclass(*args): # a dummy class which will setup the correct metaclass when this # dummy class is used as base class. # - # In detail: let T = with_metaclass(*args). When the user does - # "class X(with_metaclass(*args))", Python will first determine + # In detail: let T = with_metaclass(meta, *bases). When the user does + # "class X(with_metaclass(meta, *bases))", Python will first determine # the metaclass of X from its bases. So, the metaclass will be # type(T). # Next, Python will call type(T)("X", (T,), methods) and it is this @@ -74,7 +74,7 @@ def with_metaclass(*args): # The metaclass type(T) is returned by _WithMetaclass.get(...) and # the dummy() method creates an instance of this metaclass, which # is a regular class. - return _WithMetaclass.get(*args).dummy() + return _WithMetaclass.get(meta, bases).dummy() class _WithMetaclass(type): @@ -82,7 +82,7 @@ class _WithMetaclass(type): Metaclasses to be used in ``with_metaclass``. """ @classmethod - def get(cls, meta, *bases): + def get(cls, meta, bases): """ Return a metaclass which, if an instance of it is used as base class, will create a class with metaclass ``meta`` and bases From 92e803b2ee3a509d8061dff3bed31d1488665341 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 11 Apr 2017 16:01:50 +0200 Subject: [PATCH 074/151] Adds patch to ECL to work around the issue in https://trac.sagemath.org/ticket/22337 --- build/pkgs/ecl/package-version.txt | 2 +- build/pkgs/ecl/patches/flisten-bug.patch | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/ecl/patches/flisten-bug.patch diff --git a/build/pkgs/ecl/package-version.txt b/build/pkgs/ecl/package-version.txt index fda104a5551..9b754a194a9 100644 --- a/build/pkgs/ecl/package-version.txt +++ b/build/pkgs/ecl/package-version.txt @@ -1 +1 @@ -16.1.2.p2 +16.1.2.p3 diff --git a/build/pkgs/ecl/patches/flisten-bug.patch b/build/pkgs/ecl/patches/flisten-bug.patch new file mode 100644 index 00000000000..cec8c36a448 --- /dev/null +++ b/build/pkgs/ecl/patches/flisten-bug.patch @@ -0,0 +1,16 @@ +Prevent busy loop during ECL interpreter shutdown on Cygwin. + +See https://trac.sagemath.org/ticket/22337 +diff --git a/src/c/file.d b/src/c/file.d +index 2285a60..aeed899 100755 +--- a/src/c/file.d ++++ b/src/c/file.d +@@ -5317,7 +5317,7 @@ static int + flisten(cl_object stream, FILE *fp) + { + int aux; +- if (feof(fp)) ++ if (feof(fp) || ferror(fp)) + return ECL_LISTEN_EOF; + #ifdef FILE_CNT + if (FILE_CNT(fp) > 0) From c473b60796d78a49cc572901dd7192d9ecea5cf5 Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Wed, 12 Apr 2017 20:11:24 +0200 Subject: [PATCH 075/151] fix docstring formatting --- src/sage/matrix/matrix2.pyx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 43ce3112998..10087d37113 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -14903,19 +14903,19 @@ def _matrix_power_symbolic(A, n): r""" Symbolic matrix power. - This function implements $f(A) = A^n$ assuming that the base ring of A is - exact. It relies in the Jordan normal form of A, available for exact rings - as `jordan_form()`. See Sec. 1.2 of [Hig2008]_ for further details. + This function implements `f(A) = A^n` and relies in the Jordan normal form + of `A`, available for exact rings as ``jordan_form()``. + See Sec. 1.2 of [Hig2008]_ for further details. INPUT: - - ``A`` - a square matrix over an exact field. + - ``A`` -- a square matrix over an exact field - - ``n`` - the symbolic exponent. + - ``n`` -- the symbolic exponent OUTPUT: - Matrix A^n (symbolic). + Matrix `A^n` (symbolic). EXAMPLES:: @@ -14971,7 +14971,7 @@ def _matrix_power_symbolic(A, n): # the number of Jordan blocks num_jordan_blocks = 1+len(J.subdivisions()[0]) - # FJ stores the application of f to the Jordan blocks + # FJ stores the application of f = x^n to the Jordan blocks FJ = matrix(SR, J.ncols()) FJ.subdivide(J.subdivisions()) From 32e7cf9cee379d13642eeefdf81aa89ff73efc3b Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Wed, 12 Apr 2017 21:15:17 +0200 Subject: [PATCH 076/151] updates in _matrix_power_symbolic --- src/sage/matrix/matrix2.pyx | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 10087d37113..f7d32a17caf 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -14951,9 +14951,8 @@ def _matrix_power_symbolic(A, n): [-1/2*2^(2*n + 1) 1/2*2^(2*n + 1)] """ from sage.rings.qqbar import AlgebraicNumber - from sage.modules.free_module_element import vector from sage.matrix.constructor import matrix - from sage.functions.other import factorial + from sage.functions.other import binomial from sage.symbolic.ring import SR from sage.rings.qqbar import QQbar @@ -14974,8 +14973,6 @@ def _matrix_power_symbolic(A, n): # FJ stores the application of f = x^n to the Jordan blocks FJ = matrix(SR, J.ncols()) FJ.subdivide(J.subdivisions()) - - factorial_n = factorial(n) for k in range(num_jordan_blocks): @@ -14991,17 +14988,16 @@ def _matrix_power_symbolic(A, n): # compute the first row of f(Jk) vk = [] for i in range(mk): - if hasattr(Jk[i][i], 'radical_expression'): - Jk_ii = Jk[i][i].radical_expression() - else: - Jk_ii = Jk[i][i] + Jk_ii = Jk[i, i] + if hasattr(Jk_ii, 'radical_expression'): + Jk_ii = Jk_ii.radical_expression() # corresponds to \frac{D^i(f)}{i!}, with f = x^n and D the differential operator wrt x - vk += [(factorial_n/(factorial(n-i)*factorial(i))*Jk_ii**(n-i)).simplify_full()] + vk += [(binomial(n, i) * Jk_ii**(n-i)).simplify_full()] # insert vk into each row (above the main diagonal) for i in range(mk): - Jk_row_i = vector(SR, vector(SR, i).list() + vk[0:mk-i]) + Jk_row_i = [SR.zero()]*i + vk[0:mk-i] FJ_k.set_row(i, Jk_row_i) FJ.set_block(k, k, FJ_k) From fb90d5e3667237f2d9fc7ba982b466bc52802b1d Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Wed, 12 Apr 2017 14:24:16 -0700 Subject: [PATCH 077/151] trac 22764: check whether Sage is built by looking for local/bin/sage --- src/bin/sage | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/bin/sage b/src/bin/sage index d28cba2c0ce..42d3ab38271 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -383,15 +383,7 @@ fi # Prepare for running Sage, either interactively or non-interactively. sage_setup() { # Check that we're not in a source tarball which hasn't been built yet (#13561). - if [ ! -x "$SAGE_LOCAL/bin/python" ]; then - sage_built=no - else - PYTHON_VERSION=$("$SAGE_LOCAL/bin/python" -c 'import sys; print("%d.%d" % sys.version_info[:2])') - if [ ! -d "$SAGE_LOCAL/lib/python$PYTHON_VERSION/site-packages/sage" ]; then - sage_built=no - fi - fi - if [ $sage_built = no ]; then + if [ ! -x "$SAGE_LOCAL/bin/sage" ]; then echo >&2 '************************************************************************' echo >&2 'It seems that you are attempting to run Sage from an unpacked source' echo >&2 'tarball, but you have not compiled it yet (or maybe the build has not' From 74c992aad9f7be73705d6880bfc871d26be10219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 13 Apr 2017 14:12:47 +1200 Subject: [PATCH 078/151] raise tolerance of test broken with clang+OSX --- src/sage/schemes/elliptic_curves/lseries_ell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/lseries_ell.py b/src/sage/schemes/elliptic_curves/lseries_ell.py index 4b4370d51a9..5f04ed2e600 100644 --- a/src/sage/schemes/elliptic_curves/lseries_ell.py +++ b/src/sage/schemes/elliptic_curves/lseries_ell.py @@ -374,7 +374,7 @@ def twist_values(self, s, dmin, dmax): sage: E = EllipticCurve('37a') sage: vals = E.lseries().twist_values(1, -12, -4) - sage: vals # abs tol 1e-17 + sage: vals # abs tol 1e-15 [(-11, 1.47824342), (-8, 8.9590946e-18), (-7, 1.85307619), (-4, 2.45138938)] sage: F = E.quadratic_twist(-8) sage: F.rank() From f089c1345497165b7a31c9e7c5d0942f217dfc80 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 13 Apr 2017 10:04:41 +0200 Subject: [PATCH 079/151] 22090: Gosper algorithm and Wilf-Zeilberger certificate --- src/sage/libs/pynac/pynac.pxd | 7 +- src/sage/symbolic/expression.pyx | 147 +++++++++++++++++++++ src/sage/tests/gosper-sum.py | 216 +++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 src/sage/tests/gosper-sum.py diff --git a/src/sage/libs/pynac/pynac.pxd b/src/sage/libs/pynac/pynac.pxd index 15e8ac2127d..c3af047ff13 100644 --- a/src/sage/libs/pynac/pynac.pxd +++ b/src/sage/libs/pynac/pynac.pxd @@ -150,7 +150,12 @@ cdef extern from "sage/libs/pynac/wrap.h": py_object_from_numeric(GEx e) except + # Algorithms - GEx g_gcd "gcd"(GEx a, GEx b) except + + GEx g_gcd "gcd"(GEx a, GEx b) except + + GEx g_gosper_term "gosper_term"(GEx the_ex, GEx n) except + + GEx g_gosper_sum_definite "gosper_sum_definite"(GEx the_ex, + GEx n, GEx a, GEx b, int* p) except + + GEx g_gosper_sum_indefinite "gosper_sum_indefinite"(GEx the_ex, + GEx n, int* p) except + GEx to_gamma(GEx expr) except + GEx gamma_normalize(GEx expr) except + GEx g_resultant "resultant"(GEx a, GEx b, GEx v) except + diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 01319b5e687..14146a5686e 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -6654,6 +6654,153 @@ cdef class Expression(CommutativeRingElement): sig_off() return new_Expression_from_GEx(self._parent, x) + def gosper_sum(self, *args): + """ + Return the summation of this hypergeometric expression using + Gosper's algorithm. + + INPUT: + + - a symbolic expression that may contain rational functions, + powers, factorials, gamma function terms, binomial + coefficients, and Pochhammer symbols that are rational-linear + in their arguments + + - the main variable and, optionally, summation limits + + EXAMPLES:: + + sage: a,b,k,m,n = var('a b k m n') + sage: SR(1).gosper_sum(n) + n + sage: SR(1).gosper_sum(n,5,8) + 4 + sage: n.gosper_sum(n) + 1/2*(n - 1)*n + sage: n.gosper_sum(n,0,5) + 15 + sage: n.gosper_sum(n,0,m) + 1/2*(m + 1)*m + sage: n.gosper_sum(n,a,b) + -1/2*(a + b)*(a - b - 1) + + :: + + sage: (factorial(m + n)/factorial(n)).gosper_sum(n) + n*factorial(m + n)/((m + 1)*factorial(n)) + sage: (binomial(m + n, n)).gosper_sum(n) + n*binomial(m + n, n)/(m + 1) + sage: (binomial(m + n, n)).gosper_sum(n, 0, a) + (a + m + 1)*binomial(a + m, a)/(m + 1) + sage: (binomial(m + n, n)).gosper_sum(n, 0, 5) + 1/120*(m + 6)*(m + 5)*(m + 4)*(m + 3)*(m + 2) + sage: (rising_factorial(a,n)/rising_factorial(b,n)).gosper_sum(n) + (b + n - 1)*gamma(a + n)*gamma(b)/((a - b + 1)*gamma(a)*gamma(b + n)) + sage: factorial(n).gosper_term(n) + Traceback (most recent call last): + ... + ValueError: expression not Gosper-summable + """ + cdef Expression s, a, b + cdef GEx x + cdef int i = 1 + if len(args) > 1: + n, l1, l2 = args + s = self.coerce_in(n) + a = self.coerce_in(l1) + b = self.coerce_in(l2) + sig_on() + try: + x = g_gosper_sum_definite(self._gobj, s._gobj, + a._gobj, b._gobj, &i) + finally: + sig_off() + if i == 0: + raise ValueError("expression not Gosper-summable") + return new_Expression_from_GEx(self._parent, x) + else: + s = self.coerce_in(args[0]) + sig_on() + try: + x = g_gosper_sum_indefinite(self._gobj, s._gobj, &i) + finally: + sig_off() + if i == 0: + raise ValueError("expression not Gosper-summable") + return new_Expression_from_GEx(self._parent, x) + + def gosper_term(self, n): + """ + Return Gosper's hypergeometric term for ``self``. + + Suppose ``f``=``self`` is a hypergeometric term such that: + + .. math:: + s_n = \sum_{k=0}^{n-1} f_k + + and `f_k` doesn't depend on `n`. Return a hypergeometric + term `g_n` such that `g_{n+1} - g_n = f_n`. + + EXAMPLES:: + + sage: _ = var('n') + sage: SR(1).gosper_term(n) + n + sage: n.gosper_term(n) + 1/2*(n^2 - n)/n + sage: (n*factorial(n)).gosper_term(n) + 1/n + sage: factorial(n).gosper_term(n) + Traceback (most recent call last): + ... + ValueError: expression not Gosper-summable + """ + cdef Expression s + cdef int i = 1 + s = self.coerce_in(n) + sig_on() + try: + x = g_gosper_term(self._gobj, s._gobj) + except ValueError: + raise ValueError("expression not Gosper-summable") + finally: + sig_off() + return new_Expression_from_GEx(self._parent, x) + + def WZ_certificate(self, n, k): + r""" + Return the Wilf-Zeilberger certificate for this hypergeometric + summand in ``n``, ``k``. + + To prove the identity `\sum_k F(n,k)=\textrm{const}` it suffices + to show that `F(n+1,k)-F(n,k)=G(n,k+1)-G(n,k),` with `G=RF` and + `R` the WZ certificate. + + EXAMPLES: + + To show that `\sum_k \binom{n}{k} = 2^n` do:: + + sage: _ = var('k n') + sage: F(n,k) = binomial(n,k) / 2^n + sage: c = F(n,k).WZ_certificate(n,k); c + 1/2*k/(k - n - 1) + sage: G(n,k) = c * F(n,k); G + (n, k) |--> 1/2*k*binomial(n, k)/(2^n*(k - n - 1)) + sage: (F(n+1,k) - F(n,k) - G(n,k+1) + G(n,k)).simplify_full() + 0 + """ + cdef Expression s, f + cdef int i = 1 + s = self.coerce_in(k) + f = self.subs(n == n+1) - self + sig_on() + try: + x = g_gosper_term(f._gobj, s._gobj) + finally: + sig_off() + g = new_Expression_from_GEx(self._parent, x) + return (f*g / self).to_gamma().gamma_normalize().simplify_full().factor() + def lcm(self, b): """ Return the lcm of self and b. diff --git a/src/sage/tests/gosper-sum.py b/src/sage/tests/gosper-sum.py new file mode 100644 index 00000000000..4b961fc5ecd --- /dev/null +++ b/src/sage/tests/gosper-sum.py @@ -0,0 +1,216 @@ +""" + References + ========== + + .. [1] Marko Petkovsek, Herbert S. Wilf, Doron Zeilberger, A = B, + AK Peters, Ltd., Wellesley, MA, USA, 1997, pp. 73--100 + +TESTS:: + + sage: _ = var('a b k m n r') + sage: SR(1).gosper_sum(k) + k + sage: SR(1).gosper_sum(k,0,n) + n + 1 + sage: SR(1).gosper_sum(k, a, b) + -a + b + 1 + sage: a.gosper_sum(k) + a*k + sage: a.gosper_sum(k,0,n) + a*(n + 1) + sage: a.gosper_sum(k,a,b) + -(a - b - 1)*a + sage: k.gosper_sum(k) + 1/2*(k - 1)*k + sage: k.gosper_sum(k,0,n) + 1/2*(n + 1)*n + sage: k.gosper_sum(k, a, b) + -1/2*(a + b)*(a - b - 1) + sage: k.gosper_sum(k, a+1, b) + -1/2*(a + b + 1)*(a - b) + sage: k.gosper_sum(k, a, b+1) + -1/2*(a + b + 1)*(a - b - 2) + sage: (k^3).gosper_sum(k) + 1/4*(k - 1)^2*k^2 + sage: (k^3).gosper_sum(k, a, b) + -1/4*(a^2 + b^2 - a + b)*(a + b)*(a - b - 1) + + sage: (1/k).gosper_sum(k) + Traceback (most recent call last): + ... + ValueError: expression not Gosper-summable + sage: x = (1/k/(k+1)/(k+2)/(k+3)/(k+5)/(k+7)).gosper_sum(k,a,b) + sage: y = sum(1/k/(k+1)/(k+2)/(k+3)/(k+5)/(k+7) for k in range(5,500)) + sage: assert x.subs(a==5,b==499) == y + +The following are from A==B, p.78 ff. Since the resulting expressions +get more and more complicated with many correct representations that +may differ depending on future capabilities, we check correctness by +doing random summations:: + + sage: def check(ex, var, val1, val2from, val2to): + ....: import random + ....: symb = SR.var('symb') + ....: s1 = ex.gosper_sum(var, val1, symb) + ....: R = random.SystemRandom + ....: val2 = R().randint(val2from, val2to) + ....: s2 = sum(ex.subs(var==i) for i in range(val1, val2+1)) + ....: assert s1.subs(symb==val2) == s2 + + sage: def check_unsolvable(ex, *args): + ....: try: + ....: SR(ex).gosper_sum(*args) + ....: raise AssertionError + ....: except ValueError: + ....: pass + + sage: check((4*n+1) * factorial(n)/factorial(2*n+1), n, 0, 10, 20) + sage: check_unsolvable(factorial(k), k,0,n) + sage: (k * factorial(k)).gosper_sum(k,1,n) + n*factorial(n) + factorial(n) - 1 + sage: (k * factorial(k)).gosper_sum(k) + factorial(k) + sage: check_unsolvable(binomial(n,k), k,0,n) + sage: ((-1)^k*binomial(n,k)).gosper_sum(k,0,n) + 0 + sage: ((-1)^k*binomial(n,k)).gosper_sum(k,0,a) + -(-1)^a*(a - n)*binomial(n, a)/n + sage: (binomial(1/2,a-k+1)*binomial(1/2,a+k)).gosper_sum(k,1,b) + (2*a + 2*b - 1)*(a - b + 1)*b*binomial(1/2, a + b)*binomial(1/2, a - b + 1)/((2*a + 1)*a) + sage: t = (binomial(2*k,k)/4^k).gosper_sum(k,0,n); t + (2*n + 1)*binomial(2*n, n)/4^n + sage: t = t.gosper_sum(n,0,n); t + 1/3*(2*n + 3)*(2*n + 1)*binomial(2*n, n)/4^n + sage: t = t.gosper_sum(n,0,n); t + 1/15*(2*n + 5)*(2*n + 3)*(2*n + 1)*binomial(2*n, n)/4^n + sage: t = t.gosper_sum(n,0,n); t + 1/105*(2*n + 7)*(2*n + 5)*(2*n + 3)*(2*n + 1)*binomial(2*n, n)/4^n + sage: (binomial(2*k+2*a,2*a)*binomial(2*k,k)/binomial(k+a,a)/4^k).gosper_sum(k,0,n) + (2*a + 2*n + 1)*binomial(2*a + 2*n, 2*a)*binomial(2*n, n)/(4^n*(2*a + 1)*binomial(a + n, a)) + sage: (4^k/binomial(2*k,k)).gosper_sum(k,0,n) + 1/3*(2*4^n*n + 2*4^n + binomial(2*n, n))/binomial(2*n, n) + + # The following are from A==B, 5.7 Exercises + sage: for k in range(1,5): (n^k).gosper_sum(n,0,m) + 1/2*(m + 1)*m + 1/6*(2*m + 1)*(m + 1)*m + 1/4*(m + 1)^2*m^2 + 1/30*(3*m^2 + 3*m - 1)*(2*m + 1)*(m + 1)*m + sage: for k in range(1,4): (n^k*2^n).gosper_sum(n,0,m) + 2*2^m*m - 2*2^m + 2 + 2*2^m*m^2 - 4*2^m*m + 6*2^m - 6 + 2*2^m*m^3 - 6*2^m*m^2 + 18*2^m*m - 26*2^m + 26 + sage: (1 / (n^2 + sqrt(5)*n - 1)).gosper_sum(n,0,m) # known bug + ....: # TODO: algebraic solutions + sage: check((n^4 * 4^n / binomial(2*n, n)), n, 0, 10, 20) + sage: check((factorial(3*n) / (factorial(n) * factorial(n+1) * factorial(n+2) * 27^n)), n,0,10,20) + sage: (binomial(2*n, n)^2 / (n+1) / 4^(2*n)).gosper_sum(n,0,m) + (2*m + 1)^2*binomial(2*m, m)^2/(4^(2*m)*(m + 1)) + sage: (((4*n-1) * binomial(2*n, n)^2) / (2*n-1)^2 / 4^(2*n)).gosper_sum(n,0,m) + -binomial(2*m, m)^2/4^(2*m) + sage: check(n * factorial(n-1/2)^2 / factorial(n+1)^2, n,0,10,20) + + sage: (n^2 * a^n).gosper_sum(n,0,m) + (a^2*a^m*m^2 - 2*a*a^m*m^2 - 2*a*a^m*m + a^m*m^2 + a*a^m + 2*a^m*m + a^m - a - 1)*a/(a - 1)^3 + sage: ((n - r/2)*binomial(r, n)).gosper_sum(n,0,m) + 1/2*(m - r)*binomial(r, m) + sage: x = var('x') + sage: (factorial(n-1)^2 / factorial(n-x) / factorial(n+x)).gosper_sum(n,1,m) + (m^2*factorial(m - 1)^2*factorial(x + 1)*factorial(-x + 1) + x^2*factorial(m + x)*factorial(m - x) - factorial(m + x)*factorial(m - x))/(x^2*factorial(m + x)*factorial(m - x)*factorial(x + 1)*factorial(-x + 1)) + sage: ((n*(n+a+b)*a^n*b^n)/factorial(n+a)/factorial(n+b)).gosper_sum(n,1,m).simplify_full() + -(a^(m + 1)*b^(m + 1)*factorial(a - 1)*factorial(b - 1) - factorial(a + m)*factorial(b + m))/(factorial(a + m)*factorial(a - 1)*factorial(b + m)*factorial(b - 1)) + + sage: check_unsolvable(1/n, n,1,m) + sage: check_unsolvable(1/n^2, n,1,m) + sage: check_unsolvable(1/n^3, n,1,m) + sage: ((6*n + 3) / (4*n^4 + 8*n^3 + 8*n^2 + 4*n + 3)).gosper_sum(n,1,m) + (m + 2)*m/(2*m^2 + 4*m + 3) + sage: (2^n * (n^2 - 2*n - 1)/(n^2 * (n+1)^2)).gosper_sum(n,1,m) + -2*(m^2 - 2^m + 2*m + 1)/(m + 1)^2 + sage: ((4^n * n^2)/((n+2) * (n+1))).gosper_sum(n,1,m) + 2/3*(2*4^m*m - 2*4^m + m + 2)/(m + 2) + sage: check_unsolvable(2^n / (n+1), n,0,m-1) + sage: check((4*(1-n) * (n^2-2*n-1) / n^2 / (n+1)^2 / (n-2)^2 / (n-3)^2), n, 4, 10, 20) + sage: check(((n^4-14*n^2-24*n-9) * 2^n / n^2 / (n+1)^2 / (n+2)^2 / (n+3)^2), n, 1, 10, 20) + +Exercises 3 (h), (i), (j) require symbolic product support so we leave +them out for now. + +:: + + sage: _ = var('a b k m n r') + sage: check_unsolvable(binomial(2*n, n) * a^n, n) + sage: (binomial(2*n,n)*(1/4)^n).gosper_sum(n) + 2*(1/4)^n*n*binomial(2*n, n) + sage: ((k-1) / factorial(k)).gosper_sum(k) + -k/factorial(k) + sage: F(n, k) = binomial(n, k) / 2^n + sage: check_unsolvable(F(n, k), k) + sage: _ = (F(n+1,k)-F(n,k)).gosper_term(k) + sage: F(n,k).WZ_certificate(n,k) + 1/2*k/(k - n - 1) + sage: F(n, k) = binomial(n, k)^2 / binomial(2*n, n) + sage: check_unsolvable(F(n, k), k) + sage: _ =(F(n+1, k) - F(n, k)).gosper_term(k) + sage: F(n,k).WZ_certificate(n,k) + 1/2*(2*k - 3*n - 3)*k^2/((k - n - 1)^2*(2*n + 1)) + sage: F(n, k) = binomial(n,k) * factorial(n) / factorial(k) / factorial(a-k) / factorial(a+n) + sage: check_unsolvable(F(n, k), k) + sage: _ = (F(n+1, k) - F(n, k)).gosper_term(k) + sage: F(n,k).WZ_certificate(n,k) + k^2/((a + n + 1)*(k - n - 1)) + + sage: (1/n/(n+1)/(n+2)/(n+5)).gosper_sum(n) + 1/720*(55*n^5 + 550*n^4 + 1925*n^3 + 2510*n^2 - 1728)/((n + 4)*(n + 3)*(n + 2)*(n + 1)*n) + sage: (1/n/(n+1)/(n+2)/(n+7)).gosper_sum(n) + 1/1050*(91*n^7 + 1911*n^6 + 15925*n^5 + 66535*n^4 + 142534*n^3 + 132104*n^2 - 54000)/((n + 6)*(n + 5)*(n + 4)*(n + 3)*(n + 2)*(n + 1)*n) + sage: (1/n/(n+1)/(n+2)/(n+5)/(n+7)).gosper_sum(n) + 1/10080*(133*n^7 + 2793*n^6 + 23275*n^5 + 97755*n^4 + 213472*n^3 + 206892*n^2 - 103680)/((n + 6)*(n + 5)*(n + 4)*(n + 3)*(n + 2)*(n + 1)*n) + +The following are from A=B, 7.2 WZ Proofs of the hypergeometric database:: + + sage: _ = var('a b c i j k m n r') + sage: F(n,k) = factorial(n+k)*factorial(b+k)*factorial(c-n-1)*factorial(c-b-1)/factorial(c+k)/factorial(n-1)/factorial(c-n-b-1)/factorial(k+1)/factorial(b-1) + sage: F(n,k).WZ_certificate(n,k) + -(c + k)*(k + 1)/((c - n - 1)*n) + sage: F(n,k)=(-1)^(n+k)*factorial(2*n+c-1)*factorial(n)*factorial(n+c-1)/factorial(2*n+c-1-k)/factorial(2*n-k)/factorial(c+k-1)/factorial(k) + sage: F(n,k).WZ_certificate(n,k) + 1/2*(c^2 - 2*c*k + k^2 + 7*c*n - 6*k*n + 10*n^2 + 4*c - 3*k + 10*n + 2)*(c + k - 1)*k/((c - k + 2*n + 1)*(c - k + 2*n)*(k - 2*n - 1)*(k - 2*n - 2)) + sage: F(n,k)=factorial(a+k-1)*factorial(b+k-1)*factorial(n)*factorial(n+c-b-a-k-1)*factorial(n+c-1)/factorial(k)/factorial(n-k)/factorial(k+c-1)/factorial(n+c-a-1)/factorial(n+c-b-1) + sage: F(n,k).WZ_certificate(n,k) + -(a + b - c + k - n)*(c + k - 1)*k/((a - c - n)*(b - c - n)*(k - n - 1)) + sage: F(n,k)=(-1)^k*binomial(n+b,n+k)*binomial(n+c,c+k)*binomial(b+c,b+k)/factorial(n+b+c)*factorial(n)*factorial(b)*factorial(c) + sage: F(n,k).WZ_certificate(n,k) + 1/2*(b + k)*(c + k)/((b + c + n + 1)*(k - n - 1)) + sage: phi(t) = factorial(a+t-1)*factorial(b+t-1)/factorial(t)/factorial(-1/2+a+b+t) + sage: psi(t) = factorial(t+a+b-1/2)*factorial(t)*factorial(t+2*a+2*b-1)/factorial(t+2*a-1)/factorial(t+a+b-1)/factorial(t+2*b-1) + sage: F(n,k) = phi(k)*phi(n-k)*psi(n) + sage: F(n,k).WZ_certificate(n,k) + (2*a + 2*b + 2*k - 1)*(2*a + 2*b - 2*k + 3*n + 2)*(a - k + n)*(b - k + n)*k/((2*a + 2*b - 2*k + 2*n + 1)*(2*a + n)*(a + b + n)*(2*b + n)*(k - n - 1)) + +The following are also from A=B, 7 The WZ Phenomenon:: + + sage: F(n,k) = factorial(n-i)*factorial(n-j)*factorial(i-1)*factorial(j-1)/factorial(n-1)/factorial(k-1)/factorial(n-i-j+k)/factorial(i-k)/factorial(j-k) + sage: F(n,k).WZ_certificate(n,k) + (k - 1)/n + sage: F(n,k) = binomial(3*n,k)/8^n + sage: F(n,k).WZ_certificate(n,k) + 1/8*(4*k^2 - 30*k*n + 63*n^2 - 22*k + 93*n + 32)*k/((k - 3*n - 1)*(k - 3*n - 2)*(k - 3*n - 3)) + +Example 7.5.1 gets a different but correct certificate (the certificate +in the book fails the proof):: + + sage: F(n,k) = 2^(k+1)*(k+1)*factorial(2*n-k-2)*factorial(n)/factorial(n-k-1)/factorial(2*n) + sage: c = F(n,k).WZ_certificate(n,k); c + -1/2*(k - 2*n + 1)*k/((k - n)*(2*n + 1)) + sage: G(n,k) = c*F(n,k) + sage: t = (F(n+1,k) - F(n,k) - G(n,k+1) + G(n,k)) + sage: t.simplify_full().is_trivial_zero() + True + sage: c = k/2/(-1+k-n) + sage: GG(n,k) = c*F(n,k) + sage: t = (F(n+1,k) - F(n,k) - GG(n,k+1) + GG(n,k)) + sage: t.simplify_full().is_trivial_zero() + False +""" + From 2fd76c9932c53ebf73ed3ebd9a8b3810c3125c1b Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 12 Apr 2017 15:13:54 +0200 Subject: [PATCH 080/151] Fix building libfplll 5.1.0 on Cygwin --- build/pkgs/libfplll/package-version.txt | 2 +- .../patches/disable-recursive-enum.patch | 88 +++++++++++++++++++ .../libfplll/patches/tests-long-double.patch | 84 ++++++++++++++++++ build/pkgs/libfplll/spkg-install | 7 +- 4 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 build/pkgs/libfplll/patches/disable-recursive-enum.patch create mode 100644 build/pkgs/libfplll/patches/tests-long-double.patch diff --git a/build/pkgs/libfplll/package-version.txt b/build/pkgs/libfplll/package-version.txt index 831446cbd27..d084d805a5d 100644 --- a/build/pkgs/libfplll/package-version.txt +++ b/build/pkgs/libfplll/package-version.txt @@ -1 +1 @@ -5.1.0 +5.1.0.p0 diff --git a/build/pkgs/libfplll/patches/disable-recursive-enum.patch b/build/pkgs/libfplll/patches/disable-recursive-enum.patch new file mode 100644 index 00000000000..c3d025c9b0f --- /dev/null +++ b/build/pkgs/libfplll/patches/disable-recursive-enum.patch @@ -0,0 +1,88 @@ +This optional implementation feature needs to be disabled on Cygwin (for now, +due to bugs in its binutils version). This patch adds a ./configure flag to +make that easier. + +https://trac.sagemath.org/ticket/22800 +diff --git a/configure b/configure +index ce0e7fd..f1eccd5 100755 +--- a/configure ++++ b/configure +@@ -776,6 +776,7 @@ with_gmp + with_mpfr + with_qd + with_max_enumeration_dimension ++enable_recursive_enum + ' + ac_precious_vars='build_alias + host_alias +@@ -1420,6 +1421,8 @@ Optional Features: + --disable-dependency-tracking + speeds up one-time build + --disable-libtool-lock avoid locking (might break parallel builds) ++ --disable-recursive-enum ++ Disable recursive enumeration + + Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] +@@ -18220,6 +18223,19 @@ cat >>confdefs.h <<_ACEOF + _ACEOF + + ++# Check whether --enable-recursive-enum was given. ++if test "${enable_recursive_enum+set}" = set; then : ++ enableval=$enable_recursive_enum; ++fi ++ ++ ++if test "x$enable_recursive_enum" != "xno"; then : ++ ++ ++$as_echo "#define FPLLL_WITH_RECURSIVE_ENUM 1" >>confdefs.h ++ ++fi ++ + # Store version numbers in header + + +diff --git a/configure.ac b/configure.ac +index 401bbba..b367cb5 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -124,6 +124,13 @@ AC_ARG_WITH(max-enumeration-dimension, + + AC_DEFINE_UNQUOTED([FPLLL_MAX_ENUM_DIMENSION], $max_enumeration_dimension, [maximum supported enumeration dimension]) + ++AC_ARG_ENABLE(recursive-enum, ++ AS_HELP_STRING([--disable-recursive-enum], ++ [Disable recursive enumeration])) ++ ++AS_IF([test "x$enable_recursive_enum" != "xno"], [ ++ AC_DEFINE([FPLLL_WITH_RECURSIVE_ENUM], [1], [recursive enumeration enabled])]) ++ + # Store version numbers in header + + AC_DEFINE_UNQUOTED([FPLLL_MAJOR_VERSION],[$FPLLL_MAJOR_VERSION],[major version]) +diff --git a/fplll/enum/enumerate_base.h b/fplll/enum/enumerate_base.h +index 3fcffa5..860b207 100644 +--- a/fplll/enum/enumerate_base.h ++++ b/fplll/enum/enumerate_base.h +@@ -32,7 +32,6 @@ inline void roundto(int &dest, const double &src) { dest = (int)round(src); } + inline void roundto(double &dest, const double &src) { dest = round(src); } + + /* config */ +-#define FPLLL_WITH_RECURSIVE_ENUM 1 + #define MAXTEMPLATEDDIMENSION 80 // unused + //#define FORCE_ENUM_INLINE // not recommended + /* end config */ +diff --git a/fplll/fplll_config.h.in b/fplll/fplll_config.h.in +index 574db4d..0119015 100644 +--- a/fplll/fplll_config.h.in ++++ b/fplll/fplll_config.h.in +@@ -25,4 +25,7 @@ + /* Maximum supported enumeration dimension */ + #define FPLLL_MAX_ENUM_DIMENSION @FPLLL_MAX_ENUM_DIMENSION@ + ++/* Recursive enumeration enabled */ ++#undef FPLLL_WITH_RECURSIVE_ENUM ++ + #endif //FPLLL_CONFIG__H diff --git a/build/pkgs/libfplll/patches/tests-long-double.patch b/build/pkgs/libfplll/patches/tests-long-double.patch new file mode 100644 index 00000000000..70e8df0bc25 --- /dev/null +++ b/build/pkgs/libfplll/patches/tests-long-double.patch @@ -0,0 +1,84 @@ +Patch required for the test suites to build/run on Cygwin, where long double +is not really supported; see https://trac.sagemath.org/ticket/22800 +diff --git a/tests/test_nr.cpp b/tests/test_nr.cpp +index aa7b055..3813ce5 100644 +--- a/tests/test_nr.cpp ++++ b/tests/test_nr.cpp +@@ -72,7 +72,9 @@ int main(int argc, char *argv[]) + + int status = 0; + status |= test_arithmetic>(); ++#ifdef FPLLL_WITH_LONG_DOUBLE + status |= test_arithmetic>(); ++#endif + #ifdef FPLLL_WITH_DPE + status |= test_arithmetic>(); + #endif +@@ -83,7 +85,9 @@ int main(int argc, char *argv[]) + status |= test_arithmetic>(); + + status |= test_std>(); ++#ifdef FPLLL_WITH_LONG_DOUBLE + status |= test_std>(); ++#endif + #ifdef FPLLL_WITH_DPE + status |= test_std>(); + #endif +@@ -94,7 +98,9 @@ int main(int argc, char *argv[]) + status |= test_std>(); + + status |= test_root>(); ++#ifdef FPLLL_WITH_LONG_DOUBLE + status |= test_root>(); ++#endif + #ifdef FPLLL_WITH_QD + status |= test_root>(); + status |= test_root>(); +@@ -102,7 +108,9 @@ int main(int argc, char *argv[]) + status |= test_root>(); + + status |= test_str>(); ++#ifdef FPLLL_WITH_LONG_DOUBLE + status |= test_str>(); ++#endif + #ifdef FPLLL_WITH_DPE + status |= test_str>(); + #endif +diff --git a/tests/test_pruner.cpp b/tests/test_pruner.cpp +index 6e3e3f0..476f043 100644 +--- a/tests/test_pruner.cpp ++++ b/tests/test_pruner.cpp +@@ -520,20 +520,25 @@ int main(int argc, char *argv[]) + cerr << endl << "d" << endl; + status += test_unpruned>(); + print_status(status); ++#ifdef FPLLL_WITH_LONG_DOUBLE + cerr << endl << "ld" << endl; + status += test_unpruned>(); + print_status(status); ++#endif + cerr << endl << "MPRF" << endl; + status += test_unpruned>(); + print_status(status); + + status += test_prepruned>(); + print_status(status); ++#ifdef FPLLL_WITH_LONG_DOUBLE + status += test_prepruned>(); + print_status(status); ++#endif + status += test_prepruned>(); + print_status(status); + ++#ifdef FPLLL_WITH_LONG_DOUBLE + Pruner>::TestPruner tp; + status += tp.test_enforce(); + print_status(status); +@@ -543,6 +548,7 @@ int main(int argc, char *argv[]) + print_status(status); + status += tp.test_relative_volume(); + print_status(status); ++#endif + + #ifdef FPLLL_WITH_QD + Pruner>::TestPruner tp2; diff --git a/build/pkgs/libfplll/spkg-install b/build/pkgs/libfplll/spkg-install index e96c2526de5..279914a97cd 100755 --- a/build/pkgs/libfplll/spkg-install +++ b/build/pkgs/libfplll/spkg-install @@ -15,6 +15,7 @@ if [ "$UNAME" = "CYGWIN" ]; then fi CXXFLAGS="$CXXFLAGS -fPIC $INCLUDES -L$SAGE_LOCAL/lib" CPPFLAGS="$INCLUDES" +CONFIGUREFLAGS="--prefix=$SAGE_LOCAL --libdir=$SAGE_LOCAL/lib" # Allow the user to specify a compiler flag for 64-bit builds. # This will be -m64 with both Sun Studio and gcc, but other compilers @@ -35,14 +36,14 @@ if [ "$UNAME" = "CYGWIN" ]; then echo "Disable parallel building on Cygwin" MAKE="$MAKE -j1" export MAKE + CONFIGUREFLAGS="$CONFIGUREFLAGS --disable-recursive-enum" fi if [ "x$SAGE_DEBUG" = "xyes" ]; then CXXFLAGS="$CXXFLAGS -O0" - ENABLE_DEBUG="--enable-debug" + CONFIGUREFLAGS="$CONFIGUREFLAGS --enable-debug" else CXXFLAGS="$CXXFLAGS -O3" - ENABLE_DEBUG="" fi export CXXFLAGS="$CXXFLAGS" @@ -50,7 +51,7 @@ export CPPFLAGS="$CPPFLAGS" export CXX="$CXX" echo "Now configuring fplll..." -./configure --prefix="$SAGE_LOCAL" --libdir="$SAGE_LOCAL/lib" $ENABLE_DEBUG +./configure $CONFIGUREFLAGS if [ $? -ne 0 ]; then echo "Error configuring fplll" exit 1 From 9d99d78028ac1c47a2a60e09b0812f5a673f6ee1 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 13 Apr 2017 12:15:57 +0200 Subject: [PATCH 081/151] Adds patch for the current version of fpylll to build / pass tests on Cygwin --- build/pkgs/fpylll/package-version.txt | 2 +- .../fpylll/patches/cygwin-long-double.patch | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/fpylll/patches/cygwin-long-double.patch diff --git a/build/pkgs/fpylll/package-version.txt b/build/pkgs/fpylll/package-version.txt index 0182229b68c..36abba34a1b 100644 --- a/build/pkgs/fpylll/package-version.txt +++ b/build/pkgs/fpylll/package-version.txt @@ -1 +1 @@ -0.2.4dev +0.2.4dev.p0 diff --git a/build/pkgs/fpylll/patches/cygwin-long-double.patch b/build/pkgs/fpylll/patches/cygwin-long-double.patch new file mode 100644 index 00000000000..737bf06e2be --- /dev/null +++ b/build/pkgs/fpylll/patches/cygwin-long-double.patch @@ -0,0 +1,86 @@ +Adds fixes needed to support Cygwin (no long double support); see +https://trac.sagemath.org/ticket/22800 +diff --git a/src/fpylll/fplll/decl.pxd b/src/fpylll/fplll/decl.pxd +index b7d403c..3aa85e1 100644 +--- a/src/fpylll/fplll/decl.pxd ++++ b/src/fpylll/fplll/decl.pxd +@@ -228,12 +228,12 @@ ELSE: + FastEvaluator[FP_NR[dpe_t]] *dpe + FastEvaluator[FP_NR[dd_real]] *dd + FastEvaluator[FP_NR[qd_real]] *qd +- FastEvaluator[FP_NR[mpfr_t]] *mpfr ++ FastErrorBoundedEvaluator *mpfr + ELSE: + ctypedef union fast_evaluator_core_t: + FastEvaluator[FP_NR[double]] *double + FastEvaluator[FP_NR[dpe_t]] *dpe +- FastEvaluator[FP_NR[mpfr_t]] *mpfr ++ FastErrorBoundedEvaluator *mpfr + + IF HAVE_QD: + ctypedef union enumeration_core_t: +diff --git a/src/fpylll/fplll/pruner.pyx b/src/fpylll/fplll/pruner.pyx +index 508fcb1..c966f12 100644 +--- a/src/fpylll/fplll/pruner.pyx ++++ b/src/fpylll/fplll/pruner.pyx +@@ -22,9 +22,9 @@ Pruner + from libcpp.vector cimport vector + from math import log, exp + +-from decl cimport mpz_double, mpz_ld, mpz_dpe, mpz_mpfr, fp_nr_t, mpz_t, dpe_t, mpfr_t ++from decl cimport mpz_double, mpz_dpe, mpz_mpfr, fp_nr_t, mpz_t, dpe_t, mpfr_t + from bkz_param cimport Pruning +-from fplll cimport FT_DOUBLE, FT_LONG_DOUBLE, FT_DPE, FT_MPFR, FloatType ++from fplll cimport FT_DOUBLE, FT_DPE, FT_MPFR, FloatType + from fpylll.fplll.fplll cimport PRUNER_METHOD_GRADIENT, PRUNER_METHOD_NM, PRUNER_METHOD_HYBRID, PRUNER_METHOD_GREEDY + from fplll cimport FP_NR, Z_NR + from fplll cimport MatGSO as MatGSO_c +@@ -35,6 +35,10 @@ from fplll cimport svp_probability as svp_probability_c + from fpylll.util import adjust_radius_to_gh_bound, precision, get_precision + from fpylll.util cimport check_float_type, check_precision, check_descent_method, check_pruner_metric + ++IF HAVE_LONG_DOUBLE: ++ from fplll cimport FT_LONG_DOUBLE ++ from decl cimport mpz_ld ++ + IF HAVE_QD: + from fpylll.qd.qd cimport dd_real, qd_real + from decl cimport mpz_dd, mpz_qd +@@ -109,13 +113,14 @@ def prune(double enumeration_radius, double preproc_cost, double target, M, + if descent_method == PRUNER_METHOD_GREEDY: + return enumeration_radius, pruning + return pruning +- if ft == FT_LONG_DOUBLE: +- sig_on() +- prune_c[FP_NR[longdouble]]((pruning)._core, enumeration_radius, preproc_cost, target, vec, descent_method, metric, reset) +- sig_off() +- if descent_method == PRUNER_METHOD_GREEDY: +- return enumeration_radius, pruning +- return pruning ++ IF HAVE_LONG_DOUBLE: ++ if ft == FT_LONG_DOUBLE: ++ sig_on() ++ prune_c[FP_NR[longdouble]]((pruning)._core, enumeration_radius, preproc_cost, target, vec, descent_method, metric, reset) ++ sig_off() ++ if descent_method == PRUNER_METHOD_GREEDY: ++ return enumeration_radius, pruning ++ return pruning + if ft == FT_DPE: + sig_on() + prune_c[FP_NR[dpe_t]]((pruning)._core, enumeration_radius, preproc_cost, target, vec, descent_method, metric, reset) +diff --git a/tests/test_util.py b/tests/test_util.py +index b7ebc63..281aaf8 100644 +--- a/tests/test_util.py ++++ b/tests/test_util.py +@@ -16,7 +16,10 @@ def test_gh(): + for n in dimensions: + set_random_seed(n) + A = make_integer_matrix(n) +- M = GSO.Mat(A, float_type="ld") ++ try: ++ M = GSO.Mat(A, float_type="ld") ++ except ValueError: ++ M = GSO.Mat(A, float_type="d") + M.discover_all_rows() + M.update_gso() + radius = M.get_r(0, 0) From b4082d29c285968abe2cfce4bb8587bfba7a6a11 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 13 Apr 2017 12:51:33 +0200 Subject: [PATCH 082/151] function _simplify_ for cancelling factors in intermediate result (quotient) --- src/sage/rings/polynomial/omega.py | 46 ++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/sage/rings/polynomial/omega.py b/src/sage/rings/polynomial/omega.py index f0e94cca344..25edadca094 100644 --- a/src/sage/rings/polynomial/omega.py +++ b/src/sage/rings/polynomial/omega.py @@ -349,6 +349,52 @@ def MacMahonOmega(var, expression, denominator=None, op=operator.ge, simplify=Factorization_simplify) +def _simplify_(numerator, terms): + r""" + Cancels common factors of numerator and denominator. + + INPUT: + + - ``numerator`` -- a Laurent polynomial + + - ``terms`` -- a tuple or other iterable of Laurent polynomials + + The denominator is the product of factors `1 - t` for each + `t` in ``terms``. + + OUTPUT: + + A pair of a Laurent polynomial and a tuple of Laurent polynomials + representing numerator and denominator as described in the + INPUT-section. + + EXAMPLES:: + + sage: from sage.rings.polynomial.omega import _simplify_ + sage: L. = LaurentPolynomialRing(ZZ) + sage: _simplify_(1-x^2, (x, y)) + (x + 1, (y,)) + + TESTS:: + + sage: _simplify_(1-x^2, (x, -x)) + (1, ()) + sage: _simplify_(1-x^2, (y^2, y)) + (-x^2 + 1, (y^2, y)) + sage: _simplify_(1-x^2, (x, L(2))) + (x + 1, (2,)) + """ + new_terms = [] + for t in terms: + if not t.is_constant(): + quo, rem = numerator.quo_rem(1 - t) + if rem == 0: + numerator = quo + continue + new_terms.append(t) + return numerator, tuple(new_terms) + + def _Omega_(A, decoded_factors): r""" Helper function for :func:`MacMahonOmega` which accesses the low level functions From b02b7f1399e07b467fbfdea21a7dcff496a44935 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 13 Apr 2017 12:51:50 +0200 Subject: [PATCH 083/151] use and test new feature in _Omega_-function --- src/sage/rings/polynomial/omega.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/omega.py b/src/sage/rings/polynomial/omega.py index 25edadca094..ea1cbd3ed69 100644 --- a/src/sage/rings/polynomial/omega.py +++ b/src/sage/rings/polynomial/omega.py @@ -438,6 +438,12 @@ def _Omega_(A, decoded_factors): (42, ()) sage: _Omega_({-1: 42}, []) (0, ()) + + + :: + + sage: MacMahonOmega(mu, 1 - x^2, [1 - x*mu, 1 - y/mu]) + (x + 1) * (-x*y + 1)^-1 """ if not decoded_factors: return sum(c for a, c in iteritems(A) if a >= 0), tuple() @@ -462,7 +468,8 @@ def _Omega_(A, decoded_factors): if numerator == 0: factors_denominator = tuple() - return numerator, tuple(f.subs(rules) for f in factors_denominator) + return _simplify_(numerator, + tuple(f.subs(rules) for f in factors_denominator)) @cached_function From 3a5b000527841a04824fa26ffd11e707e438cad3 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 14 Apr 2017 10:52:09 +0200 Subject: [PATCH 084/151] Re-order patch so that configure.ac is modified before configure --- .../patches/disable-recursive-enum.patch | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/build/pkgs/libfplll/patches/disable-recursive-enum.patch b/build/pkgs/libfplll/patches/disable-recursive-enum.patch index c3d025c9b0f..2dd60ad9211 100644 --- a/build/pkgs/libfplll/patches/disable-recursive-enum.patch +++ b/build/pkgs/libfplll/patches/disable-recursive-enum.patch @@ -3,6 +3,24 @@ due to bugs in its binutils version). This patch adds a ./configure flag to make that easier. https://trac.sagemath.org/ticket/22800 +diff --git a/configure.ac b/configure.ac +index 401bbba..b367cb5 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -124,6 +124,13 @@ AC_ARG_WITH(max-enumeration-dimension, + + AC_DEFINE_UNQUOTED([FPLLL_MAX_ENUM_DIMENSION], $max_enumeration_dimension, [maximum supported enumeration dimension]) + ++AC_ARG_ENABLE(recursive-enum, ++ AS_HELP_STRING([--disable-recursive-enum], ++ [Disable recursive enumeration])) ++ ++AS_IF([test "x$enable_recursive_enum" != "xno"], [ ++ AC_DEFINE([FPLLL_WITH_RECURSIVE_ENUM], [1], [recursive enumeration enabled])]) ++ + # Store version numbers in header + + AC_DEFINE_UNQUOTED([FPLLL_MAJOR_VERSION],[$FPLLL_MAJOR_VERSION],[major version]) diff --git a/configure b/configure index ce0e7fd..f1eccd5 100755 --- a/configure @@ -44,24 +62,6 @@ index ce0e7fd..f1eccd5 100755 # Store version numbers in header -diff --git a/configure.ac b/configure.ac -index 401bbba..b367cb5 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -124,6 +124,13 @@ AC_ARG_WITH(max-enumeration-dimension, - - AC_DEFINE_UNQUOTED([FPLLL_MAX_ENUM_DIMENSION], $max_enumeration_dimension, [maximum supported enumeration dimension]) - -+AC_ARG_ENABLE(recursive-enum, -+ AS_HELP_STRING([--disable-recursive-enum], -+ [Disable recursive enumeration])) -+ -+AS_IF([test "x$enable_recursive_enum" != "xno"], [ -+ AC_DEFINE([FPLLL_WITH_RECURSIVE_ENUM], [1], [recursive enumeration enabled])]) -+ - # Store version numbers in header - - AC_DEFINE_UNQUOTED([FPLLL_MAJOR_VERSION],[$FPLLL_MAJOR_VERSION],[major version]) diff --git a/fplll/enum/enumerate_base.h b/fplll/enum/enumerate_base.h index 3fcffa5..860b207 100644 --- a/fplll/enum/enumerate_base.h From b85140a17d884f04d98d4b4cf4fc94a4084b6030 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 13 Apr 2017 17:44:30 +0200 Subject: [PATCH 085/151] Stop using cdefs.pxi --- src/sage/coding/binary_code.pyx | 18 +++++++++------- src/sage/combinat/expnums.pyx | 3 +-- src/sage/graphs/chrompoly.pyx | 13 ++++++++---- src/sage/graphs/generic_graph_pyx.pyx | 21 +++++++++++-------- .../graphs/graph_decompositions/cutwidth.pyx | 20 +++++++++++------- .../graph_decompositions/fast_digraph.pyx | 9 ++------ .../vertex_separation.pyx | 21 ++++++++++++------- src/sage/graphs/matchpoly.pyx | 1 - src/sage/libs/ecl.pyx | 2 +- src/sage/libs/lcalc/lcalc_Lfunction.pyx | 4 ++-- src/sage/libs/libecm.pyx | 14 +++++++------ src/sage/libs/ntl/ntl_ZZ.pyx | 1 - src/sage/libs/ntl/ntl_ZZ_p.pyx | 1 - src/sage/libs/ntl/ntl_ZZ_pX.pyx | 2 +- src/sage/libs/ntl/ntl_lzz_p.pyx | 17 ++++++--------- src/sage/libs/ntl/ntl_lzz_pContext.pyx | 15 +++++-------- src/sage/libs/ntl/ntl_lzz_pX.pyx | 17 ++++++--------- src/sage/matrix/matrix_cyclo_dense.pyx | 1 - src/sage/matrix/matrix_integer_dense.pyx | 7 ++++--- src/sage/matrix/matrix_modn_sparse.pyx | 3 ++- src/sage/matrix/matrix_rational_dense.pyx | 7 ++++--- src/sage/matrix/misc.pyx | 3 ++- src/sage/modular/pollack_stevens/dist.pyx | 1 - src/sage/rings/bernmm.pyx | 2 +- src/sage/rings/fraction_field_FpT.pyx | 2 +- src/sage/rings/integer.pyx | 20 +++++++++--------- src/sage/rings/integer_ring.pyx | 6 +++--- .../rings/number_field/totallyreal_data.pyx | 8 ++++--- .../rings/padics/padic_ZZ_pX_FM_element.pyx | 3 ++- src/sage/rings/padics/padic_ZZ_pX_element.pyx | 2 +- src/sage/rings/polynomial/real_roots.pyx | 7 +++---- src/sage/rings/real_double.pyx | 13 ++++++------ src/sage/rings/real_mpfi.pyx | 3 ++- 33 files changed, 136 insertions(+), 131 deletions(-) diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index 82ac22a98a9..9a204dfc726 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -30,15 +30,19 @@ AUTHOR: """ -#******************************************************************************* -# Copyright (C) 2007 Robert L. Miller +#***************************************************************************** +# Copyright (C) 2007 Robert L. Miller # -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#******************************************************************************* -from __future__ import print_function +# 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/ +#***************************************************************************** -include 'sage/ext/cdefs.pxi' +from __future__ import absolute_import, print_function + +from libc.string cimport memcpy from cpython.mem cimport * from cpython.object cimport PyObject_RichCompare include "cysignals/memory.pxi" diff --git a/src/sage/combinat/expnums.pyx b/src/sage/combinat/expnums.pyx index 3fedb1ce92d..e241594334d 100644 --- a/src/sage/combinat/expnums.pyx +++ b/src/sage/combinat/expnums.pyx @@ -7,10 +7,9 @@ AUTHORS: """ include "cysignals/memory.pxi" -include "sage/ext/cdefs.pxi" +from sage.libs.gmp.mpz cimport * from sage.rings.integer cimport Integer - from sage.rings.integer_ring import ZZ def expnums(int n, int aa): diff --git a/src/sage/graphs/chrompoly.pyx b/src/sage/graphs/chrompoly.pyx index c40412f6944..e05dc858ff7 100644 --- a/src/sage/graphs/chrompoly.pyx +++ b/src/sage/graphs/chrompoly.pyx @@ -13,17 +13,22 @@ REFERENCE: """ #***************************************************************************** -# Copyright (C) 2008 Robert Miller and Gordon Royle +# Copyright (C) 2008 Robert Miller +# Copyright (C) 2008 Gordon Royle # -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ +# 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.libs.gmp.mpz cimport * from sage.rings.integer_ring import ZZ from sage.rings.integer cimport Integer from sage.ext.memory_allocator cimport MemoryAllocator from sage.misc.all import prod include "cysignals/signals.pxi" -include 'sage/ext/cdefs.pxi' def chromatic_polynomial(G, return_tree_basis=False): diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index 09f1dc2a32b..af69556c94b 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -10,23 +10,26 @@ AUTHORS: """ #***************************************************************************** -# Copyright (C) 2007 Robert L. Miller -# 2007 Robert W. Bradshaw +# Copyright (C) 2007 Robert L. Miller +# 2007 Robert W. Bradshaw # -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ +# 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 __future__ import print_function + +from __future__ import absolute_import, print_function include "cysignals/signals.pxi" -include 'sage/ext/cdefs.pxi' include "cysignals/memory.pxi" include "sage/data_structures/binary_matrix.pxi" +from libc.math cimport sqrt +from libc.string cimport memset -# import from Python standard library +from sage.libs.gmp.mpz cimport * from sage.misc.prandom import random - -# import from third-party library from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph diff --git a/src/sage/graphs/graph_decompositions/cutwidth.pyx b/src/sage/graphs/graph_decompositions/cutwidth.pyx index 6761019de5b..8f31566a38a 100644 --- a/src/sage/graphs/graph_decompositions/cutwidth.pyx +++ b/src/sage/graphs/graph_decompositions/cutwidth.pyx @@ -156,21 +156,25 @@ Authors Methods ------- """ + #***************************************************************************** -# Copyright (C) 2015 David Coudert +# Copyright (C) 2015 David Coudert # -# Distributed under the terms of the GNU General Public License (GPL) -# 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/ +# 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 __future__ import print_function + +from __future__ import absolute_import, print_function include "cysignals/signals.pxi" -include 'sage/ext/cdefs.pxi' +from libc.stdint cimport uint8_t +from libc.string cimport memset + from sage.graphs.graph_decompositions.fast_digraph cimport FastDigraph, popcount32 from sage.graphs.graph_decompositions.vertex_separation import is_valid_ordering -from libc.stdint cimport uint8_t include "cysignals/memory.pxi" from sage.rings.integer_ring import ZZ diff --git a/src/sage/graphs/graph_decompositions/fast_digraph.pyx b/src/sage/graphs/graph_decompositions/fast_digraph.pyx index 99b2eb7c35f..c1feeae1114 100644 --- a/src/sage/graphs/graph_decompositions/fast_digraph.pyx +++ b/src/sage/graphs/graph_decompositions/fast_digraph.pyx @@ -15,7 +15,6 @@ set if element i belongs to the set. from __future__ import print_function include "cysignals/memory.pxi" -include 'sage/ext/cdefs.pxi' from libc.stdint cimport uint8_t @@ -32,11 +31,7 @@ cdef class FastDigraph: raise OverflowError("Too many vertices. This structure can only encode digraphs on at most %i vertices"%(8*sizeof(int))) self.n = D.order() - self.graph = NULL - - self.graph = sig_malloc(self.n*sizeof(int)) - - memset(self.graph, 0, self.n * sizeof(int)) + self.graph = check_calloc(self.n, sizeof(int)) cdef int i, j cdef int tmp @@ -61,7 +56,7 @@ cdef class FastDigraph: tmp |= 1 << vertices_to_int[v] self.graph[vertices_to_int[u]] = tmp - self.degree = sig_malloc(self.n*sizeof(int)) + self.degree = check_allocarray(self.n, sizeof(int)) for i in range(self.n): self.degree[i] = popcount32(self.graph[i]) diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index eb744974a57..de8bd6cdfa3 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -270,22 +270,27 @@ Authors Methods ------- """ -from __future__ import print_function + +#***************************************************************************** +# Copyright (C) 2011 Nathann Cohen +# +# 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 __future__ import absolute_import, print_function include "cysignals/memory.pxi" include "cysignals/signals.pxi" -include 'sage/ext/cdefs.pxi' +from libc.string cimport memset from sage.graphs.graph_decompositions.fast_digraph cimport FastDigraph, compute_out_neighborhood_cardinality, popcount32 from libc.stdint cimport uint8_t, int8_t include "sage/data_structures/binary_matrix.pxi" from sage.graphs.base.static_dense_graph cimport dense_graph_init -#***************************************************************************** -# Copyright (C) 2011 Nathann Cohen -# -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** ############### # Lower Bound # diff --git a/src/sage/graphs/matchpoly.pyx b/src/sage/graphs/matchpoly.pyx index 9c7ea5e202d..dc3f0b7b9b8 100644 --- a/src/sage/graphs/matchpoly.pyx +++ b/src/sage/graphs/matchpoly.pyx @@ -36,7 +36,6 @@ from sage.rings.integer_ring import ZZ from sage.rings.integer cimport Integer from sage.misc.all import prod include "cysignals/signals.pxi" -include 'sage/ext/cdefs.pxi' include "cysignals/memory.pxi" from sage.libs.flint.fmpz cimport * diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index 50454a8ed09..60f91a9935d 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -16,13 +16,13 @@ from __future__ import print_function #adapted to work with pure Python types. include "cysignals/signals.pxi" -include "sage/ext/cdefs.pxi" from libc.stdlib cimport abort from libc.signal cimport SIGINT, SIGBUS, SIGSEGV, SIGCHLD from libc.signal cimport raise_ as signal_raise from posix.signal cimport sigaction, sigaction_t +from sage.libs.gmp.types cimport mpz_t from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from cpython.object cimport Py_EQ, Py_NE diff --git a/src/sage/libs/lcalc/lcalc_Lfunction.pyx b/src/sage/libs/lcalc/lcalc_Lfunction.pyx index 10eabdf6526..c92ff30f570 100644 --- a/src/sage/libs/lcalc/lcalc_Lfunction.pyx +++ b/src/sage/libs/lcalc/lcalc_Lfunction.pyx @@ -20,9 +20,9 @@ AUTHORS: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -include "cysignals/signals.pxi" -include "sage/ext/cdefs.pxi" +include "cysignals/signals.pxi" +from sage.libs.gmp.mpz cimport * from sage.libs.mpfr cimport * from sage.rings.integer cimport Integer diff --git a/src/sage/libs/libecm.pyx b/src/sage/libs/libecm.pyx index 755f03ee1b3..c06b9b96048 100644 --- a/src/sage/libs/libecm.pyx +++ b/src/sage/libs/libecm.pyx @@ -27,20 +27,22 @@ EXAMPLES:: sage: ecmfactor(2^128+1,1000,sigma=227140902) (True, 5704689200685129054721, 227140902) """ + #***************************************************************************** # Copyright (C) 2008 Robert Miller # Copyright (C) 2012 Jeroen Demeyer # -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. +# 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 __future__ import print_function -include 'sage/ext/cdefs.pxi' -include "cysignals/signals.pxi" +from __future__ import absolute_import, print_function +include "cysignals/signals.pxi" +from sage.libs.gmp.mpz cimport * from sage.rings.integer cimport Integer cdef extern from "ecm.h": diff --git a/src/sage/libs/ntl/ntl_ZZ.pyx b/src/sage/libs/ntl/ntl_ZZ.pyx index 7f641e9e4c7..4c29605ae68 100644 --- a/src/sage/libs/ntl/ntl_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_ZZ.pyx @@ -16,7 +16,6 @@ from __future__ import print_function include "cysignals/signals.pxi" include "sage/ext/stdsage.pxi" -include "sage/ext/cdefs.pxi" include 'misc.pxi' include 'decl.pxi' diff --git a/src/sage/libs/ntl/ntl_ZZ_p.pyx b/src/sage/libs/ntl/ntl_ZZ_p.pyx index 86156027f48..3f5db0ba969 100644 --- a/src/sage/libs/ntl/ntl_ZZ_p.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_p.pyx @@ -15,7 +15,6 @@ from __future__ import print_function include "cysignals/signals.pxi" -include "sage/ext/cdefs.pxi" include 'misc.pxi' include 'decl.pxi' diff --git a/src/sage/libs/ntl/ntl_ZZ_pX.pyx b/src/sage/libs/ntl/ntl_ZZ_pX.pyx index ceccc32005e..9fe37f096f3 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pX.pyx @@ -13,7 +13,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import division, print_function +from __future__ import absolute_import, division, print_function include "cysignals/signals.pxi" include "sage/ext/stdsage.pxi" diff --git a/src/sage/libs/ntl/ntl_lzz_p.pyx b/src/sage/libs/ntl/ntl_lzz_p.pyx index 9aba1879d7b..42063ddcd41 100644 --- a/src/sage/libs/ntl/ntl_lzz_p.pyx +++ b/src/sage/libs/ntl/ntl_lzz_p.pyx @@ -18,24 +18,19 @@ AUTHORS: #***************************************************************************** # Copyright (C) 2005 William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# +# 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 __future__ import division +from __future__ import absolute_import, division include "cysignals/signals.pxi" -include "sage/ext/cdefs.pxi" include 'misc.pxi' include 'decl.pxi' +from sage.libs.gmp.mpz cimport * from cpython.object cimport Py_EQ, Py_NE from sage.rings.integer import Integer diff --git a/src/sage/libs/ntl/ntl_lzz_pContext.pyx b/src/sage/libs/ntl/ntl_lzz_pContext.pyx index db20eaa389b..284f31304c1 100644 --- a/src/sage/libs/ntl/ntl_lzz_pContext.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pContext.pyx @@ -1,19 +1,14 @@ #***************************************************************************** # Copyright (C) 2005 William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# +# 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/ #***************************************************************************** -include "sage/ext/cdefs.pxi" +from sage.libs.gmp.mpz cimport mpz_get_si include 'misc.pxi' include 'decl.pxi' diff --git a/src/sage/libs/ntl/ntl_lzz_pX.pyx b/src/sage/libs/ntl/ntl_lzz_pX.pyx index 5225cdfd5f5..ef0285bd7fd 100644 --- a/src/sage/libs/ntl/ntl_lzz_pX.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pX.pyx @@ -10,24 +10,19 @@ AUTHORS: #***************************************************************************** # Copyright (C) 2005 William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# +# 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 __future__ import division +from __future__ import absolute_import, division include "cysignals/signals.pxi" -include "sage/ext/cdefs.pxi" include 'misc.pxi' include 'decl.pxi' +from sage.libs.gmp.mpz cimport * from cpython.object cimport Py_EQ, Py_NE from sage.rings.integer import Integer diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index a6cb21459b6..a5db5b69395 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -39,7 +39,6 @@ AUTHORS: from __future__ import absolute_import include "cysignals/signals.pxi" -include "sage/ext/cdefs.pxi" include "sage/libs/ntl/decl.pxi" from sage.structure.element cimport ModuleElement, RingElement, Element, Vector diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index bf60610d921..fd5971b9429 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -54,11 +54,12 @@ TESTS:: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -from __future__ import absolute_import + +from __future__ import absolute_import, print_function from libc.stdint cimport int64_t -include "sage/ext/cdefs.pxi" +from libc.string cimport strcpy, strlen +from sage.libs.gmp.mpz cimport * from sage.modules.vector_integer_dense cimport Vector_integer_dense diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx index fb1b6e07b3c..778b11c9423 100644 --- a/src/sage/matrix/matrix_modn_sparse.pyx +++ b/src/sage/matrix/matrix_modn_sparse.pyx @@ -74,9 +74,9 @@ TESTS:: # The full text of the GPL is available at: # http://www.gnu.org/licenses/ ############################################################################# + from __future__ import absolute_import -include "sage/ext/cdefs.pxi" include "cysignals/signals.pxi" include "cysignals/memory.pxi" @@ -84,6 +84,7 @@ from sage.modules.vector_modn_sparse cimport * from cpython.sequence cimport * +from sage.libs.gmp.mpz cimport mpz_init_set_si cimport sage.matrix.matrix as matrix cimport sage.matrix.matrix_sparse as matrix_sparse cimport sage.matrix.matrix_dense as matrix_dense diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 323934c2bc7..312fc48b5ac 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -50,12 +50,13 @@ TESTS:: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import absolute_import -from __future__ import print_function + +from __future__ import absolute_import, print_function + +from libc.string cimport strcpy, strlen from sage.modules.vector_rational_dense cimport Vector_rational_dense -include "sage/ext/cdefs.pxi" include "cysignals/signals.pxi" include "sage/ext/stdsage.pxi" diff --git a/src/sage/matrix/misc.pyx b/src/sage/matrix/misc.pyx index 1b09211c822..3ba53d9cfe7 100644 --- a/src/sage/matrix/misc.pyx +++ b/src/sage/matrix/misc.pyx @@ -12,9 +12,10 @@ relevant classes and this file deleted. from __future__ import absolute_import include "cysignals/signals.pxi" -include "sage/ext/cdefs.pxi" from sage.ext.mod_int cimport * +from sage.libs.gmp.mpz cimport * +from sage.libs.gmp.mpq cimport * from sage.libs.mpfr cimport * from sage.arith.rational_reconstruction cimport mpq_rational_reconstruction diff --git a/src/sage/modular/pollack_stevens/dist.pyx b/src/sage/modular/pollack_stevens/dist.pyx index 34d0ab1f5b0..945f6bf1a24 100644 --- a/src/sage/modular/pollack_stevens/dist.pyx +++ b/src/sage/modular/pollack_stevens/dist.pyx @@ -52,7 +52,6 @@ from sage.rings.rational cimport Rational from sage.misc.misc import verbose, cputime from sage.rings.infinity import Infinity -include "sage/ext/cdefs.pxi" include "cysignals/signals.pxi" include "sage/ext/stdsage.pxi" diff --git a/src/sage/rings/bernmm.pyx b/src/sage/rings/bernmm.pyx index ef088488fa1..9538e3d16c3 100644 --- a/src/sage/rings/bernmm.pyx +++ b/src/sage/rings/bernmm.pyx @@ -14,8 +14,8 @@ AUTHOR: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/cdefs.pxi" include "cysignals/signals.pxi" +from sage.libs.gmp.types cimport mpq_t cdef extern from "bernmm/bern_rat.h": diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index e95e02a8370..fdb885b48c1 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -3,9 +3,9 @@ from __future__ import print_function import sys -include "sage/ext/cdefs.pxi" include "cysignals/signals.pxi" +from sage.libs.gmp.mpz cimport * from sage.rings.all import GF from sage.libs.flint.nmod_poly cimport * from sage.libs.flint.ulong_extras cimport n_jacobi diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index f4d00e8e935..ca7514c8ff3 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -126,22 +126,25 @@ real field R:: # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function + +from __future__ import absolute_import, print_function # Do not create any Integer, especially non cdef'ed ones, before the hooked # creation and deletion are setup by the call to hook_fast_tp_functions cimport cython +from libc.math cimport ldexp +from libc.string cimport memcpy import operator - import sys +from sage.libs.gmp.mpz cimport * +from sage.libs.gmp.mpq cimport * from sage.misc.superseded import deprecated_function_alias from sage.misc.long cimport pyobject_to_long include "cysignals/signals.pxi" -include "sage/ext/cdefs.pxi" include "sage/ext/stdsage.pxi" include "cysignals/memory.pxi" from cpython.list cimport * @@ -337,7 +340,7 @@ from sage.structure.element cimport EuclideanDomainElement, ModuleElement, Eleme from sage.structure.element import bin_op from sage.structure.coerce_exceptions import CoercionException -import integer_ring +from . import integer_ring cdef Parent the_integer_ring = integer_ring.ZZ # The documentation for the ispseudoprime() function in the PARI @@ -2277,8 +2280,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): cdef int pow_2 cdef size_t upper,lower,middle - import real_mpfi - R=real_mpfi.RIF + from .real_mpfi import RIF as R rif_self = R(self) @@ -2458,8 +2460,8 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): # (suggested by David Harvey and Carl Witty) # "for randomly distributed integers, the chance of this # interval-based comparison failing is absurdly low" - import real_mpfi - approx_compare = real_mpfi.RIF(m)**guess + from .real_mpfi import RIF + approx_compare = RIF(m)**guess if self > approx_compare: guess_filled = 1 elif self < approx_compare: @@ -6837,7 +6839,6 @@ cdef class int_to_Z(Morphism): sage: f.parent() Set of Morphisms from Set of Python objects of type 'int' to Integer Ring in Category of sets """ - import integer_ring import sage.categories.homset from sage.structure.parent import Set_PythonType Morphism.__init__(self, sage.categories.homset.Hom(Set_PythonType(int), integer_ring.ZZ)) @@ -6891,7 +6892,6 @@ cdef class long_to_Z(Morphism): -10000000000000000000001 """ def __init__(self): - import integer_ring import sage.categories.homset from sage.structure.parent import Set_PythonType Morphism.__init__(self, sage.categories.homset.Hom(Set_PythonType(long), integer_ring.ZZ)) diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 722ccdf5a24..9dec9d241bd 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -42,10 +42,9 @@ other types will also coerce to the integers, when it makes sense. # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import absolute_import -from __future__ import print_function -include "sage/ext/cdefs.pxi" +from __future__ import absolute_import, print_function + include "sage/ext/stdsage.pxi" include "cysignals/signals.pxi" @@ -53,6 +52,7 @@ from cpython.int cimport * from cpython.list cimport * from cpython.object cimport Py_NE +from sage.libs.gmp.mpz cimport * import sage.rings.infinity import sage.rings.rational import sage.rings.rational_field diff --git a/src/sage/rings/number_field/totallyreal_data.pyx b/src/sage/rings/number_field/totallyreal_data.pyx index 10976464932..81023469a66 100644 --- a/src/sage/rings/number_field/totallyreal_data.pyx +++ b/src/sage/rings/number_field/totallyreal_data.pyx @@ -22,12 +22,14 @@ AUTHORS: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -include "sage/ext/cdefs.pxi" +from __future__ import absolute_import, print_function + include "cysignals/memory.pxi" +from libc.math cimport sqrt from sage.arith.all import binomial, gcd +from sage.libs.gmp.mpz cimport * from sage.rings.rational_field import RationalField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.real_mpfi import RealIntervalField @@ -450,7 +452,7 @@ def easy_is_irreducible_py(f): cdef double eps_global eps_global = 10.**(-4) -from totallyreal_phc import __lagrange_bounds_phc +from .totallyreal_phc import __lagrange_bounds_phc cdef class tr_data: r""" diff --git a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx index 457dce8ce60..176a9cbfccc 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -119,7 +119,6 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/cdefs.pxi" include "sage/ext/stdsage.pxi" include "cysignals/signals.pxi" include "sage/libs/ntl/decl.pxi" @@ -131,6 +130,8 @@ from sage.rings.integer_ring import ZZ from sage.rings.integer cimport Integer from sage.rings.padics.padic_generic_element cimport pAdicGenericElement from sage.rings.padics.padic_ext_element cimport pAdicExtElement +from sage.libs.gmp.mpz cimport * +from sage.libs.gmp.mpq cimport * from sage.libs.ntl.ntl_ZZ_pX cimport ntl_ZZ_pX from sage.libs.ntl.ntl_ZZX cimport ntl_ZZX from sage.libs.ntl.ntl_ZZ cimport ntl_ZZ diff --git a/src/sage/rings/padics/padic_ZZ_pX_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_element.pyx index aa1f1d83bd4..d24e0451bc1 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_element.pyx @@ -22,10 +22,10 @@ AUTHORS: from __future__ import print_function from sage.ext.stdsage cimport PY_NEW -include "sage/ext/cdefs.pxi" from cpython.list cimport * include "sage/libs/ntl/decl.pxi" +from sage.libs.gmp.mpz cimport * from sage.libs.ntl.ntl_ZZ cimport ntl_ZZ from sage.libs.ntl.ntl_ZZ_p cimport ntl_ZZ_p from sage.libs.ntl.ntl_ZZ_pX cimport ntl_ZZ_pX diff --git a/src/sage/rings/polynomial/real_roots.pyx b/src/sage/rings/polynomial/real_roots.pyx index 90a9cc04b6e..821ee40276c 100644 --- a/src/sage/rings/polynomial/real_roots.pyx +++ b/src/sage/rings/polynomial/real_roots.pyx @@ -152,11 +152,10 @@ from sage.rings.real_mpfr cimport RealNumber cimport numpy -# TODO: Just for the fabs function below -from math import fabs - -include "sage/ext/cdefs.pxi" +from libc.math cimport fabs, sqrt, ldexp, frexp +from sage.libs.gmp.mpz cimport * +from sage.libs.gmp.mpq cimport * from sage.libs.mpfr cimport * cdef class interval_bernstein_polynomial: diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 87a29514ac6..c35d61c0125 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -39,16 +39,17 @@ Test NumPy conversions:: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** + from __future__ import print_function from cpython.object cimport * from cpython.float cimport * include "sage/ext/python_debug.pxi" -include 'sage/ext/cdefs.pxi' include 'sage/ext/stdsage.pxi' include "cysignals/signals.pxi" from sage.libs.gsl.all cimport * cimport libc.math +from libc.string cimport memcpy gsl_set_error_handler_off() @@ -835,7 +836,7 @@ cdef class RealDoubleElement(FieldElement): """ # First, check special values if self._value == 0: - return RealDoubleElement(ldexp(1.0, -1074)) + return RealDoubleElement(libc.math.ldexp(1.0, -1074)) if gsl_isnan(self._value): return self if gsl_isinf(self._value): @@ -843,12 +844,12 @@ cdef class RealDoubleElement(FieldElement): # Normal case cdef int e - frexp(self._value, &e) + libc.math.frexp(self._value, &e) e -= 53 # Correction for denormals if e < -1074: e = -1074 - return RealDoubleElement(ldexp(1.0, e)) + return RealDoubleElement(libc.math.ldexp(1.0, e)) def real(self): """ @@ -1761,7 +1762,7 @@ cdef class RealDoubleElement(FieldElement): [1.4142135623730951*I, -1.4142135623730951*I] """ if self._value >= 0: - x = self._new_c(sqrt(self._value)) + x = self._new_c(libc.math.sqrt(self._value)) if all: if x.is_zero(): return [x] @@ -2469,7 +2470,7 @@ cdef class RealDoubleElement(FieldElement): return self._parent.nan() while True: a1 = (a+b)/2 - b1 = sqrt(a*b) + b1 = libc.math.sqrt(a*b) if abs((b1/a1)-1) < eps: return self._new_c(a1) a, b = a1, b1 diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index d4969151073..439bc2013b6 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -247,10 +247,11 @@ import sys import operator include "cysignals/signals.pxi" -include "sage/ext/cdefs.pxi" from cpython.mem cimport * from cpython.object cimport Py_EQ, Py_NE, Py_LT, Py_LE, Py_GT, Py_GE +from libc.string cimport strlen +from sage.libs.gmp.mpz cimport * cimport sage.rings.ring cimport sage.structure.element from sage.structure.element cimport RingElement, Element, ModuleElement From 97a2ce4eb2a81977c261f5f0d4668c7717bb0451 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 10 Aug 2016 10:46:02 +0200 Subject: [PATCH 086/151] On Cygwin only, make PPL think setitimer is not supported, to prevent it from overriding cysignals' SIGALRM handler. This is not an issue on other platforms, where SIGPRFL is used instead of SIGALRM. This is a hacky workaround for https://trac.sagemath.org/ticket/21190 --- build/pkgs/ppl/spkg-install | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/pkgs/ppl/spkg-install b/build/pkgs/ppl/spkg-install index 83b60df567b..4a87e49474c 100755 --- a/build/pkgs/ppl/spkg-install +++ b/build/pkgs/ppl/spkg-install @@ -48,6 +48,10 @@ if [ $? -ne 0 ]; then fi fi +if [ "$UNAME" = "CYGWIN" ]; then + sed -i 's/#define HAVE_DECL_SETITIMER 1/#define HAVE_DECL_SETITIMER 0/' config.h +fi + $MAKE if [ $? -ne 0 ]; then echo >&2 "Error building the Parma Polyhedra Library." From f8dbb749ee4f9ee621c15dd75639c6128d35dba8 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 14 Apr 2017 11:12:20 +0200 Subject: [PATCH 087/151] Add a comment referencing the ticket --- build/pkgs/ppl/spkg-install | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/pkgs/ppl/spkg-install b/build/pkgs/ppl/spkg-install index 4a87e49474c..dcfbd0f1eb9 100755 --- a/build/pkgs/ppl/spkg-install +++ b/build/pkgs/ppl/spkg-install @@ -48,6 +48,9 @@ if [ $? -ne 0 ]; then fi fi +# Workaround to disable PPL's "watchdog timer", preventing it from clobbering +# Cysignals' SIGALRM handler on Cygwin; see +# https://trac.sagemath.org/ticket/21190 if [ "$UNAME" = "CYGWIN" ]; then sed -i 's/#define HAVE_DECL_SETITIMER 1/#define HAVE_DECL_SETITIMER 0/' config.h fi From 2d30f4911305c6ab3f17bb4146671a5d93c6fe55 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 14 Apr 2017 21:58:28 +0100 Subject: [PATCH 088/151] right order for gfan --- build/pkgs/gfan/patches/Makefile.patch | 29 ++++++++++++++++++++------ build/pkgs/gfan/spkg-install | 4 ---- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/build/pkgs/gfan/patches/Makefile.patch b/build/pkgs/gfan/patches/Makefile.patch index 804315ecfc2..1c51753d7a5 100644 --- a/build/pkgs/gfan/patches/Makefile.patch +++ b/build/pkgs/gfan/patches/Makefile.patch @@ -1,6 +1,23 @@ ---- a/Makefile.orig 2012-07-11 11:50:44.733022911 +1200 -+++ b/Makefile 2012-07-11 12:20:02.644997882 +1200 -@@ -75,18 +75,10 @@ +diff --git a/Makefile b/Makefile +index 0892969..f816b22 100644 +--- a/Makefile ++++ b/Makefile +@@ -33,13 +33,8 @@ GMP_LINKOPTIONS = $(gmppath)/lib/libgmp.a + GMP_INCLUDEOPTIONS = -I $(gmppath)/include + endif + +-ifeq ($(cddpath),) +-CDD_LINKOPTIONS = -L/usr/local -lcddgmp ++CDD_LINKOPTIONS = -L$SAGE_LOCAL/lib -lcddgmp -lgmp -lm + CDD_INCLUDEOPTIONS = +-else +-CDD_LINKOPTIONS = $(cddpath)/lib/libcddgmp.a +-CDD_INCLUDEOPTIONS = -I $(cddpath)/include +-endif + + ifeq ($(soplex),) + SOPLEX_PATH = +@@ -75,18 +70,10 @@ ADDITIONALINCLUDEOPTIONS = $(CDD_INCLUDEOPTIONS) $(GMP_INCLUDEOPTIONS) $(SOPLEX_ MKDIR=mkdir -p @@ -21,16 +38,16 @@ CATSOBJECTS = $(GCATSPATH)lp_cdd.o \ $(SOPLEX_OBJECTS) \ -@@ -317,7 +309,7 @@ +@@ -317,7 +304,7 @@ $(PREFIX): default: $(OBJECTS) $(ADDITIONALOBJECTS) $(EXECS) $(MAIN): $(OBJECTS) - $(CCLINKER) $(OBJECTS) $(ADDITIONALLINKOPTIONS) $(GPROFFLAG) -o $(MAIN) -+ $(CXX) $(CXXFLAGS) $(OBJECTS) $(ADDITIONALLINKOPTIONS) $(GPROFFLAG) -o $(MAIN) $(LDFLAGS) ++ $(CXX) $(CXXFLAGS) -o $(MAIN) $(LDFLAGS) $(OBJECTS) $(ADDITIONALLINKOPTIONS) $(GPROFFLAG) release: rm -f -r $(RELEASEDIR)/* -@@ -431,11 +423,11 @@ +@@ -431,11 +418,11 @@ library: cp gfanlib_* gfanlib/ tar zcf - gfanlib > gfanlib.tar.gz .c.o: diff --git a/build/pkgs/gfan/spkg-install b/build/pkgs/gfan/spkg-install index d03a87a9c61..ad0b7c64bb7 100755 --- a/build/pkgs/gfan/spkg-install +++ b/build/pkgs/gfan/spkg-install @@ -6,10 +6,6 @@ if [[ -z "$SAGE_LOCAL" ]]; then exit 1 fi - -# set LDFLAGS with proper flags to link against gmp/mpir and cddlib -LDFLAGS="-L$SAGE_LOCAL/lib -lcddgmp -lgmp -lm $LDFLAGS" - if [[ -z $CFLAG64 ]]; then CFLAG64=-m64 fi From 4ded8e24cfa768a309d0c122dda0b2eb394a8eaa Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Sat, 15 Apr 2017 06:36:43 +0200 Subject: [PATCH 089/151] #22814 : update cmake to 3.8.0 --- build/pkgs/cmake/checksums.ini | 6 ++-- build/pkgs/cmake/package-version.txt | 2 +- build/pkgs/cmake/patches/osx10.10.patch | 44 ------------------------- 3 files changed, 4 insertions(+), 48 deletions(-) delete mode 100644 build/pkgs/cmake/patches/osx10.10.patch diff --git a/build/pkgs/cmake/checksums.ini b/build/pkgs/cmake/checksums.ini index da9dbd8cd7e..82cd3470925 100644 --- a/build/pkgs/cmake/checksums.ini +++ b/build/pkgs/cmake/checksums.ini @@ -1,4 +1,4 @@ tarball=cmake-VERSION.tar.gz -sha1=fa176cc5b1ccf2e98196b50908432d0268323501 -md5=d51c92bf66b1e9d4fe2b7aaedd51377c -cksum=3408528151 +sha1=660ec06a46b46dc5d675371a2256ec739f8bb8b7 +md5=f28cba717ba38ad82a488daed8f45b5b +cksum=233346693 diff --git a/build/pkgs/cmake/package-version.txt b/build/pkgs/cmake/package-version.txt index b347b11eac8..19811903a7f 100644 --- a/build/pkgs/cmake/package-version.txt +++ b/build/pkgs/cmake/package-version.txt @@ -1 +1 @@ -3.2.3 +3.8.0 diff --git a/build/pkgs/cmake/patches/osx10.10.patch b/build/pkgs/cmake/patches/osx10.10.patch deleted file mode 100644 index 78045dcbb63..00000000000 --- a/build/pkgs/cmake/patches/osx10.10.patch +++ /dev/null @@ -1,44 +0,0 @@ -Patch CMake to avoid using faulty system header in OSX 10.10 - -CMake project files include CoreFoundation/CoreFoundation.h which is a collection -of headers one of which is CoreFoundation/CFStream.h. It includes faulty header -dispatch/dispatch.h. Since CoreFoundation/CFStream.h is not needed for CMake, -specific headers needed are included to replace CoreFoundation/CoreFoundation.h - -diff -dru src/Source/cmFindProgramCommand.cxx new/Source/cmFindProgramCommand.cxx ---- src/Source/cmFindProgramCommand.cxx 2015-03-10 20:08:44.000000000 +0530 -+++ new/Source/cmFindProgramCommand.cxx 2015-06-17 13:26:32.543667394 +0530 -@@ -14,7 +14,7 @@ - #include - - #if defined(__APPLE__) --#include -+#include - #endif - - // cmFindProgramCommand -diff -dru src/Source/cmXCodeObject.cxx new/Source/cmXCodeObject.cxx ---- src/Source/cmXCodeObject.cxx 2015-03-10 20:08:44.000000000 +0530 -+++ new/Source/cmXCodeObject.cxx 2015-06-17 13:26:37.191667311 +0530 -@@ -12,7 +12,7 @@ - #include "cmXCodeObject.h" - #include "cmSystemTools.h" - --#include // CFUUIDCreate -+#include // CFUUIDCreate - - //---------------------------------------------------------------------------- - const char* cmXCodeObject::PBXTypeNames[] = { -diff -dru src/Source/CPack/OSXScriptLauncher.cxx new/Source/CPack/OSXScriptLauncher.cxx ---- src/Source/CPack/OSXScriptLauncher.cxx 2015-03-10 20:08:44.000000000 +0530 -+++ new/Source/CPack/OSXScriptLauncher.cxx 2015-06-17 13:26:21.575667590 +0530 -@@ -14,7 +14,7 @@ - #include - #include - --#include -+#include - - // For the PATH_MAX constant - #include - From be9e797bd28bc06441313b8e16da0dd2640473f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 15 Apr 2017 23:25:51 +0200 Subject: [PATCH 090/151] some more care for range.. --- .../lie_algebras/structure_coefficients.py | 2 +- src/sage/algebras/schur_algebra.py | 7 ++++--- src/sage/algebras/steenrod/steenrod_algebra.py | 9 +++++---- src/sage/games/sudoku.py | 4 +++- .../perm_gps/partn_ref/refinement_graphs.pyx | 14 +++++++------- src/sage/homology/delta_complex.py | 5 ++++- src/sage/matrix/matrix0.pyx | 2 +- src/sage/matrix/matrix_complex_ball_dense.pyx | 2 +- src/sage/modules/filtered_vector_space.py | 3 ++- src/sage/rings/polynomial/symmetric_ideal.py | 11 ++++++----- src/sage/schemes/projective/projective_space.py | 3 ++- 11 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/sage/algebras/lie_algebras/structure_coefficients.py b/src/sage/algebras/lie_algebras/structure_coefficients.py index b78405e2739..394e0070b2a 100644 --- a/src/sage/algebras/lie_algebras/structure_coefficients.py +++ b/src/sage/algebras/lie_algebras/structure_coefficients.py @@ -112,7 +112,7 @@ def __classcall_private__(cls, R, s_coeff, names=None, index_set=None, **kwds): Check that we convert names to the indexing set:: - sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}}, index_set=range(3)) + sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}}, index_set=list(range(3))) sage: (x,y,z) = L.gens() sage: L[x,y] L[2] diff --git a/src/sage/algebras/schur_algebra.py b/src/sage/algebras/schur_algebra.py index e7ab20aafd7..5c937b1a09f 100644 --- a/src/sage/algebras/schur_algebra.py +++ b/src/sage/algebras/schur_algebra.py @@ -28,6 +28,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from six.moves import range import itertools @@ -428,7 +429,7 @@ def __init__(self, R, n, r): sage: T = SchurTensorModule(QQ, 2, 3) sage: TestSuite(T).run() """ - C = CombinatorialFreeModule(R, range(1, n + 1)) + C = CombinatorialFreeModule(R, list(range(1, n + 1))) self._n = n self._r = r self._sga = SymmetricGroupAlgebra(R, r) @@ -465,7 +466,7 @@ def _monomial_product(self, xi, v): B[1] # B[1] # B[2] + B[1] # B[2] # B[1] + B[2] # B[1] # B[1] """ ret = [] - for i in itertools.product(range(1, self._n + 1), repeat=self._r): + for i in itertools.product(list(range(1, self._n + 1)), repeat=self._r): if schur_representative_from_index(i, v) == xi: ret.append(tuple(i)) return self.sum_of_monomials(ret) @@ -580,7 +581,7 @@ def GL_irreducible_character(n, mu, KK): #make ST the superstandard tableau of shape mu from sage.combinat.tableau import from_shape_and_word - ST = from_shape_and_word(mu, range(1, r + 1), convention='English') + ST = from_shape_and_word(mu, list(range(1, r + 1)), convention='English') #make ell the reading word of the highest weight tableau of shape mu ell = [i + 1 for i, l in enumerate(mu) for dummy in range(l)] diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index 3bc25f7d6b6..3f8b3eaeaf7 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -451,8 +451,8 @@ # Copyright (C) 2008-2010 John H. Palmieri # Distributed under the terms of the GNU General Public License (GPL) #***************************************************************************** -from __future__ import print_function -from __future__ import absolute_import +from __future__ import print_function, absolute_import +from six.moves import range from sage.combinat.free_module import CombinatorialFreeModule, \ CombinatorialFreeModuleElement @@ -4152,8 +4152,9 @@ def AA(n=None, p=2): if n is None: return SteenrodAlgebra(p=p) if p == 2: - return SteenrodAlgebra(p=p, profile=range(n+1, 0, -1)) - return SteenrodAlgebra(p=p, profile=(range(n, 0, -1), [2]*(n+1))) + return SteenrodAlgebra(p=p, profile=list(range(n+1, 0, -1))) + return SteenrodAlgebra(p=p, profile=(list(range(n, 0, -1)), [2]*(n+1))) + def Sq(*nums): r""" diff --git a/src/sage/games/sudoku.py b/src/sage/games/sudoku.py index 0911d214e93..68fdd361786 100644 --- a/src/sage/games/sudoku.py +++ b/src/sage/games/sudoku.py @@ -22,10 +22,12 @@ ###################################################################### from __future__ import print_function from __future__ import absolute_import +from six.moves import range import six from sage.structure.sage_object import SageObject + def sudoku(m): r""" Solves Sudoku puzzles described by matrices. @@ -421,7 +423,7 @@ def to_ascii(self): n = self.n nsquare = n*n m = self.to_matrix() - m.subdivide(range(0,nsquare+1,n), range(0,nsquare+1,n)) + m.subdivide(list(range(0,nsquare+1,n)), list(range(0,nsquare+1,n))) naked_zero = compile('([\|, ]+)0') blanked = naked_zero.sub(lambda x: x.group(1)+' ', m.str()) brackets = compile('[\[,\]]') diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx index b55eb47bd69..385c07a17b7 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx @@ -81,10 +81,10 @@ def isomorphic(G1, G2, partn, ordering2, dig, use_indicator_function, sparse=Fal for G_in in [G1, G2]: if which_G == 1: GS = GS1 - first=True + first = True else: GS = GS2 - first=False + first = False if isinstance(G_in, GenericGraph): if G_in.has_loops(): loops = 1 @@ -92,7 +92,7 @@ def isomorphic(G1, G2, partn, ordering2, dig, use_indicator_function, sparse=Fal n = G_in.num_verts() elif n != G_in.num_verts(): return False - if G_in.vertices() != range(n): + if G_in.vertices() != list(xrange(n)): G_in = copy(G_in) to = G_in.relabel(return_map=True) frm = {} @@ -383,7 +383,7 @@ def search_tree(G_in, partition, lab=True, dig=False, dict_rep=False, certificat if isinstance(G_in, GenericGraph): loops = G_in.has_loops() n = G_in.num_verts() - if G_in.vertices() != range(n): + if G_in.vertices() != list(xrange(n)): G_in = copy(G_in) to = G_in.relabel(return_map=True) frm = {} @@ -840,7 +840,7 @@ def random_tests(num=10, n_max=60, perms_per_graph=5): print(H.graph6_string()) print(perm) return - isom = isomorphic(G, H, [range(n)], range(n), 0, 1) + isom = isomorphic(G, H, [list(xrange(n))], list(xrange(n)), 0, 1) if not isom or G.relabel(isom, inplace=False) != H: print("isom FAILURE: graph6-") print(H.graph6_string()) @@ -865,7 +865,7 @@ def random_tests(num=10, n_max=60, perms_per_graph=5): print(E.dig6_string()) print(perm) return - isom = isomorphic(D, E, [range(n)], range(n), 1, 1) + isom = isomorphic(D, E, [list(xrange(n))], list(xrange(n)), 1, 1) if not isom or D.relabel(isom, inplace=False) != E: print("isom FAILURE: dig6-") print(E.dig6_string()) @@ -917,7 +917,7 @@ def orbit_partition(gamma, list_perm=False): seen[i] = 1 else: for j in range(n): - if seen[j]==0: + if seen[j] == 0: i = j break partition.append([i]) diff --git a/src/sage/homology/delta_complex.py b/src/sage/homology/delta_complex.py index d77974a3e7a..576ecf6c34f 100644 --- a/src/sage/homology/delta_complex.py +++ b/src/sage/homology/delta_complex.py @@ -49,6 +49,7 @@ page instead. """ from __future__ import absolute_import +from six.moves import range from copy import copy from sage.homology.cell_complex import GenericCellComplex @@ -1006,7 +1007,9 @@ def product(self, other): # Simplex, as well as the function # 'lattice_paths', in # simplicial_complex.py.) - for path in lattice_paths(range(k+1), range(n+1), length=d+1): + for path in lattice_paths(list(range(k + 1)), + list(range(n + 1)), + length=d+1): path = tuple(path) new[(k, k_idx, n, n_idx, path)] = len(simplices) bdry_list = [] diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index c5959f08be8..50b5d00374b 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -5177,7 +5177,7 @@ cdef class Matrix(sage.structure.element.Matrix): if not B[self._nrows-1, self._ncols-1]: raise ZeroDivisionError("input matrix must be nonsingular") - return B.matrix_from_columns(range(self._ncols, 2*self._ncols)) + return B.matrix_from_columns(list(xrange(self._ncols, 2 * self._ncols))) def __pos__(self): """ diff --git a/src/sage/matrix/matrix_complex_ball_dense.pyx b/src/sage/matrix/matrix_complex_ball_dense.pyx index 4289eb84859..3d36dc7fb87 100644 --- a/src/sage/matrix/matrix_complex_ball_dense.pyx +++ b/src/sage/matrix/matrix_complex_ball_dense.pyx @@ -229,7 +229,7 @@ cdef class Matrix_complex_ball_dense(matrix_dense.Matrix_dense): list, so the following also works:: sage: v = reversed(range(4)); type(v) - <... 'listreverseiterator'> + <...iterator'> sage: A(v) [3.000000000000000 2.000000000000000] [1.000000000000000 0] diff --git a/src/sage/modules/filtered_vector_space.py b/src/sage/modules/filtered_vector_space.py index 763760a447c..de8facc3723 100644 --- a/src/sage/modules/filtered_vector_space.py +++ b/src/sage/modules/filtered_vector_space.py @@ -108,6 +108,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from six import iteritems +from six.moves import range from sage.rings.all import QQ, ZZ, RDF, RR, Integer from sage.rings.infinity import InfinityRing, infinity, minus_infinity @@ -851,7 +852,7 @@ def _repr_degrees(self, min_deg, max_deg): sage: F._repr_degrees(-2, 4) ['QQ^2', 'QQ^2', 'QQ^2', 'QQ^1', 'QQ^1', '0', '0', '0'] """ - degrees = range(min_deg, max_deg+1) + degrees = list(range(min_deg, max_deg + 1)) dims = [] for i in degrees + [infinity]: d = self.get_degree(i).dimension() diff --git a/src/sage/rings/polynomial/symmetric_ideal.py b/src/sage/rings/polynomial/symmetric_ideal.py index 4bbf86aa0ae..62583a822c3 100644 --- a/src/sage/rings/polynomial/symmetric_ideal.py +++ b/src/sage/rings/polynomial/symmetric_ideal.py @@ -55,6 +55,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function +from six.moves import range from sage.rings.ideal import Ideal_generic from sage.rings.integer import Integer @@ -275,11 +276,11 @@ def __mul__ (self, other): if hasattr(other,'gens'): other = SymmetricIdeal(PARENT, other.gens(), coerce=True) other = other.symmetrisation() - sN = max([X.max_index() for X in self.gens()]+[1]) - oN = max([X.max_index() for X in other.gens()]+[1]) + sN = max([X.max_index() for X in self.gens()] + [1]) + oN = max([X.max_index() for X in other.gens()] + [1]) from sage.combinat.permutation import Permutation - P = Permutation(range(2,sN+oN+1)+[1]) + P = Permutation(list(range(2, sN + oN + 1)) + [1]) oGen = list(other.gens()) SymL = oGen for i in range(sN): @@ -309,9 +310,9 @@ def __pow__(self, n): Symmetric Ideal (x_1^2, x_2*x_1) of Infinite polynomial ring in x over Rational Field """ - OUT = SymmetricIdeal(self.ring(),[1]) + OUT = SymmetricIdeal(self.ring(), [1]) for i in range(n): - OUT = self*OUT + OUT = self * OUT return OUT def is_maximal(self): diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index 3a3f1a3da6b..f3af7b786e3 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -79,6 +79,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function +from six.moves import range from sage.arith.misc import binomial @@ -1277,7 +1278,7 @@ def subscheme_from_Chow_form(self, Ch, dim): vars = list(R.gens()) #create the brackets associated to variables L1 = [] - for t in UnorderedTuples(range(n+1), dim+1): + for t in UnorderedTuples(list(range(n + 1)), dim+1): if all([t[i] Date: Sun, 16 Apr 2017 07:50:15 +0300 Subject: [PATCH 091/151] Typo correction. --- src/sage/combinat/posets/lattices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index de1c920d1cb..9e34b163e3a 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -59,7 +59,7 @@ :meth:`~FiniteLatticePoset.is_supersolvable` | Return ``True`` if the lattice is supersolvable. :meth:`~FiniteLatticePoset.is_planar` | Return ``True`` if the lattice has an upward planar drawing. :meth:`~FiniteLatticePoset.is_dismantlable` | Return ``True`` if the lattice is dismantlable. - :meth:`~FiniteLatticePoset.is_stone` | Return ``True`` if the lattice is Stone algebra. + :meth:`~FiniteLatticePoset.is_stone` | Return ``True`` if the lattice is a Stone lattice. :meth:`~FiniteLatticePoset.is_vertically_decomposable` | Return ``True`` if the lattice is vertically decomposable. :meth:`~FiniteLatticePoset.is_simple` | Return ``True`` if the lattice has no nontrivial congruences. :meth:`~FiniteLatticePoset.is_isoform` | Return ``True`` if all congruences of the lattice consists of isoform blocks. From 2a6571b484b104fc1ed96d02579a3ce70de58e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Sun, 16 Apr 2017 08:05:08 +0300 Subject: [PATCH 092/151] More typo corrections. --- src/sage/combinat/posets/lattices.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 9e34b163e3a..7ad94b72824 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -1016,13 +1016,13 @@ def is_meet_distributive(self, certificate=False): def is_stone(self, certificate=False): r""" - Return ``True`` if the lattice is Stone lattice, and ``False`` + Return ``True`` if the lattice is a Stone lattice, and ``False`` otherwise. The lattice is expected to be distributive (and hence pseudocomplemented). - A pseudocomplemented lattice is Stone lattice if + A pseudocomplemented lattice is a Stone lattice if .. MATH:: @@ -1062,7 +1062,7 @@ def is_stone(self, certificate=False): True sage: L = LatticePoset(DiGraph('GW?_W@?W@?O?')) - sage: L.is_stone() # Pass the fast check, but not Stone lattice + sage: L.is_stone() # Pass the fast check, but not a Stone lattice False """ from sage.arith.misc import factor @@ -1079,7 +1079,7 @@ def is_stone(self, certificate=False): return ok # Quick check: - # Stone lattice is direct product of distributive lattices with + # A Stone lattice is direct product of distributive lattices with # one atom. Return False if for example the lattice has two atoms # and odd number of elements. atoms_n = self._hasse_diagram.out_degree(0) From 3608619fe354c4ec18beb4ca59da0ff6e97d2853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Sun, 16 Apr 2017 08:21:01 +0300 Subject: [PATCH 093/151] Reformat if-clause. --- src/sage/combinat/posets/lattices.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 7ad94b72824..2626233e648 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -1086,11 +1086,9 @@ def is_stone(self, certificate=False): if atoms_n == 1: return ok if not certificate: - if (sum([x[1] for x in factor(self.cardinality())]) < - atoms_n): + if sum([x[1] for x in factor(self.cardinality())]) < atoms_n: return False - if (self._hasse_diagram.in_degree(self.cardinality()-1) < - atoms_n): + if self._hasse_diagram.in_degree(self.cardinality()-1) < atoms_n: return False # Quick check failed From a904582db50569d31224fa18c1f61ed66f01b60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Sun, 16 Apr 2017 08:22:02 +0300 Subject: [PATCH 094/151] Move import. --- src/sage/combinat/posets/lattices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 2626233e648..373c3bb238a 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -1065,13 +1065,13 @@ def is_stone(self, certificate=False): sage: L.is_stone() # Pass the fast check, but not a Stone lattice False """ - from sage.arith.misc import factor # TODO: For now we can factor only undirected graphs. When that # is extended to directed, use that; see comment below. if not self.is_distributive(): raise ValueError("the lattice is not distributive") + from sage.arith.misc import factor ok = (True, None) if certificate else True # Needed for the empty lattice that has no bottom element. From 9646aeec27030d370b71b517b65b4fb90f006049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 16 Apr 2017 11:33:36 +0200 Subject: [PATCH 095/151] py3 cleanup of hyperbolic models --- .../hyperbolic_space/hyperbolic_coercion.py | 4 +- .../hyperbolic_space/hyperbolic_geodesic.py | 27 +++++++--- .../hyperbolic_space/hyperbolic_interface.py | 2 +- .../hyperbolic_space/hyperbolic_isometry.py | 8 +-- .../hyperbolic_space/hyperbolic_model.py | 36 ++++++++++++- .../hyperbolic_space/hyperbolic_point.py | 54 +++++++++---------- .../hecke_triangle_group_element.py | 5 +- 7 files changed, 88 insertions(+), 48 deletions(-) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py b/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py index a8b9c225c7b..babe8801e89 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py @@ -139,8 +139,8 @@ def __invert__(self): sage: phi = UHP.coerce_map_from(PD) sage: ~phi Coercion Isometry morphism: - From: Hyperbolic plane in the Upper Half Plane Model model - To: Hyperbolic plane in the Poincare Disk Model model + From: Hyperbolic plane in the Upper Half Plane Model + To: Hyperbolic plane in the Poincare Disk Model """ return self.domain().coerce_map_from(self.codomain()) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index ac2ed22991f..9f3c2b550db 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -236,15 +236,28 @@ def __eq__(self, other): False sage: g1 == g1 True - """ - if not isinstance(other, HyperbolicGeodesic): return False return (self._model is other._model and self._start == other._start and self._end == other._end) + def __ne__(self, other): + """ + Test unequality of self and other. + + EXAMPLES:: + + sage: g1 = HyperbolicPlane().UHP().get_geodesic(I, 2*I) + sage: g2 = HyperbolicPlane().UHP().get_geodesic(2*I, I) + sage: g1 != g2 + True + sage: g1 != g1 + False + """ + return not (self == other) + ####################### # Setters and Getters # ####################### @@ -298,22 +311,20 @@ def model(self): sage: UHP = HyperbolicPlane().UHP() sage: UHP.get_geodesic(I, 2*I).model() - Hyperbolic plane in the Upper Half Plane Model model + Hyperbolic plane in the Upper Half Plane Model sage: PD = HyperbolicPlane().PD() sage: PD.get_geodesic(0, I/2).model() - Hyperbolic plane in the Poincare Disk Model model + Hyperbolic plane in the Poincare Disk Model sage: KM = HyperbolicPlane().KM() sage: KM.get_geodesic((0, 0), (0, 1/2)).model() - Hyperbolic plane in the Klein Disk Model model + Hyperbolic plane in the Klein Disk Model sage: HM = HyperbolicPlane().HM() sage: HM.get_geodesic((0, 0, 1), (0, 1, sqrt(2))).model() - Hyperbolic plane in the Hyperboloid Model model - + Hyperbolic plane in the Hyperboloid Model """ - return self._model def to_model(self, model): diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py b/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py index 947090cfd58..5e7d78c7010 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py @@ -120,7 +120,7 @@ def a_realization(self): sage: H = HyperbolicPlane() sage: H.a_realization() - Hyperbolic plane in the Upper Half Plane Model model + Hyperbolic plane in the Upper Half Plane Model """ return self.UHP() diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py index 18c02bc0fac..72e57cef1a1 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py @@ -358,16 +358,16 @@ def model(self): EXAMPLES:: sage: HyperbolicPlane().UHP().get_isometry(identity_matrix(2)).model() - Hyperbolic plane in the Upper Half Plane Model model + Hyperbolic plane in the Upper Half Plane Model sage: HyperbolicPlane().PD().get_isometry(identity_matrix(2)).model() - Hyperbolic plane in the Poincare Disk Model model + Hyperbolic plane in the Poincare Disk Model sage: HyperbolicPlane().KM().get_isometry(identity_matrix(3)).model() - Hyperbolic plane in the Klein Disk Model model + Hyperbolic plane in the Klein Disk Model sage: HyperbolicPlane().HM().get_isometry(identity_matrix(3)).model() - Hyperbolic plane in the Hyperboloid Model model + Hyperbolic plane in the Hyperboloid Model """ return self.domain() diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_model.py b/src/sage/geometry/hyperbolic_space/hyperbolic_model.py index 4feb66efa5f..2704f74a5f4 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_model.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_model.py @@ -143,6 +143,38 @@ def __init__(self, space, name, short_name, bounded, conformal, from sage.geometry.hyperbolic_space.hyperbolic_interface import HyperbolicModels Parent.__init__(self, category=HyperbolicModels(space)) + def __eq__(self, other): + """ + Test equality of ``self`` and ``other``. + + EXAMPLES:: + + sage: PD = HyperbolicPlane().PD() + sage: KM = HyperbolicPlane().KM() + sage: KM == KM + True + sage: PD == KM + False + """ + if not isinstance(other, HyperbolicModel): + return False + return self._name == other._name + + def __ne__(self, other): + """ + Test unequality of ``self`` and ``other``. + + EXAMPLES:: + + sage: PD = HyperbolicPlane().PD() + sage: KM = HyperbolicPlane().KM() + sage: KM != KM + False + sage: PD != KM + True + """ + return not (self == other) + def _repr_(self): # Abstract """ Return a string representation of ``self``. @@ -150,9 +182,9 @@ def _repr_(self): # Abstract EXAMPLES:: sage: HyperbolicPlane().UHP() - Hyperbolic plane in the Upper Half Plane Model model + Hyperbolic plane in the Upper Half Plane Model """ - return u'Hyperbolic plane in the {} model'.format(self._name) + return u'Hyperbolic plane in the {}'.format(self._name) def _element_constructor_(self, x, is_boundary=None, **graphics_options): #Abstract """ diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_point.py b/src/sage/geometry/hyperbolic_space/hyperbolic_point.py index 451781353c8..c18ecc4a327 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_point.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_point.py @@ -61,6 +61,7 @@ #*********************************************************************** from sage.structure.element import Element +from sage.structure.sage_object import richcmp, op_NE from sage.symbolic.all import I from sage.misc.latex import latex from sage.matrix.matrix import is_Matrix @@ -88,34 +89,36 @@ class HyperbolicPoint(Element): EXAMPLES: - Note that the coordinate representation does not differentiate the - different models:: + Comparison between different models is performed via coercion:: - sage: p = HyperbolicPlane().UHP().get_point(.2 + .3*I); p + sage: UHP = HyperbolicPlane().UHP() + sage: p = UHP.get_point(.2 + .3*I); p Point in UHP 0.200000000000000 + 0.300000000000000*I - sage: q = HyperbolicPlane().PD().get_point(0.2 + 0.3*I); q + sage: PD = HyperbolicPlane().PD() + sage: q = PD.get_point(0.2 + 0.3*I); q Point in PD 0.200000000000000 + 0.300000000000000*I sage: p == q False + sage: PD(p) + Point in PD 0.231213872832370 - 0.502890173410405*I sage: bool(p.coordinates() == q.coordinates()) True Similarly for boundary points:: - sage: p = HyperbolicPlane().UHP().get_point(1); p - Boundary point in UHP 1 + sage: p = UHP.get_point(-1); p + Boundary point in UHP -1 - sage: q = HyperbolicPlane().PD().get_point(1); q - Boundary point in PD 1 + sage: q = PD.get_point(-1); q + Boundary point in PD -1 sage: p == q - False - - sage: bool(p.coordinates() == q.coordinates()) True + sage: PD(p) + Boundary point in PD -1 It is an error to specify a point that does not lie in the appropriate model:: @@ -167,7 +170,7 @@ class HyperbolicPoint(Element): Boundary point in KM (1, 0) In the HM model, the coordinates of a point are on the - hyperboloidgiven by `x^2 + y^2 - z^2 = -1`:: + hyperboloid given by `x^2 + y^2 - z^2 = -1`:: sage: HyperbolicPlane().HM().get_point((0,0,1)) Point in HM (0, 0, 1) @@ -260,13 +263,12 @@ def _latex_(self): """ return latex(self._coordinates) - def __eq__(self, other): + def _richcmp_(self, other, op): r""" - Return ``True`` if ``self`` is equal to ``other``. + Comparison of self and other. EXAMPLES:: - sage: from sage.geometry.hyperbolic_space.hyperbolic_point import * sage: p1 = HyperbolicPlane().UHP().get_point(1 + I) sage: p2 = HyperbolicPlane().UHP().get_point(2 + I) sage: p1 == p2 @@ -274,8 +276,6 @@ def __eq__(self, other): sage: p1 == p1 True - :: - sage: p1 = HyperbolicPlane().PD().get_point(0) sage: p2 = HyperbolicPlane().PD().get_point(1/2 + 2*I/3) sage: p1 == p2 @@ -283,23 +283,21 @@ def __eq__(self, other): sage: p1 == p1 True - :: - sage: p1 = HyperbolicPlane().KM().get_point((0,0)) sage: p2 = HyperbolicPlane().KM().get_point((0, 1/2)) sage: p1 == p2 False - :: - sage: p1 = HyperbolicPlane().HM().get_point((0,0,1)) sage: p2 = HyperbolicPlane().HM().get_point((0,0,1/1)) sage: p1 == p2 True """ - return (isinstance(other, HyperbolicPoint) - and self.parent() is other.parent() - and bool(self._coordinates == other._coordinates)) + if not(isinstance(other, HyperbolicPoint) + or self.parent() is other.parent()): + return op == op_NE + # bool is required to convert symbolic (in)equalities + return bool(richcmp(self._coordinates, other._coordinates, op)) def __rmul__(self, other): r""" @@ -361,16 +359,16 @@ def model(self): EXAMPLES:: sage: HyperbolicPlane().UHP().get_point(I).model() - Hyperbolic plane in the Upper Half Plane Model model + Hyperbolic plane in the Upper Half Plane Model sage: HyperbolicPlane().PD().get_point(0).model() - Hyperbolic plane in the Poincare Disk Model model + Hyperbolic plane in the Poincare Disk Model sage: HyperbolicPlane().KM().get_point((0,0)).model() - Hyperbolic plane in the Klein Disk Model model + Hyperbolic plane in the Klein Disk Model sage: HyperbolicPlane().HM().get_point((0,0,1)).model() - Hyperbolic plane in the Hyperboloid Model model + Hyperbolic plane in the Hyperboloid Model """ return self.parent() diff --git a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py index 77a3056c9f6..fc1019cb3b9 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py @@ -3308,9 +3308,8 @@ def as_hyperbolic_plane_isometry(self, model="UHP"): [lam^2 - 1 lam] [lam^2 - 1 lam^2 - 1] sage: el.as_hyperbolic_plane_isometry().parent() - Set of Morphisms from Hyperbolic plane in the Upper Half Plane Model model to Hyperbolic plane in the Upper Half Plane Model model in Category of hyperbolic models of Hyperbolic plane + Set of Morphisms from Hyperbolic plane in the Upper Half Plane Model to Hyperbolic plane in the Upper Half Plane Model in Category of hyperbolic models of Hyperbolic plane sage: el.as_hyperbolic_plane_isometry("KM").parent() - Set of Morphisms from Hyperbolic plane in the Klein Disk Model model to Hyperbolic plane in the Klein Disk Model model in Category of hyperbolic models of Hyperbolic plane + Set of Morphisms from Hyperbolic plane in the Klein Disk Model to Hyperbolic plane in the Klein Disk Model in Category of hyperbolic models of Hyperbolic plane """ - return HyperbolicPlane().UHP().get_isometry(self._matrix).to_model(model) From c0e1c44c2514304c6ecce553f63452e64381c101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 16 Apr 2017 13:25:29 +0200 Subject: [PATCH 096/151] richcmp for Hecke module elements --- src/sage/modular/hecke/element.py | 23 +++++++++++++++++++++++ src/sage/modular/modsym/element.py | 17 ----------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/sage/modular/hecke/element.py b/src/sage/modular/hecke/element.py index 790887f4895..4d7aa4317aa 100644 --- a/src/sage/modular/hecke/element.py +++ b/src/sage/modular/hecke/element.py @@ -23,6 +23,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.structure.sage_object import richcmp, op_NE from sage.structure.element import ModuleElement def is_HeckeModuleElement(x): @@ -140,6 +141,28 @@ def _vector_(self, R=None): if R is None: return self.__element return self.__element.change_ring(R) + def _richcmp_(self, other, op): + """ + Rich comparison of ``self`` and ``other``. + + EXAMPLES:: + + sage: M = ModularSymbols(11, 2) + sage: M.0 == M.1 # indirect doctest + False + sage: M.0 == (M.1 + M.0 - M.1) + True + sage: M.0 == ModularSymbols(13, 2).0 + False + + sage: x = BrandtModule(37)([0,1,-1]) + sage: x != x + False + """ + if not isinstance(other, HeckeModuleElement): + return op == op_NE + return richcmp(self.element(), other.element(), op) + def ambient_module(self): """ Return the ambient Hecke module that contains this element. diff --git a/src/sage/modular/modsym/element.py b/src/sage/modular/modsym/element.py index 8eac52b09ea..f2f1ea63a24 100644 --- a/src/sage/modular/modsym/element.py +++ b/src/sage/modular/modsym/element.py @@ -120,23 +120,6 @@ def __init__(self, parent, x, check=True): raise TypeError("x (of degree %s) must be of degree the same as the degree of the parent (of degree %s)."%(x.degree(), parent.degree())) hecke.HeckeModuleElement.__init__(self, parent, x) - def __cmp__(self, other): - r""" Standard comparison function. - - EXAMPLES:: - - sage: M = ModularSymbols(11, 2) - sage: M.0 == M.1 # indirect doctest - False - sage: M.0 == (M.1 + M.0 - M.1) - True - sage: M.0 == ModularSymbols(13, 2).0 - False - sage: M.0 == 4 - False - """ - return self.element().__cmp__(other.element()) - def _repr_(self): r""" String representation of self. The output will depend on the global From e7add6def823f041ea6f70391b88169aa28088e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= Date: Sun, 16 Apr 2017 07:59:44 -0400 Subject: [PATCH 097/151] 22765: improved type checking - the previous test was logically broken - the optional parent now has to be a Modules.WithBasis "quacking like a ModulesWithBasis by having a sum and terms method" was not defining a clean and stable API anyway: the method was already using other ModulesWithBasis methods (base_ring, /), and we now are using sum_of_terms instead. --- src/sage/categories/finite_permutation_groups.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/finite_permutation_groups.py b/src/sage/categories/finite_permutation_groups.py index 50dce5a5d3e..3d4727f578b 100644 --- a/src/sage/categories/finite_permutation_groups.py +++ b/src/sage/categories/finite_permutation_groups.py @@ -193,12 +193,12 @@ def cycle_index(self, parent = None): sage: P.parent() is F True - This parent should have a ``term`` and ``sum`` method:: + This parent should be a module with basis indexed by partitions:: sage: CyclicPermutationGroup(6).cycle_index(parent = QQ) Traceback (most recent call last): ... - ValueError: `parent` should be (or behave as) a free module with basis indexed by partitions + ValueError: `parent` should be a module with basis indexed by partitions REFERENCES: @@ -219,12 +219,13 @@ def cycle_index(self, parent = None): sage: P.cycle_index() p[1] """ + from sage.categories.modules import Modules if parent is None: from sage.rings.rational_field import QQ from sage.combinat.sf.sf import SymmetricFunctions parent = SymmetricFunctions(QQ).powersum() - elif not hasattr(parent, "term") and hasattr(parent, "sum"): - raise ValueError("`parent` should be (or behave as) a free module with basis indexed by partitions") + elif not parent in Modules.WithBasis: + raise ValueError("`parent` should be a module with basis indexed by partitions") base_ring = parent.base_ring() return parent.sum_of_terms([C.an_element().cycle_type(), base_ring(C.cardinality())] for C in self.conjugacy_classes() From c773c01bc5e9a78881bb5f0eeb1b742d8cf2f176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 16 Apr 2017 21:34:24 +0200 Subject: [PATCH 098/151] removed useless __eq__ and __ne__ --- .../hyperbolic_space/hyperbolic_model.py | 53 +++++-------------- 1 file changed, 14 insertions(+), 39 deletions(-) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_model.py b/src/sage/geometry/hyperbolic_space/hyperbolic_model.py index 2704f74a5f4..3654c66ba21 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_model.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_model.py @@ -109,6 +109,7 @@ ##################################################################### ## Abstract model + class HyperbolicModel(Parent, UniqueRepresentation, BindableClass): r""" Abstract base class for hyperbolic models. @@ -143,38 +144,6 @@ def __init__(self, space, name, short_name, bounded, conformal, from sage.geometry.hyperbolic_space.hyperbolic_interface import HyperbolicModels Parent.__init__(self, category=HyperbolicModels(space)) - def __eq__(self, other): - """ - Test equality of ``self`` and ``other``. - - EXAMPLES:: - - sage: PD = HyperbolicPlane().PD() - sage: KM = HyperbolicPlane().KM() - sage: KM == KM - True - sage: PD == KM - False - """ - if not isinstance(other, HyperbolicModel): - return False - return self._name == other._name - - def __ne__(self, other): - """ - Test unequality of ``self`` and ``other``. - - EXAMPLES:: - - sage: PD = HyperbolicPlane().PD() - sage: KM = HyperbolicPlane().KM() - sage: KM != KM - False - sage: PD != KM - True - """ - return not (self == other) - def _repr_(self): # Abstract """ Return a string representation of ``self``. @@ -688,7 +657,8 @@ def dist(self, a, b): sage: UHP.dist(I, 2*I) arccosh(5/4) """ - coords = lambda x: self(x).coordinates() + def coords(x): + return self(x).coordinates() if isinstance(a, HyperbolicGeodesic): if isinstance(b, HyperbolicGeodesic): @@ -759,7 +729,9 @@ def _dist_geod_point(self, start, end, p): """ R = self.realization_of().a_realization() assert R is not self - phi = lambda c: R.coerce_map_from(self).image_coordinates(c) + + def phi(c): + return R.coerce_map_from(self).image_coordinates(c) return R._dist_geod_point(phi(start), phi(end), phi(p)) #################### @@ -1169,6 +1141,7 @@ def _moebius_sending(z, w): #UHP ##################################################################### ## Poincaré disk model + class HyperbolicModelPD(HyperbolicModel): r""" Poincaré Disk Model. @@ -1269,8 +1242,8 @@ def isometry_in_model(self, A): # alpha = A[0][0] # beta = A[0][1] # Orientation preserving and reversing - return (HyperbolicIsometryPD._orientation_preserving(A) - or HyperbolicIsometryPD._orientation_preserving(I * A)) + return (HyperbolicIsometryPD._orientation_preserving(A) or + HyperbolicIsometryPD._orientation_preserving(I * A)) def get_background_graphic(self, **bdry_options): r""" @@ -1383,12 +1356,13 @@ def isometry_in_model(self, A): """ if isinstance(A, HyperbolicIsometry): return True - return bool((A*LORENTZ_GRAM*A.transpose() - LORENTZ_GRAM).norm()**2 - < EPSILON) + return bool((A*LORENTZ_GRAM*A.transpose() - LORENTZ_GRAM).norm()**2 < + EPSILON) def get_background_graphic(self, **bdry_options): r""" Return a graphic object that makes the model easier to visualize. + For the Klein model, the background object is the ideal boundary. EXAMPLES:: @@ -1396,11 +1370,12 @@ def get_background_graphic(self, **bdry_options): sage: circ = HyperbolicPlane().KM().get_background_graphic() """ from sage.plot.circle import circle - return circle((0,0), 1, axes=False, color='black') + return circle((0, 0), 1, axes=False, color='black') ##################################################################### ## Hyperboloid model + class HyperbolicModelHM(HyperbolicModel): r""" Hyperboloid Model. From 96cae4e257a59bba9a6f6fcb46a20cb715bec6ad Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Mon, 17 Apr 2017 06:26:46 +0200 Subject: [PATCH 099/151] #22814 : make the self tests pass + fix dependencies --- build/pkgs/cmake/SPKG.txt | 1 + build/pkgs/cmake/dependencies | 5 +++++ build/pkgs/cmake/spkg-check | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/cmake/dependencies diff --git a/build/pkgs/cmake/SPKG.txt b/build/pkgs/cmake/SPKG.txt index 765f543db9d..e917e612238 100644 --- a/build/pkgs/cmake/SPKG.txt +++ b/build/pkgs/cmake/SPKG.txt @@ -25,5 +25,6 @@ CMake is distributed under the OSI-approved BSD 3-clause License. == Dependencies == +* curl * zlib * bzip2 diff --git a/build/pkgs/cmake/dependencies b/build/pkgs/cmake/dependencies new file mode 100644 index 00000000000..0c60285196e --- /dev/null +++ b/build/pkgs/cmake/dependencies @@ -0,0 +1,5 @@ +curl zlib bzip2 + +---------- +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/cmake/spkg-check b/build/pkgs/cmake/spkg-check index ec72970e697..2ed1889112a 100755 --- a/build/pkgs/cmake/spkg-check +++ b/build/pkgs/cmake/spkg-check @@ -1,4 +1,6 @@ #!/usr/bin/env bash cd src -ctest -E CTestTestStopTime +unset MACOSX_DEPLOYMENT_TARGET +ctest --extra-verbose --output-on-failure -E "(CTestTestStopTime|TestUpload|ctest_submit)" + From 2a7197dbf419288dcb27bad78a8d3e41b623d8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 17 Apr 2017 13:58:55 +0200 Subject: [PATCH 100/151] trying to make conway package sage-independent --- build/pkgs/conway_polynomials/package-version.txt | 2 +- build/pkgs/conway_polynomials/spkg-install | 9 +++++---- src/sage/databases/conway.py | 8 +++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/build/pkgs/conway_polynomials/package-version.txt b/build/pkgs/conway_polynomials/package-version.txt index fba722f5f9c..304012ce339 100644 --- a/build/pkgs/conway_polynomials/package-version.txt +++ b/build/pkgs/conway_polynomials/package-version.txt @@ -1 +1 @@ -0.4.p0 +0.4.p1 diff --git a/build/pkgs/conway_polynomials/spkg-install b/build/pkgs/conway_polynomials/spkg-install index 441dc8e8381..9c2ae525c82 100755 --- a/build/pkgs/conway_polynomials/spkg-install +++ b/build/pkgs/conway_polynomials/spkg-install @@ -1,21 +1,22 @@ #!/usr/bin/env python - import os -from sage.structure.sage_object import save +from six.moves import cPickle as pickle from sage.env import SAGE_SHARE install_root = os.path.join(SAGE_SHARE, 'conway_polynomials') + def create_db(): db = {} from src import conway_polynomials - for p,n,v in conway_polynomials: + for p, n, v in conway_polynomials: if not p in db: db[p] = {} db[p][n] = v if not os.path.exists(install_root): os.makedirs(install_root) - save(db, os.path.join(install_root, 'conway_polynomials.sobj')) + with open('conway_polynomials.p', 'wb') as f: + pickle.dump(db, f) if __name__ == '__main__': create_db() diff --git a/src/sage/databases/conway.py b/src/sage/databases/conway.py index e608c063619..3e6f9c25e40 100644 --- a/src/sage/databases/conway.py +++ b/src/sage/databases/conway.py @@ -20,6 +20,7 @@ #***************************************************************************** from six import itervalues, iteritems +from six.moves import cPickle as pickle import collections import os @@ -92,6 +93,7 @@ def __repr__(self): """ return repr(self._store) + class ConwayPolynomials(collections.Mapping): def __init__(self): """ @@ -107,9 +109,9 @@ def __init__(self): if _conwaydict is None: if not os.path.exists(_CONWAYDATA): raise RuntimeError('In order to initialize the database, ' - + '%s must exist.'%_CONWAYDATA) - from sage.structure.sage_object import load - _conwaydict = load(_CONWAYDATA) + + '%s must exist.' % _CONWAYDATA) + with open(_CONWAYDATA, 'rb') as f: + _conwaydict = pickle.load(f) self._store = _conwaydict def __repr__(self): From 467e4e147c8b18e4cc671af87d6df2f33181a7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 17 Apr 2017 14:08:56 +0200 Subject: [PATCH 101/151] trac 22775 fixing details --- build/pkgs/conway_polynomials/spkg-install | 2 +- src/sage/databases/conway.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/conway_polynomials/spkg-install b/build/pkgs/conway_polynomials/spkg-install index 9c2ae525c82..15ba836d6a9 100755 --- a/build/pkgs/conway_polynomials/spkg-install +++ b/build/pkgs/conway_polynomials/spkg-install @@ -15,7 +15,7 @@ def create_db(): db[p][n] = v if not os.path.exists(install_root): os.makedirs(install_root) - with open('conway_polynomials.p', 'wb') as f: + with open(os.path.join(install_root, 'conway_polynomials.p'), 'wb') as f: pickle.dump(db, f) if __name__ == '__main__': diff --git a/src/sage/databases/conway.py b/src/sage/databases/conway.py index 3e6f9c25e40..adc2ff9c18f 100644 --- a/src/sage/databases/conway.py +++ b/src/sage/databases/conway.py @@ -27,7 +27,7 @@ from sage.env import CONWAY_POLYNOMIALS_DATA_DIR -_CONWAYDATA = os.path.join(CONWAY_POLYNOMIALS_DATA_DIR, 'conway_polynomials.sobj') +_CONWAYDATA = os.path.join(CONWAY_POLYNOMIALS_DATA_DIR, 'conway_polynomials.p') _conwaydict = None class DictInMapping(collections.Mapping): From f2bcabd8c5342e576ba93a61897915be1468ab6d Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 11 Apr 2017 15:53:26 +0200 Subject: [PATCH 102/151] Upgrade to cysignals version 1.6.3 --- build/pkgs/cysignals/checksums.ini | 6 +++--- build/pkgs/cysignals/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cysignals/checksums.ini b/build/pkgs/cysignals/checksums.ini index 0a80fd77096..fa79cacd901 100644 --- a/build/pkgs/cysignals/checksums.ini +++ b/build/pkgs/cysignals/checksums.ini @@ -1,4 +1,4 @@ tarball=cysignals-VERSION.tar.bz2 -sha1=4394b36d8b30a7afd81df45c6266048d49567af0 -md5=abc98cb7b07c3a40979ee24dead06a38 -cksum=1213194811 +sha1=34508e9ff71da18b66b1eec3464c1ff6a63608e9 +md5=f00b9a39a3e785ee822ca00de27da362 +cksum=722688943 diff --git a/build/pkgs/cysignals/package-version.txt b/build/pkgs/cysignals/package-version.txt index 1892b926767..266146b87cb 100644 --- a/build/pkgs/cysignals/package-version.txt +++ b/build/pkgs/cysignals/package-version.txt @@ -1 +1 @@ -1.3.2 +1.6.3 From 8c059574a67f401635ebc4a5708e8bf56ae2c3e5 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 13 Apr 2017 17:26:58 +0200 Subject: [PATCH 103/151] Fix doctest for cysignals upgrade --- src/sage/repl/ipython_kernel/kernel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/repl/ipython_kernel/kernel.py b/src/sage/repl/ipython_kernel/kernel.py index 95e145a76d9..d2e11b9a1da 100644 --- a/src/sage/repl/ipython_kernel/kernel.py +++ b/src/sage/repl/ipython_kernel/kernel.py @@ -180,7 +180,7 @@ def pre_handler_hook(self): sage: k = SageKernel.__new__(SageKernel) sage: k.pre_handler_hook() sage: k.saved_sigint_handler - + """ from cysignals import init_cysignals self.saved_sigint_handler = init_cysignals() From c241ef9e9447d755518b94581d7bbc7b5daeeae5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 17 Apr 2017 14:10:56 -0500 Subject: [PATCH 104/151] Removing Kleshchev crystal (now in #15508) and cmp. --- src/doc/en/reference/combinat/module_list.rst | 1 - src/doc/en/reference/references/index.rst | 4 + src/sage/combinat/crystals/catalog.py | 2 - .../crystals/highest_weight_crystals.py | 14 - src/sage/combinat/crystals/kleshchev.py | 356 ------------------ src/sage/combinat/crystals/multisegments.py | 30 +- 6 files changed, 14 insertions(+), 393 deletions(-) delete mode 100644 src/sage/combinat/crystals/kleshchev.py diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 56219e62145..8713d4f31ee 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -67,7 +67,6 @@ Comprehensive Module list sage/combinat/crystals/infinity_crystals sage/combinat/crystals/polyhedral_realization sage/combinat/crystals/kirillov_reshetikhin - sage/combinat/crystals/kleshchev sage/combinat/crystals/kyoto_path_model sage/combinat/crystals/letters sage/combinat/crystals/littelmann_path diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 672e7374041..f653998f336 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1129,6 +1129,10 @@ REFERENCES: Operads*. Springer-Verlag Berlin Heidelberg (2012). :doi:`10.1007/978-3-642-30362-3`. +.. [LTV1999] Bernard Leclerc, Jean-Yves Thibon, and Eric Vasserot. + *Zelevinsky's involution at roots of unity*. + J. Reine Angew. Math. 513:33-51 (1999). + .. [LW2012] David Loeffler and Jared Weinstein, *On the computation of local components of a newform*, Mathematics of Computation **81** (2012) 1179-1200. :doi:`10.1090/S0025-5718-2011-02530-5` diff --git a/src/sage/combinat/crystals/catalog.py b/src/sage/combinat/crystals/catalog.py index 7c6b5df7fd4..d6e9cab5e7d 100644 --- a/src/sage/combinat/crystals/catalog.py +++ b/src/sage/combinat/crystals/catalog.py @@ -22,7 +22,6 @@ * :func:`HighestWeight ` * :class:`Induced ` * :func:`KirillovReshetikhin ` -* :class:`KleshchevPartitions ` * :class:`KyotoPathModel ` * :class:`Letters ` * :class:`LSPaths ` @@ -69,7 +68,6 @@ from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinCrystal as KirillovReshetikhin from sage.combinat.rigged_configurations.rc_crystal import CrystalOfRiggedConfigurations as RiggedConfigurations -from sage.combinat.crystals.kleshchev import KleshchevCrystal as KleshchevPartitions from sage.combinat.crystals.induced_structure import InducedCrystal as Induced diff --git a/src/sage/combinat/crystals/highest_weight_crystals.py b/src/sage/combinat/crystals/highest_weight_crystals.py index 124929b1475..82982ae5283 100644 --- a/src/sage/combinat/crystals/highest_weight_crystals.py +++ b/src/sage/combinat/crystals/highest_weight_crystals.py @@ -30,7 +30,6 @@ from sage.combinat.crystals.generalized_young_walls import CrystalOfGeneralizedYoungWalls from sage.combinat.crystals.monomial_crystals import CrystalOfNakajimaMonomials from sage.combinat.rigged_configurations.rc_crystal import CrystalOfRiggedConfigurations -from sage.combinat.crystals.kleshchev import KleshchevCrystal def HighestWeightCrystal(dominant_weight, model=None): r""" @@ -64,8 +63,6 @@ def HighestWeightCrystal(dominant_weight, model=None): ` * ``'GeneralizedYoungWalls'`` - :class:`generalized Young walls ` - * ``'KleshchevPartitions'`` - :class:` Kleshchev multipartitions - ` * ``'RiggedConfigurations'`` - :class:`rigged configurations ` @@ -160,8 +157,6 @@ def HighestWeightCrystal(dominant_weight, model=None): sage: crystals.HighestWeight(wt, model='GeneralizedYoungWalls') Highest weight crystal of generalized Young walls of Cartan type ['A', 3, 1] and highest weight Lambda[0] + Lambda[2] - sage: crystals.HighestWeight(wt, model='KleshchevPartitions') - The crystal of multipartitions of type ['A', 4, 1] and residues (0, 2) """ cartan_type = dominant_weight.parent().cartan_type() if model is None: @@ -220,15 +215,6 @@ def HighestWeightCrystal(dominant_weight, model=None): wt = P.sum_of_terms((i, c) for i,c in dominant_weight) return CrystalOfGeneralizedYoungWalls(cartan_type.rank()-1, wt) - if model == 'KleshchevPartitions': - if not cartan_type.is_affine(): - raise ValueError("only for affine types") - if cartan_type.type() != 'A': - raise NotImplementedError("only for affine type A") - from sage.misc.flatten import flatten - wt = flatten([[i]*c for i,c in dominant_weight]) - return KleshchevCrystal(cartan_type.rank(), wt) - if model == 'RiggedConfigurations': # Make sure it's in the weight lattice P = dominant_weight.parent().root_system.weight_lattice() diff --git a/src/sage/combinat/crystals/kleshchev.py b/src/sage/combinat/crystals/kleshchev.py deleted file mode 100644 index 147ca8bfc7a..00000000000 --- a/src/sage/combinat/crystals/kleshchev.py +++ /dev/null @@ -1,356 +0,0 @@ -""" -Kleshchev Partition (Tuple) Crystals -""" -#***************************************************************************** -# Copyright (C) 2013 Travis Scrimshaw -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# -# http://www.gnu.org/licenses/ -#**************************************************************************** - -from sage.structure.parent import Parent -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.element_wrapper import ElementWrapper -from sage.categories.highest_weight_crystals import HighestWeightCrystals -from sage.categories.regular_crystals import RegularCrystals -from sage.rings.finite_rings.integer_mod_ring import IntegerModRing -from sage.rings.all import ZZ -from sage.combinat.partition_tuple import PartitionTuple -from sage.combinat.root_system.cartan_type import CartanType - -class KleshchevCrystal(Parent, UniqueRepresentation): - r""" - The Kleshchev multipartition (or partition tuple) crystal. - - We consider type `A_n^{(1)}` crystals, and let `r = (r_i \mid r_i \in - \ZZ / n \ZZ)` be a finite sequence of length `k` (often called the level) - and `\lambda = \sum_i \Lambda_{r_i}`. We will model the highest weight - `U_q(\mathfrak{g})`-crystal `B(\lambda)` by a particular subset of - partition tuples of level `k`. We define a regular crystal structure - as follows: - - We create a word in the letters `A_i` and `R_i` by taking the partitions - (components) from right to left and reading from top to bottom in each - partition (in English notation) where an addable `i`-cell is an `A_i` and - a removable `i`-cell is an `R_i`. We then cancel pairs `R_i A_i`, so the - reduced word is of the form `A_i \cdots A_i R_i \cdots R_i`. Now `e_i` - (resp. `f_i`) acts by removing (resp. adding) the cell corresponding to - the left-most `R_i` (resp. right-most `A_i`) in the reduced word. - - The Kleshchev crystal `B(\lambda)` is the crystal generated by the empty - partition tuple. We can compute the weight of an element `\mu` by taking - `\lambda - \sum_{i=0}^n c_i \alpha_i` where `c_i` is the number of cells - of `n`-residue `i` in `\mu`. Partition tuples in the crystal are known - as `r`-Kleshchev partition tuples, and if `r = (r_1)`, then the partitions - are known as *Kleshchev* partitions. - - .. NOTE:: - - We can describe `r`-Kleshchev partition tuples in `B(\lambda)` as - partition tuples `\mu` such that `\mu^{(t)}_{r_t - r_{t+1} + x} < - \mu^{(t+1)}_x` for all `x \geq 1` and `1 \leq t \leq k - 1`. - - INPUT: - - - ``n`` -- for type `A_n^{(1)}` - - ``r`` -- the sequence `r` - - EXAMPLES: - - We first do an example of a level 1 crystal:: - - sage: C = crystals.KleshchevPartitions(2, [0]) - sage: C - The crystal of multipartitions of type ['A', 2, 1] and residues (0,) - sage: mg = C.module_generators[0] - sage: mg - [] - sage: mg.f(0) - [1] - sage: mg.f(1) - sage: mg.f(2) - sage: mg.f_string([0,2,1,0]) - [1, 1, 1, 1] - sage: mg.f_string([0,1,2,0]) - [2, 2] - sage: S = C.subcrystal(max_depth=5) - sage: G = C.digraph(subset=S) - sage: B = crystals.LSPaths(['A',2,1], [1,0,0]) - sage: SB = B.subcrystal(max_depth=5) - sage: GB = B.digraph(subset=SB) - sage: G.is_isomorphic(GB, edge_labels=True) - True - - Now a higher level crystal:: - - sage: C = crystals.KleshchevPartitions(2, [0,2]) - sage: mg = C.module_generators[0] - sage: mg - ([], []) - sage: mg.f(0) - ([1], []) - sage: mg.f(2) - ([], [1]) - sage: mg.f_string([0,1,2,0]) - ([2, 2], []) - sage: mg.f_string([0,2,1,0]) - ([1, 1, 1, 1], []) - sage: mg.f_string([2,0,1,0]) - ([2], [2]) - sage: S = C.subcrystal(max_depth=3) - sage: G = C.digraph(subset=S) - sage: B = crystals.LSPaths(['A',2,1], [1,0,1]) - sage: SB = B.subcrystal(max_depth=3) - sage: GB = B.digraph(subset=SB) - sage: G.is_isomorphic(GB, edge_labels=True) - True - - The ordering of the residues gives a different representation of the - higher level crystals (but it is still isomorphic):: - - sage: C2 = crystals.KleshchevPartitions(2, [2,0]) - sage: mg2 = C2.highest_weight_vector() - sage: mg2.f_string([0,1,2,0]) - ([2], [2]) - sage: mg2.f_string([0,2,1,0]) - ([1, 1, 1], [1]) - sage: mg2.f_string([2,0,1,0]) - ([2, 1], [1]) - sage: S2 = C2.subcrystal(max_depth=5) - sage: G2 = C2.digraph(subset=S) - sage: G.is_isomorphic(G2, edge_labels=True) - True - - REFERENCES: - - .. [Ariki2001] Susumu Ariki. On the classification of simple modules for - cyclotomic Hecke algebras of type `G(m,1,n)` and Kleshchev - multipartitions. Osaka J. Math. **38** (2001). :arxiv:`9908004v2`. - - .. [Vazirani2002] Monica Vazirani. *Parameterizing Hecek algebra modules: - Bernstein-Zelevinsky multisegments, Kleshchev multipartitions, and - crystal graphs*. Transform. Groups **7** (2002). pp. 267-303. - :arxiv:`0107052v1`, :doi:`10.1007/s00031-002-0014-1`. - - .. [TingleyLN] Peter Tingley. Explicit `\widehat{\mathfrak{sl}}_n` crystal - maps between cylindric plane partitions, multi-partitions, and - multi-segments. Lecture notes. - http://webpages.math.luc.edu/~ptingley/lecturenotes/explicit_bijections.pdf - - .. [Tingley2007] Peter Tingley. Three combinatorial models for - `\widehat{\mathfrak{sl}}_n` crystals, with applications to cylindric - plane partitions. International Mathematics Research Notices. (2007). - :arxiv:`0702062v3`. - """ - @staticmethod - def __classcall_private__(cls, n, r): - """ - Normalize input to ensure a uniqure representation. - - EXAMPLES:: - - sage: C1 = crystals.KleshchevPartitions(2, [0,2]) - sage: C2 = crystals.KleshchevPartitions(2, (0,2)) - sage: C1 is C2 - True - """ - if r in ZZ: - r = [r] - M = IntegerModRing(n+1) - r = tuple(map(M, r)) - return super(KleshchevCrystal, cls).__classcall__(cls, n, r) - - def __init__(self, n, r): - """ - Initialize ``self``. - - EXAMPLES:: - - sage: C = crystals.KleshchevPartitions(2, [0,2]) - sage: TestSuite(C).run() # long time - """ - self._cartan_type = CartanType(['A', n, 1]) - self._r = r - Parent.__init__(self, category=(HighestWeightCrystals(), RegularCrystals())) - self.module_generators = (self.element_class(self, PartitionTuple([[]]*len(r))),) - - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - sage: crystals.KleshchevPartitions(2, [0,2]) - The crystal of multipartitions of type ['A', 2, 1] and residues (0, 2) - """ - return "The crystal of multipartitions of type {} and residues {}".format(self._cartan_type, self._r) - - class Element(ElementWrapper): - """ - An element in the multipartition crystal. - """ - def e(self, i): - r""" - Return the action of `e_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: C = crystals.KleshchevPartitions(2, [0,2]) - sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) - sage: x.e(2) - ([5, 4, 1], [3, 1, 1, 1]) - """ - rem_cells = [] - rs = self.parent()._r - rev_enum = lambda l: reversed(list(enumerate(l))) - - for j,p in rev_enum(self.value.components()): - r = rs[j] - for a,v in enumerate(p): - res = v - a + r - if res - 1 == i and (a >= len(p)-1 or p[a] > p[a+1]): # Removable - rem_cells.append((j,a,v-1)) - elif res == i and len(rem_cells) > 0 and (a == 0 or p[a-1] > p[a]) > 0: # Addable - rem_cells.pop() - if r - len(p) == i and len(rem_cells) > 0: # Last addable cell - rem_cells.pop() - - if len(rem_cells) == 0: - return None - c = rem_cells[0] - if len(rs) == 1: # Special case when it is a single partition - c = c[1:] - return self.__class__(self.parent(), self.value.remove_cell(*c)) - - def f(self, i): - r""" - Return the action of `f_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: C = crystals.KleshchevPartitions(2, [0,2]) - sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) - sage: x.e(2) - ([5, 4, 1], [3, 1, 1, 1]) - sage: x.f(2) - ([5, 4, 1], [4, 2, 1, 1]) - """ - add_cells = [] - rs = self.parent()._r - rev_enum = lambda l: reversed(list(enumerate(l))) - - for j,p in enumerate(self.value.components()): - r = rs[j] - if r - len(p) == i: # Last addable cell - add_cells.append((j,len(p),0)) - for a,v in rev_enum(p): - res = v - a + r - if res == i and (a == 0 or p[a-1] > p[a]): # Addable - add_cells.append((j,a,v)) - elif res - 1 == i and len(add_cells) > 0 and (a >= len(p)-1 or p[a] > p[a+1]): # Removable - add_cells.pop() - - if len(add_cells) == 0: - return None - c = add_cells[0] - if len(rs) == 1: # Special case when it is a single partition - c = c[1:] - return self.__class__(self.parent(), self.value.add_cell(*c)) - - def epsilon(self, i): - r""" - Return `\varepsilon_i` of ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: C = crystals.KleshchevPartitions(2, [0,2]) - sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) - sage: x.epsilon(2) - 1 - """ - ep = 0 - rs = self.parent()._r - rev_enum = lambda l: reversed(list(enumerate(l))) - for j,p in rev_enum(self.value.components()): - r = rs[j] - for a,v in enumerate(p): - res = v - a + r - if res - 1 == i and (a >= len(p)-1 or p[a] > p[a+1]): # Addable - ep += 1 - elif res == i and ep > 0 and (a == 0 or p[a-1] > p[a]): # Removable - ep -= 1 - if r - len(p) == i and ep > 0: # Last addable cell - ep -= 1 - return ep - - def phi(self, i): - r""" - Return `\varphi_i` of ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: C = crystals.KleshchevPartitions(2, [0,2]) - sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) - sage: x.phi(2) - 1 - """ - phi = 0 - rs = self.parent()._r - rev_enum = lambda l: reversed(list(enumerate(l))) - for j,p in enumerate(self.value.components()): - r = rs[j] - if r - len(p) == i: # Last addable cell - phi += 1 - for a,v in rev_enum(p): - res = v - a + r - if res == i and (a == 0 or p[a-1] > p[a]): # Removable - phi += 1 - elif res - 1 == i and phi > 0 and (a >= len(p)-1 or p[a] > p[a+1]): # Addable - phi -= 1 - return phi - - def weight(self): - """ - Return the weight of ``self``. - - EXAMPLES:: - - sage: C = crystals.KleshchevPartitions(2, [0,2]) - sage: x = C(PartitionTuple([[5,4,1],[3,2,1,1]])) - sage: x.weight() - 3*Lambda[0] - Lambda[1] - sage: x.Phi() - x.Epsilon() - 3*Lambda[0] - Lambda[1] - """ - WLR = self.parent().weight_lattice_realization() - alpha = WLR.simple_roots() - La = WLR.fundamental_weights() - r = self.parent()._r - pt = self.value - wt = WLR.sum(La[ZZ(x)] for x in r) - return wt - sum(alpha[pt.content(*c, multicharge=r)] for c in pt.cells()) - diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index e86aa45ae0f..03d2c8af12b 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -3,19 +3,14 @@ """ #***************************************************************************** -# Copyright (C) 2013 Travis Scrimshaw -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: +# Copyright (C) 2013 Travis Scrimshaw # +# 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.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -87,9 +82,7 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): REFERENCES: - .. [LTV1999] Bernard Leclerc, Jean-Yves Thibon, and Eric Vasserot. - *Zelevinsky's involution at roots of unity*. J. Reine Angew. Math. - 513:33-51 (1999). + - [LTV1999]_ """ def __init__(self, n): """ @@ -156,12 +149,9 @@ def __init__(self, parent, value): """ ZM = IntegerModRing(parent.cartan_type().rank()) value = [(k, ZM(i)) for k,i in value] - def sort_cmp(x,y): - c = cmp(y[0],x[0]) - if not c: - c = cmp(ZZ(x[1]),ZZ(y[1])) - return c - ElementWrapper.__init__(self, parent, tuple(sorted(value, cmp=sort_cmp))) + def sort_key(x): + return (-x[0], ZZ(x[1])) + ElementWrapper.__init__(self, parent, tuple(sorted(value, key=sort_key))) def _repr_(self): r""" From 180f43dcfefcaeedad8eeeb2a9f828ebf0a7d38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Mon, 17 Apr 2017 21:23:20 +0200 Subject: [PATCH 105/151] 22744: bring doctest coverage to 100% --- src/sage/combinat/tiling.py | 114 ++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 17 deletions(-) diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index f83baf253c4..91abffa2fa1 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -419,21 +419,40 @@ def ncube_isometry_group_cosets(n, orientation_preserving=True): ############################## class Polyomino_generic(SageObject): r""" + A generic class for polyomino in `\ZZ^d`. - Methods to be implemented in the herited classes: + INPUT: + + - ``dimension`` -- integer + - ``color`` -- string (default: ``'gray'``), color for display - - ``__repr__`` - - ``__len__`` - - ``frozenset`` + The following methods are to be implemented in the herited classes: - By default the following methods depend on the previous:: + - ``__repr__`` + - ``frozenset`` - - ``sorted_list`` depends on ``frozenset`` - - ``__len__`` depends on ``frozenset`` - - ``__iter__`` depends on ``sorted_list`` - - ``bounding_box`` depends on ``__iter__`` + By default the following methods depend on the previous, they may be + overwritten in the herited classes if something more efficient can be + done: + + - ``__len__`` depends on ``frozenset`` + - ``sorted_list`` depends on ``frozenset`` + - ``__iter__`` depends on ``sorted_list`` + - ``bounding_box`` depends on ``__iter__`` """ def __init__(self, dimension, color='gray'): + r""" + Constructor. + + See :mod:`Polyomino_generic` for full documentation. + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino_generic + sage: p = Polyomino_generic(2, color='blue') + sage: p + + """ from sage.modules.free_module import FreeModule from sage.rings.integer_ring import ZZ self._dimension = ZZ(dimension) @@ -456,15 +475,33 @@ def color(self): """ return self._color - def __len__(self): - raise NotImplementedError - - @cached_method def frozenset(self): + r""" + Return the elements of `\ZZ^d` in the polyomino as a frozenset. + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino_generic + sage: p = Polyomino_generic(2, color='blue') + sage: p.frozenset() + Traceback (most recent call last): + ... + NotImplementedError + """ raise NotImplementedError @cached_method def sorted_list(self): + r""" + Return the color of the polyomino. + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') + sage: p.sorted_list() + [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)] + """ return sorted(self.frozenset()) def __len__(self): @@ -1143,7 +1180,7 @@ class Polyomino(Polyomino_generic): The polyomino is the union of the unit square (or cube, or n-cube) centered at those coordinates. Such an object should be connected, but - the code do not make this assumption. + the code does not make this assumption. INPUT: @@ -1158,10 +1195,9 @@ class Polyomino(Polyomino_generic): """ def __init__(self, coords, color='gray'): r""" - INPUT: + Constructor. - - ``coords`` - iterable of tuple - - ``color`` - string (optional, default: ``'gray'``), the color + See :mod:`Polyomino` for full documentation. EXAMPLES:: @@ -1206,13 +1242,52 @@ def __repr__(self): def frozenset(self): r""" + Return the elements of `\ZZ^d` in the polyomino as a frozenset. + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='red') + sage: p.frozenset() + frozenset({(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)}) """ return self._blocs class RectanglePolyomino(Polyomino_generic): r""" + Return the polyomino defined by a cartesian product of integer intervals. + + INPUT: + + - ``min_coords`` - iterable of integers, lower left corner + - ``max_coords`` - iterable of integers, upper right corner + - ``color`` - string (optional, default: ``'gray'``), the color + + EXAMPLES:: + + sage: from sage.combinat.tiling import RectanglePolyomino + sage: b = RectanglePolyomino([0,0], [2,3]) + sage: b + Rectangle Polyomino: [0,2[ x [0,3[, Color: gray + + :: + + sage: from sage.combinat.tiling import RectanglePolyomino + sage: box = RectanglePolyomino([-2,0,-3], [2,2,4]) + sage: box + Rectangle Polyomino: [-2,2[ x [0,2[ x [-3,4[, Color: gray """ def __init__(self, min_coords, max_coords, color='gray'): + r""" + Constructor. + + See :mod:`RectanglePolyomino` for full documentation. + + EXAMPLES:: + + sage: from sage.combinat.tiling import RectanglePolyomino + sage: b = RectanglePolyomino([0,0], [2,3]) + """ self._min_coords = min_coords self._max_coords = max_coords dimension = len(min_coords) @@ -1237,6 +1312,9 @@ def __repr__(self): def __len__(self): r""" + Return the size of the polyomino, i.e. the number of n-dimensional + unit cubes. + EXAMPLES:: sage: from sage.combinat.tiling import RectanglePolyomino @@ -1269,6 +1347,8 @@ def __iter__(self): def frozenset(self): r""" + Return the elements of `\ZZ^d` in the polyomino as a frozenset. + EXAMPLES:: sage: from sage.combinat.tiling import RectanglePolyomino From 05f23985238d1c542221b47e52e6fe9e468f2d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Mon, 17 Apr 2017 21:33:24 +0200 Subject: [PATCH 106/151] 22744: fixing doctests --- src/sage/games/quantumino.py | 63 ++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/sage/games/quantumino.py b/src/sage/games/quantumino.py index 0c7f8b67f52..9ea4f59e947 100644 --- a/src/sage/games/quantumino.py +++ b/src/sage/games/quantumino.py @@ -83,23 +83,22 @@ pentamino:: sage: for p in s: p # long time (<1s) - Polyomino: [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 2, 0), (1, 1, 0)], Color: deeppink - Polyomino: [(0, 1, 1), (1, 1, 1), (1, 2, 1), (2, 1, 0), (2, 1, 1)], Color: deeppink - Polyomino: [(1, 0, 0), (1, 0, 1), (2, 0, 1), (3, 0, 1), (3, 1, 1)], Color: orange - Polyomino: [(2, 0, 0), (3, 0, 0), (4, 0, 0), (4, 0, 1), (4, 1, 1)], Color: green - Polyomino: [(0, 2, 1), (0, 3, 1), (0, 4, 1), (1, 4, 0), (1, 4, 1)], Color: green - Polyomino: [(0, 3, 0), (0, 4, 0), (1, 2, 0), (1, 3, 0), (1, 3, 1)], Color: red - Polyomino: [(2, 2, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1), (4, 1, 0)], Color: red - Polyomino: [(2, 2, 1), (2, 3, 0), (2, 3, 1), (2, 4, 0), (3, 4, 0)], Color: purple - Polyomino: [(3, 3, 0), (4, 2, 0), (4, 2, 1), (4, 3, 0), (4, 3, 1)], Color: yellow - Polyomino: [(3, 3, 1), (3, 4, 1), (4, 4, 0), (4, 4, 1), (4, 5, 0)], Color: blue - Polyomino: [(2, 4, 1), (2, 5, 0), (2, 5, 1), (3, 5, 0), (3, 6, 0)], Color: blue - Polyomino: [(3, 5, 1), (4, 5, 1), (4, 6, 0), (4, 6, 1), (4, 7, 0)], Color: purple - Polyomino: [(2, 7, 1), (3, 6, 1), (3, 7, 0), (3, 7, 1), (4, 7, 1)], Color: gray + Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink + Polyomino: [(0, 0, 1), (0, 1, 0), (0, 1, 1), (0, 2, 1), (1, 2, 1)], Color: deeppink + Polyomino: [(0, 2, 0), (0, 3, 0), (0, 4, 0), (1, 4, 0), (1, 4, 1)], Color: green + Polyomino: [(0, 3, 1), (1, 3, 1), (2, 2, 0), (2, 2, 1), (2, 3, 1)], Color: green + Polyomino: [(1, 3, 0), (2, 3, 0), (2, 4, 0), (2, 4, 1), (3, 4, 0)], Color: red + Polyomino: [(1, 0, 1), (2, 0, 1), (2, 1, 0), (2, 1, 1), (3, 1, 1)], Color: red + Polyomino: [(2, 0, 0), (3, 0, 0), (3, 0, 1), (3, 1, 0), (4, 0, 0)], Color: gray + Polyomino: [(3, 2, 0), (4, 0, 1), (4, 1, 0), (4, 1, 1), (4, 2, 0)], Color: purple + Polyomino: [(3, 2, 1), (3, 3, 0), (3, 3, 1), (4, 2, 1), (4, 3, 1)], Color: yellow + Polyomino: [(3, 4, 1), (3, 5, 1), (4, 3, 0), (4, 4, 0), (4, 4, 1)], Color: blue + Polyomino: [(0, 4, 1), (0, 5, 0), (0, 5, 1), (0, 6, 1), (1, 5, 0)], Color: midnightblue Polyomino: [(0, 6, 0), (0, 7, 0), (0, 7, 1), (1, 7, 0), (2, 7, 0)], Color: darkblue - Polyomino: [(1, 5, 1), (1, 6, 1), (1, 7, 1), (2, 6, 0), (2, 6, 1)], Color: midnightblue - Polyomino: [(0, 5, 0), (0, 5, 1), (0, 6, 1), (1, 5, 0), (1, 6, 0)], Color: yellow - + Polyomino: [(1, 7, 1), (2, 6, 0), (2, 6, 1), (2, 7, 1), (3, 6, 0)], Color: blue + Polyomino: [(1, 5, 1), (1, 6, 0), (1, 6, 1), (2, 5, 0), (2, 5, 1)], Color: yellow + Polyomino: [(3, 6, 1), (3, 7, 0), (3, 7, 1), (4, 5, 1), (4, 6, 1)], Color: purple + Polyomino: [(3, 5, 0), (4, 5, 0), (4, 6, 0), (4, 7, 0), (4, 7, 1)], Color: orange To get all the solutions, use the iterator returned by the ``solve`` method. Note that finding the first solution is the most time consuming @@ -509,22 +508,22 @@ def solve(self, partial=None): The explicit solution:: sage: for p in s: p # long time (fast) - Polyomino: [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 2, 0), (1, 1, 0)], Color: deeppink - Polyomino: [(0, 1, 1), (1, 1, 1), (1, 2, 1), (2, 1, 0), (2, 1, 1)], Color: deeppink - Polyomino: [(1, 0, 0), (1, 0, 1), (2, 0, 1), (3, 0, 1), (3, 1, 1)], Color: orange - Polyomino: [(2, 0, 0), (3, 0, 0), (4, 0, 0), (4, 0, 1), (4, 1, 1)], Color: green - Polyomino: [(0, 2, 1), (0, 3, 1), (0, 4, 1), (1, 4, 0), (1, 4, 1)], Color: green - Polyomino: [(0, 3, 0), (0, 4, 0), (1, 2, 0), (1, 3, 0), (1, 3, 1)], Color: red - Polyomino: [(2, 2, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1), (4, 1, 0)], Color: red - Polyomino: [(2, 2, 1), (2, 3, 0), (2, 3, 1), (2, 4, 1), (3, 3, 0)], Color: midnightblue - Polyomino: [(3, 3, 1), (3, 4, 1), (4, 2, 0), (4, 2, 1), (4, 3, 1)], Color: purple - Polyomino: [(3, 4, 0), (4, 3, 0), (4, 4, 0), (4, 4, 1), (4, 5, 0)], Color: gray - Polyomino: [(1, 5, 1), (1, 6, 1), (2, 4, 0), (2, 5, 0), (2, 5, 1)], Color: blue - Polyomino: [(0, 5, 0), (0, 5, 1), (0, 6, 1), (1, 5, 0), (1, 6, 0)], Color: yellow - Polyomino: [(0, 6, 0), (0, 7, 0), (0, 7, 1), (1, 7, 0), (2, 7, 0)], Color: darkblue - Polyomino: [(1, 7, 1), (2, 6, 0), (2, 6, 1), (2, 7, 1), (3, 6, 0)], Color: blue - Polyomino: [(3, 7, 0), (4, 5, 1), (4, 6, 0), (4, 6, 1), (4, 7, 0)], Color: purple - Polyomino: [(3, 5, 0), (3, 5, 1), (3, 6, 1), (3, 7, 1), (4, 7, 1)], Color: orange + Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink + Polyomino: [(0, 0, 1), (0, 1, 0), (0, 1, 1), (0, 2, 1), (1, 2, 1)], Color: deeppink + Polyomino: [(0, 2, 0), (0, 3, 0), (0, 4, 0), (1, 4, 0), (1, 4, 1)], Color: green + Polyomino: [(0, 3, 1), (1, 3, 1), (2, 2, 0), (2, 2, 1), (2, 3, 1)], Color: green + Polyomino: [(1, 3, 0), (2, 3, 0), (2, 4, 0), (2, 4, 1), (3, 4, 0)], Color: red + Polyomino: [(1, 0, 1), (2, 0, 0), (2, 0, 1), (2, 1, 0), (3, 0, 1)], Color: midnightblue + Polyomino: [(0, 4, 1), (0, 5, 0), (0, 5, 1), (0, 6, 0), (1, 5, 0)], Color: red + Polyomino: [(2, 1, 1), (3, 0, 0), (3, 1, 0), (3, 1, 1), (4, 0, 0)], Color: blue + Polyomino: [(3, 2, 0), (4, 0, 1), (4, 1, 0), (4, 1, 1), (4, 2, 0)], Color: purple + Polyomino: [(3, 2, 1), (3, 3, 0), (4, 2, 1), (4, 3, 0), (4, 3, 1)], Color: yellow + Polyomino: [(3, 3, 1), (3, 4, 1), (4, 4, 0), (4, 4, 1), (4, 5, 0)], Color: blue + Polyomino: [(0, 6, 1), (0, 7, 0), (0, 7, 1), (1, 5, 1), (1, 6, 1)], Color: purple + Polyomino: [(1, 6, 0), (1, 7, 0), (1, 7, 1), (2, 7, 0), (3, 7, 0)], Color: darkblue + Polyomino: [(2, 5, 0), (2, 6, 0), (3, 6, 0), (4, 6, 0), (4, 6, 1)], Color: orange + Polyomino: [(2, 5, 1), (3, 5, 0), (3, 5, 1), (3, 6, 1), (4, 5, 1)], Color: gray + Polyomino: [(2, 6, 1), (2, 7, 1), (3, 7, 1), (4, 7, 0), (4, 7, 1)], Color: orange Enumerate the solutions:: From a9677554b73518c5a72f07ef427a1d0befa046f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 17 Apr 2017 21:53:01 +0200 Subject: [PATCH 107/151] trac 16473 deprecate MultichooseNK --- src/sage/combinat/multichoose_nk.py | 16 ++++++++++++++++ .../root_system/root_lattice_realizations.py | 7 +++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/multichoose_nk.py b/src/sage/combinat/multichoose_nk.py index 084d610c407..8ea8ae00c90 100644 --- a/src/sage/combinat/multichoose_nk.py +++ b/src/sage/combinat/multichoose_nk.py @@ -21,6 +21,8 @@ from .combinat import CombinatorialClass from sage.arith.all import binomial import sage.misc.prandom as rnd +from sage.misc.superseded import deprecation + class MultichooseNK(CombinatorialClass): def __init__(self, n, k): @@ -28,9 +30,14 @@ def __init__(self, n, k): TESTS:: sage: a = MultichooseNK(3,2) + doctest:...: DeprecationWarning: MultichooseNK should be + replaced by itertools.combinations_with_replacement + See http://trac.sagemath.org/16473 for details. sage: a == loads(dumps(a)) True """ + deprecation(16473, "MultichooseNK should be replaced by " + "itertools.combinations_with_replacement") self._n = n self._k = k @@ -42,6 +49,9 @@ def cardinality(self): EXAMPLES:: sage: MultichooseNK(3,2).cardinality() + doctest:...: DeprecationWarning: MultichooseNK should be + replaced by itertools.combinations_with_replacement + See http://trac.sagemath.org/16473 for details. 6 """ n,k = self._n, self._k @@ -54,6 +64,9 @@ def __iter__(self): EXAMPLES:: sage: [c for c in MultichooseNK(3,2)] + doctest:...: DeprecationWarning: MultichooseNK should be + replaced by itertools.combinations_with_replacement + See http://trac.sagemath.org/16473 for details. [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]] """ n,k = self._n, self._k @@ -97,6 +110,9 @@ def random_element(self): EXAMPLES:: sage: MultichooseNK(5,2).random_element() + doctest:...: DeprecationWarning: MultichooseNK should be + replaced by itertools.combinations_with_replacement + See http://trac.sagemath.org/16473 for details. [0, 2] sage: MultichooseNK(5,2).random_element() [0, 1] diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 5ef534fb0f9..3cd701a1064 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -11,8 +11,7 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -from __future__ import absolute_import +from __future__ import print_function, absolute_import from sage.misc.abstract_method import abstract_method, AbstractMethod from sage.misc.misc import attrcall @@ -29,6 +28,7 @@ from sage.modules.free_module_element import vector from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.combinat.root_system.plot import PlotOptions, barycentric_projection_matrix +from itertools import combinations_with_replacement class RootLatticeRealizations(Category_over_base_ring): @@ -1126,13 +1126,12 @@ def generalized_nonnesting_partition_lattice(self, m, facade=False): sage: P.coxeter_transformation()**20 == 1 True """ - from sage.combinat.multichoose_nk import MultichooseNK Phi_plus = self.positive_roots() L = self.nonnesting_partition_lattice(facade=True) chains = [chain for chain in L.chains().list() if len(chain) <= m] multichains = [] for chain in chains: - for multilist in MultichooseNK(len(chain), m): + for multilist in combinations_with_replacement(range(len(chain)), m): if len(set(multilist)) == len(chain): multichains.append(tuple([chain[i] for i in multilist])) def is_saturated_chain(chain): From 9745f93735a156f30b7e9ff31d7fb2687ff6e4b6 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 13 Apr 2017 17:07:39 +0200 Subject: [PATCH 108/151] cython(): do not include .pxi files --- src/sage/misc/cython.py | 73 +++++++++++++++++++++++++++++++++--- src/sage/misc/sageinspect.py | 7 +--- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index ab9772b411f..56643e9cc4e 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -46,7 +46,46 @@ 'ntl'] -offset = 0 +# Functions which used to be automatically declared. +# We list these here in order to give useful warnings. +old_pxi_names = { + "cysignals.signals": [ + "sig_on", "sig_str", "sig_check", "sig_off", + "sig_retry", "sig_error", "sig_block", "sig_unblock", + "sig_on_no_except", "sig_str_no_except", "sig_check_no_except", + "cython_check_exception", + ], + "sage.ext.stdsage": [ + "PY_NEW", "HAS_DICTIONARY", + ], + "cysignals.memory": [ + "sig_malloc", "sig_realloc", "sig_calloc", "sig_free", + "check_allocarray", "check_reallocarray", + "check_malloc", "check_realloc", "check_calloc", + ], + "libc.string": [ + "strlen", "strcpy", "memset", "memcpy", "memcmp", + ], + "libc.math": [ + "sqrt", "frexp", "ldexp", + ], + "libc.stdio": [ + "stdin", "stdout", "stderr", + "FOPEN_MAX", "FILENAME_MAX", + "fopen", "freopen", "fdopen", "fclose", + "remove", "rename", "tmpfile", + "setvbuf", "BUFSIZ", "setbuf", + "fread", "fwrite", "fflush", + "EOF", "clearerr", "feof", "ferror", + "SEEK_SET", "SEEK_CUR", "SEEK_END", + "fseek", "rewind", "ftell", "fgetpos", "fsetpos", + "scanf", "sscanf", "fscanf", + "printf", "sprintf", "snprintf", "fprintf", + "perror", "gets", "fgets", "getchar", "fgetc", "getc", "ungetc", + "puts", "fputs", "putchar", "fputc", "putc", "getline", + ] + } + def parse_keywords(kwd, s): r""" @@ -134,7 +173,6 @@ def pyx_preparse(s): r""" Preparse a pyx file: - * include ``cdefs.pxi``, ``signals.pxi`` from ``cysignals``, ``stdsage.pxi`` * parse ``clang`` pragma (c or c++) * parse ``clib`` pragma (additional libraries to link in) * parse ``cinclude`` (additional include directories) @@ -164,7 +202,7 @@ def pyx_preparse(s): sage: from sage.misc.cython import pyx_preparse sage: pyx_preparse("") - ('\ninclude "cysignals/signals.pxi" # ctrl-c interrupt block support\ninclude "stdsage.pxi"\n\ninclude "cdefs.pxi"\n', + ('', ['mpfr', 'gmp', 'gmpxx', @@ -235,8 +273,6 @@ def pyx_preparse(s): v, s = parse_keywords('cinclude', s) inc = [environ_parse(x.replace('"','').replace("'","")) for x in v] + sage_include_directories() - s = """\ninclude "cdefs.pxi"\n""" + s - s = """\ninclude "cysignals/signals.pxi" # ctrl-c interrupt block support\ninclude "stdsage.pxi"\n""" + s args, s = parse_keywords('cargs', s) args = ['-w','-O2'] + args libdirs = cblas_library_dirs @@ -349,6 +385,19 @@ def cython(filename, verbose=False, compile_message=False, ....: "from libcpp.vector cimport vector\n" ....: "cdef vector[int] * v = new vector[int](4)\n") sage: output = sage.misc.cython.cython("test.pyx", create_local_c_file=True) + + Sage used to automatically include various ``.pxi`` files. Since + :trac:`22805`, we no longer do this. But we make sure to give a + useful message in case the ``.pxi`` files were needed:: + + sage: cython("sig_malloc(0)") + Traceback (most recent call last): + ... + RuntimeError: Error converting ... to C: + ... + NOTE: Sage no longer automatically includes the deprecated files + "cdefs.pxi", "signals.pxi" and "stdsage.pxi" in Cython files. + You can fix your code by adding "from cysignals.memory cimport sig_malloc". """ if not filename.endswith('pyx'): print("Warning: file (={}) should have extension .pyx".format(filename), file=sys.stderr) @@ -486,7 +535,14 @@ def cython(filename, verbose=False, compile_message=False, print(cmd) if os.system(cmd): log = open('%s/log'%build_dir).read() - err = subtract_from_line_numbers(open('%s/err'%build_dir).read(), offset) + err = open('%s/err'%build_dir).read() + for pxd, names in old_pxi_names.items(): + for name in names: + if ("undeclared name not builtin: " + name) in err: + err += '\nNOTE: Sage no longer automatically includes the deprecated files\n' \ + '"cdefs.pxi", "signals.pxi" and "stdsage.pxi" in Cython files.\n' \ + 'You can fix your code by adding "from %s cimport %s".' % \ + (pxd, name) raise RuntimeError("Error converting {} to C:\n{}\n{}".format(filename, log, err)) cmd = 'cd %s && python setup.py build 1>log 2>err'%build_dir @@ -525,10 +581,15 @@ def subtract_from_line_numbers(s, n): sage: from sage.misc.cython import subtract_from_line_numbers sage: subtract_from_line_numbers('hello:1234:hello', 3) + doctest:...: DeprecationWarning: subtract_from_line_numbers is deprecated + See http://trac.sagemath.org/22805 for details. 'hello:1231:hello\n' sage: subtract_from_line_numbers('text:123\nhello:1234:', 3) 'text:123\nhello:1231:\n' """ + from sage.misc.superseded import deprecation + deprecation(22805, 'subtract_from_line_numbers is deprecated') + ans = [] for X in s.split('\n'): i = X.find(':') diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 8feb203afab..a71066aeb8d 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -204,11 +204,6 @@ def _extract_embedded_position(docstring): sage: cython('''cpdef test_funct(x,y): return''') sage: print(open(_extract_embedded_position(inspect.getdoc(test_funct))[1]).read()) - - include "cysignals/signals.pxi" # ctrl-c interrupt block support - include "stdsage.pxi" - - include "cdefs.pxi" cpdef test_funct(x,y): return AUTHORS: @@ -1997,7 +1992,7 @@ def sage_getsourcelines(obj): sage: cython('''cpdef test_funct(x,y): return''') sage: sage_getsourcelines(test_funct) - (['cpdef test_funct(x,y): return\n'], 6) + (['cpdef test_funct(x,y): return\n'], 1) The following tests that an instance of ``functools.partial`` is correctly dealt with (see :trac:`9976`):: From 9c66c20b0e7424bde20c2b4e4ac60e940deb7980 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 17 Apr 2017 22:04:51 +0200 Subject: [PATCH 109/151] Fix .spyx doctest --- src/sage/tests/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tests/cmdline.py b/src/sage/tests/cmdline.py index e421090835e..7387867d886 100644 --- a/src/sage/tests/cmdline.py +++ b/src/sage/tests/cmdline.py @@ -279,7 +279,7 @@ def test_executable(args, input="", timeout=100.0, **kwds): sage: dir = tmp_dir(); name = 'sage_test_file.spyx' sage: fullname = os.path.join(dir, name) sage: F = open(fullname, 'w') - sage: F.write("from sage.rings.integer cimport Integer\ncdef long i, s = 0\nsig_on()\nfor i in range(1000): s += i\nsig_off()\nprint(Integer(s))") + sage: F.write("from cysignals.signals cimport *\nfrom sage.rings.integer cimport Integer\ncdef long i, s = 0\nsig_on()\nfor i in range(1000): s += i\nsig_off()\nprint(Integer(s))") sage: F.close() sage: (out, err, ret) = test_executable(["sage", fullname]) sage: print(out) From 0a8c8ba321ba1a8e1870dbb09c5130c3ae5c1143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Tue, 18 Apr 2017 00:10:35 +0200 Subject: [PATCH 110/151] 22744: updated documentation --- src/sage/combinat/tiling.py | 103 ++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index 91abffa2fa1..a07f2a90145 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -1,15 +1,7 @@ r""" Tiling Solver -Tiling a n-dimensional box into non-intersecting n-dimensional polyominoes. - -This uses dancing links code which is in Sage. Dancing links were -originally introduced by Donald Knuth in 2000 [Knuth1]_. In -particular, Knuth used dancing links to solve tilings of a region by -2d pentaminoes. Here we extend the method to any dimension. - -In particular, the :mod:`sage.games.quantumino` module is based on -the Tiling Solver and allows to solve the 3d Quantumino puzzle. +Tiling a n-dimensional polyomino with n-dimensional polyominoes. This module defines two classes: @@ -18,17 +10,26 @@ rotated, reflected and/or translated copies of a polyomino that are contained in a certain box. -- :class:`sage.combinat.tiling.TilingSolver` class, to solve the general - problem of tiling a rectangular `n`-dimensional box with a set of - `n`-dimensional polyominoes. One can specify if rotations and reflections - are allowed or not and if pieces can be reused or not. This class convert - the tiling data into rows of a matrix that are passed to the DLX solver. - It also allows to compute the number of solutions. +- :class:`sage.combinat.tiling.TilingSolver` class, to solve the problem of + tiling a `n`-dimensional polyomino with a set of `n`-dimensional + polyominoes. One can specify if rotations and reflections are allowed or + not and if pieces can be reused or not. This class convert the tiling + data into rows of a matrix that are passed to the DLX solver. It also + allows to compute the number of solutions. + +This uses dancing links code which is in Sage. Dancing links were +originally introduced by Donald Knuth in 2000 [Knuth1]_. Knuth used dancing +links to solve tilings of a region by 2d pentaminoes. Here we extend the +method to any dimension. + +In particular, the :mod:`sage.games.quantumino` module is based on +the Tiling Solver and allows to solve the 3d Quantumino puzzle. AUTHOR: - Sebastien Labbe, June 2011, initial version - Sebastien Labbe, July 2015, count solutions up to rotations + - Sebastien Labbe, April 2017, tiling a polyomino, not only a rectangular box EXAMPLES: @@ -55,6 +56,62 @@ sage: T.number_of_solutions() 2 +Scott's pentomino problem +------------------------- + +As mentionned in the introduction of [Knuth1]_, Scott's pentomino problem +consists in tiling a chessboard leaving the center four squares vacant with +the 12 distinct pentominoes. + +The 12 pentominoes:: + + sage: from sage.combinat.tiling import Polyomino + sage: I = Polyomino([(0,0),(1,0),(2,0),(3,0),(4,0)], color='brown') + sage: N = Polyomino([(1,0),(1,1),(1,2),(0,2),(0,3)], color='yellow') + sage: L = Polyomino([(0,0),(1,0),(0,1),(0,2),(0,3)], color='magenta') + sage: U = Polyomino([(0,0),(1,0),(0,1),(0,2),(1,2)], color='violet') + sage: X = Polyomino([(1,0),(0,1),(1,1),(1,2),(2,1)], color='pink') + sage: W = Polyomino([(2,0),(2,1),(1,1),(1,2),(0,2)], color='green') + sage: P = Polyomino([(1,0),(2,0),(0,1),(1,1),(2,1)], color='orange') + sage: F = Polyomino([(1,0),(1,1),(0,1),(2,1),(2,2)], color='gray') + sage: Z = Polyomino([(0,0),(1,0),(1,1),(1,2),(2,2)], color='yellow') + sage: T = Polyomino([(0,0),(0,1),(1,1),(2,1),(0,2)], color='red') + sage: Y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='green') + sage: V = Polyomino([(0,0),(0,1),(0,2),(1,0),(2,0)], color='blue') + +A 8x8 chessboard leaving the center four squares vacant:: + + sage: import itertools + sage: s = set(itertools.product(range(8), repeat=2)) + sage: s.difference_update([(3,3), (3,4), (4,3), (4,4)]) + sage: chessboard = Polyomino(s) + sage: len(chessboard) + 60 + +This problem is represented by a matrix made of 1568 rows and 72 columns. +It has 65 different solutions up to isometries:: + + sage: from sage.combinat.tiling import TilingSolver + sage: T = TilingSolver([I,N,L,U,X,W,P,F,Z,T,Y,V], box=chessboard, reflection=True) + sage: T + Tiling solver of 12 pieces into the box + Polyomino: [...], Color: gray + Rotation allowed: True + Reflection allowed: True + Reusing pieces allowed: False + sage: len(T.rows()) # long time + 1568 + sage: T.number_of_solutions() # long time + 520 + sage: 520 / 8 + 65 + +Showing one solution:: + + sage: solution = next(T.solve()) # not tested + sage: G = sum([piece.show2d() for piece in solution], Graphics()) # not tested + sage: G.show(aspect_ratio=1, axes=False) # not tested + 1d Easy Example --------------- @@ -94,16 +151,16 @@ sage: L.append(Polyomino([(0,0),(0,1),(0,2),(1,0),(1,1),(1,2)],"pink")) By default, rotations are allowed and reflections are not. In this case, -there are no solution:: +there are no solution for tiling a 8x8 rectangular box:: - sage: T = TilingSolver(L, (8,8)) + sage: T = TilingSolver(L, box=(8,8)) sage: T.number_of_solutions() # long time (2.5 s) 0 If reflections are allowed, there are solutions. Solve the puzzle and show one solution:: - sage: T = TilingSolver(L, (8,8), reflection=True) + sage: T = TilingSolver(L, box=(8,8), reflection=True) sage: solution = next(T.solve()) # long time (7s) sage: G = sum([piece.show2d() for piece in solution], Graphics()) # long time (<1s) sage: G.show(aspect_ratio=1, axes=False) # long time (2s) @@ -139,7 +196,7 @@ Solve the puzzle and show one solution:: - sage: T = TilingSolver(L, (8,8,1)) + sage: T = TilingSolver(L, box=(8,8,1)) sage: solution = next(T.solve()) # long time (8s) sage: G = sum([p.show3d(size=0.85) for p in solution], Graphics()) # long time (<1s) sage: G.show(aspect_ratio=1, viewer='tachyon') # long time (2s) @@ -1901,7 +1958,7 @@ def row_to_polyomino(self, row_number): def dlx_solver(self): r""" - Return the sage DLX solver of that 3d tiling problem. + Return the sage DLX solver of that tiling problem. OUTPUT: @@ -2070,8 +2127,8 @@ def _dlx_incremental_solutions_iterator(self): def solve(self, partial=None): r""" - Returns an iterator of list of 3d polyominoes that are an exact - cover of the box. + Returns an iterator of list of polyominoes that are an exact cover + of the box. INPUT: @@ -2085,7 +2142,7 @@ def solve(self, partial=None): OUTPUT: - iterator of list of 3d polyominoes + iterator of list of polyominoes EXAMPLES:: From cc87885202d9d71e1308f862b2b1ec126ab2a834 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 17 Apr 2017 17:13:32 -0500 Subject: [PATCH 111/151] Changing repr/latex formatting to use linear combinations. --- src/doc/en/reference/references/index.rst | 9 + src/sage/combinat/crystals/multisegments.py | 312 +++++++++++--------- 2 files changed, 176 insertions(+), 145 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index f653998f336..45c576522ba 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -57,6 +57,11 @@ REFERENCES: .. [Aj1996] \M. Ajtai. Generating hard instances of lattice problems (extended abstract). STOC, pp. 99--108, ACM, 1996. + +.. [AJL2011] Susumu Ariki, Nicolas Jacon, and Cedric Lecouvey. + *The modular branching rule for affine Hecke algebras of type A*. + Adv. Math. 228:481-526 (2011). + .. [Al1947] \A. A. Albert, *A Structure Theory for Jordan Algebras*. Annals of Mathematics, Second Series, Vol. 48, No. 3 (Jul., 1947), pp. 546--567. @@ -877,6 +882,10 @@ REFERENCES: .. [Ja1971] \N. Jacobson. *Exceptional Lie Algebras*. Marcel Dekker, Inc. New York. 1971. IBSN No. 0-8247-1326-5. +.. [JL2009] Nicolas Jacon and Cedric Lecouvey. + *Kashiwara and Zelevinsky involutions in affine type A*. + Pac. J. Math. 243(2):287-311 (2009). + .. [Joh1990] \D.L. Johnson. *Presentations of Groups*. Cambridge University Press. (1990). diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index d454cb16215..64c8d134dfd 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -13,6 +13,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc.cachefunc import cached_method from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element_wrapper import ElementWrapper @@ -28,119 +29,109 @@ class InfinityCrystalOfMultisegments(Parent, UniqueRepresentation): Bernstein-Zelevinsky (BZ) multisegments. Using (a modified version of the) notation from [JL2009]_, for `\ell \in - \ZZ_{>0}` and `i \in \ZZ /(n+1)\ZZ`, a segment of length `\ell` and head `i` - is the sequence of consecutive residues `[i,i+1,\dots,i+\ell-1]`. The + \ZZ_{>0}` and `i \in \ZZ / (n+1)\ZZ`, a segment of length `\ell` and head + `i` is the sequence of consecutive residues `[i,i+1,\dots,i+\ell-1]`. The notation for a segment of length `\ell` and head `i` is simplified to - `[i;\ell)`. Similarly, a segment of length `\ell` and tail `i` is the - sequence of consecutive residues `[i-\ell+1,\dots,i-1,i]`. The latter is - denoted simply by `(\ell;i]`. Finally, a multisegment is a formal linear - combination of segments, usually written in the form + `[i; \ell)`. Similarly, a segment of length `\ell` and tail `i` is the + sequence of consecutive residues `[i-\ell+1, \ldots, i-1, i]`. The latter + is denoted simply by `(\ell;i]`. Finally, a multisegment is a formal + linear combination of segments, usually written in the form .. MATH:: \psi = \sum_{\substack{i \in \ZZ/(n+1)\ZZ \\ \ell \in \ZZ_{>0}}} - m_{(\ell;i]}(\ell;i]. + m_{(\ell;i]} (\ell; i]. - Such a multisegment is called aperiodic if, for every `\ell>0`, there exists - some `i \in \ZZ/(n+1)\ZZ` such that `(\ell;i]` does not appear in `\psi`. - Denote the set of all periodic multisegments, together with the empty - multisegment `\varnothing`, by `\Psi`. We define a crystal - structure on multisegments as follows. Set `S_{\ell,i} = \sum_{k\ge \ell} + Such a multisegment is called aperiodic if, for every `\ell > 0`, there + exists some `i \in \ZZ / (n+1)\ZZ` such that `(\ell; i]` does not appear + in `\psi`. Denote the set of all periodic multisegments, together with + the empty multisegment `\varnothing`, by `\Psi`. We define a crystal + structure on multisegments as follows. Set `S_{\ell,i} = \sum_{k \ge \ell} (m_{(k;i-1]} - m_{(k;i]})` and let `\ell_f` be the minimal `\ell` that - attains the value `\min_{\ell>0} S_{\ell,i}`. Then `f_i\psi = - \psi_{\ell_f,i}`, where + attains the value `\min_{\ell > 0} S_{\ell,i}`. Then we have .. MATH:: - \psi_{\ell_f,i} = + f_i \psi = \begin{cases} \psi + (1;i] & \text{ if } \ell_f = 1,\\ \psi + (\ell_f;i] - (\ell_f-1;i-1] & \text{ if } \ell_f > 1. \end{cases} Similarly, let `\ell_e` be the maximal `\ell` that attains the value - `\min_{\ell>0} S_{\ell,i}`. Then `e_i\psi = \psi_{\ell_e,i}`, where + `\min_{\ell > 0} S_{\ell,i}`. Then we have .. MATH:: - \psi_{\ell_e,i} = + e_i \psi = \begin{cases} - 0 & \text{ if } \min_{\ell>0} S_{\ell,i} = 0, \\ - \psi + (1;i] & \text{ if } \ell_e = 1,\\ - \psi - (\ell_e;i] + (\ell_e-1;i-1] & \text{ if } \ell_e > 1. + 0 & \text{ if } \min_{\ell > 0} S_{\ell,i} = 0, \\ + \psi + (1; i] & \text{ if } \ell_e = 1,\\ + \psi - (\ell_e; i] + (\ell_e-1; i-1] & \text{ if } \ell_e > 1. \end{cases} - Alternatively, the crystal operators may be defined using a signature rule, - as detailed in Section 4 of [JL2009]_ (following [AJL2011]_). For - `\psi \in \Psi` and `i \in \ZZ/(n+1)\ZZ`, encode all segments in `\psi` with - tail `i` by the symbol `R` and all segments in `\psi` with tail `i-1` by - `A`. For `\ell> 0`, set - `w_{i,\ell} = R^{m_{(\ell,i]}} A^{m_{(\ell,i-1]}}` and - `w_i = \prod_{\ell\ge 1} w_{i,\ell}`. By successively canceling out as many - `RA` factors as possible, set - `\widetilde{w}_i = A^{a_i(\psi)}R^{r_i(\psi)}`. If `a_i(\psi)>0`, denote - by `\ell_f > 0` the length of the rightmost segment `A` in + Alternatively, the crystal operators may be defined using a signature + rule, as detailed in Section 4 of [JL2009]_ (following [AJL2011]_). For + `\psi \in \Psi` and `i \in \ZZ/(n+1)\ZZ`, encode all segments in `\psi` + with tail `i` by the symbol `R` and all segments in `\psi` with tail + `i-1` by `A`. For `\ell > 0`, set + `w_{i,\ell} = R^{m_{(\ell;i]}} A^{m_{(\ell;i-1]}}` and + `w_i = \prod_{\ell\ge 1} w_{i,\ell}`. By successively canceling out + as many `RA` factors as possible, set + `\widetilde{w}_i = A^{a_i(\psi)} R^{r_i(\psi)}`. If `a_i(\psi) > 0`, + denote by `\ell_f > 0` the length of the rightmost segment `A` in `\widetilde{w}_i`. If `a_i(\psi) = 0`, set `\ell_f = 0`. Then .. MATH:: - f_i\psi = + f_i \psi = \begin{cases} - \psi + (1;i] & \text{ if } a_i(\psi) = 0,\\ - \psi + (\ell_f;i] - (\ell_f-1;i-1] & \text{ if } a_i(\psi) > 0. + \psi + (1; i] & \text{ if } a_i(\psi) = 0,\\ + \psi + (\ell_f; i] - (\ell_f-1; i-1] & \text{ if } a_i(\psi) > 0. \end{cases} - The rule for computing `e_i\psi` is similar. + The rule for computing `e_i \psi` is similar. INPUT: - ``n`` -- for type `A_n^{(1)}` -# EXAMPLES:: -# -# sage: B = crystals.infinity.Multisegments(2) -# sage: x = B([(8,1),(6,0),(5,1),(5,0),(4,0),(4,1),(4,1),(3,0),(3,0),(3,1),(3,1),(1,0),(1,2),(1,2)]); x -# {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], -# (3; 0], (3; 0], (3; 1], (3; 1], (1; 0], (1; 2], (1; 2]} -# sage: x.f(1) -# {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], -# (3; 0], (3; 0], (3; 1], (3; 1], (2; 1], (1; 2], (1; 2]} -# sage: x.f(1).f(1) -# {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], (3; 0], -# (3; 0], (3; 1], (3; 1], (2; 1], (1; 1], (1; 2], (1; 2]} -# sage: x.e(1) -# {(7; 0], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], (3; 0], (3; 0], (3; 1], (3; 1], (1; 0], (1; 2], (1; 2]} -# sage: x.e(1).e(1) -# sage: x.f(0) -# {(8; 1], (6; 0], (5; 0], (5; 1], (4; 0], (4; 1], (4; 1], -# (3; 0], (3; 0], (3; 1], (3; 1], (2; 0], (1; 0], (1; 2]} + EXAMPLES:: + + sage: B = crystals.infinity.Multisegments(2) + sage: x = B([(8,1),(6,0),(5,1),(5,0),(4,0),(4,1),(4,1),(3,0),(3,0),(3,1),(3,1),(1,0),(1,2),(1,2)]); x + (8; 1] + (6; 0] + (5; 0] + (5; 1] + (4; 0] + 2 * (4; 1] + + 2 * (3; 0] + 2 * (3; 1] + (1; 0] + 2 * (1; 2] + sage: x.f(1) + (8; 1] + (6; 0] + (5; 0] + (5; 1] + (4; 0] + 2 * (4; 1] + + 2 * (3; 0] + 2 * (3; 1] + (2; 1] + 2 * (1; 2] + sage: x.f(1).f(1) + (8; 1] + (6; 0] + (5; 0] + (5; 1] + (4; 0] + 2 * (4; 1] + + 2 * (3; 0] + 2 * (3; 1] + (2; 1] + (1; 1] + 2 * (1; 2] + sage: x.e(1) + (7; 0] + (6; 0] + (5; 0] + (5; 1] + (4; 0] + 2 * (4; 1] + + 2 * (3; 0] + 2 * (3; 1] + (1; 0] + 2 * (1; 2] + sage: x.e(1).e(1) + sage: x.f(0) + (8; 1] + (6; 0] + (5; 0] + (5; 1] + (4; 0] + 2 * (4; 1] + + 2 * (3; 0] + 2 * (3; 1] + (2; 0] + (1; 0] + (1; 2] We check an `\widehat{\mathfrak{sl}}_2` example against the generalized Young walls:: sage: B = crystals.infinity.Multisegments(1) - sage: S = B.subcrystal(max_depth=4) - sage: G = B.digraph(subset=S) + sage: G = B.subcrystal(max_depth=4).digraph() sage: C = crystals.infinity.GeneralizedYoungWalls(1) - sage: SC = C.subcrystal(max_depth=4) - sage: GC = C.digraph(subset=SC) + sage: GC = C.subcrystal(max_depth=4).digraph() sage: G.is_isomorphic(GC, edge_labels=True) True REFERENCES: - .. [AJL2011] Susumu Ariki, Nicolas Jacon, and Cedric Lecouvey. - *The modular branching rule for affine Hecke algebras of type A*. - Adv. Math. 228:481-526 (2011). - - .. [JL2009] Nicolas Jacon and Cedric Lecouvey. - *Kashiwara and Zelevinsky involutions in affine type A*. - Pac. J. Math. 243(2):287-311 (2009). - - .. [LTV1999] Bernard Leclerc, Jean-Yves Thibon, and Eric Vasserot. - *Zelevinsky's involution at roots of unity*. - J. Reine Angew. Math. 513:33-51 (1999). + - [AJL2011]_ + - [JL2009]_ + - [LTV1999]_ """ def __init__(self, n): """ @@ -152,8 +143,9 @@ def __init__(self, n): sage: TestSuite(B).run() """ self._cartan_type = CartanType(['A', n, 1]) + self._Zn = IntegerModRing(n+1) Parent.__init__(self, category=(HighestWeightCrystals(), InfiniteEnumeratedSets())) - self.module_generators = (self.module_generator(),) + self.module_generators = (self.highest_weight_vector(),) def _repr_(self): """ @@ -162,19 +154,20 @@ def _repr_(self): EXAMPLES:: sage: crystals.infinity.Multisegments(2) - The infinity crystal of multisegments of type ['A', 2, 1] + Infinity crystal of multisegments of type ['A', 2, 1] """ - return "The infinity crystal of multisegments of type {}".format(self._cartan_type) + return "Infinity crystal of multisegments of type {}".format(self._cartan_type) - def module_generator(self): + @cached_method + def highest_weight_vector(self): """ - Return the module generator of ``self``. + Return the highest weight vector of ``self``. EXAMPLES:: sage: B = crystals.infinity.Multisegments(2) - sage: B.module_generator() - {} + sage: B.highest_weight_vector() + 0 """ return self.element_class(self, ()) @@ -201,40 +194,68 @@ def __init__(self, parent, value): EXAMPLES:: sage: B = crystals.infinity.Multisegments(2) - sage: mg = B.module_generator() + sage: mg = B.highest_weight_vector() sage: TestSuite(mg).run() """ - ZM = IntegerModRing(parent.cartan_type().rank()) - value = [(k, ZM(i)) for k,i in value] def sort_key(x): return (-x[0], ZZ(x[1])) + ZM = parent._Zn + value = [(k, ZM(i)) for k,i in value] ElementWrapper.__init__(self, parent, tuple(sorted(value, key=sort_key))) def _repr_(self): r""" Return a string representation of ``self``. -# EXAMPLES:: -# -# sage: B = crystals.infinity.Multisegments(2) -# sage: B([(4,2), (3,0), (3,1), (1,1), (1,0)]) -# {(4; 2], (3; 0], (3; 1], (1; 0], (1; 1]} + EXAMPLES:: + + sage: B = crystals.infinity.Multisegments(2) + sage: B.highest_weight_vector() + 0 + sage: B([(4,2), (3,0), (3,1), (3,1), (1,1), (1,0)]) + (4; 2] + (3; 0] + 2 * (3; 1] + (1; 0] + (1; 1] """ - seg = lambda x: "({}; {}]".format(x[0], x[1]) - return "{" + ", ".join(seg(x) for x in self.value) + "}" + if not self.value: + return '0' + def sort_key(mc): + x = mc[0] + return (-x[0], ZZ(x[1])) + def seg(x): + m, c = x + if c != 1: + return "{} * ({}; {}]".format(c, m[0], m[1]) + return "({}; {}]".format(m[0], m[1]) + d = {} + for x in self.value: + d[x] = d.get(x, 0) + 1 + return " + ".join(seg(x) for x in sorted(d.items(), key=sort_key)) def _latex_(self): r""" Return a LaTeX representation of ``self``. -# EXAMPLES:: -# -# sage: B = crystals.infinity.Multisegments(2) -# sage: latex(B([(4,2), (3,0), (3,1), (1,1), (1,0)])) -# \bigl\{(4; 2], (3; 0], (3; 1], (1; 0], (1; 1]\bigr\} + EXAMPLES:: + + sage: B = crystals.infinity.Multisegments(2) + sage: latex(B.highest_weight_vector()) + 0 + sage: latex(B([(4,2), (3,0), (3,1), (3,1), (1,1), (1,0)])) + (4; 2] + (3; 0] + 2 (3; 1] + (1; 0] + (1; 1] """ - seg = lambda x: "({}; {}]".format(x[0], x[1]) - return "\\bigl\\{" + ", ".join(seg(x) for x in self.value) + "\\bigr\\}" + if not self.value: + return "0" + def sort_key(mc): + x = mc[0] + return (-x[0], ZZ(x[1])) + def seg(x): + m, c = x + if c != 1: + return "{} ({}; {}]".format(c, m[0], m[1]) + return "({}; {}]".format(m[0], m[1]) + d = {} + for x in self.value: + d[x] = d.get(x, 0) + 1 + return " + ".join(seg(x) for x in sorted(d.items(), key=sort_key)) def _sig(self, i): r""" @@ -251,14 +272,14 @@ def _sig(self, i): if there is no such block and ``ep`` is the number of unmatched `-`. -# EXAMPLES:: -# -# sage: B = crystals.infinity.Multisegments(2) -# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) -# sage: b._sig(0) -# (1, None, 1) -# sage: b._sig(1) -# (None, None, 0) + EXAMPLES:: + + sage: B = crystals.infinity.Multisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b._sig(0) + (1, None, 1) + sage: b._sig(1) + (None, None, 0) """ if not self.value: return (None, None) @@ -304,17 +325,17 @@ def e(self, i): - ``i`` -- an element of the index set -# EXAMPLES:: -# -# sage: B = crystals.infinity.Multisegments(2) -# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) -# sage: b.e(0) -# {(4; 2], (3; 0], (3; 1], (1; 1]} -# sage: b.e(1) -# sage: b.e(2) -# {(3; 0], (3; 1], (3; 1], (1; 0], (1; 1]} + EXAMPLES:: + + sage: B = crystals.infinity.Multisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b.e(0) + (4; 2] + (3; 0] + (3; 1] + (1; 1] + sage: b.e(1) + sage: b.e(2) + (3; 0] + 2 * (3; 1] + (1; 0] + (1; 1] """ - i = IntegerModRing(self.parent()._cartan_type.rank())(i) + i = self.parent()._Zn(i) m = self._sig(i)[0] if m is None: return None @@ -334,18 +355,18 @@ def f(self, i): - ``i`` -- an element of the index set -# EXAMPLES:: -# -# sage: B = crystals.infinity.Multisegments(2) -# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) -# sage: b.f(0) -# {(4; 2], (3; 0], (3; 1], (1; 0], (1; 0], (1; 1]} -# sage: b.f(1) -# {(4; 2], (3; 0], (3; 1], (1; 0], (1; 1], (1; 1]} -# sage: b.f(2) -# {(4; 2], (4; 2], (3; 0], (1; 0], (1; 1]} + EXAMPLES:: + + sage: B = crystals.infinity.Multisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b.f(0) + (4; 2] + (3; 0] + (3; 1] + 2 * (1; 0] + (1; 1] + sage: b.f(1) + (4; 2] + (3; 0] + (3; 1] + (1; 0] + 2 * (1; 1] + sage: b.f(2) + 2 * (4; 2] + (3; 0] + (1; 0] + (1; 1] """ - i = IntegerModRing(self.parent()._cartan_type.rank())(i) + i = self.parent()._Zn(i) p = self._sig(i)[1] M = self.value if p is None: @@ -362,18 +383,18 @@ def epsilon(self, i): - ``i`` -- an element of the index set -# EXAMPLES:: -# -# sage: B = crystals.infinity.Multisegments(2) -# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) -# sage: b.epsilon(0) -# 1 -# sage: b.epsilon(1) -# 0 -# sage: b.epsilon(2) -# 1 + EXAMPLES:: + + sage: B = crystals.infinity.Multisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b.epsilon(0) + 1 + sage: b.epsilon(1) + 0 + sage: b.epsilon(2) + 1 """ - i = IntegerModRing(self.parent()._cartan_type.rank())(i) + i = self.parent()._Zn(i) return self._sig(i)[2] def phi(self, i): @@ -391,15 +412,15 @@ def phi(self, i): EXAMPLES:: -# sage: B = crystals.infinity.Multisegments(2) -# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) -# sage: b.phi(0) -# 1 -# sage: b.phi(1) -# 0 -# sage: mg = B.module_generator() -# sage: mg.f(1).phi(0) -# -1 + sage: B = crystals.infinity.Multisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b.phi(0) + 1 + sage: b.phi(1) + 0 + sage: mg = B.highest_weight_vector() + sage: mg.f(1).phi(0) + -1 """ h = self.parent().weight_lattice_realization().simple_coroots() return self.epsilon(i) + self.weight().scalar(h[i]) @@ -410,13 +431,14 @@ def weight(self): EXAMPLES:: -# sage: B = crystals.infinity.Multisegments(2) -# sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) -# sage: b.weight() -# 4*delta + sage: B = crystals.infinity.Multisegments(2) + sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) + sage: b.weight() + 4*delta """ WLR = self.parent().weight_lattice_realization() alpha = WLR.simple_roots() n = self.parent()._cartan_type.rank() - return WLR.sum(alpha[j % n] for k,i in self.value for j in range(ZZ(i),ZZ(i)+k)) + return WLR.sum(alpha[j % n] for k,i in self.value + for j in range(ZZ(i),ZZ(i)+k)) From 3c82b4370366942e55885cdf88b504d4caaa6529 Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Tue, 18 Apr 2017 00:28:30 +0200 Subject: [PATCH 112/151] directly modify FJ --- src/sage/matrix/matrix2.pyx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index f7d32a17caf..0cd8952ffae 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -14982,9 +14982,6 @@ def _matrix_power_symbolic(A, n): # dimension of Jordan block Jk mk = Jk.ncols() - # f applied to the Jordan block Jk - FJ_k = matrix(SR, mk, mk) - # compute the first row of f(Jk) vk = [] for i in range(mk): @@ -14994,13 +14991,9 @@ def _matrix_power_symbolic(A, n): # corresponds to \frac{D^i(f)}{i!}, with f = x^n and D the differential operator wrt x vk += [(binomial(n, i) * Jk_ii**(n-i)).simplify_full()] - - # insert vk into each row (above the main diagonal) - for i in range(mk): - Jk_row_i = [SR.zero()]*i + vk[0:mk-i] - FJ_k.set_row(i, Jk_row_i) - FJ.set_block(k, k, FJ_k) + # insert vk into each row (above the main diagonal) + FJ.set_block(k, k, matrix(SR, [[SR.zero()]*i + vk[0:mk-i] for i in range(mk)])) # we change the entries of P and P^-1 into symbolic expressions if not got_SR: From aa69545229e8f7c57dc1e7ec6ee7a9450eaf962e Mon Sep 17 00:00:00 2001 From: Ben Salisbury Date: Mon, 17 Apr 2017 19:37:24 -0400 Subject: [PATCH 113/151] added negative sign to weight function on multisegments and extended the weight lattice for generalized young walls in highest_weight_crystals --- src/sage/combinat/crystals/highest_weight_crystals.py | 2 +- src/sage/combinat/crystals/multisegments.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/crystals/highest_weight_crystals.py b/src/sage/combinat/crystals/highest_weight_crystals.py index 82982ae5283..b6c74823063 100644 --- a/src/sage/combinat/crystals/highest_weight_crystals.py +++ b/src/sage/combinat/crystals/highest_weight_crystals.py @@ -211,7 +211,7 @@ def HighestWeightCrystal(dominant_weight, model=None): if cartan_type.type() != 'A': raise NotImplementedError("only for affine type A") # Make sure it's in the weight lattice - P = dominant_weight.parent().root_system.weight_lattice() + P = dominant_weight.parent().root_system.weight_lattice(extended=True) wt = P.sum_of_terms((i, c) for i,c in dominant_weight) return CrystalOfGeneralizedYoungWalls(cartan_type.rank()-1, wt) diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index 64c8d134dfd..ebdec232eab 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -420,7 +420,7 @@ def phi(self, i): 0 sage: mg = B.highest_weight_vector() sage: mg.f(1).phi(0) - -1 + 1 """ h = self.parent().weight_lattice_realization().simple_coroots() return self.epsilon(i) + self.weight().scalar(h[i]) @@ -434,11 +434,11 @@ def weight(self): sage: B = crystals.infinity.Multisegments(2) sage: b = B([(4,2), (3,0), (3,1), (1,1), (1,0)]) sage: b.weight() - 4*delta + -4*delta """ WLR = self.parent().weight_lattice_realization() alpha = WLR.simple_roots() n = self.parent()._cartan_type.rank() - return WLR.sum(alpha[j % n] for k,i in self.value + return WLR.sum(-1*alpha[j % n] for k,i in self.value for j in range(ZZ(i),ZZ(i)+k)) From 071abec9e2057a63a977c696d4bad1b77640d023 Mon Sep 17 00:00:00 2001 From: Ben Salisbury Date: Mon, 17 Apr 2017 19:41:29 -0400 Subject: [PATCH 114/151] removed B.S. from authorship, fixed date, and updated email --- src/sage/combinat/crystals/multisegments.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/crystals/multisegments.py b/src/sage/combinat/crystals/multisegments.py index ebdec232eab..f3eb20ed413 100644 --- a/src/sage/combinat/crystals/multisegments.py +++ b/src/sage/combinat/crystals/multisegments.py @@ -3,8 +3,7 @@ """ #***************************************************************************** -# Copyright (C) 2015 Ben Salisbury -# Travis Scrimshaw +# Copyright (C) 2017 Travis Scrimshaw # # 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 From 218e40639f27675b8b741c01f0d9e4940470f74f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Tue, 18 Apr 2017 08:19:03 +0200 Subject: [PATCH 115/151] 22744: merged polyomino classes into one --- src/sage/combinat/tiling.py | 323 +++++++++-------------------------- src/sage/games/quantumino.py | 12 +- 2 files changed, 85 insertions(+), 250 deletions(-) diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index a07f2a90145..0b6f8ff4982 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -94,8 +94,7 @@ sage: from sage.combinat.tiling import TilingSolver sage: T = TilingSolver([I,N,L,U,X,W,P,F,Z,T,Y,V], box=chessboard, reflection=True) sage: T - Tiling solver of 12 pieces into the box - Polyomino: [...], Color: gray + Tiling solver of 12 pieces into a box of size 60 Rotation allowed: True Reflection allowed: True Reusing pieces allowed: False @@ -474,44 +473,52 @@ def ncube_isometry_group_cosets(n, orientation_preserving=True): ############################## # Class Polyomino ############################## -class Polyomino_generic(SageObject): +class Polyomino(SageObject): r""" - A generic class for polyomino in `\ZZ^d`. + A polyomino in `\ZZ^d`. + + The polyomino is the union of the unit square (or cube, or n-cube) + centered at those coordinates. Such an object should be connected, but + the code does not make this assumption. INPUT: - - ``dimension`` -- integer + - ``coords`` -- iterable of integer coordinates in `\ZZ^d` - ``color`` -- string (default: ``'gray'``), color for display - The following methods are to be implemented in the herited classes: - - - ``__repr__`` - - ``frozenset`` - - By default the following methods depend on the previous, they may be - overwritten in the herited classes if something more efficient can be - done: + EXAMPLES:: - - ``__len__`` depends on ``frozenset`` - - ``sorted_list`` depends on ``frozenset`` - - ``__iter__`` depends on ``sorted_list`` - - ``bounding_box`` depends on ``__iter__`` + sage: from sage.combinat.tiling import Polyomino + sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') + Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue """ - def __init__(self, dimension, color='gray'): + def __init__(self, coords, color='gray'): r""" Constructor. - See :mod:`Polyomino_generic` for full documentation. + See :mod:`Polyomino` for full documentation. EXAMPLES:: - sage: from sage.combinat.tiling import Polyomino_generic - sage: p = Polyomino_generic(2, color='blue') - sage: p - + sage: from sage.combinat.tiling import Polyomino + sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') + Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue + + :: + + sage: from sage.combinat.tiling import Polyomino + sage: Polyomino([(0,0), (1,0), (2,0)]) + Polyomino: [(0, 0), (1, 0), (2, 0)], Color: gray """ from sage.modules.free_module import FreeModule from sage.rings.integer_ring import ZZ + + if not isinstance(coords, (tuple,list)): + coords = list(coords) + if not coords: + raise ValueError("Polyomino must be non empty") + + dimension = len(coords[0]) self._dimension = ZZ(dimension) self._free_module = FreeModule(ZZ, self._dimension) @@ -519,6 +526,26 @@ def __init__(self, dimension, color='gray'): raise TypeError("color = ({!r}) must be a string".format(color)) self._color = color + self._blocs = coords + self._blocs = map(self._free_module, self._blocs) + for b in self._blocs: + b.set_immutable() + self._blocs = frozenset(self._blocs) + + def __repr__(self): + r""" + String representation. + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='red') + Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: red + """ + s = "Polyomino: %s, " % sorted(self._blocs) + s += "Color: %s" % self._color + return s + def color(self): r""" Return the color of the polyomino. @@ -538,14 +565,12 @@ def frozenset(self): EXAMPLES:: - sage: from sage.combinat.tiling import Polyomino_generic - sage: p = Polyomino_generic(2, color='blue') + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='red') sage: p.frozenset() - Traceback (most recent call last): - ... - NotImplementedError + frozenset({(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)}) """ - raise NotImplementedError + return self._blocs @cached_method def sorted_list(self): @@ -635,7 +660,7 @@ def __eq__(self, other): sage: p == r False """ - return isinstance(other, Polyomino_generic) and self.frozenset() == other.frozenset() + return isinstance(other, Polyomino) and self.frozenset() == other.frozenset() def __ne__(self, other): r""" @@ -676,15 +701,15 @@ def __le__(self, other): EXAMPLES:: - sage: from sage.combinat.tiling import RectanglePolyomino, Polyomino + sage: from sage.combinat.tiling import Polyomino sage: p = Polyomino([(0,0)]) - sage: b = RectanglePolyomino([0,0], [2,3]) + sage: b = Polyomino([(0,0), (0,1), (1,1), (2,1)]) sage: p <= b True sage: b <= p False """ - return isinstance(other, Polyomino_generic) and self.frozenset() <= other.frozenset() + return isinstance(other, Polyomino) and self.frozenset() <= other.frozenset() def __ge__(self, other): r""" @@ -700,15 +725,15 @@ def __ge__(self, other): EXAMPLES:: - sage: from sage.combinat.tiling import RectanglePolyomino, Polyomino + sage: from sage.combinat.tiling import Polyomino sage: p = Polyomino([(0,0)]) - sage: b = RectanglePolyomino([0,0], [2,3]) + sage: b = Polyomino([(0,0), (0,1), (1,1), (2,1)]) sage: p >= b False sage: b >= p True """ - return isinstance(other, Polyomino_generic) and self.frozenset() >= other.frozenset() + return isinstance(other, Polyomino) and self.frozenset() >= other.frozenset() def __sub__(self, v): r""" @@ -938,10 +963,9 @@ def translated_copies(self, box): sage: list(p.translated_copies(box=(1,1,1))) [] - Using a RectanglePolyomino as input:: + Using a Polyomino as input:: - sage: from sage.combinat.tiling import RectanglePolyomino - sage: b = RectanglePolyomino([0,0], [2,3]) + sage: b = Polyomino([(0,0), (0,1), (0,2), (1,0), (1,1), (1,2)]) sage: p = Polyomino([(0,0)]) sage: list(p.translated_copies(b)) [Polyomino: [(0, 0)], Color: gray, @@ -960,9 +984,9 @@ def translated_copies(self, box): Polyomino: [(0, 1), (0, 2), (1, 1)], Color: gray, Polyomino: [(1, 0), (1, 1), (2, 0)], Color: gray] """ - if not isinstance(box, Polyomino_generic): - dim = len(box) - box = RectanglePolyomino([0]*dim, box) + if not isinstance(box, Polyomino): + ranges = [range(a) for a in box] + box = Polyomino(itertools.product(*ranges)) if not box._dimension == self._dimension: raise ValueError("Dimension of input box must match the " "dimension of the polyomino") @@ -1029,14 +1053,15 @@ def isometric_copies(self, box, orientation_preserving=True, Polyomino: [(0, 0), (0, 1), (1, 1)], Color: gray, Polyomino: [(0, 1), (1, 0), (1, 1)], Color: gray] """ - if not isinstance(box, Polyomino_generic): - dim = len(box) - box = RectanglePolyomino([0]*dim, box) + if not isinstance(box, Polyomino): + ranges = [range(a) for a in box] + box = Polyomino(itertools.product(*ranges)) if not box._dimension == self._dimension: raise ValueError("Dimension of input box must match the " "dimension of the polyomino") - if mod_box_isometries and len(set(b-a for (a,b) in zip(box._min_coords, - box._max_coords))) < box._dimension: + box_min_coords, box_max_coords = box.bounding_box() + if mod_box_isometries and len(set(b-a for (a,b) in zip(box_min_coords, + box_max_coords))) < box._dimension: raise NotImplementedError("The code below assumes that the" " sizes of the box (={}) are all distinct when" " argument `mod_box_isometries` is True.".format(box)) @@ -1231,189 +1256,6 @@ def show2d(self, size=0.7, color='black', thickness=1): canonical_orthogonals = deprecated_function_alias(19107, canonical_isometric_copies) translated = deprecated_function_alias(19107, translated_copies) translated_orthogonals = deprecated_function_alias(19107, isometric_copies) -class Polyomino(Polyomino_generic): - r""" - Return the polyomino defined by a set of coordinates. - - The polyomino is the union of the unit square (or cube, or n-cube) - centered at those coordinates. Such an object should be connected, but - the code does not make this assumption. - - INPUT: - - - ``coords`` - iterable of tuple - - ``color`` - string (optional, default: ``'gray'``), the color - - EXAMPLES:: - - sage: from sage.combinat.tiling import Polyomino - sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') - Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue - """ - def __init__(self, coords, color='gray'): - r""" - Constructor. - - See :mod:`Polyomino` for full documentation. - - EXAMPLES:: - - sage: from sage.combinat.tiling import Polyomino - sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') - Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue - - :: - - sage: from sage.combinat.tiling import Polyomino - sage: Polyomino([(0,0), (1,0), (2,0)]) - Polyomino: [(0, 0), (1, 0), (2, 0)], Color: gray - """ - if not isinstance(coords, (tuple,list)): - coords = list(coords) - if not coords: - raise ValueError("Polyomino must be non empty") - - dimension = len(coords[0]) - Polyomino_generic.__init__(self, dimension, color) - - self._blocs = coords - self._blocs = map(self._free_module, self._blocs) - for b in self._blocs: - b.set_immutable() - #self._blocs = tuple(sorted(set(self._blocs))) - self._blocs = frozenset(self._blocs) - - def __repr__(self): - r""" - String representation. - - EXAMPLES:: - - sage: from sage.combinat.tiling import Polyomino - sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='red') - Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: red - """ - s = "Polyomino: %s, " % sorted(self._blocs) - s += "Color: %s" % self._color - return s - - def frozenset(self): - r""" - Return the elements of `\ZZ^d` in the polyomino as a frozenset. - - EXAMPLES:: - - sage: from sage.combinat.tiling import Polyomino - sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='red') - sage: p.frozenset() - frozenset({(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)}) - """ - return self._blocs - -class RectanglePolyomino(Polyomino_generic): - r""" - Return the polyomino defined by a cartesian product of integer intervals. - - INPUT: - - - ``min_coords`` - iterable of integers, lower left corner - - ``max_coords`` - iterable of integers, upper right corner - - ``color`` - string (optional, default: ``'gray'``), the color - - EXAMPLES:: - - sage: from sage.combinat.tiling import RectanglePolyomino - sage: b = RectanglePolyomino([0,0], [2,3]) - sage: b - Rectangle Polyomino: [0,2[ x [0,3[, Color: gray - - :: - - sage: from sage.combinat.tiling import RectanglePolyomino - sage: box = RectanglePolyomino([-2,0,-3], [2,2,4]) - sage: box - Rectangle Polyomino: [-2,2[ x [0,2[ x [-3,4[, Color: gray - """ - def __init__(self, min_coords, max_coords, color='gray'): - r""" - Constructor. - - See :mod:`RectanglePolyomino` for full documentation. - - EXAMPLES:: - - sage: from sage.combinat.tiling import RectanglePolyomino - sage: b = RectanglePolyomino([0,0], [2,3]) - """ - self._min_coords = min_coords - self._max_coords = max_coords - dimension = len(min_coords) - Polyomino_generic.__init__(self, dimension, color) - - def __repr__(self): - r""" - String representation. - - EXAMPLES:: - - sage: from sage.combinat.tiling import RectanglePolyomino - sage: b = RectanglePolyomino([0,0], [2,3]) - sage: b - Rectangle Polyomino: [0,2[ x [0,3[, Color: gray - """ - b = ' x '.join("[{},{}[".format(a,b) for (a,b) in zip(self._min_coords, - self._max_coords)) - s = "Rectangle Polyomino: %s, " % b - s += "Color: %s" % self._color - return s - - def __len__(self): - r""" - Return the size of the polyomino, i.e. the number of n-dimensional - unit cubes. - - EXAMPLES:: - - sage: from sage.combinat.tiling import RectanglePolyomino - sage: b = RectanglePolyomino([0,0], [2,3]) - sage: len(b) - 6 - sage: b = RectanglePolyomino([0,0,0], [2,3,5]) - sage: len(b) - 30 - """ - from sage.misc.misc_c import prod - return prod(b-a for (a,b) in zip(self._min_coords, - self._max_coords)) - - def __iter__(self): - r""" - EXAMPLES:: - - sage: from sage.combinat.tiling import RectanglePolyomino - sage: b = RectanglePolyomino([0,0], [2,3]) - sage: list(b) - [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)] - """ - ranges = [range(a,b) for (a,b) in zip(self._min_coords, - self._max_coords)] - for v in itertools.product(*ranges): - v = self._free_module(v) - v.set_immutable() - yield v - - def frozenset(self): - r""" - Return the elements of `\ZZ^d` in the polyomino as a frozenset. - - EXAMPLES:: - - sage: from sage.combinat.tiling import RectanglePolyomino - sage: b = RectanglePolyomino([0,0], [2,3]) - sage: b.frozenset() - frozenset({(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)}) - """ - return frozenset(self) ####################### # General tiling solver @@ -1447,8 +1289,7 @@ class TilingSolver(SageObject): sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)]) sage: T = TilingSolver([p,q,r], box=(1,1,6)) sage: T - Tiling solver of 3 pieces into the box - Rectangle Polyomino: [0,1[ x [0,1[ x [0,6[, Color: gray + Tiling solver of 3 pieces into a box of size 6 Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False @@ -1496,19 +1337,18 @@ def __init__(self, pieces, box, rotation=True, sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)]) sage: T = TilingSolver([p,q,r], box=(1,1,6)) sage: T - Tiling solver of 3 pieces into the box - Rectangle Polyomino: [0,1[ x [0,1[ x [0,6[, Color: gray + Tiling solver of 3 pieces into a box of size 6 Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False """ self._pieces = pieces self._free_module = self._pieces[0]._free_module - if not isinstance(box, Polyomino_generic): - dim = len(box) - self._box = RectanglePolyomino([0]*dim, box) - else: + if isinstance(box, Polyomino): self._box = box + else: + ranges = [range(a) for a in box] + self._box = Polyomino(itertools.product(*ranges)) self._rotation = rotation self._reflection = reflection if not self._rotation and self._reflection: @@ -1527,15 +1367,14 @@ def __repr__(self): sage: q = Polyomino([(0,0,0), (0,0,1)]) sage: r = Polyomino([(0,0,0), (0,0,1), (0,0,2)]) sage: TilingSolver([p,q,r], box=(1,1,6)) - Tiling solver of 3 pieces into the box - Rectangle Polyomino: [0,1[ x [0,1[ x [0,6[, Color: gray + Tiling solver of 3 pieces into a box of size 6 Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False """ - s = "Tiling solver of %s pieces into the box\n" % len(self._pieces) - s += "%s\n" % self._box + s = "Tiling solver of %s pieces " % len(self._pieces) + s += "into a box of size %s\n" % len(self._box) s += "Rotation allowed: %s\n" % self._rotation s += "Reflection allowed: %s\n" % self._reflection s += "Reusing pieces allowed: %s" % self._reusable diff --git a/src/sage/games/quantumino.py b/src/sage/games/quantumino.py index 9ea4f59e947..01d79ada28c 100644 --- a/src/sage/games/quantumino.py +++ b/src/sage/games/quantumino.py @@ -141,8 +141,7 @@ sage: q = QuantuminoSolver(0) sage: T = q.tiling_solver() sage: T - Tiling solver of 16 pieces into the box - Rectangle Polyomino: [0,5[ x [0,8[ x [0,2[, Color: gray + Tiling solver of 16 pieces into a box of size 80 Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False @@ -453,20 +452,17 @@ def tiling_solver(self): sage: from sage.games.quantumino import QuantuminoSolver sage: QuantuminoSolver(0).tiling_solver() - Tiling solver of 16 pieces into the box - Rectangle Polyomino: [0,5[ x [0,8[ x [0,2[, Color: gray + Tiling solver of 16 pieces into a box of size 80 Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False sage: QuantuminoSolver(14).tiling_solver() - Tiling solver of 16 pieces into the box - Rectangle Polyomino: [0,5[ x [0,8[ x [0,2[, Color: gray + Tiling solver of 16 pieces into a box of size 80 Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False sage: QuantuminoSolver(14, box=(5,4,4)).tiling_solver() - Tiling solver of 16 pieces into the box - Rectangle Polyomino: [0,5[ x [0,4[ x [0,4[, Color: gray + Tiling solver of 16 pieces into a box of size 80 Rotation allowed: True Reflection allowed: False Reusing pieces allowed: False From 70462e26e4d02bba1bbe8c6264587b564314e417 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 18 Apr 2017 08:21:59 +0200 Subject: [PATCH 116/151] 22090: cosmetics --- src/sage/symbolic/expression.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 14146a5686e..a692a46afbd 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -6736,6 +6736,7 @@ cdef class Expression(CommutativeRingElement): Suppose ``f``=``self`` is a hypergeometric term such that: .. math:: + s_n = \sum_{k=0}^{n-1} f_k and `f_k` doesn't depend on `n`. Return a hypergeometric From ab020de1f84ff8150f1cfca322cdaebd230c7afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Tue, 18 Apr 2017 08:25:29 +0200 Subject: [PATCH 117/151] 22744: _repr_ and long time tag --- src/sage/combinat/tiling.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index 0b6f8ff4982..a137fe852e6 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -107,9 +107,9 @@ Showing one solution:: - sage: solution = next(T.solve()) # not tested - sage: G = sum([piece.show2d() for piece in solution], Graphics()) # not tested - sage: G.show(aspect_ratio=1, axes=False) # not tested + sage: solution = next(T.solve()) # long time + sage: G = sum([piece.show2d() for piece in solution], Graphics()) # long time + sage: G.show(aspect_ratio=1, axes=False) # long time 1d Easy Example --------------- @@ -532,7 +532,7 @@ def __init__(self, coords, color='gray'): b.set_immutable() self._blocs = frozenset(self._blocs) - def __repr__(self): + def _repr_(self): r""" String representation. @@ -1356,7 +1356,7 @@ def __init__(self, pieces, box, rotation=True, "rotation is not allowed") self._reusable = reusable - def __repr__(self): + def _repr_(self): r""" String representation From f11f773689833978ba9d964f5d4de9e295012555 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 18 Apr 2017 10:12:36 +0200 Subject: [PATCH 118/151] Run "check-install" (to check installed version of cysignals) --- build/pkgs/cysignals/spkg-check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/cysignals/spkg-check b/build/pkgs/cysignals/spkg-check index 452769ad968..6bfab19af4d 100755 --- a/build/pkgs/cysignals/spkg-check +++ b/build/pkgs/cysignals/spkg-check @@ -6,4 +6,4 @@ if [ -z "$SAGE_LOCAL" ]; then exit 1 fi -cd src && $MAKE check +cd src && $MAKE check-install From 6bed2907c081a43a2576c5ba956f74ad55618bf1 Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Tue, 18 Apr 2017 15:22:46 +0200 Subject: [PATCH 119/151] add see also block --- src/sage/symbolic/integration/integral.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index 960ba47b63a..da6f5112448 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -351,7 +351,13 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False): To prevent automatic evaluation use the ``hold`` argument. - EXAMPLES:: + .. SEEALSO:: + + To integrate a polynomial over a polytope, use the optional + ``latte_int`` package + :meth:`sage.geometry.polyhedron.base.Polyhedron_base.integrate`. + + EXAMPLES:: sage: x = var('x') sage: h = sin(x)/(cos(x))^2 From 339a49d64e447cbf00ae89ff736b1dfc3605d6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 18 Apr 2017 16:14:15 +0200 Subject: [PATCH 120/151] new version of conway polynomial package --- build/pkgs/conway_polynomials/checksums.ini | 6 +++--- build/pkgs/conway_polynomials/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/conway_polynomials/checksums.ini b/build/pkgs/conway_polynomials/checksums.ini index ce78541cb48..cedb5575263 100644 --- a/build/pkgs/conway_polynomials/checksums.ini +++ b/build/pkgs/conway_polynomials/checksums.ini @@ -1,4 +1,4 @@ tarball=conway_polynomials-VERSION.tar.bz2 -sha1=25b7abe5c935d20c3ebcde943308652c412d14dc -md5=17f061204b3a6133abb21e17b3abea19 -cksum=3204784255 +sha1=d4d89bda60ac54e73121f84635e774766e19a8b6 +md5=a2725ba21f44554196781424d957f68a +cksum=1387933493 diff --git a/build/pkgs/conway_polynomials/package-version.txt b/build/pkgs/conway_polynomials/package-version.txt index 304012ce339..2eb3c4fe4ee 100644 --- a/build/pkgs/conway_polynomials/package-version.txt +++ b/build/pkgs/conway_polynomials/package-version.txt @@ -1 +1 @@ -0.4.p1 +0.5 From f0e18290cc91018841820bb335ffa9b4d5f607e9 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 18 Apr 2017 16:38:33 +0100 Subject: [PATCH 121/151] fixed typos noticed by the reviewer --- src/sage/graphs/generators/classical_geometries.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index e18b4a599bf..3ed064ec943 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -1328,16 +1328,16 @@ def CossidentePenttilaGraph(q): def Nowhere0WordsTwoWeightCodeGraph(q, hyperoval=None, field=None, check_hyperoval=True): r""" - Return the subgraph of nowhere 0 words from two-weight code of projective plane hyperoval + Return the subgraph of nowhere 0 words from two-weight code of projective plane hyperoval. Let `q=2^k` and `\Pi=PG(2,q)`. Fix a `hyperoval `__ `O \subset \Pi`. Let `V=F_q^3` and `C` the two-weight 3-dimensional linear code over `F_q` with words `c(v)` obtained from `v\in V` by computing - ..math:: + .. MATH:: - c(v)=(,...,), o_j \in O. + c(v)=(\langle v,o_1 \rangle,...,\langle v,o_{q+2} \rangle), o_j \in O. `C` contains `q(q-1)^2/2` words without 0 entries. The subgraph of the strongly regular graph of `C` induced on the latter words is also strongly regular, From e90cb7dcecdab2322a7a01629368fc830285d4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 18 Apr 2017 20:20:28 +0200 Subject: [PATCH 122/151] trac 22762 adding total_ordering --- src/sage/quadratic_forms/binary_qf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py index 9fe876521d0..972786a0c7a 100644 --- a/src/sage/quadratic_forms/binary_qf.py +++ b/src/sage/quadratic_forms/binary_qf.py @@ -44,6 +44,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from functools import total_ordering from sage.libs.pari.all import pari from sage.rings.all import ZZ, is_fundamental_discriminant @@ -52,6 +53,7 @@ from sage.misc.cachefunc import cached_method +@total_ordering class BinaryQF(SageObject): """ A binary quadratic form over `\ZZ`. @@ -313,6 +315,8 @@ def __lt__(self, right): False sage: Q < P True + sage: Q <= P + True """ if not isinstance(right, BinaryQF): return False From 6c05f14f7ec23889f005782404b006b0788306c9 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 18 Apr 2017 22:03:11 +0200 Subject: [PATCH 123/151] Remove redundant V=1 options from build rules --- build/pkgs/curl/spkg-check | 2 +- build/pkgs/curl/spkg-install | 4 ++-- build/pkgs/pcre/spkg-check | 2 +- build/pkgs/pcre/spkg-install | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/pkgs/curl/spkg-check b/build/pkgs/curl/spkg-check index 5fedf7faa0f..35f04fc8862 100755 --- a/build/pkgs/curl/spkg-check +++ b/build/pkgs/curl/spkg-check @@ -1,4 +1,4 @@ #!/usr/bin/env bash cd src -$MAKE check V=1 +$MAKE check diff --git a/build/pkgs/curl/spkg-install b/build/pkgs/curl/spkg-install index 2b7912bc88a..a431d217601 100755 --- a/build/pkgs/curl/spkg-install +++ b/build/pkgs/curl/spkg-install @@ -29,13 +29,13 @@ if [ $? -ne 0 ]; then exit 1 fi -$MAKE V=1 +$MAKE if [ $? -ne 0 ]; then echo >&2 "Error building curl." exit 1 fi -$MAKE install V=1 +$MAKE install if [ $? -ne 0 ]; then echo >&2 "Error installing curl." exit 1 diff --git a/build/pkgs/pcre/spkg-check b/build/pkgs/pcre/spkg-check index 5fedf7faa0f..35f04fc8862 100755 --- a/build/pkgs/pcre/spkg-check +++ b/build/pkgs/pcre/spkg-check @@ -1,4 +1,4 @@ #!/usr/bin/env bash cd src -$MAKE check V=1 +$MAKE check diff --git a/build/pkgs/pcre/spkg-install b/build/pkgs/pcre/spkg-install index 29862b72be3..c37d40d693f 100755 --- a/build/pkgs/pcre/spkg-install +++ b/build/pkgs/pcre/spkg-install @@ -14,13 +14,13 @@ if [ $? -ne 0 ]; then exit 1 fi -$MAKE V=1 +$MAKE if [ $? -ne 0 ]; then echo >&2 "Error building pcre." exit 1 fi -$MAKE install V=1 +$MAKE install if [ $? -ne 0 ]; then echo >&2 "Error installing pcre." exit 1 From 1e2d3bbb9875dcefd003d5dfbc6e257bc7ca4e92 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 1 Apr 2017 11:59:39 -0700 Subject: [PATCH 124/151] New variable SAGE_SILENT_BUILD to implement V=0 --- build/bin/sage-logger | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/build/bin/sage-logger b/build/bin/sage-logger index fb344eb050d..d920ca72a33 100755 --- a/build/bin/sage-logger +++ b/build/bin/sage-logger @@ -60,14 +60,17 @@ else SED=cat fi -# Store the current value of V, but do all logging of child processes -# with V=1 to ensure that no information is lost. -currentV=$V -export V=1 - mkdir -p "$logdir" -if [[ "$currentV" = 0 && $use_prefix = true ]]; then +# Do all logging of child processes with V=1 to ensure that no +# information is lost. +export MAKEFLAGS="$MAKEFLAGS V=1" + +if [ "$V" = 0 ]; then + export SAGE_SILENT_BUILD=yes +fi + +if [ -n "$SAGE_SILENT_BUILD" -a ${use_prefix} = true ]; then # Silent build. # Similar to https://www.gnu.org/software/automake/manual/html_node/Automake-Silent-Rules.html#Automake-Silent-Rules echo "[$logname] installing. Log file: $logfile" From 91bf3bf53ac31e47241985b6ea4d03e583c7cf34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Tue, 18 Apr 2017 18:14:31 -0400 Subject: [PATCH 125/151] 22744: using latex in docstring --- src/sage/combinat/tiling.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index a137fe852e6..34979927624 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -79,7 +79,7 @@ sage: Y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='green') sage: V = Polyomino([(0,0),(0,1),(0,2),(1,0),(2,0)], color='blue') -A 8x8 chessboard leaving the center four squares vacant:: +A `8 \times 8` chessboard leaving the center four squares vacant:: sage: import itertools sage: s = set(itertools.product(range(8), repeat=2)) @@ -150,7 +150,7 @@ sage: L.append(Polyomino([(0,0),(0,1),(0,2),(1,0),(1,1),(1,2)],"pink")) By default, rotations are allowed and reflections are not. In this case, -there are no solution for tiling a 8x8 rectangular box:: +there are no solution for tiling a `8 \times 8` rectangular box:: sage: T = TilingSolver(L, box=(8,8)) sage: T.number_of_solutions() # long time (2.5 s) @@ -513,19 +513,18 @@ def __init__(self, coords, color='gray'): from sage.modules.free_module import FreeModule from sage.rings.integer_ring import ZZ + if not isinstance(color, str): + raise TypeError("color = ({!r}) must be a string".format(color)) + self._color = color + if not isinstance(coords, (tuple,list)): coords = list(coords) if not coords: raise ValueError("Polyomino must be non empty") - dimension = len(coords[0]) - self._dimension = ZZ(dimension) + self._dimension = ZZ(len(coords[0])) self._free_module = FreeModule(ZZ, self._dimension) - if not isinstance(color, str): - raise TypeError("color = ({!r}) must be a string".format(color)) - self._color = color - self._blocs = coords self._blocs = map(self._free_module, self._blocs) for b in self._blocs: @@ -1679,9 +1678,9 @@ def _rows_mod_box_isometries(self, i): True Now, a real use case. A solution of the game Quantumino is a tiling - of a 5x8x2 box. Since a 5x8x2 box has four orientation preserving - isometries, each solution up to rotation is counted four times by - this dancing links solver:: + of a `5 \times 8 \times 2` box. Since a `5 \times 8 \times 2` box + has four orientation preserving isometries, each solution up to + rotation is counted four times by this dancing links solver:: sage: from sage.games.quantumino import QuantuminoSolver sage: from sage.combinat.matrices.dancing_links import dlx_solver From 1d18a976daf3a1bba374cb69d08e44b390924620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Wed, 19 Apr 2017 12:13:50 +1200 Subject: [PATCH 126/151] Simplify spkg-install and make it always use a system (sage) provided curl --- build/pkgs/cmake/spkg-install | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/build/pkgs/cmake/spkg-install b/build/pkgs/cmake/spkg-install index d96256a7fd1..60d59360c74 100755 --- a/build/pkgs/cmake/spkg-install +++ b/build/pkgs/cmake/spkg-install @@ -2,17 +2,10 @@ cd src -system_curl="" - -if [ "$UNAME" = "Darwin" ]; then - system_curl="--system-curl" - unset MACOSX_DEPLOYMENT_TARGET -fi - ./bootstrap --prefix=$SAGE_LOCAL \ --system-bzip2 \ --system-zlib \ - $system_curl \ + --system-curl \ -- \ -DCMAKE_PREFIX_PATH=$SAGE_LOCAL \ -DCMAKE_TESTS_CDASH_SERVER=NOTFOUND From 8471784d3d245e41405cc03ffa235f9238f2bdb1 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Wed, 19 Apr 2017 04:31:39 +0200 Subject: [PATCH 127/151] Use xz/liblzma provided by Sage --- build/pkgs/cmake/SPKG.txt | 2 ++ build/pkgs/cmake/dependencies | 2 +- build/pkgs/cmake/spkg-install | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build/pkgs/cmake/SPKG.txt b/build/pkgs/cmake/SPKG.txt index e917e612238..57770de3604 100644 --- a/build/pkgs/cmake/SPKG.txt +++ b/build/pkgs/cmake/SPKG.txt @@ -28,3 +28,5 @@ CMake is distributed under the OSI-approved BSD 3-clause License. * curl * zlib * bzip2 +* xz + diff --git a/build/pkgs/cmake/dependencies b/build/pkgs/cmake/dependencies index 0c60285196e..8637994a7b7 100644 --- a/build/pkgs/cmake/dependencies +++ b/build/pkgs/cmake/dependencies @@ -1,4 +1,4 @@ -curl zlib bzip2 +curl zlib bzip2 xz ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/cmake/spkg-install b/build/pkgs/cmake/spkg-install index 60d59360c74..413effc9539 100755 --- a/build/pkgs/cmake/spkg-install +++ b/build/pkgs/cmake/spkg-install @@ -5,6 +5,7 @@ cd src ./bootstrap --prefix=$SAGE_LOCAL \ --system-bzip2 \ --system-zlib \ + --system-liblzma \ --system-curl \ -- \ -DCMAKE_PREFIX_PATH=$SAGE_LOCAL \ From 0b1961d997d28d78e62fb1fcb3dfed3a407dcca3 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 19 Apr 2017 11:58:16 +0200 Subject: [PATCH 128/151] Removed configure.ac changes entirely from patch applied when building in Sage This really ensures that the makefile won't try to regenerate the configure script. The original configure.ac changes can still be found in the upstream pull request. --- .../patches/disable-recursive-enum.patch | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/build/pkgs/libfplll/patches/disable-recursive-enum.patch b/build/pkgs/libfplll/patches/disable-recursive-enum.patch index 2dd60ad9211..c410db1d2e3 100644 --- a/build/pkgs/libfplll/patches/disable-recursive-enum.patch +++ b/build/pkgs/libfplll/patches/disable-recursive-enum.patch @@ -3,24 +3,6 @@ due to bugs in its binutils version). This patch adds a ./configure flag to make that easier. https://trac.sagemath.org/ticket/22800 -diff --git a/configure.ac b/configure.ac -index 401bbba..b367cb5 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -124,6 +124,13 @@ AC_ARG_WITH(max-enumeration-dimension, - - AC_DEFINE_UNQUOTED([FPLLL_MAX_ENUM_DIMENSION], $max_enumeration_dimension, [maximum supported enumeration dimension]) - -+AC_ARG_ENABLE(recursive-enum, -+ AS_HELP_STRING([--disable-recursive-enum], -+ [Disable recursive enumeration])) -+ -+AS_IF([test "x$enable_recursive_enum" != "xno"], [ -+ AC_DEFINE([FPLLL_WITH_RECURSIVE_ENUM], [1], [recursive enumeration enabled])]) -+ - # Store version numbers in header - - AC_DEFINE_UNQUOTED([FPLLL_MAJOR_VERSION],[$FPLLL_MAJOR_VERSION],[major version]) diff --git a/configure b/configure index ce0e7fd..f1eccd5 100755 --- a/configure From 3493eeb21e5d00394ae707e4005c46c50483456f Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 19 Apr 2017 12:00:31 +0200 Subject: [PATCH 129/151] Bump patch level --- build/pkgs/psutil/package-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/psutil/package-version.txt b/build/pkgs/psutil/package-version.txt index 91ff57278e3..def2490a89c 100644 --- a/build/pkgs/psutil/package-version.txt +++ b/build/pkgs/psutil/package-version.txt @@ -1 +1 @@ -5.2.0 +5.2.0.p0 From a7e6a24d3e120f6232ebffaea48e5727b45b2fe2 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 19 Apr 2017 12:25:34 +0200 Subject: [PATCH 130/151] Upgrade sagenb_export to version 3.2 --- build/pkgs/sagenb_export/checksums.ini | 6 +++--- build/pkgs/sagenb_export/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/sagenb_export/checksums.ini b/build/pkgs/sagenb_export/checksums.ini index 7ccedb06dc4..d85abd74c6a 100644 --- a/build/pkgs/sagenb_export/checksums.ini +++ b/build/pkgs/sagenb_export/checksums.ini @@ -1,4 +1,4 @@ tarball=sagenb_export-VERSION.tar.gz -sha1=b5441150dc47e80a2436a8402e84defc9a79fee5 -md5=bdfd92e3187e3b0f4debea6dc0f1b3d4 -cksum=1703654922 +sha1=e465fc45eea3b7b007c4f0706d9d81bb7d0ca5ff +md5=661bee6bdfb000e191c512c8efba6637 +cksum=1195180613 diff --git a/build/pkgs/sagenb_export/package-version.txt b/build/pkgs/sagenb_export/package-version.txt index 8c50098d8ae..a3ec5a4bd3d 100644 --- a/build/pkgs/sagenb_export/package-version.txt +++ b/build/pkgs/sagenb_export/package-version.txt @@ -1 +1 @@ -3.1 +3.2 From f26f64e6aecc9c86280164ba34e1bf02a4cb379b Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 14 Mar 2017 15:13:37 +0000 Subject: [PATCH 131/151] Initial attempt to add a test runner option to stop a test run as soon as a single failure is encountered. Doesn't seem to work yet though, at least not for parallelized test runs. --- src/bin/sage-runtests | 1 + src/sage/doctest/control.py | 1 + src/sage/doctest/forker.py | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 1a9840387d2..4e29a59b61f 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -38,6 +38,7 @@ if __name__ == "__main__": parser.add_option("--file-iterations", "--file_iterations", type=int, default=0, help="repeat each file this many times, stopping on the first failure") parser.add_option("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file") + parser.add_option("--fail-once", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception") parser.add_option("--force_lib", "--force-lib", action="store_true", default=False, help="do not import anything from the tested file(s)") parser.add_option("--abspath", action="store_true", default=False, help="print absolute paths rather than relative paths") parser.add_option("--verbose", action="store_true", default=False, help="print debugging output during the test") diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 741d94699db..f2a5daca403 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -87,6 +87,7 @@ def __init__(self, **kwds): self.global_iterations = 1 # sage-runtests default is 0 self.file_iterations = 1 # sage-runtests default is 0 self.initial = False + self.fail_once = False self.force_lib = False self.abspath = True # sage-runtests default is False self.verbose = False diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 49488c9c805..89da5594ebe 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -551,11 +551,15 @@ def _run(self, test, compileflags, out): if not quiet: self.report_failure(out, test, example, got, test.globs) failures += 1 + if self.options.fail_once: + break elif outcome is BOOM: if not quiet: self.report_unexpected_exception(out, test, example, exc_info) failures += 1 + if self.options.fail_once: + break else: assert False, ("unknown outcome", outcome) @@ -1424,6 +1428,8 @@ def serial_dispatch(self): output = outtmpfile.read() self.controller.reporter.report(source, False, 0, result, output) + if self.controller.options.fail_once and result[1].failures: + break def parallel_dispatch(self): """ From 2e9a80cc857e33069a642656c3138dbfa4e30058 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Wed, 19 Apr 2017 11:35:58 +0000 Subject: [PATCH 132/151] Make --fail-once work with parallel doctests. --- src/sage/doctest/forker.py | 88 ++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 89da5594ebe..ddaf533bd37 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -1432,7 +1432,7 @@ def serial_dispatch(self): break def parallel_dispatch(self): - """ + r""" Run the doctests from the controller's specified sources in parallel. This creates :class:`DocTestWorker` subprocesses, while the master @@ -1460,6 +1460,41 @@ def parallel_dispatch(self): [... tests, ... s] sage -t .../rings/big_oh.py [... tests, ... s] + + If the ``fail_once=True`` option is given, the results for a failing + module will be immediately printed and any other ongoing tests + canceled:: + + sage: test1 = os.path.join(SAGE_TMP, 'test1.py') + sage: test2 = os.path.join(SAGE_TMP, 'test2.py') + sage: with open(test1, 'w') as f: + ....: f.write('"""\nsage: import time; time.sleep(60)\n"""') + sage: with open(test2, 'w') as f: + ....: f.write('"""\nsage: True\nFalse\n"""') + sage: DC = DocTestController(DocTestDefaults(fail_once=True, + ....: nthreads=2), + ....: [test1, test2]) + sage: DC.expand_files_into_sources() + sage: DD = DocTestDispatcher(DC) + sage: DR = DocTestReporter(DC) + sage: DC.reporter = DR + sage: DC.dispatcher = DD + sage: DC.timer = Timer().start() + sage: DD.parallel_dispatch() + sage -t .../test2.py + ********************************************************************** + File ".../test2.py", line 2, in test2 + Failed example: + True + Expected: + False + Got: + True + ********************************************************************** + 1 item had failures: + 1 of 1 in test2 + [1 test, 1 failure, ... s] + Killing test .../test1.py """ opt = self.controller.options source_iter = iter(self.controller.sources) @@ -1565,25 +1600,41 @@ def sel_exit(): # Similarly, process finished workers. new_finished = [] + fail_immediately = False for w in finished: - if follow is not None and follow is not w: + if opt.fail_once and w.result[1].failures: + fail_immediately = True + + if (follow is not None and follow is not w and + not fail_immediately): # We are following a different worker, so - # we cannot report now + # we cannot report now (unless we have fail_once + # enabled, in which case we report the failure + # immediately even if we were not following this + # worker) new_finished.append(w) - else: - # Report the completion of this worker - log(w.messages, end="") - self.controller.reporter.report( - w.source, - w.killed, - w.exitcode, - w.result, - w.output, - pid=w.pid) - restart = True - follow = None - finished = new_finished + continue + + # Report the completion of this worker + log(w.messages, end="") + self.controller.reporter.report( + w.source, + w.killed, + w.exitcode, + w.result, + w.output, + pid=w.pid) + + if fail_immediately: + break + restart = True + follow = None + + if fail_immediately: + break + + finished = new_finished # Start new workers if possible while source_iter is not None and len(workers) < opt.nthreads: try: @@ -2140,7 +2191,10 @@ def __call__(self, options, outtmpfile=None, msgfile=None, result_queue=None): timer = Timer().start() for test in doctests: - runner.run(test) + result = runner.run(test) + if options.fail_once and result.failed: + break + runner.filename = file failed, tried = runner.summarize(options.verbose) timer.stop().annotate(runner) From c2bff84e640507306d52b108e3ee440eb555ddba Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 19 Apr 2017 14:54:21 +0200 Subject: [PATCH 133/151] Rename --fail-once to --exitfirst; minor fixes; add test --- src/bin/sage-runtests | 2 +- src/sage/doctest/control.py | 2 +- src/sage/doctest/forker.py | 54 ++++++++++++++++++------------------- src/sage/doctest/test.py | 22 +++++++++++++++ 4 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 4e29a59b61f..fdc0cdd0211 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -38,7 +38,7 @@ if __name__ == "__main__": parser.add_option("--file-iterations", "--file_iterations", type=int, default=0, help="repeat each file this many times, stopping on the first failure") parser.add_option("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file") - parser.add_option("--fail-once", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception") + parser.add_option("--exitfirst", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception") parser.add_option("--force_lib", "--force-lib", action="store_true", default=False, help="do not import anything from the tested file(s)") parser.add_option("--abspath", action="store_true", default=False, help="print absolute paths rather than relative paths") parser.add_option("--verbose", action="store_true", default=False, help="print debugging output during the test") diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index f2a5daca403..bd2e65aec9e 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -87,7 +87,7 @@ def __init__(self, **kwds): self.global_iterations = 1 # sage-runtests default is 0 self.file_iterations = 1 # sage-runtests default is 0 self.initial = False - self.fail_once = False + self.exitfirst = False self.force_lib = False self.abspath = True # sage-runtests default is False self.verbose = False diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index ddaf533bd37..1845bc6fe3a 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -439,6 +439,7 @@ def _run(self, test, compileflags, out): # Keep track of the number of failures and tries. failures = tries = 0 + quiet = False # Save the option flags (since option directives can be used # to modify them). @@ -450,11 +451,16 @@ def _run(self, test, compileflags, out): # Process each example. for examplenum, example in enumerate(test.examples): + if failures: + # If exitfirst is set, abort immediately after a + # failure. + if self.options.exitfirst: + break - # If REPORT_ONLY_FIRST_FAILURE is set, then suppress - # reporting after the first failure. - quiet = (self.optionflags & doctest.REPORT_ONLY_FIRST_FAILURE and - failures > 0) + # If REPORT_ONLY_FIRST_FAILURE is set, then suppress + # reporting after the first failure (but continue + # running the tests). + quiet |= (self.optionflags & doctest.REPORT_ONLY_FIRST_FAILURE) # Merge in the example's options. self.optionflags = original_optionflags @@ -551,15 +557,11 @@ def _run(self, test, compileflags, out): if not quiet: self.report_failure(out, test, example, got, test.globs) failures += 1 - if self.options.fail_once: - break elif outcome is BOOM: if not quiet: self.report_unexpected_exception(out, test, example, exc_info) failures += 1 - if self.options.fail_once: - break else: assert False, ("unknown outcome", outcome) @@ -1428,7 +1430,7 @@ def serial_dispatch(self): output = outtmpfile.read() self.controller.reporter.report(source, False, 0, result, output) - if self.controller.options.fail_once and result[1].failures: + if self.controller.options.exitfirst and result[1].failures: break def parallel_dispatch(self): @@ -1461,17 +1463,17 @@ def parallel_dispatch(self): sage -t .../rings/big_oh.py [... tests, ... s] - If the ``fail_once=True`` option is given, the results for a failing + If the ``exitfirst=True`` option is given, the results for a failing module will be immediately printed and any other ongoing tests canceled:: sage: test1 = os.path.join(SAGE_TMP, 'test1.py') sage: test2 = os.path.join(SAGE_TMP, 'test2.py') sage: with open(test1, 'w') as f: - ....: f.write('"""\nsage: import time; time.sleep(60)\n"""') + ....: f.write("'''\nsage: import time; time.sleep(60)\n'''") sage: with open(test2, 'w') as f: - ....: f.write('"""\nsage: True\nFalse\n"""') - sage: DC = DocTestController(DocTestDefaults(fail_once=True, + ....: f.write("'''\nsage: True\nFalse\n'''") + sage: DC = DocTestController(DocTestDefaults(exitfirst=True, ....: nthreads=2), ....: [test1, test2]) sage: DC.expand_files_into_sources() @@ -1521,6 +1523,9 @@ def parallel_dispatch(self): # whose results have not been reported yet. finished = [] + # If exitfirst is set and we got a failure. + abort_now = False + # One particular worker that we are "following": we report the # messages while it's running. For other workers, we report the # messages if there is no followed worker. @@ -1602,16 +1607,11 @@ def sel_exit(): new_finished = [] fail_immediately = False for w in finished: - if opt.fail_once and w.result[1].failures: - fail_immediately = True - - if (follow is not None and follow is not w and - not fail_immediately): + if opt.exitfirst and w.result[1].failures: + abort_now = True + elif follow is not None and follow is not w: # We are following a different worker, so - # we cannot report now (unless we have fail_once - # enabled, in which case we report the failure - # immediately even if we were not following this - # worker) + # we cannot report now. new_finished.append(w) continue @@ -1625,16 +1625,14 @@ def sel_exit(): w.output, pid=w.pid) - if fail_immediately: - break - restart = True follow = None - if fail_immediately: + finished = new_finished + + if abort_now: break - finished = new_finished # Start new workers if possible while source_iter is not None and len(workers) < opt.nthreads: try: @@ -2192,7 +2190,7 @@ def __call__(self, options, outtmpfile=None, msgfile=None, result_queue=None): for test in doctests: result = runner.run(test) - if options.fail_once and result.failed: + if options.exitfirst and result.failed: break runner.filename = file diff --git a/src/sage/doctest/test.py b/src/sage/doctest/test.py index a6cc4d0c657..26261f31b7a 100644 --- a/src/sage/doctest/test.py +++ b/src/sage/doctest/test.py @@ -134,6 +134,28 @@ ... 1 +Test the ``--exitfirst`` option:: + + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--exitfirst", "initial.rst"], **kwds) # long time + Running doctests... + Doctesting 1 file. + sage -t --warn-long 0.0 initial.rst + ********************************************************************** + File "initial.rst", line 4, in sage.doctest.tests.initial + Failed example: + a = binomiak(10,5) # random to test that we still get the exception + Exception raised: + Traceback (most recent call last): + ... + NameError: name 'binomiak' is not defined + ********************************************************************** + ... + ---------------------------------------------------------------------- + sage -t --warn-long 0.0 initial.rst # 1 doctest failed + ---------------------------------------------------------------------- + ... + 1 + Test a timeout using the ``SAGE_TIMEOUT`` environment variable:: sage: from copy import deepcopy From 8597c98fcf5866ad8c1759cfee296cab19b2328c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Wed, 19 Apr 2017 19:38:55 +0300 Subject: [PATCH 134/151] for-loop to any-clause. --- src/sage/combinat/posets/hasse_diagram.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index ad7d7c85ce3..3265c5ea6cc 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -2861,9 +2861,8 @@ def is_congruence_normal(self): for mi in range(n): if self.out_degree(mi) == 1: cong = SetPartition(self.congruence([[mi, next(self.neighbor_out_iterator(mi))]])) - for ji in congs_ji[cong]: - if self.is_lequal(ji, mi): - return False + if any(self.is_lequal(ji, mi) for ji in congs_ji[cong]): + return False return True From 1b21e8630fc92d114e830bd3cfefd9251acd568b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 20 Apr 2017 11:25:19 +1200 Subject: [PATCH 135/151] Minimal change to Makefile --- build/pkgs/gfan/patches/Makefile.patch | 37 ++++++++++++++------------ 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/build/pkgs/gfan/patches/Makefile.patch b/build/pkgs/gfan/patches/Makefile.patch index 1c51753d7a5..648fa569028 100644 --- a/build/pkgs/gfan/patches/Makefile.patch +++ b/build/pkgs/gfan/patches/Makefile.patch @@ -1,23 +1,26 @@ diff --git a/Makefile b/Makefile -index 0892969..f816b22 100644 +index 0892969..c626eb9 100644 --- a/Makefile +++ b/Makefile -@@ -33,13 +33,8 @@ GMP_LINKOPTIONS = $(gmppath)/lib/libgmp.a - GMP_INCLUDEOPTIONS = -I $(gmppath)/include +@@ -26,7 +26,7 @@ sage_link.so: sage_link.pyx setup.py endif --ifeq ($(cddpath),) + ifeq ($(gmppath),) +-GMP_LINKOPTIONS = -lgmp ++GMP_LINKOPTIONS = -lgmp -lm + GMP_INCLUDEOPTIONS = + else + GMP_LINKOPTIONS = $(gmppath)/lib/libgmp.a +@@ -34,7 +34,7 @@ GMP_INCLUDEOPTIONS = -I $(gmppath)/include + endif + + ifeq ($(cddpath),) -CDD_LINKOPTIONS = -L/usr/local -lcddgmp -+CDD_LINKOPTIONS = -L$SAGE_LOCAL/lib -lcddgmp -lgmp -lm ++CDD_LINKOPTIONS = -lcddgmp CDD_INCLUDEOPTIONS = --else --CDD_LINKOPTIONS = $(cddpath)/lib/libcddgmp.a --CDD_INCLUDEOPTIONS = -I $(cddpath)/include --endif - - ifeq ($(soplex),) - SOPLEX_PATH = -@@ -75,18 +70,10 @@ ADDITIONALINCLUDEOPTIONS = $(CDD_INCLUDEOPTIONS) $(GMP_INCLUDEOPTIONS) $(SOPLEX_ + else + CDD_LINKOPTIONS = $(cddpath)/lib/libcddgmp.a +@@ -75,18 +75,11 @@ ADDITIONALINCLUDEOPTIONS = $(CDD_INCLUDEOPTIONS) $(GMP_INCLUDEOPTIONS) $(SOPLEX_ MKDIR=mkdir -p @@ -27,7 +30,7 @@ index 0892969..f816b22 100644 -CLINKER = $(CC) -CXX = g++ -CCLINKER = $(CXX) --#OPTFLAGS = -O2 -DGMPRATIONAL -DNDEBUG + #OPTFLAGS = -O2 -DGMPRATIONAL -DNDEBUG OPTFLAGS = -O2 -DGMPRATIONAL -Wuninitialized -CFLAGS = $(OPTFLAGS) $(GPROFFLAG) $(ADDITIONALINCLUDEOPTIONS) #-pedantic @@ -38,7 +41,7 @@ index 0892969..f816b22 100644 CATSOBJECTS = $(GCATSPATH)lp_cdd.o \ $(SOPLEX_OBJECTS) \ -@@ -317,7 +304,7 @@ $(PREFIX): +@@ -317,7 +310,7 @@ $(PREFIX): default: $(OBJECTS) $(ADDITIONALOBJECTS) $(EXECS) $(MAIN): $(OBJECTS) @@ -47,7 +50,7 @@ index 0892969..f816b22 100644 release: rm -f -r $(RELEASEDIR)/* -@@ -431,11 +418,11 @@ library: +@@ -431,11 +424,11 @@ library: cp gfanlib_* gfanlib/ tar zcf - gfanlib > gfanlib.tar.gz .c.o: @@ -57,7 +60,7 @@ index 0892969..f816b22 100644 $(CXX) -c $< .cpp.o: - $(CXX) $(CFLAGS) -c $< -+ $(CXX) $(CXXFLAGS_LOCAL) $(CPPFLAGS) -c $< ++ $(CXX) $(CFLAGS_LOCAL) $(CPPFLAGS) -c $< .C.o: $(CXX) -c $< # wget http://ftp.sunet.se/pub/gnu/gmp/gmp-4.2.2.tar.gz From 6cf97b072cc02a17963f365007c89f85da99ccb7 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 21 Mar 2017 17:05:45 +0100 Subject: [PATCH 136/151] Add %%fortran cell magic --- src/sage/misc/inline_fortran.py | 9 ++--- src/sage/repl/ipython_extension.py | 56 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/sage/misc/inline_fortran.py b/src/sage/misc/inline_fortran.py index 39c82baa6da..2701a6f0533 100644 --- a/src/sage/misc/inline_fortran.py +++ b/src/sage/misc/inline_fortran.py @@ -12,10 +12,8 @@ class InlineFortran: def __init__(self, globals=None): - if globals is None: - self.globs = {} - else: - self.globs = globals # Deprecated + # globals=None means: use user globals from REPL + self.globs = globals self.library_paths=[] self.libraries=[] self.verbose = False @@ -81,6 +79,9 @@ def eval(self, x, globals=None, locals=None): """ if globals is None: globals = self.globs + if globals is None: + from sage.repl.user_globals import get_globals + globals = get_globals() from numpy import f2py diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 5888f773ba5..a6ee14d19c6 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -17,6 +17,8 @@ - ``%%cython`` + - ``%%fortran`` + * preparsing of input * loading Sage library @@ -355,6 +357,60 @@ def cython(self, line, cell): from sage.misc.cython_c import cython_compile return cython_compile(cell) + @cell_magic + def fortran(self, line, cell): + """ + Fortran cell magic. + + This is syntactic sugar on the + :func:`~sage.misc.inline_fortran.fortran` function. + + INPUT: + + - ``line`` -- ignored. + + - ``cell`` -- string. The Cython source code to process. + + OUTPUT: + + None. The Fortran code is compiled and loaded. + + EXAMPLES:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell(''' + ....: %%fortran + ....: C FILE: FIB1.F + ....: SUBROUTINE FIB(A,N) + ....: C + ....: C CALCULATE FIRST N FIBONACCI NUMBERS + ....: C + ....: INTEGER N + ....: REAL*8 A(N) + ....: DO I=1,N + ....: IF (I.EQ.1) THEN + ....: A(I) = 0.0D0 + ....: ELSEIF (I.EQ.2) THEN + ....: A(I) = 1.0D0 + ....: ELSE + ....: A(I) = A(I-1) + A(I-2) + ....: ENDIF + ....: ENDDO + ....: END + ....: C END FILE FIB1.F + ....: ''') + sage: fib + + sage: from numpy import array + sage: a = array(range(10), dtype=float) + sage: fib(a, 10) + sage: a + array([ 0., 1., 1., 2., 3., 5., 8., 13., 21., 34.]) + """ + from sage.misc.inline_fortran import fortran + return fortran(cell) + class SageCustomizations(object): From c6a5b40146506795873a1cb7b64e645258251a90 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 20 Apr 2017 11:33:57 +0200 Subject: [PATCH 137/151] Remove forgotten line --- src/sage/doctest/forker.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 1845bc6fe3a..1ebe5c9f495 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -1605,7 +1605,6 @@ def sel_exit(): # Similarly, process finished workers. new_finished = [] - fail_immediately = False for w in finished: if opt.exitfirst and w.result[1].failures: abort_now = True From 613c825abc213c3138281fd1d7e94292af22690c Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 20 Apr 2017 12:39:49 +0100 Subject: [PATCH 138/151] bumped up the version --- build/pkgs/gfan/package-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/gfan/package-version.txt b/build/pkgs/gfan/package-version.txt index b29450b8d51..a035fd459cb 100644 --- a/build/pkgs/gfan/package-version.txt +++ b/build/pkgs/gfan/package-version.txt @@ -1 +1 @@ -0.5.p1 +0.5.p2 From a55e73411211516d6ab69cb8ba0edb694c597688 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 20 Apr 2017 14:49:35 +0200 Subject: [PATCH 139/151] This is fixed, for now, in sage by https://trac.sagemath.org/ticket/21399 so we can live without this patch. --- .../3.2.6-ctypes-util-find_library.patch | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 build/pkgs/python3/patches/3.2.6-ctypes-util-find_library.patch diff --git a/build/pkgs/python3/patches/3.2.6-ctypes-util-find_library.patch b/build/pkgs/python3/patches/3.2.6-ctypes-util-find_library.patch deleted file mode 100644 index ffe15a218ae..00000000000 --- a/build/pkgs/python3/patches/3.2.6-ctypes-util-find_library.patch +++ /dev/null @@ -1,34 +0,0 @@ ---- a/Lib/ctypes/util.py -+++ b/Lib/ctypes/util.py -@@ -82,6 +82,20 @@ if os.name == "posix" and sys.platform == "darwin": - continue - return None - -+elif sys.platform == "cygwin": -+ def find_library(name): -+ for libdir in ['/usr/lib', '/usr/local/lib']: -+ for libext in ['lib%s.dll.a' % name, 'lib%s.a' % name]: -+ implib = os.path.join(libdir, libext) -+ if not os.path.exists(implib): -+ continue -+ cmd = "dlltool -I " + implib + " 2>/dev/null" -+ res = os.popen(cmd).read().replace("\n","") -+ if not res: -+ continue -+ return res -+ return None -+ - elif os.name == "posix": - # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump - import re, tempfile, errno -@@ -224,6 +238,10 @@ def test(): - print(cdll.LoadLibrary("libcrypto.dylib")) - print(cdll.LoadLibrary("libSystem.dylib")) - print(cdll.LoadLibrary("System.framework/System")) -+ elif sys.platform == "cygwin": -+ print cdll.LoadLibrary("cygbz2-1.dll") -+ print find_library("crypt") -+ print cdll.LoadLibrary("cygcrypt-0.dll") - else: - print(cdll.LoadLibrary("libm.so")) - print(cdll.LoadLibrary("libcrypt.so")) From 90e46fd1f8769a256cd122c5972a60dabe7b047c Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 20 Apr 2017 14:51:38 +0200 Subject: [PATCH 140/151] This patch has been part of the Cygwin port of Python since Python 2.6, but it has not been needed in Sage. I think whatever use case it was needed for is pretty obscure actually, and might only be to get the Python tests to pass. I can't find any code in Sage that relies on this. --- .../patches/3.2.6-export-PySignal_SetWakeupFd.patch | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 build/pkgs/python3/patches/3.2.6-export-PySignal_SetWakeupFd.patch diff --git a/build/pkgs/python3/patches/3.2.6-export-PySignal_SetWakeupFd.patch b/build/pkgs/python3/patches/3.2.6-export-PySignal_SetWakeupFd.patch deleted file mode 100644 index f98f2440a94..00000000000 --- a/build/pkgs/python3/patches/3.2.6-export-PySignal_SetWakeupFd.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/Include/pyerrors.h -+++ b/Include/pyerrors.h -@@ -244,7 +244,7 @@ PyAPI_FUNC(void) PyErr_SetInterrupt(void); - - /* In signalmodule.c */ - #ifndef Py_LIMITED_API --int PySignal_SetWakeupFd(int fd); -+PyAPI_FUNC(int) PySignal_SetWakeupFd(int fd); - #endif - - /* Support for adding program text to SyntaxErrors */ From d6b0c9656d9700f4f1ed56a03d026147d5f0d432 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 20 Apr 2017 13:57:48 +0100 Subject: [PATCH 141/151] make tachyon properly respect LDFLAGS --- build/pkgs/tachyon/package-version.txt | 2 +- build/pkgs/tachyon/patches/Make-config.patch | 2 +- build/pkgs/tachyon/patches/Makefile.patch | 46 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 build/pkgs/tachyon/patches/Makefile.patch diff --git a/build/pkgs/tachyon/package-version.txt b/build/pkgs/tachyon/package-version.txt index d2aaa0c9b5f..a6439c5ae1c 100644 --- a/build/pkgs/tachyon/package-version.txt +++ b/build/pkgs/tachyon/package-version.txt @@ -1 +1 @@ -0.98.9.p5 +0.98.9.p6 diff --git a/build/pkgs/tachyon/patches/Make-config.patch b/build/pkgs/tachyon/patches/Make-config.patch index 7e4bfc156b3..21cf0c7c6b9 100644 --- a/build/pkgs/tachyon/patches/Make-config.patch +++ b/build/pkgs/tachyon/patches/Make-config.patch @@ -71,7 +71,7 @@ -PNGLIB= +USEPNG= -DUSEPNG +PNGINC= -I$(SAGE_LOCAL)/include -+PNGLIB= -L$(SAGE_LOCAL)/lib -lpng12 -lz $(LDFLAGS) ++PNGLIB= -L$(SAGE_LOCAL)/lib -lpng12 -lz -# Uncomment the following lines to enable PNG support -#USEPNG= -DUSEPNG diff --git a/build/pkgs/tachyon/patches/Makefile.patch b/build/pkgs/tachyon/patches/Makefile.patch new file mode 100644 index 00000000000..08bb0ced8a0 --- /dev/null +++ b/build/pkgs/tachyon/patches/Makefile.patch @@ -0,0 +1,46 @@ +--- a/unix/Makefile ++++ b/unix/Makefile +@@ -121,35 +121,35 @@ ${MGFLIB} : ../libmgf/Makefile + }; + + ${ARCHDIR}/tachyon : ${RAYLIB} ${PARSELIB} ${OBJDIR}/main.o ${OBJDIR}/getargs.o ${OBJDIR}/parse.o ${OBJDIR}/nffparse.o ${OBJDIR}/glwin.o ${OBJDIR}/spaceball.o ${OBJDIR}/trackball.o ${PARSEOBJS} +- ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/tachyon ${OBJDIR}/main.o ${OBJDIR}/getargs.o ${OBJDIR}/parse.o ${OBJDIR}/nffparse.o ${OBJDIR}/glwin.o ${OBJDIR}/spaceball.o ${OBJDIR}/trackball.o ${PARSEOBJS} -L${RAYLIBDIR} ${PARSELIBS} ${LIBS} ++ ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/tachyon ${LDFLAGS} ${OBJDIR}/main.o ${OBJDIR}/getargs.o ${OBJDIR}/parse.o ${OBJDIR}/nffparse.o ${OBJDIR}/glwin.o ${OBJDIR}/spaceball.o ${OBJDIR}/trackball.o ${PARSEOBJS} -L${RAYLIBDIR} ${PARSELIBS} ${LIBS} + ${STRIP} ${ARCHDIR}/tachyon + + ${ARCHDIR}/animray : ${RAYLIB} ${OBJDIR}/mainanim.o +- ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/animray ${OBJDIR}/mainanim.o -L${RAYLIBDIR} ${LIBS} ++ ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/animray ${LDFLAGS} ${OBJDIR}/mainanim.o -L${RAYLIBDIR} ${LIBS} + ${STRIP} ${ARCHDIR}/animray + + ${ARCHDIR}/animspheres : ${RAYLIB} ${OBJDIR}/animspheres.o +- ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/animspheres ${OBJDIR}/animspheres.o -L${RAYLIBDIR} ${LIBS} ++ ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/animspheres ${LDFLAGS} ${OBJDIR}/animspheres.o -L${RAYLIBDIR} ${LIBS} + ${STRIP} ${ARCHDIR}/animspheres + + ${ARCHDIR}/animspheres2 : ${RAYLIB} ${OBJDIR}/animspheres2.o +- ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/animspheres2 ${OBJDIR}/animspheres2.o -L${RAYLIBDIR} ${LIBS} ++ ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/animspheres2 ${LDFLAGS} ${OBJDIR}/animspheres2.o -L${RAYLIBDIR} ${LIBS} + ${STRIP} ${ARCHDIR}/animspheres2 + + ${ARCHDIR}/hypertex : ${RAYLIB} ${OBJDIR}/hypertex.o ${DEMOSRC}/hypertex.c +- ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/hypertex ${OBJDIR}/hypertex.o -L${RAYLIBDIR} ${LIBS} ++ ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/hypertex ${LDFLAGS} ${OBJDIR}/hypertex.o -L${RAYLIBDIR} ${LIBS} + ${STRIP} ${ARCHDIR}/hypertex + + ${ARCHDIR}/fire : ${RAYLIB} ${OBJDIR}/fire.o ${DEMOSRC}/fire.c +- ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/fire ${OBJDIR}/fire.o -L${RAYLIBDIR} ${LIBS} ++ ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/fire ${LDFLAGS} ${OBJDIR}/fire.o -L${RAYLIBDIR} ${LIBS} + ${STRIP} ${ARCHDIR}/fire + + ${ARCHDIR}/animskull : ${RAYLIB} ${OBJDIR}/animskull.o +- ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/animskull ${OBJDIR}/animskull.o -L${RAYLIBDIR} ${LIBS} ++ ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/animskull ${LDFLAGS} ${OBJDIR}/animskull.o -L${RAYLIBDIR} ${LIBS} + ${STRIP} ${ARCHDIR}/animskull + + ${ARCHDIR}/tgatoyuv : ${RAYLIB} ${DEMOSRC}/tgatoyuv.c +- ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/tgatoyuv ${DEMOSRC}/tgatoyuv.c -L${RAYLIBDIR} ${LIBS} ++ ${CC} ${CFLAGS} ${DEMOINC} -o ${ARCHDIR}/tgatoyuv ${LDFLAGS} ${DEMOSRC}/tgatoyuv.c -L${RAYLIBDIR} ${LIBS} + ${STRIP} ${ARCHDIR}/tgatoyuv + + # From cd51023705656562733d4330264eab6e8d82a9f3 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 20 Apr 2017 15:16:50 +0200 Subject: [PATCH 142/151] This patch seems to have been part of Cygwin's Python for a long time, but I'm not actually sure what case it's for and can't find anything referencing it. The dbm module seems to build fine without it. It's possible there's just a test in Python's test suite that fails without it, but Python's test suite isn't fully working under Cygwin anyways. --- build/pkgs/python3/patches/3.3.6-dbm.patch | 29 ---------------------- 1 file changed, 29 deletions(-) delete mode 100644 build/pkgs/python3/patches/3.3.6-dbm.patch diff --git a/build/pkgs/python3/patches/3.3.6-dbm.patch b/build/pkgs/python3/patches/3.3.6-dbm.patch deleted file mode 100644 index 234966b6e02..00000000000 --- a/build/pkgs/python3/patches/3.3.6-dbm.patch +++ /dev/null @@ -1,29 +0,0 @@ -diff --git a/setup.py b/setup.py -index d594e02..e34994d 100644 ---- a/setup.py -+++ b/setup.py -@@ -1136,7 +1136,7 @@ class PyBuildExt(build_ext): - dbm_setup_debug = False # verbose debug prints from this script? - dbm_order = ['gdbm'] - # The standard Unix dbm module: -- if host_platform not in ['cygwin']: -+ if host_platform not in ['win32']: - config_args = [arg.strip("'") - for arg in sysconfig.get_config_var("CONFIG_ARGS").split()] - dbm_args = [arg for arg in config_args -@@ -1191,6 +1191,15 @@ class PyBuildExt(build_ext): - ], - libraries = gdbm_libs) - break -+ if find_file("ndbm.h", inc_dirs, []) is not None: -+ print("building dbm using gdbm") -+ dbmext = Extension( -+ '_dbm', ['_dbmmodule.c'], -+ define_macros=[ -+ ('HAVE_NDBM_H', None), -+ ], -+ libraries = gdbm_libs) -+ break - elif cand == "bdb": - if db_incs is not None: - if dbm_setup_debug: print("building dbm using bdb") From eb020040775c73abdfd94906a6c63d41c82c9fa7 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 20 Apr 2017 15:32:00 +0200 Subject: [PATCH 143/151] This patch was needed for the tkinter module to build. But we don't guarantee tkinter as part of Sage anyways so maybe it can be ommitted for now (?) --- .../python3/patches/3.3.6-tkinter-x11.patch | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 build/pkgs/python3/patches/3.3.6-tkinter-x11.patch diff --git a/build/pkgs/python3/patches/3.3.6-tkinter-x11.patch b/build/pkgs/python3/patches/3.3.6-tkinter-x11.patch deleted file mode 100644 index 2cc3470078c..00000000000 --- a/build/pkgs/python3/patches/3.3.6-tkinter-x11.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/setup.py b/setup.py -index 3373dd6..9d1844b 100644 ---- a/setup.py -+++ b/setup.py -@@ -1747,12 +1747,6 @@ class PyBuildExt(build_ext): - include_dirs.append('/usr/X11/include') - added_lib_dirs.append('/usr/X11/lib') - -- # If Cygwin, then verify that X is installed before proceeding -- if host_platform == 'cygwin': -- x11_inc = find_file('X11/Xlib.h', [], include_dirs) -- if x11_inc is None: -- return -- - # Check for BLT extension - if self.compiler.find_library_file(lib_dirs + added_lib_dirs, - 'BLT8.0'): -@@ -1770,9 +1764,7 @@ class PyBuildExt(build_ext): - if host_platform in ['aix3', 'aix4']: - libs.append('ld') - -- # Finally, link with the X11 libraries (not appropriate on cygwin) -- if host_platform != "cygwin": -- libs.append('X11') -+ libs.append('X11') - - ext = Extension('_tkinter', ['_tkinter.c', 'tkappinit.c'], - define_macros=[('WITH_APPINIT', 1)] + defs, From 46df7dca8f45df271960ccb1d9922b9851d17f6c Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 20 Apr 2017 15:32:40 +0200 Subject: [PATCH 144/151] Rewrote this patch to the actual fix to the issue that was accepted upstream --- build/pkgs/python3/patches/3.4.5-struct.patch | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/build/pkgs/python3/patches/3.4.5-struct.patch b/build/pkgs/python3/patches/3.4.5-struct.patch index 2fe8d93bf8a..3b677a72e90 100644 --- a/build/pkgs/python3/patches/3.4.5-struct.patch +++ b/build/pkgs/python3/patches/3.4.5-struct.patch @@ -1,13 +1,15 @@ -diff --git a/Modules/_struct.c b/Modules/_struct.c -index ad9959e..0a5e5ed 100644 ---- a/Modules/_struct.c -+++ b/Modules/_struct.c -@@ -1627,7 +1627,7 @@ unpackiter_iternext(unpackiterobject *self) - } +Needed to fix compilation of the struct module in Cygwin on Python 3. This +as been fixed upstream for Python 3.7; see https://bugs.python.org/issue21124 +diff -r b244bf74b638 Modules/_struct.c +--- a/Modules/_struct.c Sun Oct 02 13:49:05 2016 +0300 ++++ b/Modules/_struct.c Sun Oct 02 19:54:56 2016 +0900 +@@ -2301,6 +2301,9 @@ + if (PyType_Ready(&PyStructType) < 0) + return NULL; - static PyTypeObject unpackiter_type = { -- PyVarObject_HEAD_INIT(&PyType_Type, 0) -+ PyVarObject_HEAD_INIT(NULL, 0) - "unpack_iterator", /* tp_name */ - sizeof(unpackiterobject), /* tp_basicsize */ - 0, /* tp_itemsize */ ++ if (PyType_Ready(&unpackiter_type) < 0) ++ return NULL; ++ + /* Check endian and swap in faster functions */ + { + const formatdef *native = native_table; From f7f4535fd94051c7232b07edc5d0c6161d603589 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 20 Apr 2017 15:44:53 +0200 Subject: [PATCH 145/151] This patch has been part of Cygwin's Python for a long time, but I don't think it's still needed. Cygwin now includes a static import library for libm that can be used at link time (the actual math library itself is linked into cygwin1.dll). --- build/pkgs/python3/patches/3.5.2-no-libm.patch | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 build/pkgs/python3/patches/3.5.2-no-libm.patch diff --git a/build/pkgs/python3/patches/3.5.2-no-libm.patch b/build/pkgs/python3/patches/3.5.2-no-libm.patch deleted file mode 100644 index f36137a072f..00000000000 --- a/build/pkgs/python3/patches/3.5.2-no-libm.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/setup.py 2017-03-20 17:09:14.414219000 +0100 -+++ b/setup.py 2017-03-20 17:10:59.210847200 +0100 -@@ -570,7 +570,7 @@ - - # Check for MacOS X, which doesn't need libm.a at all - math_libs = ['m'] -- if host_platform == 'darwin': -+ if host_platform in ['darwin', 'cygwin']: - math_libs = [] - - # XXX Omitted modules: gl, pure, dl, SGI-specific modules From 1618c0f0d26c989df228b28db45c714fbc281178 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 20 Apr 2017 15:51:08 +0200 Subject: [PATCH 146/151] Adds descriptions of more of the new patches --- .../python3/patches/2.6.5-FD_SETSIZE.patch | 4 ++ build/pkgs/python3/patches/2.7.3-dylib.patch | 3 ++ .../patches/3.2.6-getpath-exe-extension.patch | 2 + .../patches/3.2.6-no-enable-new-dtags.patch | 2 + .../python3/patches/3.2.6-no-native-tls.patch | 54 ++++++++++--------- .../python3/patches/3.4.5-issue13756.patch | 2 + .../3.5.2-struct_siginfo_si_band.patch | 3 ++ .../pkgs/python3/patches/cygwin-ctypes.patch | 2 + 8 files changed, 48 insertions(+), 24 deletions(-) diff --git a/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch b/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch index 8675734eee9..1059ca4ad45 100644 --- a/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch +++ b/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch @@ -1,3 +1,7 @@ +This patch has never been submitted upstream for some reason, but it simply +increases the default number of file descriptors the Python process can have on +Cygwin, which mitigates some issues; see +https://cygwin.com/ml/cygwin/2011-03/msg00651.html --- a/Modules/selectmodule.c 2012-02-02 22:35:21.835125000 -0500 +++ b/Modules/selectmodule.c 2012-02-02 22:41:41.210125000 -0500 @@ -6,6 +6,21 @@ diff --git a/build/pkgs/python3/patches/2.7.3-dylib.patch b/build/pkgs/python3/patches/2.7.3-dylib.patch index 27b030ecd9f..c45fc26f698 100644 --- a/build/pkgs/python3/patches/2.7.3-dylib.patch +++ b/build/pkgs/python3/patches/2.7.3-dylib.patch @@ -1,3 +1,6 @@ +This patch provides a workaround to one long-standing problem with building +extension modules on Cygwin; see https://bugs.python.org/issue2445 for more +background. --- a/Lib/distutils/unixccompiler.py 2012-11-27 07:44:15.409993500 -0500 +++ b/Lib/distutils/unixccompiler.py 2012-11-27 08:09:57.801770900 -0500 @@ -141,6 +141,7 @@ diff --git a/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch b/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch index 3ededf469a3..ce4e403a2e6 100644 --- a/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch +++ b/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch @@ -1,3 +1,5 @@ +Fixes a known issue with sys.executable ambiguity on Cygwin. This is a +simpler version of the patch found at http://bugs.python.org/issue28441 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -491,6 +491,28 @@ calculate_path(void) diff --git a/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch b/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch index 864714b3d2a..7b642805925 100644 --- a/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch +++ b/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch @@ -1,3 +1,5 @@ +The --enable-new-dtags linker flag should not be used when building non-ELF +binaries. This is related to http://bugs.python.org/issue17362 diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py index 69045f8..7ed0a5f 100644 --- a/Lib/distutils/unixccompiler.py diff --git a/build/pkgs/python3/patches/3.2.6-no-native-tls.patch b/build/pkgs/python3/patches/3.2.6-no-native-tls.patch index 775909ded73..8675880c2b5 100644 --- a/build/pkgs/python3/patches/3.2.6-no-native-tls.patch +++ b/build/pkgs/python3/patches/3.2.6-no-native-tls.patch @@ -1,3 +1,9 @@ +This changes Python's configure script to not use the pthread "native TLS" +implementation on systems (such as Cygwin) where pthread_key_t is not defined +to be an integer. This is a workaround to the issue discussed in more detail +at http://bugs.python.org/issue25658 + +This is expected to be fixed in a better way in future versions of Python. diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index de42f1a..a4eab78 100644 --- a/Python/thread_pthread.h @@ -21,6 +27,30 @@ index de42f1a..a4eab78 100644 {} + +#endif /* PTHREAD_KEY_T_IS_INTEGER */ +diff --git a/configure.ac b/configure.ac +index 0ab4430..89e422a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1610,6 +1610,19 @@ if test "$have_pthread_t" = yes ; then + #endif + ]) + fi ++ ++# Issue #25658: POSIX doesn't require that pthread_key_t is integer. ++AC_MSG_CHECKING(whether pthread_key_t is integer) ++AC_COMPILE_IFELSE( ++ [AC_LANG_PROGRAM([[#include ]], [[pthread_key_t k; k * 1;]])], ++ [ac_pthread_key_t_is_integer=yes], ++ [ac_pthread_key_t_is_integer=no] ++) ++AC_MSG_RESULT($ac_pthread_key_t_is_integer) ++if test "$ac_pthread_key_t_is_integer" = yes ; then ++ AC_DEFINE(PTHREAD_KEY_T_IS_INTEGER, 1, ++ [Define if pthread_key_t is integer.]) ++fi + CC="$ac_save_cc" + + AC_SUBST(OTHER_LIBTOOL_OPT) diff --git a/configure b/configure index b107bf2..59f80cb 100755 --- a/configure @@ -61,30 +91,6 @@ index b107bf2..59f80cb 100755 CC="$ac_save_cc" -diff --git a/configure.ac b/configure.ac -index 0ab4430..89e422a 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -1610,6 +1610,19 @@ if test "$have_pthread_t" = yes ; then - #endif - ]) - fi -+ -+# Issue #25658: POSIX doesn't require that pthread_key_t is integer. -+AC_MSG_CHECKING(whether pthread_key_t is integer) -+AC_COMPILE_IFELSE( -+ [AC_LANG_PROGRAM([[#include ]], [[pthread_key_t k; k * 1;]])], -+ [ac_pthread_key_t_is_integer=yes], -+ [ac_pthread_key_t_is_integer=no] -+) -+AC_MSG_RESULT($ac_pthread_key_t_is_integer) -+if test "$ac_pthread_key_t_is_integer" = yes ; then -+ AC_DEFINE(PTHREAD_KEY_T_IS_INTEGER, 1, -+ [Define if pthread_key_t is integer.]) -+fi - CC="$ac_save_cc" - - AC_SUBST(OTHER_LIBTOOL_OPT) diff --git a/pyconfig.h.in b/pyconfig.h.in index cf0ea1f..0bd8387 100644 --- a/pyconfig.h.in diff --git a/build/pkgs/python3/patches/3.4.5-issue13756.patch b/build/pkgs/python3/patches/3.4.5-issue13756.patch index ceab9dbc92b..7ac330d7aa0 100644 --- a/build/pkgs/python3/patches/3.4.5-issue13756.patch +++ b/build/pkgs/python3/patches/3.4.5-issue13756.patch @@ -1,3 +1,5 @@ +This patch is needed to correctly build and link with the DLL import library +for libpython on Cygwin; see https://bugs.python.org/issue13756 diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py index acbe648..6bedbb2 100644 --- a/Lib/distutils/command/build_ext.py diff --git a/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch b/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch index f82f86f5288..c2ee6104051 100644 --- a/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch +++ b/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch @@ -1,3 +1,6 @@ +This fixes building the signal module on Cygwin, where the si_band member +of the siginfo_t struct is not present as expected by Python. This has been +fixed upstream for Python 3.7; see https://bugs.python.org/issue21085 diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 753d987..c69c0ce 100644 --- a/Modules/signalmodule.c diff --git a/build/pkgs/python3/patches/cygwin-ctypes.patch b/build/pkgs/python3/patches/cygwin-ctypes.patch index af6265ac213..7b2f0717733 100644 --- a/build/pkgs/python3/patches/cygwin-ctypes.patch +++ b/build/pkgs/python3/patches/cygwin-ctypes.patch @@ -1,3 +1,5 @@ +This patch is needed for the ctypes library to get the correct name of the +libpython DLL, and is related to https://bugs.python.org/issue13756 --- a/Lib/ctypes/__init__.py 2017-03-21 15:16:01.564958900 +0100 +++ b/Lib/ctypes/__init__.py 2017-03-21 15:18:10.476792300 +0100 @@ -430,7 +430,8 @@ From cebe992b40c8eca9c5f9666447910c111bbb905a Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 20 Apr 2017 15:04:47 +0100 Subject: [PATCH 147/151] put LDFLAGS in the right place --- build/pkgs/lcalc/patches/Makefile.patch | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build/pkgs/lcalc/patches/Makefile.patch b/build/pkgs/lcalc/patches/Makefile.patch index 95cbdbd08cf..b9c6de4d518 100644 --- a/build/pkgs/lcalc/patches/Makefile.patch +++ b/build/pkgs/lcalc/patches/Makefile.patch @@ -79,7 +79,7 @@ -else - LDFLAGS = $(LDFLAGS2) -endif -+LDFLAGS := -L$(LOCATION_PARI_LIBRARY) -lpari -lgmp $(LDFLAGS) ++LDFLAGS := $(LDFLAGS) -L$(LOCATION_PARI_LIBRARY) -lpari -lgmp @@ -134,25 +134,25 @@ -lcalc: $(OBJECTS) - $(CC) $(CCFLAGS) $(INCLUDEFILES) $(OBJECTS) $(LDFLAGS) -o lcalc $(GMP_FLAGS) +lcalc$(EXEEXT): $(OBJECTS) -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) $(OBJECTS) $(LDFLAGS) -o lcalc$(EXEEXT) $(GMP_FLAGS) ++ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) $(LDFLAGS) $(OBJECTS) -o lcalc$(EXEEXT) $(GMP_FLAGS) examples: - $(CC) $(CCFLAGS) $(INCLUDEFILES) example_programs/example.cc libLfunction.so -o example_programs/example $(GMP_FLAGS) -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) example_programs/example.cc libLfunction$(LIBEXT) -o example_programs/example$(EXEEXT) $(GMP_FLAGS) ++ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) example_programs/example.cc $(LDFLAGS) libLfunction$(LIBEXT) -o example_programs/example$(EXEEXT) $(GMP_FLAGS) + +proc$(EXEEXT): -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) example_programs/proc.cc libLfunction$(LIBEXT) -o example_programs/proc$(EXEEXT) $(GMP_FLAGS) ++ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) example_programs/proc.cc $(LDFLAGS) libLfunction$(LIBEXT) -o example_programs/proc$(EXEEXT) $(GMP_FLAGS) -proc: - $(CC) $(CCFLAGS) $(INCLUDEFILES) example_programs/proc.cc libLfunction.so -o example_programs/proc $(GMP_FLAGS) +test$(EXEEXT): -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) example_programs/test.cc libLfunction$(LIBEXT) -o example_programs/test$(EXEEXT) $(GMP_FLAGS) ++ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) example_programs/test.cc $(LDFLAGS) libLfunction$(LIBEXT) -o example_programs/test$(EXEEXT) $(GMP_FLAGS) -test: - $(CC) $(CCFLAGS) $(INCLUDEFILES) example_programs/test.cc libLfunction.so -o example_programs/test $(GMP_FLAGS) +find_L$(EXEEXT): -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) find_L_functions/find_L_functions.cc libLfunction$(LIBEXT) -o find_L_functions/find_L$(EXEEXT) $(GMP_FLAGS) ++ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) find_L_functions/find_L_functions.cc $(LDFLAGS) libLfunction$(LIBEXT) -o find_L_functions/find_L$(EXEEXT) $(GMP_FLAGS) -find_L: - $(CC) $(CCFLAGS) $(INCLUDEFILES) find_L_functions/find_L_functions.cc libLfunction.so -o find_L_functions/find_L $(GMP_FLAGS) @@ -201,7 +201,7 @@ -libLfunction.so: $(OBJ_L) - g++ -$(DYN_OPTION) -o libLfunction.so $(OBJ_L) +libLfunction$(LIBEXT): $(OBJ_L) -+ $(CXX) -$(DYN_OPTION) $(CXXFLAG64) -o libLfunction$(LIBEXT) $(OBJ_L) $(LDFLAGS) ++ $(CXX) -$(DYN_OPTION) $(CXXFLAG64) -o libLfunction$(LIBEXT) $(LDFLAGS) $(OBJ_L) clean: - rm -f *.o lcalc libLfunction.so example_programs/example From 5031e422023459fdbe3a48cb5159abc63ec57cf2 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 21 Apr 2017 16:30:44 +0200 Subject: [PATCH 148/151] Added a brief description of the ncurses patch with link to the original issue. --- build/pkgs/python3/patches/2.6.5-ncurses-abi6.patch | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/pkgs/python3/patches/2.6.5-ncurses-abi6.patch b/build/pkgs/python3/patches/2.6.5-ncurses-abi6.patch index 6587121a346..b828e964b68 100644 --- a/build/pkgs/python3/patches/2.6.5-ncurses-abi6.patch +++ b/build/pkgs/python3/patches/2.6.5-ncurses-abi6.patch @@ -1,3 +1,8 @@ +Fixes compilation of the curses module on Cygwin; see +https://bugs.python.org/issue14438 + +This patch was never accepted upstream, though there is a new fix for it +in progress at https://bugs.python.org/issue28190 but also not yet accepted. --- a/Include/py_curses.h 2009-09-06 16:23:05.000000000 -0500 +++ b/Include/py_curses.h 2010-04-14 15:21:23.008971400 -0500 @@ -17,6 +17,13 @@ From 841676d4931c9f59019b2b6b82afbbe7df222c10 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 21 Apr 2017 16:58:40 +0200 Subject: [PATCH 149/151] Define functions before calling them --- .../matrix/matrix_modn_dense_template.pxi | 138 +++++++++--------- src/sage/rings/multi_power_series_ring.py | 1 - .../polynomial_integer_dense_flint.pyx | 2 - .../polynomial/polynomial_rational_flint.pyx | 1 - src/sage/sets/finite_set_map_cy.pyx | 94 ++++++------ 5 files changed, 118 insertions(+), 118 deletions(-) diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index 84a2700db69..ab60bf1f4c5 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -352,6 +352,77 @@ cdef inline linbox_charpoly(celement modulus, Py_ssize_t nrows, celement* entrie del F return l + +cpdef __matrix_from_rows_of_matrices(X): + """ + Return a matrix whose row ``i`` is constructed from the entries of + matrix ``X[i]``. + + INPUT: + + - ``X`` - a nonempty list of matrices of the same size mod a + single modulus `n` + + EXAMPLES:: + + sage: X = [random_matrix(GF(17), 4, 4) for _ in range(10)]; X + [ + [ 2 14 0 15] [12 14 3 13] [ 9 15 8 1] [ 2 12 6 10] + [11 10 16 2] [10 1 14 6] [ 5 8 10 11] [12 0 6 9] + [ 9 4 10 14] [ 2 14 13 7] [ 5 12 4 9] [ 7 7 3 8] + [ 1 14 3 14], [ 6 14 10 3], [15 2 6 11], [ 2 9 1 5], + + [12 13 7 16] [ 5 3 16 2] [14 15 16 4] [ 1 15 11 0] + [ 7 11 11 1] [11 10 12 14] [14 1 12 13] [16 13 8 14] + [ 0 2 0 4] [ 0 7 16 4] [ 5 5 16 13] [13 14 16 4] + [ 7 9 8 15], [ 6 5 2 3], [10 12 1 7], [15 6 6 6], + + [ 4 10 11 15] [13 12 5 1] + [11 2 9 14] [16 13 16 7] + [12 5 4 4] [12 2 0 11] + [ 2 0 12 8], [13 11 6 15] + ] + sage: X[0]._matrix_from_rows_of_matrices(X) # indirect doctest + [ 2 14 0 15 11 10 16 2 9 4 10 14 1 14 3 14] + [12 14 3 13 10 1 14 6 2 14 13 7 6 14 10 3] + [ 9 15 8 1 5 8 10 11 5 12 4 9 15 2 6 11] + [ 2 12 6 10 12 0 6 9 7 7 3 8 2 9 1 5] + [12 13 7 16 7 11 11 1 0 2 0 4 7 9 8 15] + [ 5 3 16 2 11 10 12 14 0 7 16 4 6 5 2 3] + [14 15 16 4 14 1 12 13 5 5 16 13 10 12 1 7] + [ 1 15 11 0 16 13 8 14 13 14 16 4 15 6 6 6] + [ 4 10 11 15 11 2 9 14 12 5 4 4 2 0 12 8] + [13 12 5 1 16 13 16 7 12 2 0 11 13 11 6 15] + + OUTPUT: A single matrix mod ``p`` whose ``i``-th row is ``X[i].list()``. + + .. note:: + + Do not call this function directly but use the static method + ``Matrix_modn_dense_float/double._matrix_from_rows_of_matrices`` + """ + # The code below is just a fast version of the following: + ## from constructor import matrix + ## K = X[0].base_ring() + ## v = sum([y.list() for y in X],[]) + ## return matrix(K, len(X), X[0].nrows()*X[0].ncols(), v) + + from matrix_space import MatrixSpace + cdef Matrix_modn_dense_template A, T + cdef Py_ssize_t i, n, m + n = len(X) + + T = X[0] + m = T._nrows * T._ncols + A = T.__class__.__new__(T.__class__, MatrixSpace(X[0].base_ring(), n, m), 0, 0, 0) + A.p = T.p + + for i from 0 <= i < n: + T = X[i] + memcpy(A._entries + i*m, T._entries, sizeof(celement)*m) + return A + + cdef class Matrix_modn_dense_template(Matrix_dense): def __cinit__(self, parent, entries, copy, coerce): """ @@ -2994,72 +3065,5 @@ cdef class Matrix_modn_dense_template(Matrix_dense): for j in range(self._ncols): to[j] = _from[j] -cpdef __matrix_from_rows_of_matrices(X): - """ - Return a matrix whose row ``i`` is constructed from the entries of - matrix ``X[i]``. - - INPUT: - - - ``X`` - a nonempty list of matrices of the same size mod a - single modulus `n` - EXAMPLES:: - - sage: X = [random_matrix(GF(17), 4, 4) for _ in range(10)]; X - [ - [ 2 14 0 15] [12 14 3 13] [ 9 15 8 1] [ 2 12 6 10] - [11 10 16 2] [10 1 14 6] [ 5 8 10 11] [12 0 6 9] - [ 9 4 10 14] [ 2 14 13 7] [ 5 12 4 9] [ 7 7 3 8] - [ 1 14 3 14], [ 6 14 10 3], [15 2 6 11], [ 2 9 1 5], - - [12 13 7 16] [ 5 3 16 2] [14 15 16 4] [ 1 15 11 0] - [ 7 11 11 1] [11 10 12 14] [14 1 12 13] [16 13 8 14] - [ 0 2 0 4] [ 0 7 16 4] [ 5 5 16 13] [13 14 16 4] - [ 7 9 8 15], [ 6 5 2 3], [10 12 1 7], [15 6 6 6], - - [ 4 10 11 15] [13 12 5 1] - [11 2 9 14] [16 13 16 7] - [12 5 4 4] [12 2 0 11] - [ 2 0 12 8], [13 11 6 15] - ] - sage: X[0]._matrix_from_rows_of_matrices(X) # indirect doctest - [ 2 14 0 15 11 10 16 2 9 4 10 14 1 14 3 14] - [12 14 3 13 10 1 14 6 2 14 13 7 6 14 10 3] - [ 9 15 8 1 5 8 10 11 5 12 4 9 15 2 6 11] - [ 2 12 6 10 12 0 6 9 7 7 3 8 2 9 1 5] - [12 13 7 16 7 11 11 1 0 2 0 4 7 9 8 15] - [ 5 3 16 2 11 10 12 14 0 7 16 4 6 5 2 3] - [14 15 16 4 14 1 12 13 5 5 16 13 10 12 1 7] - [ 1 15 11 0 16 13 8 14 13 14 16 4 15 6 6 6] - [ 4 10 11 15 11 2 9 14 12 5 4 4 2 0 12 8] - [13 12 5 1 16 13 16 7 12 2 0 11 13 11 6 15] - - OUTPUT: A single matrix mod ``p`` whose ``i``-th row is ``X[i].list()``. - - .. note:: - - Do not call this function directly but use the static method - ``Matrix_modn_dense_float/double._matrix_from_rows_of_matrices`` - """ - # The code below is just a fast version of the following: - ## from constructor import matrix - ## K = X[0].base_ring() - ## v = sum([y.list() for y in X],[]) - ## return matrix(K, len(X), X[0].nrows()*X[0].ncols(), v) - - from matrix_space import MatrixSpace - cdef Matrix_modn_dense_template A, T - cdef Py_ssize_t i, n, m - n = len(X) - - T = X[0] - m = T._nrows * T._ncols - A = T.__class__.__new__(T.__class__, MatrixSpace(X[0].base_ring(), n, m), 0, 0, 0) - A.p = T.p - - for i from 0 <= i < n: - T = X[i] - memcpy(A._entries + i*m, T._entries, sizeof(celement)*m) - return A diff --git a/src/sage/rings/multi_power_series_ring.py b/src/sage/rings/multi_power_series_ring.py index b50090c3cb3..521d21eece5 100644 --- a/src/sage/rings/multi_power_series_ring.py +++ b/src/sage/rings/multi_power_series_ring.py @@ -206,7 +206,6 @@ from sage.rings.ring import CommutativeRing from sage.rings.polynomial.all import PolynomialRing -from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.rings.polynomial.polynomial_ring import is_PolynomialRing from sage.rings.polynomial.multi_polynomial import is_MPolynomial from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index ec7de9b07ac..f3122f1dc93 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -43,8 +43,6 @@ from sage.rings.polynomial.polynomial_element cimport Polynomial from sage.structure.element cimport ModuleElement, RingElement from sage.structure.element import coerce_binop -from sage.rings.polynomial.polynomial_element cimport is_Polynomial - from sage.libs.ntl.ntl_ZZX cimport ntl_ZZX from sage.rings.integer_ring import ZZ diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index e50c087a979..36a86720b5a 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -39,7 +39,6 @@ from sage.rings.fraction_field_element import FractionFieldElement from sage.rings.rational cimport Rational from sage.rings.rational_field import QQ from sage.rings.polynomial.polynomial_element cimport Polynomial -from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.rings.polynomial.polynomial_integer_dense_flint cimport Polynomial_integer_dense_flint from sage.structure.parent cimport Parent diff --git a/src/sage/sets/finite_set_map_cy.pyx b/src/sage/sets/finite_set_map_cy.pyx index 6b20a42d369..0cbbcf9f7dc 100644 --- a/src/sage/sets/finite_set_map_cy.pyx +++ b/src/sage/sets/finite_set_map_cy.pyx @@ -384,6 +384,53 @@ cdef class FiniteSetMap_MN(ClonableIntArray): return res +cpdef FiniteSetMap_Set FiniteSetMap_Set_from_list(t, parent, lst): + """ + Creates a ``FiniteSetMap`` from a list + + .. warning:: no check is performed ! + + TESTS:: + + sage: from sage.sets.finite_set_map_cy import FiniteSetMap_Set_from_list as from_list + sage: F = FiniteSetMaps(["a", "b"], [3, 4, 5]) + sage: f = from_list(F.element_class, F, [0,2]); f.check(); f + map: a -> 3, b -> 5 + sage: f.parent() is F + True + sage: f.is_immutable() + True + """ + cdef FiniteSetMap_MN res + cdef type cls = t + res = cls.__new__(cls) + super(FiniteSetMap_MN, res).__init__(parent, lst) + return res + +cpdef FiniteSetMap_Set FiniteSetMap_Set_from_dict(t, parent, d): + """ + Creates a ``FiniteSetMap`` from a dictionary + + .. warning:: no check is performed ! + + TESTS:: + + sage: from sage.sets.finite_set_map_cy import FiniteSetMap_Set_from_dict as from_dict + sage: F = FiniteSetMaps(["a", "b"], [3, 4, 5]) + sage: f = from_dict(F.element_class, F, {"a": 3, "b": 5}); f.check(); f + map: a -> 3, b -> 5 + sage: f.parent() is F + True + sage: f.is_immutable() + True + """ + cdef FiniteSetMap_Set res + cdef type cls = t + res = cls.__new__(cls) + res.__init__(parent, d.__getitem__) + return res + + cdef class FiniteSetMap_Set(FiniteSetMap_MN): """ Data structure for maps @@ -554,53 +601,6 @@ cdef class FiniteSetMap_Set(FiniteSetMap_MN): return "map: "+", ".join([("%s -> %s"%(i, self(i))) for i in self.domain()]) -cpdef FiniteSetMap_Set FiniteSetMap_Set_from_list(t, parent, lst): - """ - Creates a ``FiniteSetMap`` from a list - - .. warning:: no check is performed ! - - TESTS:: - - sage: from sage.sets.finite_set_map_cy import FiniteSetMap_Set_from_list as from_list - sage: F = FiniteSetMaps(["a", "b"], [3, 4, 5]) - sage: f = from_list(F.element_class, F, [0,2]); f.check(); f - map: a -> 3, b -> 5 - sage: f.parent() is F - True - sage: f.is_immutable() - True - """ - cdef FiniteSetMap_MN res - cdef type cls = t - res = cls.__new__(cls) - super(FiniteSetMap_MN, res).__init__(parent, lst) - return res - -cpdef FiniteSetMap_Set FiniteSetMap_Set_from_dict(t, parent, d): - """ - Creates a ``FiniteSetMap`` from a dictionary - - .. warning:: no check is performed ! - - TESTS:: - - sage: from sage.sets.finite_set_map_cy import FiniteSetMap_Set_from_dict as from_dict - sage: F = FiniteSetMaps(["a", "b"], [3, 4, 5]) - sage: f = from_dict(F.element_class, F, {"a": 3, "b": 5}); f.check(); f - map: a -> 3, b -> 5 - sage: f.parent() is F - True - sage: f.is_immutable() - True - """ - cdef FiniteSetMap_Set res - cdef type cls = t - res = cls.__new__(cls) - res.__init__(parent, d.__getitem__) - return res - - cdef class FiniteSetEndoMap_N(FiniteSetMap_MN): """ Maps from ``range(n)`` to itself. From db180c742fba80a93b9d37b89e9fb6f23876c89d Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 21 Apr 2017 09:42:58 +0200 Subject: [PATCH 150/151] getattr() debugger --- src/sage/all.py | 1 + src/sage/tests/cython.pyx | 191 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) diff --git a/src/sage/all.py b/src/sage/all.py index cd7508d3a0b..98d28b0bad9 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -118,6 +118,7 @@ from sage.calculus.all import * import sage.tests.all as tests +from sage.tests.cython import getattr_debug from sage.crypto.all import * import sage.crypto.mq as mq diff --git a/src/sage/tests/cython.pyx b/src/sage/tests/cython.pyx index de81399da40..b4abd31384f 100644 --- a/src/sage/tests/cython.pyx +++ b/src/sage/tests/cython.pyx @@ -11,7 +11,16 @@ This file collects tests requiring Cython. # http://www.gnu.org/licenses/ #***************************************************************************** +from cpython.object cimport PyObject, Py_TYPE, descrgetfunc, descrsetfunc + +cdef extern from "Python.h": + # Helper to get a pointer to an object's __dict__ slot, if any + PyObject** _PyObject_GetDictPtr(obj) + +from sage.structure.misc cimport AttributeErrorMessage from sage.misc.fast_methods cimport FastHashable_class + + cdef class ClassWithLargeHash(FastHashable_class): """ This class tests against a bug with :class:`FastHashable_class` @@ -32,3 +41,185 @@ cdef class ClassWithLargeHash(FastHashable_class): def __repr__(self): return 'A successfully created object with a very large hash' + + +def shortrepr(obj, max=50): + """ + Return ``repr(obj)`` bounded to ``max`` characters. If the string + is too long, it is truncated and ``~~~`` is added to the end. + + EXAMPLES:: + + sage: from sage.tests.cython import shortrepr + sage: print(shortrepr("Hello world!")) + 'Hello world!' + sage: print(shortrepr("Hello world!" * 4)) + 'Hello world!Hello world!Hello world!Hello world!' + sage: print(shortrepr("Hello world!" * 5)) + 'Hello world!Hello world!Hello world!Hello worl~~~ + """ + r = repr(obj) + if len(r) > max: + r = r[:max-3] + "~~~" + return r + + +cdef object _no_default = object() # Unique object + +def getattr_debug(obj, name, default=_no_default): + r""" + A re-implementation of ``getattr()`` with lots of debugging info. + + This will correctly use ``__getattr__`` if needed. On the other + hand, it assumes a generic (not overridden) implementation of + ``__getattribute__``. Note that Cython implements ``__getattr__`` + for a cdef class using ``__getattribute__``, so this will not + detect a ``__getattr__`` in that case. + + INPUT: + + - ``obj`` -- the object whose attribute is requested + + - ``name`` -- (string) the name of the attribute + + - ``default`` -- default value to return if attribute was not found + + EXAMPLES:: + + sage: _ = getattr_debug(list, "reverse") + getattr_debug(obj=, name='reverse'): + type(obj) = + object has __dict__ slot () + did not find 'reverse' in MRO classes + found 'reverse' in object __dict__ + returning () + sage: _ = getattr_debug([], "reverse") + getattr_debug(obj=[], name='reverse'): + type(obj) = + object does not have __dict__ slot + found 'reverse' in dict of + got () + attribute is ordinary descriptor (has __get__) + calling __get__() + returning ) + sage: _ = getattr_debug([], "__doc__") + getattr_debug(obj=[], name='__doc__'): + type(obj) = + object does not have __dict__ slot + found '__doc__' in dict of + got "list() -> new empty list\nlist(iterable) -> ne~~~ () + returning "list() -> new empty list\nlist(iterable) -> ne~~~ () + sage: _ = getattr_debug(gp(1), "log") + getattr_debug(obj=1, name='log'): + type(obj) = + object has __dict__ slot () + did not find 'log' in MRO classes + object __dict__ does not have 'log' + calling __getattr__() + returning log () + sage: from ipywidgets import IntSlider + sage: _ = getattr_debug(IntSlider(), "value") + getattr_debug(obj=IntSlider(value=0, min=0, max=100, step=1), name='value'): + type(obj) = + object has __dict__ slot () + found 'value' in dict of + got ) + attribute is data descriptor (has __get__ and __set__) + ignoring __dict__ because we have a data descriptor + calling __get__() + returning 0 () + sage: _ = getattr_debug(1, "foo") + Traceback (most recent call last): + ... + AttributeError: 'sage.rings.integer.Integer' object has no attribute 'foo' + sage: _ = getattr_debug(1, "foo", "xyz") + getattr_debug(obj=1, name='foo'): + type(obj) = + object does not have __dict__ slot + did not find 'foo' in MRO classes + class does not have __getattr__ + attribute not found + returning default 'xyz' + """ + if default is not _no_default: + try: + return getattr_debug(obj, name) + except AttributeError: + print(f" returning default {shortrepr(default)}") + return default + + name = str(name) + print(f"getattr_debug(obj={shortrepr(obj)}, name={name!r}):") + print(f" type(obj) = {type(obj)}") + + cdef object attr = None + cdef object dct = None + + cdef PyObject** dictptr = _PyObject_GetDictPtr(obj) + if dictptr is not NULL: + if dictptr[0] is not NULL: + dct = (dictptr[0]) + print(f" object has __dict__ slot ({type(dct)})") + else: + print(f" object has uninitialized __dict__ slot") + else: + print(f" object does not have __dict__ slot") + + cdef descrgetfunc get = NULL + cdef descrsetfunc set = NULL + + # Look for name in dicts of types in MRO + cdef bint attr_in_class = False + for cls in type(obj).__mro__: + if name in cls.__dict__: + attr = cls.__dict__[name] + print(f" found {name!r} in dict of {cls}") + print(f" got {shortrepr(attr)} ({type(attr)})") + + get = Py_TYPE(attr).tp_descr_get + set = Py_TYPE(attr).tp_descr_set + if get is not NULL: + if set is not NULL: + print(f" attribute is data descriptor (has __get__ and __set__)") + if dct is not None: + print(f" ignoring __dict__ because we have a data descriptor") + print(f" calling __get__()") + attr = get(attr, obj, type(obj)) + print(f" returning {shortrepr(attr)} ({type(attr)})") + return attr + else: + print(f" attribute is ordinary descriptor (has __get__)") + attr_in_class = True + break + + if not attr_in_class: + print(f" did not find {name!r} in MRO classes") + + if dct is not None: + if name in dct: + attr = dct[name] + print(f" found {name!r} in object __dict__") + print(f" returning {shortrepr(attr)} ({type(attr)})") + return attr + else: + print(f" object __dict__ does not have {name!r}") + + if attr_in_class: + if get is not NULL: + print(f" calling __get__()") + attr = get(attr, obj, type(obj)) + print(f" returning {shortrepr(attr)} ({type(attr)})") + return attr + + try: + tpgetattr = type(obj).__getattr__ + except AttributeError: + print(f" class does not have __getattr__") + else: + print(f" calling __getattr__()") + attr = tpgetattr(obj, name) + print(f" returning {shortrepr(attr)} ({type(attr)})") + return attr + + print(f" attribute not found") + raise AttributeError(AttributeErrorMessage(obj, name)) From 2f5a28f235a1b61cdb5c394bf5743d6c242afea6 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 23 Apr 2017 14:59:16 +0200 Subject: [PATCH 151/151] Updated SageMath version to 8.0.beta3 --- 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 70f1eab5ef3..95b12e86ea2 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 8.0.beta2, Release Date: 2017-04-12 +SageMath version 8.0.beta3, Release Date: 2017-04-23 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index f63ecb19d54..38535811fe2 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=8bdfb4b87a803b90d6b9a937d961835955723a09 -md5=1f1aa69a7ac98e4443e82611a3bbea23 -cksum=4121821509 +sha1=1191f0229692e23f4544b68cf56c3443391c9c88 +md5=a37eab0bd6a75b55be567ea8fff5b09e +cksum=1527621800 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index a817176f4a6..0ddd619ce32 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -216 +217 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index fbbb323323d..ac7175afabd 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath version 8.0.beta2, Release Date: 2017-04-12 │ +│ SageMath version 8.0.beta3, Release Date: 2017-04-23 │ │ 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 d7ea566a230..8441f8f980f 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='8.0.beta2' -SAGE_RELEASE_DATE='2017-04-12' +SAGE_VERSION='8.0.beta3' +SAGE_RELEASE_DATE='2017-04-23' diff --git a/src/sage/version.py b/src/sage/version.py index c24735c795e..10eb786abd1 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 = '8.0.beta2' -date = '2017-04-12' +version = '8.0.beta3' +date = '2017-04-23'