Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove args and func overriding of AppliedPredicate #20847

Merged
merged 10 commits into from
Apr 13, 2021
2 changes: 1 addition & 1 deletion doc/src/special_topics/classification.rst
Expand Up @@ -93,7 +93,7 @@ func
>>> Add(x + x).func
<class 'sympy.core.mul.Mul'>
>>> Q.even(x).func
Q.even
<class 'sympy.assumptions.assume.AppliedPredicate'>

As you can see, resulting head may be a class or another SymPy object.
Keep this in mind when you classify the object with this attribute.
Expand Down
14 changes: 4 additions & 10 deletions sympy/assumptions/assume.py
Expand Up @@ -137,16 +137,6 @@ def arg(self):
return args[1]
raise TypeError("'arg' property is allowed only for unary predicates.")

@property
def args(self):
# Will be deprecated and return normal Basic.func
return self._args[1:]

@property
def func(self):
# Will be deprecated and return normal Basic.func
return self._args[0]

@property
def function(self):
"""
Expand Down Expand Up @@ -361,6 +351,10 @@ def eval(self, args, assumptions=True):
pass
return result

def _eval_refine(self, assumptions):
# When Predicate is no longer Boolean, delete this method
return self


class UndefinedPredicate(Predicate):
"""
Expand Down
4 changes: 2 additions & 2 deletions sympy/assumptions/satask.py
Expand Up @@ -92,15 +92,15 @@ def find_symbols(pred):
req_keys |= tmp_keys
keys |= {l for l in lkeys if find_symbols(l) & req_keys != set()}

exprs = {key.args[0] if isinstance(key, AppliedPredicate) else key for key in keys}
exprs = {key.arguments[0] if isinstance(key, AppliedPredicate) else key for key in keys}
return exprs, relevant_facts

for expr in exprs:
for fact in fact_registry[expr.func]:
cnf_fact = CNF.to_CNF(fact)
newfact = cnf_fact.rcall(expr)
relevant_facts = relevant_facts._and(newfact)
newexprs |= {key.args[0] for key in newfact.all_predicates()
newexprs |= {key.arguments[0] for key in newfact.all_predicates()
if isinstance(key, AppliedPredicate)}

return newexprs - exprs, relevant_facts
Expand Down
23 changes: 20 additions & 3 deletions sympy/assumptions/sathandlers.py
Expand Up @@ -16,6 +16,23 @@
# APIs here may be subject to change


def _find_freepredicate(expr):
# Find unapplied predicate from expression tree.
# Ignore the predicate in AppliedPredicate.
if isinstance(expr, Predicate):
return {expr}
if not expr.args:
return set()
if isinstance(expr, AppliedPredicate):
args = expr.arguments
else:
args = expr.args
result = set()
for arg in args:
result.update(_find_freepredicate(arg))
return result


class UnevaluatedOnFree(BooleanFunction):
"""
Represents a Boolean function that remains unevaluated on free predicates.
Expand Down Expand Up @@ -53,7 +70,7 @@ class UnevaluatedOnFree(BooleanFunction):
def __new__(cls, arg):
# Mostly type checking here
arg = _sympify(arg)
predicates = arg.atoms(Predicate)
predicates = _find_freepredicate(arg)
applied_predicates = arg.atoms(AppliedPredicate)
if predicates and applied_predicates:
raise ValueError("arg must be either completely free or singly applied")
Expand All @@ -62,12 +79,12 @@ def __new__(cls, arg):
obj.pred = arg
obj.expr = None
return obj
predicate_args = {pred.args[0] for pred in applied_predicates}
predicate_args = {pred.arguments[0] for pred in applied_predicates}
if len(predicate_args) > 1:
raise ValueError("The AppliedPredicates in arg must be applied to a single expression.")
obj = BooleanFunction.__new__(cls, arg)
obj.expr = predicate_args.pop()
obj.pred = arg.xreplace(Transform(lambda e: e.func, lambda e:
obj.pred = arg.xreplace(Transform(lambda e: e.function, lambda e:
isinstance(e, AppliedPredicate)))
applied = obj.apply(obj.expr)
if applied is None:
Expand Down