JMIG57762019-10-02T18:09:39+00:00https://jmig5776.github.io/Jogi Miglanijmig5776@gmail.comFinal report for GSoC 2019 (Week 12)2019-08-18T00:00:00+00:00https://jmig5776.github.io//gsoc-final-report<p>It’s finally the last week of the Google Summer of Code 2019. Before I start
discussing my work over the summer I would like to highlight my general
experience with the GSoC program.</p>
<p>GSoC gives students all over the world the opportunity to connect and
collaborate with some of the best programmers involved in open source from
around the world. I found the programme tremendusly enriching both in terms of
the depth in which I got to explore some of the areas involved in my project
and also gave me exxposure to some areas I had no previous idea about.
The role of a mentor in GSoC is the most important and I consider myself
very lucky to have got Yathartha Anirudh Joshi and Amit Kumar as my mentors.
Amit and Yathartha has been tremendously encouraging and helpful throughout the summer.
I would also like to mention the importance of the entire community involved,
just being part of the SymPy community.</p>
<h3 id="work-completed">Work Completed</h3>
<p>Here is a list of PRs which were opened during the span of GSoC:</p>
<ol>
<li>
<p><a href="https://github.com/sympy/sympy/pull/16976">#16796 Added <code class="highlighter-rouge">_solve_modular</code> for handling equations a - Mod(b, c) = 0 where only b is expr</a></p>
</li>
<li>
<p><a href="https://github.com/sympy/sympy/pull/16890">#16890 Fixing lambert in bivariate to give all real solutions</a></p>
</li>
<li>
<p><a href="https://github.com/sympy/sympy/pull/16960">#16960 (Don’t Merge)(Prototype) Adding abs while converting equation to log form to get solved by <code class="highlighter-rouge">_lambert</code></a></p>
</li>
<li>
<p><a href="https://github.com/sympy/sympy/pull/17043">#17043 Feature power_list to return all powers of a variable present in f</a></p>
</li>
<li>
<p><a href="https://github.com/sympy/sympy/pull/17079">#17079 Defining ImageSet Union</a></p>
</li>
</ol>
<p>Here is a list of PRs merged:</p>
<ol>
<li>
<p><a href="https://github.com/sympy/sympy/pull/16976">#16796 Added <code class="highlighter-rouge">_solve_modular</code> for handling equations a - Mod(b, c) = 0 where only b is expr</a></p>
</li>
<li>
<p><a href="https://github.com/sympy/sympy/pull/16890">#16890 Fixing lambert in bivariate to give all real solutions</a></p>
</li>
</ol>
<p>Here is all the brief description about the PRs merged:</p>
<ol>
<li><a href="https://github.com/sympy/sympy/pull/16976">#16796 Added <code class="highlighter-rouge">_solve_modular</code> for handling equations a - Mod(b, c) = 0 where only b is expr</a></li>
</ol>
<p>In this PR a new solver <code class="highlighter-rouge">_solve_modular</code> was made for solving modular equations.</p>
<h3 id="what-type-of-equations-to-be-considered-and-what-domain">What type of equations to be considered and what domain?</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A - Mod(B, C) = 0
A -> This can or cannot be a function specifically(Linear, nth degree single
Pow, a**f_x and Add and Mul) of symbol.(But currently its not a
function of x)
B -> This is surely a function of symbol.
C -> It is an integer.
And domain should be a subset of S.Integers.
</code></pre></div></div>
<h3 id="filtering-out-equations">Filtering out equations</h3>
<p>A check is being applied named <code class="highlighter-rouge">_is_modular</code> which verifies that only above
mentioned type equation should return True.</p>
<h3 id="working-of-_solve_modular">Working of <code class="highlighter-rouge">_solve_modular</code></h3>
<p>In the starting of it there is a check if domain is a subset of Integers.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>domain.is_subset(S.Integers)
</code></pre></div></div>
<p>Only domain of integers and it subset are being considered while solving
these equations.
Now after this it separates out a modterm and the rest term on either
sides by this code.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>modterm = list(f.atoms(Mod))[0]
rhs = -(S.One)*(f.subs(modterm, S.Zero))
if f.as_coefficients_dict()[modterm].is_negative:
# f.as_coefficient(modterm) was returning None don't know why
# checks if coefficient of modterm is negative in main equation.
rhs *= -(S.One)
</code></pre></div></div>
<p>Now the equation is being inverted with the helper routine <code class="highlighter-rouge">_invert_modular</code>
like this.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>n = Dummy('n', integer=True)
f_x, g_n = _invert_modular(modterm, rhs, n, symbol)
</code></pre></div></div>
<p>I am defining n in <code class="highlighter-rouge">_solve_modular</code> because <code class="highlighter-rouge">_invert_modular</code> contains
recursive calls to itself so if define the n there then it was going to have
many instances which of no use. Thats y I am defining it in <code class="highlighter-rouge">_solve_modular</code>.</p>
<p>Now after the equation is inverted now solution finding takes place.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if f_x is modterm and g_n is rhs:
return unsolved_result
</code></pre></div></div>
<p>First of all if <code class="highlighter-rouge">_invert_modular</code> fails to invert then a ConditionSet is being
returned.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> if f_x is symbol:
if domain is not S.Integers:
return domain.intersect(g_n)
return g_n
</code></pre></div></div>
<p>And if <code class="highlighter-rouge">_invert_modular</code> is fully able to invert the equation then only domain
intersection needs to takes place. <code class="highlighter-rouge">_invert_modular</code> inverts the equation
considering S.Integers as its default domain.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> if isinstance(g_n, ImageSet):
lamda_expr = g_n.lamda.expr
lamda_vars = g_n.lamda.variables
base_set = g_n.base_set
sol_set = _solveset(f_x - lamda_expr, symbol, S.Integers)
if isinstance(sol_set, FiniteSet):
tmp_sol = EmptySet()
for sol in sol_set:
tmp_sol += ImageSet(Lambda(lamda_vars, sol), base_set)
sol_set = tmp_sol
return domain.intersect(sol_set)
</code></pre></div></div>
<p>In this case when g_n is an ImageSet of n and f_x is not symbol so the
equation is being solved by calling <code class="highlighter-rouge">_solveset</code> (this will not lead to
recursion because equation to be entered is free from Mod) and then
the domain intersection takes place.</p>
<h3 id="what-does-_invert_modular-do">What does <code class="highlighter-rouge">_invert_modular</code> do?</h3>
<p>This function helps to convert the equation <code class="highlighter-rouge">A - Mod(B, C) = 0</code> to a
form (f_x, g_n).
First of all it checks the possible instances of invertible cases if not then
it returns the equation as it is.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a, m = modterm.args
if not isinstance(a, (Dummy, Symbol, Add, Mul, Pow)):
return modterm, rhs
</code></pre></div></div>
<p>Now here is the check for complex arguments and returns the equation as it is
if somewhere it finds I.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if rhs.is_real is False or any(term.is_real is False \
for term in list(_term_factors(a))):
# Check for complex arguments
return modterm, rhs
</code></pre></div></div>
<p>Now after this we check of emptyset as a solution by checking range of both
sides of equation.
As modterm can have values between [0, m - 1] and if rhs is out of this range
then emptySet is being returned.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (abs(rhs) - abs(m)).is_positive or (abs(rhs) - abs(m)) is S.Zero:
# if rhs has value greater than value of m.
return symbol, EmptySet()
</code></pre></div></div>
<p>Now the equation haveing these types are being returned as the following</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if a is symbol:
return symbol, ImageSet(Lambda(n, m*n + rhs), S.Integers)
if a.is_Add:
# g + h = a
g, h = a.as_independent(symbol)
if g is not S.Zero:
return _invert_modular(Mod(h, m), (rhs - Mod(g, m)) % m, n, symbol)
if a.is_Mul:
# g*h = a
g, h = a.as_independent(symbol)
if g is not S.One:
return _invert_modular(Mod(h, m), (rhs*invert(g, m)) % m, n, symbol)
</code></pre></div></div>
<p>The more peculiar case is of <code class="highlighter-rouge">a.is_Pow</code> which is handled as following.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if a.is_Pow:
# base**expo = a
base, expo = a.args
if expo.has(symbol) and not base.has(symbol):
# remainder -> solution independent of n of equation.
# m, rhs are made coprime by dividing igcd(m, rhs)
try:
remainder = discrete_log(m / igcd(m, rhs), rhs, a.base)
except ValueError: # log does not exist
return modterm, rhs
# period -> coefficient of n in the solution and also referred as
# the least period of expo in which it is repeats itself.
# (a**(totient(m)) - 1) divides m. Here is link of theoram:
# (https://en.wikipedia.org/wiki/Euler's_theorem)
period = totient(m)
for p in divisors(period):
# there might a lesser period exist than totient(m).
if pow(a.base, p, m / igcd(m, a.base)) == 1:
period = p
break
return expo, ImageSet(Lambda(n, period*n + remainder), S.Naturals0)
elif base.has(symbol) and not expo.has(symbol):
remainder_list = nthroot_mod(rhs, expo, m, all_roots=True)
if remainder_list is None:
return symbol, EmptySet()
g_n = EmptySet()
for rem in remainder_list:
g_n += ImageSet(Lambda(n, m*n + rem), S.Integers)
return base, g_n
</code></pre></div></div>
<p>Two cases are being created based of a.is_Pow</p>
<ol>
<li>x**a</li>
<li>a**x</li>
</ol>
<p>x**a - It is being handled by the helper function <code class="highlighter-rouge">nthroot_mod</code> which returns
required solution. I am not going into very mch detail for more
information you can read the documentation of nthroot_mod.</p>
<p>a**x - For this <code class="highlighter-rouge">totient</code> is being used in the picture whose meaning can be
find on this <a href="https://en.wikipedia.org/wiki/Euler's_theorem">Wikipedia</a>
page. And then its divisors are being checked to find the least period
of solutions.</p>
<ol>
<li><a href="https://github.com/sympy/sympy/pull/16890">#16890 Fixing lambert in bivariate to give all real solutions</a></li>
</ol>
<p>This PR went through many up and downs and nearly made to the most commented PR.
And with the help of @smichr it was successfully merged. It mainly solved the
bug for not returning all solutions of lambert.</p>
<h2 id="explaining-the-function-_solve_lambert-main-function-to-solve-lambert-equations">Explaining the function <code class="highlighter-rouge">_solve_lambert</code> (main function to solve lambert equations)</h2>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input - f, symbol, gens
OutPut - Solution of f = 0 if its lambert type expression else NotImplementedError
</code></pre></div></div>
<p>This function separates out cases as below based on the main function present in
the main equation.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>For the first ones:
1a1) B**B = R != 0 (when 0, there is only a solution if the base is 0,
but if it is, the exp is 0 and 0**0=1
comes back as B*log(B) = log(R)
1a2) B*(a + b*log(B))**p = R or with monomial expanded or with whole
thing expanded comes back unchanged
log(B) + p*log(a + b*log(B)) = log(R)
lhs is Mul:
expand log of both sides to give:
log(B) + log(log(B)) = log(log(R))
1b) d*log(a*B + b) + c*B = R
lhs is Add:
isolate c*B and expand log of both sides:
log(c) + log(B) = log(R - d*log(a*B + b))
</code></pre></div></div>
<p>If the equation are of type 1a1, 1a2 and 1b then the mainlog of the equation is
taken into concern as the deciding factor lies in the main logarithmic term of equation.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>For the next two,
collect on main exp
2a) (b*B + c)*exp(d*B + g) = R
lhs is mul:
log to give
log(b*B + c) + d*B = log(R) - g
2b) -b*B + g*exp(d*B + h) = R
lhs is add:
add b*B
log and rearrange
log(R + b*B) - d*B = log(g) + h
</code></pre></div></div>
<p>If the equation are of type 2a and 2b then the mainexp of the equation is
taken into concern as the deciding factor lies in the main exponential term of equation.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3) d*p**(a*B + b) + c*B = R
collect on main pow
log(R - c*B) - a*B*log(p) = log(d) + b*log(p)
</code></pre></div></div>
<p>If the equation are of type 3 then the mainpow of the equation is
taken into concern as the deciding factor lies in the main power term of equation.</p>
<p>Eventually from all of the three cases the equation is meant to be converted to this form:-</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>f(x, a..f) = a*log(b*X + c) + d*X - f = 0 which has the
solution, X = -c/b + (a/d)*W(d/(a*b)*exp(c*d/a/b)*exp(f/a)).
</code></pre></div></div>
<p>And the solution calculation process is done by <code class="highlighter-rouge">_lambert</code> function.</p>
<p>Everything seems flawless?? You might be thinking no modification is required. Lets
see what loopholes are there in it.</p>
<h2 id="what-does-pr-16890-do">What does PR <a href="https://github.com/sympy/sympy/pull/16890">#16890</a> do?</h2>
<p>There are basically two flaws present with the this approach.</p>
<ol>
<li>Not considering all branches of equation while taking log both sides.</li>
<li>Calculation of roots should consider all roots in case having rational power.</li>
</ol>
<h3 id="1-not-considering-all-branches-of-equation-while-taking-log-both-sides">1. Not considering all branches of equation while taking log both sides.</h3>
<p>Let us consider this equation to be solved by <code class="highlighter-rouge">_solve_lambert</code> function.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-1/x**2 + exp(x/2)/2 = 0
</code></pre></div></div>
<p>So what the old <code class="highlighter-rouge">_solve_lambert</code> do is to convert this equation to following.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2*log(x) + x/2 = 0
</code></pre></div></div>
<p>and calculates its roots from <code class="highlighter-rouge">_lambert</code>.
But it missed this branch of equation while taking log on main equation.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2*log(-x) + x/2 = 0
</code></pre></div></div>
<p>Yeah you can reproduce the original equation from this equation.So basically the problem
was that it missed the branches of equation while taking log. And when does the
main equation have more than one branch?? The terms having even powers of variable x
leads to two different branches of equation.</p>
<p>So how it is solved?
What I has done is that before actually gets into solving I preprocess the main equation
and if it has more than one branches of equation while converting taking log then I consider
all the equations generated from them.(with the help of <code class="highlighter-rouge">_solve_even_degree_expr</code>)</p>
<p>How I preprocess the equation?
So what I do is I replace all the even powers of x present with even powers of t(dummy variable).</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Code for targeted replacement
lhs = lhs.replace(
lambda i: # find symbol**even
i.is_Pow and i.base == symbol and i.exp.is_even,
lambda i: # replace t**even
t**i.exp)
Example:-
Main equation -> -1/x**2 + exp(x/2)/2 = 0
After replacement -> -1/t**2 + exp(x/2)/2 = 0
</code></pre></div></div>
<p>Now I take logarithms on both sides and simplify it.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>After simplifying -> 2*log(t) + x/2 = 0
</code></pre></div></div>
<p>Now I call function <code class="highlighter-rouge">_solve_even_degree_expr</code> to replace the t with +/-x to generate two equations.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Replacing t with +/-x
1. 2*log(x) + x/2 = 0
2. 2*log(-x) + x/2 = 0
</code></pre></div></div>
<p>And consider the solutions of both of the equations to return all lambert real solutions
of <code class="highlighter-rouge">-1/x**2 + exp(x/2)/2 = 0</code>.</p>
<p>Hope you could understand the logic behind this work.</p>
<h3 id="2-calculation-of-roots-should-consider-all-roots-in-case-having-rational-power">2. Calculation of roots should consider all roots in case having rational power.</h3>
<p>This flaw is in the calculation of roots in function <code class="highlighter-rouge">_lambert</code>.
Earlier the function_lambert has the working like :-</p>
<ol>
<li>Find all the values of a, b, c, d, e in the required loagrithmic equation</li>
<li>Then it defines a solution of the form
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-c/b + (a/d)*l where l = LambertW(d/(a*b)*exp(c*d/a/b)*exp(-f/a), k)
</code></pre></div> </div>
<p>and then it included that solution.
I agree everything seems flawless here. but try to see the step where we are defining l.</p>
</li>
</ol>
<p>Let us suppose a hypothetical algorithm just like algorithm used in <code class="highlighter-rouge">_lambert</code>
in which equation to be solved is</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x**3 - 1 = 0
</code></pre></div></div>
<p>and in which we define solution of the form</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x = exp(I*2*pi/n) where n is the power of x in equation
</code></pre></div></div>
<p>so the algorithm will give solution</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x = exp(I*2*pi/3) # but expected was [1, exp(I*2*pi/3), exp(-I*2*pi/3)]
</code></pre></div></div>
<p>which can be found by finding all solutions of</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x**n - exp(2*I*pi) = 0
</code></pre></div></div>
<p>by a different correct algorithm. Thats y it was wrong.
The above algorithm would have given correct values for <code class="highlighter-rouge">x - 1 = 0</code>.</p>
<p>And the question in your mind may arise that why only exp() because the
possiblity of having more than one roots is in exp(), because if the algorithm
would have been like <code class="highlighter-rouge">x = a</code>, where a is some real constant then there is not
any possiblity of further roots rather than solution like <code class="highlighter-rouge">x = a**(1/n)</code>.
And its been done in code like this:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>code
num, den = ((c*d-b*f)/a/b).as_numer_denom()
p, den = den.as_coeff_Mul()
e = exp(num/den)
t = Dummy('t')
args = [d/(a*b)*t for t in roots(t**p - e, t).keys()]
</code></pre></div></div>
<h3 id="work-under-development">Work under development</h3>
<ul>
<li><a href="https://github.com/sympy/sympy/pull/17079">#17079 Defining ImageSet Union</a></li>
</ul>
<p>This PR tends to define a unifying algorithm for linear relations.</p>
<h3 id="future-work">Future Work</h3>
<p>Here is a list that comprises of all the ideas (which were a part of my GSoC
Proposal and/or thought over during the SoC) which can extend my GSoC project.</p>
<ol>
<li>
<p>Integrating helper solvers within solveset: linsolve, solve_decomposition, nonlinsolve</p>
</li>
<li>
<p>Handle nested trigonometric equations.</p>
</li>
</ol>
GSoC 2019 - Week 112019-08-11T00:00:00+00:00https://jmig5776.github.io//gsoc-week-11<p>This was the eleventh week meeting with the GSoC mentors which was scheduled on
Sunday 11th August, 2019 between 11:30 - 12:30 PM (IST). Me, Yathartha and Amit
were the attendees of the meeting. <code class="highlighter-rouge">_solve_modular</code> was discussed in this meeting.</p>
<p>Here is all the brief description about new solver <code class="highlighter-rouge">_solve_modular</code> for solving
modular equations.</p>
<h3 id="what-type-of-equations-to-be-considered-and-what-domain">What type of equations to be considered and what domain?</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A - Mod(B, C) = 0
A -> This can or cannot be a function specifically(Linear, nth degree single
Pow, a**f_x and Add and Mul) of symbol.(But currently its not a
function of x)
B -> This is surely a function of symbol.
C -> It is an integer.
And domain should be a subset of S.Integers.
</code></pre></div></div>
<h3 id="filtering-out-equations">Filtering out equations</h3>
<p>A check is being applied named <code class="highlighter-rouge">_is_modular</code> which verifies that only above
mentioned type equation should return True.</p>
<h3 id="working-of-_solve_modular">Working of <code class="highlighter-rouge">_solve_modular</code></h3>
<p>In the starting of it there is a check if domain is a subset of Integers.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>domain.is_subset(S.Integers)
</code></pre></div></div>
<p>Only domain of integers and it subset are being considered while solving
these equations.
Now after this it separates out a modterm and the rest term on either
sides by this code.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>modterm = list(f.atoms(Mod))[0]
rhs = -(S.One)*(f.subs(modterm, S.Zero))
if f.as_coefficients_dict()[modterm].is_negative:
# f.as_coefficient(modterm) was returning None don't know why
# checks if coefficient of modterm is negative in main equation.
rhs *= -(S.One)
</code></pre></div></div>
<p>Now the equation is being inverted with the helper routine <code class="highlighter-rouge">_invert_modular</code>
like this.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>n = Dummy('n', integer=True)
f_x, g_n = _invert_modular(modterm, rhs, n, symbol)
</code></pre></div></div>
<p>I am defining n in <code class="highlighter-rouge">_solve_modular</code> because <code class="highlighter-rouge">_invert_modular</code> contains
recursive calls to itself so if define the n there then it was going to have
many instances which of no use. Thats y I am defining it in <code class="highlighter-rouge">_solve_modular</code>.</p>
<p>Now after the equation is inverted now solution finding takes place.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if f_x is modterm and g_n is rhs:
return unsolved_result
</code></pre></div></div>
<p>First of all if <code class="highlighter-rouge">_invert_modular</code> fails to invert then a ConditionSet is being
returned.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> if f_x is symbol:
if domain is not S.Integers:
return domain.intersect(g_n)
return g_n
</code></pre></div></div>
<p>And if <code class="highlighter-rouge">_invert_modular</code> is fully able to invert the equation then only domain
intersection needs to takes place. <code class="highlighter-rouge">_invert_modular</code> inverts the equation
considering S.Integers as its default domain.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> if isinstance(g_n, ImageSet):
lamda_expr = g_n.lamda.expr
lamda_vars = g_n.lamda.variables
base_set = g_n.base_set
sol_set = _solveset(f_x - lamda_expr, symbol, S.Integers)
if isinstance(sol_set, FiniteSet):
tmp_sol = EmptySet()
for sol in sol_set:
tmp_sol += ImageSet(Lambda(lamda_vars, sol), base_set)
sol_set = tmp_sol
return domain.intersect(sol_set)
</code></pre></div></div>
<p>In this case when g_n is an ImageSet of n and f_x is not symbol so the
equation is being solved by calling <code class="highlighter-rouge">_solveset</code> (this will not lead to
recursion because equation to be entered is free from Mod) and then
the domain intersection takes place.</p>
<h3 id="what-does-_invert_modular-do">What does <code class="highlighter-rouge">_invert_modular</code> do?</h3>
<p>This function helps to convert the equation <code class="highlighter-rouge">A - Mod(B, C) = 0</code> to a
form (f_x, g_n).
First of all it checks the possible instances of invertible cases if not then
it returns the equation as it is.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a, m = modterm.args
if not isinstance(a, (Dummy, Symbol, Add, Mul, Pow)):
return modterm, rhs
</code></pre></div></div>
<p>Now here is the check for complex arguments and returns the equation as it is
if somewhere it finds I.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if rhs.is_real is False or any(term.is_real is False \
for term in list(_term_factors(a))):
# Check for complex arguments
return modterm, rhs
</code></pre></div></div>
<p>Now after this we check of emptyset as a solution by checking range of both
sides of equation.
As modterm can have values between [0, m - 1] and if rhs is out of this range
then emptySet is being returned.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (abs(rhs) - abs(m)).is_positive or (abs(rhs) - abs(m)) is S.Zero:
# if rhs has value greater than value of m.
return symbol, EmptySet()
</code></pre></div></div>
<p>Now the equation haveing these types are being returned as the following</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if a is symbol:
return symbol, ImageSet(Lambda(n, m*n + rhs), S.Integers)
if a.is_Add:
# g + h = a
g, h = a.as_independent(symbol)
if g is not S.Zero:
return _invert_modular(Mod(h, m), (rhs - Mod(g, m)) % m, n, symbol)
if a.is_Mul:
# g*h = a
g, h = a.as_independent(symbol)
if g is not S.One:
return _invert_modular(Mod(h, m), (rhs*invert(g, m)) % m, n, symbol)
</code></pre></div></div>
<p>The more peculiar case is of <code class="highlighter-rouge">a.is_Pow</code> which is handled as following.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if a.is_Pow:
# base**expo = a
base, expo = a.args
if expo.has(symbol) and not base.has(symbol):
# remainder -> solution independent of n of equation.
# m, rhs are made coprime by dividing igcd(m, rhs)
try:
remainder = discrete_log(m / igcd(m, rhs), rhs, a.base)
except ValueError: # log does not exist
return modterm, rhs
# period -> coefficient of n in the solution and also referred as
# the least period of expo in which it is repeats itself.
# (a**(totient(m)) - 1) divides m. Here is link of theoram:
# (https://en.wikipedia.org/wiki/Euler's_theorem)
period = totient(m)
for p in divisors(period):
# there might a lesser period exist than totient(m).
if pow(a.base, p, m / igcd(m, a.base)) == 1:
period = p
break
return expo, ImageSet(Lambda(n, period*n + remainder), S.Naturals0)
elif base.has(symbol) and not expo.has(symbol):
remainder_list = nthroot_mod(rhs, expo, m, all_roots=True)
if remainder_list is None:
return symbol, EmptySet()
g_n = EmptySet()
for rem in remainder_list:
g_n += ImageSet(Lambda(n, m*n + rem), S.Integers)
return base, g_n
</code></pre></div></div>
<p>Two cases are being created based of a.is_Pow</p>
<ol>
<li>x**a</li>
<li>a**x</li>
</ol>
<p>x**a - It is being handled by the helper function <code class="highlighter-rouge">nthroot_mod</code> which returns
required solution. I am not going into very mch detail for more
information you can read the documentation of nthroot_mod.</p>
<p>a**x - For this <code class="highlighter-rouge">totient</code> is being used in the picture whose meaning can be
find on this <a href="https://en.wikipedia.org/wiki/Euler's_theorem">Wikipedia</a>
page. And then its divisors are being checked to find the least period
of solutions.</p>
<p>Hope I am able to clear out everything!!</p>
<p>Code improvement takes time!!</p>
GSoC 2019 - Week 102019-08-04T00:00:00+00:00https://jmig5776.github.io//gsoc-week-10<p>This was the tenth week meeting with the GSoC mentors which was scheduled on
Sunday 4th August, 2019 between 1:00 - 2:00 PM (IST). Me, Yathartha
were the attendees of the meeting.</p>
<ul>
<li>Discussing previous week’s progress</li>
</ul>
<ol>
<li>
<p>Progress of <code class="highlighter-rouge">_solve_modular</code>:- In PR <a href="https://github.com/sympy/sympy/pull/16976">#16976</a>
After discussing with Yathartha, I decided to change the basic model of the <code class="highlighter-rouge">_solve_modular </code>
such that I should be able to target equations more efficiently and also the rest
of the types of equation should return ConditionSet. Cases like <code class="highlighter-rouge">Mod(a**x, m) - rhs = 0</code>
are special type and will be handled differently with the helper functions of ntheory module.</p>
</li>
<li>
<p>Progress of ImageSet Union:- In PR <a href="https://github.com/sympy/sympy/pull/17079">#17079</a>
This PR is currently been left for review.</p>
</li>
</ol>
<ul>
<li>
<p>Next week goals</p>
</li>
<li>Work upon <code class="highlighter-rouge">_solve_modular</code></li>
<li>In the following week I will be changing the domain of solving equations to
Integers only.</li>
</ul>
<p>Code improvement takes time!!</p>
GSoC 2019 - Week 92019-07-27T00:00:00+00:00https://jmig5776.github.io//gsoc-week-9<p>This was the eigth week meeting with the GSoC mentors which was scheduled on
Saturday 27th July, 2019 between 1:30 - 2:30 PM (IST). Me, Yathartha and Amit
were the attendees of the meeting.</p>
<ul>
<li>Discussing previous week’s progress</li>
</ul>
<ol>
<li>
<p>Progress of <code class="highlighter-rouge">_solve_modular</code>:- In PR <a href="https://github.com/sympy/sympy/pull/16976">#16976</a>
I implemented the basic design of <code class="highlighter-rouge">_solve_modular</code>. Some very good suggestion by
Yathartha for creating <code class="highlighter-rouge">_invert_modular</code> were very helpful. Now basically the
<code class="highlighter-rouge">_solve_modular</code> first do preprocessing and invert it and then find solution and
in final domain intersection takes place.</p>
</li>
<li>
<p>Progress of ImageSet Union:- In PR <a href="https://github.com/sympy/sympy/pull/17079">#17079</a>
I will be implementing an algorithm based on heurestics for performing ImageSet
Union this week. Basic code is being already written for defining a function and
only algorithm part is left which will be performed in this week.</p>
</li>
</ol>
<ul>
<li>
<p>Next week goals</p>
</li>
<li>
<p>Work upon <code class="highlighter-rouge">_solve_modular</code> and ImageSet Union PR</p>
</li>
</ul>
<p>Code improvement takes time!!</p>
GSoC 2019 - Week 82019-07-20T00:00:00+00:00https://jmig5776.github.io//gsoc-week-8<p>This was the eigth week meeting with the GSoC mentors which was scheduled on
Saturday 20th July, 2019 between 12:30 - 1:30 PM (IST). Me, Yathartha and Amit
were the attendees of the meeting.
Finally the PR <a href="https://github.com/sympy/sympy/pull/16890">#16890</a> is merged to
Sympy master and the work for Lambert has been completed. It was only possible
because of the mentors and especially @smichr for his great suggestions in code.</p>
<ul>
<li>Discussing previous week’s progress</li>
</ul>
<p>In this meeting the goals for the time left for GSoC were decided as follows:-</p>
<ol>
<li>
<p>Lambert:- Completed and merged.</p>
</li>
<li>
<p>Solve modular:-
What I experienced with lambert’s PR that got stretched so much which I was not
expecting. Algorithms made from heuristics takes so much time to be ready for
merging so I really don’t know how much time it will take. But It is sure that
it will be completed before the final evaluation.</p>
</li>
<li>
<p>ImageSet Union:- This task will be taken after the solve modular. This is a
very complex task and will need proper guidance and algorithm. I had searched
for some research papers, but what I found out was not that what we want.
Before GSoC final evaluation this task will be started to implement, but I am
not really sure if it would get merged before final evaluation.</p>
</li>
</ol>
<ul>
<li>
<p>Next week goals</p>
</li>
<li>
<p>Work upon <code class="highlighter-rouge">_solve_modular</code> PR</p>
</li>
<li>
<p>If time left then find plan for Imageset Union.</p>
</li>
</ul>
<p>Code improvement takes time!!</p>
Lambert Solver Of Sympy (GSoC 2019 - Week 7)2019-07-15T00:00:00+00:00https://jmig5776.github.io//gsoc-weel-7<p>This was the seventh week meeting with the GSoC mentors which was scheduled on
Monday 15th July, 2019 between 6:00 - 7:00 PM (IST). Me and Yathartha
were the attendees of the meeting. In this blog I will be describing the lambert
equation solver for Sympy and what problems it faced before and how PR
<a href="https://github.com/sympy/sympy/pull/16890">#16890</a> will solve the problems.
It is preassumed that you know what lambert type equations are, so I will not be
explaining that.</p>
<h2 id="explaining-the-function-_solve_lambert-main-function-to-solve-lambert-equations">Explaining the function <code class="highlighter-rouge">_solve_lambert</code> (main function to solve lambert equations)</h2>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input - f, symbol, gens
OutPut - Solution of f = 0 if its lambert type expression else NotImplementedError
</code></pre></div></div>
<p>This function separates out cases as below based on the main function present in
the main equation.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>For the first ones:
1a1) B**B = R != 0 (when 0, there is only a solution if the base is 0,
but if it is, the exp is 0 and 0**0=1
comes back as B*log(B) = log(R)
1a2) B*(a + b*log(B))**p = R or with monomial expanded or with whole
thing expanded comes back unchanged
log(B) + p*log(a + b*log(B)) = log(R)
lhs is Mul:
expand log of both sides to give:
log(B) + log(log(B)) = log(log(R))
1b) d*log(a*B + b) + c*B = R
lhs is Add:
isolate c*B and expand log of both sides:
log(c) + log(B) = log(R - d*log(a*B + b))
</code></pre></div></div>
<p>If the equation are of type 1a1, 1a2 and 1b then the mainlog of the equation is
taken into concern as the deciding factor lies in the main logarithmic term of equation.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>For the next two,
collect on main exp
2a) (b*B + c)*exp(d*B + g) = R
lhs is mul:
log to give
log(b*B + c) + d*B = log(R) - g
2b) -b*B + g*exp(d*B + h) = R
lhs is add:
add b*B
log and rearrange
log(R + b*B) - d*B = log(g) + h
</code></pre></div></div>
<p>If the equation are of type 2a and 2b then the mainexp of the equation is
taken into concern as the deciding factor lies in the main exponential term of equation.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3) d*p**(a*B + b) + c*B = R
collect on main pow
log(R - c*B) - a*B*log(p) = log(d) + b*log(p)
</code></pre></div></div>
<p>If the equation are of type 3 then the mainpow of the equation is
taken into concern as the deciding factor lies in the main power term of equation.</p>
<p>Eventually from all of the three cases the equation is meant to be converted to this form:-</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>f(x, a..f) = a*log(b*X + c) + d*X - f = 0 which has the
solution, X = -c/b + (a/d)*W(d/(a*b)*exp(c*d/a/b)*exp(f/a)).
</code></pre></div></div>
<p>And the solution calculation process is done by <code class="highlighter-rouge">_lambert</code> function.</p>
<p>Everything seems flawless?? You might be thinking no modification is required. Lets
see what loopholes are there in it.</p>
<h2 id="what-does-pr-16890-do">What does PR <a href="https://github.com/sympy/sympy/pull/16890">#16890</a> do?</h2>
<p>There are basically two flaws present with the this approach.</p>
<ol>
<li>Not considering all branches of equation while taking log both sides.</li>
<li>Calculation of roots should consider all roots in case having rational power.</li>
</ol>
<h3 id="1-not-considering-all-branches-of-equation-while-taking-log-both-sides">1. Not considering all branches of equation while taking log both sides.</h3>
<p>Let us consider this equation to be solved by <code class="highlighter-rouge">_solve_lambert</code> function.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-1/x**2 + exp(x/2)/2 = 0
</code></pre></div></div>
<p>So what the old <code class="highlighter-rouge">_solve_lambert</code> do is to convert this equation to following.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2*log(x) + x/2 = 0
</code></pre></div></div>
<p>and calculates its roots from <code class="highlighter-rouge">_lambert</code>.
But it missed this branch of equation while taking log on main equation.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2*log(-x) + x/2 = 0
</code></pre></div></div>
<p>Yeah you can reproduce the original equation from this equation.So basically the problem
was that it missed the branches of equation while taking log. And when does the
main equation have more than one branch?? The terms having even powers of variable x
leads to two different branches of equation.</p>
<p>So how it is solved?
What I has done is that before actually gets into solving I preprocess the main equation
and if it has more than one branches of equation while converting taking log then I consider
all the equations generated from them.(with the help of <code class="highlighter-rouge">_solve_even_degree_expr</code>)</p>
<p>How I preprocess the equation?
So what I do is I replace all the even powers of x present with even powers of t(dummy variable).</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Code for targeted replacement
lhs = lhs.replace(
lambda i: # find symbol**even
i.is_Pow and i.base == symbol and i.exp.is_even,
lambda i: # replace t**even
t**i.exp)
Example:-
Main equation -> -1/x**2 + exp(x/2)/2 = 0
After replacement -> -1/t**2 + exp(x/2)/2 = 0
</code></pre></div></div>
<p>Now I take logarithms on both sides and simplify it.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>After simplifying -> 2*log(t) + x/2 = 0
</code></pre></div></div>
<p>Now I call function <code class="highlighter-rouge">_solve_even_degree_expr</code> to replace the t with +/-x to generate two equations.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Replacing t with +/-x
1. 2*log(x) + x/2 = 0
2. 2*log(-x) + x/2 = 0
</code></pre></div></div>
<p>And consider the solutions of both of the equations to return all lambert real solutions
of <code class="highlighter-rouge">-1/x**2 + exp(x/2)/2 = 0</code>.</p>
<p>Hope you could understand the logic behind this work.</p>
<h3 id="2-calculation-of-roots-should-consider-all-roots-in-case-having-rational-power">2. Calculation of roots should consider all roots in case having rational power.</h3>
<p>This flaw is in the calculation of roots in function <code class="highlighter-rouge">_lambert</code>.
Earlier the function_lambert has the working like :-</p>
<ol>
<li>Find all the values of a, b, c, d, e in the required loagrithmic equation</li>
<li>Then it defines a solution of the form
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-c/b + (a/d)*l where l = LambertW(d/(a*b)*exp(c*d/a/b)*exp(-f/a), k)
</code></pre></div> </div>
<p>and then it included that solution.
I agree everything seems flawless here. but try to see the step where we are defining l.</p>
</li>
</ol>
<p>Let us suppose a hypothetical algorithm just like algorithm used in <code class="highlighter-rouge">_lambert</code>
in which equation to be solved is</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x**3 - 1 = 0
</code></pre></div></div>
<p>and in which we define solution of the form</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x = exp(I*2*pi/n) where n is the power of x in equation
</code></pre></div></div>
<p>so the algorithm will give solution</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x = exp(I*2*pi/3) # but expected was [1, exp(I*2*pi/3), exp(-I*2*pi/3)]
</code></pre></div></div>
<p>which can be found by finding all solutions of</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x**n - exp(2*I*pi) = 0
</code></pre></div></div>
<p>by a different correct algorithm. Thats y it was wrong.
The above algorithm would have given correct values for <code class="highlighter-rouge">x - 1 = 0</code>.</p>
<p>And the question in your mind may arise that why only exp() because the
possiblity of having more than one roots is in exp(), because if the algorithm
would have been like <code class="highlighter-rouge">x = a</code>, where a is some real constant then there is not
any possiblity of further roots rather than solution like <code class="highlighter-rouge">x = a**(1/n)</code>.
And its been done in code like this:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>code
num, den = ((c*d-b*f)/a/b).as_numer_denom()
p, den = den.as_coeff_Mul()
e = exp(num/den)
t = Dummy('t')
args = [d/(a*b)*t for t in roots(t**p - e, t).keys()]
</code></pre></div></div>
<p>Thank you! Thats all it was!!.</p>
GSoC 2019 - Week 62019-07-07T00:00:00+00:00https://jmig5776.github.io//gsoc-week-6<p>This was the sixth week meeting with the GSoC mentors which was scheduled on
Sunday 7th July, 2019 between 1:45 - 2:45 PM (IST). Me, Yathartha and Amit
were the attendees of the meeting. This meeting was short.</p>
<ul>
<li>Discussing previous week’s progress</li>
</ul>
<p>In this meeting both the mentors were convinced by the code for Lambert’s.
And few modifications in documentation and code clean up were suggested by them.
In this week the whole idea of power_list was droppped because @smichr suggested
code for replacing the symbol more targetted as we wanted by which the whole code
was improved. And it was decided to work upon on <code class="highlighter-rouge">_solve_modular</code> mainly now
onwards.</p>
<ul>
<li>
<p>Next week goals</p>
</li>
<li>
<p>Getting merge existing PR for Lambert</p>
</li>
<li>
<p>Work upon <code class="highlighter-rouge">_solve_modular</code> PR</p>
</li>
<li>
<p>If time left then find plan for Imageset Union.</p>
</li>
</ul>
<p>Code improvement takes time!!</p>
GSoC 2019 - Week 52019-06-29T00:00:00+00:00https://jmig5776.github.io//gsoc-week-5<p>This was the fifth week meeting with the GSoC mentors which was scheduled on
Saturday 29th June, 2019 between 11:30 - 12:30 PM (IST). Me, Yathartha and Amit
were the attendees of the meeting. I passed my first evaluation, Amit gave his
feedback and told me some very important points to take notes on. I do personally
believe that his suggestions are the best a mentor could gave to his student after
practicing his suggestions in my real life.</p>
<ul>
<li>Discussing previous week’s progress</li>
</ul>
<p>In this meeting both mentors suggested me to work upon the code improvements and
documentation improvement. And make it more readable to user. ALthough somehow
@smichr had some doubts on the logic that we were implementing. Although a lot
of progress has been there. So I decided to create and discussion for thinking
new logic for implementing Lambert all solutions and work on the current PR as
goes on.</p>
<ul>
<li>
<p>Next week goals</p>
</li>
<li>
<p>Improving existing PR for Lambert</p>
</li>
<li>
<p>Improving <code class="highlighter-rouge">_solve_modular</code> PR also</p>
</li>
<li>
<p>If time left then find plan for Imageset Union.</p>
</li>
</ul>
<p>Code improvement takes time!!</p>
GSoC 2019 - Week 42019-06-22T00:00:00+00:00https://jmig5776.github.io//gsoc-week-4<p>This was the fourth week meeting with the GSoC mentors which was scheduled on
Sunday 22th June, 2019 between 12:30 - 01:30 PM (IST). Me and Yathartha
were the attendees of the meeting.</p>
<ul>
<li>Discussing previous week’s progress</li>
</ul>
<p>In the previous week many things happened very suddenly. In the starting of the
week it was decided that we had to create a function <code class="highlighter-rouge">power_list</code> which could
return powers. So I thought to create this type of function as a new feature in
this PR <a href="https://github.com/sympy/sympy/pull/17043">#17043</a>.
But as @jksoum and @smichr advised that this function can act as
helper function in <code class="highlighter-rouge">bivariate</code> itself so it was decided to make it as helper
function there. And we discussed the code for handling modular equations also.</p>
<ul>
<li>
<p>Next week goals</p>
</li>
<li>
<p>Implementing and get plan for defining imageset union</p>
</li>
</ul>
<p>As per the timeline we decided that Imageset Union should be defined first rather
than code for handling nested trigonometric equation solver. So according to
timeline this week goes for discussion and getting plan for implementing imageset
union. A PR <a href="https://github.com/sympy/sympy/pull/17079">#17079</a> is also started
based on fundamental approach to solve the problem.</p>
<ul>
<li>Getting merge Lambert Solver</li>
</ul>
<p>Most of the work had been done only a little improvement is to be done in <code class="highlighter-rouge">power_list</code>
function. Hope it gets merged by this week.</p>
<p>Code improvement takes time!!</p>
GSoC 2019 - Week 32019-06-16T00:00:00+00:00https://jmig5776.github.io//gsoc-week-3<p>This was the third week meeting with the GSoC mentors which was scheduled on
Sunday 16th June, 2019 between 12:00 - 01:00 PM (IST). Me, Amit, Yathartha and
Shekhar were the attendees of the meeting. In this meeting mainly it was discussed
to improve the code quality and make routine of independent working code.</p>
<ul>
<li>Code quality</li>
</ul>
<p>Every programmer should master this skill to write a good quality code. Everything from
naming, documentation to make routines includes in this skill.</p>
<p>And we discussed mainly the code for the PR <a href="https://github.com/sympy/sympy/pull/16890">#16890</a>
and realising that a new function <code class="highlighter-rouge">power_list</code> to be made to return all the
powers of symbols present in an expression.</p>
<p>Learning new skills!!</p>
GSoC 2019 - Week 22019-06-08T00:00:00+00:00https://jmig5776.github.io//gsoc-week-2<p>This was the second week meeting with the GSoC mentors which was scheduled on
Saturday 8th June, 2019 between 12:00 - 01:00 PM (IST). Me, Amit, Yathartha and Harsh
were the attendees of the meeting. It was a surprise having Harsh on this meeting.
In this meeting we mainly discussed about how to integrate lambert another way because
the existing method in PR <a href="https://github.com/sympy/sympy/pull/16890">#16890</a> is
not trustworthy and upto the mark. So this week I will try to implement the method
or approach told by Yathartha. Important lesson learned in this meeting was that
the method that I implement should have these following qualities to be a better
software developer:-</p>
<ul>
<li>Proof Of Correctness</li>
</ul>
<p>This is very important because I should have a plan before I get to code. Many
things already get straightened up while planning for what to implement and it
ultimately reduces coding time and let us get a more accurate and concrete code.</p>
<ul>
<li>Clean and Understandable Code</li>
</ul>
<p>This factor is also very important to write a code that is easily understandable.
It is better for us as well as it also persuade new contributors easily who are
willing to contribute.</p>
<ul>
<li>Extensible Code</li>
</ul>
<p>A well written code is always extensible. Proper techniques should be learned to
write an extensible code rather than a code that looks like patch.</p>
<p>In this week I also started a PR <a href="https://github.com/sympy/sympy/pull/16976">#16976</a>
to handle modular equations.<br />
It was a worth it meeting because I got to learn various important and necessary
qualities from all of the mentors. Harsh was also glad that we were trying to focus
on well written clean code.</p>
<p>A lot more to learn and implement :) !!</p>
GSoC 2019 - Week 12019-06-02T00:00:00+00:00https://jmig5776.github.io//gsoc-week-1<p>This was the first week meeting with the GSoC mentors which was scheduled on
Sunday 2nd June, 2019 between 11:30 AM - 12:30 PM (IST). Me, Amit and Yathartha
were the attendees of the meeting. In this meeting we mainly discussed about the
problems and implementation for my pull request <a href="https://github.com/sympy/sympy/pull/16890">#16890</a>
to complete the lambert solver.<br />
As Amit requested changes at the pull request to describe the problem in detail,
to give proof of correctness and describe the plan more briefly. So here below
are the all details for what I am trying to achieve and how I am trying to
achieve this:-</p>
<h3 id="current-implementation">Current Implementation</h3>
<p>Let me show here code from <code class="highlighter-rouge">_lambert</code> function to show the current
implementation so that I could describe the problem in detail:-</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">u</span> <span class="o">=</span> <span class="n">Dummy</span><span class="p">(</span><span class="s">'rhs'</span><span class="p">)</span>
<span class="n">sol</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c"># check only real solutions:</span>
<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">]:</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">LambertW</span><span class="p">(</span><span class="n">d</span><span class="o">/</span><span class="p">(</span><span class="n">a</span><span class="o">*</span><span class="n">b</span><span class="p">)</span><span class="o">*</span><span class="n">exp</span><span class="p">(</span><span class="n">c</span><span class="o">*</span><span class="n">d</span><span class="o">/</span><span class="n">a</span><span class="o">/</span><span class="n">b</span><span class="p">)</span><span class="o">*</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">f</span><span class="o">/</span><span class="n">a</span><span class="p">),</span> <span class="n">k</span><span class="p">)</span>
<span class="c"># if W's arg is between -1/e and 0 there is</span>
<span class="c"># a -1 branch real solution, too.</span>
<span class="k">if</span> <span class="n">k</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">l</span><span class="o">.</span><span class="n">is_real</span><span class="p">:</span>
<span class="k">continue</span>
<span class="n">rhs</span> <span class="o">=</span> <span class="o">-</span><span class="n">c</span><span class="o">/</span><span class="n">b</span> <span class="o">+</span> <span class="p">(</span><span class="n">a</span><span class="o">/</span><span class="n">d</span><span class="p">)</span><span class="o">*</span><span class="n">l</span>
<span class="n">solns</span> <span class="o">=</span> <span class="n">solve</span><span class="p">(</span><span class="n">X1</span> <span class="o">-</span> <span class="n">u</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">tmp</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">solns</span><span class="p">):</span>
<span class="n">solns</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">tmp</span><span class="o">.</span><span class="n">subs</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">rhs</span><span class="p">)</span>
<span class="n">sol</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">solns</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
<span class="k">return</span> <span class="n">sol</span>
</code></pre></div></div>
<p>Explanation of for loop:</p>
<ol>
<li><code class="highlighter-rouge">k == -1</code> :- In this case all the real solutions are considered due to <code class="highlighter-rouge">not l.is_real</code>.</li>
<li><code class="highlighter-rouge">k == 0</code> :- In this case all the solutions come out to be real as always.</li>
</ol>
<h3 id="what-solutions-are-missed">What solutions are missed?</h3>
<p>The solutions are missed due to the argument inside the <code class="highlighter-rouge">LambertW</code> function of value</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>l = LambertW(d/(a*b)*exp(c*d/a/b)*exp(-f/a), k)
</code></pre></div></div>
<p>While converting all lambert solvable equations to this form
<code class="highlighter-rouge">F(X, a..f) = a*log(b*X + c) + d*X + f = 0</code> some solutions get missed.</p>
<p>For eg:
Consider the equation <code class="highlighter-rouge">(a/x + exp(x/2)).diff(x) = 0</code> which is
<code class="highlighter-rouge">-a/x**2 + exp(x/2)/2 = 0</code>(Please take note of the <code class="highlighter-rouge">x**2</code> in denominator of a).
And we can also write this equation as <code class="highlighter-rouge">-a/(-x)**2 + exp(x/2)/2 = 0</code>.
What sympy do is to convert this equation to this form:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>F(X, a..f) = a*log(b*X + c) + d*X + f = 0
</code></pre></div></div>
<p>which will be <code class="highlighter-rouge">2*log(x) + x/2 - log(a) = 0</code> and <code class="highlighter-rouge">2*log(-x) + x/2 - log(a) = 0</code> respectively:
So solutions corresponding to both equations are:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>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
</code></pre></div></div>
<h3 id="what-type-of-lambert-type-equation-it-is-for">What type of lambert type equation it is for?</h3>
<p>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.</p>
<h3 id="whats-the-current-algorithm-to-solve-such-type-of-equation-and-whats-wrong-with-current-logic">What’s the current algorithm to solve such type of equation and what’s wrong with current logic?</h3>
<p>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.
<code class="highlighter-rouge">2*log(-x) + x/2 - log(a) = 0</code> in case of <code class="highlighter-rouge">(a/x + exp(x/2)).diff(x) = 0</code>.</p>
<h3 id="whats-the-proposed-solution">What’s the proposed solution?</h3>
<p>This problem can be solved by two methods as follows:</p>
<ul>
<li>Combining all the logarithm forms generated by the original equation(by taking abs)
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>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
</code></pre></div> </div>
<p>I think this method is not viable at this time as it will be very complex to solve
the equation invloving abs with log.</p>
</li>
<li>This method I propose to solve this problem i.e considering all solutions and
eliminating by substitution.
For example for this equation <code class="highlighter-rouge">(1/x + exp(x/2)).diff(x) = 0</code>
Possible solutions considered <code class="highlighter-rouge">[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)]</code>
Solutions after filtering from checksol <code class="highlighter-rouge">[4*LambertW(-sqrt(2)*sqrt(a)/4), 4*LambertW(sqrt(2)*sqrt(a)/4)]</code>.</li>
</ul>
<h3 id="why-is-the-proposed-solution-better-and-how-it-wont-effect-the-other-equations-no-side-effects">Why is the proposed solution better and how it won’t effect the other equations (no side effects)?</h3>
<p>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.</p>
<p>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.</p>
<h3 id="what-is-the-testing-strategy-to-verify-that-the-proposed-solution-works">What is the testing strategy to verify that the proposed solution works?</h3>
<p>Testing strategy should be to involve the cases where current implementation is
missing other logarithmic equations.</p>
<h3 id="which-examplesequations-are-we-going-to-use-to-make-sure-that-those-are-necessary-and-sufficient-for-testing-purposes">Which examples/equations are we going to use to make sure that those are necessary and sufficient for testing purposes?</h3>
<p>According to me these tests are sufficient test this strategy:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>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)}]
</code></pre></div></div>
<h3 id="some-test-cases-that-fail-due-to-some-other-reasons">Some test cases that fail due to some other reasons</h3>
<ul>
<li>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>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]
</code></pre></div> </div>
<p>These tests are failing because checksol returns false but expected to return true
Although <code class="highlighter-rouge">_lambert</code> is returning correct solutions of equation.</p>
</li>
<li>
<p><code class="highlighter-rouge">assert solve(a/x + exp(x/2), x) == [2*LambertW(-a/2), 2*LambertW(a/2)]</code>
In this case <code class="highlighter-rouge">2*LambertW(a/2)</code> which is not a solution is included because checksol returns none.</p>
</li>
<li>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>>>>solve((1/x + exp(x/2)).diff(x, 2), x)
[6*LambertW((-1)**(1/3)/3), 6*LambertW((-1)**(1/3)/3, -1)]
</code></pre></div> </div>
<p>here <code class="highlighter-rouge">(-1)**1/3</code> can have these values <code class="highlighter-rouge">-1, 1/6(1 - I*3**1/3), 1/6(1 + I*3**1/3)</code>
but sympy only take <code class="highlighter-rouge">(-1)**1/3</code> to be <code class="highlighter-rouge">1/6(1 - I*3**1/3)</code> whereas
real solutions are <code class="highlighter-rouge">[6*LambertW(-1/3), 6*LambertW(-1/3, -1)]</code></p>
</li>
</ul>
<h3 id="why-including-checksol-in-_solve_lambert-">Why including checksol in <code class="highlighter-rouge">_solve_lambert</code> ?</h3>
<p>Due to this test case:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>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))]
</code></pre></div></div>
<p>To remove the solutions which were supposed to be checked by checksol later in solve.</p>
<p>Pardon me if I missed a thing!!</p>
GSoC 2019 - Community bonding period2019-05-23T00:00:00+00:00https://jmig5776.github.io//gsoc-cbp<p>My first meeting with the GSoC mentors was on Saturday 18th May, 2019 scheduled
between 11:30 AM - 12:30 PM (IST). Me, Amit and Yathartha were the attendees of
the meeting. As per the community bonding period the first meeting was meant to
increase the familiarity between me and my mentors and we also discussed about
completing lambert solver. Major points discussed in the meeting are:-</p>
<ul>
<li>
<p>Schedule and frequency of meetings.</p>
</li>
<li>
<p>How to proceed with completing lambert, understanding existing code and
writing tests for it.</p>
</li>
</ul>
<p>As of now I have looked few things:</p>
<ul>
<li>
<p>How <code class="highlighter-rouge">lambert</code> works in <code class="highlighter-rouge">bivariate</code>.</p>
</li>
<li>
<p>How equations are converted to target equation.</p>
</li>
<li>
<p>Filtering out the tests to include all solutions.</p>
</li>
<li>
<p>What will be the flow to fix this problem.</p>
</li>
</ul>
<p>In the previous days I have been understanding the flow of <code class="highlighter-rouge">bivariate</code> along with
<code class="highlighter-rouge">solve</code> and <code class="highlighter-rouge">solveset</code>. It might take time for me to go through it and arrive
at a good plan but I am trying to learn the art of general aspects of a open
source software. I believe in understanding rather than rushing. The mentors were
very kind and helpfull.</p>
<p>Hope things go as planned!!</p>