def __call__(self, x, maximum_bits=20000): """ Allows an object of this class to behave like a function. If ``ceil`` is an instance of this class, we can do ``ceil(n)`` to get the ceiling of ``n``. TESTS:: sage: ceil(SR(10^50 + 10^(-50))) 100000000000000000000000000000000000000000000000001 sage: ceil(SR(10^50 - 10^(-50))) 100000000000000000000000000000000000000000000000000 sage: ceil(int(10^50)) 100000000000000000000000000000000000000000000000000 """ try: return x.ceil() except AttributeError: if isinstance(x, (int, long)): return Integer(x) elif isinstance(x, (float, complex)): return Integer(int(math.ceil(x))) elif type(x).__module__ == 'numpy': import numpy return numpy.ceil(x) x_original = x from sage.rings.all import RealIntervalField # If x can be coerced into a real interval, then we should # try increasing the number of bits of precision until # we get the ceiling at each of the endpoints is the same. # The precision will continue to be increased up to maximum_bits # of precision at which point it will raise a value error. bits = 53 try: x_interval = RealIntervalField(bits)(x) upper_ceil = x_interval.upper().ceil() lower_ceil = x_interval.lower().ceil() while upper_ceil != lower_ceil and bits < maximum_bits: bits += 100 x_interval = RealIntervalField(bits)(x) upper_ceil = x_interval.upper().ceil() lower_ceil = x_interval.lower().ceil() if bits < maximum_bits: return lower_ceil else: try: return ceil(SR(x).full_simplify()) except ValueError: pass raise ValueError, "x (= %s) requires more than %s bits of precision to compute its ceiling" % ( x, maximum_bits) except TypeError: # If x cannot be coerced into a RealField, then # it should be left as a symbolic expression. return BuiltinFunction.__call__(self, SR(x_original))
def _sage_(self): r""" Convert a maple expression back to a Sage expression. This currently does not implement a parser for the Maple output language, therefore only very simple expressions will convert successfully. EXAMPLES:: sage: m = maple('x^2 + 5*y') # optional - maple sage: m.sage() # optional - maple x^2 + 5*y sage: m._sage_() # optional - maple x^2 + 5*y :: sage: m = maple('sin(sqrt(1-x^2)) * (1 - cos(1/x))^2') # optional - maple sage: m.sage() # optional - maple (cos(1/x) - 1)^2*sin(sqrt(-x^2 + 1)) """ result = repr(self) # The next few lines are a very crude excuse for a maple "parser". result = result.replace("Pi", "pi") try: from sage.symbolic.all import SR return SR(result) except Exception: raise NotImplementedError("Unable to parse Maple output: %s" % result)
def automatic_name_eval(s, globals, max_names=10000): """ Exec the string ``s`` in the scope of the ``globals`` dictionary, and if any :exc:`NameError`\ s are raised, try to fix them by defining the variable that caused the error to be raised, then eval again. Try up to ``max_names`` times. INPUT: - ``s`` -- a string - ``globals`` -- a dictionary - ``max_names`` -- a positive integer (default: 10000) """ # This entire automatic naming system really boils down to # this bit of code below. We simply try to exec the string s # in the globals namespace, defining undefined variables and # functions until everything is defined. for _ in range(max_names): try: exec s in globals return except NameError, msg: # Determine if we hit a NameError that is probably # caused by a variable or function not being defined: if len(msg.args) == 0: raise # not NameError with # specific variable name v = msg.args[0].split("'") if len(v) < 2: raise # also not NameError with # specific variable name We did # find an undefined variable: we # simply define it and try # again. nm = v[1] globals[nm] = AutomaticVariable(SR, SR.var(nm))
def automatic_name_eval(s, globals, max_names=10000): r""" Exec the string ``s`` in the scope of the ``globals`` dictionary, and if any :exc:`NameError`\ s are raised, try to fix them by defining the variable that caused the error to be raised, then eval again. Try up to ``max_names`` times. INPUT: - ``s`` -- a string - ``globals`` -- a dictionary - ``max_names`` -- a positive integer (default: 10000) """ # This entire automatic naming system really boils down to # this bit of code below. We simply try to exec the string s # in the globals namespace, defining undefined variables and # functions until everything is defined. for _ in range(max_names): try: exec(s , globals) return except NameError as msg: # Determine if we hit a NameError that is probably # caused by a variable or function not being defined: if len(msg.args) == 0: raise # not NameError with # specific variable name v = msg.args[0].split("'") if len(v) < 2: raise # also not NameError with # specific variable name We did # find an undefined variable: we # simply define it and try # again. nm = v[1] globals[nm] = AutomaticVariable(SR, SR.var(nm)) raise NameError("Too many automatic variable names and functions created (limit=%s)" % max_names)
def _sage_(self): r""" Convert a giac expression back to a Sage expression. This currently does not implement a parser for the Giac output language, therefore only very simple expressions will convert successfully. Warning: List conversion is slow. EXAMPLE:: sage: m = giac('x^2 + 5*y') # optional - requires giac sage: m.sage() # optional - requires giac x^2 + 5*y :: sage: m = giac('sin(2*sqrt(1-x^2)) * (1 - cos(1/x))^2') # optional - requires giac sage: m.trigexpand().sage() # optional - requires giac 2*(cos(1/x) - 1)^2*sin(sqrt(-x^2 + 1))*cos(sqrt(-x^2 + 1)) """ result = repr(self) if str(self.type()) != 'DOM_LIST' : try: from sage.symbolic.all import SR return SR(result) except: raise NotImplementedError, "Unable to parse Giac output: %s" % result else: return [entry.sage() for entry in self]
def _do_sqrt(x, prec=None, extend=True, all=False): r""" Used internally to compute the square root of x. INPUT: - ``x`` - a number - ``prec`` - None (default) or a positive integer (bits of precision) If not None, then compute the square root numerically to prec bits of precision. - ``extend`` - bool (default: True); this is a place holder, and is always ignored since in the symbolic ring everything has a square root. - ``extend`` - bool (default: True); whether to extend the base ring to find roots. The extend parameter is ignored if prec is a positive integer. - ``all`` - bool (default: False); whether to return a list of all the square roots of x. EXAMPLES:: sage: from sage.functions.other import _do_sqrt sage: _do_sqrt(3) sqrt(3) sage: _do_sqrt(3,prec=10) 1.7 sage: _do_sqrt(3,prec=100) 1.7320508075688772935274463415 sage: _do_sqrt(3,all=True) [sqrt(3), -sqrt(3)] Note that the extend parameter is ignored in the symbolic ring:: sage: _do_sqrt(3,extend=False) sqrt(3) """ from sage.rings.all import RealField, ComplexField if prec: if x >= 0: return RealField(prec)(x).sqrt(all=all) else: return ComplexField(prec)(x).sqrt(all=all) if x == -1: from sage.symbolic.pynac import I z = I else: z = SR(x)**one_half if all: if z: return [z, -z] else: return [z] return z
def adapt_if_symbolic(f): """ If f is symbolic find the variables u, v to substitute into f. Otherwise raise a TypeError. This function is used internally by the plot commands for efficiency reasons only. """ from sage.symbolic.all import is_Expression, SR if sum([is_Expression(a) for a in f]) > 0: g = [SR(a) for a in f] vars = list(set(sum([list(a.variables()) for a in g], []))) vars.sort() if len(vars) > 0: u = vars[0] if len(vars) > 1: v = vars[1] else: v = None return g, u, v else: g = [lambda x: float(a) for a in g] return g, None, None
def plot_hyperplane(hyperplane, **kwds): r""" Return the plot of a single hyperplane. INPUT: - ``**kwds`` -- plot options: see below OUTPUT: A graphics object of the plot. .. RUBRIC:: Plot Options Beside the usual plot options (enter ``plot?``), the plot command for hyperplanes includes the following: - ``hyperplane_label`` -- Boolean value or string (default: ``True``). If ``True``, the hyperplane is labeled with its equation, if a string, it is labeled by that string, otherwise it is not labeled. - ``label_color`` -- (Default: ``'black'``) Color for hyperplane_label. - ``label_fontsize`` -- Size for ``hyperplane_label`` font (default: 14) (does not work in 3d, yet). - ``label_offset`` -- (Default: 0-dim: 0.1, 1-dim: (0,1), 2-dim: (0,0,0.2)) Amount by which label is offset from ``hyperplane.point()``. - ``point_size`` -- (Default: 50) Size of points in a zero-dimensional arrangement or of an arrangement over a finite field. - ``ranges`` -- Range for the parameters for the parametric plot of the hyperplane. If a single positive number ``r`` is given for the value of ``ranges``, then the ranges for all parameters are set to `[-r, r]`. Otherwise, for a line in the plane, ``ranges`` has the form ``[a, b]`` (default: [-3,3]), and for a plane in 3-space, the ``ranges`` has the form ``[[a, b], [c, d]]`` (default: [[-3,3],[-3,3]]). (The ranges are centered around ``hyperplane.point()``.) EXAMPLES:: sage: H1.<x> = HyperplaneArrangements(QQ) sage: a = 3*x + 4 sage: a.plot() # indirect doctest Graphics object consisting of 3 graphics primitives sage: a.plot(point_size=100,hyperplane_label='hello') Graphics object consisting of 3 graphics primitives sage: H2.<x,y> = HyperplaneArrangements(QQ) sage: b = 3*x + 4*y + 5 sage: b.plot() Graphics object consisting of 2 graphics primitives sage: b.plot(ranges=(1,5),label_offset=(2,-1)) Graphics object consisting of 2 graphics primitives sage: opts = {'hyperplane_label':True, 'label_color':'green', ....: 'label_fontsize':24, 'label_offset':(0,1.5)} sage: b.plot(**opts) Graphics object consisting of 2 graphics primitives sage: H3.<x,y,z> = HyperplaneArrangements(QQ) sage: c = 2*x + 3*y + 4*z + 5 sage: c.plot() Graphics3d Object sage: c.plot(label_offset=(1,0,1), color='green', label_color='red', frame=False) Graphics3d Object sage: d = -3*x + 2*y + 2*z + 3 sage: d.plot(opacity=0.8) Graphics3d Object sage: e = 4*x + 2*z + 3 sage: e.plot(ranges=[[-1,1],[0,8]], label_offset=(2,2,1), aspect_ratio=1) Graphics3d Object """ if hyperplane.base_ring().characteristic(): raise NotImplementedError('base field must have characteristic zero') elif hyperplane.dimension() not in [ 0, 1, 2 ]: # dimension of hyperplane, not ambient space raise ValueError('can only plot hyperplanes in dimensions 1, 2, 3') # handle extra keywords if 'hyperplane_label' in kwds: hyp_label = kwds.pop('hyperplane_label') if not hyp_label: has_hyp_label = False else: has_hyp_label = True else: # default hyp_label = True has_hyp_label = True if has_hyp_label: if hyp_label: # then label hyperplane with its equation if hyperplane.dimension() == 2: # jmol does not like latex label = hyperplane._repr_linear(include_zero=False) else: label = hyperplane._latex_() else: label = hyp_label # a string if 'label_color' in kwds: label_color = kwds.pop('label_color') else: label_color = 'black' if 'label_fontsize' in kwds: label_fontsize = kwds.pop('label_fontsize') else: label_fontsize = 14 if 'label_offset' in kwds: has_offset = True label_offset = kwds.pop('label_offset') else: has_offset = False # give default values below if 'point_size' in kwds: pt_size = kwds.pop('point_size') else: pt_size = 50 if 'ranges' in kwds: ranges_set = True ranges = kwds.pop('ranges') else: ranges_set = False # give default values below # the extra keywords have now been handled # now create the plot if hyperplane.dimension() == 0: # a point on a line x, = hyperplane.A() d = hyperplane.b() p = point((d / x, 0), size=pt_size, **kwds) if has_hyp_label: if not has_offset: label_offset = 0.1 p += text(label, (d / x, label_offset), color=label_color, fontsize=label_fontsize) p += text('', (d / x, label_offset + 0.4)) # add space at top if 'ymax' not in kwds: kwds['ymax'] = 0.5 elif hyperplane.dimension() == 1: # a line in the plane pnt = hyperplane.point() w = hyperplane.linear_part().matrix() t = SR.var('t') if ranges_set: if isinstance(ranges, (list, tuple)): t0, t1 = ranges else: # ranges should be a single positive number t0, t1 = -ranges, ranges else: # default t0, t1 = -3, 3 p = parametric_plot(pnt + t * w[0], (t, t0, t1), **kwds) if has_hyp_label: if has_offset: b0, b1 = label_offset else: b0, b1 = 0, 0.2 label = text(label, (pnt[0] + b0, pnt[1] + b1), color=label_color, fontsize=label_fontsize) p += label elif hyperplane.dimension() == 2: # a plane in 3-space pnt = hyperplane.point() w = hyperplane.linear_part().matrix() s, t = SR.var('s t') if ranges_set: if isinstance(ranges, (list, tuple)): s0, s1 = ranges[0] t0, t1 = ranges[1] else: # ranges should be a single positive integers s0, s1 = -ranges, ranges t0, t1 = -ranges, ranges else: # default s0, s1 = -3, 3 t0, t1 = -3, 3 p = parametric_plot3d(pnt + s * w[0] + t * w[1], (s, s0, s1), (t, t0, t1), **kwds) if has_hyp_label: if has_offset: b0, b1, b2 = label_offset else: b0, b1, b2 = 0, 0, 0 label = text3d(label, (pnt[0] + b0, pnt[1] + b1, pnt[2] + b2), color=label_color, fontsize=label_fontsize) p += label return p
def plot_hyperplane(hyperplane, **kwds): r""" Return the plot of a single hyperplane. INPUT: - ``**kwds`` -- plot options: see below OUTPUT: A graphics object of the plot. .. RUBRIC:: Plot Options Beside the usual plot options (enter ``plot?``), the plot command for hyperplanes includes the following: - ``hyperplane_label`` -- Boolean value or string (default: ``True``). If ``True``, the hyperplane is labeled with its equation, if a string, it is labeled by that string, otherwise it is not labeled. - ``label_color`` -- (Default: ``'black'``) Color for hyperplane_label. - ``label_fontsize`` -- Size for ``hyperplane_label`` font (default: 14) (does not work in 3d, yet). - ``label_offset`` -- (Default: 0-dim: 0.1, 1-dim: (0,1), 2-dim: (0,0,0.2)) Amount by which label is offset from ``hyperplane.point()``. - ``point_size`` -- (Default: 50) Size of points in a zero-dimensional arrangement or of an arrangement over a finite field. - ``ranges`` -- Range for the parameters for the parametric plot of the hyperplane. If a single positive number ``r`` is given for the value of ``ranges``, then the ranges for all parameters are set to `[-r, r]`. Otherwise, for a line in the plane, ``ranges`` has the form ``[a, b]`` (default: [-3,3]), and for a plane in 3-space, the ``ranges`` has the form ``[[a, b], [c, d]]`` (default: [[-3,3],[-3,3]]). (The ranges are centered around ``hyperplane.point()``.) EXAMPLES:: sage: H1.<x> = HyperplaneArrangements(QQ) sage: a = 3*x + 4 sage: a.plot() # indirect doctest Graphics object consisting of 3 graphics primitives sage: a.plot(point_size=100,hyperplane_label='hello') Graphics object consisting of 3 graphics primitives sage: H2.<x,y> = HyperplaneArrangements(QQ) sage: b = 3*x + 4*y + 5 sage: b.plot() Graphics object consisting of 2 graphics primitives sage: b.plot(ranges=(1,5),label_offset=(2,-1)) Graphics object consisting of 2 graphics primitives sage: opts = {'hyperplane_label':True, 'label_color':'green', ....: 'label_fontsize':24, 'label_offset':(0,1.5)} sage: b.plot(**opts) Graphics object consisting of 2 graphics primitives sage: H3.<x,y,z> = HyperplaneArrangements(QQ) sage: c = 2*x + 3*y + 4*z + 5 sage: c.plot() Graphics3d Object sage: c.plot(label_offset=(1,0,1), color='green', label_color='red', frame=False) Graphics3d Object sage: d = -3*x + 2*y + 2*z + 3 sage: d.plot(opacity=0.8) Graphics3d Object sage: e = 4*x + 2*z + 3 sage: e.plot(ranges=[[-1,1],[0,8]], label_offset=(2,2,1), aspect_ratio=1) Graphics3d Object """ if hyperplane.base_ring().characteristic() != 0: raise NotImplementedError('base field must have characteristic zero') elif hyperplane.dimension() not in [0, 1, 2]: # dimension of hyperplane, not ambient space raise ValueError('can only plot hyperplanes in dimensions 1, 2, 3') # handle extra keywords if 'hyperplane_label' in kwds: hyp_label = kwds.pop('hyperplane_label') if not hyp_label: has_hyp_label = False else: has_hyp_label = True else: # default hyp_label = True has_hyp_label = True if has_hyp_label: if hyp_label: # then label hyperplane with its equation if hyperplane.dimension() == 2: # jmol does not like latex label = hyperplane._repr_linear(include_zero=False) else: label = hyperplane._latex_() else: label = hyp_label # a string if 'label_color' in kwds: label_color = kwds.pop('label_color') else: label_color = 'black' if 'label_fontsize' in kwds: label_fontsize = kwds.pop('label_fontsize') else: label_fontsize = 14 if 'label_offset' in kwds: has_offset = True label_offset = kwds.pop('label_offset') else: has_offset = False # give default values below if 'point_size' in kwds: pt_size = kwds.pop('point_size') else: pt_size = 50 if 'ranges' in kwds: ranges_set = True ranges = kwds.pop('ranges') else: ranges_set = False # give default values below # the extra keywords have now been handled # now create the plot if hyperplane.dimension() == 0: # a point on a line x, = hyperplane.A() d = hyperplane.b() p = point((d/x,0), size = pt_size, **kwds) if has_hyp_label: if not has_offset: label_offset = 0.1 p += text(label, (d/x,label_offset), color=label_color,fontsize=label_fontsize) p += text('',(d/x,label_offset+0.4)) # add space at top if 'ymax' not in kwds: kwds['ymax'] = 0.5 elif hyperplane.dimension() == 1: # a line in the plane pnt = hyperplane.point() w = hyperplane.linear_part().matrix() x, y = hyperplane.A() d = hyperplane.b() t = SR.var('t') if ranges_set: if type(ranges) in [list,tuple]: t0, t1 = ranges else: # ranges should be a single positive number t0, t1 = -ranges, ranges else: # default t0, t1 = -3, 3 p = parametric_plot(pnt+t*w[0], (t,t0,t1), **kwds) if has_hyp_label: if has_offset: b0, b1 = label_offset else: b0, b1 = 0, 0.2 label = text(label,(pnt[0]+b0,pnt[1]+b1), color=label_color,fontsize=label_fontsize) p += label elif hyperplane.dimension() == 2: # a plane in 3-space pnt = hyperplane.point() w = hyperplane.linear_part().matrix() a, b, c = hyperplane.A() d = hyperplane.b() s,t = SR.var('s t') if ranges_set: if type(ranges) in [list,tuple]: s0, s1 = ranges[0] t0, t1 = ranges[1] else: # ranges should be a single positive integers s0, s1 = -ranges, ranges t0, t1 = -ranges, ranges else: # default s0, s1 = -3, 3 t0, t1 = -3, 3 p = parametric_plot3d(pnt+s*w[0]+t*w[1],(s,s0,s1),(t,t0,t1),**kwds) if has_hyp_label: if has_offset: b0, b1, b2 = label_offset else: b0, b1, b2 = 0, 0, 0 label = text3d(label,(pnt[0]+b0,pnt[1]+b1,pnt[2]+b2), color=label_color,fontsize=label_fontsize) p += label return p
def demazure_character(self, w, f=None): r""" Return the Demazure character associated to ``w``. INPUT: - ``w`` -- an element of the ambient weight lattice realization of the crystal, or a reduced word, or an element in the associated Weyl group OPTIONAL: - ``f`` -- a function from the crystal to a module This is currently only supported for crystals whose underlying weight space is the ambient space. The Demazure character is obtained by applying the Demazure operator `D_w` (see :meth:`sage.categories.regular_crystals.RegularCrystals.ParentMethods.demazure_operator`) to the highest weight element of the classical crystal. The simple Demazure operators `D_i` (see :meth:`sage.categories.regular_crystals.RegularCrystals.ElementMethods.demazure_operator_simple`) do not braid on the level of crystals, but on the level of characters they do. That is why it makes sense to input ``w`` either as a weight, a reduced word, or as an element of the underlying Weyl group. EXAMPLES:: sage: T = crystals.Tableaux(['A',2], shape = [2,1]) sage: e = T.weight_lattice_realization().basis() sage: weight = e[0] + 2*e[2] sage: weight.reduced_word() [2, 1] sage: T.demazure_character(weight) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x1*x3^2 sage: T = crystals.Tableaux(['A',3],shape=[2,1]) sage: T.demazure_character([1,2,3]) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3 sage: W = WeylGroup(['A',3]) sage: w = W.from_reduced_word([1,2,3]) sage: T.demazure_character(w) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3 sage: T = crystals.Tableaux(['B',2], shape = [2]) sage: e = T.weight_lattice_realization().basis() sage: weight = -2*e[1] sage: T.demazure_character(weight) x1^2 + x1*x2 + x2^2 + x1 + x2 + x1/x2 + 1/x2 + 1/x2^2 + 1 sage: T = crystals.Tableaux("B2",shape=[1/2,1/2]) sage: b2=WeylCharacterRing("B2",base_ring=QQ).ambient() sage: T.demazure_character([1,2],f=lambda x:b2(x.weight())) b2(-1/2,1/2) + b2(1/2,-1/2) + b2(1/2,1/2) REFERENCES: - [De1974]_ - [Ma2009]_ """ from sage.misc.misc_c import prod from sage.rings.integer_ring import ZZ if hasattr(w, 'reduced_word'): word = w.reduced_word() else: word = w n = self.weight_lattice_realization().n u = self.algebra(ZZ).sum_of_monomials(self.module_generators) u = self.demazure_operator(u, word) if f is None: from sage.symbolic.all import SR as P x = [P.var('x%s' % (i + 1)) for i in range(n)] # TODO: use P.linear_combination when PolynomialRing will be a ModulesWithBasis return sum((coeff * prod((x[i]**(c.weight()[i]) for i in range(n)), P.one()) for c, coeff in u), P.zero()) else: return sum(coeff * f(c) for c, coeff in u)
def demazure_character(self, w, f = None): r""" Return the Demazure character associated to ``w``. INPUT: - ``w`` -- an element of the ambient weight lattice realization of the crystal, or a reduced word, or an element in the associated Weyl group OPTIONAL: - ``f`` -- a function from the crystal to a module This is currently only supported for crystals whose underlying weight space is the ambient space. The Demazure character is obtained by applying the Demazure operator `D_w` (see :meth:`sage.categories.regular_crystals.RegularCrystals.ParentMethods.demazure_operator`) to the highest weight element of the classical crystal. The simple Demazure operators `D_i` (see :meth:`sage.categories.regular_crystals.RegularCrystals.ElementMethods.demazure_operator_simple`) do not braid on the level of crystals, but on the level of characters they do. That is why it makes sense to input ``w`` either as a weight, a reduced word, or as an element of the underlying Weyl group. EXAMPLES:: sage: T = crystals.Tableaux(['A',2], shape = [2,1]) sage: e = T.weight_lattice_realization().basis() sage: weight = e[0] + 2*e[2] sage: weight.reduced_word() [2, 1] sage: T.demazure_character(weight) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x1*x3^2 sage: T = crystals.Tableaux(['A',3],shape=[2,1]) sage: T.demazure_character([1,2,3]) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3 sage: W = WeylGroup(['A',3]) sage: w = W.from_reduced_word([1,2,3]) sage: T.demazure_character(w) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3 sage: T = crystals.Tableaux(['B',2], shape = [2]) sage: e = T.weight_lattice_realization().basis() sage: weight = -2*e[1] sage: T.demazure_character(weight) x1^2 + x1*x2 + x2^2 + x1 + x2 + x1/x2 + 1/x2 + 1/x2^2 + 1 sage: T = crystals.Tableaux("B2",shape=[1/2,1/2]) sage: b2=WeylCharacterRing("B2",base_ring=QQ).ambient() sage: T.demazure_character([1,2],f=lambda x:b2(x.weight())) b2(-1/2,1/2) + b2(1/2,-1/2) + b2(1/2,1/2) REFERENCES: - [De1974]_ - [Ma2009]_ """ from sage.misc.misc_c import prod from sage.rings.integer_ring import ZZ if hasattr(w, 'reduced_word'): word = w.reduced_word() else: word = w n = self.weight_lattice_realization().n u = self.algebra(ZZ).sum_of_monomials(self.module_generators) u = self.demazure_operator(u, word) if f is None: from sage.symbolic.all import SR as P x = [P.var('x%s' % (i+1)) for i in range(n)] # TODO: use P.linear_combination when PolynomialRing will be a ModulesWithBasis return sum((coeff*prod((x[i]**(c.weight()[i]) for i in range(n)), P.one()) for c, coeff in u), P.zero()) else: return sum(coeff * f(c) for c, coeff in u)
def _sage_(self): r""" Convert a maple expression back to a Sage expression. This currently does not implement a parser for the Maple output language, therefore only very simple expressions will convert successfully. EXAMPLES:: sage: m = maple('x^2 + 5*y') # optional - maple sage: m.sage() # optional - maple x^2 + 5*y sage: m._sage_() # optional - maple x^2 + 5*y :: sage: m = maple('sin(sqrt(1-x^2)) * (1 - cos(1/x))^2') # optional - maple sage: m.sage() # optional - maple (cos(1/x) - 1)^2*sin(sqrt(-x^2 + 1)) Some matrices can be converted back:: sage: m = matrix(2, 2, [1, 2, x, 3]) # optional - maple sage: mm = maple(m) # optional - maple sage: mm.sage() == m # optional - maple True Some vectors can be converted back:: sage: m = vector([1, x, 2, 3]) # optional - maple sage: mm = maple(m) # optional - maple sage: mm.sage() == m # optional - maple True """ from sage.matrix.constructor import matrix from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ result = repr(self) # The next few lines are a very crude excuse for a maple "parser". result = result.replace("Pi", "pi") if result[:6] == "Matrix": content = result[7:-1] m, n = content.split(',')[:2] m = ZZ(m.strip()) n = ZZ(n.strip()) coeffs = [self[i + 1, j + 1].sage() for i in range(m) for j in range(n)] return matrix(m, n, coeffs) elif result[:6] == "Vector": start = result.index('(') content = result[start + 1:-1] m = ZZ(content.split(',')[0].strip()) return vector([self[i + 1].sage() for i in range(m)]) try: from sage.symbolic.all import SR return SR(result) except Exception: raise NotImplementedError("Unable to parse Maple output: %s" % result)
is_ComplexNumber, ComplexField from sage.misc.latex import latex import math import sage.structure.element coercion_model = sage.structure.element.get_coercion_model() from sage.structure.coerce import parent from sage.symbolic.constants import pi from sage.symbolic.function import is_inexact from sage.functions.log import exp from sage.functions.trig import arctan2 from sage.functions.transcendental import Ei from sage.libs.mpmath import utils as mpmath_utils one_half = ~SR(2) class Function_erf(BuiltinFunction): _eval_ = BuiltinFunction._eval_default def __init__(self): r""" The error function, defined for real values as `\text{erf}(x) = \frac{2}{\sqrt{\pi}} \int_0^x e^{-t^2} dt`. This function is also defined for complex values, via analytic continuation. Sage implements the error function via the ``erfc()`` function in PARI. EXAMPLES:
def _sage_(self): r""" Convert a maple expression back to a Sage expression. This currently does not implement a parser for the Maple output language, therefore only very simple expressions will convert successfully. REFERENCE: https://www.asc.tuwien.ac.at/compmath/download/Monagan_Maple_Programming.pdf EXAMPLES:: sage: m = maple('x^2 + 5*y') # optional - maple sage: m.sage() # optional - maple x^2 + 5*y sage: m._sage_() # optional - maple x^2 + 5*y sage: m = maple('sin(sqrt(1-x^2)) * (1 - cos(1/x))^2') # optional - maple sage: m.sage() # optional - maple (cos(1/x) - 1)^2*sin(sqrt(-x^2 + 1)) Some matrices can be converted back:: sage: m = matrix(2, 2, [1, 2, x, 3]) # optional - maple sage: mm = maple(m) # optional - maple sage: mm.sage() == m # optional - maple True Some vectors can be converted back:: sage: m = vector([1, x, 2, 3]) # optional - maple sage: mm = maple(m) # optional - maple sage: mm.sage() == m # optional - maple True Integers and rationals are converted as such:: sage: maple(33).sage().parent() # optional - maple Integer Ring sage: maple(191/5).sage().parent() # optional - maple Rational Field Sets, lists, sequences:: sage: maple("[4,5,6]").sage() # optional - maple [4, 5, 6] sage: maple({14,33,6}).sage() # optional - maple {6, 14, 33} sage: maple("seq(i**2,i=1..5)").sage() # optional - maple (1, 4, 9, 16, 25) Strings:: sage: maple('"banane"').sage() # optional - maple '"banane"' Floats:: sage: Z3 = maple('evalf(Zeta(3))') # optional - maple sage: Z3.sage().parent() # optional - maple Real Field with 53 bits of precision sage: sq5 = maple('evalf(sqrt(5),100)') # optional - maple sage: sq5 = sq5.sage(); sq5 # optional - maple 2.23606797749978969640... sage: sq5.parent() # optional - maple Real Field with 332 bits of precision Functions are not yet converted back correctly:: sage: maple(hypergeometric([3,4],[5],x)) # optional - maple hypergeom([3, 4],[5],x) sage: _.sage() # known bug # optional - maple hypergeometric((3, 4), (5,), x) """ from sage.matrix.constructor import matrix from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ # The next few lines are a very crude excuse for a maple "parser" maple_type = repr(self.whattype()) result = repr(self) result = result.replace("Pi", "pi") if maple_type == 'symbol': # pi pass # left to symbolic ring elif maple_type == 'string': # "banane" return result elif maple_type == 'exprseq': # 2, 2 n = self.parent()(f"[{self._name}]").nops()._sage_() return tuple(self[i] for i in range(1, n + 1)) elif maple_type == 'set': # {1, 2} n = self.nops()._sage_() return set(self.op(i)._sage_() for i in range(1, n + 1)) elif maple_type == 'list': # [1, 2] n = self.nops()._sage_() return [self.op(i)._sage_() for i in range(1, n + 1)] elif maple_type == "Matrix": # Matrix(2, 2, [[1,2],[3,4]]) mn = self.op(1) m = mn[1]._sage_() n = mn[2]._sage_() coeffs = [ self[i + 1, j + 1]._sage_() for i in range(m) for j in range(n) ] return matrix(m, n, coeffs) elif maple_type[:6] == "Vector": # Vector[row](3, [4,5,6]) n = self.op(1)._sage_() return vector([self[i + 1]._sage_() for i in range(n)]) elif maple_type == 'integer': return ZZ(result) elif maple_type == 'fraction': return self.op(1)._sage_() / self.op(2)._sage_() elif maple_type == "function": pass # TODO : here one should translate back function names elif maple_type == "float": from sage.rings.real_mpfr import RealField mantissa = len(repr(self.op(1))) prec = max(53, (mantissa * 13301) // 4004) R = RealField(prec) return R(result) elif maple_type == '`=`': # (1, 1) = 2 return (self.op(1)._sage_() == self.op(2)._sage()) try: from sage.symbolic.all import SR return SR(result) except Exception: raise NotImplementedError("Unable to parse Maple output: %s" % result)
def local_basis(dop, point, order=None): r""" Generalized series expansions the local basis. INPUT: * dop - Differential operator * point - Point where the local basis is to be computed * order (optional) - Number of terms to compute, **starting from each “leftmost” valuation of a group of solutions with valuations differing by integers**. (Thus, the absolute truncation order will be the same for all solutions in such a group, with some solutions having more actual coefficients computed that others.) The default is to choose the truncation order in such a way that the structure of the basis is apparent, and in particular that logarithmic terms appear if logarithms are involved at all in that basis. The corresponding order may be very large in some cases. EXAMPLES:: sage: from ore_algebra import * sage: from ore_algebra.analytic.local_solutions import local_basis sage: Dops, x, Dx = DifferentialOperators(QQ, 'x') sage: local_basis(Dx - 1, 0) [1 + x + 1/2*x^2 + 1/6*x^3] sage: from ore_algebra.analytic.examples import ssw sage: local_basis(ssw.dop3, 0) [t^(-4) + 24*log(t)/t^2 - 48*log(t) - 96*t^2*log(t) - 88*t^2, t^(-2), 1 + 2*t^2] sage: dop = (x^2*(x^2-34*x+1)*Dx^3 + 3*x*(2*x^2-51*x+1)*Dx^2 ....: + (7*x^2-112*x+1)*Dx + (x-5)) sage: local_basis(dop, 0, 3) [1/2*log(x)^2 + 5/2*x*log(x)^2 + 12*x*log(x) + 73/2*x^2*log(x)^2 + 210*x^2*log(x) + 72*x^2, log(x) + 5*x*log(x) + 12*x + 73*x^2*log(x) + 210*x^2, 1 + 5*x + 73*x^2] sage: roots = dop.leading_coefficient().roots(AA) sage: local_basis(dop, roots[1][0], 3) [1 - (-239/12*a+169/6)*(x + 12*sqrt(2) - 17)^2, sqrt(x + 12*sqrt(2) - 17) - (-203/32*a+9)*(x + 12*sqrt(2) - 17)^(3/2) + (-24031/160*a+1087523/5120)*(x + 12*sqrt(2) - 17)^(5/2), x + 12*sqrt(2) - 17 - (-55/6*a+13)*(x + 12*sqrt(2) - 17)^2] TESTS:: sage: local_basis(4*x^2*Dx^2 + (-x^2+8*x-11), 0, 2) [x^(-sqrt(3) + 1/2) + (-4/11*a+2/11)*x^(-sqrt(3) + 3/2), x^(sqrt(3) + 1/2) - (-4/11*a-2/11)*x^(sqrt(3) + 3/2)] sage: local_basis((27*x^2+4*x)*Dx^2 + (54*x+6)*Dx + 6, 0, 2) [1/sqrt(x) + 3/8*sqrt(x), 1 - x] """ from .path import Point point = Point(point, dop) ldop = point.local_diffop() if order is None: ind = ldop.indicial_polynomial(ldop.base_ring().gen()) order = max(dop.order(), ind.dispersion()) + 3 sols = map_local_basis(ldop, lambda ini, bwrec: log_series(ini, bwrec, order), lambda leftmost, shift: {}) dx = SR(dop.base_ring().gen()) - point.value # Working with symbolic expressions here is too complicated: let's try # returning FormalSums. def log_monomial(expo, n, k): expo = simplify_exponent(expo) return dx**(expo + n) * symbolic_log(dx, hold=True)**k cm = get_coercion_model() Coeffs = cm.common_parent(dop.base_ring().base_ring(), point.value.parent(), *(sol.leftmost for sol in sols)) res = [ FormalSum([(c / ZZ(k).factorial(), log_monomial(sol.leftmost, n, k)) for n, vec in enumerate(sol.value) for k, c in reversed(list(enumerate(vec)))], FormalSums(Coeffs)) for sol in sols ] return res