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

Solving trignometric equations which previously gave error #13941

Merged
merged 7 commits into from Jan 23, 2018

Conversation

jashan498
Copy link
Member

Fixes #13899

Previously all trigonometric equations were solved by changing all trigino functions in their respective Euler form. This many times leads to the introduction of two variables leading to formation of multivariate polynomials leading to a PolyError.
So, i added _solve_trig2() helper function (as explained by @normalhuman in the issue) which gets called everytime the primary helper function (_solve_trig1()) cant solve the equation.
It first coverts coefficients of all angle into integer and then write all trigonometric functions in form of tan using half-angle substitution. After that tan is substituted with a 'Dummy' variable, thus transforming it into a polynomial which is at the end solved to get the solution.

Some examples which give an error in current master :
>>> x=Symbol('x')
>>> solveset(2*tan(x)*sin(x) + 1, x)

>>> x=Symbol('x')
>>> solveset(2*cos(x)*cos(2*x) - 1, x)

denominators.append(Rational(c).q)

x = Dummy('x')
f = f.subs(symbol, 2*lcm(denominators)*x)
Copy link
Member

Choose a reason for hiding this comment

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

precompute and store as LCM_den = ilcm(*denominators) (or similar) and use when needed below.


else :
raise NotImplementedError(filldedent('''
Solution to this kind of triginometric equations
Copy link
Member

Choose a reason for hiding this comment

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

trigi -> trigo

except ValueError:
raise ValueError("give up, we can't solve if this is not a polynomial in x")
if poly_ar.degree() > 1: # degree >1 still bad
raise ValueError("degree of variable inside polynomail should not exceed one")
Copy link
Member

Choose a reason for hiding this comment

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

mail -> mial

@jashan498 jashan498 changed the title Solving trigino equations which previously gave error Solving trignometric equations which previously gave error Jan 17, 2018
if poly_ar.degree() == 0: # degree 0, no x, don't care
continue
c = poly_ar.all_coeffs(x)[0] # got the coefficient of x
denominators.append(Rational(c).q)
Copy link
Member

Choose a reason for hiding this comment

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

Does it make sense to remove the gcd of the numerators, too?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think numerators would matter. We just want to change the fraction coefficient of x into an integer. If any of the coefficients of x remains fractional, then expand_trig() cant convert it into tan(x) , for doing so there is no need to do anything with numerator.
eg: m = sin(S(3)*x/2) + sin(5*x)
replacing x with 2*2*s will make the coefficients as 6*s and 20*s respectively which can be easily converted to respective tan with an integer coefficient by expand_trig(). 3 and 5 being there don't gonna affect it because they have nothing to do with coefficient being fractional.
So, I don't think there would be any role of numerator in here : )

Copy link
Member

Choose a reason for hiding this comment

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

If you rewrite the expression you suggested in terms of tan and replace the x with 4s or 8s (either of which clears the numerator) you end up with a very different set of equations to solve. By not being economical with the replacement you would be forced to solve a larger set of equations -- of higher order:

>>> [degree(i.subs(tan(s),y),y) for i in (factor(expand_trig(eq.rewrite(tan).subs(x,8*s)))).as_numer_denom()[0].args]
[0,1,1,6,6,12,12,1]

>>> [degree(i.subs(tan(s),y),y) for i in (factor(expand_trig(eq.rewrite(tan).subs(x,4*s)))).as_numer_denom()[0].args]
[0,6,12,1]

Granted, in this case, there would be no reason to use 8 instead of 4 but if the original expression were sin(S(3)*x/2) + sin(6*x) it would be better to rewrite replacing x with 4s/3 instead of 4s, wouldn't it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, you are right. It's indeed more economical. Added gcd for numerators too.

denominators.append(Rational(c).q)

x = Dummy('x')
d_lcm = lcm(denominators)
Copy link
Member

Choose a reason for hiding this comment

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

I think you will want to use the integer-optimized ilcm and igcd; then compute const = Rational(2)*d_lcm/n_gcd and use that when doing the subs at lines 474 and 489 and 491 (at least I think it has to be used in all 3 places -- if no tests fail then a test that affirms this need should be added).

@smichr
Copy link
Member

smichr commented Jan 23, 2018

I killed the test run -- let's not waste a test for whitespace alone. You didn't added a test for an expression that fails in master but not here that needs the gcd of the numerators extracted: does solveset(sin(3*x/2) + sin(6*x)) fail in master?

@jashan498
Copy link
Member Author

No, it returns a conditionset . Added commit by mistake. Can I make a new cleaner PR? A lot of commits on this one.
Also, gcd is for making the code more faster, would any test fail without it ?

@smichr
Copy link
Member

smichr commented Jan 23, 2018

would any test fail without it ?

If you make no mistake in the code, no...but you did and no test caught it because the gcd was 1 so it won't show up until someone tries an equation that doesn't have a gcd of 1. ;-) So if the equation I gave you returns a solution in this PR you can confirm (with a print statement in the code) that the gcd is not 1, remove the print statement, add the test, check locally that the test passes, and then commit it.

WITHOUT CLOSING this PR, locally just git reset head~n where n is the number of commits that you want to recommit as a single group. Then commit them like usual and then git push -f jashan498 sympy and this PR will be updated.

@jashan498
Copy link
Member Author

@smichr, I used solveset(cos(2*x)*cos(4*x) - 1) instead of solveset(sin(3*x/2) + sin(6*x)) as this gives an exception in current master and gcd comes out to be two.

@smichr
Copy link
Member

smichr commented Jan 23, 2018

+1 Looking good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants