Полиномиальная функция не может быть решена с помощью Python

У меня есть проблемы, решая полиномиальную функцию с sympy. В следующем примере показан случай, который выдает сообщение об ошибке, которым я не могу управлять. Если полином становится проще, решатель работает правильно. Пожалуйста, скопируйте и вставьте код, чтобы проверить ошибку в вашей системе.

 import sympy
 from sympy import I
 omega = sympy.symbols('omega')

 def function(omega):
    return - 0.34*omega**4      \
           + 7.44*omega**3      \
           + 4.51*I*omega**3    \
           + 87705.64*omega**2  \
           - 53.08*I*omega**2   \
           - 144140.03*omega    \
           - 22959.95*I*omega   \
           + 42357.18 + 50317.77*I
 sympy.solve(function(omega), omega)

Вы знаете, как я могу добиться результата? Спасибо за помощь.

РЕДАКТИРОВАТЬ:

Это сообщение об ошибке:


TypeError                                 Traceback (most recent call last)
<ipython-input-7-512446a62fa9> in <module>()
      1 def function(omega):
      2     return - 0.34*omega**4                 + 7.44*omega**3                 + 4.51*I*omega**3               + 87705.64*omega**2             - 53.08*I*omega**2              - 144140.03*omega               - 22959.95*I*omega              + 42357.18 + 50317.77*I
----> 3 sympy.solve(function(omega), omega)

C:\Anaconda\lib\site-packages\sympy\solvers\solvers.pyc in solve(f, *symbols, **flags)
   1123     # restore floats
   1124     if floats and solution and flags.get('rational', None) is None:
-> 1125         solution = nfloat(solution, exponent=False)
   1126 
   1127     if check and solution:  # assumption checking

C:\Anaconda\lib\site-packages\sympy\core\function.pyc in nfloat(expr, n, exponent)
   2463             return type(expr)([(k, nfloat(v, n, exponent)) for k, v in
   2464                                list(expr.items())])
-> 2465         return type(expr)([nfloat(a, n, exponent) for a in expr])
   2466     rv = sympify(expr)
   2467 

C:\Anaconda\lib\site-packages\sympy\core\function.pyc in nfloat(expr, n, exponent)
   2497     return rv.xreplace(Transform(
   2498         lambda x: x.func(*nfloat(x.args, n, exponent)),
-> 2499         lambda x: isinstance(x, Function)))
   2500 
   2501 

C:\Anaconda\lib\site-packages\sympy\core\basic.pyc in xreplace(self, rule)
   1085 
   1086         """
-> 1087         value, _ = self._xreplace(rule)
   1088         return value
   1089 

C:\Anaconda\lib\site-packages\sympy\core\basic.pyc in _xreplace(self, rule)
   1093         """
   1094         if self in rule:
-> 1095             return rule[self], True
   1096         elif rule:
   1097             args = []

C:\Anaconda\lib\site-packages\sympy\core\rules.pyc in __getitem__(self, key)
     57     def __getitem__(self, key):
     58         if self._filter(key):
---> 59             return self._transform(key)
     60         else:
     61             raise KeyError(key)

C:\Anaconda\lib\site-packages\sympy\core\function.pyc in <lambda>(x)
   2496 
   2497     return rv.xreplace(Transform(
-> 2498         lambda x: x.func(*nfloat(x.args, n, exponent)),
   2499         lambda x: isinstance(x, Function)))
   2500 

C:\Anaconda\lib\site-packages\sympy\core\function.pyc in nfloat(expr, n, exponent)
   2463             return type(expr)([(k, nfloat(v, n, exponent)) for k, v in
   2464                                list(expr.items())])
-> 2465         return type(expr)([nfloat(a, n, exponent) for a in expr])
   2466     rv = sympify(expr)
   2467 

C:\Anaconda\lib\site-packages\sympy\core\function.pyc in nfloat(expr, n, exponent)
   2463             return type(expr)([(k, nfloat(v, n, exponent)) for k, v in
   2464                                list(expr.items())])
-> 2465         return type(expr)([nfloat(a, n, exponent) for a in expr])
   2466     rv = sympify(expr)
   2467 

TypeError: __new__() takes exactly 3 arguments (2 given)

3 ответа

Решение

Как я упоминал в комментариях, я не знаком с sympy, но вот как найти корни вашего уравнения, используя модуль mpmath с произвольной точностью.

Чтобы избежать проблем с точностью с плавающими в Python, обычно передают плавающие значения в mpmath в строковой форме, если их удобно создавать из целых чисел. Я думаю, это не проблема с вашим уравнением, так как ваши коэффициенты имеют довольно низкую точность, но в любом случае...

Вот ваше уравнение, переведенное непосредственно в синтаксис mpmath:

from mpmath import mp

I = mp.mpc(0, 1)

def func(x):
    return (-mp.mpf('0.34') * x ** 4
        + mp.mpf('7.44') * x ** 3
        + mp.mpf('4.51') * I * x ** 3
        + mp.mpf('87705.64') * x ** 2
        - mp.mpf('53.08') * I * x ** 2
        - mp.mpf('144140.03') * x
        - mp.mpf('22959.95') * I * x
        + mp.mpf('42357.18') + mp.mpf('50317.77') * I)

mpf конструктор с плавающей точкой произвольной точности, mpc конструктор комплексных чисел Пожалуйста, смотрите документацию mpmath для информации о том, как вызывать эти конструкторы.

Тем не менее, нам не нужно возиться с I вот так: мы можем просто определить коэффициенты напрямую как комплексные числа.

from __future__ import print_function
from mpmath import mp

# set precision to ~30 decimal places
mp.dps = 30

def func(x):
    return (mp.mpf('-0.34') * x ** 4
        + mp.mpc('7.44', '4.51') * x ** 3
        + mp.mpc('87705.64', '-53.08') * x ** 2
        + mp.mpc('-144140.03', '-22959.95') * x
        + mp.mpc('42357.18', '50317.77'))

x = mp.findroot(func, 1)
print(x)
print('test:', func(x))

выход

(1.35717989161653180236360985534 - 0.202974596285109153971324419197j)
test: (-3.2311742677852643549664402034e-26 + 6.4623485355705287099328804068e-27j)

Но как мы можем найти другие корни? Просто!

Пусть u корень из f (x). Тогда пусть f(x) = g(x)(x - u) и любой корень из g (x) также является корнем из f (x). Мы можем удобно сделать это несколько раз, используя for Цикл, который сохраняет каждый найденный корень в список, а затем создает новую функцию из предыдущей функции, сохраняя эту новую функцию в другом списке.

В этой версии я использую решатель "muller", что рекомендуется при поиске сложных корней, но на самом деле он дает те же ответы, что и используемый по умолчанию решатель "secant".

from __future__ import print_function
from mpmath import mp

# set precision to ~30 decimal places
mp.dps = 30

def func(x):
    return (mp.mpf('-0.34') * x ** 4
        + mp.mpc('7.44', '4.51') * x ** 3
        + mp.mpc('87705.64', '-53.08') * x ** 2
        + mp.mpc('-144140.03', '-22959.95') * x
        + mp.mpc('42357.18', '50317.77'))

x = mp.findroot(func, 1)
print(x)
print('test:', func(x))

funcs = [func]
roots = []

#Find all 4 roots
for i in range(4):
    x = mp.findroot(funcs[i], 1, solver="muller")
    print('%d: %s' % (i, x))
    print('test: %s\n%s\n' % (funcs[i](x), funcs[0](x)))
    roots.append(x)
    funcs.append(lambda x,i=i: funcs[i](x) / (x - roots[i]))    

выход

(1.35717989161653180236360985534 - 0.202974596285109153971324419197j)
test: (-3.2311742677852643549664402034e-26 + 6.4623485355705287099328804068e-27j)
0: (1.35717989161653180236360985534 - 0.202974596285109153971324419197j)
test: (-3.2311742677852643549664402034e-26 + 6.4623485355705287099328804068e-27j)
(-3.2311742677852643549664402034e-26 + 6.4623485355705287099328804068e-27j)

1: (0.2859569222439674364374376897 + 0.465618100581394597267702975072j)
test: (2.70967991111831205485691272044e-27 - 4.34146435347156317045282996313e-27j)
(0.0 + 6.4623485355705287099328804068e-27j)

2: (-497.86487129703641182172086688 + 6.49836193448774263077718499855j)
test: (1.25428695883356196194609388771e-26 - 3.46609896266051486795576778151e-28j)
(3.11655159180984988723836070362e-21 - 1.65830325771275337225587644119e-22j)

3: (518.104087424352383171155113452 + 6.50370044356891310239702468087j)
test: (-4.82755073209873082528016484574e-30 + 7.38353321095804877623117526215e-31j)
(-1.31713649147437845007238988587e-21 + 1.68350641700147843422461467477e-22j)

В

lambda x,i=i: funcs[i](x) / (x - roots[i])

мы указываем i в качестве аргумента ключевого слова по умолчанию, так что значение, которое i имел, когда была определена функция. В противном случае текущее значение i используется при вызове функции, а это не то, что нам нужно.


Этот метод поиска нескольких корней может быть использован для произвольных функций. Однако, когда мы хотим решить полиномы, у mpmath есть лучший способ найти все корни одновременно: функция polyroots. Нам просто нужно дать ему список (или кортеж) коэффициентов полинома.

from __future__ import print_function
from mpmath import mp

# set precision to ~30 decimal places
mp.dps = 30

coeff = (
    mp.mpf('-0.34'),
    mp.mpc('7.44', '4.51'),
    mp.mpc('87705.64', '-53.08'),
    mp.mpc('-144140.03', '-22959.95'),
    mp.mpc('42357.18', '50317.77'),
)

roots = mp.polyroots(coeff)
for i, x in enumerate(roots):
    print('%d: %s' % (i, x))
    print('test: %s\n' % mp.polyval(coeff, x))

выход

0: (1.35717989161653180236360985534 - 0.202974596285109153971324419197j)
test: (6.4623485355705287099328804068e-27 - 6.4623485355705287099328804068e-27j)

1: (0.2859569222439674364374376897 + 0.465618100581394597267702975072j)
test: (0.0 + 0.0j)

2: (-497.86487129703641182172086688 + 6.49836193448774263077718499855j)
test: (-2.27689218423463552142807161949e-21 + 7.09242751778865525915133624646e-23j)

3: (518.104087424352383171155113452 + 6.50370044356891310239702468087j)
test: (-7.83663157514495734538720675411e-22 - 1.08373584941517766465574404422e-23j)

Как видите, результаты очень близки к тем, которые были получены по предыдущей методике. С помощью polyroots Он не только более точен, но и имеет большое преимущество: нам не нужно указывать начальное приближение для корня, mpmath достаточно умен, чтобы создать его для себя.

Благодаря помощи PM2Ring я создал полный пример кода, который извлекает коэффициенты произвольного заданного полинома симпли четвертого порядка и решает его.

import sympy
from sympy import I
import mpmath as mp
import numpy as np

omega = sympy.symbols('omega')

# Define polynomial in sympy
def function(omega):
    return ( -  0.34                   *omega**4      \
             + (7.44 + 4.51*I)         *omega**3      \
             + (87705.64 - 53.08*I)    *omega**2  \
             - (144140.03 + 22959.95*I)*omega \
             + 42357.18 + 50317.77*I)

# Show defined polynomial
print''+ str(function(omega).expand()) +'\n'
result = sympy.Poly(function(omega).expand(), omega)

# Obtain coefficients of the polynomial
coeff = (
    mp.mpc(str(np.real(sympy.lambdify( (I),result.coeffs()[0])(1j))) , str(np.imag(sympy.lambdify( (I),result.coeffs()[0])(1j)))),
    mp.mpc(str(np.real(sympy.lambdify( (I),result.coeffs()[1])(1j))) , str(np.imag(sympy.lambdify( (I),result.coeffs()[1])(1j)))),
    mp.mpc(str(np.real(sympy.lambdify( (I),result.coeffs()[2])(1j))) , str(np.imag(sympy.lambdify( (I),result.coeffs()[2])(1j)))),
    mp.mpc(str(np.real(sympy.lambdify( (I),result.coeffs()[3])(1j))) , str(np.imag(sympy.lambdify( (I),result.coeffs()[3])(1j)))),
    mp.mpc(str(np.real(sympy.lambdify( (I),result.coeffs()[4])(1j))) , str(np.imag(sympy.lambdify( (I),result.coeffs()[4])(1j))) ),
  )

# Calculate roots of the polynomial
roots = mp.polyroots(coeff)
for no, frequency in enumerate(roots):
    print frequency

Выход

-0.34*omega**4 + 7.44*omega**3 + 4.51*I*omega**3 + 87705.64*omega**2 - 53.08*I*omega**2 - 144140.03*omega - 22959.95*I*omega + 42357.18 + 50317.77*I

(1.35717989161653 - 0.202974596285109j)
(0.285956922243967 + 0.465618100581395j)
(-497.864871297036 + 6.49836193448774j)
(518.104087424352 + 6.50370044356891j)
      import sympy as *

x = symbols('x')

F = - 0.34*x**4+ 7.44*x**3+ 4.51*I*x**3+ 87705.64*x**2- 53.08*I*x**2- 144140.03*x- 22959.95*I*x+ 42357.18 + 50317.77*I

solve(F,x,dict = True)

[{x: -497.864871297036 + 6.49836193448774*I}, {x: 0.285956922243967 + 0.465618100581395*I}, {x: 1.35717989161653 - 0.202974596285109*I}, {x: 518.104087424352 + 6.50370044356891*I}]

Другие вопросы по тегам