def contains(self, other): """ Is the other GeometryEntity contained within this Segment? Examples ======== >>> from diofant import Point, Segment >>> p1, p2 = Point(0, 1), Point(3, 4) >>> s = Segment(p1, p2) >>> s2 = Segment(p2, p1) >>> s.contains(s2) True """ if isinstance(other, Segment): return other.p1 in self and other.p2 in self elif isinstance(other, Point): if Point.is_collinear(self.p1, self.p2, other): t = Dummy('t') x, y = self.arbitrary_point(t).args if self.p1.x != self.p2.x: ti = solve(x - other.x, t)[0] else: ti = solve(y - other.y, t)[0] if ti.is_number: return 0 <= ti <= 1 return return False
def singularities(f, x): """Find singularities of real-valued function `f` with respect to `x`. Examples ======== >>> from diofant import Symbol, exp, log >>> from diofant.abc import x >>> singularities(1/(1 + x), x) == {-1} True >>> singularities(exp(1/x) + log(x + 1), x) == {-1, 0} True >>> singularities(exp(1/log(x + 1)), x) == {0} True Notes ===== Removable singularities are not supported now. References ========== .. [1] http://en.wikipedia.org/wiki/Mathematical_singularity """ f, x = sympify(f), sympify(x) guess, res = set(), set() assert x.is_Symbol if f.is_number: return set() elif f.is_polynomial(x): return set() elif f.func in (Add, Mul): guess = guess.union(*[singularities(a, x) for a in f.args]) elif f.func is Pow: if f.exp.is_number and f.exp.is_negative: guess = {v for v in solve(f.base, x) if v.is_real} else: guess |= singularities(log(f.base)*f.exp, x) elif f.func in (log, sign) and len(f.args) == 1: guess |= singularities(f.args[0], x) guess |= {v for v in solve(f.args[0], x) if v.is_real} else: # pragma: no cover raise NotImplementedError for s in guess: l = Limit(f, x, s, dir="real") try: r = l.doit() if r == l or f.subs(x, s) != r: # pragma: no cover raise NotImplementedError except PoleError: res.add(s) return res
def test_nfloat(): x = Symbol("x") eq = x**Rational(4, 3) + 4*cbrt(x)/3 assert _aresame(nfloat(eq), x**Rational(4, 3) + (4.0/3)*cbrt(x)) assert _aresame(nfloat(eq, exponent=True), x**(4.0/3) + (4.0/3)*x**(1.0/3)) eq = x**Rational(4, 3) + 4*x**(x/3)/3 assert _aresame(nfloat(eq), x**Rational(4, 3) + (4.0/3)*x**(x/3)) big = 12345678901234567890 # specify precision to match value used in nfloat Float_big = Float(big, 15) assert _aresame(nfloat(big), Float_big) assert _aresame(nfloat(big*x), Float_big*x) assert _aresame(nfloat(x**big, exponent=True), x**Float_big) assert nfloat({x: sqrt(2)}) == {x: nfloat(sqrt(2))} assert nfloat({sqrt(2): x}) == {sqrt(2): x} assert nfloat(cos(x + sqrt(2))) == cos(x + nfloat(sqrt(2))) # issue sympy/sympy#6342 lamda = Symbol('lamda') f = x*lamda + lamda**3*(x/2 + Rational(1, 2)) + lamda**2 + Rational(1, 4) assert not any(a[lamda].free_symbols for a in solve(f.subs({x: -0.139}))) # issue sympy/sympy#6632 assert nfloat(-100000*sqrt(2500000001) + 5000000001) == \ 9.99999999800000e-11 # issue sympy/sympy#7122 eq = cos(3*x**4 + y)*RootOf(x**5 + 3*x**3 + 1, 0) assert str(nfloat(eq, exponent=False, n=1)) == '-0.7*cos(3.0*x**4 + y)'
def test_nfloat(): x = Symbol("x") eq = x**Rational(4, 3) + 4 * cbrt(x) / 3 assert _aresame(nfloat(eq), x**Rational(4, 3) + (4.0 / 3) * cbrt(x)) assert _aresame(nfloat(eq, exponent=True), x**(4.0 / 3) + (4.0 / 3) * x**(1.0 / 3)) eq = x**Rational(4, 3) + 4 * x**(x / 3) / 3 assert _aresame(nfloat(eq), x**Rational(4, 3) + (4.0 / 3) * x**(x / 3)) big = 12345678901234567890 # specify precision to match value used in nfloat Float_big = Float(big, 15) assert _aresame(nfloat(big), Float_big) assert _aresame(nfloat(big * x), Float_big * x) assert _aresame(nfloat(x**big, exponent=True), x**Float_big) assert nfloat({x: sqrt(2)}) == {x: nfloat(sqrt(2))} assert nfloat({sqrt(2): x}) == {sqrt(2): x} assert nfloat(cos(x + sqrt(2))) == cos(x + nfloat(sqrt(2))) # issue sympy/sympy#6342 lamda = Symbol('lamda') f = x * lamda + lamda**3 * (x / 2 + Rational(1, 2)) + lamda**2 + Rational( 1, 4) assert not any(a[lamda].free_symbols for a in solve(f.subs({x: -0.139}))) # issue sympy/sympy#6632 assert nfloat(-100000*sqrt(2500000001) + 5000000001) == \ 9.99999999800000e-11 # issue sympy/sympy#7122 eq = cos(3 * x**4 + y) * RootOf(x**5 + 3 * x**3 + 1, 0) assert str(nfloat(eq, exponent=False, n=1)) == '-0.7*cos(3.0*x**4 + y)'
def arbitrary_point(self, t=None): """ Returns an arbitrary point on the Plane; varying `t` from 0 to 2*pi will move the point in a circle of radius 1 about p1 of the Plane. Examples ======== >>> from diofant.geometry.plane import Plane >>> from diofant.abc import t >>> p = Plane((0, 0, 0), (0, 0, 1), (0, 1, 0)) >>> p.arbitrary_point(t) Point3D(0, cos(t), sin(t)) >>> _.distance(p.p1).simplify() 1 Returns ======= Point3D """ from diofant import cos, sin t = t or Dummy('t') x, y, z = self.normal_vector a, b, c = self.p1.args if x == y == 0: return Point3D(a + cos(t), b + sin(t), c) elif x == z == 0: return Point3D(a + cos(t), b, c + sin(t)) elif y == z == 0: return Point3D(a, b + cos(t), c + sin(t)) m = Dummy() p = self.projection( Point3D(self.p1.x + cos(t), self.p1.y + sin(t), 0) * m) return p.xreplace({m: solve(p.distance(self.p1) - 1, m)[0]})
def minimize_univariate(f, x, dom): extr = {} if dom.is_Union: for d in dom.args: fp, r = minimize_univariate(f, x, d) extr[r[x]] = fp elif dom.is_Interval: if not dom.left_open: extr[dom.start] = limit(f, x, dom.start) if not dom.right_open: extr[dom.end] = limit(f, x, dom.end, dir="-") for s in singularities(f, x): if s in dom: m = Min(limit(f, x, s), limit(f, x, s, dir="-")) if m is -oo: return -oo, dict({x: s}) else: extr[s] = m for p in solve(diff(f, x), x): if p in dom: extr[p] = f.subs(x, p) elif dom.is_FiniteSet: for p in dom.args: extr[p] = f.subs(x, p) else: # pragma: no cover raise NotImplementedError if extr: min, point = oo, nan for p, fp in sorted(extr.items()): if fp < min: point, min = p, fp return min, dict({x: point})
def _remove_multiple_delta(expr): """Evaluate products of KroneckerDelta's. """ from diofant.solvers import solve if expr.is_Add: return expr.func(*list(map(_remove_multiple_delta, expr.args))) if not expr.is_Mul: return expr eqs = [] newargs = [] for arg in expr.args: if isinstance(arg, KroneckerDelta): eqs.append(arg.args[0] - arg.args[1]) else: newargs.append(arg) if not eqs: return expr solns = solve(eqs, dict=True) if len(solns) == 0: return S.Zero elif len(solns) == 1: for key in solns[0].keys(): newargs.append(KroneckerDelta(key, solns[0][key])) expr2 = expr.func(*newargs) if expr != expr2: return _remove_multiple_delta(expr2) return expr
def _eval_subs(self, old, new): if old in self.variables: newexpr = self.expr.subs(old, new) i = self.variables.index(old) newvars = list(self.variables) newpt = list(self.point) if new.is_Symbol: newvars[i] = new else: syms = new.free_symbols if len(syms) == 1 or old in syms: if old in syms: var = self.variables[i] else: var = syms.pop() # First, try to substitute self.point in the "new" # expr to see if this is a fixed point. # E.g. O(y).subs(y, sin(x)) point = new.subs(var, self.point[i]) if point != self.point[i]: from diofant.solvers import solve d = Dummy() res = solve(old - new.subs(var, d), d, dict=True) point = d.subs(res[0]).limit(old, self.point[i]) newvars[i] = var newpt[i] = point elif old not in syms: del newvars[i], newpt[i] if not syms and new == self.point[i]: newvars.extend(syms) newpt.extend([S.Zero]*len(syms)) else: return return Order(newexpr, *zip(newvars, newpt))
def _contains(self, other): from diofant.solvers import solve L = self.lamda if self._is_multivariate(): solns = solve([expr - val for val, expr in zip(other, L.expr)], L.variables) else: solns = solve(L.expr - other, L.variables[0]) for soln in solns: try: if soln in self.base_set: return S.true except TypeError: if soln.evalf() in self.base_set: return S.true return S.false
def perpendicular_segment(self, p): """Create a perpendicular line segment from `p` to this line. The enpoints of the segment are ``p`` and the closest point in the line containing self. (If self is not a line, the point might not be in self.) Parameters ========== p : Point3D Returns ======= segment : Segment3D Notes ===== Returns `p` itself if `p` is on this linear entity. See Also ======== perpendicular_line Examples ======== >>> from diofant import Point3D, Line3D >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(1, 1, 1), Point3D(0, 2, 0) >>> l1 = Line3D(p1, p2) >>> s1 = l1.perpendicular_segment(p3) >>> l1.is_perpendicular(s1) True >>> p3 in s1 True >>> l1.perpendicular_segment(Point3D(4, 0, 0)) Segment3D(Point3D(4/3, 4/3, 4/3), Point3D(4, 0, 0)) """ p = Point3D(p) if p in self: raise NotImplementedError("Given point should not be on the line") t = Dummy() a = self.arbitrary_point(t) b = [i - j for i, j in zip(p.args, a.args)] c = sum(i * j for i, j in zip(b, self.direction_ratio)) d = solve(c, t) e = a.subs(t, d[0]) return Segment3D(p, e)
def _do_ellipse_intersection(self, o): """The intersection of an ellipse with another ellipse or a circle. Private helper method for `intersection`. """ x = Dummy('x', extended_real=True) y = Dummy('y', extended_real=True) seq = self.equation(x, y) oeq = o.equation(x, y) result = solve([seq, oeq], [x, y]) return [Point(*r) for r in list(uniq(result))]
def _simplify_delta(expr): """Rewrite a KroneckerDelta's indices in its simplest form. """ from diofant.solvers import solve if isinstance(expr, KroneckerDelta): try: slns = solve(expr.args[0] - expr.args[1], dict=True) if slns and len(slns) == 1: return Mul(*[ KroneckerDelta(*(key, value)) for key, value in slns[0].items() ]) except NotImplementedError: pass return expr
def contains(self, o): """ Return True if o is on this Line, or False otherwise. Examples ======== >>> from diofant import Line,Point >>> p1, p2 = Point(0, 1), Point(3, 4) >>> l = Line(p1, p2) >>> l.contains(p1) True >>> l.contains((0, 1)) True >>> l.contains((0, 0)) False """ if is_sequence(o): o = Point(o) if isinstance(o, Point): o = o.func(*[simplify(i) for i in o.args]) x, y = Dummy(), Dummy() eq = self.equation(x, y) if not eq.has(y): return (solve(eq, x)[0] - o.x).equals(0) if not eq.has(x): return (solve(eq, y)[0] - o.y).equals(0) return (solve(eq.subs(x, o.x), y)[0] - o.y).equals(0) elif not isinstance(o, LinearEntity): return False elif isinstance(o, Line): return self.equal(o) elif not self.is_similar(o): return False else: return o.p1 in self and o.p2 in self
def minimize(f, *v): """Minimizes `f` with respect to given variables `v`. Examples ======== >>> from diofant.calculus import minimize >>> from diofant.abc import x >>> minimize(x**2, x) (0, {x: 0}) >>> minimize([x**2, x >= 1], x) (1, {x: 1}) >>> minimize([-x**2, x >= -2, x <= 1], x) (-4, {x: -2}) See Also ======== maximize """ f = set(map(sympify, f if is_sequence(f) else [f])) constr = {c for c in f if c.is_Relational} assert len(f - constr) == 1 f = (f - constr).pop() if not v: v = f.free_symbols if not v: return f, dict() v = tuple(v) assert all(x.is_Symbol for x in v) if constr: dom = solve(constr, *v).as_set() else: dom = Interval(-oo, oo, True, True)**len(v) if len(v) == 1: return minimize_univariate(f, v[0], dom) else: # pragma: no cover return NotImplementedError
def telescopic(L, R, limits): '''Tries to perform the summation using the telescopic property return None if not possible ''' (i, a, b) = limits if L.is_Add or R.is_Add: return # We want to solve(L.subs(i, i + m) + R, m) # First we try a simple match since this does things that # solve doesn't do, e.g. solve(f(k+m)-f(k), m) fails k = Wild("k") sol = (-R).match(L.subs(i, i + k)) s = None if sol and k in sol: s = sol[k] if not (s.is_Integer and L.subs(i, i + s) == -R): # sometimes match fail(f(x+2).match(-f(x+k))->{k: -2 - 2x})) s = None # But there are things that match doesn't do that solve # can do, e.g. determine that 1/(x + m) = 1/(1 - x) when m = 1 if s is None: m = Dummy('m') try: sol = solve(L.subs(i, i + m) + R, m) or [] except NotImplementedError: return sol = [ si for si in sol if si.is_Integer and (L.subs(i, i + si) + R).expand().is_zero ] if len(sol) != 1: return s = sol[0] if s < 0: return telescopic_direct(R, L, abs(s), (i, a, b)) elif s > 0: return telescopic_direct(L, R, s, (i, a, b))
def perpendicular_line(self, p): """Create a new Line perpendicular to this linear entity which passes through the point `p`. Parameters ========== p : Point3D Returns ======= line : Line3D See Also ======== is_perpendicular, perpendicular_segment Examples ======== >>> from diofant import Point3D, Line3D >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(2, 3, 4), Point3D(-2, 2, 0) >>> l1 = Line3D(p1, p2) >>> l2 = l1.perpendicular_line(p3) >>> p3 in l2 True >>> l1.is_perpendicular(l2) True """ p = Point3D(p) if p in self: raise NotImplementedError("Given point should not be on the line") t = Dummy() a = self.arbitrary_point(t) b = [i - j for i, j in zip(p.args, a.args)] c = sum(i * j for i, j in zip(b, self.direction_ratio)) d = solve(c, t) e = a.subs(t, d[0]) return Line3D(p, e)
def _nthroot_solve(p, n, prec): """ helper function for ``nthroot`` It denests ``p**Rational(1, n)`` using its minimal polynomial """ from diofant.polys.numberfields import _minimal_polynomial_sq from diofant.solvers import solve while n % 2 == 0: p = sqrtdenest(sqrt(p)) n = n // 2 if n == 1: return p pn = p**Rational(1, n) x = Symbol('x') f = _minimal_polynomial_sq(p, n, x) if f is None: return sols = solve(f, x) for sol in sols: if abs(sol - pn).n() < 1. / 10**prec: sol = sqrtdenest(sol) if _mexpand(sol**n) == p: return sol
def apart_undetermined_coeffs(P, Q): """Partial fractions via method of undetermined coefficients. """ X = numbered_symbols(cls=Dummy) partial, symbols = [], [] _, factors = Q.factor_list() for f, k in factors: n, q = f.degree(), Q for i in range(1, k + 1): coeffs, q = take(X, n), q.quo(f) partial.append((coeffs, q, f, i)) symbols.extend(coeffs) dom = Q.get_domain().inject(*symbols) F = Poly(0, Q.gen, domain=dom) for i, (coeffs, q, f, k) in enumerate(partial): h = Poly(coeffs, Q.gen, domain=dom) partial[i] = (h, f, k) q = q.set_domain(dom) F += h * q system, result = [], Integer(0) for (k, ), coeff in F.terms(): system.append(coeff - P.nth(k)) from diofant.solvers import solve solution = solve(system, symbols) for h, f, k in partial: h = h.as_expr().subs(solution) result += h / f.as_expr()**k return result
def deltasummation(f, limit, no_piecewise=False): """Handle summations containing a KroneckerDelta. The idea for summation is the following: - If we are dealing with a KroneckerDelta expression, i.e. KroneckerDelta(g(x), j), we try to simplify it. If we could simplify it, then we sum the resulting expression. We already know we can sum a simplified expression, because only simple KroneckerDelta expressions are involved. If we couldn't simplify it, there are two cases: 1) The expression is a simple expression: we return the summation, taking care if we are dealing with a Derivative or with a proper KroneckerDelta. 2) The expression is not simple (i.e. KroneckerDelta(cos(x))): we can do nothing at all. - If the expr is a multiplication expr having a KroneckerDelta term: First we expand it. If the expansion did work, then we try to sum the expansion. If not, we try to extract a simple KroneckerDelta term, then we have two cases: 1) We have a simple KroneckerDelta term, so we return the summation. 2) We didn't have a simple term, but we do have an expression with simplified KroneckerDelta terms, so we sum this expression. Examples ======== >>> from diofant import oo, symbols >>> from diofant.abc import k >>> i, j = symbols('i, j', integer=True, finite=True) >>> from diofant import KroneckerDelta, Piecewise >>> deltasummation(KroneckerDelta(i, k), (k, -oo, oo)) 1 >>> deltasummation(KroneckerDelta(i, k), (k, 0, oo)) Piecewise((1, 0 <= i), (0, true)) >>> deltasummation(KroneckerDelta(i, k), (k, 1, 3)) Piecewise((1, And(1 <= i, i <= 3)), (0, true)) >>> deltasummation(k*KroneckerDelta(i, j)*KroneckerDelta(j, k), (k, -oo, oo)) j*KroneckerDelta(i, j) >>> deltasummation(j*KroneckerDelta(i, j), (j, -oo, oo)) i >>> deltasummation(i*KroneckerDelta(i, j), (i, -oo, oo)) j See Also ======== deltaproduct diofant.functions.special.tensor_functions.KroneckerDelta diofant.concrete.sums.summation """ from diofant.concrete.summations import summation from diofant.solvers import solve if ((limit[2] - limit[1]) < 0) is S.true: return S.Zero if not f.has(KroneckerDelta): return summation(f, limit) x = limit[0] g = _expand_delta(f, x) if g.is_Add: return piecewise_fold( g.func(*[deltasummation(h, limit, no_piecewise) for h in g.args])) # try to extract a simple KroneckerDelta term delta, expr = _extract_delta(g, x) if not delta: return summation(f, limit) solns = solve(delta.args[0] - delta.args[1], x) if len(solns) == 0: return S.Zero elif len(solns) != 1: return Sum(f, limit) value = solns[0] if no_piecewise: return expr.subs(x, value) return Piecewise( (expr.subs(x, value), Interval(*limit[1:3]).as_relational(value)), (S.Zero, True))
def parametric_log_deriv_heu(fa, fd, wa, wd, DE, c1=None): """ Parametric logarithmic derivative heuristic. Given a derivation D on k[t], f in k(t), and a hyperexponential monomial theta over k(t), raises either NotImplementedError, in which case the heuristic failed, or returns None, in which case it has proven that no solution exists, or returns a solution (n, m, v) of the equation n*f == Dv/v + m*Dtheta/theta, with v in k(t)* and n, m in ZZ with n != 0. If this heuristic fails, the structure theorem approach will need to be used. The argument w == Dtheta/theta """ # TODO: finish writing this and write tests c1 = c1 or Dummy('c1') p, a = fa.div(fd) q, b = wa.div(wd) B = max(0, derivation(DE.t, DE).degree(DE.t) - 1) C = max(p.degree(DE.t), q.degree(DE.t)) if q.degree(DE.t) > B: eqs = [p.nth(i) - c1*q.nth(i) for i in range(B + 1, C + 1)] s = solve(eqs, c1) if not s or not s[c1].is_Rational: # deg(q) > B, no solution for c. return N, M = s[c1].as_numer_denom() # N and M are integers N, M = Poly(N, DE.t), Poly(M, DE.t) nfmwa = N*fa*wd - M*wa*fd nfmwd = fd*wd Qv = is_log_deriv_k_t_radical_in_field(N*fa*wd - M*wa*fd, fd*wd, DE, 'auto') if Qv is None: # (N*f - M*w) is not the logarithmic derivative of a k(t)-radical. return Q, e, v = Qv if e != 1: return if Q.is_zero or v.is_zero: return return Q*N, Q*M, v if p.degree(DE.t) > B: return c = lcm(fd.as_poly(DE.t).LC(), wd.as_poly(DE.t).LC()) l = fd.monic().lcm(wd.monic())*Poly(c, DE.t) ln, ls = splitfactor(l, DE) z = ls*ln.gcd(ln.diff(DE.t)) if not z.has(DE.t): raise NotImplementedError("parametric_log_deriv_heu() " "heuristic failed: z in k.") u1, r1 = (fa*l.quo(fd)).div(z) # (l*f).div(z) u2, r2 = (wa*l.quo(wd)).div(z) # (l*w).div(z) eqs = [r1.nth(i) - c1*r2.nth(i) for i in range(z.degree(DE.t))] s = solve(eqs, c1) if not s or not s[c1].is_Rational: # deg(q) <= B, no solution for c. return M, N = s[c1].as_numer_denom() nfmwa = N.as_poly(DE.t)*fa*wd - M.as_poly(DE.t)*wa*fd nfmwd = fd*wd Qv = is_log_deriv_k_t_radical_in_field(nfmwa, nfmwd, DE) if Qv is None: # (N*f - M*w) is not the logarithmic derivative of a k(t)-radical. return Q, v = Qv if Q.is_zero or v.is_zero: return return Q*N, Q*M, v
def tangent_lines(self, p): """Tangent lines between `p` and the ellipse. If `p` is on the ellipse, returns the tangent line through point `p`. Otherwise, returns the tangent line(s) from `p` to the ellipse, or None if no tangent line is possible (e.g., `p` inside ellipse). Parameters ========== p : Point Returns ======= tangent_lines : list with 1 or 2 Lines Raises ====== NotImplementedError Can only find tangent lines for a point, `p`, on the ellipse. See Also ======== diofant.geometry.point.Point, diofant.geometry.line.Line Examples ======== >>> from diofant import Point, Ellipse >>> e1 = Ellipse(Point(0, 0), 3, 2) >>> e1.tangent_lines(Point(3, 0)) [Line(Point2D(3, 0), Point2D(3, -12))] """ p = Point(p) if self.encloses_point(p): return [] if p in self: delta = self.center - p rise = (self.vradius**2) * delta.x run = -(self.hradius**2) * delta.y p2 = Point(simplify(p.x + run), simplify(p.y + rise)) return [Line(p, p2)] else: if len(self.foci) == 2: f1, f2 = self.foci maj = self.hradius test = (2 * maj - Point.distance(f1, p) - Point.distance(f2, p)) else: test = self.radius - Point.distance(self.center, p) if test.is_number and test.is_positive: return [] # else p is outside the ellipse or we can't tell. In case of the # latter, the solutions returned will only be valid if # the point is not inside the ellipse; if it is, nan will result. x, y = Dummy('x'), Dummy('y') eq = self.equation(x, y) dydx = idiff(eq, y, x) slope = Line(p, Point(x, y)).slope tangent_points = solve([slope - dydx, eq], [x, y]) # handle horizontal and vertical tangent lines if len(tangent_points) == 1: assert tangent_points[0][0] == p.x or tangent_points[0][ 1] == p.y return [Line(p, p + Point(1, 0)), Line(p, p + Point(0, 1))] # others return [Line(p, tangent_points[0]), Line(p, tangent_points[1])]
def normal_lines(self, p, prec=None): """Normal lines between `p` and the ellipse. Parameters ========== p : Point Returns ======= normal_lines : list with 1, 2 or 4 Lines Examples ======== >>> from diofant import Line, Point, Ellipse >>> e = Ellipse((0, 0), 2, 3) >>> c = e.center >>> e.normal_lines(c + Point(1, 0)) [Line(Point2D(0, 0), Point2D(1, 0))] >>> e.normal_lines(c) [Line(Point2D(0, 0), Point2D(0, 1)), Line(Point2D(0, 0), Point2D(1, 0))] Off-axis points require the solution of a quartic equation. This often leads to very large expressions that may be of little practical use. An approximate solution of `prec` digits can be obtained by passing in the desired value: >>> e.normal_lines((3, 3), prec=2) [Line(Point2D(-38/47, -85/31), Point2D(9/47, -21/17)), Line(Point2D(19/13, -43/21), Point2D(32/13, -8/3))] Whereas the above solution has an operation count of 12, the exact solution has an operation count of 2020. """ p = Point(p) # XXX change True to something like self.angle == 0 if the arbitrarily # rotated ellipse is introduced. # https://github.com/sympy/sympy/issues/2815) if True: rv = [] if p.x == self.center.x: rv.append(Line(self.center, slope=oo)) if p.y == self.center.y: rv.append(Line(self.center, slope=0)) if rv: # at these special orientations of p either 1 or 2 normals # exist and we are done return rv # find the 4 normal points and construct lines through them with # the corresponding slope x, y = Dummy('x', extended_real=True), Dummy('y', extended_real=True) eq = self.equation(x, y) dydx = idiff(eq, y, x) norm = -1 / dydx slope = Line(p, (x, y)).slope seq = slope - norm yis = solve(seq, y)[0] xeq = eq.subs(y, yis).as_numer_denom()[0].expand() if len(xeq.free_symbols) == 1: try: # this is so much faster, it's worth a try xsol = Poly(xeq, x).real_roots() except (DomainError, PolynomialError, NotImplementedError): xsol = _nsort(solve(xeq, x), separated=True)[0] points = [Point(i, solve(eq.subs(x, i), y)[0]) for i in xsol] else: raise NotImplementedError( 'intersections for the general ellipse are not supported') slopes = [norm.subs(zip((x, y), pt.args)) for pt in points] if prec is not None: points = [pt.n(prec) for pt in points] slopes = [i if _not_a_coeff(i) else i.n(prec) for i in slopes] return [Line(pt, slope=s) for pt, s in zip(points, slopes)]
def intersection(self, o): """ The intersection with other geometrical entity. Parameters ========== Point, Point3D, LinearEntity, LinearEntity3D, Plane Returns ======= List Examples ======== >>> from diofant import Point, Point3D, Line, Line3D, Plane >>> a = Plane(Point3D(1, 2, 3), normal_vector=(1, 1, 1)) >>> b = Point3D(1, 2, 3) >>> a.intersection(b) [Point3D(1, 2, 3)] >>> c = Line3D(Point3D(1, 4, 7), Point3D(2, 2, 2)) >>> a.intersection(c) [Point3D(2, 2, 2)] >>> d = Plane(Point3D(6, 0, 0), normal_vector=(2, -5, 3)) >>> e = Plane(Point3D(2, 0, 0), normal_vector=(3, 4, -3)) >>> d.intersection(e) [Line3D(Point3D(78/23, -24/23, 0), Point3D(147/23, 321/23, 23))] """ from diofant.geometry.line3d import LinearEntity3D from diofant.geometry.line import LinearEntity if isinstance(o, (Point, Point3D)): if o in self: return [Point3D(o)] else: return [] if isinstance(o, (LinearEntity, LinearEntity3D)): if o in self: p1, p2 = o.p1, o.p2 if isinstance(o, Segment): o = Segment3D(p1, p2) elif isinstance(o, Ray): o = Ray3D(p1, p2) elif isinstance(o, Line): o = Line3D(p1, p2) else: raise ValueError('unhandled linear entity: %s' % o.func) return [o] else: x, y, z = map(Dummy, 'xyz') t = Dummy() # unnamed else it may clash with a symbol in o a = Point3D(o.arbitrary_point(t)) b = self.equation(x, y, z) c = solve(b.subs(list(zip((x, y, z), a.args))), t) if not c: return [] else: p = a.subs(t, c[0]) if p not in self: return [] # e.g. a segment might not intersect a plane return [p] if isinstance(o, Plane): if o == self: return [self] if self.is_parallel(o): return [] else: x, y, z = map(Dummy, 'xyz') a, b = Matrix([self.normal_vector]), Matrix([o.normal_vector]) c = list(a.cross(b)) d = self.equation(x, y, z) e = o.equation(x, y, z) f = solve((d.subs(z, 0), e.subs(z, 0)), [x, y]) if len(f) == 2: return [Line3D(Point3D(f[x], f[y], 0), direction_ratio=c)] g = solve((d.subs(y, 0), e.subs(y, 0)), [x, z]) if len(g) == 2: return [Line3D(Point3D(g[x], 0, g[z]), direction_ratio=c)] h = solve((d.subs(x, 0), e.subs(x, 0)), [y, z]) if len(h) == 2: return [Line3D(Point3D(0, h[y], h[z]), direction_ratio=c)]
def deltaintegrate(f, x): """ deltaintegrate(f, x) The idea for integration is the following: - If we are dealing with a DiracDelta expression, i.e. DiracDelta(g(x)), we try to simplify it. If we could simplify it, then we integrate the resulting expression. We already know we can integrate a simplified expression, because only simple DiracDelta expressions are involved. If we couldn't simplify it, there are two cases: 1) The expression is a simple expression: we return the integral, taking care if we are dealing with a Derivative or with a proper DiracDelta. 2) The expression is not simple (i.e. DiracDelta(cos(x))): we can do nothing at all. - If the node is a multiplication node having a DiracDelta term: First we expand it. If the expansion did work, the we try to integrate the expansion. If not, we try to extract a simple DiracDelta term, then we have two cases: 1) We have a simple DiracDelta term, so we return the integral. 2) We didn't have a simple term, but we do have an expression with simplified DiracDelta terms, so we integrate this expression. Examples ======== >>> from diofant.abc import x, y, z >>> from diofant.integrals.deltafunctions import deltaintegrate >>> from diofant import sin, cos, DiracDelta, Heaviside >>> deltaintegrate(x*sin(x)*cos(x)*DiracDelta(x - 1), x) sin(1)*cos(1)*Heaviside(x - 1) >>> deltaintegrate(y**2*DiracDelta(x - z)*DiracDelta(y - z), y) z**2*DiracDelta(x - z)*Heaviside(y - z) See Also ======== diofant.functions.special.delta_functions.DiracDelta diofant.integrals.integrals.Integral """ if not f.has(DiracDelta): return from diofant.integrals import Integral, integrate from diofant.solvers import solve # g(x) = DiracDelta(h(x)) if f.func == DiracDelta: h = f.simplify(x) if h == f: # can't simplify the expression # FIXME: the second term tells whether is DeltaDirac or Derivative # For integrating derivatives of DiracDelta we need the chain rule if f.is_simple(x): if (len(f.args) <= 1 or f.args[1] == 0): return Heaviside(f.args[0]) else: return (DiracDelta(f.args[0], f.args[1] - 1) / f.args[0].as_poly().LC()) else: # let's try to integrate the simplified expression fh = integrate(h, x) return fh elif f.is_Mul or f.is_Pow: # g(x) = a*b*c*f(DiracDelta(h(x)))*d*e g = f.expand() if f != g: # the expansion worked fh = integrate(g, x) if fh is not None and not isinstance(fh, Integral): return fh else: # no expansion performed, try to extract a simple DiracDelta term dg, rest_mult = change_mul(f, x) if not dg: if rest_mult: fh = integrate(rest_mult, x) return fh else: dg = dg.simplify(x) if dg.is_Mul: # Take out any extracted factors dg, rest_mult_2 = change_mul(dg, x) rest_mult = rest_mult * rest_mult_2 point = solve(dg.args[0], x)[0] return (rest_mult.subs(x, point) * Heaviside(x - point)) return
def continued_fraction_reduce(cf): """Reduce a continued fraction to a rational or quadratic irrational. Compute the rational or quadratic irrational number from its terminating or periodic continued fraction expansion. The continued fraction expansion (cf) should be supplied as a terminating iterator supplying the terms of the expansion. For terminating continued fractions, this is equivalent to ``list(continued_fraction_convergents(cf))[-1]``, only a little more efficient. If the expansion has a repeating part, a list of the repeating terms should be returned as the last element from the iterator. This is the format returned by continued_fraction_periodic. For quadratic irrationals, returns the largest solution found, which is generally the one sought, if the fraction is in canonical form (all terms positive except possibly the first). Examples ======== >>> from diofant.ntheory.continued_fraction import continued_fraction_reduce >>> continued_fraction_reduce([1, 2, 3, 4, 5]) 225/157 >>> continued_fraction_reduce([-2, 1, 9, 7, 1, 2]) -256/233 >>> continued_fraction_reduce([2, 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8]).n(10) 2.718281835 >>> continued_fraction_reduce([1, 4, 2, [3, 1]]) (sqrt(21) + 287)/238 >>> continued_fraction_reduce([[1]]) 1/2 + sqrt(5)/2 >>> from diofant.ntheory.continued_fraction import continued_fraction_periodic >>> continued_fraction_reduce(continued_fraction_periodic(8, 5, 13)) (sqrt(13) + 8)/5 See Also ======== continued_fraction_periodic """ from diofant.core.symbol import Dummy from diofant.solvers import solve period = [] x = Dummy('x') def untillist(cf): for nxt in cf: if isinstance(nxt, list): period.extend(nxt) yield x break yield nxt a = Integer(0) for a in continued_fraction_convergents(untillist(cf)): pass if period: y = Dummy('y') solns = solve(continued_fraction_reduce(period + [y]) - y, y) solns.sort() pure = solns[-1] return a.subs(x, pure).radsimp() else: return a
def rsolve_poly(coeffs, f, n, **hints): """ Given linear recurrence operator `\operatorname{L}` of order `k` with polynomial coefficients and inhomogeneous equation `\operatorname{L} y = f`, where `f` is a polynomial, we seek for all polynomial solutions over field `K` of characteristic zero. The algorithm performs two basic steps: (1) Compute degree `N` of the general polynomial solution. (2) Find all polynomials of degree `N` or less of `\operatorname{L} y = f`. There are two methods for computing the polynomial solutions. If the degree bound is relatively small, i.e. it's smaller than or equal to the order of the recurrence, then naive method of undetermined coefficients is being used. This gives system of algebraic equations with `N+1` unknowns. In the other case, the algorithm performs transformation of the initial equation to an equivalent one, for which the system of algebraic equations has only `r` indeterminates. This method is quite sophisticated (in comparison with the naive one) and was invented together by Abramov, Bronstein and Petkovšek. It is possible to generalize the algorithm implemented here to the case of linear q-difference and differential equations. Lets say that we would like to compute `m`-th Bernoulli polynomial up to a constant. For this we can use `b(n+1) - b(n) = m n^{m-1}` recurrence, which has solution `b(n) = B_m + C`. For example: >>> from diofant import Symbol, rsolve_poly >>> n = Symbol('n', integer=True) >>> rsolve_poly([-1, 1], 4*n**3, n) C0 + n**4 - 2*n**3 + n**2 References ========== .. [1] S. A. Abramov, M. Bronstein and M. Petkovšek, On polynomial solutions of linear operator equations, in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York, 1995, 290-296. .. [2] M. Petkovšek, Hypergeometric solutions of linear recurrences with polynomial coefficients, J. Symbolic Computation, 14 (1992), 243-264. .. [3] M. Petkovšek, H. S. Wilf, D. Zeilberger, A = B, 1996. """ f = sympify(f) if not f.is_polynomial(n): return homogeneous = f.is_zero r = len(coeffs) - 1 coeffs = [Poly(coeff, n) for coeff in coeffs] g = gcd_list(coeffs + [f], n, polys=True) if not g.is_ground: coeffs = [quo(c, g, n, polys=False) for c in coeffs] f = quo(f, g, n, polys=False) polys = [Poly(0, n)] * (r + 1) terms = [(S.Zero, S.NegativeInfinity)] * (r + 1) for i in range(0, r + 1): for j in range(i, r + 1): polys[i] += coeffs[j] * binomial(j, i) if not polys[i].is_zero: (exp, ), coeff = polys[i].LT() terms[i] = (coeff, exp) d = b = terms[0][1] for i in range(1, r + 1): if terms[i][1] > d: d = terms[i][1] if terms[i][1] - i > b: b = terms[i][1] - i d, b = int(d), int(b) x = Dummy('x') degree_poly = S.Zero for i in range(0, r + 1): if terms[i][1] - i == b: degree_poly += terms[i][0] * FallingFactorial(x, i) nni_roots = list( roots(degree_poly, x, filter='Z', predicate=lambda r: r >= 0).keys()) if nni_roots: N = [max(nni_roots)] else: N = [] if homogeneous: N += [-b - 1] else: N += [f.as_poly(n).degree() - b, -b - 1] N = int(max(N)) if N < 0: if homogeneous: if hints.get('symbols', False): return S.Zero, [] else: return S.Zero else: return if N <= r: C = [] y = E = S.Zero for i in range(0, N + 1): C.append(Symbol('C' + str(i))) y += C[i] * n**i for i in range(0, r + 1): E += coeffs[i].as_expr() * y.subs(n, n + i) solutions = solve_undetermined_coeffs(E - f, C, n) if solutions is not None: C = [c for c in C if (c not in solutions)] result = y.subs(solutions) else: return # TBD else: A = r U = N + A + b + 1 nni_roots = list( roots(polys[r], filter='Z', predicate=lambda r: r >= 0).keys()) if nni_roots != []: a = max(nni_roots) + 1 else: a = S.Zero def _zero_vector(k): return [S.Zero] * k def _one_vector(k): return [S.One] * k def _delta(p, k): B = S.One D = p.subs(n, a + k) for i in range(1, k + 1): B *= -Rational(k - i + 1, i) D += B * p.subs(n, a + k - i) return D alpha = {} for i in range(-A, d + 1): I = _one_vector(d + 1) for k in range(1, d + 1): I[k] = I[k - 1] * (x + i - k + 1) / k alpha[i] = S.Zero for j in range(0, A + 1): for k in range(0, d + 1): B = binomial(k, i + j) D = _delta(polys[j].as_expr(), k) alpha[i] += I[k] * B * D V = Matrix(U, A, lambda i, j: int(i == j)) if homogeneous: for i in range(A, U): v = _zero_vector(A) for k in range(1, A + b + 1): if i - k < 0: break B = alpha[k - A].subs(x, i - k) for j in range(0, A): v[j] += B * V[i - k, j] denom = alpha[-A].subs(x, i) for j in range(0, A): V[i, j] = -v[j] / denom else: G = _zero_vector(U) for i in range(A, U): v = _zero_vector(A) g = S.Zero for k in range(1, A + b + 1): if i - k < 0: break B = alpha[k - A].subs(x, i - k) for j in range(0, A): v[j] += B * V[i - k, j] g += B * G[i - k] denom = alpha[-A].subs(x, i) for j in range(0, A): V[i, j] = -v[j] / denom G[i] = (_delta(f, i - A) - g) / denom P, Q = _one_vector(U), _zero_vector(A) for i in range(1, U): P[i] = (P[i - 1] * (n - a - i + 1) / i).expand() for i in range(0, A): Q[i] = Add(*[(v * p).expand() for v, p in zip(V[:, i], P)]) if not homogeneous: h = Add(*[(g * p).expand() for g, p in zip(G, P)]) C = [Symbol('C' + str(i)) for i in range(0, A)] def g(i): return Add(*[c * _delta(q, i) for c, q in zip(C, Q)]) if homogeneous: E = [g(i) for i in range(N + 1, U)] else: E = [g(i) + _delta(h, i) for i in range(N + 1, U)] if E != []: solutions = solve(E, *C) if not solutions: if homogeneous: if hints.get('symbols', False): return S.Zero, [] else: return S.Zero else: return else: solutions = {} if homogeneous: result = S.Zero else: result = h for c, q in list(zip(C, Q)): if c in solutions: s = solutions[c] * q C.remove(c) else: s = c * q result += s.expand() if hints.get('symbols', False): return result, C else: return result
def findrecur(self, F=Function('F'), n=None): """Find a recurrence formula for the summand of the sum. Given a sum `f(n) = \sum_k F(n, k)`, where `F(n, k)` is doubly hypergeometric (that's, both `F(n + 1, k)/F(n, k)` and `F(n, k + 1)/F(n, k)` are rational functions of `n` and `k`), we find a recurrence for the summand `F(n, k)` of the form .. math:: \sum_{i=0}^I\sum_{j=0}^J a_{i,j}F(n - j, k - i) = 0 Examples ======== >>> from diofant import symbols, factorial, oo >>> n, k = symbols('n, k', integer=True) >>> s = Sum(factorial(n)/(factorial(k)*factorial(n - k)), (k, 0, oo)) >>> s.findrecur() -F(n, k) + F(n - 1, k) + F(n - 1, k - 1) Notes ===== We use Sister Celine's algorithm, see [1]_. References ========== .. [1] M. Petkovšek, H. S. Wilf, D. Zeilberger, A = B, 1996, Ch. 4. """ from diofant import expand_func, gamma, factor, Mul from diofant.polys import together from diofant.simplify import collect if len(self.variables) > 1: raise ValueError else: if self.limits[0][1:] != (S.Zero, S.Infinity): raise ValueError k = self.variables[0] if not n: try: n = (self.function.free_symbols - {k}).pop() except KeyError: raise ValueError a = Function('a') def f(i, j): return self.function.subs([(n, i), (k, j)]) I, J, step = 0, 1, 1 y, x, sols = S.Zero, [], {} while not any(v for a, v in sols.items()): if step % 2 != 0: dy = sum(a(I, j) * f(n - j, k - I) / f(n, k) for j in range(J)) dx = [a(I, j) for j in range(J)] I += 1 else: dy = sum(a(i, J) * f(n - J, k - i) / f(n, k) for i in range(I)) dx = [a(i, J) for i in range(I)] J += 1 step += 1 y += expand_func(dy.rewrite(gamma)) x += dx t = together(y) numer = t.as_numer_denom()[0] numer = Mul( *[t for t in factor(numer).as_coeff_mul()[1] if t.has(a)]) if not numer.is_rational_function(n, k): raise ValueError() z = collect(numer, k) eq = z.as_poly(k).all_coeffs() sols = dict(solve(eq, *x)) y = sum(a(i, j) * F(n - j, k - i) for i in range(I) for j in range(J)) y = y.subs(sols).subs(map(lambda a: (a, 1), x)) return y if y else None
def intersection(self, o): """The intersection with another geometrical entity. Parameters ========== o : Point or LinearEntity3D Returns ======= intersection : list of geometrical entities See Also ======== diofant.geometry.point.Point3D Examples ======== >>> from diofant import Point3D, Line3D, Segment3D >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(1, 1, 1), Point3D(7, 7, 7) >>> l1 = Line3D(p1, p2) >>> l1.intersection(p3) [Point3D(7, 7, 7)] >>> l1 = Line3D(Point3D(4,19,12), Point3D(5,25,17)) >>> l2 = Line3D(Point3D(-3, -15, -19), direction_ratio=[2,8,8]) >>> l1.intersection(l2) [Point3D(1, 1, -3)] >>> p6, p7 = Point3D(0, 5, 2), Point3D(2, 6, 3) >>> s1 = Segment3D(p6, p7) >>> l1.intersection(s1) [] """ if isinstance(o, Point3D): if o in self: return [o] else: return [] elif isinstance(o, LinearEntity3D): if self == o: return [self] elif self.is_parallel(o): if isinstance(self, Line3D): if o.p1 in self: return [o] return [] elif isinstance(self, Ray3D): if isinstance(o, Ray3D): # case 1, rays in the same direction if self.xdirection == o.xdirection and \ self.ydirection == o.ydirection and \ self.zdirection == o.zdirection: return [self] if (self.source in o) else [o] # case 2, rays in the opposite directions else: if o.source in self: if self.source == o.source: return [self.source] return [Segment3D(o.source, self.source)] return [] elif isinstance(o, Segment3D): if o.p1 in self: if o.p2 in self: return [o] return [Segment3D(o.p1, self.source)] elif o.p2 in self: return [Segment3D(o.p2, self.source)] return [] elif isinstance(self, Segment3D): if isinstance(o, Segment3D): # A reminder that the points of Segments are ordered # in such a way that the following works. See # Segment3D.__new__ for details on the ordering. if self.p1 not in o: if self.p2 not in o: # Neither of the endpoints are in o so either # o is contained in this segment or it isn't if o in self: return [o] return [] else: # p1 not in o but p2 is. Either there is a # segment as an intersection, or they only # intersect at an endpoint if self.p2 == o.p1: return [o.p1] return [Segment3D(o.p1, self.p2)] elif self.p2 not in o: # p2 not in o but p1 is. Either there is a # segment as an intersection, or they only # intersect at an endpoint if self.p1 == o.p2: return [o.p2] return [Segment3D(o.p2, self.p1)] # Both points of self in o so the whole segment # is in o return [self] else: # unrecognized LinearEntity raise NotImplementedError else: # If the lines are not parallel then solve their arbitrary points # to obtain the point of intersection t = t1, t2 = Dummy(), Dummy() a = self.arbitrary_point(t1) b = o.arbitrary_point(t2) dx = a.x - b.x c = solve([dx, a.y - b.y], t) d = solve([dx, a.z - b.z], t) if len(c) == 1 and len(d) == 1: return [] e = a.subs(t1, c[t1]) if e in self and e in o: return [e] else: return [] return o.intersection(self)
def rsolve(f, y, init=None): """ Solve univariate recurrence with rational coefficients. Given `k`-th order linear recurrence `\operatorname{L} y = f`, or equivalently: .. math:: a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + \ldots + a_{0}(n) y(n) = f(n) where `a_{i}(n)`, for `i=0, \ldots, k`, are polynomials or rational functions in `n`, and `f` is a hypergeometric function or a sum of a fixed number of pairwise dissimilar hypergeometric terms in `n`, finds all solutions or returns ``None``, if none were found. Initial conditions can be given as a dictionary in two forms: (1) ``{ n_0 : v_0, n_1 : v_1, ..., n_m : v_m }`` (2) ``{ y(n_0) : v_0, y(n_1) : v_1, ..., y(n_m) : v_m }`` or as a list ``L`` of values: ``L = [ v_0, v_1, ..., v_m ]`` where ``L[i] = v_i``, for `i=0, \ldots, m`, maps to `y(n_i)`. Examples ======== Lets consider the following recurrence: .. math:: (n - 1) y(n + 2) - (n^2 + 3 n - 2) y(n + 1) + 2 n (n + 1) y(n) = 0 >>> from diofant import Function, rsolve >>> from diofant.abc import n >>> y = Function('y') >>> f = (n - 1)*y(n + 2) - (n**2 + 3*n - 2)*y(n + 1) + 2*n*(n + 1)*y(n) >>> rsolve(f, y(n)) 2**n*C0 + C1*factorial(n) >>> rsolve(f, y(n), { y(0):0, y(1):3 }) 3*2**n - 3*factorial(n) See Also ======== rsolve_poly, rsolve_ratio, rsolve_hyper """ if isinstance(f, Equality): f = f.lhs - f.rhs f = f.expand() n = y.args[0] h_part = defaultdict(lambda: S.Zero) i_part = S.Zero for h, c in f.collect(y.func(Wild('n')), evaluate=False).items(): if h.func == y.func: k = Wild('k', exclude=(n, )) r = h.args[0].match(n + k) if r: c = simplify(c) if not c.is_rational_function(n): raise ValueError( "Rational function of '%s' expected, got '%s'" % (n, c)) h_part[int(r[k])] = c else: raise ValueError("'%s(%s + Integer)' expected, got '%s'" % (y.func, n, h)) else: i_term = h * c if i_term.find(y.func(Wild('k'))): raise ValueError( "Linear recurrence for '%s' expected, got '%s'" % (y.func, f)) i_part -= i_term if not i_part.is_rational_function(n): raise ValueError( "Inhomogeneous part should be a rational function of '%s', got '%s'" % (n, i_part)) k_min, k_max = min(h_part.keys()), max(h_part.keys()) if k_min < 0: return rsolve(f.subs(n, n + abs(k_min)), y, init) i_numer, i_denom = i_part.as_numer_denom() common = lcm_list([x.as_numer_denom()[1] for x in h_part.values()] + [i_denom]) if common is not S.One: for k, coeff in h_part.items(): numer, denom = coeff.as_numer_denom() h_part[k] = numer * quo(common, denom, n) i_part = i_numer * quo(common, i_denom, n) coeffs = [h_part[i] for i in range(k_max + 1)] result = rsolve_hyper(coeffs, i_part, n, symbols=True) if result is None: return solution, symbols = result if init == {} or init == []: init = None if symbols and init is not None: if type(init) is list: init = {i: init[i] for i in range(len(init))} equations = [] for k, v in init.items(): try: i = int(k) except TypeError: if k.is_Function and k.func == y.func: i = int(k.args[0]) else: raise ValueError( "Integer or term '%s(Integer)' expected, got '%s'" % (y.func, k)) try: eq = solution.limit(n, i) - v except NotImplementedError: eq = solution.subs(n, i) - v equations.append(eq) result = solve(equations, *symbols) if not result: return else: solution = solution.subs(result) return solution