def reduce_inequalities(inequalities, symbols=[]): """ Reduce a system of inequalities with rational coefficients. Examples ======== >>> from diofant.solvers.inequalities import reduce_inequalities >>> x = Symbol('x', real=True) >>> y = Symbol('y', real=True) >>> reduce_inequalities(0 <= x + 3, []) -3 <= x >>> reduce_inequalities(0 <= x + y*2 - 1, [x]) -2*y + 1 <= x """ if not iterable(inequalities): inequalities = [inequalities] # prefilter keep = [] for i in inequalities: if isinstance(i, Relational): i = i.func(i.lhs.as_expr() - i.rhs.as_expr(), 0) elif i not in (True, False): i = Eq(i, 0) if i == S.true: continue elif i == S.false: return S.false if i.lhs.is_number: raise NotImplementedError("Couldn't determine truth value of %s" % i) keep.append(i) inequalities = keep del keep gens = reduce(set.union, [i.free_symbols for i in inequalities], set()) if not iterable(symbols): symbols = [symbols] symbols = set(symbols) or gens # make vanilla symbol real recast = { i: Dummy(i.name, extended_real=True) for i in gens if i.is_extended_real is None } inequalities = [i.xreplace(recast) for i in inequalities] symbols = {i.xreplace(recast) for i in symbols} # solve system rv = _reduce_inequalities(inequalities, symbols) # restore original symbols and return return rv.xreplace({v: k for k, v in recast.items()})
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, *args, **kwargs): evaluate = kwargs.get('evaluate', global_evaluate[0]) if iterable(args[0]): if isinstance(args[0], Point) and not evaluate: return args[0] args = args[0] # unpack the arguments into a friendly Tuple # if we were already a Point, we're doing an excess # iteration, but we'll worry about efficiency later coords = Tuple(*args) if any(a.is_number and im(a) for a in coords): raise ValueError('Imaginary coordinates not permitted.') # Turn any Floats into rationals and simplify # any expressions before we instantiate if evaluate: coords = coords.xreplace(dict( [(f, simplify(nsimplify(f, rational=True))) for f in coords.atoms(Float)])) if len(coords) == 2: return Point2D(coords, **kwargs) if len(coords) == 3: return Point3D(coords, **kwargs) return GeometryEntity.__new__(cls, *coords)
def _find_opts(expr): if expr.is_Atom or expr.is_Order: return if iterable(expr): list(map(_find_opts, expr)) return if expr in seen_subexp: return expr seen_subexp.add(expr) list(map(_find_opts, expr.args)) if _coeff_isneg(expr): neg_expr = -expr if not neg_expr.is_Atom: opt_subs[expr] = Mul(S.NegativeOne, neg_expr, evaluate=False) seen_subexp.add(neg_expr) expr = neg_expr if expr.is_Mul: muls.add(expr) elif expr.is_Add: adds.add(expr) elif expr.is_Pow: if _coeff_isneg(expr.exp): opt_subs[expr] = Pow(Pow(expr.base, -expr.exp), S.NegativeOne, evaluate=False)
def convert(self, elem, M=None): """ Convert ``elem`` into the internal representation. This method is called implicitly whenever computations involve elements not in the internal representation. >>> from diofant.abc import x >>> from diofant import QQ >>> F = QQ.old_poly_ring(x).free_module(2) >>> F.convert([1, 0]) [1, 0] """ if isinstance(elem, FreeModuleElement): if elem.module is self: return elem if elem.module.rank != self.rank: raise CoercionFailed return FreeModuleElement( self, tuple( self.ring.convert(x, elem.module.ring) for x in elem.data)) elif iterable(elem): tpl = tuple(self.ring.convert(x) for x in elem) if len(tpl) != self.rank: raise CoercionFailed return FreeModuleElement(self, tpl) elif elem is 0: return FreeModuleElement(self, (self.ring.convert(0), ) * self.rank) else: raise CoercionFailed
def posify(eq): """Return eq (with generic symbols made positive) and a dictionary containing the mapping between the old and new symbols. Any symbol that has positive=None will be replaced with a positive dummy symbol having the same name. This replacement will allow more symbolic processing of expressions, especially those involving powers and logarithms. A dictionary that can be sent to subs to restore eq to its original symbols is also returned. >>> from diofant import posify, Symbol, log, solve >>> from diofant.abc import x >>> posify(x + Symbol('p', positive=True) + Symbol('n', negative=True)) (n + p + _x, {_x: x}) >>> eq = 1/x >>> log(eq).expand() log(1/x) >>> log(posify(eq)[0]).expand() -log(_x) >>> p, rep = posify(eq) >>> log(p).expand().subs(rep) -log(x) It is possible to apply the same transformations to an iterable of expressions: >>> eq = x**2 - 4 >>> solve(eq, x) [-2, 2] >>> eq_x, reps = posify([eq, x]); eq_x [_x**2 - 4, _x] >>> solve(*eq_x) [2] """ eq = sympify(eq) if iterable(eq): f = type(eq) eq = list(eq) syms = set() for e in eq: syms = syms.union(e.atoms(Symbol)) reps = {} for s in syms: reps.update({v: k for k, v in posify(s)[1].items()}) for i, e in enumerate(eq): eq[i] = e.subs(reps) return f(eq), {r: s for s, r in reps.items()} reps = { s: Dummy(s.name, positive=True) for s in eq.free_symbols if s.is_positive is None } eq = eq.subs(reps) return eq, {r: s for s, r in reps.items()}
def __init__(self, monom, gens=None): if not iterable(monom): rep, gens = dict_from_expr(sympify(monom), gens=gens) if len(rep) == 1 and list(rep.values())[0] == 1: monom = list(rep.keys())[0] else: raise ValueError("Expected a monomial got %s" % monom) self.exponents = tuple(map(int, monom)) self.gens = gens
def __new__(cls, *args, **kw_args): """The constructor for the Prufer object. Examples ======== >>> from diofant.combinatorics.prufer import Prufer A Prufer object can be constructed from a list of edges: >>> a = Prufer([[0, 1], [0, 2], [0, 3]]) >>> a.prufer_repr [0, 0] If the number of nodes is given, no checking of the nodes will be performed; it will be assumed that nodes 0 through n - 1 are present: >>> Prufer([[0, 1], [0, 2], [0, 3]], 4) Prufer([[0, 1], [0, 2], [0, 3]], 4) A Prufer object can be constructed from a Prufer sequence: >>> b = Prufer([1, 3]) >>> b.tree_repr [[0, 1], [1, 3], [2, 3]] """ ret_obj = Basic.__new__(cls, *args, **kw_args) args = [list(args[0])] if args[0] and iterable(args[0][0]): if not args[0][0]: raise ValueError( 'Prufer expects at least one edge in the tree.') if len(args) > 1: nnodes = args[1] else: nodes = set(flatten(args[0])) nnodes = max(nodes) + 1 if nnodes != len(nodes): missing = set(range(nnodes)) - nodes if len(missing) == 1: msg = 'Node %s is missing.' % missing.pop() else: msg = 'Nodes %s are missing.' % list(sorted(missing)) raise ValueError(msg) ret_obj._tree_repr = [list(i) for i in args[0]] ret_obj._nodes = nnodes else: ret_obj._prufer_repr = args[0] ret_obj._nodes = len(ret_obj._prufer_repr) + 2 return ret_obj
def __add__(self, other): """Add other to self by incrementing self's coordinates by those of other. See Also ======== diofant.geometry.entity.GeometryEntity.translate """ if iterable(other) and len(other) == len(self): return Point([simplify(a + b) for a, b in zip(self, other)]) else: raise ValueError( "Points must have the same number of dimensions")
def _rebuild(expr): if not expr.args: return expr if iterable(expr): new_args = [_rebuild(arg) for arg in expr] return expr.func(*new_args) if expr in subs: return subs[expr] orig_expr = expr if expr in opt_subs: expr = opt_subs[expr] # If enabled, parse Muls and Adds arguments by order to ensure # replacement order independent from hashes if order != 'none': if expr.is_Mul: c, nc = expr.args_cnc() args = list(ordered(c)) + nc elif expr.is_Add: args = list(ordered(expr.args)) else: args = expr.args else: args = expr.args new_args = list(map(_rebuild, args)) if new_args != args: new_expr = expr.func(*new_args) else: new_expr = expr if orig_expr in to_eliminate: try: sym = next(symbols) except StopIteration: raise ValueError("Symbols iterator ran out of symbols.") subs[orig_expr] = sym replacements.append((sym, new_expr)) return sym else: return new_expr
def _nodes(e): """ A helper for ordered() which returns the node count of ``e`` which for Basic objects is the number of Basic nodes in the expression tree but for other objects is 1 (unless the object is an iterable or dict for which the sum of nodes is returned). """ from .basic import Basic if isinstance(e, Basic): return e.count(Basic) elif iterable(e): return 1 + sum(_nodes(ei) for ei in e) elif isinstance(e, dict): return 1 + sum(_nodes(k) + _nodes(v) for k, v in e.items()) else: return 1
def parse_hints(hints): """Split hints into (n, funcs, iterables, gens).""" n = 1 funcs, iterables, gens = [], [], [] for e in hints: if isinstance(e, (int, Integer)): n = e elif isinstance(e, FunctionClass): funcs.append(e) elif iterable(e): iterables.append((e[0], e[1:])) # XXX sin(x+2y)? # Note: we go through polys so e.g. # sin(-x) -> -sin(x) -> sin(x) gens.extend( parallel_poly_from_expr([e[0](x) for x in e[1:]] + [e[0](Add(*e[1:]))])[1].gens) else: gens.append(e) return n, funcs, iterables, gens
def _find_repeated(expr): if expr.is_Atom or expr.is_Order: return if iterable(expr): args = expr else: if expr in seen_subexp: to_eliminate.add(expr) return seen_subexp.add(expr) if expr in opt_subs: expr = opt_subs[expr] args = expr.args list(map(_find_repeated, args))
def __new__(cls, *args, **kwargs): eval = kwargs.get('evaluate', global_evaluate[0]) if isinstance(args[0], (Point, Point3D)): if not eval: return args[0] args = args[0].args else: if iterable(args[0]): args = args[0] if len(args) not in (2, 3): raise TypeError( "Enter a 2 or 3 dimensional point") coords = Tuple(*args) if len(coords) == 2: coords += (S.Zero,) if eval: coords = coords.xreplace(dict( [(f, simplify(nsimplify(f, rational=True))) for f in coords.atoms(Float)])) return GeometryEntity.__new__(cls, *coords)
def _together(expr): if isinstance(expr, Basic): if expr.is_Atom or (expr.is_Function and not deep): return expr elif expr.is_Add: return gcd_terms(list(map(_together, Add.make_args(expr)))) elif expr.is_Pow: base = _together(expr.base) if deep: exp = _together(expr.exp) else: exp = expr.exp return expr.__class__(base, exp) else: return expr.__class__(*[_together(arg) for arg in expr.args]) elif iterable(expr): return expr.__class__([_together(ex) for ex in expr]) return expr
def is_sequence(i, include=None): """ Return a boolean indicating whether ``i`` is a sequence in the Diofant sense. If anything that fails the test below should be included as being a sequence for your application, set 'include' to that object's type; multiple types should be passed as a tuple of types. Note: although generators can generate a sequence, they often need special handling to make sure their elements are captured before the generator is exhausted, so these are not included by default in the definition of a sequence. See Also ======== iterable Examples ======== >>> from diofant.utilities.iterables import is_sequence >>> from types import GeneratorType >>> is_sequence([]) True >>> is_sequence(set()) False >>> is_sequence('abc') False >>> is_sequence('abc', include=str) True >>> generator = (c for c in 'abc') >>> is_sequence(generator) False >>> is_sequence(generator, include=(str, GeneratorType)) True """ return (hasattr(i, '__getitem__') and iterable(i) or bool(include) and isinstance(i, include))
def __new__(cls, *args, **kwargs): eval = kwargs.get('evaluate', global_evaluate[0]) check = True if isinstance(args[0], Point2D): if not eval: return args[0] args = args[0].args check = False else: if iterable(args[0]): args = args[0] if len(args) != 2: raise ValueError( "Only two dimensional points currently supported") coords = Tuple(*args) if check: if any(a.is_number and im(a) for a in coords): raise ValueError('Imaginary args not permitted.') if eval: coords = coords.xreplace(dict( [(f, simplify(nsimplify(f, rational=True))) for f in coords.atoms(Float)])) return GeometryEntity.__new__(cls, *coords)
def as_finite_diff(derivative, points=1, x0=None, wrt=None): """ Returns an approximation of a derivative of a function in the form of a finite difference formula. The expression is a weighted sum of the function at a number of discrete values of (one of) the independent variable(s). Parameters ========== derivative: a Derivative instance (needs to have an variables and expr attribute). points: sequence or coefficient, optional If sequence: discrete values (length >= order+1) of the independent variable used for generating the finite difference weights. If it is a coefficient, it will be used as the step-size for generating an equidistant sequence of length order+1 centered around x0. default: 1 (step-size 1) x0: number or Symbol, optional the value of the independent variable (wrt) at which the derivative is to be approximated. default: same as wrt wrt: Symbol, optional "with respect to" the variable for which the (partial) derivative is to be approximated for. If not provided it is required that the Derivative is ordinary. default: None Examples ======== >>> from diofant import symbols, Function, exp, sqrt, Symbol, as_finite_diff >>> x, h = symbols('x h') >>> f = Function('f') >>> as_finite_diff(f(x).diff(x)) -f(x - 1/2) + f(x + 1/2) The default step size and number of points are 1 and ``order + 1`` respectively. We can change the step size by passing a symbol as a parameter: >>> as_finite_diff(f(x).diff(x), h) -f(-h/2 + x)/h + f(h/2 + x)/h We can also specify the discretized values to be used in a sequence: >>> as_finite_diff(f(x).diff(x), [x, x+h, x+2*h]) -3*f(x)/(2*h) + 2*f(h + x)/h - f(2*h + x)/(2*h) The algorithm is not restricted to use equidistant spacing, nor do we need to make the approximation around x0, but we can get an expression estimating the derivative at an offset: >>> e, sq2 = exp(1), sqrt(2) >>> xl = [x-h, x+h, x+e*h] >>> as_finite_diff(f(x).diff(x, 1), xl, x+h*sq2) 2*h*f(E*h + x)*((h + sqrt(2)*h)/(2*h) -\ (-sqrt(2)*h + h)/(2*h))/((-h + E*h)*(h + E*h)) +\ f(-h + x)*(-(-sqrt(2)*h + h)/(2*h) - (-sqrt(2)*h + E*h)/(2*h))/(h +\ E*h) + f(h + x)*(-(h + sqrt(2)*h)/(2*h) + (-sqrt(2)*h +\ E*h)/(2*h))/(-h + E*h) Partial derivatives are also supported: >>> y = Symbol('y') >>> d2fdxdy=f(x,y).diff(x,y) >>> as_finite_diff(d2fdxdy, wrt=x) -f(x - 1/2, y) + f(x + 1/2, y) See also ======== diofant.calculus.finite_diff.apply_finite_diff diofant.calculus.finite_diff.finite_diff_weights """ if wrt is None: wrt = derivative.variables[0] # we need Derivative to be univariate to guess wrt if any(v != wrt for v in derivative.variables): raise ValueError('if the function is not univariate' + ' then `wrt` must be given') order = derivative.variables.count(wrt) if x0 is None: x0 = wrt if not iterable(points): # points is simply the step-size, let's make it a # equidistant sequence centered around x0 if order % 2 == 0: # even order => odd number of points, grid point included points = [ x0 + points * i for i in range(-order // 2, order // 2 + 1) ] else: # odd order => even number of points, half-way wrt grid point points = [ x0 + points * i / Integer(2) for i in range(-order, order + 1, 2) ] if len(points) < order + 1: raise ValueError("Too few points for order %d" % order) return apply_finite_diff(order, points, [derivative.expr.subs({wrt: x}) for x in points], x0)
def collect(expr, syms, func=None, evaluate=True, exact=False, distribute_order_term=True): """Collect additive terms of an expression. This function collects additive terms of an expression with respect to a list of expression up to powers with rational exponents. By the term symbol here are meant arbitrary expressions, which can contain powers, products, sums etc. In other words symbol is a pattern which will be searched for in the expression's terms. The input expression is not expanded by :func:`collect`, so user is expected to provide an expression is an appropriate form (for example, by using :func:`~diofant.core.function.expand` prior to calling this function). This makes :func:`collect` more predictable as there is no magic happening behind the scenes. However, it is important to note, that powers of products are converted to products of powers using the :func:`~diofant.core.function.expand_power_base` function. Parameters ========== expr : Expr an expression syms : iterable of Symbol's collected symbols evaluate : boolean First, if ``evaluate`` flag is set (by default), this function will return an expression with collected terms else it will return a dictionary with expressions up to rational powers as keys and collected coefficients as values. Examples ======== >>> from diofant import collect, expand, factor >>> from diofant.abc import a, b, c, x, y This function can collect symbolic coefficients in polynomials or rational expressions. It will manage to find all integer or rational powers of collection variable: >>> collect(a*x**2 + b*x**2 + a*x - b*x + c, x) c + x**2*(a + b) + x*(a - b) The same result can be achieved in dictionary form: >>> d = collect(a*x**2 + b*x**2 + a*x - b*x + c, x, evaluate=False) >>> d[x**2] a + b >>> d[x] a - b >>> d[1] c You can also work with multivariate polynomials. However, remember that this function is greedy so it will care only about a single symbol at time, in specification order: >>> collect(x**2 + y*x**2 + x*y + y + a*y, [x, y]) x**2*(y + 1) + x*y + y*(a + 1) Also more complicated expressions can be used as patterns: >>> from diofant import sin, log >>> collect(a*sin(2*x) + b*sin(2*x), sin(2*x)) (a + b)*sin(2*x) >>> collect(a*x*log(x) + b*(x*log(x)), x*log(x)) x*(a + b)*log(x) You can use wildcards in the pattern: >>> from diofant import Wild >>> w = Wild('w1') >>> collect(a*x**y - b*x**y, w**y) x**y*(a - b) It is also possible to work with symbolic powers, although it has more complicated behavior, because in this case power's base and symbolic part of the exponent are treated as a single symbol: >>> collect(a*x**c + b*x**c, x) a*x**c + b*x**c >>> collect(a*x**c + b*x**c, x**c) x**c*(a + b) However if you incorporate rationals to the exponents, then you will get well known behavior: >>> collect(a*x**(2*c) + b*x**(2*c), x**c) x**(2*c)*(a + b) Note also that all previously stated facts about :func:`collect` function apply to the exponential function, so you can get: >>> from diofant import exp >>> collect(a*exp(2*x) + b*exp(2*x), exp(x)) E**(2*x)*(a + b) If you are interested only in collecting specific powers of some symbols then set ``exact`` flag in arguments: >>> collect(a*x**7 + b*x**7, x, exact=True) a*x**7 + b*x**7 >>> collect(a*x**7 + b*x**7, x**7, exact=True) x**7*(a + b) You can also apply this function to differential equations, where derivatives of arbitrary order can be collected. Note that if you collect with respect to a function or a derivative of a function, all derivatives of that function will also be collected. Use ``exact=True`` to prevent this from happening: >>> from diofant import Derivative as D, Function >>> f = Function('f')(x) >>> collect(a*D(f,x) + b*D(f,x), D(f,x)) (a + b)*Derivative(f(x), x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), f) (a + b)*Derivative(f(x), x, x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x), exact=True) a*Derivative(f(x), x, x) + b*Derivative(f(x), x, x) >>> collect(a*D(f,x) + b*D(f,x) + a*f + b*f, f) f(x)*(a + b) + (a + b)*Derivative(f(x), x) Or you can even match both derivative order and exponent at the same time: >>> collect(a*D(D(f,x),x)**2 + b*D(D(f,x),x)**2, D(f,x)) (a + b)*Derivative(f(x), x, x)**2 Finally, you can apply a function to each of the collected coefficients. For example you can factorize symbolic coefficients of polynomial: >>> f = expand((x + a + 1)**3) >>> collect(f, x, factor) x**3 + 3*x**2*(a + 1) + 3*x*(a + 1)**2 + (a + 1)**3 See Also ======== collect_const, collect_sqrt, rcollect """ def make_expression(terms): product = [] for term, rat, sym, deriv in terms: if deriv is not None: var, order = deriv while order > 0: term, order = Derivative(term, var), order - 1 if sym is None: if rat is S.One: product.append(term) else: product.append(Pow(term, rat)) else: product.append(Pow(term, rat * sym)) return Mul(*product) def parse_derivative(deriv): # scan derivatives tower in the input expression and return # underlying function and maximal differentiation order expr, sym, order = deriv.expr, deriv.variables[0], 1 for s in deriv.variables[1:]: if s == sym: order += 1 else: raise NotImplementedError( 'Improve MV Derivative support in collect') while isinstance(expr, Derivative): s0 = expr.variables[0] for s in expr.variables: if s != s0: raise NotImplementedError( 'Improve MV Derivative support in collect') if s0 == sym: expr, order = expr.expr, order + len(expr.variables) else: break return expr, (sym, Rational(order)) def parse_term(expr): """Parses expression expr and outputs tuple (sexpr, rat_expo, sym_expo, deriv) where: - sexpr is the base expression - rat_expo is the rational exponent that sexpr is raised to - sym_expo is the symbolic exponent that sexpr is raised to - deriv contains the derivatives the the expression for example, the output of x would be (x, 1, None, None) the output of 2**x would be (2, 1, x, None) """ rat_expo, sym_expo = S.One, None sexpr, deriv = expr, None if expr.is_Pow: if isinstance(expr.base, Derivative): sexpr, deriv = parse_derivative(expr.base) else: sexpr = expr.base if expr.exp.is_Number: rat_expo = expr.exp else: coeff, tail = expr.exp.as_coeff_Mul() if coeff.is_Number: rat_expo, sym_expo = coeff, tail else: sym_expo = expr.exp elif isinstance(expr, Derivative): sexpr, deriv = parse_derivative(expr) return sexpr, rat_expo, sym_expo, deriv def parse_expression(terms, pattern): """Parse terms searching for a pattern. terms is a list of tuples as returned by parse_terms; pattern is an expression treated as a product of factors """ pattern = Mul.make_args(pattern) if len(terms) < len(pattern): # pattern is longer than matched product # so no chance for positive parsing result return else: pattern = [parse_term(elem) for elem in pattern] terms = terms[:] # need a copy elems, common_expo, has_deriv = [], None, False for elem, e_rat, e_sym, e_ord in pattern: if elem.is_Number and e_rat == 1 and e_sym is None: # a constant is a match for everything continue for j in range(len(terms)): if terms[j] is None: continue term, t_rat, t_sym, t_ord = terms[j] # keeping track of whether one of the terms had # a derivative or not as this will require rebuilding # the expression later if t_ord is not None: has_deriv = True if (term.match(elem) is not None and (t_sym == e_sym or t_sym is not None and e_sym is not None and t_sym.match(e_sym) is not None)): if exact is False: # we don't have to be exact so find common exponent # for both expression's term and pattern's element expo = t_rat / e_rat if common_expo is None: # first time common_expo = expo else: # common exponent was negotiated before so # there is no chance for a pattern match unless # common and current exponents are equal if common_expo != expo: common_expo = 1 else: # we ought to be exact so all fields of # interest must match in every details if e_rat != t_rat or e_ord != t_ord: continue # found common term so remove it from the expression # and try to match next element in the pattern elems.append(terms[j]) terms[j] = None break else: # pattern element not found return return [_f for _f in terms if _f], elems, common_expo, has_deriv if evaluate: if expr.is_Mul: return expr.func(*[ collect(term, syms, func, True, exact, distribute_order_term) for term in expr.args ]) elif expr.is_Pow: b = collect(expr.base, syms, func, True, exact, distribute_order_term) return Pow(b, expr.exp) if iterable(syms): syms = [expand_power_base(i, deep=False) for i in syms] else: syms = [expand_power_base(syms, deep=False)] expr = sympify(expr) order_term = None if distribute_order_term: order_term = expr.getO() if order_term is not None: if order_term.has(*syms): order_term = None else: expr = expr.removeO() summa = [expand_power_base(i, deep=False) for i in Add.make_args(expr)] collected, disliked = defaultdict(list), S.Zero for product in summa: terms = [parse_term(i) for i in Mul.make_args(product)] for symbol in syms: if DIOFANT_DEBUG: print("DEBUG: parsing of expression %s with symbol %s " % (str(terms), str(symbol))) result = parse_expression(terms, symbol) if DIOFANT_DEBUG: print("DEBUG: returned %s" % str(result)) if result is not None: terms, elems, common_expo, has_deriv = result # when there was derivative in current pattern we # will need to rebuild its expression from scratch if not has_deriv: index = 1 for elem in elems: e = elem[1] if elem[2] is not None: e *= elem[2] index *= Pow(elem[0], e) else: index = make_expression(elems) terms = expand_power_base(make_expression(terms), deep=False) index = expand_power_base(index, deep=False) collected[index].append(terms) break else: # none of the patterns matched disliked += product # add terms now for each key collected = {k: Add(*v) for k, v in collected.items()} if disliked is not S.Zero: collected[S.One] = disliked if order_term is not None: for key, val in collected.items(): collected[key] = val + order_term if func is not None: collected = dict([(key, func(val)) for key, val in collected.items()]) if evaluate: return Add(*[key * val for key, val in collected.items()]) else: return collected
def default_sort_key(item, order=None): """ Return a key that can be used for sorting. The key has the structure: (class_key, (len(args), args), exponent.sort_key(), coefficient) This key is supplied by the sort_key routine of Basic objects when ``item`` is a Basic object or an object (other than a string) that sympifies to a Basic object. Otherwise, this function produces the key. The ``order`` argument is passed along to the sort_key routine and is used to determine how the terms *within* an expression are ordered. (See examples below) ``order`` options are: 'lex', 'grlex', 'grevlex', and reversed values of the same (e.g. 'rev-lex'). The default order value is None (which translates to 'lex'). Examples ======== >>> from diofant import S, I, default_sort_key, sin, cos, sqrt >>> from diofant.core.function import UndefinedFunction >>> from diofant.abc import x The following are equivalent ways of getting the key for an object: >>> x.sort_key() == default_sort_key(x) True Here are some examples of the key that is produced: >>> default_sort_key(UndefinedFunction('f')) ((0, 0, 'UndefinedFunction'), (1, ('f',)), ((1, 0, 'Number'), (0, ()), (), 1), 1) >>> default_sort_key('1') ((0, 0, 'str'), (1, ('1',)), ((1, 0, 'Number'), (0, ()), (), 1), 1) >>> default_sort_key(S.One) ((1, 0, 'Number'), (0, ()), (), 1) >>> default_sort_key(2) ((1, 0, 'Number'), (0, ()), (), 2) While sort_key is a method only defined for Diofant objects, default_sort_key will accept anything as an argument so it is more robust as a sorting key. For the following, using key= lambda i: i.sort_key() would fail because 2 doesn't have a sort_key method; that's why default_sort_key is used. Note, that it also handles sympification of non-string items likes ints: >>> a = [2, I, -I] >>> sorted(a, key=default_sort_key) [2, -I, I] The returned key can be used anywhere that a key can be specified for a function, e.g. sort, min, max, etc...: >>> a.sort(key=default_sort_key); a[0] 2 >>> min(a, key=default_sort_key) 2 Notes ===== The key returned is useful for getting items into a canonical order that will be the same across platforms. It is not directly useful for sorting lists of expressions: >>> a, b = x, 1/x Since ``a`` has only 1 term, its value of sort_key is unaffected by ``order``: >>> a.sort_key() == a.sort_key('rev-lex') True If ``a`` and ``b`` are combined then the key will differ because there are terms that can be ordered: >>> eq = a + b >>> eq.sort_key() == eq.sort_key('rev-lex') False >>> eq.as_ordered_terms() [x, 1/x] >>> eq.as_ordered_terms('rev-lex') [1/x, x] But since the keys for each of these terms are independent of ``order``'s value, they don't sort differently when they appear separately in a list: >>> sorted(eq.args, key=default_sort_key) [1/x, x] >>> sorted(eq.args, key=lambda i: default_sort_key(i, order='rev-lex')) [1/x, x] The order of terms obtained when using these keys is the order that would be obtained if those terms were *factors* in a product. Although it is useful for quickly putting expressions in canonical order, it does not sort expressions based on their complexity defined by the number of operations, power of variables and others: >>> sorted([sin(x)*cos(x), sin(x)], key=default_sort_key) [sin(x)*cos(x), sin(x)] >>> sorted([x, x**2, sqrt(x), x**3], key=default_sort_key) [sqrt(x), x, x**2, x**3] See Also ======== ordered diofant.core.expr.Expr.as_ordered_factors diofant.core.expr.Expr.as_ordered_terms """ from diofant.core import S, Basic from diofant.core.sympify import sympify, SympifyError from diofant.core.compatibility import iterable if isinstance(item, Basic): return item.sort_key(order=order) if iterable(item, exclude=(str, )): if isinstance(item, dict): args = item.items() unordered = True elif isinstance(item, set): args = item unordered = True else: # e.g. tuple, list args = list(item) unordered = False args = [default_sort_key(arg, order=order) for arg in args] if unordered: # e.g. dict, set args = sorted(args) cls_index, args = 10, (len(args), tuple(args)) else: if not isinstance(item, str): try: item = sympify(item) except SympifyError: # e.g. lambda x: x pass else: if isinstance(item, Basic): # e.g int -> Integer return default_sort_key(item) # e.g. UndefinedFunction # e.g. str cls_index, args = 0, (1, (str(item), )) return (cls_index, 0, item.__class__.__name__), args, S.One.sort_key(), S.One
def test_not_interable(): i, j = symbols('i j', integer=True) A = Indexed('A', i, i + j) assert not iterable(A)
def euler_equations(L, funcs=(), vars=()): r""" Find the Euler-Lagrange equations [1]_ for a given Lagrangian. Parameters ========== L : Expr The Lagrangian that should be a function of the functions listed in the second argument and their derivatives. For example, in the case of two functions `f(x,y)`, `g(x,y)` and two independent variables `x`, `y` the Lagrangian would have the form: .. math:: L\left(f(x,y),g(x,y),\frac{\partial f(x,y)}{\partial x}, \frac{\partial f(x,y)}{\partial y}, \frac{\partial g(x,y)}{\partial x}, \frac{\partial g(x,y)}{\partial y},x,y\right) In many cases it is not necessary to provide anything, except the Lagrangian, it will be auto-detected (and an error raised if this couldn't be done). funcs : Function or an iterable of Functions The functions that the Lagrangian depends on. The Euler equations are differential equations for each of these functions. vars : Symbol or an iterable of Symbols The Symbols that are the independent variables of the functions. Returns ======= eqns : list of Eq The list of differential equations, one for each function. Examples ======== >>> from diofant import Symbol, Function >>> x = Function('x') >>> t = Symbol('t') >>> L = (x(t).diff(t))**2/2 - x(t)**2/2 >>> euler_equations(L, x(t), t) [Eq(-x(t) - Derivative(x(t), t, t), 0)] >>> u = Function('u') >>> x = Symbol('x') >>> L = (u(t, x).diff(t))**2/2 - (u(t, x).diff(x))**2/2 >>> euler_equations(L, u(t, x), [t, x]) [Eq(-Derivative(u(t, x), t, t) + Derivative(u(t, x), x, x), 0)] References ========== .. [1] http://en.wikipedia.org/wiki/Euler%E2%80%93Lagrange_equation """ L = sympify(L) funcs = tuple(funcs) if iterable(funcs) else (funcs, ) if not funcs: funcs = tuple(L.atoms(Function)) else: for f in funcs: if not isinstance(f, Function): raise TypeError('Function expected, got: %s' % f) vars = tuple(vars) if iterable(vars) else (vars, ) if not vars: vars = funcs[0].args else: vars = tuple(sympify(var) for var in vars) if not all(isinstance(v, Symbol) for v in vars): raise TypeError('Variables are not symbols, got %s' % vars) for f in funcs: if not vars == f.args: raise ValueError("Variables %s don't match args: %s" % (vars, f)) order = max( len(d.variables) for d in L.atoms(Derivative) if d.expr in funcs) eqns = [] for f in funcs: eq = diff(L, f) for i in range(1, order + 1): for p in combinations_with_replacement(vars, i): eq = eq + S.NegativeOne**i * diff(L, diff(f, *p), *p) eqns.append(Eq(eq)) return eqns
def PolynomialRing(dom, *gens, **opts): r""" Create a generalized multivariate polynomial ring. A generalized polynomial ring is defined by a ground field `K`, a set of generators (typically `x_1, \ldots, x_n`) and a monomial order `<`. The monomial order can be global, local or mixed. In any case it induces a total ordering on the monomials, and there exists for every (non-zero) polynomial `f \in K[x_1, \ldots, x_n]` a well-defined "leading monomial" `LM(f) = LM(f, >)`. One can then define a multiplicative subset `S = S_> = \{f \in K[x_1, \ldots, x_n] | LM(f) = 1\}`. The generalized polynomial ring corresponding to the monomial order is `R = S^{-1}K[x_1, \ldots, x_n]`. If `>` is a so-called global order, that is `1` is the smallest monomial, then we just have `S = K` and `R = K[x_1, \ldots, x_n]`. Examples ======== A few examples may make this clearer. >>> from diofant.abc import x, y >>> from diofant import QQ Our first ring uses global lexicographic order. >>> R1 = QQ.old_poly_ring(x, y, order=(("lex", x, y),)) The second ring uses local lexicographic order. Note that when using a single (non-product) order, you can just specify the name and omit the variables: >>> R2 = QQ.old_poly_ring(x, y, order="ilex") The third and fourth rings use a mixed orders: >>> o1 = (("ilex", x), ("lex", y)) >>> o2 = (("lex", x), ("ilex", y)) >>> R3 = QQ.old_poly_ring(x, y, order=o1) >>> R4 = QQ.old_poly_ring(x, y, order=o2) We will investigate what elements of `K(x, y)` are contained in the various rings. >>> L = [x, 1/x, y/(1 + x), 1/(1 + y), 1/(1 + x*y)] >>> test = lambda R: [f in R for f in L] The first ring is just `K[x, y]`: >>> test(R1) [True, False, False, False, False] The second ring is R1 localised at the maximal ideal (x, y): >>> test(R2) [True, False, True, True, True] The third ring is R1 localised at the prime ideal (x): >>> test(R3) [True, False, True, False, True] Finally the fourth ring is R1 localised at `S = K[x, y] \setminus yK[y]`: >>> test(R4) [True, False, False, True, False] """ order = opts.get("order", GeneralizedPolynomialRing.default_order) if iterable(order): order = build_product_order(order, gens) order = monomial_key(order) opts['order'] = order if order.is_global: return GlobalPolynomialRing(dom, *gens, **opts) else: return GeneralizedPolynomialRing(dom, *gens, **opts)
def test_iterable(): assert iterable(0) is False assert iterable(1) is False assert iterable(None) is False
def inv(l): if iterable(l): return tuple(inv(x) for x in l) return -l
def test_not_interable(): i, j = symbols('i j', integer=True) A = Indexed('A', i, i + j) assert not iterable(A)
def test_iterable(): assert iterable(0) is False assert iterable(1) is False assert iterable(None) is False