Хабрахабр

Символьные вычисления средствами Python. Часть1. Основы

image

Основываясь на библиотеке SymPy, Python с успехом справляется с решением уравнений и систем, интегрированием и дифференцированием, вычислением пределов, разложением в ряд и суммированием рядов, упрощением выражений, выполняет поиск решения дифференциальных уравнений и систем.
При использовании символьных вычислений пользователю предоставляется возможность управлять работой программы в процессе ее исполнения путём ввода любых допустимых функций с заданным количеством переменных. Реализация алгоритмов на языке Python с использованием символьных вычислений и интерпретируемого языка очень удобна при решении задач математического моделирования процессов и объектов.

Вашему вниманию представляется цикл статей, в которых можно ознакомиться с символьными вычислениями на Python. Как преподаватель дисциплины «Компьютерная техника и программирование», в модуле, посвященном программированию на языке Python, я знакомлю студентов с возможностями этого языка для научных исследований. Хочу сразу предупредить, что данные статьи не претендуют на абсолютную уникальность, так как собраны на основании материалов из различных источников, их цель – обучить студентов основам символьных вычислений.

Если вы с этим справились, сразу перейдем к объявлению переменных. Самым первым шагом на пути к символьным вычислениям является импортирование функций модуля SymPy с помощью pip, системы управления пакетами Python.

Для сокращения записи во всех следующих примерах не приводится первая строка: from sympy import * Примечание.

Явное объявление символьных переменных

Для символьных вычислений с помощью модуля SymPy символьные переменные и функции должны быть объявлены как таковые. В программах для математических вычислений, таких как Mathematica или Maple, переменные сразу рассматриваются как символьные. В Python же их необходимо принудительно объявить символьными, и сделать это можно несколькими путями. Самым простым будет использование функций symbols() или var(). Первая функция возвращает ссылку на символьный объект в виде какой-либо переменной. Вторая, без присваивания создает символьную переменную.

Пример кода

>>> x,y,a,b = symbols('x y a b') # созданы четыре символьные переменные, предыдущие же значения переменных затираются
>>> f=a**3*x + 3*a**2*x**2/2 + a*x**3 + x**4/4 # переменная f становится автоматически символьной >>> type(f)
<class 'sympy.core.add.Add'>
>>> var('u,v')
(u, v)
>>> f=sin(u)**2+tan(v) # переменная f автоматически становится символьной
>>> type(f)
<class 'sympy.core.add.Add'>

Главное отличие между функциями symbols() и var() состоит в том, первая функция возвращает ссылку на символьный объект. Для использования в дальнейшем, ее нужно присвоить какой-либо переменной. Вторая, без присваивания, создает символьную переменную.
В функциях symbols() и var() можно объявлять символьные переменные с индексом:

Пример кода

>>> x=symbols('x:9'); x # диапазон индексов от 0 до 9
(x0, x1, x2, x3, x4, x5, x6, x7, x8)
>>> x=symbols('x5:10'); x # диапазон индексов от 5 до 9
(x5, x6, x7, x8, x9)
>>> x=var('x:9'); x # диапазон индексов от 0 до 9
(x0, x1, x2, x3, x4, x5, x6, x7, x8)
>>> x=var('x5:10'); x # диапазон индексов от 5 до 9
(x5, x6, x7, x8, x9)

Также можно назначить тип и накладывать ограничения на символьные переменные прямо в функциях symbols() и var(). Иногда без таких ограничений очевидные преобразования не работают, например, сравните:

Пример кода

>>> x = symbols('x', integer=True) #назначаем целый тип >>> sqrt(x**2)
Abs(x)
>>> x = symbols('x', positive = True, integer=True)
>>> sqrt(x**2)
x
>>> x = symbols('x')
>>> sqrt(x**2) # это x, если x≥0
sqrt(x**2) >>> x = var('x', integer=True)
>>> sqrt(x**2)
Abs(x)
>>> x = var('x', positive = True, integer=True)
>>> sqrt(x**2)
x
>>> x = var('x')
>>> sqrt(x**2) # это x, если x≥0
sqrt(x**2)

Чтобы создать контейнер для одиночного символа, используем аргумент seq=True:

>>> symbols('x',seq=True)
(x,)

Определение действительных значений для символьных переменных:

>>> x, y, z = symbols('x,y,z', real=True)
>>> x.is_real and y.is_real and z.is_real
True

Функция S()

Иногда символьные выражения могут быть проинтерпретированы как числовые константы Python, а не SymPy. Поэтому для объявления символьных переменных, а также для преобразования числовых констант в символьные, применяют функцию S(), например, сравним:

>>> expr = x**2 + sin(y) + S(10)/2; expr
x**2 + sin(y) + 5 >>> type(10)
<class 'int'>
>>> type(S(10)) # символьная константа десять
<class 'sympy.core.numbers.Integer'>

Разница между постоянной Python и символьной состоит в том, что символьная константа может быть вычислена с заданной степенью точности, как показано в следующем примере в сравнении со стандартной функцией round():

z=1/7; z # вычисляет переменную z с процессорной точностью
0.14285714285714285
z1=S(1)/7; z1
1/7
z2=z1.n(30); z2 # вычисляет переменную z2 с точностью до 30 значащих цифр
0.142857142857142857142857142857
z3=round(z1,30); z3
0.14285714285714285

Cимвольные имена

Если в текущей сессии необходимо использовать символьную математику постоянно, то можно импортировать общепринятые символьные имена из модуля sympy.abc:

Пример кода

>>> import sympy.abc >>> dir(sympy.abc)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_clash', '_clash1', '_clash2', 'a', 'alpha', 'b', 'beta', 'c', 'chi', 'd', 'delta', 'division', 'e', 'epsilon', 'eta', 'exec_', 'f', 'g', 'gamma', 'greeks', 'h', 'i', 'iota', 'j', 'k', 'kappa', 'l', 'lamda', 'm', 'mu', 'n', 'nu', 'o', 'omega', 'omicron', 'p', 'phi', 'pi', 'print_function', 'psi', 'q', 'r', 'rho', 's', 'sigma', 'string', 'symbols', 't', 'tau', 'theta', 'u', 'upsilon', 'v', 'w', 'x', 'xi', 'y', 'z', 'zeta']

Имя переменной из пространства имен можно удалить командой del имя1, имя2,..:

>>> type(x)
<class 'sympy.core.symbol.Symbol'>
>>> del x,y >>> x
NameError: name 'x' is not defined

Для восстановления значений стандартных констант, а также имен некоторых функций, нужно повторно загрузить модуль sympy.

>>> from sympy import *

Метод subs(...)

Следует помнить, что при записи символьного выражения может автоматически выполняться его упрощение, например:

>>> a,b,c,d,x,y,z,u,v,w = symbols('a b c d x y z u v w') >>> x - z + 20 -z- 15 + 3*sin(pi/2)+2*z
x + 8

Метод subs(...) используется для вычисления символьного выражения при заданных значениях переменных, например:

>>> a, x = symbols('a x')
>>> f= a**3*x + 3*a**2*x**2/2 + a*x**3 + x**4/4 >>> f.subs(a,1) # в выражение f вместо переменной a была подставлена единица
x**4/4 + x**3 + 3*x**2/2 + x

Если в методе subs использовать два аргумента, то они интерпретируются как subs(old,new), т.е. старый идентификатор old заменяется новым new. Аргумент метода subs() может быть последовательностью, которая должна содержать пары (old,new), а может быть символьным выражением, например:

>>> a,b,c,d,x,y,z = symbols('a b c d x y z')
>>> f=a*x**3 +b*y**2 + c*z+d
>>> f.subs([(a,1),(b,2),(c,3),(d,4)]) # выполнена подстановка a=1, b=2, c=3, d=4
x**3 + 2*y**2 + 3*z + 4
>>> pr= x**3+4*x**2+6*x+10 >>> pr.subs(x,1/x) # выполнена подстановка символьного выражения
10 + 6/x + 4/x**2 + x**(-3)

Обратим ваше внимание на следующую особенность работы с переменными (символьными и обычными переменными Python). Выполним следующий код:

>>> x='Hello' >>> pr=x+'world' >>> pr 'Helloworld' >>> x='AAA' #присвоили символьной переменной x новое значение
>>> pr 'Helloworld'

Здесь действует правило: если переменная изменилась, то созданное ранее выражение, содержащее эту переменную, не пересчитывается автоматически. Это правило срабатывает и для обычных переменных Python.

Операции с дробями

Модуль SymPy может проводить вычисления с дробями и приводить их к общему знаменателю, например, сравните:

>>> S(1)/3+S(2)/5
11/15 >>> 1/3+2/5
0.7333333333333334

Функции Rational(числитель, знаменатель) и Integer(...) используются для создания рациональных дробей без десятичного округления:

>>> z=Rational(1, 7)+Rational(2, 5); z 19/35
>>> Integer(1)/Integer(5)
1/5
>>> 1/5
0.2
>>> z=Integer(1)/Integer(5)+Rational(2, 7); z
17/35

Округления вычислений

В символьных вычислениях работает правило – если ничего не сказано, не делать никаких округлений. Посмотрите, как в первом случае Python преобразует выражение, но оставит в записи ответа квадратный корень и не выполнит никаких округлений, а во втором, так как одно из чисел задано с десятичной точкой, результат будет приближенным:

>>> sqrt(20) 2*sqrt(5)
>>> sqrt(20.0) # в выражении используется число с десятичной точкой
4.47213595499958

Для любого символьного объекта существует метод evalf(...)(evaluate float), который возвращает его десятичное представление:

>>> sqrt(20).evalf() # функция sqrt() модуля sympy
4.47213595499958
>>> E.evalf()
2.71828182845905

В методе evalf([n,...]) можно использовать аргумент, задающий точность результата (n = количество значащих цифр)

>>> sqrt(20).evalf(30)
4.47213595499957939281834733746
>>> pi.evalf(20)
3.1415926535897932385

Также всегда нужно помнить, что вещественная арифметика не возвращает точный результат, сравните:

>>> from sympy import *
>>> one=S('one')
>>> one = cos(1)**2 + sin(1)**2 >>> one.evalf() # равно 1
1.00000000000000
>>> (one-1).evalf() # должно быть равно 0
-0.e-124

Если известно, что результат содержит погрешность вычислений, то с помощью опции chop=True метода evalf() ее можно удалить. Очень маленькое значение вещественной или мнимой части результата в этом случае заменяется нулем. Возьмем предыдущий пример:

>>> (one-1).evalf() # должно быть равно 0
-0.e-124 >>> (one - 1).evalf(chop=True)
0

Бесконечность

После выполнения первой строки from sympy import * становится доступен символ бесконечности – oo (две буквы „o‟), с которым тоже можно выполнять определенные операции:

>>> oo+1
oo
>>> 1000000<oo
True
>>> 1/oo
0

Символ бесконечности в основном используется функциями limit() и integrate() при задании пределов интегрирования, о чем мы поговорим в одной из следующих статей.

Вывод

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

Надеюсь, моя статья будет полезна всем интересующимся программированием на языке Python, студентам и тем, кто занимается научными исследованиями.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть