Python sympy nonlinsolve依赖于简单的方程组

Python sympy nonlinsolve依赖于简单的方程组,python,sympy,Python,Sympy,我是Symphy的新手,正在尝试用它来解一个相对简单的方程组: import sympy A, B, I, AI, BI, A0, B0, I0, k1, k2, k3, k4 = sympy.symbols('A B I AI BI A_0 B_0 I_0 k1 k2 k3 k4', real=True) eqs = [A0 - AI - A, B0 - BI - B, I0 - AI - BI - I, k1*(A0 - AI)*I - k2*AI, k3*(B0 - BI)*I - k4*

我是Symphy的新手,正在尝试用它来解一个相对简单的方程组:

import sympy
A, B, I, AI, BI, A0, B0, I0, k1, k2, k3, k4 = sympy.symbols('A B I AI BI A_0 B_0 I_0 k1 k2 k3 k4', real=True)
eqs = [A0 - AI - A,
B0 - BI - B,
I0 - AI - BI - I,
k1*(A0 - AI)*I - k2*AI,
k3*(B0 - BI)*I - k4*BI]
如果我为
a、B、I
寻找解决方案,它会起作用:

# this works
result = sympy.nonlinsolve(eqs, (A,B,I))
print(result)
给出结果
FiniteSet((-AI+A_0,-BI+B_0,-AI-BI+I_0))
,但如果我要求解决变量
AI,BI,A,B,I
中的其他变量,它似乎挂起:

# this hangs
result = sympy.nonlinsolve(eqs, (AI,BI,A,B,I))
我做错了什么

(使用的是
sympy'1.6.2'

编辑:为了回应@Oscar Benjamin的有益建议,这里尝试用两种方法来解决原始系统:(1)将具有线性解的变量代入方程中,(2)简化问题,并假设我们希望我们的解是其函数的7个变量中有3个已知。这两种方法都给出了错误的答案:非真实解和非真实解。有办法解决这个问题吗

import sympy
from sympy import solve, factor, roots, nonlinsolve
A, B, I, AI, BI, A0, B0, I0, k1, k2, k3, k4 = sympy.symbols('A B I AI BI A_0 B_0 I_0 k1 k2 k3 k4', real=True)
eqs = [A0 - AI - A,
B0 - BI - B,
I0 - AI - BI - I,
k1*(A0 - AI)*I - k2*AI,
k3*(B0 - BI)*I - k4*BI]

print("trying to solve original system:")
# solve linear equations and substitute their solutions in
((As, Bs, Is),) = nonlinsolve(eqs, (A,B,I))
eqs2 = [eq.subs({A:As,B:Bs,I:Is}) for eq in eqs]
eq1, eq2 = eqs2[3:]
p = eq1.subs(AI, solve(eq2, AI)[0]).as_numer_denom()[0].expand().collect(BI)
BI1, BI2, BI3, BI4 = roots(p, BI)

def eval_solns(inputs, roots):
    for vals in inputs:
        for n, root in enumerate(roots):
            print("root %d yields: " %(n+1))
            print(root.subs(vals))

inputs = [{A0: 100, B0: 100, I0: 100, k1: 0.1, k2: 0.1, k3: 0.1, k4: 0.1},
          {A0: 100, B0: 100, I0: 100, k1: 0.2, k2: 0.1, k3: 0.3, k4: 0.4}]
# all of these give wrong, non-real solutions to the system
orig_roots = [BI1, BI2, BI3, BI4]
eval_solns(inputs, orig_roots)

# second try: simplify the problem by assuming A0, B0, I0 are given
# substitute them in
eqs2 = [eq.subs({A:As,B:Bs,I:Is}) for eq in eqs2]
eqs2 = [eq.subs({A0: 100, B0: 100, I0: 100}) for eq in eqs2]
eq1, eq2 = eqs2[3:]     
print("*\ntrying simpler system with A0,B0,I0 given: ")
print(eqs2)
p = eq1.subs(AI, solve(eq2, AI)[0]).as_numer_denom()[0].expand().collect(BI)                                                          
BI1, BI2, BI3, BI4 = roots(p, BI)
new_roots = [BI1, BI2, BI3, BI4]
# solve the simpler system
new_inputs = [{k1: 0.1, k2: 0.1, k3: 0.1, k4: 0.1},
              {k1: 0.2, k2: 0.1, k3: 0.3, k4: 0.4},
              {k1: 0.05, k2: 1.0, k3: 1.0, k4: 1.2}]
# all of these answers are wrong
eval_solns(new_inputs, new_roots)
我得到的输出包括:

...
trying simpler system with A0,B0,I0 given: 
[0, 0, 0, -AI*k2 + k1*(100 - AI)*(-AI - BI + 100), -BI*k4 + k3*(100 - BI)*(-AI - BI + 100)]
root 1 yields: 
100
root 2 yields: 
nan
root 3 yields: 
nan
root 4 yields: 
nan
root 1 yields: 
100
root 2 yields: 
-6.22222222222222 - 33.1666666666667*(9.77105856678793 + 8.49476415953825*I)**(1/3) - 182.875860785409/(9.77105856678793 + 8.49476415953825*I)**(1/3)
root 3 yields: 
-6.22222222222222 - 33.1666666666667*(-1/2 + sqrt(3)*I/2)*(9.77105856678793 + 8.49476415953825*I)**(1/3) - 182.875860785409/((-1/2 + sqrt(3)*I/2)*(9.77105856678793 + 8.49476415953825*I)**(1/3))
root 4 yields: 
-6.22222222222222 - 182.875860785409/((-1/2 - sqrt(3)*I/2)*(9.77105856678793 + 8.49476415953825*I)**(1/3)) - 33.1666666666667*(-1/2 - sqrt(3)*I/2)*(9.77105856678793 + 8.49476415953825*I)**(1/3)
root 1 yields: 
100
root 2 yields: 
104.655319148936 - 100*(-0.00146514175733681 + 0.00423691411709115*I)**(1/3) - 2.718847623359/(-0.00146514175733681 + 0.00423691411709115*I)**(1/3)
root 3 yields: 
104.655319148936 - 100*(-1/2 + sqrt(3)*I/2)*(-0.00146514175733681 + 0.00423691411709115*I)**(1/3) - 2.718847623359/((-1/2 + sqrt(3)*I/2)*(-0.00146514175733681 + 0.00423691411709115*I)**(1/3))
root 4 yields: 
104.655319148936 - 2.718847623359/((-1/2 - sqrt(3)*I/2)*(-0.00146514175733681 + 0.00423691411709115*I)**(1/3)) - 100*(-1/2 - sqrt(3)*I/2)*(-0.00146514175733681 + 0.00423691411709115*I)**(1/3)
在这个系统中,所有变量都是正实数,所有解都应该是实的。将第一部分声明为与
正数=True
一致,如下所示,没有任何区别:

A, B, I, AI, BI, A0, B0, I0, k1, k2, k3, k4 = sympy.symbols('A B I AI BI A_0 B_0 I_0 k1 k2 k3 k4', real=True, positive=True)
所有的答案仍然不正确


编辑2:为了澄清,我对解析解很感兴趣,但仍然不明白为什么它太复杂,无法在Symphy中导出(然后与插入的实际值一起使用)。我想要解析解的原因是,我可以直接求解k1,k2,k3,k4的特定值,给定A0,B0,I0的特定设置。这就是为什么在已知A0、B0和I0的情况下,可以得到解析解。然而,当所有的A0,B0,I0,k1,k2,k3,k4都给定时,得到数值解并不是我想要的。它看起来可能很简单,但这可以归结为一个具有符号系数的立方体,所以尽管可以通过代数方法找到解,但它并不简单。在这种情况下,
nonlinsolve
在计算较大的Groebner基时速度较慢。不过,有一种更快的方法可以解决这个问题

首先求解
A
B
I
的平凡线性方程组:

In [55]: ((As, Bs, Is),) = nonlinsolve(eqs, (A,B,I))                                                                                           

In [56]: As                                                                                                                                    
Out[56]: -AI + A₀

In [57]: Bs                                                                                                                                    
Out[57]: -BI + B₀

In [58]: Is                                                                                                                                    
Out[58]: -AI - BI + I₀
我们现在可以从系统中消除这些未知数和方程:

In [59]: eqs2 = [eq.subs({A:As,B:Bs,I:Is}) for eq in eqs]                                                                                      

In [60]: eqs2                                                                                                                                  
Out[60]: [0, 0, 0, -AI⋅k₂ + k₁⋅(-AI + A₀)⋅(-AI - BI + I₀), -BI⋅k₄ + k₃⋅(-BI + B₀)⋅(-AI - BI + I₀)]

In [61]: eq1, eq2 = eqs2[3:]                                                                                                                   

In [62]: eq1                                                                                                                                   
Out[62]: -AI⋅k₂ + k₁⋅(-AI + A₀)⋅(-AI - BI + I₀)

In [63]: eq2                                                                                                                                   
Out[63]: -BI⋅k₄ + k₃⋅(-BI + B₀)⋅(-AI - BI + I₀)
此时,我们在
AI
BI
中有一个二次多元多项式系统。我们可以为
AI
求解
eq2
(因为它在
AI
中是线性的),并将其代入
eq1
,以仅在
BI
中得到一个方程:

In [68]: solve(eq2, AI)                                                                                                                        
Out[68]: 
⎡    2                                            ⎤
⎢- BI ⋅k₃ + BI⋅B₀⋅k₃ + BI⋅I₀⋅k₃ + BI⋅k₄ - B₀⋅I₀⋅k₃⎥
⎢─────────────────────────────────────────────────⎥
⎣                   k₃⋅(BI - B₀)                  ⎦

In [69]: eq1.subs(AI, solve(eq2, AI)[0])                                                                                                       
Out[69]: 
   ⎛         2                                            ⎞ ⎛               2                                            ⎞      ⎛    2       
   ⎜     - BI ⋅k₃ + BI⋅B₀⋅k₃ + BI⋅I₀⋅k₃ + BI⋅k₄ - B₀⋅I₀⋅k₃⎟ ⎜           - BI ⋅k₃ + BI⋅B₀⋅k₃ + BI⋅I₀⋅k₃ + BI⋅k₄ - B₀⋅I₀⋅k₃⎟   k₂⋅⎝- BI ⋅k₃ + B
k₁⋅⎜A₀ - ─────────────────────────────────────────────────⎟⋅⎜-BI + I₀ - ─────────────────────────────────────────────────⎟ - ────────────────
   ⎝                        k₃⋅(BI - B₀)                  ⎠ ⎝                              k₃⋅(BI - B₀)                  ⎠                   

                                     ⎞
I⋅B₀⋅k₃ + BI⋅I₀⋅k₃ + BI⋅k₄ - B₀⋅I₀⋅k₃⎠
──────────────────────────────────────
     k₃⋅(BI - B₀) 
我们可以将其简化为四分之一:

In [73]: p = eq1.subs(AI, solve(eq2, AI)[0]).as_numer_denom()[0].expand().collect(BI)                                                          

In [74]: p                                                                                                                                     
Out[74]: 
  4 ⎛       2           3⎞     3 ⎛          2                2                3           2              3           2        2   ⎞     2 ⎛  
BI ⋅⎝- k₁⋅k₃ ⋅k₄ + k₂⋅k₃ ⎠ + BI ⋅⎝- A₀⋅k₁⋅k₃ ⋅k₄ + 2⋅B₀⋅k₁⋅k₃ ⋅k₄ - 3⋅B₀⋅k₂⋅k₃  + I₀⋅k₁⋅k₃ ⋅k₄ - I₀⋅k₂⋅k₃  + k₁⋅k₃⋅k₄  - k₂⋅k₃ ⋅k₄⎠ + BI ⋅⎝2⋅

           2        2      2          2      3                2                   3              2             2   ⎞      ⎛       2      2   
A₀⋅B₀⋅k₁⋅k₃ ⋅k₄ - B₀ ⋅k₁⋅k₃ ⋅k₄ + 3⋅B₀ ⋅k₂⋅k₃  - 2⋅B₀⋅I₀⋅k₁⋅k₃ ⋅k₄ + 3⋅B₀⋅I₀⋅k₂⋅k₃  - B₀⋅k₁⋅k₃⋅k₄  + 2⋅B₀⋅k₂⋅k₃ ⋅k₄⎠ + BI⋅⎝- A₀⋅B₀ ⋅k₁⋅k₃ ⋅k₄

     3      3     2         2          2         3     2      2   ⎞     3         3
 - B₀ ⋅k₂⋅k₃  + B₀ ⋅I₀⋅k₁⋅k₃ ⋅k₄ - 3⋅B₀ ⋅I₀⋅k₂⋅k₃  - B₀ ⋅k₂⋅k₃ ⋅k₄⎠ + B₀ ⋅I₀⋅k₂⋅k₃ 
factor
的简单调用显示四个根中的一个就是
BI=B0

In [84]: factor(p)                                                                                                                             
Out[84]: 
              ⎛     2                                  3              3      2     2                   2         2     2                 2   
-k₃⋅(BI - B₀)⋅⎝A₀⋅BI ⋅k₁⋅k₃⋅k₄ - A₀⋅BI⋅B₀⋅k₁⋅k₃⋅k₄ + BI ⋅k₁⋅k₃⋅k₄ - BI ⋅k₂⋅k₃  - BI ⋅B₀⋅k₁⋅k₃⋅k₄ + 2⋅BI ⋅B₀⋅k₂⋅k₃  - BI ⋅I₀⋅k₁⋅k₃⋅k₄ + BI ⋅I₀

      2     2      2     2                 2      2                                       2                      2         2⎞
⋅k₂⋅k₃  - BI ⋅k₁⋅k₄  + BI ⋅k₂⋅k₃⋅k₄ - BI⋅B₀ ⋅k₂⋅k₃  + BI⋅B₀⋅I₀⋅k₁⋅k₃⋅k₄ - 2⋅BI⋅B₀⋅I₀⋅k₂⋅k₃  - BI⋅B₀⋅k₂⋅k₃⋅k₄ + B₀ ⋅I₀⋅k₂⋅k₃ ⎠
也许这个解决方案对你来说已经足够了。其他三个则要复杂得多。您可以通过以下方式获得所有信息:

In [85]: BI1, BI2, BI3, BI4 = roots(p, BI)                                                                                                     

In [86]: BI1                                                                                                                                   
Out[86]: B₀
我不会显示
BI2
等的输出,因为它太复杂了。并不是所有这些根都一定是原始系统的解决方案,因为需要进行转换才能找到它们,所以您必须反向工作以找出哪些是

我认为这意味着
BI=B0
解决方案只有在
B0
k4
为零时才有效,例如:

In [99]: eq2.subs(BI, BI1)                                                                                                                     
Out[99]: -B₀⋅k₄
编辑:我上面的答案旨在说明如何获得一般的符号答案,但这似乎不是你真正想要的(答案太复杂了,无论如何都没有多大用处)。因为你想得到特定数字的答案,你应该把这些数字代入方程式。如果您不是在寻找分析解决方案,则可以使用nsolve:

In [29]: import sympy 
    ...: from sympy import solve, factor, roots, nonlinsolve 
    ...: A, B, I, AI, BI, A0, B0, I0, k1, k2, k3, k4 = sympy.symbols('A B I AI BI A_0 B_0 I_0 k1 k2 k3 k4', positive=True) 
    ...: eqs = [A0 - AI - A, 
    ...: B0 - BI - B, 
    ...: I0 - AI - BI - I, 
    ...: k1*(A0 - AI)*I - k2*AI, 
    ...: k3*(B0 - BI)*I - k4*BI]                                                                                                               

In [30]: inputs = [{A0: 100, B0: 100, I0: 100, k1: 0.1, k2: 0.1, k3: 0.1, k4: 0.1}, 
    ...:           {A0: 100, B0: 100, I0: 100, k1: 0.2, k2: 0.1, k3: 0.3, k4: 0.4}]                                                            

In [31]: eqs_sub = [eq.subs(inputs[0]) for eq in eqs]                                                                                          

In [32]: solve(eqs_sub, [A, B, I, AI, BI])                                                                                                     
Out[32]: [(50.4902894311622, 50.4902894311622, 0.980578862324382, 49.5097105688378, 49.5097105688378)]

In [33]: nsolve(eqs_sub, [A, B, I, AI, BI], [1, 1, 1, 1, 1])                                                                                   
Out[33]: 
⎡50.4902894311622 ⎤
⎢                 ⎥
⎢50.4902894311622 ⎥
⎢                 ⎥
⎢0.980578862324382⎥
⎢                 ⎥
⎢49.5097105688378 ⎥
⎢                 ⎥
⎣49.5097105688378 ⎦

In [34]: eqs_sub = [eq.subs(inputs[1]) for eq in eqs]                                                                                          

In [35]: nsolve(eqs_sub, [A, B, I, AI, BI], [1, 1, 1, 1, 1])                                                                                   
Out[35]: 
⎡38.3817627411582⎤
⎢                ⎥
⎢62.4209392826439⎥
⎢                ⎥
⎢0.80270202380213⎥
⎢                ⎥
⎢61.6182372588418⎥
⎢                ⎥
⎣37.5790607173561⎦

谢谢,但我对A、B、I、AI、BI的表达式感兴趣,因为它们是剩余变量(A0、B0、I0、k1、k2、k3、k4),所以任何使用A、B、I、AI或BI的表达式都是无用的。我(主要)向您展示了如何达到您想要的效果。然而,最终结果是相当复杂的。看看
BI2
。这将根据您要求的剩余变量给出
BI
。这是一个复杂的表达方式,其他的也一样。这就是我没有给出最终答案的原因。谢谢。你能不能再多说一点,为什么在nonlinsolve使用的自动程序挂起的时候,这些手部替代似乎起作用?我不这么认为,还有BI2和其余的根看起来不正确<代码>BI2.subs({A0:100,B0:100,I0:100,k1:1.0,k2:1.0,k3:1.0,k4:1.0})产生
nan
。所有正实数都应该给出一个正实数的答案。不确定出了什么问题。nonlinsolve函数在这种情况下速度很慢,因为正如我所说,它正在计算一个大的Groebner基。