def compute_solution_for_recurrence(recurr_coeff: Expr, inhom_part_solution: Expr, initial_value: Expr): """ Computes the (unique) solution to the recurrence relation: f(0) = initial_value; f(n+1) = recurr_coeff * f(n) + inhom_part_solution """ log( f"Start compute solution for recurrence, { recurr_coeff }, { inhom_part_solution }, { initial_value }", LOG_VERBOSE) n = symbols('n', integer=True, positive=True) if recurr_coeff.is_zero: return expand(inhom_part_solution.xreplace({n: n - 1})) hom_solution = (recurr_coeff**n) * initial_value k = symbols('_k', integer=True, positive=True) summand = simplify( (recurr_coeff**k) * inhom_part_solution.xreplace({n: (n - 1) - k})) particular_solution = summation(summand, (k, 0, (n - 1))) particular_solution = without_piecewise(particular_solution) solution = simplify(hom_solution + particular_solution) log( f"End compute solution for recurrence, { recurr_coeff }, { inhom_part_solution }, { initial_value }", LOG_VERBOSE) return solution
def test_factor_nc(): x, y = symbols('x,y') k = symbols('k', integer=True) n, m, o = symbols('n,m,o', commutative=False) # mul and multinomial expansion is needed e = x * (1 + y)**2 assert _mexpand(e) == x + x * 2 * y + x * y**2 def factor_nc_test(e): ex = _mexpand(e) assert ex.is_Add f = factor_nc(ex) assert not f.is_Add and _mexpand(f) == ex factor_nc_test(x * (1 + y)) factor_nc_test(n * (x + 1)) factor_nc_test(n * (x + m)) factor_nc_test((x + m) * n) factor_nc_test(n * m * (x * o + n * o * m) * n) s = Sum(x, (x, 1, 2)) factor_nc_test(x * (1 + s)) factor_nc_test(x * (1 + s) * s) factor_nc_test(x * (1 + sin(s))) factor_nc_test((1 + n)**2) factor_nc_test((x + n) * (x + m) * (x + y)) factor_nc_test(x * (n * m + 1)) factor_nc_test(x * (n * m + x)) factor_nc_test(x * (x * n * m + 1)) factor_nc_test(x * n * (x * m + 1)) factor_nc_test(x * (m * n + x * n * m)) factor_nc_test(n * (1 - m) * n**2) factor_nc_test((n + m)**2) factor_nc_test((n - m) * (n + m)**2) factor_nc_test((n + m)**2 * (n - m)) factor_nc_test((m - n) * (n + m)**2 * (n - m)) assert factor_nc(n * (n + n * m)) == n**2 * (1 + m) assert factor_nc(m * (m * n + n * m * n**2)) == m * (m + n * m * n) * n eq = m * sin(n) - sin(n) * m assert factor_nc(eq) == eq eq = (sin(n) + x) * (cos(n) + x) assert factor_nc(eq.expand()) == eq # issue sympy/sympy#6534 assert (2 * n + 2 * m).factor() == 2 * (n + m) # issue sympy/sympy#6701 assert factor_nc(n**k + n**(k + 1)) == n**k * (1 + n) assert factor_nc((m * n)**k + (m * n)**(k + 1)) == (1 + m * n) * (m * n)**k # issue sympy/sympy#6918 assert factor_nc(-n * (2 * x**2 + 2 * x)) == -2 * n * x * (x + 1) assert factor_nc(1 + Mul(Expr(), Expr(), evaluate=False)) == 1 + Expr()**2
def get_initial_polarity_for_expression(expression: Expr, program: Program) -> (bool, bool): """ Returns a sound estimate whether a given expression can initial be positive and negative. It does so by substituting the variable power in the given expression with all possible combination of lower and upper bounds of their respective supports. """ variables = expression.free_symbols.intersection(program.variables) # First we can replace n and all variables which are deterministic initially (meaning they have exactly one branch) expression = expression.xreplace( {symbols("n", integer=True, positive=True): 0}) for v in variables: if not expression.free_symbols & variables: continue if hasattr(program.initial_values[v], "branches") and len( program.initial_values[v].branches) == 1: expression = expression.subs( {v: program.initial_values[v].branches[0][0]}) variables = expression.free_symbols.intersection(program.variables) if not variables: maybePos = bool( expression > 0) if (expression > 0).is_Boolean else True maybeNeg = bool( expression < 0) if (expression < 0).is_Boolean else True return maybePos, maybeNeg expression = expression.as_poly(variables) var_powers = set() monoms = get_monoms(expression) for m in monoms: m = m.as_poly(variables) powers = m.monoms()[0] var_powers.update([(v, p) for v, p in zip(m.gens, powers) if p > 0]) initial_supports = get_initial_supports_for_variable_powers( var_powers, program) possible_substitutions = flatten_substitution_choices(initial_supports) expression = expression.as_expr() possible_initial_polarities = [] for ps in possible_substitutions: pos = expression.subs(ps) > 0 if pos.is_Boolean: possible_initial_polarities.append(bool(pos)) else: possible_initial_polarities.append(None) allPositive = all([v is True for v in possible_initial_polarities]) allNegative = all([v is False for v in possible_initial_polarities]) maybePos = not allNegative maybeNeg = not allPositive return maybePos, maybeNeg
def _eval_evalf(self, prec): from mpmath import mp, workprec from diofant import Expr z = self.args[0]._to_mpmath(prec) with workprec(prec): res = mp.airybi(z, derivative=1) return Expr._from_mpmath(res, prec)
def is_deterministic_invariant(expression: Expr) -> bool: """ Checks whether an expression only containing n eventually stays <= 0 """ n = symbols("n", integer=True, positive=True) max_0 = get_max_0(expression, n) return expression.subs({n: max_0 + 1}) <= 0
def _eval_evalf(self, prec): from mpmath import mp, workprec from diofant import Expr a = self.args[0]._to_mpmath(prec) z = self.args[1]._to_mpmath(prec) with workprec(prec): res = mp.gammainc(a, z, mp.inf) return Expr._from_mpmath(res, prec)
def _eval_evalf(self, prec): m = self.args[0] if m.is_Integer and m.is_nonnegative: from mpmath import mp from diofant import Expr m = m._to_mpmath(prec) with workprec(prec): res = mp.eulernum(m) return Expr._from_mpmath(res, prec)
def _eval_evalf(self, prec): # The default code is insufficient for polar arguments. # mpmath provides an optional argument "r", which evaluates # G(z**(1/r)). I am not sure what its intended use is, but we hijack it # here in the following way: to evaluate at a number z of |argument| # less than (say) n*pi, we put r=1/n, compute z' = root(z, n) # (carefully so as not to loose the branch information), and evaluate # G(z'**(1/r)) = G(z'**n) = G(z). from diofant.functions import exp_polar, ceiling from diofant import Expr import mpmath z = self.argument znum = self.argument._eval_evalf(prec) if znum.has(exp_polar): znum, branch = znum.as_coeff_mul(exp_polar) if len(branch) != 1: return branch = branch[0].args[0] / I else: branch = Integer(0) n = ceiling(abs(branch / S.Pi)) + 1 znum = znum**(Integer(1) / n) * exp(I * branch / n) # Convert all args to mpf or mpc try: [z, r, ap, bq] = [ arg._to_mpmath(prec) for arg in [znum, 1 / n, self.args[0], self.args[1]] ] except ValueError: return with mpmath.workprec(prec): v = mpmath.meijerg(ap, bq, z, r) return Expr._from_mpmath(v, prec)
def __new__(cls, *args, **kwargs): shape, flat_list = cls._handle_ndarray_creation_inputs(*args, **kwargs) shape = Tuple(*map(_sympify, shape)) loop_size = functools.reduce(lambda x, y: x * y, shape) if shape else 0 # Sparse array: if isinstance(flat_list, (dict, Dict)): sparse_array = Dict(flat_list) else: sparse_array = {} for i, el in enumerate(flatten(flat_list)): if el != 0: sparse_array[i] = _sympify(el) sparse_array = Dict(sparse_array) self = Expr.__new__(cls, sparse_array, shape, **kwargs) self._shape = shape self._rank = len(shape) self._loop_size = loop_size self._sparse_array = sparse_array return self
def __new__(cls, x): return Expr.__new__(cls, x)
def test_sympyissue_5486_bug(): assert abs(Expr._from_mpmath(I._to_mpmath(15), 15) - I) < 1.0e-15 pytest.raises(TypeError, lambda: Expr._from_mpmath(I, 15))
def test_issue_5486_bug(): from diofant import I, Expr assert abs(Expr._from_mpmath(I._to_mpmath(15), 15) - I) < 1.0e-15 pytest.raises(TypeError, lambda: Expr._from_mpmath(I, 15))
def test_diff_wrt_value(): assert Expr()._diff_wrt is False assert x._diff_wrt is True assert f(x)._diff_wrt is True assert Derivative(f(x), x)._diff_wrt is True assert Derivative(x**2, x)._diff_wrt is False
def test_sympyissue_5486_bug(): assert abs(Expr._from_mpmath(I._to_mpmath(15), 15) - I) < 1.0e-15
def jn_zeros(n, k, method="diofant", dps=15): """ Zeros of the spherical Bessel function of the first kind. This returns an array of zeros of jn up to the k-th zero. * method = "diofant": uses mpmath's function ``besseljzero`` * method = "scipy": uses the `SciPy's sph_jn <http://docs.scipy.org/doc/scipy/reference/generated/scipy.special.jn_zeros.html>`_ and `newton <http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.newton.html>`_ to find all roots, which is faster than computing the zeros using a general numerical solver, but it requires SciPy and only works with low precision floating point numbers. [The function used with method="diofant" is a recent addition to mpmath, before that a general solver was used.] Examples ======== >>> from diofant import jn_zeros >>> jn_zeros(2, 4, dps=5) [5.7635, 9.095, 12.323, 15.515] See Also ======== jn, yn, besselj, besselk, bessely """ from math import pi if method == "diofant": from mpmath import besseljzero from mpmath.libmp.libmpf import dps_to_prec from diofant import Expr prec = dps_to_prec(dps) return [ Expr._from_mpmath( besseljzero(sympify(n + 0.5)._to_mpmath(prec), int(l)), prec) for l in range(1, k + 1) ] elif method == "scipy": from scipy.special import sph_jn from scipy.optimize import newton def f(x): return sph_jn(n, x)[0][-1] else: raise NotImplementedError("Unknown method.") def solver(f, x): if method == "scipy": root = newton(f, x) else: raise NotImplementedError("Unknown method.") return root # we need to approximate the position of the first root: root = n + pi # determine the first root exactly: root = solver(f, root) roots = [root] for i in range(k - 1): # estimate the position of the next root using the last root + pi: root = solver(f, root + pi) roots.append(root) return roots
def test_from_mpmath(): # issue sympy/sympy#5486 pytest.raises(TypeError, lambda: Expr._from_mpmath(I, 15))
def test_sympyissue_5486(): assert not cos(sqrt(0.5 + I)).evalf(strict=False).is_Function assert abs(Expr._from_mpmath(I._to_mpmath(15), 15) - I) < 1.0e-15