def test_rewrite(): x, y, z = symbols('x y z') f1 = sin(x) + cos(x) assert f1.rewrite(cos,exp) == exp(I*x)/2 + sin(x) + exp(-I*x)/2 assert f1.rewrite([cos],sin) == sin(x) + sin(x + pi/2, evaluate=False) f2 = sin(x) + cos(y)/gamma(z) assert f2.rewrite(sin,exp) == -I*(exp(I*x) - exp(-I*x))/2 + cos(y)/gamma(z)
def test_issue_3109(): from sympy import root, Rational I = S.ImaginaryUnit assert sqrt(33**(9*I/10)) == -33**(9*I/20) assert root((6*I)**(2*I), 3).as_base_exp()[1] == Rational(1, 3) # != 2*I/3 assert root((6*I)**(I/3), 3).as_base_exp()[1] == I/9 assert sqrt(exp(3*I)) == exp(3*I/2) assert sqrt(-sqrt(3)*(1 + 2*I)) == sqrt(sqrt(3))*sqrt(-1 - 2*I)
def eval(cls, n, m, theta, phi): n, m, theta, phi = [sympify(x) for x in (n, m, theta, phi)] # Handle negative index m and arguments theta, phi if m.could_extract_minus_sign(): m = -m return S.NegativeOne**m * exp(-2*I*m*phi) * Ynm(n, m, theta, phi) if theta.could_extract_minus_sign(): theta = -theta return Ynm(n, m, theta, phi) if phi.could_extract_minus_sign(): phi = -phi return exp(-2*I*m*phi) * Ynm(n, m, theta, phi)
def test_rewrite(): x, y, z = symbols('x y z') a, b = symbols('a b') f1 = sin(x) + cos(x) assert f1.rewrite(cos,exp) == exp(I*x)/2 + sin(x) + exp(-I*x)/2 assert f1.rewrite([cos],sin) == sin(x) + sin(x + pi/2, evaluate=False) f2 = sin(x) + cos(y)/gamma(z) assert f2.rewrite(sin,exp) == -I*(exp(I*x) - exp(-I*x))/2 + cos(y)/gamma(z) assert Max(a, b).rewrite(Piecewise) == Piecewise((a, a >= b), (b, True)) assert Max(x, y, z).rewrite(Piecewise) == Piecewise((x, (x >= y) & (x >= z)), (y, y >= z), (z, True)) assert Max(x, y, a, b).rewrite(Piecewise) == Piecewise((a, (a >= b) & (a >= x) & (a >= y)), (b, (b >= x) & (b >= y)), (x, x >= y), (y, True)) assert Min(a, b).rewrite(Piecewise) == Piecewise((a, a <= b), (b, True)) assert Min(x, y, z).rewrite(Piecewise) == Piecewise((x, (x <= y) & (x <= z)), (y, y <= z), (z, True)) assert Min(x, y, a, b).rewrite(Piecewise) == Piecewise((a, (a <= b) & (a <= x) & (a <= y)), (b, (b <= x) & (b <= y)), (x, x <= y), (y, True))
def test_custom_codegen(): from sympy.printing.ccode import C99CodePrinter from sympy.functions.elementary.exponential import exp printer = C99CodePrinter(settings={'user_functions': {'exp': 'fastexp'}}) gen = C99CodeGen(printer=printer) gen.preprocessor_statements.append('#include "fastexp.h"') x, y = symbols('x y') expr = exp(x + y) expected = ( '#include "expr.h"\n' '#include <math.h>\n' '#include "fastexp.h"\n' 'double expr(double x, double y) {\n' ' double expr_result;\n' ' expr_result = fastexp(x + y);\n' ' return expr_result;\n' '}\n' ) result = codegen(('expr', expr), header=False, empty=False, code_gen=gen) source = result[0][1] assert source == expected
def test_conditional(): X = Geometric('X', S(2)/3) Y = Poisson('Y', 3) assert P(X > 2, X > 3) == 1 assert P(X > 3, X > 2) == S(1)/3 assert P(Y > 2, Y < 2) == 0 assert P(Eq(Y, 3), Y >= 0) == 9*exp(-3)/2
def _eval_evalf(self, prec): from sympy import exp, pi, I z, period = self.args p = periodic_argument(z, period)._eval_evalf(prec) if abs(p) > pi or p == -pi: return self # Cannot evalf for this argument. return (abs(z)*exp(I*p))._eval_evalf(prec)
def _unpolarify(eq, exponents_only, pause=False): if not isinstance(eq, Basic) or eq.is_Atom: return eq if not pause: if isinstance(eq, exp_polar): return exp(_unpolarify(eq.exp, exponents_only)) if isinstance(eq, principal_branch) and eq.args[1] == 2*pi: return _unpolarify(eq.args[0], exponents_only) if ( eq.is_Add or eq.is_Mul or eq.is_Boolean or eq.is_Relational and ( eq.rel_op in ('==', '!=') and 0 in eq.args or eq.rel_op not in ('==', '!=')) ): return eq.func(*[_unpolarify(x, exponents_only) for x in eq.args]) if isinstance(eq, polar_lift): return _unpolarify(eq.args[0], exponents_only) if eq.is_Pow: expo = _unpolarify(eq.exp, exponents_only) base = _unpolarify(eq.base, exponents_only, not (expo.is_integer and not pause)) return base**expo if eq.is_Function and getattr(eq.func, 'unbranched', False): return eq.func(*[_unpolarify(x, exponents_only, exponents_only) for x in eq.args]) return eq.func(*[_unpolarify(x, exponents_only, True) for x in eq.args])
def test_twave(): A1, phi1, A2, phi2, f = symbols('A1, phi1, A2, phi2, f') n = Symbol('n') # Refractive index t = Symbol('t') # Time x = Symbol('x') # Spatial varaible k = Symbol('k') # Wave number E = Function('E') w1 = TWave(A1, f, phi1) w2 = TWave(A2, f, phi2) assert w1.amplitude == A1 assert w1.frequency == f assert w1.phase == phi1 assert w1.wavelength == c/(f*n) assert w1.time_period == 1/f w3 = w1 + w2 assert w3.amplitude == sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2) assert w3.frequency == f assert w3.wavelength == c/(f*n) assert w3.time_period == 1/f assert w3.angular_velocity == 2*pi*f assert w3.wavenumber == 2*pi*f*n/c assert simplify(w3.rewrite('sin') - sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2)*sin(pi*f*n*x*s/(149896229*m) - 2*pi*f*t + atan2(A1*cos(phi1) + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2)) + pi/2)) == 0 assert w3.rewrite('pde') == epsilon*mu*Derivative(E(x, t), t, t) + Derivative(E(x, t), x, x) assert w3.rewrite(cos) == sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2)*cos(pi*f*n*x*s/(149896229*m) - 2*pi*f*t + atan2(A1*cos(phi1) + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2))) assert w3.rewrite('exp') == sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2)*exp(I*(pi*f*n*x*s/(149896229*m) - 2*pi*f*t + atan2(A1*cos(phi1) + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2))))
def _lambert(eq, x): """ Given an expression assumed to be in the form ``F(X, a..f) = a*log(b*X + c) + d*X + f = 0`` where X = g(x) and x = g^-1(X), return the Lambert solution if possible: ``x = g^-1(-c/b + (a/d)*W(d/(a*b)*exp(c*d/a/b)*exp(-f/a)))``. """ eq = _mexpand(expand_log(eq)) mainlog = _mostfunc(eq, log, x) if not mainlog: return [] # violated assumptions other = eq.subs(mainlog, 0) if (-other).func is log: eq = (eq - other).subs(mainlog, mainlog.args[0]) mainlog = mainlog.args[0] if mainlog.func is not log: return [] # violated assumptions other = -(-other).args[0] eq += other if not x in other.free_symbols: return [] # violated assumptions d, f, X2 = _linab(other, x) logterm = collect(eq - other, mainlog) a = logterm.as_coefficient(mainlog) if a is None or x in a.free_symbols: return [] # violated assumptions logarg = mainlog.args[0] b, c, X1 = _linab(logarg, x) if X1 != X2: return [] # violated assumptions u = Dummy('rhs') sol = [] # check only real solutions: for k in [-1, 0]: l = LambertW(d/(a*b)*exp(c*d/a/b)*exp(-f/a), k) # if W's arg is between -1/e and 0 there is # a -1 branch real solution, too. if k and not l.is_real: continue rhs = -c/b + (a/d)*l solns = solve(X1 - u, x) for i, tmp in enumerate(solns): solns[i] = tmp.subs(u, rhs) sol.append(solns[i]) return sol
def fdiff(self, argindex=1): """ Returns the first derivative of this function. """ if argindex == 1: return exp(*self.args) else: raise ArgumentIndexError(self, argindex)
def eval(cls, a, x): # For lack of a better place, we use this one to extract branching # information. The following can be # found in the literature (c/f references given above), albeit scattered: # 1) For fixed x != 0, lowergamma(s, x) is an entire function of s # 2) For fixed positive integers s, lowergamma(s, x) is an entire # function of x. # 3) For fixed non-positive integers s, # lowergamma(s, exp(I*2*pi*n)*x) = # 2*pi*I*n*(-1)**(-s)/factorial(-s) + lowergamma(s, x) # (this follows from lowergamma(s, x).diff(x) = x**(s-1)*exp(-x)). # 4) For fixed non-integral s, # lowergamma(s, x) = x**s*gamma(s)*lowergamma_unbranched(s, x), # where lowergamma_unbranched(s, x) is an entire function (in fact # of both s and x), i.e. # lowergamma(s, exp(2*I*pi*n)*x) = exp(2*pi*I*n*a)*lowergamma(a, x) from sympy import unpolarify, I if x == 0: return S.Zero nx, n = x.extract_branch_factor() if a.is_integer and a.is_positive: nx = unpolarify(x) if nx != x: return lowergamma(a, nx) elif a.is_integer and a.is_nonpositive: if n != 0: return 2*pi*I*n*(-1)**(-a)/factorial(-a) + lowergamma(a, nx) elif n != 0: return exp(2*pi*I*n*a)*lowergamma(a, nx) # Special values. if a.is_Number: if a is S.One: return S.One - exp(-x) elif a is S.Half: return sqrt(pi)*erf(sqrt(x)) elif a.is_Integer or (2*a).is_Integer: b = a - 1 if b.is_positive: if a.is_integer: return factorial(b) - exp(-x) * factorial(b) * Add(*[x ** k / factorial(k) for k in range(a)]) else: return gamma(a) * (lowergamma(S.Half, x)/sqrt(pi) - exp(-x) * Add(*[x**(k-S.Half) / gamma(S.Half+k) for k in range(1, a+S.Half)])) if not a.is_Integer: return (-1)**(S.Half - a) * pi*erf(sqrt(x)) / gamma(1-a) + exp(-x) * Add(*[x**(k+a-1)*gamma(a) / gamma(a+k) for k in range(1, S(3)/2-a)])
def test_discrete_probability(): X = Geometric('X', S(1)/5) Y = Poisson('Y', 4) assert P(Eq(X, 3)) == S(16)/125 assert P(X < 3) == S(9)/25 assert P(X > 3) == S(64)/125 assert P(X >= 3) == S(16)/25 assert P(X <= 3) == S(61)/125 assert P(Ne(X, 3)) == S(109)/125 assert P(Eq(Y, 3)) == 32*exp(-4)/3 assert P(Y < 3) == 13*exp(-4) assert P(Y > 3).equals(32*(-S(71)/32 + 3*exp(4)/32)*exp(-4)/3) assert P(Y >= 3).equals(32*(-39/32 + 3*exp(4)/32)*exp(-4)/3) assert P(Y <= 3) == 71*exp(-4)/3 assert P(Ne(Y, 3)).equals( 13*exp(-4) + 32*(-71/32 + 3*exp(4)/32)*exp(-4)/3) assert P(X < S.Infinity) is S.One assert P(X > S.Infinity) is S.Zero
def eval(cls, a, x): # For lack of a better place, we use this one to extract branching # information. The following can be # found in the literature (c/f references given above), albeit scattered: # 1) For fixed x != 0, lowergamma(s, x) is an entire function of s # 2) For fixed positive integers s, lowergamma(s, x) is an entire # function of x. # 3) For fixed non-positive integers s, # lowergamma(s, exp(I*2*pi*n)*x) = # 2*pi*I*n*(-1)**(-s)/factorial(-s) + lowergamma(s, x) # (this follows from lowergamma(s, x).diff(x) = x**(s-1)*exp(-x)). # 4) For fixed non-integral s, # lowergamma(s, x) = x**s*gamma(s)*lowergamma_unbranched(s, x), # where lowergamma_unbranched(s, x) is an entire function (in fact # of both s and x), i.e. # lowergamma(s, exp(2*I*pi*n)*x) = exp(2*pi*I*n*a)*lowergamma(a, x) from sympy import unpolarify, I if x == 0: return S.Zero nx, n = x.extract_branch_factor() if a.is_integer and a.is_positive: nx = unpolarify(x) if nx != x: return lowergamma(a, nx) elif a.is_integer and a.is_nonpositive: if n != 0: return 2*pi*I*n*(-1)**(-a)/factorial(-a) + lowergamma(a, nx) elif n != 0: return exp(2*pi*I*n*a)*lowergamma(a, nx) # Special values. if a.is_Number: # TODO this should be non-recursive if a is S.One: return S.One - exp(-x) elif a is S.Half: return sqrt(pi)*erf(sqrt(x)) elif a.is_Integer or (2*a).is_Integer: b = a - 1 if b.is_positive: return b*cls(b, x) - x**b * exp(-x) if not a.is_Integer: return (cls(a + 1, x) + x**a * exp(-x))/a
def fdiff(self, argindex=2): from sympy import meijerg, unpolarify if argindex == 2: a, z = self.args return -exp(-unpolarify(z))*z**(a - 1) elif argindex == 1: a, z = self.args return uppergamma(a, z)*log(z) + meijerg([], [1, 1], [0, 0, a], [], z) else: raise ArgumentIndexError(self, argindex)
def test_expand(): m0 = OperationsOnlyMatrix([[x*(x + y), 2], [((x + y)*y)*x, x*(y + x*(x + y))]]) # Test if expand() returns a matrix m1 = m0.expand() assert m1 == Matrix( [[x*y + x**2, 2], [x*y**2 + y*x**2, x*y + y*x**2 + x**3]]) a = Symbol('a', real=True) assert OperationsOnlyMatrix(1, 1, [exp(I*a)]).expand(complex=True) == \ Matrix([cos(a) + I*sin(a)])
def eval(cls, a, z): from sympy import unpolarify, I, expint if z.is_Number: if z is S.NaN: return S.NaN elif z is S.Infinity: return S.Zero elif z is S.Zero: # TODO: Holds only for Re(a) > 0: return gamma(a) # We extract branching information here. C/f lowergamma. nx, n = z.extract_branch_factor() if a.is_integer and (a > 0) == True: nx = unpolarify(z) if z != nx: return uppergamma(a, nx) elif a.is_integer and (a <= 0) == True: if n != 0: return -2*pi*I*n*(-1)**(-a)/factorial(-a) + uppergamma(a, nx) elif n != 0: return gamma(a)*(1 - exp(2*pi*I*n*a)) + exp(2*pi*I*n*a)*uppergamma(a, nx) # Special values. if a.is_Number: # TODO this should be non-recursive if a is S.One: return exp(-z) elif a is S.Half: return sqrt(pi)*(1 - erf(sqrt(z))) # TODO could use erfc... elif a.is_Integer or (2*a).is_Integer: b = a - 1 if b.is_positive: return b*cls(b, z) + z**b * exp(-z) elif b.is_Integer: return expint(-b, z)*unpolarify(z)**(b + 1) if not a.is_Integer: return (cls(a + 1, z) - z**a * exp(-z))/a
def fdiff(self, argindex=4): if argindex == 1: # Diff wrt n raise ArgumentIndexError(self, argindex) elif argindex == 2: # Diff wrt m raise ArgumentIndexError(self, argindex) elif argindex == 3: # Diff wrt theta n, m, theta, phi = self.args return (m * cot(theta) * Ynm(n, m, theta, phi) + sqrt((n - m)*(n + m + 1)) * exp(-I*phi) * Ynm(n, m + 1, theta, phi)) elif argindex == 4: # Diff wrt phi n, m, theta, phi = self.args return I * m * Ynm(n, m, theta, phi) else: raise ArgumentIndexError(self, argindex)
def test_cf(dist, support_lower_limit, support_upper_limit): pdf = density(dist) t = S('t') x = S('x') # first function is the hardcoded CF of the distribution cf1 = lambdify([t], characteristic_function(dist)(t), 'mpmath') # second function is the Fourier transform of the density function f = lambdify([x, t], pdf(x)*exp(I*x*t), 'mpmath') cf2 = lambda t: mpmath.nsum(lambda x: f(x, t), [support_lower_limit, support_upper_limit], maxdegree=10) # compare the two functions at various points for test_point in [2, 5, 8, 11]: n1 = cf1(test_point) n2 = cf2(test_point) assert abs(re(n1) - re(n2)) < 1e-12 assert abs(im(n1) - im(n2)) < 1e-12
def _shift(func, s): z = func.args[-1] d = z.collect(x, evaluate=False) b = list(d)[0] a = d[b] if isinstance(a, exp_polar): a = exp(a.as_base_exp()[1]) z = a * b t = b.as_base_exp() b = t[1] if t[0] is x else S(0) r = s / b an = (i + r for i in func.args[0][0]) ap = (i + r for i in func.args[0][1]) bm = (i + r for i in func.args[1][0]) bq = (i + r for i in func.args[1][1]) return a**-r, meijerg((an, ap), (bm, bq), z)
def eval(cls, n, x): if not n.is_Number: # Symbolic result L_n(x) # L_{n}(-x) ---> exp(-x) * L_{-n-1}(x) # L_{-n}(x) ---> exp(x) * L_{n-1}(-x) if n.could_extract_minus_sign(): return exp(x) * laguerre(n - 1, -x) # We can evaluate for some special values of x if x == S.Zero: return S.One elif x == S.NegativeInfinity: return S.Infinity elif x == S.Infinity: return S.NegativeOne**n * S.Infinity else: # n is a given fixed integer, evaluate into polynomial if n.is_negative: raise ValueError( "The index n must be nonnegative integer (got %r)" % n) else: return laguerre_poly(n, x, 0)
def _create_table(table): """ Creates the look-up table. For a similar implementation see meijerint._create_lookup_table. """ def add(formula, annihilator, arg, x0=0, y0=[]): """ Adds a formula in the dictionary """ table.setdefault(_mytype(formula, x_1), []).append((formula, HolonomicFunction(annihilator, arg, x0, y0))) R = QQ.old_poly_ring(x_1) _, Dx = DifferentialOperators(R, 'Dx') from sympy import (sin, cos, exp, log, erf, sqrt, pi, sinh, cosh, sinc, erfc, Si, Ci, Shi, erfi) # add some basic functions add(sin(x_1), Dx**2 + 1, x_1, 0, [0, 1]) add(cos(x_1), Dx**2 + 1, x_1, 0, [1, 0]) add(exp(x_1), Dx - 1, x_1, 0, 1) add(log(x_1), Dx + x_1*Dx**2, x_1, 1, [0, 1]) add(erf(x_1), 2*x_1*Dx + Dx**2, x_1, 0, [0, 2/sqrt(pi)]) add(erfc(x_1), 2*x_1*Dx + Dx**2, x_1, 0, [1, -2/sqrt(pi)]) add(erfi(x_1), -2*x_1*Dx + Dx**2, x_1, 0, [0, 2/sqrt(pi)]) add(sinh(x_1), Dx**2 - 1, x_1, 0, [0, 1]) add(cosh(x_1), Dx**2 - 1, x_1, 0, [1, 0]) add(sinc(x_1), x_1 + 2*Dx + x_1*Dx**2, x_1) add(Si(x_1), x_1*Dx + 2*Dx**2 + x_1*Dx**3, x_1) add(Ci(x_1), x_1*Dx + 2*Dx**2 + x_1*Dx**3, x_1) add(Shi(x_1), -x_1*Dx + 2*Dx**2 + x_1*Dx**3, x_1)
def eval(cls, a, z): from sympy import unpolarify, I, expint if z.is_Number: if z is S.NaN: return S.NaN elif z is S.Infinity: return S.Zero elif z is S.Zero: # TODO: Holds only for Re(a) > 0: return gamma(a) # We extract branching information here. C/f lowergamma. nx, n = z.extract_branch_factor() if a.is_integer and (a > 0) == True: nx = unpolarify(z) if z != nx: return uppergamma(a, nx) elif a.is_integer and (a <= 0) == True: if n != 0: return -2*pi*I*n*(-1)**(-a)/factorial(-a) + uppergamma(a, nx) elif n != 0: return gamma(a)*(1 - exp(2*pi*I*n*a)) + exp(2*pi*I*n*a)*uppergamma(a, nx) # Special values. if a.is_Number: if a is S.One: return exp(-z) elif a is S.Half: return sqrt(pi)*erfc(sqrt(z)) elif a.is_Integer or (2*a).is_Integer: b = a - 1 if b.is_positive: if a.is_integer: return exp(-z) * factorial(b) * Add(*[z**k / factorial(k) for k in range(a)]) else: return gamma(a) * erfc(sqrt(z)) + (-1)**(a - S(3)/2) * exp(-z) * sqrt(z) * Add(*[gamma(-S.Half - k) * (-z)**k / gamma(1-a) for k in range(a - S.Half)]) elif b.is_Integer: return expint(-b, z)*unpolarify(z)**(b + 1) if not a.is_Integer: return (-1)**(S.Half - a) * pi*erfc(sqrt(z))/gamma(1-a) - z**a * exp(-z) * Add(*[z**k * gamma(a) / gamma(a+k+1) for k in range(S.Half - a)])
def test_DiscreteMarkovChain(): # pass only the name X = DiscreteMarkovChain("X") assert isinstance(X.state_space, Range) assert X.index_set == S.Naturals0 assert isinstance(X.transition_probabilities, MatrixSymbol) t = symbols('t', positive=True, integer=True) assert isinstance(X[t], RandomIndexedSymbol) assert E(X[0]) == Expectation(X[0]) raises(TypeError, lambda: DiscreteMarkovChain(1)) raises(NotImplementedError, lambda: X(t)) raises(NotImplementedError, lambda: X.communication_classes()) raises(NotImplementedError, lambda: X.canonical_form()) raises(NotImplementedError, lambda: X.decompose()) nz = Symbol('n', integer=True) TZ = MatrixSymbol('M', nz, nz) SZ = Range(nz) YZ = DiscreteMarkovChain('Y', SZ, TZ) assert P(Eq(YZ[2], 1), Eq(YZ[1], 0)) == TZ[0, 1] raises(ValueError, lambda: sample_stochastic_process(t)) raises(ValueError, lambda: next(sample_stochastic_process(X))) # pass name and state_space # any hashable object should be a valid state # states should be valid as a tuple/set/list/Tuple/Range sym, rainy, cloudy, sunny = symbols('a Rainy Cloudy Sunny', real=True) state_spaces = [(1, 2, 3), [Str('Hello'), sym, DiscreteMarkovChain("Y", (1, 2, 3))], Tuple(S(1), exp(sym), Str('World'), sympify=False), Range(-1, 5, 2), [rainy, cloudy, sunny]] chains = [ DiscreteMarkovChain("Y", state_space) for state_space in state_spaces ] for i, Y in enumerate(chains): assert isinstance(Y.transition_probabilities, MatrixSymbol) assert Y.state_space == state_spaces[i] or Y.state_space == FiniteSet( *state_spaces[i]) assert Y.number_of_states == 3 with ignore_warnings( UserWarning): # TODO: Restore tests once warnings are removed assert P(Eq(Y[2], 1), Eq(Y[0], 2), evaluate=False) == Probability(Eq(Y[2], 1), Eq(Y[0], 2)) assert E(Y[0]) == Expectation(Y[0]) raises(ValueError, lambda: next(sample_stochastic_process(Y))) raises(TypeError, lambda: DiscreteMarkovChain("Y", dict((1, 1)))) Y = DiscreteMarkovChain("Y", Range(1, t, 2)) assert Y.number_of_states == ceiling((t - 1) / 2) # pass name and transition_probabilities chains = [ DiscreteMarkovChain("Y", trans_probs=Matrix([[]])), DiscreteMarkovChain("Y", trans_probs=Matrix([[0, 1], [1, 0]])), DiscreteMarkovChain("Y", trans_probs=Matrix([[pi, 1 - pi], [sym, 1 - sym]])) ] for Z in chains: assert Z.number_of_states == Z.transition_probabilities.shape[0] assert isinstance(Z.transition_probabilities, ImmutableMatrix) # pass name, state_space and transition_probabilities T = Matrix([[0.5, 0.2, 0.3], [0.2, 0.5, 0.3], [0.2, 0.3, 0.5]]) TS = MatrixSymbol('T', 3, 3) Y = DiscreteMarkovChain("Y", [0, 1, 2], T) YS = DiscreteMarkovChain("Y", ['One', 'Two', 3], TS) assert Y.joint_distribution(1, Y[2], 3) == JointDistribution(Y[1], Y[2], Y[3]) raises(ValueError, lambda: Y.joint_distribution(Y[1].symbol, Y[2].symbol)) assert P(Eq(Y[3], 2), Eq(Y[1], 1)).round(2) == Float(0.36, 2) assert (P(Eq(YS[3], 2), Eq(YS[1], 1)) - (TS[0, 2] * TS[1, 0] + TS[1, 1] * TS[1, 2] + TS[1, 2] * TS[2, 2])).simplify() == 0 assert P(Eq(YS[1], 1), Eq(YS[2], 2)) == Probability(Eq(YS[1], 1)) assert P(Eq(YS[3], 3), Eq( YS[1], 1)) == TS[0, 2] * TS[1, 0] + TS[1, 1] * TS[1, 2] + TS[1, 2] * TS[2, 2] TO = Matrix([[0.25, 0.75, 0], [0, 0.25, 0.75], [0.75, 0, 0.25]]) assert P(Eq(Y[3], 2), Eq(Y[1], 1) & TransitionMatrixOf(Y, TO)).round(3) == Float( 0.375, 3) with ignore_warnings( UserWarning): ### TODO: Restore tests once warnings are removed assert E(Y[3], evaluate=False) == Expectation(Y[3]) assert E(Y[3], Eq(Y[2], 1)).round(2) == Float(1.1, 3) TSO = MatrixSymbol('T', 4, 4) raises( ValueError, lambda: str(P(Eq(YS[3], 2), Eq(YS[1], 1) & TransitionMatrixOf(YS, TSO)))) raises(TypeError, lambda: DiscreteMarkovChain("Z", [0, 1, 2], symbols('M'))) raises( ValueError, lambda: DiscreteMarkovChain("Z", [0, 1, 2], MatrixSymbol('T', 3, 4))) raises(ValueError, lambda: E(Y[3], Eq(Y[2], 6))) raises(ValueError, lambda: E(Y[2], Eq(Y[3], 1))) # extended tests for probability queries TO1 = Matrix([[Rational(1, 4), Rational(3, 4), 0], [Rational(1, 3), Rational(1, 3), Rational(1, 3)], [0, Rational(1, 4), Rational(3, 4)]]) assert P( And(Eq(Y[2], 1), Eq(Y[1], 1), Eq(Y[0], 0)), Eq(Probability(Eq(Y[0], 0)), Rational(1, 4)) & TransitionMatrixOf(Y, TO1)) == Rational(1, 16) assert P(And(Eq(Y[2], 1), Eq(Y[1], 1), Eq(Y[0], 0)), TransitionMatrixOf(Y, TO1)) == \ Probability(Eq(Y[0], 0))/4 assert P( Lt(X[1], 2) & Gt(X[1], 0), Eq(X[0], 2) & StochasticStateSpaceOf(X, [0, 1, 2]) & TransitionMatrixOf(X, TO1)) == Rational(1, 4) assert P( Lt(X[1], 2) & Gt(X[1], 0), Eq(X[0], 2) & StochasticStateSpaceOf(X, [S(0), '0', 1]) & TransitionMatrixOf(X, TO1)) == Rational(1, 4) assert P( Ne(X[1], 2) & Ne(X[1], 1), Eq(X[0], 2) & StochasticStateSpaceOf(X, [0, 1, 2]) & TransitionMatrixOf(X, TO1)) is S.Zero assert P( Ne(X[1], 2) & Ne(X[1], 1), Eq(X[0], 2) & StochasticStateSpaceOf(X, [S(0), '0', 1]) & TransitionMatrixOf(X, TO1)) is S.Zero assert P(And(Eq(Y[2], 1), Eq(Y[1], 1), Eq(Y[0], 0)), Eq(Y[1], 1)) == 0.1 * Probability(Eq(Y[0], 0)) # testing properties of Markov chain TO2 = Matrix([[S.One, 0, 0], [Rational(1, 3), Rational(1, 3), Rational(1, 3)], [0, Rational(1, 4), Rational(3, 4)]]) TO3 = Matrix([[Rational(1, 4), Rational(3, 4), 0], [Rational(1, 3), Rational(1, 3), Rational(1, 3)], [0, Rational(1, 4), Rational(3, 4)]]) Y2 = DiscreteMarkovChain('Y', trans_probs=TO2) Y3 = DiscreteMarkovChain('Y', trans_probs=TO3) assert Y3.fundamental_matrix() == ImmutableMatrix( [[176, 81, -132], [36, 141, -52], [-44, -39, 208]]) / 125 assert Y2.is_absorbing_chain() == True assert Y3.is_absorbing_chain() == False assert Y2.canonical_form() == ([0, 1, 2], TO2) assert Y3.canonical_form() == ([0, 1, 2], TO3) assert Y2.decompose() == ([0, 1, 2], TO2[0:1, 0:1], TO2[1:3, 0:1], TO2[1:3, 1:3]) assert Y3.decompose() == ([0, 1, 2], TO3, Matrix(0, 3, []), Matrix(0, 0, [])) TO4 = Matrix([[Rational(1, 5), Rational(2, 5), Rational(2, 5)], [Rational(1, 10), S.Half, Rational(2, 5)], [Rational(3, 5), Rational(3, 10), Rational(1, 10)]]) Y4 = DiscreteMarkovChain('Y', trans_probs=TO4) w = ImmutableMatrix([[Rational(11, 39), Rational(16, 39), Rational(4, 13)]]) assert Y4.limiting_distribution == w assert Y4.is_regular() == True assert Y4.is_ergodic() == True TS1 = MatrixSymbol('T', 3, 3) Y5 = DiscreteMarkovChain('Y', trans_probs=TS1) assert Y5.limiting_distribution(w, TO4).doit() == True assert Y5.stationary_distribution(condition_set=True).subs( TS1, TO4).contains(w).doit() == S.true TO6 = Matrix([[S.One, 0, 0, 0, 0], [S.Half, 0, S.Half, 0, 0], [0, S.Half, 0, S.Half, 0], [0, 0, S.Half, 0, S.Half], [0, 0, 0, 0, 1]]) Y6 = DiscreteMarkovChain('Y', trans_probs=TO6) assert Y6.fundamental_matrix() == ImmutableMatrix( [[Rational(3, 2), S.One, S.Half], [S.One, S(2), S.One], [S.Half, S.One, Rational(3, 2)]]) assert Y6.absorbing_probabilities() == ImmutableMatrix( [[Rational(3, 4), Rational(1, 4)], [S.Half, S.Half], [Rational(1, 4), Rational(3, 4)]]) TO7 = Matrix([[Rational(1, 2), Rational(1, 4), Rational(1, 4)], [Rational(1, 2), 0, Rational(1, 2)], [Rational(1, 4), Rational(1, 4), Rational(1, 2)]]) Y7 = DiscreteMarkovChain('Y', trans_probs=TO7) assert Y7.is_absorbing_chain() == False assert Y7.fundamental_matrix() == ImmutableMatrix( [[Rational(86, 75), Rational(1, 25), Rational(-14, 75)], [Rational(2, 25), Rational(21, 25), Rational(2, 25)], [Rational(-14, 75), Rational(1, 25), Rational(86, 75)]]) # test for zero-sized matrix functionality X = DiscreteMarkovChain('X', trans_probs=Matrix([[]])) assert X.number_of_states == 0 assert X.stationary_distribution() == Matrix([[]]) assert X.communication_classes() == [] assert X.canonical_form() == ([], Matrix([[]])) assert X.decompose() == ([], Matrix([[]]), Matrix([[]]), Matrix([[]])) assert X.is_regular() == False assert X.is_ergodic() == False # test communication_class # see https://drive.google.com/drive/folders/1HbxLlwwn2b3U8Lj7eb_ASIUb5vYaNIjg?usp=sharing # tutorial 2.pdf TO7 = Matrix([[0, 5, 5, 0, 0], [0, 0, 0, 10, 0], [5, 0, 5, 0, 0], [0, 10, 0, 0, 0], [0, 3, 0, 3, 4]]) / 10 Y7 = DiscreteMarkovChain('Y', trans_probs=TO7) tuples = Y7.communication_classes() classes, recurrence, periods = list(zip(*tuples)) assert classes == ([1, 3], [0, 2], [4]) assert recurrence == (True, False, False) assert periods == (2, 1, 1) TO8 = Matrix([[0, 0, 0, 10, 0, 0], [5, 0, 5, 0, 0, 0], [0, 4, 0, 0, 0, 6], [10, 0, 0, 0, 0, 0], [0, 10, 0, 0, 0, 0], [0, 0, 0, 5, 5, 0] ]) / 10 Y8 = DiscreteMarkovChain('Y', trans_probs=TO8) tuples = Y8.communication_classes() classes, recurrence, periods = list(zip(*tuples)) assert classes == ([0, 3], [1, 2, 5, 4]) assert recurrence == (True, False) assert periods == (2, 2) TO9 = Matrix( [[2, 0, 0, 3, 0, 0, 3, 2, 0, 0], [0, 10, 0, 0, 0, 0, 0, 0, 0, 0], [0, 2, 2, 0, 0, 0, 0, 0, 3, 3], [0, 0, 0, 3, 0, 0, 6, 1, 0, 0], [0, 0, 0, 0, 5, 5, 0, 0, 0, 0], [0, 0, 0, 0, 0, 10, 0, 0, 0, 0], [4, 0, 0, 5, 0, 0, 1, 0, 0, 0], [2, 0, 0, 4, 0, 0, 2, 2, 0, 0], [3, 0, 1, 0, 0, 0, 0, 0, 4, 2], [0, 0, 4, 0, 0, 0, 0, 0, 3, 3]]) / 10 Y9 = DiscreteMarkovChain('Y', trans_probs=TO9) tuples = Y9.communication_classes() classes, recurrence, periods = list(zip(*tuples)) assert classes == ([0, 3, 6, 7], [1], [2, 8, 9], [5], [4]) assert recurrence == (True, True, False, True, False) assert periods == (1, 1, 1, 1, 1) # test canonical form # see https://www.dartmouth.edu/~chance/teaching_aids/books_articles/probability_book/Chapter11.pdf # example 11.13 T = Matrix([[1, 0, 0, 0, 0], [S(1) / 2, 0, S(1) / 2, 0, 0], [0, S(1) / 2, 0, S(1) / 2, 0], [0, 0, S(1) / 2, 0, S(1) / 2], [0, 0, 0, 0, S(1)]]) DW = DiscreteMarkovChain('DW', [0, 1, 2, 3, 4], T) states, A, B, C = DW.decompose() assert states == [0, 4, 1, 2, 3] assert A == Matrix([[1, 0], [0, 1]]) assert B == Matrix([[S(1) / 2, 0], [0, 0], [0, S(1) / 2]]) assert C == Matrix([[0, S(1) / 2, 0], [S(1) / 2, 0, S(1) / 2], [0, S(1) / 2, 0]]) states, new_matrix = DW.canonical_form() assert states == [0, 4, 1, 2, 3] assert new_matrix == Matrix([[1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [S(1) / 2, 0, 0, S(1) / 2, 0], [0, 0, S(1) / 2, 0, S(1) / 2], [0, S(1) / 2, 0, S(1) / 2, 0]]) # test regular and ergodic # https://www.dartmouth.edu/~chance/teaching_aids/books_articles/probability_book/Chapter11.pdf T = Matrix([[0, 4, 0, 0, 0], [1, 0, 3, 0, 0], [0, 2, 0, 2, 0], [0, 0, 3, 0, 1], [0, 0, 0, 4, 0]]) / 4 X = DiscreteMarkovChain('X', trans_probs=T) assert not X.is_regular() assert X.is_ergodic() T = Matrix([[0, 1], [1, 0]]) X = DiscreteMarkovChain('X', trans_probs=T) assert not X.is_regular() assert X.is_ergodic() # http://www.math.wisc.edu/~valko/courses/331/MC2.pdf T = Matrix([[2, 1, 1], [2, 0, 2], [1, 1, 2]]) / 4 X = DiscreteMarkovChain('X', trans_probs=T) assert X.is_regular() assert X.is_ergodic() # https://docs.ufpr.br/~lucambio/CE222/1S2014/Kemeny-Snell1976.pdf T = Matrix([[1, 1], [1, 1]]) / 2 X = DiscreteMarkovChain('X', trans_probs=T) assert X.is_regular() assert X.is_ergodic() # test is_absorbing_chain T = Matrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) X = DiscreteMarkovChain('X', trans_probs=T) assert not X.is_absorbing_chain() # https://en.wikipedia.org/wiki/Absorbing_Markov_chain T = Matrix([[1, 1, 0, 0], [0, 1, 1, 0], [1, 0, 0, 1], [0, 0, 0, 2]]) / 2 X = DiscreteMarkovChain('X', trans_probs=T) assert X.is_absorbing_chain() T = Matrix([[2, 0, 0, 0, 0], [1, 0, 1, 0, 0], [0, 1, 0, 1, 0], [0, 0, 1, 0, 1], [0, 0, 0, 0, 2]]) / 2 X = DiscreteMarkovChain('X', trans_probs=T) assert X.is_absorbing_chain() # test custom state space Y10 = DiscreteMarkovChain('Y', [1, 2, 3], TO2) tuples = Y10.communication_classes() classes, recurrence, periods = list(zip(*tuples)) assert classes == ([1], [2, 3]) assert recurrence == (True, False) assert periods == (1, 1) assert Y10.canonical_form() == ([1, 2, 3], TO2) assert Y10.decompose() == ([1, 2, 3], TO2[0:1, 0:1], TO2[1:3, 0:1], TO2[1:3, 1:3]) # testing miscellaneous queries T = Matrix([[S.Half, Rational(1, 4), Rational(1, 4)], [Rational(1, 3), 0, Rational(2, 3)], [S.Half, S.Half, 0]]) X = DiscreteMarkovChain('X', [0, 1, 2], T) assert P( Eq(X[1], 2) & Eq(X[2], 1) & Eq(X[3], 0), Eq(P(Eq(X[1], 0)), Rational(1, 4)) & Eq(P(Eq(X[1], 1)), Rational(1, 4))) == Rational(1, 12) assert P(Eq(X[2], 1) | Eq(X[2], 2), Eq(X[1], 1)) == Rational(2, 3) assert P(Eq(X[2], 1) & Eq(X[2], 2), Eq(X[1], 1)) is S.Zero assert P(Ne(X[2], 2), Eq(X[1], 1)) == Rational(1, 3) assert E(X[1]**2, Eq(X[0], 1)) == Rational(8, 3) assert variance(X[1], Eq(X[0], 1)) == Rational(8, 9) raises(ValueError, lambda: E(X[1], Eq(X[2], 1))) raises(ValueError, lambda: DiscreteMarkovChain('X', [0, 1], T)) # testing miscellaneous queries with different state space X = DiscreteMarkovChain('X', ['A', 'B', 'C'], T) assert P( Eq(X[1], 2) & Eq(X[2], 1) & Eq(X[3], 0), Eq(P(Eq(X[1], 0)), Rational(1, 4)) & Eq(P(Eq(X[1], 1)), Rational(1, 4))) == Rational(1, 12) assert P(Eq(X[2], 1) | Eq(X[2], 2), Eq(X[1], 1)) == Rational(2, 3) assert P(Eq(X[2], 1) & Eq(X[2], 2), Eq(X[1], 1)) is S.Zero assert P(Ne(X[2], 2), Eq(X[1], 1)) == Rational(1, 3) a = X.state_space.args[0] c = X.state_space.args[2] assert (E(X[1]**2, Eq(X[0], 1)) - (a**2 / 3 + 2 * c**2 / 3)).simplify() == 0 assert (variance(X[1], Eq(X[0], 1)) - (2 * (-a / 3 + c / 3)**2 / 3 + (2 * a / 3 - 2 * c / 3)**2 / 3)).simplify() == 0 raises(ValueError, lambda: E(X[1], Eq(X[2], 1))) #testing queries with multiple RandomIndexedSymbols T = Matrix([[Rational(5, 10), Rational(3, 10), Rational(2, 10)], [Rational(2, 10), Rational(7, 10), Rational(1, 10)], [Rational(3, 10), Rational(3, 10), Rational(4, 10)]]) Y = DiscreteMarkovChain("Y", [0, 1, 2], T) assert P(Eq(Y[7], Y[5]), Eq(Y[2], 0)).round(5) == Float(0.44428, 5) assert P(Gt(Y[3], Y[1]), Eq(Y[0], 0)).round(2) == Float(0.36, 2) assert P(Le(Y[5], Y[10]), Eq(Y[4], 2)).round(6) == Float(0.583120, 6) assert Float(P(Eq(Y[10], Y[5]), Eq(Y[4], 1)), 14) == Float(1 - P(Ne(Y[10], Y[5]), Eq(Y[4], 1)), 14) assert Float(P(Gt(Y[8], Y[9]), Eq(Y[3], 2)), 14) == Float(1 - P(Le(Y[8], Y[9]), Eq(Y[3], 2)), 14) assert Float(P(Lt(Y[1], Y[4]), Eq(Y[0], 0)), 14) == Float(1 - P(Ge(Y[1], Y[4]), Eq(Y[0], 0)), 14) assert P(Eq(Y[5], Y[10]), Eq(Y[2], 1)) == P(Eq(Y[10], Y[5]), Eq(Y[2], 1)) assert P(Gt(Y[1], Y[2]), Eq(Y[0], 1)) == P(Lt(Y[2], Y[1]), Eq(Y[0], 1)) assert P(Ge(Y[7], Y[6]), Eq(Y[4], 1)) == P(Le(Y[6], Y[7]), Eq(Y[4], 1)) #test symbolic queries a, b, c, d = symbols('a b c d') T = Matrix([[Rational(1, 10), Rational(4, 10), Rational(5, 10)], [Rational(3, 10), Rational(4, 10), Rational(3, 10)], [Rational(7, 10), Rational(2, 10), Rational(1, 10)]]) Y = DiscreteMarkovChain("Y", [0, 1, 2], T) query = P(Eq(Y[a], b), Eq(Y[c], d)) assert query.subs({ a: 10, b: 2, c: 5, d: 1 }).evalf().round(4) == P(Eq(Y[10], 2), Eq(Y[5], 1)).round(4) assert query.subs({ a: 15, b: 0, c: 10, d: 1 }).evalf().round(4) == P(Eq(Y[15], 0), Eq(Y[10], 1)).round(4) query_gt = P(Gt(Y[a], b), Eq(Y[c], d)) query_le = P(Le(Y[a], b), Eq(Y[c], d)) assert query_gt.subs({ a: 5, b: 2, c: 1, d: 0 }).evalf() + query_le.subs({ a: 5, b: 2, c: 1, d: 0 }).evalf() == 1 query_ge = P(Ge(Y[a], b), Eq(Y[c], d)) query_lt = P(Lt(Y[a], b), Eq(Y[c], d)) assert query_ge.subs({ a: 4, b: 1, c: 0, d: 2 }).evalf() + query_lt.subs({ a: 4, b: 1, c: 0, d: 2 }).evalf() == 1 #test issue 20078 assert (2 * Y[1] + 3 * Y[1]).simplify() == 5 * Y[1] assert (2 * Y[1] - 3 * Y[1]).simplify() == -Y[1] assert (2 * (0.25 * Y[1])).simplify() == 0.5 * Y[1] assert ((2 * Y[1]) * (0.25 * Y[1])).simplify() == 0.5 * Y[1]**2 assert (Y[1]**2 + Y[1]**3).simplify() == (Y[1] + 1) * Y[1]**2
def _eval_rewrite_as_exp(self, arg, **kwargs): return (exp(arg) + exp(-arg)) / 2
def _eval_rewrite_as_exp(self, arg): return exp(arg) - S.One
def test_issue_3109_fail(): from sympy import root, Rational I = S.ImaginaryUnit assert sqrt(exp(5*I)) == -exp(5*I/2) assert root(exp(5*I), 3).exp == Rational(1, 3)
def test_evalf_bugs(): assert NS(sin(1) + exp(-10**10), 10) == NS(sin(1), 10) assert NS(exp(10**10) + sin(1), 10) == NS(exp(10**10), 10) assert NS('expand_log(log(1+1/10**50))', 20) == '1.0000000000000000000e-50' assert NS('log(10**100,10)', 10) == '100.0000000' assert NS('log(2)', 10) == '0.6931471806' assert NS('(sin(x)-x)/x**3', 15, subs={x: '1/10**50'}) == '-0.166666666666667' assert NS(sin(1) + Rational(1, 10**100) * I, 15) == '0.841470984807897 + 1.00000000000000e-100*I' assert x.evalf() == x assert NS((1 + I)**2 * I, 6) == '-2.00000' d = { n: (-1)**Rational(6, 7), y: (-1)**Rational(4, 7), x: (-1)**Rational(2, 7) } assert NS((x * (1 + y * (1 + n))).subs(d).evalf(), 6) == '0.346011 + 0.433884*I' assert NS(((-I - sqrt(2) * I)**2).evalf()) == '-5.82842712474619' assert NS((1 + I)**2 * I, 15) == '-2.00000000000000' # issue 4758 (1/2): assert NS(pi.evalf(69) - pi) == '-4.43863937855894e-71' # issue 4758 (2/2): With the bug present, this still only fails if the # terms are in the order given here. This is not generally the case, # because the order depends on the hashes of the terms. assert NS(20 - 5008329267844 * n**25 - 477638700 * n**37 - 19 * n, subs={n: .01}) == '19.8100000000000' assert NS( ((x - 1) * (1 - x)** 1000).n()) == '(1.00000000000000 - x)**1000*(x - 1.00000000000000)' assert NS((-x).n()) == '-x' assert NS((-2 * x).n()) == '-2.00000000000000*x' assert NS((-2 * x * y).n()) == '-2.00000000000000*x*y' assert cos(x).n(subs={x: 1 + I}) == cos(x).subs(x, 1 + I).n() # issue 6660. Also NaN != mpmath.nan # In this order: # 0*nan, 0/nan, 0*inf, 0/inf # 0+nan, 0-nan, 0+inf, 0-inf # >>> n = Some Number # n*nan, n/nan, n*inf, n/inf # n+nan, n-nan, n+inf, n-inf assert (0 * E**(oo)).n() is S.NaN assert (0 / E**(oo)).n() is S.Zero assert (0 + E**(oo)).n() is S.Infinity assert (0 - E**(oo)).n() is S.NegativeInfinity assert (5 * E**(oo)).n() is S.Infinity assert (5 / E**(oo)).n() is S.Zero assert (5 + E**(oo)).n() is S.Infinity assert (5 - E**(oo)).n() is S.NegativeInfinity #issue 7416 assert as_mpmath(0.0, 10, {'chop': True}) == 0 #issue 5412 assert ((oo * I).n() == S.Infinity * I) assert ((oo + oo * I).n() == S.Infinity + S.Infinity * I) #issue 11518 assert NS(2 * x**2.5, 5) == '2.0000*x**2.5000' #issue 13076 assert NS(Mul(Max(0, y), x, evaluate=False).evalf()) == 'x*Max(0, y)' #issue 18516 assert NS( log( S(3273390607896141870013189696827599152216642046043064789483291368096133796404674554883270092325904157150886684127560071009217256545885393053328527589376 ) / 36360291795869936842385267079543319118023385026001623040346035832580600191583895484198508262979388783308179702534403855752855931517013066142992430916562025780021771247847643450125342836565813209972590371590152578728008385990139795377610001 ).evalf(15, chop=True)) == '-oo'
def test_issue_21938(): expr = sin(1 / x + exp(-x)) - sin(1 / x) assert expr.series(x, oo) == (1 / (24 * x**4) - 1 / (2 * x**2) + 1 + O(x**(-6), (x, oo))) * exp(-x)
def test_exponential_domain(): K = ZZ[E] eK = K.from_sympy(E) assert K.from_sympy(exp(3)) == eK**3 assert K.convert(exp(3)) == eK**3
def _characteristic_function(self, t): p = self.p return p * exp(I * t) / (1 - (1 - p) * exp(I * t))
def guess_generating_function(v, X=Symbol('x'), types=['all'], maxsqrtn=2): """ Tries to "guess" a generating function for a sequence of rational numbers v. Only a few patterns are implemented yet. Explanation =========== The function returns a dictionary where keys are the name of a given type of generating function. Six types are currently implemented: type | formal definition -------+---------------------------------------------------------------- ogf | f(x) = Sum( a_k * x^k , k: 0..infinity ) egf | f(x) = Sum( a_k * x^k / k! , k: 0..infinity ) lgf | f(x) = Sum( (-1)^(k+1) a_k * x^k / k , k: 1..infinity ) | (with initial index being hold as 1 rather than 0) hlgf | f(x) = Sum( a_k * x^k / k , k: 1..infinity ) | (with initial index being hold as 1 rather than 0) lgdogf | f(x) = derivate( log(Sum( a_k * x^k, k: 0..infinity )), x) lgdegf | f(x) = derivate( log(Sum( a_k * x^k / k!, k: 0..infinity )), x) In order to spare time, the user can select only some types of generating functions (default being ['all']). While forgetting to use a list in the case of a single type may seem to work most of the time as in: types='ogf' this (convenient) syntax may lead to unexpected extra results in some cases. Discarding a type when calling the function does not mean that the type will not be present in the returned dictionary; it only means that no extra computation will be performed for that type, but the function may still add it in the result when it can be easily converted from another type. Two generating functions (lgdogf and lgdegf) are not even computed if the initial term of the sequence is 0; it may be useful in that case to try again after having removed the leading zeros. Examples ======== >>> from sympy.concrete.guess import guess_generating_function as ggf >>> ggf([k+1 for k in range(12)], types=['ogf', 'lgf', 'hlgf']) {'hlgf': 1/(1 - x), 'lgf': 1/(x + 1), 'ogf': 1/(x**2 - 2*x + 1)} >>> from sympy import sympify >>> l = sympify("[3/2, 11/2, 0, -121/2, -363/2, 121]") >>> ggf(l) {'ogf': (x + 3/2)/(11*x**2 - 3*x + 1)} >>> from sympy import fibonacci >>> ggf([fibonacci(k) for k in range(5, 15)], types=['ogf']) {'ogf': (3*x + 5)/(-x**2 - x + 1)} >>> from sympy import factorial >>> ggf([factorial(k) for k in range(12)], types=['ogf', 'egf', 'lgf']) {'egf': 1/(1 - x)} >>> ggf([k+1 for k in range(12)], types=['egf']) {'egf': (x + 1)*exp(x), 'lgdegf': (x + 2)/(x + 1)} N-th root of a rational function can also be detected (below is an example coming from the sequence A108626 from http://oeis.org). The greatest n-th root to be tested is specified as maxsqrtn (default 2). >>> ggf([1, 2, 5, 14, 41, 124, 383, 1200, 3799, 12122, 38919])['ogf'] sqrt(1/(x**4 + 2*x**2 - 4*x + 1)) References ========== .. [1] "Concrete Mathematics", R.L. Graham, D.E. Knuth, O. Patashnik .. [2] https://oeis.org/wiki/Generating_functions """ # List of all types of all g.f. known by the algorithm if 'all' in types: types = ['ogf', 'egf', 'lgf', 'hlgf', 'lgdogf', 'lgdegf'] result = {} # Ordinary Generating Function (ogf) if 'ogf' in types: # Perform some convolutions of the sequence with itself t = [1 if k == 0 else 0 for k in range(len(v))] for d in range(max(1, maxsqrtn)): t = [ sum(t[n - i] * v[i] for i in range(n + 1)) for n in range(len(v)) ] g = guess_generating_function_rational(t, X=X) if g: result['ogf'] = g**Rational(1, d + 1) break # Exponential Generating Function (egf) if 'egf' in types: # Transform sequence (division by factorial) w, f = [], S.One for i, k in enumerate(v): f *= i if i else 1 w.append(k / f) # Perform some convolutions of the sequence with itself t = [1 if k == 0 else 0 for k in range(len(w))] for d in range(max(1, maxsqrtn)): t = [ sum(t[n - i] * w[i] for i in range(n + 1)) for n in range(len(w)) ] g = guess_generating_function_rational(t, X=X) if g: result['egf'] = g**Rational(1, d + 1) break # Logarithmic Generating Function (lgf) if 'lgf' in types: # Transform sequence (multiplication by (-1)^(n+1) / n) w, f = [], S.NegativeOne for i, k in enumerate(v): f = -f w.append(f * k / Integer(i + 1)) # Perform some convolutions of the sequence with itself t = [1 if k == 0 else 0 for k in range(len(w))] for d in range(max(1, maxsqrtn)): t = [ sum(t[n - i] * w[i] for i in range(n + 1)) for n in range(len(w)) ] g = guess_generating_function_rational(t, X=X) if g: result['lgf'] = g**Rational(1, d + 1) break # Hyperbolic logarithmic Generating Function (hlgf) if 'hlgf' in types: # Transform sequence (division by n+1) w = [] for i, k in enumerate(v): w.append(k / Integer(i + 1)) # Perform some convolutions of the sequence with itself t = [1 if k == 0 else 0 for k in range(len(w))] for d in range(max(1, maxsqrtn)): t = [ sum(t[n - i] * w[i] for i in range(n + 1)) for n in range(len(w)) ] g = guess_generating_function_rational(t, X=X) if g: result['hlgf'] = g**Rational(1, d + 1) break # Logarithmic derivative of ordinary generating Function (lgdogf) if v[0] != 0 and ('lgdogf' in types or ('ogf' in types and 'ogf' not in result)): # Transform sequence by computing f'(x)/f(x) # because log(f(x)) = integrate( f'(x)/f(x) ) a, w = sympify(v[0]), [] for n in range(len(v) - 1): w.append((v[n + 1] * (n + 1) - sum(w[-i - 1] * v[i + 1] for i in range(n))) / a) # Perform some convolutions of the sequence with itself t = [1 if k == 0 else 0 for k in range(len(w))] for d in range(max(1, maxsqrtn)): t = [ sum(t[n - i] * w[i] for i in range(n + 1)) for n in range(len(w)) ] g = guess_generating_function_rational(t, X=X) if g: result['lgdogf'] = g**Rational(1, d + 1) if 'ogf' not in result: result['ogf'] = exp(integrate(result['lgdogf'], X)) break # Logarithmic derivative of exponential generating Function (lgdegf) if v[0] != 0 and ('lgdegf' in types or ('egf' in types and 'egf' not in result)): # Transform sequence / step 1 (division by factorial) z, f = [], Integer(1) for i, k in enumerate(v): f *= i if i else 1 z.append(k / f) # Transform sequence / step 2 by computing f'(x)/f(x) # because log(f(x)) = integrate( f'(x)/f(x) ) a, w = z[0], [] for n in range(len(z) - 1): w.append((z[n + 1] * (n + 1) - sum(w[-i - 1] * z[i + 1] for i in range(n))) / a) # Perform some convolutions of the sequence with itself t = [1 if k == 0 else 0 for k in range(len(w))] for d in range(max(1, maxsqrtn)): t = [ sum(t[n - i] * w[i] for i in range(n + 1)) for n in range(len(w)) ] g = guess_generating_function_rational(t, X=X) if g: result['lgdegf'] = g**Rational(1, d + 1) if 'egf' not in result: result['egf'] = exp(integrate(result['lgdegf'], X)) break return result
def test_power_rewrite_exp(): assert (I**I).rewrite(exp) == exp(-pi / 2) expr = (2 + 3 * I)**(4 + 5 * I) assert expr.rewrite(exp) == exp( (4 + 5 * I) * (log(sqrt(13)) + I * atan(Rational(3, 2)))) assert expr.rewrite(exp).expand() == \ 169*exp(5*I*log(13)/2)*exp(4*I*atan(Rational(3, 2)))*exp(-5*atan(Rational(3, 2))) assert ((6 + 7 * I)**5).rewrite(exp) == 7225 * sqrt(85) * exp( 5 * I * atan(Rational(7, 6))) expr = 5**(6 + 7 * I) assert expr.rewrite(exp) == exp((6 + 7 * I) * log(5)) assert expr.rewrite(exp).expand() == 15625 * exp(7 * I * log(5)) assert Pow(123, 789, evaluate=False).rewrite(exp) == 123**789 assert (1**I).rewrite(exp) == 1**I assert (0**I).rewrite(exp) == 0**I expr = (-2)**(2 + 5 * I) assert expr.rewrite(exp) == exp((2 + 5 * I) * (log(2) + I * pi)) assert expr.rewrite(exp).expand() == 4 * exp(-5 * pi) * exp(5 * I * log(2)) assert ((-2)**S(-5)).rewrite(exp) == (-2)**S(-5) x, y = symbols('x y') assert (x**y).rewrite(exp) == exp(y * log(x)) assert (7**x).rewrite(exp) == exp(x * log(7), evaluate=False) assert ((2 + 3 * I)**x).rewrite(exp) == exp( x * (log(sqrt(13)) + I * atan(Rational(3, 2)))) assert (y**(5 + 6 * I)).rewrite(exp) == exp(log(y) * (5 + 6 * I)) assert all((1 / func(x)).rewrite(exp) == 1 / (func(x).rewrite(exp)) for func in (sin, cos, tan, sec, csc, sinh, cosh, tanh))
def _eval_rewrite_as_exp(self, arg, **kwargs): neg_exp, pos_exp = exp(-arg), exp(arg) return (pos_exp + neg_exp)/(pos_exp - neg_exp)
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs): neg_exp, pos_exp = exp(-arg), exp(arg) return (pos_exp + neg_exp)/(pos_exp - neg_exp)
def test_exp(): e1 = exp(x).series(x, 0) e2 = series(exp(x), x, 0) assert e1 == e2
def eval(cls, a, x): # For lack of a better place, we use this one to extract branching # information. The following can be # found in the literature (c/f references given above), albeit scattered: # 1) For fixed x != 0, lowergamma(s, x) is an entire function of s # 2) For fixed positive integers s, lowergamma(s, x) is an entire # function of x. # 3) For fixed non-positive integers s, # lowergamma(s, exp(I*2*pi*n)*x) = # 2*pi*I*n*(-1)**(-s)/factorial(-s) + lowergamma(s, x) # (this follows from lowergamma(s, x).diff(x) = x**(s-1)*exp(-x)). # 4) For fixed non-integral s, # lowergamma(s, x) = x**s*gamma(s)*lowergamma_unbranched(s, x), # where lowergamma_unbranched(s, x) is an entire function (in fact # of both s and x), i.e. # lowergamma(s, exp(2*I*pi*n)*x) = exp(2*pi*I*n*a)*lowergamma(a, x) from sympy import unpolarify, I if x is S.Zero: return S.Zero nx, n = x.extract_branch_factor() if a.is_integer and a.is_positive: nx = unpolarify(x) if nx != x: return lowergamma(a, nx) elif a.is_integer and a.is_nonpositive: if n != 0: return 2 * pi * I * n * (-1) ** (-a) / factorial(-a) + lowergamma(a, nx) elif n != 0: return exp(2 * pi * I * n * a) * lowergamma(a, nx) # Special values. if a.is_Number: if a is S.One: return S.One - exp(-x) elif a is S.Half: return sqrt(pi) * erf(sqrt(x)) elif a.is_Integer or (2 * a).is_Integer: b = a - 1 if b.is_positive: if a.is_integer: return factorial(b) - exp(-x) * factorial(b) * Add( *[x ** k / factorial(k) for k in range(a)] ) else: return gamma(a) * ( lowergamma(S.Half, x) / sqrt(pi) - exp(-x) * Add( *[ x ** (k - S.Half) / gamma(S.Half + k) for k in range(1, a + S.Half) ] ) ) if not a.is_Integer: return (-1) ** (S.Half - a) * pi * erf(sqrt(x)) / gamma( 1 - a ) + exp(-x) * Add( *[ x ** (k + a - 1) * gamma(a) / gamma(a + k) for k in range(1, Rational(3, 2) - a) ] ) if x.is_zero: return S.Zero
def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs): return (exp(arg) + exp(-arg)) / 2
def eval(cls, a, z): from sympy import unpolarify, I, expint if z.is_Number: if z is S.NaN: return S.NaN elif z is S.Infinity: return S.Zero elif z.is_zero: if re(a).is_positive: return gamma(a) # We extract branching information here. C/f lowergamma. nx, n = z.extract_branch_factor() if a.is_integer and a.is_positive: nx = unpolarify(z) if z != nx: return uppergamma(a, nx) elif a.is_integer and a.is_nonpositive: if n != 0: return -2 * pi * I * n * (-1) ** (-a) / factorial(-a) + uppergamma( a, nx ) elif n != 0: return gamma(a) * (1 - exp(2 * pi * I * n * a)) + exp( 2 * pi * I * n * a ) * uppergamma(a, nx) # Special values. if a.is_Number: if a is S.Zero and z.is_positive: return -Ei(-z) elif a is S.One: return exp(-z) elif a is S.Half: return sqrt(pi) * erfc(sqrt(z)) elif a.is_Integer or (2 * a).is_Integer: b = a - 1 if b.is_positive: if a.is_integer: return ( exp(-z) * factorial(b) * Add(*[z ** k / factorial(k) for k in range(a)]) ) else: return gamma(a) * erfc(sqrt(z)) + (-1) ** (a - S(3) / 2) * exp( -z ) * sqrt(z) * Add( *[ gamma(-S.Half - k) * (-z) ** k / gamma(1 - a) for k in range(a - S.Half) ] ) elif b.is_Integer: return expint(-b, z) * unpolarify(z) ** (b + 1) if not a.is_Integer: return (-1) ** (S.Half - a) * pi * erfc(sqrt(z)) / gamma( 1 - a ) - z ** a * exp(-z) * Add( *[ z ** k * gamma(a) / gamma(a + k + 1) for k in range(S.Half - a) ] ) if a.is_zero and z.is_positive: return -Ei(-z) if z.is_zero and re(a).is_positive: return gamma(a)
def _moment_generating_function(self, t): p = self.p return p * exp(t) / (1 - (1 - p) * exp(t))
def _expm1(x): return exp(x) - S.One
def _eval_rewrite_as_Sum(self, *args): from sympy.concrete.summations import Sum return exp(Sum(log(self.function), *self.limits))
def _eval_rewrite_as_exp(self, arg, **kwargs): return exp(arg) - S.One
def test_issue_20551(): expr = (exp(x) / x).series(x, n=None) terms = [next(expr) for i in range(3)] assert terms == [1 / x, 1, x / 2]
def _eval_evalf(self, prec): z, period = self.args p = periodic_argument(z, period)._eval_evalf(prec) if abs(p) > pi or p == -pi: return self # Cannot evalf for this argument. return (abs(z) * exp(I * p))._eval_evalf(prec)
def test_exp2(): e1 = exp(cos(x)).series(x, 0) e2 = series(exp(cos(x)), x, 0) assert e1 == e2
def eval(cls, arg): from sympy.simplify.simplify import signsimp from sympy.core.function import expand_mul from sympy.core.power import Pow if hasattr(arg, '_eval_Abs'): obj = arg._eval_Abs() if obj is not None: return obj if not isinstance(arg, Expr): raise TypeError("Bad argument type for Abs(): %s" % type(arg)) # handle what we can arg = signsimp(arg, evaluate=False) n, d = arg.as_numer_denom() if d.free_symbols and not n.free_symbols: return cls(n) / cls(d) if arg.is_Mul: known = [] unk = [] for t in arg.args: if t.is_Pow and t.exp.is_integer and t.exp.is_negative: bnew = cls(t.base) if isinstance(bnew, cls): unk.append(t) else: known.append(Pow(bnew, t.exp)) else: tnew = cls(t) if isinstance(tnew, cls): unk.append(t) else: known.append(tnew) known = Mul(*known) unk = cls(Mul(*unk), evaluate=False) if unk else S.One return known * unk if arg is S.NaN: return S.NaN if arg is S.ComplexInfinity: return S.Infinity if arg.is_Pow: base, exponent = arg.as_base_exp() if base.is_extended_real: if exponent.is_integer: if exponent.is_even: return arg if base is S.NegativeOne: return S.One return Abs(base)**exponent if base.is_extended_nonnegative: return base**re(exponent) if base.is_extended_negative: return (-base)**re(exponent) * exp(-S.Pi * im(exponent)) return elif not base.has(Symbol): # complex base # express base**exponent as exp(exponent*log(base)) a, b = log(base).as_real_imag() z = a + I * b return exp(re(exponent * z)) if isinstance(arg, exp): return exp(re(arg.args[0])) if isinstance(arg, AppliedUndef): if arg.is_positive: return arg elif arg.is_negative: return -arg return if arg.is_Add and arg.has(S.Infinity, S.NegativeInfinity): if any(a.is_infinite for a in arg.as_real_imag()): return S.Infinity if arg.is_zero: return S.Zero if arg.is_extended_nonnegative: return arg if arg.is_extended_nonpositive: return -arg if arg.is_imaginary: arg2 = -S.ImaginaryUnit * arg if arg2.is_extended_nonnegative: return arg2 if arg.is_extended_real: return # reject result if all new conjugates are just wrappers around # an expression that was already in the arg conj = signsimp(arg.conjugate(), evaluate=False) new_conj = conj.atoms(conjugate) - arg.atoms(conjugate) if new_conj and all(arg.has(i.args[0]) for i in new_conj): return if arg != conj and arg != -conj: ignore = arg.atoms(Abs) abs_free_arg = arg.xreplace({i: Dummy(real=True) for i in ignore}) unk = [ a for a in abs_free_arg.free_symbols if a.is_extended_real is None ] if not unk or not all(conj.has(conjugate(u)) for u in unk): return sqrt(expand_mul(arg * conj))
def _eval_rewrite_as_Sum(self, *args, **kwargs): from sympy.concrete.summations import Sum return exp(Sum(log(self.function), *self.limits))
def eval(cls, arg): from sympy.simplify.simplify import signsimp from sympy.core.function import expand_mul if hasattr(arg, '_eval_Abs'): obj = arg._eval_Abs() if obj is not None: return obj if not isinstance(arg, Expr): raise TypeError("Bad argument type for Abs(): %s" % type(arg)) # handle what we can arg = signsimp(arg, evaluate=False) if arg.is_Mul: known = [] unk = [] for t in arg.args: tnew = cls(t) if isinstance(tnew, cls): unk.append(tnew.args[0]) else: known.append(tnew) known = Mul(*known) unk = cls(Mul(*unk), evaluate=False) if unk else S.One return known*unk if arg is S.NaN: return S.NaN if arg is S.ComplexInfinity: return S.Infinity if arg.is_Pow: base, exponent = arg.as_base_exp() if base.is_real: if exponent.is_integer: if exponent.is_even: return arg if base is S.NegativeOne: return S.One if isinstance(base, cls) and exponent is S.NegativeOne: return arg return Abs(base)**exponent if base.is_nonnegative: return base**re(exponent) if base.is_negative: return (-base)**re(exponent)*exp(-S.Pi*im(exponent)) return elif not base.has(Symbol): # complex base # express base**exponent as exp(exponent*log(base)) a, b = log(base).as_real_imag() z = a + I*b return exp(re(exponent*z)) if isinstance(arg, exp): return exp(re(arg.args[0])) if isinstance(arg, AppliedUndef): return if arg.is_Add and arg.has(S.Infinity, S.NegativeInfinity): if any(a.is_infinite for a in arg.as_real_imag()): return S.Infinity if arg.is_zero: return S.Zero if arg.is_nonnegative: return arg if arg.is_nonpositive: return -arg if arg.is_imaginary: arg2 = -S.ImaginaryUnit * arg if arg2.is_nonnegative: return arg2 # reject result if all new conjugates are just wrappers around # an expression that was already in the arg conj = signsimp(arg.conjugate(), evaluate=False) new_conj = conj.atoms(conjugate) - arg.atoms(conjugate) if new_conj and all(arg.has(i.args[0]) for i in new_conj): return if arg != conj and arg != -conj: ignore = arg.atoms(Abs) abs_free_arg = arg.xreplace({i: Dummy(real=True) for i in ignore}) unk = [a for a in abs_free_arg.free_symbols if a.is_real is None] if not unk or not all(conj.has(conjugate(u)) for u in unk): return sqrt(expand_mul(arg*conj))
def _eval_rewrite_as_tractable(self, arg, **kwargs): return (exp(arg) - exp(-arg)) / 2
def test_ContinuousMarkovChain(): T1 = Matrix([[S(-2), S(2), S.Zero], [S.Zero, S.NegativeOne, S.One], [Rational(3, 2), Rational(3, 2), S(-3)]]) C1 = ContinuousMarkovChain('C', [0, 1, 2], T1) assert C1.limiting_distribution() == ImmutableMatrix( [[Rational(3, 19), Rational(12, 19), Rational(4, 19)]]) T2 = Matrix([[-S.One, S.One, S.Zero], [S.One, -S.One, S.Zero], [S.Zero, S.One, -S.One]]) C2 = ContinuousMarkovChain('C', [0, 1, 2], T2) A, t = C2.generator_matrix, symbols('t', positive=True) assert C2.transition_probabilities(A)(t) == Matrix( [[S.Half + exp(-2 * t) / 2, S.Half - exp(-2 * t) / 2, 0], [S.Half - exp(-2 * t) / 2, S.Half + exp(-2 * t) / 2, 0], [ S.Half - exp(-t) + exp(-2 * t) / 2, S.Half - exp(-2 * t) / 2, exp(-t) ]]) with ignore_warnings( UserWarning): ### TODO: Restore tests once warnings are removed assert P(Eq(C2(1), 1), Eq(C2(0), 1), evaluate=False) == Probability(Eq(C2(1), 1), Eq(C2(0), 1)) assert P(Eq(C2(1), 1), Eq(C2(0), 1)) == exp(-2) / 2 + S.Half assert P( Eq(C2(1), 0) & Eq(C2(2), 1) & Eq(C2(3), 1), Eq(P(Eq(C2(1), 0)), S.Half)) == (Rational(1, 4) - exp(-2) / 4) * (exp(-2) / 2 + S.Half) assert P( Not(Eq(C2(1), 0) & Eq(C2(2), 1) & Eq(C2(3), 2)) | (Eq(C2(1), 0) & Eq(C2(2), 1) & Eq(C2(3), 2)), Eq(P(Eq(C2(1), 0)), Rational(1, 4)) & Eq(P(Eq(C2(1), 1)), Rational(1, 4))) is S.One assert E(C2(Rational(3, 2)), Eq(C2(0), 2)) == -exp(-3) / 2 + 2 * exp(Rational(-3, 2)) + S.Half assert variance(C2(Rational(3, 2)), Eq( C2(0), 1)) == ((S.Half - exp(-3) / 2)**2 * (exp(-3) / 2 + S.Half) + (Rational(-1, 2) - exp(-3) / 2)**2 * (S.Half - exp(-3) / 2)) raises(KeyError, lambda: P(Eq(C2(1), 0), Eq(P(Eq(C2(1), 1)), S.Half))) assert P(Eq(C2(1), 0), Eq(P(Eq(C2(5), 1)), S.Half)) == Probability(Eq(C2(1), 0)) TS1 = MatrixSymbol('G', 3, 3) CS1 = ContinuousMarkovChain('C', [0, 1, 2], TS1) A = CS1.generator_matrix assert CS1.transition_probabilities(A)(t) == exp(t * A) C3 = ContinuousMarkovChain( 'C', [Symbol('0'), Symbol('1'), Symbol('2')], T2) assert P(Eq(C3(1), 1), Eq(C3(0), 1)) == exp(-2) / 2 + S.Half assert P(Eq(C3(1), Symbol('1')), Eq(C3(0), Symbol('1'))) == exp(-2) / 2 + S.Half #test probability queries G = Matrix([[-S(1), Rational(1, 10), Rational(9, 10)], [Rational(2, 5), -S(1), Rational(3, 5)], [Rational(1, 2), Rational(1, 2), -S(1)]]) C = ContinuousMarkovChain('C', state_space=[0, 1, 2], gen_mat=G) assert P(Eq(C(7.385), C(3.19)), Eq(C(0.862), 0)).round(5) == Float(0.35469, 5) assert P(Gt(C(98.715), C(19.807)), Eq(C(11.314), 2)).round(5) == Float(0.32452, 5) assert P(Le(C(5.9), C(10.112)), Eq(C(4), 1)).round(6) == Float(0.675214, 6) assert Float(P(Eq(C(7.32), C(2.91)), Eq(C(2.63), 1)), 14) == Float(1 - P(Ne(C(7.32), C(2.91)), Eq(C(2.63), 1)), 14) assert Float(P(Gt(C(3.36), C(1.101)), Eq(C(0.8), 2)), 14) == Float(1 - P(Le(C(3.36), C(1.101)), Eq(C(0.8), 2)), 14) assert Float(P(Lt(C(4.9), C(2.79)), Eq(C(1.61), 0)), 14) == Float(1 - P(Ge(C(4.9), C(2.79)), Eq(C(1.61), 0)), 14) assert P(Eq(C(5.243), C(10.912)), Eq(C(2.174), 1)) == P(Eq(C(10.912), C(5.243)), Eq(C(2.174), 1)) assert P(Gt(C(2.344), C(9.9)), Eq(C(1.102), 1)) == P(Lt(C(9.9), C(2.344)), Eq(C(1.102), 1)) assert P(Ge(C(7.87), C(1.008)), Eq(C(0.153), 1)) == P(Le(C(1.008), C(7.87)), Eq(C(0.153), 1)) #test symbolic queries a, b, c, d = symbols('a b c d') query = P(Eq(C(a), b), Eq(C(c), d)) assert query.subs({ a: 3.65, b: 2, c: 1.78, d: 1 }).evalf().round(10) == P(Eq(C(3.65), 2), Eq(C(1.78), 1)).round(10) query_gt = P(Gt(C(a), b), Eq(C(c), d)) query_le = P(Le(C(a), b), Eq(C(c), d)) assert query_gt.subs({ a: 13.2, b: 0, c: 3.29, d: 2 }).evalf() + query_le.subs({ a: 13.2, b: 0, c: 3.29, d: 2 }).evalf() == 1 query_ge = P(Ge(C(a), b), Eq(C(c), d)) query_lt = P(Lt(C(a), b), Eq(C(c), d)) assert query_ge.subs({ a: 7.43, b: 1, c: 1.45, d: 0 }).evalf() + query_lt.subs({ a: 7.43, b: 1, c: 1.45, d: 0 }).evalf() == 1 #test issue 20078 assert (2 * C(1) + 3 * C(1)).simplify() == 5 * C(1) assert (2 * C(1) - 3 * C(1)).simplify() == -C(1) assert (2 * (0.25 * C(1))).simplify() == 0.5 * C(1) assert (2 * C(1) * 0.25 * C(1)).simplify() == 0.5 * C(1)**2 assert (C(1)**2 + C(1)**3).simplify() == (C(1) + 1) * C(1)**2
def __new__(cls, expr, *args, **kwargs): expr = sympify(expr) if not args: if expr.is_Order: variables = expr.variables point = expr.point else: variables = list(expr.free_symbols) point = [S.Zero]*len(variables) else: args = list(args if is_sequence(args) else [args]) variables, point = [], [] if is_sequence(args[0]): for a in args: v, p = list(map(sympify, a)) variables.append(v) point.append(p) else: variables = list(map(sympify, args)) point = [S.Zero]*len(variables) if not all(v.is_symbol for v in variables): raise TypeError('Variables are not symbols, got %s' % variables) if len(list(uniq(variables))) != len(variables): raise ValueError('Variables are supposed to be unique symbols, got %s' % variables) if expr.is_Order: expr_vp = dict(expr.args[1:]) new_vp = dict(expr_vp) vp = dict(zip(variables, point)) for v, p in vp.items(): if v in new_vp.keys(): if p != new_vp[v]: raise NotImplementedError( "Mixing Order at different points is not supported.") else: new_vp[v] = p if set(expr_vp.keys()) == set(new_vp.keys()): return expr else: variables = list(new_vp.keys()) point = [new_vp[v] for v in variables] if expr is S.NaN: return S.NaN if any(x in p.free_symbols for x in variables for p in point): raise ValueError('Got %s as a point.' % point) if variables: if any(p != point[0] for p in point): raise NotImplementedError( "Multivariable orders at different points are not supported.") if point[0] is S.Infinity: s = {k: 1/Dummy() for k in variables} rs = {1/v: 1/k for k, v in s.items()} ps = [S.Zero for p in point] elif point[0] is S.NegativeInfinity: s = {k: -1/Dummy() for k in variables} rs = {-1/v: -1/k for k, v in s.items()} ps = [S.Zero for p in point] elif point[0] is not S.Zero: s = {k: Dummy() + point[0] for k in variables} rs = {(v - point[0]).together(): k - point[0] for k, v in s.items()} ps = [S.Zero for p in point] else: s = () rs = () ps = list(point) expr = expr.subs(s) if expr.is_Add: expr = expr.factor() if s: args = tuple([r[0] for r in rs.items()]) else: args = tuple(variables) if len(variables) > 1: # XXX: better way? We need this expand() to # workaround e.g: expr = x*(x + y). # (x*(x + y)).as_leading_term(x, y) currently returns # x*y (wrong order term!). That's why we want to deal with # expand()'ed expr (handled in "if expr.is_Add" branch below). expr = expr.expand() old_expr = None while old_expr != expr: old_expr = expr if expr.is_Add: lst = expr.extract_leading_order(args) expr = Add(*[f.expr for (e, f) in lst]) elif expr: try: expr = expr.as_leading_term(*args) except PoleError: if isinstance(expr, Function) or\ all(isinstance(arg, Function) for arg in expr.args): # It is not possible to simplify an expression # containing only functions (which raise error on # call to leading term) further pass else: orders = [] pts = tuple(zip(args, ps)) for arg in expr.args: try: lt = arg.as_leading_term(*args) except PoleError: lt = arg if lt not in args: order = Order(lt) else: order = Order(lt, *pts) orders.append(order) if expr.is_Add: new_expr = Order(Add(*orders), *pts) if new_expr.is_Add: new_expr = Order(Add(*[a.expr for a in new_expr.args]), *pts) expr = new_expr.expr elif expr.is_Mul: expr = Mul(*[a.expr for a in orders]) elif expr.is_Pow: e = expr.exp b = expr.base expr = exp(e * log(b)) # It would probably be better to handle this somewhere # else. This is needed for a testcase in which there is a # symbol with the assumptions zero=True. if expr.is_zero: expr = S.Zero else: expr = expr.as_independent(*args, as_Add=False)[1] expr = expand_power_base(expr) expr = expand_log(expr) if len(args) == 1: # The definition of O(f(x)) symbol explicitly stated that # the argument of f(x) is irrelevant. That's why we can # combine some power exponents (only "on top" of the # expression tree for f(x)), e.g.: # x**p * (-x)**q -> x**(p+q) for real p, q. x = args[0] margs = list(Mul.make_args( expr.as_independent(x, as_Add=False)[1])) for i, t in enumerate(margs): if t.is_Pow: b, q = t.args if b in (x, -x) and q.is_real and not q.has(x): margs[i] = x**q elif b.is_Pow and not b.exp.has(x): b, r = b.args if b in (x, -x) and r.is_real: margs[i] = x**(r*q) elif b.is_Mul and b.args[0] is S.NegativeOne: b = -b if b.is_Pow and not b.exp.has(x): b, r = b.args if b in (x, -x) and r.is_real: margs[i] = x**(r*q) expr = Mul(*margs) expr = expr.subs(rs) if expr.is_Order: expr = expr.expr if not expr.has(*variables) and not expr.is_zero: expr = S.One # create Order instance: vp = dict(zip(variables, point)) variables.sort(key=default_sort_key) point = [vp[v] for v in variables] args = (expr,) + Tuple(*zip(variables, point)) obj = Expr.__new__(cls, *args) return obj
def R_nl(n, l, r, Z=1): """ Returns the Hydrogen radial wavefunction R_{nl}. Parameters ========== n : integer Principal Quantum Number which is an integer with possible values as 1, 2, 3, 4,... l : integer ``l`` is the Angular Momentum Quantum Number with values ranging from 0 to ``n-1``. r : Radial coordinate. Z : Atomic number (1 for Hydrogen, 2 for Helium, ...) Everything is in Hartree atomic units. Examples ======== >>> from sympy.physics.hydrogen import R_nl >>> from sympy.abc import r, Z >>> R_nl(1, 0, r, Z) 2*sqrt(Z**3)*exp(-Z*r) >>> R_nl(2, 0, r, Z) sqrt(2)*(-Z*r + 2)*sqrt(Z**3)*exp(-Z*r/2)/4 >>> R_nl(2, 1, r, Z) sqrt(6)*Z*r*sqrt(Z**3)*exp(-Z*r/2)/12 For Hydrogen atom, you can just use the default value of Z=1: >>> R_nl(1, 0, r) 2*exp(-r) >>> R_nl(2, 0, r) sqrt(2)*(2 - r)*exp(-r/2)/4 >>> R_nl(3, 0, r) 2*sqrt(3)*(2*r**2/9 - 2*r + 3)*exp(-r/3)/27 For Silver atom, you would use Z=47: >>> R_nl(1, 0, r, Z=47) 94*sqrt(47)*exp(-47*r) >>> R_nl(2, 0, r, Z=47) 47*sqrt(94)*(2 - 47*r)*exp(-47*r/2)/4 >>> R_nl(3, 0, r, Z=47) 94*sqrt(141)*(4418*r**2/9 - 94*r + 3)*exp(-47*r/3)/27 The normalization of the radial wavefunction is: >>> from sympy import integrate, oo >>> integrate(R_nl(1, 0, r)**2 * r**2, (r, 0, oo)) 1 >>> integrate(R_nl(2, 0, r)**2 * r**2, (r, 0, oo)) 1 >>> integrate(R_nl(2, 1, r)**2 * r**2, (r, 0, oo)) 1 It holds for any atomic number: >>> integrate(R_nl(1, 0, r, Z=2)**2 * r**2, (r, 0, oo)) 1 >>> integrate(R_nl(2, 0, r, Z=3)**2 * r**2, (r, 0, oo)) 1 >>> integrate(R_nl(2, 1, r, Z=4)**2 * r**2, (r, 0, oo)) 1 """ # sympify arguments n, l, r, Z = map(S, [n, l, r, Z]) # radial quantum number n_r = n - l - 1 # rescaled "r" a = 1 / Z # Bohr radius r0 = 2 * r / (n * a) # normalization coefficient C = sqrt((S(2) / (n * a))**3 * factorial(n_r) / (2 * n * factorial(n + l))) # This is an equivalent normalization coefficient, that can be found in # some books. Both coefficients seem to be the same fast: # C = S(2)/n**2 * sqrt(1/a**3 * factorial(n_r) / (factorial(n+l))) return C * r0**l * assoc_laguerre(n_r, 2 * l + 1, r0).expand() * exp( -r0 / 2)
def test_PoissonProcess(): X = PoissonProcess("X", 3) assert X.state_space == S.Naturals0 assert X.index_set == Interval(0, oo) assert X.lamda == 3 t, d, x, y = symbols('t d x y', positive=True) assert isinstance(X(t), RandomIndexedSymbol) assert X.distribution(t) == PoissonDistribution(3 * t) raises(ValueError, lambda: PoissonProcess("X", -1)) raises(NotImplementedError, lambda: X[t]) raises(IndexError, lambda: X(-5)) assert X.joint_distribution(X(2), X(3)) == JointDistributionHandmade( Lambda((X(2), X(3)), 6**X(2) * 9**X(3) * exp(-15) / (factorial(X(2)) * factorial(X(3))))) assert X.joint_distribution(4, 6) == JointDistributionHandmade( Lambda((X(4), X(6)), 12**X(4) * 18**X(6) * exp(-30) / (factorial(X(4)) * factorial(X(6))))) assert P(X(t) < 1) == exp(-3 * t) assert P(Eq(X(t), 0), Contains(t, Interval.Lopen(3, 5))) == exp(-6) # exp(-2*lamda) res = P(Eq(X(t), 1), Contains(t, Interval.Lopen(3, 4))) assert res == 3 * exp(-3) # Equivalent to P(Eq(X(t), 1))**4 because of non-overlapping intervals assert P( Eq(X(t), 1) & Eq(X(d), 1) & Eq(X(x), 1) & Eq(X(y), 1), Contains(t, Interval.Lopen(0, 1)) & Contains(d, Interval.Lopen(1, 2)) & Contains(x, Interval.Lopen(2, 3)) & Contains(y, Interval.Lopen(3, 4))) == res**4 # Return Probability because of overlapping intervals assert P(Eq(X(t), 2) & Eq(X(d), 3), Contains(t, Interval.Lopen(0, 2)) & Contains(d, Interval.Ropen(2, 4))) == \ Probability(Eq(X(d), 3) & Eq(X(t), 2), Contains(t, Interval.Lopen(0, 2)) & Contains(d, Interval.Ropen(2, 4))) raises(ValueError, lambda: P( Eq(X(t), 2) & Eq(X(d), 3), Contains(t, Interval.Lopen(0, 4)) & Contains(d, Interval.Lopen(3, oo))) ) # no bound on d assert P(Eq(X(3), 2)) == 81 * exp(-9) / 2 assert P(Eq(X(t), 2), Contains(t, Interval.Lopen(0, 5))) == 225 * exp(-15) / 2 # Check that probability works correctly by adding it to 1 res1 = P(X(t) <= 3, Contains(t, Interval.Lopen(0, 5))) res2 = P(X(t) > 3, Contains(t, Interval.Lopen(0, 5))) assert res1 == 691 * exp(-15) assert (res1 + res2).simplify() == 1 # Check Not and Or assert P(Not(Eq(X(t), 2) & (X(d) > 3)), Contains(t, Interval.Ropen(2, 4)) & \ Contains(d, Interval.Lopen(7, 8))).simplify() == -18*exp(-6) + 234*exp(-9) + 1 assert P(Eq(X(t), 2) | Ne(X(t), 4), Contains(t, Interval.Ropen(2, 4))) == 1 - 36 * exp(-6) raises(ValueError, lambda: P(X(t) > 2, X(t) + X(d))) assert E( X(t)) == 3 * t # property of the distribution at a given timestamp assert E( X(t)**2 + X(d) * 2 + X(y)**3, Contains(t, Interval.Lopen(0, 1)) & Contains(d, Interval.Lopen(1, 2)) & Contains(y, Interval.Ropen(3, 4))) == 75 assert E(X(t)**2, Contains(t, Interval.Lopen(0, 1))) == 12 assert E(x*(X(t) + X(d))*(X(t)**2+X(d)**2), Contains(t, Interval.Lopen(0, 1)) & Contains(d, Interval.Ropen(1, 2))) == \ Expectation(x*(X(d) + X(t))*(X(d)**2 + X(t)**2), Contains(t, Interval.Lopen(0, 1)) & Contains(d, Interval.Ropen(1, 2))) # Value Error because of infinite time bound raises(ValueError, lambda: E(X(t)**3, Contains(t, Interval.Lopen(1, oo)))) # Equivalent to E(X(t)**2) - E(X(d)**2) == E(X(1)**2) - E(X(1)**2) == 0 assert E((X(t) + X(d)) * (X(t) - X(d)), Contains(t, Interval.Lopen(0, 1)) & Contains(d, Interval.Lopen(1, 2))) == 0 assert E(X(2) + x * E(X(5))) == 15 * x + 6 assert E(x * X(1) + y) == 3 * x + y assert P(Eq(X(1), 2) & Eq(X(t), 3), Contains(t, Interval.Lopen(1, 2))) == 81 * exp(-6) / 4 Y = PoissonProcess("Y", 6) Z = X + Y assert Z.lamda == X.lamda + Y.lamda == 9 raises(ValueError, lambda: X + 5) # should be added be only PoissonProcess instance N, M = Z.split(4, 5) assert N.lamda == 4 assert M.lamda == 5 raises(ValueError, lambda: Z.split(3, 2)) # 2+3 != 9 raises( ValueError, lambda: P(Eq(X(t), 0), Contains(t, Interval.Lopen(1, 3)) & Eq(X(1), 0))) # check if it handles queries with two random variables in one args res1 = P(Eq(N(3), N(5))) assert res1 == P(Eq(N(t), 0), Contains(t, Interval(3, 5))) res2 = P(N(3) > N(1)) assert res2 == P((N(t) > 0), Contains(t, Interval(1, 3))) assert P(N(3) < N(1)) == 0 # condition is not possible res3 = P(N(3) <= N(1)) # holds only for Eq(N(3), N(1)) assert res3 == P(Eq(N(t), 0), Contains(t, Interval(1, 3))) # tests from https://www.probabilitycourse.com/chapter11/11_1_2_basic_concepts_of_the_poisson_process.php X = PoissonProcess('X', 10) # 11.1 assert P(Eq(X(S(1) / 3), 3) & Eq(X(1), 10)) == exp(-10) * Rational(8000000000, 11160261) assert P(Eq(X(1), 1), Eq(X(S(1) / 3), 3)) == 0 assert P(Eq(X(1), 10), Eq(X(S(1) / 3), 3)) == P(Eq(X(S(2) / 3), 7)) X = PoissonProcess('X', 2) # 11.2 assert P(X(S(1) / 2) < 1) == exp(-1) assert P(X(3) < 1, Eq(X(1), 0)) == exp(-4) assert P(Eq(X(4), 3), Eq(X(2), 3)) == exp(-4) X = PoissonProcess('X', 3) assert P(Eq(X(2), 5) & Eq(X(1), 2)) == Rational(81, 4) * exp(-6) # check few properties assert P( X(2) <= 3, X(1) >= 1) == 3 * P(Eq(X(1), 0)) + 2 * P(Eq(X(1), 1)) + P(Eq(X(1), 2)) assert P(X(2) <= 3, X(1) > 1) == 2 * P(Eq(X(1), 0)) + 1 * P(Eq(X(1), 1)) assert P(Eq(X(2), 5) & Eq(X(1), 2)) == P(Eq(X(1), 3)) * P(Eq(X(1), 2)) assert P(Eq(X(3), 4), Eq(X(1), 3)) == P(Eq(X(2), 1)) #test issue 20078 assert (2 * X(t) + 3 * X(t)).simplify() == 5 * X(t) assert (2 * X(t) - 3 * X(t)).simplify() == -X(t) assert (2 * (0.25 * X(t))).simplify() == 0.5 * X(t) assert (2 * X(t) * 0.25 * X(t)).simplify() == 0.5 * X(t)**2 assert (X(t)**2 + X(t)**3).simplify() == (X(t) + 1) * X(t)**2
def _characteristic_function(self, t): a = self.a return a**2 * exp(I * t) / ((1 + (a - 1) * exp(I * t))**2)
def _moment_generating_function(self, t): a = self.a return a**2 * exp(t) / ((1 + (a - 1) * exp(t))**2)
def _eval_rewrite_as_tractable(self, z, **kwargs): return exp(loggamma(z))
def _eval_expand_func(self, **hints): n, m, theta, phi = self.args rv = (sqrt((2*n + 1)/(4*pi) * factorial(n - m)/factorial(n + m)) * exp(I*m*phi) * assoc_legendre(n, m, cos(theta))) # We can do this because of the range of theta return rv.subs(sqrt(-cos(theta)**2 + 1), sin(theta))