def __getitem__(self, key): """Return portion of self defined by key. If the key involves a slice then a list will be returned (if key is a single slice) or a matrix (if key was a tuple involving a slice). Examples ======== >>> from diofant import Matrix, I >>> m = Matrix([ ... [1, 2 + I], ... [3, 4 ]]) If the key is a tuple that doesn't involve a slice then that element is returned: >>> m[1, 0] 3 When a tuple key involves a slice, a matrix is returned. Here, the first column is selected (all rows, column 0): >>> m[:, 0] Matrix([ [1], [3]]) If the slice is not a tuple then it selects from the underlying list of elements that are arranged in row order and a list is returned if a slice is involved: >>> m[0] 1 >>> m[::2] [1, 3] """ if isinstance(key, tuple): i, j = key try: i, j = self.key2ij(key) return self._mat[i * self.cols + j] except (TypeError, IndexError): if isinstance(i, slice): i = range(self.rows)[i] elif is_sequence(i): pass else: i = [i] if isinstance(j, slice): j = range(self.cols)[j] elif is_sequence(j): pass else: j = [j] return self.extract(i, j) else: # row-wise decomposition of matrix if isinstance(key, slice): return self._mat[key] return self._mat[a2idx(key)]
def test_iterable_is_sequence(): ordered = [[], (), Tuple(), Matrix([[]])] unordered = [set()] not_diofant_iterable = [{}, ''] assert all(is_sequence(i) for i in ordered) assert all(not is_sequence(i) for i in unordered) assert all(iterable(i) for i in ordered + unordered) assert all(not iterable(i) for i in not_diofant_iterable) assert all(iterable(i, exclude=None) for i in not_diofant_iterable)
def test_iterable_is_sequence(): ordered = [[], (), Tuple(), Matrix([[]])] unordered = [set()] not_diofant_iterable = [{}, ''] assert all(is_sequence(i) for i in ordered) assert all(not is_sequence(i) for i in unordered) assert all(iterable(i) for i in ordered + unordered) assert all(not iterable(i) for i in not_diofant_iterable) assert all(iterable(i, exclude=None) for i in not_diofant_iterable)
def __new__(cls, function, limits): fun = sympify(function) if not is_sequence(fun) or len(fun) != 2: raise ValueError("Function argument should be (x(t), y(t)) " "but got %s" % str(function)) if not is_sequence(limits) or len(limits) != 3: raise ValueError("Limit argument should be (t, tmin, tmax) " "but got %s" % str(limits)) return GeometryEntity.__new__(cls, Tuple(*fun), Tuple(*limits))
def _eval_subs(self, old, new): from diofant.geometry.point import Point, Point3D if is_sequence(old) or is_sequence(new): if isinstance(self, Point3D): old = Point3D(old) new = Point3D(new) else: old = Point(old) new = Point(new) return self._subs(old, new)
def distance(self, o): """ Finds the shortest distance between a line and a point. Raises ====== NotImplementedError is raised if o is not a Point Examples ======== >>> from diofant import Point, Line >>> p1, p2 = Point(0, 0), Point(1, 1) >>> s = Line(p1, p2) >>> s.distance(Point(-1, 1)) sqrt(2) >>> s.distance((-1, 2)) 3*sqrt(2)/2 """ if not isinstance(o, Point): if is_sequence(o): o = Point(o) a, b, c = self.coefficients if 0 in (a, b): return self.perpendicular_segment(o).length m = self.slope x = o.x y = m*x - c/b return abs(factor_terms(o.y - y))/sqrt(1 + m**2)
def distance(self, o): """ Finds the shortest distance between the ray and a point. Raises ====== NotImplementedError is raised if o is not a Point Examples ======== >>> from diofant import Point, Ray >>> p1, p2 = Point(0, 0), Point(1, 1) >>> s = Ray(p1, p2) >>> s.distance(Point(-1, -1)) sqrt(2) >>> s.distance((-1, 2)) 3*sqrt(2)/2 """ if not isinstance(o, Point): if is_sequence(o): o = Point(o) s = self.perpendicular_segment(o) if isinstance(s, Point): if self.contains(s): return S.Zero else: # since arg-order is arbitrary, find the non-o point non_o = s.p1 if s.p1 != o else s.p2 if self.contains(non_o): return Line(self).distance(o) # = s.length but simpler # the following applies when neither of the above apply return self.source.distance(o)
def distance(self, o): """ Finds the shortest distance between a line segment and a point. Raises ====== NotImplementedError is raised if o is not a Point Examples ======== >>> from diofant import Point, Segment >>> p1, p2 = Point(0, 1), Point(3, 4) >>> s = Segment(p1, p2) >>> s.distance(Point(10, 15)) sqrt(170) >>> s.distance((0, 12)) sqrt(73) """ if is_sequence(o): o = Point(o) if isinstance(o, Point): seg_vector = self.p2 - self.p1 pt_vector = o - self.p1 t = seg_vector.dot(pt_vector)/self.length**2 if t >= 1: distance = Point.distance(self.p2, o) elif t <= 0: distance = Point.distance(self.p1, o) else: distance = Point.distance( self.p1 + Point(t*seg_vector.x, t*seg_vector.y), o) return distance raise NotImplementedError()
def distance(self, o): """ Finds the shortest distance between the ray and a point. Raises ====== NotImplementedError is raised if o is not a Point Examples ======== >>> from diofant import Point3D, Ray3D >>> p1, p2 = Point3D(0, 0, 0), Point3D(1, 1, 2) >>> s = Ray3D(p1, p2) >>> s.distance(Point3D(-1, -1, 2)) sqrt(6) >>> s.distance((-1, -1, 2)) sqrt(6) """ if not isinstance(o, Point3D): if is_sequence(o): o = Point3D(o) if o in self: return S.Zero s = self.perpendicular_segment(o) if not isinstance(s, Point3D): non_o = s.p1 if s.p1 == o else s.p2 if self.contains(non_o): return Line3D(self).distance(o) # = s.length but simpler # the following applies when neither of the above apply return self.source.distance(o)
def contains(self, other): """ Is the other GeometryEntity contained within this Segment? Examples ======== >>> from diofant import Point3D, Segment3D >>> p1, p2 = Point3D(0, 1, 1), Point3D(3, 4, 5) >>> s = Segment3D(p1, p2) >>> s2 = Segment3D(p2, p1) >>> s.contains(s2) True """ if is_sequence(other): other = Point3D(other) if isinstance(other, Segment3D): return other.p1 in self and other.p2 in self elif isinstance(other, Point3D): if Point3D.are_collinear(self.p1, self.p2, other): if other.distance(self.p1) + other.distance( self.p2) == self.length: return True else: return False return False
def __new__(cls, label, range=None, **kw_args): from diofant.utilities.misc import filldedent if isinstance(label, str): label = Symbol(label, integer=True) label, range = list(map(sympify, (label, range))) if not label.is_integer: raise TypeError("Idx object requires an integer label.") elif is_sequence(range): if len(range) != 2: raise ValueError( filldedent(""" Idx range tuple must have length 2, but got %s""" % len(range))) for bound in range: if not (bound.is_integer or abs(bound) is S.Infinity): raise TypeError("Idx object requires integer bounds.") args = label, Tuple(*range) elif isinstance(range, Expr): if not (range.is_integer or range is S.Infinity): raise TypeError("Idx object requires an integer dimension.") args = label, Tuple(0, range - 1) elif range: raise TypeError( filldedent(""" The range must be an ordered iterable or integer Diofant expression.""")) else: args = label, obj = Expr.__new__(cls, *args, **kw_args) return obj
def distance(self, o): """ Finds the shortest distance between a line and a point. Raises ====== NotImplementedError is raised if o is not an instance of Point3D Examples ======== >>> from diofant import Point3D, Line3D >>> p1, p2 = Point3D(0, 0, 0), Point3D(1, 1, 1) >>> s = Line3D(p1, p2) >>> s.distance(Point3D(-1, 1, 1)) 2*sqrt(6)/3 >>> s.distance((-1, 1, 1)) 2*sqrt(6)/3 """ if not isinstance(o, Point3D): if is_sequence(o): o = Point3D(o) if o in self: return S.Zero a = self.perpendicular_segment(o).length return a
def idiff(eq, y, x, n=1): """Return ``dy/dx`` assuming that ``eq == 0``. Parameters ========== y : the dependent variable or a list of dependent variables (with y first) x : the variable that the derivative is being taken with respect to n : the order of the derivative (default is 1) Examples ======== >>> from diofant.abc import x, y, a >>> from diofant.geometry.util import idiff >>> circ = x**2 + y**2 - 4 >>> idiff(circ, y, x) -x/y >>> idiff(circ, y, x, 2).simplify() -(x**2 + y**2)/y**3 Here, ``a`` is assumed to be independent of ``x``: >>> idiff(x + a + y, y, x) -1 Now the x-dependence of ``a`` is made explicit by listing ``a`` after ``y`` in a list. >>> idiff(x + a + y, [y, a], x) -Derivative(a, x) - 1 See Also ======== diofant.core.function.Derivative: represents unevaluated derivatives diofant.core.function.diff: explicitly differentiates wrt symbols """ if is_sequence(y): dep = set(y) y = y[0] elif isinstance(y, (Dummy, Symbol)): dep = {y} else: raise ValueError("expecting x-dependent symbol(s) but got: %s" % y) f = {s: Function(s.name)(x) for s in eq.free_symbols if s != x and s in dep} dydx = Function(y.name)(x).diff(x) eq = eq.subs(f) derivs = {} for i in range(n): yp = solve(eq.diff(x), dydx)[0].subs(derivs) if i == n - 1: return yp.subs([(v, k) for k, v in f.items()]) derivs[dydx] = yp eq = dydx - yp dydx = dydx.diff(x)
def __new__(cls, *args, **kwargs): from diofant.geometry.point import Point args = [ Tuple(*a) if is_sequence(a) and not isinstance(a, Point) else sympify(a) for a in args ] return Basic.__new__(cls, *args)
def contains(self, o): """ Is other GeometryEntity contained in this Ray? Examples ======== >>> from diofant import Ray,Point,Segment >>> p1, p2 = Point(0, 0), Point(4, 4) >>> r = Ray(p1, p2) >>> r.contains(p1) True >>> r.contains((1, 1)) True >>> r.contains((1, 3)) False >>> s = Segment((1, 1), (2, 2)) >>> r.contains(s) True >>> s = Segment((1, 2), (2, 5)) >>> r.contains(s) False >>> r1 = Ray((2, 2), (3, 3)) >>> r.contains(r1) True >>> r1 = Ray((2, 2), (3, 5)) >>> r.contains(r1) False """ if isinstance(o, Ray): return (Point.is_collinear(self.p1, self.p2, o.p1, o.p2) and self.xdirection == o.xdirection and self.ydirection == o.ydirection) elif isinstance(o, Segment): return o.p1 in self and o.p2 in self elif is_sequence(o): o = Point(o) if isinstance(o, Point): if Point.is_collinear(self.p1, self.p2, o): if self.xdirection is S.Infinity: rv = o.x >= self.source.x elif self.xdirection is S.NegativeInfinity: rv = o.x <= self.source.x elif self.ydirection is S.Infinity: rv = o.y >= self.source.y else: rv = o.y <= self.source.y if rv == S.true or rv == S.false: return bool(rv) raise Undecidable( 'Cannot determine if %s is in %s' % (o, self)) else: # Points are not collinear, so the rays are not parallel # and hence it is impossible for self to contain o return False # No other known entity can be contained in a Ray return False
def __getitem__(self, indices, **kw_args): if is_sequence(indices): # Special case needed because M[*my_tuple] is a syntax error. if self.shape and len(self.shape) != len(indices): raise IndexException("Rank mismatch.") return Indexed(self, *indices, **kw_args) else: if self.shape and len(self.shape) != 1: raise IndexException("Rank mismatch.") return Indexed(self, indices, **kw_args)
def convert(self, element, base=None): """Convert ``element`` to ``self.dtype``. """ if base is not None: return self.convert_from(element, base) if self.of_type(element): return element from diofant.polys.domains import PythonIntegerRing, GMPYIntegerRing, GMPYRationalField, RealField, ComplexField if isinstance(element, int): return self.convert_from(element, PythonIntegerRing()) if HAS_GMPY: integers = GMPYIntegerRing() if isinstance(element, integers.tp): return self.convert_from(element, integers) rationals = GMPYRationalField() if isinstance(element, rationals.tp): return self.convert_from(element, rationals) if isinstance(element, float): parent = RealField(tol=False) return self.convert_from(parent(element), parent) if isinstance(element, complex): parent = ComplexField(tol=False) return self.convert_from(parent(element), parent) if isinstance(element, DomainElement): return self.convert_from(element, element.parent()) # TODO: implement this in from_ methods if self.is_Numerical and getattr(element, 'is_ground', False): return self.convert(element.LC()) if isinstance(element, Basic): try: return self.from_diofant(element) except (TypeError, ValueError): pass else: # TODO: remove this branch if not is_sequence(element): try: element = sympify(element) if isinstance(element, Basic): return self.from_diofant(element) except (TypeError, ValueError): pass raise CoercionFailed("can't convert %s of type %s to %s" % (element, type(element), self))
def maximize(f, *v): """ Maximizes `f` with respect to given variables `v`. See Also ======== minimize """ f = set(map(sympify, f if is_sequence(f) else [f])) fv, d = minimize([e if e.is_Relational else -e for e in f], *v) return -fv, d
def __new__(cls, label, shape=None, **kw_args): if isinstance(label, str): label = Symbol(label) elif isinstance(label, (Dummy, Symbol)): pass else: raise TypeError("Base label should be a string or Symbol.") obj = Expr.__new__(cls, label, **kw_args) if is_sequence(shape): obj._shape = Tuple(*shape) else: obj._shape = sympify(shape) return obj
def __getitem__(self, key): if isinstance(key, tuple): i, j = key try: i, j = self.key2ij(key) return self._smat.get((i, j), S.Zero) except (TypeError, IndexError): if isinstance(i, slice): i = range(self.rows)[i] elif is_sequence(i): pass else: if i >= self.rows: raise IndexError('Row index out of bounds') i = [i] if isinstance(j, slice): j = range(self.cols)[j] elif is_sequence(j): pass else: if j >= self.cols: raise IndexError('Col index out of bounds') j = [j] return self.extract(i, j) # check for single arg, like M[:] or M[3] if isinstance(key, slice): lo, hi = key.indices(len(self))[:2] L = [] for i in range(lo, hi): m, n = divmod(i, self.cols) L.append(self._smat.get((m, n), S.Zero)) return L i, j = divmod(a2idx(key, len(self)), self.cols) return self._smat.get((i, j), S.Zero)
def _randrange(seed=None): """Return a randrange generator. ``seed`` can be o None - return randomly seeded generator o int - return a generator seeded with the int o list - the values to be returned will be taken from the list in the order given; the provided list is not modified. Examples ======== >>> from diofant.utilities.randtest import _randrange >>> rr = _randrange() >>> rr(1000) # doctest: +SKIP 999 >>> rr = _randrange(3) >>> rr(1000) # doctest: +SKIP 238 >>> rr = _randrange([0, 5, 1, 3, 4]) >>> rr(3), rr(3) (0, 1) """ if seed is None: return random.randrange elif isinstance(seed, int): return random.Random(seed).randrange elif is_sequence(seed): seed = list(seed) # make a copy seed.reverse() def give(a, b=None, seq=seed): if b is None: a, b = 0, a a, b = as_int(a), as_int(b) w = b - a if w < 1: raise ValueError('_randrange got empty range') try: x = seq.pop() except AttributeError: raise ValueError('_randrange expects a list-like sequence') except IndexError: raise ValueError('_randrange sequence was too short') if a <= x < b: return x else: return give(a, b, seq) return give else: raise ValueError('_randrange got an unexpected seed')
def line_integrate(field, curve, vars): """line_integrate(field, Curve, variables) Compute the line integral. Examples ======== >>> from diofant import Curve, line_integrate, E, ln >>> from diofant.abc import x, y, t >>> C = Curve([E**t + 1, E**t - 1], (t, 0, ln(2))) >>> line_integrate(x + y, C, [x, y]) 3*sqrt(2) See Also ======== diofant.integrals.integrals.integrate diofant.integrals.integrals.Integral """ from diofant.geometry import Curve F = sympify(field) if not F: raise ValueError( "Expecting function specifying field as first argument.") if not isinstance(curve, Curve): raise ValueError("Expecting Curve entity as second argument.") if not is_sequence(vars): raise ValueError("Expecting ordered iterable for variables.") if len(curve.functions) != len(vars): raise ValueError("Field variable size does not match curve dimension.") if curve.parameter in vars: raise ValueError("Curve parameter clashes with field parameters.") # Calculate derivatives for line parameter functions # F(r) -> F(r(t)) and finally F(r(t)*r'(t)) Ft = F dldt = 0 for i, var in enumerate(vars): _f = curve.functions[i] _dn = diff(_f, curve.parameter) # ...arc length dldt = dldt + (_dn * _dn) Ft = Ft.subs(var, _f) Ft = Ft * sqrt(dldt) integral = Integral(Ft, curve.limits).doit(deep=False) return integral
def __init__(self, *args): if len(args) == 1 and isinstance(args[0], SparseMatrix): self.rows = args[0].rows self.cols = args[0].cols self._smat = dict(args[0]._smat) return self._smat = {} if len(args) == 3: self.rows = as_int(args[0]) self.cols = as_int(args[1]) if isinstance(args[2], collections.Callable): op = args[2] for i in range(self.rows): for j in range(self.cols): value = self._sympify( op(self._sympify(i), self._sympify(j))) if value: self._smat[(i, j)] = value elif isinstance(args[2], (dict, Dict)): # manual copy, copy.deepcopy() doesn't work for key in args[2].keys(): v = args[2][key] if v: self._smat[key] = self._sympify(v) elif is_sequence(args[2]): if len(args[2]) != self.rows * self.cols: raise ValueError('List length (%s) != rows*columns (%s)' % (len(args[2]), self.rows * self.cols)) flat_list = args[2] for i in range(self.rows): for j in range(self.cols): value = self._sympify(flat_list[i * self.cols + j]) if value: self._smat[(i, j)] = value else: # handle full matrix forms with _handle_creation_inputs r, c, _list = Matrix._handle_creation_inputs(*args) self.rows = r self.cols = c for i in range(self.rows): for j in range(self.cols): value = _list[self.cols * i + j] if value: self._smat[(i, j)] = value
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 __new__(cls, p1, a=None, b=None, **kwargs): p1 = Point3D(p1) if a and b: p2 = Point3D(a) p3 = Point3D(b) if Point3D.are_collinear(p1, p2, p3): raise ValueError('Enter three non-collinear points') a = p1.direction_ratio(p2) b = p1.direction_ratio(p3) normal_vector = tuple(Matrix(a).cross(Matrix(b))) else: a = kwargs.pop('normal_vector', a) if is_sequence(a) and len(a) == 3: normal_vector = Point3D(a).args else: raise ValueError( filldedent(''' Either provide 3 3D points or a point with a normal vector expressed as a sequence of length 3''')) return GeometryEntity.__new__(cls, p1, normal_vector, **kwargs)
def _process_limits(*symbols): """Process the list of symbols and convert them to canonical limits, storing them as Tuple(symbol, lower, upper). The orientation of the function is also returned when the upper limit is missing so (x, 1, None) becomes (x, None, 1) and the orientation is changed. """ limits = [] orientation = 1 for V in symbols: if isinstance(V, (Dummy, Symbol)): limits.append(Tuple(V)) continue elif is_sequence(V, Tuple): V = sympify(flatten(V)) if V[0].is_Symbol: newsymbol = V[0] if len(V) == 2 and isinstance(V[1], Interval): V[1:] = [V[1].start, V[1].end] if len(V) == 3: if V[1] is None and V[2] is not None: nlim = [V[2]] elif V[1] is not None and V[2] is None: orientation *= -1 nlim = [V[1]] elif V[1] is None and V[2] is None: nlim = [] else: nlim = V[1:] limits.append(Tuple(newsymbol, *nlim)) continue elif len(V) == 1 or (len(V) == 2 and V[1] is None): limits.append(Tuple(newsymbol)) continue elif len(V) == 2: limits.append(Tuple(newsymbol, V[1])) continue raise ValueError('Invalid limits given: %s' % str(symbols)) return limits, orientation
def copyin_list(self, key, value): """Copy in elements from a list. Parameters ========== key : slice The section of this matrix to replace. value : iterable The iterable to copy values from. Examples ======== >>> from diofant.matrices import eye >>> I = eye(3) >>> I[:2, 0] = [1, 2] # col >>> I Matrix([ [1, 0, 0], [2, 1, 0], [0, 0, 1]]) >>> I[1, :2] = [[3, 4]] >>> I Matrix([ [1, 0, 0], [3, 4, 0], [0, 0, 1]]) See Also ======== diofant.matrices.dense.MutableDenseMatrix.copyin_matrix """ if not is_sequence(value): raise TypeError("`value` must be an ordered iterable, not %s." % type(value)) return self.copyin_matrix(key, Matrix(value))
def contains(self, o): """Return True if o is on this Line, or False otherwise. Examples ======== >>> from diofant import Line3D >>> a = (0, 0, 0) >>> b = (1, 1, 1) >>> c = (2, 2, 2) >>> l1 = Line3D(a, b) >>> l2 = Line3D(b, a) >>> l1 == l2 False >>> l1 in l2 True """ if is_sequence(o): o = Point3D(o) if isinstance(o, Point3D): sym = list(map(Dummy, 'xyz')) eq = self.equation(*sym) a = [eq[i].subs(sym[i], o.args[i]) for i in range(3)] a = [i for i in a if i != nan] if len(a) == 1: return True first = a.pop(0) for i in a: rv = first.equals(i) if not rv: return rv return True elif not isinstance(o, LinearEntity3D): return False elif isinstance(o, Line3D): return all(i in self for i in o.points)
def contains(self, o): """Is other GeometryEntity contained in this Ray?""" if isinstance(o, Ray3D): return (Point3D.are_collinear(self.p1, self.p2, o.p1, o.p2) and self.xdirection == o.xdirection and self.ydirection == o.ydirection and self.zdirection == o.zdirection) elif isinstance(o, Segment3D): return o.p1 in self and o.p2 in self elif is_sequence(o): o = Point3D(o) if isinstance(o, Point3D): if Point3D.are_collinear(self.p1, self.p2, o): if self.xdirection is S.Infinity: rv = o.x >= self.source.x elif self.xdirection is S.NegativeInfinity: rv = o.x <= self.source.x elif self.ydirection is S.Infinity: rv = o.y >= self.source.y elif self.ydirection is S.NegativeInfinity: rv = o.y <= self.source.y elif self.zdirection is S.Infinity: rv = o.z <= self.source.z else: rv = o.z <= self.source.z if rv == S.true or rv == S.false: return bool(rv) raise Undecidable('Cannot determine if %s is in %s' % (o, self)) else: # Points are not collinear, so the rays are not parallel # and hence it is impossible for self to contain o return False # No other known entity can be contained in a Ray return False
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 __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(isinstance(v, (Dummy, 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 if point[0] in [S.Infinity, S.NegativeInfinity]: s = {k: 1/Dummy() for k in variables} rs = {1/v: 1/k for k, v in s.items()} elif point[0] is not S.Zero: s = {k: Dummy() + point[0] for k in variables} rs = {v - point[0]: k - point[0] for k, v in s.items()} else: s = () rs = () expr = expr.subs(s) if expr.is_Add: from diofant import expand_multinomial expr = expand_multinomial(expr) 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() if expr.is_Add: lst = expr.extract_leading_order(args) expr = Add(*[f.expr for (e, f) in lst]) elif expr: expr = expr.as_leading_term(*args) 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_extended_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_extended_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_extended_real: margs[i] = x**(r*q) expr = Mul(*margs) expr = expr.subs(rs) if expr is S.Zero: return expr if expr.is_Order: expr = expr.expr if not expr.has(*variables): 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