I am trying to solve a system of polynomial equations obtained by comparing coefficients of different polynomials.
# Statement of Problem:
# We are attempting to find complex numbers a, b, c, d, e, J, u, v, r, s where
# ((a*x + c)^2)*(x^3 + (3K)*x + 2K) - ((b*x^2 + d*x + e)^2) = a^2*(x - r)^2*(x - s)^3 and
# ((a*x + c)^2)*(x^3 + (3K)*x + 2K)) - ((b*x^2 + d*x + e - 1)^2) = a^2*(x - u)*(x - v)^4
R.<x> = CC['x']
a, b, c, d, e, r, s, u, v, K = var('a, b, c, d, e, r, s, u, v, K')
y2 = x^3 + (3*K)*x + 2*K
q0 = ((a*x + c)^2)*(y2) - ((b*x^2 + d*x + e)^2)
p0 = (a^2)*((x-r)^2)*((x-s)^3)
t = (b^2 - 2*a*c)/a^2
Q0 = q0.expand()
P0 = p0.expand()
P0 = P0.substitute(s = ((t - 2*r)/3))
Relations0 = []
i = 0
while i < 6:
Relations0.append(P0.coefficient(x, n = i) - Q0.coefficient(x, n = i))
i = i+1
q1 = ((a*x + c)^2)*(y2) - ((b*x^2 + d*x + e - 1)^2)
p1 = (a^2)*(x-u)*((x-v)^4)
Q1 = q1.expand()
P1 = p1.expand()
P1 = P1.substitute(u = t - 4*v)
Relations1 = []
i = 0
while i < 6:
Relations1.append(P1.coefficient(x, n = i) - Q1.coefficient(x, n = i))
i = i+1
Relations = Relations0 + Relations1
Telling Sage to solve the system of polynomials using solve(Relations, a,b,c,d,e,r,v,K) seems highly inefficient and has only led to Sage having its memory limit exceeded. Moreover, trying to reduce the number of equations and variables by solving for some of the variables is also inefficient and has not given any fruitful results. Since attempting to find all solutions has proven so difficult, is there any way to extract only a single solution?
Both equations have degree 5, which makes 12 identities. However, the degree 5 identities are identical and always satisfied for both equations. Thus you have effectively 10 or less equations for 10 variables.
Divide by a^2, i.e., replace c, b, d, e by c/a, b/a, d/a, e/a and introduce f=1/a to reduce the degrees of the coefficient equations.
Then the resulting coefficient equations for
(x + c)^2*(x^3 + 3*K*x + 2*K) - (b*x^2 + d*x + e)^2 = (x - r)^2*(x - s)^3;
(x + c)^2*(x^3 + 3*K*x + 2*K) - (b*x^2 + d*x + e - f)^2 = (x - u)*(x - v)^4;
or on http://magma.maths.usyd.edu.au/calc/
A<b, c, d, e, f, r, s, u, v, K> :=PolynomialRing(Rationals(),10,"glex");
P<x> := PolynomialRing(A);
eq1 := (x + c)^2*(x^3 + 3*K*x + 2*K) - (b*x^2 + d*x + e)^2 - (x - r)^2*(x - s)^3;
eq2 := (x + c)^2*(x^3 + 3*K*x + 2*K) - (b*x^2 + d*x + e - f)^2 - (x - u)*(x - v)^4;
I := ideal<A|Coefficients(eq1) cat Coefficients(eq2-eq1)>; I;
giving
Ideal of Polynomial ring of rank 10 over Rational Field
Order: Graded Lexicographical
Variables: b, c, d, e, f, r, s, u, v, K
Basis:
[
r^2*s^3 + 2*c^2*K - e^2,
-3*r^2*s^2 - 2*r*s^3 + 3*c^2*K + 4*c*K - 2*d*e,
3*r^2*s + 6*r*s^2 + s^3 - 2*b*e + 6*c*K - d^2 + 2*K,
-2*b*d + c^2 - r^2 - 6*r*s - 3*s^2 + 3*K,
-b^2 + 2*c + 2*r + 3*s,
-r^2*s^3 + u*v^4 + 2*e*f - f^2,
3*r^2*s^2 + 2*r*s^3 - 4*u*v^3 - v^4 + 2*d*f,
-3*r^2*s - 6*r*s^2 - s^3 + 6*u*v^2 + 4*v^3 + 2*b*f,
r^2 + 6*r*s + 3*s^2 - 4*u*v - 6*v^2,
-2*r - 3*s + u + 4*v
]
have degrees 5,4,3,2,2,5,4,3,2,1 giving an upper bound of 28800 for the number of solutions. As the Groebner basis algorithms that are commonly used have a complexity bound of O(d^(n^2)) for the better algorithms, your runtime will be optimistically be characterized by the number 28800^10 (Bezout bound instead of d^n in (d^n)^n), which is rather large however small the constant. Even removing one variable for the linear equation will not change much in these estimates.
Thus any symbolic solution will take a long time and result in univariate polynomials of rather high degrees as part of any triangular polynomial basis.
Related
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?
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.
I am trying to compute the final composition and temperature of a mixture of syn-gas and air. They enter in at 300 K and 600 K respectively. The syn-gas is a mixture of CO and H2 the proportions of which I am varying from 3:1 to 1:3. Later on, this ratio is fixed and additional nitrogen gas is introduced. Lastly, heat loss is accounted for and its effect on temperature/composition is calculated. Focusing on the first part, I am having a hard time balancing the system of non-linear equations. The general equation for the chemical reaction is as follows:
aCO + bH2 + c*(O2 + 79/21 * N2) + dN2 = eCO + fH2 + gO2 + hN2 + jCO2 + kH2O + lNO
From conservation of species:
Carbon: a = e + j
Oxygen: a + 2c = e + 2g + 2*j + k + l
Hydrogen: 2b = 2f + 2*k
Nitrogen: 2*(79/21)c + 2d = 2*h + l
Since there are three compounds, there are three partial pressure equilibrium values called K_p. K_p is a function of temperature and from empirical data is a known constant.
K_p_NO = (X_NO * P) / (sqrt(X_N2*P)*sqrt(X_O2 * P))
K_p_H2O = (X_H2O * P) / (sqrt(P*X_O2)X_H2P)
K_p_CO2 = (X_CO2 * P) / (sqrt(P*X_O2)X_COP)
Where X is the mole fraction. E.G. X_H2O = k/(e+f+g+h+j+k+l)
The variables e,f,g,h,j,k,l are 7 unknowns and there are 7 equations. Variables a, b, c, and d are varied manually and are treated as known values. Using scipy, I implemented fsolve() as shown below:
from scipy.optimize import fsolve # required library
import sympy as sp
import scipy
# known values hard coded for testing
a = 0.25
b = 0.75
c = a + b
d = 0
kp_NO = 0.00051621
kp_H2O = 0.0000000127
kp_CO2 = 0.00000001733
p = 5 # pressure
dec = 10 # decimal point precision
# Solving the system of equations
def equations(vars):
(e, f, g, h, j, k, l) = vars
f1 = e + j - a
f2 = e + 2*g + 2*j + k + l - a - 2*c
f3 = f + k - b
f4 = 2*h + l - 2*d - (2*79/21)*c
f5 = kp_NO - (l/sp.sqrt(c*(79/21)*c))
f6 = kp_H2O - (k/(b*sp.sqrt((c*p)/(e + f + g + h + j + k + l))))
f7 = kp_CO2 - (j/(a*sp.sqrt((c*p)/(e + f + g + h + j + k + l))))
return[f1, f2, f3, f4, f5, f6, f7]
e, f, g, h, j, k, l = scipy.optimize.fsolve(equations, (0.00004, 0.00004, 0.49, 3.76, 0.25, 0.75, 0.01))
# CO, H2, O2, N2, CO2, H2O, NO
print(e, f, g, h, j, k, l)
The results are
0.2499999959640893 0.7499999911270915 0.999499382628763 3.761404150987935 4.0359107126181326e-09 8.872908576472292e-09 0.001001221833654118
Notice that e = 0.24999995 but a = 0.25. It seems that the chemical reaction is progressing little if at all. Why am I getting my inputs back as results? I know something is wrong because in some cases, the chemical coefficients are negative.
Things I've tried:
Triple checked my math/definitions. Used nsolve() from sympy, nonlinsolve() from scipy, other misc. solvers.
I do not see anything wrong in your code, albeit I would have solved it differently.
This question should be moved to Chemistry stackexchange: the Kp values you are using are wrong. The value for water formation at 500K is around 7.65E22 (when pressure is expresed in bar), I am pretty sure that the constant for CO to CO2 is also much higher.
I would have written this as a comment but I do not have enough reputation.
I have an expression that comes from
from sympy.physics.quantum import Commutator as cmm
x, t, A, V, W, D = sp.symbols('x t A V W D', commutative = False)
Q = sp.Function('Q', commutative = False)
F = (sp.diff(Q(x,t),x)+ cmm(W,Q(x,t)).doit() - sp.I*cmm(A,Q(x,t)+ cmm(W,Q(x,t)).doit()).doit())*(sp.diff(Q(x,t),x)+ cmm(W,Q(x,t)).doit() - sp.I*cmm(A,Q(x,t)+ cmm(W,Q(x,t)).doit()).doit())
F.expand()
This gives me an expression with elements with zero order in W, with first order in W and second order. I want to get only the first order W's. I tried the factorization procedures but it appears that, due to the fact of no commutation, it doesn't recognize powers of W. It always gives me 0. Is there any simple way of doing this ? Of course I can do it by hand but this is not my goal.
Thanks
You can get all the terms that are first order in W by collecting them as you traverse the arguments of F:
>>> from sympy import Add
>>> first_order_terms = []
>>> for i in Add.make_args(F.expand()):
... if i == W or i.is_Mul and i.has(W) and i.subs(W,y).as_independent(y)[1] == y:
... first_order_terms.append(i)
...
>>> Add(*first_order_terms)
-A*W*Q(x, t)*A*Q(x, t) - I*A*W*Q(x, t)*Derivative(Q(x, t), x) + A*W*Q(x, t)**2*A -
A*Q(x, t)*A*W*Q(x, t) + A*Q(x, t)*A*Q(x, t)*W + A*Q(x, t)*W*A*Q(x, t) - I*A*Q(x,
t)*W*Q(x, t) + I*A*Q(x, t)*W*Derivative(Q(x, t), x) + I*A*Q(x, t)**2*W - A*Q(x,
t)**2*W*A - I*W*Q(x, t)*A*Q(x, t) - W*Q(x, t)*A*Q(x, t)*A + I*W*Q(x,
t)*A*Derivative(Q(x, t), x) + W*Q(x, t)*A**2*Q(x, t) + W*Q(x, t)*Derivative(Q(x, t),
x) + I*W*Q(x, t)**2*A + I*Q(x, t)*A*W*Q(x, t) - Q(x, t)*A*W*Q(x, t)*A - I*Q(x,
t)*A*Q(x, t)*W + Q(x, t)*A*Q(x, t)*W*A + Q(x, t)*A**2*W*Q(x, t) - Q(x, t)*A**2*Q(x,
t)*W + I*Q(x, t)*W*A*Q(x, t) + Q(x, t)*W*A*Q(x, t)*A - I*Q(x, t)*W*A*Derivative(Q(x,
t), x) - Q(x, t)*W*A**2*Q(x, t) - I*Q(x, t)*W*Q(x, t)*A - Q(x, t)*W*Derivative(Q(x,
t), x) - I*Derivative(Q(x, t), x)*A*W*Q(x, t) + I*Derivative(Q(x, t), x)*A*Q(x, t)*W +
Derivative(Q(x, t), x)*W*Q(x, t) + I*Derivative(Q(x, t), x)*W*Q(x, t)*A -
Derivative(Q(x, t), x)*Q(x, t)*W - I*Derivative(Q(x, t), x)*Q(x, t)*W*A
A more compact way to do this, after expanding F, is to do as follows:
>>> Add(*[i for i in F.expand().atoms(Mul) if i.xreplace({W:z}).coeff(z) != 0])
coeff will only return a non-zero value if z (in this case) appears as a linear factor. Of course, this does not work if you just replace W with z in F because terms which didn't cancel because of their distince non-commutative factors will do so after the substitution so you will not get the same result.
I want to shorten a long equation with shorter expressions.
Here is a simple example:
from sympy.abc import z, h, m, G
y = 0.2 * m + 0.2 * z * m +z - h
y_1 = y.subs({m + z * m: G})
print(y_1)
Expected result is z - h + 0.2 * G but it doesn't replace the expression. I know the problem is 0.2. Is there any way to fix this automatically?
OR another solution can be :
Common subexpression elimination (cse(y)), which is not efficient as it works with default conditions to create sub-expressions.
Unfortunately, as far as I know, subs just isn't as powerful (yet?). You can try to automate splitting your substitution m + z * m: G into two separate substitutions z: G / m - 1 and m: G / (z + 1) and simplify in between.
from sympy.abc import z, h, m, G
y = 0.2 * m + z - h + 0.2 * z * m
y_1 = y.subs({m + z * m: G})
print(y_1)
y_2 = y.subs({z: G / m - 1, m: G / (z + 1)}).simplify()
print(y_2)
y_3 = y_2.subs({G / m - 1: z, G / (z + 1): m}).simplify()
print(y_3)
Which has the following output:
-h + 0.2*m*z + 0.2*m + z
0.2*G + G/m - h - 1
0.2*G - h + z
Update: When I said that you can try to automate the process, I meant something like the following code example. I do not know how much this is applicable to your situation.
from sympy import *
from sympy.abc import z, h, m, G
def elaborate_subs(expr, old, new):
subs = {}
for symbol in old.free_symbols:
solution = solve(Eq(old, new), symbol, simplify=False)
if solution:
subs[symbol] = solution[0]
expr = expr.subs(subs).simplify()
expr = expr.subs({v: k for k, v in subs.items()}).simplify()
return expr
y = 0.2 * m + z - h + 0.2 * z * m
print(elaborate_subs(y, m + z * m, G))
Which has the expected output:
0.2*G - h + z
I don't think the subs method works the way you think it does. It simply replaces a term with an input value that is passed. For example,
expr = cos(x)
expr.subs(x, 0)#here 0 is replaced with x
print(expr) # 1, sinces cos(0) is 1
#Or
expr = x**3 + 4*x*y - z
expr.subs([(x, 2), (y, 4), (z, 0)])
print(expr) #40
As you can see, in your subs method you are not telling exactly what should be replaced. Follow the examples above as a guideline and it should work. You can read more here