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] [GSoC] Fixing lambert in bivariate to give all real solutions #16890

Open
wants to merge 30 commits into
base: master
from

Conversation

Projects
None yet
6 participants
@jmig5776
Copy link
Member

commented May 25, 2019

References to other Issues or PRs

Brief description of what is fixed or changed

This PR tends to fix bivariate so that all real solutions are returned. For eg.
Earlier

>>>solve((1/x + exp(x/2)).diff(x), x)
[4*LambertW(sqrt(2)/4)]

Now

>>>solve((1/x + exp(x/2)).diff(x), x)
[4*LambertW(-sqrt(2)/4), 4*LambertW(sqrt(2)/4), 4*LambertW(-sqrt(2)/4, -1)]

Other comments

@Yathartha22 @aktech @Shekharrajak

Release Notes

  • solvers
    • improved bivariate to return all lambert solutions
@sympy-bot

This comment has been minimized.

Copy link

commented May 25, 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:

  • solvers

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
This PR tends to fix bivariate so that all real solutions are returned. For eg.
Earlier
```python
>>>solve((1/x + exp(x/2)).diff(x), x)
[4*LambertW(sqrt(2)/4)]
```
Now
```python
>>>solve((1/x + exp(x/2)).diff(x), x)
[4*LambertW(-sqrt(2)/4), 4*LambertW(sqrt(2)/4), 4*LambertW(-sqrt(2)/4, -1)]
```

#### Other comments
@Yathartha22 @aktech @Shekharrajak 

#### 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 -->
* solvers
  * improved `bivariate` to return all lambert solutions
<!-- END RELEASE NOTES -->

@jmig5776 jmig5776 changed the title [WIP] [GSOC] Fixing lambert in bivariate to give all solutions [WIP] [GSoC] Fixing lambert in bivariate to give all solutions May 25, 2019

@codecov

This comment has been minimized.

Copy link

commented May 25, 2019

Codecov Report

Merging #16890 into master will increase coverage by 0.468%.
The diff coverage is 100%.

@@              Coverage Diff              @@
##            master    #16890       +/-   ##
=============================================
+ Coverage   73.943%   74.412%   +0.468%     
=============================================
  Files          620       622        +2     
  Lines       160375    160806      +431     
  Branches     37632     37747      +115     
=============================================
+ Hits        118587    119659     +1072     
+ Misses       36300     35831      -469     
+ Partials      5488      5316      -172

@sylee957 sylee957 added the solvers label May 26, 2019

@jmig5776

This comment has been minimized.

Copy link
Member Author

commented May 27, 2019

All the test cases are checked from wolfram.

Show resolved Hide resolved sympy/solvers/bivariate.py Outdated
Show resolved Hide resolved sympy/solvers/tests/test_solvers.py Outdated
Show resolved Hide resolved sympy/solvers/bivariate.py Outdated
@jmig5776

This comment has been minimized.

Copy link
Member Author

commented May 27, 2019

Moreover I think I should add @slow on the test I am adding because they are increasing the test time significantly.

@Yathartha22
Copy link
Contributor

left a comment

I haven't checked the tests. I will try to have a look over them tonight.

Show resolved Hide resolved sympy/solvers/bivariate.py Outdated
@Yathartha22

This comment has been minimized.

Copy link
Contributor

commented May 28, 2019

Moreover I think I should add @slow on the test I am adding because they are increasing the test time significantly.

Can you mention the slow test and how much time it takes to run?

@jmig5776

This comment has been minimized.

Copy link
Member Author

commented May 28, 2019

Can you mention the slow test and how much time it takes to run?
Screenshot from 2019-05-28 13-05-45

This screenshot is from travis tests that ran on this PR.

@Yathartha22

This comment has been minimized.

Copy link
Contributor

commented May 29, 2019

I would also like @smichr to have his opinion here.

@oscarbenjamin oscarbenjamin added the GSoC label May 30, 2019

@jmig5776

This comment has been minimized.

Copy link
Member Author

commented May 30, 2019

@smichr Please review this.

@aktech
Copy link
Member

left a comment

I have made some comments based on the current state of the PR. Here are some general comments:

  • As suggested in previous meeting, please document/proof things before writing solutions.
  • The motivation of this PR is not very clear, it doesn't mentions what's the exact problem it's trying to solve. Fixing lambert in bivariate to give all solutions is too broad.
  • Please define the problem in detail to answer the questions like:

  • What solutions are missed?
  • What type of lambert type equation it is for?
  • What's the current algorithm to solve such type of equation and what's wrong with current logic?
  • What's the proposed solution?
  • Why is the proposed solution better and how it won't effect the other equations (no side effects).
  • What is the testing strategy to verify that the proposed solution works?
  • Which examples/equations are we going to use to make sure that those are necessary and sufficient for testing purposes?
Show resolved Hide resolved sympy/solvers/bivariate.py Outdated
Show resolved Hide resolved sympy/solvers/tests/test_solvers.py Outdated
Show resolved Hide resolved sympy/solvers/bivariate.py Outdated
Show resolved Hide resolved sympy/solvers/bivariate.py Outdated
Show resolved Hide resolved sympy/solvers/bivariate.py Outdated
Show resolved Hide resolved sympy/solvers/tests/test_solvers.py
@jmig5776

This comment has been minimized.

Copy link
Member Author

commented Jun 2, 2019

Screenshot from 2019-06-02 12-21-30

@jmig5776

This comment has been minimized.

Copy link
Member Author

commented Jun 3, 2019

What solutions are missed?

The solutions are missed due to the argument inside the LambertW function of value

l = LambertW(d/(a*b)*exp(c*d/a/b)*exp(-f/a), k)

While converting all lambert solvable equations to this form
F(X, a..f) = a*log(b*X + c) + d*X + f = 0 some solutions get missed.

For eg:
Consider the equation (a/x + exp(x/2)).diff(x) = 0 which is
-a/x**2 + exp(x/2)/2 = 0(Please take note of the x**2 in denominator of a).
And we can also write this equation as -a/(-x)**2 + exp(x/2)/2 = 0.
What sympy do is to convert this equation to this form:

F(X, a..f) = a*log(b*X + c) + d*X + f = 0

which will be 2*log(x) + x/2 - log(a) = 0 and 2*log(-x) + x/2 - log(a) = 0 respectively:
So solutions corresponding to both equations are:

2*log(x) + x/2 - log(a) = 0 --> [4*LambertW(sqrt(2)*sqrt(a)/4)] --> this is currently included
2*log(-x) + x/2 - log(a) = 0 --> [4*LambertW(-sqrt(2)*sqrt(a)/4)] --> this is missed

What type of lambert type equation it is for?

This is for the equations where the changing of cofficients of the target equation doesn't
change the original equation just like the above case.

What's the current algorithm to solve such type of equation and what's wrong with current logic?

Current implementation is shown above and what is wrong is that it is not considering
the other logarithm equations which are originated from the original equation.
2*log(-x) + x/2 - log(a) = 0 in case of (a/x + exp(x/2)).diff(x) = 0.

What's the proposed solution?

This problem can be solved by two methods as follows:

  • Combining all the logarithm forms generated by the original equation(by taking abs)
2*log(x) + x/2 - log(a) = 0 + 2*log(-x) + x/2 - log(a) = 0
                        \      /
                         \    /
                          \  /
            2*log(abs(x)) + x/2 - log(a) = 0                     

I think this method is not viable at this time as it will be very complex to solve
the equation invloving abs with log.

  • This method I propose to solve this problem i.e considering all solutions and
    eliminating by substitution.
    For example for this equation (1/x + exp(x/2)).diff(x) = 0
    Possible solutions considered [4*LambertW(-sqrt(2)*sqrt(a)/4), 4*LambertW(sqrt(2)*sqrt(a)/4), \ 4*LambertW(-sqrt(2)*sqrt(a)/4, -1), 4*LambertW(sqrt(2)*sqrt(a)/4, -1)]
    Solutions after filtering from checksol [4*LambertW(-sqrt(2)*sqrt(a)/4), 4*LambertW(sqrt(2)*sqrt(a)/4)].

Why is the proposed solution better and how it won't effect the other equations (no side effects).

This method involves less computation to solve this problem i.e considering all solutions and
eliminating by substitution rather than first converting the given equation to target equation
in which we doesn't know about coffecient of logarithmic equation and solving it again by
making different equations from it in case of abs taken.

This doesn't effect other equation because the only thing we are doing is considering all solutions
and not changing code for other type of equations. And we are using checksol to check the correct solutions.

What is the testing strategy to verify that the proposed solution works?

Testing strategy should be to involve the cases where current implementation is
missing other logarithmic equations.

Which examples/equations are we going to use to make sure that those are necessary and sufficient for testing purposes?

According to me these tests are sufficient test this strategy:

assert solve((a/x + exp(x/2)).diff(x), x) == \
        [4*LambertW(-sqrt(2)*sqrt(a)/4), 4*LambertW(sqrt(2)*sqrt(a)/4)]
assert solve(x*log(x) + 3*x + 1, x) == \
        [exp(-3 + LambertW(-exp(3))), exp(-3 + LambertW(-exp(3), -1))]
assert solve((1/x + exp(x/2)).diff(x, 2), x) == \
            [6*LambertW((-1)**(S(1)/3)/3), 6*LambertW((-1)**(S(1)/3)/3, -1)]
assert solve(-x**2 + 2**x, x) == [2, 4, -2*LambertW(log(2)/2)/log(2)]
# issue 4271
assert solve((a/x + exp(x/2)).diff(x, 2), x) == \
            [6*LambertW(-(-1)**(S(1)/3)*a**(S(1)/3)/3),
            6*LambertW((-1)**(S(1)/3)*a**(S(1)/3)/3),
            6*LambertW(-(-1)**(S(1)/3)*a**(S(1)/3)/3, -1),
            6*LambertW((-1)**(S(1)/3)*a**(S(1)/3)/3, -1)]
assert solve(x**2 - y**2/exp(x), x, y, dict=True) == \
                [{x: 2*LambertW(-y/2)}, {x: 2*LambertW(y/2)}]

Some test cases that fail due to some other reasons

assert solve((1/x + exp(x/2)).diff(x), x) == \
[4*LambertW(-sqrt(2)/4), 4*LambertW(sqrt(2)/4), 4*LambertW(-sqrt(2)/4, -1)]
assert solve(x**2 - 2**x, x) == [2, 4]

These tests are failing because checksol returns false but expected to return true
Although _lambert is returning correct solutions of equation.

assert solve(a/x + exp(x/2), x) == [2*LambertW(-a/2), 2*LambertW(a/2)]
In this case 2*LambertW(a/2) which is not a solution is included because checksol returns none.

>>>solve((1/x + exp(x/2)).diff(x, 2), x)
[6*LambertW((-1)**(1/3)/3), 6*LambertW((-1)**(1/3)/3, -1)]

here (-1)**1/3 can have these values -1, 1/6(1 - I*3**1/3), 1/6(1 + I*3**1/3)
but sympy only take (-1)**1/3 to be 1/6(1 - I*3**1/3) whereas
real solutions are [6*LambertW(-1/3), 6*LambertW(-1/3, -1)]

Why including checksol in _solve_lambert ?

Due to this test case:

p = symbols('p', positive=True)
eq = 4*2**(2*p + 3) - 2*p - 3
assert _solve_lambert(eq, p, _filtered_gens(Poly(eq), p)) == [
    -S(3)/2 - LambertW(-4*log(2))/(2*log(2))]

To remove the solutions which were supposed to be checked by checksol later in solve.

@jmig5776

This comment has been minimized.

Copy link
Member Author

commented Jun 4, 2019

I think its better to include checksol in solve lambert because many other tests also get failed like

Failed example:
    tsolve(log(x) + 2*x, x)
Expected:
    [LambertW(2)/2]
Got:
    [LambertW(-2)/2, LambertW(2)/2]

According to me _solve_lambert should be able to return all correct solutions. Let me know what you think or have any better suggestions. But for now I am including checksol in _solve_lambert.

@jmig5776 jmig5776 force-pushed the jmig5776:complete_lambert branch from fe9902d to 1f7d557 Jun 4, 2019

@jmig5776

This comment has been minimized.

Copy link
Member Author

commented Jun 4, 2019

Rebasing was not done properly I think.

@jmig5776

This comment has been minimized.

Copy link
Member Author

commented Jun 4, 2019

I had also tried to implement another method involving abs in #16960 .

Show resolved Hide resolved sympy/solvers/solvers.py Outdated
Show resolved Hide resolved sympy/solvers/solvers.py Outdated
@jmig5776

This comment has been minimized.

Copy link
Member Author

commented Jun 11, 2019

Now the problem is being solved by replacing even degrees in original equation with a dummy variable and then calculating the solutions with replacing the dummy variable with +symbol and -symbol.

Show resolved Hide resolved sympy/solvers/tests/test_solvers.py Outdated
assert solve((a/x + exp(x/2)).diff(x), x) == \
[4*LambertW(-sqrt(2)*sqrt(a)/4), 4*LambertW(sqrt(2)*sqrt(a)/4)]
assert solve(x*log(x) + 3*x + 1, x) == \
[exp(-3 + LambertW(-exp(3)))]

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 12, 2019

Contributor

does not have -1branch result.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 12, 2019

Author Member

In this the l in bivariate comes out to be LambertW(_u*exp(3), -1) so because of positive assumption of _u l.is_real is returning False.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 12, 2019

Author Member

Clearly the assumptions in sympy are getting _u*exp(3) to be positive and where as it know that W-1 gives real solution when args is between -1/e to 0. So l.is_real returns false. I don't think we can do here something because if we avoid assumptions while calculating this then more wrong solutions will be included. Please help here.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 12, 2019

Author Member

And moreover W-1 solution here is complex one which i think we are not covering here.

>>>N(exp(-3 + LambertW(-exp(3), -1)))
-0.216986323494879 - 0.257692008289364*I

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 13, 2019

Contributor

Yeah, it's fine then. It should not be considered as of now if it is not real.

a = S(6)/5
assert set(solve(x**a - a**x)) == set(
[a, -a*LambertW(-log(a)/a)/log(a)])
assert set(solve(3**cos(x) - cos(x)**3)) == set(

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 12, 2019

Contributor

Why is this test in XFAIL? Similar test is present above and the result is evaluated.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 12, 2019

Author Member

Here because of a which is rational so it is not giving desired result. rest other two will be taken care of in next commits.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 12, 2019

Author Member

solve(3**cos(x) - cos(x)**3) This test case is working fine and placed at right place.
solve(3*sin(x) - x*sin(3), x) This test can't be solved by SymPy as described by @aktech here
solve(x**a - a**x) for a to be rational this PR doesn't handle these cases.

Show resolved Hide resolved sympy/solvers/tests/test_solvers.py Outdated
if lhs.is_Add and lhs.has(t):
llhs1, llhs2 = map(lambda i: lhs.xreplace({t: i}), (symbol, -symbol))
sol1, sol2 = map(lambda i: _solve_lambert(i - rhs, symbol, gens), (llhs1, llhs2))
return list((set(sol1 + sol2)))

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 12, 2019

Contributor

Just do simple concatenation: return sol1 + sol2.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 12, 2019

Author Member

I applied set because same solution were being returned twice in a list in some tests.

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 13, 2019

Contributor

Interesting! Any specific reason? Maybe the same equation is being solved multiple times. Probably this needs to be fixed.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 13, 2019

Author Member

Only one test of test_ode gets failed. Here is the test and explanation:-

>>>eq5 = 2*x**2*f(x) + f(x)**3 + (x*f(x)**2 - 2*x**3)*f(x).diff(x)
>>>dsolve(eq5, hint='1st_homogeneous_coeff_best')
[Eq(f(x), exp(2*C1 + LambertW(-2*x**4*exp(-4*C1))/2)/x),
 Eq(f(x), exp(2*C1 + LambertW(-2*x**4*exp(-4*C1))/2)/x)]

In this test case the equation that enters _solve_lambert is

# lhs = _X0**2*(-2*C1 + 2*log(_X0) + log(x/_X0)) and rhs = -x**2 where
# symbol = _X0
>>>symbol.assumptions0
{'commutative': True}
>>>symbol.is_positive
None

So when in this equation _X0 gets replaced by t (becomes _t**2*(-2*C1 + 2*log(_X0) + log(x/_X0))) it enters code of lhs.is_Mul
where after lhs = expand_log(log(lhs)) happens then lhs becomes
log(_t**2*(-2*C1 + 2*log(_X0) + log(x/_X0)))
Clearly _t doesn't have assumptions to be positive so it doesn't get separated out but the equation gets solved twice with -symbol and +symbol where both equation comes out to be same.
I think its difficult to separate out those t's which can't be separated out or to determine. One thing we can do here that from assumptions checking out whether symbol.is_positive or not. But I think that will be too specific. But if you advise then I will try to do something to avoid same equations. Although it happens only when dsolve calls because in solve equations enter _solve_lambert with symbol assumptions positive. In `solve it doesn't have same equation solved twice.

sol1, sol2 = map(lambda i: _solve_lambert(i - rhs, symbol, gens), (llhs1, llhs2))
return list((set(sol1 + sol2)))

if lhs.has(t):

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 12, 2019

Contributor

If it has t, means it has even powers which implies the equation should diverge to two: with +symbol and -symbol.
Why it's not done here?

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 12, 2019

Author Member

Not every even_degree is leading to two equations. Only those equation in which even degree is affecting the cofficients in alog(bx + c) are branching out to two equations which is being taken careof by applying expand_log above.

@jmig5776 jmig5776 changed the title [WIP] [GSoC] Fixing lambert in bivariate to give all solutions [WIP] [GSoC] Fixing lambert in bivariate to give all real solutions Jun 12, 2019

lamcheck = [tmp for tmp in gens
if (tmp.func in [exp, log] or
(tmp.is_Pow and symbol in tmp.exp.free_symbols))]
if not lamcheck:
raise NotImplementedError()

if lhs.is_Add and lhs.has(t) and len(even_degrees) == 1:

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 13, 2019

Contributor

What if len(even_degrees) > 1? Any specific reason to have this check?

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 13, 2019

Author Member

Yes suppose there are more than one even degrees are present like t**2 + t**4 + t_independent_term.
And this will add into the code so diff = expand_log(log(t**2+t**4) - log(t_independent_term))
which will not be expanded which is not required here and is useless to us.

>>>t = Symbol('t', positive = True)
>>>expand_log(log(t**2 + t**4))
log(t**4 + t**2)

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 15, 2019

Contributor

Okay, then you should do the replacement (symbol -> t) only when len == 1 above. This will avoid unnecessary re-replacement to the original equation below.
Also please verify if this goes in accordance with Mul types.

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 15, 2019

Contributor

Another thing, the .is_Add case was not present before. What caused this to be included? Which equations get here?

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 15, 2019

Author Member

then you should do the replacement (symbol -> t) only when len == 1 above

No its only the case .is_Add when it is required. Mul doesn't have that type of requirement.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 15, 2019

Author Member

Another thing, the .is_Add case was not present before. What caused this to be included? Which equations get here?

Clearly from above discussion it was made to handle these type of equation solve(x**2 - 2**x, x) which were not including -2*LambertW(log(2)/2)/log(2) in the solution.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 15, 2019

Author Member

Also please verify if this goes in accordance with Mul types.

The logic of handling when lhs.is_Add and lhs.is_Mul are completely different in the way they are handled.
Moreover lhs.is_Mul can have any number of even_degrees because Mul type is easily converted to log form rather than the case with lhs.is_Add.

@@ -199,17 +199,40 @@ def _solve_lambert(f, symbol, gens):
nrhs, lhs = f.as_independent(symbol, as_Add=True)
rhs = -nrhs

even_degrees = [i for i in degree_list(lhs) if i%2 == 0]

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 15, 2019

Contributor

degree_list(lhs, symbol) will be better, we are only concerned with the symbol's degree.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 15, 2019

Author Member

Okay It will be included in next commit.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 16, 2019

Author Member

degree_list(lhs, symbol) raises this error sympy.polys.polyerrors.PolynomialError: exp(2*_X0*I/x) contains an element of the set of generators. I dont know how to fix this.

This comment has been minimized.

Copy link
@Yathartha22

Yathartha22 Jun 17, 2019

Contributor

Since degree_list takes *gens as the arguments it throws a polynomialerror if the generator is contained in something else. It expects the generator to be completely free. Even y**x for gen x throws exception.

Also I see another concern using degree_list, it returns the degree but we require all power of the concerned variable to be returned like consider f = x**2 + x**3 + y**2, we require (2, 3) to be returned and not just (3, ) which is the case with degree_list (There is a difference between degree and power).
Maybe we require a new routine to fulfil our task. The API could be something like power_list(f, *symbols). This will return all powers of the desired symbols.

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 17, 2019

Author Member

Ahh I got your point!!. Okay I will be creating power_list in a separate PR. Can you please tell me where I should put power_list. I mean I saw the working of degree_list, it was very complex. I want to know how should it work and where should it be placed.

return _solve_lambert(llhs1, symbol, gens)
else:
sol1, sol2 = map(lambda i: _solve_lambert(i, symbol, gens), (llhs1, llhs2))
return list(set(sol1 + sol2))

This comment has been minimized.

Copy link
@jmig5776

jmig5776 Jun 16, 2019

Author Member

It comes out after python debugging that this test in test_ode :

>>>eq5 = 2*x**2*f(x) + f(x)**3 + (x*f(x)**2 - 2*x**3)*f(x).diff(x)
>>>dsolve(eq5, hint='1st_homogeneous_coeff_best')
Eq(f(x), exp(2*C1 + LambertW(-2*x**4*exp(-4*C1))/2)/x)  # if set() is used
# [Eq(f(x), exp(2*C1 + LambertW(-2*x**4*exp(-4*C1))/2)/x),
# Eq(f(x), exp(2*C1 + LambertW(-2*x**4*exp(-4*C1))/2)/x)] --> if sol1 +sol2

The two branches of the equation i.e -log(-_x**2) + 2*log(_X0) + log(-2*_C1 + log(_X0) + log(_x)) and 2*log(-_X0) - log(-_x**2) + log(-2*_C1 + log(_X0) + log(_x)). Solution of both the different branches is returned from _solve_lambert to be the same i.e Eq(f(x), exp(2*C1 + LambertW(-2*x**4*exp(-4*C1))/2)/x).

Moreover I had also applied a check above that if llhs1 == llhs2 then ony once the equation is solved.

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.