def approximate(self, x, parent=None): r""" Approximate using de Bruijn's formula .. math:: \rho(x) \sim \frac{exp(-x \xi + Ei(\xi))}{\sqrt{2\pi x}\xi} which is asymptotically equal to Dickman's function, and is much faster to compute. REFERENCES: - N. De Bruijn, "The Asymptotic behavior of a function occurring in the theory of primes." J. Indian Math Soc. v 15. (1951) EXAMPLES:: sage: dickman_rho.approximate(10) 2.41739196365564e-11 sage: dickman_rho(10) 2.77017183772596e-11 sage: dickman_rho.approximate(1000) 4.32938809066403e-3464 """ log, exp, sqrt, pi = math.log, math.exp, math.sqrt, math.pi x = float(x) xi = log(x) y = (exp(xi) - 1.0) / xi - x while abs(y) > 1e-12: dydxi = (exp(xi) * (xi - 1.0) + 1.0) / (xi * xi) xi -= y / dydxi y = (exp(xi) - 1.0) / xi - x return (-x * xi + RR(xi).eint()).exp() / (sqrt(2 * pi * x) * xi)
def perpendicular_bisector(self): #UHP r""" Return the perpendicular bisector of the hyperbolic geodesic ``self`` if that geodesic has finite length. EXAMPLES:: sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.random_geodesic() sage: h = g.perpendicular_bisector() sage: c = lambda x: x.coordinates() sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) True Infinite geodesics cannot be bisected:: sage: UHP.get_geodesic(0, 1).perpendicular_bisector() Traceback (most recent call last): ... ValueError: the length must be finite """ if self.length() == infinity: raise ValueError("the length must be finite") start = self._start.coordinates() d = self._model._dist_points(start, self._end.coordinates()) / 2 S = self.complete()._to_std_geod(start) T1 = matrix([[exp(d/2), 0], [0, exp(-d/2)]]) s2 = sqrt(2) * 0.5 T2 = matrix([[s2, -s2], [s2, s2]]) isom_mtrx = S.inverse() * (T1 * T2) * S # We need to clean this matrix up. if (isom_mtrx - isom_mtrx.conjugate()).norm() < 5*EPSILON: # Imaginary part is small. isom_mtrx = (isom_mtrx + isom_mtrx.conjugate()) / 2 # Set it to its real part. H = self._model.get_isometry(isom_mtrx) return self._model.get_geodesic(H(self._start), H(self._end))
def circle_image(A, B): G = Graphics() G += circle((0, 0), 1, color='grey') from collections import defaultdict tmp = defaultdict(int) for a in A: for j in range(a): if gcd(j, a) == 1: rational = Rational(j) / Rational(a) tmp[(rational.numerator(), rational.denominator())] += 1 for b in B: for j in range(b): if gcd(j, b) == 1: rational = Rational(j) / Rational(b) tmp[(rational.numerator(), rational.denominator())] -= 1 C = ComplexField() for val in tmp: if tmp[val] > 0: G += text(str(tmp[val]), exp(C(-.2 + 2 * 3.14159 * I * val[0] / val[1])), fontsize=30, axes=False, color="green") if tmp[val] < 0: G += text(str(abs(tmp[val])), exp(C(.2 + 2 * 3.14159 * I * val[0] / val[1])), fontsize=30, axes=False, color="blue") return G
def approximate(self, x, parent=None): r""" Approximate using de Bruijn's formula .. math:: \rho(x) \sim \frac{exp(-x \xi + Ei(\xi))}{\sqrt{2\pi x}\xi} which is asymptotically equal to Dickman's function, and is much faster to compute. REFERENCES: - N. De Bruijn, "The Asymptotic behavior of a function occurring in the theory of primes." J. Indian Math Soc. v 15. (1951) EXAMPLES:: sage: dickman_rho.approximate(10) 2.41739196365564e-11 sage: dickman_rho(10) 2.77017183772596e-11 sage: dickman_rho.approximate(1000) 4.32938809066403e-3464 """ log, exp, sqrt, pi = math.log, math.exp, math.sqrt, math.pi x = float(x) xi = log(x) y = (exp(xi)-1.0)/xi - x while abs(y) > 1e-12: dydxi = (exp(xi)*(xi-1.0) + 1.0)/(xi*xi) xi -= y/dydxi y = (exp(xi)-1.0)/xi - x return (-x*xi + RR(xi).eint()).exp() / (sqrt(2*pi*x)*xi)
def error_function(model, N, x0): """ Compute the error function of a truncated ODE. INPUT: - ``model`` -- Polynomial ODE or string containing the model in text format - ``N`` -- integer; truncation order - ``x0`` -- list; initial point OUTPUT: - ``Ts`` -- convergence time computed from the reduced quadratic system - ``error`` -- function of `t`, the estimated truncation error in the supremum norm EXAMPLES:: sage: from carlin.transformation import error_function sage: from carlin.library import quadratic_scalar as P sage: Ts, error = error_function(P(0.5, 2), 2, [0, 0.5]) sage: Ts 0.8109... sage: error 0.5*(2.0*e^(0.5*t) - 2.0)^2*e^(0.5*t)/(-2.0*e^(0.5*t) + 3.0) """ from numpy.linalg import norm from sage.symbolic.ring import SR if isinstance(model, str): [F, n, k] = get_Fj_from_model(model) elif isinstance(model, PolynomialODE): [F, n, k] = get_Fj_from_model(model.funcs(), model.dim(), model.degree()) [Fquad, nquad, kquad] = quadratic_reduction(F, n, k) ch = characteristics(Fquad, nquad, kquad) norm_F1_tilde, norm_F2_tilde = ch['norm_Fi_inf'] x0_hat = [kron_power(x0, i + 1) for i in range(k - 1)] #transform to flat list x0_hat = [item for sublist in x0_hat for item in sublist] norm_x0_hat = norm(x0_hat, ord=inf) beta0 = ch['beta0_const'] * norm_x0_hat Ts = 1 / norm_F1_tilde * log(1 + 1 / beta0) t = SR.var('t') error = norm_x0_hat * exp( norm_F1_tilde * t) / (1 + beta0 - beta0 * exp(norm_F1_tilde * t)) * ( beta0 * (exp(norm_F1_tilde * t) - 1))**N return [Ts, error]
def _eval_(self, x, y): """ EXAMPLES:: sage: gamma_inc(2.,0) 1.00000000000000 sage: gamma_inc(2,0) 1 sage: gamma_inc(1/2,2) -(erf(sqrt(2)) - 1)*sqrt(pi) sage: gamma_inc(1/2,1) -(erf(1) - 1)*sqrt(pi) sage: gamma_inc(1/2,0) sqrt(pi) sage: gamma_inc(x,0) gamma(x) sage: gamma_inc(1,2) e^(-2) sage: gamma_inc(0,2) -Ei(-2) """ if not isinstance(x, Expression) and not isinstance(y, Expression) and \ (is_inexact(x) or is_inexact(y)): x, y = coercion_model.canonical_coercion(x, y) return self._evalf_(x, y, parent(x)) if y == 0: return gamma(x) if x == 1: return exp(-y) if x == 0: return -Ei(-y) if x == Rational(1) / 2: #only for x>0 return sqrt(pi) * (1 - erf(sqrt(y))) return None
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)
def _eval_(self, x, y): """ EXAMPLES:: sage: gamma_inc(2.,0) 1.00000000000000 sage: gamma_inc(2,0) 1 sage: gamma_inc(1/2,2) -(erf(sqrt(2)) - 1)*sqrt(pi) sage: gamma_inc(1/2,1) -(erf(1) - 1)*sqrt(pi) sage: gamma_inc(1/2,0) sqrt(pi) sage: gamma_inc(x,0) gamma(x) sage: gamma_inc(1,2) e^(-2) sage: gamma_inc(0,2) -Ei(-2) """ if not isinstance(x, Expression) and not isinstance(y, Expression) and \ (is_inexact(x) or is_inexact(y)): x, y = coercion_model.canonical_coercion(x, y) return self._evalf_(x, y, parent(x)) if y == 0: return gamma(x) if x == 1: return exp(-y) if x == 0: return -Ei(-y) if x == Rational(1)/2: #only for x>0 return sqrt(pi)*(1-erf(sqrt(y))) return None
def xseries(self, all_conjugates=True): r"""Returns the corresponding x-series. Parameters ---------- all_conjugates : bool (default: True) If ``True``, returns all conjugates x-representations of this Puiseux t-series. If ``False``, only returns one representative. Returns ------- list List of PuiseuxXSeries representations of this PuiseuxTSeries. """ # obtain relevant rings: # o R = parent ring of curve # o L = parent ring of T-series # o S = temporary polynomial ring over base ring of T-series # o P = Puiseux series ring L = self.ypart.parent() t = L.gen() S = L.base_ring()['z'] z = S.gen() R = self.f.parent() x, y = R.gens() P = PuiseuxSeriesRing(L.base_ring(), str(x)) x = P.gen() # given x = alpha + lambda*t^e solve for t. this involves finding an # e-th root of either (1/lambda) or of lambda, depending on e's sign e = self.ramification_index lamb = S(self.xcoefficient) order = self.order if e > 0: phi = lamb * z**e - 1 else: phi = z**abs(e) - lamb mu = phi.roots(QQbar, multiplicities=False)[0] if all_conjugates: conjugates = [ mu * exp(2 * pi * I * k / abs(e)) for k in range(abs(e)) ] else: conjugates = [mu] map(lambda x: x.exactify(), conjugates) # determine the resulting x-series xseries = [] for c in conjugates: t = self.ypart.parent().gen() fconj = self.ypart(c * t) p = P(fconj(x**(QQ(1) / e))) p = p.add_bigoh(QQ(order + 1) / abs(e)) xseries.append(p) return xseries
def _eval_(self, n, z): """ EXAMPLES:: sage: exp_integral_e(1.0, x) exp_integral_e(1.00000000000000, x) sage: exp_integral_e(x, 1.0) exp_integral_e(x, 1.00000000000000) sage: exp_integral_e(1.0, 1.0) 0.219383934395520 """ if not isinstance(n, Expression) and not isinstance(z, Expression) and \ (is_inexact(n) or is_inexact(z)): coercion_model = sage.structure.element.get_coercion_model() n, z = coercion_model.canonical_coercion(n, z) return self._evalf_(n, z, parent(n)) z_zero = False # special case: z == 0 and n > 1 if isinstance(z, Expression): if z.is_trivial_zero(): z_zero = True # for later if n > 1: return 1/(n-1) else: if not z: z_zero = True if n > 1: return 1/(n-1) # special case: n == 0 if isinstance(n, Expression): if n.is_trivial_zero(): if z_zero: return None else: return exp(-z)/z else: if not n: if z_zero: return None else: return exp(-z)/z return None # leaves the expression unevaluated
def _eval_(self, n, z): """ EXAMPLES:: sage: exp_integral_e(1.0, x) exp_integral_e(1.00000000000000, x) sage: exp_integral_e(x, 1.0) exp_integral_e(x, 1.00000000000000) sage: exp_integral_e(3, 0) 1/2 TESTS: Check that Python ints work (:trac:`14766`):: sage: exp_integral_e(int(3), 0) 1/2 """ z_zero = False # special case: z == 0 and n > 1 if isinstance(z, Expression): if z.is_trivial_zero(): z_zero = True # for later if n > 1: return 1 / (n - 1) else: if not z: z_zero = True if n > 1: return 1 / (n - 1) # special case: n == 0 if isinstance(n, Expression): if n.is_trivial_zero(): if z_zero: return None else: return exp(-z) / z else: if not n: if z_zero: return None else: return exp(-z) / z return None # leaves the expression unevaluated
def _eval_(self, n, z): """ EXAMPLES:: sage: exp_integral_e(1.0, x) exp_integral_e(1.00000000000000, x) sage: exp_integral_e(x, 1.0) exp_integral_e(x, 1.00000000000000) sage: exp_integral_e(3, 0) 1/2 TESTS: Check that Python ints work (:trac:`14766`):: sage: exp_integral_e(int(3), 0) 1/2 """ z_zero = False # special case: z == 0 and n > 1 if isinstance(z, Expression): if z.is_trivial_zero(): z_zero = True # for later if n > 1: return 1/(n-1) else: if not z: z_zero = True if n > 1: return 1/(n-1) # special case: n == 0 if isinstance(n, Expression): if n.is_trivial_zero(): if z_zero: return None else: return exp(-z)/z else: if not n: if z_zero: return None else: return exp(-z)/z return None # leaves the expression unevaluated
def _eval_(self, n, z): """ EXAMPLES:: sage: exp_integral_e(1.0, x) exp_integral_e(1.00000000000000, x) sage: exp_integral_e(x, 1.0) exp_integral_e(x, 1.00000000000000) sage: exp_integral_e(1.0, 1.0) 0.219383934395520 """ if not isinstance(n, Expression) and not isinstance(z, Expression) and \ (is_inexact(n) or is_inexact(z)): coercion_model = sage.structure.element.get_coercion_model() n, z = coercion_model.canonical_coercion(n, z) return self._evalf_(n, z, parent(n)) z_zero = False # special case: z == 0 and n > 1 if isinstance(z, Expression): if z.is_trivial_zero(): z_zero = True # for later if n > 1: return 1 / (n - 1) else: if not z: z_zero = True if n > 1: return 1 / (n - 1) # special case: n == 0 if isinstance(n, Expression): if n.is_trivial_zero(): if z_zero: return None else: return exp(-z) / z else: if not n: if z_zero: return None else: return exp(-z) / z return None # leaves the expression unevaluated
def xseries(self, all_conjugates=True): r"""Returns the corresponding x-series. Parameters ---------- all_conjugates : bool (default: True) If ``True``, returns all conjugates x-representations of this Puiseux t-series. If ``False``, only returns one representative. Returns ------- list List of PuiseuxXSeries representations of this PuiseuxTSeries. """ # obtain relevant rings: # o R = parent ring of curve # o L = parent ring of T-series # o S = temporary polynomial ring over base ring of T-series # o P = Puiseux series ring L = self.ypart.parent() t = L.gen() S = L.base_ring()['z'] z = S.gen() R = self.f.parent() x,y = R.gens() P = PuiseuxSeriesRing(L.base_ring(), str(x)) x = P.gen() # given x = alpha + lambda*t^e solve for t. this involves finding an # e-th root of either (1/lambda) or of lambda, depending on e's sign e = self.ramification_index lamb = S(self.xcoefficient) order = self.order if e > 0: phi = lamb*z**e - 1 else: phi = z**abs(e) - lamb mu = phi.roots(QQbar, multiplicities=False)[0] if all_conjugates: conjugates = [mu*exp(2*pi*I*k/abs(e)) for k in range(abs(e))] else: conjugates = [mu] map(lambda x: x.exactify(), conjugates) # determine the resulting x-series xseries = [] for c in conjugates: t = self.ypart.parent().gen() fconj = self.ypart(c*t) p = P(fconj(x**(QQ(1)/e))) p = p.add_bigoh(QQ(order+1)/abs(e)) xseries.append(p) return xseries
def get_bound_poly(F, prec=53, norm_type='norm', emb=None): """ The hyperbolic distance from `j` which must contain the smallest poly. This defines the maximum possible distance from `j` to the `z_0` covariant in the hyperbolic 3-space for which the associated `F` could have smaller coefficients. INPUT: - ``F`` -- binary form of degree at least 3 with no multiple roots - ``prec``-- positive integer. precision to use in CC - ``norm_type`` -- string, either norm or height - ``emb`` -- embedding into CC OUTPUT: a positive real number EXAMPLES:: sage: from sage.rings.polynomial.binary_form_reduce import get_bound_poly sage: R.<x,y> = QQ[] sage: F = -2*x^3 + 2*x^2*y + 3*x*y^2 + 127*y^3 sage: get_bound_poly(F) # tol 1e-12 28.0049336543295 sage: get_bound_poly(F, norm_type='height') # tol 1e-11 111.890642019092 """ def coshdelta(z): #The cosh of the hyperbolic distance from z = t+uj to j return (z.norm() + 1) / (2 * z.imag()) if F.base_ring() != ComplexField(prec=prec): if emb is None: compF = F.change_ring(ComplexField(prec=prec)) else: compF = F.change_ring(emb) else: compF = F n = F.degree() assert (n > 2), "degree 2 polynomial" z0F, thetaF = covariant_z0(compF, prec=prec, emb=emb) if norm_type == 'norm': #euclidean norm squared normF = (sum([abs(i)**2 for i in compF.coefficients()])) target = (2**(n - 1)) * normF / thetaF elif norm_type == 'height': hF = exp(max([c.global_height(prec=prec) for c in F.coefficients()])) # height target = (2**(n - 1)) * (n + 1) * (hF**2) / thetaF else: raise ValueError('type must be norm or height') return cosh(epsinv(F, target, prec=prec))
def random_polygon_2d(num_vertices, **kwargs): r"""Generate a random polygon (2d) obtained by uniform sampling over the unit circle. INPUT: * ``num_vertices`` - the number of vertices of the generated polyhedron. * ``base_ring`` - (default: ``QQ``). The ring passed to the constructor of Polyhedron. alid options are ``QQ`` and ``RDF``. * ``scale`` - (default: 1). The scale factor; each vertex is chosen randomly from the unit circle, and then multiplied by scale. OUTPUT: A random polygon (object of type Polyhedron), whose vertices belong to a circle of radius ``scale``. NOTES: - If ``RDF`` is chosen as ``base_ring``, sometimes there are exceptions related to numerical errors, and show up as ``'FrozenSet'`` exceptions. This occurs particularly frequently for a large number of vertices (more than 30). """ from sage.functions.log import exp from sage.symbolic.constants import pi from sage.symbolic.all import I base_ring = kwargs['base_ring'] if 'base_ring' in kwargs else QQ scale = kwargs['scale'] if 'scale' in kwargs else 1 angles = [ random.uniform(0, 2 * pi.n(digits=5)) for i in range(num_vertices) ] vert = [[ scale * exp(I * angles[i]).real(), scale * exp(I * angles[i]).imag() ] for i in range(num_vertices)] return Polyhedron(vertices=vert, base_ring=base_ring)
def eval(self, tau, prec=10): """ Evaluates the QExpansion with prec many terms at tau. Prec can be set to 'max', to use all available Fourier coefficients. """ if prec == 'max': prec = self.prec pi = ComplexField(53).pi() I = ComplexField(53).gen() return sum([ self[n] * ComplexField(53)(exp(2 * pi * I * tau * n / self.param_level)) for n in range(prec) ])
def _eval_(self, n, x): """ EXAMPLES:: sage: bessel_K(1,0) bessel_K(1, 0) sage: bessel_K(1.0, 0.0) +infinity sage: bessel_K(-1, 1).n(128) 0.60190723019723457473754000153561733926 """ # special identity if n == Integer(1) / Integer(2) and x > 0: return sqrt(pi / 2) * exp(-x) * x**(-Integer(1) / Integer(2))
def _eval_(self, n, x): """ EXAMPLES:: sage: bessel_K(1,0) bessel_K(1, 0) sage: bessel_K(1.0, 0.0) +infinity sage: bessel_K(-1, 1).n(128) 0.60190723019723457473754000153561733926 """ # special identity if n == Integer(1) / Integer(2) and x > 0: return sqrt(pi / 2) * exp(-x) * x ** (-Integer(1) / Integer(2))
def circle_image(A,B): G = Graphics() G += circle((0,0), 1 , color = 'grey') from collections import defaultdict tmp = defaultdict(int) for a in A: for j in range(a): if gcd(j,a) == 1: rational = Rational(j)/Rational(a) tmp[(rational.numerator(),rational.denominator())] += 1 for b in B: for j in range(b): if gcd(j,b) == 1: rational = Rational(j)/Rational(b) tmp[(rational.numerator(),rational.denominator())] -= 1 C = ComplexField() for val in tmp: if tmp[val] > 0: G += text(str(tmp[val]),exp(C(2*3.14159*I*val[0]/val[1])), fontsize = 30, axes = False, color = "green") if tmp[val] < 0: G += text(str(abs(tmp[val])),exp(C(2*3.14159*I*val[0]/val[1])), fontsize = 30, axes = False, color = "red") return G
def perpendicular_bisector(self): # UHP r""" Return the perpendicular bisector of the hyperbolic geodesic ``self`` if that geodesic has finite length. EXAMPLES:: sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.random_geodesic() sage: h = g.perpendicular_bisector() sage: c = lambda x: x.coordinates() sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) True Infinite geodesics cannot be bisected:: sage: UHP.get_geodesic(0, 1).perpendicular_bisector() Traceback (most recent call last): ... ValueError: the length must be finite """ if self.length() == infinity: raise ValueError("the length must be finite") start = self._start.coordinates() d = self._model._dist_points(start, self._end.coordinates()) / 2 S = self.complete()._to_std_geod(start) T1 = matrix([[exp(d / 2), 0], [0, exp(-d / 2)]]) s2 = sqrt(2) * 0.5 T2 = matrix([[s2, -s2], [s2, s2]]) isom_mtrx = S.inverse() * (T1 * T2) * S # We need to clean this matrix up. if (isom_mtrx - isom_mtrx.conjugate()).norm() < 5 * EPSILON: # Imaginary part is small. isom_mtrx = (isom_mtrx + isom_mtrx.conjugate()) / 2 # Set it to its real part. H = self._model.get_isometry(isom_mtrx) return self._model.get_geodesic(H(self._start), H(self._end))
def _derivative_(self, x, diff_param=None): """ EXAMPLES:: sage: Ei(x).diff(x) e^x/x sage: Ei(x).diff(x).subs(x=1) e sage: Ei(x^2).diff(x) 2*e^(x^2)/x sage: f = function('f') sage: Ei(f(x)).diff(x) e^f(x)*D[0](f)(x)/f(x) """ return exp(x) / x
def _derivative_(self, x, diff_param=None): """ EXAMPLES:: sage: Ei(x).diff(x) e^x/x sage: Ei(x).diff(x).subs(x=1) e sage: Ei(x^2).diff(x) 2*e^(x^2)/x sage: f = function('f') sage: Ei(f(x)).diff(x) e^f(x)*D[0](f)(x)/f(x) """ return exp(x)/x
def circle_image(A, B): G = Graphics() G += circle((0, 0), 1, color='black', thickness=3) G += circle( (0, 0), 1.4, color='black', alpha=0 ) # This adds an invisible framing circle to the plot, which protects the aspect ratio from being skewed. from collections import defaultdict tmp = defaultdict(int) for a in A: for j in range(a): if gcd(j, a) == 1: rational = Rational(j) / Rational(a) tmp[(rational.numerator(), rational.denominator())] += 1 for b in B: for j in range(b): if gcd(j, b) == 1: rational = Rational(j) / Rational(b) tmp[(rational.numerator(), rational.denominator())] -= 1 C = ComplexField() color1 = (41 / 255, 95 / 255, 45 / 255) color2 = (0 / 255, 0 / 255, 150 / 255) for val in tmp: if tmp[val] > 0: G += text(str(tmp[val]), exp(C(-.2 + 2 * 3.14159 * I * val[0] / val[1])), fontsize=30, axes=False, color=color1) if tmp[val] < 0: G += text(str(abs(tmp[val])), exp(C(.2 + 2 * 3.14159 * I * val[0] / val[1])), fontsize=30, axes=False, color=color2) return G
def _derivative_(self, z, diff_param=None): """ The derivative of `E_1(z)` is `-e^{-z}/z`. See [AS], 5.1.26. EXAMPLES:: sage: x = var('x') sage: f = exp_integral_e1(x) sage: f.diff(x) -e^(-x)/x sage: f = exp_integral_e1(x^2) sage: f.diff(x) -2*e^(-x^2)/x """ return -exp(-z)/z
def _derivative_(self, z, diff_param=None): """ The derivative of `E_1(z)` is `-e^{-z}/z`. See [AS], 5.1.26. EXAMPLES:: sage: x = var('x') sage: f = exp_integral_e1(x) sage: f.diff(x) -e^(-x)/x sage: f = exp_integral_e1(x^2) sage: f.diff(x) -2*e^(-x^2)/x """ return -exp(-z) / z
def __getattr__(self, name): """ EXAMPLE:: sage: from sage.crypto.lwe import DiscreteGaussianSamplerRejection sage: DiscreteGaussianSamplerRejection(3.0).foo Traceback (most recent call last): ... AttributeError: 'DiscreteGaussianSamplerRejection' object has no attribute 'foo' """ if name == "rho": # we delay the creation of rho until we actually need it R = RealField(self.precision) self.rho = [round(self.max_precs * exp((-(R(x) / R(self.stddev))**2)/R(2))) for x in range(0,self.upper_bound)] self.rho[0] = self.rho[0] / 2 return self.rho else: raise AttributeError("'%s' object has no attribute '%s'"%(self.__class__.__name__, name))
def _derivative_(self, x, diff_param=None): """ Derivative of erf function EXAMPLES:: sage: erf(x).diff(x) 2*e^(-x^2)/sqrt(pi) TESTS:: Check if #8568 is fixed:: sage: var('c,x') (c, x) sage: derivative(erf(c*x),x) 2*c*e^(-c^2*x^2)/sqrt(pi) sage: erf(c*x).diff(x)._maxima_init_() '((%pi)^(-1/2))*(c)*(exp(((c)^(2))*((x)^(2))*(-1)))*(2)' """ return 2*exp(-x**2)/sqrt(pi)
def _derivative_(self, x, diff_param=None): """ Derivative of erf function EXAMPLES:: sage: erf(x).diff(x) 2*e^(-x^2)/sqrt(pi) TESTS:: Check if #8568 is fixed:: sage: var('c,x') (c, x) sage: derivative(erf(c*x),x) 2*c*e^(-c^2*x^2)/sqrt(pi) sage: erf(c*x).diff(x)._maxima_init_() '((%pi)^(-1/2))*(c)*(exp(((c)^(2))*((x)^(2))*(-1)))*(2)' """ return 2 * exp(-x**2) / sqrt(pi)
def __getattr__(self, name): """ EXAMPLE:: sage: DiscreteGaussianSamplerRejection(3.0).foo Traceback (most recent call last): ... AttributeError: 'DiscreteGaussianSamplerRejection' object has no attribute 'foo' """ if name == "rho": # we delay the creation of rho until we actually need it R = RealField(self.precision) self.rho = [ round(self.max_precs * exp( (-(R(x) / R(self.stddev))**2) / R(2))) for x in range(0, self.upper_bound) ] self.rho[0] = self.rho[0] / 2 return self.rho else: raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))
def _eval_(self, n, x): """ EXAMPLES:: sage: bessel_K(1,0) bessel_K(1, 0) sage: bessel_K(1.0, 0.0) +infinity sage: bessel_K(-1, 1).n(128) 0.60190723019723457473754000153561733926 """ if (not isinstance(n, Expression) and not isinstance(x, Expression) and (is_inexact(n) or is_inexact(x))): coercion_model = get_coercion_model() n, x = coercion_model.canonical_coercion(n, x) return self._evalf_(n, x, parent(n)) # special identity if n == Integer(1) / Integer(2) and x > 0: return sqrt(pi / 2) * exp(-x) * x**(-Integer(1) / Integer(2)) return None # leaves the expression unevaluated
def _eval_(self, n, x): """ EXAMPLES:: sage: bessel_K(1,0) bessel_K(1, 0) sage: bessel_K(1.0, 0.0) +infinity sage: bessel_K(-1, 1).n(128) 0.60190723019723457473754000153561733926 """ if (not isinstance(n, Expression) and not isinstance(x, Expression) and (is_inexact(n) or is_inexact(x))): coercion_model = get_coercion_model() n, x = coercion_model.canonical_coercion(n, x) return self._evalf_(n, x, parent(n)) # special identity if n == Integer(1) / Integer(2) and x > 0: return sqrt(pi / 2) * exp(-x) * x ** (-Integer(1) / Integer(2)) return None # leaves the expression unevaluated
def midpoint(self): # UHP r""" Return the (hyperbolic) midpoint of ``self`` if it exists. EXAMPLES:: sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.random_geodesic() sage: m = g.midpoint() sage: d1 = UHP.dist(m, g.start()) sage: d2 = UHP.dist(m, g.end()) sage: bool(abs(d1 - d2) < 10**-9) True Infinite geodesics have no midpoint:: sage: UHP.get_geodesic(0, 2).midpoint() Traceback (most recent call last): ... ValueError: the length must be finite """ if self.length() == infinity: raise ValueError("the length must be finite") start = self._start.coordinates() end = self._end.coordinates() d = self._model._dist_points(start, end) / 2 S = self.complete()._to_std_geod(start) T = matrix([[exp(d), 0], [0, 1]]) M = S.inverse() * T * S if ((real(start - end) < EPSILON) or (abs(real(start - end)) < EPSILON and imag(start - end) < EPSILON)): end_p = start else: end_p = end return self._model.get_point(mobius_transform(M, end_p))
def Stirling(var, precision=None, skip_constant_factor=False): r""" Return Stirling's approximation formula for factorials. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- (default: ``None``) an integer `\ge 3`. If ``None``, then the default precision of the asymptotic ring is used. - ``skip_constant_factor`` -- (default: ``False``) a boolean. If set, then the constant factor `\sqrt{2\pi}` is left out. As a consequence, the coefficient ring of the output changes from ``Symbolic Constants Subring`` (if ``False``) to ``Rational Field`` (if ``True``). OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.Stirling('n', precision=5) sqrt(2)*sqrt(pi)*e^(n*log(n))*(e^n)^(-1)*n^(1/2) + 1/12*sqrt(2)*sqrt(pi)*e^(n*log(n))*(e^n)^(-1)*n^(-1/2) + 1/288*sqrt(2)*sqrt(pi)*e^(n*log(n))*(e^n)^(-1)*n^(-3/2) + O(e^(n*log(n))*(e^n)^(-1)*n^(-5/2)) sage: _.parent() Asymptotic Ring <(e^(n*log(n)))^QQ * (e^n)^QQ * n^QQ * log(n)^QQ> over Symbolic Constants Subring .. SEEALSO:: :meth:`log_Stirling`, :meth:`~sage.rings.asymptotic.asymptotic_ring.AsymptoticExpansion.factorial`. TESTS:: sage: expansion = asymptotic_expansions.Stirling('n', precision=5) sage: n = expansion.parent().gen() sage: expansion.compare_with_values(n, lambda x: x.factorial(), [5, 10, 20]) # rel tol 1e-6 [(5, 0.00675841118?), (10, 0.0067589306?), (20, 0.006744925?)] sage: asymptotic_expansions.Stirling('n', precision=5, ....: skip_constant_factor=True) e^(n*log(n))*(e^n)^(-1)*n^(1/2) + 1/12*e^(n*log(n))*(e^n)^(-1)*n^(-1/2) + 1/288*e^(n*log(n))*(e^n)^(-1)*n^(-3/2) + O(e^(n*log(n))*(e^n)^(-1)*n^(-5/2)) sage: _.parent() Asymptotic Ring <(e^(n*log(n)))^QQ * (e^n)^QQ * n^QQ * log(n)^QQ> over Rational Field sage: asymptotic_expansions.Stirling('m', precision=4) sqrt(2)*sqrt(pi)*e^(m*log(m))*(e^m)^(-1)*m^(1/2) + O(e^(m*log(m))*(e^m)^(-1)*m^(-1/2)) sage: asymptotic_expansions.Stirling('m', precision=3) O(e^(m*log(m))*(e^m)^(-1)*m^(1/2)) sage: asymptotic_expansions.Stirling('m', precision=2) Traceback (most recent call last): ... ValueError: precision must be at least 3 """ if precision < 3: raise ValueError("precision must be at least 3") log_Stirling = AsymptoticExpansionGenerators.log_Stirling( var, precision=precision, skip_constant_summand=True) P = log_Stirling.parent().change_parameter( growth_group= '(e^({n}*log({n})))^QQ * (e^{n})^QQ * {n}^QQ * log({n})^QQ'.format( n=var)) from sage.functions.log import exp result = exp(P(log_Stirling)) if not skip_constant_factor: from sage.symbolic.ring import SR SCR = SR.subring(no_variables=True) result *= (2 * SCR('pi')).sqrt() return result
def createLists (f, pars): ''' Creates list1 and list 2 from the right side function f of an ODE: input: f -> right side function for ODE system pars -> list with the parameters on f output: list1 and list2 Example with Lorenz Equation sage: var('t, x, y, z') # variables for lorenz equations sage: var('s, r, b') # parameters for lorenz equations sage: f(t,x,y,z) = [s*(y-x), x*(r-z) - y, x*y - b*z] # Right side function for Lorenz equation ''' vars = f[0].arguments () # gets the list of variables from function f varpar = list (vars) + list (pars) # gets the list of vars and pars totegher _f = f (*vars).function (varpar) # _f is f but with vars and pars as arguments fastCallList = flatten ([fast_callable (i,vars=varpar).op_list () for i in f], max_level=1) # This list is the fast callable version of f using a stack-mode call ''' We create create the lists list1, list2 and stack. stack will be expresion stack-list ''' list1 = []; list2 = []; stack = []; ''' Starts parser on fastCallList. ''' for s in fastCallList: if s[0] == 'load_arg': # Loads a variable in stack. no changes on list1, or list2 stack.append (varpar[s[1]]) # appends the variable or parameter on symbolic stack elif s[0] == 'ipow': # Integer power. if s[1] in NN: # If natural, parser as products basis = stack[-1] for j in range (s[1]-1): a=stack.pop (-1) stack.append (a*basis) list1.append (stack[-1]) list2.append (('mul', a, basis)) elif -s[1] in NN: basis = stack[-1] for j in range (-s[1]-1): a=stack.pop (-1) stack.append (a*basis) list1.append (stack[-1]) list2.append (('mul', a, basis)) a = stack.pop (-1); stack.append (1/a); list1.append (stack[-1]) list2.append (('div', 1, a)) else: # Attach as normal power a = stack.pop (-1) #basis stack.append (a ** s[1]) list1.append (stack[-1]) list2.append (('pow', a, s[1])) elif s[0] == 'load_const': # Loads a constant value on stack. Not in list1 or list2 stack.append (s[1]) elif s == 'neg': # multiplies by -1.0 a = stack.pop (-1) # expresion to be multiplied by -1 stack.append (-a) list1.append (stack[-1]) list2.append (('mul', -1, a)) elif s == 'mul': # Product a=stack.pop (-1) b=stack.pop (-1) list2.append (('mul', a, b)) stack.append (a*b) list1.append (stack[-1]) elif s == 'div': # divission Numerator First. b=stack.pop (-1) # denominator (after a in stack) a=stack.pop (-1) # numerator (before b in stack) if expresionIsConstant (b, pars): list1.append(1/b) list2.append(('div', 1, b)) b = 1/b; stack.append (a*b) list1.append(stack[-1]) list2.append (('mul', a, b)) else: list2.append (('div', a, b)) stack.append (a/b) list1.append (stack[-1]) elif s == 'add': # addition b = stack.pop (-1) # second operand a = stack.pop (-1) # first operand stack.append (a+b) list1.append (stack[-1]) list2.append (('add', a, b)) elif s == 'pow': # any other pow b = stack.pop (-1) # exponent a = stack.pop (-1) # basis stack.append (a**b) list1.append (stack[-1]) list2.append (('pow', a, b)) elif s[0] == 'py_call' and 'sqrt' in str (s[1]): # square root. Compute as power a = stack.pop (-1) # argument of sqrt stack.append (sqrt (a)) list1.append (stack[-1]) list2.append (('pow', a, 0.5)) elif s[0] == 'py_call' and str (s[1]) == 'log': # logarithm a = stack.pop (-1); # argument of log stack.append (log (a)) list1.append (stack[-1]) list2.append (('log', a)) elif s[0] == 'py_call' and str (s[1]) == 'exp': a = stack.pop (-1); # argument of exp stack.append (exp (a)) list1.append (stack[-1]) list2.append (('exp', a)) elif s[0] == 'py_call' and str (s[1]) == 'sin': # sine. For AD needs computation of cos a = stack.pop (-1) stack.append (sin (a)) list1.append (sin (a)) list1.append (cos (a)) list2.append (('sin', a)) list2.append (('cos', a)) elif s[0] == 'py_call' and str (s[1]) == 'cos': # cosine. For AD needs computation of sin a = stack.pop (-1) stack.append (cos (a)) list1.append (sin (a)) list1.append (cos (a)) list2.append (('sin', a)) list2.append (('cos', a)) elif s[0] == 'py_call' and str (s[1]) == 'tan': a = stack.pop (-1) stack.append (tan (a)) list1.append (sin (a)) list1.append (cos (a)) list1.append (tan (a)) list2.append (('sin', a)) list2.append (('cos', a)) list2.append (('div', sin (a), cos (a))) return list1, list2
def createLists(f, pars): ''' Creates list1 and list 2 from the right side function f of an ODE: input: f -> right side function for ODE system pars -> list with the parameters on f output: list1 and list2 Example with Lorenz Equation sage: var('t, x, y, z') # variables for lorenz equations sage: var('s, r, b') # parameters for lorenz equations sage: f(t,x,y,z) = [s*(y-x), x*(r-z) - y, x*y - b*z] # Right side function for Lorenz equation ''' vars = f[0].arguments() # gets the list of variables from function f varpar = list(vars) + list(pars) # gets the list of vars and pars totegher _f = f(*vars).function( varpar) # _f is f but with vars and pars as arguments fastCallList = flatten( [fast_callable(i, vars=varpar).op_list() for i in f], max_level=1) # This list is the fast callable version of f using a stack-mode call ''' We create create the lists list1, list2 and stack. stack will be expresion stack-list ''' list1 = [] list2 = [] stack = [] ''' Starts parser on fastCallList. ''' for s in fastCallList: if s[0] == 'load_arg': # Loads a variable in stack. no changes on list1, or list2 stack.append(varpar[ s[1]]) # appends the variable or parameter on symbolic stack elif s[0] == 'ipow': # Integer power. if s[1] in NN: # If natural, parser as products basis = stack[-1] for j in range(s[1] - 1): a = stack.pop(-1) stack.append(a * basis) list1.append(stack[-1]) list2.append(('mul', a, basis)) elif -s[1] in NN: basis = stack[-1] for j in range(-s[1] - 1): a = stack.pop(-1) stack.append(a * basis) list1.append(stack[-1]) list2.append(('mul', a, basis)) a = stack.pop(-1) stack.append(1 / a) list1.append(stack[-1]) list2.append(('div', 1, a)) else: # Attach as normal power a = stack.pop(-1) #basis stack.append(a**s[1]) list1.append(stack[-1]) list2.append(('pow', a, s[1])) elif s[0] == 'load_const': # Loads a constant value on stack. Not in list1 or list2 stack.append(s[1]) elif s == 'neg': # multiplies by -1.0 a = stack.pop(-1) # expresion to be multiplied by -1 stack.append(-a) list1.append(stack[-1]) list2.append(('mul', -1, a)) elif s == 'mul': # Product a = stack.pop(-1) b = stack.pop(-1) list2.append(('mul', a, b)) stack.append(a * b) list1.append(stack[-1]) elif s == 'div': # divission Numerator First. b = stack.pop(-1) # denominator (after a in stack) a = stack.pop(-1) # numerator (before b in stack) if expresionIsConstant(b, pars): list1.append(1 / b) list2.append(('div', 1, b)) b = 1 / b stack.append(a * b) list1.append(stack[-1]) list2.append(('mul', a, b)) else: list2.append(('div', a, b)) stack.append(a / b) list1.append(stack[-1]) elif s == 'add': # addition b = stack.pop(-1) # second operand a = stack.pop(-1) # first operand stack.append(a + b) list1.append(stack[-1]) list2.append(('add', a, b)) elif s == 'pow': # any other pow b = stack.pop(-1) # exponent a = stack.pop(-1) # basis stack.append(a**b) list1.append(stack[-1]) list2.append(('pow', a, b)) elif s[0] == 'py_call' and 'sqrt' in str( s[1]): # square root. Compute as power a = stack.pop(-1) # argument of sqrt stack.append(sqrt(a)) list1.append(stack[-1]) list2.append(('pow', a, 0.5)) elif s[0] == 'py_call' and str(s[1]) == 'log': # logarithm a = stack.pop(-1) # argument of log stack.append(log(a)) list1.append(stack[-1]) list2.append(('log', a)) elif s[0] == 'py_call' and str(s[1]) == 'exp': a = stack.pop(-1) # argument of exp stack.append(exp(a)) list1.append(stack[-1]) list2.append(('exp', a)) elif s[0] == 'py_call' and str( s[1]) == 'sin': # sine. For AD needs computation of cos a = stack.pop(-1) stack.append(sin(a)) list1.append(sin(a)) list1.append(cos(a)) list2.append(('sin', a)) list2.append(('cos', a)) elif s[0] == 'py_call' and str( s[1]) == 'cos': # cosine. For AD needs computation of sin a = stack.pop(-1) stack.append(cos(a)) list1.append(sin(a)) list1.append(cos(a)) list2.append(('sin', a)) list2.append(('cos', a)) elif s[0] == 'py_call' and str(s[1]) == 'tan': a = stack.pop(-1) stack.append(tan(a)) list1.append(sin(a)) list1.append(cos(a)) list1.append(tan(a)) list2.append(('sin', a)) list2.append(('cos', a)) list2.append(('div', sin(a), cos(a))) return list1, list2
def surface_density_gaussian(r, phi, param): r""" Surface density of a matter blob with a Gaussian profile INPUT: - ``r`` -- Boyer-Lindquist radial coordinate `\bar{r}` in the matter blob - ``phi`` -- Boyer-Lindquist azimuthal coordinate `\bar{\phi}` in the matter blob - ``param`` -- list of parameters defining the position and width of matter blob: - ``param[0]``: mean radius `r_0` (Boyer-Lindquist coordinate) - ``param[1]``: mean azimuthal angle `\phi_0` (Boyer-Lindquist coordinate) - ``param[2]``: width `\lambda` of the Gaussian profile - ``param[3]`` (optional): amplitude `\Sigma_0`; if not provided, then `\Sigma_0=1` is used OUTPUT: - surface density `\Sigma(\bar{r}, \bar{\phi})` EXAMPLES:: sage: from kerrgeodesic_gw import surface_density_gaussian sage: param = [6.5, 0., 0.3] sage: surface_density_gaussian(6.5, 0, param) 1.0 sage: surface_density_gaussian(8., 0, param) # tol 1.0e-13 1.3887943864964021e-11 sage: surface_density_gaussian(6.5, pi/16, param) # tol 1.0e-13 1.4901161193847656e-08 3D representation: `z=\Sigma(\bar{r}, \bar{\phi})` in terms of `x:=\bar{r}\cos\bar\phi` and `y:=\bar{r}\sin\bar\phi`:: sage: s_plot = lambda r, phi: surface_density_gaussian(r, phi, param) sage: r, phi, z = var('r phi z') sage: plot3d(s_plot, (r, 6, 8), (phi, -0.4, 0.4), ....: transformation=(r*cos(phi), r*sin(phi), z)) Graphics3d Object .. PLOT:: from kerrgeodesic_gw import surface_density_gaussian param = param = [6.5, 0., 0.3] s_plot = lambda r, phi: surface_density_gaussian(r, phi, param) r, phi, z = var('r phi z') g = plot3d(s_plot, (r, 6, 8), (phi, -0.4, 0.4), \ transformation=(r*cos(phi), r*sin(phi), z)) sphinx_plot(g) Use with a non-default amplitude (`\Sigma_0=10^{-5}`):: sage: sigma0 = 1.e-5 sage: param = [6.5, 0., 0.3, sigma0] sage: surface_density_gaussian(6.5, 0, param) 1e-05 """ r0, phi0, lam = param[0], param[1], param[2] Sigma0 = param[3] if len(param) == 4 else float(1) return float(Sigma0*exp(-((r - r0*cos(phi-phi0))**2 + (r0*sin(phi-phi0))**2)/lam**2))
def h_toy_model_semi_analytic(u, theta, phi, a, r0, phi0, lam, Dphi, l_max=10): r""" Return the gravitational wave emitted by a matter blob orbiting a Kerr black hole (semi-analytic computation based on a toy model surface density). The surface density of the matter blob is that given by :func:`surface_density_toy_model`. The gravitational wave is computed according to the formula .. MATH:: h = \frac{2\mu}{r} \, \sum_{\ell=2}^{\infty} \sum_{m=-\ell}^\ell \frac{Z^\infty_{\ell m}(r_0)}{(m\omega_0)^2} \; \text{sinc}\left( \frac{m}{2} \Delta\varphi \right) \, \text{sinc}\left( \frac{3}{4} \varepsilon \, m \omega_0 (1-a\omega_0)u \right) e^{- i m (\omega_0 u + \phi_0)} \, _{-2}S_{\ell m}^{a m \omega_0}(\theta,\varphi) INPUT: - ``u`` -- retarded time coordinate of the observer (in units of `M`, the BH mass): `u = t - r_*`, where `t` is the Boyer-Lindquist time coordinate and `r_*` is the tortoise coordinate - ``theta`` -- Boyer-Lindquist colatitute `\theta` of the observer - ``phi`` -- Boyer-Lindquist azimuthal coordinate `\phi` of the observer - ``a`` -- BH angular momentum parameter (in units of `M`) - ``r0`` -- mean radius `r_0` of the matter blob (Boyer-Lindquist coordinate) - ``phi0`` -- mean azimuthal angle `\phi_0` of the matter blob (Boyer-Lindquist coordinate) - ``lam`` -- radial extent `\lambda` of the matter blob - ``Dphi``-- opening angle `\Delta\phi` of the matter blob - ``l_max`` -- (default: 10) upper bound in the summation over the harmonic degree `\ell` OUTPUT: - a pair ``(hp, hc)``, where ``hp`` (resp. ``hc``) is `(r / \mu) h_+` (resp. `(r / \mu) h_\times`), `\mu` being the blob's mass and `r` is the Boyer-Lindquist radial coordinate of the observer EXAMPLES: Schwarzschild black hole:: sage: from kerrgeodesic_gw import h_toy_model_semi_analytic sage: a = 0 sage: r0, phi0, lam, Dphi = 6.5, 0, 0.6, 0.1 sage: u = 60. sage: h_toy_model_semi_analytic(u, pi/4, 0., a, r0, phi0, lam, Dphi) # tol 1.0e-13 (0.2999183296797872, 0.36916647790743246) sage: hp, hc = _ Comparison with the exact value:: sage: from kerrgeodesic_gw import (h_blob, blob_mass, ....: surface_density_toy_model) sage: param_surf_dens = [r0, phi0, lam, Dphi] sage: integ_range = [6.2, 6.8, -0.05, 0.05] sage: mu = blob_mass(a, surface_density_toy_model, param_surf_dens, ....: integ_range)[0] sage: hp0 = h_blob(u, pi/4, 0., a, surface_density_toy_model, ....: param_surf_dens, integ_range)[0] / mu sage: hc0 = h_blob(u, pi/4, 0., a, surface_density_toy_model, ....: param_surf_dens, integ_range, mode='x')[0] / mu sage: hp0, hc0 # tol 1.0e-13 (0.2951163078053617, 0.3743683023327848) sage: (hp - hp0) / hp0 # tol 1.0e-13 0.01627162494047128 sage: (hc - hc0) / hc0 # tol 1.0e-13 -0.013894938201066784 """ import numpy from sage.rings.real_double import RDF from sage.rings.complex_double import CDF from sage.symbolic.all import i as I from .spin_weighted_spherical_harm import spin_weighted_spherical_harmonic from .spin_weighted_spheroidal_harm import spin_weighted_spheroidal_harmonic from .zinf import Zinf u = RDF(u) theta = RDF(theta) phi = RDF(phi) a = RDF(a) omega0 = RDF(1. / (r0**1.5 + a)) eps = lam/r0 resu = CDF(0) for l in range(2, l_max+1): for m in range(-l, l+1): if m == 0: # m=0 is skipped continue # m_omega0 = RDF(m*omega0) if a == 0: Slm = spin_weighted_spherical_harmonic(-2, l, m, theta, phi, numerical=RDF) else: a = RDF(a) Slm = spin_weighted_spheroidal_harmonic(-2, l, m, a*m_omega0, theta, phi) # Division by pi in the Sinc function due to the defintion used by numpy resu += Zinf(a, l, m, r0) / m_omega0**2 \ * numpy.sinc(m*Dphi/2./numpy.pi) \ * numpy.sinc(0.75*eps*m_omega0*(1-a*omega0)*u/numpy.pi) \ * CDF(exp(-I*(m_omega0*u + m*phi0))) * Slm resu *= 2 return (resu.real(), -resu.imag())
def compute_flowpipe(A=None, X0=None, B=None, U=None, **kwargs): r"""Implements LGG reachability algorithm for the linear continuous system dx/dx = Ax + Bu. INPUTS: * ``A`` -- coefficient matrix of the system * ``X0`` -- initial set * ``B`` -- transformation of the input * ``U`` -- input set * ``time_step`` -- (default = 1e-2) time step * ``initial_time`` -- (default = 0) the initial time * ``time_horizon`` -- (default = 1) the final time * ``number_of_time_steps`` -- (default = ceil(T/tau)) number of time steps * "directions" -- (default: random, and a box) dictionary * ``solver`` -- LP solver. Valid options are: * 'GLPK' (default). * 'Gurobi' * ``base_ring`` -- base ring where polyhedral computations are performed Valid options are: * QQ - (default) rational field * RDF - real double field OUTPUTS: * ``flowpipe`` """ # ################ # Parse input # # ################ if A is None: raise ValueError('System matrix A is missing.') else: if 'sage.matrix' in str(type(A)): n = A.ncols() elif type(A) == np.ndarray: n = A.shape[0] base_ring = kwargs['base_ring'] if 'base_ring' in kwargs else QQ if X0 is None: raise ValueError('Initial state X0 is missing.') elif 'sage.geometry.polyhedron' not in str(type(X0)) and type(X0) == list: # If X0 is not some type of polyhedron, set an initial point X0 = Polyhedron(vertices = [X0], base_ring = base_ring) elif 'sage.geometry.polyhedron' not in str(type(X0)) and X0.is_vector(): X0 = Polyhedron(vertices = [X0], base_ring = base_ring) elif 'sage.geometry.polyhedron' in str(type(X0)): # ensure that all input sets are on the same ring # not sure about this if 1==0: if X0.base_ring() != base_ring: [F, g] = polyhedron_to_Hrep(X0) X0 = polyhedron_from_Hrep(F, g, base_ring=base_ring) else: raise ValueError('Initial state X0 not understood') if B is None: # the system is homogeneous: dx/dt = Ax got_homogeneous = True else: got_homogeneous = False if U is None: raise ValueError('Input range U is missing.') tau = kwargs['time_step'] if 'time_step' in kwargs else 1e-2 t0 = kwargs['initial_time'] if 'initial_time' in kwargs else 0 T = kwargs['time_horizon'] if 'time_horizon' in kwargs else 1 global N N = kwargs['number_of_time_steps'] if 'number_of_time_steps' in kwargs else ceil(T/tau) directions = kwargs['directions'] if 'directions' in kwargs else {'select':'box'} global solver solver = kwargs['solver'] if 'solver' in kwargs else 'GLPK' global verbose verbose = kwargs['verbose'] if 'verbose' in kwargs else 0 # this involves the convex hull of X0 and a Minkowski sum #first_element_evaluation = kwargs['first_element_evaluation'] if 'first_element_evaluation' in kwargs else 'approximate' # ####################################################### # Generate template directions # # ####################################################### if directions['select'] == 'box': if n==2: theta = [0,pi/2,pi,3*pi/2] # box dList = [vector(RR,[cos(t), sin(t)]) for t in theta] else: # directions of hypercube dList = [] dList += [-identity_matrix(n).column(i) for i in range(n)] dList += [identity_matrix(n).column(i) for i in range(n)] elif directions['select'] == 'oct': if n != 2: raise NotImplementedError('Directions select octagon not implemented for n other than 2. Try box.') theta = [i*pi/4 for i in range(8)] # octagon dList = [vector(RR,[cos(t), sin(t)]) for t in theta] elif directions['select'] == 'random': order = directions['order'] if 'order' in directions else 12 if n == 2: theta = [random.uniform(0, 2*pi.n(digits=5)) for i in range(order)] dList = [vector(RR,[cos(theta[i]), sin(theta[i])]) for i in range(order)] else: raise NotImplementedError('Directions select random not implemented for n greater than 2. Try box.') elif directions['select'] == 'custom': dList = directions['dList'] else: raise TypeError('Template directions not understood.') # transform directions to numpy array, and get number of directions dArray = np.array(dList) k = len(dArray) global Phi_tau, expX0, alpha_tau_B if got_homogeneous: # dx/dx = Ax # ####################################################### # Compute first element of the approximating sequence # # ####################################################### # compute matrix exponential exp(A*tau) Phi_tau = expm(np.multiply(A, tau)) # compute exp(tau*A)X0 expX0 = Phi_tau * X0 # compute the bloating factor Ainfty = A.norm(Infinity) RX0 = radius(X0) unitBall = BoxInfty(center = zero_vector(n), radius = 1, base_ring = base_ring) alpha_tau = (exp(tau*Ainfty) - 1 - tau*Ainfty)*(RX0) alpha_tau_B = (alpha_tau*np.identity(n)) * unitBall # now we have that: # Omega0 = X0.convex_hull(expX0.Minkowski_sum(alpha_tau_B)) # compute the first element of the approximating sequence, Omega_0 #if first_element_evaluation == 'exact': # Omega0 = X0.convex_hull(expX0.Minkowski_sum(alpha_tau_B)) #elif first_element_evaluation == 'approximate': # NOT TESTED!!! #Omega0_A = dArray # Omega0_b = np.zeros(k) # for i, d in enumerate(dArray): # rho_X0_d = supp_fun_polyhedron(X0, d, solver=solver, verbose=verbose) # rho_expX0_d = supp_fun_polyhedron(expX0, d, solver=solver, verbose=verbose) # rho_alpha_tau_B_d = supp_fun_polyhedron(alpha_tau_B, d, solver=solver, verbose=verbose) # Omega0_b[i] = max(rho_X0_d, rho_expX0_d + rho_alpha_tau_B_d); # Omega0 = PolyhedronFromHSpaceRep(dArray, Omega0_b); #W_tau = Polyhedron(vertices = [], ambient_dim=n) # since W_tau = [], supp_fun_polyhedron returns 0 # ################################################ # Build the sequence of approximations Omega_i # # ################################################ Omega_i_Family_SF = [_Omega_i_supports_hom(d, X0) for d in dArray] else: # dx/dx = Ax + Bu global tau_V, beta_tau_B # compute range of the input under B, V = BU V = B * U # compute matrix exponential exp(A*tau) Phi_tau = expm(np.multiply(A, tau)) # compute exp(tau*A)X0 expX0 = Phi_tau * X0 # compute the initial over-approximation tau_V = (tau*np.identity(n)) * V # compute the bloating factor Ainfty = A.norm(Infinity) RX0 = radius(X0) RV = radius(V) unitBall = BoxInfty(center = zero_vector(n), radius = 1, base_ring = base_ring) alpha_tau = (exp(tau*Ainfty) - 1 - tau*Ainfty)*(RX0 + RV/Ainfty) alpha_tau_B = (alpha_tau*np.identity(n)) * unitBall # compute the first element of the approximating sequence, Omega_0 #aux = expX0.Minkowski_sum(tau_V) #Omega0 = X0.convex_hull(aux.Minkowski_sum(alpha_tau_B)) beta_tau = (exp(tau*Ainfty) - 1 - tau*Ainfty)*(RV/Ainfty) beta_tau_B = (beta_tau*np.identity(n)) * unitBall #W_tau = tau_V.Minkowski_sum(beta_tau_B) # ################################################ # Build the sequence of approximations Omega_i # # ################################################ Omega_i_Family_SF = [_Omega_i_supports_inhom(d, X0) for d in dArray] # ################################################ # Build the approximating polyhedra # # ################################################ # each polytope is built using the support functions over-approximation Omega_i_Poly = list() # This loop can be vectorized (?) for i in range(N): # we have N polytopes # for each one, use all directions A = matrix(base_ring, k, n); b = vector(base_ring, k) for j in range(k): #run over directions s_fun = Omega_i_Family_SF[j][i] A.set_row(j, dList[j]) b[j] = s_fun Omega_i_Poly.append( polyhedron_from_Hrep(A, b, base_ring = base_ring) ) return Omega_i_Poly
def _closed_form(hyp): a, b, z = hyp.operands() a, b = a.operands(), b.operands() p, q = len(a), len(b) if z == 0: return Integer(1) if p == q == 0: return exp(z) if p == 1 and q == 0: return (1 - z)**(-a[0]) if p == 0 and q == 1: # TODO: make this require only linear time def _0f1(b, z): F12 = cosh(2 * sqrt(z)) F32 = sinh(2 * sqrt(z)) / (2 * sqrt(z)) if 2 * b == 1: return F12 if 2 * b == 3: return F32 if 2 * b > 3: return ((b - 2) * (b - 1) / z * (_0f1(b - 2, z) - _0f1(b - 1, z))) if 2 * b < 1: return (_0f1(b + 1, z) + z / (b * (b + 1)) * _0f1(b + 2, z)) raise ValueError # Can evaluate 0F1 in terms of elementary functions when # the parameter is a half-integer if 2 * b[0] in ZZ and b[0] not in ZZ: return _0f1(b[0], z) # Confluent hypergeometric function if p == 1 and q == 1: aa, bb = a[0], b[0] if aa * 2 == 1 and bb * 2 == 3: t = sqrt(-z) return sqrt(pi) / 2 * erf(t) / t if a == 1 and b == 2: return (exp(z) - 1) / z n, m = aa, bb if n in ZZ and m in ZZ and m > 0 and n > 0: rf = rising_factorial if m <= n: return (exp(z) * sum( rf(m - n, k) * (-z)**k / factorial(k) / rf(m, k) for k in xrange(n - m + 1))) else: T = sum( rf(n - m + 1, k) * z**k / (factorial(k) * rf(2 - m, k)) for k in xrange(m - n)) U = sum( rf(1 - n, k) * (-z)**k / (factorial(k) * rf(2 - m, k)) for k in xrange(n)) return (factorial(m - 2) * rf(1 - m, n) * z**(1 - m) / factorial(n - 1) * (T - exp(z) * U)) if p == 2 and q == 1: R12 = QQ('1/2') R32 = QQ('3/2') def _2f1(a, b, c, z): """ Evaluation of 2F1(a, b, c, z), assuming a, b, c positive integers or half-integers """ if b == c: return (1 - z)**(-a) if a == c: return (1 - z)**(-b) if a == 0 or b == 0: return Integer(1) if a > b: a, b = b, a if b >= 2: F1 = _2f1(a, b - 1, c, z) F2 = _2f1(a, b - 2, c, z) q = (b - 1) * (z - 1) return (((c - 2 * b + 2 + (b - a - 1) * z) * F1 + (b - c - 1) * F2) / q) if c > 2: # how to handle this case? if a - c + 1 == 0 or b - c + 1 == 0: raise NotImplementedError F1 = _2f1(a, b, c - 1, z) F2 = _2f1(a, b, c - 2, z) r1 = (c - 1) * (2 - c - (a + b - 2 * c + 3) * z) r2 = (c - 1) * (c - 2) * (1 - z) q = (a - c + 1) * (b - c + 1) * z return (r1 * F1 + r2 * F2) / q if (a, b, c) == (R12, 1, 2): return (2 - 2 * sqrt(1 - z)) / z if (a, b, c) == (1, 1, 2): return -log(1 - z) / z if (a, b, c) == (1, R32, R12): return (1 + z) / (1 - z)**2 if (a, b, c) == (1, R32, 2): return 2 * (1 / sqrt(1 - z) - 1) / z if (a, b, c) == (R32, 2, R12): return (1 + 3 * z) / (1 - z)**3 if (a, b, c) == (R32, 2, 1): return (2 + z) / (2 * (sqrt(1 - z) * (1 - z)**2)) if (a, b, c) == (2, 2, 1): return (1 + z) / (1 - z)**3 raise NotImplementedError aa, bb = a cc, = b if z == 1: return (gamma(cc) * gamma(cc - aa - bb) / gamma(cc - aa) / gamma(cc - bb)) if ((aa * 2) in ZZ and (bb * 2) in ZZ and (cc * 2) in ZZ and aa > 0 and bb > 0 and cc > 0): try: return _2f1(aa, bb, cc, z) except NotImplementedError: pass return hyp
def subexpressions_list(f, pars=None): """ Construct the lists with the intermediate steps on the evaluation of the function. INPUT: - ``f`` -- a symbolic function of several components. - ``pars`` -- a list of the parameters that appear in the function this should be the symbolic constants that appear in f but are not arguments. OUTPUT: - a list of the intermediate subexpressions that appear in the evaluation of f. - a list with the operations used to construct each of the subexpressions. each element of this list is a tuple, formed by a string describing the operation made, and the operands. For the trigonometric functions, some extra expressions will be added. These extra expressions will be used later to compute their derivatives. EXAMPLES:: sage: from sage.interfaces.tides import subexpressions_list sage: var('x,y') (x, y) sage: f(x,y) = [x^2+y, cos(x)/log(y)] sage: subexpressions_list(f) ([x^2, x^2 + y, sin(x), cos(x), log(y), cos(x)/log(y)], [('mul', x, x), ('add', y, x^2), ('sin', x), ('cos', x), ('log', y), ('div', log(y), cos(x))]) :: sage: f(a)=[cos(a), arctan(a)] sage: from sage.interfaces.tides import subexpressions_list sage: subexpressions_list(f) ([sin(a), cos(a), a^2, a^2 + 1, arctan(a)], [('sin', a), ('cos', a), ('mul', a, a), ('add', 1, a^2), ('atan', a)]) :: sage: from sage.interfaces.tides import subexpressions_list sage: var('s,b,r') (s, b, r) sage: f(t,x,y,z)= [s*(y-x),x*(r-z)-y,x*y-b*z] sage: subexpressions_list(f,[s,b,r]) ([-y, x - y, s*(x - y), -s*(x - y), -z, r - z, (r - z)*x, -y, (r - z)*x - y, x*y, b*z, -b*z, x*y - b*z], [('mul', -1, y), ('add', -y, x), ('mul', x - y, s), ('mul', -1, s*(x - y)), ('mul', -1, z), ('add', -z, r), ('mul', x, r - z), ('mul', -1, y), ('add', -y, (r - z)*x), ('mul', y, x), ('mul', z, b), ('mul', -1, b*z), ('add', -b*z, x*y)]) :: sage: var('x, y') (x, y) sage: f(x,y)=[exp(x^2+sin(y))] sage: from sage.interfaces.tides import * sage: subexpressions_list(f) ([x^2, sin(y), cos(y), x^2 + sin(y), e^(x^2 + sin(y))], [('mul', x, x), ('sin', y), ('cos', y), ('add', sin(y), x^2), ('exp', x^2 + sin(y))]) """ from sage.functions.trig import sin, cos, arcsin, arctan, arccos variables = f[0].arguments() if not pars: parameters = [] else: parameters = pars varpar = list(parameters) + list(variables) F = symbolic_expression([i(*variables) for i in f]).function(*varpar) lis = flatten([fast_callable(i,vars=varpar).op_list() for i in F], max_level=1) stack = [] const =[] stackcomp=[] detail=[] for i in lis: if i[0] == 'load_arg': stack.append(varpar[i[1]]) elif i[0] == 'ipow': if i[1] in NN: basis = stack[-1] for j in range(i[1]-1): a=stack.pop(-1) detail.append(('mul', a, basis)) stack.append(a*basis) stackcomp.append(stack[-1]) else: detail.append(('pow',stack[-1],i[1])) stack[-1]=stack[-1]**i[1] stackcomp.append(stack[-1]) elif i[0] == 'load_const': const.append(i[1]) stack.append(i[1]) elif i == 'mul': a=stack.pop(-1) b=stack.pop(-1) detail.append(('mul', a, b)) stack.append(a*b) stackcomp.append(stack[-1]) elif i == 'div': a=stack.pop(-1) b=stack.pop(-1) detail.append(('div', a, b)) stack.append(b/a) stackcomp.append(stack[-1]) elif i == 'add': a=stack.pop(-1) b=stack.pop(-1) detail.append(('add',a,b)) stack.append(a+b) stackcomp.append(stack[-1]) elif i == 'pow': a=stack.pop(-1) b=stack.pop(-1) detail.append(('pow', b, a)) stack.append(b**a) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='log': a=stack.pop(-1) detail.append(('log', a)) stack.append(log(a)) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='exp': a=stack.pop(-1) detail.append(('exp', a)) stack.append(exp(a)) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='sin': a=stack.pop(-1) detail.append(('sin', a)) detail.append(('cos', a)) stackcomp.append(sin(a)) stackcomp.append(cos(a)) stack.append(sin(a)) elif i[0] == 'py_call' and str(i[1])=='cos': a=stack.pop(-1) detail.append(('sin', a)) detail.append(('cos', a)) stackcomp.append(sin(a)) stackcomp.append(cos(a)) stack.append(cos(a)) elif i[0] == 'py_call' and str(i[1])=='tan': a=stack.pop(-1) b = sin(a) c = cos(a) detail.append(('sin', a)) detail.append(('cos', a)) detail.append(('div', b, c)) stackcomp.append(b) stackcomp.append(c) stackcomp.append(b/c) stack.append(b/c) elif i[0] == 'py_call' and str(i[1])=='arctan': a=stack.pop(-1) detail.append(('mul', a, a)) detail.append(('add', 1, a*a)) detail.append(('atan', a)) stackcomp.append(a*a) stackcomp.append(1+a*a) stackcomp.append(arctan(a)) stack.append(arctan(a)) elif i[0] == 'py_call' and str(i[1])=='arcsin': a=stack.pop(-1) detail.append(('mul', a, a)) detail.append(('mul', -1, a*a)) detail.append(('add', 1, -a*a)) detail.append(('pow', 1- a*a, 0.5)) detail.append(('asin', a)) stackcomp.append(a*a) stackcomp.append(-a*a) stackcomp.append(1-a*a) stackcomp.append(sqrt(1-a*a)) stackcomp.append(arcsin(a)) stack.append(arcsin(a)) elif i[0] == 'py_call' and str(i[1])=='arccos': a=stack.pop(-1) detail.append(('mul', a, a)) detail.append(('mul', -1, a*a)) detail.append(('add', 1, -a*a)) detail.append(('pow', 1- a*a, 0.5)) detail.append(('mul', -1, sqrt(1-a*a))) detail.append(('acos', a)) stackcomp.append(a*a) stackcomp.append(-a*a) stackcomp.append(1-a*a) stackcomp.append(sqrt(1-a*a)) stackcomp.append(-sqrt(1-a*a)) stackcomp.append(arccos(a)) stack.append(arccos(a)) elif i[0] == 'py_call' and 'sqrt' in str(i[1]): a=stack.pop(-1) detail.append(('pow', a, 0.5)) stackcomp.append(sqrt(a)) stack.append(sqrt(a)) elif i == 'neg': a = stack.pop(-1) detail.append(('mul', -1, a)) stack.append(-a) stackcomp.append(-a) return stackcomp,detail
def __init__(self, B, sigma=1, c=None, precision=None): r""" Construct a discrete Gaussian sampler over the lattice `Λ(B)` with parameter ``sigma`` and center `c`. INPUT: - ``B`` -- a basis for the lattice, one of the following: - an integer matrix, - an object with a ``matrix()`` method, e.g. ``ZZ^n``, or - an object where ``matrix(B)`` succeeds, e.g. a list of vectors. - ``sigma`` -- Gaussian parameter `σ>0`. - ``c`` -- center `c`, any vector in `\ZZ^n` is supported, but `c ∈ Λ(B)` is faster. - ``precision`` -- bit precision `≥ 53`. EXAMPLES:: sage: from sage.stats.distributions.discrete_gaussian_lattice import DiscreteGaussianDistributionLatticeSampler sage: n = 2; sigma = 3.0; m = 5000 sage: D = DiscreteGaussianDistributionLatticeSampler(ZZ^n, sigma) sage: f = D.f sage: c = D._normalisation_factor_zz(); c 56.2162803067524 sage: l = [D() for _ in range(m)] sage: v = vector(ZZ, n, (-3,-3)) sage: l.count(v), ZZ(round(m*f(v)/c)) (39, 33) sage: target = vector(ZZ, n, (0,0)) sage: l.count(target), ZZ(round(m*f(target)/c)) (116, 89) sage: from sage.stats.distributions.discrete_gaussian_lattice import DiscreteGaussianDistributionLatticeSampler sage: qf = QuadraticForm(matrix(3, [2, 1, 1, 1, 2, 1, 1, 1, 2])) sage: D = DiscreteGaussianDistributionLatticeSampler(qf, 3.0); D Discrete Gaussian sampler with σ = 3.000000, c=(0, 0, 0) over lattice with basis <BLANKLINE> [2 1 1] [1 2 1] [1 1 2] sage: D() (0, 1, -1) """ precision = DiscreteGaussianDistributionLatticeSampler.compute_precision(precision, sigma) self._RR = RealField(precision) self._sigma = self._RR(sigma) try: B = matrix(B) except (TypeError, ValueError): pass try: B = B.matrix() except AttributeError: pass self.B = B self._G = B.gram_schmidt()[0] try: c = vector(ZZ, B.ncols(), c) except TypeError: try: c = vector(QQ, B.ncols(), c) except TypeError: c = vector(RR, B.ncols(), c) self._c = c self.f = lambda x: exp(-(vector(ZZ, B.ncols(), x) - c).norm() ** 2 / (2 * self._sigma ** 2)) # deal with trivial case first, it is common if self._G == 1 and self._c == 0: self._c_in_lattice = True D = DiscreteGaussianDistributionIntegerSampler(sigma=sigma) self.D = tuple([D for _ in range(self.B.nrows())]) self.VS = FreeModule(ZZ, B.nrows()) return w = B.solve_left(c) if w in ZZ ** B.nrows(): self._c_in_lattice = True D = [] for i in range(self.B.nrows()): sigma_ = self._sigma / self._G[i].norm() D.append(DiscreteGaussianDistributionIntegerSampler(sigma=sigma_)) self.D = tuple(D) self.VS = FreeModule(ZZ, B.nrows()) else: self._c_in_lattice = False
def _compute_sw_spherical_harm(s, l, m, theta, phi, condon_shortley=True, numerical=None): r""" Compute the spin-weighted spherical harmonic of spin weight ``s`` and indices ``(l,m)`` as a callable symbolic expression in (theta,phi) INPUT: - ``s`` -- integer; the spin weight - ``l`` -- non-negative integer; the harmonic degree - ``m`` -- integer within the range ``[-l, l]``; the azimuthal number - ``theta`` -- colatitude angle - ``phi`` -- azimuthal angle - ``condon_shortley`` -- (default: ``True``) determines whether the Condon-Shortley phase of `(-1)^m` is taken into account (see below) - ``numerical`` -- (default: ``None``) determines whether a symbolic or a numerical computation of a given type is performed; allowed values are - ``None``: a symbolic computation is performed - ``RDF``: Sage's machine double precision floating-point numbers (``RealDoubleField``) - ``RealField(n)``, where ``n`` is a number of bits: Sage's floating-point numbers with an arbitrary precision; note that ``RR`` is a shortcut for ``RealField(53)``. - ``float``: Python's floating-point numbers OUTPUT: - `{}_s Y_l^m(\theta,\phi)` either - as a symbolic expression if ``numerical`` is ``None`` - or a pair of floating-point numbers, each of them being of the type corresponding to ``numerical`` and representing respectively the real and imaginary parts of `{}_s Y_l^m(\theta,\phi)` ALGORITHM: The spin-weighted spherical harmonic is evaluated according to Eq. (3.1) of J. N. Golberg et al., J. Math. Phys. **8**, 2155 (1967) [:doi:`10.1063/1.1705135`], with an extra `(-1)^m` factor (the so-called *Condon-Shortley phase*) if ``condon_shortley`` is ``True``, the actual formula being then the one given in :wikipedia:`Spin-weighted_spherical_harmonics#Calculating` TESTS:: sage: from kerrgeodesic_gw.spin_weighted_spherical_harm import _compute_sw_spherical_harm sage: theta, phi = var("theta phi") sage: _compute_sw_spherical_harm(-2, 2, 1, theta, phi) 1/4*(sqrt(5)*cos(theta) + sqrt(5))*e^(I*phi)*sin(theta)/sqrt(pi) """ if abs(s) > l: return ZZ(0) if abs(theta) < 1.e-6: # TODO: fix the treatment of small theta values if theta < 0: # possibly with exact formula for theta=0 theta = -1.e-6 # else: # theta = 1.e-6 # cott2 = cos(theta / 2) / sin(theta / 2) res = 0 for r in range(l - s + 1): res += (-1)**(l - r - s) * (binomial(l - s, r) * binomial( l + s, r + s - m) * cott2**(2 * r + s - m)) res *= sin(theta / 2)**(2 * l) ff = factorial(l + m) * factorial(l - m) * (2 * l + 1) / ( factorial(l + s) * factorial(l - s)) if numerical: pre = sqrt(numerical(ff) / numerical(pi)) / 2 else: pre = sqrt(ff) / (2 * sqrt(pi)) res *= pre if condon_shortley: res *= (-1)**m if numerical: return (numerical(res * cos(m * phi)), numerical(res * sin(m * phi))) # Symbolic case: res = res.simplify_full() res = res.reduce_trig() # get rid of cos(theta/2) and sin(theta/2) res = res.simplify_trig() # further trigonometric simplifications res *= exp(I * m * phi) return res
def f(t): return exp(I * ((cos(t) + I * sin(t)) * arc_radius + arc_center)) * radius
def __init__(self, B, sigma=1, c=None, precision=None): r""" Construct a discrete Gaussian sampler over the lattice `Λ(B)` with parameter ``sigma`` and center `c`. INPUT: - ``B`` -- a basis for the lattice, one of the following: - an integer matrix, - an object with a ``matrix()`` method, e.g. ``ZZ^n``, or - an object where ``matrix(B)`` succeeds, e.g. a list of vectors. - ``sigma`` -- Gaussian parameter `σ>0`. - ``c`` -- center `c`, any vector in `\ZZ^n` is supported, but `c ∈ Λ(B)` is faster. - ``precision`` -- bit precision `≥ 53`. EXAMPLE:: sage: from sage.stats.distributions.discrete_gaussian_lattice import DiscreteGaussianDistributionLatticeSampler sage: n = 2; sigma = 3.0; m = 5000 sage: D = DiscreteGaussianDistributionLatticeSampler(ZZ^n, sigma) sage: f = D.f sage: c = D._normalisation_factor_zz(); c 56.2162803067524 sage: l = [D() for _ in xrange(m)] sage: v = vector(ZZ, n, (-3,-3)) sage: l.count(v), ZZ(round(m*f(v)/c)) (39, 33) sage: target = vector(ZZ, n, (0,0)) sage: l.count(target), ZZ(round(m*f(target)/c)) (116, 89) sage: from sage.stats.distributions.discrete_gaussian_lattice import DiscreteGaussianDistributionLatticeSampler sage: qf = QuadraticForm(matrix(3, [2, 1, 1, 1, 2, 1, 1, 1, 2])) sage: D = DiscreteGaussianDistributionLatticeSampler(qf, 3.0); D Discrete Gaussian sampler with σ = 3.000000, c=(0, 0, 0) over lattice with basis <BLANKLINE> [2 1 1] [1 2 1] [1 1 2] sage: D() (0, 1, -1) """ precision = DiscreteGaussianDistributionLatticeSampler.compute_precision(precision, sigma) self._RR = RealField(precision) self._sigma = self._RR(sigma) try: B = matrix(B) except ValueError: pass try: B = B.matrix() except AttributeError: pass self.B = B self._G = B.gram_schmidt()[0] try: c = vector(ZZ, B.ncols(), c) except TypeError: try: c = vector(QQ, B.ncols(), c) except TypeError: c = vector(RR, B.ncols(), c) self._c = c self.f = lambda x: exp(-(vector(ZZ, B.ncols(), x)-c).norm()**2/(2*self._sigma**2)) # deal with trivial case first, it is common if self._G == 1 and self._c == 0: self._c_in_lattice = True D = DiscreteGaussianDistributionIntegerSampler(sigma=sigma) self.D = tuple([D for _ in range(self.B.nrows())]) self.VS = FreeModule(ZZ, B.nrows()) return w = B.solve_left(c) if w in ZZ**B.nrows(): self._c_in_lattice = True D = [] for i in range(self.B.nrows()): sigma_ = self._sigma/self._G[i].norm() D.append( DiscreteGaussianDistributionIntegerSampler(sigma=sigma_) ) self.D = tuple(D) self.VS = FreeModule(ZZ, B.nrows()) else: self._c_in_lattice = False
def smallest_poly(F, prec=53, norm_type='norm', emb=None): r""" Determine the poly with smallest coefficients in `SL(2,\Z)` orbit of ``F`` Smallest can be in the sense of `L_2` norm or height. The method is the algorithm in Hutz-Stoll [HS2018]_. ``F`` needs to be a binary form with no multiple roots of degree at least 3. It should already be reduced in the sense of Cremona-Stoll [CS2003]_. INPUT: - ``F`` -- binary form of degree at least 3 with no multiple roots - ``norm_type`` -- string - ``norm`` or ``height`` controlling what ``smallest`` means for the coefficients. OUTPUT: pair [poly, matrix] EXAMPLES:: sage: from sage.rings.polynomial.binary_form_reduce import smallest_poly sage: R.<x,y> = QQ[] sage: F = -x^8 + 6*x^7*y - 7*x^6*y^2 - 12*x^5*y^3 + 27*x^4*y^4\ ....: - 4*x^3*y^5 - 19*x^2*y^6 + 10*x*y^7 - 5*y^8 sage: smallest_poly(F, prec=100) #long time [ -x^8 - 2*x^7*y + 7*x^6*y^2 + 16*x^5*y^3 + 2*x^4*y^4 - 2*x^3*y^5 + 4*x^2*y^6 - 5*y^8, <BLANKLINE> [1 1] [0 1] ] :: sage: from sage.rings.polynomial.binary_form_reduce import smallest_poly, get_bound_poly sage: R.<x,y> = QQ[] sage: F = -2*x^3 + 2*x^2*y + 3*x*y^2 + 127*y^3 sage: smallest_poly(F) [ [1 4] -2*x^3 - 22*x^2*y - 77*x*y^2 + 43*y^3, [0 1] ] sage: F0, M = smallest_poly(F, norm_type='height') sage: F0, M # random ( [5 4] -58*x^3 - 47*x^2*y + 52*x*y^2 + 43*y^3, [1 1] ) sage: M in SL2Z, F0 == R.hom(M * vector([x, y]))(F) (True, True) sage: get_bound_poly(F0, norm_type='height') # tol 1e-12 23.3402702199809 An example with a multiple root:: sage: R.<x,y> = QQ[] sage: F = -16*x^7 - 114*x^6*y - 345*x^5*y^2 - 599*x^4*y^3 - 666*x^3*y^4\ ....: - 481*x^2*y^5 - 207*x*y^6 - 40*y^7 sage: F.reduced_form() ( [-1 -1] -x^5*y^2 - 24*x^3*y^4 - 3*x^2*y^5 - 2*x*y^6 + 16*y^7, [ 1 0] ) """ def insert_item(pts, item, index): #binary insertion to maintain list of points left to consider N = len(pts) if N == 0: return [item] elif N == 1: if item[index] > pts[0][index]: pts.insert(0, item) else: pts.append(item) return pts else: # binary insertion left = 1 right = N mid = (left + right) // 2 # these are ints so this is .floor() if item[index] > pts[mid][index]: # item goes into first half return insert_item(pts[:mid], item, index) + pts[mid:N] else: # item goes into second half return pts[:mid] + insert_item(pts[mid:N], item, index) def coshdelta(z): #The cosh of the hyperbolic distance from z = t+uj to j return (z.norm() + 1) / (2 * z.imag() ) #reduce in the sense of Cremona-Stoll G = F MG = matrix(ZZ, 2, 2, [1, 0, 0, 1]) x, y = G.parent().gens() if norm_type == 'norm': current_size = sum([abs(i)**2 for i in G.coefficients() ]) #euclidean norm squared elif norm_type == 'height': #height current_size = exp( max([c.global_height(prec=prec) for c in G.coefficients()])) else: raise ValueError('type must be norm or height') v0, th = covariant_z0(G, prec=prec, emb=emb) rep = 2 * CC.gen(0) #representative point in fundamental domain from math import isnan if isnan(v0.abs()): raise ValueError("invalid covariant: %s" % v0) R = get_bound_poly(G, prec=prec, norm_type=norm_type) #check orbit S = matrix(ZZ, 2, 2, [0, -1, 1, 0]) T = matrix(ZZ, 2, 2, [1, 1, 0, 1]) TI = matrix(ZZ, 2, 2, [1, -1, 0, 1]) count = 0 pts = [[G, v0, rep, MG, coshdelta(v0), 0]] #label - 0:None, 1:S, 2:T, 3:T^(-1) current_min = [G, v0, rep, MG, coshdelta(v0)] while pts != []: G, v, rep, M, D, label = pts.pop() #apply ST and keep z, Sz if D > R: break #all remaining pts are too far away #check if it is smaller. If so, we can improve the bound count += 1 if norm_type == 'norm': new_size = sum([abs(i)**2 for i in G.coefficients() ]) #euclidean norm squared else: #height new_size = exp( max([c.global_height(prec=prec) for c in G.coefficients()])) if new_size < current_size: current_min = [G, v, rep, M, coshdelta(v)] current_size = new_size R = get_bound_poly(G, norm_type=norm_type, prec=prec, emb=emb) #add new points to check if label != 1 and min((rep + 1).norm(), (rep - 1).norm()) >= 1: #don't undo S # the 2nd condition is equivalent to |\Re(-1/rep)| <= 1/2 # this means that rep can have resulted from an inversion step in # the shift-and-invert procedure, so don't invert # do inversion z = -1 / v new_pt = [ G.subs({ x: -y, y: x }), z, -1 / rep, M * S, coshdelta(z), 1 ] pts = insert_item(pts, new_pt, 4) if label != 3: #don't undo TI #do right shift z = v - 1 new_pt = [G.subs({x: x + y}), z, rep - 1, M * T, coshdelta(z), 2] pts = insert_item(pts, new_pt, 4) if label != 2: #don't undo T #do left shift z = v + 1 new_pt = [G.subs({x: x - y}), z, rep + 1, M * TI, coshdelta(z), 3] pts = insert_item(pts, new_pt, 4) return [current_min[0], current_min[3]]
def _closed_form(hyp): a, b, z = hyp.operands() a, b = a.operands(), b.operands() p, q = len(a), len(b) if z == 0: return Integer(1) if p == q == 0: return exp(z) if p == 1 and q == 0: return (1 - z) ** (-a[0]) if p == 0 and q == 1: # TODO: make this require only linear time def _0f1(b, z): F12 = cosh(2 * sqrt(z)) F32 = sinh(2 * sqrt(z)) / (2 * sqrt(z)) if 2 * b == 1: return F12 if 2 * b == 3: return F32 if 2 * b > 3: return ((b - 2) * (b - 1) / z * (_0f1(b - 2, z) - _0f1(b - 1, z))) if 2 * b < 1: return (_0f1(b + 1, z) + z / (b * (b + 1)) * _0f1(b + 2, z)) raise ValueError # Can evaluate 0F1 in terms of elementary functions when # the parameter is a half-integer if 2 * b[0] in ZZ and b[0] not in ZZ: return _0f1(b[0], z) # Confluent hypergeometric function if p == 1 and q == 1: aa, bb = a[0], b[0] if aa * 2 == 1 and bb * 2 == 3: t = sqrt(-z) return sqrt(pi) / 2 * erf(t) / t if a == 1 and b == 2: return (exp(z) - 1) / z n, m = aa, bb if n in ZZ and m in ZZ and m > 0 and n > 0: rf = rising_factorial if m <= n: return (exp(z) * sum(rf(m - n, k) * (-z) ** k / factorial(k) / rf(m, k) for k in xrange(n - m + 1))) else: T = sum(rf(n - m + 1, k) * z ** k / (factorial(k) * rf(2 - m, k)) for k in xrange(m - n)) U = sum(rf(1 - n, k) * (-z) ** k / (factorial(k) * rf(2 - m, k)) for k in xrange(n)) return (factorial(m - 2) * rf(1 - m, n) * z ** (1 - m) / factorial(n - 1) * (T - exp(z) * U)) if p == 2 and q == 1: R12 = QQ('1/2') R32 = QQ('3/2') def _2f1(a, b, c, z): """ Evaluation of 2F1(a, b; c; z), assuming a, b, c positive integers or half-integers """ if b == c: return (1 - z) ** (-a) if a == c: return (1 - z) ** (-b) if a == 0 or b == 0: return Integer(1) if a > b: a, b = b, a if b >= 2: F1 = _2f1(a, b - 1, c, z) F2 = _2f1(a, b - 2, c, z) q = (b - 1) * (z - 1) return (((c - 2 * b + 2 + (b - a - 1) * z) * F1 + (b - c - 1) * F2) / q) if c > 2: # how to handle this case? if a - c + 1 == 0 or b - c + 1 == 0: raise NotImplementedError F1 = _2f1(a, b, c - 1, z) F2 = _2f1(a, b, c - 2, z) r1 = (c - 1) * (2 - c - (a + b - 2 * c + 3) * z) r2 = (c - 1) * (c - 2) * (1 - z) q = (a - c + 1) * (b - c + 1) * z return (r1 * F1 + r2 * F2) / q if (a, b, c) == (R12, 1, 2): return (2 - 2 * sqrt(1 - z)) / z if (a, b, c) == (1, 1, 2): return -log(1 - z) / z if (a, b, c) == (1, R32, R12): return (1 + z) / (1 - z) ** 2 if (a, b, c) == (1, R32, 2): return 2 * (1 / sqrt(1 - z) - 1) / z if (a, b, c) == (R32, 2, R12): return (1 + 3 * z) / (1 - z) ** 3 if (a, b, c) == (R32, 2, 1): return (2 + z) / (2 * (sqrt(1 - z) * (1 - z) ** 2)) if (a, b, c) == (2, 2, 1): return (1 + z) / (1 - z) ** 3 raise NotImplementedError aa, bb = a cc, = b if z == 1: return (gamma(cc) * gamma(cc - aa - bb) / gamma(cc - aa) / gamma(cc - bb)) if ((aa * 2) in ZZ and (bb * 2) in ZZ and (cc * 2) in ZZ and aa > 0 and bb > 0 and cc > 0): try: return _2f1(aa, bb, cc, z) except NotImplementedError: pass return hyp
def Stirling(var, precision=None, skip_constant_factor=False): r""" Return Stirling's approximation formula for factorials. INPUT: - ``var`` -- a string for the variable name. - ``precision`` -- (default: ``None``) an integer `\ge 3`. If ``None``, then the default precision of the asymptotic ring is used. - ``skip_constant_factor`` -- (default: ``False``) a boolean. If set, then the constant factor `\sqrt{2\pi}` is left out. As a consequence, the coefficient ring of the output changes from ``Symbolic Constants Subring`` (if ``False``) to ``Rational Field`` (if ``True``). OUTPUT: An asymptotic expansion. EXAMPLES:: sage: asymptotic_expansions.Stirling('n', precision=5) sqrt(2)*sqrt(pi)*e^(n*log(n))*(e^n)^(-1)*n^(1/2) + 1/12*sqrt(2)*sqrt(pi)*e^(n*log(n))*(e^n)^(-1)*n^(-1/2) + 1/288*sqrt(2)*sqrt(pi)*e^(n*log(n))*(e^n)^(-1)*n^(-3/2) + O(e^(n*log(n))*(e^n)^(-1)*n^(-5/2)) sage: _.parent() Asymptotic Ring <(e^(n*log(n)))^QQ * (e^n)^QQ * n^QQ * log(n)^QQ> over Symbolic Constants Subring .. SEEALSO:: :meth:`log_Stirling`, :meth:`~sage.rings.asymptotic.asymptotic_ring.AsymptoticExpansion.factorial`. TESTS:: sage: expansion = asymptotic_expansions.Stirling('n', precision=5) sage: n = expansion.parent().gen() sage: expansion.compare_with_values(n, lambda x: x.factorial(), [5, 10, 20]) # rel tol 1e-6 [(5, 0.00675841118?), (10, 0.0067589306?), (20, 0.006744925?)] sage: asymptotic_expansions.Stirling('n', precision=5, ....: skip_constant_factor=True) e^(n*log(n))*(e^n)^(-1)*n^(1/2) + 1/12*e^(n*log(n))*(e^n)^(-1)*n^(-1/2) + 1/288*e^(n*log(n))*(e^n)^(-1)*n^(-3/2) + O(e^(n*log(n))*(e^n)^(-1)*n^(-5/2)) sage: _.parent() Asymptotic Ring <(e^(n*log(n)))^QQ * (e^n)^QQ * n^QQ * log(n)^QQ> over Rational Field sage: asymptotic_expansions.Stirling('m', precision=4) sqrt(2)*sqrt(pi)*e^(m*log(m))*(e^m)^(-1)*m^(1/2) + O(e^(m*log(m))*(e^m)^(-1)*m^(-1/2)) sage: asymptotic_expansions.Stirling('m', precision=3) O(e^(m*log(m))*(e^m)^(-1)*m^(1/2)) sage: asymptotic_expansions.Stirling('m', precision=2) Traceback (most recent call last): ... ValueError: precision must be at least 3 """ if precision < 3: raise ValueError("precision must be at least 3") log_Stirling = AsymptoticExpansionGenerators.log_Stirling( var, precision=precision, skip_constant_summand=True) P = log_Stirling.parent().change_parameter( growth_group='(e^({n}*log({n})))^QQ * (e^{n})^QQ * {n}^QQ * log({n})^QQ'.format(n=var)) from sage.functions.log import exp result = exp(P(log_Stirling)) if not skip_constant_factor: from sage.symbolic.ring import SR SCR = SR.subring(no_variables=True) result *= (2*SCR('pi')).sqrt() return result