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 shape(self): """Returns a list with dimensions of each index. Dimensions is a property of the array, not of the indices. Still, if the IndexedBase does not define a shape attribute, it is assumed that the ranges of the indices correspond to the shape of the array. >>> from diofant.tensor.indexed import IndexedBase, Idx >>> from diofant import symbols >>> n, m = symbols('n m', integer=True) >>> i = Idx('i', m) >>> j = Idx('j', m) >>> A = IndexedBase('A', shape=(n, n)) >>> B = IndexedBase('B') >>> A[i, j].shape (n, n) >>> B[i, j].shape (m, m) """ from diofant.utilities.misc import filldedent if self.base.shape: return self.base.shape try: return Tuple(*[i.upper - i.lower + 1 for i in self.indices]) except AttributeError: raise IndexException( filldedent(""" Range is not defined for all indices in: %s""" % self)) except TypeError: raise IndexException( filldedent(""" Shape cannot be inferred from Idx with undefined range: %s""" % self))
def _reduce_inequalities(inequalities, symbols): # helper for reduce_inequalities poly_part, pw_part = {}, {} other = [] for inequality in inequalities: if inequality is S.true: continue elif inequality is S.false: return S.false expr, rel = inequality.lhs, inequality.rel_op # rhs is 0 # check for gens using atoms which is more strict than free_symbols to # guard against EX domain which won't be handled by # reduce_rational_inequalities gens = expr.atoms(Dummy, Symbol) if len(gens) == 1: gen = gens.pop() else: common = expr.free_symbols & symbols if len(common) == 1: gen = common.pop() other.append( solve_univariate_inequality(Relational(expr, 0, rel), gen)) continue else: raise NotImplementedError( filldedent(''' inequality has more than one symbol of interest''')) if expr.is_polynomial(gen): poly_part.setdefault(gen, []).append((expr, rel)) else: components = expr.find(lambda u: u.has(gen) and ( u.is_Function or u.is_Pow and not u.exp.is_Integer)) if components and all( isinstance(i, Abs) or isinstance(i, Piecewise) for i in components): pw_part.setdefault(gen, []).append((expr, rel)) else: other.append( solve_univariate_inequality(Relational(expr, 0, rel), gen)) poly_reduced = [] pw_reduced = [] for gen, exprs in poly_part.items(): poly_reduced.append(reduce_rational_inequalities([exprs], gen)) for gen, exprs in pw_part.items(): pw_reduced.append(reduce_piecewise_inequalities(exprs, gen)) return And(*(poly_reduced + pw_reduced + other))
def _calc_limit(a, b): """ replace d with a, using subs if possible, otherwise limit where sign of b is considered """ avals = list({_calc_limit_1(Fi, a, b) for Fi in F}) if len(avals) > 1: raise ValueError( filldedent(''' The mapping between F(x) and f(u) did not give a unique limit.''')) return avals[0]
def __new__(cls, base, *args, **kw_args): from diofant.utilities.misc import filldedent if not args: raise IndexException("Indexed needs at least one index.") if isinstance(base, (str, Symbol)): base = IndexedBase(base) elif not hasattr(base, '__getitem__') and not isinstance( base, IndexedBase): raise TypeError( filldedent(""" Indexed expects string, Symbol or IndexedBase as base.""")) args = list(map(sympify, args)) return Expr.__new__(cls, base, *args, **kw_args)
def copyin_matrix(self, key, value): """Copy in values from a matrix into the given bounds. Parameters ========== key : slice The section of this matrix to replace. value : Matrix The matrix to copy values from. Examples ======== >>> from diofant.matrices import Matrix, eye >>> M = Matrix([[0, 1], [2, 3], [4, 5]]) >>> I = eye(3) >>> I[:3, :2] = M >>> I Matrix([ [0, 1, 0], [2, 3, 0], [4, 5, 1]]) >>> I[0, 1] = M >>> I Matrix([ [0, 0, 1], [2, 2, 3], [4, 4, 5]]) See Also ======== diofant.matrices.dense.MutableDenseMatrix.copyin_list """ rlo, rhi, clo, chi = self.key2bounds(key) shape = value.shape dr, dc = rhi - rlo, chi - clo if shape != (dr, dc): raise ShapeError( filldedent("The Matrix `value` doesn't have the " "same dimensions " "as the in sub-Matrix given by `key`.")) for i in range(value.rows): for j in range(value.cols): self[i + rlo, j + clo] = value[i, j]
def __new__(cls, p1, pt=None, angle=None, **kwargs): p1 = Point(p1) if pt is not None and angle is None: try: p2 = Point(pt) except NotImplementedError: from diofant.utilities.misc import filldedent raise ValueError(filldedent(''' The 2nd argument was not a valid Point; if it was meant to be an angle it should be given with keyword "angle".''')) if p1 == p2: raise ValueError('A Ray requires two distinct points.') elif angle is not None and pt is None: # we need to know if the angle is an odd multiple of pi/2 c = pi_coeff(sympify(angle)) p2 = None if c is not None: if c.is_Rational: if c.q == 2: if c.p == 1: p2 = p1 + Point(0, 1) elif c.p == 3: p2 = p1 + Point(0, -1) elif c.q == 1: if c.p == 0: p2 = p1 + Point(1, 0) elif c.p == 1: p2 = p1 + Point(-1, 0) if p2 is None: c *= S.Pi else: c = angle % (2*S.Pi) if not p2: m = 2*c/S.Pi left = And(1 < m, m < 3) # is it in quadrant 2 or 3? x = Piecewise((-1, left), (Piecewise((0, Eq(m % 1, 0)), (1, True)), True)) y = Piecewise((-tan(c), left), (Piecewise((1, Eq(m, 1)), (-1, Eq(m, 3)), (tan(c), True)), True)) p2 = p1 + Point(x, y) else: raise ValueError('A 2nd point or keyword "angle" must be used.') return LinearEntity.__new__(cls, p1, p2, **kwargs)
def reflect(self, line): """Override GeometryEntity.reflect since the radius is not a GeometryEntity. Examples ======== >>> from diofant import Circle, Line >>> Circle((0, 1), 1).reflect(Line((0, 0), (1, 1))) Circle(Point2D(1, 0), -1) >>> from diofant import Ellipse, Line, Point >>> Ellipse(Point(3, 4), 1, 3).reflect(Line(Point(0, -4), Point(5, 0))) Traceback (most recent call last): ... NotImplementedError: General Ellipse is not supported but the equation of the reflected Ellipse is given by the zeros of: f(x, y) = (9*x/41 + 40*y/41 + 37/41)**2 + (40*x/123 - 3*y/41 - 364/123)**2 - 1 Notes ===== Until the general ellipse (with no axis parallel to the x-axis) is supported a NotImplemented error is raised and the equation whose zeros define the rotated ellipse is given. """ from .util import _uniquely_named_symbol if line.slope in (0, oo): c = self.center c = c.reflect(line) return self.func(c, -self.hradius, self.vradius) else: x, y = [_uniquely_named_symbol(name, self, line) for name in 'xy'] expr = self.equation(x, y) p = Point(x, y).reflect(line) result = expr.subs(zip((x, y), p.args), simultaneous=True) raise NotImplementedError( filldedent( 'General Ellipse is not supported but the equation ' 'of the reflected Ellipse is given by the zeros of: ' + "f(%s, %s) = %s" % (str(x), str(y), str(result))))
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 arbitrary_point(self, parameter='t'): """A parameterized point on the ellipse. Parameters ========== parameter : str, optional Default value is 't'. Returns ======= arbitrary_point : Point Raises ====== ValueError When `parameter` already appears in the functions. See Also ======== diofant.geometry.point.Point Examples ======== >>> from diofant import Point, Ellipse >>> e1 = Ellipse(Point(0, 0), 3, 2) >>> e1.arbitrary_point() Point2D(3*cos(t), 2*sin(t)) """ t = _symbol(parameter) if t.name in (f.name for f in self.free_symbols): raise ValueError( filldedent('Symbol %s already appears in object ' 'and cannot be used as a parameter.' % t.name)) return Point(self.center.x + self.hradius * cos(t), self.center.y + self.vradius * sin(t))
def __str__(self): return '\n%s\n' % filldedent(self.fullMessage)
def reduce_piecewise_inequality(expr, rel, gen): """ Reduce an inequality with nested piecewise functions. Examples ======== >>> from diofant import Abs, Symbol, Piecewise >>> from diofant.solvers.inequalities import reduce_piecewise_inequality >>> x = Symbol('x', real=True) >>> reduce_piecewise_inequality(Abs(x - 5) - 3, '<', x) And(2 < x, x < 8) >>> reduce_piecewise_inequality(Abs(x + 2)*3 - 13, '<', x) And(-19/3 < x, x < 7/3) >>> reduce_piecewise_inequality(Piecewise((1, x < 1), ... (3, True)) - 1, '>', x) 1 <= x See Also ======== reduce_piecewise_inequalities """ if gen.is_extended_real is False: raise TypeError( filldedent(''' can't solve inequalities with piecewise functions containing non-real variables''')) def _bottom_up_scan(expr): exprs = [] if expr.is_Add or expr.is_Mul: op = expr.func for arg in expr.args: _exprs = _bottom_up_scan(arg) if not exprs: exprs = _exprs else: args = [] for expr, conds in exprs: for _expr, _conds in _exprs: args.append((op(expr, _expr), conds + _conds)) exprs = args elif expr.is_Pow: n = expr.exp if not n.is_Integer: raise NotImplementedError("only integer powers are supported") _exprs = _bottom_up_scan(expr.base) for expr, conds in _exprs: exprs.append((expr**n, conds)) elif isinstance(expr, Abs): _exprs = _bottom_up_scan(expr.args[0]) for expr, conds in _exprs: exprs.append((expr, conds + [Ge(expr, 0)])) exprs.append((-expr, conds + [Lt(expr, 0)])) elif isinstance(expr, Piecewise): for a in expr.args: _exprs = _bottom_up_scan(a.expr) for ex, conds in _exprs: if a.cond is not S.true: exprs.append((ex, conds + [a.cond])) else: oconds = [ c[1] for c in expr.args if c[1] is not S.true ] exprs.append( (ex, conds + [And(*[~c for c in oconds])])) else: exprs = [(expr, [])] return exprs exprs = _bottom_up_scan(expr) mapping = {'<': '>', '<=': '>='} inequalities = [] for expr, conds in exprs: if rel not in mapping.keys(): expr = Relational(expr, 0, rel) else: expr = Relational(-expr, 0, mapping[rel]) inequalities.append([expr] + conds) return reduce_rational_inequalities(inequalities, gen)
def reduce_rational_inequalities(exprs, gen, relational=True): """ Reduce a system of rational inequalities with rational coefficients. Examples ======== >>> from diofant import Poly, Symbol >>> from diofant.solvers.inequalities import reduce_rational_inequalities >>> x = Symbol('x', real=True) >>> reduce_rational_inequalities([[x**2 <= 0]], x) Eq(x, 0) >>> reduce_rational_inequalities([[x + 2 > 0]], x) -2 < x >>> reduce_rational_inequalities([[(x + 2, ">")]], x) -2 < x >>> reduce_rational_inequalities([[x + 2]], x) Eq(x, -2) """ exact = True eqs = [] solution = S.Reals if exprs else S.EmptySet for _exprs in exprs: _eqs = [] for expr in _exprs: if isinstance(expr, tuple): expr, rel = expr else: if expr.is_Relational: expr, rel = expr.lhs - expr.rhs, expr.rel_op else: expr, rel = expr, '==' if expr is S.true: numer, denom, rel = S.Zero, S.One, '==' elif expr is S.false: numer, denom, rel = S.One, S.One, '==' else: numer, denom = expr.together().as_numer_denom() try: (numer, denom), opt = parallel_poly_from_expr((numer, denom), gen) except PolynomialError: raise PolynomialError( filldedent(''' only polynomials and rational functions are supported in this context''')) if not opt.domain.is_Exact: numer, denom, exact = numer.to_exact(), denom.to_exact(), False domain = opt.domain.get_exact() if not (domain.is_ZZ or domain.is_QQ): expr = numer / denom expr = Relational(expr, 0, rel) solution &= solve_univariate_inequality(expr, gen, relational=False) else: _eqs.append(((numer, denom), rel)) if _eqs: eqs.append(_eqs) if eqs: solution &= solve_rational_inequalities(eqs) if not exact: solution = solution.evalf() if relational: solution = solution.as_relational(gen) return solution
def transform(self, x, u): r""" Performs a change of variables from `x` to `u` using the relationship given by `x` and `u` which will define the transformations `f` and `F` (which are inverses of each other) as follows: 1) If `x` is a Symbol (which is a variable of integration) then `u` will be interpreted as some function, f(u), with inverse F(u). This, in effect, just makes the substitution of x with f(x). 2) If `u` is a Symbol then `x` will be interpreted as some function, F(x), with inverse f(u). This is commonly referred to as u-substitution. Once f and F have been identified, the transformation is made as follows: .. math:: \int_a^b x \mathrm{d}x \rightarrow \int_{F(a)}^{F(b)} f(x) \frac{\mathrm{d}}{\mathrm{d}x} where `F(x)` is the inverse of `f(x)` and the limits and integrand have been corrected so as to retain the same value after integration. Notes ===== The mappings, F(x) or f(u), must lead to a unique integral. Linear or rational linear expression, `2*x`, `1/x` and `sqrt(x)`, will always work; quadratic expressions like `x**2 - 1` are acceptable as long as the resulting integrand does not depend on the sign of the solutions (see examples). The integral will be returned unchanged if `x` is not a variable of integration. `x` must be (or contain) only one of of the integration variables. If `u` has more than one free symbol then it should be sent as a tuple (`u`, `uvar`) where `uvar` identifies which variable is replacing the integration variable. XXX can it contain another integration variable? Examples ======== >>> from diofant.abc import a, b, c, d, x, u, y >>> from diofant import Integral, S, cos, sqrt >>> i = Integral(x*cos(x**2 - 1), (x, 0, 1)) transform can change the variable of integration >>> i.transform(x, u) Integral(u*cos(u**2 - 1), (u, 0, 1)) transform can perform u-substitution as long as a unique integrand is obtained: >>> i.transform(x**2 - 1, u) Integral(cos(u)/2, (u, -1, 0)) This attempt fails because x = +/-sqrt(u + 1) and the sign does not cancel out of the integrand: >>> Integral(cos(x**2 - 1), (x, 0, 1)).transform(x**2 - 1, u) Traceback (most recent call last): ... ValueError: The mapping between F(x) and f(u) did not give a unique integrand. transform can do a substitution. Here, the previous result is transformed back into the original expression using "u-substitution": >>> ui = _ >>> _.transform(sqrt(u + 1), x) == i True We can accomplish the same with a regular substitution: >>> ui.transform(u, x**2 - 1) == i True If the `x` does not contain a symbol of integration then the integral will be returned unchanged. Integral `i` does not have an integration variable `a` so no change is made: >>> i.transform(a, x) == i True When `u` has more than one free symbol the symbol that is replacing `x` must be identified by passing `u` as a tuple: >>> Integral(x, (x, 0, 1)).transform(x, (u + a, u)) Integral(a + u, (u, -a, -a + 1)) >>> Integral(x, (x, 0, 1)).transform(x, (u + a, a)) Integral(a + u, (a, -u, -u + 1)) See Also ======== diofant.concrete.expr_with_limits.ExprWithLimits.variables : Lists the integration variables diofant.concrete.expr_with_limits.ExprWithLimits.as_dummy : Replace integration variables with dummy ones """ from diofant.solvers.solvers import solve, posify d = Dummy('d') xfree = x.free_symbols.intersection(self.variables) if len(xfree) > 1: raise ValueError('F(x) can only contain one of: %s' % self.variables) xvar = xfree.pop() if xfree else d if xvar not in self.variables: return self u = sympify(u) if isinstance(u, Expr): ufree = u.free_symbols if len(ufree) != 1: raise ValueError( filldedent(''' When f(u) has more than one free symbol, the one replacing x must be identified: pass f(u) as (f(u), u)''')) uvar = ufree.pop() else: u, uvar = u if uvar not in u.free_symbols: raise ValueError( filldedent(''' Expecting a tuple (expr, symbol) where symbol identified a free symbol in expr, but symbol is not in expr's free symbols.''')) if not isinstance(uvar, (Dummy, Symbol)): raise ValueError( filldedent(''' Expecting a tuple (expr, symbol) but didn't get a symbol; got %s''' % uvar)) if x.is_Symbol and u.is_Symbol: return self.xreplace({x: u}) if not x.is_Symbol and not u.is_Symbol: raise ValueError('either x or u must be a symbol') if uvar == xvar: return self.transform(x, (u.subs(uvar, d), d)).xreplace({d: uvar}) if uvar in self.limits: raise ValueError( filldedent(''' u must contain the same variable as in x or a variable that is not already an integration variable''')) if not x.is_Symbol: F = [x.subs(xvar, d)] soln = solve(u - x, xvar, check=False) if not soln: raise ValueError('no solution for solve(F(x) - f(u), x)') f = [fi.subs(uvar, d) for fi in soln] else: f = [u.subs(uvar, d)] pdiff, reps = posify(u - x) puvar = uvar.subs([(v, k) for k, v in reps.items()]) soln = [s.subs(reps) for s in solve(pdiff, puvar)] if not soln: raise ValueError('no solution for solve(F(x) - f(u), u)') F = [fi.subs(xvar, d) for fi in soln] newfuncs = {(self.function.subs(xvar, fi) * fi.diff(d)).subs(d, uvar) for fi in f} if len(newfuncs) > 1: raise ValueError( filldedent(''' The mapping between F(x) and f(u) did not give a unique integrand.''')) newfunc = newfuncs.pop() def _calc_limit_1(F, a, b): """ replace d with a, using subs if possible, otherwise limit where sign of b is considered """ wok = F.subs(d, a) if wok is S.NaN or wok.is_finite is False and a.is_finite: return limit(sign(b) * F, d, a) return wok def _calc_limit(a, b): """ replace d with a, using subs if possible, otherwise limit where sign of b is considered """ avals = list({_calc_limit_1(Fi, a, b) for Fi in F}) if len(avals) > 1: raise ValueError( filldedent(''' The mapping between F(x) and f(u) did not give a unique limit.''')) return avals[0] newlimits = [] for xab in self.limits: sym = xab[0] if sym == xvar: if len(xab) == 3: a, b = xab[1:] a, b = _calc_limit(a, b), _calc_limit(b, a) if a - b > 0: a, b = b, a newfunc = -newfunc newlimits.append((uvar, a, b)) elif len(xab) == 2: a = _calc_limit(xab[1], 1) newlimits.append((uvar, a)) else: newlimits.append(uvar) else: newlimits.append(xab) return self.func(newfunc, *newlimits)