Skip to content

basic implementation of ConditionalExpression, Assuming and $Assumptions#1271

Closed
mmatera wants to merge 1 commit intomasterfrom
ConditionalExpression
Closed

basic implementation of ConditionalExpression, Assuming and $Assumptions#1271
mmatera wants to merge 1 commit intomasterfrom
ConditionalExpression

Conversation

@mmatera
Copy link
Contributor

@mmatera mmatera commented Apr 12, 2021

In this PR, I addressed issue #1231, together with a skeleton for handling Assuming and $Assumptions. ConditionalExpression allows a method to_sympy, which translates it to a Piecewise expression in sympy.
$Assumptions are not currently taken into account in the evaluations, but it can be hooked in the future in the implementation of Simplify, Reduce, Integrate, and other symbols.

@mmatera mmatera requested a review from rocky April 12, 2021 21:39
@rocky
Copy link
Member

rocky commented Apr 13, 2021

@mmatera I am looking forward to reviewing this in detail, but it will be maybe towards the end of the week when I can look at and understand.

Copy link
Member

@rocky rocky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent some time trying ConditionalExpression, Assuming and $Assumptions, and aside from the words getting recognized, I couldn't make this do anything that seemed useful.

I will put in a PR into this branch for mostly small doc-change stuff.

My take is merge this when it does something useful, and for that probably just hooking this into Piecewise evaluation would do it.

@mmatera mmatera force-pushed the ConditionalExpression branch 3 times, most recently from 2ca5e76 to bd76d24 Compare April 20, 2021 21:52
@mmatera
Copy link
Contributor Author

mmatera commented Apr 21, 2021

I think this is ready now: the function predicate_evaluation(predicate, evaluation) looks at $Assumptions and implement some replacements on predicate accordingly. This function is called then in the evaluation of Piecewise, and ConditionalExpression, in a way that propagates automatically to Integrate.

# If the result is defined as a Piecewise expression,
# use ConditionalExpression.
# This does not work now because the form sympy returns the values
if result.get_head_name() == "System`Piecewise":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another small thing. I think the idiom typically used is result.is_form() right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to have in mind, has_form is a method of Expression, so to use it, we need to check first that the object is not an atom...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even checking with is_atom() the method fails for strings. For this reason, I reverted this to the previous strategy. Maybe later a better approach comes up.

Copy link
Member

@rocky rocky Apr 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a suggestion: add the has_form() method to Symbol. The implementation is pretty simple: return False.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a great idea! I implemented it in BaseExpression instead of Symbol.

@rocky
Copy link
Member

rocky commented Apr 21, 2021

I think this is ready now: the function predicate_evaluation(predicate, evaluation) looks at $Assumptions and implement some replacements on predicate accordingly. This function is called then in the evaluation of Piecewise, and ConditionalExpression, in a way that propagates automatically to Integrate.

A superficial look through and I think this is exactly the right thing to do. However I want to pull this down and try it and maybe even experiment with the code to understand what's up with sympy mentioned in one of the comments. And probably the weekend is when I'll have an opportunity to do.

@mmatera mmatera force-pushed the ConditionalExpression branch 2 times, most recently from 75ab288 to e5ff1ce Compare April 21, 2021 15:19
@mmatera
Copy link
Contributor Author

mmatera commented Apr 21, 2021

I think this is ready now: the function predicate_evaluation(predicate, evaluation) looks at $Assumptions and implement some replacements on predicate accordingly. This function is called then in the evaluation of Piecewise, and ConditionalExpression, in a way that propagates automatically to Integrate.

A superficial look through and I think this is exactly the right thing to do. However I want to pull this down and try it and maybe even experiment with the code to understand what's up with sympy mentioned in one of the comments. And probably the weekend is when I'll have an opportunity to do.

Good. So, I made the corrections that you mention above, and I have rebased to master. Now I leave this here until you look at this. I will continue with other issues then.

"%(name)s[items__]"
result = self.to_sympy(Expression("Piecewise", *items.get_sequence()))
result = self.to_sympy(
Expression("Piecewise", *items.get_sequence()), evaluation=evaluation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! This is what I was expecting to see!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ready now: the function predicate_evaluation(predicate, evaluation) looks at $Assumptions and implement some replacements on predicate accordingly. This function is called then in the evaluation of Piecewise, and ConditionalExpression, in a way that propagates automatically to Integrate.

A superficial look through and I think this is exactly the right thing to do. However I want to pull this down and try it and maybe even experiment with the code to understand what's up with sympy mentioned in one of the comments. And probably the weekend is when I'll have an opportunity to do.

Update: I realized that in WMA, logic expressions are evaluated using assumptions just if you surround them by Simplify. On the other hand, Integrate uses the option Assumptions (default $Assumptions) before return the result. Now this behavior is implemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I found -I guess- a safe way to reproduce the use of ConditionalExpression instead of Piecewise in the return of Integrate.

rocky
rocky previously approved these changes Apr 25, 2021
Copy link
Member

@rocky rocky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just had a chance to go over in detail and here's what I encountered.

Assuming[0 < n < 1, Integrate[x^n, {x, 0, 1}]]
Out[3]= Piecewise[{{1 / (1 + n) - 0 ^ (1 + n) / (1 + n), n > -Infinity && n < Infinity && n != -1}, {Infinity, True}}]

In[4]:= Assuming[n = 1, Integrate[x^n, {x, 0, 1}]]
Out[4]= 1 / 2

In[5]:= Assuming[n = 2, Integrate[x^n, {x, 0, 1}]]
Out[5]= 1 / 3

In[6]:= Assuming[Or[n = 2, n = 1] , Integrate[x^n, {x, 0, 1}]]
Out[6]= 1 / 2

In[7]:= Assuming[Or[n = 1, n = 2] , Integrate[x^n, {x, 0, 1}]]
Out[7]= 1 / 3

If you want to call this a current limitation, ok. If you'd like to explain why we get what we get, I'd appreciate it.

I leave to you whether we want to fix or merge and fix later.

In f12c348 I removed an unused import. See also #1313 if these are tweaks you want to add.

@mmatera
Copy link
Contributor Author

mmatera commented Apr 25, 2021

Just had a chance to go over in detail and here's what I encountered.

Assuming[0 < n < 1, Integrate[x^n, {x, 0, 1}]]
Out[3]= Piecewise[{{1 / (1 + n) - 0 ^ (1 + n) / (1 + n), n > -Infinity && n < Infinity && n != -1}, {Infinity, True}}]

In[4]:= Assuming[n = 1, Integrate[x^n, {x, 0, 1}]]
Out[4]= 1 / 2

In[5]:= Assuming[n = 2, Integrate[x^n, {x, 0, 1}]]
Out[5]= 1 / 3

In[6]:= Assuming[Or[n = 2, n = 1] , Integrate[x^n, {x, 0, 1}]]
Out[6]= 1 / 2

In[7]:= Assuming[Or[n = 1, n = 2] , Integrate[x^n, {x, 0, 1}]]
Out[7]= 1 / 3

If you want to call this a current limitation, ok. If you'd like to explain why we get what we get, I'd appreciate it.

I leave to you whether we want to fix or merge and fix later.

In f12c348 I removed an unused import. See also #1313 if these are tweaks you want to add.

The first output is just wrong, but is wrong due an issue in sympy. The problem we have here is the format of the sympy return: we get something that is a piecewise definition, with the last (default) value being the unevaluated integral, but not exactly in the same form. So, there is no safe way to reduce this to a ConditionalExpression, without performing a more or les extense set of tests in the default value...

@rocky
Copy link
Member

rocky commented Apr 25, 2021

Just had a chance to go over in detail and here's what I encountered.

Assuming[0 < n < 1, Integrate[x^n, {x, 0, 1}]]
Out[3]= Piecewise[{{1 / (1 + n) - 0 ^ (1 + n) / (1 + n), n > -Infinity && n < Infinity && n != -1}, {Infinity, True}}]

In[4]:= Assuming[n = 1, Integrate[x^n, {x, 0, 1}]]
Out[4]= 1 / 2

In[5]:= Assuming[n = 2, Integrate[x^n, {x, 0, 1}]]
Out[5]= 1 / 3

In[6]:= Assuming[Or[n = 2, n = 1] , Integrate[x^n, {x, 0, 1}]]
Out[6]= 1 / 2

In[7]:= Assuming[Or[n = 1, n = 2] , Integrate[x^n, {x, 0, 1}]]
Out[7]= 1 / 3

If you want to call this a current limitation, ok. If you'd like to explain why we get what we get, I'd appreciate it.
I leave to you whether we want to fix or merge and fix later.
In f12c348 I removed an unused import. See also #1313 if these are tweaks you want to add.

The first output is just wrong, but is wrong due an issue in sympy. The problem we have here is the format of the sympy return: we get something that is a piecewise definition, with the last (default) value being the unevaluated integral, but not exactly in the same form. So, there is no safe way to reduce this to a ConditionalExpression, without performing a more or les extense set of tests in the default value...

And what about the last two? "Or" to me doesn't mean that you can pick which ever one you want, but that both may be true. Note that saying "And" here would be wrong since x can't be two values at once.

@mmatera
Copy link
Contributor Author

mmatera commented Apr 25, 2021

Just had a chance to go over in detail and here's what I encountered.

Assuming[0 < n < 1, Integrate[x^n, {x, 0, 1}]]
Out[3]= Piecewise[{{1 / (1 + n) - 0 ^ (1 + n) / (1 + n), n > -Infinity && n < Infinity && n != -1}, {Infinity, True}}]

In[4]:= Assuming[n = 1, Integrate[x^n, {x, 0, 1}]]
Out[4]= 1 / 2

In[5]:= Assuming[n = 2, Integrate[x^n, {x, 0, 1}]]
Out[5]= 1 / 3

In[6]:= Assuming[Or[n = 2, n = 1] , Integrate[x^n, {x, 0, 1}]]
Out[6]= 1 / 2

In[7]:= Assuming[Or[n = 1, n = 2] , Integrate[x^n, {x, 0, 1}]]
Out[7]= 1 / 3

If you want to call this a current limitation, ok. If you'd like to explain why we get what we get, I'd appreciate it.
I leave to you whether we want to fix or merge and fix later.
In f12c348 I removed an unused import. See also #1313 if these are tweaks you want to add.

The first output is just wrong, but is wrong due an issue in sympy. The problem we have here is the format of the sympy return: we get something that is a piecewise definition, with the last (default) value being the unevaluated integral, but not exactly in the same form. So, there is no safe way to reduce this to a ConditionalExpression, without performing a more or les extense set of tests in the default value...

And what about the last two? "Or" to me doesn't mean that you can pick which ever one you want, but that both may be true. Note that saying "And" here would be wrong since x can't be two values at once.

You are right: to assume Or[a==1, a == 2] is True should be equivalent to assume that And[a!=1, a!= 2] is False, isn't it?

@rocky
Copy link
Member

rocky commented Apr 25, 2021

Just had a chance to go over in detail and here's what I encountered.

Assuming[0 < n < 1, Integrate[x^n, {x, 0, 1}]]
Out[3]= Piecewise[{{1 / (1 + n) - 0 ^ (1 + n) / (1 + n), n > -Infinity && n < Infinity && n != -1}, {Infinity, True}}]

In[4]:= Assuming[n = 1, Integrate[x^n, {x, 0, 1}]]
Out[4]= 1 / 2

In[5]:= Assuming[n = 2, Integrate[x^n, {x, 0, 1}]]
Out[5]= 1 / 3

In[6]:= Assuming[Or[n = 2, n = 1] , Integrate[x^n, {x, 0, 1}]]
Out[6]= 1 / 2

In[7]:= Assuming[Or[n = 1, n = 2] , Integrate[x^n, {x, 0, 1}]]
Out[7]= 1 / 3

If you want to call this a current limitation, ok. If you'd like to explain why we get what we get, I'd appreciate it.
I leave to you whether we want to fix or merge and fix later.
In f12c348 I removed an unused import. See also #1313 if these are tweaks you want to add.

The first output is just wrong, but is wrong due an issue in sympy. The problem we have here is the format of the sympy return: we get something that is a piecewise definition, with the last (default) value being the unevaluated integral, but not exactly in the same form. So, there is no safe way to reduce this to a ConditionalExpression, without performing a more or les extense set of tests in the default value...

And what about the last two? "Or" to me doesn't mean that you can pick which ever one you want, but that both may be true. Note that saying "And" here would be wrong since x can't be two values at once.

You are right: to assume Or[a==1, a == 2] is True should be equivalent to assume that And[a!=1, a!= 2] is False, isn't it?

Yes, that is DeMorgan's rule.

Changing the single equal = to a double one == doesn't change Mathics' output.

@mmatera
Copy link
Contributor Author

mmatera commented Apr 25, 2021

Just had a chance to go over in detail and here's what I encountered.

Assuming[0 < n < 1, Integrate[x^n, {x, 0, 1}]]
Out[3]= Piecewise[{{1 / (1 + n) - 0 ^ (1 + n) / (1 + n), n > -Infinity && n < Infinity && n != -1}, {Infinity, True}}]

In[4]:= Assuming[n = 1, Integrate[x^n, {x, 0, 1}]]
Out[4]= 1 / 2

In[5]:= Assuming[n = 2, Integrate[x^n, {x, 0, 1}]]
Out[5]= 1 / 3

In[6]:= Assuming[Or[n = 2, n = 1] , Integrate[x^n, {x, 0, 1}]]
Out[6]= 1 / 2

In[7]:= Assuming[Or[n = 1, n = 2] , Integrate[x^n, {x, 0, 1}]]
Out[7]= 1 / 3

If you want to call this a current limitation, ok. If you'd like to explain why we get what we get, I'd appreciate it.
I leave to you whether we want to fix or merge and fix later.
In f12c348 I removed an unused import. See also #1313 if these are tweaks you want to add.

The first output is just wrong, but is wrong due an issue in sympy. The problem we have here is the format of the sympy return: we get something that is a piecewise definition, with the last (default) value being the unevaluated integral, but not exactly in the same form. So, there is no safe way to reduce this to a ConditionalExpression, without performing a more or les extense set of tests in the default value...

And what about the last two? "Or" to me doesn't mean that you can pick which ever one you want, but that both may be true. Note that saying "And" here would be wrong since x can't be two values at once.

You are right: to assume Or[a==1, a == 2] is True should be equivalent to assume that And[a!=1, a!= 2] is False, isn't it?

Yes, that is DeMorgan's rule.

Changing the single equal = to a double one == doesn't change Mathics' output.
That is weird...

@mmatera
Copy link
Contributor Author

mmatera commented Apr 25, 2021

In any case, it is clear that And[a,b,c] -> False is not equivalent to {a->False, b->False, c->False}, but
Or[a,b]->False implies a->False and b->False. This is the rule that should be implemented, while
And[a,b]->False and Or[a,b]->True shouldn't be reduced, isn't it?

@mmatera
Copy link
Contributor Author

mmatera commented Apr 25, 2021

Update:

In[6]:= Assuming[Or[n = 2, n = 1] , Integrate[x^n, {x, 0, 1}]]
Out[6]= 1 / 2

In[7]:= Assuming[Or[n = 1, n = 2] , Integrate[x^n, {x, 0, 1}]]
Out[7]= 1 / 3

You are assigning here, so this is why it evaluates to the first number. If you use == instead, the guilty is my wrong implementation for the inference rules.

@mmatera mmatera force-pushed the ConditionalExpression branch 5 times, most recently from 16f2e5d to 1426585 Compare April 29, 2021 18:16
@mmatera
Copy link
Contributor Author

mmatera commented Apr 29, 2021

Well, this is getting bigger and bigger. I tried to delegate the predicate evaluation to sympy, but it is simply very limited, and too complicated to use here. So, I implemented our own evaluator. There are several aspects that can be improved, but I think this is a good point to merge this.

Integrate uses now ConditionalExpression

Remove an unused import

Misc Arg tweaks:

* Arg[0] needs to be defined 0 (otherwise it can be indeterminate)
* More complete doc for Arg
* Add Method option

improving the behaviour of Piecewise returns in Integrate

adding tests

new  inference module. Many Improvings on inference

disabling debug prints in inference
@mmatera mmatera force-pushed the ConditionalExpression branch from 1426585 to d809887 Compare April 29, 2021 18:24
Copy link
Member

@rocky rocky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While there is great stuff in here, I am getting concerned that the overall number of changes is getting too hard to review and understand.

It would be great to figure out how to split this up into smaller pieces and deliver those. (Sort of like I have been trying to do with adding builtin subdirectories/subcategories.

Some of the changes are trivial and not likely to change like Integer(0) -> Integer0 and Symbol("True")->SymbolTrue.

Something else easy might be Nand and Nor.

Is there anything else though?

Maybe FixedPoint with the SameTest options?

@rocky rocky self-requested a review April 29, 2021 18:30
@rocky rocky dismissed their stale review April 29, 2021 18:31

Number of commits and size of commit has grown.

Copy link
Member

@rocky rocky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's break this up. I am happy to help here.

@mmatera
Copy link
Contributor Author

mmatera commented Apr 29, 2021

If you want, most of the non-trivial changes are included in the new module inference.py. We can split this into a very raw implementation of the new module, (let's say, one in which evaluate_predicate just returns the input). I can try to do it after I finish removing errors, warnings, and debug code...

@mmatera
Copy link
Contributor Author

mmatera commented Apr 30, 2021

This PR was splitted into #1328 and #1330

@mmatera mmatera closed this Apr 30, 2021
@mmatera mmatera deleted the ConditionalExpression branch May 3, 2021 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants