[英]Using symbols in solutions of a system of differential equations with sympy (python)
[英]Complete solutions of system of equations with python
我有以下方程組,我試圖用 sympy 得到解決方案:
from sympy import *
x,y = symbols('x,y')
rea1 = (1.0*10**(-4)*(x+2)*(4*y+3*x+1)**3) - 1.3 * ((-2*y-x+1)*(-y-x+1)*(2*y+2*x+6)**2)
rea2 = (1.0*10**(-4)*(y+1)*(4*y+3*x+1)**4) - 2.99 * ((-2*y-x+1)**2*(-y-x+1)*(2*y+2*x+6)**2)
solu = solve([rea1,rea2],[x,y])
sol = nsolve([rea1,rea2],[x,y],[-0.1,1])
print(solu)
print(sol)
結果
[(-11.0, 8.0), (-3.0, 2.0), (-2.0, -1.0), (5.0, -4.0)]
和
Matrix([[1.32358494772278], [-0.324195048403443]])
但是我從 Maxima 知道以下解決方案:
[[x=-11,y=8],
[x=-3,y=2],
[x=5,y=-4],
[x=-2,y=-1],
[x=1.3236,y=-0.3242],
[x=-2.0091,y=-0.98836],
[x=-3.8143,y=0.84582],
[x=3.004,y=-1.0016],
[x=-4.0297,y=0.9959],
[x=-8.4744,y=9.4724]]
如何使用 Python 獲得所有解決方案?
對於具有多個解的非線性系統,訣竅是在解附近進行猜測。 所以nsolve((rea1,rea2),(x,y),(1.3,-0.3))
會給你一個解決方案。 但如果你不知道那里有解決方案,你怎么能得到呢? 一種方法是使用“連續參數”,您可以將其值從 0 更改為 1,以“關閉”方程的高度非線性部分。 在您的情況下,我將4*y+3*x+1
替換為z*(4*y+3*x+1)
並求解z=0
>>> from sympy import Tuple,nsimplify
>>> eqs = Tuple(*[nsimplify(i) for i in (rea1,rea2)]) # get rid of floats
>>> eqs = eqs.subs(4*y+3*x+1, z*(4*y+3*x+1)) # introduce continuation param
>>> z0 = solve(eqs.subs(z,0)); z0
[{x:1−2y}, {x:1−y}, {x:−y−3}]
有了這三個關於x
與y
的關系的提示,我就完全打開了非線性項,並希望我在計算y
感興趣值的x
值時足夠接近——為此你必須有對您要查找的內容有所了解。 我將假設我們正在查看 [-11,10] 的范圍並收集獲得的答案(並忽略失敗的嘗試):
>>> saw = set()
>>> for yg in range(-11,11):
... for xs in z0:
... guess = xs[x].subs(y,yg),yg
... try:
... ans = nsolve(eqs.subs(z,1),(x,y),guess)
... except:continue
... saw.add((ans[0,0],ans[1,0]))
讓我們看看我們得到了什么:
>>> from sympy import Point
>>> for i in sorted({Point(i).n(3) for i in saw},key=lambda x:x[0]):print(i)
Point2D(-11.0, 8.0)
Point2D(-8.47, 9.47)
Point2D(-4.03, 0.996)
Point2D(-3.81, 0.846)
Point2D(-3.0, 2.0)
Point2D(-2.0, -1.0)
Point2D(1.32, -0.324)
Point2D(3.0, -1.0)
Point2D(5.0, -4.0)
由於原始解決方案略有不同,其中 2 個解決方案似乎是重復的; 通過評估到 3 位精度來刪除它們。 您必須調查 (-2,-1) 附近是否有不同的解決方案。 但是使用已經找到的解決方案附近的猜測將是確認這一點的一種方法。
好的,這是第二個答案。 我想我知道為什么 SymPy 的主要求解器似乎不適用於這種情況。 我已經編寫了一些更好的代碼來執行此操作,並將其發布在 SymPy 問題中: https ://github.com/sympy/sympy/issues/23637#issuecomment-1159205521
我也會在這里展示它:
from sympy import *
def solve_poly_numeric(polys, syms, exact=False, prec=None):
"""Solve a system of polynomials having rational coefficients."""
_, info = parallel_poly_from_expr(polys)
domain = info['domain']
if domain not in (ZZ, QQ):
raise ValueError("Poly should have rational coefficients")
# Compute a preliminary Groebner basis
gb = groebner(polys, syms)
# Handle inconsistent or infinite cases
if 1 in gb:
return []
elif not gb.is_zero_dimensional:
raise ValueError("Infinitely many solutions")
# Split the system by factorising the final polynomial
c, fms = factor_list(gb[-1])
gbs = []
for factor, m in fms:
gb_new = groebner(gb[:-1] + [factor], syms)
gbs.append(gb_new)
# Now solve each subsystem
solutions = []
for gbi in gbs:
solutions.extend(solve_separating(gbi))
# Make the solutions approximate (this is because otherwise you'll see
# complicated RootOf expressions).
if not exact:
solutions = [[s.evalf(prec) for s in sol] for sol in solutions]
return solutions
def solve_separating(gb):
syms = gb.gens
N = len(syms)
s = Dummy('s')
i = 0
while True:
eq_s = s - sum(j**i*syms[j] for j in range(N))
gb = groebner(list(gb) + [eq_s], syms + (s,))
if is_separated(gb):
return solve_rur(gb)
i += 1
def is_separated(gb):
"""Test if a Groebner basis is separated"""
for p in gb.polys[:-1]:
if sum(p.degree_list()[:-1]) != 1:
return False
return sum(gb.polys[-1].degree_list()[:-1]) == 0
def solve_rur(gb):
[sol] = linsolve(gb[:-1], gb.gens[:-1])
s = gb.gens[-1]
s_sols = set(gb.polys[-1].as_poly(s).all_roots())
return [sol.subs(s, s_sol) for s_sol in s_sols]
這樣你就得到了你所期待的所有根源:
In [100]: solve_poly_numeric([nsimplify(rea1), nsimplify(rea2)], [x, y])
Out[100]:
[[-2.0, -1.0], [5.0, -4.0], [-11.0, 8.0], [-3.0, 2.0], [-4.02970802196492, 0.995904370231472], [-8.
47443484879371, 9.47242621738025], [3.00401593628982, -1.00159247934001], [-3.81434166231536, 0.845
816419487192], [-2.00911635093861, -0.988359479355467], [1.32358494772277, -0.32419504840344]]
我不確定為什么solve
在這里失敗。 這應該被認為是一個錯誤並報告給 GitHub 上的 SymPy 項目,而不是 SO 上的這里。
你可以得到這樣的解決方案:
In [128]: eq1 = nsimplify(rea1)
In [129]: eq2 = nsimplify(rea2)
In [130]: r = resultant(eq1, eq2, x)
In [131]: rs = list(set(real_roots(r)))
In [132]: [root.n(5) for root in rs]
Out[132]: [-0.98836, 2.0, 0.9959, 8.0, -1.0016, 0.84582, -0.32419, 9.4724, -4.0, -1.0]
這給出了y
的值,因此只需確定x
的相應值,例如:
In [133]: xvals = nroots(eq1.subs(y, rs[0].n()))
In [134]: xvals
Out[134]: [-2.0148693245473, -2.00911635093857, 1.98822706053502, 2.97754996447177]
這些值中只有一個是正確的(即也滿足eq2
):
In [135]: [eq2.subs({y:rs[0], x:xval}).n(3) for xval in xvals]
Out[135]: [-0.00481, -2.55e-13, -0.0247, 0.00169]
真正solve
應該能夠處理所有這些,所以應該提出一個錯誤。
您的案例是一個非線性方程組。 solveset
為方程組提供求解集模塊,具體取決於系統類型。
你可以試試solveset.nonlinsolve()
https://docs.sympy.org/latest/modules/solvers/solveset.html#sympy.solvers.solveset.nonlinsolve
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.