Использование sympy checksol — получены некоторые результаты, которые не прошли проверку в Excel

#excel #sympy

#excel #sympy

Вопрос:

Спасибо за чтение: у меня возникла некоторая путаница при попытке решить группу линейных уравнений с использованием sympy, где я столкнулся с огромным разочарованием в достижении согласованных результатов от разных решателей sympy и scipy, а также от MathCAD. Должно быть, я что-то упускаю. Ниже приведена простая часть головоломки, пожалуйста, кто-нибудь может помочь указать на мою проблему, заранее спасибо!

 #-------------------------------------
import sympy as sy 
#
def test_sols(f=None,sols_test=None):
    C1,C2,C3,C4,C5,C6,C7,C8=sy.symbols('C1,C2,C3,C4,C5,C6,C7,C8')
    #
    Cs=[C1,C2,C3,C4,C5,C6,C7,C8]
    #
    dct=dict(zip(Cs,sols_test))
    isRoot=sy.checksol(f,dct)
    print(isRoot)
    #
    err=f.evalf(subs=dct)
    print(err)
    #
#
C1,C2,C3,C4,C5,C6,C7,C8=sy.symbols('C1,C2,C3,C4,C5,C6,C7,C8')
#
f=C1   0.5*C2   1033885634874.62*C3   1033885634874.62*C4 - C5 - 0.5*C6 - 1033885634874.62*C7 - 1033885634874.62*C8 - 0.0636904761904762
sols_test_1=[0.133112996062315,0,0,0,0.196961877236444,-0.254761904761905,163772637.090428,-163772637.090428]
sols_test_2=[0.133112996062315,0,0,0,0.196961877236444,-0.254761904761905,0,0]
#
test_sols(f,sols_test_1)
test_sols(f,sols_test_2) 
# -----------------------------------------------------
 

Приведенный выше код пытается протестировать два набора решений для многомерного (C1 … C8) выражения f . Результаты кода являются True и False для sols_test_1 и sols_test_2 соответственно. скриншот bash
Я поместил цифры в Excel, но обнаружил противоположный вывод проверки Excel. Я получил в Excel значение (ошибка) f(sol_test_1) = -0.063690476 и f (sol_test_2) = -0.000158405. Первая ошибка больше последней, что предполагает вывод False и True для sols_test_1 и sols_test_2 соответственно.

Я подозреваю, что у меня проблема с использованием типов номеров sympy или похожих. Пожалуйста, помогите.

Ответ №1:

Проблема в том, что вы добавляете и вычитаете очень большие числа с очень маленькими числами. Это проблематично с плавающей запятой из-за катастрофической отмены: https://en.wikipedia.org/wiki/Catastrophic_cancellation

Используя sympy, вы можете точно вычислить результаты без ошибок округления:

 from sympy import *
from sympy import Rational as R

C1,C2,C3,C4,C5,C6,C7,C8 = syms = sy.symbols('C1,C2,C3,C4,C5,C6,C7,C8')

f = (C1   R('0.5')*C2   R('1033885634874.62')*C3   R('1033885634874.62')*C4 - C5
    - R('0.5')*C6 - R('1033885634874.62')*C7 - R('1033885634874.62')*C8 -
    R('0.0636904761904762'))

sols_test_1=[R('0.133112996062315'),0,0,0,R('0.196961877236444'),
             R('-0.254761904761905'),R('163772637.090428'),R('-163772637.090428')]
sols_test_2=[R('0.133112996062315'),0,0,0,R('0.196961877236444'),R('-0.254761904761905'),0,0]

print(f.subs(zip(syms, sols_test_1)))
print(f.subs(zip(syms, sols_test_2)))
 

Это дает:

 -1584049836527/10000000000000000
-1584049836527/10000000000000000
 

Таким образом, оба ответа дают одинаковую ошибку, если вычисление выполняется точно. Ошибка здесь небольшая по сравнению с некоторыми очень большими членами в уравнении. При вычислении с плавающей запятой трудно сказать, следует ли это считать «решением» или нет checksol .

Комментарии:

1. Большое спасибо! Так что на самом деле моя проверка Excel прошла неправильно из-за катастрофической отмены. Я также заметил, что мой код выдает ошибку, идентичную вашей «рационализированной», может ли это быть связано с тем, что они автоматически преобразуются в объекты sumpy? Мой другой вывод заключается в том, что checksol не подходит для этого случая вычисления с плавающей запятой.

2. Я бы скорее сказал, что плавающая точка не подходит для такого рода задач, чем сам checksol. Как вы можете видеть, он также не работает в Excel и других средах. SymPy может вычислять с плавающей запятой с более высокой точностью, и достаточно высокая точность решит эту проблему. К сожалению, числа, которые вы ввели в качестве входных данных в своем коде, были литералами с плавающей запятой Python, которые всегда должны иметь точность 53 бита, поэтому SymPy не может улучшить точность, если входные данные задаются в виде таких чисел с плавающей запятой.

3. Еще раз спасибо, Оскар. Да, действительно, это имеет смысл. Мне интересно, будет ли проблема по-прежнему существовать, если эти числа с плавающей запятой были вычислены внутренне с помощью метода expr.subs() вместо прямого ввода? Поскольку мой приведенный выше код был упрощенным примером, в котором я скопировал числа с плавающей запятой из командной строки. В моем исходном коде это больше похоже на: функция f(x, y, C1 … C8) затем очищает x и y, выполняя f.subs((x,x_float_input),(y,y_float_input)) где x_float_input и y_float_input по-прежнему являются числами с плавающей запятой, но являются «нормальными» короткими числами, такими как 0.5, 2.54e3 или 300e9, аналогично.

4. У вас была бы та же проблема subs . evalf(subs=...) Предоставляется метод, который должен быть способен обрабатывать подобные вещи, но он будет работать только в таком случае, если предоставленные вами входные данные являются eaxct.