From c352b1d5920ce9ffaebada2fe172784b2ba3e3fb Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 5 Aug 2013 16:10:17 +0200 Subject: [PATCH 001/206] Trac #15005: fix ref to SAGE_ROOT that broke %edit See also: trac #13432 git 1b96ec30440eac87b7361b7106fb4cae8ecc45a6 --- src/sage/misc/edit_module.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/misc/edit_module.py b/src/sage/misc/edit_module.py index 42046bee627..582c6e42c19 100644 --- a/src/sage/misc/edit_module.py +++ b/src/sage/misc/edit_module.py @@ -301,9 +301,8 @@ def edit_devel(self, filename, linenum): editor supports it, also at the line in wich gcd is defined. """ import IPython.core.hooks - sageroot = sage.misc.sageinspect.SAGE_ROOT+'/' - runpathpattern = '^'+sageroot+'local/lib/python[^/]*/site-packages' - develbranch = sageroot+'devel/sage' + runpathpattern = '^'+sage.env.SAGE_LIB + develbranch = sage.env.SAGE_SRC filename=re.sub(runpathpattern,develbranch,filename) IPython.core.hooks.editor(self, filename, linenum) From e069bbee9c41ab462de216971454b56fc83cd08f Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Tue, 6 Aug 2013 13:46:29 +0200 Subject: [PATCH 002/206] Trac #15010: Make Rational.minpoly consistent with NumberFieldElement.minpoly Make the var parameter of Rational.minpoly() default to 'x', for consistency with NumberFieldElement.minpoly(). (This is desirable since is_NumberField(QQ) == True.) --- src/sage/rings/rational.pyx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index c752c708e75..f662e310bac 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -2658,7 +2658,7 @@ cdef class Rational(sage.structure.element.FieldElement): QQ = self.parent() return QQ[var]([-self,1]) - def minpoly(self, var): + def minpoly(self, var='x'): """ Return the minimal polynomial of this rational number. This will always be just ``x - self``; this is really here so that code written @@ -2672,8 +2672,10 @@ cdef class Rational(sage.structure.element.FieldElement): EXAMPLES:: - sage: (1/3).minpoly('x') - x - 1/3 + sage: (1/3).minpoly() + x - 1/3 + sage: (1/3).minpoly('y') + y - 1/3 AUTHORS: From 104f7a69a37e84180c622f52c4be13530c8d9276 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Tue, 6 Aug 2013 16:26:45 +0200 Subject: [PATCH 003/206] Declare embedding Order -> ambient number field --- src/sage/rings/number_field/order.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index 711e29acf67..f50477a006d 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -155,10 +155,19 @@ def __init__(self, K, is_maximal): sage: k = CyclotomicField(5) sage: k.maximal_order() Maximal Order in Cyclotomic Field of order 5 and degree 4 + + TESTS:: + + sage: k. = NumberField(x^7+3*x+1, embedding=CC(0,1)) + sage: O = k.order(alg) + sage: ordelt = O(alg) + sage: CC(ordelt) + 0.0535229072603327 + 1.20934552493846*I """ self._K = K self._is_maximal = is_maximal IntegralDomain.__init__(self, ZZ, names = K.variable_names(), normalize = False) + self._populate_coercion_lists_(embedding=self.number_field()) def fractional_ideal(self, *args, **kwds): """ From 0e1c492cd0b7467df2606bb1342550f652d9719e Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Tue, 6 Aug 2013 16:53:15 +0200 Subject: [PATCH 004/206] Trac #14989: Fix conversion of quadratic order elements to CC --- src/sage/rings/complex_field.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/complex_field.py b/src/sage/rings/complex_field.py index 8bb021f4888..93222968bc5 100644 --- a/src/sage/rings/complex_field.py +++ b/src/sage/rings/complex_field.py @@ -30,6 +30,7 @@ from sage.structure.parent import Parent from sage.structure.parent_gens import ParentWithGens +NumberField_quadratic = None NumberFieldElement_quadratic = None AlgebraicNumber_base = None AlgebraicNumber = None @@ -47,6 +48,7 @@ def late_import(): sage: sage.rings.complex_field.late_import() """ + global NumberField_quadratic global NumberFieldElement_quadratic global AlgebraicNumber_base global AlgebraicNumber @@ -54,7 +56,9 @@ def late_import(): global AA, QQbar, SR global CLF, RLF, CDF if NumberFieldElement_quadratic is None: + import sage.rings.number_field.number_field import sage.rings.number_field.number_field_element_quadratic as nfeq + NumberField_quadratic = sage.rings.number_field.number_field.NumberField_quadratic NumberFieldElement_quadratic = nfeq.NumberFieldElement_quadratic import sage.rings.qqbar AlgebraicNumber_base = sage.rings.qqbar.AlgebraicNumber_base @@ -354,6 +358,14 @@ def _element_constructor_(self, x): sage: CC((1,2)) # indirect doctest 1.00000000000000 + 2.00000000000000*I + + Check that :trac:`14989` is fixed:: + + sage: QQi = NumberField(x^2+1, 'i', embedding=CC(0,1)) + sage: i = QQi.order(QQi.gen()).gen(1) + sage: CC(i) + 1.00000000000000*I + """ if not isinstance(x, (real_mpfr.RealNumber, tuple)): if isinstance(x, complex_double.ComplexDoubleElement): @@ -365,9 +377,10 @@ def _element_constructor_(self, x): sage_eval(x.replace(' ',''), locals={"I":self.gen(),"i":self.gen()})) late_import() - if isinstance(x, NumberFieldElement_quadratic) and list(x.parent().polynomial()) == [1, 0, 1]: - (re, im) = list(x) - return complex_number.ComplexNumber(self, re, im) + if isinstance(x, NumberFieldElement_quadratic): + if isinstance(x.parent(), NumberField_quadratic) and list(x.parent().polynomial()) == [1, 0, 1]: + (re, im) = list(x) + return complex_number.ComplexNumber(self, re, im) try: return self(x.sage()) From 0be2c23dc1a7c5a9a903f4fa8ecd4674cb591891 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Fri, 16 Aug 2013 13:46:28 +0100 Subject: [PATCH 005/206] Do not pickle caches of toric varieties --- src/sage/schemes/toric/variety.py | 53 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/sage/schemes/toric/variety.py b/src/sage/schemes/toric/variety.py index 59924169884..c99ff04d521 100644 --- a/src/sage/schemes/toric/variety.py +++ b/src/sage/schemes/toric/variety.py @@ -336,6 +336,7 @@ from sage.schemes.generic.ambient_space import AmbientSpace from sage.schemes.toric.homset import SchemeHomset_points_toric_field from sage.categories.fields import Fields +from sage.misc.cachefunc import ClearCacheOnPickle _Fields = Fields() @@ -520,7 +521,7 @@ def AffineToricVariety(cone, *args, **kwds): return ToricVariety(fan, *args, **kwds) -class ToricVariety_field(AmbientSpace): +class ToricVariety_field(ClearCacheOnPickle, AmbientSpace): r""" Construct a toric variety associated to a rational polyhedral fan. @@ -1929,6 +1930,7 @@ def linear_equivalence_ideal(self): self._linear_equivalence_ideal = self._fan.linear_equivalence_ideal(R) return self._linear_equivalence_ideal + @cached_method def cohomology_ring(self): r""" Return the cohomology ring of the toric variety. @@ -1966,14 +1968,23 @@ def cohomology_ring(self): ('x', 'u', 'y', 'v', 'z', 'w') sage: X.cohomology_ring().gens() ([y + v - w], [-y + z + w], [y], [v], [z], [w]) + + TESTS: + + The cohomology ring is a circular reference that is + potentially troublesome on unpickling, see :trac:`15050` :: + + sage: variety = toric_varieties.P(1) + sage: _ = variety.cohomology_ring(), variety.cohomology_basis(), variety.volume_class() + sage: loads(dumps(variety)) == variety + True """ - if "_cohomology_ring" not in self.__dict__: - if self.base_ring().characteristic()>0: - raise NotImplementedError('Only characteristic 0 base fields ' - 'are implemented.') - self._cohomology_ring = CohomologyRing(self) - return self._cohomology_ring + if self.base_ring().characteristic()>0: + raise NotImplementedError('Only characteristic 0 base fields ' + 'are implemented.') + return CohomologyRing(self) + @cached_method def cohomology_basis(self, d=None): r""" Return a basis for the cohomology of the toric variety. @@ -2003,10 +2014,6 @@ def cohomology_basis(self, d=None): if d!=None: return self.cohomology_basis()[d] - try: - return self._cohomology_basis - except AttributeError: - pass H = self.cohomology_ring() # Make an empty list for each d-piece basis = [[] for d in range(self.dimension() + 1)] @@ -2015,11 +2022,10 @@ def cohomology_basis(self, d=None): basis[x.total_degree()].append(x) # Convert list of lists of polynomials to # tuple of tuples of cohomology classes - basis = tuple(tuple(H(x) for x in dbasis) - for dbasis in basis) - self._cohomology_basis = basis - return self._cohomology_basis + return tuple(tuple(H(x) for x in dbasis) + for dbasis in basis) + @cached_method def volume_class(self): r""" Return the cohomology class of the volume form on the toric @@ -2090,16 +2096,15 @@ def volume_class(self): Sheldon Katz and Stein Arild Stromme, A Maple package for intersection theory and enumerative geometry. """ - if "_volume_class" not in self.__dict__: - if not self.is_orbifold(): - raise NotImplementedError('Cohomology computations are only ' - 'implemented for orbifolds.') - HH = self.cohomology_ring() - dim = self.dimension_relative() - self._volume_class = HH(self.fan().generating_cone(0)).part_of_degree(dim) - if self._volume_class.is_zero(): + if not self.is_orbifold(): + raise NotImplementedError('Cohomology computations are only ' + 'implemented for orbifolds.') + HH = self.cohomology_ring() + dim = self.dimension_relative() + dVol = HH(self.fan().generating_cone(0)).part_of_degree(dim) + if dVol.is_zero(): raise ValueError, 'Volume class does not exist.' - return self._volume_class + return dVol def integrate(self, cohomology_class): """ From 0bb8e789ad2dc15f37264830d39ab36fa0a279f5 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Fri, 16 Aug 2013 14:42:22 +0100 Subject: [PATCH 006/206] make sure simplex_points() works with immutable vectors, too --- src/sage/geometry/integral_points.pyx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/integral_points.pyx b/src/sage/geometry/integral_points.pyx index e7d01ea4806..98dc9cedc37 100644 --- a/src/sage/geometry/integral_points.pyx +++ b/src/sage/geometry/integral_points.pyx @@ -280,10 +280,15 @@ def simplex_points(vertices): sage: v = [(4,-1,-1,-1), (-1,4,-1,-1), (-1,-1,4,-1), (-1,-1,-1,4), (-1,-1,-1,-1)] sage: P4mirror = Polyhedron(v); P4mirror A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices - sage: len( simplex_points(P4mirror.Vrepresentation()) ) + sage: len(simplex_points(P4mirror.Vrepresentation())) 126 + + sage: vertices = map(vector, [(1,2,3), (2,3,7), (-2,-3,-11)]) + sage: for v in vertices: v.set_immutable() + sage: simplex_points(vertices) + ((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7)) """ - rays = [ vector(ZZ, v) for v in vertices ] + rays = [vector(ZZ, list(v)) for v in vertices] if len(rays)==0: return tuple() origin = rays.pop() From 93c8a85ccc17ee5b4acb47907d1ad6cc2e37a733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 17 Aug 2013 15:22:45 +0200 Subject: [PATCH 007/206] cleanup of the file incidence_structures.py --- .../combinat/designs/incidence_structures.py | 85 +++++++++---------- 1 file changed, 39 insertions(+), 46 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index a1494095746..02229630242 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -39,10 +39,10 @@ from sage.matrix.matrix_space import MatrixSpace from sage.rings.integer_ring import ZZ from sage.rings.arith import binomial -from sage.misc.decorators import rename_keyword ### utility functions ------------------------------------------------------- + def coordinatewise_product(L): """ Returns the coordinatewise product of a list of vectors. @@ -66,6 +66,7 @@ def coordinatewise_product(L): ans = [ans[i]*x[i] for i in range(n)] return ans + def IncidenceStructureFromMatrix(M, name=None): """ Builds and incidence structure from a matrix. @@ -92,11 +93,12 @@ def IncidenceStructureFromMatrix(M, name=None): for i in range(b): B = [] for j in range(v): - if M[i,j]!=0: + if M[i, j] != 0: B.append(j) blocks.append(B) return IncidenceStructure(range(v), blocks, name=nm) + class IncidenceStructure(object): """ This the base class for block designs. @@ -150,7 +152,7 @@ def __init__(self, pts, blks, inc_mat=None, name=None, test=True): if test: for x in block: if not(x in self.pnts): - raise ValueError('Point %s is not in the base set.'%x) + raise ValueError('Point %s is not in the base set.' % x) try: y = block[:] y.sort() @@ -176,7 +178,6 @@ def __iter__(self): return iter(self.blcks) - def __repr__(self): """ A print method. @@ -188,7 +189,7 @@ def __repr__(self): sage: BD Incidence structure with 7 points and 7 blocks """ - repr = 'Incidence structure with %s points and %s blocks'%(len(self.pnts),len(self.blcks)) + repr = 'Incidence structure with %s points and %s blocks' % (len(self.pnts), len(self.blcks)) return repr def __str__(self): @@ -206,9 +207,11 @@ def __str__(self): IncidenceStructure """ if self.name: - repr = '%s'%(self.name, self.pnts, self.blcks) + repr = '%s' % (self.name, self.pnts, + self.blcks) else: - repr = 'IncidenceStructure'%( self.pnts, self.blcks) + repr = 'IncidenceStructure' % (self.pnts, + self.blcks) return repr def automorphism_group(self): @@ -234,7 +237,7 @@ def automorphism_group(self): from sage.groups.perm_gps.permgroup import PermutationGroup from sage.groups.perm_gps.permgroup_named import SymmetricGroup M1 = self.incidence_matrix() - M2 = MatrixStruct(M1) + M2 = MatrixStruct(M1) M2.run() gens = M2.automorphism_group()[0] v = len(self.points()) @@ -295,7 +298,7 @@ def block_design_checker(self, t, v, k, lmbda, type=None): from sage.sets.set import Set if not(v == len(self.points())): return False - b = lmbda*binomial(v,t)/binomial(k,t) + b = lmbda*binomial(v, t)/binomial(k, t) r = int(b*k/v) if not(b == len(self.blocks())): return False @@ -310,21 +313,20 @@ def block_design_checker(self, t, v, k, lmbda, type=None): for i in range(v): if not(sum(A.rows()[i]) == r): return False - gD = self._gap_() - if type==None: + if type is None: return True - if type=="binary": + if type == "binary": for b in self.blocks(): - if len(b)!=len(Set(b)): - return False + if len(b) != len(Set(b)): + return False return True - if type=="simple": + if type == "simple": B = self.blocks() for b in B: - if B.count(b)>1: - return False + if B.count(b) > 1: + return False return True - if type=="connected": + if type == "connected": Gamma = self.incidence_graph() if Gamma.is_connected(): return True @@ -374,7 +376,7 @@ def block_sizes(self): sage: BD.block_sizes() [3, 3, 3, 3, 3, 3, 3] """ - self._block_sizes = map(len,self.blocks()) + self._block_sizes = map(len, self.blocks()) return self._block_sizes def _gap_(self): @@ -388,12 +390,11 @@ def _gap_(self): sage: BD._gap_() 'BlockDesign(7,[[1, 2, 3], [1, 4, 5], [1, 6, 7], [2, 4, 6], [2, 5, 7], [3, 4, 7], [3, 5, 6]])' """ - from sage.sets.set import Set B = self.blocks() v = len(self.points()) gB = [] for b in B: - gB.append([x+1 for x in b]) + gB.append([x+1 for x in b]) return "BlockDesign("+str(v)+","+str(gB)+")" def dual_incidence_structure(self, algorithm=None): @@ -431,12 +432,9 @@ def dual_incidence_structure(self, algorithm=None): - Soicher, Leonard, Design package manual, available at http://www.gap-system.org/Manuals/pkg/design/htm/CHAP003.htm """ - from sage.interfaces.gap import gap, GapElement - from sage.sets.set import Set - from sage.misc.flatten import flatten - from sage.combinat.designs.block_design import BlockDesign + from sage.interfaces.gap import gap from sage.misc.functional import transpose - if algorithm=="gap": + if algorithm == "gap": gap.load_package("design") gD = self._gap_() gap.eval("DD:=DualBlockDesign("+gD+")") @@ -473,19 +471,19 @@ def incidence_matrix(self): [0 0 1 1 0 0 1] [0 0 1 0 1 1 0] """ - if self._incidence_matrix!=None: + if not self._incidence_matrix is None: return self._incidence_matrix else: v = len(self.points()) blks = self.blocks() b = len(blks) - MS = MatrixSpace(ZZ,v,b) + MS = MatrixSpace(ZZ, v, b) A = MS(0) #A = NUM.zeros((v,b), NUM.Int) for i in range(v): for j, b in enumerate(blks): if i in b: - A[i,j] = 1 + A[i, j] = 1 self._incidence_matrix = A return A @@ -536,7 +534,6 @@ def is_block_design(self): (True, [2, 8, 2, 2]) """ from sage.combinat.designs.incidence_structures import coordinatewise_product - from sage.combinat.combinat import unordered_tuples from sage.combinat.combination import Combinations A = self.incidence_matrix() v = len(self.points()) @@ -553,27 +550,27 @@ def is_block_design(self): return False t_found_yet = False lambdas = [] - for t in range(2,min(v,11)): + for t in range(2, min(v, 11)): #print t - L1 = Combinations(range(v),t) + L1 = Combinations(range(v), t) L2 = [[rowsA[i] for i in L] for L in L1] #print t,len(L2) lmbda = VS(coordinatewise_product(L2[0])).hamming_weight() lambdas.append(lmbda) - pars = [t,v,k,lmbda] + pars = [t, v, k, lmbda] #print pars for ell in L2: a = VS(coordinatewise_product(ell)).hamming_weight() - if not(a == lmbda) or a==0: + if not(a == lmbda) or a == 0: if not(t_found_yet): - pars = [t-1,v,k,lambdas[t-3]] + pars = [t-1, v, k, lambdas[t-3]] return False, pars else: #print pars, lambdas - pars = [t-1,v,k,lambdas[t-3]] + pars = [t-1, v, k, lambdas[t-3]] return True, pars t_found_yet = True - pars = [t-1,v,k,lambdas[t-3]] + pars = [t-1, v, k, lambdas[t-3]] return True, pars def parameters(self, t=2): @@ -596,8 +593,8 @@ def parameters(self, t=2): b = len(blks) #A = self.incidence_matrix() #r = sum(A.rows()[0]) - lmbda = int(b/(binomial(v,t)/binomial(k,t))) - return (t,v,k,lmbda) + lmbda = int(b/(binomial(v, t)/binomial(k, t))) + return (t, v, k, lmbda) def points(self): """ @@ -631,12 +628,8 @@ def points_from_gap(self): from sage.misc.superseded import deprecation deprecation(14499, ('Unless somebody protests this method will be ' 'removed, as nobody seems to know why it is there.')) - from sage.interfaces.gap import gap, GapElement - from sage.sets.set import Set + from sage.interfaces.gap import gap gap.load_package("design") gD = self._gap_() - gP = gap.eval("BlockDesignPoints("+gD+")").replace("..",",") - return range(eval(gP)[0],eval(gP)[1]+1) - - - + gP = gap.eval("BlockDesignPoints("+gD+")").replace("..", ",") + return range(eval(gP)[0], eval(gP)[1]+1) From c968d52f3c563073c11a461ad16fcd4af0ff3564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 17 Aug 2013 15:43:13 +0200 Subject: [PATCH 008/206] correct link to trac --- src/sage/combinat/designs/incidence_structures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 02229630242..2e68916f8a5 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -133,11 +133,11 @@ def __init__(self, pts, blks, inc_mat=None, name=None, test=True): TESTS: - The following shows that Trac Ticket #11333 is fixed. :: + The following shows that :trac:`11333` is fixed. :: sage: A = IncidenceStructure([0,1],[[0]]) sage: B = IncidenceStructure([1,0],[[0]]) - sage: B==A + sage: B == A True REFERENCES: From 92bb1482146368f6739f88027c2d2b21fcc1fa8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 19 Aug 2013 18:35:55 +0200 Subject: [PATCH 009/206] correction in the doc of fast callable --- src/sage/ext/fast_callable.pyx | 186 ++++++++++++++++++++++----------- 1 file changed, 124 insertions(+), 62 deletions(-) diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 8a4315e6516..0927f4d3fad 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -192,7 +192,8 @@ class), implement a method \code{_fast_callable_(self, etb)} for your class. This method takes an \class{ExpressionTreeBuilder}, and returns an expression tree built up using the methods described above. -EXAMPLES: +EXAMPLES:: + sage: var('x') x sage: f = fast_callable(sqrt(x^7+1), vars=[x], domain=float) @@ -323,7 +324,8 @@ def fast_callable(x, domain=None, vars=None, specified (unless x is a symbolic expression with only one variable, and expect_one_var is True, in which case we will use that variable). - EXAMPLES: + EXAMPLES:: + sage: var('x') x sage: expr = sin(x) + 3*x^2 @@ -467,7 +469,8 @@ def function_name(fn): Only used when printing Expressions. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import function_name sage: function_name(operator.pow) 'pow' @@ -492,7 +495,8 @@ cdef class ExpressionTreeBuilder: you can also instantiate it yourself to create your own expressions for fast_callable, bypassing _fast_callable_. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder('x') sage: x = etb.var('x') @@ -517,7 +521,8 @@ cdef class ExpressionTreeBuilder: constants will be converted twice (once when building the Expression, and once when generating code). - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder('x') sage: etb(3^50) @@ -544,7 +549,8 @@ cdef class ExpressionTreeBuilder: method, then call the method with self as an argument. Otherwise, use self.constant() to turn it into a constant. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder('x') sage: v = etb(3); v, type(v) @@ -568,7 +574,8 @@ cdef class ExpressionTreeBuilder: r""" Give a variable name, given a variable. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder('x') sage: var('x') @@ -592,7 +599,8 @@ cdef class ExpressionTreeBuilder: Turn the argument into an ExpressionConstant, converting it to our domain if we have one. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder('x') sage: etb.constant(pi) @@ -610,7 +618,8 @@ cdef class ExpressionTreeBuilder: Turn the argument into an ExpressionVariable. Looks it up in the list of variables. (Variables are matched by name.) - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: var('a,b,some_really_long_name') (a, b, some_really_long_name) @@ -638,7 +647,8 @@ cdef class ExpressionTreeBuilder: r""" Given an integer, return the variable with that index. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=('a','b','c','d')) sage: etb._var_number(0) @@ -664,7 +674,8 @@ cdef class ExpressionTreeBuilder: the second argument is integral, and constructs an ExpressionIPow instead. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: etb.call(cos, x) @@ -691,7 +702,8 @@ cdef class ExpressionTreeBuilder: (It's possible to create choice nodes, but they don't work yet.) - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: etb.choice(etb.call(operator.eq, x, 0), 0, 1/x) @@ -723,7 +735,8 @@ cdef class Expression: non-Expression is converted to an expression (using the __call__ method of the Expression's ExpressionTreeBuilder). - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb.var(x) @@ -747,7 +760,8 @@ cdef class Expression: r""" Initialize an Expression. Sets the _etb member. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: v = etb(3); v # indirect doctest @@ -761,7 +775,8 @@ cdef class Expression: r""" Returns the ExpressionTreeBuilder used to build a given expression. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: v = etb(3); v @@ -775,7 +790,8 @@ cdef class Expression: r""" Compute a sum of two Expressions. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -796,7 +812,8 @@ cdef class Expression: r""" Compute a difference of two Expressions. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -817,7 +834,8 @@ cdef class Expression: r""" Compute a product of two Expressions. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -838,7 +856,8 @@ cdef class Expression: r""" Compute a quotient of two Expressions. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -859,7 +878,8 @@ cdef class Expression: r""" Compute the floordiv (the floor of the quotient) of two Expressions. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -883,7 +903,8 @@ cdef class Expression: If the second Expression is a constant integer, then return an ExpressionIPow instead of an ExpressionCall. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -923,7 +944,8 @@ cdef class Expression: r""" Compute the negation of an Expression. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -938,7 +960,8 @@ cdef class Expression: r""" Compute the absolute value of an Expression. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -955,7 +978,8 @@ cdef class Expression: r""" Compute the absolute value of an Expression. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -972,7 +996,8 @@ cdef class Expression: r""" Compute the inverse of an Expression. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -1001,7 +1026,8 @@ cdef class ExpressionConstant(Expression): r""" Initialize an ExpressionConstant. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionConstant sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: etb(3) @@ -1022,7 +1048,8 @@ cdef class ExpressionConstant(Expression): r""" Return the constant value of an ExpressionConstant. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: etb(3).value() @@ -1035,7 +1062,8 @@ cdef class ExpressionConstant(Expression): Give a string representing this ExpressionConstant. (We use the repr of its value.) - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: v = etb.constant(pi) @@ -1064,7 +1092,8 @@ cdef class ExpressionVariable(Expression): r""" Initialize an ExpressionVariable. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionVariable sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: etb(x) @@ -1083,7 +1112,8 @@ cdef class ExpressionVariable(Expression): r""" Return the variable index of an ExpressionVariable. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: etb(x).variable_index() @@ -1095,7 +1125,8 @@ cdef class ExpressionVariable(Expression): r""" Give a string representing this ExpressionVariable. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: v = etb._var_number(0) @@ -1128,7 +1159,8 @@ cdef class ExpressionCall(Expression): r""" Initialize an ExpressionCall. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionCall sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -1151,7 +1183,8 @@ cdef class ExpressionCall(Expression): r""" Return the function from this ExpressionCall. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: etb.call(sin, x).function() @@ -1163,7 +1196,8 @@ cdef class ExpressionCall(Expression): r""" Return the arguments from this ExpressionCall. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: etb.call(sin, x).arguments() @@ -1175,7 +1209,8 @@ cdef class ExpressionCall(Expression): r""" Give a string representing this ExpressionCall. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb.var(x) @@ -1211,7 +1246,8 @@ cdef class ExpressionIPow(Expression): r""" Initialize an ExpressionIPow. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionIPow sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -1234,7 +1270,8 @@ cdef class ExpressionIPow(Expression): r""" Return the base from this ExpressionIPow. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: (etb(33)^42).base() @@ -1246,7 +1283,8 @@ cdef class ExpressionIPow(Expression): r""" Return the exponent from this ExpressionIPow. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: (etb(x)^(-1)).exponent() @@ -1258,7 +1296,8 @@ cdef class ExpressionIPow(Expression): r""" Give a string representing this ExpressionIPow. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb.var(x) @@ -1297,7 +1336,8 @@ cdef class ExpressionChoice(Expression): r""" Initialize an ExpressionChoice. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder, ExpressionChoice sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -1323,7 +1363,8 @@ cdef class ExpressionChoice(Expression): r""" Return the condition of an ExpressionChoice. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -1336,7 +1377,8 @@ cdef class ExpressionChoice(Expression): r""" Return the true branch of an ExpressionChoice. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -1349,7 +1391,8 @@ cdef class ExpressionChoice(Expression): r""" Return the false branch of an ExpressionChoice. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -1363,7 +1406,8 @@ cdef class ExpressionChoice(Expression): Give a string representation for this ExpressionChoice. (Based on the syntax for Python conditional expressions.) - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder(vars=(x,)) sage: x = etb(x) @@ -1459,7 +1503,8 @@ class IntegerPowerFunction(object): r""" Initializes an IntegerPowerFunction. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import IntegerPowerFunction sage: cube = IntegerPowerFunction(3) sage: cube @@ -1475,7 +1520,8 @@ class IntegerPowerFunction(object): r""" Return a string representing this IntegerPowerFunction. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import IntegerPowerFunction sage: square = IntegerPowerFunction(2) sage: square @@ -1496,7 +1542,8 @@ class IntegerPowerFunction(object): r""" Call this IntegerPowerFunction, to compute a power of its argument. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import IntegerPowerFunction sage: square = IntegerPowerFunction(2) sage: square.__call__(5) @@ -1832,7 +1879,8 @@ cdef class InstructionStream: domain - The domain of interpretation (this is just passed to the wrapper class) - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.interpreters.wrapper_rdf import metadata sage: from sage.ext.fast_callable import InstructionStream sage: instr_stream = InstructionStream(metadata, 1) @@ -1862,7 +1910,8 @@ cdef class InstructionStream: r""" Add a 'load_const' instruction to this InstructionStream. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.interpreters.wrapper_rdf import metadata sage: from sage.ext.fast_callable import InstructionStream, op_list sage: instr_stream = InstructionStream(metadata, 1) @@ -1885,7 +1934,8 @@ cdef class InstructionStream: r""" Add a 'load_arg' instruction to this InstructionStream. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.interpreters.wrapper_rdf import metadata sage: from sage.ext.fast_callable import InstructionStream sage: instr_stream = InstructionStream(metadata, 12) @@ -1903,7 +1953,8 @@ cdef class InstructionStream: Check whether this InstructionStream knows how to generate code for a given instruction. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.interpreters.wrapper_rdf import metadata sage: from sage.ext.fast_callable import InstructionStream sage: instr_stream = InstructionStream(metadata, 1) @@ -1925,7 +1976,8 @@ cdef class InstructionStream: CompilerInstrSpec describes how to interpret the arguments. (This is documented in the class docstring for CompilerInstrSpec.) - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.interpreters.wrapper_rdf import metadata sage: from sage.ext.fast_callable import InstructionStream sage: instr_stream = InstructionStream(metadata, 1) @@ -2002,7 +2054,8 @@ cdef class InstructionStream: The code generator sometimes uses this to decide which code to generate. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.interpreters.wrapper_rdf import metadata sage: from sage.ext.fast_callable import InstructionStream sage: instr_stream = InstructionStream(metadata, 1) @@ -2019,7 +2072,8 @@ cdef class InstructionStream: It's OK to call this, then add more instructions. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.interpreters.wrapper_rdf import metadata sage: from sage.ext.fast_callable import InstructionStream sage: instr_stream = InstructionStream(metadata, 1) @@ -2040,7 +2094,8 @@ cdef class InstructionStream: NOTE: The dictionary includes internal data structures of the InstructionStream; you must not modify it. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.interpreters.wrapper_rdf import metadata sage: from sage.ext.fast_callable import InstructionStream sage: instr_stream = InstructionStream(metadata, 1) @@ -2087,7 +2142,8 @@ cdef class InterpreterMetadata(object): r""" Initialize an InterpreterMetadata object. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import InterpreterMetadata sage: metadata = InterpreterMetadata(by_opname={'opname dict goes here': True}, by_opcode=['opcode list goes here'], ipow_range=(2, 57)) sage: metadata.by_opname @@ -2130,7 +2186,8 @@ class CompilerInstrSpec(object): r""" Initialize a CompilerInstrSpec. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import CompilerInstrSpec sage: CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs']) CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs']) @@ -2143,7 +2200,8 @@ class CompilerInstrSpec(object): r""" Give a string representation for this CompilerInstrSpec. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import CompilerInstrSpec sage: v = CompilerInstrSpec(0, 1, ['py_constants', 'n_inputs']) sage: v @@ -2214,7 +2272,8 @@ cdef class Wrapper: r""" Initialize a Wrapper object. - EXAMPLES: + EXAMPLES:: + sage: from sage.ext.fast_callable import ExpressionTreeBuilder, generate_code, InstructionStream sage: etb = ExpressionTreeBuilder('x') sage: x = etb.var('x') @@ -2244,7 +2303,8 @@ cdef class Wrapper: (Probably only useful when writing doctests.) - EXAMPLES: + EXAMPLES:: + sage: fast_callable(sin(x)/x, vars=[x], domain=RDF).get_orig_args() {'domain': Real Double Field, 'code': [0, 0, 16, 0, 0, 8, 2], 'py_constants': [], 'args': 1, 'stack': 2, 'constants': []} """ @@ -2254,7 +2314,8 @@ cdef class Wrapper: r""" Return the list of instructions in this wrapper. - EXAMPLES: + EXAMPLES:: + sage: fast_callable(cos(x)*x, vars=[x], domain=RDF).op_list() [('load_arg', 0), ('load_arg', 0), 'cos', 'mul', 'return'] """ @@ -2269,7 +2330,8 @@ cdef class Wrapper: optimization opportunity where a Sage developer could speed this up by adding a new instruction to the interpreter.) - EXAMPLES: + EXAMPLES:: + sage: fast_callable(abs(sin(x)), vars=[x], domain=RDF).python_calls() [] sage: fast_callable(abs(sin(factorial(x))), vars=[x]).python_calls() From c9adebf2196c1ef0aeff861dca2ae7e744bf6aab Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Sun, 18 Aug 2013 12:36:50 +0200 Subject: [PATCH 010/206] tutorial: Update URL --- src/doc/en/tutorial/latex.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/tutorial/latex.rst b/src/doc/en/tutorial/latex.rst index d5329d49bae..1f6703b04cf 100644 --- a/src/doc/en/tutorial/latex.rst +++ b/src/doc/en/tutorial/latex.rst @@ -483,7 +483,7 @@ http://sourceforge.net/projects/dvipng/ and as part of Rendering combinatorial graphs requires a recent version of the PGF library, and the files ``tkz-graph.sty``, ``tkz-arith.sty`` and perhaps ``tkz-berge.sty``, all from the `Altermundus site -`_. +`_. External Programs ================= From bab8d92ce146668b1a7733c4f7e77618c18ed5be Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Thu, 15 Aug 2013 12:31:16 +0200 Subject: [PATCH 011/206] tutorial: typos --- src/doc/en/tutorial/latex.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/tutorial/latex.rst b/src/doc/en/tutorial/latex.rst index 1f6703b04cf..300a1e0522d 100644 --- a/src/doc/en/tutorial/latex.rst +++ b/src/doc/en/tutorial/latex.rst @@ -73,7 +73,7 @@ Here we demonstrate some basic uses of the ``latex()`` function. :: \end{array}\right) Basic MathJax functionality is largely automatic in the notebook, but -we can partially demonstrate this support with the ``MathJax`` class, +we can partially demonstrate this support with the ``MathJax`` class. The ``eval`` function of this class converts a Sage object to its LaTeX representation and then wraps it in HTML that invokes the CSS "math" class, which then employs MathJax. :: @@ -211,7 +211,7 @@ done in written work. This is accomplished by redefining the sage: latex.blackboard_bold(False) It is possible to take advantage of the extensible nature of -tex by adding in new macros and new packages. First, +TeX by adding in new macros and new packages. First, individual macros can be added so that they are used when MathJax interprets a snippet of TeX in the notebook. :: From 82648c8109b9654208eb015466893e069c972e1c Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Sun, 18 Aug 2013 14:38:56 +0200 Subject: [PATCH 012/206] tutorial: update links --- src/doc/en/tutorial/tour_coercion.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/doc/en/tutorial/tour_coercion.rst b/src/doc/en/tutorial/tour_coercion.rst index 99f6e0d41e9..2b8d526b2fb 100644 --- a/src/doc/en/tutorial/tour_coercion.rst +++ b/src/doc/en/tutorial/tour_coercion.rst @@ -13,7 +13,7 @@ in Sage effectively and efficiently. Note that we try to explain notions, but we do not show here how to implement them. An implementation-oriented tutorial is available as a -`Sage worksheet `_. +`Sage thematic tutorial `_. Elements -------- @@ -204,8 +204,8 @@ type conversion in C with conversion in Sage! We give here a rather brief account. For a detailed description and for information on the implementation, we refer to the section on -coercion in the reference manual and to the `tutorial worksheet -`_. +coercion in the reference manual and to the +`thematic tutorial `_. There are two extremal positions concerning the possibility of doing arithmetic with elements of *different* rings: @@ -378,7 +378,8 @@ fraction field of ``ZZ['x']``. However, Sage tries to choose a in our example). If several potential common parents seem equally natural, Sage will *not* pick one of them at random, in order to have a reliable result. The mechanisms which that choice is based upon is -explained in the `tutorial worksheet `_ +explained in the +`thematic tutorial `_. No coercion into a common parent will take place in the following example: From c0792f2c20862f8cd4e1f5f713d4866f4e940330 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Thu, 15 Aug 2013 11:02:10 +0200 Subject: [PATCH 013/206] fr tutorial: remove mention of clisp and openmath Corresponding change to the English origial: 454f867577c6b6b418fee97fa5778051e149e3d2 See also trac #8328. --- src/doc/fr/tutorial/interfaces.rst | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/doc/fr/tutorial/interfaces.rst b/src/doc/fr/tutorial/interfaces.rst index ccac9a968a5..3038be647ad 100644 --- a/src/doc/fr/tutorial/interfaces.rst +++ b/src/doc/fr/tutorial/interfaces.rst @@ -253,19 +253,18 @@ Singular (Sage y fera tout de même appel en coulisses pour le calcul). Maxima ====== -Le système de calcul formel Maxima est fourni avec Sage accompagné de -clisp, une version du langage Lisp, et d'openmath, un programme de tracé -de courbes en Tcl/Tk utilisé par Maxima. En revanche, gnuplot (que -Maxima utilise par défaut pour tracer des graphiques) n'est distribué -que comme paquet optionnel de Sage. Maxima fournit notamment des -routines de calcul sur des expressions formelles. Il permet de calculer -des dérivées, primitives et intégrales, de résoudre des équations -différentielles d'ordre 1 et souvent d'ordre 2, et de résoudre par -transformée de Laplace les équations différentielles linéaires d'ordre -quelconque. Maxima dispose aussi d'un grand nombre de fonctions -spéciales, permet de tracer des graphes de fonctions via gnuplot, et de -manipuler des matrices (réduction en lignes, valeurs propres, vecteurs -propres...) ou encore des équations polynomiales. +Le système de calcul formel Maxima est fourni avec Sage accompagné d'une +implémentation du langage Lisp. Le logiciel gnuplot (que Maxima utilise +par défaut pour tracer des graphiques) est disponible comme paquet +optionnel. Maxima fournit notamment des routines de calcul sur des +expressions formelles. Il permet de calculer des dérivées, primitives et +intégrales, de résoudre des équations différentielles d'ordre 1 et +souvent d'ordre 2, et de résoudre par transformée de Laplace les +équations différentielles linéaires d'ordre quelconque. Maxima dispose +aussi d'un grand nombre de fonctions spéciales, permet de tracer des +graphes de fonctions via gnuplot, et de manipuler des matrices +(réduction en lignes, valeurs propres, vecteurs propres...) ou encore +des équations polynomiales. Utilisons par exemple l'interface Sage/Maxima pour construire la matrice dont le coefficient d'indice :math:`i,j` vaut :math:`i/j`, From 9fc1b496427553754bc7c3d329b8da217b276248 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Fri, 16 Aug 2013 09:52:01 +0200 Subject: [PATCH 014/206] fr tutorial: add translation of latex.rst --- src/doc/fr/tutorial/index.rst | 1 + src/doc/fr/tutorial/latex.rst | 469 ++++++++++++++++++++++++++++++++++ 2 files changed, 470 insertions(+) create mode 100644 src/doc/fr/tutorial/latex.rst diff --git a/src/doc/fr/tutorial/index.rst b/src/doc/fr/tutorial/index.rst index 028a12fa9cd..bff89b0ede7 100644 --- a/src/doc/fr/tutorial/index.rst +++ b/src/doc/fr/tutorial/index.rst @@ -38,6 +38,7 @@ __ http://creativecommons.org/licenses/by-sa/3.0/ tour interactive_shell interfaces + latex programming sagetex afterword diff --git a/src/doc/fr/tutorial/latex.rst b/src/doc/fr/tutorial/latex.rst new file mode 100644 index 00000000000..6531f14149a --- /dev/null +++ b/src/doc/fr/tutorial/latex.rst @@ -0,0 +1,469 @@ +********************************* +Sage, LaTeX et compagnie +********************************* + +AUTEUR: Rob Beezer (2010-05-23) + +Sage et le dialecte LaTeX de TeX entretiennent une forte synergie. L'objet de +cette section est de présenter leurs différentes interactions. Nous commençons +par les plus basiques, avant de passer à des fonctionnalités plus obscures. +(Une partie de cette section peut donc être sautée en première lecture.) + +Vue d'ensemble +============== + +Le plus simple pour comprendre comment Sage peut faire appel à LaTeX est +peut-être de passer en revue les trois principales techniques. + + #. Dans Sage, chaque « objet » doit disposer d'une représentation LaTeX. La + représentation d'un objet ``foo`` est accessible par la commande + ``latex(foo)``, utilisable dans le bloc-notes ou en ligne de commande. + Celle-ci renvoie une chaîne de caractères qui, interprétée par TeX en + mode mathématique (entre signes dollar simples par exemple) devrait + produire une représentation raisonnable de l'objet ``foo``. Nous verrons + quelques exemples plus bas. + + On peut ainsi utiliser Sage pour préparer des fragments de document + LaTeX : il suffit de définir ou obtenir par un calcul un objet Sage et + de copier-coller le résultat de ``latex()`` appliqué à l'objet dans le + document en question. + + #. Le bloc-notes de Sage fait par défaut appel à + `MathJax `_ + pour afficher proprement les formules mathématiques dans le navigateur + web. MathJax est un moteur de rendu mathématique open source écrit en + JavaScript compatible avec tous les navigateurs récents. MathJax + interprète un sous-ensemble conséquent de TeX, mais pas la totalité. + Orienté avant tout vers le rendu correct de petits fragments de TeX, il + ne gère par exemple ni les tableaux compliqués ni le découpage en + sections du document. Le rendu automatique des formules mathématiques + dans le bloc-notes fonctionne en convertissant la sortie de ``latex()`` + mentionnée ci-dessus en une portion de document HTML que MathJax sait + interpréter. + + MathJax utilise ses propres polices de caractères vectorielles, et + fournit ainsi un rendu de meilleure qualité que les méthodes d'affichage + d'équations ou d'autres fragments de TeX qui passent par des images + bitmap statiques. + + #. Il est possible de faire appel à une installation extérieure de LaTeX + depuis la ligne de commande de Sage, ou depuis le bloc-notes pour + interpréter du code plus compliqué que ce que MathJax sait traiter. La + distribution Sage inclut pratiquement tout le nécessaire pour compiler + et utiliser le logiciel Sage, à la notable exception de TeX. Il faut + donc installer par ailleurs TeX et quelques utilitaires de conversion + associés pour l'utiliser depuis Sage de cette manière. + +Voici quelques exemples d'utilisation élémentaire de la fonction ``latex()``. :: + + sage: var('z') + z + sage: latex(z^12) + z^{12} + sage: latex(integrate(z^4, z)) + \frac{1}{5} \, z^{5} + sage: latex('a string') + \verb|a|\phantom{\verb!x!}\verb|string| + sage: latex(QQ) + \Bold{Q} + sage: latex(matrix(QQ, 2, 3, [[2,4,6],[-1,-1,-1]])) + \left(\begin{array}{rrr} + 2 & 4 & 6 \\ + -1 & -1 & -1 + \end{array}\right) + +L'utilisation de base de MathJax dans le bloc-notes est largement automatique. +Nous pouvons tout de même en voir quelques exemples en employant la classe +``MathJax``. La méthode ``eval`` de celle-ci convertit un objet Sage en sa +représentation LaTeX, puis emballe le résultat dans du code HTML qui fait +possède la classe CSS "math", laquelle indique de faire appel à MathJax. :: + + sage: from sage.misc.latex import MathJax + sage: mj = MathJax() + sage: var('z') + z + sage: mj(z^12) + + sage: mj(QQ) + + sage: mj(ZZ[x]) + + sage: mj(integrate(z^4, z)) + + +Utilisation de base +=================== + +Comme indiqué dans la vue d'ensemble, la manière la plus simple d'exploiter le +support LaTeX de Sage consiste à appeler la fonction ``latex()`` pour produire +du code LaTeX représentant un objet mathématique. Les chaînes obtenues peuvent +ensuite être incorporées dans des documents LaTeX indépendants. Cela fonctionne +de la même manière dans le bloc-notes et en ligne de commande. + +L'autre extrême est la commande ``view()``, qui fait tout le nécessaire pour +afficher le rendu correspondant au code LaTeX. + +En ligne de commande, ``view(foo)`` produit la représentation LaTeX de ``foo``, +la place dans un document LaTeX simple, et compile ce document en utilisant +l'installation de TeX du système. Elle appelle ensuite un autre programme +externe pour afficher le résultat de la compilation. La version de TeX (et donc +le format de sortie) ainsi que la visionneuse à utiliser sont configurables, +voir :ref:`sec-custom-processing`. + +Dans le bloc-notes, ``view(foo)`` produit une combinaison de HTML et CSS qui +indique à MathJax de s'occuper du rendu de la représentation LaTeX. +L'utilisateur voit une version joliment formatée de la sortie à la place de la +sortie ASCII par défaut de Sage. Certains objets ont cependant des +représentations LaTeX trop compliquées pour être affichés par MathJax. Lorsque +c'est le cas, il est possible de contourner l'interprétation par MathJax, +d'appeler l'installation LaTeX du système, et de convertir le document produit +en image pour l'afficher dans le bloc-note. La section +:ref:`sec-custom-generation` ci-dessous explique comment configurer et +contrôler ce processus. + +Le bloc-notes dispose de deux autres fonctionnalités pour appeler LaTeX. +Premièrement, lorsque la case « Typeset » (juste au-dessus de la première +cellule d'une feuille de travail, à droite des quatre listes déroulantes) est +cochée, le résultat de l'évaluation d'une cellule est automatiquement +interprété par MathJax et affiché sous forme de formule plutôt que de texte +brut. Les sorties déjà affichées ne sont pas modifiées tant que l'on ne +ré-évalue pas les cellules correspondantes. Cocher la case « Typeset » revient +essentiellement à appeler ``view()`` sur le résultat de chaque cellule. + +Deuxièmement, le bloc-notes permet d'annoter une feuille de travail en +saisissant du TeX. Un clic en tenant la touche Maj enfoncée sur la barre bleue +qui apparaît lorsque l'on place le curseur de la souris entre deux cellules +ouvre un mini-traitement de texte appelé TinyMCE. Cela permet de saisir du +texte pour commenter la feuille de travail, et de le mettre en forme avec un +éditeur WYSIWIG de HTML et CSS. Mais le texte placé entre signes dollar simples +ou doubles est interprété par MathJax, respectivement comme formule composée en +ligne ou hors texte. + +.. _sec-custom-generation: + +Personnaliser le code LaTeX produit +=================================== + +Les méthodes de l'objet prédéfini ``latex`` permettent de personnaliser le code +LaTeX produit par la commande ``latex()`` de différentes manières. Cela +s'applique dans le bloc-notes comme en ligne de commande. On obtient la liste +des méthodes en saisissant ``latex.`` (noter la présence du point) puis en +appuyant sur la touche tabulation. + +Un bon exemple est la méthode ``latex.matrix_delimiters``, qui sert à modifier +les symboles entourant les matrices -- parenthèses, crochets, accolades +ou barres verticales par exemple. Les délimiteurs gauche et droit sont +donnés par des chaînes LaTeX. Ils n'ont pas besoin de se correspondre. Notons +comment les contre-obliques qui doivent être interprétées par TeX sont +protégées par une seconde contre-oblique dans l'exemple ci-dessous. :: + + sage: A = matrix(ZZ, 2, 2, range(4)) + sage: latex(A) + \left(\begin{array}{rr} + 0 & 1 \\ + 2 & 3 + \end{array}\right) + sage: latex.matrix_delimiters(left='[', right=']') + sage: latex(A) + \left[\begin{array}{rr} + 0 & 1 \\ + 2 & 3 + \end{array}\right] + sage: latex.matrix_delimiters(left='\\{', right='\\}') + sage: latex(A) + \left\{\begin{array}{rr} + 0 & 1 \\ + 2 & 3 + \end{array}\right\} + +La méthode ``latex.vector_delimiters`` fonctionne de manière analogue. + +Les anneaux et corps usuels (entiers, rationnels, réels, etc.) sont par défaut +composés en gras. La méthode ``latex.blackboard_bold`` permet de changer pour +des lettres ajourées. Elle ne change pas la sortie de la commande ``latex()`` +mais la définition de la macro TeX ``\Bold{}`` fournie par Sage. :: + + sage: latex(QQ) + \Bold{Q} + sage: from sage.misc.latex import MathJax + sage: mj=MathJax() + sage: mj(QQ) + + sage: latex.blackboard_bold(True) + sage: mj(QQ) + + sage: latex.blackboard_bold(False) + +On peut aussi définir de nouvelles macros TeX ou charger des packages +supplémentaires. L'exemple suivant montre comment ajouter des macros qui seront +utilisées à chaque fois que MathJax interprète un fragment de TeX dans le +bloc-notes. :: + + sage: latex.extra_macros() + '' + sage: latex.add_macro("\\newcommand{\\foo}{bar}") + sage: latex.extra_macros() + '\\newcommand{\\foo}{bar}' + sage: var('x y') + (x, y) + sage: latex(x+y) + x + y + sage: from sage.misc.latex import MathJax + sage: mj=MathJax() + sage: mj(x+y) + + +Ces macros supplémentaires sont disponibles aussi quand Sage appelle TeX pour +compiler un fragment de document trop gros pour MathJax. C'est la fonction +``latex_extra_preamble``, appelée pour préparer le préambule du document LaTeX, +qui les définit, comme l'illustre l'exemple suivant. Notons à nouveau le +dédoublement des ``\`` dans les chaînes Python. :: + + sage: latex.extra_macros('') + sage: latex.extra_preamble('') + sage: from sage.misc.latex import latex_extra_preamble + sage: print latex_extra_preamble() + \newcommand{\ZZ}{\Bold{Z}} + ... + \newcommand{\Bold}[1]{\mathbf{#1}} + sage: latex.add_macro("\\newcommand{\\foo}{bar}") + sage: print latex_extra_preamble() + \newcommand{\ZZ}{\Bold{Z}} + ... + \newcommand{\Bold}[1]{\mathbf{#1}} + \newcommand{\foo}{bar} + +On peut aussi charger des packages LaTeX, ou ajouter n'importe quelle autre +commande au préambule, grâce à la méthode ``latex.add_package_to_preamble``. Sa +variante plus spécialisée ``latex.add_package_to_preamble_if_available`` +vérifie qu'un package donné est disponible avant de l'ajouter au préambule si +c'est bien le cas. + +Dans l'exemple suivant, nous ajoutons au préambule la commande qui charge le +package ``geometry``, puis nous l'utilisons pour régler la taille de la zone de +texte (et donc indirectement les marges) du document TeX. Une fois encore, les +contre-obliques sont dédoublées. :: + + sage: from sage.misc.latex import latex_extra_preamble + sage: latex.extra_macros('') + sage: latex.extra_preamble('') + sage: latex.add_to_preamble('\\usepackage{geometry}') + sage: latex.add_to_preamble('\\geometry{letterpaper,total={8in,10in}}') + sage: latex.indirectement les marges) du document LaTeX. ) + '\\usepackage{geometry}\\geometry{letterpaper,total={8in,10in}}' + sage: print latex_extra_preamble() + \usepackage{geometry}\geometry{letterpaper,total={8in,10in}} + \newcommand{\ZZ}{\Bold{Z}} + ... + \newcommand{\Bold}[1]{\mathbf{#1}} + +Voici enfin comment ajouter un package en vérifiant sa disponibilité, et ce +qu'il se passe quand le package n'existe pas. :: + + sage: latex.extra_preamble('') + sage: latex.extra_preamble() + '' + sage: latex.add_to_preamble('\\usepackage{foo-bar-unchecked}') + sage: latex.extra_preamble() + '\\usepackage{foo-bar-unchecked}' + sage: latex.add_package_to_preamble_if_available('foo-bar-checked') + sage: latex.extra_preamble() + '\\usepackage{foo-bar-unchecked}' + +.. _sec-custom-processing: + +Personnaliser le traitement du code par LaTeX +============================================= + +En plus de modifier LaTeX produit par Sage, on peut choisir la variante de +TeX appelée pour le traiter, et donc la nature du document produit. De même, il +est possible de contrôler dans quelles circonstances le bloc-notes utilise +MathJax (c'est-à-dire quels fragments de code TeX sont jugés suffisamment +simples) et quand est-ce qu'il se rabat sur l'installation de TeX du système. + +La méthode ``latex.engine()`` permet de choisir lequel des moteurs TeX +``latex``, ``pdflatex`` et ``xelatex`` doit servir à compiler les expressions +LaTeX complexes. Lorsque l'on appelle ``view`` en ligne de commande, si le +moteur actif est ``latex``, celui-ci produit un fichier dvi, puis Sage fait +appel à une visionneuse dvi (par exemple xdvi) pour afficher le résultat. Si en +revanche le moteur est ``pdflatex``, il produit par défaut un fichier PDF, que +Sage affiche grâce à la visionneuse PDF du système (Adobe Reader, Okular, +evince...). + +Dans le bloc-notes, la première étape est de décider s'il faut utiliser MathJax +ou LaTeX pour interpréter un fragment de TeX donné. La décision se fonde sur +une liste de chaînes « interdites » dont la présence dans le fragment indique +d'appeler latex (ou plus généralement le moteur choisi via ``latex.engine()``) +au lieu MathJax. Les méthodes ``latex.add_to_mathjax_avoid_list`` et +``latex.mathjax_avoid_list`` permettent de gérer le contenu de cette liste. :: + + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() + [] + sage: latex.mathjax_avoid_list(['foo', 'bar']) + sage: latex.mathjax_avoid_list() + ['foo', 'bar'] + sage: latex.add_to_mathjax_avoid_list('tikzpicture') + sage: latex.mathjax_avoid_list() + ['foo', 'bar', 'tikzpicture'] + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() + [] + +Supposons maintenant que, dans le bloc-notes, un appel à ``view()`` ou +l'évaluation d'une cellule lorsque la case "Typeset" est cochée produise un +résultat dont le mécanisme décrit ci-dessus détermine qu'il doit être passé au +moteur LaTeX externe. Comme en ligne de commande, l'exécutable spécifié par +``latex.engine()`` traite alors le document. Cependant, au lieu d'appeler une +visionneuse externe pour afficher le document produit, Sage tente de recadrer +le document en rognant les zones blanches, et de le convertir en une image qui +est ensuite insérée dans le bloc-notes comme sortie associée à la cellule. + +Plusieurs facteurs influencent la conversion, principalement le moteur TeX +choisi et la palette d'utilitaires de conversion disponibles sur le système. +Les convertisseurs suivants couvrent à eux quatre toutes les situations : +``dvips``, ``ps2pdf``, ``dvipng`` et ``convert`` (de la collection ImageMagick). +Dans tous les cas, il s'agit d'arriver à produire une image PNG à insérer dans +la feuille de travail. Lorsque le moteur LaTeX produit un fichier dvi, le +programme dvipng suffit en général à effectuer la conversion. Il peut cependant +arriver que le fichier dvi contienne des instructions spécifiques à un pilote +(commande TeX ``\special``) que dvipng est incapable d'interpréter, auquel cas +on utilise ``dvips`` pour créer un fichier PostScript. Celui-ci, de même que +le fichier PDF produit par le moteur le cas échéant, est ensuite converti en +image png avec ``convert``. Les commandes ``have_dvipng()`` et +``have_convert()`` permettent de tester la présence sur le système des +utilitaires en question. + +Toutes ces conversions sont automatiques lorsque les outils nécessaires sont +installés. Dans le cas contraire, un message d'erreur indique ce qu'il manque +et où le télécharger. + +La section suivante (:ref:`sec-tkz-graph`) présente un exemple concret de +traitement d'expressions LaTeX complexes, en l'occurrence pour obtenir un rendu +de qualité de graphes grâce au package LaTeX ``tkz-graph``. Les tests inclus +dans Sage contiennent d'autres exemples. On y accède en important l'objet +prédéfini ``sage.misc.latex.latex_examples``, instance de la classe +``sage.misc.latex.LatexExamples``, comme illustré ci-dessous. Les exemples +fournis actuellement couvrent les types d'objets suivants : diagrammes +commutatifs (utilisant le package `xy`), graphes combinatoires (`tkz-graph`), +nœuds (`xypic`), schémas pstricks (`pstricks`). Pour obtenir la liste des +exemples, utilisez la complétion de ligne de commande après avoir importé +``latex_examples``. Chaque exemple affiche quand on l'appelle des instructions +sur la configuration nécessaire pour qu'il fonctionne correctement. Une fois le +préambule, le moteur LaTeX etc. configurés comme indiqué, il suffit d'appeler +la commande ``view()`` pour visualiser l'exemple. :: + + sage: from sage.misc.latex import latex_examples + sage: latex_examples.diagram() + LaTeX example for testing display of a commutative diagram produced + by xypic. + + To use, try to view this object -- it won't work. Now try + 'latex.add_to_preamble("\\usepackage[matrix,arrow,curve,cmtip]{xy}")', + and try viewing again -- it should work in the command line but not + from the notebook. In the notebook, run + 'latex.add_to_mathjax_avoid_list("xymatrix")' and try again -- you + should get a picture (a part of the diagram arising from a filtered + chain complex). + +.. _sec-tkz-graph: + +Example : rendu de graphes avec tkz-graph +========================================= + +Le package ``tkz-graph`` permet de produire des dessins de graphes +(combinatoires) de qualité. Il repose sur TikZ, lui-même une interface pour la +bibliothèque TeX pgf : pgf, TikZ et tkz-graph doivent donc tous être présents +dans l'installation TeX du système pour que cet exemple fonctionne. Les +versions fournies par certaines distributions TeX sont parfois trop anciennes, +et il peut donc être souhaitable de les installer manuellement dans son arbre +texmf personnel. On consultera la documentation de la distribution TeX pour la +procédure à suivre, qui dépasse le cadre de ce document. La section +:ref:`sec-system-wide-tex` donne la liste des fichiers nécessaires. + +Il nous faut tout d'abord nous assurer que les packages requis sont inclus dans +le document LaTeX, en les ajoutant au préambule. Le rendu des graphes n'est pas +correct quand on passe par le format dvi, aussi il est préférable de +sélectionner ``pdflatex`` comme moteur TeX. Après ces réglages, une instruction +du genre ``view(graphs.CompleteGraph(4))`` saisie dans l'interface en ligne de +commande doit produire un fichier PDF contenant un dessin du graphe complet +`K_4`. + +Pour que la même chose fonctionne dans le bloc-notes, il faut de plus +désactiver l'interprétation du code LaTeX produisant le graphe par MathJax, à +l'aide de la liste de motifs exclus. Le nom de l'environnement ``tikzpicture``, +dans lequel sont placés les graphes, est un bon choix de chaîne à exclure. Une +fois cela fait, la commande ``view(graphs.CompleteGraph(4))`` dans une feuille +de travail du bloc-notes appelle pdflatex pour produire un fichier PDF, puis +``convert`` pour en extraire une image PNG à placer dans la zone de sortie de +la feuille de travail. Les commandes suivantes reprennent l'ensemble des +étapes de configuration. :: + + sage: from sage.graphs.graph_latex import setup_latex_preamble + sage: setup_latex_preamble() + sage: latex.extra_preamble() # random - depends on system's TeX installation + '\\usepackage{tikz}\n\\usepackage{tkz-graph}\n\\usepackage{tkz-berge}\n' + sage: latex.engine('pdflatex') + sage: latex.add_to_mathjax_avoid_list('tikzpicture') + sage: latex.mathjax_avoid_list() + ['tikzpicture'] + +La mise en forme du graphe est faite en traitant des commandes ``tkz-graph`` +qui le décrivent avec ``pdflatex``. Diverses options pour influencer ce rendu, +qui sortent du cadre de cette section, sont décrites dans la section intitulée +"LaTeX Options for Graphs" du manuel de référence de Sage. + +.. _sec-system-wide-tex: + +Une installation TeX pleinement opérationnelle +============================================== + +Beaucoup de fonctionnalités avancées de l'intégration Sage-TeX nécessitent +qu'une installation extérieure de TeX soit disponible sur le système. Les +distributions Linux en fournissent généralement, sous forme de paquets basés +sur TeX Live ; sous OS X, on peut installer TeXshop ; et sous Windows, MikTeX. +L'utilitaire ``convert`` fait partie de la boîte à outils `ImageMagick +`_ (probablement disponible dans l'archive de +paquets de votre système ou facile à télécharger et installer). Les programmes +``dvipng``, ``ps2pdf``, and ``dvips`` sont parfois inclus dans les +installations de TeX, et les deux premiers sont par ailleurs disponibles +respectivement à l'adresse http://sourceforge.net/projects/dvipng/ et dans +`Ghostscript `_. + +Le rendu des graphes nécessite une version suffisamment récente de PGF, ainsi +que les fichiers ``tkz-graph.sty``, ``tkz-arith.sty`` et suivant les cas ``tkz-berge.sty``, tous issus du site web `Altermundus `_ (`version anglaise +`_). + +Programmes externes +=================== + +Trois programmes séparés contribuent encore à l'intégration TeX-Sage. + +Le premier, sagetex, est (pour simplifier) une collection de macros TeX qui +permettent d'introduire dans un document LaTeX des instructions qui seront +interprétées par Sage pour effectuer des calculs et/ou mettre en forme des +objets mathématiques avec la commande ``latex()`` de Sage. Il est donc possible +de faire faire des calculs à Sage et de produire les sorties LaTeX associées +comme étape intermédiaire de la compilation d'un document LaTeX. Par exemple, +on peut imaginer de maintenir la correspondance entre questions et réponses +dans un sujet d'examen en utilisant Sage pour calculer les unes à partir des +autres. Sagetex est décrit plus en détail en section :ref:`sec-sagetex` de ce +document. + +tex2sws est un convertisseur LaTeX vers feuille de travail Sage. Il prend lui +aussi en entrée un document LaTeX contenant du code Sage dans des +environnements spécifiques. Après traitement convenable, on obtient une feuille +de travail pour le bloc-notes, dans laquelle les formules du document de départ +sont affichées avec MathJax et le code Sage repris dans des cellules d'entrée. +Ainsi, un manuel ou un article initialement rédigé avec LaTeX qui contient du +code Sage peut être transformé en une page web interactive où les formules +mathématiques restent formatées correctement tandis que les blocs de code Sage +deviennent exécutables. Cet outil est en cours de développement, on consultera +la page `tex2sws @ BitBucket `_ pour +plus d'information. + +sws2tex fait l'inverse : il part d'une feuille de travail Sage, qu'il convertit +en document LaTeX pour permettre de la traiter ensuite avec tous les outils +disponibles pour les documents LaTeX. sws2tex est en cours de développement, on +pourra se référer à la page `sws2tex @ BitBucket +`_ pour plus d'information. From 946f3f0444719d4497ea95fa582c72ced9132fb3 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Sun, 18 Aug 2013 14:23:43 +0200 Subject: [PATCH 015/206] fr tutorial: document %edit and friends Corresponding changes to the original version: 2a193149aed587b5026b07b3726647054c9845d4 6664ebedb003253107df7c22fb55da5f1fbda6a3 See also trac #11219, #13823, #14051 --- src/doc/fr/tutorial/interactive_shell.rst | 77 ++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/doc/fr/tutorial/interactive_shell.rst b/src/doc/fr/tutorial/interactive_shell.rst index e808c1a4cb6..de832557991 100644 --- a/src/doc/fr/tutorial/interactive_shell.rst +++ b/src/doc/fr/tutorial/interactive_shell.rst @@ -51,7 +51,8 @@ Une session est la suite des entrées et sorties qui interviennent entre le moment où vous démarrez Sage et celui où vous le quittez. Sage enregistre un journal de toutes les entrées via IPython. Si vous utilisez le shell interactif (par opposition à l'interface *notebook*), -vous pouvez taper ``%hist`` à n'importe quel moment pour obtenir la +vous pouvez taper ``%history`` (ou ``%hist``) à n'importe +quel moment pour obtenir la liste de toutes les lignes de commandes entrées depuis le début de la session. Tapez ``?`` à l'invite de commande Sage pour plus d'informations sur IPython. Par exemple : « IPython fournit des invites @@ -376,6 +377,80 @@ la machine ``sage.math.washington.edu``). Mais en raison du surcoût de l'interface pexpect, la comparaison avec Sage, qui est le plus rapide, n'est pas vraiment équitable. +Trucs et astuces IPython +======================== + +Comme signalé plus haut, Sage utilise l'interpréteur de commandes IPython, et +met donc à votre disposition toutes les commandes et fonctionnalités de +celui-ci. Vous voudrez peut-être consulter la `documentation complète de IPython `_. Voici en attendant quelques astuces utiles -- qui reposent sur ce que IPython appelle des « commandes magiques » : + +- La commande magique ``%bg`` lance une commande en arrière-plan. Le résultat + sera ensuite accessible à travers l'objet ``jobs``, comme dans l'exemple + ci-dessous. (Les commentaires « not tested » sont là parce que ``%bg`` ne + fonctionne pas correctement dans l'infrastructure de test automatisé de Sage, + mais si vous reproduisez l'exemple, il devrait fonctionner comme indiqué. + Naturellement, ``%bg`` est surtout utile pour les commandes dont l'exécution + prend beaucoup de temps.) + + :: + + sage: def quick(m): return 2*m + sage: %bg quick(20) # not tested + Starting job # 0 in a separate thread. + sage: jobs.status() # not tested + Completed jobs: + 0 : quick(20) + sage: jobs[0].result # the actual answer, not tested + 40 + + Attention, les tâches lancées en arrière-plan ignorent le préprocesseur Sage + (voir section :ref:`section-mathannoy`). Une manière (certes pas très + commode) de contourner le problème est la suivante :: + + sage: %bg eval(preparse('quick(20)')) # not tested + + Mais il est plus simple et plus sûr de réserver ``%bg`` aux commandes en pur + Python, qui ne font pas appel au préprocesseur. + +- Lorsque l'on souhaite saisir un morceau de code complexe, on peut utiliser + ``%edit`` (ou ``%ed``, ou ``ed``) pour ouvrir un éditeur de texte. + Assurez-vous que la variable d'environnement :envvar:`EDITOR` est réglée à + votre éditeur favori au démarrage de Sage (en plaçant si nécessaire quelque + chose du genre ``export EDITOR=/usr/bin/emacs`` ou encore ``export + EDITOR=/usr/bin/vim`` dans un fichier de configuration convenable, par + exemple ``.profile``). La commande ``%edit`` à l'invite de Sage ouvrira + l'éditeur sélectionné. Vous pouvez alors par exemple saisir une définition de + fonction:: + + def some_function(n): + return n**2 + 3*n + 2 + + puis enregistrer le fichier et quitter l'éditeur. La fonction + ``some_function`` est désormais disponible dans votre session Sage, et vous + pouvez la modifier en saisissant ``edit some_function`` à l'invite de + commande. + +- Si vous souhaitez reprendre une version modifiée du résultat d'un calcul dans + une nouvelle commande, tapez ``%rep`` après avoir fait le calcul. Cela + récupère le texte du résultat et le place sur la ligne de commande, prêt à + être modifié. :: + + sage: f(x) = cos(x) + sage: f(x).derivative(x) + -sin(x) + + Ainsi, après les commandes ci-dessus, la commande ``%rep`` fournit un nouvel + invite de commande pré-rempli avec le texte ``-sin(x)`` et le curseur en fin + de ligne. + +Pour plus d'information, entrez la commande ``%quickref`` pour un résumé des +possibilités de IPython. Au moment où cette documentation est écrite +(avril 2011), Sage emploie IPython 0.9.1. La `documentation des commandes +magiques +`_ +est disponible en ligne, et divers aspects un peu plus avancés de leur +fonctionnement sont décrits `ici `_. + Erreurs et exceptions ===================== From e3b706590315b54cbdac2a7a374eb57bba2bf224 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Sun, 18 Aug 2013 14:30:50 +0200 Subject: [PATCH 016/206] fr tutorial: reflect changes in bc8058497885a8a82793d5d621366db2ac14b540 --- src/doc/fr/tutorial/interfaces.rst | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/doc/fr/tutorial/interfaces.rst b/src/doc/fr/tutorial/interfaces.rst index 3038be647ad..ee0e63e2b72 100644 --- a/src/doc/fr/tutorial/interfaces.rst +++ b/src/doc/fr/tutorial/interfaces.rst @@ -1,3 +1,5 @@ +.. linkall + ********** Interfaces ********** @@ -105,8 +107,6 @@ Commençons par créer une liste PARI à partir d'une liste Python. En Sage, les objets PARI sont de type ``py_pari.gen``. Le type PARI de l'objet sous-jacent est donné par la méthode ``type``. -.. link - :: sage: v.type() @@ -117,8 +117,6 @@ Pour créer une courbe elliptique en PARI, on utiliserait ``ellinit`` devient une méthode qui peut être appelée sur n'importe quel objet PARI, par exemle notre ``t_VEC v``. -.. link - :: sage: e = v.ellinit() @@ -130,8 +128,6 @@ objet PARI, par exemle notre ``t_VEC v``. À présent que nous disposons d'une courbe elliptique, faisons quelques calculs avec. -.. link - :: sage: e.elltors() @@ -217,8 +213,6 @@ fournie par Sage (n'entrez pas les ``...``) : Maintenant que nous avons défini :math:`f`, affichons-le puis factorisons-le. -.. link - :: sage: f From 01effb9ac745fce416e7d141b814c88611296c8e Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 19 Aug 2013 10:07:22 +0200 Subject: [PATCH 017/206] fr tutorial: add translation of tour_coercion.rst See also trac #8821. --- src/doc/fr/tutorial/programming.rst | 13 +- src/doc/fr/tutorial/tour.rst | 1 + src/doc/fr/tutorial/tour_coercion.rst | 395 ++++++++++++++++++++++++++ 3 files changed, 403 insertions(+), 6 deletions(-) create mode 100644 src/doc/fr/tutorial/tour_coercion.rst diff --git a/src/doc/fr/tutorial/programming.rst b/src/doc/fr/tutorial/programming.rst index d6557b3ccba..17dfc6a772b 100644 --- a/src/doc/fr/tutorial/programming.rst +++ b/src/doc/fr/tutorial/programming.rst @@ -712,12 +712,13 @@ Pour évaluer des inégalités symboliques, utilisez ``bool`` : sage: bool(x < x + 1) True -Lorsque l'on cherche à comparer des objets de types différents, Sage -essaie le plus souvent de trouver une coercition canonique des deux -objets dans un même parent. Si cela réussit, la comparaison est faite -entre les objets convertis ; sinon, les objets sont simplement considérés -comme différents. Pour tester si deux variables font référence au même -objet, on utilise l'opérateur ``is``. +Lorsque l'on cherche à comparer des objets de types différents, Sage essaie le +plus souvent de trouver une coercition canonique des deux objets dans un même +parent (voir la section :ref:`section-coercion` pour plus de détails). Si cela +réussit, la comparaison est faite entre les objets convertis ; sinon, les +objets sont simplement considérés comme différents. Pour tester si deux +variables font référence au même objet, on utilise l'opérateur ``is``. Ainsi, +l'entier Python (``int``) ``1`` est unique, mais pas l'entier Sage ``1`` : :: diff --git a/src/doc/fr/tutorial/tour.rst b/src/doc/fr/tutorial/tour.rst index 21c9042e832..94a80e67434 100644 --- a/src/doc/fr/tutorial/tour.rst +++ b/src/doc/fr/tutorial/tour.rst @@ -29,6 +29,7 @@ il vous faudra peut-être appuyer sur ``maj-return`` plutôt que tour_functions tour_rings tour_polynomial + tour_coercion tour_linalg tour_groups tour_numtheory diff --git a/src/doc/fr/tutorial/tour_coercion.rst b/src/doc/fr/tutorial/tour_coercion.rst new file mode 100644 index 00000000000..3c150689ac8 --- /dev/null +++ b/src/doc/fr/tutorial/tour_coercion.rst @@ -0,0 +1,395 @@ +.. -*- coding: utf-8 -*- + +.. _section-coercion: + +================================= +Parents, conversions, coercitions +================================= + +Cette section peut paraître plus technique que celles qui précèdent, mais nous +pensons qu'il est important de comprendre ce que sont les parents et les +coercitions pour utiliser comme il faut les structures algébriques fournies par +Sage. + +Nous allons voir ici ce que ces notions signifient, mais pas comment les mettre +en œuvre pour implémenter une nouvelle structure algébrique. Un tutorial +thématique couvrant ce point est disponible `ici `_. + +Éléments +-------- + +Une première approximation en Python de la notion mathématique d'anneau +pourrait consister à définir une classe pour les éléments ``X`` de l'anneau +concerné, de fournir les méthodes « double-underscore » nécessaires pour donner +un sens aux opérations de l'anneau, par exemple ``__add__``, ``__sub__`` et +``__mul__``, et naturellement de s'assurer qu'elles respectent les axiomes de +la structure d'anneau. + +Python étant un language (dynamiquement) fortement typé, on pourrait s'attendre +à devoir implémenter une classe pour chaque anneau. Après tout, Python définit +bien un type ```` pour les entiers, un type ```` pour les réels, et +ainsi de suite. Mais cette approche ne peut pas fonctionner : il y a une +infinité d'anneaux différents, et l'on ne peut pas implémenter une infinité de +classes ! + +Une autre idée est de créer une hiérarchie de classes destinées à implémenter +les éléments des structures algébriques usuelles : éléments de groupes, +d'anneaux, d'algèbres à division, d'anneaux commutatifs, de corps, d'algèbres, +etc. + +Mais cela signifie que des éléments d'anneaux franchement différents peuvent +avoir le même type. + +:: + + sage: P. = GF(3)[] + sage: Q. = GF(4,'z')[] + sage: type(x)==type(a) + True + +On pourrait aussi vouloir avoir des classes Python différentes pour fournir +plusieurs implémentations d'une même structure mathématique (matrices denses +contre matrices creuses par exemple). + +:: + + sage: P. = PolynomialRing(ZZ) + sage: Q. = PolynomialRing(ZZ, sparse=True) + sage: R. = PolynomialRing(ZZ, implementation='NTL') + sage: type(a); type(b); type(c) + + + + +Deux problèmes se posent alors. D'une part, si deux éléments sont instances de +la même classe, on s'attend à ce que leur méthode ``__add__`` soit capable de +les additionner, alors que ce n'est pas ce que l'on souhaite si les éléments +appartiennent en fait à des anneaux différents. D'autre part, si l'on a deux +éléments qui appartiennent à des implémentations différentes d'un même anneau, +on veut pouvoir les ajouter, et ce n'est pas immédiats s'ils ne sont pas +instances de la même classe. + +La solution à ces difficultés est fournie par le mécanisme de *coercition* +décrit ci-dessous. + +Mais avant tout, il est essentiel que chaque élément « sache » de quoi il est +élément. Cette information est donnée par la méthode ``parent()``. + +.. link + +:: + + sage: a.parent(); b.parent(); c.parent() + Univariate Polynomial Ring in a over Integer Ring + Sparse Univariate Polynomial Ring in b over Integer Ring + Univariate Polynomial Ring in c over Integer Ring (using NTL) + + +Parents et catégories +--------------------- + +En plus d'une hiérarchie de classes destinée à implémenter les éléments de +structures algébriques, Sage fournit une hiérarchie similaire pour les +structures elles-mêmes. Ces structures s'appellent en Sage des *parents*, et +leurs classes dérivent d'une même classe de base. Celle-ci a des sous-classes +« ensemble », « anneau », « corps », et ainsi de suite, dont la hiérarchie +correspond à peu près à celle des concepts mathématiques qu'elles décrivent : + +:: + + sage: isinstance(QQ,Field) + True + sage: isinstance(QQ, Ring) + True + sage: isinstance(ZZ,Field) + False + sage: isinstance(ZZ, Ring) + True + +Or en algèbre, on regroupe les objets qui partagent le même genre de structure +algébrique en ce que l'on appelle des *catégories*. Il y a donc un parallèle +approximatif entre la hiérarchie des classes de Sage et la hiérarchie des +catégories. Mais cette correspondance n'est pas parfaite, et Sage implémente +par ailleurs les catégories en tant que telles : + +:: + + sage: Rings() + Category of rings + sage: ZZ.category() + Category of euclidean domains + sage: ZZ.category().is_subcategory(Rings()) + True + sage: ZZ in Rings() + True + sage: ZZ in Fields() + False + sage: QQ in Fields() + True + +Tandis que la hiérarchie des classes est déterminée avant tout par des +considérations de programmation, l'infrastructure des catégories cherche plutôt +à respecter la structure mathématique. Elle permet de munir les objets d'une +catégorie de méthodes et de tests génériques, qui ne dépendent pas de +l'implémentation particulière d'un objet donné de la catégorie. + +Les parents en tant qu'objets Python doivent être uniques. Ainsi, lorsqu'un +anneau de polynômes sur un anneau donné et avec une liste donnée de générateurs +est construit, il est conservé en cache et réutilisé par la suite : + +:: + + sage: RR['x','y'] is RR['x','y'] + True + + +Types et parents +---------------- + +Il ne faut pas confondre le type ``RingElement`` avec la notion mathématique +d'élément d'anneau : il peut arriver que pour des raisons pratiques, un objet +soit de type ``RingElement`` alors qu'il n'appartient pas à un anneau : + +:: + + sage: M = Matrix(ZZ,2,3); M + [0 0 0] + [0 0 0] + sage: isinstance(M, RingElement) + True + +Si les *parents* sont censés être uniques, des *éléments* égaux d'un parent ne +sont pas nécessairement identiques. Le comportement de Sage diffère ici de +celui de Python pour certains entiers (pas tous) : + +:: + + sage: int(1) is int(1) # Python int + True + sage: int(-15) is int(-15) + False + sage: 1 is 1 # Sage Integer + False + +Il faut bien comprendre que les éléments d'anneaux différents ne se distinguent +généralement pas par leur type, mais par leur parent : + +:: + + sage: a = GF(2)(1) + sage: b = GF(5)(1) + sage: type(a) is type(b) + True + sage: parent(a) + Finite Field of size 2 + sage: parent(b) + Finite Field of size 5 + +Ainsi, **le parent d'un élément est plus important que son type** du point de +vue algébrique. + +Conversion et coercition +------------------------- + +Il est parfois possible de convertir un élément d'un certain parent en élément +d'un autre parent. Une telle conversion peut être explicite ou implicite. Les +conversions implicites sont appelées *coercitions*. + +Le lecteur aura peut-être rencontré les notions de *conversion de type* et de +*coercition de type* dans le contexte du langage C par exemple. En Sage, il +existe aussi des notions de conversion et de coercition, mais elles +s'appliquent aux *parents* et non aux types. Attention donc à ne pas confondre +les conversions en Sage avec les conversions de type du C ! + +Nous nous limitons ici à une brève présentation, et renvoyons le lecteur à la +section du manuel de référence consacrée aux coercitions ainsi qu'au +`tutoriel `_ +spécifique pour plus de détails. + +On peut adopter deux positions extrêmes sur les opérations arithmétiques entre +éléments d'anneaux *différents* : + +* les anneaux différents sont des mondes indépendants, et l'addition ou la + multiplication entre éléments d'anneaux différents n'ont aucun sens ; même + ``1 + 1/2`` n'a pas de sens puisque le premier terme est un entier et le + second un rationnel ; + +ou + +* si un élément ``r1`` d'un anneau ``R1`` peut, d'une manière ou d'une autre, + s'interpréter comme élément d'un autre anneau ``R2``, alors toutes les + opérations arithmétiques entre ``r1`` et un élément quelconque de ``R2`` sont + permises. En particulier, les éléments neutres de la multiplication dans les + corps et anneaux doivent tous être égaux entre eux. + +Sage adopte un compromis. Si ``P1`` et ``P2`` sont des parents et si ``p1`` est +un élément de ``P1``, l'utilisateur peut demander explicitement comment ``P1`` +s'interprète dans ``P2``. Cela n'a pas forcément de sens dans tous les cas, et +l'interprétation peut n'être définie que pour certains éléments de ``P1`` ; +c'est à l'utilisateur de s'assurer que la conversion a un sens. Cela s'appelle +une **conversion** : + +:: + + sage: a = GF(2)(1) + sage: b = GF(5)(1) + sage: GF(5)(a) == b + True + sage: GF(2)(b) == a + True + +Cependant, une conversion *implicite* (c'est-à-dire automatique) n'est +possible que si elle peut se faire *systématiquement* et de manière +*cohérente*. Il faut ici absolument faire preuve de rigueur. + +Une telle conversion implicite s'appelle une **coercition**. Si une coercition +est définie entre deux parents, elle doit coïncider avec la conversion. De +plus, les coercitions doivent obéir aux deux conditions suivantes : + +#. Une coercition de ``P1`` dans ``P2`` doit être un morphisme (par exemple + un morphisme d'anneaux). Elle doit être définie pour *tous* les éléments de + ``P1``, et préserver la structure algébrique de celui-ci. +#. Le choix des applications de coercition doit être fait de manière cohérente. + Si ``P3`` est un troisième parent, la composée de la coercition choisie + de ``P1`` dans ``P2`` et de celle de ``P2`` dans ``P3`` doit être la + coercition de ``P1`` dans ``P3``. En particulier, s'il existe des + coercitions de ``P1`` dans ``P2`` et de ``P2`` dans ``P1``, leur composée + doit être l'identité sur ``P1``. + +Ainsi, bien qu'il soit possible de convertir tout élément de ``GF(2)`` en un +élément de ``GF(5)``, la conversion ne peut être une coercition, puisque il +n'existe pas de morphisme d'anneaux de ``GF(2)`` dans ``GF(5)``. + +Le second point — la cohérence des choix — est un peu plus compliqué à +expliquer. Illustrons-le sur l'exemple des anneaux de polynômes multivariés. +Dans les applications, il s'avère utile que les coercitions respectent les noms +des variables. Nous avons donc : + +:: + + sage: R1. = ZZ[] + sage: R2 = ZZ['y','x'] + sage: R2.has_coerce_map_from(R1) + True + sage: R2(x) + x + sage: R2(y) + y + +En l'absence den morphisme d'anneau qui préserve les noms de variable, la +coercition entre anneaux de polynômes multivariés n'est pas définie. Il peut +tout de même exister une conversion qui envoie les variables d'un anneau sur +celle de l'autre en fonction de leur position dans la liste des générateurs : + +.. link + +:: + + sage: R3 = ZZ['z','x'] + sage: R3.has_coerce_map_from(R1) + False + sage: R3(x) + z + sage: R3(y) + x + +Mais une telle conversion ne répond pas aux critères pour être une coercition : +en effet, en composant l'application de ``ZZ['x','y']`` dans ``ZZ['y','x']`` +avec celle qui préserve les positions de ``ZZ['y','x']`` dans ``ZZ['a','b']``, +nous obtiendrions une application qui ne préserve ni les noms ni les positions, +ce qui viole la règle de cohérence. + +Lorsqu'une coercition est définie, elle est souvent utilisée pour comparer des +éléments d'anneaux différents ou pour effectuer des opérations arithmétiques. +Cela est commode, mais il faut être prudent en étendant la relation d'égalité +``==`` au-delà des frontières d'un parent donné. Par exemple, si ``==`` est +bien censé être une relation d'équivalence entre éléments d'*un* anneau, il +n'en va pas forcément de même quand on compare des éléments d'anneaux +différents. Ainsi, les éléments ``1`` de ``ZZ`` et d'un corps fini sont +considérés comme égaux, puisqu'il existe une coercition canonique des entiers +dans tout corps fini. En revanche, il n'y a en général pas de coercition entre +deux corps finis quelconques. On a donc + +.. link + +:: + + sage: GF(5)(1) == 1 + True + sage: 1 == GF(2)(1) + True + sage: GF(5)(1) == GF(2)(1) + False + sage: GF(5)(1) != GF(2)(1) + True + +De même, on a + +.. link + +:: + + sage: R3(R1.1) == R3.1 + True + sage: R1.1 == R3.1 + False + sage: R1.1 != R3.1 + True + +Une autre conséquence de la condition de cohérence est que les coercitions ne +sont possible que des anneaux exacts (comme les rationnels ``QQ``) vers les +anneaux inexacts (comme les réels à précision donnée ``RR``), jamais l'inverse. +En effet, pour qu'une conversion de ``RR`` dans ``QQ`` puisse être une +coercition, il faudrait que la composée de la coercition de ``QQ`` dans ``RR`` +et de cette conversion soit l'identité sur ``QQ``, ce qui n'est pas possible +puisque des rationnels distincts peuvent très bien être envoyés sur le même +élément de ``RR`` : + +:: + + sage: RR(1/10^200+1/10^100) == RR(1/10^100) + True + sage: 1/10^200+1/10^100 == 1/10^100 + False + +Lorsque l'on compare des éléments de deux parents ``P1`` et ``P2``, il peut +arriver qu'il n'existe pas de coercition entre ``P1`` et ``P2``, mais qu'il y +ait un choix canonique de parent ``P3`` tel que ``P1`` et ``P2`` admettent tous +deux des coercitions dans ``P3``. Dans ce cas aussi, la coercition a lieu. Un +exemple typique de ce mécanisme est l'addition d'un rationnel et d'un polynôme +à coefficients entiers, qui produit un polynôme à coefficients rationnels : + +:: + + sage: P1. = ZZ[] + sage: p = 2*x+3 + sage: q = 1/2 + sage: parent(p) + Univariate Polynomial Ring in x over Integer Ring + sage: parent(p+q) + Univariate Polynomial Ring in x over Rational Field + +Notons qu'en principe, on aurait très bien pu choisir pour ``P3`` le corps des +fractions de ``ZZ['x']``. Cependant, Sage tente de choisir un parent commun +*canonique* aussi naturel que possible (ici ``QQ['x']``). Afin que cela +fonctionne de façon fiable, Sage ne se contente *pas* de prendre n'importe +lequel lorsque plusieurs candidats semblent aussi naturels les uns que les +autres. La manière dont le choix est fait est décrite dans le `tutoriel +`_ +spécifique déjà mentionné. + +Dans l'exemple suivant, il n'y a pas de coercition vers un parent commun : + +:: + + sage: R. = QQ[] + sage: S. = QQ[] + sage: x+y + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for '+': 'Univariate Polynomial Ring in x over Rational Field' and 'Univariate Polynomial Ring in y over Rational Field' + +En effet, Sage refuse de choisi entre les candidats ``QQ['x']['y']``, +``QQ['y']['x']``, ``QQ['x','y']`` et ``QQ['y','x']``, car ces quatre structures +deux à deux distinctes semblent toutes des parents communs naturels, et aucun +choix canonique ne s'impose. From c76166dded49e5c5e60b9ec89269a4766ed86cef Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 19 Aug 2013 10:21:04 +0200 Subject: [PATCH 018/206] fr tutorial: reflect changes in b1008e96d2bc4a3c2dde7542b285e29cec966766 See also trac #8245. --- src/doc/fr/tutorial/tour_algebra.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/fr/tutorial/tour_algebra.rst b/src/doc/fr/tutorial/tour_algebra.rst index 3c815214e65..f332b28d25e 100644 --- a/src/doc/fr/tutorial/tour_algebra.rst +++ b/src/doc/fr/tutorial/tour_algebra.rst @@ -333,7 +333,7 @@ Il nous faut réduire l'équation différentielle d'ordre 2 à un système de de 3/4 0.82 -0.16 -0.66 -0.081 1 0.65 -0.18 -0.74 0.022 -On en déduit :math:`z(1)\approx 0.75`. +On en déduit :math:`z(1)\approx 0.65`. On peut également tracer le graphe des points :math:`(x,y)` pour obtenir une image approchée de la courbe. La fonction ``eulers_method_2x2_plot`` From 37a16deb21776b959b1b1e3c38636d9a73e001da Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 19 Aug 2013 10:43:19 +0200 Subject: [PATCH 019/206] fr tutorial: update tour_linalg.rst - add RDF, CDF (#13657) - adapt some other changes to the original that were apparently overlooked a while ago Corresponding changes to the original version: e98ddeecb6fd8ee3d594e72f99ff28e159611da6 955ae40e207ebb17d2708d7f2c35493d41dbc752 --- src/doc/fr/tutorial/tour_linalg.rst | 85 +++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/src/doc/fr/tutorial/tour_linalg.rst b/src/doc/fr/tutorial/tour_linalg.rst index 2d3a2d3ce9c..2a9d16ee1de 100644 --- a/src/doc/fr/tutorial/tour_linalg.rst +++ b/src/doc/fr/tutorial/tour_linalg.rst @@ -1,3 +1,5 @@ +.. _section-linalg: + Algèbre linéaire ================ @@ -37,7 +39,7 @@ une matrice (ou un vecteur) :math:`X` tel que :math:`AX=Y`: sage: X = A.solve_right(Y) sage: X (-2, 1, 0) - sage: A * X #vérifions la réponse... + sage: A * X # vérifions la réponse... (0, -4, -1) Un antislash (contre-oblique) ``\`` peut être employé à la place de @@ -65,6 +67,61 @@ S'il n'y a aucune solution, Sage renvoie une erreur : De même, il faut utiliser ``A.solve_left(Y)`` pour résoudre en :math:`X` l'équation :math:`XA=Y`. +Sage sait aussi calculer les valeurs propres et vecteurs propres:: + + sage: A = matrix([[0, 4], [-1, 0]]) + sage: A.eigenvalues () + [-2*I, 2*I] + sage: B = matrix([[1, 3], [3, 1]]) + sage: B.eigenvectors_left() + [(4, [ + (1, 1) + ], 1), (-2, [ + (1, -1) + ], 1)] + +(La sortie de ``eigenvectors_left`` est une liste de triplets (valeur propre, +vecteur propre, multiplicité).) Sur ``QQ`` et ``RR``, on peut aussi utiliser +Maxima (voir la section :ref:`section-maxima` ci-dessous). + +Comme signalé en :ref:`section-rings`, l'anneau sur lequel une matrice est +définie a une influence sur les propriétés de la matrice. Dans l'exemple +suivant, le premier argument de la commande ``matrix`` indique à Sage s'il faut +traiter la matrice comme une matrice d'entier (``ZZ``), de rationnels (``QQ``) +ou de réels (``RR``):: + + sage: AZ = matrix(ZZ, [[2,0], [0,1]]) + sage: AQ = matrix(QQ, [[2,0], [0,1]]) + sage: AR = matrix(RR, [[2,0], [0,1]]) + sage: AZ.echelon_form() + [2 0] + [0 1] + sage: AQ.echelon_form() + [1 0] + [0 1] + sage: AR.echelon_form() + [ 1.00000000000000 0.000000000000000] + [0.000000000000000 1.00000000000000] + +Pour le calcul de valeurs propres et vecteurs propres sur les nombres à virgule +flottante réels ou complexes, la matrice doit être respectivement à +coefficients dans ``RDF`` (*Real Double Field*, nombres réels à précision +machine) ou ``CDF`` (*Complex Double Field*). Lorsque l'on définit une matrice +avec des coefficients flottants sans spécifier explicitement l'anneau de base, +ce ne sont pas ``RDF`` ou ``CDF`` qui sont utilisés par défaut, mais ``RR`` et +``CC``, sur lesquels ces calculs ne sont pas implémentés dans tous les cas:: + + sage: ARDF = matrix(RDF, [[1.2, 2], [2, 3]]) + sage: ARDF.eigenvalues() + [-0.0931712199461, 4.29317121995] + sage: ACDF = matrix(CDF, [[1.2, I], [2, 3]]) + sage: ACDF.eigenvectors_right() + [(0.881845698329 - 0.820914065343*I, [(0.750560818381, -0.616145932705 + 0.238794153033*I)], 1), + (3.31815430167 + 0.820914065343*I, [(0.145594698293 + 0.37566908585*I, 0.915245825866)], 1)] + +Espaces de matrices +------------------- + Créons l'espace :math:`\text{Mat}_{3\times 3}(\QQ)`: :: @@ -138,7 +195,7 @@ des corps finis : [(1, 1, 0, 0, 1, 1, 1, 1), (0, 1, 0, 0, 1, 0, 1, 1), (0, 0, 1, 0, 1, 1, 0, 1), (0, 0, 1, 1, 1, 1, 1, 0)] -Nous créons le sous-espace engendré sur :math:`\GF{2}` par les +Nous créons le sous-espace engendré sur `\GF{2}` par les vecteurs lignes ci-dessus. .. link @@ -160,8 +217,8 @@ vecteurs lignes ci-dessus. [0 0 1 0 1 1 0 1] [0 0 0 1 0 0 1 1] -La base de :math:`S` utilisée par Sage est obtenue à partir des lignes -non-nulles de la matrice des générateurs de :math:`S` réduite sous forme +La base de `S` utilisée par Sage est obtenue à partir des lignes +non nulles de la matrice des générateurs de `S` réduite sous forme échelonnée en lignes. Algèbre linéaire creuse @@ -196,23 +253,3 @@ Notez que Python distingue les majuscules des minuscules : Traceback (most recent call last): ... TypeError: __classcall__() got an unexpected keyword argument 'Sparse' - -Sage peut calculer des valeurs propres et des vecteurs propres : - -:: - - - sage: g = matrix(GF(7), [[5, 1], [4, 1]]) - sage: g.eigenvalues() - [4, 2] - sage: g.eigenvectors_right() # renvoie (valeurs propres, [vecteurs propres], multiplicités algébriques) - [(4, [ - (1, 6) - ], 1), (2, [ - (1, 4) - ], 1)] - - - -Les valeurs propres et vecteurs propres peuvent aussi être calculés avec -Maxima (voir :ref:`section-maxima` ci-dessous). From c1b52ba595eb2ba685d08e166a7cfb5ef655e621 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 19 Aug 2013 10:47:29 +0200 Subject: [PATCH 020/206] fr tutorial: reflect changes in 1aeda8850fffe3476870683be767356c9b4e1c9c See also #14184. --- src/doc/fr/tutorial/interfaces.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/fr/tutorial/interfaces.rst b/src/doc/fr/tutorial/interfaces.rst index ee0e63e2b72..147fd4985c6 100644 --- a/src/doc/fr/tutorial/interfaces.rst +++ b/src/doc/fr/tutorial/interfaces.rst @@ -159,7 +159,7 @@ installer séparément comme décrit plus bas. Group( [ (1,2,3)(4,5), (3,4) ] ) sage: G.Center() Group( () ) - sage: G.IdGroup() # nécessite le paquet facultatif database_gap (optional) + sage: G.IdGroup() # optional - database_gap [ 120, 34 ] sage: G.Order() 120 @@ -172,7 +172,7 @@ l'interface GAP comme suit : sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]]) sage: G.center() Subgroup of (Permutation Group with generators [(3,4), (1,2,3)(4,5)]) generated by [()] - sage: G.group_id() # nécessite le paquet facultatif database_gap (optional) + sage: G.group_id() # optional - database_gap [120, 34] sage: n = G.order(); n 120 From aa57141fc53ad7076c471187643c11d1dd66d25a Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 19 Aug 2013 10:52:31 +0200 Subject: [PATCH 021/206] fr tutorial: document ^^ (bitwise xor) Corresponding change to the original version: 16b45e70f044676e1b1e6dec75f9813e7574f973 See also: #14621. --- src/doc/fr/tutorial/afterword.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/doc/fr/tutorial/afterword.rst b/src/doc/fr/tutorial/afterword.rst index f83a800e566..38a02b1e310 100644 --- a/src/doc/fr/tutorial/afterword.rst +++ b/src/doc/fr/tutorial/afterword.rst @@ -100,6 +100,18 @@ Aussi, Sage se comporte différemment de Python à plusieurs égards. sage: "3^2" '3^2' + Le ou exclusif bit à bit est quant à lui noté ``^^``, et l'opération en + place ``^^=`` fonctionne comme on s'y attend : + + :: + + sage: 3^^2 + 1 + sage: a = 2 + sage: a ^^= 8 + sage: a + 10 + - **Division entière :** L'expression Python ``2/3`` ne se comporte pas de la manière à laquelle s'attendraient des mathématiciens. En Python, si ``m`` et ``n`` sont de type int, alors ``m/n`` est aussi de type int, c'est From d12d0c3bda8888be76a1d650286d0927c8f06fcb Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 26 Aug 2013 17:36:36 +0200 Subject: [PATCH 022/206] Missing :: in many files, plus random doctest fixes --- .../quatalg/quaternion_algebra_element.pyx | 2 +- src/sage/categories/category_singleton.pyx | 2 +- src/sage/categories/sets_cat.py | 2 +- src/sage/coding/binary_code.pyx | 2 +- src/sage/coding/linear_code.py | 2 +- src/sage/combinat/perfect_matching.py | 2 +- .../combinat/root_system/type_reducible.py | 2 +- src/sage/combinat/root_system/weight_space.py | 2 +- .../combinat/root_system/weyl_characters.py | 2 +- src/sage/combinat/root_system/weyl_group.py | 4 ++-- src/sage/dynamics/interval_exchanges/iet.py | 2 +- src/sage/ext/fast_callable.pyx | 6 +++--- src/sage/games/hexad.py | 2 +- src/sage/geometry/fan_isomorphism.py | 2 +- src/sage/geometry/polyhedron/palp_database.py | 2 +- src/sage/geometry/triangulation/base.pyx | 2 +- src/sage/graphs/base/dense_graph.pyx | 2 +- src/sage/graphs/graph_bundle.py | 2 +- src/sage/graphs/pq_trees.py | 2 +- src/sage/interfaces/ecm.py | 2 +- src/sage/interfaces/lie.py | 2 +- src/sage/interfaces/maxima.py | 2 +- src/sage/libs/pari/gen.pyx | 2 +- src/sage/matrix/matrix0.pyx | 2 +- src/sage/matrix/matrix_mpolynomial_dense.pyx | 8 ++++---- src/sage/matrix/matrix_sparse.pyx | 2 +- src/sage/matrix/symplectic_basis.py | 2 +- src/sage/matrix/tests.py | 2 +- src/sage/misc/displayhook.py | 2 +- src/sage/misc/functional.py | 4 ++-- src/sage/misc/latex.py | 2 +- src/sage/misc/memory_info.py | 2 +- .../modular/arithgroup/congroup_generic.py | 2 +- src/sage/modular/modsym/ambient.py | 4 ++-- src/sage/modular/modsym/p1list.pyx | 6 +++--- .../modular/overconvergent/hecke_series.py | 2 +- src/sage/modules/fg_pid/fgp_module.py | 2 +- src/sage/modules/free_quadratic_module.py | 6 +++--- src/sage/plot/circle.py | 2 +- src/sage/rings/bernoulli_mod_p.pyx | 19 +++++++++++-------- src/sage/rings/factorint.pyx | 2 +- src/sage/rings/number_field/number_field.py | 6 +++--- .../number_field/number_field_element.pyx | 2 +- .../rings/number_field/number_field_rel.py | 2 +- .../polynomial/multi_polynomial_ideal.py | 2 +- .../multi_polynomial_ring_generic.pyx | 2 +- src/sage/rings/polynomial/pbori.pyx | 8 ++++---- .../rings/polynomial/polynomial_element.pyx | 2 +- src/sage/sandpiles/sandpile.py | 2 +- .../elliptic_curves/ell_finite_field.py | 2 +- src/sage/schemes/generic/homset.py | 2 +- .../hyperelliptic_curves/invariants.py | 2 +- src/sage/schemes/toric/variety.py | 2 +- src/sage/schemes/toric/weierstrass.py | 2 +- src/sage/server/notebook/tutorial.py | 2 +- src/sage/stats/hmm/hmm.pyx | 2 +- src/sage/stats/intlist.pyx | 2 +- src/sage/structure/sage_object.pyx | 2 +- src/sage/structure/sequence.py | 2 +- 59 files changed, 86 insertions(+), 83 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx index 6d2aebfb79c..31f91f8620c 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx @@ -574,7 +574,7 @@ cdef class QuaternionAlgebraElement_abstract(AlgebraElement): ... ValueError: action must be either 'left' or 'right' - We test over a more generic base field: + We test over a more generic base field:: sage: K. = QQ['x'] sage: Q. = QuaternionAlgebra(Frac(K),-5,-2) diff --git a/src/sage/categories/category_singleton.pyx b/src/sage/categories/category_singleton.pyx index ef6932b3992..c9359e7061e 100644 --- a/src/sage/categories/category_singleton.pyx +++ b/src/sage/categories/category_singleton.pyx @@ -145,7 +145,7 @@ class Category_singleton(Category): 10000 loops, best of 3: 2.99 µs per loop So this is an improvement, but not yet competitive with a pure - Cython method: + Cython method:: sage: timeit("R.is_ring()", number=10000) # not tested 10000 loops, best of 3: 383 ns per loop diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 398ab83d014..8f9792fc73b 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -469,7 +469,7 @@ def _test_elements(self, tester = None, **options): running ._test_pickling() . . . pass - Debugging tip: in case of failure of this test, run instead: + Debugging tip: in case of failure of this test, run instead:: sage: TestSuite(C.an_element()).run() diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index 10bc7457598..101f6c2339d 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -2587,7 +2587,7 @@ cdef class PartitionStack: def _refine(self, k, alpha, CG): """ - EXAMPLE: + EXAMPLE:: sage: import sage.coding.binary_code sage: from sage.coding.binary_code import * diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index de6b6090086..6f7ac247fe2 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -367,7 +367,7 @@ def min_wt_vec_gap(Gmat, n, k, F, algorithm=None): sage: sage.coding.linear_code.min_wt_vec_gap(Gstr,7,4,GF(2)) (0, 1, 0, 1, 0, 1, 0) - This output is different but still a minimum weight vector: + This output is different but still a minimum weight vector:: sage: sage.coding.linear_code.min_wt_vec_gap(Gstr,7,4,GF(2),algorithm="guava") # optional - gap_packages (Guava package) (0, 0, 1, 0, 1, 1, 0) diff --git a/src/sage/combinat/perfect_matching.py b/src/sage/combinat/perfect_matching.py index ccff3724e13..e69c6d1ce77 100644 --- a/src/sage/combinat/perfect_matching.py +++ b/src/sage/combinat/perfect_matching.py @@ -133,7 +133,7 @@ def __classcall_private__(cls, p): The function checks that the given list or permutation is a valid perfect matching (i.e. a list of pairs with pairwise disjoint elements or a - fixpoint-free involution) and raises a ValueError otherwise: + fixpoint-free involution) and raises a ValueError otherwise:: sage: PerfectMatching([(1, 2, 3), (4, 5)]) Traceback (most recent call last): diff --git a/src/sage/combinat/root_system/type_reducible.py b/src/sage/combinat/root_system/type_reducible.py index 21500ed951b..eb7635e3515 100644 --- a/src/sage/combinat/root_system/type_reducible.py +++ b/src/sage/combinat/root_system/type_reducible.py @@ -487,7 +487,7 @@ def simple_root(self, i): @cached_method def simple_coroot(self, i): """ - EXAMPLES: + EXAMPLES:: sage: A = RootSystem("A1xB2").ambient_space() sage: A.simple_coroot(2) diff --git a/src/sage/combinat/root_system/weight_space.py b/src/sage/combinat/root_system/weight_space.py index 2eaec1e8faa..6fe49510dce 100644 --- a/src/sage/combinat/root_system/weight_space.py +++ b/src/sage/combinat/root_system/weight_space.py @@ -442,7 +442,7 @@ def scalar(self, lambdacheck): sage: Lambda[1].scalar(alphacheck[2]) 0 - The fundamental weights and the simple coroots are dual bases: + The fundamental weights and the simple coroots are dual bases:: sage: matrix([ [ Lambda[i].scalar(alphacheck[j]) ... for i in L.index_set() ] diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 9247a2ce22b..b0929af09fc 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -109,7 +109,7 @@ def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice"): def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice"): """ - EXAMPLES: + EXAMPLES:: sage: A2 = WeylCharacterRing("A2") sage: TestSuite(A2).run() diff --git a/src/sage/combinat/root_system/weyl_group.py b/src/sage/combinat/root_system/weyl_group.py index 3fb3dbd0c0f..9314384f246 100644 --- a/src/sage/combinat/root_system/weyl_group.py +++ b/src/sage/combinat/root_system/weyl_group.py @@ -376,7 +376,7 @@ def domain(self): sage: G.domain() Root space over the Rational Field of the Root system of type ['A', 3, 1] - This method used to be called ``lattice``: + This method used to be called ``lattice``:: sage: G.lattice() doctest:...: DeprecationWarning: lattice is deprecated. Please use domain instead. @@ -505,7 +505,7 @@ def bruhat_graph(self, x, y): generalizations: classical methods (University Park, PA, 1991), 53--61, Proc. Sympos. Pure Math., 56, Part 1, Amer. Math. Soc., Providence, RI, 1994. - EXAMPLES: + EXAMPLES:: sage: W = WeylGroup("A3", prefix = "s") sage: [s1,s2,s3] = W.simple_reflections() diff --git a/src/sage/dynamics/interval_exchanges/iet.py b/src/sage/dynamics/interval_exchanges/iet.py index 623194d14c8..0cb1fdbc8b5 100644 --- a/src/sage/dynamics/interval_exchanges/iet.py +++ b/src/sage/dynamics/interval_exchanges/iet.py @@ -709,7 +709,7 @@ def _rauzy_move(self,side=-1): - ``side`` - must be 0 or -1 (no verification) - TEST: + TEST:: sage: t = iet.IntervalExchangeTransformation(('a b c','c b a'),[1,1,3]) sage: t diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 0927f4d3fad..50e3ad39e76 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -82,13 +82,13 @@ sage: fc_wilk_float = fast_callable(wilk, vars=[x], domain=float) sage: timeit('fc_wilk_float(30.0)') # random, long time 625 loops, best of 3: 5.04 us per loop -We also have support for RR: +We also have support for ``RR``: sage: fc_wilk_rr = fast_callable(wilk, vars=[x], domain=RR) sage: timeit('fc_wilk_rr(30.0)') # random, long time 625 loops, best of 3: 13 us per loop -And support for CDF: +And support for CDF:: sage: fc_wilk_rr = fast_callable(wilk, vars=[x], domain=CDF) sage: timeit('fc_wilk_rr(30.0)') # random, long time @@ -208,7 +208,7 @@ EXAMPLES:: (which takes 2 arguments from the stack), push the constant 1.0, add the top two arguments of the stack, and then call sqrt. -Here we take sin of the first argument and add it to f: +Here we take sin of the first argument and add it to f:: sage: from sage.ext.fast_callable import ExpressionTreeBuilder sage: etb = ExpressionTreeBuilder('x') diff --git a/src/sage/games/hexad.py b/src/sage/games/hexad.py index a995370ca38..81b74c8f7a8 100644 --- a/src/sage/games/hexad.py +++ b/src/sage/games/hexad.py @@ -391,7 +391,7 @@ def find_hexad2(self, pts, x0): ([], []) The above output indicates that there is no hexad of type 2 - containing {2,3,4,5}. However, there is one containing {2,3,4,8}: + containing {2,3,4,5}. However, there is one containing {2,3,4,8}:: sage: M.find_hexad2([2,3,4,8],0) ([0, 2, 3, 4, 8, 9], ['cross 12', 'picture 0']) diff --git a/src/sage/geometry/fan_isomorphism.py b/src/sage/geometry/fan_isomorphism.py index e4cbaca9def..d42dcccca88 100644 --- a/src/sage/geometry/fan_isomorphism.py +++ b/src/sage/geometry/fan_isomorphism.py @@ -280,7 +280,7 @@ def fan_2d_cyclically_ordered_rays(fan): N( 1, -1) in 2-d lattice N - TESTS: + TESTS:: sage: fan = Fan(cones=[], rays=[], lattice=ZZ^2) sage: from sage.geometry.fan_isomorphism import fan_2d_cyclically_ordered_rays diff --git a/src/sage/geometry/polyhedron/palp_database.py b/src/sage/geometry/polyhedron/palp_database.py index 0d2312ec061..4c9b965d17e 100644 --- a/src/sage/geometry/polyhedron/palp_database.py +++ b/src/sage/geometry/polyhedron/palp_database.py @@ -412,7 +412,7 @@ class Reflexive4dHodge(PALPreader): Any additional keyword arguments are passed to :class:`PALPreader`. - EXAMPLES: + EXAMPLES:: sage: from sage.geometry.polyhedron.palp_database import Reflexive4dHodge sage: ref = Reflexive4dHodge(1,101) # optional - polytopes_db_4d diff --git a/src/sage/geometry/triangulation/base.pyx b/src/sage/geometry/triangulation/base.pyx index 16787913841..15357c124c7 100644 --- a/src/sage/geometry/triangulation/base.pyx +++ b/src/sage/geometry/triangulation/base.pyx @@ -106,7 +106,7 @@ cdef class Point(SageObject): A :class:`~sage.geometry.triangulation.point_configuration.PointConfiguration`. - EXAMPLES: + EXAMPLES:: sage: pc = PointConfiguration([ (0,0), (1,0), (0,1) ]) sage: p = pc.point(0) diff --git a/src/sage/graphs/base/dense_graph.pyx b/src/sage/graphs/base/dense_graph.pyx index b7ac6812a75..5712e68e94b 100644 --- a/src/sage/graphs/base/dense_graph.pyx +++ b/src/sage/graphs/base/dense_graph.pyx @@ -736,7 +736,7 @@ class DenseGraphBackend(CGraphBackend): """ Initialize a dense graph with n vertices. - EXAMPLE: + EXAMPLE:: sage: D = sage.graphs.base.dense_graph.DenseGraphBackend(9) sage: D.add_edge(0,1,None,False) diff --git a/src/sage/graphs/graph_bundle.py b/src/sage/graphs/graph_bundle.py index 25e4b77a7db..2f154b59386 100644 --- a/src/sage/graphs/graph_bundle.py +++ b/src/sage/graphs/graph_bundle.py @@ -10,7 +10,7 @@ AUTHORS: -- Robert L. Miller (2008-01-20): initial version -TESTS: +TESTS:: sage: B = graphs.CompleteBipartiteGraph(7,9) sage: loads(dumps(B)) == B diff --git a/src/sage/graphs/pq_trees.py b/src/sage/graphs/pq_trees.py index a8d83656133..2055ad4d78d 100644 --- a/src/sage/graphs/pq_trees.py +++ b/src/sage/graphs/pq_trees.py @@ -337,7 +337,7 @@ def ordering(self): Returns the current ordering given by listing the leaves from left to right. - EXAMPLE: + EXAMPLE:: sage: from sage.graphs.pq_trees import P, Q sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])]) diff --git a/src/sage/interfaces/ecm.py b/src/sage/interfaces/ecm.py index 0474ecfd905..dbbadf44bb3 100644 --- a/src/sage/interfaces/ecm.py +++ b/src/sage/interfaces/ecm.py @@ -394,7 +394,7 @@ def time(self, n, factor_digits, verbose=0): n -- a positive integer factor_digits -- the (estimated) number of digits of the smallest factor - EXAMPLES: + EXAMPLES:: sage: n = next_prime(11^23)*next_prime(11^37) diff --git a/src/sage/interfaces/lie.py b/src/sage/interfaces/lie.py index 1c33528d16e..2106f97ba8d 100644 --- a/src/sage/interfaces/lie.py +++ b/src/sage/interfaces/lie.py @@ -202,7 +202,7 @@ With the exception of groups, all LiE data types can be converted into native Sage data types by calling the .sage() method. -Integers: +Integers:: sage: a = lie('1234') # optional - lie sage: b = a.sage(); b # optional - lie diff --git a/src/sage/interfaces/maxima.py b/src/sage/interfaces/maxima.py index e90e920982b..22d7f47285d 100644 --- a/src/sage/interfaces/maxima.py +++ b/src/sage/interfaces/maxima.py @@ -577,7 +577,7 @@ def _start(self): sage: m.is_running() True - Test that we can use more than 256MB RAM (see trac 6722): + Test that we can use more than 256MB RAM (see trac :trac:`6722`):: sage: a = maxima(10)^(10^5) sage: b = a^600 # long time -- about 10-15 seconds diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index dfe00ab5876..74d6210facd 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -7741,7 +7741,7 @@ cdef class gen(sage.structure.element.RingElement): sage: nf = pari(y^2 - 6*y + 24).nfinit() sage: rnf = nf.rnfinit(x^2 - pari(y)) - This is the relative HNF of the inert ideal (2) in rnf: + This is the relative HNF of the inert ideal (2) in rnf:: sage: P = pari('[[[1, 0]~, [0, 0]~; [0, 0]~, [1, 0]~], [[2, 0; 0, 2], [2, 0; 0, 1/2]]]') diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 83c0cc7235c..8c89d7626c1 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -3748,7 +3748,7 @@ cdef class Matrix(sage.structure.element.Matrix): This returns a tuple so it is immutable; see #10752. - EXAMPLES: + EXAMPLES:: sage: A = matrix(QQ, 2, 2, range(4)) sage: A.pivots() diff --git a/src/sage/matrix/matrix_mpolynomial_dense.pyx b/src/sage/matrix/matrix_mpolynomial_dense.pyx index a2344cfb52f..ca8ca091f2e 100644 --- a/src/sage/matrix/matrix_mpolynomial_dense.pyx +++ b/src/sage/matrix/matrix_mpolynomial_dense.pyx @@ -379,7 +379,7 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): sage: E1 == E2 True - If no entries are constant, nothing happens: + If no entries are constant, nothing happens:: sage: P. = PolynomialRing(GF(2),4) sage: A = Matrix(P,2,2,[x0,y0,x0,y0]); A @@ -391,7 +391,7 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): sage: B == A True - A more interesting example: + A more interesting example:: sage: P. = PolynomialRing(GF(2),4) sage: l = [1, 1, 1, 1, 1, \ @@ -493,7 +493,7 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): EXAMPLES: - We compute the determinant of the arbitrary 3x3 matrix: + We compute the determinant of the arbitrary `3x3` matrix:: sage: R = PolynomialRing(QQ,9,'x') sage: A = matrix(R,3,R.gens()) @@ -517,7 +517,7 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): sage: C.change_ring(R.change_ring(QQbar)).det() (-6/5)*x^2*y^2 + (-3)*x*y^3 + 6/5*x^2*y + 11/12*x*y^2 + (-18)*y^2 + (-3/4)*y - Finally, we check whether the \Singular interface is working: + Finally, we check whether the \Singular interface is working:: sage: R. = RR[] sage: C = random_matrix(R,2,2,terms=2) diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index da2d5e0417f..d30923e8caa 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -37,7 +37,7 @@ cdef class Matrix_sparse(matrix.Matrix): Always returns a copy (unless self is immutable, in which case returns self). - EXAMPLES: + EXAMPLES:: sage: A = matrix(QQ['x,y'], 2, [0,-1,2*x,-2], sparse=True); A [ 0 -1] diff --git a/src/sage/matrix/symplectic_basis.py b/src/sage/matrix/symplectic_basis.py index 4ac0cf2eea6..a5e2d437b65 100644 --- a/src/sage/matrix/symplectic_basis.py +++ b/src/sage/matrix/symplectic_basis.py @@ -196,7 +196,7 @@ def symplectic_basis_over_field(M): sage: F == C * E * C.transpose() True - The tricky case of characteristic 2: + The tricky case of characteristic 2:: sage: E = matrix(GF(2), 8, 8, [0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0]); E [0 0 1 1 0 1 0 1] diff --git a/src/sage/matrix/tests.py b/src/sage/matrix/tests.py index f77e421b762..8fdfc4c2747 100644 --- a/src/sage/matrix/tests.py +++ b/src/sage/matrix/tests.py @@ -1,7 +1,7 @@ """ TESTS: -We test various degenerate cases of kernel computation: +We test various degenerate cases of kernel computation:: sage: matrix(ZZ,1,0).kernel() Free module of degree 1 and rank 1 over Integer Ring diff --git a/src/sage/misc/displayhook.py b/src/sage/misc/displayhook.py index 826a95ca1ea..09041c91337 100644 --- a/src/sage/misc/displayhook.py +++ b/src/sage/misc/displayhook.py @@ -178,7 +178,7 @@ def format_obj(obj): sage: import sage.misc.displayhook - For most objects, nothing is done (``None`` is returned): + For most objects, nothing is done (``None`` is returned):: sage: sage.misc.displayhook.format_obj('Hello, world!') sage: sage.misc.displayhook.format_obj((1, 2, 3, 4)) diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index ace10e545c2..139d0000a53 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -1377,8 +1377,8 @@ def numerical_approx(x, prec=None, digits=None): sage: all(check_str_length(k) and check_precision(k) for k in ks) True - Testing we have sufficient precision for the golden ratio (trac #12163), note - that the decimal point adds 1 to the string length: + Testing we have sufficient precision for the golden ratio (:trac:`12163`), note + that the decimal point adds 1 to the string length:: sage: len(str(n(golden_ratio, digits=5000))) 5001 diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index a86e38c406a..1156d8c8b8b 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -2644,7 +2644,7 @@ def _repr_(self): """ String representation - EXAMPLES: + EXAMPLES:: sage: from sage.misc.latex import latex_examples sage: G = latex_examples.graph() diff --git a/src/sage/misc/memory_info.py b/src/sage/misc/memory_info.py index 3a9e1182d7c..9a05f816721 100644 --- a/src/sage/misc/memory_info.py +++ b/src/sage/misc/memory_info.py @@ -298,7 +298,7 @@ def _parse_top_output(self, meminfo): See :meth:`_parse_top`. - TESTS: + TESTS:: sage: from sage.misc.memory_info import MemoryInfo_OSX sage: m = MemoryInfo_OSX.__new__(MemoryInfo_OSX) diff --git a/src/sage/modular/arithgroup/congroup_generic.py b/src/sage/modular/arithgroup/congroup_generic.py index 19f28cedd6c..ef04c3221ec 100644 --- a/src/sage/modular/arithgroup/congroup_generic.py +++ b/src/sage/modular/arithgroup/congroup_generic.py @@ -382,7 +382,7 @@ def image_mod_n(self): r""" Raise an error: all derived subclasses should override this function. - EXAMPLE: + EXAMPLE:: sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroup(5).image_mod_n() Traceback (most recent call last): diff --git a/src/sage/modular/modsym/ambient.py b/src/sage/modular/modsym/ambient.py index b5feb605419..f3d93026fde 100644 --- a/src/sage/modular/modsym/ambient.py +++ b/src/sage/modular/modsym/ambient.py @@ -973,7 +973,7 @@ def _compute_hecke_matrix_prime(self, p, rows=None): sage: m._compute_hecke_matrix_prime(3).charpoly('x') x^6 + 3*x^4 - 19*x^3 + 24*x^2 - 9*x - In some situations we do not need all the rows of the result, and can thereby save time: + In some situations we do not need all the rows of the result, and can thereby save time:: sage: m = ModularSymbols(1,weight=12) sage: m._compute_hecke_matrix_prime(2) @@ -2533,7 +2533,7 @@ def _cuspidal_new_submodule_dimension_formula(self): r""" Return the dimension of the new cuspidal subspace, via the formula. - EXAMPLES: + EXAMPLES:: sage: M = ModularSymbols(100,2) sage: M._cuspidal_new_submodule_dimension_formula() diff --git a/src/sage/modular/modsym/p1list.pyx b/src/sage/modular/modsym/p1list.pyx index 5f9b4d6ed3b..fd6939babcc 100644 --- a/src/sage/modular/modsym/p1list.pyx +++ b/src/sage/modular/modsym/p1list.pyx @@ -880,7 +880,7 @@ cdef class P1List: :: - This operation is an involution: + This operation is an involution:: sage: all([L.apply_I(L.apply_I(i))==i for i in xrange(len(L))]) True @@ -916,7 +916,7 @@ cdef class P1List: :: - This operation is an involution: + This operation is an involution:: sage: all([L.apply_S(L.apply_S(i))==i for i in xrange(len(L))]) True @@ -952,7 +952,7 @@ cdef class P1List: :: - This operation has order three: + This operation has order three:: sage: all([L.apply_T(L.apply_T(L.apply_T(i)))==i for i in xrange(len(L))]) True diff --git a/src/sage/modular/overconvergent/hecke_series.py b/src/sage/modular/overconvergent/hecke_series.py index 128d0a4acc0..232d62e26da 100644 --- a/src/sage/modular/overconvergent/hecke_series.py +++ b/src/sage/modular/overconvergent/hecke_series.py @@ -826,7 +826,7 @@ def compute_Wi(k,p,h,hj,E4,E6): - list of q-expansions (to same finite precision), and q-expansion. - EXAMPLES: + EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import compute_Wi sage: p = 17 diff --git a/src/sage/modules/fg_pid/fgp_module.py b/src/sage/modules/fg_pid/fgp_module.py index 1d819aeeeb7..e9b890521ce 100644 --- a/src/sage/modules/fg_pid/fgp_module.py +++ b/src/sage/modules/fg_pid/fgp_module.py @@ -415,7 +415,7 @@ def _coerce_map_from_(self, S): sage: Q._coerce_map_from_(ZZ^2) False - Of course, `V` canonically coerces to `Q`, as does twice `V`: + Of course, `V` canonically coerces to `Q`, as does twice `V`:: sage: Q._coerce_map_from_(V) True diff --git a/src/sage/modules/free_quadratic_module.py b/src/sage/modules/free_quadratic_module.py index 787dca6308a..107d48bdd76 100644 --- a/src/sage/modules/free_quadratic_module.py +++ b/src/sage/modules/free_quadratic_module.py @@ -1120,7 +1120,7 @@ def __init__(self, ambient, basis, inner_product_matrix, """ Create a free module with basis over a PID. - EXAMPLES: + EXAMPLES:: sage: A = diagonal_matrix([1,2,2]) sage: M = FreeQuadraticModule(ZZ,3,inner_product_matrix=A) @@ -1402,7 +1402,7 @@ class FreeQuadraticModule_submodule_with_basis_field( sage: vector(QQ, W.coordinates(v)) * W.basis_matrix() (1, 5, 9) - We can load and save submodules: + We can load and save submodules:: sage: loads(W.dumps()) == W True @@ -1498,7 +1498,7 @@ class FreeQuadraticModule_submodule_field( EXAMPLES: Since this is an embedded vector subspace with echelonized basis, - the echelon_coordinates() and user coordinates() agree: + the echelon_coordinates() and user coordinates() agree:: sage: V = QQ^3 sage: W = V.span([[1,2,3],[4,5,6]]) diff --git a/src/sage/plot/circle.py b/src/sage/plot/circle.py index 2f51273c175..99372e07b62 100644 --- a/src/sage/plot/circle.py +++ b/src/sage/plot/circle.py @@ -172,7 +172,7 @@ def plot3d(self, z=0, **kwds): - ``z`` - optional 3D height above `xy`-plane. - EXAMPLES: + EXAMPLES:: sage: circle((0,0), 1).plot3d() diff --git a/src/sage/rings/bernoulli_mod_p.pyx b/src/sage/rings/bernoulli_mod_p.pyx index 1ee9fe4108f..478ba59e9da 100644 --- a/src/sage/rings/bernoulli_mod_p.pyx +++ b/src/sage/rings/bernoulli_mod_p.pyx @@ -39,7 +39,10 @@ def verify_bernoulli_mod_p(data): Computes checksum for bernoulli numbers. It checks the identity - $$ \sum_{n=0}^{(p-3)/2} 2^{2n} (2n+1) B_{2n} \equiv -2 \pmod p $$ + + .. MATH:: + + \sum_{n=0}^{(p-3)/2} 2^{2n} (2n+1) B_{2n} \equiv -2 \pmod p (see "Irregular Primes to One Million", Buhler et al) @@ -86,24 +89,24 @@ def verify_bernoulli_mod_p(data): def bernoulli_mod_p(int p): r""" - Returns the bernoulli numbers $B_0, B_2, ... B_{p-3}$ modulo $p$. + Returns the bernoulli numbers `B_0, B_2, ... B_{p-3}` modulo `p`. INPUT: p -- integer, a prime OUTPUT: - list -- Bernoulli numbers modulo $p$ as a list + list -- Bernoulli numbers modulo `p` as a list of integers [B(0), B(2), ... B(p-3)]. ALGORITHM: Described in accompanying latex file. PERFORMANCE: - Should be complexity $O(p \log p)$. + Should be complexity `O(p \log p)`. EXAMPLES: Check the results against PARI's C-library implementation (that - computes exact rationals) for $p = 37$: + computes exact rationals) for `p = 37`:: sage: bernoulli_mod_p(37) [1, 31, 16, 15, 16, 4, 17, 32, 22, 31, 15, 15, 17, 12, 29, 2, 0, 2] @@ -212,16 +215,16 @@ def bernoulli_mod_p(int p): def bernoulli_mod_p_single(long p, long k): r""" - Returns the bernoulli number $B_k$ mod $p$. + Returns the bernoulli number `B_k` mod `p`. - If $B_k$ is not $p$-integral, an ArithmeticError is raised. + If `B_k` is not `p`-integral, an ArithmeticError is raised. INPUT: p -- integer, a prime k -- non-negative integer OUTPUT: - The $k$-th bernoulli number mod $p$. + The `k`-th bernoulli number mod `p`. EXAMPLES: sage: bernoulli_mod_p_single(1009, 48) diff --git a/src/sage/rings/factorint.pyx b/src/sage/rings/factorint.pyx index 61b84551893..9fba2d82d9c 100644 --- a/src/sage/rings/factorint.pyx +++ b/src/sage/rings/factorint.pyx @@ -43,7 +43,7 @@ cpdef aurifeuillian(n, m, F=None): List of factors - EXAMPLES: + EXAMPLES:: sage: from sage.rings.factorint import aurifeuillian sage: aurifeuillian(2,2) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 9ae257698ff..52e1e38482d 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -6268,7 +6268,7 @@ def _subfields_helper(self, degree=0, name=None, both_maps=True, optimize=False) (Number Field in a0 with defining polynomial x^2 - 23, -4.79583152331) If we take a different embedding of the large field, we get a - different embedding of the degree 2 subfield: + different embedding of the degree 2 subfield:: sage: K. = NumberField(x^4 - 23, embedding=-50) sage: L2, _, _ = K.subfields(2)[0]; L2, CDF(L2.gen()) # indirect doctest @@ -6442,7 +6442,7 @@ def order(self, *args, **kwds): ValueError: each generator must be integral Alternatively, an order can be constructed by adjoining elements to - `\ZZ`: + `\ZZ`:: sage: K. = NumberField(x^3 - 2) sage: ZZ[a] @@ -7822,7 +7822,7 @@ def _libgap_(self): """ Return a LibGAP representation of ``self``. - TESTS: + TESTS:: sage: K = CyclotomicField(8) sage: K._libgap_() diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 0ad1eb3b2e1..6d8b2bb4188 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -2344,7 +2344,7 @@ cdef class NumberFieldElement(FieldElement): - a list whose length corresponding to the degree of this element written in terms of a generator. - EXAMPLES: + EXAMPLES:: sage: K. = NumberField(x^3 - 2) sage: (b^2 + 1)._coefficients() diff --git a/src/sage/rings/number_field/number_field_rel.py b/src/sage/rings/number_field/number_field_rel.py index 6af4ae6b584..5b832cde357 100644 --- a/src/sage/rings/number_field/number_field_rel.py +++ b/src/sage/rings/number_field/number_field_rel.py @@ -2218,7 +2218,7 @@ def order(self, *gens, **kwds): sage: R = P.order([a,b,c]); R Relative Order in Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field - The base ring of an order in a relative extension is still `\ZZ`.: + The base ring of an order in a relative extension is still `\ZZ`.:: sage: R.base_ring() Integer Ring diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 756cbd58dff..1aafe520c4b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -3052,7 +3052,7 @@ def reduce(self,p): True Here, we see that the relation that we just found in the quotient - is actually a consequence of the given relations: + is actually a consequence of the given relations:: sage: I.std() Twosided Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index c484f14775e..e8765c02b14 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -208,7 +208,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): sage: P.remove_var(y,z,x,w) Integer Ring - If possible, the term order is kept: + If possible, the term order is kept:: sage: R. = PolynomialRing(ZZ, order='deglex') sage: R.remove_var(y).term_order() diff --git a/src/sage/rings/polynomial/pbori.pyx b/src/sage/rings/polynomial/pbori.pyx index b98566e077f..b3194cd44aa 100644 --- a/src/sage/rings/polynomial/pbori.pyx +++ b/src/sage/rings/polynomial/pbori.pyx @@ -619,7 +619,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): sage: ZZ['a'].gen() + c a + c - Check that :trac:`13284` is fixed: + Check that :trac:`13284` is fixed:: sage: from sage.rings.ideal import Cyclic sage: R = BooleanPolynomialRing(10, 'x') @@ -1717,7 +1717,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): sage: C(y*z) > C(x) False - Now we change variable names: + Now we change variable names:: sage: P. = BooleanPolynomialRing(2) sage: P @@ -1729,7 +1729,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): sage: Q Boolean PolynomialRing in t, x1 - We can also append blocks to block orderings this way: + We can also append blocks to block orderings this way:: sage: R. = BooleanPolynomialRing(order='deglex(1),deglex(3)') sage: x2 > x3*x4 @@ -6592,7 +6592,7 @@ cdef class FGLMStrategy: sage: FGLMStrategy(old_ring, new_ring, ideal) - Check that :trac:`13883` is fixed: + Check that :trac:`13883` is fixed:: sage: nonreduced = BooleanPolynomialVector([x+z, x+y]) sage: FGLMStrategy(old_ring, new_ring, nonreduced) # optional - debug diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 21f6c0f8c09..6d891edd8e0 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -4807,7 +4807,7 @@ cdef class Polynomial(CommutativeAlgebraElement): doctest... UserWarning: NumPy does not support arbitrary precision arithmetic. The roots found will likely have less precision than you expect. [(-1.77245385090551..., 1), (1.77245385090551..., 1)] - We can also find roots over number fields: + We can also find roots over number fields:: sage: K. = CyclotomicField(15) sage: R. = PolynomialRing(K) diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index bd1ca1dcfbf..17827623b5d 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -58,7 +58,7 @@ EXAMPLES:: -A weighted directed graph given as a Python dictionary: +A weighted directed graph given as a Python dictionary:: sage: from sage.sandpiles import * sage: g = {0: {}, \ diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 6ada87e6e32..c7d3963e4eb 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -67,7 +67,7 @@ def __init__(self, x, y=None): Elliptic Curve defined by y^2 = x^3 + 2*x + 3 over Finite Field in a of size 101^2 Elliptic curves over `\ZZ/N\ZZ` with `N` prime are of type - "elliptic curve over a finite field": + "elliptic curve over a finite field":: sage: F = Zmod(101) sage: EllipticCurve(F, [2, 3]) diff --git a/src/sage/schemes/generic/homset.py b/src/sage/schemes/generic/homset.py index 50f851244fe..ed70feaa213 100644 --- a/src/sage/schemes/generic/homset.py +++ b/src/sage/schemes/generic/homset.py @@ -340,7 +340,7 @@ def _element_constructor_(self, x, check=True): From: Integer Ring To: Rational Field - TESTS: + TESTS:: sage: H._element_constructor_(f) Affine Scheme morphism: diff --git a/src/sage/schemes/hyperelliptic_curves/invariants.py b/src/sage/schemes/hyperelliptic_curves/invariants.py index 73ddfa23d0c..f7a5da7341b 100644 --- a/src/sage/schemes/hyperelliptic_curves/invariants.py +++ b/src/sage/schemes/hyperelliptic_curves/invariants.py @@ -274,7 +274,7 @@ def igusa_clebsch_invariants(f): TESTS:: - Let's check a symbolic example: + Let's check a symbolic example:: sage: R. = QQ[] sage: S. = R[] diff --git a/src/sage/schemes/toric/variety.py b/src/sage/schemes/toric/variety.py index 59924169884..a387a6d75a5 100644 --- a/src/sage/schemes/toric/variety.py +++ b/src/sage/schemes/toric/variety.py @@ -1366,7 +1366,7 @@ def is_affine(self): Boolean. - EXMAMPLES: + EXMAMPLES:: sage: toric_varieties.A2().is_affine() True diff --git a/src/sage/schemes/toric/weierstrass.py b/src/sage/schemes/toric/weierstrass.py index eef8e893662..ac6c1a8c664 100644 --- a/src/sage/schemes/toric/weierstrass.py +++ b/src/sage/schemes/toric/weierstrass.py @@ -778,7 +778,7 @@ def _check_polynomial_P1xP1(biquadric, variables): polynomial ring. A ``ValueError`` is raised if the polynomial is not homogeneous. - EXAMPLES: + EXAMPLES:: sage: from sage.schemes.toric.weierstrass import _check_polynomial_P1xP1 sage: R. = QQ[] diff --git a/src/sage/server/notebook/tutorial.py b/src/sage/server/notebook/tutorial.py index 0cc3fa94a4c..52c093a49b5 100644 --- a/src/sage/server/notebook/tutorial.py +++ b/src/sage/server/notebook/tutorial.py @@ -37,7 +37,7 @@ The Sage graphical user interface is unusual in that it operates via your web browser. It provides you with Sage worksheets that you can edit and evaluate, which contain scalable typeset mathematics and -beautiful antialised images. To try it out immediately, do this: +beautiful antialised images. To try it out immediately, do this:: sage: notebook(open_viewer=True) # not tested the sage notebook starts... diff --git a/src/sage/stats/hmm/hmm.pyx b/src/sage/stats/hmm/hmm.pyx index de35f2c7721..dd173940e5e 100644 --- a/src/sage/stats/hmm/hmm.pyx +++ b/src/sage/stats/hmm/hmm.pyx @@ -790,7 +790,7 @@ cdef class DiscreteHiddenMarkovModel(HiddenMarkovModel): - a nonnegative int - EXAMPLES: + EXAMPLES:: sage: m = hmm.DiscreteHiddenMarkovModel([[0.4,0.6],[0.1,0.9]], [[0.1,0.9],[0.9,0.1]], [.2,.8]) sage: set_random_seed(0) diff --git a/src/sage/stats/intlist.pyx b/src/sage/stats/intlist.pyx index a1a0547a62c..b62bc5a7ce2 100644 --- a/src/sage/stats/intlist.pyx +++ b/src/sage/stats/intlist.pyx @@ -559,7 +559,7 @@ def unpickle_intlist_v1(v, Py_ssize_t n): INPUT: v -- a raw char buffer - EXAMPLES: + EXAMPLES:: sage: v = stats.IntList([1,2,3]) sage: s = v.__reduce__()[1][0] diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 463eb7cf9d4..c2a30191d84 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -1381,7 +1381,7 @@ def unpickle_all(dir = None, debug=False, run_test_suite=False): Create a custom-made ``SourPickle`` for the last example. - If you want to find *lots* of little issues in Sage then try the following: + If you want to find *lots* of little issues in Sage then try the following:: sage: print "x"; sage.structure.sage_object.unpickle_all(run_test_suite = True) # todo: not tested diff --git a/src/sage/structure/sequence.py b/src/sage/structure/sequence.py index 6e14d4f2155..e600b4d9cbf 100644 --- a/src/sage/structure/sequence.py +++ b/src/sage/structure/sequence.py @@ -22,7 +22,7 @@ Rational Field then since ``w.universe()`` is `\QQ`, you're guaranteed that all -elements of `w` are rationals: +elements of `w` are rationals:: sage: v[0].parent() Integer Ring From 2c0f2faedc5ef5bd6c6d4e2c76530d1910d1feba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 26 Aug 2013 18:48:39 +0200 Subject: [PATCH 023/206] a few more details in the doc of fast_callable --- src/sage/ext/fast_callable.pyx | 152 +++++++++++++++--------------- src/sage/schemes/toric/variety.py | 2 +- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 50e3ad39e76..b2efedf56e5 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -13,34 +13,34 @@ inefficient. This module provides a function, \function{fast_callable}, to transform such expressions into a form where they can be evaluated -quickly. +quickly:: -sage: f = sin(x) + 3*x^2 -sage: ff = fast_callable(f, vars=[x]) -sage: ff(3.5) -36.3992167723104 -sage: ff(RIF(3.5)) -36.39921677231038? + sage: f = sin(x) + 3*x^2 + sage: ff = fast_callable(f, vars=[x]) + sage: ff(3.5) + 36.3992167723104 + sage: ff(RIF(3.5)) + 36.39921677231038? By default, \function{fast_callable} only removes some interpretive overhead from the evaluation, but all of the individual arithmetic operations are done using standard \sage arithmetic. This is still a huge win over sage.calculus, which evidently has a lot of overhead. Compare the cost of evaluating Wilkinson's polynomial (in unexpanded -form) at x=30: +form) at x=30:: -sage: wilk = prod((x-i) for i in [1 .. 20]); wilk -(x - 1)*(x - 2)*(x - 3)*(x - 4)*(x - 5)*(x - 6)*(x - 7)*(x - 8)*(x - 9)*(x - 10)*(x - 11)*(x - 12)*(x - 13)*(x - 14)*(x - 15)*(x - 16)*(x - 17)*(x - 18)*(x - 19)*(x - 20) -sage: timeit('wilk.subs(x=30)') # random, long time -625 loops, best of 3: 1.43 ms per loop -sage: fc_wilk = fast_callable(wilk, vars=[x]) -sage: timeit('fc_wilk(30)') # random, long time -625 loops, best of 3: 9.72 us per loop + sage: wilk = prod((x-i) for i in [1 .. 20]); wilk + (x - 1)*(x - 2)*(x - 3)*(x - 4)*(x - 5)*(x - 6)*(x - 7)*(x - 8)*(x - 9)*(x - 10)*(x - 11)*(x - 12)*(x - 13)*(x - 14)*(x - 15)*(x - 16)*(x - 17)*(x - 18)*(x - 19)*(x - 20) + sage: timeit('wilk.subs(x=30)') # random, long time + 625 loops, best of 3: 1.43 ms per loop + sage: fc_wilk = fast_callable(wilk, vars=[x]) + sage: timeit('fc_wilk(30)') # random, long time + 625 loops, best of 3: 9.72 us per loop You can specify a particular domain for the evaluation using -\code{domain=}: +\code{domain=}:: -sage: fc_wilk_zz = fast_callable(wilk, vars=[x], domain=ZZ) + sage: fc_wilk_zz = fast_callable(wilk, vars=[x], domain=ZZ) The meaning of domain=D is that each intermediate and final result is converted to type D. For instance, the previous example of @@ -56,77 +56,77 @@ To mitigate the costs, we check whether the value already has the correct parent before we call D. We don't yet have a special interpreter with domain ZZ, so we can see -how that compares to the generic fc_wilk example above: +how that compares to the generic fc_wilk example above:: -sage: timeit('fc_wilk_zz(30)') # random, long time -625 loops, best of 3: 15.4 us per loop + sage: timeit('fc_wilk_zz(30)') # random, long time + 625 loops, best of 3: 15.4 us per loop However, for other types, using domain=D will get a large speedup, because we have special-purpose interpreters for those types. One example is RDF. Since with domain=RDF we know that every single operation will be floating-point, we can just execute the floating-point operations directly and skip all the Python object -creations that you would get from actually using RDF objects. +creations that you would get from actually using RDF objects:: -sage: fc_wilk_rdf = fast_callable(wilk, vars=[x], domain=RDF) -sage: timeit('fc_wilk_rdf(30.0)') # random, long time -625 loops, best of 3: 7 us per loop + sage: fc_wilk_rdf = fast_callable(wilk, vars=[x], domain=RDF) + sage: timeit('fc_wilk_rdf(30.0)') # random, long time + 625 loops, best of 3: 7 us per loop The domain does not need to be a Sage type; for instance, domain=float also works. (We actually use the same fast interpreter for domain=float and domain=RDF; the only difference is that when domain=RDF is used, the return value is an RDF element, and when domain=float is used, -the return value is a Python float.) +the return value is a Python float.) :: -sage: fc_wilk_float = fast_callable(wilk, vars=[x], domain=float) -sage: timeit('fc_wilk_float(30.0)') # random, long time -625 loops, best of 3: 5.04 us per loop + sage: fc_wilk_float = fast_callable(wilk, vars=[x], domain=float) + sage: timeit('fc_wilk_float(30.0)') # random, long time + 625 loops, best of 3: 5.04 us per loop -We also have support for ``RR``: +We also have support for ``RR``:: -sage: fc_wilk_rr = fast_callable(wilk, vars=[x], domain=RR) -sage: timeit('fc_wilk_rr(30.0)') # random, long time -625 loops, best of 3: 13 us per loop + sage: fc_wilk_rr = fast_callable(wilk, vars=[x], domain=RR) + sage: timeit('fc_wilk_rr(30.0)') # random, long time + 625 loops, best of 3: 13 us per loop -And support for CDF:: +And support for ``CDF``:: -sage: fc_wilk_rr = fast_callable(wilk, vars=[x], domain=CDF) -sage: timeit('fc_wilk_rr(30.0)') # random, long time -625 loops, best of 3: 10.2 us per loop + sage: fc_wilk_rr = fast_callable(wilk, vars=[x], domain=CDF) + sage: timeit('fc_wilk_rr(30.0)') # random, long time + 625 loops, best of 3: 10.2 us per loop Currently, \function{fast_callable} can accept two kinds of objects: polynomials (univariate and multivariate) and symbolic expressions (elements of the Symbolic Ring). (This list is likely to grow significantly in the near future.) For polynomials, you can omit the 'vars' argument; the variables will default to the ring generators (in -the order used when creating the ring). +the order used when creating the ring). :: -sage: K. = QQ[] -sage: p = 10*y + 100*z + x -sage: fp = fast_callable(p) -sage: fp(1,2,3) -321 + sage: K. = QQ[] + sage: p = 10*y + 100*z + x + sage: fp = fast_callable(p) + sage: fp(1,2,3) + 321 But you can also specify the variable names to override the default -ordering (you can include extra variable names here, too). +ordering (you can include extra variable names here, too). :: -sage: fp = fast_callable(p, vars=('x','w','z','y')) + sage: fp = fast_callable(p, vars=('x','w','z','y')) For symbolic expressions, you need to specify the variable names, so -that \function{fast_callable} knows what order to use. +that \function{fast_callable} knows what order to use. :: -sage: var('y,z,x') -(y, z, x) -sage: f = 10*y + 100*z + x -sage: ff = fast_callable(f, vars=(x,y,z)) -sage: ff(1,2,3) -321 + sage: var('y,z,x') + (y, z, x) + sage: f = 10*y + 100*z + x + sage: ff = fast_callable(f, vars=(x,y,z)) + sage: ff(1,2,3) + 321 -You can also specify extra variable names: +You can also specify extra variable names:: -sage: ff = fast_callable(f, vars=('x','w','z','y')) -sage: ff(1,2,3,4) -341 + sage: ff = fast_callable(f, vars=('x','w','z','y')) + sage: ff(1,2,3,4) + 341 This should be enough for normal use of \function{fast_callable}; let's discuss some more advanced topics. @@ -140,51 +140,51 @@ Internally, \function{fast_callable} works in two stages: it constructs an expression tree from its argument, and then it builds a fast evaluator from that expression tree. You can bypass the first phase by building your own expression tree and passing that directly to -\function{fast_callable}, using an \class{ExpressionTreeBuilder}. +\function{fast_callable}, using an \class{ExpressionTreeBuilder}. :: -sage: from sage.ext.fast_callable import ExpressionTreeBuilder -sage: etb = ExpressionTreeBuilder(vars=('x','y','z')) + sage: from sage.ext.fast_callable import ExpressionTreeBuilder + sage: etb = ExpressionTreeBuilder(vars=('x','y','z')) An \class{ExpressionTreeBuilder} has three interesting methods: \method{constant}, \method{var}, and \method{call}. All of these methods return \class{ExpressionTree} objects. The \method{var} method takes a string, and returns an expression tree -for the corresponding variable. +for the corresponding variable. :: -sage: x = etb.var('x') -sage: y = etb.var('y') -sage: z = etb.var('y') + sage: x = etb.var('x') + sage: y = etb.var('y') + sage: z = etb.var('y') Expression trees support Python's numeric operators, so you can easily -build expression trees representing arithmetic expressions. +build expression trees representing arithmetic expressions. :: -sage: v1 = (x+y)*(y+z) + (y//z) + sage: v1 = (x+y)*(y+z) + (y//z) The \method{constant} method takes a \sage value, and returns an -expression tree representing that value. +expression tree representing that value. :: -sage: v2 = etb.constant(3.14159) * x + etb.constant(1729) * y + sage: v2 = etb.constant(3.14159) * x + etb.constant(1729) * y The \method{call} method takes a \sage/Python function and zero or more expression trees, and returns an expression tree representing -the function call. +the function call. :: -sage: v3 = etb.call(sin, v1+v2) -sage: v3 -sin(add(add(mul(add(v_0, v_1), add(v_1, v_1)), floordiv(v_1, v_1)), add(mul(3.14159000000000, v_0), mul(1729, v_1)))) + sage: v3 = etb.call(sin, v1+v2) + sage: v3 + sin(add(add(mul(add(v_0, v_1), add(v_1, v_1)), floordiv(v_1, v_1)), add(mul(3.14159000000000, v_0), mul(1729, v_1)))) Many \sage/Python built-in functions are specially handled; for instance, when evaluating an expression involving \function{sin} over \code{RDF}, the C math library function \function{sin} is called. Arbitrary functions are allowed, but will be much slower since they will call back to -Python code on every call; for example, the following will work. +Python code on every call; for example, the following will work. :: -sage: def my_sqrt(x): return pow(x, 0.5) -sage: e = etb.call(my_sqrt, v1); e -{my_sqrt}(add(mul(add(v_0, v_1), add(v_1, v_1)), floordiv(v_1, v_1))) -sage: fast_callable(e)(1, 2, 3) -3.60555127546399 + sage: def my_sqrt(x): return pow(x, 0.5) + sage: e = etb.call(my_sqrt, v1); e + {my_sqrt}(add(mul(add(v_0, v_1), add(v_1, v_1)), floordiv(v_1, v_1))) + sage: fast_callable(e)(1, 2, 3) + 3.60555127546399 To provide \function{fast_callable} for your own class (so that \code{fast_callable(x)} works when \variable{x} is an instance of your diff --git a/src/sage/schemes/toric/variety.py b/src/sage/schemes/toric/variety.py index a387a6d75a5..0a17669d238 100644 --- a/src/sage/schemes/toric/variety.py +++ b/src/sage/schemes/toric/variety.py @@ -1366,7 +1366,7 @@ def is_affine(self): Boolean. - EXMAMPLES:: + EXAMPLES:: sage: toric_varieties.A2().is_affine() True From ee320334af1a43233536bd1d3f0b67abdf6e1197 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 20 Aug 2013 17:32:42 +0200 Subject: [PATCH 024/206] fr tutorial: Reviewer's patch --- src/doc/fr/tutorial/interactive_shell.rst | 5 ++++- src/doc/fr/tutorial/latex.rst | 22 +++++++++++++++++----- src/doc/fr/tutorial/tour_coercion.rst | 6 +++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/doc/fr/tutorial/interactive_shell.rst b/src/doc/fr/tutorial/interactive_shell.rst index de832557991..84724787193 100644 --- a/src/doc/fr/tutorial/interactive_shell.rst +++ b/src/doc/fr/tutorial/interactive_shell.rst @@ -382,7 +382,10 @@ Trucs et astuces IPython Comme signalé plus haut, Sage utilise l'interpréteur de commandes IPython, et met donc à votre disposition toutes les commandes et fonctionnalités de -celui-ci. Vous voudrez peut-être consulter la `documentation complète de IPython `_. Voici en attendant quelques astuces utiles -- qui reposent sur ce que IPython appelle des « commandes magiques » : +celui-ci. Vous voudrez peut-être consulter la `documentation complète de IPython +`_. Voici en attendant quelques +astuces utiles -- qui reposent sur ce que IPython appelle des « commandes +magiques » : - La commande magique ``%bg`` lance une commande en arrière-plan. Le résultat sera ensuite accessible à travers l'objet ``jobs``, comme dans l'exemple diff --git a/src/doc/fr/tutorial/latex.rst b/src/doc/fr/tutorial/latex.rst index 6531f14149a..0a314cb45cb 100644 --- a/src/doc/fr/tutorial/latex.rst +++ b/src/doc/fr/tutorial/latex.rst @@ -121,6 +121,16 @@ en image pour l'afficher dans le bloc-note. La section :ref:`sec-custom-generation` ci-dessous explique comment configurer et contrôler ce processus. +La commande interne ``pretty_print()`` permet de convertir un objet Sage en code +HTML utilisant MathJax. C'est le code qui sera ensuite utilisé dans le +bloc-notes :: + + sage: from sage.misc.latex import pretty_print + sage: pretty_print(x^12) + + sage: pretty_print(integrate(sin(x), x)) + + Le bloc-notes dispose de deux autres fonctionnalités pour appeler LaTeX. Premièrement, lorsque la case « Typeset » (juste au-dessus de la première cellule d'une feuille de travail, à droite des quatre listes déroulantes) est @@ -277,9 +287,9 @@ Personnaliser le traitement du code par LaTeX En plus de modifier LaTeX produit par Sage, on peut choisir la variante de TeX appelée pour le traiter, et donc la nature du document produit. De même, il -est possible de contrôler dans quelles circonstances le bloc-notes utilise -MathJax (c'est-à-dire quels fragments de code TeX sont jugés suffisamment -simples) et quand est-ce qu'il se rabat sur l'installation de TeX du système. +est possible de contrôler dans quelles circonstances le bloc-notes utilisera +MathJax (c'est-à-dire quels fragments de code TeX seront jugés suffisamment +simples) et quand il choisira de se rabattre sur l'installation de TeX du système. La méthode ``latex.engine()`` permet de choisir lequel des moteurs TeX ``latex``, ``pdflatex`` et ``xelatex`` doit servir à compiler les expressions @@ -427,11 +437,13 @@ L'utilitaire ``convert`` fait partie de la boîte à outils `ImageMagick paquets de votre système ou facile à télécharger et installer). Les programmes ``dvipng``, ``ps2pdf``, and ``dvips`` sont parfois inclus dans les installations de TeX, et les deux premiers sont par ailleurs disponibles -respectivement à l'adresse http://sourceforge.net/projects/dvipng/ et dans +respectivement à l'adresse http://sourceforge.net/projects/dvipng/ et dans `Ghostscript `_. Le rendu des graphes nécessite une version suffisamment récente de PGF, ainsi -que les fichiers ``tkz-graph.sty``, ``tkz-arith.sty`` et suivant les cas ``tkz-berge.sty``, tous issus du site web `Altermundus `_ (`version anglaise +que les fichiers ``tkz-graph.sty``, ``tkz-arith.sty`` et suivant les cas +``tkz-berge.sty``, tous issus du site web `Altermundus +`_ (`version anglaise `_). Programmes externes diff --git a/src/doc/fr/tutorial/tour_coercion.rst b/src/doc/fr/tutorial/tour_coercion.rst index 3c150689ac8..4f863887d73 100644 --- a/src/doc/fr/tutorial/tour_coercion.rst +++ b/src/doc/fr/tutorial/tour_coercion.rst @@ -276,7 +276,7 @@ des variables. Nous avons donc : sage: R2(y) y -En l'absence den morphisme d'anneau qui préserve les noms de variable, la +En l'absence d'un morphisme d'anneau qui préserve les noms de variable, la coercition entre anneaux de polynômes multivariés n'est pas définie. Il peut tout de même exister une conversion qui envoie les variables d'un anneau sur celle de l'autre en fonction de leur position dans la liste des générateurs : @@ -337,7 +337,7 @@ De même, on a True Une autre conséquence de la condition de cohérence est que les coercitions ne -sont possible que des anneaux exacts (comme les rationnels ``QQ``) vers les +sont possibles que des anneaux exacts (comme les rationnels ``QQ``) vers les anneaux inexacts (comme les réels à précision donnée ``RR``), jamais l'inverse. En effet, pour qu'une conversion de ``RR`` dans ``QQ`` puisse être une coercition, il faudrait que la composée de la coercition de ``QQ`` dans ``RR`` @@ -389,7 +389,7 @@ Dans l'exemple suivant, il n'y a pas de coercition vers un parent commun : ... TypeError: unsupported operand parent(s) for '+': 'Univariate Polynomial Ring in x over Rational Field' and 'Univariate Polynomial Ring in y over Rational Field' -En effet, Sage refuse de choisi entre les candidats ``QQ['x']['y']``, +En effet, Sage refuse de choisir entre les candidats ``QQ['x']['y']``, ``QQ['y']['x']``, ``QQ['x','y']`` et ``QQ['y','x']``, car ces quatre structures deux à deux distinctes semblent toutes des parents communs naturels, et aucun choix canonique ne s'impose. From 667d8fce5c8a7406cb055166d522983b6d799deb Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Wed, 28 Aug 2013 09:08:58 +0200 Subject: [PATCH 025/206] Fix broken example. --- src/doc/fr/tutorial/latex.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/fr/tutorial/latex.rst b/src/doc/fr/tutorial/latex.rst index 0a314cb45cb..b2dd89e1e3b 100644 --- a/src/doc/fr/tutorial/latex.rst +++ b/src/doc/fr/tutorial/latex.rst @@ -259,7 +259,7 @@ contre-obliques sont dédoublées. :: sage: latex.extra_preamble('') sage: latex.add_to_preamble('\\usepackage{geometry}') sage: latex.add_to_preamble('\\geometry{letterpaper,total={8in,10in}}') - sage: latex.indirectement les marges) du document LaTeX. ) + sage: latex.extra_preamble() '\\usepackage{geometry}\\geometry{letterpaper,total={8in,10in}}' sage: print latex_extra_preamble() \usepackage{geometry}\geometry{letterpaper,total={8in,10in}} From 391d4b724079c011222b2ce033af38c5836f67a2 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Wed, 28 Aug 2013 10:37:29 +0200 Subject: [PATCH 026/206] Update to sage-5.12.beta3 Propagate output change introduced by #14382 --- src/doc/fr/tutorial/latex.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/fr/tutorial/latex.rst b/src/doc/fr/tutorial/latex.rst index b2dd89e1e3b..5ca6a2ad15d 100644 --- a/src/doc/fr/tutorial/latex.rst +++ b/src/doc/fr/tutorial/latex.rst @@ -63,7 +63,7 @@ Voici quelques exemples d'utilisation élémentaire de la fonction ``latex()``. sage: latex(integrate(z^4, z)) \frac{1}{5} \, z^{5} sage: latex('a string') - \verb|a|\phantom{\verb!x!}\verb|string| + \text{\texttt{a{ }string}} sage: latex(QQ) \Bold{Q} sage: latex(matrix(QQ, 2, 3, [[2,4,6],[-1,-1,-1]])) From 940c518e58438f56e9f9e068bbeeac3b31daf355 Mon Sep 17 00:00:00 2001 From: Paul Zimmermann Date: Sun, 25 Aug 2013 11:07:39 +0000 Subject: [PATCH 027/206] #15097: improve documentation of is_prime --- src/sage/rings/integer.pyx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 00101a954e5..79f26101c4d 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -4452,6 +4452,20 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sage: z.is_prime(proof=True) True + When starting Sage the arithmetic proof flag is True. We can change + it to False as follows:: + + sage: proof.arithmetic() + True + sage: n = 10^100 + 267 + sage: timeit("n.is_prime()") # random + 5 loops, best of 3: 163 ms per loop + sage: proof.arithmetic(False) + sage: proof.arithmetic() + False + sage: timeit("n.is_prime()") # random + 1000 loops, best of 3: 573 us per loop + IMPLEMENTATION: Calls the PARI ``isprime`` function. """ from sage.structure.proof.proof import get_flag From f82b4226e689e4d0dc211afc2b8ad6cbfc3031ac Mon Sep 17 00:00:00 2001 From: Felix Salfelder Date: Fri, 19 Jul 2013 09:28:45 +0200 Subject: [PATCH 028/206] cython: patch: gdb output dir option --- build/pkgs/cython/patches/gdbout.patch | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 build/pkgs/cython/patches/gdbout.patch diff --git a/build/pkgs/cython/patches/gdbout.patch b/build/pkgs/cython/patches/gdbout.patch new file mode 100644 index 00000000000..bf188e988c4 --- /dev/null +++ b/build/pkgs/cython/patches/gdbout.patch @@ -0,0 +1,20 @@ +--- src.orig/Cython/Compiler/CmdLine.py 2013-05-11 07:46:55.000000000 +0200 ++++ src/Cython/Compiler/CmdLine.py 2013-06-04 19:09:59.000000000 +0200 +@@ -28,6 +28,7 @@ Options: + -w, --working Sets the working directory for Cython (the directory modules + are searched from) + --gdb Output debug information for cygdb ++ --gdb-outdir Specify gdb debug information output directory. Implies --gdb. + + -D, --no-docstrings Strip docstrings from the compiled module. + -a, --annotate Produce a colorized HTML version of the source. +@@ -138,6 +125,9 @@ def parse_command_line(args): + elif option == "--gdb": + options.gdb_debug = True + options.output_dir = os.curdir ++ elif option == "--gdb-outdir": ++ options.gdb_debug = True ++ options.output_dir = pop_arg() + elif option == '-2': + options.language_level = 2 + elif option == '-3': From 15a41647c634421769963f6b6ccabe65c7907789 Mon Sep 17 00:00:00 2001 From: Jan Keitel Date: Tue, 3 Sep 2013 17:44:39 +0200 Subject: [PATCH 029/206] Replaced more private_variables by cached_method to enable pickling of ToricVarieties. --- src/sage/schemes/toric/variety.py | 106 +++++++++++++----------------- 1 file changed, 47 insertions(+), 59 deletions(-) diff --git a/src/sage/schemes/toric/variety.py b/src/sage/schemes/toric/variety.py index c99ff04d521..3f067d5784f 100644 --- a/src/sage/schemes/toric/variety.py +++ b/src/sage/schemes/toric/variety.py @@ -1444,6 +1444,7 @@ def is_smooth(self): """ return self.fan().is_smooth() + @cached_method def Kaehler_cone(self): r""" Return the closure of the Kähler cone of ``self``. @@ -1475,23 +1476,22 @@ def Kaehler_cone(self): Basis lattice of The toric rational divisor class group of a 2-d CPR-Fano toric variety covered by 4 affine patches """ - if "_Kaehler_cone" not in self.__dict__: - fan = self.fan() - GT = fan.Gale_transform().columns() - from sage.schemes.toric.divisor import \ - ToricRationalDivisorClassGroup_basis_lattice - L = ToricRationalDivisorClassGroup_basis_lattice( - self.rational_class_group()) - n = fan.nrays() - K = None - for cone in fan: - sigma = Cone([GT[i] for i in range(n) - if i not in cone.ambient_ray_indices()], - lattice=L) - K = K.intersection(sigma) if K is not None else sigma - self._Kaehler_cone = K - return self._Kaehler_cone + fan = self.fan() + GT = fan.Gale_transform().columns() + from sage.schemes.toric.divisor import \ + ToricRationalDivisorClassGroup_basis_lattice + L = ToricRationalDivisorClassGroup_basis_lattice( + self.rational_class_group()) + n = fan.nrays() + K = None + for cone in fan: + sigma = Cone([GT[i] for i in range(n) + if i not in cone.ambient_ray_indices()], + lattice=L) + K = K.intersection(sigma) if K is not None else sigma + return K + @cached_method def Mori_cone(self): r""" Returns the Mori cone of ``self``. @@ -1531,13 +1531,11 @@ def Mori_cone(self): in Ambient free module of rank 7 over the principal ideal domain Integer Ring """ - if "_Mori_cone" not in self.__dict__: - # Ideally, self.Kaehler_cone().dual() should be it, but - # so far this is not the case. - rays = (ray * self._fan.Gale_transform() - for ray in self.Kaehler_cone().dual().rays()) - self._Mori_cone = Cone(rays, lattice=ZZ**(self._fan.nrays()+1)) - return self._Mori_cone + # Ideally, self.Kaehler_cone().dual() should be it, but + # so far this is not the case. + rays = (ray * self._fan.Gale_transform() + for ray in self.Kaehler_cone().dual().rays()) + return Cone(rays, lattice=ZZ**(self._fan.nrays()+1)) def plot(self, **options): r""" @@ -1972,10 +1970,12 @@ def cohomology_ring(self): TESTS: The cohomology ring is a circular reference that is - potentially troublesome on unpickling, see :trac:`15050` :: + potentially troublesome on unpickling, see :trac:`15050` + and :trac:`15149` :: sage: variety = toric_varieties.P(1) - sage: _ = variety.cohomology_ring(), variety.cohomology_basis(), variety.volume_class() + sage: a = [variety.cohomology_ring(), variety.cohomology_basis(), variety.volume_class()] + sage: b = [variety.Todd_class(), variety.Chern_class(), variety.Chern_character(), variety.Kaehler_cone(), variety.Mori_cone()] sage: loads(dumps(variety)) == variety True """ @@ -2165,6 +2165,7 @@ def integrate(self, cohomology_class): if top_form.is_zero(): return 0 return top_form.lc() / self.volume_class().lc() + @cached_method def Chern_class(self, deg=None): """ Return Chern classes of the (tangent bundle of the) toric variety. @@ -2202,17 +2203,13 @@ def Chern_class(self, deg=None): True """ assert self.is_orbifold(), "Requires the toric variety to be an orbifold." - try: - c = self._chern_class - except AttributeError: - c = prod([ 1+self.cohomology_ring().gen(i) for i in range(0,self._fan.nrays()) ]) - self._chern_class = c - + c = prod([ 1+self.cohomology_ring().gen(i) for i in range(0,self._fan.nrays()) ]) if deg==None: return c else: return c.part_of_degree(deg) + @cached_method def Chern_character(self, deg=None): """ Return the Chern character (of the tangent bundle) of the toric @@ -2247,19 +2244,15 @@ def Chern_character(self, deg=None): True """ assert self.is_orbifold(), "Requires the toric variety to be an orbifold." - try: - ch = self._chern_character - except AttributeError: - n_rels = self._fan.nrays() - self.dimension() - ch = sum([ self.cohomology_ring().gen(i).exp() - for i in range(0,self._fan.nrays()) ]) - n_rels - self._chern_character = ch - + n_rels = self._fan.nrays() - self.dimension() + ch = sum([ self.cohomology_ring().gen(i).exp() + for i in range(0,self._fan.nrays()) ]) - n_rels if deg==None: return ch else: return ch.part_of_degree(deg) + @cached_method def Todd_class(self, deg=None): """ Return the Todd class (of the tangent bundle) of the toric variety. @@ -2291,26 +2284,21 @@ def Todd_class(self, deg=None): sage: dP6.integrate( dP6.Td() ) 1 """ - try: - Td = self._Todd - except AttributeError: - Td = QQ(1) - if self.dimension() >= 1: - c1 = self.Chern_class(1) - Td += QQ(1)/2 * c1 - if self.dimension() >= 2: - c2 = self.Chern_class(2) - Td += QQ(1)/12 * (c1**2 + c2) - if self.dimension() >= 3: - Td += QQ(1)/24 * c1*c2 - if self.dimension() >= 4: - c3 = self.Chern_class(3) - c4 = self.Chern_class(4) - Td += -QQ(1)/720 * (c1**4 -4*c1**2*c2 -3*c2**2 -c1*c3 +c4) - if self.dimension() >= 5: - raise NotImplemented, 'Todd class is currently only implemented up to degree 4' - self._Todd = Td - + Td = QQ(1) + if self.dimension() >= 1: + c1 = self.Chern_class(1) + Td += QQ(1)/2 * c1 + if self.dimension() >= 2: + c2 = self.Chern_class(2) + Td += QQ(1)/12 * (c1**2 + c2) + if self.dimension() >= 3: + Td += QQ(1)/24 * c1*c2 + if self.dimension() >= 4: + c3 = self.Chern_class(3) + c4 = self.Chern_class(4) + Td += -QQ(1)/720 * (c1**4 -4*c1**2*c2 -3*c2**2 -c1*c3 +c4) + if self.dimension() >= 5: + raise NotImplementedError('Todd class is currently only implemented up to degree 4') if deg==None: return Td else: From d0dde81ff2e8027dfb58322f538c4d818fa68fd1 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Thu, 5 Sep 2013 22:37:37 +0100 Subject: [PATCH 030/206] Refactoring ChainComplex as a Parent for Chains * Various code refactoring to make it less copy&pasteish * Slight change of conventions: ChainComplex._diff shall contain all differentials that are not 0x0. Before, we left out "obvious" incoming zero differentials that are 0xn. --- src/sage/homology/cell_complex.py | 36 +- src/sage/homology/chain_complex.py | 981 ++++++++------------ src/sage/homology/chain_complex_morphism.py | 81 +- src/sage/homology/cubical_complex.py | 4 +- src/sage/homology/delta_complex.py | 2 +- src/sage/homology/homology_group.py | 185 ++++ src/sage/homology/matrix_utils.py | 212 +++++ src/sage/homology/simplicial_complex.py | 7 +- src/sage/homology/tests.py | 2 +- 9 files changed, 835 insertions(+), 675 deletions(-) create mode 100644 src/sage/homology/homology_group.py create mode 100644 src/sage/homology/matrix_utils.py diff --git a/src/sage/homology/cell_complex.py b/src/sage/homology/cell_complex.py index 66bcfa55979..e821e4db3ff 100644 --- a/src/sage/homology/cell_complex.py +++ b/src/sage/homology/cell_complex.py @@ -20,19 +20,20 @@ the :meth:`~GenericCellComplex.homology` method get passed on to the :meth:`~GenericCellComplex.chain_complex` method and also to the constructor for chain complexes in - :class:`~sage.homology.chain_complex.ChainComplex`, as well its + :class:`sage.homology.chain_complex.ChainComplex_class `, as well its associated - :meth:`~sage.homology.chain_complex.ChainComplex.homology` method. + :meth:`~sage.homology.chain_complex.ChainComplex_class.homology` method. This means that those keywords should have consistent meaning in all of those situations. It also means that it is easy to implement new keywords: for example, if you implement a new keyword for the - :meth:`sage.homology.chain_complex.ChainComplex.homology` method, + :meth:`sage.homology.chain_complex.ChainComplex_class.homology` method, then it will be automatically accessible through the :meth:`~GenericCellComplex.homology` method for cell complexes -- just make sure it gets documented. """ + from sage.structure.sage_object import SageObject from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ @@ -438,7 +439,7 @@ def homology(self, dim=None, **kwds): method, which calls the Pari package. For any large matrix, reduce it using the Dumas, Heckenbach, Saunders, and Welker elimination algorithm: see - :func:`sage.homology.chain_complex.dhsw_snf` for details. + :func:`sage.homology.matrix_utils.dhsw_snf` for details. Finally, ``algorithm`` may also be 'pari' or 'dhsw', which forces the named algorithm to be used regardless of the size @@ -485,7 +486,7 @@ def homology(self, dim=None, **kwds): from sage.homology.cubical_complex import CubicalComplex from sage.homology.simplicial_complex import SimplicialComplex from sage.modules.all import VectorSpace - from sage.homology.chain_complex import HomologyGroup + from sage.homology.homology_group import HomologyGroup base_ring = kwds.get('base_ring', ZZ) cohomology = kwds.get('cohomology', False) @@ -526,22 +527,13 @@ def homology(self, dim=None, **kwds): answer = {} if not dims: for d in range(self.dimension() + 1): - if base_ring == ZZ: - answer[d] = H.get(d, HomologyGroup(0)) - else: - answer[d] = H.get(d, VectorSpace(base_ring, 0)) + answer[d] = H.get(d, HomologyGroup(0, base_ring)) else: for d in dims: - if base_ring == ZZ: - answer[d] = H.get(d, HomologyGroup(0)) - else: - answer[d] = H.get(d, VectorSpace(base_ring, 0)) + answer[d] = H.get(d, HomologyGroup(0, base_ring)) if dim is not None: if not isinstance(dim, (list, tuple)): - if base_ring == ZZ: - answer = answer.get(dim, HomologyGroup(0)) - else: - answer = answer.get(dim, VectorSpace(base_ring, 0)) + answer = answer.get(dim, HomologyGroup(0, base_ring)) return answer # Derived classes can implement specialized algorithms using a @@ -567,10 +559,7 @@ def homology(self, dim=None, **kwds): del answer[-1] for d in range(self.dimension() + 1): if d not in answer: - if base_ring == ZZ: - answer[d] = HomologyGroup(0) - else: - answer[d] = VectorSpace(base_ring, 0) + answer[d] = HomologyGroup(0, base_ring) if dim is not None: if isinstance(dim, (list, tuple)): @@ -579,10 +568,7 @@ def homology(self, dim=None, **kwds): temp[n] = answer[n] answer = temp else: # just a single dimension - if base_ring == ZZ: - answer = answer.get(dim, HomologyGroup(0)) - else: - answer = answer.get(dim, VectorSpace(base_ring, 0)) + answer = answer.get(dim, HomologyGroup(0, base_ring)) return answer def cohomology(self, dim=None, **kwds): diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index b6d8c7bc1a3..4c2eede90a7 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -5,9 +5,9 @@ - John H. Palmieri (2009-04) -This module implements chain complexes of free `R`-modules, for any -commutative ring `R` (although the interesting things, like homology, -only work if `R` is the integers or a field). +This module implements bounded chain complexes of free `R`-modules, +for any commutative ring `R` (although the interesting things, like +homology, only work if `R` is the integers or a field). Fix a ring `R`. A chain complex over `R` is a collection of `R`-modules `\{C_n\}` indexed by the integers, with `R`-module maps @@ -33,204 +33,34 @@ parameter used in defining the chain complex. """ -from sage.structure.sage_object import SageObject + +######################################################################## +# Copyright (C) 2013 John H. Palmieri +# Volker Braun +# +# 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/ +######################################################################## + +from copy import copy + +from sage.structure.parent import Parent +from sage.structure.element import ModuleElement +from sage.misc.cachefunc import cached_method + from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.modules.free_module import FreeModule, VectorSpace +from sage.modules.free_module import FreeModule from sage.modules.free_module_element import vector from sage.matrix.matrix0 import Matrix from sage.matrix.constructor import matrix, prepare_dict from sage.misc.latex import latex -from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup_fixed_gens -#from sage.groups.abelian_gps.abelian_group import AbelianGroup_class from sage.rings.all import GF, prime_range +from sage.misc.decorators import rename_keyword -def dhsw_snf(mat, verbose=False): - """ - Preprocess a matrix using the "Elimination algorithm" described by - Dumas et al. [DHSW]_, and then call ``elementary_divisors`` on the - resulting (smaller) matrix. - - .. NOTE:: - - 'snf' stands for 'Smith Normal Form.' - - INPUT: - - - ``mat`` -- an integer matrix, either sparse or dense. - - (They use the transpose of the matrix considered here, so they use - rows instead of columns.) - - Algorithm: go through ``mat`` one column at a time. For each - column, add multiples of previous columns to it until either - - - it's zero, in which case it should be deleted. - - its first nonzero entry is 1 or -1, in which case it should be kept. - - its first nonzero entry is something else, in which case it is - deferred until the second pass. - - Then do a second pass on the deferred columns. - - At this point, the columns with 1 or -1 in the first entry - contribute to the rank of the matrix, and these can be counted and - then deleted (after using the 1 or -1 entry to clear out its row). - Suppose that there were `N` of these. - - The resulting matrix should be much smaller; we then feed it - to Sage's ``elementary_divisors`` function, and prepend `N` 1's to - account for the rows deleted in the previous step. - - EXAMPLES:: - - sage: from sage.homology.chain_complex import dhsw_snf - sage: mat = matrix(ZZ, 3, 4, range(12)) - sage: dhsw_snf(mat) - [1, 4, 0] - sage: mat = random_matrix(ZZ, 20, 20, x=-1, y=2) - sage: mat.elementary_divisors() == dhsw_snf(mat) - True - - REFERENCES: - - .. [DHSW] Dumas, Heckenbach, Saunders, Welker, "Computing simplicial - homology based on efficient Smith normal form algorithms," in - "Algebra, geometry, and software systems" (2003), 177-206. - """ - ring = mat.base_ring() - rows = mat.nrows() - cols = mat.ncols() - new_data = {} - new_mat = matrix(ring, rows, cols, new_data) - add_to_rank = 0 - zero_cols = 0 - if verbose: - print "old matrix: %s by %s" % (rows, cols) - # leading_positions: dictionary of lists indexed by row: if first - # nonzero entry in column c is in row r, then leading_positions[r] - # should contain c - leading_positions = {} - # pass 1: - if verbose: - print "starting pass 1" - for j in range(cols): - # new_col is a matrix with one column: sparse matrices seem to - # be less buggy than sparse vectors (#5184, #5185), and - # perhaps also faster. - new_col = mat.matrix_from_columns([j]) - if new_col.is_zero(): - zero_cols += 1 - else: - check_leading = True - while check_leading: - i = new_col.nonzero_positions_in_column(0)[0] - entry = new_col[i,0] - check_leading = False - if i in leading_positions: - for c in leading_positions[i]: - earlier = new_mat[i,c] - # right now we don't check to see if entry divides - # earlier, because we don't want to modify the - # earlier columns of the matrix. Deal with this - # in pass 2. - if entry and earlier.divides(entry): - quo = entry.divide_knowing_divisible_by(earlier) - new_col = new_col - quo * new_mat.matrix_from_columns([c]) - entry = 0 - if not new_col.is_zero(): - check_leading = True - if not new_col.is_zero(): - new_mat.set_column(j-zero_cols, new_col.column(0)) - i = new_col.nonzero_positions_in_column(0)[0] - if i in leading_positions: - leading_positions[i].append(j-zero_cols) - else: - leading_positions[i] = [j-zero_cols] - else: - zero_cols += 1 - # pass 2: - # first eliminate the zero columns at the end - cols = cols - zero_cols - zero_cols = 0 - new_mat = new_mat.matrix_from_columns(range(cols)) - if verbose: - print "starting pass 2" - keep_columns = range(cols) - check_leading = True - while check_leading: - check_leading = False - new_leading = leading_positions.copy() - for i in leading_positions: - if len(leading_positions[i]) > 1: - j = leading_positions[i][0] - jth = new_mat[i, j] - for n in leading_positions[i][1:]: - nth = new_mat[i,n] - if jth.divides(nth): - quo = nth.divide_knowing_divisible_by(jth) - new_mat.add_multiple_of_column(n, j, -quo) - elif nth.divides(jth): - quo = jth.divide_knowing_divisible_by(nth) - jth = nth - new_mat.swap_columns(n, j) - new_mat.add_multiple_of_column(n, j, -quo) - else: - (g,r,s) = jth.xgcd(nth) - (unit,A,B) = r.xgcd(-s) # unit ought to be 1 here - jth_col = new_mat.column(j) - nth_col = new_mat.column(n) - new_mat.set_column(j, r*jth_col + s*nth_col) - new_mat.set_column(n, B*jth_col + A*nth_col) - nth = B*jth + A*nth - jth = g - # at this point, jth should divide nth - quo = nth.divide_knowing_divisible_by(jth) - new_mat.add_multiple_of_column(n, j, -quo) - new_leading[i].remove(n) - if new_mat.column(n).is_zero(): - keep_columns.remove(n) - zero_cols += 1 - else: - new_r = new_mat.column(n).nonzero_positions()[0] - if new_r in new_leading: - new_leading[new_r].append(n) - else: - new_leading[new_r] = [n] - check_leading = True - leading_positions = new_leading - # pass 3: get rid of columns which start with 1 or -1 - if verbose: - print "starting pass 3" - max_leading = 1 - for i in leading_positions: - j = leading_positions[i][0] - entry = new_mat[i,j] - if entry.abs() == 1: - add_to_rank += 1 - keep_columns.remove(j) - for c in new_mat.nonzero_positions_in_row(i): - if c in keep_columns: - new_mat.add_multiple_of_column(c, j, -entry * new_mat[i,c]) - else: - max_leading = max(max_leading, new_mat[i,j].abs()) - # form the new matrix - if max_leading != 1: - new_mat = new_mat.matrix_from_columns(keep_columns) - if verbose: - print "new matrix: %s by %s" % (new_mat.nrows(), new_mat.ncols()) - if new_mat.is_sparse(): - ed = [1]*add_to_rank + new_mat.dense_matrix().elementary_divisors() - else: - ed = [1]*add_to_rank + new_mat.elementary_divisors() - else: - if verbose: - print "new matrix: all pivots are 1 or -1" - ed = [1]*add_to_rank - - if len(ed) < rows: - return ed + [0]*(rows - len(ed)) - else: - return ed[:rows] def _latex_module(R, m): """ @@ -258,7 +88,9 @@ def _latex_module(R, m): else: return str(latex(FreeModule(R, m))) -class ChainComplex(SageObject): + +@rename_keyword(deprecation=15151, check_products='check', check_diffs='check') +def ChainComplex(data=None, **kwds): r""" Define a chain complex. @@ -271,14 +103,15 @@ class ChainComplex(SageObject): which the chain complex is defined. If this is not specified, it is determined by the data defining the chain complex. - - ``grading_group`` -- a free abelian group (optional, default - ``ZZ``), the group over which the chain complex is indexed. + - ``grading_group`` -- a additive free abelian group (optional, + default ``ZZ``), the group over which the chain complex is + indexed. - ``degree`` -- element of grading_group (optional, default 1), the degree of the differential. - - ``check_products`` -- boolean (optional, default ``True``). If - ``True``, check that each consecutive pair of differentials are + - ``check`` -- boolean (optional, default ``True``). If ``True``, + check that each consecutive pair of differentials are composable and have composite equal to zero. OUTPUT: a chain complex @@ -298,18 +131,18 @@ class ChainComplex(SageObject): `n`. (Note that the shape of the matrix then determines the rank of the free modules `C_n` and `C_{n+d}`.) - 2. a list or tuple of the form `[C_0, d_0, C_1, d_1, C_2, d_2, - ...]`, where each `C_i` is a free module and each `d_i` is a - matrix, as above. This only makes sense if ``grading_group`` + 2. a list/tuple/iterable of the form `[C_0, d_0, C_1, d_1, C_2, + d_2, ...]`, where each `C_i` is a free module and each `d_i` is + a matrix, as above. This only makes sense if ``grading_group`` is `\ZZ` and ``degree`` is 1. - 3. a list or tuple of the form `[r_0, d_0, r_1, d_1, r_2, d_2, - ...]`, where `r_i` is the rank of the free module `C_i` and - each `d_i` is a matrix, as above. This only makes sense if + 3. a list/tuple/iterable of the form `[r_0, d_0, r_1, d_1, r_2, + d_2, ...]`, where `r_i` is the rank of the free module `C_i` + and each `d_i` is a matrix, as above. This only makes sense if ``grading_group`` is `\ZZ` and ``degree`` is 1. - 4. a list or tuple of the form `[d_0, d_1, d_2, ...]` where each - `d_i` is a matrix, as above. This only makes sense if + 4. a list/tuple/iterable of the form `[d_0, d_1, d_2, ...]` where + each `d_i` is a matrix, as above. This only makes sense if ``grading_group`` is `\ZZ` and ``degree`` is 1. .. NOTE:: @@ -322,9 +155,9 @@ class ChainComplex(SageObject): of different things in it, and all of the non-matrices will be ignored.) No error checking is done to make sure, for instance, that the given modules have the appropriate ranks for - the given matrices. However, as long as ``check_products`` is - True, the code checks to see if the matrices are composable and - that each appropriate composite is zero. + the given matrices. However, as long as ``check`` is True, the + code checks to see if the matrices are composable and that each + appropriate composite is zero. If the base ring is not specified, then the matrices are examined to determine a ring over which they are all naturally defined, and @@ -337,14 +170,19 @@ class ChainComplex(SageObject): EXAMPLES:: sage: ChainComplex() - Chain complex with at most 0 nonzero terms over Integer Ring + Trivial chain complex over Integer Ring + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) sage: C Chain complex with at most 2 nonzero terms over Integer Ring - sage: D = ChainComplex([matrix(ZZ, 2, 2, [0, 1, 0, 0]), matrix(ZZ, 2, 2, [0, 1, 0, 0])], base_ring=GF(2)); D + + sage: m = matrix(ZZ, 2, 2, [0, 1, 0, 0]) + sage: D = ChainComplex([m, m], base_ring=GF(2)); D Chain complex with at most 3 nonzero terms over Finite Field of size 2 sage: D == loads(dumps(D)) True + sage: D.differential(0)==m, m.is_immutable(), D.differential(0).is_immutable() + (True, False, True) Note that when a chain complex is defined in Sage, new differentials may be created: every nonzero module in the chain @@ -353,7 +191,7 @@ class ChainComplex(SageObject): sage: IZ = ChainComplex({0: identity_matrix(ZZ, 1)}) sage: IZ.differential() # the differentials in the chain complex - {0: [1], 1: []} + {0: [1], 1: [], -1: []} sage: IZ.differential(1).parent() Full MatrixSpace of 0 by 1 dense matrices over Integer Ring sage: mat = ChainComplex({0: matrix(ZZ, 3, 4)}).differential(1) @@ -380,112 +218,243 @@ class ChainComplex(SageObject): sage: ChainComplex([matrix(GF(125, 'a'), 3, 1)], base_ring=QQ) Traceback (most recent call last): ... - TypeError: Unable to coerce 0 () to Rational + TypeError: Unable to coerce 0 () to Rational """ + check = kwds.get('check', True) + base_ring = kwds.get('base_ring', None) + grading_group = kwds.get('grading_group', ZZ) + degree = kwds.get('degree', 1) + try: + degree = grading_group(degree) + except StandardError: + raise ValueError('degree is not an element of the grading group') + + if data is None or (isinstance(data, (list, tuple)) and len(data) == 0): + # the zero chain complex + try: + zero = grading_group.identity() + except AttributeError: + zero = grading_group.zero_element() + if base_ring is None: + base_ring = ZZ + data_dict = {zero: matrix(base_ring, 0, 0, [])} + elif isinstance(data, dict): # data is dictionary + data_dict = data + else: # data is list/tuple/iterable + data_matrices = filter(lambda x: isinstance(x, Matrix), data) + if degree != 1: + raise ValueError('degree must be +1 if the data argument is a list or tuple') + if grading_group != ZZ: + raise ValueError('grading_group must be ZZ if the data argument is a list or tuple') + data_dict = dict((grading_group(i), m) for i,m in enumerate(data_matrices)) + + if base_ring is None: + _, base_ring = prepare_dict(dict([n, data_dict[n].base_ring()(0)] for n in data_dict)) + + # make sure values in data_dict are appropriate matrices + for n in data_dict.keys(): + if not n in grading_group: + raise ValueError('one of the dictionary keys is not an element of the grading group') + mat = data_dict[n] + if not isinstance(mat, Matrix): + raise TypeError('One of the differentials in the data is not a matrix') + if mat.base_ring() is base_ring: + if not mat.is_immutable(): + mat = copy(mat) # do not make any arguments passed immutable + mat.set_immutable() + else: + mat = mat.change_ring(base_ring) + mat.set_immutable() + data_dict[n] = mat + + # include any "obvious" zero matrices that are not 0x0 + for n in data_dict.keys(): # note: data_dict will be mutated in this loop + mat1 = data_dict[n] + if (n+degree not in data_dict) and (mat1.nrows() != 0): + if n+2*degree in data_dict: + mat2 = matrix(base_ring, data_dict[n+2*degree].ncols(), mat1.nrows()) + else: + mat2 = matrix(base_ring, 0, mat1.nrows()) + mat2.set_immutable() + data_dict[n+degree] = mat2 + if (n-degree not in data_dict) and (mat1.ncols() != 0): + if n-2*degree in data_dict: + mat0 = matrix(base_ring, mat1.ncols(), data_dict[n-2*degree].nrows()) + else: + mat0 = matrix(base_ring, mat1.ncols(), 0) + mat0.set_immutable() + data_dict[n-degree] = mat0 + + # check that this is a complex: going twice is zero + if check: + for n in data_dict.keys(): + mat0 = data_dict[n] + try: + mat1 = data_dict[n+degree] + except KeyError: + continue + try: + prod = mat1 * mat0 + except TypeError: + raise TypeError('the differentials d_{%s} and d_{%s} are not compatible: ' + 'their product is not defined' % (n, n+degree)) + if not prod.is_zero(): + raise ValueError('the differentials d_{%s} and d_{%s} are not compatible: ' + 'their composition is not zero.' % (n, n+degree)) + + return ChainComplex_class(grading_group, degree, base_ring, data_dict) + + +class Chain_class(ModuleElement): + + def __init__(self, parent, *args, **kwds): + """ + Parent for all chain complexes over a given ``base_ring`` + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}, base_ring=GF(7)) + sage: C.category() + Category of chain complexes over Finite Field of size 7 + """ + super(Chain_class, self).__init__(parent) + + def is_cycle(self): + pass # TODO + + def is_boundary(self): + pass # TODO + - def __init__(self, data=None, **kwds): + +class ChainComplex_class(Parent): + + def __init__(self, grading_group, degree_of_differential, base_ring, differentials): """ - See :mod:`ChainComplex` for full documentation. + See :func:`ChainComplex` for full documentation. EXAMPLES:: sage: C = ChainComplex(); C - Chain complex with at most 0 nonzero terms over Integer Ring + Trivial chain complex over Integer Ring + sage: D = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) sage: D Chain complex with at most 2 nonzero terms over Integer Ring + + sage: ChainComplex().base_ring() + Integer Ring """ - check_products = kwds.get('check_products', True) or kwds.get('check_diffs') - base_ring = kwds.get('base_ring', None) - grading_group = kwds.get('grading_group', ZZ) - degree = kwds.get('degree', 1) + assert all(d.base_ring() == base_ring and d.is_immutable() + for d in differentials.values()) + assert degree_of_differential.parent() is grading_group + assert grading_group is ZZ or not grading_group.is_multiplicative() + # all differentials that are not 0x0 must be specified to the constructor + assert all(dim+degree_of_differential in differentials or d.nrows() == 0 + for dim, d in differentials.iteritems()) + assert all(dim-degree_of_differential in differentials or d.ncols() == 0 + for dim, d in differentials.iteritems()) + self._grading_group = grading_group + self._degree_of_differential = degree_of_differential + self._diff = differentials + + from sage.categories.all import ChainComplexes + category = ChainComplexes(base_ring) + super(ChainComplex_class, self).__init__(base=base_ring, category=category) + + Element = Chain_class + + def _element_constructor_(self, *args): + """ + The element constructor. + This is part of the Parent/Element framework. Calling the + parent uses this method to construct elements. + + EXAMPLES:: + + sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])}) + sage: D._element_constructor_(0) + """ + return self.element_class(self, *args) + + @cached_method + def rank(self, degree, ring=None): + r""" + Return the rank of a differential + + INPUT: + + - ``degree`` -- an element `\delta` of the grading + group. Which differential `d_{\delta}` we want to know the + rank of. + + - ``ring`` -- a commutative ring `S` or ``None`` (default). If + specified, the rank is computed after changing to this ring. + + OUTPUT: + + The rank of the differential ``d_{\delta} \otimes_R S`, where + `R` is the base ring of the chain complex. + + EXAMPLES:: + + sage: C = ChainComplex({0:matrix(ZZ, [[2]])}) + sage: C.differential(0) + [2] + sage: C.rank(0) + 1 + sage: C.rank(0, ring=GF(2)) + 0 + """ + degree = self.grading_group()(degree) try: - deg = grading_group(degree) - except StandardError: - raise ValueError, "The 'degree' does not appear to be an element of the grading group." - # check form of data - new_data = {} - if data is None or (isinstance(data, (list, tuple)) and len(data) == 0): - # the zero chain complex - try: - zero = grading_group.zero_vector() - except AttributeError: - zero = ZZ.zero_element() - if base_ring is None: - base_ring = ZZ - new_data = {zero: matrix(base_ring, 0, 0, [])} + d = self._diff[degree] + except IndexError: + return ZZ.zero() + if d.nrows() == 0 or d.ncols() == 0: + return ZZ.zero() + if ring is None: + return d.rank() else: - if isinstance(data, dict): # case 1, dictionary - temp_dict = data - elif isinstance(data, (list, tuple)): # cases 2, 3, 4: list or tuple - if degree != 1: - raise ValueError, "The degree must be +1 if the data argument is a list or tuple." - if grading_group != ZZ: - raise ValueError, "The grading_group must be ZZ if the data argument is a list or tuple." - new_list = filter(lambda x: isinstance(x, Matrix), data) - temp_dict = dict(zip(range(len(new_list)), new_list)) - else: - raise TypeError, "The data for a chain complex must be a dictionary, list, or tuple." - - if base_ring is None: - junk, ring = prepare_dict(dict([n, temp_dict[n].base_ring()(0)] for n in temp_dict)) - base_ring = ring - # 'dict' is the dictionary of matrices. check to see that - # each entry is in fact a matrix, and that the codomain of - # one matches up with the domain of the next. if the - # number of rows in the differential in degree n is - # nonzero, then make sure that the differential in degree - # n+degree is present (although it of course may be zero). - # If it's not present, add a zero matrix of the - # appropriate shape. This way, if self._data does not - # have a key for n, then the complex is zero in degree n. - for n in temp_dict.keys(): - if not n in grading_group: - raise ValueError, "One of the dictionary keys does not appear to be an element of the grading group." - mat = temp_dict[n] - if not isinstance(mat, Matrix): - raise TypeError, "One of the differentials in the data dictionary indexed by does not appear to be a matrix." - if mat.base_ring() == base_ring: - new_data[n] = mat - else: - new_data[n] = mat.change_ring(base_ring) - for n in temp_dict.keys(): - mat = temp_dict[n] - if n+degree in temp_dict: - mat2 = temp_dict[n+degree] - if check_products: - try: - prod = mat2 * mat - except TypeError: - raise TypeError, "The differentials d_{%s} and d_{%s} are not compatible: their product is not defined." % (n, n+degree) - if not prod.is_zero(): - raise ValueError, "The differentials d_{%s} and d_{%s} are not compatible: their composition is not zero." % (n, n+degree) - else: - if not mat.nrows() == 0: - if n+2*degree in temp_dict: - new_data[n+degree] = matrix(base_ring, temp_dict[n+2*degree].ncols(), mat.nrows()) - else: - new_data[n+degree] = matrix(base_ring, 0, mat.nrows()) - # here ends the initialization/error-checking of the data - self._grading_group = grading_group - self._degree = deg - self._base_ring = base_ring - self._diff = new_data - # self._ranks: dictionary for caching the ranks of the - # differentials: keys are pairs (dim, base_ring) - self._ranks = {} - - def base_ring(self): + return d.change_ring(ring).rank() + + def grading_group(self): + r""" + Return the grading group + + OUTPUT: + + The discrete abelian group that indexes the individual modules + of the complex. Usually `\ZZ`. + + EXAMPLES:: + + sage: G = AdditiveAbelianGroup([0, 3]) + sage: C = ChainComplex(grading_group=G, degree=G([1,2])) + sage: C.grading_group() + Additive abelian group isomorphic to Z/3 + Z + sage: C.degree_of_differential() + (2, 1) + """ + return self._grading_group + + def degree_of_differential(self): """ - The base ring for this simplicial complex. + Return the degree of the differentials of the complex + + OUTPUT: + + An element of the grading group. EXAMPLES:: - sage: ChainComplex().base_ring() - Integer Ring + sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])}) + sage: D.degree_of_differential() + 1 """ - return self._base_ring + return self._degree_of_differential def differential(self, dim=None): """ @@ -493,10 +462,10 @@ def differential(self, dim=None): INPUT: - - ``dim`` - element of the grading group (optional, default + - ``dim`` -- element of the grading group (optional, default ``None``). If this is ``None``, return a dictionary of all - of the differentials. If this is a single element, return the - differential starting in that dimension. + of the differentials. If this is a single element, return + the differential starting in that dimension. OUTPUT: either a dictionary of all of the differentials or a single differential (i.e., a matrix) @@ -506,22 +475,23 @@ def differential(self, dim=None): sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])}) sage: D.differential() {0: [1 0] - [0 2], 1: []} + [0 2], 1: [], -1: []} sage: D.differential(0) [1 0] [0 2] sage: C = ChainComplex({0: identity_matrix(ZZ, 40)}) sage: C.differential() - {0: 40 x 40 dense matrix over Integer Ring, 1: []} + {0: 40 x 40 dense matrix over Integer Ring, 1: [], -1: 40 x 0 dense matrix over Integer Ring} """ if dim is None: - return self._diff - deg = self._degree - if dim in self._diff: + return copy(self._diff) + dim = self.grading_group()(dim) + try: return self._diff[dim] - if dim+deg in self._diff: - return matrix(self._base_ring, self._diff[dim+deg].ncols(), 0) - return matrix(self._base_ring, 0, 0) + except KeyError: + pass + # all differentials that are not 0x0 are in self._diff + return matrix(self.base_ring(), 0, 0) def dual(self): """ @@ -537,12 +507,12 @@ def dual(self): EXAMPLES:: sage: C = ChainComplex({2: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) - sage: C._degree + sage: C.degree_of_differential() 1 sage: C.differential(2) [3 0 0] [0 0 0] - sage: C.dual()._degree + sage: C.dual().degree_of_differential() -1 sage: C.dual().differential(3) [3 0] @@ -550,10 +520,9 @@ def dual(self): [0 0] """ data = {} - deg = self._degree + deg = self.degree_of_differential() for d in self.differential(): data[(d+deg)] = self.differential()[d].transpose() - return ChainComplex(data, degree=-deg) def free_module(self): @@ -573,7 +542,7 @@ def free_module(self): Ambient free module of rank 5 over the principal ideal domain Integer Ring """ rank = sum([mat.ncols() for mat in self.differential().values()]) - return FreeModule(self._base_ring, rank) + return FreeModule(self.base_ring(), rank) def __cmp__(self, other): """ @@ -588,9 +557,12 @@ def __cmp__(self, other): sage: C == D True """ - if self._base_ring != other._base_ring: - return -1 - R = self._base_ring + if not isinstance(other, ChainComplex_class): + return cmp(type(other), ChainComplex_class) + c = cmp(self.base_ring(), other.base_ring()) + if c != 0: + return c + R = self.base_ring() equal = True for d,mat in self.differential().iteritems(): if d not in other.differential(): @@ -665,7 +637,7 @@ def homology(self, dim=None, **kwds): method, which calls the Pari package. For any large matrix, reduce it using the Dumas, Heckenbach, Saunders, and Welker elimination algorithm [DHSW]_: see - :func:`~sage.homology.chain_complex.dhsw_snf` for details. + :func:`~sage.homology.matrix_utils.dhsw_snf` for details. Finally, ``algorithm`` may also be ``'pari'`` or ``'dhsw'``, which forces the named algorithm to be used regardless of the size @@ -721,19 +693,26 @@ def homology(self, dim=None, **kwds): 2: [(Vector space of dimension 1 over Rational Field, (1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1))]} """ + from sage.homology.homology_group import HomologyGroup from sage.interfaces.chomp import have_chomp, homchain + if dim is not None and dim not in self.grading_group(): + raise ValueError('dimension is not an element of the grading group') + algorithm = kwds.get('algorithm', 'auto') + if algorithm not in ['dhsw', 'pari', 'auto', 'no_chomp']: + raise NotImplementedError('algorithm not recognized') + verbose = kwds.get('verbose', False) base_ring = kwds.get('base_ring', None) generators = kwds.get('generators', False) - if base_ring is None or base_ring == self._base_ring: + if base_ring is None or base_ring == self.base_ring(): change_ring = False - base_ring = self._base_ring + base_ring = self.base_ring() else: change_ring = True - if (not base_ring.is_field()) and (base_ring != ZZ): - raise NotImplementedError, "Can't compute homology if the base ring is not the integers or a field." + if not (base_ring.is_field() or base_ring is ZZ): + raise NotImplementedError('can only compute homology if the base ring is the integers or a field') # try to use CHomP if working over Z or F_p, p a prime. if (algorithm == 'auto' and (base_ring == ZZ or @@ -745,25 +724,18 @@ def homology(self, dim=None, **kwds): # now pick off the requested dimensions if H: if dim is not None: - field = base_ring.is_prime_field() answer = {} if isinstance(dim, (list, tuple)): for d in dim: if d in H: answer[d] = H[d] else: - if field: - answer[d] = VectorSpace(base_ring, 0) - else: - answer[d] = HomologyGroup(0) + answer[d] = HomologyGroup(0, base_ring) else: if dim in H: answer = H[dim] else: - if field: - answer = VectorSpace(base_ring, 0) - else: - answer = HomologyGroup(0) + answer = HomologyGroup(0, base_ring) else: answer = H return answer @@ -771,15 +743,16 @@ def homology(self, dim=None, **kwds): if verbose: print "ran CHomP, but no output." - if algorithm not in ['dhsw', 'pari', 'auto', 'no_chomp']: - raise NotImplementedError, "algorithm not recognized" # if dim is None, return all of the homology groups + degree = self.degree_of_differential() if dim is None: answer = {} for n in self._diff.keys(): + if n-degree not in self._diff: + continue if verbose: print "Computing homology of the chain complex in dimension %s..." % n - if base_ring == self._base_ring: + if base_ring == self.base_ring(): answer[n] = self.homology(n, verbose=verbose, generators=generators, algorithm=algorithm) @@ -789,34 +762,18 @@ def homology(self, dim=None, **kwds): generators=generators, algorithm=algorithm) return answer - # now compute the homology in the given dimension - degree = self._degree - # check that dim is in grading_group - if not dim in self._grading_group: - raise ValueError, "The dimension does not appear to be an element of the grading group." + # now compute the homology in the given dimension if dim in self._diff: # d_out is the differential going out of degree dim, # d_in is the differential entering degree dim d_out_cols = self._diff[dim].ncols() d_out_rows = self._diff[dim].nrows() - # avoid bugs in rank of sparse mod n matrices (#5099): - if min(d_out_cols, d_out_rows) == 0: - d_out_rank = 0 if base_ring == ZZ: temp_ring = QQ else: temp_ring = base_ring - if (dim, temp_ring) in self._ranks: - d_out_rank = self._ranks[(dim, temp_ring)] - else: - if min(d_out_cols, d_out_rows) == 0: - d_out_rank = 0 - elif change_ring or base_ring == ZZ: - d_out_rank = self._diff[dim].change_ring(temp_ring).rank() - else: - d_out_rank = self._diff[dim].rank() - self._ranks[(dim, temp_ring)] = d_out_rank + d_out_rank = self.rank(dim, ring=temp_ring) if dim - degree in self._diff: if change_ring: @@ -845,30 +802,13 @@ def homology(self, dim=None, **kwds): non_triv = non_triv + 1 divisors = filter(lambda x: x != 1, all_divs) gens = (K * P.inverse().submatrix(col=non_triv)).transpose() - if base_ring.is_field(): - answer = [(VectorSpace(base_ring, 1), gens[i] ) \ - for i in range(len(divisors))] - elif base_ring == ZZ: - answer = [(HomologyGroup(1, [divisors[i]]), gens[i] ) \ - for i in range(len(divisors))] + answer = [(HomologyGroup(1, base_ring, [divisors[i]]), gens[i]) + for i in range(len(divisors))] else: if base_ring.is_field(): - # avoid bugs in rank of sparse mod n matrices (#5099): - if d_out_cols == 0: - null = 0 - elif d_out_rows == 0: - null = d_out_cols - else: - null = d_out_cols - d_out_rank - if d_in.nrows() == 0 or d_in.ncols() == 0: - rk = 0 - else: - if (dim-degree, temp_ring) in self._ranks: - rk = self._ranks[(dim-degree, temp_ring)] - else: - rk = d_in.rank() - self._ranks[(dim-degree, temp_ring)] = rk - answer = VectorSpace(base_ring, null - rk) + null = d_out_cols - d_out_rank + rk = self.rank(dim-degree, ring=temp_ring) + answer = HomologyGroup(null - rk, base_ring) elif base_ring == ZZ: nullity = d_out_cols - d_out_rank if d_in.ncols() == 0: @@ -882,6 +822,7 @@ def homology(self, dim=None, **kwds): else: algorithm = 'pari' if algorithm == 'dhsw': + from sage.homology.matrix_utils import dhsw_snf all_divs = dhsw_snf(d_in, verbose=verbose) else: algorithm = 'pari' @@ -893,27 +834,15 @@ def homology(self, dim=None, **kwds): # divisors equal to 1 produce trivial # summands, so filter them out divisors = filter(lambda x: x != 1, all_divs) - answer = HomologyGroup(len(divisors), divisors) + answer = HomologyGroup(len(divisors), base_ring, divisors) else: # no incoming differential: it's zero - if base_ring.is_field(): - answer = VectorSpace(base_ring, d_out_cols - d_out_rank) - elif base_ring == ZZ: - answer = HomologyGroup(d_out_cols - d_out_rank) - + answer = HomologyGroup(d_out_cols - d_out_rank, base_ring) if generators: #Include the generators of the nullspace # Find the kernel of the out-going differential. K = self._diff[dim].right_kernel().matrix().change_ring(base_ring) answer = [( answer, vector(base_ring, K.list()) )] else: # chain complex is zero here, so return the zero module - if base_ring.is_field(): - answer = VectorSpace(base_ring, 0) - elif base_ring == ZZ: - answer = HomologyGroup(0) - else: - # This code is not in use: base ring isn't a field - # or ZZ - answer = FreeModule(self._base_ring, rank=0) - + answer = HomologyGroup(0, base_ring) if generators: answer = [(answer, vector(base_ring, []))] if verbose: @@ -958,7 +887,7 @@ def betti(self, dim=None, **kwds): base_ring = kwds.get('base_ring', None) if base_ring is None: - base_ring = self._base_ring + base_ring = self.base_ring() if base_ring == ZZ: base_ring = QQ if base_ring.is_field(): @@ -1014,50 +943,35 @@ def torsion_list(self, max_prime, min_prime=2): sage: C.torsion_list(5) [(2, [1]), (3, [3])] """ - if self._base_ring != ZZ: - raise ValueError, "This only works if the base ring of the chain complex is the integers" - else: - answer = [] - torsion_free = self.betti(base_ring=GF(max_prime)) - for p in prime_range(min_prime, max_prime): - mod_p_betti = self.betti(base_ring=GF(p)) - if mod_p_betti != torsion_free: - diff_dict = {} - temp_diff = {} - D = self._degree - for i in torsion_free: - temp_diff[i] = mod_p_betti.get(i, 0) - torsion_free[i] - for i in temp_diff: - if temp_diff[i] > 0: - if i+D in diff_dict: - lower = diff_dict[i+D] - else: - lower = 0 - current = temp_diff[i] - if current > lower: - diff_dict[i] = current - lower - if i-D in diff_dict: - diff_dict[i-D] -= current - lower - differences = [] - for i in diff_dict: - if diff_dict[i] != 0: - differences.append(i) - answer.append((p,differences)) - return answer - - def category(self): - """ - Return the category to which this chain complex belongs: the - category of all chain complexes over the base ring. - - EXAMPLES:: - - sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}, base_ring=GF(7)) - sage: C.category() - Category of chain complexes over Finite Field of size 7 - """ - import sage.categories.all - return sage.categories.all.ChainComplexes(self.base_ring()) + if self.base_ring() != ZZ: + raise NotImplementedError('only implemented for base ring the integers') + answer = [] + torsion_free = self.betti(base_ring=GF(max_prime)) + for p in prime_range(min_prime, max_prime): + mod_p_betti = self.betti(base_ring=GF(p)) + if mod_p_betti != torsion_free: + diff_dict = {} + temp_diff = {} + D = self.degree_of_differential() + for i in torsion_free: + temp_diff[i] = mod_p_betti.get(i, 0) - torsion_free[i] + for i in temp_diff: + if temp_diff[i] > 0: + if i+D in diff_dict: + lower = diff_dict[i+D] + else: + lower = 0 + current = temp_diff[i] + if current > lower: + diff_dict[i] = current - lower + if i-D in diff_dict: + diff_dict[i-D] -= current - lower + differences = [] + for i in diff_dict: + if diff_dict[i] != 0: + differences.append(i) + answer.append((p,differences)) + return answer def _Hom_(self, other, category=None): """ @@ -1085,19 +999,19 @@ def _flip_(self): EXAMPLES:: sage: C = ChainComplex({2: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) - sage: C._degree + sage: C.degree_of_differential() 1 sage: C.differential(2) [3 0 0] [0 0 0] - sage: C._flip_()._degree + sage: C._flip_().degree_of_differential() -1 sage: C._flip_().differential(-2) [3 0 0] [0 0 0] """ data = {} - deg = self._degree + deg = self.degree_of_differential() for d in self.differential(): data[-d] = self.differential()[d] return ChainComplex(data, degree=-deg) @@ -1122,13 +1036,15 @@ def _chomp_repr_(self): sage: C._chomp_repr_() 'chain complex\n\nmax dimension = 1\n\ndimension 0\n boundary a1 = 0\n\ndimension 1\n boundary a1 = + 3 * a1 \n boundary a2 = 0\n boundary a3 = 0\n\n' """ - if (self._grading_group != ZZ or - (self._degree != 1 and self._degree != -1)): - raise ValueError, "CHomP only works on Z-graded chain complexes with differential of degree 1 or -1." - base_ring = self._base_ring + deg = self.degree_of_differential() + if (self.grading_group() != ZZ or + (deg != 1 and deg != -1)): + raise ValueError('CHomP only works on Z-graded chain complexes with ' + 'differential of degree 1 or -1') + base_ring = self.base_ring() if (base_ring == QQ) or (base_ring != ZZ and not (base_ring.is_prime_field())): - raise ValueError, "CHomP doesn't compute over the rationals, only over Z or F_p." - if self._degree == -1: + raise ValueError('CHomP doesn\'t compute over the rationals, only over Z or F_p') + if deg == -1: diffs = self.differential() else: diffs = self._flip_().differential() @@ -1137,9 +1053,9 @@ def _chomp_repr_(self): mindim = min(diffs) # will shift chain complex by subtracting mindim from # dimensions, so its bottom dimension is zero. - s = "chain complex\n\nmax dimension = %s\n\n" % (maxdim - mindim,) + s = "chain complex\n\nmax dimension = %s\n\n" % (maxdim - mindim - 1,) - for i in range(0, maxdim - mindim + 1): + for i in range(0, maxdim - mindim): s += "dimension %s\n" % i mat = diffs.get(i + mindim, matrix(base_ring, 0, 0)) for idx in range(mat.ncols()): @@ -1159,7 +1075,6 @@ def _chomp_repr_(self): s += "0" s += "\n" s += "\n" - return s def _repr_(self): @@ -1174,10 +1089,24 @@ def _repr_(self): """ diffs = filter(lambda mat: mat.nrows() + mat.ncols() > 0, self._diff.values()) - string1 = "Chain complex with at most" - string2 = " %s nonzero terms over %s" % (len(diffs), - self._base_ring) - return string1 + string2 + if len(diffs) == 0: + s = 'Trivial chain complex' + else: + s = 'Chain complex with at most {0} nonzero terms'.format(len(diffs)-1) + s += ' over {0}'.format(self.base_ring()) + return s + + def _ascii_art_(self): + """ + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + """ + if self.grading_group() is not ZZ: + return super(ChainComplex_class, self)._ascii_art_() + from sage.misc.ascii_art import AsciiArt + dmin = min(self._diff.keys()) + dmax = max(self._diff.keys()) def _latex_(self): """ @@ -1199,9 +1128,9 @@ def _latex_(self): # put into trying to fix this. string = "" dict = self._diff - deg = self._degree - ring = self._base_ring - if self._grading_group != ZZ: + deg = self.degree_of_differential() + ring = self.base_ring() + if self.grading_group() != ZZ: guess = dict.keys()[0] if guess-deg in dict: string += "\\dots \\xrightarrow{d_{%s}} " % latex(guess-deg) @@ -1211,7 +1140,7 @@ def _latex_(self): backwards = (deg < 0) sorted_list = sorted(dict.keys(), reverse=backwards) if len(dict) <= 6: - for n in sorted_list[:-1]: + for n in sorted_list[1:-1]: mat = dict[n] string += _latex_module(ring, mat.ncols()) string += " \\xrightarrow{d_{%s}} " % latex(n) @@ -1229,145 +1158,7 @@ def _latex_(self): string += _latex_module(ring, mat.ncols()) return string -class HomologyGroup_class(AdditiveAbelianGroup_fixed_gens): - """ - Abelian group on `n` generators. This class inherits from - :class:`AdditiveAbelianGroup`; see that for more documentation. The main - difference between the classes is in the print representation. - - EXAMPLES:: - - sage: from sage.homology.chain_complex import HomologyGroup - sage: G = AbelianGroup(5,[5,5,7,8,9]); G - Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9 - sage: H = HomologyGroup(5,[5,5,7,8,9]); H - C5 x C5 x C7 x C8 x C9 - sage: G == loads(dumps(G)) - True - sage: AbelianGroup(4) - Multiplicative Abelian group isomorphic to Z x Z x Z x Z - sage: HomologyGroup(4) - Z x Z x Z x Z - sage: HomologyGroup(100) - Z^100 - """ - def __init__(self, n, invfac): - """ - See :func:`HomologyGroup` for full documentation. - - EXAMPLES:: - sage: from sage.homology.chain_complex import HomologyGroup - sage: H = HomologyGroup(5,[5,5,7,8,9]); H - C5 x C5 x C7 x C8 x C9 - """ - n = len(invfac) - A = ZZ**n - B = A.span([A.gen(i) * invfac[i] for i in xrange(n)]) - - AdditiveAbelianGroup_fixed_gens.__init__(self, A, B, A.gens()) - self._original_invts = invfac +from sage.structure.sage_object import register_unpickle_override +register_unpickle_override('sage.homology.chain_complex', 'ChainComplex', ChainComplex_class) - def _repr_(self): - """ - Print representation - - EXAMPLES:: - - sage: from sage.homology.chain_complex import HomologyGroup - sage: H = HomologyGroup(7,[4,4,4,4,4,7,7]) - sage: H._repr_() - 'C4^5 x C7 x C7' - sage: HomologyGroup(6) - Z^6 - """ - eldv = self._original_invts - if len(eldv) == 0: - return "0" - rank = len(filter(lambda x: x == 0, eldv)) - torsion = sorted(filter(lambda x: x, eldv)) - if rank > 4: - g = ["Z^%s" % rank] - else: - g = ["Z"] * rank - if len(torsion) != 0: - printed = [] - for t in torsion: - numfac = torsion.count(t) - too_many = (numfac > 4) - if too_many: - if t not in printed: - g.append("C%s^%s" % (t, numfac)) - printed.append(t) - else: - g.append("C%s" % t) - times = " x " - return times.join(g) - - def _latex_(self): - """ - LaTeX representation - - EXAMPLES:: - - sage: from sage.homology.chain_complex import HomologyGroup - sage: H = HomologyGroup(7,[4,4,4,4,4,7,7]) - sage: H._latex_() - 'C_{4}^{5} \\times C_{7} \\times C_{7}' - sage: latex(HomologyGroup(6)) - \ZZ^{6} - """ - eldv = self._original_invts - if len(eldv) == 0: - return "0" - rank = len(filter(lambda x: x == 0, eldv)) - torsion = sorted(filter(lambda x: x, eldv)) - if rank > 4: - g = ["\\ZZ^{%s}" % rank] - else: - g = ["\\ZZ"] * rank - if len(torsion) != 0: - printed = [] - for t in torsion: - numfac = torsion.count(t) - too_many = (numfac > 4) - if too_many: - if t not in printed: - g.append("C_{%s}^{%s}" % (t, numfac)) - printed.append(t) - else: - g.append("C_{%s}" % t) - times = " \\times " - return times.join(g) - -def HomologyGroup(n, invfac=None): - """ - Abelian group on `n` generators. - - EXAMPLES:: - - sage: from sage.homology.chain_complex import HomologyGroup - sage: G = AbelianGroup(5,[5,5,7,8,9]); G - Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9 - sage: H = HomologyGroup(5,[5,5,7,8,9]); H - C5 x C5 x C7 x C8 x C9 - sage: AbelianGroup(4) - Multiplicative Abelian group isomorphic to Z x Z x Z x Z - sage: HomologyGroup(4) - Z x Z x Z x Z - sage: HomologyGroup(100) - Z^100 - """ - # copied from AbelianGroup: - if invfac is None: - if isinstance(n, (list, tuple)): - invfac = n - n = len(n) - else: - invfac = [] - if len(invfac) < n: - invfac = [0] * (n - len(invfac)) + invfac - elif len(invfac) > n: - raise ValueError, "invfac (=%s) must have length n (=%s)"%(invfac, n) - M = HomologyGroup_class(n, invfac) - return M diff --git a/src/sage/homology/chain_complex_morphism.py b/src/sage/homology/chain_complex_morphism.py index 5eae58788cd..c696ead6aca 100644 --- a/src/sage/homology/chain_complex_morphism.py +++ b/src/sage/homology/chain_complex_morphism.py @@ -22,7 +22,7 @@ sage: C.differential() {0: [], 1: [ 1 1 0] [ 0 -1 -1] - [-1 0 1]} + [-1 0 1], 2: []} sage: f = {0:zero_matrix(ZZ,3,3),1:zero_matrix(ZZ,3,3)} sage: G = Hom(C,C) sage: x = G(f) @@ -34,7 +34,6 @@ [0 0 0], 1: [0 0 0] [0 0 0] [0 0 0]} - """ #***************************************************************************** @@ -83,7 +82,7 @@ class ChainComplexMorphism(SageObject): """ An element of this class is a morphism of chain complexes. """ - def __init__(self,matrices,C,D): + def __init__(self, matrices, C, D): """ Create a morphism from a dictionary of matrices. @@ -97,7 +96,7 @@ def __init__(self,matrices,C,D): sage: C.differential() {0: [], 1: [ 1 1 0] [ 0 -1 -1] - [-1 0 1]} + [-1 0 1], 2: []} sage: f = {0:zero_matrix(ZZ,3,3),1:zero_matrix(ZZ,3,3)} sage: G = Hom(C,C) sage: x = G(f) @@ -118,46 +117,35 @@ def __init__(self,matrices,C,D): sage: g.associated_chain_complex_morphism() Chain complex morphism from Chain complex with at most 2 nonzero terms over Integer Ring to Chain complex with at most 1 nonzero terms over Integer Ring """ + if not C.base_ring()==D.base_ring(): + raise NotImplementedError('morphisms between chain complexes of different' + ' base rings are not implemented') if C._grading_group != ZZ: - raise NotImplementedError, "Chain complex morphisms are not implemented over gradings other than ZZ." - d = C._degree - if d != D._degree: - raise ValueError, "Chain complex morphisms are not defined for chain complexes of different degrees." + raise NotImplementedError('only implemented over gradings other than ZZ') + d = C.degree_of_differential() + if d != D.degree_of_differential(): + raise ValueError('degree of differential does not match') if d != -1 and d != 1: - raise NotImplementedError, "Chain complex morphisms are not implemented for degrees besides -1 and 1." - dim_min = min(min(C.differential().keys()),min(D.differential().keys())) - dim_max = max(max(C.differential().keys()),max(D.differential().keys())) - if not C.base_ring()==D.base_ring(): - raise NotImplementedError, "Chain complex morphisms between chain complexes of different base rings are not implemented." - for i in range(dim_min,dim_max): - try: - matrices[i] - except KeyError: - matrices[i] = matrix.zero_matrix(C.base_ring(),D.differential()[i].ncols(),C.differential()[i].ncols(),sparse=True) - try: - matrices[i+1] - except KeyError: - matrices[i+1] = matrix.zero_matrix(C.base_ring(),D.differential()[i+1].ncols(),C.differential()[i+1].ncols(),sparse=True) - if d==-1: - if (i+1) in C.differential().keys() and (i+1) in D.differential().keys(): - if not matrices[i]*C.differential()[i+1]==D.differential()[i+1]*matrices[i+1]: - raise ValueError, "Matrices must define a chain complex morphism." - elif (i+1) in C.differential().keys(): - if not (matrices[i]*C.differential()[i+1]).is_zero(): - raise ValueError, "Matrices must define a chain complex morphism." - elif (i+1) in D.differential().keys(): - if not (D.differential()[i+1]*matrices[i+1]).is_zero(): - raise ValueError, "Matrices must define a chain complex morphism." + raise NotImplementedError('only implemented for degrees -1 and 1') + + dim_min = min(C.differential().keys() + D.differential().keys()) + dim_max = max(C.differential().keys() + D.differential().keys()) + for i in range(dim_min, dim_max): + if i not in matrices: + matrices[i] = matrix.zero_matrix(C.base_ring(), + D.differential(i).ncols(), + C.differential(i).ncols(), sparse=True) + chain_morphism_error = ValueError('matrices must define a chain complex morphism') + for i in range(dim_min, dim_max): + Dm = D.differential(i) * matrices[i] + if dim_min <= i+d < dim_max: + mC = matrices[i+d] * C.differential(i) + if mC != Dm: + raise chain_morphism_error else: - if i in C.differential().keys() and i in D.differential().keys(): - if not matrices[i+1]*C.differential()[i]==D.differential()[i]*matrices[i]: - raise ValueError, "Matrices must define a chain complex morphism." - elif i in C.differential().keys(): - if not (matrices[i+1]*C.differential()[i]).is_zero(): - raise ValueError, "Matrices must define a chain complex morphism." - elif i in D.differential().keys(): - if not (D.differential()[i]*matrices[i]).is_zero(): - raise ValueError, "Matrices must define a chain complex morphism." + if not Dm.is_zero(): + raise chain_morphism_error + self._matrix_dictionary = matrices self._domain = C self._codomain = D @@ -354,14 +342,14 @@ def __eq__(self,x): sage: i = H.identity() sage: x = i.associated_chain_complex_morphism() sage: x - Chain complex morphism from Chain complex with at most 0 nonzero terms over Integer Ring to Chain complex with at most 0 nonzero terms over Integer Ring + Chain complex morphism from Trivial chain complex over Integer Ring + to Trivial chain complex over Integer Ring sage: f = x._matrix_dictionary sage: C = S.chain_complex() sage: G = Hom(C,C) sage: y = G(f) sage: x == y True - """ if not isinstance(x,ChainComplexMorphism) or self._codomain != x._codomain or self._domain != x._domain or self._matrix_dictionary != x._matrix_dictionary: return False @@ -379,9 +367,10 @@ def _repr_(self): sage: i = H.identity() sage: x = i.associated_chain_complex_morphism() sage: x - Chain complex morphism from Chain complex with at most 0 nonzero terms over Integer Ring to Chain complex with at most 0 nonzero terms over Integer Ring + Chain complex morphism from Trivial chain complex over Integer Ring + to Trivial chain complex over Integer Ring sage: x._repr_() - 'Chain complex morphism from Chain complex with at most 0 nonzero terms over Integer Ring to Chain complex with at most 0 nonzero terms over Integer Ring' - + 'Chain complex morphism from Trivial chain complex over Integer Ring + to Trivial chain complex over Integer Ring' """ return "Chain complex morphism from " + self._domain._repr_() + " to " + self._codomain._repr_() diff --git a/src/sage/homology/cubical_complex.py b/src/sage/homology/cubical_complex.py index e30173933ce..1ecb0aaf510 100644 --- a/src/sage/homology/cubical_complex.py +++ b/src/sage/homology/cubical_complex.py @@ -1080,10 +1080,10 @@ def chain_complex(self, **kwds): # finally, return the chain complex if cochain: return ChainComplex(data=differentials, base_ring=base_ring, - degree=1, check_products=check_diffs) + degree=1, check=check_diffs) else: return ChainComplex(data=differentials, base_ring=base_ring, - degree=-1, check_products=check_diffs) + degree=-1, check=check_diffs) def n_skeleton(self, n): r""" diff --git a/src/sage/homology/delta_complex.py b/src/sage/homology/delta_complex.py index 8502d74bdff..b88d9386fbf 100644 --- a/src/sage/homology/delta_complex.py +++ b/src/sage/homology/delta_complex.py @@ -590,7 +590,7 @@ def chain_complex(self, **kwds): Z sage: T = T = delta_complexes.Torus() sage: T.chain_complex(subcomplex=T) - Chain complex with at most 0 nonzero terms over Integer Ring + Trivial chain complex over Integer Ring sage: T.homology(subcomplex=T) {0: 0, 1: 0, 2: 0} sage: A = T.subcomplex({2: [1]}) # one of the two triangles forming T diff --git a/src/sage/homology/homology_group.py b/src/sage/homology/homology_group.py new file mode 100644 index 00000000000..c97987aea19 --- /dev/null +++ b/src/sage/homology/homology_group.py @@ -0,0 +1,185 @@ +""" +Homology Groups + +This module defines a :meth:`HomologyGroup` class which is an abelian +group that prints itself in a way that is suitable for homology +groups. +""" + +######################################################################## +# Copyright (C) 2013 John H. Palmieri +# Volker Braun +# +# 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/ +######################################################################## + + +from sage.modules.free_module import VectorSpace +from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup_fixed_gens +from sage.rings.integer_ring import ZZ + + +class HomologyGroup_class(AdditiveAbelianGroup_fixed_gens): + """ + Discrete Abelian group on `n` generators. This class inherits from + :class:`AdditiveAbelianGroup`; see that for more + documentation. The main difference between the classes is in the + print representation. + + EXAMPLES:: + + sage: from sage.homology.homology_group import HomologyGroup + sage: G = AbelianGroup(5, [5,5,7,8,9]); G + Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9 + sage: H = HomologyGroup(5, ZZ, [5,5,7,8,9]); H + C5 x C5 x C7 x C8 x C9 + sage: G == loads(dumps(G)) + True + sage: AbelianGroup(4) + Multiplicative Abelian group isomorphic to Z x Z x Z x Z + sage: HomologyGroup(4, ZZ) + Z x Z x Z x Z + sage: HomologyGroup(100, ZZ) + Z^100 + """ + def __init__(self, n, invfac): + """ + See :func:`HomologyGroup` for full documentation. + + EXAMPLES:: + + sage: from sage.homology.homology_group import HomologyGroup + sage: H = HomologyGroup(5, ZZ, [5,5,7,8,9]); H + C5 x C5 x C7 x C8 x C9 + """ + n = len(invfac) + A = ZZ**n + B = A.span([A.gen(i) * invfac[i] for i in xrange(n)]) + + AdditiveAbelianGroup_fixed_gens.__init__(self, A, B, A.gens()) + self._original_invts = invfac + + def _repr_(self): + """ + Print representation + + EXAMPLES:: + + sage: from sage.homology.homology_group import HomologyGroup + sage: H = HomologyGroup(7, ZZ, [4,4,4,4,4,7,7]) + sage: H._repr_() + 'C4^5 x C7 x C7' + sage: HomologyGroup(6, ZZ) + Z^6 + """ + eldv = self._original_invts + if len(eldv) == 0: + return "0" + rank = len(filter(lambda x: x == 0, eldv)) + torsion = sorted(filter(lambda x: x, eldv)) + if rank > 4: + g = ["Z^%s" % rank] + else: + g = ["Z"] * rank + if len(torsion) != 0: + printed = [] + for t in torsion: + numfac = torsion.count(t) + too_many = (numfac > 4) + if too_many: + if t not in printed: + g.append("C%s^%s" % (t, numfac)) + printed.append(t) + else: + g.append("C%s" % t) + times = " x " + return times.join(g) + + def _latex_(self): + """ + LaTeX representation + + EXAMPLES:: + + sage: from sage.homology.homology_group import HomologyGroup + sage: H = HomologyGroup(7, ZZ, [4,4,4,4,4,7,7]) + sage: H._latex_() + 'C_{4}^{5} \\times C_{7} \\times C_{7}' + sage: latex(HomologyGroup(6, ZZ)) + \ZZ^{6} + """ + eldv = self._original_invts + if len(eldv) == 0: + return "0" + rank = len(filter(lambda x: x == 0, eldv)) + torsion = sorted(filter(lambda x: x, eldv)) + if rank > 4: + g = ["\\ZZ^{%s}" % rank] + else: + g = ["\\ZZ"] * rank + if len(torsion) != 0: + printed = [] + for t in torsion: + numfac = torsion.count(t) + too_many = (numfac > 4) + if too_many: + if t not in printed: + g.append("C_{%s}^{%s}" % (t, numfac)) + printed.append(t) + else: + g.append("C_{%s}" % t) + times = " \\times " + return times.join(g) + +def HomologyGroup(n, base_ring, invfac=None): + """ + Abelian group on `n` generators. + + INPUT: + + - ``n`` -- integer. The number of generators. + + - ``base_ring`` -- ring. The base ring over which the homology is computed. + + - ``inv_fac`` -- list of integers. The invariant factors. Ignored + if the base ring is a field. + + OUTPUT: + + A class that can represent the homology group in a fixed + homological degree. + + EXAMPLES:: + + sage: from sage.homology.homology_group import HomologyGroup + sage: G = AbelianGroup(5, [5,5,7,8,9]); G + Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9 + sage: H = HomologyGroup(5, ZZ, [5,5,7,8,9]); H + C5 x C5 x C7 x C8 x C9 + sage: AbelianGroup(4) + Multiplicative Abelian group isomorphic to Z x Z x Z x Z + sage: HomologyGroup(4, ZZ) + Z x Z x Z x Z + sage: HomologyGroup(100, ZZ) + Z^100 + """ + if base_ring.is_field(): + return VectorSpace(base_ring, n) + + # copied from AbelianGroup: + if invfac is None: + if isinstance(n, (list, tuple)): + invfac = n + n = len(n) + else: + invfac = [] + if len(invfac) < n: + invfac = [0] * (n - len(invfac)) + invfac + elif len(invfac) > n: + raise ValueError, "invfac (=%s) must have length n (=%s)"%(invfac, n) + M = HomologyGroup_class(n, invfac) + return M diff --git a/src/sage/homology/matrix_utils.py b/src/sage/homology/matrix_utils.py new file mode 100644 index 00000000000..f63ce67df37 --- /dev/null +++ b/src/sage/homology/matrix_utils.py @@ -0,0 +1,212 @@ +""" +Utility Functions for Matrices + +The actual computation of homology groups ends up being linear algebra +with the differentials thought of as matrices. This module contains +some utility functions for this purpose. +""" + +######################################################################## +# Copyright (C) 2013 John H. Palmieri +# +# 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/ +######################################################################## + + +# TODO: this module is a clear candidate for cythonizing. Need to +# evaluate speed benefits. + +from sage.matrix.constructor import matrix + + +def dhsw_snf(mat, verbose=False): + """ + Preprocess a matrix using the "Elimination algorithm" described by + Dumas et al. [DHSW]_, and then call ``elementary_divisors`` on the + resulting (smaller) matrix. + + .. NOTE:: + + 'snf' stands for 'Smith Normal Form.' + + INPUT: + + - ``mat`` -- an integer matrix, either sparse or dense. + + (They use the transpose of the matrix considered here, so they use + rows instead of columns.) + + Algorithm: go through ``mat`` one column at a time. For each + column, add multiples of previous columns to it until either + + - it's zero, in which case it should be deleted. + - its first nonzero entry is 1 or -1, in which case it should be kept. + - its first nonzero entry is something else, in which case it is + deferred until the second pass. + + Then do a second pass on the deferred columns. + + At this point, the columns with 1 or -1 in the first entry + contribute to the rank of the matrix, and these can be counted and + then deleted (after using the 1 or -1 entry to clear out its row). + Suppose that there were `N` of these. + + The resulting matrix should be much smaller; we then feed it + to Sage's ``elementary_divisors`` function, and prepend `N` 1's to + account for the rows deleted in the previous step. + + EXAMPLES:: + + sage: from sage.homology.matrix_utils import dhsw_snf + sage: mat = matrix(ZZ, 3, 4, range(12)) + sage: dhsw_snf(mat) + [1, 4, 0] + sage: mat = random_matrix(ZZ, 20, 20, x=-1, y=2) + sage: mat.elementary_divisors() == dhsw_snf(mat) + True + + REFERENCES: + + .. [DHSW] Dumas, Heckenbach, Saunders, Welker, "Computing simplicial + homology based on efficient Smith normal form algorithms," in + "Algebra, geometry, and software systems" (2003), 177-206. + """ + ring = mat.base_ring() + rows = mat.nrows() + cols = mat.ncols() + new_data = {} + new_mat = matrix(ring, rows, cols, new_data) + add_to_rank = 0 + zero_cols = 0 + if verbose: + print "old matrix: %s by %s" % (rows, cols) + # leading_positions: dictionary of lists indexed by row: if first + # nonzero entry in column c is in row r, then leading_positions[r] + # should contain c + leading_positions = {} + # pass 1: + if verbose: + print "starting pass 1" + for j in range(cols): + # new_col is a matrix with one column: sparse matrices seem to + # be less buggy than sparse vectors (#5184, #5185), and + # perhaps also faster. + new_col = mat.matrix_from_columns([j]) + if new_col.is_zero(): + zero_cols += 1 + else: + check_leading = True + while check_leading: + i = new_col.nonzero_positions_in_column(0)[0] + entry = new_col[i,0] + check_leading = False + if i in leading_positions: + for c in leading_positions[i]: + earlier = new_mat[i,c] + # right now we don't check to see if entry divides + # earlier, because we don't want to modify the + # earlier columns of the matrix. Deal with this + # in pass 2. + if entry and earlier.divides(entry): + quo = entry.divide_knowing_divisible_by(earlier) + new_col = new_col - quo * new_mat.matrix_from_columns([c]) + entry = 0 + if not new_col.is_zero(): + check_leading = True + if not new_col.is_zero(): + new_mat.set_column(j-zero_cols, new_col.column(0)) + i = new_col.nonzero_positions_in_column(0)[0] + if i in leading_positions: + leading_positions[i].append(j-zero_cols) + else: + leading_positions[i] = [j-zero_cols] + else: + zero_cols += 1 + # pass 2: + # first eliminate the zero columns at the end + cols = cols - zero_cols + zero_cols = 0 + new_mat = new_mat.matrix_from_columns(range(cols)) + if verbose: + print "starting pass 2" + keep_columns = range(cols) + check_leading = True + while check_leading: + check_leading = False + new_leading = leading_positions.copy() + for i in leading_positions: + if len(leading_positions[i]) > 1: + j = leading_positions[i][0] + jth = new_mat[i, j] + for n in leading_positions[i][1:]: + nth = new_mat[i,n] + if jth.divides(nth): + quo = nth.divide_knowing_divisible_by(jth) + new_mat.add_multiple_of_column(n, j, -quo) + elif nth.divides(jth): + quo = jth.divide_knowing_divisible_by(nth) + jth = nth + new_mat.swap_columns(n, j) + new_mat.add_multiple_of_column(n, j, -quo) + else: + (g,r,s) = jth.xgcd(nth) + (unit,A,B) = r.xgcd(-s) # unit ought to be 1 here + jth_col = new_mat.column(j) + nth_col = new_mat.column(n) + new_mat.set_column(j, r*jth_col + s*nth_col) + new_mat.set_column(n, B*jth_col + A*nth_col) + nth = B*jth + A*nth + jth = g + # at this point, jth should divide nth + quo = nth.divide_knowing_divisible_by(jth) + new_mat.add_multiple_of_column(n, j, -quo) + new_leading[i].remove(n) + if new_mat.column(n).is_zero(): + keep_columns.remove(n) + zero_cols += 1 + else: + new_r = new_mat.column(n).nonzero_positions()[0] + if new_r in new_leading: + new_leading[new_r].append(n) + else: + new_leading[new_r] = [n] + check_leading = True + leading_positions = new_leading + # pass 3: get rid of columns which start with 1 or -1 + if verbose: + print "starting pass 3" + max_leading = 1 + for i in leading_positions: + j = leading_positions[i][0] + entry = new_mat[i,j] + if entry.abs() == 1: + add_to_rank += 1 + keep_columns.remove(j) + for c in new_mat.nonzero_positions_in_row(i): + if c in keep_columns: + new_mat.add_multiple_of_column(c, j, -entry * new_mat[i,c]) + else: + max_leading = max(max_leading, new_mat[i,j].abs()) + # form the new matrix + if max_leading != 1: + new_mat = new_mat.matrix_from_columns(keep_columns) + if verbose: + print "new matrix: %s by %s" % (new_mat.nrows(), new_mat.ncols()) + if new_mat.is_sparse(): + ed = [1]*add_to_rank + new_mat.dense_matrix().elementary_divisors() + else: + ed = [1]*add_to_rank + new_mat.elementary_divisors() + else: + if verbose: + print "new matrix: all pivots are 1 or -1" + ed = [1]*add_to_rank + + if len(ed) < rows: + return ed + [0]*(rows - len(ed)) + else: + return ed[:rows] + diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py index 53188a448a0..90fa13c4108 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/homology/simplicial_complex.py @@ -1864,7 +1864,7 @@ def _homology_(self, dim=None, **kwds): {0: 0, 1: 0, 2: Z} """ from sage.modules.all import VectorSpace - from sage.homology.chain_complex import HomologyGroup + from sage.homology.homology_group import HomologyGroup base_ring = kwds.get('base_ring', ZZ) cohomology = kwds.get('cohomology', False) @@ -1938,10 +1938,7 @@ def _homology_(self, dim=None, **kwds): temp[n] = answer[n] answer = temp else: # just a single dimension - if base_ring == ZZ: - answer = answer.get(dim, HomologyGroup(0)) - else: - answer = answer.get(dim, VectorSpace(base_ring, 0)) + answer = answer.get(dim, HomologyGroup(0, base_ring)) return answer def add_face(self, face): diff --git a/src/sage/homology/tests.py b/src/sage/homology/tests.py index 86ae5bc074c..7f62adc8f61 100644 --- a/src/sage/homology/tests.py +++ b/src/sage/homology/tests.py @@ -35,7 +35,7 @@ def random_chain_complex(level=1): sage: C = random_chain_complex() sage: C Chain complex with at most 2 nonzero terms over Integer Ring - sage: C._degree # random: either 1 or -1 + sage: C.degree_of_differential() # random: either 1 or -1 1 """ from sage.misc.prandom import randint From 0a6eabe9508d1a414e101694f6a633ffab53a2ea Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Fri, 6 Sep 2013 22:10:34 +0100 Subject: [PATCH 031/206] Ascii art and more code refactoring for chain complexes --- src/sage/homology/cell_complex.py | 54 ++--- src/sage/homology/chain_complex.py | 217 +++++++++++++++++--- src/sage/homology/chain_complex_morphism.py | 57 ++--- 3 files changed, 254 insertions(+), 74 deletions(-) diff --git a/src/sage/homology/cell_complex.py b/src/sage/homology/cell_complex.py index e821e4db3ff..ec4753f6c40 100644 --- a/src/sage/homology/cell_complex.py +++ b/src/sage/homology/cell_complex.py @@ -546,29 +546,37 @@ def homology(self, dim=None, **kwds): if 'subcomplex' in kwds: del kwds['subcomplex'] answer = C.homology(**kwds) - if isinstance(answer, dict): - if cohomology: - too_big = self.dimension() + 1 - if (not ((isinstance(dim, (list, tuple)) and too_big in dim) - or too_big == dim) - and too_big in answer): - del answer[too_big] - if -2 in answer: - del answer[-2] - if -1 in answer: - del answer[-1] - for d in range(self.dimension() + 1): - if d not in answer: - answer[d] = HomologyGroup(0, base_ring) - - if dim is not None: - if isinstance(dim, (list, tuple)): - temp = {} - for n in dim: - temp[n] = answer[n] - answer = temp - else: # just a single dimension - answer = answer.get(dim, HomologyGroup(0, base_ring)) + assert isinstance(answer, dict) + if dim is None: + zero = HomologyGroup(0, base_ring) + return dict([d, answer.get(d, zero)] for d in range(self.dimension()+1)) + else: + return answer[dim] + + + + # if cohomology: + # too_big = self.dimension() + 1 + # if (not ((isinstance(dim, (list, tuple)) and too_big in dim) + # or too_big == dim) + # and too_big in answer): + # del answer[too_big] + # if -2 in answer: + # del answer[-2] + # if -1 in answer: + # del answer[-1] + # for d in range(self.dimension() + 1): + # if d not in answer: + # answer[d] = HomologyGroup(0, base_ring) + + # if dim is not None: + # if isinstance(dim, (list, tuple)): + # temp = {} + # for n in dim: + # temp[n] = answer[n] + # answer = temp + # else: # just a single dimension + # answer = answer.get(dim, HomologyGroup(0, base_ring)) return answer def cohomology(self, dim=None, **kwds): diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 4c2eede90a7..8233593437c 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -20,18 +20,19 @@ differentials may change dimension by any fixed integer. Also, the modules may be indexed over an abelian group other than the -integers, e.g., `\ZZ^{m}` for some integer `m \geq 1`, in which -case the differentials may change the grading by any element of that -grading group. +integers, e.g., `\ZZ^{m}` for some integer `m \geq 1`, in which case +the differentials may change the grading by any element of that +grading group. The elements of the grading group are generally called +degrees, so `C_n` is the module in degree `n` and so on. In this implementation, the ring `R` must be commutative and the modules `C_n` must be free `R`-modules. As noted above, homology -calculations will only work if the ring `R` is either `\ZZ` or a field. -The modules may be indexed by any free abelian group. The +calculations will only work if the ring `R` is either `\ZZ` or a +field. The modules may be indexed by any free abelian group. The differentials may increase degree by 1 or decrease it, or indeed -change it by any fixed amount: this is controlled by the ``degree`` -parameter used in defining the chain complex. -""" +change it by any fixed amount: this is controlled by the +``degree_of_differential`` parameter used in defining the chain +complex. """ ######################################################################## @@ -98,6 +99,8 @@ def ChainComplex(data=None, **kwds): - ``data`` -- the data defining the chain complex; see below for more details. + + The following keyword arguments are supported: - ``base_ring`` -- a commutative ring (optional), the ring over which the chain complex is defined. If this is not specified, @@ -107,8 +110,10 @@ def ChainComplex(data=None, **kwds): default ``ZZ``), the group over which the chain complex is indexed. - - ``degree`` -- element of grading_group (optional, default 1), - the degree of the differential. + - ``degree_of_differential`` -- element of grading_group + (optional, default ``1``). The degree of the differential. + + - ``degree`` -- alias for ``degree_of_differential``. - ``check`` -- boolean (optional, default ``True``). If ``True``, check that each consecutive pair of differentials are @@ -224,12 +229,13 @@ def ChainComplex(data=None, **kwds): check = kwds.get('check', True) base_ring = kwds.get('base_ring', None) grading_group = kwds.get('grading_group', ZZ) - degree = kwds.get('degree', 1) + degree = kwds.get('degree_of_differential', kwds.get('degree', 1)) try: degree = grading_group(degree) except StandardError: raise ValueError('degree is not an element of the grading group') + # transform data into data_dict if data is None or (isinstance(data, (list, tuple)) and len(data) == 0): # the zero chain complex try: @@ -238,7 +244,7 @@ def ChainComplex(data=None, **kwds): zero = grading_group.zero_element() if base_ring is None: base_ring = ZZ - data_dict = {zero: matrix(base_ring, 0, 0, [])} + data_dict = dict() elif isinstance(data, dict): # data is dictionary data_dict = data else: # data is list/tuple/iterable @@ -258,7 +264,7 @@ def ChainComplex(data=None, **kwds): raise ValueError('one of the dictionary keys is not an element of the grading group') mat = data_dict[n] if not isinstance(mat, Matrix): - raise TypeError('One of the differentials in the data is not a matrix') + raise TypeError('one of the differentials in the data is not a matrix') if mat.base_ring() is base_ring: if not mat.is_immutable(): mat = copy(mat) # do not make any arguments passed immutable @@ -271,14 +277,16 @@ def ChainComplex(data=None, **kwds): # include any "obvious" zero matrices that are not 0x0 for n in data_dict.keys(): # note: data_dict will be mutated in this loop mat1 = data_dict[n] - if (n+degree not in data_dict) and (mat1.nrows() != 0): + if (mat1.nrows(), mat1.ncols()) == (0, 0): + del data_dict[n] + if (mat1.nrows() != 0) and (n+degree not in data_dict): if n+2*degree in data_dict: mat2 = matrix(base_ring, data_dict[n+2*degree].ncols(), mat1.nrows()) else: mat2 = matrix(base_ring, 0, mat1.nrows()) mat2.set_immutable() data_dict[n+degree] = mat2 - if (n-degree not in data_dict) and (mat1.ncols() != 0): + if (mat1.ncols() != 0) and (n-degree not in data_dict): if n-2*degree in data_dict: mat0 = matrix(base_ring, mat1.ncols(), data_dict[n-2*degree].nrows()) else: @@ -331,9 +339,22 @@ def is_boundary(self): class ChainComplex_class(Parent): def __init__(self, grading_group, degree_of_differential, base_ring, differentials): - """ + r""" See :func:`ChainComplex` for full documentation. + The differentials are required to be in the following canonical form: + + * All differentials that are not `0\times 0` must be specified + (even if they have zero rows or zero columns), and + + * Differentials that are `0\times 0` must not be specified. + + * Immutable matrices over the `base_ring` + + This and more is ensured by the assertions in the + constructor. The :func:`ChainComplex` factory function must + ensure that only valid input is passed. + EXAMPLES:: sage: C = ChainComplex(); C @@ -346,11 +367,12 @@ def __init__(self, grading_group, degree_of_differential, base_ring, differentia sage: ChainComplex().base_ring() Integer Ring """ - assert all(d.base_ring() == base_ring and d.is_immutable() + assert all(d.base_ring() == base_ring and d.is_immutable() and + (d.ncols(), d.nrows()) != (0, 0) for d in differentials.values()) assert degree_of_differential.parent() is grading_group assert grading_group is ZZ or not grading_group.is_multiplicative() - # all differentials that are not 0x0 must be specified to the constructor + # all differentials (excluding the 0x0 ones) must be specified to the constructor assert all(dim+degree_of_differential in differentials or d.nrows() == 0 for dim, d in differentials.iteritems()) assert all(dim-degree_of_differential in differentials or d.ncols() == 0 @@ -395,7 +417,7 @@ def rank(self, degree, ring=None): OUTPUT: - The rank of the differential ``d_{\delta} \otimes_R S`, where + The rank of the differential `d_{\delta} \otimes_R S`, where `R` is the base ring of the chain complex. EXAMPLES:: @@ -439,6 +461,60 @@ def grading_group(self): (2, 1) """ return self._grading_group + + @cached_method + def linearized_degrees(self, start=None): + r""" + Sort the degrees in the linear order determined by the differential + + INPUT: + + - ``start`` -- a degree (element of the grading group) or + ``None`` (default). + + OUTPUT: + + If ``start`` has been specified, the longest tuple of degrees + + * containing ``start`` + + * in ascending order relative to :meth:`degree_of_differential` + + * such that none of the corresponding differentials are `0\times 0`. + + If ``start`` has not been specified, a tuple of such tuples of + degrees. One for each sequence of non-zero differentials. They + are returned in sort order. + + EXAMPLES:: + + sage: one = matrix(ZZ, [[1]]) + sage: D = ChainComplex({0: one, 2: one, 6:one}) + sage: D.linearized_degrees() + ((-1, 0, 1, 2, 3), (5, 6, 7)) + """ + if start is None: + result = [] + degrees = set(self._diff.keys()) + while len(degrees) > 0: + linear = self.linearized_degrees(degrees.pop()) + result.append(linear) + degrees.difference_update(linear) + result.sort() + return tuple(result) + import collections + deg = start + result = collections.deque() + result.append(start) + next_deg = start + self.degree_of_differential() + while next_deg in self._diff: + result.append(next_deg) + next_deg += self.degree_of_differential() + prev_deg = start - self.degree_of_differential() + while prev_deg in self._diff: + result.appendleft(prev_deg) + prev_deg -= self.degree_of_differential() + return tuple(result) def degree_of_differential(self): """ @@ -524,24 +600,66 @@ def dual(self): for d in self.differential(): data[(d+deg)] = self.differential()[d].transpose() return ChainComplex(data, degree=-deg) + + def free_module_rank(self, degree): + r""" + Return the rank of the free module at the given ``degree``. - def free_module(self): + INPUT: + + - ``degree`` -- an element of the grading group. + + OUTPUT: + + Integer. The rank of the free module `C_n` at the given degree + `n`. + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1: matrix(ZZ, [[0, 1]])}) + sage: [C.free_module_rank(i) for i in range(-2, 5)] + [0, 0, 3, 2, 1, 0, 0] """ - The free module underlying this chain complex. + try: + return self._diff[degree].ncols() + except KeyError: + return ZZ.zero() + + def free_module(self, degree=None): + r""" + Return the free module at fixed ``degree``, or their sum. + + INPUT: + + - ``degree`` -- an element of the grading group or ``None`` (default). + + OUTPUT: + + The free module `C_n` at the given degree `n`. If the degree + is not specified, the sum `\bigoplus C_n` is returned. EXAMPLES:: - sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1: matrix(ZZ, 0, 2)}) + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1: matrix(ZZ, [[0, 1]])}) sage: C.free_module() - Ambient free module of rank 5 over the principal ideal domain Integer Ring + Ambient free module of rank 6 over the principal ideal domain Integer Ring + sage: C.free_module(0) + Ambient free module of rank 3 over the principal ideal domain Integer Ring + sage: C.free_module(1) + Ambient free module of rank 2 over the principal ideal domain Integer Ring + sage: C.free_module(2) + Ambient free module of rank 1 over the principal ideal domain Integer Ring This defines the forgetful functor from the category of chain complexes to the category of free modules:: sage: FreeModules(ZZ)(C) - Ambient free module of rank 5 over the principal ideal domain Integer Ring + Ambient free module of rank 6 over the principal ideal domain Integer Ring """ - rank = sum([mat.ncols() for mat in self.differential().values()]) + if degree is None: + rank = sum([mat.ncols() for mat in self.differential().values()]) + else: + rank = self.free_module_rank(degree) return FreeModule(self.base_ring(), rank) def __cmp__(self, other): @@ -1098,15 +1216,58 @@ def _repr_(self): def _ascii_art_(self): """ + Return an ascii art representation. + + Note that arrows go to the left so that composition of + differentials is the usual matrix multiplication. + EXAMPLES:: - sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1:zero_matrix(1,2)}) + sage: ascii_art(C) + [3 0 0] + [0 0] [0 0 0] + 0 <-- C_2 <------ C_1 <-------- C_0 <-- 0 + + sage: one = matrix(ZZ, [[1]]) + sage: D = ChainComplex({0: one, 2: one, 6:one}) + sage: ascii_art(D) + [1] [1] [0] [1] + 0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0 """ if self.grading_group() is not ZZ: return super(ChainComplex_class, self)._ascii_art_() from sage.misc.ascii_art import AsciiArt - dmin = min(self._diff.keys()) - dmax = max(self._diff.keys()) + + def arrow_art(n): + d_n = self.differential(n) + if d_n.nrows() == 0 or d_n.ncols() == 0: + return AsciiArt(['<--']) + d_str = [' '+line+' ' for line in str(d_n).splitlines()] + arrow = '<' + '-'*(len(d_str[0])-1) + d_str.append(arrow) + return AsciiArt(d_str) + + def module_art(n): + C_n = self.free_module(n) + if C_n.rank() == 0: + return AsciiArt([' 0 ']) + else: + return AsciiArt([' C_{0} '.format(n)]) + + result = [] + for linear in self.linearized_degrees(): + linear = list(reversed(linear)) + if len(linear) == 0: + return AsciiArt(['0']) + result_linear = module_art(linear[0] + self.degree_of_differential()) + for n in linear: + result_linear += arrow_art(n) + module_art(n) + result = [result_linear] + result + concatenated = result[0] + for r in result[1:]: + concatenated += AsciiArt([' ... ']) + r + return concatenated def _latex_(self): """ diff --git a/src/sage/homology/chain_complex_morphism.py b/src/sage/homology/chain_complex_morphism.py index c696ead6aca..864054aec48 100644 --- a/src/sage/homology/chain_complex_morphism.py +++ b/src/sage/homology/chain_complex_morphism.py @@ -71,10 +71,10 @@ def is_ChainComplexMorphism(x): sage: i = H.identity() sage: x = i.associated_chain_complex_morphism() sage: x # indirect doctest - Chain complex morphism from Chain complex with at most 7 nonzero terms over Integer Ring to Chain complex with at most 7 nonzero terms over Integer Ring + Chain complex morphism from Chain complex with at most 7 nonzero terms over + Integer Ring to Chain complex with at most 7 nonzero terms over Integer Ring sage: is_ChainComplexMorphism(x) True - """ return isinstance(x,ChainComplexMorphism) @@ -82,7 +82,7 @@ class ChainComplexMorphism(SageObject): """ An element of this class is a morphism of chain complexes. """ - def __init__(self, matrices, C, D): + def __init__(self, matrices, C, D, check=True): """ Create a morphism from a dictionary of matrices. @@ -101,7 +101,9 @@ def __init__(self, matrices, C, D): sage: G = Hom(C,C) sage: x = G(f) sage: x - Chain complex morphism from Chain complex with at most 2 nonzero terms over Integer Ring to Chain complex with at most 2 nonzero terms over Integer Ring + Chain complex morphism from Chain complex with at most 2 nonzero terms + over Integer Ring to Chain complex with at most 2 nonzero terms over + Integer Ring sage: x._matrix_dictionary {0: [0 0 0] [0 0 0] @@ -115,37 +117,46 @@ def __init__(self, matrices, C, D): sage: Y = simplicial_complexes.Simplex(0) sage: g = Hom(X,Y)({0:0, 1:0}) sage: g.associated_chain_complex_morphism() - Chain complex morphism from Chain complex with at most 2 nonzero terms over Integer Ring to Chain complex with at most 1 nonzero terms over Integer Ring + Chain complex morphism from Chain complex with at most 2 nonzero + terms over Integer Ring to Chain complex with at most 1 nonzero terms + over Integer Ring """ if not C.base_ring()==D.base_ring(): raise NotImplementedError('morphisms between chain complexes of different' ' base rings are not implemented') - if C._grading_group != ZZ: - raise NotImplementedError('only implemented over gradings other than ZZ') d = C.degree_of_differential() if d != D.degree_of_differential(): raise ValueError('degree of differential does not match') - if d != -1 and d != 1: - raise NotImplementedError('only implemented for degrees -1 and 1') - - dim_min = min(C.differential().keys() + D.differential().keys()) - dim_max = max(C.differential().keys() + D.differential().keys()) - for i in range(dim_min, dim_max): - if i not in matrices: + + from sage.misc.misc import uniq + degrees = uniq(C.differential().keys() + D.differential().keys()) + initial_matrices = matrices + matrices = dict() + for i in degrees: + if i - d not in degrees: + assert C.free_module_rank(i) == D.free_module_rank(i) == 0 + continue + try: + matrices[i] = initial_matrices.pop(i) + except KeyError: matrices[i] = matrix.zero_matrix(C.base_ring(), D.differential(i).ncols(), C.differential(i).ncols(), sparse=True) - chain_morphism_error = ValueError('matrices must define a chain complex morphism') - for i in range(dim_min, dim_max): - Dm = D.differential(i) * matrices[i] - if dim_min <= i+d < dim_max: + if check: + # all remaining matrices given must be 0x0 + assert all(m.ncols() == m.nrows() == 0 for m in initial_matrices.values()) + # check commutativity + for i in degrees: + if i - d not in degrees: + assert C.free_module_rank(i) == D.free_module_rank(i) == 0 + continue + if i + d not in degrees: + assert C.free_module_rank(i+d) == D.free_module_rank(i+d) == 0 + continue + Dm = D.differential(i) * matrices[i] mC = matrices[i+d] * C.differential(i) if mC != Dm: - raise chain_morphism_error - else: - if not Dm.is_zero(): - raise chain_morphism_error - + raise ValueError('matrices must define a chain complex morphism') self._matrix_dictionary = matrices self._domain = C self._codomain = D From 2249b00348748e37176f1f6a045eaed2642cebd9 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 7 Sep 2013 02:43:32 +0200 Subject: [PATCH 032/206] Added DiscreteValueGroup There has been no class for the value group of a valuation such as the valuation on the p-adics. Implemented as a facade of the rationals. --- src/sage/rings/padics/all.py | 1 + src/sage/rings/padics/discrete_value_group.py | 214 ++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 src/sage/rings/padics/discrete_value_group.py diff --git a/src/sage/rings/padics/all.py b/src/sage/rings/padics/all.py index 065deed57db..bc0107046a9 100644 --- a/src/sage/rings/padics/all.py +++ b/src/sage/rings/padics/all.py @@ -5,3 +5,4 @@ from padic_generic import local_print_mode from pow_computer import PowComputer from pow_computer_ext import PowComputer_ext_maker +from discrete_value_group import DiscreteValueGroup diff --git a/src/sage/rings/padics/discrete_value_group.py b/src/sage/rings/padics/discrete_value_group.py new file mode 100644 index 00000000000..2b7edd749f9 --- /dev/null +++ b/src/sage/rings/padics/discrete_value_group.py @@ -0,0 +1,214 @@ +r""" +Value groups of discrete valuations + +This file defines additive subgroups of \QQ generated by a rational number. + +AUTHORS: + +- Julian Rueth (2013-09-06): initial version + +""" +#***************************************************************************** +# Copyright (C) 2013 Julian Rueth +# +# 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/ +#***************************************************************************** +from sage.rings.all import ZZ, QQ, Rational, infinity +from sage.categories.modules import Modules +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation + +category = Modules(ZZ) + +class DiscreteValueGroup(UniqueRepresentation, Parent): + r""" + The value group of a discrete valuation, an additive subgroup of \QQ + generated by ``generator``. + + INPUT: + + - ``generator`` -- a rational number + + EXAMPLES:: + + sage: D1 = DiscreteValueGroup(0); D1 + DiscreteValueGroup(0) + sage: D2 = DiscreteValueGroup(4/3); D2 + DiscreteValueGroup(4/3) + sage: D3 = DiscreteValueGroup(-1/3); D3 + DiscreteValueGroup(1/3) + + TESTS:: + + sage: TestSuite(D1).run() + sage: TestSuite(D2).run() + sage: TestSuite(D3).run() + + """ + @staticmethod + def __classcall__(cls, generator, category=category): + r""" + Normalizes ``generator`` to a positive rational so that this is a + unique parent. + + TESTS:: + + sage: DiscreteValueGroup(1) is DiscreteValueGroup(-1) + True + + """ + from sage.misc.functional import coerce + coerce(QQ, generator) + generator = generator.abs() + return super(DiscreteValueGroup, cls).__classcall__(cls, generator, category) + + def __init__(self, generator, category): + r""" + Initialization. + + TESTS:: + + sage: type(DiscreteValueGroup(0)) + + + """ + self._generator = generator + + Parent.__init__(self, facade=QQ, category=category) + + def _element_constructor_(self, x): + r""" + Create an element in this group from ``x``. + + INPUT: + + - ``x`` -- a rational number + + TESTS:: + + sage: DiscreteValueGroup(0)(0) + 0 + sage: DiscreteValueGroup(0)(1) + Traceback (most recent call last): + ... + ValueError: `1` is not in DiscreteValueGroup(0). + sage: DiscreteValueGroup(1)(1) + 1 + sage: DiscreteValueGroup(1)(1/2) + Traceback (most recent call last): + ... + ValueError: `1/2` is not in DiscreteValueGroup(1). + + """ + from sage.misc.functional import coerce + x = coerce(QQ, x) + if x == 0 or (self._generator != 0 and x/self._generator in ZZ): + return x + + raise ValueError("`{0}` is not in {1}.".format(x,self)) + + def _repr_(self): + r""" + Return a printable representation for this group. + + EXAMPLES:: + + sage: DiscreteValueGroup(0) # indirect doctest + DiscreteValueGroup(0) + + """ + return "DiscreteValueGroup({0})".format(self._generator) + + def __add__(self, other): + r""" + Return the subgroup of \QQ generated by this group and ``other``. + + INPUT: + + - ``other`` -- a discrete value group or a rational number + + EXAMPLES:: + + sage: D = DiscreteValueGroup(1/2) + sage: D + 1/3 + DiscreteValueGroup(1/6) + sage: D + D + DiscreteValueGroup(1/2) + sage: D + 1 + DiscreteValueGroup(1/2) + sage: DiscreteValueGroup(2/7) + DiscreteValueGroup(4/9) + DiscreteValueGroup(2/63) + + """ + if not isinstance(other, DiscreteValueGroup): + from sage.structure.element import is_Element + if is_Element(other) and QQ.has_coerce_map_from(other.parent()): + return self + DiscreteValueGroup(other, category=self.category()) + raise ValueError("`other` must be a DiscreteValueGroup or a rational number") + if self.category() is not other.category(): + raise ValueError("`other` must be in the same category") + return DiscreteValueGroup(self._generator.gcd(other._generator), category=self.category()) + + def _mul_(self, other, switch_sides=False): + r""" + Return the group generated by ``other`` times the generator of this + group. + + INPUT: + + - ``other`` -- a rational number + + EXAMPLES:: + + sage: D = DiscreteValueGroup(1/2) + sage: 1/2 * D + DiscreteValueGroup(1/4) + sage: D * (1/2) + DiscreteValueGroup(1/4) + sage: D * 0 + DiscreteValueGroup(0) + + """ + from sage.misc.functional import coerce + other = coerce(QQ, other) + return DiscreteValueGroup(self._generator*other, category=self.category()) + + def index(self, other): + r""" + Return the index of ``other`` in this group. + + INPUT: + + - ``other`` -- a subgroup of this group + + EXAMPLES:: + + sage: DiscreteValueGroup(3/8).index(DiscreteValueGroup(3)) + 8 + sage: DiscreteValueGroup(3).index(DiscreteValueGroup(3/8)) + Traceback (most recent call last): + ... + ValueError: `other` must be a subgroup of this group + sage: DiscreteValueGroup(3).index(DiscreteValueGroup(0)) + +Infinity + sage: DiscreteValueGroup(0).index(DiscreteValueGroup(0)) + 1 + sage: DiscreteValueGroup(0).index(DiscreteValueGroup(3)) + Traceback (most recent call last): + ... + ValueError: `other` must be a subgroup of this group + + """ + if not isinstance(other, DiscreteValueGroup): + raise ValueError("`other` must be a DiscreteValueGroup") + if other._generator not in self: + raise ValueError("`other` must be a subgroup of this group") + if other._generator == 0: + if self._generator == 0: + return ZZ(1) + else: + return infinity + return ZZ(other._generator / self._generator) From ed95223058827b5c3012c51f9b24b659867c5b4f Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 7 Sep 2013 14:10:26 +0100 Subject: [PATCH 033/206] Chains (elements of chain complexes) and their module structure --- src/sage/categories/category_types.py | 7 +- src/sage/homology/chain_complex.py | 347 ++++++++++++++++++-- src/sage/homology/chain_complex_morphism.py | 2 +- 3 files changed, 324 insertions(+), 32 deletions(-) diff --git a/src/sage/categories/category_types.py b/src/sage/categories/category_types.py index da6516d4b3f..ced52b995b1 100644 --- a/src/sage/categories/category_types.py +++ b/src/sage/categories/category_types.py @@ -450,4 +450,9 @@ def super_categories(self): sage: ChainComplexes(Integers(9)).super_categories() [Category of objects] """ - return [Objects()] # anything better? + from sage.categories.all import Fields, FreeModules, VectorSpaces + base_ring = self.base_ring() + if base_ring in Fields(): + return [VectorSpaces(base_ring)] + else: + return [FreeModules(base_ring)] diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 8233593437c..5875b822e1a 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -316,25 +316,238 @@ def ChainComplex(data=None, **kwds): class Chain_class(ModuleElement): - def __init__(self, parent, *args, **kwds): - """ - Parent for all chain complexes over a given ``base_ring`` + def __init__(self, parent, vectors, check=True): + r""" + A Chain in a Chain Complex + + A chain is collection of module elements for each module `C_n` + of the chain complex `(C_n, d_n)`. There is no restriction on + how the differentials `d_n` act on the elements of the chain. + + .. note: + + You must use the chain complex to construct chains. EXAMPLES:: sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}, base_ring=GF(7)) sage: C.category() Category of chain complexes over Finite Field of size 7 + + TESTS:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: c = C({0:vector([0, 1, 2]), 1:vector([3, 4])}) + sage: TestSuite(c).run() """ + # only nonzero vectors shall be stored, ensuring this is the + # job of the _element constructor_ + assert all(v.is_immutable() and not v.is_zero() + and v.base_ring() is parent.base_ring() + for v in vectors.values()) + self._vec = vectors super(Chain_class, self).__init__(parent) + def vector(self, degree): + """ + Return the free module element in ``degree``. + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: c = C({0:vector([1, 2, 3]), 1:vector([4, 5])}) + sage: c.vector(0) + (1, 2, 3) + sage: c.vector(1) + (4, 5) + sage: c.vector(2) + () + """ + try: + return self._vec[degree] + except KeyError: + return self.parent().free_module(degree).zero() + + def _repr_(self): + """ + Print representation. + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: C() + Trivial chain + sage: c = C({0:vector([1, 2, 3]), 1:vector([4, 5])}); c + Chain with 2 nonzero terms over Integer Ring + sage: c._repr_() + 'Chain with 2 nonzero terms over Integer Ring' + """ + n = len(self._vec) + if n == 0: + return 'Trivial chain' + else: + return 'Chain with {0} nonzero terms over {1}'.format( + n, self.parent().base_ring()) + + def _ascii_art_(self): + """ + Return an ascii art representation. + + Note that arrows go to the left so that composition of + differentials is the usual matrix multiplication. + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1:zero_matrix(1,2)}) + sage: c = C({0:vector([1, 2, 3]), 1:vector([4, 5])}) + sage: ascii_art(c) + d_2 d_1 d_0 [1] d_-1 + 0 <---- [0] <---- [4] <---- [2] <----- 0 + [5] [3] + """ + from sage.misc.ascii_art import AsciiArt + + def arrow_art(d): + d_str = [' d_{0} '.format(d)] + arrow = ' <' + '-'*(len(d_str[0])-3) + ' ' + d_str.append(arrow) + return AsciiArt(d_str, baseline=0) + + def vector_art(d): + v = self.vector(d) + if v.degree() == 0: + return AsciiArt(['0']) + v = str(v.column()).splitlines() + return AsciiArt(v, baseline=len(v)/2) + + result = [] + chain_complex = self.parent() + for ordered in chain_complex.ordered_degrees(): + ordered = list(reversed(ordered)) + if len(ordered) == 0: + return AsciiArt(['0']) + result_ordered = vector_art(ordered[0] + chain_complex.degree_of_differential()) + for n in ordered: + result_ordered += arrow_art(n) + vector_art(n) + result = [result_ordered] + result + concatenated = result[0] + for r in result[1:]: + concatenated += AsciiArt([' ... ']) + r + return concatenated + def is_cycle(self): - pass # TODO + """ + Return whether the chain is a cycle. + + OUTPUT: + + Boolean. Whether the elements of the chain are in the kernel + of the differentials. + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: c = C({0:vector([0, 1, 2]), 1:vector([3, 4])}) + sage: c.is_cycle() + True + """ + chain_complex = self.parent() + for d, v in self._vec.iteritems(): + dv = chain_complex.differential(d) * v + if not dv.is_zero(): + return False + return True def is_boundary(self): - pass # TODO + """ + Return whether the chain is a boundary. + + OUTPUT: + + Boolean. Whether the elements of the chain are in the image of + the differentials. + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: c = C({0:vector([0, 1, 2]), 1:vector([3, 4])}) + sage: c.is_boundary() + False + """ + chain_complex = self.parent() + for d, v in self._vec.iteritems(): + d = chain_complex.differential(d - chain_complex.degree_of_differential()) + if v not in d.image(): + return False + return True + + def _add_(self, other): + """ + Module addition + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: c = C({0:vector([0, 1, 2]), 1:vector([3, 4])}) + sage: c + c + Chain with 2 nonzero terms over Integer Ring + sage: ascii_art(c + c) + d_1 d_0 [0] d_-1 + 0 <---- [6] <---- [2] <----- 0 + [8] [4] + """ + vectors = dict() + for d in set(self._vec.keys() + other._vec.keys()): + v = self.vector(d) + other.vector(d) + if not v.is_zero(): + v.set_immutable() + vectors[d] = v + parent = self.parent() + return parent.element_class(parent, vectors) + + def _rmul_(self, scalar): + """ + Scalar multiplication + EXAMPLES:: + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: c = C({0:vector([0, 1, 2]), 1:vector([3, 4])}) + sage: 2 * c + Chain with 2 nonzero terms over Integer Ring + sage: 2 * c == c + c == c * 2 + True + """ + vectors = dict() + for d, v in self._vec.iteritems(): + v = scalar * v + if not v.is_zero(): + v.set_immutable() + vectors[d] = v + parent = self.parent() + return parent.element_class(parent, vectors) + + def __cmp__(self, other): + """ + Compare two chains + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: c = C({0:vector([0, 1, 2]), 1:vector([3, 4])}) + sage: c == c + True + sage: c == C(0) + False + """ + c = cmp(type(self), type(other)) + if c != 0: + return c + c = cmp(self.parent(), other.parent()) + if c != 0: + return c + return cmp(self._vec, other._vec) + class ChainComplex_class(Parent): @@ -364,8 +577,13 @@ def __init__(self, grading_group, degree_of_differential, base_ring, differentia sage: D Chain complex with at most 2 nonzero terms over Integer Ring + TESTS:: + sage: ChainComplex().base_ring() Integer Ring + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: TestSuite(C).run() """ assert all(d.base_ring() == base_ring and d.is_immutable() and (d.ncols(), d.nrows()) != (0, 0) @@ -387,19 +605,53 @@ def __init__(self, grading_group, degree_of_differential, base_ring, differentia Element = Chain_class - def _element_constructor_(self, *args): + def _element_constructor_(self, vectors, check=True): """ The element constructor. This is part of the Parent/Element framework. Calling the parent uses this method to construct elements. - EXAMPLES:: + TESTS:: sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])}) sage: D._element_constructor_(0) + Trivial chain + """ + if not vectors: # special case: the zero chain + return self.element_class(self, {}) + if isinstance(vectors, Chain_class): + vectors = vectors._vec + data = dict() + for degree, vec in vectors.iteritems(): + if check and vec.degree() != self.free_module_rank(degree): + raise ValueError('vector dimension does not match module dimension') + if vec.is_zero(): + continue + if vec.base_ring() != self.base_ring(): + vec = vec.change_ring(self.base_ring()) + if not vec.is_immutable(): + vec = copy(vec) + vec.set_immutable() + data[degree] = vec + return self.element_class(self, data) + + def random_element(self): """ - return self.element_class(self, *args) + Return a random element. + + EXAMPLES:: + + sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])}) + sage: D.random_element() # random output + Chain with 1 nonzero terms over Integer Ring + """ + vec = dict() + for d in self.nonzero_degrees(): + vec[d] = self.free_module(d).random_element() + return self(vec) + + _an_element_ = random_element @cached_method def rank(self, degree, ring=None): @@ -463,20 +715,50 @@ def grading_group(self): return self._grading_group @cached_method - def linearized_degrees(self, start=None): + def nonzero_degrees(self): r""" - Sort the degrees in the linear order determined by the differential + Return the degrees in which the module is non-trivial. + + See also :meth:`ordered_degrees`. + + OUTPUT: + + The tuple containing all degrees `n` (grading group elements) + such that the module `C_n` of the chain is non-trivial. + + EXAMPLES:: + + sage: one = matrix(ZZ, [[1]]) + sage: D = ChainComplex({0: one, 2: one, 6:one}) + sage: ascii_art(D) + [1] [1] [0] [1] + 0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0 + sage: D.nonzero_degrees() + (0, 1, 2, 3, 6, 7) + """ + return tuple(sorted(n for n,d in self._diff.iteritems() if d.ncols() > 0)) + + @cached_method + def ordered_degrees(self, start=None, exclude_first=False): + r""" + Sort the degrees in the order determined by the differential INPUT: - ``start`` -- a degree (element of the grading group) or ``None`` (default). + - ``exclude_first`` -- boolean (optional; default: + ``False``). Whether to exclude the lowest degree. This is a + handy way to just get the degrees of the non-zero modules, + as the domain of the first differential is zero. + OUTPUT: If ``start`` has been specified, the longest tuple of degrees - * containing ``start`` + * containing ``start`` (unless ``start`` would be the first + and ``exclude_first=True``) * in ascending order relative to :meth:`degree_of_differential` @@ -490,16 +772,27 @@ def linearized_degrees(self, start=None): sage: one = matrix(ZZ, [[1]]) sage: D = ChainComplex({0: one, 2: one, 6:one}) - sage: D.linearized_degrees() + sage: ascii_art(D) + [1] [1] [0] [1] + 0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0 + sage: D.ordered_degrees() ((-1, 0, 1, 2, 3), (5, 6, 7)) + sage: D.ordered_degrees(exclude_first=True) + ((0, 1, 2, 3), (6, 7)) + sage: D.ordered_degrees(6) + (5, 6, 7) + sage: D.ordered_degrees(5, exclude_first=True) + (6, 7) """ if start is None: result = [] degrees = set(self._diff.keys()) while len(degrees) > 0: - linear = self.linearized_degrees(degrees.pop()) - result.append(linear) - degrees.difference_update(linear) + ordered = self.ordered_degrees(degrees.pop()) + degrees.difference_update(ordered) + if exclude_first: + ordered = tuple(ordered[1:]) + result.append(ordered) result.sort() return tuple(result) import collections @@ -514,6 +807,8 @@ def linearized_degrees(self, start=None): while prev_deg in self._diff: result.appendleft(prev_deg) prev_deg -= self.degree_of_differential() + if exclude_first: + result.popleft() return tuple(result) def degree_of_differential(self): @@ -649,12 +944,6 @@ def free_module(self, degree=None): Ambient free module of rank 2 over the principal ideal domain Integer Ring sage: C.free_module(2) Ambient free module of rank 1 over the principal ideal domain Integer Ring - - This defines the forgetful functor from the category of chain - complexes to the category of free modules:: - - sage: FreeModules(ZZ)(C) - Ambient free module of rank 6 over the principal ideal domain Integer Ring """ if degree is None: rank = sum([mat.ncols() for mat in self.differential().values()]) @@ -1235,8 +1524,6 @@ def _ascii_art_(self): [1] [1] [0] [1] 0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0 """ - if self.grading_group() is not ZZ: - return super(ChainComplex_class, self)._ascii_art_() from sage.misc.ascii_art import AsciiArt def arrow_art(n): @@ -1256,14 +1543,14 @@ def module_art(n): return AsciiArt([' C_{0} '.format(n)]) result = [] - for linear in self.linearized_degrees(): - linear = list(reversed(linear)) - if len(linear) == 0: + for ordered in self.ordered_degrees(): + ordered = list(reversed(ordered)) + if len(ordered) == 0: return AsciiArt(['0']) - result_linear = module_art(linear[0] + self.degree_of_differential()) - for n in linear: - result_linear += arrow_art(n) + module_art(n) - result = [result_linear] + result + result_ordered = module_art(ordered[0] + self.degree_of_differential()) + for n in ordered: + result_ordered += arrow_art(n) + module_art(n) + result = [result_ordered] + result concatenated = result[0] for r in result[1:]: concatenated += AsciiArt([' ... ']) + r diff --git a/src/sage/homology/chain_complex_morphism.py b/src/sage/homology/chain_complex_morphism.py index 864054aec48..a262ee29916 100644 --- a/src/sage/homology/chain_complex_morphism.py +++ b/src/sage/homology/chain_complex_morphism.py @@ -130,7 +130,7 @@ def __init__(self, matrices, C, D, check=True): from sage.misc.misc import uniq degrees = uniq(C.differential().keys() + D.differential().keys()) - initial_matrices = matrices + initial_matrices = dict(matrices) matrices = dict() for i in degrees: if i - d not in degrees: From 7543b853cfc7a1d446acbee801cec9e5bf19b668 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 7 Sep 2013 19:23:12 +0100 Subject: [PATCH 034/206] finished refactoring the homology() method --- src/sage/homology/chain_complex.py | 360 +++++++++++++++-------------- 1 file changed, 183 insertions(+), 177 deletions(-) diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 5875b822e1a..159d972b3ac 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -61,6 +61,7 @@ from sage.misc.latex import latex from sage.rings.all import GF, prime_range from sage.misc.decorators import rename_keyword +from sage.homology.homology_group import HomologyGroup def _latex_module(R, m): @@ -984,17 +985,39 @@ def __cmp__(self, other): return 0 return -1 - def homology(self, dim=None, **kwds): + def _homology_chomp(deg, base_ring, verbose, generators): + """ + Helper function for :meth:`homology` + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}, base_ring=GF(2)) + sage: C._homology_chomp(None, GF(2), False, False) # optional - CHomP + + """ + H = homchain(self, **kwds) + if H is None: + raise RuntimeError('ran CHomP, but no output') + if deg is None: + return H + try: + return H[d] + except KeyError: + return HomologyGroup(0, base_ring) + + + @rename_keyword(deprecation=15151, dim='deg') + def homology(self, deg=None, **kwds): r""" - The homology of the chain complex in the given dimension. + The homology of the chain complex. INPUT: - - ``dim`` -- an element of the grading group for the chain - complex (optional, default ``None``): the degree in which to - compute homology. If this is ``None``, return the homology in - every dimension in which the chain complex is possibly - nonzero. + - ``deg`` -- an element of the grading group for the chain + complex (optional, default ``None``): the degree in which + to compute homology. If this is ``None``, return the + homology in every degree in which the chain complex is + possibly nonzero. - ``base_ring`` -- a commutative ring (optional, default is the base ring for the chain complex). Must be either the @@ -1007,22 +1030,22 @@ def homology(self, dim=None, **kwds): - ``verbose`` - boolean (optional, default ``False``). If ``True``, print some messages as the homology is computed. - - ``algorithm`` - string (optional, default ``'auto'``). The - options are ``'auto'``, ``'dhsw'``, ``'pari'`` or ``'no_chomp'``. - See below for descriptions. + - ``algorithm`` - string (optional, default ``'auto'``). The + options are ``'auto'``, ``'chomp'``, ``'dhsw'``, ``'pari'`` + or ``'no_chomp'``. See below for descriptions. OUTPUT: - If dim is specified, the homology in dimension ``dim``. + If the degree is specified, the homology in degree ``deg``. Otherwise, the homology in every dimension as a dictionary indexed by dimension. ALGORITHM: If ``algorithm`` is set to ``'auto'`` (the default), then use - CHomP if available. (CHomP is available at the web page + CHomP if available. CHomP is available at the web page http://chomp.rutgers.edu/. It is also an experimental package - for Sage.) + for Sage. If ``algorithm`` is ``chomp``, always use chomp. CHomP computes homology, not cohomology, and only works over the integers or finite prime fields. Therefore if any of @@ -1065,7 +1088,7 @@ def homology(self, dim=None, **kwds): sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) sage: C.homology() {0: Z x Z, 1: Z x C3} - sage: C.homology(dim=1, base_ring = GF(3)) + sage: C.homology(deg=1, base_ring = GF(3)) Vector space of dimension 2 over Finite Field of size 3 sage: D = ChainComplex({0: identity_matrix(ZZ, 4), 4: identity_matrix(ZZ, 30)}) sage: D.homology() @@ -1100,186 +1123,171 @@ def homology(self, dim=None, **kwds): 2: [(Vector space of dimension 1 over Rational Field, (1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1))]} """ - from sage.homology.homology_group import HomologyGroup from sage.interfaces.chomp import have_chomp, homchain - if dim is not None and dim not in self.grading_group(): - raise ValueError('dimension is not an element of the grading group') - - algorithm = kwds.get('algorithm', 'auto') - if algorithm not in ['dhsw', 'pari', 'auto', 'no_chomp']: - raise NotImplementedError('algorithm not recognized') + if deg is not None and deg not in self.grading_group(): + raise ValueError('degree is not an element of the grading group') verbose = kwds.get('verbose', False) - base_ring = kwds.get('base_ring', None) generators = kwds.get('generators', False) - if base_ring is None or base_ring == self.base_ring(): - change_ring = False - base_ring = self.base_ring() - else: - change_ring = True + base_ring = kwds.get('base_ring', self.base_ring()) if not (base_ring.is_field() or base_ring is ZZ): raise NotImplementedError('can only compute homology if the base ring is the integers or a field') - # try to use CHomP if working over Z or F_p, p a prime. - if (algorithm == 'auto' and (base_ring == ZZ or - (base_ring.is_prime_field() and base_ring != QQ))): - # compute all of homology, then pick off requested dimensions - H = None - if have_chomp('homchain'): - H = homchain(self, **kwds) - # now pick off the requested dimensions - if H: - if dim is not None: - answer = {} - if isinstance(dim, (list, tuple)): - for d in dim: - if d in H: - answer[d] = H[d] - else: - answer[d] = HomologyGroup(0, base_ring) - else: - if dim in H: - answer = H[dim] - else: - answer = HomologyGroup(0, base_ring) - else: - answer = H - return answer - else: - if verbose: - print "ran CHomP, but no output." - - # if dim is None, return all of the homology groups - degree = self.degree_of_differential() - if dim is None: + algorithm = kwds.get('algorithm', 'auto') + if algorithm not in ['dhsw', 'pari', 'auto', 'no_chomp', 'chomp']: + raise NotImplementedError('algorithm not recognized') + if algorithm == 'auto' \ + and (base_ring == ZZ or (base_ring.is_prime_field() and base_ring != QQ)) \ + and have_chomp('homchain'): + algorithm = 'chomp' + if algorithm == 'chomp': + return self._homology_chomp(deg, base_ring, verbose, generators) + + if deg is None: + deg = self.nonzero_degrees() + if isinstance(deg, (list, tuple)): answer = {} - for n in self._diff.keys(): - if n-degree not in self._diff: - continue - if verbose: - print "Computing homology of the chain complex in dimension %s..." % n - if base_ring == self.base_ring(): - answer[n] = self.homology(n, verbose=verbose, - generators=generators, - algorithm=algorithm) - else: - answer[n] = self.homology(n, base_ring=base_ring, - verbose=verbose, - generators=generators, - algorithm=algorithm) + for deg in self.nonzero_degrees(): + answer[deg] = self._homology_in_degree(deg, base_ring, verbose, generators, algorithm) return answer + else: + return self._homology_in_degree(deg, base_ring, verbose, generators, algorithm) + + def _homology_in_degree(self, deg, base_ring, verbose, generators, algorithm): + """ + Helper method for :meth:`homology`. - # now compute the homology in the given dimension - if dim in self._diff: - # d_out is the differential going out of degree dim, - # d_in is the differential entering degree dim - d_out_cols = self._diff[dim].ncols() - d_out_rows = self._diff[dim].nrows() - if base_ring == ZZ: - temp_ring = QQ + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: C.homology(1) == C._homology_in_degree(1, ZZ, False, False, 'auto') + True + """ + if deg not in self.nonzero_degrees(): + zero_homology = HomologyGroup(0, base_ring) + if generators: + return (zero_homology, vector(base_ring, [])) else: - temp_ring = base_ring - d_out_rank = self.rank(dim, ring=temp_ring) + return zero_homology + if verbose: + print('Computing homology of the chain complex in dimension %s...' % n) + + fraction_field = base_ring.fraction_field() + def change_ring(X): + if X.base_ring() is base_ring: + return X + return X.change_ring(base_ring) + + # d_out is the differential going out of degree deg, + # d_in is the differential entering degree deg + differential = self.degree_of_differential() + d_in = change_ring(self.differential(deg - differential)) + d_out = change_ring(self.differential(deg)) + d_out_rank = self.rank(deg, ring=fraction_field) + d_out_nullity = d_out.ncols() - d_out_rank + + if d_in.is_zero(): + if generators: #Include the generators of the nullspace + return [(HomologyGroup(1, base_ring), gen) + for gen in d_out.right_kernel().basis()] + else: + return HomologyGroup(d_out_nullity, base_ring) - if dim - degree in self._diff: - if change_ring: - d_in = self._diff[dim-degree].change_ring(base_ring) - else: - d_in = self._diff[dim-degree] - - if generators: - # Find the kernel of the out-going differential. - K = self._diff[dim].right_kernel().matrix().transpose().change_ring(base_ring) - - # Compute the induced map to the kernel - S = K.augment(d_in).hermite_form() - d_in_induced = S.submatrix(row=0, nrows=d_in.nrows()-d_out_rank, - col=d_in.nrows()-d_out_rank, ncols=d_in.ncols()) - - # Find the SNF of the induced matrix and appropriate generators - (N, P, Q) = d_in_induced.smith_form() - all_divs = [0]*N.nrows() - non_triv = 0 - for i in range(0, N.nrows()): - if i >= N.ncols(): - break - all_divs[i] = N[i][i] - if N[i][i] == 1: - non_triv = non_triv + 1 - divisors = filter(lambda x: x != 1, all_divs) - gens = (K * P.inverse().submatrix(col=non_triv)).transpose() - answer = [(HomologyGroup(1, base_ring, [divisors[i]]), gens[i]) - for i in range(len(divisors))] + if generators: + orders, gens = self._homology_generators_snf(d_in, d_out, d_out_rank) + answer = [(HomologyGroup(1, base_ring, [order]), gen) + for order, gen in zip(orders, gens)] + else: + if base_ring.is_field(): + d_in_rank = self.rank(deg-differential, ring=base_ring) + answer = HomologyGroup(d_out_nullity - d_in_rank, base_ring) + elif base_ring == ZZ: + if d_in.ncols() == 0: + all_divs = [0] * d_out_nullity else: - if base_ring.is_field(): - null = d_out_cols - d_out_rank - rk = self.rank(dim-degree, ring=temp_ring) - answer = HomologyGroup(null - rk, base_ring) - elif base_ring == ZZ: - nullity = d_out_cols - d_out_rank - if d_in.ncols() == 0: - all_divs = [0] * nullity + if algorithm in ['auto', 'no_chomp']: + if ((d_in.ncols() > 300 and d_in.nrows() > 300) + or (min(d_in.ncols(), d_in.nrows()) > 100 and + d_in.ncols() + d_in.nrows() > 600)): + algorithm = 'dhsw' else: - if algorithm == 'auto': - if ((d_in.ncols() > 300 and d_in.nrows() > 300) - or (min(d_in.ncols(), d_in.nrows()) > 100 and - d_in.ncols() + d_in.nrows() > 600)): - algorithm = 'dhsw' - else: - algorithm = 'pari' - if algorithm == 'dhsw': - from sage.homology.matrix_utils import dhsw_snf - all_divs = dhsw_snf(d_in, verbose=verbose) - else: - algorithm = 'pari' - if d_in.is_sparse(): - all_divs = d_in.dense_matrix().elementary_divisors(algorithm) - else: - all_divs = d_in.elementary_divisors(algorithm) - all_divs = all_divs[:nullity] - # divisors equal to 1 produce trivial - # summands, so filter them out - divisors = filter(lambda x: x != 1, all_divs) - answer = HomologyGroup(len(divisors), base_ring, divisors) - else: # no incoming differential: it's zero - answer = HomologyGroup(d_out_cols - d_out_rank, base_ring) - if generators: #Include the generators of the nullspace - # Find the kernel of the out-going differential. - K = self._diff[dim].right_kernel().matrix().change_ring(base_ring) - answer = [( answer, vector(base_ring, K.list()) )] - else: # chain complex is zero here, so return the zero module - answer = HomologyGroup(0, base_ring) - if generators: - answer = [(answer, vector(base_ring, []))] - if verbose: - print " Homology is %s" % answer + algorithm = 'pari' + if algorithm == 'dhsw': + from sage.homology.matrix_utils import dhsw_snf + all_divs = dhsw_snf(d_in, verbose=verbose) + elif algorithm == 'pari': + all_divs = d_in.elementary_divisors(algorithm) + else: + raise ValueError('unsupported algorithm') + all_divs = all_divs[:d_out_nullity] + # divisors equal to 1 produce trivial + # summands, so filter them out + divisors = filter(lambda x: x != 1, all_divs) + answer = HomologyGroup(len(divisors), base_ring, divisors) + else: + raise NotImplementedError('only base rings ZZ and fields are supported') return answer - def betti(self, dim=None, **kwds): + def _homology_generators_snf(self, d_in, d_out, d_out_rank): """ - The Betti number of the homology of the chain complex in this - dimension. + Compute the homology generators using Smith normal form + + EXAMPLES:: - That is, write the homology in this dimension as a direct sum + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: C.homology(1) + Z x C3 + sage: C._homology_generators_snf(C.differential(0), C.differential(1), 0) + ([3, 0], [(1, 0), (0, 1)]) + """ + # Find the kernel of the out-going differential. + K = d_out.right_kernel().matrix().transpose().change_ring(d_out.base_ring()) + + # Compute the induced map to the kernel + S = K.augment(d_in).hermite_form() + d_in_induced = S.submatrix(row=0, nrows=d_in.nrows()-d_out_rank, + col=d_in.nrows()-d_out_rank, ncols=d_in.ncols()) + + # Find the SNF of the induced matrix and appropriate generators + (N, P, Q) = d_in_induced.smith_form() + all_divs = [0]*N.nrows() + non_triv = 0 + for i in range(0, N.nrows()): + if i >= N.ncols(): + break + all_divs[i] = N[i][i] + if N[i][i] == 1: + non_triv = non_triv + 1 + divisors = filter(lambda x: x != 1, all_divs) + gens = (K * P.inverse().submatrix(col=non_triv)).columns() + return divisors, gens + + def betti(self, deg=None, base_ring=None): + """ + The Betti number the chain complex. + + That is, write the homology in this degree as a direct sum of a free module and a torsion module; the Betti number is the rank of the free summand. INPUT: - - ``dim`` -- an element of the grading group for the chain + - ``deg`` -- an element of the grading group for the chain complex or None (optional, default ``None``). If ``None``, then return every Betti number, as a dictionary indexed by degree. If an element of the grading group, then return - the Betti number in that dimension. + the Betti number in that degree. - ``base_ring`` -- a commutative ring (optional, default is the base ring for the chain complex). Compute homology with these coefficients. Must be either the integers or a field. - OUTPUT: the Betti number in dimension ``dim`` - the rank of - the free part of the homology module in this dimension. + OUTPUT: + + The Betti number in degree ``deg`` - the rank of the free + part of the homology module in this degree. EXAMPLES:: @@ -1291,21 +1299,17 @@ def betti(self, dim=None, **kwds): sage: C.betti() {0: 2, 1: 1} """ - base_ring = kwds.get('base_ring', None) - if base_ring is None: - base_ring = self.base_ring() - if base_ring == ZZ: base_ring = QQ - if base_ring.is_field(): - kwds['base_ring'] = base_ring - H = self.homology(dim=dim, **kwds) - if isinstance(H, dict): - return dict([i, H[i].dimension()] for i in H) - else: - return H.dimension() + try: + base_ring = base_ring.fraction_field() + except AttributeError: + raise NotImplementedError('only implemented if the base ring is ZZ or a field') + H = self.homology(deg, base_ring=base_ring) + if isinstance(H, dict): + return dict([deg, homology_group.dimension()] for deg, homology_group in H.iteritems()) else: - raise NotImplementedError, "Not implemented: unable to compute Betti numbers if the base ring is not ZZ or a field." + return H.dimension() def torsion_list(self, max_prime, min_prime=2): r""" @@ -1320,9 +1324,9 @@ def torsion_list(self, max_prime, min_prime=2): - ``min_prime`` -- prime (optional, default 2): search for torsion mod `p` for primes at least as big as this. - Return a list of pairs (`p`, ``dims``) where `p` is a prime at - which there is torsion and ``dims`` is a list of dimensions in - which this torsion occurs. + Return a list of pairs `(p, d)` where `p` is a prime at which + there is torsion and `d` is a list of dimensions in which this + torsion occurs. The base ring for the chain complex must be the integers; if not, an error is raised. @@ -1392,7 +1396,9 @@ def _Hom_(self, other, category=None): sage: C = S.chain_complex(augmented=True,cochain=True) sage: D = T.chain_complex(augmented=True,cochain=True) sage: Hom(C,D) # indirect doctest - Set of Morphisms from Chain complex with at most 4 nonzero terms over Integer Ring to Chain complex with at most 4 nonzero terms over Integer Ring in Category of chain complexes over Integer Ring + Set of Morphisms from Chain complex with at most 4 nonzero terms over + Integer Ring to Chain complex with at most 4 nonzero terms over Integer + Ring in Category of chain complexes over Integer Ring """ from sage.homology.chain_complex_homspace import ChainComplexHomspace return ChainComplexHomspace(self, other) From 7e075ed92f58f2c1cf03518e0facbf9ee48c8a78 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 7 Sep 2013 22:20:31 +0100 Subject: [PATCH 035/206] Fixed super categories and all doctests --- src/sage/categories/category_types.py | 2 +- src/sage/homology/cell_complex.py | 47 +++++-------------------- src/sage/homology/chain_complex.py | 19 ++++++---- src/sage/homology/simplicial_complex.py | 28 +++++---------- 4 files changed, 29 insertions(+), 67 deletions(-) diff --git a/src/sage/categories/category_types.py b/src/sage/categories/category_types.py index ced52b995b1..6d6df3274ae 100644 --- a/src/sage/categories/category_types.py +++ b/src/sage/categories/category_types.py @@ -448,7 +448,7 @@ def super_categories(self): EXAMPLES:: sage: ChainComplexes(Integers(9)).super_categories() - [Category of objects] + [Category of modules with basis over Ring of integers modulo 9] """ from sage.categories.all import Fields, FreeModules, VectorSpaces base_ring = self.base_ring() diff --git a/src/sage/homology/cell_complex.py b/src/sage/homology/cell_complex.py index ec4753f6c40..d736837a057 100644 --- a/src/sage/homology/cell_complex.py +++ b/src/sage/homology/cell_complex.py @@ -490,7 +490,7 @@ def homology(self, dim=None, **kwds): base_ring = kwds.get('base_ring', ZZ) cohomology = kwds.get('cohomology', False) - subcomplex = kwds.get('subcomplex', None) + subcomplex = kwds.pop('subcomplex', None) verbose = kwds.get('verbose', False) algorithm = kwds.get('algorithm', 'auto') @@ -514,13 +514,9 @@ def homology(self, dim=None, **kwds): H = None if isinstance(self, CubicalComplex): if have_chomp('homcubes'): - if 'subcomplex' in kwds: - del kwds['subcomplex'] H = homcubes(self, subcomplex, **kwds) elif isinstance(self, SimplicialComplex): if have_chomp('homsimpl'): - if 'subcomplex' in kwds: - del kwds['subcomplex'] H = homsimpl(self, subcomplex, **kwds) # now pick off the requested dimensions if H: @@ -539,45 +535,18 @@ def homology(self, dim=None, **kwds): # Derived classes can implement specialized algorithms using a # _homology_ method. See SimplicialComplex for one example. if hasattr(self, '_homology_'): - return self._homology_(dim, **kwds) + return self._homology_(dim, subcomplex=subcomplex, **kwds) C = self.chain_complex(cochain=cohomology, augmented=True, - dimensions=dims, **kwds) - if 'subcomplex' in kwds: - del kwds['subcomplex'] + dimensions=dims, subcomplex=subcomplex, **kwds) answer = C.homology(**kwds) - assert isinstance(answer, dict) if dim is None: - zero = HomologyGroup(0, base_ring) - return dict([d, answer.get(d, zero)] for d in range(self.dimension()+1)) + dim = range(self.dimension()+1) + zero = HomologyGroup(0, base_ring) + if isinstance(dim, (list, tuple)): + return dict([d, answer.get(d, zero)] for d in dim) else: - return answer[dim] - - - - # if cohomology: - # too_big = self.dimension() + 1 - # if (not ((isinstance(dim, (list, tuple)) and too_big in dim) - # or too_big == dim) - # and too_big in answer): - # del answer[too_big] - # if -2 in answer: - # del answer[-2] - # if -1 in answer: - # del answer[-1] - # for d in range(self.dimension() + 1): - # if d not in answer: - # answer[d] = HomologyGroup(0, base_ring) - - # if dim is not None: - # if isinstance(dim, (list, tuple)): - # temp = {} - # for n in dim: - # temp[n] = answer[n] - # answer = temp - # else: # just a single dimension - # answer = answer.get(dim, HomologyGroup(0, base_ring)) - return answer + return answer.get(dim, zero) def cohomology(self, dim=None, **kwds): r""" diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 159d972b3ac..1d40aed353c 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -378,6 +378,8 @@ def _repr_(self): sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) sage: C() Trivial chain + sage: C({0:vector([1, 2, 3])}) + Chain(0:(1, 2, 3)) sage: c = C({0:vector([1, 2, 3]), 1:vector([4, 5])}); c Chain with 2 nonzero terms over Integer Ring sage: c._repr_() @@ -386,6 +388,9 @@ def _repr_(self): n = len(self._vec) if n == 0: return 'Trivial chain' + elif n == 1: + deg, vec = self._vec.iteritems().next() + return 'Chain({0}:{1})'.format(deg, vec) else: return 'Chain with {0} nonzero terms over {1}'.format( n, self.parent().base_ring()) @@ -995,7 +1000,7 @@ def _homology_chomp(deg, base_ring, verbose, generators): sage: C._homology_chomp(None, GF(2), False, False) # optional - CHomP """ - H = homchain(self, **kwds) + H = homchain(self, base_ring=base_ring, verbose=verbose, generators=generators) if H is None: raise RuntimeError('ran CHomP, but no output') if deg is None: @@ -1115,13 +1120,13 @@ def homology(self, deg=None, **kwds): sage: T = simplicial_complexes.Torus() sage: C_t = T.chain_complex() sage: C_t.homology(base_ring=QQ, generators=True) - {0: [(Vector space of dimension 1 over Rational Field, (0, 0, 0, 0, 0, 0, 1))], + {0: [(Vector space of dimension 1 over Rational Field, Chain(0:(0, 0, 0, 0, 0, 0, 1)))], 1: [(Vector space of dimension 1 over Rational Field, - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, -1, 0, 1, 0)), + Chain(1:(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, -1, 0, 1, 0))), (Vector space of dimension 1 over Rational Field, - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, -1, -1))], + Chain(1:(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, -1, -1)))], 2: [(Vector space of dimension 1 over Rational Field, - (1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1))]} + Chain(2:(1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1)))]} """ from sage.interfaces.chomp import have_chomp, homchain @@ -1189,14 +1194,14 @@ def change_ring(X): if d_in.is_zero(): if generators: #Include the generators of the nullspace - return [(HomologyGroup(1, base_ring), gen) + return [(HomologyGroup(1, base_ring), self({deg:gen})) for gen in d_out.right_kernel().basis()] else: return HomologyGroup(d_out_nullity, base_ring) if generators: orders, gens = self._homology_generators_snf(d_in, d_out, d_out_rank) - answer = [(HomologyGroup(1, base_ring, [order]), gen) + answer = [(HomologyGroup(1, base_ring, [order]), self({deg:gen})) for order, gen in zip(orders, gens)] else: if base_ring.is_field(): diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py index 90fa13c4108..b965f8426ce 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/homology/simplicial_complex.py @@ -1920,26 +1920,14 @@ def _homology_(self, dim=None, **kwds): if 'subcomplex' in kwds: del kwds['subcomplex'] answer = C.homology(**kwds) - if isinstance(answer, dict): - if cohomology: - too_big = self.dimension() + 1 - if (not ((isinstance(dim, (list, tuple)) and too_big in dim) - or too_big == dim) - and too_big in answer): - del answer[too_big] - if -2 in answer: - del answer[-2] - if -1 in answer: - del answer[-1] - if dim is not None: - if isinstance(dim, (list, tuple)): - temp = {} - for n in dim: - temp[n] = answer[n] - answer = temp - else: # just a single dimension - answer = answer.get(dim, HomologyGroup(0, base_ring)) - return answer + + if dim is None: + dim = range(self.dimension()+1) + zero = HomologyGroup(0, base_ring) + if isinstance(dim, (list, tuple)): + return dict([d, answer.get(d, zero)] for d in dim) + else: + return answer.get(dim, zero) def add_face(self, face): """ From 8ec7ded27028f928c50b9d8372371e2e91143cf9 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 8 Sep 2013 12:40:15 +0100 Subject: [PATCH 036/206] fixed bug in is_boundary() --- src/sage/homology/chain_complex.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 1d40aed353c..71171f7bf8d 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -50,6 +50,7 @@ from sage.structure.parent import Parent from sage.structure.element import ModuleElement +from sage.structure.element import is_Vector from sage.misc.cachefunc import cached_method from sage.rings.integer_ring import ZZ @@ -479,10 +480,17 @@ def is_boundary(self): sage: c = C({0:vector([0, 1, 2]), 1:vector([3, 4])}) sage: c.is_boundary() False - """ + sage: z3 = C({1:(1, 0)}) + sage: z3.is_cycle() + True + sage: (2*z3).is_boundary() + False + sage: (3*z3).is_boundary() + True + """ chain_complex = self.parent() for d, v in self._vec.iteritems(): - d = chain_complex.differential(d - chain_complex.degree_of_differential()) + d = chain_complex.differential(d - chain_complex.degree_of_differential()).transpose() if v not in d.image(): return False return True @@ -623,6 +631,8 @@ def _element_constructor_(self, vectors, check=True): sage: D = ChainComplex({0: matrix(ZZ, 2, 2, [1,0,0,2])}) sage: D._element_constructor_(0) Trivial chain + sage: D({0:[2, 3]}) + Chain(0:(2, 3)) """ if not vectors: # special case: the zero chain return self.element_class(self, {}) @@ -630,6 +640,9 @@ def _element_constructor_(self, vectors, check=True): vectors = vectors._vec data = dict() for degree, vec in vectors.iteritems(): + if not is_Vector(vec): + vec = vector(self.base_ring(), vec) + vec.set_immutable() if check and vec.degree() != self.free_module_rank(degree): raise ValueError('vector dimension does not match module dimension') if vec.is_zero(): From 290721604dd5b076b1dcd472e7b6892762371324 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Tue, 10 Sep 2013 22:17:05 +0200 Subject: [PATCH 037/206] Fixed a missing assignment in DiscreteValueGroup. --- src/sage/rings/padics/discrete_value_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/discrete_value_group.py b/src/sage/rings/padics/discrete_value_group.py index 2b7edd749f9..0ad2cf5b630 100644 --- a/src/sage/rings/padics/discrete_value_group.py +++ b/src/sage/rings/padics/discrete_value_group.py @@ -61,7 +61,7 @@ def __classcall__(cls, generator, category=category): """ from sage.misc.functional import coerce - coerce(QQ, generator) + generator = coerce(QQ, generator) generator = generator.abs() return super(DiscreteValueGroup, cls).__classcall__(cls, generator, category) From 220d7eecb24639d9464d20a6cc480c1b988a1ef4 Mon Sep 17 00:00:00 2001 From: Marco Streng Date: Thu, 26 Sep 2013 19:34:12 +0200 Subject: [PATCH 038/206] Add base_extend method for hyperelliptic curves. --- .../hyperelliptic_generic.py | 19 +++++++++++++------ .../hyperelliptic_curves/jacobian_generic.py | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py index 635e549328a..10b75c9ae03 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py @@ -72,17 +72,22 @@ def __init__(self, PP, f, h=None, names=None, genus=None): def change_ring(self, R): """ - Returns this HyperEllipticCurve over a new base ring R. + Returns this HyperellipticCurve over a new base ring R. EXAMPLES:: - sage: R. = QQ['x'] - sage: H = HyperellipticCurve(x^3-10*x+9) + sage: R. = QQ[] + sage: H = HyperellipticCurve(x^5 - 10*x + 9) sage: K = Qp(3,5) - sage: J. = K.extension(x^30-3) + sage: L. = K.extension(x^30-3) sage: HK = H.change_ring(K) - sage: HJ = HK.change_ring(J); HJ - Hyperelliptic Curve over Eisenstein Extension of 3-adic Field with capped relative precision 5 in a defined by (1 + O(3^5))*x^30 + (O(3^6))*x^29 + (O(3^6))*x^28 + (O(3^6))*x^27 + (O(3^6))*x^26 + (O(3^6))*x^25 + (O(3^6))*x^24 + (O(3^6))*x^23 + (O(3^6))*x^22 + (O(3^6))*x^21 + (O(3^6))*x^20 + (O(3^6))*x^19 + (O(3^6))*x^18 + (O(3^6))*x^17 + (O(3^6))*x^16 + (O(3^6))*x^15 + (O(3^6))*x^14 + (O(3^6))*x^13 + (O(3^6))*x^12 + (O(3^6))*x^11 + (O(3^6))*x^10 + (O(3^6))*x^9 + (O(3^6))*x^8 + (O(3^6))*x^7 + (O(3^6))*x^6 + (O(3^6))*x^5 + (O(3^6))*x^4 + (O(3^6))*x^3 + (O(3^6))*x^2 + (O(3^6))*x + (2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + O(3^6)) defined by (1 + O(a^150))*y^2 = (1 + O(a^150))*x^3 + (2 + 2*a^30 + a^60 + 2*a^90 + 2*a^120 + O(a^150))*x + a^60 + O(a^210) + sage: HL = HK.change_ring(L); HL + Hyperelliptic Curve over Eisenstein Extension of 3-adic Field with capped relative precision 5 in a defined by (1 + O(3^5))*x^30 + (O(3^6))*x^29 + (O(3^6))*x^28 + (O(3^6))*x^27 + (O(3^6))*x^26 + (O(3^6))*x^25 + (O(3^6))*x^24 + (O(3^6))*x^23 + (O(3^6))*x^22 + (O(3^6))*x^21 + (O(3^6))*x^20 + (O(3^6))*x^19 + (O(3^6))*x^18 + (O(3^6))*x^17 + (O(3^6))*x^16 + (O(3^6))*x^15 + (O(3^6))*x^14 + (O(3^6))*x^13 + (O(3^6))*x^12 + (O(3^6))*x^11 + (O(3^6))*x^10 + (O(3^6))*x^9 + (O(3^6))*x^8 + (O(3^6))*x^7 + (O(3^6))*x^6 + (O(3^6))*x^5 + (O(3^6))*x^4 + (O(3^6))*x^3 + (O(3^6))*x^2 + (O(3^6))*x + (2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + O(3^6)) defined by (1 + O(a^150))*y^2 = (1 + O(a^150))*x^5 + (2 + 2*a^30 + a^60 + 2*a^90 + 2*a^120 + O(a^150))*x + a^60 + O(a^210) + + sage: R. = FiniteField(7)[] + sage: H = HyperellipticCurve(x^8 + x + 5) + sage: H.base_extend(FiniteField(7^2, 'a')) + Hyperelliptic Curve over Finite Field in a of size 7^2 defined by y^2 = x^8 + x + 5 """ from constructor import HyperellipticCurve f, h = self._hyperelliptic_polynomials @@ -90,6 +95,8 @@ def change_ring(self, R): x = self._printing_ring.base_ring().variable_name() return HyperellipticCurve(f.change_ring(R), h, "%s,%s"%(x,y)) + base_extend = change_ring + def _repr_(self): """ String representation of hyperelliptic curves. diff --git a/src/sage/schemes/hyperelliptic_curves/jacobian_generic.py b/src/sage/schemes/hyperelliptic_curves/jacobian_generic.py index 3a3a3ffbaed..17541921459 100644 --- a/src/sage/schemes/hyperelliptic_curves/jacobian_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/jacobian_generic.py @@ -53,7 +53,7 @@ class HyperellipticJacobian_generic(Jacobian_generic): Set of rational points of Hyperelliptic Curve over Rational Field defined by v^2 + u*v = u^5 - u + 1 sage: K. = NumberField(x^2-2) sage: C(K) - Set of rational points of Closed subscheme of Projective Space of dimension 2 over Number Field in t with defining polynomial x^2 - 2 defined by: -x0^5 + x0*x1*x2^3 + x1^2*x2^3 + x0*x2^4 - x2^5 + Set of rational points of Hyperelliptic Curve over Number Field in t with defining polynomial x^2 - 2 defined by v^2 + u*v = u^5 - u + 1 sage: P = C(QQ)(0,1,1); P (0 : 1 : 1) sage: P == C(0,1,1) From d66b7d10231d055a4dda0b11c9ee3dfdb2a9f79f Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 14 Aug 2013 15:17:52 +0200 Subject: [PATCH 039/206] Meredith Graph constructor Conflicts: src/sage/graphs/graph_generators.py --- src/sage/graphs/generators/smallgraphs.py | 53 +++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 4 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 146d45c2574..1be40c38f1f 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -1270,6 +1270,59 @@ def DoubleStarSnark(): return g +def MeredithGraph(): + r""" + Returns the Meredith Graph + + The Meredith Graph is a 4-regular 4-connected non-hamiltonian graph. For + more information on the Meredith Graph, see the :wikipedia:`Meredith_graph`. + + EXAMPLES:: + + sage: g = graphs.MeredithGraph() + sage: g.is_regular(4) + True + sage: g.order() + 70 + sage: g.size() + 140 + sage: g.radius() + 7 + sage: g.diameter() + 8 + sage: g.girth() + 4 + sage: g.chromatic_number() + 3 + sage: g.is_hamiltonian() # long time + False + """ + g = Graph(name="Meredith Graph") + g.add_vertex(0) + + # Edges between copies of K_{4,3} + for i in range(5): + g.add_edge(('outer',i,3),('outer',(i+1)%5,0)) + g.add_edge(('inner',i,3),('inner',(i+2)%5,0)) + g.add_edge(('outer',i,1),('inner',i ,1)) + g.add_edge(('outer',i,2),('inner',i ,2)) + + # Edges inside of the K_{4,3}s. + for i in range(5): + for j in range(4): + for k in range(3): + g.add_edge(('inner',i,j),('inner',i,k+4)) + g.add_edge(('outer',i,j),('outer',i,k+4)) + + _circle_embedding(g, sum([[('outer',i,j) for j in range(4)]+10*[0] for i in range(5)],[]), radius = 1, shift = 2) + _circle_embedding(g, sum([[('outer',i,j) for j in range(4,7)]+10*[0] for i in range(5)],[]), radius = 1.2, shift = 2.2) + _circle_embedding(g, sum([[('inner',i,j) for j in range(4)]+7*[0] for i in range(5)],[]), radius = .6, shift = 1.24) + _circle_embedding(g, sum([[('inner',i,j) for j in range(4,7)]+5*[0] for i in range(5)],[]), radius = .4, shift = 1.05) + + g.delete_vertex(0) + g.relabel() + return g + def CameronGraph(): r""" Returns the Cameron graph. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index b1b9ab06942..db884686a77 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -120,9 +120,10 @@ def __append_to_doc(methods): "HoltGraph", "KrackhardtKiteGraph", "LjubljanaGraph", + "M22Graph", "McGeeGraph", "McLaughlinGraph", - "M22Graph", + "MeredithGraph", "MoebiusKantorGraph", "MoserSpindle", "NauruGraph", @@ -1267,6 +1268,7 @@ def fusenes(self, hexagon_count, benzenoids=False): LjubljanaGraph = staticmethod(sage.graphs.generators.smallgraphs.LjubljanaGraph) McGeeGraph = staticmethod(sage.graphs.generators.smallgraphs.McGeeGraph) McLaughlinGraph = staticmethod(sage.graphs.generators.smallgraphs.McLaughlinGraph) + MeredithGraph = staticmethod(sage.graphs.generators.smallgraphs.MeredithGraph) M22Graph = staticmethod(sage.graphs.generators.smallgraphs.M22Graph) MoebiusKantorGraph = staticmethod(sage.graphs.generators.smallgraphs.MoebiusKantorGraph) MoserSpindle = staticmethod(sage.graphs.generators.smallgraphs.MoserSpindle) From 3a63295451c4812fa89f30c936e046aaadc78a18 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 15 Aug 2013 11:57:03 +0200 Subject: [PATCH 040/206] Horton Graph constructor --- src/sage/graphs/generators/smallgraphs.py | 62 +++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 2 + 2 files changed, 64 insertions(+) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 1be40c38f1f..d53657f25c1 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -1668,6 +1668,68 @@ def DyckGraph(): return Graph(edge_dict, pos=pos_dict, name="Dyck graph") +def HortonGraph(): + r""" + Returns the Horton Graph. + + The Horton graph is a cubic 3-connected non-hamiltonian graph. For more + information, see the :wikipedia:`Horton_Graph`. + + EXAMPLES:: + + sage: g = graphs.HortonGraph() + sage: g.order() + 96 + sage: g.size() + 144 + sage: g.radius() + 10 + sage: g.diameter() + 10 + sage: g.girth() + 6 + sage: g.automorphism_group().cardinality() + 96 + sage: g.chromatic_number() + 2 + sage: g.is_hamiltonian() # not tested -- veeeery long + False + """ + g = Graph(name = "Horton Graph") + + # Each group of the 6 groups of vertices is based on the same 3-regular + # graph. + from sage.graphs.generators.families import LCFGraph + lcf = LCFGraph(16,[5,-5],8) + lcf.delete_edge(15,0) + lcf.delete_edge(7,8) + + for i in range(6): + for u,v in lcf.edges(labels=False): + g.add_edge((i,u),(i,v)) + + # Modifying the groups and linking them together + for i in range(3): + g.add_edge((2*i,0),(2*i+1,7)) + g.add_edge((2*i+1,8),(2*i,7)) + g.add_edge((2*i,15),(2*i+1,0)) + g.add_edge((2*i,8),1) + g.add_edge((2*i+1,14),2) + g.add_edge((2*i+1,10),0) + + # Embedding + for i in range(6): + _circle_embedding(g, [(i,j) for j in range(16)], center=(cos(2*i*pi/6),sin(2*i*pi/6)), radius=.3) + + for i in range(3): + g.delete_vertex((2*i+1,15)) + + _circle_embedding(g, range(3), radius=.2, shift=-0.75) + + g.relabel() + + return g + def EllinghamHorton54Graph(): r""" Returns the Ellingham-Horton 54-graph. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index db884686a77..243669545f2 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -118,6 +118,7 @@ def __append_to_doc(methods): "HoffmanGraph", "HoffmanSingletonGraph", "HoltGraph", + "HortonGraph", "KrackhardtKiteGraph", "LjubljanaGraph", "M22Graph", @@ -1264,6 +1265,7 @@ def fusenes(self, hexagon_count, benzenoids=False): HoffmanGraph = staticmethod(sage.graphs.generators.smallgraphs.HoffmanGraph) HoffmanSingletonGraph = staticmethod(sage.graphs.generators.smallgraphs.HoffmanSingletonGraph) HoltGraph = staticmethod(sage.graphs.generators.smallgraphs.HoltGraph) + HortonGraph = staticmethod(sage.graphs.generators.smallgraphs.HortonGraph) KrackhardtKiteGraph = staticmethod(sage.graphs.generators.smallgraphs.KrackhardtKiteGraph) LjubljanaGraph = staticmethod(sage.graphs.generators.smallgraphs.LjubljanaGraph) McGeeGraph = staticmethod(sage.graphs.generators.smallgraphs.McGeeGraph) From d63d5a24d8cc4612dab345f45d5910ff33cb6dbe Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 15 Aug 2013 18:19:08 +0200 Subject: [PATCH 041/206] Kittell Graph constructor --- src/sage/graphs/generators/smallgraphs.py | 47 +++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 2 + 2 files changed, 49 insertions(+) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index d53657f25c1..c396e30f62c 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -1323,6 +1323,53 @@ def MeredithGraph(): g.relabel() return g +def KittellGraph(): + r""" + Returns the Kittell Graph. + + For more information on the Kittell Graph, see the `corresponding Wolfram + page `_. + + EXAMPLES:: + + sage: g = graphs.KittellGraph() + sage: g.order() + 23 + sage: g.size() + 63 + sage: g.radius() + 3 + sage: g.diameter() + 4 + sage: g.girth() + 3 + sage: g.chromatic_number() + 4 + """ + g = Graph({0: [1, 2, 4, 5, 6, 7], 1: [0, 2, 7, 10, 11, 13], + 2: [0, 1, 11, 4, 14], 3: [16, 12, 4, 5, 14], 4: [0, 2, 3, 5, 14], + 5: [0, 16, 3, 4, 6], 6: [0, 5, 7, 15, 16, 17, 18], + 7: [0, 1, 6, 8, 13, 18], 8: [9, 18, 19, 13, 7], + 9: [8, 10, 19, 20, 13], 10: [1, 9, 11, 13, 20, 21], + 11: [1, 2, 10, 12, 14, 15, 21], 12: [11, 16, 3, 14, 15], + 13: [8, 1, 10, 9, 7], 14: [11, 12, 2, 3, 4], + 15: [6, 11, 12, 16, 17, 21, 22], + 16: [3, 12, 5, 6, 15], 17: [18, 19, 22, 6, 15], + 18: [8, 17, 19, 6, 7], 19: [8, 9, 17, 18, 20, 22], + 20: [9, 10, 19, 21, 22], 21: [10, 11, 20, 22, 15], + 22: [17, 19, 20, 21, 15]}, + name = "Kittell Graph") + + _circle_embedding(g, range(3), shift=.75) + _circle_embedding(g, range(3,13), radius = .4) + _circle_embedding(g, range(15,22), radius = .2, shift=-.15) + pos = g.get_pos() + pos[13] = (-.65,-.35) + pos[14] = (.65,-.35) + pos[22] = (0,0) + + return g + def CameronGraph(): r""" Returns the Cameron graph. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 243669545f2..b1376aeb213 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -119,6 +119,7 @@ def __append_to_doc(methods): "HoffmanSingletonGraph", "HoltGraph", "HortonGraph", + "KittellGraph", "KrackhardtKiteGraph", "LjubljanaGraph", "M22Graph", @@ -1266,6 +1267,7 @@ def fusenes(self, hexagon_count, benzenoids=False): HoffmanSingletonGraph = staticmethod(sage.graphs.generators.smallgraphs.HoffmanSingletonGraph) HoltGraph = staticmethod(sage.graphs.generators.smallgraphs.HoltGraph) HortonGraph = staticmethod(sage.graphs.generators.smallgraphs.HortonGraph) + KittellGraph = staticmethod(sage.graphs.generators.smallgraphs.KittellGraph) KrackhardtKiteGraph = staticmethod(sage.graphs.generators.smallgraphs.KrackhardtKiteGraph) LjubljanaGraph = staticmethod(sage.graphs.generators.smallgraphs.LjubljanaGraph) McGeeGraph = staticmethod(sage.graphs.generators.smallgraphs.McGeeGraph) From 4ed1802e3eab0326a8bd218a72fa71a1cf521c44 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 15 Aug 2013 20:03:02 +0200 Subject: [PATCH 042/206] =?UTF-8?q?Markstr=C3=B6m=20Graph=20constructor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/graphs/generators/smallgraphs.py | 46 +++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 4 +- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index c396e30f62c..fffa6304d78 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -3056,6 +3056,52 @@ def M22Graph(): return g +def MarkstroemGraph(): + r""" + Returns the Markström Graph. + + The Markström Graph is a cubic planar graph with no cycles of length 4 nor + 8, but containing cycles of length 16. For more information on the Markström + Graph, see the `corresponding Wolfram page + `_. + + EXAMPLES:: + + sage: g = graphs.MarkstroemGraph() + sage: g.order() + 24 + sage: g.size() + 36 + sage: g.is_planar() + True + sage: g.is_regular(3) + True + sage: g.subgraph_search(graphs.CycleGraph(4)) is None + True + sage: g.subgraph_search(graphs.CycleGraph(8)) is None + True + sage: g.subgraph_search(graphs.CycleGraph(16)) + Subgraph of (Markstroem Graph): Graph on 16 vertices + """ + g = Graph(name="Markstroem Graph") + + g.add_cycle(range(9)) + g.add_path([0,9,10,11,2,1,11]) + g.add_path([3,12,13,14,5,4,14]) + g.add_path([6,15,16,17,8,7,17]) + g.add_cycle([10,9,18]) + g.add_cycle([12,13,19]) + g.add_cycle([15,16,20]) + g.add_cycle([21,22,23]) + g.add_edges([(19,22),(18,21),(20,23)]) + + _circle_embedding(g, sum([[9+3*i+j for j in range(3)]+[0]*2 for i in range(3)],[]), radius=.6, shift=.7) + _circle_embedding(g, [18,19,20], radius=.35, shift=.25) + _circle_embedding(g, [21,22,23], radius=.15, shift=.25) + _circle_embedding(g, range(9)) + + return g + def McGeeGraph(embedding=2): r""" Returns the McGee Graph. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index b1376aeb213..28ff401afb3 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -123,6 +123,7 @@ def __append_to_doc(methods): "KrackhardtKiteGraph", "LjubljanaGraph", "M22Graph", + "Markstroem", "McGeeGraph", "McLaughlinGraph", "MeredithGraph", @@ -1270,10 +1271,11 @@ def fusenes(self, hexagon_count, benzenoids=False): KittellGraph = staticmethod(sage.graphs.generators.smallgraphs.KittellGraph) KrackhardtKiteGraph = staticmethod(sage.graphs.generators.smallgraphs.KrackhardtKiteGraph) LjubljanaGraph = staticmethod(sage.graphs.generators.smallgraphs.LjubljanaGraph) + M22Graph = staticmethod(sage.graphs.generators.smallgraphs.M22Graph) + MarkstroemGraph = staticmethod(sage.graphs.generators.smallgraphs.MarkstroemGraph) McGeeGraph = staticmethod(sage.graphs.generators.smallgraphs.McGeeGraph) McLaughlinGraph = staticmethod(sage.graphs.generators.smallgraphs.McLaughlinGraph) MeredithGraph = staticmethod(sage.graphs.generators.smallgraphs.MeredithGraph) - M22Graph = staticmethod(sage.graphs.generators.smallgraphs.M22Graph) MoebiusKantorGraph = staticmethod(sage.graphs.generators.smallgraphs.MoebiusKantorGraph) MoserSpindle = staticmethod(sage.graphs.generators.smallgraphs.MoserSpindle) NauruGraph = staticmethod(sage.graphs.generators.smallgraphs.NauruGraph) From a42ac156b6800fc8fe9a5af6d730f19c89892b04 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 15 Aug 2013 20:37:43 +0200 Subject: [PATCH 043/206] Sousselier Graph constructor --- src/sage/graphs/generators/smallgraphs.py | 40 +++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 2 ++ 2 files changed, 42 insertions(+) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index fffa6304d78..ceb9356eb74 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -3665,6 +3665,46 @@ def SimsGewirtzGraph(): g.name("Sims-Gewirtz Graph") return g +def SousselierGraph(): + r""" + Returns the Sousselier Graph. + + The Sousselier graph is a hypohamiltonian graph on 16 vertices and 27 + edges. For more information, see the corresponding `Wikpedia page (in + French) `_. + + EXAMPLES:: + + sage: g = graphs.SousselierGraph() + sage: g.order() + 16 + sage: g.size() + 27 + sage: g.radius() + 2 + sage: g.diameter() + 3 + sage: g.automorphism_group().cardinality() + 2 + sage: g.is_hamiltonian() + False + sage: g.delete_vertex(g.random_vertex()) + sage: g.is_hamiltonian() + True + """ + g = Graph(name="Sousselier Graph") + + g.add_cycle(range(15)) + g.add_path([12,8,3,14]) + g.add_path([9,5,0,11]) + g.add_edge(6,2) + g.add_edges([(15,i) for i in range(15) if i%3==1]) + + _circle_embedding(g, range(15), shift=-.25) + g.get_pos()[15] = (0,0) + + return g + def ThomsenGraph(): """ Returns the Thomsen Graph. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 28ff401afb3..a522ca91cfd 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -136,6 +136,7 @@ def __append_to_doc(methods): "SchlaefliGraph", "ShrikhandeGraph", "SimsGewirtzGraph", + "Sousselier", "SylvesterGraph", "ThomsenGraph", "Tutte12Cage", @@ -1285,6 +1286,7 @@ def fusenes(self, hexagon_count, benzenoids=False): SchlaefliGraph = staticmethod(sage.graphs.generators.smallgraphs.SchlaefliGraph) ShrikhandeGraph = staticmethod(sage.graphs.generators.smallgraphs.ShrikhandeGraph) SimsGewirtzGraph = staticmethod(sage.graphs.generators.smallgraphs.SimsGewirtzGraph) + SousselierGraph = staticmethod(sage.graphs.generators.smallgraphs.SousselierGraph) SylvesterGraph = staticmethod(sage.graphs.generators.smallgraphs.SylvesterGraph) ThomsenGraph = staticmethod(sage.graphs.generators.smallgraphs.ThomsenGraph) Tutte12Cage = staticmethod(sage.graphs.generators.smallgraphs.Tutte12Cage) From 686d7762f7f78edcba50150c69e2f1b8ed09a5fa Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 15 Aug 2013 21:22:27 +0200 Subject: [PATCH 044/206] Poussin Graph constructor --- src/sage/graphs/generators/smallgraphs.py | 29 +++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 2 ++ 2 files changed, 31 insertions(+) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index ceb9356eb74..af3b31288a2 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -3387,6 +3387,35 @@ def PappusGraph(): 10:[15,17],11:[12,16],12:[15],13:[16],14:[17]},\ pos=pos_dict, name="Pappus Graph") +def PoussinGraph(): + r""" + Returns the Poussin Graph. + + For more information on the Poussin Graph, see its corresponding `Wolfram + page `_. + + EXAMPLES:: + + sage: g = graphs.PoussinGraph() + sage: g.order() + 15 + sage: g.is_planar() + True + """ + g = Graph({2:[7,8,3,4],1:[7,6],0:[6,5,4],3:[5]},name="Poussin Graph") + + g.add_cycle(range(3)) + g.add_cycle(range(3,9)) + g.add_cycle(range(9,14)) + g.add_path([8,12,7,11,6,10,5,9,3,13,8,12]) + g.add_edges([(14,i) for i in range(9,14)]) + _circle_embedding(g, range(3), shift=.75) + _circle_embedding(g, range(3,9), radius=.4, shift=0) + _circle_embedding(g, range(9,14), radius=.2, shift=.4) + g.get_pos()[14] = (0,0) + + return g + def PetersenGraph(): """ The Petersen Graph is a named graph that consists of 10 vertices diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index a522ca91cfd..b12d918cadd 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -131,6 +131,7 @@ def __append_to_doc(methods): "MoserSpindle", "NauruGraph", "PappusGraph", + "PoussinGraph", "PetersenGraph", "RobertsonGraph", "SchlaefliGraph", @@ -1281,6 +1282,7 @@ def fusenes(self, hexagon_count, benzenoids=False): MoserSpindle = staticmethod(sage.graphs.generators.smallgraphs.MoserSpindle) NauruGraph = staticmethod(sage.graphs.generators.smallgraphs.NauruGraph) PappusGraph = staticmethod(sage.graphs.generators.smallgraphs.PappusGraph) + PoussinGraph = staticmethod(sage.graphs.generators.smallgraphs.PoussinGraph) PetersenGraph = staticmethod(sage.graphs.generators.smallgraphs.PetersenGraph) RobertsonGraph = staticmethod(sage.graphs.generators.smallgraphs.RobertsonGraph) SchlaefliGraph = staticmethod(sage.graphs.generators.smallgraphs.SchlaefliGraph) From 706c06faf504323ac5ef0d92307e9cea9858a0bb Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 16 Aug 2013 11:37:26 +0200 Subject: [PATCH 045/206] Tutte Graph constructor --- src/sage/graphs/generators/smallgraphs.py | 61 +++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 2 + 2 files changed, 63 insertions(+) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index af3b31288a2..755414ba40d 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -3848,6 +3848,67 @@ def TutteCoxeterGraph(embedding=2): else: raise ValueError("The value of embedding must be 1 or 2.") +def TutteGraph(): + r""" + Returns the Tutte Graph. + + The Tutte graph is a 3-regular, 3-connected, and planar non-hamiltonian + graph. For more information on the Tutte Graph, see the + :wikipedia:`Tutte_graph`. + + EXAMPLES:: + + sage: g = graphs.TutteGraph() + sage: g.order() + 46 + sage: g.size() + 69 + sage: g.is_planar() + True + sage: g.vertex_connectivity() # long + 3 + sage: g.girth() + 4 + sage: g.automorphism_group().cardinality() + 3 + sage: g.is_hamiltonian() + False + """ + g = Graph(name="Tutte Graph") + from sage.graphs.graph_plot import _circle_embedding + + g.add_cycle([(i,j) for i in range(3) for j in range(3) ]) + for i in range(3): + g.add_cycle([(i,j) for j in range(9)]) + g.add_cycle([(i,j) for j in range(9,14)]) + g.add_edge((i,5),0) + g.add_edge((i,13),(i,3)) + g.add_edge((i,12),(i,1)) + g.add_edge((i,11),(i,8)) + g.add_edge((i,10),(i,7)) + g.add_edge((i,6),(i,14)) + g.add_edge((i,4),(i,14)) + g.add_edge((i,9),(i,14)) + + _circle_embedding(g, [(i,j) for i in range(3) for j in range(6)], shift=.5) + _circle_embedding(g, [(i,14) for i in range(3) ], radius=.3,shift=.25) + + for i in range(3): + _circle_embedding(g, [(i,j) for j in range(3,9)]+[0]*5, + shift=3.7*(i-2)+.75, + radius=.4, + center=(.6*cos(2*(i+.25)*pi/3),.6*sin(2*(i+.25)*pi/3))) + _circle_embedding(g, [(i,j) for j in range(9,14)], + shift=1.7*(i-2)+1, + radius=.2, + center=(.6*cos(2*(i+.25)*pi/3),.6*sin(2*(i+.25)*pi/3))) + + g.get_pos()[0] = (0,0) + + return g + + + def WagnerGraph(): """ Returns the Wagner Graph. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index b12d918cadd..69d25e4a88b 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -142,6 +142,7 @@ def __append_to_doc(methods): "ThomsenGraph", "Tutte12Cage", "TutteCoxeterGraph", + "TutteGraph", "WagnerGraph", "WellsGraph"]) @@ -1293,6 +1294,7 @@ def fusenes(self, hexagon_count, benzenoids=False): ThomsenGraph = staticmethod(sage.graphs.generators.smallgraphs.ThomsenGraph) Tutte12Cage = staticmethod(sage.graphs.generators.smallgraphs.Tutte12Cage) TutteCoxeterGraph = staticmethod(sage.graphs.generators.smallgraphs.TutteCoxeterGraph) + TutteGraph = staticmethod(sage.graphs.generators.smallgraphs.TutteGraph) WagnerGraph = staticmethod(sage.graphs.generators.smallgraphs.WagnerGraph) ########################################################################### From 80ee6475a952928ceaf333e8fd7fa862eb12a7f9 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 17 Aug 2013 13:35:05 +0200 Subject: [PATCH 046/206] Wiener-Araya Graph constructor --- src/sage/graphs/generators/smallgraphs.py | 63 ++++++++++++++++++++++- src/sage/graphs/graph_generators.py | 4 +- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 755414ba40d..80c3a9b3de3 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -3907,8 +3907,6 @@ def TutteGraph(): return g - - def WagnerGraph(): """ Returns the Wagner Graph. @@ -3934,3 +3932,64 @@ def WagnerGraph(): g.name("Wagner Graph") return g +def WienerArayaGraph(): + r""" + Returns the Wiener-Araya Graph. + + The Wiener-Araya Graph is a planar hypohamiltonian graph on 42 vertices and + 67 edges. For more information on the Wiener-Araya Graph, see its + corresponding `Wolfram Page + `_ or its `(french) + Wikipedia page `_. + + EXAMPLES:: + + sage: g = graphs.WienerArayaGraph() + sage: g.order() + 42 + sage: g.size() + 67 + sage: g.girth() + 4 + sage: g.is_planar() + True + sage: g.is_hamiltonian() # not tested -- around 30s long + False + sage: g.delete_vertex(g.random_vertex()) + sage: g.is_hamiltonian() + True + """ + g = Graph(name="Wiener-Araya Graph") + from sage.graphs.graph_plot import _circle_embedding + + g.add_cycle([(0,i) for i in range(4)]) + g.add_cycle([(1,i) for i in range(12)]) + g.add_cycle([(2,i) for i in range(20)]) + g.add_cycle([(3,i) for i in range(6)]) + _circle_embedding(g, [(0,i) for i in range(4)], shift=.5) + _circle_embedding(g, + sum([[(1,3*i),(1,3*i+1)]+[0]*3+[(1,3*i+2)]+[0]*3 for i in range(4)],[]), + shift=4, + radius=.65) + _circle_embedding(g, [(2,i) for i in range(20)], radius=.5) + _circle_embedding(g, [(3,i) for i in range(6)], radius=.3, shift=.5) + + for i in range(4): + g.delete_edge((1,3*i),(1,3*i+1)) + g.add_edge((1,3*i),(0,i)) + g.add_edge((1,3*i+1),(0,i)) + g.add_edge((2,5*i+2),(1,3*i)) + g.add_edge((2,5*i+3),(1,3*i+1)) + g.add_edge((2,(5*i+5)%20),(1,3*i+2)) + g.add_edge((2,(5*i+1)%20),(3,i+(i>=1)+(i>=3))) + g.add_edge((2,(5*i+4)%20),(3,i+(i>=1)+(i>=3))) + + g.delete_edge((3,1),(3,0)) + g.add_edge((3,1),(2,4)) + g.delete_edge((3,4),(3,3)) + g.add_edge((3,4),(2,14)) + g.add_edge((3,1),(3,4)) + + g.get_pos().pop(0) + g.relabel() + return g diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 69d25e4a88b..abb7c3ff839 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -144,7 +144,8 @@ def __append_to_doc(methods): "TutteCoxeterGraph", "TutteGraph", "WagnerGraph", - "WellsGraph"]) + "WellsGraph", + "WienerArayaGraph"]) __doc__ += """ *Platonic solids* (ordered ascending by number of vertices) @@ -1296,6 +1297,7 @@ def fusenes(self, hexagon_count, benzenoids=False): TutteCoxeterGraph = staticmethod(sage.graphs.generators.smallgraphs.TutteCoxeterGraph) TutteGraph = staticmethod(sage.graphs.generators.smallgraphs.TutteGraph) WagnerGraph = staticmethod(sage.graphs.generators.smallgraphs.WagnerGraph) + WienerArayaGraph = staticmethod(sage.graphs.generators.smallgraphs.WienerArayaGraph) ########################################################################### # Platonic Solids From 3025ccaaf3bb5ffef92298d4d9fc175287cd9d61 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 27 Aug 2013 12:14:14 +0200 Subject: [PATCH 047/206] Two broken links in the documentation of graph generators --- src/sage/graphs/generators/smallgraphs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 80c3a9b3de3..8766eb72a53 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -1720,7 +1720,7 @@ def HortonGraph(): Returns the Horton Graph. The Horton graph is a cubic 3-connected non-hamiltonian graph. For more - information, see the :wikipedia:`Horton_Graph`. + information, see the :wikipedia:`Horton_graph`. EXAMPLES:: @@ -3699,8 +3699,8 @@ def SousselierGraph(): Returns the Sousselier Graph. The Sousselier graph is a hypohamiltonian graph on 16 vertices and 27 - edges. For more information, see the corresponding `Wikpedia page (in - French) `_. + edges. For more information, see the corresponding `Wikipedia page (in + French) `_. EXAMPLES:: From f6887027152ade3adfc8c19ede1d7c546f17425c Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 15 Aug 2013 21:37:49 +0200 Subject: [PATCH 048/206] Tietze Graph constructor --- src/sage/graphs/generators/smallgraphs.py | 31 +++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 2 ++ 2 files changed, 33 insertions(+) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 8766eb72a53..ed9712ac2a3 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -3757,6 +3757,37 @@ def ThomsenGraph(): G = networkx.complete_bipartite_graph(3,3) return Graph(G, pos=pos_dict, name="Thomsen graph") +def TietzeGraph(): + r""" + Returns the Tietze Graph. + + For more information on the Tietze Graph, see the + :wikipedia:`Tietze's_graph`. + + EXAMPLES:: + + sage: g = graphs.TietzeGraph() + sage: g.order() + 12 + sage: g.size() + 18 + sage: g.diameter() + 3 + sage: g.girth() + 3 + sage: g.automorphism_group().cardinality() + 12 + sage: g.automorphism_group().is_isomorphic(groups.permutation.Dihedral(6)) + True + """ + g = Graph([(0,9),(3,10),(6,11),(1,5),(2,7),(4,8),(7,2)], name="Tietze Graph") + g.add_cycle(range(9)) + g.add_cycle([9,10,11]) + _circle_embedding(g,range(9)) + _circle_embedding(g,[9,10,11],radius=.5) + + return g + def Tutte12Cage(): r""" Returns Tutte's 12-Cage. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index abb7c3ff839..58d0d48ab1e 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -140,6 +140,7 @@ def __append_to_doc(methods): "Sousselier", "SylvesterGraph", "ThomsenGraph", + "TietzeGraph", "Tutte12Cage", "TutteCoxeterGraph", "TutteGraph", @@ -1293,6 +1294,7 @@ def fusenes(self, hexagon_count, benzenoids=False): SousselierGraph = staticmethod(sage.graphs.generators.smallgraphs.SousselierGraph) SylvesterGraph = staticmethod(sage.graphs.generators.smallgraphs.SylvesterGraph) ThomsenGraph = staticmethod(sage.graphs.generators.smallgraphs.ThomsenGraph) + TietzeGraph = staticmethod(sage.graphs.generators.smallgraphs.TietzeGraph) Tutte12Cage = staticmethod(sage.graphs.generators.smallgraphs.Tutte12Cage) TutteCoxeterGraph = staticmethod(sage.graphs.generators.smallgraphs.TutteCoxeterGraph) TutteGraph = staticmethod(sage.graphs.generators.smallgraphs.TutteGraph) From dc00a5f269efeb6126baa7300818f70af82305e1 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 17 Aug 2013 14:32:18 +0200 Subject: [PATCH 049/206] Szekeres Snark Graph constructor --- src/sage/graphs/generators/smallgraphs.py | 45 ++++++++++++++++++++++- src/sage/graphs/graph_generators.py | 2 + 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index ed9712ac2a3..780e2b1e056 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -3734,12 +3734,53 @@ def SousselierGraph(): return g +def SzekeresSnarkGraph(): + r""" + Returns the Szekeres Snark Graph. + + The Szekeres graph is a snark with 50 vertices and 75 edges. For more + information on this graph, see the :wikipedia:`Szekeres_snark`. + + EXAMPLES:: + + sage: g = graphs.SzekeresSnarkGraph() + sage: g.order() + 50 + sage: g.size() + 75 + sage: g.chromatic_number() + 3 + """ + g = Graph(name="Szekeres Snark Graph") + + for i in range(5): + g.add_cycle([(i,j) for j in range(9)]) + g.delete_edge((i,0),(i,8)) + g.add_edge((i,1),i) + g.add_edge((i,4),i) + g.add_edge((i,7),i) + g.add_edge((i,0),(i,5)) + g.add_edge((i,8),(i,3)) + + g.add_edge((i,0),((i+1)%5,8)) + g.add_edge((i,6),((i+2)%5,2)) + _circle_embedding(g, [(i,j) for j in range(9)], + radius=.3, + center=(cos(2*(i+.25)*pi/5),sin(2*(i+.25)*pi/5)), + shift=5.45+1.8*i) + + _circle_embedding(g, range(5), radius=1, shift=.25) + + g.relabel() + return g + + def ThomsenGraph(): """ Returns the Thomsen Graph. - The Thomsen Graph is actually a complete bipartite graph with (n1, - n2) = (3, 3). It is also called the Utility graph. + The Thomsen Graph is actually a complete bipartite graph with `(n1, n2) = + (3, 3)`. It is also called the Utility graph. PLOTTING: See CompleteBipartiteGraph. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 58d0d48ab1e..728b5aa0352 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -139,6 +139,7 @@ def __append_to_doc(methods): "SimsGewirtzGraph", "Sousselier", "SylvesterGraph", + "SzekeresSnarkGraph", "ThomsenGraph", "TietzeGraph", "Tutte12Cage", @@ -1293,6 +1294,7 @@ def fusenes(self, hexagon_count, benzenoids=False): SimsGewirtzGraph = staticmethod(sage.graphs.generators.smallgraphs.SimsGewirtzGraph) SousselierGraph = staticmethod(sage.graphs.generators.smallgraphs.SousselierGraph) SylvesterGraph = staticmethod(sage.graphs.generators.smallgraphs.SylvesterGraph) + SzekeresSnarkGraph = staticmethod(sage.graphs.generators.smallgraphs.SzekeresSnarkGraph) ThomsenGraph = staticmethod(sage.graphs.generators.smallgraphs.ThomsenGraph) TietzeGraph = staticmethod(sage.graphs.generators.smallgraphs.TietzeGraph) Tutte12Cage = staticmethod(sage.graphs.generators.smallgraphs.Tutte12Cage) From 72fe3a86f3b76200984fb59c98c8590cb80cc16c Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 17 Aug 2013 14:54:09 +0200 Subject: [PATCH 050/206] Watkins Snark graph constructor --- src/sage/graphs/generators/smallgraphs.py | 36 +++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 2 ++ 2 files changed, 38 insertions(+) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 780e2b1e056..9e579ef5128 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -4004,6 +4004,42 @@ def WagnerGraph(): g.name("Wagner Graph") return g +def WatkinsSnarkGraph(): + r""" + Returns the Watkins Snark Graph. + + The Watkins Graph is a snark with 50 vertices and 75 edges. For more + information, see the :wikipedia:`Watkins_snark`. + + EXAMPLES:: + + sage: g = graphs.WatkinsSnarkGraph() + sage: g.order() + 50 + sage: g.size() + 75 + sage: g.chromatic_number() + 3 + """ + g = Graph(name="Szekeres Snark Graph") + + for i in range(5): + g.add_cycle([(i,j) for j in range(9)]) + _circle_embedding(g, + [(i,j) for j in range(4)]+[0]*2+[(i,4)]+[0]*2+[(i,j) for j in range(5,9)], + radius=.3, + center=(cos(2*(i+.25)*pi/5),sin(2*(i+.25)*pi/5)), + shift=2.7*i+7.55) + g.add_edge((i,5),((i+1)%5,0)) + g.add_edge((i,8),((i+2)%5,3)) + g.add_edge((i,1),i) + g.add_edge((i,7),i) + g.add_edge((i,4),i) + g.add_edge((i,6),(i,2)) + + _circle_embedding(g, range(5), shift=.25, radius=1.1) + return g + def WienerArayaGraph(): r""" Returns the Wiener-Araya Graph. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 728b5aa0352..b4862a92b83 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -146,6 +146,7 @@ def __append_to_doc(methods): "TutteCoxeterGraph", "TutteGraph", "WagnerGraph", + "WatkinsSnarkGraph", "WellsGraph", "WienerArayaGraph"]) @@ -1301,6 +1302,7 @@ def fusenes(self, hexagon_count, benzenoids=False): TutteCoxeterGraph = staticmethod(sage.graphs.generators.smallgraphs.TutteCoxeterGraph) TutteGraph = staticmethod(sage.graphs.generators.smallgraphs.TutteGraph) WagnerGraph = staticmethod(sage.graphs.generators.smallgraphs.WagnerGraph) + WatkinsSnarkGraph = staticmethod(sage.graphs.generators.smallgraphs.WatkinsSnarkGraph) WienerArayaGraph = staticmethod(sage.graphs.generators.smallgraphs.WienerArayaGraph) ########################################################################### From 63d241d40b1f6a90dbb35edf627551e44403e914 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 17 Aug 2013 15:32:40 +0200 Subject: [PATCH 051/206] Blanusa Snarks graph constructors --- src/sage/graphs/generators/smallgraphs.py | 84 +++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 4 ++ 2 files changed, 88 insertions(+) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 9e579ef5128..4eb6d520df6 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -951,6 +951,90 @@ def BiggsSmithGraph(embedding=1): return g +def BlanusaFirstSnarkGraph(): + r""" + Returns the first Blanusa Snark Graph. + + The Blanusa graphs are two snarks on 18 vertices and 27 edges. For more + information on them, see the :wikipedia:`Blanusa_snarks`. + + EXAMPLES:: + + sage: g = graphs.BlanusaFirstSnarkGraph() + sage: g.order() + 18 + sage: g.size() + 27 + sage: g.diameter() + 4 + sage: g.girth() + 5 + sage: g.automorphism_group().cardinality() + 8 + """ + g = Graph({17:[4,7,1],0:[5], + 3:[8],13:[9],12:[16], + 10:[15],11:[6],14:[2]}, + name="Blanusa First Snark Graph") + + g.add_cycle(range(17)) + _circle_embedding(g, range(17), shift=0.25) + g.get_pos()[17] = (0,0) + return g + +def BlanusaSecondSnarkGraph(): + r""" + Returns the second Blanusa Snark Graph. + + The Blanusa graphs are two snarks on 18 vertices and 27 edges. For more + information on them, see the :wikipedia:`Blanusa_snarks`. + + EXAMPLES:: + + sage: g = graphs.BlanusaSecondSnarkGraph() + sage: g.order() + 18 + sage: g.size() + 27 + sage: g.diameter() + 4 + sage: g.girth() + 5 + sage: g.automorphism_group().cardinality() + 4 + """ + g = Graph({0:[(0,0),(1,4),1],1:[(0,3),(1,1)],(0,2):[(0,5)], + (0,6):[(0,4)],(0,7):[(0,1)],(1,7):[(1,2)], + (1,0):[(1,6)],(1,3):[(1,5)]}, + name="Blanusa Second Snark Graph") + + g.add_cycle([(0,i) for i in range(5)]) + g.add_cycle([(1,i) for i in range(5)]) + g.add_cycle([(0,5),(0,6),(0,7),(1,5),(1,6),(1,7)]) + + _circle_embedding(g, + [(0,(2*i)%5) for i in range(5)], + center = (-1.5,0), + shift = .5) + _circle_embedding(g, + [(1,(2*i)%5) for i in range(5)], + center = (1.5,0)) + + _circle_embedding(g, + [(0,i) for i in range(5,8)]+[0]*4, + center = (-1.2,0), + shift = 2.5, + radius = 2.2) + _circle_embedding(g, + [(1,i) for i in range(5,8)]+[0]*4, + center = (1.2,0), + shift = -1, + radius = 2.2) + + _circle_embedding(g,[0,1], shift=.5) + g.relabel() + return g + def BrinkmannGraph(): r""" Returns the Brinkmann graph. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index b4862a92b83..78d92b1eef6 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -87,6 +87,8 @@ def __append_to_doc(methods): "Balaban11Cage", "BidiakisCube", "BiggsSmithGraph", + "BlanusaFirstSnarkGraph", + "BlanusaSecondSnarkGraph", "BrinkmannGraph", "BrouwerHaemersGraph", "BuckyBall", @@ -1242,6 +1244,8 @@ def fusenes(self, hexagon_count, benzenoids=False): Balaban11Cage = staticmethod(sage.graphs.generators.smallgraphs.Balaban11Cage) BidiakisCube = staticmethod(sage.graphs.generators.smallgraphs.BidiakisCube) BiggsSmithGraph = staticmethod(sage.graphs.generators.smallgraphs.BiggsSmithGraph) + BlanusaFirstSnarkGraph = staticmethod(sage.graphs.generators.smallgraphs.BlanusaFirstSnarkGraph) + BlanusaSecondSnarkGraph = staticmethod(sage.graphs.generators.smallgraphs.BlanusaSecondSnarkGraph) BrinkmannGraph = staticmethod(sage.graphs.generators.smallgraphs.BrinkmannGraph) BrouwerHaemersGraph = staticmethod(sage.graphs.generators.smallgraphs.BrouwerHaemersGraph) BuckyBall = staticmethod(sage.graphs.generators.smallgraphs.BuckyBall) From c75aeda60c6722c7371cc07dec3c5478549dc746 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 27 Aug 2013 17:32:44 +0200 Subject: [PATCH 052/206] Fixing a stupid mistake --- src/sage/graphs/generators/smallgraphs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 4eb6d520df6..1d39f9385bf 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -4105,7 +4105,7 @@ def WatkinsSnarkGraph(): sage: g.chromatic_number() 3 """ - g = Graph(name="Szekeres Snark Graph") + g = Graph(name="Watkins Snark Graph") for i in range(5): g.add_cycle([(i,j) for j in range(9)]) From 67e3383e9483b58981a8e4b42e9fba7620423900 Mon Sep 17 00:00:00 2001 From: Charles Bouillaguet Date: Sun, 20 Jan 2013 22:41:46 +0000 Subject: [PATCH 053/206] Trac #13850: implement a generic interface for boolean polynomial solving --- .../polynomial/multi_polynomial_sequence.py | 170 +++++++++++++++++- 1 file changed, 165 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 5e51329b410..3a3027b3459 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -17,6 +17,7 @@ - Martin Albrecht (2009): refactoring, clean-up, new functions - Martin Albrecht (2011): refactoring, moved to sage.rings.polynomial - Alex Raichev (2011-06): added algebraic_dependence() +- Charles Bouillaguet (2013-1): added solve() EXAMPLES: @@ -140,7 +141,7 @@ sage: loads(dumps(F)) == F True -.. note:: +.. NOTE:: In many other computer algebra systems (cf. Singular) this class would be called ``Ideal`` but an ideal is a very distinct object @@ -156,16 +157,19 @@ """ from types import GeneratorType +from sage.misc.package import is_package_installed -from sage.structure.sequence import Sequence_generic +from sage.structure.sequence import Sequence, Sequence_generic from sage.rings.infinity import Infinity +from sage.rings.finite_rings.constructor import FiniteField as GF from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing from sage.rings.quotient_ring import is_QuotientRing from sage.rings.quotient_ring_element import QuotientRingElement from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal from sage.rings.polynomial.multi_polynomial import is_MPolynomial from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.pbori import BooleanPolynomialRing from sage.interfaces.singular import singular @@ -1021,7 +1025,6 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc """ from polybori import gauss_on_polys from polybori.ll import eliminate,ll_encode,ll_red_nf_redsb - from sage.rings.polynomial.pbori import BooleanPolynomialRing R = self.ring() @@ -1100,8 +1103,6 @@ def _groebner_strategy(self): """ R = self.ring() - from sage.rings.polynomial.pbori import BooleanPolynomialRing - if not isinstance(R, BooleanPolynomialRing): from sage.libs.singular.groebner_strategy import GroebnerStrategy return GroebnerStrategy(self.ideal()) @@ -1113,6 +1114,165 @@ def _groebner_strategy(self): g.reduction_strategy.opt_red_tail=True return g + def solve(self, algorithm='polybori', n=1, eliminate_linear_variables=True, verbose=False, **kwds): + r""" + Find solutions of this boolean polynomial system. + + This function provide a unified interface to several algorithms + dedicated to solving systems of boolean equations. Depending on + the particular nature of the system, some might be much faster + than some others. + + INPUT: + + * ``self`` - a sequence of boolean polynomials + + * ``algorithm`` - the method to use. Possible values are + ``polybori``, ``sat`` and ``exhaustive_search``. (default: + ``polybori``, since it is always available) + + * ``n`` - number of solutions to return. If ``n == +Infinity`` + then all solutions are returned. If `n < \infty` then `n` + solutions are returned if the equations have at least `n` + solutions. Otherwise, all the solutions are + returned. (default: ``1``) + + * ``eliminate_linear_variables`` - whether to eliminate + variables that appear linearly. This reduces the number of + variables (makes solving faster a priori), but is likely to + make the equations denser (may make solving slower depending + on the method). + + * ``verbose`` - whether to display progress and (potentially) + useful information while the computation runs. (default: + ``False``) + + EXAMPLES: + + Without argument, a single arbitrary solution is returned:: + + sage: R. = BooleanPolynomialRing() + sage: S = Sequence([x*y+z, y*z+x, x+y+z+1]) + sage: sol = S.solve(); sol # random + [{y: 1, z: 0, x: 0}] + + We check that it is actually a solution:: + + sage: S.subs( sol[0] ) + [0, 0, 0] + + We obtain all solutions:: + + sage: sols = S.solve(n=Infinity); sols # random + [{x: 0, y: 1, z: 0}, {x: 1, y: 1, z: 1}] + sage: map( lambda x: S.subs(x), sols) + [[0, 0, 0], [0, 0, 0]] + + We can force the use of exhaustive search if the optional + package ``FES`` is present:: + + sage: sol = S.solve(algorithm='exhaustive_search'); sol # random, optional - needs FES + [{x: 1, y: 1, z: 1}] + sage: S.subs( sol[0] ) + [0, 0, 0] + + And we may use SAT-solvers if they are available:: + + sage: sol = S.solve(algorithm='sat'); sol # random, optional - needs CryptoMiniSat + [{y: 1, z: 0, x: 0}] + sage: S.subs( sol[0] ) + [0, 0, 0] + + TESTS: + + Make sure that variables not occuring in the equations are no problem:: + + sage: R. = BooleanPolynomialRing() + sage: S = Sequence([x*y+z, y*z+x, x+y+z+1]) + sage: sols = S.solve(n=Infinity) + sage: map( lambda x: S.subs(x), sols) + [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]] + + Not eliminating linear variables:: + + sage: sols = S.solve(n=Infinity, eliminate_linear_variables=False) + sage: map( lambda x: S.subs(x), sols) + [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]] + + A tricky case where the linear equations are insatisfiable:: + + sage: R. = BooleanPolynomialRing() + sage: S = Sequence([x*y*z+x*y+z*y+x*z, x+y+z+1, x+y+z]) + sage: S.solve() + [] + + """ + from sage.modules.free_module import VectorSpace + + S = self + R_origin = R_solving = self.ring() + reductors = [] + + if eliminate_linear_variables: + T, reductors = self.eliminate_linear_variables(return_reductors=True) + if T.variables() != (): + R_solving = BooleanPolynomialRing( T.nvariables(), map(str, list(T.variables())) ) + S = PolynomialSequence( R_solving, [ R_solving(f) for f in T] ) + + if S != []: + if algorithm == "exhaustive_search": + if not is_package_installed('fes'): + raise ValueError('algorithm=exhaustive_search requires the optional library FES. Run "install_package(\'fes\')" to install it.') + from sage.libs.fes import exhaustive_search + solutions = exhaustive_search(S, max_sols=n, verbose=verbose, **kwds) + + elif algorithm == "polybori": + I = S.ideal() + if verbose: + I.groebner_basis(full_prot=True, **kwds) + else: + I.groebner_basis(**kwds) + solutions = I.variety() + if len(solutions) >= n: + solutions = solutions[:n] + + elif algorithm == "sat": + from sage.sat.boolean_polynomials import solve as solve_sat + if verbose: + solutions = solve_sat(S, n=n, s_verbosity=1, **kwds) + else: + solutions = solve_sat(S, n=n, **kwds) + else: + raise ValueError("unknown 'algorithm' value") + else: + solutions = [] + + if S.variables() == (): + solved_variables = set() + else: + solved_variables = { R_origin(x).lm() for x in R_solving.gens() } + eliminated_variables = { f.lex_lead() for f in reductors } + leftover_variables = { x.lm() for x in R_origin.gens() } - solved_variables - eliminated_variables + + if leftover_variables != set(): + partial_solutions = solutions + solutions = [] + for sol in partial_solutions: + for v in VectorSpace( GF(2), len(leftover_variables) ): + new_solution = sol.copy() + for var,val in zip(leftover_variables, v): + new_solution[ var ] = val + solutions.append( new_solution ) + + for r in reductors: + for sol in solutions: + sol[ r.lm() ] = r.subs(sol).constant_coefficient() + + return solutions + + + + class PolynomialSequence_gf2e(PolynomialSequence_generic): """ PolynomialSequence over `\mathbb{F}_{2^e}`, i.e extensions over From 4daab78ad359d500b6a264708a30cafdbeb2a03e Mon Sep 17 00:00:00 2001 From: Charles Bouillaguet Date: Fri, 4 Oct 2013 09:21:56 +0200 Subject: [PATCH 054/206] fix doctest syntax for optional packages --- src/sage/rings/polynomial/multi_polynomial_sequence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 3a3027b3459..1c8ec4e532a 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -1171,14 +1171,14 @@ def solve(self, algorithm='polybori', n=1, eliminate_linear_variables=True, ver We can force the use of exhaustive search if the optional package ``FES`` is present:: - sage: sol = S.solve(algorithm='exhaustive_search'); sol # random, optional - needs FES + sage: sol = S.solve(algorithm='exhaustive_search'); sol # random, optional - FES [{x: 1, y: 1, z: 1}] sage: S.subs( sol[0] ) [0, 0, 0] And we may use SAT-solvers if they are available:: - sage: sol = S.solve(algorithm='sat'); sol # random, optional - needs CryptoMiniSat + sage: sol = S.solve(algorithm='sat'); sol # random, optional - CryptoMiniSat [{y: 1, z: 0, x: 0}] sage: S.subs( sol[0] ) [0, 0, 0] From 13b500bf57a4e6c6ee60b92666ddb235c101179b Mon Sep 17 00:00:00 2001 From: Simon King Date: Fri, 16 Aug 2013 10:36:00 +0000 Subject: [PATCH 055/206] #12601: Cached special methods --- src/sage/misc/cachefunc.pyx | 186 ++++++++++++++++++++++++++++++++++-- 1 file changed, 176 insertions(+), 10 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index df0daa8289d..5ee2c98dba4 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -9,7 +9,7 @@ AUTHORS: methods to instances). - Tom Boothby (added DiskCachedFunction). - Simon King (improved performance, more doctests, cython version, - added CachedMethodCallerNoArgs. Weak cached function). + CachedMethodCallerNoArgs, weak cached function, cached special methods). EXAMPLES: @@ -1615,7 +1615,7 @@ cdef class CachedMethodCaller(CachedFunction): """ # This is for Parents or Elements that do not allow attribute assignment try: - return (inst.__cached_methods).__getitem__(self._cachedmethod._cachedfunc.__name__) + return (inst.__cached_methods)[self._cachedmethod._cachedfunc.__name__] except (AttributeError,TypeError,KeyError): pass Caller = CachedMethodCaller(self._cachedmethod, inst, cache=self._cachedmethod._get_instance_cache(inst), inst_in_key=self._inst_in_key, name=self._cachedmethod._cachedfunc.__name__) @@ -1628,7 +1628,7 @@ cdef class CachedMethodCaller(CachedFunction): if inst.__cached_methods is None: inst.__cached_methods = {self._cachedmethod._cachedfunc.__name__ : Caller} else: - (inst.__cached_methods).__setitem__(self._cachedmethod._cachedfunc.__name__, Caller) + (inst.__cached_methods)[self._cachedmethod._cachedfunc.__name__] = Caller except AttributeError,msg: pass return Caller @@ -1924,7 +1924,7 @@ cdef class CachedMethodCallerNoArgs(CachedFunction): """ # This is for Parents or Elements that do not allow attribute assignment try: - return (inst.__cached_methods).__getitem__(self.__name__) + return (inst.__cached_methods)[self.__name__] except (AttributeError,TypeError,KeyError),msg: pass Caller = CachedMethodCallerNoArgs(inst, self.f, name=self.__name__) @@ -1937,7 +1937,7 @@ cdef class CachedMethodCallerNoArgs(CachedFunction): if inst.__cached_methods is None: inst.__cached_methods = {self.__name__ : Caller} else: - (inst.__cached_methods).__setitem__(self.__name__, Caller) + (inst.__cached_methods)[self.__name__] = Caller except AttributeError,msg: pass return Caller @@ -2118,7 +2118,7 @@ cdef class CachedMethod(object): except AttributeError: return {} - def __get__(self, inst, cls): #cls=None): + def __get__(self, object inst, cls): #cls=None): """ Get a CachedMethodCaller bound to this specific instance of the class of the cached method. @@ -2152,12 +2152,13 @@ cdef class CachedMethod(object): """ # This is for Parents or Elements that do not allow attribute assignment: + cdef str name try: name = self._cachedfunc.__name__ except AttributeError: name = self.__name__ try: - return (inst.__cached_methods).__getitem__(name) + return (inst.__cached_methods)[name] except (AttributeError,TypeError,KeyError),msg: pass # Apparently we need to construct the caller. @@ -2181,7 +2182,7 @@ cdef class CachedMethod(object): cache=self._get_instance_cache(inst), name=name) try: - setattr(inst,name, Caller) + setattr(inst, name, Caller) return Caller except AttributeError: pass @@ -2189,7 +2190,7 @@ cdef class CachedMethod(object): if inst.__cached_methods is None: inst.__cached_methods = {name : Caller} else: - (inst.__cached_methods).__setitem__(name, Caller) + (inst.__cached_methods)[name] = Caller except AttributeError: pass return Caller @@ -2201,8 +2202,173 @@ cdef class CachedMethod(object): # The disadvantage to this is that it does not provide # is_in_cache(), set_cache(), clear_cache(), ... methods. +cdef class CachedSpecialMethod(CachedMethod): + """ + Cached version of *special* python methods. + + IMPLEMENTATION: + + For new style classes ``C``, it is not possible to override a special + method, such as ``__hash__``, in the ``__dict__`` of an instance ``c`` of + ``C``, because Python will for efficiency reasons always use what is + provided by the class, not by the instance. + + By consequence, if ``__hash__`` would be wrapped by using + :class:`CachedMethod`, then ``hash(c)`` will access `C.__hash__` and bind + it to ``c``, which means that the ``__get__`` method of + :class:`CachedMethod` will be called. But there, we assume that Python has + already inspected ``__dict__``, and thus a :class:`CachedMethodCaller` + will be created over and over again. + + Here, the `__get__` method will explicitly access the `__dict__`, so that + ``hash(c)`` will rely on a single :class:`CachedMethodCaller` stored in + the ``__dict__``. + + EXAMPLES:: + + sage: class C: + ....: @cached_method + ....: def __hash__(self): + ....: print "compute hash" + ....: return int(5) + ....: + sage: c = C() + sage: type(C.__hash__) + + + The hash is computed only once, subsequent calls will use the value from + the cache. This was implemented in :trac:`12601`. + + sage: hash(c) # indirect doctest + compute hash + 5 + sage: hash(c) + 5 + + """ + def __get__(self, object inst, cls): + """ + Bind a :class:`CachedMethodCaller` to a specific instance, using `__dict__`. -cached_method = CachedMethod + EXAMPLES:: + + sage: class C: + ....: @cached_method + ....: def __hash__(self): + ....: print "compute hash" + ....: return int(5) + ....: + sage: c = C() + sage: type(C.__hash__) + + sage: hash(c) # indirect doctest + compute hash + 5 + sage: hash(c) + 5 + """ + # This is for Parents or Elements that do not allow attribute assignment: + cdef str name + try: + name = self._cachedfunc.__name__ + except AttributeError: + name = self.__name__ + cdef dict D = None + if inst is not None: + try: + D = inst.__dict__ + except (TypeError, AttributeError): + try: + D = inst.__cached_methods + except (TypeError, AttributeError): + raise TypeError("For a cached special method, either attribute assignment or a public '__cached_methods' attribute of type is needed") + if D is None: + # This can only happen in the case of __cached_methods + D = inst.__cached_methods = {} + else: + try: + return D[name] + except KeyError: + pass + # Apparently we need to construct the caller. + # Since we have an optimized version for functions that do not accept arguments, + # we need to analyse the argspec + f = (self._cachedfunc).f + if self.nargs==0: + args, varargs, keywords, defaults = sage_getargspec(f) + if varargs is None and keywords is None and len(args)<=1: + self.nargs = 1 + Caller = CachedMethodCallerNoArgs(inst, f, name=name) + else: + self.nargs = 2 # don't need the exact number + Caller = CachedMethodCaller(self, inst, + cache=self._get_instance_cache(inst), + name=name) + elif self.nargs==1: + Caller = CachedMethodCallerNoArgs(inst, f, name=name) + else: + Caller = CachedMethodCaller(self, inst, + cache=self._get_instance_cache(inst), + name=name) + if inst is not None: + try: + setattr(inst,name, Caller) + return Caller + except AttributeError: + pass + D[name] = Caller + return Caller + +def cached_method(f, name=None): + """ + + EXAMPLES: + + In the following examples, one can see how a cached method works in applicationy. + Below, we demonstrate what is done behind the scenes:: + + sage: class C: + ....: @cached_method + ....: def __hash__(self): + ....: print "compute hash" + ....: return int(5) + ....: @cached_method + ....: def f(self, x): + ....: print "computing cached method" + ....: return x*2 + sage: c = C() + sage: type(C.__hash__) + + sage: hash(c) + compute hash + 5 + + When calling a cached method for the second time with the same arguments, + the value is gotten from the cache, so that a new computation is not + needed:: + + sage: hash(c) + 5 + sage: c.f(4) + computing cached method + 8 + sage: c.f(4) is c.f(4) + True + + Using cached methods for the hash and other special methods was + implemented in :trac:`12601`, by means of :class:`CachedSpecialMethod`. We + show that it is used behind the scenes:: + + sage: cached_method(c.__hash__) + + sage: cached_method(c.f) + + + """ + cdef str fname = name or f.__name__ + if fname.startswith("__") and fname.endswith("__"): + return CachedSpecialMethod(f, name) + return CachedMethod(f, name) cdef class CachedInParentMethod(CachedMethod): r""" From fe1e73994b44b7bfc81e179a438756e7cb859b64 Mon Sep 17 00:00:00 2001 From: Simon King Date: Mon, 14 Oct 2013 23:17:48 +0200 Subject: [PATCH 056/206] Remove trailing whitespace --- src/sage/misc/cachefunc.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 5ee2c98dba4..26eef70fa74 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -2226,7 +2226,7 @@ cdef class CachedSpecialMethod(CachedMethod): EXAMPLES:: - sage: class C: + sage: class C: ....: @cached_method ....: def __hash__(self): ....: print "compute hash" @@ -2252,7 +2252,7 @@ cdef class CachedSpecialMethod(CachedMethod): EXAMPLES:: - sage: class C: + sage: class C: ....: @cached_method ....: def __hash__(self): ....: print "compute hash" @@ -2327,7 +2327,7 @@ def cached_method(f, name=None): In the following examples, one can see how a cached method works in applicationy. Below, we demonstrate what is done behind the scenes:: - sage: class C: + sage: class C: ....: @cached_method ....: def __hash__(self): ....: print "compute hash" From 6cf1fad162f761dd9a0191a7f8d862cb6300583b Mon Sep 17 00:00:00 2001 From: Simon King Date: Tue, 15 Oct 2013 10:54:50 +0200 Subject: [PATCH 057/206] Faster and more thorough detection of special method names --- src/sage/misc/cachefunc.pyx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index 26eef70fa74..77e8691deb0 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -424,6 +424,21 @@ from sage.misc.sageinspect import sage_getfile, sage_getsourcelines, sage_getarg from weakref import WeakValueDictionary +cdef frozenset special_method_names = frozenset(['__abs__', '__add__', + '__and__', '__call__', '__cmp__', '__coerce__', '__complex__', '__contains__', '__del__', + '__delattr__', '__delete__', '__delitem__', '__delslice__', '__dir__', '__div__', + '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__get__', '__getattr__', + '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__hex__', + '__iadd__', '__iand__', '__idiv__', '__ifloordiv__', '__ilshift__', '__imod__', '__imul__', + '__index__', '__init__', '__instancecheck__', '__int__', '__invert__', '__ior__', '__ipow__', + '__irshift__', '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', + '__length_hint__', '__long__', '__lshift__', '__lt__', '__missing__', '__mod__', '__mul__', + '__ne__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', + '__radd__', '__rand__', '__rdiv__', '__repr__', '__reversed__', '__rfloordiv__', '__rlshift__', + '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', + '__rtruediv__', '__rxor__', '__set__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', + '__str__', '__sub__', '__subclasscheck__', '__truediv__', '__unicode__', '__xor__', 'next']) + def _cached_function_unpickle(module,name): """ Unpickling of cached functions. @@ -2366,7 +2381,7 @@ def cached_method(f, name=None): """ cdef str fname = name or f.__name__ - if fname.startswith("__") and fname.endswith("__"): + if fname in special_method_names: return CachedSpecialMethod(f, name) return CachedMethod(f, name) From 012f44bcc29ee9fe36d7453ce6d1a7474c1bca31 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 16 Oct 2013 10:38:22 +0200 Subject: [PATCH 058/206] Memory leak in the interface with Gurobi, and broken doctest --- .../thematic_tutorials/linear_programming.rst | 2 +- .../numerical/backends/gurobi_backend.pxd | 5 +- .../numerical/backends/gurobi_backend.pyx | 199 ++++++------------ 3 files changed, 70 insertions(+), 136 deletions(-) diff --git a/src/doc/en/thematic_tutorials/linear_programming.rst b/src/doc/en/thematic_tutorials/linear_programming.rst index c0f39fcb869..d693465922c 100644 --- a/src/doc/en/thematic_tutorials/linear_programming.rst +++ b/src/doc/en/thematic_tutorials/linear_programming.rst @@ -494,7 +494,7 @@ change !) filename is joined. * A compiled version of the library * CPLEX : ``libcplex.a`` - * GUROBI : ``libgurobi45.so`` + * GUROBI : ``libgurobi55.so`` (or more recent) * The library file * CPLEX : ``cplex.h`` diff --git a/src/sage/numerical/backends/gurobi_backend.pxd b/src/sage/numerical/backends/gurobi_backend.pxd index 7c56d8be734..2941a579626 100644 --- a/src/sage/numerical/backends/gurobi_backend.pxd +++ b/src/sage/numerical/backends/gurobi_backend.pxd @@ -102,8 +102,9 @@ cdef extern from "gurobi_c.h": cdef class GurobiBackend(GenericBackend): cdef GRBenv * env - cdef GRBmodel ** model - cdef GurobiBackend copy(self) + cdef GRBenv * env_master + cdef GRBmodel * model + cpdef GurobiBackend copy(self) cdef int num_vars diff --git a/src/sage/numerical/backends/gurobi_backend.pyx b/src/sage/numerical/backends/gurobi_backend.pyx index fb9254d007c..2fee2dd8dae 100644 --- a/src/sage/numerical/backends/gurobi_backend.pyx +++ b/src/sage/numerical/backends/gurobi_backend.pyx @@ -39,29 +39,27 @@ cdef class GurobiBackend(GenericBackend): """ cdef int error - cdef GRBenv ** env - env = sage_malloc(sizeof(GRBenv *)) + # Initializing the master Environment. This one is kept to be + # deallocated on __dealloc__ + error = GRBloadenv(&self.env_master, NULL) - error = GRBloadenv(env, NULL) - - check(self.env, error) - - if env[0] == NULL: + if self.env_master == NULL: raise RuntimeError("Could not initialize Gurobi environment") - self.model = sage_malloc(sizeof(GRBmodel *)) + check(self.env_master, error) - error = GRBnewmodel(env[0], self.model, NULL, 0, NULL, NULL, NULL, NULL, NULL) + # Initializing the model + error = GRBnewmodel(self.env_master, &self.model, NULL, 0, NULL, NULL, NULL, NULL, NULL) - self.env = GRBgetenv (self.model[0]) + self.env = GRBgetenv (self.model) if error: raise RuntimeError("Could not initialize Gurobi model") if maximization: - error = GRBsetintattr(self.model[0], "ModelSense", -1) + error = GRBsetintattr(self.model, "ModelSense", -1) else: - error = GRBsetintattr(self.model[0], "ModelSense", +1) + error = GRBsetintattr(self.model, "ModelSense", +1) check(self.env, error) @@ -70,8 +68,6 @@ cdef class GurobiBackend(GenericBackend): self.set_verbosity(0) self.obj_constant_term = 0.0 - - cpdef int add_variable(self, lower_bound=0.0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0.0, name=None) except -1: """ Add a variable. @@ -152,11 +148,11 @@ cdef class GurobiBackend(GenericBackend): lower_bound = -GRB_INFINITY - error = GRBaddvar(self.model[0], 0, NULL, NULL, obj, lower_bound, upper_bound, vtype, c_name) + error = GRBaddvar(self.model, 0, NULL, NULL, obj, lower_bound, upper_bound, vtype, c_name) check(self.env,error) - check(self.env,GRBupdatemodel(self.model[0])) + check(self.env,GRBupdatemodel(self.model)) return self.ncols()-1 @@ -212,70 +208,6 @@ cdef class GurobiBackend(GenericBackend): obj = obj, name = None if names is None else names[i]) return value -# cdef double * p_obj = NULL -# cdef double * p_lb = NULL -# cdef double * p_ub = NULL -# cdef char ** p_names = NULL -# cdef char * p_types = NULL -# cdef int i -# cdef int error -# -# cdef int NAME_MAX_LEN = 50 -# -# # Objective coefficients -# if obj: -# p_obj = sage_malloc(number*sizeof(double)) -# for i in range(number): -# p_obj[i] = obj -# -# # Lower bounds -# if lower_bound != 0: -# p_lb = sage_malloc(number*sizeof(double)) -# for i in range(number): -# p_lb[i] = -GRB_INFINITY if lower_bound is None else lower_bound -# -# # Upper bounds -# if not upper_bound is None: -# p_ub = sage_malloc(number*sizeof(double)) -# for i in range(number): -# p_ub[i] = upper_bound -# -# # Type -# if not continuous: -# p_types = sage_malloc(number*sizeof(char)) -# if binary: -# p_types[0] = GRB_BINARY -# else: -# p_types[0] = GRB_INTEGER -# -# for i in range(2, number): -# p_types[i] = p_types[i-1] -# -# # Names -# if not names is None: -# p_names = sage_malloc((number+1)*sizeof(char *)) -# p_names[0] = sage_malloc(number*NAME_MAX_LEN*sizeof(char)) -# for i, name in enumerate(names): -# p_names[i+1] = p_names[i] + NAME_MAX_LEN -# p_names[i][0] = str(name) -# -# error = GRBaddvars (self.model[0], number, 0, NULL, NULL, NULL, p_obj, p_lb, p_ub, p_types, p_names) -# -# # Freeing the memory -# if p_obj != NULL: -# sage_free(p_obj) -# if p_lb != NULL: -# sage_free(p_lb) -# if p_ub != NULL: -# sage_free(p_ub) -# if p_names != NULL: -# for i in range(number+1): -# sage_free(p_names[i]) -# sage_free(p_names) -# if p_types != NULL: -# sage_free(p_types) -# -# check(self.env, error) cpdef set_variable_type(self, int variable, int vtype): """ @@ -306,14 +238,14 @@ cdef class GurobiBackend(GenericBackend): cdef int error if vtype == 1: - error = GRBsetcharattrelement(self.model[0], "VType", variable, 'I') + error = GRBsetcharattrelement(self.model, "VType", variable, 'I') elif vtype == 0: - error = GRBsetcharattrelement(self.model[0], "VType", variable, 'B') + error = GRBsetcharattrelement(self.model, "VType", variable, 'B') else: - error = GRBsetcharattrelement(self.model[0], "VType", variable, 'C') + error = GRBsetcharattrelement(self.model, "VType", variable, 'C') check(self.env, error) - check(self.env,GRBupdatemodel(self.model[0])) + check(self.env,GRBupdatemodel(self.model)) cpdef set_sense(self, int sense): """ @@ -339,12 +271,12 @@ cdef class GurobiBackend(GenericBackend): cdef int error if sense == 1: - error = GRBsetintattr(self.model[0], "ModelSense", -1) + error = GRBsetintattr(self.model, "ModelSense", -1) else: - error = GRBsetintattr(self.model[0], "ModelSense", +1) + error = GRBsetintattr(self.model, "ModelSense", +1) check(self.env, error) - check(self.env,GRBupdatemodel(self.model[0])) + check(self.env,GRBupdatemodel(self.model)) cpdef objective_coefficient(self, int variable, coeff=None): """ @@ -373,11 +305,11 @@ cdef class GurobiBackend(GenericBackend): cdef double value[1] if coeff: - error = GRBsetdblattrelement(self.model[0], "Obj", variable, coeff) + error = GRBsetdblattrelement(self.model, "Obj", variable, coeff) check(self.env, error) - check(self.env,GRBupdatemodel(self.model[0])) + check(self.env,GRBupdatemodel(self.model)) else: - error = GRBgetdblattrelement(self.model[0], "Obj", variable, value) + error = GRBgetdblattrelement(self.model, "Obj", variable, value) check(self.env, error) return value[0] @@ -408,12 +340,12 @@ cdef class GurobiBackend(GenericBackend): cdef char * pp_name[1] if name: - error = GRBsetstrattr(self.model[0], "ModelName", name) + error = GRBsetstrattr(self.model, "ModelName", name) check(self.env, error) - check(self.env,GRBupdatemodel(self.model[0])) + check(self.env,GRBupdatemodel(self.model)) else: - check(self.env,GRBgetstrattr(self.model[0], "ModelName", pp_name)) + check(self.env,GRBgetstrattr(self.model, "ModelName", pp_name)) if pp_name[0] == NULL: value = "" else: @@ -458,11 +390,11 @@ cdef class GurobiBackend(GenericBackend): cdef int error for value in coeff: - error = GRBsetdblattrelement (self.model[0], "Obj", i, value) + error = GRBsetdblattrelement (self.model, "Obj", i, value) check(self.env,error) i += 1 - check(self.env,GRBupdatemodel(self.model[0])) + check(self.env,GRBupdatemodel(self.model)) self.obj_constant_term = d @@ -516,10 +448,10 @@ cdef class GurobiBackend(GenericBackend): cdef int ind[1] ind[0] = i cdef int error - error = GRBdelconstrs (self.model[0], 1, ind ) + error = GRBdelconstrs (self.model, 1, ind ) check(self.env, error) - error = GRBupdatemodel(self.model[0]) + error = GRBupdatemodel(self.model) check(self.env,error) cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None): @@ -572,21 +504,21 @@ cdef class GurobiBackend(GenericBackend): name = "" if upper_bound is not None and lower_bound is None: - error = GRBaddconstr(self.model[0], len(coefficients), row_i, row_values, GRB_LESS_EQUAL, upper_bound, name) + error = GRBaddconstr(self.model, len(coefficients), row_i, row_values, GRB_LESS_EQUAL, upper_bound, name) elif lower_bound is not None and upper_bound is None: - error = GRBaddconstr(self.model[0], len(coefficients), row_i, row_values, GRB_GREATER_EQUAL, lower_bound, name) + error = GRBaddconstr(self.model, len(coefficients), row_i, row_values, GRB_GREATER_EQUAL, lower_bound, name) elif upper_bound is not None and lower_bound is not None: if lower_bound == upper_bound: - error = GRBaddconstr(self.model[0], len(coefficients), row_i, row_values, GRB_EQUAL, lower_bound, name) + error = GRBaddconstr(self.model, len(coefficients), row_i, row_values, GRB_EQUAL, lower_bound, name) else: - error = GRBaddrangeconstr(self.model[0], len(coefficients), row_i, row_values, lower_bound, upper_bound, name) + error = GRBaddrangeconstr(self.model, len(coefficients), row_i, row_values, lower_bound, upper_bound, name) check(self.env,error) - error = GRBupdatemodel(self.model[0]) + error = GRBupdatemodel(self.model) check(self.env,error) @@ -624,13 +556,13 @@ cdef class GurobiBackend(GenericBackend): cdef int fake[1] cdef int length[1] - error = GRBgetconstrs(self.model[0], length, NULL, NULL, NULL, index, 1 ) + error = GRBgetconstrs(self.model, length, NULL, NULL, NULL, index, 1 ) check(self.env,error) cdef int * p_indices = sage_malloc(length[0] * sizeof(int)) cdef double * p_values = sage_malloc(length[0] * sizeof(double)) - error = GRBgetconstrs(self.model[0], length, fake, p_indices, p_values, index, 1 ) + error = GRBgetconstrs(self.model, length, fake, p_indices, p_values, index, 1 ) check(self.env,error) cdef list indices = [] @@ -677,10 +609,10 @@ cdef class GurobiBackend(GenericBackend): cdef char sense[1] cdef int error - error = GRBgetcharattrelement(self.model[0], "Sense", index, sense) + error = GRBgetcharattrelement(self.model, "Sense", index, sense) check(self.env, error) - error = GRBgetdblattrelement(self.model[0], "RHS", index, d) + error = GRBgetdblattrelement(self.model, "RHS", index, d) check(self.env, error) if sense[0] == '>': @@ -719,10 +651,10 @@ cdef class GurobiBackend(GenericBackend): cdef double lb[1], ub[1] - error = GRBgetdblattrelement(self.model[0], "LB", index, lb) + error = GRBgetdblattrelement(self.model, "LB", index, lb) check(self.env, error) - error = GRBgetdblattrelement(self.model[0], "UB", index, ub) + error = GRBgetdblattrelement(self.model, "UB", index, ub) check(self.env, error) return (None if lb[0] <= -2147483647 else lb[0], @@ -755,10 +687,10 @@ cdef class GurobiBackend(GenericBackend): cdef int error global mip_status - check(self.env, GRBoptimize(self.model[0])) + check(self.env, GRBoptimize(self.model)) cdef int status[1] - check(self.env, GRBgetintattr(self.model[0], "Status", status)) + check(self.env, GRBgetintattr(self.model, "Status", status)) # Has there been a problem ? if status[0] != GRB_OPTIMAL: @@ -792,7 +724,7 @@ cdef class GurobiBackend(GenericBackend): """ cdef double p_value[1] - check(self.env,GRBgetdblattr(self.model[0], "ObjVal", p_value)) + check(self.env,GRBgetdblattr(self.model, "ObjVal", p_value)) return p_value[0] + self.obj_constant_term @@ -823,7 +755,7 @@ cdef class GurobiBackend(GenericBackend): """ cdef double value[1] - check(self.env,GRBgetdblattrelement(self.model[0], "X", variable, value)) + check(self.env,GRBgetdblattrelement(self.model, "X", variable, value)) return round(value[0]) if self.is_variable_binary(variable) else value[0] cpdef int ncols(self): @@ -842,7 +774,7 @@ cdef class GurobiBackend(GenericBackend): 2 """ cdef int i[1] - check(self.env,GRBgetintattr(self.model[0], "NumVars", i)) + check(self.env,GRBgetintattr(self.model, "NumVars", i)) return i[0] cpdef int nrows(self): @@ -861,7 +793,7 @@ cdef class GurobiBackend(GenericBackend): 2 """ cdef int i[1] - check(self.env,GRBgetintattr(self.model[0], "NumConstrs", i)) + check(self.env,GRBgetintattr(self.model, "NumConstrs", i)) return i[0] cpdef col_name(self, int index): @@ -882,7 +814,7 @@ cdef class GurobiBackend(GenericBackend): 'I am a variable' """ cdef char * name[1] - check(self.env,GRBgetstrattrelement(self.model[0], "VarName", index, name)) + check(self.env,GRBgetstrattrelement(self.model, "VarName", index, name)) if name[0] == NULL: value = "" else: @@ -906,7 +838,7 @@ cdef class GurobiBackend(GenericBackend): 'Empty constraint 1' """ cdef char * name[1] - check(self.env,GRBgetstrattrelement(self.model[0], "ConstrName", index, name)) + check(self.env,GRBgetstrattrelement(self.model, "ConstrName", index, name)) if name[0] == NULL: value = "" else: @@ -934,7 +866,7 @@ cdef class GurobiBackend(GenericBackend): True """ cdef char vtype[1] - check(self.env, GRBgetcharattrelement(self.model[0], "VType", index, vtype)) + check(self.env, GRBgetcharattrelement(self.model, "VType", index, vtype)) return vtype[0] == 'B' @@ -959,7 +891,7 @@ cdef class GurobiBackend(GenericBackend): True """ cdef char vtype[1] - check(self.env, GRBgetcharattrelement(self.model[0], "VType", index, vtype)) + check(self.env, GRBgetcharattrelement(self.model, "VType", index, vtype)) return vtype[0] == 'I' cpdef bint is_variable_continuous(self, int index): @@ -986,7 +918,7 @@ cdef class GurobiBackend(GenericBackend): """ cdef char vtype[1] - check(self.env, GRBgetcharattrelement(self.model[0], "VType", index, vtype)) + check(self.env, GRBgetcharattrelement(self.model, "VType", index, vtype)) return vtype[0] == 'C' cpdef bint is_maximization(self): @@ -1004,7 +936,7 @@ cdef class GurobiBackend(GenericBackend): False """ cdef int sense[1] - check(self.env,GRBgetintattr(self.model[0], "ModelSense", sense)) + check(self.env,GRBgetintattr(self.model, "ModelSense", sense)) return sense[0] == -1 cpdef variable_upper_bound(self, int index, value = False): @@ -1035,13 +967,13 @@ cdef class GurobiBackend(GenericBackend): if not value is False: check(self.env, GRBsetdblattrelement( - self.model[0], "UB", + self.model, "UB", index, value if value is not None else GRB_INFINITY)) - check(self.env,GRBupdatemodel(self.model[0])) + check(self.env,GRBupdatemodel(self.model)) else: - error = GRBgetdblattrelement(self.model[0], "UB", index, b) + error = GRBgetdblattrelement(self.model, "UB", index, b) check(self.env, error) return None if b[0] >= 2147483647 else b[0] @@ -1074,14 +1006,14 @@ cdef class GurobiBackend(GenericBackend): if not value is False: check(self.env, GRBsetdblattrelement( - self.model[0], "LB", + self.model, "LB", index, value if value is not None else -GRB_INFINITY)) - check(self.env,GRBupdatemodel(self.model[0])) + check(self.env,GRBupdatemodel(self.model)) else: - error = GRBgetdblattrelement(self.model[0], "LB", index, b) + error = GRBgetdblattrelement(self.model, "LB", index, b) check(self.env, error) return None if b[0] <= -2147483647 else b[0] @@ -1103,7 +1035,7 @@ cdef class GurobiBackend(GenericBackend): sage: p.set_objective([2, 5]) # optional - Gurobi sage: p.write_lp(os.path.join(SAGE_TMP, "lp_problem.lp")) # optional - Gurobi """ - check(self.env, GRBwrite(self.model[0], filename)) + check(self.env, GRBwrite(self.model, filename)) cpdef write_mps(self, char * filename, int modern): """ @@ -1123,7 +1055,7 @@ cdef class GurobiBackend(GenericBackend): sage: p.set_objective([2, 5]) # optional - Gurobi sage: p.write_lp(os.path.join(SAGE_TMP, "lp_problem.lp")) # optional - Gurobi """ - check(self.env, GRBwrite(self.model[0], filename)) + check(self.env, GRBwrite(self.model, filename)) cpdef solver_parameter(self, name, value = None): """ @@ -1205,7 +1137,7 @@ cdef class GurobiBackend(GenericBackend): else: raise RuntimeError("This should not happen.") - cdef GurobiBackend copy(self): + cpdef GurobiBackend copy(self): """ Returns a copy of self. @@ -1220,16 +1152,17 @@ cdef class GurobiBackend(GenericBackend): 6.0 """ cdef GurobiBackend p = GurobiBackend(maximization = self.is_maximization()) - p.model[0] = GRBcopymodel(self.model[0]) - p.env = GRBgetenv(p.model[0]) + p.model = GRBcopymodel(self.model) + p.env = GRBgetenv(p.model) return p def __dealloc__(self): """ Destructor """ - if self.model != NULL: - GRBfreemodel(self.model[0]) + GRBfreeenv(self.env) + GRBfreeenv(self.env_master) + GRBfreemodel(self.model) cdef dict errors = { 10001 : "GRB_ERROR_OUT_OF_MEMORY", From 9d8e7374e1c356bd7d1f50cc1e3dc1bba12c5099 Mon Sep 17 00:00:00 2001 From: Simon King Date: Sun, 27 Oct 2013 00:09:10 +0200 Subject: [PATCH 059/206] Different completion functors must not commute, to fix pushout of completed fields --- src/sage/categories/pushout.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 3ca27eac193..68fd8876687 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -2262,8 +2262,17 @@ def commutes(self,other): sage: (C*F)(ZZ['x']) is (F*C)(ZZ['x']) True + The following was fixed in :trac:`15329` (it used to result + in an infinite recursion):: + + sage: from sage.categories.pushout import pushout + sage: pushout(Qp(7),RLF) + Traceback (most recent call last): + ... + CoercionException: ('Ambiguous Base Extension', 7-adic Field with capped relative precision 20, Real Lazy Field) + """ - return isinstance(other,(FractionField,CompletionFunctor)) + return isinstance(other,FractionField) class QuotientFunctor(ConstructionFunctor): """ From 2fc96d427d55f4e8fdba30df99b54b8fd3dc138b Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 27 Oct 2013 01:07:49 +0200 Subject: [PATCH 060/206] Poset.is_chain is wrong and other details --- src/sage/combinat/posets/hasse_diagram.py | 48 ++++++++++++----------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index e6acc7b0c01..3f58d268404 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -424,11 +424,29 @@ def is_chain(self): sage: V = Poset({0:[1,2]}) sage: V.is_chain() False + + TESTS: + + Check :trac:`15330`:: + + sage: p = Poset(DiGraph({0:[1],2:[1]})) + sage: p.is_chain() + False """ - outdegs = self.out_degree() - outdegs.remove(0) - if len(set(outdegs))==1: return True - return False + # There is one minimum and all other vertices have out-degree 1 + seen_0 = False + for d in self.out_degree(): + if d == 1: + pass + elif d == 0: + if seen_0: + return False + seen_0 = True + else: + return False + + # Maximum in-degree is 1 + return all(d<=1 for d in self.in_degree()) def dual(self): """ @@ -456,8 +474,8 @@ def dual(self): def interval(self, x, y): """ - Returns a list of the elements z such that x <= z <= y. The order is - that induced by the ordering in self.linear_extension. + Returns a list of the elements `z` such that `x \leq z \leq y`. The order is + that induced by the ordering in ``self.linear_extension``. INPUT: @@ -480,21 +498,7 @@ def interval(self, x, y): return [z for z in range(self.order())[x:y+1] if self.is_lequal(x,z) and self.is_lequal(z,y)] - def closed_interval(self, x, y): - """ - Returns a list of the elements z such that x = z = y. The order is - that induced by the ordering in self.linear_extension. - - EXAMPLES:: - - sage: uc = [[1,3,2],[4],[4,5,6],[6],[7],[7],[7],[]] - sage: dag = DiGraph(dict(zip(range(len(uc)),uc))) - sage: from sage.combinat.posets.hasse_diagram import HasseDiagram - sage: H = HasseDiagram(dag) - sage: set([2,5,6,4,7]) == set(H.closed_interval(2,7)) - True - """ - return self.interval(x,y) + closed_interval = interval def open_interval(self, x, y): """ @@ -578,7 +582,7 @@ def rank_function(self): def _rank_dict(self): r""" Builds the rank dictionnary of the poset, if it exists, i.e. - a dictionary ``d`` where ``d[object] = self.rank_function()(object) + a dictionary ``d`` where ``d[object] = self.rank_function()(object)`` A *rank function* of a poset `P` is a function `r` that maps elements of `P` to integers and satisfies: From fe1870146db03147df832855daf74ee3cac659eb Mon Sep 17 00:00:00 2001 From: darij Date: Tue, 5 Nov 2013 17:52:20 -0800 Subject: [PATCH 061/206] trac #15330: minor docstring edit --- src/sage/combinat/posets/hasse_diagram.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 00fd4181e6f..0f2ea7121d1 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -474,16 +474,15 @@ def dual(self): def interval(self, x, y): """ - Returns a list of the elements `z` such that `x \leq z \leq y`. The order is - that induced by the ordering in ``self.linear_extension``. + Return a list of the elements `z` of ``self`` such that + `x \leq z \leq y`. The order is that induced by the + ordering in ``self.linear_extension``. INPUT: + - ``x`` -- any element of the poset - - ``x`` - any element of the poset - - - ``y`` - any element of the poset - + - ``y`` -- any element of the poset EXAMPLES:: @@ -502,8 +501,8 @@ def interval(self, x, y): def open_interval(self, x, y): """ - Returns a list of the elements `z` such that `x < z < y`. The - order is that induced by the ordering in + Return a list of the elements `z` of ``self`` such that + `x < z < y`. The order is that induced by the ordering in ``self.linear_extension``. EXAMPLES:: @@ -1405,8 +1404,8 @@ def is_complemented_lattice(self): def complements(self): r""" - Returns a list ``l`` such that ``l[i]`` is a complement of - ``i`` in ``self``. + Return a list ``l`` such that ``l[i]`` is a complement of + ``i`` in ``self``, or ``None`` if no such complement exists. A complement of ``x`` is an element ``y`` such that the meet of ``x`` and ``y`` is the bottom element of ``self`` and the From f0f6a986bb6fda42316b097968c241aeffb18e0b Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Thu, 7 Nov 2013 01:56:01 +0100 Subject: [PATCH 062/206] First commit for ticket 14770 --- src/sage/combinat/alternating_sign_matrix.py | 2845 ++++++++++-------- 1 file changed, 1570 insertions(+), 1275 deletions(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 3900bc8eaa6..2bb6ffe8670 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -1,1275 +1,1570 @@ -r""" -Alternating Sign Matrices - -AUTHORS: - -- Mike Hansen (2007): Initial version -- Pierre Cange, Luis Serrano (2012): Added monotone triangles -- Travis Scrimshaw (2013-28-03): Added element class for ASM's and made - :class:`MonotoneTriangles` inherit from :class:`GelfandTsetlinPatterns`. -""" -#***************************************************************************** -# Copyright (C) 2007 Mike Hansen , -# 2012 Pierre Cagne , -# Luis Serrano -# 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/ -#***************************************************************************** - -import itertools -import copy -from sage.misc.classcall_metaclass import ClasscallMetaclass -from sage.misc.flatten import flatten -from sage.misc.misc import prod -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.parent import Parent -from sage.structure.element import Element -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from sage.matrix.matrix_space import MatrixSpace -from sage.matrix.constructor import matrix -from sage.rings.all import ZZ, factorial -from sage.rings.integer import Integer -from sage.combinat.posets.lattices import LatticePoset -from sage.combinat.gelfand_tsetlin_patterns import GelfandTsetlinPatternsTopRow -from sage.sets.set import Set -from sage.combinat.combinatorial_map import combinatorial_map -from sage.combinat.non_decreasing_parking_function import NonDecreasingParkingFunction -from sage.combinat.permutation import Permutation - -class AlternatingSignMatrix(Element): - r""" - An alternating sign matrix. - - An alternating sign matrix is a square matrix of `0`'s, `1`'s and `-1`'s - such that the sum of each row and column is `1` and the non-zero - entries in each row and column alternate in sign. - """ - __metaclass__ = ClasscallMetaclass - - @staticmethod - def __classcall_private__(cls, asm): - """ - Create an ASM. - - EXAMPLES:: - - sage: AlternatingSignMatrix([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) - [1 0 0] - [0 1 0] - [0 0 1] - """ - asm = matrix(asm) - if not asm.is_square(): - raise ValueError("The alternating sign matrices must be square") - P = AlternatingSignMatrices(asm.nrows()) - if asm not in P: - raise ValueError("Invalid alternating sign matrix") - return P(asm) - - def __init__(self, parent, asm): - """ - Initialize ``self``. - - EXAMPLES: - - sage: A = AlternatingSignMatrices(3) - sage: elt = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) - sage: TestSuite(elt).run() - """ - self._matrix = asm - Element.__init__(self, parent) - - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) - [1 0 0] - [0 1 0] - [0 0 1] - """ - return repr(self._matrix) - - def __eq__(self, other): - """ - Check equality. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: M = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) - sage: M == A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) - True - sage: M == A([[1, 0, 0],[0, 0, 1],[0, 1, 0]]) - False - """ - if isinstance(other, AlternatingSignMatrix): - return self._matrix == other._matrix - return self._matrix == other - - def __ne__(self, other): - """ - Check not equals. This is needed, see :trac:`14762`. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: M = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) - sage: M != A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) - False - sage: M != A([[1, 0, 0],[0, 0, 1],[0, 1, 0]]) - True - """ - return not self.__eq__(other) - - def _latex_(self): - r""" - Return a `\LaTeX` representation of ``self``. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: latex(A([[1, 0, 0],[0, 1, 0],[0, 0, 1]])) - \left(\begin{array}{rrr} - 1 & 0 & 0 \\ - 0 & 1 & 0 \\ - 0 & 0 & 1 - \end{array}\right) - """ - return self._matrix._latex_() - - def to_matrix(self): - """ - Return ``self`` as a regular matrix. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: asm = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) - sage: m = asm.to_matrix(); m - [1 0 0] - [0 1 0] - [0 0 1] - sage: m.parent() - Full MatrixSpace of 3 by 3 dense matrices over Integer Ring - """ - return copy.copy(self._matrix) - - @combinatorial_map(name='to monotone triangle') - def to_monotone_triangle(self): - r""" - Return a monotone triangle from ``self``. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]).to_monotone_triangle() - [[3, 2, 1], [2, 1], [1]] - sage: asm = A([[0, 1, 0],[1, -1, 1],[0, 1, 0]]) - sage: asm.to_monotone_triangle() - [[3, 2, 1], [3, 1], [2]] - sage: asm = A([[0, 0, 1],[1, 0, 0],[0, 1, 0]]) - sage: asm.to_monotone_triangle() - [[3, 2, 1], [3, 1], [3]] - sage: A.from_monotone_triangle(asm.to_monotone_triangle()) == asm - True - """ - n = self._matrix.nrows() - triangle = [None]*n - prev = [0]*n - for j, row in enumerate(self._matrix): - add_row = [a+b for (a,b) in itertools.izip(row, prev)] - line = [i+1 for (i,val) in enumerate(add_row) if val==1] - triangle[n-1-j] = list(reversed(line)) - prev = add_row - return MonotoneTriangles(n)(triangle) - - @combinatorial_map(name='to Dyck word') - def to_dyck_word(self): - r""" - Return the Dyck word determined by the last diagonal of - the monotone triangle corresponding to ``self``. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: A([[0,1,0],[1,0,0],[0,0,1]]).to_dyck_word() - [1, 1, 0, 0, 1, 0] - sage: d = A([[0,1,0],[1,-1,1],[0,1,0]]).to_dyck_word(); d - [1, 1, 0, 1, 0, 0] - sage: parent(d) - Complete Dyck words - """ - MT = self.to_monotone_triangle() - nplus = self._matrix.nrows() + 1 - parkfn = [nplus - row[0] for row in list(MT) if len(row) > 0] - return NonDecreasingParkingFunction(parkfn).to_dyck_word().reverse() - - def number_negative_ones(self): - """ - Return the number of entries in ``self`` equal to -1. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: asm = A([[0,1,0],[1,0,0],[0,0,1]]) - sage: asm.number_negative_ones() - 0 - sage: asm = A([[0,1,0],[1,-1,1],[0,1,0]]) - sage: asm.number_negative_ones() - 1 - """ - a = self._matrix - return sum(1 for (i,j) in a.nonzero_positions() if a[i,j] == -1) - - def is_permutation(self): - """ - Return ``True`` if ``self`` is a permutation matrix - and ``False`` otherwise. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: asm = A([[0,1,0],[1,0,0],[0,0,1]]) - sage: asm.is_permutation() - True - sage: asm = A([[0,1,0],[1,-1,1],[0,1,0]]) - sage: asm.is_permutation() - False - """ - return self.number_negative_ones() == 0 - - def to_permutation(self): - """ - Return the corresponding permutation if ``self`` is a permutation - matrix. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: asm = A([[0,1,0],[1,0,0],[0,0,1]]) - sage: p = asm.to_permutation(); p - [2, 1, 3] - sage: parent(p) - Standard permutations - sage: asm = A([[0,1,0],[1,-1,1],[0,1,0]]) - sage: asm.to_permutation() - Traceback (most recent call last): - ... - ValueError: Not a permutation matrix - """ - if not self.is_permutation(): - raise ValueError('Not a permutation matrix') - asm_matrix = self.to_matrix() - return Permutation([ j+1 for (i,j) in asm_matrix.nonzero_positions() ]) - - @combinatorial_map(name='to semistandard tableau') - def to_semistandard_tableau(self): - """ - Return the semistandard tableau corresponding the monotone triangle - corresponding to ``self``. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: A([[0,0,1],[1,0,0],[0,1,0]]).to_semistandard_tableau() - [[1, 1, 3], [2, 3], [3]] - sage: t = A([[0,1,0],[1,-1,1],[0,1,0]]).to_semistandard_tableau(); t - [[1, 1, 2], [2, 3], [3]] - sage: parent(t) - Semistandard tableaux - """ - from sage.combinat.tableau import SemistandardTableau, SemistandardTableaux - mt = self.to_monotone_triangle() - ssyt = [[0]*(len(mt) - j) for j in range(len(mt))] - for i in range(len(mt)): - for j in range(len(mt[i])): - ssyt[i][j] = mt[j][-(i+1)] - return SemistandardTableau(ssyt) - - def left_key(self): - r""" - Return the left key of the alternating sign matrix ``self``. - - The left key of an alternating sign matrix was defined by Lascoux - in [LascouxPreprint]_ and is obtained by successively removing all the - `-1`'suntil what remains is a permutation matrix. This notion - corresponds to the notion of left key for semistandard tableaux. So - our algorithm proceeds as follows: we map ``self`` to its - corresponding monotone triangle, view that monotone triangle as a - semistandard tableaux, take its left key, and then map back through - monotone triangles to the permutation matrix which is the left key. - - REFERENCES: - - .. [Aval07] J.-C. Aval. *Keys and alternating sign matrices*. - Sem. Lothar. Combin. 59 (2007/10), Art. B59f, 13 pp. - - .. [LascouxPreprint] A. Lascoux. *Chern and Yang through ice*. - Preprint. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: A([[0,0,1],[1,0,0],[0,1,0]]).left_key() - [0 0 1] - [1 0 0] - [0 1 0] - sage: t = A([[0,1,0],[1,-1,1],[0,1,0]]).left_key(); t - [1 0 0] - [0 0 1] - [0 1 0] - sage: parent(t) - Alternating sign matrices of size 3 - """ - from sage.combinat.tableau import SemistandardTableau, SemistandardTableaux - lkey = self.to_semistandard_tableau().left_key_tableau() - mt = [[0]*(len(lkey) - j) for j in range(len(lkey))] - for i in range(len(lkey)): - for j in range(len(lkey[i])): - mt[i][j] = lkey[len(lkey[i])-j-1][i] - A = AlternatingSignMatrices(len(lkey)) - return A.from_monotone_triangle(mt) - - @combinatorial_map(name='to left key permutation') - def left_key_as_permutation(self): - """ - Return the permutation of the left key of ``self``. - - .. SEEALSO:: - - - :meth:`left_key()` - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: A([[0,0,1],[1,0,0],[0,1,0]]).left_key_as_permutation() - [3, 1, 2] - sage: t = A([[0,1,0],[1,-1,1],[0,1,0]]).left_key_as_permutation(); t - [1, 3, 2] - sage: parent(t) - Standard permutations - """ - return self.left_key().to_permutation() - -class AlternatingSignMatrices(Parent, UniqueRepresentation): - r""" - Class of all `n \times n` alternating sign matrices. - - An alternating sign matrix of size `n` is an `n \times n` matrix of `0`'s, - `1`'s and `-1`'s such that the sum of each row and column is `1` and the - non-zero entries in each row and column alternate in sign. - - Alternating sign matrices of size `n` are in bijection with - :class:`monotone triangles ` with `n` rows. - - INPUT: - - - `n` -- an integer, the size of the matrices. - - - ``use_monotone_triangle`` -- (Default: ``True``) If ``True``, the - generation of the matrices uses monotone triangles, else it will use the - earlier and now obsolete contre-tableaux implementation; - must be ``True`` to generate a lattice (with the ``lattice`` method) - - EXAMPLES: - - This will create an instance to manipulate the alternating sign - matrices of size 3:: - - sage: A = AlternatingSignMatrices(3) - sage: A - Alternating sign matrices of size 3 - sage: A.cardinality() - 7 - - Notably, this implementation allows to make a lattice of it:: - - sage: L = A.lattice() - sage: L - Finite lattice containing 7 elements - sage: L.category() - Category of facade finite lattice posets - """ - def __init__(self, n, use_monotone_triangles=True): - r""" - Initialize ``self``. - - TESTS:: - - sage: A = AlternatingSignMatrices(4) - sage: TestSuite(A).run() - sage: A == AlternatingSignMatrices(4, use_monotone_triangles=False) - False - """ - self._n = n - self._matrix_space = MatrixSpace(ZZ, n) - self._umt = use_monotone_triangles - Parent.__init__(self, category=FiniteEnumeratedSets()) - - def _repr_(self): - r""" - Return a string representation of ``self``. - - TESTS:: - - sage: A = AlternatingSignMatrices(4); A - Alternating sign matrices of size 4 - """ - return "Alternating sign matrices of size %s" % self._n - - def _repr_option(self, key): - """ - Metadata about the :meth:`_repr_` output. - - See :meth:`sage.structure.parent._repr_option` for details. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: A._repr_option('element_ascii_art') - True - """ - return self._matrix_space._repr_option(key) - - def __contains__(self, asm): - """ - Check if ``asm`` is in ``self``. - - TESTS:: - - sage: A = AlternatingSignMatrices(3) - sage: [[0,1,0],[1,0,0],[0,0,1]] in A - True - sage: [[0,1,0],[1,-1,1],[0,1,0]] in A - True - sage: [[0, 1],[1,0]] in A - False - sage: [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]] in A - False - sage: [[-1, 1, 1],[1,-1,1],[1,1,-1]] in A - False - """ - if isinstance(asm, AlternatingSignMatrix): - return asm._matrix.nrows() == self._n - try: - asm = self._matrix_space(asm) - except (TypeError, ValueError): - return False - for row in asm: - pos = False - for val in row: - if val > 0: - if pos: - return False - else: - pos = True - elif val < 0: - if pos: - pos = False - else: - return False - if not pos: - return False - if any(sum(row) != 1 for row in asm.columns()): - return False - return True - - def _element_constructor_(self, asm): - """ - Construct an element of ``self``. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: elt = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]); elt - [1 0 0] - [0 1 0] - [0 0 1] - sage: elt.parent() is A - True - sage: A([[3, 2, 1], [2, 1], [1]]) - [1 0 0] - [0 1 0] - [0 0 1] - """ - if isinstance(asm, AlternatingSignMatrix): - if asm.parent() is self: - return asm - raise ValueError("Cannot convert between alternating sign matrices of different sizes") - if asm in MonotoneTriangles(self._n): - return self.from_monotone_triangle(asm) - return self.element_class(self, self._matrix_space(asm)) - - Element = AlternatingSignMatrix - - def _an_element_(self): - """ - Return an element of ``self``. - """ - return self.element_class(self, self._matrix_space.identity_matrix()) - - def from_monotone_triangle(self, triangle): - r""" - Return an alternating sign matrix from a monotone triangle. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: A.from_monotone_triangle([[3, 2, 1], [2, 1], [1]]) - [1 0 0] - [0 1 0] - [0 0 1] - sage: A.from_monotone_triangle([[3, 2, 1], [3, 2], [3]]) - [0 0 1] - [0 1 0] - [1 0 0] - """ - n = len(triangle) - if n != self._n: - raise ValueError("Incorrect size") - asm = [] - - prev = [0]*n - for line in reversed(triangle): - v = [1 if j+1 in reversed(line) else 0 for j in range(n)] - row = [a-b for (a, b) in zip(v, prev)] - asm.append(row) - prev = v - - return self.element_class(self, self._matrix_space(asm)) - - def size(self): - r""" - Return the size of the matrices in ``self``. - - TESTS:: - - sage: A = AlternatingSignMatrices(4) - sage: A.size() - 4 - """ - return self._n - - def cardinality(self): - r""" - Return the cardinality of ``self``. - - The number of `n \times n` alternating sign matrices is equal to - - .. MATH:: - - \prod_{k=0}^{n-1} \frac{(3k+1)!}{(n+k)!} = \frac{1! 4! 7! 10! - \cdots (3n-2)!}{n! (n+1)! (n+2)! (n+3)! \cdots (2n-1)!} - - EXAMPLES:: - - sage: [AlternatingSignMatrices(n).cardinality() for n in range(0, 11)] - [1, 1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460, 129534272700] - """ - return Integer(prod( [ factorial(3*k+1)/factorial(self._n+k) - for k in range(self._n)] )) - - def matrix_space(self): - """ - Return the underlying matrix space. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: A.matrix_space() - Full MatrixSpace of 3 by 3 dense matrices over Integer Ring - """ - return self._matrix_space - - def __iter__(self): - r""" - Iterator on the alternating sign matrices of size `n`. - - If defined using ``use_monotone_triangles``, this iterator - will use the iteration on the monotone triangles. Else, it - will use the iteration on contre-tableaux. - - TESTS:: - - sage: A = AlternatingSignMatrices(4) - sage: len(list(A)) - 42 - """ - if self._umt: - for t in MonotoneTriangles(self._n): - yield self.from_monotone_triangle(t) - else: - for c in ContreTableaux(self._n): - yield from_contre_tableau(c) - - def _lattice_initializer(self): - r""" - Return a 2-tuple to use in argument of ``LatticePoset``. - - For more details about the cover relations, see - ``MonotoneTriangles``. Notice that the returned matrices are - made immutable to ensure their hashability required by - ``LatticePoset``. - - EXAMPLES: - - Proof of the lattice property for alternating sign matrices of - size 3:: - - sage: A = AlternatingSignMatrices(3) - sage: P = Poset(A._lattice_initializer()) - sage: P.is_lattice() - True - """ - assert(self._umt) - (mts, rels) = MonotoneTriangles(self._n)._lattice_initializer() - bij = dict((t, self.from_monotone_triangle(t)) for t in mts) - asms, rels = bij.itervalues(), [(bij[a], bij[b]) for (a,b) in rels] - return (asms, rels) - - def cover_relations(self): - r""" - Iterate on the cover relations between the alternating sign - matrices. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: for (a,b) in A.cover_relations(): - ... eval('a, b') - ... - ( - [1 0 0] [0 1 0] - [0 1 0] [1 0 0] - [0 0 1], [0 0 1] - ) - ( - [1 0 0] [1 0 0] - [0 1 0] [0 0 1] - [0 0 1], [0 1 0] - ) - ( - [0 1 0] [ 0 1 0] - [1 0 0] [ 1 -1 1] - [0 0 1], [ 0 1 0] - ) - ( - [1 0 0] [ 0 1 0] - [0 0 1] [ 1 -1 1] - [0 1 0], [ 0 1 0] - ) - ( - [ 0 1 0] [0 0 1] - [ 1 -1 1] [1 0 0] - [ 0 1 0], [0 1 0] - ) - ( - [ 0 1 0] [0 1 0] - [ 1 -1 1] [0 0 1] - [ 0 1 0], [1 0 0] - ) - ( - [0 0 1] [0 0 1] - [1 0 0] [0 1 0] - [0 1 0], [1 0 0] - ) - ( - [0 1 0] [0 0 1] - [0 0 1] [0 1 0] - [1 0 0], [1 0 0] - ) - - """ - (_, rels) = self._lattice_initializer() - return (_ for _ in rels) - - def lattice(self): - r""" - Return the lattice of the alternating sign matrices of size - `n`, created by ``LatticePoset``. - - EXAMPLES:: - - sage: A = AlternatingSignMatrices(3) - sage: L = A.lattice() - sage: L - Finite lattice containing 7 elements - - """ - return LatticePoset(self._lattice_initializer(), cover_relations=True) - - -class MonotoneTriangles(GelfandTsetlinPatternsTopRow): - r""" - Monotone triangles with `n` rows. - - A monotone triangle is a number triangle `(a_{i,j})_{1 \leq i \leq - n , 1 \leq j \leq i}` on `\{1, \dots, n\}` such that: - - - `a_{i,j} < a_{i,j+1}` - - - `a_{i+1,j} < a_{i,j} \leq a_{i+1,j+1}` - - This notably requires that the bottom column is ``[1,...,n]``. - - Alternatively a monotone triangle is a strict Gelfand-Tsetlin pattern with - top row `(n, \ldots, 2, 1)`. - - INPUT: - - - ``n`` -- The number of rows in the monotone triangles - - EXAMPLES: - - This represents the monotone triangles with base ``[3,2,1]``:: - - sage: M = MonotoneTriangles(3) - sage: M - Monotone triangles with 3 rows - sage: M.cardinality() - 7 - - The monotone triangles are a lattice:: - - sage: M.lattice() - Finite lattice containing 7 elements - - Monotone triangles can be converted to alternating sign matrices - and back:: - - sage: M = MonotoneTriangles(5) - sage: A = AlternatingSignMatrices(5) - sage: all(A.from_monotone_triangle(m).to_monotone_triangle() == m for m in M) - True - """ - def __init__(self, n): - r""" - Initialize ``self``. - - TESTS:: - - sage: M = MonotoneTriangles(4) - sage: TestSuite(M).run() - sage: M2 = MonotoneTriangles(int(4)) - sage: M is M2 - True - """ - GelfandTsetlinPatternsTopRow.__init__(self, tuple(reversed(range(1, n+1))), True) - - def _repr_(self): - r""" - String representation. - - TESTS:: - - sage: M = MonotoneTriangles(4) - sage: M - Monotone triangles with 4 rows - """ - return "Monotone triangles with %s rows" % self._n - - def cardinality(self): - r""" - Cardinality of ``self``. - - The number of monotone triangles with `n` rows is equal to - - .. MATH:: - - \prod_{k=0}^{n-1} \frac{(3k+1)!}{(n+k)!} = \frac{1! 4! 7! 10! - \cdots (3n-2)!}{n! (n+1)! (n+2)! (n+3)! \cdots (2n-1)!} - - EXAMPLES:: - - sage: M = MonotoneTriangles(4) - sage: M.cardinality() - 42 - """ - return Integer(prod( [ factorial(3*k+1)/factorial(self._n+k) - for k in range(self._n)] )) - - def _lattice_initializer(self): - r""" - Return a 2-tuple to use in argument of ``LatticePoset``. - - This couple is composed by the set of the monotone triangles - with `n` rows and the cover relations. Specializing this - function allows to generate the monotone triangles just once, - and so to speed up the computation in comparison of - ``(list(self), self.cover_relations())``. Notice that the - function also switch the representation of monotone triangles - from list of list to tuple of tuple in order to make them - hashable (required to make a poset with them). - - EXAMPLES:: - - sage: M = MonotoneTriangles(3) - sage: P = Poset(M._lattice_initializer()) - sage: P.is_lattice() - True - """ - # get a list of the elements and switch to a tuple - # representation - set_ = list(self) - set_ = map(lambda x: tuple(map(tuple, x)), set_) - return (set_, [(a,b) for a in set_ for b in set_ if _is_a_cover(a,b)]) - - def cover_relations(self): - r""" - Iterate on the cover relations in the set of monotone triangles - with `n` rows. - - EXAMPLES:: - - sage: M = MonotoneTriangles(3) - sage: for (a,b) in M.cover_relations(): - ... eval('a, b') - ... - ([[3, 2, 1], [2, 1], [1]], [[3, 2, 1], [2, 1], [2]]) - ([[3, 2, 1], [2, 1], [1]], [[3, 2, 1], [3, 1], [1]]) - ([[3, 2, 1], [2, 1], [2]], [[3, 2, 1], [3, 1], [2]]) - ([[3, 2, 1], [3, 1], [1]], [[3, 2, 1], [3, 1], [2]]) - ([[3, 2, 1], [3, 1], [2]], [[3, 2, 1], [3, 1], [3]]) - ([[3, 2, 1], [3, 1], [2]], [[3, 2, 1], [3, 2], [2]]) - ([[3, 2, 1], [3, 1], [3]], [[3, 2, 1], [3, 2], [3]]) - ([[3, 2, 1], [3, 2], [2]], [[3, 2, 1], [3, 2], [3]]) - """ - set_ = list(self) - return ((a,b) for a in set_ for b in set_ if _is_a_cover(a,b)) - - def lattice(self): - r""" - Return the lattice of the monotone triangles with `n` rows. - - EXAMPLES:: - - sage: M = MonotoneTriangles(3) - sage: P = M.lattice() - sage: P - Finite lattice containing 7 elements - - """ - return LatticePoset(self._lattice_initializer(), cover_relations=True) - -def _is_a_cover(mt0, mt1): - r""" - Define the cover relations. - - Return ``True`` if and only if the second argument is a cover of - the first one. - - EXAMPLES:: - - sage: import sage.combinat.alternating_sign_matrix as asm - sage: asm._is_a_cover([[1,2,3],[1,2],[1]], [[1,2,3],[1,3],[1]]) - True - sage: asm._is_a_cover([[1,2,3],[1,3],[2]], [[1,2,3],[1,2],[1]]) - False - """ - diffs = 0 - for (a,b) in itertools.izip(flatten(mt0), flatten(mt1)): - if a != b: - if a+1 == b: - diffs += 1 - else: - return False - if diffs > 1: - return False - return diffs == 1 - -# Deprecated methods - -def to_monotone_triangle(matrix): - """ - Deprecated method, use :meth:`AlternatingSignMatrix.to_monotone_triangle()` - instead. - - EXAMPLES:: - - sage: sage.combinat.alternating_sign_matrix.to_monotone_triangle([[0,1],[1,0]]) - doctest:...: DeprecationWarning: to_monotone_triangle() is deprecated. Use AlternatingSignMatrix.to_monotone_triangle() instead - See http://trac.sagemath.org/14301 for details. - [[2, 1], [2]] - """ - from sage.misc.superseded import deprecation - deprecation(14301,'to_monotone_triangle() is deprecated. Use AlternatingSignMatrix.to_monotone_triangle() instead') - return AlternatingSignMatrix(matrix).to_monotone_triangle() - -def from_monotone_triangle(triangle): - """ - Deprecated method, use - :meth:`AlternatingSignMatrices.from_monotone_triangle()` instead. - - EXAMPLES:: - - sage: sage.combinat.alternating_sign_matrix.from_monotone_triangle([[1, 2], [2]]) - doctest:...: DeprecationWarning: from_monotone_triangle() is deprecated. Use AlternatingSignMatrix.from_monotone_triangle() instead - See http://trac.sagemath.org/14301 for details. - [0 1] - [1 0] - """ - from sage.misc.superseded import deprecation - deprecation(14301,'from_monotone_triangle() is deprecated. Use AlternatingSignMatrix.from_monotone_triangle() instead') - return AlternatingSignMatrices(len(triangle)).from_monotone_triangle(triangle) - -# For old pickles -def AlternatingSignMatrices_n(n): - """ - For old pickles of ``AlternatingSignMatrices_n``. - - EXAMPLES:: - - sage: sage.combinat.alternating_sign_matrix.AlternatingSignMatrices_n(3) - doctest:...: DeprecationWarning: this class is deprecated. Use sage.combinat.alternating_sign_matrix.AlternatingSignMatrices instead - See http://trac.sagemath.org/14301 for details. - Alternating sign matrices of size 3 - """ - from sage.misc.superseded import deprecation - deprecation(14301,'this class is deprecated. Use sage.combinat.alternating_sign_matrix.AlternatingSignMatrices instead') - return AlternatingSignMatrices(n) - -def MonotoneTriangles_n(n): - """ - For old pickles of ``MonotoneTriangles_n``. - - EXAMPLES:: - - sage: sage.combinat.alternating_sign_matrix.MonotoneTriangles_n(3) - doctest:...: DeprecationWarning: this class is deprecated. Use sage.combinat.alternating_sign_matrix.MonotoneTriangles instead - See http://trac.sagemath.org/14301 for details. - Monotone triangles with 3 rows - """ - from sage.misc.superseded import deprecation - deprecation(14301,'this class is deprecated. Use sage.combinat.alternating_sign_matrix.MonotoneTriangles instead') - return MonotoneTriangles(n) - -from sage.structure.sage_object import register_unpickle_override -register_unpickle_override('sage.combinat.alternating_sign_matrix', 'AlternatingSignMatrices_n', AlternatingSignMatrices) -register_unpickle_override('sage.combinat.alternating_sign_matrix', 'MonotoneTriangles_n', MonotoneTriangles) -register_unpickle_override('sage.combinat.alternating_sign_matrix', 'MonotoneTriangles_n', MonotoneTriangles_n) - -# Here are the previous implementations of the combinatorial structure -# of the alternating sign matrices. Please, consider it obsolete and -# tend to use the monotone triangles instead. - -def from_contre_tableau(comps): - r""" - Returns an alternating sign matrix from a contre-tableau. - - EXAMPLES:: - - sage: import sage.combinat.alternating_sign_matrix as asm - sage: asm.from_contre_tableau([[1, 2, 3], [1, 2], [1]]) - doctest:...: DeprecationWarning: You can use from_monotone_triangle instead. - See http://trac.sagemath.org/12930 for details. - [0 0 1] - [0 1 0] - [1 0 0] - sage: asm.from_contre_tableau([[1, 2, 3], [2, 3], [3]]) - [1 0 0] - [0 1 0] - [0 0 1] - """ - from sage.misc.superseded import deprecation - deprecation(12930, 'You can use from_monotone_triangle instead.') - n = len(comps) - MS = MatrixSpace(ZZ, n) - M = [ [0 for _ in range(n)] for _ in range(n) ] - - previous_set = Set([]) - - for col in range(n-1, -1, -1): - s = Set( comps[col] ) - for x in s - previous_set: - M[x-1][col] = 1 - - for x in previous_set - s: - M[x-1][col] = -1 - - previous_set = s - - return MS(M) - - -class ContreTableaux(Parent): - """ - Factory class for the combinatorial class of contre tableaux of size `n`. - - EXAMPLES:: - - sage: ct4 = ContreTableaux(4); ct4 - Contre tableaux of size 4 - sage: ct4.cardinality() - 42 - """ - __metaclass__ = ClasscallMetaclass - @staticmethod - def __classcall_private__(cls, n, **kwds): - r""" - Factory pattern. - - Check properties on arguments, then call the appropriate class. - - EXAMPLES:: - - sage: C = ContreTableaux(4) - sage: type(C) - - - """ - assert(isinstance(n, (int, Integer))) - return ContreTableaux_n(n, **kwds) - - -class ContreTableaux_n(ContreTableaux): - def __init__(self, n): - """ - TESTS:: - - sage: ct2 = ContreTableaux(2); ct2 - Contre tableaux of size 2 - sage: ct2 == loads(dumps(ct2)) - True - """ - self.n = n - - def __repr__(self): - """ - TESTS:: - - sage: repr(ContreTableaux(2)) - 'Contre tableaux of size 2' - """ - return "Contre tableaux of size %s"%self.n - - def __eq__(self, other): - """ - TESTS:: - - sage: C = ContreTableaux(4) - sage: C == loads(dumps(C)) - True - - """ - return self.n == other.n - - def cardinality(self): - """ - EXAMPLES:: - - sage: [ ContreTableaux(n).cardinality() for n in range(0, 11)] - [1, 1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460, 129534272700] - """ - return prod( [ factorial(3*k+1)/factorial(self.n+k) for k in range(self.n)] ) - - def _iterator_rec(self, i): - """ - EXAMPLES:: - - sage: c = ContreTableaux(2) - sage: list(c._iterator_rec(0)) - [[]] - sage: list(c._iterator_rec(1)) - [[[1, 2]]] - sage: list(c._iterator_rec(2)) - [[[1, 2], [1]], [[1, 2], [2]]] - """ - if i == 0: - yield [] - elif i == 1: - yield [range(1, self.n+1)] - else: - for columns in self._iterator_rec(i-1): - previous_column = columns[-1] - for column in _next_column_iterator(previous_column, len(previous_column)-1): - yield columns + [ column ] - - def __iter__(self): - """ - EXAMPLES:: - - sage: list(ContreTableaux(0)) - [[]] - sage: list(ContreTableaux(1)) - [[[1]]] - sage: list(ContreTableaux(2)) - [[[1, 2], [1]], [[1, 2], [2]]] - sage: list(ContreTableaux(3)) - [[[1, 2, 3], [1, 2], [1]], - [[1, 2, 3], [1, 2], [2]], - [[1, 2, 3], [1, 3], [1]], - [[1, 2, 3], [1, 3], [2]], - [[1, 2, 3], [1, 3], [3]], - [[1, 2, 3], [2, 3], [2]], - [[1, 2, 3], [2, 3], [3]]] - """ - - for z in self._iterator_rec(self.n): - yield z - - -def _next_column_iterator(previous_column, height, i = None): - """ - Returns a generator for all columns of height height properly - filled from row 1 to ``i`` - - EXAMPLES:: - - sage: import sage.combinat.alternating_sign_matrix as asm - sage: list(asm._next_column_iterator([1], 0)) - [[]] - sage: list(asm._next_column_iterator([1,5],1)) - [[1], [2], [3], [4], [5]] - sage: list(asm._next_column_iterator([1,4,5],2)) - [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]] - """ - if i is None: - i = height - if i == 0: - yield [-1]*height - else: - for column in _next_column_iterator(previous_column, height, i-1): - min_value = previous_column[i-1] - if i > 1: - min_value = max(min_value, column[i-2]+1) - for value in range(min_value, previous_column[i]+1): - c = copy.copy(column) - c[i-1] = value - yield c - - -def _previous_column_iterator(column, height, max_value): - """ - EXAMPLES:: - - sage: import sage.combinat.alternating_sign_matrix as asm - sage: list(asm._previous_column_iterator([2,3], 3, 4)) - [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] - """ - new_column = [1] + column + [ max_value ] * (height - len(column)) - return _next_column_iterator(new_column, height) - - -class TruncatedStaircases(Parent): - """ - Factory class for the combinatorial class of truncated staircases - of size ``n`` with last column ``last_column``. - - EXAMPLES:: - - sage: t4 = TruncatedStaircases(4, [2,3]); t4 - Truncated staircases of size 4 with last column [2, 3] - sage: t4.cardinality() - 4 - """ - __metaclass__ = ClasscallMetaclass - @staticmethod - def __classcall_private__(cls, n, last_column, **kwds): - r""" - Factory pattern. - - Check properties on arguments, then call the appropriate class. - - TESTS:: - - sage: T = TruncatedStaircases(4, [2,3]) - sage: type(T) - - - """ - assert(isinstance(n, (int, Integer))) - return TruncatedStaircases_nlastcolumn(n, last_column, **kwds) - - -class TruncatedStaircases_nlastcolumn(TruncatedStaircases): - def __init__(self, n, last_column): - """ - TESTS:: - - sage: t4 = TruncatedStaircases(4, [2,3]); t4 - Truncated staircases of size 4 with last column [2, 3] - sage: t4 == loads(dumps(t4)) - True - """ - self.n = n - self.last_column = last_column - - def __repr__(self): - """ - TESTS:: - - sage: repr(TruncatedStaircases(4, [2,3])) - 'Truncated staircases of size 4 with last column [2, 3]' - """ - return "Truncated staircases of size %s with last column %s"%(self.n, self.last_column) - - def _iterator_rec(self, i): - """ - EXAMPLES:: - - sage: t = TruncatedStaircases(3, [2,3]) - sage: list(t._iterator_rec(1)) - [] - sage: list(t._iterator_rec(2)) - [[[2, 3]]] - sage: list(t._iterator_rec(3)) - [[[1, 2, 3], [2, 3]]] - """ - if i < len(self.last_column): - return - elif i == len(self.last_column): - yield [self.last_column] - else: - for columns in self._iterator_rec(i-1): - previous_column = columns[0] - for column in _previous_column_iterator(previous_column, len(previous_column)+1, self.n): - yield [column] + columns - - def __iter__(self): - """ - EXAMPLES::: - - sage: list(TruncatedStaircases(4, [2,3])) - [[[4, 3, 2, 1], [3, 2, 1], [3, 2]], [[4, 3, 2, 1], [4, 2, 1], [3, 2]], [[4, 3, 2, 1], [4, 3, 1], [3, 2]], [[4, 3, 2, 1], [4, 3, 2], [3, 2]]] - """ - for z in self._iterator_rec(self.n): - yield map(lambda x: list(reversed(x)), z) - - def __eq__(self, other): - r""" - TESTS:: - - sage: T = TruncatedStaircases(4, [2,3]) - sage: T == loads(dumps(T)) - True - """ - return ((self.n == other.n) and - (self.last_column == other.last_column)) - - def cardinality(self): - r""" - EXAMPLES:: - - sage: T = TruncatedStaircases(4, [2,3]) - sage: T.cardinality() - 4 - """ - c = 0 - for _ in self: - c += 1 - return c - +r""" +Alternating Sign Matrices + +AUTHORS: + +- Mike Hansen (2007): Initial version +- Pierre Cange, Luis Serrano (2012): Added monotone triangles +- Travis Scrimshaw (2013-28-03): Added element class for ASM's and made + :class:`MonotoneTriangles` inherit from :class:`GelfandTsetlinPatterns`. +- Jessica Striker (2013): Added methods. +""" +#***************************************************************************** +# Copyright (C) 2007 Mike Hansen , +# 2012 Pierre Cagne , +# Luis Serrano +# 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/ +#***************************************************************************** + +import itertools +import copy +from sage.misc.classcall_metaclass import ClasscallMetaclass +from sage.misc.flatten import flatten +from sage.misc.misc import prod +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent +from sage.structure.element import Element +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.matrix.matrix_space import MatrixSpace +from sage.matrix.constructor import matrix +from sage.rings.all import ZZ, factorial +from sage.rings.integer import Integer +from sage.combinat.posets.lattices import LatticePoset +from sage.combinat.gelfand_tsetlin_patterns import GelfandTsetlinPatternsTopRow +from sage.sets.set import Set +from sage.combinat.combinatorial_map import combinatorial_map +from sage.combinat.non_decreasing_parking_function import NonDecreasingParkingFunction +from sage.combinat.permutation import Permutation + +class AlternatingSignMatrix(Element): + r""" + An alternating sign matrix. + + An alternating sign matrix is a square matrix of `0`'s, `1`'s and `-1`'s + such that the sum of each row and column is `1` and the non-zero + entries in each row and column alternate in sign. + """ + __metaclass__ = ClasscallMetaclass + + @staticmethod + def __classcall_private__(cls, asm): + """ + Create an ASM. + + EXAMPLES:: + + sage: AlternatingSignMatrix([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + [1 0 0] + [0 1 0] + [0 0 1] + """ + asm = matrix(asm) + if not asm.is_square(): + raise ValueError("The alternating sign matrices must be square") + P = AlternatingSignMatrices(asm.nrows()) + if asm not in P: + raise ValueError("Invalid alternating sign matrix") + return P(asm) + + def __init__(self, parent, asm): + """ + Initialize ``self``. + + EXAMPLES: + + sage: A = AlternatingSignMatrices(3) + sage: elt = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + sage: TestSuite(elt).run() + """ + self._matrix = asm + Element.__init__(self, parent) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + [1 0 0] + [0 1 0] + [0 0 1] + """ + return repr(self._matrix) + + def __eq__(self, other): + """ + Check equality. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: M = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + sage: M == A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + True + sage: M == A([[1, 0, 0],[0, 0, 1],[0, 1, 0]]) + False + """ + if isinstance(other, AlternatingSignMatrix): + return self._matrix == other._matrix + return self._matrix == other + + def __ne__(self, other): + """ + Check not equals. This is needed, see :trac:`14762`. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: M = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + sage: M != A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + False + sage: M != A([[1, 0, 0],[0, 0, 1],[0, 1, 0]]) + True + """ + return not self.__eq__(other) + + def _latex_(self): + r""" + Return a `\LaTeX` representation of ``self``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: latex(A([[1, 0, 0],[0, 1, 0],[0, 0, 1]])) + \left(\begin{array}{rrr} + 1 & 0 & 0 \\ + 0 & 1 & 0 \\ + 0 & 0 & 1 + \end{array}\right) + """ + return self._matrix._latex_() + + def to_matrix(self): + """ + Return ``self`` as a regular matrix. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: asm = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + sage: m = asm.to_matrix(); m + [1 0 0] + [0 1 0] + [0 0 1] + sage: m.parent() + Full MatrixSpace of 3 by 3 dense matrices over Integer Ring + """ + return copy.copy(self._matrix) + + def to_monotone_triangle(self): + r""" + Return a monotone triangle from ``self``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]).to_monotone_triangle() + [[3, 2, 1], [2, 1], [1]] + sage: asm = A([[0, 1, 0],[1, -1, 1],[0, 1, 0]]) + sage: asm.to_monotone_triangle() + [[3, 2, 1], [3, 1], [2]] + sage: asm = A([[0, 0, 1],[1, 0, 0],[0, 1, 0]]) + sage: asm.to_monotone_triangle() + [[3, 2, 1], [3, 1], [3]] + sage: A.from_monotone_triangle(asm.to_monotone_triangle()) == asm + True + """ + n = self._matrix.nrows() + triangle = [None]*n + prev = [0]*n + for j, row in enumerate(self._matrix): + add_row = [a+b for (a,b) in itertools.izip(row, prev)] + line = [i+1 for (i,val) in enumerate(add_row) if val==1] + triangle[n-1-j] = list(reversed(line)) + prev = add_row + return MonotoneTriangles(n)(triangle) + + def corner_sum_matrix(self): + r""" + Return the corner sum matrix from ``self``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]).corner_sum_matrix() + [0 0 0 0] + [0 1 1 1] + [0 1 2 2] + [0 1 2 3] + sage: asm = A([[0, 1, 0],[1, -1, 1],[0, 1, 0]]) + sage: asm.corner_sum_matrix() + [0 0 0 0] + [0 0 1 1] + [0 1 1 2] + [0 1 2 3] + sage: asm = A([[0, 0, 1],[1, 0, 0],[0, 1, 0]]) + sage: asm.corner_sum_matrix() + [0 0 0 0] + [0 0 1 1] + [0 0 1 2] + [0 1 2 3] + """ + asm = self.to_matrix() + return matrix([[nw_corner_sum(asm,i,j) for i in range(0,len(list(asm))+1)] for j in range(0,len(list(asm))+1)]) + + def height_function(self): + r""" + Return the height function from ``self``. A height function corresponding + to an nxn ASM is an (n+1)x(n+1) matrix such that the first row is 0,1,...,n, + the last row is n,n-1,...,1,0, and the difference between adjacent entries is 1. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]).height_function() + [0 1 2 3] + [1 0 1 2] + [2 1 0 1] + [3 2 1 0] + sage: asm = A([[0, 1, 0],[1, -1, 1],[0, 1, 0]]) + sage: asm.height_function() + [0 1 2 3] + [1 2 1 2] + [2 1 2 1] + [3 2 1 0] + sage: asm = A([[0, 0, 1],[1, 0, 0],[0, 1, 0]]) + sage: asm.height_function() + [0 1 2 3] + [1 2 1 2] + [2 3 2 1] + [3 2 1 0] + """ + asm = self.to_matrix() + return matrix([[i+j-2*nw_corner_sum(asm,i,j) for i in range(len(list(asm))+1)] for j in range(len(list(asm))+1)]) + + def gyration(self): + r""" + Return the matrix obtained by applying the gyration action to the height function + in bijection with ``self``. + + Gyration acts on height functions as follows. Go through the entries of the matrix, + first those for which the sum of the row and column indices is even, then for those + for which it is odd, and increment or decrement the squares by 2 wherever possible + such that the resulting matrix is still a height function. Gyration was first defined + in [Wieland00] as an action on fully-packed loops. + + REFERENCES: + + .. [Wieland00] B. Wieland. *A large dihedral symmetry of the set of alternating sign matrices*. + Electron. J. Combin. 7 (2000). + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]).gyration() + [0 0 1] + [0 1 0] + [1 0 0] + sage: asm = A([[0, 1, 0],[1, -1, 1],[0, 1, 0]]) + sage: asm.gyration() + [1 0 0] + [0 1 0] + [0 0 1] + sage: asm = A([[0, 0, 1],[1, 0, 0],[0, 1, 0]]) + sage: asm.gyration() + [0 1 0] + [0 0 1] + [1 0 0] + """ + A = AlternatingSignMatrices(self.to_matrix().nrows()) + hf = list(self.height_function()) + for i in range(1,len(hf)-1): + for j in range(1,len(hf)-1): + if (i+j)%2==0: + if hf[i-1][j] == hf[i+1][j] == hf[i][j+1] == hf[i][j-1]: + if hf[i][j] < hf[i+1][j]: + hf[i][j] = hf[i][j] + 2 + else: + hf[i][j] = hf[i][j] - 2 + for i in range(1,len(hf)-1): + for j in range(1,len(hf)-1): + if (i+j)%2==1: + if hf[i-1][j] == hf[i+1][j] == hf[i][j+1] == hf[i][j-1]: + if hf[i][j] < hf[i+1][j]: + hf[i][j] = hf[i][j] + 2 + else: + hf[i][j] = hf[i][j] - 2 + return A.from_height_function(matrix(hf)) + + def ASM_compatible(self,B): + r""" + Returns ``true`` if ``self`` and B are compatible alternating sign matrices. + + EXAMPLES:: + + sage: A = AlternatingSignMatrix(matrix([[0,0,1,0],[0,1,-1,1],[1,0,0,0],[0,0,1,0]])) + sage: B = AlternatingSignMatrix(matrix([[0,0,1,0,0],[0,0,0,1,0],[1,0,0,-1,1],[0,1,0,0,0],[0,0,0,1,0]])) + sage: A.ASM_compatible(B) + True + sage: A = AlternatingSignMatrix(matrix([[0,1,0],[1,-1,1],[0,1,0]])) + sage: B = AlternatingSignMatrix(matrix([[0,0,1,0],[0,0,0,1],[1,0,0,0],[0,1,0,0]])) + sage: A.ASM_compatible(B) + False + """ + if len(B.to_matrix()[0])-len(self.to_matrix()[0])!=1: + return "error" + AA=self.corner_sum_matrix() + BB=B.corner_sum_matrix() + for i in range(0,len(AA[0])): + for j in range(0,len(AA[0])): + if not (AA[i,j]>=BB[i,j] and AA[i,j]>=BB[i+1,j+1]-1 and AA[i,j]<=BB[i+1,j] and AA[i,j]<=BB[i,j+1]): + return False + return True + + def ASM_compatible_bigger(self): + r""" + Returns the list of larger ASMs that are compatible with ``self`` . + + EXAMPLES:: + sage: A = AlternatingSignMatrix(matrix([[1,0],[0,1]])) + sage: A.ASM_compatible_bigger() + [ + [ 0 1 0] [1 0 0] [0 1 0] [1 0 0] + [ 1 -1 1] [0 0 1] [1 0 0] [0 1 0] + [ 0 1 0], [0 1 0], [0 0 1], [0 0 1] + ] + sage: B = AlternatingSignMatrix(matrix([[0,1],[1,0]])) + sage: B.ASM_compatible_bigger() + [ + [0 0 1] [0 0 1] [0 1 0] [ 0 1 0] + [0 1 0] [1 0 0] [0 0 1] [ 1 -1 1] + [1 0 0], [0 1 0], [1 0 0], [ 0 1 0] + ] + """ + n=len(self.to_matrix()[0]) + M=AlternatingSignMatrices(n+1) + sign=[] + B=matrix(n+2) + A=matrix([[2*(i+j-2*nw_corner_sum(self.to_matrix(),i,j))+1 for i in range(n+1)] for j in range(n+1)]) + for a in range(0,n+2): + B[a,0]=2*a + B[0,a]=2*a + B[a,n+1]=2*(n+1-a) + B[n+1,a]=2*(n+1-a) + for i in range(1,n+1): + for j in range(1,n+1): + if (A[i-1,j-1]==A[i,j]==A[i-1,j]-2==A[i,j-1]-2): + B[i,j]=-A[i,j] + sign.append([i,j]) + else: + B[i,j]=list({A[i-1,j-1]-1,A[i-1,j-1]+3} & {A[i-1,j]-3,A[i-1,j]+1} & {A[i,j-1]-3,A[i,j-1]+1} & {A[i,j]-1,A[i,j]+3})[0] + output=[B] + for b in range(0,len(sign)): + N=len(output) + for c in range(0,N): + d=copy.copy(output[c]) + output[c][sign[b][0],sign[b][1]]=-output[c][sign[b][0],sign[b][1]]+3 + d[sign[b][0],sign[b][1]]=-d[sign[b][0],sign[b][1]]-1 + output.append(d) + for k in range(0,len(output)): + output[k]=M.from_height_function(output[k]/2) + return(output) + + def ASM_compatible_smaller(self): + r""" + Returns the list of larger ASMs that are compatible with ``self`` . + + EXAMPLES:: + + sage: A = AlternatingSignMatrix(matrix([[0,0,1,0],[0,1,-1,1],[1,0,0,0],[0,0,1,0]])) + sage: A.ASM_compatible_smaller() + [ + [0 0 1] [ 0 1 0] + [1 0 0] [ 1 -1 1] + [0 1 0], [ 0 1 0] + ] + sage: B = AlternatingSignMatrix(matrix([[1,0,0],[0,0,1],[0,1,0]])) + sage: B.ASM_compatible_smaller() + [ + [1 0] + [0 1] + ] + + """ + n=len(self.to_matrix()[0]) + M=AlternatingSignMatrices(n) + A=matrix(n) + B=matrix([[2*(i+j-2*nw_corner_sum(self.to_matrix(),i,j)) for i in range(n)] for j in range(n)]) + sign=[] + for a in range(0,n): + A[a,0]=2*a+1 + A[0,a]=2*a+1 + A[n-1,a]=2*(n-a)-1 + A[a,n-1]=2*(n-a)-1 + for i in range(0,n-1): + for j in range(0,n-1): + if B[i+1,j+1]==B[i,j]==B[i,j+1]+2==B[i+1,j]+2: + A[i,j]=-B[i,j] + sign.append([i,j]) + else: + A[i,j]=list({B[i,j]+1,B[i,j]-3} & {B[i,j+1]+3,B[i,j+1]-1} & {B[i+1,j]+3,B[i+1,j]-1} & {B[i+1,j+1]+1,B[i+1,j+1]-3})[0] + output=[A] + for b in range(0,len(sign)): + N=len(output) + for c in range(0,N): + d=copy.copy(output[c]) + output[c][sign[b][0],sign[b][1]]=-output[c][sign[b][0],sign[b][1]]+1 + d[sign[b][0],sign[b][1]]=-d[sign[b][0],sign[b][1]]-3 + output.append(d) + for k in range(0,len(output)): + output[k]=M.from_height_function((output[k]-matrix.ones(n,n))/2) + return(output) + + @combinatorial_map(name='to Dyck word') + def to_dyck_word(self): + r""" + Return the Dyck word determined by the last diagonal of + the monotone triangle corresponding to ``self``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[0,1,0],[1,0,0],[0,0,1]]).to_dyck_word() + [1, 1, 0, 0, 1, 0] + sage: d = A([[0,1,0],[1,-1,1],[0,1,0]]).to_dyck_word(); d + [1, 1, 0, 1, 0, 0] + sage: parent(d) + Complete Dyck words + """ + MT = self.to_monotone_triangle() + nplus = self._matrix.nrows() + 1 + parkfn = [nplus - row[0] for row in list(MT) if len(row) > 0] + return NonDecreasingParkingFunction(parkfn).to_dyck_word().reverse() + + def number_negative_ones(self): + """ + Return the number of entries in ``self`` equal to -1. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: asm = A([[0,1,0],[1,0,0],[0,0,1]]) + sage: asm.number_negative_ones() + 0 + sage: asm = A([[0,1,0],[1,-1,1],[0,1,0]]) + sage: asm.number_negative_ones() + 1 + """ + a = self._matrix + return sum(1 for (i,j) in a.nonzero_positions() if a[i,j] == -1) + + def is_permutation(self): + """ + Return ``True`` if ``self`` is a permutation matrix + and ``False`` otherwise. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: asm = A([[0,1,0],[1,0,0],[0,0,1]]) + sage: asm.is_permutation() + True + sage: asm = A([[0,1,0],[1,-1,1],[0,1,0]]) + sage: asm.is_permutation() + False + """ + return self.number_negative_ones() == 0 + + def to_permutation(self): + """ + Return the corresponding permutation if ``self`` is a permutation + matrix. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: asm = A([[0,1,0],[1,0,0],[0,0,1]]) + sage: p = asm.to_permutation(); p + [2, 1, 3] + sage: parent(p) + Standard permutations + sage: asm = A([[0,1,0],[1,-1,1],[0,1,0]]) + sage: asm.to_permutation() + Traceback (most recent call last): + ... + ValueError: Not a permutation matrix + """ + if not self.is_permutation(): + raise ValueError('Not a permutation matrix') + asm_matrix = self.to_matrix() + return Permutation([ j+1 for (i,j) in asm_matrix.nonzero_positions() ]) + + @combinatorial_map(name='to semistandard tableau') + def to_semistandard_tableau(self): + """ + Return the semistandard tableau corresponding the monotone triangle + corresponding to ``self``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[0,0,1],[1,0,0],[0,1,0]]).to_semistandard_tableau() + [[1, 1, 3], [2, 3], [3]] + sage: t = A([[0,1,0],[1,-1,1],[0,1,0]]).to_semistandard_tableau(); t + [[1, 1, 2], [2, 3], [3]] + sage: parent(t) + Semistandard tableaux + """ + from sage.combinat.tableau import SemistandardTableau, SemistandardTableaux + mt = self.to_monotone_triangle() + ssyt = [[0]*(len(mt) - j) for j in range(len(mt))] + for i in range(len(mt)): + for j in range(len(mt[i])): + ssyt[i][j] = mt[j][-(i+1)] + return SemistandardTableau(ssyt) + + def left_key(self): + r""" + Return the left key of the alternating sign matrix ``self``. + + The left key of an alternating sign matrix was defined by Lascoux + in [LascouxPreprint]_ and is obtained by successively removing all the + `-1`'suntil what remains is a permutation matrix. This notion + corresponds to the notion of left key for semistandard tableaux. So + our algorithm proceeds as follows: we map ``self`` to its + corresponding monotone triangle, view that monotone triangle as a + semistandard tableaux, take its left key, and then map back through + monotone triangles to the permutation matrix which is the left key. + + REFERENCES: + + .. [Aval07] J.-C. Aval. *Keys and alternating sign matrices*. + Sem. Lothar. Combin. 59 (2007/10), Art. B59f, 13 pp. + + .. [LascouxPreprint] A. Lascoux. *Chern and Yang through ice*. + Preprint. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[0,0,1],[1,0,0],[0,1,0]]).left_key() + [0 0 1] + [1 0 0] + [0 1 0] + sage: t = A([[0,1,0],[1,-1,1],[0,1,0]]).left_key(); t + [1 0 0] + [0 0 1] + [0 1 0] + sage: parent(t) + Alternating sign matrices of size 3 + """ + from sage.combinat.tableau import SemistandardTableau, SemistandardTableaux + lkey = self.to_semistandard_tableau().left_key_tableau() + mt = [[0]*(len(lkey) - j) for j in range(len(lkey))] + for i in range(len(lkey)): + for j in range(len(lkey[i])): + mt[i][j] = lkey[len(lkey[i])-j-1][i] + A = AlternatingSignMatrices(len(lkey)) + return A.from_monotone_triangle(mt) + + @combinatorial_map(name='to left key permutation') + def left_key_as_permutation(self): + """ + Return the permutation of the left key of ``self``. + + .. SEEALSO:: + + - :meth:`left_key()` + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[0,0,1],[1,0,0],[0,1,0]]).left_key_as_permutation() + [3, 1, 2] + sage: t = A([[0,1,0],[1,-1,1],[0,1,0]]).left_key_as_permutation(); t + [1, 3, 2] + sage: parent(t) + Standard permutations + """ + return self.left_key().to_permutation() + +class AlternatingSignMatrices(Parent, UniqueRepresentation): + r""" + Class of all `n \times n` alternating sign matrices. + + An alternating sign matrix of size `n` is an `n \times n` matrix of `0`'s, + `1`'s and `-1`'s such that the sum of each row and column is `1` and the + non-zero entries in each row and column alternate in sign. + + Alternating sign matrices of size `n` are in bijection with + :class:`monotone triangles ` with `n` rows. + + INPUT: + + - `n` -- an integer, the size of the matrices. + + - ``use_monotone_triangle`` -- (Default: ``True``) If ``True``, the + generation of the matrices uses monotone triangles, else it will use the + earlier and now obsolete contre-tableaux implementation; + must be ``True`` to generate a lattice (with the ``lattice`` method) + + EXAMPLES: + + This will create an instance to manipulate the alternating sign + matrices of size 3:: + + sage: A = AlternatingSignMatrices(3) + sage: A + Alternating sign matrices of size 3 + sage: A.cardinality() + 7 + + Notably, this implementation allows to make a lattice of it:: + + sage: L = A.lattice() + sage: L + Finite lattice containing 7 elements + sage: L.category() + Category of facade finite lattice posets + """ + def __init__(self, n, use_monotone_triangles=True): + r""" + Initialize ``self``. + + TESTS:: + + sage: A = AlternatingSignMatrices(4) + sage: TestSuite(A).run() + sage: A == AlternatingSignMatrices(4, use_monotone_triangles=False) + False + """ + self._n = n + self._matrix_space = MatrixSpace(ZZ, n) + self._umt = use_monotone_triangles + Parent.__init__(self, category=FiniteEnumeratedSets()) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + TESTS:: + + sage: A = AlternatingSignMatrices(4); A + Alternating sign matrices of size 4 + """ + return "Alternating sign matrices of size %s" % self._n + + def _repr_option(self, key): + """ + Metadata about the :meth:`_repr_` output. + + See :meth:`sage.structure.parent._repr_option` for details. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A._repr_option('element_ascii_art') + True + """ + return self._matrix_space._repr_option(key) + + def __contains__(self, asm): + """ + Check if ``asm`` is in ``self``. + + TESTS:: + + sage: A = AlternatingSignMatrices(3) + sage: [[0,1,0],[1,0,0],[0,0,1]] in A + True + sage: [[0,1,0],[1,-1,1],[0,1,0]] in A + True + sage: [[0, 1],[1,0]] in A + False + sage: [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]] in A + False + sage: [[-1, 1, 1],[1,-1,1],[1,1,-1]] in A + False + """ + if isinstance(asm, AlternatingSignMatrix): + return asm._matrix.nrows() == self._n + try: + asm = self._matrix_space(asm) + except (TypeError, ValueError): + return False + for row in asm: + pos = False + for val in row: + if val > 0: + if pos: + return False + else: + pos = True + elif val < 0: + if pos: + pos = False + else: + return False + if not pos: + return False + if any(sum(row) != 1 for row in asm.columns()): + return False + return True + + def _element_constructor_(self, asm): + """ + Construct an element of ``self``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: elt = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]); elt + [1 0 0] + [0 1 0] + [0 0 1] + sage: elt.parent() is A + True + sage: A([[3, 2, 1], [2, 1], [1]]) + [1 0 0] + [0 1 0] + [0 0 1] + """ + if isinstance(asm, AlternatingSignMatrix): + if asm.parent() is self: + return asm + raise ValueError("Cannot convert between alternating sign matrices of different sizes") + if asm in MonotoneTriangles(self._n): + return self.from_monotone_triangle(asm) + return self.element_class(self, self._matrix_space(asm)) + + Element = AlternatingSignMatrix + + def _an_element_(self): + """ + Return an element of ``self``. + """ + return self.element_class(self, self._matrix_space.identity_matrix()) + + def from_monotone_triangle(self, triangle): + r""" + Return an alternating sign matrix from a monotone triangle. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A.from_monotone_triangle([[3, 2, 1], [2, 1], [1]]) + [1 0 0] + [0 1 0] + [0 0 1] + sage: A.from_monotone_triangle([[3, 2, 1], [3, 2], [3]]) + [0 0 1] + [0 1 0] + [1 0 0] + """ + n = len(triangle) + if n != self._n: + raise ValueError("Incorrect size") + asm = [] + + prev = [0]*n + for line in reversed(triangle): + v = [1 if j+1 in reversed(line) else 0 for j in range(n)] + row = [a-b for (a, b) in zip(v, prev)] + asm.append(row) + prev = v + + return self.element_class(self, self._matrix_space(asm)) + + def from_corner_sum(self,corner): + r""" + Return an alternating sign matrix from a corner sum matrix. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A.from_corner_sum(matrix([[0,0,0,0],[0,1,1,1],[0,1,2,2],[0,1,2,3]])) + [1 0 0] + [0 1 0] + [0 0 1] + sage: A.from_corner_sum(matrix([[0,0,0,0],[0,0,1,1],[0,1,1,2],[0,1,2,3]])) + [ 0 1 0] + [ 1 -1 1] + [ 0 1 0] + """ + asm_list=[] + n=len(list(corner))-1 + for k in range(n): + asm_list.append([]) + for i in range(n): + for j in range(n): + y=corner[i+1][j+1]-sum([sum([asm_list[i2][j2] for i2 in range(i)]) for j2 in range(j)])-sum([asm_list[i2][j] for i2 in range(i)])-sum([asm_list[i][j2] for j2 in range(j)]) + asm_list[i].append(y) + return AlternatingSignMatrix(asm_list) + + def from_height_function(self,height): + r""" + Return an alternating sign matrix from a height function. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A.from_height_function(matrix([[0,1,2,3],[1,2,1,2],[2,3,2,1],[3,2,1,0]])) + [0 0 1] + [1 0 0] + [0 1 0] + sage: A.from_height_function(matrix([[0,1,2,3],[1,2,1,2],[2,1,2,1],[3,2,1,0]])) + [ 0 1 0] + [ 1 -1 1] + [ 0 1 0] + """ + return self.from_corner_sum(matrix([[((i+j-height[i][j])/int(2)) for i in range(len(list(height)))] for j in range(len(list(height)))])) + + def size(self): + r""" + Return the size of the matrices in ``self``. + + TESTS:: + + sage: A = AlternatingSignMatrices(4) + sage: A.size() + 4 + """ + return self._n + + def cardinality(self): + r""" + Return the cardinality of ``self``. + + The number of `n \times n` alternating sign matrices is equal to + + .. MATH:: + + \prod_{k=0}^{n-1} \frac{(3k+1)!}{(n+k)!} = \frac{1! 4! 7! 10! + \cdots (3n-2)!}{n! (n+1)! (n+2)! (n+3)! \cdots (2n-1)!} + + EXAMPLES:: + + sage: [AlternatingSignMatrices(n).cardinality() for n in range(0, 11)] + [1, 1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460, 129534272700] + """ + return Integer(prod( [ factorial(3*k+1)/factorial(self._n+k) + for k in range(self._n)] )) + + def matrix_space(self): + """ + Return the underlying matrix space. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A.matrix_space() + Full MatrixSpace of 3 by 3 dense matrices over Integer Ring + """ + return self._matrix_space + + def __iter__(self): + r""" + Iterator on the alternating sign matrices of size `n`. + + If defined using ``use_monotone_triangles``, this iterator + will use the iteration on the monotone triangles. Else, it + will use the iteration on contre-tableaux. + + TESTS:: + + sage: A = AlternatingSignMatrices(4) + sage: len(list(A)) + 42 + """ + if self._umt: + for t in MonotoneTriangles(self._n): + yield self.from_monotone_triangle(t) + else: + for c in ContreTableaux(self._n): + yield from_contre_tableau(c) + + def _lattice_initializer(self): + r""" + Return a 2-tuple to use in argument of ``LatticePoset``. + + For more details about the cover relations, see + ``MonotoneTriangles``. Notice that the returned matrices are + made immutable to ensure their hashability required by + ``LatticePoset``. + + EXAMPLES: + + Proof of the lattice property for alternating sign matrices of + size 3:: + + sage: A = AlternatingSignMatrices(3) + sage: P = Poset(A._lattice_initializer()) + sage: P.is_lattice() + True + """ + assert(self._umt) + (mts, rels) = MonotoneTriangles(self._n)._lattice_initializer() + bij = dict((t, self.from_monotone_triangle(t)) for t in mts) + asms, rels = bij.itervalues(), [(bij[a], bij[b]) for (a,b) in rels] + return (asms, rels) + + def cover_relations(self): + r""" + Iterate on the cover relations between the alternating sign + matrices. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: for (a,b) in A.cover_relations(): + ... eval('a, b') + ... + ( + [1 0 0] [0 1 0] + [0 1 0] [1 0 0] + [0 0 1], [0 0 1] + ) + ( + [1 0 0] [1 0 0] + [0 1 0] [0 0 1] + [0 0 1], [0 1 0] + ) + ( + [0 1 0] [ 0 1 0] + [1 0 0] [ 1 -1 1] + [0 0 1], [ 0 1 0] + ) + ( + [1 0 0] [ 0 1 0] + [0 0 1] [ 1 -1 1] + [0 1 0], [ 0 1 0] + ) + ( + [ 0 1 0] [0 0 1] + [ 1 -1 1] [1 0 0] + [ 0 1 0], [0 1 0] + ) + ( + [ 0 1 0] [0 1 0] + [ 1 -1 1] [0 0 1] + [ 0 1 0], [1 0 0] + ) + ( + [0 0 1] [0 0 1] + [1 0 0] [0 1 0] + [0 1 0], [1 0 0] + ) + ( + [0 1 0] [0 0 1] + [0 0 1] [0 1 0] + [1 0 0], [1 0 0] + ) + + """ + (_, rels) = self._lattice_initializer() + return (_ for _ in rels) + + def lattice(self): + r""" + Return the lattice of the alternating sign matrices of size + `n`, created by ``LatticePoset``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: L = A.lattice() + sage: L + Finite lattice containing 7 elements + + """ + return LatticePoset(self._lattice_initializer(), cover_relations=True) + + +class MonotoneTriangles(GelfandTsetlinPatternsTopRow): + r""" + Monotone triangles with `n` rows. + + A monotone triangle is a number triangle `(a_{i,j})_{1 \leq i \leq + n , 1 \leq j \leq i}` on `\{1, \dots, n\}` such that: + + - `a_{i,j} < a_{i,j+1}` + + - `a_{i+1,j} < a_{i,j} \leq a_{i+1,j+1}` + + This notably requires that the bottom column is ``[1,...,n]``. + + Alternatively a monotone triangle is a strict Gelfand-Tsetlin pattern with + top row `(n, \ldots, 2, 1)`. + + INPUT: + + - ``n`` -- The number of rows in the monotone triangles + + EXAMPLES: + + This represents the monotone triangles with base ``[3,2,1]``:: + + sage: M = MonotoneTriangles(3) + sage: M + Monotone triangles with 3 rows + sage: M.cardinality() + 7 + + The monotone triangles are a lattice:: + + sage: M.lattice() + Finite lattice containing 7 elements + + Monotone triangles can be converted to alternating sign matrices + and back:: + + sage: M = MonotoneTriangles(5) + sage: A = AlternatingSignMatrices(5) + sage: all(A.from_monotone_triangle(m).to_monotone_triangle() == m for m in M) + True + """ + def __init__(self, n): + r""" + Initialize ``self``. + + TESTS:: + + sage: M = MonotoneTriangles(4) + sage: TestSuite(M).run() + sage: M2 = MonotoneTriangles(int(4)) + sage: M is M2 + True + """ + GelfandTsetlinPatternsTopRow.__init__(self, tuple(reversed(range(1, n+1))), True) + + def _repr_(self): + r""" + String representation. + + TESTS:: + + sage: M = MonotoneTriangles(4) + sage: M + Monotone triangles with 4 rows + """ + return "Monotone triangles with %s rows" % self._n + + def cardinality(self): + r""" + Cardinality of ``self``. + + The number of monotone triangles with `n` rows is equal to + + .. MATH:: + + \prod_{k=0}^{n-1} \frac{(3k+1)!}{(n+k)!} = \frac{1! 4! 7! 10! + \cdots (3n-2)!}{n! (n+1)! (n+2)! (n+3)! \cdots (2n-1)!} + + EXAMPLES:: + + sage: M = MonotoneTriangles(4) + sage: M.cardinality() + 42 + """ + return Integer(prod( [ factorial(3*k+1)/factorial(self._n+k) + for k in range(self._n)] )) + + def _lattice_initializer(self): + r""" + Return a 2-tuple to use in argument of ``LatticePoset``. + + This couple is composed by the set of the monotone triangles + with `n` rows and the cover relations. Specializing this + function allows to generate the monotone triangles just once, + and so to speed up the computation in comparison of + ``(list(self), self.cover_relations())``. Notice that the + function also switch the representation of monotone triangles + from list of list to tuple of tuple in order to make them + hashable (required to make a poset with them). + + EXAMPLES:: + + sage: M = MonotoneTriangles(3) + sage: P = Poset(M._lattice_initializer()) + sage: P.is_lattice() + True + """ + # get a list of the elements and switch to a tuple + # representation + set_ = list(self) + set_ = map(lambda x: tuple(map(tuple, x)), set_) + return (set_, [(a,b) for a in set_ for b in set_ if _is_a_cover(a,b)]) + + def cover_relations(self): + r""" + Iterate on the cover relations in the set of monotone triangles + with `n` rows. + + EXAMPLES:: + + sage: M = MonotoneTriangles(3) + sage: for (a,b) in M.cover_relations(): + ... eval('a, b') + ... + ([[3, 2, 1], [2, 1], [1]], [[3, 2, 1], [2, 1], [2]]) + ([[3, 2, 1], [2, 1], [1]], [[3, 2, 1], [3, 1], [1]]) + ([[3, 2, 1], [2, 1], [2]], [[3, 2, 1], [3, 1], [2]]) + ([[3, 2, 1], [3, 1], [1]], [[3, 2, 1], [3, 1], [2]]) + ([[3, 2, 1], [3, 1], [2]], [[3, 2, 1], [3, 1], [3]]) + ([[3, 2, 1], [3, 1], [2]], [[3, 2, 1], [3, 2], [2]]) + ([[3, 2, 1], [3, 1], [3]], [[3, 2, 1], [3, 2], [3]]) + ([[3, 2, 1], [3, 2], [2]], [[3, 2, 1], [3, 2], [3]]) + """ + set_ = list(self) + return ((a,b) for a in set_ for b in set_ if _is_a_cover(a,b)) + + def lattice(self): + r""" + Return the lattice of the monotone triangles with `n` rows. + + EXAMPLES:: + + sage: M = MonotoneTriangles(3) + sage: P = M.lattice() + sage: P + Finite lattice containing 7 elements + + """ + return LatticePoset(self._lattice_initializer(), cover_relations=True) + +def _is_a_cover(mt0, mt1): + r""" + Define the cover relations. + + Return ``True`` if and only if the second argument is a cover of + the first one. + + EXAMPLES:: + + sage: import sage.combinat.alternating_sign_matrix as asm + sage: asm._is_a_cover([[1,2,3],[1,2],[1]], [[1,2,3],[1,3],[1]]) + True + sage: asm._is_a_cover([[1,2,3],[1,3],[2]], [[1,2,3],[1,2],[1]]) + False + """ + diffs = 0 + for (a,b) in itertools.izip(flatten(mt0), flatten(mt1)): + if a != b: + if a+1 == b: + diffs += 1 + else: + return False + if diffs > 1: + return False + return diffs == 1 + +# Deprecated methods + +def to_monotone_triangle(matrix): + """ + Deprecated method, use :meth:`AlternatingSignMatrix.to_monotone_triangle()` + instead. + + EXAMPLES:: + + sage: sage.combinat.alternating_sign_matrix.to_monotone_triangle([[0,1],[1,0]]) + doctest:...: DeprecationWarning: to_monotone_triangle() is deprecated. Use AlternatingSignMatrix.to_monotone_triangle() instead + See http://trac.sagemath.org/14301 for details. + [[2, 1], [2]] + """ + from sage.misc.superseded import deprecation + deprecation(14301,'to_monotone_triangle() is deprecated. Use AlternatingSignMatrix.to_monotone_triangle() instead') + return AlternatingSignMatrix(matrix).to_monotone_triangle() + +def from_monotone_triangle(triangle): + """ + Deprecated method, use + :meth:`AlternatingSignMatrices.from_monotone_triangle()` instead. + + EXAMPLES:: + + sage: sage.combinat.alternating_sign_matrix.from_monotone_triangle([[1, 2], [2]]) + doctest:...: DeprecationWarning: from_monotone_triangle() is deprecated. Use AlternatingSignMatrix.from_monotone_triangle() instead + See http://trac.sagemath.org/14301 for details. + [0 1] + [1 0] + """ + from sage.misc.superseded import deprecation + deprecation(14301,'from_monotone_triangle() is deprecated. Use AlternatingSignMatrix.from_monotone_triangle() instead') + return AlternatingSignMatrices(len(triangle)).from_monotone_triangle(triangle) + +# For old pickles +def AlternatingSignMatrices_n(n): + """ + For old pickles of ``AlternatingSignMatrices_n``. + + EXAMPLES:: + + sage: sage.combinat.alternating_sign_matrix.AlternatingSignMatrices_n(3) + doctest:...: DeprecationWarning: this class is deprecated. Use sage.combinat.alternating_sign_matrix.AlternatingSignMatrices instead + See http://trac.sagemath.org/14301 for details. + Alternating sign matrices of size 3 + """ + from sage.misc.superseded import deprecation + deprecation(14301,'this class is deprecated. Use sage.combinat.alternating_sign_matrix.AlternatingSignMatrices instead') + return AlternatingSignMatrices(n) + +def MonotoneTriangles_n(n): + """ + For old pickles of ``MonotoneTriangles_n``. + + EXAMPLES:: + + sage: sage.combinat.alternating_sign_matrix.MonotoneTriangles_n(3) + doctest:...: DeprecationWarning: this class is deprecated. Use sage.combinat.alternating_sign_matrix.MonotoneTriangles instead + See http://trac.sagemath.org/14301 for details. + Monotone triangles with 3 rows + """ + from sage.misc.superseded import deprecation + deprecation(14301,'this class is deprecated. Use sage.combinat.alternating_sign_matrix.MonotoneTriangles instead') + return MonotoneTriangles(n) + +from sage.structure.sage_object import register_unpickle_override +register_unpickle_override('sage.combinat.alternating_sign_matrix', 'AlternatingSignMatrices_n', AlternatingSignMatrices) +register_unpickle_override('sage.combinat.alternating_sign_matrix', 'MonotoneTriangles_n', MonotoneTriangles) +register_unpickle_override('sage.combinat.alternating_sign_matrix', 'MonotoneTriangles_n', MonotoneTriangles_n) + +# Here are the previous implementations of the combinatorial structure +# of the alternating sign matrices. Please, consider it obsolete and +# tend to use the monotone triangles instead. + +def from_contre_tableau(comps): + r""" + Returns an alternating sign matrix from a contre-tableau. + + EXAMPLES:: + + sage: import sage.combinat.alternating_sign_matrix as asm + sage: asm.from_contre_tableau([[1, 2, 3], [1, 2], [1]]) + doctest:...: DeprecationWarning: You can use from_monotone_triangle instead. + See http://trac.sagemath.org/12930 for details. + [0 0 1] + [0 1 0] + [1 0 0] + sage: asm.from_contre_tableau([[1, 2, 3], [2, 3], [3]]) + [1 0 0] + [0 1 0] + [0 0 1] + """ + from sage.misc.superseded import deprecation + deprecation(12930, 'You can use from_monotone_triangle instead.') + n = len(comps) + MS = MatrixSpace(ZZ, n) + M = [ [0 for _ in range(n)] for _ in range(n) ] + + previous_set = Set([]) + + for col in range(n-1, -1, -1): + s = Set( comps[col] ) + for x in s - previous_set: + M[x-1][col] = 1 + + for x in previous_set - s: + M[x-1][col] = -1 + + previous_set = s + + return MS(M) + + +class ContreTableaux(Parent): + """ + Factory class for the combinatorial class of contre tableaux of size `n`. + + EXAMPLES:: + + sage: ct4 = ContreTableaux(4); ct4 + Contre tableaux of size 4 + sage: ct4.cardinality() + 42 + """ + __metaclass__ = ClasscallMetaclass + @staticmethod + def __classcall_private__(cls, n, **kwds): + r""" + Factory pattern. + + Check properties on arguments, then call the appropriate class. + + EXAMPLES:: + + sage: C = ContreTableaux(4) + sage: type(C) + + + """ + assert(isinstance(n, (int, Integer))) + return ContreTableaux_n(n, **kwds) + + +class ContreTableaux_n(ContreTableaux): + def __init__(self, n): + """ + TESTS:: + + sage: ct2 = ContreTableaux(2); ct2 + Contre tableaux of size 2 + sage: ct2 == loads(dumps(ct2)) + True + """ + self.n = n + + def __repr__(self): + """ + TESTS:: + + sage: repr(ContreTableaux(2)) + 'Contre tableaux of size 2' + """ + return "Contre tableaux of size %s"%self.n + + def __eq__(self, other): + """ + TESTS:: + + sage: C = ContreTableaux(4) + sage: C == loads(dumps(C)) + True + + """ + return self.n == other.n + + def cardinality(self): + """ + EXAMPLES:: + + sage: [ ContreTableaux(n).cardinality() for n in range(0, 11)] + [1, 1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460, 129534272700] + """ + return prod( [ factorial(3*k+1)/factorial(self.n+k) for k in range(self.n)] ) + + def _iterator_rec(self, i): + """ + EXAMPLES:: + + sage: c = ContreTableaux(2) + sage: list(c._iterator_rec(0)) + [[]] + sage: list(c._iterator_rec(1)) + [[[1, 2]]] + sage: list(c._iterator_rec(2)) + [[[1, 2], [1]], [[1, 2], [2]]] + """ + if i == 0: + yield [] + elif i == 1: + yield [range(1, self.n+1)] + else: + for columns in self._iterator_rec(i-1): + previous_column = columns[-1] + for column in _next_column_iterator(previous_column, len(previous_column)-1): + yield columns + [ column ] + + def __iter__(self): + """ + EXAMPLES:: + + sage: list(ContreTableaux(0)) + [[]] + sage: list(ContreTableaux(1)) + [[[1]]] + sage: list(ContreTableaux(2)) + [[[1, 2], [1]], [[1, 2], [2]]] + sage: list(ContreTableaux(3)) + [[[1, 2, 3], [1, 2], [1]], + [[1, 2, 3], [1, 2], [2]], + [[1, 2, 3], [1, 3], [1]], + [[1, 2, 3], [1, 3], [2]], + [[1, 2, 3], [1, 3], [3]], + [[1, 2, 3], [2, 3], [2]], + [[1, 2, 3], [2, 3], [3]]] + """ + + for z in self._iterator_rec(self.n): + yield z + + +def _next_column_iterator(previous_column, height, i = None): + """ + Returns a generator for all columns of height height properly + filled from row 1 to ``i`` + + EXAMPLES:: + + sage: import sage.combinat.alternating_sign_matrix as asm + sage: list(asm._next_column_iterator([1], 0)) + [[]] + sage: list(asm._next_column_iterator([1,5],1)) + [[1], [2], [3], [4], [5]] + sage: list(asm._next_column_iterator([1,4,5],2)) + [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]] + """ + if i is None: + i = height + if i == 0: + yield [-1]*height + else: + for column in _next_column_iterator(previous_column, height, i-1): + min_value = previous_column[i-1] + if i > 1: + min_value = max(min_value, column[i-2]+1) + for value in range(min_value, previous_column[i]+1): + c = copy.copy(column) + c[i-1] = value + yield c + + +def _previous_column_iterator(column, height, max_value): + """ + EXAMPLES:: + + sage: import sage.combinat.alternating_sign_matrix as asm + sage: list(asm._previous_column_iterator([2,3], 3, 4)) + [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] + """ + new_column = [1] + column + [ max_value ] * (height - len(column)) + return _next_column_iterator(new_column, height) + + +class TruncatedStaircases(Parent): + """ + Factory class for the combinatorial class of truncated staircases + of size ``n`` with last column ``last_column``. + + EXAMPLES:: + + sage: t4 = TruncatedStaircases(4, [2,3]); t4 + Truncated staircases of size 4 with last column [2, 3] + sage: t4.cardinality() + 4 + """ + __metaclass__ = ClasscallMetaclass + @staticmethod + def __classcall_private__(cls, n, last_column, **kwds): + r""" + Factory pattern. + + Check properties on arguments, then call the appropriate class. + + TESTS:: + + sage: T = TruncatedStaircases(4, [2,3]) + sage: type(T) + + + """ + assert(isinstance(n, (int, Integer))) + return TruncatedStaircases_nlastcolumn(n, last_column, **kwds) + + +class TruncatedStaircases_nlastcolumn(TruncatedStaircases): + def __init__(self, n, last_column): + """ + TESTS:: + + sage: t4 = TruncatedStaircases(4, [2,3]); t4 + Truncated staircases of size 4 with last column [2, 3] + sage: t4 == loads(dumps(t4)) + True + """ + self.n = n + self.last_column = last_column + + def __repr__(self): + """ + TESTS:: + + sage: repr(TruncatedStaircases(4, [2,3])) + 'Truncated staircases of size 4 with last column [2, 3]' + """ + return "Truncated staircases of size %s with last column %s"%(self.n, self.last_column) + + def _iterator_rec(self, i): + """ + EXAMPLES:: + + sage: t = TruncatedStaircases(3, [2,3]) + sage: list(t._iterator_rec(1)) + [] + sage: list(t._iterator_rec(2)) + [[[2, 3]]] + sage: list(t._iterator_rec(3)) + [[[1, 2, 3], [2, 3]]] + """ + if i < len(self.last_column): + return + elif i == len(self.last_column): + yield [self.last_column] + else: + for columns in self._iterator_rec(i-1): + previous_column = columns[0] + for column in _previous_column_iterator(previous_column, len(previous_column)+1, self.n): + yield [column] + columns + + def __iter__(self): + """ + EXAMPLES::: + + sage: list(TruncatedStaircases(4, [2,3])) + [[[4, 3, 2, 1], [3, 2, 1], [3, 2]], [[4, 3, 2, 1], [4, 2, 1], [3, 2]], [[4, 3, 2, 1], [4, 3, 1], [3, 2]], [[4, 3, 2, 1], [4, 3, 2], [3, 2]]] + """ + for z in self._iterator_rec(self.n): + yield map(lambda x: list(reversed(x)), z) + + def __eq__(self, other): + r""" + TESTS:: + + sage: T = TruncatedStaircases(4, [2,3]) + sage: T == loads(dumps(T)) + True + """ + return ((self.n == other.n) and + (self.last_column == other.last_column)) + + def cardinality(self): + r""" + EXAMPLES:: + + sage: T = TruncatedStaircases(4, [2,3]) + sage: T.cardinality() + 4 + """ + c = 0 + for _ in self: + c += 1 + return c + +def nw_corner_sum(M,i,j): + r""" + Return the sum of entries to the northwest of `(i,j)` in matrix. + + EXAMPLES:: + + sage: from sage.combinat.alternating_sign_matrix import nw_corner_sum + sage: A = matrix.ones(3,3) + sage: nw_corner_sum(A,2,2) + 4 + """ + if i>=0 and j>=0: + return sum([sum([M[i2][j2] for j2 in range(j)]) for i2 in range(i)]) + else: + return 0 From af87227341c6d0e5854d63b48f83add1fbd53fc0 Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Thu, 7 Nov 2013 02:15:11 +0100 Subject: [PATCH 063/206] Second commit fixing indent error for ticket 14770 --- src/sage/combinat/alternating_sign_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 2bb6ffe8670..f362e9f1cf5 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -255,7 +255,7 @@ def height_function(self): asm = self.to_matrix() return matrix([[i+j-2*nw_corner_sum(asm,i,j) for i in range(len(list(asm))+1)] for j in range(len(list(asm))+1)]) - def gyration(self): + def gyration(self): r""" Return the matrix obtained by applying the gyration action to the height function in bijection with ``self``. From 6d75c23822d2a11a434f6791214e88ee7d1b3c28 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 27 Oct 2013 01:11:04 +0200 Subject: [PATCH 064/206] Poset.lt computes too much --- src/sage/combinat/posets/posets.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 0bd689a0b22..e52c75d5009 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -1702,8 +1702,10 @@ def is_lequal(self, x, y): sage: Q.is_lequal(z,z) True """ - r = self.compare_elements(x,y) - return r == 0 or r == -1 + i = self._element_to_vertex(x) + j = self._element_to_vertex(y) + return (i == j or self._hasse_diagram.is_lequal(i, j)) + le = is_lequal def is_less_than(self, x, y): @@ -1726,7 +1728,10 @@ def is_less_than(self, x, y): sage: Q.is_less_than(z,z) False """ - return self.compare_elements(x,y) == -1 + i = self._element_to_vertex(x) + j = self._element_to_vertex(y) + return self._hasse_diagram.is_less_than(i, j) + lt = is_less_than def is_gequal(self, x, y): @@ -1751,8 +1756,10 @@ def is_gequal(self, x, y): sage: Q.is_gequal(z,z) True """ - r = self.compare_elements(x,y) - return r == 0 or r == 1 + i = self._element_to_vertex(x) + j = self._element_to_vertex(y) + return (i == j or self._hasse_diagram.is_lequal(j, i)) + ge = is_gequal def is_greater_than(self, x, y): @@ -1777,7 +1784,10 @@ def is_greater_than(self, x, y): sage: Q.is_greater_than(z,z) False """ - return self.compare_elements(x,y) == 1 + i = self._element_to_vertex(x) + j = self._element_to_vertex(y) + return self._hasse_diagram.is_less_than(j, i) + gt = is_greater_than def compare_elements(self, x, y): From 017084f76b1445d46faea228d1550cf6006359ae Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 7 Nov 2013 13:08:52 +0100 Subject: [PATCH 065/206] Broken link in groups catalog doc --- src/sage/groups/groups_catalog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/groups/groups_catalog.py b/src/sage/groups/groups_catalog.py index 9c9d58baeee..1592f784322 100644 --- a/src/sage/groups/groups_catalog.py +++ b/src/sage/groups/groups_catalog.py @@ -31,8 +31,8 @@ - Matrix Groups (``groups.matrix.``) - :func:`groups.matrix.QuaternionGF3 ` - - :func:`groups.matrix.GL ` - - :func:`groups.matrix.SL ` + - :func:`groups.matrix.GL ` + - :func:`groups.matrix.SL ` - :func:`groups.matrix.Sp ` - :func:`groups.matrix.GU ` - :func:`groups.matrix.SU ` From 2a68051bb138ff28e0bd0a7e665e155601a9634b Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 7 Nov 2013 13:09:39 +0100 Subject: [PATCH 066/206] Add affine groups to groups. --- src/sage/groups/affine_gps/catalog.py | 16 ++++++++++++++++ src/sage/groups/groups_catalog.py | 6 ++++++ 2 files changed, 22 insertions(+) create mode 100644 src/sage/groups/affine_gps/catalog.py diff --git a/src/sage/groups/affine_gps/catalog.py b/src/sage/groups/affine_gps/catalog.py new file mode 100644 index 00000000000..fea181ae3bc --- /dev/null +++ b/src/sage/groups/affine_gps/catalog.py @@ -0,0 +1,16 @@ +r""" +Type ``groups.affine.`` to access examples +of groups implemented as affine groups. +""" + +# groups imported here will be available +# via groups.affine. +# +# Do not use this file for code +# +# If you import a new group, then add an +# entry to the list in the module-level +# docstring of groups/groups_catalog.py + +from affine_group import AffineGroup as Affine +from euclidean_group import EuclideanGroup as Euclidean diff --git a/src/sage/groups/groups_catalog.py b/src/sage/groups/groups_catalog.py index 1592f784322..771fc0546ef 100644 --- a/src/sage/groups/groups_catalog.py +++ b/src/sage/groups/groups_catalog.py @@ -50,6 +50,11 @@ - :func:`groups.presentation.Quaternion ` - :func:`groups.presentation.Symmetric ` +- Affine Groups (``groups.affine.``) + + - :func:`groups.affine.Affine ` + - :func:`groups.affine.Euclidean ` + - Miscellaneous Groups (``groups.misc.``) """ @@ -75,4 +80,5 @@ from sage.groups.matrix_gps import catalog as matrix from sage.groups.perm_gps import permutation_groups_catalog as permutation from sage.groups.misc_gps import misc_groups_catalog as misc +from sage.groups.affine_gps import catalog as affine from sage.groups import finitely_presented_catalog as presentation From 3d75bd32b63c732cd8023d5300946a4e9999d3a8 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 7 Nov 2013 13:26:12 +0100 Subject: [PATCH 067/206] Move QuaternionMatrixGroupGF3 from groups.misc to groups.matrix --- src/sage/groups/groups_catalog.py | 2 +- src/sage/groups/matrix_gps/all.py | 1 + src/sage/groups/matrix_gps/catalog.py | 2 +- .../groups/matrix_gps/finitely_generated.py | 62 +++++++++++++++++- src/sage/groups/misc_gps/misc_groups.py | 64 ------------------- 5 files changed, 63 insertions(+), 68 deletions(-) diff --git a/src/sage/groups/groups_catalog.py b/src/sage/groups/groups_catalog.py index 771fc0546ef..7b2ce7d815f 100644 --- a/src/sage/groups/groups_catalog.py +++ b/src/sage/groups/groups_catalog.py @@ -30,7 +30,7 @@ - Matrix Groups (``groups.matrix.``) - - :func:`groups.matrix.QuaternionGF3 ` + - :func:`groups.matrix.QuaternionGF3 ` - :func:`groups.matrix.GL ` - :func:`groups.matrix.SL ` - :func:`groups.matrix.Sp ` diff --git a/src/sage/groups/matrix_gps/all.py b/src/sage/groups/matrix_gps/all.py index 4568c80bb8a..5cb3b5e85fa 100644 --- a/src/sage/groups/matrix_gps/all.py +++ b/src/sage/groups/matrix_gps/all.py @@ -8,6 +8,7 @@ lazy_import('sage.groups.matrix_gps.orthogonal', 'GO') lazy_import('sage.groups.matrix_gps.orthogonal', 'SO') lazy_import('sage.groups.matrix_gps.finitely_generated', 'MatrixGroup') +lazy_import('sage.groups.matrix_gps.finitely_generated', 'QuaternionMatrixGroupGF3') #from matrix_group_element import is_MatrixGroupElement #from matrix_group import MatrixGroup, is_MatrixGroup diff --git a/src/sage/groups/matrix_gps/catalog.py b/src/sage/groups/matrix_gps/catalog.py index 19a9378cbf0..001e63969a7 100644 --- a/src/sage/groups/matrix_gps/catalog.py +++ b/src/sage/groups/matrix_gps/catalog.py @@ -15,5 +15,5 @@ # docstring of groups/groups_catalog.py from all import GL, SL, Sp, SU, GU, SO, GO -from sage.groups.misc_gps.misc_groups import QuaternionMatrixGroupGF3 as QuaternionGF3 +from all import QuaternionMatrixGroupGF3 as QuaternionGF3 diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index a6171376623..85c7cd268c3 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -137,7 +137,67 @@ def normalize_square_matrices(matrices): raise ValueError('matrices must be square') return gens +def QuaternionMatrixGroupGF3(): + r""" + The quaternion group as a set of `2\times 2` matrices over `GF(3)`. + + OUTPUT: + A matrix group consisting of `2\times 2` matrices with + elements from the finite field of order 3. The group is + the quaternion group, the nonabelian group of order 8 that + is not isomorphic to the group of symmetries of a square + (the dihedral group `D_4`). + + .. note:: + This group is most easily available via ``groups.matrix.QuaternionGF3()``. + + EXAMPLES: + + The generators are the matrix representations of the + elements commonly called `I` and `J`, while `K` + is the product of `I` and `J`. :: + + sage: from sage.groups.matrix_gps.finitely_generated import QuaternionMatrixGroupGF3 + sage: Q = QuaternionMatrixGroupGF3() + sage: Q.order() + 8 + sage: aye = Q.gens()[0]; aye + [1 1] + [1 2] + sage: jay = Q.gens()[1]; jay + [2 1] + [1 1] + sage: kay = aye*jay; kay + [0 2] + [1 0] + + TESTS:: + + sage: groups.matrix.QuaternionGF3() + Matrix group over Finite Field of size 3 with 2 generators ( + [1 1] [2 1] + [1 2], [1 1] + ) + + sage: Q = QuaternionMatrixGroupGF3() + sage: QP = Q.as_permutation_group() + sage: QP.is_isomorphic(QuaternionGroup()) + True + sage: H = DihedralGroup(4) + sage: H.order() + 8 + sage: QP.is_abelian(), H.is_abelian() + (False, False) + sage: QP.is_isomorphic(H) + False + """ + from sage.rings.finite_rings.constructor import FiniteField + from sage.matrix.matrix_space import MatrixSpace + MS = MatrixSpace(FiniteField(3), 2) + aye = MS([1,1,1,2]) + jay = MS([2,1,1,1]) + return MatrixGroup([aye, jay]) def MatrixGroup(*gens, **kwds): r""" @@ -235,8 +295,6 @@ def MatrixGroup(*gens, **kwds): except (TypeError, ValueError): return FinitelyGeneratedMatrixGroup_generic(degree, base_ring, gens) - - ################################################################### # # Matrix group over a generic ring diff --git a/src/sage/groups/misc_gps/misc_groups.py b/src/sage/groups/misc_gps/misc_groups.py index 5d505d43eaf..fb6bcb30fb7 100644 --- a/src/sage/groups/misc_gps/misc_groups.py +++ b/src/sage/groups/misc_gps/misc_groups.py @@ -8,67 +8,3 @@ # When adding a group here make an entry # in the misc_groups_catalog.py module # or a more closely-related catalog module - -def QuaternionMatrixGroupGF3(): - r""" - The quaternion group as a set of `2\times 2` matrices over `GF(3)`. - - OUTPUT: - - A matrix group consisting of `2\times 2` matrices with - elements from the finite field of order 3. The group is - the quaternion group, the nonabelian group of order 8 that - is not isomorphic to the group of symmetries of a square - (the dihedral group `D_4`). - - .. note:: - This group is most easily available via ``groups.matrix.QuaternionGF3()``. - - EXAMPLES: - - The generators are the matrix representations of the - elements commonly called `I` and `J`, while `K` - is the product of `I` and `J`. :: - - sage: from sage.groups.misc_gps.misc_groups import QuaternionMatrixGroupGF3 - sage: Q = QuaternionMatrixGroupGF3() - sage: Q.order() - 8 - sage: aye = Q.gens()[0]; aye - [1 1] - [1 2] - sage: jay = Q.gens()[1]; jay - [2 1] - [1 1] - sage: kay = aye*jay; kay - [0 2] - [1 0] - - TESTS:: - - sage: groups.matrix.QuaternionGF3() - Matrix group over Finite Field of size 3 with 2 generators ( - [1 1] [2 1] - [1 2], [1 1] - ) - - sage: Q = QuaternionMatrixGroupGF3() - sage: QP = Q.as_permutation_group() - sage: QP.is_isomorphic(QuaternionGroup()) - True - sage: H = DihedralGroup(4) - sage: H.order() - 8 - sage: QP.is_abelian(), H.is_abelian() - (False, False) - sage: QP.is_isomorphic(H) - False - """ - from sage.rings.finite_rings.constructor import FiniteField - from sage.matrix.matrix_space import MatrixSpace - from sage.groups.matrix_gps.all import MatrixGroup - MS = MatrixSpace(FiniteField(3), 2) - aye = MS([1,1,1,2]) - jay = MS([2,1,1,1]) - return MatrixGroup([aye, jay]) - From 0ad88a693361ceee5c1035ec3b4292ced17f0f48 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 7 Nov 2013 13:35:28 +0100 Subject: [PATCH 068/206] Additions to groups.misc : AdditiveAbelian, Free, Braid, SemimonomialTransformation --- src/sage/groups/groups_catalog.py | 6 ++++++ src/sage/groups/misc_gps/misc_groups_catalog.py | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/sage/groups/groups_catalog.py b/src/sage/groups/groups_catalog.py index 7b2ce7d815f..e115e75452f 100644 --- a/src/sage/groups/groups_catalog.py +++ b/src/sage/groups/groups_catalog.py @@ -56,6 +56,12 @@ - :func:`groups.affine.Euclidean ` - Miscellaneous Groups (``groups.misc.``) + + - :func:`groups.misc.AdditiveAbelian ` + - :func:`groups.misc.Braid ` + - :func:`groups.misc.Free ` + - :func:`groups.misc.SemimonomialTransformation ` + """ # Implementation notes: diff --git a/src/sage/groups/misc_gps/misc_groups_catalog.py b/src/sage/groups/misc_gps/misc_groups_catalog.py index 007a079d13c..d53c8f864dd 100644 --- a/src/sage/groups/misc_gps/misc_groups_catalog.py +++ b/src/sage/groups/misc_gps/misc_groups_catalog.py @@ -11,3 +11,8 @@ # If you import a new group, then add an # entry to the list in the module-level # docstring of groups/groups_catalog.py + +from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup as AdditiveAbelian +from sage.groups.free_group import FreeGroup as Free +from sage.groups.braid import BraidGroup as Braid +from sage.groups.semimonomial_transformations.semimonomial_transformation_group import SemimonomialTransformationGroup as SemimonomialTransformation From bdf8a9d9ffd483b1577f4d8e05c2dbe15c4f9adc Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Thu, 7 Nov 2013 19:39:29 +0100 Subject: [PATCH 069/206] Trac #14770: Edited documentation. --- src/sage/combinat/alternating_sign_matrix.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index f362e9f1cf5..a47e94e711c 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -5,8 +5,7 @@ - Mike Hansen (2007): Initial version - Pierre Cange, Luis Serrano (2012): Added monotone triangles -- Travis Scrimshaw (2013-28-03): Added element class for ASM's and made - :class:`MonotoneTriangles` inherit from :class:`GelfandTsetlinPatterns`. +- Travis Scrimshaw (2013-28-03): Added element class for ASM's and made :class:`MonotoneTriangles` inherit from :class:`GelfandTsetlinPatterns`. - Jessica Striker (2013): Added methods. """ #***************************************************************************** @@ -436,11 +435,11 @@ def ASM_compatible_smaller(self): @combinatorial_map(name='to Dyck word') def to_dyck_word(self): r""" - Return the Dyck word determined by the last diagonal of + Return the Dyck word determined by the last diagonal of the monotone triangle corresponding to ``self``. EXAMPLES:: - + sage: A = AlternatingSignMatrices(3) sage: A([[0,1,0],[1,0,0],[0,0,1]]).to_dyck_word() [1, 1, 0, 0, 1, 0] @@ -473,7 +472,7 @@ def number_negative_ones(self): def is_permutation(self): """ - Return ``True`` if ``self`` is a permutation matrix + Return ``True`` if ``self`` is a permutation matrix and ``False`` otherwise. EXAMPLES:: @@ -512,7 +511,7 @@ def to_permutation(self): asm_matrix = self.to_matrix() return Permutation([ j+1 for (i,j) in asm_matrix.nonzero_positions() ]) - @combinatorial_map(name='to semistandard tableau') + @combinatorial_map(name='to semistandard tableau') def to_semistandard_tableau(self): """ Return the semistandard tableau corresponding the monotone triangle From 84187f00d844f8f477b2652ff0db9c2de2246dda Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Thu, 7 Nov 2013 19:53:41 +0100 Subject: [PATCH 070/206] Trac #14770: finished documentation --- src/sage/combinat/alternating_sign_matrix.py | 72 ++++++++++++++------ 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index a47e94e711c..f829123c518 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -13,6 +13,7 @@ # 2012 Pierre Cagne , # Luis Serrano # 2013 Travis Scrimshaw +# 2013 Jessica Striker # # Distributed under the terms of the GNU General Public License (GPL) # @@ -227,8 +228,8 @@ def corner_sum_matrix(self): def height_function(self): r""" Return the height function from ``self``. A height function corresponding - to an nxn ASM is an (n+1)x(n+1) matrix such that the first row is 0,1,...,n, - the last row is n,n-1,...,1,0, and the difference between adjacent entries is 1. + to an nxn ASM is an (n+1)x(n+1) matrix such that the first row is 0,1,...,n, + the last row is n,n-1,...,1,0, and the difference between adjacent entries is 1. EXAMPLES:: @@ -256,19 +257,20 @@ def height_function(self): def gyration(self): r""" - Return the matrix obtained by applying the gyration action to the height function - in bijection with ``self``. - - Gyration acts on height functions as follows. Go through the entries of the matrix, - first those for which the sum of the row and column indices is even, then for those - for which it is odd, and increment or decrement the squares by 2 wherever possible - such that the resulting matrix is still a height function. Gyration was first defined - in [Wieland00] as an action on fully-packed loops. + Return the matrix obtained by applying the gyration action to the height + function in bijection with ``self``. + + Gyration acts on height functions as follows. Go through the entries of + the matrix, first those for which the sum of the row and column indices + is even, then for those for which it is odd, and increment or decrement + the squares by 2 wherever possible such that the resulting matrix is + still a height function. Gyration was first defined in [Wieland00] as an + action on fully-packed loops. REFERENCES: - .. [Wieland00] B. Wieland. *A large dihedral symmetry of the set of alternating sign matrices*. - Electron. J. Combin. 7 (2000). + .. [Wieland00] B. Wieland. *A large dihedral symmetry of the set of + alternating sign matrices*. Electron. J. Combin. 7 (2000). EXAMPLES:: @@ -310,7 +312,19 @@ def gyration(self): def ASM_compatible(self,B): r""" - Returns ``true`` if ``self`` and B are compatible alternating sign matrices. + Return ``true`` if ``self`` and B are compatible alternating sign + matrices in the sense of [EKLP92]. (If ``self`` is of size n, B must + be of size n+1.) + + In [ELKP92], there is a notion of a pair of ASMs with sizes differing + by 1 being compatible, in the sense that they can be combined to encode + a tiling of the Aztec Diamond. + + REFERENCES: + + .. [EKLP92] N. Elkies, G. Kuperberg, M. Larsen, J. Propp, + *Alternating-Sign Matrices and Domino Tilings*, Journal of Algebraic + Combinatorics, volume 1 (1992), p. 111-132. EXAMPLES:: @@ -335,7 +349,18 @@ def ASM_compatible(self,B): def ASM_compatible_bigger(self): r""" - Returns the list of larger ASMs that are compatible with ``self`` . + Return all ASMs compatible with ``self`` that are of size one greater + than ``self``. + + Given an nxn alternating sign matrix A, there are as many ASMs of size + n+1 compatible with A as 2 raised to the power of the number of 1's in + A [ELKP92]. + + REFERENCES: + + .. [EKLP92] N. Elkies, G. Kuperberg, M. Larsen, J. Propp, + *Alternating-Sign Matrices and Domino Tilings*, Journal of Algebraic + Combinatorics, volume 1 (1992), p. 111-132. EXAMPLES:: sage: A = AlternatingSignMatrix(matrix([[1,0],[0,1]])) @@ -384,7 +409,18 @@ def ASM_compatible_bigger(self): def ASM_compatible_smaller(self): r""" - Returns the list of larger ASMs that are compatible with ``self`` . + Return the list of all ASMs compatible with ``self`` that are of size one + smaller than ``self``. + + Given an alternating sign matrix A of size n, there are as many ASMs + of size n-1 compatible with it as 2 raised to the power of the number + of -1's in A [EKLP92]. + + REFERENCES: + + .. [EKLP92] N. Elkies, G. Kuperberg, M. Larsen, J. Propp, + *Alternating-Sign Matrices and Domino Tilings*, Journal of Algebraic + Combinatorics, volume 1 (1992), p. 111-132. EXAMPLES:: @@ -929,8 +965,7 @@ def cover_relations(self): sage: A = AlternatingSignMatrices(3) sage: for (a,b) in A.cover_relations(): - ... eval('a, b') - ... + ....: eval('a, b') ( [1 0 0] [0 1 0] [0 1 0] [1 0 0] @@ -1116,8 +1151,7 @@ def cover_relations(self): sage: M = MonotoneTriangles(3) sage: for (a,b) in M.cover_relations(): - ... eval('a, b') - ... + ....: eval('a, b') ([[3, 2, 1], [2, 1], [1]], [[3, 2, 1], [2, 1], [2]]) ([[3, 2, 1], [2, 1], [1]], [[3, 2, 1], [3, 1], [1]]) ([[3, 2, 1], [2, 1], [2]], [[3, 2, 1], [3, 1], [2]]) From b70b9a446699d41081149549a43eb5d39800a54c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 7 Nov 2013 15:19:52 -0800 Subject: [PATCH 071/206] First review changes. Fixed up the docstrings and some general code formating. --- src/sage/combinat/alternating_sign_matrix.py | 256 ++++++++++--------- 1 file changed, 133 insertions(+), 123 deletions(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index f829123c518..4e0ed42fba0 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -6,7 +6,7 @@ - Mike Hansen (2007): Initial version - Pierre Cange, Luis Serrano (2012): Added monotone triangles - Travis Scrimshaw (2013-28-03): Added element class for ASM's and made :class:`MonotoneTriangles` inherit from :class:`GelfandTsetlinPatterns`. -- Jessica Striker (2013): Added methods. +- Jessica Striker (2013): Added additional methods. """ #***************************************************************************** # Copyright (C) 2007 Mike Hansen , @@ -223,13 +223,16 @@ def corner_sum_matrix(self): [0 1 2 3] """ asm = self.to_matrix() - return matrix([[nw_corner_sum(asm,i,j) for i in range(0,len(list(asm))+1)] for j in range(0,len(list(asm))+1)]) + n = asm.nrows() + 1 + return matrix([[nw_corner_sum(asm,i,j) for i in range(n)] for j in range(n)]) def height_function(self): r""" - Return the height function from ``self``. A height function corresponding - to an nxn ASM is an (n+1)x(n+1) matrix such that the first row is 0,1,...,n, - the last row is n,n-1,...,1,0, and the difference between adjacent entries is 1. + Return the height function from ``self``. A height function + corresponding to an `n \times n` ASM is an `(n+1) \times (n+1)` matrix + such that the first row is `0, 1, \ldots, n`, the last row is + `n, n-1, \ldots, 1, 0`, and the difference between adjacent entries + is 1. EXAMPLES:: @@ -253,24 +256,25 @@ def height_function(self): [3 2 1 0] """ asm = self.to_matrix() - return matrix([[i+j-2*nw_corner_sum(asm,i,j) for i in range(len(list(asm))+1)] for j in range(len(list(asm))+1)]) + n = asm.nrows() + 1 + return matrix([[i+j-2*nw_corner_sum(asm,i,j) for i in range(n)] for j in range(n)]) def gyration(self): r""" - Return the matrix obtained by applying the gyration action to the height - function in bijection with ``self``. + Return the matrix obtained by applying the gyration action to the + height function in bijection with ``self``. - Gyration acts on height functions as follows. Go through the entries of - the matrix, first those for which the sum of the row and column indices - is even, then for those for which it is odd, and increment or decrement - the squares by 2 wherever possible such that the resulting matrix is - still a height function. Gyration was first defined in [Wieland00] as an - action on fully-packed loops. + Gyration acts on height functions as follows. Go through the entries of + the matrix, first those for which the sum of the row and column indices + is even, then for those for which it is odd, and increment or decrement + the squares by 2 wherever possible such that the resulting matrix is + still a height function. Gyration was first defined in [Wieland00]_ as + an action on fully-packed loops. REFERENCES: .. [Wieland00] B. Wieland. *A large dihedral symmetry of the set of - alternating sign matrices*. Electron. J. Combin. 7 (2000). + alternating sign matrices*. Electron. J. Combin. 7 (2000). EXAMPLES:: @@ -290,41 +294,42 @@ def gyration(self): [0 0 1] [1 0 0] """ - A = AlternatingSignMatrices(self.to_matrix().nrows()) + A = self.parent() hf = list(self.height_function()) - for i in range(1,len(hf)-1): - for j in range(1,len(hf)-1): - if (i+j)%2==0: - if hf[i-1][j] == hf[i+1][j] == hf[i][j+1] == hf[i][j-1]: - if hf[i][j] < hf[i+1][j]: - hf[i][j] = hf[i][j] + 2 - else: - hf[i][j] = hf[i][j] - 2 - for i in range(1,len(hf)-1): - for j in range(1,len(hf)-1): - if (i+j)%2==1: - if hf[i-1][j] == hf[i+1][j] == hf[i][j+1] == hf[i][j-1]: - if hf[i][j] < hf[i+1][j]: - hf[i][j] = hf[i][j] + 2 - else: - hf[i][j] = hf[i][j] - 2 + k = len(hf) - 1 + for i in range(1,k): + for j in range(1,k): + if (i+j) % 2 == 0 \ + and hf[i-1][j] == hf[i+1][j] == hf[i][j+1] == hf[i][j-1]: + if hf[i][j] < hf[i+1][j]: + hf[i][j] += 2 + else: + hf[i][j] -= 2 + for i in range(1,k): + for j in range(1,k): + if (i+j) % 2 == 1 \ + and hf[i-1][j] == hf[i+1][j] == hf[i][j+1] == hf[i][j-1]: + if hf[i][j] < hf[i+1][j]: + hf[i][j] += 2 + else: + hf[i][j] -= 2 return A.from_height_function(matrix(hf)) - def ASM_compatible(self,B): + def ASM_compatible(self, B): r""" - Return ``true`` if ``self`` and B are compatible alternating sign - matrices in the sense of [EKLP92]. (If ``self`` is of size n, B must - be of size n+1.) + Return ``True`` if ``self`` and ``B`` are compatible alternating sign + matrices in the sense of [EKLP92]_. (If ``self`` is of size `n`, ``B`` + must be of size `n+1`.) - In [ELKP92], there is a notion of a pair of ASMs with sizes differing + In [ELKP92]_, there is a notion of a pair of ASM's with sizes differing by 1 being compatible, in the sense that they can be combined to encode - a tiling of the Aztec Diamond. + a tiling of the Aztec Diamond. REFERENCES: .. [EKLP92] N. Elkies, G. Kuperberg, M. Larsen, J. Propp, - *Alternating-Sign Matrices and Domino Tilings*, Journal of Algebraic - Combinatorics, volume 1 (1992), p. 111-132. + *Alternating-Sign Matrices and Domino Tilings*, Journal of Algebraic + Combinatorics, volume 1 (1992), p. 111-132. EXAMPLES:: @@ -337,32 +342,29 @@ def ASM_compatible(self,B): sage: A.ASM_compatible(B) False """ - if len(B.to_matrix()[0])-len(self.to_matrix()[0])!=1: - return "error" - AA=self.corner_sum_matrix() - BB=B.corner_sum_matrix() - for i in range(0,len(AA[0])): - for j in range(0,len(AA[0])): - if not (AA[i,j]>=BB[i,j] and AA[i,j]>=BB[i+1,j+1]-1 and AA[i,j]<=BB[i+1,j] and AA[i,j]<=BB[i,j+1]): + if B.parent()._n - self.parent()._n != 1: + raise ValueError("mismatched sizes") + + AA = self.corner_sum_matrix() + BB = B.corner_sum_matrix() + for i in range(0, len(AA[0])): + for j in range(0, len(AA[0])): + if not (AA[i,j]>=BB[i,j] and AA[i,j]>=BB[i+1,j+1]-1 \ + and AA[i,j]<=BB[i+1,j] and AA[i,j]<=BB[i,j+1]): return False return True def ASM_compatible_bigger(self): r""" - Return all ASMs compatible with ``self`` that are of size one greater + Return all ASM's compatible with ``self`` that are of size one greater than ``self``. - Given an nxn alternating sign matrix A, there are as many ASMs of size - n+1 compatible with A as 2 raised to the power of the number of 1's in - A [ELKP92]. - - REFERENCES: - - .. [EKLP92] N. Elkies, G. Kuperberg, M. Larsen, J. Propp, - *Alternating-Sign Matrices and Domino Tilings*, Journal of Algebraic - Combinatorics, volume 1 (1992), p. 111-132. + Given an `n \times n` alternating sign matrix `A`, there are as many + ASM's of size `n+1` compatible with `A` as 2 raised to the power of + the number of 1's in `A` [ELKP92]_. EXAMPLES:: + sage: A = AlternatingSignMatrix(matrix([[1,0],[0,1]])) sage: A.ASM_compatible_bigger() [ @@ -378,49 +380,48 @@ def ASM_compatible_bigger(self): [1 0 0], [0 1 0], [1 0 0], [ 0 1 0] ] """ - n=len(self.to_matrix()[0]) - M=AlternatingSignMatrices(n+1) - sign=[] - B=matrix(n+2) - A=matrix([[2*(i+j-2*nw_corner_sum(self.to_matrix(),i,j))+1 for i in range(n+1)] for j in range(n+1)]) - for a in range(0,n+2): - B[a,0]=2*a - B[0,a]=2*a - B[a,n+1]=2*(n+1-a) - B[n+1,a]=2*(n+1-a) - for i in range(1,n+1): - for j in range(1,n+1): - if (A[i-1,j-1]==A[i,j]==A[i-1,j]-2==A[i,j-1]-2): - B[i,j]=-A[i,j] + n = self.parent()._n + 1 + M = AlternatingSignMatrices(n) + sign = [] + asm = self.to_matrix() + B = matrix(n+1) + A = matrix([[2*(i+j-2*nw_corner_sum(asm,i,j))+1 for i in range(n)] + for j in range(n)]) + for a in range(n+1): + B[a,0] = 2*a + B[0,a] = 2*a + B[a,n] = 2*(n-a) + B[n,a] = 2*(n-a) + + for i in range(1,n): + for j in range(1,n): + if A[i-1,j-1] == A[i,j] == A[i-1,j]-2 == A[i,j-1]-2: + B[i,j] = -A[i,j] sign.append([i,j]) else: - B[i,j]=list({A[i-1,j-1]-1,A[i-1,j-1]+3} & {A[i-1,j]-3,A[i-1,j]+1} & {A[i,j-1]-3,A[i,j-1]+1} & {A[i,j]-1,A[i,j]+3})[0] - output=[B] - for b in range(0,len(sign)): - N=len(output) - for c in range(0,N): - d=copy.copy(output[c]) - output[c][sign[b][0],sign[b][1]]=-output[c][sign[b][0],sign[b][1]]+3 - d[sign[b][0],sign[b][1]]=-d[sign[b][0],sign[b][1]]-1 + B[i,j] = list({A[i-1,j-1]-1,A[i-1,j-1]+3} & {A[i-1,j]-3,A[i-1,j]+1} & {A[i,j-1]-3,A[i,j-1]+1} & {A[i,j]-1,A[i,j]+3})[0] + + output = [B] + for b in range(len(sign)): + N = len(output) + for c in range(N): + d = copy.copy(output[c]) + output[c][sign[b][0],sign[b][1]] = -output[c][sign[b][0], sign[b][1]] + 3 + d[sign[b][0],sign[b][1]] = -d[sign[b][0], sign[b][1]]-1 output.append(d) - for k in range(0,len(output)): - output[k]=M.from_height_function(output[k]/2) + + for k in range(len(output)): + output[k] = M.from_height_function(output[k]/2) return(output) def ASM_compatible_smaller(self): r""" - Return the list of all ASMs compatible with ``self`` that are of size one - smaller than ``self``. - - Given an alternating sign matrix A of size n, there are as many ASMs - of size n-1 compatible with it as 2 raised to the power of the number - of -1's in A [EKLP92]. + Return the list of all ASMs compatible with ``self`` that are of size + one smaller than ``self``. - REFERENCES: - - .. [EKLP92] N. Elkies, G. Kuperberg, M. Larsen, J. Propp, - *Alternating-Sign Matrices and Domino Tilings*, Journal of Algebraic - Combinatorics, volume 1 (1992), p. 111-132. + Given an alternating sign matrix `A` of size `n`, there are as many + ASM's of size `n-1` compatible with it as 2 raised to the power of + the number of `-1`'s in `A` [EKLP92]_. EXAMPLES:: @@ -439,33 +440,36 @@ def ASM_compatible_smaller(self): ] """ - n=len(self.to_matrix()[0]) - M=AlternatingSignMatrices(n) - A=matrix(n) - B=matrix([[2*(i+j-2*nw_corner_sum(self.to_matrix(),i,j)) for i in range(n)] for j in range(n)]) - sign=[] - for a in range(0,n): - A[a,0]=2*a+1 - A[0,a]=2*a+1 - A[n-1,a]=2*(n-a)-1 - A[a,n-1]=2*(n-a)-1 - for i in range(0,n-1): - for j in range(0,n-1): - if B[i+1,j+1]==B[i,j]==B[i,j+1]+2==B[i+1,j]+2: - A[i,j]=-B[i,j] + n = self.parent()._n + M = AlternatingSignMatrices(n) + A = matrix(n) + asm = self.to_matrix() + B = matrix([[2*(i+j-2*nw_corner_sum(asm,i,j)) for i in range(n)] for j in range(n)]) + sign = [] + for a in range(n): + A[a,0] = 2*a + 1 + A[0,a] = 2*a + 1 + A[n-1,a] = 2*(n-a) - 1 + A[a,n-1] = 2*(n-a) - 1 + + for i in range(n-1): + for j in range(n-1): + if B[i+1,j+1] == B[i,j] == B[i,j+1]+2 == B[i+1,j]+2: + A[i,j] = -B[i,j] sign.append([i,j]) else: - A[i,j]=list({B[i,j]+1,B[i,j]-3} & {B[i,j+1]+3,B[i,j+1]-1} & {B[i+1,j]+3,B[i+1,j]-1} & {B[i+1,j+1]+1,B[i+1,j+1]-3})[0] - output=[A] - for b in range(0,len(sign)): - N=len(output) - for c in range(0,N): - d=copy.copy(output[c]) - output[c][sign[b][0],sign[b][1]]=-output[c][sign[b][0],sign[b][1]]+1 - d[sign[b][0],sign[b][1]]=-d[sign[b][0],sign[b][1]]-3 - output.append(d) + A[i,j] = list({B[i,j]+1,B[i,j]-3} & {B[i,j+1]+3,B[i,j+1]-1} & {B[i+1,j]+3,B[i+1,j]-1} & {B[i+1,j+1]+1,B[i+1,j+1]-3})[0] + + output = [A] + for b in range(len(sign)): + N = len(output) + for c in range(N): + d = copy.copy(output[c]) + output[c][sign[b][0],sign[b][1]] = -output[c][sign[b][0], sign[b][1]]+1 + d[sign[b][0],sign[b][1]] = -d[sign[b][0], sign[b][1]]-3 + output.append(d) for k in range(0,len(output)): - output[k]=M.from_height_function((output[k]-matrix.ones(n,n))/2) + output[k] = M.from_height_function((output[k]-matrix.ones(n,n))/2) return(output) @combinatorial_map(name='to Dyck word') @@ -823,7 +827,7 @@ def from_monotone_triangle(self, triangle): return self.element_class(self, self._matrix_space(asm)) - def from_corner_sum(self,corner): + def from_corner_sum(self, corner): r""" Return an alternating sign matrix from a corner sum matrix. @@ -840,12 +844,16 @@ def from_corner_sum(self,corner): [ 0 1 0] """ asm_list=[] - n=len(list(corner))-1 + n = len(list(corner)) - 1 for k in range(n): asm_list.append([]) for i in range(n): for j in range(n): - y=corner[i+1][j+1]-sum([sum([asm_list[i2][j2] for i2 in range(i)]) for j2 in range(j)])-sum([asm_list[i2][j] for i2 in range(i)])-sum([asm_list[i][j2] for j2 in range(j)]) + y = corner[i+1][j+1] \ + - sum([sum([asm_list[i2][j2] for i2 in range(i)]) + for j2 in range(j)]) \ + - sum([asm_list[i2][j] for i2 in range(i)]) \ + - sum([asm_list[i][j2] for j2 in range(j)]) asm_list[i].append(y) return AlternatingSignMatrix(asm_list) @@ -865,7 +873,9 @@ def from_height_function(self,height): [ 1 -1 1] [ 0 1 0] """ - return self.from_corner_sum(matrix([[((i+j-height[i][j])/int(2)) for i in range(len(list(height)))] for j in range(len(list(height)))])) + return self.from_corner_sum(matrix( [[((i+j-height[i][j])/int(2)) + for i in range(len(list(height)))] + for j in range(len(list(height)))] )) def size(self): r""" @@ -1597,7 +1607,7 @@ def nw_corner_sum(M,i,j): sage: nw_corner_sum(A,2,2) 4 """ - if i>=0 and j>=0: + if i >= 0 and j >= 0: return sum([sum([M[i2][j2] for j2 in range(j)]) for i2 in range(i)]) - else: - return 0 + return 0 + From c8dce2ac8d0971a93cc6fe5d651bcb30aa2a7216 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 7 Nov 2013 15:30:06 -0800 Subject: [PATCH 072/206] Fixed references. --- src/sage/combinat/alternating_sign_matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 4e0ed42fba0..59d9dc48be6 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -321,7 +321,7 @@ def ASM_compatible(self, B): matrices in the sense of [EKLP92]_. (If ``self`` is of size `n`, ``B`` must be of size `n+1`.) - In [ELKP92]_, there is a notion of a pair of ASM's with sizes differing + In [EKLP92]_, there is a notion of a pair of ASM's with sizes differing by 1 being compatible, in the sense that they can be combined to encode a tiling of the Aztec Diamond. @@ -361,7 +361,7 @@ def ASM_compatible_bigger(self): Given an `n \times n` alternating sign matrix `A`, there are as many ASM's of size `n+1` compatible with `A` as 2 raised to the power of - the number of 1's in `A` [ELKP92]_. + the number of 1's in `A` [EKLP92]_. EXAMPLES:: From f6fcec1f94ff3c973fd9915c4267582667a37bfb Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Fri, 8 Nov 2013 02:56:24 +0100 Subject: [PATCH 073/206] Trac #15372: bug fix --- src/sage/combinat/alternating_sign_matrix.py | 64 ++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 3900bc8eaa6..57bc01b00bd 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -135,6 +135,70 @@ def __ne__(self, other): """ return not self.__eq__(other) + def __le__(self, other): + """ + Check less than or equal to. This is needed, see :trac:`15372`. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: M = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + sage: M <= A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + True + sage: M <= A([[1, 0, 0],[0, 0, 1],[0, 1, 0]]) + False + """ + if isinstance(other, AlternatingSignMatrix): + return self._matrix <= other._matrix + return False #return False if other is not an ASM + + def __lt__(self, other): + """ + Check less than. This is needed, see :trac:`15372`. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: M = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + sage: M < A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + False + """ + if isinstance(other, AlternatingSignMatrix): + return self._matrix < other._matrix + return False #return False if other is not an ASM + + def __ge__(self, other): + """ + Check greater than or equal to. This is needed, see :trac:`15372`. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: M = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + sage: M >= A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + True + sage: M >= A([[1, 0, 0],[0, 0, 1],[0, 1, 0]]) + True + """ + if isinstance(other, AlternatingSignMatrix): + return self._matrix >= other._matrix + return False #return False if other is not an ASM + + def __gt__(self, other): + """ + Check greater than. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: M = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + sage: M > A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) + False + """ + if isinstance(other, AlternatingSignMatrix): + return self._matrix > other._matrix + return False #return False if other is not an ASM + def _latex_(self): r""" Return a `\LaTeX` representation of ``self``. From 7f07dce6b43ab07f8e55d190b8d91d957d03a8de Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Fri, 8 Nov 2013 03:16:11 +0100 Subject: [PATCH 074/206] Trac #15372: added doc test that plot works on the ASM lattice --- src/sage/combinat/alternating_sign_matrix.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 57bc01b00bd..31c8e8829ff 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -186,7 +186,7 @@ def __ge__(self, other): def __gt__(self, other): """ - Check greater than. + Check greater than. This is needed, see :trac:`15372`. EXAMPLES:: @@ -925,7 +925,8 @@ def lattice(self): sage: P = M.lattice() sage: P Finite lattice containing 7 elements - + sage: P.plot() + """ return LatticePoset(self._lattice_initializer(), cover_relations=True) From 1413810f9070ed26533d45c00b94178e7b33107b Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Sun, 10 Nov 2013 18:16:53 +0100 Subject: [PATCH 075/206] ASM changes --- src/sage/combinat/alternating_sign_matrix.py | 47 +++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 59d9dc48be6..932ae4072c6 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -197,6 +197,48 @@ def to_monotone_triangle(self): prev = add_row return MonotoneTriangles(n)(triangle) + @combinatorial_map(name='rotate counterclockwise') + def rotate_ccw(self): + r""" + Return the counterclockwise quarter turn rotation of ``self``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]).rotate_ccw() + [0 0 1] + [0 1 0] + [1 0 0] + sage: asm = A([[0, 0, 1],[1, 0, 0],[0, 1, 0]]) + sage: asm.rotate_ccw() + [1 0 0] + [0 0 1] + [0 1 0] + """ + l = list(mat._matrix.transpose()) + l.reverse() + return AlternatingSignMatrix(matrix(l)) + + @combinatorial_map(name='transpose') + def transpose(self): + r""" + Return the counterclockwise quarter turn rotation of ``self``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]).transpose() + [1 0 0] + [0 1 0] + [0 0 1] + sage: asm = A([[0, 0, 1],[1, 0, 0],[0, 1, 0]]) + sage: asm.transpose() + [0 1 0] + [0 0 1] + [1 0 0] + """ + return AlternatingSignMatrix(self._matrix.transpose()) + def corner_sum_matrix(self): r""" Return the corner sum matrix from ``self``. @@ -259,10 +301,11 @@ def height_function(self): n = asm.nrows() + 1 return matrix([[i+j-2*nw_corner_sum(asm,i,j) for i in range(n)] for j in range(n)]) + @combinatorial_map(name='gyration') def gyration(self): r""" - Return the matrix obtained by applying the gyration action to the - height function in bijection with ``self``. + Return the alternating sign matrix obtained by applying the gyration + action to the height function in bijection with ``self``. Gyration acts on height functions as follows. Go through the entries of the matrix, first those for which the sum of the row and column indices From 91f9baef3debff2ac5b2e937331df7f5f1dc64ed Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Sun, 10 Nov 2013 20:27:23 +0100 Subject: [PATCH 076/206] 14770: added cw map --- src/sage/combinat/alternating_sign_matrix.py | 24 +++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 932ae4072c6..d5b1ab6b1f8 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -215,10 +215,32 @@ def rotate_ccw(self): [0 0 1] [0 1 0] """ - l = list(mat._matrix.transpose()) + l = list(self._matrix.transpose()) l.reverse() return AlternatingSignMatrix(matrix(l)) + @combinatorial_map(name='rotate clockwise') + def rotate_cw(self): + r""" + Return the clockwise quarter turn rotation of ``self``. + + EXAMPLES:: + + sage: A = AlternatingSignMatrices(3) + sage: A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]).rotate_cw() + [0 0 1] + [0 1 0] + [1 0 0] + sage: asm = A([[0, 0, 1],[1, 0, 0],[0, 1, 0]]) + sage: asm.rotate_cw() + [0 1 0] + [1 0 0] + [0 0 1] + """ + l = list(self._matrix.transpose()) + l.reverse() + return AlternatingSignMatrix(matrix(l).transpose().antitranspose()) + @combinatorial_map(name='transpose') def transpose(self): r""" From 145959f9a299688134f5c127ccc29f343b50e6c9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 11 Nov 2013 10:07:47 -0800 Subject: [PATCH 077/206] Starting on 6 vertex model. --- src/sage/combinat/six_vertex_model.py | 81 +++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/sage/combinat/six_vertex_model.py diff --git a/src/sage/combinat/six_vertex_model.py b/src/sage/combinat/six_vertex_model.py new file mode 100644 index 00000000000..85db8505fa2 --- /dev/null +++ b/src/sage/combinat/six_vertex_model.py @@ -0,0 +1,81 @@ +r""" +Six Vertex Model +""" + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element import Element +from sage.combinat.combinat import CombinatorialObject + +class SixVertexConfiguration(CombinatorialObject, Element): + """ + A configuration in the six vertex model. + """ + def __init__(self, parent, lst): + """ + Initialize ``self``. + """ + Element.__init__(self, parent) + CombinatorialObject.__init__(self, lst) + + def to_digraph(self): + """ + Return a digraph version of ``self``. + """ + + def plot(self): + """ + Return a plot of ``self``. + """ + +class SixVertexModel(Parent, UniqueRepresentation): + """ + The six vertex model. + + We model a configuration by indicating which configuration by the + following six configurations which are determined by the two outgoing + arrows: + + 1 - LR + 2 - LU + 3 - LD + 4 - UD + 5 - UR + 6 - RD + + INPUT: + + - ``n`` -- the number of rows + - ``m`` -- (optional) the number of columns, if not specified, then + the number of columns is the number of rows + """ + @staticmethod + def __classcall_private__(cls, n, m=None, boundary_conditions=None): + """ + Normalize input to ensure a unique representation. + """ + if m is None: + m = n + if boundary_conditions is not None: + boundary_conditions = tuple(tuple(x) for x in boundary_conditions) + else: + boundary_conditions = (None, None, None, None) + return super(SixVertexModel, cls).__classcall__(cls, n, m, boundary_conditions) + + def __init__(self, n, m, boundary_conditions): + """ + Initialize ``self``. + """ + self._nrows = n + self._ncols = m + self._boundary_conditions = boundary_conditions + Parent.__init__(self, category=FiniteEnumeratedSets()) + + def __iter__(self): + """ + Iterate through ``self``. + """ + # TODO + + Element = SixVertexConfiguration + From 6900a31a72d206e56062d55c1d4561658f1a1ad1 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 11 Nov 2013 12:58:22 -0800 Subject: [PATCH 078/206] Implemented iterator. --- src/sage/combinat/all.py | 3 + src/sage/combinat/six_vertex_model.py | 200 ++++++++++++++++++++++++-- 2 files changed, 191 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 4a9d84a39f4..9ef30573261 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -158,3 +158,6 @@ # Gelfand-Tsetlin patterns from gelfand_tsetlin_patterns import GelfandTsetlinPattern, GelfandTsetlinPatterns + +# Six Vertex Model +from six_vertex_model import SixVertexModel diff --git a/src/sage/combinat/six_vertex_model.py b/src/sage/combinat/six_vertex_model.py index 85db8505fa2..a932e8d161f 100644 --- a/src/sage/combinat/six_vertex_model.py +++ b/src/sage/combinat/six_vertex_model.py @@ -4,19 +4,76 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.element import Element -from sage.combinat.combinat import CombinatorialObject +from sage.structure.list_clone import ClonableArray +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -class SixVertexConfiguration(CombinatorialObject, Element): +class SixVertexConfiguration(ClonableArray): """ A configuration in the six vertex model. """ - def __init__(self, parent, lst): + def check(self): """ - Initialize ``self``. + Check if ``self`` is a valid 6 vertex configuration. + """ + pass # We're not checking for now + + def _repr_(self): """ - Element.__init__(self, parent) - CombinatorialObject.__init__(self, lst) + Return a string representation of ``self``. + """ + # List are in the order of URDL + ascii = [[r' V ', ' -', r' ^ ', '- '], # LR + [r' | ', ' <', r' ^ ', '- '], # LU + [r' V ', ' <', r' | ', '- '], # LD + [r' | ', ' <', r' | ', '> '], # UD + [r' | ', ' -', r' ^ ', '> '], # UR + [r' V ', ' -', r' | ', '> ']] # RD + ret = ' ' + # Do the top line + for entry in self[0]: + if entry == 1 or entry == 3 or entry == 4: + ret += ' ^ ' + else: + ret += ' | ' + + # Do the meat of the ascii art + for row in self: + ret += '\n ' + # Do the top row + for entry in row: + ret += ascii[entry][0] + ret += '\n' + + # Do the left-most entry + if row[0] == 0 or row[0] == 1 or row[0] == 2: + ret += '<-' + else: + ret += '--' + + # Do the middle row + for entry in row: + ret += ascii[entry][3] + '#' + ascii[entry][1] + + # Do the right-most entry + if row[-1] == 0 or row[-1] == 4 or row[-1] == 5: + ret += '->' + else: + ret += '--' + + # Do the bottom row + ret += '\n ' + for entry in row: + ret += ascii[entry][2] + + # Do the bottom line + ret += '\n ' + for entry in self[-1]: + if entry == 2 or entry == 3 or entry == 5: + ret += ' V ' + else: + ret += ' | ' + + return ret def to_digraph(self): """ @@ -48,6 +105,19 @@ class SixVertexModel(Parent, UniqueRepresentation): - ``n`` -- the number of rows - ``m`` -- (optional) the number of columns, if not specified, then the number of columns is the number of rows + - ``boundary_conditions`` -- (optional) a quadruple of tuples whose + entries are either: + + * ``True`` for an inward arrow, + * ``False`` for an outward arrow, or + * ``None`` for no boundary condition + + There are also the following special cases: + + * ``'inward'`` - all arrows are inward arrows + * ``'outward'`` - all arrows are outward arrows + * ``'ice'`` - the top and bottom boundary conditions are outward and the + left and right boundary conditions are inward """ @staticmethod def __classcall_private__(cls, n, m=None, boundary_conditions=None): @@ -56,10 +126,16 @@ def __classcall_private__(cls, n, m=None, boundary_conditions=None): """ if m is None: m = n - if boundary_conditions is not None: - boundary_conditions = tuple(tuple(x) for x in boundary_conditions) + if boundary_conditions is None: + boundary_conditions = ((None,)*m, (None,)*n)*2 + elif boundary_conditions == 'inward': + boundary_conditions = ((True,)*m, (True,)*n)*2 + elif boundary_conditions == 'outward': + boundary_conditions = ((False,)*m, (False,)*n)*2 + elif boundary_conditions == 'ice': + boundary_conditions = ((False,)*m, (True,)*n)*2 else: - boundary_conditions = (None, None, None, None) + boundary_conditions = tuple(tuple(x) for x in boundary_conditions) return super(SixVertexModel, cls).__classcall__(cls, n, m, boundary_conditions) def __init__(self, n, m, boundary_conditions): @@ -68,14 +144,114 @@ def __init__(self, n, m, boundary_conditions): """ self._nrows = n self._ncols = m - self._boundary_conditions = boundary_conditions + self._bdry_cond = boundary_conditions # Ordered URDL Parent.__init__(self, category=FiniteEnumeratedSets()) + def _repr_(self): + """ + Return a string representation of ``self``. + """ + return "The six vertex model on an {} by {} grid".format(self._nrows, self._ncols) + + def boundary_conditions(self): + """ + Return the boundary conditions of ``self``. + """ + return self._bdry_cond + + def _repr_option(self, key): + """ + Metadata about the ``_repr_()`` output. + + See :meth:`sage.structure.parent._repr_option` for details. + """ + if key == 'element_ascii_art': + return True + return Parent._repr_option(self, key) + + def _element_constructor_(self, x): + """ + Construct an element of ``self``. + """ + if isinstance(x, SixVertexConfiguration): + if x.parent() is not self: + return self.element_class(self, tuple(x)) + return x + + verts = ['LR', 'LU', 'LD', 'UD', 'UR', 'RD'] + elt = [] + for row in x: + elt.append([]) + for entry in row: + if x in verts: + elt[-1].append(verts.index(x)) + elif x in range(6): + elt[-1].append(x) + else: + raise ValueError("invalid entry") + elt[-1] = tuple(elt[-1]) + return self.element_class(self, tuple(elt)) + def __iter__(self): """ Iterate through ``self``. """ - # TODO + # Boundary conditions ordered URDL + # The top row boundary condition of True is a downward arrow + # The left condition of True is a right arrow + # verts = ['LR', 'LU', 'LD', 'UD', 'UR', 'RD'] + next_top = [False, False, True, True, False, True] + next_left = [True, False, False, False, True, True] + check_top = [True, False, True, False, False, True] + check_left = [False, False, False, True, True, True] + + bdry = [self._bdry_cond[0]] + lbd = list(self._bdry_cond[3]) + [None] # Dummy + left = [ [lbd[0]] ] + cur = [[-1]] + n = self._nrows + m = self._ncols + # [[3, 1], [5, 3]] + # [[4, 3], [3, 2]] + + while len(cur) > 0: + # If we're at the last row and all our final boundry condition is statisfied + if len(cur) > n and all(x is not self._bdry_cond[2][i] + for i,x in enumerate(bdry[-1])): + cur.pop() + bdry.pop() + left.pop() + yield self.element_class(self, tuple(tuple(x) for x in cur)) + + # Find the next row + row = cur[-1] + l = left[-1] + i = len(cur) - 1 + while len(row) > 0: + row[-1] += 1 + # Check to see if we have more vertices + if row[-1] > 5: + row.pop() + l.pop() + continue + # Check to see if we can add the vertex + if check_left[row[-1]] is l[-1] \ + and check_top[row[-1]] is bdry[-1][len(row)-1]: + if len(row) != m: + l.append(next_left[row[-1]]) + row.append(-1) + # Check the right bdry condition since we are at the rightmost entry + elif next_left[row[-1]] is not self._bdry_cond[1][i]: + bdry.append(map(lambda x: next_top[x], row)) + cur.append([-1]) + left.append([lbd[i+1]]) + break + + # If we've killed this row, backup + if len(cur[-1]) == 0: + cur.pop() + bdry.pop() + left.pop() Element = SixVertexConfiguration From 2eedb671eecc70f5bd5725bd5c5c7071fae59df8 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 11 Nov 2013 17:38:29 -0800 Subject: [PATCH 079/206] More updates to 6VM. --- src/sage/combinat/six_vertex_model.py | 266 ++++++++++++++++++++++++-- 1 file changed, 253 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/six_vertex_model.py b/src/sage/combinat/six_vertex_model.py index a932e8d161f..77a12361457 100644 --- a/src/sage/combinat/six_vertex_model.py +++ b/src/sage/combinat/six_vertex_model.py @@ -14,12 +14,34 @@ class SixVertexConfiguration(ClonableArray): def check(self): """ Check if ``self`` is a valid 6 vertex configuration. + + EXAMPLES:: + + sage: M = SixVertexModel(3, boundary_conditions='ice') + sage: M[0].check() """ - pass # We're not checking for now + if self not in self.parent(): + raise ValueError("invalid configuration") def _repr_(self): """ Return a string representation of ``self``. + + EXAMPLES:: + + sage: M = SixVertexModel(3, boundary_conditions='ice') + sage: M[0] + ^ ^ ^ + | | | + --> # <- # <- # <-- + | ^ ^ + V | | + --> # -> # <- # <-- + | | ^ + V V | + --> # -> # -> # <-- + | | | + V V V """ # List are in the order of URDL ascii = [[r' V ', ' -', r' ^ ', '- '], # LR @@ -75,15 +97,132 @@ def _repr_(self): return ret - def to_digraph(self): - """ - Return a digraph version of ``self``. + def to_signed_matrix(self): """ + Return the signed matrix of ``self``. + + The signed matrix corresponding to a six vertex configuration is + given by `0` if there is a cross flow, a `1` if the outward arrows + are vertical and `-1` if the outward arrows are horizonal. + + EXAMPLES:: - def plot(self): + sage: M = SixVertexModel(3, boundary_conditions='ice') + sage: map(lambda x: x.to_signed_matrix(), M) + [ + [1 0 0] [1 0 0] [ 0 1 0] [0 1 0] [0 1 0] [0 0 1] [0 0 1] + [0 1 0] [0 0 1] [ 1 -1 1] [1 0 0] [0 0 1] [1 0 0] [0 1 0] + [0 0 1], [0 1 0], [ 0 1 0], [0 0 1], [1 0 0], [0 1 0], [1 0 0] + ] + """ + from sage.matrix.constructor import matrix + # verts = ['LR', 'LU', 'LD', 'UD', 'UR', 'RD'] + def matrix_sign(x): + if x == 0: + return -1 + if x == 3: + return 1 + return 0 + return matrix([map(matrix_sign, row) for row in self]) + + def plot(self, color='sign'): """ Return a plot of ``self``. + + INPUT: + + - ``color`` -- can be any of the following: + + * ``4`` - use 4 colors: black, red, blue, and green with each + corresponding to up, right, down, and left respectively + * ``2`` - use 2 colors: red for horizontal, blue for vertical arrows + * ``'sign'`` - use red for right and down arrows, blue for left + and up arrows + * a list of 4 colors for each direction + * a function which takes a direction and a boolean corresponding + to the sign + + EXAMPLES:: + + sage: M = SixVertexModel(2, boundary_conditions='ice') + sage: print M[0].plot().description() + Arrow from (-1.0,0.0) to (0.0,0.0) + Arrow from (-1.0,1.0) to (0.0,1.0) + Arrow from (0.0,0.0) to (0.0,-1.0) + Arrow from (0.0,0.0) to (0.0,1.0) + Arrow from (0.0,1.0) to (1.0,1.0) + Arrow from (0.0,2.0) to (0.0,1.0) + Arrow from (1.0,0.0) to (0.0,0.0) + Arrow from (1.0,0.0) to (1.0,-1.0) + Arrow from (1.0,1.0) to (1.0,0.0) + Arrow from (1.0,1.0) to (1.0,2.0) + Arrow from (2.0,0.0) to (1.0,0.0) + Arrow from (2.0,1.0) to (1.0,1.0) """ + from sage.plot.graphics import Graphics + from sage.plot.circle import circle + from sage.plot.arrow import arrow + + if color == 4: + color_list = ['black', 'red', 'blue', 'green'] + cfunc = lambda d,pm: color_list[d] + elif color == 2: + cfunc = lambda d,pm: 'red' if d % 2 == 0 else 'blue' + elif color == 1 or color is None: + cfunc = lambda d,pm: 'black' + elif color == 'sign': + cfunc = lambda d,pm: 'red' if pm else 'blue' # RD are True + elif isinstance(color, (list, tuple)): + cfunc = lambda d,pm: color[d] + else: + cfunc = color + + G = Graphics() + for j,row in enumerate(self): + for i,entry in enumerate(row): + if entry == 0: # LR + G += arrow((i,j+1), (i,j), color=cfunc(2, True)) + G += arrow((i,j), (i+1,j), color=cfunc(1, True)) + if j == 0: + G += arrow((i,j-1), (i,j), color=cfunc(0, False)) + if i == 0: + G += arrow((i,j), (i-1,j), color=cfunc(3, False)) + elif entry == 1: # LU + G += arrow((i,j+1), (i,j), color=cfunc(2, True)) + G += arrow((i+1,j), (i,j), color=cfunc(3, False)) + if j == 0: + G += arrow((i,j), (i,j-1), color=cfunc(2, True)) + if i == 0: + G += arrow((i,j), (i-1,j), color=cfunc(3, False)) + elif entry == 2: # LD + G += arrow((i,j), (i,j+1), color=cfunc(0, False)) + G += arrow((i+1,j), (i,j), color=cfunc(3, False)) + if j == 0: + G += arrow((i,j-1), (i,j), color=cfunc(0, False)) + if i == 0: + G += arrow((i,j), (i-1,j), color=cfunc(3, False)) + elif entry == 3: # UD + G += arrow((i,j), (i,j+1), color=cfunc(0, False)) + G += arrow((i+1,j), (i,j), color=cfunc(3, False)) + if j == 0: + G += arrow((i,j), (i,j-1), color=cfunc(2, True)) + if i == 0: + G += arrow((i-1,j), (i,j), color=cfunc(1, True)) + elif entry == 4: # UR + G += arrow((i,j), (i,j+1), color=cfunc(0, False)) + G += arrow((i,j), (i+1,j), color=cfunc(1, True)) + if j == 0: + G += arrow((i,j-1), (i,j), color=cfunc(0, False)) + if i == 0: + G += arrow((i-1,j), (i,j), color=cfunc(1, True)) + elif entry == 5: # RD + G += arrow((i,j+1), (i,j), color=cfunc(2, True)) + G += arrow((i,j), (i+1,j), color=cfunc(1, True)) + if j == 0: + G += arrow((i,j), (i,j-1), color=cfunc(2, True)) + if i == 0: + G += arrow((i-1,j), (i,j), color=cfunc(1, True)) + return G class SixVertexModel(Parent, UniqueRepresentation): """ @@ -91,7 +230,7 @@ class SixVertexModel(Parent, UniqueRepresentation): We model a configuration by indicating which configuration by the following six configurations which are determined by the two outgoing - arrows: + arrows in the **U**p, **R**ight, **D**own, **L**eft directions: 1 - LR 2 - LU @@ -118,11 +257,61 @@ class SixVertexModel(Parent, UniqueRepresentation): * ``'outward'`` - all arrows are outward arrows * ``'ice'`` - the top and bottom boundary conditions are outward and the left and right boundary conditions are inward + + EXAMPLES: + + Here are the six types of vertices that can be created:: + + sage: M = SixVertexModel(1, 1) + sage: list(M) + [ + | ^ | ^ ^ | + V | V | | V + <-- # --> <-- # <-- <-- # <-- --> # <-- --> # --> --> # --> + ^ ^ | | ^ | + | , | , V , V , | , V + ] + + When using the square ice model, it is known that the number of + configurations is equal to the number of alternating sign matrices:: + + sage: M = SixVertexModel(1, boundary_conditions='ice') + sage: len(M) + 1 + sage: M = SixVertexModel(4, boundary_conditions='ice') + sage: len(M) + 42 + sage: all(len(SixVertexModel(n, boundary_conditions='ice')) + ....: == AlternatingSignMatrices(n).cardinality() for n in range(1, 7)) + True + + An example with a specified non-standard boundary condition and + non-rectangular shape:: + + sage: M = SixVertexModel(2, 1, [[None],[True,True],[None],[None,None]]) + sage: list(M) + [ + ^ ^ | ^ + | | V | + <-- # <-- <-- # <-- <-- # <-- --> # <-- + ^ ^ | | + | | V V + <-- # <-- --> # <-- <-- # <-- <-- # <-- + ^ | | | + | , V , V , V + ] """ @staticmethod def __classcall_private__(cls, n, m=None, boundary_conditions=None): """ Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: M1 = SixVertexModel(1, boundary_conditions=[[False],[True],[False],[True]]) + sage: M2 = SixVertexModel(1, 1, ((False,),(True,),(False,),(True,))) + sage: M1 is M2 + True """ if m is None: m = n @@ -141,6 +330,11 @@ def __classcall_private__(cls, n, m=None, boundary_conditions=None): def __init__(self, n, m, boundary_conditions): """ Initialize ``self``. + + EXAMPLES:: + + sage: M = SixVertexModel(2, boundary_conditions='ice') + sage: TestSuite(M).run() """ self._nrows = n self._ncols = m @@ -150,12 +344,23 @@ def __init__(self, n, m, boundary_conditions): def _repr_(self): """ Return a string representation of ``self``. + + EXAMPLES:: + + sage: SixVertexModel(2, boundary_conditions='ice') + The six vertex model on a 2 by 2 grid """ - return "The six vertex model on an {} by {} grid".format(self._nrows, self._ncols) + return "The six vertex model on a {} by {} grid".format(self._nrows, self._ncols) def boundary_conditions(self): """ Return the boundary conditions of ``self``. + + EXAMPLES:: + + sage: M = SixVertexModel(2, boundary_conditions='ice') + sage: M.boundary_conditions() + ((False, False), (True, True), (False, False), (True, True)) """ return self._bdry_cond @@ -164,6 +369,12 @@ def _repr_option(self, key): Metadata about the ``_repr_()`` output. See :meth:`sage.structure.parent._repr_option` for details. + + EXAMPLES:: + + sage: M = SixVertexModel(2, boundary_conditions='ice') + sage: M._repr_option('element_ascii_art') + True """ if key == 'element_ascii_art': return True @@ -172,6 +383,19 @@ def _repr_option(self, key): def _element_constructor_(self, x): """ Construct an element of ``self``. + + EXAMPLES:: + + sage: M = SixVertexModel(2, boundary_conditions='ice') + sage: M([[3,1],[5,3]]) + ^ ^ + | | + --> # <- # <-- + | ^ + V | + --> # -> # <-- + | | + V V """ if isinstance(x, SixVertexConfiguration): if x.parent() is not self: @@ -183,10 +407,10 @@ def _element_constructor_(self, x): for row in x: elt.append([]) for entry in row: - if x in verts: - elt[-1].append(verts.index(x)) - elif x in range(6): - elt[-1].append(x) + if entry in verts: + elt[-1].append(verts.index(entry)) + elif entry in range(6): + elt[-1].append(entry) else: raise ValueError("invalid entry") elt[-1] = tuple(elt[-1]) @@ -195,6 +419,21 @@ def _element_constructor_(self, x): def __iter__(self): """ Iterate through ``self``. + + EXAMPLES:: + + sage: M = SixVertexModel(2, boundary_conditions='ice') + sage: list(M) + [ + ^ ^ ^ ^ + | | | | + --> # <- # <-- --> # -> # <-- + | ^ ^ | + V | | V + --> # -> # <-- --> # <- # <-- + | | | | + V V , V V + ] """ # Boundary conditions ordered URDL # The top row boundary condition of True is a downward arrow @@ -235,8 +474,9 @@ def __iter__(self): l.pop() continue # Check to see if we can add the vertex - if check_left[row[-1]] is l[-1] \ - and check_top[row[-1]] is bdry[-1][len(row)-1]: + if (check_left[row[-1]] is l[-1] or l[-1] is None) \ + and (check_top[row[-1]] is bdry[-1][len(row)-1] + or bdry[-1][len(row)-1] is None): if len(row) != m: l.append(next_left[row[-1]]) row.append(-1) From dddb1fb66db711b30e093c67f1533c091c0aa2b8 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 12 Nov 2013 11:40:18 -0800 Subject: [PATCH 080/206] Adding additional methods to 6VM. --- src/doc/en/reference/combinat/index.rst | 1 + src/sage/combinat/six_vertex_model.py | 330 +++++++++++++++++++++--- 2 files changed, 297 insertions(+), 34 deletions(-) diff --git a/src/doc/en/reference/combinat/index.rst b/src/doc/en/reference/combinat/index.rst index f8106aadbda..4c83a58b249 100644 --- a/src/doc/en/reference/combinat/index.rst +++ b/src/doc/en/reference/combinat/index.rst @@ -49,6 +49,7 @@ Combinatorics sage/combinat/q_bernoulli sage/combinat/rsk sage/combinat/sidon_sets + sage/combinat/six_vertex_model sage/combinat/set_partition_ordered sage/combinat/set_partition sage/combinat/abstract_tree diff --git a/src/sage/combinat/six_vertex_model.py b/src/sage/combinat/six_vertex_model.py index 77a12361457..47df03ed992 100644 --- a/src/sage/combinat/six_vertex_model.py +++ b/src/sage/combinat/six_vertex_model.py @@ -6,6 +6,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.list_clone import ClonableArray from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.combinat.combinatorial_map import combinatorial_map class SixVertexConfiguration(ClonableArray): """ @@ -149,12 +150,12 @@ def plot(self, color='sign'): Arrow from (-1.0,0.0) to (0.0,0.0) Arrow from (-1.0,1.0) to (0.0,1.0) Arrow from (0.0,0.0) to (0.0,-1.0) - Arrow from (0.0,0.0) to (0.0,1.0) - Arrow from (0.0,1.0) to (1.0,1.0) - Arrow from (0.0,2.0) to (0.0,1.0) - Arrow from (1.0,0.0) to (0.0,0.0) + Arrow from (0.0,0.0) to (1.0,0.0) + Arrow from (0.0,1.0) to (0.0,0.0) + Arrow from (0.0,1.0) to (0.0,2.0) Arrow from (1.0,0.0) to (1.0,-1.0) - Arrow from (1.0,1.0) to (1.0,0.0) + Arrow from (1.0,0.0) to (1.0,1.0) + Arrow from (1.0,1.0) to (0.0,1.0) Arrow from (1.0,1.0) to (1.0,2.0) Arrow from (2.0,0.0) to (1.0,0.0) Arrow from (2.0,1.0) to (1.0,1.0) @@ -178,7 +179,7 @@ def plot(self, color='sign'): cfunc = color G = Graphics() - for j,row in enumerate(self): + for j,row in enumerate(reversed(self)): for i,entry in enumerate(row): if entry == 0: # LR G += arrow((i,j+1), (i,j), color=cfunc(2, True)) @@ -188,17 +189,17 @@ def plot(self, color='sign'): if i == 0: G += arrow((i,j), (i-1,j), color=cfunc(3, False)) elif entry == 1: # LU - G += arrow((i,j+1), (i,j), color=cfunc(2, True)) + G += arrow((i,j), (i,j+1), color=cfunc(0, False)) G += arrow((i+1,j), (i,j), color=cfunc(3, False)) if j == 0: - G += arrow((i,j), (i,j-1), color=cfunc(2, True)) + G += arrow((i,j-1), (i,j), color=cfunc(0, False)) if i == 0: G += arrow((i,j), (i-1,j), color=cfunc(3, False)) elif entry == 2: # LD - G += arrow((i,j), (i,j+1), color=cfunc(0, False)) + G += arrow((i,j+1), (i,j), color=cfunc(2, True)) G += arrow((i+1,j), (i,j), color=cfunc(3, False)) if j == 0: - G += arrow((i,j-1), (i,j), color=cfunc(0, False)) + G += arrow((i,j), (i,j-1), color=cfunc(2, True)) if i == 0: G += arrow((i,j), (i-1,j), color=cfunc(3, False)) elif entry == 3: # UD @@ -222,22 +223,126 @@ def plot(self, color='sign'): G += arrow((i,j), (i,j-1), color=cfunc(2, True)) if i == 0: G += arrow((i-1,j), (i,j), color=cfunc(1, True)) + G.axes(False) return G + def energy(self, epsilon): + r""" + Return the energy of the configuration. + + The energy of a configuration `\nu` is defined as + + .. MATH:: + + E(\nu) = n_0 \epsilon_0 + n_1 \epsilon_1 + \cdots + n_5 \epsilon_5 + + where `n_i` is the number of vertices of type `i` and + `\epsilon_i` is the `i`-th energy constant. + + .. NOTE:: + + We number our configurations as: + + 0. LR + 1. LU + 2. LD + 3. UD + 4. UR + 5. RD + + which differs from :wikipedia:`Ice-type_model`. + + EXAMPLES:: + + sage: M = SixVertexModel(3, boundary_conditions='ice') + sage: nu = M[2]; nu + ^ ^ ^ + | | | + --> # -> # <- # <-- + ^ | ^ + | V | + --> # <- # -> # <-- + | ^ | + V | V + --> # -> # <- # <-- + | | | + V V V + sage: nu.energy([1,2,1,2,1,2]) + 15 + + A KDP energy:: + + sage: nu.energy([1,1,0,1,0,1]) + 7 + + A Rys `F` energy:: + + sage: nu.energy([0,1,1,0,1,1]) + 4 + + The zero field assumption:: + + sage: nu.energy([1,2,3,1,3,2]) + 15 + """ + if len(epsilon) != 6: + raise ValueError("there must be 6 energy constants") + return sum(epsilon[entry] for row in self for entry in row) + class SixVertexModel(Parent, UniqueRepresentation): """ The six vertex model. We model a configuration by indicating which configuration by the following six configurations which are determined by the two outgoing - arrows in the **U**p, **R**ight, **D**own, **L**eft directions: + arrows in the Up, Right, Down, Left directions: + + 1. LR:: + + | + V + <-- # --> + ^ + | + + 2. LU:: + + ^ + | + <-- # <-- + ^ + | - 1 - LR - 2 - LU - 3 - LD - 4 - UD - 5 - UR - 6 - RD + 3. LD:: + + | + V + <-- # <-- + | + V + + 4. UD:: + + ^ + | + --> # <-- + | + V + + 5. UR:: + + ^ + | + --> # --> + ^ + | + 6. RD:: + + | + V + --> # --> + | + V INPUT: @@ -249,14 +354,15 @@ class SixVertexModel(Parent, UniqueRepresentation): * ``True`` for an inward arrow, * ``False`` for an outward arrow, or - * ``None`` for no boundary condition + * ``None`` for no boundary condition. - There are also the following special cases: + There are also the following predefined boundary conditions: * ``'inward'`` - all arrows are inward arrows * ``'outward'`` - all arrows are outward arrows * ``'ice'`` - the top and bottom boundary conditions are outward and the - left and right boundary conditions are inward + left and right boundary conditions are inward; this gives the square + ice model EXAMPLES: @@ -300,6 +406,11 @@ class SixVertexModel(Parent, UniqueRepresentation): ^ | | | | , V , V , V ] + + REFERENCES: + + - :wikipedia:`Vertex_model` + - :wikipedia:`Ice-type_model` """ @staticmethod def __classcall_private__(cls, n, m=None, boundary_conditions=None): @@ -322,7 +433,7 @@ def __classcall_private__(cls, n, m=None, boundary_conditions=None): elif boundary_conditions == 'outward': boundary_conditions = ((False,)*m, (False,)*n)*2 elif boundary_conditions == 'ice': - boundary_conditions = ((False,)*m, (True,)*n)*2 + return SquareIceModel(n) else: boundary_conditions = tuple(tuple(x) for x in boundary_conditions) return super(SixVertexModel, cls).__classcall__(cls, n, m, boundary_conditions) @@ -352,18 +463,6 @@ def _repr_(self): """ return "The six vertex model on a {} by {} grid".format(self._nrows, self._ncols) - def boundary_conditions(self): - """ - Return the boundary conditions of ``self``. - - EXAMPLES:: - - sage: M = SixVertexModel(2, boundary_conditions='ice') - sage: M.boundary_conditions() - ((False, False), (True, True), (False, False), (True, True)) - """ - return self._bdry_cond - def _repr_option(self, key): """ Metadata about the ``_repr_()`` output. @@ -416,6 +515,8 @@ def _element_constructor_(self, x): elt[-1] = tuple(elt[-1]) return self.element_class(self, tuple(elt)) + Element = SixVertexConfiguration + def __iter__(self): """ Iterate through ``self``. @@ -493,5 +594,166 @@ def __iter__(self): bdry.pop() left.pop() - Element = SixVertexConfiguration + def boundary_conditions(self): + """ + Return the boundary conditions of ``self``. + + EXAMPLES:: + + sage: M = SixVertexModel(2, boundary_conditions='ice') + sage: M.boundary_conditions() + ((False, False), (True, True), (False, False), (True, True)) + """ + return self._bdry_cond + + def partition_function(self, beta, epsilon): + r""" + Return the partition function of ``self``. + + The partition function of a 6 vertex model is defined by: + + .. MATH:: + + Z = \sum_{\nu} e^{-\beta E(\nu)} + + where we sum over all configurations and `E` is the energy function. + The constant `\beta` is known as the *inverse temperature* and is + equal to `1 / k_B T` where `k_B` is Boltzmann's constant and `T` is + the system's temperature. + + INPUT: + + - ``beta`` -- the inverse temperature constant `\beta` + - ``epsilon`` -- the energy constants, see + :meth:`~sage.combinat.six_vertex_model.SixVertexConfiguration.energy()` + + EXAMPLES:: + + sage: M = SixVertexModel(3, boundary_conditions='ice') + sage: M.partition_function(2, [1,2,1,2,1,2]) + e^(-24) + 2*e^(-28) + e^(-30) + 2*e^(-32) + e^(-36) + + REFERENCES: + + :wikipedia:`Partition_function_(statistical_mechanics)` + """ + from sage.functions.log import exp + return sum(exp(-beta * nu.energy(epsilon)) for nu in self) + +class SquareIceModel(SixVertexModel): + r""" + The square ice model. + + The square ice model is a 6 vertex model on an `n \times n` grid with + the boundary conditions that the top and bottom boundaries are pointing + outward and the left and right boundaries are pointing inward. + + These are in bijection with alternating sign matrices. + """ + def __init__(self, n): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: M = SixVertexModel(3, boundary_conditions='ice') + sage: TestSuite(M).run() + """ + boundary_conditions = ((False,)*n, (True,)*n)*2 + SixVertexModel.__init__(self, n, n, boundary_conditions) + + def from_alternating_sign_matrix(self, asm): + """ + Return a configuration from the alternating sign matrix ``asm``. + + EXAMPLES:: + + sage: M = SixVertexModel(3, boundary_conditions='ice') + sage: asm = AlternatingSignMatrix([[0,1,0],[1,-1,1],[0,1,0]]) + sage: M.from_alternating_sign_matrix(asm) + ^ ^ ^ + | | | + --> # -> # <- # <-- + ^ | ^ + | V | + --> # <- # -> # <-- + | ^ | + V | V + --> # -> # <- # <-- + | | | + V V V + + TESTS:: + + sage: M = SixVertexModel(5, boundary_conditions='ice') + sage: ASM = AlternatingSignMatrices(5) + sage: all(M.from_alternating_sign_matrix(x.to_alternating_sign_matrix()) == x + ....: for x in M) + True + sage: all(M.from_alternating_sign_matrix(x).to_alternating_sign_matrix() == x + ....: for x in ASM) + True + """ + if asm.parent().size() != self._nrows: + raise ValueError("mismatched size") + + #verts = ['LR', 'LU', 'LD', 'UD', 'UR', 'RD'] + ret = [] + bdry = [False]*self._nrows # False = up + for row in asm.to_matrix(): + cur = [] + right = True # True = right + for j,entry in enumerate(row): + if entry == -1: + cur.append(0) + right = True + bdry[j] = False + elif entry == 1: + cur.append(3) + right = False + bdry[j] = True + else: # entry == 0 + if bdry[j]: + if right: + cur.append(5) + else: + cur.append(2) + else: + if right: + cur.append(4) + else: + cur.append(1) + ret.append(tuple(cur)) + return self.element_class(self, tuple(ret)) + + class Element(SixVertexConfiguration): + """ + An element in the square ice model. + """ + @combinatorial_map(name='to alternating sign matrix') + def to_alternating_sign_matrix(self): + """ + Return an alternating sign matrix of ``self``. + + .. SEEALSO:: + + :meth:`~sage.combinat.six_vertex_model.SixVertexConfiguration.to_signed_matrix()` + + EXAMPLES:: + + sage: M = SixVertexModel(4, boundary_conditions='ice') + sage: M[6].to_alternating_sign_matrix() + [1 0 0 0] + [0 0 0 1] + [0 0 1 0] + [0 1 0 0] + sage: M[7].to_alternating_sign_matrix() + [ 0 1 0 0] + [ 1 -1 1 0] + [ 0 1 -1 1] + [ 0 0 1 0] + """ + from sage.combinat.alternating_sign_matrix import AlternatingSignMatrices + ASM = AlternatingSignMatrices(self.parent()._nrows) + return ASM(self.to_signed_matrix()) From 76ee864428576db91a6421c88a86afd0f5436d0d Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Wed, 13 Nov 2013 00:11:29 -0800 Subject: [PATCH 081/206] Simplify sage/symbolic cythonization. --- src/module_list.py | 34 +++++----------------------------- src/sage/libs/ginac.pxd | 3 +++ src/sage/symbolic/pynac.pyx | 2 ++ 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/src/module_list.py b/src/module_list.py index 8f3ddbc3b7d..fbbee1a5346 100755 --- a/src/module_list.py +++ b/src/module_list.py @@ -43,7 +43,6 @@ flint_depends = [SAGE_INC + '/flint/flint.h'] singular_depends = [SAGE_INC + '/libsingular.h', SAGE_INC + '/givaro/givconfig.h'] -ginac_depends = [SAGE_INC + '/pynac/ginac.h'] ######################################################### ### M4RI flags @@ -1979,41 +1978,18 @@ def uname_specific(name, value, alternative): ## sage.symbolic ## ################################ - Extension('sage.symbolic.constants_c', - sources = ['sage/symbolic/constants_c.pyx'], - language = 'c++', - depends = ginac_depends, - libraries = ["pynac", "gmp"]), - - Extension('sage.symbolic.expression', - sources = ['sage/symbolic/expression.pyx'], - language = 'c++', - depends = ginac_depends, - libraries = ["pynac", "gmp"]), - Extension('sage.symbolic.getitem', - sources = ['sage/symbolic/getitem.pyx'], - language = 'c++', - depends = [SAGE_INC + "/pynac/ginac.h"], - libraries = ["pynac", "gmp"]), + # TODO: Verify numpy depends are also found automatically. Extension('sage.symbolic.function', sources = ['sage/symbolic/function.pyx'], - language = 'c++', - depends = ginac_depends + numpy_depends, - libraries = ["pynac", "gmp"]), - - Extension('sage.symbolic.pynac', - sources = ['sage/symbolic/pynac.pyx'], - language = 'c++', - depends = ginac_depends, - libraries = ["pynac", "gmp", "gsl"]), + depends = numpy_depends), Extension('sage.symbolic.ring', sources = ['sage/symbolic/ring.pyx'], - language = 'c++', - depends = ginac_depends + numpy_depends, - libraries = ["pynac", "gmp"]), + depends = numpy_depends), + + Extension('*', ['sage/symbolic/*.pyx']), ################################ ## diff --git a/src/sage/libs/ginac.pxd b/src/sage/libs/ginac.pxd index a0423581474..c8b59b9d4ef 100644 --- a/src/sage/libs/ginac.pxd +++ b/src/sage/libs/ginac.pxd @@ -11,6 +11,9 @@ # we do *not* have to use sig_on() and sig_off(). We do use it a little # in the actual pyx code to catch control-c for long running functions. +# distutils: language = c++ +# distutils: libraries = pynac gmp + from cpython cimport PyObject cdef extern from "ginac_wrap.h": diff --git a/src/sage/symbolic/pynac.pyx b/src/sage/symbolic/pynac.pyx index 62b1425a9f2..8bd4613ad07 100644 --- a/src/sage/symbolic/pynac.pyx +++ b/src/sage/symbolic/pynac.pyx @@ -7,6 +7,8 @@ # http://www.gnu.org/licenses/ ############################################################################### +# distutils: libraries = gsl + cdef extern from "pynac_cc.h": long double sage_logl(long double) long double sage_sqrtl(long double) From 96421645fc2f50074a441d1832130e0b1a24db3c Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Wed, 13 Nov 2013 21:21:55 +0100 Subject: [PATCH 082/206] Documentation edits. --- src/sage/combinat/six_vertex_model.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/six_vertex_model.py b/src/sage/combinat/six_vertex_model.py index 47df03ed992..81469bf8b62 100644 --- a/src/sage/combinat/six_vertex_model.py +++ b/src/sage/combinat/six_vertex_model.py @@ -362,7 +362,7 @@ class SixVertexModel(Parent, UniqueRepresentation): * ``'outward'`` - all arrows are outward arrows * ``'ice'`` - the top and bottom boundary conditions are outward and the left and right boundary conditions are inward; this gives the square - ice model + ice model. Also called domain wall boundary conditions. EXAMPLES: @@ -646,9 +646,11 @@ class SquareIceModel(SixVertexModel): The square ice model is a 6 vertex model on an `n \times n` grid with the boundary conditions that the top and bottom boundaries are pointing - outward and the left and right boundaries are pointing inward. + outward and the left and right boundaries are pointing inward. These + boundary conditions are also called domain wall boundary conditions. - These are in bijection with alternating sign matrices. + Configurations of the 6 vertex model with domain wall boundary conditions + are in bijection with alternating sign matrices. """ def __init__(self, n): """ From c72f0729a133872b3fc373c75fadb9ebe5a1f824 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 13 Nov 2013 15:11:12 -0800 Subject: [PATCH 083/206] Made changes that Jessica suggested. --- src/sage/combinat/six_vertex_model.py | 51 ++++++++++++++++++--------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/sage/combinat/six_vertex_model.py b/src/sage/combinat/six_vertex_model.py index 81469bf8b62..d285cc2c1fd 100644 --- a/src/sage/combinat/six_vertex_model.py +++ b/src/sage/combinat/six_vertex_model.py @@ -358,17 +358,19 @@ class SixVertexModel(Parent, UniqueRepresentation): There are also the following predefined boundary conditions: - * ``'inward'`` - all arrows are inward arrows - * ``'outward'`` - all arrows are outward arrows - * ``'ice'`` - the top and bottom boundary conditions are outward and the + * ``'ice'`` - The top and bottom boundary conditions are outward and the left and right boundary conditions are inward; this gives the square ice model. Also called domain wall boundary conditions. + * ``'domain wall'`` - Same as ``'ice'``. + * ``'alternating'`` - The boundary conditions alternate between inward + and outward. + * ``'free'`` - There are no boundary conditions. EXAMPLES: Here are the six types of vertices that can be created:: - sage: M = SixVertexModel(1, 1) + sage: M = SixVertexModel(1) sage: list(M) [ | ^ | ^ ^ | @@ -394,7 +396,7 @@ class SixVertexModel(Parent, UniqueRepresentation): An example with a specified non-standard boundary condition and non-rectangular shape:: - sage: M = SixVertexModel(2, 1, [[None],[True,True],[None],[None,None]]) + sage: M = SixVertexModel(2, 1, [[None], [True,True], [None], [None,None]]) sage: list(M) [ ^ ^ | ^ @@ -426,14 +428,27 @@ def __classcall_private__(cls, n, m=None, boundary_conditions=None): """ if m is None: m = n - if boundary_conditions is None: + if boundary_conditions is None or boundary_conditions == 'free': boundary_conditions = ((None,)*m, (None,)*n)*2 - elif boundary_conditions == 'inward': - boundary_conditions = ((True,)*m, (True,)*n)*2 - elif boundary_conditions == 'outward': - boundary_conditions = ((False,)*m, (False,)*n)*2 - elif boundary_conditions == 'ice': - return SquareIceModel(n) + elif boundary_conditions == 'alternating': + bdry = True + cond = [] + for dummy in range(2): + val = [] + for k in range(m): + val.append(bdry) + bdry = not bdry + cond.append(tuple(val)) + val = [] + for k in range(n): + val.append(bdry) + bdry = not bdry + cond.append(tuple(val)) + boundary_conditions = tuple(cond) + elif boundary_conditions == 'ice' or boundary_conditions == 'domain wall': + if m == n: + return SquareIceModel(n) + boundary_conditions = ((False,)*m, (True,)*n)*2 else: boundary_conditions = tuple(tuple(x) for x in boundary_conditions) return super(SixVertexModel, cls).__classcall__(cls, n, m, boundary_conditions) @@ -555,13 +570,15 @@ def __iter__(self): # [[4, 3], [3, 2]] while len(cur) > 0: - # If we're at the last row and all our final boundry condition is statisfied - if len(cur) > n and all(x is not self._bdry_cond[2][i] - for i,x in enumerate(bdry[-1])): + # If we're at the last row + if len(cur) > n: cur.pop() bdry.pop() left.pop() - yield self.element_class(self, tuple(tuple(x) for x in cur)) + # Check if all our bottom boundry conditions are statisfied + if all(x is not self._bdry_cond[2][i] + for i,x in enumerate(bdry[-1])): + yield self.element_class(self, tuple(tuple(x) for x in cur)) # Find the next row row = cur[-1] @@ -589,7 +606,7 @@ def __iter__(self): break # If we've killed this row, backup - if len(cur[-1]) == 0: + if len(row) == 0: cur.pop() bdry.pop() left.pop() From d00a68d4377dbafa8495e3f445d6cadb54f09c16 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 14 Nov 2013 09:41:17 -0800 Subject: [PATCH 084/206] Fixed bug. --- src/sage/combinat/six_vertex_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/six_vertex_model.py b/src/sage/combinat/six_vertex_model.py index d285cc2c1fd..759e45ad869 100644 --- a/src/sage/combinat/six_vertex_model.py +++ b/src/sage/combinat/six_vertex_model.py @@ -573,12 +573,12 @@ def __iter__(self): # If we're at the last row if len(cur) > n: cur.pop() - bdry.pop() left.pop() # Check if all our bottom boundry conditions are statisfied if all(x is not self._bdry_cond[2][i] for i,x in enumerate(bdry[-1])): yield self.element_class(self, tuple(tuple(x) for x in cur)) + bdry.pop() # Find the next row row = cur[-1] From b2ba47e743d882999d716f4ac810916eda534413 Mon Sep 17 00:00:00 2001 From: Maria Monks Date: Thu, 14 Nov 2013 15:29:30 -0800 Subject: [PATCH 085/206] Added Foata bijection --- src/sage/combinat/permutation.py | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 965df9ef131..d5e54cb9649 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -1975,6 +1975,76 @@ def cycle_type(self): from sage.combinat.partition import Partition return Partition(cycle_type) + + @combinatorial_map(name='foata_bijection') + def foata_bijection(self): + r""" + Returns the image of the permutation under the Foata bijection `\phi`. + + The bijection shows that maj and inv are equidistributed: if + `\phi(P)=Q`, then `\mathrm{maj}(P)=\mathrm{inv}(Q)`. + + The bijection `phi` can be defined inductively on the size of + the word. Given a word `w_1w_2\ldots w_n`, start with `\phi(w_1)=w_1`. + At the `i`th step, if `phi(w_1\ldots w_i)=v_1\ldots v_i`, we define + `phi(w_1\ldots w_{i+1})` by placing `w_{i+1}` on the end of the word + `v_1\ldots v_i`, and breaking the word up into blocks as follows. + If `w_{i+1}>v_i`, place a vertical line to the right of all `v_k` + for which `w_{i+1}>v_k`. Otherwise, if `w_{i+1}1: + a=M[-2]; + M_prime=[0]*k + if a>e: + index_list=[i for i in range(k) if M[i]>e]; + index_list=[-1]+index_list; + t=len(index_list); + for j in range(1,t): + start=index_list[j-1]+1; + end=index_list[j]; + M_prime[start]=M[end]; + for x in range(start+1,end+1): + M_prime[x]=M[x-1]; + M_prime[k-1]=e; + else: + index_list=[i for i in range(k) if M[i] Date: Fri, 15 Nov 2013 22:35:17 +0100 Subject: [PATCH 086/206] Doc test edits --- src/sage/combinat/permutation.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index d5e54cb9649..b44dae8ca6b 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -1975,7 +1975,6 @@ def cycle_type(self): from sage.combinat.partition import Partition return Partition(cycle_type) - @combinatorial_map(name='foata_bijection') def foata_bijection(self): r""" @@ -1988,7 +1987,7 @@ def foata_bijection(self): the word. Given a word `w_1w_2\ldots w_n`, start with `\phi(w_1)=w_1`. At the `i`th step, if `phi(w_1\ldots w_i)=v_1\ldots v_i`, we define `phi(w_1\ldots w_{i+1})` by placing `w_{i+1}` on the end of the word - `v_1\ldots v_i`, and breaking the word up into blocks as follows. + `v_1\ldots v_i` and breaking the word up into blocks as follows. If `w_{i+1}>v_i`, place a vertical line to the right of all `v_k` for which `w_{i+1}>v_k`. Otherwise, if `w_{i+1} Date: Fri, 15 Nov 2013 17:47:14 -0800 Subject: [PATCH 087/206] formatting changes (we aren't saving the environment) --- src/sage/combinat/permutation.py | 127 ++++++++++++++++++------------- 1 file changed, 73 insertions(+), 54 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index b44dae8ca6b..f159993f9c5 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -55,6 +55,7 @@ :meth:`~sage.combinat.permutation.Permutation.longest_increasing_subsequence_length` | Returns the length of the longest increasing subsequences of ``self``. :meth:`~sage.combinat.permutation.Permutation.longest_increasing_subsequences` | Returns the list of the longest increasing subsequences of ``self``. :meth:`~sage.combinat.permutation.Permutation.cycle_type` | Returns the cycle type of ``self`` as a partition of ``len(self)``. + :meth:`~sage.combinat.permutation.Permutation.foata_bijection` | Returns the image of the permutation ``self`` under the Foata bijection `\phi`. :meth:`~sage.combinat.permutation.Permutation.to_lehmer_code` | Returns the Lehmer code of the permutation ``self``. :meth:`~sage.combinat.permutation.Permutation.to_lehmer_cocode` | Returns the Lehmer cocode of ``self``. :meth:`~sage.combinat.permutation.Permutation.reduced_word` | Returns the reduced word of the permutation ``self``. @@ -1978,70 +1979,88 @@ def cycle_type(self): @combinatorial_map(name='foata_bijection') def foata_bijection(self): r""" - Returns the image of the permutation under the Foata bijection `\phi`. + Return the image of the permutation ``self`` under the Foata + bijection `\phi`. - The bijection shows that maj and inv are equidistributed: if + The bijection shows that maj and inv are equidistributed: if `\phi(P)=Q`, then `\mathrm{maj}(P)=\mathrm{inv}(Q)`. - - The bijection `phi` can be defined inductively on the size of - the word. Given a word `w_1w_2\ldots w_n`, start with `\phi(w_1)=w_1`. - At the `i`th step, if `phi(w_1\ldots w_i)=v_1\ldots v_i`, we define - `phi(w_1\ldots w_{i+1})` by placing `w_{i+1}` on the end of the word - `v_1\ldots v_i` and breaking the word up into blocks as follows. - If `w_{i+1}>v_i`, place a vertical line to the right of all `v_k` - for which `w_{i+1}>v_k`. Otherwise, if `w_{i+1}v_i`, place a vertical line to the right + of each `v_k` for which `w_{i+1}>v_k`. Otherwise, if + `w_{i+1}1: - a=M[-2]; - M_prime=[0]*k - if a>e: - index_list=[i for i in range(k) if M[i]>e]; - index_list=[-1]+index_list; - t=len(index_list); - for j in range(1,t): - start=index_list[j-1]+1; - end=index_list[j]; - M_prime[start]=M[end]; - for x in range(start+1,end+1): - M_prime[x]=M[x-1]; - M_prime[k-1]=e; + M.append(e) + k = len(M) + if k > 1: + a = M[-2] + M_prime = [0]*k + if a > e: + index_list = [i for i in range(k) if M[i] > e] + index_list = [-1] + index_list + t = len(index_list) + for j in range(1, t): + start = index_list[j-1] + 1 + end = index_list[j] + M_prime[start] = M[end] + for x in range(start + 1, end + 1): + M_prime[x] = M[x-1] + M_prime[k-1] = e else: - index_list=[i for i in range(k) if M[i] Date: Fri, 15 Nov 2013 17:53:03 -0800 Subject: [PATCH 088/206] no need to compare e with itself --- src/sage/combinat/permutation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index f159993f9c5..7a66da064a8 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -2038,7 +2038,7 @@ def foata_bijection(self): a = M[-2] M_prime = [0]*k if a > e: - index_list = [i for i in range(k) if M[i] > e] + index_list = [i for i in range(k - 1) if M[i] > e] index_list = [-1] + index_list t = len(index_list) for j in range(1, t): @@ -2049,8 +2049,8 @@ def foata_bijection(self): M_prime[x] = M[x-1] M_prime[k-1] = e else: - index_list = [i for i in range(k) if M[i] < e] - index_list = [-1]+index_list + index_list = [i for i in range(k - 1) if M[i] < e] + index_list = [-1] + index_list t = len(index_list) for j in range(1, t): start = index_list[j-1] + 1 From 76b82a84d76a340d242cf4e6791dc763938f8d51 Mon Sep 17 00:00:00 2001 From: Darij Grinberg Date: Fri, 15 Nov 2013 18:05:58 -0800 Subject: [PATCH 089/206] +reference +example --- src/sage/combinat/permutation.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 7a66da064a8..1bd1fa1552d 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -2005,6 +2005,16 @@ def foata_bijection(self): `|4|125|3\to 45123`. So `\phi([1,4,2,5,3])=[4,5,1,2,3]`. + See section 2 of [FoSc78]_. + + REFERENCES:: + + .. [FoSc78] Dominique Foata, Marcel-Paul Schuetzenberger, + *Major Index and Inversion Number of Permutations*, + Mathematische Nachrichten, volume 83, Issue 1, pages 143-159, + 1978. + http://igm.univ-mlv.fr/~berstel/Mps/Travaux/A/1978-3MajorIndexMathNachr.pdf + EXAMPLES:: sage: Permutation([1,2,4,3]).foata_bijection() @@ -2021,6 +2031,11 @@ def foata_bijection(self): ....: for P in Permutations(4) ) True + The example from [FoSc78]_:: + + sage: Permutation([7,4,9,2,6,1,5,8,3]).foata_bijection() + [4, 7, 2, 6, 1, 9, 5, 8, 3] + Border cases:: sage: Permutation([]).foata_bijection() From 0550ecb68e3831798a46103359b5cc0116ecbf86 Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Sat, 16 Nov 2013 19:34:00 -0800 Subject: [PATCH 090/206] trac #15367: Improved implementation of TripleDict and MonoDict, based on open addressing. This implementation should be faster for all operations and have a smaller memory footprint. --- src/sage/structure/coerce_dict.pxd | 33 +- src/sage/structure/coerce_dict.pyx | 1646 ++++++++++++++-------------- 2 files changed, 822 insertions(+), 857 deletions(-) diff --git a/src/sage/structure/coerce_dict.pxd b/src/sage/structure/coerce_dict.pxd index 7c95aeb16df..089989c3fb0 100644 --- a/src/sage/structure/coerce_dict.pxd +++ b/src/sage/structure/coerce_dict.pxd @@ -1,26 +1,31 @@ +from cpython cimport PyObject + +cdef struct mono_cell + cdef class MonoDict: cdef __weakref__ - cdef Py_ssize_t _size - cdef buckets + cdef size_t mask + cdef size_t used + cdef size_t fill + cdef mono_cell* table cdef bint weak_values - cdef double threshold - cdef public MonoDictEraser eraser + cdef eraser + cdef mono_cell* lookup(self,PyObject* key) cdef get(self, object k) cdef set(self, object k, value) + cdef int resize(self) except -1 -cdef class MonoDictEraser: - cdef object D +cdef struct triple_cell cdef class TripleDict: cdef __weakref__ - cdef Py_ssize_t _size - cdef buckets + cdef size_t mask + cdef size_t used + cdef size_t fill + cdef triple_cell* table cdef bint weak_values - cdef double threshold - cdef public TripleDictEraser eraser + cdef eraser + cdef triple_cell* lookup(self, PyObject* key1, PyObject* key2, PyObject* key3) cdef get(self, object k1, object k2, object k3) cdef set(self, object k1, object k2, object k3, value) - -cdef class TripleDictEraser: - cdef object D - + cdef int resize(self) except -1 diff --git a/src/sage/structure/coerce_dict.pyx b/src/sage/structure/coerce_dict.pyx index a7d3cb979e0..b8e5b8f621f 100644 --- a/src/sage/structure/coerce_dict.pyx +++ b/src/sage/structure/coerce_dict.pyx @@ -1,6 +1,7 @@ #***************************************************************************** # Copyright (C) 2007 Robert Bradshaw # 2012 Simon King +# 2013 Nils Bruin # # Distributed under the terms of the GNU General Public License (GPL) # @@ -40,14 +41,35 @@ used with weak references on the values. """ from cpython.list cimport * - +from cpython.mem cimport * +from cpython.string cimport PyString_FromString +from cpython cimport Py_XINCREF, Py_XDECREF +from libc.string cimport memset from weakref import KeyedRef, ref +#to avoid having to go and look in the module dictionary every time, we put +#a pointer to this class in here. No monkeypatching! or if you want to, perhaps +#cdef public this so that it can still be accessed. +#furthermore, by declaring this as "type", cython will compile +#isinstance(O,fixed_KeyedRef) to PyObject_CheckType(O, fixed_KeyedRef) +#which is considerable faster than PyObject_IsInstance(O, fixed_KeyedRef) +cdef type fixed_KeyedRef = KeyedRef +cdef type fixed_ref = ref + +#we use a capsule to wrap our void* in the callback parameter. +#no assumptions on whether a void* fits in a Py_ssize_t anymore! +from cpython.pycapsule cimport PyCapsule_New, PyCapsule_GetPointer cdef extern from "Python.h": - Py_ssize_t PyInt_AsSsize_t(PyObject* io) - PyObject* PyWeakref_GetObject(object ref) + PyObject* PyWeakref_GetObject(object r) PyObject* Py_None + int PyList_SetItem(object list, Py_ssize_t index,PyObject * item) except -1 + + ctypedef int (*visitproc)(PyObject* ob, void* arg) + ctypedef struct PyTypeObject: + void * tp_traverse + void * tp_clear +#this serves no purpose here anymore. Perhaps elsewhere? cpdef inline Py_ssize_t signed_id(x): """ A function like Python's :func:`id` returning *signed* integers, @@ -70,31 +92,72 @@ cpdef inline Py_ssize_t signed_id(x): """ return (x) -import gc - -############################################ -# A note about how to store "id" keys in python structures: -# -# We use the type Py_ssize_t to store "id"s generated by the signed_id -# function defined above. Assuming that Py_ssize_t is the same as a C -# long (which is true on most Unix-like systems), this also has the -# advantage that these Py_ssize_t values are stored as a Python "int" -# (as opposed to "long"), which allow for fast conversion to/from C -# types. -# -# There is one place where we have to be careful about signs: -# Our hash values most naturally live in Py_ssize_t. We convert those into -# an index into our bucket list by taking the hash modulo the number of buckets. -# However, the modulo operator in C preserves the sign of the number we take the -# modulus of, which is not what we want. -# The solution is to always do -# ( h) % modulus -# to ensure we're doing an unsigned modulus. - -############################################ -# The following code is responsible for -# removing dead references from the cache -############################################ +#it's important that this is not an interned string: this object +#must be a unique sentinel. We could reuse the "dummy" sentinel +#that is defined in python's dictobject.c + +cdef object dummy_object = PyString_FromString("dummy") +cdef PyObject* dummy = dummy_object + +cdef struct mono_cell: + void* key_id + PyObject* key_weakref + PyObject* value + +cdef object extract_mono_cell(mono_cell *cell): + #takes the refcounted components from a mono_cell + #and puts them in a list and returns it. + #The mono_cell itself is marked as "freed". + #The refcounts originally accounting for the + #presence in the mono_cell now account for the presence + #in the returned list. + # + #the returned result is only used to throw away: + #an advantage is that the containing list participates + #in CPython's trashcan, which prevents stack overflow + #on large dereffing cascades. + # + #a slight disadvantage is that this routine needs to + #allocate a list (mainly just to be thrown away) + if cell.key_id != NULL and cell.key_id != dummy : + L=PyList_New(2) + PyList_SetItem(L,0,cell.key_weakref) + PyList_SetItem(L,1,cell.value) + cell.key_id=dummy + cell.key_weakref=NULL + cell.value=NULL + return L + else: + raise RuntimeError("unused mono_cell") + +cdef struct triple_cell: + void* key_id1 + void* key_id2 + void* key_id3 + PyObject* key_weakref1 + PyObject* key_weakref2 + PyObject* key_weakref3 + PyObject* value + +cdef object extract_triple_cell(triple_cell *cell): + #see extract_mono_cell for the rationale + #behind this routine. + if cell.key_id1 != NULL and cell.key_id1 != dummy : + L=PyList_New(4) + PyList_SetItem(L,0,cell.key_weakref1) + PyList_SetItem(L,1,cell.key_weakref2) + PyList_SetItem(L,2,cell.key_weakref3) + PyList_SetItem(L,3,cell.value) + cell.key_id1=dummy + cell.key_id2=NULL + cell.key_id3=NULL + cell.key_weakref1=NULL + cell.key_weakref2=NULL + cell.key_weakref3=NULL + cell.value=NULL + return L + else: + raise RuntimeError("unused triple_cell") cdef class MonoDictEraser: """ @@ -109,7 +172,7 @@ cdef class MonoDictEraser: sage: from sage.structure.coerce_dict import MonoDict sage: class A: pass sage: a = A() - sage: M = MonoDict(11) + sage: M = MonoDict() sage: M[a] = 1 sage: len(M) 1 @@ -119,34 +182,12 @@ cdef class MonoDictEraser: sage: len(M) # indirect doctest 0 - TESTS: - - The following shows that :trac:`15069` is fixed. Background: If a - :class:`MonoDictEraser` is called when a weakly referenced key is deleted, - then it deletes the corresponding key-value pair. But if the value is also - used as a key, then a recursion of calls to the :class:`MonoDictEraser` - may occur. Therefore, we must keep the value-to-be-deleted alive until the - callback is completed. This is achieved by letting the callback actually - *return* the value. :: - - sage: M = MonoDict(11) - sage: class A: pass - sage: a = A() - sage: prev = a - sage: for i in range(1000): - ....: newA = A() - ....: M[prev] = newA - ....: prev = newA - sage: len(M) - 1000 - sage: del a - sage: len(M) - 0 - AUTHOR: - Simon King (2012-01) + - Nils Bruin (2013-11) """ + cdef D def __init__(self, D): """ INPUT: @@ -155,134 +196,15 @@ cdef class MonoDictEraser: EXAMPLES:: - sage: from sage.structure.coerce_dict import MonoDict, MonoDictEraser - sage: D = MonoDict(11) - sage: MonoDictEraser(D) - - """ - self.D = ref(D) - - def __call__(self, r): - """ - INPUT: - - A weak reference with key. - - When this is called with a weak reference ``r``, then each item - containing ``r`` is removed from the associated :class:`MonoDict`. - Normally, this only happens when a weak reference becomes invalid. - - EXAMPLES:: - - sage: from sage.structure.coerce_dict import MonoDict - sage: class A: pass - sage: a = A() - sage: M = MonoDict(11) - sage: M[a] = 1 - sage: len(M) + sage: k = set([1]) + sage: D = sage.structure.coerce_dict.MonoDict([(k,1)]) + sage: len(D) 1 - sage: del a - sage: import gc - sage: n = gc.collect() - sage: len(M) # indirect doctest + sage: del k + sage: len(D) # indirect doctest 0 """ - # r is a (weak) reference (typically to a parent), - # and it knows the stored key of the unique singleton r() had been part - # of. - # We remove that unique tuple from self.D -- if self.D is still there! - - #WARNING! These callbacks can happen during the addition of items to the same - #dictionary. The addition code may then be in the process of adding a new entry - #(one PyList_Append call at a time) to the relevant bucket. - #The "PyList_GET_SIZE(bucket) by 3" call should mean that we round down and hence not look - #at incomplete entries. Furthermore, deleting a slice out of a buck should still be OK. - #this callback code should absolutely not resize the dictionary, because that would wreak - #complete havoc. - - cdef MonoDict D = PyWeakref_GetObject(self.D) - if D is None: - return - cdef list buckets = D.buckets - if buckets is None: - return - cdef Py_ssize_t h - cdef int offset - h,offset = r.key - cdef list bucket = PyList_GET_ITEM(buckets, (h) % PyList_GET_SIZE(buckets)) - cdef Py_ssize_t i - cdef object val - for i from 0 <= i < PyList_GET_SIZE(bucket) by 3: - if PyInt_AsSsize_t(PyList_GET_ITEM(bucket,i))==h: - if PyList_GET_ITEM(bucket,i+offset)==r: - val = PyList_GET_ITEM(bucket,i+2) - del bucket[i:i+3] - D._size -= 1 - return val - else: - return - -cdef class TripleDictEraser: - """ - Erases items from a :class:`TripleDict` when a weak reference becomes - invalid. - - This is of internal use only. Instances of this class will be passed as a - callback function when creating a weak reference. - - EXAMPLES:: - - sage: from sage.structure.coerce_dict import TripleDict - sage: class A: pass - sage: a = A() - sage: T = TripleDict(11) - sage: T[a,ZZ,None] = 1 - sage: T[ZZ,a,1] = 2 - sage: T[a,a,ZZ] = 3 - sage: len(T) - 3 - sage: del a - sage: import gc - sage: n = gc.collect() - sage: len(T) - 0 - - TESTS: - - The following tests against a bugfix from :trac:`15069`:: - - sage: from sage.structure.coerce_dict import TripleDict - sage: a,b,c = L = [A(), A(), A()] - sage: for x in range(1000): - ....: newA = A() - ....: T[L[0],L[1],L[2]] = newA - ....: tmp = L.pop(0) - ....: L.append(newA) - sage: len(T) - 1000 - sage: del a,b,c, L - sage: len(T) - 0 - - AUTHOR: - - - Simon King (2012-01) - """ - def __init__(self, D): - """ - INPUT: - - A :class:`TripleDict`. - - EXAMPLES:: - - sage: from sage.structure.coerce_dict import TripleDict, TripleDictEraser - sage: D = TripleDict(11) - sage: TripleDictEraser(D) - - - """ - self.D = ref(D) + self.D = fixed_ref(D) def __call__(self, r): """ @@ -290,63 +212,31 @@ cdef class TripleDictEraser: A weak reference with key. - When this is called with a weak reference ``r``, then each item - containing ``r`` is removed from the associated :class:`TripleDict`. - Normally, this only happens when a weak reference becomes invalid. + For internal use only. EXAMPLES:: - sage: from sage.structure.coerce_dict import TripleDict - sage: class A: pass - sage: a = A() - sage: T = TripleDict(11) - sage: T[a,ZZ,None] = 1 - sage: T[ZZ,a,1] = 2 - sage: T[a,a,ZZ] = 3 - sage: len(T) - 3 - sage: del a - sage: import gc - sage: n = gc.collect() - sage: len(T) # indirect doctest + sage: k = set([1]) + sage: D = sage.structure.coerce_dict.MonoDict([(k,1)]) + sage: len(D) + 1 + sage: del k + sage: len(D) # indirect doctest 0 """ - - #WARNING! These callbacks can happen during the addition of items to the same - #dictionary. The addition code may then be in the process of adding a new entry - #(one PyList_Append call at a time) to the relevant bucket. - #The "PyList_GET_SIZE(bucket) by 3" call should mean that we round down and hence not look - #at incomplete entries. Furthermore, deleting a slice out of a buck should still be OK. - #this callback code should absolutely not resize the dictionary, because that would wreak - #complete havoc. - - cdef TripleDict D = PyWeakref_GetObject(self.D) - if D is None: + cdef MonoDict md = PyWeakref_GetObject(self.D) + if md is None: return - cdef list buckets = D.buckets - if buckets is None: + if md.table == NULL: return - # r is a (weak) reference (typically to a parent), and it knows the - # stored key of the unique triple r() had been part of. - # We remove that unique triple from self.D - cdef Py_ssize_t k1,k2,k3 - cdef int offset - k1,k2,k3,offset = r.key - cdef Py_ssize_t h = (k1 + 13*k2 ^ 503*k3) - cdef list bucket = PyList_GET_ITEM(buckets, (h) % PyList_GET_SIZE(buckets)) - cdef Py_ssize_t i - cdef object val - for i from 0 <= i < PyList_GET_SIZE(bucket) by 7: - if PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i))==k1 and \ - PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i+1))==k2 and \ - PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i+2))==k3: - if PyList_GET_ITEM(bucket, i+offset)==r: - val = PyList_GET_ITEM(bucket,i+6) - del bucket[i:i+7] - D._size -= 1 - return val - else: - return + cdef mono_cell* cursor = md.lookup(PyCapsule_GetPointer(r.key,NULL)) + if (cursor.key_id != NULL and cursor.key_id != dummy): + if (cursor.key_weakref == r or cursor.value == r): + L=extract_mono_cell(cursor) + md.used -= 1 + else: + #this should probably never happen + raise RuntimeError("eraser: key match but no weakref match") cdef class MonoDict: """ @@ -367,10 +257,8 @@ cdef class MonoDict: IMPLEMENTATION: - It is implemented as a list of lists (hereafter called buckets). The bucket - is chosen according to a very simple hash based on the object pointer, - and each bucket is of the form [id(k1), r1, value1, id(k2), r2, value2, ...], - on which a linear search is performed. + It is implemented as a hash table with open addressing, similar to python's + dict. If ki supports weak references then ri is a weak reference to ki with a callback to remove the entry from the dictionary if ki gets garbage @@ -380,19 +268,16 @@ cdef class MonoDict: INPUT: - - ``size`` -- an integer, the initial number of buckets. To spread objects - evenly, the size should ideally be a prime, and certainly not divisible - by 2. + - ``size`` -- unused parameter, present for backward compatibility. - ``data`` -- optional iterable defining initial data. - - ``threshold`` -- optional number, default `0.7`. It determines how frequently - the dictionary will be resized (large threshold implies rare resizing). + - ``threshold`` -- unused parameter, present for backward compatibility. - ``weak_values`` -- optional bool (default False). If it is true, weak references to the values in this dictionary will be used, when possible. EXAMPLES:: sage: from sage.structure.coerce_dict import MonoDict - sage: L = MonoDict(31) + sage: L = MonoDict() sage: a = 'a'; b = 'ab'; c = -15 sage: L[a] = 1 sage: L[b] = 2 @@ -427,24 +312,10 @@ cdef class MonoDict: [('a', 1), ('ab', 2)] sage: len(L) 2 - sage: L.stats() # random - (0, 0.06451..., 1) - sage: L.bucket_lens() # random layout - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0] sage: for i in range(1000): ... L[i] = i sage: len(L) 1002 - sage: L.stats() # random - (26, 32.32258064516129, 37) - sage: L.bucket_lens() # random layout - [32, 34, 31, 32, 33, 32, 32, 33, 34, 31, 32, 32, 31, 31, 32, 34, 31, 33, 34, 32, 32, 33, 33, 31, 33, 35, 32, 32, 32, 32, 31] - sage: L = MonoDict(101, L) - sage: L.stats() # random - (8, 9.92079207920792, 12) - sage: L = MonoDict(3, L) - sage: L.stats() # random - (0, 334.0, 985) sage: L['a'] 1 sage: L['c'] @@ -452,14 +323,6 @@ cdef class MonoDict: ... KeyError: 'c' - The following illustrates why even sizes are bad:: - - sage: L = MonoDict(4, L, threshold=0) - sage: L.stats() - (0, 250.5, 1002) - sage: L.bucket_lens() - [1002, 0, 0, 0] - Note that this kind of dictionary is also used for caching actions and coerce maps. In previous versions of Sage, the cache was by strong references and resulted in a memory leak in the following @@ -523,11 +386,9 @@ cdef class MonoDict: sage: MW[k] 5 - TESTS: - The following demonstrates that :class:`MonoDict` is safer than :class:`~weakref.WeakKeyDictionary` against recursions created by nested - callbacks; compare :trac:`15069`:: + callbacks; compare :trac:`15069` (the mechanism used now is different, though):: sage: M = MonoDict(11) sage: class A: pass @@ -562,13 +423,110 @@ cdef class MonoDict: sage: len(M)>0 True + Check that also in the presence of circular references, :class:`MonoDict` + gets properly collected:: + + sage: import gc + sage: def count_type(T): + ....: return len([c for c in gc.get_objects() if isinstance(c,T)]) + sage: _=gc.collect() + sage: N=count_type(MonoDict) + sage: for i in range(100): + ....: V = [ MonoDict(11,{"id":j+100*i}) for j in range(100)] + ....: n= len(V) + ....: for i in range(n): V[i][V[(i+1)%n]]=(i+1)%n + ....: del V + ....: _=gc.collect() + ....: assert count_type(MonoDict) == N + sage: count_type(MonoDict) == N + True + AUTHORS: - Simon King (2012-01) - Nils Bruin (2012-08) - Simon King (2013-02) + - Nils Bruin (2013-11) """ - def __init__(self, size, data=None, threshold=0.7, weak_values=False): + cdef mono_cell* lookup(self, PyObject* key): + """ + This routine is used for all cases where a (potential) spot for + a key is looked up. The returned value is a pointer into the dictionary + store that either contains an entry with the requested key or a free spot + where an entry for that key should go. + """ + cdef size_t perturb + cdef size_t mask = self.mask + cdef mono_cell* table = self.table + cdef mono_cell* cursor + cdef mono_cell* first_freed = NULL + + #We seed our starting probe using the higher bits of the key as well. + #Our hash is a memory location, so the bottom bits are likely 0. + + cdef size_t i = ((key)>>8+(key)) + if key == NULL or key == dummy: + print("Request to look up invalid key") + cursor = &(table[i & mask]) + # if the cell was never used, the entry wasn't there + perturb = (key)>>3 + + #the probing algorithm is heavily inspired by python's own dict. + #there is always at least one NULL entry in the store, and the probe + #sequence eventually covers the entire store (see python's dictobject.c), + #so the loop below does terminate. Since this loop executes only + #straightforward C, we know the table will not change. + + while (cursor.key_id != key): + if cursor.key_id == NULL: + return first_freed if (first_freed != NULL) else cursor + if first_freed == NULL and cursor.key_id == dummy: + first_freed = cursor + i = 5*i + perturb +1 + cursor = &(table[i & mask]) + perturb = perturb >> 5 + return cursor + + cdef int resize(self) except -1: + """ + Resize dictionary. That can also mean shrink! Size is always a power of 2. + """ + cdef mono_cell* old_table=self.table + cdef size_t old_mask = self.mask + cdef size_t newsize = 8 + cdef size_t minsize = 2*self.used + cdef size_t i + cdef mono_cell* cursor + cdef mono_cell* entry + while newsize < minsize: + newsize = newsize<<1 + cdef mono_cell* table = PyMem_Malloc((newsize)*sizeof(mono_cell)) + if table == NULL: + raise MemoryError() + memset(table,0,(newsize)*sizeof(mono_cell)) + + #we're done with memory activity. We can move the new (empty) table into place: + + self.table = table + self.mask = newsize-1 + self.used = 0 + self.fill = 0 + + #and move all entries over. We're not changing any refcounts here, so this is a very + #tight loop that doesn't need to worry about tables changing. + + for i in range(old_mask+1): + entry = &(old_table[i]) + if entry.key_id != NULL and entry.key_id != dummy: + cursor=self.lookup((entry.key_id)) + assert cursor.key_id == NULL + cursor[0]=entry[0] + self.used +=1 + self.fill +=1 + PyMem_Free(old_table) + return 0 + + def __init__(self, size=11, data=None, threshold=0.7, weak_values=False): """ Create a special dict using singletons for keys. @@ -580,17 +538,54 @@ cdef class MonoDict: sage: L[a] = 1 sage: L[a] 1 + sage: L = MonoDict({a: 1}) + sage: L[a] + 1 """ - cdef Py_ssize_t i - self.threshold = threshold - self.buckets = [[] for i from 0 <= i < size] - self._size = 0 - self.weak_values = weak_values + #if only one argument is supplied and it's iterable, use it for data rather than + #for size. This way we're compatible with the old calling sequence (an integer is + #just ignored) and we can also use the more usual construction. + if data is None: + try: + data=size.iteritems() + except AttributeError: + try: + data=iter(size) + except TypeError: + pass + else: + try: + data=data.iteritems() + except AttributeError: + pass + if self.table != NULL: + raise RuntimeError("table already initialized. Called __init__ second time?") + cdef minsize = 8 + cdef size_t newsize = 1<<3 + while newsize < minsize: + newsize = newsize <<1 + self.mask = newsize - 1 + cdef mono_cell* table = PyMem_Malloc(newsize*sizeof(mono_cell)) + if table == NULL: + raise MemoryError() + memset(table,0,newsize*sizeof(mono_cell)) + self.table = table + self.used = 0 + self.fill = 0 self.eraser = MonoDictEraser(self) - if data is not None: - for k, v in data.iteritems(): + self.weak_values = weak_values + if data: + for k,v in data: self.set(k,v) + def __dealloc__(self): + """ + Ensure that the memory allocated by a MonoDict is properly freed. + """ + #is this required or does this get called anyway if we set tp_clear properly? + #at least it's safe, since MonoDict_clear checks if it has already run. + MonoDict_clear(self) + def __len__(self): """ The number of items in self. @@ -606,83 +601,7 @@ cdef class MonoDict: sage: len(L) 3 """ - return self._size - - def stats(self): - """ - The distribution of items in buckets. - - OUTPUT: - - - (min, avg, max) - - EXAMPLES:: - - sage: from sage.structure.coerce_dict import MonoDict - sage: L = MonoDict(37) - sage: for i in range(100): L[i] = None - sage: L.stats() # random - (2, 2.7027027027027026, 4) - - sage: L = MonoDict(3007) - sage: for i in range(100): L[i] = None - sage: L.stats() # random - (0, 0.03325573661456601, 1) - - sage: L = MonoDict(1,threshold=0) - sage: for i in range(100): L[i] = None - sage: L.stats() - (100, 100.0, 100) - """ - cdef Py_ssize_t size = self._size - cdef Py_ssize_t cur, min = size, max = 0 - for bucket in self.buckets: - if bucket: - cur = len(bucket)/3 - if cur < min: min = cur - if cur > max: max = cur - else: - min = 0 - return min, 1.0*size/len(self.buckets), max - - def bucket_lens(self): - """ - The distribution of items in buckets. - - OUTPUT: - - A list of how many items are in each bucket. - - EXAMPLES:: - - sage: from sage.structure.coerce_dict import MonoDict - sage: L = MonoDict(37) - sage: for i in range(100): L[i] = None - sage: L.bucket_lens() # random - [3, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 3, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4] - sage: sum(L.bucket_lens()) - 100 - - sage: L = MonoDict(1,threshold=0) - sage: for i in range(100): L[i] = None - sage: L.bucket_lens() - [100] - """ - return [len(self.buckets[i])/3 for i from 0 <= i < len(self.buckets)] - - def _get_buckets(self): - """ - The actual buckets of self, for debugging. - - EXAMPLE:: - - sage: from sage.structure.coerce_dict import MonoDict - sage: L = MonoDict(3) - sage: L[0] = None - sage: L._get_buckets() # random - [[0, None], [], []] - """ - return self.buckets + return self.used def __contains__(self, k): """ @@ -706,19 +625,17 @@ cdef class MonoDict: sage: 15 in L False """ - cdef Py_ssize_t h = signed_id(k) - cdef Py_ssize_t i - cdef list all_buckets = self.buckets - cdef list bucket = PyList_GET_ITEM(all_buckets, (h)% PyList_GET_SIZE(all_buckets)) - cdef object r - for i from 0 <= i < PyList_GET_SIZE(bucket) by 3: - if PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i)) == h: - r = PyList_GET_ITEM(bucket, i+1) - if isinstance(r, KeyedRef) and PyWeakref_GetObject(r) == Py_None: - return False - else: - return (not self.weak_values) or PyWeakref_GetObject(PyList_GET_ITEM(bucket, i+2)) != Py_None - return False + cdef mono_cell* cursor = self.lookup(k) + if cursor.key_id == NULL or cursor.key_id == dummy: + return False + r = cursor.key_weakref + if isinstance(r, fixed_KeyedRef) and PyWeakref_GetObject(r) == Py_None: + return False + elif not(self.weak_values): + return True + else: + value = cursor.value + return (not isinstance(value,fixed_KeyedRef)) or PyWeakref_GetObject(value) != Py_None def __getitem__(self, k): """ @@ -749,29 +666,18 @@ cdef class MonoDict: return self.get(k) cdef get(self, object k): - cdef Py_ssize_t h = signed_id(k) - cdef Py_ssize_t i - cdef list all_buckets = self.buckets - cdef list bucket = PyList_GET_ITEM(all_buckets, (h) % PyList_GET_SIZE(all_buckets)) - cdef object r, val - cdef PyObject * out - for i from 0 <= i < PyList_GET_SIZE(bucket) by 3: - if PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i)) == h: - r = PyList_GET_ITEM(bucket, i+1) - if isinstance(r, KeyedRef) and PyWeakref_GetObject(r) == Py_None: - raise KeyError, k - else: - val = PyList_GET_ITEM(bucket, i+2) - if self.weak_values: - if not isinstance(val, KeyedRef): - return val - out = PyWeakref_GetObject(val) - if out == Py_None: - raise KeyError, k - return out - else: - return val - raise KeyError, k + cdef mono_cell* cursor = self.lookup(k) + if cursor.key_id == NULL or cursor.key_id == dummy: + raise KeyError, k + r = cursor.key_weakref + if isinstance(r, fixed_KeyedRef) and PyWeakref_GetObject(r) == Py_None: + raise KeyError, k + value = cursor.value + if self.weak_values and isinstance(value,fixed_KeyedRef): + value = PyWeakref_GetObject(value) + if value is None: + raise KeyError, k + return value def __setitem__(self, k, value): """ @@ -793,56 +699,53 @@ cdef class MonoDict: """ self.set(k,value) - cdef set(self,object k, value): - if self.threshold and self._size > len(self.buckets) * self.threshold: - self.resize() - cdef Py_ssize_t h = signed_id(k) - cdef Py_ssize_t i + cdef set(self,object k, object value): + cdef mono_cell entry + cdef PyObject* old_value = NULL + cdef bint maybe_resize = False + entry.key_id = k if self.weak_values: + cap_k=PyCapsule_New((k),NULL,NULL) try: - value = KeyedRef(value,self.eraser,(h,2)) + value_store = fixed_KeyedRef(value,self.eraser,cap_k) + entry.value = value_store except TypeError: - pass - cdef list bucket = PyList_GET_ITEM(self.buckets,( h) % PyList_GET_SIZE(self.buckets)) - cdef object r - for i from 0 <= i < PyList_GET_SIZE(bucket) by 3: - if PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i)) == h: - r = PyList_GET_ITEM(bucket, i+1) - if isinstance(r, KeyedRef) and PyWeakref_GetObject(r) == Py_None: - #uh oh, an entry has died and has not received a callback yet. - #that callback might still be out there! safest thing is to simply remove the entry and - #append a new one below. - - #We checked that slice deletion is safe: Python will save references the the removed items, - #rearrange the list (so no references exist there anymore!) and only THEN delete the - #items. Therefore, any code that executes upon deallocation will see the bucket in its - #new, consistent form already. - del bucket[i:i+3] - self._size -=1 - #by now we believe dangling weakref is well and truly gone. If python still has its callback - #scheduled somewhere, we think it's in breach of contract. - break - else: - #key is present and still alive. We can just store the value. - bucket[i+2] = value - return - #key id was not present, or was storing a dead reference, which has now been removed. - - #the following code fragment may allocate new memory and hence may trigger garbage collections. - #that means it can also trigger callbacks that removes entries from the bucket - #we are adding to. However, as long as such callbacks never ADD anything to buckets, - #we're still OK building up our entry by adding entries at the end of it. - #Note that the bucket list will only have increased by a multiple of 3 in length - #after `value` has successfully been added, i.e, once the entry is complete. That means any - #search in the bucket list by a callback will round len(bucket)/3 DOWN and hence not - #investigate our partial entry. - PyList_Append(bucket, h) - try: - PyList_Append(bucket, KeyedRef(k,self.eraser,(h,1))) - except TypeError: - PyList_Append(bucket, k) - PyList_Append(bucket, value) - self._size += 1 + entry.value = value + else: + entry.value = value + Py_XINCREF(entry.value) + cursor = self.lookup(k) + if cursor.key_id == NULL or cursor.key_id == dummy: + self.used += 1 + if cursor.key_id == NULL: + self.fill += 1 + maybe_resize = True + if not(self.weak_values): + cap_k=PyCapsule_New((k),NULL,NULL) + try: + key_store=fixed_KeyedRef(k,self.eraser,cap_k) + entry.key_weakref = key_store + except TypeError: + entry.key_weakref = k + Py_XINCREF(entry.key_weakref) + + #we're taking a bit of a gamble here: we're assuming the dictionary has + #not been resized (otherwise cursor might not be a valid location + #anymore). The only way in which that could happen is if the allocation + #activity above forced a GC that triggered code that ADDS entries to this + #dictionary: the dictionary can only get reshaped if self.fill increases. + #(as happens below). Note that we're holding a strong ref to the dict + #itself, so that's not liable to disappear. + #for the truly paranoid: we could detect a change by checking if self.table + #has changed value + cursor[0] = entry + + #this is the one place where resize gets called: + if maybe_resize and 3*self.fill > 2*self.mask: self.resize() + else: + old_value = cursor.value + cursor.value = entry.value + Py_XDECREF(old_value) def __delitem__(self, k): """ @@ -872,73 +775,11 @@ cdef class MonoDict: sage: a in L False """ - cdef Py_ssize_t h = signed_id(k) - cdef object r - cdef Py_ssize_t i - cdef object tmp - cdef list all_buckets = self.buckets - cdef list bucket = PyList_GET_ITEM(all_buckets, (h) % PyList_GET_SIZE(all_buckets)) - for i from 0 <= i < PyList_GET_SIZE(bucket) by 3: - if PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i)) == h: - r = PyList_GET_ITEM(bucket, i+1) - del bucket[i:i+3] - self._size -= 1 - if isinstance(r, KeyedRef) and PyWeakref_GetObject(r) == Py_None: - break - else: - return - raise KeyError, k - - def resize(self, int buckets=0): - """ - Change the number of buckets of self, while preserving the contents. - - If the number of buckets is 0 or not given, it resizes self to the - smallest prime that is at least twice as large as self. - - EXAMPLES:: - - sage: from sage.structure.coerce_dict import MonoDict - sage: L = MonoDict(8) - sage: for i in range(100): L[i] = None - sage: L.bucket_lens() # random - [50, 0, 0, 0, 50, 0, 0, 0] - sage: L.resize(7) - sage: L.bucket_lens() # random - [15, 14, 14, 14, 14, 15, 14] - sage: L.resize() - sage: len(L.bucket_lens()) - 17 - """ - if buckets == 0: - buckets = next_odd_prime(2*len(self.buckets)) - cdef list old_buckets = self.buckets - cdef list bucket - cdef Py_ssize_t i - cdef Py_ssize_t h - cdef list new_buckets = [[] for i from 0 <= i < buckets] - cdef object r - cdef object v - - #this would be a very bad place for a garbage collection to happen - #so we disable them. - cdef bint gc_originally_enabled = gc.isenabled() - if gc_originally_enabled: - gc.disable() - - #BEGIN of critical block. NO GC HERE! - for bucket in old_buckets: - for i from 0 <= i < PyList_GET_SIZE(bucket) by 3: - h = PyInt_AsSsize_t(PyList_GET_ITEM(bucket,i)) - r = PyList_GET_ITEM(bucket,i+1) - v = PyList_GET_ITEM(bucket,i+2) - #this line can trigger allocation, so GC must be turned off! - new_buckets[(h) % buckets] += [h,r,v] - self.buckets = new_buckets - #END of critical block. The dict is consistent again. - - if gc_originally_enabled: - gc.enable() + cdef mono_cell* cursor = self.lookup(k) + if cursor.key_id == NULL or cursor.key_id==dummy: + raise KeyError, k + L=extract_mono_cell(cursor) + self.used -= 1 def iteritems(self): """ @@ -951,20 +792,30 @@ cdef class MonoDict: sage: list(sorted(L.iteritems())) [(1, None), (2, True)] """ - cdef list bucket - cdef Py_ssize_t i - # We test whether the references are still valid. - # However, we must not delete them, since we are - # iterating. - for bucket in self.buckets: - for i from 0<=iPyList_GET_ITEM(bucket,i+1) - if isinstance(r, KeyedRef): - r = PyWeakref_GetObject(r) - if r is None: + #iteration is tricky because the table could change from under us. + #the following iterates properly if the dictionary does not + #get "resize"d, which is guaranteed if no NEW entries in the + #dictionary are introduced. At least we make sure to get our data fresh + #from "self" every iteration, so that at least we're not reading random memory + #(if the dictionary changes, it's not guaranteed you get to see any particular entry) + cdef size_t i = 0 + while i <= self.mask: + cursor = &(self.table[i]) + i += 1 + if cursor.key_id != NULL and cursor.key_id != dummy: + key = (cursor.key_weakref) + value = (cursor.value) + if isinstance(key,fixed_KeyedRef): + key = PyWeakref_GetObject(key) + if key is None: + print "found defunct key" continue - yield r, PyList_GET_ITEM(bucket,i+2) - + if self.weak_values and isinstance(value,fixed_KeyedRef): + value = PyWeakref_GetObject(value) + if value is None: + print "found defunct value" + continue + yield (key, value) def __reduce__(self): """ @@ -981,7 +832,165 @@ cdef class MonoDict: sage: list(loads(dumps(L)).iteritems()) [(1, True)] """ - return MonoDict, (len(self.buckets), dict(self.iteritems()), self.threshold) + return MonoDict, (11, dict(self.iteritems()), 0.7) + +#the cython supplied tp_traverse and tp_clear do not take the dynamically +#allocated table into account, so we have to supply our own. +#the only additional link to follow (that cython does pick up and we have +#to replicate here) is the "eraser" which in its closure stores a reference +#back to the dictionary itself (meaning that MonoDicts only disappear +#on cyclic GC) + +cdef int MonoDict_traverse(MonoDict op, visitproc visit, void *arg): + cdef int r + if op.table == NULL: + return 0 + table=op.table + cdef size_t i = 0 + if ((op.eraser)): + r=visit((op.eraser),arg) + if r: return r + for i in range(op.mask+1): + cursor = &table[i] + if cursor.key_id != NULL and cursor.key_id != dummy: + r=visit(cursor.key_weakref,arg) + if r: return r + r=visit(cursor.value,arg) + if r: return r + return 0 + + +#we clear a monodict by taking first taking away the table before dereffing +#its contents. That shortcuts callbacks, so we deref the entries straight here. +#that means this code does not participate in Python's trashcan the way that +#deletion code based on extract_mono_cell does, so there is probably a way +#this code can be used to overflow the C stack. It would have to be a pretty +#devious example, though. +cdef int MonoDict_clear(MonoDict op): + if op.table == NULL: + return 0 + + tmp=op.eraser + cdef mono_cell* table=op.table + cdef size_t mask=op.mask + op.table=NULL + + op.eraser=None + op.mask=0 + op.used=0 + op.fill=0 + + #this deletion method incurs an extra refcount +-, but it seems very difficult in cython to do it + #any other way and still be sure that the reference op.eraser is gone by the time the object gets + #deallocated. + del tmp + for i in range(mask+1): + cursor = &(table[i]) + if cursor.key_id != NULL and cursor.key_id != dummy: + cursor.key_id = dummy + Py_XDECREF(cursor.key_weakref) + Py_XDECREF(cursor.value) + PyMem_Free(table) + return 0 + +(MonoDict).tp_traverse = &MonoDict_traverse +(MonoDict).tp_clear = &MonoDict_clear + +cdef class TripleDictEraser: + """ + Erases items from a :class:`TripleDict` when a weak reference becomes + invalid. + + This is of internal use only. Instances of this class will be passed as a + callback function when creating a weak reference. + + EXAMPLES:: + + sage: from sage.structure.coerce_dict import TripleDict + sage: class A: pass + sage: a = A() + sage: T = TripleDict() + sage: T[a,ZZ,None] = 1 + sage: T[ZZ,a,1] = 2 + sage: T[a,a,ZZ] = 3 + sage: len(T) + 3 + sage: del a + sage: import gc + sage: n = gc.collect() + sage: len(T) # indirect doctest + 0 + + AUTHOR: + + - Simon King (2012-01) + - Nils Bruin (2013-11) + """ + cdef D + def __init__(self, D): + """ + INPUT: + + A :class:`TripleDict`. For internal use only. + + EXAMPLES:: + + sage: D = sage.structure.coerce_dict.TripleDict() + sage: k = set([1]) + sage: D[k,1,1] = 1 + sage: len(D) + 1 + sage: del k + sage: len(D) # indirect doctest + 0 + + """ + self.D = fixed_ref(D) + + def __call__(self, r): + """ + INPUT: + + A weak reference with key. + + For internal use only. + + EXAMPLES:: + + sage: from sage.structure.coerce_dict import TripleDict + sage: class A: pass + sage: a = A() + sage: T = TripleDict() + sage: T[a,ZZ,None] = 1 + sage: T[ZZ,a,1] = 2 + sage: T[a,a,ZZ] = 3 + sage: len(T) + 3 + sage: del a + sage: import gc + sage: n = gc.collect() + sage: len(T) # indirect doctest + 0 + """ + cdef TripleDict td = PyWeakref_GetObject(self.D) + if td is None: + return + if td.table == NULL: + return + + k1,k2,k3 = r.key + cdef triple_cell* cursor = td.lookup(PyCapsule_GetPointer(k1,NULL), + PyCapsule_GetPointer(k2,NULL), + PyCapsule_GetPointer(k3,NULL)) + if (cursor.key_id1 != NULL and cursor.key_id1 != dummy): + if (cursor.key_weakref1 == r or + cursor.key_weakref2 == r or + cursor.key_weakref3 == r or + cursor.value == r): + L=extract_triple_cell(cursor) + td.used -= 1 + else: + raise RuntimeError("eraser: key match but no weakref match") cdef class TripleDict: """ @@ -1024,7 +1033,7 @@ cdef class TripleDict: EXAMPLES:: sage: from sage.structure.coerce_dict import TripleDict - sage: L = TripleDict(31) + sage: L = TripleDict() sage: a = 'a'; b = 'b'; c = 'c' sage: L[a,b,c] = 1 sage: L[a,b,c] @@ -1037,24 +1046,11 @@ cdef class TripleDict: [(('c', 'b', 'a'), -1)] sage: len(L) 1 - sage: L.stats() # min, avg, max (bucket length) - (0, 0.03225806451612903, 1) - sage: L.bucket_lens() # random layout - [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, 0, 0, 0, 0, 0, 0] sage: for i in range(1000): ... L[i,i,i] = i sage: len(L) 1001 - sage: L.stats() # random - (31, 32.29032258064516, 35) - sage: L.bucket_lens() # random layout - [33, 34, 32, 32, 35, 32, 31, 33, 34, 31, 32, 34, 32, 31, 31, 32, 32, 31, 31, 33, 32, 32, 32, 33, 33, 33, 31, 33, 33, 32, 31] - sage: L = TripleDict(101, L) - sage: L.stats() # random - (8, 9.9108910891089117, 11) - sage: L = TripleDict(3, L) - sage: L.stats() # random - (291, 333.66666666666669, 410) + sage: L = TripleDict(L) sage: L[c,b,a] -1 sage: L[a,b,c] @@ -1070,15 +1066,6 @@ cdef class TripleDict: ... KeyError: 'a' - The following illustrates why even sizes are bad (setting the threshold - zero, so that no beneficial resizing happens):: - - sage: L = TripleDict(4, L, threshold=0) - sage: L.stats() - (0, 250.25, 1001) - sage: L.bucket_lens() - [1001, 0, 0, 0] - Note that this kind of dictionary is also used for caching actions and coerce maps. In previous versions of Sage, the cache was by strong references and resulted in a memory leak in the following @@ -1167,14 +1154,11 @@ cdef class TripleDict: quantity with the same length as size_t. Storing it in a signed way gives the most efficient storage into PyInt, while preserving sign information. - As usual for a hashtable, we take h modulo some integer to obtain the bucket - number into which to store the key/value pair. A problem here is that C mandates - sign-preservation for the modulo operator "%". We cast to an unsigned type, i.e., + In previous situations there were some problems with ending up with negative + indices, which required casting to an unsigned type, i.e., ( h)% N - If we don't do this we may end up indexing lists with negative indices, which may lead to - segfaults if using the non-error-checking python macros, as happens here. - - This has been observed on 32 bits systems, see :trac:`715` for details. + since C has a sign-preserving % operation This caused problems on 32 bits systems, + see :trac:`715` for details. This is irrelevant for the current implementation. AUTHORS: @@ -1185,9 +1169,66 @@ cdef class TripleDict: - Nils Bruin, 2012-08 - Simon King, 2013-02 - """ - def __init__(self, size, data=None, threshold=0.7, weak_values=False): + - Nils Bruin, 2013-11 + """ + cdef triple_cell* lookup(self, PyObject* key1, PyObject* key2, PyObject* key3): + #returns a pointer to where key should be stored in this dictionary. + cdef size_t perturb + cdef size_t mask = self.mask + cdef triple_cell* table = self.table + cdef triple_cell* cursor + cdef triple_cell* first_freed = NULL + cdef int j + global summed_expected, samples + #we device some hash that reasonably depends on bits of all keys + #making sure it's not commutative and also involves some of the higher order + #bits at an early stage. + cdef size_t key = key1 + 13*(key2) ^ 503*(key3) + cdef size_t i = key>>8 + key + cursor = &(table[i & mask]) + perturb = (key)>>3 + j=1 + while (cursor.key_id1 != key1 or cursor.key_id2 != key2 or cursor.key_id3 != key3): + if cursor.key_id1 == NULL: + return first_freed if (first_freed != NULL) else cursor + if first_freed == NULL and cursor.key_id1 == dummy: + first_freed = cursor + i = 5*i + perturb +1 + cursor = &(table[i & mask]) + perturb = perturb >> 5 + return cursor + + cdef int resize(self) except -1: + cdef triple_cell* old_table=self.table + cdef size_t old_mask = self.mask + cdef size_t newsize = 8 + cdef size_t minsize = 2*self.used + cdef size_t i + cdef triple_cell* cursor + cdef triple_cell* entry + while newsize < minsize: + newsize = newsize<<1 + cdef triple_cell* table = PyMem_Malloc((newsize)*sizeof(triple_cell)) + if table == NULL: + raise MemoryError() + memset(table,0,(newsize)*sizeof(triple_cell)) + self.table = table + self.mask = newsize-1 + self.used = 0 + self.fill = 0 + for i in range(old_mask+1): + entry = &(old_table[i]) + if entry.key_id1 != NULL and entry.key_id1 != dummy: + cursor=self.lookup((entry.key_id1),(entry.key_id2),(entry.key_id3)) + assert cursor.key_id1 == NULL + cursor[0]=entry[0] + self.used +=1 + self.fill +=1 + PyMem_Free(old_table) + return 0 + + def __init__(self, size=11, data=None, threshold=0.7, weak_values=False): """ Create a special dict using triples for keys. @@ -1200,15 +1241,44 @@ cdef class TripleDict: sage: L[a,b,c] 1 """ - cdef int i - self.threshold = threshold - self.buckets = [[] for i from 0 <= i < size] - self._size = 0 - self.weak_values = weak_values + #if only one argument is supplied and it's iterable, use it for data rather than + #for size + if data is None: + try: + data=size.iteritems() + except AttributeError: + try: + data=iter(size) + except TypeError: + pass + else: + try: + data=data.iteritems() + except AttributeError: + pass + if self.table != NULL: + raise RuntimeError("table already initialized. Called __init__ second time?") + cdef minsize = 8 + cdef size_t newsize = 1<<3 + while newsize < minsize: + newsize = newsize <<1 + self.mask = newsize - 1 + cdef triple_cell* table = PyMem_Malloc(newsize*sizeof(triple_cell)) + if table == NULL: + raise MemoryError() + memset(table,0,newsize*sizeof(triple_cell)) + self.table = table + self.used = 0 + self.fill = 0 self.eraser = TripleDictEraser(self) - if data is not None: - for (k1,k2,k3), v in data.iteritems(): - self.set(k1,k2,k3, v) + self.weak_values = weak_values + if data: + for k,v in data: + self[k]=v + + def __dealloc__(self): + #is this required? (TripleDict_clear is safe to call multiple times) + TripleDict_clear(self) def __len__(self): """ @@ -1226,89 +1296,7 @@ cdef class TripleDict: sage: len(L) 3 """ - return self._size - - def stats(self): - """ - The distribution of items in buckets. - - OUTPUT: - - - (min, avg, max) - - EXAMPLES:: - - sage: from sage.structure.coerce_dict import TripleDict - sage: L = TripleDict(37) - sage: for i in range(100): L[i,i,i] = None - sage: L.stats() # random - (2, 2.7027027027027026, 4) - - sage: L = TripleDict(3007) - sage: for i in range(100): L[i,i,i] = None - sage: L.stats() # random - (0, 0.03325573661456601, 1) - - In order to have a test that isn't random, we use parameters - that should not be used in real applications:: - - sage: L = TripleDict(1, threshold=0) - sage: for i in range(100): L[i,i,i] = None - sage: L.stats() - (100, 100.0, 100) - """ - cdef Py_ssize_t size = self._size - cdef Py_ssize_t cur, min = size, max = 0 - for bucket in self.buckets: - if bucket: - cur = len(bucket)/7 - if cur < min: min = cur - if cur > max: max = cur - else: - min = 0 - return min, 1.0*size/len(self.buckets), max - - def bucket_lens(self): - """ - The distribution of items in buckets. - - OUTPUT: - - A list of how many items are in each bucket. - - EXAMPLES:: - - sage: from sage.structure.coerce_dict import TripleDict - sage: L = TripleDict(37, threshold=0) - sage: for i in range(100): L[i,i,i] = None - sage: L.bucket_lens() # random - [3, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 3, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4] - sage: sum(L.bucket_lens()) - 100 - - In order to have a test that isn't random, we use parameters - that should not be used in real applications:: - - sage: L = TripleDict(1, threshold=0) - sage: for i in range(100): L[i,i,i] = None - sage: L.bucket_lens() - [100] - """ - return [len(self.buckets[i])/7 for i from 0 <= i < len(self.buckets)] - - def _get_buckets(self): - """ - The actual buckets of self, for debugging. - - EXAMPLE:: - - sage: from sage.structure.coerce_dict import TripleDict - sage: L = TripleDict(3) - sage: L[0,0,0] = None - sage: L._get_buckets() # random - [[0, 0, 0, None], [], []] - """ - return self.buckets + return self.used def __contains__(self, k): """ @@ -1334,12 +1322,23 @@ cdef class TripleDict: try: k1, k2, k3 = k except (TypeError,ValueError): + raise KeyError, k + cdef triple_cell* cursor = self.lookup(k1,k2,k3) + if cursor.key_id1 == NULL or cursor.key_id1 == dummy: return False - try: - self.get(k1,k2,k3) - except KeyError: + r = cursor.key_weakref1 + if isinstance(r, fixed_KeyedRef) and PyWeakref_GetObject(r) == Py_None: return False - return True + r = cursor.key_weakref2 + if isinstance(r, fixed_KeyedRef) and PyWeakref_GetObject(r) == Py_None: + return False + r = cursor.key_weakref3 + if isinstance(r, fixed_KeyedRef) and PyWeakref_GetObject(r) == Py_None: + return False + if not(self.weak_values): + return True + value = cursor.value + return (not isinstance(value,fixed_KeyedRef)) or PyWeakref_GetObject(value) != Py_None def __getitem__(self, k): """ @@ -1362,40 +1361,22 @@ cdef class TripleDict: return self.get(k1, k2, k3) cdef get(self, object k1, object k2, object k3): - cdef Py_ssize_t h1 = signed_id(k1) - cdef Py_ssize_t h2 = signed_id(k2) - cdef Py_ssize_t h3 = signed_id(k3) - cdef Py_ssize_t h = (h1 + 13*h2 ^ 503*h3) - - cdef object r1,r2,r3, val - cdef PyObject* ref_val - cdef Py_ssize_t i - cdef list all_buckets = self.buckets - cdef list bucket = PyList_GET_ITEM(all_buckets, (h )% PyList_GET_SIZE(all_buckets)) - for i from 0 <= i < PyList_GET_SIZE(bucket) by 7: - tmp = PyList_GET_ITEM(bucket, i) - if PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i)) == h1 and \ - PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i+1)) == h2 and \ - PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i+2)) == h3: - r1 = PyList_GET_ITEM(bucket, i+3) - r2 = PyList_GET_ITEM(bucket, i+4) - r3 = PyList_GET_ITEM(bucket, i+5) - if (isinstance(r1,KeyedRef) and PyWeakref_GetObject(r1) == Py_None) or \ - (isinstance(r2,KeyedRef) and PyWeakref_GetObject(r2) == Py_None) or \ - (isinstance(r3,KeyedRef) and PyWeakref_GetObject(r3) == Py_None): - raise KeyError, (k1,k2,k3) - else: - val = PyList_GET_ITEM(bucket, i+6) - if self.weak_values: - if not isinstance(val, KeyedRef): - return val - ref_val = PyWeakref_GetObject(val) - if ref_val == Py_None: - raise KeyError, (k1,k2,k3) - return ref_val - else: - return val - raise KeyError, (k1, k2, k3) + cdef triple_cell* cursor = self.lookup(k1,k2,k3) + if cursor.key_id1 == NULL or cursor.key_id1 == dummy: + raise KeyError, (k1, k2, k3) + r1 = cursor.key_weakref1 + r2 = cursor.key_weakref2 + r3 = cursor.key_weakref3 + if (isinstance(r1, fixed_KeyedRef) and PyWeakref_GetObject(r1) == Py_None) or \ + (isinstance(r2, fixed_KeyedRef) and PyWeakref_GetObject(r2) == Py_None) or \ + (isinstance(r3, fixed_KeyedRef) and PyWeakref_GetObject(r3) == Py_None): + raise KeyError, (k1, k2, k3) + value = cursor.value + if self.weak_values and isinstance(value,fixed_KeyedRef): + value = PyWeakref_GetObject(value) + if value is None: + raise KeyError, (k1, k2, k3) + return value def __setitem__(self, k, value): """ @@ -1418,73 +1399,70 @@ cdef class TripleDict: self.set(k1, k2, k3, value) cdef set(self, object k1, object k2, object k3, value): - if self.threshold and self._size > len(self.buckets) * self.threshold: - self.resize() - cdef Py_ssize_t h1 = signed_id(k1) - cdef Py_ssize_t h2 = signed_id(k2) - cdef Py_ssize_t h3 = signed_id(k3) - cdef Py_ssize_t h = (h1 + 13*h2 ^ 503*h3) + cdef triple_cell entry + cdef PyObject* old_value = NULL + cdef bint maybe_resize = False + entry.key_id1 = k1 + entry.key_id2 = k2 + entry.key_id3 = k3 if self.weak_values: + k = (PyCapsule_New((k1),NULL,NULL), + PyCapsule_New((k2),NULL,NULL), + PyCapsule_New((k3),NULL,NULL)) try: - value = KeyedRef(value,self.eraser,(h1, h2, h3, 6)) + value_store = fixed_KeyedRef(value,self.eraser,k) + entry.value = value_store except TypeError: - pass - - cdef object r1,r2,r3 - cdef Py_ssize_t i - cdef list all_buckets = self.buckets - cdef list bucket = PyList_GET_ITEM(all_buckets, (h) % PyList_GET_SIZE(all_buckets)) - for i from 0 <= i < PyList_GET_SIZE(bucket) by 7: - tmp = PyList_GET_ITEM(bucket, i) - if PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i)) == h1 and \ - PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i+1)) == h2 and \ - PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i+2)) == h3: - r1 = PyList_GET_ITEM(bucket, i+3) - r2 = PyList_GET_ITEM(bucket, i+4) - r3 = PyList_GET_ITEM(bucket, i+5) - if (isinstance(r1,KeyedRef) and PyWeakref_GetObject(r1) == Py_None) or \ - (isinstance(r2,KeyedRef) and PyWeakref_GetObject(r2) == Py_None) or \ - (isinstance(r3,KeyedRef) and PyWeakref_GetObject(r3) == Py_None): - #apparently one of the keys has died but the callback hasn't executed yet. - #we delete the whole entry (including the weakrefs) and hope that this - #purges the callbacks too (it should, because the weakref doesn't - #exist anymore. In particular it cannot be passed as a parameter to - #the callback anymore) - del bucket[i:i+7] - self._size -= 1 - else: - #keys are present and alive, so we can just store the new value - bucket[i+6]=value - return - - #at this point the key triple isn't present so we append a new entry. - #we first form the appropriate weakrefs to receive callbacks on. - try: - r1 = KeyedRef(k1,self.eraser,(h1, h2, h3, 3)) - except TypeError: - r1 = k1 - if k2 is not k1: + entry.value = value + else: + entry.value = value + Py_XINCREF(entry.value) + cursor = self.lookup(k1,k2,k3) + if cursor.key_id1 == NULL or cursor.key_id1 == dummy: + self.used += 1 + if cursor.key_id1 == NULL: + self.fill += 1 + maybe_resize = True + if not(self.weak_values): + k = (PyCapsule_New((k1),NULL,NULL), + PyCapsule_New((k2),NULL,NULL), + PyCapsule_New((k3),NULL,NULL)) try: - r2 = KeyedRef(k2,self.eraser,(h1, h2, h3, 4)) + key_store=fixed_KeyedRef(k1,self.eraser,k) + entry.key_weakref1 = key_store except TypeError: - r2 = k2 - else: - r2 = None - if k3 is not k2 or k3 is not k1: + entry.key_weakref1 = k1 + Py_XINCREF(entry.key_weakref1) try: - r3 = KeyedRef(k3,self.eraser,(h1, h2, h3, 5)) + key_store=fixed_KeyedRef(k2,self.eraser,k) + entry.key_weakref2 = key_store except TypeError: - r3 = k3 + entry.key_weakref2 = k2 + Py_XINCREF(entry.key_weakref2) + try: + key_store=fixed_KeyedRef(k3,self.eraser,k) + entry.key_weakref3 = key_store + except TypeError: + entry.key_weakref3 = k3 + Py_XINCREF(entry.key_weakref3) + + #we're taking a bit of a gamble here: we're assuming the dictionary has + #not been resized (otherwise cursor might not be a valid location + #anymore). The only way in which that could happen is if the allocation + #activity above forced a GC that triggered code that ADDS entries to this + #dictionary: the dictionary can only get reshaped if self.fill increases. + #(as happens below). Note that we're holding a strong ref to the dict + #itself, so that's not liable to disappear. + #for the truly paranoid: we could detect a change by checking if self.table + #has changed value + cursor[0] = entry + + #this is the only place where resize gets called: + if maybe_resize and 3*self.fill > 2*self.mask: self.resize() else: - r3 = None - PyList_Append(bucket, h1) - PyList_Append(bucket, h2) - PyList_Append(bucket, h3) - PyList_Append(bucket, r1) - PyList_Append(bucket, r2) - PyList_Append(bucket, r3) - PyList_Append(bucket, value) - self._size += 1 + old_value = cursor.value + cursor.value = entry.value + Py_XDECREF(old_value) def __delitem__(self, k): """ @@ -1505,86 +1483,15 @@ cdef class TripleDict: False """ cdef object k1,k2,k3 - cdef object r1,r2,r3 try: k1, k2, k3 = k except (TypeError,ValueError): raise KeyError, k - cdef Py_ssize_t h1 = signed_id(k1) - cdef Py_ssize_t h2 = signed_id(k2) - cdef Py_ssize_t h3 = signed_id(k3) - cdef Py_ssize_t h = (h1 + 13*h2 ^ 503*h3) - - cdef Py_ssize_t i - cdef list all_buckets = self.buckets - cdef list bucket = PyList_GET_ITEM(all_buckets, (h) % PyList_GET_SIZE(all_buckets)) - for i from 0 <= i < PyList_GET_SIZE(bucket) by 7: - if PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i)) == h1 and \ - PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i+1)) == h2 and \ - PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i+2)) == h3: - r1 = PyList_GET_ITEM(bucket, i+3) - r2 = PyList_GET_ITEM(bucket, i+4) - r3 = PyList_GET_ITEM(bucket, i+5) - del bucket[i:i+7] - self._size -= 1 - if (isinstance(r1,KeyedRef) and PyWeakref_GetObject(r1) == Py_None) or \ - (isinstance(r2,KeyedRef) and PyWeakref_GetObject(r2) == Py_None) or \ - (isinstance(r3,KeyedRef) and PyWeakref_GetObject(r3) == Py_None): - #the entry was already dead - break - else: - return - raise KeyError, k - - def resize(self, int buckets=0): - """ - Change the number of buckets of self, while preserving the contents. - - If the number of buckets is 0 or not given, it resizes self to the - smallest prime that is at least twice as large as self. - - EXAMPLES:: - - sage: from sage.structure.coerce_dict import TripleDict - sage: L = TripleDict(8) - sage: for i in range(100): L[i,i,i] = None - sage: L.bucket_lens() # random - [50, 0, 0, 0, 50, 0, 0, 0] - sage: L.resize(7) # random - [15, 14, 14, 14, 14, 15, 14] - sage: L.resize() - sage: len(L.bucket_lens()) - 17 - """ - if buckets == 0: - buckets = next_odd_prime(2*len(self.buckets)) - cdef list old_buckets = self.buckets - cdef list bucket - cdef Py_ssize_t i - cdef Py_ssize_t h - cdef list new_buckets = [[] for i from 0 <= i < buckets] - cdef Py_ssize_t k1,k2,k3 - - #this would be a very bad place for a garbage collection to happen - #so we disable them. - cdef bint gc_originally_enabled = gc.isenabled() - if gc_originally_enabled: - gc.disable() - - #BEGIN of critical block. NO GC HERE! - for bucket in old_buckets: - for i from 0 <= i < PyList_GET_SIZE(bucket) by 7: - k1 = PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i)) - k2 = PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i+1)) - k3 = PyInt_AsSsize_t(PyList_GET_ITEM(bucket, i+2)) - h = (k1 + 13*k2 ^ 503*k3) - #this line can trigger allocation, so GC must be turned off! - new_buckets[( h) % buckets] += bucket[i:i+7] - self.buckets = new_buckets - #END of critical block. The dict is consistent again. - - if gc_originally_enabled: - gc.enable() + cdef triple_cell* cursor = self.lookup(k1,k2,k3) + if cursor.key_id1 == NULL or cursor.key_id1==dummy: + raise KeyError, k + L=extract_triple_cell(cursor) + self.used -= 1 def iteritems(self): """ @@ -1596,28 +1503,37 @@ cdef class TripleDict: sage: list(L.iteritems()) [((1, 2, 3), None)] """ - cdef list bucket - cdef Py_ssize_t i - cdef object r1,r2,r3 - # We test whether the references are still valid. - # However, we must not delete them, since we are - # iterating. - for bucket in self.buckets: - for i from 0<=iPyWeakref_GetObject(r1) - if r1 is None: + + cdef size_t i = 0 + while i <= self.mask: + cursor = &(self.table[i]) + i += 1 + if cursor.key_id1 != NULL and cursor.key_id1 != dummy: + key1 = (cursor.key_weakref1) + key2 = (cursor.key_weakref2) + key3 = (cursor.key_weakref3) + value = (cursor.value) + if isinstance(key1,fixed_KeyedRef): + key1 = PyWeakref_GetObject(key1) + if key1 is None: + print "found defunct key1" + continue + if isinstance(key2,fixed_KeyedRef): + key2 = PyWeakref_GetObject(key2) + if key2 is None: + print "found defunct key2" continue - if isinstance(r2, KeyedRef): - r2 = PyWeakref_GetObject(r2) - if r2 is None: + if isinstance(key3,fixed_KeyedRef): + key3 = PyWeakref_GetObject(key3) + if key3 is None: + print "found defunct key3" continue - if isinstance(r3, KeyedRef): - r3 = PyWeakref_GetObject(r3) - if r3 is None: + if self.weak_values and isinstance(value,fixed_KeyedRef): + value = PyWeakref_GetObject(value) + if value is None: + print "found defunct value" continue - yield (r1,r2,r3), PyList_GET_ITEM(bucket,i+6) + yield ((key1,key2,key3), value) def __reduce__(self): """ @@ -1634,18 +1550,62 @@ cdef class TripleDict: sage: list(loads(dumps(L)).iteritems()) [((1, 2, 3), True)] """ - return TripleDict, (len(self.buckets), dict(self.iteritems()), self.threshold) - -cdef long next_odd_prime(long n): - if n % 2 == 0: - n -= 1 - cdef long k - while n > 0: - n += 2 - k = 3 - while k*k <= n: - if n % k == 0: - break - k += 2 - if k*k > n: - return n + return TripleDict, (11, dict(self.iteritems()), 0.7) + +#the cython supplied tp_traverse and tp_clear do not take the dynamically +#allocated table into account, so we have to supply our own. +#the only additional link to follow (that cython does pick up and we have +#to replicate here) is the "eraser" which in its closure stores a reference +#back to the dictionary itself (meaning that MonoDicts only disappear +#on cyclic GC) + +cdef int TripleDict_traverse(TripleDict op, visitproc visit, void *arg): + cdef int r + if op.table == NULL: + return 0 + table=op.table + cdef size_t i = 0 + if ((op.eraser)): + r=visit((op.eraser),arg) + if r: return r + for i in range(op.mask+1): + cursor = &table[i] + if cursor.key_id1 != NULL and cursor.key_id1 != dummy: + r=visit(cursor.key_weakref1,arg) + if r: return r + r=visit(cursor.key_weakref2,arg) + if r: return r + r=visit(cursor.key_weakref3,arg) + if r: return r + r=visit(cursor.value,arg) + if r: return r + return 0 + +cdef int TripleDict_clear(TripleDict op): + if op.table == NULL: + return 0 + + tmp=op.eraser + cdef triple_cell* table=op.table + cdef size_t mask=op.mask + op.table=NULL + op.eraser=None + op.mask=0 + op.used=0 + op.fill=0 + + #refcount dance to ensure op.eraser==None when the actual object gets deallocated + del tmp + for i in range(mask+1): + cursor = &(table[i]) + if cursor.key_id1 != NULL and cursor.key_id1 != dummy: + cursor.key_id1 = dummy + Py_XDECREF(cursor.key_weakref1) + Py_XDECREF(cursor.key_weakref2) + Py_XDECREF(cursor.key_weakref3) + Py_XDECREF(cursor.value) + PyMem_Free(table) + return 0 + +(TripleDict).tp_traverse = &TripleDict_traverse +(TripleDict).tp_clear = &TripleDict_clear From 719cdec176875685142039dce297a7fd8ae4143b Mon Sep 17 00:00:00 2001 From: Simon King Date: Tue, 12 Nov 2013 12:47:19 +0100 Subject: [PATCH 091/206] Remove unused debug method CoercionModel_cache_maps.get_stats() --- src/sage/structure/coerce.pyx | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index c0f14a48e4c..93a41ed3b1b 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -176,8 +176,6 @@ cdef class CoercionModel_cache_maps(CoercionModel): sage: A = cm.get_action(ZZ, NumberField(x^2-2, 'a'), operator.mul) sage: f, g = cm.coercion_maps(QQ, int) sage: f, g = cm.coercion_maps(ZZ, int) - sage: cm.get_stats() # random - ((0, 1.0, 4), (0, 0.0, 0)) .. note:: @@ -198,11 +196,11 @@ cdef class CoercionModel_cache_maps(CoercionModel): EXAMPLES:: sage: cm = sage.structure.element.get_coercion_model() - sage: cm.get_stats() # random - ((0, 0.3307086614173229, 3), (0, 0.1496062992125984, 2)) + sage: len(cm.get_cache()[0]) # random + 42 sage: cm.reset_cache() - sage: cm.get_stats() - ((0, 0.0, 0), (0, 0.0, 0)) + sage: cm.get_cache() + ({}, {}) """ # This MUST be a mapping of tuples, where each # tuple contains at least two elements that are either @@ -259,25 +257,6 @@ cdef class CoercionModel_cache_maps(CoercionModel): return dict([((S, R), mors) for (S, R, op), mors in self._coercion_maps.iteritems()]), \ dict(self._action_maps.iteritems()) - def get_stats(self): - """ - This returns the state of the cache of coercion maps and actions, - primarily useful for debugging and introspection. - If a class is not part of the coercion system, we should call the - __rmul__ method when it makes sense. - - The coercion maps are stored in a specialized TripleDict hashtable, - and the stats returned are (min, avg, max) of the number of items - per bucket. The lower the better. - - EXAMPLES:: - - sage: cm = sage.structure.element.get_coercion_model() - sage: cm.get_stats() # random - ((0, 0.16058394160583941, 2), (0, 0.13138686131386862, 3)) - """ - return self._coercion_maps.stats(), self._action_maps.stats() - def record_exceptions(self, bint value=True): r""" Enables (or disables) recording of the exceptions suppressed during From c0fc6f4bc92d75571006d0ab14797b52ebe6fd47 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 19 Nov 2013 12:43:33 -0800 Subject: [PATCH 092/206] Some formatting changes. --- src/sage/combinat/permutation.py | 88 +++++++++++++++----------------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 1bd1fa1552d..8b167e89ac3 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -1982,44 +1982,49 @@ def foata_bijection(self): Return the image of the permutation ``self`` under the Foata bijection `\phi`. - The bijection shows that maj and inv are equidistributed: if - `\phi(P)=Q`, then `\mathrm{maj}(P)=\mathrm{inv}(Q)`. + The bijection shows that `\mathrm{maj}` and `\mathrm{inv}` are + equidistributed: if `\phi(P) = Q`, then `\mathrm{maj}(P) = + \mathrm{inv}(Q)`. The Foata bijection `\phi` is a bijection on the set of words with no two equal letters. It can be defined by induction on the size - of the word: Given a word `w_1w_2\ldots w_n`, start with - `\phi(w_1) = w_1`. At the `i`th step, if - `\phi(w_1w_2\ldots w_i) = v_1v_2\ldots v_i`, we define - `\phi(w_1w_2\ldots w_{i+1})` by placing `w_{i+1}` on the end of - the word `v_1v_2\ldots v_i` and breaking the word up into blocks - as follows. If `w_{i+1}>v_i`, place a vertical line to the right - of each `v_k` for which `w_{i+1}>v_k`. Otherwise, if - `w_{i+1} v_i`, place a vertical line to the right + of each `v_k` for which `w_{i+1} > v_k`. Otherwise, if + `w_{i+1} < v_i`, place a vertical line to the right of each `v_k` + for which `w_{i+1} < v_k`. In either case, place a vertical line at the start of the word as well. Now, within each block between vertical lines, cyclically shift the entries one place to the right. For instance, to compute `\phi([1,4,2,5,3])`, the sequence of - words is `1`, `|1|4\to 14`, `|14|2 \to 412`, `|4|1|2|5 \to 4125`, - `|4|125|3\to 45123`. - So `\phi([1,4,2,5,3])=[4,5,1,2,3]`. + words is + + * `1`, + * `|1|4 \to 14`, + * `|14|2 \to 412`, + * `|4|1|2|5 \to 4125`, + * `|4|125|3 \to 45123`. + + So `\phi([1,4,2,5,3]) = [4,5,1,2,3]`. See section 2 of [FoSc78]_. REFERENCES:: - .. [FoSc78] Dominique Foata, Marcel-Paul Schuetzenberger, - *Major Index and Inversion Number of Permutations*, - Mathematische Nachrichten, volume 83, Issue 1, pages 143-159, - 1978. + .. [FoSc78] Dominique Foata, Marcel-Paul Schuetzenberger. + *Major Index and Inversion Number of Permutations*. + Mathematische Nachrichten, volume 83, Issue 1, pages 143-159, 1978. http://igm.univ-mlv.fr/~berstel/Mps/Travaux/A/1978-3MajorIndexMathNachr.pdf EXAMPLES:: sage: Permutation([1,2,4,3]).foata_bijection() [4, 1, 2, 3] - sage: Permutation([2,5,1,3,4]).foata_bijection() [2, 1, 3, 5, 4] @@ -2040,7 +2045,6 @@ def foata_bijection(self): sage: Permutation([]).foata_bijection() [] - sage: Permutation([1]).foata_bijection() [1] """ @@ -2049,32 +2053,24 @@ def foata_bijection(self): for e in L: M.append(e) k = len(M) - if k > 1: - a = M[-2] - M_prime = [0]*k - if a > e: - index_list = [i for i in range(k - 1) if M[i] > e] - index_list = [-1] + index_list - t = len(index_list) - for j in range(1, t): - start = index_list[j-1] + 1 - end = index_list[j] - M_prime[start] = M[end] - for x in range(start + 1, end + 1): - M_prime[x] = M[x-1] - M_prime[k-1] = e - else: - index_list = [i for i in range(k - 1) if M[i] < e] - index_list = [-1] + index_list - t = len(index_list) - for j in range(1, t): - start = index_list[j-1] + 1 - end = index_list[j] - M_prime[start] = M[end] - for x in range(start + 1, end + 1): - M_prime[x] = M[x-1] - M_prime[k-1] = e - M = M_prime + if k <= 1: + continue + + a = M[-2] + M_prime = [0]*k + if a > e: + index_list = [-1] + [i for i in range(k - 1) if M[i] > e] + else: + index_list = [-1] + [i for i in range(k - 1) if M[i] < e] + + for j in range(1, len(index_list)): + start = index_list[j-1] + 1 + end = index_list[j] + M_prime[start] = M[end] + for x in range(start + 1, end + 1): + M_prime[x] = M[x-1] + M_prime[k-1] = e + M = M_prime return Permutation(M) def to_lehmer_code(self): From 5a60319ced9c1921892f2b4235a4439e5a84672e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 19 Nov 2013 12:56:44 -0800 Subject: [PATCH 093/206] Fixes to documentation. --- src/sage/combinat/permutation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 8b167e89ac3..906449008aa 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -1990,7 +1990,7 @@ def foata_bijection(self): no two equal letters. It can be defined by induction on the size of the word: Given a word `w_1 w_2 \cdots w_n`, start with `\phi(w_1) = w_1`. At the `i`-th step, if - `\phi(w_1 w_2 \cdots w_i) = v_1 v_2 \ldots v_i`, we define + `\phi(w_1 w_2 \cdots w_i) = v_1 v_2 \cdots v_i`, we define `\phi(w_1 w_2 \cdots w_i w_{i+1})` by placing `w_{i+1}` on the end of the word `v_1 v_2 \cdots v_i` and breaking the word up into blocks as follows. If `w_{i+1} > v_i`, place a vertical line to the right @@ -2014,7 +2014,7 @@ def foata_bijection(self): See section 2 of [FoSc78]_. - REFERENCES:: + REFERENCES: .. [FoSc78] Dominique Foata, Marcel-Paul Schuetzenberger. *Major Index and Inversion Number of Permutations*. From ce3c5d9f3161a234636c6d3114c2981f32df26ab Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 22 Nov 2013 11:23:23 +0100 Subject: [PATCH 094/206] trac #15445: clean the docstrings of sage.coding.guava --- src/sage/coding/guava.py | 60 +++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/sage/coding/guava.py b/src/sage/coding/guava.py index 325333e10ce..6263647d5da 100644 --- a/src/sage/coding/guava.py +++ b/src/sage/coding/guava.py @@ -3,16 +3,23 @@ This module only contains Guava wrappers (Guava is an optional GAP package). -AUTHOR: - -- David Joyner (2005-11-22, 2006-12-03): initial version - -- Nick Alexander (2006-12-10): factor GUAVA code to guava.py - -- David Joyner (2007-05): removed Golay codes, toric and trivial - codes and placed them in code_constructions; - renamed RandomLinearCode->RandomLinearCodeGuava - -- David Joyner (2008-03): removed QR, XQR, cyclic and ReedSolomon codes - -- " (2009-05): added "optional package" comments, fixed some docstrings to - to be sphinx compatible +AUTHORS: +- David Joyner (2005-11-22, 2006-12-03): initial version + +- Nick Alexander (2006-12-10): factor GUAVA code to guava.py + +- David Joyner (2007-05): removed Golay codes, toric and trivial codes and + placed them in code_constructions; renamed RandomLinearCode to + RandomLinearCodeGuava + +- David Joyner (2008-03): removed QR, XQR, cyclic and ReedSolomon codes + +- David Joyner (2009-05): added "optional package" comments, fixed some + docstrings to to be sphinx compatible + +Functions +--------- """ #***************************************************************************** @@ -44,12 +51,15 @@ def BinaryReedMullerCode(r,k): in `m` variables. INPUT: - r, k -- positive integers with `2^k>r`. + + - ``r, k`` -- positive integers with `2^k>r`. OUTPUT: - Returns the binary 'Reed-Muller code' with dimension k and order r. + + Returns the binary 'Reed-Muller code' with dimension `k` and order `r`. EXAMPLE:: + sage: C = BinaryReedMullerCode(2,4); C # optional - gap_packages (Guava package) Linear code of length 16, dimension 11 over Finite Field of size 2 sage: C.minimum_distance() # optional - gap_packages (Guava package) @@ -88,20 +98,25 @@ def QuasiQuadraticResidueCode(p): matrix whose top row is `(0,y_1,...,y_{p-1})`, where `x_i+y_i=1` for all `i`. INPUT: - p -- a prime >2. + + - ``p`` -- a prime `>2`. OUTPUT: - Returns a QQR code of length 2p. + + Returns a QQR code of length `2p`. EXAMPLES:: + sage: C = QuasiQuadraticResidueCode(11); C # optional - gap_packages (Guava package) Linear code of length 22, dimension 11 over Finite Field of size 2 REFERENCES: - ..[BM] Bazzi and Mitter, {\it Some constructions of codes from group actions}, (preprint - March 2003, available on Mitter's MIT website). - ..[J] D. Joyner, {\it On quadratic residue codes and hyperelliptic curves}, - (preprint 2006) + + .. [BM] Bazzi and Mitter, {\it Some constructions of codes from group actions}, (preprint + March 2003, available on Mitter's MIT website). + + .. [Jresidue] D. Joyner, {\it On quadratic residue codes and hyperelliptic curves}, + (preprint 2006) These are self-orthogonal in general and self-dual when $p \\equiv 3 \\pmod 4$. @@ -116,8 +131,6 @@ def QuasiQuadraticResidueCode(p): MS = MatrixSpace(F,k,n) return LinearCode(MS(G)) - - def RandomLinearCodeGuava(n,k,F): r""" The method used is to first construct a `k \times n` matrix of the block @@ -127,12 +140,15 @@ def RandomLinearCodeGuava(n,k,F): group `S_n`. INPUT: - Integers `n,k`, with `n>k>1`. + + - ``n,k`` -- integers with `n>k>1`. OUTPUT: - Returns a "random" linear code with length n, dimension k over field F. + + Returns a "random" linear code with length `n`, dimension `k` over field `F`. EXAMPLES:: + sage: C = RandomLinearCodeGuava(30,15,GF(2)); C # optional - gap_packages (Guava package) Linear code of length 30, dimension 15 over Finite Field of size 2 sage: C = RandomLinearCodeGuava(10,5,GF(4,'a')); C # optional - gap_packages (Guava package) @@ -150,5 +166,3 @@ def RandomLinearCodeGuava(n,k,F): G = [[gfq_gap_to_sage(gap.eval("G[%s][%s]" % (i,j)),F) for j in range(1,n+1)] for i in range(1,k+1)] MS = MatrixSpace(F,k,n) return LinearCode(MS(G)) - - From a583c02be72a4ac9e5c998615218efc83deb3c3d Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 22 Nov 2013 11:27:37 +0100 Subject: [PATCH 095/206] trac #15445: adds a coding.codes_catalog module exported as codes. --- src/doc/en/reference/coding/index.rst | 4 ++- src/sage/coding/all.py | 2 ++ src/sage/coding/code_constructions.py | 38 ++++++++++++---------- src/sage/coding/codes_catalog.py | 47 +++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 18 deletions(-) create mode 100644 src/sage/coding/codes_catalog.py diff --git a/src/doc/en/reference/coding/index.rst b/src/doc/en/reference/coding/index.rst index 0ffc18067a7..a6482d9f5bd 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -2,10 +2,12 @@ Coding Theory ============= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 + sage/coding/codes_catalog sage/coding/linear_code sage/coding/code_constructions + sage/coding/guava sage/coding/sd_codes sage/coding/code_bounds sage/coding/delsarte_bounds diff --git a/src/sage/coding/all.py b/src/sage/coding/all.py index 0f4e59510be..06ad8bc0ff8 100644 --- a/src/sage/coding/all.py +++ b/src/sage/coding/all.py @@ -59,3 +59,5 @@ lazy_import("sage.coding.delsarte_bounds", ["Krawtchouk", "delsarte_bound_hamming_space", "delsarte_bound_additive_hamming_space"]) + +lazy_import('sage.coding', 'codes_catalog', 'codes') diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index b4addf86c39..ab734ebeac1 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -1,28 +1,19 @@ r""" Linear code constructions -AUTHOR: - -- David Joyner (2007-05): initial version - -- " (2008-02): added cyclic codes, Hamming codes - -- " (2008-03): added BCH code, LinearCodeFromCheckmatrix, ReedSolomonCode, WalshCode, - DuadicCodeEvenPair, DuadicCodeOddPair, QR codes (even and odd) - -- " (2008-09) fix for bug in BCHCode reported by F. Voloch - -- " (2008-10) small docstring changes to WalshCode and walsh_matrix - This file contains constructions of error-correcting codes which are pure Python/Sage and not obtained from wrapping GUAVA functions. The GUAVA wrappers are in guava.py. +All codes available here can be accessed through the ``codes`` object:: + + sage: codes.HammingCode(3,GF(2)) + Linear code of length 7, dimension 4 over Finite Field of size 2 + Let `F` be a finite field with `q` elements. Here's a constructive definition of a cyclic code of length `n`. - #. Pick a monic polynomial `g(x)\in F[x]` dividing `x^n-1`. This is called the generating polynomial of the code. @@ -35,7 +26,6 @@ `C`. Every codeword in `C` arises in this way (from some `p(x)`). - The polynomial notation for the code is to call `c_0+c_1x+...+c_{n-1}x^{n-1}` the codeword (instead of `(c_0,c_1,...,c_{n-1})`). The polynomial @@ -127,13 +117,27 @@ related to Hadamard matrices. http://en.wikipedia.org/wiki/Walsh_code -Please see the docstrings below for further details. - REFERENCES: .. [HP] W. C. Huffman, V. Pless, Fundamentals of Error-Correcting Codes, Cambridge Univ. Press, 2003. +AUTHOR: + +- David Joyner (2007-05): initial version + +- " (2008-02): added cyclic codes, Hamming codes + +- " (2008-03): added BCH code, LinearCodeFromCheckmatrix, ReedSolomonCode, WalshCode, + DuadicCodeEvenPair, DuadicCodeOddPair, QR codes (even and odd) + +- " (2008-09) fix for bug in BCHCode reported by F. Voloch + +- " (2008-10) small docstring changes to WalshCode and walsh_matrix + +Functions +--------- + """ ############################################################################ ## Copyright David Joyner, 2007. wdjoyner@gmail.com. diff --git a/src/sage/coding/codes_catalog.py b/src/sage/coding/codes_catalog.py new file mode 100644 index 00000000000..58a4bfdd6ac --- /dev/null +++ b/src/sage/coding/codes_catalog.py @@ -0,0 +1,47 @@ +r""" +Index of Codes + +The ``codes`` object may be used to access the codes that Sage can build. + +- :func:`codes.BCHCode ` +- :func:`codes.BinaryGolayCode ` +- :func:`codes.BinaryReedMullerCode ` +- :func:`codes.CyclicCodeFromGeneratingPolynomial ` +- :func:`codes.CyclicCodeFromCheckPolynomial ` +- :func:`codes.DuadicCodeEvenPair ` +- :func:`codes.DuadicCodeOddPair ` +- :func:`codes.ExtendedBinaryGolayCode ` +- :func:`codes.ExtendedQuadraticResidueCode ` +- :func:`codes.ExtendedTernaryGolayCode ` +- :func:`codes.HammingCode ` +- :func:`codes.LinearCodeFromCheckMatrix ` +- :func:`codes.QuadraticResidueCode ` +- :func:`codes.QuadraticResidueCodeEvenPair ` +- :func:`codes.QuadraticResidueCodeOddPair ` +- :func:`codes.QuasiQuadraticResidueCode ` +- :func:`codes.RandomLinearCode ` +- :func:`codes.RandomLinearCodeGuava ` +- :func:`codes.ReedSolomonCode ` +- :func:`codes.TernaryGolayCode ` +- :func:`codes.ToricCode ` +- :func:`codes.TrivialCode ` +- :func:`codes.WalshCode ` + +""" + +# Implementation note: +# +# This module is imported as "codes" in all.py so that codes. is available +# in the global namespace. + +from code_constructions import (BCHCode, BinaryGolayCode, CyclicCodeFromGeneratingPolynomial, + CyclicCodeFromCheckPolynomial, DuadicCodeEvenPair, + DuadicCodeOddPair, ExtendedBinaryGolayCode, + ExtendedQuadraticResidueCode, ExtendedTernaryGolayCode, + HammingCode, LinearCodeFromCheckMatrix, + QuadraticResidueCode, QuadraticResidueCodeEvenPair, + QuadraticResidueCodeOddPair, RandomLinearCode, + ReedSolomonCode, TernaryGolayCode, + ToricCode, TrivialCode, WalshCode) + +from guava import BinaryReedMullerCode, QuasiQuadraticResidueCode, RandomLinearCodeGuava From c84b82081012e1cf154655d5805ee3cb0c1a8a67 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 23 Nov 2013 13:10:56 +0100 Subject: [PATCH 096/206] trac #15445: add CyclicCode to codes_catalog --- src/sage/coding/codes_catalog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/coding/codes_catalog.py b/src/sage/coding/codes_catalog.py index 58a4bfdd6ac..194685fe08c 100644 --- a/src/sage/coding/codes_catalog.py +++ b/src/sage/coding/codes_catalog.py @@ -6,6 +6,7 @@ - :func:`codes.BCHCode ` - :func:`codes.BinaryGolayCode ` - :func:`codes.BinaryReedMullerCode ` +- :func:`codes.CyclicCode ` - :func:`codes.CyclicCodeFromGeneratingPolynomial ` - :func:`codes.CyclicCodeFromCheckPolynomial ` - :func:`codes.DuadicCodeEvenPair ` @@ -35,7 +36,7 @@ # in the global namespace. from code_constructions import (BCHCode, BinaryGolayCode, CyclicCodeFromGeneratingPolynomial, - CyclicCodeFromCheckPolynomial, DuadicCodeEvenPair, + CyclicCode, CyclicCodeFromCheckPolynomial, DuadicCodeEvenPair, DuadicCodeOddPair, ExtendedBinaryGolayCode, ExtendedQuadraticResidueCode, ExtendedTernaryGolayCode, HammingCode, LinearCodeFromCheckMatrix, From a1594e9121ca62e68d7b23faa6aad7a8caa6eadd Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 23 Nov 2013 13:17:47 +0100 Subject: [PATCH 097/206] trac #15445: deprecating the Code constructors imported into the global namespace --- src/sage/coding/all.py | 68 ++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/src/sage/coding/all.py b/src/sage/coding/all.py index 06ad8bc0ff8..7a12efb5531 100644 --- a/src/sage/coding/all.py +++ b/src/sage/coding/all.py @@ -3,32 +3,48 @@ from ag_code import ag_code from code_constructions import (permutation_action, - walsh_matrix,cyclotomic_cosets, - BinaryGolayCode, - BCHCode, - CyclicCode, - CyclicCodeFromGeneratingPolynomial, - CyclicCodeFromCheckPolynomial, - DuadicCodeEvenPair, - DuadicCodeOddPair, - ExtendedBinaryGolayCode, - ExtendedQuadraticResidueCode, - ExtendedTernaryGolayCode, - HammingCode, - LinearCodeFromCheckMatrix, - QuadraticResidueCode, - QuadraticResidueCodeEvenPair, - QuadraticResidueCodeOddPair, - RandomLinearCode, - ReedSolomonCode, - TernaryGolayCode, - ToricCode, - TrivialCode, - WalshCode) - -from guava import (BinaryReedMullerCode, - QuasiQuadraticResidueCode, - RandomLinearCodeGuava) + walsh_matrix,cyclotomic_cosets) + +from sage.misc.superseded import deprecated_callable_import +deprecated_callable_import(15445, + 'sage.coding.code_constructions', + globals(), + locals(), + ["BinaryGolayCode", + "BCHCode", + "CyclicCode", + "CyclicCodeFromGeneratingPolynomial", + "CyclicCodeFromCheckPolynomial", + "DuadicCodeEvenPair", + "DuadicCodeOddPair", + "ExtendedBinaryGolayCode", + "ExtendedQuadraticResidueCode", + "ExtendedTernaryGolayCode", + "HammingCode", + "LinearCodeFromCheckMatrix", + "QuadraticResidueCode", + "QuadraticResidueCodeEvenPair", + "QuadraticResidueCodeOddPair", + "RandomLinearCode", + "ReedSolomonCode", + "TernaryGolayCode", + "ToricCode", + "TrivialCode", + "WalshCode"], + ("This method soon will not be available in that " + "way anymore. To use it, you can now call it by " + "typing codes.%(name)s")) + +deprecated_callable_import(15445, + 'sage.coding.guava', + globals(), + locals(), + ["BinaryReedMullerCode", + "QuasiQuadraticResidueCode", + "RandomLinearCodeGuava"], + ("This method soon will not be available in that " + "way anymore. To use it, you can now call it by " + "typing codes.%(name)s")) from code_bounds import (codesize_upper_bound, dimension_upper_bound, From fe875f7510dcbde0d7217fc16f360682821013ef Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 23 Nov 2013 13:37:38 +0100 Subject: [PATCH 098/206] trac #15445: updates doctests to use codes. --- src/sage/coding/binary_code.pyx | 4 +- src/sage/coding/code_constructions.py | 124 ++++++++++----------- src/sage/coding/decoder.py | 8 +- src/sage/coding/guava.py | 8 +- src/sage/coding/linear_code.py | 152 +++++++++++++------------- 5 files changed, 148 insertions(+), 148 deletions(-) diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index 10bc7457598..b6949f1abda 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -1086,7 +1086,7 @@ cdef class BinaryCode: EXAMPLE: sage: from sage.coding.binary_code import * - sage: B = BinaryCode(ExtendedBinaryGolayCode().gen_mat()) + sage: B = BinaryCode(codes.ExtendedBinaryGolayCode().gen_mat()) sage: B Binary [24,12] linear code, generator matrix [100000000000101011100011] @@ -3820,7 +3820,7 @@ cdef class BinaryCodeClassifier: EXAMPLE: sage: from sage.coding.binary_code import * sage: BC = BinaryCodeClassifier() - sage: B = BinaryCode(ExtendedBinaryGolayCode().gen_mat()) + sage: B = BinaryCode(codes.ExtendedBinaryGolayCode().gen_mat()) sage: B.apply_permutation(range(24,-1,-1)) sage: B Binary [24,12] linear code, generator matrix diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index ab734ebeac1..a2233750759 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -295,8 +295,8 @@ def is_a_splitting(S1,S2,n): sage: i2_sqrd = (i2^2).quo_rem(x^n-1)[1] sage: i2_sqrd == i2 True - sage: C1 = CyclicCodeFromGeneratingPolynomial(n,i1) - sage: C2 = CyclicCodeFromGeneratingPolynomial(n,1-i2) + sage: C1 = codes.CyclicCodeFromGeneratingPolynomial(n,i1) + sage: C2 = codes.CyclicCodeFromGeneratingPolynomial(n,1-i2) sage: C1.dual_code() == C2 True @@ -529,19 +529,19 @@ def BCHCode(n,delta,F,b=0): sage: f = x^(8)-1 sage: g.divides(f) True - sage: C = CyclicCode(8,g); C + sage: C = codes.CyclicCode(8,g); C Linear code of length 8, dimension 4 over Finite Field of size 3 sage: C.minimum_distance() 4 - sage: C = BCHCode(8,3,GF(3),1); C + sage: C = codes.BCHCode(8,3,GF(3),1); C Linear code of length 8, dimension 4 over Finite Field of size 3 sage: C.minimum_distance() 4 - sage: C = BCHCode(8,3,GF(3)); C + sage: C = codes.BCHCode(8,3,GF(3)); C Linear code of length 8, dimension 5 over Finite Field of size 3 sage: C.minimum_distance() 3 - sage: C = BCHCode(26, 5, GF(5), b=1); C + sage: C = codes.BCHCode(26, 5, GF(5), b=1); C Linear code of length 26, dimension 10 over Finite Field of size 5 """ @@ -577,7 +577,7 @@ def BinaryGolayCode(): EXAMPLE:: - sage: C = BinaryGolayCode() + sage: C = codes.BinaryGolayCode() sage: C Linear code of length 23, dimension 12 over Finite Field of size 2 sage: C.minimum_distance() @@ -625,15 +625,15 @@ def CyclicCodeFromGeneratingPolynomial(n,g,ignore=True): sage: P. = PolynomialRing(GF(3),"x") sage: g = x-1 - sage: C = CyclicCodeFromGeneratingPolynomial(4,g); C + sage: C = codes.CyclicCodeFromGeneratingPolynomial(4,g); C Linear code of length 4, dimension 3 over Finite Field of size 3 sage: P. = PolynomialRing(GF(4,"a"),"x") sage: g = x^3+1 - sage: C = CyclicCodeFromGeneratingPolynomial(9,g); C + sage: C = codes.CyclicCodeFromGeneratingPolynomial(9,g); C Linear code of length 9, dimension 6 over Finite Field in a of size 2^2 sage: P. = PolynomialRing(GF(2),"x") sage: g = x^3+x+1 - sage: C = CyclicCodeFromGeneratingPolynomial(7,g); C + sage: C = codes.CyclicCodeFromGeneratingPolynomial(7,g); C Linear code of length 7, dimension 4 over Finite Field of size 2 sage: C.gen_mat() [1 1 0 1 0 0 0] @@ -641,7 +641,7 @@ def CyclicCodeFromGeneratingPolynomial(n,g,ignore=True): [0 0 1 1 0 1 0] [0 0 0 1 1 0 1] sage: g = x+1 - sage: C = CyclicCodeFromGeneratingPolynomial(4,g); C + sage: C = codes.CyclicCodeFromGeneratingPolynomial(4,g); C Linear code of length 4, dimension 3 over Finite Field of size 2 sage: C.gen_mat() [1 1 0 0] @@ -665,7 +665,7 @@ def CyclicCodeFromGeneratingPolynomial(n,g,ignore=True): sage: P. = PolynomialRing(GF(3),"x") sage: g = x^2-1 - sage: C = CyclicCodeFromGeneratingPolynomial(5,g); C + sage: C = codes.CyclicCodeFromGeneratingPolynomial(5,g); C Linear code of length 5, dimension 4 over Finite Field of size 3 followed by C = CyclicCodeFromGeneratingPolynomial(5,g,False), with @@ -705,9 +705,9 @@ def CyclicCodeFromCheckPolynomial(n,h,ignore=True): EXAMPLES:: sage: P. = PolynomialRing(GF(3),"x") - sage: C = CyclicCodeFromCheckPolynomial(4,x + 1); C + sage: C = codes.CyclicCodeFromCheckPolynomial(4,x + 1); C Linear code of length 4, dimension 1 over Finite Field of size 3 - sage: C = CyclicCodeFromCheckPolynomial(4,x^3 + x^2 + x + 1); C + sage: C = codes.CyclicCodeFromCheckPolynomial(4,x^3 + x^2 + x + 1); C Linear code of length 4, dimension 3 over Finite Field of size 3 sage: C.gen_mat() [2 1 0 0] @@ -747,7 +747,7 @@ def DuadicCodeEvenPair(F,S1,S2): sage: S2 = C[2] sage: is_a_splitting(S1,S2,11) (True, 2) - sage: DuadicCodeEvenPair(GF(q),S1,S2) + sage: codes.DuadicCodeEvenPair(GF(q),S1,S2) (Linear code of length 11, dimension 5 over Finite Field of size 3, Linear code of length 11, dimension 5 over Finite Field of size 3) """ @@ -791,7 +791,7 @@ def DuadicCodeOddPair(F,S1,S2): sage: S2 = C[2] sage: is_a_splitting(S1,S2,11) (True, 2) - sage: DuadicCodeOddPair(GF(q),S1,S2) + sage: codes.DuadicCodeOddPair(GF(q),S1,S2) (Linear code of length 11, dimension 6 over Finite Field of size 3, Linear code of length 11, dimension 6 over Finite Field of size 3) @@ -828,7 +828,7 @@ def ExtendedBinaryGolayCode(): EXAMPLES:: - sage: C = ExtendedBinaryGolayCode() + sage: C = codes.ExtendedBinaryGolayCode() sage: C Linear code of length 24, dimension 12 over Finite Field of size 2 sage: C.minimum_distance() @@ -878,18 +878,18 @@ def ExtendedQuadraticResidueCode(n,F): EXAMPLES:: - sage: C1 = QuadraticResidueCode(7,GF(2)) + sage: C1 = codes.QuadraticResidueCode(7,GF(2)) sage: C2 = C1.extended_code() - sage: C3 = ExtendedQuadraticResidueCode(7,GF(2)); C3 + sage: C3 = codes.ExtendedQuadraticResidueCode(7,GF(2)); C3 Linear code of length 8, dimension 4 over Finite Field of size 2 sage: C2 == C3 True - sage: C = ExtendedQuadraticResidueCode(17,GF(2)) + sage: C = codes.ExtendedQuadraticResidueCode(17,GF(2)) sage: C Linear code of length 18, dimension 9 over Finite Field of size 2 - sage: C3 = QuadraticResidueCodeOddPair(7,GF(2))[0] + sage: C3 = codes.QuadraticResidueCodeOddPair(7,GF(2))[0] sage: C3x = C3.extended_code() - sage: C4 = ExtendedQuadraticResidueCode(7,GF(2)) + sage: C4 = codes.ExtendedQuadraticResidueCode(7,GF(2)) sage: C3x == C4 True @@ -907,7 +907,7 @@ def ExtendedTernaryGolayCode(): EXAMPLES:: - sage: C = ExtendedTernaryGolayCode() + sage: C = codes.ExtendedTernaryGolayCode() sage: C Linear code of length 12, dimension 6 over Finite Field of size 3 sage: C.minimum_distance() @@ -954,15 +954,15 @@ def HammingCode(r,F): EXAMPLES:: - sage: HammingCode(3,GF(2)) + sage: codes.HammingCode(3,GF(2)) Linear code of length 7, dimension 4 over Finite Field of size 2 - sage: C = HammingCode(3,GF(3)); C + sage: C = codes.HammingCode(3,GF(3)); C Linear code of length 13, dimension 10 over Finite Field of size 3 sage: C.minimum_distance() 3 sage: C.minimum_distance(algorithm='gap') # long time, check d=3 3 - sage: C = HammingCode(3,GF(4,'a')); C + sage: C = codes.HammingCode(3,GF(4,'a')); C Linear code of length 21, dimension 18 over Finite Field in a of size 2^2 """ q = F.order() @@ -998,22 +998,22 @@ def LinearCodeFromCheckMatrix(H): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: H = C.check_mat(); H [1 0 1 0 1 0 1] [0 1 1 0 0 1 1] [0 0 0 1 1 1 1] - sage: LinearCodeFromCheckMatrix(H) == C + sage: codes.LinearCodeFromCheckMatrix(H) == C True - sage: C = HammingCode(2,GF(3)) + sage: C = codes.HammingCode(2,GF(3)) sage: H = C.check_mat(); H [1 0 1 1] [0 1 1 2] - sage: LinearCodeFromCheckMatrix(H) == C + sage: codes.LinearCodeFromCheckMatrix(H) == C True - sage: C = RandomLinearCode(10,5,GF(4,"a")) + sage: C = codes.RandomLinearCode(10,5,GF(4,"a")) sage: H = C.check_mat() - sage: LinearCodeFromCheckMatrix(H) == C + sage: codes.LinearCodeFromCheckMatrix(H) == C True """ Cd = LinearCode(H) @@ -1043,18 +1043,18 @@ def QuadraticResidueCode(n,F): EXAMPLES:: - sage: C = QuadraticResidueCode(7,GF(2)) + sage: C = codes.QuadraticResidueCode(7,GF(2)) sage: C Linear code of length 7, dimension 4 over Finite Field of size 2 - sage: C = QuadraticResidueCode(17,GF(2)) + sage: C = codes.QuadraticResidueCode(17,GF(2)) sage: C Linear code of length 17, dimension 9 over Finite Field of size 2 - sage: C1 = QuadraticResidueCodeOddPair(7,GF(2))[0] - sage: C2 = QuadraticResidueCode(7,GF(2)) + sage: C1 = codes.QuadraticResidueCodeOddPair(7,GF(2))[0] + sage: C2 = codes.QuadraticResidueCode(7,GF(2)) sage: C1 == C2 True - sage: C1 = QuadraticResidueCodeOddPair(17,GF(2))[0] - sage: C2 = QuadraticResidueCode(17,GF(2)) + sage: C1 = codes.QuadraticResidueCodeOddPair(17,GF(2))[0] + sage: C2 = codes.QuadraticResidueCode(17,GF(2)) sage: C1 == C2 True @@ -1078,23 +1078,23 @@ def QuadraticResidueCodeEvenPair(n,F): EXAMPLES:: - sage: QuadraticResidueCodeEvenPair(17,GF(13)) + sage: codes.QuadraticResidueCodeEvenPair(17,GF(13)) (Linear code of length 17, dimension 8 over Finite Field of size 13, Linear code of length 17, dimension 8 over Finite Field of size 13) - sage: QuadraticResidueCodeEvenPair(17,GF(2)) + sage: codes.QuadraticResidueCodeEvenPair(17,GF(2)) (Linear code of length 17, dimension 8 over Finite Field of size 2, Linear code of length 17, dimension 8 over Finite Field of size 2) - sage: QuadraticResidueCodeEvenPair(13,GF(9,"z")) + sage: codes.QuadraticResidueCodeEvenPair(13,GF(9,"z")) (Linear code of length 13, dimension 6 over Finite Field in z of size 3^2, Linear code of length 13, dimension 6 over Finite Field in z of size 3^2) - sage: C1 = QuadraticResidueCodeEvenPair(7,GF(2))[0] + sage: C1 = codes.QuadraticResidueCodeEvenPair(7,GF(2))[0] sage: C1.is_self_orthogonal() True - sage: C2 = QuadraticResidueCodeEvenPair(7,GF(2))[1] + sage: C2 = codes.QuadraticResidueCodeEvenPair(7,GF(2))[1] sage: C2.is_self_orthogonal() True - sage: C3 = QuadraticResidueCodeOddPair(17,GF(2))[0] - sage: C4 = QuadraticResidueCodeEvenPair(17,GF(2))[1] + sage: C3 = codes.QuadraticResidueCodeOddPair(17,GF(2))[0] + sage: C4 = codes.QuadraticResidueCodeEvenPair(17,GF(2))[1] sage: C3 == C4.dual_code() True @@ -1124,25 +1124,25 @@ def QuadraticResidueCodeOddPair(n,F): EXAMPLES:: - sage: QuadraticResidueCodeOddPair(17,GF(13)) + sage: codes.QuadraticResidueCodeOddPair(17,GF(13)) (Linear code of length 17, dimension 9 over Finite Field of size 13, Linear code of length 17, dimension 9 over Finite Field of size 13) - sage: QuadraticResidueCodeOddPair(17,GF(2)) + sage: codes.QuadraticResidueCodeOddPair(17,GF(2)) (Linear code of length 17, dimension 9 over Finite Field of size 2, Linear code of length 17, dimension 9 over Finite Field of size 2) - sage: QuadraticResidueCodeOddPair(13,GF(9,"z")) + sage: codes.QuadraticResidueCodeOddPair(13,GF(9,"z")) (Linear code of length 13, dimension 7 over Finite Field in z of size 3^2, Linear code of length 13, dimension 7 over Finite Field in z of size 3^2) - sage: C1 = QuadraticResidueCodeOddPair(17,GF(2))[1] + sage: C1 = codes.QuadraticResidueCodeOddPair(17,GF(2))[1] sage: C1x = C1.extended_code() - sage: C2 = QuadraticResidueCodeOddPair(17,GF(2))[0] + sage: C2 = codes.QuadraticResidueCodeOddPair(17,GF(2))[0] sage: C2x = C2.extended_code() sage: C2x.spectrum(); C1x.spectrum() [1, 0, 0, 0, 0, 0, 102, 0, 153, 0, 153, 0, 102, 0, 0, 0, 0, 0, 1] [1, 0, 0, 0, 0, 0, 102, 0, 153, 0, 153, 0, 102, 0, 0, 0, 0, 0, 1] sage: C2x == C1x.dual_code() True - sage: C3 = QuadraticResidueCodeOddPair(7,GF(2))[0] + sage: C3 = codes.QuadraticResidueCodeOddPair(7,GF(2))[0] sage: C3x = C3.extended_code() sage: C3x.spectrum() [1, 0, 0, 0, 14, 0, 0, 0, 1] @@ -1176,10 +1176,10 @@ def RandomLinearCode(n,k,F): EXAMPLES:: - sage: C = RandomLinearCode(30,15,GF(2)) + sage: C = codes.RandomLinearCode(30,15,GF(2)) sage: C Linear code of length 30, dimension 15 over Finite Field of size 2 - sage: C = RandomLinearCode(10,5,GF(4,'a')) + sage: C = codes.RandomLinearCode(10,5,GF(4,'a')) sage: C Linear code of length 10, dimension 5 over Finite Field in a of size 2^2 @@ -1225,11 +1225,11 @@ def ReedSolomonCode(n,k,F,pts = None): EXAMPLES:: - sage: C = ReedSolomonCode(6,4,GF(7)); C + sage: C = codes.ReedSolomonCode(6,4,GF(7)); C Linear code of length 6, dimension 4 over Finite Field of size 7 sage: C.minimum_distance() 3 - sage: C = ReedSolomonCode(6,4,GF(8,"a")); C + sage: C = codes.ReedSolomonCode(6,4,GF(8,"a")); C Linear code of length 6, dimension 4 over Finite Field in a of size 2^3 sage: C.minimum_distance() 3 @@ -1239,7 +1239,7 @@ def ReedSolomonCode(n,k,F,pts = None): sage: pts = [0,1,a,a^2,2*a,2*a+1] sage: len(Set(pts)) == 6 # to make sure there are no duplicates True - sage: C = ReedSolomonCode(6,4,F,pts); C + sage: C = codes.ReedSolomonCode(6,4,F,pts); C Linear code of length 6, dimension 4 over Finite Field in a of size 3^2 sage: C.minimum_distance() 3 @@ -1276,7 +1276,7 @@ def TernaryGolayCode(): EXAMPLES:: - sage: C = TernaryGolayCode() + sage: C = codes.TernaryGolayCode() sage: C Linear code of length 11, dimension 6 over Finite Field of size 3 sage: C.minimum_distance() @@ -1334,17 +1334,17 @@ def ToricCode(P,F): EXAMPLES:: - sage: C = ToricCode([[0,0],[1,0],[2,0],[0,1],[1,1]],GF(7)) + sage: C = codes.ToricCode([[0,0],[1,0],[2,0],[0,1],[1,1]],GF(7)) sage: C Linear code of length 36, dimension 5 over Finite Field of size 7 sage: C.minimum_distance() 24 - sage: C = ToricCode([[-2,-2],[-1,-2],[-1,-1],[-1,0],[0,-1],[0,0],[0,1],[1,-1],[1,0]],GF(5)) + sage: C = codes.ToricCode([[-2,-2],[-1,-2],[-1,-1],[-1,0],[0,-1],[0,0],[0,1],[1,-1],[1,0]],GF(5)) sage: C Linear code of length 16, dimension 9 over Finite Field of size 5 sage: C.minimum_distance() 6 - sage: C = ToricCode([ [0,0],[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[3,1],[3,2],[4,1]],GF(8,"a")) + sage: C = codes.ToricCode([ [0,0],[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[3,1],[3,2],[4,1]],GF(8,"a")) sage: C Linear code of length 49, dimension 11 over Finite Field in a of size 2^3 @@ -1390,9 +1390,9 @@ def WalshCode(m): EXAMPLES:: - sage: C = WalshCode(4); C + sage: C = codes.WalshCode(4); C Linear code of length 16, dimension 4 over Finite Field of size 2 - sage: C = WalshCode(3); C + sage: C = codes.WalshCode(3); C Linear code of length 8, dimension 3 over Finite Field of size 2 sage: C.spectrum() [1, 0, 0, 0, 7, 0, 0, 0, 0] diff --git a/src/sage/coding/decoder.py b/src/sage/coding/decoder.py index a090f42ab85..ba18cd51c0d 100644 --- a/src/sage/coding/decoder.py +++ b/src/sage/coding/decoder.py @@ -30,7 +30,7 @@ def syndrome(C, v): syndrome of v (ie, the coset v+C, sorted by weight). EXAMPLES: - sage: C = HammingCode(2,GF(3)); C + sage: C = codes.HammingCode(2,GF(3)); C Linear code of length 4, dimension 2 over Finite Field of size 3 sage: V = VectorSpace(GF(3), 4) sage: v = V([0, 2, 0, 1]) @@ -54,7 +54,7 @@ def coset_leader(C, v): element of the syndrome of v of lowest weight. EXAMPLES: - sage: C = HammingCode(2,GF(3)); C + sage: C = codes.HammingCode(2,GF(3)); C Linear code of length 4, dimension 2 over Finite Field of size 3 sage: V = VectorSpace(GF(3), 4) sage: v = V([0, 2, 0, 1]) @@ -89,7 +89,7 @@ def decode(C, v, algorithm="syndrome"): a brute force search) and "syndrome". EXAMPLES: - sage: C = HammingCode(2,GF(3)) + sage: C = codes.HammingCode(2,GF(3)) sage: V = VectorSpace(GF(3), 4) sage: v = V([0, 2, 0, 1]) sage: v in C @@ -101,7 +101,7 @@ def decode(C, v, algorithm="syndrome"): True sage: c = decode(C, v, algorithm="nearest neighbor");c (0, 2, 2, 1) - sage: C = HammingCode(3,GF(3)); C + sage: C = codes.HammingCode(3,GF(3)); C Linear code of length 13, dimension 10 over Finite Field of size 3 sage: V = VectorSpace(GF(3), 13) sage: v = V([2]+[0]*12) diff --git a/src/sage/coding/guava.py b/src/sage/coding/guava.py index 6263647d5da..15b2bcd14cc 100644 --- a/src/sage/coding/guava.py +++ b/src/sage/coding/guava.py @@ -60,7 +60,7 @@ def BinaryReedMullerCode(r,k): EXAMPLE:: - sage: C = BinaryReedMullerCode(2,4); C # optional - gap_packages (Guava package) + sage: C = codes.BinaryReedMullerCode(2,4); C # optional - gap_packages (Guava package) Linear code of length 16, dimension 11 over Finite Field of size 2 sage: C.minimum_distance() # optional - gap_packages (Guava package) 4 @@ -107,7 +107,7 @@ def QuasiQuadraticResidueCode(p): EXAMPLES:: - sage: C = QuasiQuadraticResidueCode(11); C # optional - gap_packages (Guava package) + sage: C = codes.QuasiQuadraticResidueCode(11); C # optional - gap_packages (Guava package) Linear code of length 22, dimension 11 over Finite Field of size 2 REFERENCES: @@ -149,9 +149,9 @@ def RandomLinearCodeGuava(n,k,F): EXAMPLES:: - sage: C = RandomLinearCodeGuava(30,15,GF(2)); C # optional - gap_packages (Guava package) + sage: C = codes.RandomLinearCodeGuava(30,15,GF(2)); C # optional - gap_packages (Guava package) Linear code of length 30, dimension 15 over Finite Field of size 2 - sage: C = RandomLinearCodeGuava(10,5,GF(4,'a')); C # optional - gap_packages (Guava package) + sage: C = codes.RandomLinearCodeGuava(10,5,GF(4,'a')); C # optional - gap_packages (Guava package) Linear code of length 10, dimension 5 over Finite Field in a of size 2^2 AUTHOR: David Joyner (11-2005) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 55f73c09cc4..85d62712656 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -257,7 +257,7 @@ def code2leon(C): EXAMPLES:: - sage: C = HammingCode(3,GF(2)); C + sage: C = codes.HammingCode(3,GF(2)); C Linear code of length 7, dimension 4 over Finite Field of size 2 sage: file_loc = sage.coding.linear_code.code2leon(C) sage: f = open(file_loc); print f.read() @@ -813,7 +813,7 @@ def automorphism_group_binary_code(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: G = C.automorphism_group_binary_code(); G doctest:...: DeprecationWarning: This function is deprecated... Permutation Group with generators [(4,5)(6,7), (4,6)(5,7), (2,3)(6,7), (2,4)(3,5), (1,2)(5,6)] @@ -836,7 +836,7 @@ def __iter__(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: [list(c) for c in C if c.hamming_weight() < 4] [[0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1, 1], [0, 1, 0, 0, 1, 0, 1], [0, 0, 1, 0, 1, 1, 0], @@ -853,7 +853,7 @@ def ambient_space(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.ambient_space() Vector space of dimension 7 over Finite Field of size 2 """ @@ -920,7 +920,7 @@ def assmus_mattson_designs(self, t, mode=None): EXAMPLES:: - sage: C = ExtendedBinaryGolayCode() # example 1 + sage: C = codes.ExtendedBinaryGolayCode() # example 1 sage: C.assmus_mattson_designs(5) ['weights from C: ', [8, 12, 16, 24], @@ -982,7 +982,7 @@ def basis(self): EXAMPLES:: - sage: C = HammingCode(3, GF(2)) + sage: C = codes.HammingCode(3, GF(2)) sage: C.basis() [(1, 0, 0, 0, 0, 1, 1), (0, 1, 0, 0, 1, 0, 1), (0, 0, 1, 0, 1, 1, 0), (0, 0, 0, 1, 1, 1, 1)] """ @@ -1007,7 +1007,7 @@ def binomial_moment(self, i): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.binomial_moment(2) 0 sage: C.binomial_moment(4) # long time @@ -1052,7 +1052,7 @@ def __contains__(self,v): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: vector((1, 0, 0, 0, 0, 1, 1)) in C # indirect doctest True sage: vector((1, 0, 0, 0, 2, 1, 1)) in C # indirect doctest @@ -1070,7 +1070,7 @@ def characteristic(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.characteristic() 2 """ @@ -1083,7 +1083,7 @@ def characteristic_polynomial(self): EXAMPLES:: - sage: C = ExtendedBinaryGolayCode() + sage: C = codes.ExtendedBinaryGolayCode() sage: C.characteristic_polynomial() -4/3*x^3 + 64*x^2 - 2816/3*x + 4096 @@ -1108,10 +1108,10 @@ def chinen_polynomial(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.chinen_polynomial() # long time 1/5*(2*sqrt(2)*t^3 + 2*sqrt(2)*t^2 + 2*t^2 + sqrt(2)*t + 2*t + 1)/(sqrt(2) + 1) - sage: C = TernaryGolayCode() + sage: C = codes.TernaryGolayCode() sage: C.chinen_polynomial() # long time 1/7*(3*sqrt(3)*t^3 + 3*sqrt(3)*t^2 + 3*t^2 + sqrt(3)*t + 3*t + 1)/(sqrt(3) + 1) @@ -1172,7 +1172,7 @@ def __cmp__(self, right): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: MS = MatrixSpace(GF(2),4,7) sage: G = MS([1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]) sage: G @@ -1199,7 +1199,7 @@ def check_mat(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: Cperp = C.dual_code() sage: C; Cperp Linear code of length 7, dimension 4 over Finite Field of size 2 @@ -1243,7 +1243,7 @@ def covering_radius(self): EXAMPLES:: - sage: C = HammingCode(5,GF(2)) + sage: C = codes.HammingCode(5,GF(2)) sage: C.covering_radius() # optional - gap_packages (Guava package) 1 """ @@ -1279,7 +1279,7 @@ def decode(self, right, algorithm="syndrome"): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: MS = MatrixSpace(GF(2),1,7) sage: F = GF(2); a = F.gen() sage: v1 = [a,a,F(0),a,a,F(0),a] @@ -1297,12 +1297,12 @@ def decode(self, right, algorithm="syndrome"): (1, 1, 0, 1, 0, 0, 1) sage: c in C True - sage: C = HammingCode(2,GF(5)) + sage: C = codes.HammingCode(2,GF(5)) sage: v = vector(GF(5),[1,0,0,2,1,0]) sage: C.decode(v) (1, 0, 0, 2, 2, 0) sage: F. = GF(4) - sage: C = HammingCode(2,F) + sage: C = codes.HammingCode(2,F) sage: v = vector(F, [1,0,0,a,1]) sage: C.decode(v) (a + 1, 0, 0, a, 1) @@ -1337,10 +1337,10 @@ def divisor(self): EXAMPLES:: - sage: C = ExtendedBinaryGolayCode() + sage: C = codes.ExtendedBinaryGolayCode() sage: C.divisor() # Type II self-dual 4 - sage: C = QuadraticResidueCodeEvenPair(17,GF(2))[0] + sage: C = codes.QuadraticResidueCodeEvenPair(17,GF(2))[0] sage: C.divisor() 2 """ @@ -1366,10 +1366,10 @@ def dual_code(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.dual_code() Linear code of length 7, dimension 3 over Finite Field of size 2 - sage: C = HammingCode(3,GF(4,'a')) + sage: C = codes.HammingCode(3,GF(4,'a')) sage: C.dual_code() Linear code of length 21, dimension 3 over Finite Field in a of size 2^2 """ @@ -1408,7 +1408,7 @@ def direct_sum(self, other): EXAMPLES:: - sage: C1 = HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(3,GF(2)) sage: C2 = C1.direct_sum(C1); C2 Linear code of length 14, dimension 8 over Finite Field of size 2 sage: C3 = C1.direct_sum(C2); C3 @@ -1437,8 +1437,8 @@ def __eq__(self, right): EXAMPLES:: - sage: C1 = HammingCode(3,GF(2)) - sage: C2 = HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(3,GF(2)) + sage: C2 = codes.HammingCode(3,GF(2)) sage: C1 == C2 True sage: C2 = C1.extended_code() @@ -1480,7 +1480,7 @@ def extended_code(self): EXAMPLES:: - sage: C = HammingCode(3,GF(4,'a')) + sage: C = codes.HammingCode(3,GF(4,'a')) sage: C Linear code of length 21, dimension 18 over Finite Field in a of size 2^2 sage: Cx = C.extended_code() @@ -1504,7 +1504,7 @@ def galois_closure(self, F0): EXAMPLES:: - sage: C = HammingCode(3,GF(4,'a')) + sage: C = codes.HammingCode(3,GF(4,'a')) sage: Cc = C.galois_closure(GF(2)) sage: C; Cc Linear code of length 21, dimension 18 over Finite Field in a of size 2^2 @@ -1578,7 +1578,7 @@ def __getitem__(self, i): EXAMPLES:: - sage: RS = ReedSolomonCode(7, 3, GF(8, 'a')) + sage: RS = codes.ReedSolomonCode(7, 3, GF(8, 'a')) sage: RS[24] (0, a^2 + a, a^2 + a + 1, a^2 + 1, 1, a, a^2) sage: RS[24] == RS.list()[24] @@ -1638,13 +1638,13 @@ def gen_mat(self): EXAMPLES:: - sage: C1 = HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(3,GF(2)) sage: C1.gen_mat() [1 0 0 0 0 1 1] [0 1 0 0 1 0 1] [0 0 1 0 1 1 0] [0 0 0 1 1 1 1] - sage: C2 = HammingCode(2,GF(4,"a")) + sage: C2 = codes.HammingCode(2,GF(4,"a")) sage: C2.gen_mat() [ 1 0 0 a + 1 a] [ 0 1 0 1 1] @@ -1678,7 +1678,7 @@ def gens(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.gens() [(1, 0, 0, 0, 0, 1, 1), (0, 1, 0, 0, 1, 0, 1), (0, 0, 1, 0, 1, 1, 0), (0, 0, 0, 1, 1, 1, 1)] """ @@ -1690,11 +1690,11 @@ def genus(self): EXAMPLES:: - sage: C1 = HammingCode(3,GF(2)); C1 + sage: C1 = codes.HammingCode(3,GF(2)); C1 Linear code of length 7, dimension 4 over Finite Field of size 2 sage: C1.genus() 1 - sage: C2 = HammingCode(2,GF(4,"a")); C2 + sage: C2 = codes.HammingCode(2,GF(4,"a")); C2 Linear code of length 5, dimension 3 over Finite Field in a of size 2^2 sage: C2.genus() 0 @@ -1739,7 +1739,7 @@ def is_permutation_automorphism(self,g): EXAMPLES:: - sage: C = HammingCode(3,GF(3)) + sage: C = codes.HammingCode(3,GF(3)) sage: g = SymmetricGroup(13).random_element() sage: C.is_permutation_automorphism(g) 0 @@ -1779,16 +1779,16 @@ def is_permutation_equivalent(self,other,algorithm=None): sage: P. = PolynomialRing(GF(2),"x") sage: g = x^3+x+1 - sage: C1 = CyclicCodeFromGeneratingPolynomial(7,g); C1 + sage: C1 = codes.CyclicCodeFromGeneratingPolynomial(7,g); C1 Linear code of length 7, dimension 4 over Finite Field of size 2 - sage: C2 = HammingCode(3,GF(2)); C2 + sage: C2 = codes.HammingCode(3,GF(2)); C2 Linear code of length 7, dimension 4 over Finite Field of size 2 sage: C1.is_permutation_equivalent(C2) True sage: C1.is_permutation_equivalent(C2,algorithm="verbose") (True, (3,4)(5,7,6)) - sage: C1 = RandomLinearCode(10,5,GF(2)) - sage: C2 = RandomLinearCode(10,5,GF(3)) + sage: C1 = codes.RandomLinearCode(10,5,GF(2)) + sage: C2 = codes.RandomLinearCode(10,5,GF(3)) sage: C1.is_permutation_equivalent(C2) False """ @@ -1822,10 +1822,10 @@ def is_self_dual(self): EXAMPLES:: - sage: C = ExtendedBinaryGolayCode() + sage: C = codes.ExtendedBinaryGolayCode() sage: C.is_self_dual() True - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.is_self_dual() False """ @@ -1841,10 +1841,10 @@ def is_self_orthogonal(self): EXAMPLES:: - sage: C = ExtendedBinaryGolayCode() + sage: C = codes.ExtendedBinaryGolayCode() sage: C.is_self_orthogonal() True - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.is_self_orthogonal() False sage: C = QuasiQuadraticResidueCode(11) # optional - gap_packages (Guava package) @@ -1859,7 +1859,7 @@ def is_galois_closed(self): EXAMPLES:: - sage: C = HammingCode(3,GF(4,"a")) + sage: C = codes.HammingCode(3,GF(4,"a")) sage: C.is_galois_closed() False """ @@ -1873,7 +1873,7 @@ def is_subcode(self, other): EXAMPLES:: - sage: C1 = HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(3,GF(2)) sage: G1 = C1.gen_mat() sage: G2 = G1.matrix_from_rows([0,1,2]) sage: C2 = LinearCode(G2) @@ -1890,7 +1890,7 @@ def is_subcode(self, other): sage: C5 = C1.shortened([1]) sage: C5.is_subcode(C1) False - sage: C1 = HammingCode(3,GF(9,"z")) + sage: C1 = codes.HammingCode(3,GF(9,"z")) sage: G1 = C1.gen_mat() sage: G2 = G1.matrix_from_rows([0,1,2]) sage: C2 = LinearCode(G2) @@ -1909,7 +1909,7 @@ def __len__(self): EXAMPLES:: - sage: C = HammingCode(3, GF(2)) + sage: C = codes.HammingCode(3, GF(2)) sage: len(C) 16 """ @@ -1921,7 +1921,7 @@ def length(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.length() 7 """ @@ -1933,7 +1933,7 @@ def list(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: Clist = C.list() sage: Clist[5]; Clist[5] in C (1, 0, 1, 0, 1, 0, 1) @@ -1947,7 +1947,7 @@ def _magma_init_(self, magma): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: Cm = magma(C) # optional - magma, indirect doctest sage: Cm.MinimumWeight() # optional - magma 3 @@ -2010,14 +2010,14 @@ def minimum_distance(self, algorithm=None): Another example.:: - sage: C = HammingCode(2,GF(4,"a")); C + sage: C = codes.HammingCode(2,GF(4,"a")); C Linear code of length 5, dimension 3 over Finite Field in a of size 2^2 sage: C.minimum_distance() 3 TESTS:: - sage: C = HammingCode(2,GF(4,"a")) + sage: C = codes.HammingCode(2,GF(4,"a")) sage: C.minimum_distance(algorithm='something') Traceback (most recent call last): ... @@ -2136,7 +2136,7 @@ def permutation_automorphism_group(self, algorithm="partition"): :: - sage: C = ExtendedTernaryGolayCode() + sage: C = codes.ExtendedTernaryGolayCode() sage: M11 = MathieuGroup(11) sage: M11.order() 7920 @@ -2146,24 +2146,24 @@ def permutation_automorphism_group(self, algorithm="partition"): Other examples:: - sage: C = ExtendedBinaryGolayCode() + sage: C = codes.ExtendedBinaryGolayCode() sage: G = C.permutation_automorphism_group() sage: G.order() 244823040 - sage: C = HammingCode(5, GF(2)) + sage: C = codes.HammingCode(5, GF(2)) sage: G = C.permutation_automorphism_group() sage: G.order() 9999360 - sage: C = HammingCode(2,GF(3)); C + sage: C = codes.HammingCode(2,GF(3)); C Linear code of length 4, dimension 2 over Finite Field of size 3 sage: C.permutation_automorphism_group(algorithm="partition") Permutation Group with generators [(1,3,4)] - sage: C = HammingCode(2,GF(4,"z")); C + sage: C = codes.HammingCode(2,GF(4,"z")); C Linear code of length 5, dimension 3 over Finite Field in z of size 2^2 sage: C.permutation_automorphism_group(algorithm="partition") Permutation Group with generators [(1,3)(4,5), (1,4)(3,5)] sage: C.permutation_automorphism_group(algorithm="gap") # optional - gap_packages (Guava package) - sage: C = TernaryGolayCode() + sage: C = codes.TernaryGolayCode() sage: C.permutation_automorphism_group(algorithm="gap") # optional - gap_packages (Guava package) Permutation Group with generators [(3,4)(5,7)(6,9)(8,11), (3,5,8)(4,11,7)(6,9,10), (2,3)(4,6)(5,8)(7,10), (1,2)(4,11)(5,8)(9,10)] @@ -2259,7 +2259,7 @@ def permuted_code(self, p): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: G = C.permutation_automorphism_group(); G Permutation Group with generators [(4,5)(6,7), (4,6)(5,7), (2,3)(6,7), (2,4)(3,5), (1,2)(5,6)] sage: g = G("(2,3)(6,7)") @@ -2304,7 +2304,7 @@ def punctured(self, L): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.punctured([1,2]) Linear code of length 5, dimension 4 over Finite Field of size 2 """ @@ -2327,7 +2327,7 @@ def random_element(self, *args, **kwds): EXAMPLES:: - sage: C = HammingCode(3,GF(4,'a')) + sage: C = codes.HammingCode(3,GF(4,'a')) sage: C.random_element() # random test (1, 0, 0, a + 1, 1, a, a, a + 1, a + 1, 1, 1, 0, a + 1, a, 0, a, a, 0, a, a, 1) @@ -2354,7 +2354,7 @@ def redundancy_matrix(C): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.gen_mat() [1 0 0 0 0 1 1] [0 1 0 0 1 0 1] @@ -2370,7 +2370,7 @@ def redundancy_matrix(C): [0 1 0 0 1 0 1] [0 0 1 0 1 1 0] [0 0 0 1 1 1 1] - sage: C = HammingCode(2,GF(3)) + sage: C = codes.HammingCode(2,GF(3)) sage: C.gen_mat() [1 0 1 1] [0 1 1 2] @@ -2452,7 +2452,7 @@ def sd_duursma_q(C,i,d0): EXAMPLES:: - sage: C1 = HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(3,GF(2)) sage: C2 = C1.extended_code(); C2 Linear code of length 8, dimension 4 over Finite Field of size 2 sage: C2.is_self_dual() @@ -2519,7 +2519,7 @@ def sd_zeta_polynomial(C, typ=1): EXAMPLES:: - sage: C1 = HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(3,GF(2)) sage: C2 = C1.extended_code(); C2 Linear code of length 8, dimension 4 over Finite Field of size 2 sage: C2.is_self_dual() @@ -2582,7 +2582,7 @@ def shortened(self, L): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.shortened([1,2]) Linear code of length 5, dimension 2 over Finite Field of size 2 """ @@ -2621,11 +2621,11 @@ def spectrum(self, algorithm=None): sage: C.spectrum() [1, 0, 0, 7, 7, 0, 0, 1] sage: F. = GF(2^2,"z") - sage: C = HammingCode(2, F); C + sage: C = codes.HammingCode(2, F); C Linear code of length 5, dimension 3 over Finite Field in z of size 2^2 sage: C.spectrum() [1, 0, 0, 30, 15, 18] - sage: C = HammingCode(3,GF(2)); C + sage: C = codes.HammingCode(3,GF(2)); C Linear code of length 7, dimension 4 over Finite Field of size 2 sage: C.spectrum(algorithm="leon") # optional - gap_packages (Guava package) [1, 0, 0, 7, 7, 0, 0, 1] @@ -2633,15 +2633,15 @@ def spectrum(self, algorithm=None): [1, 0, 0, 7, 7, 0, 0, 1] sage: C.spectrum(algorithm="binary") [1, 0, 0, 7, 7, 0, 0, 1] - sage: C = HammingCode(3,GF(3)); C + sage: C = codes.HammingCode(3,GF(3)); C Linear code of length 13, dimension 10 over Finite Field of size 3 sage: C.spectrum() == C.spectrum(algorithm="leon") # optional - gap_packages (Guava package) True - sage: C = HammingCode(2,GF(5)); C + sage: C = codes.HammingCode(2,GF(5)); C Linear code of length 6, dimension 4 over Finite Field of size 5 sage: C.spectrum() == C.spectrum(algorithm="leon") # optional - gap_packages (Guava package) True - sage: C = HammingCode(2,GF(7)); C + sage: C = codes.HammingCode(2,GF(7)); C Linear code of length 8, dimension 6 over Finite Field of size 7 sage: C.spectrum() == C.spectrum(algorithm="leon") # optional - gap_packages (Guava package) True @@ -2706,7 +2706,7 @@ def standard_form(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.gen_mat() [1 0 0 0 0 1 1] [0 1 0 0 1 0 1] @@ -2759,7 +2759,7 @@ def support(self): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.spectrum() [1, 0, 0, 7, 7, 0, 0, 1] sage: C.support() @@ -2790,7 +2790,7 @@ def weight_enumerator(self, names="xy", name2=None): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.weight_enumerator() x^7 + 7*x^4*y^3 + 7*x^3*y^4 + y^7 sage: C.weight_enumerator(names="st") @@ -2831,7 +2831,7 @@ def zeta_polynomial(self, name="T"): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.zeta_polynomial() 2/5*T^2 + 2/5*T + 1/5 sage: C = best_known_linear_code(6,3,GF(2)) # optional - gap_packages (Guava package) @@ -2839,7 +2839,7 @@ def zeta_polynomial(self, name="T"): 3 sage: C.zeta_polynomial() # optional - gap_packages (Guava package) 2/5*T^2 + 2/5*T + 1/5 - sage: C = HammingCode(4,GF(2)) + sage: C = codes.HammingCode(4,GF(2)) sage: C.zeta_polynomial() 16/429*T^6 + 16/143*T^5 + 80/429*T^4 + 32/143*T^3 + 30/143*T^2 + 2/13*T + 1/13 sage: F. = GF(4,"z") @@ -2897,7 +2897,7 @@ def zeta_function(self, name="T"): EXAMPLES:: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: C.zeta_function() (2/5*T^2 + 2/5*T + 1/5)/(2*T^2 - 3*T + 1) """ From 7c519097cd92fe3077de27bb2db902e211b74a54 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 30 Nov 2013 18:28:11 -0800 Subject: [PATCH 099/206] Some review changes -- code tweaks and doc formatting. --- src/sage/categories/category_types.py | 4 +- src/sage/homology/cell_complex.py | 30 ++- src/sage/homology/chain_complex.py | 246 +++++++++++--------- src/sage/homology/chain_complex_morphism.py | 43 ++-- src/sage/homology/homology_group.py | 26 ++- src/sage/homology/matrix_utils.py | 17 +- src/sage/homology/simplicial_complex.py | 3 +- 7 files changed, 197 insertions(+), 172 deletions(-) diff --git a/src/sage/categories/category_types.py b/src/sage/categories/category_types.py index 700f8d4b3ed..e13de519bb3 100644 --- a/src/sage/categories/category_types.py +++ b/src/sage/categories/category_types.py @@ -454,5 +454,5 @@ def super_categories(self): base_ring = self.base_ring() if base_ring in Fields(): return [VectorSpaces(base_ring)] - else: - return [FreeModules(base_ring)] + return [FreeModules(base_ring)] + diff --git a/src/sage/homology/cell_complex.py b/src/sage/homology/cell_complex.py index d736837a057..162a2037ab5 100644 --- a/src/sage/homology/cell_complex.py +++ b/src/sage/homology/cell_complex.py @@ -20,8 +20,8 @@ the :meth:`~GenericCellComplex.homology` method get passed on to the :meth:`~GenericCellComplex.chain_complex` method and also to the constructor for chain complexes in - :class:`sage.homology.chain_complex.ChainComplex_class `, as well its - associated + :class:`sage.homology.chain_complex.ChainComplex_class `, + as well as its associated :meth:`~sage.homology.chain_complex.ChainComplex_class.homology` method. This means that those keywords should have consistent meaning in all of those situations. It also means that it is easy to @@ -350,21 +350,21 @@ def chain_complex(self, **kwds): Some keywords to possibly implement in a derived class: - - ``subcomplex`` - a subcomplex: compute the relative chain complex - - ``augmented`` - a bool: whether to return the augmented complex - - ``verbose`` - a bool: whether to print informational messages as + - ``subcomplex`` -- a subcomplex: compute the relative chain complex + - ``augmented`` -- a bool: whether to return the augmented complex + - ``verbose`` -- a bool: whether to print informational messages as the chain complex is being computed - - ``check_diffs`` - a bool: whether to check that the each + - ``check_diffs`` -- a bool: whether to check that the each composite of two consecutive differentials is zero - - ``dimensions`` - if None, compute the chain complex in all + - ``dimensions`` -- if ``None``, compute the chain complex in all dimensions. If a list or tuple of integers, compute the chain complex in those dimensions, setting the chain groups in all other dimensions to zero. Definitely implement the following: - - ``base_ring`` - commutative ring (optional, default ZZ) - - ``cochain`` - a bool: whether to return the cochain complex + - ``base_ring`` -- commutative ring (optional, default ZZ) + - ``cochain`` -- a bool: whether to return the cochain complex EXAMPLES:: @@ -518,15 +518,14 @@ def homology(self, dim=None, **kwds): elif isinstance(self, SimplicialComplex): if have_chomp('homsimpl'): H = homsimpl(self, subcomplex, **kwds) + # now pick off the requested dimensions if H: answer = {} if not dims: - for d in range(self.dimension() + 1): - answer[d] = H.get(d, HomologyGroup(0, base_ring)) - else: - for d in dims: - answer[d] = H.get(d, HomologyGroup(0, base_ring)) + dims =range(self.dimension() + 1) + for d in dims: + answer[d] = H.get(d, HomologyGroup(0, base_ring)) if dim is not None: if not isinstance(dim, (list, tuple)): answer = answer.get(dim, HomologyGroup(0, base_ring)) @@ -545,8 +544,7 @@ def homology(self, dim=None, **kwds): zero = HomologyGroup(0, base_ring) if isinstance(dim, (list, tuple)): return dict([d, answer.get(d, zero)] for d in dim) - else: - return answer.get(dim, zero) + return answer.get(dim, zero) def cohomology(self, dim=None, **kwds): r""" diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 71171f7bf8d..e9f10ec976e 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -32,7 +32,8 @@ differentials may increase degree by 1 or decrease it, or indeed change it by any fixed amount: this is controlled by the ``degree_of_differential`` parameter used in defining the chain -complex. """ +complex. +""" ######################################################################## @@ -88,8 +89,7 @@ def _latex_module(R, m): """ if m == 0: return str(latex(0)) - else: - return str(latex(FreeModule(R, m))) + return str(latex(FreeModule(R, m))) @rename_keyword(deprecation=15151, check_products='check', check_diffs='check') @@ -99,18 +99,18 @@ def ChainComplex(data=None, **kwds): INPUT: - - ``data`` -- the data defining the chain complex; see below for - more details. + - ``data`` -- the data defining the chain complex; see below for + more details. The following keyword arguments are supported: - - ``base_ring`` -- a commutative ring (optional), the ring over - which the chain complex is defined. If this is not specified, - it is determined by the data defining the chain complex. + - ``base_ring`` -- a commutative ring (optional), the ring over + which the chain complex is defined. If this is not specified, + it is determined by the data defining the chain complex. - ``grading_group`` -- a additive free abelian group (optional, - default ``ZZ``), the group over which the chain complex is - indexed. + default ``ZZ``), the group over which the chain complex is + indexed. - ``degree_of_differential`` -- element of grading_group (optional, default ``1``). The degree of the differential. @@ -118,10 +118,12 @@ def ChainComplex(data=None, **kwds): - ``degree`` -- alias for ``degree_of_differential``. - ``check`` -- boolean (optional, default ``True``). If ``True``, - check that each consecutive pair of differentials are - composable and have composite equal to zero. + check that each consecutive pair of differentials are + composable and have composite equal to zero. - OUTPUT: a chain complex + OUTPUT: + + A chain complex. .. WARNING:: @@ -144,11 +146,11 @@ def ChainComplex(data=None, **kwds): is `\ZZ` and ``degree`` is 1. 3. a list/tuple/iterable of the form `[r_0, d_0, r_1, d_1, r_2, - d_2, ...]`, where `r_i` is the rank of the free module `C_i` + d_2, \ldots]`, where `r_i` is the rank of the free module `C_i` and each `d_i` is a matrix, as above. This only makes sense if ``grading_group`` is `\ZZ` and ``degree`` is 1. - 4. a list/tuple/iterable of the form `[d_0, d_1, d_2, ...]` where + 4. a list/tuple/iterable of the form `[d_0, d_1, d_2, \ldots]` where each `d_i` is a matrix, as above. This only makes sense if ``grading_group`` is `\ZZ` and ``degree`` is 1. @@ -307,11 +309,11 @@ def ChainComplex(data=None, **kwds): try: prod = mat1 * mat0 except TypeError: - raise TypeError('the differentials d_{%s} and d_{%s} are not compatible: ' - 'their product is not defined' % (n, n+degree)) + raise TypeError('the differentials d_{{{}}} and d_{{{}}} are not compatible: ' + 'their product is not defined'.format(n, n+degree)) if not prod.is_zero(): - raise ValueError('the differentials d_{%s} and d_{%s} are not compatible: ' - 'their composition is not zero.' % (n, n+degree)) + raise ValueError('the differentials d_{{{}}} and d_{{{}}} are not compatible: ' + 'their composition is not zero.'.format(n, n+degree)) return ChainComplex_class(grading_group, degree, base_ring, data_dict) @@ -326,7 +328,7 @@ def __init__(self, parent, vectors, check=True): of the chain complex `(C_n, d_n)`. There is no restriction on how the differentials `d_n` act on the elements of the chain. - .. note: + .. NOTE:: You must use the chain complex to construct chains. @@ -389,12 +391,13 @@ def _repr_(self): n = len(self._vec) if n == 0: return 'Trivial chain' - elif n == 1: + + if n == 1: deg, vec = self._vec.iteritems().next() return 'Chain({0}:{1})'.format(deg, vec) - else: - return 'Chain with {0} nonzero terms over {1}'.format( - n, self.parent().base_ring()) + + return 'Chain with {0} nonzero terms over {1}'.format( + n, self.parent().base_ring()) def _ascii_art_(self): """ @@ -564,32 +567,34 @@ def __cmp__(self, other): class ChainComplex_class(Parent): + r""" + See :func:`ChainComplex` for full documentation. - def __init__(self, grading_group, degree_of_differential, base_ring, differentials): - r""" - See :func:`ChainComplex` for full documentation. - - The differentials are required to be in the following canonical form: - - * All differentials that are not `0\times 0` must be specified - (even if they have zero rows or zero columns), and + The differentials are required to be in the following canonical form: + + * All differentials that are not `0\times 0` must be specified + (even if they have zero rows or zero columns), and - * Differentials that are `0\times 0` must not be specified. - - * Immutable matrices over the `base_ring` + * Differentials that are `0 \times 0` must not be specified. + + * Immutable matrices over the ``base_ring`` - This and more is ensured by the assertions in the - constructor. The :func:`ChainComplex` factory function must - ensure that only valid input is passed. + This and more is ensured by the assertions in the + constructor. The :func:`ChainComplex` factory function must + ensure that only valid input is passed. - EXAMPLES:: + EXAMPLES:: - sage: C = ChainComplex(); C - Trivial chain complex over Integer Ring + sage: C = ChainComplex(); C + Trivial chain complex over Integer Ring - sage: D = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) - sage: D - Chain complex with at most 2 nonzero terms over Integer Ring + sage: D = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: D + Chain complex with at most 2 nonzero terms over Integer Ring + """ + def __init__(self, grading_group, degree_of_differential, base_ring, differentials): + """ + Initialize ``self``. TESTS:: @@ -599,16 +604,21 @@ def __init__(self, grading_group, degree_of_differential, base_ring, differentia sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) sage: TestSuite(C).run() """ - assert all(d.base_ring() == base_ring and d.is_immutable() and - (d.ncols(), d.nrows()) != (0, 0) - for d in differentials.values()) - assert degree_of_differential.parent() is grading_group - assert grading_group is ZZ or not grading_group.is_multiplicative() + if any(d.base_ring() != base_ring or not d.is_immutable() or + (d.ncols(), d.nrows()) == (0, 0) + for d in differentials.values()): + raise ValueError('invalid differentials') + if degree_of_differential.parent() is not grading_group: + raise ValueError('the degree_of_differential.parent() must be grading_group') + if grading_group is not ZZ and grading_group.is_multiplicative(): + raise ValueError('grading_group must be either ZZ or multiplicative') # all differentials (excluding the 0x0 ones) must be specified to the constructor - assert all(dim+degree_of_differential in differentials or d.nrows() == 0 - for dim, d in differentials.iteritems()) - assert all(dim-degree_of_differential in differentials or d.ncols() == 0 - for dim, d in differentials.iteritems()) + if any(dim+degree_of_differential not in differentials and d.nrows() != 0 + for dim, d in differentials.iteritems()): + raise ValueError('invalid differentials') + if any(dim-degree_of_differential not in differentials and d.ncols() != 0 + for dim, d in differentials.iteritems()): + raise ValueError('invalid differentials') self._grading_group = grading_group self._degree_of_differential = degree_of_differential self._diff = differentials @@ -681,10 +691,10 @@ def rank(self, degree, ring=None): - ``degree`` -- an element `\delta` of the grading group. Which differential `d_{\delta}` we want to know the - rank of. + rank of - - ``ring`` -- a commutative ring `S` or ``None`` (default). If - specified, the rank is computed after changing to this ring. + - ``ring`` -- (optional) a commutative ring `S`; + if specified, the rank is computed after changing to this ring OUTPUT: @@ -710,12 +720,11 @@ def rank(self, degree, ring=None): return ZZ.zero() if ring is None: return d.rank() - else: - return d.change_ring(ring).rank() + return d.change_ring(ring).rank() def grading_group(self): r""" - Return the grading group + Return the grading group. OUTPUT: @@ -764,11 +773,11 @@ def ordered_degrees(self, start=None, exclude_first=False): INPUT: - - ``start`` -- a degree (element of the grading group) or - ``None`` (default). + - ``start`` -- (default: ``None``) a degree (element of the grading + group) or ``None`` - ``exclude_first`` -- boolean (optional; default: - ``False``). Whether to exclude the lowest degree. This is a + ``False``); whether to exclude the lowest degree -- this is a handy way to just get the degrees of the non-zero modules, as the domain of the first differential is zero. @@ -777,9 +786,9 @@ def ordered_degrees(self, start=None, exclude_first=False): If ``start`` has been specified, the longest tuple of degrees * containing ``start`` (unless ``start`` would be the first - and ``exclude_first=True``) + and ``exclude_first=True``), - * in ascending order relative to :meth:`degree_of_differential` + * in ascending order relative to :meth:`degree_of_differential`, and * such that none of the corresponding differentials are `0\times 0`. @@ -814,18 +823,22 @@ def ordered_degrees(self, start=None, exclude_first=False): result.append(ordered) result.sort() return tuple(result) + import collections deg = start result = collections.deque() result.append(start) + next_deg = start + self.degree_of_differential() while next_deg in self._diff: result.append(next_deg) next_deg += self.degree_of_differential() + prev_deg = start - self.degree_of_differential() while prev_deg in self._diff: result.appendleft(prev_deg) prev_deg -= self.degree_of_differential() + if exclude_first: result.popleft() return tuple(result) @@ -853,12 +866,14 @@ def differential(self, dim=None): INPUT: - ``dim`` -- element of the grading group (optional, default - ``None``). If this is ``None``, return a dictionary of all - of the differentials. If this is a single element, return - the differential starting in that dimension. + ``None``); if this is ``None``, return a dictionary of all + of the differentials, or if this is a single element, return + the differential starting in that dimension - OUTPUT: either a dictionary of all of the differentials or a single - differential (i.e., a matrix) + OUTPUT: + + Either a dictionary of all of the differentials or a single + differential (i.e., a matrix). EXAMPLES:: @@ -871,7 +886,8 @@ def differential(self, dim=None): [0 2] sage: C = ChainComplex({0: identity_matrix(ZZ, 40)}) sage: C.differential() - {0: 40 x 40 dense matrix over Integer Ring, 1: [], -1: 40 x 0 dense matrix over Integer Ring} + {0: 40 x 40 dense matrix over Integer Ring, 1: [], + -1: 40 x 0 dense matrix over Integer Ring} """ if dim is None: return copy(self._diff) @@ -891,8 +907,7 @@ def dual(self): dual in dimension `n` is isomorphic to the original chain complex in dimension `n`, and the corresponding boundary matrix is the transpose of the matrix in the original complex. - This converts a chain complex to a cochain complex and vice - versa. + This converts a chain complex to a cochain complex and vice versa. EXAMPLES:: @@ -921,7 +936,7 @@ def free_module_rank(self, degree): INPUT: - - ``degree`` -- an element of the grading group. + - ``degree`` -- an element of the grading group OUTPUT: @@ -945,7 +960,7 @@ def free_module(self, degree=None): INPUT: - - ``degree`` -- an element of the grading group or ``None`` (default). + - ``degree`` -- an element of the grading group or ``None`` (default). OUTPUT: @@ -1005,7 +1020,7 @@ def __cmp__(self, other): def _homology_chomp(deg, base_ring, verbose, generators): """ - Helper function for :meth:`homology` + Helper function for :meth:`homology`. EXAMPLES:: @@ -1032,25 +1047,32 @@ def homology(self, deg=None, **kwds): INPUT: - ``deg`` -- an element of the grading group for the chain - complex (optional, default ``None``): the degree in which - to compute homology. If this is ``None``, return the - homology in every degree in which the chain complex is - possibly nonzero. + complex (default: ``None``); the degree in which + to compute homology -- if this is ``None``, return the + homology in every degree in which the chain complex is + possibly nonzero + + - ``base_ring`` -- a commutative ring (optional, default is the + base ring for the chain complex); must be either the + integers `\ZZ` or a field + + - ``generators`` -- boolean (optional, default ``False``); if + ``True``, return generators for the homology groups along with + the groups. See :trac:`6100` - - ``base_ring`` -- a commutative ring (optional, default is the - base ring for the chain complex). Must be either the - integers `\ZZ` or a field. + - ``verbose`` - boolean (optional, default ``False``); if + ``True``, print some messages as the homology is computed - - ``generators`` -- boolean (optional, default ``False``). If - ``True``, return generators for the homology groups along with - the groups. See :trac:`6100`. + - ``algorithm`` - string (optional, default ``'auto'``); the + options are: - - ``verbose`` - boolean (optional, default ``False``). If - ``True``, print some messages as the homology is computed. + * ``'auto'`` + * ``'chomp'`` + * ``'dhsw'`` + * ``'pari'`` + * ``'no_chomp'`` - - ``algorithm`` - string (optional, default ``'auto'``). The - options are ``'auto'``, ``'chomp'``, ``'dhsw'``, ``'pari'`` - or ``'no_chomp'``. See below for descriptions. + see below for descriptions OUTPUT: @@ -1249,7 +1271,7 @@ def change_ring(X): def _homology_generators_snf(self, d_in, d_out, d_out_rank): """ - Compute the homology generators using Smith normal form + Compute the homology generators using the Smith normal form. EXAMPLES:: @@ -1292,19 +1314,19 @@ def betti(self, deg=None, base_ring=None): INPUT: - ``deg`` -- an element of the grading group for the chain - complex or None (optional, default ``None``). If ``None``, - then return every Betti number, as a dictionary indexed by - degree. If an element of the grading group, then return - the Betti number in that degree. + complex or None (default ``None``); if ``None``, + then return every Betti number, as a dictionary indexed by + degree, or if an element of the grading group, then return + the Betti number in that degree - - ``base_ring`` -- a commutative ring (optional, default is the - base ring for the chain complex). Compute homology with - these coefficients. Must be either the integers or a - field. + - ``base_ring`` -- a commutative ring (optional, default is the + base ring for the chain complex); compute homology with + these coefficients -- must be either the integers or a + field OUTPUT: - The Betti number in degree ``deg`` - the rank of the free + The Betti number in degree ``deg`` -- the rank of the free part of the homology module in this degree. EXAMPLES:: @@ -1336,11 +1358,11 @@ def torsion_list(self, max_prime, min_prime=2): INPUT: - - ``max_prime`` -- prime number: search for torsion mod `p` for - all `p` strictly less than this number. + - ``max_prime`` -- prime number; search for torsion mod `p` for + all `p` strictly less than this number - - ``min_prime`` -- prime (optional, default 2): search for - torsion mod `p` for primes at least as big as this. + - ``min_prime`` -- prime (optional, default 2); search for + torsion mod `p` for primes at least as big as this Return a list of pairs `(p, d)` where `p` is a prime at which there is torsion and `d` is a list of dimensions in which this @@ -1349,7 +1371,9 @@ def torsion_list(self, max_prime, min_prime=2): The base ring for the chain complex must be the integers; if not, an error is raised. - Algorithm: let `C` denote the chain complex. Let `P` equal + ALGORITHM: + + let `C` denote the chain complex. Let `P` equal ``max_prime``. Compute the mod `P` homology of `C`, and use this as the base-line computation: the assumption is that this is isomorphic to the integral homology tensored with @@ -1515,8 +1539,8 @@ def _repr_(self): EXAMPLES:: sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) - sage: C._repr_() - 'Chain complex with at most 2 nonzero terms over Integer Ring' + sage: C + Chain complex with at most 2 nonzero terms over Integer Ring """ diffs = filter(lambda mat: mat.nrows() + mat.ncols() > 0, self._diff.values()) @@ -1538,15 +1562,15 @@ def _ascii_art_(self): sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0]), 1:zero_matrix(1,2)}) sage: ascii_art(C) - [3 0 0] - [0 0] [0 0 0] + [3 0 0] + [0 0] [0 0 0] 0 <-- C_2 <------ C_1 <-------- C_0 <-- 0 sage: one = matrix(ZZ, [[1]]) sage: D = ChainComplex({0: one, 2: one, 6:one}) sage: ascii_art(D) - [1] [1] [0] [1] - 0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0 + [1] [1] [0] [1] + 0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0 """ from sage.misc.ascii_art import AsciiArt diff --git a/src/sage/homology/chain_complex_morphism.py b/src/sage/homology/chain_complex_morphism.py index a262ee29916..d9f62a50182 100644 --- a/src/sage/homology/chain_complex_morphism.py +++ b/src/sage/homology/chain_complex_morphism.py @@ -121,7 +121,7 @@ def __init__(self, matrices, C, D, check=True): terms over Integer Ring to Chain complex with at most 1 nonzero terms over Integer Ring """ - if not C.base_ring()==D.base_ring(): + if not C.base_ring() == D.base_ring(): raise NotImplementedError('morphisms between chain complexes of different' ' base rings are not implemented') d = C.degree_of_differential() @@ -133,9 +133,6 @@ def __init__(self, matrices, C, D, check=True): initial_matrices = dict(matrices) matrices = dict() for i in degrees: - if i - d not in degrees: - assert C.free_module_rank(i) == D.free_module_rank(i) == 0 - continue try: matrices[i] = initial_matrices.pop(i) except KeyError: @@ -144,14 +141,17 @@ def __init__(self, matrices, C, D, check=True): C.differential(i).ncols(), sparse=True) if check: # all remaining matrices given must be 0x0 - assert all(m.ncols() == m.nrows() == 0 for m in initial_matrices.values()) + if not all(m.ncols() == m.nrows() == 0 for m in initial_matrices.values()): + raise ValueError('the remaining matrices are not empty') # check commutativity for i in degrees: if i - d not in degrees: - assert C.free_module_rank(i) == D.free_module_rank(i) == 0 + if not (C.free_module_rank(i) == D.free_module_rank(i) == 0): + raise ValueError('{} and {} are not rank 0 in degree {}'.format(C, D, i)) continue if i + d not in degrees: - assert C.free_module_rank(i+d) == D.free_module_rank(i+d) == 0 + if not (C.free_module_rank(i+d) == D.free_module_rank(i+d) == 0): + raise ValueError('{} and {} are not rank 0 in degree {}'.format(C, D, i+d)) continue Dm = D.differential(i) * matrices[i] mC = matrices[i+d] * C.differential(i) @@ -192,7 +192,7 @@ def __neg__(self): f = dict() for i in self._matrix_dictionary.keys(): f[i] = -self._matrix_dictionary[i] - return ChainComplexMorphism(f,self._domain,self._codomain) + return ChainComplexMorphism(f, self._domain, self._codomain) def __add__(self,x): """ @@ -223,15 +223,15 @@ def __add__(self,x): """ if not isinstance(x,ChainComplexMorphism) or self._codomain != x._codomain or self._domain != x._domain or self._matrix_dictionary.keys() != x._matrix_dictionary.keys(): - raise TypeError, "Unsupported operation." + raise TypeError("Unsupported operation.") f = dict() for i in self._matrix_dictionary.keys(): f[i] = self._matrix_dictionary[i] + x._matrix_dictionary[i] - return ChainComplexMorphism(f,self._domain,self._codomain) + return ChainComplexMorphism(f, self._domain, self._codomain) def __mul__(self,x): """ - Returns ``self * x`` if ``self`` and ``x`` are composable morphisms + Return ``self * x`` if ``self`` and ``x`` are composable morphisms or if ``x`` is an element of the base ring. EXAMPLES:: @@ -278,7 +278,7 @@ def __mul__(self,x): try: y = self._domain.base_ring()(x) except TypeError: - raise TypeError, "Multiplication is not defined." + raise TypeError("multiplication is not defined") f = dict() for i in self._matrix_dictionary.keys(): f[i] = self._matrix_dictionary[i] * y @@ -306,7 +306,7 @@ def __rmul__(self,x): try: y = self._domain.base_ring()(x) except TypeError: - raise TypeError, "Multiplication is not defined." + raise TypeError("multiplication is not defined") f = dict() for i in self._matrix_dictionary.keys(): f[i] = y * self._matrix_dictionary[i] @@ -314,7 +314,7 @@ def __rmul__(self,x): def __sub__(self,x): """ - Returns ``self - x``. + Return ``self - x``. EXAMPLES:: @@ -344,7 +344,7 @@ def __sub__(self,x): def __eq__(self,x): """ - Returns ``True`` if and only if ``self == x``. + Return ``True`` if and only if ``self == x``. EXAMPLES:: @@ -362,14 +362,14 @@ def __eq__(self,x): sage: x == y True """ - if not isinstance(x,ChainComplexMorphism) or self._codomain != x._codomain or self._domain != x._domain or self._matrix_dictionary != x._matrix_dictionary: - return False - else: - return True + return isinstance(x,ChainComplexMorphism) \ + and self._codomain == x._codomain \ + and self._domain == x._domain \ + and self._matrix_dictionary == x._matrix_dictionary def _repr_(self): """ - Returns the string representation of ``self``. + Return the string representation of ``self``. EXAMPLES:: @@ -384,4 +384,5 @@ def _repr_(self): 'Chain complex morphism from Trivial chain complex over Integer Ring to Trivial chain complex over Integer Ring' """ - return "Chain complex morphism from " + self._domain._repr_() + " to " + self._codomain._repr_() + return "Chain complex morphism from {} to {}".format(self._domain, self._codomain) + diff --git a/src/sage/homology/homology_group.py b/src/sage/homology/homology_group.py index c97987aea19..19218509dcc 100644 --- a/src/sage/homology/homology_group.py +++ b/src/sage/homology/homology_group.py @@ -65,7 +65,7 @@ def __init__(self, n, invfac): def _repr_(self): """ - Print representation + Print representation of ``self``. EXAMPLES:: @@ -92,7 +92,7 @@ def _repr_(self): too_many = (numfac > 4) if too_many: if t not in printed: - g.append("C%s^%s" % (t, numfac)) + g.append("C{}^{}".format(t, numfac)) printed.append(t) else: g.append("C%s" % t) @@ -101,7 +101,7 @@ def _repr_(self): def _latex_(self): """ - LaTeX representation + LaTeX representation of ``self``. EXAMPLES:: @@ -118,7 +118,7 @@ def _latex_(self): rank = len(filter(lambda x: x == 0, eldv)) torsion = sorted(filter(lambda x: x, eldv)) if rank > 4: - g = ["\\ZZ^{%s}" % rank] + g = ["\\ZZ^{{{}}}".format(rank)] else: g = ["\\ZZ"] * rank if len(torsion) != 0: @@ -128,25 +128,26 @@ def _latex_(self): too_many = (numfac > 4) if too_many: if t not in printed: - g.append("C_{%s}^{%s}" % (t, numfac)) + g.append("C_{{{}}}^{{{}}}".format(t, numfac)) printed.append(t) else: - g.append("C_{%s}" % t) + g.append("C_{{{}}}".format(t)) times = " \\times " return times.join(g) def HomologyGroup(n, base_ring, invfac=None): """ - Abelian group on `n` generators. + Abelian group on `n` generators which represents a homology group in a + fixed degree. INPUT: - - ``n`` -- integer. The number of generators. + - ``n`` -- integer; the number of generators - - ``base_ring`` -- ring. The base ring over which the homology is computed. + - ``base_ring`` -- ring; the base ring over which the homology is computed - - ``inv_fac`` -- list of integers. The invariant factors. Ignored - if the base ring is a field. + - ``inv_fac`` -- list of integers; the invariant factors -- ignored + if the base ring is a field OUTPUT: @@ -180,6 +181,7 @@ def HomologyGroup(n, base_ring, invfac=None): if len(invfac) < n: invfac = [0] * (n - len(invfac)) + invfac elif len(invfac) > n: - raise ValueError, "invfac (=%s) must have length n (=%s)"%(invfac, n) + raise ValueError("invfac (={}) must have length n (={})".format(invfac, n)) M = HomologyGroup_class(n, invfac) return M + diff --git a/src/sage/homology/matrix_utils.py b/src/sage/homology/matrix_utils.py index f63ce67df37..ab8a6ce87bf 100644 --- a/src/sage/homology/matrix_utils.py +++ b/src/sage/homology/matrix_utils.py @@ -31,16 +31,18 @@ def dhsw_snf(mat, verbose=False): .. NOTE:: - 'snf' stands for 'Smith Normal Form.' + 'snf' stands for 'Smith Normal Form'. INPUT: - - ``mat`` -- an integer matrix, either sparse or dense. + - ``mat`` -- an integer matrix, either sparse or dense. (They use the transpose of the matrix considered here, so they use rows instead of columns.) - Algorithm: go through ``mat`` one column at a time. For each + ALGORITHM: + + Go through ``mat`` one column at a time. For each column, add multiples of previous columns to it until either - it's zero, in which case it should be deleted. @@ -71,9 +73,9 @@ def dhsw_snf(mat, verbose=False): REFERENCES: - .. [DHSW] Dumas, Heckenbach, Saunders, Welker, "Computing simplicial - homology based on efficient Smith normal form algorithms," in - "Algebra, geometry, and software systems" (2003), 177-206. + .. [DHSW] Dumas, Heckenbach, Saunders, and Welker. *Computing simplicial + homology based on efficient Smith normal form algorithms*. + Algebra, geometry, and software systems. (2003) 177-206. """ ring = mat.base_ring() rows = mat.nrows() @@ -207,6 +209,5 @@ def dhsw_snf(mat, verbose=False): if len(ed) < rows: return ed + [0]*(rows - len(ed)) - else: - return ed[:rows] + return ed[:rows] diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py index b965f8426ce..22b3c5a22ac 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/homology/simplicial_complex.py @@ -1926,8 +1926,7 @@ def _homology_(self, dim=None, **kwds): zero = HomologyGroup(0, base_ring) if isinstance(dim, (list, tuple)): return dict([d, answer.get(d, zero)] for d in dim) - else: - return answer.get(dim, zero) + return answer.get(dim, zero) def add_face(self, face): """ From dd02b223f28bd57c1520bec9e116129cd3f089d6 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 30 Nov 2013 22:02:19 -0800 Subject: [PATCH 100/206] Fixed minor bug introduced in chain_complex_morphism.py. --- src/sage/homology/chain_complex.py | 6 +++--- src/sage/homology/chain_complex_morphism.py | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index e9f10ec976e..b8182526285 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -571,12 +571,12 @@ class ChainComplex_class(Parent): See :func:`ChainComplex` for full documentation. The differentials are required to be in the following canonical form: - - * All differentials that are not `0\times 0` must be specified + + * All differentials that are not `0 \times 0` must be specified (even if they have zero rows or zero columns), and * Differentials that are `0 \times 0` must not be specified. - + * Immutable matrices over the ``base_ring`` This and more is ensured by the assertions in the diff --git a/src/sage/homology/chain_complex_morphism.py b/src/sage/homology/chain_complex_morphism.py index d9f62a50182..57150ec7cca 100644 --- a/src/sage/homology/chain_complex_morphism.py +++ b/src/sage/homology/chain_complex_morphism.py @@ -133,6 +133,10 @@ def __init__(self, matrices, C, D, check=True): initial_matrices = dict(matrices) matrices = dict() for i in degrees: + if i - d not in degrees: + if not (C.free_module_rank(i) == D.free_module_rank(i) == 0): + raise ValueError('{} and {} are not rank 0 in degree {}'.format(C, D, i)) + continue try: matrices[i] = initial_matrices.pop(i) except KeyError: From df0f7c9a239b08962e95945144260bfedc775554 Mon Sep 17 00:00:00 2001 From: Tomer Bauer Date: Fri, 30 Aug 2013 22:26:21 +0000 Subject: [PATCH 101/206] Add structure_description method to permutation groups --- src/sage/groups/perm_gps/permgroup.py | 62 +++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 572c8e558dc..7ccaf385ae7 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -123,6 +123,7 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** +import re from functools import wraps from sage.misc.randstate import current_randstate @@ -1603,6 +1604,67 @@ def group_primitive_id(self): raise ValueError('Group is not primitive') return Integer(self._gap_().PrimitiveIdentification()) + @cached_method + def structure_description(self, latex=False): + r""" + Return a string that try to describe the structure of ``self``. + It wraps the GAP method ``StructureDescription``. + + Requires "optional" database_gap package. + + INPUT: + + - ``latex`` -- a boolean (default: ``False``). If ``True`` return a + LaTeX formatted string. + + OUTPUT: + + - string + + EXAMPLES:: + + sage: G = CyclicPermutationGroup(6) + sage: G.structure_description() # optional - database_gap + 'C6' + sage: G.structure_description(latex=True) # optional - database_gap + 'C_{6}' + sage: G2 = G.direct_product(G, maps=False) + sage: LatexExpr(G2.structure_description(latex=True)) # optional - database_gap + C_{6} \times C_{6} + + This method is mainly intended for small groups or groups with few + normal subgroups. Even then there are some surprises:: + + sage: D3 = DihedralGroup(3) + sage: D3.structure_description() # optional - database_gap + 'S3' + + We use the Sage notation for the degree of dihedral groups:: + + sage: D4 = DihedralGroup(4) + sage: D4.structure_description() # optional - database_gap + 'D4' + + For full details, including the form of the returned string and + the algorithm to build it, see GAP documentation: + http://www.gap-system.org/Manuals/doc/ref/chap39.html + + An important note from the GAP documentation: + The string returned by StructureDescription is not an isomorphism + invariant: non-isomorphic groups can have the same string value, + and two isomorphic groups in different representations can produce + different strings. + """ + def correct_dihedral_degree(match): + return "%sD%d" % (match.group(1), int(match.group(2))/2) + + description = self._gap_().StructureDescription().str() + description = re.sub(r"(\A|\W)D(\d+)", correct_dihedral_degree, description) + if not latex: + return description + description = description.replace("x", r"\times").replace(":", r"\rtimes") + return re.sub(r"([A-Za-z])([0-9]+)", r"\g<1>_{\g<2>}", description) + def center(self): """ Return the subgroup of elements that commute with every element From bdb1f2a55fd3b0099428e425dd8a74e8df0f9c0f Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 1 Dec 2013 15:32:03 +0100 Subject: [PATCH 102/206] trac #15128: reviewer's patch -- some doc and support for other outputs --- src/sage/groups/perm_gps/permgroup.py | 37 ++++++++++++++++----------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 7ccaf385ae7..49cf50b2863 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -123,7 +123,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** -import re from functools import wraps from sage.misc.randstate import current_randstate @@ -1607,10 +1606,15 @@ def group_primitive_id(self): @cached_method def structure_description(self, latex=False): r""" - Return a string that try to describe the structure of ``self``. - It wraps the GAP method ``StructureDescription``. + Return a string that tries to describe the structure of ``self``. - Requires "optional" database_gap package. + This methods wraps GAP's ``StructureDescription`` method. + + Requires the *optional* ``database_gap`` package. + + For full details, including the form of the returned string and the + algorithm to build it, see `GAP's documentation + `_ INPUT: @@ -1621,6 +1625,14 @@ def structure_description(self, latex=False): - string + .. WARNING:: + + From GAP's documentation: The string returned by + ``StructureDescription`` is **not** an isomorphism invariant: + non-isomorphic groups can have the same string value, and two + isomorphic groups in different representations can produce different + strings. + EXAMPLES:: sage: G = CyclicPermutationGroup(6) @@ -1645,25 +1657,20 @@ def structure_description(self, latex=False): sage: D4.structure_description() # optional - database_gap 'D4' - For full details, including the form of the returned string and - the algorithm to build it, see GAP documentation: - http://www.gap-system.org/Manuals/doc/ref/chap39.html - - An important note from the GAP documentation: - The string returned by StructureDescription is not an isomorphism - invariant: non-isomorphic groups can have the same string value, - and two isomorphic groups in different representations can produce - different strings. """ + import re def correct_dihedral_degree(match): - return "%sD%d" % (match.group(1), int(match.group(2))/2) + return "%sD%d" % (match.group(1), int(match.group(2))/2) description = self._gap_().StructureDescription().str() description = re.sub(r"(\A|\W)D(\d+)", correct_dihedral_degree, description) if not latex: return description description = description.replace("x", r"\times").replace(":", r"\rtimes") - return re.sub(r"([A-Za-z])([0-9]+)", r"\g<1>_{\g<2>}", description) + description = re.sub(r"([A-Za-z]+)([0-9]+)", r"\g<1>_{\g<2>}", description) + description = re.sub(r"O([+-])", r"O^{\g<1>}", description) + + return description def center(self): """ From e955febaf5d867102f1517ccb7f0e9853fed2df7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 2 Dec 2013 08:11:44 -0800 Subject: [PATCH 103/206] Added files to documentation and fixed internal link. --- src/doc/en/reference/homology/index.rst | 2 ++ src/sage/homology/homology_group.py | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/homology/index.rst b/src/doc/en/reference/homology/index.rst index 82c2f336d93..147cd8c2aed 100644 --- a/src/doc/en/reference/homology/index.rst +++ b/src/doc/en/reference/homology/index.rst @@ -21,6 +21,8 @@ cell complexes. sage/homology/delta_complex sage/homology/cubical_complex sage/homology/cell_complex + sage/homology/homology_group + sage/homology/matrix_utils sage/interfaces/chomp .. include:: ../footer.txt diff --git a/src/sage/homology/homology_group.py b/src/sage/homology/homology_group.py index 19218509dcc..711ef7d9be3 100644 --- a/src/sage/homology/homology_group.py +++ b/src/sage/homology/homology_group.py @@ -26,9 +26,10 @@ class HomologyGroup_class(AdditiveAbelianGroup_fixed_gens): """ Discrete Abelian group on `n` generators. This class inherits from - :class:`AdditiveAbelianGroup`; see that for more - documentation. The main difference between the classes is in the - print representation. + :class:`~sage.groups.additive_abelian.additive_abelian_group.AdditiveAbelianGroup_fixed_gens`; + see :mod:`sage.groups.additive_abelian.additive_abelian_group` for more + documentation. The main difference between the classes is in the print + representation. EXAMPLES:: From 510d554d3956e3334471711091ff19591e249055 Mon Sep 17 00:00:00 2001 From: Alexandru Ghitza Date: Fri, 26 Jul 2013 08:11:22 +0000 Subject: [PATCH 104/206] trac #14970: make qsieve and ecm-gmp easily available from factor --- src/sage/rings/integer.pyx | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 07fd798c781..a3dbfe3249d 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -3377,6 +3377,18 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): - ``'kash'`` - use the KASH computer algebra system (requires the optional kash package) + - ``'magma'`` - use the MAGMA computer algebra system (requires + an installation of MAGMA) + + - ``'qsieve'`` - use Bill Hart's quadratic sieve code; + WARNING: this may not work as expected, see qsieve? for + more information + + - ``'ecm'`` - use ECM-GMP, an implementation of Hendrik + Lenstra's elliptic curve method; WARNING: the factors + returned may not be prime, see ecm.factor? for more + information + - ``proof`` - bool (default: True) whether or not to prove primality of each factor (only applicable for PARI). @@ -3428,6 +3440,27 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sage: n.factor(limit=1000) 2 * 11 * 41835640583745019265831379463815822381094652231 + We factor using a quadratic sieve algorithm:: + + sage: p = next_prime(10^20) + sage: q = next_prime(10^21) + sage: n = p*q + sage: n.factor(algorithm='qsieve') + doctest:... RuntimeWarning: the factorization returned + by qsieve may be incomplete (the factors may not be prime) + or even wrong; see qsieve? for details + 100000000000000000039 * 1000000000000000000117 + + We factor using the elliptic curve method:: + + sage: p = next_prime(10^15) + sage: q = next_prime(10^21) + sage: n = p*q + sage: n.factor(algorithm='ecm') + doctest:... RuntimeWarning: the factors returned by ecm + are not guaranteed to be prime; see ecm.factor? for details + 1000000000000037 * 1000000000000000000117 + TESTS:: sage: n.factor(algorithm='foobar') @@ -3495,6 +3528,22 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): exp_type = int if int_ else Integer F = [(Integer(p), exp_type(e)) for p,e in zip(res[0::2], res[1::2])] return Factorization(F, unit) + elif algorithm == 'qsieve': + message = "the factorization returned by qsieve may be incomplete (the factors may not be prime) or even wrong; see qsieve? for details" + from warnings import warn + warn(message, RuntimeWarning, stacklevel=5) + from sage.interfaces.qsieve import qsieve + res = [(p, 1) for p in qsieve(n)[0]] + F = IntegerFactorization(res, unit) + return F + elif algorithm == 'ecm': + message = "the factors returned by ecm are not guaranteed to be prime; see ecm.factor? for details" + from warnings import warn + warn(message, RuntimeWarning, stacklevel=2) + from sage.interfaces.ecm import ecm + res = [(p, 1) for p in ecm.factor(n)] + F = IntegerFactorization(res, unit) + return F else: raise ValueError, "Algorithm is not known" From 8c2aefff52e458b4176809e3c10c46c34d5831eb Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 3 Dec 2013 12:36:47 +0100 Subject: [PATCH 105/206] trac #15479: Finite Words should be proud of their finiteness --- src/sage/combinat/words/words.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/words/words.py b/src/sage/combinat/words/words.py index d55d1772df9..fa7b725644f 100644 --- a/src/sage/combinat/words/words.py +++ b/src/sage/combinat/words/words.py @@ -61,7 +61,7 @@ def Words(alphabet=None, length=None, finite=True, infinite=True): sage: Words(5, 3) Words of length 3 over {1, 2, 3, 4, 5} sage: Words(5, infinite=False) - Words over {1, 2, 3, 4, 5} + Finite Words over {1, 2, 3, 4, 5} sage: Words(5, finite=False) Infinite Words over {1, 2, 3, 4, 5} sage: Words('ab') @@ -69,7 +69,7 @@ def Words(alphabet=None, length=None, finite=True, infinite=True): sage: Words('ab', 2) Words of length 2 over {'a', 'b'} sage: Words('ab', infinite=False) - Words over {'a', 'b'} + Finite Words over {'a', 'b'} sage: Words('ab', finite=False) Infinite Words over {'a', 'b'} sage: Words('positive integers', finite=False) @@ -1266,8 +1266,8 @@ def _repr_(self): EXAMPLES:: - sage: Words('ab', infinite=False)._repr_() - "Words over {'a', 'b'}" + sage: Words('ab', finite=False)._repr_() + "Infinite Words over {'a', 'b'}" """ return "Infinite Words over %s" % self.alphabet() @@ -1292,9 +1292,9 @@ def _repr_(self): EXAMPLES:: sage: Words('ab', infinite=False)._repr_() - "Words over {'a', 'b'}" + "Finite Words over {'a', 'b'}" """ - return "Words over %s" % self.alphabet() + return "Finite Words over %s" % self.alphabet() class FiniteWords_length_k_over_OrderedAlphabet(FiniteWords_over_OrderedAlphabet): def __init__(self, alphabet, length): From fd217a518be20b8dc0fcda19ed30cb4f8ba651c6 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 9 Dec 2013 14:35:01 +0100 Subject: [PATCH 106/206] trac #15503: DegreeSequences(n) returns false positive --- src/sage/combinat/degree_sequences.pyx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index 808c72fb6dd..c6b42384c93 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -58,7 +58,7 @@ of a vertex is at most `n-1` -- and the sum of them is at most `n(n-1)`. Degree sequences are completely characterized by a result from Erdos and Gallai: **Erdos and Gallai:** *The sequence of integers* `d_1\geq ... \geq d_n` *is a -degree sequence if and only if* `\forall i` +degree sequence if and only if* `\sum_i d_i` is even and `\forall i` .. MATH:: \sum_{j\leq i}d_j \leq j(j-1) + \sum_{j>i}\min(d_j,i) @@ -301,12 +301,21 @@ class DegreeSequences: sage: [3,3,2,2,2,2,2,2] in DegreeSequences(8) True + + TESTS: + + :trac:`15503`:: + + sage: [2,2,2,2,1,1,1] in DegreeSequences(7) + False """ cdef int n = self._n if len(seq)!=n: return False - cdef int S = sum(seq) + # Is the sum even ? + if sum(seq)%2 == 1: + return False # Partial represents the left side of Erdos and Gallai's inequality, # i.e. the sum of the i first integers. From 16b4b265b784192dab04950e8bd57060917d73a9 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 10 Dec 2013 14:39:13 +0100 Subject: [PATCH 107/206] trac #15507: Static sparse graphs on > 65536 vertices --- .../graphs/base/static_sparse_backend.pyx | 10 +- src/sage/graphs/base/static_sparse_graph.pxd | 14 +- src/sage/graphs/base/static_sparse_graph.pyx | 54 ++++--- src/sage/graphs/distances_all_pairs.pxd | 2 +- src/sage/graphs/distances_all_pairs.pyx | 132 +++++++----------- 5 files changed, 92 insertions(+), 120 deletions(-) diff --git a/src/sage/graphs/base/static_sparse_backend.pyx b/src/sage/graphs/base/static_sparse_backend.pyx index 35df74c0393..65c5dca8fca 100644 --- a/src/sage/graphs/base/static_sparse_backend.pyx +++ b/src/sage/graphs/base/static_sparse_backend.pyx @@ -39,6 +39,7 @@ from sage.graphs.base.static_sparse_graph cimport (init_short_digraph, edge_label) from c_graph import CGraphBackend from sage.misc.bitset cimport FrozenBitset +from libc.stdint cimport uint32_t cdef class StaticSparseCGraph(CGraph): """ @@ -212,7 +213,7 @@ cdef class StaticSparseCGraph(CGraph): raise LookupError("The vertex does not belong to the graph") cdef int i - return [self.g.neighbors[u][i] for i in range(out_degree(self.g,u))] + return [ self.g.neighbors[u][i] for i in range(out_degree(self.g,u))] cpdef list in_neighbors(self, int u): r""" @@ -236,7 +237,7 @@ cdef class StaticSparseCGraph(CGraph): raise LookupError("The vertex does not belong to the graph") cdef int i - return [self.g_rev.neighbors[u][i] for i in range(out_degree(self.g_rev,u))] + return [ self.g_rev.neighbors[u][i] for i in range(out_degree(self.g_rev,u))] cpdef int out_degree(self, int u): r""" @@ -433,7 +434,7 @@ class StaticSparseBackend(CGraphBackend): cdef StaticSparseCGraph cg = self._cg cdef list l - cdef ushort * edge = has_edge(cg.g,u,v) + cdef uint32_t * edge = has_edge(cg.g,u,v) if edge == NULL: raise LookupError("The edge does not exist") @@ -475,8 +476,7 @@ class StaticSparseBackend(CGraphBackend): sage: g.has_edge(0,4,None) True """ - cdef ushort * edge = NULL - cdef ushort * tmp = NULL + cdef uint32_t * edge = NULL cdef StaticSparseCGraph cg = (self._cg) try: u = self._vertex_to_int[u] diff --git a/src/sage/graphs/base/static_sparse_graph.pxd b/src/sage/graphs/base/static_sparse_graph.pxd index 9ca5f0bc685..17ceb83848c 100644 --- a/src/sage/graphs/base/static_sparse_graph.pxd +++ b/src/sage/graphs/base/static_sparse_graph.pxd @@ -1,6 +1,8 @@ ctypedef unsigned short ushort +ctypedef unsigned int uint include "sage/misc/bitset_pxd.pxi" +from libc.stdint cimport uint32_t cdef extern from "stdlib.h": ctypedef void const_void "const void" @@ -11,11 +13,11 @@ cdef extern from "stdlib.h": size_t size, int(*compar)(const_void *, const_void *)) nogil ctypedef struct short_digraph_s: - ushort * edges - ushort ** neighbors + uint32_t * edges + uint32_t ** neighbors PyObject * edge_labels - unsigned int m - ushort n + int m + int n ctypedef short_digraph_s short_digraph[1] @@ -23,5 +25,5 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled = ?) except -1 cdef void free_short_digraph(short_digraph g) cdef int init_reverse(short_digraph dst, short_digraph src) except -1 cdef int out_degree(short_digraph g, int u) -cdef inline ushort * has_edge(short_digraph g, ushort u, ushort v) -cdef inline object edge_label(short_digraph g, ushort * edge) +cdef inline uint32_t * has_edge(short_digraph g, int u, int v) +cdef inline object edge_label(short_digraph g, uint32_t * edge) diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index eb81c6fbf89..8f0aec242d2 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -29,14 +29,14 @@ Data structure The data structure is actually pretty simple and compact. ``short_digraph`` has five fields - * ``n`` (``unsigned short``) -- the number of vertices in the graph. + * ``n`` (``int``) -- the number of vertices in the graph. - * ``m`` (``unsigned int``) -- the number of edges in the graph. + * ``m`` (``int``) -- the number of edges in the graph. - * ``edges`` (``unsigned short *``) -- array whose length is the number of - edges of the graph. + * ``edges`` (``uint32_t *``) -- array whose length is the number of edges of + the graph. - * ``neighbors`` (``unsigned short **``) -- this array has size `n+1`, and + * ``neighbors`` (``uint32_t **``) -- this array has size `n+1`, and describes how the data of ``edges`` should be read : the neighbors of vertex `i` are the elements of ``edges`` addressed by ``neighbors[i]...neighbors[i+1]-1``. The element ``neighbors[n]``, which @@ -82,10 +82,6 @@ Technical details * When creating a ``fast_digraph`` from a ``Graph`` or ``DiGraph`` named ``G``, the `i^{\text{th}}` vertex corresponds to ``G.vertices()[i]`` - * In its current implementation (with ``unsigned short`` variables), the - data structure can handle graphs with at most 65535 vertices. If - necessary, changing it to ``int`` is totally straightforward. - * Some methods return ``bitset_t`` objets when lists could be expected. There is a very useful ``bitset_list`` function for this kind of problems :-) @@ -93,7 +89,7 @@ Technical details * When the edges are labelled, most of the space taken by this graph is taken by edge labels. If no edge is labelled then this space is not allocated, but if *any* edge has a label then a (possibly empty) label is - stored for each edge, which can represent a lot of memory. + stored for each edge, which can double the memory needs. * The data structure stores the number of edges, even though it appears that this number can be reconstructed with @@ -119,8 +115,8 @@ Cython functions ``init_short_digraph(short_digraph g, G)`` | Initializes ``short_digraph g`` from a Sage (Di)Graph. ``int n_edges(short_digraph g)`` | Returns the number of edges in ``g`` ``int out_degree(short_digraph g, int i)`` | Returns the out-degree of vertex `i` in ``g`` - ``has_edge(short_digraph g, ushort u, ushort v)`` | Tests the existence of an edge. - ``edge_label(short_digraph g, ushort * edge)`` | Returns the label associated with a given edge + ``has_edge(short_digraph g, int u, int v)`` | Tests the existence of an edge. + ``edge_label(short_digraph g, int * edge)`` | Returns the label associated with a given edge ``init_empty_copy(short_digraph dst, short_digraph src)`` | Allocates ``dst`` so that it can contain as many vertices and edges as ``src``. ``init_reverse(short_digraph dst, short_digraph src)`` | Initializes ``dst`` to a copy of ``src`` with all edges in the opposite direction. ``free_short_digraph(short_digraph g)`` | Free the ressources used by ``g`` @@ -140,7 +136,6 @@ Cython functions component containing ``v`` in ``g``. The variable ``g_reversed`` is assumed to represent the reverse of ``g``. - What is this module used for ? ------------------------------ @@ -164,6 +159,7 @@ cimport cpython ############################################################################## from sage.graphs.base.c_graph cimport CGraph +from libc.stdint cimport INT32_MAX cdef int init_short_digraph(short_digraph g, G, edge_labelled = False) except -1: r""" @@ -172,12 +168,10 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled = False) except -1 If ``G`` is a ``Graph`` objet (and not a ``DiGraph``), an edge between two vertices `u` and `v` is replaced by two arcs in both directions. """ - # g.n is unsigned short, so -1 is actually the maximum value possible. - g.n = -1 g.edge_labels = NULL - if G.order() > g.n: - raise ValueError("This structure can handle at most "+str( g.n)+" vertices !") + if G.order() >= INT32_MAX: + raise ValueError("This structure can handle at most "+str(INT32_MAX)+" vertices !") else: g.n = G.order() @@ -204,11 +198,11 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled = False) except -1 for i, v in enumerate(vertices): v_to_id[v] = i - g.edges = sage_malloc(n_edges*sizeof(ushort)) + g.edges = sage_malloc(n_edges*sizeof(uint32_t)) if g.edges == NULL: raise ValueError("Problem while allocating memory (edges)") - g.neighbors = sage_malloc((1+g.n)*sizeof(ushort *)) + g.neighbors = sage_malloc((1+g.n)*sizeof(uint32_t *)) if g.neighbors == NULL: raise ValueError("Problem while allocating memory (neighbors)") @@ -250,7 +244,7 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled = False) except -1 # Sorting the neighbors for i in range(g.n): - qsort(g.neighbors[i],g.neighbors[i+1]-g.neighbors[i],sizeof(ushort),compare_ushort_p) + qsort(g.neighbors[i],g.neighbors[i+1]-g.neighbors[i],sizeof(int),compare_uint32_p) else: edge_labels = [None]*n_edges @@ -281,11 +275,11 @@ cdef int init_empty_copy(short_digraph dst, short_digraph src) except -1: dst.edge_labels = NULL cdef list edge_labels - dst.edges = sage_malloc(n_edges(src)*sizeof(ushort)) + dst.edges = sage_malloc(n_edges(src)*sizeof(uint32_t)) if dst.edges == NULL: raise ValueError("Problem while allocating memory (edges)") - dst.neighbors = sage_malloc((src.n+1)*sizeof(ushort *)) + dst.neighbors = sage_malloc((src.n+1)*sizeof(uint32_t *)) if dst.neighbors == NULL: raise ValueError("Problem while allocating memory (neighbors)") @@ -350,18 +344,18 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: return 0 -cdef int compare_ushort_p(const_void *a, const_void *b): - return ( a)[0] - ( b)[0] +cdef int compare_uint32_p(const_void *a, const_void *b): + return ( a)[0] - ( b)[0] -cdef inline ushort * has_edge(short_digraph g, ushort u, ushort v): +cdef inline uint32_t * has_edge(short_digraph g, int u, int v): r""" Tests the existence of an edge. Assumes that the neighbors of each vertex are sorted. """ - return bsearch(&v, g.neighbors[u], g.neighbors[u+1]-g.neighbors[u], sizeof(ushort), compare_ushort_p) + return bsearch(&v, g.neighbors[u], g.neighbors[u+1]-g.neighbors[u], sizeof(uint32_t), compare_uint32_p) -cdef inline object edge_label(short_digraph g, ushort * edge): +cdef inline object edge_label(short_digraph g, uint32_t * edge): r""" Returns the label associated with a given edge """ @@ -380,7 +374,7 @@ cdef int can_be_reached_from(short_digraph g, int src, bitset_t reached) except # We will be doing a Depth-First Search. We allocate the stack we need for # that, and put "src" on top of it. - cdef ushort * stack = sage_malloc(g.n*sizeof(ushort)) + cdef int * stack = sage_malloc(g.n*sizeof(int)) if stack == NULL: raise ValueError("Problem while allocating memory (stack)") @@ -389,8 +383,8 @@ cdef int can_be_reached_from(short_digraph g, int src, bitset_t reached) except # What we need to iterate over the edges cdef int i - cdef ushort * v - cdef ushort * end + cdef uint32_t * v + cdef uint32_t * end # Plain old DFS ... # diff --git a/src/sage/graphs/distances_all_pairs.pxd b/src/sage/graphs/distances_all_pairs.pxd index 81d7a00b435..4cf4bb46e43 100644 --- a/src/sage/graphs/distances_all_pairs.pxd +++ b/src/sage/graphs/distances_all_pairs.pxd @@ -1,3 +1,3 @@ cdef unsigned short * c_shortest_path_all_pairs(G) except NULL cdef unsigned short * c_distances_all_pairs(G) -cdef unsigned short * c_eccentricity(G) except NULL +cdef int * c_eccentricity(G) except NULL diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 5458085cbe8..693d63a3ab9 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -66,30 +66,30 @@ Breadth First Search per vertex of the (di)graph. is necessary for the algorithm, so this value can **not** be set to ``NULL``. - - ``unsigned short * eccentricity`` -- a pointer toward an array of size - `n\cdot\text{sizeof(unsigned short)}`. Set to ``NULL`` if you do not want - to compute the eccentricity. + - ``int * eccentricity`` -- a pointer toward an array of size + `n\cdot\text{sizeof(int)}`. Set to ``NULL`` if you do not want to compute + the eccentricity. **Technical details** - - The vertices are encoded as `1, ..., n` as they appear in the ordering of ``G.vertices()``. - Because this function works on matrices whose size is quadratic compared - to the number of vertices, it uses short variables to store the vertices' - names instead of long ones to divide by 2 the size in memory. This means - that the current implementation does not run on a graph of more than 65536 - nodes (this can be easily changed if necessary, but would require much - more memory. It may be worth writing two versions). For information, the - current version of the algorithm on a graph with `65536=2^{16}` nodes - creates in memory `2` tables on `2^{32}` short elements (2bytes each), for - a total of `2^{34}` bytes or `16` gigabytes. - - - In the C version of these functions, a value of `` -1 = - 65535`` for a distance or a predecessor means respectively ``+Infinity`` - and ``None``. These case happens when the input is a disconnected graph, - or a non-strongly-connected digraph. + to the number of vertices when computing all distances or predecessors, it + uses short variables to store the vertices' names instead of long ones to + divide by 2 the size in memory. This means that only the + diameter/eccentricities can be computed on a graph of more than 65536 + nodes. For information, the current version of the algorithm on a graph + with `65536=2^{16}` nodes creates in memory `2` tables on `2^{32}` short + elements (2bytes each), for a total of `2^{33}` bytes or `8` gigabytes. In + order to support larger sizes, we would have to replace shorts by 32-bits + int or 64-bits int, which would then require respectively 16GB or 32GB. + + - In the C version of these functions, infinite distances are represented + with `` -1 = 65535`` for ``unsigned short`` variables, and + by ``INT32_MAX`` otherwise. These case happens when the input is a + disconnected graph, or a non-strongly-connected digraph. - A memory error is raised when data structures allocation failed. This could happen with large graphs on computers with low memory space. @@ -129,7 +129,7 @@ Functions include "sage/misc/bitset_pxd.pxi" include "sage/misc/bitset.pxi" -from libc.stdint cimport uint64_t +from libc.stdint cimport uint64_t, uint32_t, INT32_MAX from sage.graphs.base.c_graph cimport CGraph from sage.graphs.base.c_graph cimport vertex_label from sage.graphs.base.c_graph cimport get_vertex @@ -140,7 +140,7 @@ from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digr cdef inline all_pairs_shortest_path_BFS(gg, unsigned short * predecessors, unsigned short * distances, - unsigned short * eccentricity): + int * eccentricity): """ See the module's documentation. """ @@ -154,49 +154,37 @@ cdef inline all_pairs_shortest_path_BFS(gg, cdef int n = len(int_to_vertex) - if n > -1: - raise ValueError("The graph backend contains more than "+str( -1)+" nodes") + # Computing the predecessors/distances can only be done if we have less than + # MAX_UNSIGNED_SHORT vertices. No problem with the eccentricities though as + # we store them on an integer vector. + if (predecessors != NULL or distances != NULL) and n > -1: + raise ValueError("The graph backend contains more than "+ + str( -1)+" nodes and we cannot "+ + "compute the matrix of distances/predecessors on "+ + "something like that !") # The vertices which have already been visited cdef bitset_t seen bitset_init(seen, n) # The list of waiting vertices, the beginning and the end of the list - - cdef unsigned short * waiting_list = sage_malloc(n*sizeof(unsigned short)) + cdef int * waiting_list = sage_malloc(n*sizeof(int)) if waiting_list == NULL: raise MemoryError() - cdef unsigned short waiting_beginning = 0 - cdef unsigned short waiting_end = 0 - - cdef int * degree = sage_malloc(n*sizeof(int)) - if degree == NULL: - sage_free(waiting_list) - raise MemoryError() + cdef int waiting_beginning = 0 + cdef int waiting_end = 0 - cdef unsigned short source - cdef unsigned short v, u - cdef unsigned short * p_tmp - cdef unsigned short * end + cdef int source + cdef int v, u + cdef uint32_t * p_tmp + cdef uint32_t * end cdef unsigned short * c_predecessors = predecessors - cdef unsigned short * c_eccentricity = eccentricity - cdef unsigned short * c_distances - - # If the user does not want to compute the distances, the distances variable - # is set to NULL. We *need* to compute the distances, though, if we want to - # compute anything with this function. In this case we allocate a vector of - # size n (and not a vector of size n^2, that is the size of the distance - # variable when it is not null) - - if distances != NULL: - c_distances = distances - else: - c_distances = sage_malloc( n * sizeof(unsigned short)) - if c_distances == NULL: - sage_free(waiting_list) - sage_free(degree) - raise MemoryError() + cdef int * c_eccentricity = eccentricity + cdef int * c_distances = sage_malloc( n * sizeof(int)) + if c_distances == NULL: + sage_free(waiting_list) + raise MemoryError() # Copying the whole graph to obtain the list of neighbors quicker than by # calling out_neighbors @@ -215,9 +203,9 @@ cdef inline all_pairs_shortest_path_BFS(gg, cdef short_digraph sd init_short_digraph(sd, gg) - cdef unsigned short ** p_vertices = sd.neighbors - cdef unsigned short * p_edges = sd.edges - cdef unsigned short * p_next = p_edges + cdef uint32_t ** p_vertices = sd.neighbors + cdef uint32_t * p_edges = sd.edges + cdef uint32_t * p_next = p_edges # We run n different BFS taking each vertex as a source for source in range(n): @@ -267,7 +255,7 @@ cdef inline all_pairs_shortest_path_BFS(gg, # If not all the vertices have been met for v in range(n): if not bitset_in(seen, v): - c_distances[v] = -1 + c_distances[v] = INT32_MAX if predecessors != NULL: c_predecessors[v] = -1 @@ -275,22 +263,19 @@ cdef inline all_pairs_shortest_path_BFS(gg, c_predecessors += n if eccentricity != NULL: - c_eccentricity[0] = 0 + c_eccentricity[source] = 0 for i in range(n): - c_eccentricity[0] = c_eccentricity[0] if c_eccentricity[0] >= c_distances[i] else c_distances[i] - - c_eccentricity += 1 + c_eccentricity[source] = c_eccentricity[source] if c_eccentricity[source] >= c_distances[i] else c_distances[i] if distances != NULL: - c_distances += n + for i in range(n): + distances[i] = c_distances[i] + distances += n bitset_free(seen) sage_free(waiting_list) - sage_free(degree) free_short_digraph(sd) - if distances == NULL: - sage_free(c_distances) - + sage_free(c_distances) ################ # Predecessors # @@ -455,9 +440,7 @@ def distances_all_pairs(G): else: d_tmp[int_to_vertex[i]] = c_distances[i] - d[int_to_vertex[j]] = d_tmp - c_distances += n sage_free(distances) @@ -671,7 +654,7 @@ def distances_and_predecessors_all_pairs(G): # Eccentricity # ################ -cdef unsigned short * c_eccentricity(G) except NULL: +cdef int * c_eccentricity(G) except NULL: r""" Returns the vector of eccentricities in G. @@ -680,15 +663,10 @@ cdef unsigned short * c_eccentricity(G) except NULL: """ cdef unsigned int n = G.order() - cdef unsigned short * ecc = sage_malloc(n*sizeof(unsigned short)) + cdef int * ecc = sage_malloc(n*sizeof(int)) if ecc == NULL: raise MemoryError() - cdef unsigned short * distances = sage_malloc(n*n*sizeof(unsigned short)) - if distances == NULL: - sage_free(ecc) - raise MemoryError() - all_pairs_shortest_path_BFS(G, NULL, distances, ecc) - sage_free(distances) + all_pairs_shortest_path_BFS(G, NULL, NULL, ecc) return ecc @@ -712,12 +690,12 @@ def eccentricity(G): if n == 0: return [] - cdef unsigned short * ecc = c_eccentricity(G) + cdef int * ecc = c_eccentricity(G) cdef list l_ecc = [] cdef int i for i in range(n): - if ecc[i] == -1: + if ecc[i] == INT32_MAX: l_ecc.append(Infinity) else: l_ecc.append(ecc[i]) @@ -876,12 +854,10 @@ def distances_distribution(G): return distr - ################## # Floyd-Warshall # ################## - def floyd_warshall(gg, paths = True, distances = False): r""" Computes the shortest path/distances between all pairs of vertices. From 561e39ff1143ff869d3bfa77801cb54ec64a8e3e Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sun, 17 Nov 2013 13:22:26 +0000 Subject: [PATCH 108/206] create pari_instance.pxd --- src/sage/libs/pari/gen.pxd | 28 ------------------------- src/sage/libs/pari/pari_instance.pxd | 31 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 28 deletions(-) create mode 100644 src/sage/libs/pari/pari_instance.pxd diff --git a/src/sage/libs/pari/gen.pxd b/src/sage/libs/pari/gen.pxd index bece49df10e..289b12ab5cd 100644 --- a/src/sage/libs/pari/gen.pxd +++ b/src/sage/libs/pari/gen.pxd @@ -1,7 +1,6 @@ include 'decl.pxi' cimport sage.structure.element -cimport sage.structure.parent_base cimport cython @cython.final @@ -16,30 +15,3 @@ cdef class gen(sage.structure.element.RingElement): cdef gen pari(self, object x) cdef GEN _deepcopy_to_python_heap(self, GEN x, pari_sp* address) cdef long get_var(self, v) - -@cython.final -cdef class PariInstance(sage.structure.parent_base.ParentWithBase): - cdef gen PARI_ZERO, PARI_ONE, PARI_TWO - cdef inline gen new_gen(self, GEN x) - cdef inline gen new_gen_noclear(self, GEN x) - cdef gen new_gen_from_mpz_t(self, mpz_t value) - cdef inline GEN _new_GEN_from_mpz_t(self, mpz_t value) - cdef gen new_gen_from_mpq_t(self, mpq_t value) - cdef inline GEN _new_GEN_from_mpq_t(self, mpq_t value) - cdef gen new_gen_from_int(self, int value) - cdef gen new_t_POL_from_int_star(self, int *vals, int length, long varnum) - cdef gen new_gen_from_padic(self, long ordp, long relprec, mpz_t prime, mpz_t p_pow, mpz_t unit) - cdef inline void clear_stack(self) - cdef gen double_to_gen_c(self, double) - cdef GEN double_to_GEN(self, double) - cdef GEN deepcopy_to_python_heap(self, GEN x, pari_sp* address) - cdef gen new_ref(self, GEN g, gen parent) - cdef gen _empty_vector(self, long n) - cdef long get_var(self, v) - cdef GEN _new_GEN_from_mpz_t_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc) - cdef GEN _new_GEN_from_mpz_t_matrix_rotate90(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc) - cdef gen integer_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc, bint permute_for_hnf) - cdef GEN _new_GEN_from_mpq_t_matrix(self, mpq_t** B, Py_ssize_t nr, Py_ssize_t nc) - cdef gen rational_matrix(self, mpq_t** B, Py_ssize_t nr, Py_ssize_t nc) - -cdef GEN _Vec_append(GEN v, GEN a, long n) diff --git a/src/sage/libs/pari/pari_instance.pxd b/src/sage/libs/pari/pari_instance.pxd new file mode 100644 index 00000000000..4669b7a34ab --- /dev/null +++ b/src/sage/libs/pari/pari_instance.pxd @@ -0,0 +1,31 @@ +include 'decl.pxi' + +cimport sage.structure.parent_base +cimport cython + +@cython.final +cdef class PariInstance(sage.structure.parent_base.ParentWithBase): + cdef gen PARI_ZERO, PARI_ONE, PARI_TWO + cdef inline gen new_gen(self, GEN x) + cdef inline gen new_gen_noclear(self, GEN x) + cdef gen new_gen_from_mpz_t(self, mpz_t value) + cdef inline GEN _new_GEN_from_mpz_t(self, mpz_t value) + cdef gen new_gen_from_mpq_t(self, mpq_t value) + cdef inline GEN _new_GEN_from_mpq_t(self, mpq_t value) + cdef gen new_gen_from_int(self, int value) + cdef gen new_t_POL_from_int_star(self, int *vals, int length, long varnum) + cdef gen new_gen_from_padic(self, long ordp, long relprec, mpz_t prime, mpz_t p_pow, mpz_t unit) + cdef inline void clear_stack(self) + cdef gen double_to_gen_c(self, double) + cdef GEN double_to_GEN(self, double) + cdef GEN deepcopy_to_python_heap(self, GEN x, pari_sp* address) + cdef gen new_ref(self, GEN g, gen parent) + cdef gen _empty_vector(self, long n) + cdef long get_var(self, v) + cdef GEN _new_GEN_from_mpz_t_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc) + cdef GEN _new_GEN_from_mpz_t_matrix_rotate90(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc) + cdef gen integer_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc, bint permute_for_hnf) + cdef GEN _new_GEN_from_mpq_t_matrix(self, mpq_t** B, Py_ssize_t nr, Py_ssize_t nc) + cdef gen rational_matrix(self, mpq_t** B, Py_ssize_t nr, Py_ssize_t nc) + +cdef GEN _Vec_append(GEN v, GEN a, long n) From 08a278fea120d52fe89440b411b961a752863417 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sun, 17 Nov 2013 13:29:31 +0000 Subject: [PATCH 109/206] create pari_instance.pyx; move some documentation and examples there --- src/sage/libs/pari/gen.pyx | 136 +---------------------- src/sage/libs/pari/pari_instance.pyx | 154 +++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 131 deletions(-) create mode 100644 src/sage/libs/pari/pari_instance.pyx diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 2b637cf68ae..aff91c0ac33 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -1,5 +1,7 @@ """ -PARI C-library interface +Sage class for PARI's GEN type + +See the ``PariInstance`` class for documentation and examples. AUTHORS: @@ -18,136 +20,8 @@ AUTHORS: - Jeroen Demeyer (2011-11-12): rewrite various conversion routines (#11611, #11854, #11952) - -EXAMPLES:: - - sage: pari('5! + 10/x') - (120*x + 10)/x - sage: pari('intnum(x=0,13,sin(x)+sin(x^2) + x)') - 85.1885681951527 - sage: f = pari('x^3-1') - sage: v = f.factor(); v - [x - 1, 1; x^2 + x + 1, 1] - sage: v[0] # indexing is 0-based unlike in GP. - [x - 1, x^2 + x + 1]~ - sage: v[1] - [1, 1]~ - -Arithmetic obeys the usual coercion rules:: - - sage: type(pari(1) + 1) - - sage: type(1 + pari(1)) - - -GUIDE TO REAL PRECISION AND THE PARI LIBRARY - -The default real precision in communicating with the Pari library -is the same as the default Sage real precision, which is 53 bits. -Inexact Pari objects are therefore printed by default to 15 decimal -digits (even if they are actually more precise). - -Default precision example (53 bits, 15 significant decimals):: - - sage: a = pari(1.23); a - 1.23000000000000 - sage: a.sin() - 0.942488801931698 - -Example with custom precision of 200 bits (60 significant -decimals):: - - sage: R = RealField(200) - sage: a = pari(R(1.23)); a # only 15 significant digits printed - 1.23000000000000 - sage: R(a) # but the number is known to precision of 200 bits - 1.2300000000000000000000000000000000000000000000000000000000 - sage: a.sin() # only 15 significant digits printed - 0.942488801931698 - sage: R(a.sin()) # but the number is known to precision of 200 bits - 0.94248880193169751002382356538924454146128740562765030213504 - -It is possible to change the number of printed decimals:: - - sage: R = RealField(200) # 200 bits of precision in computations - sage: old_prec = pari.set_real_precision(60) # 60 decimals printed - sage: a = pari(R(1.23)); a - 1.23000000000000000000000000000000000000000000000000000000000 - sage: a.sin() - 0.942488801931697510023823565389244541461287405627650302135038 - sage: pari.set_real_precision(old_prec) # restore the default printing behavior - 60 - -Unless otherwise indicated in the docstring, most Pari functions -that return inexact objects use the precision of their arguments to -decide the precision of the computation. However, if some of these -arguments happen to be exact numbers (integers, rationals, etc.), -an optional parameter indicates the precision (in bits) to which -these arguments should be converted before the computation. If this -precision parameter is missing, the default precision of 53 bits is -used. The following first converts 2 into a real with 53-bit -precision:: - - sage: R = RealField() - sage: R(pari(2).sin()) - 0.909297426825682 - -We can ask for a better precision using the optional parameter:: - - sage: R = RealField(150) - sage: R(pari(2).sin(precision=150)) - 0.90929742682568169539601986591174484270225497 - -Warning regarding conversions Sage - Pari - Sage: Some care must be -taken when juggling inexact types back and forth between Sage and -Pari. In theory, calling p=pari(s) creates a Pari object p with the -same precision as s; in practice, the Pari library's precision is -word-based, so it will go up to the next word. For example, a -default 53-bit Sage real s will be bumped up to 64 bits by adding -bogus 11 bits. The function p.python() returns a Sage object with -exactly the same precision as the Pari object p. So -pari(s).python() is definitely not equal to s, since it has 64 bits -of precision, including the bogus 11 bits. The correct way of -avoiding this is to coerce pari(s).python() back into a domain with -the right precision. This has to be done by the user (or by Sage -functions that use Pari library functions in gen.pyx). For -instance, if we want to use the Pari library to compute sqrt(pi) -with a precision of 100 bits:: - - sage: R = RealField(100) - sage: s = R(pi); s - 3.1415926535897932384626433833 - sage: p = pari(s).sqrt() - sage: x = p.python(); x # wow, more digits than I expected! - 1.7724538509055160272981674833410973484 - sage: x.prec() # has precision 'improved' from 100 to 128? - 128 - sage: x == RealField(128)(pi).sqrt() # sadly, no! - False - sage: R(x) # x should be brought back to precision 100 - 1.7724538509055160272981674833 - sage: R(x) == s.sqrt() - True - -Elliptic curves and precision: If you are working with elliptic -curves and want to compute with a precision other than the default -53 bits, you should use the precision parameter of ellinit():: - - sage: R = RealField(150) - sage: e = pari([0,0,0,-82,0]).ellinit(precision=150) - sage: eta1 = e.elleta()[0] - sage: R(eta1) - 3.6054636014326520859158205642077267748102690 - -Number fields and precision: TODO - -TESTS: - -Check that output from PARI's print command is actually seen by -Sage (ticket #9636):: - - sage: pari('print("test")') - test +- Peter Bruin (2013-11-17): move PariInstance to a separate file + (#15185) """ diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx new file mode 100644 index 00000000000..ab5e49ccf61 --- /dev/null +++ b/src/sage/libs/pari/pari_instance.pyx @@ -0,0 +1,154 @@ +""" +PARI C-library interface + +AUTHORS: + +- William Stein (2006-03-01): updated to work with PARI 2.2.12-beta + +- William Stein (2006-03-06): added newtonpoly + +- Justin Walker: contributed some of the function definitions + +- Gonzalo Tornaria: improvements to conversions; much better error + handling. + +- Robert Bradshaw, Jeroen Demeyer, William Stein (2010-08-15): + Upgrade to PARI 2.4.3 (#9343) + +- Jeroen Demeyer (2011-11-12): rewrite various conversion routines + (#11611, #11854, #11952) + +- Peter Bruin (2013-11-17): split off this file from gen.pyx (#15185) + + +EXAMPLES:: + + sage: pari('5! + 10/x') + (120*x + 10)/x + sage: pari('intnum(x=0,13,sin(x)+sin(x^2) + x)') + 85.1885681951527 + sage: f = pari('x^3-1') + sage: v = f.factor(); v + [x - 1, 1; x^2 + x + 1, 1] + sage: v[0] # indexing is 0-based unlike in GP. + [x - 1, x^2 + x + 1]~ + sage: v[1] + [1, 1]~ + +Arithmetic obeys the usual coercion rules:: + + sage: type(pari(1) + 1) + + sage: type(1 + pari(1)) + + +GUIDE TO REAL PRECISION AND THE PARI LIBRARY + +The default real precision in communicating with the Pari library +is the same as the default Sage real precision, which is 53 bits. +Inexact Pari objects are therefore printed by default to 15 decimal +digits (even if they are actually more precise). + +Default precision example (53 bits, 15 significant decimals):: + + sage: a = pari(1.23); a + 1.23000000000000 + sage: a.sin() + 0.942488801931698 + +Example with custom precision of 200 bits (60 significant +decimals):: + + sage: R = RealField(200) + sage: a = pari(R(1.23)); a # only 15 significant digits printed + 1.23000000000000 + sage: R(a) # but the number is known to precision of 200 bits + 1.2300000000000000000000000000000000000000000000000000000000 + sage: a.sin() # only 15 significant digits printed + 0.942488801931698 + sage: R(a.sin()) # but the number is known to precision of 200 bits + 0.94248880193169751002382356538924454146128740562765030213504 + +It is possible to change the number of printed decimals:: + + sage: R = RealField(200) # 200 bits of precision in computations + sage: old_prec = pari.set_real_precision(60) # 60 decimals printed + sage: a = pari(R(1.23)); a + 1.23000000000000000000000000000000000000000000000000000000000 + sage: a.sin() + 0.942488801931697510023823565389244541461287405627650302135038 + sage: pari.set_real_precision(old_prec) # restore the default printing behavior + 60 + +Unless otherwise indicated in the docstring, most Pari functions +that return inexact objects use the precision of their arguments to +decide the precision of the computation. However, if some of these +arguments happen to be exact numbers (integers, rationals, etc.), +an optional parameter indicates the precision (in bits) to which +these arguments should be converted before the computation. If this +precision parameter is missing, the default precision of 53 bits is +used. The following first converts 2 into a real with 53-bit +precision:: + + sage: R = RealField() + sage: R(pari(2).sin()) + 0.909297426825682 + +We can ask for a better precision using the optional parameter:: + + sage: R = RealField(150) + sage: R(pari(2).sin(precision=150)) + 0.90929742682568169539601986591174484270225497 + +Warning regarding conversions Sage - Pari - Sage: Some care must be +taken when juggling inexact types back and forth between Sage and +Pari. In theory, calling p=pari(s) creates a Pari object p with the +same precision as s; in practice, the Pari library's precision is +word-based, so it will go up to the next word. For example, a +default 53-bit Sage real s will be bumped up to 64 bits by adding +bogus 11 bits. The function p.python() returns a Sage object with +exactly the same precision as the Pari object p. So +pari(s).python() is definitely not equal to s, since it has 64 bits +of precision, including the bogus 11 bits. The correct way of +avoiding this is to coerce pari(s).python() back into a domain with +the right precision. This has to be done by the user (or by Sage +functions that use Pari library functions in gen.pyx). For +instance, if we want to use the Pari library to compute sqrt(pi) +with a precision of 100 bits:: + + sage: R = RealField(100) + sage: s = R(pi); s + 3.1415926535897932384626433833 + sage: p = pari(s).sqrt() + sage: x = p.python(); x # wow, more digits than I expected! + 1.7724538509055160272981674833410973484 + sage: x.prec() # has precision 'improved' from 100 to 128? + 128 + sage: x == RealField(128)(pi).sqrt() # sadly, no! + False + sage: R(x) # x should be brought back to precision 100 + 1.7724538509055160272981674833 + sage: R(x) == s.sqrt() + True + +Elliptic curves and precision: If you are working with elliptic +curves and want to compute with a precision other than the default +53 bits, you should use the precision parameter of ellinit():: + + sage: R = RealField(150) + sage: e = pari([0,0,0,-82,0]).ellinit(precision=150) + sage: eta1 = e.elleta()[0] + sage: R(eta1) + 3.6054636014326520859158205642077267748102690 + +Number fields and precision: TODO + +TESTS: + +Check that output from PARI's print command is actually seen by +Sage (ticket #9636):: + + sage: pari('print("test")') + test + +""" From 478dd9042eee6c7518d7d28d17c372621b296be0 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Tue, 10 Dec 2013 22:29:51 +0000 Subject: [PATCH 110/206] move code from gen.pyx to pari_instance.pyx --- src/sage/libs/pari/gen.pyx | 1600 +------------------------- src/sage/libs/pari/pari_instance.pyx | 1484 ++++++++++++++++++++++++ 2 files changed, 1542 insertions(+), 1542 deletions(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index aff91c0ac33..1f72d1cb1ca 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -65,194 +65,9 @@ cdef extern from "misc.h": int factorint_withproof_sage(GEN* ans, GEN x, GEN cutoff) int gcmp_sage(GEN x, GEN y) -cdef extern from "mpz_pylong.h": - cdef int mpz_set_pylong(mpz_t dst, src) except -1 - # Will be imported as needed Integer = None -# so Galois groups are represented in a sane way -# See the polgalois section of the PARI users manual. -new_galois_format = 1 - -cdef pari_sp mytop - -# real precision in decimal digits: see documentation for -# get_real_precision() and set_real_precision(). This variable is used -# in gp to set the precision of input quantities (e.g. sqrt(2)), and for -# determining the number of digits to be printed. It is *not* used as -# a "default precision" for internal computations, which always use -# the actual precision of arguments together (where relevant) with a -# "prec" parameter. In ALL cases (for real computations) the prec -# parameter is a WORD precision and NOT decimal precision. Pari reals -# with word precision w have bit precision (of the mantissa) equal to -# 32*(w-2) or 64*(w-2). -# -# Hence the only relevance of this parameter in Sage is (1) for the -# output format of components of objects of type -# 'sage.libs.pari.gen.gen'; (2) for setting the precision of pari -# variables created from strings (e.g. via sage: pari('1.2')). -# -# WARNING: Many pari library functions take a last parameter "prec" -# which should be a words precision. In many cases this is redundant -# and is simply ignored. In our wrapping of these functions we use -# the variable prec here for convenience only. -cdef unsigned long prec - -################################################################# -# conversions between various real precision models -################################################################# - -def prec_bits_to_dec(int prec_in_bits): - r""" - Convert from precision expressed in bits to precision expressed in - decimal. - - EXAMPLES:: - - sage: import sage.libs.pari.gen as gen - sage: gen.prec_bits_to_dec(53) - 15 - sage: [(32*n,gen.prec_bits_to_dec(32*n)) for n in range(1,9)] - [(32, 9), - (64, 19), - (96, 28), - (128, 38), - (160, 48), - (192, 57), - (224, 67), - (256, 77)] - """ - log_2 = 0.301029995663981 - return int(prec_in_bits*log_2) - -def prec_dec_to_bits(int prec_in_dec): - r""" - Convert from precision expressed in decimal to precision expressed - in bits. - - EXAMPLES:: - - sage: import sage.libs.pari.gen as gen - sage: gen.prec_dec_to_bits(15) - 49 - sage: [(n,gen.prec_dec_to_bits(n)) for n in range(10,100,10)] - [(10, 33), - (20, 66), - (30, 99), - (40, 132), - (50, 166), - (60, 199), - (70, 232), - (80, 265), - (90, 298)] - """ - log_10 = 3.32192809488736 - return int(prec_in_dec*log_10) - -def prec_bits_to_words(int prec_in_bits=0): - r""" - Convert from precision expressed in bits to pari real precision - expressed in words. Note: this rounds up to the nearest word, - adjusts for the two codewords of a pari real, and is - architecture-dependent. - - EXAMPLES:: - - sage: import sage.libs.pari.gen as gen - sage: gen.prec_bits_to_words(70) - 5 # 32-bit - 4 # 64-bit - - :: - - sage: [(32*n,gen.prec_bits_to_words(32*n)) for n in range(1,9)] - [(32, 3), (64, 4), (96, 5), (128, 6), (160, 7), (192, 8), (224, 9), (256, 10)] # 32-bit - [(32, 3), (64, 3), (96, 4), (128, 4), (160, 5), (192, 5), (224, 6), (256, 6)] # 64-bit - """ - if not prec_in_bits: - return prec - cdef int wordsize - wordsize = BITS_IN_LONG - - # increase prec_in_bits to the nearest multiple of wordsize - cdef int padded_bits - padded_bits = (prec_in_bits + wordsize - 1) & ~(wordsize - 1) - return int(padded_bits/wordsize + 2) - -pbw = prec_bits_to_words - -def prec_words_to_bits(int prec_in_words): - r""" - Convert from pari real precision expressed in words to precision - expressed in bits. Note: this adjusts for the two codewords of a - pari real, and is architecture-dependent. - - EXAMPLES:: - - sage: import sage.libs.pari.gen as gen - sage: gen.prec_words_to_bits(10) - 256 # 32-bit - 512 # 64-bit - sage: [(n,gen.prec_words_to_bits(n)) for n in range(3,10)] - [(3, 32), (4, 64), (5, 96), (6, 128), (7, 160), (8, 192), (9, 224)] # 32-bit - [(3, 64), (4, 128), (5, 192), (6, 256), (7, 320), (8, 384), (9, 448)] # 64-bit - """ - # see user's guide to the pari library, page 10 - return int((prec_in_words - 2) * BITS_IN_LONG) - -def prec_dec_to_words(int prec_in_dec): - r""" - Convert from precision expressed in decimal to precision expressed - in words. Note: this rounds up to the nearest word, adjusts for the - two codewords of a pari real, and is architecture-dependent. - - EXAMPLES:: - - sage: import sage.libs.pari.gen as gen - sage: gen.prec_dec_to_words(38) - 6 # 32-bit - 4 # 64-bit - sage: [(n,gen.prec_dec_to_words(n)) for n in range(10,90,10)] - [(10, 4), (20, 5), (30, 6), (40, 7), (50, 8), (60, 9), (70, 10), (80, 11)] # 32-bit - [(10, 3), (20, 4), (30, 4), (40, 5), (50, 5), (60, 6), (70, 6), (80, 7)] # 64-bit - """ - return prec_bits_to_words(prec_dec_to_bits(prec_in_dec)) - -def prec_words_to_dec(int prec_in_words): - r""" - Convert from precision expressed in words to precision expressed in - decimal. Note: this adjusts for the two codewords of a pari real, - and is architecture-dependent. - - EXAMPLES:: - - sage: import sage.libs.pari.gen as gen - sage: gen.prec_words_to_dec(5) - 28 # 32-bit - 57 # 64-bit - sage: [(n,gen.prec_words_to_dec(n)) for n in range(3,10)] - [(3, 9), (4, 19), (5, 28), (6, 38), (7, 48), (8, 57), (9, 67)] # 32-bit - [(3, 19), (4, 38), (5, 57), (6, 77), (7, 96), (8, 115), (9, 134)] # 64-bit - """ - return prec_bits_to_dec(prec_words_to_bits(prec_in_words)) - - -# The unique running Pari instance. -cdef PariInstance pari_instance, P -pari_instance = PariInstance() -P = pari_instance # shorthand notation - -# PariInstance.__init__ must not create gen objects because their parent is not constructed yet -pari_catch_sig_on() -pari_instance.PARI_ZERO = pari_instance.new_gen_noclear(gen_0) -pari_instance.PARI_ONE = pari_instance.new_gen_noclear(gen_1) -pari_instance.PARI_TWO = pari_instance.new_gen_noclear(gen_2) -pari_catch_sig_off() - -# Also a copy of PARI accessible from external pure python code. -pari = pari_instance - @cython.final cdef class gen(sage.structure.element.RingElement): @@ -9103,1380 +8918,81 @@ cdef class gen(sage.structure.element.RingElement): return P.get_var(v) +def init_pari_stack(s=8000000): + """ + Deprecated, use ``pari.allocatemem()`` instead. -cdef unsigned long num_primes - -# Callbacks from PARI to print stuff using sys.stdout.write() instead -# of C library functions like puts(). -cdef PariOUT sage_pariOut - -cdef void sage_putchar(char c): - cdef char s[2] - s[0] = c - s[1] = 0 - sys.stdout.write(s) - # Let PARI think the last character was a newline, - # so it doesn't print one when an error occurs. - pari_set_last_newline(1) - -cdef void sage_puts(char* s): - sys.stdout.write(s) - pari_set_last_newline(1) - -cdef void sage_flush(): - sys.stdout.flush() - -cdef PariOUT sage_pariErr - -cdef void sage_pariErr_putchar(char c): - cdef char s[2] - s[0] = c - s[1] = 0 - global pari_error_string - pari_error_string += str(s) - pari_set_last_newline(1) - -cdef void sage_pariErr_puts(char *s): - global pari_error_string - pari_error_string += str(s) - pari_set_last_newline(1) - -cdef void sage_pariErr_flush(): - pass - - -@cython.final -cdef class PariInstance(sage.structure.parent_base.ParentWithBase): - def __init__(self, long size=1000000, unsigned long maxprime=500000): - """ - Initialize the PARI system. - - INPUT: - - - - ``size`` -- long, the number of bytes for the initial - PARI stack (see note below) - - - ``maxprime`` -- unsigned long, upper limit on a - precomputed prime number table (default: 500000) - - - .. note:: - - In Sage, the PARI stack is different than in GP or the - PARI C library. In Sage, instead of the PARI stack - holding the results of all computations, it *only* holds - the results of an individual computation. Each time a new - Python/PARI object is computed, it it copied to its own - space in the Python heap, and the memory it occupied on the - PARI stack is freed. Thus it is not necessary to make the - stack very large. Also, unlike in PARI, if the stack does - overflow, in most cases the PARI stack is automatically - increased and the relevant step of the computation rerun. - - This design obviously involves some performance penalties - over the way PARI works, but it scales much better and is - far more robust for large projects. - - .. note:: - - If you do not want prime numbers, put ``maxprime=2``, but be - careful because many PARI functions require this table. If - you get the error message "not enough precomputed primes", - increase this parameter. - """ - if bot: - return # pari already initialized. - - global num_primes, avma, top, bot, prec - - # The size here doesn't really matter, because we will allocate - # our own stack anyway. We ask PARI not to set up signal and - # error handlers. - pari_init_opts(10000, maxprime, INIT_DFTm) - - _pari_init_error_handling() - - num_primes = maxprime - - # Free the PARI stack and allocate our own (using Cython) - pari_free(bot); bot = 0 - init_stack(size) - - GP_DATA.fmt.prettyp = 0 - - # how do I get the following to work? seems to be a circular import - #from sage.rings.real_mpfr import RealField - #prec_bits = RealField().prec() - prec = prec_bits_to_words(53) - GP_DATA.fmt.sigd = prec_bits_to_dec(53) - - # Set printing functions - global pariOut, pariErr - - pariOut = &sage_pariOut - pariOut.putch = sage_putchar - pariOut.puts = sage_puts - pariOut.flush = sage_flush - - pariErr = &sage_pariErr - pariErr.putch = sage_pariErr_putchar - pariErr.puts = sage_pariErr_puts - pariErr.flush = sage_pariErr_flush - - def debugstack(self): - r""" - Print the internal PARI variables ``top`` (top of stack), ``avma`` - (available memory address, think of this as the stack pointer), - ``bot`` (bottom of stack). - - EXAMPLE:: - - sage: pari.debugstack() # random - top = 0x60b2c60 - avma = 0x5875c38 - bot = 0x57295e0 - size = 1000000 - """ - # We deliberately use low-level functions to minimize the - # chances that something goes wrong here (for example, if we - # are out of memory). - global avma, top, bot - printf("top = %p\navma = %p\nbot = %p\nsize = %lu\n", top, avma, bot, (top) - (bot)) - fflush(stdout) - - def __dealloc__(self): - """ - Deallocation of the Pari instance. - - NOTE: - - Usually this deallocation happens only when Sage quits. - We do not provide a direct test, since usually there - is only one Pari instance, and when artificially creating - another instance, C-data are shared. - - The fact that Sage does not crash when quitting is an - indirect doctest. See the discussion at :trac:`13741`. - - """ - if bot: - sage_free(bot) - global top, bot, avma - top = 0 - bot = 0 - avma = 0 - pari_close() - - def __repr__(self): - return "Interface to the PARI C library" - - def __hash__(self): - return 907629390 # hash('pari') - - cdef has_coerce_map_from_c_impl(self, x): - return True - - def __richcmp__(left, right, int op): - """ - EXAMPLES:: - - sage: pari == pari - True - sage: pari == gp - False - sage: pari == 5 - False - """ - return (left)._richcmp(right, op) - - def default(self, variable, value=None): - if not value is None: - return self('default(%s, %s)'%(variable, value)) - return self('default(%s)'%variable) - - def set_debug_level(self, level): - """ - Set the debug PARI C library variable. - """ - self.default('debug', int(level)) - - def get_debug_level(self): - """ - Set the debug PARI C library variable. - """ - return int(self.default('debug')) - - def set_real_precision(self, long n): - """ - Sets the PARI default real precision. - - This is used both for creation of new objects from strings and for - printing. It is the number of digits *IN DECIMAL* in which real - numbers are printed. It also determines the precision of objects - created by parsing strings (e.g. pari('1.2')), which is *not* the - normal way of creating new pari objects in Sage. It has *no* - effect on the precision of computations within the pari library. - - Returns the previous PARI real precision. - """ - cdef unsigned long k - - k = GP_DATA.fmt.sigd - s = str(n) - pari_catch_sig_on() - sd_realprecision(s, 2) - pari_catch_sig_off() - return int(k) # Python int - - def get_real_precision(self): - """ - Returns the current PARI default real precision. - - This is used both for creation of new objects from strings and for - printing. It is the number of digits *IN DECIMAL* in which real - numbers are printed. It also determines the precision of objects - created by parsing strings (e.g. pari('1.2')), which is *not* the - normal way of creating new pari objects in Sage. It has *no* - effect on the precision of computations within the pari library. - """ - return GP_DATA.fmt.sigd - - def set_series_precision(self, long n): - global precdl - precdl = n - - def get_series_precision(self): - return precdl - - cdef inline void clear_stack(self): - """ - Call ``pari_catch_sig_off()``, and clear the entire PARI stack - if we are leaving the outermost ``pari_catch_sig_on() ... - pari_catch_sig_off()`` block. - - """ - global mytop, avma - if _signals.sig_on_count <= 1: - avma = mytop - pari_catch_sig_off() - - cdef inline gen new_gen(self, GEN x): - """ - Create a new gen wrapping `x`, then call ``clear_stack()``. - """ - cdef gen g = _new_gen(x) - self.clear_stack() - return g - - cdef inline gen new_gen_noclear(self, GEN x): - """ - Create a new gen, but don't free any memory on the stack and don't - call pari_catch_sig_off(). - """ - z = _new_gen(x) - return z - - cdef gen new_gen_from_mpz_t(self, mpz_t value): - """ - Create a new gen from a given MPIR-integer ``value``. - - EXAMPLES:: - - sage: pari(42) # indirect doctest - 42 - - TESTS: - - Check that the hash of an integer does not depend on existing - garbage on the stack (#11611):: - - sage: foo = pari(2^(32*1024)); # Create large integer to put PARI stack in known state - sage: a5 = pari(5); - sage: foo = pari(0xDEADBEEF * (2^(32*1024)-1)//(2^32 - 1)); # Dirty PARI stack - sage: b5 = pari(5); - sage: a5.__hash__() == b5.__hash__() - True - """ - pari_catch_sig_on() - return self.new_gen(self._new_GEN_from_mpz_t(value)) + EXAMPLES:: - cdef inline GEN _new_GEN_from_mpz_t(self, mpz_t value): - r""" - Create a new PARI ``t_INT`` from a ``mpz_t``. + sage: from sage.libs.pari.gen import init_pari_stack + sage: init_pari_stack() + doctest:...: DeprecationWarning: init_pari_stack() is deprecated; use pari.allocatemem() instead. + See http://trac.sagemath.org/10018 for details. + sage: pari.stacksize() + 8000000 + """ + from sage.misc.superseded import deprecation + deprecation(10018, 'init_pari_stack() is deprecated; use pari.allocatemem() instead.') + P.allocatemem(s, silent=True) - For internal use only; this directly uses the PARI stack. - One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. - """ - cdef unsigned long limbs = mpz_size(value) - cdef GEN z = cgeti(limbs + 2) - # Set sign and "effective length" - z[1] = evalsigne(mpz_sgn(value)) + evallgefint(limbs + 2) - mpz_export(int_LSW(z), NULL, -1, sizeof(long), 0, 0, value) +cdef gen objtogen(s): + """Convert any Sage/Python object to a PARI gen""" + cdef GEN g + cdef Py_ssize_t length, i + cdef mpz_t mpz_int + cdef gen v - return z + if isinstance(s, gen): + return s + try: + return s._pari_() + except AttributeError: + pass - cdef gen new_gen_from_int(self, int value): + # Check basic Python types. Start with strings, which are a very + # common case. + if PyString_Check(s): pari_catch_sig_on() - return self.new_gen(stoi(value)) - - cdef gen new_gen_from_mpq_t(self, mpq_t value): - """ - Create a new gen from a given MPIR-rational ``value``. - - EXAMPLES:: - - sage: pari(-2/3) - -2/3 - sage: pari(QQ(42)) - 42 - sage: pari(QQ(42)).type() - 't_INT' - sage: pari(QQ(1/42)).type() - 't_FRAC' - - TESTS: - - Check that the hash of a rational does not depend on existing - garbage on the stack (#11854):: - - sage: foo = pari(2^(32*1024)); # Create large integer to put PARI stack in known state - sage: a5 = pari(5/7); - sage: foo = pari(0xDEADBEEF * (2^(32*1024)-1)//(2^32 - 1)); # Dirty PARI stack - sage: b5 = pari(5/7); - sage: a5.__hash__() == b5.__hash__() - True - """ + g = gp_read_str(PyString_AsString(s)) + if g == gnil: + P.clear_stack() + return None + return P.new_gen(g) + if PyInt_Check(s): pari_catch_sig_on() - return self.new_gen(self._new_GEN_from_mpq_t(value)) - - cdef inline GEN _new_GEN_from_mpq_t(self, mpq_t value): - r""" - Create a new PARI ``t_INT`` or ``t_FRAC`` from a ``mpq_t``. - - For internal use only; this directly uses the PARI stack. - One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. - """ - cdef GEN num = self._new_GEN_from_mpz_t(mpq_numref(value)) - if mpz_cmpabs_ui(mpq_denref(value), 1) == 0: - # Denominator is 1, return the numerator (an integer) - return num - cdef GEN denom = self._new_GEN_from_mpz_t(mpq_denref(value)) - return mkfrac(num, denom) - - cdef gen new_t_POL_from_int_star(self, int *vals, int length, long varnum): - """ - Note that degree + 1 = length, so that recognizing 0 is easier. - - varnum = 0 is the general choice (creates a variable in x). - """ - cdef GEN z - cdef int i - + return P.new_gen(stoi(PyInt_AS_LONG(s))) + if PyBool_Check(s): + return P.PARI_ONE if s else P.PARI_ZERO + if PyLong_Check(s): pari_catch_sig_on() - z = cgetg(length + 2, t_POL) - z[1] = evalvarn(varnum) - if length != 0: - setsigne(z,1) - for i from 0 <= i < length: - set_gel(z,i+2, stoi(vals[i])) - else: - ## polynomial is zero - setsigne(z,0) - - return self.new_gen(z) - - cdef gen new_gen_from_padic(self, long ordp, long relprec, - mpz_t prime, mpz_t p_pow, mpz_t unit): - cdef GEN z + mpz_init(mpz_int) + mpz_set_pylong(mpz_int, s) + g = P._new_GEN_from_mpz_t(mpz_int) + mpz_clear(mpz_int) + return P.new_gen(g) + if PyFloat_Check(s): pari_catch_sig_on() - z = cgetg(5, t_PADIC) - z[1] = evalprecp(relprec) + evalvalp(ordp) - set_gel(z, 2, self._new_GEN_from_mpz_t(prime)) - set_gel(z, 3, self._new_GEN_from_mpz_t(p_pow)) - set_gel(z, 4, self._new_GEN_from_mpz_t(unit)) - return self.new_gen(z) - - def double_to_gen(self, x): - cdef double dx - dx = float(x) - return self.double_to_gen_c(dx) - - cdef gen double_to_gen_c(self, double x): - """ - Create a new gen with the value of the double x, using Pari's - dbltor. - - EXAMPLES:: - - sage: pari.double_to_gen(1) - 1.00000000000000 - sage: pari.double_to_gen(1e30) - 1.00000000000000 E30 - sage: pari.double_to_gen(0) - 0.E-15 - sage: pari.double_to_gen(-sqrt(RDF(2))) - -1.41421356237310 - """ - # Pari has an odd concept where it attempts to track the accuracy - # of floating-point 0; a floating-point zero might be 0.0e-20 - # (meaning roughly that it might represent any number in the - # range -1e-20 <= x <= 1e20). - - # Pari's dbltor converts a floating-point 0 into the Pari real - # 0.0e-307; Pari treats this as an extremely precise 0. This - # can cause problems; for instance, the Pari incgam() function can - # be very slow if the first argument is very precise. - - # So we translate 0 into a floating-point 0 with 53 bits - # of precision (that's the number of mantissa bits in an IEEE - # double). - - pari_catch_sig_on() - if x == 0: - return self.new_gen(real_0_bit(-53)) - else: - return self.new_gen(dbltor(x)) - - cdef GEN double_to_GEN(self, double x): - if x == 0: - return real_0_bit(-53) - else: - return dbltor(x) - - def complex(self, re, im): - """ - Create a new complex number, initialized from re and im. - """ - cdef gen t0 = self(re) - cdef gen t1 = self(im) + return P.new_gen(dbltor(PyFloat_AS_DOUBLE(s))) + if PyComplex_Check(s): pari_catch_sig_on() - return self.new_gen(mkcomplex(t0.g, t1.g)) - - cdef GEN deepcopy_to_python_heap(self, GEN x, pari_sp* address): - return deepcopy_to_python_heap(x, address) - - cdef gen new_ref(self, GEN g, gen parent): - """ - Create a new gen pointing to the given GEN, which is allocated as a - part of parent.g. + g = cgetg(3, t_COMPLEX) + set_gel(g, 1, dbltor(PyComplex_RealAsDouble(s))) + set_gel(g, 2, dbltor(PyComplex_ImagAsDouble(s))) + return P.new_gen(g) - .. note:: - - As a rule, there should never be more than one sage gen - pointing to a given Pari GEN. So that means there is only - one case where this function should be used: when a - complicated Pari GEN is allocated with a single gen - pointing to it, and one needs a gen pointing to one of its - components. - - For example, doing x = pari("[1,2]") allocates a gen pointing to - the list [1,2], but x[0] has no gen wrapping it, so new_ref - should be used there. Then parent would be x in this - case. See __getitem__ for an example of usage. - - EXAMPLES:: - - sage: pari("[[1,2],3]")[0][1] ## indirect doctest - 2 - """ - cdef gen p = PY_NEW(gen) - - p.b = 0 - p._parent = self - p._refers_to = {-1:parent} - p.g = g - - return p - - def __call__(self, s): - """ - Create the PARI object obtained by evaluating s using PARI. - - EXAMPLES:: - - sage: pari([2,3,5]) - [2, 3, 5] - sage: pari(Matrix(2,2,range(4))) - [0, 1; 2, 3] - sage: pari(x^2-3) - x^2 - 3 - - :: - - sage: a = pari(1); a, a.type() - (1, 't_INT') - sage: a = pari(1/2); a, a.type() - (1/2, 't_FRAC') - sage: a = pari(1/2); a, a.type() - (1/2, 't_FRAC') - - See :func:`pari` for more examples. - """ - return objtogen(s) - - cdef GEN _new_GEN_from_mpz_t_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc): - r""" - Create a new PARI ``t_MAT`` with ``nr`` rows and ``nc`` columns - from a ``mpz_t**``. - - For internal use only; this directly uses the PARI stack. - One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. - """ - cdef GEN x - cdef GEN A = zeromatcopy(nr, nc) - cdef Py_ssize_t i, j - for i in range(nr): - for j in range(nc): - x = self._new_GEN_from_mpz_t(B[i][j]) - set_gcoeff(A, i+1, j+1, x) # A[i+1, j+1] = x (using 1-based indexing) - return A - - cdef GEN _new_GEN_from_mpz_t_matrix_rotate90(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc): - r""" - Create a new PARI ``t_MAT`` with ``nr`` rows and ``nc`` columns - from a ``mpz_t**`` and rotate the matrix 90 degrees - counterclockwise. So the resulting matrix will have ``nc`` rows - and ``nr`` columns. This is useful for computing the Hermite - Normal Form because Sage and PARI use different definitions. - - For internal use only; this directly uses the PARI stack. - One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. - """ - cdef GEN x - cdef GEN A = zeromatcopy(nc, nr) - cdef Py_ssize_t i, j - for i in range(nr): - for j in range(nc): - x = self._new_GEN_from_mpz_t(B[i][nc-j-1]) - set_gcoeff(A, j+1, i+1, x) # A[j+1, i+1] = x (using 1-based indexing) - return A - - cdef gen integer_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc, bint permute_for_hnf): - """ - EXAMPLES:: - - sage: matrix(ZZ,2,[1..6])._pari_() # indirect doctest - [1, 2, 3; 4, 5, 6] - """ - pari_catch_sig_on() - cdef GEN g - if permute_for_hnf: - g = self._new_GEN_from_mpz_t_matrix_rotate90(B, nr, nc) - else: - g = self._new_GEN_from_mpz_t_matrix(B, nr, nc) - return self.new_gen(g) - - cdef GEN _new_GEN_from_mpq_t_matrix(self, mpq_t** B, Py_ssize_t nr, Py_ssize_t nc): - cdef GEN x - # Allocate zero matrix - cdef GEN A = zeromatcopy(nr, nc) - cdef Py_ssize_t i, j - for i in range(nr): - for j in range(nc): - x = self._new_GEN_from_mpq_t(B[i][j]) - set_gcoeff(A, i+1, j+1, x) # A[i+1, j+1] = x (using 1-based indexing) - return A - - cdef gen rational_matrix(self, mpq_t** B, Py_ssize_t nr, Py_ssize_t nc): - """ - EXAMPLES:: - - sage: matrix(QQ,2,[1..6])._pari_() # indirect doctest - [1, 2, 3; 4, 5, 6] - """ - pari_catch_sig_on() - cdef GEN g = self._new_GEN_from_mpq_t_matrix(B, nr, nc) - return self.new_gen(g) - - cdef _coerce_c_impl(self, x): - """ - Implicit canonical coercion into a PARI object. - """ - try: - return self(x) - except (TypeError, AttributeError): - raise TypeError("no canonical coercion of %s into PARI"%x) - - cdef _an_element_c_impl(self): # override this in Cython - return self.PARI_ZERO - - def new_with_bits_prec(self, s, long precision): - r""" - pari.new_with_bits_prec(self, s, precision) creates s as a PARI - gen with (at most) precision *bits* of precision. - """ - cdef unsigned long old_prec - old_prec = GP_DATA.fmt.sigd - precision = prec_bits_to_dec(precision) - if not precision: - precision = old_prec - self.set_real_precision(precision) - x = self(s) - self.set_real_precision(old_prec) - return x - - - - cdef long get_var(self, v): - """ - Converts a Python string into a PARI variable reference number. Or - if v = -1, returns -1. - """ - if v != -1: - s = str(v) - return fetch_user_var(s) - return -1 - - ############################################################ - # Initialization - ############################################################ - - def stacksize(self): - r""" - Returns the current size of the PARI stack, which is `10^6` - by default. However, the stack size is automatically doubled - when needed. It can also be set directly using - ``pari.allocatemem()``. - - EXAMPLES:: - - sage: pari.stacksize() - 1000000 - - """ - global top, bot - return ((top) - (bot)) - - def _allocate_huge_mem(self): - r""" - This function tries to allocate an impossibly large amount of - PARI stack in order to test ``init_stack()`` failing. - - TESTS:: - - sage: pari.allocatemem(10^6, silent=True) - sage: pari._allocate_huge_mem() - Traceback (most recent call last): - ... - MemoryError: Unable to allocate ... (instead, allocated 1000000 bytes) - - Test that the PARI stack is sane after this failure:: - - sage: a = pari('2^10000000') - sage: pari.allocatemem(10^6, silent=True) - """ - # Since size_t is unsigned, this should wrap over to a very - # large positive number. - init_stack((-4096)) - - def _setup_failed_retry(self): - r""" - This function pretends that the PARI stack is larger than it - actually is such that allocatemem(0) will allocate much more - than double the current stack. That allocation will then fail. - This function is meant to be used only in this doctest. - - TESTS:: - - sage: pari.allocatemem(4096, silent=True) - sage: pari._setup_failed_retry() - sage: try: # Any result is fine as long as it's not a segfault - ....: a = pari('2^1000000') - ....: except MemoryError: - ....: pass - sage: pari.allocatemem(10^6, silent=True) - """ - global top - # Pretend top is at a very high address - top = ((-16)) - - def allocatemem(self, s=0, silent=False): - r""" - Change the PARI stack space to the given size (or double the - current size if ``s`` is `0`). - - If `s = 0` and insufficient memory is avaible to double, the - PARI stack will be enlarged by a smaller amount. In any case, - a ``MemoryError`` will be raised if the requested memory cannot - be allocated. - - The PARI stack is never automatically shrunk. You can use the - command ``pari.allocatemem(10^6)`` to reset the size to `10^6`, - which is the default size at startup. Note that the results of - computations using Sage's PARI interface are copied to the - Python heap, so they take up no space in the PARI stack. - The PARI stack is cleared after every computation. - - It does no real harm to set this to a small value as the PARI - stack will be automatically doubled when we run out of memory. - However, it could make some calculations slower (since they have - to be redone from the start after doubling the stack until the - stack is big enough). - - INPUT: - - - ``s`` - an integer (default: 0). A non-zero argument should - be the size in bytes of the new PARI stack. If `s` is zero, - try to double the current stack size. - - EXAMPLES:: - - sage: pari.allocatemem(10^7) - PARI stack size set to 10000000 bytes - sage: pari.allocatemem() # Double the current size - PARI stack size set to 20000000 bytes - sage: pari.stacksize() - 20000000 - sage: pari.allocatemem(10^6) - PARI stack size set to 1000000 bytes - - The following computation will automatically increase the PARI - stack size:: - - sage: a = pari('2^100000000') - - ``a`` is now a Python variable on the Python heap and does not - take up any space on the PARI stack. The PARI stack is still - large because of the computation of ``a``:: - - sage: pari.stacksize() - 16000000 - sage: pari.allocatemem(10^6) - PARI stack size set to 1000000 bytes - sage: pari.stacksize() - 1000000 - - TESTS: - - Do the same without using the string interface and starting - from a very small stack size:: - - sage: pari.allocatemem(1) - PARI stack size set to 1024 bytes - sage: a = pari(2)^100000000 - sage: pari.stacksize() - 16777216 - """ - s = long(s) - if s < 0: - raise ValueError("Stack size must be nonnegative") - init_stack(s) - if not silent: - print "PARI stack size set to", self.stacksize(), "bytes" - - def pari_version(self): - return str(PARIVERSION) - - def init_primes(self, _M): - """ - Recompute the primes table including at least all primes up to M - (but possibly more). - - EXAMPLES:: - - sage: pari.init_primes(200000) - - We make sure that ticket #11741 has been fixed, and double check to - make sure that diffptr has not been freed:: - - sage: pari.init_primes(2^62) - Traceback (most recent call last): - ... - PariError: not enough memory # 64-bit - OverflowError: long int too large to convert # 32-bit - sage: pari.init_primes(200000) - """ - cdef unsigned long M - cdef char *tmpptr - M = _M - global diffptr, num_primes - if M <= num_primes: - return - pari_catch_sig_on() - tmpptr = initprimes(M) - pari_catch_sig_off() - pari_free( diffptr) - num_primes = M - diffptr = tmpptr - - - ############################################## - ## Support for GP Scripts - ############################################## - - def read(self, bytes filename): - r""" - Read a script from the named filename into the interpreter. The - functions defined in the script are then available for use from - Sage/PARI. The result of the last command in ``filename`` is - returned. - - EXAMPLES: - - Create a gp file:: - - sage: import tempfile - sage: gpfile = tempfile.NamedTemporaryFile(mode="w") - sage: gpfile.file.write("mysquare(n) = {\n") - sage: gpfile.file.write(" n^2;\n") - sage: gpfile.file.write("}\n") - sage: gpfile.file.write("polcyclo(5)\n") - sage: gpfile.file.flush() - - Read it in Sage, we get the result of the last line:: - - sage: pari.read(gpfile.name) - x^4 + x^3 + x^2 + x + 1 - - Call the function defined in the gp file:: - - sage: pari('mysquare(12)') - 144 - """ - pari_catch_sig_on() - return self.new_gen(gp_read_file(filename)) - - - ############################################## - - def _primelimit(self): - """ - Return the number of primes already computed - in this Pari instance. - - EXAMPLES: - sage: pari._primelimit() - 500519 - sage: pari.init_primes(600000) - sage: pari._primelimit() - 600000 - """ - global num_primes - from sage.rings.all import ZZ - return ZZ(num_primes) - - def prime_list(self, long n): - """ - prime_list(n): returns list of the first n primes - - To extend the table of primes use pari.init_primes(M). - - INPUT: - - - - ``n`` - C long - - - OUTPUT: - - - - ``gen`` - PARI list of first n primes - - - EXAMPLES:: - - sage: pari.prime_list(0) - [] - sage: pari.prime_list(-1) - [] - sage: pari.prime_list(3) - [2, 3, 5] - sage: pari.prime_list(10) - [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] - sage: pari.prime_list(20) - [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71] - sage: len(pari.prime_list(1000)) - 1000 - """ - if n >= 2: - self.nth_prime(n) - pari_catch_sig_on() - return self.new_gen(primes(n)) - - def primes_up_to_n(self, long n): - """ - Return the primes <= n as a pari list. - - EXAMPLES:: - - sage: pari.primes_up_to_n(1) - [] - sage: pari.primes_up_to_n(20) - [2, 3, 5, 7, 11, 13, 17, 19] - """ - if n <= 1: - return pari([]) - self.init_primes(n+1) - return self.prime_list(pari(n).primepi()) - -## cdef long k -## k = (n+10)/math.log(n) -## p = 2 -## while p <= n: -## p = self.nth_prime(k) -## k = 2 -## v = self.prime_list(k) -## return v[:pari(n).primepi()] - - def __nth_prime(self, long n): - """ - nth_prime(n): returns the n-th prime, where n is a C-int - """ - global num_primes - - if n <= 0: - raise ValueError, "nth prime meaningless for non-positive n (=%s)"%n - cdef GEN g - pari_catch_sig_on() - g = prime(n) - return self.new_gen(g) - - - def nth_prime(self, long n): - try: - return self.__nth_prime(n) - except PariError: - self.init_primes(max(2*num_primes,20*n)) - return self.nth_prime(n) - - def euler(self, precision=0): - """ - Return Euler's constant to the requested real precision (in bits). - - EXAMPLES:: - - sage: pari.euler() - 0.577215664901533 - sage: pari.euler(precision=100).python() - 0.577215664901532860606512090082... - """ - pari_catch_sig_on() - return self.new_gen(mpeuler(pbw(precision))) - - def pi(self, precision=0): - """ - Return the value of the constant pi to the requested real precision - (in bits). - - EXAMPLES:: - - sage: pari.pi() - 3.14159265358979 - sage: pari.pi(precision=100).python() - 3.1415926535897932384626433832... - """ - pari_catch_sig_on() - return self.new_gen(mppi(pbw(precision))) - - def pollegendre(self, long n, v=-1): - """ - pollegendre(n, v=x): Legendre polynomial of degree n (n C-integer), - in variable v. - - EXAMPLES:: - - sage: pari.pollegendre(7) - 429/16*x^7 - 693/16*x^5 + 315/16*x^3 - 35/16*x - sage: pari.pollegendre(7, 'z') - 429/16*z^7 - 693/16*z^5 + 315/16*z^3 - 35/16*z - sage: pari.pollegendre(0) - 1 - """ - pari_catch_sig_on() - return self.new_gen(pollegendre(n, self.get_var(v))) - - def poltchebi(self, long n, v=-1): - """ - poltchebi(n, v=x): Chebyshev polynomial of the first kind of degree - n, in variable v. - - EXAMPLES:: - - sage: pari.poltchebi(7) - 64*x^7 - 112*x^5 + 56*x^3 - 7*x - sage: pari.poltchebi(7, 'z') - 64*z^7 - 112*z^5 + 56*z^3 - 7*z - sage: pari.poltchebi(0) - 1 - """ - pari_catch_sig_on() - return self.new_gen(polchebyshev1(n, self.get_var(v))) - - def factorial(self, long n): - """ - Return the factorial of the integer n as a PARI gen. - - EXAMPLES:: - - sage: pari.factorial(0) - 1 - sage: pari.factorial(1) - 1 - sage: pari.factorial(5) - 120 - sage: pari.factorial(25) - 15511210043330985984000000 - """ - pari_catch_sig_on() - return self.new_gen(mpfact(n)) - - def polcyclo(self, long n, v=-1): - """ - polcyclo(n, v=x): cyclotomic polynomial of degree n, in variable - v. - - EXAMPLES:: - - sage: pari.polcyclo(8) - x^4 + 1 - sage: pari.polcyclo(7, 'z') - z^6 + z^5 + z^4 + z^3 + z^2 + z + 1 - sage: pari.polcyclo(1) - x - 1 - """ - pari_catch_sig_on() - return self.new_gen(polcyclo(n, self.get_var(v))) - - def polcyclo_eval(self, long n, v): - """ - polcyclo_eval(n, v): value of the nth cyclotomic polynomial at value v. - - EXAMPLES:: - - sage: pari.polcyclo_eval(8, 2) - 17 - sage: cyclotomic_polynomial(8)(2) - 17 - """ - cdef gen t0 = self(v) - pari_catch_sig_on() - return self.new_gen(polcyclo_eval(n, t0.g)) - - def polsubcyclo(self, long n, long d, v=-1): - """ - polsubcyclo(n, d, v=x): return the pari list of polynomial(s) - defining the sub-abelian extensions of degree `d` of the - cyclotomic field `\QQ(\zeta_n)`, where `d` - divides `\phi(n)`. - - EXAMPLES:: - - sage: pari.polsubcyclo(8, 4) - [x^4 + 1] - sage: pari.polsubcyclo(8, 2, 'z') - [z^2 - 2, z^2 + 1, z^2 + 2] - sage: pari.polsubcyclo(8, 1) - [x - 1] - sage: pari.polsubcyclo(8, 3) - [] - """ - cdef gen plist - pari_catch_sig_on() - plist = self.new_gen(polsubcyclo(n, d, self.get_var(v))) - if typ(plist.g) != t_VEC: - return pari.vector(1, [plist]) - else: - return plist - #return self.new_gen(polsubcyclo(n, d, self.get_var(v))) - - def polzagier(self, long n, long m): - pari_catch_sig_on() - return self.new_gen(polzag(n, m)) - - def setrand(self, seed): - """ - Sets PARI's current random number seed. - - INPUT: - - - ``seed`` -- either a strictly positive integer or a GEN of - type ``t_VECSMALL`` as output by ``getrand()`` - - This should not be called directly; instead, use Sage's global - random number seed handling in ``sage.misc.randstate`` - and call ``current_randstate().set_seed_pari()``. - - EXAMPLES:: - - sage: pari.setrand(50) - sage: a = pari.getrand(); a - Vecsmall([...]) - sage: pari.setrand(a) - sage: a == pari.getrand() - True - - TESTS: - - Check that invalid inputs are handled properly (#11825):: - - sage: pari.setrand(0) - Traceback (most recent call last): - ... - PariError: incorrect type in setrand - sage: pari.setrand("foobar") - Traceback (most recent call last): - ... - PariError: incorrect type in setrand - """ - cdef gen t0 = self(seed) - pari_catch_sig_on() - setrand(t0.g) - pari_catch_sig_off() - - def getrand(self): - """ - Returns PARI's current random number seed. - - OUTPUT: - - GEN of type t_VECSMALL - - EXAMPLES:: - - sage: pari.setrand(50) - sage: a = pari.getrand(); a - Vecsmall([...]) - sage: pari.setrand(a) - sage: a == pari.getrand() - True - """ - pari_catch_sig_on() - return self.new_gen(getrand()) - - def vector(self, long n, entries=None): - """ - vector(long n, entries=None): Create and return the length n PARI - vector with given list of entries. - - EXAMPLES:: - - sage: pari.vector(5, [1, 2, 5, 4, 3]) - [1, 2, 5, 4, 3] - sage: pari.vector(2, [x, 1]) - [x, 1] - sage: pari.vector(2, [x, 1, 5]) - Traceback (most recent call last): - ... - IndexError: length of entries (=3) must equal n (=2) - """ - cdef gen v = self._empty_vector(n) - if entries is not None: - if len(entries) != n: - raise IndexError, "length of entries (=%s) must equal n (=%s)"%\ - (len(entries), n) - for i, x in enumerate(entries): - v[i] = x - return v - - cdef gen _empty_vector(self, long n): - cdef gen v - pari_catch_sig_on() - v = self.new_gen(zerovec(n)) - return v - - def matrix(self, long m, long n, entries=None): - """ - matrix(long m, long n, entries=None): Create and return the m x n - PARI matrix with given list of entries. - """ - cdef long i, j, k - cdef gen A - cdef gen x - - pari_catch_sig_on() - A = self.new_gen(zeromatcopy(m,n)) - if entries is not None: - if len(entries) != m*n: - raise IndexError, "len of entries (=%s) must be %s*%s=%s"%(len(entries),m,n,m*n) - k = 0 - for i from 0 <= i < m: - for j from 0 <= j < n: - x = pari(entries[k]) - A._refers_to[(i,j)] = x - ((A.g)[j+1])[i+1] = (x.g) - k = k + 1 - return A - - -def init_pari_stack(s=8000000): - """ - Deprecated, use ``pari.allocatemem()`` instead. - - EXAMPLES:: - - sage: from sage.libs.pari.gen import init_pari_stack - sage: init_pari_stack() - doctest:...: DeprecationWarning: init_pari_stack() is deprecated; use pari.allocatemem() instead. - See http://trac.sagemath.org/10018 for details. - sage: pari.stacksize() - 8000000 - """ - from sage.misc.superseded import deprecation - deprecation(10018, 'init_pari_stack() is deprecated; use pari.allocatemem() instead.') - P.allocatemem(s, silent=True) - - -cdef int init_stack(size_t requested_size) except -1: - r""" - Low-level Cython function to allocate the PARI stack. This - function should not be called directly, use ``pari.allocatemem()`` - instead. - """ - global top, bot, avma, mytop - - cdef size_t old_size = (top) - (bot) - - cdef size_t new_size - cdef size_t max_size = (-1) - if (requested_size == 0): - if old_size < max_size/2: - # Double the stack - new_size = 2*old_size - elif old_size < 4*(max_size/5): - # We cannot possibly double since we already use over half - # the addressable memory: take the average of current and - # maximum size - new_size = max_size/2 + old_size/2 - else: - # We already use 80% of the addressable memory => give up - raise MemoryError("Unable to enlarge PARI stack (instead, kept the stack at %s bytes)"%(old_size)) - else: - new_size = requested_size - - # Align size to 16 bytes and take 1024 bytes as a minimum - new_size = (new_size/16)*16 - if (new_size < 1024): - new_size = 1024 - - # Disable interrupts - sig_on() - sig_block() - - # If this is non-zero, the size we failed to allocate - cdef size_t failed_size = 0 - - try: - # Free the current stack - if bot: - libc.stdlib.free(bot) - - # Allocate memory for new stack. - bot = libc.stdlib.malloc(new_size) - - # If doubling failed, instead add 25% to the current stack size. - # We already checked that we use less than 80% of the maximum value - # for s, so this will not overflow. - if (bot == 0) and (requested_size == 0): - new_size = (old_size/64)*80 - bot = libc.stdlib.malloc(new_size) - - if not bot: - failed_size = new_size - # We lost our PARI stack and are not able to allocate the - # requested size. If we just raise an exception now, we end up - # *without* a PARI stack which is not good. We will raise an - # exception later, after allocating *some* PARI stack. - new_size = old_size - while new_size >= 1024: # hope this never fails! - bot = libc.stdlib.malloc(new_size) - if bot: break - new_size = (new_size/32)*16 - - if not bot: - top = 0 - avma = 0 - raise SystemError("Unable to allocate PARI stack, all subsequent PARI computations will crash") - - top = bot + new_size - mytop = top - avma = top - - if failed_size: - raise MemoryError("Unable to allocate %s bytes for the PARI stack (instead, allocated %s bytes)"%(failed_size, new_size)) - - return 0 - finally: - sig_unblock() - sig_off() - - -cdef gen objtogen(s): - """Convert any Sage/Python object to a PARI gen""" - cdef GEN g - cdef Py_ssize_t length, i - cdef mpz_t mpz_int - cdef gen v - - if isinstance(s, gen): - return s - try: - return s._pari_() - except AttributeError: - pass - - # Check basic Python types. Start with strings, which are a very - # common case. - if PyString_Check(s): - pari_catch_sig_on() - g = gp_read_str(PyString_AsString(s)) - if g == gnil: - P.clear_stack() - return None - return P.new_gen(g) - if PyInt_Check(s): - pari_catch_sig_on() - return P.new_gen(stoi(PyInt_AS_LONG(s))) - if PyBool_Check(s): - return P.PARI_ONE if s else P.PARI_ZERO - if PyLong_Check(s): - pari_catch_sig_on() - mpz_init(mpz_int) - mpz_set_pylong(mpz_int, s) - g = P._new_GEN_from_mpz_t(mpz_int) - mpz_clear(mpz_int) - return P.new_gen(g) - if PyFloat_Check(s): - pari_catch_sig_on() - return P.new_gen(dbltor(PyFloat_AS_DOUBLE(s))) - if PyComplex_Check(s): - pari_catch_sig_on() - g = cgetg(3, t_COMPLEX) - set_gel(g, 1, dbltor(PyComplex_RealAsDouble(s))) - set_gel(g, 2, dbltor(PyComplex_ImagAsDouble(s))) - return P.new_gen(g) - - if isinstance(s, (types.ListType, types.XRangeType, - types.TupleType, types.GeneratorType)): - length = len(s) - v = P._empty_vector(length) - for i from 0 <= i < length: - v[i] = objtogen(s[i]) - return v + if isinstance(s, (types.ListType, types.XRangeType, + types.TupleType, types.GeneratorType)): + length = len(s) + v = P._empty_vector(length) + for i from 0 <= i < length: + v[i] = objtogen(s[i]) + return v # Simply use the string representation return objtogen(str(s)) -cdef GEN deepcopy_to_python_heap(GEN x, pari_sp* address): - cdef size_t s = gsizebyte(x) - cdef pari_sp tmp_bot, tmp_top - - tmp_bot = sage_malloc(s) - tmp_top = tmp_bot + s - address[0] = tmp_bot - return gcopy_avma(x, &tmp_top) - -cdef gen _new_gen(GEN x): - cdef GEN h - cdef pari_sp address - cdef gen y - h = deepcopy_to_python_heap(x, &address) - y = PY_NEW(gen) - y.init(h, address) - return y - cdef GEN _Vec_append(GEN v, GEN a, long n): """ This implements appending zeros (or another constant GEN ``a``) to diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx index ab5e49ccf61..561018419f6 100644 --- a/src/sage/libs/pari/pari_instance.pyx +++ b/src/sage/libs/pari/pari_instance.pyx @@ -152,3 +152,1487 @@ Sage (ticket #9636):: test """ + +cdef extern from "mpz_pylong.h": + cdef int mpz_set_pylong(mpz_t dst, src) except -1 + +# so Galois groups are represented in a sane way +# See the polgalois section of the PARI users manual. +new_galois_format = 1 + +cdef pari_sp mytop + +# real precision in decimal digits: see documentation for +# get_real_precision() and set_real_precision(). This variable is used +# in gp to set the precision of input quantities (e.g. sqrt(2)), and for +# determining the number of digits to be printed. It is *not* used as +# a "default precision" for internal computations, which always use +# the actual precision of arguments together (where relevant) with a +# "prec" parameter. In ALL cases (for real computations) the prec +# parameter is a WORD precision and NOT decimal precision. Pari reals +# with word precision w have bit precision (of the mantissa) equal to +# 32*(w-2) or 64*(w-2). +# +# Hence the only relevance of this parameter in Sage is (1) for the +# output format of components of objects of type +# 'sage.libs.pari.gen.gen'; (2) for setting the precision of pari +# variables created from strings (e.g. via sage: pari('1.2')). +# +# WARNING: Many pari library functions take a last parameter "prec" +# which should be a words precision. In many cases this is redundant +# and is simply ignored. In our wrapping of these functions we use +# the variable prec here for convenience only. +cdef unsigned long prec + +################################################################# +# conversions between various real precision models +################################################################# + +def prec_bits_to_dec(int prec_in_bits): + r""" + Convert from precision expressed in bits to precision expressed in + decimal. + + EXAMPLES:: + + sage: import sage.libs.pari.gen as gen + sage: gen.prec_bits_to_dec(53) + 15 + sage: [(32*n,gen.prec_bits_to_dec(32*n)) for n in range(1,9)] + [(32, 9), + (64, 19), + (96, 28), + (128, 38), + (160, 48), + (192, 57), + (224, 67), + (256, 77)] + """ + log_2 = 0.301029995663981 + return int(prec_in_bits*log_2) + +def prec_dec_to_bits(int prec_in_dec): + r""" + Convert from precision expressed in decimal to precision expressed + in bits. + + EXAMPLES:: + + sage: import sage.libs.pari.gen as gen + sage: gen.prec_dec_to_bits(15) + 49 + sage: [(n,gen.prec_dec_to_bits(n)) for n in range(10,100,10)] + [(10, 33), + (20, 66), + (30, 99), + (40, 132), + (50, 166), + (60, 199), + (70, 232), + (80, 265), + (90, 298)] + """ + log_10 = 3.32192809488736 + return int(prec_in_dec*log_10) + +def prec_bits_to_words(int prec_in_bits=0): + r""" + Convert from precision expressed in bits to pari real precision + expressed in words. Note: this rounds up to the nearest word, + adjusts for the two codewords of a pari real, and is + architecture-dependent. + + EXAMPLES:: + + sage: import sage.libs.pari.gen as gen + sage: gen.prec_bits_to_words(70) + 5 # 32-bit + 4 # 64-bit + + :: + + sage: [(32*n,gen.prec_bits_to_words(32*n)) for n in range(1,9)] + [(32, 3), (64, 4), (96, 5), (128, 6), (160, 7), (192, 8), (224, 9), (256, 10)] # 32-bit + [(32, 3), (64, 3), (96, 4), (128, 4), (160, 5), (192, 5), (224, 6), (256, 6)] # 64-bit + """ + if not prec_in_bits: + return prec + cdef int wordsize + wordsize = BITS_IN_LONG + + # increase prec_in_bits to the nearest multiple of wordsize + cdef int padded_bits + padded_bits = (prec_in_bits + wordsize - 1) & ~(wordsize - 1) + return int(padded_bits/wordsize + 2) + +pbw = prec_bits_to_words + +def prec_words_to_bits(int prec_in_words): + r""" + Convert from pari real precision expressed in words to precision + expressed in bits. Note: this adjusts for the two codewords of a + pari real, and is architecture-dependent. + + EXAMPLES:: + + sage: import sage.libs.pari.gen as gen + sage: gen.prec_words_to_bits(10) + 256 # 32-bit + 512 # 64-bit + sage: [(n,gen.prec_words_to_bits(n)) for n in range(3,10)] + [(3, 32), (4, 64), (5, 96), (6, 128), (7, 160), (8, 192), (9, 224)] # 32-bit + [(3, 64), (4, 128), (5, 192), (6, 256), (7, 320), (8, 384), (9, 448)] # 64-bit + """ + # see user's guide to the pari library, page 10 + return int((prec_in_words - 2) * BITS_IN_LONG) + +def prec_dec_to_words(int prec_in_dec): + r""" + Convert from precision expressed in decimal to precision expressed + in words. Note: this rounds up to the nearest word, adjusts for the + two codewords of a pari real, and is architecture-dependent. + + EXAMPLES:: + + sage: import sage.libs.pari.gen as gen + sage: gen.prec_dec_to_words(38) + 6 # 32-bit + 4 # 64-bit + sage: [(n,gen.prec_dec_to_words(n)) for n in range(10,90,10)] + [(10, 4), (20, 5), (30, 6), (40, 7), (50, 8), (60, 9), (70, 10), (80, 11)] # 32-bit + [(10, 3), (20, 4), (30, 4), (40, 5), (50, 5), (60, 6), (70, 6), (80, 7)] # 64-bit + """ + return prec_bits_to_words(prec_dec_to_bits(prec_in_dec)) + +def prec_words_to_dec(int prec_in_words): + r""" + Convert from precision expressed in words to precision expressed in + decimal. Note: this adjusts for the two codewords of a pari real, + and is architecture-dependent. + + EXAMPLES:: + + sage: import sage.libs.pari.gen as gen + sage: gen.prec_words_to_dec(5) + 28 # 32-bit + 57 # 64-bit + sage: [(n,gen.prec_words_to_dec(n)) for n in range(3,10)] + [(3, 9), (4, 19), (5, 28), (6, 38), (7, 48), (8, 57), (9, 67)] # 32-bit + [(3, 19), (4, 38), (5, 57), (6, 77), (7, 96), (8, 115), (9, 134)] # 64-bit + """ + return prec_bits_to_dec(prec_words_to_bits(prec_in_words)) + + +# The unique running Pari instance. +cdef PariInstance pari_instance, P +pari_instance = PariInstance() +P = pari_instance # shorthand notation + +# PariInstance.__init__ must not create gen objects because their parent is not constructed yet +pari_catch_sig_on() +pari_instance.PARI_ZERO = pari_instance.new_gen_noclear(gen_0) +pari_instance.PARI_ONE = pari_instance.new_gen_noclear(gen_1) +pari_instance.PARI_TWO = pari_instance.new_gen_noclear(gen_2) +pari_catch_sig_off() + +# Also a copy of PARI accessible from external pure python code. +pari = pari_instance + + +cdef unsigned long num_primes + +# Callbacks from PARI to print stuff using sys.stdout.write() instead +# of C library functions like puts(). +cdef PariOUT sage_pariOut + +cdef void sage_putchar(char c): + cdef char s[2] + s[0] = c + s[1] = 0 + sys.stdout.write(s) + # Let PARI think the last character was a newline, + # so it doesn't print one when an error occurs. + pari_set_last_newline(1) + +cdef void sage_puts(char* s): + sys.stdout.write(s) + pari_set_last_newline(1) + +cdef void sage_flush(): + sys.stdout.flush() + +cdef PariOUT sage_pariErr + +cdef void sage_pariErr_putchar(char c): + cdef char s[2] + s[0] = c + s[1] = 0 + global pari_error_string + pari_error_string += str(s) + pari_set_last_newline(1) + +cdef void sage_pariErr_puts(char *s): + global pari_error_string + pari_error_string += str(s) + pari_set_last_newline(1) + +cdef void sage_pariErr_flush(): + pass + + +@cython.final +cdef class PariInstance(sage.structure.parent_base.ParentWithBase): + def __init__(self, long size=1000000, unsigned long maxprime=500000): + """ + Initialize the PARI system. + + INPUT: + + + - ``size`` -- long, the number of bytes for the initial + PARI stack (see note below) + + - ``maxprime`` -- unsigned long, upper limit on a + precomputed prime number table (default: 500000) + + + .. note:: + + In Sage, the PARI stack is different than in GP or the + PARI C library. In Sage, instead of the PARI stack + holding the results of all computations, it *only* holds + the results of an individual computation. Each time a new + Python/PARI object is computed, it it copied to its own + space in the Python heap, and the memory it occupied on the + PARI stack is freed. Thus it is not necessary to make the + stack very large. Also, unlike in PARI, if the stack does + overflow, in most cases the PARI stack is automatically + increased and the relevant step of the computation rerun. + + This design obviously involves some performance penalties + over the way PARI works, but it scales much better and is + far more robust for large projects. + + .. note:: + + If you do not want prime numbers, put ``maxprime=2``, but be + careful because many PARI functions require this table. If + you get the error message "not enough precomputed primes", + increase this parameter. + """ + if bot: + return # pari already initialized. + + global num_primes, avma, top, bot, prec + + # The size here doesn't really matter, because we will allocate + # our own stack anyway. We ask PARI not to set up signal and + # error handlers. + pari_init_opts(10000, maxprime, INIT_DFTm) + + _pari_init_error_handling() + + num_primes = maxprime + + # Free the PARI stack and allocate our own (using Cython) + pari_free(bot); bot = 0 + init_stack(size) + + GP_DATA.fmt.prettyp = 0 + + # how do I get the following to work? seems to be a circular import + #from sage.rings.real_mpfr import RealField + #prec_bits = RealField().prec() + prec = prec_bits_to_words(53) + GP_DATA.fmt.sigd = prec_bits_to_dec(53) + + # Set printing functions + global pariOut, pariErr + + pariOut = &sage_pariOut + pariOut.putch = sage_putchar + pariOut.puts = sage_puts + pariOut.flush = sage_flush + + pariErr = &sage_pariErr + pariErr.putch = sage_pariErr_putchar + pariErr.puts = sage_pariErr_puts + pariErr.flush = sage_pariErr_flush + + def debugstack(self): + r""" + Print the internal PARI variables ``top`` (top of stack), ``avma`` + (available memory address, think of this as the stack pointer), + ``bot`` (bottom of stack). + + EXAMPLE:: + + sage: pari.debugstack() # random + top = 0x60b2c60 + avma = 0x5875c38 + bot = 0x57295e0 + size = 1000000 + """ + # We deliberately use low-level functions to minimize the + # chances that something goes wrong here (for example, if we + # are out of memory). + global avma, top, bot + printf("top = %p\navma = %p\nbot = %p\nsize = %lu\n", top, avma, bot, (top) - (bot)) + fflush(stdout) + + def __dealloc__(self): + """ + Deallocation of the Pari instance. + + NOTE: + + Usually this deallocation happens only when Sage quits. + We do not provide a direct test, since usually there + is only one Pari instance, and when artificially creating + another instance, C-data are shared. + + The fact that Sage does not crash when quitting is an + indirect doctest. See the discussion at :trac:`13741`. + + """ + if bot: + sage_free(bot) + global top, bot, avma + top = 0 + bot = 0 + avma = 0 + pari_close() + + def __repr__(self): + return "Interface to the PARI C library" + + def __hash__(self): + return 907629390 # hash('pari') + + cdef has_coerce_map_from_c_impl(self, x): + return True + + def __richcmp__(left, right, int op): + """ + EXAMPLES:: + + sage: pari == pari + True + sage: pari == gp + False + sage: pari == 5 + False + """ + return (left)._richcmp(right, op) + + def default(self, variable, value=None): + if not value is None: + return self('default(%s, %s)'%(variable, value)) + return self('default(%s)'%variable) + + def set_debug_level(self, level): + """ + Set the debug PARI C library variable. + """ + self.default('debug', int(level)) + + def get_debug_level(self): + """ + Set the debug PARI C library variable. + """ + return int(self.default('debug')) + + def set_real_precision(self, long n): + """ + Sets the PARI default real precision. + + This is used both for creation of new objects from strings and for + printing. It is the number of digits *IN DECIMAL* in which real + numbers are printed. It also determines the precision of objects + created by parsing strings (e.g. pari('1.2')), which is *not* the + normal way of creating new pari objects in Sage. It has *no* + effect on the precision of computations within the pari library. + + Returns the previous PARI real precision. + """ + cdef unsigned long k + + k = GP_DATA.fmt.sigd + s = str(n) + pari_catch_sig_on() + sd_realprecision(s, 2) + pari_catch_sig_off() + return int(k) # Python int + + def get_real_precision(self): + """ + Returns the current PARI default real precision. + + This is used both for creation of new objects from strings and for + printing. It is the number of digits *IN DECIMAL* in which real + numbers are printed. It also determines the precision of objects + created by parsing strings (e.g. pari('1.2')), which is *not* the + normal way of creating new pari objects in Sage. It has *no* + effect on the precision of computations within the pari library. + """ + return GP_DATA.fmt.sigd + + def set_series_precision(self, long n): + global precdl + precdl = n + + def get_series_precision(self): + return precdl + + cdef inline void clear_stack(self): + """ + Call ``pari_catch_sig_off()``, and clear the entire PARI stack + if we are leaving the outermost ``pari_catch_sig_on() ... + pari_catch_sig_off()`` block. + + """ + global mytop, avma + if _signals.sig_on_count <= 1: + avma = mytop + pari_catch_sig_off() + + cdef inline gen new_gen(self, GEN x): + """ + Create a new gen wrapping `x`, then call ``clear_stack()``. + """ + cdef gen g = _new_gen(x) + self.clear_stack() + return g + + cdef inline gen new_gen_noclear(self, GEN x): + """ + Create a new gen, but don't free any memory on the stack and don't + call pari_catch_sig_off(). + """ + z = _new_gen(x) + return z + + cdef gen new_gen_from_mpz_t(self, mpz_t value): + """ + Create a new gen from a given MPIR-integer ``value``. + + EXAMPLES:: + + sage: pari(42) # indirect doctest + 42 + + TESTS: + + Check that the hash of an integer does not depend on existing + garbage on the stack (#11611):: + + sage: foo = pari(2^(32*1024)); # Create large integer to put PARI stack in known state + sage: a5 = pari(5); + sage: foo = pari(0xDEADBEEF * (2^(32*1024)-1)//(2^32 - 1)); # Dirty PARI stack + sage: b5 = pari(5); + sage: a5.__hash__() == b5.__hash__() + True + """ + pari_catch_sig_on() + return self.new_gen(self._new_GEN_from_mpz_t(value)) + + cdef inline GEN _new_GEN_from_mpz_t(self, mpz_t value): + r""" + Create a new PARI ``t_INT`` from a ``mpz_t``. + + For internal use only; this directly uses the PARI stack. + One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. + """ + cdef unsigned long limbs = mpz_size(value) + + cdef GEN z = cgeti(limbs + 2) + # Set sign and "effective length" + z[1] = evalsigne(mpz_sgn(value)) + evallgefint(limbs + 2) + mpz_export(int_LSW(z), NULL, -1, sizeof(long), 0, 0, value) + + return z + + cdef gen new_gen_from_int(self, int value): + pari_catch_sig_on() + return self.new_gen(stoi(value)) + + cdef gen new_gen_from_mpq_t(self, mpq_t value): + """ + Create a new gen from a given MPIR-rational ``value``. + + EXAMPLES:: + + sage: pari(-2/3) + -2/3 + sage: pari(QQ(42)) + 42 + sage: pari(QQ(42)).type() + 't_INT' + sage: pari(QQ(1/42)).type() + 't_FRAC' + + TESTS: + + Check that the hash of a rational does not depend on existing + garbage on the stack (#11854):: + + sage: foo = pari(2^(32*1024)); # Create large integer to put PARI stack in known state + sage: a5 = pari(5/7); + sage: foo = pari(0xDEADBEEF * (2^(32*1024)-1)//(2^32 - 1)); # Dirty PARI stack + sage: b5 = pari(5/7); + sage: a5.__hash__() == b5.__hash__() + True + """ + pari_catch_sig_on() + return self.new_gen(self._new_GEN_from_mpq_t(value)) + + cdef inline GEN _new_GEN_from_mpq_t(self, mpq_t value): + r""" + Create a new PARI ``t_INT`` or ``t_FRAC`` from a ``mpq_t``. + + For internal use only; this directly uses the PARI stack. + One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. + """ + cdef GEN num = self._new_GEN_from_mpz_t(mpq_numref(value)) + if mpz_cmpabs_ui(mpq_denref(value), 1) == 0: + # Denominator is 1, return the numerator (an integer) + return num + cdef GEN denom = self._new_GEN_from_mpz_t(mpq_denref(value)) + return mkfrac(num, denom) + + cdef gen new_t_POL_from_int_star(self, int *vals, int length, long varnum): + """ + Note that degree + 1 = length, so that recognizing 0 is easier. + + varnum = 0 is the general choice (creates a variable in x). + """ + cdef GEN z + cdef int i + + pari_catch_sig_on() + z = cgetg(length + 2, t_POL) + z[1] = evalvarn(varnum) + if length != 0: + setsigne(z,1) + for i from 0 <= i < length: + set_gel(z,i+2, stoi(vals[i])) + else: + ## polynomial is zero + setsigne(z,0) + + return self.new_gen(z) + + cdef gen new_gen_from_padic(self, long ordp, long relprec, + mpz_t prime, mpz_t p_pow, mpz_t unit): + cdef GEN z + pari_catch_sig_on() + z = cgetg(5, t_PADIC) + z[1] = evalprecp(relprec) + evalvalp(ordp) + set_gel(z, 2, self._new_GEN_from_mpz_t(prime)) + set_gel(z, 3, self._new_GEN_from_mpz_t(p_pow)) + set_gel(z, 4, self._new_GEN_from_mpz_t(unit)) + return self.new_gen(z) + + def double_to_gen(self, x): + cdef double dx + dx = float(x) + return self.double_to_gen_c(dx) + + cdef gen double_to_gen_c(self, double x): + """ + Create a new gen with the value of the double x, using Pari's + dbltor. + + EXAMPLES:: + + sage: pari.double_to_gen(1) + 1.00000000000000 + sage: pari.double_to_gen(1e30) + 1.00000000000000 E30 + sage: pari.double_to_gen(0) + 0.E-15 + sage: pari.double_to_gen(-sqrt(RDF(2))) + -1.41421356237310 + """ + # Pari has an odd concept where it attempts to track the accuracy + # of floating-point 0; a floating-point zero might be 0.0e-20 + # (meaning roughly that it might represent any number in the + # range -1e-20 <= x <= 1e20). + + # Pari's dbltor converts a floating-point 0 into the Pari real + # 0.0e-307; Pari treats this as an extremely precise 0. This + # can cause problems; for instance, the Pari incgam() function can + # be very slow if the first argument is very precise. + + # So we translate 0 into a floating-point 0 with 53 bits + # of precision (that's the number of mantissa bits in an IEEE + # double). + + pari_catch_sig_on() + if x == 0: + return self.new_gen(real_0_bit(-53)) + else: + return self.new_gen(dbltor(x)) + + cdef GEN double_to_GEN(self, double x): + if x == 0: + return real_0_bit(-53) + else: + return dbltor(x) + + def complex(self, re, im): + """ + Create a new complex number, initialized from re and im. + """ + cdef gen t0 = self(re) + cdef gen t1 = self(im) + pari_catch_sig_on() + return self.new_gen(mkcomplex(t0.g, t1.g)) + + cdef GEN deepcopy_to_python_heap(self, GEN x, pari_sp* address): + return deepcopy_to_python_heap(x, address) + + cdef gen new_ref(self, GEN g, gen parent): + """ + Create a new gen pointing to the given GEN, which is allocated as a + part of parent.g. + + .. note:: + + As a rule, there should never be more than one sage gen + pointing to a given Pari GEN. So that means there is only + one case where this function should be used: when a + complicated Pari GEN is allocated with a single gen + pointing to it, and one needs a gen pointing to one of its + components. + + For example, doing x = pari("[1,2]") allocates a gen pointing to + the list [1,2], but x[0] has no gen wrapping it, so new_ref + should be used there. Then parent would be x in this + case. See __getitem__ for an example of usage. + + EXAMPLES:: + + sage: pari("[[1,2],3]")[0][1] ## indirect doctest + 2 + """ + cdef gen p = PY_NEW(gen) + + p.b = 0 + p._parent = self + p._refers_to = {-1:parent} + p.g = g + + return p + + def __call__(self, s): + """ + Create the PARI object obtained by evaluating s using PARI. + + EXAMPLES:: + + sage: pari([2,3,5]) + [2, 3, 5] + sage: pari(Matrix(2,2,range(4))) + [0, 1; 2, 3] + sage: pari(x^2-3) + x^2 - 3 + + :: + + sage: a = pari(1); a, a.type() + (1, 't_INT') + sage: a = pari(1/2); a, a.type() + (1/2, 't_FRAC') + sage: a = pari(1/2); a, a.type() + (1/2, 't_FRAC') + + See :func:`pari` for more examples. + """ + return objtogen(s) + + cdef GEN _new_GEN_from_mpz_t_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc): + r""" + Create a new PARI ``t_MAT`` with ``nr`` rows and ``nc`` columns + from a ``mpz_t**``. + + For internal use only; this directly uses the PARI stack. + One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. + """ + cdef GEN x + cdef GEN A = zeromatcopy(nr, nc) + cdef Py_ssize_t i, j + for i in range(nr): + for j in range(nc): + x = self._new_GEN_from_mpz_t(B[i][j]) + set_gcoeff(A, i+1, j+1, x) # A[i+1, j+1] = x (using 1-based indexing) + return A + + cdef GEN _new_GEN_from_mpz_t_matrix_rotate90(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc): + r""" + Create a new PARI ``t_MAT`` with ``nr`` rows and ``nc`` columns + from a ``mpz_t**`` and rotate the matrix 90 degrees + counterclockwise. So the resulting matrix will have ``nc`` rows + and ``nr`` columns. This is useful for computing the Hermite + Normal Form because Sage and PARI use different definitions. + + For internal use only; this directly uses the PARI stack. + One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. + """ + cdef GEN x + cdef GEN A = zeromatcopy(nc, nr) + cdef Py_ssize_t i, j + for i in range(nr): + for j in range(nc): + x = self._new_GEN_from_mpz_t(B[i][nc-j-1]) + set_gcoeff(A, j+1, i+1, x) # A[j+1, i+1] = x (using 1-based indexing) + return A + + cdef gen integer_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc, bint permute_for_hnf): + """ + EXAMPLES:: + + sage: matrix(ZZ,2,[1..6])._pari_() # indirect doctest + [1, 2, 3; 4, 5, 6] + """ + pari_catch_sig_on() + cdef GEN g + if permute_for_hnf: + g = self._new_GEN_from_mpz_t_matrix_rotate90(B, nr, nc) + else: + g = self._new_GEN_from_mpz_t_matrix(B, nr, nc) + return self.new_gen(g) + + cdef GEN _new_GEN_from_mpq_t_matrix(self, mpq_t** B, Py_ssize_t nr, Py_ssize_t nc): + cdef GEN x + # Allocate zero matrix + cdef GEN A = zeromatcopy(nr, nc) + cdef Py_ssize_t i, j + for i in range(nr): + for j in range(nc): + x = self._new_GEN_from_mpq_t(B[i][j]) + set_gcoeff(A, i+1, j+1, x) # A[i+1, j+1] = x (using 1-based indexing) + return A + + cdef gen rational_matrix(self, mpq_t** B, Py_ssize_t nr, Py_ssize_t nc): + """ + EXAMPLES:: + + sage: matrix(QQ,2,[1..6])._pari_() # indirect doctest + [1, 2, 3; 4, 5, 6] + """ + pari_catch_sig_on() + cdef GEN g = self._new_GEN_from_mpq_t_matrix(B, nr, nc) + return self.new_gen(g) + + cdef _coerce_c_impl(self, x): + """ + Implicit canonical coercion into a PARI object. + """ + try: + return self(x) + except (TypeError, AttributeError): + raise TypeError("no canonical coercion of %s into PARI"%x) + + cdef _an_element_c_impl(self): # override this in Cython + return self.PARI_ZERO + + def new_with_bits_prec(self, s, long precision): + r""" + pari.new_with_bits_prec(self, s, precision) creates s as a PARI + gen with (at most) precision *bits* of precision. + """ + cdef unsigned long old_prec + old_prec = GP_DATA.fmt.sigd + precision = prec_bits_to_dec(precision) + if not precision: + precision = old_prec + self.set_real_precision(precision) + x = self(s) + self.set_real_precision(old_prec) + return x + + + + cdef long get_var(self, v): + """ + Converts a Python string into a PARI variable reference number. Or + if v = -1, returns -1. + """ + if v != -1: + s = str(v) + return fetch_user_var(s) + return -1 + + ############################################################ + # Initialization + ############################################################ + + def stacksize(self): + r""" + Returns the current size of the PARI stack, which is `10^6` + by default. However, the stack size is automatically doubled + when needed. It can also be set directly using + ``pari.allocatemem()``. + + EXAMPLES:: + + sage: pari.stacksize() + 1000000 + + """ + global top, bot + return ((top) - (bot)) + + def _allocate_huge_mem(self): + r""" + This function tries to allocate an impossibly large amount of + PARI stack in order to test ``init_stack()`` failing. + + TESTS:: + + sage: pari.allocatemem(10^6, silent=True) + sage: pari._allocate_huge_mem() + Traceback (most recent call last): + ... + MemoryError: Unable to allocate ... (instead, allocated 1000000 bytes) + + Test that the PARI stack is sane after this failure:: + + sage: a = pari('2^10000000') + sage: pari.allocatemem(10^6, silent=True) + """ + # Since size_t is unsigned, this should wrap over to a very + # large positive number. + init_stack((-4096)) + + def _setup_failed_retry(self): + r""" + This function pretends that the PARI stack is larger than it + actually is such that allocatemem(0) will allocate much more + than double the current stack. That allocation will then fail. + This function is meant to be used only in this doctest. + + TESTS:: + + sage: pari.allocatemem(4096, silent=True) + sage: pari._setup_failed_retry() + sage: try: # Any result is fine as long as it's not a segfault + ....: a = pari('2^1000000') + ....: except MemoryError: + ....: pass + sage: pari.allocatemem(10^6, silent=True) + """ + global top + # Pretend top is at a very high address + top = ((-16)) + + def allocatemem(self, s=0, silent=False): + r""" + Change the PARI stack space to the given size (or double the + current size if ``s`` is `0`). + + If `s = 0` and insufficient memory is avaible to double, the + PARI stack will be enlarged by a smaller amount. In any case, + a ``MemoryError`` will be raised if the requested memory cannot + be allocated. + + The PARI stack is never automatically shrunk. You can use the + command ``pari.allocatemem(10^6)`` to reset the size to `10^6`, + which is the default size at startup. Note that the results of + computations using Sage's PARI interface are copied to the + Python heap, so they take up no space in the PARI stack. + The PARI stack is cleared after every computation. + + It does no real harm to set this to a small value as the PARI + stack will be automatically doubled when we run out of memory. + However, it could make some calculations slower (since they have + to be redone from the start after doubling the stack until the + stack is big enough). + + INPUT: + + - ``s`` - an integer (default: 0). A non-zero argument should + be the size in bytes of the new PARI stack. If `s` is zero, + try to double the current stack size. + + EXAMPLES:: + + sage: pari.allocatemem(10^7) + PARI stack size set to 10000000 bytes + sage: pari.allocatemem() # Double the current size + PARI stack size set to 20000000 bytes + sage: pari.stacksize() + 20000000 + sage: pari.allocatemem(10^6) + PARI stack size set to 1000000 bytes + + The following computation will automatically increase the PARI + stack size:: + + sage: a = pari('2^100000000') + + ``a`` is now a Python variable on the Python heap and does not + take up any space on the PARI stack. The PARI stack is still + large because of the computation of ``a``:: + + sage: pari.stacksize() + 16000000 + sage: pari.allocatemem(10^6) + PARI stack size set to 1000000 bytes + sage: pari.stacksize() + 1000000 + + TESTS: + + Do the same without using the string interface and starting + from a very small stack size:: + + sage: pari.allocatemem(1) + PARI stack size set to 1024 bytes + sage: a = pari(2)^100000000 + sage: pari.stacksize() + 16777216 + """ + s = long(s) + if s < 0: + raise ValueError("Stack size must be nonnegative") + init_stack(s) + if not silent: + print "PARI stack size set to", self.stacksize(), "bytes" + + def pari_version(self): + return str(PARIVERSION) + + def init_primes(self, _M): + """ + Recompute the primes table including at least all primes up to M + (but possibly more). + + EXAMPLES:: + + sage: pari.init_primes(200000) + + We make sure that ticket #11741 has been fixed, and double check to + make sure that diffptr has not been freed:: + + sage: pari.init_primes(2^62) + Traceback (most recent call last): + ... + PariError: not enough memory # 64-bit + OverflowError: long int too large to convert # 32-bit + sage: pari.init_primes(200000) + """ + cdef unsigned long M + cdef char *tmpptr + M = _M + global diffptr, num_primes + if M <= num_primes: + return + pari_catch_sig_on() + tmpptr = initprimes(M) + pari_catch_sig_off() + pari_free( diffptr) + num_primes = M + diffptr = tmpptr + + + ############################################## + ## Support for GP Scripts + ############################################## + + def read(self, bytes filename): + r""" + Read a script from the named filename into the interpreter. The + functions defined in the script are then available for use from + Sage/PARI. The result of the last command in ``filename`` is + returned. + + EXAMPLES: + + Create a gp file:: + + sage: import tempfile + sage: gpfile = tempfile.NamedTemporaryFile(mode="w") + sage: gpfile.file.write("mysquare(n) = {\n") + sage: gpfile.file.write(" n^2;\n") + sage: gpfile.file.write("}\n") + sage: gpfile.file.write("polcyclo(5)\n") + sage: gpfile.file.flush() + + Read it in Sage, we get the result of the last line:: + + sage: pari.read(gpfile.name) + x^4 + x^3 + x^2 + x + 1 + + Call the function defined in the gp file:: + + sage: pari('mysquare(12)') + 144 + """ + pari_catch_sig_on() + return self.new_gen(gp_read_file(filename)) + + + ############################################## + + def _primelimit(self): + """ + Return the number of primes already computed + in this Pari instance. + + EXAMPLES: + sage: pari._primelimit() + 500519 + sage: pari.init_primes(600000) + sage: pari._primelimit() + 600000 + """ + global num_primes + from sage.rings.all import ZZ + return ZZ(num_primes) + + def prime_list(self, long n): + """ + prime_list(n): returns list of the first n primes + + To extend the table of primes use pari.init_primes(M). + + INPUT: + + + - ``n`` - C long + + + OUTPUT: + + + - ``gen`` - PARI list of first n primes + + + EXAMPLES:: + + sage: pari.prime_list(0) + [] + sage: pari.prime_list(-1) + [] + sage: pari.prime_list(3) + [2, 3, 5] + sage: pari.prime_list(10) + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] + sage: pari.prime_list(20) + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71] + sage: len(pari.prime_list(1000)) + 1000 + """ + if n >= 2: + self.nth_prime(n) + pari_catch_sig_on() + return self.new_gen(primes(n)) + + def primes_up_to_n(self, long n): + """ + Return the primes <= n as a pari list. + + EXAMPLES:: + + sage: pari.primes_up_to_n(1) + [] + sage: pari.primes_up_to_n(20) + [2, 3, 5, 7, 11, 13, 17, 19] + """ + if n <= 1: + return pari([]) + self.init_primes(n+1) + return self.prime_list(pari(n).primepi()) + +## cdef long k +## k = (n+10)/math.log(n) +## p = 2 +## while p <= n: +## p = self.nth_prime(k) +## k = 2 +## v = self.prime_list(k) +## return v[:pari(n).primepi()] + + def __nth_prime(self, long n): + """ + nth_prime(n): returns the n-th prime, where n is a C-int + """ + global num_primes + + if n <= 0: + raise ValueError, "nth prime meaningless for non-positive n (=%s)"%n + cdef GEN g + pari_catch_sig_on() + g = prime(n) + return self.new_gen(g) + + + def nth_prime(self, long n): + try: + return self.__nth_prime(n) + except PariError: + self.init_primes(max(2*num_primes,20*n)) + return self.nth_prime(n) + + def euler(self, precision=0): + """ + Return Euler's constant to the requested real precision (in bits). + + EXAMPLES:: + + sage: pari.euler() + 0.577215664901533 + sage: pari.euler(precision=100).python() + 0.577215664901532860606512090082... + """ + pari_catch_sig_on() + return self.new_gen(mpeuler(pbw(precision))) + + def pi(self, precision=0): + """ + Return the value of the constant pi to the requested real precision + (in bits). + + EXAMPLES:: + + sage: pari.pi() + 3.14159265358979 + sage: pari.pi(precision=100).python() + 3.1415926535897932384626433832... + """ + pari_catch_sig_on() + return self.new_gen(mppi(pbw(precision))) + + def pollegendre(self, long n, v=-1): + """ + pollegendre(n, v=x): Legendre polynomial of degree n (n C-integer), + in variable v. + + EXAMPLES:: + + sage: pari.pollegendre(7) + 429/16*x^7 - 693/16*x^5 + 315/16*x^3 - 35/16*x + sage: pari.pollegendre(7, 'z') + 429/16*z^7 - 693/16*z^5 + 315/16*z^3 - 35/16*z + sage: pari.pollegendre(0) + 1 + """ + pari_catch_sig_on() + return self.new_gen(pollegendre(n, self.get_var(v))) + + def poltchebi(self, long n, v=-1): + """ + poltchebi(n, v=x): Chebyshev polynomial of the first kind of degree + n, in variable v. + + EXAMPLES:: + + sage: pari.poltchebi(7) + 64*x^7 - 112*x^5 + 56*x^3 - 7*x + sage: pari.poltchebi(7, 'z') + 64*z^7 - 112*z^5 + 56*z^3 - 7*z + sage: pari.poltchebi(0) + 1 + """ + pari_catch_sig_on() + return self.new_gen(polchebyshev1(n, self.get_var(v))) + + def factorial(self, long n): + """ + Return the factorial of the integer n as a PARI gen. + + EXAMPLES:: + + sage: pari.factorial(0) + 1 + sage: pari.factorial(1) + 1 + sage: pari.factorial(5) + 120 + sage: pari.factorial(25) + 15511210043330985984000000 + """ + pari_catch_sig_on() + return self.new_gen(mpfact(n)) + + def polcyclo(self, long n, v=-1): + """ + polcyclo(n, v=x): cyclotomic polynomial of degree n, in variable + v. + + EXAMPLES:: + + sage: pari.polcyclo(8) + x^4 + 1 + sage: pari.polcyclo(7, 'z') + z^6 + z^5 + z^4 + z^3 + z^2 + z + 1 + sage: pari.polcyclo(1) + x - 1 + """ + pari_catch_sig_on() + return self.new_gen(polcyclo(n, self.get_var(v))) + + def polcyclo_eval(self, long n, v): + """ + polcyclo_eval(n, v): value of the nth cyclotomic polynomial at value v. + + EXAMPLES:: + + sage: pari.polcyclo_eval(8, 2) + 17 + sage: cyclotomic_polynomial(8)(2) + 17 + """ + cdef gen t0 = self(v) + pari_catch_sig_on() + return self.new_gen(polcyclo_eval(n, t0.g)) + + def polsubcyclo(self, long n, long d, v=-1): + """ + polsubcyclo(n, d, v=x): return the pari list of polynomial(s) + defining the sub-abelian extensions of degree `d` of the + cyclotomic field `\QQ(\zeta_n)`, where `d` + divides `\phi(n)`. + + EXAMPLES:: + + sage: pari.polsubcyclo(8, 4) + [x^4 + 1] + sage: pari.polsubcyclo(8, 2, 'z') + [z^2 - 2, z^2 + 1, z^2 + 2] + sage: pari.polsubcyclo(8, 1) + [x - 1] + sage: pari.polsubcyclo(8, 3) + [] + """ + cdef gen plist + pari_catch_sig_on() + plist = self.new_gen(polsubcyclo(n, d, self.get_var(v))) + if typ(plist.g) != t_VEC: + return pari.vector(1, [plist]) + else: + return plist + #return self.new_gen(polsubcyclo(n, d, self.get_var(v))) + + def polzagier(self, long n, long m): + pari_catch_sig_on() + return self.new_gen(polzag(n, m)) + + def setrand(self, seed): + """ + Sets PARI's current random number seed. + + INPUT: + + - ``seed`` -- either a strictly positive integer or a GEN of + type ``t_VECSMALL`` as output by ``getrand()`` + + This should not be called directly; instead, use Sage's global + random number seed handling in ``sage.misc.randstate`` + and call ``current_randstate().set_seed_pari()``. + + EXAMPLES:: + + sage: pari.setrand(50) + sage: a = pari.getrand(); a + Vecsmall([...]) + sage: pari.setrand(a) + sage: a == pari.getrand() + True + + TESTS: + + Check that invalid inputs are handled properly (#11825):: + + sage: pari.setrand(0) + Traceback (most recent call last): + ... + PariError: incorrect type in setrand + sage: pari.setrand("foobar") + Traceback (most recent call last): + ... + PariError: incorrect type in setrand + """ + cdef gen t0 = self(seed) + pari_catch_sig_on() + setrand(t0.g) + pari_catch_sig_off() + + def getrand(self): + """ + Returns PARI's current random number seed. + + OUTPUT: + + GEN of type t_VECSMALL + + EXAMPLES:: + + sage: pari.setrand(50) + sage: a = pari.getrand(); a + Vecsmall([...]) + sage: pari.setrand(a) + sage: a == pari.getrand() + True + """ + pari_catch_sig_on() + return self.new_gen(getrand()) + + def vector(self, long n, entries=None): + """ + vector(long n, entries=None): Create and return the length n PARI + vector with given list of entries. + + EXAMPLES:: + + sage: pari.vector(5, [1, 2, 5, 4, 3]) + [1, 2, 5, 4, 3] + sage: pari.vector(2, [x, 1]) + [x, 1] + sage: pari.vector(2, [x, 1, 5]) + Traceback (most recent call last): + ... + IndexError: length of entries (=3) must equal n (=2) + """ + cdef gen v = self._empty_vector(n) + if entries is not None: + if len(entries) != n: + raise IndexError, "length of entries (=%s) must equal n (=%s)"%\ + (len(entries), n) + for i, x in enumerate(entries): + v[i] = x + return v + + cdef gen _empty_vector(self, long n): + cdef gen v + pari_catch_sig_on() + v = self.new_gen(zerovec(n)) + return v + + def matrix(self, long m, long n, entries=None): + """ + matrix(long m, long n, entries=None): Create and return the m x n + PARI matrix with given list of entries. + """ + cdef long i, j, k + cdef gen A + cdef gen x + + pari_catch_sig_on() + A = self.new_gen(zeromatcopy(m,n)) + if entries is not None: + if len(entries) != m*n: + raise IndexError, "len of entries (=%s) must be %s*%s=%s"%(len(entries),m,n,m*n) + k = 0 + for i from 0 <= i < m: + for j from 0 <= j < n: + x = pari(entries[k]) + A._refers_to[(i,j)] = x + ((A.g)[j+1])[i+1] = (x.g) + k = k + 1 + return A + + +cdef int init_stack(size_t requested_size) except -1: + r""" + Low-level Cython function to allocate the PARI stack. This + function should not be called directly, use ``pari.allocatemem()`` + instead. + """ + global top, bot, avma, mytop + + cdef size_t old_size = (top) - (bot) + + cdef size_t new_size + cdef size_t max_size = (-1) + if (requested_size == 0): + if old_size < max_size/2: + # Double the stack + new_size = 2*old_size + elif old_size < 4*(max_size/5): + # We cannot possibly double since we already use over half + # the addressable memory: take the average of current and + # maximum size + new_size = max_size/2 + old_size/2 + else: + # We already use 80% of the addressable memory => give up + raise MemoryError("Unable to enlarge PARI stack (instead, kept the stack at %s bytes)"%(old_size)) + else: + new_size = requested_size + + # Align size to 16 bytes and take 1024 bytes as a minimum + new_size = (new_size/16)*16 + if (new_size < 1024): + new_size = 1024 + + # Disable interrupts + sig_on() + sig_block() + + # If this is non-zero, the size we failed to allocate + cdef size_t failed_size = 0 + + try: + # Free the current stack + if bot: + libc.stdlib.free(bot) + + # Allocate memory for new stack. + bot = libc.stdlib.malloc(new_size) + + # If doubling failed, instead add 25% to the current stack size. + # We already checked that we use less than 80% of the maximum value + # for s, so this will not overflow. + if (bot == 0) and (requested_size == 0): + new_size = (old_size/64)*80 + bot = libc.stdlib.malloc(new_size) + + if not bot: + failed_size = new_size + # We lost our PARI stack and are not able to allocate the + # requested size. If we just raise an exception now, we end up + # *without* a PARI stack which is not good. We will raise an + # exception later, after allocating *some* PARI stack. + new_size = old_size + while new_size >= 1024: # hope this never fails! + bot = libc.stdlib.malloc(new_size) + if bot: break + new_size = (new_size/32)*16 + + if not bot: + top = 0 + avma = 0 + raise SystemError("Unable to allocate PARI stack, all subsequent PARI computations will crash") + + top = bot + new_size + mytop = top + avma = top + + if failed_size: + raise MemoryError("Unable to allocate %s bytes for the PARI stack (instead, allocated %s bytes)"%(failed_size, new_size)) + + return 0 + finally: + sig_unblock() + sig_off() + + +cdef GEN deepcopy_to_python_heap(GEN x, pari_sp* address): + cdef size_t s = gsizebyte(x) + cdef pari_sp tmp_bot, tmp_top + + tmp_bot = sage_malloc(s) + tmp_top = tmp_bot + s + address[0] = tmp_bot + return gcopy_avma(x, &tmp_top) + +cdef gen _new_gen(GEN x): + cdef GEN h + cdef pari_sp address + cdef gen y + h = deepcopy_to_python_heap(x, &address) + y = PY_NEW(gen) + y.init(h, address) + return y From 5091563427e5528318ef58e755df9f1ffbc69787 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sun, 17 Nov 2013 14:31:22 +0000 Subject: [PATCH 111/206] fix imports in gen_py.py --- src/sage/libs/pari/gen_py.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sage/libs/pari/gen_py.py b/src/sage/libs/pari/gen_py.py index 4a7c2d1652e..c08b6172bd5 100644 --- a/src/sage/libs/pari/gen_py.py +++ b/src/sage/libs/pari/gen_py.py @@ -1,6 +1,3 @@ -import sage.libs.pari.gen as gen -from sage.misc.sage_eval import sage_eval - from sage.rings.all import * def pari(x): @@ -88,7 +85,8 @@ def pari(x): sage: type(pari("dummy = 0; kill(dummy)")) """ - return gen.pari(x) + from sage.libs.pari.pari_instance import pari + return pari(x) def python(z, locals=None): """ @@ -215,9 +213,11 @@ def python(z, locals=None): sage: pari(K(11^-5)).sage() 11^-5 + O(11^0) """ + from sage.libs.pari.pari_instance import prec_words_to_bits + t = z.type() if t == "t_REAL": - return RealField(gen.prec_words_to_bits(z.precision()))(z) + return RealField(prec_words_to_bits(z.precision()))(z) elif t == "t_FRAC": Q = RationalField() return Q(z) @@ -233,11 +233,11 @@ def python(z, locals=None): xprec = z.real().precision() # will be 0 if exact yprec = z.imag().precision() # will be 0 if exact if xprec == 0: - prec = gen.prec_words_to_bits(yprec) + prec = prec_words_to_bits(yprec) elif yprec == 0: - prec = gen.prec_words_to_bits(xprec) + prec = prec_words_to_bits(xprec) else: - prec = max(gen.prec_words_to_bits(xprec),gen.prec_words_to_bits(yprec)) + prec = max(prec_words_to_bits(xprec), prec_words_to_bits(yprec)) R = RealField(prec) C = ComplexField(prec) return C(R(z.real()), R(z.imag())) @@ -261,4 +261,5 @@ def python(z, locals=None): K = Qp(Z(p), rprec) return K(z.lift()) else: + from sage.misc.sage_eval import sage_eval return sage_eval(str(z), locals=locals) From fcf7972e55bf078863ca9e5d1c368b883de03889 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sun, 17 Nov 2013 17:57:45 +0000 Subject: [PATCH 112/206] adapt miscellaneous files in the Sage library --- src/sage/combinat/sloane_functions.py | 2 +- src/sage/functions/other.py | 1 - src/sage/matrix/matrix1.pyx | 2 +- src/sage/matrix/matrix2.pyx | 2 +- src/sage/matrix/matrix_integer_dense.pyx | 30 ++++++++----------- src/sage/matrix/matrix_rational_dense.pyx | 28 ++++++++--------- src/sage/quadratic_forms/qfsolve.py | 8 ++--- src/sage/rings/arith.py | 2 +- src/sage/rings/bernoulli_mod_p.pyx | 5 ++-- src/sage/rings/complex_double.pyx | 25 +++++++--------- src/sage/rings/contfrac.py | 2 +- src/sage/rings/factorint.pyx | 2 +- src/sage/rings/fast_arith.pyx | 2 +- .../rings/finite_rings/element_pari_ffelt.pyx | 5 ++-- src/sage/rings/integer.pyx | 12 ++++---- src/sage/rings/laurent_series_ring.py | 2 +- src/sage/rings/number_field/galois_group.py | 2 +- src/sage/rings/number_field/maps.py | 2 +- src/sage/rings/number_field/number_field.py | 4 +-- .../number_field/number_field_element.pyx | 3 +- .../rings/number_field/number_field_ideal.py | 3 +- .../rings/number_field/number_field_rel.py | 4 +-- .../small_primes_of_degree_one.py | 2 +- src/sage/rings/number_field/totallyreal.pyx | 12 ++++---- .../rings/number_field/totallyreal_rel.py | 2 +- src/sage/rings/number_field/unit_group.py | 2 +- .../padics/padic_capped_absolute_element.pyx | 5 +--- .../padics/padic_capped_relative_element.pxd | 4 +-- .../padics/padic_capped_relative_element.pyx | 5 ++-- .../rings/padics/padic_fixed_mod_element.pyx | 6 ++-- .../rings/padics/padic_generic_element.pyx | 6 ++-- src/sage/rings/polynomial/cyclotomic.pyx | 2 +- src/sage/rings/power_series_ring_element.pyx | 3 +- src/sage/rings/qqbar.py | 1 - src/sage/rings/rational.pyx | 9 +++--- src/sage/rings/real_double.pyx | 9 +++--- src/sage/rings/real_mpfr.pyx | 8 ++--- src/sage/schemes/elliptic_curves/ell_point.py | 4 +-- .../schemes/projective/projective_morphism.py | 3 +- src/sage/structure/sage_object.pyx | 2 +- src/sage/symbolic/ring.pyx | 2 +- 41 files changed, 105 insertions(+), 130 deletions(-) diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index b00c09dba58..70085cf2fd9 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -305,7 +305,6 @@ def __getitem__(self, n): import sage.rings.arith as arith from sage.matrix.matrix_space import MatrixSpace from sage.rings.rational_field import QQ -from sage.libs.pari.gen import pari from sage.combinat import combinat from sage.misc.misc import prod import sage.interfaces.gap as gap @@ -7412,6 +7411,7 @@ def _powerful_numbers_in_range(self, n, m): if n < 4: n = 4 # Use PARI directly -- much faster. + from sage.libs.pari.pari_instance import pari L = pari('v=listcreate(); for(i=%s,%s,if(vecmin(factor(i)[,2])>1,listput(v,i))); v'%(n,m)) return [ZZ(x) for x in L] # not very many, so not much overhead diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 341a2ae8c12..95590cdaab8 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -5,7 +5,6 @@ from sage.symbolic.expression import Expression from sage.symbolic.pynac import register_symbol, symbol_table from sage.symbolic.pynac import py_factorial_py -from sage.libs.pari.gen import pari from sage.symbolic.all import SR from sage.rings.all import Integer, Rational, RealField, RR, ComplexField from sage.rings.complex_number import is_ComplexNumber diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index 792eb0e3b61..e29baa73d67 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -87,7 +87,7 @@ cdef class Matrix(matrix0.Matrix): sage: b[0][0].precision() # in words 3 """ - from sage.libs.pari.gen import pari + from sage.libs.pari.pari_instance import pari return pari.matrix(self._nrows, self._ncols, self._list()) def _gap_init_(self): diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index c048e83a758..243acbefeca 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -324,7 +324,7 @@ cdef class Matrix(matrix1.Matrix): if not K.is_integral_domain(): from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing if is_IntegerModRing(K): - from sage.libs.pari.gen import pari + from sage.libs.pari.pari_instance import pari A = pari(self.lift()) b = pari([c.lift() for c in B]).Col() ret = A.matsolvemod(pari(K.cardinality()), b) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index ea697552364..af7172a8a6a 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -60,8 +60,11 @@ from sage.matrix.matrix_rational_dense cimport Matrix_rational_dense ######################################################### # PARI C library -from sage.libs.pari.gen cimport gen, PariInstance -from sage.libs.pari.gen import pari +from sage.libs.pari.gen cimport gen +from sage.libs.pari.pari_instance cimport PariInstance + +import sage.libs.pari.pari_instance +cdef PariInstance pari = sage.libs.pari.pari_instance.pari include "sage/libs/pari/decl.pxi" include "sage/libs/pari/pari_err.pxi" @@ -4888,8 +4891,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse sage: type(pari(a)) """ - cdef PariInstance P = sage.libs.pari.gen.pari - return P.integer_matrix(self._matrix, self._nrows, self._ncols, 0) + return pari.integer_matrix(self._matrix, self._nrows, self._ncols, 0) def _det_pari(self, int flag=0): """ @@ -4907,13 +4909,12 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse sage: matrix(ZZ,3,[1..9])._det_pari(1) 0 """ - cdef PariInstance P = sage.libs.pari.gen.pari pari_catch_sig_on() cdef GEN d = det0(pari_GEN(self), flag) # now convert d to a Sage integer e cdef Integer e = Integer() t_INT_to_ZZ(e.value, d) - P.clear_stack() + pari.clear_stack() return e def _rank_pari(self): @@ -4927,10 +4928,9 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse sage: matrix(ZZ,3,[1..9])._rank_pari() 2 """ - cdef PariInstance P = sage.libs.pari.gen.pari pari_catch_sig_on() cdef long r = rank(pari_GEN(self)) - P.clear_stack() + pari.clear_stack() return r def _hnf_pari(self, int flag=0, bint include_zero_rows=True): @@ -4994,13 +4994,12 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse sage: pari('mathnf(Mat([0,1]), 4)') [Mat(1), [1, 0; 0, 1]] """ - cdef PariInstance P = sage.libs.pari.gen.pari cdef GEN A pari_catch_sig_on() - A = P._new_GEN_from_mpz_t_matrix_rotate90(self._matrix, self._nrows, self._ncols) + A = pari._new_GEN_from_mpz_t_matrix_rotate90(self._matrix, self._nrows, self._ncols) cdef GEN H = mathnf0(A, flag) B = self.extract_hnf_from_pari_matrix(H, flag, include_zero_rows) - P.clear_stack() # This calls pari_catch_sig_off() + pari.clear_stack() # This calls pari_catch_sig_off() return B @@ -5057,12 +5056,11 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse [1 2 3] [0 3 6] """ - cdef PariInstance P = sage.libs.pari.gen.pari - cdef gen H = P.integer_matrix(self._matrix, self._nrows, self._ncols, 1) + cdef gen H = pari.integer_matrix(self._matrix, self._nrows, self._ncols, 1) H = H.mathnf(flag) pari_catch_sig_on() B = self.extract_hnf_from_pari_matrix(H.g, flag, include_zero_rows) - P.clear_stack() # This calls pari_catch_sig_off() + pari.clear_stack() # This calls pari_catch_sig_off() return B cdef extract_hnf_from_pari_matrix(self, GEN H, int flag, bint include_zero_rows): @@ -5093,9 +5091,7 @@ cdef inline GEN pari_GEN(Matrix_integer_dense B): For internal use only; this directly uses the PARI stack. One should call ``sig_on()`` before and ``sig_off()`` after. """ - cdef PariInstance P = sage.libs.pari.gen.pari - cdef GEN A - A = P._new_GEN_from_mpz_t_matrix(B._matrix, B._nrows, B._ncols) + cdef GEN A = pari._new_GEN_from_mpz_t_matrix(B._matrix, B._nrows, B._ncols) return A diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 7f6aea6a08a..5f6ba284e04 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -78,8 +78,11 @@ from sage.misc.misc import verbose, get_verbose, prod ######################################################### # PARI C library -from sage.libs.pari.gen cimport gen, PariInstance -from sage.libs.pari.gen import pari, PariError +from sage.libs.pari.gen cimport gen +from sage.libs.pari.pari_instance cimport PariInstance + +import sage.libs.pari.pari_instance +cdef PariInstance pari = sage.libs.pari.pari_instance.pari include "sage/libs/pari/decl.pxi" include "sage/libs/pari/pari_err.pxi" @@ -743,6 +746,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): else: algorithm = "iml" if algorithm == "pari": + from sage.libs.pari.gen import PariError try: return self._invert_pari() except PariError: @@ -2569,13 +2573,12 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): """ if self._nrows != self._ncols: raise ValueError("self must be a square matrix") - cdef PariInstance P = pari pari_catch_sig_on() cdef GEN d = det0(pari_GEN(self), flag) # now convert d to a Sage rational cdef Rational e = Rational() t_FRAC_to_QQ(e.value, d) - P.clear_stack() + pari.clear_stack() return e def _rank_pari(self): @@ -2587,10 +2590,9 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): sage: matrix(QQ,3,[1..9])._rank_pari() 2 """ - cdef PariInstance P = pari pari_catch_sig_on() cdef long r = rank(pari_GEN(self)) - P.clear_stack() + pari.clear_stack() return r def _multiply_pari(self, Matrix_rational_dense right): @@ -2617,11 +2619,10 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): # pari doesn't work in case of 0 rows or columns # This case is easy, since the answer must be the 0 matrix. return self.matrix_space(self._nrows, right._ncols).zero_matrix().__copy__() - cdef PariInstance P = pari pari_catch_sig_on() cdef GEN M = gmul(pari_GEN(self), pari_GEN(right)) A = new_matrix_from_pari_GEN(self.matrix_space(self._nrows, right._ncols), M) - P.clear_stack() + pari.clear_stack() return A def _invert_pari(self): @@ -2640,7 +2641,6 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): """ if self._nrows != self._ncols: raise ValueError("self must be a square matrix") - cdef PariInstance P = pari cdef GEN M, d pari_catch_sig_on() @@ -2649,7 +2649,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): # Convert matrix back to Sage. A = new_matrix_from_pari_GEN(self._parent, d) - P.clear_stack() + pari.clear_stack() return A def _pari_(self): @@ -2661,8 +2661,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): sage: matrix(QQ,2,[1/5,-2/3,3/4,4/9])._pari_() [1/5, -2/3; 3/4, 4/9] """ - cdef PariInstance P = pari - return P.rational_matrix(self._matrix, self._nrows, self._ncols) + return pari.rational_matrix(self._matrix, self._nrows, self._ncols) def row(self, Py_ssize_t i, from_list=False): """ @@ -2759,8 +2758,5 @@ cdef inline GEN pari_GEN(Matrix_rational_dense B): For internal use only; this directly uses the PARI stack. One should call ``sig_on()`` before and ``sig_off()`` after. """ - cdef PariInstance P = pari - cdef GEN A - A = P._new_GEN_from_mpq_t_matrix(B._matrix, B._nrows, B._ncols) + cdef GEN A = pari._new_GEN_from_mpq_t_matrix(B._matrix, B._nrows, B._ncols) return A - diff --git a/src/sage/quadratic_forms/qfsolve.py b/src/sage/quadratic_forms/qfsolve.py index e19d13c4cc2..937b017a93c 100644 --- a/src/sage/quadratic_forms/qfsolve.py +++ b/src/sage/quadratic_forms/qfsolve.py @@ -27,7 +27,6 @@ from sage.interfaces.gp import Gp from sage.rings.all import ZZ, QQ -from sage.libs.pari.gen import pari _gp_for_simon_interpreter = None # Global GP interpreter for Denis Simon's code def _gp_for_simon(): @@ -97,7 +96,7 @@ def qfsolve(G, factD=None): gp = _gp_for_simon() if factD is not None: raise NotImplementedError, "qfsolve not implemented with parameter factD" - ret = pari(gp('Qfsolve(%s)' % G._pari_())) + ret = gp('Qfsolve(%s)' % G._pari_())._pari_() if ret.type() == 't_COL': return tuple([QQ(r) for r in ret]) return ZZ(ret) @@ -138,7 +137,6 @@ def qfparam(G, sol): gp = _gp_for_simon() R = QQ['t'] t = R.gen() - s = 'Qfparam(%s, (%s)~)*[t^2,t,1]~' % (G._pari_(), pari(gp(sol))._pari_()) - ret = pari(gp(s)) + s = 'Qfparam(%s, (%s)~)*[t^2,t,1]~' % (G._pari_(), gp(sol)._pari_()) + ret = gp(s)._pari_() return tuple([R(r) for r in ret]) - diff --git a/src/sage/rings/arith.py b/src/sage/rings/arith.py index ffe4149009b..0d19143c6dc 100644 --- a/src/sage/rings/arith.py +++ b/src/sage/rings/arith.py @@ -14,7 +14,7 @@ import math import sys import sage.misc.misc as misc -from sage.libs.pari.gen import pari +from sage.libs.pari.pari_instance import pari import sage.libs.flint.arith as flint_arith from sage.rings.rational_field import QQ diff --git a/src/sage/rings/bernoulli_mod_p.pyx b/src/sage/rings/bernoulli_mod_p.pyx index 1ee9fe4108f..f6efe23ebf6 100644 --- a/src/sage/rings/bernoulli_mod_p.pyx +++ b/src/sage/rings/bernoulli_mod_p.pyx @@ -28,7 +28,6 @@ import sage.rings.arith from sage.libs.ntl import all as ntl from sage.libs.ntl.ntl_ZZ_pX cimport ntl_ZZ_pX -import sage.libs.pari.gen from sage.rings.finite_rings.integer_mod_ring import Integers from sage.rings.bernmm import bernmm_bern_modp @@ -122,7 +121,7 @@ def bernoulli_mod_p(int p): if p <= 2: raise ValueError, "p (=%s) must be a prime >= 3"%p - if not sage.libs.pari.gen.pari(p).isprime(): + if not sage.rings.arith.is_prime(p): raise ValueError, "p (=%s) must be a prime"%p cdef int g, gSqr, gInv, gInvSqr, isOdd @@ -287,7 +286,7 @@ def bernoulli_mod_p_single(long p, long k): if p <= 2: raise ValueError, "p (=%s) must be a prime >= 3"%p - if not sage.libs.pari.gen.pari(p).isprime(): + if not sage.rings.arith.is_prime(p): raise ValueError, "p (=%s) must be a prime"%p R = Integers(p) diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index d31fadc3806..bc9018790e4 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -81,9 +81,11 @@ cimport sage.rings.integer from sage.structure.element cimport RingElement, Element, ModuleElement, FieldElement from sage.structure.parent cimport Parent -cimport sage.libs.pari.gen -import sage.libs.pari.gen +from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.pari.pari_instance cimport PariInstance +import sage.libs.pari.pari_instance +cdef PariInstance pari = sage.libs.pari.pari_instance.pari import complex_number @@ -341,7 +343,7 @@ cdef class ComplexDoubleField_class(sage.rings.ring.Field): sage: CDF((1,2)) # indirect doctest 1.0 + 2.0*I """ - cdef sage.libs.pari.gen.gen g + cdef pari_gen g if PY_TYPE_CHECK(x, ComplexDoubleElement): return x elif PY_TYPE_CHECK(x, tuple): @@ -352,7 +354,7 @@ cdef class ComplexDoubleField_class(sage.rings.ring.Field): return ComplexDoubleElement(x.real, x.imag) elif isinstance(x, complex_number.ComplexNumber): return ComplexDoubleElement(x.real(), x.imag()) - elif isinstance(x, sage.libs.pari.gen.gen): + elif isinstance(x, pari_gen): g = x if typ(g.g) == t_COMPLEX: return ComplexDoubleElement(gtodouble(gel(g.g, 1)), gtodouble(gel(g.g, 2))) @@ -688,7 +690,7 @@ cdef class ComplexDoubleElement(FieldElement): z._complex = x return z - cdef _new_from_gen(self, sage.libs.pari.gen.gen g): + cdef _new_from_gen(self, pari_gen g): """ C-level code for creating a :class:`ComplexDoubleElement` from a PARI gen. @@ -1006,9 +1008,8 @@ cdef class ComplexDoubleElement(FieldElement): cdef GEN _gen(self): cdef GEN y y = cgetg(3, t_COMPLEX) # allocate space for a complex number - cdef sage.libs.pari.gen.PariInstance P = sage.libs.pari.gen.pari - set_gel(y,1,P.double_to_GEN(self._complex.dat[0])) - set_gel(y,2,P.double_to_GEN(self._complex.dat[1])) + set_gel(y, 1, pari.double_to_GEN(self._complex.dat[0])) + set_gel(y, 2, pari.double_to_GEN(self._complex.dat[1])) return y def _pari_(self): @@ -1022,10 +1023,8 @@ cdef class ComplexDoubleElement(FieldElement): sage: pari(CDF(1,2)) 1.00000000000000 + 2.00000000000000*I """ - cdef sage.libs.pari.gen.PariInstance P - P = sage.libs.pari.gen.pari pari_catch_sig_on() - return P.new_gen(self._gen()) + return pari.new_gen(self._gen()) ####################################################################### # Arithmetic @@ -2222,10 +2221,8 @@ cdef class ComplexDoubleElement(FieldElement): sage: CDF(1,5).algdep(2) x^2 - 2*x + 26 """ - cdef sage.libs.pari.gen.PariInstance P - P = sage.libs.pari.gen.pari pari_catch_sig_on() - f = P.new_gen(algdep0(self._gen(), n, 0)) + f = pari.new_gen(algdep0(self._gen(), n, 0)) from polynomial.polynomial_ring_constructor import PolynomialRing from integer_ring import ZZ R = PolynomialRing(ZZ ,'x') diff --git a/src/sage/rings/contfrac.py b/src/sage/rings/contfrac.py index 42dcfb94f74..1e6124309cb 100644 --- a/src/sage/rings/contfrac.py +++ b/src/sage/rings/contfrac.py @@ -60,7 +60,7 @@ """ from sage.structure.element import FieldElement from sage.structure.parent_gens import ParentWithGens -from sage.libs.pari.all import pari +from sage.libs.pari.pari_instance import pari from field import Field from rational_field import QQ diff --git a/src/sage/rings/factorint.pyx b/src/sage/rings/factorint.pyx index 61b84551893..685fc988f34 100644 --- a/src/sage/rings/factorint.pyx +++ b/src/sage/rings/factorint.pyx @@ -285,7 +285,7 @@ cpdef factor_using_pari(n, int_=False, debug_level=0, proof=None): sage: factor(-2**72 + 3, algorithm='pari') # indirect doctest -1 * 83 * 131 * 294971519 * 1472414939 """ - from sage.libs.pari.gen import pari + from sage.libs.pari.pari_instance import pari if proof is None: from sage.structure.proof.proof import get_flag diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index 287c4106c50..c0b7cf986ec 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -49,8 +49,8 @@ cdef extern from "pari/pari.h": cdef long NEXT_PRIME_VIADIFF(long, unsigned char*) from sage.rings.integer_ring import ZZ -from sage.libs.pari.gen import pari from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.pari.pari_instance import pari from sage.rings.integer cimport Integer cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False): diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index 397b73b9afe..6ac84f926cb 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -27,7 +27,8 @@ from integer_mod import IntegerMod_abstract import sage.libs.pari import sage.rings.integer from sage.interfaces.gap import is_GapElement -from sage.libs.pari.gen cimport gen as pari_gen, PariInstance +from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.pari.pari_instance cimport PariInstance from sage.modules.free_module_element import FreeModuleElement from sage.rings.integer cimport Integer from sage.rings.polynomial.polynomial_element import Polynomial @@ -37,7 +38,7 @@ from sage.structure.element cimport Element, ModuleElement, RingElement cdef long mpz_t_offset = sage.rings.integer.mpz_t_offset_python -cdef PariInstance pari = sage.libs.pari.gen.pari +cdef PariInstance pari = sage.libs.pari.pari_instance.pari cdef extern from "sage/libs/pari/misc.h": int gcmp_sage(GEN x, GEN y) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 07fd798c781..c39ed12e87c 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -172,11 +172,14 @@ cdef extern from "mpz_longlong.h": cdef extern from "convert.h": cdef void t_INT_to_ZZ( mpz_t value, long *g ) -from sage.libs.pari.gen cimport gen as pari_gen, PariInstance +from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.pari.pari_instance cimport PariInstance from sage.libs.flint.ulong_extras cimport * import sage.rings.infinity -import sage.libs.pari.all + +import sage.libs.pari.pari_instance +cdef PariInstance P = sage.libs.pari.pari_instance.pari from sage.structure.element import canonical_coercion from sage.misc.superseded import deprecated_function_alias @@ -675,9 +678,8 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): elif paritype == t_FFELT: # x = (f modulo defining polynomial of finite field); # we extract f. - pari = sage.libs.pari.gen.pari sig_on() - x = pari.new_gen(FF_to_FpXQ_i((x).g)) + x = P.new_gen(FF_to_FpXQ_i((x).g)) else: raise TypeError, "Unable to coerce PARI %s to an Integer"%x @@ -4948,8 +4950,6 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): return self._pari_c() cdef _pari_c(self): - cdef PariInstance P - P = sage.libs.pari.gen.pari return P.new_gen_from_mpz_t(self.value) def _interface_init_(self, I=None): diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index 0c54dd29343..c3e0346eb87 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -24,7 +24,7 @@ import field from sage.structure.parent_gens import ParentWithGens -from sage.libs.all import pari_gen +from sage.libs.pari.gen import gen as pari_gen from sage.categories.fields import Fields _Fields = Fields() diff --git a/src/sage/rings/number_field/galois_group.py b/src/sage/rings/number_field/galois_group.py index 7e9145f5ee6..5d448caec05 100644 --- a/src/sage/rings/number_field/galois_group.py +++ b/src/sage/rings/number_field/galois_group.py @@ -25,7 +25,7 @@ from sage.groups.perm_gps.permgroup import PermutationGroup_generic from sage.groups.perm_gps.permgroup_element import PermutationGroupElement from sage.misc.cachefunc import cached_method -from sage.libs.pari.gen import pari +from sage.libs.pari.pari_instance import pari from sage.rings.infinity import infinity from sage.rings.number_field.number_field import refine_embedding from sage.rings.number_field.morphism import NumberFieldHomomorphism_im_gens diff --git a/src/sage/rings/number_field/maps.py b/src/sage/rings/number_field/maps.py index 1f590627bb8..bab674ba821 100644 --- a/src/sage/rings/number_field/maps.py +++ b/src/sage/rings/number_field/maps.py @@ -40,7 +40,7 @@ import sage.rings.rational_field as rational_field -from sage.libs.pari.all import pari +from sage.libs.pari.pari_instance import pari QQ = rational_field.RationalField() diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index ea138427f09..3f235498ee1 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -86,7 +86,6 @@ from sage.misc.cachefunc import cached_method import sage.libs.ntl.all as ntl -import sage.libs.pari.all as pari import sage.interfaces.gap import sage.rings.arith @@ -200,7 +199,8 @@ def proof_flag(t): import number_field_element import number_field_element_quadratic from number_field_ideal import is_NumberFieldIdeal, NumberFieldFractionalIdeal -from sage.libs.all import pari, pari_gen +from sage.libs.pari.pari_instance import pari +from sage.libs.pari.gen import gen as pari_gen QQ = rational_field.RationalField() ZZ = integer_ring.IntegerRing() diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 1f07981892f..2a58f4e19bd 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -50,8 +50,7 @@ from sage.rings.rational cimport Rational from sage.modules.free_module_element import vector -from sage.libs.all import pari_gen, pari -from sage.libs.pari.gen import PariError +from sage.libs.pari.gen import gen as pari_gen from sage.structure.element cimport Element, generic_power_c from sage.structure.element import canonical_coercion, parent diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 1518b24f738..bb453a97d06 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -51,7 +51,6 @@ import number_field -from sage.libs.all import pari_gen, PariError from sage.rings.ideal import (Ideal_generic, Ideal_fractional) from sage.misc.misc import prod from sage.misc.mrange import xmrange_iter @@ -173,6 +172,7 @@ def __init__(self, field, gens, coerce=True): if len(gens) == 1 and isinstance(gens[0], (list, tuple)): gens = gens[0] + from sage.libs.pari.gen import gen as pari_gen if len(gens) == 1 and isinstance(gens[0], pari_gen): # Init from PARI gens = gens[0] @@ -2448,6 +2448,7 @@ def _pari_bid_(self, flag=1): sage: bid.getattr('clgp') [2, [2]] """ + from sage.libs.pari.gen import PariError try: bid = self._bid if flag==2: diff --git a/src/sage/rings/number_field/number_field_rel.py b/src/sage/rings/number_field/number_field_rel.py index 4c2629f24f6..f7cdb7a8f2d 100644 --- a/src/sage/rings/number_field/number_field_rel.py +++ b/src/sage/rings/number_field/number_field_rel.py @@ -78,7 +78,6 @@ from sage.structure.parent_gens import localvars import sage.libs.ntl.all as ntl -import sage.libs.pari.all as pari import sage.rings.arith from sage.categories.map import is_Map @@ -104,7 +103,7 @@ from sage.rings.number_field.number_field_base import is_NumberField from sage.rings.number_field.order import RelativeOrder from sage.rings.number_field.morphism import RelativeNumberFieldHomomorphism_from_abs -from sage.libs.all import pari, pari_gen +from sage.libs.pari.gen import gen as pari_gen from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ @@ -1524,6 +1523,7 @@ def _gen_relative(self): sage: c^2 + 3 0 """ + from sage.libs.pari.pari_instance import pari rnfeqn = self._pari_rnfequation() f = (pari('x') - rnfeqn[2]*rnfeqn[1]).lift() g = self._element_class(self, f) diff --git a/src/sage/rings/number_field/small_primes_of_degree_one.py b/src/sage/rings/number_field/small_primes_of_degree_one.py index b957093e9e7..a572a2a8847 100644 --- a/src/sage/rings/number_field/small_primes_of_degree_one.py +++ b/src/sage/rings/number_field/small_primes_of_degree_one.py @@ -102,7 +102,6 @@ #***************************************************************************** from sage.rings.all import ZZ -from sage.libs.pari.gen import pari class Small_primes_of_degree_one_iter(): r""" @@ -143,6 +142,7 @@ def __init__(self, field, num_integer_primes=10000, max_iterations=100): self._poly = ZZ['x'](self._poly.denominator() * self._poly()) # make integer polynomial # this uses that [ O_K : Z[a] ]^2 = | disc(f(x)) / disc(O_K) | + from sage.libs.pari.pari_instance import pari self._prod_of_small_primes = ZZ(pari('TEMPn = %s; TEMPps = primes(TEMPn); prod(X = 1, TEMPn, TEMPps[X])' % num_integer_primes)) self._prod_of_small_primes //= self._prod_of_small_primes.gcd(self._poly.discriminant()) diff --git a/src/sage/rings/number_field/totallyreal.pyx b/src/sage/rings/number_field/totallyreal.pyx index 5aae1e9a0ee..fb4c9f7c81b 100644 --- a/src/sage/rings/number_field/totallyreal.pyx +++ b/src/sage/rings/number_field/totallyreal.pyx @@ -116,11 +116,12 @@ include 'sage/libs/pari/decl.pxi' import math, sys, bisect -import sage.libs.pari.gen -from sage.libs.pari.gen import pari -cimport sage.libs.pari.gen +from sage.libs.pari.pari_instance cimport PariInstance from sage.libs.pari.gen cimport gen as pari_gen +import sage.libs.pari.pari_instance +cdef PariInstance pari = sage.libs.pari.pari_instance.pari + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.integer import Integer from sage.rings.integer cimport Integer @@ -256,7 +257,6 @@ def enumerate_totallyreal_fields_prim(n, B, a = [], verbose=0, return_seqs=False cdef bint found, use_t2, phc_flag, verb_int, temp_bint cdef Py_ssize_t k0, ind, lenS cdef tr_data T - cdef sage.libs.pari.gen.PariInstance P = sage.libs.pari.gen.pari cdef Integer dB cdef double db_odlyzko @@ -275,7 +275,7 @@ def enumerate_totallyreal_fields_prim(n, B, a = [], verbose=0, return_seqs=False lenS = 0 # This is just to quiet valgrind down - B_pari = P(0) + B_pari = pari(0) d = B_pari d_poly = B_pari keepB = B_pari @@ -355,7 +355,7 @@ def enumerate_totallyreal_fields_prim(n, B, a = [], verbose=0, return_seqs=False T.incr(f_out,0,0,phc_flag) while f_out[n]: - nf = P.new_t_POL_from_int_star(f_out, n_int+1, 0) + nf = pari.new_t_POL_from_int_star(f_out, n_int+1, 0) if verb_int: print "==>", nf, "[" for j from 0 <= j < n-1: diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py index a224b775e1c..6e110ff7d3d 100644 --- a/src/sage/rings/number_field/totallyreal_rel.py +++ b/src/sage/rings/number_field/totallyreal_rel.py @@ -92,7 +92,7 @@ from sage.rings.number_field.number_field import NumberField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.number_field.totallyreal import weed_fields, odlyzko_bound_totallyreal, enumerate_totallyreal_fields_prim -from sage.libs.pari.gen import pari +from sage.libs.pari.pari_instance import pari import math, bisect, sys diff --git a/src/sage/rings/number_field/unit_group.py b/src/sage/rings/number_field/unit_group.py index 49019c3ca8e..4b1524fbb64 100644 --- a/src/sage/rings/number_field/unit_group.py +++ b/src/sage/rings/number_field/unit_group.py @@ -140,7 +140,7 @@ from sage.groups.abelian_gps.values import AbelianGroupWithValues_class from sage.structure.sequence import Sequence from sage.structure.proof.proof import get_flag -from sage.libs.all import pari +from sage.libs.pari.pari_instance import pari from sage.misc.misc import prod from sage.rings.integer_ring import ZZ diff --git a/src/sage/rings/padics/padic_capped_absolute_element.pyx b/src/sage/rings/padics/padic_capped_absolute_element.pyx index b1b052c1453..a121752b756 100644 --- a/src/sage/rings/padics/padic_capped_absolute_element.pyx +++ b/src/sage/rings/padics/padic_capped_absolute_element.pyx @@ -34,7 +34,6 @@ from sage.rings.padics.padic_generic_element cimport pAdicGenericElement import sage.rings.padics.padic_generic_element import sage.rings.finite_rings.integer_mod -import sage.libs.pari.gen import sage.rings.integer import sage.rings.rational @@ -42,10 +41,8 @@ from sage.rings.infinity import infinity from sage.rings.finite_rings.integer_mod import Mod from sage.rings.padics.precision_error import PrecisionError -pari = sage.libs.pari.gen.pari -pari_gen = sage.libs.pari.gen.gen +from sage.libs.pari.gen cimport gen as pari_gen gp_element = sage.interfaces.gp.GpElement -PariError = sage.libs.pari.gen.PariError cdef class pAdicCappedAbsoluteElement(pAdicBaseGenericElement): def __init__(pAdicCappedAbsoluteElement self, parent, x, absprec=infinity, relprec = infinity, empty=False): diff --git a/src/sage/rings/padics/padic_capped_relative_element.pxd b/src/sage/rings/padics/padic_capped_relative_element.pxd index 7ede84f3647..bb716365b2f 100644 --- a/src/sage/rings/padics/padic_capped_relative_element.pxd +++ b/src/sage/rings/padics/padic_capped_relative_element.pxd @@ -15,10 +15,8 @@ from sage.rings.rational cimport Rational cimport sage.rings.padics.pow_computer from sage.rings.padics.pow_computer cimport PowComputer_class -#import sage.libs.pari.gen -cimport sage.libs.pari.gen from sage.libs.pari.gen cimport gen as pari_gen -from sage.libs.pari.gen cimport PariInstance +from sage.libs.pari.pari_instance cimport PariInstance cdef class pAdicCappedRelativeElement(pAdicBaseGenericElement): cdef mpz_t unit #An exact zero is indicated by unit < 0 diff --git a/src/sage/rings/padics/padic_capped_relative_element.pyx b/src/sage/rings/padics/padic_capped_relative_element.pyx index 392cfdc11a2..e0ec320f1c4 100644 --- a/src/sage/rings/padics/padic_capped_relative_element.pyx +++ b/src/sage/rings/padics/padic_capped_relative_element.pyx @@ -55,11 +55,10 @@ from sage.rings.infinity import infinity from sage.rings.finite_rings.integer_mod import Mod from sage.rings.padics.precision_error import PrecisionError +import sage.libs.pari.pari_instance +cdef PariInstance P = sage.libs.pari.pari_instance.pari from sage.interfaces.gp import GpElement - -cdef PariInstance P = sage.libs.pari.all.pari - cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 # 1073741823 or 4611686018427387903 on 32/64 bit. cdef long minusmaxordp = -maxordp diff --git a/src/sage/rings/padics/padic_fixed_mod_element.pyx b/src/sage/rings/padics/padic_fixed_mod_element.pyx index 7ba34e8210d..6ddcc5d6d4a 100644 --- a/src/sage/rings/padics/padic_fixed_mod_element.pyx +++ b/src/sage/rings/padics/padic_fixed_mod_element.pyx @@ -39,7 +39,6 @@ from sage.rings.padics.padic_printing cimport pAdicPrinter_class #import sage.rings.padics.padic_field_generic_element #import sage.rings.padics.padic_lazy_element import sage.rings.finite_rings.integer_mod -import sage.libs.pari.gen import sage.rings.integer import sage.rings.rational @@ -49,10 +48,9 @@ from sage.rings.padics.precision_error import PrecisionError #pAdicLazyElement = sage.rings.padics.padic_lazy_element.pAdicLazyElement #pAdicGenericElement = sage.rings.padics.padic_generic_element.pAdicGenericElement -pari = sage.libs.pari.gen.pari -pari_gen = sage.libs.pari.gen.gen + +from sage.libs.pari.gen cimport gen as pari_gen from sage.interfaces.gp import GpElement -PariError = sage.libs.pari.gen.PariError cdef class pAdicFixedModElement(pAdicBaseGenericElement): def __init__(pAdicFixedModElement self, parent, x, absprec = None, relprec = None, empty = False): diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index b660e1f14c8..049e7c3924b 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -35,8 +35,6 @@ from sage.rings.padics.local_generic_element cimport LocalGenericElement from sage.rings.rational cimport Rational from sage.rings.integer cimport Integer from sage.rings.infinity import infinity -from sage.libs.pari.gen import pari -from sage.libs.pari.gen import PariError import sage.rings.rational_field cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 @@ -1840,9 +1838,11 @@ cdef class pAdicGenericElement(LocalGenericElement): # whose square root is a real number....! if self.valuation() is infinity: return self + + from sage.libs.pari.gen import PariError try: # use pari - ans = self.parent()(pari(self).sqrt()) + ans = self.parent()(self._pari_().sqrt()) if all: return [ans, -ans] else: diff --git a/src/sage/rings/polynomial/cyclotomic.pyx b/src/sage/rings/polynomial/cyclotomic.pyx index 676c3733205..0e7f38b7da6 100644 --- a/src/sage/rings/polynomial/cyclotomic.pyx +++ b/src/sage/rings/polynomial/cyclotomic.pyx @@ -36,7 +36,7 @@ from sage.misc.misc import prod, subsets from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.libs.pari.gen cimport gen -from sage.libs.pari.gen import pari, PariError +from sage.libs.pari.pari_instance import pari def cyclotomic_coeffs(nn, sparse=None): u""" diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 8d6612ccddd..ec7b2b2276a 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -111,8 +111,7 @@ import sage.misc.latex import rational_field, integer_ring from integer import Integer from sage.rings.finite_rings.integer_mod_ring import IntegerModRing -from sage.libs.pari.all import pari -from sage.libs.all import PariError +from sage.libs.pari.pari_instance import pari from sage.misc.functional import sqrt, log from sage.rings.arith import integer_ceil as ceil diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index b43d49a1b78..ebb188c48a4 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -498,7 +498,6 @@ from sage.rings.number_field.number_field import NumberField, QuadraticField, CyclotomicField from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic from sage.rings.arith import factor -from sage.libs.pari.gen import pari from sage.structure.element import generic_power, canonical_coercion import infinity from sage.misc.functional import cyclotomic_polynomial diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 428d0720cf0..e06e02dd154 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -55,13 +55,14 @@ from sage.misc.mathml import mathml import sage.misc.misc as misc import sage.rings.rational_field -import sage.libs.pari.all cimport integer import integer from integer cimport Integer -from sage.libs.pari.gen cimport gen as pari_gen, PariInstance +import sage.libs.pari.pari_instance +from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.pari.pari_instance cimport PariInstance from integer_ring import ZZ @@ -528,7 +529,7 @@ cdef class Rational(sage.structure.element.FieldElement): raise ValueError, "denominator must not be 0" mpq_canonicalize(self.value) - elif isinstance(x, sage.libs.pari.all.pari_gen): + elif isinstance(x, pari_gen): x = x.simplify() if typ((x).g) == t_FRAC: t_FRAC_to_QQ(self.value, (x).g) @@ -3442,7 +3443,7 @@ cdef class Rational(sage.structure.element.FieldElement): sage: m.type() 't_FRAC' """ - cdef PariInstance P = sage.libs.pari.gen.pari + cdef PariInstance P = sage.libs.pari.pari_instance.pari return P.new_gen_from_mpq_t(self.value) def _interface_init_(self, I=None): diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 9ba06c1ffc9..f82c4774d99 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -45,9 +45,8 @@ gsl_set_error_handler_off() import math, operator -cimport sage.libs.pari.gen -import sage.libs.pari.gen - +import sage.libs.pari.pari_instance +from sage.libs.pari.pari_instance cimport PariInstance import sage.rings.integer import sage.rings.rational @@ -1528,8 +1527,8 @@ cdef class RealDoubleElement(FieldElement): sage: RDF(1.5)._pari_() 1.50000000000000 """ - cdef sage.libs.pari.gen.PariInstance P = sage.libs.pari.gen.pari - return P.double_to_gen_c(self._value) + cdef PariInstance P = sage.libs.pari.pari_instance.pari + return P.new_gen_from_double(self._value) ########################################### diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 5d93283cf81..93c0b6e194d 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -138,8 +138,9 @@ import sage.misc.weak_dict import operator -from sage.libs.pari.gen import PariInstance, gen -from sage.libs.pari.gen cimport PariInstance, gen +import sage.libs.pari.pari_instance +from sage.libs.pari.gen cimport gen +from sage.libs.pari.pari_instance cimport PariInstance from sage.libs.mpmath.utils cimport mpfr_to_mpfval @@ -3042,8 +3043,7 @@ cdef class RealNumber(sage.structure.element.RingElement): mpz_export(&pari_float[2], NULL, 1, wordsize/8, 0, 0, mantissa) mpz_clear(mantissa) - cdef PariInstance P - P = sage.libs.pari.all.pari + cdef PariInstance P = sage.libs.pari.pari_instance.pari return P.new_gen(pari_float) def _mpmath_(self, prec=None, rounding=None): diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index fdab1744357..801adcdf2af 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -130,8 +130,7 @@ from sage.rings.all import ZZ from sage.groups.all import AbelianGroup import sage.groups.generic as generic -from sage.libs.pari.all import pari, PariError -from sage.libs.pari.gen import prec_words_to_bits +from sage.libs.pari.pari_instance import pari, prec_words_to_bits from sage.structure.sequence import Sequence from sage.schemes.plane_curves.projective_curve import Hasse_bounds @@ -2075,6 +2074,7 @@ def order(self): E = self.curve() # Special code for curves over Q, calling PARI + from sage.libs.pari.gen import PariError try: n = int(E.pari_curve().ellorder(self)) if n == 0: diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 5cc0f37ba81..2bf8ba662c2 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -37,7 +37,6 @@ from sage.categories.homset import Hom from sage.functions.all import sqrt -from sage.libs.pari.gen import pari from sage.matrix.constructor import matrix, identity_matrix from sage.misc.cachefunc import cached_method from sage.misc.misc import subsets @@ -1023,7 +1022,7 @@ def resultant(self, normalize=False): d=self.degree() f=F[0].substitute({y:1}) g=F[1].substitute({y:1}) - res=(f.lc()**(d-g.degree())*g.lc()**(d-f.degree())*pari(f).polresultant(g,x)) + res=(f.lc()**(d-g.degree())*g.lc()**(d-f.degree())*f._pari_().polresultant(g, x)) return(self.codomain().base_ring()(res)) @cached_method diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 463eb7cf9d4..6ae1e36ceed 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -741,7 +741,7 @@ cdef class SageObject: return self.__pari except AttributeError: pass - from sage.libs.pari.all import pari + from sage.libs.pari.pari_instance import pari x = pari(self._pari_init_()) if self._interface_is_cached_(): try: diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 8977711d7e4..0421b837137 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -26,6 +26,7 @@ from sage.rings.real_mpfr import RealNumber from sage.symbolic.expression cimport Expression, new_Expression_from_GEx, new_Expression_from_pyobject, is_Expression +from sage.libs.pari.pari_instance cimport PariInstance from sage.misc.latex import latex_variable_name from sage.structure.element cimport RingElement, Element from sage.structure.parent_base import ParentWithBase @@ -166,7 +167,6 @@ cdef class SymbolicRing(CommutativeRing): is_FiniteField) from sage.interfaces.maxima import Maxima - from sage.libs.pari.gen import PariInstance if ComplexField(mpfr_prec_min()).has_coerce_map_from(R): # Anything with a coercion into any precision of CC From db8cd7e36829d173baad8d27276e091265e6d998 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sun, 17 Nov 2013 13:23:33 +0000 Subject: [PATCH 113/206] top-level changes related to PariInstance --- src/doc/en/reference/libs/index.rst | 1 + src/module_list.py | 4 ++++ src/sage/libs/pari/all.py | 14 ++------------ 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/doc/en/reference/libs/index.rst b/src/doc/en/reference/libs/index.rst index d1975ebee18..7081b20cb55 100644 --- a/src/doc/en/reference/libs/index.rst +++ b/src/doc/en/reference/libs/index.rst @@ -32,6 +32,7 @@ to be aware of the modules described in this chapter. sage/libs/mwrank/all sage/libs/mwrank/mwrank sage/libs/mwrank/interface + sage/libs/pari/pari_instance sage/libs/pari/gen sage/libs/ppl sage/ext/pselect diff --git a/src/module_list.py b/src/module_list.py index 3fa481ee7af..2351e81c97c 100755 --- a/src/module_list.py +++ b/src/module_list.py @@ -708,6 +708,10 @@ def uname_specific(name, value, alternative): sources = ["sage/libs/pari/handle_error.pyx"], libraries = ['pari', 'gmp']), + Extension('sage.libs.pari.pari_instance', + sources = ["sage/libs/pari/pari_instance.pyx"], + libraries = ['pari', 'gmp']), + Extension('sage.libs.ppl', sources = ['sage/libs/ppl.pyx', 'sage/libs/ppl_shim.cc'], libraries = ['ppl', 'gmpxx', 'gmp', 'm'], diff --git a/src/sage/libs/pari/all.py b/src/sage/libs/pari/all.py index c346a8b3a02..ce4ad8c5e9e 100644 --- a/src/sage/libs/pari/all.py +++ b/src/sage/libs/pari/all.py @@ -1,13 +1,3 @@ -import gen as _gen - -pari = _gen.pari - -pari_gen = _gen.gen - +from gen import gen as pari_gen, PariError +from pari_instance import pari allocatemem = pari.allocatemem - -PariError = _gen.PariError - -from gen import (prec_dec_to_words, prec_dec_to_bits, - prec_bits_to_words, prec_bits_to_dec, - prec_words_to_bits, prec_words_to_dec) From 34013cc5ecd209957432bbd09a2dbbf44f269cf1 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 11 Dec 2013 00:51:07 +0000 Subject: [PATCH 114/206] fixes in sage.libs.pari --- src/sage/libs/pari/gen.pxd | 2 + src/sage/libs/pari/gen.pyx | 22 ++++++----- src/sage/libs/pari/handle_error.pyx | 3 +- src/sage/libs/pari/pari_instance.pxd | 4 +- src/sage/libs/pari/pari_instance.pyx | 57 ++++++++++++++++++---------- 5 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/sage/libs/pari/gen.pxd b/src/sage/libs/pari/gen.pxd index 289b12ab5cd..371834f5e78 100644 --- a/src/sage/libs/pari/gen.pxd +++ b/src/sage/libs/pari/gen.pxd @@ -15,3 +15,5 @@ cdef class gen(sage.structure.element.RingElement): cdef gen pari(self, object x) cdef GEN _deepcopy_to_python_heap(self, GEN x, pari_sp* address) cdef long get_var(self, v) + +cdef gen objtogen(object s) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 1f72d1cb1ca..5664435443e 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -39,17 +39,12 @@ AUTHORS: #***************************************************************************** -import sys import math import types import operator import sage.structure.element from sage.structure.element cimport ModuleElement, RingElement, Element -from sage.structure.parent cimport Parent from sage.misc.randstate cimport randstate, current_randstate -from sage.libs.pari.handle_error cimport pari_error_string, \ - _pari_init_error_handling, _pari_check_warning, \ - _pari_handle_exception, _pari_err_recover from sage.misc.misc_c import is_64_bit @@ -58,16 +53,26 @@ include 'sage/ext/stdsage.pxi' include 'sage/ext/python.pxi' include 'sage/ext/interrupt.pxi' -cimport libc.stdlib cimport cython cdef extern from "misc.h": int factorint_withproof_sage(GEN* ans, GEN x, GEN cutoff) int gcmp_sage(GEN x, GEN y) +cdef extern from "mpz_pylong.h": + cdef int mpz_set_pylong(mpz_t dst, src) except -1 + # Will be imported as needed Integer = None +import pari_instance +from pari_instance cimport PariInstance +cdef PariInstance P, pari +P = pari = pari_instance.pari + +from pari_instance import pbw, prec_bits_to_words, prec_words_to_dec + +cdef long prec = prec_bits_to_words(53) @cython.final cdef class gen(sage.structure.element.RingElement): @@ -80,7 +85,7 @@ cdef class gen(sage.structure.element.RingElement): self._refers_to = {} def parent(self): - return pari_instance + return P cdef void init(self, GEN g, pari_sp b): """ @@ -5334,9 +5339,8 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(500509).primepi() 41581 """ - global num_primes pari_catch_sig_on() - if self > num_primes: + if self > P._primelimit(): P.init_primes(self + 10) if signe(self.g) != 1: pari_catch_sig_off() diff --git a/src/sage/libs/pari/handle_error.pyx b/src/sage/libs/pari/handle_error.pyx index b8b21f3512b..56664c0e0c5 100644 --- a/src/sage/libs/pari/handle_error.pyx +++ b/src/sage/libs/pari/handle_error.pyx @@ -76,16 +76,17 @@ cdef int _pari_handle_exception(long err) except 0: PariError: division by zero """ - from sage.libs.pari.gen import pari, PariError if err == errpile: # PARI is out of memory. We double the size of the PARI stack # and retry the computation. + from sage.libs.pari.pari_instance import pari pari.allocatemem(silent=True) return 0 if err == user: raise RuntimeError("PARI user exception\n%s" % pari_error_string) else: + from sage.libs.pari.gen import PariError raise PariError(err, pari_error_string) cdef void _pari_err_recover(long err): diff --git a/src/sage/libs/pari/pari_instance.pxd b/src/sage/libs/pari/pari_instance.pxd index 4669b7a34ab..083f7f06fcb 100644 --- a/src/sage/libs/pari/pari_instance.pxd +++ b/src/sage/libs/pari/pari_instance.pxd @@ -3,6 +3,8 @@ include 'decl.pxi' cimport sage.structure.parent_base cimport cython +from sage.libs.pari.gen cimport gen + @cython.final cdef class PariInstance(sage.structure.parent_base.ParentWithBase): cdef gen PARI_ZERO, PARI_ONE, PARI_TWO @@ -27,5 +29,3 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): cdef gen integer_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc, bint permute_for_hnf) cdef GEN _new_GEN_from_mpq_t_matrix(self, mpq_t** B, Py_ssize_t nr, Py_ssize_t nc) cdef gen rational_matrix(self, mpq_t** B, Py_ssize_t nr, Py_ssize_t nc) - -cdef GEN _Vec_append(GEN v, GEN a, long n) diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx index 561018419f6..af33521696d 100644 --- a/src/sage/libs/pari/pari_instance.pyx +++ b/src/sage/libs/pari/pari_instance.pyx @@ -153,8 +153,22 @@ Sage (ticket #9636):: """ -cdef extern from "mpz_pylong.h": - cdef int mpz_set_pylong(mpz_t dst, src) except -1 +include 'pari_err.pxi' +include 'sage/ext/stdsage.pxi' +include 'sage/ext/python.pxi' +include 'sage/ext/interrupt.pxi' + +import sys + +cimport libc.stdlib +cimport cython + +from sage.structure.parent cimport Parent + +from sage.libs.pari.gen cimport gen, objtogen +from sage.libs.pari.handle_error cimport pari_error_string, \ + _pari_init_error_handling, _pari_check_warning, \ + _pari_handle_exception, _pari_err_recover # so Galois groups are represented in a sane way # See the polgalois section of the PARI users manual. @@ -195,10 +209,10 @@ def prec_bits_to_dec(int prec_in_bits): EXAMPLES:: - sage: import sage.libs.pari.gen as gen - sage: gen.prec_bits_to_dec(53) + sage: from sage.libs.pari.pari_instance import prec_bits_to_dec + sage: prec_bits_to_dec(53) 15 - sage: [(32*n,gen.prec_bits_to_dec(32*n)) for n in range(1,9)] + sage: [(32*n, prec_bits_to_dec(32*n)) for n in range(1, 9)] [(32, 9), (64, 19), (96, 28), @@ -218,10 +232,10 @@ def prec_dec_to_bits(int prec_in_dec): EXAMPLES:: - sage: import sage.libs.pari.gen as gen - sage: gen.prec_dec_to_bits(15) + sage: from sage.libs.pari.pari_instance import prec_dec_to_bits + sage: prec_dec_to_bits(15) 49 - sage: [(n,gen.prec_dec_to_bits(n)) for n in range(10,100,10)] + sage: [(n, prec_dec_to_bits(n)) for n in range(10, 100, 10)] [(10, 33), (20, 66), (30, 99), @@ -244,14 +258,14 @@ def prec_bits_to_words(int prec_in_bits=0): EXAMPLES:: - sage: import sage.libs.pari.gen as gen - sage: gen.prec_bits_to_words(70) + sage: from sage.libs.pari.pari_instance import prec_bits_to_words + sage: prec_bits_to_words(70) 5 # 32-bit 4 # 64-bit :: - sage: [(32*n,gen.prec_bits_to_words(32*n)) for n in range(1,9)] + sage: [(32*n, prec_bits_to_words(32*n)) for n in range(1, 9)] [(32, 3), (64, 4), (96, 5), (128, 6), (160, 7), (192, 8), (224, 9), (256, 10)] # 32-bit [(32, 3), (64, 3), (96, 4), (128, 4), (160, 5), (192, 5), (224, 6), (256, 6)] # 64-bit """ @@ -275,11 +289,11 @@ def prec_words_to_bits(int prec_in_words): EXAMPLES:: - sage: import sage.libs.pari.gen as gen - sage: gen.prec_words_to_bits(10) + sage: from sage.libs.pari.pari_instance import prec_words_to_bits + sage: prec_words_to_bits(10) 256 # 32-bit 512 # 64-bit - sage: [(n,gen.prec_words_to_bits(n)) for n in range(3,10)] + sage: [(n, prec_words_to_bits(n)) for n in range(3, 10)] [(3, 32), (4, 64), (5, 96), (6, 128), (7, 160), (8, 192), (9, 224)] # 32-bit [(3, 64), (4, 128), (5, 192), (6, 256), (7, 320), (8, 384), (9, 448)] # 64-bit """ @@ -294,11 +308,11 @@ def prec_dec_to_words(int prec_in_dec): EXAMPLES:: - sage: import sage.libs.pari.gen as gen - sage: gen.prec_dec_to_words(38) + sage: from sage.libs.pari.pari_instance import prec_dec_to_words + sage: prec_dec_to_words(38) 6 # 32-bit 4 # 64-bit - sage: [(n,gen.prec_dec_to_words(n)) for n in range(10,90,10)] + sage: [(n, prec_dec_to_words(n)) for n in range(10, 90, 10)] [(10, 4), (20, 5), (30, 6), (40, 7), (50, 8), (60, 9), (70, 10), (80, 11)] # 32-bit [(10, 3), (20, 4), (30, 4), (40, 5), (50, 5), (60, 6), (70, 6), (80, 7)] # 64-bit """ @@ -312,11 +326,11 @@ def prec_words_to_dec(int prec_in_words): EXAMPLES:: - sage: import sage.libs.pari.gen as gen - sage: gen.prec_words_to_dec(5) + sage: from sage.libs.pari.pari_instance import prec_words_to_dec + sage: prec_words_to_dec(5) 28 # 32-bit 57 # 64-bit - sage: [(n,gen.prec_words_to_dec(n)) for n in range(3,10)] + sage: [(n, prec_words_to_dec(n)) for n in range(3, 10)] [(3, 9), (4, 19), (5, 28), (6, 38), (7, 48), (8, 57), (9, 67)] # 32-bit [(3, 19), (4, 38), (5, 57), (6, 77), (7, 96), (8, 115), (9, 134)] # 64-bit """ @@ -1183,7 +1197,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): EXAMPLES: sage: pari._primelimit() - 500519 + 500000 sage: pari.init_primes(600000) sage: pari._primelimit() 600000 @@ -1270,6 +1284,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): def nth_prime(self, long n): + from sage.libs.pari.gen import PariError try: return self.__nth_prime(n) except PariError: From 11a3bfbda21bde9c0d0778442cdd3607db14e583 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 18 Nov 2013 00:43:43 +0000 Subject: [PATCH 115/206] more fixes related to relocation of PariInstance --- src/sage/misc/randstate.pyx | 2 +- src/sage/rings/integer.pyx | 7 +++---- src/sage/rings/real_double.pyx | 2 +- src/sage/schemes/elliptic_curves/cm.py | 2 +- src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/sage/misc/randstate.pyx b/src/sage/misc/randstate.pyx index a277295cc30..def865c694b 100644 --- a/src/sage/misc/randstate.pyx +++ b/src/sage/misc/randstate.pyx @@ -794,7 +794,7 @@ cdef class randstate: """ global _pari_seed_randstate if _pari_seed_randstate is not self: - from sage.libs.pari.gen import pari + from sage.libs.pari.pari_instance import pari if self._pari_saved_seed is not None: seed = self._pari_saved_seed diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index c39ed12e87c..64c57b067fe 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -179,7 +179,7 @@ from sage.libs.flint.ulong_extras cimport * import sage.rings.infinity import sage.libs.pari.pari_instance -cdef PariInstance P = sage.libs.pari.pari_instance.pari +cdef PariInstance pari = sage.libs.pari.pari_instance.pari from sage.structure.element import canonical_coercion from sage.misc.superseded import deprecated_function_alias @@ -621,7 +621,6 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): cdef unsigned int ibase cdef Element lift - cdef PariInstance pari if x is None: if mpz_sgn(self.value) != 0: @@ -679,7 +678,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): # x = (f modulo defining polynomial of finite field); # we extract f. sig_on() - x = P.new_gen(FF_to_FpXQ_i((x).g)) + x = pari.new_gen(FF_to_FpXQ_i((x).g)) else: raise TypeError, "Unable to coerce PARI %s to an Integer"%x @@ -4950,7 +4949,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): return self._pari_c() cdef _pari_c(self): - return P.new_gen_from_mpz_t(self.value) + return pari.new_gen_from_mpz_t(self.value) def _interface_init_(self, I=None): """ diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index f82c4774d99..f8b20a3c937 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -1528,7 +1528,7 @@ cdef class RealDoubleElement(FieldElement): 1.50000000000000 """ cdef PariInstance P = sage.libs.pari.pari_instance.pari - return P.new_gen_from_double(self._value) + return P.double_to_gen(self._value) ########################################### diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py index e14a2755f78..6bb5a103db4 100644 --- a/src/sage/schemes/elliptic_curves/cm.py +++ b/src/sage/schemes/elliptic_curves/cm.py @@ -411,7 +411,7 @@ def discriminants_with_bounded_class_number(hmax, B=None, proof=None): """ # imports that are needed only for this function from sage.structure.proof.proof import get_flag - from sage.libs.pari.gen import pari + from sage.libs.pari.pari_instance import pari import math from sage.misc.functional import round diff --git a/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx b/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx index 56ee3a33c9b..ba0c446bae9 100644 --- a/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx +++ b/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx @@ -15,7 +15,6 @@ cdef object x_ZZ = polygen(ZZ) from sage.rings.polynomial.real_roots import real_roots from sage.rings.arith import prime_divisors from sage.misc.all import walltime, cputime -from sage.libs.pari.gen import pari from sage.all import ntl from sage.rings.integer cimport Integer @@ -1247,6 +1246,7 @@ def two_descent_by_two_isogeny_work(Integer c, Integer d, p_list_len += 1 else: # Factor more slowly using Pari via Python. + from sage.libs.pari.pari_instance import pari d = Integer(0) mpz_set(d.value, d_mpz) primes = list(pari(d).factor()[0]) From 95a622a9843d9f99f7fa8009babb86ac27e2b32b Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 11 Dec 2013 02:19:10 +0000 Subject: [PATCH 116/206] better fix for real_double.pyx --- src/sage/rings/real_double.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index f8b20a3c937..f5fbb42507d 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -47,6 +47,7 @@ import math, operator import sage.libs.pari.pari_instance from sage.libs.pari.pari_instance cimport PariInstance +cdef PariInstance pari = sage.libs.pari.pari_instance.pari import sage.rings.integer import sage.rings.rational @@ -1527,8 +1528,7 @@ cdef class RealDoubleElement(FieldElement): sage: RDF(1.5)._pari_() 1.50000000000000 """ - cdef PariInstance P = sage.libs.pari.pari_instance.pari - return P.double_to_gen(self._value) + return pari.double_to_gen_c(self._value) ########################################### From 05b93b74d49de78357895d254e6c8c4a3d10c69d Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 11 Dec 2013 13:04:29 +0000 Subject: [PATCH 117/206] remove "pbw" as an abbreviation for "prec_bits_to_words" --- src/sage/libs/pari/gen.pyx | 68 ++++++++++++++-------------- src/sage/libs/pari/pari_instance.pyx | 6 +-- 2 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 5664435443e..fb08b38228f 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -70,7 +70,7 @@ from pari_instance cimport PariInstance cdef PariInstance P, pari P = pari = pari_instance.pari -from pari_instance import pbw, prec_bits_to_words, prec_words_to_dec +from pari_instance import prec_bits_to_words, prec_words_to_dec cdef long prec = prec_bits_to_words(53) @@ -3946,7 +3946,7 @@ cdef class gen(sage.structure.element.RingElement): 0.849343054245252 - 1.09770986682533*I """ pari_catch_sig_on() - return P.new_gen(gacos(x.g, pbw(precision))) + return P.new_gen(gacos(x.g, prec_bits_to_words(precision))) def acosh(gen x, precision=0): r""" @@ -3971,7 +3971,7 @@ cdef class gen(sage.structure.element.RingElement): 0.881373587019543 + 1.57079632679490*I """ pari_catch_sig_on() - return P.new_gen(gach(x.g, pbw(precision))) + return P.new_gen(gach(x.g, prec_bits_to_words(precision))) def agm(gen x, y, precision=0): r""" @@ -4019,7 +4019,7 @@ cdef class gen(sage.structure.element.RingElement): 0.463647609000806 """ pari_catch_sig_on() - return P.new_gen(garg(x.g, pbw(precision))) + return P.new_gen(garg(x.g, prec_bits_to_words(precision))) def asin(gen x, precision=0): r""" @@ -4041,7 +4041,7 @@ cdef class gen(sage.structure.element.RingElement): 1.57079632679490 - 1.31695789692482*I """ pari_catch_sig_on() - return P.new_gen(gasin(x.g, pbw(precision))) + return P.new_gen(gasin(x.g, prec_bits_to_words(precision))) def asinh(gen x, precision=0): r""" @@ -4062,7 +4062,7 @@ cdef class gen(sage.structure.element.RingElement): 1.52857091948100 + 0.427078586392476*I """ pari_catch_sig_on() - return P.new_gen(gash(x.g, pbw(precision))) + return P.new_gen(gash(x.g, prec_bits_to_words(precision))) def atan(gen x, precision=0): r""" @@ -4083,7 +4083,7 @@ cdef class gen(sage.structure.element.RingElement): 1.10714871779409 + 0.255412811882995*I """ pari_catch_sig_on() - return P.new_gen(gatan(x.g, pbw(precision))) + return P.new_gen(gatan(x.g, prec_bits_to_words(precision))) def atanh(gen x, precision=0): r""" @@ -4105,7 +4105,7 @@ cdef class gen(sage.structure.element.RingElement): 0.549306144334055 - 1.57079632679490*I """ pari_catch_sig_on() - return P.new_gen(gath(x.g, pbw(precision))) + return P.new_gen(gath(x.g, prec_bits_to_words(precision))) def bernfrac(gen x): r""" @@ -4358,7 +4358,7 @@ cdef class gen(sage.structure.element.RingElement): 1 - 1/2*x^2 + 1/24*x^4 - 1/720*x^6 + 1/40320*x^8 + O(x^9) """ pari_catch_sig_on() - return P.new_gen(gcos(x.g, pbw(precision))) + return P.new_gen(gcos(x.g, prec_bits_to_words(precision))) def cosh(gen x, precision=0): """ @@ -4380,7 +4380,7 @@ cdef class gen(sage.structure.element.RingElement): 1 + 1/2*x^2 + 1/24*x^4 + 1/720*x^6 + O(x^8) """ pari_catch_sig_on() - return P.new_gen(gch(x.g, pbw(precision))) + return P.new_gen(gch(x.g, prec_bits_to_words(precision))) def cotan(gen x, precision=0): """ @@ -4407,7 +4407,7 @@ cdef class gen(sage.structure.element.RingElement): -8.17674825 E15 """ pari_catch_sig_on() - return P.new_gen(gcotan(x.g, pbw(precision))) + return P.new_gen(gcotan(x.g, prec_bits_to_words(precision))) def dilog(gen x, precision=0): r""" @@ -4429,7 +4429,7 @@ cdef class gen(sage.structure.element.RingElement): 0.616850275068085 + 1.46036211675312*I """ pari_catch_sig_on() - return P.new_gen(dilog(x.g, pbw(precision))) + return P.new_gen(dilog(x.g, prec_bits_to_words(precision))) def eint1(gen x, long n=0, precision=0): r""" @@ -4457,9 +4457,9 @@ cdef class gen(sage.structure.element.RingElement): """ pari_catch_sig_on() if n <= 0: - return P.new_gen(eint1(x.g, pbw(precision))) + return P.new_gen(eint1(x.g, prec_bits_to_words(precision))) else: - return P.new_gen(veceint1(x.g, stoi(n), pbw(precision))) + return P.new_gen(veceint1(x.g, stoi(n), prec_bits_to_words(precision))) def erfc(gen x, precision=0): r""" @@ -4482,7 +4482,7 @@ cdef class gen(sage.structure.element.RingElement): 0.157299207050285 """ pari_catch_sig_on() - return P.new_gen(gerfc(x.g, pbw(precision))) + return P.new_gen(gerfc(x.g, prec_bits_to_words(precision))) def eta(gen x, flag=0, precision=0): r""" @@ -4511,8 +4511,8 @@ cdef class gen(sage.structure.element.RingElement): """ pari_catch_sig_on() if flag == 1: - return P.new_gen(trueeta(x.g, pbw(precision))) - return P.new_gen(eta(x.g, pbw(precision))) + return P.new_gen(trueeta(x.g, prec_bits_to_words(precision))) + return P.new_gen(eta(x.g, prec_bits_to_words(precision))) def exp(gen self, precision=0): """ @@ -4533,7 +4533,7 @@ cdef class gen(sage.structure.element.RingElement): 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + 1/720*x^6 + 1/5040*x^7 + O(x^8) """ pari_catch_sig_on() - return P.new_gen(gexp(self.g, pbw(precision))) + return P.new_gen(gexp(self.g, prec_bits_to_words(precision))) def gamma(gen s, precision=0): """ @@ -4562,7 +4562,7 @@ cdef class gen(sage.structure.element.RingElement): PariError: non-positive integer argument in ggamma """ pari_catch_sig_on() - return P.new_gen(ggamma(s.g, pbw(precision))) + return P.new_gen(ggamma(s.g, prec_bits_to_words(precision))) def gammah(gen s, precision=0): """ @@ -4584,7 +4584,7 @@ cdef class gen(sage.structure.element.RingElement): 0.575315188063452 + 0.0882106775440939*I """ pari_catch_sig_on() - return P.new_gen(ggamd(s.g, pbw(precision))) + return P.new_gen(ggamd(s.g, prec_bits_to_words(precision))) def hyperu(gen a, b, x, precision=0): r""" @@ -4699,7 +4699,7 @@ cdef class gen(sage.structure.element.RingElement): 0.E-19 + 1.57079632679490*I """ pari_catch_sig_on() - return P.new_gen(glog(x.g, pbw(precision))) + return P.new_gen(glog(x.g, prec_bits_to_words(precision))) def lngamma(gen x, precision=0): r""" @@ -4742,7 +4742,7 @@ cdef class gen(sage.structure.element.RingElement): 359.134205369575 """ pari_catch_sig_on() - return P.new_gen(glngamma(x.g, pbw(precision))) + return P.new_gen(glngamma(x.g, prec_bits_to_words(precision))) def polylog(gen x, long m, flag=0, precision=0): """ @@ -4770,7 +4770,7 @@ cdef class gen(sage.structure.element.RingElement): -0.400459056163451 """ pari_catch_sig_on() - return P.new_gen(polylog0(m, x.g, flag, pbw(precision))) + return P.new_gen(polylog0(m, x.g, flag, prec_bits_to_words(precision))) def psi(gen x, precision=0): r""" @@ -4790,7 +4790,7 @@ cdef class gen(sage.structure.element.RingElement): -0.577215664901533 """ pari_catch_sig_on() - return P.new_gen(gpsi(x.g, pbw(precision))) + return P.new_gen(gpsi(x.g, prec_bits_to_words(precision))) def sin(gen x, precision=0): """ @@ -4810,7 +4810,7 @@ cdef class gen(sage.structure.element.RingElement): 1.29845758141598 + 0.634963914784736*I """ pari_catch_sig_on() - return P.new_gen(gsin(x.g, pbw(precision))) + return P.new_gen(gsin(x.g, prec_bits_to_words(precision))) def sinh(gen x, precision=0): """ @@ -4830,7 +4830,7 @@ cdef class gen(sage.structure.element.RingElement): 0.634963914784736 + 1.29845758141598*I """ pari_catch_sig_on() - return P.new_gen(gsh(x.g, pbw(precision))) + return P.new_gen(gsh(x.g, prec_bits_to_words(precision))) def sqr(gen x): """ @@ -4874,7 +4874,7 @@ cdef class gen(sage.structure.element.RingElement): 1.41421356237310 """ pari_catch_sig_on() - return P.new_gen(gsqrt(x.g, pbw(precision))) + return P.new_gen(gsqrt(x.g, prec_bits_to_words(precision))) def sqrtn(gen x, n, precision=0): r""" @@ -4954,7 +4954,7 @@ cdef class gen(sage.structure.element.RingElement): 0.E-19 + 0.761594155955765*I """ pari_catch_sig_on() - return P.new_gen(gtan(x.g, pbw(precision))) + return P.new_gen(gtan(x.g, prec_bits_to_words(precision))) def tanh(gen x, precision=0): """ @@ -4979,7 +4979,7 @@ cdef class gen(sage.structure.element.RingElement): 1.55740772465490 """ pari_catch_sig_on() - return P.new_gen(gth(x.g, pbw(precision))) + return P.new_gen(gth(x.g, prec_bits_to_words(precision))) def teichmuller(gen x): r""" @@ -5030,7 +5030,7 @@ cdef class gen(sage.structure.element.RingElement): 0.548978532560341 """ pari_catch_sig_on() - return P.new_gen(thetanullk(q.g, k, pbw(precision))) + return P.new_gen(thetanullk(q.g, k, prec_bits_to_words(precision))) def weber(gen x, flag=0, precision=0): r""" @@ -5061,7 +5061,7 @@ cdef class gen(sage.structure.element.RingElement): 1.09050773266526 """ pari_catch_sig_on() - return P.new_gen(weber0(x.g, flag, pbw(precision))) + return P.new_gen(weber0(x.g, flag, prec_bits_to_words(precision))) def zeta(gen s, precision=0): """ @@ -5457,7 +5457,7 @@ cdef class gen(sage.structure.element.RingElement): [0, x, 0, 2*x, 1, 4*x, 4*x, 4, -4*x^2 + 4*x, 16*x^2 - 96*x, -64*x^3 + 576*x^2 - 864, 64*x^4 - 576*x^3 + 576*x^2 - 432, (256*x^6 - 4608*x^5 + 27648*x^4 - 55296*x^3)/(4*x^4 - 36*x^3 + 36*x^2 - 27)] """ pari_catch_sig_on() - return P.new_gen(ellinit0(self.g, flag, pbw(precision))) + return P.new_gen(ellinit0(self.g, flag, prec_bits_to_words(precision))) def ellglobalred(self): """ @@ -5623,7 +5623,7 @@ cdef class gen(sage.structure.element.RingElement): [2, 1.51863300057685] """ pari_catch_sig_on() - return self.new_gen(ellanalyticrank(self.g, 0, pbw(precision))) + return self.new_gen(ellanalyticrank(self.g, 0, prec_bits_to_words(precision))) def ellap(self, p): r""" @@ -7306,7 +7306,7 @@ cdef class gen(sage.structure.element.RingElement): # If explicit precision is given, use only that if precision: pari_catch_sig_on() - return P.new_gen(nfinit0(self.g, flag, pbw(precision))) + return P.new_gen(nfinit0(self.g, flag, prec_bits_to_words(precision))) # Otherwise, start with 64 bits of precision and increase as needed: precision = 64 diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx index af33521696d..555d765579f 100644 --- a/src/sage/libs/pari/pari_instance.pyx +++ b/src/sage/libs/pari/pari_instance.pyx @@ -279,8 +279,6 @@ def prec_bits_to_words(int prec_in_bits=0): padded_bits = (prec_in_bits + wordsize - 1) & ~(wordsize - 1) return int(padded_bits/wordsize + 2) -pbw = prec_bits_to_words - def prec_words_to_bits(int prec_in_words): r""" Convert from pari real precision expressed in words to precision @@ -1303,7 +1301,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): 0.577215664901532860606512090082... """ pari_catch_sig_on() - return self.new_gen(mpeuler(pbw(precision))) + return self.new_gen(mpeuler(prec_bits_to_words(precision))) def pi(self, precision=0): """ @@ -1318,7 +1316,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): 3.1415926535897932384626433832... """ pari_catch_sig_on() - return self.new_gen(mppi(pbw(precision))) + return self.new_gen(mppi(prec_bits_to_words(precision))) def pollegendre(self, long n, v=-1): """ From 866f7fd13656bd10d4f33712695a93f228547be5 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 11 Dec 2013 13:06:17 +0000 Subject: [PATCH 118/206] remove methods sage.libs.pari.gen.*_unsafe --- src/sage/libs/pari/gen.pyx | 152 ------------------------------------- 1 file changed, 152 deletions(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index fb08b38228f..061f31ee010 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -210,120 +210,18 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gadd(self.g, (right).g)) - def _add_unsafe(gen self, gen right): - """ - VERY FAST addition of self and right on stack (and leave on stack) - without any type checking. - - Basically, this is often about 10 times faster than just typing - "self + right". The drawback is that (1) if self + right would give - an error in PARI, it will totally crash Sage, and (2) the memory - used by self + right is *never* returned - it gets allocated on - the PARI stack and will never be freed. - - EXAMPLES:: - - sage: pari(2)._add_unsafe(pari(3)) - 5 - """ - global mytop - cdef GEN z - cdef gen w - z = gadd(self.g, right.g) - w = PY_NEW(gen) - w.init(z,0) - mytop = avma - return w - cpdef ModuleElement _sub_(self, ModuleElement right): pari_catch_sig_on() return P.new_gen(gsub(self.g, ( right).g)) - def _sub_unsafe(gen self, gen right): - """ - VERY FAST subtraction of self and right on stack (and leave on - stack) without any type checking. - - Basically, this is often about 10 times faster than just typing - "self - right". The drawback is that (1) if self - right would give - an error in PARI, it will totally crash Sage, and (2) the memory - used by self - right is *never* returned - it gets allocated on - the PARI stack and will never be freed. - - EXAMPLES:: - - sage: pari(2)._sub_unsafe(pari(3)) - -1 - """ - global mytop - cdef GEN z - cdef gen w - z = gsub(self.g, right.g) - w = PY_NEW(gen) - w.init(z, 0) - mytop = avma - return w - cpdef RingElement _mul_(self, RingElement right): pari_catch_sig_on() return P.new_gen(gmul(self.g, (right).g)) - def _mul_unsafe(gen self, gen right): - """ - VERY FAST multiplication of self and right on stack (and leave on - stack) without any type checking. - - Basically, this is often about 10 times faster than just typing - "self \* right". The drawback is that (1) if self \* right would - give an error in PARI, it will totally crash Sage, and (2) the - memory used by self \* right is *never* returned - it gets - allocated on the PARI stack and will never be freed. - - EXAMPLES:: - - sage: pari(2)._mul_unsafe(pari(3)) - 6 - """ - global mytop - cdef GEN z - cdef gen w - z = gmul(self.g, right.g) - w = PY_NEW(gen) - w.init(z, 0) - mytop = avma - return w - cpdef RingElement _div_(self, RingElement right): pari_catch_sig_on() return P.new_gen(gdiv(self.g, (right).g)) - def _div_unsafe(gen self, gen right): - """ - VERY FAST division of self and right on stack (and leave on stack) - without any type checking. - - Basically, this is often about 10 times faster than just typing - "self / right". The drawback is that (1) if self / right would give - an error in PARI, it will totally crash Sage, and (2) the memory - used by self / right is *never* returned - it gets allocated on - the PARI stack and will never be freed. - - EXAMPLES:: - - sage: pari(2)._div_unsafe(pari(3)) - 2/3 - """ - global mytop - cdef GEN z - cdef gen w - z = gdiv(self.g, right.g) - w = PY_NEW(gen) - w.init(z, 0) - mytop = avma - return w - - ################################################################# - def _add_one(gen self): """ Return self + 1. @@ -1274,56 +1172,6 @@ cdef class gen(sage.structure.element.RingElement): Integer = sage.rings.integer.Integer return int(Integer(self)) - def int_unsafe(gen self): - """ - Returns int form of self, but raises an exception if int does not - fit into a long integer. - - This is about 5 times faster than the usual int conversion. - """ - return gtolong(self.g) - - def intvec_unsafe(self): - """ - Returns Python int list form of entries of self, but raises an - exception if int does not fit into a long integer. Here self must - be a vector. - - EXAMPLES:: - - sage: pari('[3,4,5]').type() - 't_VEC' - sage: pari('[3,4,5]').intvec_unsafe() - [3, 4, 5] - sage: type(pari('[3,4,5]').intvec_unsafe()[0]) - - - TESTS:: - - sage: pari(3).intvec_unsafe() - Traceback (most recent call last): - ... - TypeError: gen must be of PARI type t_VEC - sage: pari('[2^150,1]').intvec_unsafe() - Traceback (most recent call last): - ... - PariError: overflow in t_INT-->long assignment - """ - cdef int n, L - cdef object v - cdef GEN g - g = self.g - if typ(g) != t_VEC: - raise TypeError, "gen must be of PARI type t_VEC" - - pari_catch_sig_on() - L = glength(g) - v = [] - for n from 0 <= n < L: - v.append(gtolong( (g[n+1]))) - pari_catch_sig_off() - return v - def python_list_small(gen self): """ Return a Python list of the PARI gens. This object must be of type From 511f05be5b6829afdf11b020ab383e1354c9b143 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 11 Dec 2013 13:07:25 +0000 Subject: [PATCH 119/206] remove global variable mytop --- src/sage/libs/pari/pari_instance.pyx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx index 555d765579f..cd6b6e33452 100644 --- a/src/sage/libs/pari/pari_instance.pyx +++ b/src/sage/libs/pari/pari_instance.pyx @@ -174,8 +174,6 @@ from sage.libs.pari.handle_error cimport pari_error_string, \ # See the polgalois section of the PARI users manual. new_galois_format = 1 -cdef pari_sp mytop - # real precision in decimal digits: see documentation for # get_real_precision() and set_real_precision(). This variable is used # in gp to set the precision of input quantities (e.g. sqrt(2)), and for @@ -603,9 +601,9 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): pari_catch_sig_off()`` block. """ - global mytop, avma + global top, avma if _signals.sig_on_count <= 1: - avma = mytop + avma = top pari_catch_sig_off() cdef inline gen new_gen(self, GEN x): @@ -1554,7 +1552,7 @@ cdef int init_stack(size_t requested_size) except -1: function should not be called directly, use ``pari.allocatemem()`` instead. """ - global top, bot, avma, mytop + global top, bot, avma cdef size_t old_size = (top) - (bot) @@ -1620,7 +1618,6 @@ cdef int init_stack(size_t requested_size) except -1: raise SystemError("Unable to allocate PARI stack, all subsequent PARI computations will crash") top = bot + new_size - mytop = top avma = top if failed_size: From 3fa4f1dffa9b223a3537c1ca4b0188b162e1c2bc Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 11 Dec 2013 13:53:55 +0000 Subject: [PATCH 120/206] remove redundant methods from sage.libs.pari.gen --- src/sage/libs/pari/gen.pxd | 6 --- src/sage/libs/pari/gen.pyx | 91 ++++++++++++++------------------------ 2 files changed, 34 insertions(+), 63 deletions(-) diff --git a/src/sage/libs/pari/gen.pxd b/src/sage/libs/pari/gen.pxd index 371834f5e78..a9eadbcd8ab 100644 --- a/src/sage/libs/pari/gen.pxd +++ b/src/sage/libs/pari/gen.pxd @@ -9,11 +9,5 @@ cdef class gen(sage.structure.element.RingElement): cdef object _refers_to cdef pari_sp b cdef void init(self, GEN g, pari_sp b) - cdef GEN _gen(self) - cdef gen new_gen(self, GEN x) - cdef gen new_gen_noclear(self, GEN x) - cdef gen pari(self, object x) - cdef GEN _deepcopy_to_python_heap(self, GEN x, pari_sp* address) - cdef long get_var(self, v) cdef gen objtogen(object s) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 061f31ee010..b0c134dd135 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -137,9 +137,6 @@ cdef class gen(sage.structure.element.RingElement): T._init(self) return T - cdef GEN _gen(self): - return self.g - def list(self): """ Convert self to a list of PARI gens. @@ -302,7 +299,7 @@ cdef class gen(sage.structure.element.RingElement): # The hardcoded 1 below refers to the position in the internal # representation of a INTMOD or POLDMOD where the modulus is # stored. - return self.new_gen(gel(self.g, 1)) + return P.new_gen(gel(self.g, 1)) def nf_get_pol(self): """ @@ -430,7 +427,7 @@ cdef class gen(sage.structure.element.RingElement): 8 """ pari_catch_sig_on() - return self.new_gen(bnf_get_no(self.g)) + return P.new_gen(bnf_get_no(self.g)) def bnf_get_cyc(self): """ @@ -446,7 +443,7 @@ cdef class gen(sage.structure.element.RingElement): [4, 2] """ pari_catch_sig_on() - return self.new_gen(bnf_get_cyc(self.g)) + return P.new_gen(bnf_get_cyc(self.g)) def bnf_get_gen(self): """ @@ -464,7 +461,7 @@ cdef class gen(sage.structure.element.RingElement): [Fractional ideal (3, a + 2), Fractional ideal (2, a + 1)] """ pari_catch_sig_on() - return self.new_gen(bnf_get_gen(self.g)) + return P.new_gen(bnf_get_gen(self.g)) def bnf_get_reg(self): """ @@ -479,7 +476,7 @@ cdef class gen(sage.structure.element.RingElement): 2.66089858019037... """ pari_catch_sig_on() - return self.new_gen(bnf_get_reg(self.g)) + return P.new_gen(bnf_get_reg(self.g)) def pr_get_p(self): """ @@ -497,7 +494,7 @@ cdef class gen(sage.structure.element.RingElement): 5 """ pari_catch_sig_on() - return self.new_gen(pr_get_p(self.g)) + return P.new_gen(pr_get_p(self.g)) def pr_get_e(self): """ @@ -567,7 +564,7 @@ cdef class gen(sage.structure.element.RingElement): i - 2 """ pari_catch_sig_on() - return self.new_gen(pr_get_gen(self.g)) + return P.new_gen(pr_get_gen(self.g)) def bid_get_cyc(self): """ @@ -585,7 +582,7 @@ cdef class gen(sage.structure.element.RingElement): [4, 2] """ pari_catch_sig_on() - return self.new_gen(bid_get_cyc(self.g)) + return P.new_gen(bid_get_cyc(self.g)) def bid_get_gen(self): """ @@ -612,7 +609,7 @@ cdef class gen(sage.structure.element.RingElement): PariError: missing bid generators. Use idealstar(,,2) """ pari_catch_sig_on() - return self.new_gen(bid_get_gen(self.g)) + return P.new_gen(bid_get_gen(self.g)) def __getitem__(gen self, n): """ @@ -5340,7 +5337,7 @@ cdef class gen(sage.structure.element.RingElement): [17, [1, 0, 0, 0], 4] """ pari_catch_sig_on() - return self.new_gen(ellglobalred(self.g)) + return P.new_gen(ellglobalred(self.g)) def elladd(self, z0, z1): """ @@ -5456,7 +5453,7 @@ cdef class gen(sage.structure.element.RingElement): (pari).clear_stack() return v else: - return self.new_gen(anell(self.g, n)) + return P.new_gen(anell(self.g, n)) def ellanalyticrank(self, long precision = 0): r""" @@ -5471,7 +5468,7 @@ cdef class gen(sage.structure.element.RingElement): [2, 1.51863300057685] """ pari_catch_sig_on() - return self.new_gen(ellanalyticrank(self.g, 0, prec_bits_to_words(precision))) + return P.new_gen(ellanalyticrank(self.g, 0, prec_bits_to_words(precision))) def ellap(self, p): r""" @@ -5660,7 +5657,7 @@ cdef class gen(sage.structure.element.RingElement): """ pari_catch_sig_on() # the prec argument has no effect - return self.new_gen(elleta(self.g, prec)) + return P.new_gen(elleta(self.g, prec)) def ellheight(self, a, flag=2, precision=0): """ @@ -5997,8 +5994,8 @@ cdef class gen(sage.structure.element.RingElement): cdef pari_sp t pari_catch_sig_on() x = ellminimalmodel(self.g, &y) - change = self.new_gen_noclear(y) - model = self.new_gen(x) + change = P.new_gen_noclear(y) + model = P.new_gen(x) return model, change def ellorder(self, x): @@ -6280,7 +6277,7 @@ cdef class gen(sage.structure.element.RingElement): [12, [6, 2], [[-2, 8], [3, -2]]] """ pari_catch_sig_on() - return self.new_gen(elltors0(self.g, flag)) + return P.new_gen(elltors0(self.g, flag)) def ellzeta(self, z): """ @@ -6635,7 +6632,7 @@ cdef class gen(sage.structure.element.RingElement): [[65, 8; 0, 1], [65, 47; 0, 1], [65, 18; 0, 1], [65, 57; 0, 1]] """ pari_catch_sig_on() - return self.new_gen(ideallist0(self.g, bound, flag)) + return P.new_gen(ideallist0(self.g, bound, flag)) def ideallog(self, x, bid): """ @@ -6778,7 +6775,7 @@ cdef class gen(sage.structure.element.RingElement): EXAMPLES: """ pari_catch_sig_on() - return self.new_gen(modreverse(self.g)) + return P.new_gen(modreverse(self.g)) def nfbasis(self, long flag=0, fa=None): """ @@ -6963,7 +6960,7 @@ cdef class gen(sage.structure.element.RingElement): else: g = NULL pari_catch_sig_on() - return self.new_gen(nfdisc0(self.g, flag, g)) + return P.new_gen(nfdisc0(self.g, flag, g)) def nfeltdiveuc(self, x, y): """ @@ -7510,18 +7507,18 @@ cdef class gen(sage.structure.element.RingElement): f.poldist(var=x): Return the discriminant of this polynomial. """ pari_catch_sig_on() - return self.new_gen(poldisc0(self.g, self.get_var(var))) + return P.new_gen(poldisc0(self.g, P.get_var(var))) def poldiscreduced(self): pari_catch_sig_on() - return self.new_gen(reduceddiscsmith(self.g)) + return P.new_gen(reduceddiscsmith(self.g)) def polgalois(self): """ f.polgalois(): Galois group of the polynomial f """ pari_catch_sig_on() - return self.new_gen(polgalois(self.g, prec)) + return P.new_gen(polgalois(self.g, prec)) def nfgaloisconj(self, long flag=0, denom=None, long precision=0): r""" @@ -7798,7 +7795,7 @@ cdef class gen(sage.structure.element.RingElement): [1, 4, 7; 2, 5, 8; 3, 6, 9] """ pari_catch_sig_on() - return self.new_gen(gtrans(self.g)).Mat() + return P.new_gen(gtrans(self.g)).Mat() def matadjoint(self): """ @@ -7812,7 +7809,7 @@ cdef class gen(sage.structure.element.RingElement): [(i*e - h*f), (-i*b + h*c), (f*b - e*c); (-i*d + g*f), i*a - g*c, -f*a + d*c; (h*d - g*e), -h*a + g*b, e*a - d*b] """ pari_catch_sig_on() - return self.new_gen(adj(self.g)).Mat() + return P.new_gen(adj(self.g)).Mat() def qflll(self, long flag=0): """ @@ -7827,7 +7824,7 @@ cdef class gen(sage.structure.element.RingElement): polynomial coefficients. """ pari_catch_sig_on() - return self.new_gen(qflll0(self.g,flag)).Mat() + return P.new_gen(qflll0(self.g,flag)).Mat() def qflllgram(self, long flag=0): """ @@ -7840,7 +7837,7 @@ cdef class gen(sage.structure.element.RingElement): polynomials. """ pari_catch_sig_on() - return self.new_gen(qflllgram0(self.g,flag)).Mat() + return P.new_gen(qflllgram0(self.g,flag)).Mat() def lllgram(self): return self.qflllgram(0) @@ -7975,7 +7972,7 @@ cdef class gen(sage.structure.element.RingElement): [Mod(1, 2); Mod(0, 2); Mod(1, 2)] """ pari_catch_sig_on() - return self.new_gen(matker0(self.g, flag)) + return P.new_gen(matker0(self.g, flag)) def matkerint(self, long flag=0): """ @@ -8001,7 +7998,7 @@ cdef class gen(sage.structure.element.RingElement): [1; -2] """ pari_catch_sig_on() - return self.new_gen(matkerint0(self.g, flag)) + return P.new_gen(matkerint0(self.g, flag)) def matdet(self, long flag=0): """ @@ -8023,7 +8020,7 @@ cdef class gen(sage.structure.element.RingElement): -2 """ pari_catch_sig_on() - return self.new_gen(det0(self.g, flag)) + return P.new_gen(det0(self.g, flag)) def trace(self): """ @@ -8035,7 +8032,7 @@ cdef class gen(sage.structure.element.RingElement): 5 """ pari_catch_sig_on() - return self.new_gen(gtrace(self.g)) + return P.new_gen(gtrace(self.g)) def mathnf(self, flag=0): """ @@ -8061,7 +8058,7 @@ cdef class gen(sage.structure.element.RingElement): [6, 1; 3, 1; 0, 1] """ pari_catch_sig_on() - return self.new_gen(mathnf0(self.g, flag)) + return P.new_gen(mathnf0(self.g, flag)) def mathnfmod(self, d): """ @@ -8141,7 +8138,7 @@ cdef class gen(sage.structure.element.RingElement): [0, 3, 1] """ pari_catch_sig_on() - return self.new_gen(matsnf0(self.g, flag)) + return P.new_gen(matsnf0(self.g, flag)) def matfrobenius(self, flag=0): r""" @@ -8183,7 +8180,7 @@ cdef class gen(sage.structure.element.RingElement): - Martin Albrect (2006-04-02) """ pari_catch_sig_on() - return self.new_gen(matfrobenius(self.g, flag, 0)) + return P.new_gen(matfrobenius(self.g, flag, 0)) ########################################### @@ -8627,7 +8624,7 @@ cdef class gen(sage.structure.element.RingElement): """ pari_catch_sig_on() # the argument prec has no effect - return self.new_gen(elleisnum(self.g, k, flag, prec)) + return P.new_gen(elleisnum(self.g, k, flag, prec)) def ellwp(gen self, z='z', long n=20, long flag=0): """ @@ -8750,26 +8747,6 @@ cdef class gen(sage.structure.element.RingElement): return - ################################################## - # Technical functions that can be used by other - # classes that derive from gen. - ################################################## - cdef gen pari(self, object x): - return pari(x) - - cdef gen new_gen(self, GEN x): - return P.new_gen(x) - - cdef gen new_gen_noclear(self, GEN x): - return P.new_gen_noclear(x) - - cdef GEN _deepcopy_to_python_heap(self, GEN x, pari_sp* address): - return P.deepcopy_to_python_heap(x, address) - - cdef long get_var(self, v): - return P.get_var(v) - - def init_pari_stack(s=8000000): """ Deprecated, use ``pari.allocatemem()`` instead. From 5bb8f8d04511943d49b8ffa2911e7559b457be95 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 11 Dec 2013 14:07:01 +0000 Subject: [PATCH 121/206] remove commented-out method gen.python() --- src/sage/libs/pari/gen.pyx | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index b0c134dd135..57267d16bb0 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -1257,33 +1257,6 @@ cdef class gen(sage.structure.element.RingElement): sage = python -# This older version illustrates how irrelevant the real_precision -# global variable is in the pari library. The output of this -# function when self is a t_REAL is completely independent of the -# precision parameter. The new version above has no precision -# parameter at all: the caller can coerce into a lower-precision -# RealField if desired (or even in to a higher precision one, but -# that will just pad with 0 bits). - -# def python(self, precision=0, bits_prec=None): -# """ -# Return Python eval of self. -# """ -# if not bits_prec is None: -# precision = int(bits_prec * 3.4) + 1 -# import sage.libs.pari.gen_py -# cdef long orig_prec -# if precision: -# orig_prec = P.get_real_precision() -# P.set_real_precision(precision) -# print "self.precision() = ",self.precision() -# x = sage.libs.pari.gen_py.python(self) -# print "x.prec() = ",x.prec() -# P.set_real_precision(orig_prec) -# return x -# else: -# return sage.libs.pari.gen_py.python(self) - _sage_ = _eval_ = python def __long__(gen self): From 995b7031a21c6a7fa30e526fcf16756c91d3cdf8 Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Wed, 19 Dec 2012 14:58:48 +0100 Subject: [PATCH 122/206] Trac #13848: mq.SR: use deglex and polybori (if GF(2)) by default --- src/sage/crypto/mq/sr.py | 63 ++++------ .../polynomial/multi_polynomial_sequence.py | 114 +++++++++--------- 2 files changed, 81 insertions(+), 96 deletions(-) diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index 23021fca4a2..f87e7e20244 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -43,7 +43,7 @@ Polynomial Ring Base Ring : Finite Field in a of size 2^4 Size : 20 Variables - Block 0 : Ordering : degrevlex + Block 0 : Ordering : deglex Names : k100, k101, k102, k103, x100, x101, x102, x103, w100, w101, w102, w103, s000, s001, s002, s003, k000, k001, k002, k003 However, this can be prevented by passing in ``reverse_variables=False`` to the constructor. @@ -346,9 +346,9 @@ def SR(n=1, r=1, c=1, e=4, star=False, **kwargs): - ``gf2`` - generate polynomial systems over `\GF{2}` rather than over `\GF{2^e}` (default: ``False``) - ``polybori`` - use the ``BooleanPolynomialRing`` as polynomial - representation (default: ``False``, `\GF{2}` only) + representation (default: ``True``, `\GF{2}` only) - ``order`` - a string to specify the term ordering of the - variables + variables (default: ``deglex``) - ``postfix`` - a string which is appended after the variable name (default: '') - ``allow_zero_inversions`` - a boolean to control whether zero @@ -458,7 +458,7 @@ def __init__(self, n=1, r=1, c=1, e=4, star=False, **kwargs): self._base = self.base_ring() self._postfix = kwargs.get("postfix", "") - self._order = kwargs.get("order", "degrevlex") + self._order = kwargs.get("order", "deglex") self._aes_mode = kwargs.get("aes_mode", True) self._gf2 = kwargs.get("gf2", False) self._allow_zero_inversions = bool(kwargs.get("allow_zero_inversions", False)) @@ -469,7 +469,7 @@ def __init__(self, n=1, r=1, c=1, e=4, star=False, **kwargs): self._sub_byte_lookup = sub_byte_lookup if self._gf2: - self._polybori = kwargs.get("polybori",False) + self._polybori = kwargs.get("polybori", True) def new_generator(self, **kwds): r""" @@ -1873,20 +1873,16 @@ def key_schedule_polynomials(self, i): EXAMPLE:: - sage: sr = mq.SR(1, 1, 1, 4, gf2=True) + sage: sr = mq.SR(1, 1, 1, 4, gf2=True, polybori=False) The 0-th subkey is the user provided key, so only conjugacy - relations are added. - - :: + relations or field polynomials are added.:: sage: sr.key_schedule_polynomials(0) (k000^2 + k000, k001^2 + k001, k002^2 + k002, k003^2 + k003) The 1-th subkey is derived from the user provided key according to - the key schedule which is non-linear. - - :: + the key schedule which is non-linear.:: sage: sr.key_schedule_polynomials(1) (k100 + s000 + s002 + s003, @@ -1895,18 +1891,18 @@ def key_schedule_polynomials(self, i): k103 + s001 + s002 + s003 + 1, k100^2 + k100, k101^2 + k101, k102^2 + k102, k103^2 + k103, s000^2 + s000, s001^2 + s001, s002^2 + s002, s003^2 + s003, - s000*k000 + s003*k000 + s002*k001 + s001*k002 + s000*k003, - s000*k000 + s001*k000 + s000*k001 + s003*k001 + s002*k002 + s001*k003, - s001*k000 + s002*k000 + s000*k001 + s001*k001 + s000*k002 + s003*k002 + s002*k003, - s000*k000 + s002*k000 + s003*k000 + s000*k001 + s001*k001 + s002*k002 + s000*k003 + k000, - s001*k000 + s003*k000 + s001*k001 + s002*k001 + s000*k002 + s003*k002 + s001*k003 + k001, - s000*k000 + s002*k000 + s000*k001 + s002*k001 + s003*k001 + s000*k002 + s001*k002 + s002*k003 + k002, - s001*k000 + s002*k000 + s000*k001 + s003*k001 + s001*k002 + s003*k003 + k003, - s000*k000 + s001*k000 + s003*k000 + s001*k001 + s000*k002 + s002*k002 + s000*k003 + s000, - s002*k000 + s000*k001 + s001*k001 + s003*k001 + s001*k002 + s000*k003 + s002*k003 + s001, - s000*k000 + s001*k000 + s002*k000 + s002*k001 + s000*k002 + s001*k002 + s003*k002 + s001*k003 + s002, - s001*k000 + s000*k001 + s002*k001 + s000*k002 + s001*k003 + s003*k003 + s003, - s002*k000 + s001*k001 + s000*k002 + s003*k003 + 1) + s000*k000 + s000*k003 + s001*k002 + s002*k001 + s003*k000, + s000*k000 + s000*k001 + s001*k000 + s001*k003 + s002*k002 + s003*k001, + s000*k001 + s000*k002 + s001*k000 + s001*k001 + s002*k000 + s002*k003 + s003*k002, + s000*k000 + s000*k001 + s000*k003 + s001*k001 + s002*k000 + s002*k002 + s003*k000 + k000, + s000*k002 + s001*k000 + s001*k001 + s001*k003 + s002*k001 + s003*k000 + s003*k002 + k001, + s000*k000 + s000*k001 + s000*k002 + s001*k002 + s002*k000 + s002*k001 + s002*k003 + s003*k001 + k002, + s000*k001 + s001*k000 + s001*k002 + s002*k000 + s003*k001 + s003*k003 + k003, + s000*k000 + s000*k002 + s000*k003 + s001*k000 + s001*k001 + s002*k002 + s003*k000 + s000, + s000*k001 + s000*k003 + s001*k001 + s001*k002 + s002*k000 + s002*k003 + s003*k001 + s001, + s000*k000 + s000*k002 + s001*k000 + s001*k002 + s001*k003 + s002*k000 + s002*k001 + s003*k002 + s002, + s000*k001 + s000*k002 + s001*k000 + s001*k003 + s002*k001 + s003*k003 + s003, + s000*k002 + s001*k001 + s002*k000 + s003*k003 + 1) """ R = self.R r = self.r @@ -3137,18 +3133,9 @@ def inversion_polynomials(self, xi, wi, length): sage: xi = sr.vars('x', 1) sage: wi = sr.vars('w', 1) sage: sr.inversion_polynomials(xi, wi, len(xi))[:3] - [x100*w100 + x102*w100 + x103*w100 + x107*w100 + x101*w101 - + x102*w101 + x106*w101 + x100*w102 + x101*w102 + - x105*w102 + x100*w103 + x104*w103 + x103*w104 + x102*w105 - + x101*w106 + x100*w107, x101*w100 + x103*w100 + x104*w100 - + x100*w101 + x102*w101 + x103*w101 + x107*w101 + - x101*w102 + x102*w102 + x106*w102 + x100*w103 + x101*w103 - + x105*w103 + x100*w104 + x104*w104 + x103*w105 + - x102*w106 + x101*w107, x102*w100 + x104*w100 + x105*w100 + - x101*w101 + x103*w101 + x104*w101 + x100*w102 + x102*w102 - + x103*w102 + x107*w102 + x101*w103 + x102*w103 + - x106*w103 + x100*w104 + x101*w104 + x105*w104 + x100*w105 - + x104*w105 + x103*w106 + x102*w107] + [x100*w100 + x100*w102 + x100*w103 + x100*w107 + x101*w101 + x101*w102 + x101*w106 + x102*w100 + x102*w101 + x102*w105 + x103*w100 + x103*w104 + x104*w103 + x105*w102 + x106*w101 + x107*w100, + x100*w101 + x100*w103 + x100*w104 + x101*w100 + x101*w102 + x101*w103 + x101*w107 + x102*w101 + x102*w102 + x102*w106 + x103*w100 + x103*w101 + x103*w105 + x104*w100 + x104*w104 + x105*w103 + x106*w102 + x107*w101, + x100*w102 + x100*w104 + x100*w105 + x101*w101 + x101*w103 + x101*w104 + x102*w100 + x102*w102 + x102*w103 + x102*w107 + x103*w101 + x103*w102 + x103*w106 + x104*w100 + x104*w101 + x104*w105 + x105*w100 + x105*w104 + x106*w103 + x107*w102] """ if is_Matrix(xi): xi = xi.list() @@ -3174,7 +3161,7 @@ def field_polynomials(self, name, i, l=None): EXAMPLE:: - sage: sr = mq.SR(3, 1, 1, 8, gf2=True) + sage: sr = mq.SR(3, 1, 1, 8, gf2=True, polybori=False) sage: sr.field_polynomials('x', 2) [x200^2 + x200, x201^2 + x201, x202^2 + x202, x203^2 + x203, @@ -3366,7 +3353,7 @@ def test_consistency(max_n=2, **kwargs): try: F, s = sr.polynomial_system() F = F.subs(s) - consistent &= (F.groebner_basis('libsingular:slimgb')[0] != 1) + consistent &= (F.groebner_basis()[0] != 1) if not consistent: print sr, " is not consistent" zero_division = False diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 5e51329b410..e48eb138f95 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -43,69 +43,67 @@ w211 + k111 + x110 + x111 + x113 + 1, w212 + k112 + x110 + x111 + x112 + 1, w213 + k113 + x111 + x112 + x113, - w100*x100 + w100*x103 + w101*x102 + w102*x101 + w103*x100, - w100*x100 + w100*x101 + w101*x100 + w101*x103 + w102*x102 + w103*x101, - w100*x101 + w100*x102 + w101*x100 + w101*x101 + w102*x100 + w102*x103 + w103*x102, - w100*x100 + w100*x101 + w100*x103 + w101*x101 + w102*x100 + w102*x102 + w103*x100 + x100, - w100*x102 + w101*x100 + w101*x101 + w101*x103 + w102*x101 + w103*x100 + w103*x102 + x101, - w100*x100 + w100*x101 + w100*x102 + w101*x102 + w102*x100 + w102*x101 + w102*x103 + w103*x101 + x102, - w100*x101 + w101*x100 + w101*x102 + w102*x100 + w103*x101 + w103*x103 + x103, - w100*x100 + w100*x102 + w100*x103 + w101*x100 + w101*x101 + w102*x102 + w103*x100 + w100, - w100*x101 + w100*x103 + w101*x101 + w101*x102 + w102*x100 + w102*x103 + w103*x101 + w101, - w100*x100 + w100*x102 + w101*x100 + w101*x102 + w101*x103 + w102*x100 + w102*x101 + w103*x102 + w102, - w100*x101 + w100*x102 + w101*x100 + w101*x103 + w102*x101 + w103*x103 + w103, - w100*x102 + w101*x101 + w102*x100 + w103*x103 + 1, - w110*x110 + w110*x113 + w111*x112 + w112*x111 + w113*x110, - w110*x110 + w110*x111 + w111*x110 + w111*x113 + w112*x112 + w113*x111, - w110*x111 + w110*x112 + w111*x110 + w111*x111 + w112*x110 + w112*x113 + w113*x112, - w110*x110 + w110*x111 + w110*x113 + w111*x111 + w112*x110 + w112*x112 + w113*x110 + x110, - w110*x112 + w111*x110 + w111*x111 + w111*x113 + w112*x111 + w113*x110 + w113*x112 + x111, - w110*x110 + w110*x111 + w110*x112 + w111*x112 + w112*x110 + w112*x111 + w112*x113 + w113*x111 + x112, - w110*x111 + w111*x110 + w111*x112 + w112*x110 + w113*x111 + w113*x113 + x113, - w110*x110 + w110*x112 + w110*x113 + w111*x110 + w111*x111 + w112*x112 + w113*x110 + w110, - w110*x111 + w110*x113 + w111*x111 + w111*x112 + w112*x110 + w112*x113 + w113*x111 + w111, - w110*x110 + w110*x112 + w111*x110 + w111*x112 + w111*x113 + w112*x110 + w112*x111 + w113*x112 + w112, - w110*x111 + w110*x112 + w111*x110 + w111*x113 + w112*x111 + w113*x113 + w113, - w110*x112 + w111*x111 + w112*x110 + w113*x113 + 1) + x100*w100 + x100*w103 + x101*w102 + x102*w101 + x103*w100, + x100*w100 + x100*w101 + x101*w100 + x101*w103 + x102*w102 + x103*w101, + x100*w101 + x100*w102 + x101*w100 + x101*w101 + x102*w100 + x102*w103 + x103*w102, + x100*w100 + x100*w102 + x100*w103 + x101*w100 + x101*w101 + x102*w102 + x103*w100 + x100, + x100*w101 + x100*w103 + x101*w101 + x101*w102 + x102*w100 + x102*w103 + x103*w101 + x101, + x100*w100 + x100*w102 + x101*w100 + x101*w102 + x101*w103 + x102*w100 + x102*w101 + x103*w102 + x102, + x100*w101 + x100*w102 + x101*w100 + x101*w103 + x102*w101 + x103*w103 + x103, + x100*w100 + x100*w101 + x100*w103 + x101*w101 + x102*w100 + x102*w102 + x103*w100 + w100, + x100*w102 + x101*w100 + x101*w101 + x101*w103 + x102*w101 + x103*w100 + x103*w102 + w101, + x100*w100 + x100*w101 + x100*w102 + x101*w102 + x102*w100 + x102*w101 + x102*w103 + x103*w101 + w102, + x100*w101 + x101*w100 + x101*w102 + x102*w100 + x103*w101 + x103*w103 + w103, + x100*w102 + x101*w101 + x102*w100 + x103*w103 + 1, + x110*w110 + x110*w113 + x111*w112 + x112*w111 + x113*w110, + x110*w110 + x110*w111 + x111*w110 + x111*w113 + x112*w112 + x113*w111, + x110*w111 + x110*w112 + x111*w110 + x111*w111 + x112*w110 + x112*w113 + x113*w112, + x110*w110 + x110*w112 + x110*w113 + x111*w110 + x111*w111 + x112*w112 + x113*w110 + x110, + x110*w111 + x110*w113 + x111*w111 + x111*w112 + x112*w110 + x112*w113 + x113*w111 + x111, + x110*w110 + x110*w112 + x111*w110 + x111*w112 + x111*w113 + x112*w110 + x112*w111 + x113*w112 + x112, + x110*w111 + x110*w112 + x111*w110 + x111*w113 + x112*w111 + x113*w113 + x113, + x110*w110 + x110*w111 + x110*w113 + x111*w111 + x112*w110 + x112*w112 + x113*w110 + w110, + x110*w112 + x111*w110 + x111*w111 + x111*w113 + x112*w111 + x113*w110 + x113*w112 + w111, + x110*w110 + x110*w111 + x110*w112 + x111*w112 + x112*w110 + x112*w111 + x112*w113 + x113*w111 + w112, + x110*w111 + x111*w110 + x111*w112 + x112*w110 + x113*w111 + x113*w113 + w113, + x110*w112 + x111*w111 + x112*w110 + x113*w113 + 1) We separate the system in independent subsystems:: sage: C = Sequence(r2).connected_components(); C [[w213 + k113 + x111 + x112 + x113, - w212 + k112 + x110 + x111 + x112 + 1, - w211 + k111 + x110 + x111 + x113 + 1, - w210 + k110 + x110 + x112 + x113, - w110*x112 + w111*x111 + w112*x110 + w113*x113 + 1, - w110*x112 + w111*x110 + w111*x111 + w111*x113 + w112*x111 + w113*x110 + w113*x112 + x111, - w110*x111 + w111*x110 + w111*x112 + w112*x110 + w113*x111 + w113*x113 + x113, - w110*x111 + w110*x113 + w111*x111 + w111*x112 + w112*x110 + w112*x113 + w113*x111 + w111, - w110*x111 + w110*x112 + w111*x110 + w111*x113 + w112*x111 + w113*x113 + w113, - w110*x111 + w110*x112 + w111*x110 + w111*x111 + w112*x110 + w112*x113 + w113*x112, - w110*x110 + w110*x113 + w111*x112 + w112*x111 + w113*x110, - w110*x110 + w110*x112 + w111*x110 + w111*x112 + w111*x113 + w112*x110 + w112*x111 + w113*x112 + w112, - w110*x110 + w110*x112 + w110*x113 + w111*x110 + w111*x111 + w112*x112 + w113*x110 + w110, - w110*x110 + w110*x111 + w111*x110 + w111*x113 + w112*x112 + w113*x111, - w110*x110 + w110*x111 + w110*x113 + w111*x111 + w112*x110 + w112*x112 + w113*x110 + x110, - w110*x110 + w110*x111 + w110*x112 + w111*x112 + w112*x110 + w112*x111 + w112*x113 + w113*x111 + x112], - [w203 + k103 + x101 + x102 + x103, - w202 + k102 + x100 + x101 + x102 + 1, - w201 + k101 + x100 + x101 + x103 + 1, - w200 + k100 + x100 + x102 + x103, - w100*x102 + w101*x101 + w102*x100 + w103*x103 + 1, - w100*x102 + w101*x100 + w101*x101 + w101*x103 + w102*x101 + w103*x100 + w103*x102 + x101, - w100*x101 + w101*x100 + w101*x102 + w102*x100 + w103*x101 + w103*x103 + x103, - w100*x101 + w100*x103 + w101*x101 + w101*x102 + w102*x100 + w102*x103 + w103*x101 + w101, - w100*x101 + w100*x102 + w101*x100 + w101*x103 + w102*x101 + w103*x103 + w103, - w100*x101 + w100*x102 + w101*x100 + w101*x101 + w102*x100 + w102*x103 + w103*x102, - w100*x100 + w100*x103 + w101*x102 + w102*x101 + w103*x100, - w100*x100 + w100*x102 + w101*x100 + w101*x102 + w101*x103 + w102*x100 + w102*x101 + w103*x102 + w102, - w100*x100 + w100*x102 + w100*x103 + w101*x100 + w101*x101 + w102*x102 + w103*x100 + w100, - w100*x100 + w100*x101 + w101*x100 + w101*x103 + w102*x102 + w103*x101, - w100*x100 + w100*x101 + w100*x103 + w101*x101 + w102*x100 + w102*x102 + w103*x100 + x100, - w100*x100 + w100*x101 + w100*x102 + w101*x102 + w102*x100 + w102*x101 + w102*x103 + w103*x101 + x102]] - + w212 + k112 + x110 + x111 + x112 + 1, + w211 + k111 + x110 + x111 + x113 + 1, + w210 + k110 + x110 + x112 + x113, + x110*w112 + x111*w111 + x112*w110 + x113*w113 + 1, + x110*w112 + x111*w110 + x111*w111 + x111*w113 + x112*w111 + x113*w110 + x113*w112 + w111, + x110*w111 + x111*w110 + x111*w112 + x112*w110 + x113*w111 + x113*w113 + w113, + x110*w111 + x110*w113 + x111*w111 + x111*w112 + x112*w110 + x112*w113 + x113*w111 + x111, + x110*w111 + x110*w112 + x111*w110 + x111*w113 + x112*w111 + x113*w113 + x113, + x110*w111 + x110*w112 + x111*w110 + x111*w111 + x112*w110 + x112*w113 + x113*w112, + x110*w110 + x110*w113 + x111*w112 + x112*w111 + x113*w110, + x110*w110 + x110*w112 + x111*w110 + x111*w112 + x111*w113 + x112*w110 + x112*w111 + x113*w112 + x112, + x110*w110 + x110*w112 + x110*w113 + x111*w110 + x111*w111 + x112*w112 + x113*w110 + x110, + x110*w110 + x110*w111 + x111*w110 + x111*w113 + x112*w112 + x113*w111, + x110*w110 + x110*w111 + x110*w113 + x111*w111 + x112*w110 + x112*w112 + x113*w110 + w110, + x110*w110 + x110*w111 + x110*w112 + x111*w112 + x112*w110 + x112*w111 + x112*w113 + x113*w111 + w112], + [w203 + k103 + x101 + x102 + x103, + w202 + k102 + x100 + x101 + x102 + 1, + w201 + k101 + x100 + x101 + x103 + 1, + w200 + k100 + x100 + x102 + x103, + x100*w102 + x101*w101 + x102*w100 + x103*w103 + 1, + x100*w102 + x101*w100 + x101*w101 + x101*w103 + x102*w101 + x103*w100 + x103*w102 + w101, + x100*w101 + x101*w100 + x101*w102 + x102*w100 + x103*w101 + x103*w103 + w103, + x100*w101 + x100*w103 + x101*w101 + x101*w102 + x102*w100 + x102*w103 + x103*w101 + x101, + x100*w101 + x100*w102 + x101*w100 + x101*w103 + x102*w101 + x103*w103 + x103, x100*w101 + x100*w102 + x101*w100 + x101*w101 + x102*w100 + x102*w103 + x103*w102, + x100*w100 + x100*w103 + x101*w102 + x102*w101 + x103*w100, + x100*w100 + x100*w102 + x101*w100 + x101*w102 + x101*w103 + x102*w100 + x102*w101 + x103*w102 + x102, + x100*w100 + x100*w102 + x100*w103 + x101*w100 + x101*w101 + x102*w102 + x103*w100 + x100, + x100*w100 + x100*w101 + x101*w100 + x101*w103 + x102*w102 + x103*w101, + x100*w100 + x100*w101 + x100*w103 + x101*w101 + x102*w100 + x102*w102 + x103*w100 + w100, + x100*w100 + x100*w101 + x100*w102 + x101*w102 + x102*w100 + x102*w101 + x102*w103 + x103*w101 + w102]] sage: C[0].groebner_basis() - Polynomial Sequence with 26 Polynomials in 16 Variables + Polynomial Sequence with 30 Polynomials in 16 Variables and compute the coefficient matrix:: @@ -787,7 +785,7 @@ def _repr_(self): sage: sr = mq.SR(allow_zero_inversions=True,gf2=True) sage: F,s = sr.polynomial_system(); F - Polynomial Sequence with 56 Polynomials in 20 Variables + Polynomial Sequence with 36 Polynomials in 20 Variables """ if len(self) < 20: From e2a0f58edd456bc2dcc9c78dea59ef82b55591d2 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Mon, 11 Nov 2013 17:24:47 +0100 Subject: [PATCH 123/206] Trac #15078: finite state machines, automata, transducers --- src/doc/en/reference/combinat/index.rst | 1 + src/sage/combinat/all.py | 4 + src/sage/combinat/finite_state_machine.py | 5173 +++++++++++++++++++++ 3 files changed, 5178 insertions(+) create mode 100644 src/sage/combinat/finite_state_machine.py diff --git a/src/doc/en/reference/combinat/index.rst b/src/doc/en/reference/combinat/index.rst index 2983d63d0dc..134ebbf77ee 100644 --- a/src/doc/en/reference/combinat/index.rst +++ b/src/doc/en/reference/combinat/index.rst @@ -81,6 +81,7 @@ Combinatorics designs species developer + sage/combinat/finite_state_machine words sage/combinat/dict_addition diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 2d3c2e6e281..600ca693108 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -159,5 +159,9 @@ # Gelfand-Tsetlin patterns from gelfand_tsetlin_patterns import GelfandTsetlinPattern, GelfandTsetlinPatterns +# Finite State Machines (Automaton, Transducer) +sage.misc.lazy_import.lazy_import('sage.combinat.finite_state_machine', + ['Automaton', 'Transducer', + 'FiniteStateMachine']) # Binary Recurrence Sequences from binary_recurrence_sequences import BinaryRecurrenceSequence diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py new file mode 100644 index 00000000000..efdaaba6e47 --- /dev/null +++ b/src/sage/combinat/finite_state_machine.py @@ -0,0 +1,5173 @@ +# -*- coding: utf-8 -*- +""" +Finite State Machines, Automata, Transducers + +This module adds support for finite state machines, automata and +transducers. See class :class:`FiniteStateMachine` and the examples +below for details creating one. + +Examples +======== + + +A simple finite state machine +----------------------------- + +We can easily create a finite state machine by + +:: + + sage: fsm = FiniteStateMachine() + sage: fsm + Finite state machine with 0 states + +By default this is the empty finite state machine, so not very +interesting. Let's create some states and transitions:: + + sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition + sage: day = FSMState('day') + sage: night = FSMState('night') + sage: sunrise = FSMTransition(night, day) + sage: sunset = FSMTransition(day, night) + +And now let's add those states and transitions to our finite state machine:: + + sage: fsm.add_transition(sunrise) + Transition from 'night' to 'day': -|- + sage: fsm.add_transition(sunset) + Transition from 'day' to 'night': -|- + +Note that the states are added automatically, since they are present +in the transitions. We could add the states manually by + +:: + + sage: fsm.add_state(day) + 'day' + sage: fsm.add_state(night) + 'night' + +Anyhow, we got the following finite state machine:: + + sage: fsm + Finite state machine with 2 states + +We can also visualize it as a graph by + +:: + + sage: fsm.graph() + Digraph on 2 vertices + +Alternatively, we could have created the finite state machine above +simply by + +:: + + sage: FiniteStateMachine([('night', 'day'), ('day', 'night')]) + Finite state machine with 2 states + +or by + +:: + + sage: fsm = FiniteStateMachine() + sage: day = fsm.add_state('day') + sage: night = fsm.add_state('night') + sage: sunrise = fsm.add_transition(night, day) + sage: sunset = fsm.add_transition(day, night) + sage: fsm + Finite state machine with 2 states + +A simple Automaton (recognizing NAFs) +--------------------------------------- + +We want to build an automaton which recognizes non-adjacent forms +(NAFs), i.e., sequences which have no adjacent non-zeros. +We use `0`, `1`, and `-1` as digits:: + + sage: NAF = Automaton( + ....: {'A': [('A', 0), ('B', 1), ('B', -1)], 'B': [('A', 0)]}) + sage: NAF.state('A').is_initial = True + sage: NAF.state('A').is_final = True + sage: NAF.state('B').is_final = True + sage: NAF + Automaton with 2 states + +Of course, we could have specified the initial and final states +directly in the definition of ``NAF`` by ``initial_states=['A']`` and +``final_states=['A', 'B']``. + +So let's test the automaton with some input:: + + sage: NAF([0])[0] + True + sage: NAF([0, 1])[0] + True + sage: NAF([1, -1])[0] + False + sage: NAF([0, -1, 0, 1])[0] + True + sage: NAF([0, -1, -1, -1, 0])[0] + False + sage: NAF([-1, 0, 0, 1, 1])[0] + False + +Alternatively, we could call that by + +:: + + sage: NAF.process([-1, 0, 0, 1, 1])[0] + False + +A simple transducer (binary inverter) +------------------------------------- + +Let's build a simple transducer, which rewrites a binary word by +iverting each bit:: + + sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, + ....: initial_states=['A'], final_states=['A']) + +We can look at the states and transitions:: + + sage: inverter.states() + ['A'] + sage: for t in inverter.transitions(): + ....: print t + Transition from 'A' to 'A': 0|1 + Transition from 'A' to 'A': 1|0 + +Now we apply a word to it and see what the transducer does:: + + sage: inverter([0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1]) + (True, 'A', [1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0]) + +``True`` means, that we landed in a final state, that state is labeled +``'A'``, and we also got an output. + + +A transducer which performs division by `3` in binary +----------------------------------------------------- + +Now we build a transducer, which divides a binary number by 3. +The labels of the states are the remainder of the division. +The transition function is + +:: + + sage: def f(state_from, read): + ....: if state_from + read <= 1: + ....: state_to = 2*state_from + read + ....: write = 0 + ....: else: + ....: state_to = 2*state_from + read - 3 + ....: write = 1 + ....: return (state_to, write) + +We get the transducer with + +:: + + sage: D = Transducer(f, initial_states=[0], final_states=[0], + ....: input_alphabet=[0, 1]) + +Now we want to divide 13 by 3:: + + sage: D([1, 1, 0, 1]) + (False, 1, [0, 1, 0, 0]) + +So we have 13 : 3 = 4 and the reminder is 1. ``False`` means 13 is not +divisible by 3. + + +Using the hook-functions +------------------------ + +Let's use the previous example "divison by `3`" to demonstrate the +optional state and transition parameters ``hook``. + +First, we define, what those functions should do. In our case, this is +just saying in which state we are and which transition we take + +:: + + sage: def state_hook(state, process): + ....: print "We are now in State %s." % (state.label(),) + sage: from sage.combinat.finite_state_machine import FSMWordSymbol + sage: def transition_hook(transition, process): + ....: print ("Currently we go from %s to %s, " + ....: "reading %s and writing %s." % ( + ....: transition.from_state, transition.to_state, + ....: FSMWordSymbol(transition.word_in), + ....: FSMWordSymbol(transition.word_out))) + +Now, let's add these hook-functions to the existing transducer:: + + sage: for s in D.iter_states(): + ....: s.hook = state_hook + sage: for t in D.iter_transitions(): + ....: t.hook = transition_hook + +Rerunning the process again now gives the following output:: + + sage: D.process([1, 1, 0, 1]) + We are now in State 0. + Currently we go from 0 to 1, reading 1 and writing 0. + We are now in State 1. + Currently we go from 1 to 0, reading 1 and writing 1. + We are now in State 0. + Currently we go from 0 to 0, reading 0 and writing 0. + We are now in State 0. + Currently we go from 0 to 1, reading 1 and writing 0. + We are now in State 1. + (False, 1, [0, 1, 0, 0]) + +The example above just explains the basic idea of using +hook-functions. In the following, we will use those hooks more seriously. + + +Detecting sequences with same number of `0` and `1` +--------------------------------------------------- + +Suppose we have a binary input and want to accept all sequences with +the same number of `0` and `1`. This cannot be done with a finite +automaton. Anyhow, we can make usage of the hook functions to extend +our finite automaton by a counter:: + + sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition + sage: C = Automaton() + sage: def update_counter(state, process): + ....: l = process.read_letter() + ....: process.fsm.counter += 1 if l == 1 else -1 + ....: if process.fsm.counter > 0: + ....: next_state = 'positive' + ....: elif process.fsm.counter < 0: + ....: next_state = 'negative' + ....: else: + ....: next_state = 'zero' + ....: return FSMTransition(state, process.fsm.state(next_state), + ....: l, process.fsm.counter) + sage: C.add_state(FSMState('zero', hook=update_counter, + ....: is_initial=True, is_final=True)) + 'zero' + sage: C.add_state(FSMState('positive', hook=update_counter)) + 'positive' + sage: C.add_state(FSMState('negative', hook=update_counter)) + 'negative' + +Now, let's input some sequence:: + + sage: C.counter = 0; C([1, 1, 1, 1, 0, 0]) + (False, 'positive', [1, 2, 3, 4, 3, 2]) + +The result is False, since there are four `1` but only two `0`. We +land in the state ``positive`` and we can also see the values of the +counter in each step. + +Let's try some other examples:: + + sage: C.counter = 0; C([1, 1, 0, 0]) + (True, 'zero', [1, 2, 1, 0]) + sage: C.counter = 0; C([0, 1, 0, 0]) + (False, 'negative', [-1, 0, -1, -2]) + + +AUTHORS: + +- Daniel Krenn (2012-03-27): initial version +- Clemens Heuberger (2012-04-05): initial version +- Sara Kropf (2012-04-17): initial version +- Clemens Heuberger (2013-08-21): release candidate for Sage patch +- Daniel Krenn (2013-08-21): release candidate for Sage patch +- Sara Kropf (2013-08-21): release candidate for Sage patch +- Clemens Heuberger (2013-09-02): documentation improved +- Daniel Krenn (2013-09-13): comments from trac worked in +- Clemens Heuberger (2013-11-03): output (labels) of determinisation, + product, composition, etc. changed (for consistency), + representation of state changed, documentation improved +- Daniel Krenn (2013-11-04): whitespaces in documentation corrected +- Clemens Heuberger (2013-11-04): full_group_by added +- Daniel Krenn (2013-11-04): next release candidate for Sage patch +- Sara Kropf (2013-11-08): fix for adjacency matrix +- Clemens Heuberger (2013-11-11): fix for prepone_output +- Daniel Krenn (2013-11-11): comments from trac 15078 included: + docstring of FiniteStateMachine rewritten, Automaton and Transducer + inherited from FiniteStateMachine +- Daniel Krenn (2013-11-25): documentation improved according to + comments from trac 15078 + +ACKNOWLEDGEMENT: + +- Daniel Krenn, Clemens Heuberger and Sara Kropf are supported by the + Austrian Science Fund (FWF): P 24644-N26. + +""" + +#***************************************************************************** +# Copyright (C) 2012, 2013 Daniel Krenn +# 2012, 2013 Clemens Heuberger +# 2012, 2013 Sara Kropf +# +# 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/ +#***************************************************************************** + +from sage.structure.sage_object import SageObject +from sage.graphs.digraph import DiGraph +from sage.matrix.constructor import matrix +from sage.rings.integer_ring import ZZ +from sage.calculus.var import var +from sage.misc.latex import latex +from sage.functions.trig import cos, sin, atan2 +from sage.symbolic.constants import pi + +from copy import copy +from copy import deepcopy + +import itertools +from collections import defaultdict + + +def full_group_by(l, key=lambda x: x): + """ + Group iterable ``l`` by values of ``key``. + + INPUT: + + - iterable ``l`` + - key function ``key`` + + OUTPUT: + + A list of pairs ``(k, elements)`` such that ``key(e)=k`` for all + ``e`` in ``elements``. + + This is similar to ``itertools.groupby`` except that lists are + returned instead of iterables and no prior sorting is required. + + We do not require + + - that the keys are sortable (in contrast to the + approach via ``sorted`` and ``itertools.groupby``) and + - that the keys are hashable (in contrast to the + implementation proposed in ``_). + + However, it is required + + - that distinct keys have distinct ``str``-representations. + + The implementation is inspired by + ``_, but non-hashable keys are + allowed. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import full_group_by + sage: t = [2/x, 1/x, 2/x] + sage: r = full_group_by([0,1,2], key=lambda i:t[i]) + sage: sorted(r, key=lambda p:p[1]) + [(2/x, [0, 2]), (1/x, [1])] + sage: from itertools import groupby + sage: for k, elements in groupby(sorted([0,1,2], + ....: key=lambda i:t[i]), + ....: key=lambda i:t[i]): + ....: print k, list(elements) + 2/x [0] + 1/x [1] + 2/x [2] + + Note that the behavior is different from ``itertools.groupby`` + because neither `1/x<2/x` nor `2/x<1/x` does hold. + + Here, the result ``r`` has been sorted in order to guarantee a + consistent order for the doctest suite. + """ + elements = defaultdict(list) + original_keys = {} + for item in l: + k = key(item) + s = str(k) + if s in original_keys: + if original_keys[s]!=k: + raise ValueError("Two distinct elements with representation " + "%s " % s) + else: + original_keys[s]=k + elements[s].append(item) + return [(original_keys[s], values ) for (s, values) in elements.items()] + +#***************************************************************************** + +FSMEmptyWordSymbol = '-' + +def FSMLetterSymbol(letter): + """ + Returns a string associated to the input letter. + + INPUT: + + - ``letter`` -- the input letter or ``None`` (representing the + empty word). + + OUTPUT: + + If ``letter`` is ``None`` the symbol for the empty word + ``FSMEmptyWordSymbol`` is returned, otherwise the string + associated to the letter. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMLetterSymbol + sage: FSMLetterSymbol(0) + '0' + sage: FSMLetterSymbol(None) + '-' + """ + return FSMEmptyWordSymbol if letter is None else repr(letter) + + +def FSMWordSymbol(word): + """ + Returns a string of ``word``. It may returns the symbol of the + empty word ``FSMEmptyWordSymbol``. + + INPUT: + + - ``word`` -- the input word. + + OUTPUT: + + A string of ``word``. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMWordSymbol + sage: FSMWordSymbol([0, 1, 1]) + '0,1,1' + """ + if not isinstance(word, list): + return FSMLetterSymbol(word) + if len(word) == 0: + return FSMEmptyWordSymbol + s = '' + for letter in word: + s += (',' if len(s) > 0 else '') + FSMLetterSymbol(letter) + return s + + +#***************************************************************************** + + +def is_FSMState(S): + """ + Tests whether or not ``S`` inherits from :class:`FSMState`. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import is_FSMState, FSMState + sage: is_FSMState(FSMState('A')) + True + """ + return isinstance(S, FSMState) + + +class FSMState(SageObject): + """ + Class for a state of a finite state machine. + + INPUT: + + - ``label`` -- the label of the state. + + - ``word_out`` -- (default: ``None``) a word that is written when + the state is reached. + + - ``is_initial`` -- (default: ``False``) + + - ``is_final`` -- (default: ``False``) + + - ``hook`` -- (default: ``None``) A function which is called when + the state is reached during processing input. + + OUTPUT: + + Returns a state of a finite state machine. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('state 1', word_out=0, is_initial=True) + sage: A + 'state 1' + sage: A.label() + 'state 1' + sage: B = FSMState('state 2') + sage: A == B + False + + """ + def __init__(self, label, word_out=None, + is_initial=False, is_final=False, + hook=None): + """ + See :class:`FSMState` for more information. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: FSMState('final', is_final=True) + 'final' + """ + if label is None or label == "": + raise ValueError, "You have to specify a label for the state." + self._label_ = label + + if isinstance(word_out, list): + self.word_out = word_out + elif word_out is not None: + self.word_out = [word_out] + else: + self.word_out = [] + + self.is_initial = is_initial + self.is_final = is_final + if hook is not None: + if hasattr(hook, '__call__'): + self.hook = hook + else: + raise TypeError, 'Wrong argument for hook.' + + + def label(self): + """ + Returns the label of the state. + + INPUT: + + Nothing. + + OUTPUT: + + The label of the state. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('state') + sage: A.label() + 'state' + """ + return self._label_ + + + def __copy__(self): + """ + Returns a (shallow) copy of the state. + + INPUT: + + Nothing. + + OUTPUT: + + A new state. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A') + sage: copy(A) + 'A' + """ + new = FSMState(self.label(), self.word_out, + self.is_initial, self.is_final) + if hasattr(self, 'hook'): + new.hook = self.hook + return new + + + copy = __copy__ + + + def __deepcopy__(self, memo): + """ + Returns a deep copy of the state. + + INPUT: + + - ``memo`` -- a dictionary storing already processed elements. + + OUTPUT: + + A new state. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A') + sage: deepcopy(A) + 'A' + """ + try: + label = self._deepcopy_relabel_ + except AttributeError: + label = deepcopy(self.label(), memo) + new = FSMState(label, deepcopy(self.word_out, memo), + self.is_initial, self.is_final) + if hasattr(self, 'hook'): + new.hook = deepcopy(self.hook, memo) + return new + + + def deepcopy(self, memo=None): + """ + Returns a deep copy of the state. + + INPUT: + + - ``memo`` -- (default: ``None``) a dictionary storing already + processed elements. + + OUTPUT: + + A new state. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A') + sage: deepcopy(A) + 'A' + """ + return deepcopy(self, memo) + + + def relabeled(self, label, memo=None): + """ + Returns a deep copy of the state with a new label. + + INPUT: + + - ``label`` -- the label of new state. + + - ``memo`` -- (default: ``None``) a dictionary storing already + processed elements. + + OUTPUT: + + A new state. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A') + sage: A.relabeled('B') + 'B' + + """ + self._deepcopy_relabel_ = label + new = deepcopy(self, memo) + del self._deepcopy_relabel_ + return new + + + def __hash__(self): + """ + Returns a hash value for the object. + + INPUT: + + Nothing. + + OUTPUT: + + The hash of this state. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A') + sage: hash(A) #random + -269909568 + """ + return hash(self.label()) + + + def _repr_(self): + """ + Returns the string "label". + + INPUT: + + Nothing. + + OUTPUT: + + A string. + + TESTS: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: FSMState('A')._repr_() + "'A'" + """ + return repr(self.label()) + + + def __eq__(left, right): + """ + Returns True if two states are the same, i.e., if they have + the same labels. + + Note that the hooks and whether the states are initial or + final are not checked. + + INPUT: + + - ``left`` -- a state. + + - ``right`` -- a state. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A') + sage: B = FSMState('A', is_initial=True) + sage: A == B + True + """ + if not is_FSMState(right): + return False + return left.label() == right.label() + + + def __ne__(left, right): + """ + Tests for inequality, complement of __eq__. + + INPUT: + + - ``left`` -- a state. + + - ``right`` -- a state. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A', is_initial=True) + sage: B = FSMState('A', is_final=True) + sage: A != B + False + """ + return (not (left == right)) + + + def __nonzero__(self): + """ + Returns True. + + INPUT: + + Nothing. + + OUTPUT: + + True or False. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: FSMState('A').__nonzero__() + True + """ + return True # A state cannot be zero (see __init__) + + +#***************************************************************************** + + +def is_FSMTransition(T): + """ + Tests whether or not ``T`` inherits from :class:`FSMTransition`. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import is_FSMTransition, FSMTransition + sage: is_FSMTransition(FSMTransition('A', 'B')) + True + """ + return isinstance(T, FSMTransition) + + +class FSMTransition(SageObject): + """ + Class for a transition of a finite state machine. + + INPUT: + + - ``from_state`` -- state from which transition starts. + + - ``to_state`` -- state in which transition ends. + + - ``word_in`` -- the input word of the transitions (when the + finite state machine is used as automaton) + + - ``word_out`` -- the output word of the transitions (when the + finite state machine is used as transducer) + + OUTPUT: + + A transition of a finite state machine. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition + sage: A = FSMState('A') + sage: B = FSMState('B') + sage: S = FSMTransition(A, B, 0, 1) + sage: T = FSMTransition('A', 'B', 0, 1) + sage: T == S + True + sage: U = FSMTransition('A', 'B', 0) + sage: U == T + False + + """ + def __init__(self, from_state, to_state, + word_in=None, word_out=None, + hook=None): + """ + See :class:`FSMTransition` for more information. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: FSMTransition('A', 'B', 0, 1) + Transition from 'A' to 'B': 0|1 + """ + if is_FSMState(from_state): + self.from_state = from_state + else: + self.from_state = FSMState(from_state) + if is_FSMState(to_state): + self.to_state = to_state + else: + self.to_state = FSMState(to_state) + + if isinstance(word_in, list): + self.word_in = word_in + elif word_in is not None: + self.word_in = [word_in] + else: + self.word_in = [] + + if isinstance(word_out, list): + self.word_out = word_out + elif word_out is not None: + self.word_out = [word_out] + else: + self.word_out = [] + + if hook is not None: + if hasattr(hook, '__call__'): + self.hook = hook + else: + raise TypeError, 'Wrong argument for hook.' + + + def __copy__(self): + """ + Returns a (shallow) copy of the transition. + + INPUT: + + Nothing. + + OUTPUT: + + A new transition. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: t = FSMTransition('A', 'B', 0) + sage: copy(t) + Transition from 'A' to 'B': 0|- + """ + new = FSMTransition(self.from_state, self.to_state, + self.word_in, self.word_out) + if hasattr(self, 'hook'): + new.hook = self.hook + return new + + + copy = __copy__ + + def __deepcopy__(self, memo): + """ + Returns a deep copy of the transition. + + INPUT: + + - ``memo`` -- a dictionary storing already processed elements. + + OUTPUT: + + A new transition. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: t = FSMTransition('A', 'B', 0) + sage: deepcopy(t) + Transition from 'A' to 'B': 0|- + """ + new = FSMTransition(deepcopy(self.from_state, memo), + deepcopy(self.to_state, memo), + deepcopy(self.word_in, memo), + deepcopy(self.word_out, memo)) + if hasattr(self, 'hook'): + new.hook = deepcopy(self.hook, memo) + return new + + + def deepcopy(self, memo=None): + """ + Returns a deep copy of the transition. + + INPUT: + + - ``memo`` -- (default: ``None``) a dictionary storing already + processed elements. + + OUTPUT: + + A new transition. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: t = FSMTransition('A', 'B', 0) + sage: deepcopy(t) + Transition from 'A' to 'B': 0|- + """ + return deepcopy(self, memo) + + + def __hash__(self): + """ + Since transitions are mutable, they should not be hashable, so + we return a type error. + + INPUT: + + Nothing. + + OUTPUT: + + The hash of this transition. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: hash(FSMTransition('A', 'B')) + Traceback (most recent call last): + ... + TypeError: Transitions are mutable, and thus not hashable. + + """ + raise TypeError, "Transitions are mutable, and thus not hashable." + + + def _repr_(self): + """ + Represents a transitions as from state to state and input, output. + + INPUT: + + Nothing. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: FSMTransition('A', 'B', 0, 0)._repr_() + "Transition from 'A' to 'B': 0|0" + + """ + return "Transition from %s to %s: %s" % (repr(self.from_state), + repr(self.to_state), + self._in_out_label_()) + + + def _in_out_label_(self): + """ + Returns the input and output of a transition as + "word_in|word_out". + + INPUT: + + Nothing. + + OUTPUT: + + A string of the input and output labels. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: FSMTransition('A', 'B', 0, 1)._in_out_label_() + '0|1' + """ + return "%s|%s" % (FSMWordSymbol(self.word_in), + FSMWordSymbol(self.word_out)) + + + def __eq__(left, right): + """ + Returns True if the two transitions are the same, i.e., if the + both go from the same states to the same states and read and + write the same words. + + Note that the hooks are not checked. + + INPUT: + + - ``left`` -- a transition. + + - ``right`` -- a transition. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition + sage: A = FSMState('A', is_initial=True) + sage: t1 = FSMTransition('A', 'B', 0, 1) + sage: t2 = FSMTransition(A, 'B', 0, 1) + sage: t1 == t2 + True + """ + if not is_FSMTransition(right): + raise TypeError, 'Only instances of FSMTransition ' \ + 'can be compared.' + return left.from_state == right.from_state \ + and left.to_state == right.to_state \ + and left.word_in == right.word_in \ + and left.word_out == right.word_out + + + def __ne__(left, right): + """ + + INPUT: + + - ``left`` -- a transition. + + - ``right`` -- a transition. + + OUTPUT: + + True or False. + Tests for inequality, complement of __eq__. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition + sage: A = FSMState('A', is_initial=True) + sage: t1 = FSMTransition('A', 'B', 0, 1) + sage: t2 = FSMTransition(A, 'B', 0, 1) + sage: t1 != t2 + False + """ + return (not (left == right)) + + + def __nonzero__(self): + """ + Returns True. + + INPUT: + + Nothing. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: FSMTransition('A', 'B', 0).__nonzero__() + True + """ + return True # A transition cannot be zero (see __init__) + + +#***************************************************************************** + + +def is_FiniteStateMachine(FSM): + """ + Tests whether or not ``FSM`` inherits from :class:`FiniteStateMachine`. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import is_FiniteStateMachine + sage: is_FiniteStateMachine(FiniteStateMachine()) + True + sage: is_FiniteStateMachine(Automaton()) + True + sage: is_FiniteStateMachine(Transducer()) + True + """ + return isinstance(FSM, FiniteStateMachine) + + +class FiniteStateMachine(SageObject): + """ + Class for a finite state machine. + + A finite state machine is a finite set of states connected by + transitions. + + INPUT: + + - ``data`` -- can be any of the following: + + #. a dictionary of dictionaries (of transitions), + + #. a dictionary of lists (of states or transitions), + + #. a list (of transitions), + + #. a function (transition function), + + #. an other instance of a finite state machine. + + - ``initial_states`` and ``final_states`` -- the initial and + final states of this machine + + - ``input_alphabet`` and ``output_alphabet`` -- the input and + output alphabets of this machine + + - ``determine_alphabets`` -- If True, then the function + ``determine_alphabets()`` is called after ``data`` was read and + processed, if ``False``, then not. If it is ``None``, then it is + decided during the construction of the finite state machine + whether ``determine_alphabets()`` should be called. + + - ``store_states_dict`` -- If ``True``, then additionally the states + are stored in an interal dictionary for speed up. + + OUTPUT: + + A finite state machine. + + The object creation of :class:`Automaton` and :class:`Transducer` + is the same as the one described here (i.e. just replace the word + ``FiniteStateMachine`` by ``Automaton`` or ``Transducer``). + + Each transition of an automaton has an input label. Automata can, + for example, be determinised (see + :meth:`Automaton.determinisation`) and minimized (see + :meth:`Automaton.minimization`). Each transition of a transducer + has an input and an output label. Transducers can, for example, be + simplified (see :meth:`Transducer.simplification`). + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition + + See documentation for more examples. + + We illustrate the different input formats: + + #. The input-data can be a dictionary of dictionaries, where + + - the keys of the outer dictionary are state-labels (from-states of + transitions), + - the keys of the inner dictionaries are state-labels (to-states of + transitions), + - the values of the inner dictionaries specify the transition + more precisely. + + The easiest is to use a tuple consisting of an input and an + output word:: + + sage: FiniteStateMachine({'a':{'b':(0, 1), 'c':(1, 1)}}) + Finite state machine with 3 states + + Instead of the tuple anything iterable (e.g. a list) can be + used as well. + + If you want to use the arguments of ``FSMTransition`` + directly, you can use a dictionary:: + + sage: FiniteStateMachine({'a':{'b':{'word_in':0, 'word_out':1}, + ....: 'c':{'word_in':1, 'word_out':1}}}) + Finite state machine with 3 states + + In the case you already have instances of ``FSMTransition``, it is + possible to use them directly:: + + sage: FiniteStateMachine({'a':{'b':FSMTransition('a', 'b', 0, 1), + ....: 'c':FSMTransition('a', 'c', 1, 1)}}) + Finite state machine with 3 states + + #. The input-data can be a dictionary of lists, where the keys + are states or label of states. + + The list-elements can be states:: + + sage: a = FSMState('a') + sage: b = FSMState('b') + sage: c = FSMState('c') + sage: FiniteStateMachine({a:[b, c]}) + Finite state machine with 3 states + + Or the list-elements can simply be labels of states:: + + sage: FiniteStateMachine({'a':['b', 'c']}) + Finite state machine with 3 states + + The list-elements can also be transitions:: + + sage: FiniteStateMachine({'a':[FSMTransition('a', 'b', 0, 1), + ....: FSMTransition('a', 'c', 1, 1)]}) + Finite state machine with 3 states + + Or they can be tuples of a label, an input word and an output + word specifying a transition:: + + sage: FiniteStateMachine({'a':[('b', 0, 1), ('c', 1, 1)]}) + Finite state machine with 3 states + + #. The input-data can be a list, where its elements specify + transitions:: + + sage: FiniteStateMachine([FSMTransition('a', 'b', 0, 1), + ....: FSMTransition('a', 'c', 1, 1)]) + Finite state machine with 3 states + + It is possible to skip ``FSMTransition`` in the example above:: + + sage: FiniteStateMachine([('a', 'b', 0, 1), ('a', 'c', 1, 1)]) + Finite state machine with 3 states + + The parameters of the transition are given in tuples. Anyhow, + anything iterable (e.g. a list) is possible. + + You can also name the parameters of the transition. For this + purpose you take a dictionary:: + + sage: FiniteStateMachine([{'from_state':'a', 'to_state':'b', + ....: 'word_in':0, 'word_out':1}, + ....: {'from_state':'a', 'to_state':'c', + ....: 'word_in':1, 'word_out':1}]) + Finite state machine with 3 states + + Other arguments, which :class:`FSMTransition` accepts, can be + added, too. + + #. The input-data can also be function acting as transition + function: + + This function has two input arguments: + + #. a label of a state (from which the transition starts), + + #. a letter of the (input-)alphabet (as input-label of the transition). + + It returns a tuple with the following entries: + + #. a label of a state (to which state the transition goes), + + #. a letter of or a word over the (output-)alphabet (as + output-label of the transition). + + It may also output a list of such tuples if several + transitions from the from-state and the input letter exist + (this means that the finite state machine is + non-deterministic). + + If the transition does not exist, the function should raise a + ``LookupError`` or return an empty list. + + When constructing a finite state machine in this way, some + inital states and an input alphabet have to be specified. + + :: + + sage: def f(state_from, read): + ....: if int(state_from) + read <= 2: + ....: state_to = 2*int(state_from)+read + ....: write = 0 + ....: else: + ....: state_to = 2*int(state_from) + read - 5 + ....: write = 1 + ....: return (str(state_to), write) + sage: F = FiniteStateMachine(f, input_alphabet=[0, 1], + ....: initial_states=['0'], + ....: final_states=['0']) + sage: F([1, 0, 1]) + (True, '0', [0, 0, 1]) + + #. The input-data can be an other instance of a finite state machine:: + + sage: FiniteStateMachine(FiniteStateMachine([])) + Traceback (most recent call last): + ... + NotImplementedError + + + TESTS:: + + sage: a = FSMState('S_a', 'a') + sage: b = FSMState('S_b', 'b') + sage: c = FSMState('S_c', 'c') + sage: d = FSMState('S_d', 'd') + sage: FiniteStateMachine({a:[b, c], b:[b, c, d], + ....: c:[a, b], d:[a, c]}) + Finite state machine with 4 states + + We have several constructions which lead to the same finite + state machine:: + + sage: A = FSMState('A') + sage: B = FSMState('B') + sage: C = FSMState('C') + sage: FSM1 = FiniteStateMachine( + ....: {A:{B:{'word_in':0, 'word_out':1}, + ....: C:{'word_in':1, 'word_out':1}}}) + sage: FSM2 = FiniteStateMachine({A:{B:(0, 1), C:(1, 1)}}) + sage: FSM3 = FiniteStateMachine( + ....: {A:{B:FSMTransition(A, B, 0, 1), + ....: C:FSMTransition(A, C, 1, 1)}}) + sage: FSM4 = FiniteStateMachine({A:[(B, 0, 1), (C, 1, 1)]}) + sage: FSM5 = FiniteStateMachine( + ....: {A:[FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1)]}) + sage: FSM6 = FiniteStateMachine( + ....: [{'from_state':A, 'to_state':B, 'word_in':0, 'word_out':1}, + ....: {'from_state':A, 'to_state':C, 'word_in':1, 'word_out':1}]) + sage: FSM7 = FiniteStateMachine([(A, B, 0, 1), (A, C, 1, 1)]) + sage: FSM8 = FiniteStateMachine( + ....: [FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1)]) + + sage: FSM1 == FSM2 == FSM3 == FSM4 == FSM5 == FSM6 == FSM7 == FSM8 + True + + It is possible to skip ``FSMTransition`` in the example above. + + Some more tests for different input-data:: + + sage: FiniteStateMachine({'a':{'a':[0, 0], 'b':[1, 1]}, + ....: 'b':{'b':[1, 0]}}) + Finite state machine with 2 states + + sage: a = FSMState('S_a', 'a') + sage: b = FSMState('S_b', 'b') + sage: c = FSMState('S_c', 'c') + sage: d = FSMState('S_d', 'd') + sage: t1 = FSMTransition(a, b) + sage: t2 = FSMTransition(b, c) + sage: t3 = FSMTransition(b, d) + sage: t4 = FSMTransition(c, d) + sage: FiniteStateMachine([t1, t2, t3, t4]) + Finite state machine with 4 states + """ + + #************************************************************************* + # init + #************************************************************************* + + + def __init__(self, + data=None, + initial_states=None, final_states=None, + input_alphabet=None, output_alphabet=None, + determine_alphabets=None, + store_states_dict=True): + """ + See :class:`FiniteStateMachine` for more information. + + TEST:: + + sage: FiniteStateMachine() + Finite state machine with 0 states + """ + self._states_ = [] # List of states in the finite state + # machine. Each state stores a list of + # outgoing transitions. + if store_states_dict: + self._states_dict_ = {} + + if initial_states is not None: + if not hasattr(initial_states, '__iter__'): + raise TypeError, 'Initial states must be iterable ' \ + '(e.g. a list of states).' + for s in initial_states: + state = self.add_state(s) + state.is_initial = True + + if final_states is not None: + if not hasattr(final_states, '__iter__'): + raise TypeError, 'Final states must be iterable ' \ + '(e.g. a list of states).' + for s in final_states: + state = self.add_state(s) + state.is_final = True + + self.input_alphabet = input_alphabet + self.output_alphabet = output_alphabet + + if data is None: + pass + elif is_FiniteStateMachine(data): + raise NotImplementedError + elif hasattr(data, 'iteritems'): + # data is a dict (or something similar), + # format: key = from_state, value = iterator of transitions + for (sf, iter_transitions) in data.iteritems(): + self.add_state(sf) + if hasattr(iter_transitions, 'iteritems'): + for (st, transition) in iter_transitions.iteritems(): + self.add_state(st) + if is_FSMTransition(transition): + self.add_transition(transition) + elif hasattr(transition, 'iteritems'): + self.add_transition(sf, st, **transition) + elif hasattr(transition, '__iter__'): + self.add_transition(sf, st, *transition) + else: + self.add_transition(sf, st, transition) + elif hasattr(iter_transitions, '__iter__'): + for transition in iter_transitions: + if hasattr(transition, '__iter__'): + L = [sf] + L.extend(transition) + elif is_FSMTransition(transition): + L = transition + else: + L = [sf, transition] + self.add_transition(L) + else: + raise TypeError, 'Wrong input data for transition.' + if determine_alphabets is None and input_alphabet is None \ + and output_alphabet is None: + determine_alphabets = True + elif hasattr(data, '__iter__'): + # data is a something that is iterable, + # items are transitions + for transition in data: + if is_FSMTransition(transition): + self.add_transition(transition) + elif hasattr(transition, 'iteritems'): + self.add_transition(transition) + elif hasattr(transition, '__iter__'): + self.add_transition(transition) + else: + raise TypeError, 'Wrong input data for transition.' + if determine_alphabets is None and input_alphabet is None \ + and output_alphabet is None: + determine_alphabets = True + elif hasattr(data, '__call__'): + self.add_from_transition_function(data) + else: + raise TypeError, 'Cannot decide what to do with data.' + + if determine_alphabets: + self.determine_alphabets() + + + #************************************************************************* + # copy and hash + #************************************************************************* + + + def __copy__(self): + """ + Returns a (shallow) copy of the finite state machine. + + INPUT: + + Nothing. + + OUTPUT: + + A new finite state machine. + + TESTS:: + + sage: copy(FiniteStateMachine()) + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + + copy = __copy__ + + def empty_copy(self, memo=None): + """ + Returns an empty deep copy of the finite state machine, i.e., + input_alphabet, output_alphabet are preserved, but states and + transitions are not. + + INPUT: + + - ``memo`` -- a dictionary storing already processed elements. + + OUTPUT: + + A new finite state machine. + + EXAMPLES:: + + sage: F = FiniteStateMachine([('A', 'A', 0, 2), ('A', 'A', 1, 3)], + ....: input_alphabet=[0,1], + ....: output_alphabet=[2,3]) + sage: FE = F.empty_copy(); FE + Finite state machine with 0 states + sage: FE.input_alphabet + [0, 1] + sage: FE.output_alphabet + [2, 3] + """ + new = self.__class__() + new.input_alphabet = deepcopy(self.input_alphabet, memo) + new.output_alphabet = deepcopy(self.output_alphabet, memo) + return new + + def __deepcopy__(self, memo): + """ + Returns a deep copy of the finite state machine. + + INPUT: + + - ``memo`` -- a dictionary storing already processed elements. + + OUTPUT: + + A new finite state machine. + + EXAMPLES:: + + sage: F = FiniteStateMachine([('A', 'A', 0, 1), ('A', 'A', 1, 0)]) + sage: deepcopy(F) + Finite state machine with 1 states + """ + relabel = hasattr(self, '_deepcopy_relabel_') + new = self.empty_copy(memo=memo) + relabel_iter = itertools.count(0) + for state in self.iter_states(): + if relabel: + state._deepcopy_relabel_ = relabel_iter.next() + s = deepcopy(state, memo) + if relabel: + del state._deepcopy_relabel_ + new.add_state(s) + for transition in self.iter_transitions(): + new.add_transition(deepcopy(transition, memo)) + return new + + + def deepcopy(self, memo=None): + """ + Returns a deep copy of the finite state machine. + + INPUT: + + - ``memo`` -- (default: ``None``) a dictionary storing already + processed elements. + + OUTPUT: + + A new finite state machine. + + EXAMPLES:: + + sage: F = FiniteStateMachine([('A', 'A', 0, 1), ('A', 'A', 1, 0)]) + sage: deepcopy(F) + Finite state machine with 1 states + """ + return deepcopy(self, memo) + + + def relabeled(self, memo=None): + """ + Returns a deep copy of the finite state machine, but the + states are relabeled by integers starting with 0. + + INPUT: + + - ``memo`` -- (default: ``None``) a dictionary storing already + processed elements. + + OUTPUT: + + A new finite state machine. + + EXAMPLES:: + + sage: FSM1 = FiniteStateMachine([('A', 'B'), ('B', 'C'), ('C', 'A')]) + sage: FSM1.states() + ['A', 'B', 'C'] + sage: FSM2 = FSM1.relabeled() + sage: FSM2.states() + [0, 1, 2] + """ + self._deepcopy_relabel_ = True + new = deepcopy(self, memo) + del self._deepcopy_relabel_ + return new + + + def __hash__(self): + """ + Since finite state machines are mutable, they should not be + hashable, so we return a type error. + + INPUT: + + Nothing. + + OUTPUT: + + The hash of this finite state machine. + + EXAMPLES:: + + sage: hash(FiniteStateMachine()) + Traceback (most recent call last): + ... + TypeError: Finite state machines are mutable, and thus not hashable. + """ + if getattr(self, "_immutable", False): + return hash((tuple(self.states()), tuple(self.transitions()))) + raise TypeError, "Finite state machines are mutable, " \ + "and thus not hashable." + + + #************************************************************************* + # operators + #************************************************************************* + + + def __add__(self, other): + """ + Returns the disjoint union of the finite state machines self and other. + + INPUT: + + - ``other`` -- a finite state machine. + + OUTPUT: + + A new finite state machine. + + TESTS:: + + sage: FiniteStateMachine() + FiniteStateMachine([('A', 'B')]) + Traceback (most recent call last): + ... + NotImplementedError + """ + if is_FiniteStateMachine(other): + return self.disjoint_union(other) + + + def __iadd__(self, other): + """ + TESTS:: + + sage: F = FiniteStateMachine() + sage: F += FiniteStateMachine() + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + + def __mul__(self, other): + """ + TESTS:: + + sage: FiniteStateMachine() * FiniteStateMachine([('A', 'B')]) + Traceback (most recent call last): + ... + NotImplementedError + """ + if is_FiniteStateMachine(other): + return self.intersection(other) + + + def __imul__(self, other): + """ + TESTS:: + + sage: F = FiniteStateMachine() + sage: F *= FiniteStateMachine() + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + + def __call__(self, *args, **kwargs): + """ + Calls either method :meth:`.composition` or :meth:`.process`. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A', is_initial=True, is_final=True) + sage: binary_inverter = Transducer({A:[(A, 0, 1), (A, 1, 0)]}) + sage: binary_inverter([0, 1, 0, 0, 1, 1]) + (True, 'A', [1, 0, 1, 1, 0, 0]) + + :: + + sage: F = Transducer([('A', 'B', 1, 0), ('B', 'B', 1, 1), + ....: ('B', 'B', 0, 0)], + ....: initial_states=['A'], final_states=['B']) + sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0), + ....: (2, 2, 0, 1), (2, 1, 1, 1)], + ....: initial_states=[1], final_states=[1]) + sage: H = G(F) + sage: H.states() + [('A', 1), ('B', 1), ('B', 2)] + """ + if len(args) == 0: + raise TypeError, "Called with too few arguments." + if is_FiniteStateMachine(args[0]): + return self.composition(*args, **kwargs) + if hasattr(args[0], '__iter__'): + return self.process(*args, **kwargs) + raise TypeError, "Do not know what to do with that arguments." + + + #************************************************************************* + # tests + #************************************************************************* + + + def __nonzero__(self): + """ + Returns True if the finite state machine consists of at least + one state. + + INPUT: + + Nothing. + + OUTPUT: + + True or False. + + TESTS:: + + sage: FiniteStateMachine().__nonzero__() + False + """ + return len(self._states_) > 0 + + + def __eq__(left, right): + """ + Returns True if the two finite state machines are equal, i.e., + if they have the same states and the same transitions. + + INPUT: + + - ``left`` -- a finite state machine. + + - ``right`` -- a finite state machine. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: F = FiniteStateMachine([('A', 'B', 1)]) + sage: F == FiniteStateMachine() + False + """ + if not is_FiniteStateMachine(right): + raise TypeError, 'Only instances of FiniteStateMachine ' \ + 'can be compared.' + if len(left._states_) != len(right._states_): + return False + for state in left.iter_states(): + if state not in right._states_: + return False + left_transitions = state.transitions + right_transitions = right.state(state).transitions + if len(left_transitions) != len(right_transitions): + return False + for t in left_transitions: + if t not in right_transitions: + return False + return True + + + def __ne__(left, right): + """ + Tests for inequality, complement of :meth:`.__eq__`. + + INPUT: + + - ``left`` -- a finite state machine. + + - ``right`` -- a finite state machine. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: E = FiniteStateMachine([('A', 'B', 0)]) + sage: F = Automaton([('A', 'B', 0)]) + sage: G = Transducer([('A', 'B', 0, 1)]) + sage: E == F + True + sage: E == G + False + """ + return (not (left == right)) + + + def __contains__(self, item): + """ + Returns true, if the finite state machine contains the + state or transition item. Note that only the labels of the + states and the input and output words are tested. + + INPUT: + + - ``item`` -- a state or a transition. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition + sage: F = FiniteStateMachine([('A', 'B', 0), ('B', 'A', 1)]) + sage: FSMState('A', is_initial=True) in F + True + sage: 'A' in F + False + sage: FSMTransition('A', 'B', 0) in F + True + """ + if is_FSMState(item): + return self.has_state(item) + if is_FSMTransition(item): + return self.has_transition(item) + return False + + + #************************************************************************* + # representations / LaTeX + #************************************************************************* + + + def _repr_(self): + """ + Represents the finite state machine as "Finite state machine + with n states" where n is the number of states. + + INPUT: + + Nothing. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: FiniteStateMachine()._repr_() + 'Finite state machine with 0 states' + """ + return "Finite state machine with %s states" % len(self._states_) + + + def _latex_(self): + r""" + Returns a LaTeX code for the graph of the finite state machine. + + INPUT: + + Nothing. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: F = FiniteStateMachine([('A', 'B', 1, 2)]) + sage: F._latex_() + '\\begin{tikzpicture}[auto]\n\\node[state] (v0) at (3.000000,0.000000) {\\text{\\texttt{A}}}\n;\\node[state] (v1) at (-3.000000,0.000000) {\\text{\\texttt{B}}}\n;\\path[->] (v0) edge node {$ $} (v1);\n\\end{tikzpicture}' + """ + result = "\\begin{tikzpicture}[auto]\n" + j = 0; + for vertex in self.states(): + if not hasattr(vertex, "coordinates"): + vertex.coordinates = (3*cos(2*pi*j/len(self.states())), + 3*sin(2*pi*j/len(self.states()))) + options = "" + if vertex in self.final_states(): + options += ",accepting" + if hasattr(vertex, "format_label"): + label = vertex.format_label() + elif hasattr(self, "format_state_label"): + label = self.format_state_label(vertex) + else: + label = latex(vertex.label()) + result += "\\node[state%s] (v%d) at (%f,%f) {%s}\n;" % ( + options, j, vertex.coordinates[0], + vertex.coordinates[1], label) + vertex._number_ = j + j += 1 + adjacent = {} + for source in self.states(): + for target in self.states(): + transitions = filter(lambda transition: \ + transition.to_state == target, + source.transitions) + adjacent[source, target] = transitions + + for ((source, target), transitions) in adjacent.iteritems(): + if len(transitions) > 0: + labels = [] + for transition in transitions: + if hasattr(transition, "format_label"): + labels.append(transition.format_label()) + continue + elif hasattr(self, "format_transition_label"): + format_transition_label = self.format_transition_label + else: + format_transition_label = latex + labels.append(self._latex_transition_label_( + transition, format_transition_label)) + label = ", ".join(labels) + if source != target: + if len(adjacent[target, source]) > 0: + angle = atan2( + target.coordinates[1] - source.coordinates[1], + target.coordinates[0]-source.coordinates[0])*180/pi + angle_source = ".%.2f" % ((angle+5).n(),) + angle_target = ".%.2f" % ((angle+175).n(),) + else: + angle_source = "" + angle_target = "" + result += "\\path[->] (v%d%s) edge node {$%s$} (v%d%s);\n" % ( + source._number_, angle_source, label, + target._number_, angle_target) + else: + result += "\\path[->] (v%d) edge[loop above] node {$%s$} ();\n" % ( + source._number_, label) + + result += "\\end{tikzpicture}" + return result + + + def _latex_transition_label_(self, transition, format_function=latex): + r""" + Returns the proper transition label. + + INPUT: + + - ``transition`` - a transition + + - ``format_function'' - a function formatting the labels + + OUTPUT: + + A string. + + TESTS:: + + sage: F = FiniteStateMachine([('A', 'B', 0, 1)]) + sage: t = F.transitions()[0] + sage: F._latex_transition_label_(t) + ' ' + """ + return ' ' + + + #************************************************************************* + # other + #************************************************************************* + + + def _matrix_(self, R=None): + """ + Returns the adjacency matrix of the finite state machine. + See :meth:`.adjacency_matrix` for more information. + + EXAMPLES:: + + sage: B = FiniteStateMachine({0: {0: (0, 0), 'a': (1, 0)}, + ....: 'a': {2: (0, 0), 3: (1, 0)}, + ....: 2:{0:(1, 1), 4:(0, 0)}, + ....: 3:{'a':(0, 1), 2:(1, 1)}, + ....: 4:{4:(1, 1), 3:(0, 1)}}, + ....: initial_states=[0]) + sage: B._matrix_() + [1 1 0 0 0] + [0 0 1 1 0] + [x 0 0 0 1] + [0 x x 0 0] + [0 0 0 x x] + """ + return self.adjacency_matrix() + + + def adjacency_matrix(self, input=None, + entry=(lambda transition:var('x')**transition.word_out[0])): + """ + Returns the adjacency matrix of the underlying graph. + + INPUT: + + - ``input`` -- Only transitions with input label ``input`` are + respected. + + - ``entry`` -- The function ``entry`` takes a transition and + the return value is written in the matrix as the entry + ``(transition.from_state, transition.to_state)``. + + OUTPUT: + + A matrix. + + If any label of a state is not an integer, the finite state + machine is relabeled at the beginning. If there are more than + one transitions between two states, then the different return + values of ``entry`` are added up. + + The default value of entry takes the variable ``x`` to the + power of the output word of the transition. + + EXAMPLES:: + + sage: B = FiniteStateMachine({0:{0:(0, 0), 'a':(1, 0)}, + ....: 'a':{2:(0, 0), 3:(1, 0)}, + ....: 2:{0:(1, 1), 4:(0, 0)}, + ....: 3:{'a':(0, 1), 2:(1, 1)}, + ....: 4:{4:(1, 1), 3:(0, 1)}}, + ....: initial_states=[0]) + sage: B.adjacency_matrix() + [1 1 0 0 0] + [0 0 1 1 0] + [x 0 0 0 1] + [0 x x 0 0] + [0 0 0 x x] + sage: B.adjacency_matrix(entry=(lambda transition: 1)) + [1 1 0 0 0] + [0 0 1 1 0] + [1 0 0 0 1] + [0 1 1 0 0] + [0 0 0 1 1] + sage: B.adjacency_matrix(1, entry=(lambda transition: + ....: exp(I*transition.word_out[0]*var('t')))) + [ 0 1 0 0 0] + [ 0 0 0 1 0] + [e^(I*t) 0 0 0 0] + [ 0 0 e^(I*t) 0 0] + [ 0 0 0 0 e^(I*t)] + + """ + relabeledFSM = self + l = len(relabeledFSM.states()) + for state in self.states(): + if state.label() not in ZZ or state.label() >= l or state.label() < 0: + relabeledFSM = self.relabeled() + break + dictionary = {} + for transition in relabeledFSM.iter_transitions(): + if input is None or transition.word_in == [input]: + if (transition.from_state.label(), transition.to_state.label()) in dictionary: + dictionary[(transition.from_state.label(), transition.to_state.label())] += entry(transition) + else: + dictionary[(transition.from_state.label(), transition.to_state.label())] = entry(transition) + return matrix(len(relabeledFSM.states()), dictionary) + + + def determine_alphabets(self, reset=True): + """ + Determines the input and output alphabet according to the + transitions in self. + + INPUT: + + - ``reset`` -- If reset is True, then the existing input + alphabet is erased, otherwise new letters are appended to + the existing alphabet. + + OUTPUT: + + Nothing. + + After this operation the input alphabet and the output + alphabet of self are a list of letters. + + EXAMPLES:: + + sage: T = Transducer([(1, 1, 1, 0), (1, 2, 2, 1), + ....: (2, 2, 1, 1), (2, 2, 0, 0)], + ....: determine_alphabets=False) + sage: (T.input_alphabet, T.output_alphabet) + (None, None) + sage: T.determine_alphabets() + sage: (T.input_alphabet, T.output_alphabet) + ([0, 1, 2], [0, 1]) + """ + if reset: + ain = set() + aout = set() + else: + ain = set(self.input_alphabet) + aout = set(self.output_alphabet) + + for t in self.iter_transitions(): + for letter in t.word_in: + ain.add(letter) + for letter in t.word_out: + aout.add(letter) + self.input_alphabet = list(ain) + self.output_alphabet = list(aout) + + + #************************************************************************* + # get states and transitions + #************************************************************************* + + + def states(self): + """ + Returns the states of the finite state machine. + + INPUT: + + Nothing. + + OUTPUT: + + The states of the finite state machine as list. + + EXAMPLES:: + + sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)]) + sage: FSM.states() + ['1', '2'] + + """ + return copy(self._states_) + + + def iter_states(self): + """ + Returns an iterator of the states. + + INPUT: + + Nothing. + + OUTPUT: + + An iterator of the states of the finite state machine. + + EXAMPLES:: + + sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)]) + sage: [s.label() for s in FSM.iter_states()] + ['1', '2'] + """ + return iter(self._states_) + + + def transitions(self, from_state=None): + """ + Returns a list of all transitions. + + INPUT: + + - ``from_state`` -- (default: ``None``) If ``from_state`` is + given, then a list of transitions starting there is given. + + OUTPUT: + + A list of all transitions. + + EXAMPLES:: + + sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)]) + sage: FSM.transitions() + [Transition from '1' to '2': 1|-, + Transition from '2' to '2': 0|-] + """ + return list(self.iter_transitions(from_state)) + + + def iter_transitions(self, from_state=None): + """ + Returns an iterator of all transitions. + + INPUT: + + - ``from_state`` -- (default: ``None``) If ``from_state`` is + given, then a list of transitions starting there is given. + + OUTPUT: + + An iterator of all transitions. + + EXAMPLES:: + + sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)]) + sage: [(t.from_state.label(), t.to_state.label()) + ....: for t in FSM.iter_transitions('1')] + [('1', '2')] + sage: [(t.from_state.label(), t.to_state.label()) + ....: for t in FSM.iter_transitions('2')] + [('2', '2')] + sage: [(t.from_state.label(), t.to_state.label()) + ....: for t in FSM.iter_transitions()] + [('1', '2'), ('2', '2')] + """ + if from_state is None: + return self._iter_transitions_all_() + else: + return iter(self.state(from_state).transitions) + + + def _iter_transitions_all_(self): + """ + Returns an iterator over all transitions. + + INPUT: + + Nothing. + + OUTPUT: + + An iterator over all transitions. + + EXAMPLES:: + + sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)]) + sage: [(t.from_state.label(), t.to_state.label()) + ....: for t in FSM._iter_transitions_all_()] + [('1', '2'), ('2', '2')] + """ + for state in self.iter_states(): + for t in state.transitions: + yield t + + + def initial_states(self): + """ + Returns a list of all initial states. + + INPUT: + + Nothing. + + OUTPUT: + + A list of all initial states. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A', is_initial=True) + sage: B = FSMState('B') + sage: F = FiniteStateMachine([(A, B, 1, 0)]) + sage: F.initial_states() + ['A'] + """ + return list(self.iter_initial_states()) + + + def iter_initial_states(self): + """ + Returns an iterator of the initial states. + + INPUT: + + Nothing. + + OUTPUT: + + An iterator over all initial states. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A', is_initial=True) + sage: B = FSMState('B') + sage: F = FiniteStateMachine([(A, B, 1, 0)]) + sage: [s.label() for s in F.iter_initial_states()] + ['A'] + """ + return itertools.ifilter(lambda s:s.is_initial, self.iter_states()) + + + def final_states(self): + """ + Returns a list of all final states. + + INPUT: + + Nothing. + + OUTPUT: + + A list of all final states. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A', is_final=True) + sage: B = FSMState('B', is_initial=True) + sage: C = FSMState('C', is_final=True) + sage: F = FiniteStateMachine([(A, B), (A, C)]) + sage: F.final_states() + ['A', 'C'] + """ + return list(self.iter_final_states()) + + + def iter_final_states(self): + """ + Returns an iterator of the final states. + + INPUT: + + Nothing. + + OUTPUT: + + An iterator over all initial states. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A', is_final=True) + sage: B = FSMState('B', is_initial=True) + sage: C = FSMState('C', is_final=True) + sage: F = FiniteStateMachine([(A, B), (A, C)]) + sage: [s.label() for s in F.iter_final_states()] + ['A', 'C'] + """ + return itertools.ifilter(lambda s:s.is_final, self.iter_states()) + + + def state(self, state): + """ + Returns the state of the finite state machine. + + INPUT: + + - ``state`` -- If ``state`` is not an instance of + :class:`FSMState`, then it is assumed that it is the label + of a state. + + OUTPUT: + + Returns the state of the finite state machine corresponding to + ``state``. + + If no state is found, then a ``LookupError`` is thrown. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A') + sage: FSM = FiniteStateMachine([(A, 'B'), ('C', A)]) + sage: FSM.state('A') == A + True + sage: FSM.state('xyz') + Traceback (most recent call last): + ... + LookupError: No state with label xyz found. + """ + def what(s, switch): + if switch: + return s.label() + else: + return s + switch = is_FSMState(state) + + try: + return self._states_dict_[what(state, switch)] + except AttributeError: + for s in self.iter_states(): + if what(s, not switch) == state: + return s + except KeyError: + pass + raise LookupError, \ + "No state with label %s found." % (what(state, switch),) + + + def transition(self, transition): + """ + Returns the transition of the finite state machine. + + INPUT: + + - ``transition`` -- If ``transition`` is not an instance of + :class:`FSMTransition`, then it is assumed that it is a + tuple ``(from_state, to_state, word_in, word_out)``. + + OUTPUT: + + Returns the transition of the finite state machine + corresponding to ``transition``. + + If no transition is found, then a ``LookupError`` is thrown. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: t = FSMTransition('A', 'B', 0) + sage: F = FiniteStateMachine([t]) + sage: F.transition(('A', 'B', 0)) + Transition from 'A' to 'B': 0|- + sage: id(t) == id(F.transition(('A', 'B', 0))) + True + """ + if not is_FSMTransition(transition): + transition = FSMTransition(*transition) + for s in self.iter_transitions(transition.from_state): + if s == transition: + return s + raise LookupError, "No transition found." + + + #************************************************************************* + # properties (state and transitions) + #************************************************************************* + + + def has_state(self, state): + """ + Returns whether ``state`` is one of the states of the finite + state machine. + + INPUT: + + - ``state`` can be a :class:`FSMState` or a label of a state. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: FiniteStateMachine().has_state('A') + False + """ + try: + self.state(state) + return True + except LookupError: + return False + + + def has_transition(self, transition): + """ + Returns whether ``transition`` is one of the transitions of + the finite state machine. + + INPUT: + + - ``transition`` has to be a :class:`FSMTransition`. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: t = FSMTransition('A', 'A', 0, 1) + sage: FiniteStateMachine().has_transition(t) + False + sage: FiniteStateMachine().has_transition(('A', 'A', 0, 1)) + Traceback (most recent call last): + ... + TypeError: Transition is not an instance of FSMTransition. + """ + if is_FSMTransition(transition): + return transition in self.iter_transitions() + raise TypeError, "Transition is not an instance of FSMTransition." + + + def has_initial_state(self, state): + """ + Returns whether ``state`` is one of the initial states of the + finite state machine. + + INPUT: + + - ``state`` can be a :class:`FSMState` or a label. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: F = FiniteStateMachine([('A', 'A')], initial_states=['A']) + sage: F.has_initial_state('A') + True + """ + try: + return self.state(state).is_initial + except LookupError: + return False + + + def has_initial_states(self): + """ + Returns whether the finite state machine has an initial state. + + INPUT: + + Nothing. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: FiniteStateMachine().has_initial_states() + False + """ + return len(self.initial_states()) > 0 + + + def has_final_state(self, state): + """ + Returns whether ``state`` is one of the final states of the + finite state machine. + + INPUT: + + - ``state`` can be a :class:`FSMState` or a label. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: FiniteStateMachine(final_states=['A']).has_final_state('A') + True + """ + try: + return self.state(state).is_final + except LookupError: + return False + + + def has_final_states(self): + """ + Returns whether the finite state machine has a final state. + + INPUT: + + Nothing. + + OUTPUT: + + True or False. + + EXAMPLES:: + + sage: FiniteStateMachine().has_final_states() + False + """ + return len(self.final_states()) > 0 + + + #************************************************************************* + # properties + #************************************************************************* + + + def is_deterministic(self): + """ + Returns whether the finite finite state machine is deterministic. + + INPUT: + + Nothing. + + OUTPUT: + + True or False. + + A finite state machine is considered to be deterministic if + each transition has input label of length one and for each + pair `(q,a)` where `q` is a state and `a` is an element of the + input alphabet, there is at most one transition from `q` with + input label `a`. + + TESTS:: + + sage: fsm = FiniteStateMachine() + sage: fsm.add_transition(('A', 'B', 0, [])) + Transition from 'A' to 'B': 0|- + sage: fsm.is_deterministic() + True + sage: fsm.add_transition(('A', 'C', 0, [])) + Transition from 'A' to 'C': 0|- + sage: fsm.is_deterministic() + False + sage: fsm.add_transition(('A', 'B', [0,1], [])) + Transition from 'A' to 'B': 0,1|- + sage: fsm.is_deterministic() + False + """ + for state in self.states(): + for transition in state.transitions: + if len(transition.word_in) != 1: + return False + + transition_classes_by_word_in = full_group_by( + state.transitions, + key=lambda t:t.word_in) + + for key,transition_class in transition_classes_by_word_in: + if len(transition_class) > 1: + return False + return True + + + def is_connected(self): + """ + TESTS:: + + sage: FiniteStateMachine().is_connected() + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + + #************************************************************************* + # let the finite state machine work + #************************************************************************* + + + def process(self, *args, **kwargs): + """ + Returns whether the finite state machine accepts the input, the state + where the computation stops and which output is generated. + + INPUT: + + - ``input_tape`` -- The input tape can be a list with entries from + the input alphabet. + + - ``initial_state`` -- (default: ``None``) The state in which + to start. If this parameter is ``None`` and there is only + one initial state in the machine, then this state is taken. + + OUTPUT: + + A triple, where + + - the first entry is true if the input string is accepted, + + - the second gives the reached state after processing the + input tape, and + + - the third gives a list of the output labels used during + processing (in the case the finite state machine runs as + transducer). + + Note that in the case the finite state machine is not + deterministic, one possible path is gone. This means, that in + this case the output can be wrong. Use + :meth:`.determinisation` to get a deterministic finite state + machine and try again. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A', is_initial = True, is_final = True) + sage: binary_inverter = Transducer({A:[(A, 0, 1), (A, 1, 0)]}) + sage: binary_inverter.process([0, 1, 0, 0, 1, 1]) + (True, 'A', [1, 0, 1, 1, 0, 0]) + + Alternatively, we can invoke this function by:: + + sage: binary_inverter([0, 1, 0, 0, 1, 1]) + (True, 'A', [1, 0, 1, 1, 0, 0]) + + :: + + sage: NAF_ = FSMState('_', is_initial = True, is_final = True) + sage: NAF1 = FSMState('1', is_final = True) + sage: NAF = Automaton( + ....: {NAF_: [(NAF_, 0), (NAF1, 1)], NAF1: [(NAF_, 0)]}) + sage: [NAF.process(w)[0] for w in [[0], [0, 1], [1, 1], [0, 1, 0, 1], + ....: [0, 1, 1, 1, 0], [1, 0, 0, 1, 1]]] + [True, True, False, True, False, False] + + """ + it = self.iter_process(*args, **kwargs) + for _ in it: + pass + return (it.accept_input, it.current_state, it.output_tape) + + + def iter_process(self, input_tape=None, initial_state=None): + """ + See `process` for more informations. + + EXAMPLES:: + + sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, + ....: initial_states=['A'], final_states=['A']) + sage: it = inverter.iter_process(input_tape=[0, 1, 1]) + sage: for _ in it: + ....: pass + sage: it.output_tape + [1, 0, 0] + """ + return FSMProcessIterator(self, input_tape, initial_state) + + + #************************************************************************* + # change finite state machine (add/remove state/transitions) + #************************************************************************* + + + def add_state(self, state): + """ + Adds a state to the finite state machine and returns the new + state. If the state already exists, that existing state is + returned. + + INPUT: + + - ``state`` is either an instance of FSMState or, otherwise, a + label of a state. + + OUTPUT: + + The new or existing state. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: F = FiniteStateMachine() + sage: A = FSMState('A', is_initial=True) + sage: F.add_state(A) + 'A' + """ + try: + return self.state(state) + except LookupError: + pass + # at this point we know that we have a new state + if is_FSMState(state): + s = state + else: + s = FSMState(state) + s.transitions = list() + self._states_.append(s) + try: + self._states_dict_[s.label()] = s + except AttributeError: + pass + return s + + + def add_states(self, states): + """ + Adds several states. See add_state for more information. + + INPUT: + + - ``states`` -- a list of states or iterator over states. + + OUTPUT: + + Nothing. + + EXAMPLES:: + + sage: F = FiniteStateMachine() + sage: F.add_states(['A', 'B']) + sage: F.states() + ['A', 'B'] + """ + for state in states: + self.add_state(state) + + + def add_transition(self, *args, **kwargs): + """ + Adds a transition to the finite state machine and returns the + new transition. If the transition already exists, that + existing transition is returned. + + INPUT: + + The following forms are all accepted: + + :: + + sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition + sage: A = FSMState('A') + sage: B = FSMState('B') + + sage: FSM = FiniteStateMachine() + sage: FSM.add_transition(FSMTransition(A, B, 0, 1)) + Transition from 'A' to 'B': 0|1 + + sage: FSM = FiniteStateMachine() + sage: FSM.add_transition(A, B, 0, 1) + Transition from 'A' to 'B': 0|1 + + sage: FSM = FiniteStateMachine() + sage: FSM.add_transition(A, B, word_in=0, word_out=1) + Transition from 'A' to 'B': 0|1 + + sage: FSM = FiniteStateMachine() + sage: FSM.add_transition('A', 'B', {'word_in': 0, 'word_out': 1}) + Transition from 'A' to 'B': {'word_in': 0, 'word_out': 1}|- + + sage: FSM = FiniteStateMachine() + sage: FSM.add_transition(from_state=A, to_state=B, + ....: word_in=0, word_out=1) + Transition from 'A' to 'B': 0|1 + + sage: FSM = FiniteStateMachine() + sage: FSM.add_transition({'from_state': A, 'to_state': B, + ....: 'word_in': 0, 'word_out': 1}) + Transition from 'A' to 'B': 0|1 + + sage: FSM = FiniteStateMachine() + sage: FSM.add_transition((A, B, 0, 1)) + Transition from 'A' to 'B': 0|1 + + sage: FSM = FiniteStateMachine() + sage: FSM.add_transition([A, B, 0, 1]) + Transition from 'A' to 'B': 0|1 + + If the states ``A`` and ``B`` are not instances of FSMState, then + it is assumed that they are labels of states. + + OUTPUT: + + The new or existing transition. + """ + if len(args) + len(kwargs) == 0: + return + if len(args) + len(kwargs) == 1: + if len(args) == 1: + d = args[0] + if is_FSMTransition(d): + return self._add_fsm_transition_(d) + else: + d = kwargs.itervalues().next() + if hasattr(d, 'iteritems'): + args = [] + kwargs = d + elif hasattr(d, '__iter__'): + args = d + kwargs = {} + else: + raise TypeError, "Cannot decide what to do with input." + + data = dict(zip( + ('from_state', 'to_state', 'word_in', 'word_out', 'hook'), + args)) + data.update(kwargs) + + data['from_state'] = self.add_state(data['from_state']) + data['to_state'] = self.add_state(data['to_state']) + + return self._add_fsm_transition_(FSMTransition(**data)) + + + def _add_fsm_transition_(self, t): + """ + Adds a transition. + + INPUT: + + - ``t`` -- an instance of ``FSMTransition``. + + OUTPUT: + + The new transition. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: F = FiniteStateMachine() + sage: F._add_fsm_transition_(FSMTransition('A', 'B')) + Transition from 'A' to 'B': -|- + """ + try: + return self.transition(t) + except LookupError: + pass + from_state = self.add_state(t.from_state) + self.add_state(t.to_state) + from_state.transitions.append(t) + return t + + + def add_from_transition_function(self, function, initial_states=None, + explore_existing_states=True): + """ + Constructs a finite state machine from a transition function. + + INPUT: + + - ``function`` may return a tuple (new_state, output_word) or a + list of such tuples. + + - ``initial_states`` -- If no initial states are given, the + already existing initial states of self are taken. + + - If ``explore_existing_states`` is True (default), then + already existing states in self (e.g. already given final + states) will also be processed if they are reachable from + the initial states. + + OUTPUT: + + Nothing. + + EXAMPLES:: + + sage: F = FiniteStateMachine(initial_states=['A'], + ....: input_alphabet=[0, 1]) + sage: def f(state, input): + ....: return [('A', input), ('B', 1-input)] + sage: F.add_from_transition_function(f) + sage: F.transitions() + [Transition from 'A' to 'A': 0|0, + Transition from 'A' to 'B': 0|1, + Transition from 'A' to 'A': 1|1, + Transition from 'A' to 'B': 1|0, + Transition from 'B' to 'A': 0|0, + Transition from 'B' to 'B': 0|1, + Transition from 'B' to 'A': 1|1, + Transition from 'B' to 'B': 1|0] + + Initial states can also be given as a parameter:: + + sage: F = FiniteStateMachine(input_alphabet=[0,1]) + sage: def f(state, input): + ....: return [('A', input), ('B', 1-input)] + sage: F.add_from_transition_function(f,initial_states=['A']) + sage: F.initial_states() + ['A'] + + Already existing states in the finite state machine (the final + states in the example below) are also explored:: + + sage: F = FiniteStateMachine(initial_states=[0], + ....: final_states=[1], + ....: input_alphabet=[0]) + sage: def transition_function(state, letter): + ....: return(1-state, []) + sage: F.add_from_transition_function(transition_function) + sage: F.transitions() + [Transition from 0 to 1: 0|-, + Transition from 1 to 0: 0|-] + + If ``explore_existing_states=False``, however, this behavior + is turned off, i.e., already existing states are not + explored:: + + sage: F = FiniteStateMachine(initial_states=[0], + ....: final_states=[1], + ....: input_alphabet=[0]) + sage: def transition_function(state, letter): + ....: return(1-state, []) + sage: F.add_from_transition_function(transition_function, + ....: explore_existing_states=False) + sage: F.transitions() + [Transition from 0 to 1: 0|-] + + TEST:: + + sage: F = FiniteStateMachine(initial_states=['A']) + sage: def f(state, input): + ....: return [('A', input), ('B', 1-input)] + sage: F.add_from_transition_function(f) + Traceback (most recent call last): + ... + ValueError: No input alphabet is given. + Try calling determine_alphabets(). + """ + if self.input_alphabet is None: + raise ValueError, ("No input alphabet is given. " + "Try calling determine_alphabets().") + + if initial_states is None: + not_done = self.initial_states() + elif hasattr(initial_states, '__iter__'): + not_done = [] + for s in initial_states: + state = self.add_state(s) + state.is_initial = True + not_done.append(state) + else: + raise TypeError, 'Initial states must be iterable ' \ + '(e.g. a list of states).' + if len(not_done) == 0: + raise ValueError, "No state is initial." + if explore_existing_states: + ignore_done = self.states() + for s in not_done: + try: + ignore_done.remove(s) + except ValueError: + pass + else: + ignore_done = [] + while len(not_done) > 0: + s = not_done.pop(0) + for letter in self.input_alphabet: + try: + return_value = function(s.label(), letter) + except LookupError: + continue + if not hasattr(return_value, "pop"): + return_value = [return_value] + try: + for (st_label, word) in return_value: + if not self.has_state(st_label): + not_done.append(self.add_state(st_label)) + elif len(ignore_done) > 0: + u = self.state(st_label) + if u in ignore_done: + not_done.append(u) + ignore_done.remove(u) + self.add_transition(s, st_label, + word_in=letter, word_out=word) + except TypeError: + raise ValueError("The callback function for add_from_transition is expected to return a pair (new_state, output_label) or a list of such pairs. For the state %s and the input letter %s, it however returned %s, which is not acceptable." % (s.label(), letter, return_value)) + + + def add_transitions_from_function(self, function, labels_as_input=True): + """ + Adds a transition if ``function(state, state)`` says that there is one. + + INPUT: + + - ``function`` -- a transition function. Given two states + ``from_state`` and ``to_state`` (or their labels, if + ``label_as_input`` is true), this function shall return a + tuple ``(word_in, word_out)`` to add a transition from + ``from_state`` to ``to_state`` with input and output labels + ``word_in`` and ``word_out``, respectively. If no such + addition is to be added, the transition function shall + return ``None``. + + - ``label_as_input`` -- (default: ``True``) + + OUTPUT: + + Nothing. + + EXAMPLES:: + + sage: F = FiniteStateMachine() + sage: F.add_states(['A', 'B', 'C']) + sage: def f(state1, state2): + ....: if state1 == 'C': + ....: return None + ....: return (0, 1) + sage: F.add_transitions_from_function(f) + sage: len(F.transitions()) + 6 + """ + for s_from in self.iter_states(): + for s_to in self.iter_states(): + if labels_as_input: + t = function(s_from.label(), s_to.label()) + else: + t = function(s_from, s_to) + if hasattr(t, '__getitem__'): + label_in = t[0] + try: + label_out = t[1] + except LookupError: + label_out = None + self.add_transition(s_from, s_to, label_in, label_out) + + + def delete_transition(self, t): + """ + Deletes a transition by removing it from the list of transitions of + the state, where the transition starts. + + INPUT: + + - ``t`` -- a transition. + + OUTPUT: + + Nothing. + + EXAMPLES:: + + sage: F = FiniteStateMachine([('A', 'B', 0), ('B', 'A', 1)]) + sage: F.delete_transition(('A', 'B', 0)) + sage: F.transitions() + [Transition from 'B' to 'A': 1|-] + """ + transition = self.transition(t) + transition.from_state.transitions.remove(transition) + + + def delete_state(self, s): + """ + Deletes a state and all transitions coming or going to this state. + + INPUT: + + - ``s`` -- s has to be a label of a state or :class:`FSMState`. + + OUTPUT: + + Nothing. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMTransition + sage: t1 = FSMTransition('A', 'B', 0) + sage: t2 = FSMTransition('B', 'B', 1) + sage: F = FiniteStateMachine([t1, t2]) + sage: F.delete_state('A') + sage: F. transitions() + [Transition from 'B' to 'B': 1|-] + """ + state = self.state(s) + for transition in self.transitions(): + if transition.to_state == state: + self.delete_transition(transition) + self._states_.remove(state) + + + def remove_epsilon_transitions(self): + """ + TESTS:: + + sage: FiniteStateMachine().remove_epsilon_transitions() + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + + def accessible_components(self): + """ + Returns a new finite state machine with the accessible states + of self and all transitions between those states. + + INPUT: + + Nothing. + + OUTPUT: + + A finite state machine with the accessible states of self and + all transitions between those states. + + A state is accessible if there is a directed path from an + initial state to the state. If self has no initial states then + a copy of the finite state machine self is returned. + + EXAMPLES:: + + sage: F = Automaton([(0, 0, 0), (0, 1, 1), (1, 1, 0), (1, 0, 1)], + ....: initial_states=[0]) + sage: F.accessible_components() + Automaton with 2 states + + :: + + sage: F = Automaton([(0, 0, 1), (0, 0, 1), (1, 1, 0), (1, 0, 1)], + ....: initial_states=[0]) + sage: F.accessible_components() + Automaton with 1 states + """ + if len(self.initial_states()) == 0: + return deepcopy(self) + + memo = {} + def accessible(sf, read): + trans = filter(lambda x: x.word_in[0] == read, + self.transitions(sf)) + return map(lambda x: (deepcopy(x.to_state, memo), x.word_out), + trans) + + new_initial_states=map(lambda x: deepcopy(x, memo), + self.initial_states()) + result = self.empty_copy() + result.add_from_transition_function(accessible, + initial_states=new_initial_states) + for final_state in self.iter_final_states(): + try: + new_final_state=result.state(final_state.label) + new_final_state.is_final=True + except LookupError: + pass + return result + + + # ************************************************************************* + # creating new finite state machines + # ************************************************************************* + + + def disjoint_union(self, other): + """ + TESTS:: + + sage: F = FiniteStateMachine([('A', 'A')]) + sage: FiniteStateMachine().disjoint_union(F) + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + + def concatenation(self, other): + """ + TESTS:: + + sage: F = FiniteStateMachine([('A', 'A')]) + sage: FiniteStateMachine().concatenation(F) + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + + def Kleene_closure(self): + """ + TESTS:: + + sage: FiniteStateMachine().Kleene_closure() + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + + def intersection(self, other): + """ + TESTS:: + + sage: F = FiniteStateMachine([('A', 'A')]) + sage: FiniteStateMachine().intersection(F) + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + + def product_FiniteStateMachine(self, other, function, + new_input_alphabet=None, + only_accessible_components=True): + """ + Returns a new finite state machine whose states are + pairs of states of the original finite state machines. + + INPUT: + + - ``other`` -- a finite state machine. + + - ``function`` has to accept two transitions from `A` to `B` + and `C` to `D` and returns a pair ``(word_in, word_out)`` + which is the label of the transition `(A, C)` to `(B, + D)`. If there is no transition from `(A, C)` to `(B, D)`, + then ``function`` should raise a ``LookupError``. + + - ``new_input_alphabet`` (optional)-- the new input alphabet + as a list. + + - ``only_accessible_components`` -- If true (default), then + the result is piped through ``accessible_components``. If no + ``new_input_alphabet`` is given, it is determined by + ``determine_alphabets``. + + OUTPUT: + + A finite state machine whose states are pairs of states of the + original finite state machines. + + The labels of the transitions are defined by ``function``. + + EXAMPLES:: + + sage: F = Automaton([('A', 'B', 1), ('A', 'A', 0), ('B', 'A', 2)], + ....: initial_states=['A'], final_states=['B'], + ....: determine_alphabets=True) + sage: G = Automaton([(1, 1, 1)], initial_states=[1], final_states=[1]) + sage: def addition(transition1, transition2): + ....: return (transition1.word_in[0] + transition2.word_in[0], + ....: None) + sage: H = F.product_FiniteStateMachine(G, addition, [0, 1, 2, 3], only_accessible_components=False) + sage: H.transitions() + [Transition from ('A', 1) to ('B', 1): 2|-, + Transition from ('A', 1) to ('A', 1): 1|-, + Transition from ('B', 1) to ('A', 1): 3|-] + sage: H1 = F.product_FiniteStateMachine(G, addition, [0, 1, 2, 3], only_accessible_components=False) + sage: H1.states()[0].label()[0] is F.states()[0] + True + sage: H1.states()[0].label()[1] is G.states()[0] + True + + :: + + sage: F = Automaton([(0,1,1/4), (0,0,3/4), (1,1,3/4), (1,0,1/4)], + ....: initial_states=[0] ) + sage: G = Automaton([(0,0,1), (1,1,3/4), (1,0,1/4)], + ....: initial_states=[0] ) + sage: H = F.product_FiniteStateMachine( + ....: G, lambda t1,t2: (t1.word_in[0]*t2.word_in[0], None)) + sage: H.states() + [(0, 0), (1, 0)] + + :: + + sage: F = Automaton([(0,1,1/4), (0,0,3/4), (1,1,3/4), (1,0,1/4)], + ....: initial_states=[0] ) + sage: G = Automaton([(0,0,1), (1,1,3/4), (1,0,1/4)], + ....: initial_states=[0] ) + sage: H = F.product_FiniteStateMachine(G, + ....: lambda t1,t2: (t1.word_in[0]*t2.word_in[0], None), + ....: only_accessible_components=False) + sage: H.states() + [(0, 0), (1, 0), (0, 1), (1, 1)] + """ + result = self.empty_copy() + if new_input_alphabet is not None: + result.input_alphabet = new_input_alphabet + else: + result.input_alphabet = None + + for transition1 in self.transitions(): + for transition2 in other.transitions(): + try: + word = function(transition1, transition2) + except LookupError: + continue + result.add_transition((transition1.from_state, + transition2.from_state), + (transition1.to_state, + transition2.to_state), + word[0], word[1]) + for state in result.states(): + if all(map(lambda s: s.is_initial, state.label())): + state.is_initial = True + if all(map(lambda s: s.is_final, state.label())): + state.is_final = True + + if only_accessible_components: + if new_input_alphabet is None: + result.determine_alphabets() + return result.accessible_components() + else: + return result + + + def cartesian_product(self, other, only_accessible_components=True): + """ + Returns a new finite state machine, which is the cartesian + product of self and other. + + INPUT: + + - ``other`` -- a finite state machine. + + - ``only_accessible_components`` + + OUTPUT: + + A new finite state machine, which is the cartesian product of + self and other. + + The set of states of the new automaton is the cartesian + product of the set of states of both given automata. There is + a transition `((A, B), (C, D), a)` in the new automaton if + there are transitions `(A, C, a)` and `(B, C, a)` in the old + automata. + + EXAMPLES:: + + sage: aut1 = Automaton([('1', '2', 1), ('2', '2', 1), ('2', '2', 0)], + ....: initial_states=['1'], final_states=['2'], + ....: determine_alphabets=True) + sage: aut2 = Automaton([('A', 'A', 1), ('A', 'B', 1), + ....: ('B', 'B', 0), ('B', 'A', 0)], + ....: initial_states=['A'], final_states=['B'], + ....: determine_alphabets=True) + sage: res = aut1.cartesian_product(aut2) + sage: res.transitions() + [Transition from ('1', 'A') to ('2', 'A'): 1|-, + Transition from ('1', 'A') to ('2', 'B'): 1|-, + Transition from ('2', 'A') to ('2', 'A'): 1|-, + Transition from ('2', 'A') to ('2', 'B'): 1|-, + Transition from ('2', 'B') to ('2', 'B'): 0|-, + Transition from ('2', 'B') to ('2', 'A'): 0|-] + """ + def function(transition1, transition2): + if transition1.word_in == transition2.word_in \ + and transition1.word_out == transition2.word_out: + return (transition1.word_in, transition1.word_out) + else: + raise LookupError + + return self.product_FiniteStateMachine( + other, function, + only_accessible_components = only_accessible_components) + + + def composition(self, other, algorithm=None, + only_accessible_components=True): + """ + Returns a new transducer which is the composition of self and + other. + + INPUT: + + - ``other`` -- a transducer + + - ``algorithm`` -- can be one of the following + + - ``direct`` -- The composition is calculated directly. + + There can be arbitrarily many initial and final states, + but the input and output labels must have length 1. + + + WARNING: The output of other is fed into self. + + - ``explorative`` -- An explorative algorithm is used. + + At least the following restrictions apply, but are not + checked: + - both self and other have exactly one initial state + - all input labels of transitions have length exactly 1 + + The input alphabet of self has to be specified. + + This is a very limited implementation of composition. + WARNING: The output of ``other`` is fed into ``self``. + + If algorithm is ``None``, then the algorithm is chosen + automatically (at the moment always ``direct``). + + OUTPUT: + + A new transducer. + + The labels of the new finite state machine are pairs + of states of the original finite state machines. + + EXAMPLES:: + + sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)], + ....: initial_states=['A', 'B'], final_states=['B'], + ....: determine_alphabets=True) + sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1), + ....: (2, 2, 1, 1), (2, 2, 0, 0)], + ....: initial_states=[1], final_states=[2], + ....: determine_alphabets=True) + sage: Hd = F.composition(G, algorithm='direct') + sage: Hd.initial_states() + [(1, 'B'), (1, 'A')] + sage: Hd.transitions() + [Transition from (1, 'B') to (1, 'A'): 1|1, + Transition from (1, 'A') to (2, 'B'): 0|0, + Transition from (2, 'B') to (2, 'A'): 0|1, + Transition from (2, 'A') to (2, 'B'): 1|0] + + :: + + sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1), + ....: ('B', 'B', 0, 0)], + ....: initial_states=['A'], final_states=['B']) + sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0), + ....: (2, 2, 0, 1), (2, 1, 1, 1)], + ....: initial_states=[1], final_states=[1]) + sage: He = G.composition(F, algorithm='explorative') + sage: He.transitions() + [Transition from ('A', 1) to ('B', 2): 1|0,1, + Transition from ('B', 2) to ('B', 2): 0|1, + Transition from ('B', 2) to ('B', 1): 1|1, + Transition from ('B', 1) to ('B', 1): 0|0, + Transition from ('B', 1) to ('B', 2): 1|0] + + Be aware that after composition, different transitions may + share the same output label (same python object):: + + sage: F = Transducer([ ('A','B',0,0), ('B','A',0,0)], + ....: initial_states=['A'], + ....: final_states=['A']) + sage: F.transitions()[0].word_out is F.transitions()[1].word_out + False + sage: G = Transducer([('C','C',0,1)],) + ....: initial_states=['C'], + ....: final_states=['C']) + sage: H = G.composition(F) + sage: H.transitions()[0].word_out is H.transitions()[1].word_out + True + + TESTS: + + Due to the limitations of the two algorithms the following + (examples from above, but different algorithm used) does not + give a full answer or does not work + + In the following, ``algorithm='explorative'`` is inadequate, + as ``F`` has more than one initial state:: + + sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)], + ....: initial_states=['A', 'B'], final_states=['B'], + ....: determine_alphabets=True) + sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1), + ....: (2, 2, 1, 1), (2, 2, 0, 0)], + ....: initial_states=[1], final_states=[2], + ....: determine_alphabets=True) + sage: He = F.composition(G, algorithm='explorative') + sage: He.initial_states() + [(1, 'A')] + sage: He.transitions() + [Transition from (1, 'A') to (2, 'B'): 0|0, + Transition from (2, 'B') to (2, 'A'): 0|1, + Transition from (2, 'A') to (2, 'B'): 1|0] + + In the following example, ``algorithm='direct'`` is inappropriate + as there are edges with output labels of length greater than 1:: + + sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1), + ....: ('B', 'B', 0, 0)], + ....: initial_states=['A'], final_states=['B']) + sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0), + ....: (2, 2, 0, 1), (2, 1, 1, 1)], + ....: initial_states=[1], final_states=[1]) + sage: Hd = G.composition(F, algorithm='direct') + """ + if algorithm == None: + algorithm = 'direct' + if algorithm == 'direct': + return self._composition_direct_(other, only_accessible_components) + elif algorithm == 'explorative': + return self._composition_explorative_(other) + else: + raise ValueError, "Unknown algorithm %s." % (algorithm,) + + + def _composition_direct_(self, other, only_accessible_components=True): + """ + See :meth:`.composition` for details. + + TESTS:: + + sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)], + ....: initial_states=['A', 'B'], final_states=['B'], + ....: determine_alphabets=True) + sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1), + ....: (2, 2, 1, 1), (2, 2, 0, 0)], + ....: initial_states=[1], final_states=[2], + ....: determine_alphabets=True) + sage: Hd = F._composition_direct_(G) + sage: Hd.initial_states() + [(1, 'B'), (1, 'A')] + sage: Hd.transitions() + [Transition from (1, 'B') to (1, 'A'): 1|1, + Transition from (1, 'A') to (2, 'B'): 0|0, + Transition from (2, 'B') to (2, 'A'): 0|1, + Transition from (2, 'A') to (2, 'B'): 1|0] + + """ + def function(transition1, transition2): + if transition1.word_out == transition2.word_in: + return (transition1.word_in, transition2.word_out) + else: + raise LookupError + + return other.product_FiniteStateMachine( + self, function, + only_accessible_components=only_accessible_components) + + + def _composition_explorative_(self, other): + """ + See :meth:`.composition` for details. + + TESTS:: + + sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1), + ....: ('B', 'B', 0, 0)], + ....: initial_states=['A'], final_states=['B']) + sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0), + ....: (2, 2, 0, 1), (2, 1, 1, 1)], + ....: initial_states=[1], final_states=[1]) + sage: He = G._composition_explorative_(F) + sage: He.transitions() + [Transition from ('A', 1) to ('B', 2): 1|0,1, + Transition from ('B', 2) to ('B', 2): 0|1, + Transition from ('B', 2) to ('B', 1): 1|1, + Transition from ('B', 1) to ('B', 1): 0|0, + Transition from ('B', 1) to ('B', 2): 1|0] + + TODO: + + The explorative algorithm should be re-implemented using the + process iterators of both finite state machines. + """ + def composition_transition(state, input): + (state1, state2) = state + transition1 = None + for transition in other.iter_transitions(state1): + if transition.word_in == [input]: + transition1 = transition + break + if transition1 is None: + raise LookupError + new_state1 = transition1.to_state + new_state2 = state2 + output = [] + for o in transition1.word_out: + transition2 = None + for transition in self.iter_transitions(new_state2): + if transition.word_in == [o]: + transition2 = transition + break + if transition2 is None: + raise LookupError + new_state2 = transition2.to_state + output += transition2.word_out + return ((new_state1, new_state2), output) + + F = other.empty_copy() + new_initial_states = [(other.initial_states()[0], self.initial_states()[0])] + F.add_from_transition_function(composition_transition, + initial_states=new_initial_states) + + for state in F.states(): + if all(map(lambda s: s.is_final, state.label())): + state.is_final = True + + return F + + + def input_projection(self): + """ + Returns an automaton where the output of each transition of + self is deleted. + + INPUT: + + Nothing + + OUTPUT: + + An automaton. + + EXAMPLES:: + + sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1), + ....: ('B', 'B', 1, 0)]) + sage: G = F.input_projection() + sage: G.transitions() + [Transition from 'A' to 'B': 0|-, + Transition from 'A' to 'A': 1|-, + Transition from 'B' to 'B': 1|-] + """ + return self.projection(what='input') + + + def output_projection(self): + """ + Returns a automaton where the input of each transition of self + is deleted and the new input is the original output. + + INPUT: + + Nothing + + OUTPUT: + + An automaton. + + EXAMPLES:: + + sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1), + ....: ('B', 'B', 1, 0)]) + sage: G = F.output_projection() + sage: G.transitions() + [Transition from 'A' to 'B': 1|-, + Transition from 'A' to 'A': 1|-, + Transition from 'B' to 'B': 0|-] + """ + return self.projection(what='output') + + + def projection(self, what='input'): + """ + Returns an Automaton which transition labels are the projection + of the transition labels of the input. + + INPUT: + + - ``what`` -- (default: ``input``) either ``input`` or ``output``. + + OUTPUT: + + An automaton. + + EXAMPLES:: + + sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1), + ....: ('B', 'B', 1, 0)]) + sage: G = F.projection(what='output') + sage: G.transitions() + [Transition from 'A' to 'B': 1|-, + Transition from 'A' to 'A': 1|-, + Transition from 'B' to 'B': 0|-] + """ + new = Automaton() + + if what == 'input': + new.input_alphabet = copy(self.input_alphabet) + elif what == 'output': + new.input_alphabet = copy(self.output_alphabet) + else: + raise NotImplementedError + + state_mapping = {} + for state in self.iter_states(): + state_mapping[state] = new.add_state(deepcopy(state)) + for transition in self.iter_transitions(): + if what == 'input': + new_word_in = transition.word_in + elif what == 'output': + new_word_in = transition.word_out + else: + raise NotImplementedError + new.add_transition((state_mapping[transition.from_state], + state_mapping[transition.to_state], + new_word_in, None)) + return new + + + def transposition(self): + """ + Returns a new finite state machine, where all transitions of the + input finite state machine are reversed. + + INPUT: + + Nothing. + + OUTPUT: + + A new finite state machine. + + EXAMPLES:: + + sage: aut = Automaton([('A', 'A', 0), ('A', 'A', 1), ('A', 'B', 0)], + ....: initial_states=['A'], final_states=['B']) + sage: aut.transposition().transitions('B') + [Transition from 'B' to 'A': 0|-] + + :: + + sage: aut = Automaton([('1', '1', 1), ('1', '2', 0), ('2', '2', 0)], + ....: initial_states=['1'], final_states=['1', '2']) + sage: aut.transposition().initial_states() + ['1', '2'] + """ + transposition = self.empty_copy() + + for state in self.states(): + transposition.add_state(deepcopy(state)) + + for transition in self.transitions(): + transposition.add_transition( + transition.to_state.label(), transition.from_state.label(), + transition.word_in, transition.word_out) + + for initial in self.initial_states(): + state = transposition.state(initial.label()) + if not initial.is_final: + state.is_final = True + state.is_initial = False + + for final in self.final_states(): + state = transposition.state(final.label()) + if not final.is_initial: + state.is_final = False + state.is_initial = True + + return transposition + + + def split_transitions(self): + """ + Returns a new transducer, where all transitions in self with input + labels consisting of more than one letter + are replaced by a path of the corresponding length. + + INPUT: + + Nothing. + + OUTPUT: + + A new transducer. + + EXAMPLES:: + + sage: A = Transducer([('A', 'B', [1, 2, 3], 0)], + ....: initial_states=['A'], final_states=['B']) + sage: A.split_transitions().states() + [('A', ()), ('B', ()), + ('A', (1,)), ('A', (1, 2))] + """ + new = self.empty_copy() + for state in self.states(): + new.add_state(FSMState((state, ()), is_initial=state.is_initial, + is_final=state.is_final)) + for transition in self.transitions(): + for j in range(len(transition.word_in)-1): + new.add_transition(( + (transition.from_state, tuple(transition.word_in[:j])), + (transition.from_state, tuple(transition.word_in[:j+1])), + transition.word_in[j], + [])) + new.add_transition(( + (transition.from_state, tuple(transition.word_in[:-1])), + (transition.to_state, ()), + transition.word_in[-1:], + transition.word_out)) + return new + + + # ************************************************************************* + # simplifications + # ************************************************************************* + + + def prepone_output(self): + """ + Apply the following to each state `s` (except initial and + final states) of the finite state machine as often as + possible: + + If the letter a is prefix of the output label of all + transitions from `s`, then remove it from all these labels and + append it to all output labels of all transitions leading to + `s`. + + We assume that the states have no output labels. + + INPUT: + + Nothing. + + OUTPUT: + + Nothing. + + EXAMPLES:: + + sage: A = Transducer([('A', 'B', 1, 1), ('B', 'B', 0, 0), ('B', 'C', 1, 0)], + ....: initial_states=['A'], final_states=['C']) + sage: A.prepone_output() + sage: A.transitions() + [Transition from 'A' to 'B': 1|1,0, + Transition from 'B' to 'B': 0|0, + Transition from 'B' to 'C': 1|-] + + :: + + sage: B = Transducer([('A', 'B', 0, 1), ('B', 'C', 1, [1, 1]), ('B', 'C', 0, 1)], + ....: initial_states=['A'], final_states=['C']) + sage: B.prepone_output() + sage: B.transitions() + [Transition from 'A' to 'B': 0|1,1, + Transition from 'B' to 'C': 1|1, + Transition from 'B' to 'C': 0|-] + + If initial states are not labeled as such, unexpected results may be obtained:: + + sage: C = Transducer([(0,1,0,0)]) + sage: C.prepone_output() + prepone_output: All transitions leaving state 0 have an + output label with prefix 0. However, there is no inbound + transition and it is not an initial state. This routine + (possibly called by simplification) therefore erased this + prefix from all outbound transitions. + sage: C.transitions() + [Transition from 0 to 1: 0|-] + + """ + def find_common_output(state): + if len(filter(lambda transition: len(transition.word_out) == 0, self.transitions(state))) > 0: + return () + first_letters = set(map(lambda transition: transition.word_out[0], self.transitions(state))) + if len(first_letters) == 1: + return (first_letters.pop(),) + return () + + changed = 1 + iteration = 0 + while changed > 0: + changed = 0 + iteration += 1 + for state in self.states(): + if state.is_initial or state.is_final: + continue + assert len(state.word_out) == 0, \ + "prepone_output assumes that all states have empty output word, but state %s has output word %s" % \ + (state, state.word_out) + common_output = find_common_output(state) + if len(common_output) > 0: + changed += 1 + for transition in self.transitions(state): + assert transition.word_out[0] == common_output[0] + transition.word_out = transition.word_out[1:] + found_inbound_transition = False + for transition in self.transitions(): + if transition.to_state == state: + transition.word_out = transition.word_out + [common_output[0]] + found_inbound_transition = True + if not found_inbound_transition: + print "prepone_output: All transitions leaving state %s have an output label with prefix %s. "\ + "However, there is no inbound transition and it is not an initial state. "\ + "This routine (possibly called by simplification) therefore erased this prefix from all "\ + "outbound transitions." % (state, common_output[0]) + + + + def equivalence_classes(self): + """ + Returns a list of equivalence classes of states. + + INPUT: + + Nothing. + + OUTPUT: + + A list of equivalence classes of states. + + Two states `a` and `b` are equivalent, if and only if for each + input label word_in the following holds: + + For paths `p_a` from `a` to `a'` with input label ``word_in`` + and output label ``word_out_a`` and `p_b` from `b` to `b'` + with input label ``word_in`` and output label ``word_out_b``, + we have ``word_out_a=word_out_b``, `a'` and `b'` have the same + output label and are both final or both non-final. + + The function :meth:`.equivalence_classes` returns a list of + the equivalence classes to this equivalence relation. + + This is one step of Moore's minimization algorithm. + + .. SEEALSO:: + + :meth:`.minimization` + + EXAMPLES:: + + sage: fsm = FiniteStateMachine([("A", "B", 0, 1), ("A", "B", 1, 0), + ....: ("B", "C", 0, 0), ("B", "C", 1, 1), + ....: ("C", "D", 0, 1), ("C", "D", 1, 0), + ....: ("D", "A", 0, 0), ("D", "A", 1, 1)]) + sage: fsm.equivalence_classes() + [['A', 'C'], ['B', 'D']] + """ + + # Two states a and b are said to be 0-equivalent, if their output + # labels agree and if they are both final or non-final. + # + # For some j >= 1, two states a and b are said to be j-equivalent, if + # they are j-1 equivalent and if for each element letter letter_in of + # the input alphabet and transitions t_a from a with input label + # letter_in, output label word_out_a to a' and t_b from b with input + # label letter_in, output label word_out_b to b', we have + # word_out_a=word_out_b and a' and b' are j-1 equivalent. + + # If for some j the relations j-1 equivalent and j-equivalent + # coincide, then they are equal to the equivalence relation described + # in the docstring. + + # classes_current holds the equivalence classes of j-equivalence, + # classes_previous holds the equivalence classes of j-1 equivalence. + + if not self.is_deterministic(): + raise NotImplementedError, "Minimization via Moore's Algorithm is only implemented for deterministic finite state machines" + + # initialize with 0-equivalence + classes_previous = [] + key_0 = lambda state: (state.is_final, state.word_out) + states_grouped = full_group_by(self.states(), key=key_0) + classes_current = [equivalence_class for + (key,equivalence_class) in states_grouped] + + while len(classes_current) != len(classes_previous): + class_of = {} + classes_previous = classes_current + classes_current = [] + + for k in range(len(classes_previous)): + for state in classes_previous[k]: + class_of[state] = k + + key_current = lambda state: sorted( + [(transition.word_in, + transition.word_out, + class_of[transition.to_state]) + for transition in state.transitions]) + + for class_previous in classes_previous: + states_grouped = full_group_by(class_previous, key=key_current) + classes_current.extend([equivalence_class for + (key,equivalence_class) in states_grouped]) + + return classes_current + + + def quotient(self, classes): + """ + Constructs the quotient with respect to the equivalence + classes. + + INPUT: + + - ``classes`` is a list of equivalence classes of states. + + OUTPUT: + + A finite state machine. + + Assume that `c` is a class and `s`, `s'` are states in `c`. If + there is a transition from `s` to some `t` with input label + ``word_in`` and output label ``word_out``, then there has to + be a transition from `s'` to some `t'` with input label + ``word_in`` and output label ``word_out`` such that `s'` and + `t'` are states of the same class `c'`. Then there is a + transition from `c` to `c'` in the quotient with input label + ``word_in`` and output label ``word_out``. + + Non-initial states may be merged with initial states, the + resulting state is an initial state. + + All states in a class must have the same ``is_final`` and + ``word_out`` values. + + EXAMPLES:: + + sage: fsm = FiniteStateMachine([("A", "B", 0, 1), ("A", "B", 1, 0), + ....: ("B", "C", 0, 0), ("B", "C", 1, 1), + ....: ("C", "D", 0, 1), ("C", "D", 1, 0), + ....: ("D", "A", 0, 0), ("D", "A", 1, 1)]) + sage: fsmq = fsm.quotient([[fsm.state("A"), fsm.state("C")], + ....: [fsm.state("B"), fsm.state("D")]]) + sage: fsmq.transitions() + [Transition from ('A', 'C') + to ('B', 'D'): 0|1, + Transition from ('A', 'C') + to ('B', 'D'): 1|0, + Transition from ('B', 'D') + to ('A', 'C'): 0|0, + Transition from ('B', 'D') + to ('A', 'C'): 1|1] + sage: fsmq.relabeled().transitions() + [Transition from 0 to 1: 0|1, + Transition from 0 to 1: 1|0, + Transition from 1 to 0: 0|0, + Transition from 1 to 0: 1|1] + sage: fsmq1 = fsm.quotient(fsm.equivalence_classes()) + sage: fsmq1 == fsmq + True + sage: fsm.quotient([[fsm.state("A"), fsm.state("B"), fsm.state("C"), fsm.state("D")]]) + Traceback (most recent call last): + ... + ValueError: There is a transition Transition from 'B' to 'C': 0|0 in the original transducer, but no corresponding transition in the new transducer. + """ + new = self.empty_copy() + state_mapping = {} + + # Create new states and build state_mapping + for c in classes: + new_state = new.add_state(tuple(c)) + for state in c: + state_mapping[state] = new_state + + # Copy data from old transducer + for c in classes: + new_state = state_mapping[c[0]] + # copy all data from first class member + new_state.is_initial = c[0].is_initial + new_state.is_final = c[0].is_final + new_state.word_out = c[0].word_out + for transition in self.iter_transitions(c[0]): + new.add_transition( + from_state=new_state, + to_state = state_mapping[transition.to_state], + word_in = transition.word_in, + word_out = transition.word_out) + + # check that all class members have the same information (modulo classes) + for state in c: + new_state.is_initial = new_state.is_initial or state.is_initial + assert new_state.is_final == state.is_final, "Class %s mixes final and non-final states" % (c,) + assert new_state.word_out == state.word_out, "Class %s mixes different word_out" % (c,) + assert len(self.transitions(state)) == len(new.transitions(new_state)), \ + "Class %s has %d outgoing transitions, but class member %s has %d outgoing transitions" % \ + (c, len(new.transitions(new_state)), state, len(self.transitions(state))) + for transition in self.transitions(state): + try: + new.transition((new_state, state_mapping[transition.to_state], transition.word_in, transition.word_out)) + except LookupError: + raise ValueError, "There is a transition %s in the original transducer, but no corresponding transition in the new transducer." % transition + return new + + + # ************************************************************************* + # other + # ************************************************************************* + + + def graph(self, edge_labels='words_in_out'): + """ + Returns the graph of the finite state machine with labeled + vertices and labeled edges. + + INPUT: + + - ``edge_label``: (default: ``'words_in_out'``) can be + - ``'words_in_out'`` (labels will be strings ``'i|o'``) + - a function with which takes as input a transition + and outputs (returns) the label + + OUTPUT: + + A graph. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMState + sage: A = FSMState('A') + sage: T = Transducer() + sage: T.graph() + Digraph on 0 vertices + sage: T.add_state(A) + 'A' + sage: T.graph() + Digraph on 1 vertex + sage: T.add_transition(('A', 'A', 0, 1)) + Transition from 'A' to 'A': 0|1 + sage: T.graph() + Looped digraph on 1 vertex + """ + if edge_labels == 'words_in_out': + label_fct = lambda t:t._in_out_label_() + elif hasattr(edge_labels, '__call__'): + label_fct = edge_labels + else: + raise TypeError, 'Wrong argument for edge_labels.' + + graph_data = [] + isolated_vertices = [] + for state in self.iter_states(): + transitions = state.transitions + if len(transitions) == 0: + isolated_vertices.append(state.label()) + for t in transitions: + graph_data.append((t.from_state.label(), t.to_state.label(), + label_fct(t))) + + G = DiGraph(graph_data) + G.add_vertices(isolated_vertices) + return G + + + digraph = graph + + + def plot(self): + """ + Plots a graph of the finite state machine with labeled + vertices and labeled edges. + + INPUT: + + Nothing. + + OUTPUT: + + A plot of the graph of the finite state machine. + + TESTS:: + + sage: FiniteStateMachine([('A', 'A', 0)]).plot() + """ + return self.graph(edge_labels='words_in_out').plot() + + + def predecessors(self, state, valid_input=None): + """ + Lists all predecessors of a state. + + INPUT: + + - ``state`` -- the state from which the predecessors should be + listed. + + - ``valid_input`` -- If ``valid_input`` is a list, then we + only consider transitions whose input labels are contained + in ``valid_input``. ``state`` has to be a :class:`FSMState` + (not a label of a state). If input labels of length larger + than `1` are used, then ``valid_input`` has to be a list of + lists. + + OUTPUT: + + A list of states. + + EXAMPLES:: + + sage: A = Transducer([('I', 'A', 'a', 'b'), ('I', 'B', 'b', 'c'), + ....: ('I', 'C', 'c', 'a'), ('A', 'F', 'b', 'a'), + ....: ('B', 'F', ['c', 'b'], 'b'), ('C', 'F', 'a', 'c')], + ....: initial_states=['I'], final_states=['F']) + sage: A.predecessors(A.state('A')) + ['A', 'I'] + sage: A.predecessors(A.state('F'), valid_input=['b', 'a']) + ['F', 'C', 'A', 'I'] + sage: A.predecessors(A.state('F'), valid_input=[['c', 'b'], 'a']) + ['F', 'C', 'B'] + """ + if valid_input != None: + valid_list = list() + for input in valid_input: + input_list = input + if not isinstance(input_list, list): + input_list = [input] + valid_list.append(input_list) + valid_input = valid_list + + unhandeled_direct_predecessors = {s:[] for s in self.states() } + for t in self.transitions(): + if valid_input is None or t.word_in in valid_input: + unhandeled_direct_predecessors[t.to_state].append(t.from_state) + done = [] + open = [state] + while len(open) > 0: + s = open.pop() + candidates = unhandeled_direct_predecessors[s] + if candidates is not None: + open.extend(candidates) + unhandeled_direct_predecessors[s] = None + done.append(s) + return(done) + + +#***************************************************************************** + + +def is_Automaton(FSM): + """ + Tests whether or not ``FSM`` inherits from :class:`Automaton`. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import is_FiniteStateMachine, is_Automaton + sage: is_Automaton(FiniteStateMachine()) + False + sage: is_Automaton(Automaton()) + True + sage: is_FiniteStateMachine(Automaton()) + True + """ + return isinstance(FSM, Automaton) + + +class Automaton(FiniteStateMachine): + """ + This creates an automaton, which is a finite state machine, whose + transitions have input labels. + + An automaton has additional features like creating a deterministic + and a minimized automaton. + + See class :class:`FiniteStateMachine` for more information. + + EXAMPLES: + + We can create an automaton recognizing even numbers (given in + binary and read from left to right) in the following way:: + + sage: A = Automaton([('P', 'Q', 0), ('P', 'P', 1), + ....: ('Q', 'P', 1), ('Q', 'Q', 0)], + ....: initial_states=['P'], final_states=['Q']) + sage: A + Automaton with 2 states + sage: A([0])[0] + True + sage: A([1,1,0])[0] + True + sage: A([1,0,1])[0] + False + + Note that the full output of the commands above gives more + information and looks like this:: + + sage: A([1,0,1]) + (False, 'P', []) + + TESTS:: + + sage: Automaton() + Automaton with 0 states + """ + + def _repr_(self): + """ + Represents the finite state machine as "Automaton with n + states" where n is the number of states. + + INPUT: + + Nothing. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: Automaton()._repr_() + 'Automaton with 0 states' + """ + return "Automaton with %s states" % len(self._states_) + + def _latex_transition_label_(self, transition, format_function=latex): + r""" + Returns the proper transition label. + + INPUT: + + - ``transition`` - a transition + + - ``format_function'' - a function formatting the labels + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: F = Automaton([('A', 'B', 1)]) + sage: F._latex_() + '\\begin{tikzpicture}[auto]\n\\node[state] (v0) at (3.000000,0.000000) {\\text{\\texttt{A}}}\n;\\node[state] (v1) at (-3.000000,0.000000) {\\text{\\texttt{B}}}\n;\\path[->] (v0) edge node {$\\left[1\\right]$} (v1);\n\\end{tikzpicture}' + + TESTS:: + + sage: F = Automaton([('A', 'B', 0, 1)]) + sage: t = F.transitions()[0] + sage: F._latex_transition_label_(t) + \left[0\right] + """ + return format_function(transition.word_in) + + def determinisation(self): + """ + Returns a deterministic automaton which accepts the same input + words as the original one. + + INPUT: + + Nothing. + + OUTPUT: + + A new automaton, which is deterministic. + + The labels of the states of the new automaton are frozensets of + states of ``self``. + + The input alphabet must be specified. It is restricted to nice + cases: input words have to have length at most `1`. + + EXAMPLES:: + + sage: aut = Automaton([('A', 'A', 0), ('A', 'B', 1), ('B', 'B', 1)], + ....: initial_states=['A'], final_states=['B']) + sage: aut.determinisation().transitions() + [Transition from frozenset(['A']) + to frozenset(['A']): 0|-, + Transition from frozenset(['A']) + to frozenset(['B']): 1|-, + Transition from frozenset(['B']) + to frozenset([]): 0|-, + Transition from frozenset(['B']) + to frozenset(['B']): 1|-, + Transition from frozenset([]) + to frozenset([]): 0|-, + Transition from frozenset([]) + to frozenset([]): 1|-] + + :: + + sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1), + ....: ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)], + ....: initial_states=['A'], final_states=['C']) + sage: A.determinisation().states() + [frozenset(['A']), frozenset(['A', 'B']), + frozenset(['A', 'C']), frozenset(['A', 'C', 'B'])] + + TESTS: + + This is from #15078, comment 13. + + :: + + sage: D = {'A': [('A', 'a'), ('B', 'a'), ('A', 'b')], + ....: 'C': [], 'B': [('C', 'b')]} + sage: auto = Automaton(D, initial_states=['A'], final_states=['C']) + sage: auto.is_deterministic() + False + sage: auto.process(list('aaab')) + (False, 'A', []) + sage: auto.states() + ['A', 'C', 'B'] + sage: auto.determinisation() + Automaton with 3 states + """ + for transition in self.transitions(): + assert len(transition.word_in) <= 1, "%s has input label of length > 1, which we cannot handle" % (transition,) + + epsilon_successors = {} + direct_epsilon_successors = {} + for state in self.states(): + direct_epsilon_successors[state] = set(map(lambda t:t.to_state, + filter(lambda transition: len(transition.word_in) == 0, + self.transitions(state) + ) + ) + ) + epsilon_successors[state] = set([state]) + + old_count_epsilon_successors = 0 + count_epsilon_successors = len(epsilon_successors) + + while old_count_epsilon_successors < count_epsilon_successors: + old_count_epsilon_successors = count_epsilon_successors + count_epsilon_successors = 0 + for state in self.states(): + for direct_successor in direct_epsilon_successors[state]: + epsilon_successors[state] = epsilon_successors[state].union(epsilon_successors[direct_successor]) + count_epsilon_successors += len(epsilon_successors[state]) + + + def set_transition(states, letter): + result = set() + for state in states: + for transition in self.transitions(state): + if transition.word_in == [letter]: + result.add(transition.to_state) + result = result.union(*map(lambda s:epsilon_successors[s], result)) + return (frozenset(result), []) + + result = self.empty_copy() + new_initial_states = [frozenset([state for state in self.initial_states()])] + result.add_from_transition_function(set_transition, + initial_states=new_initial_states) + + for state in result.states(): + if any(map(lambda s: s.is_final, state.label())): + state.is_final = True + + + return result + + + def minimization(self, algorithm=None): + """ + Returns the minimization of the input automaton as a new automaton. + + INPUT: + + - ``algorithm`` -- Either Moore's algorithm is used (default + or ``algorithm='Moore'``), or Brzozowski's algorithm when + ``algorithm='Brzozowski'``. + + OUTPUT: + + A new automaton. + + The resulting automaton is deterministic and has a minimal + number of states. + + EXAMPLES:: + + sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1), + ....: ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)], + ....: initial_states=['A'], final_states=['C']) + sage: B = A.minimization(algorithm='Brzozowski') + sage: B.transitions(B.states()[1]) + [Transition from frozenset([frozenset(['A', 'C', 'B']), + frozenset(['C', 'B']), frozenset(['A', 'C'])]) to + frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']), + frozenset(['A', 'C']), frozenset(['C'])]): 0|-, + Transition from frozenset([frozenset(['A', 'C', 'B']), + frozenset(['C', 'B']), frozenset(['A', 'C'])]) to + frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']), + frozenset(['A', 'C'])]): 1|-] + sage: len(B.states()) + 3 + sage: C = A.minimization(algorithm='Brzozowski') + sage: C.transitions(C.states()[1]) + [Transition from frozenset([frozenset(['A', 'C', 'B']), + frozenset(['C', 'B']), frozenset(['A', 'C'])]) to + frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']), + frozenset(['A', 'C']), frozenset(['C'])]): 0|-, + Transition from frozenset([frozenset(['A', 'C', 'B']), + frozenset(['C', 'B']), frozenset(['A', 'C'])]) to + frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']), + frozenset(['A', 'C'])]): 1|-] + sage: len(C.states()) + 3 + + :: + + sage: aut = Automaton([('1', '2', 'a'), ('2', '3', 'b'), + ....: ('3', '2', 'a'), ('2', '1', 'b'), + ....: ('3', '4', 'a'), ('4', '3', 'b')], + ....: initial_states=['1'], final_states=['1']) + sage: min = aut.minimization(algorithm='Brzozowski') + sage: [len(min.states()), len(aut.states())] + [3, 4] + sage: min = aut.minimization(algorithm='Moore') + Traceback (most recent call last): + ... + NotImplementedError: Minimization via Moore's Algorithm is only + implemented for deterministic finite state machines + """ + if algorithm is None or algorithm == "Moore": + return self._minimization_Moore_() + elif algorithm == "Brzozowski": + return self._minimization_Brzozowski_() + else: + raise NotImplementedError, "Algorithm '%s' is not implemented. Choose 'Moore' or 'Brzozowski'" % algorithm + + + def _minimization_Brzozowski_(self): + """ + Returns a minimized automaton by using Brzozowski's algorithm. + + See also :meth:`.minimization`. + + TESTS:: + + sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1), + ....: ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)], + ....: initial_states=['A'], final_states=['C']) + sage: B = A._minimization_Brzozowski_() + sage: len(B.states()) + 3 + """ + return self.transposition().determinisation().transposition().determinisation() + + + def _minimization_Moore_(self): + """ + Returns a minimized automaton by using Brzozowski's algorithm. + + See also :meth:`.minimization`. + + TESTS:: + + sage: aut = Automaton([('1', '2', 'a'), ('2', '3', 'b'), + ....: ('3', '2', 'a'), ('2', '1', 'b'), + ....: ('3', '4', 'a'), ('4', '3', 'b')], + ....: initial_states=['1'], final_states=['1']) + sage: min = aut._minimization_Moore_() + Traceback (most recent call last): + ... + NotImplementedError: Minimization via Moore's Algorithm is only + implemented for deterministic finite state machines + """ + return self.quotient(self.equivalence_classes()) + + +#***************************************************************************** + + +def is_Transducer(FSM): + """ + Tests whether or not ``FSM`` inherits from :class:`Transducer`. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import is_FiniteStateMachine, is_Transducer + sage: is_Transducer(FiniteStateMachine()) + False + sage: is_Transducer(Transducer()) + True + sage: is_FiniteStateMachine(Transducer()) + True + """ + return isinstance(FSM, Transducer) + + +class Transducer(FiniteStateMachine): + """ + This creates a transducer, which is a finite state machine, whose + transitions have input and output labels. + + An transducer has additional features like creating a simplified + transducer. + + See class :class:`FiniteStateMachine` for more information. + + EXAMPLES: + + We can create a transducer performing the addition of 1 (for + numbers given in binary and read from right to left) in the + following way:: + + sage: T = Transducer([('C', 'C', 1, 0), ('C', 'N', 0, 1), + ....: ('N', 'N', 0, 0), ('N', 'N', 1, 1)], + ....: initial_states=['C'], final_states=['N']) + sage: T + Transducer with 2 states + sage: T([0]) + (True, 'N', [1]) + sage: T([1,1,0]) + (True, 'N', [0, 0, 1]) + sage: ZZ(T(15.digits(base=2)+[0])[2], base=2) + 16 + + Note that we have padded the binary input sequence by a `0` so + that the transducer can reach its final state. + + TESTS:: + + sage: Transducer() + Transducer with 0 states + """ + + def _repr_(self): + """ + Represents the transducer as "Transducer with n states" where + n is the number of states. + + INPUT: + + Nothing. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: Transducer()._repr_() + 'Transducer with 0 states' + """ + return "Transducer with %s states" % len(self._states_) + + def _latex_transition_label_(self, transition, format_function=latex): + r""" + Returns the proper transition label. + + INPUT: + + - ``transition`` - a transition + + - ``format_function'' - a function formatting the labels + + OUTPUT: + + A string. + + sage: F = Transducer([('A', 'B', 1, 2)]) + sage: F._latex_() + '\\begin{tikzpicture}[auto]\n\\node[state] (v0) at (3.000000,0.000000) {\\text{\\texttt{A}}}\n;\\node[state] (v1) at (-3.000000,0.000000) {\\text{\\texttt{B}}}\n;\\path[->] (v0) edge node {$\\left[1\\right] \\mid \\left[2\\right]$} (v1);\n\\end{tikzpicture}' + + TESTS:: + + sage: F = Transducer([('A', 'B', 0, 1)]) + sage: t = F.transitions()[0] + sage: F._latex_transition_label_(t) + \left[0\right] \mid \left[1\right] + """ + return (format_function(transition.word_in) + "\\mid" + + format_function(transition.word_out)) + + + def simplification(self): + """ + Returns a simplified transducer. + + INPUT: + + Nothing. + + OUTPUT: + + A new transducer. + + This function simplifies a transducer by Moore's algorithm, + first moving common output labels of transitions leaving a + state to output labels of transitions entering the state + (cf. :meth:`.prepone_output`). + + The resulting transducer implements the same function as the + original transducer. + + EXAMPLES:: + + sage: fsm = Transducer([("A", "B", 0, 1), ("A", "B", 1, 0), + ....: ("B", "C", 0, 0), ("B", "C", 1, 1), + ....: ("C", "D", 0, 1), ("C", "D", 1, 0), + ....: ("D", "A", 0, 0), ("D", "A", 1, 1)]) + sage: fsms = fsm.simplification() + sage: fsms + Transducer with 2 states + sage: fsms.transitions() + [Transition from ('A', 'C') + to ('B', 'D'): 0|1, + Transition from ('A', 'C') + to ('B', 'D'): 1|0, + Transition from ('B', 'D') + to ('A', 'C'): 0|0, + Transition from ('B', 'D') + to ('A', 'C'): 1|1] + sage: fsms.relabeled().transitions() + [Transition from 0 to 1: 0|1, + Transition from 0 to 1: 1|0, + Transition from 1 to 0: 0|0, + Transition from 1 to 0: 1|1] + """ + fsm = deepcopy(self) + fsm.prepone_output() + return fsm.quotient(fsm.equivalence_classes()) + + +#***************************************************************************** + + +def is_FSMProcessIterator(PI): + """ + Tests whether or not ``PI`` inherits from :class:`FSMProcessIterator`. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import is_FSMProcessIterator, FSMProcessIterator + sage: is_FSMProcessIterator(FSMProcessIterator(FiniteStateMachine())) + Traceback (most recent call last): + ... + ValueError: No state is initial. + """ + return isinstance(PI, FSMProcessIterator) + + +class FSMProcessIterator: + """ + This class is for processing an input string on a finite state + machine. + + An instance of this class is generated when + :meth:`FiniteStateMachine.process` or + :meth:`FiniteStateMachine.iter_process` of the finite state + machine is invoked. It behaves like an iterator which, in each + step, takes one letter of the input and runs (one step on) the + finite state machine with this input. More precisely, in each + step, the process iterator takes an outgoing transition of the + current state, whose input label equals the input letter of the + tape. The output label of the transition, if present, is written + on the output tape. + + INPUT: + + - ``fsm`` -- The finite state machine on which the input should be + processed. + + - ``input_tape`` -- The input tape. It can be anything that is + iterable. + + - ``initial_state`` -- The initial state in which the machine + starts. If this is ``None``, the unique inital state of the + finite state machine is takes. If there are several, an error is + reported. + + The process (iteration) stops if there are no more input letters + on the tape. In this case a StopIteration exception is thrown. As + result the following attributes are available: + + - ``accept_input`` -- Is True if the reached state is a final state. + + - ``current_state`` -- The current/reached state in the process. + + - ``output_tape`` -- The written output. + + Current values of those attribtes (except ``accept_input``) are + (also) available during the iteration. + + OUTPUT: + + An iterator. + + EXAMPLES: + + The following transducer reads binary words and outputs a word, + where blocks of ones are replaced by just a single one. Further + only words that end with a zero are accepted. + + :: + + sage: T = Transducer({'A': [('A', 0, 0), ('B', 1, None)], + ....: 'B': [('B', 1, None), ('A', 0, [1, 0])]}, + ....: initial_states=['A'], final_states=['A']) + sage: input = [1, 1, 0, 0, 1, 0, 1, 1, 1, 0] + sage: T.process(input) + (True, 'A', [1, 0, 0, 1, 0, 1, 0]) + + The function :meth:`FiniteStateMachine.process` created a new + ``FSMProcessIterator``. We can do that manually, too, and get full + access to the iteration process:: + + sage: from sage.combinat.finite_state_machine import FSMProcessIterator + sage: it = FSMProcessIterator(T, input_tape=input) + sage: for _ in it: + ....: print (it.current_state, it.output_tape) + ('B', []) + ('B', []) + ('A', [1, 0]) + ('A', [1, 0, 0]) + ('B', [1, 0, 0]) + ('A', [1, 0, 0, 1, 0]) + ('B', [1, 0, 0, 1, 0]) + ('B', [1, 0, 0, 1, 0]) + ('B', [1, 0, 0, 1, 0]) + ('A', [1, 0, 0, 1, 0, 1, 0]) + sage: it.accept_input + True + """ + def __init__(self, fsm, input_tape=None, initial_state=None): + """ + See :class:`FSMProcessIterator` for more information. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMProcessIterator + sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, + ....: initial_states=['A'], final_states=['A']) + sage: it = FSMProcessIterator(inverter, input_tape=[0, 1]) + sage: for _ in it: + ....: pass + sage: it.output_tape + [1, 0] + """ + self.fsm = fsm + if initial_state is None: + fsm_initial_states = self.fsm.initial_states() + try: + self.current_state = fsm_initial_states[0] + except IndexError: + raise ValueError, "No state is initial." + if len(fsm_initial_states) > 1: + raise ValueError, "Several initial states." + else: + self.current_state = initial_state + self.output_tape = [] + if input_tape is None: + self._input_tape_iter_ = iter([]) + else: + if hasattr(input_tape, '__iter__'): + self._input_tape_iter_ = iter(input_tape) + else: + raise ValueError, "Given input tape is not iterable." + + def __iter__(self): + """ + Returns ``self``. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import FSMProcessIterator + sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, + ....: initial_states=['A'], final_states=['A']) + sage: it = FSMProcessIterator(inverter, input_tape=[0, 1]) + sage: id(it) == id(iter(it)) + True + """ + return self + + def next(self): + """ + Makes one step in processing the input tape. + + INPUT: + + Nothing. + + OUTPUT: + + It returns the taken transition. A ``StopIteration`` exception is + thrown when there is nothing more to read. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMProcessIterator + sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, + ....: initial_states=['A'], final_states=['A']) + sage: it = FSMProcessIterator(inverter, input_tape=[0, 1]) + sage: it.next() + Transition from 'A' to 'A': 0|1 + sage: it.next() + Transition from 'A' to 'A': 1|0 + sage: it.next() + Traceback (most recent call last): + ... + StopIteration + """ + if hasattr(self, 'accept_input'): + raise StopIteration + try: + # process current state + transition = None + try: + transition = self.current_state.hook( + self.current_state, self) + except AttributeError: + pass + self.write_word(self.current_state.word_out) + + # get next + if not isinstance(transition, FSMTransition): + next_word = [] + found = False + + try: + while not found: + next_word.append(self.read_letter()) + try: + transition = self.get_next_transition( + next_word) + found = True + except ValueError: + pass + except StopIteration: + # this means input tape is finished + if len(next_word) > 0: + self.accept_input = False + raise StopIteration + + # process transition + try: + transition.hook(transition, self) + except AttributeError: + pass + self.write_word(transition.word_out) + + # go to next state + self.current_state = transition.to_state + + except StopIteration: + # this means, either input tape is finished or + # someone has thrown StopIteration manually (in one + # of the hooks) + if not self.current_state.is_final: + self.accept_input = False + if not hasattr(self, 'accept_input'): + self.accept_input = True + raise StopIteration + + # return + return transition + + def read_letter(self): + """ + Reads a letter from the input tape. + + INPUT: + + Nothing. + + OUTPUT: + + A letter. + + Exception ``StopIteration`` is thrown if tape has reached + the end. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMProcessIterator + sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, + ....: initial_states=['A'], final_states=['A']) + sage: it = FSMProcessIterator(inverter, input_tape=[0, 1]) + sage: it.read_letter() + 0 + """ + return self._input_tape_iter_.next() + + def write_letter(self, letter): + """ + Writes a letter on the output tape. + + INPUT: + + - ``letter`` -- the letter to be written. + + OUTPUT: + + Nothing. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMProcessIterator + sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, + ....: initial_states=['A'], final_states=['A']) + sage: it = FSMProcessIterator(inverter, input_tape=[0, 1]) + sage: it.write_letter(42) + sage: it.output_tape + [42] + """ + self.output_tape.append(letter) + + def write_word(self, word): + """ + Writes a word on the output tape. + + INPUT: + + - ``word`` -- the word to be written. + + OUTPUT: + + Nothing. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMProcessIterator + sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, + ....: initial_states=['A'], final_states=['A']) + sage: it = FSMProcessIterator(inverter, input_tape=[0, 1]) + sage: it.write_word([4, 2]) + sage: it.output_tape + [4, 2] + """ + for letter in word: + self.write_letter(letter) + + def get_next_transition(self, word_in): + """ + Returns the next transition according to ``word_in``. It is + assumed that we are in state ``self.current_state``. + + INPUT: + + - ``word_in`` -- the input word. + + OUTPUT: + + The next transition according to ``word_in``. It is assumed + that we are in state ``self.current_state``. + + EXAMPLES:: + + sage: from sage.combinat.finite_state_machine import FSMProcessIterator + sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]}, + ....: initial_states=['A'], final_states=['A']) + sage: it = FSMProcessIterator(inverter, input_tape=[0, 1]) + sage: it.get_next_transition([0]) + Transition from 'A' to 'A': 0|1 + """ + for transition in self.current_state.transitions: + if transition.word_in == word_in: + return transition + raise ValueError + + +#***************************************************************************** + + +def setup_latex_preamble(): + """ + This function adds the package ``tikz`` with support for automata + to the preamble of Latex so that the finite state machines can be + drawn nicely. + + INPUT: + + Nothing. + + OUTPUT: + + Nothing. + + TESTS:: + + sage: from sage.combinat.finite_state_machine import setup_latex_preamble + sage: setup_latex_preamble() + """ + latex.add_package_to_preamble_if_available('tikz') + latex.add_to_preamble('\\usetikzlibrary{automata}') + + +#***************************************************************************** From 893df8993ac3dc3034949f581f25f1004f631ddb Mon Sep 17 00:00:00 2001 From: Frederic Chapoton Date: Fri, 4 Oct 2013 20:57:49 +0200 Subject: [PATCH 124/206] Trac #8793: minor clean of logic/boolformula.py --- src/doc/en/reference/logic/index.rst | 1 + src/sage/logic/boolformula.py | 90 ++++++++++++++-------------- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/doc/en/reference/logic/index.rst b/src/doc/en/reference/logic/index.rst index b032444be24..4632443e325 100644 --- a/src/doc/en/reference/logic/index.rst +++ b/src/doc/en/reference/logic/index.rst @@ -5,6 +5,7 @@ Symbolic Logic :maxdepth: 2 sage/logic/propcalc + sage/logic/boolformula sage/logic/booleval .. include:: ../footer.txt diff --git a/src/sage/logic/boolformula.py b/src/sage/logic/boolformula.py index b20057cb741..27c8837b0ea 100644 --- a/src/sage/logic/boolformula.py +++ b/src/sage/logic/boolformula.py @@ -1,18 +1,11 @@ r""" Module that creates boolean formulas as instances of the BooleanFormula class. -Formulas consist of the operators &, |, ~, ^, ->, <->, corresponding -to and, or, not, xor, if...then, if and only if. Operators can -be applied to variables that consist of a leading letter and trailing -underscores and alphanumerics. Parentheses may be used to -explicitly show order of operation. - -AUTHORS: - -- Chris Gorecki (2006): initial version - -- Paul Scurek (2013-08-03): added polish_notation, full_tree, - updated docstring formatting +Formulas consist of the operators ``&``, ``|``, ``~``, ``^``, ``->``, ``<->``, +corresponding to ``and``, ``or``, ``not``, ``xor``, ``if...then``, ``if and +only if``. Operators can be applied to variables that consist of a leading +letter and trailing underscores and alphanumerics. Parentheses may be used +to explicitly show order of operation. EXAMPLES: @@ -118,6 +111,12 @@ ... NameError: invalid variable name 9b: identifiers must begin with a letter and contain only alphanumerics and underscores +AUTHORS: + +- Chris Gorecki (2006): initial version + +- Paul Scurek (2013-08-03): added polish_notation, full_tree, + updated docstring formatting """ #***************************************************************************** # Copyright (C) 2006 William Stein @@ -134,7 +133,7 @@ import logictable import logicparser # import boolopt -from types import * +from types import TupleType, ListType from sage.misc.flatten import flatten latex_operators = [('&', '\\wedge '), @@ -144,6 +143,7 @@ ('<->', '\\leftrightarrow '), ('->', '\\rightarrow ')] + class BooleanFormula: __expression = "" __tree = [] @@ -630,7 +630,7 @@ def __eq__(self, other): """ return self.equivalent(other) - def truthtable(self, start = 0, end = -1): + def truthtable(self, start=0, end=-1): r""" Return a truth table for the calling formula. @@ -788,7 +788,7 @@ def is_satisfiable(self): """ table = self.truthtable().get_table_list() for row in table[1:]: - if row[-1] == True: + if row[-1] is True: return True return False @@ -925,7 +925,7 @@ def convert_cnf_table(self): An instance of :class:`BooleanFormula` in conjunctive normal form - EXAMPLES:: + EXAMPLES: This example illustrates how to convert a formula to cnf. @@ -958,10 +958,10 @@ def convert_cnf_table(self): table = t.get_table_list() vars = table[0] for row in table[1:]: - if row[-1] == False: + if row[-1] is False: str += '(' for i in range(len(row) - 1): - if row[i] == True: + if row[i] is True: str += '~' str += vars[i] str += '|' @@ -998,7 +998,7 @@ def convert_cnf_recur(self): sage: s (~a|a|c)&(~b|a|c)&(~a|b|c)&(~b|b|c)&(~c|a|b)&(~c|~a|~b) - .. NOTES:: + .. NOTE:: This function works by applying a set of rules that are guaranteed to convert the formula. Worst case the converted @@ -1041,7 +1041,7 @@ def satformat(self): sage: f.satformat() 'p cnf 3 0\n1 -2 3 0 1 -2 -3 \n0 -1 2 -3' - .. NOTES:: + .. NOTE:: See www.cs.ubc.ca/~hoos/SATLIB/Benchmarks/SAT/satformat.ps for a description of satformat. @@ -1166,11 +1166,12 @@ def convert_opt(self, tree): OUTPUT: - A 3-tupple + A 3-tuple EXAMPLES: - This example illustrates the conersion of a formula into its corresponding tupple. + This example illustrates the conversion of a formula into its + corresponding tuple. :: @@ -1180,17 +1181,17 @@ def convert_opt(self, tree): sage: logicparser.apply_func(tree, s.convert_opt) ('and', ('prop', 'a'), ('or', ('prop', 'b'), ('not', ('prop', 'c')))) - .. NOTES:: + .. NOTE:: - This function only works on one branch of the parse tree. to + This function only works on one branch of the parse tree. To apply the function to every branch of a parse tree, pass the function as an argument in :func:`apply_func` in logicparser.py. """ - if type(tree[1]) is not TupleType and tree[1] != None: + if type(tree[1]) is not TupleType and not (tree[1] is None): lval = ('prop', tree[1]) else: lval = tree[1] - if type(tree[2]) is not TupleType and tree[2] != None: + if type(tree[2]) is not TupleType and not(tree[2] is None): rval = ('prop', tree[2]) else: rval = tree[2] @@ -1204,7 +1205,7 @@ def convert_opt(self, tree): def add_statement(self, other, op): r""" - Combine two formulas with the give operator. + Combine two formulas with the given operator. INPUT: @@ -1295,7 +1296,7 @@ def get_bit(self, x, c): sage: s.get_bit(64, -2) False - .. NOTES:: + .. NOTE:: The 0 bit is the low order bit. Errors should be handled gracefully by a return of false, and negative numbers x @@ -1343,7 +1344,7 @@ def reduce_op(self, tree): sage: logicparser.apply_func(tree, s.reduce_op) ['|', ['~', 'a', None], ['&', ['|', 'b', 'c'], ['~', ['&', 'b', 'c'], None]]] - .. NOTES:: + .. NOTE:: This function only operates on a single branch of a parse tree. To apply the function to an entire parse tree, pass the function @@ -1351,11 +1352,11 @@ def reduce_op(self, tree): """ if tree[0] == '<->': # parse tree for (~tree[1]|tree[2])&(~tree[2]|tree[1]) - new_tree = ['&', ['|', ['~', tree[1], None], tree[2]], \ + new_tree = ['&', ['|', ['~', tree[1], None], tree[2]], ['|', ['~', tree[2], None], tree[1]]] elif tree[0] == '^': # parse tree for (tree[1]|tree[2])&~(tree[1]&tree[2]) - new_tree = ['&', ['|', tree[1], tree[2]], \ + new_tree = ['&', ['|', tree[1], tree[2]], ['~', ['&', tree[1], tree[2]], None]] elif tree[0] == '->': # parse tree for ~tree[1]|tree[2] @@ -1391,7 +1392,7 @@ def dist_not(self, tree): sage: logicparser.apply_func(tree, s.dist_not) #long time ['|', ['~', 'a', None], ['~', 'b', None]] - .. NOTES:: + .. NOTE:: This function only operates on a single branch of a parse tree. To apply the function to an entire parse tree, pass the function @@ -1420,7 +1421,7 @@ def dist_ors(self, tree): - ``self`` -- calling object - _ ``tree`` -- a list. This represents a branch of + - ``tree`` -- a list. This represents a branch of a parse tree. OUTPUT: @@ -1439,18 +1440,20 @@ def dist_ors(self, tree): sage: logicparser.apply_func(tree, s.dist_ors) #long time ['&', ['&', ['|', 'a', 'a'], ['|', 'b', 'a']], ['&', ['|', 'a', 'c'], ['|', 'b', 'c']]] - .. NOTES:: + .. NOTE:: This function only operates on a single branch of a parse tree. To apply the function to an entire parse tree, pass the function as an argument to :func:`apply_func` in logicparser.py. """ if tree[0] == '|' and type(tree[2]) is ListType and tree[2][0] == '&': - new_tree = ['&', ['|', tree[1], tree[2][1]], ['|', tree[1], tree[2][2]]] + new_tree = ['&', ['|', tree[1], tree[2][1]], + ['|', tree[1], tree[2][2]]] return logicparser.apply_func(new_tree, self.dist_ors) if tree[0] == '|' and type(tree[1]) is ListType and tree[1][0] == '&': - new_tree = ['&', ['|', tree[1][1], tree[2]], ['|', tree[1][2], tree[2]]] - return logicparser.apply_func(new_tree, self.dist_ors) + new_tree = ['&', ['|', tree[1][1], tree[2]], + ['|', tree[1][2], tree[2]]] + return logicparser.apply_func(new_tree, self.dist_ors) return tree def to_infix(self, tree): @@ -1461,7 +1464,7 @@ def to_infix(self, tree): - ``self`` -- calling object - _ ``tree`` -- a list. This represents a branch + - ``tree`` -- a list. This represents a branch of a parse tree. OUTPUT: @@ -1480,7 +1483,7 @@ def to_infix(self, tree): sage: logicparser.apply_func(tree, s.to_infix) [['a', '&', 'b'], '|', ['a', '&', 'c']] - .. NOTES:: + .. NOTE:: This function only operates on a single branch of a parse tree. To apply the function to an entire parse tree, pass the function @@ -1496,7 +1499,7 @@ def convert_expression(self): INPUT: - -- ``self`` -- calling object + - ``self`` -- calling object OUTPUT: @@ -1518,7 +1521,6 @@ def convert_expression(self): self.__expression = '' str_tree = str(ttree) open_flag = False - put_flag = False i = 0 for c in str_tree: if i < len(str_tree) - 1: @@ -1534,7 +1536,7 @@ def convert_expression(self): if i < len(str_tree) and str_tree[i] not in ' \',[]': self.__expression += str_tree[i] i += 1 - if open_flag == True: + if open_flag is True: self.__expression += ')' def get_next_op(self, str): @@ -1545,7 +1547,7 @@ def get_next_op(self, str): - ``self`` -- calling object - _ ``str`` -- a string. This contains a logical + - ``str`` -- a string. This contains a logical expression. OUTPUT: @@ -1563,7 +1565,7 @@ def get_next_op(self, str): sage: s.get_next_op("abra|cadabra") '|' - .. NOTES:: + .. NOTE:: The parameter ``str`` is not necessarily the string representation of the calling object. From 1331eba6caa5107cd48b243494652b29f453e939 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 25 Nov 2013 22:57:34 +0100 Subject: [PATCH 125/206] Trac #15453: Wrap IML calls in sig_on() --- src/sage/matrix/matrix_integer_dense.pyx | 79 +++++++++++++++++------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 23aeae9680d..ea697552364 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -3697,6 +3697,36 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse sage: A*C == d*B True + Test wrong dimensions:: + + sage: A = random_matrix(ZZ, 4, 4) + sage: B = random_matrix(ZZ, 2, 3) + sage: B._solve_iml(A) + Traceback (most recent call last): + ... + ValueError: self must be a square matrix + sage: A._solve_iml(B, right=False) + Traceback (most recent call last): + ... + ArithmeticError: B's number of columns must match self's number of rows + sage: A._solve_iml(B, right=True) + Traceback (most recent call last): + ... + ArithmeticError: B's number of rows must match self's number of columns + + Check that this can be interrupted properly (:trac:`15453`):: + + sage: A = random_matrix(ZZ, 2000, 2000) + sage: B = random_matrix(ZZ, 2000, 2000) + sage: t0 = walltime() + sage: alarm(2); A._solve_iml(B) # long time + Traceback (most recent call last): + ... + AlarmInterrupt + sage: t = walltime(t0) + sage: t < 10 or t + True + ALGORITHM: Uses IML. AUTHORS: @@ -3710,11 +3740,12 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse if self._nrows != self._ncols: # This is *required* by the IML function we call below. - raise ArithmeticError("self must be a square matrix") + raise ValueError("self must be a square matrix") if self.nrows() == 1: return B, self[0,0] + cdef SOLU_POS solu_pos if right: if self._ncols != B._nrows: @@ -3722,6 +3753,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse n = self._ncols m = B._ncols + P = self.matrix_space(n, m) if self._nrows == 0 or self._ncols == 0: return P.zero_matrix(), Integer(1) @@ -3729,12 +3761,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse if m == 0 or n == 0: return self.new_matrix(nrows = n, ncols = m), Integer(1) - mpz_init(mp_D) - mp_N = sage_malloc( n * m * sizeof(mpz_t) ) - for i from 0 <= i < n * m: - mpz_init( mp_N[i] ) - - nonsingSolvLlhsMM(RightSolu, n, m, self._entries, B._entries, mp_N, mp_D) + solu_pos = RightSolu else: # left if self._nrows != B._ncols: @@ -3750,26 +3777,34 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse if m == 0 or n == 0: return self.new_matrix(nrows = m, ncols = n), Integer(1) - mpz_init(mp_D) - mp_N = sage_malloc( n * m * sizeof(mpz_t) ) - for i from 0 <= i < n * m: - mpz_init( mp_N[i] ) + solu_pos = LeftSolu - nonsingSolvLlhsMM(LeftSolu, n, m, self._entries, B._entries, mp_N, mp_D) + sig_check() + mp_N = sage_malloc( n * m * sizeof(mpz_t) ) + for i from 0 <= i < n * m: + mpz_init(mp_N[i]) + mpz_init(mp_D) - M = Matrix_integer_dense.__new__(Matrix_integer_dense, P, None, None, None) - for i from 0 <= i < n*m: - mpz_init_set(M._entries[i], mp_N[i]) - mpz_clear(mp_N[i]) - sage_free(mp_N) - M._initialized = True + try: + sig_on() + nonsingSolvLlhsMM(solu_pos, n, m, self._entries, B._entries, mp_N, mp_D) + sig_off() + + M = Matrix_integer_dense.__new__(Matrix_integer_dense, P, None, None, None) + for i from 0 <= i < n*m: + mpz_init_set(M._entries[i], mp_N[i]) + M._initialized = True - D = PY_NEW(Integer) - mpz_set(D.value, mp_D) - mpz_clear(mp_D) + D = PY_NEW(Integer) + mpz_set(D.value, mp_D) - return M,D + return M, D + finally: + mpz_clear(mp_D) + for i from 0 <= i < n*m: + mpz_clear(mp_N[i]) + sage_free(mp_N) def _rational_echelon_via_solve(self): r""" From 259a37a7e5281d73630e852af3da004e667b8f6d Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 25 Nov 2013 18:05:03 +0000 Subject: [PATCH 126/206] Trac #11868: eliminate global GEN variables --- src/sage/libs/pari/gen.pxd | 4 - src/sage/libs/pari/gen.pyx | 1004 ++++++++--------- .../rings/finite_rings/element_pari_ffelt.pyx | 2 +- 3 files changed, 459 insertions(+), 551 deletions(-) diff --git a/src/sage/libs/pari/gen.pxd b/src/sage/libs/pari/gen.pxd index e2999502753..c53d3b50b73 100644 --- a/src/sage/libs/pari/gen.pxd +++ b/src/sage/libs/pari/gen.pxd @@ -14,12 +14,10 @@ cdef class gen(sage.structure.element.RingElement): cdef gen pari(self, object x) cdef GEN _deepcopy_to_python_heap(self, GEN x, pari_sp* address) cdef long get_var(self, v) - cdef GEN get_nf(self) except NULL cdef class PariInstance(sage.structure.parent_base.ParentWithBase): cdef gen PARI_ZERO, PARI_ONE, PARI_TWO cdef gen new_gen(self, GEN x) - cdef object new_gen_to_string(self, GEN x) cdef gen new_gen_noclear(self, GEN x) cdef gen new_gen_from_mpz_t(self, mpz_t value) cdef inline GEN _new_GEN_from_mpz_t(self, mpz_t value) @@ -29,14 +27,12 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): cdef gen new_t_POL_from_int_star(self, int *vals, int length, long varnum) cdef gen new_gen_from_padic(self, long ordp, long relprec, mpz_t prime, mpz_t p_pow, mpz_t unit) cdef void clear_stack(self) - cdef void set_mytop_to_avma(self) cdef gen double_to_gen_c(self, double) cdef GEN double_to_GEN(self, double) cdef GEN deepcopy_to_python_heap(self, GEN x, pari_sp* address) cdef gen new_ref(self, GEN g, gen parent) cdef gen _empty_vector(self, long n) cdef long get_var(self, v) - cdef GEN toGEN(self, x, int i) except NULL cdef GEN _new_GEN_from_mpz_t_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc) cdef GEN _new_GEN_from_mpz_t_matrix_rotate90(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc) cdef gen integer_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc, bint permute_for_hnf) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 89a06f3ed30..c662ad8f7da 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -193,11 +193,6 @@ cdef extern from "misc.h": cdef extern from "mpz_pylong.h": cdef int mpz_set_pylong(mpz_t dst, src) except -1 -# Make sure we don't use mpz_t_offset before initializing it by -# putting in a value that's likely to provoke a segmentation fault, -# rather than silently corrupting memory. -cdef long mpz_t_offset = 1000000000 - # so Galois groups are represented in a sane way # See the polgalois section of the PARI users manual. new_galois_format = 1 @@ -380,30 +375,6 @@ pari_catch_sig_off() # Also a copy of PARI accessible from external pure python code. pari = pari_instance -# temp variables -cdef GEN t0,t1,t2,t3,t4 -t0heap = [0]*5 - -cdef t0GEN(x): - global t0 - t0 = P.toGEN(x, 0) - -cdef t1GEN(x): - global t1 - t1 = P.toGEN(x, 1) - -cdef t2GEN(x): - global t2 - t2 = P.toGEN(x, 2) - -cdef t3GEN(x): - global t3 - t3 = P.toGEN(x, 3) - -cdef t4GEN(x): - global t4 - t4 = P.toGEN(x, 4) - cdef object Integer cdef void late_import(): @@ -415,8 +386,6 @@ cdef void late_import(): import sage.rings.integer Integer = sage.rings.integer.Integer - global mpz_t_offset - mpz_t_offset = sage.rings.integer.mpz_t_offset_python cdef class gen(sage.structure.element.RingElement): """ @@ -446,8 +415,14 @@ cdef class gen(sage.structure.element.RingElement): sage_free( self.b) def __repr__(self): + cdef char *c + cdef str s pari_catch_sig_on() - return P.new_gen_to_string(self.g) + c = GENtostr(self.g) + s = str(c) + pari_free(c) + pari_catch_sig_off() + return s def __hash__(self): """ @@ -685,15 +660,10 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(gmod(selfgen.g, othergen.g)) return sage.structure.element.bin_op(self, other, operator.mod) - def __pow__(self, n, m): - t0GEN(self) - t1GEN(n) + def __pow__(gen self, n, m): + cdef gen t0 = P(n) pari_catch_sig_on() - # Note: the prec parameter here has no effect when t0,t1 are - # real; the precision of the result is the minimum of the - # precisions of t0 and t1. In any case the 3rd parameter to - # gpow should be a word-precision, not a decimal precision. - return P.new_gen(gpow(t0, t1, prec)) + return P.new_gen(gpow(self.g, t0.g, prec)) def __neg__(gen self): pari_catch_sig_on() @@ -719,9 +689,7 @@ cdef class gen(sage.structure.element.RingElement): # ACCESS ########################################### def getattr(self, attr): - t0GEN(str(self) + '.' + str(attr)) - pari_catch_sig_on() - return self.new_gen(t0) + return P(str(self) + '.' + str(attr)) def mod(self): """ @@ -746,50 +714,6 @@ cdef class gen(sage.structure.element.RingElement): # stored. return self.new_gen(gel(self.g, 1)) - cdef GEN get_nf(self) except NULL: - """ - Given a PARI object `self`, convert it to a proper PARI number - field (nf) structure. - - INPUT: - - - ``self`` -- A PARI number field being the output of ``nfinit()``, - ``bnfinit()`` or ``bnrinit()``. - - TESTS: - - We test this indirectly through `nf_get_pol()`:: - - sage: x = polygen(QQ) - sage: K. = NumberField(x^4 - 4*x^2 + 1) - sage: K.pari_nf().nf_get_pol() - y^4 - 4*y^2 + 1 - sage: K.pari_bnf().nf_get_pol() - y^4 - 4*y^2 + 1 - sage: bnr = pari("K = bnfinit(x^4 - 4*x^2 + 1); bnrinit(K, 2*x)") - sage: bnr.nf_get_pol() - x^4 - 4*x^2 + 1 - - It does not work with ``rnfinit()`` or garbage input:: - - sage: K.extension(x^2 - 5, 'b').pari_rnf().nf_get_pol() - Traceback (most recent call last): - ... - TypeError: Not a PARI number field - sage: pari("[0]").nf_get_pol() - Traceback (most recent call last): - ... - TypeError: Not a PARI number field - """ - cdef GEN nf - cdef long nftyp - pari_catch_sig_on() - nf = get_nf(self.g, &nftyp) - pari_catch_sig_off() - if not nf: - raise TypeError("Not a PARI number field") - return nf - def nf_get_pol(self): """ Returns the defining polynomial of this number field. @@ -808,20 +732,33 @@ cdef class gen(sage.structure.element.RingElement): sage: bnr.nf_get_pol() x^4 - 4*x^2 + 1 - For relative extensions, we can only get the absolute polynomial, + For relative extensions, this returns the absolute polynomial, not the relative one:: sage: L. = K.extension(x^2 - 5) sage: pari(L).nf_get_pol() # Absolute polynomial y^8 - 28*y^6 + 208*y^4 - 408*y^2 + 36 sage: L.pari_rnf().nf_get_pol() + x^8 - 28*x^6 + 208*x^4 - 408*x^2 + 36 + + TESTS:: + + sage: x = polygen(QQ) + sage: K. = NumberField(x^4 - 4*x^2 + 1) + sage: K.pari_nf().nf_get_pol() + y^4 - 4*y^2 + 1 + sage: K.pari_bnf().nf_get_pol() + y^4 - 4*y^2 + 1 + + An error is raised for invalid input:: + + sage: pari("[0]").nf_get_pol() Traceback (most recent call last): ... - TypeError: Not a PARI number field + PariError: incorrect type in pol """ - cdef GEN nf = self.get_nf() pari_catch_sig_on() - return self.new_gen(nf_get_pol(nf)) + return P.new_gen(member_pol(self.g)) def nf_get_diff(self): """ @@ -838,10 +775,8 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(K).nf_get_diff() [12, 0, 0, 0; 0, 12, 8, 0; 0, 0, 4, 0; 0, 0, 0, 4] """ - cdef GEN nf = self.get_nf() pari_catch_sig_on() - # Very bad code, but there doesn't seem to be a better way - return self.new_gen(gel(gel(nf, 5), 5)) + return P.new_gen(member_diff(self.g)) def nf_get_sign(self): """ @@ -867,8 +802,12 @@ cdef class gen(sage.structure.element.RingElement): """ cdef long r1 cdef long r2 - cdef GEN nf = self.get_nf() - nf_get_sign(nf, &r1, &r2) + cdef GEN sign + pari_catch_sig_on() + sign = member_sign(self.g) + r1 = itos(gel(sign, 1)) + r2 = itos(gel(sign, 2)) + pari_catch_sig_off() return [r1, r2] def nf_get_zk(self): @@ -887,9 +826,8 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(K).nf_get_zk() [1, y, y^3 - 4*y, y^2 - 2] """ - cdef GEN nf = self.get_nf() pari_catch_sig_on() - return self.new_gen(nf_get_zk(nf)) + return P.new_gen(member_zk(self.g)) def bnf_get_no(self): """ @@ -1632,6 +1570,7 @@ cdef class gen(sage.structure.element.RingElement): sage: int(pari("Mod(2, 7)")) 2 """ + late_import() return int(Integer(self)) def int_unsafe(gen self): @@ -1826,6 +1765,7 @@ cdef class gen(sage.structure.element.RingElement): sage: long(pari("Mod(2, 7)")) 2L """ + late_import() return long(Integer(self)) def __float__(gen self): @@ -1917,9 +1857,9 @@ cdef class gen(sage.structure.element.RingElement): sage: a.gequal(c) False """ - t0GEN(b) + cdef gen t0 = P(b) pari_catch_sig_on() - cdef int ret = gequal(a.g, t0) + cdef int ret = gequal(a.g, t0.g) pari_catch_sig_off() return ret != 0 @@ -2138,6 +2078,7 @@ cdef class gen(sage.structure.element.RingElement): """ cdef int n cdef GEN x + cdef gen t0 if k is None: pari_catch_sig_on() @@ -2148,10 +2089,9 @@ cdef class gen(sage.structure.element.RingElement): else: return n, P.new_gen(x) else: - k = int(k) - t0GEN(k) + t0 = P(k) pari_catch_sig_on() - n = ispower(self.g, t0, &x) + n = ispower(self.g, t0.g, &x) if n == 0: pari_catch_sig_off() return False, None @@ -2167,34 +2107,36 @@ cdef class gen(sage.structure.element.RingElement): 2-dimensional column vector the quotient and the remainder, with respect to v (to main variable if v is omitted). """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(divrem(x.g, t0, P.get_var(var))) + return P.new_gen(divrem(x.g, t0.g, P.get_var(var))) def lex(gen x, y): """ lex(x,y): Compare x and y lexicographically (1 if xy, 0 if x==y, -1 if xy) """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return lexcmp(x.g, t0) + r = lexcmp(x.g, t0.g) + pari_catch_sig_off() + return r def max(gen x, y): """ max(x,y): Return the maximum of x and y. """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(gmax(x.g, t0)) + return P.new_gen(gmax(x.g, t0.g)) def min(gen x, y): """ min(x,y): Return the minimum of x and y. """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(gmin(x.g, t0)) + return P.new_gen(gmin(x.g, t0.g)) def shift(gen x, long n): """ @@ -2225,7 +2167,9 @@ cdef class gen(sage.structure.element.RingElement): # Pari throws an error if you attempt to take the sign of # a complex number. pari_catch_sig_on() - return gsigne(x.g) + r = gsigne(x.g) + pari_catch_sig_off() + return r def vecmax(gen x): """ @@ -2490,9 +2434,9 @@ cdef class gen(sage.structure.element.RingElement): sage: a.type() 't_POLMOD' """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(gmodulo(x.g,t0)) + return P.new_gen(gmodulo(x.g, t0.g)) def Pol(self, v=-1): """ @@ -2638,9 +2582,11 @@ cdef class gen(sage.structure.element.RingElement): ... PariError: square discriminant in Qfb """ - t0GEN(b); t1GEN(c); t2GEN(D) + cdef gen t0 = P(b) + cdef gen t1 = P(c) + cdef gen t2 = P(D) pari_catch_sig_on() - return P.new_gen(Qfb0(a.g, t0, t1, t2, prec)) + return P.new_gen(Qfb0(a.g, t0.g, t1.g, t2.g, prec)) def Ser(gen x, v=-1, long seriesprecision = 16): @@ -2772,7 +2718,7 @@ cdef class gen(sage.structure.element.RingElement): cdef char* c pari_catch_sig_on() c = GENtostr(self.g) - v = self.new_gen(strtoGENstr(c)) + v = P.new_gen(strtoGENstr(c)) pari_free(c) return v @@ -3087,9 +3033,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(-1).bitand(-1) -1 """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(gbitand(x.g, t0)) + return P.new_gen(gbitand(x.g, t0.g)) def bitneg(gen x, long n=-1): @@ -3167,9 +3113,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(8+4).bitnegimply(8) 4 """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(gbitnegimply(x.g, t0)) + return P.new_gen(gbitnegimply(x.g, t0.g)) def bitor(gen x, y): @@ -3202,9 +3148,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(13).bitor(1) 13 """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(gbitor(x.g, t0)) + return P.new_gen(gbitor(x.g, t0.g)) def bittest(gen x, long n): @@ -3276,9 +3222,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(6).bitxor(0) 6 """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(gbitxor(x.g, t0)) + return P.new_gen(gbitxor(x.g, t0.g)) def ceil(gen x): @@ -4168,10 +4114,9 @@ cdef class gen(sage.structure.element.RingElement): 2147483647 # 32-bit 9223372036854775807 # 64-bit """ - cdef long v - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - v = ggval(x.g, t0) + v = ggval(x.g, t0.g) pari_catch_sig_off() return v @@ -4337,9 +4282,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(1+i).agm(-3) -0.964731722290876 + 1.15700282952632*I """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(agm(x.g, t0, pbw(precision))) + return P.new_gen(agm(x.g, t0.g, prec_bits_to_words(precision))) def arg(gen x, precision=0): r""" @@ -4514,9 +4459,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2).besselh1(3) 0.486091260585891 - 0.160400393484924*I """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return P.new_gen(hbessel1(nu.g, t0, pbw(precision))) + return P.new_gen(hbessel1(nu.g, t0.g, prec_bits_to_words(precision))) def besselh2(gen nu, x, precision=0): r""" @@ -4534,9 +4479,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2).besselh2(3) 0.486091260585891 + 0.160400393484924*I """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return P.new_gen(hbessel2(nu.g, t0, pbw(precision))) + return P.new_gen(hbessel2(nu.g, t0.g, prec_bits_to_words(precision))) def besselj(gen nu, x, precision=0): r""" @@ -4557,9 +4502,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2).besselj(3) 0.486091260585891 """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return P.new_gen(jbessel(nu.g, t0, pbw(precision))) + return P.new_gen(jbessel(nu.g, t0.g, prec_bits_to_words(precision))) def besseljh(gen nu, x, precision=0): """ @@ -4582,9 +4527,9 @@ cdef class gen(sage.structure.element.RingElement): 0.4127100324 # 32-bit 0.412710032209716 # 64-bit """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return P.new_gen(jbesselh(nu.g, t0, pbw(precision))) + return P.new_gen(jbesselh(nu.g, t0.g, prec_bits_to_words(precision))) def besseli(gen nu, x, precision=0): r""" @@ -4608,9 +4553,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2).besseli(3+i) 1.12539407613913 + 2.08313822670661*I """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return P.new_gen(ibessel(nu.g, t0, pbw(precision))) + return P.new_gen(ibessel(nu.g, t0.g, prec_bits_to_words(precision))) def besselk(gen nu, x, long flag=0, precision=0): """ @@ -4651,9 +4596,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2+i).besselk(300, flag=1) 3.74224603319728 E-132 + 2.49071062641525 E-134*I """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return P.new_gen(kbessel(nu.g, t0, pbw(precision))) + return P.new_gen(kbessel(nu.g, t0.g, prec_bits_to_words(precision))) def besseln(gen nu, x, precision=0): """ @@ -4672,9 +4617,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2+i).besseln(3) -0.280775566958244 - 0.486708533223726*I """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return P.new_gen(nbessel(nu.g, t0, pbw(precision))) + return P.new_gen(nbessel(nu.g, t0.g, prec_bits_to_words(precision))) def cos(gen x, precision=0): """ @@ -4939,10 +4884,10 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(1).hyperu(2,3) 0.333333333333333 """ - t0GEN(b) - t1GEN(x) + cdef gen t0 = P(b) + cdef gen t1 = P(x) pari_catch_sig_on() - return P.new_gen(hyperu(a.g, t0, t1, pbw(precision))) + return P.new_gen(hyperu(a.g, t0.g, t1.g, prec_bits_to_words(precision))) def incgam(gen s, x, y=None, precision=0): r""" @@ -4960,13 +4905,15 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(1+i).incgam(3-i) -0.0458297859919946 + 0.0433696818726677*I """ - t0GEN(x) - pari_catch_sig_on() + cdef gen t0 = P(x) + cdef gen t1 if y is None: - return P.new_gen(incgam(s.g, t0, pbw(precision))) + pari_catch_sig_on() + return P.new_gen(incgam(s.g, t0.g, prec_bits_to_words(precision))) else: - t1GEN(y) - return P.new_gen(incgam0(s.g, t0, t1, pbw(precision))) + t1 = P(y) + pari_catch_sig_on() + return P.new_gen(incgam0(s.g, t0.g, t1.g, prec_bits_to_words(precision))) def incgamc(gen s, x, precision=0): r""" @@ -4990,9 +4937,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(1).incgamc(2) 0.864664716763387 """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return P.new_gen(incgamc(s.g, t0, pbw(precision))) + return P.new_gen(incgamc(s.g, t0.g, prec_bits_to_words(precision))) def log(gen x, precision=0): r""" @@ -5267,9 +5214,9 @@ cdef class gen(sage.structure.element.RingElement): """ # TODO: ??? lots of good examples in the PARI docs ??? cdef GEN zetan - t0GEN(n) + cdef gen t0 = P(n) pari_catch_sig_on() - ans = P.new_gen_noclear(gsqrtn(x.g, t0, &zetan, pbw(precision))) + ans = P.new_gen_noclear(gsqrtn(x.g, t0.g, &zetan, prec_bits_to_words(precision))) return ans, P.new_gen(zetan) def tan(gen x, precision=0): @@ -5347,9 +5294,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(0.5).theta(2) 1.63202590295260 """ - t0GEN(z) + cdef gen t0 = P(z) pari_catch_sig_on() - return P.new_gen(theta(q.g, t0, pbw(precision))) + return P.new_gen(theta(q.g, t0.g, prec_bits_to_words(precision))) def thetanullk(gen q, long k, precision=0): """ @@ -5447,7 +5394,7 @@ cdef class gen(sage.structure.element.RingElement): 4*7^-2 + 5*7^-1 + O(7^0) """ pari_catch_sig_on() - return P.new_gen(gzeta(s.g, pbw(precision))) + return P.new_gen(gzeta(s.g, prec_bits_to_words(precision))) ########################################### # 4: NUMBER THEORETICAL functions @@ -5456,9 +5403,9 @@ cdef class gen(sage.structure.element.RingElement): def bezout(gen x, y): cdef gen u, v, g cdef GEN U, V, G - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - G = gbezout(x.g, t0, &U, &V) + G = gbezout(x.g, t0.g, &U, &V) g = P.new_gen_noclear(G) u = P.new_gen_noclear(U) v = P.new_gen(V) @@ -5496,9 +5443,9 @@ cdef class gen(sage.structure.element.RingElement): a bound for the number of terms in the continued fraction expansion. """ - t0GEN(b) + cdef gen t0 = P(b) pari_catch_sig_on() - return P.new_gen(contfrac0(x.g, t0, lmax)) + return P.new_gen(contfrac0(x.g, t0.g, lmax)) def contfracpnqn(gen x, b=0, long lmax=0): """ @@ -5582,9 +5529,9 @@ cdef class gen(sage.structure.element.RingElement): (x and y must be polynomials), 2 use the subresultant algorithm (x and y must be polynomials) """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(ggcd0(x.g, t0)) + return P.new_gen(ggcd0(x.g, t0.g)) def issquare(gen x, find_root=False): """ @@ -5628,9 +5575,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(10).lcm(15) 30 """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(glcm(x.g, t0)) + return P.new_gen(glcm(x.g, t0.g)) def numdiv(gen n): """ @@ -5863,9 +5810,10 @@ cdef class gen(sage.structure.element.RingElement): sage: e.elladd([1,0], [-1,1]) [-3/4, -15/8] """ - t0GEN(z0); t1GEN(z1) + cdef gen t0 = P(z0) + cdef gen t1 = P(z1) pari_catch_sig_on() - return self.new_gen(addell(self.g, t0, t1)) + return P.new_gen(addell(self.g, t0.g, t1.g)) def ellak(self, n): r""" @@ -5901,9 +5849,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellak(0) 0 """ - t0GEN(n) + cdef gen t0 = P(n) pari_catch_sig_on() - return self.new_gen(akell(self.g, t0)) + return P.new_gen(akell(self.g, t0.g)) def ellan(self, long n, python_ints=False): @@ -5996,9 +5944,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellak(-1) 0 """ - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - return self.new_gen(ellap(self.g, t0)) + return P.new_gen(ellap(self.g, t0.g)) def ellaplist(self, long n, python_ints=False): @@ -6050,26 +5998,29 @@ cdef class gen(sage.structure.element.RingElement): sage: type(v[0]) """ - # 1. make a table of primes up to n. - pari_catch_sig_on() - if n < 2: - return self.new_gen(zerovec(0)) + cdef gen t0 cdef GEN g - pari.init_primes(n+1) - t0GEN(n) - g = primes(gtolong(primepi(t0))) - - # 2. Replace each prime in the table by ellap of it. cdef long i + if n < 2: + pari_catch_sig_on() + return P.new_gen(zerovec(0)) + + # 1. Make a table of primes up to n. + P.init_primes(n+1) + t0 = P(n) + pari_catch_sig_on() + g = primes(gtolong(primepi(t0.g))) + + # 2. Replace each prime in the table by ellap of it. if python_ints: - v = [gtolong(ellap(self.g, g[i+1])) \ + v = [gtolong(ellap(self.g, gel(g, i + 1))) \ for i in range(glength(g))] - (pari).clear_stack() + P.clear_stack() return v else: for i from 0 <= i < glength(g): - g[i+1] = ellap(self.g, g[i+1]) + set_gel(g, i + 1, ellap(self.g, gel(g, i + 1))) return self.new_gen(g) @@ -6093,10 +6044,10 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellbil([1, 0], [-1, 1]) 0.418188984498861 """ - t0GEN(z0); t1GEN(z1) + cdef gen t0 = P(z0) + cdef gen t1 = P(z1) pari_catch_sig_on() - # the prec argument has no effect - return self.new_gen(bilhell(self.g, t0, t1, prec)) + return P.new_gen(bilhell(self.g, t0.g, t1.g, prec)) def ellchangecurve(self, ch): """ @@ -6125,9 +6076,9 @@ cdef class gen(sage.structure.element.RingElement): sage: f[:5] [1, -1, 0, 4, 3] """ - t0GEN(ch) + cdef gen t0 = P(ch) pari_catch_sig_on() - return self.new_gen(ellchangecurve(self.g, t0)) + return P.new_gen(ellchangecurve(self.g, t0.g)) def elleta(self): """ @@ -6194,9 +6145,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellheight([1,0], flag=1) 0.476711659343740 """ - t0GEN(a) + cdef gen t0 = P(a) pari_catch_sig_on() - return self.new_gen(ellheight0(self.g, t0, flag, pbw(precision))) + return P.new_gen(ellheight0(self.g, t0.g, flag, prec_bits_to_words(precision))) def ellheightmatrix(self, x): """ @@ -6222,10 +6173,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellheightmatrix([[1,0], [-1,1]]) [0.476711659343740, 0.418188984498861; 0.418188984498861, 0.686667083305587] """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - # the argument prec has no effect - return self.new_gen(mathell(self.g, t0, prec)) + return P.new_gen(mathell(self.g, t0.g, prec)) def ellisoncurve(self, x): """ @@ -6249,9 +6199,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellisoncurve([0]) True """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - t = bool(oncurve(self.g, t0) == 1) + t = bool(oncurve(self.g, t0.g) == 1) pari_catch_sig_off() return t @@ -6406,9 +6356,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.elllocalred(3) [2, -10, [1, 96, 1, 316], 4] """ - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - return self.new_gen(elllocalred(self.g, t0)) + return P.new_gen(elllocalred(self.g, t0.g)) def elllseries(self, s, A=1): """ @@ -6448,10 +6398,10 @@ cdef class gen(sage.structure.element.RingElement): sage: e.elllseries(2.1, A=1.1) 0.402838047956645 """ - t0GEN(s); t1GEN(A) + cdef gen t0 = P(s) + cdef gen t1 = P(A) pari_catch_sig_on() - # the argument prec has no effect - return self.new_gen(elllseries(self.g, t0, t1, prec)) + return P.new_gen(elllseries(self.g, t0.g, t1.g, prec)) def ellminimalmodel(self): """ @@ -6518,9 +6468,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellorder([1,0]) 0 """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(orderell(self.g, t0)) + return P.new_gen(orderell(self.g, t0.g)) def ellordinate(self, x): """ @@ -6549,15 +6499,14 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellordinate('z+2*z^2+O(z^4)') [-2*z - 7*z^2 - 23*z^3 + O(z^4), -1 + 2*z + 7*z^2 + 23*z^3 + O(z^4)] """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - # the prec argument has no effect - return self.new_gen(ellordinate(self.g, t0, prec)) + return P.new_gen(ellordinate(self.g, t0.g, prec)) - def ellpointtoz(self, P, long precision=0): + def ellpointtoz(self, pt, long precision=0): """ - e.ellpointtoz(P): return the complex number (in the fundamental - parallelogram) corresponding to the point P on the elliptic curve + e.ellpointtoz(pt): return the complex number (in the fundamental + parallelogram) corresponding to the point ``pt`` on the elliptic curve e, under the complex uniformization of e given by the Weierstrass p-function. @@ -6576,9 +6525,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellpointtoz([0]) 0 """ - t0GEN(P) + cdef gen t0 = P(pt) pari_catch_sig_on() - return self.new_gen(zell(self.g, t0, pbw(precision))) + return P.new_gen(zell(self.g, t0.g, prec_bits_to_words(precision))) def ellpow(self, z, n): """ @@ -6638,9 +6587,10 @@ cdef class gen(sage.structure.element.RingElement): ....: P0 = e.elladd(e.ellpow(P, cm_minpoly[0]), e.ellpow(P2, cm)) ....: assert(P0 == E(0)) """ - t0GEN(z); t1GEN(n) + cdef gen t0 = P(z) + cdef gen t1 = P(n) pari_catch_sig_on() - return self.new_gen(powell(self.g, t0, t1)) + return P.new_gen(powell(self.g, t0.g, t1.g)) def ellrootno(self, p=1): """ @@ -6673,10 +6623,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellrootno(1009) 1 """ - cdef long rootno - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - rootno = ellrootno(self.g, t0) + rootno = ellrootno(self.g, t0.g) pari_catch_sig_off() return rootno @@ -6693,10 +6642,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellsigma(2+i) 1.43490215804166 + 1.80307856719256*I """ - t0GEN(z) + cdef gen t0 = P(z) pari_catch_sig_on() - # the prec argument has no effect - return self.new_gen(ellsigma(self.g, t0, flag, prec)) + return P.new_gen(ellsigma(self.g, t0.g, flag, prec)) def ellsub(self, z0, z1): """ @@ -6720,13 +6668,14 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellsub([1,0], [-1,1]) [0, 0] """ - t0GEN(z0); t1GEN(z1) + cdef gen t0 = P(z0) + cdef gen t1 = P(z1) pari_catch_sig_on() - return self.new_gen(subell(self.g, t0, t1)) + return P.new_gen(subell(self.g, t0.g, t1.g)) def elltaniyama(self): pari_catch_sig_on() - return self.new_gen(taniyama(self.g)) + return P.new_gen(taniyama(self.g)) def elltors(self, flag=0): """ @@ -6803,10 +6752,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellzeta(i-1) -0.350122658523049 - 0.350122658523049*I """ - t0GEN(z) + cdef gen t0 = P(z) pari_catch_sig_on() - # the prec argument has no effect - return self.new_gen(ellzeta(self.g, t0, prec)) + return P.new_gen(ellzeta(self.g, t0.g, prec)) def ellztopoint(self, z): """ @@ -6838,14 +6786,13 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellztopoint(0) [0] """ - t0GEN(z) + cdef gen t0 = P(z) try: dprec = prec_words_to_dec(z.precision()) except AttributeError: dprec = prec pari_catch_sig_on() - # the prec argument has no effect - return self.new_gen(pointell(self.g, t0, dprec)) + return P.new_gen(pointell(self.g, t0.g, dprec)) def omega(self): """ @@ -6916,53 +6863,53 @@ cdef class gen(sage.structure.element.RingElement): .. [PariUsers] User's Guide to PARI/GP, http://pari.math.u-bordeaux.fr/pub/pari/manuals/2.5.1/users.pdf """ - cdef long n pari_catch_sig_on() n = bnfcertify(self.g) pari_catch_sig_off() return n def bnfinit(self, long flag=0, tech=None): + cdef gen t0 if tech is None: pari_catch_sig_on() - return P.new_gen(bnfinit0(self.g, flag, 0, prec)) + return P.new_gen(bnfinit0(self.g, flag, NULL, prec)) else: - t0GEN(tech) + t0 = P(tech) pari_catch_sig_on() - return P.new_gen(bnfinit0(self.g, flag, t0, prec)) + return P.new_gen(bnfinit0(self.g, flag, t0.g, prec)) def bnfisintnorm(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(bnfisintnorm(self.g, t0)) + return P.new_gen(bnfisintnorm(self.g, t0.g)) def bnfisnorm(self, x, long flag=0): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(bnfisnorm(self.g, t0, flag)) + return P.new_gen(bnfisnorm(self.g, t0.g, flag)) def bnfisprincipal(self, x, long flag=1): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(bnfisprincipal0(self.g, t0, flag)) + return P.new_gen(bnfisprincipal0(self.g, t0.g, flag)) def bnfnarrow(self): pari_catch_sig_on() - return self.new_gen(buchnarrow(self.g)) + return P.new_gen(buchnarrow(self.g)) - def bnfsunit(bnf, S, long precision=0): - t0GEN(S) + def bnfsunit(self, S, long precision=0): + cdef gen t0 = P(S) pari_catch_sig_on() - return bnf.new_gen(bnfsunit(bnf.g, t0, pbw(precision))) + return P.new_gen(bnfsunit(self.g, t0.g, prec_bits_to_words(precision))) def bnfunit(self): pari_catch_sig_on() - return self.new_gen(bnf_get_fu(self.g)) + return P.new_gen(bnf_get_fu(self.g)) def bnfisunit(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(bnfisunit(self.g, t0)) + return P.new_gen(bnfisunit(self.g, t0.g)) def bnrclassno(self, I): r""" @@ -6982,69 +6929,73 @@ cdef class gen(sage.structure.element.RingElement): sage: K.pari_bnf().bnrclassno(p._pari_bid_()) 3 """ - t0GEN(I) + cdef gen t0 = P(I) pari_catch_sig_on() - return self.new_gen(bnrclassno(self.g, t0)) + return P.new_gen(bnrclassno(self.g, t0.g)) def bnfissunit(self, sunit_data, x): - t0GEN(x) - t1GEN(sunit_data) + cdef gen t0 = P(x) + cdef gen t1 = P(sunit_data) pari_catch_sig_on() - return self.new_gen(bnfissunit(self.g, t1, t0)) + return P.new_gen(bnfissunit(self.g, t1.g, t0.g)) def dirzetak(self, n): - t0GEN(n) + cdef gen t0 = P(n) pari_catch_sig_on() - return self.new_gen(dirzetak(self.g, t0)) + return P.new_gen(dirzetak(self.g, t0.g)) def galoisapply(self, aut, x): - t0GEN(aut) - t1GEN(x) + cdef gen t0 = P(aut) + cdef gen t1 = P(x) pari_catch_sig_on() - return self.new_gen(galoisapply(self.g, t0, t1)) + return P.new_gen(galoisapply(self.g, t0.g, t1.g)) def galoisinit(self, den=None): """ galoisinit(K{,den}): calculate Galois group of number field K; see PARI manual for meaning of den """ + cdef gen t0 if den is None: pari_catch_sig_on() - return self.new_gen(galoisinit(self.g, NULL)) + return P.new_gen(galoisinit(self.g, NULL)) else: - t0GEN(den) + t0 = P(den) pari_catch_sig_on() - return self.new_gen(galoisinit(self.g, t0)) + return P.new_gen(galoisinit(self.g, t0.g)) def galoispermtopol(self, perm): - t0GEN(perm) + cdef gen t0 = P(perm) pari_catch_sig_on() - return self.new_gen(galoispermtopol(self.g, t0)) + return P.new_gen(galoispermtopol(self.g, t0.g)) def galoisfixedfield(self, perm, long flag=0, v=-1): - t0GEN(perm); + cdef gen t0 = P(perm) pari_catch_sig_on() - return self.new_gen(galoisfixedfield(self.g, t0, flag, P.get_var(v))) + return P.new_gen(galoisfixedfield(self.g, t0.g, flag, P.get_var(v))) def idealred(self, I, vdir=0): - t0GEN(I); t1GEN(vdir) + cdef gen t0 = P(I) + cdef gen t1 = P(vdir) pari_catch_sig_on() - return self.new_gen(idealred0(self.g, t0, t1 if vdir else NULL)) + return P.new_gen(idealred0(self.g, t0.g, t1.g if vdir else NULL)) def idealadd(self, x, y): - t0GEN(x); t1GEN(y) + cdef gen t0 = P(x) + cdef gen t1 = P(y) pari_catch_sig_on() - return self.new_gen(idealadd(self.g, t0, t1)) + return P.new_gen(idealadd(self.g, t0.g, t1.g)) def idealaddtoone(self, x, y): - t0GEN(x); t1GEN(y) + cdef gen t0 = P(x) + cdef gen t1 = P(y) pari_catch_sig_on() - return self.new_gen(idealaddtoone0(self.g, t0, t1)) + return P.new_gen(idealaddtoone0(self.g, t0.g, t1.g)) def idealappr(self, x, long flag=0): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(idealappr0(self.g, t0, flag)) + return P.new_gen(idealappr0(self.g, t0.g, flag)) def idealcoprime(self, x, y): """ @@ -7066,34 +7017,38 @@ cdef class gen(sage.structure.element.RingElement): sage: nf.idealcoprime(x, y) [5/43, 9/43, -1/43]~ """ - t0GEN(x); t1GEN(y) + cdef gen t0 = P(x) + cdef gen t1 = P(y) pari_catch_sig_on() - return self.new_gen(idealcoprime(self.g, t0, t1)) + return P.new_gen(idealcoprime(self.g, t0.g, t1.g)) def idealdiv(self, x, y, long flag=0): - t0GEN(x); t1GEN(y) + cdef gen t0 = P(x) + cdef gen t1 = P(y) pari_catch_sig_on() - return self.new_gen(idealdiv0(self.g, t0, t1, flag)) + return P.new_gen(idealdiv0(self.g, t0.g, t1.g, flag)) def idealfactor(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(idealfactor(self.g, t0)) + return P.new_gen(idealfactor(self.g, t0.g)) def idealhnf(self, a, b=None): - t0GEN(a) + cdef gen t0 = P(a) + cdef gen t1 if b is None: pari_catch_sig_on() - return self.new_gen(idealhnf(self.g, t0)) + return P.new_gen(idealhnf(self.g, t0.g)) else: - t1GEN(b) + t1 = P(b) pari_catch_sig_on() - return self.new_gen(idealhnf0(self.g, t0, t1)) + return P.new_gen(idealhnf0(self.g, t0.g, t1.g)) def idealintersection(self, x, y): - t0GEN(x); t1GEN(y) + cdef gen t0 = P(x) + cdef gen t1 = P(y) pari_catch_sig_on() - return self.new_gen(idealintersect(self.g, t0, t1)) + return P.new_gen(idealintersect(self.g, t0.g, t1.g)) def ideallist(self, long bound, long flag = 4): """ @@ -7150,22 +7105,24 @@ cdef class gen(sage.structure.element.RingElement): sage: nf.ideallog(x, bid) [25]~ """ - t0GEN(x); t1GEN(bid) + cdef gen t0 = P(x) + cdef gen t1 = P(bid) pari_catch_sig_on() - return self.new_gen(ideallog(self.g, t0, t1)) + return P.new_gen(ideallog(self.g, t0.g, t1.g)) def idealmul(self, x, y, long flag=0): - t0GEN(x); t1GEN(y) + cdef gen t0 = P(x) + cdef gen t1 = P(y) pari_catch_sig_on() if flag == 0: - return self.new_gen(idealmul(self.g, t0, t1)) + return P.new_gen(idealmul(self.g, t0.g, t1.g)) else: - return self.new_gen(idealmulred(self.g, t0, t1)) + return P.new_gen(idealmulred(self.g, t0.g, t1.g)) def idealnorm(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(idealnorm(self.g, t0)) + return P.new_gen(idealnorm(self.g, t0.g)) def idealprimedec(nf, p): """ @@ -7183,9 +7140,9 @@ cdef class gen(sage.structure.element.RingElement): sage: F[0].pr_get_p() 5 """ - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - return nf.new_gen(idealprimedec(nf.g, t0)) + return P.new_gen(idealprimedec(nf.g, t0.g)) def idealstar(self, I, long flag=1): """ @@ -7223,33 +7180,34 @@ cdef class gen(sage.structure.element.RingElement): sage: nf.idealstar(I) [[[43, 9, 5; 0, 1, 0; 0, 0, 1], [0]], [42, [42]], Mat([[43, [9, 1, 0]~, 1, 1, [-5, -9, 1]~], 1]), [[[[42], [[3, 0, 0]~], [[3, 0, 0]~], [Vecsmall([])], 1]], [[], [], []]], Mat(1)] """ - t0GEN(I) + cdef gen t0 = P(I) pari_catch_sig_on() - return self.new_gen(idealstar0(self.g, t0, flag)) + return P.new_gen(idealstar0(self.g, t0.g, flag)) def idealtwoelt(self, x, a=None): - t0GEN(x) + cdef gen t0 = P(x) + cdef gen t1 if a is None: pari_catch_sig_on() - return self.new_gen(idealtwoelt0(self.g, t0, NULL)) + return P.new_gen(idealtwoelt0(self.g, t0.g, NULL)) else: - t1GEN(a) + t1 = P(a) pari_catch_sig_on() - return self.new_gen(idealtwoelt0(self.g, t0, t1)) + return P.new_gen(idealtwoelt0(self.g, t0.g, t1.g)) def idealval(self, x, p): - cdef long v - t0GEN(x); t1GEN(p) + cdef gen t0 = P(x) + cdef gen t1 = P(p) pari_catch_sig_on() - v = idealval(self.g, t0, t1) + v = idealval(self.g, t0.g, t1.g) pari_catch_sig_off() return v def elementval(self, x, p): - cdef long v - t0GEN(x); t1GEN(p) + cdef gen t0 = P(x) + cdef gen t1 = P(p) pari_catch_sig_on() - v = nfval(self.g, t0, t1) + v = nfval(self.g, t0.g, t1.g) pari_catch_sig_off() return v @@ -7302,12 +7260,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari('x^3 - 17').nfbasis(flag = 2) [1, x, 1/3*x^2 - 1/3*x + 1/3] """ - global t0 - t0GEN(fa) - if typ(t0) != t_MAT: - t0 = 0 + cdef gen t0 = P(fa) pari_catch_sig_on() - return self.new_gen(nfbasis0(self.g, flag, t0)) + return P.new_gen(nfbasis0(self.g, flag, t0.g if typ(t0.g) == t_MAT else NULL)) def nfbasis_d(self, long flag=0, fa=0): """ @@ -7331,14 +7286,11 @@ cdef class gen(sage.structure.element.RingElement): sage: pari([-2,0,0,1]).Polrev().nfbasis_d() ([1, x, x^2], -108) """ - global t0 cdef GEN disc - t0GEN(fa) - if typ(t0) != t_MAT: - t0 = 0 + cdef gen t0 = P(fa) pari_catch_sig_on() - B = self.new_gen_noclear(nfbasis(self.g, &disc, flag, t0)) - D = self.new_gen(disc); + B = P.new_gen_noclear(nfbasis(self.g, &disc, flag, t0.g if typ(t0.g) == t_MAT else NULL)) + D = P.new_gen(disc); return B,D def nfbasistoalg(nf, x): @@ -7371,9 +7323,9 @@ cdef class gen(sage.structure.element.RingElement): sage: Kpari.getattr('zk') * pari("[3/2, -5, 0]~") -5/3*y^2 + 5/3*y - 1/6 """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return nf.new_gen(basistoalg(nf.g, t0)) + return P.new_gen(basistoalg(nf.g, t0.g)) def nfbasistoalg_lift(nf, x): r""" @@ -7404,9 +7356,9 @@ cdef class gen(sage.structure.element.RingElement): sage: Kpari.getattr('zk') * pari("[3/2, -5, 0]~") -5/3*y^2 + 5/3*y - 1/6 """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return nf.new_gen(gel(basistoalg(nf.g, t0), 2)) + return P.new_gen(gel(basistoalg(nf.g, t0.g), 2)) def nfdisc(self, long flag=0, p=0): """ @@ -7456,9 +7408,10 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(k).nfeltdiveuc(pari(x), pari(y)) [2, -2]~ """ - t0GEN(x); t1GEN(y) + cdef gen t0 = P(x) + cdef gen t1 = P(y) pari_catch_sig_on() - return self.new_gen(nfdiveuc(self.g, t0, t1)) + return P.new_gen(nfdiveuc(self.g, t0.g, t1.g)) def nfeltreduce(self, x, I): """ @@ -7476,14 +7429,15 @@ cdef class gen(sage.structure.element.RingElement): sage: 12 - k(kp.nfeltreduce(12, I.pari_hnf())) in I True """ - t0GEN(x); t1GEN(I) + cdef gen t0 = P(x) + cdef gen t1 = P(I) pari_catch_sig_on() - return self.new_gen(nfreduce(self.g, t0, t1)) + return P.new_gen(nfreduce(self.g, t0.g, t1.g)) def nffactor(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(nffactor(self.g, t0)) + return P.new_gen(nffactor(self.g, t0.g)) def nfgenerator(self): f = self[0] @@ -7512,17 +7466,17 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(K).nfhilbert(pari(t), pari(t+2), P.pari_prime()) 1 """ - cdef long r - t0GEN(a) - t1GEN(b) + cdef gen t0 = P(a) + cdef gen t1 = P(b) + cdef gen t2 if p: - t2GEN(p) + t2 = P(p) pari_catch_sig_on() - r = nfhilbert0(self.g, t0, t1, t2) + r = nfhilbert0(self.g, t0.g, t1.g, t2.g) else: pari_catch_sig_on() - r = nfhilbert(self.g, t0, t1) - P.clear_stack() + r = nfhilbert(self.g, t0.g, t1.g) + pari_catch_sig_off() return r @@ -7588,11 +7542,9 @@ cdef class gen(sage.structure.element.RingElement): - Aly Deines (2012-09-19) """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(nfhnf(self.g,t0)) - - + return P.new_gen(nfhnf(self.g, t0.g)) def nfinit(self, long flag=0, long precision=0): @@ -7719,37 +7671,39 @@ cdef class gen(sage.structure.element.RingElement): - ``d`` - C long integer """ pari_catch_sig_on() - return self.new_gen(nfsubfields(self.g, d)) + return P.new_gen(nfsubfields(self.g, d)) def rnfcharpoly(self, T, a, v='x'): - t0GEN(T); t1GEN(a); t2GEN(v) + cdef gen t0 = P(T) + cdef gen t1 = P(a) + cdef gen t2 = P(v) pari_catch_sig_on() - return self.new_gen(rnfcharpoly(self.g, t0, t1, gvar(t2))) + return P.new_gen(rnfcharpoly(self.g, t0.g, t1.g, gvar(t2.g))) def rnfdisc(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(rnfdiscf(self.g, t0)) + return P.new_gen(rnfdiscf(self.g, t0.g)) def rnfeltabstorel(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(rnfelementabstorel(self.g, t0)) + return P.new_gen(rnfelementabstorel(self.g, t0.g)) def rnfeltreltoabs(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(rnfelementreltoabs(self.g, t0)) + return P.new_gen(rnfelementreltoabs(self.g, t0.g)) def rnfequation(self, poly, long flag=0): - t0GEN(poly) + cdef gen t0 = P(poly) pari_catch_sig_on() - return self.new_gen(rnfequation0(self.g, t0, flag)) + return P.new_gen(rnfequation0(self.g, t0.g, flag)) def rnfidealabstorel(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(rnfidealabstorel(self.g, t0)) + return P.new_gen(rnfidealabstorel(self.g, t0.g)) def rnfidealdown(self, x): r""" @@ -7772,29 +7726,29 @@ cdef class gen(sage.structure.element.RingElement): sage: rnf.rnfidealdown(P) [2, 0; 0, 2] """ - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(rnfidealdown(self.g, t0)) + return P.new_gen(rnfidealdown(self.g, t0.g)) def rnfidealhnf(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(rnfidealhermite(self.g, t0)) + return P.new_gen(rnfidealhermite(self.g, t0.g)) def rnfidealnormrel(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(rnfidealnormrel(self.g, t0)) + return P.new_gen(rnfidealnormrel(self.g, t0.g)) def rnfidealreltoabs(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(rnfidealreltoabs(self.g, t0)) + return P.new_gen(rnfidealreltoabs(self.g, t0.g)) def rnfidealtwoelt(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(rnfidealtwoelement(self.g, t0)) + return P.new_gen(rnfidealtwoelement(self.g, t0.g)) def rnfinit(self, poly): """ @@ -7808,14 +7762,14 @@ cdef class gen(sage.structure.element.RingElement): sage: g = x^5 - x^2 + y sage: L = K.rnfinit(g) """ - t0GEN(poly) + cdef gen t0 = P(poly) pari_catch_sig_on() - return P.new_gen(rnfinit(self.g, t0)) + return P.new_gen(rnfinit(self.g, t0.g)) def rnfisfree(self, poly): - t0GEN(poly) + cdef gen t0 = P(poly) pari_catch_sig_on() - r = rnfisfree(self.g, t0) + r = rnfisfree(self.g, t0.g) pari_catch_sig_off() return r @@ -7865,16 +7819,16 @@ cdef class gen(sage.structure.element.RingElement): 2/15 """ pari_catch_sig_on() - return self.new_gen(content(self.g)) + return P.new_gen(content(self.g)) def deriv(self, v=-1): pari_catch_sig_on() - return self.new_gen(deriv(self.g, self.get_var(v))) + return P.new_gen(deriv(self.g, P.get_var(v))) def eval(self, x): - t0GEN(x) + cdef gen t0 = P(x) pari_catch_sig_on() - return self.new_gen(poleval(self.g, t0)) + return P.new_gen(poleval(self.g, t0.g)) def __call__(self, x): return self.eval(x) @@ -7893,9 +7847,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(x^2 - 2).factornf(K.pari_polynomial("a")) [x + Mod(-4*a, 8*a^2 - 1), 1; x + Mod(4*a, 8*a^2 - 1), 1] """ - t0GEN(t) + cdef gen t0 = P(t) pari_catch_sig_on() - return self.new_gen(polfnf(self.g, t0)) + return P.new_gen(polfnf(self.g, t0.g)) def factorpadic(self, p, long r=20, long flag=0): """ @@ -7903,9 +7857,9 @@ cdef class gen(sage.structure.element.RingElement): polynomial x to precision r. flag is optional and may be set to 0 (use round 4) or 1 (use Buchmann-Lenstra) """ - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - return self.new_gen(factorpadic0(self.g, t0, r, flag)) + return P.new_gen(factorpadic0(self.g, t0.g, r, flag)) def factormod(self, p, long flag=0): """ @@ -7914,9 +7868,9 @@ cdef class gen(sage.structure.element.RingElement): simple factormod, same except that only the degrees of the irreducible factors are given. """ - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - return self.new_gen(factormod0(self.g, t0, flag)) + return P.new_gen(factormod0(self.g, t0.g, flag)) def intformal(self, y=-1): """ @@ -7924,16 +7878,16 @@ cdef class gen(sage.structure.element.RingElement): variable of y, or to the main variable of x if y is omitted """ pari_catch_sig_on() - return self.new_gen(integ(self.g, self.get_var(y))) + return P.new_gen(integ(self.g, P.get_var(y))) def padicappr(self, a): """ x.padicappr(a): p-adic roots of the polynomial x congruent to a mod p """ - t0GEN(a) + cdef gen t0 = P(a) pari_catch_sig_on() - return self.new_gen(padicappr(self.g, t0)) + return P.new_gen(padicappr(self.g, t0.g)) def newtonpoly(self, p): """ @@ -7946,9 +7900,9 @@ cdef class gen(sage.structure.element.RingElement): sage: x.newtonpoly(3) [1, 1, -1/3, -1/3, -1/3, -1/3, -1/3, -1/3] """ - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - return self.new_gen(newtonpoly(self.g, t0)) + return P.new_gen(newtonpoly(self.g, t0.g)) def polcoeff(self, long n, var=-1): """ @@ -7967,19 +7921,19 @@ cdef class gen(sage.structure.element.RingElement): x """ pari_catch_sig_on() - return self.new_gen(polcoeff0(self.g, n, self.get_var(var))) + return P.new_gen(polcoeff0(self.g, n, P.get_var(var))) def polcompositum(self, pol2, long flag=0): - t0GEN(pol2) + cdef gen t0 = P(pol2) pari_catch_sig_on() - return self.new_gen(polcompositum0(self.g, t0, flag)) + return P.new_gen(polcompositum0(self.g, t0.g, flag)) def poldegree(self, var=-1): """ f.poldegree(var=x): Return the degree of this polynomial. """ pari_catch_sig_on() - n = poldegree(self.g, self.get_var(var)) + n = poldegree(self.g, P.get_var(var)) pari_catch_sig_off() return n @@ -8022,13 +7976,14 @@ cdef class gen(sage.structure.element.RingElement): sage: nf.nfgaloisconj() [-x, x]~ """ - global t0 - if denom is not None: - t0GEN(denom) + cdef gen t0 + if denom is None: + pari_catch_sig_on() + return P.new_gen(galoisconj0(self.g, flag, NULL, prec_bits_to_words(precision))) else: - t0 = NULL - pari_catch_sig_on() - return self.new_gen(galoisconj0(self.g, flag, t0, pbw(precision))) + t0 = P(denom) + pari_catch_sig_on() + return P.new_gen(galoisconj0(self.g, flag, t0.g, prec_bits_to_words(precision))) def nfroots(self, poly): r""" @@ -8048,9 +8003,9 @@ cdef class gen(sage.structure.element.RingElement): sage: nf.nfroots(y^4 + 2) [Mod(-zz, zz^4 + 2), Mod(zz, zz^4 + 2)] """ - t0GEN(poly) + cdef gen t0 = P(poly) pari_catch_sig_on() - return self.new_gen(nfroots(self.g, t0)) + return P.new_gen(nfroots(self.g, t0.g)) def polhensellift(self, y, p, long e): """ @@ -8058,10 +8013,10 @@ cdef class gen(sage.structure.element.RingElement): modulo p to a factorization modulo `p^e` using Hensel lift. The factors in y must be pairwise relatively prime modulo p. """ - t0GEN(y) - t1GEN(p) + cdef gen t0 = P(y) + cdef gen t1 = P(p) pari_catch_sig_on() - return self.new_gen(polhensellift(self.g, t0, t1, e)) + return P.new_gen(polhensellift(self.g, t0.g, t1.g, e)) def polisirreducible(self): """ @@ -8080,33 +8035,34 @@ cdef class gen(sage.structure.element.RingElement): variable v otherwise """ pari_catch_sig_on() - return self.new_gen(pollead(self.g, self.get_var(v))) + return P.new_gen(pollead(self.g, P.get_var(v))) def polrecip(self): pari_catch_sig_on() - return self.new_gen(polrecip(self.g)) + return P.new_gen(polrecip(self.g)) def polred(self, flag=0, fa=None): + cdef gen t0 if fa is None: pari_catch_sig_on() - return self.new_gen(polred0(self.g, flag, NULL)) + return P.new_gen(polred0(self.g, flag, NULL)) else: - t0GEN(fa) + t0 = P(fa) pari_catch_sig_on() - return self.new_gen(polred0(self.g, flag, t0)) + return P.new_gen(polred0(self.g, flag, t0.g)) def polredabs(self, flag=0): pari_catch_sig_on() - return self.new_gen(polredabs0(self.g, flag)) + return P.new_gen(polredabs0(self.g, flag)) def polredbest(self, flag=0): pari_catch_sig_on() - return self.new_gen(polredbest(self.g, flag)) + return P.new_gen(polredbest(self.g, flag)) def polresultant(self, y, var=-1, flag=0): - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return self.new_gen(polresultant0(self.g, t0, self.get_var(var), flag)) + return P.new_gen(polresultant0(self.g, t0.g, P.get_var(var), flag)) def polroots(self, flag=0, precision=0): """ @@ -8115,28 +8071,28 @@ cdef class gen(sage.structure.element.RingElement): by Gourdon, or 1: uses a modified Newton method. """ pari_catch_sig_on() - return self.new_gen(roots0(self.g, flag, pbw(precision))) + return P.new_gen(roots0(self.g, flag, prec_bits_to_words(precision))) def polrootsmod(self, p, flag=0): - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - return self.new_gen(rootmod0(self.g, t0, flag)) + return P.new_gen(rootmod0(self.g, t0.g, flag)) def polrootspadic(self, p, r=20): - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - return self.new_gen(rootpadic(self.g, t0, r)) + return P.new_gen(rootpadic(self.g, t0.g, r)) def polrootspadicfast(self, p, r=20): - t0GEN(p) + cdef gen t0 = P(p) pari_catch_sig_on() - return self.new_gen(rootpadicfast(self.g, t0, r)) + return P.new_gen(rootpadicfast(self.g, t0.g, r)) def polsturm(self, a, b): - t0GEN(a) - t1GEN(b) + cdef gen t0 = P(a) + cdef gen t1 = P(b) pari_catch_sig_on() - n = sturmpart(self.g, t0, t1) + n = sturmpart(self.g, t0.g, t1.g) pari_catch_sig_off() return n @@ -8147,22 +8103,22 @@ cdef class gen(sage.structure.element.RingElement): return n def polsylvestermatrix(self, g): - t0GEN(g) + cdef gen t0 = P(g) pari_catch_sig_on() - return self.new_gen(sylvestermatrix(self.g, t0)) + return P.new_gen(sylvestermatrix(self.g, t0.g)) def polsym(self, long n): pari_catch_sig_on() - return self.new_gen(polsym(self.g, n)) + return P.new_gen(polsym(self.g, n)) def serconvol(self, g): - t0GEN(g) + cdef gen t0 = P(g) pari_catch_sig_on() - return self.new_gen(convol(self.g, t0)) + return P.new_gen(convol(self.g, t0.g)) def serlaplace(self): pari_catch_sig_on() - return self.new_gen(laplace(self.g)) + return P.new_gen(laplace(self.g)) def serreverse(self): """ @@ -8183,22 +8139,22 @@ cdef class gen(sage.structure.element.RingElement): x + O(x^4) """ pari_catch_sig_on() - return self.new_gen(recip(self.g)) + return P.new_gen(recip(self.g)) def thueinit(self, flag=0): pari_catch_sig_on() - return self.new_gen(thueinit(self.g, flag, prec)) + return P.new_gen(thueinit(self.g, flag, prec)) def rnfisnorminit(self, polrel, long flag=2): - t0GEN(polrel) + cdef gen t0 = P(polrel) pari_catch_sig_on() - return self.new_gen(rnfisnorminit(self.g, t0, flag)) + return P.new_gen(rnfisnorminit(self.g, t0.g, flag)) def rnfisnorm(self, T, long flag=0): - t0GEN(T) + cdef gen t0 = P(T) pari_catch_sig_on() - return self.new_gen(rnfisnorm(t0, self.g, flag)) + return P.new_gen(rnfisnorm(t0.g, self.g, flag)) ########################################### # 8: Vectors, matrices, LINEAR ALGEBRA and sets @@ -8218,14 +8174,15 @@ cdef class gen(sage.structure.element.RingElement): This function uses the PARI row and column indexing, so the first row or column is indexed by 1 instead of 0. """ - t0GEN(y) + cdef gen t0 = P(y) + cdef gen t1 if z is None: pari_catch_sig_on() - return P.new_gen(shallowextract(self.g, t0)) + return P.new_gen(shallowextract(self.g, t0.g)) else: - t1GEN(z) + t1 = P(z) pari_catch_sig_on() - return P.new_gen(extract0(self.g, t0, t1)) + return P.new_gen(extract0(self.g, t0.g, t1.g)) def ncols(self): """ @@ -8332,10 +8289,10 @@ cdef class gen(sage.structure.element.RingElement): robust, slower implementation, valid for non integral quadratic forms. """ - t0GEN(B) - t1GEN(max) + cdef gen t0 = P(B) + cdef gen t1 = P(max) pari_catch_sig_on() - return self.new_gen(qfminim0(self.g,t0,t1,flag,precdl)) + return P.new_gen(qfminim0(self.g, t0.g, t1.g, flag, precdl)) def qfrep(self, B, long flag=0): """ @@ -8344,9 +8301,9 @@ cdef class gen(sage.structure.element.RingElement): digits of flag mean 1: count vectors of even norm from 1 to 2B, 2: return a t_VECSMALL instead of a t_VEC. """ - t0GEN(B) + cdef gen t0 = P(B) pari_catch_sig_on() - return self.new_gen(qfrep0(self.g,t0,flag)) + return P.new_gen(qfrep0(self.g, t0.g, flag)) def matsolve(self, B): """ @@ -8371,9 +8328,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari('[1,1;1,-1]').matsolve(pari('[1;0]')) [1/2; 1/2] """ - t0GEN(B) + cdef gen t0 = P(B) pari_catch_sig_on() - return self.new_gen(gauss(self.g,t0)) + return P.new_gen(gauss(self.g, t0.g)) def matsolvemod(self, D, B, long flag = 0): r""" @@ -8417,10 +8374,10 @@ cdef class gen(sage.structure.element.RingElement): sage: M2.matsolvemod(9, pari('[2,45]~'), 1) [[1, 1]~, [-1, -4; 1, -5]] """ - t0GEN(D) - t1GEN(B) + cdef gen t0 = P(D) + cdef gen t1 = P(B) pari_catch_sig_on() - return self.new_gen(matsolvemod0(self.g, t0, t1, flag)) + return P.new_gen(matsolvemod0(self.g, t0.g, t1.g, flag)) def matker(self, long flag=0): """ @@ -8567,9 +8524,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(M).mathnfmod(12) [1, 0, 0; 0, 2, 0; 0, 0, 6] """ - t0GEN(d) + cdef gen t0 = P(d) pari_catch_sig_on() - return self.new_gen(hnfmod(self.g, t0)) + return P.new_gen(hnfmod(self.g, t0.g)) def mathnfmodid(self, d): """ @@ -8596,9 +8553,9 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(M).mathnfmod(6) [1, 0, 0; 0, 1, 0; 0, 0, 6] """ - t0GEN(d) + cdef gen t0 = P(d) pari_catch_sig_on() - return self.new_gen(hnfmodid(self.g, t0)) + return P.new_gen(hnfmodid(self.g, t0.g)) def matsnf(self, flag=0): """ @@ -8705,6 +8662,7 @@ cdef class gen(sage.structure.element.RingElement): PariError: sorry, factor for general polynomials is not yet implemented """ cdef int r + cdef GEN t0 cdef GEN cutoff if limit == -1 and typ(self.g) == t_INT and proof: pari_catch_sig_on() @@ -8726,18 +8684,17 @@ cdef class gen(sage.structure.element.RingElement): ########################################### def hilbert(x, y, p): - cdef long ret - t0GEN(y) - t1GEN(p) + cdef gen t0 = P(y) + cdef gen t1 = P(p) pari_catch_sig_on() - ret = hilbert0(x.g, t0, t1) + ret = hilbert0(x.g, t0.g, t1.g) pari_catch_sig_off() return ret def chinese(self, y): - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(chinese(self.g, t0)) + return P.new_gen(chinese(self.g, t0.g)) def order(self): pari_catch_sig_on() @@ -8871,17 +8828,16 @@ cdef class gen(sage.structure.element.RingElement): sage: f.subst(x, "xyz")^2 xyz^6 + 34*xyz^4 + 6*xyz^3 + 289*xyz^2 + 102*xyz + 9 """ - cdef long n - n = P.get_var(var) - t0GEN(z) + cdef gen t0 = P(z) pari_catch_sig_on() - return P.new_gen(gsubst(self.g, n, t0)) + cdef long n = P.get_var(var) + return P.new_gen(gsubst(self.g, n, t0.g)) def substpol(self, y, z): - t0GEN(y) - t1GEN(z) + cdef gen t0 = P(y) + cdef gen t1 = P(z) pari_catch_sig_on() - return self.new_gen(gsubstpol(self.g, t0, t1)) + return P.new_gen(gsubstpol(self.g, t0.g, t1.g)) def nf_subst(self, z): """ @@ -8920,20 +8876,19 @@ cdef class gen(sage.structure.element.RingElement): sage: Lpari.bnf_get_cyc() # We still have a bnf after substituting [2] """ - cdef GEN nf = self.get_nf() - t0GEN(z) + cdef gen t0 = P(z) pari_catch_sig_on() - return P.new_gen(gsubst(self.g, nf_get_varn(nf), t0)) + return P.new_gen(gsubst(self.g, gvar(self.g), t0.g)) def taylor(self, v=-1): pari_catch_sig_on() - return self.new_gen(tayl(self.g, self.get_var(v), precdl)) + return P.new_gen(tayl(self.g, P.get_var(v), precdl)) def thue(self, rhs, ne): - t0GEN(rhs) - t1GEN(ne) + cdef gen t0 = P(rhs) + cdef gen t1 = P(ne) pari_catch_sig_on() - return self.new_gen(thue(self.g, t0, t1)) + return P.new_gen(thue(self.g, t0.g, t1.g)) def charpoly(self, var=-1, flag=0): """ @@ -8947,9 +8902,9 @@ cdef class gen(sage.structure.element.RingElement): def kronecker(gen self, y): - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return P.new_gen(gkronecker(self.g, t0)) + return P.new_gen(gkronecker(self.g, t0.g)) def type(gen self): @@ -9017,13 +8972,13 @@ cdef class gen(sage.structure.element.RingElement): P(self[i]) = ya[i] for all i). Also return an error estimate on the returned value. """ - t0GEN(ya) - t1GEN(x) + cdef gen t0 = P(ya) + cdef gen t1 = P(x) cdef GEN dy, g pari_catch_sig_on() - g = polint(self.g, t0, t1, &dy) - dif = self.new_gen_noclear(dy) - return self.new_gen(g), dif + g = polint(self.g, t0.g, t1.g, &dy) + dif = P.new_gen_noclear(dy) + return P.new_gen(g), dif def algdep(self, long n): """ @@ -9041,26 +8996,26 @@ cdef class gen(sage.structure.element.RingElement): 210 """ pari_catch_sig_on() - return self.new_gen(algdep(self.g, n)) + return P.new_gen(algdep(self.g, n)) def concat(self, y): - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return self.new_gen(concat(self.g, t0)) + return P.new_gen(concat(self.g, t0.g)) def lindep(self, flag=0): pari_catch_sig_on() - return self.new_gen(lindep0(self.g, flag)) + return P.new_gen(lindep0(self.g, flag)) def listinsert(self, obj, long n): - t0GEN(obj) + cdef gen t0 = P(obj) pari_catch_sig_on() - return self.new_gen(listinsert(self.g, t0, n)) + return P.new_gen(listinsert(self.g, t0.g, n)) def listput(self, obj, long n): - t0GEN(obj) + cdef gen t0 = P(obj) pari_catch_sig_on() - return self.new_gen(listput(self.g, t0, n)) + return P.new_gen(listput(self.g, t0.g, n)) @@ -9171,15 +9126,15 @@ cdef class gen(sage.structure.element.RingElement): sage: E.ellwp(1, flag=1) [13.9658695257485 + 0.E-18*I, 50.5619300880073 ... E-18*I] """ - t0GEN(z) + cdef gen t0 = P(z) pari_catch_sig_on() cdef long dprec - dprec = gprecision(t0) + dprec = gprecision(t0.g) if dprec: dprec = prec_words_to_dec(dprec) else: dprec = prec - return self.new_gen(ellwp0(self.g, t0, flag, n+2, dprec)) + return P.new_gen(ellwp0(self.g, t0.g, flag, n+2, dprec)) def ellchangepoint(self, y): """ @@ -9202,9 +9157,9 @@ cdef class gen(sage.structure.element.RingElement): sage: f.ellisoncurve([-1,4]) True """ - t0GEN(y) + cdef gen t0 = P(y) pari_catch_sig_on() - return self.new_gen(ellchangepoint(self.g, t0)) + return P.new_gen(ellchangepoint(self.g, t0.g)) def debug(gen self, long depth = -1): r""" @@ -9450,16 +9405,6 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): """ return int(self.default('debug')) - cdef GEN toGEN(self, x, int i) except NULL: - cdef gen _x - if PY_TYPE_CHECK(x, gen): - _x = x - return _x.g - - t0heap[i] = self(x) - _x = t0heap[i] - return _x.g - def set_real_precision(self, long n): """ Sets the PARI default real precision. @@ -9502,51 +9447,25 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): def get_series_precision(self): return precdl - - ########################################### - # Create a gen from a GEN object. - # This *steals* a reference to the GEN, as it - # frees the memory the GEN occupied. - ########################################### - - cdef gen new_gen(self, GEN x): - """ - Create a new gen, then free the \*entire\* stack and call - pari_catch_sig_off(). + cdef void clear_stack(self): """ - cdef gen g - g = _new_gen(x) - global mytop, avma - avma = mytop - pari_catch_sig_off() - return g + Call ``pari_catch_sig_off()``, and clear the entire PARI stack + if we are leaving the outermost ``pari_catch_sig_on() ... + pari_catch_sig_off()`` block. - cdef object new_gen_to_string(self, GEN x): """ - Converts a gen to a Python string, free the \*entire\* stack and call - pari_catch_sig_off(). This is meant to be used in place of new_gen(). - """ - cdef char* c - cdef int n - c = GENtostr(x) - s = str(c) - pari_free(c) global mytop, avma - avma = mytop + if _signals.sig_on_count <= 1: + avma = mytop pari_catch_sig_off() - return s - cdef void clear_stack(self): + cdef gen new_gen(self, GEN x): """ - Clear the entire PARI stack and call pari_catch_sig_off(). + Create a new gen wrapping `x`, then call ``clear_stack()``. """ - global mytop, avma - avma = mytop - pari_catch_sig_off() - - cdef void set_mytop_to_avma(self): - global mytop, avma - mytop = avma + cdef gen g = _new_gen(x) + self.clear_stack() + return g cdef gen new_gen_noclear(self, GEN x): """ @@ -9728,13 +9647,10 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): """ Create a new complex number, initialized from re and im. """ - t0GEN(re) - t1GEN(im) - cdef GEN cp + cdef gen t0 = self(re) + cdef gen t1 = self(im) pari_catch_sig_on() - cp = cgetg(3, t_COMPLEX) - set_gel(cp, 1, t0) - set_gel(cp, 2, t1) + cdef GEN cp = mkcomplex(t0.g, t1.g) return self.new_gen(cp) cdef GEN deepcopy_to_python_heap(self, GEN x, pari_sp* address): @@ -9797,15 +9713,13 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): See :func:`pari` for more examples. """ + cdef GEN g cdef int length, i + cdef mpz_t mpz_int cdef gen v - late_import() - if isinstance(s, gen): return s - elif isinstance(s, Integer): - return self.new_gen_from_mpz_t(s + mpz_t_offset) elif PyObject_HasAttrString(s, "_pari_"): return s._pari_() @@ -9815,8 +9729,6 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): return self.new_gen(stoi(PyInt_AS_LONG(s))) if PyBool_Check(s): return self.PARI_ONE if s else self.PARI_ZERO - cdef mpz_t mpz_int - cdef GEN g if PyLong_Check(s): pari_catch_sig_on() mpz_init(mpz_int) @@ -9843,7 +9755,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): return v t = str(s) - pari_catch_sig_str('evaluating PARI string') + pari_catch_sig_on() g = gp_read_str(t) if g == gnil: pari_catch_sig_off() @@ -10384,9 +10296,9 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): sage: cyclotomic_polynomial(8)(2) 17 """ - t0GEN(v) + cdef gen t0 = self(v) pari_catch_sig_on() - return self.new_gen(polcyclo_eval(n, t0)) + return self.new_gen(polcyclo_eval(n, t0.g)) def polsubcyclo(self, long n, long d, v=-1): """ @@ -10454,9 +10366,9 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): ... PariError: incorrect type in setrand """ - t0GEN(seed) + cdef gen t0 = self(seed) pari_catch_sig_on() - setrand(t0) + setrand(t0.g) pari_catch_sig_off() def getrand(self): diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index 612b5dac4f0..397b73b9afe 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -326,7 +326,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): c^4 + 2*c^3 """ pari_catch_sig_on() - return pari.new_gen_to_string(self.val) + return str(pari.new_gen(self.val)) def __hash__(FiniteFieldElement_pari_ffelt self): """ From 97eb512870dc68ce0f70f3eb2ffb2b7f3741c613 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sun, 24 Nov 2013 21:45:46 +0000 Subject: [PATCH 127/206] Trac #11868: remove workarounds for bug related to global GEN variables --- src/sage/libs/pari/gen.pyx | 6 +++++- src/sage/rings/number_field/number_field.py | 8 ++------ src/sage/schemes/elliptic_curves/ell_point.py | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index c662ad8f7da..c928d08a90d 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -7455,14 +7455,18 @@ cdef class gen(sage.structure.element.RingElement): sage: x = polygen(QQ) sage: K. = NumberField(x^3 - x + 1) - sage: pari(K).nfhilbert(t, t+2) # not tested, known bug #11868 + sage: pari(K).nfhilbert(t, t + 2) -1 sage: pari(K).nfhilbert(pari(t), pari(t+2)) -1 sage: P = K.ideal(t^2 + t - 2) # Prime ideal above 5 + sage: pari(K).nfhilbert(t, t + 2, P.pari_prime()) + -1 sage: pari(K).nfhilbert(pari(t), pari(t+2), P.pari_prime()) -1 sage: P = K.ideal(t^2 + 3*t - 1) # Prime ideal above 23, ramified + sage: pari(K).nfhilbert(t, t + 2, P.pari_prime()) + 1 sage: pari(K).nfhilbert(pari(t), pari(t+2), P.pari_prime()) 1 """ diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 6f26f2fe870..ea138427f09 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -7730,9 +7730,7 @@ def hilbert_symbol(self, a, b, P = None): a = self(a) b = self(b) if P is None: - # We MUST convert a and b to pari before calling the function - # to work around Trac #11868 -- Jeroen Demeyer - return pari(self).nfhilbert(pari(a), pari(b)) + return pari(self).nfhilbert(a, b) from sage.rings.morphism import is_RingHomomorphism if is_RingHomomorphism(P): @@ -7759,9 +7757,7 @@ def hilbert_symbol(self, a, b, P = None): raise ValueError, "P (=%s) should be an ideal of self (=%s) in hilbert_symbol, not of %s" % (P, self, P.number_field()) if not P.is_prime(): raise ValueError, "Non-prime ideal P (=%s) in hilbert_symbol" % P - # We MUST convert a and b to pari before calling the function - # to work around Trac #11868 -- Jeroen Demeyer - return pari(self).nfhilbert(pari(a), pari(b), P.pari_prime()) + return pari(self).nfhilbert(a, b, P.pari_prime()) def hilbert_conductor(self,a,b): """ diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index c1623b9448e..fdab1744357 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -432,9 +432,10 @@ def _pari_(self): sage: P._pari_() [Mod(1, 11), Mod(2, 11)] - We need to explicitly call ``pari()`` because of :trac:`11868`:: + We no longer need to explicitly call ``pari(O)`` and ``pari(P)`` + after :trac:`11868`:: - sage: pari(E).elladd(pari(O), pari(P)) + sage: pari(E).elladd(O, P) [Mod(1, 11), Mod(2, 11)] """ if self[2]: From 6e14083fa6e0877587763d4b6e8a1342eeb348c7 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 27 Nov 2013 21:17:43 +0100 Subject: [PATCH 128/206] Trac #11868: Big PARI fix: reviewer patch --- src/sage/libs/pari/gen.pxd | 9 +- src/sage/libs/pari/gen.pyx | 717 +++++++++++++++++++------------------ 2 files changed, 381 insertions(+), 345 deletions(-) diff --git a/src/sage/libs/pari/gen.pxd b/src/sage/libs/pari/gen.pxd index c53d3b50b73..bece49df10e 100644 --- a/src/sage/libs/pari/gen.pxd +++ b/src/sage/libs/pari/gen.pxd @@ -2,7 +2,9 @@ include 'decl.pxi' cimport sage.structure.element cimport sage.structure.parent_base +cimport cython +@cython.final cdef class gen(sage.structure.element.RingElement): cdef GEN g cdef object _refers_to @@ -15,10 +17,11 @@ cdef class gen(sage.structure.element.RingElement): cdef GEN _deepcopy_to_python_heap(self, GEN x, pari_sp* address) cdef long get_var(self, v) +@cython.final cdef class PariInstance(sage.structure.parent_base.ParentWithBase): cdef gen PARI_ZERO, PARI_ONE, PARI_TWO - cdef gen new_gen(self, GEN x) - cdef gen new_gen_noclear(self, GEN x) + cdef inline gen new_gen(self, GEN x) + cdef inline gen new_gen_noclear(self, GEN x) cdef gen new_gen_from_mpz_t(self, mpz_t value) cdef inline GEN _new_GEN_from_mpz_t(self, mpz_t value) cdef gen new_gen_from_mpq_t(self, mpq_t value) @@ -26,7 +29,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): cdef gen new_gen_from_int(self, int value) cdef gen new_t_POL_from_int_star(self, int *vals, int length, long varnum) cdef gen new_gen_from_padic(self, long ordp, long relprec, mpz_t prime, mpz_t p_pow, mpz_t unit) - cdef void clear_stack(self) + cdef inline void clear_stack(self) cdef gen double_to_gen_c(self, double) cdef GEN double_to_GEN(self, double) cdef GEN deepcopy_to_python_heap(self, GEN x, pari_sp* address) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index c928d08a90d..2b637cf68ae 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -185,6 +185,7 @@ include 'sage/ext/python.pxi' include 'sage/ext/interrupt.pxi' cimport libc.stdlib +cimport cython cdef extern from "misc.h": int factorint_withproof_sage(GEN* ans, GEN x, GEN cutoff) @@ -193,6 +194,9 @@ cdef extern from "misc.h": cdef extern from "mpz_pylong.h": cdef int mpz_set_pylong(mpz_t dst, src) except -1 +# Will be imported as needed +Integer = None + # so Galois groups are represented in a sane way # See the polgalois section of the PARI users manual. new_galois_format = 1 @@ -375,18 +379,8 @@ pari_catch_sig_off() # Also a copy of PARI accessible from external pure python code. pari = pari_instance -cdef object Integer - -cdef void late_import(): - global Integer - - if Integer is not None: - return - - import sage.rings.integer - Integer = sage.rings.integer.Integer - +@cython.final cdef class gen(sage.structure.element.RingElement): """ Python extension class that models the PARI GEN type. @@ -416,12 +410,16 @@ cdef class gen(sage.structure.element.RingElement): def __repr__(self): cdef char *c - cdef str s pari_catch_sig_on() + # Use sig_block(), which is needed because GENtostr() uses + # malloc(), which is dangerous inside sig_on() + sig_block() c = GENtostr(self.g) + sig_unblock() + pari_catch_sig_off() + s = str(c) pari_free(c) - pari_catch_sig_off() return s def __hash__(self): @@ -661,7 +659,7 @@ cdef class gen(sage.structure.element.RingElement): return sage.structure.element.bin_op(self, other, operator.mod) def __pow__(gen self, n, m): - cdef gen t0 = P(n) + cdef gen t0 = objtogen(n) pari_catch_sig_on() return P.new_gen(gpow(self.g, t0.g, prec)) @@ -689,7 +687,7 @@ cdef class gen(sage.structure.element.RingElement): # ACCESS ########################################### def getattr(self, attr): - return P(str(self) + '.' + str(attr)) + return objtogen(str(self) + '.' + str(attr)) def mod(self): """ @@ -1569,8 +1567,17 @@ cdef class gen(sage.structure.element.RingElement): 10 sage: int(pari("Mod(2, 7)")) 2 + sage: int(pari(RealField(63)(2^63-1))) + 9223372036854775807L # 32-bit + 9223372036854775807 # 64-bit + sage: int(pari(RealField(63)(2^63+2))) + 9223372036854775810L + """ - late_import() + global Integer + if Integer is None: + import sage.rings.integer + Integer = sage.rings.integer.Integer return int(Integer(self)) def int_unsafe(gen self): @@ -1765,7 +1772,10 @@ cdef class gen(sage.structure.element.RingElement): sage: long(pari("Mod(2, 7)")) 2L """ - late_import() + global Integer + if Integer is None: + import sage.rings.integer + Integer = sage.rings.integer.Integer return long(Integer(self)) def __float__(gen self): @@ -1857,7 +1867,7 @@ cdef class gen(sage.structure.element.RingElement): sage: a.gequal(c) False """ - cdef gen t0 = P(b) + cdef gen t0 = objtogen(b) pari_catch_sig_on() cdef int ret = gequal(a.g, t0.g) pari_catch_sig_off() @@ -1949,11 +1959,10 @@ cdef class gen(sage.structure.element.RingElement): sage: n.isprime(2) False """ - cdef bint t pari_catch_sig_on() - t = (signe(gisprime(self.g, flag)) != 0) - pari_catch_sig_off() - return t + cdef long t = signe(gisprime(self.g, flag)) + P.clear_stack() + return t != 0 def qfbhclassno(gen n): r""" @@ -2029,26 +2038,16 @@ cdef class gen(sage.structure.element.RingElement): sage: n.ispseudoprime(2) False """ - cdef long z pari_catch_sig_on() - z = ispseudoprime(self.g, flag) + cdef long t = ispseudoprime(self.g, flag) pari_catch_sig_off() - return (z != 0) + return t != 0 def ispower(gen self, k=None): r""" Determine whether or not self is a perfect k-th power. If k is not specified, find the largest k so that self is a k-th power. - .. note::: - - There is a BUG in the PARI C-library function (at least in - PARI 2.2.12-beta) that is used to implement this function! This is - in GP:: - - ? p=nextprime(10^100); n=p^100; m=p^2; m^50==n; ispower(n,50) - - INPUT: @@ -2089,7 +2088,7 @@ cdef class gen(sage.structure.element.RingElement): else: return n, P.new_gen(x) else: - t0 = P(k) + t0 = objtogen(k) pari_catch_sig_on() n = ispower(self.g, t0.g, &x) if n == 0: @@ -2107,7 +2106,7 @@ cdef class gen(sage.structure.element.RingElement): 2-dimensional column vector the quotient and the remainder, with respect to v (to main variable if v is omitted). """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(divrem(x.g, t0.g, P.get_var(var))) @@ -2116,7 +2115,7 @@ cdef class gen(sage.structure.element.RingElement): lex(x,y): Compare x and y lexicographically (1 if xy, 0 if x==y, -1 if xy) """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() r = lexcmp(x.g, t0.g) pari_catch_sig_off() @@ -2126,7 +2125,7 @@ cdef class gen(sage.structure.element.RingElement): """ max(x,y): Return the maximum of x and y. """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(gmax(x.g, t0.g)) @@ -2134,7 +2133,7 @@ cdef class gen(sage.structure.element.RingElement): """ min(x,y): Return the minimum of x and y. """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(gmin(x.g, t0.g)) @@ -2161,11 +2160,27 @@ cdef class gen(sage.structure.element.RingElement): def sign(gen x): """ - sign(x): Return the sign of x, where x is of type integer, real or + Return the sign of x, where x is of type integer, real or fraction. + + EXAMPLES:: + + sage: pari(pi).sign() + 1 + sage: pari(0).sign() + 0 + sage: pari(-1/2).sign() + -1 + + PARI throws an error if you attempt to take the sign of a + complex number:: + + sage: pari(I).sign() + Traceback (most recent call last): + ... + PariError: incorrect type in gsigne + """ - # Pari throws an error if you attempt to take the sign of - # a complex number. pari_catch_sig_on() r = gsigne(x.g) pari_catch_sig_off() @@ -2434,7 +2449,7 @@ cdef class gen(sage.structure.element.RingElement): sage: a.type() 't_POLMOD' """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(gmodulo(x.g, t0.g)) @@ -2582,9 +2597,9 @@ cdef class gen(sage.structure.element.RingElement): ... PariError: square discriminant in Qfb """ - cdef gen t0 = P(b) - cdef gen t1 = P(c) - cdef gen t2 = P(D) + cdef gen t0 = objtogen(b) + cdef gen t1 = objtogen(c) + cdef gen t2 = objtogen(D) pari_catch_sig_on() return P.new_gen(Qfb0(a.g, t0.g, t1.g, t2.g, prec)) @@ -2717,7 +2732,11 @@ cdef class gen(sage.structure.element.RingElement): """ cdef char* c pari_catch_sig_on() + # Use sig_block(), which is needed because GENtostr() uses + # malloc(), which is dangerous inside sig_on() + sig_block() c = GENtostr(self.g) + sig_unblock() v = P.new_gen(strtoGENstr(c)) pari_free(c) return v @@ -3033,7 +3052,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(-1).bitand(-1) -1 """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(gbitand(x.g, t0.g)) @@ -3113,7 +3132,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(8+4).bitnegimply(8) 4 """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(gbitnegimply(x.g, t0.g)) @@ -3148,7 +3167,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(13).bitor(1) 13 """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(gbitor(x.g, t0.g)) @@ -3190,9 +3209,9 @@ cdef class gen(sage.structure.element.RingElement): [True, False, True, True, True, True, True, True, True, True] """ pari_catch_sig_on() - b = bool(bittest(x.g, n)) + cdef long b = bittest(x.g, n) pari_catch_sig_off() - return b + return b != 0 def bitxor(gen x, y): """ @@ -3222,7 +3241,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(6).bitxor(0) 6 """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(gbitxor(x.g, t0.g)) @@ -3661,16 +3680,20 @@ cdef class gen(sage.structure.element.RingElement): sage: y.padicprec(17) Traceback (most recent call last): ... - ValueError: not the same prime in padicprec + PariError: not the same prime in padicprec + + This works for polynomials too:: + + sage: R. = PolynomialRing(Zp(3)) + sage: pol = R([O(3^4), O(3^6), O(3^5)]) + sage: pari(pol).padicprec(3) + 4 """ - cdef gen _p - _p = pari(p) - if typ(_p.g) != t_INT: - raise TypeError("p (=%s) must be of type t_INT, but is of type %s."%( - _p, _p.type())) - if not gequal(gel(x.g, 2), _p.g): - raise ValueError("not the same prime in padicprec") - return padicprec(x.g, _p.g) + cdef gen t0 = objtogen(p) + pari_catch_sig_on() + cdef long prec = padicprec(x.g, t0.g) + pari_catch_sig_off() + return prec def padicprime(gen x): """ @@ -4114,7 +4137,7 @@ cdef class gen(sage.structure.element.RingElement): 2147483647 # 32-bit 9223372036854775807 # 64-bit """ - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() v = ggval(x.g, t0.g) pari_catch_sig_off() @@ -4282,7 +4305,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(1+i).agm(-3) -0.964731722290876 + 1.15700282952632*I """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(agm(x.g, t0.g, prec_bits_to_words(precision))) @@ -4459,7 +4482,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2).besselh1(3) 0.486091260585891 - 0.160400393484924*I """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(hbessel1(nu.g, t0.g, prec_bits_to_words(precision))) @@ -4479,7 +4502,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2).besselh2(3) 0.486091260585891 + 0.160400393484924*I """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(hbessel2(nu.g, t0.g, prec_bits_to_words(precision))) @@ -4502,7 +4525,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2).besselj(3) 0.486091260585891 """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(jbessel(nu.g, t0.g, prec_bits_to_words(precision))) @@ -4527,7 +4550,7 @@ cdef class gen(sage.structure.element.RingElement): 0.4127100324 # 32-bit 0.412710032209716 # 64-bit """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(jbesselh(nu.g, t0.g, prec_bits_to_words(precision))) @@ -4553,7 +4576,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2).besseli(3+i) 1.12539407613913 + 2.08313822670661*I """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(ibessel(nu.g, t0.g, prec_bits_to_words(precision))) @@ -4596,7 +4619,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2+i).besselk(300, flag=1) 3.74224603319728 E-132 + 2.49071062641525 E-134*I """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(kbessel(nu.g, t0.g, prec_bits_to_words(precision))) @@ -4617,7 +4640,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(2+i).besseln(3) -0.280775566958244 - 0.486708533223726*I """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(nbessel(nu.g, t0.g, prec_bits_to_words(precision))) @@ -4884,8 +4907,8 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(1).hyperu(2,3) 0.333333333333333 """ - cdef gen t0 = P(b) - cdef gen t1 = P(x) + cdef gen t0 = objtogen(b) + cdef gen t1 = objtogen(x) pari_catch_sig_on() return P.new_gen(hyperu(a.g, t0.g, t1.g, prec_bits_to_words(precision))) @@ -4905,13 +4928,13 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(1+i).incgam(3-i) -0.0458297859919946 + 0.0433696818726677*I """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) cdef gen t1 if y is None: pari_catch_sig_on() return P.new_gen(incgam(s.g, t0.g, prec_bits_to_words(precision))) else: - t1 = P(y) + t1 = objtogen(y) pari_catch_sig_on() return P.new_gen(incgam0(s.g, t0.g, t1.g, prec_bits_to_words(precision))) @@ -4937,7 +4960,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(1).incgamc(2) 0.864664716763387 """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(incgamc(s.g, t0.g, prec_bits_to_words(precision))) @@ -5214,7 +5237,7 @@ cdef class gen(sage.structure.element.RingElement): """ # TODO: ??? lots of good examples in the PARI docs ??? cdef GEN zetan - cdef gen t0 = P(n) + cdef gen t0 = objtogen(n) pari_catch_sig_on() ans = P.new_gen_noclear(gsqrtn(x.g, t0.g, &zetan, prec_bits_to_words(precision))) return ans, P.new_gen(zetan) @@ -5294,7 +5317,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(0.5).theta(2) 1.63202590295260 """ - cdef gen t0 = P(z) + cdef gen t0 = objtogen(z) pari_catch_sig_on() return P.new_gen(theta(q.g, t0.g, prec_bits_to_words(precision))) @@ -5403,7 +5426,7 @@ cdef class gen(sage.structure.element.RingElement): def bezout(gen x, y): cdef gen u, v, g cdef GEN U, V, G - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() G = gbezout(x.g, t0.g, &U, &V) g = P.new_gen_noclear(G) @@ -5443,7 +5466,7 @@ cdef class gen(sage.structure.element.RingElement): a bound for the number of terms in the continued fraction expansion. """ - cdef gen t0 = P(b) + cdef gen t0 = objtogen(b) pari_catch_sig_on() return P.new_gen(contfrac0(x.g, t0.g, lmax)) @@ -5529,30 +5552,30 @@ cdef class gen(sage.structure.element.RingElement): (x and y must be polynomials), 2 use the subresultant algorithm (x and y must be polynomials) """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(ggcd0(x.g, t0.g)) def issquare(gen x, find_root=False): """ - issquare(x,n): true(1) if x is a square, false(0) if not. If - find_root is given, also returns the exact square root if it was - computed. + issquare(x,n): ``True`` if x is a square, ``False`` if not. If + ``find_root`` is given, also returns the exact square root. """ - cdef GEN G, t + cdef GEN G + cdef long t cdef gen g pari_catch_sig_on() if find_root: - t = gissquareall(x.g, &G) - v = bool(P.new_gen_noclear(t)) - if v: - return v, P.new_gen(G) + t = itos(gissquareall(x.g, &G)) + if t: + return True, P.new_gen(G) else: - pari_catch_sig_off() - return v, None + P.clear_stack() + return False, None else: - return P.new_gen(gissquare(x.g)) - + t = itos(gissquare(x.g)) + pari_catch_sig_off() + return t != 0 def issquarefree(gen self): """ @@ -5564,9 +5587,9 @@ cdef class gen(sage.structure.element.RingElement): False """ pari_catch_sig_on() - t = bool(issquarefree(self.g)) + cdef long t = issquarefree(self.g) pari_catch_sig_off() - return t + return t != 0 def lcm(gen x, y): """ @@ -5575,7 +5598,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(10).lcm(15) 30 """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(glcm(x.g, t0.g)) @@ -5810,8 +5833,8 @@ cdef class gen(sage.structure.element.RingElement): sage: e.elladd([1,0], [-1,1]) [-3/4, -15/8] """ - cdef gen t0 = P(z0) - cdef gen t1 = P(z1) + cdef gen t0 = objtogen(z0) + cdef gen t1 = objtogen(z1) pari_catch_sig_on() return P.new_gen(addell(self.g, t0.g, t1.g)) @@ -5849,7 +5872,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellak(0) 0 """ - cdef gen t0 = P(n) + cdef gen t0 = objtogen(n) pari_catch_sig_on() return P.new_gen(akell(self.g, t0.g)) @@ -5944,7 +5967,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellak(-1) 0 """ - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() return P.new_gen(ellap(self.g, t0.g)) @@ -5958,12 +5981,14 @@ cdef class gen(sage.structure.element.RingElement): INPUT: + - ``self`` -- an elliptic curve - - ``n`` - a long integer + - ``n`` -- a long integer - - ``python_ints`` - bool (default is False); if True, - return a list of Python ints instead of a PARI gen wrapper. + - ``python_ints`` -- bool (default is False); if True, + return a list of Python ints instead of a PARI gen wrapper. + .. WARNING:: The curve e must be a medium or long vector of the type given by ellinit. For this function to work for every n and not just those @@ -5971,15 +5996,6 @@ cdef class gen(sage.structure.element.RingElement): If this is not the case, use the function ellminimalmodel first before using ellaplist (or you will get INCORRECT RESULTS!) - - INPUT: - - - - ``e`` - a PARI elliptic curve. - - - ``n`` - an integer - - EXAMPLES:: sage: e = pari([0, -1, 1, -10, -20]).ellinit() @@ -5997,10 +6013,18 @@ cdef class gen(sage.structure.element.RingElement): sage: type(v[0]) + + TESTS:: + + sage: v = e.ellaplist(1) + sage: print v, type(v) + [] + sage: v = e.ellaplist(1, python_ints=True) + sage: print v, type(v) + [] """ - cdef gen t0 - cdef GEN g - cdef long i + if python_ints: + return [int(x) for x in self.ellaplist(n)] if n < 2: pari_catch_sig_on() @@ -6008,20 +6032,15 @@ cdef class gen(sage.structure.element.RingElement): # 1. Make a table of primes up to n. P.init_primes(n+1) - t0 = P(n) + cdef gen t0 = objtogen(n) pari_catch_sig_on() - g = primes(gtolong(primepi(t0.g))) + cdef GEN g = primes(gtolong(primepi(t0.g))) # 2. Replace each prime in the table by ellap of it. - if python_ints: - v = [gtolong(ellap(self.g, gel(g, i + 1))) \ - for i in range(glength(g))] - P.clear_stack() - return v - else: - for i from 0 <= i < glength(g): - set_gel(g, i + 1, ellap(self.g, gel(g, i + 1))) - return self.new_gen(g) + cdef long i + for i from 0 <= i < glength(g): + set_gel(g, i + 1, ellap(self.g, gel(g, i + 1))) + return P.new_gen(g) def ellbil(self, z0, z1): @@ -6044,8 +6063,8 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellbil([1, 0], [-1, 1]) 0.418188984498861 """ - cdef gen t0 = P(z0) - cdef gen t1 = P(z1) + cdef gen t0 = objtogen(z0) + cdef gen t1 = objtogen(z1) pari_catch_sig_on() return P.new_gen(bilhell(self.g, t0.g, t1.g, prec)) @@ -6076,7 +6095,7 @@ cdef class gen(sage.structure.element.RingElement): sage: f[:5] [1, -1, 0, 4, 3] """ - cdef gen t0 = P(ch) + cdef gen t0 = objtogen(ch) pari_catch_sig_on() return P.new_gen(ellchangecurve(self.g, t0.g)) @@ -6145,7 +6164,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellheight([1,0], flag=1) 0.476711659343740 """ - cdef gen t0 = P(a) + cdef gen t0 = objtogen(a) pari_catch_sig_on() return P.new_gen(ellheight0(self.g, t0.g, flag, prec_bits_to_words(precision))) @@ -6173,7 +6192,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellheightmatrix([[1,0], [-1,1]]) [0.476711659343740, 0.418188984498861; 0.418188984498861, 0.686667083305587] """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(mathell(self.g, t0.g, prec)) @@ -6199,11 +6218,11 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellisoncurve([0]) True """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() - t = bool(oncurve(self.g, t0.g) == 1) + cdef int t = oncurve(self.g, t0.g) pari_catch_sig_off() - return t + return t != 0 def elllocalred(self, p): r""" @@ -6356,7 +6375,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.elllocalred(3) [2, -10, [1, 96, 1, 316], 4] """ - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() return P.new_gen(elllocalred(self.g, t0.g)) @@ -6398,8 +6417,8 @@ cdef class gen(sage.structure.element.RingElement): sage: e.elllseries(2.1, A=1.1) 0.402838047956645 """ - cdef gen t0 = P(s) - cdef gen t1 = P(A) + cdef gen t0 = objtogen(s) + cdef gen t1 = objtogen(A) pari_catch_sig_on() return P.new_gen(elllseries(self.g, t0.g, t1.g, prec)) @@ -6468,7 +6487,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellorder([1,0]) 0 """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(orderell(self.g, t0.g)) @@ -6499,7 +6518,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellordinate('z+2*z^2+O(z^4)') [-2*z - 7*z^2 - 23*z^3 + O(z^4), -1 + 2*z + 7*z^2 + 23*z^3 + O(z^4)] """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(ellordinate(self.g, t0.g, prec)) @@ -6525,7 +6544,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellpointtoz([0]) 0 """ - cdef gen t0 = P(pt) + cdef gen t0 = objtogen(pt) pari_catch_sig_on() return P.new_gen(zell(self.g, t0.g, prec_bits_to_words(precision))) @@ -6587,8 +6606,8 @@ cdef class gen(sage.structure.element.RingElement): ....: P0 = e.elladd(e.ellpow(P, cm_minpoly[0]), e.ellpow(P2, cm)) ....: assert(P0 == E(0)) """ - cdef gen t0 = P(z) - cdef gen t1 = P(n) + cdef gen t0 = objtogen(z) + cdef gen t1 = objtogen(n) pari_catch_sig_on() return P.new_gen(powell(self.g, t0.g, t1.g)) @@ -6623,9 +6642,9 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellrootno(1009) 1 """ - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() - rootno = ellrootno(self.g, t0.g) + rootno = ellrootno(self.g, t0.g) pari_catch_sig_off() return rootno @@ -6642,7 +6661,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellsigma(2+i) 1.43490215804166 + 1.80307856719256*I """ - cdef gen t0 = P(z) + cdef gen t0 = objtogen(z) pari_catch_sig_on() return P.new_gen(ellsigma(self.g, t0.g, flag, prec)) @@ -6668,8 +6687,8 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellsub([1,0], [-1,1]) [0, 0] """ - cdef gen t0 = P(z0) - cdef gen t1 = P(z1) + cdef gen t0 = objtogen(z0) + cdef gen t1 = objtogen(z1) pari_catch_sig_on() return P.new_gen(subell(self.g, t0.g, t1.g)) @@ -6752,7 +6771,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellzeta(i-1) -0.350122658523049 - 0.350122658523049*I """ - cdef gen t0 = P(z) + cdef gen t0 = objtogen(z) pari_catch_sig_on() return P.new_gen(ellzeta(self.g, t0.g, prec)) @@ -6786,7 +6805,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e.ellztopoint(0) [0] """ - cdef gen t0 = P(z) + cdef gen t0 = objtogen(z) try: dprec = prec_words_to_dec(z.precision()) except AttributeError: @@ -6874,22 +6893,22 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(bnfinit0(self.g, flag, NULL, prec)) else: - t0 = P(tech) + t0 = objtogen(tech) pari_catch_sig_on() return P.new_gen(bnfinit0(self.g, flag, t0.g, prec)) def bnfisintnorm(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(bnfisintnorm(self.g, t0.g)) def bnfisnorm(self, x, long flag=0): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(bnfisnorm(self.g, t0.g, flag)) def bnfisprincipal(self, x, long flag=1): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(bnfisprincipal0(self.g, t0.g, flag)) @@ -6898,7 +6917,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(buchnarrow(self.g)) def bnfsunit(self, S, long precision=0): - cdef gen t0 = P(S) + cdef gen t0 = objtogen(S) pari_catch_sig_on() return P.new_gen(bnfsunit(self.g, t0.g, prec_bits_to_words(precision))) @@ -6907,7 +6926,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(bnf_get_fu(self.g)) def bnfisunit(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(bnfisunit(self.g, t0.g)) @@ -6929,24 +6948,24 @@ cdef class gen(sage.structure.element.RingElement): sage: K.pari_bnf().bnrclassno(p._pari_bid_()) 3 """ - cdef gen t0 = P(I) + cdef gen t0 = objtogen(I) pari_catch_sig_on() return P.new_gen(bnrclassno(self.g, t0.g)) def bnfissunit(self, sunit_data, x): - cdef gen t0 = P(x) - cdef gen t1 = P(sunit_data) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(sunit_data) pari_catch_sig_on() return P.new_gen(bnfissunit(self.g, t1.g, t0.g)) def dirzetak(self, n): - cdef gen t0 = P(n) + cdef gen t0 = objtogen(n) pari_catch_sig_on() return P.new_gen(dirzetak(self.g, t0.g)) def galoisapply(self, aut, x): - cdef gen t0 = P(aut) - cdef gen t1 = P(x) + cdef gen t0 = objtogen(aut) + cdef gen t1 = objtogen(x) pari_catch_sig_on() return P.new_gen(galoisapply(self.g, t0.g, t1.g)) @@ -6960,40 +6979,40 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(galoisinit(self.g, NULL)) else: - t0 = P(den) + t0 = objtogen(den) pari_catch_sig_on() return P.new_gen(galoisinit(self.g, t0.g)) def galoispermtopol(self, perm): - cdef gen t0 = P(perm) + cdef gen t0 = objtogen(perm) pari_catch_sig_on() return P.new_gen(galoispermtopol(self.g, t0.g)) def galoisfixedfield(self, perm, long flag=0, v=-1): - cdef gen t0 = P(perm) + cdef gen t0 = objtogen(perm) pari_catch_sig_on() return P.new_gen(galoisfixedfield(self.g, t0.g, flag, P.get_var(v))) def idealred(self, I, vdir=0): - cdef gen t0 = P(I) - cdef gen t1 = P(vdir) + cdef gen t0 = objtogen(I) + cdef gen t1 = objtogen(vdir) pari_catch_sig_on() return P.new_gen(idealred0(self.g, t0.g, t1.g if vdir else NULL)) def idealadd(self, x, y): - cdef gen t0 = P(x) - cdef gen t1 = P(y) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(y) pari_catch_sig_on() return P.new_gen(idealadd(self.g, t0.g, t1.g)) def idealaddtoone(self, x, y): - cdef gen t0 = P(x) - cdef gen t1 = P(y) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(y) pari_catch_sig_on() return P.new_gen(idealaddtoone0(self.g, t0.g, t1.g)) def idealappr(self, x, long flag=0): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(idealappr0(self.g, t0.g, flag)) @@ -7017,36 +7036,36 @@ cdef class gen(sage.structure.element.RingElement): sage: nf.idealcoprime(x, y) [5/43, 9/43, -1/43]~ """ - cdef gen t0 = P(x) - cdef gen t1 = P(y) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(y) pari_catch_sig_on() return P.new_gen(idealcoprime(self.g, t0.g, t1.g)) def idealdiv(self, x, y, long flag=0): - cdef gen t0 = P(x) - cdef gen t1 = P(y) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(y) pari_catch_sig_on() return P.new_gen(idealdiv0(self.g, t0.g, t1.g, flag)) def idealfactor(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(idealfactor(self.g, t0.g)) def idealhnf(self, a, b=None): - cdef gen t0 = P(a) + cdef gen t0 = objtogen(a) cdef gen t1 if b is None: pari_catch_sig_on() return P.new_gen(idealhnf(self.g, t0.g)) else: - t1 = P(b) + t1 = objtogen(b) pari_catch_sig_on() return P.new_gen(idealhnf0(self.g, t0.g, t1.g)) def idealintersection(self, x, y): - cdef gen t0 = P(x) - cdef gen t1 = P(y) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(y) pari_catch_sig_on() return P.new_gen(idealintersect(self.g, t0.g, t1.g)) @@ -7105,14 +7124,14 @@ cdef class gen(sage.structure.element.RingElement): sage: nf.ideallog(x, bid) [25]~ """ - cdef gen t0 = P(x) - cdef gen t1 = P(bid) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(bid) pari_catch_sig_on() return P.new_gen(ideallog(self.g, t0.g, t1.g)) def idealmul(self, x, y, long flag=0): - cdef gen t0 = P(x) - cdef gen t1 = P(y) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(y) pari_catch_sig_on() if flag == 0: return P.new_gen(idealmul(self.g, t0.g, t1.g)) @@ -7120,7 +7139,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(idealmulred(self.g, t0.g, t1.g)) def idealnorm(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(idealnorm(self.g, t0.g)) @@ -7140,7 +7159,7 @@ cdef class gen(sage.structure.element.RingElement): sage: F[0].pr_get_p() 5 """ - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() return P.new_gen(idealprimedec(nf.g, t0.g)) @@ -7180,32 +7199,32 @@ cdef class gen(sage.structure.element.RingElement): sage: nf.idealstar(I) [[[43, 9, 5; 0, 1, 0; 0, 0, 1], [0]], [42, [42]], Mat([[43, [9, 1, 0]~, 1, 1, [-5, -9, 1]~], 1]), [[[[42], [[3, 0, 0]~], [[3, 0, 0]~], [Vecsmall([])], 1]], [[], [], []]], Mat(1)] """ - cdef gen t0 = P(I) + cdef gen t0 = objtogen(I) pari_catch_sig_on() return P.new_gen(idealstar0(self.g, t0.g, flag)) def idealtwoelt(self, x, a=None): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) cdef gen t1 if a is None: pari_catch_sig_on() return P.new_gen(idealtwoelt0(self.g, t0.g, NULL)) else: - t1 = P(a) + t1 = objtogen(a) pari_catch_sig_on() return P.new_gen(idealtwoelt0(self.g, t0.g, t1.g)) def idealval(self, x, p): - cdef gen t0 = P(x) - cdef gen t1 = P(p) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(p) pari_catch_sig_on() v = idealval(self.g, t0.g, t1.g) pari_catch_sig_off() return v def elementval(self, x, p): - cdef gen t0 = P(x) - cdef gen t1 = P(p) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(p) pari_catch_sig_on() v = nfval(self.g, t0.g, t1.g) pari_catch_sig_off() @@ -7220,7 +7239,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return self.new_gen(modreverse(self.g)) - def nfbasis(self, long flag=0, fa=0): + def nfbasis(self, long flag=0, fa=None): """ nfbasis(x, flag, fa): integral basis of the field QQ[a], where ``a`` is a root of the polynomial x. @@ -7260,11 +7279,16 @@ cdef class gen(sage.structure.element.RingElement): sage: pari('x^3 - 17').nfbasis(flag = 2) [1, x, 1/3*x^2 - 1/3*x + 1/3] """ - cdef gen t0 = P(fa) - pari_catch_sig_on() - return P.new_gen(nfbasis0(self.g, flag, t0.g if typ(t0.g) == t_MAT else NULL)) + cdef gen t0 + if not fa: + pari_catch_sig_on() + return P.new_gen(nfbasis0(self.g, flag, NULL)) + else: + t0 = objtogen(fa) + pari_catch_sig_on() + return P.new_gen(nfbasis0(self.g, flag, t0.g)) - def nfbasis_d(self, long flag=0, fa=0): + def nfbasis_d(self, long flag=0, fa=None): """ nfbasis_d(x): Return a basis of the number field defined over QQ by x and its discriminant. @@ -7286,12 +7310,17 @@ cdef class gen(sage.structure.element.RingElement): sage: pari([-2,0,0,1]).Polrev().nfbasis_d() ([1, x, x^2], -108) """ + cdef gen t0 cdef GEN disc - cdef gen t0 = P(fa) - pari_catch_sig_on() - B = P.new_gen_noclear(nfbasis(self.g, &disc, flag, t0.g if typ(t0.g) == t_MAT else NULL)) - D = P.new_gen(disc); - return B,D + if not fa: + pari_catch_sig_on() + B = P.new_gen_noclear(nfbasis(self.g, &disc, flag, NULL)) + else: + t0 = objtogen(fa) + pari_catch_sig_on() + B = P.new_gen_noclear(nfbasis(self.g, &disc, flag, t0.g)) + D = P.new_gen(disc) + return B, D def nfbasistoalg(nf, x): r""" @@ -7323,7 +7352,7 @@ cdef class gen(sage.structure.element.RingElement): sage: Kpari.getattr('zk') * pari("[3/2, -5, 0]~") -5/3*y^2 + 5/3*y - 1/6 """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(basistoalg(nf.g, t0.g)) @@ -7356,7 +7385,7 @@ cdef class gen(sage.structure.element.RingElement): sage: Kpari.getattr('zk') * pari("[3/2, -5, 0]~") -5/3*y^2 + 5/3*y - 1/6 """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(gel(basistoalg(nf.g, t0.g), 2)) @@ -7408,8 +7437,8 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(k).nfeltdiveuc(pari(x), pari(y)) [2, -2]~ """ - cdef gen t0 = P(x) - cdef gen t1 = P(y) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(y) pari_catch_sig_on() return P.new_gen(nfdiveuc(self.g, t0.g, t1.g)) @@ -7429,13 +7458,13 @@ cdef class gen(sage.structure.element.RingElement): sage: 12 - k(kp.nfeltreduce(12, I.pari_hnf())) in I True """ - cdef gen t0 = P(x) - cdef gen t1 = P(I) + cdef gen t0 = objtogen(x) + cdef gen t1 = objtogen(I) pari_catch_sig_on() return P.new_gen(nfreduce(self.g, t0.g, t1.g)) def nffactor(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(nffactor(self.g, t0.g)) @@ -7444,7 +7473,7 @@ cdef class gen(sage.structure.element.RingElement): x = f.variable() return x.Mod(f) - def nfhilbert(self, a, b, p = None): + def nfhilbert(self, a, b, p=None): """ nfhilbert(nf,a,b,{p}): if p is omitted, global Hilbert symbol (a,b) in nf, that is 1 if X^2-aY^2-bZ^2 has a non-trivial solution (X,Y,Z) @@ -7457,24 +7486,18 @@ cdef class gen(sage.structure.element.RingElement): sage: K. = NumberField(x^3 - x + 1) sage: pari(K).nfhilbert(t, t + 2) -1 - sage: pari(K).nfhilbert(pari(t), pari(t+2)) - -1 sage: P = K.ideal(t^2 + t - 2) # Prime ideal above 5 sage: pari(K).nfhilbert(t, t + 2, P.pari_prime()) -1 - sage: pari(K).nfhilbert(pari(t), pari(t+2), P.pari_prime()) - -1 sage: P = K.ideal(t^2 + 3*t - 1) # Prime ideal above 23, ramified sage: pari(K).nfhilbert(t, t + 2, P.pari_prime()) 1 - sage: pari(K).nfhilbert(pari(t), pari(t+2), P.pari_prime()) - 1 """ - cdef gen t0 = P(a) - cdef gen t1 = P(b) + cdef gen t0 = objtogen(a) + cdef gen t1 = objtogen(b) cdef gen t2 if p: - t2 = P(p) + t2 = objtogen(p) pari_catch_sig_on() r = nfhilbert0(self.g, t0.g, t1.g, t2.g) else: @@ -7546,7 +7569,7 @@ cdef class gen(sage.structure.element.RingElement): - Aly Deines (2012-09-19) """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(nfhnf(self.g, t0.g)) @@ -7678,34 +7701,34 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(nfsubfields(self.g, d)) def rnfcharpoly(self, T, a, v='x'): - cdef gen t0 = P(T) - cdef gen t1 = P(a) - cdef gen t2 = P(v) + cdef gen t0 = objtogen(T) + cdef gen t1 = objtogen(a) + cdef gen t2 = objtogen(v) pari_catch_sig_on() return P.new_gen(rnfcharpoly(self.g, t0.g, t1.g, gvar(t2.g))) def rnfdisc(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(rnfdiscf(self.g, t0.g)) def rnfeltabstorel(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(rnfelementabstorel(self.g, t0.g)) def rnfeltreltoabs(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(rnfelementreltoabs(self.g, t0.g)) def rnfequation(self, poly, long flag=0): - cdef gen t0 = P(poly) + cdef gen t0 = objtogen(poly) pari_catch_sig_on() return P.new_gen(rnfequation0(self.g, t0.g, flag)) def rnfidealabstorel(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(rnfidealabstorel(self.g, t0.g)) @@ -7730,27 +7753,27 @@ cdef class gen(sage.structure.element.RingElement): sage: rnf.rnfidealdown(P) [2, 0; 0, 2] """ - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(rnfidealdown(self.g, t0.g)) def rnfidealhnf(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(rnfidealhermite(self.g, t0.g)) def rnfidealnormrel(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(rnfidealnormrel(self.g, t0.g)) def rnfidealreltoabs(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(rnfidealreltoabs(self.g, t0.g)) def rnfidealtwoelt(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(rnfidealtwoelement(self.g, t0.g)) @@ -7766,12 +7789,12 @@ cdef class gen(sage.structure.element.RingElement): sage: g = x^5 - x^2 + y sage: L = K.rnfinit(g) """ - cdef gen t0 = P(poly) + cdef gen t0 = objtogen(poly) pari_catch_sig_on() return P.new_gen(rnfinit(self.g, t0.g)) def rnfisfree(self, poly): - cdef gen t0 = P(poly) + cdef gen t0 = objtogen(poly) pari_catch_sig_on() r = rnfisfree(self.g, t0.g) pari_catch_sig_off() @@ -7830,7 +7853,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(deriv(self.g, P.get_var(v))) def eval(self, x): - cdef gen t0 = P(x) + cdef gen t0 = objtogen(x) pari_catch_sig_on() return P.new_gen(poleval(self.g, t0.g)) @@ -7851,7 +7874,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(x^2 - 2).factornf(K.pari_polynomial("a")) [x + Mod(-4*a, 8*a^2 - 1), 1; x + Mod(4*a, 8*a^2 - 1), 1] """ - cdef gen t0 = P(t) + cdef gen t0 = objtogen(t) pari_catch_sig_on() return P.new_gen(polfnf(self.g, t0.g)) @@ -7861,7 +7884,7 @@ cdef class gen(sage.structure.element.RingElement): polynomial x to precision r. flag is optional and may be set to 0 (use round 4) or 1 (use Buchmann-Lenstra) """ - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() return P.new_gen(factorpadic0(self.g, t0.g, r, flag)) @@ -7872,7 +7895,7 @@ cdef class gen(sage.structure.element.RingElement): simple factormod, same except that only the degrees of the irreducible factors are given. """ - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() return P.new_gen(factormod0(self.g, t0.g, flag)) @@ -7889,7 +7912,7 @@ cdef class gen(sage.structure.element.RingElement): x.padicappr(a): p-adic roots of the polynomial x congruent to a mod p """ - cdef gen t0 = P(a) + cdef gen t0 = objtogen(a) pari_catch_sig_on() return P.new_gen(padicappr(self.g, t0.g)) @@ -7904,7 +7927,7 @@ cdef class gen(sage.structure.element.RingElement): sage: x.newtonpoly(3) [1, 1, -1/3, -1/3, -1/3, -1/3, -1/3, -1/3] """ - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() return P.new_gen(newtonpoly(self.g, t0.g)) @@ -7928,7 +7951,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(polcoeff0(self.g, n, P.get_var(var))) def polcompositum(self, pol2, long flag=0): - cdef gen t0 = P(pol2) + cdef gen t0 = objtogen(pol2) pari_catch_sig_on() return P.new_gen(polcompositum0(self.g, t0.g, flag)) @@ -7985,7 +8008,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(galoisconj0(self.g, flag, NULL, prec_bits_to_words(precision))) else: - t0 = P(denom) + t0 = objtogen(denom) pari_catch_sig_on() return P.new_gen(galoisconj0(self.g, flag, t0.g, prec_bits_to_words(precision))) @@ -8007,7 +8030,7 @@ cdef class gen(sage.structure.element.RingElement): sage: nf.nfroots(y^4 + 2) [Mod(-zz, zz^4 + 2), Mod(zz, zz^4 + 2)] """ - cdef gen t0 = P(poly) + cdef gen t0 = objtogen(poly) pari_catch_sig_on() return P.new_gen(nfroots(self.g, t0.g)) @@ -8017,8 +8040,8 @@ cdef class gen(sage.structure.element.RingElement): modulo p to a factorization modulo `p^e` using Hensel lift. The factors in y must be pairwise relatively prime modulo p. """ - cdef gen t0 = P(y) - cdef gen t1 = P(p) + cdef gen t0 = objtogen(y) + cdef gen t1 = objtogen(p) pari_catch_sig_on() return P.new_gen(polhensellift(self.g, t0.g, t1.g, e)) @@ -8028,8 +8051,9 @@ cdef class gen(sage.structure.element.RingElement): non-constant polynomial, or False if f is reducible or constant. """ pari_catch_sig_on() - return bool(self.new_gen(gisirreducible(self.g))) - + cdef long t = itos(gisirreducible(self.g)) + P.clear_stack() + return t != 0 def pollead(self, v=-1): """ @@ -8051,7 +8075,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(polred0(self.g, flag, NULL)) else: - t0 = P(fa) + t0 = objtogen(fa) pari_catch_sig_on() return P.new_gen(polred0(self.g, flag, t0.g)) @@ -8064,7 +8088,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(polredbest(self.g, flag)) def polresultant(self, y, var=-1, flag=0): - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(polresultant0(self.g, t0.g, P.get_var(var), flag)) @@ -8078,23 +8102,23 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(roots0(self.g, flag, prec_bits_to_words(precision))) def polrootsmod(self, p, flag=0): - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() return P.new_gen(rootmod0(self.g, t0.g, flag)) def polrootspadic(self, p, r=20): - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() return P.new_gen(rootpadic(self.g, t0.g, r)) def polrootspadicfast(self, p, r=20): - cdef gen t0 = P(p) + cdef gen t0 = objtogen(p) pari_catch_sig_on() return P.new_gen(rootpadicfast(self.g, t0.g, r)) def polsturm(self, a, b): - cdef gen t0 = P(a) - cdef gen t1 = P(b) + cdef gen t0 = objtogen(a) + cdef gen t1 = objtogen(b) pari_catch_sig_on() n = sturmpart(self.g, t0.g, t1.g) pari_catch_sig_off() @@ -8107,7 +8131,7 @@ cdef class gen(sage.structure.element.RingElement): return n def polsylvestermatrix(self, g): - cdef gen t0 = P(g) + cdef gen t0 = objtogen(g) pari_catch_sig_on() return P.new_gen(sylvestermatrix(self.g, t0.g)) @@ -8116,7 +8140,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(polsym(self.g, n)) def serconvol(self, g): - cdef gen t0 = P(g) + cdef gen t0 = objtogen(g) pari_catch_sig_on() return P.new_gen(convol(self.g, t0.g)) @@ -8151,12 +8175,12 @@ cdef class gen(sage.structure.element.RingElement): def rnfisnorminit(self, polrel, long flag=2): - cdef gen t0 = P(polrel) + cdef gen t0 = objtogen(polrel) pari_catch_sig_on() return P.new_gen(rnfisnorminit(self.g, t0.g, flag)) def rnfisnorm(self, T, long flag=0): - cdef gen t0 = P(T) + cdef gen t0 = objtogen(T) pari_catch_sig_on() return P.new_gen(rnfisnorm(t0.g, self.g, flag)) @@ -8178,13 +8202,13 @@ cdef class gen(sage.structure.element.RingElement): This function uses the PARI row and column indexing, so the first row or column is indexed by 1 instead of 0. """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) cdef gen t1 if z is None: pari_catch_sig_on() return P.new_gen(shallowextract(self.g, t0.g)) else: - t1 = P(z) + t1 = objtogen(z) pari_catch_sig_on() return P.new_gen(extract0(self.g, t0.g, t1.g)) @@ -8229,7 +8253,7 @@ cdef class gen(sage.structure.element.RingElement): EXAMPLES:: - sage: pari('[1,2,3; 4,5,6; 7,8,9]').mattranspose() + sage: pari('[1,2,3; 4,5,6; 7,8,9]').mattranspose() [1, 4, 7; 2, 5, 8; 3, 6, 9] """ pari_catch_sig_on() @@ -8293,8 +8317,8 @@ cdef class gen(sage.structure.element.RingElement): robust, slower implementation, valid for non integral quadratic forms. """ - cdef gen t0 = P(B) - cdef gen t1 = P(max) + cdef gen t0 = objtogen(B) + cdef gen t1 = objtogen(max) pari_catch_sig_on() return P.new_gen(qfminim0(self.g, t0.g, t1.g, flag, precdl)) @@ -8305,7 +8329,7 @@ cdef class gen(sage.structure.element.RingElement): digits of flag mean 1: count vectors of even norm from 1 to 2B, 2: return a t_VECSMALL instead of a t_VEC. """ - cdef gen t0 = P(B) + cdef gen t0 = objtogen(B) pari_catch_sig_on() return P.new_gen(qfrep0(self.g, t0.g, flag)) @@ -8332,7 +8356,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari('[1,1;1,-1]').matsolve(pari('[1;0]')) [1/2; 1/2] """ - cdef gen t0 = P(B) + cdef gen t0 = objtogen(B) pari_catch_sig_on() return P.new_gen(gauss(self.g, t0.g)) @@ -8378,8 +8402,8 @@ cdef class gen(sage.structure.element.RingElement): sage: M2.matsolvemod(9, pari('[2,45]~'), 1) [[1, 1]~, [-1, -4; 1, -5]] """ - cdef gen t0 = P(D) - cdef gen t1 = P(B) + cdef gen t0 = objtogen(D) + cdef gen t1 = objtogen(B) pari_catch_sig_on() return P.new_gen(matsolvemod0(self.g, t0.g, t1.g, flag)) @@ -8528,7 +8552,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(M).mathnfmod(12) [1, 0, 0; 0, 2, 0; 0, 0, 6] """ - cdef gen t0 = P(d) + cdef gen t0 = objtogen(d) pari_catch_sig_on() return P.new_gen(hnfmod(self.g, t0.g)) @@ -8557,7 +8581,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(M).mathnfmod(6) [1, 0, 0; 0, 1, 0; 0, 0, 6] """ - cdef gen t0 = P(d) + cdef gen t0 = objtogen(d) pari_catch_sig_on() return P.new_gen(hnfmodid(self.g, t0.g)) @@ -8688,15 +8712,15 @@ cdef class gen(sage.structure.element.RingElement): ########################################### def hilbert(x, y, p): - cdef gen t0 = P(y) - cdef gen t1 = P(p) + cdef gen t0 = objtogen(y) + cdef gen t1 = objtogen(p) pari_catch_sig_on() ret = hilbert0(x.g, t0.g, t1.g) pari_catch_sig_off() return ret def chinese(self, y): - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(chinese(self.g, t0.g)) @@ -8832,14 +8856,13 @@ cdef class gen(sage.structure.element.RingElement): sage: f.subst(x, "xyz")^2 xyz^6 + 34*xyz^4 + 6*xyz^3 + 289*xyz^2 + 102*xyz + 9 """ - cdef gen t0 = P(z) + cdef gen t0 = objtogen(z) pari_catch_sig_on() - cdef long n = P.get_var(var) - return P.new_gen(gsubst(self.g, n, t0.g)) + return P.new_gen(gsubst(self.g, P.get_var(var), t0.g)) def substpol(self, y, z): - cdef gen t0 = P(y) - cdef gen t1 = P(z) + cdef gen t0 = objtogen(y) + cdef gen t1 = objtogen(z) pari_catch_sig_on() return P.new_gen(gsubstpol(self.g, t0.g, t1.g)) @@ -8880,7 +8903,7 @@ cdef class gen(sage.structure.element.RingElement): sage: Lpari.bnf_get_cyc() # We still have a bnf after substituting [2] """ - cdef gen t0 = P(z) + cdef gen t0 = objtogen(z) pari_catch_sig_on() return P.new_gen(gsubst(self.g, gvar(self.g), t0.g)) @@ -8889,8 +8912,8 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(tayl(self.g, P.get_var(v), precdl)) def thue(self, rhs, ne): - cdef gen t0 = P(rhs) - cdef gen t1 = P(ne) + cdef gen t0 = objtogen(rhs) + cdef gen t1 = objtogen(ne) pari_catch_sig_on() return P.new_gen(thue(self.g, t0.g, t1.g)) @@ -8906,7 +8929,7 @@ cdef class gen(sage.structure.element.RingElement): def kronecker(gen self, y): - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(gkronecker(self.g, t0.g)) @@ -8976,8 +8999,8 @@ cdef class gen(sage.structure.element.RingElement): P(self[i]) = ya[i] for all i). Also return an error estimate on the returned value. """ - cdef gen t0 = P(ya) - cdef gen t1 = P(x) + cdef gen t0 = objtogen(ya) + cdef gen t1 = objtogen(x) cdef GEN dy, g pari_catch_sig_on() g = polint(self.g, t0.g, t1.g, &dy) @@ -9003,7 +9026,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(algdep(self.g, n)) def concat(self, y): - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(concat(self.g, t0.g)) @@ -9012,12 +9035,12 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(lindep0(self.g, flag)) def listinsert(self, obj, long n): - cdef gen t0 = P(obj) + cdef gen t0 = objtogen(obj) pari_catch_sig_on() return P.new_gen(listinsert(self.g, t0.g, n)) def listput(self, obj, long n): - cdef gen t0 = P(obj) + cdef gen t0 = objtogen(obj) pari_catch_sig_on() return P.new_gen(listput(self.g, t0.g, n)) @@ -9130,7 +9153,7 @@ cdef class gen(sage.structure.element.RingElement): sage: E.ellwp(1, flag=1) [13.9658695257485 + 0.E-18*I, 50.5619300880073 ... E-18*I] """ - cdef gen t0 = P(z) + cdef gen t0 = objtogen(z) pari_catch_sig_on() cdef long dprec dprec = gprecision(t0.g) @@ -9161,7 +9184,7 @@ cdef class gen(sage.structure.element.RingElement): sage: f.ellisoncurve([-1,4]) True """ - cdef gen t0 = P(y) + cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(ellchangepoint(self.g, t0.g)) @@ -9248,6 +9271,7 @@ cdef void sage_pariErr_flush(): pass +@cython.final cdef class PariInstance(sage.structure.parent_base.ParentWithBase): def __init__(self, long size=1000000, unsigned long maxprime=500000): """ @@ -9451,7 +9475,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): def get_series_precision(self): return precdl - cdef void clear_stack(self): + cdef inline void clear_stack(self): """ Call ``pari_catch_sig_off()``, and clear the entire PARI stack if we are leaving the outermost ``pari_catch_sig_on() ... @@ -9463,7 +9487,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): avma = mytop pari_catch_sig_off() - cdef gen new_gen(self, GEN x): + cdef inline gen new_gen(self, GEN x): """ Create a new gen wrapping `x`, then call ``clear_stack()``. """ @@ -9471,7 +9495,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): self.clear_stack() return g - cdef gen new_gen_noclear(self, GEN x): + cdef inline gen new_gen_noclear(self, GEN x): """ Create a new gen, but don't free any memory on the stack and don't call pari_catch_sig_off(). @@ -9654,8 +9678,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): cdef gen t0 = self(re) cdef gen t1 = self(im) pari_catch_sig_on() - cdef GEN cp = mkcomplex(t0.g, t1.g) - return self.new_gen(cp) + return self.new_gen(mkcomplex(t0.g, t1.g)) cdef GEN deepcopy_to_python_heap(self, GEN x, pari_sp* address): return deepcopy_to_python_heap(x, address) @@ -9717,54 +9740,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): See :func:`pari` for more examples. """ - cdef GEN g - cdef int length, i - cdef mpz_t mpz_int - cdef gen v - - if isinstance(s, gen): - return s - elif PyObject_HasAttrString(s, "_pari_"): - return s._pari_() - - # Check basic Python types - if PyInt_Check(s): - pari_catch_sig_on() - return self.new_gen(stoi(PyInt_AS_LONG(s))) - if PyBool_Check(s): - return self.PARI_ONE if s else self.PARI_ZERO - if PyLong_Check(s): - pari_catch_sig_on() - mpz_init(mpz_int) - mpz_set_pylong(mpz_int, s) - g = self._new_GEN_from_mpz_t(mpz_int) - mpz_clear(mpz_int) - return self.new_gen(g) - if PyFloat_Check(s): - pari_catch_sig_on() - return self.new_gen(dbltor(PyFloat_AS_DOUBLE(s))) - if PyComplex_Check(s): - pari_catch_sig_on() - g = cgetg(3, t_COMPLEX) - set_gel(g, 1, dbltor(PyComplex_RealAsDouble(s))) - set_gel(g, 2, dbltor(PyComplex_ImagAsDouble(s))) - return self.new_gen(g) - - if isinstance(s, (types.ListType, types.XRangeType, - types.TupleType, types.GeneratorType)): - length = len(s) - v = self._empty_vector(length) - for i from 0 <= i < length: - v[i] = self(s[i]) - return v - - t = str(s) - pari_catch_sig_on() - g = gp_read_str(t) - if g == gnil: - pari_catch_sig_off() - return None - return self.new_gen(g) + return objtogen(s) cdef GEN _new_GEN_from_mpz_t_matrix(self, mpz_t** B, Py_ssize_t nr, Py_ssize_t nc): r""" @@ -10552,6 +10528,63 @@ cdef int init_stack(size_t requested_size) except -1: sig_off() +cdef gen objtogen(s): + """Convert any Sage/Python object to a PARI gen""" + cdef GEN g + cdef Py_ssize_t length, i + cdef mpz_t mpz_int + cdef gen v + + if isinstance(s, gen): + return s + try: + return s._pari_() + except AttributeError: + pass + + # Check basic Python types. Start with strings, which are a very + # common case. + if PyString_Check(s): + pari_catch_sig_on() + g = gp_read_str(PyString_AsString(s)) + if g == gnil: + P.clear_stack() + return None + return P.new_gen(g) + if PyInt_Check(s): + pari_catch_sig_on() + return P.new_gen(stoi(PyInt_AS_LONG(s))) + if PyBool_Check(s): + return P.PARI_ONE if s else P.PARI_ZERO + if PyLong_Check(s): + pari_catch_sig_on() + mpz_init(mpz_int) + mpz_set_pylong(mpz_int, s) + g = P._new_GEN_from_mpz_t(mpz_int) + mpz_clear(mpz_int) + return P.new_gen(g) + if PyFloat_Check(s): + pari_catch_sig_on() + return P.new_gen(dbltor(PyFloat_AS_DOUBLE(s))) + if PyComplex_Check(s): + pari_catch_sig_on() + g = cgetg(3, t_COMPLEX) + set_gel(g, 1, dbltor(PyComplex_RealAsDouble(s))) + set_gel(g, 2, dbltor(PyComplex_ImagAsDouble(s))) + return P.new_gen(g) + + if isinstance(s, (types.ListType, types.XRangeType, + types.TupleType, types.GeneratorType)): + length = len(s) + v = P._empty_vector(length) + for i from 0 <= i < length: + v[i] = objtogen(s[i]) + return v + + # Simply use the string representation + return objtogen(str(s)) + + cdef GEN deepcopy_to_python_heap(GEN x, pari_sp* address): cdef size_t s = gsizebyte(x) cdef pari_sp tmp_bot, tmp_top From 07361276be83e38b9aebfb0ab01d2fe0068c7e20 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 22 Aug 2013 15:55:46 +0200 Subject: [PATCH 129/206] Trac #15079: unique_round for RIF --- src/sage/rings/real_mpfi.pyx | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index f0cefbaa1c9..31584bd1ef3 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -3035,6 +3035,65 @@ cdef class RealIntervalFieldElement(sage.structure.element.RingElement): else: raise ValueError("interval does not have a unique ceil") + def unique_round(self): + """ + Returns the unique round (nearest integer) of this interval, + if it is well defined, otherwise raises a ``ValueError``. + + OUTPUT: + + - an integer. + + EXAMPLES:: + + sage: RIF(pi).unique_round() + 3 + sage: RIF(1000*pi).unique_round() + 3142 + sage: RIF(100, 200).unique_round() + Traceback (most recent call last): + ... + ValueError: interval does not have a unique round (nearest integer) + sage: RIF(1.2, 1.7).unique_round() + Traceback (most recent call last): + ... + ValueError: interval does not have a unique round (nearest integer) + sage: RIF(0.7, 1.2).unique_round() + 1 + sage: RIF(-pi).unique_round() + -3 + sage: (RIF(4.5).unique_round(), RIF(-4.5).unique_round()) + (5, -5) + + TESTS:: + + sage: RIF(-1/2, -1/3).unique_round() + Traceback (most recent call last): + ... + ValueError: interval does not have a unique round (nearest integer) + sage: RIF(-1/2, 1/3).unique_round() + Traceback (most recent call last): + ... + ValueError: interval does not have a unique round (nearest integer) + sage: RIF(-1/3, 1/3).unique_round() + 0 + sage: RIF(-1/2, 0).unique_round() + Traceback (most recent call last): + ... + ValueError: interval does not have a unique round (nearest integer) + sage: RIF(1/2).unique_round() + 1 + sage: RIF(-1/2).unique_round() + -1 + sage: RIF(0).unique_round() + 0 + """ + a, b = self.lower().round(), self.upper().round() + if a == b: + return a + else: + raise ValueError("interval does not have a unique round (nearest integer)") + def unique_integer(self): """ Return the unique integer in this interval, if there is exactly one, From 7c65f4c9e18d4b3a2eb5f9a1bc7f42bb70dfc564 Mon Sep 17 00:00:00 2001 From: Joris Vankerschaver Date: Wed, 3 Jul 2013 18:31:24 +0100 Subject: [PATCH 130/206] Trac #10132: Parametrization of (metric) surfaces in 3D Euclidean space --- src/doc/en/reference/index.rst | 1 + .../en/reference/riemannian_geometry/conf.py | 73 + .../reference/riemannian_geometry/index.rst | 10 + src/sage/all.py | 1 + .../geometry/riemannian_manifolds/__init__.py | 1 + src/sage/geometry/riemannian_manifolds/all.py | 6 + .../parametrized_surface3d.py | 1684 +++++++++++++++++ .../surface3d_generators.py | 457 +++++ 8 files changed, 2233 insertions(+) create mode 100644 src/doc/en/reference/riemannian_geometry/conf.py create mode 100644 src/doc/en/reference/riemannian_geometry/index.rst create mode 100644 src/sage/geometry/riemannian_manifolds/__init__.py create mode 100644 src/sage/geometry/riemannian_manifolds/all.py create mode 100644 src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py create mode 100644 src/sage/geometry/riemannian_manifolds/surface3d_generators.py diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index 4278d60caed..c1d40d89baa 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -91,6 +91,7 @@ Geometry and Topology * :doc:`Combinatorial Geometry ` * :doc:`Cell Complexes and their Homology ` * :doc:`Differential Forms ` +* :doc:`Parametrized Surfaces ` Number Theory, Algebraic Geometry --------------------------------- diff --git a/src/doc/en/reference/riemannian_geometry/conf.py b/src/doc/en/reference/riemannian_geometry/conf.py new file mode 100644 index 00000000000..ae3b7eaf67f --- /dev/null +++ b/src/doc/en/reference/riemannian_geometry/conf.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# +# Sage documentation build configuration file, created by +# sphinx-quickstart on Thu Aug 21 20:15:55 2008. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# The contents of this file are pickled, so don't put values in the namespace +# that aren't pickleable (module imports are okay, they're removed automatically). +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os +sys.path.append(os.environ['SAGE_DOC']) +from common.conf import * + +# settings for the intersphinx extension: + +ref_src = os.path.join(SAGE_DOC, 'en', 'reference') +ref_out = os.path.join(SAGE_DOC, 'output', 'html', 'en', 'reference') +intersphinx_mapping[ref_out] = None + +for doc in os.listdir(ref_src): + if os.path.exists(os.path.join(ref_src, doc, 'index.rst')): + intersphinx_mapping[os.path.join(ref_out, doc)] = None + +# We use the main document's title, if we can find it. +rst_file = open('index.rst', 'r') +rst_lines = rst_file.read().splitlines() +rst_file.close() + +title = u'' +for i in xrange(len(rst_lines)): + if rst_lines[i].startswith('==') and i > 0: + title = rst_lines[i-1].strip() + break + +# Otherwise, we use this directory's name. +name = os.path.basename(os.path.abspath('.')) +if not title: + title = name.capitalize() +title = title.replace(u'`', u'$') + +# General information about the project. +project = u'Sage Reference Manual: ' + title + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = u'Sage Reference Manual v' + release + ': ' + title + +# A shorter title for the navigation bar. Default is the same as html_title. +html_short_title = title + +# HTML theme (e.g., 'default', 'sphinxdoc'). The pages for the +# reference manual use a custom theme, a slight variant on the 'sage' +# theme, to set the links in the top line. +html_theme = 'sageref' + +# Output file base name for HTML help builder. +htmlhelp_basename = name + +# Grouping the document tree into LaTeX files. List of tuples (source +# start file, target name, title, author, document class +# [howto/manual]). +latex_documents = [ +('index', name + '.tex', project, u'The Sage Development Team', 'manual') +] + +#Ignore all .rst in the _sage subdirectory +exclude_trees = exclude_trees + ['_sage'] + +multidocs_is_master = False diff --git a/src/doc/en/reference/riemannian_geometry/index.rst b/src/doc/en/reference/riemannian_geometry/index.rst new file mode 100644 index 00000000000..caa6ea15685 --- /dev/null +++ b/src/doc/en/reference/riemannian_geometry/index.rst @@ -0,0 +1,10 @@ +Differential Geometry of Curves and Surfaces +============================================ + +.. toctree:: + :maxdepth: 2 + + sage/geometry/riemannian_manifolds/parametrized_surface3d + sage/geometry/riemannian_manifolds/surface3d_generators + +.. include:: ../footer.txt diff --git a/src/sage/all.py b/src/sage/all.py index 0301640ac66..4e2be778a4e 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -118,6 +118,7 @@ from sage.geometry.all import * from sage.geometry.triangulation.all import * +from sage.geometry.riemannian_manifolds.all import * from sage.dynamics.all import * diff --git a/src/sage/geometry/riemannian_manifolds/__init__.py b/src/sage/geometry/riemannian_manifolds/__init__.py new file mode 100644 index 00000000000..2ef28f2f31e --- /dev/null +++ b/src/sage/geometry/riemannian_manifolds/__init__.py @@ -0,0 +1 @@ +# This comment is here so the file is non-empty (so Mercurial will check it in). diff --git a/src/sage/geometry/riemannian_manifolds/all.py b/src/sage/geometry/riemannian_manifolds/all.py new file mode 100644 index 00000000000..a3e8d2691d5 --- /dev/null +++ b/src/sage/geometry/riemannian_manifolds/all.py @@ -0,0 +1,6 @@ +from sage.misc.lazy_import import lazy_import +lazy_import('sage.geometry.riemannian_manifolds.parametrized_surface3d', + 'ParametrizedSurface3D') +lazy_import('sage.geometry.riemannian_manifolds.surface3d_generators', + 'surfaces') + diff --git a/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py b/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py new file mode 100644 index 00000000000..e054b82c1a1 --- /dev/null +++ b/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py @@ -0,0 +1,1684 @@ +""" +Differential Geometry of Parametrized Surfaces. + +AUTHORS: + - Mikhail Malakhaltsev (2010-09-25): initial version + - Joris Vankerschaver (2010-10-25): implementation, doctests + +""" +#***************************************************************************** +# Copyright (C) 2010 Mikhail Malakhaltsev +# Copyright (C) 2010 Joris Vankerschaver +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from itertools import product + +from sage.structure.sage_object import SageObject +from sage.modules.free_module_element import vector +from sage.matrix.constructor import matrix +from sage.calculus.functional import diff +from sage.functions.other import sqrt +from sage.misc.cachefunc import cached_method +from sage.symbolic.ring import SR +from sage.symbolic.constants import pi +from sage.symbolic.assumptions import assume + +def _simplify_full_rad(f): + """ + Helper function to conveniently call :meth:`simplify_full` and + :meth:`simplify_radical` in succession. + + INPUT: + + - ``f`` - a symbolic expression. + + EXAMPLES:: + + sage: from sage.geometry.riemannian_manifolds.parametrized_surface3d import _simplify_full_rad + sage: _simplify_full_rad(sqrt(x^2)/x) + 1 + + """ + return f.simplify_full().simplify_radical() + + +class ParametrizedSurface3D(SageObject): + r""" + Class representing a parametrized two-dimensional surface in + Euclidian three-space. Provides methods for calculating the main + geometrical objects related to such a surface, such as the first + and the second fundamental form, the total (Gaussian) and the mean + curvature, the geodesic curves, parallel transport, etc. + + + INPUT: + + - ``surface_equation`` -- a 3-tuple of functions specifying a parametric + representation of the surface. + + - ``variables`` -- a 2-tuple of intrinsic coordinates `(u, v)` on the + surface, with `u` and `v` symbolic variables, or a 2-tuple of triples + $(u, u_{min}, u_{max})$, + $(v, v_{min}, v_{max})$ when the parameter range + for the coordinates is known. + + - ``name`` -- name of the surface (optional). + + + .. note:: + + Throughout the documentation, we use the Einstein summation + convention: whenever an index appears twice, once as a + subscript, and once as a superscript, summation over that index + is implied. For instance, `g_{ij} g^{jk}` stands for `\sum_j + g_{ij}g^{jk}`. + + + EXAMPLES: + + We give several examples of standard surfaces in differential + geometry. First, let's construct an elliptic paraboloid by + explicitly specifying its parametric equation:: + + sage: u, v = var('u,v', domain='real') + sage: eparaboloid = ParametrizedSurface3D((u, v, u^2 + v^2), (u, v),'elliptic paraboloid'); eparaboloid + Parametrized surface ('elliptic paraboloid') with equation (u, v, u^2 + v^2) + + When the ranges for the intrinsic coordinates are known, they can be + specified explicitly. This is mainly useful for plotting. Here we + construct half of an ellipsoid:: + + sage: u1, u2 = var ('u1, u2', domain='real'); + sage: coords = ((u1, -pi/2, pi/2), (u2, 0, pi)) + sage: ellipsoid_eq = (cos(u1)*cos(u2), 2*sin(u1)*cos(u2), 3*sin(u2)) + sage: ellipsoid = ParametrizedSurface3D(ellipsoid_eq, coords, 'ellipsoid'); ellipsoid + Parametrized surface ('ellipsoid') with equation (cos(u1)*cos(u2), 2*cos(u2)*sin(u1), 3*sin(u2)) + sage: ellipsoid.plot() + + Standard surfaces can be constructed using the ``surfaces`` generator:: + + sage: klein = surfaces.Klein(); klein + Parametrized surface ('Klein bottle') with equation (-(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*cos(u), -(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*sin(u), cos(1/2*u)*sin(2*v) + sin(1/2*u)*sin(v)) + + Latex representation of the surfaces:: + + sage: u, v = var('u, v', domain='real') + sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v), 'sphere') + sage: print latex(sphere) + \left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right) + sage: print sphere._latex_() + \left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right) + sage: print sphere + Parametrized surface ('sphere') with equation (cos(u)*cos(v), cos(v)*sin(u), sin(v)) + + To plot a parametric surface, use the :meth:`plot` member function:: + + sage: enneper = surfaces.Enneper(); enneper + Parametrized surface ('Enneper's surface') with equation (-1/9*(u^2 - 3*v^2 - 3)*u, -1/9*(3*u^2 - v^2 + 3)*v, 1/3*u^2 - 1/3*v^2) + sage: enneper.plot(aspect_ratio='automatic') + + We construct an ellipsoid whose axes are given by symbolic variables `a`, + `b` and `c`, and find the natural frame of tangent vectors, + expressed in intrinsic coordinates. Note that the result is a + dictionary of vector fields:: + + sage: a, b, c = var('a, b, c', domain='real') + sage: u1, u2 = var('u1, u2', domain='real') + sage: ellipsoid_eq = (a*cos(u1)*cos(u2), b*sin(u1)*cos(u2), c*sin(u2)) + sage: ellipsoid = ParametrizedSurface3D(ellipsoid_eq, (u1, u2), 'Symbolic ellipsoid'); ellipsoid + Parametrized surface ('Symbolic ellipsoid') with equation (a*cos(u1)*cos(u2), b*cos(u2)*sin(u1), c*sin(u2)) + + sage: ellipsoid.natural_frame() + {1: (-a*cos(u2)*sin(u1), b*cos(u1)*cos(u2), 0), 2: (-a*cos(u1)*sin(u2), -b*sin(u1)*sin(u2), c*cos(u2))} + + We find the normal vector field to the surface. The normal vector + field is the vector product of the vectors of the natural frame, + and is given by:: + + sage: ellipsoid.normal_vector() + (b*c*cos(u1)*cos(u2)^2, a*c*cos(u2)^2*sin(u1), a*b*cos(u2)*sin(u2)) + + By default, the normal vector field is not normalized. To obtain + the unit normal vector field of the elliptic paraboloid, we put:: + + sage: u, v = var('u,v', domain='real') + sage: eparaboloid = ParametrizedSurface3D([u,v,u^2+v^2],[u,v],'elliptic paraboloid') + sage: eparaboloid.normal_vector(normalized=True) + (-2*u/sqrt(4*u^2 + 4*v^2 + 1), -2*v/sqrt(4*u^2 + 4*v^2 + 1), 1/sqrt(4*u^2 + 4*v^2 + 1)) + + Now let us compute the coefficients of the first fundamental form of the torus:: + + sage: u, v = var('u, v', domain='real') + sage: a, b = var('a, b', domain='real') + sage: torus = ParametrizedSurface3D(((a + b*cos(u))*cos(v),(a + b*cos(u))*sin(v), b*sin(u)),[u,v],'torus') + sage: torus.first_fundamental_form_coefficients() + {(1, 2): 0, (1, 1): b^2, (2, 1): 0, (2, 2): b^2*cos(u)^2 + 2*a*b*cos(u) + a^2} + + The first fundamental form can be used to compute the length of a + curve on the surface. For example, let us find the length of the + curve $u^1 = t$, $u^2 = t$, $t \in [0,2\pi]$, on the ellipsoid + with axes $a=1$, $b=1.5$ and $c=1$. So we take the curve:: + + sage: t = var('t', domain='real') + sage: u1 = t + sage: u2 = t + + Then find the tangent vector:: + + sage: du1 = diff(u1,t) + sage: du2 = diff(u2,t) + sage: du = vector([du1, du2]); du + (1, 1) + + Once we specify numerical values for the axes of the ellipsoid, we can + determine the numerical value of the length integral:: + + sage: L = sqrt(ellipsoid.first_fundamental_form(du, du).substitute(u1=u1,u2=u2)) + sage: print numerical_integral(L.substitute(a=2, b=1.5, c=1),0,1)[0] + 2.00127905972 + + We find the area of the sphere of radius $R$:: + + sage: R = var('R', domain='real'); + sage: u, v = var('u,v', domain='real'); + sage: assume(R>0) + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere') + sage: integral(integral(sphere.area_form(),u,0,2*pi),v,-pi/2,pi/2) + 4*pi*R^2 + + We can find an orthonormal frame field $\{e_1, e_2\}$ of a surface + and calculate its structure functions. Let us first determine the + orthonormal frame field for the elliptic paraboloid:: + + sage: u, v = var('u,v', domain='real') + sage: eparaboloid = ParametrizedSurface3D([u,v,u^2+v^2],[u,v],'elliptic paraboloid') + sage: eparaboloid.orthonormal_frame() + {1: (1/sqrt(4*u^2 + 1), 0, 2*u/sqrt(4*u^2 + 1)), 2: (-4*u*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)), sqrt(4*u^2 + 1)/sqrt(4*u^2 + 4*v^2 + 1), 2*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)))} + + We can express the orthogonal frame field both in exterior + coordinates (i.e. expressed as vector field fields in the ambient + space $\RR^3$, the default) or in intrinsic coordinates + (with respect to the natural frame). Here we use intrinsic + coordinates:: + + sage: eparaboloid.orthonormal_frame(coordinates='int') + {1: (1/sqrt(4*u^2 + 1), 0), 2: (-4*u*v/(sqrt(4*u^2 + 4*v^2 + 1)*sqrt(4*u^2 + 1)), sqrt(4*u^2 + 1)/sqrt(4*u^2 + 4*v^2 + 1))} + + Using the orthonormal frame in interior coordinates, we can calculate + the structure functions $c^k_{ij}$ of the surface, defined by + $[e_i,e_j] = c^k_{ij} e_k$, where $[e_i, e_j]$ represents the Lie + bracket of two frame vector fields $e_i, e_j$. For the + elliptic paraboloid, we get:: + + sage: EE = eparaboloid.orthonormal_frame(coordinates='int') + sage: E1 = EE[1]; E2 = EE[2] + sage: CC = eparaboloid.frame_structure_functions(E1,E2) + sage: CC[1,2,1].simplify_full() + 4*sqrt(4*u^2 + 4*v^2 + 1)*v/((16*u^4 + 4*(4*u^2 + 1)*v^2 + 8*u^2 + 1)*sqrt(4*u^2 + 1)) + + We compute the Gaussian and mean curvatures of the sphere:: + + sage: sphere = surfaces.Sphere(); sphere + Parametrized surface ('Sphere') with equation (cos(u)*cos(v), cos(v)*sin(u), sin(v)) + sage: K = sphere.gauss_curvature(); K # Not tested -- see trac 12737 + 1 + sage: H = sphere.mean_curvature(); H # Not tested -- see trac 12737 + -1 + + We can easily generate a color plot of the Gaussian curvature of a surface. + Here we deal with the ellipsoid:: + + sage: u1, u2 = var('u1,u2', domain='real'); + sage: u = [u1,u2] + sage: ellipsoid_equation(u1,u2) = [2*cos(u1)*cos(u2),1.5*cos(u1)*sin(u2),sin(u1)] + sage: ellipsoid = ParametrizedSurface3D(ellipsoid_equation(u1,u2), [u1, u2],'ellipsoid') + sage: # set intervals for variables and the number of division points + sage: u1min, u1max = -1.5, 1.5 + sage: u2min, u2max = 0, 6.28 + sage: u1num, u2num = 10, 20 + sage: # make the arguments array + sage: from numpy import linspace + sage: u1_array = linspace(u1min, u1max, u1num) + sage: u2_array = linspace(u2min, u2max, u2num) + sage: u_array = [ (uu1,uu2) for uu1 in u1_array for uu2 in u2_array] + sage: # Find the gaussian curvature + sage: K(u1,u2) = ellipsoid.gauss_curvature() + sage: # Make array of K values + sage: K_array = [K(uu[0],uu[1]) for uu in u_array] + sage: # Find minimum and max of the gauss curvature + sage: K_max = max(K_array) + sage: K_min = min(K_array) + sage: # Make the array of color coefficients + sage: cc_array = [ (ccc - K_min)/(K_max - K_min) for ccc in K_array ] + sage: points_array = [ellipsoid_equation(u_array[counter][0],u_array[counter][1]) for counter in range(0,len(u_array)) ] + sage: curvature_ellipsoid_plot = sum( point([xx for xx in points_array[counter]],color=hue(cc_array[counter]/2)) for counter in range(0,len(u_array)) ) + sage: curvature_ellipsoid_plot.show(aspect_ratio=1) + + We can find the principal curvatures and principal directions of the + elliptic paraboloid:: + + sage: u, v = var('u, v', domain='real') + sage: eparaboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v], 'elliptic paraboloid') + sage: pd = eparaboloid.principal_directions(); pd + [(2*sqrt(4*u^2 + 4*v^2 + 1)/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1), [(1, v/u)], 1), (2/sqrt(4*u^2 + 4*v^2 + 1), [(1, -u/v)], 1)] + + We extract the principal curvatures:: + + sage: k1 = pd[0][0].simplify_full() + sage: k1 + 2*sqrt(4*u^2 + 4*v^2 + 1)/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1) + sage: k2 = pd[1][0].simplify_full() + sage: k2 + 2/sqrt(4*u^2 + 4*v^2 + 1) + + and check them by comparison with the Gaussian and mean curvature + expressed in terms of the principal curvatures:: + + sage: K = eparaboloid.gauss_curvature().simplify_full() + sage: K + 4/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1) + sage: H = eparaboloid.mean_curvature().simplify_full() + sage: H + 2*(2*u^2 + 2*v^2 + 1)/(4*u^2 + 4*v^2 + 1)^(3/2) + sage: (K - k1*k2).simplify_full() + 0 + sage: (2*H - k1 - k2).simplify_full() + 0 + + We can find the intrinsic (local coordinates) of the principal directions:: + + sage: pd[0][1] + [(1, v/u)] + sage: pd[1][1] + [(1, -u/v)] + + The ParametrizedSurface3D class also contains functionality to + compute the coefficients of the second fundamental form, the shape + operator, the rotation on the surface at a given angle, the + connection coefficients. One can also calculate numerically the + geodesics and the parallel translation along a curve. + + Here we compute a number of geodesics on the sphere emanating + from the point ``(1, 0, 0)``, in various directions. The geodesics + intersect again in the antipodal point ``(-1, 0, 0)``, indicating + that these points are conjugate:: + + sage: S = surfaces.Sphere() + sage: g1 = [c[-1] for c in S.geodesics_numerical((0,0),(1,0),(0,2*pi,100))] + sage: g2 = [c[-1] for c in S.geodesics_numerical((0,0),(cos(pi/3),sin(pi/3)),(0,2*pi,100))] + sage: g3 = [c[-1] for c in S.geodesics_numerical((0,0),(cos(2*pi/3),sin(2*pi/3)),(0,2*pi,100))] + sage: (S.plot(opacity=0.3) + line3d(g1,color='red') + line3d(g2,color='red') + line3d(g3,color='red')).show() + + """ + + + def __init__(self, equation, variables, name=None): + r""" + See ``ParametrizedSurface3D`` for full documentation. + + .. note:: + + The orientation of the surface is determined by the + parametrization, that is, the natural frame with positive + orientation is given by `\partial_1 \vec r`, `\partial_2 \vec + r`. + + + EXAMPLES:: + + sage: u, v = var('u,v', domain='real') + sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2)) + sage: enneper = ParametrizedSurface3D(eq, (u, v),'Enneper Surface'); enneper + Parametrized surface ('Enneper Surface') with equation (-u^3 + 3*u*v^2 + 3*u, 3*u^2*v - v^3 + 3*v, 3*u^2 - 3*v^2) + + """ + self.equation = tuple(equation) + + if len(variables[0]) > 0: + self.variables_range = (variables[0][1:3], variables[1][1:3]) + self.variables_list = (variables[0][0], variables[1][0]) + else: + self.variables_range = None + self.variables_list = variables + + self.variables = {1:self.variables_list[0],2:self.variables_list[1]} + self.name = name + + + def _latex_(self): + r""" + Returns the LaTeX representation of this parametrized surface. + + EXAMPLES:: + + sage: u, v = var('u, v') + sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v),'sphere') + sage: latex(sphere) + \left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right) + sage: sphere._latex_() + \left(\cos\left(u\right) \cos\left(v\right), \cos\left(v\right) \sin\left(u\right), \sin\left(v\right)\right) + + """ + from sage.misc.latex import latex + return latex(self.equation) + + + def _repr_(self): + r""" + Returns the string representation of this parametrized surface. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2)) + sage: enneper = ParametrizedSurface3D(eq,[u,v],'enneper_surface') + sage: print enneper + Parametrized surface ('enneper_surface') with equation (-u^3 + 3*u*v^2 + 3*u, 3*u^2*v - v^3 + 3*v, 3*u^2 - 3*v^2) + sage: enneper._repr_() + "Parametrized surface ('enneper_surface') with equation (-u^3 + 3*u*v^2 + 3*u, 3*u^2*v - v^3 + 3*v, 3*u^2 - 3*v^2)" + + """ + name = 'Parametrized surface' + if self.name is not None: + name += " ('%s')" % self.name + s ='%(designation)s with equation %(eq)s' % \ + {'designation': name, 'eq': str(self.equation)} + return s + + + def point(self, coords): + r""" + Returns a point on the surface given its intrinsic coordinates. + + INPUT: + + - ``coords`` - 2-tuple specifying the intrinsic coordinates ``(u, v)`` of the point. + + OUTPUT: + + - 3-vector specifying the coordinates in `\RR^3` of the point. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: torus = ParametrizedSurface3D(((2 + cos(u))*cos(v),(2 + cos(u))*sin(v), sin(u)),[u,v],'torus') + sage: torus.point((0, pi/2)) + (0, 3, 0) + sage: torus.point((pi/2, pi)) + (-2, 0, 1) + sage: torus.point((pi, pi/2)) + (0, 1, 0) + + """ + + d = dict(zip(self.variables_list, coords)) + return vector([f.subs(d) for f in self.equation]) + + + def tangent_vector(self, coords, components): + r""" + Returns the components of a tangent vector given the intrinsic + coordinates of the base point and the components of the vector + in the intrinsic frame. + + INPUT: + + - ``coords`` - 2-tuple specifying the intrinsic coordinates ``(u, v)`` of the point. + + - ``components`` - 2-tuple specifying the components of the tangent vector in the intrinsic coordinate frame. + + OUTPUT: + + - 3-vector specifying the components in `\RR^3` of the vector. + + EXAMPLES: + + We compute two tangent vectors to Enneper's surface along the + coordinate lines and check that their cross product gives the + normal vector:: + + sage: u, v = var('u,v', domain='real') + sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2)) + sage: e = ParametrizedSurface3D(eq, (u, v),'Enneper Surface') + + sage: w1 = e.tangent_vector((1, 2), (1, 0)); w1 + (12, 12, 6) + sage: w2 = e.tangent_vector((1, 2), (0, 1)); w2 + (12, -6, -12) + sage: w1.cross_product(w2) + (-108, 216, -216) + + sage: n = e.normal_vector().subs({u: 1, v: 2}); n + (-108, 216, -216) + sage: n == w1.cross_product(w2) + True + + """ + + components = vector(components) + d = dict(zip(self.variables_list, coords)) + jacobian = matrix([[f.diff(u).subs(d) for u in self.variables_list] + for f in self.equation]) + return jacobian * components + + + def plot(self, urange=None, vrange=None, **kwds): + r""" + Enable easy plotting directly from the surface class. + + The optional keywords ``urange`` and ``vrange`` specify the range for + the surface parameters `u` and `v`. If either of these parameters + is ``None``, the method checks whether a parameter range was + specified when the surface was created. If not, the default of + $(0, 2 \pi)$ is used. + + INPUT: + + - ``urange`` - 2-tuple specifying the parameter range for `u`. + - ``vrange`` - 2-tuple specifying the parameter range for `v`. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: eq = (3*u + 3*u*v^2 - u^3, 3*v + 3*u^2*v - v^3, 3*(u^2-v^2)) + sage: enneper = ParametrizedSurface3D(eq, (u, v), 'Enneper Surface') + sage: enneper.plot((-5, 5), (-5, 5)) + + """ + + from sage.plot.plot3d.parametric_plot3d import parametric_plot3d + + if self.variables_range is None: + if urange is None: + urange = (0, 2*pi) + if vrange is None: + vrange = (0, 2*pi) + else: + if urange is None: + urange = self.variables_range[0] + if vrange is None: + vrange = self.variables_range[1] + + urange3 = (self.variables[1],) + tuple(urange) + vrange3 = (self.variables[2],) + tuple(vrange) + P = parametric_plot3d(self.equation, urange3, vrange3, **kwds) + + return P + + + @cached_method + def natural_frame(self): + """ + Returns the natural tangent frame on the parametrized surface. + The vectors of this frame are tangent to the coordinate lines + on the surface. + + OUTPUT: + + - The natural frame as a dictionary. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v), 'elliptic paraboloid') + sage: eparaboloid.natural_frame() + {1: (1, 0, 2*u), 2: (0, 1, 2*v)} + """ + + dr1 = \ + vector([_simplify_full_rad( diff(f,self.variables[1]) ) + for f in self.equation]) + dr2 = \ + vector([_simplify_full_rad( diff(f,self.variables[2]) ) + for f in self.equation]) + + return {1:dr1, 2:dr2} + + + @cached_method + def normal_vector(self, normalized=False): + """ + Returns the normal vector field of the parametrized surface. + + INPUT: + + - ``normalized`` - default ``False`` - specifies whether the normal vector should be normalized. + + OUTPUT: + + - Normal vector field. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: eparaboloid = ParametrizedSurface3D((u, v, u^2 + v^2), (u, v), 'elliptic paraboloid') + sage: eparaboloid.normal_vector(normalized=False) + (-2*u, -2*v, 1) + sage: eparaboloid.normal_vector(normalized=True) + (-2*u/sqrt(4*u^2 + 4*v^2 + 1), -2*v/sqrt(4*u^2 + 4*v^2 + 1), 1/sqrt(4*u^2 + 4*v^2 + 1)) + + """ + + dr = self.natural_frame() + normal = dr[1].cross_product(dr[2]) + + if normalized: + normal /= normal.norm() + return _simplify_full_rad(normal) + + + @cached_method + def _compute_first_fundamental_form_coefficient(self, index): + """ + Helper function to compute coefficients of the first fundamental form. + + Do not call this method directly; instead use + ``first_fundamental_form_coefficient``. + This method is cached, and expects its argument to be a list. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v)) + sage: eparaboloid._compute_first_fundamental_form_coefficient((1,2)) + 4*u*v + + """ + dr = self.natural_frame() + return _simplify_full_rad(dr[index[0]]*dr[index[1]]) + + + def first_fundamental_form_coefficient(self, index): + r""" + Compute a single component $g_{ij}$ of the first fundamental form. If + the parametric representation of the surface is given by the vector + function $\vec r(u^i)$, where $u^i$, $i = 1, 2$ are curvilinear + coordinates, then $g_{ij} = \frac{\partial \vec r}{\partial u^i} \cdot \frac{\partial \vec r}{\partial u^j}$. + + INPUT: + + - ``index`` - tuple ``(i, j)`` specifying the index of the component $g_{ij}$. + + OUTPUT: + + - Component $g_{ij}$ of the first fundamental form + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v)) + sage: eparaboloid.first_fundamental_form_coefficient((1,2)) + 4*u*v + + When the index is invalid, an error is raised:: + + sage: u, v = var('u, v', domain='real') + sage: eparaboloid = ParametrizedSurface3D((u, v, u^2+v^2), (u, v)) + sage: eparaboloid.first_fundamental_form_coefficient((1,5)) + Traceback (most recent call last): + ... + ValueError: Index (1, 5) out of bounds. + + """ + index = tuple(sorted(index)) + if len(index) == 2 and all(i == 1 or i == 2 for i in index): + return self._compute_first_fundamental_form_coefficient(index) + else: + raise ValueError("Index %s out of bounds." % str(index)) + + def first_fundamental_form_coefficients(self): + r""" + Returns the coefficients of the first fundamental form as a dictionary. + The keys are tuples $(i, j)$, where $i$ and $j$ range over $1, 2$, + while the values are the corresponding coefficients $g_{ij}$. + + OUTPUT: + + - Dictionary of first fundamental form coefficients. + + EXAMPLES:: + + sage: u, v = var('u,v', domain='real') + sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v), 'sphere') + sage: sphere.first_fundamental_form_coefficients() + {(1, 2): 0, (1, 1): cos(v)^2, (2, 1): 0, (2, 2): 1} + + """ + coefficients = {} + for index in product((1, 2), repeat=2): + sorted_index = list(sorted(index)) + coefficients[index] = \ + self._compute_first_fundamental_form_coefficient(index) + return coefficients + + + def first_fundamental_form(self, vector1, vector2): + r""" + Evaluate the first fundamental form on two vectors expressed with + respect to the natural coordinate frame on the surface. In other words, + if the vectors are $v = (v^1, v^2)$ and $w = (w^1, w^2)$, calculate + $g_{11} v^1 w^1 + g_{12}(v^1 w^2 + v^2 w^1) + g_{22} v^2 w^2$, with + $g_{ij}$ the coefficients of the first fundamental form. + + INPUT: + + - ``vector1``, ``vector2`` - vectors on the surface. + + OUTPUT: + + - First fundamental form evaluated on the input vectors. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: v1, v2, w1, w2 = var('v1, v2, w1, w2', domain='real') + sage: sphere = ParametrizedSurface3D((cos(u)*cos(v), sin(u)*cos(v), sin(v)), (u, v),'sphere') + sage: sphere.first_fundamental_form(vector([v1,v2]),vector([w1,w2])) + v1*w1*cos(v)^2 + v2*w2 + + sage: vv = vector([1,2]) + sage: sphere.first_fundamental_form(vv,vv) + cos(v)^2 + 4 + + sage: sphere.first_fundamental_form([1,1],[2,1]) + 2*cos(v)^2 + 1 + """ + gamma = self.first_fundamental_form_coefficients() + return sum(gamma[(i,j)] * vector1[i - 1] * vector2[j - 1] + for i, j in product((1, 2), repeat=2)) + + + def area_form_squared(self): + """ + Returns the square of the coefficient of the area form on the surface. + In terms of the coefficients $g_{ij}$ (where $i, j = 1, 2$) of the + first fundamental form, this invariant is given by + $A^2 = g_{11}g_{22} - g_{12}^2$. + + See also :meth:`.area_form`. + + OUTPUT: + + - Square of the area form + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: sphere.area_form_squared() + cos(v)^2 + + """ + gamma = self.first_fundamental_form_coefficients() + sq = gamma[(1,1)] * gamma[(2,2)] - gamma[(1,2)]**2 + return _simplify_full_rad(sq) + + + def area_form(self): + r""" + Returns the coefficient of the area form on the surface. In terms of + the coefficients $g_{ij}$ (where $i, j = 1, 2$) of the first + fundamental form, the coefficient of the area form is given by + $A = \sqrt{g_{11}g_{22} - g_{12}^2}$. + + See also :meth:`.area_form_squared`. + + OUTPUT: + + - Coefficient of the area form + + EXAMPLES:: + + sage: u, v = var('u,v', domain='real') + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: sphere.area_form() + cos(v) + + """ + f = abs(sqrt(self.area_form_squared())) + return _simplify_full_rad(f) + + + def first_fundamental_form_inverse_coefficients(self): + r""" + Returns the coefficients $g^{ij}$ of the inverse of the fundamental + form, as a dictionary. The inverse coefficients are defined by + $g^{ij} g_{jk} = \delta^i_k$ with $\delta^i_k$ the Kronecker + delta. + + OUTPUT: + + - Dictionary of the inverse coefficients. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: sphere.first_fundamental_form_inverse_coefficients() + {(1, 2): 0, (1, 1): cos(v)^(-2), (2, 1): 0, (2, 2): 1} + + """ + + g = self.first_fundamental_form_coefficients() + D = g[(1,1)] * g[(2,2)] - g[(1,2)]**2 + + gi11 = _simplify_full_rad(g[(2,2)]/D) + gi12 = _simplify_full_rad(-g[(1,2)]/D) + gi21 = gi12 + gi22 = _simplify_full_rad(g[(1,1)]/D) + + return {(1,1): gi11, (1,2): gi12, (2,1): gi21, (2,2): gi22} + + + def first_fundamental_form_inverse_coefficient(self, index): + r""" + Returns a specific component $g^{ij}$ of the inverse of the fundamental + form. + + INPUT: + + - ``index`` - tuple ``(i, j)`` specifying the index of the component $g^{ij}$. + + OUTPUT: + + - Component of the inverse of the fundamental form. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: sphere.first_fundamental_form_inverse_coefficient((1, 2)) + 0 + sage: sphere.first_fundamental_form_inverse_coefficient((1, 1)) + cos(v)^(-2) + + """ + + index = tuple(sorted(index)) + if len(index) == 2 and all(i == 1 or i == 2 for i in index): + return self.first_fundamental_form_inverse_coefficients()[index] + else: + raise ValueError("Index %s out of bounds." % str(index)) + + + + @cached_method + def rotation(self,theta): + r""" + Gives the matrix of the rotation operator over a given angle $\theta$ + with respect to the natural frame. + + INPUT: + + - ``theta`` - rotation angle + + OUTPUT: + + - Rotation matrix with respect to the natural frame. + + ALGORITHM: + + The operator of rotation over $\pi/2$ is $J^i_j = g^{ik}\omega_{jk}$, + where $\omega$ is the area form. The operator of rotation over an + angle $\theta$ is $\cos(\theta) I + sin(\theta) J$. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + + We first compute the matrix of rotation over $\pi/3$:: + + sage: rotation = sphere.rotation(pi/3); rotation + [ 1/2 -1/2*sqrt(3)/cos(v)] + [ 1/2*sqrt(3)*cos(v) 1/2] + + We verify that three succesive rotations over $\pi/3$ yield minus the identity:: + + sage: rotation^3 + [-1 0] + [ 0 -1] + + """ + + from sage.functions.trig import sin, cos + + gi = self.first_fundamental_form_inverse_coefficients() + w12 = self.area_form() + R11 = (cos(theta) + sin(theta)*gi[1,2]*w12).simplify_full() + R12 = (- sin(theta)*gi[1,1]*w12).simplify_full() + R21 = (sin(theta)*gi[2,2]*w12).simplify_full() + R22 = (cos(theta) - sin(theta)*gi[2,1]*w12).simplify_full() + return matrix([[R11,R12],[R21,R22]]) + + + @cached_method + def orthonormal_frame(self, coordinates='ext'): + r""" + Returns the orthonormal frame field on the surface, expressed either + in exterior coordinates (i.e. expressed as vector fields in the + ambient space $\mathbb{R}^3$, the default) or interior coordinates + (with respect to the natural frame) + + INPUT: + + - ``coordinates`` - either ``ext`` (default) or ``int``. + + OUTPUT: + + - Orthogonal frame field as a dictionary. + + ALGORITHM: + + We normalize the first vector $\vec e_1$ of the natural frame and then + get the second frame vector as $\vec e_2 = [\vec n, \vec e_1]$, where + $\vec n$ is the unit normal to the surface. + + EXAMPLES:: + + sage: u, v = var('u,v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v), sin(u)*cos(v), sin(v)], [u, v],'sphere') + sage: frame = sphere.orthonormal_frame(); frame + {1: (-sin(u), cos(u), 0), 2: (-cos(u)*sin(v), -sin(u)*sin(v), cos(v))} + sage: (frame[1]*frame[1]).simplify_full() + 1 + sage: (frame[1]*frame[2]).simplify_full() + 0 + sage: frame[1] == sphere.orthonormal_frame_vector(1) + True + + We compute the orthonormal frame with respect to the natural frame on + the surface:: + + sage: frame_int = sphere.orthonormal_frame(coordinates='int'); frame_int + {1: (1/cos(v), 0), 2: (0, 1)} + sage: sphere.first_fundamental_form(frame_int[1], frame_int[1]) + 1 + sage: sphere.first_fundamental_form(frame_int[1], frame_int[2]) + 0 + sage: sphere.first_fundamental_form(frame_int[2], frame_int[2]) + 1 + + """ + + + from sage.symbolic.constants import pi + + if coordinates not in ['ext', 'int']: + raise ValueError("Coordinate system must be exterior ('ext') " + "or interior ('int').") + + c = self.first_fundamental_form_coefficient([1,1]) + if coordinates == 'ext': + f1 = self.natural_frame()[1] + + E1 = _simplify_full_rad(f1/sqrt(c)) + E2 = _simplify_full_rad( + self.normal_vector(normalized=True).cross_product(E1)) + else: + E1 = vector([_simplify_full_rad(1/sqrt(c)), 0]) + E2 = (self.rotation(pi/2)*E1).simplify_full() + return {1:E1, 2:E2} + + + def orthonormal_frame_vector(self, index, coordinates='ext'): + r""" + Returns a specific basis vector field of the orthonormal frame field on + the surface, expressed in exterior or interior coordinates. See + :meth:`orthogonal_frame` for more details. + + INPUT: + + - ``index`` - index of the basis vector; + - ``coordinates`` - either ``ext`` (default) or ``int``. + + OUTPUT: + + - Orthonormal frame vector field. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: V1 = sphere.orthonormal_frame_vector(1); V1 + (-sin(u), cos(u), 0) + sage: V2 = sphere.orthonormal_frame_vector(2); V2 + (-cos(u)*sin(v), -sin(u)*sin(v), cos(v)) + sage: (V1*V1).simplify_full() + 1 + sage: (V1*V2).simplify_full() + 0 + + sage: n = sphere.normal_vector(normalized=True) + sage: (V1.cross_product(V2) - n).simplify_full() + (0, 0, 0) + """ + + return self.orthonormal_frame(coordinates)[index] + + + def lie_bracket(self, v, w): + r""" + Returns the Lie bracket of two vector fields that are tangent + to the surface. The vector fields should be given in intrinsic + coordinates, i.e. with respect to the natural frame. + + INPUT: + + - ``v`` and ``w`` - vector fields on the surface, expressed + as pairs of functions or as vectors of length 2. + + OUTPUT: + + - The Lie bracket $[v, w]$. + + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: sphere.lie_bracket([u,v],[-v,u]) + (0, 0) + + sage: EE_int = sphere.orthonormal_frame(coordinates='int') + sage: sphere.lie_bracket(EE_int[1],EE_int[2]) + (sin(v)/cos(v)^2, 0) + """ + v = vector(SR, v) + w = vector(SR, w) + + variables = self.variables_list + Dv = matrix([[_simplify_full_rad(diff(component, u)) + for u in variables] for component in v]) + Dw = matrix([[_simplify_full_rad(diff(component, u)) + for u in variables] for component in w]) + return vector(Dv*w - Dw*v).simplify_full() + + + def frame_structure_functions(self, e1, e2): + r""" + Returns the structure functions $c^k_{ij}$ for a frame field + $e_1, e_2$, i.e. a pair of vector fields on the surface which are + linearly independent at each point. The structure functions are + defined using the Lie bracket by $[e_i,e_j] = c^k_{ij}e_k$. + + INPUT: + + - ``e1``, ``e2`` - vector fields in intrinsic coordinates on + the surface, expressed as pairs of functions, or as vectors of + length 2. + + OUTPUT: + + - Dictionary of structure functions, where the key ``(i, j, k)`` refers to + the structure function $c_{i,j}^k$. + + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: assume(cos(v) > 0) + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v), sin(u)*cos(v), sin(v)], [u, v], 'sphere') + sage: sphere.frame_structure_functions([u, v], [-v, u]) + {(1, 2, 1): 0, (2, 1, 2): 0, (2, 2, 2): 0, (1, 2, 2): 0, (1, 1, 1): 0, (2, 1, 1): 0, (2, 2, 1): 0, (1, 1, 2): 0} + + We construct the structure functions of the orthonormal frame on the + surface:: + + sage: EE_int = sphere.orthonormal_frame(coordinates='int') + sage: CC = sphere.frame_structure_functions(EE_int[1],EE_int[2]); CC + {(1, 2, 1): sin(v)/cos(v), (2, 1, 2): 0, (2, 2, 2): 0, (1, 2, 2): 0, (1, 1, 1): 0, (2, 1, 1): -sin(v)/cos(v), (2, 2, 1): 0, (1, 1, 2): 0} + sage: sphere.lie_bracket(EE_int[1],EE_int[2]) - CC[(1,2,1)]*EE_int[1] - CC[(1,2,2)]*EE_int[2] + (0, 0) + """ + e1 = vector(SR, e1) + e2 = vector(SR, e2) + + lie_bracket = self.lie_bracket(e1, e2).simplify_full() + transformation = matrix(SR, [e1, e2]).transpose() + + w = (transformation.inverse()*lie_bracket).simplify_full() + + return {(1,1,1): 0, (1,1,2): 0, (1,2,1): w[0], (1,2,2): w[1], + (2,1,1): -w[0], (2,1,2): -w[1], (2,2,1): 0, (2,2,2): 0} + + + @cached_method + def _compute_second_order_frame_element(self, index): + """ + Compute an element of the second order frame of the surface. See + :meth:`second_order_natural_frame` for more details. + + This method expects its arguments in tuple form for caching. + As it does no input checking, it should not be called directly. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: paraboloid = ParametrizedSurface3D([u, v, u^2 + v^2], [u,v], 'paraboloid') + sage: paraboloid._compute_second_order_frame_element((1, 2)) + (0, 0, 0) + sage: paraboloid._compute_second_order_frame_element((2, 2)) + (0, 0, 2) + + """ + variables = [self.variables[i] for i in index] + ddr_element = vector([_simplify_full_rad(diff(f, variables)) + for f in self.equation]) + + return ddr_element + + + def second_order_natural_frame(self): + r""" + Returns the second-order frame of the surface, i.e. computes the + second-order derivatives (with respect to the parameters on the + surface) of the parametric expression $\vec r = \vec r(u^1,u^2)$ + of the surface. + + OUTPUT: + + - Dictionary where the keys are 2-tuples ``(i, j)`` and the values are the corresponding derivatives $r_{ij}$. + + EXAMPLES: + + We compute the second-order natural frame of the sphere:: + + sage: u, v = var('u, v', domain='real') + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: sphere.second_order_natural_frame() + {(1, 2): (sin(u)*sin(v), -cos(u)*sin(v), 0), (1, 1): (-cos(u)*cos(v), -cos(v)*sin(u), 0), (2, 1): (sin(u)*sin(v), -cos(u)*sin(v), 0), (2, 2): (-cos(u)*cos(v), -cos(v)*sin(u), -sin(v))} + + """ + + vectors = {} + for index in product((1, 2), repeat=2): + sorted_index = tuple(sorted(index)) + vectors[index] = \ + self._compute_second_order_frame_element(sorted_index) + return vectors + + + def second_order_natural_frame_element(self, index): + r""" + Returns a vector in the second-order frame of the surface, i.e. + computes the second-order derivatives of the parametric expression + $\vec{r}$ of the surface with respect to the parameters listed in the + argument. + + INPUT: + + - ``index`` - a 2-tuple ``(i, j)`` specifying the element of the second-order frame. + + OUTPUT: + + - The second-order derivative $r_{ij}$. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: sphere.second_order_natural_frame_element((1, 2)) + (sin(u)*sin(v), -cos(u)*sin(v), 0) + + """ + + index = tuple(sorted(index)) + if len(index) == 2 and all(i == 1 or i == 2 for i in index): + return self._compute_second_order_frame_element(index) + else: + raise ValueError("Index %s out of bounds." % str(index)) + + @cached_method + def _compute_second_fundamental_form_coefficient(self, index): + """ + Compute a coefficient of the second fundamental form of the surface. + See ``second_fundamental_form_coefficient`` for more details. + + This method expects its arguments in tuple form for caching. As it + does no input checking, it should not be called directly. + + EXAMPLES:: + + sage: u, v = var('u,v', domain='real') + sage: paraboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v], 'paraboloid') + sage: paraboloid._compute_second_fundamental_form_coefficient((1,1)) + 2/sqrt(4*u^2 + 4*v^2 + 1) + + """ + N = self.normal_vector(normalized=True) + v = self.second_order_natural_frame_element(index) + return _simplify_full_rad(v*N) + + + def second_fundamental_form_coefficient(self, index): + r""" + Returns the coefficient $h_{ij}$ of the second fundamental form + corresponding to the index $(i, j)$. If the equation of the surface + is $\vec{r}(u^1, u^2)$, then $h_{ij} = \vec{r}_{u^i u^j} \cdot \vec{n}$, + where $\vec{n}$ is the unit normal. + + INPUT: + + - ``index`` - a 2-tuple ``(i, j)`` + + OUTPUT: + + - Component $h_{ij}$ of the second fundamental form. + + EXAMPLES:: + + sage: u, v = var('u,v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: sphere.second_fundamental_form_coefficient((1, 1)) + -cos(v)^2 + sage: sphere.second_fundamental_form_coefficient((2, 1)) + 0 + + """ + index = tuple(index) + if len(index) == 2 and all(i == 1 or i == 2 for i in index): + return self._compute_second_fundamental_form_coefficient(index) + else: + raise ValueError("Index %s out of bounds." % str(index)) + + + def second_fundamental_form_coefficients(self): + """ + Returns the coefficients $h_{ij}$ of the second fundamental form as + a dictionary, where the keys are the indices $(i, j)$ and the values + are the corresponding components $h_{ij}$. + + When only one component is needed, consider instead the function + :meth:`second_fundamental_form_coefficient`. + + OUTPUT: + + Dictionary of second fundamental form coefficients. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: sphere.second_fundamental_form_coefficients() + {(1, 2): 0, (1, 1): -cos(v)^2, (2, 1): 0, (2, 2): -1} + + """ + + coefficients = {} + for index in product((1, 2), repeat=2): + coefficients[index] = \ + self._compute_second_fundamental_form_coefficient(index) + return coefficients + + + def second_fundamental_form(self,vector1,vector2): + r""" + Evaluates the second fundamental form on two vectors on the surface. + If the vectors are given by $v=(v^1,v^2)$ and $w=(w^1,w^2)$, the + result of this function is $h_{11} v^1 w^1 + h_{12}(v^1 w^2 + v^2 w^1) + h_{22} v^2 w^2$. + + INPUT: + + - ``vector1``, ``vector2`` - 2-tuples representing the input vectors. + + OUTPUT: + + - Value of the second fundamental form evaluated on the given vectors. + + EXAMPLES: + + We evaluate the second fundamental form on two symbolic vectors:: + + sage: u, v = var('u, v', domain='real') + sage: v1, v2, w1, w2 = var('v1, v2, w1, w2', domain='real') + sage: assume(cos(v) > 0) + sage: sphere = ParametrizedSurface3D([cos(u)*cos(v),sin(u)*cos(v),sin(v)],[u,v],'sphere') + sage: sphere.second_fundamental_form(vector([v1, v2]), vector([w1, w2])) + -v1*w1*cos(v)^2 - v2*w2 + + We evaluate the second fundamental form on vectors with numerical + components:: + + sage: vect = vector([1,2]) + sage: sphere.second_fundamental_form(vect, vect) + -cos(v)^2 - 4 + sage: sphere.second_fundamental_form([1,1], [2,1]) + -2*cos(v)^2 - 1 + + """ + hh = self.second_fundamental_form_coefficients() + return sum(hh[(i, j)] * vector1[i - 1] * vector2[j - 1] + for (i, j) in product((1, 2), repeat=2)) + + + def gauss_curvature(self): + r""" + Finds the gaussian curvature of the surface, given by + $K = \frac{h_{11}h_{22} - h_{12}^2}{g_{11}g_{22} - g_{12}^2}$, + where $g_{ij}$ and $h_{ij}$ are the coefficients of the first + and second fundamental form, respectively. + + OUTPUT: + + - Gaussian curvature of the surface. + + EXAMPLES:: + + sage: R = var('R') + sage: assume(R>0) + sage: u, v = var('u,v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere') + sage: sphere.gauss_curvature() + R^(-2) + + """ + hh = self.second_fundamental_form_coefficients() + return _simplify_full_rad( + (hh[(1,1)] * hh[(2,2)] - hh[(1,2)]**2)/self.area_form_squared()) + + + def mean_curvature(self): + r""" + Finds the mean curvature of the surface, given by + $H = \frac{1}{2}\frac{g_{22}h_{11} - 2g_{12}h_{12} + g_{11}h_{22}}{g_{11}g_{22} - g_{12}^2}$, + where $g_{ij}$ and $h_{ij}$ are the components of the first and second + fundamental forms, respectively. + + OUTPUT: + + - Mean curvature of the surface + + EXAMPLES:: + + sage: R = var('R') + sage: assume(R>0) + sage: u, v = var('u,v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere') + sage: sphere.mean_curvature() + -1/R + + """ + gg = self.first_fundamental_form_coefficients() + hh = self.second_fundamental_form_coefficients() + denom = 2*self.area_form_squared() + numer = (gg[(2,2)]*hh[(1,1)] - 2*gg[(1,2)]*hh[(1,2)] + + gg[(1,1)]*hh[(2,2)]).simplify_full() + return _simplify_full_rad(numer/denom) + + + @cached_method + def shape_operator_coefficients(self): + r""" + Returns the components of the shape operator of the surface as a + dictionary. See ``shape_operator`` for more information. + + OUTPUT: + + - Dictionary where the keys are two-tuples ``(i, j)``, with values the + corresponding component of the shape operator. + + EXAMPLES:: + + sage: R = var('R') + sage: u, v = var('u,v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere') + sage: sphere.shape_operator_coefficients() + {(1, 2): 0, (1, 1): -1/R, (2, 1): 0, (2, 2): -1/R} + + """ + + gi = self.first_fundamental_form_inverse_coefficients() + hh = self.second_fundamental_form_coefficients() + + sh_op11 = _simplify_full_rad(gi[(1,1)]*hh[(1,1)] + gi[(1,2)]*hh[(1,2)]) + sh_op12 = _simplify_full_rad(gi[(1,1)]*hh[(2,1)] + gi[(1,2)]*hh[(2,2)]) + sh_op21 = _simplify_full_rad(gi[(2,1)]*hh[(1,1)] + gi[(2,2)]*hh[(1,2)]) + sh_op22 = _simplify_full_rad(gi[(2,1)]*hh[(2,1)] + gi[(2,2)]*hh[(2,2)]) + + return {(1,1): sh_op11, (1,2): sh_op12, (2,1): sh_op21, (2,2): sh_op22} + + + def shape_operator(self): + r""" + Returns the shape operator of the surface as a matrix. The shape + operator is defined as the derivative of the Gauss map, and is + computed here in terms of the first and second fundamental form by + means of the Weingarten equations. + + OUTPUT: + + - Matrix of the shape operator + + EXAMPLES:: + + sage: R = var('R') + sage: assume(R>0) + sage: u, v = var('u,v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([R*cos(u)*cos(v),R*sin(u)*cos(v),R*sin(v)],[u,v],'sphere') + sage: S = sphere.shape_operator(); S + [-1/R 0] + [ 0 -1/R] + + The eigenvalues of the shape operator are the principal curvatures of + the surface:: + + sage: u, v = var('u,v', domain='real') + sage: paraboloid = ParametrizedSurface3D([u, v, u^2+v^2], [u, v], 'paraboloid') + sage: S = paraboloid.shape_operator(); S + [2*(4*v^2 + 1)/(4*u^2 + 4*v^2 + 1)^(3/2) -8*u*v/(4*u^2 + 4*v^2 + 1)^(3/2)] + [ -8*u*v/(4*u^2 + 4*v^2 + 1)^(3/2) 2*(4*u^2 + 1)/(4*u^2 + 4*v^2 + 1)^(3/2)] + sage: S.eigenvalues() + [2*sqrt(4*u^2 + 4*v^2 + 1)/(16*u^4 + 16*v^4 + 8*(4*u^2 + 1)*v^2 + 8*u^2 + 1), 2/sqrt(4*u^2 + 4*v^2 + 1)] + + """ + + shop = self.shape_operator_coefficients() + shop_matrix=matrix([[shop[(1,1)],shop[(1,2)]], + [shop[(2,1)],shop[(2,2)]]]) + return shop_matrix + + + def principal_directions(self): + r""" + Finds the principal curvatures and principal directions of the surface + + OUTPUT: + + For each principal curvature, returns a list of the form + $(\rho, V, n)$, where $\rho$ is the principal curvature, + $V$ is the corresponding principal direction, and $n$ is + the multiplicity. + + EXAMPLES:: + + sage: u, v = var('u, v', domain='real') + sage: R, r = var('R,r', domain='real') + sage: assume(R>r,r>0) + sage: torus = ParametrizedSurface3D([(R+r*cos(v))*cos(u),(R+r*cos(v))*sin(u),r*sin(v)],[u,v],'torus') + sage: torus.principal_directions() + [(-cos(v)/(r*cos(v) + R), [(1, 0)], 1), (-1/r, [(0, 1)], 1)] + + """ + return self.shape_operator().eigenvectors_left() + + + @cached_method + def connection_coefficients(self): + r""" + Computes the connection coefficients or Christoffel symbols + $\Gamma^k_{ij}$ of the surface. If the coefficients of the first + fundamental form are given by $g_{ij}$ (where $i, j = 1, 2$), then + $\Gamma^k_{ij} = \frac{1}{2} g^{kl} \left( \frac{\partial g_{li}}{\partial x^j} + - \frac{\partial g_{ij}}{\partial x^l} + + \frac{\partial g_{lj}}{\partial x^i} \right)$. + Here, $(g^{kl})$ is the inverse of the matrix $(g_{ij})$, with + $i, j = 1, 2$. + + OUTPUT: + + Dictionary of connection coefficients, where the keys are 3-tuples + $(i,j,k)$ and the values are the corresponding coefficients + $\Gamma^k_{ij}$. + + EXAMPLES:: + + sage: r = var('r') + sage: assume(r > 0) + sage: u, v = var('u,v', domain='real') + sage: assume(cos(v)>0) + sage: sphere = ParametrizedSurface3D([r*cos(u)*cos(v),r*sin(u)*cos(v),r*sin(v)],[u,v],'sphere') + sage: sphere.connection_coefficients() + {(1, 2, 1): -sin(v)/cos(v), (2, 2, 2): 0, (1, 2, 2): 0, (2, 1, 1): -sin(v)/cos(v), (1, 1, 2): cos(v)*sin(v), (2, 2, 1): 0, (2, 1, 2): 0, (1, 1, 1): 0} + + """ + x = self.variables + gg = self.first_fundamental_form_coefficients() + gi = self.first_fundamental_form_inverse_coefficients() + + dg = {} + for i,j,k in product((1, 2), repeat=3): + dg[(i,j,k)] = _simplify_full_rad(gg[(j,k)].differentiate(x[i])) + + structfun={} + for i,j,k in product((1, 2), repeat=3): + structfun[(i,j,k)] = sum(gi[(k,s)]*(dg[(i,j,s)] + dg[(j,i,s)] + -dg[(s,i,j)])/2 + for s in (1,2)) + structfun[(i,j,k)] = _simplify_full_rad(structfun[(i,j,k)]) + return structfun + + + @cached_method + def _create_geodesic_ode_system(self): + r""" + Helper method to create a fast floating-point version of the + geodesic equations, used by :meth:`geodesics_numerical`. + + EXAMPLES:: + sage: p, q = var('p,q', domain='real') + sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere') + sage: ode = sphere._create_geodesic_ode_system() + sage: ode.function(0.0, (1.0, 0.0, 1.0, 1.0)) + [1.00000000000000, 1.00000000000000, -0.4546487134128409, 3.114815449309804] + + """ + from sage.ext.fast_eval import fast_float + from sage.calculus.var import var + from sage.gsl.ode import ode_solver + + u1 = self.variables[1] + u2 = self.variables[2] + v1, v2 = var('v1, v2', domain='real') + + C = self.connection_coefficients() + + dv1 = - C[(1,1,1)]*v1**2 - 2*C[(1,2,1)]*v1*v2 - C[(2,2,1)]*v2**2 + dv2 = - C[(1,1,2)]*v1**2 - 2*C[(1,2,2)]*v1*v2 - C[(2,2,2)]*v2**2 + fun1 = fast_float(dv1, str(u1), str(u2), str(v1), str(v2)) + fun2 = fast_float(dv2, str(u1), str(u2), str(v1), str(v2)) + + geodesic_ode = ode_solver() + geodesic_ode.function = \ + lambda t, (u1, u2, v1, v2) : \ + [v1, v2, fun1(u1, u2, v1, v2), fun2(u1, u2, v1, v2)] + return geodesic_ode + + + def geodesics_numerical(self, p0, v0, tinterval): + r""" + Numerical integration of the geodesic equations. Explicitly, the + geodesic equations are given by + $\frac{d^2 u^i}{dt^2} + \Gamma^i_{jk} \frac{d u^j}{dt} \frac{d u^k}{dt} = 0$. + + Solving these equations gives the coordinates $(u^1, u^2)$ of + the geodesic on the surface. The coordinates in space can + then be found by substituting $(u^1, u^2)$ into the vector + $\vec{r}(u^1, u^2)$ representing the surface. + + ALGORITHM: + + The geodesic equations are integrated forward in time using + the ode solvers from ``sage.gsl.ode``. See the member + function ``_create_geodesic_ode_system`` for more details. + + INPUT: + + - ``p0`` - 2-tuple with coordinates of the initial point. + + - ``v0`` - 2-tuple with components of the initial tangent vector to the geodesic. + + - ``tinterval`` - List ``[a, b, M]``, where ``(a,b)`` is the domain of the geodesic and ``M`` is the number of subdivision points used when returning the solution. + + OUTPUT: + + List of lists ``[t, [u1(t), u2(t)], [v1(t), v2(t)], [x1(t), x2(t), x3(t)]]``, where + + - ``t`` is a subdivision point; + + - ``[u1(t), u2(t)]`` are the intrinsic coordinates of the geodesic point; + + - ``[v1(t), v2(t)]`` are the intrinsic coordinates of the tangent vector to the geodesic; + + - ``[x1(t), x2(t), x3(t)]`` are the coordinates of the geodesic point in the three-dimensional space. + + EXAMPLES:: + + sage: p, q = var('p,q', domain='real') + sage: assume(cos(q)>0) + sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere') + sage: geodesic = sphere.geodesics_numerical([0.0,0.0],[1.0,1.0],[0,2*pi,5]) + sage: times, points, tangent_vectors, ext_points = zip(*geodesic) + + sage: round4 = lambda vec: [N(x, digits=4) for x in vec] # helper function to round to 4 digits + sage: round4(times) + [0.0000, 1.257, 2.513, 3.770, 5.027, 6.283] + sage: [round4(p) for p in points] + [[0.0000, 0.0000], [0.7644, 1.859], [-0.2876, 3.442], [-0.6137, 5.502], [0.5464, 6.937], [0.3714, 9.025]] + sage: [round4(p) for p in ext_points] + [[1.000, 0.0000, 0.0000], [-0.2049, 0.6921, 0.6921], [-0.9160, -0.2836, -0.2836], [0.5803, -0.5759, -0.5759], [0.6782, 0.5196, 0.5196], [-0.8582, 0.3629, 0.3629]] + """ + + u1 = self.variables[1] + u2 = self.variables[2] + + solver = self._create_geodesic_ode_system() + + t_interval, n = tinterval[0:2], tinterval[2] + solver.y_0 = [p0[0], p0[1], v0[0], v0[1]] + solver.ode_solve(t_span=t_interval, num_points=n) + + parsed_solution = \ + [[vec[0], vec[1][0:2], vec[1][2:], self.point(vec[1])] + for vec in solver.solution] + + return parsed_solution + + + @cached_method + def _create_pt_ode_system(self, curve, t): + """ + Helper method to create a fast floating-point version of the parallel + transport equations, used by ``parallel_translation_numerical``. + + INPUT: + + - ``curve`` - curve in intrinsic coordinates along which to do parallel transport. + - ``t`` - curve parameter + + EXAMPLES:: + sage: p, q = var('p,q', domain='real') + sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],[p,q],'sphere') + sage: s = var('s') + sage: ode = sphere._create_pt_ode_system((s, s), s) + sage: ode.function(0.0, (1.0, 1.0)) + [-0.0, 0.0] + + """ + + from sage.ext.fast_eval import fast_float + from sage.calculus.var import var + from sage.gsl.ode import ode_solver + + u1 = self.variables[1] + u2 = self.variables[2] + v1, v2 = var('v1, v2', domain='real') + + du1 = diff(curve[0], t) + du2 = diff(curve[1], t) + + C = self.connection_coefficients() + for coef in C: + C[coef] = C[coef].subs({u1: curve[0], u2: curve[1]}) + + dv1 = - C[(1,1,1)]*v1*du1 - C[(1,2,1)]*(du1*v2 + du2*v1) - \ + C[(2,2,1)]*du2*v2 + dv2 = - C[(1,1,2)]*v1*du1 - C[(1,2,2)]*(du1*v2 + du2*v1) - \ + C[(2,2,2)]*du2*v2 + fun1 = fast_float(dv1, str(t), str(v1), str(v2)) + fun2 = fast_float(dv2, str(t), str(v1), str(v2)) + + pt_ode = ode_solver() + pt_ode.function = lambda t, (v1, v2): [fun1(t, v1, v2), fun2(t, v1, v2)] + return pt_ode + + + def parallel_translation_numerical(self,curve,t,v0,tinterval): + r""" + Numerically solves the equations for parallel translation of a vector + along a curve on the surface. Explicitly, the equations for parallel + translation are given by + $\frac{d u^i}{dt} + u^j \frac{d c^k}{dt} \Gamma^i_{jk} = 0$, + where $\Gamma^i_{jk}$ are the connection coefficients of the surface, + the vector to be transported has components $u^j$ and the curve along + which to transport has components $c^k$. + + ALGORITHM: + + The parallel transport equations are integrated forward in time using + the ode solvers from ``sage.gsl.ode``. See :meth:`_create_pt_ode_system` + for more details. + + INPUT: + + - ``curve`` - 2-tuple of functions which determine the curve with respect to + the local coordinate system; + + - ``t`` - symbolic variable denoting the curve parameter; + + - ``v0`` - 2-tuple representing the initial vector; + + - ``tinterval`` - list ``[a, b, N]``, where ``(a, b)`` is the domain of the curve + and ``N`` is the number of subdivision points. + + OUTPUT: + + The list consisting of lists ``[t, [v1(t), v2(t)]]``, where + + - ``t`` is a subdivision point; + + - ``[v1(t), v2(t)]`` is the list of coordinates of the vector parallel translated + along the curve. + + EXAMPLES:: + + sage: p, q = var('p,q', domain='real') + sage: v = [p,q] + sage: assume(cos(q)>0) + sage: sphere = ParametrizedSurface3D([cos(q)*cos(p),sin(q)*cos(p),sin(p)],v,'sphere') + sage: s = var('s') + sage: vector_field = sphere.parallel_translation_numerical([s,s],s,[1.0,1.0],[0.0, pi/4, 5]) + sage: times, components = zip(*vector_field) + + sage: round4 = lambda vec: [N(x, digits=4) for x in vec] # helper function to round to 4 digits + sage: round4(times) + [0.0000, 0.1571, 0.3142, 0.4712, 0.6283, 0.7854] + sage: [round4(v) for v in components] + [[1.000, 1.000], [0.9876, 1.025], [0.9499, 1.102], [0.8853, 1.238], [0.7920, 1.448], [0.6687, 1.762]] + + """ + + u1 = self.variables[1] + u2 = self.variables[2] + + solver = self._create_pt_ode_system(tuple(curve), t) + + t_interval, n = tinterval[0:2], tinterval[2] + solver.y_0 = v0 + solver.ode_solve(t_span=t_interval, num_points=n) + + return solver.solution diff --git a/src/sage/geometry/riemannian_manifolds/surface3d_generators.py b/src/sage/geometry/riemannian_manifolds/surface3d_generators.py new file mode 100644 index 00000000000..62b17a73378 --- /dev/null +++ b/src/sage/geometry/riemannian_manifolds/surface3d_generators.py @@ -0,0 +1,457 @@ +r""" +Common parametrized surfaces in 3D. + +AUTHORS:: + +- Joris Vankerschaver (2012-06-16) + +""" + +#***************************************************************************** +# Copyright (C) 2010 Joris Vankerschaver +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +from sage.symbolic.constants import pi +from sage.functions.log import log +from sage.functions.trig import sin, cos, tan +from sage.functions.hyperbolic import cosh, sinh, tanh +from sage.symbolic.ring import SR, var +from sage.geometry.riemannian_manifolds.parametrized_surface3d import \ + ParametrizedSurface3D + + +class SurfaceGenerators(): + """ + A class consisting of generators for several common parametrized surfaces + in 3D. + + """ + @staticmethod + def Catenoid(c=1, name="Catenoid"): + r""" + Returns a catenoid surface, with parametric representation + + .. MATH:: + + \begin{aligned} + x(u, v) & = c \cosh(v/c) \cos(u); \\ + y(u, v) & = c \cosh(v/c) \sin(u); \\ + z(u, v) & = v. + \end{aligned} + + INPUT: + + - ``c`` -- surface parameter. + + - ``name`` -- string. Name of the surface. + + + EXAMPLES:: + + sage: cat = surfaces.Catenoid(); cat + Parametrized surface ('Catenoid') with equation (cos(u)*cosh(v), cosh(v)*sin(u), v) + sage: cat.plot() + + """ + u, v = var('u, v') + catenoid_eq = [c*cosh(v/c)*cos(u), c*cosh(v/c)*sin(u), v] + coords = ((u, 0, 2*pi), (v, -1, 1)) + + return ParametrizedSurface3D(catenoid_eq, coords, name) + + @staticmethod + def Crosscap(r=1, name="Crosscap"): + r""" + Returns a crosscap surface, with parametrization + + .. MATH:: + + \begin{aligned} + x(u, v) & = r(1 + \cos(v)) \cos(u); \\ + y(u, v) & = r(1 + \cos(v)) \sin(u); \\ + z(u, v) & = - r\tanh(u - \pi) \sin(v). + \end{aligned} + + INPUT: + + - ``r`` -- surface parameter. + + - ``name`` -- string. Name of the surface. + + EXAMPLES:: + + sage: crosscap = surfaces.Crosscap(); crosscap + Parametrized surface ('Crosscap') with equation ((cos(v) + 1)*cos(u), (cos(v) + 1)*sin(u), -sin(v)*tanh(-pi + u)) + sage: crosscap.plot() + + """ + + u, v = var('u, v') + crosscap_eq = [r*(1+cos(v))*cos(u), r*(1+cos(v))*sin(u), + -tanh(u-pi)*r*sin(v)] + coords = ((u, 0, 2*pi), (v, 0, 2*pi)) + + return ParametrizedSurface3D(crosscap_eq, coords, name) + + @staticmethod + def Dini(a=1, b=1, name="Dini's surface"): + r""" + Returns Dini's surface, with parametrization + + .. MATH:: + + \begin{aligned} + x(u, v) & = a \cos(u)\sin(v); \\ + y(u, v) & = a \sin(u)\sin(v); \\ + z(u, v) & = u + \log(\tan(v/2)) + \cos(v). + \end{aligned} + + INPUT: + + - ``a, b`` -- surface parameters. + + - ``name`` -- string. Name of the surface. + + EXAMPLES:: + + sage: dini = surfaces.Dini(a=3, b=4); dini + Parametrized surface ('Dini's surface') with equation (3*cos(u)*sin(v), 3*sin(u)*sin(v), 4*u + 3*cos(v) + 3*log(tan(1/2*v))) + sage: dini.plot() # not tested -- known bug (see #10132) + + """ + + u, v = var('u, v') + dini_eq = [a*cos(u)*sin(v), a*sin(u)*sin(v), + a*(cos(v) + log(tan(v/2))) + b*u] + coords = ((u, 0, 2*pi), (v, 0, 2*pi)) + + + return ParametrizedSurface3D(dini_eq, coords, name) + + @staticmethod + def Ellipsoid(center=(0,0,0), axes=(1,1,1), name="Ellipsoid"): + r""" + Returns an ellipsoid centered at ``center`` whose semi-principal axes + have lengths given by the components of ``axes``. The + parametrization of the ellipsoid is given by + + .. MATH:: + + \begin{aligned} + x(u, v) & = x_0 + a \cos(u) \cos(v); \\ + y(u, v) & = y_0 + b \sin(u) \cos(v); \\ + z(u, v) & = z_0 + c \sin(v). + \end{aligned} + + INPUT: + + - ``center`` -- 3-tuple. Coordinates of the center of the ellipsoid. + + - ``axes`` -- 3-tuple. Lengths of the semi-principal axes. + + - ``name`` -- string. Name of the ellipsoid. + + EXAMPLES:: + + sage: ell = surfaces.Ellipsoid(axes=(1, 2, 3)); ell + Parametrized surface ('Ellipsoid') with equation (cos(u)*cos(v), 2*cos(v)*sin(u), 3*sin(v)) + sage: ell.plot() + + """ + + u, v = var ('u, v') + x, y, z = center + a, b, c = axes + ellipsoid_parametric_eq = [x + a*cos(u)*cos(v), + y + b*sin(u)*cos(v), + z + c*sin(v)] + coords = ((u, 0, 2*pi), (v, -pi/2, pi/2)) + + return ParametrizedSurface3D(ellipsoid_parametric_eq, coords, name) + + @staticmethod + def Enneper(name="Enneper's surface"): + r""" + Returns Enneper's surface, with parametrization + + .. MATH:: + + \begin{aligned} + x(u, v) & = u(1 - u^2/3 + v^2)/3; \\ + y(u, v) & = -v(1 - v^2/3 + u^2)/3; \\ + z(u, v) & = (u^2 - v^2)/3. + \end{aligned} + + INPUT: + + - ``name`` -- string. Name of the surface. + + EXAMPLES:: + + sage: enn = surfaces.Enneper(); enn + Parametrized surface ('Enneper's surface') with equation (-1/9*(u^2 - 3*v^2 - 3)*u, -1/9*(3*u^2 - v^2 + 3)*v, 1/3*u^2 - 1/3*v^2) + sage: enn.plot() + + """ + + u, v = var('u, v') + enneper_eq = [u*(1-u**2/3+v**2)/3, -v*(1-v**2/3+u**2)/3, (u**2-v**2)/3] + coords = ((u, -3, 3), (v, -3, 3)) + + return ParametrizedSurface3D(enneper_eq, coords, name) + + @staticmethod + def Helicoid(h=1, name="Helicoid"): + r""" + Returns a helicoid surface, with parametrization + + .. MATH:: + + \begin{aligned} + x(\rho, \theta) & = \rho \cos(\theta); \\ + y(\rho, \theta) & = \rho \sin(\theta); \\ + z(\rho, \theta) & = h\theta/(2\pi). + \end{aligned} + + INPUT: + + - ``h`` -- distance along the z-axis between two + successive turns of the helicoid. + + - ``name`` -- string. Name of the surface. + + EXAMPLES:: + + sage: helicoid = surfaces.Helicoid(h=2); helicoid + Parametrized surface ('Helicoid') with equation (rho*cos(theta), rho*sin(theta), theta/pi) + sage: helicoid.plot() + + """ + + rho, theta = var('rho, theta') + helicoid_eq = [rho*cos(theta), rho*sin(theta), h*theta/(2*pi)] + coords = ((rho, -2, 2), (theta, 0, 2*pi)) + + return ParametrizedSurface3D(helicoid_eq, [rho, theta], name) + + @staticmethod + def Klein(r=1, name="Klein bottle"): + r""" + Returns the Klein bottle, in the figure-8 parametrization given by + + .. MATH:: + + \begin{aligned} + x(u, v) & = (r + \cos(u/2)\cos(v) - \sin(u/2)\sin(2v)) \cos(u); \\ + y(u, v) & = (r + \cos(u/2)\cos(v) - \sin(u/2)\sin(2v)) \sin(u); \\ + z(u, v) & = \sin(u/2)\cos(v) + \cos(u/2)\sin(2v). + \end{aligned} + + INPUT: + + - ``r`` -- radius of the "figure-8" circle. + + - ``name`` -- string. Name of the surface. + + EXAMPLES:: + + sage: klein = surfaces.Klein(); klein + Parametrized surface ('Klein bottle') with equation (-(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*cos(u), -(sin(1/2*u)*sin(2*v) - cos(1/2*u)*sin(v) - 1)*sin(u), cos(1/2*u)*sin(2*v) + sin(1/2*u)*sin(v)) + sage: klein.plot() + + """ + + u, v = var('u, v') + x = (r + cos(u/2)*sin(v) - sin(u/2)*sin(2*v))*cos(u) + y = (r + cos(u/2)*sin(v) - sin(u/2)*sin(2*v))*sin(u) + z = sin(u/2)*sin(v) + cos(u/2)*sin(2*v) + klein_eq = [x, y, z] + coords = ((u, 0, 2*pi), (v, 0, 2*pi)) + + return ParametrizedSurface3D(klein_eq, coords, name) + + @staticmethod + def MonkeySaddle(name="Monkey saddle"): + r""" + Returns a monkey saddle surface, with equation + + .. MATH:: + + z = x^3 - 3xy^2. + + INPUT: + + - ``name`` -- string. Name of the surface. + + EXAMPLES:: + + sage: saddle = surfaces.MonkeySaddle(); saddle + Parametrized surface ('Monkey saddle') with equation (u, v, u^3 - 3*u*v^2) + sage: saddle.plot() + + """ + + u, v = var('u, v') + monkey_eq = [u, v, u**3 - 3*u*v**2] + coords = ((u, -2, 2), (v, -2, 2)) + + return ParametrizedSurface3D(monkey_eq, coords, name) + + @staticmethod + def Paraboloid(a=1, b=1, c=1, elliptic=True, name=None): + r""" + Returns a paraboloid with equation + + .. MATH:: + + \frac{z}{c} = \pm \frac{x^2}{a^2} + \frac{y^2}{b^2} + + When the plus sign is selected, the paraboloid is elliptic. Otherwise + the surface is a hyperbolic paraboloid. + + INPUT: + + - ``a``, ``b``, ``c`` -- Surface parameters. + + - ``elliptic`` (default: True) -- whether to create an elliptic or + hyperbolic paraboloid. + + - ``name`` -- string. Name of the surface. + + EXAMPLES:: + + sage: epar = surfaces.Paraboloid(1, 3, 2); epar + Parametrized surface ('Elliptic paraboloid') with equation (u, v, 2*u^2 + 2/9*v^2) + sage: epar.plot() + + sage: hpar = surfaces.Paraboloid(2, 3, 1, elliptic=False); hpar + Parametrized surface ('Hyperbolic paraboloid') with equation (u, v, -1/4*u^2 + 1/9*v^2) + sage: hpar.plot() + + """ + + u, v = var('u, v') + x = u; y = v + if elliptic: + z = c*(v**2/b**2 + u**2/a**2) + else: + z = c*(v**2/b**2 - u**2/a**2) + paraboloid_eq = [x, y, z] + coords = ((u, -3, 3), (v, -3, 3)) + + if name is None: + if elliptic: + name = "Elliptic paraboloid" + else: + name = "Hyperbolic paraboloid" + + return ParametrizedSurface3D(paraboloid_eq, coords, name) + + @staticmethod + def Sphere(center=(0,0,0), R=1, name="Sphere"): + r""" + Returns a sphere of radius ``R`` centered at ``center``. + + INPUT: + + - ``center`` -- 3-tuple, center of the sphere. + + - ``R`` -- Radius of the sphere. + + - ``name`` -- string. Name of the surface. + + EXAMPLES:: + + sage: sphere = surfaces.Sphere(center=(0, 1, -1), R=2); sphere + Parametrized surface ('Sphere') with equation (2*cos(u)*cos(v), 2*cos(v)*sin(u) + 1, 2*sin(v) - 1) + sage: sphere.plot() + + Note that the radius of the sphere can be negative. The surface thus + obtained is equal to the sphere (or part thereof) with positive radius, + whose coordinate functions have been multiplied by -1. Compare for + instant the first octant of the unit sphere with positive radius:: + + sage: octant1 = surfaces.Sphere(R=1); octant1 + Parametrized surface ('Sphere') with equation (cos(u)*cos(v), cos(v)*sin(u), sin(v)) + sage: octant1.plot((0, pi/2), (0, pi/2)) + + with the first octant of the unit sphere with negative radius:: + + sage: octant2 = surfaces.Sphere(R=-1); octant2 + Parametrized surface ('Sphere') with equation (-cos(u)*cos(v), -cos(v)*sin(u), -sin(v)) + sage: octant2.plot((0, pi/2), (0, pi/2)) + + """ + + return SurfaceGenerators.Ellipsoid(center, (R, R, R), name) + + @staticmethod + def Torus(r=2, R=3, name="Torus"): + r""" + Returns a torus obtained by revolving a circle of radius ``r`` around + a coplanar axis ``R`` units away from the center of the circle. The + parametrization used is + + .. MATH:: + + \begin{aligned} + x(u, v) & = (R + r \cos(v)) \cos(u); \\ + y(u, v) & = (R + r \cos(v)) \sin(u); \\ + z(u, v) & = r \sin(v). + \end{aligned} + + INPUT: + + - ``r``, ``R`` -- Minor and major radius of the torus. + + - ``name`` -- string. Name of the surface. + + EXAMPLES:: + + sage: torus = surfaces.Torus(); torus + Parametrized surface ('Torus') with equation ((2*cos(v) + 3)*cos(u), (2*cos(v) + 3)*sin(u), 2*sin(v)) + sage: torus.plot() + + """ + + u, v = var('u, v') + torus_eq = [(R+r*cos(v))*cos(u), (R+r*cos(v))*sin(u), r*sin(v)] + coords = ((u, 0, 2*pi), (v, 0, 2*pi)) + + return ParametrizedSurface3D(torus_eq, coords, name) + + @staticmethod + def WhitneyUmbrella(name="Whitney's umbrella"): + r""" + Returns Whitney's umbrella, with parametric representation + + .. MATH:: + + x(u, v) = uv, \quad y(u, v) = u, \quad z(u, v) = v^2. + + INPUT: + + - ``name`` -- string. Name of the surface. + + EXAMPLES:: + + sage: whitney = surfaces.WhitneyUmbrella(); whitney + Parametrized surface ('Whitney's umbrella') with equation (u*v, u, v^2) + sage: whitney.plot() + + """ + + u, v = var('u, v') + whitney_eq = [u*v, u, v**2] + coords = ((u, -1, 1), (v, -1, 1)) + + return ParametrizedSurface3D(whitney_eq, coords, name) + + +# Easy access to the surface generators +surfaces = SurfaceGenerators() + From 05f98df69d4bae344e96888de97c7def5e3c072b Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Fri, 22 Nov 2013 12:47:25 +0000 Subject: [PATCH 131/206] Trac #11628: cache factored order in FiniteField_base --- src/sage/rings/finite_rings/finite_field_base.pyx | 3 +++ src/sage/rings/finite_rings/finite_field_prime_modn.py | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index de0729425a2..fca287ff1f7 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -524,6 +524,9 @@ cdef class FiniteField(Field): """ raise NotImplementedError + # cached because constructing the Factorization is slow; + # see :trac:`11628`. + @cached_method def factored_order(self): """ Returns the factored order of this field. For compatibility with diff --git a/src/sage/rings/finite_rings/finite_field_prime_modn.py b/src/sage/rings/finite_rings/finite_field_prime_modn.py index 98fb8742b80..a31329a6566 100644 --- a/src/sage/rings/finite_rings/finite_field_prime_modn.py +++ b/src/sage/rings/finite_rings/finite_field_prime_modn.py @@ -78,7 +78,6 @@ def __init__(self, p, name=None, check=True): if check and not arith.is_prime(p): raise ArithmeticError, "p must be prime" self.__char = p - self._IntegerModRing_generic__factored_order = factorization.Factorization([(p,1)], integer.Integer(1)) self._kwargs = {} # FiniteField_generic does nothing more than IntegerModRing_generic, and # it saves a non trivial overhead From 0d84908da34beb0e7ab257566a60b5e21b4b9488 Mon Sep 17 00:00:00 2001 From: Alyson Deines Date: Tue, 11 Jan 2011 22:02:39 -0800 Subject: [PATCH 132/206] Trac #9411: lll_reduce for elliptic curves over number fields --- .../elliptic_curves/ell_number_field.py | 124 ++++++++++++++++++ .../elliptic_curves/ell_rational_field.py | 80 ----------- 2 files changed, 124 insertions(+), 80 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index f476d062987..bc19d047261 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -100,6 +100,7 @@ import ell_torsion from ell_generic import is_EllipticCurve +from sage.libs.pari.all import pari, PariError from gp_simon import simon_two_descent from constructor import EllipticCurve from sage.rings.all import PolynomialRing, ZZ, RealField @@ -2156,6 +2157,129 @@ def isogeny_degree(self, other): raise NotImplementedError, "Not all isogenies implemented over general number fields." + def lll_reduce(self, points, height_matrix=None, precision=None): + """ + Returns an LLL-reduced basis from a given basis, with transform + matrix. + + INPUT: + + + - ``points`` - a list of points on this elliptic + curve, which should be independent. + + - ``height_matrix`` - the height-pairing matrix of + the points, or None. If None, it will be computed. + + - ``precision`` - number of bits of precision of intermediate + computations (default: None, for default RealField + precision; ignored if height_matrix is supplied) + + + OUTPUT: A tuple (newpoints,U) where U is a unimodular integer + matrix, new_points is the transform of points by U, such that + new_points has LLL-reduced height pairing matrix + + .. note:: + + If the input points are not independent, the output depends + on the undocumented behaviour of PARI's ``qflllgram()`` + function when applied to a gram matrix which is not + positive definite. + + EXAMPLES: + + Some examples over `QQ`:: + + sage: E = EllipticCurve([0, 1, 1, -2, 42]) + sage: Pi = E.gens(); Pi + [(-4 : 1 : 1), (-3 : 5 : 1), (-11/4 : 43/8 : 1), (-2 : 6 : 1)] + sage: Qi, U = E.lll_reduce(Pi) + sage: all(sum(U[i,j]*Pi[i] for i in range(4)) == Qi[j] for j in range(4)) + True + sage: sorted(Qi) + [(-4 : 1 : 1), (-3 : 5 : 1), (-2 : 6 : 1), (0 : 6 : 1)] + sage: U.det() + 1 + sage: E.regulator_of_points(Pi) + 4.59088036960573 + sage: E.regulator_of_points(Qi) + 4.59088036960574 + + :: + + sage: E = EllipticCurve([1,0,1,-120039822036992245303534619191166796374,504224992484910670010801799168082726759443756222911415116]) + sage: xi = [2005024558054813068,\ + -4690836759490453344,\ + 4700156326649806635,\ + 6785546256295273860,\ + 6823803569166584943,\ + 7788809602110240789,\ + 27385442304350994620556,\ + 54284682060285253719/4,\ + -94200235260395075139/25,\ + -3463661055331841724647/576,\ + -6684065934033506970637/676,\ + -956077386192640344198/2209,\ + -27067471797013364392578/2809,\ + -25538866857137199063309/3721,\ + -1026325011760259051894331/108241,\ + 9351361230729481250627334/1366561,\ + 10100878635879432897339615/1423249,\ + 11499655868211022625340735/17522596,\ + 110352253665081002517811734/21353641,\ + 414280096426033094143668538257/285204544,\ + 36101712290699828042930087436/4098432361,\ + 45442463408503524215460183165/5424617104,\ + 983886013344700707678587482584/141566320009,\ + 1124614335716851053281176544216033/152487126016] + sage: points = [E.lift_x(x) for x in xi] + sage: newpoints, U = E.lll_reduce(points) # long time (35s on sage.math, 2011) + sage: [P[0] for P in newpoints] # long time + [6823803569166584943, 5949539878899294213, 2005024558054813068, 5864879778877955778, 23955263915878682727/4, 5922188321411938518, 5286988283823825378, 175620639884534615751/25, -11451575907286171572, 3502708072571012181, 1500143935183238709184/225, 27180522378120223419/4, -5811874164190604461581/625, 26807786527159569093, 7404442636649562303, 475656155255883588, 265757454726766017891/49, 7272142121019825303, 50628679173833693415/4, 6951643522366348968, 6842515151518070703, 111593750389650846885/16, 2607467890531740394315/9, -1829928525835506297] + + An example to show the explicit use of the height pairing matrix:: + + sage: E = EllipticCurve([0, 1, 1, -2, 42]) + sage: Pi = E.gens() + sage: H=E.height_pairing_matrix(Pi,3) + sage: E.lll_reduce(Pi,height_matrix=H) + ( + [1 0 0 1] + [0 1 0 1] + [0 0 0 1] + [(-4 : 1 : 1), (-3 : 5 : 1), (-2 : 6 : 1), (1 : -7 : 1)], [0 0 1 1] + ) + + Some examples over number fields (see trac #9411):: + + sage: K. = QuadraticField(-23, 'a') + sage: E = EllipticCurve(K, '37') + sage: E.lll_reduce(E.gens()) + ( + [1 1] + [(-1 : 0 : 1), (-2 : 1/2*a - 1/2 : 1)], [0 1] + ) + + :: + + sage: K. = QuadraticField(-5) + sage: E = EllipticCurve(K,[0,a]) + sage: points = [E.point([-211/841*a - 6044/841,-209584/24389*a + 53634/24389]),E.point([-17/18*a - 1/9, -109/108*a - 277/108]) ] + sage: E.lll_reduce(points) + ( + [(-a + 4 : -3*a + 7 : 1), (-17/18*a - 1/9 : 109/108*a + 277/108 : 1)], + [ 1 0] + [ 1 -1] + ) + """ + r = len(points) + if height_matrix is None: + height_matrix = self.height_pairing_matrix(points,precision) + U = pari(height_matrix).lllgram().python() + new_points = [sum([U[j,i]*points[j] for j in range(r)]) for i in range(r)] + return new_points, U + def galois_representation(self): r""" diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index a6db94e4af8..e640d6cfa16 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -5232,86 +5232,6 @@ def height(self, precision=None): h_gs = max(1, log_g2) return max(R(1),h_j, h_gs) - def lll_reduce(self, points, height_matrix=None): - """ - Returns an LLL-reduced basis from a given basis, with transform - matrix. - - INPUT: - - - - ``points`` - a list of points on this elliptic - curve, which should be independent. - - - ``height_matrix`` - the height-pairing matrix of - the points, or None. If None, it will be computed. - - - OUTPUT: A tuple (newpoints,U) where U is a unimodular integer - matrix, new_points is the transform of points by U, such that - new_points has LLL-reduced height pairing matrix - - .. note:: - - If the input points are not independent, the output depends - on the undocumented behaviour of PARI's ``qflllgram()`` - function when applied to a gram matrix which is not - positive definite. - - EXAMPLE:: - - sage: E = EllipticCurve([0, 1, 1, -2, 42]) - sage: Pi = E.gens(); Pi - [(-4 : 1 : 1), (-3 : 5 : 1), (-11/4 : 43/8 : 1), (-2 : 6 : 1)] - sage: Qi, U = E.lll_reduce(Pi) - sage: sorted(Qi) - [(-4 : 1 : 1), (-3 : 5 : 1), (-2 : 6 : 1), (0 : 6 : 1)] - sage: U.det() - 1 - sage: E.regulator_of_points(Pi) - 4.59088036960573 - sage: E.regulator_of_points(Qi) - 4.59088036960574 - - :: - - sage: E = EllipticCurve([1,0,1,-120039822036992245303534619191166796374,504224992484910670010801799168082726759443756222911415116]) - sage: xi = [2005024558054813068,\ - -4690836759490453344,\ - 4700156326649806635,\ - 6785546256295273860,\ - 6823803569166584943,\ - 7788809602110240789,\ - 27385442304350994620556,\ - 54284682060285253719/4,\ - -94200235260395075139/25,\ - -3463661055331841724647/576,\ - -6684065934033506970637/676,\ - -956077386192640344198/2209,\ - -27067471797013364392578/2809,\ - -25538866857137199063309/3721,\ - -1026325011760259051894331/108241,\ - 9351361230729481250627334/1366561,\ - 10100878635879432897339615/1423249,\ - 11499655868211022625340735/17522596,\ - 110352253665081002517811734/21353641,\ - 414280096426033094143668538257/285204544,\ - 36101712290699828042930087436/4098432361,\ - 45442463408503524215460183165/5424617104,\ - 983886013344700707678587482584/141566320009,\ - 1124614335716851053281176544216033/152487126016] - sage: points = [E.lift_x(x) for x in xi] - sage: newpoints, U = E.lll_reduce(points) # long time (36s on sage.math, 2011) - sage: [P[0] for P in newpoints] # long time - [6823803569166584943, 5949539878899294213, 2005024558054813068, 5864879778877955778, 23955263915878682727/4, 5922188321411938518, 5286988283823825378, 175620639884534615751/25, -11451575907286171572, 3502708072571012181, 1500143935183238709184/225, 27180522378120223419/4, -5811874164190604461581/625, 26807786527159569093, 7404442636649562303, 475656155255883588, 265757454726766017891/49, 7272142121019825303, 50628679173833693415/4, 6951643522366348968, 6842515151518070703, 111593750389650846885/16, 2607467890531740394315/9, -1829928525835506297] - """ - r = len(points) - if height_matrix is None: - height_matrix = self.height_pairing_matrix(points) - U = pari(height_matrix).lllgram().python() - new_points = [sum([U[j,i]*points[j] for j in range(r)]) for i in range(r)] - return new_points, U - def antilogarithm(self, z, max_denominator=None): r""" Returns the rational point (if any) associated to this complex From ca14f3d03d4967d0345cbe8d260cb75575ef4029 Mon Sep 17 00:00:00 2001 From: Frederic Chapoton Date: Wed, 11 Sep 2013 22:04:09 +0200 Subject: [PATCH 133/206] Trac #9411: review patch (minor details) --- .../elliptic_curves/ell_number_field.py | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index bc19d047261..f20ca053b24 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -684,14 +684,6 @@ def _reduce_model(self): return self.rst_transform(r, s, t) - def local_information(self, P=None, proof=None): - r""" - \code{local_information} has been renamed \code{local_data} - and is being deprecated. - """ - raise DeprecationWarning, "local_information is deprecated; use local_data instead" - return self.local_data(P,proof) - def local_data(self, P=None, proof = None, algorithm="pari"): r""" Local data for this elliptic curve at the prime `P`. @@ -2155,7 +2147,7 @@ def isogeny_degree(self, other): degrees.extend(newdegs) k = k+1 - raise NotImplementedError, "Not all isogenies implemented over general number fields." + raise NotImplementedError("Not all isogenies implemented over general number fields.") def lll_reduce(self, points, height_matrix=None, precision=None): """ @@ -2164,32 +2156,30 @@ def lll_reduce(self, points, height_matrix=None, precision=None): INPUT: + - ``points`` - a list of points on this elliptic + curve, which should be independent. - - ``points`` - a list of points on this elliptic - curve, which should be independent. - - - ``height_matrix`` - the height-pairing matrix of - the points, or None. If None, it will be computed. + - ``height_matrix`` - the height-pairing matrix of + the points, or ``None``. If ``None``, it will be computed. - ``precision`` - number of bits of precision of intermediate - computations (default: None, for default RealField - precision; ignored if height_matrix is supplied) + computations (default: ``None``, for default RealField + precision; ignored if ``height_matrix`` is supplied) - - OUTPUT: A tuple (newpoints,U) where U is a unimodular integer + OUTPUT: A tuple (newpoints, U) where U is a unimodular integer matrix, new_points is the transform of points by U, such that new_points has LLL-reduced height pairing matrix .. note:: - If the input points are not independent, the output depends - on the undocumented behaviour of PARI's ``qflllgram()`` - function when applied to a gram matrix which is not - positive definite. + If the input points are not independent, the output + depends on the undocumented behaviour of PARI's + ``qflllgram()`` function when applied to a gram matrix which + is not positive definite. EXAMPLES: - Some examples over `QQ`:: + Some examples over `\QQ`:: sage: E = EllipticCurve([0, 1, 1, -2, 42]) sage: Pi = E.gens(); Pi @@ -2242,7 +2232,7 @@ def lll_reduce(self, points, height_matrix=None, precision=None): sage: E = EllipticCurve([0, 1, 1, -2, 42]) sage: Pi = E.gens() - sage: H=E.height_pairing_matrix(Pi,3) + sage: H = E.height_pairing_matrix(Pi,3) sage: E.lll_reduce(Pi,height_matrix=H) ( [1 0 0 1] @@ -2251,7 +2241,7 @@ def lll_reduce(self, points, height_matrix=None, precision=None): [(-4 : 1 : 1), (-3 : 5 : 1), (-2 : 6 : 1), (1 : -7 : 1)], [0 0 1 1] ) - Some examples over number fields (see trac #9411):: + Some examples over number fields (see :trac:`9411`):: sage: K. = QuadraticField(-23, 'a') sage: E = EllipticCurve(K, '37') @@ -2275,12 +2265,12 @@ def lll_reduce(self, points, height_matrix=None, precision=None): """ r = len(points) if height_matrix is None: - height_matrix = self.height_pairing_matrix(points,precision) + height_matrix = self.height_pairing_matrix(points, precision) U = pari(height_matrix).lllgram().python() - new_points = [sum([U[j,i]*points[j] for j in range(r)]) for i in range(r)] + new_points = [sum([U[j, i]*points[j] for j in range(r)]) + for i in range(r)] return new_points, U - def galois_representation(self): r""" The compatible family of the Galois representation @@ -2310,4 +2300,3 @@ def galois_representation(self): [5] """ return gal_reps_number_field.GaloisRepresentation(self) - From 4410193228ae7582682141cc590082f5017f1936 Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Wed, 16 Jan 2013 08:52:13 +0000 Subject: [PATCH 134/206] Trac #13849: deprecate degrevlex --- src/sage/crypto/mq/sr.py | 22 +++--- .../polynomial/multi_polynomial_sequence.py | 4 +- src/sage/rings/polynomial/pbori.pyx | 71 ++++++------------- .../polynomial/polynomial_ring_constructor.py | 8 +-- 4 files changed, 38 insertions(+), 67 deletions(-) diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index f87e7e20244..dfac47e0dfb 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -1671,9 +1671,9 @@ def block_order(self): sage: sr = mq.SR(2, 1, 1, 4) sage: sr.block_order() Block term order with blocks: - (Degree reverse lexicographic term order of length 16, - Degree reverse lexicographic term order of length 16, - Degree reverse lexicographic term order of length 4) + (Degree lexicographic term order of length 16, + Degree lexicographic term order of length 16, + Degree lexicographic term order of length 4) :: @@ -1682,11 +1682,11 @@ def block_order(self): Polynomial Ring Base Ring : Finite Field in a of size 2^4 Size : 36 Variables - Block 0 : Ordering : degrevlex + Block 0 : Ordering : deglex Names : k200, k201, k202, k203, x200, x201, x202, x203, w200, w201, w202, w203, s100, s101, s102, s103 - Block 1 : Ordering : degrevlex + Block 1 : Ordering : deglex Names : k100, k101, k102, k103, x100, x101, x102, x103, w100, w101, w102, w103, s000, s001, s002, s003 - Block 2 : Ordering : degrevlex + Block 2 : Ordering : deglex Names : k000, k001, k002, k003 """ r = self.r @@ -1696,9 +1696,9 @@ def block_order(self): T = None for _n in range(n): - T = TermOrder('degrevlex', r*e + 3*r*c*e ) + T + T = TermOrder('deglex', r*e + 3*r*c*e ) + T - T += TermOrder('degrevlex', r*c*e) + T += TermOrder('deglex', r*c*e) return T @@ -1738,11 +1738,11 @@ def ring(self, order=None, reverse_variables=None): Polynomial Ring Base Ring : Finite Field in a of size 2^4 Size : 36 Variables - Block 0 : Ordering : degrevlex + Block 0 : Ordering : deglex Names : k200, k201, k202, k203, x200, x201, x202, x203, w200, w201, w202, w203, s100, s101, s102, s103 - Block 1 : Ordering : degrevlex + Block 1 : Ordering : deglex Names : k100, k101, k102, k103, x100, x101, x102, x103, w100, w101, w102, w103, s000, s001, s002, s003 - Block 2 : Ordering : degrevlex + Block 2 : Ordering : deglex Names : k000, k001, k002, k003 """ r = self.r diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index e48eb138f95..01b2f9ba23a 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -384,9 +384,9 @@ def ring(self): Polynomial Ring Base Ring : Finite Field of size 2 Size : 20 Variables - Block 0 : Ordering : degrevlex + Block 0 : Ordering : deglex Names : k100, k101, k102, k103, x100, x101, x102, x103, w100, w101, w102, w103, s000, s001, s002, s003 - Block 1 : Ordering : degrevlex + Block 1 : Ordering : deglex Names : k000, k001, k002, k003 """ return self._ring diff --git a/src/sage/rings/polynomial/pbori.pyx b/src/sage/rings/polynomial/pbori.pyx index 8b6496f97c6..e275933c8bd 100644 --- a/src/sage/rings/polynomial/pbori.pyx +++ b/src/sage/rings/polynomial/pbori.pyx @@ -295,19 +295,13 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): :: - sage: R = BooleanPolynomialRing(3,'x',order='degrevlex') + sage: R = BooleanPolynomialRing(3,'x',order='deglex') sage: R.term_order() - Degree reverse lexicographic term order + Degree lexicographic term order TESTS:: - sage: P. = BooleanPolynomialRing(2,order='degrevlex') - sage: x > y - True - - :: - - sage: P. = BooleanPolynomialRing(4,order='degrevlex(2),degrevlex(2)') + sage: P. = BooleanPolynomialRing(4,order='deglex(2),deglex(2)') sage: x0 > x1 True sage: x2 > x3 @@ -378,9 +372,14 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): raise ValueError, "Only order keys " + \ ', '.join(order_mapping.keys()) + " are supported." + + if pb_order_code in (pbdp, pbblock_dp): + from sage.misc.superseded import deprecation + deprecation(13849, "using 'degrevlex' in Boolean polynomial rings is deprecated. If needed, reverse the order of variables manually and use 'degneglex'") + if order.is_block_order(): if pb_order_code is pblp: - raise ValueError, "Only deglex and degrevlex are supported for block orders." + raise ValueError, "Only deglex and degneglex are supported for block orders." elif pb_order_code is pbdlex: pb_order_code = pbblock_dlex elif pb_order_code is pbdp_asc: @@ -390,7 +389,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): for i in range(1, len(order.blocks())): if order[0].name() != order[i].name(): raise ValueError, "Each block must have the same order " + \ - "type (deglex, degneglex or degrevlex) for block orders." + "type (deglex and degneglex) for block orders." if pb_order_code is pbdp: for i from 0 <= i < n: @@ -505,7 +504,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): TESTS:: - sage: P. = BooleanPolynomialRing(3, order='dp') + sage: P. = BooleanPolynomialRing(3, order='deglex') sage: P.gen(0) x """ @@ -535,11 +534,6 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): sage: P.gens() (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) - TESTS:: - - sage: P. = BooleanPolynomialRing(3,order='degrevlex') - sage: P.gens() - (x, y, z) """ return tuple([new_BP_from_PBVar(self, self._pbring.variable(self.pbind[i])) \ @@ -759,15 +753,6 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): sage: P(x) x - Test conversion between 'degrevlex' and 'lex':: - - sage: B. = BooleanPolynomialRing(order='lex') - sage: P. = BooleanPolynomialRing(order='degrevlex') - sage: B(P('a')) - a - sage: P(B('a')) - a - Test that #10797 is really fixed:: sage: B. = BooleanPolynomialRing() @@ -905,13 +890,6 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): sage: p = x^3+2*x^2+x+1 sage: P(p) x - - Check that trac ticket #13202 is fixed: - - sage: B. = BooleanPolynomialRing(order='degrevlex') - sage: P. = BooleanPolynomialRing(order='lex') - sage: P(B(c)) - c """ cdef int i @@ -1594,7 +1572,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): TESTS:: - sage: P. = BooleanPolynomialRing(3, order='dp') + sage: P. = BooleanPolynomialRing(3, order='deglex') sage: P.variable(0) x """ @@ -1810,13 +1788,6 @@ def get_var_mapping(ring, other): [z, x] sage: sage.rings.polynomial.pbori.get_var_mapping(P, x^2) [None, x] - - Check that ticket #13202 is fixed:: - - sage: B. = BooleanPolynomialRing(order='degrevlex') - sage: P. = BooleanPolynomialRing(order='lex') - sage: sage.rings.polynomial.pbori.get_var_mapping(B, P) - [c, d] """ my_names = list(ring._names) # we need .index(.) @@ -3205,10 +3176,10 @@ cdef class BooleanPolynomial(MPolynomial): :: - sage: P. = BooleanPolynomialRing(3, order='degrevlex') + sage: P. = BooleanPolynomialRing(3, order='deglex') sage: p = x + z + x*y + y*z + x*y*z sage: list(iter(p)) - [z*y*x, y*x, z*y, x, z] + [x*y*z, x*y, y*z, x, z] TESTS:: @@ -3868,10 +3839,10 @@ cdef class BooleanPolynomial(MPolynomial): :: - sage: P. = BooleanPolynomialRing(3,order='degrevlex') + sage: P. = BooleanPolynomialRing(3,order='deglex') sage: f = a + c*b sage: f.monomials() - [c*b, a] + [b*c, a] sage: P.zero().monomials() [] """ @@ -3907,10 +3878,10 @@ cdef class BooleanPolynomial(MPolynomial): sage: f.terms() [a, b*c] - sage: P. = BooleanPolynomialRing(3,order='degrevlex') + sage: P. = BooleanPolynomialRing(3,order='deglex') sage: f = a + c*b sage: f.terms() - [c*b, a] + [b*c, a] """ return list(self) @@ -4491,7 +4462,7 @@ cdef class BooleanPolynomial(MPolynomial): EXAMPLE:: - sage: B. = BooleanPolynomialRing(4,order='degrevlex') + sage: B. = BooleanPolynomialRing(4,order='deglex') sage: f = (a*b + 1)*(c + 1) sage: f.reducible_by(d) False @@ -5109,10 +5080,10 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): EXAMPLES:: - sage: B. = BooleanPolynomialRing(order='degrevlex') + sage: B. = BooleanPolynomialRing(order='deglex') sage: id = B.ideal((x + y)*(y + z), x*y*z) sage: id._groebner_basis() - [z*x, y*x + z*y + y] + [x*z, x*y + y*z + y] sage: B. = BooleanPolynomialRing(order='deglex') sage: id = B.ideal((x + y)*(y + z), x*y*z) diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index 336b6a2b2d1..bf68832c703 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -683,9 +683,9 @@ def BooleanPolynomialRing_constructor(n=None, names=None, order="lex"): (Degree lexicographic term order of length 3, Degree lexicographic term order of length 2) - sage: R = BooleanPolynomialRing(3,'x',order='degrevlex') + sage: R = BooleanPolynomialRing(3,'x',order='degneglex') sage: R.term_order() - Degree reverse lexicographic term order + Degree negative lexicographic term order sage: BooleanPolynomialRing(names=('x','y')) Boolean PolynomialRing in x, y @@ -695,11 +695,11 @@ def BooleanPolynomialRing_constructor(n=None, names=None, order="lex"): TESTS:: - sage: P. = BooleanPolynomialRing(2,order='degrevlex') + sage: P. = BooleanPolynomialRing(2,order='deglex') sage: x > y True - sage: P. = BooleanPolynomialRing(4,order='degrevlex(2),degrevlex(2)') + sage: P. = BooleanPolynomialRing(4,order='deglex(2),deglex(2)') sage: x0 > x1 True sage: x2 > x3 From ef04b864adf02744a0ee2086e128fff33f6fa57c Mon Sep 17 00:00:00 2001 From: darij grinberg Date: Sun, 8 Sep 2013 17:31:11 -0700 Subject: [PATCH 135/206] Trac #15174: permutations multiplication order made more explicit and less of a minefield; also other fixes to permutation.py --- src/sage/combinat/permutation.py | 745 +++++++++++++++---- src/sage/combinat/posets/poset_examples.py | 36 +- src/sage/combinat/symmetric_group_algebra.py | 579 +++++++++++++- 3 files changed, 1150 insertions(+), 210 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 965df9ef131..b6f4064e293 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -1,8 +1,8 @@ r""" Permutations -The Permutations module. Use Permutation? to get information about -the Permutation class, and Permutations? to get information about +The Permutations module. Use ``Permutation?`` to get information about +the Permutation class, and ``Permutations?`` to get information about the combinatorial class of permutations. .. WARNING:: @@ -32,6 +32,8 @@ :widths: 30, 70 :delim: | + :meth:`~sage.combinat.permutation.Permutation.left_action_product` | Returns the product of ``self`` with another permutation, in which the other permutation is applied first. + :meth:`~sage.combinat.permutation.Permutation.right_action_product` | Returns the product of ``self`` with another permutation, in which ``self`` is applied first. :meth:`~sage.combinat.permutation.Permutation.size` | Returns the size of the permutation ``self``. :meth:`~sage.combinat.permutation.Permutation.cycle_string` | Returns the disjoint-cycles representation of ``self`` as string. :meth:`~sage.combinat.permutation.Permutation.next` | Returns the permutation that follows ``self`` in lexicographic order (in the same symmetric group as ``self``). @@ -47,6 +49,8 @@ :meth:`~sage.combinat.permutation.Permutation.inversions` | Returns a list of the inversions of permutation ``self``. :meth:`~sage.combinat.permutation.Permutation.show` | Displays the permutation as a drawing. :meth:`~sage.combinat.permutation.Permutation.number_of_inversions` | Returns the number of inversions in the permutation ``self``. + :meth:`~sage.combinat.permutation.Permutation.noninversions` | Returns the ``k``-noninversions in the permutation ``self``. + :meth:`~sage.combinat.permutation.Permutation.number_of_noninversions` | Returns the number of ``k``-noninversions in the permutation ``self``. :meth:`~sage.combinat.permutation.Permutation.length` | Returns the Coxeter length of a permutation ``self``. :meth:`~sage.combinat.permutation.Permutation.inverse` | Returns the inverse of a permutation ``self``. :meth:`~sage.combinat.permutation.Permutation.ishift` | Returns the ``i``-shift of ``self``. @@ -79,7 +83,7 @@ :meth:`~sage.combinat.permutation.Permutation.number_of_peaks` | Returns the number of peaks of the permutation ``self``. :meth:`~sage.combinat.permutation.Permutation.saliances` | Returns a list of the saliances of the permutation ``self``. :meth:`~sage.combinat.permutation.Permutation.number_of_saliances` | Returns the number of saliances of the permutation ``self``. - :meth:`~sage.combinat.permutation.Permutation.bruhat_lequal` | Returns ``True`` if self is less than ``p2`` in the Bruhat order. + :meth:`~sage.combinat.permutation.Permutation.bruhat_lequal` | Returns ``True`` if self is less or equal to ``p2`` in the Bruhat order. :meth:`~sage.combinat.permutation.Permutation.weak_excedences` | Returns all the numbers ``self[i]`` such that ``self[i] >= i+1``. :meth:`~sage.combinat.permutation.Permutation.bruhat_inversions` | Returns the list of inversions of ``self`` such that the application of this inversion to ``self`` decrements its number of inversions. :meth:`~sage.combinat.permutation.Permutation.bruhat_inversions_iterator` | Returns an iterator over Bruhat inversions of ``self``. @@ -89,7 +93,7 @@ :meth:`~sage.combinat.permutation.Permutation.bruhat_pred_iterator` | An iterator for the permutations covered by ``self`` in the Bruhat order. :meth:`~sage.combinat.permutation.Permutation.bruhat_smaller` | Returns the combinatorial class of permutations smaller than or equal to ``self`` in the Bruhat order. :meth:`~sage.combinat.permutation.Permutation.bruhat_greater` | Returns the combinatorial class of permutations greater than or equal to ``self`` in the Bruhat order. - :meth:`~sage.combinat.permutation.Permutation.permutohedron_lequal` | Returns ``True`` if ``self`` is less than ``p2`` in the permutohedron order. + :meth:`~sage.combinat.permutation.Permutation.permutohedron_lequal` | Returns ``True`` if ``self`` is less or equal to ``p2`` in the permutohedron order. :meth:`~sage.combinat.permutation.Permutation.permutohedron_succ` | Returns a list of the permutations covering ``self`` in the permutohedron order. :meth:`~sage.combinat.permutation.Permutation.permutohedron_pred` | Returns a list of the permutations covered by ``self`` in the permutohedron order. :meth:`~sage.combinat.permutation.Permutation.permutohedron_smaller` | Returns a list of permutations smaller than or equal to ``self`` in the permutohedron order. @@ -190,6 +194,10 @@ * (2013-07-13): Removed ``CombinatorialClass`` and moved permutations to the category framework. +- Darij Grinberg (2013-09-07): added methods; ameliorated :trac:`14885` by + exposing and documenting methods for global-independent + multiplication. + Classes and methods =================== """ @@ -231,7 +239,6 @@ from sage.graphs.digraph import DiGraph import itertools from combinat import CombinatorialObject, catalan_number -import copy from necklace import Necklaces from sage.misc.misc import uniq from sage.misc.cachefunc import cached_method @@ -246,7 +253,7 @@ the multiplication done from left to right (like in GAP) -- that is, `(\pi \psi)(i) = \psi(\pi(i))` for all `i`. - ..NOTE:: + .. NOTE:: These options have no effect on permutation group elements. """, @@ -345,13 +352,36 @@ class Permutation(CombinatorialObject, Element): .. WARNING:: - Since :trac:`13742` the input is checked for correctness : it is not - accepted unless actually is a permutation on `1...n`. It means that some - :meth:`Permutation` objects cannot be created anymore without setting - ``check_input = False``, as there is no certainty that its functions can - handle them, and this should be fixed in a much better way ASAP (the - functions should be rewritten to handle those cases, and new tests be - added). + Since :trac:`13742` the input is checked for correctness : it is not + accepted unless actually is a permutation on `\{1, \ldots, n\}`. It + means that some :meth:`Permutation` objects cannot be created anymore + without setting ``check_input = False``, as there is no certainty that + its functions can handle them, and this should be fixed in a much + better way ASAP (the functions should be rewritten to handle those + cases, and new tests be added). + + .. WARNING:: + + There are two possible conventions for multiplying permutations, and + the one currently enabled in Sage by default is the one which has + `(pq)(i) = q(p(i))` for any permutations `p \in S_n` and `q \in S_n` + and any `1 \leq i \leq n`. (This equation looks less strange when + the action of permutations on numbers is written from the right: + then it takes the form `i^{pq} = (i^p)^q`, which is an associativity + law). There is an alternative convention, which has + `(pq)(i) = p(q(i))` instead. The conventions can be switched at + runtime using + :meth:`sage.combinat.permutation.Permutations.global_options`. + It is best for code not to rely on this setting being set to a + particular standard, but rather use the methods + :meth:`left_action_product` and :meth:`right_action_product` for + multiplying permutations (these methods don't depend on the setting). + See :trac:`14885` for more details. + + .. NOTE:: + + The ``bruhat*`` methods refer to the *strong* Bruhat order. To use + the *weak* Bruhat order, look under ``permutohedron*``. EXAMPLES:: @@ -936,7 +966,7 @@ def to_cycles(self, singletons=True): ....: timeit('[p._to_cycles_list(False) for p in lp]') ....: timeit('[p._to_cycles_orig(False) for p in lp]') - and larger ones:: + and larger ones:: sage: for size in [10, 20, 50, 75, 100, 200, 500, 1000, # not tested ....: 2000, 5000, 10000, 15000, 20000, 30000, @@ -1071,7 +1101,7 @@ def _to_cycles_set(self, singletons=True): def _to_cycles_list(self, singletons=True): r""" - Return the permutation p as a list of disjoint cycles. + Return the permutation ``self`` as a list of disjoint cycles. EXAMPLES:: @@ -1140,7 +1170,7 @@ def signature(self): .. NOTE:: - Sign may be used as an alias to signature. + :meth:`sign` can be used as an alias for :meth:`signature`. EXAMPLES:: @@ -1148,6 +1178,8 @@ def signature(self): -1 sage: Permutation([1,3,2,5,4]).sign() 1 + sage: Permutation([]).sign() + 1 """ return (-1)**(len(self)-len(self.to_cycles())) @@ -1279,40 +1311,78 @@ def __rmul__(self, lp): else: return self._left_to_right_multiply_on_right(lp) - def _left_to_right_multiply_on_left(self,lp): - """ + def left_action_product(self, lp): + r""" + Return the permutation obtained by composing ``self`` with + ``lp`` in such an order that ``lp`` is applied first and + ``self`` is applied afterwards. + + This is usually denoted by either ``self * lp`` or ``lp * self`` + depending on the conventions used by the author. If the value + of a permutation `p \in S_n` on an integer + `i \in \{ 1, 2, \cdots, n \}` is denoted by `p(i)`, then this + should be denoted by ``self * lp`` in order to have + associativity (i.e., in order to have + `(p \cdot q)(i) = p(q(i))` for all `p`, `q` and `i`). If, on + the other hand, the value of a permutation `p \in S_n` on an + integer `i \in \{ 1, 2, \cdots, n \}` is denoted by `i^p`, then + this should be denoted by ``lp * self`` in order to have + associativity (i.e., in order to have + `i^{p \cdot q} = (i^p)^q` for all `p`, `q` and `i`). + EXAMPLES:: sage: p = Permutation([2,1,3]) sage: q = Permutation([3,1,2]) - sage: p._left_to_right_multiply_on_left(q) + sage: p.left_action_product(q) [3, 2, 1] - sage: q._left_to_right_multiply_on_left(p) + sage: q.left_action_product(p) [1, 3, 2] """ - #Pad the permutations if they are of - #different sizes + # Pad the permutations if they are of + # different sizes new_lp = lp[:] + [i+1 for i in range(len(lp), len(self))] new_p1 = self[:] + [i+1 for i in range(len(self), len(lp))] return Permutation([ new_p1[i-1] for i in new_lp ]) - def _left_to_right_multiply_on_right(self, rp): + _left_to_right_multiply_on_left = left_action_product + + def right_action_product(self, rp): """ + Return the permutation obtained by composing ``self`` with + ``lp`` in such an order that ``self`` is applied first and + ``lp`` is applied afterwards. + + This is usually denoted by either ``self * lp`` or ``lp * self`` + depending on the conventions used by the author. If the value + of a permutation `p \in S_n` on an integer + `i \in \{ 1, 2, \cdots, n \}` is denoted by `p(i)`, then this + should be denoted by ``lp * self`` in order to have + associativity (i.e., in order to have + `(p \cdot q)(i) = p(q(i))` for all `p`, `q` and `i`). If, on + the other hand, the value of a permutation `p \in S_n` on an + integer `i \in \{ 1, 2, \cdots, n \}` is denoted by `i^p`, then + this should be denoted by ``self * lp`` in order to have + associativity (i.e., in order to have + `i^{p \cdot q} = (i^p)^q` for all `p`, `q` and `i`). + EXAMPLES:: sage: p = Permutation([2,1,3]) sage: q = Permutation([3,1,2]) - sage: p._left_to_right_multiply_on_right(q) + sage: p.right_action_product(q) [1, 3, 2] - sage: q._left_to_right_multiply_on_right(p) + sage: q.right_action_product(p) [3, 2, 1] """ - #Pad the permutations if they are of - #different sizes + # Pad the permutations if they are of + # different sizes new_rp = rp[:] + [i+1 for i in range(len(rp), len(self))] new_p1 = self[:] + [i+1 for i in range(len(self), len(rp))] return Permutation([ new_rp[i-1] for i in new_p1 ]) + _left_to_right_multiply_on_right = right_action_product + def __call__(self, i): r""" Return the image of the integer `i` under ``self``. @@ -1386,8 +1456,10 @@ def to_inversion_vector(self): r""" Return the inversion vector of ``self``. - If `v` is the inversion vector, then `v_i` is the number of elements - larger than `i` that appear to the left of `i` in the permutation. + The inversion vector of a permutation `p \in S_n` is defined as + the vector `(v_1, v_2, \ldots, v_n)`, where `v_i` is the + number of elements larger than `i` that appear to the left + of `i` in the permutation `p`. The algorithm is of complexity `O(n\log(n))` where `n` is the size of the given permutation. @@ -1417,7 +1489,7 @@ def to_inversion_vector(self): p = self._list l = len(p) # lightning fast if the length is less than 3 - # (is it really usefull?) + # (is it really useful?) if l<4: if l==0: return [] @@ -1443,10 +1515,12 @@ def _to_inversion_vector_orig(self): r""" Return the inversion vector of ``self``. - (it's probably not the most efficient implementation) + The inversion vector of a permutation `p \in S_n` is defined as + the vector `(v_1 , v_2 , \ldots , v_n)`, where `v_i` is the + number of elements larger than `i` that appear to the left + of `i` in the permutation `p`. - If `v` is the inversion vector, then `v_i` is the number of elements - larger than `i` that appear to the left of `i` in the permutation. + (This implementation is probably not the most efficient one.) EXAMPLES:: @@ -1468,10 +1542,13 @@ def _to_inversion_vector_small(self): r""" Return the inversion vector of ``self``. - (best choice for ``5 < size < 420`` approximately) + The inversion vector of a permutation `p \in S_n` is defined as + the vector `(v_1, v_2, \ldots, v_n)`, where `v_i` is the + number of elements larger than `i` that appear to the left + of `i` in the permutation `p`. - If `v` is the inversion vector, then `v_i` is the number of elements - larger than `i` that appear to the left of `i` in the permutation. + (This implementation is the best choice for ``5 < size < 420`` + approximately.) EXAMPLES:: @@ -1492,10 +1569,13 @@ def _to_inversion_vector_divide_and_conquer(self): r""" Return the inversion vector of a permutation ``self``. - (best choice for ``size > 410`` approximately) + The inversion vector of a permutation `p \in S_n` is defined as + the vector `(v_1, v_2, \ldots, v_n)`, where `v_i` is the + number of elements larger than `i` that appear to the left + of `i` in the permutation `p`. - If `v` is the inversion vector, then `v_i` is the number of elements - larger than `i` that appear to the left of `i` in the permutation. + (This implementation is the best choice for ``size > 410`` + approximately.) EXAMPLE:: @@ -1553,7 +1633,8 @@ def inversions(self): r""" Return a list of the inversions of ``self``. - An inversion is a pair `(i, j)` such that `i < j` and `p(i) > p(j)`. + An inversion of a permutation `p` is a pair `(i, j)` such that + `i < j` and `p(i) > p(j)`. EXAMPLES:: @@ -1600,7 +1681,7 @@ def show(self, representation="cycles", orientation="landscape", **args): sage: Permutations(20).random_element().show(representation = "modern_art") Traceback (most recent call last): ... - ValueError: The value of 'representation' must be equal to 'cycles', 'chord-digraph' or 'braid' + ValueError: The value of 'representation' must be equal to 'cycles', 'chord-diagram' or 'braid' """ if representation == "cycles" or representation == "chord-diagram": d = DiGraph(loops = True) @@ -1635,7 +1716,7 @@ def show(self, representation="cycles", orientation="landscape", **args): else: raise ValueError("The value of 'representation' must be equal to "+ - "'cycles', 'chord-digraph' or 'braid'") + "'cycles', 'chord-diagram' or 'braid'") def number_of_inversions(self): @@ -1658,6 +1739,108 @@ def number_of_inversions(self): """ return sum(self.to_inversion_vector()) + def noninversions(self, k): + r""" + Return the list of all ``k``-noninversions in ``self``. + + If `k` is an integer and `p \in S_n` is a permutation, then + a `k`-noninversion in `p` is defined as a strictly increasing + sequence `(i_1, i_2, \ldots, i_k)` of elements of + `\{ 1, 2, \ldots, n \}` satisfying + `p(i_1) < p(i_2) < \cdots < p(i_k)`. (In other words, a + `k`-noninversion in `p` can be regarded as a `k`-element + subset of `\{ 1, 2, \ldots, n \}` on which `p` restricts + to an increasing map.) + + EXAMPLES:: + + sage: p = Permutation([3, 2, 4, 1, 5]) + sage: p.noninversions(1) + [[3], [2], [4], [1], [5]] + sage: p.noninversions(2) + [[3, 4], [3, 5], [2, 4], [2, 5], [4, 5], [1, 5]] + sage: p.noninversions(3) + [[3, 4, 5], [2, 4, 5]] + sage: p.noninversions(4) + [] + sage: p.noninversions(5) + [] + + TESTS:: + + sage: q = Permutation([]) + sage: q.noninversions(1) + [] + """ + if k > len(self): + return [] + return filter( lambda pos: all( pos[i] < pos[i+1] for i in range(k-1) ), + subword.Subwords(self, k) ) + + def number_of_noninversions(self, k): + r""" + Return the number of ``k``-noninversions in ``self``. + + If `k` is an integer and `p \in S_n` is a permutation, then + a `k`-noninversion in `p` is defined as a strictly increasing + sequence `(i_1, i_2, \ldots, i_k)` of elements of + `\{ 1, 2, \ldots, n \}` satisfying + `p(i_1) < p(i_2) < \cdots < p(i_k)`. (In other words, a + `k`-noninversion in `p` can be regarded as a `k`-element + subset of `\{ 1, 2, \ldots, n \}` on which `p` restricts + to an increasing map.) + + The number of `k`-noninversions in `p` has been denoted by + `\mathrm{noninv}_k(p)` in [RSW2011]_, where conjectures + and results regarding this number have been stated. + + REFERENCES: + + .. [RSW2011] Victor Reiner, Franco Saliola, Volkmar Welker. + *Spectra of Symmetrized Shuffling Operators*. + :arXiv:`1102.2460v2`. + + EXAMPLES:: + + sage: p = Permutation([3, 2, 4, 1, 5]) + sage: p.number_of_noninversions(1) + 5 + sage: p.number_of_noninversions(2) + 6 + sage: p.number_of_noninversions(3) + 2 + sage: p.number_of_noninversions(4) + 0 + sage: p.number_of_noninversions(5) + 0 + + The number of `2`-noninversions of a permutation `p \in S_n` + is `\binom{n}{2}` minus its number of inversions:: + + sage: b = binomial(5, 2) + sage: all( x.number_of_noninversions(2) == b - x.number_of_inversions() + ....: for x in Permutations(5) ) + True + + We also check some corner cases:: + + sage: all( x.number_of_noninversions(1) == 5 for x in Permutations(5) ) + True + sage: all( x.number_of_noninversions(0) == 1 for x in Permutations(5) ) + True + sage: Permutation([]).number_of_noninversions(1) + 0 + sage: Permutation([]).number_of_noninversions(0) + 1 + sage: Permutation([2, 1]).number_of_noninversions(3) + 0 + """ + if k > len(self): + return 0 + incr_iterator = itertools.ifilter( lambda pos: all( pos[i] < pos[i+1] + for i in range(k-1) ), + iter(subword.Subwords(self, k)) ) + return sum(1 for _ in incr_iterator) def length(self): r""" @@ -1694,14 +1877,15 @@ def _icondition(self, i): """ Return a string which shows the relative positions of `i-1,i,i+1` in ``self``, along with the actual positions of these three letters in - ``self``. Note that `i` corresponds to a 2 in the string. + ``self``. The string represents the letters `i-1,i,i+1` by `1,2,3`, + respectively. - .. note:: + .. NOTE:: - An imove can only be applied when the relative positions - are one of '213', '132', '231', or '312'. ``None`` is returned - in the other cases to signal that an imove cannot be - applied. + An imove (that is, an iswitch or an ishift) can only be applied + when the relative positions of `i-1,i,i+1` are one of '213', + '132', '231', or '312'. ``None`` is returned in the other cases + to signal that an imove cannot be applied. EXAMPLES:: @@ -1721,6 +1905,10 @@ def _icondition(self, i): Traceback (most recent call last): ... ValueError: i (= 3) must be between 2 and n-1 + + .. SEEALSO:: + + :meth:`ishift`, :meth:`iswitch` """ if i not in range(2, len(self)): raise ValueError("i (= %s) must be between 2 and n-1"%i) @@ -1731,9 +1919,9 @@ def _icondition(self, i): if pos_i < pos_im1 and pos_im1 < pos_ip1: state = '213' elif pos_im1 < pos_ip1 and pos_ip1 < pos_i: - state = '132' + state = '132' elif pos_i < pos_ip1 and pos_ip1 < pos_im1: - state = '231' + state = '231' elif pos_ip1 < pos_im1 and pos_im1 < pos_i: state = '312' else: @@ -1751,11 +1939,11 @@ def ishift(self, i): relative positions of `i-1` and `i+1` in place. All other entries of the permutations are also left in place. - EXAMPLES: Here, `2` is to the left of both `1` and `3`. A `2`-shift - can be applied which moves the `2` to the right and leaves `1` and - `3` in their same relative order. + EXAMPLES: - :: + Here, `2` is to the left of both `1` and `3`. A `2`-shift + can be applied which moves the `2` to the right and leaves `1` and + `3` in their same relative order:: sage: Permutation([2,1,3]).ishift(2) [1, 3, 2] @@ -1766,9 +1954,7 @@ def ishift(self, i): [1, 4, 3, 2] Since `2` is between `1` and `3` in ``[1,2,3]``, a `2`-shift cannot - be applied. - - :: + be applied to ``[1,2,3]`` :: sage: Permutation([1,2,3]).ishift(2) [1, 2, 3] @@ -1797,15 +1983,14 @@ def ishift(self, i): l[pos_im1] = i+1 l[pos_i] = i-1 else: + # This branch should never occur, no matter what the user does. raise ValueError("invalid state") return Permutation(l) - - def iswitch(self, i): """ - Returns the ``i``-switch of ``self``. If an ``i``-switch of ``self`` + Return the ``i``-switch of ``self``. If an ``i``-switch of ``self`` can't be performed, then ``self`` is returned. An `i`-switch can be applied when the subsequence of ``self`` formed @@ -1818,9 +2003,7 @@ def iswitch(self, i): Here, `2` is to the left of both `1` and `3`. A `2`-switch can be applied which moves the `2` to the right and switches the relative - order between `1` and `3`. - - :: + order between `1` and `3`:: sage: Permutation([2,1,3]).iswitch(2) [3, 1, 2] @@ -1831,9 +2014,7 @@ def iswitch(self, i): [3, 4, 1, 2] Since `2` is between `1` and `3` in ``[1,2,3]``, a `2`-switch - cannot be applied. - - :: + cannot be applied to ``[1,2,3]`` :: sage: Permutation([1,2,3]).iswitch(2) [1, 2, 3] @@ -1861,6 +2042,7 @@ def iswitch(self, i): l[pos_ip1] = i l[pos_i] = i+1 else: + # This branch should never occur, no matter what the user does. raise ValueError("invalid state") return Permutation(l) @@ -1893,13 +2075,13 @@ def runs(self): sage: Permutation([1]).runs() [[1]] - The example from above: + The example from above:: sage: Permutation([6,1,7,3,4,5,2]).runs() [[6], [1, 7], [3, 4, 5], [2]] The number of runs in a nonempty permutation equals its - number of descents plus 1: + number of descents plus 1:: sage: all( len(p.runs()) == p.number_of_descents() + 1 ....: for p in Permutations(6) ) @@ -1909,14 +2091,14 @@ def runs(self): runs = [] current_value = p[0] current_run = [p[0]] - for i in range(1, len(p)): - if p[i] < current_value: + for i in p[1:]: + if i < current_value: runs.append(current_run) - current_run = [p[i]] + current_run = [i] else: - current_run.append(p[i]) + current_run.append(i) - current_value = p[i] + current_value = i runs.append(current_run) return runs @@ -1931,6 +2113,8 @@ def longest_increasing_subsequence_length(self): 3 sage: all([i.longest_increasing_subsequence_length() == len(RSK(i)[0][0]) for i in Permutations(5)]) True + sage: Permutation([]).longest_increasing_subsequence_length() + 0 """ r=[] for x in self: @@ -2215,13 +2399,13 @@ def number_of_recoils(self): return len(self.recoils()) def recoils_composition(self): - """ - Return the composition corresponding to the recoils of ``self``. + r""" + Return the recoils composition of ``self``. - The recoils composition of a permutation `p` is a composition of - `\ell(p)` whose descent set is the set of the recoils of `p` (not - their positions). In other words, this is the descents - composition of `p^{-1}`. + The recoils composition of a permutation `p \in S_n` is the + composition of `n` whose descent set is the set of the recoils + of `p` (not their positions). In other words, this is the + descents composition of `p^{-1}`. EXAMPLES:: @@ -2241,13 +2425,17 @@ def descents(self, final_descent=False): r""" Return the list of the descents of ``self``. - A descent of a permutation `p` is an integer `i` such that - `p(i) > p(i+1)`. + A descent of a permutation ``p`` is an integer ``i`` such that + ``p[i] > p[i+1]``. Here, Python's indexing convention is used, + so ``p[i]`` means `p(i+1)`. + With the ``final_descent`` option, the last position of a non-empty permutation is also considered as a descent. EXAMPLES:: + sage: Permutation([3,1,2]).descents() + [0] sage: Permutation([1,4,3,2]).descents() [1, 2] sage: Permutation([1,4,3,2]).descents(final_descent=True) @@ -2266,14 +2454,20 @@ def descents(self, final_descent=False): def idescents(self, final_descent=False): """ - Returns a list of the idescents of self, that is the list of the - descents of self's inverse. + Return a list of the idescents of ``self``, that is the list of + the descents of ``self``'s inverse. - With the final_descent option, the last position of a non empty - permutation is also considered as a descent. + A descent of a permutation ``p`` is an integer ``i`` such that + ``p[i] > p[i+1]``. Here, Python's indexing convention is used, + so ``p[i]`` means `p(i+1)`. + + With the ``final_descent`` option, the last position of a + non-empty permutation is also considered as a descent. EXAMPLES:: + sage: Permutation([2,3,1]).idescents() + [0] sage: Permutation([1,4,3,2]).idescents() [1, 2] sage: Permutation([1,4,3,2]).idescents(final_descent=True) @@ -2283,8 +2477,14 @@ def idescents(self, final_descent=False): def idescents_signature(self, final_descent=False): """ - Each position in self is mapped to -1 if it is an idescent and 1 if - it is not an idescent. + Return the list obtained as follows: Each position in ``self`` + is mapped to `-1` if it is an idescent and `1` if it is not an + idescent. + + See :meth:`idescents` for a definition of idescents. + + With the ``final_descent`` option, the last position of a + non-empty permutation is also considered as a descent. EXAMPLES:: @@ -2301,6 +2501,9 @@ def number_of_descents(self, final_descent=False): r""" Return the number of descents of ``self``. + With the ``final_descent`` option, the last position of a + non-empty permutation is also considered as a descent. + EXAMPLES:: sage: Permutation([1,4,3,2]).number_of_descents() @@ -2314,6 +2517,11 @@ def number_of_idescents(self, final_descent=False): r""" Return the number of idescents of ``self``. + See :meth:`idescents` for a definition of idescents. + + With the ``final_descent`` option, the last position of a + non-empty permutation is also considered as a descent. + EXAMPLES:: sage: Permutation([1,4,3,2]).number_of_idescents() @@ -2325,8 +2533,19 @@ def number_of_idescents(self, final_descent=False): @combinatorial_map(name='descent composition') def descents_composition(self): - """ - Return the composition corresponding to the descents of ``self``. + r""" + Return the descent composition of ``self``. + + The descent composition of a permutation `p \in S_n` is defined + as the composition of `n` whose descent set equals the descent + set of `p`. Here, the descent set of `p` is defined as the set + of all `i \in \{ 1, 2, \ldots, n-1 \}` satisfying + `p(i) > p(i+1)` (note that this differs from the output of the + :meth:`descents` method, since the latter uses Python's + indexing which starts at `0` instead of `1`). The descent set + of a composition `c = (i_1, i_2, \ldots, i_k)` is defined as + the set `\{ i_1, i_1 + i_2, i_1 + i_2 + i_3, \ldots, + i_1 + i_2 + \cdots + i_{k-1} \}`. EXAMPLES:: @@ -2339,19 +2558,27 @@ def descents_composition(self): """ if len(self) == 0: return Composition([]) - d = [ -1 ] + self.descents() + [len(self)-1] + d = [-1] + self.descents() + [len(self)-1] return Composition([ d[i+1]-d[i] for i in range(len(d)-1)]) def descent_polynomial(self): r""" - Returns the descent polynomial of the permutation p. + Return the descent polynomial of the permutation ``self``. + + The descent polynomial of a permutation `p` is the product of + all the ``z[p[i]]`` where ``i`` ranges over the descents of + ``p``. - The descent polynomial of p is the product of all the z[p[i]] where - i ranges over the descents of p. + A descent of a permutation ``p`` is an integer ``i`` such that + ``p[i] > p[i+1]``. Here, Python's indexing convention is used, + so ``p[i]`` means `p(i+1)`. REFERENCES: - - Garsia and Stanton 1984 + .. [GarStan1984] A. M. Garsia, Dennis Stanton, + *Group actions on Stanley-Reisner rings and invariants of + permutation groups*, Adv. in Math. 51 (1984), 107-201. + http://www.sciencedirect.com/science/article/pii/0001870884900057 EXAMPLES:: @@ -2359,6 +2586,13 @@ def descent_polynomial(self): z1 sage: Permutation([4,3,2,1]).descent_polynomial() z1*z2^2*z3^3 + + .. TODO:: + + This docstring needs to be fixed. First, the definition + does not match the implementation (or the examples). + Second, this doesn't seem to be defined in [GarStan1984]_ + (the descent monomial in their (7.23) is different). """ p = self z = [] @@ -2386,6 +2620,9 @@ def major_index(self, final_descent=False): Since our permutation indices are 0-based, we need to add the number of descents. + With the ``final_descent`` option, the last position of a + non-empty permutation is also considered as a descent. + EXAMPLES:: sage: Permutation([2,1,3]).major_index() @@ -2408,6 +2645,9 @@ def imajor_index(self, final_descent=False): Since our permutation indices are 0-based, we need to add the number of descents. + With the ``final_descent`` option, the last position of a + non-empty permutation is also considered as a descent. + EXAMPLES:: sage: Permutation([2,1,3]).imajor_index() @@ -2430,6 +2670,10 @@ def to_major_code(self, final_descent=False): index of the permutation obtained by erasing all letters smaller than `i` from `p`. + With the ``final_descent`` option, the last position of a + non-empty permutation is also considered as a descent. + This has an effect on the computation of major indices. + REFERENCES: - Carlitz, L. *q-Bernoulli and Eulerian Numbers*, @@ -2540,6 +2784,9 @@ def number_of_saliances(self): r""" Return the number of saliances of ``self``. + A saliance of a permutation `p` is an integer `i` such that + `p(i) > p(j)` for all `j > i`. + EXAMPLES:: sage: Permutation([2,3,1,5,4]).number_of_saliances() @@ -2554,12 +2801,52 @@ def number_of_saliances(self): ################ def bruhat_lequal(self, p2): r""" - Return ``True`` if ``self`` is less than ``p2`` in the Bruhat order. + Return ``True`` if ``self`` is less or equal to ``p2`` in + the Bruhat order. + + The Bruhat order (also called strong Bruhat order or Chevalley + order) on the symmetric group `S_n` is the partial order on `S_n` + determined by the following condition: If `p` is a permutation, + and `i` and `j` are two indices satisfying `p(i) > p(j)` and + `i < j` (that is, `(i, j)` is an inversion of `p` with `i < j`), + then `p \circ (i, j)` (the permutation obtained by first + switching `i` with `j` and then applying `p`) is smaller than `p` + in the Bruhat order. + + One can show that a permutation `p \in S_n` is less or equal to + a permutation `q \in S_n` in the Bruhat order if and only if + for every `i \in \{ 0, 1, \cdots , n \}` and + `j \in \{ 1, 2, \cdots , n \}`, the number of the elements among + `p(1), p(2), \cdots, p(j)` that are greater than `i` is `\leq` + to the number of the elements among `q(1), q(2), \cdots, q(j)` + that are greater than `i`. + + This method assumes that ``self`` and ``p2`` are permutations + of the same integer `n`. EXAMPLES:: sage: Permutation([2,4,3,1]).bruhat_lequal(Permutation([3,4,2,1])) True + + sage: Permutation([2,1,3]).bruhat_lequal(Permutation([2,3,1])) + True + sage: Permutation([2,1,3]).bruhat_lequal(Permutation([3,1,2])) + True + sage: Permutation([2,1,3]).bruhat_lequal(Permutation([1,2,3])) + False + sage: Permutation([1,3,2]).bruhat_lequal(Permutation([2,1,3])) + False + sage: Permutation([1,3,2]).bruhat_lequal(Permutation([2,3,1])) + True + sage: Permutation([2,3,1]).bruhat_lequal(Permutation([1,3,2])) + False + sage: sorted( [len([b for b in Permutations(3) if a.bruhat_lequal(b)]) + ....: for a in Permutations(3)] ) + [1, 2, 2, 4, 4, 6] + + sage: Permutation([]).bruhat_lequal(Permutation([])) + True """ p1 = self n1 = len(p1) @@ -2570,12 +2857,15 @@ def bruhat_lequal(self, p2): if p1[0] > p2[0] or p1[n1-1] < p2[n1-1]: return False - for i in range(n1): + for i in range(1, n1): c = 0 - for j in range(n1): - if p2[j] > i+1: + for j in range(n1 - 2): + # We should really check this for all j in range(n1), but for + # j == n1 - 1 it is tautological and for j == n1 - 2 the check + # is contained in the check p1[n1-1] < p2[n1-1] already made. + if p2[j] > i: c += 1 - if p1[j] > i+1: + if p1[j] > i: c -= 1 if c < 0: return False @@ -2651,6 +2941,8 @@ def bruhat_succ(self): the Bruhat order (on the symmetric group containing ``self``) such that there is no permutation between one of those and ``self``. + See :meth:`bruhat_lequal` for the definition of the Bruhat order. + EXAMPLES:: sage: Permutation([6,1,4,5,2,3]).bruhat_succ() @@ -2668,6 +2960,8 @@ def bruhat_succ_iterator(self): ``self``) such that there is no permutation between one of those and ``self``. + See :meth:`bruhat_lequal` for the definition of the Bruhat order. + EXAMPLES:: sage: [x for x in Permutation([6,1,4,5,2,3]).bruhat_succ_iterator()] @@ -2693,6 +2987,8 @@ def bruhat_pred(self): in the Bruhat order (on the symmetric group containing ``self``) such that there is no permutation between one of those and ``self``. + See :meth:`bruhat_lequal` for the definition of the Bruhat order. + EXAMPLES:: sage: Permutation([6,1,4,5,2,3]).bruhat_pred() @@ -2712,6 +3008,8 @@ def bruhat_pred_iterator(self): the Bruhat order (on the symmetric group containing ``self``) such that there is no permutation between one of those and ``self``. + See :meth:`bruhat_lequal` for the definition of the Bruhat order. + EXAMPLES:: sage: [x for x in Permutation([6,1,4,5,2,3]).bruhat_pred_iterator()] @@ -2737,6 +3035,8 @@ def bruhat_smaller(self): equal to ``self`` in the Bruhat order (on the symmetric group containing ``self``). + See :meth:`bruhat_lequal` for the definition of the Bruhat order. + EXAMPLES:: sage: Permutation([4,1,2,3]).bruhat_smaller().list() @@ -2758,6 +3058,8 @@ def bruhat_greater(self): equal to ``self`` in the Bruhat order (on the symmetric group containing ``self``). + See :meth:`bruhat_lequal` for the definition of the Bruhat order. + EXAMPLES:: sage: Permutation([4,1,2,3]).bruhat_greater().list() @@ -2777,11 +3079,54 @@ def bruhat_greater(self): def permutohedron_lequal(self, p2, side="right"): r""" - Returns True if self is less than p2 in the permutohedron order. + Return ``True`` if ``self`` is less or equal to ``p2`` in the + permutohedron order. By default, the computations are done in the right permutohedron. - If you pass the option side='left', then they will be done in the - left permutohedron. + If you pass the option ``side='left'``, then they will be done in + the left permutohedron. + + For every nonnegative integer `n`, the right (resp. left) + permutohedron order (also called the right (resp. left) weak + order, or the right (resp. left) weak Bruhat order) is a partial + order on the symmetric group `S_n`. It can be defined in various + ways, including the following ones: + + - Two permutations `u` and `v` in `S_n` satisfy `u \leq v` in + the right (resp. left) permutohedron order if and only if + the (Coxeter) length of the permutation `v^{-1} \circ u` + (resp. of the permutation `u \circ v^{-1}`) equals the + length of `v` minus the length of `u`. Here, `p \circ q` means + the permutation obtained by applying `q` first and then `p`. + (Recall that the Coxeter length of a permutation is its number + of inversions.) + + - Two permutations `u` and `v` in `S_n` satisfy `u \leq v` in + the right (resp. left) permutohedron order if and only if + every pair `(i, j)` of elements of `\{ 1, 2, \cdots, n \}` + such that `i < j` and `u^{-1}(i) > u^{-1}(j)` (resp. + `u(i) > u(j)`) also satisfies `v^{-1}(i) > v^{-1}(j)` + (resp. `v(i) > v(j)`). + + - A permutation `v \in S_n` covers a permutation `u \in S_n` in + the right (resp. left) permutohedron order if and only if we + have `v = u \circ (i, i + 1)` (resp. `v = (i, i + 1) \circ u`) + for some `i \in \{ 1, 2, \cdots, n - 1 \}` satisfying + `u(i) < u(i + 1)` (resp. `u^{-1}(i) < u^{-1}(i + 1)`). Here, + again, `p \circ q` means the permutation obtained by applying + `q` first and then `p`. + + The right and the left permutohedron order are mutually + isomorphic, with the isomorphism being the map sending every + permutation to its inverse. Each of these orders endows the + symmetric group `S_n` with the structure of a graded poset + (the rank function being the Coxeter length). + + .. WARNING:: + + The permutohedron order is not to be mistaken for the + strong Bruhat order (:meth:`bruhat_lequal`), despite both + orders being occasionally referred to as the Bruhat order. EXAMPLES:: @@ -2790,6 +3135,32 @@ def permutohedron_lequal(self, p2, side="right"): False sage: p.permutohedron_lequal(Permutation([4,2,1,3]), side='left') True + sage: p.permutohedron_lequal(p) + True + + sage: Permutation([2,1,3]).permutohedron_lequal(Permutation([2,3,1])) + True + sage: Permutation([2,1,3]).permutohedron_lequal(Permutation([3,1,2])) + False + sage: Permutation([2,1,3]).permutohedron_lequal(Permutation([1,2,3])) + False + sage: Permutation([1,3,2]).permutohedron_lequal(Permutation([2,1,3])) + False + sage: Permutation([1,3,2]).permutohedron_lequal(Permutation([2,3,1])) + False + sage: Permutation([2,3,1]).permutohedron_lequal(Permutation([1,3,2])) + False + sage: Permutation([2,1,3]).permutohedron_lequal(Permutation([2,3,1]), side='left') + False + sage: sorted( [len([b for b in Permutations(3) if a.permutohedron_lequal(b)]) + ....: for a in Permutations(3)] ) + [1, 2, 2, 3, 3, 6] + sage: sorted( [len([b for b in Permutations(3) if a.permutohedron_lequal(b, side="left")]) + ....: for a in Permutations(3)] ) + [1, 2, 2, 3, 3, 6] + + sage: Permutation([]).permutohedron_lequal(Permutation([])) + True """ p1 = self l1 = p1.number_of_inversions() @@ -2807,13 +3178,16 @@ def permutohedron_lequal(self, p2, side="right"): def permutohedron_succ(self, side="right"): r""" - Returns a list of the permutations strictly greater than p in the - permutohedron order such that there is no permutation between one - of those and p. + Return a list of the permutations strictly greater than ``self`` + in the permutohedron order such that there is no permutation + between any of those and ``self``. By default, the computations are done in the right permutohedron. - If you pass the option side='left', then they will be done in the - left permutohedron. + If you pass the option ``side='left'``, then they will be done in + the left permutohedron. + + See :meth:`permutohedron_lequal` for the definition of the + permutohedron orders. EXAMPLES:: @@ -2834,25 +3208,27 @@ def permutohedron_succ(self, side="right"): pp[i+1] = p[i] succ.append(Permutation(pp)) else: - advance = lambda perm: [i for i in range(1,n) if perm.index(i) < perm.index(i+1)] + advance = lambda perm: [i for i in range(1,n) if perm.index(i) < perm.index(i+1)] for i in advance(p): pp = p[:] pp[p.index(i)] = i+1 pp[p.index(i+1)] = i succ.append(Permutation(pp)) - return succ def permutohedron_pred(self, side="right"): r""" - Returns a list of the permutations strictly smaller than p in the - permutohedron order such that there is no permutation between one - of those and p. + Return a list of the permutations strictly smaller than ``self`` + in the permutohedron order such that there is no permutation + between any of those and ``self``. By default, the computations are done in the right permutohedron. - If you pass the option side='left', then they will be done in the - left permutohedron. + If you pass the option ``side='left'``, then they will be done in + the left permutohedron. + + See :meth:`permutohedron_lequal` for the definition of the + permutohedron orders. EXAMPLES:: @@ -2883,12 +3259,15 @@ def permutohedron_pred(self, side="right"): def permutohedron_smaller(self, side="right"): r""" - Returns a list of permutations smaller than or equal to p in the - permutohedron order. + Return a list of permutations smaller than or equal to ``self`` + in the permutohedron order. By default, the computations are done in the right permutohedron. - If you pass the option side='left', then they will be done in the - left permutohedron. + If you pass the option ``side='left'``, then they will be done in + the left permutohedron. + + See :meth:`permutohedron_lequal` for the definition of the + permutohedron orders. EXAMPLES:: @@ -2914,18 +3293,20 @@ def permutohedron_smaller(self, side="right"): [4, 1, 2, 3], [4, 2, 1, 3]] """ - return transitive_ideal(lambda x: x.permutohedron_pred(side), self) def permutohedron_greater(self, side="right"): r""" - Returns a list of permutations greater than or equal to p in the - permutohedron order. + Return a list of permutations greater than or equal to ``self`` + in the permutohedron order. By default, the computations are done in the right permutohedron. - If you pass the option side='left', then they will be done in the - left permutohedron. + If you pass the option ``side='left'``, then they will be done in + the left permutohedron. + + See :meth:`permutohedron_lequal` for the definition of the + permutohedron orders. EXAMPLES:: @@ -2934,14 +3315,16 @@ def permutohedron_greater(self, side="right"): sage: Permutation([4,2,1,3]).permutohedron_greater(side='left') [[4, 2, 1, 3], [4, 3, 1, 2], [4, 3, 2, 1]] """ - return transitive_ideal(lambda x: x.permutohedron_succ(side), self) - def right_permutohedron_interval_iterator(self, other) : + def right_permutohedron_interval_iterator(self, other): r""" - Returns an iterator on the permutations (represented as integer - lists) belonging to the right permutohedron interval where `self` - is the minimal element and `other` the maximal element. + Return an iterator on the permutations (represented as integer + lists) belonging to the right permutohedron interval where + ``self`` is the minimal element and ``other`` the maximal element. + + See :meth:`permutohedron_lequal` for the definition of the + permutohedron orders. EXAMPLES:: @@ -2960,11 +3343,14 @@ def right_permutohedron_interval_iterator(self, other) : for j in xrange(i, len(other)) if other[i] < other[j]]) return LinearExtensions(d) - def right_permutohedron_interval(self, other) : + def right_permutohedron_interval(self, other): r""" - Returns the list of the permutations belonging to the right - permutohedron interval where `self` is the minimal element and - `other` the maximal element. + Return the list of the permutations belonging to the right + permutohedron interval where ``self`` is the minimal element and + ``other`` the maximal element. + + See :meth:`permutohedron_lequal` for the definition of the + permutohedron orders. EXAMPLES:: @@ -2998,7 +3384,8 @@ def right_permutohedron_interval(self, other) : def has_pattern(self, patt): r""" - Tests whether the permutation matches the pattern. + Test whether the permutation ``self`` contains the pattern + ``patt``. EXAMPLES:: @@ -3017,7 +3404,8 @@ def has_pattern(self, patt): def avoids(self, patt): """ - Tests whether the permutation avoids the pattern. + Test whether the permutation ``self`` avoids the pattern + ``patt``. EXAMPLES:: @@ -3032,7 +3420,8 @@ def avoids(self, patt): def pattern_positions(self, patt): r""" - Returns the list of positions where the pattern patt appears in p. + Return the list of positions where the pattern ``patt`` appears + in the permutation ``self``. EXAMPLES:: @@ -3117,9 +3506,11 @@ def reverse(self): @combinatorial_map(order=2,name='complement') def complement(self): - """ - Returns the complement of the permutation which is obtained by - replacing each value x in the list with n - x + 1. + r""" + Return the complement of the permutation ``self``. + + The complement of a permutation `w \in S_n` is defined as the + permutation in `S_n` sending each `i` to `n + 1 - w(i)`. EXAMPLES:: @@ -3184,7 +3575,11 @@ def dict(self): def action(self, a): """ - Returns the action of the permutation on a list. + Return the action of the permutation ``self`` on a list ``a``. + + The action of a permutation `p \in S_n` on an `n`-element list + `(a_1, a_2, \ldots, a_n)` is defined to be + `(a_{p(1)}, a_{p(2)}, \ldots, a_{p(n)})`. EXAMPLES:: @@ -3197,6 +3592,11 @@ def action(self, a): Traceback (most recent call last): ... ValueError: len(a) must equal len(self) + + sage: q = Permutation([2,3,1]) + sage: a = range(3) + sage: q.action(a) + [1, 2, 0] """ if len(a) != len(self): raise ValueError("len(a) must equal len(self)") @@ -3568,14 +3968,17 @@ def shifted_shuffle(self, other): # Base class for permutations class Permutations(Parent, UniqueRepresentation): - """ + r""" Permutations. ``Permutations(n)`` returns the class of permutations of ``n``, if ``n`` is an integer, list, set, or string. - ``Permutations(n, k)`` returns the class of permutations of ``n`` (where n - is any of the above things) of length ``k``; ``k`` must be an integer. + ``Permutations(n, k)`` returns the class of length-``k`` partial + permutations of ``n`` (where ``n`` is any of the above things); ``k`` + must be a nonnegative integer. A length-`k` partial permutation of `n` + is defined as a `k`-tuple of pairwise distinct elements of + `\{ 1, 2, \ldots, n \}`. Valid keyword arguments are: 'descents', 'bruhat_smaller', 'bruhat_greater', 'recoils_finer', 'recoils_fatter', 'recoils', @@ -3596,15 +3999,19 @@ class Permutations(Parent, UniqueRepresentation): permutations with descents composition ``list``. ``Permutations(bruhat_smaller=p)`` and ``Permutations(bruhat_greater=p)`` - return the class of permutations smaller or greater, respectively, than - the given permutation ``p`` in Bruhat order. + return the class of permutations smaller-or-equal or greater-or-equal, + respectively, than the given permutation ``p`` in the Bruhat order. + (The Bruhat order is defined in + :meth:`~sage.combinat.permutation.Permutation.bruhat_lequal`. + It is also referred to as the *strong* Bruhat order.) ``Permutations(recoils=p)`` returns the class of permutations whose - recoils composition is ``p``. + recoils composition is ``p``. Unlike the ``descents=(list, n)`` syntax, + this actually takes a *composition* as input. ``Permutations(recoils_fatter=p)`` and ``Permutations(recoils_finer=p)`` return the class of permutations whose recoils composition is fatter or - finer, respectively, than the given permutation ``p``. + finer, respectively, than the given composition ``p``. ``Permutations(n, avoiding=P)`` returns the class of permutations of ``n`` avoiding ``P``. Here ``P`` may be a single permutation or a list of @@ -3834,7 +4241,7 @@ def __classcall_private__(cls, n=None, k=None, **kwargs): class Permutations_nk(Permutations): r""" - Permutations of length `k` of `\{1, 2, \ldots, n\}`. + Length-`k` partial permutations of `\{1, 2, \ldots, n\}`. """ def __init__(self, n, k): """ @@ -3849,11 +4256,12 @@ def __init__(self, n, k): class Element(ClonableArray): """ - A permutation of length `k` of `[n]`. + A length-`k` partial permutation of `[n]`. """ def check(self): """ - Verify that ``self`` is a valid permutation of length `k` of `[n]`. + Verify that ``self`` is a valid length-`k` partial + permutation of `[n]`. EXAMPLES:: @@ -3941,11 +4349,12 @@ def random_element(self): class Permutations_mset(Permutations): - """ + r""" Permutations of a multiset `M`. - A permutation is represented by a list that contains exactly the - same elements as `M`, but possibly in different order. If `M` is + A permutation of a multiset `M` is represented by a list that + contains exactly the same elements as `M` (with the same + multiplicities), but possibly in different order. If `M` is a proper set there are `|M| !` such permutations. Otherwise, if the first element appears `k_1` times, the second element appears `k_2` times and so on, the number @@ -4279,7 +4688,12 @@ def random_element(self): class Permutations_msetk(Permutations_mset): """ - Permutations of length `k` of a multiset. + Length-`k` partial permutations of a multiset. + + A length-`k` partial permutation of a multiset `M` is + represented by a list of length `k` whose entries are + elements of `M`, appearing in the list with a multiplicity + not higher than their respective multiplicity in `M`. """ @staticmethod def __classcall__(cls, mset, k): @@ -4353,11 +4767,11 @@ def __iter__(self): class Permutations_setk(Permutations_set): """ - Permutations of length `k` of an arbitrary given finite set. + Length-`k` partial permutations of an arbitrary given finite set. - Here, a "permutation of length `k` of a finite set `S`" means - a list of length `k` whose entries are pairwise distinct and - all belong to `S`. + Here, a "length-`k` partial permutation of a finite set `S`" means + a list of length `k` whose entries are pairwise distinct and all + belong to `S`. """ @staticmethod def __classcall_private__(cls, s, k): @@ -4612,6 +5026,8 @@ def __iter__(self): class StandardPermutations_n(Permutations): """ Permutations of the set `\{1, 2, \ldots, n\}`. + + These are also called permutations of size `n`. """ def __init__(self, n): """ @@ -4626,7 +5042,7 @@ def __init__(self, n): def __call__(self, x): """ A close variant of ``__call__`` which just attempts to extend the - permutation to the correct length before constructing the element. + permutation to the correct size before constructing the element. sage: P = Permutations(5) sage: P([2,3,1]) @@ -4703,7 +5119,7 @@ def element_in_conjugacy_classes(self, nu): def cardinality(self): """ - Return the number of permutations of length `n` which is `n!`. + Return the number of permutations of size `n`, which is `n!`. EXAMPLES:: @@ -4718,7 +5134,7 @@ def cardinality(self): def identity(self): r""" - Return the identity permutation of length `n`. + Return the identity permutation of size `n`. EXAMPLES:: @@ -4983,7 +5399,7 @@ def bistochastic_as_sum_of_permutations(M, check = True): the bistochastic matrix ``M``. A stochastic matrix is a matrix with nonnegative real entries such that the - sum of the elements of any row is equal to 1. A bistochastic matrix is a + sum of the elements of any row is equal to `1`. A bistochastic matrix is a stochastic matrix whose transpose matrix is also stochastic ( there are conditions both on the rows and on the columns ). @@ -5012,11 +5428,12 @@ def bistochastic_as_sum_of_permutations(M, check = True): .. NOTE:: - - In this function, we just assume 1 to be any constant : for us a matrix M - is bistochastic if there exists `c>0` such that `M/c` is bistochastic. + - In this function, we just assume 1 to be any constant : for us a + matrix `M` is bistochastic if there exists `c>0` such that `M/c` + is bistochastic. - You can obtain a sequence of pairs ``(permutation,coeff)``, where - ``permutation` is a Sage ``Permutation`` instance, and ``coeff`` + ``permutation`` is a Sage ``Permutation`` instance, and ``coeff`` its corresponding coefficient from the result of this function by applying the ``list`` function. diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 426a6771efc..a1850d8b620 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -486,9 +486,16 @@ def SymmetricGroupBruhatIntervalPoset(start, end): return Poset(nodes) @staticmethod - def SymmetricGroupWeakOrderPoset(n,labels="permutations"): - """ - The poset of permutations with respect to weak order. + def SymmetricGroupWeakOrderPoset(n, labels="permutations", side="right"): + r""" + The poset of permutations of `\{ 1, 2, \ldots, n \}` with respect + to the weak order (also known as the permutohedron order, cf. + :meth:`~sage.combinat.permutation.Permutation.permutohedron_lequal`). + + The optional variable ``labels`` (default: ``"permutations"``) + determines the labelling of the elements if `n < 10`. The optional + variable ``side`` (default: ``"right"``) determines whether the + right or the left permutohedron order is to be used. EXAMPLES:: @@ -499,13 +506,22 @@ def SymmetricGroupWeakOrderPoset(n,labels="permutations"): element_labels = dict([[s,"".join(map(str,s))] for s in Permutations(n)]) if n < 10 and labels == "reduced_words": element_labels = dict([[s,"".join(map(str,s.reduced_word_lexmin()))] for s in Permutations(n)]) - def weak_covers(s): - r""" - Nested function for computing the covers of elements in the - poset of weak order for the symmetric group. - """ - return [v for v in s.bruhat_succ() if - s.length() + (s.inverse()*v).length() == v.length()] + if side == "left": + def weak_covers(s): + r""" + Nested function for computing the covers of elements in the + poset of left weak order for the symmetric group. + """ + return [v for v in s.bruhat_succ() if + s.length() + (s.inverse().right_action_product(v)).length() == v.length()] + else: + def weak_covers(s): + r""" + Nested function for computing the covers of elements in the + poset of right weak order for the symmetric group. + """ + return [v for v in s.bruhat_succ() if + s.length() + (s.inverse().left_action_product(v)).length() == v.length()] return Poset(dict([[s,weak_covers(s)] for s in Permutations(n)]),element_labels) posets = Posets diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index a3937f9cbdb..4ac5f560698 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -85,6 +85,21 @@ def SymmetricGroupAlgebra(R, n): 10)`` currently triggers the construction of all symmetric group algebras of smaller order. Is this a feature we really want to have? + .. WARNING:: + + The semantics of multiplication in symmetric group algebras is + determined by the order in which permutations are multiplied, + which currently defaults to "in such a way that multiplication + is associative with permutations acting on integers from the + right", but can be changed to the opposite order at runtime + by setting a global variable (see + :meth:`sage.combinat.permutation.Permutations.global_options` ). + In view of this, it is recommended that code not rely on the + usual multiplication function, but rather use the methods + :meth:`left_action_product` and :meth:`right_action_product` + for multiplying permutations (these methods don't depend on the + setting). See :trac:`14885` for more information. + TESTS:: sage: TestSuite(QS3).run() @@ -151,6 +166,110 @@ def product_on_basis(self, left, right): """ return self.monomial(left * right) + def left_action_product(self, left, right): + """ + Return the product of two elements ``left`` and ``right`` of + ``self``, where multiplication is defined in such a way that + for two permutations `p` and `q`, the product `pq` is the + permutation obtained by first applying `q` and then applying + `p`. This definition of multiplication is tailored to make + multiplication of products associative with their action on + numbers if permutations are to act on numbers from the left. + + EXAMPLES:: + + sage: QS3 = SymmetricGroupAlgebra(QQ, 3) + sage: p1 = Permutation([2, 1, 3]) + sage: p2 = Permutation([3, 1, 2]) + sage: QS3.left_action_product(QS3(p1), QS3(p2)) + [3, 2, 1] + sage: x = QS3([1, 2, 3]) - 2*QS3([1, 3, 2]) + sage: y = 1/2 * QS3([3, 1, 2]) + 3*QS3([1, 2, 3]) + sage: QS3.left_action_product(x, y) + 3*[1, 2, 3] - 6*[1, 3, 2] - [2, 1, 3] + 1/2*[3, 1, 2] + sage: QS3.left_action_product(0, x) + 0 + + The method coerces its input into the algebra ``self``:: + + sage: QS4 = SymmetricGroupAlgebra(QQ, 4) + sage: QS4.left_action_product(QS3([1, 2, 3]), QS3([2, 1, 3])) + [2, 1, 3, 4] + sage: QS4.left_action_product(1, Permutation([4, 1, 2, 3])) + [4, 1, 2, 3] + + .. WARNING:: + + Note that coercion presently works from permutations of ``n`` + into the ``n``-th symmetric group algebra, and also from all + smaller symmetric group algebras into the ``n``-th symmetric + group algebra, but not from permutations of integers smaller + than ``n`` into the ``n``-th symmetric group algebra. + """ + a = self(left) + b = self(right) + from sage.combinat.permutation import Permutation + return self.sum_of_terms([(Permutation([p[i-1] for i in q]), x * y) + for (p, x) in a for (q, y) in b]) + # Why did we use Permutation([p[i-1] for i in q]) + # instead of p.left_action_product(q) ? + # Because having cast a and b into self, we already know that + # p and q are permutations of the same number of elements, + # and thus we don't need to waste our time on the input + # sanitizing of left_action_product. + + def right_action_product(self, left, right): + """ + Return the product of two elements ``left`` and ``right`` of + ``self``, where multiplication is defined in such a way that + for two permutations `p` and `q`, the product `pq` is the + permutation obtained by first applying `p` and then applying + `q`. This definition of multiplication is tailored to make + multiplication of products associative with their action on + numbers if permutations are to act on numbers from the right. + + EXAMPLES:: + + sage: QS3 = SymmetricGroupAlgebra(QQ, 3) + sage: p1 = Permutation([2, 1, 3]) + sage: p2 = Permutation([3, 1, 2]) + sage: QS3.right_action_product(QS3(p1), QS3(p2)) + [1, 3, 2] + sage: x = QS3([1, 2, 3]) - 2*QS3([1, 3, 2]) + sage: y = 1/2 * QS3([3, 1, 2]) + 3*QS3([1, 2, 3]) + sage: QS3.right_action_product(x, y) + 3*[1, 2, 3] - 6*[1, 3, 2] + 1/2*[3, 1, 2] - [3, 2, 1] + sage: QS3.right_action_product(0, x) + 0 + + The method coerces its input into the algebra ``self``:: + + sage: QS4 = SymmetricGroupAlgebra(QQ, 4) + sage: QS4.right_action_product(QS3([1, 2, 3]), QS3([2, 1, 3])) + [2, 1, 3, 4] + sage: QS4.right_action_product(1, Permutation([4, 1, 2, 3])) + [4, 1, 2, 3] + + .. WARNING:: + + Note that coercion presently works from permutations of ``n`` + into the ``n``-th symmetric group algebra, and also from all + smaller symmetric group algebras into the ``n``-th symmetric + group algebra, but not from permutations of integers smaller + than ``n`` into the ``n``-th symmetric group algebra. + """ + a = self(left) + b = self(right) + from sage.combinat.permutation import Permutation + return self.sum_of_terms([(Permutation([q[i-1] for i in p]), x * y) + for (p, x) in a for (q, y) in b]) + # Why did we use Permutation([q[i-1] for i in p]) + # instead of p.right_action_product(q) ? + # Because having cast a and b into self, we already know that + # p and q are permutations of the same number of elements, + # and thus we don't need to waste our time on the input + # sanitizing of right_action_product. + def canonical_embedding(self, other): """ Return the canonical embedding of ``self`` into ``other``. @@ -205,6 +324,34 @@ def monomial_from_smaller_permutation(self, permutation): """ return self.monomial( self.basis().keys()(permutation) ) + def antipode(self, x): + r""" + Return the image of the element ``x`` of ``self`` under the + antipode of the Hopf algebra ``self`` (where the + comultiplication is the usual one on a group algebra). + + Explicitly, this is obtained by replacing each permutation + `\sigma` by `\sigma^{-1}` in ``x`` while keeping all + coefficients as they are. + + EXAMPLES:: + + sage: QS4 = SymmetricGroupAlgebra(QQ, 4) + sage: QS4.antipode(2 * QS4([1, 3, 4, 2]) - 1/2 * QS4([1, 4, 2, 3])) + -1/2*[1, 3, 4, 2] + 2*[1, 4, 2, 3] + sage: all( QS4.antipode(QS4(p)) == QS4(p.inverse()) + ....: for p in Permutations(4) ) + True + + sage: ZS3 = SymmetricGroupAlgebra(ZZ, 3) + sage: ZS3.antipode(ZS3.zero()) + 0 + sage: ZS3.antipode(-ZS3(Permutation([2, 3, 1]))) + -[3, 1, 2] + """ + return self.sum_of_terms([(p.inverse(), coeff) for + (p, coeff) in self(x)], + distinct=True) # def _coerce_start(self, x): # """ @@ -274,8 +421,6 @@ def cpi(self, p): character_table = eval(gap.eval("Display(Irr(SymmetricGroup(%d)));"%self.n)) - cpi = self.zero() - np = partition.Partitions_n(self.n).list() np.reverse() p_index = np.index(p) @@ -341,12 +486,273 @@ def _conjugacy_classes_representatives_underlying_group(self): """ return [permutation.Permutations(self.n).element_in_conjugacy_classes(nu) for nu in partition.Partitions(self.n)] - def jucys_murphy(self, k): + def rsw_shuffling_element(self, k): + r""" + Return the `k`-th Reiner-Saliola-Welker shuffling element in + the group algebra ``self``. + + The `k`-th Reiner-Saliola-Welker shuffling element in the + symmetric group algebra `R S_n` over a ring `R` is defined as the + sum `\sum_{\sigma \in S_n} \mathrm{noninv}_k(\sigma) \cdot \sigma`, + where for every permutation `\sigma`, the number + `\mathrm{noninv}_k(\sigma)` is the number of all + `k`-noninversions of `\sigma` (that is, the number of all + `k`-element subsets of `\{ 1, 2, \ldots, n \}` on which + `\sigma` restricts to a strictly increasing map). See + :meth:`sage.combinat.permutation.number_of_noninversions` for + the `\mathrm{noninv}` map. + + This element is more or less the operator `\nu_{k, 1^{n-k}}` + introduced in [RSW2011]_; more precisely, `\nu_{k, 1^{n-k}}` + is the left multiplication by this element. + + It is a nontrivial theorem (Theorem 1.1 in [RSW2011]_) that + the operators `\nu_{k, 1^{n-k}}` (for fixed `n` and varying + `k`) pairwise commute. It is a conjecture (Conjecture 1.2 in + [RSW2011]_) that all their eigenvalues are integers (which, in + light of their commutativity and easily established symmetry, + yields that they can be simultaneously diagonalized over `\QQ` + with only integer eigenvalues). + + EXAMPLES: + + The Reiner-Saliola-Welker shuffling elements on `\QQ S_3`:: + + sage: QS3 = SymmetricGroupAlgebra(QQ, 3) + sage: QS3.rsw_shuffling_element(0) + [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] + [3, 2, 1] + sage: QS3.rsw_shuffling_element(1) + 3*[1, 2, 3] + 3*[1, 3, 2] + 3*[2, 1, 3] + 3*[2, 3, 1] + 3*[3, 1, 2] + 3*[3, 2, 1] + sage: QS3.rsw_shuffling_element(2) + 3*[1, 2, 3] + 2*[1, 3, 2] + 2*[2, 1, 3] + [2, 3, 1] + [3, 1, 2] + sage: QS3.rsw_shuffling_element(3) + [1, 2, 3] + sage: QS3.rsw_shuffling_element(4) + 0 + + Checking the commutativity of Reiner-Saliola-Welker shuffling + elements (we leave out the ones for which it is trivial):: + + sage: def test_rsw_comm(n): + ....: QSn = SymmetricGroupAlgebra(QQ, n) + ....: rsws = [QSn.rsw_shuffling_element(k) for k in range(2, n)] + ....: return all( all( rsws[i] * rsws[j] == rsws[j] * rsws[i] + ....: for j in range(i) ) + ....: for i in range(len(rsws)) ) + sage: test_rsw_comm(3) + True + sage: test_rsw_comm(4) + True + sage: test_rsw_comm(5) # long time + True + + .. NOTE:: + + For large ``k`` (relative to ``n``), it might be faster to call + ``QSn.left_action_product(QSn.semi_rsw_element(k), QSn.antipode(binary_unshuffle_sum(k)))`` + than ``QSn.rsw_shuffling_element(n)``. + + .. SEEALSO:: + + :meth:`semi_rsw_element`, :meth:`binary_unshuffle_sum` + """ + return self.sum_of_terms([(p, p.number_of_noninversions(k)) + for p in permutation.Permutations(self.n)], + distinct=True) + + def semi_rsw_element(self, k): + r""" + Return the `k`-th semi-RSW element in the group algebra ``self``. + + The `k`-th semi-RSW element in the symmetric group algebra + `R S_n` over a ring `R` is defined as the sum of all permutations + `\sigma \in S_n` satisfying + `\sigma(1) < \sigma(2) < \cdots < \sigma(k)`. + + This element has the property that, if it is denoted by `s_k`, + then `s_k S(s_k)` is `(n-k)!` times the `k`-th + Reiner-Saliola-Welker shuffling element of `R S_n` (see + :meth:`rsw_shuffling_element`). Here, `S` denotes the antipode + of the group algebra `R S_n`. + + The `k`-th semi-RSW element is the image of the complete + non-commutative symmetric function `S^{(k, 1^{n-k})}` in the + ring of non-commutative symmetric functions under the canonical + projection on the symmetric group algebra (through the descent + algebra). + + EXAMPLES: + + The semi-RSW elements on `\QQ S_3`:: + + sage: QS3 = SymmetricGroupAlgebra(QQ, 3) + sage: QS3.semi_rsw_element(0) + [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] + [3, 2, 1] + sage: QS3.semi_rsw_element(1) + [1, 2, 3] + [1, 3, 2] + [2, 1, 3] + [2, 3, 1] + [3, 1, 2] + [3, 2, 1] + sage: QS3.semi_rsw_element(2) + [1, 2, 3] + [1, 3, 2] + [2, 3, 1] + sage: QS3.semi_rsw_element(3) + [1, 2, 3] + sage: QS3.semi_rsw_element(4) + 0 + + Let us check the relation with the `k`-th Reiner-Saliola-Welker + shuffling element stated in the docstring:: + + sage: def test_rsw(n): + ....: ZSn = SymmetricGroupAlgebra(ZZ, n) + ....: for k in range(1, n): + ....: a = ZSn.semi_rsw_element(k) + ....: b = ZSn.left_action_product(a, ZSn.antipode(a)) + ....: if factorial(n-k) * ZSn.rsw_shuffling_element(k) != b: + ....: return False + ....: return True + sage: test_rsw(3) + True + sage: test_rsw(4) + True + sage: test_rsw(5) # long time + True + + Let us also check the statement about the complete + non-commutative symmetric function:: + + sage: def test_rsw_ncsf(n): + ....: ZSn = SymmetricGroupAlgebra(ZZ, n) + ....: NSym = NonCommutativeSymmetricFunctions(ZZ) + ....: S = NSym.S() + ....: for k in range(1, n): + ....: a = S(Composition([k] + [1]*(n-k))).to_symmetric_group_algebra() + ....: if a != ZSn.semi_rsw_element(k): + ....: return False + ....: return True + sage: test_rsw_ncsf(3) + True + sage: test_rsw_ncsf(4) + True + sage: test_rsw_ncsf(5) # long time + True + """ + n = self.n + if n < k: + return self.zero() + def complement(xs): + res = range(1, n+1) + for x in xs: + res.remove(x) + return res + return self.sum_of_monomials([permutation.Permutation(complement(q) + list(q)) + for q in permutation.Permutations_nk(n, n-k)]) + + def binary_unshuffle_sum(self, k): + r""" + Return the `k`-th binary unshuffle sum in the group algebra + ``self``. + + The `k`-th binary unshuffle sum in the symmetric group algebra + `R S_n` over a ring `R` is defined as the sum of all permutations + `\sigma \in S_n` satisfying + `\sigma(1) < \sigma(2) < \cdots < \sigma(k)` and + `\sigma(k+1) < \sigma(k+2) < \cdots < \sigma(n)`. + + This element has the property that, if it is denoted by `t_k`, + and if the `k`-th semi-RSW element (see :meth:`semi_rsw_element`) + is denoted by `s_k`, then `s_k S(t_k)` and `t_k S(s_k)` both + equal the `k`-th Reiner-Saliola-Welker shuffling element of + `R S_n` (see :meth:`rsw_shuffling_element`). + + The `k`-th binary unshuffle sum is the image of the complete + non-commutative symmetric function `S^{(k, n-k)}` in the + ring of non-commutative symmetric functions under the canonical + projection on the symmetric group algebra (through the descent + algebra). + + EXAMPLES: + + The binary unshuffle sums on `\QQ S_3`:: + + sage: QS3 = SymmetricGroupAlgebra(QQ, 3) + sage: QS3.binary_unshuffle_sum(0) + [1, 2, 3] + sage: QS3.binary_unshuffle_sum(1) + [1, 2, 3] + [2, 1, 3] + [3, 1, 2] + sage: QS3.binary_unshuffle_sum(2) + [1, 2, 3] + [1, 3, 2] + [2, 3, 1] + sage: QS3.binary_unshuffle_sum(3) + [1, 2, 3] + sage: QS3.binary_unshuffle_sum(4) + 0 + + Let us check the relation with the `k`-th Reiner-Saliola-Welker + shuffling element stated in the docstring:: + + sage: def test_rsw(n): + ....: ZSn = SymmetricGroupAlgebra(ZZ, n) + ....: for k in range(1, n): + ....: a = ZSn.semi_rsw_element(k) + ....: b = ZSn.binary_unshuffle_sum(k) + ....: c = ZSn.left_action_product(a, ZSn.antipode(b)) + ....: d = ZSn.left_action_product(b, ZSn.antipode(a)) + ....: e = ZSn.rsw_shuffling_element(k) + ....: if c != e or d != e: + ....: return False + ....: return True + sage: test_rsw(3) + True + sage: test_rsw(4) # long time + True + sage: test_rsw(5) # long time + True + + Let us also check the statement about the complete + non-commutative symmetric function:: + + sage: def test_rsw_ncsf(n): + ....: ZSn = SymmetricGroupAlgebra(ZZ, n) + ....: NSym = NonCommutativeSymmetricFunctions(ZZ) + ....: S = NSym.S() + ....: for k in range(1, n): + ....: a = S(Composition([k, n-k])).to_symmetric_group_algebra() + ....: if a != ZSn.binary_unshuffle_sum(k): + ....: return False + ....: return True + sage: test_rsw_ncsf(3) + True + sage: test_rsw_ncsf(4) + True + sage: test_rsw_ncsf(5) # long time + True """ + n = self.n + if n < k: + return self.zero() + def complement(xs): + res = range(1, n+1) + for x in xs: + res.remove(x) + return res + from sage.combinat.subset import Subsets + return self.sum_of_monomials([permutation.Permutation(sorted(q) + complement(q)) + for q in Subsets(n, k)]) + + def jucys_murphy(self, k): + r""" Return the Jucys-Murphy element `J_k` (also known as a Young-Jucys-Murphy element) for the symmetric group algebra ``self``. + The Jucys-Murphy element `J_k` in the symmetric group algebra + `R S_n` is defined for every `k \in \{ 1, 2, \ldots, n \}` by + + .. MATH:: + + J_k = (1, k) + (2, k) + \cdots + (k-1, k) \in R S_n, + + where the addends are transpositions in `S_n` (regarded as + elements of `R S_n`). We note that there is not a dependence on `n`, + so it is often surpressed in the notation. + EXAMPLES:: sage: QS3 = SymmetricGroupAlgebra(QQ, 3) @@ -376,11 +782,11 @@ def jucys_murphy(self, k): ... ValueError: k (= 4) must be between 1 and n (= 3) (inclusive) """ - res = self.zero() - if k < 1 or k > self.n: raise ValueError("k (= {k}) must be between 1 and n (= {n}) (inclusive)".format(k=k, n=self.n)) + res = self.zero() + for i in range(1, k): p = range(1, self.n+1) p[i-1] = k @@ -462,6 +868,12 @@ def epsilon_ik(self, itab, ktab, star=0): Return the seminormal basis element of ``self`` corresponding to the pair of tableaux ``itab`` and ``ktab``. + .. WARNING:: + + This currently depends on the state of the ``mult`` global + option of the :class:`~sage.combinat.permutation.Permutations` + class. + EXAMPLES:: sage: QS3 = SymmetricGroupAlgebra(QQ, 3) @@ -574,7 +986,15 @@ def epsilon(tab, star=0): def pi_ik(itab, ktab): - """ + r""" + Return the permutation `p` which sends every entry of the + tableau ``itab`` to the respective entry of the tableau + ``ktab``, as an element of the corresponding symmetric group + algebra. + + This assumes that ``itab`` and ``ktab`` are tableaux (possibly + given just as lists of lists) of the same shape. + EXAMPLES:: sage: from sage.combinat.symmetric_group_algebra import pi_ik @@ -597,7 +1017,8 @@ def pi_ik(itab, ktab): def kappa(alpha): r""" Return `\kappa_\alpha`, which is `n!` divided by the number - of standard tableaux of shape `\alpha`. + of standard tableaux of shape `\alpha` (where `alpha` is a + partition of `n`). EXAMPLES:: @@ -613,7 +1034,6 @@ def kappa(alpha): n = sum(alpha) return factorial(n) / StandardTableaux(alpha).cardinality() - def a(tableau, star=0): r""" The row projection operator corresponding to the Young tableau @@ -696,7 +1116,7 @@ def b(tableau, star=0): sage: b([[1, 2, 4], [5, 3]]) [1, 2, 3, 4, 5] - [1, 3, 2, 4, 5] - [5, 2, 3, 4, 1] + [5, 3, 2, 4, 1] - With the `l2r` setting for multiplication, the unnormalized + With the ``l2r`` setting for multiplication, the unnormalized Young symmetrizer ``e(tableau)`` should be the product ``b(tableau) * a(tableau)`` for every ``tableau``. Let us check this on the standard tableaux of size 5:: @@ -741,6 +1161,26 @@ def e(tableau, star=0): every integer from `1` to its size precisely once, but may and may not be standard). + If `n` is a nonnegative integer, and `T` is a Young tableau + containing every integer from `1` to `n` exactly once, then + the unnormalized Young projector `e(T)` is defined by + + .. MATH:: + + e(T) = a(T) b(T) \in \QQ S_n, + + where `a(T) \in \QQ S_n` is the sum of all permutations in `S_n` + which fix the rows of `T` (as sets), and `b(T) \in \QQ S_n` is the + signed sum of all permutations in `S_n` which fix the columns of + `T` (as sets). Here, "signed" means that each permutation is + multiplied with its sign; and the product on the group `S_n` is + defined in such a way that `(pq)(i) = p(q(i))` for any + permutations `p` and `q` and any `1 \leq i \leq n`. + + Note that the definition of `e(T)` is not uniform across + literature. Others define it as `b(T) a(T)` instead, or include + certain scalar factors (we do not, whence "unnormalized"). + EXAMPLES:: sage: from sage.combinat.symmetric_group_algebra import e @@ -761,9 +1201,6 @@ def e(tableau, star=0): if star: t = t.restrict(t.size()-star) - mult = permutation_options['mult'] - permutation_options['mult'] = 'l2r' - if t in e_cache: res = e_cache[t] else: @@ -781,7 +1218,7 @@ def e(tableau, star=0): cd = dict((P(v), v.sign()*one) for v in cs) antisym = QSn._from_dict(cd) - res = antisym*sym + res = QSn.right_action_product(antisym, sym) # Ugly hack for the case of an empty tableau, due to the # annoyance of Permutation(Tableau([]).row_stabilizer()[0]) @@ -793,8 +1230,6 @@ def e(tableau, star=0): e_cache[t] = res - permutation_options['mult'] = mult - return res ehat_cache = {} @@ -848,25 +1283,20 @@ def e_ik(itab, ktab, star=0): if it.shape() != kt.shape(): raise ValueError("the two tableaux must be of the same shape") - mult = permutation_options['mult'] - permutation_options['mult'] = 'l2r' - if kt == it: - res = e(it) - elif (it, kt) in e_ik_cache: - res = e_ik_cache[(it,kt)] - else: - pi = pi_ik(it,kt) - e_ik_cache[(it,kt)] = e(it)*pi - res = e_ik_cache[(it,kt)] - - permutation_options['mult'] = mult + return e(it) + if (it, kt) in e_ik_cache: + return e_ik_cache[(it,kt)] + + pi = pi_ik(it,kt) + QSn = pi.parent() + res = QSn.right_action_product(e(it), pi) + e_ik_cache[(it,kt)] = res return res - def seminormal_test(n): """ - Runs a variety of tests to verify that the construction of the + Run a variety of tests to verify that the construction of the seminormal basis works as desired. The numbers appearing are Theorems in James and Kerber's 'Representation Theory of the Symmetric Group'. @@ -913,8 +1343,65 @@ def seminormal_test(n): def HeckeAlgebraSymmetricGroupT(R, n, q=None): - """ - Return the Hecke algebra of the symmetric group on the T basis. + r""" + Return the Hecke algebra of the symmetric group `S_n` on the T-basis + with quantum parameter ``q`` over the ring `R`. + + If `R` is a commutative ring and `q` is an invertible element of `R`, + and if `n` is a nonnegative integer, then the Hecke algebra of the + symmetric group `S_n` over `R` with quantum parameter `q` is defined + as the algebra generated by the generators `T_1, T_2, \ldots, T_{n-1}` + with relations + + .. MATH:: + + T_i T_{i+1} T_i = T_{i+1} T_i T_{i+1} + + for all `i < n-1` ("braid relations"), + + .. MATH:: + + T_i T_j = T_j T_i + + for all `i` and `j` such that `| i-j | > 1` ("locality relations"), + and + + .. MATH:: + + T_i^2 = q + (q-1) T_i + + for all `i` (the "quadratic relations", also known in the form + `(T_i + 1) (T_i - q) = 0`). (This is only one of several existing + definitions in literature, not all of which are fully equivalent. + We are following the conventions of [GS93]_.) For any permutation + `w \in S_n`, we can define an element `T_w` of this Hecke algebra by + setting `T_w = T_{i_1} T_{i_2} \cdots T_{i_k}`, where + `w = s_{i_1} s_{i_2} \cdots s_{i_k}` is a reduced word for `w` + (with `s_i` meaning the transposition `(i, i+1)`, and the product of + permutations being evaluated by first applying `s_{i_k}`, then + `s_{i_{k-1}}`, etc.). This element is independent of the choice of + the reduced decomposition, and can be computed in Sage by calling + ``H[w]`` where ``H`` is the Hecke algebra and ``w`` is the + permutation. + + The Hecke algebra of the symmetric group `S_n` with quantum parameter + `q` over `R` can be seen as a deformation of the group algebra + `R S_n`; indeed, it becomes `R S_n` when `q = 1`. + + .. WARNING:: + + The multiplication on the Hecke algebra of the symmetric group + does *not* follow the global option ``mult`` of the + :class:`Permutations` class (see + :meth:`~sage.combinat.permutation.Permutations.global_options`). + It is always as defined above. It does not match the default + option (``mult=l2r``) of the symmetric group algebra! + + REFERENCES: + + .. [GS93] David M. Goldschmidt. + *Group characters, symmetric functions, and the Hecke algebras*. + AMS 1993. EXAMPLES:: @@ -925,6 +1412,18 @@ def HeckeAlgebraSymmetricGroupT(R, n, q=None): sage: HeckeAlgebraSymmetricGroupT(QQ, 3, 2) Hecke algebra of the symmetric group of order 3 with q=2 on the T basis over Rational Field + + The multiplication on the Hecke algebra follows a different convention + than the one on the symmetric group algebra does by default:: + + sage: H3 = HeckeAlgebraSymmetricGroupT(QQ, 3) + sage: H3([1,3,2]) * H3([2,1,3]) + T[3, 1, 2] + sage: S3 = SymmetricGroupAlgebra(QQ, 3) + sage: S3([1,3,2]) * S3([2,1,3]) + [2, 3, 1] + + sage: TestSuite(H3).run() """ return HeckeAlgebraSymmetricGroup_t(R, n, q) @@ -1007,7 +1506,10 @@ def __init__(self, R, n, q=None): self.print_options(prefix="T") def t_action_on_basis(self, perm, i): - """ + r""" + Return the product `T_i \cdot T_{perm}`, where ``perm`` is a + permutation in the symmetric group `S_n`. + EXAMPLES:: sage: H3 = HeckeAlgebraSymmetricGroupT(QQ, 3) @@ -1025,7 +1527,12 @@ def t_action_on_basis(self, perm, i): raise ValueError("i (= %(i)d) must be between 1 and n (= %(n)d)" % {'i': i, 'n': self.n}) t_i = permutation.Permutation( (i, i+1) ) - perm_i = t_i * perm + perm_i = t_i.right_action_product(perm) + # This used to be perm_i = t_i * perm. I have changed it to + # perm_i = t_i.right_action_product(perm) because it would + # otherwise cause TestSuite(H3) to fail when + # Permutations.global_options(mult) would be set to "r2l". + # -- Darij, 19 Nov 2013 if perm[i-1] < perm[i]: return self.monomial(self._basis_keys(perm_i)) @@ -1037,8 +1544,8 @@ def t_action_on_basis(self, perm, i): def t_action(self, a, i): - """ - Return the action of T_i on a. + r""" + Return the product `T_i \cdot a`. EXAMPLES:: @@ -1107,7 +1614,7 @@ def algebra_generators(self): def jucys_murphy(self, k): """ Return the Jucys-Murphy element `J_k` of the Hecke algebra. The - Jucys-Murphy elements generate the maximal commutative sub-algebra + Jucys-Murphy elements generate a maximal commutative sub-algebra of the Hecke algebra. EXAMPLES:: From b554827a94872c3e62cd7311c22e28d38e635ffc Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 21 Feb 2013 17:48:28 +0000 Subject: [PATCH 136/206] Trac #13872 - Implemented RC<->KR tableaux bijections for non-exceptional types --- .../en/reference/combinat/rc_bijections.rst | 17 + .../combinat/rigged_configurations.rst | 7 +- .../combinat/crystals/kirillov_reshetikhin.py | 67 +- src/sage/combinat/crystals/letters.pyx | 85 +- .../combinat/rigged_configurations/all.py | 2 - .../bij_abstract_class.py | 237 ++- .../rigged_configurations/bij_type_A.py | 48 +- .../rigged_configurations/bij_type_A2_dual.py | 327 ++++ .../rigged_configurations/bij_type_A2_even.py | 213 +++ .../rigged_configurations/bij_type_A2_odd.py | 195 ++ .../rigged_configurations/bij_type_B.py | 827 +++++++++ .../rigged_configurations/bij_type_C.py | 262 +++ .../rigged_configurations/bij_type_D.py | 305 +++- .../bij_type_D_twisted.py | 538 ++++++ .../rigged_configurations/bijection.py | 107 +- .../rigged_configurations/kr_tableaux.py | 1062 +++++++++-- .../rigged_configuration_element.py | 633 ++++--- .../rigged_configurations.py | 1589 +++++++++++++---- .../rigged_configurations/rigged_partition.py | 309 +++- .../tensor_product_kr_tableaux.py | 524 ++---- .../tensor_product_kr_tableaux_element.py | 342 ++-- 21 files changed, 6115 insertions(+), 1581 deletions(-) create mode 100644 src/doc/en/reference/combinat/rc_bijections.rst create mode 100644 src/sage/combinat/rigged_configurations/bij_type_A2_dual.py create mode 100644 src/sage/combinat/rigged_configurations/bij_type_A2_even.py create mode 100644 src/sage/combinat/rigged_configurations/bij_type_A2_odd.py create mode 100644 src/sage/combinat/rigged_configurations/bij_type_B.py create mode 100644 src/sage/combinat/rigged_configurations/bij_type_C.py create mode 100644 src/sage/combinat/rigged_configurations/bij_type_D_twisted.py diff --git a/src/doc/en/reference/combinat/rc_bijections.rst b/src/doc/en/reference/combinat/rc_bijections.rst new file mode 100644 index 00000000000..f20901e3382 --- /dev/null +++ b/src/doc/en/reference/combinat/rc_bijections.rst @@ -0,0 +1,17 @@ +Rigged Configuration Bijections +=============================== + +.. toctree:: + :maxdepth: 1 + + ../sage/combinat/rigged_configurations/bijection + ../sage/combinat/rigged_configurations/bij_abstract_class + ../sage/combinat/rigged_configurations/bij_type_A + ../sage/combinat/rigged_configurations/bij_type_B + ../sage/combinat/rigged_configurations/bij_type_C + ../sage/combinat/rigged_configurations/bij_type_D + ../sage/combinat/rigged_configurations/bij_type_A2_odd + ../sage/combinat/rigged_configurations/bij_type_A2_even + ../sage/combinat/rigged_configurations/bij_type_A2_dual + ../sage/combinat/rigged_configurations/bij_type_D_twisted + diff --git a/src/doc/en/reference/combinat/rigged_configurations.rst b/src/doc/en/reference/combinat/rigged_configurations.rst index 115f8c7be07..730755de6af 100644 --- a/src/doc/en/reference/combinat/rigged_configurations.rst +++ b/src/doc/en/reference/combinat/rigged_configurations.rst @@ -2,7 +2,7 @@ Rigged Configurations ===================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 ../sage/combinat/rigged_configurations/rigged_configurations ../sage/combinat/rigged_configurations/rigged_configuration_element @@ -15,8 +15,5 @@ Rigged Configurations ../sage/combinat/rigged_configurations/rigged_partition - ../sage/combinat/rigged_configurations/bijection - ../sage/combinat/rigged_configurations/bij_abstract_class - ../sage/combinat/rigged_configurations/bij_type_A - ../sage/combinat/rigged_configurations/bij_type_D + rc_bijections diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index 546f2da472e..0703ba03a52 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -397,9 +397,9 @@ def _repr_(self): """ return "Kirillov-Reshetikhin crystal of type %s with (r,s)=(%d,%d)" % (self.cartan_type(), self.r(), self.s()) - def _element_constructor_(self, *value, **options): + def _element_constructor_(self, *args, **options): """ - Construct an element of ``self`` from ``elt``. + Construct an element of ``self`` from the input. EXAMPLES:: @@ -408,10 +408,10 @@ def _element_constructor_(self, *value, **options): [[1], [2]] """ from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableauxElement - if isinstance(value[0], KirillovReshetikhinTableauxElement): - elt = value[0] + if isinstance(args[0], KirillovReshetikhinTableauxElement): + elt = args[0] # Check to make sure it can be converted - if elt.cartan_type().affine() != self.cartan_type() \ + if elt.cartan_type() != self.cartan_type() \ or elt.parent().r() != self._r or elt.parent().s() != self._s: raise ValueError("The Kirillov-Reshetikhin tableau must have the same Cartan type and shape") @@ -424,7 +424,7 @@ def _element_constructor_(self, *value, **options): hw_elt = self(rows=rows) f_str = reversed(to_hw[1]) return hw_elt.f_string(f_str) - return AffineCrystalFromClassical._element_constructor_(self, *value, **options) + return AffineCrystalFromClassical._element_constructor_(self, *args, **options) @abstract_method def classical_decomposition(self): @@ -614,6 +614,7 @@ def R_matrix(self, K): g = { gen1 : gen2 } return T1.crystal_morphism(g, acyclic = False) + @cached_method def Kirillov_Reshetikhin_tableaux(self): """ Return the corresponding set of :class:`KirillovReshetikhinTableaux`. @@ -631,6 +632,7 @@ class KirillovReshetikhinGenericCrystalElement(AffineCrystalFromClassicalElement """ Abstract class for all Kirillov-Reshetikhin crystal elements. """ + @cached_method def to_Kirillov_Reshetikhin_tableau(self): r""" Construct the corresponding @@ -1638,7 +1640,7 @@ def from_ambient_crystal(self): return self.crystal_morphism( pdict_inv, index_set = [j+1 for j in self.cartan_type().classical().index_set()], automorphism = lambda i : i-1 ) -class KR_type_A2Element(AffineCrystalFromClassicalElement): +class KR_type_A2Element(KirillovReshetikhinGenericCrystalElement): r""" Class for the elements in the Kirillov-Reshetikhin crystals `B^{r,s}` of type `A_{2n}^{(2)}` for `r0) True """ - def _element_constructor_(self, *value, **options): + def _element_constructor_(self, *args, **options): """ - Construct an element of ``self`` from ``elt``. + Construct an element of ``self`` from the input. EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 4, 3) sage: KRC = KirillovReshetikhinCrystal(['D',4,1], 4, 3) - sage: elt = KRT([-3,-4,2,1,-3,-4,2,1,-2,-4,3,1]); elt + sage: elt = KRT(-3,-4,2,1,-3,-4,2,1,-2,-4,3,1); elt [[1, 1, 1], [2, 2, 3], [-4, -4, -4], [-3, -3, -2]] sage: KRC(elt) # indirect doctest [++--, [[1], [3], [-4], [-3]]] @@ -2708,17 +2743,17 @@ def _element_constructor_(self, *value, **options): True """ from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableauxElement - if isinstance(value[0], KirillovReshetikhinTableauxElement): - elt = value[0] + if isinstance(args[0], KirillovReshetikhinTableauxElement): + elt = args[0] # Check to make sure it can be converted - if elt.cartan_type().affine() != self.cartan_type() \ + if elt.cartan_type() != self.cartan_type() \ or elt.parent().r() != self._r or elt.parent().s() != self._s: raise ValueError("The Kirillov-Reshetikhin tableau must have the same Cartan type and shape") to_hw = elt.to_classical_highest_weight() f_str = reversed(to_hw[1]) return self.module_generator().f_string(f_str) - KirillovReshetikhinCrystalFromPromotion._element_constructor_(self, *value, **options) + return KirillovReshetikhinCrystalFromPromotion._element_constructor_(self, *args, **options) def classical_decomposition(self): r""" diff --git a/src/sage/combinat/crystals/letters.pyx b/src/sage/combinat/crystals/letters.pyx index f8397b4ee8c..8a6da022fc2 100644 --- a/src/sage/combinat/crystals/letters.pyx +++ b/src/sage/combinat/crystals/letters.pyx @@ -442,7 +442,24 @@ cdef class Letter(Element): True sage: C(4) == C(4) True + + TESTS:: + + sage: C = CrystalOfLetters(['C', 3]) + sage: C('E') == C(2) + False + sage: C(2) == C('E') + False + sage: C('E') == C('E') + True """ + # Special case for the empty letter + if isinstance(left, EmptyLetter): + return isinstance(right, EmptyLetter) \ + and (op == Py_EQ or op == Py_LE or op == Py_GE) + if isinstance(right, EmptyLetter): + return op == Py_NE + cdef Letter self, x self = left x = right @@ -462,11 +479,42 @@ cdef class Letter(Element): cdef class EmptyLetter(Element): """ - The (affine) letter `\emptyset` thought of as a classical crystal letter + The affine)letter `\emptyset` thought of as a classical crystal letter in classical type `B_n` and `C_n`. + .. WARNING:: + + This is not a classical letter. + Used in the rigged configuration bijections. """ + cdef readonly str value + + def __init__(self, parent): + """ + Initialize ``xelf``. + + EXAMPLES:: + + sage: C = CrystalOfLetters(['C', 3]) + sage: TestSuite(C('E')).run() + """ + self.value = 'E' + Element.__init__(self, parent) + + def __reduce__(self): + r""" + Used in pickling crystal of letters elements. + + EXAMPLES:: + + sage: C = CrystalOfLetters(['C',3]) + sage: a = C('E') + sage: a.__reduce__() + (The crystal of letters for type ['C', 3], ('E',)) + """ + return (self._parent, ('E',)) + def _repr_(self): """ Return a string representation of ``self``. @@ -491,6 +539,18 @@ cdef class EmptyLetter(Element): """ return "\\emptyset" + def __hash__(self): + """ + Return the hash value of ``self``. + + EXAMPLES:: + + sage: C = CrystalOfLetters(['D', 4]) + sage: hash(C('E')) == hash('E') + True + """ + return hash(self.value) + def weight(self): """ Return the weight of ``self``. @@ -514,7 +574,16 @@ cdef class EmptyLetter(Element): """ return None - f = e + cpdef f(self, int i): + """ + Return `f_i` of ``self`` which is ``None``. + + EXAMPLES:: + + sage: C = CrystalOfLetters(['C', 3]) + sage: C('E').f(1) + """ + return None cpdef int epsilon(self, int i): r""" @@ -528,7 +597,17 @@ cdef class EmptyLetter(Element): """ return 0 - phi = epsilon + cpdef int phi(self, int i): + r""" + Return `\varphi_i` of ``self``. + + EXAMPLES:: + + sage: C = CrystalOfLetters(['C', 3]) + sage: C('E').phi(1) + 0 + """ + return 0 ######################### # Type A diff --git a/src/sage/combinat/rigged_configurations/all.py b/src/sage/combinat/rigged_configurations/all.py index bd34bcef34e..dbb3b62c4ba 100644 --- a/src/sage/combinat/rigged_configurations/all.py +++ b/src/sage/combinat/rigged_configurations/all.py @@ -1,6 +1,4 @@ -from rigged_configurations import HighestWeightRiggedConfigurations from rigged_configurations import RiggedConfigurations -from tensor_product_kr_tableaux import HighestWeightTensorProductOfKirillovReshetikhinTableaux from tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux from kr_tableaux import KirillovReshetikhinTableaux diff --git a/src/sage/combinat/rigged_configurations/bij_abstract_class.py b/src/sage/combinat/rigged_configurations/bij_abstract_class.py index 1bf5fbefc7e..b09e187b157 100644 --- a/src/sage/combinat/rigged_configurations/bij_abstract_class.py +++ b/src/sage/combinat/rigged_configurations/bij_abstract_class.py @@ -43,26 +43,25 @@ class KRTToRCBijectionAbstract: This class should never be created directly. """ - def __init__(self, krt): + def __init__(self, tp_krt): """ Initialize the bijection by obtaining the important information from the KR tableaux. INPUT: - - ``krt`` -- The tensor product of KR tableaux + - ``parent`` -- The parent of tensor product of KR tableaux EXAMPLES:: sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 4, 1], [[2,1]]) sage: from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA - sage: bijection = KRTToRCBijectionTypeA(KRT(pathlist=[[4,3]])) + sage: bijection = KRTToRCBijectionTypeA(KRT(pathlist=[[3,1]])) sage: TestSuite(bijection).run() """ - self.ret_rig_con = krt.parent()._bijection_class( - krt.parent().affine_ct, - krt.parent().dims)(partition_list=[[]] * - krt.parent().cartan_type().n) + self.tp_krt = tp_krt + self.n = tp_krt.parent().cartan_type().classical().rank() + self.ret_rig_con = tp_krt.parent().rigged_configurations()(partition_list=[[]] * self.n) # We allow this to be mutable to make the bijection easier to program. # Upon completing the bijection, this will be set to immutable. # Do not call this, the object could be in a mutable state and ultimately @@ -84,13 +83,90 @@ def __eq__(self, rhs): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 4, 1], [[2,1]]) sage: from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA - sage: bijection = KRTToRCBijectionTypeA(KRT(pathlist=[[4,3]])) - sage: bijection2 = KRTToRCBijectionTypeA(KRT(pathlist=[[4,3]])) + sage: bijection = KRTToRCBijectionTypeA(KRT(pathlist=[[5,3]])) + sage: bijection2 = KRTToRCBijectionTypeA(KRT(pathlist=[[5,3]])) sage: bijection == bijection2 True """ return isinstance(rhs, KRTToRCBijectionAbstract) + def run(self, verbose=False): + """ + Run the bijection from a tensor product of KR tableaux to a rigged + configuration. + + INPUT: + + - ``tp_krt`` -- A tensor product of KR tableaux + + - ``verbose`` -- (Default: ``False``) Display each step in the + bijection + + EXAMPLES:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 4, 1], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA + sage: KRTToRCBijectionTypeA(KRT(pathlist=[[5,2]])).run() + + -1[ ]-1 + + 1[ ]1 + + 0[ ]0 + + -1[ ]-1 + + """ + if verbose: + from sage.combinat.rigged_configurations.tensor_product_kr_tableaux_element \ + import TensorProductOfKirillovReshetikhinTableauxElement + + for cur_crystal in reversed(self.tp_krt): + # Iterate through the columns + for col_number, cur_column in enumerate(reversed(cur_crystal.to_array(False))): + self.cur_path.insert(0, []) # Prepend an empty list + + self.cur_dims.insert(0, [0, 1]) + + for letter in reversed(cur_column): + self.cur_dims[0][0] += 1 + val = letter.value # Convert from a CrystalOfLetter to an Integer + + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + + # Build the next state + self.cur_path[0].insert(0, [letter]) # Prepend the value + self.next_state(val) + + # If we've split off a column, we need to merge the current column + # to the current crystal tableau + if col_number > 0: + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + print("Applying column merge") + + for i, letter_singleton in enumerate(self.cur_path[0]): + self.cur_path[1][i].insert(0, letter_singleton[0]) + self.cur_dims[1][1] += 1 + self.cur_path.pop(0) + self.cur_dims.pop(0) + + # And perform the inverse column splitting map on the RC + for a in range(self.n): + self._update_vacancy_nums(a) + + self.ret_rig_con.set_immutable() # Return it to immutable + return self.ret_rig_con + @abstract_method def next_state(self, val): r""" @@ -98,14 +174,13 @@ def next_state(self, val): INPUT: - - ``val`` -- The value we are adding - - ``tableau_height`` -- The height of the tableau + - ``val`` -- The value we are adding TESTS:: sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 4, 1], [[2,1]]) sage: from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA - sage: bijection = KRTToRCBijectionTypeA(KRT(pathlist=[[4,3]])) + sage: bijection = KRTToRCBijectionTypeA(KRT(pathlist=[[5,3]])) sage: bijection.cur_path.insert(0, []) sage: bijection.cur_dims.insert(0, [0, 1]) sage: bijection.cur_path[0].insert(0, [3]) @@ -138,7 +213,7 @@ def _update_vacancy_nums(self, a): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 4, 1], [[2,1]]) sage: from sage.combinat.rigged_configurations.bij_abstract_class import KRTToRCBijectionAbstract - sage: bijection = KRTToRCBijectionAbstract(KRT(pathlist=[[4,3]])) + sage: bijection = KRTToRCBijectionAbstract(KRT(pathlist=[[3,2]])) sage: bijection._update_vacancy_nums(2) """ # Check to make sure we have a valid index (currently removed) @@ -147,17 +222,17 @@ def _update_vacancy_nums(self, a): return # Setup the first block - blockLen = self.ret_rig_con[a][0] + block_len = self.ret_rig_con[a][0] vac_num = self.ret_rig_con.parent()._calc_vacancy_number(self.ret_rig_con.nu(), a, 0, dims=self.cur_dims) for i, row_len in enumerate(self.ret_rig_con[a]): # If we've gone to a different sized block, then update the # values which change when moving to a new block size - if blockLen != row_len: + if block_len != row_len: vac_num = self.ret_rig_con.parent()._calc_vacancy_number(self.ret_rig_con.nu(), a, i, dims=self.cur_dims) - blockLen = row_len + block_len = row_len self.ret_rig_con[a].vacancy_numbers[i] = vac_num def _update_partition_values(self, a): @@ -167,7 +242,7 @@ def _update_partition_values(self, a): Helper function to update the partition values of a given rigged partition row. This will go through all of our partition values and set them to our vacancy number if the corresponding row has been changed - (indicated by being set to None). + (indicated by being set to ``None``). INPUT: @@ -177,23 +252,22 @@ def _update_partition_values(self, a): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 4, 1], [[2,1]]) sage: from sage.combinat.rigged_configurations.bij_abstract_class import KRTToRCBijectionAbstract - sage: bijection = KRTToRCBijectionAbstract(KRT(pathlist=[[4,3]])) + sage: bijection = KRTToRCBijectionAbstract(KRT(pathlist=[[5,2]])) sage: bijection._update_partition_values(2) """ rigged_partition = self.ret_rig_con[a] for index, value in enumerate(rigged_partition.rigging): if value == None: rigged_partition.rigging[index] = rigged_partition.vacancy_numbers[index] - if index > 0 and rigged_partition[index - 1] == rigged_partition[index] \ + if index > 0 and rigged_partition[index - 1] == rigged_partition[index] \ and rigged_partition.rigging[index - 1] < rigged_partition.rigging[index]: # If we need to reorder pos = 0 width = rigged_partition[index] - #width = rigged_partition._get_item(index) # ...New way... - Change all val = rigged_partition.rigging[index] - for i in reversed(range(index - 1)): + for i in reversed(range(index-1)): if rigged_partition[i] > width or rigged_partition.rigging[i] >= val: - pos = i + pos = i + 1 break rigged_partition.rigging.pop(index) @@ -201,7 +275,8 @@ def _update_partition_values(self, a): class RCToKRTBijectionAbstract: """ - Root abstract class for the bijection from rigged configurations to crystal paths. + Root abstract class for the bijection from rigged configurations to + tensor product of Kirillov-Reshetikhin tableaux. This class holds the state of the bijection and generates the next state. This class should never be created directly. @@ -220,26 +295,13 @@ def __init__(self, RC_element): sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 1]]) sage: from sage.combinat.rigged_configurations.bij_abstract_class import RCToKRTBijectionAbstract sage: bijection = RCToKRTBijectionAbstract(RC(partition_list=[[1],[1],[1],[1]])) - sage: bijection.rigged_con - - -1[ ]-1 - - 1[ ]1 - - 0[ ]0 - - -1[ ]-1 - sage: TestSuite(bijection).run() """ - # L = [[0]] * crystalPath.parent().cartan_type().n - # for dim in crystalPath.parent().dims: - # L[dim[0]-1][0] += 1 - # Make a mutable clone of the rigged configuration for the bijection # This will be deleted when the bijection is completed - #self.rigged_con = deepcopy(RC_element) self.rigged_con = RC_element.__copy__() + self.n = RC_element.parent().cartan_type().classical().rank() + self.KRT = RC_element.parent().tensor_product_of_Kirillov_Reshetikhin_tableaux() # Make a (deep) copy of the dimensions for the bijection self.cur_dims = [list(x[:]) for x in self.rigged_con.parent().dims] @@ -279,6 +341,81 @@ def __eq__(self, rhs): """ return isinstance(rhs, RCToKRTBijectionAbstract) + def run(self, verbose=False): + """ + Run the bijection from rigged configurations to tensor product of KR + tableaux. + + INPUT: + + - ``verbose`` -- (Default: ``False``) Display each step in the + bijection + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA + sage: RCToKRTBijectionTypeA(RC(partition_list=[[1],[1],[1],[1]])).run() + [[2], [5]] + """ + from sage.combinat.crystals.letters import CrystalOfLetters + letters = CrystalOfLetters(self.rigged_con.parent()._cartan_type.classical()) + + # This is technically bad, but because the first thing we do is append + # an empty list to ret_crystal_path, we correct this. We do it this + # way so that we do not have to remove an empty list after the + # bijection has been performed. + ret_crystal_path = [] + + for dim in self.rigged_con.parent().dims: + ret_crystal_path.append([]) + + # Iterate over each column + for dummy_var in range(dim[1]): + # Split off a new column if necessary + if self.cur_dims[0][1] > 1: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + print("Applying column split") + + self.cur_dims[0][1] -= 1 + self.cur_dims.insert(0, [dim[0], 1]) + + # Perform the corresponding splitting map on rigged configurations + # All it does is update the vacancy numbers on the RC side + for a in range(self.n): + self._update_vacancy_numbers(a) + + while self.cur_dims[0][0] > 0: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + + self.cur_dims[0][0] -= 1 # This takes care of the indexing + b = self.next_state(self.cur_dims[0][0]) + + # Make sure we have a crystal letter + ret_crystal_path[-1].append(letters(b)) # Append the rank + + self.cur_dims.pop(0) # Pop off the leading column + + # Basic check to make sure we end with the empty configuration + #tot_len = sum([len(rp) for rp in self.cur_partitions]) + #if tot_len != 0: + # print "Invalid bijection end for:" + # print self.rigged_con + # print "-----------------------" + # print self.cur_partitions + # raise ValueError("Invalid bijection end") + return self.KRT(pathlist=ret_crystal_path) + @abstract_method def next_state(self, height): """ @@ -322,29 +459,31 @@ def _update_vacancy_numbers(self, a): partition = self.cur_partitions[a] # Setup the first block - blockLen = partition[0] - vacNum = self.rigged_con.parent()._calc_vacancy_number(self.cur_partitions, - a, 0, dims=self.cur_dims) + block_len = partition[0] + vac_num = self.rigged_con.parent()._calc_vacancy_number(self.cur_partitions, + a, 0, dims=self.cur_dims) - for i, rowLen in enumerate(self.cur_partitions[a]): + for i, row_len in enumerate(self.cur_partitions[a]): # If we've gone to a different sized block, then update the # values which change when moving to a new block size - if blockLen != rowLen: - vacNum = self.rigged_con.parent()._calc_vacancy_number(self.cur_partitions, - a, i, dims=self.cur_dims) - blockLen = rowLen + if block_len != row_len: + vac_num = self.rigged_con.parent()._calc_vacancy_number(self.cur_partitions, + a, i, dims=self.cur_dims) + block_len = row_len - partition.vacancy_numbers[i] = vacNum + partition.vacancy_numbers[i] = vac_num def _find_singular_string(self, partition, last_size): r""" - Return the index of the singular string or None if not found. + Return the index of the singular string or ``None`` if not found. - Helper method to find a singular string at least as long as last_size. + Helper method to find a singular string at least as long as + ``last_size``. INPUT: - ``partition`` -- The partition to look in + - ``last_size`` -- The last size found TESTS:: diff --git a/src/sage/combinat/rigged_configurations/bij_type_A.py b/src/sage/combinat/rigged_configurations/bij_type_A.py index 8d0e3e5cf22..ee4070ad567 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_A.py +++ b/src/sage/combinat/rigged_configurations/bij_type_A.py @@ -13,7 +13,11 @@ sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 4, 1], [[2,1]]) sage: from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA - sage: bijection = KRTToRCBijectionTypeA(KRT(pathlist=[[4,3]])) + sage: bijection = KRTToRCBijectionTypeA(KRT(pathlist=[[5,2]])) + sage: TestSuite(bijection).run() + sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA + sage: bijection = RCToKRTBijectionTypeA(RC(partition_list=[[],[],[],[]])) sage: TestSuite(bijection).run() """ @@ -37,12 +41,13 @@ class KRTToRCBijectionTypeA(KRTToRCBijectionAbstract): r""" - Specific implementation of the bijection from KR tableaux to rigged configurations for type `A_n^{(1)}`. + Specific implementation of the bijection from KR tableaux to rigged + configurations for type `A_n^{(1)}`. """ def next_state(self, val): r""" - Build the next state for type `A_n^{(1)}` + Build the next state for type `A_n^{(1)}`. EXAMPLES:: @@ -53,37 +58,27 @@ def next_state(self, val): sage: bijection.cur_dims.insert(0, [0, 1]) sage: bijection.cur_path[0].insert(0, [3]) sage: bijection.next_state(3) - sage: bijection.ret_rig_con - - -1[ ]-1 - - -1[ ]-1 - - (/) - - (/) - """ tableau_height = len(self.cur_path[0]) - 1 + n = self.n # Note first we subtract off for the n = max value (in the path) - 1, # then we remove 1 to match the indices between math and programming. if val - 1 > tableau_height: - retRowPrev = -1 # Always add a cell to the first singular value in the first # tableau we are updating. if len(self.ret_rig_con[val - 2]) > 0: - maxWidth = self.ret_rig_con[val - 2][0] # + 1 + max_width = self.ret_rig_con[val - 2][0] else: - maxWidth = 1 + max_width = 1 # Insert a cell into the rightmost rigged partition - maxWidth = self.ret_rig_con[val - 2].insert_cell(maxWidth) + max_width = self.ret_rig_con[val - 2].insert_cell(max_width) # Move to the left and update values as we have finished modifying # everything which affects its vacancy/partition values for a in reversed(range(tableau_height, val - 2)): - maxWidth = self.ret_rig_con[a].insert_cell(maxWidth) + max_width = self.ret_rig_con[a].insert_cell(max_width) self._update_vacancy_nums(a + 1) self._update_partition_values(a + 1) @@ -94,14 +89,14 @@ def next_state(self, val): self._update_vacancy_nums(tableau_height) self._update_partition_values(tableau_height) - if val - 1 < self.ret_rig_con.parent()._cartan_type.n: + if val - 1 < n: self._update_vacancy_nums(val - 1) if tableau_height > 0: self._update_vacancy_nums(tableau_height - 1) - elif tableau_height - 1 < self.ret_rig_con.parent()._cartan_type.n: + elif tableau_height - 1 < n: # Otherwise we just need to update the vacancy numbers that are affected - if tableau_height < self.ret_rig_con.parent()._cartan_type.n: + if tableau_height < n: self._update_vacancy_nums(tableau_height) if tableau_height > 0: @@ -109,7 +104,8 @@ def next_state(self, val): class RCToKRTBijectionTypeA(RCToKRTBijectionAbstract): r""" - Specific implementation of the bijection from rigged configurations to tensor products of KR tableaux for type `A_n^{(1)}`. + Specific implementation of the bijection from rigged configurations to + tensor products of KR tableaux for type `A_n^{(1)}`. """ def next_state(self, height): @@ -123,14 +119,8 @@ def next_state(self, height): sage: bijection = RCToKRTBijectionTypeA(RC(partition_list=[[1],[1],[1],[1]])) sage: bijection.next_state(0) 5 - sage: bijection.cur_partitions - [(/) - , (/) - , (/) - , (/) - ] """ - n = self.rigged_con.parent()._cartan_type.n + n = self.n ell = [None] * n b = None diff --git a/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py b/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py new file mode 100644 index 00000000000..78085ff3558 --- /dev/null +++ b/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py @@ -0,0 +1,327 @@ +r""" +Bijection classes for type `A_{2n}^{(2)\dagger}`. + +Part of the (internal) classes which runs the bijection between rigged +configurations and KR tableaux of type `A_{2n}^{(2)\dagger}`. + +AUTHORS: + +- Travis Scrimshaw (2012-12-21): Initial version + +TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(CartanType(['A', 4, 2]).dual(), [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_dual import KRTToRCBijectionTypeA2Dual + sage: bijection = KRTToRCBijectionTypeA2Dual(KRT(pathlist=[[2,1]])) + sage: TestSuite(bijection).run() + sage: RC = RiggedConfigurations(CartanType(['A', 4, 2]).dual(), [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_dual import RCToKRTBijectionTypeA2Dual + sage: bijection = RCToKRTBijectionTypeA2Dual(RC(partition_list=[[],[]])) + sage: TestSuite(bijection).run() +""" + +#***************************************************************************** +# Copyright (C) 2012 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.combinat.rigged_configurations.bij_type_C import KRTToRCBijectionTypeC +from sage.combinat.rigged_configurations.bij_type_C import RCToKRTBijectionTypeC +from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA + +from sage.rings.all import QQ + +class KRTToRCBijectionTypeA2Dual(KRTToRCBijectionTypeC): + r""" + Specific implementation of the bijection from KR tableaux to rigged + configurations for type `A_{2n}^{(2)\dagger}`. + + This inherits from type `C_n^{(1)}` because we use the same methods in + some places. + """ + + def next_state(self, val): + r""" + Build the next state for type `A_{2n}^{(2)\dagger}`. + + TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(CartanType(['A', 4, 2]).dual(), [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_dual import KRTToRCBijectionTypeA2Dual + sage: bijection = KRTToRCBijectionTypeA2Dual(KRT(pathlist=[[-1,2]])) + sage: bijection.cur_path.insert(0, []) + sage: bijection.cur_dims.insert(0, [0, 1]) + sage: bijection.cur_path[0].insert(0, [2]) + sage: bijection.next_state(2) + """ + n = self.n + tableau_height = len(self.cur_path[0]) - 1 + + if val > 0: + # If it is a regular value, we follow the A_n rules + KRTToRCBijectionTypeA.next_state(self, val) + return + + pos_val = -val + + if pos_val == 0: + if len(self.ret_rig_con[pos_val - 1]) > 0: + max_width = self.ret_rig_con[n-1][0] + else: + max_width = 1 + max_width = self.ret_rig_con[n-1].insert_cell(max_width) + width_n = max_width + 1 + + # Follow regular A_n rules + for a in reversed(range(tableau_height, n-1)): + max_width = self.ret_rig_con[a].insert_cell(max_width) + self._update_vacancy_nums(a + 1) + self._update_partition_values(a + 1) + self._update_vacancy_nums(tableau_height) + self._update_partition_values(tableau_height) + + # Make the new string at n quasi-singular + p = self.ret_rig_con[n-1] + for i in range(len(p)): + if p._list[i] == width_n: + p.rigging[i] = p.rigging[i] - QQ(1)/QQ(2) + break + return + + case_S = [None] * n + pos_val = -val + + # Always add a cell to the first singular value in the first + # tableau we are updating. + if len(self.ret_rig_con[pos_val - 1]) > 0: + max_width = self.ret_rig_con[pos_val - 1][0] + else: + max_width = 1 + + # Add cells similar to type A_n but we move to the right until we + # reach the value of n-1 + for a in range(pos_val - 1, n-1): + max_width = self.ret_rig_con[a].insert_cell(max_width) + case_S[a] = max_width + + # Special case for n + # If we find a quasi-singular string first, then we are in case (Q, S) + # otherwise we will find a singular string and insert 2 cells + partition = self.ret_rig_con[n-1] + num_rows = len(partition) + case_QS = False + for i in range(num_rows + 1): + if i == num_rows: + max_width = 0 + if case_QS: + partition._list.append(1) + partition.vacancy_numbers.append(None) + # Go through our partition until we find a length of greater than 1 + j = len(partition._list) - 1 + while j >= 0 and partition._list[j] == 1: + j -= 1 + partition.rigging.insert(j + 1, None) + width_n = 1 + else: + # Go through our partition until we find a length of greater than 2 + j = len(partition._list) - 1 + while j >= 0 and partition._list[j] <= 2: + j -= 1 + partition._list.insert(j+1, 2) + partition.vacancy_numbers.insert(j+1, None) + partition.rigging.insert(j+1, None) + break + elif partition._list[i] <= max_width: + if partition.vacancy_numbers[i] == partition.rigging[i]: + max_width = partition._list[i] + if case_QS: + partition._list[i] += 1 + width_n = partition._list[i] + partition.rigging[i] = None + else: + j = i - 1 + while j >= 0 and partition._list[j] <= max_width + 2: + partition.rigging[j+1] = partition.rigging[j] # Shuffle it along + j -= 1 + partition._list.pop(i) + partition._list.insert(j+1, max_width + 2) + partition.rigging[j+1] = None + break + elif partition.vacancy_numbers[i] - QQ(1)/QQ(2) == partition.rigging[i] and not case_QS: + case_QS = True + partition._list[i] += 1 + partition.rigging[i] = None + # No need to set max_width here since we will find a singular string + + # Now go back following the regular C_n (ish) rules + for a in reversed(range(tableau_height, n-1)): + if case_S[a] == max_width: + self._insert_cell_case_S(self.ret_rig_con[a]) + else: + max_width = self.ret_rig_con[a].insert_cell(max_width) + self._update_vacancy_nums(a + 1) + self._update_partition_values(a + 1) + + # Update the final rigged partitions + if tableau_height < n: + self._update_vacancy_nums(tableau_height) + self._update_partition_values(tableau_height) + + if pos_val <= tableau_height: + for a in range(pos_val-1, tableau_height): + self._update_vacancy_nums(a) + self._update_partition_values(a) + if pos_val > 1: + self._update_vacancy_nums(pos_val - 2) + self._update_partition_values(pos_val - 2) + elif tableau_height > 0: + self._update_vacancy_nums(tableau_height - 1) + self._update_partition_values(tableau_height - 1) + + if case_QS: + # Make the new string quasi-singular + num_rows = len(partition) + for i in range(num_rows): + if partition._list[i] == width_n: + partition.rigging[i] = partition.rigging[i] - QQ(1)/QQ(2) + +class RCToKRTBijectionTypeA2Dual(RCToKRTBijectionTypeC): + r""" + Specific implementation of the bijection from rigged configurations to + tensor products of KR tableaux for type `A_{2n}^{(2)\dagger}`. + """ + + def next_state(self, height): + r""" + Build the next state for type `A_{2n}^{(2)\dagger}`. + + TESTS:: + + sage: RC = RiggedConfigurations(CartanType(['A', 4, 2]).dual(), [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_dual import RCToKRTBijectionTypeA2Dual + sage: bijection = RCToKRTBijectionTypeA2Dual(RC(partition_list=[[2],[2,2]])) + sage: bijection.next_state(1) + -1 + """ + n = self.n + ell = [None] * (2*n) + case_S = [False] * n + case_Q = False + b = None + + # Calculate the rank and ell values + + last_size = 0 + for a in range(height, n-1): + ell[a] = self._find_singular_string(self.cur_partitions[a], last_size) + + if ell[a] is None: + b = a + 1 + break + else: + last_size = self.cur_partitions[a][ell[a]] + + if b is None: + partition = self.cur_partitions[n-1] + # Special case for n + for i in reversed(range(len(partition))): + if partition[i] >= last_size: + if partition.vacancy_numbers[i] == partition.rigging[i]: + last_size = partition[i] + case_S[n-1] = True + ell[2*n-1] = i + break + elif partition.vacancy_numbers[i] - QQ(1)/QQ(2) == partition.rigging[i] and not case_Q: + case_Q = True + # This will never be singular + last_size = partition[i] + 1 + ell[n-1] = i + + if ell[2*n-1] is None: + if not case_Q: + b = n + else: + b = 0 + + if b is None: + # Now go back + for a in reversed(range(n-1)): + if a >= height and self.cur_partitions[a][ell[a]] == last_size: + ell[n+a] = ell[a] + case_S[a] = True + else: + ell[n+a] = self._find_singular_string(self.cur_partitions[a], last_size) + + if ell[n + a] is None: + b = -(a + 2) + break + else: + last_size = self.cur_partitions[a][ell[n + a]] + + if b is None: + b = -1 + + # Determine the new rigged configuration by removing boxes from the + # selected string and then making the new string singular + if case_S[0]: + row_num = None + row_num_bar = self.cur_partitions[0].remove_cell(ell[n], 2) + else: + row_num = self.cur_partitions[0].remove_cell(ell[0]) + row_num_bar = self.cur_partitions[0].remove_cell(ell[n]) + for a in range(1, n-1): + if case_S[a]: + row_num_next = None + row_num_bar_next = self.cur_partitions[a].remove_cell(ell[n+a], 2) + else: + row_num_next = self.cur_partitions[a].remove_cell(ell[a]) + row_num_bar_next = self.cur_partitions[a].remove_cell(ell[n+a]) + + self._update_vacancy_numbers(a - 1) + if row_num is not None: + self.cur_partitions[a-1].rigging[row_num] = self.cur_partitions[a-1].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[a-1].rigging[row_num_bar] = self.cur_partitions[a-1].vacancy_numbers[row_num_bar] + row_num = row_num_next + row_num_bar = row_num_bar_next + + if case_Q: + row_num_next = self.cur_partitions[n-1].remove_cell(ell[n-1]) + if case_S[n-1]: + row_num_bar_next = self.cur_partitions[n-1].remove_cell(ell[2*n-1]) + else: + row_num_bar_next = None + elif case_S[n-1]: + row_num_next = None + row_num_bar_next = self.cur_partitions[n-1].remove_cell(ell[2*n-1], 2) + else: + row_num_next = None + row_num_bar_next = None + + self._update_vacancy_numbers(n - 2) + if row_num is not None: + self.cur_partitions[n-2].rigging[row_num] = self.cur_partitions[n-2].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[n-2].rigging[row_num_bar] = self.cur_partitions[n-2].vacancy_numbers[row_num_bar] + + self._update_vacancy_numbers(n - 1) + if row_num_next is not None: + self.cur_partitions[n-1].rigging[row_num_next] = self.cur_partitions[n-1].vacancy_numbers[row_num_next] + if row_num_bar_next is not None: + if case_Q: + # This will always be the largest value + self.cur_partitions[n-1].rigging[row_num_bar_next] = self.cur_partitions[n-1].vacancy_numbers[row_num_bar_next] - QQ(1)/QQ(2) + else: + self.cur_partitions[n-1].rigging[row_num_bar_next] = self.cur_partitions[n-1].vacancy_numbers[row_num_bar_next] + + return(b) diff --git a/src/sage/combinat/rigged_configurations/bij_type_A2_even.py b/src/sage/combinat/rigged_configurations/bij_type_A2_even.py new file mode 100644 index 00000000000..ce0f195c3b4 --- /dev/null +++ b/src/sage/combinat/rigged_configurations/bij_type_A2_even.py @@ -0,0 +1,213 @@ +r""" +Bijection classes for type `A_{2n}^{(2)}`. + +Part of the (internal) classes which runs the bijection between rigged +configurations and KR tableaux of type `A_{2n}^{(2)}`. + +AUTHORS: + +- Travis Scrimshaw (2012-12-21): Initial version + +TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 4, 2], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_even import KRTToRCBijectionTypeA2Even + sage: bijection = KRTToRCBijectionTypeA2Even(KRT(pathlist=[[-1,2]])) + sage: TestSuite(bijection).run() + sage: RC = RiggedConfigurations(['A', 4, 2], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_even import RCToKRTBijectionTypeA2Even + sage: bijection = RCToKRTBijectionTypeA2Even(RC(partition_list=[[],[]])) + sage: TestSuite(bijection).run() +""" + +#***************************************************************************** +# Copyright (C) 2012 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.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA +from sage.combinat.rigged_configurations.bij_type_C import KRTToRCBijectionTypeC +from sage.combinat.rigged_configurations.bij_type_C import RCToKRTBijectionTypeC + +class KRTToRCBijectionTypeA2Even(KRTToRCBijectionTypeC): + r""" + Specific implementation of the bijection from KR tableaux to rigged + configurations for type `A_{2n}^{(2)}`. + + This inherits from type `C_n^{(1)}` because we use the same methods in + some places. + """ + + def next_state(self, val): + r""" + Build the next state for type `A_{2n}^{(2)}`. + + TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 4, 2], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_even import KRTToRCBijectionTypeA2Even + sage: bijection = KRTToRCBijectionTypeA2Even(KRT(pathlist=[[-1,-2]])) + sage: bijection.cur_path.insert(0, []) + sage: bijection.cur_dims.insert(0, [0, 1]) + sage: bijection.cur_path[0].insert(0, [-2]) + sage: bijection.next_state(-2) + """ + # Note that we must subtract 1 from n to match the indices. + n = self.n + tableau_height = len(self.cur_path[0]) - 1 + + # If it is a regular value, we follow the A_n rules + if val != 'E' and val > 0: + KRTToRCBijectionTypeA.next_state(self, val) + return + + case_S = [None] * n + if val == 'E': + pos_val = n + max_width = self.ret_rig_con[n-1].insert_cell(0) + else: + pos_val = -val + + # Always add a cell to the first singular value in the first + # tableau we are updating. + if len(self.ret_rig_con[pos_val - 1]) > 0: + max_width = self.ret_rig_con[pos_val - 1][0] + else: + max_width = 1 + + # Add cells similar to type A_n but we move to the right until we + # reach the value of n + for a in range(pos_val - 1, n): + max_width = self.ret_rig_con[a].insert_cell(max_width) + case_S[a] = max_width + + # Special case for n + self._insert_cell_case_S(self.ret_rig_con[n-1]) + + # Now go back following the regular C_n (ish) rules + for a in reversed(range(tableau_height, n-1)): + if case_S[a] == max_width: + self._insert_cell_case_S(self.ret_rig_con[a]) + else: + max_width = self.ret_rig_con[a].insert_cell(max_width) + self._update_vacancy_nums(a + 1) + self._update_partition_values(a + 1) + + # Update the final rigged partitions + if tableau_height < n: + self._update_vacancy_nums(tableau_height) + self._update_partition_values(tableau_height) + + if pos_val <= tableau_height: + for a in range(pos_val-1, tableau_height): + self._update_vacancy_nums(a) + self._update_partition_values(a) + if pos_val > 1: + self._update_vacancy_nums(pos_val - 2) + self._update_partition_values(pos_val - 2) + elif tableau_height > 0: + self._update_vacancy_nums(tableau_height - 1) + self._update_partition_values(tableau_height - 1) + +class RCToKRTBijectionTypeA2Even(RCToKRTBijectionTypeC): + r""" + Specific implementation of the bijection from rigged configurations to + tensor products of KR tableaux for type `A_{2n}^{(2)}`. + """ + + def next_state(self, height): + r""" + Build the next state for type `A_{2n}^{(2)}`. + + TESTS:: + + sage: RC = RiggedConfigurations(['A', 4, 2], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_even import RCToKRTBijectionTypeA2Even + sage: bijection = RCToKRTBijectionTypeA2Even(RC(partition_list=[[2],[2,2]])) + sage: bijection.next_state(1) + -1 + """ + n = self.n + ell = [None] * (2*n) + case_S = [False] * n + b = None + + # Calculate the rank and ell values + + last_size = 0 + for a in range(height, n): + ell[a] = self._find_singular_string(self.cur_partitions[a], last_size) + + if ell[a] is None: + b = a + 1 + break + else: + last_size = self.cur_partitions[a][ell[a]] + + if b is None and last_size == 1: + b = 'E' + # This is a slight hack since remove_cell() will just delete the + # appropriate row + case_S[n-1] = True + + if b is None: + # Now go back + ell[2*n-1] = ell[n-1] + case_S[n-1] = True + for a in reversed(range(n-1)): + if a >= height and self.cur_partitions[a][ell[a]] == last_size: + ell[n+a] = ell[a] + case_S[a] = True + else: + ell[n+a] = self._find_singular_string(self.cur_partitions[a], last_size) + + if ell[n + a] is None: + b = -(a + 2) + break + else: + last_size = self.cur_partitions[a][ell[n + a]] + + if b is None: + b = -1 + + # Determine the new rigged configuration by removing boxes from the + # selected string and then making the new string singular + if case_S[0]: + row_num = self.cur_partitions[0].remove_cell(ell[0], 2) + row_num_bar = None + else: + row_num = self.cur_partitions[0].remove_cell(ell[0]) + row_num_bar = self.cur_partitions[0].remove_cell(ell[n]) + for a in range(1, n): + if case_S[a]: + row_num_next = self.cur_partitions[a].remove_cell(ell[a], 2) + row_num_bar_next = None + else: + row_num_next = self.cur_partitions[a].remove_cell(ell[a]) + row_num_bar_next = self.cur_partitions[a].remove_cell(ell[n+a]) + + self._update_vacancy_numbers(a - 1) + if row_num is not None: + self.cur_partitions[a-1].rigging[row_num] = self.cur_partitions[a-1].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[a-1].rigging[row_num_bar] = self.cur_partitions[a-1].vacancy_numbers[row_num_bar] + row_num = row_num_next + row_num_bar = row_num_bar_next + + self._update_vacancy_numbers(n - 1) + if row_num is not None: + self.cur_partitions[n-1].rigging[row_num] = self.cur_partitions[n-1].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[n-1].rigging[row_num_bar] = self.cur_partitions[n-1].vacancy_numbers[row_num_bar] + + return(b) diff --git a/src/sage/combinat/rigged_configurations/bij_type_A2_odd.py b/src/sage/combinat/rigged_configurations/bij_type_A2_odd.py new file mode 100644 index 00000000000..7ae915dff8c --- /dev/null +++ b/src/sage/combinat/rigged_configurations/bij_type_A2_odd.py @@ -0,0 +1,195 @@ +r""" +Bijection classes for type `A_{2n-1}^{(2)}`. + +Part of the (internal) classes which runs the bijection between rigged +configurations and KR tableaux of type `A_{2n-1}^{(2)}`. + +AUTHORS: + +- Travis Scrimshaw (2012-12-21): Initial version + +TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 5, 2], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_odd import KRTToRCBijectionTypeA2Odd + sage: bijection = KRTToRCBijectionTypeA2Odd(KRT(pathlist=[[-1,2]])) + sage: TestSuite(bijection).run() + sage: RC = RiggedConfigurations(['A', 5, 2], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_odd import RCToKRTBijectionTypeA2Odd + sage: bijection = RCToKRTBijectionTypeA2Odd(RC(partition_list=[[],[],[]])) + sage: TestSuite(bijection).run() +""" + +#***************************************************************************** +# Copyright (C) 2012 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.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA +from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA + +class KRTToRCBijectionTypeA2Odd(KRTToRCBijectionTypeA): + r""" + Specific implementation of the bijection from KR tableaux to rigged + configurations for type `A_{2n-1}^{(2)}`. + + This inherits from type `A_n^{(1)}` because we use the same methods in + some places. + """ + + def next_state(self, val): + r""" + Build the next state for type `A_{2n-1}^{(2)}`. + + TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 5, 2], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_odd import KRTToRCBijectionTypeA2Odd + sage: bijection = KRTToRCBijectionTypeA2Odd(KRT(pathlist=[[-2,3]])) + sage: bijection.cur_path.insert(0, []) + sage: bijection.cur_dims.insert(0, [0, 1]) + sage: bijection.cur_path[0].insert(0, [3]) + sage: bijection.next_state(3) + """ + # Note that we must subtract 1 from n to match the indices. + n = self.n + tableau_height = len(self.cur_path[0]) - 1 + + # If it is a regular value, we follow the A_n rules + if val > 0: + KRTToRCBijectionTypeA.next_state(self, val) + return + + pos_val = -val + + # Always add a cell to the first singular value in the first + # tableau we are updating. + if len(self.ret_rig_con[pos_val - 1]) > 0: + max_width = self.ret_rig_con[pos_val - 1][0] + else: + max_width = 1 + + # Add cells similar to type A_n but we move to the right until we + # reach the value of n + for a in range(pos_val - 1, n): + max_width = self.ret_rig_con[a].insert_cell(max_width) + + # Now go back following the regular A_n rules + for a in reversed(range(tableau_height, n - 1)): + max_width = self.ret_rig_con[a].insert_cell(max_width) + self._update_vacancy_nums(a + 1) + self._update_partition_values(a + 1) + + # Update the final rigged partitions + if tableau_height < n: + self._update_vacancy_nums(tableau_height) + self._update_partition_values(tableau_height) + + if pos_val <= tableau_height: + for a in range(pos_val-1, tableau_height): + self._update_vacancy_nums(a) + self._update_partition_values(a) + if pos_val > 1: + self._update_vacancy_nums(pos_val - 2) + self._update_partition_values(pos_val - 2) + elif tableau_height > 0: + self._update_vacancy_nums(tableau_height - 1) + self._update_partition_values(tableau_height - 1) + +class RCToKRTBijectionTypeA2Odd(RCToKRTBijectionTypeA): + r""" + Specific implementation of the bijection from rigged configurations to + tensor products of KR tableaux for type `A_{2n-1}^{(2)}`. + """ + + def next_state(self, height): + r""" + Build the next state for type `A_{2n-1}^{(2)}`. + + TESTS:: + + sage: RC = RiggedConfigurations(['A', 5, 2], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_A2_odd import RCToKRTBijectionTypeA2Odd + sage: bijection = RCToKRTBijectionTypeA2Odd(RC(partition_list=[[1],[2,1],[2]])) + sage: bijection.next_state(0) + -2 + """ + n = self.n + ell = [None] * (2*n) + b = None + + # Calculate the rank and ell values + + last_size = 0 + for a in range(height, n): + ell[a] = self._find_singular_string(self.cur_partitions[a], last_size) + + if ell[a] is None: + b = a + 1 + break + else: + last_size = self.cur_partitions[a][ell[a]] + + if b is None: + # Now go back + for a in reversed(range(n - 1)): + # Modified form of _find_singular_string() + end = ell[a] + if a < height: + end = len(self.cur_partitions[a]) + for i in reversed(range(end)): + if self.cur_partitions[a][i] >= last_size and \ + self.cur_partitions[a].vacancy_numbers[i] == self.cur_partitions[a].rigging[i]: + ell[n + a] = i + break + + if ell[n + a] is None: + b = -(a + 2) + break + else: + last_size = self.cur_partitions[a][ell[n + a]] + + if b is None: + b = -1 + + # Determine the new rigged configuration by removing a box from the selected + # string and then making the new string singular + ret_row = self.cur_partitions[0].remove_cell(ell[0]) + ret_row_bar = self.cur_partitions[0].remove_cell(ell[n]) + for a in range(1, n - 1): + ret_row_next = self.cur_partitions[a].remove_cell(ell[a]) + ret_row_bar_next = self.cur_partitions[a].remove_cell(ell[n + a]) + + self._update_vacancy_numbers(a - 1) + if ret_row is not None: + self.cur_partitions[a-1].rigging[ret_row] = self.cur_partitions[a-1].vacancy_numbers[ret_row] + if ret_row_bar is not None: + self.cur_partitions[a-1].rigging[ret_row_bar] = self.cur_partitions[a-1].vacancy_numbers[ret_row_bar] + + ret_row = ret_row_next + ret_row_bar = ret_row_bar_next + + ret_row_next = self.cur_partitions[n-1].remove_cell(ell[n-1]) + + self._update_vacancy_numbers(n - 2) + if ret_row is not None: + self.cur_partitions[n-2].rigging[ret_row] = self.cur_partitions[n-2].vacancy_numbers[ret_row] + if ret_row_bar is not None: + self.cur_partitions[n-2].rigging[ret_row_bar] = self.cur_partitions[n-2].vacancy_numbers[ret_row_bar] + + self._update_vacancy_numbers(n - 1) + if ret_row_next is not None: + self.cur_partitions[n-1].rigging[ret_row_next] = self.cur_partitions[n-1].vacancy_numbers[ret_row_next] + + return(b) + diff --git a/src/sage/combinat/rigged_configurations/bij_type_B.py b/src/sage/combinat/rigged_configurations/bij_type_B.py new file mode 100644 index 00000000000..5c034925a63 --- /dev/null +++ b/src/sage/combinat/rigged_configurations/bij_type_B.py @@ -0,0 +1,827 @@ +r""" +Bijection classes for type `B_n^{(1)}`. + +Part of the (internal) classes which runs the bijection between rigged +configurations and KR tableaux of type `B_n^{(1)}`. + +AUTHORS: + +- Travis Scrimshaw (2012-12-21): Initial version + +TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['B', 3, 1], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_B import KRTToRCBijectionTypeB + sage: bijection = KRTToRCBijectionTypeB(KRT(pathlist=[[-1,2]])) + sage: TestSuite(bijection).run() + sage: RC = RiggedConfigurations(['B', 3, 1], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_B import RCToKRTBijectionTypeB + sage: bijection = RCToKRTBijectionTypeB(RC(partition_list=[[],[],[]])) + sage: TestSuite(bijection).run() +""" + +#***************************************************************************** +# Copyright (C) 2012 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.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA +from sage.combinat.rigged_configurations.bij_type_C import KRTToRCBijectionTypeC +from sage.combinat.rigged_configurations.bij_type_C import RCToKRTBijectionTypeC + +class KRTToRCBijectionTypeB(KRTToRCBijectionTypeC): + r""" + Specific implementation of the bijection from KR tableaux to rigged + configurations for type `B_n^{(1)}`. + """ + def run(self, verbose=False): + """ + Run the bijection from a tensor product of KR tableaux to a rigged + configuration. + + INPUT: + + - ``tp_krt`` -- A tensor product of KR tableaux + + - ``verbose`` -- (Default: ``False``) Display each step in the + bijection + + EXAMPLES:: + + sage: from sage.combinat.rigged_configurations.bij_type_B import KRTToRCBijectionTypeB + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['B', 3, 1], [[2, 1]]) + sage: KRTToRCBijectionTypeB(KRT(pathlist=[[0,3]])).run() + + 0[ ]0 + + -1[ ]-1 + -1[ ]-1 + + 0[]0 + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['B', 3, 1], [[3, 1]]) + sage: KRTToRCBijectionTypeB(KRT(pathlist=[[-2,3,1]])).run() + + (/) + + -1[ ]-1 + + 0[]0 + + """ + if verbose: + from sage.combinat.rigged_configurations.tensor_product_kr_tableaux_element \ + import TensorProductOfKirillovReshetikhinTableauxElement + + for cur_crystal in reversed(self.tp_krt): + r = cur_crystal.parent().r() + + # Check if it is a spinor + if r == self.n: + # Perform the spinor bijection by converting to type A_{2n-1}^{(2)} + # doing the bijection there and pulling back + from sage.combinat.rigged_configurations.bij_type_A2_odd import KRTToRCBijectionTypeA2Odd + from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux + from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition + + if verbose: + print "====================" + if len(self.cur_path) == 0: + print(repr([])) # Special case for displaying when the rightmost factor is a spinor + else: + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + print("Applying doubling map") + + # Convert to a type A_{2n-1}^{(2)} RC + dims = self.cur_dims[:] + dims.insert(0, [r, cur_crystal.parent().s()]) + KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 2*self.n-1, 2], dims) + # Convert the n-th partition into a regular rigged partition + self.ret_rig_con[-1] = RiggedPartition(self.ret_rig_con[-1]._list, + self.ret_rig_con[-1].rigging, + self.ret_rig_con[-1].vacancy_numbers) + bij = KRTToRCBijectionTypeA2Odd(KRT.module_generators[0]) # Placeholder element + bij.ret_rig_con = KRT.rigged_configurations()(*self.ret_rig_con) + bij.cur_path = self.cur_path + bij.cur_dims = self.cur_dims + for i in range(len(self.cur_dims)): + if bij.cur_dims[i][0] != self.n: + bij.cur_dims[i][1] *= 2 + for i in range(self.n-1): + for j in range(len(bij.ret_rig_con[i])): + bij.ret_rig_con[i]._list[j] *= 2 + bij.ret_rig_con[i].rigging[j] *= 2 + bij.ret_rig_con[i].vacancy_numbers[j] *= 2 + + # Perform the type A_{2n-1}^{(2)} bijection + r = cur_crystal.parent().r() + # Iterate through the columns + for col_number, cur_column in enumerate(reversed(cur_crystal.to_array(False))): + bij.cur_path.insert(0, []) # Prepend an empty list + bij.cur_dims.insert(0, [0, 1]) + + # Note that we do not need to worry about iterating over columns + # (see previous note about the data structure). + for letter in reversed(cur_column): + bij.cur_dims[0][0] += 1 + val = letter.value # Convert from a CrystalOfLetter to an Integer + + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), bij.cur_path))) + print("--------------------") + print(repr(bij.ret_rig_con)) + print("--------------------\n") + + # Build the next state + bij.cur_path[0].insert(0, [letter]) # Prepend the value + bij.next_state(val) + + # If we've split off a column, we need to merge the current column + # to the current crystal tableau + if col_number > 0: + for i, letter_singleton in enumerate(self.cur_path[0]): + bij.cur_path[1][i].insert(0, letter_singleton[0]) + bij.cur_dims[1][1] += 1 + bij.cur_path.pop(0) + bij.cur_dims.pop(0) + + # And perform the inverse column splitting map on the RC + for a in range(self.n): + bij._update_vacancy_nums(a) + + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), bij.cur_path))) + print("--------------------") + print(repr(bij.ret_rig_con)) + print("--------------------\n") + print("Applying halving map") + + # Convert back to a type B_n^{(1)} + for i in range(self.n-1): + for j in range(len(bij.ret_rig_con[i])): + bij.ret_rig_con[i]._list[j] //= 2 + bij.ret_rig_con[i].rigging[j] //= 2 + bij.ret_rig_con[i].vacancy_numbers[j] //= 2 + self.ret_rig_con = self.tp_krt.parent().rigged_configurations()(*bij.ret_rig_con) + # Make it mutable so we don't have to keep making copies, at the + # end of the bijection, we will make it immutable again + self.ret_rig_con._set_mutable() + else: + # Perform the regular type B_n^{(1)} bijection + # Iterate through the columns + for col_number, cur_column in enumerate(reversed(cur_crystal.to_array(False))): + self.cur_path.insert(0, []) # Prepend an empty list + self.cur_dims.insert(0, [0, 1]) + + # Note that we do not need to worry about iterating over columns + # (see previous note about the data structure). + for letter in reversed(cur_column): + self.cur_dims[0][0] += 1 + val = letter.value # Convert from a CrystalOfLetter to an Integer + + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + + # Build the next state + self.cur_path[0].insert(0, [letter]) # Prepend the value + self.next_state(val) + + # If we've split off a column, we need to merge the current column + # to the current crystal tableau + if col_number > 0: + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + print("Applying column merge") + + for i, letter_singleton in enumerate(self.cur_path[0]): + self.cur_path[1][i].insert(0, letter_singleton[0]) + self.cur_dims[1][1] += 1 + self.cur_path.pop(0) + self.cur_dims.pop(0) + + # And perform the inverse column splitting map on the RC + for a in range(self.n): + self._update_vacancy_nums(a) + self.ret_rig_con.set_immutable() # Return it to immutable + return self.ret_rig_con + + def next_state(self, val): + r""" + Build the next state for type `B_n^{(1)}`. + + TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['B', 3, 1], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_B import KRTToRCBijectionTypeB + sage: bijection = KRTToRCBijectionTypeB(KRT(pathlist=[[-1,2]])) + sage: bijection.cur_path.insert(0, []) + sage: bijection.cur_dims.insert(0, [0, 1]) + sage: bijection.cur_path[0].insert(0, [3]) + sage: bijection.next_state(3) + """ + n = self.n + tableau_height = len(self.cur_path[0]) - 1 + + # If it is a regular value, we follow the A_n rules + if val > 0: + KRTToRCBijectionTypeA.next_state(self, val) + return + + pos_val = -val + + # Special case for 0 + if pos_val == 0: + if len(self.ret_rig_con[pos_val - 1]) > 0: + max_width = self.ret_rig_con[n-1][0] + else: + max_width = 1 + max_width = self.ret_rig_con[n-1].insert_cell(max_width) + width_n = max_width + 1 + max_width = max_width // 2 + + # Check to see if we need to make the new string quasi-singular + max_width = self.ret_rig_con[n-2].insert_cell(max_width) + self._update_vacancy_nums(n - 1) + self._update_partition_values(n - 1) + + # Check if we need to make the new string at n quasi-singular + p = self.ret_rig_con[n-1] + num_rows = len(p) + # Note that max width is 1 less than the corresponding string length + if max_width*2 + 1 != width_n: + for i in range(num_rows): + if p._list[i] == width_n: + j = i+1 + while j < num_rows and p._list[j] == width_n \ + and p.vacancy_numbers[j] == p.rigging[j]: + j += 1 + p.rigging[j-1] -= 1 + break + + # Follow regular A_n rules + for a in reversed(range(tableau_height, n-2)): + max_width = self.ret_rig_con[a].insert_cell(max_width) + self._update_vacancy_nums(a + 1) + self._update_partition_values(a + 1) + self._update_vacancy_nums(tableau_height) + self._update_partition_values(tableau_height) + return + + # Always add a cell to the first singular value in the first + # tableau we are updating. + if len(self.ret_rig_con[pos_val - 1]) > 0: + max_width = self.ret_rig_con[pos_val - 1][0] + else: + max_width = 0 + + # Add cells similar to type A_n but we move to the right until n + for a in range(pos_val - 1, n - 1): + max_width = self.ret_rig_con[a].insert_cell(max_width) + + # Handle the special behavior at n + if pos_val != n: + max_width = max_width * 2 + + # Find either the quasi-singular string and the next largest singular, + # the largest singular string if smaller than the max width + # or the two largest singular strings + singular_max_width = False + case_QS = False + # Note, case_QS and singular_max_width will never both be True + p = self.ret_rig_con[n-1] + num_rows = len(p) + width_n = 0 + for i in range(num_rows + 1): + if i == num_rows: + if case_QS: + # If we are in case (QS), we will be adding a box + p._list.append(1) + p.vacancy_numbers.append(None) + p.rigging.append(None) + width_n = 1 + max_width = 0 + elif not singular_max_width: + # If we have not found a (quasi)singular string, we must add 2 boxes + # Go through our partition until we find a length of greater than 2 + j = len(p._list) - 1 + while j >= 0 and p._list[j] <= 2: + j -= 1 + p._list.insert(j+1, 2) + p.vacancy_numbers.insert(j+1, None) + p.rigging.insert(j+1, None) + max_width = 0 + break + elif p.vacancy_numbers[i] == p.rigging[i]: + if p._list[i] < max_width: + if singular_max_width: + width_n = p._list[i] + break + + max_width = p._list[i] + if case_QS: + p._list[i] += 1 + p.rigging[i] = None + width_n = max_width + 1 + else: + # Add 2 boxes + j = i - 1 + while j >= 0 and p._list[j] <= max_width + 2: + p.rigging[j+1] = p.rigging[j] # Shuffle it along + j -= 1 + p._list.pop(i) + p._list.insert(j+1, max_width + 2) + p.rigging[j+1] = None + break + + if p._list[i] == max_width and not singular_max_width: + p._list[i] += 1 # We always at least add a box to the first singular value + p.rigging[i] = None + if case_QS: + break + singular_max_width = True + elif p._list[i] == max_width + 1: + # If we can't add 2 boxes, we must be in case (QS) + p._list[i] += 1 + p.rigging[i] = None + width_n = max_width + case_QS = True + elif p.vacancy_numbers[i] - 1 == p.rigging[i] and not case_QS and not singular_max_width: + case_QS = True + max_width = p._list[i] + p._list[i] += 1 + p.rigging[i] = None + + if singular_max_width: + # There are 2 possibilities, case (S) and case (QS), we might need + # to attempt both + # Make a *deep* copy of the element + cp = self.ret_rig_con.__copy__() + for i,rp in enumerate(cp): + cp[i] = rp._clone() + # We attempt case (S) first + self._insert_cell_case_S(p) + + max_width = max_width // 2 + + # We need to do the next partition in order to determine the step at n + max_width = self.ret_rig_con[n-2].insert_cell(max_width) + + self._update_vacancy_nums(n - 1) + self._update_partition_values(n - 1) + + # If we need to make the smaller added string quasisingular + # Note that max width is 1 less than the corresponding string length + if case_QS and max_width*2 + 1 != width_n: + for i in range(num_rows): + if p._list[i] == width_n: + j = i+1 + while j < num_rows and p._list[j] == width_n \ + and p.vacancy_numbers[j] == p.rigging[j]: + j += 1 + p.rigging[j-1] -= 1 + break + + # Continue back following the regular A_n rules + for a in reversed(range(tableau_height, n - 2)): + max_width = self.ret_rig_con[a].insert_cell(max_width) + self._update_vacancy_nums(a + 1) + self._update_partition_values(a + 1) + + # Update the final rigged partitions + if tableau_height < n: + self._update_vacancy_nums(tableau_height) + self._update_partition_values(tableau_height) + + if 0 < pos_val and pos_val <= tableau_height: + for a in range(pos_val-1, tableau_height): + self._update_vacancy_nums(a) + self._update_partition_values(a) + if pos_val > 1: + self._update_vacancy_nums(pos_val - 2) + self._update_partition_values(pos_val - 2) + elif tableau_height > 0: + self._update_vacancy_nums(tableau_height - 1) + self._update_partition_values(tableau_height - 1) + + if singular_max_width: + try: + self.ret_rig_con.check() + except StandardError: + self.other_outcome(cp, pos_val, width_n) + + def other_outcome(self, rc, pos_val, width_n): + r""" + Do the other case `(QS)` possibility. + + This arises from the ambiguity when we found a singular string at the + max width in `\nu^{(n)}`. We had first attempted case `(S)`, and if + that resulted in an invalid rigged configuration, we now + finish the bijection using case `(QS)`. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['B',3,1], [[2,1],[1,2]]) + sage: rc = RC(partition_list=[[2,1], [2,1,1], [5,1]]) + sage: t = rc.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + sage: t.to_rigged_configuration() == rc # indirect doctest + True + """ + n = self.n + tableau_height = len(self.cur_path[0]) - 1 + self.ret_rig_con = rc + + # We need to do the next partition in order to determine the step at n + max_width = self.ret_rig_con[n-2].insert_cell(width_n // 2) + + # We now attempt case (QS) + case_QS = False + p = self.ret_rig_con[n-1] + num_rows = len(p) + for i in range(len(p._list)): + if p._list[i] == width_n: + p._list[i] += 1 + p.rigging[i] = None + case_QS = True + break + if not case_QS: # we have not added a box yet + p._list.append(1) + p.rigging.append(None) + p.vacancy_numbers.append(None) + case_QS = True + width_n += 1 + + self._update_vacancy_nums(n - 1) + self._update_partition_values(n - 1) + + # If we need to make the smaller added string quasisingular + # Note that max width is 1 less than the corresponding string length + if case_QS and max_width*2 + 1 != width_n: + for i in range(num_rows): + if p._list[i] == width_n: + j = i+1 + while j < num_rows and p._list[j] == width_n \ + and p.vacancy_numbers[j] == p.rigging[j]: + j += 1 + p.rigging[j-1] -= 1 + break + + # Continue back following the regular A_n rules + for a in reversed(range(tableau_height, n - 2)): + max_width = self.ret_rig_con[a].insert_cell(max_width) + self._update_vacancy_nums(a + 1) + self._update_partition_values(a + 1) + + # Update the final rigged partitions + if tableau_height < n: + self._update_vacancy_nums(tableau_height) + self._update_partition_values(tableau_height) + + if 0 < pos_val and pos_val <= tableau_height: + for a in range(pos_val-1, tableau_height): + self._update_vacancy_nums(a) + self._update_partition_values(a) + if pos_val > 1: + self._update_vacancy_nums(pos_val - 2) + self._update_partition_values(pos_val - 2) + elif tableau_height > 0: + self._update_vacancy_nums(tableau_height - 1) + self._update_partition_values(tableau_height - 1) + +class RCToKRTBijectionTypeB(RCToKRTBijectionTypeC): + r""" + Specific implementation of the bijection from rigged configurations to + tensor products of KR tableaux for type `B_n^{(1)}`. + """ + def run(self, verbose=False): + """ + Run the bijection from rigged configurations to tensor product of KR + tableaux for type `B_n^{(1)}`. + + INPUT: + + - ``verbose`` -- (Default: ``False``) Display each step in the + bijection + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['B', 3, 1], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_B import RCToKRTBijectionTypeB + sage: RCToKRTBijectionTypeB(RC(partition_list=[[1],[1,1],[1]])).run() + [[3], [0]] + sage: RC = RiggedConfigurations(['B', 3, 1], [[3, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_B import RCToKRTBijectionTypeB + sage: RCToKRTBijectionTypeB(RC(partition_list=[[],[1],[1]])).run() + [[1], [3], [-2]] + """ + from sage.combinat.crystals.letters import CrystalOfLetters + letters = CrystalOfLetters(self.rigged_con.parent()._cartan_type.classical()) + + # This is technically bad, but because the first thing we do is append + # an empty list to ret_crystal_path, we correct this. We do it this + # way so that we do not have to remove an empty list after the + # bijection has been performed. + ret_crystal_path = [] + + for dim in self.rigged_con.parent().dims: + ret_crystal_path.append([]) + + # Check to see if we are a spinor + if dim[0] == self.n: + # Perform the spinor bijection by converting to type A_{2n-1}^{(2)} + # doing the bijection there and pulling back + + from sage.combinat.rigged_configurations.bij_type_A2_odd import RCToKRTBijectionTypeA2Odd + from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations + from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition, RiggedPartitionTypeB + + # Convert to a type A_{2n-1}^{(2)} RC + RC = RiggedConfigurations(['A', 2*self.n-1, 2], self.cur_dims) + if verbose: + print("====================") + print(repr(RC(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + print("Applying doubling map\n") + # Convert the n-th partition into a regular rigged partition + self.cur_partitions[-1] = RiggedPartition(self.cur_partitions[-1]._list, + self.cur_partitions[-1].rigging, + self.cur_partitions[-1].vacancy_numbers) + + bij = RCToKRTBijectionTypeA2Odd(RC(*self.cur_partitions)) + for i in range(len(self.cur_dims)): + if bij.cur_dims[i][0] != self.n: + bij.cur_dims[i][1] *= 2 + for i in range(self.n-1): + for j in range(len(bij.cur_partitions[i])): + bij.cur_partitions[i]._list[j] *= 2 + bij.cur_partitions[i].rigging[j] *= 2 + bij.cur_partitions[i].vacancy_numbers[j] *= 2 + + # Perform the type A_{2n-1}^{(2)} bijection + + # Iterate over each column + for dummy_var in range(dim[1]): + # Split off a new column if necessary + if bij.cur_dims[0][1] > 1: + bij.cur_dims[0][1] -= 1 + bij.cur_dims.insert(0, [dim[0], 1]) + + # Perform the corresponding splitting map on rigged configurations + # All it does is update the vacancy numbers on the RC side + for a in range(self.n): + bij._update_vacancy_numbers(a) + + while bij.cur_dims[0][0] > 0: + if verbose: + print("====================") + print(repr(RC(*bij.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + + bij.cur_dims[0][0] -= 1 # This takes care of the indexing + b = bij.next_state(bij.cur_dims[0][0]) + # Make sure we have a crystal letter + ret_crystal_path[-1].append(letters(b)) # Append the rank + + bij.cur_dims.pop(0) # Pop off the leading column + + self.cur_dims.pop(0) # Pop off the spin rectangle + + self.cur_partitions = bij.cur_partitions + # Convert the n-th partition back into the special type B one + self.cur_partitions[-1] = RiggedPartitionTypeB(self.cur_partitions[-1]) + + # Convert back to a type B_n^{(1)} + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*bij.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + print("Applying halving map\n") + + for i in range(self.n-1): + for j in range(len(self.cur_partitions[i])): + self.cur_partitions[i]._list[j] //= 2 + self.cur_partitions[i].rigging[j] //= 2 + self.cur_partitions[i].vacancy_numbers[j] //= 2 + else: + # Perform the regular type B_n^{(1)} bijection + + # Iterate over each column + for dummy_var in range(dim[1]): + # Split off a new column if necessary + if self.cur_dims[0][1] > 1: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + print("Applying column split") + + self.cur_dims[0][1] -= 1 + self.cur_dims.insert(0, [dim[0], 1]) + + # Perform the corresponding splitting map on rigged configurations + # All it does is update the vacancy numbers on the RC side + for a in range(self.n): + self._update_vacancy_numbers(a) + + while self.cur_dims[0][0] > 0: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + + self.cur_dims[0][0] -= 1 # This takes care of the indexing + b = self.next_state(self.cur_dims[0][0]) + + # Make sure we have a crystal letter + ret_crystal_path[-1].append(letters(b)) # Append the rank + + self.cur_dims.pop(0) # Pop off the leading column + + return self.KRT(pathlist=ret_crystal_path) + + def next_state(self, height): + r""" + Build the next state for type `B_n^{(1)}`. + + TESTS:: + + sage: RC = RiggedConfigurations(['B', 3, 1], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_B import RCToKRTBijectionTypeB + sage: bijection = RCToKRTBijectionTypeB(RC(partition_list=[[1],[1,1],[1]])) + sage: bijection.next_state(0) + 0 + """ + n = self.n + ell = [None] * (2*n) + case_S = False + case_Q = False + b = None + + # Calculate the rank and ell values + + last_size = 0 + for a in range(height, n-1): + ell[a] = self._find_singular_string(self.cur_partitions[a], last_size) + + if ell[a] is None: + b = a + 1 + break + else: + last_size = self.cur_partitions[a][ell[a]] + + # Special case for n + if b is None: + last_size = 2 * last_size - 1 + partition = self.cur_partitions[n-1] + # Modified version of _find_singular_string() + for i in reversed(range(len(partition))): + if partition[i] == last_size \ + and partition.vacancy_numbers[i] == partition.rigging[i]: + case_Q = True + ell[n-1] = i + elif partition[i] > last_size: + if not case_Q and partition.vacancy_numbers[i] - 1 == partition.rigging[i]: + case_Q = True + # Check if the block is singular as well + block_size = partition[i] + for j in reversed(range(i)): + if partition[j] != block_size: + break + elif partition.vacancy_numbers[j] == partition.rigging[j]: + case_Q = False + ell[2*n-1] = j + last_size = partition[j] + case_S = True + break + if not case_Q: # We found a singular string above the quasi-singular one + break + ell[n-1] = i + last_size = partition[i] + 1 + elif partition.vacancy_numbers[i] == partition.rigging[i]: + ell[2*n-1] = i + last_size = partition[i] + case_S = True + break + + if ell[2*n-1] is None: + if not case_Q: + b = n + else: + b = 0 + + if b is None: + # Now go back + last_size = (last_size + 1) // 2 + for a in reversed(range(n - 1)): + # Modified form of _find_singular_string + end = ell[a] + if a < height: + end = len(self.cur_partitions[a]) + for i in reversed(range(0, end)): + if self.cur_partitions[a][i] >= last_size and \ + self.cur_partitions[a].vacancy_numbers[i] == self.cur_partitions[a].rigging[i]: + ell[n + a] = i + break + + if ell[n + a] is None: + b = -(a + 2) + break + else: + last_size = self.cur_partitions[a][ell[n + a]] + + if b is None: + b = -1 + + # Determine the new rigged configuration by removing boxes from the + # selected string and then making the new string singular + + # Determine if we need to make the n-th string quasisingular + make_quasisingular = case_Q and case_S and \ + (ell[2*n-2] is None + or self.cur_partitions[n-1][ell[2*n-1]] + < 2*self.cur_partitions[n-2][ell[2*n-2]]) + + row_num = self.cur_partitions[0].remove_cell(ell[0]) + row_num_bar = self.cur_partitions[0].remove_cell(ell[n]) + for a in range(1, n-1): + row_num_next = self.cur_partitions[a].remove_cell(ell[a]) + row_num_bar_next = self.cur_partitions[a].remove_cell(ell[n+a]) + + self._update_vacancy_numbers(a - 1) + if row_num is not None: + self.cur_partitions[a-1].rigging[row_num] = self.cur_partitions[a-1].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[a-1].rigging[row_num_bar] = self.cur_partitions[a-1].vacancy_numbers[row_num_bar] + row_num = row_num_next + row_num_bar = row_num_bar_next + + if case_Q: + if case_S: + row_num_next = self.cur_partitions[n-1].remove_cell(ell[n-1]) + row_num_bar_next = self.cur_partitions[n-1].remove_cell(ell[2*n-1]) + else: + row_num_next = self.cur_partitions[n-1].remove_cell(ell[n-1]) + row_num_bar_next = None + elif case_S: + row_num_next = self.cur_partitions[n-1].remove_cell(ell[2*n-1], 2) + row_num_bar_next = None + else: + row_num_next = None + row_num_bar_next = None + + self._update_vacancy_numbers(n - 2) + if row_num is not None: + self.cur_partitions[n-2].rigging[row_num] = self.cur_partitions[n-2].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[n-2].rigging[row_num_bar] = self.cur_partitions[n-2].vacancy_numbers[row_num_bar] + + self._update_vacancy_numbers(n - 1) + if row_num_next is not None: + self.cur_partitions[n-1].rigging[row_num_next] = self.cur_partitions[n-1].vacancy_numbers[row_num_next] + if row_num_bar_next is not None: # If we enter here, it means case (Q, S) holds + vac_num = self.cur_partitions[n-1].vacancy_numbers[row_num_bar_next] + self.cur_partitions[n-1].rigging[row_num_bar_next] = vac_num + if make_quasisingular: + block_len = self.cur_partitions[n-1][row_num_bar_next] + j = row_num_bar_next + 1 + length = len(self.cur_partitions[n-1]) + # Find the place for the quasisingular rigging + while j < length and self.cur_partitions[n-1][j] == block_len \ + and self.cur_partitions[n-1].rigging[j] == vac_num: + j += 1 + self.cur_partitions[n-1].rigging[j-1] = vac_num - 1 + + return(b) + diff --git a/src/sage/combinat/rigged_configurations/bij_type_C.py b/src/sage/combinat/rigged_configurations/bij_type_C.py new file mode 100644 index 00000000000..84b0c7666c6 --- /dev/null +++ b/src/sage/combinat/rigged_configurations/bij_type_C.py @@ -0,0 +1,262 @@ +r""" +Bijection classes for type `C_n^{(1)}`. + +Part of the (internal) classes which runs the bijection between rigged +configurations and KR tableaux of type `C_n^{(1)}`. + +AUTHORS: + +- Travis Scrimshaw (2012-12-21): Initial version + +TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['C', 3, 1], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_C import KRTToRCBijectionTypeC + sage: bijection = KRTToRCBijectionTypeC(KRT(pathlist=[[-1,2]])) + sage: TestSuite(bijection).run() + sage: RC = RiggedConfigurations(['C', 3, 1], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_C import RCToKRTBijectionTypeC + sage: bijection = RCToKRTBijectionTypeC(RC(partition_list=[[],[],[]])) + sage: TestSuite(bijection).run() +""" + +#***************************************************************************** +# Copyright (C) 2012 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.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA +from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA + +class KRTToRCBijectionTypeC(KRTToRCBijectionTypeA): + r""" + Specific implementation of the bijection from KR tableaux to rigged + configurations for type `C_n^{(1)}`. + + This inherits from type `A_n^{(1)}` because we use the same methods in + some places. + """ + + def next_state(self, val): + r""" + Build the next state for type `C_n^{(1)}`. + + TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['C', 3, 1], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_C import KRTToRCBijectionTypeC + sage: bijection = KRTToRCBijectionTypeC(KRT(pathlist=[[-1,2]])) + sage: bijection.cur_path.insert(0, []) + sage: bijection.cur_dims.insert(0, [0, 1]) + sage: bijection.cur_path[0].insert(0, [2]) + sage: bijection.next_state(2) + """ + # Note that we must subtract 1 from n to match the indices. + n = self.n + tableau_height = len(self.cur_path[0]) - 1 + + # If it is a regular value, we follow the A_n rules + if val > 0: + KRTToRCBijectionTypeA.next_state(self, val) + return + + pos_val = -val + case_S = [None] * n + + # Always add a cell to the first singular value in the first + # tableau we are updating. + if len(self.ret_rig_con[pos_val - 1]) > 0: + max_width = self.ret_rig_con[pos_val - 1][0] + else: + max_width = 1 + + # Special case for inserting -n + if pos_val == n: + max_width *= 2 + + # Add cells similar to type A_n but we move to the right until we + # reach the value of n + for a in range(pos_val - 1, n - 1): + max_width = self.ret_rig_con[a].insert_cell(max_width) + case_S[a] = max_width + + # Special case for n + max_width = self.ret_rig_con[n-1].insert_cell(max_width // 2) * 2 + + # Now go back following the special C_n rules + for a in reversed(range(tableau_height, n - 1)): + if case_S[a] == max_width: + self._insert_cell_case_S(self.ret_rig_con[a]) + else: + max_width = self.ret_rig_con[a].insert_cell(max_width) + self._update_vacancy_nums(a + 1) + self._update_partition_values(a + 1) + + # Update the final rigged partitions + if tableau_height < n: + self._update_vacancy_nums(tableau_height) + self._update_partition_values(tableau_height) + + if pos_val <= tableau_height: + for a in range(pos_val-1, tableau_height): + self._update_vacancy_nums(a) + self._update_partition_values(a) + if pos_val > 1: + self._update_vacancy_nums(pos_val - 2) + self._update_partition_values(pos_val - 2) + elif tableau_height > 0: + self._update_vacancy_nums(tableau_height - 1) + self._update_partition_values(tableau_height - 1) + + def _insert_cell_case_S(self, partition): + """ + Insert a cell when case `(S)` holds. + + TESTS:: + + sage: RC = RiggedConfigurations(['C', 2, 1], [[2, 2]]) + sage: RP = RC(partition_list=[[2],[2,2]])[1] + sage: RP + -4[ ][ ]-4 + -4[ ][ ]-4 + + sage: RP.rigging[0] = None + sage: from sage.combinat.rigged_configurations.bij_type_C import KRTToRCBijectionTypeC + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['C', 3, 1], [[2,1]]) + sage: bijection = KRTToRCBijectionTypeC(KRT(pathlist=[[-1,2]])) + sage: bijection._insert_cell_case_S(RP) + sage: RP + -4[ ][ ][ ]None + -4[ ][ ]-4 + + """ + # Special case when adding twice to the first row + if partition.rigging[0] is None: + partition._list[0] += 1 + return + + num_rows = len(partition) + for i in reversed(range(1, num_rows)): + if partition.rigging[i] is None: + j = i - 1 + while j >= 0 and partition._list[j] == partition._list[i]: + partition.rigging[j+1] = partition.rigging[j] # Shuffle it along + j -= 1 + partition._list[j+1] += 1 + partition.rigging[j+1] = None + return + +class RCToKRTBijectionTypeC(RCToKRTBijectionTypeA): + r""" + Specific implementation of the bijection from rigged configurations to + tensor products of KR tableaux for type `C_n^{(1)}`. + """ + + def next_state(self, height): + r""" + Build the next state for type `C_n^{(1)}`. + + TESTS:: + + sage: RC = RiggedConfigurations(['C', 3, 1], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_C import RCToKRTBijectionTypeC + sage: bijection = RCToKRTBijectionTypeC(RC(partition_list=[[2],[2],[1]])) + sage: bijection.next_state(0) + -1 + """ + n = self.n + ell = [None] * (2*n) + case_S = [False] * n + b = None + + # Calculate the rank and ell values + + last_size = 0 + for a in range(height, n-1): + ell[a] = self._find_singular_string(self.cur_partitions[a], last_size) + + if ell[a] is None: + b = a + 1 + break + else: + last_size = self.cur_partitions[a][ell[a]] + + # Special case for n + if b is None: + # Since we are dividing by 2, we can use the identity of + # ceiling = floor + remainder + ell[n-1] = self._find_singular_string(self.cur_partitions[n-1], + (last_size // 2) + (last_size % 2)) + + if ell[n-1] is None: + b = n + else: + last_size = self.cur_partitions[n-1][ell[n-1]] * 2 + + if b is None: + # Now go back + ell[2*n-1] = ell[n-1] + case_S[n-1] = True + for a in reversed(range(n-1)): + if a >= height and self.cur_partitions[a][ell[a]] == last_size: + ell[n+a] = ell[a] + case_S[a] = True + else: # note last_size > 1 + ell[n+a] = self._find_singular_string(self.cur_partitions[a], last_size) + + if ell[n + a] is None: + b = -(a + 2) + break + else: + last_size = self.cur_partitions[a][ell[n + a]] + + if b is None: + b = -1 + + # Determine the new rigged configuration by removing boxes from the + # selected string and then making the new string singular + if case_S[0]: + row_num = self.cur_partitions[0].remove_cell(ell[0], 2) + row_num_bar = None + else: + row_num = self.cur_partitions[0].remove_cell(ell[0]) + row_num_bar = self.cur_partitions[0].remove_cell(ell[n]) + for a in range(1, n-1): + if case_S[a]: + row_num_next = self.cur_partitions[a].remove_cell(ell[a], 2) + row_num_bar_next = None + else: + row_num_next = self.cur_partitions[a].remove_cell(ell[a]) + row_num_bar_next = self.cur_partitions[a].remove_cell(ell[n+a]) + + self._update_vacancy_numbers(a - 1) + if row_num is not None: + self.cur_partitions[a-1].rigging[row_num] = self.cur_partitions[a-1].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[a-1].rigging[row_num_bar] = self.cur_partitions[a-1].vacancy_numbers[row_num_bar] + row_num = row_num_next + row_num_bar = row_num_bar_next + + row_num_next = self.cur_partitions[n-1].remove_cell(ell[n-1]) + + self._update_vacancy_numbers(n - 2) + if row_num is not None: + self.cur_partitions[n-2].rigging[row_num] = self.cur_partitions[n-2].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[n-2].rigging[row_num_bar] = self.cur_partitions[n-2].vacancy_numbers[row_num_bar] + + self._update_vacancy_numbers(n - 1) + if row_num_next is not None: + self.cur_partitions[n-1].rigging[row_num_next] = self.cur_partitions[n-1].vacancy_numbers[row_num_next] + + return(b) diff --git a/src/sage/combinat/rigged_configurations/bij_type_D.py b/src/sage/combinat/rigged_configurations/bij_type_D.py index 55399f6c44c..dad695cd844 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_D.py +++ b/src/sage/combinat/rigged_configurations/bij_type_D.py @@ -12,7 +12,11 @@ sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1]]) sage: from sage.combinat.rigged_configurations.bij_type_D import KRTToRCBijectionTypeD - sage: bijection = KRTToRCBijectionTypeD(KRT(pathlist=[[-2, 3]])) + sage: bijection = KRTToRCBijectionTypeD(KRT(pathlist=[[3, 2]])) + sage: TestSuite(bijection).run() + sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_D import RCToKRTBijectionTypeD + sage: bijection = RCToKRTBijectionTypeD(RC(partition_list=[[],[],[],[]])) sage: TestSuite(bijection).run() """ @@ -36,12 +40,107 @@ class KRTToRCBijectionTypeD(KRTToRCBijectionTypeA): r""" - Specific implementation of the bijection from KR tableaux to rigged configurations for type `D_n^{(1)}`. + Specific implementation of the bijection from KR tableaux to rigged + configurations for type `D_n^{(1)}`. This inherits from type `A_n^{(1)}` because we use the same methods in some places. """ + def run(self, verbose=False): + """ + Run the bijection from a tensor product of KR tableaux to a rigged + configuration for type `D_n^{(1)}`. + + INPUT: + + - ``tp_krt`` -- A tensor product of KR tableaux + + - ``verbose`` -- (Default: ``False``) Display each step in the + bijection + + EXAMPLES:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_D import KRTToRCBijectionTypeD + sage: KRTToRCBijectionTypeD(KRT(pathlist=[[-3,2]])).run() + + -1[ ]-1 + + 2[ ]2 + + -1[ ]-1 + + -1[ ]-1 + + """ + if verbose: + from sage.combinat.rigged_configurations.tensor_product_kr_tableaux_element \ + import TensorProductOfKirillovReshetikhinTableauxElement + + for cur_crystal in reversed(self.tp_krt): + r = cur_crystal.parent().r() + # Iterate through the columns + for col_number, cur_column in enumerate(reversed(cur_crystal.to_array(False))): + self.cur_path.insert(0, []) # Prepend an empty list + + # Check to see if we are a spinor column + if r >= self.n-1: + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + print("Applying doubling map") + self.doubling_map() + + self.cur_dims.insert(0, [0, 1]) + + for letter in reversed(cur_column): + # This check is needed for the n-1 spin column + if self.cur_dims[0][0] < r: + self.cur_dims[0][0] += 1 + val = letter.value # Convert from a CrystalOfLetter to an Integer + + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + + # Build the next state + self.cur_path[0].insert(0, [letter]) # Prepend the value + self.next_state(val) + + # Check to see if we are a spinor column + if r >= self.n-1: + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + print("Applying halving map") + self.halving_map() + + # If we've split off a column, we need to merge the current column + # to the current crystal tableau + if col_number > 0: + for i, letter_singleton in enumerate(self.cur_path[0]): + self.cur_path[1][i].insert(0, letter_singleton[0]) + self.cur_dims[1][1] += 1 + self.cur_path.pop(0) + self.cur_dims.pop(0) + + # And perform the inverse column splitting map on the RC + for a in range(self.n): + self._update_vacancy_nums(a) + + self.ret_rig_con.set_immutable() # Return it to immutable + return self.ret_rig_con + def next_state(self, val): r""" Build the next state for type `D_n^{(1)}`. @@ -50,27 +149,17 @@ def next_state(self, val): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1]]) sage: from sage.combinat.rigged_configurations.bij_type_D import KRTToRCBijectionTypeD - sage: bijection = KRTToRCBijectionTypeD(KRT(pathlist=[[-2, 3]])) + sage: bijection = KRTToRCBijectionTypeD(KRT(pathlist=[[5,3]])) sage: bijection.cur_path.insert(0, []) sage: bijection.cur_dims.insert(0, [0, 1]) sage: bijection.cur_path[0].insert(0, [3]) sage: bijection.next_state(3) - sage: bijection.ret_rig_con - - -1[ ]-1 - - -1[ ]-1 - - (/) - - (/) - """ # Note that type D_n only contains the (absolute) values between 1 and n # (unlike type A_n which is 1 to n+1). Thus we only need to subtract 1 # to match the indices. # Also note that we must subtract 1 from n to match the indices as well. - n = self.ret_rig_con.parent()._cartan_type.n + n = self.n tableau_height = len(self.cur_path[0]) - 1 # If it is a regular value, we follow the A_n rules @@ -85,24 +174,24 @@ def next_state(self, val): self._correct_vacancy_nums() return - posVal = -val + pos_val = -val - if posVal == n: + if pos_val == n: # Special case for `\overline{n}` and adding to make height `n` # This only occurs with `r = n - 1` if self.cur_dims[0][0] == n - 1 and tableau_height == n - 1: return if len(self.ret_rig_con[n - 1]) > 0: - maxWidth = self.ret_rig_con[n - 1][0] + 1 + max_width = self.ret_rig_con[n - 1][0] + 1 else: - maxWidth = 1 + max_width = 1 # Update the last one and skip a rigged partition - maxWidth = self.ret_rig_con[n - 1].insert_cell(maxWidth) + max_width = self.ret_rig_con[n - 1].insert_cell(max_width) for a in reversed(range(tableau_height, n - 2)): - maxWidth = self.ret_rig_con[a].insert_cell(maxWidth) + max_width = self.ret_rig_con[a].insert_cell(max_width) self._update_vacancy_nums(a + 1) self._update_partition_values(a + 1) @@ -125,33 +214,33 @@ def next_state(self, val): # Always add a cell to the first singular value in the first # tableau we are updating. - if len(self.ret_rig_con[posVal - 1]) > 0: - maxWidth = self.ret_rig_con[posVal - 1][0] + 1 + if len(self.ret_rig_con[pos_val - 1]) > 0: + max_width = self.ret_rig_con[pos_val - 1][0] + 1 else: - maxWidth = 1 + max_width = 1 # Special case for `\overline{n-1}` to take the larger of the last two - if posVal == n - 1 and len(self.ret_rig_con[n - 1]) > 0 and \ - self.ret_rig_con[n - 1][0] + 1 > maxWidth: - maxWidth = self.ret_rig_con[n - 1][0] + 1 + if pos_val == n - 1 and len(self.ret_rig_con[n - 1]) > 0 and \ + self.ret_rig_con[n - 1][0] + 1 > max_width: + max_width = self.ret_rig_con[n - 1][0] + 1 # Add cells similar to type A_n but we move to the right until we reach # the value of n-2 - for a in range(posVal - 1, n - 2): - maxWidth = self.ret_rig_con[a].insert_cell(maxWidth) + for a in range(pos_val - 1, n - 2): + max_width = self.ret_rig_con[a].insert_cell(max_width) # Handle the special behavior near values of n if tableau_height <= n - 2: - maxWidth2 = self.ret_rig_con[n - 2].insert_cell(maxWidth) - maxWidth = self.ret_rig_con[n - 1].insert_cell(maxWidth) - if maxWidth2 < maxWidth: - maxWidth = maxWidth2 - elif posVal <= self.cur_dims[0][0]: + max_width2 = self.ret_rig_con[n - 2].insert_cell(max_width) + max_width = self.ret_rig_con[n - 1].insert_cell(max_width) + if max_width2 < max_width: + max_width = max_width2 + elif pos_val <= self.cur_dims[0][0]: # Special case when the height will become n - maxWidth = self.ret_rig_con[self.cur_dims[0][0] - 1].insert_cell(maxWidth) + max_width = self.ret_rig_con[self.cur_dims[0][0] - 1].insert_cell(max_width) # Go back following the regular A_n rules if tableau_height <= n - 3: - maxWidth = self.ret_rig_con[n - 3].insert_cell(maxWidth) + max_width = self.ret_rig_con[n - 3].insert_cell(max_width) self._update_vacancy_nums(n - 2) self._update_vacancy_nums(n - 1) @@ -161,7 +250,7 @@ def next_state(self, val): self._update_partition_values(n - 1) for a in reversed(range(tableau_height, n - 3)): - maxWidth = self.ret_rig_con[a].insert_cell(maxWidth) + max_width = self.ret_rig_con[a].insert_cell(max_width) self._update_vacancy_nums(a + 1) self._update_partition_values(a + 1) @@ -170,23 +259,23 @@ def next_state(self, val): self._update_vacancy_nums(tableau_height) self._update_partition_values(tableau_height) - if posVal < tableau_height: - for a in range(posVal - 1, tableau_height): + if pos_val <= tableau_height: + for a in range(pos_val - 1, tableau_height): self._update_vacancy_nums(a) self._update_partition_values(a) - if posVal > 1: - self._update_vacancy_nums(posVal-2) - self._update_partition_values(posVal-2) + if pos_val > 1: + self._update_vacancy_nums(pos_val-2) + self._update_partition_values(pos_val-2) elif 0 < tableau_height: self._update_vacancy_nums(tableau_height - 1) self._update_partition_values(tableau_height - 1) - elif posVal <= n - 1: - for a in range(posVal - 1, n - 2): + elif pos_val <= n - 1: + for a in range(pos_val - 1, n - 2): self._update_vacancy_nums(a) self._update_partition_values(a) - if posVal > 1: - self._update_vacancy_nums(posVal-2) - self._update_partition_values(posVal-2) + if pos_val > 1: + self._update_vacancy_nums(pos_val-2) + self._update_partition_values(pos_val-2) def _correct_vacancy_nums(self): r""" @@ -205,7 +294,7 @@ def _correct_vacancy_nums(self): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1]]) sage: from sage.combinat.rigged_configurations.bij_type_D import KRTToRCBijectionTypeD - sage: bijection = KRTToRCBijectionTypeD(KRT(pathlist=[[-1,-4,3,2]])) + sage: bijection = KRTToRCBijectionTypeD(KRT(pathlist=[[-1,4,3,2]])) sage: bijection.doubling_map() sage: bijection.cur_path.insert(0, []) sage: bijection.cur_dims.insert(0, [0, 1]) @@ -223,7 +312,7 @@ def _correct_vacancy_nums(self): """ - pos = self.ret_rig_con.parent()._cartan_type.n - 2 + pos = self.n - 2 if self.cur_dims[0][0] == len(self.cur_path[0]): # The current r value is never greater than the height of the current column # Thus if we do not enter into this if block, then r < height and @@ -246,7 +335,7 @@ def doubling_map(self): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[4,1]]) sage: from sage.combinat.rigged_configurations.bij_type_D import KRTToRCBijectionTypeD - sage: bijection = KRTToRCBijectionTypeD(KRT(pathlist=[[-1,-4,3,2]])) + sage: bijection = KRTToRCBijectionTypeD(KRT(pathlist=[[-1,4,3,2]])) sage: bijection.cur_path.insert(0, []) sage: bijection.cur_dims.insert(0, [0, 1]) sage: bijection.cur_path[0].insert(0, [2]) @@ -298,7 +387,7 @@ def halving_map(self): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[4,1]]) sage: from sage.combinat.rigged_configurations.bij_type_D import KRTToRCBijectionTypeD - sage: bijection = KRTToRCBijectionTypeD(KRT(pathlist=[[-1,-4,3,2]])) + sage: bijection = KRTToRCBijectionTypeD(KRT(pathlist=[[-1,4,3,2]])) sage: bijection.cur_path.insert(0, []) sage: bijection.cur_dims.insert(0, [0, 1]) sage: bijection.cur_path[0].insert(0, [2]) @@ -322,6 +411,101 @@ class RCToKRTBijectionTypeD(RCToKRTBijectionTypeA): r""" Specific implementation of the bijection from rigged configurations to tensor products of KR tableaux for type `D_n^{(1)}`. """ + def run(self, verbose=False): + """ + Run the bijection from rigged configurations to tensor product of KR + tableaux for type `D_n^{(1)}`. + + INPUT: + + - ``verbose`` -- (Default: ``False``) Display each step in the + bijection + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_D import RCToKRTBijectionTypeD + sage: RCToKRTBijectionTypeD(RC(partition_list=[[1],[1],[1],[1]])).run() + [[2], [-3]] + """ + from sage.combinat.crystals.letters import CrystalOfLetters + letters = CrystalOfLetters(self.rigged_con.parent()._cartan_type.classical()) + + # This is technically bad, but because the first thing we do is append + # an empty list to ret_crystal_path, we correct this. We do it this + # way so that we do not have to remove an empty list after the + # bijection has been performed. + ret_crystal_path = [] + + for dim in self.rigged_con.parent().dims: + ret_crystal_path.append([]) + + # Iterate over each column + for dummy_var in range(dim[1]): + # Split off a new column if necessary + if self.cur_dims[0][1] > 1: + self.cur_dims[0][1] -= 1 + self.cur_dims.insert(0, [dim[0], 1]) + + # Perform the corresponding splitting map on rigged configurations + # All it does is update the vacancy numbers on the RC side + for a in range(self.n): + self._update_vacancy_numbers(a) + + # Check to see if we are a spinor + if dim[0] >= self.n - 1: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + print("Applying doubling map") + self.doubling_map() + if dim[0] == self.n - 1: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + b = self.next_state(self.n) + if b == self.n: + b = -self.n + ret_crystal_path[-1].append(letters(b)) # Append the rank + + while self.cur_dims[0][0] > 0: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + + self.cur_dims[0][0] -= 1 # This takes care of the indexing + b = self.next_state(self.cur_dims[0][0]) + + # Corrections for spinor + if dim[0] == self.n and b == -self.n \ + and self.cur_dims[0][0] == self.n - 1: + b = -(self.n-1) + + # Make sure we have a crystal letter + ret_crystal_path[-1].append(letters(b)) # Append the rank + + self.cur_dims.pop(0) # Pop off the leading column + + # Check to see if we were a spinor + if dim[0] >= self.n-1: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + print("Applying halving map") + self.halving_map() + return self.KRT(pathlist=ret_crystal_path) def next_state(self, height): r""" @@ -332,16 +516,10 @@ def next_state(self, height): sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) sage: from sage.combinat.rigged_configurations.bij_type_D import RCToKRTBijectionTypeD sage: bijection = RCToKRTBijectionTypeD(RC(partition_list=[[],[1,1],[1],[1]])) - sage: bijection.next_state(1) - -2 - sage: bijection.cur_partitions - [(/) - , (/) - , (/) - , (/) - ] + sage: bijection.next_state(0) + 1 """ - n = self.rigged_con.parent()._cartan_type.n + n = self.n ell = [None] * (2 * n - 2) # No `\bar{\ell}^{n-1}` and `\bar{\ell}^n` b = None @@ -392,7 +570,7 @@ def next_state(self, height): if b is None: # Now go back - for a in reversed(range(0, n - 2)): + for a in reversed(range(n - 2)): # Modified form of _find_singular_string end = ell[a] if a < height: @@ -400,8 +578,8 @@ def next_state(self, height): for i in reversed(range(0, end)): if self.cur_partitions[a][i] >= last_size and \ self.cur_partitions[a].vacancy_numbers[i] == self.cur_partitions[a].rigging[i]: - ell[n + a] = i - break + ell[n + a] = i + break if ell[n + a] is None: b = -(a + 2) @@ -544,6 +722,7 @@ def _correct_vacancy_nums(self): sage: bijection.next_state(4) # indirect doctest -4 """ - n = self.rigged_con.parent()._cartan_type.n + n = self.n for i in range(len(self.cur_partitions[n-1]._list)): self.cur_partitions[n-1].vacancy_numbers[i] += 1 + diff --git a/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py b/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py new file mode 100644 index 00000000000..1afefb49fa1 --- /dev/null +++ b/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py @@ -0,0 +1,538 @@ +r""" +Bijection classes for type `D_{n+1}^{(2)}`. + +Part of the (internal) classes which runs the bijection between rigged +configurations and KR tableaux of type `D_{n+1}^{(2)}`. + +AUTHORS: + +- Travis Scrimshaw (2011-04-15): Initial version + +TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 2], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_D_twisted import KRTToRCBijectionTypeDTwisted + sage: bijection = KRTToRCBijectionTypeDTwisted(KRT(pathlist=[[-1,2]])) + sage: TestSuite(bijection).run() + sage: RC = RiggedConfigurations(['D', 4, 2], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_D_twisted import RCToKRTBijectionTypeDTwisted + sage: bijection = RCToKRTBijectionTypeDTwisted(RC()) + sage: TestSuite(bijection).run() +""" + +#***************************************************************************** +# Copyright (C) 2011, 2012 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.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA +from sage.combinat.rigged_configurations.bij_type_A2_even import KRTToRCBijectionTypeA2Even +from sage.combinat.rigged_configurations.bij_type_A2_even import RCToKRTBijectionTypeA2Even +from sage.combinat.rigged_configurations.bij_type_D import KRTToRCBijectionTypeD +from sage.combinat.rigged_configurations.bij_type_D import RCToKRTBijectionTypeD + +class KRTToRCBijectionTypeDTwisted(KRTToRCBijectionTypeD, KRTToRCBijectionTypeA2Even): + r""" + Specific implementation of the bijection from KR tableaux to rigged + configurations for type `D_{n+1}^{(2)}`. + + This inherits from type `C_n^{(1)}` and `D_n^{(1)}` because we use the + same methods in some places. + """ + def run(self, verbose=False): + """ + Run the bijection from a tensor product of KR tableaux to a rigged + configuration for type `D_{n+1}^{(2)}`. + + INPUT: + + - ``tp_krt`` -- A tensor product of KR tableaux + + - ``verbose`` -- (Default: ``False``) Display each step in the + bijection + + EXAMPLES:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 2], [[3,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_D_twisted import KRTToRCBijectionTypeDTwisted + sage: KRTToRCBijectionTypeDTwisted(KRT(pathlist=[[-1,3,2]])).run() + + -1[ ]-1 + + 0[ ]0 + + 1[ ]1 + + """ + if verbose: + from sage.combinat.rigged_configurations.tensor_product_kr_tableaux_element \ + import TensorProductOfKirillovReshetikhinTableauxElement + + for cur_crystal in reversed(self.tp_krt): + r = cur_crystal.parent().r() + # Iterate through the columns + for col_number, cur_column in enumerate(reversed(cur_crystal.to_array(False))): + self.cur_path.insert(0, []) # Prepend an empty list + + # Check to see if we are a spinor column + if r == self.n: + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + print("Applying doubling map") + self.doubling_map() + + self.cur_dims.insert(0, [0, 1]) + + for letter in reversed(cur_column): + self.cur_dims[0][0] += 1 + val = letter.value # Convert from a CrystalOfLetter to an Integer + + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + + # Build the next state + self.cur_path[0].insert(0, [letter]) # Prepend the value + self.next_state(val) + + # Check to see if we are a spinor column + if r == self.n: + if verbose: + print("====================") + print(repr(TensorProductOfKirillovReshetikhinTableauxElement(self.tp_krt.parent(), self.cur_path))) + print("--------------------") + print(repr(self.ret_rig_con)) + print("--------------------\n") + print("Applying halving map") + self.halving_map() + + # If we've split off a column, we need to merge the current column + # to the current crystal tableau + if col_number > 0: + for i, letter_singleton in enumerate(self.cur_path[0]): + self.cur_path[1][i].insert(0, letter_singleton[0]) + self.cur_dims[1][1] += 1 + self.cur_path.pop(0) + self.cur_dims.pop(0) + + # And perform the inverse column splitting map on the RC + for a in range(self.n): + self._update_vacancy_nums(a) + + self.ret_rig_con.set_immutable() # Return it to immutable + return self.ret_rig_con + + def next_state(self, val): + r""" + Build the next state for type `D_{n+1}^{(2)}`. + + TESTS:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 2], [[2,1]]) + sage: from sage.combinat.rigged_configurations.bij_type_D_twisted import KRTToRCBijectionTypeDTwisted + sage: bijection = KRTToRCBijectionTypeDTwisted(KRT(pathlist=[[-1,2]])) + sage: bijection.cur_path.insert(0, []) + sage: bijection.cur_dims.insert(0, [0, 1]) + sage: bijection.cur_path[0].insert(0, [2]) + sage: bijection.next_state(2) + """ + n = self.n + tableau_height = len(self.cur_path[0]) - 1 + + if val == 'E': + KRTToRCBijectionTypeA2Even.next_state(self, val) + return + elif val > 0: + # If it is a regular value, we follow the A_n rules + KRTToRCBijectionTypeA.next_state(self, val) + if tableau_height >= n - 1: + self._correct_vacancy_nums() + return + + pos_val = -val + + if pos_val == 0: + if len(self.ret_rig_con[pos_val - 1]) > 0: + max_width = self.ret_rig_con[n-1][0] + else: + max_width = 1 + max_width = self.ret_rig_con[n-1].insert_cell(max_width) + width_n = max_width + 1 + + # Follow regular A_n rules + for a in reversed(range(tableau_height, n-1)): + max_width = self.ret_rig_con[a].insert_cell(max_width) + self._update_vacancy_nums(a + 1) + self._update_partition_values(a + 1) + self._update_vacancy_nums(tableau_height) + if tableau_height >= n - 1: + self._correct_vacancy_nums() + self._update_partition_values(tableau_height) + if tableau_height > 0: + self._update_vacancy_nums(tableau_height-1) + self._update_partition_values(tableau_height-1) + + # Make the new string at n quasi-singular + p = self.ret_rig_con[n-1] + num_rows = len(p) + for i in range(num_rows): + if p._list[i] == width_n: + j = i+1 + while j < num_rows and p._list[j] == width_n \ + and p.vacancy_numbers[j] == p.rigging[j]: + j += 1 + p.rigging[j-1] -= 1 + break + return + + case_S = [None] * n + pos_val = -val + + # Always add a cell to the first singular value in the first + # tableau we are updating. + if len(self.ret_rig_con[pos_val - 1]) > 0: + max_width = self.ret_rig_con[pos_val - 1][0] + else: + max_width = 1 + + # Add cells similar to type A_n but we move to the right until we + # reach the value of n-1 + for a in range(pos_val - 1, n-1): + max_width = self.ret_rig_con[a].insert_cell(max_width) + case_S[a] = max_width + + # Special case for n + # If we find a quasi-singular string first, then we are in case (Q, S) + # otherwise we will find a singular string and insert 2 cells + partition = self.ret_rig_con[n-1] + num_rows = len(partition) + case_QS = False + for i in range(num_rows + 1): + if i == num_rows: + max_width = 0 + if case_QS: + partition._list.append(1) + partition.vacancy_numbers.append(None) + # Go through our partition until we find a length of greater than 1 + j = len(partition._list) - 1 + while j >= 0 and partition._list[j] == 1: + j -= 1 + partition.rigging.insert(j + 1, None) + width_n = 1 + else: + # Go through our partition until we find a length of greater than 2 + j = len(partition._list) - 1 + while j >= 0 and partition._list[j] <= 2: + j -= 1 + partition._list.insert(j+1, 2) + partition.vacancy_numbers.insert(j+1, None) + partition.rigging.insert(j+1, None) + break + elif partition._list[i] <= max_width: + if partition.vacancy_numbers[i] == partition.rigging[i]: + max_width = partition._list[i] + if case_QS: + partition._list[i] += 1 + width_n = partition._list[i] + partition.rigging[i] = None + else: + j = i - 1 + while j >= 0 and partition._list[j] <= max_width + 2: + partition.rigging[j+1] = partition.rigging[j] # Shuffle it along + j -= 1 + partition._list.pop(i) + partition._list.insert(j+1, max_width + 2) + partition.rigging[j+1] = None + break + elif partition.vacancy_numbers[i] - 1 == partition.rigging[i] and not case_QS: + case_QS = True + partition._list[i] += 1 + partition.rigging[i] = None + # No need to set max_width here since we will find a singular string + + # Now go back following the regular C_n (ish) rules + for a in reversed(range(tableau_height, n-1)): + if case_S[a] == max_width: + self._insert_cell_case_S(self.ret_rig_con[a]) + else: + max_width = self.ret_rig_con[a].insert_cell(max_width) + self._update_vacancy_nums(a + 1) + self._update_partition_values(a + 1) + + # Update the final rigged partitions + self._update_vacancy_nums(tableau_height) + if tableau_height >= n - 1: + self._correct_vacancy_nums() + self._update_partition_values(tableau_height) + + if pos_val <= tableau_height: + for a in range(pos_val-1, tableau_height): + self._update_vacancy_nums(a) + self._update_partition_values(a) + if pos_val > 1: + self._update_vacancy_nums(pos_val - 2) + self._update_partition_values(pos_val - 2) + elif tableau_height > 0: + self._update_vacancy_nums(tableau_height - 1) + self._update_partition_values(tableau_height - 1) + + if case_QS: + # Make the new string quasi-singular + num_rows = len(partition) + for i in range(num_rows): + if partition._list[i] == width_n: + j = i+1 + while j < num_rows and partition._list[j] == width_n \ + and partition.vacancy_numbers[j] == partition.rigging[j]: + j += 1 + partition.rigging[j-1] -= 1 + break + +class RCToKRTBijectionTypeDTwisted(RCToKRTBijectionTypeD, RCToKRTBijectionTypeA2Even): + r""" + Specific implementation of the bijection from rigged configurations to + tensor products of KR tableaux for type `D_{n+1}^{(2)}`. + """ + def run(self, verbose=False): + """ + Run the bijection from rigged configurations to tensor product of KR + tableaux for type `D_{n+1}^{(2)}`. + + INPUT: + + - ``verbose`` -- (Default: ``False``) Display each step in the + bijection + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['D', 4, 2], [[3, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_D_twisted import RCToKRTBijectionTypeDTwisted + sage: RCToKRTBijectionTypeDTwisted(RC(partition_list=[[],[1],[1]])).run() + [[1], [3], [-2]] + """ + from sage.combinat.crystals.letters import CrystalOfLetters + letters = CrystalOfLetters(self.rigged_con.parent()._cartan_type.classical()) + + # This is technically bad, but because the first thing we do is append + # an empty list to ret_crystal_path, we correct this. We do it this + # way so that we do not have to remove an empty list after the + # bijection has been performed. + ret_crystal_path = [] + + for dim in self.rigged_con.parent().dims: + ret_crystal_path.append([]) + + # Iterate over each column + for dummy_var in range(dim[1]): + # Split off a new column if necessary + if self.cur_dims[0][1] > 1: + self.cur_dims[0][1] -= 1 + self.cur_dims.insert(0, [dim[0], 1]) + + # Perform the corresponding splitting map on rigged configurations + # All it does is update the vacancy numbers on the RC side + for a in range(self.n): + self._update_vacancy_numbers(a) + + # Check to see if we are a spinor + if dim[0] == self.n: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + print("Applying doubling map") + self.doubling_map() + + while self.cur_dims[0][0] > 0: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + + self.cur_dims[0][0] -= 1 # This takes care of the indexing + b = self.next_state(self.cur_dims[0][0]) + + # Make sure we have a crystal letter + ret_crystal_path[-1].append(letters(b)) # Append the rank + + self.cur_dims.pop(0) # Pop off the leading column + + # Check to see if we were a spinor + if dim[0] == self.n: + if verbose: + print("====================") + print(repr(self.rigged_con.parent()(*self.cur_partitions))) + print("--------------------") + print(ret_crystal_path) + print("--------------------\n") + print("Applying halving map") + self.halving_map() + return self.KRT(pathlist=ret_crystal_path) + + def next_state(self, height): + r""" + Build the next state for type `D_{n+1}^{(2)}`. + + TESTS:: + + sage: RC = RiggedConfigurations(['D', 4, 2], [[2, 1]]) + sage: from sage.combinat.rigged_configurations.bij_type_D_twisted import RCToKRTBijectionTypeDTwisted + sage: bijection = RCToKRTBijectionTypeDTwisted(RC(partition_list=[[2],[2,2],[2,2]])) + sage: bijection.next_state(0) + -1 + """ + n = self.n + ell = [None] * (2*n) + case_S = [False] * n + case_Q = False + b = None + + # Calculate the rank and ell values + + last_size = 0 + for a in range(height, n-1): + ell[a] = self._find_singular_string(self.cur_partitions[a], last_size) + + if ell[a] is None: + b = a + 1 + break + else: + last_size = self.cur_partitions[a][ell[a]] + + if b is None: + partition = self.cur_partitions[n-1] + # Modified version of _find_singular_string() + for i in reversed(range(len(partition))): + if partition[i] >= last_size: + if partition.vacancy_numbers[i] == partition.rigging[i]: + if partition[i] == 1: + b = 'E' + else: + last_size = partition[i] + case_S[n-1] = True + ell[2*n-1] = i + break + elif partition.vacancy_numbers[i] - 1 == partition.rigging[i] and not case_Q: + case_Q = True + # Check if it is singular as well + block_size = partition[i] + for j in reversed(range(i)): + if partition[j] != block_size: + break + elif partition.vacancy_numbers[j] == partition.rigging[j]: + case_Q = False + break + if case_Q: + last_size = partition[i] + 1 + ell[n-1] = i + + if ell[2*n-1] is None: + if not case_Q: + b = n + else: + b = 0 + + if b is None: + # Now go back + for a in reversed(range(n-1)): + if a >= height and self.cur_partitions[a][ell[a]] == last_size: + ell[n+a] = ell[a] + case_S[a] = True + else: + ell[n+a] = self._find_singular_string(self.cur_partitions[a], last_size) + + if ell[n + a] is None: + b = -(a + 2) + break + else: + last_size = self.cur_partitions[a][ell[n + a]] + + if b is None: + b = -1 + + # Determine the new rigged configuration by removing boxes from the + # selected string and then making the new string singular + if case_S[0]: + row_num = None + row_num_bar = self.cur_partitions[0].remove_cell(ell[n], 2) + else: + row_num = self.cur_partitions[0].remove_cell(ell[0]) + row_num_bar = self.cur_partitions[0].remove_cell(ell[n]) + for a in range(1, n-1): + if case_S[a]: + row_num_next = None + row_num_bar_next = self.cur_partitions[a].remove_cell(ell[n+a], 2) + else: + row_num_next = self.cur_partitions[a].remove_cell(ell[a]) + row_num_bar_next = self.cur_partitions[a].remove_cell(ell[n+a]) + + self._update_vacancy_numbers(a - 1) + if row_num is not None: + self.cur_partitions[a-1].rigging[row_num] = self.cur_partitions[a-1].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[a-1].rigging[row_num_bar] = self.cur_partitions[a-1].vacancy_numbers[row_num_bar] + row_num = row_num_next + row_num_bar = row_num_bar_next + + if case_Q: + row_num_next = self.cur_partitions[n-1].remove_cell(ell[n-1]) + if case_S[n-1]: + row_num_bar_next = self.cur_partitions[n-1].remove_cell(ell[2*n-1]) + else: + row_num_bar_next = None + elif case_S[n-1]: + row_num_next = None + row_num_bar_next = self.cur_partitions[n-1].remove_cell(ell[2*n-1], 2) + else: + row_num_next = None + row_num_bar_next = None + + self._update_vacancy_numbers(n - 2) + if row_num is not None: + self.cur_partitions[n-2].rigging[row_num] = self.cur_partitions[n-2].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[n-2].rigging[row_num_bar] = self.cur_partitions[n-2].vacancy_numbers[row_num_bar] + + self._update_vacancy_numbers(n - 1) + if height == n: + self._correct_vacancy_nums() + if row_num_next is not None: + self.cur_partitions[n-1].rigging[row_num_next] = self.cur_partitions[n-1].vacancy_numbers[row_num_next] + if row_num_bar_next is not None: + if case_Q: + vac_num = self.cur_partitions[n-1].vacancy_numbers[row_num_bar_next] + self.cur_partitions[n-1].rigging[row_num_bar_next] = vac_num + block_len = self.cur_partitions[n-1][row_num_bar_next] + j = row_num_bar_next + 1 + length = len(self.cur_partitions[n-1]) + # Find the place for the quasisingular rigging + while j < length and self.cur_partitions[n-1][j] == block_len \ + and self.cur_partitions[n-1].rigging[j] == vac_num: + j += 1 + self.cur_partitions[n-1].rigging[j-1] = vac_num - 1 + else: + self.cur_partitions[n-1].rigging[row_num_bar_next] = self.cur_partitions[n-1].vacancy_numbers[row_num_bar_next] + + return(b) + diff --git a/src/sage/combinat/rigged_configurations/bijection.py b/src/sage/combinat/rigged_configurations/bijection.py index 226c49a6978..4af8c98b181 100644 --- a/src/sage/combinat/rigged_configurations/bijection.py +++ b/src/sage/combinat/rigged_configurations/bijection.py @@ -7,6 +7,7 @@ AUTHORS: - Travis Scrimshaw (2011-04-15): Initial version +- Travis Scrimshaw (2012-12-21): Added all non-exceptional bijection types """ #***************************************************************************** @@ -26,10 +27,29 @@ from sage.combinat.rigged_configurations.bij_type_A import KRTToRCBijectionTypeA from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA + +from sage.combinat.rigged_configurations.bij_type_B import KRTToRCBijectionTypeB +from sage.combinat.rigged_configurations.bij_type_B import RCToKRTBijectionTypeB + +from sage.combinat.rigged_configurations.bij_type_C import KRTToRCBijectionTypeC +from sage.combinat.rigged_configurations.bij_type_C import RCToKRTBijectionTypeC + from sage.combinat.rigged_configurations.bij_type_D import KRTToRCBijectionTypeD from sage.combinat.rigged_configurations.bij_type_D import RCToKRTBijectionTypeD -def KRTToRCBijection(krt_elt): +from sage.combinat.rigged_configurations.bij_type_D_twisted import KRTToRCBijectionTypeDTwisted +from sage.combinat.rigged_configurations.bij_type_D_twisted import RCToKRTBijectionTypeDTwisted + +from sage.combinat.rigged_configurations.bij_type_A2_even import KRTToRCBijectionTypeA2Even +from sage.combinat.rigged_configurations.bij_type_A2_even import RCToKRTBijectionTypeA2Even + +from sage.combinat.rigged_configurations.bij_type_A2_dual import KRTToRCBijectionTypeA2Dual +from sage.combinat.rigged_configurations.bij_type_A2_dual import RCToKRTBijectionTypeA2Dual + +from sage.combinat.rigged_configurations.bij_type_A2_odd import KRTToRCBijectionTypeA2Odd +from sage.combinat.rigged_configurations.bij_type_A2_odd import RCToKRTBijectionTypeA2Odd + +def KRTToRCBijection(tp_krt): r""" Return the correct KR tableaux to rigged configuration bijection helper class. @@ -37,29 +57,34 @@ def KRTToRCBijection(krt_elt): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 4, 1], [[2,1]]) sage: from sage.combinat.rigged_configurations.bijection import KRTToRCBijection - sage: bijection = KRTToRCBijection(KRT(pathlist=[[4,3]])) - sage: bijection.cur_path.insert(0, []) - sage: bijection.cur_dims.insert(0, [0, 1]) - sage: bijection.cur_path[0].insert(0, [3]) - sage: bijection.next_state(3) - sage: bijection.ret_rig_con - - -1[ ]-1 - - -1[ ]-1 - - (/) - - (/) - + sage: bijection = KRTToRCBijection(KRT(pathlist=[[5,2]])) """ - ct = krt_elt.cartan_type() - if ct.letter == 'A': - return KRTToRCBijectionTypeA(krt_elt) - elif ct.letter == 'D': - return KRTToRCBijectionTypeD(krt_elt) + ct = tp_krt.cartan_type() + type = ct.type() + if ct.is_untwisted_affine(): + if type == 'A': + return KRTToRCBijectionTypeA(tp_krt) + if type == 'B': + return KRTToRCBijectionTypeB(tp_krt) + if type == 'C': + return KRTToRCBijectionTypeC(tp_krt) + if type == 'D': + return KRTToRCBijectionTypeD(tp_krt) + #if type == 'E': + #if type == 'F': + #if type == 'G': else: - raise NotImplementedError + if type == 'BC': # A_{2n}^{(2)} + return KRTToRCBijectionTypeA2Even(tp_krt) + if ct.dual().type() == 'BC': # A_{2n}^{(2)\dagger} + return KRTToRCBijectionTypeA2Dual(tp_krt) + if ct.dual().type() == 'B': # A_{2n-1}^{(2)} + return KRTToRCBijectionTypeA2Odd(tp_krt) + if ct.dual().type() == 'C': # D_{n+1}^{(2)} + return KRTToRCBijectionTypeDTwisted(tp_krt) + #if ct.dual().type() == 'F': # E_6^{(2)} + #if ct.dual().type() == 'G': # D_4^{(3)} + raise NotImplementedError def RCToKRTBijection(rigged_configuration_elt): r""" @@ -70,19 +95,31 @@ def RCToKRTBijection(rigged_configuration_elt): sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 1]]) sage: from sage.combinat.rigged_configurations.bijection import RCToKRTBijection sage: bijection = RCToKRTBijection(RC(partition_list=[[1],[1],[1],[1]])) - sage: bijection.next_state(0) - 5 - sage: bijection.cur_partitions - [(/) - , (/) - , (/) - , (/) - ] """ ct = rigged_configuration_elt.cartan_type() - if ct.letter == 'A': - return RCToKRTBijectionTypeA(rigged_configuration_elt) - elif ct.letter == 'D': - return RCToKRTBijectionTypeD(rigged_configuration_elt) + type = ct.type() + if not ct.is_affine() or ct.is_untwisted_affine(): + if type == 'A': + return RCToKRTBijectionTypeA(rigged_configuration_elt) + if type == 'B': + return RCToKRTBijectionTypeB(rigged_configuration_elt) + if type == 'C': + return RCToKRTBijectionTypeC(rigged_configuration_elt) + if type == 'D': + return RCToKRTBijectionTypeD(rigged_configuration_elt) + #if type == 'E': + #if type == 'F': + #if type == 'G': else: - raise NotImplementedError + if type == 'BC': # A_{2n}^{(2)} + return RCToKRTBijectionTypeA2Even(rigged_configuration_elt) + if ct.dual().type() == 'BC': # A_{2n}^{(2)\dagger} + return RCToKRTBijectionTypeA2Dual(rigged_configuration_elt) + if ct.dual().type() == 'B': # A_{2n-1}^{(2)} + return RCToKRTBijectionTypeA2Odd(rigged_configuration_elt) + if ct.dual().type() == 'C': # D_{n+1}^{(2)} + return RCToKRTBijectionTypeDTwisted(rigged_configuration_elt) + #if ct.dual().type() == 'F': # E_6^{(2)} + #if ct.dual().type() == 'G': # D_4^{(3)} + raise NotImplementedError + diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index 3f31a7bb563..f95841bad6a 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -1,5 +1,5 @@ r""" -Kirillov-Reshetikhin tableaux +Kirillov-Reshetikhin Tableaux Kirillov-Reshetikhin tableaux are rectangular tableaux with `r` rows and `s` columns that naturally arise under the bijection between rigged @@ -11,7 +11,6 @@ AUTHORS: - Travis Scrimshaw (2012-01-03): Initial version - - Travis Scrimshaw (2012-11-14): Added bijection to KR crystals """ @@ -37,22 +36,21 @@ from sage.misc.abstract_method import abstract_method from sage.misc.flatten import flatten -from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent +from sage.structure.element_wrapper import ElementWrapper from sage.categories.finite_crystals import FiniteCrystals from sage.categories.regular_crystals import RegularCrystals -from sage.categories.classical_crystals import ClassicalCrystals - -from sage.rings.integer import Integer -from sage.combinat.crystals.letters import CrystalOfLetters +from sage.combinat.crystals.letters import CrystalOfLetters, EmptyLetter from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.crystals.tensor_product import CrystalOfWords from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement from sage.combinat.crystals.kirillov_reshetikhin import horizontal_dominoes_removed, \ - KirillovReshetikhinCrystal, KirillovReshetikhinGenericCrystalElement + KirillovReshetikhinCrystal, KirillovReshetikhinGenericCrystalElement, \ + partitions_in_box, vertical_dominoes_removed from sage.combinat.partition import Partition +from sage.combinat.tableau import Tableau class KirillovReshetikhinTableaux(CrystalOfWords): r""" @@ -78,33 +76,47 @@ class KirillovReshetikhinTableaux(CrystalOfWords): In this case, `e_i` and `f_i` act as `e_i^2` and `f_i^2` respectively. See [BijectionDn]_. - .. WARNING:: - - The module generators for all types except `A^{(1)}_n`, `D^{(1)}_n`, - and full rectangles have not been tested, much less proven, to be the - correct output from the bijection. - For more information about the bijection between rigged configurations and tensor products of Kirillov-Reshetikhin tableaux, see :class:`TensorProductOfKirillovReshetikhinTableaux`. + .. NOTE:: + + The tableaux for all non-simply-laced provably holds if the bijection + with :class:`rigged configurations ` holds. + Therefore this is only proven for `B^{r,1}` or `B^{1,s}` and in + general for types `A_n^{(1)}` and `D_n^{(1)}`. + + INPUT: + + - ``cartan_type`` -- The Cartan type + + - ``r`` -- The number of rows + + - ``s`` -- The number of columns + EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 1) - sage: elt = KRT([4, 3]); elt + sage: elt = KRT(4, 3); elt [[3], [4]] sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 2, 1) - sage: elt = KRT([-1, 1]); elt + sage: elt = KRT(-1, 1); elt [[1], [-1]] - We can create highest weight crystals from a given shape:: + We can create highest weight crystals from a given shape or weight:: - sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 2, 1) - sage: KRT(shape=[]) - [[1], [-1]] sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 2, 2) - sage: KRT(shape=[1,1]) + sage: KRT.module_generator(shape=[1,1]) + [[1, 1], [2, -1]] + sage: KRT.module_generator(column_shape=[2]) + [[1, 1], [2, -1]] + sage: WS = RootSystem(['D',4,1]).weight_space() + sage: KRT.module_generator(weight=WS.sum_of_terms([[0,-2],[2,1]])) + [[1, 1], [2, -1]] + sage: WSC = RootSystem(['D',4]).weight_space() + sage: KRT.module_generator(classical_weight=WSC.fundamental_weight(2)) [[1, 1], [2, -1]] We can go between :func:`KirillovReshetikhinCrystal` and @@ -116,10 +128,9 @@ class KirillovReshetikhinTableaux(CrystalOfWords): [[2], [3]] sage: k = KRTab(elt); k [[2, 1], [3, -1]] - sage: k.to_Kirillov_Reshetikhin_crystal() + sage: KRCrys(k) [[2], [3]] """ - @staticmethod def __classcall_private__(cls, cartan_type, r, s): """ @@ -133,37 +144,47 @@ def __classcall_private__(cls, cartan_type, r, s): True """ ct = CartanType(cartan_type) - assert ct.is_affine() + if not ct.is_affine(): + raise ValueError("The Cartan type must be affine") + type = ct.type() if ct.is_untwisted_affine(): - if ct.letter == 'D': - if r == ct.n or r == ct.n - 1: + if type == 'A': + return KRTableauxRectangle(ct, r, s) + if type == 'B': + if r == ct.classical().rank(): + return KRTableauxBn(ct, r, s) + return KRTableauxTypeVertical(ct, r, s) + if type == 'C': + if r == ct.classical().rank(): + return KRTableauxRectangle(ct, r, s) + return KRTableauxTypeC(ct, r, s) + if type == 'D': + if r == ct.classical().rank() or r == ct.classical().rank() - 1: return KRTableauxSpin(ct, r, s) return KRTableauxTypeVertical(ct, r, s) - - if ct.letter == 'B': - if r == ct.n: - return KRTableauxBn(ct, r, s) - return KRTypeVertical(ct, r, s) - - if ct.letter == 'A' or (ct.letter == 'C' and r == ct.n): - return KRTableauxRectangle(ct, r, s) else: - if ct.dual().letter == 'B': + if type == 'BC': # A_{2n}^{(2)} + return KRTableauxTypeBox(ct, r, s) + if ct.dual().type() == 'BC': # A_{2n}^{(2)\dagger} + return KRTableauxTypeC(ct, r, s) # Placeholder + if ct.dual().type() == 'B': # A_{2n-1}^{(2)} return KRTableauxTypeVertical(ct, r, s) - - raise NotImplementedError + if ct.dual().type() == 'C': # D_{n+1}^{(2)} + if r == ct.dual().classical().rank(): + return KRTableauxDTwistedSpin(ct, r, s) + return KRTableauxTypeBox(ct, r, s) + #if ct.dual().letter == 'F': # E_6^{(2)} + #if ct.dual().letter == 'G': # D_4^{(3)} + + #print "We are returning the KR crystal because the requested type is not yet implemented!" + return KRTableauxWrapper(ct, r, s) + #raise NotImplementedError #return super(KirillovReshetikhinTableaux, cls).__classcall__(cls, ct, r, s) def __init__(self, cartan_type, r, s): r""" - Initialize the KirillovReshetikhinTableaux class. - - INPUT: - - - ``cartan_type`` -- The Cartan type - - ``r`` -- The number of rows - - ``s`` -- The number of columns + Initialize ``self``. EXAMPLES:: @@ -179,14 +200,112 @@ def __init__(self, cartan_type, r, s): """ self._r = r self._s = s - Parent.__init__(self, category=(RegularCrystals(), FiniteCrystals())) - self.rename("Kirillov-Reshetikhin tableaux of type %s and shape (%d, %d)" % (cartan_type, r, s)) + self._cartan_type = cartan_type - self._cartan_type = cartan_type.classical() - self.letters = CrystalOfLetters(self._cartan_type) + Parent.__init__(self, category=(RegularCrystals(), FiniteCrystals())) + self.letters = CrystalOfLetters(cartan_type.classical()) self.module_generators = self._build_module_generators() + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: KirillovReshetikhinTableaux(['A', 4, 1], 2, 3) + Kirillov-Reshetikhin tableaux of type ['A', 4, 1] and shape (2, 3) + """ + return "Kirillov-Reshetikhin tableaux of type {} and shape ({}, {})".format( + self._cartan_type, self._r, self._s) + + def __iter__(self): + """ + Return the iterator of ``self``. + + EXAMPLES:: + + sage: KR = KirillovReshetikhinTableaux(['A', 3, 1], 2, 1) + sage: g = KR.__iter__() + sage: g.next() + [[1], [2]] + sage: g.next() + [[1], [3]] + sage: g.next() + [[2], [3]] + """ + index_set = self._cartan_type.classical().index_set() + from sage.combinat.backtrack import TransitiveIdeal + return TransitiveIdeal(lambda x: [x.f(i) for i in index_set], + self.module_generators).__iter__() + + def module_generator(self, i=None, **options): + r""" + Return the specified module generator. + + INPUT: + + - ``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 + `k` correspond to a classical weight `\omega_k`) + - ``weight`` -- The weight + - ``classical_weight`` -- The classical weight + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 2, 2) + sage: KRT.module_generator(1) + [[1, 1], [2, -1]] + sage: KRT.module_generator(shape=[1,1]) + [[1, 1], [2, -1]] + sage: KRT.module_generator(column_shape=[2]) + [[1, 1], [2, -1]] + sage: WS = RootSystem(['D',4,1]).weight_space() + sage: KRT.module_generator(weight=WS.sum_of_terms([[0,-2],[2,1]])) + [[1, 1], [2, -1]] + sage: WSC = RootSystem(['D',4]).weight_space() + sage: KRT.module_generator(classical_weight=WSC.fundamental_weight(2)) + [[1, 1], [2, -1]] + """ + 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 + 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 + elif "column_shape" in options: + shape = list(Partition(options["column_shape"]).conjugate()) + if len(shape) < self._cartan_type.classical().rank(): + shape.extend( [0]*(n - len(shape)) ) + for mg in self.module_generators: + if list(mg.classical_weight().to_vector()) == shape: + return mg + return None + elif "weight" in options: + wt = options["weight"] + for mg in self.module_generators: + if mg.weight() == wt: + return mg + return None + elif "classical_weight" in options: + wt = options["classical_weight"] + for mg in self.module_generators: + if mg.classical_weight() == wt: + return mg + return None + raise ValueError("Invalid parameter") + @abstract_method def _build_module_generators(self): """ @@ -198,26 +317,48 @@ def _build_module_generators(self): sage: KRT._build_module_generators() ([[1, 1, 1], [2, 2, 2]],) """ -# shapes = self.Kirillov_Reshetikhin_crystal().classical_decomposition().shapes -# self.module_generators = tuple(self._fill(Partition(shape).conjugate()) for shape in shapes) - def _element_constructor_(self, list, **options): + @abstract_method + def from_Kirillov_Reshetikhin_crystal(self, krc): + """ + Construct an element of ``self`` from the Kirillov-Reshetikhin + crystal element ``krc``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 1) + sage: C = KirillovReshetikhinCrystal(['A',4,1], 2, 1) + sage: krc = C(4,3); krc + [[3], [4]] + sage: KRT.from_Kirillov_Reshetikhin_crystal(krc) + [[3], [4]] + """ + + def _element_constructor_(self, *lst, **options): """ Construct a :class:`KirillovReshetikhinTableauxElement`. EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 1) - sage: KRT([3, 4]) # indirect doctest + sage: KRT(3, 4) # indirect doctest [[4], [3]] - sage: KRT([4, 3]) + sage: KRT(4, 3) [[3], [4]] """ - return self.element_class(self, list, **options) + if isinstance(lst[0], KirillovReshetikhinGenericCrystalElement): + # Check to make sure it can be converted + if lst[0].cartan_type() != self.cartan_type() \ + or lst[0].parent().r() != self._r or lst[0].parent().s() != self._s: + raise ValueError("The Kirillov-Reshetikhin crystal must have the same Cartan type and (r,s)") + return self.from_Kirillov_Reshetikhin_crystal(lst[0]) + + return self.element_class(self, list(lst), **options) def r(self): """ - Return the value `r` for this tableaux class. + Return the value `r` for this tableaux class which corresponds to the + number of rows. EXAMPLES:: @@ -229,7 +370,8 @@ def r(self): def s(self): """ - Return the value `s` for this tableaux class. + Return the value `s` for this tableaux class which corresponds to the + number of columns. EXAMPLES:: @@ -239,6 +381,7 @@ def s(self): """ return self._s + @cached_method def Kirillov_Reshetikhin_crystal(self): """ Return the corresponding @@ -249,8 +392,7 @@ def Kirillov_Reshetikhin_crystal(self): sage: KirillovReshetikhinTableaux(['A', 4, 1], 2, 1).Kirillov_Reshetikhin_crystal() Kirillov-Reshetikhin crystal of type ['A', 4, 1] with (r,s)=(2,1) """ - return KirillovReshetikhinCrystal(self._cartan_type.affine(), - self._r, self._s) + return KirillovReshetikhinCrystal(self._cartan_type, self._r, self._s) class KRTableauxRectangle(KirillovReshetikhinTableaux): r""" @@ -261,6 +403,13 @@ class KRTableauxRectangle(KirillovReshetikhinTableaux): - `A_n^{(1)}` for all `1 \leq r \leq n`, - `C_n^{(1)}` when `r = n`. + + TESTS:: + + sage: KRT = KirillovReshetikhinTableaux(['A', 3, 1], 2, 2) + sage: TestSuite(KRT).run() + sage: KRT = KirillovReshetikhinTableaux(['C', 3, 1], 3, 2) + sage: TestSuite(KRT).run() # long time """ def _build_module_generators(self): r""" @@ -279,34 +428,27 @@ def _build_module_generators(self): for i in range(self._s): tableau.append( [self._r - j for j in range(self._r)] ) - return (self([self.letters(x) for x in flatten(tableau)]),) + return (self.element_class(self, [self.letters(x) for x in flatten(tableau)]),) - def _element_constructor_(self, list, **options): + def from_Kirillov_Reshetikhin_crystal(self, krc): """ Construct a :class:`KirillovReshetikhinTableauxElement`. EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 1) - sage: KRT([3, 4]) # indirect doctest - [[4], [3]] - sage: KRT([4, 3]) + sage: C = KirillovReshetikhinCrystal(['A',4,1], 2, 1) + sage: krc = C(4,3); krc + [[3], [4]] + sage: KRT.from_Kirillov_Reshetikhin_crystal(krc) [[3], [4]] """ - if isinstance(list, KirillovReshetikhinGenericCrystalElement): - # Check to make sure it can be converted - if list.cartan_type() != self.cartan_type().affine() \ - or list.parent().r() != self._r or list.parent().s() != self._s: - raise ValueError("The Kirillov-Reshetikhin crystal must have the same Cartan type and shape") - - # To build a KR tableau from a KR crystal: - # 1 - start with the highest weight KR tableau - # 2 - determine a path from the KR crystal to its highest weight - # 3 - apply the inverse path to the highest weight KR tableau - f_str = reversed(list.lift().to_highest_weight()[1]) - return self.module_generators[0].f_string(f_str) - - return KirillovReshetikhinTableaux._element_constructor_(self, list, **options) + # To build a KR tableau from a KR crystal: + # 1 - start with the highest weight KR tableau + # 2 - determine a path from the KR crystal to its highest weight + # 3 - apply the inverse path to the highest weight KR tableau + f_str = reversed(krc.lift().to_highest_weight()[1]) + return self.module_generators[0].f_string(f_str) class KRTableauxTypeVertical(KirillovReshetikhinTableaux): r""" @@ -315,15 +457,23 @@ class KRTableauxTypeVertical(KirillovReshetikhinTableaux): - `D_n^{(1)}` for all `1 \leq r < n-1`, - `B_n^{(1)}` for all `1 \leq r < n`, - `A_{2n-1}^{(2)}` for all `1 \leq r \leq n`. - """ - def _fill(self, shape): + TESTS:: + + sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 1, 1) + sage: TestSuite(KRT).run() + sage: KRT = KirillovReshetikhinTableaux(['B', 3, 1], 2, 2) + sage: TestSuite(KRT).run() # long time + sage: KRT = KirillovReshetikhinTableaux(['A', 5, 2], 2, 2) + sage: TestSuite(KRT).run() # long time + """ + def _fill(self, weight): r""" - Return the highest weight KR tableau of weight ``shape``. + Return the highest weight KR tableau of weight ``weight``. INPUT: - - ``shape`` -- The weight of the highest weight KR tableau (the + - ``weight`` -- The weight of the highest weight KR tableau (the conjugate of the shape of the KR crystal's tableau) OUTPUT: @@ -342,14 +492,14 @@ def _fill(self, shape): [[1, 1, 1, 1, 1, 5, 1], [2, 2, 2, 2, 2, 6, 2], [3, 3, 9, 7, 9, 7, 3], [4, 4, 10, 8, 10, 8, 4], [5, 5, 11, 9, 11, 9, 5], [6, 6, 12, 10, 12, 10, 6], [7, 7, -12, 11, -12, 11, 7], [8, 8, -11, 12, -11, 12, 8], [9, 9, -10, -12, -10, -12, -8], [10, 10, -9, -11, -9, -11, -7], [-12, 11, -8, -10, -8, -10, -6], [-11, 12, -7, -9, -7, -9, -5]] """ # Add zeros until the shape has length s - shape_list = list(shape) # Make sure we have a list - while len(shape_list) != self._s: - shape_list.append(0) + weight_list = list(weight) # Make sure we have a list + while len(weight_list) != self._s: + weight_list.append(0) tableau = [] i = 0 # Step 0 - Fill first columns of height r - while i < self._s and shape_list[i] == self._r: + while i < self._s and weight_list[i] == self._r: tableau.append( [self._r - j for j in range(self._r)] ) i += 1 @@ -357,13 +507,13 @@ def _fill(self, shape): c = -1 while i < self._s: # If it is an odd number of columns - if i == self._s - 1 or shape_list[i] != shape_list[i+1]: - c = shape_list[i] + if i == self._s - 1 or weight_list[i] != weight_list[i+1]: + c = weight_list[i] i += 1 break - temp_list = [-(shape_list[i] + j + 1) for j in range(self._r - shape_list[i])] - for j in range(shape_list[i]): - temp_list.append(shape_list[i] - j) + temp_list = [-(weight_list[i] + j + 1) for j in range(self._r - weight_list[i])] + for j in range(weight_list[i]): + temp_list.append(weight_list[i] - j) tableau.append(temp_list) tableau.append( [self._r - j for j in range(self._r)] ) i += 2 @@ -372,11 +522,11 @@ def _fill(self, shape): x = c + 1 while i < self._s: temp_list = [-x - j for j in range(self._r - x + 1)] # +1 for indexing - for j in range(x - shape_list[i] - 1): # +1 for indexing + for j in range(x - weight_list[i] - 1): # +1 for indexing temp_list.append(self._r - j) x = temp_list[-1] # This is the h+1 entry of the column - for j in range(shape_list[i]): - temp_list.append(shape_list[i] - j) + for j in range(weight_list[i]): + temp_list.append(weight_list[i] - j) tableau.append(temp_list) i += 1 @@ -389,7 +539,7 @@ def _fill(self, shape): temp_list.append(val - j) tableau.append(temp_list) - return self([self.letters(x) for x in flatten(tableau)]) + return self.element_class(self, [self.letters(x) for x in flatten(tableau)]) def _build_module_generators(self): """ @@ -399,48 +549,241 @@ def _build_module_generators(self): sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 2, 3) sage: KRT._build_module_generators() - ([[-2, 1, 1], [-1, 2, -1]], [[1, -2, 1], [2, -1, 2]], [[1, 1, 1], [2, 2, -1]], [[1, 1, 1], [2, 2, 2]]) + ([[-2, 1, 1], [-1, 2, -1]], [[1, -2, 1], [2, -1, 2]], + [[1, 1, 1], [2, 2, -1]], [[1, 1, 1], [2, 2, 2]]) """ - return tuple(self._fill(shape) for shape in + return tuple(self._fill(weight) for weight in horizontal_dominoes_removed(self._s, self._r)) - def _element_constructor_(self, list, **options): + def from_Kirillov_Reshetikhin_crystal(self, krc): """ - Construct a :class:`KirillovReshetikhinTableauxElement`. + Construct an element of ``self`` from the Kirillov-Reshetikhin + crystal element ``krc``. EXAMPLES:: - sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 1) - sage: KRT([3, 4]) # indirect doctest - [[4], [3]] - sage: KRT([4, 3]) + sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 2,3) + sage: C = KirillovReshetikhinCrystal(['D',4,1], 2,3) + sage: krc = C(4,3); krc [[3], [4]] + sage: KRT.from_Kirillov_Reshetikhin_crystal(krc) + [[3, -2, 1], [4, -1, 2]] """ - if "shape" in options: - return self._fill(Partition(options["shape"]).conjugate()) + # To build a KR tableau from a KR crystal: + # 1 - start with a highest weight KR tableau generated from the + # shape of the KR crystal + # 2 - determine a path from the KR crystal to its highest weight + # 3 - apply the inverse path to the highest weight KR tableau + lifted = krc.lift() + weight = lifted.to_tableau().shape().conjugate() + f_str = reversed(lifted.to_highest_weight()[1]) + return self._fill(weight).f_string(f_str) + +class KRTableauxTypeC(KirillovReshetikhinTableaux): + r""" + Kirillov-Reshetikhin tableaux `B^{r,s}` of type: - if isinstance(list, KirillovReshetikhinGenericCrystalElement): - # Check to make sure it can be converted - if list.cartan_type() != self.cartan_type().affine() \ - or list.parent().r() != self._r or list.parent().s() != self._s: - raise ValueError("The Kirillov-Reshetikhin crystal must have the same Cartan type and shape") - - # To build a KR tableau from a KR crystal: - # 1 - start with a highest weight KR tableau generated from the - # shape of the KR crystal - # 2 - determine a path from the KR crystal to its highest weight - # 3 - apply the inverse path to the highest weight KR tableau - lifted = list.lift() - shape = lifted.to_tableau().shape().conjugate() - f_str = reversed(lifted.to_highest_weight()[1]) - return self._fill(shape).f_string(f_str) - - return KirillovReshetikhinTableaux._element_constructor_(self, list, **options) + - `C_n^{(1)}` for `1 \leq r < n`, + - `A_{2n}^{(2)\dagger}` for `1 \leq r \leq n`. + + TESTS:: + + sage: KRT = KirillovReshetikhinTableaux(['C', 3, 1], 2, 2) + sage: TestSuite(KRT).run() # long time + sage: KRT = KirillovReshetikhinTableaux(CartanType(['A', 4, 2]).dual(), 2, 2) + sage: TestSuite(KRT).run() + """ + def _fill(self, shape): + r""" + Return the highest weight KR tableau of weight ``shape``. + + INPUT: + + - ``shape`` -- The shape of the KR crystal's tableau + + OUTPUT: + + - A `r \times s` tableau + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['C', 5, 1], 3, 5) + sage: KRT._fill([3,3,1]) + [[1, 1, 1, -3, 1], [2, 2, 2, -2, 2], [3, -3, 3, -1, 3]] + sage: KRT = KirillovReshetikhinTableaux(['C', 10, 1], 5, 6) + sage: KRT._fill([6,4,2,2]) + [[1, 1, 1, 1, 1, 1], [2, 2, 2, 2, -5, 2], [3, 3, -5, 3, -4, 3], [4, 4, -4, 4, -3, 4], [-5, 5, -3, 5, -2, 5]] + sage: KRT._fill([6,4]) + [[1, 1, 1, 1, 1, 1], [2, 2, 2, 2, -5, 2], [-5, 3, -5, 3, -4, 3], [-4, 4, -4, 4, -3, 4], [-3, 5, -3, 5, -2, 5]] + """ + # Add zeros until the shape has length s + shape_list = list(shape) # Make sure we have a list + while len(shape_list) != self._r: + shape_list.append(0) + + lst = [] + for col in range(1, self._s+1): + if (self._s - col) % 2 == 0: + lst.extend( [self.letters(self._r - x) for x in range(self._r)] ) + else: + m = self._r + for j, val in enumerate(shape_list): + if col >= val: + m = j + break + lst.extend([self.letters(-x) for x in range(m+1, self._r+1)]) + lst.extend([self.letters(m - x) for x in range(m)]) + + return self.element_class(self, lst) + + def _build_module_generators(self): + """ + Build the module generators. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['C',4,1], 2, 3) + sage: KRT._build_module_generators() + ([[1, -2, 1], [2, -1, 2]], [[1, 1, 1], [2, -2, 2]], [[1, 1, 1], [2, 2, 2]]) + """ + return tuple(self._fill(shape) for shape in horizontal_dominoes_removed(self._r, self._s)) + + def from_Kirillov_Reshetikhin_crystal(self, krc): + """ + Construct an element of ``self`` from the Kirillov-Reshetikhin + crystal element ``krc``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['C',4,1], 2,3) + sage: C = KirillovReshetikhinCrystal(['C',4,1], 2,3) + sage: krc = C(4,3); krc + [[3], [4]] + sage: KRT.from_Kirillov_Reshetikhin_crystal(krc) + [[3, -2, 1], [4, -1, 2]] + """ + # To build a KR tableau from a KR crystal: + # 1 - start with a highest weight KR tableau generated from the + # shape of the KR crystal + # 2 - determine a path from the KR crystal to its highest weight + # 3 - apply the inverse path to the highest weight KR tableau + lifted = krc.lift() + shape = lifted.to_tableau().shape() + f_str = reversed(lifted.to_highest_weight()[1]) + return self._fill(shape).f_string(f_str) + +class KRTableauxTypeBox(KRTableauxTypeVertical): + r""" + Kirillov-Reshetikhin tableaux `B^{r,s}` of type: + + - `A_{2n}^{(2)}` for all `r \leq n`, + - `D_{n+1}^{(2)}` for all `r < n`. + + TESTS:: + + sage: KRT = KirillovReshetikhinTableaux(['A', 4, 2], 2, 2) + sage: TestSuite(KRT).run() + sage: KRT = KirillovReshetikhinTableaux(['D', 4, 2], 2, 2) + sage: TestSuite(KRT).run() # long time + """ + def _fill(self, weight): + r""" + Return the highest weight KR tableau of weight ``weight``. + + INPUT: + + - ``weight`` -- The weight of the highest weight KR tableau (the + conjugate of the shape of the KR crystal's tableau) + + OUTPUT: + + - A `r \times s` tableau + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 2, 1) + sage: KRT._fill([]) + [[1], [-1]] + sage: KRT = KirillovReshetikhinTableaux(['D', 14, 1], 12, 7) + sage: KRT._fill([10,10,8,2,2,2]) + [[1, 1, 1, 1, 1, 7, 1], [2, 2, 2, 2, 2, 8, 2], [3, 3, 7, 9, 7, 9, 3], [4, 4, 8, 10, 8, 10, 4], [5, 5, 9, 11, 9, 11, 5], [6, 6, 10, 12, 10, 12, 6], [7, 7, 11, -12, 11, -12, 7], [8, 8, 12, -11, 12, -11, 8], [9, 9, -12, -10, -12, -10, 9], [10, 10, -11, -9, -11, -9, -9], [-12, 11, -10, -8, -10, -8, -8], [-11, 12, -9, -7, -9, -7, -7]] + sage: KRT._fill([10,10,6,2,2,2]) + [[1, 1, 1, 1, 1, 5, 1], [2, 2, 2, 2, 2, 6, 2], [3, 3, 9, 7, 9, 7, 3], [4, 4, 10, 8, 10, 8, 4], [5, 5, 11, 9, 11, 9, 5], [6, 6, 12, 10, 12, 10, 6], [7, 7, -12, 11, -12, 11, 7], [8, 8, -11, 12, -11, 12, 8], [9, 9, -10, -12, -10, -12, -8], [10, 10, -9, -11, -9, -11, -7], [-12, 11, -8, -10, -8, -10, -6], [-11, 12, -7, -9, -7, -9, -5]] + """ + # Add zeros until the shape has length s + weight_list = list(weight) # Make sure we have a list + while len(weight_list) != self._s: + weight_list.append(0) + + tableau = [] + i = 0 + # Step 0 - Fill first columns of height r + while i < self._s and weight_list[i] == self._r: + tableau.append( [self._r - j for j in range(self._r)] ) + i += 1 + + # Step 1 - Add the alternating columns until we hit an odd number of columns + c = -1 + while i < self._s: + # If it is an odd number of columns + if i == self._s - 1 or weight_list[i] != weight_list[i+1]: + c = weight_list[i] + i += 1 + break + temp_list = [-(weight_list[i] + j + 1) for j in range(self._r - weight_list[i])] + for j in range(weight_list[i]): + temp_list.append(weight_list[i] - j) + tableau.append(temp_list) + tableau.append( [self._r - j for j in range(self._r)] ) + i += 2 + + # Step 2 - Add the x dependent columns + x = c + 1 + while i < self._s: + temp_list = [-x - j for j in range(self._r - x + 1)] # +1 for indexing + for j in range(x - weight_list[i] - 1): # +1 for indexing + temp_list.append(self._r - j) + x = temp_list[-1] # This is the h+1 entry of the column + for j in range(weight_list[i]): + temp_list.append(weight_list[i] - j) + + tableau.append(temp_list) + i += 1 + + # Step 3 - Add the final column + if c > -1: + val = x - 1 + temp_list = ['E' for j in range(self._r - val)] + for j in range(val): + temp_list.append(val - j) + tableau.append(temp_list) + + return self.element_class(self, [self.letters(x) for x in flatten(tableau)]) + + def _build_module_generators(self): + """ + Build the module generators. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['A',4,2], 2, 2) + sage: KRT._build_module_generators() + ([[-2, 1], [-1, 2]], [[2, 1], [-2, E]], [[1, E], [2, E]], + [[1, 1], [-2, 2]], [[1, 1], [2, E]], [[1, 1], [2, 2]]) + """ + return tuple(self._fill(weight) for weight in partitions_in_box(self._s, self._r)) class KRTableauxSpin(KRTableauxRectangle): r""" Kirillov-Reshetikhin tableaux `B^{r,s}` of type `D_n^{(1)}` with `r = n, n-1`. + + TESTS:: + + sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 3, 2) + sage: TestSuite(KRT).run() + sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 4, 2) + sage: TestSuite(KRT).run() """ def _build_module_generators(self): r""" @@ -454,28 +797,70 @@ def _build_module_generators(self): sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 3, 3) sage: KRT._build_module_generators() ([[1, 1, 1], [2, 2, 2], [3, 3, 3], [-4, -4, -4]],) + sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 4, 3) + sage: KRT._build_module_generators() + ([[1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4]],) """ - if self._r == self.cartan_type().n: + n = self.cartan_type().classical().rank() + if self._r == n: return KRTableauxRectangle._build_module_generators(self) tableau = [] for i in range(self._s): - tableau.append( [-4] + [self._r - j for j in range(self._r)] ) - - return (self([self.letters(x) for x in flatten(tableau)]),) - -# Placeholder for type B_n spinors -#class KRTableauxBn(KRTableauxSpin): -# """ -# Kirillov-Reshetkhin tableaux `B^{n,s}` of type `B_n^{(1)}`. -# """ -# def _build_module_generators(self): -# """ -# Build the module generators. -# """ -# shapes = KirillovReshetikhinCrystal(cartan_type, r, s).classical_decomposition().shapes -# shapes = [Partition(map(lambda x: Integer(x*2), shape)).conjugate() for shape in shapes] -# self.module_generators = tuple(self._fill(Partition(shape).conjugate()) for shape in shapes) + tableau.append( [-n] + [self._r - j for j in range(self._r)] ) + + return (self.element_class(self, [self.letters(x) for x in flatten(tableau)]),) + +class KRTableauxBn(KRTableauxTypeC): + """ + Kirillov-Reshetkhin tableaux `B^{n,s}` of type `B_n^{(1)}`. + + TESTS:: + + sage: KRT = KirillovReshetikhinTableaux(['B', 2, 1], 2, 3) + sage: TestSuite(KRT).run() + """ + def _build_module_generators(self): + """ + Build the module generators. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['B', 2, 1], 2, 2) + sage: KRT._build_module_generators() + ([[-2, 1], [-1, 2]], [[1, 1], [2, 2]]) + """ + odd = int(self._s % 2) + shapes = [[int(x * 2 + odd) for x in sh] for sh + in vertical_dominoes_removed(self._r, self._s // 2)] + return tuple(self._fill(sh) for sh in shapes) + + def from_Kirillov_Reshetikhin_crystal(self, krc): + """ + Construct an element of ``self`` from the Kirillov-Reshetikhin + crystal element ``krc``. + + EXAMPLES:: + + sage: KR = KirillovReshetikhinTableaux(['B',3,1], 3,3) + sage: C = KirillovReshetikhinCrystal(['B',3,1], 3,3) + sage: krc = C.module_generators[1].f_string([3,2,3,1,3,3]); krc + [++-, [[2], [0], [-3]]] + sage: KR.from_Kirillov_Reshetikhin_crystal(krc) + [[1, 1, 2], [2, 2, -3], [-3, -3, -1]] + """ + # To build a KR tableau from a type B_n spinor KR crystal: + # 1 - determine a path from the KR crystal to its highest weight + # 2 - find the corresponding highest weight KR tableau + # 3 - apply the inverse path to the highest weight KR tableau + lifted = krc.lift() + to_hw = lifted.to_highest_weight() + f_str = reversed(to_hw[1]) + wt = to_hw[0].weight() + for x in self.module_generators: + if x.classical_weight() / 2 == wt: + return x.f_string(f_str) + raise ValueError("No matching highest weight element found") class KirillovReshetikhinTableauxElement(TensorProductOfRegularCrystalsElement): r""" @@ -484,7 +869,6 @@ class KirillovReshetikhinTableauxElement(TensorProductOfRegularCrystalsElement): For more information, see :class:`KirillovReshetikhinTableaux` and :class:`TensorProductOfKirillovReshetikhinTableaux`. """ - def __init__(self, parent, list, **options): r""" Initialize ``self``. @@ -492,12 +876,12 @@ def __init__(self, parent, list, **options): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 1) - sage: elt = KRT([4, 3]); elt + sage: elt = KRT(4, 3); elt [[3], [4]] sage: TestSuite(elt).run() """ # Make sure we are a list of letters - if list != [] and type(list[0]) is not parent.letters.element_class: + if list != [] and not isinstance(list[0], (parent.letters.element_class, EmptyLetter)): list = [parent.letters(x) for x in list] TensorProductOfRegularCrystalsElement.__init__(self, parent, list) @@ -508,19 +892,33 @@ def _repr_(self): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 1) - sage: KRT([3,2]) # indirect doctest + sage: KRT(3,2) # indirect doctest [[2], [3]] """ return repr(self.to_array()) - def _latex_(self): + def _repr_diagram(self): + """ + Return a string representation of ``self`` as a diagram. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['A',4,1], 2,2) + sage: elt = KRT(4,3,2,1) + sage: print elt._repr_diagram() + 3 1 + 4 2 """ + return self.to_tableau()._repr_diagram() + + def _latex_(self): + r""" Return a latex representation of ``self``. EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 3) - sage: latex(KRT([3,2,4,2,4,3])) # indirect doctest + sage: latex(KRT(3,2,4,2,4,3)) # indirect doctest {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} \raisebox{-.6ex}{$\begin{array}[b]{*{3}c}\cline{1-3} \lr{2}&\lr{2}&\lr{3}\\\cline{1-3} @@ -529,7 +927,7 @@ def _latex_(self): } """ from sage.combinat.output import tex_from_array - return tex_from_array(self.to_array()) + return tex_from_array([[val._latex_() for val in row] for row in self.to_array()]) def to_Kirillov_Reshetikhin_crystal(self): r""" @@ -546,7 +944,7 @@ def to_Kirillov_Reshetikhin_crystal(self): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 2,2) - sage: elt = KRT([3,2,-1,1]); elt + sage: elt = KRT(3,2,-1,1); elt [[2, 1], [3, -1]] sage: elt.to_Kirillov_Reshetikhin_crystal() [[2], [3]] @@ -557,7 +955,7 @@ def to_Kirillov_Reshetikhin_crystal(self): sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 4, 3) sage: KRC = KirillovReshetikhinCrystal(['D',4,1], 4, 3) - sage: elt = KRT([-3,-4,2,1,-3,-4,2,1,-2,-4,3,1]); elt + sage: elt = KRT(-3,-4,2,1,-3,-4,2,1,-2,-4,3,1); elt [[1, 1, 1], [2, 2, 3], [-4, -4, -4], [-3, -3, -2]] sage: ret = elt.to_Kirillov_Reshetikhin_crystal(); ret [++--, [[1], [3], [-4], [-3]]] @@ -600,13 +998,12 @@ def to_array(self, rows=True): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 2) - sage: elt = KRT([4, 3, 2, 1]) + sage: elt = KRT(4, 3, 2, 1) sage: elt.to_array() [[3, 1], [4, 2]] sage: elt.to_array(False) [[4, 3], [2, 1]] """ - ret_list = [] h = self.parent()._r s = self.parent()._s @@ -625,6 +1022,38 @@ def to_array(self, rows=True): return ret_list + @cached_method + def to_tableau(self): + """ + Return a :class:`Tableau` object of ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 2) + sage: elt = KRT(4, 3, 2, 1); elt + [[3, 1], [4, 2]] + sage: t = elt.to_tableau(); t + [[3, 1], [4, 2]] + sage: type(t) + + """ + return Tableau(self.to_array()) + + def pp(self): + """ + Pretty print ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 2) + sage: elt = KRT(4, 3, 2, 1); elt + [[3, 1], [4, 2]] + sage: elt.pp() + 3 1 + 4 2 + """ + self.to_tableau().pp() + def to_classical_highest_weight(self, index_set=None): r""" Return the classical highest weight element corresponding to ``self``. @@ -643,20 +1072,33 @@ def to_classical_highest_weight(self, index_set=None): EXAMPLES:: sage: KRTab = KirillovReshetikhinTableaux(['D',4,1], 2,2) - sage: elt = KRTab([3,2,-1,1]); elt + sage: elt = KRTab(3,2,-1,1); elt [[2, 1], [3, -1]] sage: elt.to_classical_highest_weight() [[[1, 1], [2, -1]], [1, 2]] """ if index_set is None: - index_set = self.index_set() + index_set = self.parent()._cartan_type.classical().index_set() for i in index_set: - if self.epsilon(i) != 0: - self = self.e(i) - hw = self.to_classical_highest_weight(index_set=index_set) + next = self.e(i) + if next is not None: + hw = next.to_classical_highest_weight(index_set=index_set) return [hw[0], [i] + hw[1]] return [self, []] + def weight(self): + """ + Return the weight of ``self``. + + EXAMPLES:: + + sage: KR = KirillovReshetikhinTableaux(['D',4,1], 2, 2) + sage: KR.module_generators[1].weight() + -2*Lambda[0] + Lambda[2] + """ + return self.Phi() - self.Epsilon() + + @cached_method def classical_weight(self): r""" Return the classical weight of ``self``. @@ -664,12 +1106,18 @@ def classical_weight(self): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 2,2) - sage: elt = KRT([3,2,-1,1]); elt + sage: elt = KRT(3,2,-1,1); elt [[2, 1], [3, -1]] sage: elt.classical_weight() (0, 1, 1, 0) """ - return self.Phi() - self.Epsilon() + F = self.cartan_type().classical().root_system() + if F.ambient_space() is None: + WLR = F.weight_lattice() + else: + WLR = F.ambient_space() + weight = lambda x: x.weight() + return sum((weight(self[j]) for j in range(len(self))), WLR.zero()) def e(self, i): """ @@ -713,14 +1161,53 @@ def f(self, i): return ret.to_Kirillov_Reshetikhin_tableau() return TensorProductOfRegularCrystalsElement.f(self, i) + def epsilon(self, i): + r""" + Compute `\epsilon_i` of ``self``. + + .. TODO:: + + Implement a direct action of `\epsilon_0` without moving to + KR crystals. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 2,2) + sage: KRT.module_generators[0].epsilon(0) + 2 + """ + if i == 0: + return self.to_Kirillov_Reshetikhin_crystal().epsilon0() + return TensorProductOfRegularCrystalsElement.epsilon(self, i) + + def phi(self, i): + r""" + Compute `\phi_i` of ``self``. + + .. TODO:: + + Compute `\phi_0` without moving to KR crystals. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 2,2) + sage: KRT.module_generators[0].phi(0) + 2 + """ + if i == 0: + return self.to_Kirillov_Reshetikhin_crystal().phi0() + return TensorProductOfRegularCrystalsElement.phi(self, i) + KirillovReshetikhinTableaux.Element = KirillovReshetikhinTableauxElement class KRTableauxSpinElement(KirillovReshetikhinTableauxElement): r""" Kirillov-Reshetikhin tableau for spinors. - Here we are in the embedding `B(\Lambda_n) \to B(2 \Lambda_n)`, so `e_i` - and `f_i` act by `e_i^2` and `f_i^2` respectively. + Here we are in the embedding `B(\Lambda_n) \hookrightarrow + B(2 \Lambda_n)`, so `e_i` and `f_i` act by `e_i^2` and `f_i^2` + respectively for all `i \neq 0`. We do this so our columns are full + width (as opposed to half width and/or uses a `\pm` representation). """ def e(self, i): r""" @@ -729,14 +1216,17 @@ def e(self, i): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 4, 1) - sage: KRT([-1, -4, 3, 2]).e(1) + sage: KRT(-1, -4, 3, 2).e(1) [[1], [3], [-4], [-2]] - sage: KRT([-1, -4, 3, 2]).e(3) + sage: KRT(-1, -4, 3, 2).e(3) """ - half = TensorProductOfRegularCrystalsElement.e(self, i) + if i == 0: # Only need to do it once since we pull to the KR crystal + return KirillovReshetikhinTableauxElement.e(self, i) + + half = KirillovReshetikhinTableauxElement.e(self, i) if half is None: return None - return TensorProductOfRegularCrystalsElement.e(half, i) + return KirillovReshetikhinTableauxElement.e(half, i) def f(self, i): r""" @@ -745,43 +1235,50 @@ def f(self, i): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 4, 1) - sage: KRT([-1, -4, 3, 2]).f(1) - sage: KRT([-1, -4, 3, 2]).f(3) + sage: KRT(-1, -4, 3, 2).f(1) + sage: KRT(-1, -4, 3, 2).f(3) [[2], [4], [-3], [-1]] """ - half = TensorProductOfRegularCrystalsElement.f(self, i) + if i == 0: # Only need to do it once since we pull to the KR crystal + return KirillovReshetikhinTableauxElement.f(self, i) + + half = KirillovReshetikhinTableauxElement.f(self, i) if half is None: return None - return TensorProductOfRegularCrystalsElement.f(half, i) + return KirillovReshetikhinTableauxElement.f(half, i) def epsilon(self, i): r""" - Compute `\epsilon_i` of ``self``. + Compute `\varepsilon_i` of ``self``. EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 4, 1) - sage: KRT([-1, -4, 3, 2]).epsilon(1) + sage: KRT(-1, -4, 3, 2).epsilon(1) 1 - sage: KRT([-1, -4, 3, 2]).epsilon(3) + sage: KRT(-1, -4, 3, 2).epsilon(3) 0 """ - return TensorProductOfRegularCrystalsElement.epsilon(self, i) / 2 + if i == 0: # Don't need to half it since we pull to the KR crystal + return KirillovReshetikhinTableauxElement.epsilon(self, i) + return KirillovReshetikhinTableauxElement.epsilon(self, i) / 2 def phi(self, i): r""" - Compute `\phi_i` of ``self``. + Compute `\varphi_i` of ``self``. EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 4, 1) - sage: KRT([-1, -4, 3, 2]).phi(1) + sage: KRT(-1, -4, 3, 2).phi(1) 0 - sage: KRT([-1, -4, 3, 2]).phi(3) + sage: KRT(-1, -4, 3, 2).phi(3) 1 """ - return TensorProductOfRegularCrystalsElement.phi(self, i) / 2 + if i == 0: # Don't need to half it since we pull to the KR crystal + return KirillovReshetikhinTableauxElement.phi(self, i) + return KirillovReshetikhinTableauxElement.phi(self, i) / 2 @cached_method def to_array(self, rows=True): @@ -815,15 +1312,14 @@ def to_array(self, rows=True): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 4, 3) - sage: elt = KRT([-3,-4,2,1,-3,-4,2,1,-2,-4,3,1]) + sage: elt = KRT(-3,-4,2,1,-3,-4,2,1,-2,-4,3,1) sage: elt.to_array() [[1, 1, 1], [2, 2, 3], [-4, -4, -4], [-3, -3, -2]] sage: elt.to_array(False) [[-3, -4, 2, 1], [-3, -4, 2, 1], [-2, -4, 3, 1]] """ - ret_list = [] - h = self.parent()._cartan_type.n + h = self.parent()._cartan_type.classical().rank() s = self.parent()._s if rows: for i in reversed(range(h)): @@ -840,4 +1336,216 @@ def to_array(self, rows=True): return ret_list +KRTableauxBn.Element = KRTableauxSpinElement KRTableauxSpin.Element = KRTableauxSpinElement + +class KRTableauxDTwistedSpin(KRTableauxRectangle): + r""" + Kirillov-Reshetikhin tableaux `B^{r,s}` of type `D_n^{(2)}` with `r = n`. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E', 6, 1], 1, 1) + sage: KRT.cardinality() + 27 + sage: KRC = KirillovReshetikhinCrystal(['E', 6, 1], 1, 1) + sage: KRT.cardinality() == KRC.cardinality() + True + """ + Element = KRTableauxSpinElement + +class KRTableauxWrapper(KirillovReshetikhinTableaux): + """ + Kirillov-Reshetikhin tableaux that are wrappers around a + Kirillov-Reshetikhin crystal. + """ + def _build_module_generators(self): + """ + Build the module generators. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1, 1) + sage: KRT._build_module_generators() + ([(1,)],) + """ + C = self.Kirillov_Reshetikhin_crystal() + return tuple(self.element_class(self, mg) for mg in C.module_generators) + + def from_Kirillov_Reshetikhin_crystal(self, krc): + """ + Construct an element of ``self`` from the Kirillov-Reshetikhin + crystal element ``krc``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1, 1) + sage: KRC = KirillovReshetikhinCrystal(['E',6,1], 1, 1) + sage: krc = KRC.module_generators[0].f(1); krc + [(-1, 3)] + sage: KRT.from_Kirillov_Reshetikhin_crystal(krc) + [(-1, 3)] + """ + return self.element_class(self, krc) + + class Element(ElementWrapper): + """ + A KR tableaux element wrapper around a KR crystal element. + """ + def _repr_diagram(self): + """ + Return a string representation of ``self`` as a diagram. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) + sage: elt = KRT.module_generators[0].f(1); elt + [(-1, 3)] + sage: elt._repr_diagram() + '[(-1, 3)]' + """ + try: + return self.to_tableau()._repr_diagram() + except (AttributeError, ValueError): + return repr(self) + + def to_Kirillov_Reshetikhin_crystal(self): + r""" + Construct a :func:`KirillovReshetikhinCrystal` element + from ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) + sage: elt = KRT.module_generators[0].f(1); elt + [(-1, 3)] + sage: elt.to_Kirillov_Reshetikhin_crystal() + [(-1, 3)] + """ + return self.value + + @cached_method + def to_tableau(self): + """ + Return a :class:`Tableau` object of ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) + sage: elt = KRT.module_generators[0].f(1); elt + [(-1, 3)] + sage: elt.to_tableau() + Traceback (most recent call last): + ... + ValueError: cannot convert [(-1, 3)] to a tableau + """ + try: + return self.value.to_tableau() + except (AttributeError, ValueError): + raise ValueError("cannot convert {} to a tableau".format(self)) + + def pp(self): + """ + Pretty print ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) + sage: elt = KRT.module_generators[0].f(1); elt + [(-1, 3)] + sage: elt.pp() + [(-1, 3)] + """ + try: + self.value.pp() + except (AttributeError, ValueError): + print self + + @cached_method + def classical_weight(self): + r""" + Return the classical weight of ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) + sage: elt = KRT.module_generators[0].f(1); elt + [(-1, 3)] + sage: elt.classical_weight() + (-1/2, 1/2, 1/2, 1/2, 1/2, -1/6, -1/6, 1/6) + """ + return self.value.lift().weight() + + def weight(self): + """ + Return the weight of ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) + sage: KRT.module_generators[0].weight() + -Lambda[0] + Lambda[1] + """ + return self.value.weight() + + def e(self, i): + """ + Perform the action of `e_i` on ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) + sage: KRT.module_generators[0].e(2) + sage: KRT.module_generators[0].e(0) + [(-2, 1)] + """ + next = self.value.e(i) + if next is None: + return None + return self.__class__(self.parent(), next) + + def f(self, i): + """ + Perform the action of `f_i` on ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) + sage: KRT.module_generators[0].f(0) + sage: KRT.module_generators[0].f(1) + [(-1, 3)] + """ + next = self.value.f(i) + if next is None: + return None + return self.__class__(self.parent(), next) + + def epsilon(self, i): + r""" + Compute `\epsilon_i` of ``self``. + + .. TODO:: + + Implement a direct action of `\epsilon_0` without moving to + KR crystals. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) + sage: KRT.module_generators[0].epsilon(0) + 1 + """ + return self.value.epsilon(i) + + def phi(self, i): + r""" + Compute `\phi_i` of ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) + sage: KRT.module_generators[0].phi(0) + 0 + """ + return self.value.phi(i) + diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index a6c77132c61..bb965cc4304 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -1,5 +1,5 @@ r""" -A specific rigged configuration +Rigged Configuration Elements A rigged configuration element is a sequence of :class:`RiggedPartition` objects. @@ -7,9 +7,7 @@ AUTHORS: - Travis Scrimshaw (2010-09-26): Initial version - -.. TODO:: Implement crystal operators `e_0` and `f_0`. - +- Travis Scrimshaw (2012-10-25): Added virtual rigged confingurations """ #***************************************************************************** @@ -27,19 +25,18 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.misc.abstract_method import abstract_method +from sage.misc.cachefunc import cached_method from sage.structure.list_clone import ClonableArray -from sage.combinat.crystals.letters import CrystalOfLetters -from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition -from sage.combinat.rigged_configurations.bijection import RCToKRTBijection +from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition, \ + RiggedPartitionTypeB class RiggedConfigurationElement(ClonableArray): """ - The RiggedConfigurationElement class is a list of :class:`RiggedPartition` - objects. + A rigged configuration for simply-laced types. For more information on rigged configurations, see - :class:`RiggedConfigurations`. + :class:`RiggedConfigurations`. For rigged configurations for + non-simply-laced types, use :class:`RCVirtualElement`. Typically to create a specific rigged configuration, the user will pass in the optional argument **partition_list** and if the user wants to specify @@ -47,6 +44,18 @@ class RiggedConfigurationElement(ClonableArray): If **rigging_list** is not passed, the rigging values are set to the corresponding vacancy numbers. + INPUT: + + - ``parent`` -- The parent of this element + + - ``rigged_partitions`` -- A list of rigged partitions + + There are two optional arguments to explicitly construct a rigged + configuration. The first is **partition_list** which gives a list of + partitions, and the second is **rigging_list** which is a list of + corresponding lists of riggings. If only partition_list is specified, + then it sets the rigging equal to the calculated vacancy numbers. + EXAMPLES: Type `A_n^{(1)}` examples:: @@ -64,7 +73,7 @@ class RiggedConfigurationElement(ClonableArray): -2[ ][ ]-2 - sage: RC = HighestWeightRiggedConfigurations(['A', 4, 1], [[1, 1], [1, 1]]) + sage: RC = RiggedConfigurations(['A', 4, 1], [[1, 1], [1, 1]]) sage: RC(partition_list=[[], [], [], []]) (/) @@ -91,7 +100,7 @@ class RiggedConfigurationElement(ClonableArray): -1[ ][ ][ ]-1 - sage: RC = HighestWeightRiggedConfigurations(['D', 4, 1], [[1,1], [2, 1]]) + sage: RC = RiggedConfigurations(['D', 4, 1], [[1,1], [2, 1]]) sage: RC(partition_list=[[1], [1,1], [1], [1]]) 1[ ]1 @@ -122,34 +131,23 @@ class RiggedConfigurationElement(ClonableArray): sage: RC = RiggedConfigurations(['D', 4, 1], [[1,1], [2,1]]) sage: rc_elt = RC(partition_list=[[1], [1,1], [1], [1]]) - sage: tp_krt = rc_elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); tp_krt + sage: tp_krtab = rc_elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); tp_krtab [[-2]] (X) [[1], [2]] - sage: tp_krc = rc_elt.to_tensor_product_of_Kirillov_Reshetikhin_crystals(); tp_krc + sage: tp_krcrys = rc_elt.to_tensor_product_of_Kirillov_Reshetikhin_crystals(); tp_krcrys [[[-2]], [[1], [2]]] - sage: tp_krc == tp_krt.to_tensor_product_of_Kirillov_Reshetikhin_crystals() + sage: tp_krcrys == tp_krtab.to_tensor_product_of_Kirillov_Reshetikhin_crystals() True - sage: RC(tp_krc) == rc_elt + sage: RC(tp_krcrys) == rc_elt True - sage: RC(tp_krt) == rc_elt + sage: RC(tp_krtab) == rc_elt True - sage: tp_krt.to_rigged_configuration() == rc_elt + sage: tp_krtab.to_rigged_configuration() == rc_elt True """ - def __init__(self, parent, *rigged_partitions, **options): + def __init__(self, parent, rigged_partitions=[], **options): r""" Construct a rigged configuration element. - INPUT: - - - ``parent`` -- The parent of this element - - ``rigged_partitions`` -- A list of rigged partitions - - There are two optional arguments to explicitly construct a rigged - configuration. The first is **partition_list** which gives a list of - partitions, and the second is **rigging_list** which is a list of - corresponding lists of riggings. If only partition_list is specified, - then it sets the rigging equal to the calculated vacancy numbers. - EXAMPLES:: sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 1]]) @@ -185,61 +183,66 @@ def __init__(self, parent, *rigged_partitions, **options): sage: TestSuite(elt).run() """ - - if "partition_list" in options: + if "KT_constructor" in options: + # Used only by the Kleber tree + # Not recommended to be called by the user since it avoids safety + # checks for speed + data = options["KT_constructor"] + shape_data = data[0] + rigging_data = data[1] + vac_data = data[2] + nu = [] + for i in range(parent._cartan_type.classical().rank()): + nu.append(RiggedPartition(shape_data[i], rigging_data[i], vac_data[i])) + # Special display case + if parent.cartan_type().type() == 'B': + nu[-1] = RiggedPartitionTypeB(nu[-1]) + ClonableArray.__init__(self, parent, nu) + return + elif "partition_list" in options: data = options["partition_list"] - n = parent._cartan_type.n + n = parent._cartan_type.classical().rank() if len(data) == 0: # Create a size n array of empty rigged tableau since no tableau # were given nu = [] - for i in range(parent._cartan_type.n): + for i in range(n): nu.append(RiggedPartition()) else: if len(data) != n: # otherwise n should be equal to the number of tableaux - raise ValueError + raise ValueError("Incorrect number of partitions") nu = [] if "rigging_list" in options: rigging_data = options["rigging_list"] if len(rigging_data) != n: - raise ValueError + raise ValueError("Incorrect number of riggings") - for i in range(parent._cartan_type.n): + for i in range(n): nu.append(RiggedPartition(tuple(data[i]), \ list(rigging_data[i]))) else: for partition_data in data: nu.append(RiggedPartition(tuple(partition_data))) - elif len(list(rigged_partitions)) == 0: - # Create a size n array of empty rigged tableau since no tableau - # were given - nu = [] - for i in range(parent._cartan_type.n): - nu.append(RiggedPartition()) - elif "KT_constructor" in options: - # Used only by the Kleber tree - # Not recommended to be called by the user since it avoids safety - # checks for speed - data = options["KT_constructor"] - shape_data = data[0] - rigging_data = data[1] - vac_data = data[2] - nu = [] - for i in range(parent._cartan_type.n): - nu.append(RiggedPartition(shape_data[i], rigging_data[i], vac_data[i])) - ClonableArray.__init__(self, parent, nu) - return - elif parent._cartan_type.n == len(list(rigged_partitions)): - ClonableArray.__init__(self, parent, list(rigged_partitions)) + elif parent._cartan_type.classical().rank() == len(rigged_partitions) and \ + isinstance(rigged_partitions[0], RiggedPartition): + # The isinstance check is to make sure we are not in the n == 1 special case because + # Parent's __call__ always passes at least 1 argument to the element constructor + + # Special display case + if parent.cartan_type().type() == 'B': + rigged_partitions[-1] = RiggedPartitionTypeB(rigged_partitions[-1]) + ClonableArray.__init__(self, parent, rigged_partitions) return else: # Otherwise we did not receive any info, create a size n array of # empty rigged partitions nu = [] - for i in range(parent._cartan_type.n): + for i in range(parent._cartan_type.classical().rank()): nu.append(RiggedPartition()) + #raise ValueError("Invalid input") + #raise ValueError("Incorrect number of rigged partitions") # Set the vacancy numbers for a, partition in enumerate(nu): @@ -262,6 +265,10 @@ def __init__(self, parent, *rigged_partitions, **options): if partition.rigging[i] is None: partition.rigging[i] = partition.vacancy_numbers[i] + # Special display case + if parent.cartan_type().type() == 'B': + nu[-1] = RiggedPartitionTypeB(nu[-1]) + ClonableArray.__init__(self, parent, nu) def _repr_(self): @@ -270,7 +277,7 @@ def _repr_(self): EXAMPLES:: - sage: RC = HighestWeightRiggedConfigurations(['D', 4, 1], [[2, 2]]) + sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 2]]) sage: RC(partition_list=[[2], [3,1], [3], [3]]) # indirect doctest -1[ ][ ]-1 @@ -293,13 +300,13 @@ def _repr_(self): (/) """ - retStr = "" + ret_str = "" for tableau in self: - retStr += "\n" + repr(tableau) - return(retStr) + ret_str += "\n" + repr(tableau) + return(ret_str) def _latex_(self): - """ + r""" Return the LaTeX representation of ``self``. EXAMPLES:: @@ -308,30 +315,30 @@ def _latex_(self): sage: latex(RC(partition_list=[[2], [3,1], [3], [3]])) # indirect doctest { \begin{array}[t]{r|c|c|l} - \cline{2-3} -1 &\phantom{x}&\phantom{x}& -1 \\ - \cline{2-3} + \cline{2-3} -1 &\phantom{|}&\phantom{|}& -1 \\ + \cline{2-3} \end{array} - } + } \quad - { + { \begin{array}[t]{r|c|c|c|l} - \cline{2-4} 2 &\phantom{x}&\phantom{x}&\phantom{x}& 2 \\ - \cline{2-4} 0 &\phantom{x}& \multicolumn{3}{l}{0} \\ - \cline{2-2} + \cline{2-4} 2 &\phantom{|}&\phantom{|}&\phantom{|}& 2 \\ + \cline{2-4} 0 &\phantom{|}& \multicolumn{3 }{l}{ 0 } \\ + \cline{2-2} \end{array} - } + } \quad - { + { \begin{array}[t]{r|c|c|c|l} - \cline{2-4} -2 &\phantom{x}&\phantom{x}&\phantom{x}& -2 \\ - \cline{2-4} + \cline{2-4} -2 &\phantom{|}&\phantom{|}&\phantom{|}& -2 \\ + \cline{2-4} \end{array} - } + } \quad - { + { \begin{array}[t]{r|c|c|c|l} - \cline{2-4} -2 &\phantom{x}&\phantom{x}&\phantom{x}& -2 \\ - \cline{2-4} + \cline{2-4} -2 &\phantom{|}&\phantom{|}&\phantom{|}& -2 \\ + \cline{2-4} \end{array} } sage: latex(RC(partition_list=[[],[],[],[]])) # indirect doctest @@ -362,15 +369,25 @@ def check(self): """ for partition in self: for i, vac_num in enumerate(partition.vacancy_numbers): - assert vac_num >= partition.rigging[i], "rigging can be at most the vacancy number" + if vac_num < partition.rigging[i]: + #print "FAILED FOR:" + #print self + #print "at:", i, vac_num, partition.rigging[i] + raise ValueError("rigging can be at most the vacancy number") - def to_tensor_product_of_Kirillov_Reshetikhin_tableaux(self, display_steps=False, **options): + def to_tensor_product_of_Kirillov_Reshetikhin_tableaux(self, display_steps=False): r""" Perform the bijection from this rigged configuration to a tensor product of Kirillov-Reshetikhin tableaux given in [RigConBijection]_ for single boxes and with [BijectionLRT]_ and [BijectionDn]_ for multiple columns and rows. + .. NOTE:: + + This is only proven to be a bijection in types `A_n^{(1)}` + and `D_n^{(1)}`, as well as `\bigotimes_i B^{r_i,1}` and + `\bigotimes_i B^{1,s_i}` for general affine types. + INPUT: - ``display_steps`` -- (default: ``False``) Boolean which indicates @@ -408,97 +425,8 @@ def to_tensor_product_of_Kirillov_Reshetikhin_tableaux(self, display_steps=False sage: elt == ret True """ - #Letters = CrystalOfLetters(self.parent()._cartan_type.classical()) - Letters = CrystalOfLetters(self.parent()._cartan_type) - n = self.parent()._cartan_type.n - type = self.parent()._cartan_type.letter - - # Pass in a copy of our partitions since the bijection is destructive to it. - bijection = RCToKRTBijection(self) - - # This is technically bad, but because the first thing we do is append - # an empty list to ret_crystal_path, we correct this. We do it this - # way so that we do not have to remove an empty list after the - # bijection has been performed. - ret_crystal_path = [] - - for dim in self.parent().dims: - ret_crystal_path.append([]) - - # Iterate over each column - for dummy_var in range(dim[1]): - # Split off a new column if necessary - if bijection.cur_dims[0][1] > 1: - bijection.cur_dims[0][1] -= 1 - bijection.cur_dims.insert(0, [dim[0], 1]) - - # Perform the corresponding splitting map on rigged configurations - # All it does is update the vacancy numbers on the RC side - for a in range(n): - bijection._update_vacancy_numbers(a) - - # Check to see if we are a spinor - if type == 'D' and dim[0] >= n - 1: - if display_steps: - print "====================" - print repr(self.parent()(*bijection.cur_partitions)) - print "--------------------" - print ret_crystal_path - print "--------------------\n" - print "Applied doubling map" - bijection.doubling_map() - if dim[0] == n - 1: - if display_steps: - print "====================" - print repr(self.parent()(*bijection.cur_partitions)) - print "--------------------" - print ret_crystal_path - print "--------------------\n" - b = bijection.next_state(n) - if b == n: - b = -n - ret_crystal_path[-1].append(Letters(b)) # Append the rank - - while bijection.cur_dims[0][0] > 0: - if display_steps: - print "====================" - print repr(self.parent()(*bijection.cur_partitions)) - print "--------------------" - print ret_crystal_path - print "--------------------\n" - - bijection.cur_dims[0][0] -= 1 # This takes care of the indexing - b = bijection.next_state(bijection.cur_dims[0][0]) - - # Corrections for spinor - if type == 'D' and dim[0] == n and b == -n \ - and bijection.cur_dims[0][0] == n - 1: - b = -(n-1) - - # Make sure we have a crystal letter - ret_crystal_path[-1].append(Letters(b)) # Append the rank - - bijection.cur_dims.pop(0) # Pop off the leading column - - # Check to see if we were a spinor - if type == 'D' and dim[0] >= n-1: - if display_steps: - print "====================" - print repr(self.parent()(*bijection.cur_partitions)) - print "--------------------" - print ret_crystal_path - print "--------------------\n" - print "Applied halving map" - bijection.halving_map() - - # If you're curious about this, see the note in AbstractTensorProductOfKRTableaux._highest_weight_iter(). - # You should never call this option. - if "KRT_init_hack" in options: - return options["KRT_init_hack"](pathlist=ret_crystal_path) - - #return self.parent()._bijection_class(self.parent()._cartan_type, - return self.parent()._bijection_class(self.parent()._affine_ct, - self.parent().dims)(pathlist=ret_crystal_path) + from sage.combinat.rigged_configurations.bijection import RCToKRTBijection + return RCToKRTBijection(self).run(display_steps) def to_tensor_product_of_Kirillov_Reshetikhin_crystals(self, display_steps=False): r""" @@ -550,7 +478,7 @@ def nu(self): EXAMPLES:: - sage: RC = HighestWeightRiggedConfigurations(['A', 4, 1], [[2, 2]]) + sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) sage: RC(partition_list=[[2], [2,2], [2], [2]]).nu() [0[ ][ ]0 , -2[ ][ ]-2 @@ -572,13 +500,18 @@ def e(self, a): colabels fixed and increasing the new label by one. If no such string exists, then `e_a` is undefined. + .. TODO:: + + Implement `f_0` without appealing to tensor product of + KR tableaux. + INPUT: - ``a`` -- The index of the partition to remove a box. OUTPUT: - - The resulting rigged configuration element. + The resulting rigged configuration element. EXAMPLES:: @@ -596,9 +529,19 @@ def e(self, a): -1[ ]-1 """ - assert a in self.parent()._cartan_type.index_set() + if a not in self.parent()._cartan_type.index_set(): + raise ValueError("{} is not in the index set".format(a)) if a == 0: - raise NotImplementedError("Only classical crystal operators implemented") + try: + ret = self.to_tensor_product_of_Kirillov_Reshetikhin_tableaux().e(0) + if ret is None: + return None + return ret.to_rigged_configuration() + except NotImplementedError: + # We haven't implemented the bijection yet, so return None + # This is to make sure we can at least view it as a classical + # crystal if there is no bijection. + return None a -= 1 # For indexing @@ -623,6 +566,7 @@ def e(self, a): # Note that because the riggings are weakly decreasing, we will always # remove the last box on of a block k = new_list[rigging_index] + set_vac_num = False if k == 1: new_list.pop() new_vac_nums.pop() @@ -632,6 +576,10 @@ def e(self, a): cur_rigging += 1 # Properly sort the riggings j = rigging_index + 1 + # Update the vacancy number if the row lengths are the same + if j < num_rows and new_list[j] == new_list[rigging_index]: + new_vac_nums[rigging_index] = new_vac_nums[j] + set_vac_num = True while j < num_rows and new_list[j] == new_list[rigging_index] \ and new_rigging[j] > cur_rigging: new_rigging[j-1] = new_rigging[j] # Shuffle it along @@ -651,16 +599,14 @@ def e(self, a): new_vac_nums[i] += 2 new_rigging[i] += 2 - if k != 1: # If we did not remove a row + + if k != 1 and not set_vac_num: # If we did not remove a row nor found another row of length k-1 new_vac_nums[rigging_index] += 2 new_partitions.append(RiggedPartition(new_list, new_rigging, new_vac_nums)) - from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations - #ret_RC = self.__class__(RiggedConfigurations(self.parent()._cartan_type, self.parent().dims), - ret_RC = self.__class__(RiggedConfigurations(self.parent()._affine_ct, self.parent().dims), - *new_partitions) - if k != 1: # If we did not remove a row + ret_RC = self.__class__(self.parent(), new_partitions) + if k != 1 and not set_vac_num: # If we did not remove a row nor found another row of length k-1 # Update that row's vacancy number ret_RC[a].vacancy_numbers[rigging_index] = \ self.parent()._calc_vacancy_number(ret_RC.nu(), a, rigging_index) @@ -688,10 +634,8 @@ def _generate_partition_e(self, a, b, k): -1[ ]-1 """ - #cartan_matrix = self.parent()._cartan_type.classical().cartan_matrix() - cartan_matrix = self.parent()._cartan_type.cartan_matrix() # Check to make sure we will do something - if cartan_matrix[a][b] == 0: + if self.parent()._cartan_matrix[a][b] == 0: return self[b] new_list = self[b][:] @@ -699,7 +643,7 @@ def _generate_partition_e(self, a, b, k): new_rigging = self[b].rigging[:] # Update the vacancy numbers and the rigging - value = cartan_matrix[a][b] + value = self.parent()._cartan_matrix[a][b] for i in range(len(new_vac_nums)): if new_list[i] < k: break @@ -722,13 +666,18 @@ def f(self, a): of the resulting vacancy numbers are larger than the labels (i.e. it is an invalid rigged configuration), then `f_a` is undefined. + .. TODO:: + + Implement `f_0` without appealing to tensor product of + KR tableaux. + INPUT: - ``a`` -- The index of the partition to add a box. OUTPUT: - - The resulting rigged configuration element. + The resulting rigged configuration element. EXAMPLES:: @@ -747,9 +696,19 @@ def f(self, a): -1[ ]-1 """ - assert a in self.parent()._cartan_type.index_set() + if a not in self.parent()._cartan_type.index_set(): + raise ValueError("{} is not in the index set".format(a)) if a == 0: - raise NotImplementedError("Only classical crystal operators implemented") + try: + ret = self.to_tensor_product_of_Kirillov_Reshetikhin_tableaux().f(0) + if ret is None: + return None + return ret.to_rigged_configuration() + except NotImplementedError: + # We haven't implemented the bijection yet, so return None + # This is to make sure we can at least view it as a classical + # crystal if there is no bijection. + return None a -= 1 # For indexing @@ -815,10 +774,7 @@ def f(self, a): # Note that we do not need to sort the rigging since if there was a # smaller rigging in a larger row, then `k` would be larger. - from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations - #return self.__class__(RiggedConfigurations(self.parent()._cartan_type, self.parent().dims), - return self.__class__(RiggedConfigurations(self.parent()._affine_ct, self.parent().dims), - *new_partitions) + return self.__class__(self.parent(), new_partitions) def _generate_partition_f(self, a, b, k): r""" @@ -842,10 +798,8 @@ def _generate_partition_f(self, a, b, k): 0[ ]0 """ - #cartan_matrix = self.parent()._cartan_type.classical().cartan_matrix() - cartan_matrix = self.parent()._cartan_type.cartan_matrix() # Check to make sure we will do something - if cartan_matrix[a][b] == 0: + if self.parent()._cartan_matrix[a][b] == 0: return self[b] new_list = self[b][:] @@ -853,7 +807,7 @@ def _generate_partition_f(self, a, b, k): new_rigging = self[b].rigging[:] # Update the vacancy numbers and the rigging - value = cartan_matrix[a][b] + value = self.parent()._cartan_matrix[a][b] for i in range(len(new_vac_nums)): if new_list[i] <= k: break @@ -863,9 +817,10 @@ def _generate_partition_f(self, a, b, k): return(RiggedPartition(new_list, new_rigging, new_vac_nums)) - def cc(self): + @cached_method + def cocharge(self): r""" - Compute the cocharge statistic. + Compute the cocharge statistic of ``self``. Computes the cocharge statistic [CrysStructSchilling06]_ on this rigged configuration `(\nu, J)`. The cocharge statistic is defined as: @@ -879,35 +834,109 @@ def cc(self): EXAMPLES:: - sage: HWRC = HighestWeightRiggedConfigurations(['A', 3, 1], [[3, 2], [2,1], [1,1]]) - sage: HWRC(partition_list=[[1], [1], []]).cc() + sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [2,1], [1,1]]) + sage: RC(partition_list=[[1], [1], []]).cocharge() 1 """ cc = 0 rigging_sum = 0 - num_partitions = len(self) - #cartan_matrix = self.parent().cartan_type().classical().cartan_matrix() - cartan_matrix = self.parent().cartan_type().cartan_matrix() - - for a in range(num_partitions): - num_rows = len(self[a]) - for i in range(num_rows): - rigging_sum += self[a].rigging[i] - - for b in range(a, num_partitions): - temp_cc = 0 - num_rows_b = len(self[b]) - # TODO - Optimize this by using the fact that a partition is weakly decreasing - for j in range(num_rows_b): - temp_cc += min(self[a][i], self[b][j]) - - if a != b: - cc += (cartan_matrix[a][b] + cartan_matrix[b][a]) * temp_cc - else: # Assuming A_{ii} = 2 for all types - cc += 2 * temp_cc - + for a, p in enumerate(self): + for pos, i in enumerate(p._list): + # Add the rigging + rigging_sum += p.rigging[pos] + # Add the L matrix contribution + for dim in self.parent().dims: + if dim[0] == a + 1: + cc += min(dim[1], i) + # Subtract the vacancy number + cc -= p.vacancy_numbers[pos] return cc / 2 + rigging_sum + cc = cocharge + + @cached_method + def charge(self): + r""" + Compute the charge statistic of ``self``. + + Let `B` denote a set of rigged configurations. The *charge* `c` of + a rigged configuration `b` is computed as + + .. MATH:: + + c(b) = \max(cc(b) \mid b \in B) - cc(b). + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [2,1], [1,1]]) + sage: RC(partition_list=[[],[],[]]).charge() + 2 + sage: RC(partition_list=[[1], [1], []]).charge() + 1 + """ + B = self.parent() + if not hasattr(B, "_max_charge"): + B._max_charge = max(b.cocharge() for b in B) + return B._max_charge - self.cocharge() + + @cached_method + def classical_weight(self): + r""" + Return the classical weight of ``self``. + + The classical weight `\Lambda` of a rigged configuration is + + .. MATH:: + + \Lambda = \sum_{a \in \overline{I}} \sum_{i > 0} + i L_i^{(a)} \Lambda_a - \sum_{a \in \overline{I}} \sum_{i > 0} + i m_i^{(a)} \alpha_a + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['D',4,1], [[2,2]]) + sage: elt = RC(partition_list=[[2],[2,1],[1],[1]]) + sage: elt.classical_weight() + (0, 1, 1, 0) + + This agrees with the corresponding classical weight as KR tableaux:: + + sage: krt = elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); krt + [[2, 1], [3, -1]] + sage: krt.classical_weight() == elt.classical_weight() + True + + TESTS: + + We check the classical weights agree in an entire crystal:: + + sage: RC = RiggedConfigurations(['A',2,1], [[2,1], [1,1]]) + sage: passed_test = True + sage: for x in RC: + ....: y = x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + ....: if x.classical_weight() != y.classical_weight(): + ....: passed_test = False + ....: break + ... + sage: passed_test + True + """ + F = self.cartan_type().classical().root_system() + if F.ambient_space() is None: + WLR = F.weight_lattice() + else: + WLR = F.ambient_space() + la = WLR.fundamental_weights() + sum = WLR.zero() + for dim in self.parent().dims: + sum += dim[1] * la[dim[0]] + + alpha = WLR.simple_roots() + for a, partition in enumerate(self): + for row_len in partition: + sum -= row_len * alpha[a+1] # +1 for indexing + return sum + def get_vacancy_numbers(self, a): r""" Return the list of all vacancy numbers of the rigged partition @@ -932,6 +961,7 @@ def get_vacancy_number(self, a, i): INPUT: - ``a`` -- The index of the rigged partition. + - ``i`` -- The row of the rigged partition. EXAMPLES:: @@ -953,3 +983,160 @@ def get_vacancy_number(self, a, i): return None +class RCVirtualElement(RiggedConfigurationElement): + """ + Virtual rigged configuration elements. + + TESTS:: + + sage: RC = RiggedConfigurations(['C',2,1], [[1,2],[1,1],[2,1]]) + sage: elt = RC(partition_list=[[3],[2]]); elt + + 0[ ][ ][ ]0 + + 0[ ][ ]0 + sage: TestSuite(elt).run() + """ + def to_virtual_configuration(self): + """ + Return the corresponding rigged configuration in the virtual crystal. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['C',2,1], [[1,2],[1,1],[2,1]]) + sage: elt = RC(partition_list=[[3],[2]]); elt + + 0[ ][ ][ ]0 + + 0[ ][ ]0 + sage: elt.to_virtual_configuration() + + 0[ ][ ][ ]0 + + 0[ ][ ][ ][ ]0 + + 0[ ][ ][ ]0 + """ + return self.parent().to_virtual(self) + + def e(self, a): + """ + Return the action of `e_a` on ``self``. + + This works by lifting into the virtual configuration, then applying + + .. MATH:: + + \hat{e}_a = \prod_{j \in \iota(a)} e_j^{\gamma_j} + + and pulling back. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A',6,2], [[1,1]]*7) + sage: elt = RC(partition_list=[[1]*5,[2,1,1],[3,2]]) + sage: elt.e(3) + + 0[ ]0 + 0[ ]0 + 0[ ]0 + 0[ ]0 + 0[ ]0 + + 0[ ][ ]0 + 1[ ]1 + 1[ ]1 + + 1[ ][ ]1 + 1[ ]0 + + """ + vct = self.parent()._folded_ct + L = [] + gamma = vct.scaling_factors() + for i in vct.folding_orbit()[a]: + L.extend([i]*gamma[a]) + virtual_rc = self.parent().to_virtual(self).e_string(L) + if virtual_rc is None: + return None + return self.parent().from_virtual(virtual_rc) + + def f(self, a): + """ + Return the action of `f_a` on ``self``. + + This works by lifting into the virtual configuration, then applying + + .. MATH:: + + \hat{f}_a = \prod_{j \in \iota(a)} f_j^{\gamma_j} + + and pulling back. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A',6,2], [[1,1]]*7) + sage: elt = RC(partition_list=[[1]*5,[2,1,1],[2,1]], rigging_list=[[0]*5,[0,1,1],[1,0]]) + sage: elt.f(3) + + 0[ ]0 + 0[ ]0 + 0[ ]0 + 0[ ]0 + 0[ ]0 + + 1[ ][ ]1 + 1[ ]1 + 1[ ]1 + + -1[ ][ ][ ]-1 + 0[ ][ ]0 + + """ + vct = self.parent()._folded_ct + L = [] + gamma = vct.scaling_factors() + for i in vct.folding_orbit()[a]: + L.extend([i]*gamma[a]) + virtual_rc = self.parent().to_virtual(self).f_string(L) + if virtual_rc is None: + return None + return self.parent().from_virtual(virtual_rc) + + @cached_method + def cocharge(self): + r""" + Compute the cocharge statistic. + + Computes the cocharge statistic [OSS03]_ on this + rigged configuration `(\nu, J)` by computing the cocharge as a virtual + rigged configuration `(\hat{\nu}, \hat{J})` and then using the + identity `cc(\hat{\nu}, \hat{J}) = \gamma_0 cc(\nu, J)`. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['C', 3, 1], [[2,1], [1,1]]) + sage: RC(partition_list=[[1,1],[2,1],[1,1]]).cocharge() + 1 + """ + #return self.to_virtual_configuration().cocharge() / self.parent()._folded_ct.gamma[0] + vct = self.parent()._folded_ct + cc = 0 + rigging_sum = 0 + sigma = vct.folding_orbit() + gamma = vct.scaling_factors() + for a, p in enumerate(self): + t_check = len(sigma[a+1]) * gamma[a+1] / gamma[0] + for pos, i in enumerate(p._list): + # Add the rigging + rigging_sum += t_check * p.rigging[pos] + # Add the L matrix contribution + for dim in self.parent().dims: + if dim[0] == a + 1: + cc += t_check * min(dim[1], i) + # Subtract the vacancy number + cc -= t_check * p.vacancy_numbers[pos] + return cc / 2 + rigging_sum + + cc = cocharge + diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 19f44bba207..586819bb2d3 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -1,5 +1,5 @@ r""" -Rigged configurations +Rigged Configurations AUTHORS: @@ -13,13 +13,15 @@ Rigged configurations exist for all affine Kac-Moody Lie algebras. See for example [HKOTT2002]_. In Sage they are specified by providing a Cartan type and a list of -rectangular shapes `R`. Currently, they are only implemented for types `A_n^{(1)}` and -`D_n^{(1)}`. The list of all (highest weight) rigged configurations for given `R` is computed -via the Kleber algorithm (see also :class:`KleberTree`). - -There exists a crystal structure on the set of rigged configurations, see [CrysStructSchilling06]_. -The highest weight rigged configurations are those where all riggings are nonnegative. -The list of all rigged configurations is computed from the highest weight ones using the +rectangular shapes `R`. The list of all (highest weight) rigged configurations +for given `R` is computed via the (virtual) Kleber algorithm (see also +:class:`~sage.combinat.rigged_configurations.kleber_tree.KleberTree` and +:class:`~sage.combinat.rigged_configurations.kleber_tree.VirtualKleberTree`). + +There exists a crystal structure on the set of rigged configurations, see +[CrysStructSchilling06]_ and [OSS03]_. The highest weight rigged +configurations are those where all riggings are nonnegative. The list of +all rigged configurations is computed from the highest weight ones using the crystal operators. Rigged configurations are in bijection with tensor products of Kirillov-Reshetikhin tableaux, @@ -32,22 +34,24 @@ INPUT: -- ``cartan_type`` -- a Cartan type (currently only types `A_n^{(1)}` and `D_n^{(1)}` are supported) -- ``B`` -- a list of positive integer tuples `[r,s]` specifying the width `s` and height `r` of the sequence of rectangles +- ``cartan_type`` -- A Cartan type + +- ``B`` -- A list of positive integer pairs `(r,s)` specifying the width `s` + and height `r` of the sequence of rectangles EXAMPLES:: sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2], [1, 1]]) sage: RC - Rigged configurations of type ['A', 3, 1] and factors ((3, 2), (1, 2), (1, 1)) + Rigged configurations of type ['A', 3, 1] and factor(s) ((3, 2), (1, 2), (1, 1)) sage: RC = RiggedConfigurations(['A', 3, 1], [[2,1]]); RC - Rigged configurations of type ['A', 3, 1] and factors ((2, 1),) + Rigged configurations of type ['A', 3, 1] and factor(s) ((2, 1),) sage: RC.cardinality() 6 sage: len(RC.list()) == RC.cardinality() True - sage: sorted(RC.list()) # random + sage: RC.list() # random [ (/) @@ -90,7 +94,7 @@ A rigged configuration element with all riggings equal to the vacancy numbers can be created as follows:: sage: RC = RiggedConfigurations(['A', 3, 1], [[3,2], [2,1], [1,1], [1,1]]); RC - Rigged configurations of type ['A', 3, 1] and factors ((3, 2), (2, 1), (1, 1), (1, 1)) + Rigged configurations of type ['A', 3, 1] and factor(s) ((3, 2), (2, 1), (1, 1), (1, 1)) sage: elt = RC(partition_list=[[1],[],[]]); elt 0[ ]0 @@ -104,7 +108,7 @@ sage: RC = RiggedConfigurations(['D', 7, 1], [[3,3],[5,2],[4,3],[2,3],[4,4],[3,1],[1,4],[2,2]]) sage: elt = RC(partition_list=[[2],[3,2,1],[2,2,1,1],[2,2,1,1,1,1],[3,2,1,1,1,1],[2,1,1],[2,2]], - ... rigging_list=[[2],[1,0,0],[4,1,2,1],[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,0],[0,0]]) + ....: rigging_list=[[2],[1,0,0],[4,1,2,1],[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,0],[0,0]]) sage: elt 3[ ][ ]2 @@ -151,24 +155,44 @@ sage: output.to_rigged_configuration().to_tensor_product_of_Kirillov_Reshetikhin_tableaux() == output True +To get the highest weight rigged configurations, use the attribute +``module_generators``:: + + sage: RC = RiggedConfigurations(['D',4,1], [[2,1]]) + sage: for x in RC.module_generators: x + + (/) + + (/) + + (/) + + (/) + + 0[ ]0 + + 0[ ]0 + 0[ ]0 + + 0[ ]0 + + 0[ ]0 + TESTS:: - sage: RC = HighestWeightRiggedConfigurations(['A', 3, 1], [[3,2], [2,1], [1,1], [1,1]]) - sage: RC.cardinality() + sage: RC = RiggedConfigurations(['A', 3, 1], [[3,2], [2,1], [1,1], [1,1]]) + sage: len(RC.module_generators) 17 sage: RC = RiggedConfigurations(['D', 4, 1], [[1, 1]]) sage: RC.cardinality() 8 sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) - sage: c= RC.cardinality(); c + sage: c = RC.cardinality(); c 29 sage: K = KirillovReshetikhinCrystal(['D',4,1],2,1) sage: K.cardinality() == c True -.. TODO:: Implement affine crystal structure and remove conversion to classical - Cartan type. - REFERENCES: .. [HKOTT2002] G. Hatayama, A. Kuniba, M. Okado, T. Takagi, Z. Tsuboi. @@ -219,70 +243,292 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent -from sage.structure.element import Element, parent from sage.combinat.misc import IterableFunctionCall +from sage.rings.all import ZZ, QQ from sage.categories.finite_crystals import FiniteCrystals from sage.categories.regular_crystals import RegularCrystals from sage.combinat.root_system.cartan_type import CartanType -from sage.combinat.partition import Partition from sage.combinat.cartesian_product import CartesianProduct -from sage.combinat.rigged_configurations.kleber_tree import KleberTree -from sage.combinat.rigged_configurations.rigged_configuration_element import RiggedConfigurationElement +from sage.combinat.rigged_configurations.kleber_tree import KleberTree, VirtualKleberTree +from sage.combinat.rigged_configurations.rigged_configuration_element import RiggedConfigurationElement, \ + RCVirtualElement -class AbstractRiggedConfigurations(UniqueRepresentation, Parent): +# Note on implementation, this class is used for simply-laced types only +class RiggedConfigurations(Parent, UniqueRepresentation): r""" - Abstract root class for all rigged configurations. + Class of rigged configurations. - This class should never be created directly. Instead see - :class:`RiggedConfigurations`. - """ - # TODO: Have the option to have L_{rc} as a dictionary to reduce the memory - # footprint in case it is sparse (as a matrix) + Let `\overline{I}` denote the classical index set associated to the Cartan type + of the rigged configurations. A rigged configuration of multiplicity + array `L_i^{(a)}` and dominant weight `\Lambda` is a sequence of partitions + `\{ \nu^{(a)} \mid a \in \overline{I} \}` such that + + .. MATH:: + + \sum_{\overline{I} \times \mathbb{Z}_{>0}} i m_i^{(a)} \alpha_a + = \sum_{\overline{I} \times \mathbb{Z}_{>0}} i L_i^{(a)} \Lambda_a + - \Lambda + + where `\alpha_a` is a simple root, `\Lambda_a` is a fundamental weight, + and `m_i^{(a)}` is the number of rows of length `i` in the partition + `\nu^{(a)}`. + + Each sequence of partitions also comes with a sequence of values called + vacancy numbers and riggings. Vacancy numbers are computed based upon the + partitions and `L_i^{(a)}`, and the riggings must satisfy certain + conditions. For more, see [RigConBijection]_ [CrysStructSchilling06]_ + [BijectionLRT]_. + + They are in bijection with + :class:`TensorProductOfKirillovReshetikhinTableaux` of non-exceptional + affine types (see [RigConBijection]_, [BijectionLRT]_, [BijectionDn]_) and all + admit a classical crystal structure [CrysStructSchilling06]_. + + .. NOTE:: + + All non-simply-laced rigged configurations have not been proven to + give rise to aligned virtual crystals (i.e. have the correct crystal + structure or ismorphic as affine crystals to the tensor product of + Kirillov-Reshetikhin tableaux). + + INPUT: + + - ``cartan_type`` -- a Cartan type + + - ``B`` -- a list of positive integer tuples `(r,s)` corresponding to the + tensor factors in the bijection with tensor product of + Kirillov-Reshetikhin tableaux + + A rigged configuration element with all riggings equal to the vacancy numbers can be created as follows:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[3,2], [2,1], [1,1], [1,1]]); RC + Rigged configurations of type ['A', 3, 1] and factor(s) ((3, 2), (2, 1), (1, 1), (1, 1)) + sage: elt = RC(partition_list=[[1],[],[]]); elt + + 0[ ]0 + + (/) + + (/) + + + If on the other hand we also want to specify the riggings, this can be achieved as follows:: + + sage: RC = RiggedConfigurations(['D', 7, 1], [[3,3],[5,2],[4,3],[2,3],[4,4],[3,1],[1,4],[2,2]]) + sage: elt = RC(partition_list=[[2],[3,2,1],[2,2,1,1],[2,2,1,1,1,1],[3,2,1,1,1,1],[2,1,1],[2,2]], + ....: rigging_list=[[2],[1,0,0],[4,1,2,1],[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,0],[0,0]]) + sage: elt + + 3[ ][ ]2 + + 1[ ][ ][ ]1 + 2[ ][ ]0 + 1[ ]0 + + 4[ ][ ]4 + 4[ ][ ]1 + 3[ ]2 + 3[ ]1 + + 2[ ][ ]1 + 2[ ][ ]0 + 0[ ]0 + 0[ ]0 + 0[ ]0 + 0[ ]0 + + 0[ ][ ][ ]0 + 2[ ][ ]1 + 0[ ]0 + 0[ ]0 + 0[ ]0 + 0[ ]0 + + 0[ ][ ]0 + 0[ ]0 + 0[ ]0 + + 0[ ][ ]0 + 0[ ][ ]0 + + + To obtain the Kirillov-Reshetikhin (KR) tableau under the bijection between rigged configurations and KR + tableaux, we can type the following. This example was checked against Reiho Sakamoto's Mathematica program + on rigged configurations:: + + sage: output = elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); output + [[1, 1, 1], [2, 3, 3], [3, 4, -5]] (X) [[1, 1], [2, 2], [3, 3], [5, -6], [6, -5]] (X) + [[1, 1, 2], [2, 2, 3], [3, 3, 7], [4, 4, -7]] (X) [[1, 1, 1], [2, 2, 2]] (X) + [[1, 1, 1, 3], [2, 2, 3, 4], [3, 3, 4, 5], [4, 4, 5, 6]] (X) [[1], [2], [3]] (X) [[1, 1, 1, 1]] (X) [[1, 1], [2, 2]] + sage: elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux().to_rigged_configuration() == elt + True + sage: output.to_rigged_configuration().to_tensor_product_of_Kirillov_Reshetikhin_tableaux() == output + True + + We can also convert between rigged configurations and tensor products of + Kirillov-Reshetikhin crystals:: + + sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) + sage: elt = RC(partition_list=[[1],[1,1],[1],[1]]) + sage: tp_krc = elt.to_tensor_product_of_Kirillov_Reshetikhin_crystals(); tp_krc + [[]] + sage: ret = RC(tp_krc) + sage: ret == elt + True + + :: - def __init__(self, cartan_type, B, biject_class): + sage: RC = RiggedConfigurations(['D', 4, 1], [[4,1], [3,3]]) + sage: KR1 = KirillovReshetikhinCrystal(['D', 4, 1], 4, 1) + sage: KR2 = KirillovReshetikhinCrystal(['D', 4, 1], 3, 3) + sage: T = TensorProductOfCrystals(KR1, KR2) + sage: t = T[1]; t + [[++++, []], [+++-, [[1], [2], [4], [-4]]]] + sage: ret = RC(t) + sage: ret.to_tensor_product_of_Kirillov_Reshetikhin_crystals() + [[++++, []], [+++-, [[1], [2], [4], [-4]]]] + """ + @staticmethod + def __classcall_private__(cls, cartan_type, B): r""" - Initialize all common elements for rigged configurations. + Normalize the input arguments to ensure unique representation. - INPUT: + EXAMPLES:: + + sage: RC1 = RiggedConfigurations(CartanType(['A',3,1]), [[2,2]]) + sage: RC2 = RiggedConfigurations(['A',3,1], [(2,2)]) + sage: RC3 = RiggedConfigurations(['A',3,1], ((2,2),)) + sage: RC2 is RC1, RC3 is RC1 + (True, True) + """ + cartan_type = CartanType(cartan_type) + if not cartan_type.is_affine(): + raise ValueError("The Cartan type must be affine") - - ``cartan_type`` -- The Cartan type - - ``B`` -- A list of dimensions `[r,s]` for rectangles of height `r` and width `s` - - ``biject_class`` -- The class the bijection creates + # Standardize B input into a tuple of tuples + B = tuple(tuple(factor) for factor in B) - TESTS:: + if cartan_type.type() == 'BC': # Type `A_{2n}^{(2)}` + return RCTypeA2Even(cartan_type, B) + if cartan_type.dual().type() == 'BC': # Type 'A_{2n}^{(2)\dagger` + return RCTypeA2Dual(cartan_type, B) + # We check the classical type to account for A^{(1)}_1 which is not + # a virtual rigged configuration. + if not cartan_type.classical().is_simply_laced(): + return RCVirtual(cartan_type, B) - sage: RC = HighestWeightRiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2], [1, 1]]) # indirect doctest - sage: RC - Highest weight rigged configurations of type ['A', 3, 1] and factors ((3, 2), (1, 2), (1, 1)) - sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2], [1, 1]]) # indirect doctest + return super(RiggedConfigurations, cls).__classcall__(cls, cartan_type, B) + + def __init__(self, cartan_type, B): + r""" + Initialize the RiggedConfigurations class. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2], [1, 1]]) + sage: RC(partition_list=[[2],[2],[2]]) + + 1[ ][ ]1 + + 0[ ][ ]0 + + 0[ ][ ]0 + sage: RC(partition_list=[[2],[2],[2]], rigging_list=[[0],[0],[0]]) + + 1[ ][ ]0 + + 0[ ][ ]0 + + 0[ ][ ]0 sage: RC - Rigged configurations of type ['A', 3, 1] and factors ((3, 2), (1, 2), (1, 1)) + Rigged configurations of type ['A', 3, 1] and factor(s) ((3, 2), (1, 2), (1, 1)) + sage: TestSuite(RC).run() # long time (4s on sage.math, 2012) + sage: RC = RiggedConfigurations(['A',1,1], [[1,1], [1,1]]) + sage: TestSuite(RC).run() + sage: RC = RiggedConfigurations(['D', 4, 1], [[2,2]]) + sage: TestSuite(RC).run() # long time + sage: RC = RiggedConfigurations(['D', 4, 1], [[3,1]]) + sage: TestSuite(RC).run() # long time + sage: RC = RiggedConfigurations(['D', 4, 1], [[4,3]]) + sage: TestSuite(RC).run() # long time """ - assert cartan_type.is_affine() - self._affine_ct = cartan_type - # Force to classical since we do not have e_0 and f_0 yet - self._cartan_type = cartan_type.classical() + self._cartan_type = cartan_type self.dims = B - self._bijection_class = biject_class - self.rename("Rigged configurations of type %s and factors %s" % (cartan_type, B)) + # We store the cartan matrix for the vacancy number calculations for speed + self._cartan_matrix = self._cartan_type.classical().cartan_matrix() Parent.__init__(self, category=(RegularCrystals(), FiniteCrystals())) + self.rename("Rigged configurations of type %s and factor(s) %s" % (cartan_type, B)) + + def __iter__(self): + """ + Returns the iterator of ``self``. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[2,1], [1,1]]) + sage: g = RC.__iter__() + sage: g.next() + + (/) + + (/) + + (/) + + sage: g.next() + + (/) + + -1[ ]-1 + + (/) + + """ + index_set = self._cartan_type.classical().index_set() + from sage.combinat.backtrack import TransitiveIdeal + return TransitiveIdeal(lambda x: [x.f(i) for i in index_set], + self.module_generators).__iter__() + + use_half_width_boxes_type_B = True - def _highest_weight_iter(self): + @lazy_attribute + def module_generators(self): r""" - Iterate over the highest weight rigged configurations by moving through - the :class:`KleberTree` and then setting appropriate + Module generators for this set of rigged configurations. + + Iterate over the highest weight rigged configurations by moving + through the :class:`KleberTree` and then setting appropriate values of the partitions. EXAMPLES:: - sage: RC = RiggedConfigurations(['A', 2, 1], [[1, 1]]) # indirect doctest - sage: len([x for x in RC]) - 3 + sage: RC = RiggedConfigurations(['D', 4, 1], [[2,1]]) + sage: for x in RC.module_generators: x + + (/) + + (/) + + (/) + + (/) + + + 0[ ]0 + + 0[ ]0 + 0[ ]0 + + 0[ ]0 + + 0[ ]0 + """ - n = self._cartan_type.n + module_gens = [] + n = self._cartan_type.classical().rank() - for tree_node in self.kleber_tree: + for tree_node in self.kleber_tree(): shapes = [] cur = tree_node path_lambda = [cur.up_root.to_vector()] # Build the lambda values @@ -300,7 +546,7 @@ def _highest_weight_iter(self): # Start with a base to calculate the vacancy numbers # Make a copy just to be safe - base = self(partition_list=shapes[:]) + base = self.element_class(self, partition_list=shapes[:]) # Build out the blocks for the partition values vac_nums = [] @@ -317,13 +563,13 @@ def _highest_weight_iter(self): continue # Setup the first block - blockLen = partition[0] + block_len = partition[0] for i, rowLen in enumerate(partition): # If we've gone to a different sized block, then update the # values which change when moving to a new block size - if blockLen != rowLen: + if block_len != rowLen: blocks[-1].append([]) - blockLen = rowLen + block_len = rowLen blocks[-1][-1].append(partition.vacancy_numbers[i]) @@ -335,12 +581,15 @@ def _highest_weight_iter(self): # TODO Find a more efficient method without appealing to the CartesianProduct C = CartesianProduct(*L) for curBlocks in C: - yield self(KT_constructor=[shapes[:], self._blocks_to_values( - curBlocks[:]), vac_nums[:]]) + module_gens.append( self.element_class(self, KT_constructor=[shapes[:], + self._blocks_to_values(curBlocks[:]), + vac_nums[:]]) ) + + return tuple(module_gens) def _block_iterator(self, container): r""" - Iterate over all possible partitions. + Iterate over all possible riggings for a particular block. Helper iterator which iterates over all possible partitions contained within the container. @@ -351,12 +600,10 @@ def _block_iterator(self, container): TESTS:: - sage: HWRC = HighestWeightRiggedConfigurations(['A', 4, 1], [[2, 2]]) - sage: for x in HWRC._block_iterator([]): x - ... + sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) + sage: for x in RC._block_iterator([]): x [] - sage: for x in HWRC._block_iterator([2,3]): x - ... + sage: for x in RC._block_iterator([2,3]): x [0, 0] [1, 0] [1, 1] @@ -370,18 +617,18 @@ def _block_iterator(self, container): pos = 0 length = len(container) - retPart = [-1] * length + ret_part = [-1] * length while pos >= 0: - retPart[pos] += 1 + ret_part[pos] += 1 - if retPart[pos] > container[pos] or (pos != 0 and retPart[pos] > retPart[pos - 1]): - retPart[pos] = -1 + if ret_part[pos] > container[pos] or (pos != 0 and ret_part[pos] > ret_part[pos - 1]): + ret_part[pos] = -1 pos -= 1 else: pos += 1 if pos == length: - yield retPart[:] + yield ret_part[:] pos -= 1 def _blocks_to_values(self, blocks): @@ -394,23 +641,23 @@ def _blocks_to_values(self, blocks): TESTS:: - sage: HWRC = HighestWeightRiggedConfigurations(['A', 4, 1], [[2, 2]]) - sage: HWRC._blocks_to_values([[[2, 1]]]) + sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) + sage: RC._blocks_to_values([[[2, 1]]]) [[2, 1]] """ values = [] - for partBlock in blocks: - if len(partBlock) == 0: + for part_block in blocks: + if len(part_block) == 0: values.append([]) else: - values.append(partBlock[0][:]) # Need to make a copy - for block in partBlock[1:]: + values.append(part_block[0][:]) # Need to make a copy + for block in part_block[1:]: values[-1].extend(block) return values def _element_constructor_(self, *lst, **options): """ - Construct a RiggedConfigurationElement. + Construct a ``RiggedConfigurationElement``. Typically the user should not call this method since it does not check if it is a valid configuration. Instead the user should use the @@ -433,7 +680,7 @@ def _element_constructor_(self, *lst, **options): from sage.combinat.rigged_configurations.tensor_product_kr_tableaux_element import TensorProductOfKirillovReshetikhinTableauxElement if isinstance(lst[0], TensorProductOfKirillovReshetikhinTableauxElement): if self != lst[0].parent().rigged_configurations(): - raise ValueError("Incorrect bijection image.") + raise ValueError("incorrect bijection image") return lst[0].to_rigged_configuration() from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement @@ -445,17 +692,22 @@ def _element_constructor_(self, *lst, **options): krt_elt = KRT(*[x.to_Kirillov_Reshetikhin_tableau() for x in lst]) return krt_elt.to_rigged_configuration() - return self.element_class(self, *lst, **options) + return self.element_class(self, list(lst), **options) def _calc_vacancy_number(self, partitions, a, i, **options): r""" Calculate the vacancy number of the `i`-th row of the `a`-th rigged partition. + This assumes that `\gamma_a = 1` for all `a` and `(\alpha_a \mid + \alpha_b ) = A_{ab}`. + INPUT: - ``partitions`` -- The list of rigged partitions we are using + - ``a`` -- The rigged partition index + - ``i`` -- The row index of the `a`-th rigged partition TESTS:: @@ -487,37 +739,25 @@ def _calc_vacancy_number(self, partitions, a, i, **options): if dim[0] == a + 1: vac_num += min(dim[1], row_len) - #for j, value in enumerate(self._cartan_type.classical().cartan_matrix().row(a)): - for j, value in enumerate(self._cartan_type.cartan_matrix().row(a)): - vac_num -= value * partitions[j].get_num_cells_to_column(row_len) + for b, value in enumerate(self._cartan_matrix.row(a)): + vac_num -= value * partitions[b].get_num_cells_to_column(row_len) return vac_num - def cartan_type(self): - r""" - Return the classical Cartan type of this set of rigged configurations. - - EXAMPLES:: - - sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) - sage: RC.cartan_type() - ['A', 4] - """ - return(self._cartan_type) - - @lazy_attribute def kleber_tree(self): r""" - Return the underlying Kleber tree used to generate all admissible configurations. + Return the underlying Kleber tree used to generate all highest + weight rigged configurations. EXAMPLES:: - sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) - sage: RC.kleber_tree - Kleber tree of Cartan type ['A', 4, 1] and B = ((2, 2),) + sage: RC = RiggedConfigurations(['A',3,1], [[1,1], [2,1]]) + sage: RC.kleber_tree() + Kleber tree of Cartan type ['A', 3, 1] and B = ((1, 1), (2, 1)) """ - return KleberTree(self._affine_ct, self.dims) + return KleberTree(self._cartan_type, self.dims) + @cached_method def tensor_product_of_Kirillov_Reshetikhin_tableaux(self): """ Return the corresponding tensor product of Kirillov-Reshetikhin @@ -527,20 +767,128 @@ def tensor_product_of_Kirillov_Reshetikhin_tableaux(self): sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2]]) sage: RC.tensor_product_of_Kirillov_Reshetikhin_tableaux() - Tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and tableau shape(s) [[2, 2, 2], [2]] + Tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and factor(s) ((3, 2), (1, 2)) """ - return self._bijection_class(self._affine_ct, self.dims) + from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux + return TensorProductOfKirillovReshetikhinTableaux(self._cartan_type, self.dims) -class HighestWeightRiggedConfigurations(AbstractRiggedConfigurations): - r""" - Class for all highest weight rigged configurations. + def cardinality(self): + """ + Return the cardinality of ``self``. - A rigged configuration is highest weight if all of its vacancy numbers and - rigging values are non-negative. + EXAMPLES:: - For more on rigged configurations see :class:`RiggedConfigurations`. - """ + sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2]]) + sage: RC.cardinality() + 100 + sage: RC = RiggedConfigurations(['B', 3, 1], [[2,2],[1,2]]) + sage: RC.cardinality() + 5130 + sage: RC = RiggedConfigurations(['E', 7, 1], [[1,1]]) + sage: RC.cardinality() + 134 + """ + try: + return self.tensor_product_of_Kirillov_Reshetikhin_tableaux().cardinality() + except NotImplementedError: # Backup by direct counting + return ZZ(sum(1 for x in self)) + + @cached_method + def tensor_product_of_Kirillov_Reshetikhin_crystals(self): + """ + Return the corresponding tensor product of Kirillov-Reshetikhin + crystals. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[3,1],[2,2]]) + sage: RC.tensor_product_of_Kirillov_Reshetikhin_crystals() + Full tensor product of the crystals + [Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(3,1), + Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(2,2)] + """ + return self.tensor_product_of_Kirillov_Reshetikhin_tableaux().tensor_product_of_Kirillov_Reshetikhin_crystals() + + def fermionic_formula(self, q=None): + r""" + Return the fermoinic formula associated to ``self``. + + Given a set of rigged configurations `RC(\Lambda, L)`, the fermonic + formula is defined as: + + .. MATH:: + + M(\lambda, L; q) = \sum_{(\nu,J)} q^{cc(\nu, J)} + + where we sum over all classically highest weight rigged + configurations where `cc` is the :meth:`cocharge statistic + `. + This is known to reduce to + + .. MATH:: + + M(\lambda, L; q) = \sum_{\nu} q^{cc(\nu)} \prod_{(a,i) \in + I \times \ZZ} \begin{bmatrix} p_i^{(a)} + m_i^{(a)} \\ m_i^{(a)} + \end{bmatrix}_q. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[3,1],[2,2]]) + sage: RC.fermionic_formula() + q + 1 + sage: RC = RiggedConfigurations(['D', 4, 1], [[3,2],[4,1],[2,2]]) + sage: RC.fermionic_formula() + q^6 + 6*q^5 + 11*q^4 + 14*q^3 + 11*q^2 + 4*q + 1 + sage: RC = RiggedConfigurations(['E', 6, 1], [[2,2]]) + sage: RC.fermionic_formula() + q^2 + q + 1 + sage: RC = RiggedConfigurations(['B', 3, 1], [[3,1],[2,2]]) + sage: RC.fermionic_formula() + q^3 + 3*q^2 + 2*q + 1 + sage: RC = RiggedConfigurations(['C', 3, 1], [[3,1],[2,2]]) + sage: RC.fermionic_formula() + q^4 + 2*q^3 + 3*q^2 + 2*q + 1 + sage: RC = RiggedConfigurations(['D', 4, 2], [[3,1],[2,2]]) + sage: RC.fermionic_formula() + q^6 + 2*q^5 + 4*q^4 + 3*q^3 + 3*q^2 + q + 1 + """ + if q is None: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + q = PolynomialRing(ZZ, 'q').gen(0) + return sum([q**x.cc() for x in self.module_generators], ZZ.zero()) + + def _test_bijection(self, **options): + r""" + Test function to make sure that the bijection between rigged + configurations and Kirillov-Reshetikhin tableaux is correct. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[2,1],[1,1]]) + sage: RC._test_bijection() + """ + tester = self._tester(**options) + rejects = [] + for x in self: + y = x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + z = y.to_rigged_configuration() + if z != x: + rejects.append((x, z)) + + tester.assertTrue(len(rejects) == 0, "Bijection is not correct: %s"%rejects) + if len(rejects) != 0: + return rejects + +RiggedConfigurations.Element = RiggedConfigurationElement + +class RCVirtual(RiggedConfigurations): + r""" + Class of virtual rigged configurations. + + These are rigged configurations which lift to a virtual configuration. + For more on rigged configurations, see :class:`RiggedConfigurations`. + """ @staticmethod def __classcall_private__(cls, cartan_type, B): r""" @@ -548,391 +896,862 @@ def __classcall_private__(cls, cartan_type, B): EXAMPLES:: - sage: RC1 = HighestWeightRiggedConfigurations(CartanType(['A',3,1]), [[2,2]]) - sage: RC2 = HighestWeightRiggedConfigurations(['A',3,1], [(2,2)]) - sage: RC3 = HighestWeightRiggedConfigurations(['A',3,1], ((2,2),)) + sage: RC1 = RiggedConfigurations(CartanType(['A',4,2]), [[2,2]]) + sage: RC2 = RiggedConfigurations(['A',4,2], [(2,2)]) + sage: RC3 = RiggedConfigurations(['BC',2,2], ((2,2),)) sage: RC2 is RC1, RC3 is RC1 (True, True) """ cartan_type = CartanType(cartan_type) # Standardize B input into a tuple of tuples - assert B is not None - B = tuple(tuple(factor) for factor in B) - return super(HighestWeightRiggedConfigurations, cls).__classcall__(cls, cartan_type, B) + B = tuple(map(tuple, B)) + return super(RCVirtual, cls).__classcall__(cls, cartan_type, B) - def __init__(self, cartan_type, B): + def __init__(self, cartan_type, dims): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['C',2,1], [[1,1]]) + sage: TestSuite(RC).run() + sage: RC = RiggedConfigurations(['C',2,1], [[1,2],[1,1],[2,1]]); RC + Rigged configurations of type ['C', 2, 1] and factor(s) ((1, 2), (1, 1), (2, 1)) + sage: TestSuite(RC).run() # long time + sage: RC = RiggedConfigurations(['B',3,1], [[3,1],[1,1]]) + sage: TestSuite(RC).run() # long time + sage: RC = RiggedConfigurations(['D',4,2], [[2,1]]) + sage: TestSuite(RC).run() # long time + sage: RC = RiggedConfigurations(['A',5,2], [[2,1]]) + sage: TestSuite(RC).run() # long time + """ + self._folded_ct = cartan_type.as_folding() + RiggedConfigurations.__init__(self, cartan_type, dims) + + def _calc_vacancy_number(self, partitions, a, i, **options): r""" - Initialize the HighestWeightRiggedConfigurations class. + Calculate the vacancy number of the `i`-th row of the `a`-th rigged + partition. INPUT: - - ``cartan_type`` -- The (affine) Cartan type - - ``B`` -- A list of dimensions `[r,s]` for rectangles of height `r` and width `s` + - ``partitions`` -- The list of rigged partitions we are using - EXAMPLES:: + - ``a`` -- The rigged partition index - sage: RC = HighestWeightRiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2], [1, 1]]) - sage: RC - Highest weight rigged configurations of type ['A', 3, 1] and factors ((3, 2), (1, 2), (1, 1)) - sage: TestSuite(RC).run() + - ``i`` -- The row index of the `a`-th rigged partition + + TESTS:: + + sage: RC = RiggedConfigurations(['C', 4, 1], [[2, 1]]) + sage: elt = RC(partition_list=[[1], [2], [2], [1]]) + sage: RC._calc_vacancy_number(elt.nu(), 1, 0) + 0 """ + row_len = partitions[a][i] + + vac_num = 0 + if "B" in options: + for tableau in options["B"]: + # The + 1 is to convert between indexing methods + if len(tableau) == a + 1: + vac_num += min(row_len, len(tableau[0])) + elif "L" in options: + L = options["L"] + if L.has_key(a): + for kvp in L[a].items(): + vac_num += min(kvp[0], row_len) * kvp[1] + elif "dims" in options: + for dim in options["dims"]: + if dim[0] == a + 1: + vac_num += min(dim[1], row_len) + else: + for dim in self.dims: + if dim[0] == a + 1: + vac_num += min(dim[1], row_len) - from tensor_product_kr_tableaux import HighestWeightTensorProductOfKirillovReshetikhinTableaux # For circular imports - AbstractRiggedConfigurations.__init__(self, cartan_type, B, HighestWeightTensorProductOfKirillovReshetikhinTableaux) - self.rename("Highest weight rigged configurations of type %s and factors %s" % (cartan_type, B)) + gamma = self._folded_ct.scaling_factors() + for b, value in enumerate(self._cartan_matrix.row(a)): + vac_num -= value * partitions[b].get_num_cells_to_column(gamma[a+1]*row_len, gamma[b+1]) // gamma[b+1] - __iter__ = AbstractRiggedConfigurations._highest_weight_iter + return vac_num - def list(self): + @lazy_attribute + def module_generators(self): r""" - Create a list of the elements by using the iterator. + Module generators for this set of rigged configurations. + + Iterate over the highest weight rigged configurations by moving + through the :class:`KleberTree` and then setting appropriate + values of the partitions. EXAMPLES:: - sage: RC = HighestWeightRiggedConfigurations(['D', 4, 1], [[2, 2]]) - sage: len(RC.list()) == RC.cardinality() - True - sage: sorted(RC.list()) # random - [ - (/) + sage: RC = RiggedConfigurations(['C', 3, 1], [[1,2]]) + sage: for x in RC.module_generators: x (/) (/) (/) - , - 0[ ]0 - - 0[ ]0 - 0[ ]0 - 0[ ]0 - 0[ ]0 - , 0[ ][ ]0 0[ ][ ]0 - 0[ ][ ]0 - 0[ ][ ]0 + 0[ ]0 - 0[ ][ ]0 - ] """ - # This is needed to overwrite the list method from the FiniteCrystals - # category which generates the list via f_a applications. - return [x for x in self] + module_gens = [] + vec_len = len(self.kleber_tree().root.up_root.to_vector()) - @lazy_attribute - def module_generators(self): + for tree_node in self.kleber_tree(): + shapes = [] + cur = tree_node + path_lambda = [cur.up_root.to_vector()] # Build the lambda values + # Note that these are not same lambda as in the paper, + # but a less computational version. + while cur.parent_node is not None: + path_lambda.insert(0, (cur.parent_node.up_root - cur.up_root).to_vector()) + cur = cur.parent_node + + for a in range(vec_len): + shapes.append([]) + for i, cur_lambda in enumerate(path_lambda): + for j in range(cur_lambda[a]): + shapes[-1].insert(0, i) + + # Convert from the virtual rigged configuration + # As a special case, we do not need to do anything for type `A_{2n}^{(2)}` + sigma = self._folded_ct.folding_orbit() + shapes = shapes[:len(sigma)-1] + if self._cartan_type.type() != 'BC': + gammatilde = self._folded_ct.scaling_factors() + for a in range(1, len(sigma)): + index = sigma[a][0] - 1 # for indexing + for i in range(len(shapes[index])): + shapes[index][i] = shapes[index][i] // gammatilde[a] + + # Start with a base to calculate the vacancy numbers + # Make a copy just to be safe + base = self.element_class(self, partition_list=shapes[:]) + + # Build out the blocks for the partition values + vac_nums = [] + blocks = [] + L = [] + + for partition in base: + vac_nums.append(partition.vacancy_numbers) + blocks.append([[]]) + + # If the partition is empty, there's nothing to do + if len(partition) <= 0: + L.append([[]]) + continue + + # Setup the first block + block_len = partition[0] + for i, rowLen in enumerate(partition): + # If we've gone to a different sized block, then update the + # values which change when moving to a new block size + if block_len != rowLen: + blocks[-1].append([]) + block_len = rowLen + + blocks[-1][-1].append(partition.vacancy_numbers[i]) + + L2 = [] + for block in blocks[-1]: + L2.append(IterableFunctionCall(self._block_iterator, block)) + L.append(CartesianProduct(*L2)) + + # TODO Find a more efficient method without appealing to the CartesianProduct + C = CartesianProduct(*L) + for curBlocks in C: + module_gens.append( self.element_class(self, KT_constructor=[shapes[:], + self._blocks_to_values(curBlocks[:]), vac_nums[:]]) ) + + return tuple(module_gens) + + def kleber_tree(self): r""" - Module generators for this set of rigged configurations. + Return the underlying (virtual) Kleber tree used to generate all + highest weight rigged configurations. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['C',3,1], [[1,1], [2,1]]) + sage: RC.kleber_tree() + Virtual Kleber tree of Cartan type ['C', 3, 1] and B = ((1, 1), (2, 1)) + """ + return VirtualKleberTree(self._cartan_type, self.dims) - The module generators for rigged configurations are the highest weight - rigged configurations. For more info, see :class:`HighestWeightRiggedConfigurations`. + @lazy_attribute + def virtual(self): + """ + Return the corresponding virtual crystal. EXAMPLES:: - sage: RC = HighestWeightRiggedConfigurations(['D', 4, 1], [[2,1]]) - sage: for x in RC.module_generators: x - ... - - (/) - - (/) - - (/) + sage: RC = RiggedConfigurations(['C',2,1], [[1,2],[1,1],[2,1]]) + sage: RC + Rigged configurations of type ['C', 2, 1] and factor(s) ((1, 2), (1, 1), (2, 1)) + sage: RC.virtual + Rigged configurations of type ['A', 3, 1] and factor(s) ((1, 2), (3, 2), (1, 1), (3, 1), (2, 2)) + """ + gamma = self._folded_ct.scaling_factors() + sigma = self._folded_ct.folding_orbit() + virtual_dims = [] + for r,s in self.dims: + for a in sigma[r]: + virtual_dims.append([a, s*gamma[r]]) + return RiggedConfigurations(self._folded_ct._folding, virtual_dims) + + def to_virtual(self, rc): + """ + Convert ``rc`` into a rigged configuration in the virtual crystal. + + INPUT: + + - ``rc`` -- A rigged configuration element + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['C',2,1], [[1,2],[1,1],[2,1]]) + sage: elt = RC(partition_list=[[3],[2]]); elt - (/) + 0[ ][ ][ ]0 + 0[ ][ ]0 + sage: velt = RC.to_virtual(elt); velt - 0[ ]0 + 0[ ][ ][ ]0 - 0[ ]0 - 0[ ]0 + 0[ ][ ][ ][ ]0 - 0[ ]0 + 0[ ][ ][ ]0 + sage: velt.parent() + Rigged configurations of type ['A', 3, 1] and factor(s) ((1, 2), (3, 2), (1, 1), (3, 1), (2, 2)) + """ + gamma = self._folded_ct.scaling_factors() + sigma = self._folded_ct.folding_orbit() + n = self._folded_ct._folding.classical().rank() + partitions = [None] * n + riggings = [None] * n + vac_nums = [None] * n + # +/- 1 for indexing + for a in range(len(rc)): + for i in sigma[a+1]: + partitions[i-1] = [row_len*gamma[a+1] for row_len in rc[a]._list] + riggings[i-1] = [rig_val*gamma[a+1] for rig_val in rc[a].rigging] + vac_nums[i-1] = [vac_num*gamma[a+1] for vac_num in rc[a].vacancy_numbers] + return self.virtual.element_class(self.virtual, partition_list=partitions, + rigging_list=riggings, + vacancy_numbers_list=vac_nums) + + def from_virtual(self, vrc): + """ + Convert ``vrc`` in the virtual crystal into a rigged configution of + the original Cartan type. + + INPUT: + + - ``vrc`` -- A virtual rigged configuration + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['C',2,1], [[1,2],[1,1],[2,1]]) + sage: elt = RC(partition_list=[[3],[2]]) + sage: vrc_elt = RC.to_virtual(elt) + sage: ret = RC.from_virtual(vrc_elt); ret - 0[ ]0 + 0[ ][ ][ ]0 + 0[ ][ ]0 + sage: ret == elt + True """ - # This was separated out for speed; only needed for __iter__ - # lazy_attribute does not inherit - return [x for x in self._highest_weight_iter()] + gamma = self._folded_ct.scaling_factors() + sigma = self._folded_ct.folding_orbit() + n = self._cartan_type.classical().rank() + partitions = [None] * n + riggings = [None] * n + vac_nums = [None] * n + # +/- 1 for indexing + for a in range(n): + index = sigma[a+1][0]-1 + partitions[a] = [row_len//gamma[a+1] for row_len in vrc[index]._list] + riggings[a] = [rig_val//gamma[a+1] for rig_val in vrc[index].rigging] + vac_nums[a] = [vac_val//gamma[a+1] for vac_val in vrc[index].vacancy_numbers] + return self.element_class(self, partition_list=partitions, + rigging_list=riggings, vacancy_numbers_list=vac_nums) + + def _test_virtual_vacancy_numbers(self, **options): + """ + Test to make sure that the vacancy numbers obtained from the virtual + rigged configuration agree with the explicit computation of the + vacancy numbers done here. + EXAMPLES:: -HighestWeightRiggedConfigurations.Element = RiggedConfigurationElement + sage: RC = RiggedConfigurations(['B', 3, 1], [[2,1]]) + sage: RC._test_virtual_vacancy_numbers() + """ + tester = self._tester(**options) + for x in self: + parts_list = [p._list[:] for p in x] + elt = self.element_class(self, partition_list=parts_list) + for i, p in enumerate(elt): + for j, vac_num in enumerate(p.vacancy_numbers): + tester.assertTrue(vac_num == x[i].vacancy_numbers[j], + "Incorrect vacancy number: %s\nComputed: %s\nFor: %s"\ + %(x[i].vacancy_numbers[j],vac_num, x)) -class RiggedConfigurations(AbstractRiggedConfigurations): - r""" - Class of rigged configurations. +RCVirtual.Element = RCVirtualElement - Let `\overline{I}` denote the classical index set associated to the Cartan type - of the rigged configurations. A rigged configuration of multiplicity - array `L_i^{(a)}` and dominant weight `\Lambda` is a sequence of partitions - `\{ \nu^{(a)} \mid a \in \overline{I} \}` such that +class RCTypeA2Even(RCVirtual): + """ + Rigged configurations for type `A_{2n}^{(2)}`. - .. MATH:: + For more on rigged configurations, see :class:`RiggedConfigurations`. - \sum_{\overline{I} \times \mathbb{Z}_{>0}} i m_i^{(a)} \alpha_a - = \sum_{\overline{I} \times \mathbb{Z}_{>0}} i L_i^{(a)} \Lambda_a - - \Lambda + EXAMPLES:: - where `\alpha_a` is a simple root, `\Lambda_a` is a fundamental weight, - and `m_i^{(a)}` is the number of rows of length `i` in the partition - `\nu^{(a)}`. + sage: RC = RiggedConfigurations(['A',4,2], [[2,1], [1,2]]) + sage: RC.cardinality() + 150 + sage: RC = RiggedConfigurations(['A',2,2], [[1,1]]) + sage: RC.cardinality() + 3 + sage: RC = RiggedConfigurations(['A',4,2], [[2,1]]) + sage: TestSuite(RC).run() # long time + """ + @lazy_attribute + def virtual(self): + """ + Return the corresponding virtual crystal. - Each sequence of partitions also comes with a sequence of values called - vacancy numbers and riggings. Vacancy numbers are computed based upon the - partitions and `L_i^{(a)}`, and the riggings must satisfy certain - conditions. For more, see [RigConBijection]_ [CrysStructSchilling06]_ - [BijectionLRT]_. + EXAMPLES:: - They are in bijection with - :class:`TensorProductOfKirillovReshetikhinTableaux` of non-exceptional - affine types (see [RigConBijection]_, [BijectionLRT]_, [BijectionDn]_) and all - admit a classical crystal structure [CrysStructSchilling06]_. + sage: RC = RiggedConfigurations(['A',4,2], [[1,2],[1,1],[2,1]]) + sage: RC + Rigged configurations of type ['BC', 2, 2] and factor(s) ((1, 2), (1, 1), (2, 1)) + sage: RC.virtual + Rigged configurations of type ['A', 3, 1] and factor(s) ((1, 2), (3, 2), (1, 1), (3, 1), (2, 1), (2, 1)) + """ + sigma = self._folded_ct.folding_orbit() + n = len(sigma) - 1 + virtual_dims = [] + for r,s in self.dims: + if r == n: + virtual_dims.extend([[n, s], [n, s]]) + else: + for a in sigma[r]: + virtual_dims.append([a, s]) + return RiggedConfigurations(self._folded_ct._folding, virtual_dims) - INPUT: + def _calc_vacancy_number(self, partitions, a, i, **options): + r""" + Calculate the vacancy number of the `i`-th row of the `a`-th rigged + partition. - - ``cartan_type`` -- a Cartan type (currently only types `A_n^{(1)}` and `D_n^{(1)}` are supported) - - ``B`` -- a list of positive integer tuples `[r,s]` specifying the width `s` and height `r` of the sequence of rectangles + This is a special implementation for type `A_{2n}^{(2)}`. - A rigged configuration element with all riggings equal to the vacancy numbers can be created as follows:: + INPUT: - sage: RC = RiggedConfigurations(['A', 3, 1], [[3,2], [2,1], [1,1], [1,1]]); RC - Rigged configurations of type ['A', 3, 1] and factors ((3, 2), (2, 1), (1, 1), (1, 1)) - sage: elt = RC(partition_list=[[1],[],[]]); elt - - 0[ ]0 - - (/) - - (/) - - - If on the other hand we also want to specify the riggings, this can be achieved as follows:: - - sage: RC = RiggedConfigurations(['D', 7, 1], [[3,3],[5,2],[4,3],[2,3],[4,4],[3,1],[1,4],[2,2]]) - sage: elt = RC(partition_list=[[2],[3,2,1],[2,2,1,1],[2,2,1,1,1,1],[3,2,1,1,1,1],[2,1,1],[2,2]], - ... rigging_list=[[2],[1,0,0],[4,1,2,1],[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,0],[0,0]]) - sage: elt - - 3[ ][ ]2 - - 1[ ][ ][ ]1 - 2[ ][ ]0 - 1[ ]0 - - 4[ ][ ]4 - 4[ ][ ]1 - 3[ ]2 - 3[ ]1 - - 2[ ][ ]1 - 2[ ][ ]0 - 0[ ]0 - 0[ ]0 - 0[ ]0 - 0[ ]0 - - 0[ ][ ][ ]0 - 2[ ][ ]1 - 0[ ]0 - 0[ ]0 - 0[ ]0 - 0[ ]0 - - 0[ ][ ]0 - 0[ ]0 - 0[ ]0 - - 0[ ][ ]0 - 0[ ][ ]0 - + - ``partitions`` -- The list of rigged partitions we are using - To obtain the Kirillov-Reshetikhin (KR) tableau under the bijection between rigged configurations and KR - tableaux, we can type the following. This example was checked against Reiho Sakamoto's Mathematica program - on rigged configurations:: + - ``a`` -- The rigged partition index - sage: output = elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); output - [[1, 1, 1], [2, 3, 3], [3, 4, -5]] (X) [[1, 1], [2, 2], [3, 3], [5, -6], [6, -5]] (X) - [[1, 1, 2], [2, 2, 3], [3, 3, 7], [4, 4, -7]] (X) [[1, 1, 1], [2, 2, 2]] (X) - [[1, 1, 1, 3], [2, 2, 3, 4], [3, 3, 4, 5], [4, 4, 5, 6]] (X) [[1], [2], [3]] (X) [[1, 1, 1, 1]] (X) [[1, 1], [2, 2]] - sage: elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux().to_rigged_configuration() == elt - True - sage: output.to_rigged_configuration().to_tensor_product_of_Kirillov_Reshetikhin_tableaux() == output - True + - ``i`` -- The row index of the `a`-th rigged partition - We can also convert between rigged configurations and tensor products of - Kirillov-Reshetikhin crystals:: + TESTS:: - sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) - sage: elt = RC(partition_list=[[1],[1,1],[1],[1]]) - sage: tp_krc = elt.to_tensor_product_of_Kirillov_Reshetikhin_crystals(); tp_krc - [[]] - sage: ret = RC(tp_krc) - sage: ret == elt - True + sage: RC = RiggedConfigurations(['A', 4, 2], [[2, 1]]) + sage: elt = RC(partition_list=[[1], [2]]) + sage: RC._calc_vacancy_number(elt.nu(), 1, 0) + 0 + """ + row_len = partitions[a][i] - :: + vac_num = 0 + if "B" in options: + for tableau in options["B"]: + # The + 1 is to convert between indexing methods + if len(tableau) == a + 1: + vac_num += min(row_len, len(tableau[0])) + elif "L" in options: + L = options["L"] + if L.has_key(a): + for kvp in L[a].items(): + vac_num += min(kvp[0], row_len) * kvp[1] + elif "dims" in options: + for dim in options["dims"]: + if dim[0] == a + 1: + vac_num += min(dim[1], row_len) + else: + for dim in self.dims: + if dim[0] == a + 1: + vac_num += min(dim[1], row_len) - sage: RC = RiggedConfigurations(['D', 4, 1], [[4,1], [3,3]]) - sage: KR1 = KirillovReshetikhinCrystal(['D', 4, 1], 4, 1) - sage: KR2 = KirillovReshetikhinCrystal(['D', 4, 1], 3, 3) - sage: T = TensorProductOfCrystals(KR1, KR2) - sage: t = T[1]; t - [[++++, []], [+++-, [[1], [2], [4], [-4]]]] - sage: ret = RC(t) - sage: ret.to_tensor_product_of_Kirillov_Reshetikhin_crystals() - [[++++, []], [+++-, [[1], [2], [4], [-4]]]] - """ - @staticmethod - def __classcall_private__(cls, cartan_type, B): - r""" - Normalize the input arguments to ensure unique representation. + gamma = self._folded_ct.scaling_factors() + for b, value in enumerate(self._cartan_matrix.row(a)): + vac_num -= value * partitions[b].get_num_cells_to_column(row_len) // gamma[b+1] - EXAMPLES:: + return vac_num - sage: RC1 = RiggedConfigurations(CartanType(['A',3,1]), [[2,2]]) - sage: RC2 = RiggedConfigurations(['A',3,1], [(2,2)]) - sage: RC3 = RiggedConfigurations(['A',3,1], ((2,2),)) - sage: RC2 is RC1, RC3 is RC1 - (True, True) + def to_virtual(self, rc): """ - cartan_type = CartanType(cartan_type) - - # Standardize B input into a tuple of tuples - assert B is not None - B = tuple(tuple(factor) for factor in B) - return super(RiggedConfigurations, cls).__classcall__(cls, cartan_type, B) - - def __init__(self, cartan_type, B): - r""" - Initialize the RiggedConfigurations class. + Convert ``rc`` into a rigged configuration in the virtual crystal. INPUT: - - ``cartan_type`` -- The Cartan type (the crystal type and n value) - - ``B`` -- A list of dimensions. + - ``rc`` -- A rigged configuration element EXAMPLES:: - sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2], [1, 1]]) - sage: RC(partition_list=[[2],[2],[2]]) + sage: RC = RiggedConfigurations(['A',4,2], [[2,2]]) + sage: elt = RC(partition_list=[[1],[1]]); elt - 1[ ][ ]1 + -1[ ]-1 - 0[ ][ ]0 + 1[ ]1 - 0[ ][ ]0 - sage: RC(partition_list=[[2],[2],[2]], rigging_list=[[0],[0],[0]]) + sage: velt = RC.to_virtual(elt); velt - 1[ ][ ]0 + -1[ ]-1 - 0[ ][ ]0 + 2[ ]2 - 0[ ][ ]0 - sage: RC - Rigged configurations of type ['A', 3, 1] and factors ((3, 2), (1, 2), (1, 1)) - sage: TestSuite(RC).run() # long time (4s on sage.math, 2012) - sage: RC = RiggedConfigurations(['D', 4, 1], [[2,2]]) - sage: TestSuite(RC).run() # long time - sage: RC = RiggedConfigurations(['D', 4, 1], [[3,1]]) - sage: TestSuite(RC).run() # long time - sage: RC = RiggedConfigurations(['D', 4, 1], [[4,3]]) - sage: TestSuite(RC).run() # long time + -1[ ]-1 + + sage: velt.parent() + Rigged configurations of type ['A', 3, 1] and factor(s) ((2, 2), (2, 2)) + """ + gamma = self._folded_ct.scaling_factors() + sigma = self._folded_ct.folding_orbit() + n = self._folded_ct._folding.classical().rank() + partitions = [None] * n + riggings = [None] * n + vac_nums = [None] * n + # +/- 1 for indexing + for a in range(len(rc)): + for i in sigma[a+1]: + partitions[i-1] = [row_len for row_len in rc[a]._list] + riggings[i-1] = [rig_val*gamma[a+1] for rig_val in rc[a].rigging] + vac_nums[i-1] = [vac_num*gamma[a+1] for vac_num in rc[a].vacancy_numbers] + return self.virtual.element_class(self.virtual, partition_list=partitions, + rigging_list=riggings, + vacancy_numbers_list=vac_nums) + + def from_virtual(self, vrc): """ - from tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux # For circular imports - AbstractRiggedConfigurations.__init__(self, cartan_type, B, TensorProductOfKirillovReshetikhinTableaux) - self.rename("Rigged configurations of type %s and factors %s" % (cartan_type, B)) + Convert ``vrc`` in the virtual crystal into a rigged configution of + the original Cartan type. + + INPUT: + + - ``vrc`` -- A virtual rigged configuration element + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A',4,2], [[2,2]]) + sage: elt = RC(partition_list=[[1],[1]]) + sage: velt = RC.to_virtual(elt) + sage: ret = RC.from_virtual(velt); ret + + -1[ ]-1 + + 1[ ]1 + + sage: ret == elt + True + """ + gamma = self._folded_ct.scaling_factors() + sigma = self._folded_ct.folding_orbit() + n = self._cartan_type.classical().rank() + partitions = [None] * n + riggings = [None] * n + vac_nums = [None] * n + # +/- 1 for indexing + for a in range(n): + index = sigma[a+1][0]-1 + partitions[index] = [row_len for row_len in vrc[index]._list] + riggings[index] = [rig_val//gamma[a+1] for rig_val in vrc[index].rigging] + vac_nums[a] = [vac_val//gamma[a+1] for vac_val in vrc[index].vacancy_numbers] + return self.element_class(self, partition_list=partitions, + rigging_list=riggings, vacancy_numbers_list=vac_nums) + +class RCTypeA2Dual(RCTypeA2Even): + r""" + Rigged configurations of type `A_{2n}^{(2)\dagger}`. + + For more on rigged configurations, see :class:`RiggedConfigurations`. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(CartanType(['A',4,2]).dual(), [[1,2],[1,1],[2,1]]) + sage: RC + Rigged configurations of type ['BC', 2, 2]^* and factor(s) ((1, 2), (1, 1), (2, 1)) + sage: RC.cardinality() + 750 + sage: RC.virtual + Rigged configurations of type ['A', 3, 1] and factor(s) ((1, 2), (3, 2), (1, 1), (3, 1), (2, 1), (2, 1)) + sage: RC = RiggedConfigurations(CartanType(['A',2,2]).dual(), [[1,1]]) + sage: RC.cardinality() + 3 + sage: RC = RiggedConfigurations(CartanType(['A',4,2]).dual(), [[2,1]]) + sage: TestSuite(RC).run() # long time + """ + def _calc_vacancy_number(self, partitions, a, i, **options): + r""" + Calculate the vacancy number. A special case is needed for the `n`-th + partition for type `A_{2n}^{(2)\dagger}`. + + INPUT: + + - ``partitions`` -- The list of rigged partitions we are using + + - ``a`` -- The rigged partition index + + - ``i`` -- The row index of the `a`-th rigged partition + + TESTS:: + + sage: RC = RiggedConfigurations(CartanType(['A', 6, 2]).dual(), [[2,1]]) + sage: elt = RC(partition_list=[[1], [2], [2]]) + sage: RC._calc_vacancy_number(elt.nu(), 0, 0) + -1 + """ + if a != self._cartan_type.classical().rank()-1: + return RCTypeA2Even._calc_vacancy_number(self, partitions, a, i, **options) + row_len = partitions[a][i] + + vac_num = 0 + if "B" in options: + for tableau in options["B"]: + # The + 1 is to convert between indexing methods + if len(tableau) == a + 1: + vac_num += min(row_len, len(tableau[0])) + elif "L" in options: + L = options["L"] + if L.has_key(a): + for kvp in L[a].items(): + vac_num += min(kvp[0], row_len) * kvp[1] + elif "dims" in options: + for dim in options["dims"]: + if dim[0] == a + 1: + vac_num += min(dim[1], row_len) + else: + for dim in self.dims: + if dim[0] == a + 1: + vac_num += min(dim[1], row_len) + + for b, value in enumerate(self._cartan_matrix.row(a)): + vac_num -= value * partitions[b].get_num_cells_to_column(row_len) / 2 + + return vac_num @lazy_attribute def module_generators(self): r""" - Module generators for this set of rigged configurations. + Module generators for rigged configurations of type + `A_{2n}^{(2)\dagger}`. - The module generators for rigged configurations are the highest weight - rigged configurations. For more info, see :class:`HighestWeightRiggedConfigurations`. + Iterate over the highest weight rigged configurations by moving + through the :class:`KleberTree` and then setting appropriate + values of the partitions. This also skips rigged configurations where + `P_i^{(n)} < 1` when `i` is odd. EXAMPLES:: - sage: RC = RiggedConfigurations(['D', 4, 1], [[2,1]]) + sage: RC = RiggedConfigurations(CartanType(['A', 4, 2]).dual(), [[1,1]]) sage: for x in RC.module_generators: x - ... (/) (/) - (/) + """ + module_gens = [] + # This is for the non-simply-laced types + vec_len = len(self.kleber_tree().root.up_root.to_vector()) + + for tree_node in self.kleber_tree(): + shapes = [] + cur = tree_node + path_lambda = [cur.up_root.to_vector()] # Build the lambda values + # Note that these are not same lambda as in the paper, + # but a less computational version. + while cur.parent_node is not None: + path_lambda.insert(0, (cur.parent_node.up_root - cur.up_root).to_vector()) + cur = cur.parent_node + + for a in range(vec_len): + shapes.append([]) + for i, cur_lambda in enumerate(path_lambda): + for j in range(cur_lambda[a]): + shapes[-1].insert(0, i) + + # We are not simply-laced, so convert from the virtual rigged configuration + sigma = self._folded_ct.folding_orbit() + shapes = shapes[:len(sigma)-1] + # Nothing more to do since gamma[i] == 1 for all i >= 1 + + # Start with a base to calculate the vacancy numbers + # Make a copy just to be safe + base = self.element_class(self, partition_list=shapes[:]) + + # Check the special condition of odd rows in the n-th partition + invalid_RC = False + for i in range(len(base[-1]._list)): + if base[-1]._list[i] % 2 == 1 and base[-1].vacancy_numbers[i] < 1: + invalid_RC = True + break + # If it is invalid, skip it + if invalid_RC: + continue + + # Build out the blocks for the partition values + vac_nums = [] + blocks = [] + L = [] + + for partition in base[:-1]: + vac_nums.append(partition.vacancy_numbers) + blocks.append([[]]) + + # If the partition is empty, there's nothing to do + if len(partition) <= 0: + L.append([[]]) + continue + + # Setup the first block + block_len = partition[0] + for i, row_len in enumerate(partition): + # If we've gone to a different sized block, then update the + # values which change when moving to a new block size + if block_len != row_len: + blocks[-1].append([]) + block_len = row_len + + blocks[-1][-1].append(partition.vacancy_numbers[i]) + + L2 = [] + for block in blocks[-1]: + L2.append(IterableFunctionCall(self._block_iterator, block)) + L.append(CartesianProduct(*L2)) + + # Special case for the final tableau + partition = base[-1] + vac_nums.append(partition.vacancy_numbers) + + # If the partition is empty, there's nothing to do + if len(partition) <= 0: + L.append([[]]) + else: + # Setup the first block + block_len = partition[0] + blocks = [[]] + odd_block = [] + for i, row_len in enumerate(partition): + # If we've gone to a different sized block, then update the + # values which change when moving to a new block size + if block_len != row_len: + blocks.append([]) + odd_block.append(block_len % 2 == 1) + block_len = row_len + + blocks[-1].append(partition.vacancy_numbers[i]) + odd_block.append(block_len % 2 == 1) + + L2 = [] + for i, block in enumerate(blocks): + if odd_block[i]: + L2.append(IterableFunctionCall(self._block_iterator_n_odd, block)) + else: + L2.append(IterableFunctionCall(self._block_iterator, block)) + L.append(CartesianProduct(*L2)) + + # TODO Find a more efficient method without appealing to the CartesianProduct + C = CartesianProduct(*L) + for curBlocks in C: + module_gens.append( self.element_class(self, KT_constructor=[shapes[:], + self._blocks_to_values(curBlocks[:]), vac_nums[:]]) ) + + return tuple(module_gens) + + def _block_iterator_n_odd(self, container): + r""" + Iterate over all possible riggings for a block of odd length in the + `n`-th rigged partition for type `A_{2n}^{(2)\dagger}`. + + Helper iterator which iterates over all possible partitions of + `\frac{2k+1}{2}` sizes contained within the container. + + INPUT: + + - ``container`` -- A list the widths of the rows of the container + + TESTS:: + + sage: RC = RiggedConfigurations(CartanType(['A', 4, 2]).dual(), [[2, 2]]) + sage: for x in RC._block_iterator_n_odd([]): x + [] + sage: for x in RC._block_iterator_n_odd([2,2]): x + [1/2, 1/2] + [3/2, 1/2] + [3/2, 3/2] + """ + if len(container) == 0: + yield [] + return + + half = lambda x: QQ(x) / QQ(2) + pos = 0 + length = len(container) + ret_part = [-1] * length + while pos >= 0: + ret_part[pos] += 2 + + if ret_part[pos] > container[pos]*2 or (pos != 0 and ret_part[pos] > ret_part[pos - 1]): + ret_part[pos] = -1 + pos -= 1 + else: + pos += 1 + + if pos == length: + yield map(half, ret_part[:]) + pos -= 1 + + def to_virtual(self, rc): + """ + Convert ``rc`` into a rigged configuration in the virtual crystal. + + INPUT: + + - ``rc`` -- A rigged configuration element + + EXAMPLES:: + + sage: RC = RiggedConfigurations(CartanType(['A',4,2]).dual(), [[2,2]]) + sage: elt = RC(partition_list=[[1],[1]]); elt - (/) + -1[ ]-1 + 1[ ]1 - 0[ ]0 + sage: velt = RC.to_virtual(elt); velt - 0[ ]0 - 0[ ]0 + -1[ ]-1 - 0[ ]0 + 2[ ]2 - 0[ ]0 + -1[ ]-1 + sage: velt.parent() + Rigged configurations of type ['A', 3, 1] and factor(s) ((2, 2), (2, 2)) """ - # This was separated out for speed; only needed for __iter__ - # lazy_attribute does not inherit - return [x for x in self._highest_weight_iter()] + gammatilde = list(self._folded_ct.scaling_factors()) + gammatilde[-1] = 2 + sigma = self._folded_ct.folding_orbit() + n = self._folded_ct._folding.classical().rank() + partitions = [None] * n + riggings = [None] * n + vac_nums = [None] * n + # +/- 1 for indexing + for a in range(len(rc)): + for i in sigma[a+1]: + partitions[i-1] = [row_len for row_len in rc[a]._list] + riggings[i-1] = [rig_val*gammatilde[a+1] for rig_val in rc[a].rigging] + vac_nums[i-1] = [vac_num for vac_num in rc[a].vacancy_numbers] + return self.virtual.element_class(self.virtual, partition_list=partitions, + rigging_list=riggings, + vacancy_numbers_list=vac_nums) + + def from_virtual(self, vrc): + """ + Convert ``vrc`` in the virtual crystal into a rigged configution of + the original Cartan type. - def _test_bijection(self, **options): - r""" - Test function to make sure that the bijection between rigged - configurations and Kirillov-Reshetikhin tableaux is correct. + INPUT: + + - ``vrc`` -- A virtual rigged configuration element EXAMPLES:: - sage: RC = RiggedConfigurations(['A', 4, 1], [[3,2],[4,1]]) - sage: RC._test_bijection() + sage: RC = RiggedConfigurations(CartanType(['A',4,2]).dual(), [[2,2]]) + sage: elt = RC(partition_list=[[1],[1]]) + sage: velt = RC.to_virtual(elt) + sage: ret = RC.from_virtual(velt); ret + + -1[ ]-1 + + 1[ ]1 + + sage: ret == elt + True """ - tester = self._tester(**options) - rejects = [] - for x in self: - y = x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() - z = y.to_rigged_configuration() - if z != x: - rejects.append((x, z)) + gammatilde = list(self._folded_ct.scaling_factors()) + gammatilde[-1] = QQ(2) + sigma = self._folded_ct.folding_orbit() + n = self._cartan_type.classical().rank() + partitions = [None] * n + riggings = [None] * n + vac_nums = [None] * n + # +/- 1 for indexing + for a in range(n): + index = sigma[a+1][0]-1 + partitions[index] = [row_len for row_len in vrc[index]._list] + riggings[index] = [rig_val/gammatilde[a+1] for rig_val in vrc[index].rigging] + vac_nums[a] = [vac_val for vac_val in vrc[index].vacancy_numbers] + return self.element_class(self, partition_list=partitions, + rigging_list=riggings, vacancy_numbers_list=vac_nums) - tester.assertTrue(len(rejects) == 0, "Bijection is not correct: %s"%rejects) - if len(rejects) != 0: - return rejects +# For experimentation purposes only. +# I'm keeping this for when we implement the R-matrix in general +def R_matrix(ct, RS1, RS2, only_highest_weight=False): + r""" + Output pairs of Kirillov-Reshetikhin tableaux under the action of the + (combinatorial) `R`-matrix. - def tensor_product_of_Kirillov_Reshetikhin_crystals(self): - """ - Return the corresponding tensor product of Kirillov-Reshetikhin - crystals. + INPUT: - EXAMPLES:: + - ``ct`` -- A Cartan type + - ``RS1``, ``RS2`` -- Pairs of `(r_i, s_i)` for `i = 1,2` for the two + tensor factors `B^{r_1, s_1} \otimes B^{r_2, s_2}` + - ``only_highest_weight`` -- Output only the highest weight elements - sage: RC = RiggedConfigurations(['A', 3, 1], [[3,1],[2,2]]) - sage: RC.tensor_product_of_Kirillov_Reshetikhin_crystals() - Full tensor product of the crystals [Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(3,1), - Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(2,2)] - """ - return self._bijection_class(self._affine_ct, self.dims).tensor_product_of_Kirillov_Reshetikhin_crystals() + EXAMPLES:: -RiggedConfigurations.Element = RiggedConfigurationElement + sage: from sage.combinat.rigged_configurations.rigged_configurations import R_matrix + sage: L = R_matrix(['D',4,1], [2,1], [1,1]) + sage: len(L) + 232 + + Check that the `R`-matrix is the identity on `B \otimes B`:: + + sage: L = R_matrix(['A',2,1], [2,3], [2,3]) + sage: len(L) + 100 + sage: all(x == y for x,y in L) + True + """ + ct = CartanType(ct) + RC = RiggedConfigurations(ct, [RS1, RS2]) + RC2 = RiggedConfigurations(ct, [RS2, RS1]) + ret_list = [] + if only_highest_weight: + L = RC.module_generators + else: + L = RC + for x in L: + x2 = RC2(*x) + ret_list.append([x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(), + x2.to_tensor_product_of_Kirillov_Reshetikhin_tableaux()]) + return ret_list -# For experimentation purposes only. -# I'm keeping this for when we implement the R-matrix in general -#def R_matrix_test(n, L1, L2): -# RC = HighestWeightRiggedConfigurations(['D', n, 1], [L1, L2]) -# RC2 = HighestWeightRiggedConfigurations(['D', n, 1], [L2, L1]) -# ret_list = [] -# for x in RC: -# x2 = RC2(*x) -# ret_list.append([x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(), -# x2.to_tensor_product_of_Kirillov_Reshetikhin_tableaux()]) -# return ret_list diff --git a/src/sage/combinat/rigged_configurations/rigged_partition.py b/src/sage/combinat/rigged_configurations/rigged_partition.py index b9a150ac21a..51099e5c9c0 100644 --- a/src/sage/combinat/rigged_configurations/rigged_partition.py +++ b/src/sage/combinat/rigged_configurations/rigged_partition.py @@ -1,5 +1,5 @@ r""" -Rigged partition +Rigged Partitions Class and methods of the rigged partition which are used by the rigged configuration class. This is an internal class used by the rigged @@ -18,7 +18,9 @@ - Travis Scrimshaw (2010-09-26): Initial version -.. TODO:: Convert this to using `m_i` (multiplicities) with a dictionary? +.. TODO:: + + Convert this to using multiplicities `m_i` (perhaps with a dictionary?)? """ #***************************************************************************** @@ -37,6 +39,7 @@ #***************************************************************************** from sage.combinat.combinat import CombinatorialObject +from sage.misc.latex import latex class RiggedPartition(CombinatorialObject): r""" @@ -68,7 +71,9 @@ def __init__(self, shape=None, rigging_list=None, vacancy_nums=None): INPUT: - ``shape`` -- (Default: ``None``) The shape + - ``rigging_list`` -- (Default: ``None``) The riggings + - ``vacancy_nums`` -- (Default: ``None``) The vacancy numbers TESTS:: @@ -92,7 +97,7 @@ def __init__(self, shape=None, rigging_list=None, vacancy_nums=None): if vacancy_nums is not None: if len(shape) != len(vacancy_nums): - raise ValueError + raise ValueError("Mismatch between shape and vacancy numbers") self.vacancy_numbers = list(vacancy_nums) else: @@ -101,7 +106,7 @@ def __init__(self, shape=None, rigging_list=None, vacancy_nums=None): if rigging_list is not None: if len(shape) != len(rigging_list): - raise ValueError + raise ValueError("Mismatch between shape and rigging list") self.rigging = list(rigging_list) else: @@ -115,14 +120,14 @@ def _repr_(self): sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) sage: elt = RC(partition_list=[[2],[2,2],[2,1],[2]])[2] - sage: elt # indirect doctest - 0[ ][ ]0 + sage: elt + 0[ ][ ]0 -1[ ]-1 sage: Partitions.global_options(convention="french") - sage: elt # indirect doctest + sage: elt -1[ ]-1 - 0[ ][ ]0 + 0[ ][ ]0 sage: Partitions.global_options.reset() """ @@ -135,30 +140,30 @@ def _repr_(self): itr = reversed(list(enumerate(self._list))) else: itr = enumerate(self._list) - retStr = "" + ret_str = "" + vac_num_width = max(len(str(vac_num)) for vac_num in self.vacancy_numbers) for i, val in itr: - retStr += str(self.vacancy_numbers[i]) - retStr += "[ ]"*val - retStr += str(self.rigging[i]) - retStr += "\n" - return(retStr) + ret_str += ("{:>" + str(vac_num_width) + "}").format(self.vacancy_numbers[i]) + ret_str += "[ ]"*val + ret_str += str(self.rigging[i]) + ret_str += "\n" + return(ret_str) def _latex_(self): - """ + r""" Returns LaTeX representation of ``self``. EXAMPLES:: sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) - sage: latex(RC(partition_list=[[2],[2,2],[2,1],[2]])[2]) # indirect doctest + sage: latex(RC(partition_list=[[2],[2,2],[2,1],[2]])[2]) { \begin{array}[t]{r|c|c|l} - \cline{2-3} 0 &\phantom{x}&\phantom{x}& 0 \\ - \cline{2-3} -1 &\phantom{x}& \multicolumn{2}{l}{-1} \\ - \cline{2-2} + \cline{2-3} 0 &\phantom{|}&\phantom{|}& 0 \\ + \cline{2-3} -1 &\phantom{|}& \multicolumn{2 }{l}{ -1 } \\ + \cline{2-2} \end{array} } - """ num_rows = len(self._list) if num_rows == 0: @@ -167,22 +172,22 @@ def _latex_(self): num_cols = self._list[0] ret_string = ("{\n\\begin{array}[t]{r|" + "c|"*num_cols + "l}\n" + "\\cline{2-" + repr(1 + num_cols) + "} " - + repr(self.vacancy_numbers[0])) + + latex(self.vacancy_numbers[0])) for i, row_len in enumerate(self._list): - ret_string += " &" + "\\phantom{x}&"*row_len + ret_string += " &" + "\\phantom{|}&"*row_len if num_cols == row_len: - ret_string += " " + repr(self.rigging[i]) + ret_string += " " + latex(self.rigging[i]) else: ret_string += " \\multicolumn{" + repr(num_cols - row_len + 1) - ret_string += "}{l}{" + repr(self.rigging[i]) + "}" + ret_string += "}{l}{" + latex(self.rigging[i]) + "}" ret_string += " \\\\\n" ret_string += "\\cline{2-" + repr(1 + row_len) + "} " if i != num_rows - 1 and row_len != self._list[i + 1]: - ret_string += repr(self.vacancy_numbers[i + 1]) + ret_string += latex(self.vacancy_numbers[i + 1]) ret_string += "\n\\end{array}\n}" return ret_string @@ -195,11 +200,11 @@ def _clone(self): sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) sage: RP = RC(partition_list=[[2],[2,2],[2,1],[2]])[2]; RP - 0[ ][ ]0 + 0[ ][ ]0 -1[ ]-1 sage: RP2 = RP._clone(); RP2 - 0[ ][ ]0 + 0[ ][ ]0 -1[ ]-1 sage: RP == RP2 @@ -207,7 +212,7 @@ def _clone(self): sage: RP is RP2 False """ - return(RiggedPartition(self._list[:], self.rigging[:], self.vacancy_numbers[:])) + return self.__class__(self._list[:], self.rigging[:], self.vacancy_numbers[:]) def __eq__(self, rhs): r""" @@ -228,7 +233,7 @@ def __eq__(self, rhs): # Should we move these functions to the CP -> RC bijections? - def get_num_cells_to_column(self, end_column): + def get_num_cells_to_column(self, end_column, t=1): r""" Get the number of cells in all columns before the ``end_column``. @@ -236,6 +241,8 @@ def get_num_cells_to_column(self, end_column): - ``end_column`` -- The index of the column to end at + - ``t`` -- The scaling factor + OUTPUT: - The number of cells @@ -250,12 +257,14 @@ def get_num_cells_to_column(self, end_column): 3 sage: RP.get_num_cells_to_column(3) 3 + sage: RP.get_num_cells_to_column(3, 2) + 5 """ sum_cells = 0 # Sum up from the reverse (the smallest row sizes) i = len(self._list) - 1 - while i >= 0 and self._list[i] < end_column: - sum_cells += self._list[i] + while i >= 0 and self._list[i]*t < end_column: + sum_cells += self._list[i]*t i -= 1 # Add the remaining cells @@ -288,17 +297,18 @@ def insert_cell(self, max_width): sage: RP.insert_cell(2) 2 sage: RP - 0[ ][ ][ ]None + 0[ ][ ][ ]None -1[ ]-1 """ - maxPos = -1 - for i, vacNum in enumerate(self.vacancy_numbers): - if self._list[i] <= max_width and vacNum == self.rigging[i]: - maxPos = i - break - - if maxPos == -1: # No singular values, then add a new row + max_pos = -1 + if max_width > 0: + for i, vac_num in enumerate(self.vacancy_numbers): + if self._list[i] <= max_width and vac_num == self.rigging[i]: + max_pos = i + break + + if max_pos == -1: # No singular values, then add a new row self._list.append(1) self.vacancy_numbers.append(None) # Go through our partition until we find a length of greater than 1 @@ -306,13 +316,13 @@ def insert_cell(self, max_width): while i >= 0 and self._list[i] == 1: i -= 1 self.rigging.insert(i + 1, None) - return + return 0 - self._list[maxPos] += 1 - self.rigging[maxPos] = None # State that we've changed this row - return self._list[maxPos] - 1 + self._list[max_pos] += 1 + self.rigging[max_pos] = None # State that we've changed this row + return self._list[max_pos] - 1 - def remove_cell(self, row): + def remove_cell(self, row, num_cells=1): r""" Removes a cell at the specified ``row``. @@ -322,7 +332,9 @@ def remove_cell(self, row): INPUT: - - ``row`` -- The row to remove the cell from. + - ``row`` -- The row to remove the cell from + + - ``num_cells`` -- (Default: 1) The number of cells to remove OUTPUT: @@ -336,52 +348,191 @@ def remove_cell(self, row): sage: RP.remove_cell(0) 0 sage: RP - 0[ ]0 + 0[ ]0 -1[ ]-1 """ if row is None: return None - if self._list[row] == 1: + if self._list[row] <= num_cells: self._list.pop(row) self.vacancy_numbers.pop(row) self.rigging.pop(row) return None - else: - # Find the beginning of the next block - block_len = self._list[row] - if row + 1 == len(self._list): - # If we are at the end, just do a simple remove - self._list[row] -= 1 - return row - else: - for i in range(row + 1, len(self._list)): - if self._list[i] != block_len: - if i == row + 1: - # If the next row is a block change, just reduce by 1 - self._list[row] -= 1 - return row - - # Otherwise we need to "move" the row - self._list.insert(i, block_len - 1) - # These should be updated (so there should be no need to carry them over) - self.vacancy_numbers.insert(i, None) - self.rigging.insert(i, None) - - self._list.pop(row) - self.vacancy_numbers.pop(row) - self.rigging.pop(row) - return i - 1 - - # We need to "move" the row to the end of the partition + + # Find the beginning of the next block we want + block_len = self._list[row] - num_cells # The length of the desired block + if row + 1 == len(self._list): + # If we are at the end, just do a simple remove + self._list[row] = block_len + return row + + for i in range(row + 1, len(self._list)): + if self._list[i] <= block_len: + if i == row + 1: + # If the next row is a block change, just reduce by num_cells + self._list[row] = block_len + return row + + # Otherwise we need to "move" the row + self._list.insert(i, block_len) + # These should be updated (so there should be no need to carry them over) + self.vacancy_numbers.insert(i, None) + self.rigging.insert(i, None) + self._list.pop(row) self.vacancy_numbers.pop(row) self.rigging.pop(row) + return i - 1 + + # We need to "move" the row to the end of the partition + self._list.pop(row) + self.vacancy_numbers.pop(row) + self.rigging.pop(row) + + self._list.append(block_len) + # Placeholders as above + self.vacancy_numbers.append(None) + self.rigging.append(None) + return len(self._list) - 1 + +class RiggedPartitionTypeB(RiggedPartition): + r""" + Rigged partitions for type `B_n^{(1)}` which has special printing rules + which comes from the fact that the `n`-th partition can have columns of + width `\frac{1}{2}`. + """ + def __init__(self, arg0, arg1=None, arg2=None): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: RP = sage.combinat.rigged_configurations.rigged_partition.RiggedPartition([2,1], [0,0], [1, 0]) + sage: B = sage.combinat.rigged_configurations.rigged_partition.RiggedPartitionTypeB(RP); B + 1[][]0 + 0[]0 + + sage: TestSuite(B).run() + """ + if arg1 is not None: + RiggedPartition.__init__(self, arg0, arg1, arg2) + return + + RiggedPartition.__init__(self, + arg0._list, + arg0.rigging, + arg0.vacancy_numbers) + + def _repr_(self): + """ + Return a string representation of ``self``. + + INPUT: + + - ``half_width_boxes`` -- (Default: ``True``) Display the partition + using half width boxes + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['B', 2, 1], [[2, 2]]) + sage: elt = RC(partition_list=[[2],[2,1]])[1] + sage: elt + -2[][]-2 + -2[]-2 + + sage: RiggedConfigurations.use_half_width_boxes_type_B = False + sage: elt + -2[ ][ ]-2 + -2[ ]-2 + + sage: RiggedConfigurations.use_half_width_boxes_type_B = True + """ + # If it is empty, return saying so + if len(self._list) == 0: + return("(/)\n") - self._list.append(block_len - 1) - # Placeholders as above - self.vacancy_numbers.append(None) - self.rigging.append(None) - return len(self._list) - 1 + from sage.combinat.partition import Partitions + if Partitions.global_options("convention") == "french": + itr = reversed(list(enumerate(self._list))) + else: + itr = enumerate(self._list) + ret_str = "" + + from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations + if RiggedConfigurations.use_half_width_boxes_type_B: + box_str = "[]" + else: + box_str = "[ ]" + + vac_num_width = max(len(str(vac_num)) for vac_num in self.vacancy_numbers) + for i, val in itr: + ret_str += ("{:>" + str(vac_num_width) + "}").format(self.vacancy_numbers[i]) + ret_str += box_str*val + ret_str += str(self.rigging[i]) + ret_str += "\n" + return(ret_str) + + def _latex_(self): + r""" + Returns LaTeX representation of ``self``. + + INPUT: + + - ``half_width_boxes`` -- (Default: ``True``) Display the partition + using half width boxes + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['B', 2, 1], [[1, 1]]) + sage: RP = RC(partition_list=[[],[2]])[1] + sage: latex(RP) + { + \begin{array}[t]{r|@{}c@{}|@{}c@{}|l} + \cline{2-3} -4 &\phantom{a}&\phantom{a}& -4 \\ + \cline{2-3} + \end{array} + } + sage: RiggedConfigurations.use_half_width_boxes_type_B = False + sage: latex(RP) + { + \begin{array}[t]{r|@{}c@{}|@{}c@{}|l} + \cline{2-3} -4 &\phantom{X|}&\phantom{X|}& -4 \\ + \cline{2-3} + \end{array} + } + sage: RiggedConfigurations.use_half_width_boxes_type_B = True + """ + num_rows = len(self._list) + if num_rows == 0: + return "{\\emptyset}" + + from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations + if RiggedConfigurations.use_half_width_boxes_type_B: + box_str = "\\phantom{a}&" + else: + box_str = "\\phantom{X|}&" + + num_cols = self._list[0] + ret_string = ("{\n\\begin{array}[t]{r|" + "@{}c@{}|"*num_cols + "l}\n" + + "\\cline{2-" + repr(1 + num_cols) + "} " + + repr(self.vacancy_numbers[0])) + for i, row_len in enumerate(self._list): + ret_string += " &" + box_str*row_len + + if num_cols == row_len: + ret_string += " " + latex(self.rigging[i]) + else: + ret_string += " \\multicolumn{" + repr(num_cols - row_len + 1) + ret_string += "}{l}{" + latex(self.rigging[i]) + "}" + + ret_string += " \\\\\n" + + ret_string += "\\cline{2-" + repr(1 + row_len) + "} " + if i != num_rows - 1 and row_len != self._list[i + 1]: + ret_string += repr(self.vacancy_numbers[i + 1]) + ret_string += "\n\\end{array}\n}" + + return ret_string diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index fd3492bb94c..de88e12f150 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -1,5 +1,5 @@ r""" -Tensor product of Kirillov-Reshetikhin tableaux +Tensor Product of Kirillov-Reshetikhin Tableaux A tensor product of :class:`KirillovReshetikhinTableaux` which are tableaux of `r` rows and `s` columns which naturally arise in the bijection between rigged @@ -19,15 +19,13 @@ sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) sage: KRT - Tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and tableau shape(s) [[1, 1, 1], [1, 1]] + Tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and factor(s) ((3, 1), (2, 1)) sage: KRT.cardinality() 24 - sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,1], [2,1], [3,1]]) - sage: HW - Highest weight tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and tableau shape(s) [[1], [1, 1], [1, 1, 1]] - sage: HW.cardinality() - 5 - sage: len(HW) + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,1], [2,1], [3,1]]) + sage: KRT + Tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and factor(s) ((1, 1), (2, 1), (3, 1)) + sage: len(KRT.module_generators) 5 sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,1], [2,1], [3,1]]) sage: KRT.cardinality() @@ -37,12 +35,11 @@ sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[1, 1], [2, 1], [1, 1]]) sage: KRT - Tensor product of Kirillov-Reshetikhin tableaux of type ['D', 4, 1] and tableau shape(s) [[1], [1, 1], [1]] - sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[1, 1], [2, 1], [1, 1]]) - sage: T = HW(pathlist=[[1], [-2, 2], [1]]) + Tensor product of Kirillov-Reshetikhin tableaux of type ['D', 4, 1] and factor(s) ((1, 1), (2, 1), (1, 1)) + sage: T = KRT(pathlist=[[1], [-2, 2], [1]]) sage: T [[1]] (X) [[2], [-2]] (X) [[1]] - sage: T2 = HW(pathlist=[[1], [2, -2], [1]]) + sage: T2 = KRT(pathlist=[[1], [2, -2], [1]]) sage: T2 [[1]] (X) [[-2], [2]] (X) [[1]] sage: T == T2 @@ -66,347 +63,116 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute +from sage.structure.unique_representation import UniqueRepresentation from sage.combinat.crystals.tensor_product import FullTensorProductOfRegularCrystals from sage.combinat.crystals.letters import CrystalOfLetters from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.rigged_configurations.tensor_product_kr_tableaux_element import TensorProductOfKirillovReshetikhinTableauxElement -from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux +from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux, \ + KirillovReshetikhinTableauxElement from sage.rings.integer import Integer -class AbstractTensorProductOfKRTableaux(FullTensorProductOfRegularCrystals): - r""" - Abstract class for all of tensor product of KR tableaux of a given Cartan type. - - See :class:`TensorProductOfKirillovReshetikhinTableaux`. This class should - never be created directly. +class HighestWeightTensorKRT(UniqueRepresentation): """ + Class so we do not have to build the module generators for + :class:`TensorProductOfKirillovReshetikhinTableaux` at initialization. - def __init__(self, cartan_type, B, biject_class): - r""" - Construct a tensor product of KR tableaux. - - INPUT: - - - ``cartan_type`` -- The crystal type and n value - - ``B`` -- An (ordered) list of dimensions - - ``biject_class`` -- The class the bijection creates - - The dimensions (i.e. `B`) is a list whose entries are lists of the - form `[r, s]` which correspond to a tableau with `r` rows and `s` - columns (or of shape `[r]*s`) and corresponds to a - Kirillov-Reshetikhin crystal `B^{r,s}`. - - TESTS:: - - sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,2]]); HW # indirect doctest - Highest weight tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and tableau shape(s) [[1, 1, 1], [2, 2]] - """ - assert cartan_type.is_affine() - - self.affine_ct = cartan_type - self.dims = B - self.letters = CrystalOfLetters(cartan_type) - self._bijection_class = biject_class - tensorProd = [] - for rectDims in B: - tensorProd.append(KirillovReshetikhinTableaux( - self.letters.cartan_type(), - rectDims[0], rectDims[1])) - FullTensorProductOfRegularCrystals.__init__(self, tuple(tensorProd)) - - def _highest_weight_iter(self): - r""" - Iterate through all of the highest weight tensor product of Kirillov-Reshetikhin tableaux. + .. WARNING:: - EXAMPLES:: - - sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) - sage: list(HW) # indirect doctest - [[[1], [2], [3]] (X) [[1], [2]], [[1], [3], [4]] (X) [[1], [2]]] - sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1]]) - sage: for x in HW: x # indirect doctest - ... - [[1], [2]] - [[1], [-1]] + This class is for internal use only! + """ + def __init__(self, tp_krt): """ - # This is a hack solution since during construction, the bijection will - # (attempt to) create a new KRT object which hasn't been fully created - # and stored in the UniqueRepresentation's cache. So this will be - # called again, causing the cycle to repeat. This hack just passes - # our self as an optional argument to hide it from the end-user and - # so we don't try to create a new KRT object. - from sage.combinat.rigged_configurations.rigged_configurations import HighestWeightRiggedConfigurations - for x in HighestWeightRiggedConfigurations(self.affine_ct, self.dims): - yield x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(KRT_init_hack=self) - - def _element_constructor_(self, *path, **options): - r""" - Construct a TensorProductOfKRTableauxElement. - - Typically the user will call this with the option **pathlist** which - will receive a list and coerce it into a path. + Initialize ``self``. EXAMPLES:: - sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) - sage: KRT(pathlist=[[4, 2, 1], [2, 1]]) # indirect doctest - [[1], [2], [4]] (X) [[1], [2]] + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1], [[2,2]]) + sage: from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import HighestWeightTensorKRT + sage: hw = HighestWeightTensorKRT(KRT) + sage: hw2 = HighestWeightTensorKRT(KRT) + sage: hw is hw2 + True """ - from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinGenericCrystalElement - if isinstance(path[0], KirillovReshetikhinGenericCrystalElement): - return self.element_class(self, *[x.to_Kirillov_Reshetikhin_tableau() for x in path]) - - from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement - if isinstance(path[0], TensorProductOfRegularCrystalsElement) and \ - isinstance(path[0][0], KirillovReshetikhinGenericCrystalElement): - return self.element_class(self, *[x.to_Kirillov_Reshetikhin_tableau() for x in path[0]]) - - from sage.combinat.rigged_configurations.rigged_configuration_element import RiggedConfigurationElement - if isinstance(path[0], RiggedConfigurationElement): - if self.rigged_configurations() != path[0].parent(): - raise ValueError("Incorrect bijection image.") - return path[0].to_tensor_product_of_Kirillov_Reshetikhin_tableaux() - - return self.element_class(self, *path, **options) + self.tp_krt = tp_krt + self._cache = None - def _convert_to_letters(self, index, tableauList): + def __getitem__(self, i): """ - Convert the entries of the list to a list of letters. - - This is a helper function to convert the list of ints to letters since - we do not convert an int to an Integer at compile time. + Return the `i`-th highest weight element in the cache. TESTS:: - sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,3]]) - sage: L = KRT._convert_to_letters(0, [3, 2, 2]); L - [3, 2, 2] - sage: type(L[0]) - - sage: L[0].value - 3 + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) + sage: KRT.module_generators[0] + [[1], [2], [3]] (X) [[1], [2]] """ - return([self.letters(x) for x in tableauList]) + if self._cache is None: + self._cache = [x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + for x in self.tp_krt.rigged_configurations().module_generators] + return self._cache[i] - def rigged_configurations(self): + def __iter__(self): """ - Return the corresponding set of rigged configurations. + Iterate over the highest weight elements. EXAMPLES:: - sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,3], [2,1]]) - sage: KRT.rigged_configurations() - Rigged configurations of type ['A', 3, 1] and factors ((1, 3), (2, 1)) - """ - return self._bijection_class(self.affine_ct, self.dims) - - def list(self): - r""" - Create a list of the elements by using the iterator. - - TESTS:: - - sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) - sage: HW.list() - [[[1], [2], [3]] (X) [[1], [2]], [[1], [3], [4]] (X) [[1], [2]]] - """ - # This is needed to overwrite the list method from the FiniteCrystals - # category which generates the list via f_a applications. - return [x for x in self] - -class HighestWeightTensorProductOfKirillovReshetikhinTableaux(AbstractTensorProductOfKRTableaux): - r""" - Container class of all highest weight tensor product of KR tableaux. - - A tensor product of KR tableaux is highest weight if the action of `e_i` - for `i \in I \setminus \{0\}` are all undefined. - - For more on tensor product of Kirillov-Reshetikhin tableaux, see - :class:`TensorProductOfKirillovReshetikhinTableaux`. - """ - - @staticmethod - def __classcall_private__(cls, cartan_type, B): + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1], [[2,1]]) + sage: from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import HighestWeightTensorKRT + sage: for x in HighestWeightTensorKRT(KRT): x + ... + [[1], [2]] + [[1], [-1]] """ - Normalizes the input arguments to ensure unique representation. + if self._cache is None: + self._cache = [x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + for x in self.tp_krt.rigged_configurations().module_generators] + for x in self._cache: + yield x - EXAMPLES:: - - sage: T1 = HighestWeightTensorProductOfKirillovReshetikhinTableaux(CartanType(['A',3,1]), [[2,2]]) - sage: T2 = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [(2,2)]) - sage: T3 = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], ((2,2),)) - sage: T2 is T1, T3 is T1 - (True, True) + def __repr__(self): """ - cartan_type = CartanType(cartan_type) - # Standardize B input into a tuple of tuples - assert B is not None - B = tuple(tuple(dim) for dim in B) - return super(HighestWeightTensorProductOfKirillovReshetikhinTableaux, cls).__classcall__(cls, cartan_type, B) - - def __init__(self, cartan_type, B): - r""" - Initialize the class. - - INPUT: - - - ``cartan_type`` -- The crystal type and n value - - ``B`` -- An (ordered) list of dimensions. - - The dimensions (i.e. `B`) is a list whose entries are lists of the - form `[r, c]` which correspond to a tableau with `r` rows and `c` - columns (or of shape `[r]*c`). + Return a string representation of ``self``. EXAMPLES:: - sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,2]]); HW - Highest weight tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and tableau shape(s) [[1, 1, 1], [2, 2]] - sage: TestSuite(HW).run() - - TESTS: - - That `__len__()` returns the correct value:: - - sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,2]]) - sage: len(HW) - 2 - sage: HW.cardinality() == len(HW) - True + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1], [[2,1]]) + sage: from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import HighestWeightTensorKRT + sage: HighestWeightTensorKRT(KRT) # indirect doctest + Highest weight elements of Tensor product of Kirillov-Reshetikhin tableaux of type ['D', 4, 1] and factor(s) ((2, 1),) """ - from rigged_configurations import HighestWeightRiggedConfigurations - AbstractTensorProductOfKRTableaux.__init__(self, cartan_type, B, HighestWeightRiggedConfigurations) - self.rename("Highest weight tensor product of Kirillov-Reshetikhin tableaux of type %s and tableau shape(s) %s" % (\ - cartan_type, list([rectDims[1]] * rectDims[0] for rectDims in B))) - - __iter__ = AbstractTensorProductOfKRTableaux._highest_weight_iter - -# Old iterator doc-string -# For type `A`, this works via a backtracing algorithm, however for all -# other types, it currently just iterates through all of them, checking -# to see if they are highest weight. - - # TODO Currently this will only be constructed for type A_n, so it needs - # to be generalized to all types. - # TODO Make it work for multiple columns - def _highest_weight_iterator_type_A(self): - r""" - Iterate over the highest weight tensor product of Kirillov-Reshetikhin - tableaux for type `A`. - - This uses a simple backtracing algorithm to determine all of the - highest weight crystals and will be significantly faster than iterating - over all crystals and checking if they are highest weight. - - TESTS:: - - sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) - sage: for x in HW._highest_weight_iterator_type_A(): x - ... - [[1], [2], [3]] (X) [[1], [2]] - [[1], [3], [4]] (X) [[1], [2]] - """ - pathLen = len(self.crystals) # This corresponds to the number of tableaux - curPath = [] # Stupid python references... - for i in range(pathLen): - curPath.append([]) - maxValue = self._cartan_type.n + 1 - # Specifies which tableau in the tensor product we are working in - tensorIndex = pathLen - 1 - value = 1 # The value we are trying to place at the current location - # Count of all the values to verify we have a highest weight crystal - valueCount = [0] * maxValue - - while True: # There is a return statement called if tensorIndex >= pathLen - if value <= maxValue: - if (value == 1 or valueCount[value - 2] >= valueCount[value - 1] + 1) \ - and (value == maxValue or - valueCount[value - 1] + 1 >= valueCount[value]): - # If it still makes it a highest weight crystal - # Prepend the value to our current location - curPath[tensorIndex].insert(0, value) # Assuming type A_n - valueCount[value - 1] += 1 # value-1 to convert to comp. indices - - # Assuming type A_n in here as well - if len(curPath[tensorIndex]) == self.dims[tensorIndex][0]: - # If the current tableau is filled - if tensorIndex == 0: - # If it is the last tableau in the path, then add it to the - # return list and then pop it off and increment the value - # Coerce into a list of crystal of tableaux - yield self(*[self.crystals[i](self._convert_to_letters(\ - i, tab)) for i, tab in enumerate(curPath)]) - valueCount[curPath[tensorIndex].pop(0) - 1] -= 1 - value += 1 - else: - # Otherwise we just continue on to the next tableau - tensorIndex -= 1 - value = 1 - else: - # Otherwise the tableau still has more values to add and since - # is an SSYT, it must be column strict inequality (note we - # are subtly again assuming its type A_n) - value += 1 - else: - # Otherwise the current value will not work - value += 1 - else: - # Otherwise its time to backtrace since we've exhausted all - # possible values for this position - - # If the current tableau is empty, back up to the previous tableau - if len(curPath[tensorIndex]) == 0: - tensorIndex += 1 - if tensorIndex == pathLen: - # Nothing more to do since there are no more tableau - return - - # Get the previous value (assuming type A_n) and increment - value = curPath[tensorIndex].pop(0) - valueCount[value - 1] -= 1 - value += 1 + return "Highest weight elements of %s"%self.tp_krt @cached_method def cardinality(self): - r""" - Return the number of highest weight tensor product of Kirillov-Reshetikhin tableaux. + """ + Return the cardinality of ``self`` which is the number of highest + weight elements. EXAMPLES:: - sage: HW = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A', 4, 1], [[2, 1]]) + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1], [[2,2]]) + sage: from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import HighestWeightTensorKRT + sage: HW = HighestWeightTensorKRT(KRT) sage: HW.cardinality() - 1 + 3 + sage: len(HW) + 3 + sage: len(KRT.module_generators) + 3 """ - count = Integer(0) - for x in self.module_generators: + count = 0 + for x in self: count += 1 - return(count) + return Integer(count) - # To override the default crystal __len__ which is the cardinality - # for the whole crystal. __len__ = cardinality - @lazy_attribute - def module_generators(self): - r""" - Module generators for this tensor product of KR tableaux. - - EXAMPLES:: - - sage: HWKR = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A', 4, 1], [[2,2]]) - sage: for x in HWKR.module_generators: x - ... - [[1, 1], [2, 2]] - """ - # This was separated out for speed; only needed for __iter__ - # lazy_attribute does not inherit - return [x for x in self._highest_weight_iter()] - -HighestWeightTensorProductOfKirillovReshetikhinTableaux.Element = TensorProductOfKirillovReshetikhinTableauxElement - -class TensorProductOfKirillovReshetikhinTableaux(AbstractTensorProductOfKRTableaux): +class TensorProductOfKirillovReshetikhinTableaux(FullTensorProductOfRegularCrystals): r""" A tensor product of :class:`KirillovReshetikhinTableaux`. @@ -416,6 +182,13 @@ class TensorProductOfKirillovReshetikhinTableaux(AbstractTensorProductOfKRTablea columns and weak increase in rows. The relation between the two tableaux models is given by a filling map. + .. NOTE:: + + The tableaux for all non-simply-laced provably holds if the bijection + with :class:`rigged configurations ` holds. + Therefore this is only proven for all factors`B^{r,1}` or all factors + `B^{1,s}`, and in general for types `A_n^{(1)}` and `D_n^{(1)}`. + For more information see [OSS2011]_ and :class:`KirillovReshetikhinTableaux`. @@ -430,12 +203,14 @@ class TensorProductOfKirillovReshetikhinTableaux(AbstractTensorProductOfKRTablea INPUT: - - ``cartan_type`` -- An affine Cartan type - - ``B`` -- An (ordered) list of dimensions. + - ``cartan_type`` -- The Cartan type - The dimensions (i.e. ``B``) is a list whose entries are lists of the - form ``[r, s]`` which correspond to Kirillov-Reshetikhin tableaux with - ``r`` rows and ``s`` columns. + - ``B`` -- An (ordered) list of dimensions + + The dimensions (i.e. `B`) is a list whose entries are lists of the + form `[r, s]` which correspond to a tableau with `r` rows and `s` + columns (or in type `A_n^{(1)}` of shape ``[r]*s``) and corresponds + to a Kirillov-Reshetikhin crystal `B^{r,s}`. EXAMPLES: @@ -468,6 +243,13 @@ class TensorProductOfKirillovReshetikhinTableaux(AbstractTensorProductOfKRTablea True sage: t == tp_krc True + + We can get the highest weight elements by using the attribute + ``module_generators``:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) + sage: list(KRT.module_generators) + [[[1], [2], [3]] (X) [[1], [2]], [[1], [3], [4]] (X) [[1], [2]]] """ @staticmethod def __classcall_private__(cls, cartan_type, B): @@ -483,28 +265,21 @@ def __classcall_private__(cls, cartan_type, B): (True, True) """ cartan_type = CartanType(cartan_type) + if not cartan_type.is_affine(): + raise ValueError("The Cartan type must be affine") + # Standardize B input into a tuple of tuples - assert B is not None B = tuple(tuple(dim) for dim in B) return super(TensorProductOfKirillovReshetikhinTableaux, cls).__classcall__(cls, cartan_type, B) def __init__(self, cartan_type, B): r""" - Initialize the TensorProductOfKirillovReshetikhinTableaux class. - - INPUT: - - - ``cartan_type`` -- The crystal type and n value - - ``B`` -- An (ordered) list of dimensions. - - The dimensions (i.e. `B`) is a list whose entries are lists of the - form `[r, c]` which correspond to a tableau with `r` rows and `c` - columns (or of shape `[r]*c`). + Initialize ``self``. EXAMPLES:: sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1],[2,2]]); KRT - Tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and tableau shape(s) [[1, 1, 1], [2, 2]] + Tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and factor(s) ((3, 1), (2, 2)) sage: TestSuite(KRT).run() # long time sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1], [[2,2]]) sage: TestSuite(KRT).run() # long time @@ -513,11 +288,34 @@ def __init__(self, cartan_type, B): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1], [[4,3]]) sage: TestSuite(KRT).run() # long time """ - from rigged_configurations import RiggedConfigurations - AbstractTensorProductOfKRTableaux.__init__(self, cartan_type, B, RiggedConfigurations) - self.rename("Tensor product of Kirillov-Reshetikhin tableaux of type %s and tableau shape(s) %s" % (\ - cartan_type, list([rectDims[1]] * rectDims[0] for rectDims in B))) - self.module_generators = HighestWeightTensorProductOfKirillovReshetikhinTableaux(cartan_type, B) + self.dims = B + self.letters = CrystalOfLetters(cartan_type.classical()) + tensor_prod = [] + for rect_dims in B: + tensor_prod.append(KirillovReshetikhinTableaux(cartan_type, rect_dims[0], rect_dims[1])) + FullTensorProductOfRegularCrystals.__init__(self, tuple(tensor_prod), cartan_type=cartan_type) + # This is needed to override the module_generators set in FullTensorProductOfRegularCrystals + self.module_generators = HighestWeightTensorKRT(self) + self.rename("Tensor product of Kirillov-Reshetikhin tableaux of type %s and factor(s) %s"%(\ + cartan_type, B)) + + def __iter__(self): + """ + Returns the iterator of ``self``. + + EXAMPLES:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 3, 1], [[2,1], [1,1]]) + sage: g = KRT.__iter__() + sage: g.next() + [[2], [3]] (X) [[1]] + sage: g.next() + [[2], [4]] (X) [[1]] + """ + index_set = self._cartan_type.classical().index_set() + from sage.combinat.backtrack import TransitiveIdeal + return TransitiveIdeal(lambda x: [x.f(i) for i in index_set], + self.module_generators).__iter__() def _test_bijection(self, **options): r""" @@ -541,6 +339,74 @@ def _test_bijection(self, **options): if len(rejects) != 0: return rejects + def _element_constructor_(self, *path, **options): + r""" + Construct a TensorProductOfKRTableauxElement. + + Typically the user will call this with the option **pathlist** which + will receive a list and coerce it into a path. + + EXAMPLES:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) + sage: KRT(pathlist=[[4, 2, 1], [2, 1]]) # indirect doctest + [[1], [2], [4]] (X) [[1], [2]] + """ + if isinstance(path[0], KirillovReshetikhinTableauxElement): + return self.element_class(self, path) + if isinstance(path[0], TensorProductOfKirillovReshetikhinTableauxElement): + return path[0] + + from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinGenericCrystalElement + if isinstance(path[0], KirillovReshetikhinGenericCrystalElement): + return self.element_class(self, [x.to_Kirillov_Reshetikhin_tableau() for x in path]) + + from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement + if isinstance(path[0], TensorProductOfRegularCrystalsElement) and \ + isinstance(path[0][0], KirillovReshetikhinGenericCrystalElement): + return self.element_class(self, [x.to_Kirillov_Reshetikhin_tableau() for x in path[0]]) + + from sage.combinat.rigged_configurations.rigged_configuration_element import RiggedConfigurationElement + if isinstance(path[0], RiggedConfigurationElement): + if self.rigged_configurations() != path[0].parent(): + raise ValueError("incorrect bijection image") + return path[0].to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + + return self.element_class(self, list(path), **options) + + @cached_method + def _module_generators_brute_force(self): + """ + Return the module generators of ``self`` by brute force searching + through all elements of ``self`` as a Cartesian product. + + EXAMPLES:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,3], [2,1]]) + sage: tuple(KRT.module_generators) + ([[1, 1, 1]] (X) [[1], [2]], [[1, 1, 3]] (X) [[1], [2]]) + sage: KRT._module_generators_brute_force() + ([[1, 1, 1]] (X) [[1], [2]], [[1, 1, 3]] (X) [[1], [2]]) + """ + index_set = self.cartan_type().classical().index_set() + return tuple(x for x in FullTensorProductOfRegularCrystals.__iter__(self) + if x.is_highest_weight(index_set)) + + @cached_method + def rigged_configurations(self): + """ + Return the corresponding set of rigged configurations. + + EXAMPLES:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[1,3], [2,1]]) + sage: KRT.rigged_configurations() + Rigged configurations of type ['A', 3, 1] and factor(s) ((1, 3), (2, 1)) + """ + from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations + return RiggedConfigurations(self.cartan_type(), self.dims) + + @cached_method def tensor_product_of_Kirillov_Reshetikhin_crystals(self): """ Return the corresponding tensor product of Kirillov-Reshetikhin @@ -565,7 +431,7 @@ def tensor_product_of_Kirillov_Reshetikhin_crystals(self): True """ return FullTensorProductOfRegularCrystals(tuple(x.Kirillov_Reshetikhin_crystal() for x in self.crystals), - cartan_type=self.affine_ct) + cartan_type=self.cartan_type()) TensorProductOfKirillovReshetikhinTableaux.Element = TensorProductOfKirillovReshetikhinTableauxElement diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py index e19de7d532c..3d54189cb21 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py @@ -1,5 +1,5 @@ r""" -An element of a tensor product of Kirillov-Reshetikhin tableaux +Tensor Product of Kirillov-Reshetikhin Tableaux Elements A tensor product of :class:`~sage.combinat.rigged_configurations.kr_tableaux.KirillovReshetikhinTableauxElement`. @@ -7,77 +7,6 @@ AUTHORS: - Travis Scrimshaw (2010-09-26): Initial version - -EXAMPLES: - -Type `A_n^{(1)}` examples:: - - sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 3, 1], [[1,1], [2,1], [1,1], [2,1], [2,1], [2,1]]) - sage: T = KRT(pathlist=[[2], [4,1], [3], [4,2], [3,1], [2,1]]) - sage: T - [[2]] (X) [[1], [4]] (X) [[3]] (X) [[2], [4]] (X) [[1], [3]] (X) [[1], [2]] - sage: T.to_rigged_configuration() - - 0[ ][ ]0 - 1[ ]1 - - 1[ ][ ]0 - 1[ ]0 - 1[ ]0 - - 0[ ][ ]0 - - sage: T = KRT(pathlist=[[1], [2,1], [1], [4,1], [3,1], [2,1]]) - sage: T - [[1]] (X) [[1], [2]] (X) [[1]] (X) [[1], [4]] (X) [[1], [3]] (X) [[1], [2]] - sage: T.to_rigged_configuration() - - (/) - - 1[ ]0 - 1[ ]0 - - 0[ ]0 - - -Type `D_n^{(1)}` examples:: - - sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[1,1], [1,1], [1,1], [1,1]]) - sage: T = KRT(pathlist=[[-1], [-1], [1], [1]]) - sage: T - [[-1]] (X) [[-1]] (X) [[1]] (X) [[1]] - sage: T.to_rigged_configuration() - - 0[ ][ ]0 - 0[ ][ ]0 - - 0[ ][ ]0 - 0[ ][ ]0 - - 0[ ][ ]0 - - 0[ ][ ]0 - - sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1], [1,1], [1,1], [1,1]]) - sage: T = KRT(pathlist=[[3,2], [1], [-1], [1]]) - sage: T - [[2], [3]] (X) [[1]] (X) [[-1]] (X) [[1]] - sage: T.to_rigged_configuration() - - 0[ ]0 - 0[ ]0 - 0[ ]0 - - 0[ ]0 - 0[ ]0 - 0[ ]0 - - 1[ ]0 - - 1[ ]0 - - sage: T.to_rigged_configuration().to_tensor_product_of_Kirillov_Reshetikhin_tableaux() - [[2], [3]] (X) [[1]] (X) [[-1]] (X) [[1]] """ #***************************************************************************** @@ -97,7 +26,6 @@ from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement -from sage.combinat.rigged_configurations.bijection import KRTToRCBijection class TensorProductOfKirillovReshetikhinTableauxElement(TensorProductOfRegularCrystalsElement): """ @@ -105,15 +33,92 @@ class TensorProductOfKirillovReshetikhinTableauxElement(TensorProductOfRegularCr For more on tensor product of Kirillov-Reshetikhin tableaux, see :class:`TensorProductOfKirillovReshetikhinTableaux`. + + The most common way to construct an element is to specify the option + ``pathlist`` which is a list of lists which will be used to generate + the individual factors of + :class:`~sage.combinat.rigged_configurations.kr_tableaux.KirillovReshetikhinTableauxElement`. + + EXAMPLES: + + Type `A_n^{(1)}` examples:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 3, 1], [[1,1], [2,1], [1,1], [2,1], [2,1], [2,1]]) + sage: T = KRT(pathlist=[[2], [4,1], [3], [4,2], [3,1], [2,1]]) + sage: T + [[2]] (X) [[1], [4]] (X) [[3]] (X) [[2], [4]] (X) [[1], [3]] (X) [[1], [2]] + sage: T.to_rigged_configuration() + + 0[ ][ ]0 + 1[ ]1 + + 1[ ][ ]0 + 1[ ]0 + 1[ ]0 + + 0[ ][ ]0 + + sage: T = KRT(pathlist=[[1], [2,1], [1], [4,1], [3,1], [2,1]]) + sage: T + [[1]] (X) [[1], [2]] (X) [[1]] (X) [[1], [4]] (X) [[1], [3]] (X) [[1], [2]] + sage: T.to_rigged_configuration() + + (/) + + 1[ ]0 + 1[ ]0 + + 0[ ]0 + + + Type `D_n^{(1)}` examples:: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[1,1], [1,1], [1,1], [1,1]]) + sage: T = KRT(pathlist=[[-1], [-1], [1], [1]]) + sage: T + [[-1]] (X) [[-1]] (X) [[1]] (X) [[1]] + sage: T.to_rigged_configuration() + + 0[ ][ ]0 + 0[ ][ ]0 + + 0[ ][ ]0 + 0[ ][ ]0 + + 0[ ][ ]0 + + 0[ ][ ]0 + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1], [1,1], [1,1], [1,1]]) + sage: T = KRT(pathlist=[[3,2], [1], [-1], [1]]) + sage: T + [[2], [3]] (X) [[1]] (X) [[-1]] (X) [[1]] + sage: T.to_rigged_configuration() + + 0[ ]0 + 0[ ]0 + 0[ ]0 + + 0[ ]0 + 0[ ]0 + 0[ ]0 + + 1[ ]0 + + 1[ ]0 + + sage: T.to_rigged_configuration().to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + [[2], [3]] (X) [[1]] (X) [[-1]] (X) [[1]] """ - def __init__(self, parent, *path, **options): + def __init__(self, parent, list=[[]], **options): r""" Construct a TensorProductOfKirillovReshetikhinTableauxElement. INPUT: - ``parent`` -- Parent for this element - - ``path`` -- The list of KR tableaux elements + + - ``list`` -- The list of KR tableaux elements EXAMPLES:: @@ -131,19 +136,16 @@ def __init__(self, parent, *path, **options): [[2], [3]] (X) [[1]] (X) [[-1]] (X) [[1]] sage: TestSuite(T).run() """ - if "pathlist" in options: pathlist = options["pathlist"] TensorProductOfRegularCrystalsElement.__init__(self, parent, - [parent.crystals[i](list(tab)) for i, tab in enumerate(pathlist)]) - elif "list" in options: - TensorProductOfRegularCrystalsElement.__init__(self, parent, **options) + [parent.crystals[i](*tab) for i, tab in enumerate(pathlist)]) else: - TensorProductOfRegularCrystalsElement.__init__(self, parent, list(path)) + TensorProductOfRegularCrystalsElement.__init__(self, parent, list) def _repr_(self): """ - Return the string representation for ``self``. + Return a string representation of ``self``. EXAMPLES:: @@ -152,44 +154,81 @@ def _repr_(self): sage: T # indirect doctest [[2], [3]] (X) [[1]] (X) [[-1]] (X) [[1]] """ - retStr = repr(self[0]) + ret_str = repr(self[0]) for i in range(1, len(self)): - retStr += " (X) " + repr(self[i]) - return(retStr) + ret_str += " (X) " + repr(self[i]) + return(ret_str) - def e(self, i): - r""" - Return the action of `e_i` on ``self``. + def _repr_diagram(self): + """ + Return a string representation of ``self`` as a diagram. EXAMPLES:: - sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1]]) - sage: T = KRT(pathlist=[[4,3]]) - sage: T.e(1) - sage: T.e(2) - [[2], [4]] + sage: TPKRT = TensorProductOfKirillovReshetikhinTableaux(['A',4,1], [[2,2],[3,1],[3,3]]) + sage: print TPKRT.module_generators[0]._repr_diagram() + 1 1 (X) 1 (X) 1 1 1 + 2 2 2 2 2 2 + 3 3 3 3 + """ + arrays = [crys.to_array() for crys in self] + col_len = [len(t)>0 and len(t[0]) or 1 for t in arrays] # columns per component + row_max = max(len(t) for t in arrays) # maximum row length + # There should be a fancier list compression for this but I couldn't get + # one to work in the cases where a component was the empty partition + diag = [] + for row in xrange(row_max): + line='' + if row == 0: + line += ' (X) '.join(''.join(map(lambda x: "%3s"%str(x), arrays[c][row]))+ + ' '*(col_len[c]-len(arrays[c][row])) for c in range(len(arrays))) + else: + for c in range(len(arrays)): + if c > 0: + line += ' ' + if row < len(arrays[c]): + line += ''.join(map(lambda x: "%3s"%str(x), arrays[c][row]))+' '*(col_len[c]-len(arrays[c][row])) + else: + line += ' '*col_len[c] + diag.append(line) + return '\n'.join(map(str,diag)) + #if TableauTuples.global_options('convention')=='english': + # return '\n'.join(map(str,diag)) + #else: + # return '\n'.join(map(str,diag[::-1])) + + def pp(self): """ - if i != 0: - return TensorProductOfRegularCrystalsElement.e(self, i) + Pretty print ``self``. - return None + EXAMPLES:: - def f(self, i): - r""" - Return the action of `f_i` on ``self``. + sage: TPKRT = TensorProductOfKirillovReshetikhinTableaux(['A',4,1], [[2,2],[3,1],[3,3]]) + sage: TPKRT.module_generators[0].pp() + 1 1 (X) 1 (X) 1 1 1 + 2 2 2 2 2 2 + 3 3 3 3 + """ + print(self._repr_diagram()) + + def classical_weight(self): + """ + Return the classical weight of ``self``. EXAMPLES:: - sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,1]]) - sage: T = KRT(pathlist=[[4,3]]) - sage: T.f(1) - sage: T.f(4) - [[-4], [4]] + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1], [[2,2]]) + sage: elt = KRT(pathlist=[[3,2,-1,1]]); elt + [[2, 1], [3, -1]] + sage: elt.classical_weight() + (0, 1, 1, 0) + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[2,2],[1,3]]) + sage: elt = KRT(pathlist=[[2,1,3,2],[1,4,4]]); elt + [[1, 2], [2, 3]] (X) [[1, 4, 4]] + sage: elt.classical_weight() + (2, 2, 1, 2) """ - if i != 0: - return TensorProductOfRegularCrystalsElement.f(self, i) - - return None + return sum([x.classical_weight() for x in self]) def to_rigged_configuration(self, display_steps=False): r""" @@ -198,6 +237,12 @@ def to_rigged_configuration(self, display_steps=False): which is described in [RigConBijection]_, [BijectionLRT]_, and [BijectionDn]_. + .. NOTE:: + + This is only proven to be a bijection in types `A_n^{(1)}` + and `D_n^{(1)}`, as well as `\bigotimes_i B^{r_i,1}` and + `\bigotimes_i B^{1,s_i}` for general affine types. + INPUT: - ``display_steps`` -- (default: ``False``) Boolean which indicates @@ -211,7 +256,7 @@ def to_rigged_configuration(self, display_steps=False): Type `A_n^{(1)}` example:: - sage: KRT = HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A', 3, 1], [[2,1], [2,1], [2,1]]) + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A', 3, 1], [[2,1], [2,1], [2,1]]) sage: T = KRT(pathlist=[[4, 2], [3, 1], [2, 1]]) sage: T [[2], [4]] (X) [[1], [3]] (X) [[1], [2]] @@ -274,73 +319,8 @@ def to_rigged_configuration(self, display_steps=False): sage: ret == T True """ - - bijection = KRTToRCBijection(self) - type = self.parent().cartan_type().letter - n = self.parent().cartan_type().n - - for cur_crystal in reversed(self): - r = cur_crystal.parent().r() - # Iterate through the columns - for col_number, cur_column in enumerate(reversed(cur_crystal.to_array(False))): - bijection.cur_path.insert(0, []) # Prepend an empty list - - # Check to see if we are a spinor column - if type == 'D' and r >= n-1: - if display_steps: - print "====================" - print repr(TensorProductOfKirillovReshetikhinTableauxElement(self.parent(), *bijection.cur_path)) - print "--------------------" - print repr(bijection.ret_rig_con) - print "--------------------\n" - print "Applied doubling map" - bijection.doubling_map() - - bijection.cur_dims.insert(0, [0, 1]) - - # Note that we do not need to worry about iterating over columns - # (see previous note about the data structure). - for letter in reversed(cur_column): - if bijection.cur_dims[0][0] < r: - bijection.cur_dims[0][0] += 1 - val = letter.value # Convert from a CrystalOfLetter to an Integer - - if display_steps: - print "====================" - print repr(TensorProductOfKirillovReshetikhinTableauxElement(self.parent(), *bijection.cur_path)) - print "--------------------" - print repr(bijection.ret_rig_con) - print "--------------------\n" - - # Build the next state - bijection.cur_path[0].insert(0, [letter]) # Prepend the value - bijection.next_state(val) - - # Check to see if we are a spinor column - if type == 'D' and r >= n-1: - if display_steps: - print "====================" - print repr(TensorProductOfKirillovReshetikhinTableauxElement(self.parent(), *bijection.cur_path)) - print "--------------------" - print repr(bijection.ret_rig_con) - print "--------------------\n" - print "Applied halving map" - bijection.halving_map() - - # If we've split off a column, we need to merge the current column - # to the current crystal tableau - if col_number > 0: - for i, letter_singleton in enumerate(bijection.cur_path[0]): - bijection.cur_path[1][i].insert(0, letter_singleton[0]) - bijection.cur_dims.pop(0) - bijection.cur_dims[0][1] += 1 - - # And perform the inverse column splitting map on the RC - for a in range(n): - bijection._update_vacancy_nums(a) - - bijection.ret_rig_con.set_immutable() # Return it to immutable - return(bijection.ret_rig_con) + from sage.combinat.rigged_configurations.bijection import KRTToRCBijection + return KRTToRCBijection(self).run(display_steps) def to_tensor_product_of_Kirillov_Reshetikhin_crystals(self): """ @@ -360,9 +340,7 @@ def to_tensor_product_of_Kirillov_Reshetikhin_crystals(self): We can recover the original tensor product of KR tableaux:: - sage: KRT(tp_krc) - [[-1]] (X) [[2, 1], [-1, -1]] - sage: ret = KRT(*tp_krc); ret + sage: ret = KRT(tp_krc); ret [[-1]] (X) [[2, 1], [-1, -1]] sage: ret == elt True @@ -370,11 +348,3 @@ def to_tensor_product_of_Kirillov_Reshetikhin_crystals(self): TP = self.parent().tensor_product_of_Kirillov_Reshetikhin_crystals() return TP(*[x.to_Kirillov_Reshetikhin_crystal() for x in self]) -# FIXME -# def is_highest_weight(self, **options): -# r""" -# Checks to see if the crystal path is a highest weight crystal path. -# """ -# return super(CrystalsElement, self).is_highest_weight( \ -# index_set=self.parent().cartan_type().classical().index_set(), **options ) -# # is_highest_weight() From f390655e9e74a9091464ecb2cb85cd1510f93901 Mon Sep 17 00:00:00 2001 From: Anne Schilling Date: Thu, 7 Nov 2013 14:49:25 -0800 Subject: [PATCH 137/206] Removed some whitespaces, beautified code --- .../rigged_configurations/tensor_product_kr_tableaux.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index de88e12f150..d268768b6b0 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -69,7 +69,8 @@ from sage.combinat.crystals.letters import CrystalOfLetters from sage.combinat.root_system.cartan_type import CartanType -from sage.combinat.rigged_configurations.tensor_product_kr_tableaux_element import TensorProductOfKirillovReshetikhinTableauxElement +from sage.combinat.rigged_configurations.tensor_product_kr_tableaux_element \ + import TensorProductOfKirillovReshetikhinTableauxElement from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux, \ KirillovReshetikhinTableauxElement @@ -150,8 +151,7 @@ def __repr__(self): @cached_method def cardinality(self): """ - Return the cardinality of ``self`` which is the number of highest - weight elements. + Return the cardinality of ``self`` which is the number of highest weight elements. EXAMPLES:: @@ -249,7 +249,7 @@ class TensorProductOfKirillovReshetikhinTableaux(FullTensorProductOfRegularCryst sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) sage: list(KRT.module_generators) - [[[1], [2], [3]] (X) [[1], [2]], [[1], [3], [4]] (X) [[1], [2]]] + [[[1], [2], [3]] (X) [[1], [2]], [[1], [3], [4]] (X) [[1], [2]]] """ @staticmethod def __classcall_private__(cls, cartan_type, B): From 57e4c5c7d14145fb7c4d53937a66e61bb6e0a43a Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 7 Nov 2013 16:12:21 -0800 Subject: [PATCH 138/206] Fixed note in KR tableaux. --- src/sage/combinat/rigged_configurations/kr_tableaux.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index f95841bad6a..9dea0db378d 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -82,10 +82,10 @@ class KirillovReshetikhinTableaux(CrystalOfWords): .. NOTE:: - The tableaux for all non-simply-laced provably holds if the bijection - with :class:`rigged configurations ` holds. - Therefore this is only proven for `B^{r,1}` or `B^{1,s}` and in - general for types `A_n^{(1)}` and `D_n^{(1)}`. + The tableaux for all non-simply-laced are provably correct if the + bijection with :class:`rigged configurations ` + holds. Therefore this is only proven for `B^{r,1}` or `B^{1,s}` and + in general for types `A_n^{(1)}` and `D_n^{(1)}`. INPUT: From 427bf8fd745b3a0e4f5e7ec4d31893c37ad03ccc Mon Sep 17 00:00:00 2001 From: Anne Schilling Date: Thu, 7 Nov 2013 16:14:15 -0800 Subject: [PATCH 139/206] removed some whitespace in kr_tableaux.py --- src/sage/combinat/rigged_configurations/kr_tableaux.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index 9dea0db378d..a14cb490aed 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -257,7 +257,7 @@ def module_generator(self, i=None, **options): - ``classical_weight`` -- The classical weight EXAMPLES:: - + sage: KRT = KirillovReshetikhinTableaux(['D', 4, 1], 2, 2) sage: KRT.module_generator(1) [[1, 1], [2, -1]] @@ -614,7 +614,7 @@ def _fill(self, shape): sage: KRT._fill([6,4,2,2]) [[1, 1, 1, 1, 1, 1], [2, 2, 2, 2, -5, 2], [3, 3, -5, 3, -4, 3], [4, 4, -4, 4, -3, 4], [-5, 5, -3, 5, -2, 5]] sage: KRT._fill([6,4]) - [[1, 1, 1, 1, 1, 1], [2, 2, 2, 2, -5, 2], [-5, 3, -5, 3, -4, 3], [-4, 4, -4, 4, -3, 4], [-3, 5, -3, 5, -2, 5]] + [[1, 1, 1, 1, 1, 1], [2, 2, 2, 2, -5, 2], [-5, 3, -5, 3, -4, 3], [-4, 4, -4, 4, -3, 4], [-3, 5, -3, 5, -2, 5]] """ # Add zeros until the shape has length s shape_list = list(shape) # Make sure we have a list @@ -708,7 +708,7 @@ def _fill(self, weight): sage: KRT._fill([10,10,8,2,2,2]) [[1, 1, 1, 1, 1, 7, 1], [2, 2, 2, 2, 2, 8, 2], [3, 3, 7, 9, 7, 9, 3], [4, 4, 8, 10, 8, 10, 4], [5, 5, 9, 11, 9, 11, 5], [6, 6, 10, 12, 10, 12, 6], [7, 7, 11, -12, 11, -12, 7], [8, 8, 12, -11, 12, -11, 8], [9, 9, -12, -10, -12, -10, 9], [10, 10, -11, -9, -11, -9, -9], [-12, 11, -10, -8, -10, -8, -8], [-11, 12, -9, -7, -9, -7, -7]] sage: KRT._fill([10,10,6,2,2,2]) - [[1, 1, 1, 1, 1, 5, 1], [2, 2, 2, 2, 2, 6, 2], [3, 3, 9, 7, 9, 7, 3], [4, 4, 10, 8, 10, 8, 4], [5, 5, 11, 9, 11, 9, 5], [6, 6, 12, 10, 12, 10, 6], [7, 7, -12, 11, -12, 11, 7], [8, 8, -11, 12, -11, 12, 8], [9, 9, -10, -12, -10, -12, -8], [10, 10, -9, -11, -9, -11, -7], [-12, 11, -8, -10, -8, -10, -6], [-11, 12, -7, -9, -7, -9, -5]] + [[1, 1, 1, 1, 1, 5, 1], [2, 2, 2, 2, 2, 6, 2], [3, 3, 9, 7, 9, 7, 3], [4, 4, 10, 8, 10, 8, 4], [5, 5, 11, 9, 11, 9, 5], [6, 6, 12, 10, 12, 10, 6], [7, 7, -12, 11, -12, 11, 7], [8, 8, -11, 12, -11, 12, 8], [9, 9, -10, -12, -10, -12, -8], [10, 10, -9, -11, -9, -11, -7], [-12, 11, -8, -10, -8, -10, -6], [-11, 12, -7, -9, -7, -9, -5]] """ # Add zeros until the shape has length s weight_list = list(weight) # Make sure we have a list @@ -904,7 +904,7 @@ def _repr_diagram(self): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A',4,1], 2,2) - sage: elt = KRT(4,3,2,1) + sage: elt = KRT(4,3,2,1) sage: print elt._repr_diagram() 3 1 4 2 From 96cae84af7fc2a3c991da214dee744c2d3356846 Mon Sep 17 00:00:00 2001 From: Anne Schilling Date: Thu, 7 Nov 2013 16:30:42 -0800 Subject: [PATCH 140/206] fixed some documentation in kr_tableaux.py --- .../rigged_configurations/kr_tableaux.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index a14cb490aed..cc4b2d6f6a2 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -62,18 +62,17 @@ class KirillovReshetikhinTableaux(CrystalOfWords): the elements of the Kirillov-Reshetikhin crystal `B^{r,s}` under the (inverse) filling map. - When the Kirillov-Reshetkihin crystal is a full `r \times s` rectangle - (such as in type `A^{(1)}_n` or `C^{(1)}_n` for `B^{n,s}`), the filling - map is trivial. Thus the highest weight module is just filled with columns - `[1, 2, \ldots, n]`. + Whenever `B^{r,s} \cong B(s\Lambda_r)` as a classical crystal (which is the case + for `B^{r,s}` in type `A_n^{(1)}` and `B^{n,s}` in type `C_n^{(1)}`), then the filling + map is trivial. - For type `D^{(1)}_n` for `B^{r,s}` when `r \leq n-2`, the filling map is + For `B^{r,s}` in type `D^{(1)}_n` when `r \leq n-2`, the filling map is defined in [AffineRigConDn]_. - For the spinor cases, the crystal `B^{k,s}` is isomorphic to the classical - crystal `B(\Lambda_k)`, and here we consider the Kirillov-Reshetikhin - tableaux as living in `B(2\Lambda_k)` under the natural doubling map. - In this case, `e_i` and `f_i` act as `e_i^2` and `f_i^2` respectively. + For the spinor cases in `D_n^{(1)}`, the crystal `B^{k,s}` where `k=n-1,n` is isomorphic as + a classical crystal to `B(s\Lambda_k)`, and here we consider the Kirillov-Reshetikhin + tableaux as living in `B(2s\Lambda_k)` under the natural doubling map. + In this case, the crystal operators `e_i` and `f_i` act as `e_i^2` and `f_i^2` respectively. See [BijectionDn]_. For more information about the bijection between rigged configurations From b98e1dd530f30b0e95b20a81d93c58a81ab4c25c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 7 Nov 2013 18:15:35 -0800 Subject: [PATCH 141/206] Changed method names and added more documentation. --- .../combinat/crystals/kirillov_reshetikhin.py | 12 +- .../bij_abstract_class.py | 2 +- .../rigged_configurations/bij_type_B.py | 2 +- .../rigged_configurations/kr_tableaux.py | 126 ++++++++++++------ .../rigged_configuration_element.py | 28 ++-- .../rigged_configurations.py | 38 +++--- .../tensor_product_kr_tableaux.py | 24 ++-- .../tensor_product_kr_tableaux_element.py | 14 +- 8 files changed, 144 insertions(+), 102 deletions(-) diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index 0703ba03a52..9658232eeae 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -615,14 +615,14 @@ def R_matrix(self, K): return T1.crystal_morphism(g, acyclic = False) @cached_method - def Kirillov_Reshetikhin_tableaux(self): + def kirillov_reshetikhin_tableaux(self): """ Return the corresponding set of :class:`KirillovReshetikhinTableaux`. EXAMPLES:: sage: KRC = KirillovReshetikhinCrystal(['D', 4, 1], 2, 2) - sage: KRC.Kirillov_Reshetikhin_tableaux() + sage: KRC.kirillov_reshetikhin_tableaux() Kirillov-Reshetikhin tableaux of type ['D', 4, 1] and shape (2, 2) """ from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux @@ -633,7 +633,7 @@ class KirillovReshetikhinGenericCrystalElement(AffineCrystalFromClassicalElement Abstract class for all Kirillov-Reshetikhin crystal elements. """ @cached_method - def to_Kirillov_Reshetikhin_tableau(self): + def to_kirillov_reshetikhin_tableau(self): r""" Construct the corresponding :class:`KirillovReshetikhinTableauxElement` from ``self``. @@ -649,13 +649,13 @@ def to_Kirillov_Reshetikhin_tableau(self): EXAMPLES:: sage: KRC = KirillovReshetikhinCrystal(['A', 4, 1], 2, 1) - sage: KRC(columns=[[2,1]]).to_Kirillov_Reshetikhin_tableau() + sage: KRC(columns=[[2,1]]).to_kirillov_reshetikhin_tableau() [[1], [2]] sage: KRC = KirillovReshetikhinCrystal(['D', 4, 1], 2, 1) - sage: KRC(rows=[]).to_Kirillov_Reshetikhin_tableau() + sage: KRC(rows=[]).to_kirillov_reshetikhin_tableau() [[1], [-1]] """ - return self.parent().Kirillov_Reshetikhin_tableaux()(self) + return self.parent().kirillov_reshetikhin_tableaux()(self) KirillovReshetikhinGenericCrystal.Element = KirillovReshetikhinGenericCrystalElement diff --git a/src/sage/combinat/rigged_configurations/bij_abstract_class.py b/src/sage/combinat/rigged_configurations/bij_abstract_class.py index b09e187b157..102213d196c 100644 --- a/src/sage/combinat/rigged_configurations/bij_abstract_class.py +++ b/src/sage/combinat/rigged_configurations/bij_abstract_class.py @@ -301,7 +301,7 @@ def __init__(self, RC_element): # This will be deleted when the bijection is completed self.rigged_con = RC_element.__copy__() self.n = RC_element.parent().cartan_type().classical().rank() - self.KRT = RC_element.parent().tensor_product_of_Kirillov_Reshetikhin_tableaux() + self.KRT = RC_element.parent().tensor_product_of_kirillov_reshetikhin_tableaux() # Make a (deep) copy of the dimensions for the bijection self.cur_dims = [list(x[:]) for x in self.rigged_con.parent().dims] diff --git a/src/sage/combinat/rigged_configurations/bij_type_B.py b/src/sage/combinat/rigged_configurations/bij_type_B.py index 5c034925a63..bfa753abd36 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_B.py +++ b/src/sage/combinat/rigged_configurations/bij_type_B.py @@ -445,7 +445,7 @@ def other_outcome(self, rc, pos_val, width_n): sage: RC = RiggedConfigurations(['B',3,1], [[2,1],[1,2]]) sage: rc = RC(partition_list=[[2,1], [2,1,1], [5,1]]) - sage: t = rc.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + sage: t = rc.to_tensor_product_of_kirillov_reshetikhin_tableaux() sage: t.to_rigged_configuration() == rc # indirect doctest True """ diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index cc4b2d6f6a2..05ad9ae6757 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -62,19 +62,61 @@ class KirillovReshetikhinTableaux(CrystalOfWords): the elements of the Kirillov-Reshetikhin crystal `B^{r,s}` under the (inverse) filling map. - Whenever `B^{r,s} \cong B(s\Lambda_r)` as a classical crystal (which is the case - for `B^{r,s}` in type `A_n^{(1)}` and `B^{n,s}` in type `C_n^{(1)}`), then the filling - map is trivial. + Whenever `B^{r,s} \cong B(s\Lambda_r)` as a classical crystal (which is + the case for `B^{r,s}` in type `A_n^{(1)}` and `B^{n,s}` in type + `C_n^{(1)}`), then the filling map is trivial. - For `B^{r,s}` in type `D^{(1)}_n` when `r \leq n-2`, the filling map is - defined in [AffineRigConDn]_. + For `B^{r,s}` in: - For the spinor cases in `D_n^{(1)}`, the crystal `B^{k,s}` where `k=n-1,n` is isomorphic as - a classical crystal to `B(s\Lambda_k)`, and here we consider the Kirillov-Reshetikhin - tableaux as living in `B(2s\Lambda_k)` under the natural doubling map. - In this case, the crystal operators `e_i` and `f_i` act as `e_i^2` and `f_i^2` respectively. + - type `D_n^{(1)}` when `r \leq n-2`, + - type `B_n^{(1)}` when `r < n`, + - type `A_{2n-1}^{(2)}` for all `r`, + + the filling map is defined in [AffineRigConDn]_. + + For the spinor cases in type `D_n^{(1)}`, the crystal `B^{k,s}` where + `k=n-1,n`, is isomorphic as a classical crystal to `B(s\Lambda_k)`, and + here we consider the Kirillov-Reshetikhin tableaux as living in + `B(2s \Lambda_k)` under the natural doubling map. In this case, the + crystal operators `e_i` and `f_i` act as `e_i^2` and `f_i^2` respectively. See [BijectionDn]_. + For the spinor case in type `B_n^{(1)}`, the crystal `B^{n,s}`, we + consider the images under the natural doubling map which is given by + removing `2 \times 2` boxes and the filling map is the same as below + (ex. the non-spin type `C_n^{(1)}`). + + For `B^{r,s}` in: + + - type `C_n^{(1)}` when `r < n`, + - type `A_{2n}^{(2)\dagger}` for all `r`, + + the filling map given as follows. Suppose we are considering (classically) + highest weight element in the classical component `B(\lambda)`, we fill + it in with the horizontal dominoes `[\bar{\imath}, i]` in the `i`-th row + from the top (in English notation) and reorganizing the columns as + necessary. Recall from above that `B^{n,s} \cong B(s\Lambda_n)` in + type `C^{(1)}_n`. + + For `B^{r,s}` in: + + - type `A_{2n}^{(2)}` for all `r`, + - type `D_{n+1}^{(2)}` when `r < n`, + + the filling map is the same as given in [AffineRigConDn]_ except for + `B^{r,1}` which is given by the column `[1, 2, \ldots, k, \emptyset, + \ldots \emptyset]` where we consider the classical component + `B(\lambda_k)`. + + For the spinor case in type `D_n^{(2)}`, the crystal `B^{n,s}`, the + crystal is the same as in type `D_n^{(1)}`. + + .. NOTE:: + + The filling map and classical decompositions in non-spinor cases can + be classified by how the special node `0` connects with the + corresponding classical diagram. + For more information about the bijection between rigged configurations and tensor products of Kirillov-Reshetikhin tableaux, see :class:`TensorProductOfKirillovReshetikhinTableaux`. @@ -84,7 +126,7 @@ class KirillovReshetikhinTableaux(CrystalOfWords): The tableaux for all non-simply-laced are provably correct if the bijection with :class:`rigged configurations ` holds. Therefore this is only proven for `B^{r,1}` or `B^{1,s}` and - in general for types `A_n^{(1)}` and `D_n^{(1)}`. + in general for types `A_n^{(1)}` and `D_n^{(C1)}`. INPUT: @@ -157,7 +199,7 @@ def __classcall_private__(cls, cartan_type, r, s): if type == 'C': if r == ct.classical().rank(): return KRTableauxRectangle(ct, r, s) - return KRTableauxTypeC(ct, r, s) + return KRTableauxTypeHorizonal(ct, r, s) if type == 'D': if r == ct.classical().rank() or r == ct.classical().rank() - 1: return KRTableauxSpin(ct, r, s) @@ -166,7 +208,7 @@ def __classcall_private__(cls, cartan_type, r, s): if type == 'BC': # A_{2n}^{(2)} return KRTableauxTypeBox(ct, r, s) if ct.dual().type() == 'BC': # A_{2n}^{(2)\dagger} - return KRTableauxTypeC(ct, r, s) # Placeholder + return KRTableauxTypeHorizonal(ct, r, s) if ct.dual().type() == 'B': # A_{2n-1}^{(2)} return KRTableauxTypeVertical(ct, r, s) if ct.dual().type() == 'C': # D_{n+1}^{(2)} @@ -318,7 +360,7 @@ def _build_module_generators(self): """ @abstract_method - def from_Kirillov_Reshetikhin_crystal(self, krc): + def from_kirillov_reshetikhin_crystal(self, krc): """ Construct an element of ``self`` from the Kirillov-Reshetikhin crystal element ``krc``. @@ -329,7 +371,7 @@ def from_Kirillov_Reshetikhin_crystal(self, krc): sage: C = KirillovReshetikhinCrystal(['A',4,1], 2, 1) sage: krc = C(4,3); krc [[3], [4]] - sage: KRT.from_Kirillov_Reshetikhin_crystal(krc) + sage: KRT.from_kirillov_reshetikhin_crystal(krc) [[3], [4]] """ @@ -350,7 +392,7 @@ def _element_constructor_(self, *lst, **options): if lst[0].cartan_type() != self.cartan_type() \ or lst[0].parent().r() != self._r or lst[0].parent().s() != self._s: raise ValueError("The Kirillov-Reshetikhin crystal must have the same Cartan type and (r,s)") - return self.from_Kirillov_Reshetikhin_crystal(lst[0]) + return self.from_kirillov_reshetikhin_crystal(lst[0]) return self.element_class(self, list(lst), **options) @@ -381,14 +423,14 @@ def s(self): return self._s @cached_method - def Kirillov_Reshetikhin_crystal(self): + def kirillov_reshetikhin_crystal(self): """ Return the corresponding :func:`Kirillov-Reshetikhin crystal`. EXAMPLES:: - sage: KirillovReshetikhinTableaux(['A', 4, 1], 2, 1).Kirillov_Reshetikhin_crystal() + sage: KirillovReshetikhinTableaux(['A', 4, 1], 2, 1).kirillov_reshetikhin_crystal() Kirillov-Reshetikhin crystal of type ['A', 4, 1] with (r,s)=(2,1) """ return KirillovReshetikhinCrystal(self._cartan_type, self._r, self._s) @@ -429,7 +471,7 @@ def _build_module_generators(self): return (self.element_class(self, [self.letters(x) for x in flatten(tableau)]),) - def from_Kirillov_Reshetikhin_crystal(self, krc): + def from_kirillov_reshetikhin_crystal(self, krc): """ Construct a :class:`KirillovReshetikhinTableauxElement`. @@ -439,7 +481,7 @@ def from_Kirillov_Reshetikhin_crystal(self, krc): sage: C = KirillovReshetikhinCrystal(['A',4,1], 2, 1) sage: krc = C(4,3); krc [[3], [4]] - sage: KRT.from_Kirillov_Reshetikhin_crystal(krc) + sage: KRT.from_kirillov_reshetikhin_crystal(krc) [[3], [4]] """ # To build a KR tableau from a KR crystal: @@ -554,7 +596,7 @@ def _build_module_generators(self): return tuple(self._fill(weight) for weight in horizontal_dominoes_removed(self._s, self._r)) - def from_Kirillov_Reshetikhin_crystal(self, krc): + def from_kirillov_reshetikhin_crystal(self, krc): """ Construct an element of ``self`` from the Kirillov-Reshetikhin crystal element ``krc``. @@ -565,7 +607,7 @@ def from_Kirillov_Reshetikhin_crystal(self, krc): sage: C = KirillovReshetikhinCrystal(['D',4,1], 2,3) sage: krc = C(4,3); krc [[3], [4]] - sage: KRT.from_Kirillov_Reshetikhin_crystal(krc) + sage: KRT.from_kirillov_reshetikhin_crystal(krc) [[3, -2, 1], [4, -1, 2]] """ # To build a KR tableau from a KR crystal: @@ -578,7 +620,7 @@ def from_Kirillov_Reshetikhin_crystal(self, krc): f_str = reversed(lifted.to_highest_weight()[1]) return self._fill(weight).f_string(f_str) -class KRTableauxTypeC(KirillovReshetikhinTableaux): +class KRTableauxTypeHorizonal(KirillovReshetikhinTableaux): r""" Kirillov-Reshetikhin tableaux `B^{r,s}` of type: @@ -647,7 +689,7 @@ def _build_module_generators(self): """ return tuple(self._fill(shape) for shape in horizontal_dominoes_removed(self._r, self._s)) - def from_Kirillov_Reshetikhin_crystal(self, krc): + def from_kirillov_reshetikhin_crystal(self, krc): """ Construct an element of ``self`` from the Kirillov-Reshetikhin crystal element ``krc``. @@ -658,7 +700,7 @@ def from_Kirillov_Reshetikhin_crystal(self, krc): sage: C = KirillovReshetikhinCrystal(['C',4,1], 2,3) sage: krc = C(4,3); krc [[3], [4]] - sage: KRT.from_Kirillov_Reshetikhin_crystal(krc) + sage: KRT.from_kirillov_reshetikhin_crystal(krc) [[3, -2, 1], [4, -1, 2]] """ # To build a KR tableau from a KR crystal: @@ -810,7 +852,7 @@ def _build_module_generators(self): return (self.element_class(self, [self.letters(x) for x in flatten(tableau)]),) -class KRTableauxBn(KRTableauxTypeC): +class KRTableauxBn(KRTableauxTypeHorizonal): """ Kirillov-Reshetkhin tableaux `B^{n,s}` of type `B_n^{(1)}`. @@ -834,7 +876,7 @@ def _build_module_generators(self): in vertical_dominoes_removed(self._r, self._s // 2)] return tuple(self._fill(sh) for sh in shapes) - def from_Kirillov_Reshetikhin_crystal(self, krc): + def from_kirillov_reshetikhin_crystal(self, krc): """ Construct an element of ``self`` from the Kirillov-Reshetikhin crystal element ``krc``. @@ -845,7 +887,7 @@ def from_Kirillov_Reshetikhin_crystal(self, krc): sage: C = KirillovReshetikhinCrystal(['B',3,1], 3,3) sage: krc = C.module_generators[1].f_string([3,2,3,1,3,3]); krc [++-, [[2], [0], [-3]]] - sage: KR.from_Kirillov_Reshetikhin_crystal(krc) + sage: KR.from_kirillov_reshetikhin_crystal(krc) [[1, 1, 2], [2, 2, -3], [-3, -3, -1]] """ # To build a KR tableau from a type B_n spinor KR crystal: @@ -928,7 +970,7 @@ def _latex_(self): from sage.combinat.output import tex_from_array return tex_from_array([[val._latex_() for val in row] for row in self.to_array()]) - def to_Kirillov_Reshetikhin_crystal(self): + def to_kirillov_reshetikhin_crystal(self): r""" Construct a :func:`KirillovReshetikhinCrystal` element from ``self``. @@ -945,7 +987,7 @@ def to_Kirillov_Reshetikhin_crystal(self): sage: KRT = KirillovReshetikhinTableaux(['D',4,1], 2,2) sage: elt = KRT(3,2,-1,1); elt [[2, 1], [3, -1]] - sage: elt.to_Kirillov_Reshetikhin_crystal() + sage: elt.to_kirillov_reshetikhin_crystal() [[2], [3]] TESTS: @@ -956,14 +998,14 @@ def to_Kirillov_Reshetikhin_crystal(self): sage: KRC = KirillovReshetikhinCrystal(['D',4,1], 4, 3) sage: elt = KRT(-3,-4,2,1,-3,-4,2,1,-2,-4,3,1); elt [[1, 1, 1], [2, 2, 3], [-4, -4, -4], [-3, -3, -2]] - sage: ret = elt.to_Kirillov_Reshetikhin_crystal(); ret + sage: ret = elt.to_kirillov_reshetikhin_crystal(); ret [++--, [[1], [3], [-4], [-3]]] sage: test = KRT(ret); test [[1, 1, 1], [2, 2, 3], [-4, -4, -4], [-3, -3, -2]] sage: test == elt True """ - return self.parent().Kirillov_Reshetikhin_crystal()(self) + return self.parent().kirillov_reshetikhin_crystal()(self) @cached_method def to_array(self, rows=True): @@ -1133,10 +1175,10 @@ def e(self, i): [[-2, 1], [-1, -1]] """ if i == 0: - ret = self.to_Kirillov_Reshetikhin_crystal().e0() + ret = self.to_kirillov_reshetikhin_crystal().e0() if ret is None: return None - return ret.to_Kirillov_Reshetikhin_tableau() + return ret.to_kirillov_reshetikhin_tableau() return TensorProductOfRegularCrystalsElement.e(self, i) def f(self, i): @@ -1154,10 +1196,10 @@ def f(self, i): [[1, 1], [2, -1]] """ if i == 0: - ret = self.to_Kirillov_Reshetikhin_crystal().f0() + ret = self.to_kirillov_reshetikhin_crystal().f0() if ret is None: return None - return ret.to_Kirillov_Reshetikhin_tableau() + return ret.to_kirillov_reshetikhin_tableau() return TensorProductOfRegularCrystalsElement.f(self, i) def epsilon(self, i): @@ -1176,7 +1218,7 @@ def epsilon(self, i): 2 """ if i == 0: - return self.to_Kirillov_Reshetikhin_crystal().epsilon0() + return self.to_kirillov_reshetikhin_crystal().epsilon0() return TensorProductOfRegularCrystalsElement.epsilon(self, i) def phi(self, i): @@ -1194,7 +1236,7 @@ def phi(self, i): 2 """ if i == 0: - return self.to_Kirillov_Reshetikhin_crystal().phi0() + return self.to_kirillov_reshetikhin_crystal().phi0() return TensorProductOfRegularCrystalsElement.phi(self, i) KirillovReshetikhinTableaux.Element = KirillovReshetikhinTableauxElement @@ -1368,10 +1410,10 @@ def _build_module_generators(self): sage: KRT._build_module_generators() ([(1,)],) """ - C = self.Kirillov_Reshetikhin_crystal() + C = self.kirillov_reshetikhin_crystal() return tuple(self.element_class(self, mg) for mg in C.module_generators) - def from_Kirillov_Reshetikhin_crystal(self, krc): + def from_kirillov_reshetikhin_crystal(self, krc): """ Construct an element of ``self`` from the Kirillov-Reshetikhin crystal element ``krc``. @@ -1382,7 +1424,7 @@ def from_Kirillov_Reshetikhin_crystal(self, krc): sage: KRC = KirillovReshetikhinCrystal(['E',6,1], 1, 1) sage: krc = KRC.module_generators[0].f(1); krc [(-1, 3)] - sage: KRT.from_Kirillov_Reshetikhin_crystal(krc) + sage: KRT.from_kirillov_reshetikhin_crystal(krc) [(-1, 3)] """ return self.element_class(self, krc) @@ -1408,7 +1450,7 @@ def _repr_diagram(self): except (AttributeError, ValueError): return repr(self) - def to_Kirillov_Reshetikhin_crystal(self): + def to_kirillov_reshetikhin_crystal(self): r""" Construct a :func:`KirillovReshetikhinCrystal` element from ``self``. @@ -1418,7 +1460,7 @@ def to_Kirillov_Reshetikhin_crystal(self): sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) sage: elt = KRT.module_generators[0].f(1); elt [(-1, 3)] - sage: elt.to_Kirillov_Reshetikhin_crystal() + sage: elt.to_kirillov_reshetikhin_crystal() [(-1, 3)] """ return self.value diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index bb965cc4304..ab8c52b7c50 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -131,11 +131,11 @@ class RiggedConfigurationElement(ClonableArray): sage: RC = RiggedConfigurations(['D', 4, 1], [[1,1], [2,1]]) sage: rc_elt = RC(partition_list=[[1], [1,1], [1], [1]]) - sage: tp_krtab = rc_elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); tp_krtab + sage: tp_krtab = rc_elt.to_tensor_product_of_kirillov_reshetikhin_tableaux(); tp_krtab [[-2]] (X) [[1], [2]] - sage: tp_krcrys = rc_elt.to_tensor_product_of_Kirillov_Reshetikhin_crystals(); tp_krcrys + sage: tp_krcrys = rc_elt.to_tensor_product_of_kirillov_reshetikhin_crystals(); tp_krcrys [[[-2]], [[1], [2]]] - sage: tp_krcrys == tp_krtab.to_tensor_product_of_Kirillov_Reshetikhin_crystals() + sage: tp_krcrys == tp_krtab.to_tensor_product_of_kirillov_reshetikhin_crystals() True sage: RC(tp_krcrys) == rc_elt True @@ -375,7 +375,7 @@ def check(self): #print "at:", i, vac_num, partition.rigging[i] raise ValueError("rigging can be at most the vacancy number") - def to_tensor_product_of_Kirillov_Reshetikhin_tableaux(self, display_steps=False): + def to_tensor_product_of_kirillov_reshetikhin_tableaux(self, display_steps=False): r""" Perform the bijection from this rigged configuration to a tensor product of Kirillov-Reshetikhin tableaux given in [RigConBijection]_ @@ -401,11 +401,11 @@ def to_tensor_product_of_Kirillov_Reshetikhin_tableaux(self, display_steps=False EXAMPLES:: sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) - sage: RC(partition_list=[[2], [2,2], [2], [2]]).to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + sage: RC(partition_list=[[2], [2,2], [2], [2]]).to_tensor_product_of_kirillov_reshetikhin_tableaux() [[3, 3], [5, 5]] sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 2]]) sage: elt = RC(partition_list=[[2], [2,2], [1], [1]]) - sage: tp_krt = elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); tp_krt + sage: tp_krt = elt.to_tensor_product_of_kirillov_reshetikhin_tableaux(); tp_krt [[2, 3], [3, -2]] This is invertible by calling @@ -428,7 +428,7 @@ def to_tensor_product_of_Kirillov_Reshetikhin_tableaux(self, display_steps=False from sage.combinat.rigged_configurations.bijection import RCToKRTBijection return RCToKRTBijection(self).run(display_steps) - def to_tensor_product_of_Kirillov_Reshetikhin_crystals(self, display_steps=False): + def to_tensor_product_of_kirillov_reshetikhin_crystals(self, display_steps=False): r""" Return the corresponding tensor product of Kirillov-Reshetikhin crystals. @@ -445,7 +445,7 @@ def to_tensor_product_of_Kirillov_Reshetikhin_crystals(self, display_steps=False sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 2]]) sage: elt = RC(partition_list=[[2], [2,2], [1], [1]]) - sage: krc = elt.to_tensor_product_of_Kirillov_Reshetikhin_crystals(); krc + sage: krc = elt.to_tensor_product_of_kirillov_reshetikhin_crystals(); krc [[[2, 3], [3, -2]]] We can recover the rigged configuration:: @@ -464,8 +464,8 @@ def to_tensor_product_of_Kirillov_Reshetikhin_crystals(self, display_steps=False sage: elt == ret True """ - kr_tab = self.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(display_steps) - return kr_tab.to_tensor_product_of_Kirillov_Reshetikhin_crystals() + kr_tab = self.to_tensor_product_of_kirillov_reshetikhin_tableaux(display_steps) + return kr_tab.to_tensor_product_of_kirillov_reshetikhin_crystals() def nu(self): r""" @@ -533,7 +533,7 @@ def e(self, a): raise ValueError("{} is not in the index set".format(a)) if a == 0: try: - ret = self.to_tensor_product_of_Kirillov_Reshetikhin_tableaux().e(0) + ret = self.to_tensor_product_of_kirillov_reshetikhin_tableaux().e(0) if ret is None: return None return ret.to_rigged_configuration() @@ -700,7 +700,7 @@ def f(self, a): raise ValueError("{} is not in the index set".format(a)) if a == 0: try: - ret = self.to_tensor_product_of_Kirillov_Reshetikhin_tableaux().f(0) + ret = self.to_tensor_product_of_kirillov_reshetikhin_tableaux().f(0) if ret is None: return None return ret.to_rigged_configuration() @@ -901,7 +901,7 @@ def classical_weight(self): This agrees with the corresponding classical weight as KR tableaux:: - sage: krt = elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); krt + sage: krt = elt.to_tensor_product_of_kirillov_reshetikhin_tableaux(); krt [[2, 1], [3, -1]] sage: krt.classical_weight() == elt.classical_weight() True @@ -913,7 +913,7 @@ def classical_weight(self): sage: RC = RiggedConfigurations(['A',2,1], [[2,1], [1,1]]) sage: passed_test = True sage: for x in RC: - ....: y = x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + ....: y = x.to_tensor_product_of_kirillov_reshetikhin_tableaux() ....: if x.classical_weight() != y.classical_weight(): ....: passed_test = False ....: break diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 586819bb2d3..301fe832692 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -148,11 +148,11 @@ tableaux, we can type the following. This example was checked against Reiho Sakamoto's Mathematica program on rigged configurations:: - sage: output = elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); output + sage: output = elt.to_tensor_product_of_kirillov_reshetikhin_tableaux(); output [[1, 1, 1], [2, 3, 3], [3, 4, -5]] (X) [[1, 1], [2, 2], [3, 3], [5, -6], [6, -5]] (X) [[1, 1, 2], [2, 2, 3], [3, 3, 7], [4, 4, -7]] (X) [[1, 1, 1], [2, 2, 2]] (X) [[1, 1, 1, 3], [2, 2, 3, 4], [3, 3, 4, 5], [4, 4, 5, 6]] (X) [[1], [2], [3]] (X) [[1, 1, 1, 1]] (X) [[1, 1], [2, 2]] - sage: elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux().to_rigged_configuration() == elt + sage: elt.to_tensor_product_of_kirillov_reshetikhin_tableaux().to_rigged_configuration() == elt True - sage: output.to_rigged_configuration().to_tensor_product_of_Kirillov_Reshetikhin_tableaux() == output + sage: output.to_rigged_configuration().to_tensor_product_of_kirillov_reshetikhin_tableaux() == output True To get the highest weight rigged configurations, use the attribute @@ -356,13 +356,13 @@ class RiggedConfigurations(Parent, UniqueRepresentation): tableaux, we can type the following. This example was checked against Reiho Sakamoto's Mathematica program on rigged configurations:: - sage: output = elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); output + sage: output = elt.to_tensor_product_of_kirillov_reshetikhin_tableaux(); output [[1, 1, 1], [2, 3, 3], [3, 4, -5]] (X) [[1, 1], [2, 2], [3, 3], [5, -6], [6, -5]] (X) [[1, 1, 2], [2, 2, 3], [3, 3, 7], [4, 4, -7]] (X) [[1, 1, 1], [2, 2, 2]] (X) [[1, 1, 1, 3], [2, 2, 3, 4], [3, 3, 4, 5], [4, 4, 5, 6]] (X) [[1], [2], [3]] (X) [[1, 1, 1, 1]] (X) [[1, 1], [2, 2]] - sage: elt.to_tensor_product_of_Kirillov_Reshetikhin_tableaux().to_rigged_configuration() == elt + sage: elt.to_tensor_product_of_kirillov_reshetikhin_tableaux().to_rigged_configuration() == elt True - sage: output.to_rigged_configuration().to_tensor_product_of_Kirillov_Reshetikhin_tableaux() == output + sage: output.to_rigged_configuration().to_tensor_product_of_kirillov_reshetikhin_tableaux() == output True We can also convert between rigged configurations and tensor products of @@ -370,7 +370,7 @@ class RiggedConfigurations(Parent, UniqueRepresentation): sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) sage: elt = RC(partition_list=[[1],[1,1],[1],[1]]) - sage: tp_krc = elt.to_tensor_product_of_Kirillov_Reshetikhin_crystals(); tp_krc + sage: tp_krc = elt.to_tensor_product_of_kirillov_reshetikhin_crystals(); tp_krc [[]] sage: ret = RC(tp_krc) sage: ret == elt @@ -385,7 +385,7 @@ class RiggedConfigurations(Parent, UniqueRepresentation): sage: t = T[1]; t [[++++, []], [+++-, [[1], [2], [4], [-4]]]] sage: ret = RC(t) - sage: ret.to_tensor_product_of_Kirillov_Reshetikhin_crystals() + sage: ret.to_tensor_product_of_kirillov_reshetikhin_crystals() [[++++, []], [+++-, [[1], [2], [4], [-4]]]] """ @staticmethod @@ -688,8 +688,8 @@ def _element_constructor_(self, *lst, **options): lst = lst[0] from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinGenericCrystalElement if isinstance(lst[0], KirillovReshetikhinGenericCrystalElement): - KRT = self.tensor_product_of_Kirillov_Reshetikhin_tableaux() - krt_elt = KRT(*[x.to_Kirillov_Reshetikhin_tableau() for x in lst]) + KRT = self.tensor_product_of_kirillov_reshetikhin_tableaux() + krt_elt = KRT(*[x.to_kirillov_reshetikhin_tableau() for x in lst]) return krt_elt.to_rigged_configuration() return self.element_class(self, list(lst), **options) @@ -758,7 +758,7 @@ def kleber_tree(self): return KleberTree(self._cartan_type, self.dims) @cached_method - def tensor_product_of_Kirillov_Reshetikhin_tableaux(self): + def tensor_product_of_kirillov_reshetikhin_tableaux(self): """ Return the corresponding tensor product of Kirillov-Reshetikhin tableaux. @@ -766,7 +766,7 @@ def tensor_product_of_Kirillov_Reshetikhin_tableaux(self): EXAMPLES:: sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2]]) - sage: RC.tensor_product_of_Kirillov_Reshetikhin_tableaux() + sage: RC.tensor_product_of_kirillov_reshetikhin_tableaux() Tensor product of Kirillov-Reshetikhin tableaux of type ['A', 3, 1] and factor(s) ((3, 2), (1, 2)) """ from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux @@ -789,12 +789,12 @@ def cardinality(self): 134 """ try: - return self.tensor_product_of_Kirillov_Reshetikhin_tableaux().cardinality() + return self.tensor_product_of_kirillov_reshetikhin_tableaux().cardinality() except NotImplementedError: # Backup by direct counting return ZZ(sum(1 for x in self)) @cached_method - def tensor_product_of_Kirillov_Reshetikhin_crystals(self): + def tensor_product_of_kirillov_reshetikhin_crystals(self): """ Return the corresponding tensor product of Kirillov-Reshetikhin crystals. @@ -802,12 +802,12 @@ def tensor_product_of_Kirillov_Reshetikhin_crystals(self): EXAMPLES:: sage: RC = RiggedConfigurations(['A', 3, 1], [[3,1],[2,2]]) - sage: RC.tensor_product_of_Kirillov_Reshetikhin_crystals() + sage: RC.tensor_product_of_kirillov_reshetikhin_crystals() Full tensor product of the crystals [Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(3,1), Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(2,2)] """ - return self.tensor_product_of_Kirillov_Reshetikhin_tableaux().tensor_product_of_Kirillov_Reshetikhin_crystals() + return self.tensor_product_of_kirillov_reshetikhin_tableaux().tensor_product_of_kirillov_reshetikhin_crystals() def fermionic_formula(self, q=None): r""" @@ -870,7 +870,7 @@ def _test_bijection(self, **options): tester = self._tester(**options) rejects = [] for x in self: - y = x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + y = x.to_tensor_product_of_kirillov_reshetikhin_tableaux() z = y.to_rigged_configuration() if z != x: rejects.append((x, z)) @@ -1751,7 +1751,7 @@ def R_matrix(ct, RS1, RS2, only_highest_weight=False): L = RC for x in L: x2 = RC2(*x) - ret_list.append([x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(), - x2.to_tensor_product_of_Kirillov_Reshetikhin_tableaux()]) + ret_list.append([x.to_tensor_product_of_kirillov_reshetikhin_tableaux(), + x2.to_tensor_product_of_kirillov_reshetikhin_tableaux()]) return ret_list diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index d268768b6b0..e68fe059f13 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -112,7 +112,7 @@ def __getitem__(self, i): [[1], [2], [3]] (X) [[1], [2]] """ if self._cache is None: - self._cache = [x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + self._cache = [x.to_tensor_product_of_kirillov_reshetikhin_tableaux() for x in self.tp_krt.rigged_configurations().module_generators] return self._cache[i] @@ -130,7 +130,7 @@ def __iter__(self): [[1], [-1]] """ if self._cache is None: - self._cache = [x.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + self._cache = [x.to_tensor_product_of_kirillov_reshetikhin_tableaux() for x in self.tp_krt.rigged_configurations().module_generators] for x in self._cache: yield x @@ -229,7 +229,7 @@ class TensorProductOfKirillovReshetikhinTableaux(FullTensorProductOfRegularCryst (/) - sage: tp_krc = tp_krt.to_tensor_product_of_Kirillov_Reshetikhin_crystals(); tp_krc + sage: tp_krc = tp_krt.to_tensor_product_of_kirillov_reshetikhin_crystals(); tp_krc [[[1], [2], [3]], [[2, 2], [3, 3]]] sage: KRT(tp_krc) == tp_krt True @@ -331,7 +331,7 @@ def _test_bijection(self, **options): rejects = [] for x in self: y = x.to_rigged_configuration() - z = y.to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + z = y.to_tensor_product_of_kirillov_reshetikhin_tableaux() if z != x: rejects.append((x, z)) @@ -359,18 +359,18 @@ def _element_constructor_(self, *path, **options): from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinGenericCrystalElement if isinstance(path[0], KirillovReshetikhinGenericCrystalElement): - return self.element_class(self, [x.to_Kirillov_Reshetikhin_tableau() for x in path]) + return self.element_class(self, [x.to_kirillov_reshetikhin_tableau() for x in path]) from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement if isinstance(path[0], TensorProductOfRegularCrystalsElement) and \ isinstance(path[0][0], KirillovReshetikhinGenericCrystalElement): - return self.element_class(self, [x.to_Kirillov_Reshetikhin_tableau() for x in path[0]]) + return self.element_class(self, [x.to_kirillov_reshetikhin_tableau() for x in path[0]]) from sage.combinat.rigged_configurations.rigged_configuration_element import RiggedConfigurationElement if isinstance(path[0], RiggedConfigurationElement): if self.rigged_configurations() != path[0].parent(): raise ValueError("incorrect bijection image") - return path[0].to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + return path[0].to_tensor_product_of_kirillov_reshetikhin_tableaux() return self.element_class(self, list(path), **options) @@ -407,7 +407,7 @@ def rigged_configurations(self): return RiggedConfigurations(self.cartan_type(), self.dims) @cached_method - def tensor_product_of_Kirillov_Reshetikhin_crystals(self): + def tensor_product_of_kirillov_reshetikhin_crystals(self): """ Return the corresponding tensor product of Kirillov-Reshetikhin crystals. @@ -415,7 +415,7 @@ def tensor_product_of_Kirillov_Reshetikhin_crystals(self): EXAMPLES:: sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1],[2,2]]) - sage: KRT.tensor_product_of_Kirillov_Reshetikhin_crystals() + sage: KRT.tensor_product_of_kirillov_reshetikhin_crystals() Full tensor product of the crystals [Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(3,1), Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(2,2)] @@ -425,12 +425,12 @@ def tensor_product_of_Kirillov_Reshetikhin_crystals(self): sage: KR1 = KirillovReshetikhinCrystal(['D', 4, 1], 4, 1) sage: KR2 = KirillovReshetikhinCrystal(['D', 4, 1], 3, 3) sage: T = TensorProductOfCrystals(KR1, KR2) - sage: T == KRT.tensor_product_of_Kirillov_Reshetikhin_crystals() + sage: T == KRT.tensor_product_of_kirillov_reshetikhin_crystals() True - sage: T is KRT.tensor_product_of_Kirillov_Reshetikhin_crystals() + sage: T is KRT.tensor_product_of_kirillov_reshetikhin_crystals() True """ - return FullTensorProductOfRegularCrystals(tuple(x.Kirillov_Reshetikhin_crystal() for x in self.crystals), + return FullTensorProductOfRegularCrystals(tuple(x.kirillov_reshetikhin_crystal() for x in self.crystals), cartan_type=self.cartan_type()) TensorProductOfKirillovReshetikhinTableaux.Element = TensorProductOfKirillovReshetikhinTableauxElement diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py index 3d54189cb21..024c15ca7b7 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py @@ -107,7 +107,7 @@ class TensorProductOfKirillovReshetikhinTableauxElement(TensorProductOfRegularCr 1[ ]0 - sage: T.to_rigged_configuration().to_tensor_product_of_Kirillov_Reshetikhin_tableaux() + sage: T.to_rigged_configuration().to_tensor_product_of_kirillov_reshetikhin_tableaux() [[2], [3]] (X) [[1]] (X) [[-1]] (X) [[1]] """ def __init__(self, parent, list=[[]], **options): @@ -309,12 +309,12 @@ def to_rigged_configuration(self, display_steps=False): This is invertible by calling - :meth:`~sage.combinat.rigged_configurations.rigged_configuration_element.RiggedConfigurationElement.to_tensor_product_of_Kirillov_Reshetikhin_tableaux()`:: + :meth:`~sage.combinat.rigged_configurations.rigged_configuration_element.RiggedConfigurationElement.to_tensor_product_of_kirillov_reshetikhin_tableaux()`:: sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D', 4, 1], [[2,2]]) sage: T = KRT(pathlist=[[2,1,4,3]]) sage: rc = T.to_rigged_configuration() - sage: ret = rc.to_tensor_product_of_Kirillov_Reshetikhin_tableaux(); ret + sage: ret = rc.to_tensor_product_of_kirillov_reshetikhin_tableaux(); ret [[1, 3], [2, 4]] sage: ret == T True @@ -322,7 +322,7 @@ def to_rigged_configuration(self, display_steps=False): from sage.combinat.rigged_configurations.bijection import KRTToRCBijection return KRTToRCBijection(self).run(display_steps) - def to_tensor_product_of_Kirillov_Reshetikhin_crystals(self): + def to_tensor_product_of_kirillov_reshetikhin_crystals(self): """ Return a tensor product of Kirillov-Reshetikhin crystals corresponding to ``self``. @@ -335,7 +335,7 @@ def to_tensor_product_of_Kirillov_Reshetikhin_crystals(self): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1], [[1,1],[2,2]]) sage: elt = KRT(pathlist=[[-1],[-1,2,-1,1]]); elt [[-1]] (X) [[2, 1], [-1, -1]] - sage: tp_krc = elt.to_tensor_product_of_Kirillov_Reshetikhin_crystals(); tp_krc + sage: tp_krc = elt.to_tensor_product_of_kirillov_reshetikhin_crystals(); tp_krc [[[-1]], [[2], [-1]]] We can recover the original tensor product of KR tableaux:: @@ -345,6 +345,6 @@ def to_tensor_product_of_Kirillov_Reshetikhin_crystals(self): sage: ret == elt True """ - TP = self.parent().tensor_product_of_Kirillov_Reshetikhin_crystals() - return TP(*[x.to_Kirillov_Reshetikhin_crystal() for x in self]) + TP = self.parent().tensor_product_of_kirillov_reshetikhin_crystals() + return TP(*[x.to_kirillov_reshetikhin_crystal() for x in self]) From bb1023df3f2bc0c934a6f285bdbcd1ca8cfd6607 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 8 Nov 2013 08:44:54 -0800 Subject: [PATCH 142/206] Fix to type D bijection. Some fixes to type B. --- .../rigged_configurations/bij_type_B.py | 14 +++++++++---- .../rigged_configurations/bij_type_D.py | 21 +++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/bij_type_B.py b/src/sage/combinat/rigged_configurations/bij_type_B.py index bfa753abd36..19a7d21894b 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_B.py +++ b/src/sage/combinat/rigged_configurations/bij_type_B.py @@ -172,6 +172,9 @@ def run(self, verbose=False): print("Applying halving map") # Convert back to a type B_n^{(1)} + for i in range(len(self.cur_dims)): + if bij.cur_dims[i][0] != self.n: + bij.cur_dims[i][1] //= 2 for i in range(self.n-1): for j in range(len(bij.ret_rig_con[i])): bij.ret_rig_con[i]._list[j] //= 2 @@ -368,7 +371,7 @@ def next_state(self, val): p.rigging[i] = None width_n = max_width case_QS = True - elif p.vacancy_numbers[i] - 1 == p.rigging[i] and not case_QS and not singular_max_width: + elif p.vacancy_numbers[i] - 1 == p.rigging[i] and not case_QS and not singular_max_width and p._list[i] < max_width: # This isn't correct!!!!! case_QS = True max_width = p._list[i] p._list[i] += 1 @@ -415,7 +418,8 @@ def next_state(self, val): self._update_vacancy_nums(tableau_height) self._update_partition_values(tableau_height) - if 0 < pos_val and pos_val <= tableau_height: + assert pos_val > 0 + if pos_val <= tableau_height: for a in range(pos_val-1, tableau_height): self._update_vacancy_nums(a) self._update_partition_values(a) @@ -499,7 +503,8 @@ def other_outcome(self, rc, pos_val, width_n): self._update_vacancy_nums(tableau_height) self._update_partition_values(tableau_height) - if 0 < pos_val and pos_val <= tableau_height: + assert pos_val > 0 + if pos_val <= tableau_height: for a in range(pos_val-1, tableau_height): self._update_vacancy_nums(a) self._update_partition_values(a) @@ -728,7 +733,8 @@ def next_state(self, height): if not case_Q: # We found a singular string above the quasi-singular one break ell[n-1] = i - last_size = partition[i] + 1 + last_size = partition[i] + # Now check for case QS elif partition.vacancy_numbers[i] == partition.rigging[i]: ell[2*n-1] = i last_size = partition[i] diff --git a/src/sage/combinat/rigged_configurations/bij_type_D.py b/src/sage/combinat/rigged_configurations/bij_type_D.py index dad695cd844..b72214a7c52 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_D.py +++ b/src/sage/combinat/rigged_configurations/bij_type_D.py @@ -178,8 +178,12 @@ def next_state(self, val): if pos_val == n: # Special case for `\overline{n}` and adding to make height `n` + # where we only update the vacancy numbers # This only occurs with `r = n - 1` if self.cur_dims[0][0] == n - 1 and tableau_height == n - 1: + self._update_vacancy_nums(n-2) + self._update_vacancy_nums(n-1) + self._correct_vacancy_nums() return if len(self.ret_rig_con[n - 1]) > 0: @@ -310,7 +314,6 @@ def _correct_vacancy_nums(self): (/) - """ pos = self.n - 2 if self.cur_dims[0][0] == len(self.cur_path[0]): @@ -400,12 +403,12 @@ def halving_map(self): """ # Skip the first column since it is a spinor for i in range(1, len(self.cur_dims)): - self.cur_dims[i][1] /= 2 + self.cur_dims[i][1] //= 2 for i in range(len(self.ret_rig_con)): for j in range(len(self.ret_rig_con[i])): - self.ret_rig_con[i]._list[j] /= 2 - self.ret_rig_con[i].rigging[j] /= 2 - self.ret_rig_con[i].vacancy_numbers[j] /= 2 + self.ret_rig_con[i]._list[j] //= 2 + self.ret_rig_con[i].rigging[j] //= 2 + self.ret_rig_con[i].vacancy_numbers[j] //= 2 class RCToKRTBijectionTypeD(RCToKRTBijectionTypeA): r""" @@ -693,12 +696,12 @@ def halving_map(self): True """ for i in range(len(self.cur_dims)): - self.cur_dims[i][1] /= 2 + self.cur_dims[i][1] //= 2 for partition in self.cur_partitions: for j in range(len(partition)): - partition._list[j] /= 2 - partition.rigging[j] /= 2 - partition.vacancy_numbers[j] /= 2 + partition._list[j] //= 2 + partition.rigging[j] //= 2 + partition.vacancy_numbers[j] //= 2 def _correct_vacancy_nums(self): """ From 25f77654947cf8e6a86393126392c1b06f0c9738 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 8 Nov 2013 10:25:54 -0800 Subject: [PATCH 143/206] Fixed type B bijection. --- src/sage/combinat/rigged_configurations/bij_type_B.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/bij_type_B.py b/src/sage/combinat/rigged_configurations/bij_type_B.py index 19a7d21894b..9c9eb2d088e 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_B.py +++ b/src/sage/combinat/rigged_configurations/bij_type_B.py @@ -296,7 +296,7 @@ def next_state(self, val): # Always add a cell to the first singular value in the first # tableau we are updating. if len(self.ret_rig_con[pos_val - 1]) > 0: - max_width = self.ret_rig_con[pos_val - 1][0] + max_width = self.ret_rig_con[pos_val - 1][0] + 1 else: max_width = 0 @@ -363,15 +363,16 @@ def next_state(self, val): p._list[i] += 1 # We always at least add a box to the first singular value p.rigging[i] = None if case_QS: + width_n = p._list[i] break singular_max_width = True - elif p._list[i] == max_width + 1: + elif p._list[i] == max_width + 1 and not case_QS: # If we can't add 2 boxes, we must be in case (QS) p._list[i] += 1 p.rigging[i] = None width_n = max_width case_QS = True - elif p.vacancy_numbers[i] - 1 == p.rigging[i] and not case_QS and not singular_max_width and p._list[i] < max_width: # This isn't correct!!!!! + elif p.vacancy_numbers[i] - 1 == p.rigging[i] and not case_QS and not singular_max_width and p._list[i] <= max_width: case_QS = True max_width = p._list[i] p._list[i] += 1 From 432e7788b4c01f6c562e531851b43dd6957fd0b2 Mon Sep 17 00:00:00 2001 From: Frederic Chapoton Date: Fri, 8 Nov 2013 22:37:52 +0100 Subject: [PATCH 144/206] Trac #15385: cleanup of hadamard matrices --- src/doc/en/reference/combinat/index.rst | 1 + src/sage/combinat/matrices/hadamard_matrix.py | 185 ++++++++++-------- 2 files changed, 102 insertions(+), 84 deletions(-) diff --git a/src/doc/en/reference/combinat/index.rst b/src/doc/en/reference/combinat/index.rst index 134ebbf77ee..c98d834a46e 100644 --- a/src/doc/en/reference/combinat/index.rst +++ b/src/doc/en/reference/combinat/index.rst @@ -25,6 +25,7 @@ Combinatorics sage/combinat/e_one_star sage/combinat/finite_class sage/combinat/hall_polynomial + sage/combinat/matrices/hadamard_matrix sage/combinat/integer_list sage/combinat/integer_matrices sage/combinat/integer_vector diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index b09f310ece7..87f5b1757be 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -1,127 +1,141 @@ r""" Hadamard matrices -A Hadamard matrix is an nxn matrix H whose entries are either +1 or -1 and whose -rows are mutually orthogonal. For example, the matrix `H_2` defined by:: - - [ 1 1 ] - [ 1 -1 ] - -is a Hadamard matrix. An nxn matrix H whose entries are either +1 or -1 -is a Hadamard matrix if and only if - (a) `|det(H)|=n^(n/2),` or - (b) `H*H^t = n\cdot I_n`, where `I_n` is the identity matrix. -In general, the tensor product of an mxm Hadamard matrix and an `n\times n` Hadamard -matrix is an `(mn)\times (mn)` matrix. In particular, if there is an nxn Hadamard -matrix then there is a `(2n)\times (2n)` Hadamard matrix (since one may tensor with `H_2`). +A Hadamard matrix is an `n\times n` matrix `H` whose entries are either `+1` or `-1` +and whose rows are mutually orthogonal. For example, the matrix `H_2` +defined by + +.. MATH:: + + \left(\begin{array}{rr} + 1 & 1 \\ + 1 & -1 + \end{array}\right) + +is a Hadamard matrix. An `n\times n` matrix `H` whose entries are either `+1` or +`-1` is a Hadamard matrix if and only if: + +(a) `|det(H)|=n^{n/2}` or + +(b) `H*H^t = n\cdot I_n`, where `I_n` is the identity matrix. + +In general, the tensor product of an `m\times m` Hadamard matrix and an +`n\times n` Hadamard matrix is an `(mn)\times (mn)` matrix. In +particular, if there is an `n\times n` Hadamard matrix then there is a +`(2n)\times (2n)` Hadamard matrix (since one may tensor with `H_2`). This particular case is sometimes called the Sylvester construction. -The Hadamard conjecture (possibly due to Paley) states that a Hadamard matrix of -order `n` exists if and only if `n=2` or `n` is a multiple of `4`. -The module below implements the Paley constructions (see for example [3]_) and -the Sylvester construction. It also allows you to pull a Hadamard matrix -from the database at [1]_. +The Hadamard conjecture (possibly due to Paley) states that a Hadamard +matrix of order `n` exists if and only if `n=2` or `n` is a multiple +of `4`. + +The module below implements the Paley constructions (see for example +[Hora]_) and the Sylvester construction. It also allows you to pull a +Hadamard matrix from the database at [HadaSloa]_. -AUTHOR:: - -- David Joyner (2009-05-17): initial version +AUTHORS: -REFERENCES:: - .. [1] N.J.A. Sloane's Library of Hadamard Matrices, at - http://www.research.att.com/~njas/hadamard - .. [2] Hadamard matrices on Wikipedia, at http://en.wikipedia.org/wiki/Hadamard_matrix - .. [3] K. J. Horadam, Hadamard Matrices and Their Applications, Princeton University - Press, 2006. +- David Joyner (2009-05-17): initial version +REFERENCES: + +.. [HadaSloa] N.J.A. Sloane's Library of Hadamard Matrices, at + http://neilsloane.com/hadamard/ +.. [HadaWiki] Hadamard matrices on Wikipedia, :wikipedia:`Hadamard_matrix` +.. [Hora] K. J. Horadam, Hadamard Matrices and Their Applications, + Princeton University Press, 2006. """ from sage.rings.arith import kronecker_symbol -from sage.rings.integer_ring import IntegerRing -ZZ = IntegerRing() +from sage.rings.integer_ring import ZZ +from sage.rings.integer import Integer from sage.matrix.constructor import matrix, block_matrix -import urllib +from urllib import urlopen from sage.misc.functional import is_even from sage.rings.arith import is_prime -def H1(i,j,p): + +def H1(i, j, p): """ Returns the i,j-th entry of the Paley matrix, type I case. EXAMPLES:: + sage: sage.combinat.matrices.hadamard_matrix.H1(1,2,3) -1 """ - if i==0: + if i == 0: return 1 - if j==0: + if j == 0: return -1 - if i==j: + if i == j: return 1 - return kronecker_symbol(i-j,p) + return kronecker_symbol(i - j, p) -def H2(i,j,p): +def H2(i, j, p): """ Returns the i,j-th entry of the Paley matrix, type II case. EXAMPLES:: + sage: sage.combinat.matrices.hadamard_matrix.H1(1,2,5) 1 - """ - if i==0 and j==0: + if i == 0 and j == 0: return 0 - if i==0 or j==0: + if i == 0 or j == 0: return 1 - if j==0: + if j == 0: return -1 - if i==j: + if i == j: return 0 - return kronecker_symbol(i-j,p) + return kronecker_symbol(i - j, p) + def hadamard_matrix_paleyI(n): """ Implements the Paley type I construction. - EXAMPLES:: + sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyI(4) [ 1 -1 -1 -1] [ 1 1 1 -1] [ 1 -1 1 1] [ 1 1 -1 1] """ - if is_prime(n-1) and (n-1)%4==3: - p = n-1 - else: - raise ValueError, "The order %s is not covered by the Paley type I construction."%n - return matrix(ZZ,[[H1(i,j,p) for i in range(n)] for j in range(n)]) + p = n - 1 + if not(is_prime(p) and (p % 4 == 3)): + raise ValueError("The order %s is not covered by the Paley type I construction." % n) + return matrix(ZZ, [[H1(i, j, p) for i in range(n)] for j in range(n)]) def hadamard_matrix_paleyII(n): """ Implements the Paley type II construction. - EXAMPLES:: + sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyI(12).det() 2985984 sage: 12^6 2985984 - """ - N = ZZ(n/2) - if is_prime(N-1) and (N-1)%4==1: - p = N-1 - else: - raise ValueError, "The order %s is not covered by the Paley type II construction."%n - S = matrix(ZZ,[[H2(i,j,p) for i in range(N)] for j in range(N)]) - return block_matrix([[S+1,S-1],[S-1,-S-1]]) + N = Integer(n/2) + p = N - 1 + if not(is_prime(p) and (p % 4 == 1)): + raise ValueError("The order %s is not covered by the Paley type II construction." % n) + S = matrix(ZZ, [[H2(i, j, p) for i in range(N)] for j in range(N)]) + return block_matrix([[S + 1, S - 1], [S - 1, -S - 1]]) + def hadamard_matrix(n): """ - Tries to construct a Hadamard matrix using a combination of Paley and Sylvester - constructions. + Tries to construct a Hadamard matrix using a combination of Paley + and Sylvester constructions. EXAMPLES:: + sage: hadamard_matrix(12).det() 2985984 sage: 12^6 @@ -138,45 +152,48 @@ def hadamard_matrix(n): [ 1 -1 1 -1 -1 1 -1 1] [ 1 1 -1 -1 -1 -1 1 1] [ 1 -1 -1 1 -1 1 1 -1] - sage: hadamard_matrix(8).det()==8^4 + sage: hadamard_matrix(8).det() == 8^4 True - """ - if not(n%4==0) and not(n==2): - raise ValueError, "The Hadamard matrix of order %s does not exist"%n - if n==2: - return matrix([[1,1],[1,-1]]) + if not(n % 4 == 0) and (n != 2): + raise ValueError("The Hadamard matrix of order %s does not exist" % n) + if n == 2: + return matrix([[1, 1], [1, -1]]) if is_even(n): - N = ZZ(n/2) - elif n==1: + N = Integer(n / 2) + elif n == 1: return matrix([1]) - if is_prime(N-1) and (N-1)%4==1: + if is_prime(N - 1) and (N - 1) % 4 == 1: return hadamard_matrix_paleyII(n) - elif n==4 or n%8==0: - had = hadamard_matrix(ZZ(n/2)) - chad1 = matrix([list(r)+list(r) for r in had.rows()]) - mhad = (-1)*had + elif n == 4 or n % 8 == 0: + had = hadamard_matrix(Integer(n / 2)) + chad1 = matrix([list(r) + list(r) for r in had.rows()]) + mhad = (-1) * had R = len(had.rows()) - chad2 = matrix([list(had.rows()[i])+list(mhad.rows()[i]) for i in range(R)]) + chad2 = matrix([list(had.rows()[i]) + list(mhad.rows()[i]) + for i in range(R)]) return chad1.stack(chad2) - elif is_prime(N-1) and (N-1)%4==3: + elif is_prime(N - 1) and (N - 1) % 4 == 3: return hadamard_matrix_paleyI(n) else: - raise ValueError, "The Hadamard matrix of order %s is not yet implemented."%n + raise ValueError("The Hadamard matrix of order %s is not yet implemented." % n) + def hadamard_matrix_www(url_file, comments=False): """ Pulls file from Sloanes database and returns the corresponding Hadamard - matrix as a Sage matrix. You must input a filename of the form - "had.n.xxx.txt" as described on the webpage - http://www.research.att.com/~njas/hadamard/, where "xxx" could be - empty or a number of some characters. + matrix as a Sage matrix. + + You must input a filename of the form "had.n.xxx.txt" as described + on the webpage http://neilsloane.com/hadamard/, where + "xxx" could be empty or a number of some characters. If comments=True then the "Automorphism..." line of the had.n.xxx.txt file is printed if it exists. Otherwise nothing is done. EXAMPLES:: - sage: hadamard_matrix_www("had.4.txt") # optional - internet + + sage: hadamard_matrix_www("had.4.txt") # optional - internet [ 1 1 1 1] [ 1 -1 1 -1] [ 1 1 -1 -1] @@ -202,20 +219,20 @@ def hadamard_matrix_www(url_file, comments=False): """ n = eval(url_file.split(".")[1]) rws = [] - url = "http://www.research.att.com/~njas/hadamard/"+url_file - f = urllib.urlopen(url) + url = "http://neilsloane.com/hadamard/" + url_file + f = urlopen(url) s = f.readlines() for i in range(n): r = [] for j in range(n): - if s[i][j]=="+": + if s[i][j] == "+": r.append(1) else: r.append(-1) rws.append(r) f.close() - if comments==True: + if comments: lastline = s[-1] - if lastline[0]=="A": + if lastline[0] == "A": print lastline return matrix(rws) From 90b0b8da2745bd0b81d55fee566db355f6d64d9e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 9 Nov 2013 15:24:30 -0800 Subject: [PATCH 145/206] Another fix for type B. --- src/sage/combinat/rigged_configurations/bij_type_B.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/combinat/rigged_configurations/bij_type_B.py b/src/sage/combinat/rigged_configurations/bij_type_B.py index 9c9eb2d088e..ab436141679 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_B.py +++ b/src/sage/combinat/rigged_configurations/bij_type_B.py @@ -291,6 +291,9 @@ def next_state(self, val): self._update_partition_values(a + 1) self._update_vacancy_nums(tableau_height) self._update_partition_values(tableau_height) + if tableau_height > 0: + self._update_vacancy_nums(tableau_height-1) + self._update_partition_values(tableau_height-1) return # Always add a cell to the first singular value in the first From 7723f9578290cb91e12801a92e0aaf203abdd688 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 13 Nov 2013 14:12:37 -0800 Subject: [PATCH 146/206] Fixes for type A2 dual bijection. --- src/sage/combinat/rigged_configurations/bij_type_A2_dual.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py b/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py index 78085ff3558..018080e5070 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py +++ b/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py @@ -89,6 +89,9 @@ def next_state(self, val): self._update_partition_values(a + 1) self._update_vacancy_nums(tableau_height) self._update_partition_values(tableau_height) + if tableau_height > 0: + self._update_vacancy_nums(tableau_height-1) + self._update_partition_values(tableau_height-1) # Make the new string at n quasi-singular p = self.ret_rig_con[n-1] @@ -194,6 +197,7 @@ def next_state(self, val): for i in range(num_rows): if partition._list[i] == width_n: partition.rigging[i] = partition.rigging[i] - QQ(1)/QQ(2) + break class RCToKRTBijectionTypeA2Dual(RCToKRTBijectionTypeC): r""" From 531f21b5c2026d7e5d29943125563536cc7be2d9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 13 Nov 2013 20:36:05 -0800 Subject: [PATCH 147/206] Fixed bijection for special case of A^(2)_2. --- .../rigged_configurations/bij_type_A2_dual.py | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py b/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py index 018080e5070..7f8e0966463 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py +++ b/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py @@ -277,12 +277,13 @@ def next_state(self, height): # Determine the new rigged configuration by removing boxes from the # selected string and then making the new string singular - if case_S[0]: - row_num = None - row_num_bar = self.cur_partitions[0].remove_cell(ell[n], 2) - else: - row_num = self.cur_partitions[0].remove_cell(ell[0]) - row_num_bar = self.cur_partitions[0].remove_cell(ell[n]) + if n > 1: + if case_S[0]: + row_num = None + row_num_bar = self.cur_partitions[0].remove_cell(ell[n], 2) + else: + row_num = self.cur_partitions[0].remove_cell(ell[0]) + row_num_bar = self.cur_partitions[0].remove_cell(ell[n]) for a in range(1, n-1): if case_S[a]: row_num_next = None @@ -311,12 +312,13 @@ def next_state(self, height): else: row_num_next = None row_num_bar_next = None - - self._update_vacancy_numbers(n - 2) - if row_num is not None: - self.cur_partitions[n-2].rigging[row_num] = self.cur_partitions[n-2].vacancy_numbers[row_num] - if row_num_bar is not None: - self.cur_partitions[n-2].rigging[row_num_bar] = self.cur_partitions[n-2].vacancy_numbers[row_num_bar] + + if n > 1: + self._update_vacancy_numbers(n - 2) + if row_num is not None: + self.cur_partitions[n-2].rigging[row_num] = self.cur_partitions[n-2].vacancy_numbers[row_num] + if row_num_bar is not None: + self.cur_partitions[n-2].rigging[row_num_bar] = self.cur_partitions[n-2].vacancy_numbers[row_num_bar] self._update_vacancy_numbers(n - 1) if row_num_next is not None: @@ -329,3 +331,4 @@ def next_state(self, height): self.cur_partitions[n-1].rigging[row_num_bar_next] = self.cur_partitions[n-1].vacancy_numbers[row_num_bar_next] return(b) + From 4970ced8b2c04c21b086984ac48bbba06cb5e6b7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 14 Nov 2013 08:58:20 -0800 Subject: [PATCH 148/206] Expanded on doctests for corner cases for rigged configurations. --- .../combinat/rigged_configurations/rigged_configurations.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 301fe832692..3e30707381d 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -445,6 +445,8 @@ def __init__(self, cartan_type, B): sage: TestSuite(RC).run() # long time (4s on sage.math, 2012) sage: RC = RiggedConfigurations(['A',1,1], [[1,1], [1,1]]) sage: TestSuite(RC).run() + sage: RC = RiggedConfigurations(['A',2,1], [[1,1], [2,1]]) + sage: TestSuite(RC).run() sage: RC = RiggedConfigurations(['D', 4, 1], [[2,2]]) sage: TestSuite(RC).run() # long time sage: RC = RiggedConfigurations(['D', 4, 1], [[3,1]]) @@ -1227,6 +1229,8 @@ class RCTypeA2Even(RCVirtual): sage: RC = RiggedConfigurations(['A',2,2], [[1,1]]) sage: RC.cardinality() 3 + sage: RC = RiggedConfigurations(['A',2,2], [[1,2],[1,1]]) + sage: TestSuite(RC).run() sage: RC = RiggedConfigurations(['A',4,2], [[2,1]]) sage: TestSuite(RC).run() # long time """ @@ -1404,6 +1408,8 @@ class RCTypeA2Dual(RCTypeA2Even): sage: RC = RiggedConfigurations(CartanType(['A',2,2]).dual(), [[1,1]]) sage: RC.cardinality() 3 + sage: RC = RiggedConfigurations(CartanType(['A',2,2]).dual(), [[1,2],[1,1]]) + sage: TestSuite(RC).run() sage: RC = RiggedConfigurations(CartanType(['A',4,2]).dual(), [[2,1]]) sage: TestSuite(RC).run() # long time """ From 6eda481dfe37959a7897b9e1b97af1629d00d8ea Mon Sep 17 00:00:00 2001 From: Anne Schilling Date: Wed, 20 Nov 2013 14:59:32 -0800 Subject: [PATCH 149/206] went through RC code together with Travis --- .../rigged_configurations/kr_tableaux.py | 49 +++++++++++-------- .../rigged_configurations.py | 10 ++-- .../tensor_product_kr_tableaux.py | 22 ++++----- 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index 05ad9ae6757..fa02fbe9cc5 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -5,7 +5,11 @@ `s` columns that naturally arise under the bijection between rigged configurations and tableaux [RigConBijection]_. They are in bijection with the elements of the Kirillov-Reshetikhin crystal `B^{r,s}` under the (inverse) -filling map. For more information, see :class:`KirillovReshetikhinTableaux` +filling map. They do not have to satisfy the semistandard row or column +restrictions. These tensor products are the result from the bijection from +rigged configurations [RigConBijection]_. + +For more information, see :class:`KirillovReshetikhinTableaux` and :class:`TensorProductOfKirillovReshetikhinTableaux`. AUTHORS: @@ -63,8 +67,8 @@ class KirillovReshetikhinTableaux(CrystalOfWords): (inverse) filling map. Whenever `B^{r,s} \cong B(s\Lambda_r)` as a classical crystal (which is - the case for `B^{r,s}` in type `A_n^{(1)}` and `B^{n,s}` in type - `C_n^{(1)}`), then the filling map is trivial. + the case for `B^{r,s}` in type `A_n^{(1)}`, `B^{n,s}` in type `C_n^{(1)}` and `D_{n+1}^{(2)}`, + `B^{n,s}` and `B^{n-1,s}` in type `D_n^{(1)}`) then the filling map is trivial. For `B^{r,s}` in: @@ -82,20 +86,21 @@ class KirillovReshetikhinTableaux(CrystalOfWords): See [BijectionDn]_. For the spinor case in type `B_n^{(1)}`, the crystal `B^{n,s}`, we - consider the images under the natural doubling map which is given by - removing `2 \times 2` boxes and the filling map is the same as below - (ex. the non-spin type `C_n^{(1)}`). + consider the images under the natural doubling map into `B^{n,2s}`. + The classical components of this crystal are now given by + removing `2 \times 2` boxes. The filling map is the same as below + (see the non-spin type `C_n^{(1)}`). For `B^{r,s}` in: - type `C_n^{(1)}` when `r < n`, - type `A_{2n}^{(2)\dagger}` for all `r`, - the filling map given as follows. Suppose we are considering (classically) - highest weight element in the classical component `B(\lambda)`, we fill + the filling map is given as follows. Suppose we are considering the (classically) + highest weight element in the classical component `B(\lambda)`. Then we fill it in with the horizontal dominoes `[\bar{\imath}, i]` in the `i`-th row - from the top (in English notation) and reorganizing the columns as - necessary. Recall from above that `B^{n,s} \cong B(s\Lambda_n)` in + from the top (in English notation) and reordering the columns so that they are + increasing. Recall from above that `B^{n,s} \cong B(s\Lambda_n)` in type `C^{(1)}_n`. For `B^{r,s}` in: @@ -104,18 +109,17 @@ class KirillovReshetikhinTableaux(CrystalOfWords): - type `D_{n+1}^{(2)}` when `r < n`, the filling map is the same as given in [AffineRigConDn]_ except for - `B^{r,1}` which is given by the column `[1, 2, \ldots, k, \emptyset, - \ldots \emptyset]` where we consider the classical component - `B(\lambda_k)`. + the rightmost column which is given by the column `[1, 2, \ldots, k, \emptyset, + \ldots \emptyset]` where `k = (r+x-1)/2` in Step 3 of [AffineRigConDn]_. - For the spinor case in type `D_n^{(2)}`, the crystal `B^{n,s}`, the - crystal is the same as in type `D_n^{(1)}`. + For the spinor case in type `D_{n+1}^{(2)}`, the crystal `B^{n,s}`, we define the filling map + in the same way as in type `D_n^{(1)}`. .. NOTE:: The filling map and classical decompositions in non-spinor cases can be classified by how the special node `0` connects with the - corresponding classical diagram. + corresponding classical diagram. For more information about the bijection between rigged configurations and tensor products of Kirillov-Reshetikhin tableaux, see @@ -123,10 +127,13 @@ class KirillovReshetikhinTableaux(CrystalOfWords): .. NOTE:: - The tableaux for all non-simply-laced are provably correct if the + The tableaux for all non-simply-laced types are provably correct if the bijection with :class:`rigged configurations ` - holds. Therefore this is only proven for `B^{r,1}` or `B^{1,s}` and - in general for types `A_n^{(1)}` and `D_n^{(C1)}`. + holds. Therefore this is currently only proven for `B^{r,1}` or `B^{1,s}` and + in general for types `A_n^{(1)}` and `D_n^{(1)}`. + + Note for Travis: add a brief explanation of crystal structure!!! Check NonImplemented type F, E, .... + add doctests on weight for filling maps; fix references; fix docs in tensor_products INPUT: @@ -219,8 +226,8 @@ def __classcall_private__(cls, cartan_type, r, s): #if ct.dual().letter == 'G': # D_4^{(3)} #print "We are returning the KR crystal because the requested type is not yet implemented!" - return KRTableauxWrapper(ct, r, s) - #raise NotImplementedError + #return KRTableauxWrapper(ct, r, s) + raise NotImplementedError #return super(KirillovReshetikhinTableaux, cls).__classcall__(cls, ct, r, s) def __init__(self, cartan_type, r, s): diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 3e30707381d..285b8027583 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -5,7 +5,7 @@ - Travis Scrimshaw (2010-09-26): Initial version -Rigged configurations form combinatorial objects first introduced by Kirillov and Reshetikhin +Rigged configurations form combinatorial objects first introduced by Kerov, Kirillov and Reshetikhin that arose from studies of statistical mechanical models using the Bethe Ansatz. They are sequences of rigged partitions. A rigged partition is a partition together with a label associated to each part that satisfy certain constraints. The labels @@ -37,7 +37,7 @@ - ``cartan_type`` -- A Cartan type - ``B`` -- A list of positive integer pairs `(r,s)` specifying the width `s` - and height `r` of the sequence of rectangles + and height `r` of the sequence of rectangles EXAMPLES:: @@ -1427,7 +1427,7 @@ def _calc_vacancy_number(self, partitions, a, i, **options): - ``i`` -- The row index of the `a`-th rigged partition TESTS:: - + sage: RC = RiggedConfigurations(CartanType(['A', 6, 2]).dual(), [[2,1]]) sage: elt = RC(partition_list=[[1], [2], [2]]) sage: RC._calc_vacancy_number(elt.nu(), 0, 0) @@ -1511,7 +1511,7 @@ def module_generators(self): # Start with a base to calculate the vacancy numbers # Make a copy just to be safe base = self.element_class(self, partition_list=shapes[:]) - + # Check the special condition of odd rows in the n-th partition invalid_RC = False for i in range(len(base[-1]._list)): @@ -1741,7 +1741,7 @@ def R_matrix(ct, RS1, RS2, only_highest_weight=False): Check that the `R`-matrix is the identity on `B \otimes B`:: - sage: L = R_matrix(['A',2,1], [2,3], [2,3]) + sage: L = R_matrix(['A',2,1], [2,3], [2,3]) sage: len(L) 100 sage: all(x == y for x,y in L) diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index e68fe059f13..31cdd4418a7 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -5,9 +5,6 @@ `r` rows and `s` columns which naturally arise in the bijection between rigged configurations and tableaux and which are in bijection with the elements of the Kirillov-Reshetikhin crystal `B^{r,s}`, see :class:`KirillovReshetikhinCrystal`. -They do not have to satisfy the semistandard row or column -restrictions. These tensor products are the result from the bijection from -rigged configurations [RigConBijection]_. AUTHORS: @@ -177,17 +174,17 @@ class TensorProductOfKirillovReshetikhinTableaux(FullTensorProductOfRegularCryst A tensor product of :class:`KirillovReshetikhinTableaux`. Through the bijection with rigged configurations, the tableaux that are - produced in the Kirillov-Reshetikhin model for type `D_n^{(1)}` are all of - rectangular shapes and do not necessarily obey the usual strict increase in - columns and weak increase in rows. The relation between the two tableaux - models is given by a filling map. + produced in all nonexceptional types are all of rectangular shapes and do not necessarily + obey the usual strict increase in columns and weak increase in rows. The relation between the + elements of the Kirillov-Reshetikhin crystal, given by the Kashiwara-Nakashima tableaux, and the + Kirillov-Reshetikhin tableaux is given by a filling map. .. NOTE:: - The tableaux for all non-simply-laced provably holds if the bijection - with :class:`rigged configurations ` holds. - Therefore this is only proven for all factors`B^{r,1}` or all factors - `B^{1,s}`, and in general for types `A_n^{(1)}` and `D_n^{(1)}`. + The tableaux for all non-simply-laced types are provably correct if the + bijection with :class:`rigged configurations ` + holds. Therefore this is currently only proven for `B^{r,1}` or `B^{1,s}` and + in general for types `A_n^{(1)}` and `D_n^{(1)}`. For more information see [OSS2011]_ and :class:`KirillovReshetikhinTableaux`. @@ -205,7 +202,8 @@ class TensorProductOfKirillovReshetikhinTableaux(FullTensorProductOfRegularCryst - ``cartan_type`` -- The Cartan type - - ``B`` -- An (ordered) list of dimensions + - ``B`` -- An (ordered) list of pairs `(r,s)` which give the dimension of a rectangle + with `r` rows and `s` columns The dimensions (i.e. `B`) is a list whose entries are lists of the form `[r, s]` which correspond to a tableau with `r` rows and `s` From 3756822636c3ca4a8ac721bb3d076af8ead7c159 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 21 Nov 2013 21:58:48 -0800 Subject: [PATCH 150/206] Fixing docstrings and other misc things. --- .../rigged_configurations/kr_tableaux.py | 43 +- .../rigged_configuration_element.py | 31 +- .../rigged_configurations.py | 436 ++++++++---------- .../tensor_product_kr_tableaux.py | 45 +- 4 files changed, 261 insertions(+), 294 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index fa02fbe9cc5..ce5f7101bad 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -76,11 +76,11 @@ class KirillovReshetikhinTableaux(CrystalOfWords): - type `B_n^{(1)}` when `r < n`, - type `A_{2n-1}^{(2)}` for all `r`, - the filling map is defined in [AffineRigConDn]_. + the filling map is defined in [OSS2011]_. For the spinor cases in type `D_n^{(1)}`, the crystal `B^{k,s}` where - `k=n-1,n`, is isomorphic as a classical crystal to `B(s\Lambda_k)`, and - here we consider the Kirillov-Reshetikhin tableaux as living in + `k = n-1, n`, is isomorphic as a classical crystal to `B(s\Lambda_k)`, + and here we consider the Kirillov-Reshetikhin tableaux as living in `B(2s \Lambda_k)` under the natural doubling map. In this case, the crystal operators `e_i` and `f_i` act as `e_i^2` and `f_i^2` respectively. See [BijectionDn]_. @@ -96,24 +96,25 @@ class KirillovReshetikhinTableaux(CrystalOfWords): - type `C_n^{(1)}` when `r < n`, - type `A_{2n}^{(2)\dagger}` for all `r`, - the filling map is given as follows. Suppose we are considering the (classically) - highest weight element in the classical component `B(\lambda)`. Then we fill - it in with the horizontal dominoes `[\bar{\imath}, i]` in the `i`-th row - from the top (in English notation) and reordering the columns so that they are - increasing. Recall from above that `B^{n,s} \cong B(s\Lambda_n)` in - type `C^{(1)}_n`. + the filling map is given as follows. Suppose we are considering the + (classically) highest weight element in the classical component + `B(\lambda)`. Then we fill it in with the horizontal dominoes + `[\bar{\imath}, i]` in the `i`-th row from the top (in English notation) + and reordering the columns so that they are increasing. Recall from above + that `B^{n,s} \cong B(s\Lambda_n)` in type `C^{(1)}_n`. For `B^{r,s}` in: - type `A_{2n}^{(2)}` for all `r`, - type `D_{n+1}^{(2)}` when `r < n`, - the filling map is the same as given in [AffineRigConDn]_ except for - the rightmost column which is given by the column `[1, 2, \ldots, k, \emptyset, - \ldots \emptyset]` where `k = (r+x-1)/2` in Step 3 of [AffineRigConDn]_. + the filling map is the same as given in [OSS2011]_ except for + the rightmost column which is given by the column `[1, 2, \ldots, k, + \emptyset, \ldots \emptyset]` where `k = (r+x-1)/2` in Step 3 of + [OSS2011]_. - For the spinor case in type `D_{n+1}^{(2)}`, the crystal `B^{n,s}`, we define the filling map - in the same way as in type `D_n^{(1)}`. + For the spinor case in type `D_{n+1}^{(2)}`, the crystal `B^{n,s}`, we + define the filling map in the same way as in type `D_n^{(1)}`. .. NOTE:: @@ -121,6 +122,13 @@ class KirillovReshetikhinTableaux(CrystalOfWords): be classified by how the special node `0` connects with the corresponding classical diagram. + The classical crystal stucture is given by the usual Kashiwara-Nakashima + tableaux rules. That is to embed this into `B(\Lambda_1)^{\otimes n s}` + by using the reading word and then applying the classical crystal + operator. The affine crystal stucture is given by converting to + the corresponding KR crystal element, performing the affine crystal + operator, and pulling back to a KR tableau. + For more information about the bijection between rigged configurations and tensor products of Kirillov-Reshetikhin tableaux, see :class:`TensorProductOfKirillovReshetikhinTableaux`. @@ -132,8 +140,8 @@ class KirillovReshetikhinTableaux(CrystalOfWords): holds. Therefore this is currently only proven for `B^{r,1}` or `B^{1,s}` and in general for types `A_n^{(1)}` and `D_n^{(1)}`. - Note for Travis: add a brief explanation of crystal structure!!! Check NonImplemented type F, E, .... - add doctests on weight for filling maps; fix references; fix docs in tensor_products + Note for Travis: Check NonImplemented type F, E, .... + add doctests on weight for filling maps INPUT: @@ -225,9 +233,8 @@ def __classcall_private__(cls, cartan_type, r, s): #if ct.dual().letter == 'F': # E_6^{(2)} #if ct.dual().letter == 'G': # D_4^{(3)} - #print "We are returning the KR crystal because the requested type is not yet implemented!" - #return KRTableauxWrapper(ct, r, s) raise NotImplementedError + #return KRTableauxWrapper(ct, r, s) #return super(KirillovReshetikhinTableaux, cls).__classcall__(cls, ct, r, s) def __init__(self, cartan_type, r, s): diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index ab8c52b7c50..1c82399a496 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -36,7 +36,7 @@ class RiggedConfigurationElement(ClonableArray): For more information on rigged configurations, see :class:`RiggedConfigurations`. For rigged configurations for - non-simply-laced types, use :class:`RCVirtualElement`. + non-simply-laced types, use :class:`RCNonSimplyLacedElement`. Typically to create a specific rigged configuration, the user will pass in the optional argument **partition_list** and if the user wants to specify @@ -983,9 +983,34 @@ def get_vacancy_number(self, a, i): return None -class RCVirtualElement(RiggedConfigurationElement): + def partition_rigging_lists(self): + """ + Return the list of partitions and the associated list of riggings + of ``self``. + + EXAMPLES:: + + sage: rc = RC(partition_list=[[2],[1],[1]], rigging_list=[[-1],[0],[-1]]); rc + + -1[ ][ ]-1 + + 1[ ]0 + + -1[ ]-1 + + sage: rc.partition_rigging_lists() + [[[2],[1],[1]], [[-1],[0],[-1]]] + """ + partitions = [] + riggings = [] + for p in self: + partitions.append(list(p)) + riggings.append(list(p.rigging)) + return [partitions, riggings] + +class RCNonSimplyLacedElement(RiggedConfigurationElement): """ - Virtual rigged configuration elements. + Rigged configuration elements for non-simply-laced types. TESTS:: diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 285b8027583..d96404d4107 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -4,224 +4,6 @@ AUTHORS: - Travis Scrimshaw (2010-09-26): Initial version - -Rigged configurations form combinatorial objects first introduced by Kerov, Kirillov and Reshetikhin -that arose from studies of statistical mechanical models using the Bethe Ansatz. -They are sequences of rigged partitions. A rigged partition is a partition together -with a label associated to each part that satisfy certain constraints. The labels -are also called riggings. - -Rigged configurations exist for all affine Kac-Moody Lie algebras. See for example -[HKOTT2002]_. In Sage they are specified by providing a Cartan type and a list of -rectangular shapes `R`. The list of all (highest weight) rigged configurations -for given `R` is computed via the (virtual) Kleber algorithm (see also -:class:`~sage.combinat.rigged_configurations.kleber_tree.KleberTree` and -:class:`~sage.combinat.rigged_configurations.kleber_tree.VirtualKleberTree`). - -There exists a crystal structure on the set of rigged configurations, see -[CrysStructSchilling06]_ and [OSS03]_. The highest weight rigged -configurations are those where all riggings are nonnegative. The list of -all rigged configurations is computed from the highest weight ones using the -crystal operators. - -Rigged configurations are in bijection with tensor products of Kirillov-Reshetikhin tableaux, -see [RigConBijection]_, [BijectionDn]_, and [BijectionLRT]_. The list of rectangles `R` corresponds -to the shape of the Kirillov-Reshetikhin crystals appearing in the tensor product. -Kirillov-Reshetikhin crystals are implemented in Sage, see :class:`KirillovReshetikhinCrystal`, however, -in the bijection with rigged configurations a different realization of the elements in the -crystal are obtained, which are coined Kirillov-Reshetkihin tableaux, see :class:`KirillovReshetikhinTableaux`. -For more details see [AffineRigConDn]_. - -INPUT: - -- ``cartan_type`` -- A Cartan type - -- ``B`` -- A list of positive integer pairs `(r,s)` specifying the width `s` - and height `r` of the sequence of rectangles - -EXAMPLES:: - - sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2], [1, 1]]) - sage: RC - Rigged configurations of type ['A', 3, 1] and factor(s) ((3, 2), (1, 2), (1, 1)) - - sage: RC = RiggedConfigurations(['A', 3, 1], [[2,1]]); RC - Rigged configurations of type ['A', 3, 1] and factor(s) ((2, 1),) - sage: RC.cardinality() - 6 - sage: len(RC.list()) == RC.cardinality() - True - sage: RC.list() # random - [ - (/) - - (/) - - (/) - , - (/) - - -1[ ]-1 - - (/) - , - (/) - - 0[ ]0 - - -1[ ]-1 - , - -1[ ]-1 - - 0[ ]0 - - (/) - , - -1[ ]-1 - - 1[ ]1 - - -1[ ]-1 - , - 0[ ]0 - - -1[ ]-1 - -1[ ]-1 - - 0[ ]0 - ] - -A rigged configuration element with all riggings equal to the vacancy numbers can be created as follows:: - - sage: RC = RiggedConfigurations(['A', 3, 1], [[3,2], [2,1], [1,1], [1,1]]); RC - Rigged configurations of type ['A', 3, 1] and factor(s) ((3, 2), (2, 1), (1, 1), (1, 1)) - sage: elt = RC(partition_list=[[1],[],[]]); elt - - 0[ ]0 - - (/) - - (/) - - -If on the other hand we also want to specify the riggings, this can be achieved as follows:: - - sage: RC = RiggedConfigurations(['D', 7, 1], [[3,3],[5,2],[4,3],[2,3],[4,4],[3,1],[1,4],[2,2]]) - sage: elt = RC(partition_list=[[2],[3,2,1],[2,2,1,1],[2,2,1,1,1,1],[3,2,1,1,1,1],[2,1,1],[2,2]], - ....: rigging_list=[[2],[1,0,0],[4,1,2,1],[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,0],[0,0]]) - sage: elt - - 3[ ][ ]2 - - 1[ ][ ][ ]1 - 2[ ][ ]0 - 1[ ]0 - - 4[ ][ ]4 - 4[ ][ ]1 - 3[ ]2 - 3[ ]1 - - 2[ ][ ]1 - 2[ ][ ]0 - 0[ ]0 - 0[ ]0 - 0[ ]0 - 0[ ]0 - - 0[ ][ ][ ]0 - 2[ ][ ]1 - 0[ ]0 - 0[ ]0 - 0[ ]0 - 0[ ]0 - - 0[ ][ ]0 - 0[ ]0 - 0[ ]0 - - 0[ ][ ]0 - 0[ ][ ]0 - - -To obtain the Kirillov-Reshetikhin (KR) tableaux under the bijection between rigged configurations and KR -tableaux, we can type the following. This example was checked against Reiho Sakamoto's Mathematica program -on rigged configurations:: - - sage: output = elt.to_tensor_product_of_kirillov_reshetikhin_tableaux(); output - [[1, 1, 1], [2, 3, 3], [3, 4, -5]] (X) [[1, 1], [2, 2], [3, 3], [5, -6], [6, -5]] (X) [[1, 1, 2], [2, 2, 3], [3, 3, 7], [4, 4, -7]] (X) [[1, 1, 1], [2, 2, 2]] (X) [[1, 1, 1, 3], [2, 2, 3, 4], [3, 3, 4, 5], [4, 4, 5, 6]] (X) [[1], [2], [3]] (X) [[1, 1, 1, 1]] (X) [[1, 1], [2, 2]] - sage: elt.to_tensor_product_of_kirillov_reshetikhin_tableaux().to_rigged_configuration() == elt - True - sage: output.to_rigged_configuration().to_tensor_product_of_kirillov_reshetikhin_tableaux() == output - True - -To get the highest weight rigged configurations, use the attribute -``module_generators``:: - - sage: RC = RiggedConfigurations(['D',4,1], [[2,1]]) - sage: for x in RC.module_generators: x - - (/) - - (/) - - (/) - - (/) - - 0[ ]0 - - 0[ ]0 - 0[ ]0 - - 0[ ]0 - - 0[ ]0 - -TESTS:: - - sage: RC = RiggedConfigurations(['A', 3, 1], [[3,2], [2,1], [1,1], [1,1]]) - sage: len(RC.module_generators) - 17 - sage: RC = RiggedConfigurations(['D', 4, 1], [[1, 1]]) - sage: RC.cardinality() - 8 - sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) - sage: c = RC.cardinality(); c - 29 - sage: K = KirillovReshetikhinCrystal(['D',4,1],2,1) - sage: K.cardinality() == c - True - -REFERENCES: - -.. [HKOTT2002] G. Hatayama, A. Kuniba, M. Okado, T. Takagi, Z. Tsuboi. - Paths, Crystals and Fermionic Formulae - Prog.Math.Phys. 23 (2002) 205-272 - -.. [CrysStructSchilling06] Anne Schilling. - Crystal structure on rigged configurations. - International Mathematics Research Notices. - Volume 2006. 2006. Article ID 97376. Pages 1-27. - -.. [RigConBijection] Masato Okado, Anne Schilling, Mark Shimozono. - A crystal to rigged configuration bijection for non-exceptional affine - algebras. - Algebraic Combinatorics and Quantum Groups. - Edited by N. Jing. World Scientific. 2003. Pages 85-124. - -.. [BijectionDn] Anne Schilling. - A bijection between type `D_n^{(1)}` crystals and rigged configurations. - J. Algebra. 285. 2005. 292-334 - -.. [BijectionLRT] Anatol N. Kirillov, Anne Schilling, Mark Shimozono. - A bijection between Littlewood-Richardson tableaux and rigged - configurations. - Selecta Mathematica (N.S.). 8. 2002. 67-135 (:mathscinet:`MR1890195`). - -.. [AffineRigConDn] Masato Okado, Reiho Sakamoto, Anne Schilling. - Affine crystal structure on rigged configurations of type `D_n^{(1)}`. - J. Algebraic Combinatorics, to appear, :doi:`10.1007/s10801-012-0383-z` (:arXiv:`1109.3523`) """ #***************************************************************************** @@ -251,15 +33,15 @@ from sage.combinat.cartesian_product import CartesianProduct from sage.combinat.rigged_configurations.kleber_tree import KleberTree, VirtualKleberTree from sage.combinat.rigged_configurations.rigged_configuration_element import RiggedConfigurationElement, \ - RCVirtualElement + RCNonSimplyLacedElement # Note on implementation, this class is used for simply-laced types only class RiggedConfigurations(Parent, UniqueRepresentation): r""" - Class of rigged configurations. + Rigged configurations as `U_q^{\prime}(\mathfrak{g})`-crystals. - Let `\overline{I}` denote the classical index set associated to the Cartan type - of the rigged configurations. A rigged configuration of multiplicity + Let `\overline{I}` denote the classical index set associated to the Cartan + type of the rigged configurations. A rigged configuration of multiplicity array `L_i^{(a)}` and dominant weight `\Lambda` is a sequence of partitions `\{ \nu^{(a)} \mid a \in \overline{I} \}` such that @@ -273,23 +55,56 @@ class RiggedConfigurations(Parent, UniqueRepresentation): and `m_i^{(a)}` is the number of rows of length `i` in the partition `\nu^{(a)}`. - Each sequence of partitions also comes with a sequence of values called - vacancy numbers and riggings. Vacancy numbers are computed based upon the - partitions and `L_i^{(a)}`, and the riggings must satisfy certain - conditions. For more, see [RigConBijection]_ [CrysStructSchilling06]_ - [BijectionLRT]_. - - They are in bijection with + Each partition `\nu^{(a)}`, in the sequence also comes with a sequence of + statistics `p_i^{(a)}` called *vacancy numbers* and a weakly decreasing + sequence `J_i^{(a)}` of length `m_i^{(a)}` called *riggings*. + Vacancy numbers are computed based upon the partitions and `L_i^{(a)}`, + and the riggings must satisfy `\max J_i^{(a)} \leq p_i^{(a)}`. We call + such a partition a *rigged partition*. For more, see + [RigConBijection]_ [CrysStructSchilling06]_ [BijectionLRT]_. + + Rigged configurations form combinatorial objects first introduced by + Kerov, Kirillov and Reshetikhin that arose from studies of statistical + mechanical models using the Bethe Ansatz. They are sequences of rigged + partitions. A rigged partition is a partition together with a label + associated to each part that satisfy certain constraints. The labels + are also called riggings. + + Rigged configurations exist for all affine Kac-Moody Lie algebras. See + for example [HKOTT2002]_. In Sage they are specified by providing a Cartan + type and a list of rectangular shapes `B`. The list of all (highest + weight) rigged configurations for given `B` is computed via the (virtual) + Kleber algorithm (see also + :class:`~sage.combinat.rigged_configurations.kleber_tree.KleberTree` and + :class:`~sage.combinat.rigged_configurations.kleber_tree.VirtualKleberTree`). + + Rigged configurations in simply-laced types all admit a classical crystal + structure [CrysStructSchilling06]_. For non-simply-laced types, the + crystal is given by using virtual rigged configurations [OSS03]_. The + highest weight rigged configurations are those where all riggings are + nonnegative. The list of all rigged configurations is computed from the + highest weight ones using the crystal operators. + + Rigged configurations are conjecturally in bijection with :class:`TensorProductOfKirillovReshetikhinTableaux` of non-exceptional - affine types (see [RigConBijection]_, [BijectionLRT]_, [BijectionDn]_) and all - admit a classical crystal structure [CrysStructSchilling06]_. + affine types where the list `B` corresponds to the tensor factors + `B^{r,s}`. The bijection has been proven in types `A_n^{(1)}` and + `D_n^{(1)}` and when the only non-zero entries of `L_i^{(a)}` are either + only `L_1^{(a)}` or only `L_i^{(1)}` (corresponding to single columns or + rows respectively) [RigConBijection]_, [BijectionLRT]_, [BijectionDn]_. + + KR crystals are implemented in Sage, see + :class:`KirillovReshetikhinCrystal`, however, in the bijection with + rigged configurations a different realization of the elements in the + crystal are obtained, which are coined KR tableaux, see + :class:`KirillovReshetikhinTableaux`. For more details see [OSS2011]_. .. NOTE:: All non-simply-laced rigged configurations have not been proven to give rise to aligned virtual crystals (i.e. have the correct crystal structure or ismorphic as affine crystals to the tensor product of - Kirillov-Reshetikhin tableaux). + KR tableaux). INPUT: @@ -297,9 +112,89 @@ class RiggedConfigurations(Parent, UniqueRepresentation): - ``B`` -- a list of positive integer tuples `(r,s)` corresponding to the tensor factors in the bijection with tensor product of - Kirillov-Reshetikhin tableaux + Kirillov-Reshetikhin tableaux or equivalently the sequence of width `s` + and height `r` rectangles + + REFERENCES: + + .. [HKOTT2002] G. Hatayama, A. Kuniba, M. Okado, T. Takagi, Z. Tsuboi. + Paths, Crystals and Fermionic Formulae + Prog.Math.Phys. 23 (2002) 205-272 + + .. [CrysStructSchilling06] Anne Schilling. + Crystal structure on rigged configurations. + International Mathematics Research Notices. + Volume 2006. 2006. Article ID 97376. Pages 1-27. + + .. [RigConBijection] Masato Okado, Anne Schilling, Mark Shimozono. + A crystal to rigged configuration bijection for non-exceptional affine + algebras. + Algebraic Combinatorics and Quantum Groups. + Edited by N. Jing. World Scientific. 2003. Pages 85-124. + + .. [BijectionDn] Anne Schilling. + A bijection between type `D_n^{(1)}` crystals and rigged configurations. + J. Algebra. 285. 2005. 292-334 + + .. [BijectionLRT] Anatol N. Kirillov, Anne Schilling, Mark Shimozono. + A bijection between Littlewood-Richardson tableaux and rigged + configurations. + Selecta Mathematica (N.S.). 8. 2002. 67-135 (:mathscinet:`MR1890195`). + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2], [1, 1]]) + sage: RC + Rigged configurations of type ['A', 3, 1] and factor(s) ((3, 2), (1, 2), (1, 1)) + + sage: RC = RiggedConfigurations(['A', 3, 1], [[2,1]]); RC + Rigged configurations of type ['A', 3, 1] and factor(s) ((2, 1),) + sage: RC.cardinality() + 6 + sage: len(RC.list()) == RC.cardinality() + True + sage: RC.list() # random + [ + (/) + + (/) + + (/) + , + (/) + + -1[ ]-1 + + (/) + , + (/) + + 0[ ]0 + + -1[ ]-1 + , + -1[ ]-1 + + 0[ ]0 + + (/) + , + -1[ ]-1 + + 1[ ]1 + + -1[ ]-1 + , + 0[ ]0 + + -1[ ]-1 + -1[ ]-1 + + 0[ ]0 + ] - A rigged configuration element with all riggings equal to the vacancy numbers can be created as follows:: + A rigged configuration element with all riggings equal to the vacancy + numbers can be created as follows:: sage: RC = RiggedConfigurations(['A', 3, 1], [[3,2], [2,1], [1,1], [1,1]]); RC Rigged configurations of type ['A', 3, 1] and factor(s) ((3, 2), (2, 1), (1, 1), (1, 1)) @@ -312,7 +207,8 @@ class RiggedConfigurations(Parent, UniqueRepresentation): (/) - If on the other hand we also want to specify the riggings, this can be achieved as follows:: + If on the other hand we also want to specify the riggings, this can be + achieved as follows:: sage: RC = RiggedConfigurations(['D', 7, 1], [[3,3],[5,2],[4,3],[2,3],[4,4],[3,1],[1,4],[2,2]]) sage: elt = RC(partition_list=[[2],[3,2,1],[2,2,1,1],[2,2,1,1,1,1],[3,2,1,1,1,1],[2,1,1],[2,2]], @@ -352,9 +248,9 @@ class RiggedConfigurations(Parent, UniqueRepresentation): 0[ ][ ]0 - To obtain the Kirillov-Reshetikhin (KR) tableau under the bijection between rigged configurations and KR - tableaux, we can type the following. This example was checked against Reiho Sakamoto's Mathematica program - on rigged configurations:: + To obtain the KR tableau under the bijection between rigged configurations + and KR tableaux, we can type the following. This example was checked + against Reiho Sakamoto's Mathematica program on rigged configurations:: sage: output = elt.to_tensor_product_of_kirillov_reshetikhin_tableaux(); output [[1, 1, 1], [2, 3, 3], [3, 4, -5]] (X) [[1, 1], [2, 2], [3, 3], [5, -6], [6, -5]] (X) @@ -366,7 +262,7 @@ class RiggedConfigurations(Parent, UniqueRepresentation): True We can also convert between rigged configurations and tensor products of - Kirillov-Reshetikhin crystals:: + KR crystals:: sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) sage: elt = RC(partition_list=[[1],[1,1],[1],[1]]) @@ -387,6 +283,21 @@ class RiggedConfigurations(Parent, UniqueRepresentation): sage: ret = RC(t) sage: ret.to_tensor_product_of_kirillov_reshetikhin_crystals() [[++++, []], [+++-, [[1], [2], [4], [-4]]]] + + TESTS:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[3,2], [2,1], [1,1], [1,1]]) + sage: len(RC.module_generators) + 17 + sage: RC = RiggedConfigurations(['D', 4, 1], [[1, 1]]) + sage: RC.cardinality() + 8 + sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) + sage: c = RC.cardinality(); c + 29 + sage: K = KirillovReshetikhinCrystal(['D',4,1],2,1) + sage: K.cardinality() == c + True """ @staticmethod def __classcall_private__(cls, cartan_type, B): @@ -415,7 +326,7 @@ def __classcall_private__(cls, cartan_type, B): # We check the classical type to account for A^{(1)}_1 which is not # a virtual rigged configuration. if not cartan_type.classical().is_simply_laced(): - return RCVirtual(cartan_type, B) + return RCNonSimplyLaced(cartan_type, B) return super(RiggedConfigurations, cls).__classcall__(cls, cartan_type, B) @@ -668,7 +579,7 @@ def _element_constructor_(self, *lst, **options): EXAMPLES:: sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 1]]) - sage: RC(partition_list=[[1], [1], [], []], rigging_list=[[-1], [0], [], []]) # indirect doctest + sage: RC(partition_list=[[1], [1], [], []], rigging_list=[[-1], [0], [], []]) -1[ ]-1 @@ -678,6 +589,30 @@ def _element_constructor_(self, *lst, **options): (/) + + TESTS:: + + sage: KT = TensorProductOfKirillovReshetikhinTableaux(['C',2,1], [[2,4],[1,2]]) + sage: t = KT(pathlist=[[2,1,2,1,-2,2,-1,-2],[2,-2]]) + sage: rc = t.to_rigged_configuration(); rc + + -1[ ][ ][ ]-1 + 0[ ][ ]0 + + -1[ ][ ]-1 + -1[ ]-1 + -1[ ]-1 + + sage: RC = RiggedConfigurations(['C',2,1], [[1,2],[2,4]]) + sage: RC(rc) + + -1[ ][ ][ ]-1 + 0[ ][ ]0 + + -1[ ][ ]-1 + -1[ ]-1 + -1[ ]-1 + """ from sage.combinat.rigged_configurations.tensor_product_kr_tableaux_element import TensorProductOfKirillovReshetikhinTableauxElement if isinstance(lst[0], TensorProductOfKirillovReshetikhinTableauxElement): @@ -694,6 +629,9 @@ def _element_constructor_(self, *lst, **options): krt_elt = KRT(*[x.to_kirillov_reshetikhin_tableau() for x in lst]) return krt_elt.to_rigged_configuration() + if isinstance(lst[0], RiggedConfigurationElement): + lst = lst[0] + return self.element_class(self, list(lst), **options) def _calc_vacancy_number(self, partitions, a, i, **options): @@ -790,10 +728,8 @@ def cardinality(self): sage: RC.cardinality() 134 """ - try: - return self.tensor_product_of_kirillov_reshetikhin_tableaux().cardinality() - except NotImplementedError: # Backup by direct counting - return ZZ(sum(1 for x in self)) + CWLR = self.cartan_type().classical().root_system().ambient_space() + return sum(CWLR.weyl_dimension(mg.classical_weight()) for mg in self.module_generators) @cached_method def tensor_product_of_kirillov_reshetikhin_crystals(self): @@ -883,9 +819,9 @@ def _test_bijection(self, **options): RiggedConfigurations.Element = RiggedConfigurationElement -class RCVirtual(RiggedConfigurations): +class RCNonSimplyLaced(RiggedConfigurations): r""" - Class of virtual rigged configurations. + Rigged configurations in non-simply-laced types. These are rigged configurations which lift to a virtual configuration. @@ -908,7 +844,7 @@ def __classcall_private__(cls, cartan_type, B): # Standardize B input into a tuple of tuples B = tuple(map(tuple, B)) - return super(RCVirtual, cls).__classcall__(cls, cartan_type, B) + return super(RCNonSimplyLaced, cls).__classcall__(cls, cartan_type, B) def __init__(self, cartan_type, dims): """ @@ -1213,9 +1149,9 @@ def _test_virtual_vacancy_numbers(self, **options): "Incorrect vacancy number: %s\nComputed: %s\nFor: %s"\ %(x[i].vacancy_numbers[j],vac_num, x)) -RCVirtual.Element = RCVirtualElement +RCNonSimplyLaced.Element = RCNonSimplyLacedElement -class RCTypeA2Even(RCVirtual): +class RCTypeA2Even(RCNonSimplyLaced): """ Rigged configurations for type `A_{2n}^{(2)}`. diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index 31cdd4418a7..8fa6f70cd78 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -3,8 +3,9 @@ A tensor product of :class:`KirillovReshetikhinTableaux` which are tableaux of `r` rows and `s` columns which naturally arise in the bijection between rigged -configurations and tableaux and which are in bijection with the elements of the -Kirillov-Reshetikhin crystal `B^{r,s}`, see :class:`KirillovReshetikhinCrystal`. +configurations and tableaux and which are in bijection with the elements of +the Kirillov-Reshetikhin crystal `B^{r,s}`, see +:class:`KirillovReshetikhinCrystal`. AUTHORS: @@ -140,7 +141,7 @@ def __repr__(self): sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1], [[2,1]]) sage: from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import HighestWeightTensorKRT - sage: HighestWeightTensorKRT(KRT) # indirect doctest + sage: HighestWeightTensorKRT(KRT) Highest weight elements of Tensor product of Kirillov-Reshetikhin tableaux of type ['D', 4, 1] and factor(s) ((2, 1),) """ return "Highest weight elements of %s"%self.tp_krt @@ -174,41 +175,39 @@ class TensorProductOfKirillovReshetikhinTableaux(FullTensorProductOfRegularCryst A tensor product of :class:`KirillovReshetikhinTableaux`. Through the bijection with rigged configurations, the tableaux that are - produced in all nonexceptional types are all of rectangular shapes and do not necessarily - obey the usual strict increase in columns and weak increase in rows. The relation between the - elements of the Kirillov-Reshetikhin crystal, given by the Kashiwara-Nakashima tableaux, and the - Kirillov-Reshetikhin tableaux is given by a filling map. + produced in all nonexceptional types are all of rectangular shapes and do + not necessarily obey the usual strict increase in columns and weak + increase in rows. The relation between the elements of the + Kirillov-Reshetikhin crystal, given by the Kashiwara-Nakashima tableaux, + and the Kirillov-Reshetikhin tableaux is given by a filling map. .. NOTE:: The tableaux for all non-simply-laced types are provably correct if the bijection with :class:`rigged configurations ` - holds. Therefore this is currently only proven for `B^{r,1}` or `B^{1,s}` and - in general for types `A_n^{(1)}` and `D_n^{(1)}`. + holds. Therefore this is currently only proven for `B^{r,1}` or + `B^{1,s}` and in general for types `A_n^{(1)}` and `D_n^{(1)}`. For more information see [OSS2011]_ and :class:`KirillovReshetikhinTableaux`. - REFERENCES: - - .. [OSS2011] Masato Okado, Reiho Sakamoto, Anne Schilling - Affine crystal structure on rigged configurations of type `D_n^{(1)}` - J. Algebraic Combinatorics, to appear, doi:10.1007/s10801-012-0383-z (arXiv:1109.3523 [math.QA]) - For more information on KR crystals, see :mod:`sage.combinat.crystals.kirillov_reshetikhin`. INPUT: - - ``cartan_type`` -- The Cartan type + - ``cartan_type`` -- a Cartan type - - ``B`` -- An (ordered) list of pairs `(r,s)` which give the dimension of a rectangle - with `r` rows and `s` columns + - ``B`` -- an (ordered) list of pairs `(r,s)` which give the dimension + of a rectangle with `r` rows and `s` columns and corresponds to a + Kirillov-Reshetikhin tableaux factor of `B^{r,s}`. - The dimensions (i.e. `B`) is a list whose entries are lists of the - form `[r, s]` which correspond to a tableau with `r` rows and `s` - columns (or in type `A_n^{(1)}` of shape ``[r]*s``) and corresponds - to a Kirillov-Reshetikhin crystal `B^{r,s}`. + REFERENCES: + + .. [OSS2011] Masato Okado, Reiho Sakamoto, Anne Schilling + Affine crystal structure on rigged configurations of type `D_n^{(1)}` + J. Algebraic Combinatorics, to appear, :doi:`10.1007/s10801-012-0383-z`, + `arxiv:`1109.3523` [math.QA] EXAMPLES: @@ -339,7 +338,7 @@ def _test_bijection(self, **options): def _element_constructor_(self, *path, **options): r""" - Construct a TensorProductOfKRTableauxElement. + Construct an element of ``self``. Typically the user will call this with the option **pathlist** which will receive a list and coerce it into a path. From 7a39d0f2358b53ffcf89bced3305e6a058ba254f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 22 Nov 2013 11:04:33 -0800 Subject: [PATCH 151/206] Changes that Anne and I discussed plus some printing improvements. Print improvements include the following: - ASCII art for RC's and KR tableaux. - Tensor products of KR tableaux can print in French notation. - Added ASCII art for tensor product of crystals. --- src/sage/combinat/crystals/tensor_product.py | 23 ++ .../rigged_configurations/kleber_tree.py | 2 +- .../rigged_configurations/kr_tableaux.py | 295 +++++------------- .../rigged_configuration_element.py | 105 +++++-- .../rigged_configurations.py | 67 ++-- .../tensor_product_kr_tableaux.py | 27 +- .../tensor_product_kr_tableaux_element.py | 49 ++- 7 files changed, 241 insertions(+), 327 deletions(-) diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index 776b0f3774b..f2a5a99e67e 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -777,6 +777,29 @@ def _latex_(self): """ return ' \otimes '.join(latex(c) for c in self) + def _ascii_art_(self): + """ + Return an ASCII art representation of ``self``. + + EXAMPLES:: + + sage: KT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1],[[3,3],[2,1],[1,2]]) + sage: ascii_art(KT.module_generators[0]) + 1 1 1 + 2 2 2 # 1 # 1 1 + 3 3 3 2 + -4 -4 -4 + """ + from sage.misc.ascii_art import ascii_art, AsciiArt + s = ascii_art(self[0]) + s._baseline = s._h // 2 + ret = s + for tableau in self[1:]: + s = ascii_art(tableau) + s._baseline = s._h // 2 + ret += AsciiArt([" # "]) + s + return ret + def __lt__(self, other): """ Non elements of the crystal are incomparable with elements of the crystal diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index 53ea894fd97..e892c730688 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -105,7 +105,7 @@ def _draw_tree(tree_node, node_label=True, style_point=None, style_node='fill=wh sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: KT = KleberTree(['A',3,1], [[3,2],[1,1]]) - sage: latex(KT) + sage: latex(KT) # indirect doctest \begin{tikzpicture} \node[fill=white] (T0) at (0.000, 0.000){$V_{\omega_{1}+2\omega_{3}}$}; \node (T00) at (0.000, -2.500){$V_{\omega_{3}}$}; diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index ce5f7101bad..48ae733f5d7 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -137,19 +137,16 @@ class KirillovReshetikhinTableaux(CrystalOfWords): The tableaux for all non-simply-laced types are provably correct if the bijection with :class:`rigged configurations ` - holds. Therefore this is currently only proven for `B^{r,1}` or `B^{1,s}` and - in general for types `A_n^{(1)}` and `D_n^{(1)}`. - - Note for Travis: Check NonImplemented type F, E, .... - add doctests on weight for filling maps + holds. Therefore this is currently only proven for `B^{r,1}` or + `B^{1,s}` and in general for types `A_n^{(1)}` and `D_n^{(1)}`. INPUT: - - ``cartan_type`` -- The Cartan type + - ``cartan_type`` -- the Cartan type - - ``r`` -- The number of rows + - ``r`` -- the Dynkin diagram index (typically the number of rows) - - ``s`` -- The number of columns + - ``s`` -- the number of columns EXAMPLES:: @@ -186,6 +183,30 @@ class KirillovReshetikhinTableaux(CrystalOfWords): [[2, 1], [3, -1]] sage: KRCrys(k) [[2], [3]] + + We check that the classical weights in the classical decompositions + agree in a few different type:: + + sage: KRCrys = KirillovReshetikhinCrystal(['D', 4, 1], 2, 2) + sage: KRTab = KirillovReshetikhinTableaux(['D', 4, 1], 2, 2) + sage: all(t.classical_weight() == KRCrys(t).classical_weight() for t in KRTab) + True + sage: KRCrys = KirillovReshetikhinCrystal(['B', 3, 1], 2, 2) + sage: KRTab = KirillovReshetikhinTableaux(['B', 3, 1], 2, 2) + sage: all(t.classical_weight() == KRCrys(t).classical_weight() for t in KRTab) + True + sage: KRCrys = KirillovReshetikhinCrystal(['C', 3, 1], 2, 2) + sage: KRTab = KirillovReshetikhinTableaux(['C', 3, 1], 2, 2) + sage: all(t.classical_weight() == KRCrys(t).classical_weight() for t in KRTab) + True + sage: KRCrys = KirillovReshetikhinCrystal(['D', 4, 2], 2, 2) + sage: KRTab = KirillovReshetikhinTableaux(['D', 4, 2], 2, 2) + sage: all(t.classical_weight() == KRCrys(t).classical_weight() for t in KRTab) + True + sage: KRCrys = KirillovReshetikhinCrystal(['A', 4, 2], 2, 2) + sage: KRTab = KirillovReshetikhinTableaux(['A', 4, 2], 2, 2) + sage: all(t.classical_weight() == KRCrys(t).classical_weight() for t in KRTab) + True """ @staticmethod def __classcall_private__(cls, cartan_type, r, s): @@ -234,7 +255,6 @@ def __classcall_private__(cls, cartan_type, r, s): #if ct.dual().letter == 'G': # D_4^{(3)} raise NotImplementedError - #return KRTableauxWrapper(ct, r, s) #return super(KirillovReshetikhinTableaux, cls).__classcall__(cls, ct, r, s) def __init__(self, cartan_type, r, s): @@ -326,6 +346,10 @@ 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 = KirillovReshetikhinTableaux(['A', 3, 1], 2, 2) + sage: KRT.module_generator() + [[1, 1], [2, 2]] """ if i is not None: return self.module_generators[i] @@ -339,7 +363,7 @@ def module_generator(self, i=None, **options): if list(mg.classical_weight().to_vector()) == shape: return mg return None - elif "column_shape" in options: + if "column_shape" in options: shape = list(Partition(options["column_shape"]).conjugate()) if len(shape) < self._cartan_type.classical().rank(): shape.extend( [0]*(n - len(shape)) ) @@ -347,18 +371,20 @@ def module_generator(self, i=None, **options): if list(mg.classical_weight().to_vector()) == shape: return mg return None - elif "weight" in options: + if "weight" in options: wt = options["weight"] for mg in self.module_generators: if mg.weight() == wt: return mg return None - elif "classical_weight" in options: + 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") @abstract_method @@ -947,7 +973,7 @@ def _repr_(self): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 1) - sage: KRT(3,2) # indirect doctest + sage: KRT(3,2) [[2], [3]] """ return repr(self.to_array()) @@ -959,10 +985,10 @@ def _repr_diagram(self): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A',4,1], 2,2) - sage: elt = KRT(4,3,2,1) + sage: elt = KRT(2,1,4,3) sage: print elt._repr_diagram() - 3 1 - 4 2 + 1 3 + 2 4 """ return self.to_tableau()._repr_diagram() @@ -973,7 +999,7 @@ def _latex_(self): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 3) - sage: latex(KRT(3,2,4,2,4,3)) # indirect doctest + sage: latex(KRT(3,2,4,2,4,3)) {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} \raisebox{-.6ex}{$\begin{array}[b]{*{3}c}\cline{1-3} \lr{2}&\lr{2}&\lr{3}\\\cline{1-3} @@ -984,6 +1010,20 @@ def _latex_(self): from sage.combinat.output import tex_from_array return tex_from_array([[val._latex_() for val in row] for row in self.to_array()]) + def _ascii_art_(self): + r""" + Return an ASCII art representation of ``self``. + + EXAMPLES:: + + sage: KRT = KirillovReshetikhinTableaux(['A',4,1], 2,2) + sage: ascii_art(KRT(2,1,4,3)) + 1 3 + 2 4 + """ + from sage.misc.ascii_art import AsciiArt + return AsciiArt(self._repr_diagram().splitlines()) + def to_kirillov_reshetikhin_crystal(self): r""" Construct a :func:`KirillovReshetikhinCrystal` element from ``self``. @@ -1053,11 +1093,11 @@ def to_array(self, rows=True): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 2) - sage: elt = KRT(4, 3, 2, 1) + sage: elt = KRT(2, 1, 4, 3) sage: elt.to_array() - [[3, 1], [4, 2]] + [[1, 3], [2, 4]] sage: elt.to_array(False) - [[4, 3], [2, 1]] + [[2, 1], [4, 3]] """ ret_list = [] h = self.parent()._r @@ -1085,10 +1125,10 @@ def to_tableau(self): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 2) - sage: elt = KRT(4, 3, 2, 1); elt - [[3, 1], [4, 2]] + sage: elt = KRT(2, 1, 4, 3); elt + [[1, 3], [2, 4]] sage: t = elt.to_tableau(); t - [[3, 1], [4, 2]] + [[1, 3], [2, 4]] sage: type(t) """ @@ -1101,11 +1141,11 @@ def pp(self): EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 2) - sage: elt = KRT(4, 3, 2, 1); elt - [[3, 1], [4, 2]] + sage: elt = KRT(2, 1, 4, 3); elt + [[1, 3], [2, 4]] sage: elt.pp() - 3 1 - 4 2 + 1 3 + 2 4 """ self.to_tableau().pp() @@ -1400,207 +1440,12 @@ class KRTableauxDTwistedSpin(KRTableauxRectangle): EXAMPLES:: - sage: KRT = KirillovReshetikhinTableaux(['E', 6, 1], 1, 1) + sage: KRT = KirillovReshetikhinTableaux(['D', 4, 2], 1, 1) sage: KRT.cardinality() - 27 - sage: KRC = KirillovReshetikhinCrystal(['E', 6, 1], 1, 1) + 8 + sage: KRC = KirillovReshetikhinCrystal(['D', 4, 2], 1, 1) sage: KRT.cardinality() == KRC.cardinality() True """ Element = KRTableauxSpinElement -class KRTableauxWrapper(KirillovReshetikhinTableaux): - """ - Kirillov-Reshetikhin tableaux that are wrappers around a - Kirillov-Reshetikhin crystal. - """ - def _build_module_generators(self): - """ - Build the module generators. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1, 1) - sage: KRT._build_module_generators() - ([(1,)],) - """ - C = self.kirillov_reshetikhin_crystal() - return tuple(self.element_class(self, mg) for mg in C.module_generators) - - def from_kirillov_reshetikhin_crystal(self, krc): - """ - Construct an element of ``self`` from the Kirillov-Reshetikhin - crystal element ``krc``. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1, 1) - sage: KRC = KirillovReshetikhinCrystal(['E',6,1], 1, 1) - sage: krc = KRC.module_generators[0].f(1); krc - [(-1, 3)] - sage: KRT.from_kirillov_reshetikhin_crystal(krc) - [(-1, 3)] - """ - return self.element_class(self, krc) - - class Element(ElementWrapper): - """ - A KR tableaux element wrapper around a KR crystal element. - """ - def _repr_diagram(self): - """ - Return a string representation of ``self`` as a diagram. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) - sage: elt = KRT.module_generators[0].f(1); elt - [(-1, 3)] - sage: elt._repr_diagram() - '[(-1, 3)]' - """ - try: - return self.to_tableau()._repr_diagram() - except (AttributeError, ValueError): - return repr(self) - - def to_kirillov_reshetikhin_crystal(self): - r""" - Construct a :func:`KirillovReshetikhinCrystal` element - from ``self``. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) - sage: elt = KRT.module_generators[0].f(1); elt - [(-1, 3)] - sage: elt.to_kirillov_reshetikhin_crystal() - [(-1, 3)] - """ - return self.value - - @cached_method - def to_tableau(self): - """ - Return a :class:`Tableau` object of ``self``. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) - sage: elt = KRT.module_generators[0].f(1); elt - [(-1, 3)] - sage: elt.to_tableau() - Traceback (most recent call last): - ... - ValueError: cannot convert [(-1, 3)] to a tableau - """ - try: - return self.value.to_tableau() - except (AttributeError, ValueError): - raise ValueError("cannot convert {} to a tableau".format(self)) - - def pp(self): - """ - Pretty print ``self``. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) - sage: elt = KRT.module_generators[0].f(1); elt - [(-1, 3)] - sage: elt.pp() - [(-1, 3)] - """ - try: - self.value.pp() - except (AttributeError, ValueError): - print self - - @cached_method - def classical_weight(self): - r""" - Return the classical weight of ``self``. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) - sage: elt = KRT.module_generators[0].f(1); elt - [(-1, 3)] - sage: elt.classical_weight() - (-1/2, 1/2, 1/2, 1/2, 1/2, -1/6, -1/6, 1/6) - """ - return self.value.lift().weight() - - def weight(self): - """ - Return the weight of ``self``. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) - sage: KRT.module_generators[0].weight() - -Lambda[0] + Lambda[1] - """ - return self.value.weight() - - def e(self, i): - """ - Perform the action of `e_i` on ``self``. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) - sage: KRT.module_generators[0].e(2) - sage: KRT.module_generators[0].e(0) - [(-2, 1)] - """ - next = self.value.e(i) - if next is None: - return None - return self.__class__(self.parent(), next) - - def f(self, i): - """ - Perform the action of `f_i` on ``self``. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) - sage: KRT.module_generators[0].f(0) - sage: KRT.module_generators[0].f(1) - [(-1, 3)] - """ - next = self.value.f(i) - if next is None: - return None - return self.__class__(self.parent(), next) - - def epsilon(self, i): - r""" - Compute `\epsilon_i` of ``self``. - - .. TODO:: - - Implement a direct action of `\epsilon_0` without moving to - KR crystals. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) - sage: KRT.module_generators[0].epsilon(0) - 1 - """ - return self.value.epsilon(i) - - def phi(self, i): - r""" - Compute `\phi_i` of ``self``. - - EXAMPLES:: - - sage: KRT = KirillovReshetikhinTableaux(['E',6,1], 1,1) - sage: KRT.module_generators[0].phi(0) - 0 - """ - return self.value.phi(i) - diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index 1c82399a496..a89cb9f3def 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -46,9 +46,9 @@ class RiggedConfigurationElement(ClonableArray): INPUT: - - ``parent`` -- The parent of this element + - ``parent`` -- the parent of this element - - ``rigged_partitions`` -- A list of rigged partitions + - ``rigged_partitions`` -- a list of rigged partitions There are two optional arguments to explicitly construct a rigged configuration. The first is **partition_list** which gives a list of @@ -278,7 +278,7 @@ def _repr_(self): EXAMPLES:: sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 2]]) - sage: RC(partition_list=[[2], [3,1], [3], [3]]) # indirect doctest + sage: RC(partition_list=[[2], [3,1], [3], [3]]) -1[ ][ ]-1 @@ -289,7 +289,7 @@ def _repr_(self): -2[ ][ ][ ]-2 - sage: RC(partition_list=[[],[],[],[]]) # indirect doctest + sage: RC(partition_list=[[],[],[],[]]) (/) @@ -312,7 +312,7 @@ def _latex_(self): EXAMPLES:: sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 2]]) - sage: latex(RC(partition_list=[[2], [3,1], [3], [3]])) # indirect doctest + sage: latex(RC(partition_list=[[2], [3,1], [3], [3]])) { \begin{array}[t]{r|c|c|l} \cline{2-3} -1 &\phantom{|}&\phantom{|}& -1 \\ @@ -341,7 +341,7 @@ def _latex_(self): \cline{2-4} \end{array} } - sage: latex(RC(partition_list=[[],[],[],[]])) # indirect doctest + sage: latex(RC(partition_list=[[],[],[],[]])) {\emptyset} \quad {\emptyset} @@ -357,9 +357,55 @@ def _latex_(self): return ret_string + def _ascii_art_(self): + """ + Return an ASCII art representation of ``self``. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 2]]) + sage: ascii_art(RC(partition_list=[[2], [3,1], [3], [3]])) + -1[ ][ ]-1 2[ ][ ][ ]2 -2[ ][ ][ ]-2 -2[ ][ ][ ]-2 + 0[ ]0 + sage: ascii_art(RC(partition_list=[[],[],[],[]])) + (/) (/) (/) (/) + sage: RC = RiggedConfigurations(['D', 7, 1], [[3,3],[5,2],[4,3],[2,3],[4,4],[3,1],[1,4],[2,2]]) + sage: elt = RC(partition_list=[[2],[3,2,1],[2,2,1,1],[2,2,1,1,1,1],[3,2,1,1,1,1],[2,1,1],[2,2]], + ....: rigging_list=[[2],[1,0,0],[4,1,2,1],[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,0],[0,0]]) + sage: ascii_art(elt) + 3[ ][ ]2 1[ ][ ][ ]1 4[ ][ ]4 2[ ][ ]1 0[ ][ ][ ]0 0[ ][ ]0 0[ ][ ]0 + 2[ ][ ]0 4[ ][ ]1 2[ ][ ]0 2[ ][ ]1 0[ ]0 0[ ][ ]0 + 1[ ]0 3[ ]2 0[ ]0 0[ ]0 0[ ]0 + 3[ ]1 0[ ]0 0[ ]0 + 0[ ]0 0[ ]0 + 0[ ]0 0[ ]0 + sage: Partitions.global_options(convention='French') + sage: ascii_art(elt) + 0[ ]0 0[ ]0 + 0[ ]0 0[ ]0 + 3[ ]1 0[ ]0 0[ ]0 + 1[ ]0 3[ ]2 0[ ]0 0[ ]0 0[ ]0 + 2[ ][ ]0 4[ ][ ]1 2[ ][ ]0 2[ ][ ]1 0[ ]0 0[ ][ ]0 + 3[ ][ ]2 1[ ][ ][ ]1 4[ ][ ]4 2[ ][ ]1 0[ ][ ][ ]0 0[ ][ ]0 0[ ][ ]0 + sage: Partitions.global_options.reset() + """ + from sage.combinat.partition import PartitionOptions + if PartitionOptions['convention'] == "French": + baseline = lambda s: 0 + else: + baseline = lambda s: len(s) + from sage.misc.ascii_art import AsciiArt + s = repr(self[0]).splitlines() + ret = AsciiArt(s, baseline=baseline(s)) + for tableau in self[1:]: + s = repr(tableau).splitlines() + ret += AsciiArt([" "], baseline=baseline(s)) + AsciiArt(s, baseline=baseline(s)) + return ret + def check(self): """ - Make sure all of the riggings are less than or equal to the vacancy number. + Make sure all of the riggings are less than or equal to the + vacancy number. TESTS:: @@ -370,9 +416,6 @@ def check(self): for partition in self: for i, vac_num in enumerate(partition.vacancy_numbers): if vac_num < partition.rigging[i]: - #print "FAILED FOR:" - #print self - #print "at:", i, vac_num, partition.rigging[i] raise ValueError("rigging can be at most the vacancy number") def to_tensor_product_of_kirillov_reshetikhin_tableaux(self, display_steps=False): @@ -390,7 +433,7 @@ def to_tensor_product_of_kirillov_reshetikhin_tableaux(self, display_steps=False INPUT: - - ``display_steps`` -- (default: ``False``) Boolean which indicates + - ``display_steps`` -- (default: ``False``) boolean which indicates if we want to output each step in the algorithm OUTPUT: @@ -438,7 +481,7 @@ def to_tensor_product_of_kirillov_reshetikhin_crystals(self, display_steps=False INPUT: - - ``display_steps`` -- (default: ``False``) Boolean which indicates + - ``display_steps`` -- (default: ``False``) boolean which indicates if we want to output each step in the algorithm EXAMPLES:: @@ -474,7 +517,7 @@ def nu(self): OUTPUT: - - The `\nu` array as a list + The `\nu` array as a list. EXAMPLES:: @@ -491,7 +534,7 @@ def nu(self): def e(self, a): r""" - Action of the crystal operator `e_a` on this rigged configuration element. + Action of the crystal operator `e_a` on ``self``. This implements the method defined in [CrysStructSchilling06]_ which finds the value `k` which is the length of the string with the @@ -507,7 +550,7 @@ def e(self, a): INPUT: - - ``a`` -- The index of the partition to remove a box. + - ``a`` -- the index of the partition to remove a box OUTPUT: @@ -619,13 +662,14 @@ def _generate_partition_e(self, a, b, k): INPUT: - - ``a`` -- The index of the partition we operated on. - - ``b`` -- The index of the partition to generate. - - ``k`` -- The length of the string with the smallest negative rigging of smallest length. + - ``a`` -- the index of the partition we operated on + - ``b`` -- the index of the partition to generate + - ``k`` -- the length of the string with the smallest negative + rigging of smallest length OUTPUT: - - The constructed rigged partition. + The constructed rigged partition. TESTS:: @@ -655,7 +699,7 @@ def _generate_partition_e(self, a, b, k): def f(self, a): r""" - Action of crystal operator `f_a` on this rigged configuration element. + Action of the crystal operator `f_a` on ``self``. This implements the method defined in [CrysStructSchilling06]_ which finds the value `k` which is the length of the string with the @@ -673,7 +717,7 @@ def f(self, a): INPUT: - - ``a`` -- The index of the partition to add a box. + - ``a`` -- the index of the partition to add a box OUTPUT: @@ -783,13 +827,14 @@ def _generate_partition_f(self, a, b, k): INPUT: - - ``a`` -- The index of the partition we operated on. - - ``b`` -- The index of the partition to generate. - - ``k`` -- The length of the string with smallest nonpositive rigging of largest length. + - ``a`` -- the index of the partition we operated on + - ``b`` -- the index of the partition to generate + - ``k`` -- the length of the string with smallest nonpositive rigging + of largest length OUTPUT: - - The constructed rigged partition. + The constructed rigged partition. TESTS:: @@ -917,7 +962,6 @@ def classical_weight(self): ....: if x.classical_weight() != y.classical_weight(): ....: passed_test = False ....: break - ... sage: passed_test True """ @@ -944,7 +988,7 @@ def get_vacancy_numbers(self, a): INPUT: - - ``a`` -- The index of the rigged partition. + - ``a`` -- the index of the rigged partition EXAMPLES:: @@ -960,9 +1004,9 @@ def get_vacancy_number(self, a, i): INPUT: - - ``a`` -- The index of the rigged partition. + - ``a`` -- the index of the rigged partition - - ``i`` -- The row of the rigged partition. + - ``i`` -- the row of the rigged partition EXAMPLES:: @@ -990,6 +1034,7 @@ def partition_rigging_lists(self): EXAMPLES:: + sage: RC = RiggedConfigurations(['A',3,1], [[1,2],[2,2]]) sage: rc = RC(partition_list=[[2],[1],[1]], rigging_list=[[-1],[0],[-1]]); rc -1[ ][ ]-1 @@ -999,7 +1044,7 @@ def partition_rigging_lists(self): -1[ ]-1 sage: rc.partition_rigging_lists() - [[[2],[1],[1]], [[-1],[0],[-1]]] + [[[2], [1], [1]], [[-1], [0], [-1]]] """ partitions = [] riggings = [] diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index d96404d4107..2760ba8dc76 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -371,7 +371,17 @@ def __init__(self, cartan_type, B): # We store the cartan matrix for the vacancy number calculations for speed self._cartan_matrix = self._cartan_type.classical().cartan_matrix() Parent.__init__(self, category=(RegularCrystals(), FiniteCrystals())) - self.rename("Rigged configurations of type %s and factor(s) %s" % (cartan_type, B)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2], [1, 1]]) + Rigged configurations of type ['A', 3, 1] and factor(s) ((3, 2), (1, 2), (1, 1)) + """ + return "Rigged configurations of type {} and factor(s) {}".format(self._cartan_type, self.dims) def __iter__(self): """ @@ -1170,6 +1180,18 @@ class RCTypeA2Even(RCNonSimplyLaced): sage: RC = RiggedConfigurations(['A',4,2], [[2,1]]) sage: TestSuite(RC).run() # long time """ + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A',4,2], [[1,1], [2,2]]) + sage: RC.cardinality() + 250 + """ + return self.tensor_product_of_kirillov_reshetikhin_tableaux().cardinality() + @lazy_attribute def virtual(self): """ @@ -1654,46 +1676,3 @@ def from_virtual(self, vrc): return self.element_class(self, partition_list=partitions, rigging_list=riggings, vacancy_numbers_list=vac_nums) -# For experimentation purposes only. -# I'm keeping this for when we implement the R-matrix in general -def R_matrix(ct, RS1, RS2, only_highest_weight=False): - r""" - Output pairs of Kirillov-Reshetikhin tableaux under the action of the - (combinatorial) `R`-matrix. - - INPUT: - - - ``ct`` -- A Cartan type - - ``RS1``, ``RS2`` -- Pairs of `(r_i, s_i)` for `i = 1,2` for the two - tensor factors `B^{r_1, s_1} \otimes B^{r_2, s_2}` - - ``only_highest_weight`` -- Output only the highest weight elements - - EXAMPLES:: - - sage: from sage.combinat.rigged_configurations.rigged_configurations import R_matrix - sage: L = R_matrix(['D',4,1], [2,1], [1,1]) - sage: len(L) - 232 - - Check that the `R`-matrix is the identity on `B \otimes B`:: - - sage: L = R_matrix(['A',2,1], [2,3], [2,3]) - sage: len(L) - 100 - sage: all(x == y for x,y in L) - True - """ - ct = CartanType(ct) - RC = RiggedConfigurations(ct, [RS1, RS2]) - RC2 = RiggedConfigurations(ct, [RS2, RS1]) - ret_list = [] - if only_highest_weight: - L = RC.module_generators - else: - L = RC - for x in L: - x2 = RC2(*x) - ret_list.append([x.to_tensor_product_of_kirillov_reshetikhin_tableaux(), - x2.to_tensor_product_of_kirillov_reshetikhin_tableaux()]) - return ret_list - diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index 8fa6f70cd78..af34133b67c 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -247,6 +247,29 @@ class TensorProductOfKirillovReshetikhinTableaux(FullTensorProductOfRegularCryst sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) sage: list(KRT.module_generators) [[[1], [2], [3]] (X) [[1], [2]], [[1], [3], [4]] (X) [[1], [2]]] + + To create elements directly (i.e. not passing in KR tableaux elements), + there is the **pathlist** option will receive a list of lists which + contain the reversed far-eastern reading word of the tableau. That is to + say, in English notation, the word obtain from reading bottom-to-top, + left-to-right. :: + + sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,2], [1,2], [2,1]]) + sage: elt = KRT(pathlist=[[3, 2, 1, 4, 2, 1], [1, 3], [3, 1]]) + sage: elt.pp() + 1 1 (X) 1 3 (X) 1 + 2 2 3 + 3 4 + + One can still create elements in the same way as tensor product of + crystals:: + + sage: K1 = KirillovReshetikhinTableaux(['A',3,1], 3,2) + sage: K2 = KirillovReshetikhinTableaux(['A',3,1], 1,2) + sage: K3 = KirillovReshetikhinTableaux(['A',3,1], 2,1) + sage: eltlong = KRT(K1(3, 2, 1, 4, 2, 1), K2(1, 3), K3(3, 1)) + sage: eltlong == elt + True """ @staticmethod def __classcall_private__(cls, cartan_type, B): @@ -341,12 +364,12 @@ def _element_constructor_(self, *path, **options): Construct an element of ``self``. Typically the user will call this with the option **pathlist** which - will receive a list and coerce it into a path. + will receive a list of lists of reversed far-eastern reading words. EXAMPLES:: sage: KRT = TensorProductOfKirillovReshetikhinTableaux(['A',3,1], [[3,1], [2,1]]) - sage: KRT(pathlist=[[4, 2, 1], [2, 1]]) # indirect doctest + sage: KRT(pathlist=[[4, 2, 1], [2, 1]]) [[1], [2], [4]] (X) [[1], [2]] """ if isinstance(path[0], KirillovReshetikhinTableauxElement): diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py index 024c15ca7b7..adc044e7e96 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py @@ -170,32 +170,31 @@ def _repr_diagram(self): 1 1 (X) 1 (X) 1 1 1 2 2 2 2 2 2 3 3 3 3 + sage: Partitions.global_options(convention='French') + sage: print TPKRT.module_generators[0]._repr_diagram() + 2 2 (X) 3 (X) 3 3 3 + 1 1 2 2 2 2 + 1 1 1 1 + sage: Partitions.global_options.reset() """ - arrays = [crys.to_array() for crys in self] - col_len = [len(t)>0 and len(t[0]) or 1 for t in arrays] # columns per component - row_max = max(len(t) for t in arrays) # maximum row length - # There should be a fancier list compression for this but I couldn't get - # one to work in the cases where a component was the empty partition - diag = [] - for row in xrange(row_max): - line='' - if row == 0: - line += ' (X) '.join(''.join(map(lambda x: "%3s"%str(x), arrays[c][row]))+ - ' '*(col_len[c]-len(arrays[c][row])) for c in range(len(arrays))) - else: - for c in range(len(arrays)): - if c > 0: - line += ' ' - if row < len(arrays[c]): - line += ''.join(map(lambda x: "%3s"%str(x), arrays[c][row]))+' '*(col_len[c]-len(arrays[c][row])) - else: - line += ' '*col_len[c] - diag.append(line) - return '\n'.join(map(str,diag)) - #if TableauTuples.global_options('convention')=='english': - # return '\n'.join(map(str,diag)) - #else: - # return '\n'.join(map(str,diag[::-1])) + comp = [crys._repr_diagram().splitlines() for crys in self] + num_comp = len(comp) # number of components + col_len = [len(t) > 0 and len(t[0]) or 1 for t in comp] # columns per component + num_rows = max(len(t) for t in comp) # number of rows + + # We take advantage of the fact the components are rectangular + diag = '' + diag += ' (X) '.join(c[0] for c in comp) + for row in xrange(1, num_rows): + diag += '\n' + for c in range(num_comp): + if c > 0: + diag += ' ' # For the tensor symbol + if row < len(comp[c]): + diag += comp[c][row] + else: + diag += ' '*col_len[c] + return diag def pp(self): """ From 321f134d426e2f67f84344e19b4be4eef0a723e2 Mon Sep 17 00:00:00 2001 From: Anne Schilling Date: Sun, 24 Nov 2013 16:31:10 -0800 Subject: [PATCH 152/206] fixed some issues with the documentation --- .../rigged_configurations/rigged_configurations.py | 3 ++- .../rigged_configurations/tensor_product_kr_tableaux.py | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 2760ba8dc76..7f0f8221430 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -833,7 +833,8 @@ class RCNonSimplyLaced(RiggedConfigurations): r""" Rigged configurations in non-simply-laced types. - These are rigged configurations which lift to a virtual configuration. + These are rigged configurations which lift to virtual rigged configurations + in a simply-laced type. For more on rigged configurations, see :class:`RiggedConfigurations`. """ diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index af34133b67c..39fb54ae4af 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -204,10 +204,9 @@ class TensorProductOfKirillovReshetikhinTableaux(FullTensorProductOfRegularCryst REFERENCES: - .. [OSS2011] Masato Okado, Reiho Sakamoto, Anne Schilling - Affine crystal structure on rigged configurations of type `D_n^{(1)}` - J. Algebraic Combinatorics, to appear, :doi:`10.1007/s10801-012-0383-z`, - `arxiv:`1109.3523` [math.QA] + .. [OSS2011] Masato Okado, Reiho Sakamoto, Anne Schilling, + Affine crystal structure on rigged configurations of type `D_n^{(1)}`, + J. Algebraic Combinatorics 37(3) (2013) 571-599 (:arxiv:`1109.3523` [math.QA]) EXAMPLES: From 06d8398b83dc7b5d82dd17dc77eabf7ad1005bc3 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Flori Date: Mon, 25 Nov 2013 06:02:51 -0800 Subject: [PATCH 153/206] #14410: let ATLAS build shared libs on Cygwin. --- build/pkgs/atlas/SPKG.txt | 7 ++ .../pkgs/atlas/patches/ATLAS-lib/Makefile.am | 114 +++++++++--------- .../pkgs/atlas/patches/ATLAS-lib/configure.ac | 28 +++++ build/pkgs/atlas/spkg-check | 51 ++++---- build/pkgs/atlas/spkg-install | 43 +++---- build/pkgs/atlas/spkg-src | 1 + 6 files changed, 130 insertions(+), 114 deletions(-) diff --git a/build/pkgs/atlas/SPKG.txt b/build/pkgs/atlas/SPKG.txt index 3375ffd39e6..e39bac2c773 100644 --- a/build/pkgs/atlas/SPKG.txt +++ b/build/pkgs/atlas/SPKG.txt @@ -95,6 +95,13 @@ The package can be configured via three environment variables: == ChangeLog == +=== atlas-3.10.1.p7, lapack-3.4.2 (Jean-Pierre Flori, 25 November 2013) === + * Trac #14410: Let ATLAS build and install shared libraries on Cygwin. + * Bunch of modifications to the autotools project generating shared libraries + so that it does not invoke libtool on Cygwin. + * Fix a bug in spkg-install. + * Cleanup spkg-check. + === atlas-3.10.1.p6, lapack-3.4.2 (Volker Braun, 11 October 2013) === * Trac #15270: Do not give up if the upstream shared library build fails diff --git a/build/pkgs/atlas/patches/ATLAS-lib/Makefile.am b/build/pkgs/atlas/patches/ATLAS-lib/Makefile.am index 287e55a8d19..46abdb9e6e4 100644 --- a/build/pkgs/atlas/patches/ATLAS-lib/Makefile.am +++ b/build/pkgs/atlas/patches/ATLAS-lib/Makefile.am @@ -5,9 +5,9 @@ SO_VERSION=3:0 # You shouldn't have to customize anything from here on -ATLAS_SERIAL_LIBS=libatlas.la libcblas.la libf77blas.la liblapack.la +ATLAS_SERIAL_LIBS=atlas cblas f77blas lapack -ATLAS_PARALLEL_LIBS=libptcblas.la libptf77blas.la libptlapack.la +ATLAS_PARALLEL_LIBS=ptcblas ptf77blas ptlapack all: all_parallel @@ -16,105 +16,103 @@ all_serial: $(ATLAS_SERIAL_LIBS) all_parallel: all_serial $(ATLAS_PARALLEL_LIBS) clean: - $(RM) -r -f .libs *.la *-obj + $(RM) -r -f .libs *.la *.dll *.dll.a *-obj install: install_parallel install_serial: $(ATLAS_SERIAL_LIBS) $(MKDIR_P) @libdir@ - @LIBTOOL@ --mode=install $(INSTALL) -c libatlas.la @libdir@/libatlas.la - @LIBTOOL@ --mode=install $(INSTALL) -c libcblas.la @libdir@/libcblas.la - @LIBTOOL@ --mode=install $(INSTALL) -c libf77blas.la @libdir@/libf77blas.la - @LIBTOOL@ --mode=install $(INSTALL) -c liblapack.la @libdir@/liblapack.la + $(MKDIR_P) @bindir@ + for module in $(ATLAS_SERIAL_LIBS); do \ + @LTINSTALL@; \ + done; @LIBTOOL@ --finish @libdir@ + @LIBTOOL@ --finish @bindir@ install_parallel: install_serial $(ATLAS_PARALLEL_LIBS) $(MKDIR_P) @libdir@ - @LIBTOOL@ --mode=install $(INSTALL) -c libptcblas.la @libdir@/libptcblas.la - @LIBTOOL@ --mode=install $(INSTALL) -c libptf77blas.la @libdir@/libptf77blas.la - @LIBTOOL@ --mode=install $(INSTALL) -c libptlapack.la @libdir@/libptlapack.la + $(MKDIR_P) @bindir@ + for module in $(ATLAS_PARALLEL_LIBS); do \ + @LTINSTALL@; \ + done; @LIBTOOL@ --finish @libdir@ - + @LIBTOOL@ --finish @bindir@ # In presence of multiple definitions of thread related functions, use the ones from *_mut.o # Works with all nm output formats (BSD/POSIX/System V) -libatlas.la: libatlas.a +atlas: libatlas.a -$(RM) -r -f libatlas-obj $(MKDIR) libatlas-obj - cd libatlas-obj && $(AR) x ../$< + cd libatlas-obj && $(AR) x ../$< && cd .. if [ `$(NM) -g $< | $(EGREP) -w 'ATL_(Set|Reset|Free|Dec)AtomicCount' | $(GREP) -w T | wc -l` -gt 4 ]; then \ $(RM) `ls -1 libatlas-obj/ATL_{Set,Reset,Free,Dec}AtomicCount_*.o | $(GREP) -v '_mut.o$$'`; \ fi - @LIBTOOL@ --tag=CC --mode=link $(CC) @LIBTOOL_TYPE@ \ - -o $@ libatlas-obj/*.o \ + @LTCLINK@ \ + -o @LTPREFIX@$@@LTSUFFIX@ libatlas-obj/*.o \ @PTHREAD_LIB@ -lm \ - -rpath @libdir@ -version-info $(SO_VERSION) - + @LTLINKFLAGS@ -libcblas.la: libcblas.a libatlas.la +cblas: libcblas.a @LTPREFIX@atlas@LTSUFFIX@ -$(RM) -r -f libcblas-obj $(MKDIR) libcblas-obj - cd libcblas-obj && $(AR) x ../$< - @LIBTOOL@ --tag=CC --mode=link $(CC) @LIBTOOL_TYPE@ \ - -o $@ libcblas-obj/*.o \ + cd libcblas-obj && $(AR) x ../$< && cd .. + @LTCLINK@ \ + -o @LTPREFIX@$@@LTSUFFIX@ libcblas-obj/*.o \ -latlas \ - -rpath @libdir@ -version-info $(SO_VERSION) - + @LTLINKFLAGS@ -libptcblas.la: libptcblas.a libatlas.la +ptcblas: libptcblas.a @LTPREFIX@atlas@LTSUFFIX@ -$(RM) -r -f libptcblas-obj $(MKDIR) libptcblas-obj - cd libptcblas-obj && $(AR) x ../$< - @LIBTOOL@ --tag=CC --mode=link $(CC) @LIBTOOL_TYPE@ \ - -o $@ libptcblas-obj/*.o \ + cd libptcblas-obj && $(AR) x ../$< && cd .. + @LTCLINK@ \ + -o @LTPREFIX@$@@LTSUFFIX@ libptcblas-obj/*.o \ @PTHREAD_LIB@ -latlas \ - -rpath @libdir@ -version-info $(SO_VERSION) + @LTLINKFLAGS@ - -libf77blas.la: libf77blas.a libatlas.la +f77blas: libf77blas.a @LTPREFIX@atlas@LTSUFFIX@ -$(RM) -r -f libf77blas-obj $(MKDIR) libf77blas-obj - cd libf77blas-obj && $(AR) x ../$< - @LIBTOOL@ --tag=F77 --mode=link $(F77) @LIBTOOL_TYPE@ \ - -o $@ libf77blas-obj/*.o \ + cd libf77blas-obj && $(AR) x ../$< && cd .. + @LTF77LINK@ \ + -o @LTPREFIX@$@@LTSUFFIX@ libf77blas-obj/*.o \ -latlas \ - -rpath @libdir@ -version-info $(SO_VERSION) + @LTLINKFLAGS@ -libptf77blas.la: libptf77blas.a libatlas.la +ptf77blas: libptf77blas.a @LTPREFIX@atlas@LTSUFFIX@ -$(RM) -r -f libptf77blas-obj $(MKDIR) libptf77blas-obj - cd libptf77blas-obj && $(AR) x ../$< - @LIBTOOL@ --tag=F77 --mode=link $(F77) @LIBTOOL_TYPE@ \ - -o $@ libptf77blas-obj/*.o \ + cd libptf77blas-obj && $(AR) x ../$< && cd .. + @LTF77LINK@ \ + -o @LTPREFIX@$@@LTSUFFIX@ libptf77blas-obj/*.o \ @PTHREAD_LIB@ -latlas \ - -rpath @libdir@ -version-info $(SO_VERSION) + @LTLINKFLAGS@ -libf77refblas.la: libf77refblas.a +f77refblas: libf77refblas.a -$(RM) -r -f libf77refblas-obj $(MKDIR) libf77refblas-obj - cd libf77refblas-obj && $(AR) x ../$< - @LIBTOOL@ --tag=F77 --mode=link $(F77) @LIBTOOL_TYPE@ \ - -o $@ libf77refblas-obj/*.o \ + cd libf77refblas-obj && $(AR) x ../$< && cd .. + @LTF77LINK@ \ + -o @LTPREFIX@$@@LTSUFFIX@ libf77refblas-obj/*.o \ -latlas \ - -rpath @libdir@ -version-info $(SO_VERSION) + @LTLINKFLAGS@ -liblapack.la: liblapack.a libatlas.la libcblas.la libf77blas.la +lapack: liblapack.a @LTPREFIX@atlas@LTSUFFIX@ @LTPREFIX@cblas@LTSUFFIX@ @LTPREFIX@f77blas@LTSUFFIX@ -$(RM) -r -f liblapack-obj $(MKDIR) liblapack-obj - cd liblapack-obj && $(AR) x ../$< - @LIBTOOL@ --tag=F77 --mode=link $(F77) @LIBTOOL_TYPE@ \ - -o $@ liblapack-obj/*.o \ - -latlas -lcblas -lf77blas -lm \ - -rpath @libdir@ -version-info $(SO_VERSION) + cd liblapack-obj && $(AR) x ../$< && cd .. + @LTF77LINK@ \ + -o @LTPREFIX@$@@LTSUFFIX@ liblapack-obj/*.o \ + -lcblas -lf77blas -latlas -lm \ + @LTLINKFLAGS@ -libptlapack.la: libptlapack.a libatlas.la libptcblas.la libptf77blas.la +ptlapack: libptlapack.a @LTPREFIX@atlas@LTSUFFIX@ @LTPREFIX@ptcblas@LTSUFFIX@ @LTPREFIX@ptf77blas@LTSUFFIX@ -$(RM) -r -f libptlapack-obj $(MKDIR) libptlapack-obj - cd libptlapack-obj && $(AR) x ../$< - @LIBTOOL@ --tag=F77 --mode=link $(F77) @LIBTOOL_TYPE@ \ - -o $@ libptlapack-obj/*.o \ - @PTHREAD_LIB@ -latlas -lptcblas -lptf77blas -lm \ - -rpath @libdir@ -version-info $(SO_VERSION) - + cd libptlapack-obj && $(AR) x ../$< && cd .. + @LTF77LINK@ \ + -o @LTPREFIX@$@@LTSUFFIX@ libptlapack-obj/*.o \ + @PTHREAD_LIB@ -lptcblas -lptf77blas -latlas -lm \ + @LTLINKFLAGS@ -.PHONY: all all_serial all_parallel build install install_serial install_parallel clean +.PHONY: all all_serial all_parallel build install install_serial install_parallel clean $(ATLAS_SERIAL_LIBS) $(ATLAS_PARALLEL_LIBS) diff --git a/build/pkgs/atlas/patches/ATLAS-lib/configure.ac b/build/pkgs/atlas/patches/ATLAS-lib/configure.ac index 261a5413a93..0ce6a0f44b9 100644 --- a/build/pkgs/atlas/patches/ATLAS-lib/configure.ac +++ b/build/pkgs/atlas/patches/ATLAS-lib/configure.ac @@ -1,6 +1,8 @@ AC_INIT([ATLAS],[3.10.1]) AM_INIT_AUTOMAKE(foreign) +AC_CANONICAL_HOST + LT_INIT AC_PROG_CC @@ -20,4 +22,30 @@ AS_IF([test "x$enable_static" = "xyes"], [libtool_type=-shared]) AC_SUBST(LIBTOOL_TYPE, [$libtool_type]) +case $host in + *-*-cygwin*) + LTPREFIX=cyg + LTSUFFIX=.dll + LTCLINK="\$(CC) -L. -shared" + LTF77LINK="\$(F77) -L. -shared" + LTLINKFLAGS="-Wl,--out-implib=lib\$@.dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import" + LTINSTALL="\$(CP) \$(LTPREFIX)\$\${module}\$(LTSUFFIX) \$(bindir)/\$(LTPREFIX)\$\${module}\$(LTSUFFIX); \$(CP) lib\$\${module}.dll.a \$(libdir)/lib\$\${module}.dll.a" + ;; + *) + LTPREFIX=lib + LTSUFFIX=.la + LTCLINK="\$(LIBTOOL) --tag=CC --mode=link \$(CC) \$(LIBTOOL_FLAG)" + LTF77LINK="\$(LIBTOOL) --tag=F77 --mode=link \$(F77) \$(LIBTOOL_FLAG)" + LTLINKFLAGS="-rpath \$(libdir) -version-info \$(SO_VERSION)" + LTINSTALL="\$(LIBTOOL) --mode=install \$(INSTALL) -c \$(LTPREFIX)\$\${module}\$(LTSUFFIX) \$(libdir)/\$(LTPREFIX)\$\${module}\$(LTSUFFIX)" + ;; +esac + +AC_SUBST(LTPREFIX) +AC_SUBST(LTSUFFIX) +AC_SUBST(LTCLINK) +AC_SUBST(LTF77LINK) +AC_SUBST(LTLINKFLAGS) +AC_SUBST(LTINSTALL) + AC_OUTPUT diff --git a/build/pkgs/atlas/spkg-check b/build/pkgs/atlas/spkg-check index 3e1e16262ac..c8c299d791c 100755 --- a/build/pkgs/atlas/spkg-check +++ b/build/pkgs/atlas/spkg-check @@ -4,9 +4,9 @@ ### Sanity check ###################################################################### -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting" - echo "Maybe run sage -sh?" +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "Error: SAGE_LOCAL undefined - exiting..." + echo >&2 "Maybe run 'sage -sh'?" exit 1 fi @@ -14,16 +14,13 @@ fi ### Skip building ATLAS on specific systems ###################################################################### -if [ `uname` = "CYGWIN" ]; then +if [ "$UNAME" = "Darwin" -a -z "$SAGE_ATLAS_ARCH" ]; then + echo "System-wide accelerate framework is used on Darwin; skipping ATLAS test suite." exit 0 fi -if [ `uname` = "Darwin" ]; then - exit 0 -fi - -if [ "x$SAGE_ATLAS_LIB" != "x" ]; then - echo "SAGE_ATLAS_LIB is set to \"$SAGE_ATLAS_LIB\"; skipping the test suite." +if [ ! -z "$SAGE_ATLAS_LIB" ]; then + echo "SAGE_ATLAS_LIB is set to \"$SAGE_ATLAS_LIB\"; skipping ATLAS test suite." exit 0 fi @@ -35,32 +32,28 @@ fi make_check() { # make sure everything builds correctly - cd ATLAS-build - make check - if [ $? -ne 0 ]; then - echo "An error occurred when running the ATLAS self-tests." - exit 1 - else - echo "The ATLAS self-tests successfully passed." - fi - cd .. + $MAKE check + if [ $? -ne 0 ]; then + echo >&2 "Error: The ATLAS self-tests failed." + exit 1 + else + echo "The ATLAS self-tests successfully passed." + fi } make_time() { # collect some timings - cd ATLAS-build - make time - if [ $? -ne 0 ]; then - echo "The ATLAS timing data failed to be collected." - exit 1 - else - echo "The ATLAS timing data was successfully collected." - fi - cd .. + $MAKE time + if [ $? -ne 0 ]; then + echo >&2 "Error: The ATLAS timing data failed to be collected." + exit 1 + else + echo "The ATLAS timing data was successfully collected." + fi } - +cd src/ATLAS-build make_check make_time diff --git a/build/pkgs/atlas/spkg-install b/build/pkgs/atlas/spkg-install index 0507cd124d2..c487d45b346 100755 --- a/build/pkgs/atlas/spkg-install +++ b/build/pkgs/atlas/spkg-install @@ -25,7 +25,7 @@ PATCH_DIR = os.path.join(conf['SPKG_DIR'], 'patches') LAPACK_TARFILE = os.path.join(conf['SPKG_DIR'], 'src', 'lapack-3.4.2.tar') # temporary directory to build everything in -BUILD_DIR = os.path.join(conf['SPKG_DIR'], 'ATLAS-build') +BUILD_DIR = os.path.join(conf['SPKG_DIR'], 'src', 'ATLAS-build') # the shared library autotools stub project BUILD_LIB_DIR = os.path.join(conf['SPKG_DIR'], 'src', 'ATLAS-lib') @@ -66,27 +66,12 @@ def assert_success(rc, good=None, bad=None): ### Skip building ATLAS on specific systems ###################################################################### -# On Cygwin we simply require that the system-wide lapack is installed. -# This includes BLAS and LAPACK and is enough to build the rest of Sage. -if conf['CYGWIN?'] and not os.environ.has_key('SAGE_ATLAS_ARCH'): - libraries = ['/usr/lib/libblas.dll.a', '/usr/lib/liblapack.dll.a'] - for lib in libraries: - if not os.path.exists(lib): - print '*'*75 - print 'Could not locate required file: "' + lib + '".' - print 'On Cygwin you must install the following standard LAPACK Cygwin packages' - print 'via the Cygwin setup.exe program in the "Math" category:' - print '* lapack,' - print '* liblapack-devel.' - print 'Alternatively you can try building your own ATLAS by setting' - print 'SAGE_ATLAS_ARCH to something sensible although that is not' - print 'officially supported.' - print '*'*75 - sys.exit(1) - for lib in libraries: - cp(lib, os.path.join(conf['SAGE_LOCAL'], 'lib')) - sys.exit(0) - +# On Cygwin, we used to require that the system-wide lapack packages were +# installed (this included the lapack-devel and lapack packages). +# These packages indeed include BLAS and LAPACK and are enough to build +# the rest of Sage, whereas building ATLAS was problematic. +# For the record, the corresponding files to symlink from the system-wide +# packages are: '/usr/lib/libblas.dll.a' and '/usr/lib/liblapack.dll.a'. if conf['Darwin?'] and not os.environ.has_key('SAGE_ATLAS_ARCH'): print 'Skipping build of ATLAS on OS X, using system library instead.' @@ -107,8 +92,12 @@ if conf['Darwin?'] and not os.environ.has_key('SAGE_ATLAS_ARCH'): if os.environ.has_key('SAGE_ATLAS_LIB'): ATLAS_LIB = os.environ['SAGE_ATLAS_LIB'] - libraries = ['libatlas', 'liblapack', 'libcblas', 'libf77blas'] - libraries_optional = ['libptcblas', 'libptf77blas'] + if conf['CYGWIN?']: + libraries = ['libblas', 'liblapack'] + libraries_optional = [] + else: + libraries = ['libatlas', 'liblapack', 'libcblas', 'libf77blas'] + libraries_optional = ['libptcblas', 'libptf77blas'] def is_atlas_lib_path(path): if path is None: @@ -164,7 +153,7 @@ if os.environ.has_key('SAGE_ATLAS_LIB'): for fname in filenames: source = os.path.join(ATLAS_LIB, fname) destination = os.path.join(SAGE_LOCAL_LIB, fname) - print 'Symlinking '+source+' -> '+destination + print 'Symlinking '+destination+' -> '+source try: os.remove(destination) except OSError: @@ -293,7 +282,7 @@ def configure(arch=None, isa_ext=None): else: CCbin, CCopt = CC, '' - cmd = '../src/ATLAS/configure' + cmd = '../ATLAS/configure' cmd += ' --prefix=' + conf['SAGE_LOCAL'] cmd += ' --with-netlib-lapack-tarfile=' + LAPACK_TARFILE cmd += ' --cc="' + CC + '"' @@ -563,7 +552,7 @@ if rc!=0: INSTALL_STATIC_LIBRARIES = True else: print 'Finished building serial shared ATLAS library (libtool).' - have_serial_libs = True + have_serial_libs = True else: have_parallel_libs = True diff --git a/build/pkgs/atlas/spkg-src b/build/pkgs/atlas/spkg-src index 77bd360cc99..617c7a5cc42 100755 --- a/build/pkgs/atlas/spkg-src +++ b/build/pkgs/atlas/spkg-src @@ -48,6 +48,7 @@ gunzip $LAPACK cp -rp "$SPKG_ROOT"/patches/ATLAS-lib . cd ATLAS-lib +mkdir m4 autoreconf -fiv rm -rf autom4te.cache From f38dea92808fbd2cce407f265d079e048b4fe717 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 28 Nov 2013 12:38:15 -0800 Subject: [PATCH 154/206] Corrected and expanded fermoinic_formula() method, for RC's. --- src/sage/combinat/crystals/tensor_product.py | 4 +- .../rigged_configuration_element.py | 33 +++++ .../rigged_configurations.py | 120 ++++++++++++++---- 3 files changed, 133 insertions(+), 24 deletions(-) diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index f2a5a99e67e..59d6d1191c7 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -287,8 +287,8 @@ def one_dimensional_configuration_sum(self, q = None, group_components = True): True """ if q is None: - from sage.rings.all import QQ - q = QQ['q'].gens()[0] + from sage.rings.all import ZZ + q = ZZ['q'].gens()[0] P0 = self.weight_lattice_realization().classical() B = P0.algebra(q.parent()) if group_components: diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index a89cb9f3def..9b5d7c4c868 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -862,6 +862,39 @@ def _generate_partition_f(self, a, b, k): return(RiggedPartition(new_list, new_rigging, new_vac_nums)) +# def epsilon(self, a): +# r""" +# Return `\varepsilon_a` of ``self``. +# +# Let `x_{\ell}` be the smallest string of `\nu^{(a)}` or `0` if +# `\nu^{(a)} = \emptyset`, then we have +# `\varepsilon_a = -\min(0, x_{\ell})`. +# +# EXAMPLES:: +# +# sage: RC = RiggedConfigurations(['A', 4, 1], [[2,1]]) +# """ +# if len(self[a-1]) == 0: +# return 0 +# return -min(0, min(self[a-1].rigging)) +# +# def phi(self, a): +# r""" +# Return `\varphi_a` of ``self``. +# +# Let `x_{\ell}` be the smallest string of `\nu^{(a)}` or `0` if +# `\nu^{(a)} = \emptyset`, then we have +# `\varepsilon_a = p_{\infty}^{(a)} - min(0, x_{\ell})`. +# +# EXAMPLES:: +# +# sage: RC = RiggedConfigurations(['A', 4, 1], [[2,1]]) +# """ +# p_inf = self.parent()._calc_vacancy_number(self, a, None) +# if len(self[a-1]) == 0: +# return p_inf +# return p_inf - min(0, min(self[a-1].rigging)) + @cached_method def cocharge(self): r""" diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 7f0f8221430..efb15faba99 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -365,7 +365,6 @@ def __init__(self, cartan_type, B): sage: RC = RiggedConfigurations(['D', 4, 1], [[4,3]]) sage: TestSuite(RC).run() # long time """ - self._cartan_type = cartan_type self.dims = B # We store the cartan matrix for the vacancy number calculations for speed @@ -757,7 +756,7 @@ def tensor_product_of_kirillov_reshetikhin_crystals(self): """ return self.tensor_product_of_kirillov_reshetikhin_tableaux().tensor_product_of_kirillov_reshetikhin_crystals() - def fermionic_formula(self, q=None): + def fermionic_formula(self, q=None, only_highest_weight=False, weight=None): r""" Return the fermoinic formula associated to ``self``. @@ -768,8 +767,9 @@ def fermionic_formula(self, q=None): M(\lambda, L; q) = \sum_{(\nu,J)} q^{cc(\nu, J)} - where we sum over all classically highest weight rigged - configurations where `cc` is the :meth:`cocharge statistic + where we sum over all (classically highest weight) rigged + configurations of weight `\lambda` where `cc` is the + :meth:`cocharge statistic `. This is known to reduce to @@ -779,31 +779,107 @@ def fermionic_formula(self, q=None): I \times \ZZ} \begin{bmatrix} p_i^{(a)} + m_i^{(a)} \\ m_i^{(a)} \end{bmatrix}_q. + Here we consider a more general fermionic formula in the weight + algebra: + + .. MATH:: + + M(L; q) = \sum_{\lambda \in P} M(\lambda, L; q) \lambda. + + This is conjecturally equal to the + :meth:`one dimensional configuration sum + ` + of the corresponding tensor product of Kirillov-Reshetikhin crystals. + + INPUT: + + - ``q`` -- the variable `q` + - ``only_highest_weight`` -- use only the classicaly highest weight + rigged configurations + - ``weight`` -- return the fermionic formula `M(\lambda, L; q)` where + `\lambda` is the classical weight ``weight`` + EXAMPLES:: - sage: RC = RiggedConfigurations(['A', 3, 1], [[3,1],[2,2]]) + sage: RC = RiggedConfigurations(['A', 2, 1], [[1,1], [1,1]]) sage: RC.fermionic_formula() + B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]] + + (q+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]] + sage: t = QQ['t'].gen(0) + sage: RC.fermionic_formula(t) + B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + + (t+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]] + sage: La = RC.weight_lattice_realization().classical().fundamental_weights() + sage: RC.fermionic_formula(weight=La[2]) q + 1 - sage: RC = RiggedConfigurations(['D', 4, 1], [[3,2],[4,1],[2,2]]) - sage: RC.fermionic_formula() - q^6 + 6*q^5 + 11*q^4 + 14*q^3 + 11*q^2 + 4*q + 1 + sage: RC.fermionic_formula(only_highest_weight=True, weight=La[2]) + q + + Only using the highest weight elements on other types:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[3,1], [2,2]]) + sage: RC.fermionic_formula(only_highest_weight=True) + q*B[Lambda[1] + Lambda[2]] + B[2*Lambda[2] + Lambda[3]] + sage: RC = RiggedConfigurations(['D', 4, 1], [[3,1], [4,1], [2,1]]) + sage: RC.fermionic_formula(only_highest_weight=True) + (q^4+q^3+q^2)*B[Lambda[1]] + (q^2+q)*B[Lambda[1] + Lambda[2]] + + q*B[Lambda[1] + 2*Lambda[3]] + q*B[Lambda[1] + 2*Lambda[4]] + + B[Lambda[2] + Lambda[3] + Lambda[4]] + (q^3+2*q^2+q)*B[Lambda[3] + Lambda[4]] sage: RC = RiggedConfigurations(['E', 6, 1], [[2,2]]) - sage: RC.fermionic_formula() - q^2 + q + 1 - sage: RC = RiggedConfigurations(['B', 3, 1], [[3,1],[2,2]]) - sage: RC.fermionic_formula() - q^3 + 3*q^2 + 2*q + 1 - sage: RC = RiggedConfigurations(['C', 3, 1], [[3,1],[2,2]]) - sage: RC.fermionic_formula() - q^4 + 2*q^3 + 3*q^2 + 2*q + 1 - sage: RC = RiggedConfigurations(['D', 4, 2], [[3,1],[2,2]]) - sage: RC.fermionic_formula() - q^6 + 2*q^5 + 4*q^4 + 3*q^3 + 3*q^2 + q + 1 + sage: RC.fermionic_formula(only_highest_weight=True) + q^2*B[0] + q*B[Lambda[2]] + B[2*Lambda[2]] + sage: RC = RiggedConfigurations(['B', 3, 1], [[3,1], [2,2]]) + sage: RC.fermionic_formula(only_highest_weight=True) # long time + q*B[Lambda[1] + Lambda[2] + Lambda[3]] + q^2*B[Lambda[1] + + Lambda[3]] + (q^2+q)*B[Lambda[2] + Lambda[3]] + B[2*Lambda[2] + + Lambda[3]] + (q^3+q^2)*B[Lambda[3]] + sage: RC = RiggedConfigurations(['C', 3, 1], [[3,1], [2,2]]) + sage: RC.fermionic_formula(only_highest_weight=True) # long time + (q^3+q^2)*B[Lambda[1] + Lambda[2]] + q*B[Lambda[1] + 2*Lambda[2]] + + (q^2+q)*B[2*Lambda[1] + Lambda[3]] + B[2*Lambda[2] + Lambda[3]] + + (q^4+q^3+q^2)*B[Lambda[3]] + sage: RC = RiggedConfigurations(['D', 4, 2], [[3,1], [2,2]]) + sage: RC.fermionic_formula(only_highest_weight=True) # long time + (q^2+q)*B[Lambda[1] + Lambda[2] + Lambda[3]] + (q^5+2*q^4+q^3)*B[Lambda[1] + + Lambda[3]] + (q^3+q^2)*B[2*Lambda[1] + Lambda[3]] + (q^4+q^3+q^2)*B[Lambda[2] + + Lambda[3]] + B[2*Lambda[2] + Lambda[3]] + (q^6+q^5+q^4)*B[Lambda[3]] + + TESTS:: + + sage: RC = RiggedConfigurations(['A', 2, 1], [[1,1], [1,1]]) + sage: KR = RC.tensor_product_of_kirillov_reshetikhin_crystals() + sage: RC.fermionic_formula() == KR.one_dimensional_configuration_sum() + True + sage: RC = RiggedConfigurations(['C', 2, 1], [[2,1], [2,1]]) + sage: KR = RC.tensor_product_of_kirillov_reshetikhin_crystals() + sage: RC.fermionic_formula() == KR.one_dimensional_configuration_sum() # long time + True + sage: t = QQ['t'].gen(0) + sage: RC = RiggedConfigurations(['D', 4, 1], [[1,1], [2,1]]) + sage: KR = RC.tensor_product_of_kirillov_reshetikhin_crystals() + sage: RC.fermionic_formula(t) == KR.one_dimensional_configuration_sum(t) # long time + True """ if q is None: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing q = PolynomialRing(ZZ, 'q').gen(0) - return sum([q**x.cc() for x in self.module_generators], ZZ.zero()) + + if only_highest_weight: + L = self.module_generators + else: + L = self + + P = q.parent() + WLR = self.weight_lattice_realization().classical() + + if weight is not None: + weight = WLR(weight) + return P.sum(q**x.cc() for x in L if WLR(x.weight()) == weight) + + B = WLR.algebra(P) + return B.sum(q**x.cc() * B(WLR(x.weight())) for x in L) def _test_bijection(self, **options): r""" @@ -1177,7 +1253,7 @@ class RCTypeA2Even(RCNonSimplyLaced): sage: RC.cardinality() 3 sage: RC = RiggedConfigurations(['A',2,2], [[1,2],[1,1]]) - sage: TestSuite(RC).run() + sage: TestSuite(RC).run() # long time sage: RC = RiggedConfigurations(['A',4,2], [[2,1]]) sage: TestSuite(RC).run() # long time """ @@ -1368,7 +1444,7 @@ class RCTypeA2Dual(RCTypeA2Even): sage: RC.cardinality() 3 sage: RC = RiggedConfigurations(CartanType(['A',2,2]).dual(), [[1,2],[1,1]]) - sage: TestSuite(RC).run() + sage: TestSuite(RC).run() # long time sage: RC = RiggedConfigurations(CartanType(['A',4,2]).dual(), [[2,1]]) sage: TestSuite(RC).run() # long time """ From e9e1cd23fdaed50e42a6cca97f50befd42b9e719 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 28 Nov 2013 14:43:21 -0800 Subject: [PATCH 155/206] Allow one_dimensional_configuration_sum() to work for TP of KR tableaux. --- src/sage/combinat/crystals/tensor_product.py | 4 +++- .../combinat/rigged_configurations/rigged_configurations.py | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index 59d6d1191c7..55b66319e53 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -1316,7 +1316,9 @@ def e_string_to_ground_state(self): sage: y.e_string_to_ground_state() () """ - if self.parent().crystals[0].__module__ != 'sage.combinat.crystals.kirillov_reshetikhin': + from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux + if self.parent().crystals[0].__module__ != 'sage.combinat.crystals.kirillov_reshetikhin' and \ + not isinstance(self.parent().crystals[0], KirillovReshetikhinTableaux): raise ValueError("All crystals in the tensor product need to be Kirillov-Reshetikhin crystals") I = self.cartan_type().classical().index_set() ell = max(ceil(K.s()/K.cartan_type().c()[K.r()]) for K in self.parent().crystals) diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index efb15faba99..893a568e3ad 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -852,6 +852,9 @@ def fermionic_formula(self, q=None, only_highest_weight=False, weight=None): sage: KR = RC.tensor_product_of_kirillov_reshetikhin_crystals() sage: RC.fermionic_formula() == KR.one_dimensional_configuration_sum() True + sage: KT = RC.tensor_product_of_kirillov_reshetikhin_tableaux() + sage: RC.fermionic_formula() == KT.one_dimensional_configuration_sum() + True sage: RC = RiggedConfigurations(['C', 2, 1], [[2,1], [2,1]]) sage: KR = RC.tensor_product_of_kirillov_reshetikhin_crystals() sage: RC.fermionic_formula() == KR.one_dimensional_configuration_sum() # long time From 877c21579cfc027e542977128e47a396c308f32e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 1 Dec 2013 11:35:10 -0800 Subject: [PATCH 156/206] Made all 1-dim config sum/fermionic formulas use QQ['t']. Added references to fermionic_formula(). --- src/sage/combinat/crystals/tensor_product.py | 4 +-- .../rigged_configurations.py | 31 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index 55b66319e53..f0d2c31029b 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -287,8 +287,8 @@ def one_dimensional_configuration_sum(self, q = None, group_components = True): True """ if q is None: - from sage.rings.all import ZZ - q = ZZ['q'].gens()[0] + from sage.rings.all import QQ + q = QQ['q'].gens()[0] P0 = self.weight_lattice_realization().classical() B = P0.algebra(q.parent()) if group_components: diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 893a568e3ad..a0f03fd76f3 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -118,28 +118,29 @@ class RiggedConfigurations(Parent, UniqueRepresentation): REFERENCES: .. [HKOTT2002] G. Hatayama, A. Kuniba, M. Okado, T. Takagi, Z. Tsuboi. - Paths, Crystals and Fermionic Formulae - Prog.Math.Phys. 23 (2002) 205-272 + Paths, Crystals and Fermionic Formulae. + Prog. Math. Phys. **23** (2002) Pages 205-272. .. [CrysStructSchilling06] Anne Schilling. Crystal structure on rigged configurations. International Mathematics Research Notices. - Volume 2006. 2006. Article ID 97376. Pages 1-27. + Volume 2006. (2006) Article ID 97376. Pages 1-27. .. [RigConBijection] Masato Okado, Anne Schilling, Mark Shimozono. A crystal to rigged configuration bijection for non-exceptional affine algebras. Algebraic Combinatorics and Quantum Groups. - Edited by N. Jing. World Scientific. 2003. Pages 85-124. + Edited by N. Jing. World Scientific. (2003) Pages 85-124. .. [BijectionDn] Anne Schilling. A bijection between type `D_n^{(1)}` crystals and rigged configurations. - J. Algebra. 285. 2005. 292-334 + J. Algebra. **285** (2005) 292-334 .. [BijectionLRT] Anatol N. Kirillov, Anne Schilling, Mark Shimozono. A bijection between Littlewood-Richardson tableaux and rigged configurations. - Selecta Mathematica (N.S.). 8. 2002. 67-135 (:mathscinet:`MR1890195`). + Selecta Mathematica (N.S.). **8** (2002) Pages 67-135. + (:mathscinet:`MR1890195`). EXAMPLES:: @@ -790,6 +791,11 @@ def fermionic_formula(self, q=None, only_highest_weight=False, weight=None): :meth:`one dimensional configuration sum ` of the corresponding tensor product of Kirillov-Reshetikhin crystals. + This has been proven in general for type `A_n^{(1)}` [BijectionLRT]_, + single factors `B^{r,s}` in type `D_n^{(1)}` [OSS2011]_ with the result + from [Sakamoto13]_, as well as for a tensor product of single columns + [OSS2003]_ or a tensor product of single rows [OSS03]_ for all + non-exceptional types. INPUT: @@ -799,6 +805,17 @@ def fermionic_formula(self, q=None, only_highest_weight=False, weight=None): - ``weight`` -- return the fermionic formula `M(\lambda, L; q)` where `\lambda` is the classical weight ``weight`` + REFERENCES: + + .. [OSS2003] Masato Okado, Anne Schilling, and Mark Shimozono. + Virtual crystals and fermionic formulas of type `D_{n+1}^{(2)}`, + `A_{2n}^{(2)}`, and `C_n^{(1)}`. Representation Theory. **7** (2003) + :arxiv:`math.QA/0105017`. + + .. [Sakamoto13] Reiho Sakamoto. + Rigged configurations and Kashiwara operators. + (2013) :arxiv:`1302.4562v1`. + EXAMPLES:: sage: RC = RiggedConfigurations(['A', 2, 1], [[1,1], [1,1]]) @@ -867,7 +884,7 @@ def fermionic_formula(self, q=None, only_highest_weight=False, weight=None): """ if q is None: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - q = PolynomialRing(ZZ, 'q').gen(0) + q = PolynomialRing(QQ, 'q').gen(0) if only_highest_weight: L = self.module_generators From 42bc8b6651ff28307cb0ade6c46d60abff2742a1 Mon Sep 17 00:00:00 2001 From: Anne Schilling Date: Tue, 3 Dec 2013 16:50:54 -0800 Subject: [PATCH 157/206] some edits to docs --- .../rigged_configurations/rigged_configurations.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index a0f03fd76f3..f7e977465d6 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -761,7 +761,7 @@ def fermionic_formula(self, q=None, only_highest_weight=False, weight=None): r""" Return the fermoinic formula associated to ``self``. - Given a set of rigged configurations `RC(\Lambda, L)`, the fermonic + Given a set of rigged configurations `RC(\lambda, L)`, the fermonic formula is defined as: .. MATH:: @@ -780,8 +780,7 @@ def fermionic_formula(self, q=None, only_highest_weight=False, weight=None): I \times \ZZ} \begin{bmatrix} p_i^{(a)} + m_i^{(a)} \\ m_i^{(a)} \end{bmatrix}_q. - Here we consider a more general fermionic formula in the weight - algebra: + The generating function of `M(\lambda, L; q)` in the weight algebra subsumes all fermionic formulas: .. MATH:: @@ -790,11 +789,11 @@ def fermionic_formula(self, q=None, only_highest_weight=False, weight=None): This is conjecturally equal to the :meth:`one dimensional configuration sum ` - of the corresponding tensor product of Kirillov-Reshetikhin crystals. + of the corresponding tensor product of Kirillov-Reshetikhin crystals, see [HKOTT2002]_. This has been proven in general for type `A_n^{(1)}` [BijectionLRT]_, single factors `B^{r,s}` in type `D_n^{(1)}` [OSS2011]_ with the result from [Sakamoto13]_, as well as for a tensor product of single columns - [OSS2003]_ or a tensor product of single rows [OSS03]_ for all + [OSS2003]_, [BijectionDn]_ or a tensor product of single rows [OSS03]_ for all non-exceptional types. INPUT: From 8f2b13651fa5026497eaab65bf4a90dfa92ca9e8 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 5 Dec 2013 11:11:15 +0000 Subject: [PATCH 158/206] 5.13.beta5 --- src/sage/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/version.py b/src/sage/version.py index 303791b056f..56c278822bc 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,2 +1,2 @@ """nodoctests""" -version='5.13.beta4'; date='2013-11-24' +version='5.13.beta5'; date='2013-12-05' From 0aa3fed94db9abeb46b9863ff273f267e69dad30 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 5 Dec 2013 11:13:08 +0000 Subject: [PATCH 159/206] 5.13.beta5 --- src/bin/sage-banner | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 8f100cd21c7..7998b4edd04 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ Sage Version 5.13.beta4, Release Date: 2013-11-24 │ +│ Sage Version 5.13.beta5, Release Date: 2013-12-05 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ From 29c9d3421aa41536292a3a02f3c71439017f15aa Mon Sep 17 00:00:00 2001 From: "R. Andrew Ohana" Date: Thu, 5 Dec 2013 21:27:23 -0800 Subject: [PATCH 160/206] [FIXUP] 5.13.beta5: update git specific files These currently include * VERSION.txt * package-version.txt * checksums.ini --- VERSION.txt | 2 +- build/pkgs/atlas/package-version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 042e0531d94..d3101fd8a0a 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 5.13.beta4, released 2013-11-24 +Sage version 5.13.beta5, released 2013-12-05 diff --git a/build/pkgs/atlas/package-version.txt b/build/pkgs/atlas/package-version.txt index 5551e798642..ea4d24b7f41 100644 --- a/build/pkgs/atlas/package-version.txt +++ b/build/pkgs/atlas/package-version.txt @@ -1 +1 @@ -3.10.1.p6 +3.10.1.p7 From 17a04d7547402cdab400ca92f8b71b51b38445ed Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 11 Dec 2013 13:01:34 -0800 Subject: [PATCH 161/206] Added deprecatd classes back with warnings. --- .../combinat/rigged_configurations/all.py | 2 ++ .../rigged_configurations.py | 22 +++++++++++++++++++ .../tensor_product_kr_tableaux.py | 17 ++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/sage/combinat/rigged_configurations/all.py b/src/sage/combinat/rigged_configurations/all.py index dbb3b62c4ba..2388f83e019 100644 --- a/src/sage/combinat/rigged_configurations/all.py +++ b/src/sage/combinat/rigged_configurations/all.py @@ -1,4 +1,6 @@ +from rigged_configurations import HighestWeightRiggedConfigurations from rigged_configurations import RiggedConfigurations from tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux +from tensor_product_kr_tableaux import HighestWeightTensorProductOfKirillovReshetikhinTableaux from kr_tableaux import KirillovReshetikhinTableaux diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index f7e977465d6..3786c237d1d 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -1772,3 +1772,25 @@ def from_virtual(self, vrc): return self.element_class(self, partition_list=partitions, rigging_list=riggings, vacancy_numbers_list=vac_nums) +def HighestWeightRiggedConfigurations(cartan_type, B): + """ + Deprecated in :trac:`13872`. Use instead the attribute + ``module_generators`` of :class:`RiggedConfigurations`. + + EXAMPLES:: + + sage: HighestWeightRiggedConfigurations(['A',2,1], [[1,1]]) + doctest:...: DeprecationWarning: this class is deprecated. + Use RiggedConfigurations(cartan_type, B).module_generators instead + See http://trac.sagemath.org/13872 for details. + ( + (/) + + (/) + ,) + """ + from sage.misc.superseded import deprecation + deprecation(13872, 'this class is deprecated. Use RiggedConfigurations(' + 'cartan_type, B).module_generators instead') + return RiggedConfigurations(cartan_type, B).module_generators + diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index 39fb54ae4af..9cd0a0c388b 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -454,3 +454,20 @@ def tensor_product_of_kirillov_reshetikhin_crystals(self): TensorProductOfKirillovReshetikhinTableaux.Element = TensorProductOfKirillovReshetikhinTableauxElement +def HighestWeightTensorProductOfKirillovReshetikhinTableaux(cartan_type, B): + """ + Deprecated in :trac:`13872`. Use instead the attribute + ``module_generators`` of :class:`TensorProductOfKirillovReshetikhinTableaux`. + + EXAMPLES:: + + sage: HighestWeightTensorProductOfKirillovReshetikhinTableaux(['A',2,1], [[1,1]]) + doctest:...: DeprecationWarning: this class is deprecated. + Use TensorProductOfKirillovReshetikhinTableaux(cartan_type, B).module_generators instead + See http://trac.sagemath.org/13872 for details. + Highest weight elements of Tensor product of Kirillov-Reshetikhin tableaux of type ['A', 2, 1] and factor(s) ((1, 1),) + """ + from sage.misc.superseded import deprecation + deprecation(13872, 'this class is deprecated. Use TensorProductOfKirillovReshetikhinTableaux(' + 'cartan_type, B).module_generators instead') + return TensorProductOfKirillovReshetikhinTableaux(cartan_type, B).module_generators From a51b39f73f237f8353ca55ecfe6a484d5d8de88f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 11 Dec 2013 13:04:37 -0800 Subject: [PATCH 162/206] Fixed minor typo in cobminat/crystals/letters.pyx. --- src/sage/combinat/crystals/letters.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/crystals/letters.pyx b/src/sage/combinat/crystals/letters.pyx index 8a6da022fc2..834d32aa192 100644 --- a/src/sage/combinat/crystals/letters.pyx +++ b/src/sage/combinat/crystals/letters.pyx @@ -479,7 +479,7 @@ cdef class Letter(Element): cdef class EmptyLetter(Element): """ - The affine)letter `\emptyset` thought of as a classical crystal letter + The affine letter `\emptyset` thought of as a classical crystal letter in classical type `B_n` and `C_n`. .. WARNING:: From 85b64461d4e1c62433615fbd7a76c8c8962e7e19 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 11 Dec 2013 13:05:48 -0800 Subject: [PATCH 163/206] Reorded all.py to match original (so fewer changes). --- src/sage/combinat/rigged_configurations/all.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/rigged_configurations/all.py b/src/sage/combinat/rigged_configurations/all.py index 2388f83e019..bd34bcef34e 100644 --- a/src/sage/combinat/rigged_configurations/all.py +++ b/src/sage/combinat/rigged_configurations/all.py @@ -1,6 +1,6 @@ from rigged_configurations import HighestWeightRiggedConfigurations from rigged_configurations import RiggedConfigurations -from tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux from tensor_product_kr_tableaux import HighestWeightTensorProductOfKirillovReshetikhinTableaux +from tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux from kr_tableaux import KirillovReshetikhinTableaux From 47183314533841e0a297b4540443ddfc5b4302bd Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Fri, 22 Nov 2013 21:59:41 +0000 Subject: [PATCH 164/206] declare closure_callgenvec in sage/libs/pari/decl.pxi --- src/sage/libs/pari/decl.pxi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/libs/pari/decl.pxi b/src/sage/libs/pari/decl.pxi index 99ac368ca96..230fd8d9d9b 100644 --- a/src/sage/libs/pari/decl.pxi +++ b/src/sage/libs/pari/decl.pxi @@ -1114,6 +1114,10 @@ cdef extern from 'pari/pari.h': void write1(char *s, GEN g) void writetex(char *s, GEN g) + # eval.c + + GEN closure_callgenvec(GEN C, GEN args) + # FF.c GEN FF_1(GEN a) From 8ce4d4764b9ec325ca0d6051cfac1e60bb2d116d Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Fri, 22 Nov 2013 22:01:12 +0000 Subject: [PATCH 165/206] allow evaluation of PARI closure objects --- src/sage/libs/pari/gen.pyx | 72 ++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 2b637cf68ae..26d45b7f5fe 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -7852,13 +7852,73 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(deriv(self.g, P.get_var(v))) - def eval(self, x): - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(poleval(self.g, t0.g)) + def eval(self, *args): + """ + Evaluate ``self`` with the given arguments. + + This is currently implemented for polynomials and objects of + type ``t_CLOSURE`` (functions in GP bytecode form). + + EXAMPLES:: + + sage: f = pari('x^2 + 1') + sage: f.type() + 't_POL' + sage: f.eval(I) + 0 + + sage: T = pari('n -> n + 2') + sage: T.type() + 't_CLOSURE' + sage: T.eval(3) + 5 - def __call__(self, x): - return self.eval(x) + """ + cdef long t = typ(self.g) + cdef gen t0 + cdef GEN result + if t == t_POL: + # evaluate univariate polynomial + if len(args) != 1: + raise TypeError("evaluating a polynomial takes exactly 1 argument") + t0 = objtogen(args[0]) + pari_catch_sig_on() + return P.new_gen(poleval(self.g, t0.g)) + elif t == t_CLOSURE: + t0 = objtogen(args) + pari_catch_sig_on() + result = closure_callgenvec(self.g, t0.g) + if result != NULL: + return P.new_gen(result) + else: + P.clear_stack() + return None + + def __call__(self, *args): + """ + Evaluate ``self`` with the given arguments. + + This has the same effect as ``self.eval(*args)``. + + TESTS:: + + sage: R. = GF(3)[] + sage: f = (x^2 + x + 1)._pari_() + sage: f.type() + 't_POL' + sage: f(2) + Mod(1, 3) + + sage: T = pari('n -> 1/n') + sage: T.type() + 't_CLOSURE' + sage: T(0) + Traceback (most recent call last): + ... + PariError: _/_: division by zero + + """ + return self.eval(*args) def factornf(self, t): """ From 7ec74e0598300f96d1c41604fb288f41db4cc9da Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 16 Dec 2013 11:22:05 +0100 Subject: [PATCH 166/206] PARI cleanup: reviewer patch --- src/sage/combinat/sloane_functions.py | 2 +- src/sage/groups/perm_gps/permgroup_element.pyx | 5 ++--- src/sage/libs/all.py | 2 +- src/sage/libs/pari/all.py | 1 - src/sage/libs/pari/gen_py.py | 2 +- src/sage/libs/pari/handle_error.pyx | 4 ++-- src/sage/libs/pari/pari_instance.pyx | 2 +- src/sage/matrix/matrix1.pyx | 2 +- src/sage/matrix/matrix2.pyx | 2 +- src/sage/matrix/matrix_rational_dense.pyx | 2 +- src/sage/misc/randstate.pyx | 2 +- src/sage/quadratic_forms/qfsolve.py | 12 ++++++------ src/sage/rings/arith.py | 2 +- src/sage/rings/contfrac.py | 2 +- src/sage/rings/factorint.pyx | 2 +- src/sage/rings/fast_arith.pyx | 2 +- src/sage/rings/laurent_series_ring.py | 2 +- src/sage/rings/number_field/galois_group.py | 2 +- src/sage/rings/number_field/maps.py | 2 +- src/sage/rings/number_field/number_field.py | 3 +-- src/sage/rings/number_field/number_field_element.pyx | 2 +- src/sage/rings/number_field/number_field_ideal.py | 4 ++-- src/sage/rings/number_field/number_field_rel.py | 4 ++-- .../rings/number_field/small_primes_of_degree_one.py | 2 +- src/sage/rings/number_field/totallyreal_rel.py | 2 +- src/sage/rings/number_field/unit_group.py | 2 +- src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx | 2 +- src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx | 2 +- src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx | 2 +- src/sage/rings/padics/padic_generic_element.pyx | 2 +- src/sage/rings/polynomial/cyclotomic.pyx | 2 +- src/sage/rings/power_series_ring_element.pyx | 2 +- src/sage/schemes/elliptic_curves/cm.py | 2 +- .../schemes/elliptic_curves/descent_two_isogeny.pyx | 2 +- src/sage/schemes/elliptic_curves/ell_point.py | 2 +- src/sage/structure/sage_object.pyx | 2 +- 36 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index 70085cf2fd9..60d8c6d4def 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -7411,7 +7411,7 @@ def _powerful_numbers_in_range(self, n, m): if n < 4: n = 4 # Use PARI directly -- much faster. - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari L = pari('v=listcreate(); for(i=%s,%s,if(vecmin(factor(i)[,2])>1,listput(v,i))); v'%(n,m)) return [ZZ(x) for x in L] # not very many, so not much overhead diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index 98f96deb862..c944df09adf 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -218,10 +218,9 @@ def standardize_generator(g, convert_dict=None): """ from sage.interfaces.gap import GapElement from sage.combinat.permutation import Permutation - from sage.libs.pari.gen import gen - from sage.combinat.permutation import Permutation + from sage.libs.pari.all import pari_gen - if isinstance(g, gen): + if isinstance(g, pari_gen): g = list(g) needs_conversion = True diff --git a/src/sage/libs/all.py b/src/sage/libs/all.py index f42aec7cb58..e924c37037c 100644 --- a/src/sage/libs/all.py +++ b/src/sage/libs/all.py @@ -1,6 +1,6 @@ import sage.libs.ntl.all as ntl -from sage.libs.pari.all import pari, pari_gen, allocatemem, PariError +from sage.libs.pari.all import pari, pari_gen, PariError from sage.libs.mwrank.all import (mwrank_EllipticCurve, mwrank_MordellWeil, mwrank_initprimes, diff --git a/src/sage/libs/pari/all.py b/src/sage/libs/pari/all.py index ce4ad8c5e9e..450bdfb7d79 100644 --- a/src/sage/libs/pari/all.py +++ b/src/sage/libs/pari/all.py @@ -1,3 +1,2 @@ from gen import gen as pari_gen, PariError from pari_instance import pari -allocatemem = pari.allocatemem diff --git a/src/sage/libs/pari/gen_py.py b/src/sage/libs/pari/gen_py.py index c08b6172bd5..0bdede5fb13 100644 --- a/src/sage/libs/pari/gen_py.py +++ b/src/sage/libs/pari/gen_py.py @@ -85,7 +85,7 @@ def pari(x): sage: type(pari("dummy = 0; kill(dummy)")) """ - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari return pari(x) def python(z, locals=None): diff --git a/src/sage/libs/pari/handle_error.pyx b/src/sage/libs/pari/handle_error.pyx index 56664c0e0c5..81472fb2404 100644 --- a/src/sage/libs/pari/handle_error.pyx +++ b/src/sage/libs/pari/handle_error.pyx @@ -79,14 +79,14 @@ cdef int _pari_handle_exception(long err) except 0: if err == errpile: # PARI is out of memory. We double the size of the PARI stack # and retry the computation. - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari pari.allocatemem(silent=True) return 0 if err == user: raise RuntimeError("PARI user exception\n%s" % pari_error_string) else: - from sage.libs.pari.gen import PariError + from sage.libs.pari.all import PariError raise PariError(err, pari_error_string) cdef void _pari_err_recover(long err): diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx index af33521696d..b68f651d49b 100644 --- a/src/sage/libs/pari/pari_instance.pyx +++ b/src/sage/libs/pari/pari_instance.pyx @@ -1284,7 +1284,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): def nth_prime(self, long n): - from sage.libs.pari.gen import PariError + from sage.libs.pari.all import PariError try: return self.__nth_prime(n) except PariError: diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index e29baa73d67..eb42a18cc9e 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -87,7 +87,7 @@ cdef class Matrix(matrix0.Matrix): sage: b[0][0].precision() # in words 3 """ - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari return pari.matrix(self._nrows, self._ncols, self._list()) def _gap_init_(self): diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 243acbefeca..24ed01195f1 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -324,7 +324,7 @@ cdef class Matrix(matrix1.Matrix): if not K.is_integral_domain(): from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing if is_IntegerModRing(K): - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari A = pari(self.lift()) b = pari([c.lift() for c in B]).Col() ret = A.matsolvemod(pari(K.cardinality()), b) diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 5f6ba284e04..9be4cfff95c 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -746,7 +746,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): else: algorithm = "iml" if algorithm == "pari": - from sage.libs.pari.gen import PariError + from sage.libs.pari.all import PariError try: return self._invert_pari() except PariError: diff --git a/src/sage/misc/randstate.pyx b/src/sage/misc/randstate.pyx index def865c694b..81e6edcfb5a 100644 --- a/src/sage/misc/randstate.pyx +++ b/src/sage/misc/randstate.pyx @@ -794,7 +794,7 @@ cdef class randstate: """ global _pari_seed_randstate if _pari_seed_randstate is not self: - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari if self._pari_saved_seed is not None: seed = self._pari_saved_seed diff --git a/src/sage/quadratic_forms/qfsolve.py b/src/sage/quadratic_forms/qfsolve.py index 937b017a93c..c0bb214a381 100644 --- a/src/sage/quadratic_forms/qfsolve.py +++ b/src/sage/quadratic_forms/qfsolve.py @@ -96,9 +96,9 @@ def qfsolve(G, factD=None): gp = _gp_for_simon() if factD is not None: raise NotImplementedError, "qfsolve not implemented with parameter factD" - ret = gp('Qfsolve(%s)' % G._pari_())._pari_() - if ret.type() == 't_COL': - return tuple([QQ(r) for r in ret]) + ret = gp('Qfsolve(%s)' % G._pari_init_()) + if str(ret.type()) == 't_COL': # Need explicit str(), see #15522 + return tuple(QQ(r) for r in ret) return ZZ(ret) def qfparam(G, sol): @@ -137,6 +137,6 @@ def qfparam(G, sol): gp = _gp_for_simon() R = QQ['t'] t = R.gen() - s = 'Qfparam(%s, (%s)~)*[t^2,t,1]~' % (G._pari_(), gp(sol)._pari_()) - ret = gp(s)._pari_() - return tuple([R(r) for r in ret]) + s = 'Qfparam((%s), (%s)~)*[t^2,t,1]~' % (G._pari_init_(), list(sol)) + ret = gp(s) + return tuple(R(str(r)) for r in ret) diff --git a/src/sage/rings/arith.py b/src/sage/rings/arith.py index 0d19143c6dc..01b95c40a2c 100644 --- a/src/sage/rings/arith.py +++ b/src/sage/rings/arith.py @@ -14,7 +14,7 @@ import math import sys import sage.misc.misc as misc -from sage.libs.pari.pari_instance import pari +from sage.libs.pari.all import pari import sage.libs.flint.arith as flint_arith from sage.rings.rational_field import QQ diff --git a/src/sage/rings/contfrac.py b/src/sage/rings/contfrac.py index 1e6124309cb..42dcfb94f74 100644 --- a/src/sage/rings/contfrac.py +++ b/src/sage/rings/contfrac.py @@ -60,7 +60,7 @@ """ from sage.structure.element import FieldElement from sage.structure.parent_gens import ParentWithGens -from sage.libs.pari.pari_instance import pari +from sage.libs.pari.all import pari from field import Field from rational_field import QQ diff --git a/src/sage/rings/factorint.pyx b/src/sage/rings/factorint.pyx index 685fc988f34..9558b6de1df 100644 --- a/src/sage/rings/factorint.pyx +++ b/src/sage/rings/factorint.pyx @@ -285,7 +285,7 @@ cpdef factor_using_pari(n, int_=False, debug_level=0, proof=None): sage: factor(-2**72 + 3, algorithm='pari') # indirect doctest -1 * 83 * 131 * 294971519 * 1472414939 """ - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari if proof is None: from sage.structure.proof.proof import get_flag diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index c0b7cf986ec..3a0e30401ae 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -50,7 +50,7 @@ cdef extern from "pari/pari.h": from sage.rings.integer_ring import ZZ from sage.libs.pari.gen cimport gen as pari_gen -from sage.libs.pari.pari_instance import pari +from sage.libs.pari.all import pari from sage.rings.integer cimport Integer cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False): diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index c3e0346eb87..7ddeee54622 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -24,7 +24,7 @@ import field from sage.structure.parent_gens import ParentWithGens -from sage.libs.pari.gen import gen as pari_gen +from sage.libs.pari.all import pari_gen from sage.categories.fields import Fields _Fields = Fields() diff --git a/src/sage/rings/number_field/galois_group.py b/src/sage/rings/number_field/galois_group.py index 5d448caec05..21268d45546 100644 --- a/src/sage/rings/number_field/galois_group.py +++ b/src/sage/rings/number_field/galois_group.py @@ -25,7 +25,7 @@ from sage.groups.perm_gps.permgroup import PermutationGroup_generic from sage.groups.perm_gps.permgroup_element import PermutationGroupElement from sage.misc.cachefunc import cached_method -from sage.libs.pari.pari_instance import pari +from sage.libs.pari.all import pari from sage.rings.infinity import infinity from sage.rings.number_field.number_field import refine_embedding from sage.rings.number_field.morphism import NumberFieldHomomorphism_im_gens diff --git a/src/sage/rings/number_field/maps.py b/src/sage/rings/number_field/maps.py index bab674ba821..1f590627bb8 100644 --- a/src/sage/rings/number_field/maps.py +++ b/src/sage/rings/number_field/maps.py @@ -40,7 +40,7 @@ import sage.rings.rational_field as rational_field -from sage.libs.pari.pari_instance import pari +from sage.libs.pari.all import pari QQ = rational_field.RationalField() diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 3f235498ee1..034ffa68ea7 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -199,8 +199,7 @@ def proof_flag(t): import number_field_element import number_field_element_quadratic from number_field_ideal import is_NumberFieldIdeal, NumberFieldFractionalIdeal -from sage.libs.pari.pari_instance import pari -from sage.libs.pari.gen import gen as pari_gen +from sage.libs.pari.all import pari, pari_gen QQ = rational_field.RationalField() ZZ = integer_ring.IntegerRing() diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 2a58f4e19bd..c600d7f94b0 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -50,7 +50,7 @@ from sage.rings.rational cimport Rational from sage.modules.free_module_element import vector -from sage.libs.pari.gen import gen as pari_gen +from sage.libs.pari.all import pari_gen from sage.structure.element cimport Element, generic_power_c from sage.structure.element import canonical_coercion, parent diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index bb453a97d06..7d4baeed46d 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -172,7 +172,7 @@ def __init__(self, field, gens, coerce=True): if len(gens) == 1 and isinstance(gens[0], (list, tuple)): gens = gens[0] - from sage.libs.pari.gen import gen as pari_gen + from sage.libs.pari.all import pari_gen if len(gens) == 1 and isinstance(gens[0], pari_gen): # Init from PARI gens = gens[0] @@ -2448,7 +2448,7 @@ def _pari_bid_(self, flag=1): sage: bid.getattr('clgp') [2, [2]] """ - from sage.libs.pari.gen import PariError + from sage.libs.pari.all import PariError try: bid = self._bid if flag==2: diff --git a/src/sage/rings/number_field/number_field_rel.py b/src/sage/rings/number_field/number_field_rel.py index f7cdb7a8f2d..9d4bfc616d8 100644 --- a/src/sage/rings/number_field/number_field_rel.py +++ b/src/sage/rings/number_field/number_field_rel.py @@ -103,7 +103,7 @@ from sage.rings.number_field.number_field_base import is_NumberField from sage.rings.number_field.order import RelativeOrder from sage.rings.number_field.morphism import RelativeNumberFieldHomomorphism_from_abs -from sage.libs.pari.gen import gen as pari_gen +from sage.libs.pari.all import pari_gen from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ @@ -1523,7 +1523,7 @@ def _gen_relative(self): sage: c^2 + 3 0 """ - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari rnfeqn = self._pari_rnfequation() f = (pari('x') - rnfeqn[2]*rnfeqn[1]).lift() g = self._element_class(self, f) diff --git a/src/sage/rings/number_field/small_primes_of_degree_one.py b/src/sage/rings/number_field/small_primes_of_degree_one.py index a572a2a8847..67d84435a0f 100644 --- a/src/sage/rings/number_field/small_primes_of_degree_one.py +++ b/src/sage/rings/number_field/small_primes_of_degree_one.py @@ -142,7 +142,7 @@ def __init__(self, field, num_integer_primes=10000, max_iterations=100): self._poly = ZZ['x'](self._poly.denominator() * self._poly()) # make integer polynomial # this uses that [ O_K : Z[a] ]^2 = | disc(f(x)) / disc(O_K) | - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari self._prod_of_small_primes = ZZ(pari('TEMPn = %s; TEMPps = primes(TEMPn); prod(X = 1, TEMPn, TEMPps[X])' % num_integer_primes)) self._prod_of_small_primes //= self._prod_of_small_primes.gcd(self._poly.discriminant()) diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py index 6e110ff7d3d..0eeb029cac0 100644 --- a/src/sage/rings/number_field/totallyreal_rel.py +++ b/src/sage/rings/number_field/totallyreal_rel.py @@ -92,7 +92,7 @@ from sage.rings.number_field.number_field import NumberField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.number_field.totallyreal import weed_fields, odlyzko_bound_totallyreal, enumerate_totallyreal_fields_prim -from sage.libs.pari.pari_instance import pari +from sage.libs.pari.all import pari import math, bisect, sys diff --git a/src/sage/rings/number_field/unit_group.py b/src/sage/rings/number_field/unit_group.py index 4b1524fbb64..7a135f5f14e 100644 --- a/src/sage/rings/number_field/unit_group.py +++ b/src/sage/rings/number_field/unit_group.py @@ -140,7 +140,7 @@ from sage.groups.abelian_gps.values import AbelianGroupWithValues_class from sage.structure.sequence import Sequence from sage.structure.proof.proof import get_flag -from sage.libs.pari.pari_instance import pari +from sage.libs.pari.all import pari from sage.misc.misc import prod from sage.rings.integer_ring import ZZ diff --git a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx index 88066f8eb0c..88a51725257 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx @@ -170,7 +170,7 @@ from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class from sage.libs.ntl.ntl_ZZ_pContext import ntl_ZZ_pContext from sage.rings.padics.padic_base_generic_element cimport pAdicBaseGenericElement from sage.rings.padics.padic_generic_element cimport pAdicGenericElement -from sage.libs.pari.gen import gen as pari_gen +from sage.libs.pari.all import pari_gen from sage.interfaces.gp import GpElement from sage.rings.finite_rings.integer_mod import is_IntegerMod from sage.rings.all import IntegerModRing diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index e7fbc0b3cd4..aba7de9d22a 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -190,7 +190,7 @@ from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class from sage.libs.ntl.ntl_ZZ_pContext import ntl_ZZ_pContext from sage.rings.padics.padic_base_generic_element cimport pAdicBaseGenericElement from sage.rings.padics.padic_generic_element cimport pAdicGenericElement -from sage.libs.pari.gen import gen as pari_gen +from sage.libs.pari.all import pari_gen from sage.interfaces.gp import GpElement from sage.rings.finite_rings.integer_mod import is_IntegerMod from sage.rings.padics.padic_ext_element cimport pAdicExtElement 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 7f67e4209ef..d3742e6a087 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -136,7 +136,7 @@ from sage.libs.ntl.ntl_ZZ_p cimport ntl_ZZ_p from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class from sage.libs.ntl.ntl_ZZ_pContext import ntl_ZZ_pContext from sage.rings.rational cimport Rational -from sage.libs.pari.gen import gen as pari_gen +from sage.libs.pari.all import pari_gen from sage.interfaces.gp import GpElement from sage.rings.finite_rings.integer_mod import is_IntegerMod from sage.rings.all import IntegerModRing diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 049e7c3924b..094e90bb119 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -1839,7 +1839,7 @@ cdef class pAdicGenericElement(LocalGenericElement): if self.valuation() is infinity: return self - from sage.libs.pari.gen import PariError + from sage.libs.pari.all import PariError try: # use pari ans = self.parent()(self._pari_().sqrt()) diff --git a/src/sage/rings/polynomial/cyclotomic.pyx b/src/sage/rings/polynomial/cyclotomic.pyx index 0e7f38b7da6..583589ba4fa 100644 --- a/src/sage/rings/polynomial/cyclotomic.pyx +++ b/src/sage/rings/polynomial/cyclotomic.pyx @@ -36,7 +36,7 @@ from sage.misc.misc import prod, subsets from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.libs.pari.gen cimport gen -from sage.libs.pari.pari_instance import pari +from sage.libs.pari.all import pari def cyclotomic_coeffs(nn, sparse=None): u""" diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index ec7b2b2276a..b8c30de89c3 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -111,7 +111,7 @@ import sage.misc.latex import rational_field, integer_ring from integer import Integer from sage.rings.finite_rings.integer_mod_ring import IntegerModRing -from sage.libs.pari.pari_instance import pari +from sage.libs.pari.all import pari from sage.misc.functional import sqrt, log from sage.rings.arith import integer_ceil as ceil diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py index 6bb5a103db4..3769453a968 100644 --- a/src/sage/schemes/elliptic_curves/cm.py +++ b/src/sage/schemes/elliptic_curves/cm.py @@ -411,7 +411,7 @@ def discriminants_with_bounded_class_number(hmax, B=None, proof=None): """ # imports that are needed only for this function from sage.structure.proof.proof import get_flag - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari import math from sage.misc.functional import round diff --git a/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx b/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx index ba0c446bae9..0c8daec2b2e 100644 --- a/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx +++ b/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx @@ -1246,7 +1246,7 @@ def two_descent_by_two_isogeny_work(Integer c, Integer d, p_list_len += 1 else: # Factor more slowly using Pari via Python. - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari d = Integer(0) mpz_set(d.value, d_mpz) primes = list(pari(d).factor()[0]) diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 801adcdf2af..6ad8e0a8701 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -2074,7 +2074,7 @@ def order(self): E = self.curve() # Special code for curves over Q, calling PARI - from sage.libs.pari.gen import PariError + from sage.libs.pari.all import PariError try: n = int(E.pari_curve().ellorder(self)) if n == 0: diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 6ae1e36ceed..463eb7cf9d4 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -741,7 +741,7 @@ cdef class SageObject: return self.__pari except AttributeError: pass - from sage.libs.pari.pari_instance import pari + from sage.libs.pari.all import pari x = pari(self._pari_init_()) if self._interface_is_cached_(): try: From 84e88aa37ee78b76f83874c78f945f03c7763d33 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 16 Dec 2013 15:49:07 +0100 Subject: [PATCH 167/206] Add precision arguments in PARI --- src/sage/libs/pari/decl.pxi | 10 +- src/sage/libs/pari/gen.pyx | 378 ++++++++++++++------------- src/sage/libs/pari/gen_py.py | 45 ++-- src/sage/libs/pari/pari_instance.pxd | 2 + src/sage/libs/pari/pari_instance.pyx | 68 +++-- 5 files changed, 272 insertions(+), 231 deletions(-) diff --git a/src/sage/libs/pari/decl.pxi b/src/sage/libs/pari/decl.pxi index 99ac368ca96..955a1edd65d 100644 --- a/src/sage/libs/pari/decl.pxi +++ b/src/sage/libs/pari/decl.pxi @@ -49,11 +49,11 @@ cdef extern from 'pari/pari.h': # parierr.h - int talker2, bugparier, alarmer, openfiler, talker, flagerr, impl, \ - archer, notfuncer, precer, typeer, consister, user, errpile, \ - overflower, matinv1, mattype1, arither1, primer1, invmoder, \ - constpoler, notpoler, redpoler, zeropoler, operi, operf, gdiver, \ - memer, negexper, sqrter5, noer + int syntaxer, bugparier, alarmer, openfiler, talker, flagerr, \ + impl, archer, notfuncer, precer, typeer, consister, user, \ + errpile, overflower, matinv1, mattype1, arither1, primer1, \ + invmoder, constpoler, notpoler, redpoler, zeropoler, operi, \ + operf, gdiver, memer, negexper, sqrter5, noer int warner, warnprec, warnfile, warnmem diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 57267d16bb0..42b81048ea1 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -66,13 +66,9 @@ cdef extern from "mpz_pylong.h": Integer = None import pari_instance -from pari_instance cimport PariInstance -cdef PariInstance P, pari -P = pari = pari_instance.pari +from pari_instance cimport PariInstance, prec_bits_to_words +cdef PariInstance P = pari_instance.pari -from pari_instance import prec_bits_to_words, prec_words_to_dec - -cdef long prec = prec_bits_to_words(53) @cython.final cdef class gen(sage.structure.element.RingElement): @@ -250,7 +246,7 @@ cdef class gen(sage.structure.element.RingElement): def __pow__(gen self, n, m): cdef gen t0 = objtogen(n) pari_catch_sig_on() - return P.new_gen(gpow(self.g, t0.g, prec)) + return P.new_gen(gpow(self.g, t0.g, prec_bits_to_words())) def __neg__(gen self): pari_catch_sig_on() @@ -826,28 +822,13 @@ cdef class gen(sage.structure.element.RingElement): ## as mentioned above return P.new_ref(gel(self.g,n+1), self) - def _gen_length(gen self): - """ - Return the length of self as a Pari object, *including* - codewords. - - EXAMPLES:: - - sage: n = pari(30) - sage: n.length() - 1 - sage: n._gen_length() - 3 - """ - return lg(self.g) - def __setitem__(gen self, n, y): r""" Set the nth entry to a reference to y. - The indexing is 0-based, like everywhere else in Python, but - *unlike* in Pari/GP. + *unlike* in PARI/GP. - Assignment sets the nth entry to a reference to y, assuming y is an object of type gen. This is the same as in Python, but @@ -929,20 +910,15 @@ cdef class gen(sage.structure.element.RingElement): """ cdef int i, j - cdef gen x + cdef gen x = objtogen(y) cdef long l cdef Py_ssize_t ii, jj, step pari_catch_sig_on() try: - if isinstance(y, gen): - x = y - else: - x = pari(y) - if isinstance(n, tuple): if typ(self.g) != t_MAT: - raise TypeError, "cannot index Pari type %s by tuple"%typ(self.g) + raise TypeError, "cannot index PARI type %s by tuple"%typ(self.g) if len(n) != 2: raise ValueError, "matrix index must be of the form [row, column]" @@ -1438,7 +1414,7 @@ cdef class gen(sage.structure.element.RingElement): ########################################### # arith1.c ########################################### - def isprime(gen self, flag=0): + def isprime(gen self, long flag=0): """ isprime(x, flag=0): Returns True if x is a PROVEN prime number, and False otherwise. @@ -1517,7 +1493,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(hclassno(n.g)) - def ispseudoprime(gen self, flag=0): + def ispseudoprime(gen self, long flag=0): """ ispseudoprime(x, flag=0): Returns True if x is a pseudo-prime number, and False otherwise. @@ -2062,7 +2038,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gtopolyrev(self.g, P.get_var(v))) - def Qfb(gen a, b, c, D=0): + def Qfb(gen a, b, c, D=0, long precision=0): """ Qfb(a,b,c,D=0.): Returns the binary quadratic form @@ -2113,7 +2089,7 @@ cdef class gen(sage.structure.element.RingElement): cdef gen t1 = objtogen(c) cdef gen t2 = objtogen(D) pari_catch_sig_on() - return P.new_gen(Qfb0(a.g, t0.g, t1.g, t2.g, prec)) + return P.new_gen(Qfb0(a.g, t0.g, t1.g, t2.g, prec_bits_to_words(precision))) def Ser(gen x, v=-1, long seriesprecision = 16): @@ -2917,7 +2893,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gconj(x.g)) - def conjvec(gen x): + def conjvec(gen x, long precision=0): """ conjvec(x): Returns the vector of all conjugates of the algebraic number x. An algebraic number is a polynomial over Q modulo an @@ -2937,9 +2913,11 @@ cdef class gen(sage.structure.element.RingElement): [-0.414213562373095, 2.41421356237310]~ sage: pari('Mod(x,x^3-3)').conjvec() [1.44224957030741, -0.721124785153704 + 1.24902476648341*I, -0.721124785153704 - 1.24902476648341*I]~ + sage: pari('Mod(1+x,x^2-2)').conjvec(precision=160)[0].sage() + -0.414213562373095048801688724209698078569671875376948073177 """ pari_catch_sig_on() - return P.new_gen(conjvec(x.g, prec)) + return P.new_gen(conjvec(x.g, prec_bits_to_words(precision))) def denominator(gen x): """ @@ -3714,7 +3692,7 @@ cdef class gen(sage.structure.element.RingElement): # Examples, docs -- William Stein ########################################### - def abs(gen x): + def abs(gen x, long precision=0): """ Returns the absolute value of x (its modulus, if x is complex). Rational functions are not allowed. Contrary to most transcendental @@ -3726,6 +3704,8 @@ cdef class gen(sage.structure.element.RingElement): sage: x = pari("-27.1") sage: x.abs() 27.1000000000000 + sage: pari('1 + I').abs(precision=128).sage() + 1.4142135623730950488016887242096980786 If x is a polynomial, returns -x if the leading coefficient is real and negative else returns x. For a power series, the constant @@ -3735,10 +3715,11 @@ cdef class gen(sage.structure.element.RingElement): sage: pari('x-1.2*x^2').abs() 1.20000000000000*x^2 - x + sage: pari('-2 + t + O(t^2)').abs() + 2 - t + O(t^2) """ pari_catch_sig_on() - # the prec parameter here has no effect - return P.new_gen(gabs(x.g, prec)) + return P.new_gen(gabs(x.g, prec_bits_to_words(precision))) def acos(gen x, long precision=0): r""" @@ -3766,7 +3747,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gacos(x.g, prec_bits_to_words(precision))) - def acosh(gen x, precision=0): + def acosh(gen x, long precision=0): r""" The principal branch of `\cosh^{-1}(x)`, so that `\Im(\mathrm{acosh}(x))` belongs to `[0,Pi]`. If @@ -3791,7 +3772,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gach(x.g, prec_bits_to_words(precision))) - def agm(gen x, y, precision=0): + def agm(gen x, y, long precision=0): r""" The arithmetic-geometric mean of x and y. In the case of complex or negative numbers, the principal square root is always chosen. @@ -3821,7 +3802,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(agm(x.g, t0.g, prec_bits_to_words(precision))) - def arg(gen x, precision=0): + def arg(gen x, long precision=0): r""" arg(x): argument of x,such that `-\pi < \arg(x) \leq \pi`. @@ -3839,7 +3820,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(garg(x.g, prec_bits_to_words(precision))) - def asin(gen x, precision=0): + def asin(gen x, long precision=0): r""" The principal branch of `\sin^{-1}(x)`, so that `\RR e(\mathrm{asin}(x))` belongs to `[-\pi/2,\pi/2]`. If @@ -3861,7 +3842,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gasin(x.g, prec_bits_to_words(precision))) - def asinh(gen x, precision=0): + def asinh(gen x, long precision=0): r""" The principal branch of `\sinh^{-1}(x)`, so that `\Im(\mathrm{asinh}(x))` belongs to `[-\pi/2,\pi/2]`. @@ -3882,7 +3863,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gash(x.g, prec_bits_to_words(precision))) - def atan(gen x, precision=0): + def atan(gen x, long precision=0): r""" The principal branch of `\tan^{-1}(x)`, so that `\RR e(\mathrm{atan}(x))` belongs to `]-\pi/2, \pi/2[`. @@ -3903,7 +3884,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gatan(x.g, prec_bits_to_words(precision))) - def atanh(gen x, precision=0): + def atanh(gen x, long precision=0): r""" The principal branch of `\tanh^{-1}(x)`, so that `\Im(\mathrm{atanh}(x))` belongs to `]-\pi/2,\pi/2]`. If @@ -3942,7 +3923,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(bernfrac(x)) - def bernreal(gen x): + def bernreal(gen x, long precision=0): r""" The Bernoulli number `B_x`, as for the function bernfrac, but `B_x` is returned as a real number (with the current @@ -3952,10 +3933,11 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(18).bernreal() 54.9711779448622 + sage: pari(18).bernreal(precision=160).sage() + 54.9711779448621553884711779448621553884711779448621553885 """ pari_catch_sig_on() - # the argument prec has no effect - return P.new_gen(bernreal(x, prec)) + return P.new_gen(bernreal(x, prec_bits_to_words(precision))) def bernvec(gen x): r""" @@ -3978,7 +3960,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(bernvec(x)) - def besselh1(gen nu, x, precision=0): + def besselh1(gen nu, x, long precision=0): r""" The `H^1`-Bessel function of index `\nu` and argument `x`. @@ -3998,7 +3980,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(hbessel1(nu.g, t0.g, prec_bits_to_words(precision))) - def besselh2(gen nu, x, precision=0): + def besselh2(gen nu, x, long precision=0): r""" The `H^2`-Bessel function of index `\nu` and argument `x`. @@ -4018,7 +4000,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(hbessel2(nu.g, t0.g, prec_bits_to_words(precision))) - def besselj(gen nu, x, precision=0): + def besselj(gen nu, x, long precision=0): r""" Bessel J function (Bessel function of the first kind), with index `\nu` and argument `x`. If `x` converts to @@ -4041,7 +4023,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(jbessel(nu.g, t0.g, prec_bits_to_words(precision))) - def besseljh(gen nu, x, precision=0): + def besseljh(gen nu, x, long precision=0): """ J-Bessel function of half integral index (Spherical Bessel function of the first kind). More precisely, besseljh(n,x) computes @@ -4066,7 +4048,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(jbesselh(nu.g, t0.g, prec_bits_to_words(precision))) - def besseli(gen nu, x, precision=0): + def besseli(gen nu, x, long precision=0): r""" Bessel I function (Bessel function of the second kind), with index `\nu` and argument `x`. If `x` converts to @@ -4092,7 +4074,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ibessel(nu.g, t0.g, prec_bits_to_words(precision))) - def besselk(gen nu, x, long flag=0, precision=0): + def besselk(gen nu, x, long flag=0, long precision=0): """ nu.besselk(x, flag=0): K-Bessel function (modified Bessel function of the second kind) of index nu, which can be complex, and argument @@ -4135,7 +4117,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(kbessel(nu.g, t0.g, prec_bits_to_words(precision))) - def besseln(gen nu, x, precision=0): + def besseln(gen nu, x, long precision=0): """ nu.besseln(x): Bessel N function (Spherical Bessel function of the second kind) of index nu and argument x. @@ -4156,7 +4138,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(nbessel(nu.g, t0.g, prec_bits_to_words(precision))) - def cos(gen x, precision=0): + def cos(gen x, long precision=0): """ The cosine function. @@ -4178,7 +4160,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gcos(x.g, prec_bits_to_words(precision))) - def cosh(gen x, precision=0): + def cosh(gen x, long precision=0): """ The hyperbolic cosine function. @@ -4200,7 +4182,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gch(x.g, prec_bits_to_words(precision))) - def cotan(gen x, precision=0): + def cotan(gen x, long precision=0): """ The cotangent of x. @@ -4227,7 +4209,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gcotan(x.g, prec_bits_to_words(precision))) - def dilog(gen x, precision=0): + def dilog(gen x, long precision=0): r""" The principal branch of the dilogarithm of `x`, i.e. the analytic continuation of the power series @@ -4249,7 +4231,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(dilog(x.g, prec_bits_to_words(precision))) - def eint1(gen x, long n=0, precision=0): + def eint1(gen x, long n=0, long precision=0): r""" x.eint1(n): exponential integral E1(x): @@ -4279,7 +4261,7 @@ cdef class gen(sage.structure.element.RingElement): else: return P.new_gen(veceint1(x.g, stoi(n), prec_bits_to_words(precision))) - def erfc(gen x, precision=0): + def erfc(gen x, long precision=0): r""" Return the complementary error function: @@ -4302,7 +4284,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gerfc(x.g, prec_bits_to_words(precision))) - def eta(gen x, flag=0, precision=0): + def eta(gen x, long flag=0, long precision=0): r""" x.eta(flag=0): if flag=0, `\eta` function without the `q^{1/24}`; otherwise `\eta` of the complex number @@ -4332,7 +4314,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(trueeta(x.g, prec_bits_to_words(precision))) return P.new_gen(eta(x.g, prec_bits_to_words(precision))) - def exp(gen self, precision=0): + def exp(gen self, long precision=0): """ x.exp(): exponential of x. @@ -4353,7 +4335,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gexp(self.g, prec_bits_to_words(precision))) - def gamma(gen s, precision=0): + def gamma(gen s, long precision=0): """ s.gamma(precision): Gamma function at s. @@ -4382,7 +4364,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ggamma(s.g, prec_bits_to_words(precision))) - def gammah(gen s, precision=0): + def gammah(gen s, long precision=0): """ s.gammah(): Gamma function evaluated at the argument x+1/2. @@ -4404,7 +4386,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ggamd(s.g, prec_bits_to_words(precision))) - def hyperu(gen a, b, x, precision=0): + def hyperu(gen a, b, x, long precision=0): r""" a.hyperu(b,x): U-confluent hypergeometric function. @@ -4424,7 +4406,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(hyperu(a.g, t0.g, t1.g, prec_bits_to_words(precision))) - def incgam(gen s, x, y=None, precision=0): + def incgam(gen s, x, y=None, long precision=0): r""" s.incgam(x, y, precision): incomplete gamma function. y is optional and is the precomputed value of gamma(s). @@ -4450,7 +4432,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(incgam0(s.g, t0.g, t1.g, prec_bits_to_words(precision))) - def incgamc(gen s, x, precision=0): + def incgamc(gen s, x, long precision=0): r""" s.incgamc(x): complementary incomplete gamma function. @@ -4476,7 +4458,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(incgamc(s.g, t0.g, prec_bits_to_words(precision))) - def log(gen x, precision=0): + def log(gen x, long precision=0): r""" x.log(): natural logarithm of x. @@ -4519,7 +4501,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(glog(x.g, prec_bits_to_words(precision))) - def lngamma(gen x, precision=0): + def lngamma(gen x, long precision=0): r""" This method is deprecated, please use :meth:`.log_gamma` instead. @@ -4536,7 +4518,7 @@ cdef class gen(sage.structure.element.RingElement): deprecation(6992, "The method lngamma() is deprecated. Use log_gamma() instead.") return x.log_gamma(precision) - def log_gamma(gen x, precision=0): + def log_gamma(gen x, long precision=0): r""" Logarithm of the gamma function of x. @@ -4562,7 +4544,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(glngamma(x.g, prec_bits_to_words(precision))) - def polylog(gen x, long m, flag=0, precision=0): + def polylog(gen x, long m, long flag=0, long precision=0): """ x.polylog(m,flag=0): m-th polylogarithm of x. flag is optional, and can be 0: default, 1: D_m -modified m-th polylog of x, 2: @@ -4590,7 +4572,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(polylog0(m, x.g, flag, prec_bits_to_words(precision))) - def psi(gen x, precision=0): + def psi(gen x, long precision=0): r""" x.psi(): psi-function at x. @@ -4610,7 +4592,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gpsi(x.g, prec_bits_to_words(precision))) - def sin(gen x, precision=0): + def sin(gen x, long precision=0): """ x.sin(): The sine of x. @@ -4630,7 +4612,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gsin(x.g, prec_bits_to_words(precision))) - def sinh(gen x, precision=0): + def sinh(gen x, long precision=0): """ The hyperbolic sine function. @@ -4677,7 +4659,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(gsqr(x.g)) - def sqrt(gen x, precision=0): + def sqrt(gen x, long precision=0): """ x.sqrt(precision): The square root of x. @@ -4694,7 +4676,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gsqrt(x.g, prec_bits_to_words(precision))) - def sqrtn(gen x, n, precision=0): + def sqrtn(gen x, n, long precision=0): r""" x.sqrtn(n): return the principal branch of the n-th root of x, i.e., the one such that @@ -4754,7 +4736,7 @@ cdef class gen(sage.structure.element.RingElement): ans = P.new_gen_noclear(gsqrtn(x.g, t0.g, &zetan, prec_bits_to_words(precision))) return ans, P.new_gen(zetan) - def tan(gen x, precision=0): + def tan(gen x, long precision=0): """ x.tan() - tangent of x @@ -4774,7 +4756,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gtan(x.g, prec_bits_to_words(precision))) - def tanh(gen x, precision=0): + def tanh(gen x, long precision=0): """ x.tanh() - hyperbolic tangent of x @@ -4814,7 +4796,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(teich(x.g)) - def theta(gen q, z, precision=0): + def theta(gen q, z, long precision=0): """ q.theta(z): Jacobi sine theta-function. @@ -4833,7 +4815,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(theta(q.g, t0.g, prec_bits_to_words(precision))) - def thetanullk(gen q, long k, precision=0): + def thetanullk(gen q, long k, long precision=0): """ q.thetanullk(k): return the k-th derivative at z=0 of theta(q,z). @@ -4850,7 +4832,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(thetanullk(q.g, k, prec_bits_to_words(precision))) - def weber(gen x, flag=0, precision=0): + def weber(gen x, long flag=0, long precision=0): r""" x.weber(flag=0): One of Weber's f functions of x. flag is optional, and can be 0: default, function @@ -4881,7 +4863,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(weber0(x.g, flag, prec_bits_to_words(precision))) - def zeta(gen s, precision=0): + def zeta(gen s, long precision=0): """ zeta(s): zeta function at s with s a complex or a p-adic number. @@ -5205,9 +5187,9 @@ cdef class gen(sage.structure.element.RingElement): # 5: Elliptic curve functions ################################################## - def ellinit(self, int flag=0, precision=0): + def ellinit(self, long flag=0, long precision=0): """ - Return the Pari elliptic curve object with Weierstrass coefficients + Return the PARI elliptic curve object with Weierstrass coefficients given by self, a list with 5 elements. INPUT: @@ -5215,29 +5197,30 @@ cdef class gen(sage.structure.element.RingElement): - ``self`` - a list of 5 coefficients - - ``flag (optional, default: 0)`` - if 0, ask for a - Pari ell structure with 19 components; if 1, ask for a Pari sell - structure with only the first 13 components + - ``flag (optional, default: 0)`` - if 0, ask for a PARI ell + structure with 19 components; if 1, ask for a shorted PARI + sell structure with only the first 13 components. - ``precision (optional, default: 0)`` - the real precision to be used in the computation of the components of the - Pari (s)ell structure; if 0, use the default 53 bits. + PARI (s)ell structure; if 0, use the default 64 bits. .. note:: - the parameter precision in :meth:`.ellinit` controls not only - the real precision of the resulting (s)ell structure, - but also the precision of most subsequent computations - with this elliptic curve. You should therefore set it - from the start to the value you require. - + The parameter ``precision`` in ``ellinit`` controls not + only the real precision of the resulting (s)ell structure, + but in some cases also the precision of most subsequent + computations with this elliptic curve (if those rely on + the precomputations done by ``ellinit``). You should + therefore set the precision from the start to the value + you require. OUTPUT: - - ``gen`` - either a Pari ell structure with 19 - components (if flag=0), or a Pari sell structure with 13 components - (if flag=1) + - ``gen`` - either a PARI ell structure with 19 components + (if flag=0), or a PARI sell structure with 13 components + (if flag=1). EXAMPLES: An elliptic curve with integer coefficients:: @@ -5258,12 +5241,12 @@ cdef class gen(sage.structure.element.RingElement): sage: R(e[14]) 3.3715007096251920857424073155981539790016018 - Using flag=1 returns a short elliptic curve Pari object:: + Using flag=1 returns a short elliptic curve PARI object:: sage: pari([0,1,0,1,0]).ellinit(flag=1) [0, 1, 0, 1, 0, 4, 2, 0, -1, -32, 224, -48, 2048/3] - The coefficients can be any ring elements that convert to Pari:: + The coefficients can be any ring elements that convert to PARI:: sage: pari([0,1/2,0,-3/4,0]).ellinit(flag=1) [0, 1/2, 0, -3/4, 0, 2, -3/2, 0, -9/16, 40, -116, 117/4, 256000/117] @@ -5423,7 +5406,7 @@ cdef class gen(sage.structure.element.RingElement): if python_ints: g = anell(self.g, n) v = [gtolong( g[i+1]) for i in range(glength(g))] - (pari).clear_stack() + P.clear_stack() return v else: return P.new_gen(anell(self.g, n)) @@ -5554,7 +5537,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(g) - def ellbil(self, z0, z1): + def ellbil(self, z0, z1, long precision=0): """ e.ellbil(z0, z1): return the value of the canonical bilinear form on z0 and z1. @@ -5577,7 +5560,7 @@ cdef class gen(sage.structure.element.RingElement): cdef gen t0 = objtogen(z0) cdef gen t1 = objtogen(z1) pari_catch_sig_on() - return P.new_gen(bilhell(self.g, t0.g, t1.g, prec)) + return P.new_gen(bilhell(self.g, t0.g, t1.g, prec_bits_to_words(precision))) def ellchangecurve(self, ch): """ @@ -5610,7 +5593,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ellchangecurve(self.g, t0.g)) - def elleta(self): + def elleta(self, long precision=0): """ e.elleta(): return the vector [eta1,eta2] of quasi-periods associated with the period lattice e.omega() of the elliptic curve @@ -5627,12 +5610,15 @@ cdef class gen(sage.structure.element.RingElement): 6.28318530717959*I sage: w1*eta2-w2*eta1 == pari(2*pi*I) True + sage: pari([0,0,0,-82,0]).ellinit(flag=1).elleta() + Traceback (most recent call last): + ... + PariError: incorrect type in elleta """ pari_catch_sig_on() - # the prec argument has no effect - return P.new_gen(elleta(self.g, prec)) + return P.new_gen(elleta(self.g, prec_bits_to_words(precision))) - def ellheight(self, a, flag=2, precision=0): + def ellheight(self, a, long flag=2, long precision=0): """ e.ellheight(a, flag=2): return the global Neron-Tate height of the point a on the elliptic curve e. @@ -5660,11 +5646,6 @@ cdef class gen(sage.structure.element.RingElement): - ``precision (optional)`` - the precision of the result, in bits. - - Note that in order to achieve the desired precision, the - elliptic curve must have been created using ellinit with the - desired precision. - EXAMPLES:: sage: e = pari([0,1,1,-2,0]).ellinit().ellminimalmodel()[0] @@ -5674,12 +5655,14 @@ cdef class gen(sage.structure.element.RingElement): 0.476711659343740 sage: e.ellheight([1,0], flag=1) 0.476711659343740 + sage: e.ellheight([1,0], precision=128).sage() + 0.47671165934373953737948605888465305932 """ cdef gen t0 = objtogen(a) pari_catch_sig_on() return P.new_gen(ellheight0(self.g, t0.g, flag, prec_bits_to_words(precision))) - def ellheightmatrix(self, x): + def ellheightmatrix(self, x, long precision=0): """ e.ellheightmatrix(x): return the height matrix for the vector x of points on the elliptic curve e. @@ -5702,10 +5685,17 @@ cdef class gen(sage.structure.element.RingElement): sage: e = pari([0,1,1,-2,0]).ellinit().ellminimalmodel()[0] sage: e.ellheightmatrix([[1,0], [-1,1]]) [0.476711659343740, 0.418188984498861; 0.418188984498861, 0.686667083305587] + + It is allowed to call :meth:`ellinit` with ``flag=1``:: + + sage: E = pari([0,1,1,-2,0]).ellinit(flag=1) + sage: E.ellheightmatrix([[1,0], [-1,1]], precision=128).sage() + [0.47671165934373953737948605888465305932 0.41818898449886058562988945821587638244] + [0.41818898449886058562988945821587638244 0.68666708330558658572355210295409678904] """ cdef gen t0 = objtogen(x) pari_catch_sig_on() - return P.new_gen(mathell(self.g, t0.g, prec)) + return P.new_gen(mathell(self.g, t0.g, prec_bits_to_words(precision))) def ellisoncurve(self, x): """ @@ -5890,7 +5880,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(elllocalred(self.g, t0.g)) - def elllseries(self, s, A=1): + def elllseries(self, s, A=1, long precision=0): """ e.elllseries(s, A=1): return the value of the `L`-series of the elliptic curve e at the complex number s. @@ -5915,23 +5905,19 @@ cdef class gen(sage.structure.element.RingElement): sage: e = pari([0,1,1,-2,0]).ellinit() sage: e.elllseries(2.1) 0.402838047956645 - sage: e.elllseries(1) # random, close to 0 - 1.822829333527862 E-19 + sage: e.elllseries(1) + -5.24750372566629 E-19 + sage: e.elllseries(1, precision=256) + 3.00282377034977 E-77 sage: e.elllseries(-2) 0 - - The following example differs for the last digit on 32 vs. 64 bit - systems - - :: - sage: e.elllseries(2.1, A=1.1) 0.402838047956645 """ cdef gen t0 = objtogen(s) cdef gen t1 = objtogen(A) pari_catch_sig_on() - return P.new_gen(elllseries(self.g, t0.g, t1.g, prec)) + return P.new_gen(elllseries(self.g, t0.g, t1.g, prec_bits_to_words(precision))) def ellminimalmodel(self): """ @@ -6002,7 +5988,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(orderell(self.g, t0.g)) - def ellordinate(self, x): + def ellordinate(self, x, long precision=0): """ e.ellordinate(x): return the `y`-coordinates of the points on the elliptic curve e having x as `x`-coordinate. @@ -6021,17 +6007,28 @@ cdef class gen(sage.structure.element.RingElement): sage: e = pari([0,1,1,-2,0]).ellinit() sage: e.ellordinate(0) [0, -1] - sage: C. = ComplexField() - sage: e.ellordinate(i) + sage: e.ellordinate(I) [0.582203589721741 - 1.38606082464177*I, -1.58220358972174 + 1.38606082464177*I] + sage: e.ellordinate(I, precision=128)[0].sage() + 0.58220358972174117723338947874993600727 - 1.3860608246417697185311834209833653345*I sage: e.ellordinate(1+3*5^1+O(5^3)) [4*5 + 5^2 + O(5^3), 4 + 3*5^2 + O(5^3)] sage: e.ellordinate('z+2*z^2+O(z^4)') [-2*z - 7*z^2 - 23*z^3 + O(z^4), -1 + 2*z + 7*z^2 + 23*z^3 + O(z^4)] + + The field in which PARI looks for the point depends on the + input field:: + + sage: e.ellordinate(5) + [] + sage: e.ellordinate(5.0) + [11.3427192823270, -12.3427192823270] + sage: e.ellordinate(RR(-3)) + [-1/2 + 3.42782730020052*I, -1/2 - 3.42782730020052*I] """ cdef gen t0 = objtogen(x) pari_catch_sig_on() - return P.new_gen(ellordinate(self.g, t0.g, prec)) + return P.new_gen(ellordinate(self.g, t0.g, prec_bits_to_words(precision))) def ellpointtoz(self, pt, long precision=0): """ @@ -6159,7 +6156,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_off() return rootno - def ellsigma(self, z, flag=0): + def ellsigma(self, z, long flag=0, long precision=0): """ e.ellsigma(z, flag=0): return the value at the complex point z of the Weierstrass `\sigma` function associated to the @@ -6174,7 +6171,7 @@ cdef class gen(sage.structure.element.RingElement): """ cdef gen t0 = objtogen(z) pari_catch_sig_on() - return P.new_gen(ellsigma(self.g, t0.g, flag, prec)) + return P.new_gen(ellsigma(self.g, t0.g, flag, prec_bits_to_words(precision))) def ellsub(self, z0, z1): """ @@ -6207,7 +6204,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(taniyama(self.g)) - def elltors(self, flag=0): + def elltors(self, long flag=0): """ e.elltors(flag = 0): return information about the torsion subgroup of the elliptic curve e @@ -6252,7 +6249,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(elltors0(self.g, flag)) - def ellzeta(self, z): + def ellzeta(self, z, long precision=0): """ e.ellzeta(z): return the value at the complex point z of the Weierstrass `\zeta` function associated with the elliptic @@ -6284,9 +6281,9 @@ cdef class gen(sage.structure.element.RingElement): """ cdef gen t0 = objtogen(z) pari_catch_sig_on() - return P.new_gen(ellzeta(self.g, t0.g, prec)) + return P.new_gen(ellzeta(self.g, t0.g, prec_bits_to_words(precision))) - def ellztopoint(self, z): + def ellztopoint(self, z, long precision=0): """ e.ellztopoint(z): return the point on the elliptic curve e corresponding to the complex number z, under the usual complex @@ -6307,8 +6304,7 @@ cdef class gen(sage.structure.element.RingElement): sage: e = pari([0,0,0,1,0]).ellinit() sage: C. = ComplexField() sage: e.ellztopoint(1+i) - [0.E-19 - 1.02152286795670*I, -0.149072813701096 - 0.149072813701096*I] # 32-bit - [... - 1.02152286795670*I, -0.149072813701096 - 0.149072813701096*I] # 64-bit + [7.96075508054992 E-21 - 1.02152286795670*I, -0.149072813701096 - 0.149072813701096*I] Complex numbers belonging to the period lattice of e are of course sent to the point at infinity on e:: @@ -6317,12 +6313,8 @@ cdef class gen(sage.structure.element.RingElement): [0] """ cdef gen t0 = objtogen(z) - try: - dprec = prec_words_to_dec(z.precision()) - except AttributeError: - dprec = prec pari_catch_sig_on() - return P.new_gen(pointell(self.g, t0.g, dprec)) + return P.new_gen(pointell(self.g, t0.g, prec_bits_to_words(precision))) def omega(self): """ @@ -6365,13 +6357,27 @@ cdef class gen(sage.structure.element.RingElement): """ return self[12] - def ellj(self): - try: - dprec = prec_words_to_dec(self.precision()) - except AttributeError: - dprec = prec + def ellj(self, long precision=0): + """ + Elliptic `j`-invariant of ``self``. + + EXAMPLES:: + + sage: pari(I).ellj() + 1728.00000000000 + sage: pari(3*I).ellj() + 153553679.396729 + sage: pari('quadgen(-3)').ellj() + 0.E-54 + sage: pari('quadgen(-7)').ellj(precision=256).sage() + -3375.000000000000000000000000000000000000000000000000000000000000000000000000 + sage: pari(-I).ellj() + Traceback (most recent call last): + ... + PariError: argument '-I' does not belong to upper half-plane + """ pari_catch_sig_on() - return P.new_gen(jell(self.g, dprec)) + return P.new_gen(jell(self.g, prec_bits_to_words(precision))) ########################################### @@ -6398,15 +6404,15 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_off() return n - def bnfinit(self, long flag=0, tech=None): + def bnfinit(self, long flag=0, tech=None, long precision=0): cdef gen t0 if tech is None: pari_catch_sig_on() - return P.new_gen(bnfinit0(self.g, flag, NULL, prec)) + return P.new_gen(bnfinit0(self.g, flag, NULL, prec_bits_to_words(precision))) else: t0 = objtogen(tech) pari_catch_sig_on() - return P.new_gen(bnfinit0(self.g, flag, t0.g, prec)) + return P.new_gen(bnfinit0(self.g, flag, t0.g, prec_bits_to_words(precision))) def bnfisintnorm(self, x): cdef gen t0 = objtogen(x) @@ -7486,12 +7492,12 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(reduceddiscsmith(self.g)) - def polgalois(self): + def polgalois(self, long precision=0): """ f.polgalois(): Galois group of the polynomial f """ pari_catch_sig_on() - return P.new_gen(polgalois(self.g, prec)) + return P.new_gen(polgalois(self.g, prec_bits_to_words(precision))) def nfgaloisconj(self, long flag=0, denom=None, long precision=0): r""" @@ -7580,7 +7586,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(polrecip(self.g)) - def polred(self, flag=0, fa=None): + def polred(self, long flag=0, fa=None): cdef gen t0 if fa is None: pari_catch_sig_on() @@ -7590,20 +7596,20 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(polred0(self.g, flag, t0.g)) - def polredabs(self, flag=0): + def polredabs(self, long flag=0): pari_catch_sig_on() return P.new_gen(polredabs0(self.g, flag)) - def polredbest(self, flag=0): + def polredbest(self, long flag=0): pari_catch_sig_on() return P.new_gen(polredbest(self.g, flag)) - def polresultant(self, y, var=-1, flag=0): + def polresultant(self, y, var=-1, long flag=0): cdef gen t0 = objtogen(y) pari_catch_sig_on() return P.new_gen(polresultant0(self.g, t0.g, P.get_var(var), flag)) - def polroots(self, flag=0, precision=0): + def polroots(self, long flag=0, long precision=0): """ polroots(x,flag=0): complex roots of the polynomial x. flag is optional, and can be 0: default, uses Schonhage's method modified @@ -7612,7 +7618,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(roots0(self.g, flag, prec_bits_to_words(precision))) - def polrootsmod(self, p, flag=0): + def polrootsmod(self, p, long flag=0): cdef gen t0 = objtogen(p) pari_catch_sig_on() return P.new_gen(rootmod0(self.g, t0.g, flag)) @@ -7680,9 +7686,9 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(recip(self.g)) - def thueinit(self, flag=0): + def thueinit(self, long flag=0, long precision=0): pari_catch_sig_on() - return P.new_gen(thueinit(self.g, flag, prec)) + return P.new_gen(thueinit(self.g, flag, prec_bits_to_words(precision))) def rnfisnorminit(self, polrel, long flag=2): @@ -8007,7 +8013,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gtrace(self.g)) - def mathnf(self, flag=0): + def mathnf(self, long flag=0): """ A.mathnf(flag=0): (upper triangular) Hermite normal form of A, basis for the lattice formed by the columns of A. @@ -8096,7 +8102,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(hnfmodid(self.g, t0.g)) - def matsnf(self, flag=0): + def matsnf(self, long flag=0): """ x.matsnf(flag=0): Smith normal form (i.e. elementary divisors) of the matrix x, expressed as a vector d. Binary digits of flag mean @@ -8113,7 +8119,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(matsnf0(self.g, flag)) - def matfrobenius(self, flag=0): + def matfrobenius(self, long flag=0): r""" M.matfrobenius(flag=0): Return the Frobenius form of the square matrix M. If flag is 1, return only the elementary divisors (a list @@ -8428,7 +8434,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(thue(self.g, t0.g, t1.g)) - def charpoly(self, var=-1, flag=0): + def charpoly(self, var=-1, long flag=0): """ charpoly(A,v=x,flag=0): det(v\*Id-A) = characteristic polynomial of A using the comatrix. flag is optional and may be set to 1 (use @@ -8447,12 +8453,12 @@ cdef class gen(sage.structure.element.RingElement): def type(gen self): """ - Return the Pari type of self as a string. + Return the PARI type of self as a string. .. note:: In Cython, it is much faster to simply use typ(self.g) for - checking Pari types. + checking PARI types. EXAMPLES:: @@ -8473,7 +8479,7 @@ cdef class gen(sage.structure.element.RingElement): # machines, and errors about freeing non-aligned # pointers on others. So we settle for the following # fast but ugly code. Note that should the list of - # valid Pari types ever be updated, this code would + # valid PARI types ever be updated, this code would # need to be updated accordingly. # cdef long t = typ(self.g) @@ -8500,7 +8506,7 @@ cdef class gen(sage.structure.element.RingElement): elif t == t_VECSMALL: return 't_VECSMALL' elif t == t_CLOSURE: return 't_CLOSURE' else: - raise TypeError, "Unknown Pari type: %s"%t + raise TypeError, "Unknown PARI type: %s"%t def polinterpolate(self, ya, x): @@ -8541,7 +8547,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(concat(self.g, t0.g)) - def lindep(self, flag=0): + def lindep(self, long flag=0): pari_catch_sig_on() return P.new_gen(lindep0(self.g, flag)) @@ -8557,7 +8563,7 @@ cdef class gen(sage.structure.element.RingElement): - def elleisnum(self, long k, int flag=0): + def elleisnum(self, long k, long flag=0, long precision=0): """ om.elleisnum(k, flag=0): om=[om1,om2] being a 2-component vector giving a basis of a lattice L and k an even positive integer, @@ -8596,10 +8602,9 @@ cdef class gen(sage.structure.element.RingElement): 2.15314248576078 E50 """ pari_catch_sig_on() - # the argument prec has no effect - return P.new_gen(elleisnum(self.g, k, flag, prec)) + return P.new_gen(elleisnum(self.g, k, flag, prec_bits_to_words(precision))) - def ellwp(gen self, z='z', long n=20, long flag=0): + def ellwp(gen self, z='z', long n=20, long flag=0, long precision=0): """ Return the value or the series expansion of the Weierstrass `P`-function at `z` on the lattice `self` (or the lattice @@ -8666,13 +8671,7 @@ cdef class gen(sage.structure.element.RingElement): """ cdef gen t0 = objtogen(z) pari_catch_sig_on() - cdef long dprec - dprec = gprecision(t0.g) - if dprec: - dprec = prec_words_to_dec(dprec) - else: - dprec = prec - return P.new_gen(ellwp0(self.g, t0.g, flag, n+2, dprec)) + return P.new_gen(ellwp0(self.g, t0.g, flag, n+2, prec_bits_to_words(precision))) def ellchangepoint(self, y): """ @@ -8892,7 +8891,9 @@ class PariError(RuntimeError): Return a suitable message for displaying this exception. This is the last line of ``self.errtext()``, with the leading - ``" *** "`` and trailing period (if any) removed. + ``" *** "`` and trailing period (if any) removed. An + exception is syntax errors, where the "syntax error" line is + shown. EXAMPLES:: @@ -8901,8 +8902,19 @@ class PariError(RuntimeError): ....: except PariError as err: ....: print err _/_: division by zero + + A syntax error: + + sage: pari('!@#$%^&*()') + Traceback (most recent call last): + ... + PariError: syntax error, unexpected $undefined: !@#$%^&*() """ lines = self.errtext().split('\n') + if self.errnum() == syntaxer: + for line in lines: + if "syntax error" in line: + return line.lstrip(" *").rstrip(" .") return lines[-1].lstrip(" *").rstrip(" .") @@ -8933,7 +8945,7 @@ cdef _factor_int_when_pari_factor_failed(x, failed_factorization): w.append((F[0][j], F[1][j])) else: w.append((p, e)) - m = pari.matrix(len(w), 2) + m = P.matrix(len(w), 2) for i in range(len(w)): m[i,0] = w[i][0] m[i,1] = w[i][1] diff --git a/src/sage/libs/pari/gen_py.py b/src/sage/libs/pari/gen_py.py index c08b6172bd5..778e00cc698 100644 --- a/src/sage/libs/pari/gen_py.py +++ b/src/sage/libs/pari/gen_py.py @@ -26,31 +26,44 @@ def pari(x): sage: a = pari(1/2); a, a.type() (1/2, 't_FRAC') - Conversion from reals uses the real's own precision, here 53 bits (the default):: + Conversion from reals uses the real's own precision:: sage: a = pari(1.2); a, a.type(), a.precision() (1.20000000000000, 't_REAL', 4) # 32-bit (1.20000000000000, 't_REAL', 3) # 64-bit - Conversion from strings uses the current pari real precision. By - default this is 4 words, 38 digits, 128 bits on 64-bit machines - and 5 words, 19 digits, 64 bits on 32-bit machines. :: + Conversion from strings uses the current PARI real precision. + By default, this is 96 bits on 32-bit systems and 128 bits on + 64-bit systems:: sage: a = pari('1.2'); a, a.type(), a.precision() (1.20000000000000, 't_REAL', 5) # 32-bit (1.20000000000000, 't_REAL', 4) # 64-bit - Conversion from matrices is supported, but not from vectors; use - lists instead:: + But we can change this precision:: + + sage: pari.set_real_precision(35) # precision in decimal digits + 15 + sage: a = pari('1.2'); a, a.type(), a.precision() + (1.2000000000000000000000000000000000, 't_REAL', 6) # 32-bit + (1.2000000000000000000000000000000000, 't_REAL', 4) # 64-bit + + Set the precision to 15 digits for the remaining tests:: + + sage: pari.set_real_precision(15) + 35 + + Conversion from matrices is supported, but not from vectors or tuples; + use lists instead:: sage: a = pari(matrix(2,3,[1,2,3,4,5,6])); a, a.type() ([1, 2, 3; 4, 5, 6], 't_MAT') sage: v = vector([1.2,3.4,5.6]) - sage: v.pari() + sage: pari(v) Traceback (most recent call last): ... - AttributeError: 'sage.modules.free_module_element.FreeModuleElement_generic_dense' object has no attribute 'pari' + PariError: syntax error, unexpected ')', expecting )-> or ',': ...00000000,5.60000000000000) sage: b = pari(list(v)); b,b.type() ([1.20000000000000, 3.40000000000000, 5.60000000000000], 't_VEC') @@ -90,7 +103,7 @@ def pari(x): def python(z, locals=None): """ - Return the closest python/Sage equivalent of the given pari object. + Return the closest Python/Sage equivalent of the given pari object. INPUT: @@ -127,11 +140,9 @@ def python(z, locals=None): Rational Field sage: a = pari('1.234').python(); a - 1.234000000000000000000000000 # 32-bit - 1.2340000000000000000000000000000000000 # 64-bit + 1.23400000000000000 sage: a.parent() - Real Field with 96 bits of precision # 32-bit - Real Field with 128 bits of precision # 64-bit + Real Field with 64 bits of precision sage: a = pari('(3+I)/2').python(); a 1/2*i + 3/2 @@ -140,17 +151,15 @@ def python(z, locals=None): Conversion of complex numbers: the next example is converting from an element of the Symbolic Ring, which goes via the string - representation and hence the precision is architecture-dependent:: + representation:: sage: I = SR(I) sage: a = pari(1.0+2.0*I).python(); a - 1.000000000000000000000000000 + 2.000000000000000000000000000*I # 32-bit - 1.0000000000000000000000000000000000000 + 2.0000000000000000000000000000000000000*I # 64-bit + 1.00000000000000000 + 2.00000000000000000*I sage: type(a) sage: a.parent() - Complex Field with 96 bits of precision # 32-bit - Complex Field with 128 bits of precision # 64-bit + Complex Field with 64 bits of precision For architecture-independent complex numbers, start from a suitable ComplexField:: diff --git a/src/sage/libs/pari/pari_instance.pxd b/src/sage/libs/pari/pari_instance.pxd index 083f7f06fcb..3b2e0aa25e6 100644 --- a/src/sage/libs/pari/pari_instance.pxd +++ b/src/sage/libs/pari/pari_instance.pxd @@ -5,6 +5,8 @@ cimport cython from sage.libs.pari.gen cimport gen +cpdef prec_bits_to_words(long prec_in_bits=*) + @cython.final cdef class PariInstance(sage.structure.parent_base.ParentWithBase): cdef gen PARI_ZERO, PARI_ONE, PARI_TWO diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx index cd6b6e33452..7a559855002 100644 --- a/src/sage/libs/pari/pari_instance.pyx +++ b/src/sage/libs/pari/pari_instance.pyx @@ -200,7 +200,7 @@ cdef unsigned long prec # conversions between various real precision models ################################################################# -def prec_bits_to_dec(int prec_in_bits): +def prec_bits_to_dec(long prec_in_bits): r""" Convert from precision expressed in bits to precision expressed in decimal. @@ -223,7 +223,7 @@ def prec_bits_to_dec(int prec_in_bits): log_2 = 0.301029995663981 return int(prec_in_bits*log_2) -def prec_dec_to_bits(int prec_in_dec): +def prec_dec_to_bits(long prec_in_dec): r""" Convert from precision expressed in decimal to precision expressed in bits. @@ -247,7 +247,7 @@ def prec_dec_to_bits(int prec_in_dec): log_10 = 3.32192809488736 return int(prec_in_dec*log_10) -def prec_bits_to_words(int prec_in_bits=0): +cpdef prec_bits_to_words(long prec_in_bits=0): r""" Convert from precision expressed in bits to pari real precision expressed in words. Note: this rounds up to the nearest word, @@ -269,15 +269,15 @@ def prec_bits_to_words(int prec_in_bits=0): """ if not prec_in_bits: return prec - cdef int wordsize + cdef long wordsize wordsize = BITS_IN_LONG # increase prec_in_bits to the nearest multiple of wordsize - cdef int padded_bits + cdef long padded_bits padded_bits = (prec_in_bits + wordsize - 1) & ~(wordsize - 1) return int(padded_bits/wordsize + 2) -def prec_words_to_bits(int prec_in_words): +def prec_words_to_bits(long prec_in_words): r""" Convert from pari real precision expressed in words to precision expressed in bits. Note: this adjusts for the two codewords of a @@ -296,7 +296,7 @@ def prec_words_to_bits(int prec_in_words): # see user's guide to the pari library, page 10 return int((prec_in_words - 2) * BITS_IN_LONG) -def prec_dec_to_words(int prec_in_dec): +def prec_dec_to_words(long prec_in_dec): r""" Convert from precision expressed in decimal to precision expressed in words. Note: this rounds up to the nearest word, adjusts for the @@ -314,7 +314,7 @@ def prec_dec_to_words(int prec_in_dec): """ return prec_bits_to_words(prec_dec_to_bits(prec_in_dec)) -def prec_words_to_dec(int prec_in_words): +def prec_words_to_dec(long prec_in_words): r""" Convert from precision expressed in words to precision expressed in decimal. Note: this adjusts for the two codewords of a pari real, @@ -433,7 +433,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): if bot: return # pari already initialized. - global num_primes, avma, top, bot, prec + global num_primes, top, bot # The size here doesn't really matter, because we will allocate # our own stack anyway. We ask PARI not to set up signal and @@ -448,14 +448,6 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): pari_free(bot); bot = 0 init_stack(size) - GP_DATA.fmt.prettyp = 0 - - # how do I get the following to work? seems to be a circular import - #from sage.rings.real_mpfr import RealField - #prec_bits = RealField().prec() - prec = prec_bits_to_words(53) - GP_DATA.fmt.sigd = prec_bits_to_dec(53) - # Set printing functions global pariOut, pariErr @@ -469,6 +461,17 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): pariErr.puts = sage_pariErr_puts pariErr.flush = sage_pariErr_flush + # Display only 15 digits + sd_format("g.15", d_SILENT) + + # Init global prec variable (PARI's precision is always a + # multiple of the machine word size) + global prec + prec = prec_bits_to_words(64) + + # Disable pretty-printing + GP_DATA.fmt.prettyp = 0 + def debugstack(self): r""" Print the internal PARI variables ``top`` (top of stack), ``avma`` @@ -554,7 +557,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): def set_real_precision(self, long n): """ - Sets the PARI default real precision. + Sets the PARI default real precision in decimal digits. This is used both for creation of new objects from strings and for printing. It is the number of digits *IN DECIMAL* in which real @@ -564,15 +567,22 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): effect on the precision of computations within the pari library. Returns the previous PARI real precision. - """ - cdef unsigned long k - k = GP_DATA.fmt.sigd - s = str(n) + EXAMPLES:: + + sage: pari.set_real_precision(60) + 15 + sage: pari('1.2') + 1.20000000000000000000000000000000000000000000000000000000000 + sage: pari.set_real_precision(15) + 60 + """ + prev = self.get_real_precision() + cdef bytes strn = str(n) pari_catch_sig_on() - sd_realprecision(s, 2) + sd_realprecision(strn, d_SILENT) pari_catch_sig_off() - return int(k) # Python int + return prev def get_real_precision(self): """ @@ -584,8 +594,16 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): created by parsing strings (e.g. pari('1.2')), which is *not* the normal way of creating new pari objects in Sage. It has *no* effect on the precision of computations within the pari library. + + EXAMPLES:: + + sage: pari.get_real_precision() + 15 """ - return GP_DATA.fmt.sigd + pari_catch_sig_on() + cdef long prev = itos(sd_realprecision(NULL, d_RETURN)) + pari_catch_sig_off() + return prev def set_series_precision(self, long n): global precdl From 3c2644471ed49e922eb27798ef61ce79c21a7556 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 16 Dec 2013 17:46:34 +0100 Subject: [PATCH 168/206] Fix double colon --- src/sage/libs/pari/gen.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 42b81048ea1..c3f33aed2c1 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -8903,7 +8903,7 @@ class PariError(RuntimeError): ....: print err _/_: division by zero - A syntax error: + A syntax error:: sage: pari('!@#$%^&*()') Traceback (most recent call last): From f0ecb06a7857c93d6fdc52dfc332909d184bbcaf Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 17 Dec 2013 12:24:47 +0100 Subject: [PATCH 169/206] Set gpd_TEST flag in GP_DATA --- src/sage/libs/pari/decl.pxi | 5 ++++- src/sage/libs/pari/gen.pyx | 8 ++++---- src/sage/libs/pari/gen_py.py | 2 +- src/sage/libs/pari/pari_instance.pyx | 5 +++++ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/sage/libs/pari/decl.pxi b/src/sage/libs/pari/decl.pxi index 955a1edd65d..077b79716bb 100644 --- a/src/sage/libs/pari/decl.pxi +++ b/src/sage/libs/pari/decl.pxi @@ -1928,6 +1928,8 @@ cdef extern from *: # paristio.h cdef extern from 'pari/paripriv.h': + int gpd_QUIET, gpd_TEST, gpd_EMACS, gpd_TEXMACS + struct pariout_t: char format # e,f,g long fieldw # 0 (ignored) or field width @@ -1937,6 +1939,7 @@ cdef extern from 'pari/paripriv.h': int TeXstyle struct gp_data: - jmp_buf env pariout_t *fmt + unsigned long flags + extern gp_data* GP_DATA diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index c3f33aed2c1..e3e0da98e4e 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -8891,8 +8891,8 @@ class PariError(RuntimeError): Return a suitable message for displaying this exception. This is the last line of ``self.errtext()``, with the leading - ``" *** "`` and trailing period (if any) removed. An - exception is syntax errors, where the "syntax error" line is + ``" *** "`` and trailing periods and colons (if any) removed. + An exception is syntax errors, where the "syntax error" line is shown. EXAMPLES:: @@ -8914,8 +8914,8 @@ class PariError(RuntimeError): if self.errnum() == syntaxer: for line in lines: if "syntax error" in line: - return line.lstrip(" *").rstrip(" .") - return lines[-1].lstrip(" *").rstrip(" .") + return line.lstrip(" *").rstrip(" .:") + return lines[-1].lstrip(" *").rstrip(" .:") cdef _factor_int_when_pari_factor_failed(x, failed_factorization): diff --git a/src/sage/libs/pari/gen_py.py b/src/sage/libs/pari/gen_py.py index 778e00cc698..29aae82b2ad 100644 --- a/src/sage/libs/pari/gen_py.py +++ b/src/sage/libs/pari/gen_py.py @@ -63,7 +63,7 @@ def pari(x): sage: pari(v) Traceback (most recent call last): ... - PariError: syntax error, unexpected ')', expecting )-> or ',': ...00000000,5.60000000000000) + PariError: syntax error, unexpected ')', expecting )-> or ',' sage: b = pari(list(v)); b,b.type() ([1.20000000000000, 3.40000000000000, 5.60000000000000], 't_VEC') diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx index 7a559855002..92b5c392954 100644 --- a/src/sage/libs/pari/pari_instance.pyx +++ b/src/sage/libs/pari/pari_instance.pyx @@ -472,6 +472,11 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): # Disable pretty-printing GP_DATA.fmt.prettyp = 0 + # This causes PARI/GP to use output independent of the terminal + # (which is want we want for the PARI library interface). + GP_DATA.flags = gpd_TEST + + def debugstack(self): r""" Print the internal PARI variables ``top`` (top of stack), ``avma`` From 033f60bc83fce2a7a803999e2ab2e6aa04a43a0a Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 17 Dec 2013 14:29:14 +0100 Subject: [PATCH 170/206] Support __call__(**kwds) using gsubstvec() --- src/sage/libs/pari/gen.pyx | 148 ++++++++++++++++++++++++++++++++----- 1 file changed, 129 insertions(+), 19 deletions(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 40a16deac35..17054d1d7e4 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -7363,12 +7363,21 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(deriv(self.g, P.get_var(v))) - def eval(self, *args): + def eval(self, *args, **kwds): """ Evaluate ``self`` with the given arguments. - This is currently implemented for polynomials and objects of - type ``t_CLOSURE`` (functions in GP bytecode form). + This is currently implemented in 3 cases: + + - univariate polynomials (using a single unnamed argument or + keyword arguments), + - any PARI object supporting the PARI function ``substvec`` + (in particular, multivariate polynomials) using keyword + arguments, + - objects of type ``t_CLOSURE`` (functions in GP bytecode form) + using unnamed arguments. + + In no case is mixing unnamed and keyword arguments allowed. EXAMPLES:: @@ -7377,6 +7386,35 @@ cdef class gen(sage.structure.element.RingElement): 't_POL' sage: f.eval(I) 0 + sage: f.eval(x=2) + 5 + + Evaluating multivariate polynomials:: + + sage: f = pari('y^2 + x^3') + sage: f(1) # Dangerous, depends on PARI variable ordering + y^2 + 1 + sage: f(x=1) # Safe + y^2 + 1 + sage: f(y=1) + x^3 + 1 + sage: f(1, 2) + Traceback (most recent call last): + ... + TypeError: evaluating a polynomial takes exactly 1 argument (2 given) + sage: f(y='x', x='2*y') + x^2 + 8*y^3 + sage: f() + x^3 + y^2 + + It's not an error to substitute variables which do not appear:: + + sage: f.eval(z=37) + x^3 + y^2 + sage: pari(42).eval(t=0) + 42 + + We can define and evaluate closures as follows:: sage: T = pari('n -> n + 2') sage: T.type() @@ -7384,34 +7422,85 @@ cdef class gen(sage.structure.element.RingElement): sage: T.eval(3) 5 + sage: T = pari('() -> 42') + sage: T() + 42 + + sage: pr = pari('s -> print(s)') + sage: pr.eval('"hello world"') + hello world + + sage: f = pari('myfunc(x,y) = x*y') + sage: f.eval(5, 6) + 30 + + Using keyword arguments, we can substitute in more complicated + objects, for example a number field:: + + sage: K. = NumberField(x^2 + 1) + sage: nf = K._pari_() + sage: nf + [y^2 + 1, [0, 1], -4, 1, [Mat([1, 0.E-19 - 1.00000000000000*I]), [1, -1.00000000000000; 1, 1.00000000000000], [1, -1; 1, 1], [2, 0; 0, -2], [2, 0; 0, 2], [1, 0; 0, -1], [1, [0, -1; 1, 0]]], [0.E-19 - 1.00000000000000*I], [1, y], [1, 0; 0, 1], [1, 0, 0, -1; 0, 1, 1, 0]] + sage: nf(y='x') + [x^2 + 1, [0, 1], -4, 1, [Mat([1, 0.E-19 - 1.00000000000000*I]), [1, -1.00000000000000; 1, 1.00000000000000], [1, -1; 1, 1], [2, 0; 0, -2], [2, 0; 0, 2], [1, 0; 0, -1], [1, [0, -1; 1, 0]]], [0.E-19 - 1.00000000000000*I], [1, x], [1, 0; 0, 1], [1, 0, 0, -1; 0, 1, 1, 0]] """ cdef long t = typ(self.g) cdef gen t0 cdef GEN result - if t == t_POL: - # evaluate univariate polynomial - if len(args) != 1: - raise TypeError("evaluating a polynomial takes exactly 1 argument") - t0 = objtogen(args[0]) - pari_catch_sig_on() - return P.new_gen(poleval(self.g, t0.g)) - elif t == t_CLOSURE: + cdef long arity + cdef long nargs = len(args) + cdef long nkwds = len(kwds) + + # Closure must be evaluated using *args + if t == t_CLOSURE: + if nkwds > 0: + raise TypeError("cannot evaluate a PARI closure using keyword arguments") + # XXX: use undocumented internals to get arity of closure + # (In PARI 2.6, there is closure_arity() which does this) + arity = self.g[1] + if nargs != arity: + raise TypeError("PARI closure takes exactly %d argument%s (%d given)"%( + arity, "s" if (arity!=1) else "", nargs)) t0 = objtogen(args) pari_catch_sig_on() result = closure_callgenvec(self.g, t0.g) - if result != NULL: - return P.new_gen(result) - else: + if result == gnil: P.clear_stack() return None + return P.new_gen(result) + + # Evaluate univariate polynomial using *args + if nargs > 0: + if nkwds > 0: + raise TypeError("mixing unnamed and keyword arguments not allowed when evaluating a PARI object") + if t == t_POL: + # evaluate univariate polynomial + if nargs != 1: + raise TypeError("evaluating a polynomial takes exactly 1 argument (%d given)"%nargs) + t0 = objtogen(args[0]) + pari_catch_sig_on() + return P.new_gen(poleval(self.g, t0.g)) + raise TypeError("cannot evaluate %s using unnamed arguments"%self.type()) + + # Call substvec() using **kwds + vstr = kwds.keys() # Variables as Python strings + t0 = objtogen(kwds.values()) # Replacements + + pari_catch_sig_on() + cdef GEN v = cgetg(nkwds+1, t_VEC) # Variables as PARI polynomials + cdef long i + for i in range(nkwds): + set_gel(v, i+1, pol_x(P.get_var(vstr[i]))) + return P.new_gen(gsubstvec(self.g, v, t0.g)) + - def __call__(self, *args): + def __call__(self, *args, **kwds): """ Evaluate ``self`` with the given arguments. - This has the same effect as ``self.eval(*args)``. + This has the same effect as :meth:`eval`. - TESTS:: + EXAMPLES:: sage: R. = GF(3)[] sage: f = (x^2 + x + 1)._pari_() @@ -7420,6 +7509,8 @@ cdef class gen(sage.structure.element.RingElement): sage: f(2) Mod(1, 3) + TESTS:: + sage: T = pari('n -> 1/n') sage: T.type() 't_CLOSURE' @@ -7427,9 +7518,28 @@ cdef class gen(sage.structure.element.RingElement): Traceback (most recent call last): ... PariError: _/_: division by zero - + sage: pari('() -> 42')(1,2,3) + Traceback (most recent call last): + ... + TypeError: PARI closure takes exactly 0 arguments (3 given) + sage: pari('n -> n')() + Traceback (most recent call last): + ... + TypeError: PARI closure takes exactly 1 argument (0 given) + sage: pari('n -> n')(n=2) + Traceback (most recent call last): + ... + TypeError: cannot evaluate a PARI closure using keyword arguments + sage: pari('x + y')(4, y=1) + Traceback (most recent call last): + ... + TypeError: mixing unnamed and keyword arguments not allowed when evaluating a PARI object + sage: pari("12345")(4) + Traceback (most recent call last): + ... + TypeError: cannot evaluate t_INT using unnamed arguments """ - return self.eval(*args) + return self.eval(*args, **kwds) def factornf(self, t): """ From 96d3280183edb4663ee017322800df140961502c Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 17 Dec 2013 15:56:20 +0100 Subject: [PATCH 171/206] Changed precision arguments to "unsigned long" Also optimized prec_bits_to_words() --- src/sage/libs/pari/gen.pyx | 147 +++++++++++++-------------- src/sage/libs/pari/pari_instance.pxd | 2 +- src/sage/libs/pari/pari_instance.pyx | 27 +++-- 3 files changed, 86 insertions(+), 90 deletions(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index e3e0da98e4e..da562ba8062 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -246,7 +246,7 @@ cdef class gen(sage.structure.element.RingElement): def __pow__(gen self, n, m): cdef gen t0 = objtogen(n) pari_catch_sig_on() - return P.new_gen(gpow(self.g, t0.g, prec_bits_to_words())) + return P.new_gen(gpow(self.g, t0.g, prec_bits_to_words(0))) def __neg__(gen self): pari_catch_sig_on() @@ -2038,7 +2038,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gtopolyrev(self.g, P.get_var(v))) - def Qfb(gen a, b, c, D=0, long precision=0): + def Qfb(gen a, b, c, D=0, unsigned long precision=0): """ Qfb(a,b,c,D=0.): Returns the binary quadratic form @@ -2092,7 +2092,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(Qfb0(a.g, t0.g, t1.g, t2.g, prec_bits_to_words(precision))) - def Ser(gen x, v=-1, long seriesprecision = 16): + def Ser(gen x, v=-1, long seriesprecision=16): """ Ser(x,v=x): Create a power series from x with main variable v and return the result. @@ -2140,9 +2140,9 @@ cdef class gen(sage.structure.element.RingElement): 1 + 2*v + 3*v^2 + 4*v^3 + 5*v^4 + O(v^5) sage: pari(1)/f 1 - 2*v + v^2 + O(v^5) - sage: pari('x^5').Ser(seriesprecision = 20) + sage: pari('x^5').Ser(seriesprecision=20) x^5 + O(x^25) - sage: pari('1/x').Ser(seriesprecision = 1) + sage: pari('1/x').Ser(seriesprecision=1) x^-1 + O(x^0) """ pari_catch_sig_on() @@ -2893,7 +2893,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gconj(x.g)) - def conjvec(gen x, long precision=0): + def conjvec(gen x, unsigned long precision=0): """ conjvec(x): Returns the vector of all conjugates of the algebraic number x. An algebraic number is a polynomial over Q modulo an @@ -3692,7 +3692,7 @@ cdef class gen(sage.structure.element.RingElement): # Examples, docs -- William Stein ########################################### - def abs(gen x, long precision=0): + def abs(gen x, unsigned long precision=0): """ Returns the absolute value of x (its modulus, if x is complex). Rational functions are not allowed. Contrary to most transcendental @@ -3721,7 +3721,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gabs(x.g, prec_bits_to_words(precision))) - def acos(gen x, long precision=0): + def acos(gen x, unsigned long precision=0): r""" The principal branch of `\cos^{-1}(x)`, so that `\RR e(\mathrm{acos}(x))` belongs to `[0,Pi]`. If `x` @@ -3747,7 +3747,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gacos(x.g, prec_bits_to_words(precision))) - def acosh(gen x, long precision=0): + def acosh(gen x, unsigned long precision=0): r""" The principal branch of `\cosh^{-1}(x)`, so that `\Im(\mathrm{acosh}(x))` belongs to `[0,Pi]`. If @@ -3772,7 +3772,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gach(x.g, prec_bits_to_words(precision))) - def agm(gen x, y, long precision=0): + def agm(gen x, y, unsigned long precision=0): r""" The arithmetic-geometric mean of x and y. In the case of complex or negative numbers, the principal square root is always chosen. @@ -3802,7 +3802,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(agm(x.g, t0.g, prec_bits_to_words(precision))) - def arg(gen x, long precision=0): + def arg(gen x, unsigned long precision=0): r""" arg(x): argument of x,such that `-\pi < \arg(x) \leq \pi`. @@ -3820,7 +3820,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(garg(x.g, prec_bits_to_words(precision))) - def asin(gen x, long precision=0): + def asin(gen x, unsigned long precision=0): r""" The principal branch of `\sin^{-1}(x)`, so that `\RR e(\mathrm{asin}(x))` belongs to `[-\pi/2,\pi/2]`. If @@ -3842,7 +3842,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gasin(x.g, prec_bits_to_words(precision))) - def asinh(gen x, long precision=0): + def asinh(gen x, unsigned long precision=0): r""" The principal branch of `\sinh^{-1}(x)`, so that `\Im(\mathrm{asinh}(x))` belongs to `[-\pi/2,\pi/2]`. @@ -3863,7 +3863,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gash(x.g, prec_bits_to_words(precision))) - def atan(gen x, long precision=0): + def atan(gen x, unsigned long precision=0): r""" The principal branch of `\tan^{-1}(x)`, so that `\RR e(\mathrm{atan}(x))` belongs to `]-\pi/2, \pi/2[`. @@ -3884,7 +3884,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gatan(x.g, prec_bits_to_words(precision))) - def atanh(gen x, long precision=0): + def atanh(gen x, unsigned long precision=0): r""" The principal branch of `\tanh^{-1}(x)`, so that `\Im(\mathrm{atanh}(x))` belongs to `]-\pi/2,\pi/2]`. If @@ -3923,7 +3923,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(bernfrac(x)) - def bernreal(gen x, long precision=0): + def bernreal(gen x, unsigned long precision=0): r""" The Bernoulli number `B_x`, as for the function bernfrac, but `B_x` is returned as a real number (with the current @@ -3960,7 +3960,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(bernvec(x)) - def besselh1(gen nu, x, long precision=0): + def besselh1(gen nu, x, unsigned long precision=0): r""" The `H^1`-Bessel function of index `\nu` and argument `x`. @@ -3980,7 +3980,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(hbessel1(nu.g, t0.g, prec_bits_to_words(precision))) - def besselh2(gen nu, x, long precision=0): + def besselh2(gen nu, x, unsigned long precision=0): r""" The `H^2`-Bessel function of index `\nu` and argument `x`. @@ -4000,7 +4000,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(hbessel2(nu.g, t0.g, prec_bits_to_words(precision))) - def besselj(gen nu, x, long precision=0): + def besselj(gen nu, x, unsigned long precision=0): r""" Bessel J function (Bessel function of the first kind), with index `\nu` and argument `x`. If `x` converts to @@ -4023,7 +4023,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(jbessel(nu.g, t0.g, prec_bits_to_words(precision))) - def besseljh(gen nu, x, long precision=0): + def besseljh(gen nu, x, unsigned long precision=0): """ J-Bessel function of half integral index (Spherical Bessel function of the first kind). More precisely, besseljh(n,x) computes @@ -4048,7 +4048,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(jbesselh(nu.g, t0.g, prec_bits_to_words(precision))) - def besseli(gen nu, x, long precision=0): + def besseli(gen nu, x, unsigned long precision=0): r""" Bessel I function (Bessel function of the second kind), with index `\nu` and argument `x`. If `x` converts to @@ -4074,7 +4074,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ibessel(nu.g, t0.g, prec_bits_to_words(precision))) - def besselk(gen nu, x, long flag=0, long precision=0): + def besselk(gen nu, x, long flag=0, unsigned long precision=0): """ nu.besselk(x, flag=0): K-Bessel function (modified Bessel function of the second kind) of index nu, which can be complex, and argument @@ -4117,7 +4117,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(kbessel(nu.g, t0.g, prec_bits_to_words(precision))) - def besseln(gen nu, x, long precision=0): + def besseln(gen nu, x, unsigned long precision=0): """ nu.besseln(x): Bessel N function (Spherical Bessel function of the second kind) of index nu and argument x. @@ -4138,7 +4138,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(nbessel(nu.g, t0.g, prec_bits_to_words(precision))) - def cos(gen x, long precision=0): + def cos(gen x, unsigned long precision=0): """ The cosine function. @@ -4160,7 +4160,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gcos(x.g, prec_bits_to_words(precision))) - def cosh(gen x, long precision=0): + def cosh(gen x, unsigned long precision=0): """ The hyperbolic cosine function. @@ -4182,7 +4182,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gch(x.g, prec_bits_to_words(precision))) - def cotan(gen x, long precision=0): + def cotan(gen x, unsigned long precision=0): """ The cotangent of x. @@ -4209,7 +4209,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gcotan(x.g, prec_bits_to_words(precision))) - def dilog(gen x, long precision=0): + def dilog(gen x, unsigned long precision=0): r""" The principal branch of the dilogarithm of `x`, i.e. the analytic continuation of the power series @@ -4231,7 +4231,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(dilog(x.g, prec_bits_to_words(precision))) - def eint1(gen x, long n=0, long precision=0): + def eint1(gen x, long n=0, unsigned long precision=0): r""" x.eint1(n): exponential integral E1(x): @@ -4261,7 +4261,7 @@ cdef class gen(sage.structure.element.RingElement): else: return P.new_gen(veceint1(x.g, stoi(n), prec_bits_to_words(precision))) - def erfc(gen x, long precision=0): + def erfc(gen x, unsigned long precision=0): r""" Return the complementary error function: @@ -4284,7 +4284,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gerfc(x.g, prec_bits_to_words(precision))) - def eta(gen x, long flag=0, long precision=0): + def eta(gen x, long flag=0, unsigned long precision=0): r""" x.eta(flag=0): if flag=0, `\eta` function without the `q^{1/24}`; otherwise `\eta` of the complex number @@ -4314,7 +4314,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(trueeta(x.g, prec_bits_to_words(precision))) return P.new_gen(eta(x.g, prec_bits_to_words(precision))) - def exp(gen self, long precision=0): + def exp(gen self, unsigned long precision=0): """ x.exp(): exponential of x. @@ -4335,7 +4335,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gexp(self.g, prec_bits_to_words(precision))) - def gamma(gen s, long precision=0): + def gamma(gen s, unsigned long precision=0): """ s.gamma(precision): Gamma function at s. @@ -4364,7 +4364,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ggamma(s.g, prec_bits_to_words(precision))) - def gammah(gen s, long precision=0): + def gammah(gen s, unsigned long precision=0): """ s.gammah(): Gamma function evaluated at the argument x+1/2. @@ -4386,7 +4386,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ggamd(s.g, prec_bits_to_words(precision))) - def hyperu(gen a, b, x, long precision=0): + def hyperu(gen a, b, x, unsigned long precision=0): r""" a.hyperu(b,x): U-confluent hypergeometric function. @@ -4406,7 +4406,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(hyperu(a.g, t0.g, t1.g, prec_bits_to_words(precision))) - def incgam(gen s, x, y=None, long precision=0): + def incgam(gen s, x, y=None, unsigned long precision=0): r""" s.incgam(x, y, precision): incomplete gamma function. y is optional and is the precomputed value of gamma(s). @@ -4432,7 +4432,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(incgam0(s.g, t0.g, t1.g, prec_bits_to_words(precision))) - def incgamc(gen s, x, long precision=0): + def incgamc(gen s, x, unsigned long precision=0): r""" s.incgamc(x): complementary incomplete gamma function. @@ -4458,7 +4458,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(incgamc(s.g, t0.g, prec_bits_to_words(precision))) - def log(gen x, long precision=0): + def log(gen x, unsigned long precision=0): r""" x.log(): natural logarithm of x. @@ -4501,7 +4501,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(glog(x.g, prec_bits_to_words(precision))) - def lngamma(gen x, long precision=0): + def lngamma(gen x, unsigned long precision=0): r""" This method is deprecated, please use :meth:`.log_gamma` instead. @@ -4518,7 +4518,7 @@ cdef class gen(sage.structure.element.RingElement): deprecation(6992, "The method lngamma() is deprecated. Use log_gamma() instead.") return x.log_gamma(precision) - def log_gamma(gen x, long precision=0): + def log_gamma(gen x, unsigned long precision=0): r""" Logarithm of the gamma function of x. @@ -4544,7 +4544,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(glngamma(x.g, prec_bits_to_words(precision))) - def polylog(gen x, long m, long flag=0, long precision=0): + def polylog(gen x, long m, long flag=0, unsigned long precision=0): """ x.polylog(m,flag=0): m-th polylogarithm of x. flag is optional, and can be 0: default, 1: D_m -modified m-th polylog of x, 2: @@ -4572,7 +4572,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(polylog0(m, x.g, flag, prec_bits_to_words(precision))) - def psi(gen x, long precision=0): + def psi(gen x, unsigned long precision=0): r""" x.psi(): psi-function at x. @@ -4592,7 +4592,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gpsi(x.g, prec_bits_to_words(precision))) - def sin(gen x, long precision=0): + def sin(gen x, unsigned long precision=0): """ x.sin(): The sine of x. @@ -4612,7 +4612,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gsin(x.g, prec_bits_to_words(precision))) - def sinh(gen x, long precision=0): + def sinh(gen x, unsigned long precision=0): """ The hyperbolic sine function. @@ -4659,7 +4659,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(gsqr(x.g)) - def sqrt(gen x, long precision=0): + def sqrt(gen x, unsigned long precision=0): """ x.sqrt(precision): The square root of x. @@ -4676,7 +4676,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gsqrt(x.g, prec_bits_to_words(precision))) - def sqrtn(gen x, n, long precision=0): + def sqrtn(gen x, n, unsigned long precision=0): r""" x.sqrtn(n): return the principal branch of the n-th root of x, i.e., the one such that @@ -4736,7 +4736,7 @@ cdef class gen(sage.structure.element.RingElement): ans = P.new_gen_noclear(gsqrtn(x.g, t0.g, &zetan, prec_bits_to_words(precision))) return ans, P.new_gen(zetan) - def tan(gen x, long precision=0): + def tan(gen x, unsigned long precision=0): """ x.tan() - tangent of x @@ -4756,7 +4756,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(gtan(x.g, prec_bits_to_words(precision))) - def tanh(gen x, long precision=0): + def tanh(gen x, unsigned long precision=0): """ x.tanh() - hyperbolic tangent of x @@ -4796,7 +4796,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(teich(x.g)) - def theta(gen q, z, long precision=0): + def theta(gen q, z, unsigned long precision=0): """ q.theta(z): Jacobi sine theta-function. @@ -4815,7 +4815,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(theta(q.g, t0.g, prec_bits_to_words(precision))) - def thetanullk(gen q, long k, long precision=0): + def thetanullk(gen q, long k, unsigned long precision=0): """ q.thetanullk(k): return the k-th derivative at z=0 of theta(q,z). @@ -4832,7 +4832,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(thetanullk(q.g, k, prec_bits_to_words(precision))) - def weber(gen x, long flag=0, long precision=0): + def weber(gen x, long flag=0, unsigned long precision=0): r""" x.weber(flag=0): One of Weber's f functions of x. flag is optional, and can be 0: default, function @@ -4863,7 +4863,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(weber0(x.g, flag, prec_bits_to_words(precision))) - def zeta(gen s, long precision=0): + def zeta(gen s, unsigned long precision=0): """ zeta(s): zeta function at s with s a complex or a p-adic number. @@ -5187,7 +5187,7 @@ cdef class gen(sage.structure.element.RingElement): # 5: Elliptic curve functions ################################################## - def ellinit(self, long flag=0, long precision=0): + def ellinit(self, long flag=0, unsigned long precision=0): """ Return the PARI elliptic curve object with Weierstrass coefficients given by self, a list with 5 elements. @@ -5411,7 +5411,7 @@ cdef class gen(sage.structure.element.RingElement): else: return P.new_gen(anell(self.g, n)) - def ellanalyticrank(self, long precision = 0): + def ellanalyticrank(self, unsigned long precision=0): r""" Returns a 2-component vector with the order of vanishing at `s = 1` of the L-function of the elliptic curve and the value @@ -5536,8 +5536,7 @@ cdef class gen(sage.structure.element.RingElement): set_gel(g, i + 1, ellap(self.g, gel(g, i + 1))) return P.new_gen(g) - - def ellbil(self, z0, z1, long precision=0): + def ellbil(self, z0, z1, unsigned long precision=0): """ e.ellbil(z0, z1): return the value of the canonical bilinear form on z0 and z1. @@ -5593,7 +5592,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ellchangecurve(self.g, t0.g)) - def elleta(self, long precision=0): + def elleta(self, unsigned long precision=0): """ e.elleta(): return the vector [eta1,eta2] of quasi-periods associated with the period lattice e.omega() of the elliptic curve @@ -5618,7 +5617,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(elleta(self.g, prec_bits_to_words(precision))) - def ellheight(self, a, long flag=2, long precision=0): + def ellheight(self, a, long flag=2, unsigned long precision=0): """ e.ellheight(a, flag=2): return the global Neron-Tate height of the point a on the elliptic curve e. @@ -5662,7 +5661,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ellheight0(self.g, t0.g, flag, prec_bits_to_words(precision))) - def ellheightmatrix(self, x, long precision=0): + def ellheightmatrix(self, x, unsigned long precision=0): """ e.ellheightmatrix(x): return the height matrix for the vector x of points on the elliptic curve e. @@ -5880,7 +5879,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(elllocalred(self.g, t0.g)) - def elllseries(self, s, A=1, long precision=0): + def elllseries(self, s, A=1, unsigned long precision=0): """ e.elllseries(s, A=1): return the value of the `L`-series of the elliptic curve e at the complex number s. @@ -5988,7 +5987,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(orderell(self.g, t0.g)) - def ellordinate(self, x, long precision=0): + def ellordinate(self, x, unsigned long precision=0): """ e.ellordinate(x): return the `y`-coordinates of the points on the elliptic curve e having x as `x`-coordinate. @@ -6030,7 +6029,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ellordinate(self.g, t0.g, prec_bits_to_words(precision))) - def ellpointtoz(self, pt, long precision=0): + def ellpointtoz(self, pt, unsigned long precision=0): """ e.ellpointtoz(pt): return the complex number (in the fundamental parallelogram) corresponding to the point ``pt`` on the elliptic curve @@ -6156,7 +6155,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_off() return rootno - def ellsigma(self, z, long flag=0, long precision=0): + def ellsigma(self, z, long flag=0, unsigned long precision=0): """ e.ellsigma(z, flag=0): return the value at the complex point z of the Weierstrass `\sigma` function associated to the @@ -6249,7 +6248,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(elltors0(self.g, flag)) - def ellzeta(self, z, long precision=0): + def ellzeta(self, z, unsigned long precision=0): """ e.ellzeta(z): return the value at the complex point z of the Weierstrass `\zeta` function associated with the elliptic @@ -6283,7 +6282,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(ellzeta(self.g, t0.g, prec_bits_to_words(precision))) - def ellztopoint(self, z, long precision=0): + def ellztopoint(self, z, unsigned long precision=0): """ e.ellztopoint(z): return the point on the elliptic curve e corresponding to the complex number z, under the usual complex @@ -6357,7 +6356,7 @@ cdef class gen(sage.structure.element.RingElement): """ return self[12] - def ellj(self, long precision=0): + def ellj(self, unsigned long precision=0): """ Elliptic `j`-invariant of ``self``. @@ -6404,7 +6403,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_off() return n - def bnfinit(self, long flag=0, tech=None, long precision=0): + def bnfinit(self, long flag=0, tech=None, unsigned long precision=0): cdef gen t0 if tech is None: pari_catch_sig_on() @@ -6433,7 +6432,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(buchnarrow(self.g)) - def bnfsunit(self, S, long precision=0): + def bnfsunit(self, S, unsigned long precision=0): cdef gen t0 = objtogen(S) pari_catch_sig_on() return P.new_gen(bnfsunit(self.g, t0.g, prec_bits_to_words(precision))) @@ -7091,7 +7090,7 @@ cdef class gen(sage.structure.element.RingElement): return P.new_gen(nfhnf(self.g, t0.g)) - def nfinit(self, long flag=0, long precision=0): + def nfinit(self, long flag=0, unsigned long precision=0): """ nfinit(pol, {flag=0}): ``pol`` being a nonconstant irreducible polynomial, gives a vector containing all the data necessary for PARI @@ -7492,14 +7491,14 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(reduceddiscsmith(self.g)) - def polgalois(self, long precision=0): + def polgalois(self, unsigned long precision=0): """ f.polgalois(): Galois group of the polynomial f """ pari_catch_sig_on() return P.new_gen(polgalois(self.g, prec_bits_to_words(precision))) - def nfgaloisconj(self, long flag=0, denom=None, long precision=0): + def nfgaloisconj(self, long flag=0, denom=None, unsigned long precision=0): r""" Edited from the pari documentation: @@ -7609,7 +7608,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(polresultant0(self.g, t0.g, P.get_var(var), flag)) - def polroots(self, long flag=0, long precision=0): + def polroots(self, long flag=0, unsigned long precision=0): """ polroots(x,flag=0): complex roots of the polynomial x. flag is optional, and can be 0: default, uses Schonhage's method modified @@ -7686,7 +7685,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(recip(self.g)) - def thueinit(self, long flag=0, long precision=0): + def thueinit(self, long flag=0, unsigned long precision=0): pari_catch_sig_on() return P.new_gen(thueinit(self.g, flag, prec_bits_to_words(precision))) @@ -8563,7 +8562,7 @@ cdef class gen(sage.structure.element.RingElement): - def elleisnum(self, long k, long flag=0, long precision=0): + def elleisnum(self, long k, long flag=0, unsigned long precision=0): """ om.elleisnum(k, flag=0): om=[om1,om2] being a 2-component vector giving a basis of a lattice L and k an even positive integer, @@ -8604,7 +8603,7 @@ cdef class gen(sage.structure.element.RingElement): pari_catch_sig_on() return P.new_gen(elleisnum(self.g, k, flag, prec_bits_to_words(precision))) - def ellwp(gen self, z='z', long n=20, long flag=0, long precision=0): + def ellwp(gen self, z='z', long n=20, long flag=0, unsigned long precision=0): """ Return the value or the series expansion of the Weierstrass `P`-function at `z` on the lattice `self` (or the lattice diff --git a/src/sage/libs/pari/pari_instance.pxd b/src/sage/libs/pari/pari_instance.pxd index 3b2e0aa25e6..a7242a0753b 100644 --- a/src/sage/libs/pari/pari_instance.pxd +++ b/src/sage/libs/pari/pari_instance.pxd @@ -5,7 +5,7 @@ cimport cython from sage.libs.pari.gen cimport gen -cpdef prec_bits_to_words(long prec_in_bits=*) +cpdef long prec_bits_to_words(unsigned long prec_in_bits) @cython.final cdef class PariInstance(sage.structure.parent_base.ParentWithBase): diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx index 92b5c392954..b3359399682 100644 --- a/src/sage/libs/pari/pari_instance.pyx +++ b/src/sage/libs/pari/pari_instance.pyx @@ -194,13 +194,13 @@ new_galois_format = 1 # which should be a words precision. In many cases this is redundant # and is simply ignored. In our wrapping of these functions we use # the variable prec here for convenience only. -cdef unsigned long prec +cdef long prec ################################################################# # conversions between various real precision models ################################################################# -def prec_bits_to_dec(long prec_in_bits): +def prec_bits_to_dec(unsigned long prec_in_bits): r""" Convert from precision expressed in bits to precision expressed in decimal. @@ -220,10 +220,10 @@ def prec_bits_to_dec(long prec_in_bits): (224, 67), (256, 77)] """ - log_2 = 0.301029995663981 + cdef double log_2 = 0.301029995663981 return int(prec_in_bits*log_2) -def prec_dec_to_bits(long prec_in_dec): +def prec_dec_to_bits(unsigned long prec_in_dec): r""" Convert from precision expressed in decimal to precision expressed in bits. @@ -244,10 +244,10 @@ def prec_dec_to_bits(long prec_in_dec): (80, 265), (90, 298)] """ - log_10 = 3.32192809488736 + cdef double log_10 = 3.32192809488736 return int(prec_in_dec*log_10) -cpdef prec_bits_to_words(long prec_in_bits=0): +cpdef long prec_bits_to_words(unsigned long prec_in_bits): r""" Convert from precision expressed in bits to pari real precision expressed in words. Note: this rounds up to the nearest word, @@ -269,13 +269,10 @@ cpdef prec_bits_to_words(long prec_in_bits=0): """ if not prec_in_bits: return prec - cdef long wordsize - wordsize = BITS_IN_LONG + cdef unsigned long wordsize = BITS_IN_LONG - # increase prec_in_bits to the nearest multiple of wordsize - cdef long padded_bits - padded_bits = (prec_in_bits + wordsize - 1) & ~(wordsize - 1) - return int(padded_bits/wordsize + 2) + # This equals ceil(prec_in_bits/wordsize) + 2 + return (prec_in_bits - 1)//wordsize + 3 def prec_words_to_bits(long prec_in_words): r""" @@ -294,7 +291,7 @@ def prec_words_to_bits(long prec_in_words): [(3, 64), (4, 128), (5, 192), (6, 256), (7, 320), (8, 384), (9, 448)] # 64-bit """ # see user's guide to the pari library, page 10 - return int((prec_in_words - 2) * BITS_IN_LONG) + return (prec_in_words - 2) * BITS_IN_LONG def prec_dec_to_words(long prec_in_dec): r""" @@ -1310,7 +1307,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): self.init_primes(max(2*num_primes,20*n)) return self.nth_prime(n) - def euler(self, precision=0): + def euler(self, unsigned long precision=0): """ Return Euler's constant to the requested real precision (in bits). @@ -1324,7 +1321,7 @@ cdef class PariInstance(sage.structure.parent_base.ParentWithBase): pari_catch_sig_on() return self.new_gen(mpeuler(prec_bits_to_words(precision))) - def pi(self, precision=0): + def pi(self, unsigned long precision=0): """ Return the value of the constant pi to the requested real precision (in bits). From 520f0438e74d9b0a706a48da01c5464dfa59a1a8 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 17 Dec 2013 16:21:44 +0100 Subject: [PATCH 172/206] Fix doctests on 32-bit machines --- src/sage/libs/pari/gen.pyx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index da562ba8062..91b175b7e41 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -2913,7 +2913,7 @@ cdef class gen(sage.structure.element.RingElement): [-0.414213562373095, 2.41421356237310]~ sage: pari('Mod(x,x^3-3)').conjvec() [1.44224957030741, -0.721124785153704 + 1.24902476648341*I, -0.721124785153704 - 1.24902476648341*I]~ - sage: pari('Mod(1+x,x^2-2)').conjvec(precision=160)[0].sage() + sage: pari('Mod(1+x,x^2-2)').conjvec(precision=192)[0].sage() -0.414213562373095048801688724209698078569671875376948073177 """ pari_catch_sig_on() @@ -3933,7 +3933,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari(18).bernreal() 54.9711779448622 - sage: pari(18).bernreal(precision=160).sage() + sage: pari(18).bernreal(precision=192).sage() 54.9711779448621553884711779448621553884711779448621553885 """ pari_catch_sig_on() @@ -5904,8 +5904,8 @@ cdef class gen(sage.structure.element.RingElement): sage: e = pari([0,1,1,-2,0]).ellinit() sage: e.elllseries(2.1) 0.402838047956645 - sage: e.elllseries(1) - -5.24750372566629 E-19 + sage: e.elllseries(1, precision=128) + 2.87490929644255 E-38 sage: e.elllseries(1, precision=256) 3.00282377034977 E-77 sage: e.elllseries(-2) @@ -6303,7 +6303,8 @@ cdef class gen(sage.structure.element.RingElement): sage: e = pari([0,0,0,1,0]).ellinit() sage: C. = ComplexField() sage: e.ellztopoint(1+i) - [7.96075508054992 E-21 - 1.02152286795670*I, -0.149072813701096 - 0.149072813701096*I] + [0.E-19 - 1.02152286795670*I, -0.149072813701096 - 0.149072813701096*I] # 32-bit + [7.96075508054992 E-21 - 1.02152286795670*I, -0.149072813701096 - 0.149072813701096*I] # 64-bit Complex numbers belonging to the period lattice of e are of course sent to the point at infinity on e:: From dcccf2c5a870505a9e95c8bf83d8e0c7766ae00e Mon Sep 17 00:00:00 2001 From: "R. Andrew Ohana" Date: Tue, 17 Dec 2013 13:22:05 -0800 Subject: [PATCH 173/206] csage: use forward python3 compatible api --- src/c_lib/src/mpn_pylong.c | 36 ++++++++++++++++++------------------ src/c_lib/src/mpz_pylong.c | 8 ++++---- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/c_lib/src/mpn_pylong.c b/src/c_lib/src/mpn_pylong.c index 9cf66450f28..91e0064fcca 100644 --- a/src/c_lib/src/mpn_pylong.c +++ b/src/c_lib/src/mpn_pylong.c @@ -4,7 +4,7 @@ * Date: March 2006 * License: GPL v2 or later * - * the code to change the base to 2^SHIFT is based on the function + * the code to change the base to 2^PyLong_SHIFT is based on the function * mpn_get_str from GNU MP, but the new bugs are mine * * this is free software: if it breaks, you get to keep all the pieces @@ -12,8 +12,8 @@ #include "mpn_pylong.h" -/* This code assumes that SHIFT < GMP_NUMB_BITS */ -#if SHIFT >= GMP_NUMB_BITS +/* This code assumes that PyLong_SHIFT < GMP_NUMB_BITS */ +#if PyLong_SHIFT >= GMP_NUMB_BITS #error "Python limb larger than GMP limb !!!" #endif @@ -61,15 +61,15 @@ pylong_sizebits(digit *digits, py_size_t size) { unsigned long cnt; digit x; if (size==0) return 0; - cnt = (size - 1) * SHIFT; + cnt = (size - 1) * PyLong_SHIFT; x = digits[size - 1]; -#if SHIFT > 32 +#if PyLong_SHIFT > 32 if ((x >> 32) != 0) { x >>= 32; cnt += 32; } #endif -#if SHIFT > 16 +#if PyLong_SHIFT > 16 if ((x >> 16) != 0) { x >>= 16; cnt += 16; } #endif -#if SHIFT > 8 +#if PyLong_SHIFT > 8 if ((x >> 8) != 0) { x >>= 8; cnt += 8; } #endif return cnt + ((x & 0x80) ? 8 : __sizebits_tab[x]); @@ -81,7 +81,7 @@ pylong_sizebits(digit *digits, py_size_t size) { int mpn_pylong_size (mp_ptr up, mp_size_t un) { - return (mpn_sizebits(up, un) + SHIFT - 1) / SHIFT; + return (mpn_sizebits(up, un) + PyLong_SHIFT - 1) / PyLong_SHIFT; } /* this is based from GMP code in mpn/get_str.c */ @@ -106,19 +106,19 @@ mpn_get_pylong (digit *digits, py_size_t size, mp_ptr up, mp_size_t un) i = un - 1; n1 = up[i]; - bit_pos = size * SHIFT - i * GMP_NUMB_BITS; + bit_pos = size * PyLong_SHIFT - i * GMP_NUMB_BITS; for (;;) { - bit_pos -= SHIFT; + bit_pos -= PyLong_SHIFT; while (bit_pos >= 0) { - *--s = (n1 >> bit_pos) & MASK; - bit_pos -= SHIFT; + *--s = (n1 >> bit_pos) & PyLong_MASK; + bit_pos -= PyLong_SHIFT; } if (i == 0) break; - n0 = (n1 << -bit_pos) & MASK; + n0 = (n1 << -bit_pos) & PyLong_MASK; n1 = up[--i]; bit_pos += GMP_NUMB_BITS; *--s = n0 | (n1 >> bit_pos); @@ -150,22 +150,22 @@ mpn_set_pylong (mp_ptr up, mp_size_t un, digit *digits, py_size_t size) i = un - 1; n1 = 0; - bit_pos = size * SHIFT - i * GMP_NUMB_BITS; + bit_pos = size * PyLong_SHIFT - i * GMP_NUMB_BITS; for (;;) { - bit_pos -= SHIFT; + bit_pos -= PyLong_SHIFT; while (bit_pos >= 0) { d = (mp_limb_t) *--s; n1 |= (d << bit_pos) & GMP_NUMB_MASK; - bit_pos -= SHIFT; + bit_pos -= PyLong_SHIFT; } if (i == 0) break; d = (mp_limb_t) *--s; - /* add some high bits of d; maybe none if bit_pos=-SHIFT */ - up[i--] = n1 | (d & MASK) >> -bit_pos; + /* add some high bits of d; maybe none if bit_pos=-PyLong_SHIFT */ + up[i--] = n1 | (d & PyLong_MASK) >> -bit_pos; bit_pos += GMP_NUMB_BITS; n1 = (d << bit_pos) & GMP_NUMB_MASK; } diff --git a/src/c_lib/src/mpz_pylong.c b/src/c_lib/src/mpz_pylong.c index 848d688b023..843b187d648 100644 --- a/src/c_lib/src/mpz_pylong.c +++ b/src/c_lib/src/mpz_pylong.c @@ -37,7 +37,7 @@ mpz_get_pylong(mpz_srcptr z) { mpn_get_pylong(l->ob_digit, size, z->_mp_d, abs(z->_mp_size)); if (z->_mp_size < 0) - l->ob_size = -(l->ob_size); + Py_SIZE(l) = -Py_SIZE(l); } return (PyObject *) l; @@ -68,13 +68,13 @@ mpz_set_pylong(mpz_ptr z, PyObject * ll) return -1; } - size = mpn_size_from_pylong(l->ob_digit, abs(l->ob_size)); + size = mpn_size_from_pylong(l->ob_digit, abs(Py_SIZE(l))); if (z->_mp_alloc < size) _mpz_realloc (z, size); - mpn_set_pylong(z->_mp_d, size, l->ob_digit, abs(l->ob_size)); - z->_mp_size = l->ob_size < 0 ? -size : size; + mpn_set_pylong(z->_mp_d, size, l->ob_digit, abs(Py_SIZE(l))); + z->_mp_size = Py_SIZE(l) < 0 ? -size : size; return size; } From 463bffe560eda24298ebff260cf6fb2c2abd2496 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 18 Dec 2013 14:02:28 +0100 Subject: [PATCH 174/206] Declare various sig_ macros as nogil --- src/c_lib/include/interrupt.h | 7 - src/c_lib/src/interrupt.c | 12 +- src/c_lib/src/stdsage.c | 8 +- src/module_list.py | 5 + src/sage/ext/interrupt.pxi | 32 +- src/sage/ext/signals.pxi | 2 +- src/sage/ext/stdsage.pxi | 36 +- src/sage/libs/gap/util.pyx | 14 +- src/sage/libs/ntl/ntl_ZZ_pX.pyx | 240 +++++++----- .../polynomial_integer_dense_flint.pyx | 4 +- src/sage/tests/interrupt.pyx | 348 +++++++++--------- 11 files changed, 381 insertions(+), 327 deletions(-) diff --git a/src/c_lib/include/interrupt.h b/src/c_lib/include/interrupt.h index 76c2cf6fbff..653d2f39f0d 100644 --- a/src/c_lib/include/interrupt.h +++ b/src/c_lib/include/interrupt.h @@ -307,13 +307,6 @@ static inline void _sig_off_(const char* file, int line) #define sig_str(message) _sig_on_(message) #define sig_off() _sig_off_(__FILE__, __LINE__) -/* These deprecated macros provide backwards compatibility with - * sage-4.6 and earlier */ -#define _sig_on {if (!_sig_on_(NULL)) return 0;} -#define _sig_str(s) {if (!_sig_on_(s)) return 0;} -#define _sig_off {_sig_off_(__FILE__, __LINE__);} - - /* sig_check() should be functionally equivalent to sig_on(); sig_off(); * but much faster. Essentially, it checks whether we missed any * interrupts. diff --git a/src/c_lib/src/interrupt.c b/src/c_lib/src/interrupt.c index 68f800ef6ab..cfc41073896 100644 --- a/src/c_lib/src/interrupt.c +++ b/src/c_lib/src/interrupt.c @@ -214,10 +214,11 @@ void sage_signal_handler(int sig) /* This calls the externally defined function _signals.raise_exception - * to actually raise the exception. It may only be called synchronously - * with the Global Interpreter Lock held. */ + * to actually raise the exception. Since it uses the Python API, the + * GIL needs to be acquired first. */ static void do_raise_exception(int sig) { + PyGILState_STATE gilstate_save = PyGILState_Ensure(); #if ENABLE_DEBUG_INTERRUPT struct timeval raisetime; if (sage_interrupt_debug_level >= 2) { @@ -235,6 +236,8 @@ static void do_raise_exception(int sig) _signals.raise_exception(sig, _signals.s); assert(PyErr_Occurred()); } + + PyGILState_Release(gilstate_save); } @@ -268,7 +271,12 @@ void _sig_off_warning(const char* file, int line) { char buf[320]; snprintf(buf, sizeof(buf), "sig_off() without sig_on() at %s:%i", file, line); + + /* Raise a warning with the Python GIL acquired */ + PyGILState_STATE gilstate_save = PyGILState_Ensure(); PyErr_WarnEx(PyExc_RuntimeWarning, buf, 2); + PyGILState_Release(gilstate_save); + print_backtrace(); } diff --git a/src/c_lib/src/stdsage.c b/src/c_lib/src/stdsage.c index 3b8eee3f3ef..45692e62aea 100644 --- a/src/c_lib/src/stdsage.c +++ b/src/c_lib/src/stdsage.c @@ -33,13 +33,13 @@ void init_global_empty_tuple(void) { /* This function gets called whenever NTL calls Error(). s is the error message generated by NTL. - We just copy the error message into a global buffer, and then abort() to run - the usual interrupt machinery. + We raise a RuntimeError and then call sig_error() such that the + exception will be seen by sig_on(). */ void global_NTL_error_callback(const char* s, void* context) { - set_sage_signal_handler_message(s); - abort(); + PyErr_SetString(PyExc_RuntimeError, s); + sig_error(); } diff --git a/src/module_list.py b/src/module_list.py index c62c123dc2e..36989908c8a 100755 --- a/src/module_list.py +++ b/src/module_list.py @@ -2030,6 +2030,11 @@ def uname_specific(name, value, alternative): Extension('sage.tests.interrupt', sources = ['sage/tests/interrupt.pyx', 'sage/tests/c_lib.c']), + Extension('sage.tests.parallel', + sources = ['sage/tests/parallel.pyx'], + extra_compile_args=["-fopenmp"], + extra_link_args=["-fopenmp"]), + Extension('sage.tests.stl_vector', sources = ['sage/tests/stl_vector.pyx'], libraries = ['gmp'], diff --git a/src/sage/ext/interrupt.pxi b/src/sage/ext/interrupt.pxi index ad89b65a205..8997e777741 100644 --- a/src/sage/ext/interrupt.pxi +++ b/src/sage/ext/interrupt.pxi @@ -2,18 +2,19 @@ # See c_lib/include/interrupt.h # cdef extern from 'interrupt.h': - int sig_on() except 0 - int sig_str(char*) except 0 - int sig_check() except 0 - int sig_on_no_except() - int sig_str_no_except(char*) - int sig_check_no_except() - void sig_off() - void sig_retry() - void sig_error() - void sig_block() - void sig_unblock() - void set_sage_signal_handler_message(char* s) + int sig_on() nogil except 0 + int sig_str(char*) nogil except 0 + int sig_check() nogil except 0 + int sig_on_no_except() nogil + int sig_str_no_except(char*) nogil + int sig_check_no_except() nogil + void sig_off() nogil + void sig_retry() nogil # Does not return + void sig_error() nogil # Does not return + void sig_block() nogil + void sig_unblock() nogil + void set_sage_signal_handler_message(char* s) nogil + void cython_check_exception() nogil except * ctypedef struct sage_signals_t: int sig_on_count @@ -24,10 +25,3 @@ cdef extern from 'interrupt.h': int (*raise_exception)(int sig, const char* msg) except 0 sage_signals_t _signals - - # These provide backwards compatibility with sage-4.6 and earlier - int _sig_on - void _sig_str(char*) - int _sig_off - - void cython_check_exception() except * diff --git a/src/sage/ext/signals.pxi b/src/sage/ext/signals.pxi index 96cea916b36..7be1f2db499 100644 --- a/src/sage/ext/signals.pxi +++ b/src/sage/ext/signals.pxi @@ -1,7 +1,7 @@ # Declare system calls related to signal handling cdef extern from "": - void abort() + void abort() nogil cdef extern from "": ctypedef void *sigset_t diff --git a/src/sage/ext/stdsage.pxi b/src/sage/ext/stdsage.pxi index 3f3980c067f..c7960cae9cf 100644 --- a/src/sage/ext/stdsage.pxi +++ b/src/sage/ext/stdsage.pxi @@ -1,24 +1,20 @@ """ -Standard SAGE Pyrex Helper Code -""" +Standard C helper code for Cython modules -################################################################################ -# stdsage.pxi -# Standard useful stuff for SAGE modules to include: -# See stdsage.h for macros and stdsage.c for C functions. -# -# Each module currently gets its own copy of this, which is why -# we call the initialization code below. -# -################################################################################ +Standard useful stuff for Sage Cython modules to include: +See stdsage.h for macros and stdsage.c for C functions. -############################################################################### -# SAGE: System for Algebra and Geometry Experimentation +Each module currently gets its own copy of this, which is why +we call the initialization code below. +""" +#***************************************************************************** # Copyright (C) 2005, 2006 William Stein +# # Distributed under the terms of the GNU General Public License (GPL) -# The full text of the GPL is available at: +# 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/ -############################################################################### +#***************************************************************************** cdef extern from "stdsage.h": ctypedef void PyObject @@ -40,13 +36,13 @@ cdef extern from "stdsage.h": # Memory management cdef extern from "stdsage.h": - void sage_free(void *p) - void* sage_realloc(void *p, size_t n) - void* sage_malloc(size_t) - void* sage_calloc(size_t nmemb, size_t size) + void sage_free(void *p) nogil + void* sage_realloc(void *p, size_t n) nogil + void* sage_malloc(size_t) nogil + void* sage_calloc(size_t nmemb, size_t size) nogil + void init_memory_functions() nogil void init_csage() void init_csage_module() - void init_memory_functions() # Do this for every single module that links in stdsage. diff --git a/src/sage/libs/gap/util.pyx b/src/sage/libs/gap/util.pyx index 7c3bdd9178e..f9ef25fa1a2 100644 --- a/src/sage/libs/gap/util.pyx +++ b/src/sage/libs/gap/util.pyx @@ -321,24 +321,22 @@ cdef void hold_reference(libGAP_Obj obj): ### Error handler ########################################################## ############################################################################ -cdef extern from 'stdlib.h': - void abort() - include 'sage/ext/interrupt.pxi' +from cpython.exc cimport PyErr_SetObject cdef void error_handler(char* msg): """ The libgap error handler - We call ``abort()`` which causes us to jump back to the Sage + We call ``sig_error()`` which causes us to jump back to the Sage signal handler. Since we wrap libGAP C calls in ``sig_on`` / - ``sig_off`` blocks, this then jumps back to the ``sig_on`` and - raises a Python ``RuntimeError``. + ``sig_off`` blocks, this then jumps back to the ``sig_on`` where + the ``RuntimeError`` we raise here will be seen. """ msg_py = msg msg_py = msg_py.replace('For debugging hints type ?Recovery from NoMethodFound\n', '') - set_sage_signal_handler_message(msg_py) - abort() + PyErr_SetObject(RuntimeError, msg_py) + sig_error() ############################################################################ diff --git a/src/sage/libs/ntl/ntl_ZZ_pX.pyx b/src/sage/libs/ntl/ntl_ZZ_pX.pyx index 8afd2c3fd0a..a25073d82e2 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pX.pyx @@ -65,7 +65,8 @@ cdef class ntl_ZZ_pX: # See ntl_ZZ_pX.pxd for definition of data members def __init__(self, v = None, modulus = None): """ - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(ntl.ZZ(20)) sage: f = ntl.ZZ_pX([1,2,5,-9], c) sage: f @@ -141,7 +142,8 @@ cdef class ntl_ZZ_pX: def __reduce__(self): """ - TEST: + TESTS:: + sage: f = ntl.ZZ_pX([1,2,3,7], 5); f [1 2 3 2] sage: loads(dumps(f)) == f @@ -153,7 +155,8 @@ cdef class ntl_ZZ_pX: """ Return the string representation of self. - EXAMPLES: + EXAMPLES:: + sage: x = ntl.ZZ_pX([1,0,8],5) sage: x [1 0 3] @@ -171,7 +174,8 @@ cdef class ntl_ZZ_pX: """ Return a copy of self. - EXAMPLES: + EXAMPLES:: + sage: x = ntl.ZZ_pX([0,5,-3],11) sage: y = copy(x) sage: x == y @@ -190,7 +194,8 @@ cdef class ntl_ZZ_pX: """ Return the modulus for self. - EXAMPLES: + EXAMPLES:: + sage: x = ntl.ZZ_pX([0,5,3],17) sage: c = x.get_modulus_context() sage: y = ntl.ZZ_pX([5],c) @@ -227,11 +232,13 @@ cdef class ntl_ZZ_pX: r""" This method exists solely for automated testing of setitem_from_int(). - sage: c = ntl.ZZ_pContext(20) - sage: x = ntl.ZZ_pX([2, 3, 4], c) - sage: x._setitem_from_int_doctest(5, 42) - sage: x - [2 3 4 0 0 2] + TESTS:: + + sage: c = ntl.ZZ_pContext(20) + sage: x = ntl.ZZ_pX([2, 3, 4], c) + sage: x._setitem_from_int_doctest(5, 42) + sage: x + [2 3 4 0 0 2] """ self.c.restore_c() self.setitem_from_int(i, value) @@ -240,10 +247,12 @@ cdef class ntl_ZZ_pX: r""" Returns the ith entry of self. - sage: c = ntl.ZZ_pContext(20) - sage: x = ntl.ZZ_pX([2, 3, 4], c) - sage: x[1] - 3 + EXAMPLES:: + + sage: c = ntl.ZZ_pContext(20) + sage: x = ntl.ZZ_pX([2, 3, 4], c) + sage: x[1] + 3 """ cdef ntl_ZZ_p r r = PY_NEW(ntl_ZZ_p) @@ -277,15 +286,17 @@ cdef class ntl_ZZ_pX: r""" This method exists solely for automated testing of getitem_as_int(). - sage: c = ntl.ZZ_pContext(20) - sage: x = ntl.ZZ_pX([2, 3, 5, -7, 11], c) - sage: i = x._getitem_as_int_doctest(3) - sage: print i - 13 - sage: print type(i) - - sage: print x._getitem_as_int_doctest(15) - 0 + TESTS:: + + sage: c = ntl.ZZ_pContext(20) + sage: x = ntl.ZZ_pX([2, 3, 5, -7, 11], c) + sage: i = x._getitem_as_int_doctest(3) + sage: print i + 13 + sage: print type(i) + + sage: print x._getitem_as_int_doctest(15) + 0 """ # self.c.restore_c() # restored in getitem_as_int() return self.getitem_as_int(i) @@ -294,7 +305,8 @@ cdef class ntl_ZZ_pX: """ Return list of entries as a list of ntl_ZZ_p. - EXAMPLES: + EXAMPLES:: + sage: x = ntl.ZZ_pX([1,3,5],11) sage: x.list() [1, 3, 5] @@ -308,7 +320,8 @@ cdef class ntl_ZZ_pX: def __add__(ntl_ZZ_pX self, ntl_ZZ_pX other): """ - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: ntl.ZZ_pX(range(5),c) + ntl.ZZ_pX(range(6),c) [0 2 4 6 8 5] @@ -324,7 +337,8 @@ cdef class ntl_ZZ_pX: def __sub__(ntl_ZZ_pX self, ntl_ZZ_pX other): """ - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: ntl.ZZ_pX(range(5),c) - ntl.ZZ_pX(range(6),c) [0 0 0 0 0 15] @@ -340,7 +354,8 @@ cdef class ntl_ZZ_pX: def __mul__(ntl_ZZ_pX self, ntl_ZZ_pX other): """ - EXAMPLES: + EXAMPLES:: + sage: c1 = ntl.ZZ_pContext(20) sage: alpha = ntl.ZZ_pX(range(5), c1) sage: beta = ntl.ZZ_pX(range(6), c1) @@ -367,7 +382,8 @@ cdef class ntl_ZZ_pX: Compute quotient self / other, if the quotient is a polynomial. Otherwise an Exception is raised. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX([1,2,3], c) * ntl.ZZ_pX([4,5], c)**2 sage: g = ntl.ZZ_pX([4,5], c) @@ -403,15 +419,23 @@ cdef class ntl_ZZ_pX: If p is not prime this function may raise a RuntimeError due to division by a noninvertible element of ZZ_p. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(ntl.ZZ(17)) sage: f = ntl.ZZ_pX([2,4,6], c); g = ntl.ZZ_pX([2], c) sage: f % g # 0 [] - sage: f = ntl.ZZ_pX(range(10), c); g = ntl.ZZ_pX([-1,0,1], c) sage: f % g [3 8] + + sage: c = ntl.ZZ_pContext(ntl.ZZ(16)) + sage: f = ntl.ZZ_pX([2,4,6], c); g = ntl.ZZ_pX([2], c) + sage: f % g + Traceback (most recent call last): + ... + RuntimeError: ZZ_p: division by non-invertible element + """ if self.c is not other.c: raise ValueError, "You can not perform arithmetic with elements of different moduli." @@ -427,7 +451,8 @@ cdef class ntl_ZZ_pX: Returns the unique integral q and r such that self = q*other + r, if they exist. Otherwise raises an Exception. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX(range(10), c); g = ntl.ZZ_pX([-1,0,1], c) sage: q, r = f.quo_rem(g) @@ -450,7 +475,8 @@ cdef class ntl_ZZ_pX: """ Return f*f. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX([-1,0,1], c) sage: f*f @@ -467,7 +493,8 @@ cdef class ntl_ZZ_pX: """ Return the n-th nonnegative power of self. - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(20) sage: g = ntl.ZZ_pX([-1,0,1],c) sage: g**10 @@ -486,7 +513,8 @@ cdef class ntl_ZZ_pX: """ Decide whether or not self and other are equal. - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,2,3],c) sage: g = ntl.ZZ_pX([1,2,3,0],c) @@ -505,7 +533,8 @@ cdef class ntl_ZZ_pX: """ Return True exactly if this polynomial is 0. - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([0,0,0,20],c) sage: f.is_zero() @@ -523,7 +552,8 @@ cdef class ntl_ZZ_pX: """ Return True exactly if this polynomial is 1. - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,1],c) sage: f.is_one() @@ -539,7 +569,8 @@ cdef class ntl_ZZ_pX: """ Return True exactly if this polynomial is monic. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([2,0,0,1],c) sage: f.is_monic() @@ -561,7 +592,9 @@ cdef class ntl_ZZ_pX: def __neg__(self): """ Return the negative of self. - EXAMPLES: + + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([2,0,0,1],c) sage: -f @@ -577,7 +610,8 @@ cdef class ntl_ZZ_pX: Return the polynomial obtained by shifting all coefficients of this polynomial to the left n positions. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([2,0,0,1],c) sage: f @@ -603,7 +637,8 @@ cdef class ntl_ZZ_pX: Return the polynomial obtained by shifting all coefficients of this polynomial to the right n positions. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([2,0,0,1],c) sage: f @@ -653,7 +688,8 @@ cdef class ntl_ZZ_pX: Return the gcd d = gcd(a, b), where by convention the leading coefficient of d is >= 0. We uses a multi-modular algorithm. - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX([1,2,3],c) * ntl.ZZ_pX([4,5],c)**2 sage: g = ntl.ZZ_pX([1,1,1],c)**3 * ntl.ZZ_pX([1,2,3],c) @@ -677,14 +713,17 @@ cdef class ntl_ZZ_pX: function computes s and t such that: a*s + b*t = r; otherwise s and t are both 0. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX([1,2,3],c) * ntl.ZZ_pX([4,5],c)**2 sage: g = ntl.ZZ_pX([1,1,1],c)**3 * ntl.ZZ_pX([1,2,3],c) sage: f.xgcd(g) # nothing since they are not coprime ([6 12 1], [15 13 6 8 7 9], [4 13]) - In this example the input quadratic polynomials have a common root modulo 13. + In this example the input quadratic polynomials have a common + root modulo 13:: + sage: f = ntl.ZZ_pX([5,0,1],c) sage: g = ntl.ZZ_pX([18,0,1],c) sage: f.xgcd(g) @@ -707,7 +746,8 @@ cdef class ntl_ZZ_pX: Return the degree of this polynomial. The degree of the 0 polynomial is -1. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([5,0,1],c) sage: f.degree() @@ -729,7 +769,8 @@ cdef class ntl_ZZ_pX: """ Return the leading coefficient of this polynomial. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([3,6,9],c) sage: f.leading_coefficient() @@ -748,7 +789,8 @@ cdef class ntl_ZZ_pX: """ Return the constant coefficient of this polynomial. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([3,6,9],c) sage: f.constant_term() @@ -765,7 +807,8 @@ cdef class ntl_ZZ_pX: """ Set this polynomial to the monomial "x". - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([],c) sage: f.set_x() @@ -775,7 +818,8 @@ cdef class ntl_ZZ_pX: sage: f == g True - Though f and g are equal, they are not the same objects in memory: + Though f and g are equal, they are not the same objects in memory:: + sage: f is g False """ @@ -788,7 +832,8 @@ cdef class ntl_ZZ_pX: """ True if this is the polynomial "x". - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([],c) sage: f.set_x() @@ -811,21 +856,22 @@ cdef class ntl_ZZ_pX: (in which case self is reduced modulo c.p) or self.c.p should divide c.p (in which case self is lifted to something modulo c.p congruent to self modulo self.c.p) - EXAMPLES: - sage: a = ntl.ZZ_pX([412,181,991],5^4) - sage: a - [412 181 366] - sage: b = ntl.ZZ_pX([198,333,91],5^4) - sage: ap = a.convert_to_modulus(ntl.ZZ_pContext(5^2)) - sage: bp = b.convert_to_modulus(ntl.ZZ_pContext(5^2)) - sage: ap - [12 6 16] - sage: bp - [23 8 16] - sage: ap*bp - [1 9 8 24 6] - sage: (a*b).convert_to_modulus(ntl.ZZ_pContext(5^2)) - [1 9 8 24 6] + EXAMPLES:: + + sage: a = ntl.ZZ_pX([412,181,991],5^4) + sage: a + [412 181 366] + sage: b = ntl.ZZ_pX([198,333,91],5^4) + sage: ap = a.convert_to_modulus(ntl.ZZ_pContext(5^2)) + sage: bp = b.convert_to_modulus(ntl.ZZ_pContext(5^2)) + sage: ap + [12 6 16] + sage: bp + [23 8 16] + sage: ap*bp + [1 9 8 24 6] + sage: (a*b).convert_to_modulus(ntl.ZZ_pContext(5^2)) + [1 9 8 24 6] """ c.restore_c() cdef ntl_ZZ_pX ans = PY_NEW(ntl_ZZ_pX) @@ -839,7 +885,8 @@ cdef class ntl_ZZ_pX: """ Return the derivative of this polynomial. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,7,0,13],c) sage: f.derivative() @@ -859,8 +906,10 @@ cdef class ntl_ZZ_pX: NOTE: The roots are returned in a random order. EXAMPLES: + These computations use pseudo-random numbers, so we set the - seed for reproducible testing. + seed for reproducible testing. :: + sage: set_random_seed(0) sage: ntl.ZZ_pX([-1,0,0,0,0,1],5).factor() [([4 1], 5)] @@ -910,7 +959,8 @@ cdef class ntl_ZZ_pX: also that the result is not deterministic, i.e. the order of the roots returned is random. - EXAMPLES: + EXAMPLES:: + sage: ntl.ZZ_pX([-1,0,0,0,0,1],5).linear_roots() [1, 1, 1, 1, 1] sage: roots = ntl.ZZ_pX([-1,0,0,0,1],5).linear_roots() @@ -950,7 +1000,8 @@ cdef class ntl_ZZ_pX: of this polynomial. If hi is set then this function behaves as if this polynomial has degree hi. - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,2,3,4,5],c) sage: f.reverse() @@ -974,7 +1025,8 @@ cdef class ntl_ZZ_pX: Return the truncation of this polynomial obtained by removing all terms of degree >= m. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,2,3,4,5],c) sage: f.truncate(3) @@ -1002,7 +1054,8 @@ cdef class ntl_ZZ_pX: """ Return self*other but with terms of degree >= m removed. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,2,3,4,5],c) sage: g = ntl.ZZ_pX([10],c) @@ -1023,7 +1076,8 @@ cdef class ntl_ZZ_pX: """ Return self*self but with terms of degree >= m removed. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,2,3,4,5],c) sage: f.square_and_truncate(4) @@ -1044,7 +1098,8 @@ cdef class ntl_ZZ_pX: Compute and return the inverse of self modulo $x^m$. The constant term of self must be a unit. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,2,3,4,5,6,7],c) sage: f.invert_and_truncate(20) @@ -1121,7 +1176,8 @@ cdef class ntl_ZZ_pX: Return self*other % modulus. The modulus must be monic with deg(modulus) > 0, and self and other must have smaller degree. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(ntl.ZZ(20)) sage: modulus = ntl.ZZ_pX([1,2,0,1],c) # must be monic sage: g = ntl.ZZ_pX([-1,0,1],c) @@ -1141,7 +1197,8 @@ cdef class ntl_ZZ_pX: The modulus must be monic, and of positive degree degree bigger than the degree of self. - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,2,0,3],c) sage: mod = ntl.ZZ_pX([5,3,-1,1,1],c) @@ -1162,13 +1219,15 @@ cdef class ntl_ZZ_pX: monomial x modulo this polynomial for i = 0, ..., deg(f)-1. This polynomial must be monic. - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,2,0,3,0,1],c) sage: f.trace_list() [5, 0, 14, 0, 10] - The input polynomial must be monic or a ValueError is raised: + The input polynomial must be monic or a ValueError is raised:: + sage: c=ntl.ZZ_pContext(20) sage: f = ntl.ZZ_pX([1,2,0,3,0,2],c) sage: f.trace_list() @@ -1189,7 +1248,8 @@ cdef class ntl_ZZ_pX: """ Return the resultant of self and other. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX([17,0,1,1],c) sage: g = ntl.ZZ_pX([34,-17,18,2],c) @@ -1210,14 +1270,16 @@ cdef class ntl_ZZ_pX: modulus must be monic, and of positive degree strictly greater than the degree of self. - EXAMPLE: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX([1,2,0,3],c) sage: mod = ntl.ZZ_pX([-5,2,0,0,1],c) sage: f.norm_mod(mod) 11 - The norm is the constant term of the characteristic polynomial. + The norm is the constant term of the characteristic polynomial:: + sage: f.charpoly_mod(mod) [11 1 8 14 1] """ @@ -1237,7 +1299,8 @@ cdef class ntl_ZZ_pX: $$ where m = deg(a), and lc(a) is the leading coefficient of a. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(ntl.ZZ(17)) sage: f = ntl.ZZ_pX([1,2,0,3],c) sage: f.discriminant() @@ -1258,7 +1321,8 @@ cdef class ntl_ZZ_pX: the modulus. The modulus must be monic of degree bigger than self. - EXAMPLES: + EXAMPLES:: + sage: c=ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX([1,2,0,3],c) sage: mod = ntl.ZZ_pX([-5,2,0,0,1],c) @@ -1277,15 +1341,17 @@ cdef class ntl_ZZ_pX: modulus. The modulus must be monic of degree bigger than self. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX([0,0,1],c) sage: g = f*f sage: f.charpoly_mod(g) [0 0 0 0 1] - However, since $f^2 = 0$ modulo $g$, its minimal polynomial - is of degree $2$. + However, since `f^2 = 0` modulo `g`, its minimal polynomial + is of degree `2`:: + sage: f.minpoly_mod(g) [0 0 1] """ @@ -1299,7 +1365,8 @@ cdef class ntl_ZZ_pX: """ Reset this polynomial to 0. Changes this polynomial in place. - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX([1,2,3],c) sage: f @@ -1319,7 +1386,8 @@ cdef class ntl_ZZ_pX: the polynomial grows. (You might save a millisecond with this function.) - EXAMPLES: + EXAMPLES:: + sage: c = ntl.ZZ_pContext(17) sage: f = ntl.ZZ_pX([1,2,3],c) sage: f.preallocate_space(20) diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index de1a1baeddf..34b13807d9f 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -296,10 +296,10 @@ cdef class Polynomial_integer_dense_flint(Polynomial): x0 = x[0] if isinstance(x, Polynomial_integer_dense_flint): f = self._new() - _sig_on + sig_on() fmpz_poly_compose(f.__poly, self.__poly, \ ( x0).__poly) - _sig_off + sig_off() return f if isinstance(x0, (int, long)): x0 = Integer(x0) diff --git a/src/sage/tests/interrupt.pyx b/src/sage/tests/interrupt.pyx index 2f01eafab2e..9cba6554391 100644 --- a/src/sage/tests/interrupt.pyx +++ b/src/sage/tests/interrupt.pyx @@ -5,6 +5,7 @@ AUTHORS: - Jeroen Demeyer (2010-09-29): initial version (#10030) + - Jeroen Demeyer (2013-11-04): wrap some tests within nogil (#15352) """ #***************************************************************************** # Copyright (C) 2010 Jeroen Demeyer @@ -19,9 +20,9 @@ AUTHORS: import signal cdef extern from '../tests/c_lib.h': - void ms_sleep(long ms) - void signal_after_delay(int signum, long ms) - void signals_after_delay(int signum, long ms, long interval, int n) + void ms_sleep(long ms) nogil + void signal_after_delay(int signum, long ms) nogil + void signals_after_delay(int signum, long ms, long interval, int n) nogil cdef extern from *: ctypedef int volatile_int "volatile int" @@ -30,6 +31,7 @@ cdef extern from *: include 'sage/ext/signals.pxi' include 'sage/ext/interrupt.pxi' include 'sage/ext/stdsage.pxi' +from cpython cimport PyErr_SetString # Default delay in milliseconds before raising signals @@ -39,11 +41,11 @@ cdef long DEFAULT_DELAY = 200 ######################################################################## # C helper functions # ######################################################################## -cdef void infinite_loop(): +cdef void infinite_loop() nogil: while True: pass -cdef void infinite_malloc_loop(): +cdef void infinite_malloc_loop() nogil: cdef size_t s = 1 while True: sage_free(sage_malloc(s)) @@ -53,7 +55,7 @@ cdef void infinite_malloc_loop(): # Dereference a NULL pointer on purpose. This signals a SIGSEGV on most # systems, but on older Mac OS X and possibly other systems, this # signals a SIGBUS instead. In any case, this should give some signal. -cdef void dereference_null_pointer(): +cdef void dereference_null_pointer() nogil: cdef long* ptr = (0) ptr[0] += 1 @@ -64,17 +66,14 @@ cdef void dereference_null_pointer(): class return_exception: """ Decorator class which makes a function *return* an exception which - is raised, to simplify doctests raising exceptions. This can be - used to work around a bug in the doctesting script where a - ``KeyboardInterrupt`` always causes the doctester to stop, even if - the expected output of a doctest is a ``KeyboardInterrupt``. + is raised, to simplify doctests raising exceptions. EXAMPLES:: sage: from sage.tests.interrupt import return_exception sage: @return_exception - ... def raise_interrupt(): - ... raise KeyboardInterrupt("just testing") + ....: def raise_interrupt(): + ....: raise KeyboardInterrupt("just testing") sage: raise_interrupt() KeyboardInterrupt('just testing',) """ @@ -104,10 +103,10 @@ def interrupt_after_delay(ms_delay = 500): sage: import sage.tests.interrupt sage: try: - ... sage.tests.interrupt.interrupt_after_delay() - ... factor(10^1000 + 3) - ... except KeyboardInterrupt: - ... print "Caught KeyboardInterrupt" + ....: sage.tests.interrupt.interrupt_after_delay() + ....: factor(10^1000 + 3) + ....: except KeyboardInterrupt: + ....: print "Caught KeyboardInterrupt" Caught KeyboardInterrupt """ signal_after_delay(SIGINT, ms_delay) @@ -115,6 +114,8 @@ def interrupt_after_delay(ms_delay = 500): ######################################################################## # Test basic macros from c_lib/headers/interrupt.h # +# Since these are supposed to work without the GIL, we do all tests # +# (if possible) within a "with nogil" block # ######################################################################## def test_sig_off(): """ @@ -123,8 +124,9 @@ def test_sig_off(): sage: from sage.tests.interrupt import * sage: test_sig_off() """ - sig_on() - sig_off() + with nogil: + sig_on() + sig_off() @return_exception def test_sig_on(long delay = DEFAULT_DELAY): @@ -135,9 +137,10 @@ def test_sig_on(long delay = DEFAULT_DELAY): sage: test_sig_on() KeyboardInterrupt() """ - signal_after_delay(SIGINT, delay) - sig_on() - infinite_loop() + with nogil: + signal_after_delay(SIGINT, delay) + sig_on() + infinite_loop() def test_sig_str(long delay = DEFAULT_DELAY): """ @@ -149,9 +152,10 @@ def test_sig_str(long delay = DEFAULT_DELAY): ... RuntimeError: Everything ok! """ - sig_str("Everything ok!") - signal_after_delay(SIGABRT, delay) - infinite_loop() + with nogil: + sig_str("Everything ok!") + signal_after_delay(SIGABRT, delay) + infinite_loop() cdef c_test_sig_on_cython(): sig_on() @@ -169,7 +173,7 @@ def test_sig_on_cython(long delay = DEFAULT_DELAY): signal_after_delay(SIGINT, delay) c_test_sig_on_cython() -cdef int c_test_sig_on_cython_except() except 42: +cdef int c_test_sig_on_cython_except() nogil except 42: sig_on() infinite_loop() @@ -182,10 +186,11 @@ def test_sig_on_cython_except(long delay = DEFAULT_DELAY): sage: test_sig_on_cython_except() KeyboardInterrupt() """ - signal_after_delay(SIGINT, delay) - c_test_sig_on_cython_except() + with nogil: + signal_after_delay(SIGINT, delay) + c_test_sig_on_cython_except() -cdef void c_test_sig_on_cython_except_all() except *: +cdef void c_test_sig_on_cython_except_all() nogil except *: sig_on() infinite_loop() @@ -198,8 +203,9 @@ def test_sig_on_cython_except_all(long delay = DEFAULT_DELAY): sage: test_sig_on_cython_except_all() KeyboardInterrupt() """ - signal_after_delay(SIGINT, delay) - c_test_sig_on_cython_except_all() + with nogil: + signal_after_delay(SIGINT, delay) + c_test_sig_on_cython_except_all() @return_exception def test_sig_check(long delay = DEFAULT_DELAY): @@ -212,7 +218,8 @@ def test_sig_check(long delay = DEFAULT_DELAY): """ signal_after_delay(SIGINT, delay) while True: - sig_check() + with nogil: + sig_check() @return_exception def test_sig_check_inside_sig_on(long delay = DEFAULT_DELAY): @@ -223,11 +230,16 @@ def test_sig_check_inside_sig_on(long delay = DEFAULT_DELAY): sage: test_sig_check_inside_sig_on() KeyboardInterrupt() """ - signal_after_delay(SIGINT, delay) - sig_on() - while True: - sig_check() + with nogil: + signal_after_delay(SIGINT, delay) + sig_on() + while True: + sig_check() + +######################################################################## +# Test sig_retry() and sig_error() # +######################################################################## def test_sig_retry(): """ TESTS:: @@ -238,11 +250,12 @@ def test_sig_retry(): """ cdef volatile_int v = 0 - sig_on() - if v < 10: - v = v + 1 - sig_retry() - sig_off() + with nogil: + sig_on() + if v < 10: + v = v + 1 + sig_retry() + sig_off() return v @return_exception @@ -256,12 +269,27 @@ def test_sig_retry_and_signal(long delay = DEFAULT_DELAY): """ cdef volatile_int v = 0 + with nogil: + sig_on() + if v < 10: + v = v + 1 + sig_retry() + signal_after_delay(SIGINT, delay) + infinite_loop() + +@return_exception +def test_sig_error(): + """ + TESTS:: + + sage: from sage.tests.interrupt import * + sage: test_sig_error() + ValueError('some error',) + """ sig_on() - if v < 10: - v = v + 1 - sig_retry() - signal_after_delay(SIGINT, delay) - infinite_loop() + PyErr_SetString(ValueError, "some error") + sig_error() + ######################################################################## # Test no_except macros # @@ -325,52 +353,12 @@ def test_sig_check_no_except(long delay = DEFAULT_DELAY): sage: test_sig_check_no_except() KeyboardInterrupt() """ - signal_after_delay(SIGINT, delay) - while True: - if not sig_check_no_except(): - cython_check_exception() - return 0 # fail - - -######################################################################## -# Test deprecated macros for backwards compatibility # -######################################################################## -def test_old_sig_off(): - """ - TESTS:: - - sage: from sage.tests.interrupt import * - sage: test_old_sig_off() - """ - _sig_on - _sig_off - -@return_exception -def test_old_sig_on(long delay = DEFAULT_DELAY): - """ - TESTS:: - - sage: from sage.tests.interrupt import * - sage: test_old_sig_on() - KeyboardInterrupt() - """ - signal_after_delay(SIGINT, delay) - _sig_on - infinite_loop() - -def test_old_sig_str(long delay = DEFAULT_DELAY): - """ - TESTS:: - - sage: from sage.tests.interrupt import * - sage: test_old_sig_str() - Traceback (most recent call last): - ... - RuntimeError: Everything ok! - """ - _sig_str("Everything ok!") - signal_after_delay(SIGABRT, delay) - infinite_loop() + with nogil: + signal_after_delay(SIGINT, delay) + while True: + if not sig_check_no_except(): + cython_check_exception() + break # fail ######################################################################## @@ -386,9 +374,10 @@ def test_signal_segv(long delay = DEFAULT_DELAY): ... SignalError: Segmentation fault """ - sig_on() - signal_after_delay(SIGSEGV, delay) - infinite_loop() + with nogil: + sig_on() + signal_after_delay(SIGSEGV, delay) + infinite_loop() def test_signal_fpe(long delay = DEFAULT_DELAY): """ @@ -400,9 +389,10 @@ def test_signal_fpe(long delay = DEFAULT_DELAY): ... FloatingPointError: Floating point exception """ - sig_on() - signal_after_delay(SIGFPE, delay) - infinite_loop() + with nogil: + sig_on() + signal_after_delay(SIGFPE, delay) + infinite_loop() def test_signal_ill(long delay = DEFAULT_DELAY): """ @@ -414,9 +404,10 @@ def test_signal_ill(long delay = DEFAULT_DELAY): ... SignalError: Illegal instruction """ - sig_on() - signal_after_delay(SIGILL, delay) - infinite_loop() + with nogil: + sig_on() + signal_after_delay(SIGILL, delay) + infinite_loop() def test_signal_abrt(long delay = DEFAULT_DELAY): """ @@ -428,9 +419,10 @@ def test_signal_abrt(long delay = DEFAULT_DELAY): ... RuntimeError: Aborted """ - sig_on() - signal_after_delay(SIGABRT, delay) - infinite_loop() + with nogil: + sig_on() + signal_after_delay(SIGABRT, delay) + infinite_loop() def test_signal_bus(long delay = DEFAULT_DELAY): """ @@ -442,9 +434,10 @@ def test_signal_bus(long delay = DEFAULT_DELAY): ... SignalError: Bus error """ - sig_on() - signal_after_delay(SIGBUS, delay) - infinite_loop() + with nogil: + sig_on() + signal_after_delay(SIGBUS, delay) + infinite_loop() def test_signal_quit(long delay = DEFAULT_DELAY): """ @@ -459,9 +452,10 @@ def test_signal_quit(long delay = DEFAULT_DELAY): ---...--- """ # The sig_on() shouldn't make a difference for SIGQUIT - sig_on() - signal_after_delay(SIGQUIT, delay) - infinite_loop() + with nogil: + sig_on() + signal_after_delay(SIGQUIT, delay) + infinite_loop() ######################################################################## @@ -480,8 +474,9 @@ def test_dereference_null_pointer(): ... SignalError: ... """ - sig_on() - dereference_null_pointer() + with nogil: + sig_on() + dereference_null_pointer() def unguarded_dereference_null_pointer(): """ @@ -500,7 +495,8 @@ def unguarded_dereference_null_pointer(): Sage will now terminate. ------------------------------------------------------------------------ """ - dereference_null_pointer() + with nogil: + dereference_null_pointer() def test_abort(): """ @@ -512,8 +508,9 @@ def test_abort(): ... RuntimeError: Aborted """ - sig_on() - abort() + with nogil: + sig_on() + abort() def unguarded_abort(): """ @@ -531,7 +528,8 @@ def unguarded_abort(): Sage will now terminate. ------------------------------------------------------------------------ """ - abort() + with nogil: + abort() def test_bad_str(long delay = DEFAULT_DELAY): """ @@ -550,9 +548,10 @@ def test_bad_str(long delay = DEFAULT_DELAY): ------------------------------------------------------------------------ """ cdef char* s = (16) - sig_str(s) - signal_after_delay(SIGILL, delay) - infinite_loop() + with nogil: + sig_str(s) + signal_after_delay(SIGILL, delay) + infinite_loop() ######################################################################## @@ -567,10 +566,11 @@ def test_sig_on_cython_after_delay(long delay = DEFAULT_DELAY): sage: test_sig_on_cython_after_delay() KeyboardInterrupt() """ - signal_after_delay(SIGINT, delay) - ms_sleep(delay * 2) # We get signaled during this sleep - sig_on() # The signal should be detected here - abort() # This should not be reached + with nogil: + signal_after_delay(SIGINT, delay) + ms_sleep(delay * 2) # We get signaled during this sleep + sig_on() # The signal should be detected here + abort() # This should not be reached def test_sig_on_inside_try(long delay = DEFAULT_DELAY): """ @@ -580,9 +580,10 @@ def test_sig_on_inside_try(long delay = DEFAULT_DELAY): sage: test_sig_on_inside_try() """ try: - sig_on() - signal_after_delay(SIGABRT, delay) - infinite_loop() + with nogil: + sig_on() + signal_after_delay(SIGABRT, delay) + infinite_loop() except RuntimeError: pass @@ -610,29 +611,15 @@ def test_interrupt_bomb(int n = 100, int p = 10): i = 0 while True: try: - sig_on() - infinite_loop() + with nogil: + sig_on() + infinite_loop() except KeyboardInterrupt: i = i + 1 except RuntimeError: break print "Received %i/%i interrupts"%(i,n*p) -def test_sig_on_loop(): - """ - Test sig_on() and sig_off() in a loop, this is also useful for - benchmarking. - - TESTS:: - - sage: from sage.tests.interrupt import * - sage: test_sig_on_loop() - """ - cdef int i - for i in range(10000000): - sig_on() - sig_off() - # Special thanks to Robert Bradshaw for suggesting the try/finally # construction. -- Jeroen Demeyer def test_try_finally_signal(long delay = DEFAULT_DELAY): @@ -706,13 +693,14 @@ def test_sig_block(long delay = DEFAULT_DELAY): cdef volatile_int v = 0 try: - sig_on() - sig_block() - signal_after_delay(SIGINT, delay) - ms_sleep(delay * 2) # We get signaled during this sleep - v = 42 - sig_unblock() # Here, the interrupt will be handled - sig_off() + with nogil: + sig_on() + sig_block() + signal_after_delay(SIGINT, delay) + ms_sleep(delay * 2) # We get signaled during this sleep + v = 42 + sig_unblock() # Here, the interrupt will be handled + sig_off() except KeyboardInterrupt: return v @@ -727,15 +715,14 @@ def test_sig_block_outside_sig_on(long delay = DEFAULT_DELAY): sage: test_sig_block_outside_sig_on() 'Success' """ - signal_after_delay(SIGINT, delay) - cdef int v = 0 - cdef int* p = &v + with nogil: + signal_after_delay(SIGINT, delay) - # sig_block()/sig_unblock() shouldn't do anything - # since we're outside of sig_on() - sig_block() - ms_sleep(delay * 2) # We get signaled during this sleep - sig_unblock() + # sig_block()/sig_unblock() shouldn't do anything + # since we're outside of sig_on() + sig_block() + ms_sleep(delay * 2) # We get signaled during this sleep + sig_unblock() try: sig_on() # Interrupt caught here @@ -755,10 +742,11 @@ def test_signal_during_malloc(long delay = DEFAULT_DELAY): sage: for i in range(4): # Several times to reduce chances of false positive ... test_signal_during_malloc() """ - signal_after_delay(SIGINT, delay) try: - sig_on() - infinite_malloc_loop() + with nogil: + signal_after_delay(SIGINT, delay) + sig_on() + infinite_malloc_loop() except KeyboardInterrupt: pass @@ -776,9 +764,10 @@ def sig_on_bench(): sage: sig_on_bench() """ cdef int i - for i in range(1000000): - sig_on() - sig_off() + with nogil: + for i in range(1000000): + sig_on() + sig_off() def sig_check_bench(): """ @@ -790,8 +779,9 @@ def sig_check_bench(): sage: sig_check_bench() """ cdef int i - for i in range(1000000): - sig_check() + with nogil: + for i in range(1000000): + sig_check() ######################################################################## @@ -809,9 +799,10 @@ def test_sighup(long delay = DEFAULT_DELAY): sage: test_sighup() SystemExit() """ - signal_after_delay(SIGHUP, delay) - while True: - sig_check() + with nogil: + signal_after_delay(SIGHUP, delay) + while True: + sig_check() @return_exception def test_sigterm_and_sigint(long delay = DEFAULT_DELAY): @@ -825,16 +816,17 @@ def test_sigterm_and_sigint(long delay = DEFAULT_DELAY): sage: test_sigterm_and_sigint() SystemExit() """ - sig_on() - sig_block() - signal_after_delay(SIGHUP, delay) - signal_after_delay(SIGINT, delay) - # 3 sleeps to ensure both signals arrive - ms_sleep(delay) - ms_sleep(delay) - ms_sleep(delay) - sig_unblock() - sig_off() + with nogil: + sig_on() + sig_block() + signal_after_delay(SIGHUP, delay) + signal_after_delay(SIGINT, delay) + # 3 sleeps to ensure both signals arrive + ms_sleep(delay) + ms_sleep(delay) + ms_sleep(delay) + sig_unblock() + sig_off() def test_graceful_exit(): r""" From 089e5877a848c00699d40db8235c3a62ac4f4b28 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 18 Dec 2013 15:28:00 +0100 Subject: [PATCH 175/206] Revert parts of 67a76032fda3ee140c3bc52422020ef722cbee52 --- src/doc/en/developer/coding_in_cython.rst | 188 +++++++++++----------- 1 file changed, 92 insertions(+), 96 deletions(-) diff --git a/src/doc/en/developer/coding_in_cython.rst b/src/doc/en/developer/coding_in_cython.rst index dccd708be72..abc54656843 100644 --- a/src/doc/en/developer/coding_in_cython.rst +++ b/src/doc/en/developer/coding_in_cython.rst @@ -189,9 +189,10 @@ version with a type declaration, by changing ``def is2pow(n):`` to Interrupt and Signal Handling ============================= -When writing Cython code for Sage, special care must be taken to -ensure the code can be interrupted with ``CTRL-C``. Since Cython -optimizes for speed, Cython normally does not check for interrupts. +When writing Cython code for Sage, special care must be taken to ensure +the code can be interrupted with ``CTRL-C``. +Since Cython optimizes for speed, +Cython normally does not check for interrupts. For example, code like the following cannot be interrupted: .. skip @@ -201,9 +202,9 @@ For example, code like the following cannot be interrupted: sage: cython('while True: pass') # DON'T DO THIS While this is running, pressing ``CTRL-C`` has no effect. The only -way out is to kill the Sage process. On certain systems, you can -still quit Sage by typing ``CTRL-\`` (sending a Quit signal) instead -of ``CTRL-C``. +way out is to kill the Sage process. +On certain systems, you can still quit Sage by typing ``CTRL-\`` +(sending a Quit signal) instead of ``CTRL-C``. Using ``sig_on()`` and ``sig_off()`` ------------------------------------ @@ -226,13 +227,14 @@ In practice your function will probably look like:: # (some harmless post-processing) return something -You can put ``sig_on()`` and ``sig_off()`` in all kinds of Cython -functions: ``def``, ``cdef`` or ``cpdef``. You cannot put them in -pure Python code (i.e. files with extension ``.py``). +You can put ``sig_on()`` and ``sig_off()`` in all kinds of Cython functions: +``def``, ``cdef`` or ``cpdef``. +You cannot put them in pure Python code (i.e. files with extension ``.py``). -It is possible to put ``sig_on()`` and ``sig_off()`` in different -functions, provided that ``sig_off()`` is called before the function -which calls ``sig_on()`` returns. The following code is *invalid*:: +It is possible to put ``sig_on()`` and ``sig_off()`` in different functions, +provided that ``sig_off()`` is called before the function which calls +``sig_on()`` returns. +The following code is *invalid*:: # INVALID code because we return from function foo() # without calling sig_off() first. @@ -254,29 +256,30 @@ interactively:: sig_on() return foo() -For clarity however, it is best to avoid this. One good example where -the above makes sense is the ``new_gen()`` function in -:ref:`section-pari-library`. +For clarity however, it is best to avoid this. +One good example where the above makes sense is the ``new_gen()`` +function in :ref:`section-pari-library`. -A common mqistake is to put ``sig_off()`` towards the end of a +A common mistake is to put ``sig_off()`` towards the end of a function (before the ``return``) when the function has multiple -``return`` statements. So make sure there is a ``sig_off()`` before -*every* ``return`` (and also before every ``raise``). +``return`` statements. +So make sure there is a ``sig_off()`` before *every* ``return`` +(and also before every ``raise``). .. WARNING:: - The code inside ``sig_on()`` should be pure C or Cython code. If - you call Python code, an interrupt is likely to mess up Python. + The code inside ``sig_on()`` should be pure C or Cython code. + If you call Python code, an interrupt is likely to mess up Python. Also, when an interrupt occurs inside ``sig_on()``, code execution - immediately stops without cleaning up. For example, any memory - allocated inside ``sig_on()`` is lost. See :ref:`advanced-sig` - for ways to deal with this. + immediately stops without cleaning up. + For example, any memory allocated inside ``sig_on()`` is lost. + See :ref:`advanced-sig` for ways to deal with this. -When the user presses ``CTRL-C`` inside ``sig_on()``, execution will -jump back to ``sig_on()`` (the first one if there is a stack) and -``sig_on()`` will raise ``KeyboardInterrupt``. These can be handled -just like other Python exceptions:: +When the user presses ``CTRL-C`` inside ``sig_on()``, execution will jump back +to ``sig_on()`` (the first one if there is a stack) and ``sig_on()`` +will raise ``KeyboardInterrupt``. These can be handled just like other +Python exceptions:: def catch_interrupts(): try: @@ -287,16 +290,17 @@ just like other Python exceptions:: # (handle interrupt) Certain C libraries in Sage are written in a way that they will raise -Python exceptions: NTL and PARI are examples of this. NTL can raise -``RuntimeError`` and PARI can raise ``PariError``. Since these use -the ``sig_on()`` mechanism, these exceptions can be caught just like -the ``KeyboardInterrupt`` in the example above. - -It is possible to stack ``sig_on()`` and ``sig_off()``. If you do -this, the effect is exactly the same as if only the outer +Python exceptions: NTL and PARI are examples of this. +NTL can raise ``RuntimeError`` and PARI can raise ``PariError``. +Since these use the ``sig_on()`` mechanism, +these exceptions can be caught just like the ``KeyboardInterrupt`` +in the example above. + +It is possible to stack ``sig_on()`` and ``sig_off()``. +If you do this, the effect is exactly the same as if only the outer ``sig_on()``/``sig_off()`` was there. The inner ones will just change -a reference counter and otherwise do nothing. Make sure that the -number of ``sig_on()`` calls equal the number of ``sig_off()`` calls:: +a reference counter and otherwise do nothing. Make sure that the number +of ``sig_on()`` calls equal the number of ``sig_off()`` calls:: def stack_sig_on(): sig_on() @@ -308,10 +312,9 @@ number of ``sig_on()`` calls equal the number of ``sig_off()`` calls:: sig_off() Extra care must be taken with exceptions raised inside ``sig_on()``. -The problem is that, if you do not do anything special, the -``sig_off()`` will never be called if there is an exception. If you -need to *raise* an exception yourself, call a ``sig_off()`` before -it:: +The problem is that, if you do not do anything special, the ``sig_off()`` +will never be called if there is an exception. +If you need to *raise* an exception yourself, call a ``sig_off()`` before it:: def raising_an_exception(): sig_on() @@ -339,11 +342,11 @@ Other Signals ------------- Apart from handling interrupts, ``sig_on()`` provides more general -signal handling. Indeed, if the code inside ``sig_on()`` would -generate a segmentation fault or call the C function ``abort()`` (or -more generally, raise any of SIGSEGV, SIGILL, SIGABRT, SIGFPE, -SIGBUS), this is caught by the interrupt framework and a -``RuntimeError`` is raised:: +signal handling. +Indeed, if the code inside ``sig_on()`` would generate +a segmentation fault or call the C function ``abort()`` +(or more generally, raise any of SIGSEGV, SIGILL, SIGABRT, SIGFPE, SIGBUS), +this is caught by the interrupt framework and a ``RuntimeError`` is raised:: cdef extern from 'stdlib.h': void abort() @@ -360,16 +363,17 @@ SIGBUS), this is caught by the interrupt framework and a ... RuntimeError: Aborted -This exception can then be caught as explained above. This means that -``abort()`` can be used as an alternative to exceptions within -``sig_on()``/``sig_off()``. A segmentation fault unguarded by -``sig_on()`` would simply terminate Sage. +This exception can then be caught as explained above. +This means that ``abort()`` can be used +as an alternative to exceptions within ``sig_on()``/``sig_off()``. +A segmentation fault unguarded by ``sig_on()`` would simply terminate Sage. Instead of ``sig_on()``, there is also a function ``sig_str(s)``, -which takes a C string ``s`` as argument. It behaves the same as -``sig_on()``, except that the string ``s`` will be used as a string -for the exception. ``sig_str(s)`` should still be closed by -``sig_off()``. Example Cython code:: +which takes a C string ``s`` as argument. +It behaves the same as ``sig_on()``, except that the string ``s`` +will be used as a string for the exception. +``sig_str(s)`` should still be closed by ``sig_off()``. +Example Cython code:: cdef extern from 'stdlib.h': void abort() @@ -389,27 +393,26 @@ Executing this gives: RuntimeError: custom error message With regard to ordinary interrupts (i.e. SIGINT), ``sig_str(s)`` -behaves the same as ``sig_on()``: a simple ``KeyboardInterrupt`` is -raised. - +behaves the same as ``sig_on()``: +a simple ``KeyboardInterrupt`` is raised. .. _advanced-sig: Advanced Functions ------------------ -There are several more specialized functions for dealing with -interrupts. The function ``sig_check()`` behaves exactly as -``sig_on(); sig_off()`` (except that ``sig_check()`` is faster since -it does not involve a ``setjmp()`` call). +There are several more specialized functions for dealing with interrupts. +The function ``sig_check()`` behaves exactly as ``sig_on(); sig_off()`` +(except that ``sig_check()`` is faster since it does not involve a ``setjmp()`` call). -``sig_check()`` can be used to check for pending interrupts. If an -interrupt happens outside of a ``sig_on()``/``sig_off()`` block, it -will be caught by the next ``sig_check()`` or ``sig_on()``. +``sig_check()`` can be used to check for pending interrupts. +If an interrupt happens outside of a ``sig_on()``/``sig_off()`` block, +it will be caught by the next ``sig_check()`` or ``sig_on()``. The typical use case for ``sig_check()`` is within tight loops doing -complicated stuff (mixed Python and Cython code, potentially raising -exceptions). It gives more control, because a ``KeyboardInterrupt`` +complicated stuff +(mixed Python and Cython code, potentially raising exceptions). +It gives more control, because a ``KeyboardInterrupt`` can *only* be raised during ``sig_check()``:: def sig_check_example(): @@ -418,18 +421,19 @@ can *only* be raised during ``sig_check()``:: sig_check() As mentioned above, ``sig_on()`` makes no attempt to clean anything up -(restore state or freeing memory) when an interrupt occurs. In fact, -it would be impossible for ``sig_on()`` to do that. If you want to -add some cleanup code, use ``sig_on_no_except()`` for this. This -function behaves *exactly* like ``sig_on()``, except that any -exception raised (either ``KeyboardInterrupt`` or ``RuntimeError``) is -not yet passed to Python. Essentially, the exception is there, but we -prevent Cython from looking for the exception. Then -``cython_check_exception()`` can be used to make Cython look for the -exception. - -Normally, ``sig_on_no_except()`` returns 1. If a signal was caught -and an exception raised, ``sig_on_no_except()`` instead returns 0. +(restore state or freeing memory) when an interrupt occurs. +In fact, it would be impossible for ``sig_on()`` to do that. +If you want to add some cleanup code, use ``sig_on_no_except()`` +for this. This function behaves *exactly* like ``sig_on()``, except that +any exception raised (either ``KeyboardInterrupt`` or ``RuntimeError``) +is not yet passed to Python. Essentially, the exception is there, but +we prevent Cython from looking for the exception. +Then ``cython_check_exception()`` can be used to make Cython look +for the exception. + +Normally, ``sig_on_no_except()`` returns 1. +If a signal was caught and an exception raised, ``sig_on_no_except()`` +instead returns 0. The following example shows how to use ``sig_on_no_except()``:: def no_except_example(): @@ -443,8 +447,8 @@ The following example shows how to use ``sig_on_no_except()``:: # (some long computation, messing up internal state of objects) sig_off() -There is also a function ``sig_str_no_except(s)`` which is analogous -to ``sig_str(s)``. +There is also a function ``sig_str_no_except(s)`` +which is analogous to ``sig_str(s)``. .. NOTE:: @@ -458,31 +462,23 @@ Testing Interrupts When writing :ref:`section-docstrings`, one sometimes wants to check that certain code can be interrupted in a clean way. -In the module ``sage.tests.interrupt``, there is a function -``interrupt_after_delay(ms_delay = 500)`` which can be used to test interrupts. -That function simulates a ``CTRL-C`` (by sending SIGINT) -after ``ms_delay`` milliseconds. +The best way to do this is to use :func:`alarm`. The following is an example of a doctest demonstrating that the function ``factor()`` can be interrupted:: - sage: import sage.tests.interrupt - sage: try: - ... sage.tests.interrupt.interrupt_after_delay() - ... factor(10^1000 + 3) - ... except KeyboardInterrupt: - ... print "ok!" - ok! - + sage: alarm(0.5); factor(10^1000 + 3) + Traceback (most recent call last): + ... + AlarmInterrupt Unpickling Cython Code ====================== -Pickling for python classes and extension classes, such as cython, is -different. This is discussed in the `python pickling -documentation`_. For the unpickling of extension classes you need to -write a :meth:`__reduce__` method which typically returns a tuple -``(f, args,...)`` such that ``f(*args)`` returns (a copy of) the +Pickling for python classes and extension classes, such as cython, is different. +This is discussed in the `python pickling documentation`_. For the unpickling of +extension classes you need to write a :meth:`__reduce__` method which typically +returns a tuple ``(f, args, ...)`` such that ``f(*args)`` returns (a copy of) the original object. As an example, the following code snippet is the :meth:`~sage.rings.integer.Integer.__reduce__` method from :class:`sage.rings.integer.Integer`:: From 9923d11e79ef3ebe565e45feaf51444ec30ff40e Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 18 Dec 2013 15:32:04 +0100 Subject: [PATCH 176/206] Update signals section of developer manual --- src/doc/en/developer/coding_in_cython.rst | 68 +++++++++++++++-------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/src/doc/en/developer/coding_in_cython.rst b/src/doc/en/developer/coding_in_cython.rst index abc54656843..253dc23d9fe 100644 --- a/src/doc/en/developer/coding_in_cython.rst +++ b/src/doc/en/developer/coding_in_cython.rst @@ -337,16 +337,41 @@ exceptions raised by subroutines inside the ``try``:: sig_off() return something +Using ``sig_check()`` +--------------------- + +The function ``sig_check()`` behaves exactly as ``sig_on(); sig_off()`` +(except that ``sig_check()`` is faster since it does not involve a ``setjmp()`` call). + +``sig_check()`` can be used to check for pending interrupts. +If an interrupt happens outside of a ``sig_on()``/``sig_off()`` block, +it will be caught by the next ``sig_check()`` or ``sig_on()``. + +The typical use case for ``sig_check()`` is within tight loops doing +complicated stuff +(mixed Python and Cython code, potentially raising exceptions). +It is safer to use and gives more control, because a +``KeyboardInterrupt`` can *only* be raised during ``sig_check()``:: + + def sig_check_example(): + for x in foo: + # (one loop iteration which does not take a long time) + sig_check() Other Signals ------------- Apart from handling interrupts, ``sig_on()`` provides more general signal handling. -Indeed, if the code inside ``sig_on()`` would generate +It handles :func:`alarm` time-outs by raising an ``AlarmInterrupt`` +(inherited from ``KeyboardInterrupt``) exception. + +If the code inside ``sig_on()`` would generate a segmentation fault or call the C function ``abort()`` (or more generally, raise any of SIGSEGV, SIGILL, SIGABRT, SIGFPE, SIGBUS), -this is caught by the interrupt framework and a ``RuntimeError`` is raised:: +this is caught by the interrupt framework and an exception is raised +(``RuntimeError`` for SIGABRT, ``FloatingPointError`` for SIGFPE +and the custom exception ``SignalError``, based on ``BaseException``, otherwise):: cdef extern from 'stdlib.h': void abort() @@ -364,9 +389,8 @@ this is caught by the interrupt framework and a ``RuntimeError`` is raised:: RuntimeError: Aborted This exception can then be caught as explained above. -This means that ``abort()`` can be used -as an alternative to exceptions within ``sig_on()``/``sig_off()``. -A segmentation fault unguarded by ``sig_on()`` would simply terminate Sage. +A segmentation fault or ``abort()`` unguarded by ``sig_on()`` would +simply terminate Sage. Instead of ``sig_on()``, there is also a function ``sig_str(s)``, which takes a C string ``s`` as argument. @@ -402,24 +426,6 @@ Advanced Functions ------------------ There are several more specialized functions for dealing with interrupts. -The function ``sig_check()`` behaves exactly as ``sig_on(); sig_off()`` -(except that ``sig_check()`` is faster since it does not involve a ``setjmp()`` call). - -``sig_check()`` can be used to check for pending interrupts. -If an interrupt happens outside of a ``sig_on()``/``sig_off()`` block, -it will be caught by the next ``sig_check()`` or ``sig_on()``. - -The typical use case for ``sig_check()`` is within tight loops doing -complicated stuff -(mixed Python and Cython code, potentially raising exceptions). -It gives more control, because a ``KeyboardInterrupt`` -can *only* be raised during ``sig_check()``:: - - def sig_check_example(): - for x in foo: - # (one loop iteration which does not take a long time) - sig_check() - As mentioned above, ``sig_on()`` makes no attempt to clean anything up (restore state or freeing memory) when an interrupt occurs. In fact, it would be impossible for ``sig_on()`` to do that. @@ -472,6 +478,22 @@ the function ``factor()`` can be interrupted:: ... AlarmInterrupt +Releasing the Global Interpreter Lock (GIL) +------------------------------------------- + +All the functions related to interrupt and signal handling are declared +``nogil``. This means that they can be used in Cython code inside +``with nogil`` blocks. If ``sig_on()`` needs to raise an exception, +the GIL is temporarily acquired internally. + +.. WARNING:: + + The GIL should never be released or acquired inside a ``sig_on()`` + block. If you want to use a ``with nogil`` block, put both + ``sig_on()`` and ``sig_off()`` inside that block. When in doubt, + choose to use ``sig_check()`` instead, which is always safe to use. + + Unpickling Cython Code ====================== From db039591798a068f5f9e892ac5af5448954d4229 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 18 Dec 2013 17:08:56 +0100 Subject: [PATCH 177/206] Document sig_error() --- src/doc/en/developer/coding_in_cython.rst | 51 +++++++++++++++++++---- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/doc/en/developer/coding_in_cython.rst b/src/doc/en/developer/coding_in_cython.rst index 253dc23d9fe..14c9aceeeac 100644 --- a/src/doc/en/developer/coding_in_cython.rst +++ b/src/doc/en/developer/coding_in_cython.rst @@ -290,11 +290,12 @@ Python exceptions:: # (handle interrupt) Certain C libraries in Sage are written in a way that they will raise -Python exceptions: NTL and PARI are examples of this. -NTL can raise ``RuntimeError`` and PARI can raise ``PariError``. -Since these use the ``sig_on()`` mechanism, -these exceptions can be caught just like the ``KeyboardInterrupt`` -in the example above. +Python exceptions: +libGAP and NTL can raise ``RuntimeError`` and PARI can raise ``PariError``. +These exceptions behave exactly like the ``KeyboardInterrupt`` +in the example above and can be caught by putting the ``sig_on()`` +inside a ``try``/``except`` block. +See :ref:`sig-error` to see how this is implmented. It is possible to stack ``sig_on()`` and ``sig_off()``. If you do this, the effect is exactly the same as if only the outer @@ -420,6 +421,36 @@ With regard to ordinary interrupts (i.e. SIGINT), ``sig_str(s)`` behaves the same as ``sig_on()``: a simple ``KeyboardInterrupt`` is raised. +.. _sig-error: + +Error Handling in C Libraries +----------------------------- + +Some C libraries can produce errors and use some sort of callback +mechanism to report errors: an external error handling function needs +to be set up which will be called by the C library if an error occurs. + +The function ``sig_error()`` can be used to deal with these errors. +This function should be called within a ``sig_on()`` block (otherwise +Sage will crash hard) after raising a Python exception. You need to +use the `Python/C API `_ +for this and call ``sig_error()`` after calling some variant of +:func:`PyErr_SetObject`. Even within Cython, you cannot use the ``raise`` +statement, because then the ``sig_error()`` will never be executed. +The call to ``sig_error()`` will use the ``sig_on()`` machinery +such that the exception will be seen by ``sig_on()``. + +A typical error handler implemented in Cython would look as follows:: + + include "sage/ext/interrupt.pxi" + from cpython.exc cimport PyErr_SetString + + cdef void error_handler(char *msg): + PyErr_SetString(RuntimeError, msg) + sig_error() + +In Sage, this mechanism is used for libGAP, NTL and PARI. + .. _advanced-sig: Advanced Functions @@ -486,6 +517,11 @@ All the functions related to interrupt and signal handling are declared ``with nogil`` blocks. If ``sig_on()`` needs to raise an exception, the GIL is temporarily acquired internally. +If you use C libraries without the GIL and you want to raise an exception +after :ref:`sig_error() `, remember to acquire the GIL +while raising the exception. Within Cython, you can use a +`with gil context `_. + .. WARNING:: The GIL should never be released or acquired inside a ``sig_on()`` @@ -493,12 +529,11 @@ the GIL is temporarily acquired internally. ``sig_on()`` and ``sig_off()`` inside that block. When in doubt, choose to use ``sig_check()`` instead, which is always safe to use. - Unpickling Cython Code ====================== -Pickling for python classes and extension classes, such as cython, is different. -This is discussed in the `python pickling documentation`_. For the unpickling of +Pickling for Python classes and extension classes, such as Cython, is different. +This is discussed in the `Python pickling documentation`_. For the unpickling of extension classes you need to write a :meth:`__reduce__` method which typically returns a tuple ``(f, args, ...)`` such that ``f(*args)`` returns (a copy of) the original object. As an example, the following code snippet is the From 99450f433e75a1f151457462369ecc4942cd6a18 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 18 Dec 2013 17:12:58 +0100 Subject: [PATCH 178/206] Mention that PARI supports conversion from tuples --- src/sage/libs/pari/gen_py.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/libs/pari/gen_py.py b/src/sage/libs/pari/gen_py.py index 29aae82b2ad..fb2b94f6bf9 100644 --- a/src/sage/libs/pari/gen_py.py +++ b/src/sage/libs/pari/gen_py.py @@ -53,8 +53,8 @@ def pari(x): sage: pari.set_real_precision(15) 35 - Conversion from matrices is supported, but not from vectors or tuples; - use lists instead:: + Conversion from matrices is supported, but not from vectors; + use lists or tuples instead:: sage: a = pari(matrix(2,3,[1,2,3,4,5,6])); a, a.type() ([1, 2, 3; 4, 5, 6], 't_MAT') @@ -66,6 +66,8 @@ def pari(x): PariError: syntax error, unexpected ')', expecting )-> or ',' sage: b = pari(list(v)); b,b.type() ([1.20000000000000, 3.40000000000000, 5.60000000000000], 't_VEC') + sage: b = pari(tuple(v)); b, b.type() + ([1.20000000000000, 3.40000000000000, 5.60000000000000], 't_VEC') Some more exotic examples:: From ee5e39e59a08b3d96471fc368819784baf020eb8 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 18 Dec 2013 18:16:04 +0100 Subject: [PATCH 179/206] Allow default arguments in closures --- src/sage/libs/pari/gen.pyx | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 17054d1d7e4..104e5d7123b 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -7434,6 +7434,19 @@ cdef class gen(sage.structure.element.RingElement): sage: f.eval(5, 6) 30 + Default arguments work, missing arguments are treated as zero + (like in GP):: + + sage: f = pari("(x, y, z=1.0) -> [x, y, z]") + sage: f(1, 2, 3) + [1, 2, 3] + sage: f(1, 2) + [1, 2, 1.00000000000000] + sage: f(1) + [1, 0, 1.00000000000000] + sage: f() + [0, 0, 1.00000000000000] + Using keyword arguments, we can substitute in more complicated objects, for example a number field:: @@ -7458,8 +7471,8 @@ cdef class gen(sage.structure.element.RingElement): # XXX: use undocumented internals to get arity of closure # (In PARI 2.6, there is closure_arity() which does this) arity = self.g[1] - if nargs != arity: - raise TypeError("PARI closure takes exactly %d argument%s (%d given)"%( + if nargs > arity: + raise TypeError("PARI closure takes at most %d argument%s (%d given)"%( arity, "s" if (arity!=1) else "", nargs)) t0 = objtogen(args) pari_catch_sig_on() @@ -7521,11 +7534,7 @@ cdef class gen(sage.structure.element.RingElement): sage: pari('() -> 42')(1,2,3) Traceback (most recent call last): ... - TypeError: PARI closure takes exactly 0 arguments (3 given) - sage: pari('n -> n')() - Traceback (most recent call last): - ... - TypeError: PARI closure takes exactly 1 argument (0 given) + TypeError: PARI closure takes at most 0 arguments (3 given) sage: pari('n -> n')(n=2) Traceback (most recent call last): ... From 679310bd938fd20812c638c0e73e1c7ce3e60eef Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 18 Dec 2013 22:02:30 +0000 Subject: [PATCH 180/206] gen.eval() documentation: mention that f(x) == f.eval(x) --- src/sage/libs/pari/gen.pyx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 104e5d7123b..1f3e72debf2 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -7389,6 +7389,11 @@ cdef class gen(sage.structure.element.RingElement): sage: f.eval(x=2) 5 + The notation ``f(x)`` is an alternative for ``f.eval(x)``:: + + sage: f(3) == f.eval(3) + True + Evaluating multivariate polynomials:: sage: f = pari('y^2 + x^3') From 02dd0dcb216bc8a09c541d20bf37a7d3f54971c4 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 19 Dec 2013 08:31:46 -0500 Subject: [PATCH 181/206] removed isinteger option in delsarte_bound_hamming_space, as (mathematically) incorrect. --- src/sage/coding/delsarte_bounds.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sage/coding/delsarte_bounds.py b/src/sage/coding/delsarte_bounds.py index a546045c960..8f573377183 100644 --- a/src/sage/coding/delsarte_bounds.py +++ b/src/sage/coding/delsarte_bounds.py @@ -103,8 +103,7 @@ def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc = 0): p.add_constraint(sum([A[r] for r in xrange(n+1)]), max=maxc) return A, p -def delsarte_bound_hamming_space(n, d, q, - isinteger=False, return_data=False, solver="PPL"): +def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL"): """ Find the classical Delsarte bound [1]_ on codes in Hamming space ``H_q^n`` of minimal distance ``d`` @@ -118,9 +117,6 @@ def delsarte_bound_hamming_space(n, d, q, - ``q`` -- the size of the alphabet - - ``isinteger`` -- if ``True``, uses an integer programming solver (ILP), rather - that an LP solver. Can be very slow if set to ``True``. - - ``return_data`` -- if ``True``, return a triple ``(W,LP,bound)``, where ``W`` is a weights vector, and ``LP`` the Delsarte bound LP; both of them are Sage LP data. ``W`` need not be a weight distribution of a code, or, @@ -170,7 +166,7 @@ def delsarte_bound_hamming_space(n, d, q, """ from sage.numerical.mip import MIPSolverException - A, p = _delsarte_LP_building(n, d, 0, q, isinteger, solver) + A, p = _delsarte_LP_building(n, d, 0, q, isinteger=False, solver) try: bd=p.solve() except MIPSolverException, exc: From 3f7ff3a96a680318c2a51f64910dee014396c5c4 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 19 Dec 2013 08:44:45 -0500 Subject: [PATCH 182/206] also changed the order of options, as requested in comment 1, and few other minor edits --- src/sage/coding/delsarte_bounds.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/sage/coding/delsarte_bounds.py b/src/sage/coding/delsarte_bounds.py index 8f573377183..a3c98e11718 100644 --- a/src/sage/coding/delsarte_bounds.py +++ b/src/sage/coding/delsarte_bounds.py @@ -119,8 +119,7 @@ def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL"): - ``return_data`` -- if ``True``, return a triple ``(W,LP,bound)``, where ``W`` is a weights vector, and ``LP`` the Delsarte bound LP; both of them are Sage LP - data. ``W`` need not be a weight distribution of a code, or, - if ``isinteger==False``, even have integer entries. + data. ``W`` need not be a weight distribution of a code. - ``solver`` -- the LP/ILP solver to be used. Defaults to ``PPL``. It is arbitrary precision, thus there will be no rounding errors. With other solvers @@ -166,7 +165,7 @@ def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL"): """ from sage.numerical.mip import MIPSolverException - A, p = _delsarte_LP_building(n, d, 0, q, isinteger=False, solver) + A, p = _delsarte_LP_building(n, d, 0, q, False, solver) try: bd=p.solve() except MIPSolverException, exc: @@ -181,7 +180,7 @@ def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL"): return bd def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, - isinteger=False, return_data=False, solver="PPL"): + return_data=False, solver="PPL", isinteger=False): """ Find the Delsarte LP bound on ``F_{q_base}``-dimension of additive codes in Hamming space ``H_q^n`` of minimal distance ``d`` with minimal distance of the dual @@ -204,9 +203,6 @@ def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, - ``q_base`` -- if ``0``, the code is assumed to be nonlinear. Otherwise, ``q=q_base^m`` and the code is linear over ``F_{q_base}``. - - ``isinteger`` -- if ``True``, uses an integer programming solver (ILP), rather - that an LP solver. Can be very slow if set to ``True``. - - ``return_data`` -- if ``True``, return a triple ``(W,LP,bound)``, where ``W`` is a weights vector, and ``LP`` the Delsarte bound LP; both of them are Sage LP data. ``W`` need not be a weight distribution of a code, or, @@ -216,6 +212,9 @@ def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, precision, thus there will be no rounding errors. With other solvers (see :class:`MixedIntegerLinearProgram` for the list), you are on your own! + - ``isinteger`` -- if ``True``, uses an integer programming solver (ILP), rather + that an LP solver. Can be very slow if set to ``True``. + EXAMPLES: The bound on dimension of linear `F_2`-codes of length 11 and minimal distance 6:: From 7d0dcfa2c2e38548b619b4fa9ac91806df7e838c Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 19 Dec 2013 14:49:10 +0100 Subject: [PATCH 183/206] Improve handling of make targets sage, csage, extcode, scripts --- build/deps | 36 ++++++++++++++++++++---------------- build/install | 4 ++-- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/build/deps b/build/deps index 8579f484d20..e799cf0e5c3 100644 --- a/build/deps +++ b/build/deps @@ -109,10 +109,10 @@ all-sage: \ $(INST)/$(NCURSES) \ $(INST)/$(ZLIB) \ $(INST)/$(ZNPOLY) \ - scripts \ - sage \ - csage \ - extcode + $(INST)/sage \ + $(INST)/csage \ + $(EXTCODE) \ + $(SCRIPTS) # TOOLCHAIN consists of dependencies determined by build/install, # including for example the GCC package. @@ -133,7 +133,7 @@ toolchain-deps: # Everything needed to start up Sage using "./sage". Of course, not # every part of Sage will work. It does not include Maxima for example. -SAGERUNTIME = scripts sage $(INST)/$(SAGENB) $(INST)/$(IPYTHON) $(INST)/$(GAP) +SAGERUNTIME = $(SCRIPTS) $(INST)/sage $(INST)/$(SAGENB) $(INST)/$(IPYTHON) ############################################################################### # Building the base system @@ -308,7 +308,7 @@ $(INST)/$(SQLITE): $(INST)/$(READLINE) +$(PIPE) "$(SAGE_SPKG) $(SQLITE) 2>&1" "tee -a $(SAGE_LOGS)/$(SQLITE).log" # To build SageTeX, you just need Python, but to test (SAGE_CHECK=yes) -# SageTeX, you actually need to run sage, produce plots,... +# SageTeX, you actually need to run Sage, produce plots,... $(INST)/$(SAGETEX): $(INST)/$(PYTHON) \ $(SAGERUNTIME) $(INST)/$(MAXIMA) $(INST)/$(SCIPY) \ $(INST)/$(MATPLOTLIB) $(INST)/$(PIL) $(INST)/$(TACHYON) @@ -418,7 +418,7 @@ $(INST)/$(PYGMENTS): $(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) # on the one hand, programs needed for the build/install process of the # Sage library (e.g. JINJA2), and on the # other hand all dependencies for Cython files (e.g. PARI, NTL, MPIR). -sage: \ +$(INST)/sage: \ $(INST)/$(ATLAS) \ $(INST)/$(CEPHES) \ $(INST)/$(CLIQUER) \ @@ -456,21 +456,25 @@ sage: \ $(INST)/$(SINGULAR) \ $(INST)/$(SYMMETRICA) \ $(INST)/$(ZNPOLY) \ - csage - $(PIPE) '{ if [ -z "$(SAGE_INSTALL_FETCH_ONLY)" ]; then cd $(SAGE_SRC) && . ./bin/sage-env && time python setup.py install; fi; } 2>&1' 'tee -a $(SAGE_LOGS)/sage-$(SAGE_VERSION).log' - -scripts: $(SCRIPT_TARGETS) - -extcode: $(EXTCODE_TARGETS) - -csage: $(INST)/$(SCONS) \ + $(INST)/csage + if [ -z "$$SAGE_INSTALL_FETCH_ONLY" ]; then \ + cd $(SAGE_SRC) && . bin/sage-env && \ + $(PIPE) 'time python setup.py install 2>&1' 'tee -a $(SAGE_LOGS)/sage-$(SAGE_VERSION).log' && \ + touch $@; \ + fi + +$(INST)/csage: $(INST)/$(SCONS) \ $(INST)/$(MPIR) \ $(INST)/$(NTL) \ $(INST)/$(PARI) \ $(INST)/$(POLYBORI) \ $(INST)/$(PYNAC) \ $(INST)/$(PYTHON) - $(PIPE) '{ if [ -z "$(SAGE_INSTALL_FETCH_ONLY)" ]; then cd $(SAGE_SRC)/c_lib && . ../bin/sage-env && time scons -Q install; fi; } 2>&1' 'tee -a $(SAGE_LOGS)/csage-$(SAGE_VERSION).log' + if [ -z "$$SAGE_INSTALL_FETCH_ONLY" ]; then \ + cd $(SAGE_SRC) && . bin/sage-env && cd c_lib && \ + $(PIPE) 'time scons -Q install 2>&1' 'tee -a $(SAGE_LOGS)/csage-$(SAGE_VERSION).log' && \ + touch $@; \ + fi $(INST)/ccache: $(BASE) $(INST)/$(ZLIB) +$(PIPE) "$(SAGE_SPKG) ccache 2>&1" "tee -a $(SAGE_LOGS)/ccache.log" diff --git a/build/install b/build/install index 440db863a34..f16ba2c0fa4 100755 --- a/build/install +++ b/build/install @@ -490,7 +490,7 @@ for file in "$SAGE_SRC/bin/"*; do echo >&3 " \$(SAGE_SRC)${file#$SAGE_SRC} \\" done echo >&3 -echo >&3 'SCRIPT_TARGETS = \' +echo >&3 'SCRIPTS = \' for file in "$SAGE_SRC/bin/"*; do echo >&3 " \$(SAGE_LOCAL)${file#$SAGE_SRC} \\" done @@ -500,7 +500,7 @@ for file in `find "$SAGE_SRC"/ext -type f`; do echo >&3 " \$(SAGE_SRC)${file#$SAGE_SRC} \\" done echo >&3 -echo >&3 'EXTCODE_TARGETS = \' +echo >&3 'EXTCODE = \' for file in `find "$SAGE_SRC"/ext -type f`; do echo >&3 " \$(SAGE_EXTCODE)${file#$SAGE_SRC/ext} \\" done From 3015a963048482bfb6be8f2e61f0cda351b0010a Mon Sep 17 00:00:00 2001 From: "J. H. Palmieri" Date: Wed, 23 Oct 2013 18:29:38 +0000 Subject: [PATCH 184/206] Update installation guide for installation with OS X Mavericks --- src/doc/en/installation/source.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 23f563e9326..3db6f3ad61b 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -166,8 +166,11 @@ Alternatively, if you have already installed `Xcode `_ (which at the time of writing is freely available in the Mac App Store, or through http://developer.apple.com/downloads/ provided you registered for an -Apple Developer account), -you can open Xcode's "Downloads" preference pane and install the command line +Apple Developer account), you can install the command line tools from +there: with OS X Mavericks, run the command ``xcode-select --install`` +from a Terminal window and click "Install" in the pop-up dialog +box. Using OS X Mountain Lion or earlier, run Xcode, open its "Downloads" +preference pane and install the command line tools from there. On pre-Lion OS X systems, the command line tools are not available as a separate download and you have to install the full-blown Xcode supporting your From 0a4240c84ab1575c8d4ac80cdebddc5442c9b943 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 19 Dec 2013 16:12:21 +0100 Subject: [PATCH 185/206] trac #15054: reviewer's comments --- src/sage/graphs/generators/smallgraphs.py | 8 ++++++++ src/sage/graphs/graph_generators.py | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index a966ddf898e..0acf5b26187 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -958,6 +958,10 @@ def BlanusaFirstSnarkGraph(): The Blanusa graphs are two snarks on 18 vertices and 27 edges. For more information on them, see the :wikipedia:`Blanusa_snarks`. + .. SEEALSO:: + + * :meth:`~sage.graphs.graph_generators.GraphGenerators.BlanusaSecondSnarkGraph`. + EXAMPLES:: sage: g = graphs.BlanusaFirstSnarkGraph() @@ -989,6 +993,10 @@ def BlanusaSecondSnarkGraph(): The Blanusa graphs are two snarks on 18 vertices and 27 edges. For more information on them, see the :wikipedia:`Blanusa_snarks`. + .. SEEALSO:: + + * :meth:`~sage.graphs.graph_generators.GraphGenerators.BlanusaFirstSnarkGraph`. + EXAMPLES:: sage: g = graphs.BlanusaSecondSnarkGraph() diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 776135d6ea4..5e739089ea9 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -125,7 +125,7 @@ def __append_to_doc(methods): "KrackhardtKiteGraph", "LjubljanaGraph", "M22Graph", - "Markstroem", + "MarkstroemGraph", "McGeeGraph", "McLaughlinGraph", "MeredithGraph", @@ -139,7 +139,7 @@ def __append_to_doc(methods): "SchlaefliGraph", "ShrikhandeGraph", "SimsGewirtzGraph", - "Sousselier", + "SousselierGraph", "SylvesterGraph", "SzekeresSnarkGraph", "ThomsenGraph", From 349d7a29b8e1ea8e9aa47447a9ff5c255b2e3484 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 19 Dec 2013 18:15:48 +0100 Subject: [PATCH 186/206] Trac #15445: review + broken doctests --- src/doc/en/constructions/linear_codes.rst | 12 ++++++------ src/sage/coding/code_constructions.py | 17 +++++++++++++++++ src/sage/matroids/catalog.py | 4 ++-- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/doc/en/constructions/linear_codes.rst b/src/doc/en/constructions/linear_codes.rst index 66ee91180d0..e40913b91a3 100644 --- a/src/doc/en/constructions/linear_codes.rst +++ b/src/doc/en/constructions/linear_codes.rst @@ -22,7 +22,7 @@ Sage can compute Hamming codes :: - sage: C = HammingCode(3,GF(3)) + sage: C = codes.HammingCode(3,GF(3)) sage: C Linear code of length 13, dimension 10 over Finite Field of size 3 sage: C.minimum_distance() @@ -46,7 +46,7 @@ the four Golay codes :: - sage: C = ExtendedTernaryGolayCode() + sage: C = codes.ExtendedTernaryGolayCode() sage: C Linear code of length 12, dimension 6 over Finite Field of size 3 sage: C.minimum_distance() @@ -74,7 +74,7 @@ a check matrix, and the dual code: :: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: Cperp = C.dual_code() sage: C; Cperp Linear code of length 7, dimension 4 over Finite Field of size 2 @@ -90,7 +90,7 @@ a check matrix, and the dual code: [0 0 0 1 1 1 1] sage: C.dual_code() Linear code of length 7, dimension 3 over Finite Field of size 2 - sage: C = HammingCode(3,GF(4,'a')) + sage: C = codes.HammingCode(3,GF(4,'a')) sage: C.dual_code() Linear code of length 21, dimension 3 over Finite Field in a of size 2^2 @@ -102,7 +102,7 @@ implemented. :: - sage: C = HammingCode(3,GF(2)) + sage: C = codes.HammingCode(3,GF(2)) sage: MS = MatrixSpace(GF(2),1,7) sage: F = GF(2); a = F.gen() sage: v1 = [a,a,F(0),a,a,F(0),a] @@ -120,7 +120,7 @@ can use the matplotlib package included with Sage: :: - sage: C = HammingCode(4,GF(2)) + sage: C = codes.HammingCode(4,GF(2)) sage: C Linear code of length 15, dimension 11 over Finite Field of size 2 sage: w = C.weight_distribution(); w diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index a2233750759..bc004135dd5 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -964,6 +964,15 @@ def HammingCode(r,F): 3 sage: C = codes.HammingCode(3,GF(4,'a')); C Linear code of length 21, dimension 18 over Finite Field in a of size 2^2 + + While the ``codes`` object now gathers all code constructors, + ``HammingCode`` is still available in the global namespace:: + + sage: HammingCode(3,GF(2)) + doctest:1: DeprecationWarning: This method soon will not be available in that way anymore. To use it, you can now call it by typing codes.HammingCode + See http://trac.sagemath.org/15445 for details. + Linear code of length 7, dimension 4 over Finite Field of size 2 + """ q = F.order() n = (q**r-1)/(q-1) @@ -1244,6 +1253,14 @@ def ReedSolomonCode(n,k,F,pts = None): sage: C.minimum_distance() 3 + While the ``codes`` object now gathers all code constructors, + ``ReedSolomonCode`` is still available in the global namespace:: + + sage: ReedSolomonCode(6,4,GF(7)) + doctest:1: DeprecationWarning: This method soon will not be available in that way anymore. To use it, you can now call it by typing codes.ReedSolomonCode + See http://trac.sagemath.org/15445 for details. + Linear code of length 6, dimension 4 over Finite Field of size 7 + REFERENCES: - [W] http://en.wikipedia.org/wiki/Reed-Solomon diff --git a/src/sage/matroids/catalog.py b/src/sage/matroids/catalog.py index 68c414e60de..a739d43de1b 100644 --- a/src/sage/matroids/catalog.py +++ b/src/sage/matroids/catalog.py @@ -1390,7 +1390,7 @@ def ExtendedBinaryGolayCode(): sage: M = matroids.named_matroids.ExtendedBinaryGolayCode() sage: C = LinearCode(M.representation()) - sage: C.is_permutation_equivalent(ExtendedBinaryGolayCode()) # long time + sage: C.is_permutation_equivalent(codes.ExtendedBinaryGolayCode()) # long time True sage: M.is_valid() True @@ -1426,7 +1426,7 @@ def ExtendedTernaryGolayCode(): sage: M = matroids.named_matroids.ExtendedTernaryGolayCode() sage: C = LinearCode(M.representation()) - sage: C.is_permutation_equivalent(ExtendedTernaryGolayCode()) # long time + sage: C.is_permutation_equivalent(codes.ExtendedTernaryGolayCode()) # long time True sage: M.is_valid() True From 02a4a872c173eb89f56b5d9b5dbcd8ba2a6a54c4 Mon Sep 17 00:00:00 2001 From: Charles Bouillaguet Date: Fri, 20 Dec 2013 09:41:00 +0100 Subject: [PATCH 187/206] bug fix : importing BooleanPolynomialRing at the top-level prevents sage from starting The import has been moved into (several) methods. It seems ugly, but it works --- src/sage/rings/polynomial/multi_polynomial_sequence.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 1af6a0a762b..47fb3877601 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -167,7 +167,6 @@ from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal from sage.rings.polynomial.multi_polynomial import is_MPolynomial from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.polynomial.pbori import BooleanPolynomialRing from sage.interfaces.singular import singular @@ -1021,6 +1020,7 @@ def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reduc Cryptology ePrint Archive: Report 2007/024. available at http://eprint.iacr.org/2007/024 """ + from sage.rings.polynomial.pbori import BooleanPolynomialRing from polybori import gauss_on_polys from polybori.ll import eliminate,ll_encode,ll_red_nf_redsb @@ -1099,6 +1099,7 @@ def _groebner_strategy(self): sage: F._groebner_strategy() """ + from sage.rings.polynomial.pbori import BooleanPolynomialRing R = self.ring() if not isinstance(R, BooleanPolynomialRing): @@ -1205,6 +1206,7 @@ def solve(self, algorithm='polybori', n=1, eliminate_linear_variables=True, ver [] """ + from sage.rings.polynomial.pbori import BooleanPolynomialRing from sage.modules.free_module import VectorSpace S = self From 1d5d52a76338cc9a11f2b9cbd56d5f5cced7e7f1 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 20 Dec 2013 12:51:43 +0100 Subject: [PATCH 188/206] trac #15553: Broken links in the doc of graph/ and numerical/ --- src/sage/graphs/distances_all_pairs.pyx | 4 +-- src/sage/graphs/generic_graph.py | 31 ++++++++++--------- src/sage/graphs/graph.py | 12 +++---- src/sage/graphs/graph_plot.py | 17 +++++----- src/sage/graphs/isgci.py | 4 +-- src/sage/graphs/line_graph.py | 9 +++--- .../numerical/backends/generic_backend.pyx | 2 +- src/sage/numerical/mip.pyx | 10 +++--- 8 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 727193adc7a..890180ac1fd 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -483,8 +483,8 @@ def is_distance_regular(G, parameters = False): .. SEEALSO:: - * :meth:`Graph.is_regular` - * :meth:`Graph.is_strongly_regular` + * :meth:`~sage.graphs.generic_graph.GenericGraph.is_regular` + * :meth:`~Graph.is_strongly_regular` EXAMPLES:: diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index a9a7c36bd03..33fd3e0d78a 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -1224,10 +1224,11 @@ def distance_matrix(self): .. WARNING:: The ordering of vertices in the matrix has no reason to correspond - to the order of vertices in :meth:`~Graph.vertices`. In particular, - if two integers `i,j` are vertices of a graph `G` with distance - matrix ``M``, then ``M[i][i]`` is not necessarily the distance - between vertices `i` and `j`. + to the order of vertices in + :meth:`~sage.graphs.generic_graph.GenericGraph.vertices`. In + particular, if two integers `i,j` are vertices of a graph `G` with + distance matrix ``M``, then ``M[i][i]`` is not necessarily the + distance between vertices `i` and `j`. EXAMPLES:: @@ -1250,8 +1251,8 @@ def distance_matrix(self): .. SEEALSO:: - * :meth:`~sage.graphs.generic_graph.distance_all_pairs` -- computes - the distance between any two vertices. + * :meth:`~sage.graphs.generic_graph.GenericGraph.distance_all_pairs` + -- computes the distance between any two vertices. """ from sage.matrix.constructor import matrix @@ -10828,9 +10829,10 @@ def is_circulant(self, certificate = False): lists_of_parameters)`` each element of ``lists_of_parameters`` can be used to define the graph as a circulant graph. - See the documentation of :meth:`graphs.CirculantGraph` and - :meth:`digraphs.CirculantGraph` for more information, and the examples - below. + See the documentation of + :func:`~sage.graphs.graph_generators.GraphGenerators.CirculantGraph` and + :meth:`~sage.graphs.digraph_generators.DiGraphGenerators.Circulant` for + more information, and the examples below. .. SEEALSO:: @@ -11562,7 +11564,7 @@ def distance_all_pairs(self, algorithm = "auto"): .. SEEALSO:: - * :meth:`~sage.graphs.generic_graph.distance_matrix` + * :meth:`~sage.graphs.generic_graph.GenericGraph.distance_matrix` """ if algorithm == "auto": if self.order() <= 20: @@ -14731,7 +14733,7 @@ def plot(self, **options): be set through the :class:`~sage.misc.decorators.options` mechanism. For more information on this different way to set default parameters, see the help of the :class:`options decorator - <~sage.misc.decorators.options>`. + `. - See also the :mod:`sage.graphs.graph_latex` module for ways to use LaTeX to produce an image of a graph. @@ -14932,8 +14934,8 @@ def show(self, **kwds): INPUT: This method accepts any option understood by - :meth:`~sage.graphs.generic_graph.plot` (graph-specific) or by - :meth:`sage.plot.graphics.Graphics.show`. + :meth:`~sage.graphs.generic_graph.GenericGraph.plot` (graph-specific) or + by :meth:`sage.plot.graphics.Graphics.show`. .. NOTE:: @@ -15094,7 +15096,6 @@ def plot3d(self, bgcolor=(1,1,1), - :meth:`plot` - :meth:`graphviz_string` - - :meth:`_color_by_label` """ import graph_plot layout_options = dict( (key,kwds[key]) for key in kwds.keys() if key in graph_plot.layout_options ) @@ -15935,7 +15936,7 @@ def eigenspaces(self, laplacian=False): For some graphs, some of the the eigenspaces are described exactly by vector spaces over a - :class:`~sage.rings.number_field.number_field.NumberField`. + :func:`~sage.rings.number_field.number_field.NumberField`. For numerical eigenvectors use :meth:`eigenvectors`. EXAMPLES:: diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 27b84a27efd..d9d884010c3 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -2821,7 +2821,7 @@ def is_semi_symmetric(self): - :meth:`~Graph.is_edge_transitive` - :meth:`~Graph.is_arc_transitive` - - :meth:`~Graph.is_half-transitive` + - :meth:`~Graph.is_half_transitive` EXAMPLES: @@ -4918,7 +4918,7 @@ def clique_maximum(self, algorithm="Cliquer"): - If ``algorithm = "MILP"``, the problem is solved through a Mixed Integer Linear Program. - (see :class:`MixedIntegerLinearProgram `) + (see :class:`~sage.numerical.mip.MixedIntegerLinearProgram`) .. NOTE:: @@ -4984,7 +4984,7 @@ def clique_number(self, algorithm="Cliquer", cliques=None): - If ``algorithm = "MILP"``, the problem is solved through a Mixed Integer Linear Program. - (see :class:`MixedIntegerLinearProgram `) + (see :class:`~sage.numerical.mip.MixedIntegerLinearProgram`) - ``cliques`` - an optional list of cliques that can be input if already computed. Ignored unless ``algorithm=="networkx"``. @@ -5164,7 +5164,7 @@ def independent_set(self, algorithm = "Cliquer", value_only = False, reduction_r * If ``algorithm = "MILP"``, the problem is solved through a Mixed Integer Linear Program. - (see :class:`MixedIntegerLinearProgram `) + (see :class:`~sage.numerical.mip.MixedIntegerLinearProgram`) - ``value_only`` -- boolean (default: ``False``). If set to ``True``, only the size of a maximum independent set is returned. Otherwise, @@ -5179,9 +5179,9 @@ def independent_set(self, algorithm = "Cliquer", value_only = False, reduction_r solver to be used. If set to ``None``, the default one is used. For more information on LP solvers and which default solver is used, see the method - :meth:`solve ` + :meth:`~sage.numerical.mip.MixedIntegerLinearProgram.solve` of the class - :class:`MixedIntegerLinearProgram `. + :class:`~sage.numerical.mip.MixedIntegerLinearProgram`. - ``verbosity`` -- non-negative integer (default: ``0``). Set the level of verbosity you want from the linear program solver. Since the diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index 3e307aeb1a7..924c82fb281 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -26,8 +26,8 @@ **Plot options** -Here is the list of options accepted by :meth:`GenericGraph.plot -` and the constructor of +Here is the list of options accepted by +:meth:`~sage.graphs.generic_graph.GenericGraph.plot` and the constructor of :class:`GraphPlot`. .. csv-table:: @@ -94,16 +94,17 @@ **Default options** This module defines two dictionaries containing default options for the -:meth:`GenericGraph.plot` and :meth:`GenericGraph.show` methods. These two -dictionaries are ``sage.graphs.graph_plot.DEFAULT_PLOT_OPTIONS`` and +:meth:`~sage.graphs.generic_graph.GenericGraph.plot` and +:meth:`~sage.graphs.generic_graph.GenericGraph.show` methods. These two dictionaries are +``sage.graphs.graph_plot.DEFAULT_PLOT_OPTIONS`` and ``sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS``, respectively. Obviously, these values are overruled when arguments are given explicitly. Here is how to define the default size of a graph drawing to be ``[6,6]``. The -first two calls to :meth:`~sage.graphs.generic_graph.show` use this option, -while the third does not (a value for ``figsize`` is explicitly given):: +first two calls to :meth:`~sage.graphs.generic_graph.GenericGraph.show` use this +option, while the third does not (a value for ``figsize`` is explicitly given):: sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = [6,6] sage: graphs.PetersenGraph().show() # long time @@ -696,7 +697,7 @@ def show(self, **kwds): information on default values of this method. - Any options not used by plot will be passed on to the - :meth:`~sage.plot.plot.Graphics.show` method. + :meth:`~sage.plot.graphics.Graphics.show` method. EXAMPLE:: @@ -719,7 +720,7 @@ def plot(self, **kwds): The options accepted by this method are to be found in the documentation of the :mod:`sage.graphs.graph_plot` module, and the - :meth:`~sage.plot.plot.Graphics.show` method. + :meth:`~sage.plot.graphics.Graphics.show` method. .. NOTE:: diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index 42f7e7b408e..5124bd24134 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -111,7 +111,7 @@ * - BinaryTrees - :meth:`~sage.graphs.graph_generators.GraphGenerators.BalancedTree`, - :meth:`~sage.graphs.generic_graph.GenericGraph.is_tree` + :meth:`~Graph.is_tree` * - Bipartite @@ -172,7 +172,7 @@ * - Tree - :meth:`~sage.graphs.graph_generators.GraphGenerators.trees`, - :meth:`~sage.graphs.generic_graph.GenericGraph.is_tree` + :meth:`~Graph.is_tree` * - UnitDisk - :meth:`~sage.graphs.graph_generators.GraphGenerators.IntervalGraph`, diff --git a/src/sage/graphs/line_graph.py b/src/sage/graphs/line_graph.py index 2da632478dc..c51ea5e3a68 100644 --- a/src/sage/graphs/line_graph.py +++ b/src/sage/graphs/line_graph.py @@ -161,10 +161,11 @@ def is_line_graph(g, certificate = False): This method sequentially tests each of the forbidden subgraphs in order to know whether the graph is a line graph, which is a very slow - method. It could eventually be replaced by :meth:`root_graph` when this - method will not require an exponential time to run on general graphs - anymore (see its documentation for more information on this problem)... - and if it can be improved to return negative certificates ! + method. It could eventually be replaced by + :func:`~sage.graphs.line_graph.root_graph` when this method will not + require an exponential time to run on general graphs anymore (see its + documentation for more information on this problem)... and if it can be + improved to return negative certificates ! .. NOTE:: diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 43df1dfafcf..662d0b770ef 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -848,7 +848,7 @@ cdef class GenericBackend: .. NOTE:: The list of available parameters is available at - :meth:`sage.numerical.mip.MixedIntegerlinearProgram.solver_parameter` + :meth:`~sage.numerical.mip.MixedIntegerLinearProgram.solver_parameter`. EXAMPLE:: diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 8045c3ad889..d6dd3f2f660 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -358,7 +358,7 @@ cdef class MixedIntegerLinearProgram(SageObject): """ Return the parent for all linear constraints - See :mod:`~sage.numerical.linear_constraints` for more + See :mod:`~sage.numerical.linear_functions` for more details. EXAMPLES:: @@ -739,11 +739,11 @@ cdef class MixedIntegerLinearProgram(SageObject): INPUT: All arguments given to this method are forwarded to the constructor of - the :class:`Polyhedron` class. + the :func:`Polyhedron` class. OUTPUT: - A :class:`Polyhedron` object whose `i`-th variable represents the `i`-th + A :func:`Polyhedron` object whose `i`-th variable represents the `i`-th variable of ``self``. .. warning:: @@ -1994,13 +1994,13 @@ cdef class MixedIntegerLinearProgram(SageObject): cpdef sum(self, L): r""" Efficiently computes the sum of a sequence of - :class:`~sage.numerical.linear_function.LinearFunction` elements + :class:`~sage.numerical.linear_functions.LinearFunction` elements INPUT: - ``mip`` -- the :class:`MixedIntegerLinearProgram` parent. - - ``L`` -- list of :class:`~sage.numerical.linear_function.LinearFunction` instances. + - ``L`` -- list of :class:`~sage.numerical.linear_functions.LinearFunction` instances. .. NOTE:: From 0cb642bc59e625e80c6a69e84949ca47dc4e8adc Mon Sep 17 00:00:00 2001 From: Luis Felipe Tabera Alonso Date: Fri, 20 Dec 2013 15:37:53 +0100 Subject: [PATCH 189/206] Trac #14186 coerce_binop errors with keyword arguments --- src/sage/structure/element.pyx | 37 +++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 8e9581e7404..b233a1ee85e 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -3205,11 +3205,22 @@ cdef class NamedBinopMethod: sage: from sage.structure.element import NamedBinopMethod sage: test_func = NamedBinopMethod(lambda x, y, **kwds: (x, y, kwds), '_add_') + sage: class test_class(Rational): + ....: def __init__(self,value): + ....: self.v = value + ....: @NamedBinopMethod + ....: def test_add(self, other, keyword='z'): + ....: return (self.v, other, keyword) Calls func directly if the two arguments have the same parent:: sage: test_func(1, 2) (1, 2, {}) + sage: x = test_class(1) + sage: x.test_add(1/2) + (1, 1/2, 'z') + sage: x.test_add(1/2, keyword=3) + (1, 1/2, 3) Passes through coercion and does a method lookup if the left operand is not the same:: @@ -3220,6 +3231,30 @@ cdef class NamedBinopMethod: (1, 2, {'algorithm': 'fast'}) sage: test_func(1, 1/2) 3/2 + sage: x.test_add(2) + (1, 2, 'z') + sage: x.test_add(2, keyword=3) + (1, 2, 3) + + A real example:: + + sage: R1=QQ['x,y'] + sage: R2=QQ['x,y,z'] + sage: f=R1(1) + sage: g=R1(2) + sage: h=R2(1) + sage: f.gcd(g) + 1 + sage: f.gcd(g,algorithm='modular') + 1 + sage: f.gcd(h) + 1 + sage: f.gcd(h,algorithm='modular') + 1 + sage: h.gcd(f) + 1 + sage: h.gcd(f,algorithm='modular') + 1 """ if y is None: if self._self is None: @@ -3230,7 +3265,7 @@ cdef class NamedBinopMethod: old_x = x x,y = coercion_model.canonical_coercion(x, y) if old_x is x: - return self._func(x,y, *kwds) + return self._func(x,y, **kwds) else: return getattr(x, self._name)(y, **kwds) else: From 55eb0aa40402e5fc61a4f7294420c4df3d5e303f Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 6 Dec 2013 23:21:13 -0500 Subject: [PATCH 190/206] Trac #12322: Add a doctest for the correct behavior introduced in trac #12737. --- src/sage/symbolic/expression.pyx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 573289d7edd..f19195650b3 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -7818,6 +7818,17 @@ cdef class Expression(CommutativeRingElement): sage: original - simplified 0 + The invalid simplification from :trac:`12322` should not occur + after :trac:`12737`:: + + sage: t = var('t') + sage: assume(t, 'complex') + sage: assumptions() + [t is complex] + sage: f = (1/2)*log(2*t) + (1/2)*log(1/t) + sage: f.simplify_full() + 1/2*log(2*t) - 1/2*log(t) + """ x = self x = x.simplify_factorial() From dcd3c9e8d900e7f7853df5bb9ee67137a979cf1c Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 11 Dec 2013 15:15:25 +0000 Subject: [PATCH 191/206] Run Jmol from current directory --- src/sage/interfaces/jmoldata.py | 76 ++++++++++----------------------- src/sage/interfaces/r.py | 8 +--- src/sage/plot/plot3d/base.pyx | 76 ++++++++++++++------------------- 3 files changed, 55 insertions(+), 105 deletions(-) diff --git a/src/sage/interfaces/jmoldata.py b/src/sage/interfaces/jmoldata.py index 92bf1545fa8..399b1beaeb2 100644 --- a/src/sage/interfaces/jmoldata.py +++ b/src/sage/interfaces/jmoldata.py @@ -21,34 +21,12 @@ from sage.structure.sage_object import SageObject -from sage.misc.misc import tmp_filename from sage.misc.misc import SAGE_LOCAL, DOT_SAGE, sage_makedirs +from sage.misc.temporary_file import tmp_filename import subprocess import os -class JmolDataError(Exception): - - def __init__(self, value): - """ - TEST:: - - sage: from sage.interfaces.jmoldata import JmolDataError - sage: print(JmolDataError("test of init").value) - test of init - """ - self.value = value - - def __str__(self): - """ - TEST:: - - sage: from sage.interfaces.jmoldata import JmolDataError - sage: print str(JmolDataError("test of error")) - 'test of error' - """ - return repr(self.value) - class JmolData(SageObject): r""" .. todo:: @@ -145,8 +123,8 @@ def export_image(self, sage: JData = JmolData() sage: script = "load =1lcd;display DNA;moveto 0.0 { -473 -713 -518 59.94} 100.0 0.0 0.0 {21.17 26.72 27.295} 27.544636 {0.0 0.0 0.0} -25.287832 64.8414 0.0;" sage: testfile = tmp_filename(ext="DNA.png") - sage: JData.export_image(targetfile=testfile,datafile=script,image_type="PNG") # optional -- internet - sage: print os.path.exists(testfile) # optional -- internet + sage: JData.export_image(targetfile=testfile,datafile=script,image_type="PNG") # optional -- java internet + sage: print os.path.exists(testfile) # optional -- java internet True Use Jmol to save an image of a 3-D object created in Sage. @@ -165,31 +143,23 @@ def export_image(self, sage: print os.path.exists(testfile) # optional -- java True - """ - if (self.is_jvm_available()): - # Set up paths, file names and scripts - jmolpath = os.path.join(SAGE_LOCAL, "share", "jmol", "JmolData.jar") - launchscript = "" - if (datafile_cmd!='script'): - launchscript = "load " - launchscript = launchscript + datafile - #print launchscript - imagescript = "write "+ image_type +" "+targetfile+"\n" - #print imagescript - - sizeStr = "%sx%s" %(figsize*100,figsize*100) - #scratch file for Jmol errors and status - jmolscratch = os.path.join(DOT_SAGE, "sage_notebook.sagenb", "jmol_scratch") - if not os.path.exists(jmolscratch): - sage_makedirs(jmolscratch) - scratchout = os.path.join(jmolscratch,"jmolout.txt") - jout=open(scratchout,'w') - #now call the java application and write the file. - result = subprocess.call(["java","-Xmx512m","-Djava.awt.headless=true","-jar",jmolpath,"-iox","-g",sizeStr,"-J",launchscript,"-j",imagescript],stdout=jout) - jout.close() - else: - errStr = "Java Virtual Machine not available.\n" - errStr +="This should be checked before calling JmolData().export_image().\n" - errStr +="Use JmolData().is_jvm_available() to check.\n" - errStr +="Administrator should install JVM." - raise JmolDataError(errStr) + """ + # Set up paths, file names and scripts + jmolpath = os.path.join(SAGE_LOCAL, "share", "jmol", "JmolData.jar") + launchscript = "" + if (datafile_cmd!='script'): + launchscript = "load " + launchscript = launchscript + datafile + imagescript = "write "+ image_type +" "+targetfile+"\n" + + sizeStr = "%sx%s" %(figsize*100,figsize*100) + # Scratch file for Jmol errors + scratchout = tmp_filename(ext=".txt") + with open(scratchout, 'w') as jout: + # Now call the java application and write the file. + subprocess.call(["java", "-Xmx512m", "-Djava.awt.headless=true", + "-jar", jmolpath, "-iox", "-g", sizeStr, + "-J", launchscript, "-j", imagescript], stdout=jout, stderr=jout) + if not os.path.isfile(targetfile): + raise RuntimeError("Jmol failed to create file %s, see %s for details"%(repr(targetfile), repr(scratchout))) + os.unlink(scratchout) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 03fdaf6f73c..6867e9cdb83 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -399,7 +399,7 @@ def install_packages(self, package_name): EXAMPLES:: - sage: r.install_packages('aaMI') # optional - internet + sage: r.install_packages('aaMI') # not tested ... R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. @@ -411,12 +411,6 @@ def install_packages(self, package_name): os.system("time echo '%s' | R --vanilla"%cmd) print "Please restart Sage in order to use '%s'."%package_name - # For now, r.restart() seems to be broken - #print "Please restart Sage or restart the R interface (via r.restart()) in order to use '%s'."%package_name - - #s = r.eval('install.packages("%s")'%package_name) - #print s - def __repr__(self): """ Return string representation of this R interface. diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index de405547aeb..6a124a5c009 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -1060,7 +1060,6 @@ end_scene""" % (render_params.antialiasing, - ``axes`` - (default: False) if True, draw coordinate axes - - ``**kwds`` - other options, which make sense for particular rendering engines @@ -1120,10 +1119,9 @@ end_scene""" % (render_params.antialiasing, axes = opts['axes'] import sage.misc.misc - if 'filename' in kwds: - filename = kwds['filename'] - del kwds['filename'] - else: + try: + filename = kwds.pop('filename') + except KeyError: filename = tmp_filename() from sage.plot.plot import EMBEDDED_MODE @@ -1163,62 +1161,50 @@ end_scene""" % (render_params.antialiasing, # (This will be removed once we have dynamic resizing of applets in the browser.) base, ext = os.path.splitext(filename) fg = figsize[0] - #if fg >= 2: - # fg = 2 filename = '%s-size%s%s'%(base, fg*100, ext) + if EMBEDDED_MODE: ext = "jmol" - else: - ext = "spt" - archive_name = "%s.%s.zip" % (filename, ext) - if EMBEDDED_MODE: # jmol doesn't seem to correctly parse the ?params part of a URL archive_name = "%s-%s.%s.zip" % (filename, randint(0, 1 << 30), ext) + else: + ext = "spt" + archive_name = "%s.%s.zip" % (filename, ext) T = self._prepare_for_jmol(frame, axes, frame_aspect_ratio, aspect_ratio, zoom) T.export_jmol(archive_name, force_reload=EMBEDDED_MODE, zoom=zoom*100, **kwds) - viewer_app = os.path.join(sage.misc.misc.SAGE_LOCAL, "bin/jmol") - - # We need a script to load the file - f = open(filename + '.'+ext, 'w') - import sagenb - if EMBEDDED_MODE: - path = "cells/%s/%s" %(sagenb.notebook.interact.SAGE_CELL_ID, archive_name) - else: - path = archive_name - f.write('set defaultdirectory "%s"\n' %path) - f.write('script SCRIPT\n') - f.close() + viewer_app = os.path.join(sage.misc.misc.SAGE_LOCAL, "bin", "jmol") # If the server has a Java installation we can make better static images with Jmol # Test for Java then make image with Jmol or Tachyon if no JavaVM if EMBEDDED_MODE: - #name image file - head,tail = os.path.split(archive_name) - png_path = os.path.join(head,'.jmol_images') - if not os.path.exists(png_path): - os.mkdir(png_path) - png_name = os.path.join(png_path,filename) - #test for JavaVM + # We need a script for the Notebook. + # When the notebook sees this file, it will know to + # display the static file and the "Make Interactive" + # button. + import sagenb + path = "cells/%s/%s" %(sagenb.notebook.interact.SAGE_CELL_ID, archive_name) + with open(filename + '.' + ext, 'w') as f: + f.write('set defaultdirectory "%s"\n' % path) + f.write('script SCRIPT\n') + + # Filename for the static image + png_path = '.jmol_images' + sage.misc.misc.sage_makedirs(png_path) + png_name = os.path.join(png_path, filename + ".jmol.png") + from sage.interfaces.jmoldata import JmolData jdata = JmolData() - if (jdata.is_jvm_available()): - # make the image with Jmol - # hack...need absolute paths since jvm running outside of sage environment - cellhead, celltail =os.path.split(os.path.realpath(os.path.join(os.path.curdir,"data"))) - celldir = os.path.join(cellhead,"cells",str(sagenb.notebook.interact.SAGE_CELL_ID)) - png_name = png_name+".jmol.png" - png_fullpath = os.path.join(celldir,png_name) - archive_fullpath = os.path.join(celldir,archive_name) - #print png_fullpath - script = 'set defaultdirectory \"'+archive_fullpath+'\"\n script SCRIPT\n' - #print script - jdata.export_image(targetfile = png_fullpath,datafile=script,image_type="PNG", figsize = fg) + if jdata.is_jvm_available(): + # Java needs absolute paths + archive_name = os.path.abspath(archive_name) + png_name = os.path.abspath(png_name) + script = '''set defaultdirectory "%s"\nscript SCRIPT\n''' % archive_name + jdata.export_image(targetfile=png_name, datafile=script, image_type="PNG", figsize=fg) else: - #make the image with tachyon + # Render the image with tachyon T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom) - tachyon_rt(T.tachyon(), png_name+".jmol.png", verbosity, True, opts) - + tachyon_rt(T.tachyon(), png_name, verbosity, True, opts) if viewer == 'canvas3d': T = self._prepare_for_tachyon(frame, axes, frame_aspect_ratio, aspect_ratio, zoom) From 603ea722edbbd46d2dcde0abb6c85f32ef6cdf18 Mon Sep 17 00:00:00 2001 From: Punarbasu Purkayastha Date: Fri, 19 Apr 2013 15:56:20 +0000 Subject: [PATCH 192/206] allow numpy arrays in list_plot, line, points --- src/sage/plot/line.py | 26 +++++++++++++++++++++++-- src/sage/plot/plot.py | 34 ++++++++++++++++++++++++--------- src/sage/plot/plot3d/shapes2.py | 30 +++++++++++++++++++++++------ src/sage/plot/point.py | 30 +++++++++++++++++++++++++---- 4 files changed, 99 insertions(+), 21 deletions(-) diff --git a/src/sage/plot/line.py b/src/sage/plot/line.py index ac7932e4524..6b79c756712 100644 --- a/src/sage/plot/line.py +++ b/src/sage/plot/line.py @@ -266,6 +266,11 @@ def line(points, **kwds): Returns either a 2-dimensional or 3-dimensional line depending on value of points. + INPUT: + + - ``points`` - either a single point (as a tuple), a list of + points, a single complex number, or a list of complex numbers. + For information regarding additional arguments, see either line2d? or line3d?. @@ -291,6 +296,11 @@ def line2d(points, **options): r""" Create the line through the given list of points. + INPUT: + + - ``points`` - either a single point (as a tuple), a list of + points, a single complex number, or a list of complex numbers. + Type ``line2d.options`` for a dictionary of the default options for lines. You can change this to change the defaults for all future lines. Use ``line2d.reset()`` to reset to the default options. @@ -355,8 +365,13 @@ def line2d(points, **options): A line with no points or one point:: sage: line([]) #returns an empty plot + sage: import numpy; line(numpy.array([])) sage: line([(1,1)]) + A line with numpy arrays:: + + sage: line(numpy.array([[1,2], [3,4]])) + A line with a legend:: sage: line([(0,0),(1,1)], legend_label='line') @@ -460,8 +475,15 @@ def line2d(points, **options): """ from sage.plot.all import Graphics from sage.plot.plot import xydata_from_point_list - if points == []: - return Graphics() + from sage.rings.all import CC, CDF + if points in CC or points in CDF: + pass + else: + try: + if not points: + return Graphics() + except ValueError: # numpy raises a ValueError if not empty + pass xdata, ydata = xydata_from_point_list(points) g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index fcabacf0589..55f9e75af80 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1637,8 +1637,8 @@ def polar_plot(funcs, *args, **kwds): @options(aspect_ratio='automatic') def list_plot(data, plotjoined=False, **kwargs): r""" - ``list_plot`` takes either a list of numbers, a list of tuples, - or a dictionary and plots the corresponding points. + ``list_plot`` takes either a list of numbers, a list of tuples, a numpy + array, or a dictionary and plots the corresponding points. If given a list of numbers (that is, not a list of tuples or lists), ``list_plot`` forms a list of tuples ``(i, x_i)`` where ``i`` goes from @@ -1676,6 +1676,13 @@ def list_plot(data, plotjoined=False, **kwargs): sage: list_plot(r, plotjoined=True, color='purple') + You can provide a numpy array.:: + + sage: import numpy + sage: list_plot(numpy.arange(10)) + + sage: list_plot(numpy.array([[1,2], [2,3], [3,4]])) + Plot a list of complex numbers:: sage: list_plot([1, I, pi + I/2, CC(.25, .25)]) @@ -1761,18 +1768,27 @@ def list_plot(data, plotjoined=False, **kwargs): 100.0 """ from sage.plot.all import line, point - if data == {} or data == () or data == []: - return Graphics() + try: + if not data: + return Graphics() + except ValueError: # numpy raises ValueError if it is not empty + pass + if not isinstance(plotjoined, bool): + raise TypeError("The second argument 'plotjoined' should be boolean " + "(True or False). If you meant to plot two lists 'x' " + "and 'y' against each other, use 'list_plot(zip(x,y))'.") if isinstance(data, dict): if plotjoined: list_data = sorted(list(data.iteritems())) else: list_data = list(data.iteritems()) return list_plot(list_data, plotjoined=plotjoined, **kwargs) - if not isinstance(data[0], (list, tuple)): - data = zip(range(len(data)), data) - if isinstance(plotjoined, (list, tuple)): - raise TypeError, "The second argument 'plotjoined' should be boolean (True or False). If you meant to plot two lists 'x' and 'y' against each other, use 'list_plot(zip(x,y))'." + try: + from sage.rings.all import RDF + tmp = RDF(data[0]) + data = list(enumerate(data)) + except TypeError: + pass try: if plotjoined: return line(data, **kwargs) @@ -1785,7 +1801,7 @@ def list_plot(data, plotjoined=False, **kwargs): # gets to (1, I). from sage.rings.complex_field import ComplexField CC = ComplexField() - # if we get here, we already did "zip(range(len(data)), data)", + # if we get here, we already did "list(enumerate(data))", # so look at z[1] in inner list data = [(z.real(), z.imag()) for z in [CC(z[1]) for z in data]] if plotjoined: diff --git a/src/sage/plot/plot3d/shapes2.py b/src/sage/plot/plot3d/shapes2.py index bfde0bd9793..d6a8d187630 100644 --- a/src/sage/plot/plot3d/shapes2.py +++ b/src/sage/plot/plot3d/shapes2.py @@ -82,6 +82,11 @@ def line3d(points, thickness=1, radius=None, arrow_head=False, **kwds): sage: line3d([(1,2,3), (1,0,-2), (3,1,4), (2,1,-2)], color='red') + The points of the line provided as a numpy array:: + + sage: import numpy + sage: line3d(numpy.array([(1,2,3), (1,0,-2), (3,1,4), (2,1,-2)])) + A transparent thick green line and a little blue line:: sage: line3d([(0,0,0), (1,1,1), (1,0,2)], opacity=0.5, radius=0.1, \ @@ -1016,11 +1021,24 @@ def point3d(v, size=5, **kwds): We check to make sure the options work:: sage: point3d((4,3,2),size=20,color='red',opacity=.5) + + numpy arrays can be provided as input:: + + sage: import numpy + sage: point3d(numpy.array([1,2,3])) + + sage: point3d(numpy.array([[1,2,3], [4,5,6], [7,8,9]])) + """ - if len(v) == 3 and v[0] in RDF: - return Point(v, size, **kwds) - else: - A = sum([Point(z, size, **kwds) for z in v]) - A._set_extra_kwds(kwds) - return A + if len(v) == 3: + try: + # check if the first element can be changed to a float + tmp = RDF(v[0]) + return Point(v, size, **kwds) + except TypeError: + pass + + A = sum([Point(z, size, **kwds) for z in v]) + A._set_extra_kwds(kwds) + return A diff --git a/src/sage/plot/point.py b/src/sage/plot/point.py index c9038a8a162..dc8f7b9d150 100644 --- a/src/sage/plot/point.py +++ b/src/sage/plot/point.py @@ -280,7 +280,8 @@ def point(points, **kwds): INPUT: - - ``points`` - either a single point (as a tuple) or a list of points. + - ``points`` - either a single point (as a tuple), a list of + points, a single complex number, or a list of complex numbers. For information regarding additional arguments, see either point2d? or point3d?. @@ -318,7 +319,11 @@ def point(points, **kwds): def point2d(points, **options): r""" A point of size ``size`` defined by point = `(x,y)`. - Point takes either a single tuple of coordinates or a list of tuples. + + INPUT: + + - ``points`` - either a single point (as a tuple), a list of + points, a single complex number, or a list of complex numbers. Type ``point2d.options`` to see all options. @@ -331,6 +336,7 @@ def point2d(points, **options): Passing an empty list returns an empty plot:: sage: point([]) + sage: import numpy; point(numpy.array([])) If you need a 2D point to live in 3-space later, this is possible:: @@ -378,11 +384,27 @@ def point2d(points, **options): :: sage: point((3,4), pointsize=100) + + We can plot a single complex number:: + + sage: point(CC(1+I), pointsize=100) + + We can also plot a list of complex numbers:: + + sage: point([CC(I), CC(I+1), CC(2+2*I)], pointsize=100) + """ from sage.plot.plot import xydata_from_point_list from sage.plot.all import Graphics - if points == []: - return Graphics() + from sage.rings.all import CC, CDF + if points in CC or points in CDF: + pass + else: + try: + if not points: + return Graphics() + except ValueError: # numpy raises a ValueError if not empty + pass xdata, ydata = xydata_from_point_list(points) g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) From ff26babce37eb6c18b051c7d9b0466dc2dc0791c Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 21 Dec 2013 12:33:04 +0100 Subject: [PATCH 193/206] Use bash as SHELL for build/Makefile --- build/deps | 4 ++-- build/install | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/build/deps b/build/deps index e799cf0e5c3..351e761e135 100644 --- a/build/deps +++ b/build/deps @@ -458,7 +458,7 @@ $(INST)/sage: \ $(INST)/$(ZNPOLY) \ $(INST)/csage if [ -z "$$SAGE_INSTALL_FETCH_ONLY" ]; then \ - cd $(SAGE_SRC) && . bin/sage-env && \ + cd $(SAGE_SRC) && source bin/sage-env && \ $(PIPE) 'time python setup.py install 2>&1' 'tee -a $(SAGE_LOGS)/sage-$(SAGE_VERSION).log' && \ touch $@; \ fi @@ -471,7 +471,7 @@ $(INST)/csage: $(INST)/$(SCONS) \ $(INST)/$(PYNAC) \ $(INST)/$(PYTHON) if [ -z "$$SAGE_INSTALL_FETCH_ONLY" ]; then \ - cd $(SAGE_SRC) && . bin/sage-env && cd c_lib && \ + cd $(SAGE_SRC) && source bin/sage-env && cd c_lib && \ $(PIPE) 'time scons -Q install 2>&1' 'tee -a $(SAGE_LOGS)/csage-$(SAGE_VERSION).log' && \ touch $@; \ fi diff --git a/build/install b/build/install index f16ba2c0fa4..034fa06732b 100755 --- a/build/install +++ b/build/install @@ -331,6 +331,11 @@ cat >&3 <&3 "SHELL = `command -v bash`" +echo >&3 + # If the user (or the Makefile) has set SAGE_PARALLEL_SPKG_BUILD=no, # then turn off parallel building: disable just building multiple # packages at the same time. Individual packages can still be built From 8c1320a8b7f8034423c7b0cb6fcb97d12bb900b8 Mon Sep 17 00:00:00 2001 From: Luis Felipe Tabera Alonso Date: Sat, 21 Dec 2013 13:01:56 +0100 Subject: [PATCH 194/206] Fixing Whitespace errors --- src/sage/plot/plot3d/shapes2.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/plot/plot3d/shapes2.py b/src/sage/plot/plot3d/shapes2.py index d6a8d187630..b320a06aae2 100644 --- a/src/sage/plot/plot3d/shapes2.py +++ b/src/sage/plot/plot3d/shapes2.py @@ -1040,5 +1040,4 @@ def point3d(v, size=5, **kwds): A = sum([Point(z, size, **kwds) for z in v]) A._set_extra_kwds(kwds) - return A - + return A \ No newline at end of file From 13c6ffc614b783f4be13a1f05c02fcf9a0adbd82 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 21 Dec 2013 12:37:07 +0000 Subject: [PATCH 195/206] Updated Sage version to 6.1.beta1 --- VERSION.txt | 2 +- src/bin/sage-banner | 2 +- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 10af116cae2..491e8d2cc2a 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 6.1.beta0, released 2013-12-19 +Sage version 6.1.beta1, released 2013-12-21 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 6b04d1721b3..aa40026722b 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ Sage Version 6.1.beta0, Release Date: 2013-12-19 │ +│ Sage Version 6.1.beta1, Release Date: 2013-12-21 │ │ 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 78facc3249f..3e205e11235 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='6.1.beta0' -SAGE_RELEASE_DATE='2013-12-19' +SAGE_VERSION='6.1.beta1' +SAGE_RELEASE_DATE='2013-12-21' diff --git a/src/sage/version.py b/src/sage/version.py index 2ddab48155a..df25e3136bd 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ """nodoctests""" # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '6.1.beta0' -date = '2013-12-19' +version = '6.1.beta1' +date = '2013-12-21' From 75d1e43102ff43dd7336906f2be5aaacdca80afc Mon Sep 17 00:00:00 2001 From: Stefan Reiterer Date: Sun, 8 Dec 2013 23:04:42 +0000 Subject: [PATCH 196/206] trac 9706: Collective patch. Bugfixes, extensions, optimizations, documentation, doctests for chebyshev_T, chebyshev_U and base class for ortho polys --- src/sage/functions/orthogonal_polys.py | 800 +++++++++++++++++++++++-- 1 file changed, 758 insertions(+), 42 deletions(-) diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index 9f73b71fbb0..aea5b432d0b 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -261,39 +261,47 @@ (it loads "specfun", but I'm not sure if that is the real reason). The next call is usually faster but not always. -TODO: Implement associated Legendre polynomials and Zernike -polynomials. (Neither is in Maxima.) -http://en.wikipedia.org/wiki/Associated_Legendre_polynomials -http://en.wikipedia.org/wiki/Zernike_polynomials +.. TODO:: + + Implement associated Legendre polynomials and Zernike + polynomials. (Neither is in Maxima.) + :wikipedia:`Associated_Legendre_polynomials` + :wikipedia:`Zernike_polynomials` REFERENCES: -- Abramowitz and Stegun: Handbook of Mathematical Functions, - http://www.math.sfu.ca/ cbm/aands/ +.. [ASHandbook] Abramowitz and Stegun: Handbook of Mathematical Functions, + http://www.math.sfu.ca/ cbm/aands/ -- http://en.wikipedia.org/wiki/Chebyshev_polynomials +.. :wikipedia:`Chebyshev_polynomials` -- http://en.wikipedia.org/wiki/Legendre_polynomials +.. :wikipedia:`Legendre_polynomials` -- http://en.wikipedia.org/wiki/Hermite_polynomials +.. :wikipedia:`Hermite_polynomials` -- http://mathworld.wolfram.com/GegenbauerPolynomial.html +.. http://mathworld.wolfram.com/GegenbauerPolynomial.html -- http://en.wikipedia.org/wiki/Jacobi_polynomials +.. :wikipedia:`Jacobi_polynomials` -- http://en.wikipedia.org/wiki/Laguerre_polynomia +.. :wikipedia:`Laguerre_polynomia` -- http://en.wikipedia.org/wiki/Associated_Legendre_polynomials +.. :wikipedia:`Associated_Legendre_polynomials` +.. [EffCheby] Wolfram Koepf: Effcient Computation of Chebyshev Polynomials + in Computer Algebra + Computer Algebra Systems: A Practical Guide. + John Wiley, Chichester (1999): 79-99. AUTHORS: - David Joyner (2006-06) +- Stefan Reiterer (2010-) """ #***************************************************************************** # Copyright (C) 2006 William Stein # 2006 David Joyner +# 2010 Stefan Reiterer # # Distributed under the terms of the GNU General Public License (GPL) # @@ -307,10 +315,25 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +import warnings + from sage.misc.sage_eval import sage_eval -from sage.rings.all import ZZ +from sage.rings.all import ZZ, RR, CC, RIF, CIF from sage.calculus.calculus import maxima + +from sage.symbolic.function import BuiltinFunction, GinacFunction, is_inexact +from sage.symbolic.expression import is_Expression +import sage.functions.special +from sage.functions.special import MaximaFunction, meval +from sage.functions.other import floor, gamma, factorial, abs, binomial +from sage.functions.other import sqrt, conjugate +from sage.functions.trig import sin, cos +from sage.functions.log import ln +import sage.symbolic.expression as expression +from sage.structure.parent import Parent +from sage.structure.coerce import parent + _done = False def _init(): """ @@ -327,12 +350,13 @@ def _init(): Then after using one of these functions, it changes:: - sage: from sage.functions.orthogonal_polys import chebyshev_T - sage: chebyshev_T(2,x) - 2*(x - 1)^2 + 4*x - 3 + sage: from sage.functions.orthogonal_polys import legendre_P + sage: legendre_P(2,x) + 3/2*(x - 1)^2 + 3*x - 2 sage: sage.functions.orthogonal_polys._done True + Note that because here we use a Pynac variable ``x``, the representation of the function is different from its actual doctest, where a polynomial indeterminate @@ -348,49 +372,741 @@ def _init(): _done = True -def chebyshev_T(n,x): + +class OrthogonalPolynomial(BuiltinFunction): + """ + Base class for orthogonal polynomials. + + This class is an abstract base class for all orthogonal polynomials since + they share similar properties. The evaluation as a polynomial + is either done via maxima, or with pynac. + + Convention: The first argument is always the order of the polynomial, + the last one is always the value `x` where the polynomial is evaluated. + """ + def __init__(self, name, nargs=2, latex_name=None, conversions={}): + """ + :class:`OrthogonalPolynomial` class needs the same input parameter as + it's parent class. + + EXAMPLES:: + + sage: from sage.functions.orthogonal_polys import OrthogonalPolynomial + sage: new = OrthogonalPolynomial('testo_P') + sage: new + testo_P + """ + try: + self._maxima_name = conversions['maxima'] + except KeyError: + self._maxima_name = None + + super(OrthogonalPolynomial,self).__init__(name=name, nargs=nargs, + latex_name=latex_name, conversions=conversions) + + def _maxima_init_evaled_(self, *args): + r""" + Return a string which represents this function evaluated at + ``*args`` in Maxima. + + In fact these are thought to be the old wrappers for the orthogonal + polynomials. They are used when the other evaluation methods fail, + or are not fast enough. It appears that direct computation + with pynac is in most cases faster than maxima. Maxima comes into + play when all other methods fail. + + EXAMPLES:: + + sage: chebyshev_T(3,x) + 4*x^3 - 3*x + """ + return None + + def _apply_formula_(self, *args): + """ + Method which uses the three term recursion of the polynomial, + or explicit formulas instead of maxima to evaluate the polynomial + efficiently, if the `x` argument is not a symbolic expression. + + EXAMPLES:: + + sage: from sage.functions.orthogonal_polys import OrthogonalPolynomial + sage: new = OrthogonalPolynomial('testo_P') + sage: new._apply_formula_(1,2.0) + Traceback (most recent call last): + ... + NotImplementedError: no recursive calculation of values implemented + """ + raise NotImplementedError("no recursive calculation of values implemented") + + def _eval_special_values_(self,*args): + """ + Evaluate the polynomial explicitly for special values. + + EXAMPLES:: + + sage: var('n') + n + sage: chebyshev_T(n,-1) + (-1)^n + """ + raise ValueError("no special values known") + + def _eval_(self, *args): + """ + The _eval_ method decides which evaluation suits best + for the given input, and returns a proper value. + + EXAMPLES:: + + sage: chebyshev_T(5,x) + 16*x^5 - 20*x^3 + 5*x + sage: var('n') + n + sage: chebyshev_T(n,-1) + (-1)^n + sage: chebyshev_T(-7,x) + 64*x^7 - 112*x^5 + 56*x^3 - 7*x + sage: chebyshev_T(3/2,x) + chebyshev_T(3/2, x) + sage: x = PolynomialRing(QQ, 'x').gen() + sage: chebyshev_T(2,x) + 2*x^2 - 1 + sage: chebyshev_U(2,x) + 4*x^2 - 1 + sage: parent(chebyshev_T(4, RIF(5))) + Real Interval Field with 53 bits of precision + sage: RR2 = RealField(5) + sage: chebyshev_T(100000,RR2(2)) + 8.9e57180 + sage: chebyshev_T(5,Qp(3)(2)) + 2 + 3^2 + 3^3 + 3^4 + 3^5 + O(3^20) + sage: chebyshev_T(100001/2, 2) + doctest:500: RuntimeWarning: Warning: mpmath returns NoConvergence exception! Use other method instead. + chebyshev_T(100001/2, 2) + sage: chebyshev_U._eval_(1.5, Mod(8,9)) is None + True + """ + if not is_Expression(args[0]): + # If x is no expression and is inexact or n is not an integer -> make numerical evaluation + if (not is_Expression(args[-1])) and (is_inexact(args[-1]) or not args[0] in ZZ): + try: + import sage.libs.mpmath.all as mpmath + return self._evalf_(*args) + except AttributeError: + pass + except mpmath.NoConvergence: + warnings.warn("Warning: mpmath returns NoConvergence exception! Use other method instead.", + RuntimeWarning) + except ValueError: + pass + + # n is not an integer and x is an expression -> return symbolic expression. + if not args[0] in ZZ: + if is_Expression(args[-1]): + return None + + # Check for known identities + try: + return self._eval_special_values_(*args) + except ValueError: + pass + + #if negative indices are not specified + #in _eval_special_values only return symbolic + #value + if args[0] < 0 and args[0] in ZZ: + return None + + if args[0] in ZZ: + try: + return self._apply_formula_(*args) + except NotImplementedError: + pass + + if self._maxima_name is None: + return None + + if args[0] in ZZ: # use maxima as last resort + return self._old_maxima_(*args) + else: + return None + + def __call__(self,*args,**kwds): + """ + This overides the call method from SageObject to avoid problems with coercions, + since the _eval_ method is able to handle more data types than symbolic functions + would normally allow. + Thus we have the distinction between algebraic objects (if n is an integer), + and else as symbolic function. + + EXAMPLES:: + + sage: K. = NumberField(x^3-x-1) + sage: chebyshev_T(5, a) + 16*a^2 + a - 4 + sage: chebyshev_T(5,MatrixSpace(ZZ, 2)([1, 2, -4, 7])) + [-40799 44162] + [-88324 91687] + sage: R. = QQ[] + sage: parent(chebyshev_T(5, x)) + Univariate Polynomial Ring in x over Rational Field + """ + if 'hold' not in kwds: + kwds['hold'] = False + if 'coerce' not in kwds: + kwds['coerce']=True + + if args[0] in ZZ and kwds['hold'] is False: #check if n is in ZZ->consider polynomial as algebraic structure + return self._eval_(*args) # Let eval methode decide which is best + else: # Consider OrthogonalPolynomial as symbol + return super(OrthogonalPolynomial,self).__call__(*args,**kwds) + + def _old_maxima_(self,*args): + """ + Method which holds the old maxima wrappers as last alternative. + It returns None per default, and it only needs to be implemented, + if it is necessary. + + EXAMPLES:: + + sage: chebyshev_T._old_maxima_(-7,x) is None + True + """ + None + +class Func_chebyshev_T(OrthogonalPolynomial): """ - Returns the Chebyshev function of the first kind for integers - `n>-1`. + Chebyshev polynomials of the first kind. REFERENCE: - - AS 22.5.31 page 778 and AS 6.1.22 page 256. + - [ASHandbook]_ 22.5.31 page 778 and 6.1.22 page 256. EXAMPLES:: - sage: x = PolynomialRing(QQ, 'x').gen() - sage: chebyshev_T(2,x) - 2*x^2 - 1 + sage: chebyshev_T(3,x) + 4*x^3 - 3*x + sage: chebyshev_T(5,x) + 16*x^5 - 20*x^3 + 5*x + sage: var('k') + k + sage: test = chebyshev_T(k,x) + sage: test + chebyshev_T(k, x) """ - _init() - return sage_eval(maxima.eval('chebyshev_t(%s,x)'%ZZ(n)), locals={'x':x}) - -def chebyshev_U(n,x): + def __init__(self): + """ + Init method for the chebyshev polynomials of the first kind. + + EXAMPLES:: + + sage: from sage.functions.orthogonal_polys import Func_chebyshev_T + sage: chebyshev_T2 = Func_chebyshev_T() + sage: chebyshev_T2(1,x) + x + """ + super(Func_chebyshev_T,self).__init__("chebyshev_T", nargs=2, + conversions=dict(maxima='chebyshev_t', + mathematica='ChebyshevT')) + + def _eval_special_values_(self,*args): + """ + Values known for special values of x. + For details see [ASHandbook]_ 22.4 (p. 777) + + EXAMPLES: + + sage: var('n') + n + sage: chebyshev_T(n,1) + 1 + sage: chebyshev_T(n,-1) + (-1)^n + sage: chebyshev_T(-7, x) - chebyshev_T(7,x) + 0 + sage: chebyshev_T._eval_special_values_(3/2,x) + Traceback (most recent call last): + ... + ValueError: No special values for non integral indices! + sage: chebyshev_T._eval_special_values_(n, 0.1) + Traceback (most recent call last): + ... + ValueError: Value not found! + sage: chebyshev_T._eval_special_values_(26, Mod(9,9)) + Traceback (most recent call last): + ... + ValueError: Value not found! + """ + if (not is_Expression(args[0])) and (not args[0] in ZZ): + raise ValueError("No special values for non integral indices!") + + if args[-1] == 1: + return args[-1] + + if args[-1] == -1: + return args[-1]**args[0] + + if (args[-1] == 0 and args[-1] in CC): + return (1+(-1)**args[0])*(-1)**(args[0]/2)/2 + + if args[0] < 0 and args[0] in ZZ: + return self._eval_(-args[0],args[-1]) + + raise ValueError("Value not found!") + + def _evalf_(self, *args,**kwds): + """ + Evaluates :class:`chebyshev_T` numerically with mpmath. + If the index is an integer we use the recursive formula since + it is faster. + + EXAMPLES:: + + sage: chebyshev_T(10,3).n(75) + 2.261953700000000000000e7 + sage: chebyshev_T(10,I).n() + -3363.00000000000 + sage: chebyshev_T(5,0.3).n() + 0.998880000000000 + sage: chebyshev_T(1/2, 0) + 0.707106781186548 + sage: chebyshev_T._evalf_(1.5, Mod(8,9)) + Traceback (most recent call last): + ... + ValueError: No compatible type! + + """ + if args[0] in ZZ and args[0] >= 0: + return self._cheb_recur_(*args)[0] + + try: + real_parent = kwds['parent'] + except KeyError: + real_parent = parent(args[-1]) + + x_set = False + if hasattr(real_parent,"precision"): # Check if we have a data type with precision + x = args[-1] + step_parent = real_parent + x_set = True + else: + if args[-1] in RR: + x = RR(args[-1]) + step_parent = RR + x_set = True + elif args[-1] in CC: + x = CC(args[-1]) + step_parent = CC + x_set = True + + if not x_set: + raise ValueError("No compatible type!") + + from sage.libs.mpmath.all import call as mpcall + from sage.libs.mpmath.all import chebyt as mpchebyt + + return mpcall(mpchebyt,args[0],x,parent=step_parent) + + def _maxima_init_evaled_(self, *args): + """ + Evaluate the Chebyshev polynomial ``self`` with maxima. + + EXAMPLES:: + + sage: chebyshev_T._maxima_init_evaled_(1,x) + 'x' + sage: var('n') + n + sage: maxima(chebyshev_T(n,x)) + chebyshev_t(n,x) + + """ + n = args[0] + x = args[1] + return maxima.eval('chebyshev_t({0},{1})'.format(n,x)) + + + def _apply_formula_(self,*args): + """ + Applies explicit formulas for :class:`chebyshev_T`. + This is much faster for numerical evaluation than maxima! + See [ASHandbook]_ 227 (p. 782) for details for the recurions. + See also [EffCheby]_ for fast evaluation techniques. + + EXAMPLES:: + + sage: chebyshev_T._apply_formula_(2,0.1) == chebyshev_T._evalf_(2,0.1) + True + sage: chebyshev_T(51,x) + 2*(2*(2*(2*(2*(2*x^2 - 1)^2 - 1)*(2*(2*x^2 - 1)*x - x) - x)*(2*(2*(2*x^2 - 1)*x - x)^2 - 1) - x)^2 - 1)*(2*(2*(2*(2*(2*x^2 - 1)^2 - 1)*(2*(2*x^2 - 1)*x - x) - x)*(2*(2*(2*x^2 - 1)*x - x)^2 - 1) - x)*(2*(2*(2*(2*x^2 - 1)*x - x)^2 - 1)^2 - 1) - x) - x + sage: chebyshev_T._apply_formula_(10,x) + 512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1 + + """ + k = args[0] + x = args[1] + + if k == 0: + return 1 + if k == 1: + return x + + help1 = 1 + help2 = x + if is_Expression(x) and k <= 25: + # Recursion gives more compact representations for large k + help3 = 0 + for j in xrange(0,floor(k/2)+1): + f = factorial(k-j-1) / factorial(j) / factorial(k-2*j) + help3 = help3 + (-1)**j * (2*x)**(k-2*j) * f + help3 = help3 * k / 2 + return help3 + else: + return self._cheb_recur_(k,x)[0] + + def _cheb_recur_(self,n, x, both=False): + """ + Generalized recursion formula for Chebyshev polynomials. + Implementation suggested by Frederik Johansson. + returns (T(n,x), T(n-1,x)), or (T(n,x), _) if both=False + + EXAMPLES:: + + sage: chebyshev_T._cheb_recur_(5,x) + (2*(2*(2*x^2 - 1)*x - x)*(2*x^2 - 1) - x, False) + """ + if n == 0: + return 1, x + if n == 1: + return x, 1 + a, b = self._cheb_recur_((n+1)//2, x, both or n % 2) + if n % 2 == 0: + return 2*a**2 - 1, both and 2*a*b - x + else: + return 2*a*b - x, both and 2*b**2 - 1 + + + def _eval_numpy_(self, *args): + """ + Evaluate ``self`` using numpy. + + EXAMPLES:: + + sage: import numpy + sage: z = numpy.array([1,2]) + sage: z2 = numpy.array([[1,2],[1,2]]) + sage: z3 = numpy.array([1,2,3.]) + sage: chebyshev_T(1,z) + array([1, 2]) + sage: chebyshev_T(1,z2) + array([[1, 2], + [1, 2]]) + sage: chebyshev_T(1,z3) + array([ 1., 2., 3.]) + sage: chebyshev_T(z,0.1) + array([ 0.1 , -0.98]) + """ + from scipy.special import eval_chebyt + return eval_chebyt(args[0],args[-1]) + + def _derivative_(self, *args, **kwds): + """ + Return the derivative of :class:`chebyshev_T` in form of the Chebyshev + polynomial of the second kind :class:`chebyshev_U`. + + EXAMPLES:: + + sage: var('k') + k + sage: derivative(chebyshev_T(k,x),x) + k*chebyshev_U(k - 1, x) + sage: derivative(chebyshev_T(3,x),x) + 12*x^2 - 3 + sage: derivative(chebyshev_T(k,x),k) + Traceback (most recent call last): + ... + NotImplementedError: derivative w.r.t. to the index is not supported yet + """ + diff_param = kwds['diff_param'] + if diff_param == 0: + raise NotImplementedError("derivative w.r.t. to the index is not supported yet") + + return args[0]*chebyshev_U(args[0]-1,args[1]) + +chebyshev_T = Func_chebyshev_T() + +class Func_chebyshev_U(OrthogonalPolynomial): """ - Returns the Chebyshev function of the second kind for integers `n>-1`. + Class for the Chebyshev polynomial of the second kind. REFERENCE: - - AS, 22.8.3 page 783 and AS 6.1.22 page 256. + - [ASHandbook]_ 22.8.3 page 783 and 6.1.22 page 256. EXAMPLES:: sage: x = PolynomialRing(QQ, 'x').gen() sage: chebyshev_U(2,x) 4*x^2 - 1 + sage: chebyshev_U(3,x) + 8*x^3 - 4*x """ - _init() - return sage_eval(maxima.eval('chebyshev_u(%s,x)'%ZZ(n)), locals={'x':x}) + def __init__(self): + """ + Init method for the chebyshev polynomials of the second kind. + + EXAMPLES:: + + sage: from sage.functions.orthogonal_polys import Func_chebyshev_U + sage: chebyshev_U2 = Func_chebyshev_U() + sage: chebyshev_U2(1,x) + 2*x + """ + OrthogonalPolynomial.__init__(self, "chebyshev_U", nargs=2, + conversions=dict(maxima='chebyshev_u', + mathematica='ChebyshevU')) + + def _apply_formula_(self,*args): + """ + Applies explicit formulas for :class:`chebyshev_U`. + This is much faster for numerical evaluation than maxima. + See [ASHandbook]_ 227 (p. 782) for details on the recurions. + See also [EffCheby]_ for the recursion formulas. + + EXAMPLES:: + + sage: chebyshev_U._apply_formula_(2,0.1) == chebyshev_U._evalf_(2,0.1) + True + """ + k = args[0] + x = args[1] + + if k == 0: + return 1 + if k == 1: + return 2*x + + help1 = 1 + help2 = 2*x + if is_Expression(x) and k <= 25: + # Recursion gives more compact representations for large k + help3 = 0 + for j in xrange(0,floor(k/2)+1): + f = factorial(k-j) / factorial(j) / factorial(k-2*j) # Change to a binomial? + help3 = help3 + (-1)**j * (2*x)**(k-2*j) * f + return help3 + + else: + return self._cheb_recur_(k,x)[0] + + + def _cheb_recur_(self,n, x, both=False): + """ + Generalized recursion formula for Chebyshev polynomials. + Implementation suggested by Frederik Johansson. + returns (U(n,x), U(n-1,x)), or (U(n,x), _) if both=False + + EXAMPLES:: + + sage: chebyshev_U._cheb_recur_(3,x) + (4*(2*x^2 - 1)*x, False) + sage: chebyshev_U._cheb_recur_(5,x)[0] + -2*((2*x + 1)*(2*x - 1)*x - 4*(2*x^2 - 1)*x)*(2*x + 1)*(2*x - 1) + sage: abs(pari('polchebyshev(5, 2, 0.1)') - chebyshev_U(5,0.1)) < 1e-10 + True + """ + + if n == 0: + return 1, both and 2*x + if n == 1: + return 2*x, both and 4*x**2-1 + + a, b = self._cheb_recur_((n-1)//2, x, True) + if n % 2 == 0: + return (b+a)*(b-a), both and 2*b*(x*b-a) + else: + return 2*a*(b-x*a), both and (b+a)*(b-a) + + def _maxima_init_evaled_(self, *args): + """ + Uses maxima to evaluate ``self``. + + EXAMPLES:: + + sage: maxima(chebyshev_U(5,x)) + 32*x^5-32*x^3+6*x + sage: var('n') + n + sage: maxima(chebyshev_U(n,x)) + chebyshev_u(n,x) + sage: maxima(chebyshev_U(2,x)) + 4*x^2-1 + """ + n = args[0] + x = args[1] + return maxima.eval('chebyshev_u({0},{1})'.format(n,x)) + + def _evalf_(self, *args,**kwds): + """ + Evaluate :class:`chebyshev_U` numerically with mpmath. + If index is an integer use recursive formula since it is faster, + for chebyshev polynomials. + + EXAMPLES:: + + sage: chebyshev_U(5,-4+3.*I) + 98280.0000000000 - 11310.0000000000*I + sage: chebyshev_U(10,3).n(75) + 4.661117900000000000000e7 + sage: chebyshev_U._evalf_(1.5, Mod(8,9)) + Traceback (most recent call last): + ... + ValueError: No compatible type! + """ + if args[0] in ZZ and args[0] >= 0: + return self._cheb_recur_(*args)[0] + try: + real_parent = kwds['parent'] + except KeyError: + real_parent = parent(args[-1]) + + x_set = False + if hasattr(real_parent,"precision"): # Check if we have a data type with precision + x = args[-1] + step_parent = real_parent + x_set = True + else: + if args[-1] in RR: + x = RR(args[-1]) + step_parent = RR + x_set = True + elif args[-1] in CC: + x = CC(args[-1]) + step_parent = CC + x_set = True + + if not x_set: + raise ValueError("No compatible type!") + + from sage.libs.mpmath.all import call as mpcall + from sage.libs.mpmath.all import chebyu as mpchebyu + + return mpcall(mpchebyu,args[0],args[-1],parent = step_parent) + + def _eval_special_values_(self,*args): + """ + Special values that known. [ASHandbook]_ 22.4 (p.777). + + EXAMPLES:: + + sage: var('n') + n + sage: chebyshev_U(n,1) + n + 1 + sage: chebyshev_U(n,-1) + (-1)^n*(n + 1) + sage: chebyshev_U._eval_special_values_(26, Mod(0,9)) + Traceback (most recent call last): + ... + ValueError: Value not found! + sage: parent(chebyshev_U(3, Mod(8,9))) + Ring of integers modulo 9 + sage: parent(chebyshev_U(3, Mod(1,9))) + Ring of integers modulo 9 + sage: chebyshev_U(n, 0) + 1/2*(-1)^(1/2*n)*((-1)^n + 1) + sage: chebyshev_U(-3,x) + chebyshev_U(1,x) + 0 + sage: chebyshev_U._eval_special_values_(1.5, Mod(8,9)) + Traceback (most recent call last): + ... + ValueError: No special values for non integral indices! + sage: chebyshev_U(-1,Mod(5,8)) + 0 + sage: parent(chebyshev_U(-1,Mod(5,8))) + Ring of integers modulo 8 + """ + if (not is_Expression(args[0])) and (not args[0] in ZZ): + raise ValueError("No special values for non integral indices!") + + if args[0] == -1: + return args[-1]*0 + + if args[-1] == 1: + return args[-1]*(args[0]+1) + + if args[-1] == -1: + return args[-1]**args[0]*(args[0]+1) + + if (args[-1] == 0 and args[-1] in CC): + return (1+(-1)**args[0])*(-1)**(args[0]/2)/2 + + if args[0] < 0 and args[0] in ZZ: + return -self._eval_(-args[0]-2,args[-1]) + + raise ValueError("Value not found!") + + def _eval_numpy_(self, *args): + """ + Evaluate ``self`` using numpy. + + EXAMPLES:: + + sage: import numpy + sage: z = numpy.array([1,2]) + sage: z2 = numpy.array([[1,2],[1,2]]) + sage: z3 = numpy.array([1,2,3.]) + sage: chebyshev_U(1,z) + array([2, 4]) + sage: chebyshev_U(1,z2) + array([[2, 4], + [2, 4]]) + sage: chebyshev_U(1,z3) + array([ 2., 4., 6.]) + sage: chebyshev_U(z,0.1) + array([ 0.2 , -0.96]) + """ + from scipy.special import eval_chebyu + return eval_chebyu(args[0],args[1]) + + + def _derivative_(self, *args, **kwds): + """ + Return the derivative of :class:`chebyshev_U` in form of the Chebyshev + polynomials of the first and second kind. + + EXAMPLES:: + + sage: var('k') + k + sage: derivative(chebyshev_U(k,x),x) + ((k + 1)*chebyshev_T(k + 1, x) - x*chebyshev_U(k, x))/(x^2 - 1) + sage: derivative(chebyshev_U(3,x),x) + 24*x^2 - 4 + sage: derivative(chebyshev_U(k,x),k) + Traceback (most recent call last): + ... + NotImplementedError: derivative w.r.t. to the index is not supported yet + """ + diff_param = kwds['diff_param'] + if diff_param == 0: + raise NotImplementedError("derivative w.r.t. to the index is not supported yet") + + return ((args[0]+1)*chebyshev_T(args[0]+1,args[1])-args[1]* + chebyshev_U(args[0],args[1]))/(args[1]**2-1) + +chebyshev_U = Func_chebyshev_U() + def gen_laguerre(n,a,x): """ Returns the generalized Laguerre polynomial for integers `n > -1`. - Typically, a = 1/2 or a = -1/2. + Typically, `a = 1/2` or `a = -1/2`. - REFERENCE: + REFERENCES: - - table on page 789 in AS. + - Table on page 789 in [ASHandbook]_. EXAMPLES:: @@ -493,7 +1209,7 @@ def hermite(n,x): REFERENCE: - - AS 22.5.40 and 22.5.41, page 779. + - [ASHandbook]_ 22.5.40 and 22.5.41, page 779. EXAMPLES:: @@ -528,7 +1244,7 @@ def jacobi_P(n,a,b,x): REFERENCE: - - table on page 789 in AS. + - Table on page 789 in [ASHandbook]_. EXAMPLES:: @@ -543,11 +1259,11 @@ def jacobi_P(n,a,b,x): def laguerre(n,x): """ - Returns the Laguerre polynomial for integers `n > -1`. + Return the Laguerre polynomial for integers `n > -1`. REFERENCE: - - AS 22.5.16, page 778 and AS page 789. + - [ASHandbook]_ 22.5.16, page 778 and page 789. EXAMPLES:: @@ -569,7 +1285,7 @@ def legendre_P(n,x): REFERENCE: - - AS 22.5.35 page 779. + - [ASHandbook]_ 22.5.35 page 779. EXAMPLES:: @@ -592,7 +1308,7 @@ def legendre_P(n,x): def legendre_Q(n,x): """ Returns the Legendre function of the second kind for integers - `n>-1`. + `n > -1`. Computed using Maxima. @@ -620,7 +1336,7 @@ def ultraspherical(n,a,x): REFERENCE: - - AS 22.5.27 + - [ASHandbook]_ 22.5.27 EXAMPLES:: From 4677a15b75863e48a63228b96b5d93bfbf779b94 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 9 Dec 2013 18:40:57 +0000 Subject: [PATCH 197/206] Symbolic Chebyshev polynomials: reviewer patch --- src/sage/functions/orthogonal_polys.py | 794 +++++++++++++------------ 1 file changed, 412 insertions(+), 382 deletions(-) diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index aea5b432d0b..a7a036d1a30 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -287,9 +287,9 @@ .. :wikipedia:`Associated_Legendre_polynomials` -.. [EffCheby] Wolfram Koepf: Effcient Computation of Chebyshev Polynomials +.. [EffCheby] Wolfram Koepf: Effcient Computation of Chebyshev Polynomials in Computer Algebra - Computer Algebra Systems: A Practical Guide. + Computer Algebra Systems: A Practical Guide. John Wiley, Chichester (1999): 79-99. AUTHORS: @@ -318,20 +318,15 @@ import warnings from sage.misc.sage_eval import sage_eval -from sage.rings.all import ZZ, RR, CC, RIF, CIF +from sage.rings.all import ZZ, RR, CC +from sage.rings.real_mpfr import is_RealField +from sage.rings.complex_field import is_ComplexField from sage.calculus.calculus import maxima -from sage.symbolic.function import BuiltinFunction, GinacFunction, is_inexact +from sage.symbolic.function import BuiltinFunction from sage.symbolic.expression import is_Expression -import sage.functions.special -from sage.functions.special import MaximaFunction, meval -from sage.functions.other import floor, gamma, factorial, abs, binomial -from sage.functions.other import sqrt, conjugate -from sage.functions.trig import sin, cos -from sage.functions.log import ln -import sage.symbolic.expression as expression -from sage.structure.parent import Parent +from sage.functions.other import factorial, binomial from sage.structure.coerce import parent _done = False @@ -350,7 +345,7 @@ def _init(): Then after using one of these functions, it changes:: - sage: from sage.functions.orthogonal_polys import legendre_P + sage: from sage.functions.orthogonal_polys import legendre_P sage: legendre_P(2,x) 3/2*(x - 1)^2 + 3*x - 2 sage: sage.functions.orthogonal_polys._done @@ -372,7 +367,6 @@ def _init(): _done = True - class OrthogonalPolynomial(BuiltinFunction): """ Base class for orthogonal polynomials. @@ -381,14 +375,15 @@ class OrthogonalPolynomial(BuiltinFunction): they share similar properties. The evaluation as a polynomial is either done via maxima, or with pynac. - Convention: The first argument is always the order of the polynomial, - the last one is always the value `x` where the polynomial is evaluated. + Convention: The first argument is always the order of the polynomial, + the others are other values or parameters where the polynomial is + evaluated. """ def __init__(self, name, nargs=2, latex_name=None, conversions={}): """ :class:`OrthogonalPolynomial` class needs the same input parameter as it's parent class. - + EXAMPLES:: sage: from sage.functions.orthogonal_polys import OrthogonalPolynomial @@ -397,54 +392,48 @@ def __init__(self, name, nargs=2, latex_name=None, conversions={}): testo_P """ try: - self._maxima_name = conversions['maxima'] + self._maxima_name = conversions['maxima'] except KeyError: self._maxima_name = None - super(OrthogonalPolynomial,self).__init__(name=name, nargs=nargs, + super(OrthogonalPolynomial,self).__init__(name=name, nargs=nargs, latex_name=latex_name, conversions=conversions) def _maxima_init_evaled_(self, *args): r""" Return a string which represents this function evaluated at - ``*args`` in Maxima. - - In fact these are thought to be the old wrappers for the orthogonal - polynomials. They are used when the other evaluation methods fail, - or are not fast enough. It appears that direct computation - with pynac is in most cases faster than maxima. Maxima comes into - play when all other methods fail. + ``n, x`` in Maxima. EXAMPLES:: - sage: chebyshev_T(3,x) - 4*x^3 - 3*x + sage: from sage.functions.orthogonal_polys import OrthogonalPolynomial + sage: P = OrthogonalPolynomial('testo_P') + sage: P._maxima_init_evaled_(2, 5) is None + True """ return None - def _apply_formula_(self, *args): + def eval_formula(self, *args): """ - Method which uses the three term recursion of the polynomial, - or explicit formulas instead of maxima to evaluate the polynomial - efficiently, if the `x` argument is not a symbolic expression. + Evaluate this polynomial using an explicit formula. EXAMPLES:: sage: from sage.functions.orthogonal_polys import OrthogonalPolynomial - sage: new = OrthogonalPolynomial('testo_P') - sage: new._apply_formula_(1,2.0) + sage: P = OrthogonalPolynomial('testo_P') + sage: P.eval_formula(1,2.0) Traceback (most recent call last): ... - NotImplementedError: no recursive calculation of values implemented + NotImplementedError: no explicit calculation of values implemented """ - raise NotImplementedError("no recursive calculation of values implemented") + raise NotImplementedError("no explicit calculation of values implemented") - def _eval_special_values_(self,*args): + def _eval_special_values_(self, *args): """ Evaluate the polynomial explicitly for special values. EXAMPLES:: - + sage: var('n') n sage: chebyshev_T(n,-1) @@ -452,28 +441,30 @@ def _eval_special_values_(self,*args): """ raise ValueError("no special values known") - def _eval_(self, *args): + def _eval_(self, n, *args): """ The _eval_ method decides which evaluation suits best for the given input, and returns a proper value. - + EXAMPLES:: + sage: var('n,x') + (n, x) sage: chebyshev_T(5,x) - 16*x^5 - 20*x^3 + 5*x - sage: var('n') - n + 16*x^5 - 20*x^3 + 5*x + sage: chebyshev_T(64, x) + 2*(2*(2*(2*(2*(2*x^2 - 1)^2 - 1)^2 - 1)^2 - 1)^2 - 1)^2 - 1 sage: chebyshev_T(n,-1) (-1)^n sage: chebyshev_T(-7,x) 64*x^7 - 112*x^5 + 56*x^3 - 7*x sage: chebyshev_T(3/2,x) chebyshev_T(3/2, x) - sage: x = PolynomialRing(QQ, 'x').gen() - sage: chebyshev_T(2,x) - 2*x^2 - 1 - sage: chebyshev_U(2,x) - 4*x^2 - 1 + sage: R. = QQ[] + sage: chebyshev_T(2,t) + 2*t^2 - 1 + sage: chebyshev_U(2,t) + 4*t^2 - 1 sage: parent(chebyshev_T(4, RIF(5))) Real Interval Field with 53 bits of precision sage: RR2 = RealField(5) @@ -482,100 +473,79 @@ def _eval_(self, *args): sage: chebyshev_T(5,Qp(3)(2)) 2 + 3^2 + 3^3 + 3^4 + 3^5 + O(3^20) sage: chebyshev_T(100001/2, 2) - doctest:500: RuntimeWarning: Warning: mpmath returns NoConvergence exception! Use other method instead. + doctest:...: RuntimeWarning: mpmath failed, keeping expression unevaluated chebyshev_T(100001/2, 2) sage: chebyshev_U._eval_(1.5, Mod(8,9)) is None True """ - if not is_Expression(args[0]): - # If x is no expression and is inexact or n is not an integer -> make numerical evaluation - if (not is_Expression(args[-1])) and (is_inexact(args[-1]) or not args[0] in ZZ): - try: - import sage.libs.mpmath.all as mpmath - return self._evalf_(*args) - except AttributeError: - pass - except mpmath.NoConvergence: - warnings.warn("Warning: mpmath returns NoConvergence exception! Use other method instead.", - RuntimeWarning) - except ValueError: - pass - - # n is not an integer and x is an expression -> return symbolic expression. - if not args[0] in ZZ: - if is_Expression(args[-1]): - return None - - # Check for known identities - try: - return self._eval_special_values_(*args) - except ValueError: - pass - - #if negative indices are not specified - #in _eval_special_values only return symbolic - #value - if args[0] < 0 and args[0] in ZZ: + args_is_symbolic = any(is_Expression(x) for x in args) + + # n is an integer => evaluate algebraically (as polynomial) + if n in ZZ: + n = ZZ(n) + # Expanded symbolic expression only for small values of n + if args_is_symbolic and n.abs() < 32: + return self.eval_formula(n, *args) + else: + return self.eval_algebraic(n, *args) + + if args_is_symbolic or is_Expression(n): + # Check for known identities + try: + return self._eval_special_values_(n, *args) + except ValueError: + # Don't evaluate => keep symbolic return None - if args[0] in ZZ: - try: - return self._apply_formula_(*args) - except NotImplementedError: - pass - - if self._maxima_name is None: + # n is not an integer and neither n nor x is symbolic. + # We assume n and x are real/complex and evaluate numerically + try: + import sage.libs.mpmath.all as mpmath + return self._evalf_(n, *args) + except mpmath.NoConvergence: + warnings.warn("mpmath failed, keeping expression unevaluated", + RuntimeWarning) return None - - if args[0] in ZZ: # use maxima as last resort - return self._old_maxima_(*args) - else: + except StandardError: + # Numerical evaluation failed => keep symbolic return None - - def __call__(self,*args,**kwds): - """ + + def __call__(self, n, *args, **kwds): + """ This overides the call method from SageObject to avoid problems with coercions, since the _eval_ method is able to handle more data types than symbolic functions would normally allow. Thus we have the distinction between algebraic objects (if n is an integer), and else as symbolic function. - - EXAMPLES:: - - sage: K. = NumberField(x^3-x-1) - sage: chebyshev_T(5, a) - 16*a^2 + a - 4 + + EXAMPLES:: + + sage: K. = NumberField(x^3-x-1) + sage: chebyshev_T(5, a) + 16*a^2 + a - 4 sage: chebyshev_T(5,MatrixSpace(ZZ, 2)([1, 2, -4, 7])) [-40799 44162] [-88324 91687] sage: R. = QQ[] sage: parent(chebyshev_T(5, x)) Univariate Polynomial Ring in x over Rational Field - """ - if 'hold' not in kwds: - kwds['hold'] = False - if 'coerce' not in kwds: - kwds['coerce']=True - - if args[0] in ZZ and kwds['hold'] is False: #check if n is in ZZ->consider polynomial as algebraic structure - return self._eval_(*args) # Let eval methode decide which is best - else: # Consider OrthogonalPolynomial as symbol - return super(OrthogonalPolynomial,self).__call__(*args,**kwds) - - def _old_maxima_(self,*args): + sage: chebyshev_T(5, 2, hold=True) + chebyshev_T(5, 2) + sage: chebyshev_T(1,2,3) + Traceback (most recent call last): + ... + TypeError: Symbolic function chebyshev_T takes exactly 2 arguments (3 given) """ - Method which holds the old maxima wrappers as last alternative. - It returns None per default, and it only needs to be implemented, - if it is necessary. + # If n is an integer: consider the polynomial as an algebraic (not symbolic) object + if n in ZZ and not kwds.get('hold', False): + try: + return self._eval_(n, *args) + except StandardError: + pass - EXAMPLES:: - - sage: chebyshev_T._old_maxima_(-7,x) is None - True - """ - None + return super(OrthogonalPolynomial,self).__call__(n, *args, **kwds) -class Func_chebyshev_T(OrthogonalPolynomial): +class Func_chebyshev_T(OrthogonalPolynomial): """ Chebyshev polynomials of the first kind. @@ -609,8 +579,8 @@ def __init__(self): super(Func_chebyshev_T,self).__init__("chebyshev_T", nargs=2, conversions=dict(maxima='chebyshev_t', mathematica='ChebyshevT')) - - def _eval_special_values_(self,*args): + + def _eval_special_values_(self, n, x): """ Values known for special values of x. For details see [ASHandbook]_ 22.4 (p. 777) @@ -621,173 +591,218 @@ def _eval_special_values_(self,*args): n sage: chebyshev_T(n,1) 1 + sage: chebyshev_T(n,0) + 1/2*(-1)^(1/2*n)*((-1)^n + 1) sage: chebyshev_T(n,-1) (-1)^n - sage: chebyshev_T(-7, x) - chebyshev_T(7,x) - 0 sage: chebyshev_T._eval_special_values_(3/2,x) Traceback (most recent call last): ... - ValueError: No special values for non integral indices! + ValueError: no special value found sage: chebyshev_T._eval_special_values_(n, 0.1) Traceback (most recent call last): ... - ValueError: Value not found! - sage: chebyshev_T._eval_special_values_(26, Mod(9,9)) - Traceback (most recent call last): - ... - ValueError: Value not found! + ValueError: no special value found """ - if (not is_Expression(args[0])) and (not args[0] in ZZ): - raise ValueError("No special values for non integral indices!") - - if args[-1] == 1: - return args[-1] - - if args[-1] == -1: - return args[-1]**args[0] - - if (args[-1] == 0 and args[-1] in CC): - return (1+(-1)**args[0])*(-1)**(args[0]/2)/2 - - if args[0] < 0 and args[0] in ZZ: - return self._eval_(-args[0],args[-1]) - - raise ValueError("Value not found!") - - def _evalf_(self, *args,**kwds): + if x == 1: + return x + + if x == -1: + return x**n + + if x == 0: + return (1+(-1)**n)*(-1)**(n/2)/2 + + raise ValueError("no special value found") + + def _evalf_(self, n, x, **kwds): """ Evaluates :class:`chebyshev_T` numerically with mpmath. - If the index is an integer we use the recursive formula since - it is faster. EXAMPLES:: - sage: chebyshev_T(10,3).n(75) + sage: chebyshev_T._evalf_(10,3) + 2.26195370000000e7 + sage: chebyshev_T._evalf_(10,3,parent=RealField(75)) 2.261953700000000000000e7 - sage: chebyshev_T(10,I).n() + sage: chebyshev_T._evalf_(10,I) -3363.00000000000 - sage: chebyshev_T(5,0.3).n() + sage: chebyshev_T._evalf_(5,0.3) 0.998880000000000 sage: chebyshev_T(1/2, 0) 0.707106781186548 + sage: chebyshev_T(1/2, 3/2) + 1.11803398874989 sage: chebyshev_T._evalf_(1.5, Mod(8,9)) Traceback (most recent call last): ... - ValueError: No compatible type! + TypeError: cannot evaluate chebyshev_T with parent Ring of integers modulo 9 + + This simply evaluates using :class:`RealField` or :class:`ComplexField`:: + + sage: chebyshev_T(1234.5, RDF(2.1)) + 5.48174256255782e735 + sage: chebyshev_T(1234.5, I) + -1.21629397684152e472 - 1.21629397684152e472*I + For large values of ``n``, mpmath fails (but the algebraic formula + still works):: + + sage: chebyshev_T._evalf_(10^6, 0.1) + Traceback (most recent call last): + ... + NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms. + sage: chebyshev_T(10^6, 0.1) + 0.636384327171504 """ - if args[0] in ZZ and args[0] >= 0: - return self._cheb_recur_(*args)[0] - try: real_parent = kwds['parent'] except KeyError: - real_parent = parent(args[-1]) + real_parent = parent(x) + + if not is_RealField(real_parent) and not is_ComplexField(real_parent): + # parent is not a real or complex field: figure out a good parent + if x in RR: + x = RR(x) + real_parent = RR + elif x in CC: + x = CC(x) + real_parent = CC + + if not is_RealField(real_parent) and not is_ComplexField(real_parent): + raise TypeError("cannot evaluate chebyshev_T with parent %s"%real_parent) - x_set = False - if hasattr(real_parent,"precision"): # Check if we have a data type with precision - x = args[-1] - step_parent = real_parent - x_set = True - else: - if args[-1] in RR: - x = RR(args[-1]) - step_parent = RR - x_set = True - elif args[-1] in CC: - x = CC(args[-1]) - step_parent = CC - x_set = True - - if not x_set: - raise ValueError("No compatible type!") - from sage.libs.mpmath.all import call as mpcall from sage.libs.mpmath.all import chebyt as mpchebyt - return mpcall(mpchebyt,args[0],x,parent=step_parent) + return mpcall(mpchebyt, n, x, parent=real_parent) - def _maxima_init_evaled_(self, *args): + def _maxima_init_evaled_(self, n, x): """ Evaluate the Chebyshev polynomial ``self`` with maxima. EXAMPLES:: + sage: var('n, x') + (n, x) sage: chebyshev_T._maxima_init_evaled_(1,x) 'x' - sage: var('n') - n - sage: maxima(chebyshev_T(n,x)) - chebyshev_t(n,x) + sage: maxima(chebyshev_T(n, chebyshev_T(n, x))) + chebyshev_t(n,chebyshev_t(n,x)) """ - n = args[0] - x = args[1] - return maxima.eval('chebyshev_t({0},{1})'.format(n,x)) + return maxima.eval('chebyshev_t({0},{1})'.format(n._maxima_init_(), x._maxima_init_())) + - - def _apply_formula_(self,*args): + def eval_formula(self, n, x): """ - Applies explicit formulas for :class:`chebyshev_T`. - This is much faster for numerical evaluation than maxima! + Evaluate ``chebyshev_T`` using an explicit formula. See [ASHandbook]_ 227 (p. 782) for details for the recurions. See also [EffCheby]_ for fast evaluation techniques. + INPUT: + + - ``n`` -- an integer + + - ``x`` -- a value to evaluate the polynomial at (this can be + any ring element) + EXAMPLES:: - sage: chebyshev_T._apply_formula_(2,0.1) == chebyshev_T._evalf_(2,0.1) + sage: chebyshev_T.eval_formula(-1,x) + x + sage: chebyshev_T.eval_formula(0,x) + 1 + sage: chebyshev_T.eval_formula(1,x) + x + sage: chebyshev_T.eval_formula(2,0.1) == chebyshev_T._evalf_(2,0.1) True - sage: chebyshev_T(51,x) - 2*(2*(2*(2*(2*(2*x^2 - 1)^2 - 1)*(2*(2*x^2 - 1)*x - x) - x)*(2*(2*(2*x^2 - 1)*x - x)^2 - 1) - x)^2 - 1)*(2*(2*(2*(2*(2*x^2 - 1)^2 - 1)*(2*(2*x^2 - 1)*x - x) - x)*(2*(2*(2*x^2 - 1)*x - x)^2 - 1) - x)*(2*(2*(2*(2*x^2 - 1)*x - x)^2 - 1)^2 - 1) - x) - x - sage: chebyshev_T._apply_formula_(10,x) + sage: chebyshev_T.eval_formula(10,x) + 512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1 + sage: chebyshev_T.eval_algebraic(10,x).expand() 512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1 """ - k = args[0] - x = args[1] + if n < 0: + return self.eval_formula(-n, x) + elif n == 0: + return parent(x).one() + + res = parent(x).zero() + for j in xrange(0, n//2+1): + f = factorial(n-1-j) / factorial(j) / factorial(n-2*j) + res += (-1)**j * (2*x)**(n-2*j) * f + res *= n/2 + return res + + def eval_algebraic(self, n, x): + """ + Evaluate :class:`chebyshev_T` as polynomial, using a recursive + formula. - if k == 0: - return 1 - if k == 1: - return x - - help1 = 1 - help2 = x - if is_Expression(x) and k <= 25: - # Recursion gives more compact representations for large k - help3 = 0 - for j in xrange(0,floor(k/2)+1): - f = factorial(k-j-1) / factorial(j) / factorial(k-2*j) - help3 = help3 + (-1)**j * (2*x)**(k-2*j) * f - help3 = help3 * k / 2 - return help3 + INPUT: + + - ``n`` -- an integer + + - ``x`` -- a value to evaluate the polynomial at (this can be + any ring element) + + EXAMPLES:: + + sage: chebyshev_T.eval_algebraic(5, x) + 2*(2*(2*x^2 - 1)*x - x)*(2*x^2 - 1) - x + sage: chebyshev_T(-7, x) - chebyshev_T(7,x) + 0 + sage: R. = ZZ[] + sage: chebyshev_T.eval_algebraic(-1, t) + t + sage: chebyshev_T.eval_algebraic(0, t) + 1 + sage: chebyshev_T.eval_algebraic(1, t) + t + sage: chebyshev_T(7^100, 1/2) + 1/2 + sage: chebyshev_T(7^100, Mod(2,3)) + 2 + sage: n = 97; x = RIF(pi/2/n) + sage: chebyshev_T(n, cos(x)).contains_zero() + True + sage: R. = Zp(2, 8, 'capped-abs')[] + sage: chebyshev_T(10^6+1, t) + (2^7 + O(2^8))*t^5 + (O(2^8))*t^4 + (2^6 + O(2^8))*t^3 + (O(2^8))*t^2 + (1 + 2^6 + O(2^8))*t + (O(2^8)) + """ + if n == 0: + return parent(x).one() + if n > 0: + return self._eval_recursive_(n, x)[0] + else: + return self._eval_recursive_(-n, x)[0] + + def _eval_recursive_(self, n, x, both=False): + """ + If ``both=True``, compute `(T(n,x), T(n-1,x))` using a + recursive formula. + If ``both=False``, return instead a tuple `(T(n,x), False)`. + + EXAMPLES:: + + sage: chebyshev_T._eval_recursive_(5, x) + (2*(2*(2*x^2 - 1)*x - x)*(2*x^2 - 1) - x, False) + sage: chebyshev_T._eval_recursive_(5, x, True) + (2*(2*(2*x^2 - 1)*x - x)*(2*x^2 - 1) - x, 2*(2*x^2 - 1)^2 - 1) + """ + if n == 1: + return x, parent(x).one() + + assert n >= 2 + a, b = self._eval_recursive_((n+1)//2, x, both or n % 2) + if n % 2 == 0: + return 2*a*a - 1, both and 2*a*b - x else: - return self._cheb_recur_(k,x)[0] - - def _cheb_recur_(self,n, x, both=False): - """ - Generalized recursion formula for Chebyshev polynomials. - Implementation suggested by Frederik Johansson. - returns (T(n,x), T(n-1,x)), or (T(n,x), _) if both=False - - EXAMPLES:: - - sage: chebyshev_T._cheb_recur_(5,x) - (2*(2*(2*x^2 - 1)*x - x)*(2*x^2 - 1) - x, False) - """ - if n == 0: - return 1, x - if n == 1: - return x, 1 - a, b = self._cheb_recur_((n+1)//2, x, both or n % 2) - if n % 2 == 0: - return 2*a**2 - 1, both and 2*a*b - x - else: - return 2*a*b - x, both and 2*b**2 - 1 - - - def _eval_numpy_(self, *args): + return 2*a*b - x, both and 2*b*b - 1 + + + def _eval_numpy_(self, n, x): """ Evaluate ``self`` using numpy. @@ -798,19 +813,19 @@ def _eval_numpy_(self, *args): sage: z2 = numpy.array([[1,2],[1,2]]) sage: z3 = numpy.array([1,2,3.]) sage: chebyshev_T(1,z) - array([1, 2]) + array([ 1., 2.]) sage: chebyshev_T(1,z2) - array([[1, 2], - [1, 2]]) + array([[ 1., 2.], + [ 1., 2.]]) sage: chebyshev_T(1,z3) array([ 1., 2., 3.]) sage: chebyshev_T(z,0.1) array([ 0.1 , -0.98]) """ from scipy.special import eval_chebyt - return eval_chebyt(args[0],args[-1]) + return eval_chebyt(n, x) - def _derivative_(self, *args, **kwds): + def _derivative_(self, n, x, diff_param): """ Return the derivative of :class:`chebyshev_T` in form of the Chebyshev polynomial of the second kind :class:`chebyshev_U`. @@ -828,12 +843,13 @@ def _derivative_(self, *args, **kwds): ... NotImplementedError: derivative w.r.t. to the index is not supported yet """ - diff_param = kwds['diff_param'] - if diff_param == 0: + if diff_param == 0: raise NotImplementedError("derivative w.r.t. to the index is not supported yet") + elif diff_param == 1: + return n*chebyshev_U(n-1, x) + else: + raise ValueError("illegal differentiation parameter %s"%diff_param) - return args[0]*chebyshev_U(args[0]-1,args[1]) - chebyshev_T = Func_chebyshev_T() class Func_chebyshev_U(OrthogonalPolynomial): @@ -846,18 +862,18 @@ class Func_chebyshev_U(OrthogonalPolynomial): EXAMPLES:: - sage: x = PolynomialRing(QQ, 'x').gen() - sage: chebyshev_U(2,x) - 4*x^2 - 1 - sage: chebyshev_U(3,x) - 8*x^3 - 4*x + sage: R. = QQ[] + sage: chebyshev_U(2,t) + 4*t^2 - 1 + sage: chebyshev_U(3,t) + 8*t^3 - 4*t """ def __init__(self): """ Init method for the chebyshev polynomials of the second kind. EXAMPLES:: - + sage: from sage.functions.orthogonal_polys import Func_chebyshev_U sage: chebyshev_U2 = Func_chebyshev_U() sage: chebyshev_U2(1,x) @@ -866,92 +882,137 @@ def __init__(self): OrthogonalPolynomial.__init__(self, "chebyshev_U", nargs=2, conversions=dict(maxima='chebyshev_u', mathematica='ChebyshevU')) - - def _apply_formula_(self,*args): + + def eval_formula(self, n, x): """ - Applies explicit formulas for :class:`chebyshev_U`. - This is much faster for numerical evaluation than maxima. + Evaluate ``chebyshev_U`` using an explicit formula. See [ASHandbook]_ 227 (p. 782) for details on the recurions. See also [EffCheby]_ for the recursion formulas. + INPUT: + + - ``n`` -- an integer + + - ``x`` -- a value to evaluate the polynomial at (this can be + any ring element) + EXAMPLES:: - sage: chebyshev_U._apply_formula_(2,0.1) == chebyshev_U._evalf_(2,0.1) + sage: chebyshev_U.eval_formula(10, x) + 1024*x^10 - 2304*x^8 + 1792*x^6 - 560*x^4 + 60*x^2 - 1 + sage: chebyshev_U.eval_formula(-2, x) + -1 + sage: chebyshev_U.eval_formula(-1, x) + 0 + sage: chebyshev_U.eval_formula(0, x) + 1 + sage: chebyshev_U.eval_formula(1, x) + 2*x + sage: chebyshev_U.eval_formula(2,0.1) == chebyshev_U._evalf_(2,0.1) True """ - k = args[0] - x = args[1] - - if k == 0: - return 1 - if k == 1: - return 2*x - - help1 = 1 - help2 = 2*x - if is_Expression(x) and k <= 25: - # Recursion gives more compact representations for large k - help3 = 0 - for j in xrange(0,floor(k/2)+1): - f = factorial(k-j) / factorial(j) / factorial(k-2*j) # Change to a binomial? - help3 = help3 + (-1)**j * (2*x)**(k-2*j) * f - return help3 - - else: - return self._cheb_recur_(k,x)[0] - - - def _cheb_recur_(self,n, x, both=False): - """ - Generalized recursion formula for Chebyshev polynomials. - Implementation suggested by Frederik Johansson. - returns (U(n,x), U(n-1,x)), or (U(n,x), _) if both=False - - EXAMPLES:: - - sage: chebyshev_U._cheb_recur_(3,x) - (4*(2*x^2 - 1)*x, False) - sage: chebyshev_U._cheb_recur_(5,x)[0] + if n < -1: + return -self.eval_formula(-n-2, x) + + res = parent(x).zero() + for j in xrange(0, n//2+1): + f = binomial(n-j, j) + res += (-1)**j * (2*x)**(n-2*j) * f + return res + + def eval_algebraic(self, n, x): + """ + Evaluate :class:`chebyshev_U` as polynomial, using a recursive + formula. + + INPUT: + + - ``n`` -- an integer + + - ``x`` -- a value to evaluate the polynomial at (this can be + any ring element) + + EXAMPLES:: + + sage: chebyshev_U.eval_algebraic(5,x) -2*((2*x + 1)*(2*x - 1)*x - 4*(2*x^2 - 1)*x)*(2*x + 1)*(2*x - 1) - sage: abs(pari('polchebyshev(5, 2, 0.1)') - chebyshev_U(5,0.1)) < 1e-10 + sage: parent(chebyshev_U(3, Mod(8,9))) + Ring of integers modulo 9 + sage: parent(chebyshev_U(3, Mod(1,9))) + Ring of integers modulo 9 + sage: chebyshev_U(-3,x) + chebyshev_U(1,x) + 0 + sage: chebyshev_U(-1,Mod(5,8)) + 0 + sage: parent(chebyshev_U(-1,Mod(5,8))) + Ring of integers modulo 8 + sage: R. = ZZ[] + sage: chebyshev_U.eval_algebraic(-2, t) + -1 + sage: chebyshev_U.eval_algebraic(-1, t) + 0 + sage: chebyshev_U.eval_algebraic(0, t) + 1 + sage: chebyshev_U.eval_algebraic(1, t) + 2*t + sage: n = 97; x = RIF(pi/n) + sage: chebyshev_U(n-1, cos(x)).contains_zero() True - """ - - if n == 0: - return 1, both and 2*x - if n == 1: - return 2*x, both and 4*x**2-1 - - a, b = self._cheb_recur_((n-1)//2, x, True) - if n % 2 == 0: - return (b+a)*(b-a), both and 2*b*(x*b-a) - else: - return 2*a*(b-x*a), both and (b+a)*(b-a) + sage: R. = Zp(2, 6, 'capped-abs')[] + sage: chebyshev_U(10^6+1, t) + (2 + O(2^6))*t + (O(2^6)) + """ + if n == -1: + return parent(x).zero() + if n >= 0: + return self._eval_recursive_(n, x)[0] + else: + return -self._eval_recursive_(-n-2, x)[0] + + def _eval_recursive_(self, n, x, both=False): + """ + If ``both=True``, compute `(U(n,x), U(n-1,x))` using a + recursive formula. + If ``both=False``, return instead a tuple `(U(n,x), False)`. + + EXAMPLES:: + + sage: chebyshev_U._eval_recursive_(3, x) + (4*((2*x + 1)*(2*x - 1) - 2*x^2)*x, False) + sage: chebyshev_U._eval_recursive_(3, x, True) + (4*((2*x + 1)*(2*x - 1) - 2*x^2)*x, ((2*x + 1)*(2*x - 1) + 2*x)*((2*x + 1)*(2*x - 1) - 2*x)) - def _maxima_init_evaled_(self, *args): + """ + if n == 0: + return parent(x).one(), 2*x + + assert n >= 1 + a, b = self._eval_recursive_((n-1)//2, x, True) + if n % 2 == 0: + return (b+a)*(b-a), both and 2*b*(x*b-a) + else: + return 2*a*(b-x*a), both and (b+a)*(b-a) + + def _maxima_init_evaled_(self, n, x): """ Uses maxima to evaluate ``self``. EXAMPLES:: + sage: var('n, x') + (n, x) sage: maxima(chebyshev_U(5,x)) 32*x^5-32*x^3+6*x - sage: var('n') - n sage: maxima(chebyshev_U(n,x)) chebyshev_u(n,x) sage: maxima(chebyshev_U(2,x)) 4*x^2-1 """ - n = args[0] - x = args[1] - return maxima.eval('chebyshev_u({0},{1})'.format(n,x)) + return maxima.eval('chebyshev_u({0},{1})'.format(n._maxima_init_(), x._maxima_init_())) - def _evalf_(self, *args,**kwds): + def _evalf_(self, n, x, **kwds): """ Evaluate :class:`chebyshev_U` numerically with mpmath. - If index is an integer use recursive formula since it is faster, - for chebyshev polynomials. EXAMPLES:: @@ -962,92 +1023,62 @@ def _evalf_(self, *args,**kwds): sage: chebyshev_U._evalf_(1.5, Mod(8,9)) Traceback (most recent call last): ... - ValueError: No compatible type! + TypeError: cannot evaluate chebyshev_U with parent Ring of integers modulo 9 """ - if args[0] in ZZ and args[0] >= 0: - return self._cheb_recur_(*args)[0] try: real_parent = kwds['parent'] except KeyError: - real_parent = parent(args[-1]) + real_parent = parent(x) - x_set = False - if hasattr(real_parent,"precision"): # Check if we have a data type with precision - x = args[-1] - step_parent = real_parent - x_set = True - else: - if args[-1] in RR: - x = RR(args[-1]) - step_parent = RR - x_set = True - elif args[-1] in CC: - x = CC(args[-1]) - step_parent = CC - x_set = True - - if not x_set: - raise ValueError("No compatible type!") + if not is_RealField(real_parent) and not is_ComplexField(real_parent): + # parent is not a real or complex field: figure out a good parent + if x in RR: + x = RR(x) + real_parent = RR + elif x in CC: + x = CC(x) + real_parent = CC + + if not is_RealField(real_parent) and not is_ComplexField(real_parent): + raise TypeError("cannot evaluate chebyshev_U with parent %s"%real_parent) from sage.libs.mpmath.all import call as mpcall from sage.libs.mpmath.all import chebyu as mpchebyu - return mpcall(mpchebyu,args[0],args[-1],parent = step_parent) + return mpcall(mpchebyu, n, x, parent=real_parent) - def _eval_special_values_(self,*args): + def _eval_special_values_(self, n, x): """ - Special values that known. [ASHandbook]_ 22.4 (p.777). + Values known for special values of x. + See [ASHandbook]_ 22.4 (p.777). EXAMPLES:: - + sage: var('n') n sage: chebyshev_U(n,1) n + 1 + sage: chebyshev_U(n,0) + 1/2*(-1)^(1/2*n)*((-1)^n + 1) sage: chebyshev_U(n,-1) (-1)^n*(n + 1) - sage: chebyshev_U._eval_special_values_(26, Mod(0,9)) + sage: chebyshev_U._eval_special_values_(n, 2) Traceback (most recent call last): ... - ValueError: Value not found! - sage: parent(chebyshev_U(3, Mod(8,9))) - Ring of integers modulo 9 - sage: parent(chebyshev_U(3, Mod(1,9))) - Ring of integers modulo 9 - sage: chebyshev_U(n, 0) - 1/2*(-1)^(1/2*n)*((-1)^n + 1) - sage: chebyshev_U(-3,x) + chebyshev_U(1,x) - 0 - sage: chebyshev_U._eval_special_values_(1.5, Mod(8,9)) - Traceback (most recent call last): - ... - ValueError: No special values for non integral indices! - sage: chebyshev_U(-1,Mod(5,8)) - 0 - sage: parent(chebyshev_U(-1,Mod(5,8))) - Ring of integers modulo 8 + ValueError: no special value found """ - if (not is_Expression(args[0])) and (not args[0] in ZZ): - raise ValueError("No special values for non integral indices!") - - if args[0] == -1: - return args[-1]*0 - - if args[-1] == 1: - return args[-1]*(args[0]+1) - - if args[-1] == -1: - return args[-1]**args[0]*(args[0]+1) + if x == 1: + return x*(n+1) - if (args[-1] == 0 and args[-1] in CC): - return (1+(-1)**args[0])*(-1)**(args[0]/2)/2 + if x == -1: + return x**n*(n+1) - if args[0] < 0 and args[0] in ZZ: - return -self._eval_(-args[0]-2,args[-1]) + if x == 0: + return (1+(-1)**n)*(-1)**(n/2)/2 - raise ValueError("Value not found!") + raise ValueError("no special value found") - def _eval_numpy_(self, *args): + def _eval_numpy_(self, n, x): """ Evaluate ``self`` using numpy. @@ -1058,24 +1089,24 @@ def _eval_numpy_(self, *args): sage: z2 = numpy.array([[1,2],[1,2]]) sage: z3 = numpy.array([1,2,3.]) sage: chebyshev_U(1,z) - array([2, 4]) + array([ 2., 4.]) sage: chebyshev_U(1,z2) - array([[2, 4], - [2, 4]]) + array([[ 2., 4.], + [ 2., 4.]]) sage: chebyshev_U(1,z3) array([ 2., 4., 6.]) sage: chebyshev_U(z,0.1) array([ 0.2 , -0.96]) """ from scipy.special import eval_chebyu - return eval_chebyu(args[0],args[1]) + return eval_chebyu(n, x) - def _derivative_(self, *args, **kwds): + def _derivative_(self, n, x, diff_param): """ Return the derivative of :class:`chebyshev_U` in form of the Chebyshev polynomials of the first and second kind. - + EXAMPLES:: sage: var('k') @@ -1089,12 +1120,12 @@ def _derivative_(self, *args, **kwds): ... NotImplementedError: derivative w.r.t. to the index is not supported yet """ - diff_param = kwds['diff_param'] - if diff_param == 0: + if diff_param == 0: raise NotImplementedError("derivative w.r.t. to the index is not supported yet") - - return ((args[0]+1)*chebyshev_T(args[0]+1,args[1])-args[1]* - chebyshev_U(args[0],args[1]))/(args[1]**2-1) + elif diff_param == 1: + return ((n+1)*chebyshev_T(n+1, x) - x*chebyshev_U(n,x))/(x*x-1) + else: + raise ValueError("illegal differentiation parameter %s"%diff_param) chebyshev_U = Func_chebyshev_U() @@ -1122,7 +1153,6 @@ def gen_laguerre(n,a,x): sage: gen_laguerre(3,0,x) -1/6*x^3 + 3/2*x^2 - 3*x + 1 """ - from sage.functions.all import sqrt _init() return sage_eval(maxima.eval('gen_laguerre(%s,%s,x)'%(ZZ(n),a)), locals={'x':x}) From 51fcd6b7d484a67d9b91c881be02b79deb6afdbd Mon Sep 17 00:00:00 2001 From: Stefan Reiterer Date: Mon, 9 Dec 2013 22:00:56 +0000 Subject: [PATCH 198/206] trac 9706: Propose new class structure --- src/sage/functions/orthogonal_polys.py | 135 ++++++++++++++++--------- 1 file changed, 88 insertions(+), 47 deletions(-) diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index a7a036d1a30..314efc5cff7 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -446,6 +446,85 @@ def _eval_(self, n, *args): The _eval_ method decides which evaluation suits best for the given input, and returns a proper value. + EXAMPLES:: + + sage: var('n,x') + (n, x) + sage: chebyshev_T(5,x) + 16*x^5 - 20*x^3 + 5*x + """ + return None + + def __call__(self, n, *args, **kwds): + """ + This overides the call method from SageObject to avoid problems with coercions, + since the _eval_ method is able to handle more data types than symbolic functions + would normally allow. + Thus we have the distinction between algebraic objects (if n is an integer), + and else as symbolic function. + + EXAMPLES:: + + sage: K. = NumberField(x^3-x-1) + sage: chebyshev_T(5, a) + 16*a^2 + a - 4 + """ + return super(OrthogonalPolynomial,self).__call__(n, *args, **kwds) + + + +class ChebyshevPolynomial(OrthogonalPolynomial): + """ + Super class for Chebyshev polynomials of the first and second kind. + + EXAMPLES:: + + sage: chebyshev_T(3,x) + 4*x^3 - 3*x + + """ + + def __call__(self, n, *args, **kwds): + """ + This overides the call method from SageObject to avoid problems with coercions, + since the _eval_ method is able to handle more data types than symbolic functions + would normally allow. + Thus we have the distinction between algebraic objects (if n is an integer), + and else as symbolic function. + + EXAMPLES:: + + sage: K. = NumberField(x^3-x-1) + sage: chebyshev_T(5, a) + 16*a^2 + a - 4 + sage: chebyshev_T(5,MatrixSpace(ZZ, 2)([1, 2, -4, 7])) + [-40799 44162] + [-88324 91687] + sage: R. = QQ[] + sage: parent(chebyshev_T(5, x)) + Univariate Polynomial Ring in x over Rational Field + sage: chebyshev_T(5, 2, hold=True) + chebyshev_T(5, 2) + sage: chebyshev_T(1,2,3) + Traceback (most recent call last): + ... + TypeError: Symbolic function chebyshev_T takes exactly 2 arguments (3 given) + """ + # If n is an integer: consider the polynomial as an algebraic (not symbolic) object + if n in ZZ and not kwds.get('hold', False): + try: + return self._eval_(n, *args) + except StandardError: + pass + + return super(ChebyshevPolynomial,self).__call__(n, *args, **kwds) + + + def _eval_(self, n, x): + """ + The _eval_ method decides which evaluation suits best + for the given input, and returns a proper value. + EXAMPLES:: sage: var('n,x') @@ -478,21 +557,19 @@ def _eval_(self, n, *args): sage: chebyshev_U._eval_(1.5, Mod(8,9)) is None True """ - args_is_symbolic = any(is_Expression(x) for x in args) - # n is an integer => evaluate algebraically (as polynomial) if n in ZZ: n = ZZ(n) # Expanded symbolic expression only for small values of n - if args_is_symbolic and n.abs() < 32: - return self.eval_formula(n, *args) + if is_Expression(x) and n.abs() < 32: + return self.eval_formula(n, x) else: - return self.eval_algebraic(n, *args) + return self.eval_algebraic(n, x) - if args_is_symbolic or is_Expression(n): + if is_Expression(x) or is_Expression(n): # Check for known identities try: - return self._eval_special_values_(n, *args) + return self._eval_special_values_(n, x) except ValueError: # Don't evaluate => keep symbolic return None @@ -501,7 +578,7 @@ def _eval_(self, n, *args): # We assume n and x are real/complex and evaluate numerically try: import sage.libs.mpmath.all as mpmath - return self._evalf_(n, *args) + return self._evalf_(n, x) except mpmath.NoConvergence: warnings.warn("mpmath failed, keeping expression unevaluated", RuntimeWarning) @@ -510,42 +587,8 @@ def _eval_(self, n, *args): # Numerical evaluation failed => keep symbolic return None - def __call__(self, n, *args, **kwds): - """ - This overides the call method from SageObject to avoid problems with coercions, - since the _eval_ method is able to handle more data types than symbolic functions - would normally allow. - Thus we have the distinction between algebraic objects (if n is an integer), - and else as symbolic function. - - EXAMPLES:: - - sage: K. = NumberField(x^3-x-1) - sage: chebyshev_T(5, a) - 16*a^2 + a - 4 - sage: chebyshev_T(5,MatrixSpace(ZZ, 2)([1, 2, -4, 7])) - [-40799 44162] - [-88324 91687] - sage: R. = QQ[] - sage: parent(chebyshev_T(5, x)) - Univariate Polynomial Ring in x over Rational Field - sage: chebyshev_T(5, 2, hold=True) - chebyshev_T(5, 2) - sage: chebyshev_T(1,2,3) - Traceback (most recent call last): - ... - TypeError: Symbolic function chebyshev_T takes exactly 2 arguments (3 given) - """ - # If n is an integer: consider the polynomial as an algebraic (not symbolic) object - if n in ZZ and not kwds.get('hold', False): - try: - return self._eval_(n, *args) - except StandardError: - pass - - return super(OrthogonalPolynomial,self).__call__(n, *args, **kwds) - -class Func_chebyshev_T(OrthogonalPolynomial): + +class Func_chebyshev_T(ChebyshevPolynomial): """ Chebyshev polynomials of the first kind. @@ -555,8 +598,6 @@ class Func_chebyshev_T(OrthogonalPolynomial): EXAMPLES:: - sage: chebyshev_T(3,x) - 4*x^3 - 3*x sage: chebyshev_T(5,x) 16*x^5 - 20*x^3 + 5*x sage: var('k') @@ -852,7 +893,7 @@ def _derivative_(self, n, x, diff_param): chebyshev_T = Func_chebyshev_T() -class Func_chebyshev_U(OrthogonalPolynomial): +class Func_chebyshev_U(ChebyshevPolynomial): """ Class for the Chebyshev polynomial of the second kind. From bb227b60e93b7d8806b32b00874351b9201123cd Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 11 Dec 2013 22:40:12 +0000 Subject: [PATCH 199/206] #9706: review patch. --- src/sage/functions/orthogonal_polys.py | 61 ++++++++++---------------- 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index 314efc5cff7..0b83b0305e3 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -443,7 +443,7 @@ def _eval_special_values_(self, *args): def _eval_(self, n, *args): """ - The _eval_ method decides which evaluation suits best + The :meth:`_eval_()` method decides which evaluation suits best for the given input, and returns a proper value. EXAMPLES:: @@ -471,19 +471,15 @@ def __call__(self, n, *args, **kwds): """ return super(OrthogonalPolynomial,self).__call__(n, *args, **kwds) - - class ChebyshevPolynomial(OrthogonalPolynomial): """ - Super class for Chebyshev polynomials of the first and second kind. + Abstract base class for Chebyshev polynomials of the first and second kind. EXAMPLES:: sage: chebyshev_T(3,x) 4*x^3 - 3*x - """ - def __call__(self, n, *args, **kwds): """ This overides the call method from SageObject to avoid problems with coercions, @@ -518,11 +514,10 @@ def __call__(self, n, *args, **kwds): pass return super(ChebyshevPolynomial,self).__call__(n, *args, **kwds) - def _eval_(self, n, x): """ - The _eval_ method decides which evaluation suits best + The :meth:`_eval_()` method decides which evaluation suits best for the given input, and returns a proper value. EXAMPLES:: @@ -563,8 +558,7 @@ def _eval_(self, n, x): # Expanded symbolic expression only for small values of n if is_Expression(x) and n.abs() < 32: return self.eval_formula(n, x) - else: - return self.eval_algebraic(n, x) + return self.eval_algebraic(n, x) if is_Expression(x) or is_Expression(n): # Check for known identities @@ -617,9 +611,9 @@ def __init__(self): sage: chebyshev_T2(1,x) x """ - super(Func_chebyshev_T,self).__init__("chebyshev_T", nargs=2, - conversions=dict(maxima='chebyshev_t', - mathematica='ChebyshevT')) + ChebyshevPolynomial.__init__(self, "chebyshev_T", nargs=2, + conversions=dict(maxima='chebyshev_t', + mathematica='ChebyshevT')) def _eval_special_values_(self, n, x): """ @@ -711,7 +705,7 @@ def _evalf_(self, n, x, **kwds): real_parent = CC if not is_RealField(real_parent) and not is_ComplexField(real_parent): - raise TypeError("cannot evaluate chebyshev_T with parent %s"%real_parent) + raise TypeError("cannot evaluate chebyshev_T with parent {}".format(real_parent)) from sage.libs.mpmath.all import call as mpcall from sage.libs.mpmath.all import chebyt as mpchebyt @@ -730,11 +724,9 @@ def _maxima_init_evaled_(self, n, x): 'x' sage: maxima(chebyshev_T(n, chebyshev_T(n, x))) chebyshev_t(n,chebyshev_t(n,x)) - """ return maxima.eval('chebyshev_t({0},{1})'.format(n._maxima_init_(), x._maxima_init_())) - def eval_formula(self, n, x): """ Evaluate ``chebyshev_T`` using an explicit formula. @@ -762,7 +754,6 @@ def eval_formula(self, n, x): 512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1 sage: chebyshev_T.eval_algebraic(10,x).expand() 512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1 - """ if n < 0: return self.eval_formula(-n, x) @@ -814,16 +805,15 @@ def eval_algebraic(self, n, x): """ if n == 0: return parent(x).one() - if n > 0: - return self._eval_recursive_(n, x)[0] - else: + if n < 0: return self._eval_recursive_(-n, x)[0] + return self._eval_recursive_(n, x)[0] def _eval_recursive_(self, n, x, both=False): """ - If ``both=True``, compute `(T(n,x), T(n-1,x))` using a + If ``both=True``, compute ``(T(n,x), T(n-1,x))`` using a recursive formula. - If ``both=False``, return instead a tuple `(T(n,x), False)`. + If ``both=False``, return instead a tuple ``(T(n,x), False)``. EXAMPLES:: @@ -888,8 +878,7 @@ def _derivative_(self, n, x, diff_param): raise NotImplementedError("derivative w.r.t. to the index is not supported yet") elif diff_param == 1: return n*chebyshev_U(n-1, x) - else: - raise ValueError("illegal differentiation parameter %s"%diff_param) + raise ValueError("illegal differentiation parameter {}".format(diff_param)) chebyshev_T = Func_chebyshev_T() @@ -920,9 +909,9 @@ def __init__(self): sage: chebyshev_U2(1,x) 2*x """ - OrthogonalPolynomial.__init__(self, "chebyshev_U", nargs=2, - conversions=dict(maxima='chebyshev_u', - mathematica='ChebyshevU')) + ChebyshevPolynomial.__init__(self, "chebyshev_U", nargs=2, + conversions=dict(maxima='chebyshev_u', + mathematica='ChebyshevU')) def eval_formula(self, n, x): """ @@ -1005,16 +994,15 @@ def eval_algebraic(self, n, x): """ if n == -1: return parent(x).zero() - if n >= 0: - return self._eval_recursive_(n, x)[0] - else: + if n < 0: return -self._eval_recursive_(-n-2, x)[0] + return self._eval_recursive_(n, x)[0] def _eval_recursive_(self, n, x, both=False): """ - If ``both=True``, compute `(U(n,x), U(n-1,x))` using a + If ``both=True``, compute ``(U(n,x), U(n-1,x))`` using a recursive formula. - If ``both=False``, return instead a tuple `(U(n,x), False)`. + If ``both=False``, return instead a tuple ``(U(n,x), False)``. EXAMPLES:: @@ -1022,7 +1010,6 @@ def _eval_recursive_(self, n, x, both=False): (4*((2*x + 1)*(2*x - 1) - 2*x^2)*x, False) sage: chebyshev_U._eval_recursive_(3, x, True) (4*((2*x + 1)*(2*x - 1) - 2*x^2)*x, ((2*x + 1)*(2*x - 1) + 2*x)*((2*x + 1)*(2*x - 1) - 2*x)) - """ if n == 0: return parent(x).one(), 2*x @@ -1081,7 +1068,7 @@ def _evalf_(self, n, x, **kwds): real_parent = CC if not is_RealField(real_parent) and not is_ComplexField(real_parent): - raise TypeError("cannot evaluate chebyshev_U with parent %s"%real_parent) + raise TypeError("cannot evaluate chebyshev_U with parent {}".format(real_parent)) from sage.libs.mpmath.all import call as mpcall from sage.libs.mpmath.all import chebyu as mpchebyu @@ -1142,7 +1129,6 @@ def _eval_numpy_(self, n, x): from scipy.special import eval_chebyu return eval_chebyu(n, x) - def _derivative_(self, n, x, diff_param): """ Return the derivative of :class:`chebyshev_U` in form of the Chebyshev @@ -1164,9 +1150,8 @@ def _derivative_(self, n, x, diff_param): if diff_param == 0: raise NotImplementedError("derivative w.r.t. to the index is not supported yet") elif diff_param == 1: - return ((n+1)*chebyshev_T(n+1, x) - x*chebyshev_U(n,x))/(x*x-1) - else: - raise ValueError("illegal differentiation parameter %s"%diff_param) + return ((n+1)*chebyshev_T(n+1, x) - x*chebyshev_U(n,x)) / (x*x-1) + raise ValueError("illegal differentiation parameter {}".format(diff_param)) chebyshev_U = Func_chebyshev_U() From cfd405b9cee1887b44f6026f59722032d83548b5 Mon Sep 17 00:00:00 2001 From: Robert Harron Date: Mon, 8 Apr 2013 02:20:23 +0000 Subject: [PATCH 200/206] Trac 13101: Fix bug in enumerate_totallyreal_fields_all --- src/sage/rings/number_field/totallyreal_data.pyx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/totallyreal_data.pyx b/src/sage/rings/number_field/totallyreal_data.pyx index 363475fff91..2ab6def1394 100644 --- a/src/sage/rings/number_field/totallyreal_data.pyx +++ b/src/sage/rings/number_field/totallyreal_data.pyx @@ -5,7 +5,7 @@ AUTHORS: -- Craig Citro and John Voight (2007-11-04): * Type checking and other polishing. -- John Voight (2007-10-09): - * Improvements: Symth bound, Lagrange multipliers for b. + * Improvements: Smyth bound, Lagrange multipliers for b. -- John Voight (2007-09-19): * Various optimization tweaks. -- John Voight (2007-09-01): @@ -201,6 +201,13 @@ cpdef lagrange_degree_3(int n, int an1, int an2, int an3): [-1.0, -1.0] sage: sage.rings.number_field.totallyreal_data.lagrange_degree_3(3,6,1,2) # random [-5.8878, -5.8878] + + TESTS: + + The following was fixed in :trac:`13101`:: + + sage: enumerate_totallyreal_fields_all(8, 10^8) #long time + [] """ cdef double zmin, zmax, val cdef double *roots_data @@ -292,6 +299,9 @@ cpdef lagrange_degree_3(int n, int an1, int an2, int an3): rts = [rts[i][0] for i in range(len(rts))] z4minmax = [min(rts + z4minmax), max(rts + z4minmax)] + if len(z4minmax) < 1: + z4minmax = [0.0, -1.0] + return z4minmax cdef int __len_primes = 46 From a807b94a83397eb381d365460acd95321f0bd491 Mon Sep 17 00:00:00 2001 From: Frederic Chapoton Date: Tue, 20 Aug 2013 12:41:14 +0000 Subject: [PATCH 201/206] trac 13101 better doctest --- .../rings/number_field/totallyreal_data.pyx | 82 +++++++++++-------- .../rings/number_field/totallyreal_rel.py | 7 ++ 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/sage/rings/number_field/totallyreal_data.pyx b/src/sage/rings/number_field/totallyreal_data.pyx index 2ab6def1394..216727bd69e 100644 --- a/src/sage/rings/number_field/totallyreal_data.pyx +++ b/src/sage/rings/number_field/totallyreal_data.pyx @@ -49,48 +49,57 @@ cdef extern from "math.h": cdef int abs(int x) -#********************************************************************************* +#********************************************************************* # Auxiliary routine # Hermite constant, naive Newton-Raphson, and a specialized Lagrange # multiplier solver. -#********************************************************************************* +#********************************************************************* def hermite_constant(n): r""" - This function returns the nth Hermite constant (typically denoted - $gamma_n$), defined to be - $$ \max_L \min_{0 \neq x \in L} ||x||^2 $$ - where $L$ runs over all lattices of dimension $n$ and determinant $1$. - For $n \leq 8$ it returns the exact value of $\gamma_n$, and for $n > 9$ - it returns an upper bound on $\gamma_n$. + This function returns the nth Hermite constant + + The nth Hermite constant (typically denoted $gamma_n$), is defined + to be $$ \max_L \min_{0 \neq x \in L} ||x||^2 $$ where $L$ runs + over all lattices of dimension $n$ and determinant $1$. + + For $n \leq 8$ it returns the exact value of $\gamma_n$, and for + $n > 9$ it returns an upper bound on $\gamma_n$. INPUT: - n -- integer + + - n -- integer OUTPUT: - (an upper bound for) the Hermite constant gamma_n - EXAMPLES: - sage: hermite_constant(1) # trivial one-dimensional lattice - 1.0 - sage: hermite_constant(2) # Eisenstein lattice - 1.1547005383792515 - sage: 2/sqrt(3.) - 1.15470053837925 - sage: hermite_constant(8) # E_8 - 2.0 - - NOTES: - The upper bounds used can be found in [CS] and [CE]. + - (an upper bound for) the Hermite constant gamma_n + + EXAMPLES:: + + sage: hermite_constant(1) # trivial one-dimensional lattice + 1.0 + sage: hermite_constant(2) # Eisenstein lattice + 1.1547005383792515 + sage: 2/sqrt(3.) + 1.15470053837925 + sage: hermite_constant(8) # E_8 + 2.0 + + .. NOTE:: + + The upper bounds used can be found in [CS]_ and [CE]_. REFERENCES: - [CE] Henry Cohn and Noam Elkies, New upper bounds on sphere - packings I, Ann. Math. 157 (2003), 689--714. - [CS] J.H. Conway and N.J.A. Sloane, Sphere packings, lattices and - groups, 3rd. ed., Grundlehren der Mathematischen Wissenschaften, - vol. 290, Springer-Verlag, New York, 1999. + + .. [CE] Henry Cohn and Noam Elkies, New upper bounds on sphere + packings I, Ann. Math. 157 (2003), 689--714. + + .. [CS] J.H. Conway and N.J.A. Sloane, Sphere packings, lattices + and groups, 3rd. ed., Grundlehren der Mathematischen + Wissenschaften, vol. 290, Springer-Verlag, New York, 1999. AUTHORS: + - John Voight (2007-09-03) """ @@ -195,19 +204,20 @@ cpdef lagrange_degree_3(int n, int an1, int an2, int an3): We output the largest value of z which occurs. We use a precomputed elimination ideal. - EXAMPLES: + EXAMPLES:: + sage: ls = sage.rings.number_field.totallyreal_data.lagrange_degree_3(3,0,1,2) sage: [RealField(10)(x) for x in ls] [-1.0, -1.0] sage: sage.rings.number_field.totallyreal_data.lagrange_degree_3(3,6,1,2) # random [-5.8878, -5.8878] - + TESTS: - - The following was fixed in :trac:`13101`:: - - sage: enumerate_totallyreal_fields_all(8, 10^8) #long time - [] + + Check that :trac:`13101` is solved:: + + sage: sage.rings.number_field.totallyreal_data.lagrange_degree_3(4,12,19,42) + [0.0, -1.0] """ cdef double zmin, zmax, val cdef double *roots_data @@ -299,8 +309,8 @@ cpdef lagrange_degree_3(int n, int an1, int an2, int an3): rts = [rts[i][0] for i in range(len(rts))] z4minmax = [min(rts + z4minmax), max(rts + z4minmax)] - if len(z4minmax) < 1: - z4minmax = [0.0, -1.0] + if not z4minmax: + return [0.0, -1.0] return z4minmax diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py index 0eeb029cac0..221a205d87b 100644 --- a/src/sage/rings/number_field/totallyreal_rel.py +++ b/src/sage/rings/number_field/totallyreal_rel.py @@ -865,6 +865,13 @@ def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False): [2000, x^4 - 5*x^2 + 5]] In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). + + TESTS: + + The following was fixed in :trac:`13101`:: + + sage: enumerate_totallyreal_fields_all(8, 10^6) + [] """ S = [] From 3923f3ad585170c563a4ba7f4d3edbe1c6356f96 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 11 Dec 2013 12:19:29 +0000 Subject: [PATCH 202/206] Trac 13101: mark doctest as "long time" --- src/sage/rings/number_field/totallyreal_rel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py index 221a205d87b..c5d5993d20a 100644 --- a/src/sage/rings/number_field/totallyreal_rel.py +++ b/src/sage/rings/number_field/totallyreal_rel.py @@ -870,7 +870,7 @@ def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False): The following was fixed in :trac:`13101`:: - sage: enumerate_totallyreal_fields_all(8, 10^6) + sage: enumerate_totallyreal_fields_all(8, 10^6) # long time (about 2 s) [] """ From 1c869b360f1acd7e607efa43d867961c8eda9304 Mon Sep 17 00:00:00 2001 From: TB Date: Sat, 21 Dec 2013 17:59:58 +0200 Subject: [PATCH 203/206] minor typography --- src/sage/groups/perm_gps/permgroup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 49cf50b2863..a63fc604b2e 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -1614,7 +1614,7 @@ def structure_description(self, latex=False): For full details, including the form of the returned string and the algorithm to build it, see `GAP's documentation - `_ + `_. INPUT: @@ -1660,7 +1660,7 @@ def structure_description(self, latex=False): """ import re def correct_dihedral_degree(match): - return "%sD%d" % (match.group(1), int(match.group(2))/2) + return "%sD%d" % (match.group(1), int(match.group(2))/2) description = self._gap_().StructureDescription().str() description = re.sub(r"(\A|\W)D(\d+)", correct_dihedral_degree, description) From 90c5549704229895b2c24d19ecb908acf4031c53 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 21 Dec 2013 22:00:16 +0000 Subject: [PATCH 204/206] fix documentation --- src/sage/misc/cachefunc.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index f5286565fb4..7c0703e6e0c 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -2230,13 +2230,13 @@ cdef class CachedSpecialMethod(CachedMethod): provided by the class, not by the instance. By consequence, if ``__hash__`` would be wrapped by using - :class:`CachedMethod`, then ``hash(c)`` will access `C.__hash__` and bind + :class:`CachedMethod`, then ``hash(c)`` will access ``C.__hash__`` and bind it to ``c``, which means that the ``__get__`` method of :class:`CachedMethod` will be called. But there, we assume that Python has already inspected ``__dict__``, and thus a :class:`CachedMethodCaller` will be created over and over again. - Here, the `__get__` method will explicitly access the `__dict__`, so that + Here, the ``__get__`` method will explicitly access the ``__dict__``, so that ``hash(c)`` will rely on a single :class:`CachedMethodCaller` stored in the ``__dict__``. @@ -2264,7 +2264,7 @@ cdef class CachedSpecialMethod(CachedMethod): """ def __get__(self, object inst, cls): """ - Bind a :class:`CachedMethodCaller` to a specific instance, using `__dict__`. + Bind a :class:`CachedMethodCaller` to a specific instance, using ``__dict__``. EXAMPLES:: From 7a83737b8e1275b28c1dd03e89f3dd66377a8617 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 21 Dec 2013 22:14:09 +0000 Subject: [PATCH 205/206] fix latex --- src/sage/schemes/toric/homset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/toric/homset.py b/src/sage/schemes/toric/homset.py index b565e4e0b4d..40733fdbeb8 100644 --- a/src/sage/schemes/toric/homset.py +++ b/src/sage/schemes/toric/homset.py @@ -416,7 +416,7 @@ class SchemeHomset_points_toric_field(SchemeHomset_points_toric_base): [1 : 3 : 6]] As for a non-compact example, the blow-up of the plane is the line - bundle $O_{\mathbbb{P}^1}(-1)$. Its point set is the cartesian + bundle $O_{\mathbf{P}^1}(-1)$. Its point set is the cartesian product of the points on the base $\mathbf{P}^1$ with the points on the fiber:: From 036984d734e7a22f65ab657f24ea68e0149ac850 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Mon, 23 Dec 2013 12:06:33 +0000 Subject: [PATCH 206/206] Updated Sage version to 6.1.beta2 --- VERSION.txt | 2 +- src/bin/sage-banner | 2 +- src/bin/sage-version.sh | 4 ++-- src/sage/version.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 491e8d2cc2a..7f62da14c05 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 6.1.beta1, released 2013-12-21 +Sage version 6.1.beta2, released 2013-12-23 diff --git a/src/bin/sage-banner b/src/bin/sage-banner index aa40026722b..47a0551c44b 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ Sage Version 6.1.beta1, Release Date: 2013-12-21 │ +│ Sage Version 6.1.beta2, Release Date: 2013-12-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 3e205e11235..44537841a74 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='6.1.beta1' -SAGE_RELEASE_DATE='2013-12-21' +SAGE_VERSION='6.1.beta2' +SAGE_RELEASE_DATE='2013-12-23' diff --git a/src/sage/version.py b/src/sage/version.py index df25e3136bd..9a0bb32438e 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ """nodoctests""" # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '6.1.beta1' -date = '2013-12-21' +version = '6.1.beta2' +date = '2013-12-23'