def _reduce_inequalities(inequalities, symbols): # helper for reduce_inequalities poly_part, pw_part = {}, {} other = [] for inequality in inequalities: if inequality is S.true: continue elif inequality is S.false: return S.false expr, rel = inequality.lhs, inequality.rel_op # rhs is 0 # check for gens using atoms which is more strict than free_symbols to # guard against EX domain which won't be handled by # reduce_rational_inequalities gens = expr.atoms(Dummy, Symbol) if len(gens) == 1: gen = gens.pop() else: common = expr.free_symbols & symbols if len(common) == 1: gen = common.pop() other.append( solve_univariate_inequality(Relational(expr, 0, rel), gen)) continue else: raise NotImplementedError( filldedent(''' inequality has more than one symbol of interest''')) if expr.is_polynomial(gen): poly_part.setdefault(gen, []).append((expr, rel)) else: components = expr.find(lambda u: u.has(gen) and ( u.is_Function or u.is_Pow and not u.exp.is_Integer)) if components and all( isinstance(i, Abs) or isinstance(i, Piecewise) for i in components): pw_part.setdefault(gen, []).append((expr, rel)) else: other.append( solve_univariate_inequality(Relational(expr, 0, rel), gen)) poly_reduced = [] pw_reduced = [] for gen, exprs in poly_part.items(): poly_reduced.append(reduce_rational_inequalities([exprs], gen)) for gen, exprs in pw_part.items(): pw_reduced.append(reduce_piecewise_inequalities(exprs, gen)) return And(*(poly_reduced + pw_reduced + other))
def test_core_relational(): for c in (Equality, Equality(x, y), GreaterThan, GreaterThan(x, y), LessThan, LessThan(x, y), Relational, Relational(x, y), StrictGreaterThan, StrictGreaterThan(x, y), StrictLessThan, StrictLessThan(x, y), Unequality, Unequality(x, y)): check(c)
def test_fcode_Relational(): assert fcode(Relational(x, y, "=="), source_format="free") == "Eq(x, y)" assert fcode(Relational(x, y, "!="), source_format="free") == "Ne(x, y)" assert fcode(Relational(x, y, ">="), source_format="free") == "x >= y" assert fcode(Relational(x, y, "<="), source_format="free") == "x <= y" assert fcode(Relational(x, y, ">"), source_format="free") == "x > y" assert fcode(Relational(x, y, "<"), source_format="free") == "x < y"
def __new__(cls, lhs, rhs=0, **assumptions): from diofant.matrices.expressions.matexpr import ( MatrixElement, MatrixSymbol) from diofant.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 test_wrappers(): e = x + x**2 res = Relational(y, e, '==') assert Rel(y, x + x**2, '==') == res assert Eq(y, x + x**2) == res res = Relational(y, e, '<') assert Lt(y, x + x**2) == res res = Relational(y, e, '<=') assert Le(y, x + x**2) == res res = Relational(y, e, '>') assert Gt(y, x + x**2) == res res = Relational(y, e, '>=') assert Ge(y, x + x**2) == res res = Relational(y, e, '!=') assert Ne(y, x + x**2) == res
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 test_new_relational(): assert Eq(x) == Relational(x, 0) # None ==> Equality assert Eq(x) == Relational(x, 0, '==') assert Eq(x) == Relational(x, 0, 'eq') assert Eq(x) == Equality(x, 0) 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) != Relational(x, 1) # None ==> Equality assert Eq(x) != Relational(x, 1, '==') assert Eq(x) != Relational(x, 1, 'eq') assert Eq(x) != 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 for i in range(100): while 1: strtype, length = (chr, 65535) if random.randint(0, 1) else (chr, 255) relation_type = strtype(random.randint(0, length)) if random.randint(0, 1): relation_type += strtype(random.randint(0, length)) if relation_type not in ('==', 'eq', '!=', '<>', 'ne', '>=', 'ge', '<=', 'le', '>', 'gt', '<', 'lt'): break pytest.raises(ValueError, lambda: Relational(x, 1, relation_type))
def reduce_piecewise_inequality(expr, rel, gen): """ Reduce an inequality with nested piecewise functions. Examples ======== >>> from diofant import Abs, Symbol, Piecewise >>> from diofant.solvers.inequalities import reduce_piecewise_inequality >>> x = Symbol('x', real=True) >>> reduce_piecewise_inequality(Abs(x - 5) - 3, '<', x) And(2 < x, x < 8) >>> reduce_piecewise_inequality(Abs(x + 2)*3 - 13, '<', x) And(-19/3 < x, x < 7/3) >>> reduce_piecewise_inequality(Piecewise((1, x < 1), ... (3, True)) - 1, '>', x) 1 <= x See Also ======== reduce_piecewise_inequalities """ if gen.is_extended_real is False: raise TypeError( filldedent(''' can't solve inequalities with piecewise functions containing non-real variables''')) def _bottom_up_scan(expr): exprs = [] if expr.is_Add or expr.is_Mul: op = expr.func for arg in expr.args: _exprs = _bottom_up_scan(arg) if not exprs: exprs = _exprs else: args = [] for expr, conds in exprs: for _expr, _conds in _exprs: args.append((op(expr, _expr), conds + _conds)) exprs = args elif expr.is_Pow: n = expr.exp if not n.is_Integer: raise NotImplementedError("only integer powers are supported") _exprs = _bottom_up_scan(expr.base) for expr, conds in _exprs: exprs.append((expr**n, conds)) elif isinstance(expr, Abs): _exprs = _bottom_up_scan(expr.args[0]) for expr, conds in _exprs: exprs.append((expr, conds + [Ge(expr, 0)])) exprs.append((-expr, conds + [Lt(expr, 0)])) elif isinstance(expr, Piecewise): for a in expr.args: _exprs = _bottom_up_scan(a.expr) for ex, conds in _exprs: if a.cond is not S.true: exprs.append((ex, conds + [a.cond])) else: oconds = [ c[1] for c in expr.args if c[1] is not S.true ] exprs.append( (ex, conds + [And(*[~c for c in oconds])])) else: exprs = [(expr, [])] return exprs exprs = _bottom_up_scan(expr) mapping = {'<': '>', '<=': '>='} inequalities = [] for expr, conds in exprs: if rel not in mapping.keys(): expr = Relational(expr, 0, rel) else: expr = Relational(-expr, 0, mapping[rel]) inequalities.append([expr] + conds) return reduce_rational_inequalities(inequalities, gen)
def reduce_rational_inequalities(exprs, gen, relational=True): """ Reduce a system of rational inequalities with rational coefficients. Examples ======== >>> from diofant import Poly, Symbol >>> from diofant.solvers.inequalities import reduce_rational_inequalities >>> x = Symbol('x', real=True) >>> reduce_rational_inequalities([[x**2 <= 0]], x) Eq(x, 0) >>> reduce_rational_inequalities([[x + 2 > 0]], x) -2 < x >>> reduce_rational_inequalities([[(x + 2, ">")]], x) -2 < x >>> reduce_rational_inequalities([[x + 2]], x) Eq(x, -2) """ exact = True eqs = [] solution = S.Reals if exprs else S.EmptySet for _exprs in exprs: _eqs = [] for expr in _exprs: if isinstance(expr, tuple): expr, rel = expr else: if expr.is_Relational: expr, rel = expr.lhs - expr.rhs, expr.rel_op else: expr, rel = expr, '==' if expr is S.true: numer, denom, rel = S.Zero, S.One, '==' elif expr is S.false: numer, denom, rel = S.One, S.One, '==' else: numer, denom = expr.together().as_numer_denom() try: (numer, denom), opt = parallel_poly_from_expr((numer, denom), gen) except PolynomialError: raise PolynomialError( filldedent(''' only polynomials and rational functions are supported in this context''')) if not opt.domain.is_Exact: numer, denom, exact = numer.to_exact(), denom.to_exact(), False domain = opt.domain.get_exact() if not (domain.is_ZZ or domain.is_QQ): expr = numer / denom expr = Relational(expr, 0, rel) solution &= solve_univariate_inequality(expr, gen, relational=False) else: _eqs.append(((numer, denom), rel)) if _eqs: eqs.append(_eqs) if eqs: solution &= solve_rational_inequalities(eqs) if not exact: solution = solution.evalf() if relational: solution = solution.as_relational(gen) return solution
def solve_poly_inequality(poly, rel): """ Solve a polynomial inequality with rational coefficients. Examples ======== >>> from diofant import Poly >>> from diofant.solvers.inequalities import solve_poly_inequality >>> from diofant.abc import x >>> solve_poly_inequality(Poly(x, x, domain='ZZ'), '==') [{0}] >>> solve_poly_inequality(Poly(x**2 - 1, x, domain='ZZ'), '!=') [(-oo, -1), (-1, 1), (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('`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("Couldn't 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: sign = +1 if poly.LC() > 0 else -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_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 true assert e.subs({x: 1}) is false