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

[WIP] Make complex->finite in the codebase. #16978

Open
wants to merge 9 commits into
base: master
from

Conversation

Projects
None yet
6 participants
@ShubhamKJha
Copy link
Member

commented Jun 6, 2019

References to other Issues or PRs

Brief description of what is fixed or changed

[Do not merge this PR]
This PR attempts to make is_complex imply is_finite. The assumption rules used are:

_assume_rules = FactRules([

    'integer        ->  rational',
    'rational       ->  real',
    'rational       ->  algebraic',
    'algebraic      ->  complex',
    'transcendental ==  complex & !algebraic',
    'real           ->  hermitian',
    'imaginary      ->  complex',
    'imaginary      ->  antihermitian',
    'extended_real  ->  commutative',
    'complex        ->  commutative',
    'complex        ->  finite',

    'odd            ==  integer & !even',
    'even           ==  integer & !odd',

    'real           ->  complex',
    'extended_real  ->  real | infinite',
    'real           ==  extended_real & finite',

    'extended_real        ==  extended_negative | zero | extended_positive',
    'extended_negative    ==  extended_nonpositive & extended_nonzero',
    'extended_positive    ==  extended_nonnegative & extended_nonzero',

    'extended_nonpositive ==  extended_real & !extended_positive',
    'extended_nonnegative ==  extended_real & !extended_negative',

    'real           ==  negative | zero | positive',
    'negative       ==  nonpositive & nonzero',
    'positive       ==  nonnegative & nonzero',

    'nonpositive    ==  real & !positive',
    'nonnegative    ==  real & !negative',

    'positive       ==  extended_positive & finite',
    'negative       ==  extended_negative & finite',
    'nonpositive    ==  extended_nonpositive & finite',
    'nonnegative    ==  extended_nonnegative & finite',
    'nonzero        ==  extended_nonzero & finite',

    'zero           ->  even & finite',
    'zero           ==  extended_nonnegative & extended_nonpositive',
    'zero           ==  nonnegative & nonpositive',
    'nonzero        ->  real',

    'prime          ->  integer & positive',
    'composite      ->  integer & positive & !prime',
    '!composite     ->  !positive | !even | prime',

    'irrational     ==  real & !rational',

    'imaginary      ->  !extended_real',

    'infinite       ->  !finite',
    'noninteger     ==  extended_real & !integer',
    'extended_nonzero == extended_real & !zero',
])

Other comments

This breaks zoo.is_complex throughout the codebase. I have tried to do this without including any extra facts (i.e. extended_complex for entities like zoo)

Release Notes

  • core
    • The assumptions system is changed so now only finite values will be considered complex. With this zoo is no longer complex in this sense. And hence, zoo.is_complex now returns False. Also, any symbol with assumption complex = True would become finite and with finite = False will become complex = False by default.
    • A number of instances of _eval_is_complex is changed to better accommodate the finite nature of them.
@sympy-bot

This comment has been minimized.

Copy link

commented Jun 6, 2019

Hi, I am the SymPy bot (v147). 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:

  • core
    • The assumptions system is changed so now only finite values will be considered complex. With this zoo is no longer complex in this sense. And hence, zoo.is_complex now returns False. Also, any symbol with assumption complex = True would become finite and with finite = False will become complex = False by default. (#16978 by @ShubhamKJha)

    • A number of instances of _eval_is_complex is changed to better accommodate the finite nature of them. (#16978 by @ShubhamKJha)

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

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://github.com/blog/1506-closing-issues-via-pull-requests . Please also
write a comment on that issue linking back to this pull request once it is
open. -->


#### Brief description of what is fixed or changed
**[Do not merge this PR]**
This PR attempts to make `is_complex` imply `is_finite`. The assumption rules used are:
```py
_assume_rules = FactRules([

    'integer        ->  rational',
    'rational       ->  real',
    'rational       ->  algebraic',
    'algebraic      ->  complex',
    'transcendental ==  complex & !algebraic',
    'real           ->  hermitian',
    'imaginary      ->  complex',
    'imaginary      ->  antihermitian',
    'extended_real  ->  commutative',
    'complex        ->  commutative',
    'complex        ->  finite',

    'odd            ==  integer & !even',
    'even           ==  integer & !odd',

    'real           ->  complex',
    'extended_real  ->  real | infinite',
    'real           ==  extended_real & finite',

    'extended_real        ==  extended_negative | zero | extended_positive',
    'extended_negative    ==  extended_nonpositive & extended_nonzero',
    'extended_positive    ==  extended_nonnegative & extended_nonzero',

    'extended_nonpositive ==  extended_real & !extended_positive',
    'extended_nonnegative ==  extended_real & !extended_negative',

    'real           ==  negative | zero | positive',
    'negative       ==  nonpositive & nonzero',
    'positive       ==  nonnegative & nonzero',

    'nonpositive    ==  real & !positive',
    'nonnegative    ==  real & !negative',

    'positive       ==  extended_positive & finite',
    'negative       ==  extended_negative & finite',
    'nonpositive    ==  extended_nonpositive & finite',
    'nonnegative    ==  extended_nonnegative & finite',
    'nonzero        ==  extended_nonzero & finite',

    'zero           ->  even & finite',
    'zero           ==  extended_nonnegative & extended_nonpositive',
    'zero           ==  nonnegative & nonpositive',
    'nonzero        ->  real',

    'prime          ->  integer & positive',
    'composite      ->  integer & positive & !prime',
    '!composite     ->  !positive | !even | prime',

    'irrational     ==  real & !rational',

    'imaginary      ->  !extended_real',

    'infinite       ->  !finite',
    'noninteger     ==  extended_real & !integer',
    'extended_nonzero == extended_real & !zero',
])
```

#### Other comments
This breaks `zoo.is_complex` throughout the codebase. I have tried to do this without including any extra facts (i.e. `extended_complex` for entities like `zoo`)

#### 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 -->
* core
  * The assumptions system is changed so now only finite values will be considered complex. With this `zoo` is no longer complex in this sense. And hence, `zoo.is_complex` now returns `False`. Also, any symbol with assumption `complex = True` would become `finite ` and with `finite = False` will become `complex = False` by default.
  * A number of instances of `_eval_is_complex` is changed to better accommodate the finite nature of them. 
<!-- END RELEASE NOTES -->

@@ -247,7 +247,7 @@ def add(self, other):
# If q2 is a number or a sympy expression instead of a quaternion
if not isinstance(q2, Quaternion):
if q1.real_field:
if q2.is_complex:
if q2.is_complex or q2.is_infinite:

This comment has been minimized.

Copy link
@jksuom

jksuom Jun 6, 2019

Member

I don't think that infinite components should be accepted for quaternions. So maybe this could be left as it is.

This comment has been minimized.

Copy link
@ShubhamKJha

ShubhamKJha Jun 6, 2019

Author Member

It seems this has been accepting infinite components till now. Should this be changed back?

This comment has been minimized.

Copy link
@jksuom

jksuom Jun 6, 2019

Member

It has been accepting infinite components but that is probably not intentional. Anyway, I think that only finite components should be allowed in algebras.

This comment has been minimized.

Copy link
@asmeurer

asmeurer Jun 10, 2019

Member

Probably quite a few places are like this. For the places where you changed is_complex to is_complex or is_infinite did you check if it makes sense for infinite quantities?

@codecov

This comment has been minimized.

Copy link

commented Jun 6, 2019

Codecov Report

Merging #16978 into master will increase coverage by 0.017%.
The diff coverage is 98.214%.

@@              Coverage Diff              @@
##            master    #16978       +/-   ##
=============================================
+ Coverage   74.411%   74.429%   +0.017%     
=============================================
  Files          622       622               
  Lines       160930    160968       +38     
  Branches     37784     37797       +13     
=============================================
+ Hits        119751    119808       +57     
+ Misses       35858     35839       -19     
  Partials      5321      5321
Show resolved Hide resolved sympy/core/numbers.py Outdated
@@ -31,7 +31,7 @@

x, y, z = map(Symbol, 'xyz')


@XFAIL
def test_single_normal():

This comment has been minimized.

Copy link
@oscarbenjamin

oscarbenjamin Jun 7, 2019

Contributor

Why is this XFAIL?

This comment has been minimized.

Copy link
@ShubhamKJha

ShubhamKJha Jun 7, 2019

Author Member

This test is failing. I am not familiar with the code in the stats part, so I couldn't figure it out.
The result I am getting is:

>>> from sympy import *
>>> from sympy.stats import Normal, quantile
>>> x = Symbol('x')
>>> mu = Symbol('mu', real=True)
>>> sigma = Symbol('sigma', positive=True)
>>> X = Normal('x', 0, 1)
>>> Y = X*sigma + mu
>>> quantile(Y)(x)
{sqrt(2)*sigma*(sqrt(2)*mu/(2*sigma) + erfinv(2*x - 1))}

## or
⎧     ⎛√2⋅μ                  ⎞⎫
⎨√2⋅σ⋅⎜──── + erfinv(2⋅x - 1)⎟⎬
⎩     ⎝2⋅σ                   ⎠⎭

while the original result is:

>>> Intersection(S.Reals, FiniteSet(sqrt(2)*sigma*(sqrt(2)*mu/(2*sigma) + erfinv(
2*x - 1))))
    ⎧     ⎛√2⋅μ                  ⎞⎫
ℝ ∩ ⎨√2⋅σ⋅⎜──── + erfinv(2⋅x - 1)⎟⎬
    ⎩     ⎝2⋅σ                   ⎠⎭

This comment has been minimized.

Copy link
@oscarbenjamin

oscarbenjamin Jun 7, 2019

Contributor

We need to check the assumptions on that expression. The intersection will evaluate if the expression is deemed to be real (or not).

This comment has been minimized.

Copy link
@ShubhamKJha

ShubhamKJha Jun 14, 2019

Author Member

The intersection doesn't evaluate, the operation quantile(Y)(x) is resulting the evaluated result only.

This comment has been minimized.

Copy link
@czgdp1807

czgdp1807 Jun 15, 2019

Member

the result you are getting is correct, may be the code of quantile may need some changes in assumptions of variables, try some debugging and check where the Intersection is generated.

This comment has been minimized.

Copy link
@czgdp1807

czgdp1807 Jun 15, 2019

Member

I would say your result looks more intuitive, change the test if needed.

This comment has been minimized.

Copy link
@oscarbenjamin

oscarbenjamin Jun 23, 2019

Contributor

I'm unresolving this because the XFAIL is still there.

@oscarbenjamin

This comment has been minimized.

Copy link
Contributor

commented Jun 7, 2019

This generally looks good although I left comments

There are lots of places where you've changed e.g. real->extended_real or positive->extended_positive. Are those needed because of the changes here or is that just unfinished business from #16666?

It looks like there are a number of places where you are testing if x.is_complex or x.is_infinite. Potentially those would benefit from being x.is_extended_complex. How many places are there in the code where x.is_complex is used and how many need to also check for x.is_finite? I count 40 occurrences of is_complex:

$ git grep is_complex | grep -v test_ | wc -l
      40
@ShubhamKJha

This comment has been minimized.

Copy link
Member Author

commented Jun 7, 2019

There are lots of places where you've changed e.g. real->extended_real or positive->extended_positive. Are those needed because of the changes here or is that just unfinished business from #16666?

I tried not to revert any from #16666, most of them appeared to be left unchanged during #16666.

Apart from the occurrences of is_complex I also checked the occurrences of complex and changed when seemed appropriate.
The present notion of complex is that of extended_complex, therefore, I changed the instances of is_complex to is_complex or is_infinite to better account for oo, -oo, zoo.

@ShubhamKJha

This comment has been minimized.

Copy link
Member Author

commented Jun 7, 2019

Thanks @oscarbenjamin for this review, I will go through the diff for the instances where is_complex is actually asking for finite complex.

@oscarbenjamin

This comment has been minimized.

Copy link
Contributor

commented Jun 21, 2019

This branch has merge conflicts now

@smichr smichr changed the title [WIP] Make comple->finite in the codebase. [WIP] Make complex->finite in the codebase. Jun 21, 2019

@@ -376,7 +376,7 @@ def __init__(cls, *args, **kws):
if pname not in cls.__dict__:
setattr(cls, pname, make_property(fact))

# Finally, add any missing automagic property (e.g. for Basic)
# Finally, add any missing automatic property (e.g. for Basic)

This comment has been minimized.

Copy link
@oscarbenjamin

oscarbenjamin Jun 23, 2019

Contributor

I think this is probably deliberately spelled as automagic (which is a word)

@@ -332,7 +332,7 @@ def __ge__(self, other):
except SympifyError:
raise TypeError("Invalid comparison %s >= %s" % (self, other))
for me in (self, other):
if me.is_complex and me.is_extended_real is False:
if (me.is_complex or me.is_infinite) and me.is_extended_real is False:

This comment has been minimized.

Copy link
@oscarbenjamin

oscarbenjamin Jun 23, 2019

Contributor

In general this replacement will not be equivalent since if x is complex then (1/x) will be complex or infinite but (1/x).is_complex and (1/x).is_infinite will both return None. It isn't possible to make something equivalent without having extended_complex.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.