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

update ConditionSet #19512

Merged
merged 2 commits into from Jun 23, 2020
Merged

update ConditionSet #19512

merged 2 commits into from Jun 23, 2020

Conversation

smichr
Copy link
Member

@smichr smichr commented Jun 8, 2020

References to other Issues or PRs

closes #19520
closes #19517
have not checked if #11174 has been fixed with these changes

Brief description of what is fixed or changed

Other comments

Release Notes

  • sets
    • nested multi-symbol first arg for ConditionSet now handled with subs/as_dummy
    • the bound symbols cannot be replaced with subs
    • the error checking for mismatched signatures for sym and the base set has been improved
    • ConditionSet still tries to unify symbols and denest a base set that is given as a ConditionSet but will no longer introduce new symbols (and will leave the base set a a ConditionSet) when this cannot be done
  • solvers
    • solveset will always use a symbol that has only either the real or complex attribute and no other attribute; when a ConditionSet is returned, the original symbol for which the solution is being sought will be used if it does not cause evaluation of the result.

@sympy-bot
Copy link

sympy-bot commented Jun 8, 2020

Hi, I am the SymPy bot (v160). I'm here to help you write a release notes entry. Please read the guide on how to write release notes.

Your release notes are in good order.

Here is what the release notes will look like:

  • sets

    • nested multi-symbol first arg for ConditionSet now handled with subs/as_dummy (#19512 by @smichr)

    • the bound symbols cannot be replaced with subs (#19512 by @smichr)

    • the error checking for mismatched signatures for sym and the base set has been improved (#19512 by @smichr)

    • ConditionSet still tries to unify symbols and denest a base set that is given as a ConditionSet but will no longer introduce new symbols (and will leave the base set a a ConditionSet) when this cannot be done (#19512 by @smichr)

  • solvers

    • solveset will always use a symbol that has only either the real or complex attribute and no other attribute; when a ConditionSet is returned, the original symbol for which the solution is being sought will be used if it does not cause evaluation of the result. (#19512 by @smichr)

This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.7.

Note: This comment will be updated with the latest check if you edit the pull request. You need to reload the page to see it.

Click here to see the pull request description that was parsed.

<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234" (see
https://tinyurl.com/auto-closing for more information). Also, please
write a comment on that issue linking back to this pull request once it is
open. -->

closes #19520
closes #19517 
have not checked if #11174 has been fixed with these changes

#### Brief description of what is fixed or changed


#### Other comments


#### Release Notes

<!-- Write the release notes for this release below. See
https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more information
on how to write release notes. The bot will check your release notes
automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->
* sets
  * nested multi-symbol first arg for ConditionSet now handled with subs/as_dummy
  * the bound symbols cannot be replaced with `subs`
  * the error checking for mismatched signatures for sym and the base set has been improved
  * ConditionSet still tries to unify symbols and denest a base set that is given as a ConditionSet but will no longer introduce new symbols (and will leave the base set a a ConditionSet) when this cannot be done
* solvers
  * solveset will always use a symbol that has only either the real or complex attribute and no other attribute; when a ConditionSet is returned, the original symbol for which the solution is being sought will be used if it does not cause evaluation of the result.
<!-- END RELEASE NOTES -->

Update

The release notes on the wiki have been updated.

@smichr
Copy link
Member Author

smichr commented Jun 8, 2020

Should ConditionSet have limitations like Lambda on the structure of the first argument in terms of how many times a symbol can appear?

>>> ConditionSet((x,(x,y)),x<y,Reals*Reals**2)
ConditionSet((x, (x, y)), x < y, ProductSet(Reals, ProductSet(Reals, Reals)))

>>> Lambda((x,(x,y)),x+y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "sympy\core\function.py", line 1964, in __new__
    cls._check_signature(sig)
  File "sympy\core\function.py", line 1990, in _check_signature
    rcheck(sig)
  File "sympy\core\function.py", line 1982, in rcheck
    rcheck(a)
  File "sympy\core\function.py", line 1979, in rcheck
    raise BadSignatureError("Duplicate symbol %s" % a)
sympy.core.function.BadSignatureError: Duplicate symbol x

>>> Lambda((x,(z,y)),x+y)
Lambda((x, (z, y)), x + y)

@smichr smichr force-pushed the sol-cond branch 2 times, most recently from 3899d96 to 6a57b17 Compare June 8, 2020 06:14
@oscarbenjamin
Copy link
Contributor

Should ConditionSet have limitations like Lambda on the structure of the first argument in terms of how many times a symbol can appear?

Yes I think so.

There is another problem:

In [4]: C = ConditionSet((x, y), x<y, Reals**2)                                                                                   

In [5]: C                                                                                                                         
Out[5]:2          ⎫
⎨(x, y) | (x, y)  (x < y)⎬
⎩                              ⎭

In [6]: (1, 2) in C                                                                                                               
---------------------------------------------------------------------------
BadArgumentsError 

That can be fixed with

diff --git a/sympy/sets/conditionset.py b/sympy/sets/conditionset.py
index 61972eb609..63d710553b 100644
--- a/sympy/sets/conditionset.py
+++ b/sympy/sets/conditionset.py
@@ -201,7 +201,7 @@ def bound_symbols(self):
     def _contains(self, other):
         return And(
             Contains(other, self.base_set),
-            Lambda(self.sym, self.condition)(other))
+            Lambda((self.sym,), self.condition)(other))
 
     def as_relational(self, other):
         return And(Lambda(self.sym, self.condition)(

That does make me wonder if though if ConditionSet should nest its symbols in a tuple internally and should then accept multiple base set arguments like ProductSet.

@smichr
Copy link
Member Author

smichr commented Jun 8, 2020

Yes I think so.

But should it have anything other than a flat structure? I can understand Lambda having structure to match how one is expecting to pass the arguments, but am not sure that such is needed for ConditionSet.

@oscarbenjamin
Copy link
Contributor

The thing is that the signature for the ConditionSet also specifies the structure of the objects represented by the set. So e.g.

In [242]: ConditionSet((x, (y, z)), (x<y) & (y<z), Reals*Reals**2)                                                                
Out[242]: 
⎧                                ⎛ 2⎞          ⎫
⎨(x, (y, z)) | (x, (y, z)) × ⎝ℝ ⎠  (False)⎬
⎩                                              ⎭

This represents a subset of the set R*R**2 which is the set of 2-tuples of a real and a 2-tuple of a pair of reals.

It isn't strictly necessary since we can always use an extra ImageSet to rearrange the shape e.g.:

In [4]: C = ConditionSet((x, y, z), (x<y) & (y<z), Reals*Reals**2)                                                                

In [5]: I = ImageSet(Lambda(((x, y, z),), (x, (y, z))), C)                                                                        

In [6]: I                                                                                                                         
Out[6]: 
⎧                          ⎧                            ⎛ 2⎞                  ⎫⎫
⎨(x, (y, z)) | (x, y, z)  ⎨(x, y, z) | (x, y, z) × ⎝ℝ ⎠  (x < y  y < z)⎬⎬
⎩                          ⎩                                                  ⎭⎭

It might be the case though that it's easier to manipulate ImageSet and ConditionSet together if they have interchangeable args.

@smichr
Copy link
Member Author

smichr commented Jun 8, 2020

I don't think assumptions on dummy args should be supported or else it confounds the meaning of the set:

>>> n = Symbol('n', negative=True)
>>> ConditionSet(n, n < 2, S.Integers)
Integers

But that set should be "all integers less than 2". Is that a garbage-in-garbage-out result?

@smichr smichr force-pushed the sol-cond branch 4 times, most recently from 307451c to 9a77514 Compare June 8, 2020 23:27
@codecov
Copy link

codecov bot commented Jun 9, 2020

Codecov Report

Merging #19512 into master will decrease coverage by 0.013%.
The diff coverage is 93.670%.

@@              Coverage Diff              @@
##            master    #19512       +/-   ##
=============================================
- Coverage   75.682%   75.668%   -0.014%     
=============================================
  Files          657       657               
  Lines       170378    170405       +27     
  Branches     40174     40182        +8     
=============================================
- Hits        128946    128943        -3     
- Misses       35801     35817       +16     
- Partials      5631      5645       +14     

@smichr smichr marked this pull request as ready for review June 9, 2020 02:55
@smichr
Copy link
Member Author

smichr commented Jun 9, 2020

I have not added any condition checking with symbols and now allow substitutions which were formerly ignored. I marked some questions with XXX in the added code.

@smichr
Copy link
Member Author

smichr commented Jun 10, 2020

There is now 100% nominal converage of conditionset.py

@smichr
Copy link
Member Author

smichr commented Jun 17, 2020

@oscarbenjamin , if you want to give this a look again, I think tests will be passing

sympy/sets/sets.py Outdated Show resolved Hide resolved
sympy/core/symbol.py Outdated Show resolved Hide resolved
@smichr smichr force-pushed the sol-cond branch 4 times, most recently from 408bdea to 6b78e0a Compare June 17, 2020 21:06
sympy/solvers/solveset.py Outdated Show resolved Hide resolved

"""
def __new__(cls, sym, condition, base_set=S.UniversalSet):
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure what to do about this: if I make the signature like ImageSet and allow multiple sets to correspond to multiple args then we lose the keyword base_set since even if I made the signature (cls, sym, condition, *base_set) I couldn't pass the sets by name (e.g. ...base_set=Reals) only by position (as I understand it).

Copy link
Contributor

Choose a reason for hiding this comment

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

It would be possible by having *args and **kwargs and manually checking if a keyword argument was supplied.

I'm not sure whether it's a good idea though. I wonder if the multi-arg form of ImageSet is a bad idea as it does complicate the definition. It makes some sense for ImageSet because it allows to matches a multi-arg Lambda. I think it definitely makes less sense for CondtionSet though as it would break some symmetries in the n-ary case:

ConditionSet(x, p(x), S1, S2, S3)  = {x : p(x), x in prod(S1, S2, S3)} 
ConditionSet(x, p(x), S1, S2)  = {x : p(x), x in prod(S1, S2)}
ConditionSet(x, p(x), S1)  = {x : p(x), x in S1}

Here the first two are sets of 3-tuples and 2-tuples. Shouldn't the third be a set of 1-tuples?

We don't need to keep the ImageSet in this form though since there is always an alternative. I meant to add a method to ImageSet (not sure if I did) that would convert it to single base set form, something like:

>>> ImageSet(Lambda((x, y, z), x+y+z, S1, S2, S3).canonical()
ImageSet(Lambda(((x, y, z),), x+y+z, ProductSet(S1, S2, S3))

Perhaps in code that wants to relate ImageSet and ConditionSet we should just always call the canonical method. Maybe canonical isn't a good name and it could be flatten (or unflatten?) or a property rather than a method. Not sure... Maybe ImageSet should automatically canonicalise itself... or maybe doit should?

Copy link
Member Author

Choose a reason for hiding this comment

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

ConditionSet(x, p(x), S1, S2, S3) = {x : p(x), x in prod(S1, S2, S3)}

The way I have it set now, this would raise an error since x is passed; it would not raise an error for ConditionSet((x,y,z), p(x,y,z), S!, S2, S3). I don't think we would want to allow the form you gave to pass or else how would the individual components of x be used in the predicate?

Copy link
Contributor

Choose a reason for hiding this comment

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

Does that not leave an ambiguity:

ConditionSet((x, y, z), p(x, y, z), S)
ConditionSet((x, y, z), p(x, y, z), S1, S2, S3)

Then in the first case (x, y, z) is an element of S. In the second case it is an element of prod(S1, S2, S3). Then in the n-ary case we should have

syms = tuple(Dummy() for _ in range(n))
pred = sum(syms) < 0
basesets = [Reals] * n
ConditionSet(syms, pred, *basesets)

We have that syms represents an element of ProductSet(*basesets). That's fine and for case 1 that gives

ConditionSet((x,), x<0, Reals)

However that's now inconsistent with

ConditionSet(x, x<0, Reals)

because here x is not an element of Reals**1.

If we disallow the multiple base sets then we can have a consistent definition that

ConditionSet(x, p, X)
ConditionSet((x,), p, ProductSet(X))
ConditionSet((x, y), p, ProductSet(X, Y))
ConditionSet((x, y, z), p, ProductSet(X, Y, Z))

Here the first argument to ConditionSet always represents an element of the 3rd argument.

Copy link
Member Author

@smichr smichr Jun 20, 2020

Choose a reason for hiding this comment

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

ConditionSet canonicalizes the base set so ...S1, S2, S3 is interpreted (and stored) as ProductSet(S1, S2, S3).

Copy link
Member Author

Choose a reason for hiding this comment

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

Should I close this PR?

Copy link
Member Author

Choose a reason for hiding this comment

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

A FiniteSet can have anything including tuples and non-tuples and tuples of mixed length

I suppose there could be some sort of predicate that would not require uniformly shaped elements in a FiniteSet...that seems a little obtuse to me, however.

Copy link
Contributor

Choose a reason for hiding this comment

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

There are lots of good changes here but I think it would be better to stick to only having a single base set and not modify the base set to make a ProductSet in ConditionSet((x, y), x<0, Integers).

some sort of predicate that would not require uniformly shaped elements

You could have something like:

In [1]: ConditionSet((x, y), x<0, {(1, (2, 3)), (4, (5, 6, 7))})                                                                               
Out[1]: {(x, y) | (x, y)  {(1, (2, 3)), (4, (5, 6, 7))}  (x < 0)}

In this PR that comes out strangely:

In [2]: ConditionSet((x, y), x<0, {(1, (2, 3)), (4, (5, 6, 7))})                                                                               
Out[2]:2          ⎫
⎨(x, y) | (x, y)  {(1, (2, 3)), (4, (5, 6, 7))}   (x < 0)⎬
⎩                                                          ⎭

Copy link
Member Author

Choose a reason for hiding this comment

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

not modify the base set to make a ProductSet

You could have something like:
In [1]: ConditionSet((x, y),

So should we just let errors rise when the user tries to do anything with the set?

Copy link
Contributor

Choose a reason for hiding this comment

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

So should we just let errors rise when the user tries to do anything with the set?

I think for now it's fine to do that. The most important thing to pin down is what exactly is the definition of ConditionSet when given valid arguments.

If we wanted to handle the invalid arguments better at the outset we could add some kind of is_tuple_set property to sets that tells you whether it could possibly be a tuple in general (It could be always True for ProductSet, always None for FiniteSet, False for most atomic sets, fuzzy_and for Union, fuzzy_or? for Intersection). We know e.g. that Integers does not contain any tuples. Then a case like ConditionSet((x, y), x<0, Integers) could raise by checking baseset.is_tuple_set is False.

sympy/sets/conditionset.py Outdated Show resolved Hide resolved
sympy/sets/conditionset.py Outdated Show resolved Hide resolved
@smichr
Copy link
Member Author

smichr commented Jun 22, 2020

@oscarbenjamin, the checking of the signature/base_set coherence has been removed.

@oscarbenjamin
Copy link
Contributor

The tests have failed because of unused imports but this looks good to me

@smichr smichr merged commit b9baf36 into sympy:master Jun 23, 2020
@smichr smichr deleted the sol-cond branch June 23, 2020 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

simplify ConditionSet should ConditionSet allow assumptions on bound variables?
4 participants