def test_rel_subs(): e = Relational(x, y, '==') e = e.subs(x,z) assert isinstance(e, Equality) assert e.lhs == z assert e.rhs == y e = Relational(x, y, '<') e = e.subs(x,z) assert isinstance(e, StrictInequality) assert e.lhs == z assert e.rhs == y
def test_rel_subs(): e = Relational(x, y, "==") e = e.subs(x, z) assert isinstance(e, Equality) assert e.lhs == z assert e.rhs == y e = Relational(x, y, "<") e = e.subs(x, z) assert isinstance(e, StrictInequality) assert e.lhs == z assert e.rhs == y e = Eq(x, 0) assert e.subs(x, 0) == True assert e.subs(x, 1) == False
def __new__(cls, lhs, rhs=0, **assumptions): from sympy.matrices.expressions.matexpr import ( MatrixElement, MatrixSymbol) from sympy.tensor.indexed import Indexed lhs = _sympify(lhs) rhs = _sympify(rhs) # Tuple of things that can be on the lhs of an assignment assignable = (Symbol, MatrixSymbol, MatrixElement, Indexed) if not isinstance(lhs, assignable): raise TypeError("Cannot assign to lhs of type %s." % type(lhs)) # Indexed types implement shape, but don't define it until later. This # causes issues in assignment validation. For now, matrices are defined # as anything with a shape that is not an Indexed lhs_is_mat = hasattr(lhs, 'shape') and not isinstance(lhs, Indexed) rhs_is_mat = hasattr(rhs, 'shape') and not isinstance(rhs, Indexed) # If lhs and rhs have same structure, then this assignment is ok if lhs_is_mat: if not rhs_is_mat: raise ValueError("Cannot assign a scalar to a matrix.") elif lhs.shape != rhs.shape: raise ValueError("Dimensions of lhs and rhs don't align.") elif rhs_is_mat and not lhs_is_mat: raise ValueError("Cannot assign a matrix to a scalar.") return Relational.__new__(cls, lhs, rhs, **assumptions)
def __init__(self, fx, x0, direction="+-"): """ ------------------------------------------------------------------ __init__: Initializes variables for Delta Epsilon Proof. ------------------------------------------------------------------ Parameters: fx: The given function we're taking the limit of. x0: What x is approaching. direction: In which direction x is approaching. ------------------------------------------------------------------ """ # evaluate limit try: limit = sm.limit(fx, sm.Symbol('x'), x0, dir=direction) # limit does not exist except ValueError: self.L = sm.Symbol("l") limit = self.L # save variables self.fx = fx self.x0 = x0 self.direction = direction self.limit = limit # setup x self.x = (sm.Symbol("x", real=True, positive=True) if x0 == sm.oo else sm.Symbol("x", real=True, negative=True) if x0 == -sm.oo else sm.Symbol("x", real=True)) # setup the other variables self.delta = sm.Symbol("N" if x0 == sm.oo or x0 == -sm.oo else "delta", positive=True) self.epsilon = sm.Symbol( "M" if limit == sm.oo or limit == -sm.oo else "epsilon", positive=True) # possible givens givens = [[ Relational(abs(self.x - x0), self.delta, "<"), Relational(self.x - x0, self.delta, "<"), Relational(x0 - self.x, self.delta, "<") ], [Relational(self.x, self.delta, ">")], [Relational(-self.x, self.delta, ">")]] # possible starting equations str_starting_equations = [[ "|" + sm.latex(fx) + "-" + sm.latex(limit) + "|", sm.latex(fx) + "-" + sm.latex(limit), sm.latex(limit) + "-" + sm.latex(fx) ], [sm.latex(fx)] * 3, [sm.latex(-fx)] * 3] # possible starting equations starting_equations = [[abs(fx - limit), fx - limit, limit - fx], [fx] * 3, [-fx] * 3] # dictionary for side side_dict = {"+-": 0, "+": 1, "-": 2} side = side_dict[direction] x0_inf = 1 if x0 == sm.oo else 2 if x0 == -sm.oo else 0 fx_inf = 1 if limit == sm.oo else 2 if limit == -sm.oo else 0 # setup equations self.given = givens[x0_inf][side] self.starting_equation = starting_equations[fx_inf][side] self.str_starting_equation = str_starting_equations[fx_inf][side] # setup substitutions self.sub_format_list = [(str(self.epsilon), self.epsilon), (str(self.delta), self.delta), ("x", self.x)] self.sub_list = [(self.delta, self.given.lhs)] # store what delta is self.delta_exp_latex = "" self.delta_exp = None self.delta_bound = 0 self.curr_bounding_equation = None self.bounded_exp = (None, None) self.bounding_equations = []
def test_rel_subs(): e = Relational(x, y, '==') e = e.subs(x, z) assert isinstance(e, Equality) assert e.lhs == z assert e.rhs == y e = Relational(x, y, '>=') e = e.subs(x, z) assert isinstance(e, GreaterThan) assert e.lhs == z assert e.rhs == y e = Relational(x, y, '<=') e = e.subs(x, z) assert isinstance(e, LessThan) assert e.lhs == z assert e.rhs == y e = Relational(x, y, '>') e = e.subs(x, z) assert isinstance(e, StrictGreaterThan) assert e.lhs == z assert e.rhs == y e = Relational(x, y, '<') e = e.subs(x, z) assert isinstance(e, StrictLessThan) assert e.lhs == z assert e.rhs == y e = Eq(x, 0) assert e.subs(x, 0) is S.true assert e.subs(x, 1) is S.false
def test_rel_ne(): assert Relational(x, y, '!=') == Ne(x, y)
def reduce_abs_inequality(expr, rel, gen): """Reduce an inequality with nested absolute values. Examples ======== >>> from sympy import Abs, Symbol >>> from sympy.solvers.inequalities import reduce_abs_inequality >>> x = Symbol('x', real=True) >>> reduce_abs_inequality(Abs(x - 5) - 3, '<', x) (2 < x) & (x < 8) >>> reduce_abs_inequality(Abs(x + 2)*3 - 13, '<', x) (-19/3 < x) & (x < 7/3) See Also ======== reduce_abs_inequalities """ if gen.is_real is False: raise TypeError( filldedent(''' can't solve inequalities with absolute values 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 ValueError("Only Integer Powers are allowed on Abs.") _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)])) 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_abs_inequality(expr, rel, gen, assume=True): """Reduce an inequality with nested absolute values. """ if not ask(Q.real(gen), assumptions=assume): raise NotImplementedError( "can't solve inequalities with absolute values of a complex variable" ) def bottom_up_scan(expr): exprs = [] if expr.is_Add or expr.is_Mul: op = expr.__class__ 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 or n < 0: raise ValueError( "only non-negative integer powers are allowed") _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)])) 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_poly_inequalities(inequalities, gen, assume)
def eval(cls, *_args): """Either return a modified version of the args or, if no modifications were made, return None. Modifications that are made here: 1) relationals are made canonical 2) any False conditions are dropped 3) any repeat of a previous condition is ignored 3) any args past one with a true condition are dropped If there are no args left, nan will be returned. If there is a single arg with a True condition, its corresponding expression will be returned. """ from sympy.functions.elementary.complexes import im, re if not _args: return Undefined if len(_args) == 1 and _args[0][-1] == True: return _args[0][0] newargs = [] # the unevaluated conditions current_cond = set() # the conditions up to a given e, c pair # make conditions canonical args = [] for e, c in _args: if (not c.is_Atom and not isinstance(c, Relational) and not c.has(im, re)): free = c.free_symbols if len(free) == 1: funcs = [ i for i in c.atoms(Function) if not isinstance(i, Boolean) ] if len(funcs) == 1 and len( c.xreplace({ list(funcs)[0]: Dummy() }).free_symbols) == 1: # we can treat function like a symbol free = funcs _c = c x = free.pop() try: c = c.as_set().as_relational(x) except NotImplementedError: pass else: reps = {} for i in c.atoms(Relational): ic = i.canonical if ic.rhs in (S.Infinity, S.NegativeInfinity): if not _c.has(ic.rhs): # don't accept introduction of # new Relationals with +/-oo reps[i] = S.true elif ('=' not in ic.rel_op and c.xreplace( {x: i.rhs}) != _c.xreplace({x: i.rhs})): reps[i] = Relational( i.lhs, i.rhs, i.rel_op + '=') c = c.xreplace(reps) args.append((e, _canonical(c))) for expr, cond in args: # Check here if expr is a Piecewise and collapse if one of # the conds in expr matches cond. This allows the collapsing # of Piecewise((Piecewise((x,x<0)),x<0)) to Piecewise((x,x<0)). # This is important when using piecewise_fold to simplify # multiple Piecewise instances having the same conds. # Eventually, this code should be able to collapse Piecewise's # having different intervals, but this will probably require # using the new assumptions. if isinstance(expr, Piecewise): unmatching = [] for i, (e, c) in enumerate(expr.args): if c in current_cond: # this would already have triggered continue if c == cond: if c != True: # nothing past this condition will ever # trigger and only those args before this # that didn't match a previous condition # could possibly trigger if unmatching: expr = Piecewise(*(unmatching + [(e, c)])) else: expr = e break else: unmatching.append((e, c)) # check for condition repeats got = False # -- if an And contains a condition that was # already encountered, then the And will be # False: if the previous condition was False # then the And will be False and if the previous # condition is True then then we wouldn't get to # this point. In either case, we can skip this condition. for i in ([cond] + (list(cond.args) if isinstance(cond, And) else [])): if i in current_cond: got = True break if got: continue # -- if not(c) is already in current_cond then c is # a redundant condition in an And. This does not # apply to Or, however: (e1, c), (e2, Or(~c, d)) # is not (e1, c), (e2, d) because if c and d are # both False this would give no results when the # true answer should be (e2, True) if isinstance(cond, And): nonredundant = [] for c in cond.args: if (isinstance(c, Relational) and c.negated.canonical in current_cond): continue nonredundant.append(c) cond = cond.func(*nonredundant) elif isinstance(cond, Relational): if cond.negated.canonical in current_cond: cond = S.true current_cond.add(cond) # collect successive e,c pairs when exprs or cond match if newargs: if newargs[-1].expr == expr: orcond = Or(cond, newargs[-1].cond) if isinstance(orcond, (And, Or)): orcond = distribute_and_over_or(orcond) newargs[-1] = ExprCondPair(expr, orcond) continue elif newargs[-1].cond == cond: newargs[-1] = ExprCondPair(expr, cond) continue newargs.append(ExprCondPair(expr, cond)) # some conditions may have been redundant missing = len(newargs) != len(_args) # some conditions may have changed same = all(a == b for a, b in zip(newargs, _args)) # if either change happened we return the expr with the # updated args if not newargs: raise ValueError( filldedent(''' There are no conditions (or none that are not trivially false) to define an expression.''')) if missing or not same: return cls(*newargs)
def __new__(cls, lhs, rhs=0, **options): lhs = _sympify(lhs) rhs = _sympify(rhs) return Relational.__new__(cls, lhs, rhs, **options)
def test_rel_ne(): Relational(x, y, '!=') # this used to raise
def reduce_rational_inequalities(exprs, gen, relational=True): """Reduce a system of rational inequalities with rational coefficients. Examples ======== >>> from sympy import Symbol >>> from sympy.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) This function find the non-infinite solution set so if the unknown symbol is declared as extended real rather than real then the result may include finiteness conditions: >>> y = Symbol('y', extended_real=True) >>> reduce_rational_inequalities([[y + 2 > 0]], y) (-2 < y) & (y < oo) """ 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) exclude = solve_rational_inequalities([[((d, d.one), '==') for i in eqs for ((n, d), _) in i if d.has(gen)]]) solution -= exclude if not exact and solution: solution = solution.evalf() if relational: solution = solution.as_relational(gen) return solution
def trim(f, *symbols, **flags): """Cancel common factors in a given formal rational expression. Given an arbitrary expression, map all functional components to temporary symbols, rewriting this expression to rational function form and perform cancelation of common factors. When given a rational function or a list of symbols discards all functional components, then this procedure is equivalent to cancel(). Note that this procedure can thread over composite objects like big operators, matrices, relational operators etc. It can be also called recursively (to change this behaviour unset 'recursive' flag). >>> from sympy import * >>> x,y = symbols('xy') >>> f = Function('f') >>> trim((f(x)**2+f(x))/f(x)) 1 + f(x) >>> trim((x**2+x)/x) 1 + x Recursively simplify expressions: >>> trim(sin((f(x)**2+f(x))/f(x))) sin(1 + f(x)) """ f = sympify(f) if isinstance(f, Relational): return Relational(trim(f.lhs, *symbols, **flags), trim(f.rhs, *symbols, **flags), f.rel_op) #elif isinstance(f, Matrix): # return f.applyfunc(lambda g: trim(g, *symbols, **flags)) else: recursive = flags.get('recursive', True) def is_functional(g): return not (g.is_Atom or g.is_number) \ and (not symbols or g.has(*symbols)) def components(g): result = set() if is_functional(g): if g.is_Add or g.is_Mul: args = [] for h in g.args: h, terms = components(h) result |= terms args.append(h) g = g.__class__(*args) elif g.is_Pow: if recursive: base = trim(g.base, *symbols, **flags) else: base = g.base if g.exp.is_Rational: if g.exp.is_Integer: if g.exp is S.NegativeOne: h, terms = components(base) return h**S.NegativeOne, terms else: h = base else: h = base**Rational(1, g.exp.q) g = base**g.exp else: if recursive: h = g = base**trim(g.exp, *symbols, **flags) else: h = g = base**g.exp if is_functional(h): result.add(h) else: if not recursive: result.add(g) else: g = g.__class__(*[trim(h, *symbols, **flags) for h in g.args]) if is_functional(g): result.add(g) return g, result if f.is_number or not f.has_any_symbols(*symbols): return f f = together(f.expand()) f, terms = components(f) if not terms: return Poly.cancel(f, *symbols) else: mapping, reverse = {}, {} for g in terms: mapping[g] = Temporary() reverse[mapping[g]] = g p, q = f.as_numer_denom() f = p.expand()/q.expand() if not symbols: symbols = tuple(f.atoms(Symbol)) symbols = tuple(mapping.values()) + symbols H = Poly.cancel(f.subs(mapping), *symbols) if not flags.get('extract', True): return H.subs(reverse) else: def extract(f): p = f.args[0] for q in f.args[1:]: p = gcd(p, q, *symbols) if p.is_number: return S.One, f return p, Add(*[quo(g, p, *symbols) for g in f.args]) P, Q = H.as_numer_denom() if P.is_Add: GP, P = extract(P) else: GP = S.One if Q.is_Add: GQ, Q = extract(Q) else: GQ = S.One return ((GP*P)/(GQ*Q)).subs(reverse)
def test_new_relational(): x = Symbol('x') assert Eq(x, 0) == Relational(x, 0) # None ==> Equality assert Eq(x, 0) == Relational(x, 0, '==') assert Eq(x, 0) == Relational(x, 0, 'eq') assert Eq(x, 0) == Equality(x, 0) assert Eq(x, 0) != Relational(x, 1) # None ==> Equality assert Eq(x, 0) != Relational(x, 1, '==') assert Eq(x, 0) != Relational(x, 1, 'eq') assert Eq(x, 0) != Equality(x, 1) assert Eq(x, -1) == Relational(x, -1) # None ==> Equality assert Eq(x, -1) == Relational(x, -1, '==') assert Eq(x, -1) == Relational(x, -1, 'eq') assert Eq(x, -1) == Equality(x, -1) assert Eq(x, -1) != Relational(x, 1) # None ==> Equality assert Eq(x, -1) != Relational(x, 1, '==') assert Eq(x, -1) != Relational(x, 1, 'eq') assert Eq(x, -1) != Equality(x, 1) assert Ne(x, 0) == Relational(x, 0, '!=') assert Ne(x, 0) == Relational(x, 0, '<>') assert Ne(x, 0) == Relational(x, 0, 'ne') assert Ne(x, 0) == Unequality(x, 0) assert Ne(x, 0) != Relational(x, 1, '!=') assert Ne(x, 0) != Relational(x, 1, '<>') assert Ne(x, 0) != Relational(x, 1, 'ne') assert Ne(x, 0) != Unequality(x, 1) assert Ge(x, 0) == Relational(x, 0, '>=') assert Ge(x, 0) == Relational(x, 0, 'ge') assert Ge(x, 0) == GreaterThan(x, 0) assert Ge(x, 1) != Relational(x, 0, '>=') assert Ge(x, 1) != Relational(x, 0, 'ge') assert Ge(x, 1) != GreaterThan(x, 0) assert (x >= 1) == Relational(x, 1, '>=') assert (x >= 1) == Relational(x, 1, 'ge') assert (x >= 1) == GreaterThan(x, 1) assert (x >= 0) != Relational(x, 1, '>=') assert (x >= 0) != Relational(x, 1, 'ge') assert (x >= 0) != GreaterThan(x, 1) assert Le(x, 0) == Relational(x, 0, '<=') assert Le(x, 0) == Relational(x, 0, 'le') assert Le(x, 0) == LessThan(x, 0) assert Le(x, 1) != Relational(x, 0, '<=') assert Le(x, 1) != Relational(x, 0, 'le') assert Le(x, 1) != LessThan(x, 0) assert (x <= 1) == Relational(x, 1, '<=') assert (x <= 1) == Relational(x, 1, 'le') assert (x <= 1) == LessThan(x, 1) assert (x <= 0) != Relational(x, 1, '<=') assert (x <= 0) != Relational(x, 1, 'le') assert (x <= 0) != LessThan(x, 1) assert Gt(x, 0) == Relational(x, 0, '>') assert Gt(x, 0) == Relational(x, 0, 'gt') assert Gt(x, 0) == StrictGreaterThan(x, 0) assert Gt(x, 1) != Relational(x, 0, '>') assert Gt(x, 1) != Relational(x, 0, 'gt') assert Gt(x, 1) != StrictGreaterThan(x, 0) assert (x > 1) == Relational(x, 1, '>') assert (x > 1) == Relational(x, 1, 'gt') assert (x > 1) == StrictGreaterThan(x, 1) assert (x > 0) != Relational(x, 1, '>') assert (x > 0) != Relational(x, 1, 'gt') assert (x > 0) != StrictGreaterThan(x, 1) assert Lt(x, 0) == Relational(x, 0, '<') assert Lt(x, 0) == Relational(x, 0, 'lt') assert Lt(x, 0) == StrictLessThan(x, 0) assert Lt(x, 1) != Relational(x, 0, '<') assert Lt(x, 1) != Relational(x, 0, 'lt') assert Lt(x, 1) != StrictLessThan(x, 0) assert (x < 1) == Relational(x, 1, '<') assert (x < 1) == Relational(x, 1, 'lt') assert (x < 1) == StrictLessThan(x, 1) assert (x < 0) != Relational(x, 1, '<') assert (x < 0) != Relational(x, 1, 'lt') assert (x < 0) != StrictLessThan(x, 1) # finally, some fuzz testing from random import randint for i in range(100): while 1: strtype, length = (chr, 65535) if randint(0, 1) else (chr, 255) relation_type = strtype(randint(0, length)) if randint(0, 1): relation_type += strtype(randint(0, length)) if relation_type not in ('==', 'eq', '!=', '<>', 'ne', '>=', 'ge', '<=', 'le', '>', 'gt', '<', 'lt', ':=', '+=', '-=', '*=', '/=', '%='): break raises(ValueError, lambda: Relational(x, 1, relation_type)) assert all(Relational(x, 0, op).rel_op == '==' for op in ('eq', '==')) assert all( Relational(x, 0, op).rel_op == '!=' for op in ('ne', '<>', '!=')) assert all(Relational(x, 0, op).rel_op == '>' for op in ('gt', '>')) assert all(Relational(x, 0, op).rel_op == '<' for op in ('lt', '<')) assert all(Relational(x, 0, op).rel_op == '>=' for op in ('ge', '>=')) assert all(Relational(x, 0, op).rel_op == '<=' for op in ('le', '<='))
def reduce_abs_inequality(expr, rel, gen, assume=True): """Reduce an inequality with nested absolute values. Examples ======== >>> from sympy import Q, Abs >>> from sympy.abc import x >>> from sympy.solvers.inequalities import reduce_abs_inequality >>> reduce_abs_inequality(Abs(x - 5) - 3, '<', x, assume=Q.real(x)) And(2 < x, x < 8) >>> reduce_abs_inequality(Abs(x + 2)*3 - 13, '<', x, assume=Q.real(x)) And(-19/3 < x, x < 7/3) See Also ======== reduce_abs_inequalities """ if not ask(Q.real(gen), assumptions=assume): raise NotImplementedError("can't solve inequalities with absolute " "values of a complex variable") def _bottom_up_scan(expr): exprs = [] if expr.is_Add or expr.is_Mul: op = expr.__class__ 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 or n < 0: raise ValueError( "only non-negative integer powers are allowed") _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)])) 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, assume)
def test_rel_ne(): assert Relational(x, y, '!=') == Ne(x, y) # issue 6116 p = Symbol('p', positive=True) assert Ne(p, 0) is S.true
def reduce_rational_inequalities(exprs, gen, relational=True): """Reduce a system of rational inequalities with rational coefficients. Examples ======== >>> from sympy import Poly, Symbol >>> from sympy.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) & (x < oo) >>> reduce_rational_inequalities([[(x + 2, ">")]], x) (-2 < x) & (x < oo) >>> 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 solve_poly_inequality(poly, rel): """Solve a polynomial inequality with rational coefficients. Examples ======== >>> from sympy import Poly >>> from sympy.abc import x >>> from sympy.solvers.inequalities import solve_poly_inequality >>> solve_poly_inequality(Poly(x, x, domain='ZZ'), '==') [{0}] >>> solve_poly_inequality(Poly(x**2 - 1, x, domain='ZZ'), '!=') [Interval.open(-oo, -1), Interval.open(-1, 1), Interval.open(1, oo)] >>> solve_poly_inequality(Poly(x**2 - 1, x, domain='ZZ'), '==') [{-1}, {1}] See Also ======== solve_poly_inequalities """ if not isinstance(poly, Poly): raise ValueError( 'For efficiency reasons, `poly` should be a Poly instance') if poly.is_number: t = Relational(poly.as_expr(), 0, rel) if t is S.true: return [S.Reals] elif t is S.false: return [S.EmptySet] else: raise NotImplementedError("could not determine truth value of %s" % t) reals, intervals = poly.real_roots(multiple=False), [] if rel == '==': for root, _ in reals: interval = Interval(root, root) intervals.append(interval) elif rel == '!=': left = S.NegativeInfinity for right, _ in reals + [(S.Infinity, 1)]: interval = Interval(left, right, True, True) intervals.append(interval) left = right else: if poly.LC() > 0: sign = +1 else: sign = -1 eq_sign, equal = None, False if rel == '>': eq_sign = +1 elif rel == '<': eq_sign = -1 elif rel == '>=': eq_sign, equal = +1, True elif rel == '<=': eq_sign, equal = -1, True else: raise ValueError("'%s' is not a valid relation" % rel) right, right_open = S.Infinity, True for left, multiplicity in reversed(reals): if multiplicity % 2: if sign == eq_sign: intervals.insert( 0, Interval(left, right, not equal, right_open)) sign, right, right_open = -sign, left, not equal else: if sign == eq_sign and not equal: intervals.insert(0, Interval(left, right, True, right_open)) right, right_open = left, True elif sign != eq_sign and equal: intervals.insert(0, Interval(left, left)) if sign == eq_sign: intervals.insert( 0, Interval(S.NegativeInfinity, right, True, right_open)) return intervals
def test_new_relational(): x = Symbol("x") assert Eq(x, 0) == Relational(x, 0) # None ==> Equality assert Eq(x, 0) == Relational(x, 0, "==") assert Eq(x, 0) == Relational(x, 0, "eq") assert Eq(x, 0) == Equality(x, 0) assert Eq(x, 0) != Relational(x, 1) # None ==> Equality assert Eq(x, 0) != Relational(x, 1, "==") assert Eq(x, 0) != Relational(x, 1, "eq") assert Eq(x, 0) != Equality(x, 1) assert Eq(x, -1) == Relational(x, -1) # None ==> Equality assert Eq(x, -1) == Relational(x, -1, "==") assert Eq(x, -1) == Relational(x, -1, "eq") assert Eq(x, -1) == Equality(x, -1) assert Eq(x, -1) != Relational(x, 1) # None ==> Equality assert Eq(x, -1) != Relational(x, 1, "==") assert Eq(x, -1) != Relational(x, 1, "eq") assert Eq(x, -1) != Equality(x, 1) assert Ne(x, 0) == Relational(x, 0, "!=") assert Ne(x, 0) == Relational(x, 0, "<>") assert Ne(x, 0) == Relational(x, 0, "ne") assert Ne(x, 0) == Unequality(x, 0) assert Ne(x, 0) != Relational(x, 1, "!=") assert Ne(x, 0) != Relational(x, 1, "<>") assert Ne(x, 0) != Relational(x, 1, "ne") assert Ne(x, 0) != Unequality(x, 1) assert Ge(x, 0) == Relational(x, 0, ">=") assert Ge(x, 0) == Relational(x, 0, "ge") assert Ge(x, 0) == GreaterThan(x, 0) assert Ge(x, 1) != Relational(x, 0, ">=") assert Ge(x, 1) != Relational(x, 0, "ge") assert Ge(x, 1) != GreaterThan(x, 0) assert (x >= 1) == Relational(x, 1, ">=") assert (x >= 1) == Relational(x, 1, "ge") assert (x >= 1) == GreaterThan(x, 1) assert (x >= 0) != Relational(x, 1, ">=") assert (x >= 0) != Relational(x, 1, "ge") assert (x >= 0) != GreaterThan(x, 1) assert Le(x, 0) == Relational(x, 0, "<=") assert Le(x, 0) == Relational(x, 0, "le") assert Le(x, 0) == LessThan(x, 0) assert Le(x, 1) != Relational(x, 0, "<=") assert Le(x, 1) != Relational(x, 0, "le") assert Le(x, 1) != LessThan(x, 0) assert (x <= 1) == Relational(x, 1, "<=") assert (x <= 1) == Relational(x, 1, "le") assert (x <= 1) == LessThan(x, 1) assert (x <= 0) != Relational(x, 1, "<=") assert (x <= 0) != Relational(x, 1, "le") assert (x <= 0) != LessThan(x, 1) assert Gt(x, 0) == Relational(x, 0, ">") assert Gt(x, 0) == Relational(x, 0, "gt") assert Gt(x, 0) == StrictGreaterThan(x, 0) assert Gt(x, 1) != Relational(x, 0, ">") assert Gt(x, 1) != Relational(x, 0, "gt") assert Gt(x, 1) != StrictGreaterThan(x, 0) assert (x > 1) == Relational(x, 1, ">") assert (x > 1) == Relational(x, 1, "gt") assert (x > 1) == StrictGreaterThan(x, 1) assert (x > 0) != Relational(x, 1, ">") assert (x > 0) != Relational(x, 1, "gt") assert (x > 0) != StrictGreaterThan(x, 1) assert Lt(x, 0) == Relational(x, 0, "<") assert Lt(x, 0) == Relational(x, 0, "lt") assert Lt(x, 0) == StrictLessThan(x, 0) assert Lt(x, 1) != Relational(x, 0, "<") assert Lt(x, 1) != Relational(x, 0, "lt") assert Lt(x, 1) != StrictLessThan(x, 0) assert (x < 1) == Relational(x, 1, "<") assert (x < 1) == Relational(x, 1, "lt") assert (x < 1) == StrictLessThan(x, 1) assert (x < 0) != Relational(x, 1, "<") assert (x < 0) != Relational(x, 1, "lt") assert (x < 0) != StrictLessThan(x, 1) # finally, some fuzz testing from random import randint from sympy.core.compatibility import unichr for i in range(100): while 1: strtype, length = (unichr, 65535) if randint(0, 1) else (chr, 255) relation_type = strtype(randint(0, length)) if randint(0, 1): relation_type += strtype(randint(0, length)) if relation_type not in ( "==", "eq", "!=", "<>", "ne", ">=", "ge", "<=", "le", ">", "gt", "<", "lt", ":=", "+=", "-=", "*=", "/=", "%=", ): break raises(ValueError, lambda: Relational(x, 1, relation_type)) assert all(Relational(x, 0, op).rel_op == "==" for op in ("eq", "==")) assert all(Relational(x, 0, op).rel_op == "!=" for op in ("ne", "<>", "!=")) assert all(Relational(x, 0, op).rel_op == ">" for op in ("gt", ">")) assert all(Relational(x, 0, op).rel_op == "<" for op in ("lt", "<")) assert all(Relational(x, 0, op).rel_op == ">=" for op in ("ge", ">=")) assert all(Relational(x, 0, op).rel_op == "<=" for op in ("le", "<="))