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 solvers to handle binomial. #18802

Merged
merged 21 commits into from
Mar 11, 2020
Merged

Update solvers to handle binomial. #18802

merged 21 commits into from
Mar 11, 2020

Conversation

sbt4104
Copy link
Contributor

@sbt4104 sbt4104 commented Mar 8, 2020

References to other Issues or PRs

Fixes #10993

Brief description of what is fixed or changed

Added conditions to expand binomial in solvers
Update expand function for binomial

Other comments

Please suggest changes.

Release Notes

  • solvers
    • Fix solvers to handle binomials
  • functions
    • Update expand function for binomials.

@sympy-bot
Copy link

sympy-bot commented Mar 8, 2020

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

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

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.

#### References to other Issues or PRs
Fixes #10993

#### Brief description of what is fixed or changed
Added conditions to expand binomial in solvers
Update expand function for binomial
#### Other comments 
Please suggest changes.

### Release Notes
<!-- BEGIN RELEASE NOTES -->
* solvers
    - Fix solvers to handle binomials
* functions
    - Update expand function for binomials.
<!-- END RELEASE NOTES -->

Update

The release notes on the wiki have been updated.

@oscarbenjamin
Copy link
Contributor

I'm not sure where this belongs but certainly not as the opening lines in the solve function. Unfortunately solve is a bit of a mess so there isn't necessarily a correct place to put something like this.

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 8, 2020

I think putting those conditions in "preprocess equations" is better.

@codecov
Copy link

codecov bot commented Mar 8, 2020

Codecov Report

Merging #18802 into master will increase coverage by 0.024%.
The diff coverage is 100%.

@@              Coverage Diff              @@
##            master    #18802       +/-   ##
=============================================
+ Coverage   75.651%   75.676%   +0.024%     
=============================================
  Files          647       647               
  Lines       168420    168512       +92     
  Branches     39682     39705       +23     
=============================================
+ Hits        127413    127524      +111     
+ Misses       35454     35440       -14     
+ Partials      5553      5548        -5

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 9, 2020

Please review and suggest the changes.

@oscarbenjamin
Copy link
Contributor

Consider that you might have an expression involving a binomial like

In [4]: expr = 1 + binomial(x, 3)                                                                                                                             

In [5]: isinstance(expr, binomial)                                                                                                                            
Out[5]: False

In [6]: expr.has(binomial)                                                                                                                                    
Out[6]: True

We probably don't want to expand all functions so it would be better to use expr.replace to pick out the binomials and expand them.

This code is still not in the right place: solve hasn't even converted Eqs to Expr at that point. Look through solve to find where similar transformations are happening.

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 9, 2020

I have made the changes so that only binomial terms are expanded and added the code after Eqs are checked and converted. Please check if it is correctly done.

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 9, 2020

can someone explain to me why my test on travis-ci is failing.
$ bin/test_travis.sh
+[[ '' == \t\r\u\e ]]
+[[ '' == \t\r\u\e ]]
+[[ '' == \t\r\u\e ]]
+[[ '' == \t\r

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 9, 2020

I pushed the same changes again and it passed.
Please review the changes.

@oscarbenjamin
Copy link
Contributor

I think translating binomials should still come later after the loop that it is in now.

This will need to have a test added for equations with binomial coefficients that can be solved.

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 10, 2020

I will make the changes.

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 10, 2020

I have added the test.

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 10, 2020

I have added the binomial expansion after the loop, please suggest changes.

@oscarbenjamin
Copy link
Contributor

It is failing if in binomial(n,k) n is a polynomial in x, example
y = solve(1-binomial(x+1, 3),x)

In what way was it failing?

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 11, 2020

I had written it like this in the previous commit
f = f.replace(binomial, lambda n, k: expand_func(binomial(n, k)) if n==symbol else binomial(n, k))
so for y = solve(1-binomial(x+1, 3),x), it was comparing x+1 with x and returned 'NotImplementedError:
No algorithms are implemented to solve equation binomial(x + 3, 3)'

though it worked perfectly for y = solve(1-binomial(x+1, 3), x+1)

so i changed it to solve cases like this
f = f.replace(binomial, lambda n, k: expand_func(binomial(n, k)) if symbol in n.free_symbols and len(n.free_symbols) == 1 else binomial(n,k))

@oscarbenjamin
Copy link
Contributor

This is probably best:

In [5]: syms = [x]                                                                                                                                            

In [6]: binomial(x, 2).replace(lambda e: isinstance(e, binomial) and e.has(*syms), lambda e: expand_func(e))                                                  
Out[6]: 
x⋅(x - 1)
─────────
    2  

@oscarbenjamin
Copy link
Contributor

It looks like expand_func for binomial could be improved:

In [18]: expand_func(binomial(x, 2))                                                                                                                          
Out[18]: 
x⋅(x - 1)
─────────
    2    

In [19]: expand_func(binomial(x+2, x))                                                                                                                        
Out[19]: 
⎛x + 2⎞
⎜     ⎟
⎝  x  ⎠

In [20]: gammasimp(_.rewrite(factorial))                                                                                                                      
Out[20]: 
(x + 1)⋅(x + 2)
───────────────
       2   

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 11, 2020

This is probably best:

In [5]: syms = [x]                                                                                                                                            

In [6]: binomial(x, 2).replace(lambda e: isinstance(e, binomial) and e.has(*syms), lambda e: expand_func(e))                                                  
Out[6]: 
x⋅(x - 1)
─────────
    2  

Should we expand the binomial if we have a multivariable equation, example
binomial(x + y, 2)

@oscarbenjamin
Copy link
Contributor

Should we expand the binomial if we have a multivariable equation

Yes if any of the symbols of interest is in the binomial. Otherwise we won't be able to solve the equations.

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 11, 2020

ok, I will make that change.

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 11, 2020

I have made the changes, please review.

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 11, 2020

It looks like expand_func for binomial could be improved:

In [18]: expand_func(binomial(x, 2))                                                                                                                          
Out[18]: 
x⋅(x - 1)
─────────
    2    

In [19]: expand_func(binomial(x+2, x))                                                                                                                        
Out[19]: 
⎛x + 2⎞
⎜     ⎟
⎝  x  ⎠

In [20]: gammasimp(_.rewrite(factorial))                                                                                                                      
Out[20]: 
(x + 1)⋅(x + 2)
───────────────
       2   

expand_func expands binomial(x, x-1) properly but fails for binomial(x+1, x)

I think changing
if k.is_Add and n in k.args : k = n - k
to
if (k.is_Add and n in k.args) or (n.is_Add and k in n.args) : k = n - k
will solve this problem

@oscarbenjamin
Copy link
Contributor

Or maybe just if (k - n).is_Integer

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 11, 2020

yes, this one will also expand cases like
solve_func(binomial(x + 1, x - 1))

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 11, 2020

should I send a PR for this along with the issue?

@oscarbenjamin
Copy link
Contributor

You can make those changes in this PR as well

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 11, 2020

I have made the changes, please review.

@@ -1012,7 +1012,7 @@ def _eval_expand_func(self, **hints):
return binomial(*self.args)

k = self.args[1]
if k.is_Add and n in k.args:
if (n-k).is_Integer:
k = n - k
Copy link
Contributor

Choose a reason for hiding this comment

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

There should be a test for this.

@@ -1460,6 +1460,9 @@ def _solve(f, *symbols, **flags):
raise NotImplementedError(not_impl_msg % f)
symbol = symbols[0]

#expand binomials only if it has the unknown symbol
f = f.replace(lambda e: isinstance(e, binomial) and e.has(symbol), lambda e: expand_func(e))
Copy link
Contributor

Choose a reason for hiding this comment

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

This line is too long

assert solve(Eq(binomial(x,2),0)) == [0, 1]
assert solve(a+binomial(x, 3),a) == [-binomial(x, 3)]
assert solve(x-binomial(a, 3) + binomial(y,2) + sin(a),x) == [-sin(a) + binomial(a, 3) - binomial(y, 2)]
assert solve((x+1)-binomial(x+1, 3),x) == [-2, -1, 3]
Copy link
Contributor

Choose a reason for hiding this comment

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

Include a space after a comma

@oscarbenjamin
Copy link
Contributor

This looks good. I'll merge it once tests pass.

For future PRs can you try to use better commit messages? The messages here started out good but then got less good as time went on. You should imagine that the message will be looked at in future by someone who does not know the full context of the PR. Something like "add test" does not convey much about the change that is being made to the code.

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 11, 2020

I will keep it in mind, this will not happen again.

@oscarbenjamin oscarbenjamin merged commit 516fa83 into sympy:master Mar 11, 2020
@oscarbenjamin
Copy link
Contributor

Merged. Thanks for this!

@sbt4104
Copy link
Contributor Author

sbt4104 commented Mar 12, 2020

Thank you, l learned a lot from this.

@sbt4104 sbt4104 deleted the update_solver_to_handle_binomial branch March 12, 2020 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Solving equations with binomial coefficients should expand them first
4 participants