def get_xy_ref(self): ''' Gets x in function of 'xi' and 'eta' Gets y in function of 'xi; and 'eta' X is the Sum of N_i * X_i for each node of the element. Y is the Sum of N_i * Y_i for each node of the element. ''' if self.Ne_ref is None: self.get_Ne_ref() x_coord = 0 y_coord = 0 for i in range(self.num_nodes): x_coord = x_coord + (self.nodes[i].x * self.Ne_ref[i]) y_coord = y_coord + (self.nodes[i].y * self.Ne_ref[i]) for i in sy.preorder_traversal(x_coord): if isinstance(i, sy.Float) and abs(i) < 1e-10: x_coord = x_coord.subs(i, round(i, 1)) elif isinstance(i, sy.Float): x_coord = x_coord.subs(i, round(i, 15)) for i in sy.preorder_traversal(y_coord): if isinstance(i, sy.Float) and abs(i) < 1e-10: y_coord = y_coord.subs(i, round(i, 1)) elif isinstance(i, sy.Float): y_coord = y_coord.subs(i, round(i, 15)) self.x_coord = x_coord self.y_coord = y_coord
def checkanswer(self, user_answer): user_answer = user_answer.lower() user_answer = user_answer.replace('^', '**') user_answer = parse_expr(user_answer, transformations=transformations, evaluate=False) print('user stuff', user_answer, type(user_answer)) if isinstance(user_answer, sy.Pow): if user_answer.args[1] == -1: # print('My fault!') print(user_answer.args) if isinstance(user_answer.args[0], sy.Pow): user_base = user_answer.args[0].args[0] user_expo = -user_answer.args[0].args[1] user_answer = sy.Pow(user_base, sy.simplify(user_expo), evaluate=False) print('alt', user_answer, type(user_answer)) for arg in sy.preorder_traversal(user_answer): print(arg) else: user_base = user_answer.args[0] user_expo = user_answer.args[1] user_answer = sy.Pow(user_base, sy.simplify(user_expo), evaluate=False) print('standard', user_answer, type(user_answer)) for arg in sy.preorder_traversal(user_answer): print(arg) print('self.answer', self.answer) answer = self.answer # answer = parse_expr(str(self.answer), transformations=transformations) print('answer stuff', answer, type(answer)) for arg in sy.preorder_traversal(answer): print(arg) return user_answer == answer
def cse_postprocess(cse_output): replaced, reduced = cse_output i = 0 while i < len(replaced): sym, expr = replaced[i] # Search through replaced expressions for negative symbols if (expr.func == sp.Mul and len(expr.args) == 2 and \ any((arg.func == sp.Symbol) for arg in expr.args) and \ any((arg == sp.S.NegativeOne or '_NegativeOne_' in str(arg)) for arg in expr.args)): for k in range(i + 1, len(replaced)): if sym in replaced[k][1].free_symbols: replaced[k] = (replaced[k][0], replaced[k][1].subs(sym, expr)) for k in range(len(reduced)): if sym in reduced[k].free_symbols: reduced[k] = reduced[k].subs(sym, expr) # Remove the replaced expression from the list replaced.pop(i) if i != 0: i -= 1 # Search through replaced expressions for addition/product of 2 or less symbols if ((expr.func == sp.Add or expr.func == sp.Mul) and 0 < len(expr.args) < 3 and \ all((arg.func == sp.Symbol or arg.is_integer or arg.is_rational) for arg in expr.args)) or \ (expr.func == sp.Pow and expr.args[0].func == sp.Symbol and expr.args[1] == 2): sym_count = 0 # Count the number of occurrences of the substituted symbol for k in range(len(replaced) - i): # Check if the substituted symbol appears in the replaced expressions if sym in replaced[i + k][1].free_symbols: for arg in sp.preorder_traversal(replaced[i + k][1]): if arg.func == sp.Symbol and str(arg) == str(sym): sym_count += 1 for k in range(len(reduced)): # Check if the substituted symbol appears in the reduced expression if sym in reduced[k].free_symbols: for arg in sp.preorder_traversal(reduced[k]): if arg.func == sp.Symbol and str(arg) == str(sym): sym_count += 1 # If the number of occurrences of the substituted symbol is 2 or less, back-substitute if 0 < sym_count < 3: for k in range(i + 1, len(replaced)): if sym in replaced[k][1].free_symbols: replaced[k] = (replaced[k][0], replaced[k][1].subs(sym, expr)) for k in range(len(reduced)): if sym in reduced[k].free_symbols: reduced[k] = reduced[k].subs(sym, expr) # Remove the replaced expression from the list replaced.pop(i) i -= 1 i += 1 return replaced, reduced
def get_index_derivatives(expr): """ """ coord = ['x', 'y', 'z'] d = OrderedDict() for c in coord: d[c] = 0 ops = [ a for a in preorder_traversal(expr) if isinstance(a, _partial_derivatives) ] for i in ops: op = type(i) if isinstance(i, dx): d['x'] += 1 elif isinstance(i, dy): d['y'] += 1 elif isinstance(i, dz): d['z'] += 1 return d
def get_index_logical_derivatives(expr): """ """ coord = ['x1', 'x2', 'x3'] d = OrderedDict() for c in coord: d[c] = 0 ops = [ a for a in preorder_traversal(expr) if isinstance(a, _logical_partial_derivatives) ] for i in ops: op = type(i) if isinstance(i, dx1): d['x1'] += 1 elif isinstance(i, dx2): d['x2'] += 1 elif isinstance(i, dx3): d['x3'] += 1 return d
def gelatize(expr, dim): # ... in the case of a Lambda expression args = None if isinstance(expr, Lambda): args = expr.variables expr = expr.expr # ... # ... we first need to find the ordered list of generic operators ops = [a for a in preorder_traversal(expr) if isinstance(a, _generic_ops)] # ... # ... for i in ops: # if i = Grad(u) then type(i) is Grad op = type(i) new = eval('{0}_{1}d'.format(op, dim)) expr = expr.subs(op, new) # ... if args: return Lambda(args, expr) else: return expr
def filter_expr2(expr, threshold=0.01): """Sets all constants under threshold to 0 TODO: Test""" for a in sym.preorder_traversal(expr): if isinstance(a, sym.Float) and a < threshold: expr = expr.subs(a, 0) return expr
def _eval_is_zero(self): if self.shape: return if len(self.args) == 2: if self.is_extended_real: if self.min().is_extended_positive: return False if self.max().is_extended_negative: return False from sympy import preorder_traversal, KroneckerDelta delta = None for arg in preorder_traversal(self): if isinstance(arg, KroneckerDelta): delta = arg break if delta is not None: #to prevent infinite loop! this = self._subs(delta, S.Zero) if this is self: return delta0 = this.is_zero this = self._subs(delta, S.One) if this is self: return delta1 = this.is_zero if delta0 and delta1: return True if delta0 == False and delta1 == False: return False
def round_sympy_expr(expr, decimals): """Returns the expression with every float rounded to the given number of decimals.""" rounded_expr = expr for a in sympy.preorder_traversal(expr): if isinstance(a, sympy.Float): rounded_expr = rounded_expr.subs(a, round(a, decimals)) return rounded_expr
def traversal(expr): """Traverse sympy expression tree Args: expr (sympy expression) """ return list(preorder_traversal(expr))
def expr_to_infix(expr, mul_add_arity_fixed=False): """ Returns preorder traversal of the symbolic expression """ pre = [] pre_arity = [] for expr_node in snp.preorder_traversal(expr): if expr_node.func.is_symbol: pre.append(expr_node.name) pre_arity.append(len(expr_node.args)) elif expr_node.is_Function or expr_node.is_Add or expr_node.is_Mul or expr_node.is_Pow: if mul_add_arity_fixed and (expr_node.is_Add or expr_node.is_Mul): for i in range(len(expr_node.args) - 1): pre_arity.append(2) pre.append(type(expr_node).__name__) else: pre_arity.append(len(expr_node.args)) pre.append(type(expr_node).__name__) elif expr_node.is_constant(): pre.append(float(expr_node)) post_arity.append(len(expr_node.args)) return pre, pre_arity
def _extract_advection(self, order_dict): node = Add(*flatten(order_dict.itervalues())) divs = filter(lambda x: isinstance(x, div), preorder_traversal(node)) div_args = map(lambda d: d.args[0], divs) split_div_args = flatten(map(self._split_on_add, div_args)) div_args = filter(lambda n: not self._has_grad(n), split_div_args) return Add(*div_args)
def traversal(expr): '''Traverse sympy expression tree Args: expr (sympy expression) ''' return list(preorder_traversal(expr))
def to_monomials(expr): try: e = expr.expand() except Exception: if hasattr(expr, 'copy'): e = expr.copy() else: e = expr while True: has_change = False for x in preorder_traversal(e): # print("Doing %s" % str(x)) if hasattr(x, 'is_Mul') and x.is_Mul: # print("here") for i, y in enumerate(x.args): if hasattr(y, 'is_Add') and y.is_Add and\ noncommutative(y.args): if i == 0: terms = add_list_head(y.args, x.args[1:]) elif i == len(y.args)-1: terms = add_list_tail(y.args, x.args[:-1]) else: terms = add_list_middle( y.args, x.args[:i], x.args[i+1:]) enew = e.xreplace({x: terms}) if enew != e: has_change = True e = enew break if has_change: break if not has_change: break return e
def _conditional_replace(expr, condition, replacement): for x in preorder_traversal(expr): try: if condition(x): expr = expr.xreplace({x: replacement(x)}) except AttributeError: # scalar ops like Add won't have is_Trace pass return expr
def contain_exp(expr): """ Return True if expression contains the exponential operator. """ for arg in sym.preorder_traversal(expr): # check if there is an exponential function if arg.func == sym.exp: return True return False
def _extract_potential(self, order_dict): second_order = order_dict.get(2, 0) div_grads = filter(self._is_div_grad, preorder_traversal(second_order)) div_grad_args = map(lambda d: d.args[0], div_grads) potentials = flatten((map(self._find_grad_args, div_grad_args))) if len(potentials) == 1: potentials = potentials[0] return potentials
def tree_size(eq): i = 0 for e in sympy.preorder_traversal(self.expr): if e.is_Integer: i += int(abs(e)) continue i += 1 return i
def symbolic_equal(expression1, expression2, x, mu): locals = {"x": x, "mu": mu} difference = sympify(expression1, locals=locals) - sympify(expression2, locals=locals) difference = simplify(difference) for node in preorder_traversal(difference): if isinstance(node, Float): difference = difference.subs(node, round(node, 10)) return difference == 0
def tree_size(eq): i = 0 for e in sympy.preorder_traversal(self.expr): if e.is_Integer: i += int(abs(e)) continue i+=1 return i
def count_occurrences2(expr): """ Count atom occurrences in an expression. """ result = {} for sub_expr in sp.preorder_traversal(expr): if sub_expr.is_Atom: result[sub_expr] = result.get(sub_expr, 0) + 1 return result
def exprIsPositive(expr): '''This function is *incredibly* sketchy. It sometimes determines whether an expression is positive or negative. It's true for "x*y" and false for "-x*y".''' for a in preorder_traversal(expr): if issubclass(a.func, Number): if a < 0: return False
def generate_propagator_solver(self, output_timestep_symbol="__h"): r""" Generate the propagator matrix and symbolic expressions for propagator-based updates; return as JSON. """ # # generate the propagator matrix # P = sympy.simplify(sympy.exp(self.A_ * sympy.Symbol(output_timestep_symbol))) if sympy.I in sympy.preorder_traversal(P): raise PropagatorGenerationException("The imaginary unit was found in the propagator matrix. This can happen if the dynamical system that was passed to ode-toolbox is unstable, i.e. one or more state variables will diverge to minus or positive infinity.") # # generate symbols for each nonzero entry of the propagator matrix # P_sym = sympy.zeros(*P.shape) # each entry in the propagator matrix is assigned its own symbol P_expr = {} # the expression corresponding to each propagator symbol update_expr = {} # keys are str(variable symbol), values are str(expressions) that evaluate to the new value of the corresponding key for row in range(P_sym.shape[0]): if not _is_zero(self.c_[row]): raise PropagatorGenerationException("For symbol " + str(self.x_[row]) + ": nonlinear part should be zero for propagators") if not _is_zero(self.b_[row]) and self.shape_order_from_system_matrix(row) > 1: raise PropagatorGenerationException("For symbol " + str(self.x_[row]) + ": higher-order inhomogeneous ODEs are not supported") update_expr_terms = [] for col in range(P_sym.shape[1]): if not _is_zero(P[row, col]): sym_str = "__P__{}__{}".format(str(self.x_[row]), str(self.x_[col])) P_sym[row, col] = sympy.parsing.sympy_parser.parse_expr(sym_str, global_dict=Shape._sympy_globals) P_expr[sym_str] = P[row, col] if _is_zero(self.b_[row]): # homogeneous ODE update_expr_terms.append(sym_str + " * " + str(self.x_[col])) else: # inhomogeneous ODE particular_solution = -self.b_[row] / self.A_[row, row] update_expr_terms.append(sym_str + " * (" + str(self.x_[col]) + " - (" + str(particular_solution) + "))" + " + (" + str(particular_solution) + ")") update_expr[str(self.x_[row])] = " + ".join(update_expr_terms) update_expr[str(self.x_[row])] = sympy.parsing.sympy_parser.parse_expr(update_expr[str(self.x_[row])], global_dict=Shape._sympy_globals) if not _is_zero(self.b_[row]): # only simplify in case an inhomogeneous term is present update_expr[str(self.x_[row])] = sympy.simplify(update_expr[str(self.x_[row])]) all_state_symbols = [str(sym) for sym in self.x_] initial_values = {sym: str(self.get_initial_value(sym)) for sym in all_state_symbols} solver_dict = {"propagators": P_expr, "update_expressions": update_expr, "state_variables": all_state_symbols, "initial_values": initial_values} return solver_dict
def exprIsPositive(expr): '''This function is *incredibly* sketchy. It sometimes determines whether an expression is positive or negative. It's true for "x*y" and false for "-x*y".''' for a in preorder_traversal(expr): if issubclass(a.func,Number): if a<0: return False
def list_indices(expr): "Gathers the list of pulse indices present in expr. Returns a sorted list" return sorted( list( set( chain(*[ map(abs, a.k) for a in sy.preorder_traversal(expr) if isinstance(a, Signal) ]))))
def round_nested(expression: sp.Expr, n_decimals: int) -> sp.Expr: for node in sp.preorder_traversal(expression): if node.free_symbols: continue if isinstance(node, (float, sp.Float)): expression = expression.subs(node, round(node, n_decimals)) if isinstance(node, sp.Pow) and node.args[1] == 1 / 2: expression = expression.subs(node, round(node.n(), n_decimals)) return expression
def _is_div_grad(node): """Returns true node is a div with a grad argument inside""" is_div = isinstance(node, div) div_grad = False if is_div: for arg in preorder_traversal(node.args[0]): if isinstance(arg, grad): div_grad = True break return div_grad
def round_and_simplify(stuff): simplified_stuff = simplify(stuff) rounded_stuff = simplified_stuff for a in preorder_traversal(rounded_stuff): if isinstance(a, Float): rounded_stuff = rounded_stuff.subs(a, round(a, 10)) rounded_and_simplified_stuff = simplify(rounded_stuff) return rounded_and_simplified_stuff
def extract_funcs(expr): """ Given a sympy expression, return a tuple of all the functions contained within that expression. """ functions = set() for arg in preorder_traversal(expr): if isinstance(arg, Function): functions.add(arg) return tuple(functions)
def calc_tree_size(self): i = 0 p = 0 for e in sympy.preorder_traversal(self.expr): if e.is_Integer: i += int(abs(e)) continue if e.is_Function: p += 2 # add an extra penalty i += 1 return i, i + p
def dse_dimensions(expr): """ Collect all function dimensions used in a sympy expression. """ dimensions = [] for e in preorder_traversal(expr): if isinstance(e, SymbolicData): dimensions += [i for i in e.indices if i not in dimensions] return dimensions
def calc_jac_size(self): i, p = 0, 0 for jac in self.jac: for e in sympy.preorder_traversal(jac): if e.is_Integer: i += int(abs(e)) continue if e.is_Function: p += 2 # add an extra penalty i += 1 return i, i + p
def calc_tree_size(self): i = 0 p = 0 for e in sympy.preorder_traversal(self.expr): if e.is_Integer: i += int(abs(e)) continue if e.is_Function: p += 2 # add an extra penalty i+=1 return i, i+p
def calc_jac_size(self): i,p = 0,0 for jac in self.jac: for e in sympy.preorder_traversal(jac): if e.is_Integer: i += int(abs(e)) continue if e.is_Function: p += 2 # add an extra penalty i+=1 return i, i+p
def indexed_terms_appearing_in(term, indexed, only_subscripts=False, do_traversal=False): indexed_terms_set = set() for subterm in preorder_traversal(term) if do_traversal else flatten(term.args, cls=Add): try: with bind_Mul_indexed(subterm, indexed) as (_, subscripts): indexed_terms_set.add(subscripts if only_subscripts else indexed[subscripts]) except DestructuringError: continue return list(indexed_terms_set)
def encode(self,expr): # print expr iis = [] ffs = [] for i,e in enumerate(sympy.preorder_traversal(expr)): # print i, e.func ii, ff = self.mapper.get(e) iis = iis + ii if ff is not None: iis.append(len(ffs)) ffs = ffs + ff # print " ", ii, ff return iis, ffs
def symbols_from_expr(expr, include_numbers=False, include_derivatives=False): """ Returns a set of all symbols of an expression Arguments: ---------- expr : sympy expression A sympy expression containing sympy.Symbols or sympy.AppliedUndef functions. include_numbers : bool If True numbers will also be returned include_derivatives : bool If True derivatives will be returned instead of its variables """ from sympy.core.function import AppliedUndef symbols = set() pt = sp.preorder_traversal(expr) for node in pt: # Do not traverse AppliedUndef if isinstance(node, AppliedUndef): pt.skip() symbols.add(node) elif isinstance(node, sp.Symbol) and not isinstance(node, sp.Dummy) \ and node.name: symbols.add(node) elif include_numbers and isinstance(node, sp.Number): symbols.add(node) elif include_derivatives and isinstance(node, sp.Derivative): # Do not traverse Derivative pt.skip() symbols.add(node) return symbols
def rearangeForDerivs(self,eoms,secondOrder=False): suffix = self.derivative_suffix if secondOrder: suffix += suffix derivSet = set() for eom in eoms: for x in sympy.preorder_traversal(eom): if type(x) == sympy.Symbol and suffix in str(x): derivSet.add(x) derivList = list(derivSet) if len(derivList) == 0: raise Exception("No time derivative was found in eom: '{0}', where derivative has suffix of '{1}'".format(eom,suffix)) solns = sympy.solve(eoms,derivList) if len(solns) < len(derivList): raise Exception("Too few solutions, {2}, were found for '{1}'".format(eom,derivList,solns)) if len(solns) > len(derivList): raise Exception("Too many solutions, {2}, were found for '{1}'".format(eom,derivList,solns)) for deriv in derivList: try: solns[deriv] except KeyError: raise Exception("Couldn't find solution for {1}, in solutions {2}, in EOMs '{0}'".format(eom,deriv,solns)) return solns
def positif(expression, variable=None, strict=False, _niveau=0, _changement_variable=None): """Retourne l'ensemble sur lequel une expression à variable réelle est positive (resp. strictement positive).""" from .sympy_functions import factor # L'étude du signe se fait dans R, on indique donc à sympy que la variable est réelle. if variable is None: variable = extract_var(expression) if hasattr(expression, "subs"): old_variable = variable variable = Dummy(real=True) expression = expression.subs({old_variable:variable}) # Ensemble de définition et périodicité ens_def = ensemble_definition(expression, variable) T = periode(expression, variable) if T not in (0, oo): ens_def &= Intervalle(0, T) if param.debug: print('Fonction périodique %s détectée. Résolution sur [0;%s]' % (expression, T)) # On remplace sqrt(x^2) par |x|. a = Wild('a', exclude=[expression]) dic = expression.match(sqrt(a**2)) if dic is not None: sub_expr = dic[a] # On évite de détecter sqrt(x) comme étant sqrt(sqrt(x)**2) ! if not (sub_expr.is_Pow and sub_expr.as_base_exp()[1].is_integer): expression = expression.replace(sqrt(a**2),Abs(a)) expression = expression.replace(Abs(sqrt(a)),sqrt(a)) del a # On factorise au maximum. try: expression = factor(expression, variable, "R", decomposer_entiers = False) except NotImplementedError: if param.debug: print("Warning: Factorisation impossible de ", expression) if expression.is_Pow and expression.as_base_exp()[1].is_rational: base, p = expression.as_base_exp() # Le dénominateur ne doit pas s'annuler : if p < 0: strict = True if p.is_integer and p.is_even: if strict: return ens_def & (R - (positif(base, variable, strict=False) - positif(base, variable, strict=True))) else: return ens_def else: return ens_def & positif(base, variable, strict=strict) if expression.is_Mul: posit = R posit_nul = R for facteur in expression.args: # pos : ensemble des valeurs pour lequelles l'expression est positive # pos_nul : ensemble des valeurs pour lequelles l'expression est positive ou nulle pos = positif(facteur, variable, strict = True) pos_nul = positif(facteur, variable, strict = False) # posit : les deux sont strictements positifs, ou les deux sont strictements négatifs # posit_nul : les deux sont positifs ou nuls, ou les deux sont négatifs ou nuls posit, posit_nul = ((posit & pos) + (-posit_nul & -pos_nul) & ens_def, ((posit_nul & pos_nul) + (-posit & -pos)) & ens_def) if strict: return posit else: return posit_nul if expression.is_positive is True: # > 0 return ens_def if expression.is_negative is True: # < 0 return vide if expression.is_positive is False and strict: # <= 0 return vide if expression.is_negative is False and not strict: # >= 0 return ens_def if expression.is_zero is True and not strict: # == 0 return ens_def if isinstance(expression, (int, float)): if expression > 0 or (expression == 0 and not strict): return ens_def else: return vide # Inutile de se préoccuper de l'ensemble de définition pour les fonctions polynômiales. if expression.is_polynomial(): P = expression.as_poly(variable) if P.degree() == 1: a, b = P.all_coeffs() if a > 0: return Intervalle(-b/a, +oo, inf_inclus = not strict) else: # a<0 (car a != 0) return Intervalle(-oo, -b/a, sup_inclus = not strict) elif P.degree() == 2: a, b, c = P.all_coeffs() d = b**2 - 4*a*c if d > 0: x1 = (-b - sqrt(d))/(2*a) x2 = (-b + sqrt(d))/(2*a) x1, x2 = min(x1, x2), max(x1, x2) if a > 0: return Intervalle(-oo, x1, sup_inclus = not strict) + Intervalle(x2, +oo, inf_inclus = not strict) else: # a<0 (car a != 0) return Intervalle(x1, x2, inf_inclus = not strict, sup_inclus = not strict) elif d == 0: x0 = -b/(2*a) if a > 0: return Intervalle(-oo, x0, sup_inclus = not strict) + Intervalle(x0, +oo, inf_inclus = not strict) else: return Intervalle(x0, x0, sup_inclus = not strict) else: # d < 0 if a > 0: return R else: return vide # Valeur absolue seule: if isinstance(expression, Abs): return ens_def # Valeur absolue: cas général. L'idée est simplement de supprimer la valeur # absolue en faisant deux cas: |f(x)| = f(x) si f(x)>=0 et -f(x) si f(x)<0. # (Le `for` est trompeur: on ne supprime qu'une seule valeur absolue à la fois, la récursivité fait le reste...) for subexpr in expression.find(Abs): # subexpr = |f(x)| # val = f(x) val = subexpr.args[0] pos = positif(val, variable) # g(|f(x)|, x) > 0 <=> g(f(x), x) > 0 et f(x) >= 0 ou g(-f(x), x) > 0 et f(x) < 0 return ((positif(expression.xreplace({subexpr: val}), variable, strict=strict) & pos) | (positif(expression.xreplace({subexpr: -val}), variable, strict=strict) & -pos)) # Puissances # Résolution de a*x^q-b > 0, où q n'est pas entier if expression.is_Add: a = Wild('a', exclude=[variable, 0]) q = Wild('q', exclude=[variable, 1]) b = Wild('b', exclude=[variable]) X = Wild('X') # match ne semble pas fonctionner avec a*X**q - b match = expression.match(a*X**q + b) if match and X in match: a = match[a] q = match[q] b = -match[b]/a X = match[X] # Le cas où q est entier est déjà traité par ailleurs (polynômes). # Lorsque q est de la forme 1/n, on peut définir la fonction x−>x^(1/n) # sur R si n est impair et sur R+ sinon. # Dans tous les autres cas, on définit la fonction x->x^q sur ]0;+oo[. if not q.is_integer: if a.is_negative: # si a < 0, a*x^q-b > 0 <=> x^q-b/a < 0 <=> non(x^q-b/a >= 0) strict = not strict if b.is_negative: # pour X > 0, X^(2/3)-b > X^2/3 > 0 sols = ens_def else: # pour X > 0, X^(2/3)-b > 0 <=> X > b^(3/2) sols = ens_def & positif(X - b**(1/q), strict=strict) # si a < 0, a*x^q-b > 0 <=> x^q-b/a < 0 <=> non(x^q-b/a >= 0) if a.is_negative: sols = ens_def - sols return sols del a, q, b, X # résolution de a*sqrt(B)-c*sqrt(D) > 0 où signe(c) = signe(a) if expression.is_Add: a = Wild('a', exclude=[0, variable]) B = Wild('B', exclude=[0]) c = Wild('c', exclude=[0, variable]) D = Wild('D', exclude=[0]) match = expression.match(a*sqrt(B) - c*sqrt(D)) if match: a = match[a] B = match[B] c = match[c] D = match[D] if a.is_positive and c.is_positive: return ens_def & positif(a**2*B - c**2*D, variable, strict=strict) elif a.is_negative and c.is_negative: return ens_def & positif(c**2*D - a**2*B, variable, strict=strict) del a, B, c, D # Logarithme : if isinstance(expression, ln): return positif(expression.args[0] - 1, variable, strict=strict) # Sommes contenant des logarithmes : # Résolution de ln(X1) + ln(X2) + ... + b > 0, où X1=f1(x), X2 = f2(x) ... if expression.is_Add and expression.has(ln): args = expression.args liste_constantes = [] liste_ln = [] # Premier passage : on remplace a*ln(X1) par ln(X1**a) for arg in args: if getattr(arg, "is_Mul", False): liste_constantes = [] liste_ln = [] for facteur in arg.args: if isinstance(facteur, ln) and is_var(facteur, variable): liste_ln.append(facteur) elif not is_var(facteur, variable): liste_constantes.append(facteur) if len(liste_constantes) == len(arg.args) - 1 and len(liste_ln) == 1: expression += ln(liste_ln[0].args[0]**Add(*liste_constantes)) - arg # Deuxième passage : ln(X1)+ln(X2)+b>0 <=> X1*X2-exp(-b)>0 for arg in args: if isinstance(arg, ln) and hasattr(arg, "has") and arg.has(variable): liste_ln.append(arg) elif not hasattr(arg, "has") or not arg.has(variable): liste_constantes.append(arg) if liste_ln and len(liste_ln) + len(liste_constantes) == len(args): # ln(X1)+ln(X2)+b>0 <=> X1*X2-exp(-b)>0 contenu_log = Mul(*(logarithme.args[0] for logarithme in liste_ln)) contenu_cst = exp(- Add(*liste_constantes)) return ens_def & positif(contenu_log - contenu_cst, variable, strict=strict) # Cas très particulier : on utilise le fait que ln(x)<=x-1 sur ]0;+oo[ expr = expression changements = False for arg in expr.args: if isinstance(arg, ln): changements = True expr += arg.args[0] + 1 - arg if changements: try: non_positif = positif(-expr, variable, strict = not strict) # complementaire (ens_def - positif(expr, variable, strict = not strict) == vide) if (ens_def - non_positif == vide): return vide except NotImplementedError: pass # Si aucune autre méthode n'a fonctionné, on tente ln(a)+ln(b)>0 <=> a*b>1 (pour a>0 et b>0) expr = Mul(*(exp(arg) for arg in expression.args)) - 1 try: return ens_def*positif(expr, variable, strict=strict) except NotImplementedError: pass # Exponentielle # Résolution de a*exp(f(x)) + b > 0 if expression.is_Add and expression.has(exp): a = Wild('a', exclude=[variable, 0]) b = Wild('b', exclude=[variable]) X = Wild('X') match = expression.match(a*exp(X) + b) if match: a = match[a] b = match[b] X = match[X] if is_pos(b): if is_pos(a): return ens_def elif is_neg(a): # l'ensemble de définition ne change pas return positif(- X + ln(-b/a), variable, strict=strict) elif is_neg(b): if is_pos(a): return positif(X - ln(-b/a), variable, strict=strict) elif is_neg(a): return vide del a, b, X # Cas très particulier : on utilise le fait que exp(x)>=x+1 sur R expr = expression changements = False for arg in expr.args: if isinstance(arg, exp): changements = True expr += arg.args[0] + 1 - arg if changements and (ens_def - positif(expr, variable, strict=strict) == vide): return ens_def # Cas très particulier : si a≥0, b*(a*x-1+exp(x))>0 <=> b*x>0 a = Wild('a', exclude=[variable, 0]) b = Wild('b', exclude=[variable, 0]) X = Wild('X') match = expression.match(b*(a*X - 1 + exp(X))) if match is not None and X in match: a = match[a] if a >= 0: b = match[b] X = match[X] return positif(b*X, variable, strict=strict) del a, b, X # Aucun cas connu n'a été détecté, on tente un changement de variable. # NB: _niveau sert à éviter les récursions infinies ! if _niveau > 10: print("Infinite recursion suspected. Aborting...") raise NotImplementedError # La technique globalement est la suivante : # sqrt(x² + 5) - 9 > 0 <=> sqrt(X) - 9 > 0 (avec X = x² + 5) <=> X in ]3;+oo[ <=> X - 3 > 0 <=> x² + 5 - 3 > 0, etc. def _resolution_par_substitution(expr, X): try: solutions_intermediaires = positif(expr, X, strict=strict, _niveau=_niveau + 1, _changement_variable=Lambda(variable,sub)) solution = vide for intervalle in solutions_intermediaires.intervalles: sol = R a = intervalle.inf b = intervalle.sup if a != - oo: sol &= positif(sub - a, variable, strict=(not intervalle.inf_inclus)) if b != oo: sol &= positif(b - sub, variable, strict=(not intervalle.sup_inclus)) solution += sol return ens_def & solution except NotImplementedError: return None # On commence par lister tous les changements de variable potentiels, # c'est-à-dire toutes les sous-expressions qui contiennent la variable. changements = sorted(set(subexpr for subexpr in preorder_traversal(expression) if subexpr.has(variable)) - {variable, expression}, key=count_ops) a = Wild('a', exclude=[variable, 0]) b = Wild('b', exclude=[variable, 0]) X = Dummy(real=True) changements_incomplets = [] for sub in changements: if _changement_variable is not None: enchainement = _changement_variable(sub) if enchainement.match(a*variable) or enchainement.match(a*abs(variable)): # On enchaîne deux changements de variable réciproque, # comme ln() et exp() ou encore x->x² et x-> sqrt(x). # Ce qui conduirait à une récursion infinie. continue match = expression.match(a*sub + b) if match and match[a] in (-1, 1): # Ceci conduirait à des récursions infinies du genre : # sqrt(x² + 5) - 9 > 0 <=> X - 9 > 0 (avec X = sqrt(x² + 5)) <=> X in ]9;+oo[ <=> X - 9 > 0 <=> sqrt(x² + 5) - 9 > 0, etc. continue # On tente le changement de variable. new = expression.subs(sub, X) if new.has(variable): # Changement de variable incomplet. # On le garde en réserve pour une 2e passe si on n'a pas trouvé mieux. changements_incomplets.append(sub) else: sols = _resolution_par_substitution(new, X) if sols is not None: return sols for sub in changements_incomplets: # On cherche si le changement de variable est inversible. # Si oui, au lieu de remplacer f(x) par X, on remplace x par f-¹(X) # Bizarrement, l'assertion real=True fait planter le solver() de sympy # lorsqu'on lance solve(sqrt(x)-y, x). On utilise donc des variables # temporaires sans assertions. var1 = Dummy() var2 = Dummy() antecedents = solve((sub - X).xreplace({variable:var1, X:var2}), var1) if len(antecedents) == 1: new = expression.subs(variable, antecedents[0].xreplace({var1:variable, var2:X})) sols = _resolution_par_substitution(new, X) if sols is not None: return sols # En dernier ressort, résolution par continuité. # Les fonctions usuelles sont continues sur tout intervalle de leur ensemble de définition. # Il suffit donc de rechercher les zéros de la fonction, et d'évaluer le signe # de l'expression sur chaque tronçon. # Cette méthode est simple, mais elle suppose que **tous** les zéros aient bien # été trouvés par solve(), ce qui n'est pas toujours le cas. # C'est pourquoi elle est proposée seulement en dernier recours. if param.debug: print("Warning: résolution par continuité. Les résultats peuvent être\n" "faux si certaines racines ne sont pas touvées !") racines = nul(expression, variable, intervalle=False) solutions = vide for intervalle in ens_def.intervalles: inf = intervalle.inf sup = intervalle.sup decoupage = {S(inf), S(sup)} for borne in decoupage: if borne in intervalle: if expression.subs(variable, borne) > 0: solutions |= Intervalle(borne, borne) for rac in racines: if rac in intervalle: if not strict: solutions |= Intervalle(rac, rac) decoupage.add(rac) decoupage = sorted(decoupage) for val1, val2 in zip(decoupage[:-1], decoupage[1:]): if val1 == -oo and val2 == oo: milieu = 0 elif val2 == oo: milieu = val1 + 1 elif val1 == -oo: milieu = val2 - 1 else: milieu = (val1 + val2)/2 if expression.subs(variable, milieu) > 0: solutions |= Intervalle(val1, val2, False, False) return ens_def&solutions
def _has_grad(node): """Return true if node has a grad in it""" return len(filter(lambda n: isinstance(n, grad), preorder_traversal(node))) > 0
def _has_div(node): """Return true if node has a div in it""" return len(filter(lambda n: isinstance(n, div), preorder_traversal(node))) > 0
def _extract_diffusion(self, order_dict): second_order = order_dict.get(2, 0) div_grads = filter(self._is_div_grad, preorder_traversal(second_order)) div_grad_args = map(lambda d: d.args[0], div_grads) grads = map(self._find_grad_coefficient, div_grad_args) return Add(*list(grads))
def has_sqrt(sympy_obj): return any(el.func is C.Pow and el.args[-1] is S.Half for el in preorder_traversal(sympy_obj))