Why does SymPy yield a "worse" solution than WolframAlpha? - python

I needed to solve $sin(x)+a*sin(3x)=0$ symbolically for $x$.
import sympy as sp
a,x = sp.symbols('a,x')
roots = sp.solve([sp.sin(x)+a*sp.sin(3*x)],[x])
print(roots)
produced
(I*(-log((-a - sqrt(-3*a**2 + 2*a + 1) - 1)/a) + log(2))/2,),
(I*(-log(-sqrt((-a + sqrt(-3*a**2 + 2*a + 1) - 1)/a)) + log(2)/2),),
(I*(-log((-a + sqrt(-3*a**2 + 2*a + 1) - 1)/a) + log(2))/2,)]
whereas WolframAlpha produced a much "better" solution:
solve sin(x) + a sin(3 x) = 0
yields
x = π n and n element Z
x = 2 π n and a = -1/3 and n element Z
x = 2 π n - 2 tan^(-1)(sqrt((5 a - 4 sqrt((a - 1) a) - 1)/(3 a + 1))) and 3 a + 1!=0 and sqrt((a - 1) a)!=2 a and n element Z
x = 2 (tan^(-1)(sqrt((5 a - 4 sqrt((a - 1) a) - 1)/(3 a + 1))) + π n) and 3 a + 1!=0 and sqrt((a - 1) a)!=2 a and n element Z
x = 2 π n - 2 tan^(-1)(sqrt((5 a + 4 sqrt((a - 1) a) - 1)/(3 a + 1))) and 3 a + 1!=0 and 2 a + sqrt((a - 1) a)!=0 and n element Z
Question:
can the SymPy solver be configured to produce solutions in the way WolframAlpha does?
Is anything known why SymPy expresses the solutions via (complex) logarithms instead of arc tangents?

Related

python algorithm for solving systems of equations without

I need algorithm, that solve systems like this:
Example 1:
5x - 6y = 0 <--- line
(10- x)**2 + (10- y)**2 = 2 <--- circle
Solution:
find y:
(10- 6/5*y)**2 + (10- y)**2 = 2
100 - 24y + 1.44y**2 + 100 - 20y + y**2 = 2
2.44y**2 - 44y + 198 = 0
D = b**2 - 4ac
D = 44*44 - 4*2.44*198 = 3.52
y[1,2] = (-b+-sqrt(D))/2a
y[1,2] = (44+-1.8761)/4.88 = 9.4008 , 8.6319
find x:
(10- x)**2 + (10- 5/6y)**2 = 2
100 - 20x + y**2 + 100 - 5/6*20y + (5/6*y)**2 = 2
1.6944x**2 - 36.6666x + 198 = 0
D = b**2 - 4ac
D = 36.6666*36.6666 - 4*1.6944*198 = 2.4747
x[1,2] = (-b+-sqrt(D))/2a
x[1,2] = (36.6666+-1.5731)/3.3888 = 11.2841 , 10.3557
my skills are not enough to write this algorithm please help
and another algorithm that solve this system.
5x - 6y = 0 <--- line
|-10 - x| + |-10 - y| = 2 <--- rhomb
as answer here i need two x and two y.
You can use sympy, Python's symbolic math library.
Solutions for fixed parameters
from sympy import symbols, Eq, solve
x, y = symbols('x y', real=True)
eq1 = Eq(5 * x - 6 * y, 0)
eq2 = Eq((10 - x) ** 2 + (10 - y) ** 2, 2)
solutions = solve([eq1, eq2], (x, y))
print(solutions)
for x, y in solutions:
print(f'{x.evalf()}, {y.evalf()}')
This leads to two solutions:
[(660/61 - 6*sqrt(22)/61, 550/61 - 5*sqrt(22)/61),
(6*sqrt(22)/61 + 660/61, 5*sqrt(22)/61 + 550/61)]
10.3583197613288, 8.63193313444070
11.2810245009662, 9.40085375080520
The other equations work very similar:
eq1 = Eq(5 * x - 6 * y, 0)
eq2 = Eq(Abs(-10 - x) + Abs(-10 - y), 2)
leading to :
[(-12, -10),
(-108/11, -90/11)]
-12.0000000000000, -10.0000000000000
-9.81818181818182, -8.18181818181818
Dealing with arbitrary parameters
For your new question, how to deal with arbitrary parameters, sympy can help to find formulas, at least when the structure of the equations is fixed:
from sympy import symbols, Eq, Abs, solve
x, y = symbols('x y', real=True)
a, b, xc, yc = symbols('a b xc yc', real=True)
r = symbols('r', real=True, positive=True)
eq1 = Eq(a * x - b * y, 0)
eq2 = Eq((xc - x) ** 2 + (yc - y) ** 2, r ** 2)
solutions = solve([eq1, eq2], (x, y))
Studying the generated solutions, some complicated expressions are repeated. Those could be substituted by auxiliary variables. Note that this step isn't necessary, but helps a lot in making sense of the solutions. Also note that substitution in sympy often only considers quite literal replacements. That's by the introduction of c below is done in two steps:
c, d = symbols('c d', real=True)
for xi, yi in solutions:
print(xi.subs(a ** 2 + b ** 2, c)
.subs(r ** 2 * a ** 2 + r ** 2 * b ** 2, c * r ** 2)
.subs(-a ** 2 * xc ** 2 + 2 * a * b * xc * yc - b ** 2 * yc ** 2 + c * r ** 2, d)
.simplify())
print(yi.subs(a ** 2 + b ** 2, c)
.subs(r ** 2 * a ** 2 + r ** 2 * b ** 2, c * r ** 2)
.subs(-a ** 2 * xc ** 2 + 2 * a * b * xc * yc - b ** 2 * yc ** 2 + c * r ** 2, d)
.simplify())
Which gives the formulas:
x1 = b*(a*yc + b*xc - sqrt(d))/c
y1 = a*(a*yc + b*xc - sqrt(d))/c
x2 = b*(a*yc + b*xc + sqrt(d))/c
y2 = a*(a*yc + b*xc + sqrt(d))/c
These formulas then can be converted to regular Python code without the need of sympy. That code will only work for an arbitrary line and circle. Some tests need to be added around, such as c == 0 (meaning the line is just a dot), and d either be zero, positive or negative.
The stand-alone code could look like:
import math
def give_solutions(a, b, xc, yc, r):
# intersection between a line a*x-b*y==0 and a circle with center (xc, yc) and radius r
c =a ** 2 + b ** 2
if c == 0:
print("degenerate line equation given")
else:
d = -a**2 * xc**2 + 2*a*b * xc*yc - b**2 * yc**2 + c * r**2
if d < 0:
print("no solutions")
elif d == 0:
print("1 solution:")
print(f" x1 = {b*(a*yc + b*xc)/c}")
print(f" y1 = {a*(a*yc + b*xc)/c}")
else: # d > 0
print("2 solutions:")
sqrt_d = math.sqrt(d)
print(f" x1 = {b*(a*yc + b*xc - sqrt_d)/c}")
print(f" y1 = {a*(a*yc + b*xc - sqrt_d)/c}")
print(f" x2 = {b*(a*yc + b*xc + sqrt_d)/c}")
print(f" y2 = {a*(a*yc + b*xc + sqrt_d)/c}")
For the rhombus, sympy doesn't seem to be able to work well with abs in the equations. However, you could use equations for the 4 sides, and test whether the obtained intersections are inside the range of the rhombus. (The four sides would be obtained by replacing abs with either + or -, giving four combinations.)
Working this out further, is far beyond the reach of a typical stackoverflow answer, especially as you seem to ask for an even more general solution.

Shifted Chebyshev polynomials in Python

I am trying to extract the coefficients of a shifted Chebyshev Polynomial in Python, but I couldn't find the function to do that.
There is this function:
scipy.special.eval_sh_chebyt
scipy.special.eval_sh_chebyt(n, x, out=None) = <ufunc 'eval_sh_chebyt'>
but I can't extract only the coefficients.
The shifted Chebyshev polynomials are:
T^{*}_n(x) = T_n (2x -1)
and then:
T_1^{*}(x) = 2x - 1
T_2^{*}(x) = 8x^2 - 8x -1
I would like to extract a matrix only with the coefficients, like 2 and 1 or 8,8 and 1.
scipy.special.eval_sh_chebyt evaluates the function T* at a given point x. Unless you are very smart in choosing your arguments, this is not an appropriate way to get the coeffcients.
You can instead calculate the coefficients directly from the recurrence formula T0 = 1, T1 = x, T{n} = 2xT{n-1} - T{n-2}. And then calculate the shifted polynomials from _T*{n} = T{n}(2x - 1).
degree = 10
coeffs = []
# for T_0
coeffLine = [1]
coeffs.append(coeffLine)
# for T_1
coeffLine = [0, 1]
coeffs.append(coeffLine)
for i in range(2, degree + 1):
coeffLine = [0] * (1 + i)
coeffLine[0] = -coeffs[i - 2][0]
for j in range(1, i - 1):
coeffLine[j] = 2 * coeffs[i - 1][j - 1] - coeffs[i - 2][j]
coeffLine[-2] = 2 * coeffs[i - 1][-2]
coeffLine[-1] = 2 * coeffs[i - 1][-1]
coeffs.append(coeffLine)
print("T_%d" % degree, coeffs[-1])
shiftedCoeffs = [0] * (degree + 1);
for i in range(degree + 1):
binom = 1
for j in range(i + 1):
shiftedCoeffs[i - j] += coeffs[-1][i] * 2 ** (i - j) * (-1) ** j * binom
binom *= (i - j) / (j + 1)
print("T*_%d" % degree, shiftedCoeffs)
Edit: I originally misread the formula for T*{n} as _T{n}(x) * (2x - 1). However it is T*{n} = T{n}(2x - 1). We therefore need to calculate the binomial coefficients sum them up depending on the order of x.

Dropping lower order terms of a polynomial with Sympy

Say we have this function,
f = poly(2*x**2 + 3*x - 1,x)
How would one go about dropping terms of degree n or lower.
For instance if n = 1 the result would be 2*x**2.
from sympy import poly
from sympy.abc import x
p = poly(x ** 5 + 2 * x ** 4 - x ** 3 - 2 * x ** 2 + x)
print(p)
n = 2
new_p = poly(sum(c * x ** i[0] for i, c in p.terms() if i[0] > n))
print(new_p)
Output:
Poly(x**5 + 2*x**4 - x**3 - 2*x**2 + x, x, domain='ZZ')
Poly(x**5 + 2*x**4 - x**3, x, domain='ZZ')

Pythonic Way to Solve this Matrix

I've been thinking on this problem, but I can't seem to wrap my head around it.
I want to solve a matrix with three equations with unknowns x, y, z so they all equal the same number.
Lets say my equations are:
x + 3 = A
y(2y - 2) = 2A
z(4z - 1) = A
So I can construct a matrix looking like:
[(X + 3) , 0 , 0] [0] [A]
[ 0 ,(2y - 2), 0] [y] = [2A]
[ 0 , , 0, (4z -1)] [z] [A]
I know numpy has a linear algebra but that is only when the answer (A) is already known.
My question is, would I have to construct a loop to brute force the answer of (A) or is there a more pythonic way of answering these series of equations?
Linear algebra can only solve for multiples of your variables, not powers (that is why it is called linear, ie the equation for a straight line, Ax + By + Cz = 0).
For this set of equations you can use the quadratic formula to solve in terms of a:
x + 3 = a => x = a - 3
y * (y - 1) = a => y**2 - y - a = 0
y = (1 +/- (1 + 4*a) ** 0.5) / 2
= 0.5 +/- (0.25 + a) ** 0.5
(a >= -0.25 for real roots)
z * (4*z - 1) = a => 4 * z**2 - z - a = 0
z = (1 +/- (1 + 16*a) ** 0.5) / 8
= 0.125 +/- (0.015625 + 0.25*a) ** 0.5
(a >= -0.0625 for real roots)
then
def solve(a):
assert a >= -0.625, "No real solution"
x = a - 3
yoffs = (0.25 * a) ** 0.5
ylo = 0.5 - yoffs
yhi = 0.5 + yoffs
zoffs = (0.015625 + 0.25 * a) ** 0.5
zlo = 0.125 - zoffs
zhi = 0.125 + zoffs
return [
(x, ylo, zlo),
(x, ylo, zhi),
(x, yhi, zlo),
(x, yhi, zhi)
]
You do not have a system of 3 equations with 3 unknowns. You have a system of 3 equations with 4 unknowns: x, y, z and A.
That means your answer will be parameterized on A, because you do not have enough equations to solve for all unknowns.
Solving a general system of polynomial equations can be done by the so-called Groebner basis approach, which is what sympy uses. Here is a snippet on how to use the library to solve this or similar problems:
from sympy.solvers.polysys import solve_poly_system
from sympy.abc import x, y, z, A
f1 = x + 3 - A
f2 = y * (2 * y - 2) - 2 * A
f3 = z * (4 * z - 1) - A
solve_poly_system([f1, f2, f3], x, y, z)
# Outputs:
# [(A - 3, -sqrt(4*A + 1)/2 + 1/2, -sqrt(16*A + 1)/8 + 1/8),
# (A - 3, -sqrt(4*A + 1)/2 + 1/2, sqrt(16*A + 1)/8 + 1/8),
# (A - 3, sqrt(4*A + 1)/2 + 1/2, -sqrt(16*A + 1)/8 + 1/8),
# (A - 3, sqrt(4*A + 1)/2 + 1/2, sqrt(16*A + 1)/8 + 1/8)]
As you can see, the result requires to fix the value of A to be fully determined.

How to set up and solve simultaneous equations in python

For a fixed integer n, I have a set of 2(n-1) simultaneous equations as follows.
M(p) = 1+((n-p-1)/n)*M(n-1) + (2/n)*N(p-1) + ((p-1)/n)*M(p-1)
N(p) = 1+((n-p-1)/n)*M(n-1) + (p/n)*N(p-1)
M(1) = 1+((n-2)/n)*M(n-1) + (2/n)*N(0)
N(0) = 1+((n-1)/n)*M(n-1)
M(p) is defined for 1 <= p <= n-1. N(p) is defined for 0 <= p <= n-2. Notice also that p is just a constant integer in every equation so the whole system is linear.
I have been using Maple but I would like to set these up and solve them in python now, maybe using numpy.linalg.solve (or any other better method). I actually only want the value of M(n-1). For example, when n=2 the answer should be M(1) = 4, I believe. This is because the equations become
M(1) = 1+(2/2)*N(0)
N(0) = 1 + (1/2)*M(1)
Therefore
M(1)/2 = 1+1
and so
M(1) = 4.
If I want to plug in n=50, say, how can you set up this system of simultaneous equations in python so that numpy.linalg.solve can solve them?
Update The answers are great but they use dense solvers where the system of equations is sparse. Posted follow up to Using scipy sparse matrices to solve system of equations .
Updated: added implementation using scipy.sparse
This gives the solution in the order N_max,...,N_0,M_max,...,M_1.
The linear system to solve is of the shape A dot x == const 1-vector.
x is the sought after solution vector.
Here I ordered the equations such that x is N_max,...,N_0,M_max,...,M_1.
Then I build up the A-coefficient matrix from 4 block matrices.
Here's a snapshot for the example case n=50 showing how you can derive the coefficient matrix and understand the block structure. The coefficient matrix A is light blue, the constant right side is orange. The sought after solution vector x is here light green and used to label the columns. The first column show from which of the above given eqs. the row (= eq.) has been derived:
As Jaime suggested, multiplying by n improves the code. This is not reflected in the spreadsheet above but has been implemented in the code below:
Implementation using numpy:
import numpy as np
import numpy.linalg as linalg
def solve(n):
# upper left block
n_to_M = -2. * np.eye(n-1)
# lower left block
n_to_N = (n * np.eye(n-1)) - np.diag(np.arange(n-2, 0, -1), 1)
# upper right block
m_to_M = n_to_N.copy()
m_to_M[1:, 0] = -np.arange(1, n-1)
# lower right block
m_to_N = np.zeros((n-1, n-1))
m_to_N[:,0] = -np.arange(1,n)
# build A, combine all blocks
coeff_mat = np.hstack(
(np.vstack((n_to_M, n_to_N)),
np.vstack((m_to_M, m_to_N))))
# const vector, right side of eq.
const = n * np.ones((2 * (n-1),1))
return linalg.solve(coeff_mat, const)
Solution using scipy.sparse:
from scipy.sparse import spdiags, lil_matrix, vstack, hstack
from scipy.sparse.linalg import spsolve
import numpy as np
def solve(n):
nrange = np.arange(n)
diag = np.ones(n-1)
# upper left block
n_to_M = spdiags(-2. * diag, 0, n-1, n-1)
# lower left block
n_to_N = spdiags([n * diag, -nrange[-1:0:-1]], [0, 1], n-1, n-1)
# upper right block
m_to_M = lil_matrix(n_to_N)
m_to_M[1:, 0] = -nrange[1:-1].reshape((n-2, 1))
# lower right block
m_to_N = lil_matrix((n-1, n-1))
m_to_N[:, 0] = -nrange[1:].reshape((n-1, 1))
# build A, combine all blocks
coeff_mat = hstack(
(vstack((n_to_M, n_to_N)),
vstack((m_to_M, m_to_N))))
# const vector, right side of eq.
const = n * np.ones((2 * (n-1),1))
return spsolve(coeff_mat.tocsr(), const).reshape((-1,1))
Example for n=4:
[[ 7.25 ]
[ 7.76315789]
[ 8.10526316]
[ 9.47368421] # <<< your result
[ 9.69736842]
[ 9.78947368]]
Example for n=10:
[[ 24.778976 ]
[ 25.85117842]
[ 26.65015984]
[ 27.26010007]
[ 27.73593401]
[ 28.11441922]
[ 28.42073207]
[ 28.67249606]
[ 28.88229939]
[ 30.98033266] # <<< your result
[ 31.28067182]
[ 31.44628982]
[ 31.53365219]
[ 31.57506477]
[ 31.58936225]
[ 31.58770694]
[ 31.57680467]
[ 31.560726 ]]
Here's an entirely different approach, using sympy. It's not fast, but it allows me to copy the RHS of your equations exactly, limiting the thinking I need to do (always a plus), and gives fractional answers.
from sympy import Integer, Symbol, Eq, solve
def build_equations(n):
ni = n
n = Integer(n)
Ms = {p: Symbol("M{}".format(p)) for p in range(ni)}
Ns = {p: Symbol("N{}".format(p)) for p in range(ni-1)}
M = lambda i: Ms[int(i)] if i >= 1 else 0
N = lambda i: Ns[int(i)]
M_eqs = {}
M_eqs[1] = Eq(M(1), 1+((n-2)/n)*M(n-1) + (2/n)*N(0))
for p in range(2, ni):
M_eqs[p] = Eq(M(p), 1+((n-p-1)/n)*M(n-1) + (2/n)*N(p-1) + ((p-1)/n)*M(p-1))
N_eqs = {}
N_eqs[0] = Eq(N(0), 1+((n-1)/n)*M(n-1))
for p in range(1, ni-1):
N_eqs[p] = Eq(N(p), 1+((n-p-1)/n)*M(n-1) + (p/n)*N(p-1))
return M_eqs.values() + N_eqs.values()
def solve_system(n, show=False):
eqs = build_equations(n)
sol = solve(eqs)
if show:
print 'equations:'
for eq in sorted(eqs):
print eq
print 'solution:'
for var, val in sorted(sol.items()):
print var, val, float(val)
return sol
which gives
>>> solve_system(2, True)
equations:
M1 == N0 + 1
N0 == M1/2 + 1
solution:
M1 4 4.0
N0 3 3.0
{M1: 4, N0: 3}
>>> solve_system(3, True)
equations:
M1 == M2/3 + 2*N0/3 + 1
M2 == M1/3 + 2*N1/3 + 1
N0 == 2*M2/3 + 1
N1 == M2/3 + N0/3 + 1
solution:
M1 34/5 6.8
M2 33/5 6.6
N0 27/5 5.4
N1 5 5.0
{M2: 33/5, M1: 34/5, N1: 5, N0: 27/5}
and
>>> solve_system(4, True)
equations:
M1 == M3/2 + N0/2 + 1
M2 == M1/4 + M3/4 + N1/2 + 1
M3 == M2/2 + N2/2 + 1
N0 == 3*M3/4 + 1
N1 == M3/2 + N0/4 + 1
N2 == M3/4 + N1/2 + 1
solution:
M1 186/19 9.78947368421
M2 737/76 9.69736842105
M3 180/19 9.47368421053
N0 154/19 8.10526315789
N1 295/38 7.76315789474
N2 29/4 7.25
{N2: 29/4, N1: 295/38, M1: 186/19, M3: 180/19, N0: 154/19, M2: 737/76}
which seems to match the other answers.
This is messy, but solves your problem, barring a very probable mistake transcribing the coefficients:
from __future__ import division
import numpy as np
n = 2
# Solution vector is [N[0], N[1], ..., N[n - 2], M[1], M[2], ..., M[n - 1]]
n_pos = lambda p : p
m_pos = lambda p : p + n - 2
A = np.zeros((2 * (n - 1), 2 * (n - 1)))
# p = 0
# N[0] + (1 - n) / n * M[n-1] = 1
A[n_pos(0), n_pos(0)] = 1 # N[0]
A[n_pos(0), m_pos(n - 1)] = (1 - n) / n #M[n - 1]
for p in xrange(1, n - 1) :
# M[p] + (1 + p - n) /n * M[n - 1] - 2 / n * N[p - 1] +
# (1 - p) / n * M[p - 1] = 1
A[m_pos(p), m_pos(p)] = 1 # M[p]
A[m_pos(p), m_pos(n - 1)] = (1 + p - n) / n # M[n - 1]
A[m_pos(p), n_pos(p - 1)] = -2 / n # N[p - 1]
if p > 1 :
A[m_pos(p), m_pos(p - 1)] = (1 - p) / n # M[p - 1]
# N[p] + (1 + p -n) / n * M[n - 1] - p / n * N[p - 1] = 1
A[n_pos(p), n_pos(p)] = 1 # N[p]
A[n_pos(p), m_pos(n - 1)] = (1 + p - n) / n # M[n - 1]
A[n_pos(p), n_pos(p - 1)] = -p / n # N[p - 1]
if n > 2 :
# p = n - 1
# M[n - 1] - 2 / n * N[n - 2] + (2 - n) / n * M[n - 2] = 1
A[m_pos(n - 1), m_pos(n - 1)] = 1 # M[n - 1]
A[m_pos(n - 1), n_pos(n - 2)] = -2 / n # N[n - 2]
A[m_pos(n - 1), m_pos(n - 2)] = (2 - n) / n # M[n - 2]
else :
# p = 1
#M[1] - 2 / n * N[0] = 1
A[m_pos(n - 1), m_pos(n - 1)] = 1
A[m_pos(n - 1), n_pos(n - 2)] = -2 / n
X = np.linalg.solve(A, np.ones((2 * (n - 1),)))
But it gives a solution of
>>> X[-1]
6.5999999999999979
for M(2) when n=3, which is not what you came up with.

Categories

Resources